<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="de-AT">
	<id>https://mediawiki.fernfh.ac.at/mediawiki/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=V%C3%B6lkl+Anna</id>
	<title>FernFH MediaWiki - Benutzerbeiträge [de-at]</title>
	<link rel="self" type="application/atom+xml" href="https://mediawiki.fernfh.ac.at/mediawiki/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=V%C3%B6lkl+Anna"/>
	<link rel="alternate" type="text/html" href="https://mediawiki.fernfh.ac.at/mediawiki/index.php/Spezial:Beitr%C3%A4ge/V%C3%B6lkl_Anna"/>
	<updated>2026-05-20T07:44:15Z</updated>
	<subtitle>Benutzerbeiträge</subtitle>
	<generator>MediaWiki 1.37.0</generator>
	<entry>
		<id>https://mediawiki.fernfh.ac.at/mediawiki/index.php?title=Datei:Devsecops-diagram.png&amp;diff=6590</id>
		<title>Datei:Devsecops-diagram.png</title>
		<link rel="alternate" type="text/html" href="https://mediawiki.fernfh.ac.at/mediawiki/index.php?title=Datei:Devsecops-diagram.png&amp;diff=6590"/>
		<updated>2025-05-30T14:54:39Z</updated>

		<summary type="html">&lt;p&gt;Völkl Anna: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;DevSecOps Diagramm von Atlassian, siehe https://www.atlassian.com/de/devops/devops-tools/devsecops-tools&lt;/div&gt;</summary>
		<author><name>Völkl Anna</name></author>
	</entry>
	<entry>
		<id>https://mediawiki.fernfh.ac.at/mediawiki/index.php?title=Testing_und_Qualit%C3%A4tssicherung_(Webdevelopment)&amp;diff=6589</id>
		<title>Testing und Qualitätssicherung (Webdevelopment)</title>
		<link rel="alternate" type="text/html" href="https://mediawiki.fernfh.ac.at/mediawiki/index.php?title=Testing_und_Qualit%C3%A4tssicherung_(Webdevelopment)&amp;diff=6589"/>
		<updated>2025-05-30T14:48:17Z</updated>

		<summary type="html">&lt;p&gt;Völkl Anna: Die Seite wurde neu angelegt: „= Testing und Qualitätssicherung für Web-Anwendungen = &amp;lt;p&amp;gt;Fehler, Qualitätsmängel oder Ausfälle von Web-Anwendungen haben oft gravierende Auswirkungen auf den Umsatz, die Kundenzufriedenheit und den Ruf eines Unternehmens. Aus der Perspektive der Wirtschaftsinformatik ist es daher wichtig, Qualitätssicherung (QS) und Testing nicht nur als technische Notwendigkeit zu betrachten, sondern als Instrumente zur Risikominimierung. Es gilt, die technischen…“&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Testing und Qualitätssicherung für Web-Anwendungen =&lt;br /&gt;
&amp;lt;p&amp;gt;Fehler, Qualitätsmängel oder Ausfälle von Web-Anwendungen haben oft gravierende Auswirkungen auf den Umsatz, die Kundenzufriedenheit und den Ruf eines Unternehmens. Aus der Perspektive der Wirtschaftsinformatik ist es daher wichtig, Qualitätssicherung (QS) und Testing nicht nur als technische Notwendigkeit zu betrachten, sondern als Instrumente zur Risikominimierung. Es gilt, die technischen Aspekte der Softwarequalität – wie Zuverlässigkeit, Performance oder Sicherheit – in betriebswirtschaftliche Kennzahlen und übergeordnete Geschäftsziele zu übersetzen und deren Beitrag zum Unternehmenserfolg messbar zu machen.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Ein zentrales Konzept zur Bewertung der Wirtschaftlichkeit von Qualitätsmaßnahmen ist das Modell der Qualitätskosten (Cost of Quality, CoQ). Dieses Modell differenziert zwischen Fehlerverhütungskosten (Kosten zur Vermeidung von Fehlern, z.B. durch Schulungen, Reviews oder verbesserte Designprozesse), Prüfkosten (Kosten für die Bewertung der Produktqualität, z.B. durch Tests und Inspektionen), internen Fehlerkosten (Kosten für Fehler, die vor Auslieferung an den Kunden entdeckt werden, z.B. Nachbesserungsaufwand, Ausschuss) und externen Fehlerkosten (Kosten für Fehler, die erst beim Kunden auftreten, z.B. Supportaufwand, Garantieleistungen, Reputationsschäden, Kundenverlust). Die sogenannte &amp;quot;1-10-100-Regel&amp;quot; verdeutlicht praxisnah die Eskalation dieser Kosten: Ein Fehler, dessen Behebung in der Designphase einer Web-Anwendung beispielsweise 1€ kostet, kann in der Testphase bereits 10€ und im Live-Betrieb 100€ oder ein Vielfaches davon verursachen. Ein konkretes Beispiel hierfür wäre ein Designfehler in der Navigationsstruktur einer E-Commerce-Anwendung. Wird dieser Fehler frühzeitig durch Usability-Reviews entdeckt, sind die Verhütungskosten gering. Erfolgt die Entdeckung erst während des Systemtests, entstehen bereits signifikante interne Fehlerkosten durch notwendiges Redesign, erneute Implementierung und wiederholte Testläufe. Fällt der Fehler jedoch erst nach dem Go-Live durch Kundenfeedback auf, können die externen Fehlerkosten durch Supportanfragen, verlorene Verkaufsabschlüsse und einen Imageschaden groß sein.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Der Business Value von Qualitätssicherung definiert sich somit nicht nur in der Senkung direkter Fehlerkosten. Frühzeitige und kontinuierliche Qualitätssicherung steigert darüber hinaus die Effizienz der Entwicklungsprozesse, verkürzt die Time-to-Market neuer Funktionen oder Produkte und erhöht die Kundenzufriedenheit sowie die Kundenbindung, was sich wiederum positiv auf den Umsatz auswirkt.&amp;amp;nbsp;&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Die fortschreitende Digitalisierung und die damit einhergehende, stetig wachsende Komplexität von Web-Anwendungen, beispielsweise durch den Einsatz von Microservice-Architekturen oder die Integration von Künstlicher Intelligenz, erhöhen den Stellenwert einer proaktiven und intelligenten QS-Strategie exponentiell. Traditionelle, oft reaktive Testansätze stoßen hier schnell an ihre Grenzen. Moderne Ansätze wie &amp;quot;Shift Left&amp;quot;-Testing, DevSecOps und KI-gestütztes Testen sind nicht mehr nur wünschenswert, sondern notwendig, um diese Komplexität zu beherrschen und qualitativ hochwertige Web-Anwendungen zuverlässig auszuliefern.&amp;amp;nbsp;&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Es ist wichtig, verschiedene Testmethoden und -werkzeuge zu kennen, aber auch ein tiefgreifendes Verständnis dafür zu entwickeln, wie QS-Prozesse in moderne, agile und hochautomatisierte Entwicklungs-Pipelines integriert werden können. Die Fähigkeit, die richtigen QS-Strategien für komplexe digitale Produkte auszuwählen, zu implementieren und deren Beitrag zum Geschäftserfolg zu optimieren und zu kommunizieren, entwickelt sich zu einer entscheidenden Kernkompetenz.&amp;lt;/p&amp;gt;&lt;br /&gt;
= Grundlagen des Testens: Prinzipien und Prozessorientierung =&lt;br /&gt;
&amp;lt;p&amp;gt;Ein solides Fundament im Testen von Software basiert auf etablierten Prinzipien und einem strukturierten Prozessverständnis. Diese Grundlagen sind entscheidend, um Testaktivitäten effektiv zu planen, durchzuführen und zu bewerten, insbesondere im dynamischen Umfeld der Web-Anwendungsentwicklung.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Die Sieben Grundprinzipien des Testens (nach ISTQB) – Praxisbezug für Web-Anwendungen:&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Das International Software Testing Qualifications Board (ISTQB) hat sieben Grundprinzipien formuliert, die als Leitlinien für jegliche Testaktivitäten dienen und für die Qualitätssicherung von Web-Anwendungen von hoher Relevanz sind.4&amp;lt;/p&amp;gt;&lt;br /&gt;
#Testen zeigt die Anwesenheit von Fehlern, nicht deren Abwesenheit: Selbst eine intensiv getestete Web-Anwendung kann verborgene Fehler enthalten. Das Ziel der QS ist es, das Risiko kritischer Fehler zu minimieren und Vertrauen in die Qualität zu schaffen, nicht absolute Fehlerfreiheit zu garantieren. Beispielsweise kann ein E-Commerce-Shop auf hunderte von Kaufszenarien getestet werden; ein seltener Edge Case oder eine ungewöhnliche Benutzerinteraktion kann dennoch unentdeckt bleiben.&lt;br /&gt;
#Vollständiges Testen ist unmöglich: Angesichts der immensen Kombinationsvielfalt von Eingabewerten, Benutzerpfaden, Browsern, Betriebssystemen und Endgeräten bei Web-Anwendungen ist ein hundertprozentiger Test aller denkbaren Szenarien praktisch nicht realisierbar. Risikobasierte Ansätze und die Priorisierung von Testfällen sind daher unerlässlich. Eine komplexe Web-Anwendung mit zahlreichen Formularen, dynamischen Inhalten und vielfältigen Benutzerinteraktionen kann nicht jede einzelne Permutation testen.&lt;br /&gt;
#Frühes Testen spart Zeit und Geld (Shift Left): Fehler, die bereits in frühen Phasen des Entwicklungszyklus einer Web-Anwendung – wie der Anforderungsanalyse oder dem Design – identifiziert und behoben werden, verursachen signifikant geringere Kosten als Fehler, die erst in späteren Testphasen oder gar im Live-Betrieb entdeckt werden. Ein Missverständnis in den Anforderungen für eine Benutzerberechtigungsmatrix einer Web-Anwendung, das frühzeitig durch ein Review aufgedeckt wird, verhindert aufwendige und teure Umprogrammierungen zu einem späteren Zeitpunkt.&lt;br /&gt;
#Fehler treten gehäuft auf (Defect Clustering): Die Erfahrung zeigt, dass oft eine kleine Anzahl von Modulen oder Komponenten einer Web-Anwendung für die Mehrheit der gefundenen Fehler verantwortlich ist. Testressourcen sollten daher gezielt auf diese risikoreichen Bereiche konzentriert werden. Beispielsweise sind das Zahlungsmodul oder die Integration von Drittanbieter-APIs in einem Web-Shop oft fehleranfälliger als statische Inhaltsseiten.&lt;br /&gt;
#Tests nutzen sich ab (Pestizid-Paradoxon): Wenn für eine Web-Anwendung über längere Zeit immer dieselben Testfälle wiederholt werden, sinkt deren Effektivität bei der Entdeckung neuer Fehler. Testfälle müssen daher regelmäßig überprüft, angepasst und erweitert werden, um mit den Weiterentwicklungen der Anwendung Schritt zu halten. Werden beispielsweise neue Authentifizierungsmethoden wie Zwei-Faktor-Authentifizierung in einer Web-Anwendung eingeführt, müssen bestehende Regressionstests für die Login-Funktion entsprechend erweitert werden.&lt;br /&gt;
#Testen ist kontextabhängig: Die Teststrategie und -intensität müssen an den spezifischen Kontext der Web-Anwendung angepasst werden. Eine einfache Informations-Website erfordert andere Testschwerpunkte und -verfahren als ein Online-Banking, ein komplexer E-Commerce-Shop oder eine sicherheitskritische Regierungsanwendung. Eine Banking-Anwendung erfordert beispielsweise intensive Sicherheitstests und die Prüfung komplexer Transaktionslogiken, während bei einem Unternehmensblog der Fokus eher auf Usability, korrekter Content-Darstellung und Suchmaschinenoptimierung liegen könnte.&lt;br /&gt;
#Trugschluss: „Keine Fehler“ bedeutet ein brauchbares System: Eine Web-Anwendung kann technisch fehlerfrei sein, aber dennoch die Anforderungen der Benutzer nicht erfüllen, eine schlechte User Experience bieten oder geschäftliche Ziele verfehlen und somit wertlos sein. Eine Web-Anwendung zur Reisebuchung mag technisch einwandfrei funktionieren, aber wenn der Buchungsprozess für die Nutzer umständlich, verwirrend oder langsam ist, werden sie die Anwendung verlassen und zur Konkurrenz wechseln.&lt;br /&gt;
&lt;br /&gt;
== Der Fundamentale Testprozess nach ISTQB und seine agile Anpassung ==&lt;br /&gt;
&amp;lt;p&amp;gt;Der ISTQB definiert einen fundamentalen Testprozess, der als Rahmen für die Durchführung von Testaktivitäten dient. Dieser Prozess umfasst typischerweise die Phasen: Testplanung, Testanalyse, Testentwurf, Testrealisierung, Testdurchführung und Testabschluss.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;p&amp;gt;Im Kontext agiler Entwicklungsmethoden wie Scrum oder Kanban, die in Web-Projekten weit verbreitet sind, erfährt dieser klassische Prozess eine signifikante Anpassung. Anstelle langer, sequenzieller Phasen treten kürzere Zyklen, eine kontinuierliche Integration von Testaktivitäten in die Sprints und eine enge, tägliche Zusammenarbeit im gesamten Team (Entwickler, Tester, Product Owner). Der Fokus liegt auf schnellem Feedback und der iterativen Verbesserung der Softwarequalität.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;p&amp;gt;Die Testplanung wird zu einer kontinuierlichen und iterativen Aktivität. In der Release- und Sprint-Planung werden Testaufwände geschätzt und Risikobewertungen vorgenommen.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;p&amp;gt;Testanalyse und -entwurf erfolgen parallel zur Definition und Verfeinerung von User Stories und deren Akzeptanzkriterien. Die Testdurchführung ist in agilen Web-Projekten stark automatisiert und tief in Continuous Integration/Continuous Delivery (CI/CD)-Pipelines integriert, um schnelles Feedback zu Codeänderungen zu ermöglichen. Der Testabschluss findet nicht nur am Ende des gesamten Projekts statt, sondern iterativ am Ende jedes Sprints, beispielsweise in Form von Retrospektiven und der Dokumentation von Lessons Learned, um den Prozess kontinuierlich zu verbessern.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
== Testdokumentation in agilen Web-Projekten ==&lt;br /&gt;
&amp;lt;p&amp;gt;In agilen Projekten wird von umfangreichen, detaillierten Testplänen und -spezifikationen zugunsten pragmatischerer und flexiblerer Testartefakte Abstand genommen. Ziel ist eine Dokumentation, die den Prozess unterstützt, statt ihn zu behindern.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p&amp;gt;* Test Charters sind ein nützliches Werkzeug für das explorative Testen. Sie definieren in kurzer, prägnanter Form den Auftrag, den Umfang, die Ressourcen und die Ziele einer explorativen Testsitzung. Ein Beispiel für einen Test Charter könnte lauten: &amp;quot;Erkunden Sie die neue Checkout-Seite des Web-Shops mit verschiedenen Zahlungsmethoden auf unterschiedlichen mobilen Endgeräten, um potenzielle Usability-Probleme und Fehler im Bezahlprozess unter realitätsnahen Bedingungen zu entdecken.&amp;quot;&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;* Mindmaps eignen sich hervorragend zur visuellen Strukturierung von Testideen, zur Abdeckung von Testbereichen oder zur kollaborativen Erstellung von Testplänen. Sie fördern die Kreativität und das gemeinsame Verständnis im Team.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;* Checklisten dienen als Gedächtnisstütze für wiederkehrende Prüfungen, beispielsweise im Rahmen von Release-Freigaben, zur Überprüfung der Einhaltung von Qualitätsstandards oder für Smoke-Tests nach einem Deployment.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;In traditionellen Modellen agieren Testteams oft isoliert und werden erst spät im Entwicklungsprozess involviert. Agile Methoden hingegen verstärken die kontinuierliche Zusammenarbeit und die gemeinsame Verantwortung des gesamten Teams für die Softwarequalität. Für Web-Projekte mit ihren typischerweise schnellen Release-Zyklen ist diese enge Verzahnung von Entwicklung und Test unerlässlich, um Qualität von Anfang an &amp;quot;einzubauen&amp;quot; (Built-in-Quality) anstatt sie nur am Ende &amp;quot;anzuprüfen&amp;quot;.&amp;amp;nbsp;&amp;lt;/p&amp;gt;&lt;br /&gt;
=== Dokumentation ===&lt;br /&gt;
&amp;lt;p&amp;gt;Eine “leichtgewichtige” Dokumentation in agilen Projekten bedeutet nicht, dass zu großen Teilen auf Dokumentation verzichtet wird. Vielmehr geht es um eine effektive und zweckdienliche Dokumentation. Agile Prinzipien warnen vor einer &amp;quot;umfassenden Dokumentation&amp;quot; als Selbstzweck. Dennoch ist ein gewisses Maß an Dokumentation für die Nachvollziehbarkeit, den Wissenstransfer und insbesondere bei regulierten Projekten oder komplexen Systemen unerlässlich. Test Charters, Mindmaps und Checklisten sind Beispiele dafür, wie Dokumentation schlank und wertstiftend gestaltet werden kann. Der wichtigste Aspekt aus Blick der Wirtschaftsinformatik liegt im Management des Trade-offs zwischen der gewünschten Agilität und der notwendigen Dokumentationstiefe. Es gilt, Tools und Methoden zu kennen und einzusetzen, die diesen Spagat unterstützen, mit dem Ziel einer &amp;quot;just enough&amp;quot; Dokumentation, die den Prozess effektiv unterstützt, statt ihn unnötig zu belasten.&amp;lt;/p&amp;gt;&lt;br /&gt;
= Testarten und -verfahren =&lt;br /&gt;
&amp;lt;p&amp;gt;Für die umfassende Qualitätssicherung von Web-Anwendungen steht ein breites Spektrum an Testarten und -verfahren zur Verfügung. Diese lassen sich grob in funktionale und nicht-funktionale Tests unterteilen und werden durch spezifische Testfallentwurfsmethoden konkretisiert.&amp;lt;/p&amp;gt;&lt;br /&gt;
== Funktionale Tests: Sicherstellung der korrekten Funktionsweise ==&lt;br /&gt;
&amp;lt;p&amp;gt;Funktionale Tests überprüfen, ob die Web-Anwendung die in den Anforderungen spezifizierten Funktionen korrekt und vollständig ausführt – sie beantworten die Frage: &amp;quot;Tut die Software das Richtige?&amp;quot;.56 Für den Entwurf effektiver funktionaler Testfälle kommen verschiedene Black-Box-Methoden nach ISTQB zum Einsatz:&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Äquivalenzklassenbildung: Dieses Verfahren reduziert die Anzahl der notwendigen Testfälle, indem Eingabedaten in Äquivalenzklassen gruppiert werden. Es wird davon ausgegangen, dass alle Werte innerhalb einer Klasse das gleiche Verhalten der Software hervorrufen.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Praxisbeispiel Web-Formular (Alterseingabe): Ein Eingabefeld für das Alter akzeptiert Werte zwischen 18 und 99 Jahren.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Gültige Äquivalenzklasse: Zahlen von 18 bis 99 (z.B. Test mit Repräsentant: 30).&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Ungültige Äquivalenzklassen: Zahlen kleiner als 18 (z.B. Repräsentant: 10), Zahlen größer als 99 (z.B. Repräsentant: 110), nicht-numerische Eingaben (z.B. Repräsentant: &amp;quot;ABC&amp;quot;), leere Eingabe.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;p&amp;gt;Grenzwertanalyse: Diese Methode ergänzt die Äquivalenzklassenbildung und konzentriert sich auf die &amp;quot;Ränder&amp;quot; oder Grenzen der definierten Äquivalenzklassen, da hier erfahrungsgemäß häufig Fehler auftreten.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Praxisbeispiel Web-Formular (Alterseingabe 18-99): Relevante Testwerte wären 17 (ungültig, untere Grenze -1), 18 (gültig, untere Grenze), 99 (gültig, obere Grenze) und 100 (ungültig, obere Grenze +1).&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;p&amp;gt;Entscheidungstabellentests: Dieses Verfahren ist besonders nützlich für die Überprüfung komplexer Geschäftsregeln, bei denen mehrere Bedingungen zu unterschiedlichen Aktionen oder Ergebnissen führen.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Praxisbeispiel Web-Shop Rabattsystem: Bedingungen könnten sein: Kundenstatus (z.B. Standard, Premium), Bestellwert (z.B. &amp;amp;lt;50€, 50-100€, &amp;amp;gt;100€) und Vorhandensein eines gültigen Gutscheincodes. Mögliche Aktionen wären: Kein Rabatt, 5% Rabatt, 10% Rabatt, Fehlermeldung bei ungültigem Gutschein. Jede Regel in der Entscheidungstabelle, die eine spezifische Kombination von Bedingungsausprägungen und die daraus resultierende Aktion darstellt, wird zu einem Testfall.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Zustandsübergangstests: Diese Technik modelliert und testet das Verhalten eines Systems, das sich aufgrund von Ereignissen oder Benutzeraktionen durch verschiedene Zustände bewegt.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Praxisbeispiel Benutzer-Login einer Web-Anwendung: Mögliche Zustände sind: &amp;quot;Abgemeldet&amp;quot;, &amp;quot;Anmeldeversuch&amp;quot; (Benutzername/Passwort eingegeben), &amp;quot;Angemeldet&amp;quot;, &amp;quot;Konto temporär gesperrt&amp;quot;. Ereignisse könnten sein: Eingabe gültiger Anmeldedaten, Eingabe ungültiger Anmeldedaten, dreimalige Falscheingabe, Anforderung &amp;quot;Passwort vergessen&amp;quot;. Aktionen wären beispielsweise die Weiterleitung zum Benutzer-Dashboard, die Anzeige einer Fehlermeldung oder die Sperrung des Kontos. Testfälle werden entworfen, um sowohl gültige Übergänge (z.B. erfolgreicher Login) als auch ungültige oder Fehlerübergänge (z.B. fehlgeschlagener Login, Sperrung des Kontos nach zu vielen Fehlversuchen) abzudecken.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Praxisbeispiele für funktionale Tests in verschiedenen Web-Anwendungen sind vielfältig:&amp;lt;/p&amp;gt;&lt;br /&gt;
*Benutzerverwaltung: Testen der Registrierung mit gültigen und ungültigen Daten, Login- und Logout-Prozesse, Passwort-Reset-Funktionen, Aktualisierung von Profilinformationen sowie die Überprüfung von Rollen- und Rechtekonzepten.&lt;br /&gt;
*Suchfunktion: Überprüfung der Suchergebnisse bei verschiedenen Suchkriterien, korrekte Anwendung von Filtern und Sortieroptionen, Verhalten der Anwendung bei keinen Suchtreffern und die Performance der Suche bei großen Datenmengen.&lt;br /&gt;
*Warenkorb &amp;amp; Bestellprozess (E-Commerce): Testen des Hinzufügens und Entfernens von Artikeln zum/aus dem Warenkorb, Mengenänderungen, korrekte Preisberechnung (inklusive Steuern, Versandkosten), Einlösung von Gutscheinen, Validierung von Lieferadressen, Simulation verschiedener Zahlungsabwicklungen und Überprüfung der Bestellbestätigung.&lt;br /&gt;
*Content Management Systeme (CMS): Testen der Erstellung, Bearbeitung und Löschung von Inhalten, Upload und Verwaltung von Mediendateien, Workflow-Tests (z.B. Freigabeprozesse für Artikel) und die korrekte Funktionsweise verschiedener Benutzerrollen im CMS.&lt;br /&gt;
*Online-Buchungssysteme (z.B. für Flüge, Hotels, Events): Überprüfung der Verfügbarkeitsanzeige, korrekte Auswahl von Optionen (z.B. Datum, Uhrzeit, Sitzplatz, Zimmerkategorie), Preisberechnungen (inkl. Zusatzleistungen), Versand von Buchungsbestätigungen und die Funktionalität von Stornierungsprozessen.&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
== Nicht-funktionale Tests: Absicherung der Qualitätseigenschaften ==&lt;br /&gt;
&amp;lt;p&amp;gt;Nicht-funktionale Tests bewerten, wie gut die Web-Anwendung ihre Funktionen ausführt. Sie orientieren sich an Qualitätsmerkmalen, wie sie beispielsweise in der Norm ISO/IEC 25010 definiert sind: Zuverlässigkeit, Benutzbarkeit (Usability), Effizienz (Performance), Wartbarkeit, Portabilität, Sicherheit und Kompatibilität.&amp;lt;/p&amp;gt;&lt;br /&gt;
=== Performancetests ===&lt;br /&gt;
&amp;lt;p&amp;gt;Performancetest untersuchen das Zeit- und Lastverhalten der Web-Anwendung.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Lasttests: Simulieren die erwartete maximale Benutzerlast, um sicherzustellen, dass die Anwendung die definierten Antwortzeitkriterien erfüllt und stabil bleibt. Ein typisches Szenario ist die Überprüfung eines E-Commerce-Shops während einer Black-Friday-Aktion, bei der beispielsweise 10.000 gleichzeitige Nutzer bedient werden müssen, wobei die Antwortzeiten unter zwei Sekunden bleiben sollen.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Stresstests: Setzen die Anwendung einer Last aus, die über die erwarteten Maximalwerte hinausgeht, um Systemgrenzen zu identifizieren und das Verhalten bei Überlast (z.B. kontrollierte Drosselung, Fehlermeldungen, Absturzverhalten) zu analysieren. Wie reagiert der Web-Shop, wenn plötzlich 20.000 Nutzer gleichzeitig zugreifen? Bricht er unkontrolliert zusammen oder werden Zugriffe geordnet verwaltet?&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Dauerlasttests (Endurance Tests): Überprüfen die Stabilität und Ressourcennutzung der Anwendung über einen längeren Zeitraum unter kontinuierlicher Last, um beispielsweise Speicherlecks (Memory Leaks) oder Performance-Degradation aufzudecken. Läuft ein internes Berichtsportal auch nach 24 Stunden Dauerbetrieb mit 500 Usern noch stabil und performant?&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
=== Usability-Tests ===&lt;br /&gt;
&amp;lt;p&amp;gt;Usability-Tests zielen darauf ab, eine positive und effiziente User Experience (UX) sicherzustellen.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Heuristische Evaluation: Usability-Experten bewerten die Benutzeroberfläche (UI) anhand anerkannter Usability-Prinzipien und -Heuristiken, um potenzielle Schwachstellen in der Benutzerführung zu identifizieren.100&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;A/B-Tests: Vergleichen zweier oder mehrerer Varianten einer Webseite oder eines Features (z.B. unterschiedliche Button-Farben, Texte für Call-to-Actions, Layout-Varianten), um empirisch zu ermitteln, welche Version besser bei den Nutzern ankommt oder zu höheren Konversionsraten führt.4&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Weitere Methoden: Beobachtung von Nutzern bei der Interaktion mit der Anwendung, Card Sorting zur Optimierung der Informationsarchitektur, Erstellung von Personas zur Veranschaulichung der Zielgruppen, Eye Tracking zur Analyse des Blickverhaltens, Fokusgruppen zur Erhebung von Meinungen und Wünschen sowie Online-Umfragen zur Sammlung quantitativen und qualitativen Feedbacks.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Kompatibilitätstests: Stellen sicher, dass die Web-Anwendung mit einer Vielzahl von Plattformen korrekt funktioniert und dargestellt wird.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Browserkompatibilität: Testen auf den gängigsten Webbrowsern (z.B. Google Chrome, Mozilla Firefox, Apple Safari, Microsoft Edge) und deren relevanten Versionen.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Betriebssystemkompatibilität: Überprüfung auf verschiedenen Betriebssystemen wie Windows, macOS, Linux sowie mobilen Betriebssystemen wie iOS und Android.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Gerätevielfalt: Testen auf unterschiedlichen Endgeräten, darunter Desktops, Laptops, Tablets und Smartphones mit verschiedenen Bildschirmgrößen und Auflösungen.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Responsive Design Testing: Überprüfung, ob sich das Layout und die Funktionalität der Web-Anwendung dynamisch und korrekt an verschiedene Bildschirmgrößen und Ausrichtungen (Hoch-/Querformat) anpassen. Zu den Herausforderungen gehören die enorme Vielfalt der Viewports, unterschiedliche Interaktionsmuster (Touch vs. Maus) und die Sicherstellung einer guten Performance auf mobilen Endgeräten. Techniken umfassen manuelles Testen auf echten Geräten, den Einsatz von Emulatoren und Simulatoren, die Nutzung von Browser-Entwicklertools zur Simulation verschiedener Geräte und automatisierte visuelle Regressionstests.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Barrierefreiheitstests (Accessibility Testing)&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Sicherstellung, dass Web-Anwendungen auch von Menschen mit unterschiedlichen Behinderungen (z.B. Seh-, Hör-, motorischen oder kognitiven Einschränkungen) uneingeschränkt genutzt werden können.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Die Grundlage hierfür bilden die international anerkannten Web Content Accessibility Guidelines (WCAG). Diese definieren vier Prinzipien, nach denen Webinhalte gestaltet sein sollten: Wahrnehmbar, Bedienbar, Verständlich und Robust.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Zunehmend machen auch gesetzliche Anforderungen, wie das Barrierefreiheitsgessetz (BFG) in Österreich bzw. die entsprechenden EU-Richtlinien, die Barrierefreiheit von Web-Anwendungen zur Pflicht.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Beispiele für Prüfpunkte: Sind ausreichende Farbkontraste zwischen Text und Hintergrund gegeben? Ist die gesamte Web-Anwendung per Tastatur bedienbar? Sind für alle Bilder aussagekräftige Alternativtexte hinterlegt? Werden für Videos Untertitel und gegebenenfalls Audiodeskriptionen angeboten?&amp;lt;/p&amp;gt;&lt;br /&gt;
== Auswahl und Priorisierung der Testarten ==&lt;br /&gt;
&amp;lt;p&amp;gt;Die Auswahl und Priorisierung der verschiedenen Testarten und -verfahren darf nicht willkürlich erfolgen, sondern muss sich stringent an den spezifischen Geschäftsrisiken und -zielen der jeweiligen Web-Anwendung orientieren. Da vollständiges Testen gemäß dem zweiten Testprinzip unmöglich ist und Ressourcen stets begrenzt sind, ist eine risikobasierte Herangehensweise entscheidend. Für eine E-Commerce-Anwendung beispielsweise sind die Performance unter hoher Last (z.B. während saisonaler Verkaufsaktionen) und die Sicherheit der Zahlungsdaten von existenzieller Bedeutung, da Fehler in diesen Bereichen direkten finanziellen Schaden und erheblichen Reputationsverlust nach sich ziehen können. Im Gegensatz dazu mag für ein internes Wissensmanagement-System die Usability und die Effektivität der Suchfunktion kritischer sein als die Bewältigung extremer Lastspitzen.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Eine weitere wichtige Erkenntnis ist, dass nicht-funktionale Anforderungen oft nur implizit im Raum stehen und aktiv durch den/die Entwickler*in in Zusammenarbeit mit den Stakeholdern explizit gemacht und in testbare Kriterien überführt werden müssen. Während funktionale Anforderungen (&amp;quot;Was soll das System tun?&amp;quot;) meist klarer spezifiziert sind, bleiben nicht-funktionale Anforderungen (&amp;quot;Wie gut soll das System etwas tun?&amp;quot;, z.B. bezüglich Performance, Usability, Sicherheit) häufig vage oder werden als selbstverständlich vorausgesetzt. Wenn diese nicht-funktionalen Aspekte nicht explizit definiert und systematisch getestet werden, kann eine Web-Anwendung zwar funktional korrekt sein, aber dennoch die Nutzererwartungen enttäuschen, im Betrieb versagen oder Sicherheitsrisiken bergen. Es gilt, diese impliziten Erwartungen aufzudecken (z.B. die vage Anforderung &amp;quot;Die Seite muss schnell laden&amp;quot; zu konkretisieren in &amp;quot;95% aller Seitenaufrufe müssen innerhalb von 2 Sekunden vollständig geladen sein&amp;quot;), in messbare und somit testbare Kriterien zu überführen und sicherzustellen, dass entsprechende nicht-funktionale Tests geplant und durchgeführt werden. Dies ist ein kritischer Beitrag zur Sicherstellung der Gesamtqualität und letztlich zum wirtschaftlichen Erfolg der Web-Anwendung.&amp;lt;/p&amp;gt;&lt;br /&gt;
== Sicherheit im Fokus: SAST, DAST und DevSecOps-Praktiken ==&lt;br /&gt;
&amp;lt;p&amp;gt;Die Sicherheit von Web-Anwendungen ist ein nicht zu vernachlässigender Aspekt der Qualitätssicherung, da diese Systeme oft direkt aus dem Internet erreichbar und somit potenziellen Angriffen ausgesetzt sind. Sicherheitslücken können schwerwiegende Folgen haben, darunter Datenverlust, finanzielle Schäden und einen massiven Reputationsverlust für das betroffene Unternehmen. Die OWASP Top 10 Liste, die regelmäßig die kritischsten Sicherheitsrisiken für Web-Anwendungen zusammenfasst, dient als wichtige Grundlage für die Planung und Durchführung von Sicherheitstests. Umfassende Sicherheitsprüfungen beinhalten typischerweise eine Kombination aus statischen und dynamischen Analyseverfahren sowie die Integration von Sicherheitspraktiken in den gesamten Entwicklungszyklus.&amp;lt;/p&amp;gt;&lt;br /&gt;
=== Static Application Security Testing (SAST): &amp;quot;White-Box&amp;quot;-Analyse des Codes ===&lt;br /&gt;
&amp;lt;p&amp;gt;SAST ist eine &amp;quot;White-Box&amp;quot;-Testmethode, die den Quellcode, Bytecode oder die Binärdateien einer Anwendung analysiert, ohne dass die Anwendung dafür ausgeführt werden muss.142 Der Hauptvorteil von SAST liegt in der Möglichkeit der frühen Fehlererkennung direkt im Entwicklungsprozess, oft schon während des Programmierens (Shift-Left-Prinzip). So können Schwachstellen identifiziert und behoben werden, bevor der Code überhaupt in eine Testumgebung oder gar in die Produktion gelangt.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Typische Schwachstellen, die durch SAST-Tools aufgedeckt werden können, sind beispielsweise Muster, die auf SQL-Injection oder Cross-Site-Scripting (XSS) hindeuten, Pufferüberläufe, unsichere Verwendung von APIs oder fehlerhafte Implementierungen kryptographischer Verfahren. Es gibt eine Vielzahl von SAST-Werkzeugen, sowohl kommerzielle als auch Open-Source-Lösungen, die gängige Web-Technologien wie JavaScript, Java, Python, PHP und andere unterstützen. Beispiele hierfür sind SonarQube, Checkmarx, Veracode, Snyk Code oder ESLint speziell für JavaScript-Code. Für eine kontinuierliche Sicherheitsüberprüfung ist die Integration von SAST-Scans in CI/CD-Pipelines unerlässlich. Dadurch erhalten Entwickler bei jedem Code-Commit oder Build automatisiertes und schnelles Feedback zu potenziellen Sicherheitsproblemen.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
=== Dynamic Application Security Testing (DAST): &amp;quot;Black-Box&amp;quot;-Analyse der laufenden Anwendung ===&lt;br /&gt;
&amp;lt;p&amp;gt;Im Gegensatz zu SAST ist DAST eine &amp;quot;Black-Box&amp;quot;-Testmethode, die die Web-Anwendung von außen im laufenden Zustand testet, ohne Einblick in den Quellcode zu haben. DAST-Tools simulieren typische Angriffsvektoren und -muster, um Schwachstellen aufzudecken. Der Vorteil von DAST liegt darin, dass es Laufzeit-Schwachstellen und Konfigurationsfehler finden kann, die SAST-Tools möglicherweise übersehen, da diese den Code nicht ausführen. DAST testet die Anwendung aus der Perspektive eines potenziellen Angreifers. Typische Funde von DAST-Analysen sind erfolgreich durchgeführte Cross-Site-Scripting-Angriffe durch manipulierte Eingaben, ausnutzbare SQL-Injection-Lücken in Formularfeldern, Probleme im Session-Management oder serverseitige Fehlkonfigurationen, die von außen sichtbar sind.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;p&amp;gt;Bekannte DAST-Werkzeuge sind beispielsweise der Open-Source Zed Attack Proxy (ZAP) von OWASP oder kommerzielle Tools wie Burp Suite, Invicti und Rapid7 InsightAppSec. Eine wesentliche Voraussetzung für DAST ist eine lauffähige Testumgebung, die der Produktionsumgebung möglichst nahekommt, um realistische Testergebnisse zu erzielen.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;DevSecOps: Sicherheit als integraler Bestandteil des gesamten Lebenszyklus&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;DevSecOps repräsentiert einen kulturellen und methodischen Ansatz, der Sicherheitsaktivitäten, -werkzeuge und eine sicherheitsbewusste Kultur in den gesamten DevOps-Prozess integriert. Das Ziel ist &amp;quot;Security by Design&amp;quot; und die Anwendung von &amp;quot;Shift Left&amp;quot;- (frühzeitige Sicherheitstests) und &amp;quot;Shift Right&amp;quot;-Prinzipien (kontinuierliches Sicherheitsmonitoring im Betrieb).&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Zu den Kernpraktiken von DevSecOps gehören:&amp;lt;/p&amp;gt;&lt;br /&gt;
*Die umfassende Automatisierung von Sicherheitstests (SAST, DAST, Software Composition Analysis/Dependency Scanning) direkt in den CI/CD-Pipelines.&lt;br /&gt;
*Die Absicherung der Infrastruktur durch &amp;quot;Infrastructure as Code&amp;quot; (IaC) Security-Praktiken.&lt;br /&gt;
*Kontinuierliches Monitoring und Logging von Sicherheitsereignissen in Test- und Produktionsumgebungen.&lt;br /&gt;
*Die Durchführung von Bedrohungsmodellierungen (Threat Modeling) bereits in frühen Designphasen, um potenzielle Angriffsvektoren zu identifizieren.&lt;br /&gt;
*Regelmäßige Sicherheitsschulungen für Entwickler, um das Bewusstsein für sichere Programmierpraktiken zu schärfen.&lt;br /&gt;
*Die Förderung einer engen Kollaboration und Kommunikation zwischen Entwicklungs-, Operations- und Sicherheitsteams, um Silos aufzubrechen. Das übergeordnete Ziel von DevSecOps ist die schnellere und gleichzeitig sicherere Auslieferung von Software durch die frühzeitige Erkennung und Behebung von Schwachstellen sowie die Etablierung einer nachhaltigen Sicherheitskultur im gesamten Unternehmen.&lt;br /&gt;
&amp;lt;p&amp;gt;Es ist wichtig zu verstehen, dass SAST und DAST keine konkurrierenden, sondern vielmehr komplementäre Ansätze darstellen, die im Rahmen einer umfassenden DevSecOps-Strategie optimal zusammenwirken. SAST analysiert den Code &amp;quot;von innen&amp;quot; und kann potenzielle Schwachstellen sehr früh im Entwicklungsprozess aufdecken, oft direkt in der Entwicklungsumgebung (IDE) oder als Teil der Continuous Integration Pipeline. Da SAST sprachabhängig ist, kann es tendenziell eine höhere Anzahl an False Positives generieren, also Warnungen, die sich bei genauerer Betrachtung nicht als echte Schwachstellen herausstellen. DAST hingegen testet die laufende Anwendung &amp;quot;von außen&amp;quot;, ist sprachunabhängig und findet Laufzeitfehler sowie Konfigurationsprobleme, benötigt aber eine lauffähige Anwendungsumgebung. SAST kann somit Fehler aufdecken, bevor eine Anwendung überhaupt lauffähig ist, während DAST erst dann zum Einsatz kommt. Eine effektive Sicherheitsstrategie, wie sie DevSecOps anstrebt, nutzt die Stärken beider Ansätze: SAST liefert frühes Feedback an die Entwickler, DAST prüft die Anwendung unter realitätsnahen Bedingungen in Staging- oder sogar (kontrollierten) Produktionsumgebungen. Für Studierende der Wirtschaftsinformatik ist das Verständnis entscheidend, dass nicht die Wahl eines einzelnen Tools, sondern die intelligente Kombination und nahtlose Integration verschiedener Sicherheitstesting-Ansätze in den gesamten Softwareentwicklungslebenszyklus den größten Nutzen für die Sicherheit von Web-Anwendungen stiftet.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Die bloße Einführung von SAST/DAST-Tools und die Deklaration von DevSecOps führen jedoch nicht automatisch zu mehr Sicherheit. Die Effektivität dieser Maßnahmen hängt maßgeblich von der gelebten Unternehmenskultur und der Bereitschaft zur Kollaboration zwischen allen beteiligten Teams ab. DevSecOps betont die gemeinsame Verantwortung für Sicherheit – &amp;quot;Security is everyone&amp;#039;s job&amp;quot;. Wenn Entwickler die Ergebnisse von Sicherheitsscans ignorieren, weil sie beispielsweise durch eine hohe Rate an False Positives frustriert sind, oder wenn Sicherheitsteams primär als Bremser und Blockierer neuer Entwicklungen wahrgenommen werden, bleiben die erhofften Verbesserungen aus. Eine erfolgreiche Implementierung erfordert daher begleitende Maßnahmen wie zielgerichtete Schulungen, klare und nachvollziehbare Prozesse für den Umgang mit identifizierten Schwachstellen (z.B. Priorisierung, Triage, Verantwortlichkeiten) und eine Kultur, in der Sicherheit als gemeinsames Ziel und integraler Bestandteil von Qualität verstanden und gelebt wird.&amp;amp;nbsp;&amp;lt;/p&amp;gt;&lt;br /&gt;
= Testautomatisierung und Testdatenmanagement =&lt;br /&gt;
&amp;lt;p&amp;gt;Die Testautomatisierung und ein sorgfältiges Testdatenmanagement sind entscheidende Säulen für eine effiziente und effektive Qualitätssicherung von Web-Anwendungen, insbesondere in agilen Umfeldern mit kurzen Release-Zyklen.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Die Testautomatisierungspyramide: Eine Strategie für Testautomatisierung&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Das Konzept der Testautomatisierungspyramide, ursprünglich von Mike Cohn vorgestellt und durch Martin Fowler popularisiert, bietet eine Leitlinie für die Verteilung von Testautomatisierungsaufwänden auf verschiedenen Ebenen.&amp;lt;/p&amp;gt;[[Datei:Fowler-testPyramid.png|300px|thumb|Test Pyramid]]&amp;lt;p&amp;gt;Die Testautomatisierungspyramide ist ein Modell, dessen Implementierung jedoch stark von der Testbarkeit der zugrundeliegenden Anwendungsarchitektur abhängt. Die Pyramide empfiehlt eine große Anzahl von Unit- und API-Tests und nur wenige UI-Tests. Unit-Tests erfordern gut isolierbare Code-Module, während API-Tests klar definierte und stabile Schnittstellen voraussetzen. Wenn eine Web-Anwendung beispielsweise monolithisch aufgebaut ist, mit stark verflochtenen Komponenten und ohne klar getrennte API-Schichten (im Gegensatz zu z.B. Microservice-Architekturen), wird es schwierig, Tests auf den unteren, schnellen Ebenen der Pyramide effektiv zu implementieren. Dies kann dazu führen, dass Teams gezwungen sind, sich stärker auf die langsameren und instabileren UI-Tests zu verlassen, was dem eigentlichen Ziel der Pyramide widerspricht und oft als Anti-Pattern (&amp;quot;Eistüte&amp;quot; oder &amp;quot;Testkrabbe&amp;quot;) bezeichnet wird.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;p&amp;gt;An der Basis der Pyramide befinden sich Unit-Tests (Komponententests). Dies sind zahlreiche, sehr schnell auszuführende und isolierte Tests, die einzelne, kleine Code-Einheiten wie Funktionen, Methoden oder Klassen überprüfen. Sie werden in der Regel von den Entwicklern selbst geschrieben und bieten ein sehr schnelles Feedback über die Korrektheit des Codes auf niedrigster Ebene.163 Ein Beispiel für eine Web-Anwendung in JavaScript wäre das Testen einer Funktion, die Benutzereingaben in einem Formular validiert 163, oder einer einzelnen UI-Komponente, die für die Darstellung eines bestimmten Teils der Benutzeroberfläche zuständig ist.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;In der mittleren Schicht der Pyramide sind API-Tests, Integrations- oder Service-Tests angesiedelt. Es gibt weniger davon als Unit-Tests, und sie prüfen das korrekte Zusammenspiel verschiedener Komponenten oder Dienste über deren definierte Schnittstellen (APIs), typischerweise ohne die grafische Benutzeroberfläche (UI) einzubeziehen. Diese Tests sind schneller und stabiler als UI-Tests.167 Für eine E-Commerce-Web-Anwendung könnten dies Tests sein, die API-Endpunkte für den Produktabruf, die Aktualisierung des Warenkorbs oder die Aufgabe einer Bestellung verifizieren. Auch die Simulation der Interaktion zwischen einem Frontend und verschiedenen Backend-Microservices fällt in diese Kategorie.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;An der Spitze der Pyramide stehen die UI-Tests (End-to-End-Tests). Davon sollte es am wenigsten geben, da sie tendenziell langsam in der Ausführung, aufwendig in der Erstellung und Wartung sowie anfälliger für Instabilitäten (sogenannte &amp;quot;flaky tests&amp;quot;) sind. UI-Tests simulieren komplette Benutzer-Workflows über die grafische Benutzeroberfläche und prüfen das integrierte System aus der Endbenutzerperspektive. Ein Beispiel für eine E-Commerce-Anwendung wäre das Durchtesten des gesamten Kaufprozesses: von der Produktsuche über das Hinzufügen zum Warenkorb, die Eingabe von Adress- und Zahlungsdaten bis hin zur Anzeige der Bestellbestätigungsseite. Der praktische Nutzen dieser Pyramidenstrategie liegt im Fokus auf einer breiten Basis von schnellen, kostengünstigen und stabilen Unit- und API-Tests, die ein schnelles Feedback ermöglichen, ergänzt durch eine gezielte, geringere Anzahl von umfassenderen UI-Tests, um die wichtigsten End-to-End-Szenarien abzusichern.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Vorteile, Nachteile und ROI der Testautomatisierung für Web-Anwendungen:&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Die Testautomatisierung bietet verschiedene Vorteile: schnellere Testdurchführung (insbesondere bei Regressionstests), Konsistenz und Wiederholbarkeit der Tests, potenziell höhere Testabdeckung, früheres Feedback an die Entwicklung, Entlastung manueller Tester für komplexere Aufgaben wie exploratives Testen oder anspruchsvolle Usability-Bewertungen und die Ermöglichung von Continuous Integration und Continuous Delivery (CI/CD).&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Dem stehen jedoch auch Nachteile gegenüber: hohe Initialkosten für die Auswahl und Implementierung von Tools, die Entwicklung von Testautomatisierungs-Frameworks und die Erstellung der initialen Testskripte. Der Wartungsaufwand für Testskripte, besonders bei häufigen Änderungen an der UI, kann erheblich sein. Zudem erfordert Testautomatisierung spezialisierte Kenntnisse und Fähigkeiten im Team, und nicht alle Testarten sind sinnvoll oder wirtschaftlich automatisierbar (z.B. explorative Tests, bestimmte Aspekte der Usability oder Tests, die stark von menschlicher Intuition abhängen).&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;p&amp;gt;Der Return on Investment (ROI) der Testautomatisierung ist eine wichtige Kennzahl, um die Wirtschaftlichkeit von Automatisierungsbemühungen zu bewerten. Die Grundformel lautet: ROI=Kosten der Automatisierung (Einsparungen durch Automatisierung−Kosten der Automatisierung)​⋅100. Die Einsparungen ergeben sich primär aus reduzierter manueller Testzeit, schnellerer Fehlererkennung (was zu geringeren Behebungskosten führt) und potenziell schnelleren Release-Zyklen. Die Kosten umfassen Tool-Lizenzen, Infrastrukturaufwendungen sowie die Entwicklungs- und Wartungszeit für die automatisierten Testskripte.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Fallstudien und Beispiele, insbesondere im E-Commerce-Bereich, zeigen, dass die Automatisierung von umfangreichen Regressionstest-Suiten für kritische Prozesse wie den Checkout bei häufigen Releases erhebliche manuelle Aufwände einsparen und gleichzeitig sicherstellen kann, dass die Kernfunktionalitäten stabil bleiben und keine neuen Fehler eingeschleppt werden.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
== Auswahl von Testautomatisierungswerkzeugen (UI und API) ==&lt;br /&gt;
&amp;lt;p&amp;gt;Die Auswahl geeigneter Werkzeuge ist entscheidend für den Erfolg der Testautomatisierung. Wichtige Kriterien sind: die Unterstützung der im Projekt verwendeten Technologien (Programmiersprachen, Browser, Frameworks), die vorhandenen Skills im Team (code-basierte Tools erfordern Programmierkenntnisse, während Low-Code/No-Code-Plattformen auch für Fachanwender zugänglicher sein können), das verfügbare Budget (Open-Source-Lösungen versus kommerzielle Produkte), die Skalierbarkeit der Lösung für wachsende Testumfänge, die Integrationsfähigkeit in bestehende CI/CD-Pipelines und Testmanagement-Systeme, aussagekräftige Reporting-Funktionen sowie die Verfügbarkeit von Community-Support und Dokumentation.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;p&amp;gt;Beispiele für UI-Testautomatisierungstools:&amp;lt;/p&amp;gt;&lt;br /&gt;
*Selenium: Ein weit verbreitetes Open-Source-Framework, das eine breite Palette von Programmiersprachen (Java, Python, C#, JavaScript etc.) und Browsern unterstützt. Es bietet hohe Flexibilität und verfügt über eine große, aktive Community.&lt;br /&gt;
*Cypress: Ein modernes, JavaScript-basiertes End-to-End-Testing-Framework, das für seine Entwicklerfreundlichkeit, schnelle Ausführung und gute Debugging-Möglichkeiten bekannt ist.&lt;br /&gt;
*Playwright: Ein von Microsoft entwickeltes Tool, das ebenfalls mehrere Programmiersprachen unterstützt und durch eine moderne Architektur sowie gute Performance bei Cross-Browser-Tests überzeugt.&lt;br /&gt;
&amp;lt;p&amp;gt;Beispiele für API-Testautomatisierungstools:&amp;lt;/p&amp;gt;&lt;br /&gt;
*Postman: Ein populäres Tool mit einer grafischen Benutzeroberfläche, das sich gut für manuelle und explorative API-Tests sowie für die einfache Automatisierung von API-Testabläufen eignet.&lt;br /&gt;
*REST Assured: Eine Java-Bibliothek, die sich besonders gut für die Integration von API-Tests in Java-basierte Projekte eignet und eine hohe Flexibilität für komplexe Testszenarien bietet.&lt;br /&gt;
&amp;lt;p&amp;gt;Moderne Ansätze im Testing: Der Einfluss von Künstlicher Intelligenz (KI)&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Künstliche Intelligenz und Machine Learning (ML) gewinnen auch im Bereich Softwaretests zunehmend an Bedeutung und bieten neue Möglichkeiten zur Effizienzsteigerung und Qualitätsverbesserung:&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;KI-gestützte Testfallgenerierung: ML-Modelle können dazu verwendet werden, Anforderungen, User Stories oder bestehenden Code zu analysieren, um daraus automatisch Testfälle zu generieren oder Vorschläge für Testfälle zu optimieren.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Visuelle Tests mit KI: KI-basierte Tools können automatisch visuelle Abweichungen in der Benutzeroberfläche erkennen (z.B. Layoutverschiebungen, Farbänderungen, fehlende Elemente), die von rein funktionalen Tests oft nicht erfasst werden.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;p&amp;gt;Self-Healing Automation (Selbstreparierende Testautomatisierung): KI-Algorithmen sind in der Lage, Änderungen in der UI einer Web-Anwendung (z.B. geänderte IDs oder XPath-Lokalisierer von Elementen) während der Testausführung zu erkennen und die Testskripte automatisch anzupassen. Dies kann den Wartungsaufwand für automatisierte Tests signifikant reduzieren.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Testoptimierung durch ML: Durch die Analyse von historischen Testausführungsprotokollen und Fehlerdaten können ML-Modelle dabei helfen, redundante Testfälle zu identifizieren, die Testausführung zu priorisieren (z.B. Tests für fehleranfällige Bereiche zuerst ausführen) oder Vorhersagen über die Fehlerwahrscheinlichkeit in bestimmten Modulen zu treffen.&amp;lt;/p&amp;gt;&lt;br /&gt;
== Testdatenmanagement ==&lt;br /&gt;
&amp;lt;p&amp;gt;Qualitativ hochwertige und aussagekräftige Testdaten sind eine essenzielle Voraussetzung für effektive Tests. Die Verwendung von Echtdaten aus Produktivsystemen ist jedoch aus Datenschutzgründen, insbesondere im Hinblick auf die Datenschutz-Grundverordnung (DSGVO/GDPR), oft problematisch oder schlichtweg verboten.209&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Folgende Techniken ermöglichen ein DSGVO-konformes Testdatenmanagement:&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Anonymisierung/Pseudonymisierung: Personenbezogene Daten werden so entfernt oder verfremdet, dass kein direkter Rückschluss auf einzelne Individuen mehr möglich ist. Es ist jedoch zu beachten, dass eine echte, unwiderrufliche Anonymisierung schwer zu erreichen ist und pseudonymisierte Daten unter Umständen weiterhin unter den Anwendungsbereich der DSGVO fallen können.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Generierung synthetischer Testdaten: Hierbei werden künstliche, aber realistisch wirkende Datensätze erstellt. Diese Daten spiegeln die statistischen Eigenschaften und die Struktur von Echtdaten wider, enthalten aber keine realen Personenbezüge und sind somit datenschutzrechtlich unbedenklich.&lt;/div&gt;</summary>
		<author><name>Völkl Anna</name></author>
	</entry>
	<entry>
		<id>https://mediawiki.fernfh.ac.at/mediawiki/index.php?title=Datei:Fowler-testPyramid.png&amp;diff=6588</id>
		<title>Datei:Fowler-testPyramid.png</title>
		<link rel="alternate" type="text/html" href="https://mediawiki.fernfh.ac.at/mediawiki/index.php?title=Datei:Fowler-testPyramid.png&amp;diff=6588"/>
		<updated>2025-05-30T14:47:46Z</updated>

		<summary type="html">&lt;p&gt;Völkl Anna: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Test Pyramid by Martin Fowler&lt;/div&gt;</summary>
		<author><name>Völkl Anna</name></author>
	</entry>
	<entry>
		<id>https://mediawiki.fernfh.ac.at/mediawiki/index.php?title=Performance-Optimierung_(Webdevelopment)&amp;diff=6587</id>
		<title>Performance-Optimierung (Webdevelopment)</title>
		<link rel="alternate" type="text/html" href="https://mediawiki.fernfh.ac.at/mediawiki/index.php?title=Performance-Optimierung_(Webdevelopment)&amp;diff=6587"/>
		<updated>2025-05-27T19:37:34Z</updated>

		<summary type="html">&lt;p&gt;Völkl Anna: CDN überarbeitet&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Definition und Kritikalität der Web-Performance =&lt;br /&gt;
&amp;lt;p&amp;gt;Web-Performance bezeichnet die objektive Messung und subjektive Nutzerwahrnehmung der Geschwindigkeit und Zuverlässigkeit von Webanwendungen. Sie umfasst, wie schnell Inhalte laden, interaktiv werden und auf Benutzereingaben reagieren. Die Kritikalität der Web-Performance ergibt sich aus ihrem direkten Einfluss auf die Nutzerzufriedenheit, das Engagement und letztendlich den Geschäftserfolg. In der heutigen digitalen Landschaft erwarten Nutzer schnelle und nahtlose Erlebnisse; ein Nichterfüllen dieser Erwartungen kann zu Abwanderung und verpassten Chancen führen.&amp;amp;nbsp;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;Die anfängliche Performance-Erfahrung kann eine nachhaltig negative Voreingenommenheit gegenüber einer Marke erzeugen, wodurch zukünftige positive Interaktionen weniger wirkungsvoll werden. Umgekehrt baut eine konstant gute Performance im Laufe der Zeit Vertrauen und Loyalität auf. Eine langsame oder schlecht performende Webseite führt oft zu einer höheren Absprungrate und kann folglich auch eine negative Wahrnehmung Ihrer Marke verursachen.&amp;amp;nbsp;&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Messung der Performance =&lt;br /&gt;
=== Verständnis der Core Web Vitals (CWV) ===&lt;br /&gt;
&amp;lt;p&amp;gt;Die Core Web Vitals (CWV) sind eine Reihe von nutzerzentrierten Metriken von Google, die entwickelt wurden, um Schlüsselaspekte der Nutzererfahrung zu messen: Laden, Interaktivität und visuelle Stabilität.&amp;lt;/p&amp;gt;&lt;br /&gt;
*Largest Contentful Paint (LCP): Misst die Ladeleistung – die Zeit, die benötigt wird, bis das größte Inhaltselement (z.B. ein Bild oder ein Textblock) im Ansichtsfenster sichtbar wird. Ein guter LCP-Wert liegt bei ≤2.5 Sekunden. Dies beeinflusst direkt die Wahrnehmung des Nutzers, wie schnell der Hauptinhalt lädt.&lt;br /&gt;
*Interaction to Next Paint (INP): Misst die Reaktionsfähigkeit – die Latenz aller Klick-, Tipp- und Tastaturinteraktionen während des gesamten Besuchs eines Nutzers auf einer Seite, wobei die längste Interaktion (unter Auslassung von Ausreißern) gemeldet wird. INP hat First Input Delay (FID) ersetzt. Ein guter INP-Wert liegt bei ≤200 Millisekunden. Dies ist entscheidend dafür, wie reaktionsschnell und flüssig sich die Anwendung bei Nutzerinteraktionen anfühlt. FID maß nur die Verzögerung der ersten Eingabe, während INP eine umfassendere Sicht auf die gesamte Interaktivität bietet.&lt;br /&gt;
*Cumulative Layout Shift (CLS): Misst die visuelle Stabilität – die Gesamtsumme aller einzelnen Layout-Shift-Scores für jede unerwartete Layoutverschiebung, die während der gesamten Lebensdauer der Seite auftritt. Ein guter CLS-Wert liegt bei ≤0.1. Dies adressiert die Nutzerfrustration, die durch unerwartet auf der Seite verschobene Elemente verursacht wird und oft zu Fehlklicks führt. CLS wird durch die Impact Fraction (Anteil des betroffenen Bildschirms) und die Distance Fraction (wie weit sich ein Element verschoben hat) gemessen.&lt;br /&gt;
&lt;br /&gt;
=== Performance-Messwerkzeuge: Google PageSpeed Insights, Lighthouse und Real User Monitoring (RUM) ===&lt;br /&gt;
&lt;br /&gt;
*Google PageSpeed Insights (PSI): Liefert sowohl Labordaten (über Lighthouse) als auch Felddaten (aus dem Chrome User Experience Report - CrUX) für eine bestimmte URL. Bietet Performance-Scores und Vorschläge.&lt;br /&gt;
*Lighthouse: Ein Open-Source, automatisiertes Werkzeug zur Verbesserung der Qualität von Webseiten. Prüft Performance, Barrierefreiheit, PWA, SEO usw. in einer simulierten (Labor-)Umgebung.&lt;br /&gt;
*Real User Monitoring (RUM) / Chrome User Experience Report (CrUX):&lt;br /&gt;
*RUM sammelt Leistungsdaten von tatsächlichen Nutzern, die mit der Webseite in Echtzeit interagieren, und spiegelt dabei unterschiedliche Netzwerkbedingungen, Geräte und geografische Standorte wider.&lt;br /&gt;
*CrUX ist ein öffentlicher Datensatz von Google, der widerspiegelt, wie reale Chrome-Nutzer beliebte Ziele im Web erleben, und liefert Felddaten für Core Web Vitals.&lt;br /&gt;
*Diskrepanzen zwischen Labor- (Lighthouse/PSI Labor) und Felddaten (CrUX/RUM):&lt;br /&gt;
&amp;lt;p&amp;gt;Die Labordaten stammen aus einer kontrollierten, simulierten Umgebung. Felddaten repräsentieren aggregierte reale Nutzererfahrungen über einen Zeitraum (z.B. 28 Tage für CrUX).&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;Faktoren, die Unterschiede verursachen:&amp;lt;/p&amp;gt;&lt;br /&gt;
*Caching: Echte Nutzer profitieren vom Browser-Caching bei wiederholten Besuchen oder der Navigation innerhalb der Seite, was Labortests (insbesondere Kaltstarts) nicht immer widerspiegeln.&lt;br /&gt;
*Nutzerbedingungen: Netzwerkqualität, Gerätefähigkeiten und geografischer Standort variieren bei echten Nutzern erheblich im Vergleich zu standardisierten Labortests.&lt;br /&gt;
*Inhaltsvariationen: Cookie-Banner, A/B-Tests oder personalisierte Inhalte können sich zwischen Labortests und dem, was echte Nutzer sehen, unterscheiden.&lt;br /&gt;
*Above-the-Fold vs. vollständige Seiteninteraktion: Lighthouse konzentriert sich primär auf den initialen Ladevorgang, während CrUX Metriken wie CLS und INP über den gesamten Lebenszyklus der Seite erfasst, einschließlich Verschiebungen und Interaktionen nach dem Laden.&lt;br /&gt;
&lt;br /&gt;
= Client-seitige Performance-Optimierung =&lt;br /&gt;
== JavaScript-Optimierung ==&lt;br /&gt;
=== Minimierung der DOM-Manipulation ===&lt;br /&gt;
&amp;lt;p&amp;gt;Das Document Object Model (DOM) repräsentiert eine Webseite als Baum von Objekten. Häufige direkte Manipulationen sind kostspielig, da sie Browser-Reflows (Neuberechnung des Layouts) und Repaints (Neuzeichnen von Teilen der Seite) auslösen können. Techniken zur Minimierung umfassen das Stapeln von DOM-Updates und die Verwendung von Document Fragments, um Änderungen außerhalb des DOM vorzunehmen, bevor sie angehängt werden. Der Virtual DOM (z.B. in React, Vue) ist eine Abstraktion, bei der Änderungen zuerst auf eine In-Memory-Repräsentation (Virtual DOM) angewendet werden. Ein &amp;quot;Diffing&amp;quot;-Algorithmus identifiziert dann minimale Änderungen, die am tatsächlichen DOM erforderlich sind, und optimiert so die Updates. Dies verbessert die Performance dynamischer Anwendungen mit häufigen UI-Aktualisierungen erheblich.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
=== Code-Minifizierung, Tree Shaking und Code Splitting ===&lt;br /&gt;
&lt;br /&gt;
*Minifizierung: Entfernt unnötige Zeichen (Leerzeichen, Kommentare) aus HTML-, CSS- und JavaScript-Dateien, um deren Größe zu reduzieren, was zu schnelleren Downloads und geringerem Bandbreitenverbrauch führt.&amp;amp;nbsp;&lt;br /&gt;
*Tree Shaking (Dead Code Elimination): Ein Prozess, der typischerweise von Modul-Bundlern (z.B. webpack, Rollup) durchgeführt wird und ungenutzten JavaScript-Code entfernt, indem import- und export-Anweisungen in ES6-Modulen analysiert werden. Dies reduziert die endgültige Bundle-Größe.&lt;br /&gt;
*Code Splitting: Zerlegt große JavaScript-Bundles in kleinere Chunks. Nur der für die initiale Ansicht notwendige Code wird geladen; andere Chunks werden bei Bedarf geladen (z.B. bei Navigation oder Interaktion). Dies verbessert die initiale Ladezeit.&lt;br /&gt;
&lt;br /&gt;
=== Nutzung von Web Workern für Off-Thread-Ausführung ===&lt;br /&gt;
&amp;lt;p&amp;gt;Web Worker ermöglichen die Ausführung von JavaScript in Hintergrund-Threads, getrennt vom Haupt-Browser-Thread, der UI-Updates und Nutzerinteraktionen behandelt.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;p&amp;gt;Anwendungsfälle umfassen das Auslagern CPU-intensiver Aufgaben wie komplexe Berechnungen, Datenverarbeitung oder Bildmanipulation, um ein Einfrieren der UI zu verhindern und die Reaktionsfähigkeit (INP) zu verbessern. Web Worker können nicht direkt auf das DOM zugreifen; die Kommunikation mit dem Haupt-Thread erfolgt über postMessage(). Im Gegensatz zu Service Workern, die als Netzwerk-Proxies für Caching, Offline-Support und Push-Benachrichtigungen dienen, sind Web Worker für allgemeine Hintergrundaufgaben zur Verbesserung der UI-Reaktionsfähigkeit gedacht.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
== Optimierung des kritischen Rendering-Pfads ==&lt;br /&gt;
=== Critical CSS: Generierung und Inline-Implementierung ===&lt;br /&gt;
&amp;lt;p&amp;gt;Critical CSS ist der minimale Teil von CSS, der erforderlich ist, um den Above-the-Fold-Inhalt einer Seite darzustellen - also alles, was der Benutzer beim Laden der Seite auf einem Bildschirm sehen kann, ohne zu scrollen. Sein Zweck ist es, renderblockierendes CSS für die initiale Ansicht zu eliminieren, wodurch der Browser Pixel viel schneller auf den Bildschirm malen kann, was FCP und LCP verbessert. Generierungsmethoden umfassen manuelle Extraktion (zeitaufwendig), Online-Generatoren (einfacher, aber möglicherweise nicht perfekt optimiert) und Tools/Bibliotheken wie Critical oder Penthouse (automatisiert, integrierbar in Build-Prozesse). Die Implementierung erfolgt durch Inline-Einbettung des Critical CSS in &amp;amp;lt;style&amp;amp;gt;-Tags im &amp;amp;lt;head&amp;amp;gt; des HTML-Dokuments.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
=== Asynchrones Laden von nicht-kritischem CSS und JavaScript ===&lt;br /&gt;
&amp;lt;p&amp;gt;Nicht-kritisches CSS sind Stile, die nicht für das initiale Above-the-Fold-Rendering benötigt werden. Ladetechniken für CSS umfassen die Verwendung von &amp;amp;lt;link rel=&amp;quot;preload&amp;quot; as=&amp;quot;style&amp;quot; onload=&amp;quot;this.onload=null;this.rel=&amp;#039;stylesheet&amp;#039;&amp;quot;&amp;amp;gt; oder die Verwendung von media=&amp;quot;print&amp;quot; und das Austauschen zu media=&amp;quot;all&amp;quot; beim Laden. Nicht-kritisches JavaScript sind Skripte, die nicht für das initiale Seitenrendering oder die Interaktivität unerlässlich sind. Ladetechniken für JS umfassen die Attribute async und defer bei &amp;amp;lt;script&amp;amp;gt;-Tags. async lädt das Skript asynchron herunter und führt es aus, sobald es heruntergeladen ist, möglicherweise unterbricht es das HTML-Parsing. defer lädt das Skript asynchron herunter, führt es aber erst nach Abschluss des HTML-Parsings aus und in der Reihenfolge, in der sie im Dokument erscheinen.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
=== Vorladen von Schlüsselressourcen (rel=&amp;quot;preload&amp;quot;) ===&lt;br /&gt;
&amp;lt;p&amp;gt;&amp;amp;lt;link rel=&amp;quot;preload&amp;quot;&amp;amp;gt; informiert den Browser, eine Ressource mit hoher Priorität abzurufen, da sie bald benötigt wird, typischerweise für Ressourcen, die vom Browser spät entdeckt werden. Der Browser ruft die Ressource ab und speichert sie im Cache, führt jedoch Skripte nicht aus oder wendet Stylesheets nicht sofort an. Anwendungsfälle umfassen CSS-Dateien, JavaScript-Dateien (kritische Chunks), Web-Fonts (erfordert crossorigin-Attribut) und Bilder (kritische Above-the-Fold-Bilder). Die Implementierung erfolgt über &amp;amp;lt;link rel=&amp;quot;preload&amp;quot; href=&amp;quot;critical.js&amp;quot; as=&amp;quot;script&amp;quot;&amp;amp;gt;, wobei das as-Attribut (z.B. style, script, font, image) entscheidend für die korrekte Priorisierung ist. Preloading kann LCP (für Fonts, Bilder) und INP verbessern und helfen, CLS (für Fonts) zu reduzieren. Es sollten nur kritische Ressourcen vorgeladen werden, da ein Überladen die Performance durch Ressourcenkonkurrenz beeinträchtigen kann.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
== Asset-Optimierung ==&lt;br /&gt;
=== Bildoptimierung ===&lt;br /&gt;
&amp;lt;p&amp;gt;Bilder machen oft den größten Teil des Seitengewichts aus. Moderne Formate wie WebP (bessere Kompression als JPEG/PNG, unterstützt Transparenz und Animation) und AVIF (noch effizienter als WebP, lizenzfrei) sollten bevorzugt werden. Das &amp;amp;lt;picture&amp;amp;gt;-Element kann für Art Direction und Fallbacks verwendet werden. Die Kompression sollte zwischen Dateigröße und visueller Qualität abgewogen werden (verlustbehaftet vs. verlustfrei). Responsive Bilder sollten mit srcset und sizes für verschiedene Bildschirmgrößen und Auflösungen bereitgestellt werden. Native Lazy Loading (loading=&amp;quot;lazy&amp;quot;) für Bilder unterhalb des Falzes verbessert die initiale Ladezeit. Die Angabe von width- und height-Attributen bei &amp;amp;lt;img&amp;amp;gt;-Tags verhindert Layoutverschiebungen (CLS).&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
=== Web-Font-Optimierung ===&lt;br /&gt;
&amp;lt;p&amp;gt;Priorisieren Sie WOFF2 wegen seiner überlegenen Kompression und breiten Browserunterstützung, mit WOFF als Fallback. Die CSS-Eigenschaft font-display (z.B. swap, fallback, optional) steuert das Rendering-Verhalten während des Ladens und verhindert FOUT/FOIT. Font-Subsetting, d.h. das Einbeziehen nur der benötigten Zeichen, reduziert die Dateigröße drastisch. Kritische Fonts sollten mit &amp;amp;lt;link rel=&amp;quot;preload&amp;quot;&amp;amp;gt; vorgeladen werden. Selbsthosting bietet mehr Kontrolle über Caching, während Drittanbieterdienste einfach zu verwenden sind und Fonts möglicherweise bereits im Browser des Nutzers zwischengespeichert sind.&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;Die Optimierung von Bildern und Schriftarten ist keine isolierte Aufgabe, sondern eine entscheidende Voraussetzung für gute LCP-, CLS- und sogar INP-Werte. Diese Assets sind oft die direkten Kandidaten für diese Metriken oder beeinflussen stark das Rendering und die Interaktivität von Elementen, die es sind. Das Largest Contentful Element ist häufig ein Bild oder ein großer Textblock. Optimierte Bilder laden schneller. Vorgeladene und optimierte Schriftarten stellen sicher, dass Text schnell und korrekt gerendert wird. Nicht dimensionierte Bilder sind eine Hauptursache für Layoutverschiebungen. Web-Schriftarten, die mit signifikanten Unterschieden zu Fallback-Schriftarten geladen werden, verursachen ebenfalls CLS.&amp;amp;nbsp;&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Server-seitige und Netzwerk-Performance-Optimierung =&lt;br /&gt;
== Content Delivery Networks (CDNs) ==&lt;br /&gt;
&amp;lt;p&amp;gt;Ein CDN ist ein geografisch verteiltes Netzwerk von Proxy-Servern, das statische Inhalte (Bilder, CSS, JS) in Servern (Points of Presence - PoPs) näher am Endnutzer zwischenspeichert. Anfragen werden vom nächstgelegenen PoP bedient, was die Latenz reduziert.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== CDN - Vor- und Nachteile ===&lt;br /&gt;
&amp;lt;p&amp;gt;Content Delivery Networks (CDNs) bieten viele Vorteile, die die Leistung und Zuverlässigkeit von Websites und Online-Anwendungen stark verbessern können. Der oft bedeutendste Vorteil sind schnellere Ladezeiten. Das wird durch die geografische Nähe der CDN-Server zu den Nutzern erreicht, wodurch die Latenz, also die Verzögerungszeit bei der Datenübertragung, reduziert wird.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Ein weiterer wichtiger Punkt ist die erhöhte Verfügbarkeit. Durch Lastverteilung können CDNs Traffic-Spitzen effektiv bewältigen und bieten Redundanz, falls der Server der Web-Anwendung (zB Online-Shop) ausfallen sollte.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Auch die verbesserte Sicherheit ist ein starker Pluspunkt. Viele CDNs bieten Schutzmechanismen gegen DDoS-Angriffe (Distributed Denial of Service) und stellen Web Application Firewalls (WAF) zur Verfügung, um die Sicherheit der Webanwendung zu erhöhen. Schließlich ermöglichen CDNs eine hohe Skalierbarkeit, da sie in der Lage sind, große Mengen an Traffic zu bewältigen und sich flexibel an neue Bandbreitenanforderungen anzupassen.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Zu den Nachteilen eines CDNs gehören die Kosten: Die Nutzung von CDN-Diensten ist teilweise mit (hohen) Kosten verbunden, obwohl auch kostenlose Tarife existieren, die jedoch oft eingeschränkte Funktionalitäten bieten. Die Einrichtung und Verwaltung eines CDNs kann zudem die Komplexität der IT-Infrastruktur erhöhen.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Ein weiterer Aspekt ist der teilweise Kontrollverlust, da Daten auf den Servern von Drittanbietern gespeichert werden, was für manche Organisationen Bedenken hinsichtlich der Datensicherheit und -hoheit aufwerfen kann. Bei dynamischen Inhalten stoßen CDNs an ihre Grenzen, da sie primär für statische Inhalte wie Bilder, Videos oder CSS-Dateien optimiert sind. Dynamische Inhalte erfordern meist direkte Anfragen an den Ursprungsserver,.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Die Cache-Invalidierung, also die Sicherstellung, dass die zwischengespeicherten Inhalte stets aktuell sind, kann eine Herausforderung darstellen. Veraltete Inhalte im Cache können zu einer inkonsistenten Benutzererfahrung führen. Schließlich können falsch konfigurierte CDNs zu SEO-Problemen führen, beispielsweise durch Schwierigkeiten bei der Kanonisierung von URLs oder durch die Auslieferung veralteter Inhalte an Suchmaschinen-Crawler.&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
== Caching-Strategien ==&lt;br /&gt;
=== Browser Caching (Client-Seite) ===&lt;br /&gt;
&amp;lt;p&amp;gt;Nutzt HTTP-Header, um den Browser anzuweisen, wie Ressourcen lokal zwischengespeichert werden sollen. Reduziert Serverlast und verbessert Ladezeiten für wiederholte Besuche. Wichtige HTTP-Header sind Cache-Control (mit Direktiven wie public, private, no-cache, max-age), Expires, ETag und Last-Modified.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Server-seitiges Caching ===&lt;br /&gt;
&amp;lt;p&amp;gt;Speichert häufig abgerufene Daten oder vorberechnete Antworten auf dem Server, um die Verarbeitungslast und Datenbankabfragen zu reduzieren.30 Typen umfassen Full-Page Caching, Object Caching (z.B. mit Redis, Memcached), Opcode Caching und Datenbankabfrage-Caching.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Reverse Proxy Caching (z.B. Varnish, Nginx) ===&lt;br /&gt;
&amp;lt;p&amp;gt;Ein Reverse Proxy sitzt vor den Webservern und kann Antworten zwischenspeichern, wodurch Anfragen direkt bedient werden, ohne den Ursprungsserver zu belasten.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Cache-Invalidierungs-Techniken ===&lt;br /&gt;
&amp;lt;p&amp;gt;Sicherstellen, dass bei Datenänderungen die zwischengespeicherte Version aktualisiert oder entfernt wird, um die Bereitstellung veralteter Inhalte zu verhindern. Methoden umfassen TTL-basiertes Ablaufen, Purging, Write-Through Cache, ereignisgesteuerte Invalidierung, Versionierung und Stale-While-Revalidate.&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;Effektives Caching erfordert eine mehrschichtige Strategie (Browser, CDN, serverseitig, Reverse Proxy). Obwohl diese Schichten symbiotisch zusammenarbeiten, um die Leistung zu verbessern, können Fehlkonfigurationen oder unkoordinierte Cache-Invalidierungsrichtlinien zu unerwarteten Problemen mit veralteten Inhalten führen oder die Vorteile des Cachings zunichtemachen. Browser-Caching reduziert Netzwerkanfragen. CDN-Caching reduziert Anfragen an den Ursprungsserver. Serverseitiges Caching reduziert die Verarbeitung am Ursprungsserver. Jede Schicht hat jedoch ihre eigene Cache-Dauer (TTL) und Invalidierungsmechanismen. Wenn eine Ressource auf dem Ursprungsserver aktualisiert wird und der serverseitige Cache invalidiert wird, aber der CDN-Cache eine längere TTL hat und nicht aktiv geleert wird, erhalten Benutzer weiterhin die veraltete Version vom CDN. Dies unterstreicht die Notwendigkeit einer ganzheitlichen Sichtweise, bei der Cache-Control-Header, CDN-TTLs und serverseitige Invalidierungsstrategien koordiniert werden.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Verbesserung der Serverantwort und -auslieferung ==&lt;br /&gt;
=== Reduzierung der Time To First Byte (TTFB) ===&lt;br /&gt;
&amp;lt;p&amp;gt;TTFB misst die Zeit von der initialen Anfrage bis zum Empfang des ersten Bytes der HTML-Antwort durch den Browser. Strategien umfassen Serverkonfiguration und -aktualisierung, Effizienz des Backend-Codes und Datenbankoptimierung (effiziente Abfragen, Indizierung, Verbindungspooling).&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Textkomprimierung (GZIP, Brotli) ===&lt;br /&gt;
&amp;lt;p&amp;gt;Komprimiert textbasierte Assets (HTML, CSS, JS) vor dem Senden vom Server zum Browser. GZIP ist weit verbreitet, Brotli bietet bessere Kompressionsraten. Dies reduziert Dateigrößen erheblich, was zu schnelleren Downloads führt.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Moderne Netzwerkprotokolle: HTTP/2 und HTTP/3 (QUIC) Vorteile ===&lt;br /&gt;
&amp;lt;p&amp;gt;HTTP/1.1-Beschränkungen umfassen Head-of-Line-Blocking und textbasierte Header. HTTP/2 bietet Multiplexing (gleichzeitige Anfragen über eine TCP-Verbindung), Header-Komprimierung (HPACK), Server Push und ein binäres Protokoll. HTTP/3 läuft auf QUIC (UDP-basiert), löst TCP Head-of-Line-Blocking auf Transportebene, ermöglicht schnellere Verbindungsherstellung (0-RTT) und verbesserte Verbindungsmigration, mit standardmäßiger Verschlüsselung.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Zusätzliche Performance-Überlegungen ==&lt;br /&gt;
=== Web-Rendering-Strategien: CSR, SSR, SSG, Prerendering ===&lt;br /&gt;
&lt;br /&gt;
*Client-Side Rendering (CSR): Der Browser lädt eine minimale HTML-Hülle und JavaScript. JS ruft dann Daten ab und rendert den Seiteninhalt im Browser.&lt;br /&gt;
*Vorteile: Hohe Interaktivität, schneller TTFB für die Hülle.&lt;br /&gt;
*Nachteile: Langsames initiales Laden (FCP, LCP, TTI können hoch sein), schlechtes SEO ohne sorgfältige Handhabung.&lt;br /&gt;
*Server-Side Rendering (SSR): Der Server generiert das vollständige HTML für eine Seite als Antwort auf eine Browseranfrage.&lt;br /&gt;
*Vorteile: Schnellerer FCP/LCP, gut für SEO, aktuelle Inhalte.&lt;br /&gt;
*Nachteile: Langsamerer TTFB, höhere Serverlast.&lt;br /&gt;
*Static Site Generation (SSG): Alle HTML-Seiten werden zur Build-Zeit vorab generiert und als statische Dateien bereitgestellt.&lt;br /&gt;
*Vorteile: Schnellster TTFB und Ladezeiten, sehr sicher, exzellent für SEO.&lt;br /&gt;
*Nachteile: Lange Build-Zeiten für große Seiten, Inhalte können bis zum nächsten Build veraltet sein.&lt;br /&gt;
*Prerendering (und hybride Ansätze): Generierung von statischem HTML für bestimmte Routen zur Build-Zeit, während die Haupt-App CSR sein kann. Universelles/Isomorphes Rendering: Initiale Seitenladung ist SSR, dann übernimmt clientseitiges JS.&lt;br /&gt;
*Jede Strategie hat unterschiedliche Auswirkungen auf LCP, TTI, TTFB und SEO.&lt;br /&gt;
&amp;lt;p&amp;gt;Die Wahl der Rendering-Strategie (CSR, SSR, SSG, Hybrid) ist nicht nur ein technisches Implementierungsdetail, sondern eine grundlegende architektonische Entscheidung, die nicht nur alle Core Web Vitals und die Gesamtleistung tiefgreifend beeinflusst, sondern auch die SEO-Effektivität, Entwicklungskomplexität, Infrastrukturkosten und die Fähigkeit zur Inhaltsdynamik. Diese Wahl muss mit den Kernanforderungen der Web-Anwendung übereinstimmen. SSR und SSG sind im Allgemeinen besser für SEO geeignet. SSR kann serverintensiv sein, während SSG sehr günstig zu hosten ist. SSR und hybride Modelle können die Entwicklungskomplexität erhöhen. SSG ist ungeeignet für stark personalisierte oder Echtzeit-Inhalte.&amp;amp;nbsp;&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Businessspezifische Überlegungen &amp;amp; Web-Performance =&lt;br /&gt;
=== Auswirkungen auf Key Performance Indicators (KPIs) ===&lt;br /&gt;
&lt;br /&gt;
*Konversionsraten: Schnellere Ladezeiten korrelieren direkt mit höheren Konversionsraten. Schon eine Sekunde Verzögerung kann zu einem signifikanten Rückgang führen. Eine Fallstudie zeigte einen Umsatzanstieg von 70% nach Optimierung der Ladezeit von 6.2s auf 2.8s.&lt;br /&gt;
*Absprungraten: Langsamere Seiten führen zu höheren Absprungraten. 40% der Nutzer verlassen eine Seite, wenn sie länger als 3 Sekunden lädt.&lt;br /&gt;
*Nutzerengagement &amp;amp; Sitzungsdauer: Schnellere Seiten ermutigen Nutzer, länger zu bleiben und mehr Seiten anzusehen. Seiten, die in &amp;amp;lt; 5s laden, haben 70% längere Sitzungen.&lt;br /&gt;
*SEO-Rankings: Google verwendet Seitengeschwindigkeit und Core Web Vitals als Rankingfaktoren.&lt;br /&gt;
*Kundenzufriedenheit &amp;amp; Markenglaubwürdigkeit: Schnelle, zuverlässige Seiten bauen Vertrauen und Zufriedenheit auf.&lt;br /&gt;
&lt;br /&gt;
=== Return on Investment (ROI) der Performance-Optimierung ===&lt;br /&gt;
&amp;lt;p&amp;gt;Performance ist kein Kostenfaktor, sondern eine Investition mit greifbaren Erträgen. Dies umfasst gesteigerte Einnahmen durch höhere Konversionen, reduzierte Betriebskosten (z.B. geringere Bandbreitennutzung) und niedrigere Marketingkosten durch besseres SEO.&amp;amp;nbsp;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;Angesichts der direkten und signifikanten Auswirkungen der Web-Performance auf Geschäfts-KPIs und den ROI kann Performance kein einmaliges Projekt sein. Sie erfordert kontinuierliche Überwachung, die Festlegung von Performance-Budgets und die Integration in CI/CD-Pipelines, um Regressionen zu verhindern und sich proaktiv an sich ändernde Nutzererwartungen und Geschäftsziele anzupassen. Dies verwandelt Performance von einer reaktiven Fehlerbehebung in einen proaktiven strategischen Vorteil. Webseiten sind dynamisch; neue Funktionen und Inhalte stellen ein Risiko für die Performance dar. Wenn die Performance nachlässt, leiden die Geschäfts-KPIs. Daher ist die Aufrechterhaltung einer guten Performance für den nachhaltigen Geschäftserfolg unerlässlich.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;link rel=&amp;quot;preload&amp;quot; as=&amp;quot;style&amp;quot; onload=&amp;quot;this.onload=null;this.rel=&amp;#039;stylesheet&amp;#039;&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;link rel=&amp;quot;preload&amp;quot;&amp;gt;&amp;lt;link href=&amp;quot;critical.js&amp;quot; rel=&amp;quot;preload&amp;quot; as=&amp;quot;script&amp;quot;&amp;gt;&amp;lt;link rel=&amp;quot;preload&amp;quot;&amp;gt;&lt;/div&gt;</summary>
		<author><name>Völkl Anna</name></author>
	</entry>
	<entry>
		<id>https://mediawiki.fernfh.ac.at/mediawiki/index.php?title=Performance-Optimierung_(Webdevelopment)&amp;diff=6586</id>
		<title>Performance-Optimierung (Webdevelopment)</title>
		<link rel="alternate" type="text/html" href="https://mediawiki.fernfh.ac.at/mediawiki/index.php?title=Performance-Optimierung_(Webdevelopment)&amp;diff=6586"/>
		<updated>2025-05-19T21:10:06Z</updated>

		<summary type="html">&lt;p&gt;Völkl Anna: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Definition und Kritikalität der Web-Performance =&lt;br /&gt;
&amp;lt;p&amp;gt;Web-Performance bezeichnet die objektive Messung und subjektive Nutzerwahrnehmung der Geschwindigkeit und Zuverlässigkeit von Webanwendungen. Sie umfasst, wie schnell Inhalte laden, interaktiv werden und auf Benutzereingaben reagieren. Die Kritikalität der Web-Performance ergibt sich aus ihrem direkten Einfluss auf die Nutzerzufriedenheit, das Engagement und letztendlich den Geschäftserfolg. In der heutigen digitalen Landschaft erwarten Nutzer schnelle und nahtlose Erlebnisse; ein Nichterfüllen dieser Erwartungen kann zu Abwanderung und verpassten Chancen führen.&amp;amp;nbsp;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;Die anfängliche Performance-Erfahrung kann eine nachhaltig negative Voreingenommenheit gegenüber einer Marke erzeugen, wodurch zukünftige positive Interaktionen weniger wirkungsvoll werden. Umgekehrt baut eine konstant gute Performance im Laufe der Zeit Vertrauen und Loyalität auf. Eine langsame oder schlecht performende Webseite führt oft zu einer höheren Absprungrate und kann folglich auch eine negative Wahrnehmung Ihrer Marke verursachen.&amp;amp;nbsp;&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Messung der Performance =&lt;br /&gt;
=== Verständnis der Core Web Vitals (CWV) ===&lt;br /&gt;
&amp;lt;p&amp;gt;Die Core Web Vitals (CWV) sind eine Reihe von nutzerzentrierten Metriken von Google, die entwickelt wurden, um Schlüsselaspekte der Nutzererfahrung zu messen: Laden, Interaktivität und visuelle Stabilität.&amp;lt;/p&amp;gt;&lt;br /&gt;
*Largest Contentful Paint (LCP): Misst die Ladeleistung – die Zeit, die benötigt wird, bis das größte Inhaltselement (z.B. ein Bild oder ein Textblock) im Ansichtsfenster sichtbar wird. Ein guter LCP-Wert liegt bei ≤2.5 Sekunden. Dies beeinflusst direkt die Wahrnehmung des Nutzers, wie schnell der Hauptinhalt lädt.&lt;br /&gt;
*Interaction to Next Paint (INP): Misst die Reaktionsfähigkeit – die Latenz aller Klick-, Tipp- und Tastaturinteraktionen während des gesamten Besuchs eines Nutzers auf einer Seite, wobei die längste Interaktion (unter Auslassung von Ausreißern) gemeldet wird. INP hat First Input Delay (FID) ersetzt. Ein guter INP-Wert liegt bei ≤200 Millisekunden. Dies ist entscheidend dafür, wie reaktionsschnell und flüssig sich die Anwendung bei Nutzerinteraktionen anfühlt. FID maß nur die Verzögerung der ersten Eingabe, während INP eine umfassendere Sicht auf die gesamte Interaktivität bietet.&lt;br /&gt;
*Cumulative Layout Shift (CLS): Misst die visuelle Stabilität – die Gesamtsumme aller einzelnen Layout-Shift-Scores für jede unerwartete Layoutverschiebung, die während der gesamten Lebensdauer der Seite auftritt. Ein guter CLS-Wert liegt bei ≤0.1. Dies adressiert die Nutzerfrustration, die durch unerwartet auf der Seite verschobene Elemente verursacht wird und oft zu Fehlklicks führt. CLS wird durch die Impact Fraction (Anteil des betroffenen Bildschirms) und die Distance Fraction (wie weit sich ein Element verschoben hat) gemessen.&lt;br /&gt;
&lt;br /&gt;
=== Performance-Messwerkzeuge: Google PageSpeed Insights, Lighthouse und Real User Monitoring (RUM) ===&lt;br /&gt;
&lt;br /&gt;
*Google PageSpeed Insights (PSI): Liefert sowohl Labordaten (über Lighthouse) als auch Felddaten (aus dem Chrome User Experience Report - CrUX) für eine bestimmte URL. Bietet Performance-Scores und Vorschläge.&lt;br /&gt;
*Lighthouse: Ein Open-Source, automatisiertes Werkzeug zur Verbesserung der Qualität von Webseiten. Prüft Performance, Barrierefreiheit, PWA, SEO usw. in einer simulierten (Labor-)Umgebung.&lt;br /&gt;
*Real User Monitoring (RUM) / Chrome User Experience Report (CrUX):&lt;br /&gt;
*RUM sammelt Leistungsdaten von tatsächlichen Nutzern, die mit der Webseite in Echtzeit interagieren, und spiegelt dabei unterschiedliche Netzwerkbedingungen, Geräte und geografische Standorte wider.&lt;br /&gt;
*CrUX ist ein öffentlicher Datensatz von Google, der widerspiegelt, wie reale Chrome-Nutzer beliebte Ziele im Web erleben, und liefert Felddaten für Core Web Vitals.&lt;br /&gt;
*Diskrepanzen zwischen Labor- (Lighthouse/PSI Labor) und Felddaten (CrUX/RUM):&lt;br /&gt;
&amp;lt;p&amp;gt;Die Labordaten stammen aus einer kontrollierten, simulierten Umgebung. Felddaten repräsentieren aggregierte reale Nutzererfahrungen über einen Zeitraum (z.B. 28 Tage für CrUX).&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;Faktoren, die Unterschiede verursachen:&amp;lt;/p&amp;gt;&lt;br /&gt;
*Caching: Echte Nutzer profitieren vom Browser-Caching bei wiederholten Besuchen oder der Navigation innerhalb der Seite, was Labortests (insbesondere Kaltstarts) nicht immer widerspiegeln.&lt;br /&gt;
*Nutzerbedingungen: Netzwerkqualität, Gerätefähigkeiten und geografischer Standort variieren bei echten Nutzern erheblich im Vergleich zu standardisierten Labortests.&lt;br /&gt;
*Inhaltsvariationen: Cookie-Banner, A/B-Tests oder personalisierte Inhalte können sich zwischen Labortests und dem, was echte Nutzer sehen, unterscheiden.&lt;br /&gt;
*Above-the-Fold vs. vollständige Seiteninteraktion: Lighthouse konzentriert sich primär auf den initialen Ladevorgang, während CrUX Metriken wie CLS und INP über den gesamten Lebenszyklus der Seite erfasst, einschließlich Verschiebungen und Interaktionen nach dem Laden.&lt;br /&gt;
&lt;br /&gt;
= Client-seitige Performance-Optimierung =&lt;br /&gt;
== JavaScript-Optimierung ==&lt;br /&gt;
=== Minimierung der DOM-Manipulation ===&lt;br /&gt;
&amp;lt;p&amp;gt;Das Document Object Model (DOM) repräsentiert eine Webseite als Baum von Objekten. Häufige direkte Manipulationen sind kostspielig, da sie Browser-Reflows (Neuberechnung des Layouts) und Repaints (Neuzeichnen von Teilen der Seite) auslösen können. Techniken zur Minimierung umfassen das Stapeln von DOM-Updates und die Verwendung von Document Fragments, um Änderungen außerhalb des DOM vorzunehmen, bevor sie angehängt werden. Der Virtual DOM (z.B. in React, Vue) ist eine Abstraktion, bei der Änderungen zuerst auf eine In-Memory-Repräsentation (Virtual DOM) angewendet werden. Ein &amp;quot;Diffing&amp;quot;-Algorithmus identifiziert dann minimale Änderungen, die am tatsächlichen DOM erforderlich sind, und optimiert so die Updates. Dies verbessert die Performance dynamischer Anwendungen mit häufigen UI-Aktualisierungen erheblich.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
=== Code-Minifizierung, Tree Shaking und Code Splitting ===&lt;br /&gt;
&lt;br /&gt;
*Minifizierung: Entfernt unnötige Zeichen (Leerzeichen, Kommentare) aus HTML-, CSS- und JavaScript-Dateien, um deren Größe zu reduzieren, was zu schnelleren Downloads und geringerem Bandbreitenverbrauch führt.&amp;amp;nbsp;&lt;br /&gt;
*Tree Shaking (Dead Code Elimination): Ein Prozess, der typischerweise von Modul-Bundlern (z.B. webpack, Rollup) durchgeführt wird und ungenutzten JavaScript-Code entfernt, indem import- und export-Anweisungen in ES6-Modulen analysiert werden. Dies reduziert die endgültige Bundle-Größe.&lt;br /&gt;
*Code Splitting: Zerlegt große JavaScript-Bundles in kleinere Chunks. Nur der für die initiale Ansicht notwendige Code wird geladen; andere Chunks werden bei Bedarf geladen (z.B. bei Navigation oder Interaktion). Dies verbessert die initiale Ladezeit.&lt;br /&gt;
&lt;br /&gt;
=== Nutzung von Web Workern für Off-Thread-Ausführung ===&lt;br /&gt;
&amp;lt;p&amp;gt;Web Worker ermöglichen die Ausführung von JavaScript in Hintergrund-Threads, getrennt vom Haupt-Browser-Thread, der UI-Updates und Nutzerinteraktionen behandelt.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;p&amp;gt;Anwendungsfälle umfassen das Auslagern CPU-intensiver Aufgaben wie komplexe Berechnungen, Datenverarbeitung oder Bildmanipulation, um ein Einfrieren der UI zu verhindern und die Reaktionsfähigkeit (INP) zu verbessern. Web Worker können nicht direkt auf das DOM zugreifen; die Kommunikation mit dem Haupt-Thread erfolgt über postMessage(). Im Gegensatz zu Service Workern, die als Netzwerk-Proxies für Caching, Offline-Support und Push-Benachrichtigungen dienen, sind Web Worker für allgemeine Hintergrundaufgaben zur Verbesserung der UI-Reaktionsfähigkeit gedacht.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
== Optimierung des kritischen Rendering-Pfads ==&lt;br /&gt;
=== Critical CSS: Generierung und Inline-Implementierung ===&lt;br /&gt;
&amp;lt;p&amp;gt;Critical CSS ist der minimale Teil von CSS, der erforderlich ist, um den Above-the-Fold-Inhalt einer Seite darzustellen - also alles, was der Benutzer beim Laden der Seite auf einem Bildschirm sehen kann, ohne zu scrollen. Sein Zweck ist es, renderblockierendes CSS für die initiale Ansicht zu eliminieren, wodurch der Browser Pixel viel schneller auf den Bildschirm malen kann, was FCP und LCP verbessert. Generierungsmethoden umfassen manuelle Extraktion (zeitaufwendig), Online-Generatoren (einfacher, aber möglicherweise nicht perfekt optimiert) und Tools/Bibliotheken wie Critical oder Penthouse (automatisiert, integrierbar in Build-Prozesse). Die Implementierung erfolgt durch Inline-Einbettung des Critical CSS in &amp;amp;lt;style&amp;amp;gt;-Tags im &amp;amp;lt;head&amp;amp;gt; des HTML-Dokuments.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
=== Asynchrones Laden von nicht-kritischem CSS und JavaScript ===&lt;br /&gt;
&amp;lt;p&amp;gt;Nicht-kritisches CSS sind Stile, die nicht für das initiale Above-the-Fold-Rendering benötigt werden. Ladetechniken für CSS umfassen die Verwendung von &amp;amp;lt;link rel=&amp;quot;preload&amp;quot; as=&amp;quot;style&amp;quot; onload=&amp;quot;this.onload=null;this.rel=&amp;#039;stylesheet&amp;#039;&amp;quot;&amp;amp;gt; oder die Verwendung von media=&amp;quot;print&amp;quot; und das Austauschen zu media=&amp;quot;all&amp;quot; beim Laden. Nicht-kritisches JavaScript sind Skripte, die nicht für das initiale Seitenrendering oder die Interaktivität unerlässlich sind. Ladetechniken für JS umfassen die Attribute async und defer bei &amp;amp;lt;script&amp;amp;gt;-Tags. async lädt das Skript asynchron herunter und führt es aus, sobald es heruntergeladen ist, möglicherweise unterbricht es das HTML-Parsing. defer lädt das Skript asynchron herunter, führt es aber erst nach Abschluss des HTML-Parsings aus und in der Reihenfolge, in der sie im Dokument erscheinen.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
=== Vorladen von Schlüsselressourcen (rel=&amp;quot;preload&amp;quot;) ===&lt;br /&gt;
&amp;lt;p&amp;gt;&amp;amp;lt;link rel=&amp;quot;preload&amp;quot;&amp;amp;gt; informiert den Browser, eine Ressource mit hoher Priorität abzurufen, da sie bald benötigt wird, typischerweise für Ressourcen, die vom Browser spät entdeckt werden. Der Browser ruft die Ressource ab und speichert sie im Cache, führt jedoch Skripte nicht aus oder wendet Stylesheets nicht sofort an. Anwendungsfälle umfassen CSS-Dateien, JavaScript-Dateien (kritische Chunks), Web-Fonts (erfordert crossorigin-Attribut) und Bilder (kritische Above-the-Fold-Bilder). Die Implementierung erfolgt über &amp;amp;lt;link rel=&amp;quot;preload&amp;quot; href=&amp;quot;critical.js&amp;quot; as=&amp;quot;script&amp;quot;&amp;amp;gt;, wobei das as-Attribut (z.B. style, script, font, image) entscheidend für die korrekte Priorisierung ist. Preloading kann LCP (für Fonts, Bilder) und INP verbessern und helfen, CLS (für Fonts) zu reduzieren. Es sollten nur kritische Ressourcen vorgeladen werden, da ein Überladen die Performance durch Ressourcenkonkurrenz beeinträchtigen kann.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
== Asset-Optimierung ==&lt;br /&gt;
=== Bildoptimierung ===&lt;br /&gt;
&amp;lt;p&amp;gt;Bilder machen oft den größten Teil des Seitengewichts aus. Moderne Formate wie WebP (bessere Kompression als JPEG/PNG, unterstützt Transparenz und Animation) und AVIF (noch effizienter als WebP, lizenzfrei) sollten bevorzugt werden. Das &amp;amp;lt;picture&amp;amp;gt;-Element kann für Art Direction und Fallbacks verwendet werden. Die Kompression sollte zwischen Dateigröße und visueller Qualität abgewogen werden (verlustbehaftet vs. verlustfrei). Responsive Bilder sollten mit srcset und sizes für verschiedene Bildschirmgrößen und Auflösungen bereitgestellt werden. Native Lazy Loading (loading=&amp;quot;lazy&amp;quot;) für Bilder unterhalb des Falzes verbessert die initiale Ladezeit. Die Angabe von width- und height-Attributen bei &amp;amp;lt;img&amp;amp;gt;-Tags verhindert Layoutverschiebungen (CLS).&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
=== Web-Font-Optimierung ===&lt;br /&gt;
&amp;lt;p&amp;gt;Priorisieren Sie WOFF2 wegen seiner überlegenen Kompression und breiten Browserunterstützung, mit WOFF als Fallback. Die CSS-Eigenschaft font-display (z.B. swap, fallback, optional) steuert das Rendering-Verhalten während des Ladens und verhindert FOUT/FOIT. Font-Subsetting, d.h. das Einbeziehen nur der benötigten Zeichen, reduziert die Dateigröße drastisch. Kritische Fonts sollten mit &amp;amp;lt;link rel=&amp;quot;preload&amp;quot;&amp;amp;gt; vorgeladen werden. Selbsthosting bietet mehr Kontrolle über Caching, während Drittanbieterdienste einfach zu verwenden sind und Fonts möglicherweise bereits im Browser des Nutzers zwischengespeichert sind.&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;Die Optimierung von Bildern und Schriftarten ist keine isolierte Aufgabe, sondern eine entscheidende Voraussetzung für gute LCP-, CLS- und sogar INP-Werte. Diese Assets sind oft die direkten Kandidaten für diese Metriken oder beeinflussen stark das Rendering und die Interaktivität von Elementen, die es sind. Das Largest Contentful Element ist häufig ein Bild oder ein großer Textblock. Optimierte Bilder laden schneller. Vorgeladene und optimierte Schriftarten stellen sicher, dass Text schnell und korrekt gerendert wird. Nicht dimensionierte Bilder sind eine Hauptursache für Layoutverschiebungen. Web-Schriftarten, die mit signifikanten Unterschieden zu Fallback-Schriftarten geladen werden, verursachen ebenfalls CLS.&amp;amp;nbsp;&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Server-seitige und Netzwerk-Performance-Optimierung =&lt;br /&gt;
== Content Delivery Networks (CDNs) ==&lt;br /&gt;
&amp;lt;p&amp;gt;Ein CDN ist ein geografisch verteiltes Netzwerk von Proxy-Servern, das statische Inhalte (Bilder, CSS, JS) in Servern (Points of Presence - PoPs) näher am Endnutzer zwischenspeichert. Anfragen werden vom nächstgelegenen PoP bedient, was die Latenz reduziert.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== CDN - Wichtige Vor- und Nachteile ===&lt;br /&gt;
&amp;lt;br&amp;gt;Vorteile&lt;br /&gt;
&lt;br /&gt;
Schnellere Ladezeiten&amp;lt;br&amp;gt;Reduzierte Latenz durch geografische Nähe der Server zu den Nutzern.&amp;lt;br&amp;gt;Geringere Bandbreitenkosten&amp;lt;br&amp;gt;Entlastung des Ursprungsservers von Traffic, da Inhalte von CDN-Servern ausgeliefert werden.&amp;lt;br&amp;gt;Erhöhte Verfügbarkeit&amp;lt;br&amp;gt;Lastverteilung, Bewältigung von Traffic-Spitzen, Redundanz bei Ausfall des Ursprungsservers.&amp;lt;br&amp;gt;Verbesserte Sicherheit&amp;lt;br&amp;gt;Können DDoS-Schutz und Web Application Firewalls (WAF) bieten.&amp;lt;br&amp;gt;Skalierbarkeit&amp;lt;br&amp;gt;Fähigkeit, große Mengen an Traffic zu bewältigen und sich an neue Bandbreitenanforderungen anzupassen.&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Nachteile&lt;br /&gt;
&lt;br /&gt;
Kosten&amp;lt;br&amp;gt;CDN-Dienste sind mit Kosten verbunden, obwohl kostenlose Tarife existieren.&amp;lt;br&amp;gt;Komplexität&amp;lt;br&amp;gt;Einrichtung und Verwaltung können die Infrastruktur komplexer machen.&amp;lt;br&amp;gt;Kontrollverlust (teilweise)&amp;lt;br&amp;gt;Daten befinden sich auf Servern von Drittanbietern, was einen gewissen Kontrollverlust bedeutet.&amp;lt;br&amp;gt;Dynamische Inhalte&amp;lt;br&amp;gt;Primär für statische Inhalte; dynamische Inhalte erfordern oft Anfragen an den Ursprungsserver, obwohl Lösungen existieren.&amp;lt;br&amp;gt;Cache-Invalidierung&amp;lt;br&amp;gt;Sicherstellung der Aktualität zwischengespeicherter Inhalte kann herausfordernd sein.&amp;lt;br&amp;gt;SEO-Probleme (Fehlkonfig.)&amp;lt;br&amp;gt;Falsch konfigurierte CDNs können zu Problemen mit der Kanonisierung oder veralteten Inhalten führen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
== Caching-Strategien ==&lt;br /&gt;
=== Browser Caching (Client-Seite) ===&lt;br /&gt;
&amp;lt;p&amp;gt;Nutzt HTTP-Header, um den Browser anzuweisen, wie Ressourcen lokal zwischengespeichert werden sollen. Reduziert Serverlast und verbessert Ladezeiten für wiederholte Besuche. Wichtige HTTP-Header sind Cache-Control (mit Direktiven wie public, private, no-cache, max-age), Expires, ETag und Last-Modified.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Server-seitiges Caching ===&lt;br /&gt;
&amp;lt;p&amp;gt;Speichert häufig abgerufene Daten oder vorberechnete Antworten auf dem Server, um die Verarbeitungslast und Datenbankabfragen zu reduzieren.30 Typen umfassen Full-Page Caching, Object Caching (z.B. mit Redis, Memcached), Opcode Caching und Datenbankabfrage-Caching.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Reverse Proxy Caching (z.B. Varnish, Nginx) ===&lt;br /&gt;
&amp;lt;p&amp;gt;Ein Reverse Proxy sitzt vor den Webservern und kann Antworten zwischenspeichern, wodurch Anfragen direkt bedient werden, ohne den Ursprungsserver zu belasten.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Cache-Invalidierungs-Techniken ===&lt;br /&gt;
&amp;lt;p&amp;gt;Sicherstellen, dass bei Datenänderungen die zwischengespeicherte Version aktualisiert oder entfernt wird, um die Bereitstellung veralteter Inhalte zu verhindern. Methoden umfassen TTL-basiertes Ablaufen, Purging, Write-Through Cache, ereignisgesteuerte Invalidierung, Versionierung und Stale-While-Revalidate.&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;Effektives Caching erfordert eine mehrschichtige Strategie (Browser, CDN, serverseitig, Reverse Proxy). Obwohl diese Schichten symbiotisch zusammenarbeiten, um die Leistung zu verbessern, können Fehlkonfigurationen oder unkoordinierte Cache-Invalidierungsrichtlinien zu unerwarteten Problemen mit veralteten Inhalten führen oder die Vorteile des Cachings zunichtemachen. Browser-Caching reduziert Netzwerkanfragen. CDN-Caching reduziert Anfragen an den Ursprungsserver. Serverseitiges Caching reduziert die Verarbeitung am Ursprungsserver. Jede Schicht hat jedoch ihre eigene Cache-Dauer (TTL) und Invalidierungsmechanismen. Wenn eine Ressource auf dem Ursprungsserver aktualisiert wird und der serverseitige Cache invalidiert wird, aber der CDN-Cache eine längere TTL hat und nicht aktiv geleert wird, erhalten Benutzer weiterhin die veraltete Version vom CDN. Dies unterstreicht die Notwendigkeit einer ganzheitlichen Sichtweise, bei der Cache-Control-Header, CDN-TTLs und serverseitige Invalidierungsstrategien koordiniert werden.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Verbesserung der Serverantwort und -auslieferung ==&lt;br /&gt;
=== Reduzierung der Time To First Byte (TTFB) ===&lt;br /&gt;
&amp;lt;p&amp;gt;TTFB misst die Zeit von der initialen Anfrage bis zum Empfang des ersten Bytes der HTML-Antwort durch den Browser. Strategien umfassen Serverkonfiguration und -aktualisierung, Effizienz des Backend-Codes und Datenbankoptimierung (effiziente Abfragen, Indizierung, Verbindungspooling).&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Textkomprimierung (GZIP, Brotli) ===&lt;br /&gt;
&amp;lt;p&amp;gt;Komprimiert textbasierte Assets (HTML, CSS, JS) vor dem Senden vom Server zum Browser. GZIP ist weit verbreitet, Brotli bietet bessere Kompressionsraten. Dies reduziert Dateigrößen erheblich, was zu schnelleren Downloads führt.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Moderne Netzwerkprotokolle: HTTP/2 und HTTP/3 (QUIC) Vorteile ===&lt;br /&gt;
&amp;lt;p&amp;gt;HTTP/1.1-Beschränkungen umfassen Head-of-Line-Blocking und textbasierte Header. HTTP/2 bietet Multiplexing (gleichzeitige Anfragen über eine TCP-Verbindung), Header-Komprimierung (HPACK), Server Push und ein binäres Protokoll. HTTP/3 läuft auf QUIC (UDP-basiert), löst TCP Head-of-Line-Blocking auf Transportebene, ermöglicht schnellere Verbindungsherstellung (0-RTT) und verbesserte Verbindungsmigration, mit standardmäßiger Verschlüsselung.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Zusätzliche Performance-Überlegungen ==&lt;br /&gt;
=== Web-Rendering-Strategien: CSR, SSR, SSG, Prerendering ===&lt;br /&gt;
&lt;br /&gt;
*Client-Side Rendering (CSR): Der Browser lädt eine minimale HTML-Hülle und JavaScript. JS ruft dann Daten ab und rendert den Seiteninhalt im Browser.&lt;br /&gt;
*Vorteile: Hohe Interaktivität, schneller TTFB für die Hülle.&lt;br /&gt;
*Nachteile: Langsames initiales Laden (FCP, LCP, TTI können hoch sein), schlechtes SEO ohne sorgfältige Handhabung.&lt;br /&gt;
*Server-Side Rendering (SSR): Der Server generiert das vollständige HTML für eine Seite als Antwort auf eine Browseranfrage.&lt;br /&gt;
*Vorteile: Schnellerer FCP/LCP, gut für SEO, aktuelle Inhalte.&lt;br /&gt;
*Nachteile: Langsamerer TTFB, höhere Serverlast.&lt;br /&gt;
*Static Site Generation (SSG): Alle HTML-Seiten werden zur Build-Zeit vorab generiert und als statische Dateien bereitgestellt.&lt;br /&gt;
*Vorteile: Schnellster TTFB und Ladezeiten, sehr sicher, exzellent für SEO.&lt;br /&gt;
*Nachteile: Lange Build-Zeiten für große Seiten, Inhalte können bis zum nächsten Build veraltet sein.&lt;br /&gt;
*Prerendering (und hybride Ansätze): Generierung von statischem HTML für bestimmte Routen zur Build-Zeit, während die Haupt-App CSR sein kann. Universelles/Isomorphes Rendering: Initiale Seitenladung ist SSR, dann übernimmt clientseitiges JS.&lt;br /&gt;
*Jede Strategie hat unterschiedliche Auswirkungen auf LCP, TTI, TTFB und SEO.&lt;br /&gt;
&amp;lt;p&amp;gt;Die Wahl der Rendering-Strategie (CSR, SSR, SSG, Hybrid) ist nicht nur ein technisches Implementierungsdetail, sondern eine grundlegende architektonische Entscheidung, die nicht nur alle Core Web Vitals und die Gesamtleistung tiefgreifend beeinflusst, sondern auch die SEO-Effektivität, Entwicklungskomplexität, Infrastrukturkosten und die Fähigkeit zur Inhaltsdynamik. Diese Wahl muss mit den Kernanforderungen der Web-Anwendung übereinstimmen. SSR und SSG sind im Allgemeinen besser für SEO geeignet. SSR kann serverintensiv sein, während SSG sehr günstig zu hosten ist. SSR und hybride Modelle können die Entwicklungskomplexität erhöhen. SSG ist ungeeignet für stark personalisierte oder Echtzeit-Inhalte.&amp;amp;nbsp;&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Businessspezifische Überlegungen &amp;amp; Web-Performance =&lt;br /&gt;
=== Auswirkungen auf Key Performance Indicators (KPIs) ===&lt;br /&gt;
&lt;br /&gt;
*Konversionsraten: Schnellere Ladezeiten korrelieren direkt mit höheren Konversionsraten. Schon eine Sekunde Verzögerung kann zu einem signifikanten Rückgang führen. Eine Fallstudie zeigte einen Umsatzanstieg von 70% nach Optimierung der Ladezeit von 6.2s auf 2.8s.&lt;br /&gt;
*Absprungraten: Langsamere Seiten führen zu höheren Absprungraten. 40% der Nutzer verlassen eine Seite, wenn sie länger als 3 Sekunden lädt.&lt;br /&gt;
*Nutzerengagement &amp;amp; Sitzungsdauer: Schnellere Seiten ermutigen Nutzer, länger zu bleiben und mehr Seiten anzusehen. Seiten, die in &amp;amp;lt; 5s laden, haben 70% längere Sitzungen.&lt;br /&gt;
*SEO-Rankings: Google verwendet Seitengeschwindigkeit und Core Web Vitals als Rankingfaktoren.&lt;br /&gt;
*Kundenzufriedenheit &amp;amp; Markenglaubwürdigkeit: Schnelle, zuverlässige Seiten bauen Vertrauen und Zufriedenheit auf.&lt;br /&gt;
&lt;br /&gt;
=== Return on Investment (ROI) der Performance-Optimierung ===&lt;br /&gt;
&amp;lt;p&amp;gt;Performance ist kein Kostenfaktor, sondern eine Investition mit greifbaren Erträgen. Dies umfasst gesteigerte Einnahmen durch höhere Konversionen, reduzierte Betriebskosten (z.B. geringere Bandbreitennutzung) und niedrigere Marketingkosten durch besseres SEO.&amp;amp;nbsp;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;Angesichts der direkten und signifikanten Auswirkungen der Web-Performance auf Geschäfts-KPIs und den ROI kann Performance kein einmaliges Projekt sein. Sie erfordert kontinuierliche Überwachung, die Festlegung von Performance-Budgets und die Integration in CI/CD-Pipelines, um Regressionen zu verhindern und sich proaktiv an sich ändernde Nutzererwartungen und Geschäftsziele anzupassen. Dies verwandelt Performance von einer reaktiven Fehlerbehebung in einen proaktiven strategischen Vorteil. Webseiten sind dynamisch; neue Funktionen und Inhalte stellen ein Risiko für die Performance dar. Wenn die Performance nachlässt, leiden die Geschäfts-KPIs. Daher ist die Aufrechterhaltung einer guten Performance für den nachhaltigen Geschäftserfolg unerlässlich.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;link rel=&amp;quot;preload&amp;quot; as=&amp;quot;style&amp;quot; onload=&amp;quot;this.onload=null;this.rel=&amp;#039;stylesheet&amp;#039;&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;link rel=&amp;quot;preload&amp;quot;&amp;gt;&amp;lt;link href=&amp;quot;critical.js&amp;quot; rel=&amp;quot;preload&amp;quot; as=&amp;quot;script&amp;quot;&amp;gt;&amp;lt;link rel=&amp;quot;preload&amp;quot;&amp;gt;&lt;/div&gt;</summary>
		<author><name>Völkl Anna</name></author>
	</entry>
	<entry>
		<id>https://mediawiki.fernfh.ac.at/mediawiki/index.php?title=Performance-Optimierung_(Webdevelopment)&amp;diff=6585</id>
		<title>Performance-Optimierung (Webdevelopment)</title>
		<link rel="alternate" type="text/html" href="https://mediawiki.fernfh.ac.at/mediawiki/index.php?title=Performance-Optimierung_(Webdevelopment)&amp;diff=6585"/>
		<updated>2025-05-19T21:09:54Z</updated>

		<summary type="html">&lt;p&gt;Völkl Anna: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Definition und Kritikalität der Web-Performance =&lt;br /&gt;
&amp;lt;p&amp;gt;Web-Performance bezeichnet die objektive Messung und subjektive Nutzerwahrnehmung der Geschwindigkeit und Zuverlässigkeit von Webanwendungen. Sie umfasst, wie schnell Inhalte laden, interaktiv werden und auf Benutzereingaben reagieren. Die Kritikalität der Web-Performance ergibt sich aus ihrem direkten Einfluss auf die Nutzerzufriedenheit, das Engagement und letztendlich den Geschäftserfolg. In der heutigen digitalen Landschaft erwarten Nutzer schnelle und nahtlose Erlebnisse; ein Nichterfüllen dieser Erwartungen kann zu Abwanderung und verpassten Chancen führen.&amp;amp;nbsp;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;Die anfängliche Performance-Erfahrung kann eine nachhaltig negative Voreingenommenheit gegenüber einer Marke erzeugen, wodurch zukünftige positive Interaktionen weniger wirkungsvoll werden. Umgekehrt baut eine konstant gute Performance im Laufe der Zeit Vertrauen und Loyalität auf. Eine langsame oder schlecht performende Webseite führt oft zu einer höheren Absprungrate und kann folglich auch eine negative Wahrnehmung Ihrer Marke verursachen.&amp;amp;nbsp;&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Messung der Performance =&lt;br /&gt;
=== Verständnis der Core Web Vitals (CWV) ===&lt;br /&gt;
&amp;lt;p&amp;gt;Die Core Web Vitals (CWV) sind eine Reihe von nutzerzentrierten Metriken von Google, die entwickelt wurden, um Schlüsselaspekte der Nutzererfahrung zu messen: Laden, Interaktivität und visuelle Stabilität.&amp;lt;/p&amp;gt;&lt;br /&gt;
*Largest Contentful Paint (LCP): Misst die Ladeleistung – die Zeit, die benötigt wird, bis das größte Inhaltselement (z.B. ein Bild oder ein Textblock) im Ansichtsfenster sichtbar wird. Ein guter LCP-Wert liegt bei ≤2.5 Sekunden. Dies beeinflusst direkt die Wahrnehmung des Nutzers, wie schnell der Hauptinhalt lädt.&lt;br /&gt;
*Interaction to Next Paint (INP): Misst die Reaktionsfähigkeit – die Latenz aller Klick-, Tipp- und Tastaturinteraktionen während des gesamten Besuchs eines Nutzers auf einer Seite, wobei die längste Interaktion (unter Auslassung von Ausreißern) gemeldet wird. INP hat First Input Delay (FID) ersetzt. Ein guter INP-Wert liegt bei ≤200 Millisekunden. Dies ist entscheidend dafür, wie reaktionsschnell und flüssig sich die Anwendung bei Nutzerinteraktionen anfühlt. FID maß nur die Verzögerung der ersten Eingabe, während INP eine umfassendere Sicht auf die gesamte Interaktivität bietet.&lt;br /&gt;
*Cumulative Layout Shift (CLS): Misst die visuelle Stabilität – die Gesamtsumme aller einzelnen Layout-Shift-Scores für jede unerwartete Layoutverschiebung, die während der gesamten Lebensdauer der Seite auftritt. Ein guter CLS-Wert liegt bei ≤0.1. Dies adressiert die Nutzerfrustration, die durch unerwartet auf der Seite verschobene Elemente verursacht wird und oft zu Fehlklicks führt. CLS wird durch die Impact Fraction (Anteil des betroffenen Bildschirms) und die Distance Fraction (wie weit sich ein Element verschoben hat) gemessen.&lt;br /&gt;
&lt;br /&gt;
=== Performance-Messwerkzeuge: Google PageSpeed Insights, Lighthouse und Real User Monitoring (RUM) ===&lt;br /&gt;
&lt;br /&gt;
*Google PageSpeed Insights (PSI): Liefert sowohl Labordaten (über Lighthouse) als auch Felddaten (aus dem Chrome User Experience Report - CrUX) für eine bestimmte URL. Bietet Performance-Scores und Vorschläge.&lt;br /&gt;
*Lighthouse: Ein Open-Source, automatisiertes Werkzeug zur Verbesserung der Qualität von Webseiten. Prüft Performance, Barrierefreiheit, PWA, SEO usw. in einer simulierten (Labor-)Umgebung.&lt;br /&gt;
*Real User Monitoring (RUM) / Chrome User Experience Report (CrUX):&lt;br /&gt;
*RUM sammelt Leistungsdaten von tatsächlichen Nutzern, die mit der Webseite in Echtzeit interagieren, und spiegelt dabei unterschiedliche Netzwerkbedingungen, Geräte und geografische Standorte wider.&lt;br /&gt;
*CrUX ist ein öffentlicher Datensatz von Google, der widerspiegelt, wie reale Chrome-Nutzer beliebte Ziele im Web erleben, und liefert Felddaten für Core Web Vitals.&lt;br /&gt;
*Diskrepanzen zwischen Labor- (Lighthouse/PSI Labor) und Felddaten (CrUX/RUM):&lt;br /&gt;
&amp;lt;p&amp;gt;Die Labordaten stammen aus einer kontrollierten, simulierten Umgebung. Felddaten repräsentieren aggregierte reale Nutzererfahrungen über einen Zeitraum (z.B. 28 Tage für CrUX).&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;Faktoren, die Unterschiede verursachen:&amp;lt;/p&amp;gt;&lt;br /&gt;
*Caching: Echte Nutzer profitieren vom Browser-Caching bei wiederholten Besuchen oder der Navigation innerhalb der Seite, was Labortests (insbesondere Kaltstarts) nicht immer widerspiegeln.&lt;br /&gt;
*Nutzerbedingungen: Netzwerkqualität, Gerätefähigkeiten und geografischer Standort variieren bei echten Nutzern erheblich im Vergleich zu standardisierten Labortests.&lt;br /&gt;
*Inhaltsvariationen: Cookie-Banner, A/B-Tests oder personalisierte Inhalte können sich zwischen Labortests und dem, was echte Nutzer sehen, unterscheiden.&lt;br /&gt;
*Above-the-Fold vs. vollständige Seiteninteraktion: Lighthouse konzentriert sich primär auf den initialen Ladevorgang, während CrUX Metriken wie CLS und INP über den gesamten Lebenszyklus der Seite erfasst, einschließlich Verschiebungen und Interaktionen nach dem Laden.&lt;br /&gt;
&lt;br /&gt;
= Client-seitige Performance-Optimierung =&lt;br /&gt;
== JavaScript-Optimierung ==&lt;br /&gt;
=== Minimierung der DOM-Manipulation ===&lt;br /&gt;
&amp;lt;p&amp;gt;Das Document Object Model (DOM) repräsentiert eine Webseite als Baum von Objekten. Häufige direkte Manipulationen sind kostspielig, da sie Browser-Reflows (Neuberechnung des Layouts) und Repaints (Neuzeichnen von Teilen der Seite) auslösen können. Techniken zur Minimierung umfassen das Stapeln von DOM-Updates und die Verwendung von Document Fragments, um Änderungen außerhalb des DOM vorzunehmen, bevor sie angehängt werden. Der Virtual DOM (z.B. in React, Vue) ist eine Abstraktion, bei der Änderungen zuerst auf eine In-Memory-Repräsentation (Virtual DOM) angewendet werden. Ein &amp;quot;Diffing&amp;quot;-Algorithmus identifiziert dann minimale Änderungen, die am tatsächlichen DOM erforderlich sind, und optimiert so die Updates. Dies verbessert die Performance dynamischer Anwendungen mit häufigen UI-Aktualisierungen erheblich.&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
=== Code-Minifizierung, Tree Shaking und Code Splitting ===&lt;br /&gt;
&lt;br /&gt;
*Minifizierung: Entfernt unnötige Zeichen (Leerzeichen, Kommentare) aus HTML-, CSS- und JavaScript-Dateien, um deren Größe zu reduzieren, was zu schnelleren Downloads und geringerem Bandbreitenverbrauch führt.&amp;amp;nbsp;&lt;br /&gt;
*Tree Shaking (Dead Code Elimination): Ein Prozess, der typischerweise von Modul-Bundlern (z.B. webpack, Rollup) durchgeführt wird und ungenutzten JavaScript-Code entfernt, indem import- und export-Anweisungen in ES6-Modulen analysiert werden. Dies reduziert die endgültige Bundle-Größe.&lt;br /&gt;
*Code Splitting: Zerlegt große JavaScript-Bundles in kleinere Chunks. Nur der für die initiale Ansicht notwendige Code wird geladen; andere Chunks werden bei Bedarf geladen (z.B. bei Navigation oder Interaktion). Dies verbessert die initiale Ladezeit.&lt;br /&gt;
&lt;br /&gt;
=== Nutzung von Web Workern für Off-Thread-Ausführung ===&lt;br /&gt;
&amp;lt;p&amp;gt;Web Worker ermöglichen die Ausführung von JavaScript in Hintergrund-Threads, getrennt vom Haupt-Browser-Thread, der UI-Updates und Nutzerinteraktionen behandelt.&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;p&amp;gt;Anwendungsfälle umfassen das Auslagern CPU-intensiver Aufgaben wie komplexe Berechnungen, Datenverarbeitung oder Bildmanipulation, um ein Einfrieren der UI zu verhindern und die Reaktionsfähigkeit (INP) zu verbessern. Web Worker können nicht direkt auf das DOM zugreifen; die Kommunikation mit dem Haupt-Thread erfolgt über postMessage(). Im Gegensatz zu Service Workern, die als Netzwerk-Proxies für Caching, Offline-Support und Push-Benachrichtigungen dienen, sind Web Worker für allgemeine Hintergrundaufgaben zur Verbesserung der UI-Reaktionsfähigkeit gedacht.&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
== Optimierung des kritischen Rendering-Pfads ==&lt;br /&gt;
=== Critical CSS: Generierung und Inline-Implementierung ===&lt;br /&gt;
&amp;lt;p&amp;gt;Critical CSS ist der minimale Teil von CSS, der erforderlich ist, um den Above-the-Fold-Inhalt einer Seite darzustellen - also alles, was der Benutzer beim Laden der Seite auf einem Bildschirm sehen kann, ohne zu scrollen. Sein Zweck ist es, renderblockierendes CSS für die initiale Ansicht zu eliminieren, wodurch der Browser Pixel viel schneller auf den Bildschirm malen kann, was FCP und LCP verbessert. Generierungsmethoden umfassen manuelle Extraktion (zeitaufwendig), Online-Generatoren (einfacher, aber möglicherweise nicht perfekt optimiert) und Tools/Bibliotheken wie Critical oder Penthouse (automatisiert, integrierbar in Build-Prozesse). Die Implementierung erfolgt durch Inline-Einbettung des Critical CSS in &amp;amp;lt;style&amp;amp;gt;-Tags im &amp;amp;lt;head&amp;amp;gt; des HTML-Dokuments.&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
=== Asynchrones Laden von nicht-kritischem CSS und JavaScript ===&lt;br /&gt;
&amp;lt;p&amp;gt;Nicht-kritisches CSS sind Stile, die nicht für das initiale Above-the-Fold-Rendering benötigt werden. Ladetechniken für CSS umfassen die Verwendung von &amp;amp;lt;link rel=&amp;quot;preload&amp;quot; as=&amp;quot;style&amp;quot; onload=&amp;quot;this.onload=null;this.rel=&amp;#039;stylesheet&amp;#039;&amp;quot;&amp;amp;gt; oder die Verwendung von media=&amp;quot;print&amp;quot; und das Austauschen zu media=&amp;quot;all&amp;quot; beim Laden. Nicht-kritisches JavaScript sind Skripte, die nicht für das initiale Seitenrendering oder die Interaktivität unerlässlich sind. Ladetechniken für JS umfassen die Attribute async und defer bei &amp;amp;lt;script&amp;amp;gt;-Tags. async lädt das Skript asynchron herunter und führt es aus, sobald es heruntergeladen ist, möglicherweise unterbricht es das HTML-Parsing. defer lädt das Skript asynchron herunter, führt es aber erst nach Abschluss des HTML-Parsings aus und in der Reihenfolge, in der sie im Dokument erscheinen.&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
=== Vorladen von Schlüsselressourcen (rel=&amp;quot;preload&amp;quot;) ===&lt;br /&gt;
&amp;lt;p&amp;gt;&amp;amp;lt;link rel=&amp;quot;preload&amp;quot;&amp;amp;gt; informiert den Browser, eine Ressource mit hoher Priorität abzurufen, da sie bald benötigt wird, typischerweise für Ressourcen, die vom Browser spät entdeckt werden. Der Browser ruft die Ressource ab und speichert sie im Cache, führt jedoch Skripte nicht aus oder wendet Stylesheets nicht sofort an. Anwendungsfälle umfassen CSS-Dateien, JavaScript-Dateien (kritische Chunks), Web-Fonts (erfordert crossorigin-Attribut) und Bilder (kritische Above-the-Fold-Bilder). Die Implementierung erfolgt über &amp;amp;lt;link rel=&amp;quot;preload&amp;quot; href=&amp;quot;critical.js&amp;quot; as=&amp;quot;script&amp;quot;&amp;amp;gt;, wobei das as-Attribut (z.B. style, script, font, image) entscheidend für die korrekte Priorisierung ist. Preloading kann LCP (für Fonts, Bilder) und INP verbessern und helfen, CLS (für Fonts) zu reduzieren. Es sollten nur kritische Ressourcen vorgeladen werden, da ein Überladen die Performance durch Ressourcenkonkurrenz beeinträchtigen kann.&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
== Asset-Optimierung ==&lt;br /&gt;
=== Bildoptimierung ===&lt;br /&gt;
&amp;lt;p&amp;gt;Bilder machen oft den größten Teil des Seitengewichts aus. Moderne Formate wie WebP (bessere Kompression als JPEG/PNG, unterstützt Transparenz und Animation) und AVIF (noch effizienter als WebP, lizenzfrei) sollten bevorzugt werden. Das &amp;amp;lt;picture&amp;amp;gt;-Element kann für Art Direction und Fallbacks verwendet werden. Die Kompression sollte zwischen Dateigröße und visueller Qualität abgewogen werden (verlustbehaftet vs. verlustfrei). Responsive Bilder sollten mit srcset und sizes für verschiedene Bildschirmgrößen und Auflösungen bereitgestellt werden. Native Lazy Loading (loading=&amp;quot;lazy&amp;quot;) für Bilder unterhalb des Falzes verbessert die initiale Ladezeit. Die Angabe von width- und height-Attributen bei &amp;amp;lt;img&amp;amp;gt;-Tags verhindert Layoutverschiebungen (CLS).&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
=== Web-Font-Optimierung ===&lt;br /&gt;
&amp;lt;p&amp;gt;Priorisieren Sie WOFF2 wegen seiner überlegenen Kompression und breiten Browserunterstützung, mit WOFF als Fallback. Die CSS-Eigenschaft font-display (z.B. swap, fallback, optional) steuert das Rendering-Verhalten während des Ladens und verhindert FOUT/FOIT. Font-Subsetting, d.h. das Einbeziehen nur der benötigten Zeichen, reduziert die Dateigröße drastisch. Kritische Fonts sollten mit &amp;amp;lt;link rel=&amp;quot;preload&amp;quot;&amp;amp;gt; vorgeladen werden. Selbsthosting bietet mehr Kontrolle über Caching, während Drittanbieterdienste einfach zu verwenden sind und Fonts möglicherweise bereits im Browser des Nutzers zwischengespeichert sind.&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;Die Optimierung von Bildern und Schriftarten ist keine isolierte Aufgabe, sondern eine entscheidende Voraussetzung für gute LCP-, CLS- und sogar INP-Werte. Diese Assets sind oft die direkten Kandidaten für diese Metriken oder beeinflussen stark das Rendering und die Interaktivität von Elementen, die es sind. Das Largest Contentful Element ist häufig ein Bild oder ein großer Textblock. Optimierte Bilder laden schneller. Vorgeladene und optimierte Schriftarten stellen sicher, dass Text schnell und korrekt gerendert wird. Nicht dimensionierte Bilder sind eine Hauptursache für Layoutverschiebungen. Web-Schriftarten, die mit signifikanten Unterschieden zu Fallback-Schriftarten geladen werden, verursachen ebenfalls CLS.&amp;amp;nbsp;&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Server-seitige und Netzwerk-Performance-Optimierung =&lt;br /&gt;
== Content Delivery Networks (CDNs) ==&lt;br /&gt;
&amp;lt;p&amp;gt;Ein CDN ist ein geografisch verteiltes Netzwerk von Proxy-Servern, das statische Inhalte (Bilder, CSS, JS) in Servern (Points of Presence - PoPs) näher am Endnutzer zwischenspeichert. Anfragen werden vom nächstgelegenen PoP bedient, was die Latenz reduziert.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== CDN - Wichtige Vor- und Nachteile ===&lt;br /&gt;
&amp;lt;br&amp;gt;Vorteile&lt;br /&gt;
&lt;br /&gt;
Schnellere Ladezeiten&amp;lt;br&amp;gt;Reduzierte Latenz durch geografische Nähe der Server zu den Nutzern.&amp;lt;br&amp;gt;Geringere Bandbreitenkosten&amp;lt;br&amp;gt;Entlastung des Ursprungsservers von Traffic, da Inhalte von CDN-Servern ausgeliefert werden.&amp;lt;br&amp;gt;Erhöhte Verfügbarkeit&amp;lt;br&amp;gt;Lastverteilung, Bewältigung von Traffic-Spitzen, Redundanz bei Ausfall des Ursprungsservers.&amp;lt;br&amp;gt;Verbesserte Sicherheit&amp;lt;br&amp;gt;Können DDoS-Schutz und Web Application Firewalls (WAF) bieten.&amp;lt;br&amp;gt;Skalierbarkeit&amp;lt;br&amp;gt;Fähigkeit, große Mengen an Traffic zu bewältigen und sich an neue Bandbreitenanforderungen anzupassen.&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Nachteile&lt;br /&gt;
&lt;br /&gt;
Kosten&amp;lt;br&amp;gt;CDN-Dienste sind mit Kosten verbunden, obwohl kostenlose Tarife existieren.&amp;lt;br&amp;gt;Komplexität&amp;lt;br&amp;gt;Einrichtung und Verwaltung können die Infrastruktur komplexer machen.&amp;lt;br&amp;gt;Kontrollverlust (teilweise)&amp;lt;br&amp;gt;Daten befinden sich auf Servern von Drittanbietern, was einen gewissen Kontrollverlust bedeutet.&amp;lt;br&amp;gt;Dynamische Inhalte&amp;lt;br&amp;gt;Primär für statische Inhalte; dynamische Inhalte erfordern oft Anfragen an den Ursprungsserver, obwohl Lösungen existieren.&amp;lt;br&amp;gt;Cache-Invalidierung&amp;lt;br&amp;gt;Sicherstellung der Aktualität zwischengespeicherter Inhalte kann herausfordernd sein.&amp;lt;br&amp;gt;SEO-Probleme (Fehlkonfig.)&amp;lt;br&amp;gt;Falsch konfigurierte CDNs können zu Problemen mit der Kanonisierung oder veralteten Inhalten führen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
== Caching-Strategien ==&lt;br /&gt;
=== Browser Caching (Client-Seite) ===&lt;br /&gt;
&amp;lt;p&amp;gt;Nutzt HTTP-Header, um den Browser anzuweisen, wie Ressourcen lokal zwischengespeichert werden sollen. Reduziert Serverlast und verbessert Ladezeiten für wiederholte Besuche. Wichtige HTTP-Header sind Cache-Control (mit Direktiven wie public, private, no-cache, max-age), Expires, ETag und Last-Modified.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Server-seitiges Caching ===&lt;br /&gt;
&amp;lt;p&amp;gt;Speichert häufig abgerufene Daten oder vorberechnete Antworten auf dem Server, um die Verarbeitungslast und Datenbankabfragen zu reduzieren.30 Typen umfassen Full-Page Caching, Object Caching (z.B. mit Redis, Memcached), Opcode Caching und Datenbankabfrage-Caching.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Reverse Proxy Caching (z.B. Varnish, Nginx) ===&lt;br /&gt;
&amp;lt;p&amp;gt;Ein Reverse Proxy sitzt vor den Webservern und kann Antworten zwischenspeichern, wodurch Anfragen direkt bedient werden, ohne den Ursprungsserver zu belasten.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Cache-Invalidierungs-Techniken ===&lt;br /&gt;
&amp;lt;p&amp;gt;Sicherstellen, dass bei Datenänderungen die zwischengespeicherte Version aktualisiert oder entfernt wird, um die Bereitstellung veralteter Inhalte zu verhindern. Methoden umfassen TTL-basiertes Ablaufen, Purging, Write-Through Cache, ereignisgesteuerte Invalidierung, Versionierung und Stale-While-Revalidate.&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;Effektives Caching erfordert eine mehrschichtige Strategie (Browser, CDN, serverseitig, Reverse Proxy). Obwohl diese Schichten symbiotisch zusammenarbeiten, um die Leistung zu verbessern, können Fehlkonfigurationen oder unkoordinierte Cache-Invalidierungsrichtlinien zu unerwarteten Problemen mit veralteten Inhalten führen oder die Vorteile des Cachings zunichtemachen. Browser-Caching reduziert Netzwerkanfragen. CDN-Caching reduziert Anfragen an den Ursprungsserver. Serverseitiges Caching reduziert die Verarbeitung am Ursprungsserver. Jede Schicht hat jedoch ihre eigene Cache-Dauer (TTL) und Invalidierungsmechanismen. Wenn eine Ressource auf dem Ursprungsserver aktualisiert wird und der serverseitige Cache invalidiert wird, aber der CDN-Cache eine längere TTL hat und nicht aktiv geleert wird, erhalten Benutzer weiterhin die veraltete Version vom CDN. Dies unterstreicht die Notwendigkeit einer ganzheitlichen Sichtweise, bei der Cache-Control-Header, CDN-TTLs und serverseitige Invalidierungsstrategien koordiniert werden.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Verbesserung der Serverantwort und -auslieferung ==&lt;br /&gt;
=== Reduzierung der Time To First Byte (TTFB) ===&lt;br /&gt;
&amp;lt;p&amp;gt;TTFB misst die Zeit von der initialen Anfrage bis zum Empfang des ersten Bytes der HTML-Antwort durch den Browser. Strategien umfassen Serverkonfiguration und -aktualisierung, Effizienz des Backend-Codes und Datenbankoptimierung (effiziente Abfragen, Indizierung, Verbindungspooling).&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Textkomprimierung (GZIP, Brotli) ===&lt;br /&gt;
&amp;lt;p&amp;gt;Komprimiert textbasierte Assets (HTML, CSS, JS) vor dem Senden vom Server zum Browser. GZIP ist weit verbreitet, Brotli bietet bessere Kompressionsraten. Dies reduziert Dateigrößen erheblich, was zu schnelleren Downloads führt.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Moderne Netzwerkprotokolle: HTTP/2 und HTTP/3 (QUIC) Vorteile ===&lt;br /&gt;
&amp;lt;p&amp;gt;HTTP/1.1-Beschränkungen umfassen Head-of-Line-Blocking und textbasierte Header. HTTP/2 bietet Multiplexing (gleichzeitige Anfragen über eine TCP-Verbindung), Header-Komprimierung (HPACK), Server Push und ein binäres Protokoll. HTTP/3 läuft auf QUIC (UDP-basiert), löst TCP Head-of-Line-Blocking auf Transportebene, ermöglicht schnellere Verbindungsherstellung (0-RTT) und verbesserte Verbindungsmigration, mit standardmäßiger Verschlüsselung.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Zusätzliche Performance-Überlegungen ==&lt;br /&gt;
=== Web-Rendering-Strategien: CSR, SSR, SSG, Prerendering ===&lt;br /&gt;
&lt;br /&gt;
*Client-Side Rendering (CSR): Der Browser lädt eine minimale HTML-Hülle und JavaScript. JS ruft dann Daten ab und rendert den Seiteninhalt im Browser.&lt;br /&gt;
*Vorteile: Hohe Interaktivität, schneller TTFB für die Hülle.&lt;br /&gt;
*Nachteile: Langsames initiales Laden (FCP, LCP, TTI können hoch sein), schlechtes SEO ohne sorgfältige Handhabung.&lt;br /&gt;
*Server-Side Rendering (SSR): Der Server generiert das vollständige HTML für eine Seite als Antwort auf eine Browseranfrage.&lt;br /&gt;
*Vorteile: Schnellerer FCP/LCP, gut für SEO, aktuelle Inhalte.&lt;br /&gt;
*Nachteile: Langsamerer TTFB, höhere Serverlast.&lt;br /&gt;
*Static Site Generation (SSG): Alle HTML-Seiten werden zur Build-Zeit vorab generiert und als statische Dateien bereitgestellt.&lt;br /&gt;
*Vorteile: Schnellster TTFB und Ladezeiten, sehr sicher, exzellent für SEO.&lt;br /&gt;
*Nachteile: Lange Build-Zeiten für große Seiten, Inhalte können bis zum nächsten Build veraltet sein.&lt;br /&gt;
*Prerendering (und hybride Ansätze): Generierung von statischem HTML für bestimmte Routen zur Build-Zeit, während die Haupt-App CSR sein kann. Universelles/Isomorphes Rendering: Initiale Seitenladung ist SSR, dann übernimmt clientseitiges JS.&lt;br /&gt;
*Jede Strategie hat unterschiedliche Auswirkungen auf LCP, TTI, TTFB und SEO.&lt;br /&gt;
&amp;lt;p&amp;gt;Die Wahl der Rendering-Strategie (CSR, SSR, SSG, Hybrid) ist nicht nur ein technisches Implementierungsdetail, sondern eine grundlegende architektonische Entscheidung, die nicht nur alle Core Web Vitals und die Gesamtleistung tiefgreifend beeinflusst, sondern auch die SEO-Effektivität, Entwicklungskomplexität, Infrastrukturkosten und die Fähigkeit zur Inhaltsdynamik. Diese Wahl muss mit den Kernanforderungen der Web-Anwendung übereinstimmen. SSR und SSG sind im Allgemeinen besser für SEO geeignet. SSR kann serverintensiv sein, während SSG sehr günstig zu hosten ist. SSR und hybride Modelle können die Entwicklungskomplexität erhöhen. SSG ist ungeeignet für stark personalisierte oder Echtzeit-Inhalte.&amp;amp;nbsp;&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Businessspezifische Überlegungen &amp;amp; Web-Performance ==&lt;br /&gt;
=== Auswirkungen auf Key Performance Indicators (KPIs) ===&lt;br /&gt;
&lt;br /&gt;
*Konversionsraten: Schnellere Ladezeiten korrelieren direkt mit höheren Konversionsraten. Schon eine Sekunde Verzögerung kann zu einem signifikanten Rückgang führen. Eine Fallstudie zeigte einen Umsatzanstieg von 70% nach Optimierung der Ladezeit von 6.2s auf 2.8s.&lt;br /&gt;
*Absprungraten: Langsamere Seiten führen zu höheren Absprungraten. 40% der Nutzer verlassen eine Seite, wenn sie länger als 3 Sekunden lädt.&lt;br /&gt;
*Nutzerengagement &amp;amp; Sitzungsdauer: Schnellere Seiten ermutigen Nutzer, länger zu bleiben und mehr Seiten anzusehen. Seiten, die in &amp;amp;lt; 5s laden, haben 70% längere Sitzungen.&lt;br /&gt;
*SEO-Rankings: Google verwendet Seitengeschwindigkeit und Core Web Vitals als Rankingfaktoren.&lt;br /&gt;
*Kundenzufriedenheit &amp;amp; Markenglaubwürdigkeit: Schnelle, zuverlässige Seiten bauen Vertrauen und Zufriedenheit auf.&lt;br /&gt;
&lt;br /&gt;
=== Return on Investment (ROI) der Performance-Optimierung ===&lt;br /&gt;
&amp;lt;p&amp;gt;Performance ist kein Kostenfaktor, sondern eine Investition mit greifbaren Erträgen. Dies umfasst gesteigerte Einnahmen durch höhere Konversionen, reduzierte Betriebskosten (z.B. geringere Bandbreitennutzung) und niedrigere Marketingkosten durch besseres SEO.&amp;amp;nbsp;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;Angesichts der direkten und signifikanten Auswirkungen der Web-Performance auf Geschäfts-KPIs und den ROI kann Performance kein einmaliges Projekt sein. Sie erfordert kontinuierliche Überwachung, die Festlegung von Performance-Budgets und die Integration in CI/CD-Pipelines, um Regressionen zu verhindern und sich proaktiv an sich ändernde Nutzererwartungen und Geschäftsziele anzupassen. Dies verwandelt Performance von einer reaktiven Fehlerbehebung in einen proaktiven strategischen Vorteil. Webseiten sind dynamisch; neue Funktionen und Inhalte stellen ein Risiko für die Performance dar. Wenn die Performance nachlässt, leiden die Geschäfts-KPIs. Daher ist die Aufrechterhaltung einer guten Performance für den nachhaltigen Geschäftserfolg unerlässlich.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;link rel=&amp;quot;preload&amp;quot; as=&amp;quot;style&amp;quot; onload=&amp;quot;this.onload=null;this.rel=&amp;#039;stylesheet&amp;#039;&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;link rel=&amp;quot;preload&amp;quot;&amp;gt;&amp;lt;link href=&amp;quot;critical.js&amp;quot; rel=&amp;quot;preload&amp;quot; as=&amp;quot;script&amp;quot;&amp;gt;&amp;lt;link rel=&amp;quot;preload&amp;quot;&amp;gt;&lt;/div&gt;</summary>
		<author><name>Völkl Anna</name></author>
	</entry>
	<entry>
		<id>https://mediawiki.fernfh.ac.at/mediawiki/index.php?title=Performance-Optimierung_(Webdevelopment)&amp;diff=6584</id>
		<title>Performance-Optimierung (Webdevelopment)</title>
		<link rel="alternate" type="text/html" href="https://mediawiki.fernfh.ac.at/mediawiki/index.php?title=Performance-Optimierung_(Webdevelopment)&amp;diff=6584"/>
		<updated>2025-05-19T21:09:31Z</updated>

		<summary type="html">&lt;p&gt;Völkl Anna: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Definition und Kritikalität der Web-Performance =&lt;br /&gt;
&amp;lt;p&amp;gt;Web-Performance bezeichnet die objektive Messung und subjektive Nutzerwahrnehmung der Geschwindigkeit und Zuverlässigkeit von Webanwendungen. Sie umfasst, wie schnell Inhalte laden, interaktiv werden und auf Benutzereingaben reagieren. Die Kritikalität der Web-Performance ergibt sich aus ihrem direkten Einfluss auf die Nutzerzufriedenheit, das Engagement und letztendlich den Geschäftserfolg. In der heutigen digitalen Landschaft erwarten Nutzer schnelle und nahtlose Erlebnisse; ein Nichterfüllen dieser Erwartungen kann zu Abwanderung und verpassten Chancen führen.&amp;amp;nbsp;&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Die anfängliche Performance-Erfahrung kann eine nachhaltig negative Voreingenommenheit gegenüber einer Marke erzeugen, wodurch zukünftige positive Interaktionen weniger wirkungsvoll werden. Umgekehrt baut eine konstant gute Performance im Laufe der Zeit Vertrauen und Loyalität auf. Eine langsame oder schlecht performende Webseite führt oft zu einer höheren Absprungrate und kann folglich auch eine negative Wahrnehmung Ihrer Marke verursachen.&amp;amp;nbsp;&amp;lt;/p&amp;gt;&lt;br /&gt;
= Messung der Performance =&lt;br /&gt;
=== Verständnis der Core Web Vitals (CWV) ===&lt;br /&gt;
&amp;lt;p&amp;gt;Die Core Web Vitals (CWV) sind eine Reihe von nutzerzentrierten Metriken von Google, die entwickelt wurden, um Schlüsselaspekte der Nutzererfahrung zu messen: Laden, Interaktivität und visuelle Stabilität.&amp;lt;/p&amp;gt;&lt;br /&gt;
*Largest Contentful Paint (LCP): Misst die Ladeleistung – die Zeit, die benötigt wird, bis das größte Inhaltselement (z.B. ein Bild oder ein Textblock) im Ansichtsfenster sichtbar wird. Ein guter LCP-Wert liegt bei ≤2.5 Sekunden. Dies beeinflusst direkt die Wahrnehmung des Nutzers, wie schnell der Hauptinhalt lädt.&lt;br /&gt;
*Interaction to Next Paint (INP): Misst die Reaktionsfähigkeit – die Latenz aller Klick-, Tipp- und Tastaturinteraktionen während des gesamten Besuchs eines Nutzers auf einer Seite, wobei die längste Interaktion (unter Auslassung von Ausreißern) gemeldet wird. INP hat First Input Delay (FID) ersetzt. Ein guter INP-Wert liegt bei ≤200 Millisekunden. Dies ist entscheidend dafür, wie reaktionsschnell und flüssig sich die Anwendung bei Nutzerinteraktionen anfühlt. FID maß nur die Verzögerung der ersten Eingabe, während INP eine umfassendere Sicht auf die gesamte Interaktivität bietet.&lt;br /&gt;
*Cumulative Layout Shift (CLS): Misst die visuelle Stabilität – die Gesamtsumme aller einzelnen Layout-Shift-Scores für jede unerwartete Layoutverschiebung, die während der gesamten Lebensdauer der Seite auftritt. Ein guter CLS-Wert liegt bei ≤0.1. Dies adressiert die Nutzerfrustration, die durch unerwartet auf der Seite verschobene Elemente verursacht wird und oft zu Fehlklicks führt. CLS wird durch die Impact Fraction (Anteil des betroffenen Bildschirms) und die Distance Fraction (wie weit sich ein Element verschoben hat) gemessen.&lt;br /&gt;
&lt;br /&gt;
=== Performance-Messwerkzeuge: Google PageSpeed Insights, Lighthouse und Real User Monitoring (RUM) ===&lt;br /&gt;
&lt;br /&gt;
*Google PageSpeed Insights (PSI): Liefert sowohl Labordaten (über Lighthouse) als auch Felddaten (aus dem Chrome User Experience Report - CrUX) für eine bestimmte URL. Bietet Performance-Scores und Vorschläge.&lt;br /&gt;
*Lighthouse: Ein Open-Source, automatisiertes Werkzeug zur Verbesserung der Qualität von Webseiten. Prüft Performance, Barrierefreiheit, PWA, SEO usw. in einer simulierten (Labor-)Umgebung.&lt;br /&gt;
*Real User Monitoring (RUM) / Chrome User Experience Report (CrUX):&lt;br /&gt;
&lt;br /&gt;
*RUM sammelt Leistungsdaten von tatsächlichen Nutzern, die mit der Webseite in Echtzeit interagieren, und spiegelt dabei unterschiedliche Netzwerkbedingungen, Geräte und geografische Standorte wider.&lt;br /&gt;
*CrUX ist ein öffentlicher Datensatz von Google, der widerspiegelt, wie reale Chrome-Nutzer beliebte Ziele im Web erleben, und liefert Felddaten für Core Web Vitals.&lt;br /&gt;
&lt;br /&gt;
*Diskrepanzen zwischen Labor- (Lighthouse/PSI Labor) und Felddaten (CrUX/RUM):&lt;br /&gt;
&amp;lt;p&amp;gt;Die Labordaten stammen aus einer kontrollierten, simulierten Umgebung. Felddaten repräsentieren aggregierte reale Nutzererfahrungen über einen Zeitraum (z.B. 28 Tage für CrUX).&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Faktoren, die Unterschiede verursachen:&amp;lt;/p&amp;gt;&lt;br /&gt;
*Caching: Echte Nutzer profitieren vom Browser-Caching bei wiederholten Besuchen oder der Navigation innerhalb der Seite, was Labortests (insbesondere Kaltstarts) nicht immer widerspiegeln.&lt;br /&gt;
*Nutzerbedingungen: Netzwerkqualität, Gerätefähigkeiten und geografischer Standort variieren bei echten Nutzern erheblich im Vergleich zu standardisierten Labortests.&lt;br /&gt;
*Inhaltsvariationen: Cookie-Banner, A/B-Tests oder personalisierte Inhalte können sich zwischen Labortests und dem, was echte Nutzer sehen, unterscheiden.&lt;br /&gt;
*Above-the-Fold vs. vollständige Seiteninteraktion: Lighthouse konzentriert sich primär auf den initialen Ladevorgang, während CrUX Metriken wie CLS und INP über den gesamten Lebenszyklus der Seite erfasst, einschließlich Verschiebungen und Interaktionen nach dem Laden.&lt;br /&gt;
&lt;br /&gt;
== Client-seitige Performance-Optimierung ==&lt;br /&gt;
== JavaScript-Optimierung ==&lt;br /&gt;
=== Minimierung der DOM-Manipulation ===&lt;br /&gt;
&amp;lt;p&amp;gt;Das Document Object Model (DOM) repräsentiert eine Webseite als Baum von Objekten. Häufige direkte Manipulationen sind kostspielig, da sie Browser-Reflows (Neuberechnung des Layouts) und Repaints (Neuzeichnen von Teilen der Seite) auslösen können. Techniken zur Minimierung umfassen das Stapeln von DOM-Updates und die Verwendung von Document Fragments, um Änderungen außerhalb des DOM vorzunehmen, bevor sie angehängt werden. Der Virtual DOM (z.B. in React, Vue) ist eine Abstraktion, bei der Änderungen zuerst auf eine In-Memory-Repräsentation (Virtual DOM) angewendet werden. Ein &amp;quot;Diffing&amp;quot;-Algorithmus identifiziert dann minimale Änderungen, die am tatsächlichen DOM erforderlich sind, und optimiert so die Updates. Dies verbessert die Performance dynamischer Anwendungen mit häufigen UI-Aktualisierungen erheblich.&amp;lt;/p&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
=== Code-Minifizierung, Tree Shaking und Code Splitting ===&lt;br /&gt;
&lt;br /&gt;
*Minifizierung: Entfernt unnötige Zeichen (Leerzeichen, Kommentare) aus HTML-, CSS- und JavaScript-Dateien, um deren Größe zu reduzieren, was zu schnelleren Downloads und geringerem Bandbreitenverbrauch führt.&amp;amp;nbsp;&lt;br /&gt;
*Tree Shaking (Dead Code Elimination): Ein Prozess, der typischerweise von Modul-Bundlern (z.B. webpack, Rollup) durchgeführt wird und ungenutzten JavaScript-Code entfernt, indem import- und export-Anweisungen in ES6-Modulen analysiert werden. Dies reduziert die endgültige Bundle-Größe.&lt;br /&gt;
*Code Splitting: Zerlegt große JavaScript-Bundles in kleinere Chunks. Nur der für die initiale Ansicht notwendige Code wird geladen; andere Chunks werden bei Bedarf geladen (z.B. bei Navigation oder Interaktion). Dies verbessert die initiale Ladezeit.&lt;br /&gt;
&lt;br /&gt;
=== Nutzung von Web Workern für Off-Thread-Ausführung ===&lt;br /&gt;
&amp;lt;p&amp;gt;Web Worker ermöglichen die Ausführung von JavaScript in Hintergrund-Threads, getrennt vom Haupt-Browser-Thread, der UI-Updates und Nutzerinteraktionen behandelt.&amp;lt;/p&amp;gt;&amp;lt;br&amp;gt;&amp;lt;p&amp;gt;Anwendungsfälle umfassen das Auslagern CPU-intensiver Aufgaben wie komplexe Berechnungen, Datenverarbeitung oder Bildmanipulation, um ein Einfrieren der UI zu verhindern und die Reaktionsfähigkeit (INP) zu verbessern. Web Worker können nicht direkt auf das DOM zugreifen; die Kommunikation mit dem Haupt-Thread erfolgt über postMessage(). Im Gegensatz zu Service Workern, die als Netzwerk-Proxies für Caching, Offline-Support und Push-Benachrichtigungen dienen, sind Web Worker für allgemeine Hintergrundaufgaben zur Verbesserung der UI-Reaktionsfähigkeit gedacht.&amp;lt;/p&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
== Optimierung des kritischen Rendering-Pfads ==&lt;br /&gt;
=== Critical CSS: Generierung und Inline-Implementierung ===&lt;br /&gt;
&amp;lt;p&amp;gt;Critical CSS ist der minimale Teil von CSS, der erforderlich ist, um den Above-the-Fold-Inhalt einer Seite darzustellen - also alles, was der Benutzer beim Laden der Seite auf einem Bildschirm sehen kann, ohne zu scrollen. Sein Zweck ist es, renderblockierendes CSS für die initiale Ansicht zu eliminieren, wodurch der Browser Pixel viel schneller auf den Bildschirm malen kann, was FCP und LCP verbessert. Generierungsmethoden umfassen manuelle Extraktion (zeitaufwendig), Online-Generatoren (einfacher, aber möglicherweise nicht perfekt optimiert) und Tools/Bibliotheken wie Critical oder Penthouse (automatisiert, integrierbar in Build-Prozesse). Die Implementierung erfolgt durch Inline-Einbettung des Critical CSS in &amp;amp;lt;style&amp;amp;gt;-Tags im &amp;amp;lt;head&amp;amp;gt; des HTML-Dokuments.&amp;lt;/p&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
=== Asynchrones Laden von nicht-kritischem CSS und JavaScript ===&lt;br /&gt;
&amp;lt;p&amp;gt;Nicht-kritisches CSS sind Stile, die nicht für das initiale Above-the-Fold-Rendering benötigt werden. Ladetechniken für CSS umfassen die Verwendung von &amp;amp;lt;link rel=&amp;quot;preload&amp;quot; as=&amp;quot;style&amp;quot; onload=&amp;quot;this.onload=null;this.rel=&amp;#039;stylesheet&amp;#039;&amp;quot;&amp;amp;gt; oder die Verwendung von media=&amp;quot;print&amp;quot; und das Austauschen zu media=&amp;quot;all&amp;quot; beim Laden. Nicht-kritisches JavaScript sind Skripte, die nicht für das initiale Seitenrendering oder die Interaktivität unerlässlich sind. Ladetechniken für JS umfassen die Attribute async und defer bei &amp;amp;lt;script&amp;amp;gt;-Tags. async lädt das Skript asynchron herunter und führt es aus, sobald es heruntergeladen ist, möglicherweise unterbricht es das HTML-Parsing. defer lädt das Skript asynchron herunter, führt es aber erst nach Abschluss des HTML-Parsings aus und in der Reihenfolge, in der sie im Dokument erscheinen.&amp;lt;/p&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
=== Vorladen von Schlüsselressourcen (rel=&amp;quot;preload&amp;quot;) ===&lt;br /&gt;
&amp;lt;p&amp;gt;&amp;amp;lt;link rel=&amp;quot;preload&amp;quot;&amp;amp;gt; informiert den Browser, eine Ressource mit hoher Priorität abzurufen, da sie bald benötigt wird, typischerweise für Ressourcen, die vom Browser spät entdeckt werden. Der Browser ruft die Ressource ab und speichert sie im Cache, führt jedoch Skripte nicht aus oder wendet Stylesheets nicht sofort an. Anwendungsfälle umfassen CSS-Dateien, JavaScript-Dateien (kritische Chunks), Web-Fonts (erfordert crossorigin-Attribut) und Bilder (kritische Above-the-Fold-Bilder). Die Implementierung erfolgt über &amp;amp;lt;link rel=&amp;quot;preload&amp;quot; href=&amp;quot;critical.js&amp;quot; as=&amp;quot;script&amp;quot;&amp;amp;gt;, wobei das as-Attribut (z.B. style, script, font, image) entscheidend für die korrekte Priorisierung ist. Preloading kann LCP (für Fonts, Bilder) und INP verbessern und helfen, CLS (für Fonts) zu reduzieren. Es sollten nur kritische Ressourcen vorgeladen werden, da ein Überladen die Performance durch Ressourcenkonkurrenz beeinträchtigen kann.&amp;lt;/p&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
== Asset-Optimierung ==&lt;br /&gt;
=== Bildoptimierung ===&lt;br /&gt;
&amp;lt;p&amp;gt;Bilder machen oft den größten Teil des Seitengewichts aus. Moderne Formate wie WebP (bessere Kompression als JPEG/PNG, unterstützt Transparenz und Animation) und AVIF (noch effizienter als WebP, lizenzfrei) sollten bevorzugt werden. Das &amp;amp;lt;picture&amp;amp;gt;-Element kann für Art Direction und Fallbacks verwendet werden. Die Kompression sollte zwischen Dateigröße und visueller Qualität abgewogen werden (verlustbehaftet vs. verlustfrei). Responsive Bilder sollten mit srcset und sizes für verschiedene Bildschirmgrößen und Auflösungen bereitgestellt werden. Native Lazy Loading (loading=&amp;quot;lazy&amp;quot;) für Bilder unterhalb des Falzes verbessert die initiale Ladezeit. Die Angabe von width- und height-Attributen bei &amp;amp;lt;img&amp;amp;gt;-Tags verhindert Layoutverschiebungen (CLS).&amp;lt;/p&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
=== Web-Font-Optimierung ===&lt;br /&gt;
&amp;lt;p&amp;gt;Priorisieren Sie WOFF2 wegen seiner überlegenen Kompression und breiten Browserunterstützung, mit WOFF als Fallback. Die CSS-Eigenschaft font-display (z.B. swap, fallback, optional) steuert das Rendering-Verhalten während des Ladens und verhindert FOUT/FOIT. Font-Subsetting, d.h. das Einbeziehen nur der benötigten Zeichen, reduziert die Dateigröße drastisch. Kritische Fonts sollten mit &amp;amp;lt;link rel=&amp;quot;preload&amp;quot;&amp;amp;gt; vorgeladen werden. Selbsthosting bietet mehr Kontrolle über Caching, während Drittanbieterdienste einfach zu verwenden sind und Fonts möglicherweise bereits im Browser des Nutzers zwischengespeichert sind.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Die Optimierung von Bildern und Schriftarten ist keine isolierte Aufgabe, sondern eine entscheidende Voraussetzung für gute LCP-, CLS- und sogar INP-Werte. Diese Assets sind oft die direkten Kandidaten für diese Metriken oder beeinflussen stark das Rendering und die Interaktivität von Elementen, die es sind. Das Largest Contentful Element ist häufig ein Bild oder ein großer Textblock. Optimierte Bilder laden schneller. Vorgeladene und optimierte Schriftarten stellen sicher, dass Text schnell und korrekt gerendert wird. Nicht dimensionierte Bilder sind eine Hauptursache für Layoutverschiebungen. Web-Schriftarten, die mit signifikanten Unterschieden zu Fallback-Schriftarten geladen werden, verursachen ebenfalls CLS.&amp;amp;nbsp;&amp;lt;/p&amp;gt;&lt;br /&gt;
= Server-seitige und Netzwerk-Performance-Optimierung =&lt;br /&gt;
== Content Delivery Networks (CDNs) ==&lt;br /&gt;
&amp;lt;p&amp;gt;Ein CDN ist ein geografisch verteiltes Netzwerk von Proxy-Servern, das statische Inhalte (Bilder, CSS, JS) in Servern (Points of Presence - PoPs) näher am Endnutzer zwischenspeichert. Anfragen werden vom nächstgelegenen PoP bedient, was die Latenz reduziert.&amp;lt;/p&amp;gt;&lt;br /&gt;
=== CDN - Wichtige Vor- und Nachteile ===&lt;br /&gt;
&amp;lt;br&amp;gt;Vorteile&lt;br /&gt;
&lt;br /&gt;
Schnellere Ladezeiten&amp;lt;br&amp;gt;Reduzierte Latenz durch geografische Nähe der Server zu den Nutzern.&amp;lt;br&amp;gt;Geringere Bandbreitenkosten&amp;lt;br&amp;gt;Entlastung des Ursprungsservers von Traffic, da Inhalte von CDN-Servern ausgeliefert werden.&amp;lt;br&amp;gt;Erhöhte Verfügbarkeit&amp;lt;br&amp;gt;Lastverteilung, Bewältigung von Traffic-Spitzen, Redundanz bei Ausfall des Ursprungsservers.&amp;lt;br&amp;gt;Verbesserte Sicherheit&amp;lt;br&amp;gt;Können DDoS-Schutz und Web Application Firewalls (WAF) bieten.&amp;lt;br&amp;gt;Skalierbarkeit&amp;lt;br&amp;gt;Fähigkeit, große Mengen an Traffic zu bewältigen und sich an neue Bandbreitenanforderungen anzupassen.&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Nachteile&lt;br /&gt;
&lt;br /&gt;
Kosten&amp;lt;br&amp;gt;CDN-Dienste sind mit Kosten verbunden, obwohl kostenlose Tarife existieren.&amp;lt;br&amp;gt;Komplexität&amp;lt;br&amp;gt;Einrichtung und Verwaltung können die Infrastruktur komplexer machen.&amp;lt;br&amp;gt;Kontrollverlust (teilweise)&amp;lt;br&amp;gt;Daten befinden sich auf Servern von Drittanbietern, was einen gewissen Kontrollverlust bedeutet.&amp;lt;br&amp;gt;Dynamische Inhalte&amp;lt;br&amp;gt;Primär für statische Inhalte; dynamische Inhalte erfordern oft Anfragen an den Ursprungsserver, obwohl Lösungen existieren.&amp;lt;br&amp;gt;Cache-Invalidierung&amp;lt;br&amp;gt;Sicherstellung der Aktualität zwischengespeicherter Inhalte kann herausfordernd sein.&amp;lt;br&amp;gt;SEO-Probleme (Fehlkonfig.)&amp;lt;br&amp;gt;Falsch konfigurierte CDNs können zu Problemen mit der Kanonisierung oder veralteten Inhalten führen.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
== Caching-Strategien ==&lt;br /&gt;
=== Browser Caching (Client-Seite) ===&lt;br /&gt;
&amp;lt;p&amp;gt;Nutzt HTTP-Header, um den Browser anzuweisen, wie Ressourcen lokal zwischengespeichert werden sollen. Reduziert Serverlast und verbessert Ladezeiten für wiederholte Besuche. Wichtige HTTP-Header sind Cache-Control (mit Direktiven wie public, private, no-cache, max-age), Expires, ETag und Last-Modified.&amp;lt;/p&amp;gt;&lt;br /&gt;
=== Server-seitiges Caching ===&lt;br /&gt;
&amp;lt;p&amp;gt;Speichert häufig abgerufene Daten oder vorberechnete Antworten auf dem Server, um die Verarbeitungslast und Datenbankabfragen zu reduzieren.30 Typen umfassen Full-Page Caching, Object Caching (z.B. mit Redis, Memcached), Opcode Caching und Datenbankabfrage-Caching.&amp;lt;/p&amp;gt;&lt;br /&gt;
=== Reverse Proxy Caching (z.B. Varnish, Nginx) ===&lt;br /&gt;
&amp;lt;p&amp;gt;Ein Reverse Proxy sitzt vor den Webservern und kann Antworten zwischenspeichern, wodurch Anfragen direkt bedient werden, ohne den Ursprungsserver zu belasten.&amp;lt;/p&amp;gt;&lt;br /&gt;
=== Cache-Invalidierungs-Techniken ===&lt;br /&gt;
&amp;lt;p&amp;gt;Sicherstellen, dass bei Datenänderungen die zwischengespeicherte Version aktualisiert oder entfernt wird, um die Bereitstellung veralteter Inhalte zu verhindern. Methoden umfassen TTL-basiertes Ablaufen, Purging, Write-Through Cache, ereignisgesteuerte Invalidierung, Versionierung und Stale-While-Revalidate.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Effektives Caching erfordert eine mehrschichtige Strategie (Browser, CDN, serverseitig, Reverse Proxy). Obwohl diese Schichten symbiotisch zusammenarbeiten, um die Leistung zu verbessern, können Fehlkonfigurationen oder unkoordinierte Cache-Invalidierungsrichtlinien zu unerwarteten Problemen mit veralteten Inhalten führen oder die Vorteile des Cachings zunichtemachen. Browser-Caching reduziert Netzwerkanfragen. CDN-Caching reduziert Anfragen an den Ursprungsserver. Serverseitiges Caching reduziert die Verarbeitung am Ursprungsserver. Jede Schicht hat jedoch ihre eigene Cache-Dauer (TTL) und Invalidierungsmechanismen. Wenn eine Ressource auf dem Ursprungsserver aktualisiert wird und der serverseitige Cache invalidiert wird, aber der CDN-Cache eine längere TTL hat und nicht aktiv geleert wird, erhalten Benutzer weiterhin die veraltete Version vom CDN. Dies unterstreicht die Notwendigkeit einer ganzheitlichen Sichtweise, bei der Cache-Control-Header, CDN-TTLs und serverseitige Invalidierungsstrategien koordiniert werden.&amp;lt;/p&amp;gt;&lt;br /&gt;
== Verbesserung der Serverantwort und -auslieferung ==&lt;br /&gt;
=== Reduzierung der Time To First Byte (TTFB) ===&lt;br /&gt;
&amp;lt;p&amp;gt;TTFB misst die Zeit von der initialen Anfrage bis zum Empfang des ersten Bytes der HTML-Antwort durch den Browser. Strategien umfassen Serverkonfiguration und -aktualisierung, Effizienz des Backend-Codes und Datenbankoptimierung (effiziente Abfragen, Indizierung, Verbindungspooling).&amp;lt;/p&amp;gt;&lt;br /&gt;
=== Textkomprimierung (GZIP, Brotli) ===&lt;br /&gt;
&amp;lt;p&amp;gt;Komprimiert textbasierte Assets (HTML, CSS, JS) vor dem Senden vom Server zum Browser. GZIP ist weit verbreitet, Brotli bietet bessere Kompressionsraten. Dies reduziert Dateigrößen erheblich, was zu schnelleren Downloads führt.&amp;lt;/p&amp;gt;&lt;br /&gt;
=== Moderne Netzwerkprotokolle: HTTP/2 und HTTP/3 (QUIC) Vorteile ===&lt;br /&gt;
&amp;lt;p&amp;gt;HTTP/1.1-Beschränkungen umfassen Head-of-Line-Blocking und textbasierte Header. HTTP/2 bietet Multiplexing (gleichzeitige Anfragen über eine TCP-Verbindung), Header-Komprimierung (HPACK), Server Push und ein binäres Protokoll. HTTP/3 läuft auf QUIC (UDP-basiert), löst TCP Head-of-Line-Blocking auf Transportebene, ermöglicht schnellere Verbindungsherstellung (0-RTT) und verbesserte Verbindungsmigration, mit standardmäßiger Verschlüsselung.&amp;lt;/p&amp;gt;&lt;br /&gt;
== Zusätzliche Performance-Überlegungen ==&lt;br /&gt;
=== Web-Rendering-Strategien: CSR, SSR, SSG, Prerendering ===&lt;br /&gt;
&lt;br /&gt;
*Client-Side Rendering (CSR): Der Browser lädt eine minimale HTML-Hülle und JavaScript. JS ruft dann Daten ab und rendert den Seiteninhalt im Browser.&lt;br /&gt;
&lt;br /&gt;
*Vorteile: Hohe Interaktivität, schneller TTFB für die Hülle.&lt;br /&gt;
*Nachteile: Langsames initiales Laden (FCP, LCP, TTI können hoch sein), schlechtes SEO ohne sorgfältige Handhabung.&lt;br /&gt;
&lt;br /&gt;
*Server-Side Rendering (SSR): Der Server generiert das vollständige HTML für eine Seite als Antwort auf eine Browseranfrage.&lt;br /&gt;
&lt;br /&gt;
*Vorteile: Schnellerer FCP/LCP, gut für SEO, aktuelle Inhalte.&lt;br /&gt;
*Nachteile: Langsamerer TTFB, höhere Serverlast.&lt;br /&gt;
&lt;br /&gt;
*Static Site Generation (SSG): Alle HTML-Seiten werden zur Build-Zeit vorab generiert und als statische Dateien bereitgestellt.&lt;br /&gt;
&lt;br /&gt;
*Vorteile: Schnellster TTFB und Ladezeiten, sehr sicher, exzellent für SEO.&lt;br /&gt;
*Nachteile: Lange Build-Zeiten für große Seiten, Inhalte können bis zum nächsten Build veraltet sein.&lt;br /&gt;
&lt;br /&gt;
*Prerendering (und hybride Ansätze): Generierung von statischem HTML für bestimmte Routen zur Build-Zeit, während die Haupt-App CSR sein kann. Universelles/Isomorphes Rendering: Initiale Seitenladung ist SSR, dann übernimmt clientseitiges JS.&lt;br /&gt;
*Jede Strategie hat unterschiedliche Auswirkungen auf LCP, TTI, TTFB und SEO.&lt;br /&gt;
&amp;lt;p&amp;gt;Die Wahl der Rendering-Strategie (CSR, SSR, SSG, Hybrid) ist nicht nur ein technisches Implementierungsdetail, sondern eine grundlegende architektonische Entscheidung, die nicht nur alle Core Web Vitals und die Gesamtleistung tiefgreifend beeinflusst, sondern auch die SEO-Effektivität, Entwicklungskomplexität, Infrastrukturkosten und die Fähigkeit zur Inhaltsdynamik. Diese Wahl muss mit den Kernanforderungen der Web-Anwendung übereinstimmen. SSR und SSG sind im Allgemeinen besser für SEO geeignet. SSR kann serverintensiv sein, während SSG sehr günstig zu hosten ist. SSR und hybride Modelle können die Entwicklungskomplexität erhöhen. SSG ist ungeeignet für stark personalisierte oder Echtzeit-Inhalte.&amp;amp;nbsp;&amp;lt;/p&amp;gt;&lt;br /&gt;
== Businessspezifische Überlegungen &amp;amp; Web-Performance ==&lt;br /&gt;
=== Auswirkungen auf Key Performance Indicators (KPIs) ===&lt;br /&gt;
&lt;br /&gt;
*Konversionsraten: Schnellere Ladezeiten korrelieren direkt mit höheren Konversionsraten. Schon eine Sekunde Verzögerung kann zu einem signifikanten Rückgang führen. Eine Fallstudie zeigte einen Umsatzanstieg von 70% nach Optimierung der Ladezeit von 6.2s auf 2.8s.&lt;br /&gt;
*Absprungraten: Langsamere Seiten führen zu höheren Absprungraten. 40% der Nutzer verlassen eine Seite, wenn sie länger als 3 Sekunden lädt.&lt;br /&gt;
*Nutzerengagement &amp;amp; Sitzungsdauer: Schnellere Seiten ermutigen Nutzer, länger zu bleiben und mehr Seiten anzusehen. Seiten, die in &amp;amp;lt; 5s laden, haben 70% längere Sitzungen.&lt;br /&gt;
*SEO-Rankings: Google verwendet Seitengeschwindigkeit und Core Web Vitals als Rankingfaktoren.&lt;br /&gt;
*Kundenzufriedenheit &amp;amp; Markenglaubwürdigkeit: Schnelle, zuverlässige Seiten bauen Vertrauen und Zufriedenheit auf.&lt;br /&gt;
&lt;br /&gt;
=== Return on Investment (ROI) der Performance-Optimierung ===&lt;br /&gt;
&amp;lt;p&amp;gt;Performance ist kein Kostenfaktor, sondern eine Investition mit greifbaren Erträgen. Dies umfasst gesteigerte Einnahmen durch höhere Konversionen, reduzierte Betriebskosten (z.B. geringere Bandbreitennutzung) und niedrigere Marketingkosten durch besseres SEO.&amp;amp;nbsp;&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Angesichts der direkten und signifikanten Auswirkungen der Web-Performance auf Geschäfts-KPIs und den ROI kann Performance kein einmaliges Projekt sein. Sie erfordert kontinuierliche Überwachung, die Festlegung von Performance-Budgets und die Integration in CI/CD-Pipelines, um Regressionen zu verhindern und sich proaktiv an sich ändernde Nutzererwartungen und Geschäftsziele anzupassen. Dies verwandelt Performance von einer reaktiven Fehlerbehebung in einen proaktiven strategischen Vorteil. Webseiten sind dynamisch; neue Funktionen und Inhalte stellen ein Risiko für die Performance dar. Wenn die Performance nachlässt, leiden die Geschäfts-KPIs. Daher ist die Aufrechterhaltung einer guten Performance für den nachhaltigen Geschäftserfolg unerlässlich.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;link rel=&amp;quot;preload&amp;quot; as=&amp;quot;style&amp;quot; onload=&amp;quot;this.onload=null;this.rel=&amp;#039;stylesheet&amp;#039;&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;link rel=&amp;quot;preload&amp;quot;&amp;gt;&amp;lt;link href=&amp;quot;critical.js&amp;quot; rel=&amp;quot;preload&amp;quot; as=&amp;quot;script&amp;quot;&amp;gt;&amp;lt;link rel=&amp;quot;preload&amp;quot;&amp;gt;&lt;/div&gt;</summary>
		<author><name>Völkl Anna</name></author>
	</entry>
	<entry>
		<id>https://mediawiki.fernfh.ac.at/mediawiki/index.php?title=Performance-Optimierung_(Webdevelopment)&amp;diff=6583</id>
		<title>Performance-Optimierung (Webdevelopment)</title>
		<link rel="alternate" type="text/html" href="https://mediawiki.fernfh.ac.at/mediawiki/index.php?title=Performance-Optimierung_(Webdevelopment)&amp;diff=6583"/>
		<updated>2025-05-19T21:01:45Z</updated>

		<summary type="html">&lt;p&gt;Völkl Anna: Die Seite wurde neu angelegt: „Web-Performance-Optimierung I. Einführung in die Web-Performance A. Definition und Kritikalität der Web-Performance Web-Performance bezeichnet die objektive Messung und subjektive Nutzerwahrnehmung der Geschwindigkeit und Zuverlässigkeit von Webanwendungen. Sie umfasst, wie schnell Inhalte laden, interaktiv werden und auf Benutzereingaben reagieren. Die Kritikalität der Web-Performance ergibt sich aus ihrem direkten Einfluss auf die Nutzerzufriedenhei…“&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Web-Performance-Optimierung&lt;br /&gt;
I. Einführung in die Web-Performance&lt;br /&gt;
A. Definition und Kritikalität der Web-Performance&lt;br /&gt;
Web-Performance bezeichnet die objektive Messung und subjektive Nutzerwahrnehmung der Geschwindigkeit und Zuverlässigkeit von Webanwendungen. Sie umfasst, wie schnell Inhalte laden, interaktiv werden und auf Benutzereingaben reagieren. Die Kritikalität der Web-Performance ergibt sich aus ihrem direkten Einfluss auf die Nutzerzufriedenheit, das Engagement und letztendlich den Geschäftserfolg. In der heutigen digitalen Landschaft erwarten Nutzer schnelle und nahtlose Erlebnisse; ein Nichterfüllen dieser Erwartungen kann zu Abwanderung und verpassten Chancen führen. &lt;br /&gt;
Die anfängliche Performance-Erfahrung kann eine nachhaltig negative Voreingenommenheit gegenüber einer Marke erzeugen, wodurch zukünftige positive Interaktionen weniger wirkungsvoll werden. Umgekehrt baut eine konstant gute Performance im Laufe der Zeit Vertrauen und Loyalität auf. Eine langsame oder schlecht performende Webseite führt oft zu einer höheren Absprungrate und kann folglich auch eine negative Wahrnehmung Ihrer Marke verursachen. &lt;br /&gt;
II. Messung der Performance: Core Web Vitals und Tools&lt;br /&gt;
A. Verständnis der Core Web Vitals (CWV)&lt;br /&gt;
Die Core Web Vitals (CWV) sind eine Reihe von nutzerzentrierten Metriken von Google, die entwickelt wurden, um Schlüsselaspekte der Nutzererfahrung zu messen: Laden, Interaktivität und visuelle Stabilität.&lt;br /&gt;
Largest Contentful Paint (LCP): Misst die Ladeleistung – die Zeit, die benötigt wird, bis das größte Inhaltselement (z.B. ein Bild oder ein Textblock) im Ansichtsfenster sichtbar wird. Ein guter LCP-Wert liegt bei ≤2.5 Sekunden. Dies beeinflusst direkt die Wahrnehmung des Nutzers, wie schnell der Hauptinhalt lädt.&lt;br /&gt;
Interaction to Next Paint (INP): Misst die Reaktionsfähigkeit – die Latenz aller Klick-, Tipp- und Tastaturinteraktionen während des gesamten Besuchs eines Nutzers auf einer Seite, wobei die längste Interaktion (unter Auslassung von Ausreißern) gemeldet wird. INP hat First Input Delay (FID) ersetzt. Ein guter INP-Wert liegt bei ≤200 Millisekunden. Dies ist entscheidend dafür, wie reaktionsschnell und flüssig sich die Anwendung bei Nutzerinteraktionen anfühlt. FID maß nur die Verzögerung der ersten Eingabe, während INP eine umfassendere Sicht auf die gesamte Interaktivität bietet.&lt;br /&gt;
Cumulative Layout Shift (CLS): Misst die visuelle Stabilität – die Gesamtsumme aller einzelnen Layout-Shift-Scores für jede unerwartete Layoutverschiebung, die während der gesamten Lebensdauer der Seite auftritt. Ein guter CLS-Wert liegt bei ≤0.1. Dies adressiert die Nutzerfrustration, die durch unerwartet auf der Seite verschobene Elemente verursacht wird und oft zu Fehlklicks führt. CLS wird durch die Impact Fraction (Anteil des betroffenen Bildschirms) und die Distance Fraction (wie weit sich ein Element verschoben hat) gemessen.&lt;br /&gt;
B. Performance-Messwerkzeuge: Google PageSpeed Insights, Lighthouse und Real User Monitoring (RUM)&lt;br /&gt;
Google PageSpeed Insights (PSI): Liefert sowohl Labordaten (über Lighthouse) als auch Felddaten (aus dem Chrome User Experience Report - CrUX) für eine bestimmte URL. Bietet Performance-Scores und Vorschläge.&lt;br /&gt;
Lighthouse: Ein Open-Source, automatisiertes Werkzeug zur Verbesserung der Qualität von Webseiten. Prüft Performance, Barrierefreiheit, PWA, SEO usw. in einer simulierten (Labor-)Umgebung.&lt;br /&gt;
Real User Monitoring (RUM) / Chrome User Experience Report (CrUX):&lt;br /&gt;
RUM sammelt Leistungsdaten von tatsächlichen Nutzern, die mit der Webseite in Echtzeit interagieren, und spiegelt dabei unterschiedliche Netzwerkbedingungen, Geräte und geografische Standorte wider.&lt;br /&gt;
CrUX ist ein öffentlicher Datensatz von Google, der widerspiegelt, wie reale Chrome-Nutzer beliebte Ziele im Web erleben, und liefert Felddaten für Core Web Vitals.&lt;br /&gt;
Diskrepanzen zwischen Labor- (Lighthouse/PSI Labor) und Felddaten (CrUX/RUM):&lt;br /&gt;
Die Labordaten stammen aus einer kontrollierten, simulierten Umgebung. Felddaten repräsentieren aggregierte reale Nutzererfahrungen über einen Zeitraum (z.B. 28 Tage für CrUX).&lt;br /&gt;
Faktoren, die Unterschiede verursachen:&lt;br /&gt;
Caching: Echte Nutzer profitieren vom Browser-Caching bei wiederholten Besuchen oder der Navigation innerhalb der Seite, was Labortests (insbesondere Kaltstarts) nicht immer widerspiegeln.&lt;br /&gt;
Nutzerbedingungen: Netzwerkqualität, Gerätefähigkeiten und geografischer Standort variieren bei echten Nutzern erheblich im Vergleich zu standardisierten Labortests.&lt;br /&gt;
Inhaltsvariationen: Cookie-Banner, A/B-Tests oder personalisierte Inhalte können sich zwischen Labortests und dem, was echte Nutzer sehen, unterscheiden.&lt;br /&gt;
Above-the-Fold vs. vollständige Seiteninteraktion: Lighthouse konzentriert sich primär auf den initialen Ladevorgang, während CrUX Metriken wie CLS und INP über den gesamten Lebenszyklus der Seite erfasst, einschließlich Verschiebungen und Interaktionen nach dem Laden.11&lt;br /&gt;
III. Client-seitige Performance-Optimierung&lt;br /&gt;
A. JavaScript-Optimierung&lt;br /&gt;
1. Minimierung der DOM-Manipulation und die Rolle des Virtual DOM&lt;br /&gt;
Das Document Object Model (DOM) repräsentiert eine Webseite als Baum von Objekten. Häufige direkte Manipulationen sind kostspielig, da sie Browser-Reflows (Neuberechnung des Layouts) und Repaints (Neuzeichnen von Teilen der Seite) auslösen können. Techniken zur Minimierung umfassen das Stapeln von DOM-Updates und die Verwendung von Document Fragments, um Änderungen außerhalb des DOM vorzunehmen, bevor sie angehängt werden. Der Virtual DOM (z.B. in React, Vue) ist eine Abstraktion, bei der Änderungen zuerst auf eine In-Memory-Repräsentation (Virtual DOM) angewendet werden. Ein &amp;quot;Diffing&amp;quot;-Algorithmus identifiziert dann minimale Änderungen, die am tatsächlichen DOM erforderlich sind, und optimiert so die Updates. Dies verbessert die Performance dynamischer Anwendungen mit häufigen UI-Aktualisierungen erheblich.&lt;br /&gt;
&lt;br /&gt;
2. Code-Minifizierung, Tree Shaking und Code Splitting&lt;br /&gt;
Minifizierung: Entfernt unnötige Zeichen (Leerzeichen, Kommentare) aus HTML-, CSS- und JavaScript-Dateien, um deren Größe zu reduzieren, was zu schnelleren Downloads und geringerem Bandbreitenverbrauch führt. &lt;br /&gt;
Tree Shaking (Dead Code Elimination): Ein Prozess, der typischerweise von Modul-Bundlern (z.B. webpack, Rollup) durchgeführt wird und ungenutzten JavaScript-Code entfernt, indem import- und export-Anweisungen in ES6-Modulen analysiert werden. Dies reduziert die endgültige Bundle-Größe.&lt;br /&gt;
Code Splitting: Zerlegt große JavaScript-Bundles in kleinere Chunks. Nur der für die initiale Ansicht notwendige Code wird geladen; andere Chunks werden bei Bedarf geladen (z.B. bei Navigation oder Interaktion). Dies verbessert die initiale Ladezeit.&lt;br /&gt;
3. Nutzung von Web Workern für Off-Thread-Ausführung&lt;br /&gt;
Web Worker ermöglichen die Ausführung von JavaScript in Hintergrund-Threads, getrennt vom Haupt-Browser-Thread, der UI-Updates und Nutzerinteraktionen behandelt.&lt;br /&gt;
&lt;br /&gt;
Anwendungsfälle umfassen das Auslagern CPU-intensiver Aufgaben wie komplexe Berechnungen, Datenverarbeitung oder Bildmanipulation, um ein Einfrieren der UI zu verhindern und die Reaktionsfähigkeit (INP) zu verbessern. Web Worker können nicht direkt auf das DOM zugreifen; die Kommunikation mit dem Haupt-Thread erfolgt über postMessage(). Im Gegensatz zu Service Workern, die als Netzwerk-Proxies für Caching, Offline-Support und Push-Benachrichtigungen dienen, sind Web Worker für allgemeine Hintergrundaufgaben zur Verbesserung der UI-Reaktionsfähigkeit gedacht.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
B. Optimierung des kritischen Rendering-Pfads&lt;br /&gt;
1. Critical CSS: Generierung und Inline-Implementierung&lt;br /&gt;
Critical CSS ist der minimale Teil von CSS, der erforderlich ist, um den Above-the-Fold-Inhalt einer Seite darzustellen - also alles, was der Benutzer beim Laden der Seite auf einem Bildschirm sehen kann, ohne zu scrollen. Sein Zweck ist es, renderblockierendes CSS für die initiale Ansicht zu eliminieren, wodurch der Browser Pixel viel schneller auf den Bildschirm malen kann, was FCP und LCP verbessert. Generierungsmethoden umfassen manuelle Extraktion (zeitaufwendig), Online-Generatoren (einfacher, aber möglicherweise nicht perfekt optimiert) und Tools/Bibliotheken wie Critical oder Penthouse (automatisiert, integrierbar in Build-Prozesse). Die Implementierung erfolgt durch Inline-Einbettung des Critical CSS in &amp;lt;style&amp;gt;-Tags im &amp;lt;head&amp;gt; des HTML-Dokuments.&lt;br /&gt;
&lt;br /&gt;
2. Asynchrones Laden von nicht-kritischem CSS und JavaScript&lt;br /&gt;
Nicht-kritisches CSS sind Stile, die nicht für das initiale Above-the-Fold-Rendering benötigt werden. Ladetechniken für CSS umfassen die Verwendung von &amp;lt;link rel=&amp;quot;preload&amp;quot; as=&amp;quot;style&amp;quot; onload=&amp;quot;this.onload=null;this.rel=&amp;#039;stylesheet&amp;#039;&amp;quot;&amp;gt; oder die Verwendung von media=&amp;quot;print&amp;quot; und das Austauschen zu media=&amp;quot;all&amp;quot; beim Laden. Nicht-kritisches JavaScript sind Skripte, die nicht für das initiale Seitenrendering oder die Interaktivität unerlässlich sind. Ladetechniken für JS umfassen die Attribute async und defer bei &amp;lt;script&amp;gt;-Tags. async lädt das Skript asynchron herunter und führt es aus, sobald es heruntergeladen ist, möglicherweise unterbricht es das HTML-Parsing. defer lädt das Skript asynchron herunter, führt es aber erst nach Abschluss des HTML-Parsings aus und in der Reihenfolge, in der sie im Dokument erscheinen.&lt;br /&gt;
&lt;br /&gt;
3. Vorladen von Schlüsselressourcen (rel=&amp;quot;preload&amp;quot;)&lt;br /&gt;
&amp;lt;link rel=&amp;quot;preload&amp;quot;&amp;gt; informiert den Browser, eine Ressource mit hoher Priorität abzurufen, da sie bald benötigt wird, typischerweise für Ressourcen, die vom Browser spät entdeckt werden. Der Browser ruft die Ressource ab und speichert sie im Cache, führt jedoch Skripte nicht aus oder wendet Stylesheets nicht sofort an. Anwendungsfälle umfassen CSS-Dateien, JavaScript-Dateien (kritische Chunks), Web-Fonts (erfordert crossorigin-Attribut) und Bilder (kritische Above-the-Fold-Bilder). Die Implementierung erfolgt über &amp;lt;link rel=&amp;quot;preload&amp;quot; href=&amp;quot;critical.js&amp;quot; as=&amp;quot;script&amp;quot;&amp;gt;, wobei das as-Attribut (z.B. style, script, font, image) entscheidend für die korrekte Priorisierung ist. Preloading kann LCP (für Fonts, Bilder) und INP verbessern und helfen, CLS (für Fonts) zu reduzieren. Es sollten nur kritische Ressourcen vorgeladen werden, da ein Überladen die Performance durch Ressourcenkonkurrenz beeinträchtigen kann.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
C. Asset-Optimierung&lt;br /&gt;
1. Bildoptimierung&lt;br /&gt;
Bilder machen oft den größten Teil des Seitengewichts aus. Moderne Formate wie WebP (bessere Kompression als JPEG/PNG, unterstützt Transparenz und Animation) und AVIF (noch effizienter als WebP, lizenzfrei) sollten bevorzugt werden. Das &amp;lt;picture&amp;gt;-Element kann für Art Direction und Fallbacks verwendet werden. Die Kompression sollte zwischen Dateigröße und visueller Qualität abgewogen werden (verlustbehaftet vs. verlustfrei). Responsive Bilder sollten mit srcset und sizes für verschiedene Bildschirmgrößen und Auflösungen bereitgestellt werden. Native Lazy Loading (loading=&amp;quot;lazy&amp;quot;) für Bilder unterhalb des Falzes verbessert die initiale Ladezeit. Die Angabe von width- und height-Attributen bei &amp;lt;img&amp;gt;-Tags verhindert Layoutverschiebungen (CLS).&lt;br /&gt;
&lt;br /&gt;
2. Web-Font-Optimierung&lt;br /&gt;
Priorisieren Sie WOFF2 wegen seiner überlegenen Kompression und breiten Browserunterstützung, mit WOFF als Fallback. Die CSS-Eigenschaft font-display (z.B. swap, fallback, optional) steuert das Rendering-Verhalten während des Ladens und verhindert FOUT/FOIT. Font-Subsetting, d.h. das Einbeziehen nur der benötigten Zeichen, reduziert die Dateigröße drastisch. Kritische Fonts sollten mit &amp;lt;link rel=&amp;quot;preload&amp;quot;&amp;gt; vorgeladen werden. Selbsthosting bietet mehr Kontrolle über Caching, während Drittanbieterdienste einfach zu verwenden sind und Fonts möglicherweise bereits im Browser des Nutzers zwischengespeichert sind.&lt;br /&gt;
Die Optimierung von Bildern und Schriftarten ist keine isolierte Aufgabe, sondern eine entscheidende Voraussetzung für gute LCP-, CLS- und sogar INP-Werte. Diese Assets sind oft die direkten Kandidaten für diese Metriken oder beeinflussen stark das Rendering und die Interaktivität von Elementen, die es sind. Das Largest Contentful Element ist häufig ein Bild oder ein großer Textblock. Optimierte Bilder laden schneller. Vorgeladene und optimierte Schriftarten stellen sicher, dass Text schnell und korrekt gerendert wird. Nicht dimensionierte Bilder sind eine Hauptursache für Layoutverschiebungen. Web-Schriftarten, die mit signifikanten Unterschieden zu Fallback-Schriftarten geladen werden, verursachen ebenfalls CLS. &lt;br /&gt;
IV. Server-seitige und Netzwerk-Performance-Optimierung&lt;br /&gt;
A. Content Delivery Networks (CDNs)&lt;br /&gt;
Ein CDN ist ein geografisch verteiltes Netzwerk von Proxy-Servern, das statische Inhalte (Bilder, CSS, JS) in Servern (Points of Presence - PoPs) näher am Endnutzer zwischenspeichert. Anfragen werden vom nächstgelegenen PoP bedient, was die Latenz reduziert.&lt;br /&gt;
CDN - Wichtige Vor- und Nachteile&lt;br /&gt;
&lt;br /&gt;
Aspekt&lt;br /&gt;
Beschreibung&lt;br /&gt;
Vorteile&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Schnellere Ladezeiten&lt;br /&gt;
Reduzierte Latenz durch geografische Nähe der Server zu den Nutzern.28&lt;br /&gt;
Geringere Bandbreitenkosten&lt;br /&gt;
Entlastung des Ursprungsservers von Traffic, da Inhalte von CDN-Servern ausgeliefert werden.28&lt;br /&gt;
Erhöhte Verfügbarkeit&lt;br /&gt;
Lastverteilung, Bewältigung von Traffic-Spitzen, Redundanz bei Ausfall des Ursprungsservers.29&lt;br /&gt;
Verbesserte Sicherheit&lt;br /&gt;
Können DDoS-Schutz und Web Application Firewalls (WAF) bieten.28&lt;br /&gt;
Skalierbarkeit&lt;br /&gt;
Fähigkeit, große Mengen an Traffic zu bewältigen und sich an neue Bandbreitenanforderungen anzupassen.28&lt;br /&gt;
Nachteile&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Kosten&lt;br /&gt;
CDN-Dienste sind mit Kosten verbunden, obwohl kostenlose Tarife existieren.29&lt;br /&gt;
Komplexität&lt;br /&gt;
Einrichtung und Verwaltung können die Infrastruktur komplexer machen.28&lt;br /&gt;
Kontrollverlust (teilweise)&lt;br /&gt;
Daten befinden sich auf Servern von Drittanbietern, was einen gewissen Kontrollverlust bedeutet.28&lt;br /&gt;
Dynamische Inhalte&lt;br /&gt;
Primär für statische Inhalte; dynamische Inhalte erfordern oft Anfragen an den Ursprungsserver, obwohl Lösungen existieren.28&lt;br /&gt;
Cache-Invalidierung&lt;br /&gt;
Sicherstellung der Aktualität zwischengespeicherter Inhalte kann herausfordernd sein.&lt;br /&gt;
SEO-Probleme (Fehlkonfig.)&lt;br /&gt;
Falsch konfigurierte CDNs können zu Problemen mit der Kanonisierung oder veralteten Inhalten führen.28&lt;br /&gt;
&lt;br /&gt;
B. Caching-Strategien&lt;br /&gt;
1. Browser Caching (Client-Seite)&lt;br /&gt;
Nutzt HTTP-Header, um den Browser anzuweisen, wie Ressourcen lokal zwischengespeichert werden sollen. Reduziert Serverlast und verbessert Ladezeiten für wiederholte Besuche. Wichtige HTTP-Header sind Cache-Control (mit Direktiven wie public, private, no-cache, max-age), Expires, ETag und Last-Modified.&lt;br /&gt;
2. Server-seitiges Caching&lt;br /&gt;
Speichert häufig abgerufene Daten oder vorberechnete Antworten auf dem Server, um die Verarbeitungslast und Datenbankabfragen zu reduzieren.30 Typen umfassen Full-Page Caching, Object Caching (z.B. mit Redis, Memcached), Opcode Caching und Datenbankabfrage-Caching.&lt;br /&gt;
3. Reverse Proxy Caching (z.B. Varnish, Nginx)&lt;br /&gt;
Ein Reverse Proxy sitzt vor den Webservern und kann Antworten zwischenspeichern, wodurch Anfragen direkt bedient werden, ohne den Ursprungsserver zu belasten.&lt;br /&gt;
4. Cache-Invalidierungs-Techniken&lt;br /&gt;
Sicherstellen, dass bei Datenänderungen die zwischengespeicherte Version aktualisiert oder entfernt wird, um die Bereitstellung veralteter Inhalte zu verhindern. Methoden umfassen TTL-basiertes Ablaufen, Purging, Write-Through Cache, ereignisgesteuerte Invalidierung, Versionierung und Stale-While-Revalidate.&lt;br /&gt;
Effektives Caching erfordert eine mehrschichtige Strategie (Browser, CDN, serverseitig, Reverse Proxy). Obwohl diese Schichten symbiotisch zusammenarbeiten, um die Leistung zu verbessern, können Fehlkonfigurationen oder unkoordinierte Cache-Invalidierungsrichtlinien zu unerwarteten Problemen mit veralteten Inhalten führen oder die Vorteile des Cachings zunichtemachen. Browser-Caching reduziert Netzwerkanfragen. CDN-Caching reduziert Anfragen an den Ursprungsserver. Serverseitiges Caching reduziert die Verarbeitung am Ursprungsserver. Jede Schicht hat jedoch ihre eigene Cache-Dauer (TTL) und Invalidierungsmechanismen. Wenn eine Ressource auf dem Ursprungsserver aktualisiert wird und der serverseitige Cache invalidiert wird, aber der CDN-Cache eine längere TTL hat und nicht aktiv geleert wird, erhalten Benutzer weiterhin die veraltete Version vom CDN. Dies unterstreicht die Notwendigkeit einer ganzheitlichen Sichtweise, bei der Cache-Control-Header, CDN-TTLs und serverseitige Invalidierungsstrategien koordiniert werden.&lt;br /&gt;
C. Verbesserung der Serverantwort und -auslieferung&lt;br /&gt;
1. Reduzierung der Time To First Byte (TTFB)&lt;br /&gt;
TTFB misst die Zeit von der initialen Anfrage bis zum Empfang des ersten Bytes der HTML-Antwort durch den Browser. Strategien umfassen Serverkonfiguration und -aktualisierung, Effizienz des Backend-Codes und Datenbankoptimierung (effiziente Abfragen, Indizierung, Verbindungspooling).&lt;br /&gt;
2. Textkomprimierung (GZIP, Brotli)&lt;br /&gt;
Komprimiert textbasierte Assets (HTML, CSS, JS) vor dem Senden vom Server zum Browser. GZIP ist weit verbreitet, Brotli bietet bessere Kompressionsraten.31 Dies reduziert Dateigrößen erheblich, was zu schnelleren Downloads führt.&lt;br /&gt;
3. Moderne Netzwerkprotokolle: HTTP/2 und HTTP/3 (QUIC) Vorteile&lt;br /&gt;
HTTP/1.1-Beschränkungen umfassen Head-of-Line-Blocking und textbasierte Header. HTTP/2 bietet Multiplexing (gleichzeitige Anfragen über eine TCP-Verbindung), Header-Komprimierung (HPACK), Server Push und ein binäres Protokoll. HTTP/3 läuft auf QUIC (UDP-basiert), löst TCP Head-of-Line-Blocking auf Transportebene, ermöglicht schnellere Verbindungsherstellung (0-RTT) und verbesserte Verbindungsmigration, mit standardmäßiger Verschlüsselung.&lt;br /&gt;
V. Fortgeschrittene Performance-Überlegungen&lt;br /&gt;
A. Web-Rendering-Strategien: CSR, SSR, SSG, Prerendering&lt;br /&gt;
Client-Side Rendering (CSR): Der Browser lädt eine minimale HTML-Hülle und JavaScript. JS ruft dann Daten ab und rendert den Seiteninhalt im Browser.&lt;br /&gt;
Vorteile: Hohe Interaktivität, schneller TTFB für die Hülle.&lt;br /&gt;
Nachteile: Langsames initiales Laden (FCP, LCP, TTI können hoch sein), schlechtes SEO ohne sorgfältige Handhabung.&lt;br /&gt;
Server-Side Rendering (SSR): Der Server generiert das vollständige HTML für eine Seite als Antwort auf eine Browseranfrage.&lt;br /&gt;
Vorteile: Schnellerer FCP/LCP, gut für SEO, aktuelle Inhalte.&lt;br /&gt;
Nachteile: Langsamerer TTFB, höhere Serverlast.&lt;br /&gt;
Static Site Generation (SSG): Alle HTML-Seiten werden zur Build-Zeit vorab generiert und als statische Dateien bereitgestellt.&lt;br /&gt;
Vorteile: Schnellster TTFB und Ladezeiten, sehr sicher, exzellent für SEO.&lt;br /&gt;
Nachteile: Lange Build-Zeiten für große Seiten, Inhalte können bis zum nächsten Build veraltet sein.&lt;br /&gt;
Prerendering (und hybride Ansätze): Generierung von statischem HTML für bestimmte Routen zur Build-Zeit, während die Haupt-App CSR sein kann. Universelles/Isomorphes Rendering: Initiale Seitenladung ist SSR, dann übernimmt clientseitiges JS.&lt;br /&gt;
Jede Strategie hat unterschiedliche Auswirkungen auf LCP, TTI, TTFB und SEO.&lt;br /&gt;
Die Wahl der Rendering-Strategie (CSR, SSR, SSG, Hybrid) ist nicht nur ein technisches Implementierungsdetail, sondern eine grundlegende architektonische Entscheidung, die nicht nur alle Core Web Vitals und die Gesamtleistung tiefgreifend beeinflusst, sondern auch die SEO-Effektivität, Entwicklungskomplexität, Infrastrukturkosten und die Fähigkeit zur Inhaltsdynamik. Diese Wahl muss mit den Kernanforderungen der Web-Anwendung übereinstimmen. SSR und SSG sind im Allgemeinen besser für SEO geeignet. SSR kann serverintensiv sein, während SSG sehr günstig zu hosten ist. SSR und hybride Modelle können die Entwicklungskomplexität erhöhen. SSG ist ungeeignet für stark personalisierte oder Echtzeit-Inhalte. &lt;br /&gt;
VI. Der Business Case für Web-Performance&lt;br /&gt;
A. Auswirkungen auf Key Performance Indicators (KPIs)&lt;br /&gt;
Konversionsraten: Schnellere Ladezeiten korrelieren direkt mit höheren Konversionsraten. Schon eine Sekunde Verzögerung kann zu einem signifikanten Rückgang führen. Eine Fallstudie zeigte einen Umsatzanstieg von 70% nach Optimierung der Ladezeit von 6.2s auf 2.8s.&lt;br /&gt;
Absprungraten: Langsamere Seiten führen zu höheren Absprungraten. 40% der Nutzer verlassen eine Seite, wenn sie länger als 3 Sekunden lädt.&lt;br /&gt;
Nutzerengagement &amp;amp; Sitzungsdauer: Schnellere Seiten ermutigen Nutzer, länger zu bleiben und mehr Seiten anzusehen. Seiten, die in &amp;lt; 5s laden, haben 70% längere Sitzungen.&lt;br /&gt;
SEO-Rankings: Google verwendet Seitengeschwindigkeit und Core Web Vitals als Rankingfaktoren.&lt;br /&gt;
Kundenzufriedenheit &amp;amp; Markenglaubwürdigkeit: Schnelle, zuverlässige Seiten bauen Vertrauen und Zufriedenheit auf.&lt;br /&gt;
B. Return on Investment (ROI) der Performance-Optimierung&lt;br /&gt;
Performance ist kein Kostenfaktor, sondern eine Investition mit greifbaren Erträgen. Dies umfasst gesteigerte Einnahmen durch höhere Konversionen, reduzierte Betriebskosten (z.B. geringere Bandbreitennutzung) und niedrigere Marketingkosten durch besseres SEO. &lt;br /&gt;
Angesichts der direkten und signifikanten Auswirkungen der Web-Performance auf Geschäfts-KPIs und den ROI kann Performance kein einmaliges Projekt sein. Sie erfordert kontinuierliche Überwachung, die Festlegung von Performance-Budgets und die Integration in CI/CD-Pipelines, um Regressionen zu verhindern und sich proaktiv an sich ändernde Nutzererwartungen und Geschäftsziele anzupassen. Dies verwandelt Performance von einer reaktiven Fehlerbehebung in einen proaktiven strategischen Vorteil. Webseiten sind dynamisch; neue Funktionen und Inhalte stellen ein Risiko für die Performance dar. Wenn die Performance nachlässt, leiden die Geschäfts-KPIs. Daher ist die Aufrechterhaltung einer guten Performance für den nachhaltigen Geschäftserfolg unerlässlich.&lt;/div&gt;</summary>
		<author><name>Völkl Anna</name></author>
	</entry>
	<entry>
		<id>https://mediawiki.fernfh.ac.at/mediawiki/index.php?title=Entwicklung_in_E-Commerce-Plattformen&amp;diff=6582</id>
		<title>Entwicklung in E-Commerce-Plattformen</title>
		<link rel="alternate" type="text/html" href="https://mediawiki.fernfh.ac.at/mediawiki/index.php?title=Entwicklung_in_E-Commerce-Plattformen&amp;diff=6582"/>
		<updated>2025-05-17T13:17:56Z</updated>

		<summary type="html">&lt;p&gt;Völkl Anna: Die Seite wurde neu angelegt: „= Entwicklung von E-Commerce-Plattformen = &amp;lt;p&amp;gt;Die Entwicklung von E-Commerce-Plattformen ist ein komplexes Feld, das sich von einfachen Online-Shops zu integrierten digitalen Handelssystemen gewandelt hat. Diese Systeme nutzen eine Vielzahl von Technologien, um den wachsenden Anforderungen gerecht zu werden.&amp;lt;/p&amp;gt; == Die Landschaft der E-Commerce-Technologie == &amp;lt;p&amp;gt;Die E-Commerce-Technologie ist ständig im Wandel, getrieben durch Kundenerwartungen und techn…“&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Entwicklung von E-Commerce-Plattformen =&lt;br /&gt;
&amp;lt;p&amp;gt;Die Entwicklung von E-Commerce-Plattformen ist ein komplexes Feld, das sich von einfachen Online-Shops zu integrierten digitalen Handelssystemen gewandelt hat. Diese Systeme nutzen eine Vielzahl von Technologien, um den wachsenden Anforderungen gerecht zu werden.&amp;lt;/p&amp;gt;&lt;br /&gt;
== Die Landschaft der E-Commerce-Technologie ==&lt;br /&gt;
&amp;lt;p&amp;gt;Die E-Commerce-Technologie ist ständig im Wandel, getrieben durch Kundenerwartungen und technologische Fortschritte wie personalisierte Erlebnisse, mobile Optimierung und KI-Integration. Nahtlose Omnichannel-Erlebnisse sind entscheidend geworden. Unternehmen müssen ihre Plattformen kontinuierlich anpassen, um relevant zu bleiben. KI und maschinelles Lernen (ML) verbessern Produktempfehlungen, Preisgestaltung und Chatbots. Diese Entwicklung erfordert flexible technologische Grundlagen.&amp;lt;/p&amp;gt;&lt;br /&gt;
== Bedeutung robuster und skalierbarer Architekturen ==&lt;br /&gt;
&amp;lt;p&amp;gt;Eine robuste und skalierbare Architektur ist fundamental, um Lastspitzen ohne Leistungseinbußen zu bewältigen und hohe Verfügbarkeit zu gewährleisten. Eine schlecht konzipierte Architektur führt zu langsamen Ladezeiten und Systemausfällen, was Umsatz und Markenimage schadet. Die Architektur muss das Geschäftswachstum unterstützen können, ohne (große) Neuentwicklungen zu erfordern. Moderne Ansätze wie Microservices, Headless Commerce und Composable Commerce bieten hier Vorteile, der sinnvolle Einsatz muss aber gegeben sein. Entwickler benötigen heute ein tiefes Verständnis von Systemarchitektur und Geschäftsprozessen.&amp;lt;/p&amp;gt;&lt;br /&gt;
= Webentwicklungs-Frameworks im E-Commerce =&lt;br /&gt;
&amp;lt;p&amp;gt;Frameworks beschleunigen die Entwicklung, verbessern Codequalität und Wartbarkeit von E-Commerce-Plattformen.&amp;lt;/p&amp;gt;&lt;br /&gt;
== Bedeutung von Frameworks ==&lt;br /&gt;
&amp;lt;p&amp;gt;Frameworks bieten vordefinierte Strukturen, wiederverwendbare Komponenten und bewährte Methoden (z.B. MVC-Muster), was Entwicklungszeit reduziert und Codeorganisation verbessert. Viele enthalten eingebaute Sicherheitsfunktionen und fördern Skalierbarkeit und Wartbarkeit.&amp;lt;/p&amp;gt;&lt;br /&gt;
=== PHP Backend-Frameworks ===&lt;br /&gt;
&amp;lt;p&amp;gt;Symfony und Laravel sind prominente PHP-Backend-Frameworks.&amp;lt;/p&amp;gt;&lt;br /&gt;
==== Symfony ====&lt;br /&gt;
&amp;lt;p&amp;gt;Symfony ist ein flexibles, skalierbares Open-Source PHP-Framework (MVC) mit wiederverwendbaren Komponenten (Bundles), geeignet für komplexe, maßgeschneiderte E-Commerce-Projekte (z.B. Shopware). Es erfordert eine anspruchsvollere Einarbeitung.&amp;lt;/p&amp;gt;&lt;br /&gt;
==== Laravel ====&lt;br /&gt;
&amp;lt;p&amp;gt;Laravel ist ein populäres Open-Source PHP-Framework (MVC), bekannt für elegante Syntax und schnelle Entwicklung. Es bietet viele eingebaute Werkzeuge und eignet sich für eine breite Palette von E-Commerce-Anwendungen, besonders bei schneller Markteinführung. Es ist tendenziell weniger flexibel bei tiefgreifenden Anpassungen als Symfony.&amp;lt;/p&amp;gt;&lt;br /&gt;
==== Andere PHP-Frameworks (z.B. CodeIgniter, Zend Framework/Laminas) ====&lt;br /&gt;
&amp;lt;p&amp;gt;CodeIgniter ist bekannt für geringe Größe und Performance. Laminas (vormals Zend Framework) ist robust und wurde in Enterprise-Anwendungen und älteren Magento-Versionen eingesetzt.&amp;lt;/p&amp;gt;&lt;br /&gt;
=== 3.3. JavaScript Frontend-Frameworks ===&lt;br /&gt;
&amp;lt;p&amp;gt;JavaScript-Frameworks erstellen dynamische, interaktive Storefronts und sind zentral für Headless-Architekturen.&amp;lt;/p&amp;gt;&lt;br /&gt;
==== React.js ====&lt;br /&gt;
&amp;lt;p&amp;gt;React.js (Meta) ist eine populäre Bibliothek für UIs mit komponentenbasierter Architektur und virtuellem DOM. Es ist relevant für SPAs und PWAs, oft mit Next.js für SSR/SSG. Magento PWA Studio und Shopifys Hydrogen basieren auf React.&amp;lt;/p&amp;gt;&lt;br /&gt;
=== Vue.js ===&lt;br /&gt;
&amp;lt;p&amp;gt;Vue.js ist ein flexibles, leichtgewichtiges Framework, bekannt für einfache Erlernbarkeit und Performance. Es wird in Shopware (Admin, Storefront) und für Headless-Magento (mit Nuxt.js) eingesetzt.&amp;lt;/p&amp;gt;&lt;br /&gt;
==== Angular ====&lt;br /&gt;
&amp;lt;p&amp;gt;Angular (Google) ist ein TypeScript-basiertes Framework für große, komplexe Anwendungen.. Es wird in Headless-Magento-Szenarien (mit Angular Universal) verwendet.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Die Entkopplung von Backend und Frontend im E-Commerce, insbesondere durch Headless-Architekturen, macht die Wahl des richtigen JavaScript-Frontend-Frameworks wichtig.&amp;lt;/p&amp;gt;&lt;br /&gt;
= Überblick über populäre E-Commerce-Plattformen und ihre Technologien =&lt;br /&gt;
&amp;lt;p&amp;gt;Die Wahl einer E-Commerce-Plattform hängt von technischen Anforderungen, Budget und Geschäftszielen ab.&amp;lt;/p&amp;gt;&lt;br /&gt;
== Magento (Adobe Commerce) ==&lt;br /&gt;
&lt;br /&gt;
*Architektur &amp;amp; Technologie: Traditionell monolithisch, entwickelt sich zu Headless. Nutzt MVVM-Muster. Backend primär PHP, mit Laminas (Zend) und Symfony-Komponenten. Frontend traditionell JS (Knockout.js), für PWA/Headless React (PWA Studio). Datenbank: MySQL/MariaDB.&lt;br /&gt;
*Zielmarkt: Mittlere bis große Unternehmen mit komplexen Anforderungen.&lt;br /&gt;
&lt;br /&gt;
== Shopify ==&lt;br /&gt;
&lt;br /&gt;
*Architektur &amp;amp; Technologie: SaaS-Plattform, Kern monolithisch, aber API-getrieben für Headless/Composable Commerce. Backend: Ruby on Rails. Frontend traditionell Liquid, für Headless React (Hydrogen). Datenbank: MySQL (intern).&lt;br /&gt;
*Zielmarkt: Kleine bis große Unternehmen, die eine gehostete Lösung suchen.&lt;br /&gt;
&lt;br /&gt;
== WooCommerce ==&lt;br /&gt;
&lt;br /&gt;
*Architektur &amp;amp; Technologie: WordPress-Plugin, Architektur eng mit WordPress (PHP, MySQL) verknüpft. Nutzt WordPress-Hooks für Erweiterungen. REST-API für Headless.&lt;br /&gt;
*Zielmarkt: Kleine bis mittlere Unternehmen, oft WordPress-Nutzer.&lt;br /&gt;
&lt;br /&gt;
== Shopware ==&lt;br /&gt;
&lt;br /&gt;
*Architektur &amp;amp; Technologie: Open-Source, API-First-Ansatz, modular. Backend: PHP (Symfony Framework). Frontend (Admin &amp;amp; Storefront): Vue.js. Datenbank: MySQL.&lt;br /&gt;
*Zielmarkt: Kleine bis Enterprise-Level B2C/B2B-Unternehmen.&lt;br /&gt;
&lt;br /&gt;
== BigCommerce ==&lt;br /&gt;
&lt;br /&gt;
*Architektur &amp;amp; Technologie: SaaS-Plattform, API-getrieben für Headless Commerce. Backend-Technologien (z.B. PHP) sind für Entwickler meist abstrahiert; Interaktion über REST/GraphQL-APIs. Frontend traditionell Stencil (Kombination aus Handlebars.js, JavaScript, and YAML Front Matter), für Headless beliebige JS-Frameworks. Datenbank: MySQL (intern).&lt;br /&gt;
*Zielmarkt: Wachsende KMU bis Enterprise-Kunden.&lt;br /&gt;
*&lt;br /&gt;
&amp;lt;p&amp;gt;Ein Trend über alle Plattformen ist die Investition in API-First-Strategien und Headless-Fähigkeiten, um flexiblere Benutzererlebnisse und Omnichannel-Strategien zu ermöglichen.&amp;lt;/p&amp;gt;&lt;br /&gt;
= Wesentliche technische Bestandteile von E-Commerce-Systemen =&lt;br /&gt;
&amp;lt;p&amp;gt;Moderne E-Commerce-Systeme bestehen aus vielen interagierenden technischen Komponenten.&amp;lt;/p&amp;gt;&lt;br /&gt;
== Architekturparadigmen im E-Commerce ==&lt;br /&gt;
=== Monolithische Architektur ===&lt;br /&gt;
&amp;lt;p&amp;gt;Alle Komponenten (Frontend, Backend, Datenzugriff) sind eng gekoppelt in einer Codebasis. Einfacher für kleine Projekte, aber schwer skalierbar und wartbar bei wachsender Komplexität.&amp;lt;/p&amp;gt;&lt;br /&gt;
=== Microservices-Architektur ===&lt;br /&gt;
&amp;lt;p&amp;gt;Eine Anwendung als Sammlung kleiner, unabhängiger, lose gekoppelter Dienste, jeder für eine spezifische Geschäftsfunktion. Verbessert Skalierbarkeit und technologische Flexibilität, aber erhöht Komplexität in Bereitstellung und Verwaltung.&amp;lt;/p&amp;gt;&lt;br /&gt;
=== Headless Commerce ===&lt;br /&gt;
&amp;lt;p&amp;gt;Frontend (&amp;quot;Head&amp;quot;) ist vollständig vom Backend entkoppelt; Kommunikation via APIs. Bietet maximale Frontend-Flexibilität, nicht immer die gepriesene schnelle Time-to-Market für neue Erlebnisse und unterstützt Omnichannel. Erfordert robuste APIs und kann initial teurer sein.&amp;lt;/p&amp;gt;&lt;br /&gt;
=== Composable Commerce ===&lt;br /&gt;
&amp;lt;p&amp;gt;Weiterentwicklung von Headless; Aufbau aus &amp;quot;Best-of-Breed&amp;quot;-Komponenten (Packaged Business Capabilities, PBCs) verschiedener Anbieter. Bietet maximale Flexibilität, Agilität und Zukunftsfähigkeit.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Moderne Architekturen tendieren zu entkoppelten Ansätzen, um Agilität zu gewährleisten, sind aber komplexer.&amp;lt;/p&amp;gt;&lt;br /&gt;
== Wichtige E-Commerce-Funktionalitäten (Anwendungsschicht) ==&lt;br /&gt;
=== Produktkatalogmanagement ===&lt;br /&gt;
&amp;lt;p&amp;gt;Erstellung, Organisation und Präsentation von Produktinformationen (Attribute, Varianten, Preise, Lagerbestand). PIM-Systeme zentralisieren dies bei komplexen Katalogen.&amp;lt;/p&amp;gt;&lt;br /&gt;
=== Warenkorb- und Checkout-Prozess ===&lt;br /&gt;
&amp;lt;p&amp;gt;Umfasst Warenkorb (Artikelauswahl, Mengen, Kostenübersicht) und Checkout-Schritte (Kundeninfo, Versand, Zahlung, Bestellübersicht, Bestätigung). Robustes Session-Management ist nötig.&amp;lt;/p&amp;gt;&lt;br /&gt;
=== Payment-Gateway-Integration ===&lt;br /&gt;
&amp;lt;p&amp;gt;Sichere Zahlungsabwicklung über Dienste wie Stripe, PayPal via APIs. PCI DSS Compliance ist zwingend für Kreditkartenzahlungen; zertifizierte Gateways reduzieren den Aufwand für Händler durch Tokenization und gehostete Zahlungsseiten.&amp;lt;/p&amp;gt;&lt;br /&gt;
=== Bestellmanagementsystem (Order Management System - OMS) ===&lt;br /&gt;
&amp;lt;p&amp;gt;Effiziente Abwicklung des Bestelllebenszyklus: Omnichannel-Erfassung, Verarbeitung, Lagerbestandsallokation, Order Routing, Versandkoordination, Kundenbenachrichtigungen, Retourenmanagement. Echtzeit-Lagerbestandsverfolgung ist eine Kernfunktion.&amp;lt;/p&amp;gt;&lt;br /&gt;
=== Benutzerkontenverwaltung ===&lt;br /&gt;
&amp;lt;p&amp;gt;Ermöglicht personalisierte Erlebnisse und vereinfachte Bestellungen (Registrierung, Login, Passwortverwaltung, Profil, Bestellhistorie, Adressbuch, gespeicherte Zahlungen).&amp;amp;nbsp;&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Im B2C Bereich ist vor allem eine schnelle, unkomplizierte Registrierung und Login (z.B. via Social Login via Google/Faceboo etc.) wichtig, um die Hürde für die Endkunden gering zu halten.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;In B2B Systemen ist meist ein Registrierungsprozess inkl. UID-Validierung erforderlich.&amp;lt;/p&amp;gt;&lt;br /&gt;
=== Suchfunktionalität ===&lt;br /&gt;
&amp;lt;p&amp;gt;Entscheidend für Produktentdeckung. Eine gute Suche liefert schnelle, relevante Ergebnisse und bietet Fehlertoleranz, Facettensuche, Autovervollständigung. Technologien: Elasticsearch, Apache Solr&amp;lt;/p&amp;gt;&lt;br /&gt;
=== APIs und Integrationen (Integrationsschicht) ===&lt;br /&gt;
&amp;lt;p&amp;gt;APIs verbinden E-Commerce-Plattformen mit internen (ERP, CRM) und externen Systemen (Zahlungsanbieter, Versand, Marktplätze). Gängige Integrationen: Payment Gateways, Versanddienstleister (Echtzeit-Raten, Labeldruck), ERP (Produktdaten, Lager, Bestellungen), CRM (Kundendaten, Interaktionen), Analyse- &amp;amp; Marketingtools. API-Design (REST, GraphQL) und Sicherheit sind wichtig.&amp;lt;/p&amp;gt;&lt;br /&gt;
= Sicherheit und Compliance in der E-Commerce-Entwicklung =&lt;br /&gt;
&amp;lt;p&amp;gt;Sicherheit und Einhaltung von Vorschriften sind im E-Commerce aufgrund sensibler Daten fundamental.&amp;lt;/p&amp;gt;&lt;br /&gt;
== Gängige Web-Schwachstellen und Prävention ==&lt;br /&gt;
&amp;lt;p&amp;gt;OWASP veröffentlicht alle 3 Jahre eine Liste mit den häufigsten Schwachstellen für Web-Anwendungen. Besonders bei E-Commerce Systemen und anderen kritischen Web-Anwendungen ist die Verhinderung bzw. Vermeidung der Sicherheitslücken besonders wichtig.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Die wichtigten Sicherheitsprobleme sind.&amp;lt;/p&amp;gt;&lt;br /&gt;
*SQL-Injection (SQLi): Prävention durch Prepared Statements (siehe Kapitel Datenbankintegration).&lt;br /&gt;
*Cross-Site Scripting (XSS): Einschleusen von bösartigem JavaScript. Prävention durch kontextsensitive Ausgabe-Kodierung (z.B. htmlspecialchars()) und Content Security Policy (CSP).&lt;br /&gt;
*Cross-Site Request Forgery (CSRF): Verleitet authentifizierte Nutzer zu unbeabsichtigten Aktionen. Prävention durch Anti-CSRF-Tokens und SameSite-Cookie-Attribut.&lt;br /&gt;
*Weitere relevante Schwachstellen: Broken Access Control, Security Misconfiguration, Vulnerable and Outdated Components, Server-Side Request Forgery (SSRF). Prävention durch sorgfältige Implementierung, Härtung, Updates und Validierung.&lt;br /&gt;
&lt;br /&gt;
== Datenschutz: DSGVO (GDPR) für Entwickler ==&lt;br /&gt;
&amp;lt;p&amp;gt;Die DSGVO betrifft alle, die personenbezogene Daten von EU-Bürgern verarbeiten.&amp;lt;/p&amp;gt;&lt;br /&gt;
*Schlüsselprinzipien: Rechtmäßigkeit, Einwilligung, Transparenz, Betroffenenrechte (Auskunft, Löschung etc.), Datensicherheit (&amp;quot;Privacy by Design/Default&amp;quot;), Meldepflicht bei Verletzungen.122&lt;br /&gt;
*Praktische Schritte: Daten-Audit, Datenschutzerklärung anpassen, Einwilligungsmechanismen implementieren, Sicherheitsmaßnahmen (Verschlüsselung, MFA), Mitarbeiterschulung, Prozesse für Betroffenenanfragen, Überprüfung von Drittanbieterverträgen (AVVs). Verstöße können hohe Bußgelder nach sich ziehen.&lt;br /&gt;
&lt;br /&gt;
== Zahlungssicherheit: PCI DSS Compliance ==&lt;br /&gt;
&amp;lt;p&amp;gt;Der Payment Card Industry Data Security Standard (PCI DSS) gilt für alle Unternehmen, die Kreditkartendaten speichern, verarbeiten oder übertragen&amp;lt;/p&amp;gt;&lt;br /&gt;
*PCI Levels (1-4): Basieren auf Transaktionsvolumen.&lt;br /&gt;
*Self-Assessment Questionnaires (SAQs): Für Level 2-4, Typ abhängig von Integrationsmethode (z.B. SAQ A für vollständiges Outsourcing, SAQ A-EP für teilweises Outsourcing mit Einfluss auf Transaktionssicherheit).&lt;br /&gt;
*Approved Scanning Vendor (ASV) Scans: Ggf. erforderliche vierteljährliche Schwachstellenscans.&lt;br /&gt;
*Rolle konformer Payment Gateways: Reduzieren PCI-Scope erheblich, wenn sie Kartendaten direkt erfassen (z.B. über gehostete Seiten/iFrames) und Tokenization verwenden.&lt;br /&gt;
&lt;br /&gt;
== E-Commerce-Betrugspräventionstechniken ==&lt;br /&gt;
&lt;br /&gt;
*Gängige Betrugsarten: Kontoübernahme (ATO), Zahlungsbetrug, Identitätsdiebstahl.&lt;br /&gt;
*Präventionsstrategien: Robuste Authentifizierung (2FA, MFA), fortschrittliche Betrugspräventions-Tools (ML, Verhaltensanalyse, Device Fingerprinting), Bot-Erkennung, Sicherheit bei Zahlungsabwicklung (AVS, CVV, 3D Secure, Tokenization), klare Betrugsmanagement-Richtlinien.&lt;br /&gt;
&lt;br /&gt;
= E-Commerce-Entwicklung =&lt;br /&gt;
&amp;lt;p&amp;gt;Der Einstieg in die E-Commerce-Entwicklung erfordert eine Kombination aus technischen Fähigkeiten und dem Verständnis von Geschäftsprozessen.&amp;lt;/p&amp;gt;&lt;br /&gt;
== Notwendige Fähigkeiten und Kenntnisse ==&lt;br /&gt;
&lt;br /&gt;
*Programmiersprachen: Fundierte Kenntnisse in Backend-Sprachen wie PHP, Python oder Ruby und Frontend-Sprachen (HTML, CSS, JavaScript) sind unerlässlich.&lt;br /&gt;
*Frameworks: Erfahrung mit relevanten Backend- (z.B. Symfony, Laravel für PHP) und Frontend-Frameworks (z.B. React, Vue.js, Angular) ist entscheidend.&lt;br /&gt;
*Datenbanken: Verständnis von relationalen (MySQL, PostgreSQL) und ggf. NoSQL-Datenbanken (MongoDB).&lt;br /&gt;
*Versionskontrolle: Umgang mit Git ist Standard.&lt;br /&gt;
*Paketmanagement: Je nach System kommen Paketmanager für Software-Abhängigkeiten zum Einsatz (npm, composer,...)&lt;br /&gt;
*E-Commerce-Plattform-Kenntnisse: Vertrautheit mit der Architektur und den Best Practise Anpassungsmöglichkeiten gängiger Plattformen (z.B. Magento, Shopify, WooCommerce) ist von Vorteil.&lt;br /&gt;
*API-Integration: Fähigkeit, mit REST- oder GraphQL-APIs zu arbeiten, um Drittanbieterdienste (Payment, Shipping etc.) zu integrieren.&lt;br /&gt;
*Sicherheitsbewusstsein: Kenntnis gängiger Web-Schwachstellen und Best Practices zur deren Vermeidung.&lt;br /&gt;
*Verständnis von Geschäftsprozessen: Einblick in typische E-Commerce-Abläufe wie Bestellmanagement, Produktkatalogpflege und Marketing ist hilfreich, um effektive technische Lösungen zu entwickeln.&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
== Typische Aufgaben in der E-Commerce-Entwicklung ==&lt;br /&gt;
&amp;lt;p&amp;gt;Die Aufgaben variieren je nach Spezialisierung (Frontend, Backend, Full-Stack) und Plattform, umfassen aber oft :&amp;lt;/p&amp;gt;&lt;br /&gt;
*Entwicklung und Wartung: Aufbau und Pflege von E-Commerce-Websites und -Anwendungen.&lt;br /&gt;
*Customizing von Plattformen: Anpassung von Standardplattformen (z.B. Magento, Shopify, WooCommerce) an spezifische Geschäftsanforderungen.&lt;br /&gt;
*Theme-Entwicklung: Erstellung oder Anpassung von Frontend-Designs und -Vorlagen.&lt;br /&gt;
*Plugin-/Modul-Entwicklung: Erstellung von Erweiterungen zur Ergänzung von Funktionalitäten.&lt;br /&gt;
*API-Integration: Anbindung von Drittanbieterdiensten wie Payment Gateways, Versandsystemen, ERP- oder CRM-Systemen.&lt;br /&gt;
*Performance-Optimierung: Sicherstellung schneller Ladezeiten und effizienter Systemleistung.&lt;br /&gt;
*Sicherheitsimplementierung: Anwendung von Best Practices zur Absicherung der Plattform und Kundendaten.&lt;br /&gt;
*Troubleshooting und Debugging: Identifizierung und Behebung technischer Probleme.&lt;br /&gt;
*Zusammenarbeit im Team: Kooperation mit Designern, Projektmanagern und anderen Entwicklern.&lt;/div&gt;</summary>
		<author><name>Völkl Anna</name></author>
	</entry>
	<entry>
		<id>https://mediawiki.fernfh.ac.at/mediawiki/index.php?title=DevOps_and_Continuous_Integration_Continuous_Deployment&amp;diff=6581</id>
		<title>DevOps and Continuous Integration Continuous Deployment</title>
		<link rel="alternate" type="text/html" href="https://mediawiki.fernfh.ac.at/mediawiki/index.php?title=DevOps_and_Continuous_Integration_Continuous_Deployment&amp;diff=6581"/>
		<updated>2025-05-17T12:40:19Z</updated>

		<summary type="html">&lt;p&gt;Völkl Anna: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= DevOps und Continuous Integration/Continuous Deployment (CI/CD) für Web-Anwendungen =&lt;br /&gt;
= Die Rolle von DevOps und CI/CD in der modernen Web-Anwendungsentwicklung =&lt;br /&gt;
&amp;lt;p&amp;gt;Die Entwicklung moderner Web-Anwendungen ist durch eine stetig steigende Komplexität und Dynamik gekennzeichnet. Unternehmen stehen vor der Herausforderung, qualitativ hochwertige Software immer schneller auf den Markt zu bringen, um wettbewerbsfähig zu bleiben und auf sich rasch ändernde Nutzeranforderungen reagieren zu können. In diesem Kontext haben sich DevOps und Continuous Integration/Continuous Deployment (CI/CD) als wichtige Methoden und Praktiken etabliert. Gitlab definiert DevOps folgendermaßen: “DevOps kombiniert Entwicklung (Dev; Development) und Betrieb (Ops; Operations), um die Effizienz, Geschwindigkeit und Sicherheit der Softwareentwicklung und -bereitstellung im Vergleich zu herkömmlichen Prozessen zu erhöhen. Ein flexiblerer Lebenszyklus der Softwareentwicklung führt zu einem Wettbewerbsvorteil für Unternehmen und ihre Kund(inn)en.” https://about.gitlab.com/de-de/topics/devops/#dev-ops-defined  &amp;lt;ref&amp;gt;https://about.gitlab.com/de-de/devops/&amp;lt;/ref&amp;gt; &amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;CI/CD sind die technischen Automatisierungsmechanismen. Die Kernherausforderung in der modernen Web-Entwicklung, nämlich die Balance zwischen Liefergeschwindigkeit, Qualität und Stabilität zu finden, wird durch DevOps und CI/CD direkt adressiert, indem Arbeitsabläufe optimiert und Schlüsselprozesse automatisiert werden.&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;Die Einführung von DevOps und CI/CD ist dabei nicht nur eine technische Aufrüstung, sondern eine strategische Notwendigkeit für Unternehmen, die Web-Anwendungen entwickeln. Die Marktanforderungen an Web-Anwendungen sind durch schnelle Feature-Entwicklungen und unmittelbares Nutzerfeedback geprägt. Traditionelle, isolierte Entwicklungsansätze stoßen hier schnell an ihre Grenzen und führen zu langsamen Release-Zyklen und potenziellen Qualitätsproblemen.&amp;amp;nbsp;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;Grundlagen: DevOps, Continuous Integration, Continuous Delivery und Continuous Deployment&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;Um die Rolle von CI/CD im Kontext von Web-Anwendungen vollständig zu erfassen, ist ein Verständnis der zugrundeliegenden Konzepte von DevOps sowie der spezifischen Praktiken von Continuous Integration, Continuous Delivery und Continuous Deployment unerlässlich.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Kernkonzepte und Ziele von DevOps und CI/CD ===&lt;br /&gt;
&amp;lt;p&amp;gt;DevOps wird als eine Kombination aus Praktiken und Werkzeugen definiert, die darauf abzielen, Anwendungen und Dienste mit hohem Automatisierungsgrad und Geschwindigkeit auszuliefern. Ein zentrales Element ist die Kombination von Entwicklungs- (Dev) und Betriebs- (Ops) Praktiken. Weitere wichtige Ziele von DevOps sind eine erhöhte Geschwindigkeit bei der Softwareauslieferung, schnelle Bereitstellung neuer Features, verbesserte Zuverlässigkeit und Skalierbarkeit der Systeme, eine optimierte Zusammenarbeit zwischen den Teams und die Integration von Sicherheitsmechanismen.&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;CI/CD steht für Continuous Integration und Continuous Delivery/Deployment und bezeichnet eine Reihe von Praktiken, die den Softwareentwicklungszyklus effizienter machen und beschleunigen sollen. Ziel ist es, Fehler zu vermeiden, einen kontinuierlichen Zyklus von Software-Updates aufrechtzuerhalten, die Komplexität zu verringern und die Effizienz zu steigern. CI/CD gilt als ein wesentlicher Bestandteil der DevOps-Methodik.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Abgrenzung und Zusammenspiel von Continuous Integration, Continuous Delivery und Continuous Deployment  ===&lt;br /&gt;
&amp;lt;p&amp;gt;Innerhalb des CI/CD-Spektrums gibt es wichtige Unterscheidungen zwischen Continuous Integration (CI), Continuous Delivery (CDel) und Continuous Deployment (CDep).&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;Da die Abkürzung CD sowohl für Continuous Integration, als auch für Continuous Delivery steht, werden manchmal auch die Begriffe CDel und CDep verwendet um eine bessere Unterscheidung zu ermöglichen.&amp;lt;/p&amp;gt;&lt;br /&gt;
*Continuous Integration (CI): Der Fokus von CI liegt auf dem häufigen Zusammenführen von Code-Änderungen verschiedener Entwickler in ein gemeinsames, zentrales Repository. Jede Integration löst automatisierte Build-Prozesse und Tests (insbesondere Unit- und Integrationstests) aus. Das Hauptziel ist schnelles Feedback an die Entwickler und die frühzeitige Erkennung von Integrationsproblemen und Fehlern.&lt;br /&gt;
*Continuous Delivery (CDel): Continuous Delivery erweitert CI, indem der Prozess der Softwarefreigabe bis hin zu verschiedenen Umgebungen (z.B. Staging- oder Testumgebungen) nach erfolgreichen Build- und Testphasen automatisiert wird. Die Software befindet sich dabei stets in einem auslieferbaren Zustand. Die endgültige Bereitstellung in der Produktionsumgebung erfordert jedoch typischerweise eine manuelle Genehmigung oder einen manuellen Anstoß. Ziel ist es, eine Codebasis zu haben, die jederzeit mit minimalem Aufwand in Produktion gebracht werden kann.&lt;br /&gt;
*Continuous Deployment (CDep): Continuous Deployment geht noch einen Schritt weiter als Continuous Delivery. Hierbei wird jede Code-Änderung, die alle automatisierten Tests erfolgreich durchlaufen hat, vollautomatisch und ohne manuellen Eingriff in die Produktionsumgebung ausgerollt. Das Ziel ist die automatische Freigabe von Updates direkt in die Produktivumgebung.&lt;br /&gt;
&amp;lt;h3 class=&amp;quot;mwt-heading&amp;quot; &amp;gt;Vorteile für die Web-Anwendungsentwicklung und den Geschäftswert&amp;lt;/h3&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;Die Implementierung von DevOps und CI/CD-Praktiken bietet eine Vielzahl von Vorteilen, sowohl auf technischer Ebene als auch im Hinblick auf den generierten Geschäftswert.&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;Zu den technischen Vorteilen zählen vor allem weniger Fehler im Produktivbetrieb, reduzierte Testkosten durch automatisierbare Tests und frühzeitige Fehlererkennung, schnellere Feedbackzyklen für Entwickler und eine insgesamt verbesserte Codequalität. Die Automatisierung von wiederkehrenden Aufgaben führt zu konsistenteren Ergebnissen und entlastet die Entwicklungsteams.&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;Diese technischen Verbesserungen schlagen sich direkt in signifikanten Geschäftsvorteilen nieder. Dazu gehören schnellere Deployments neuer Features und Updates, eine höhere Produktqualität, eine rasche Behebung von Problemen, gesteigerte Agilität des Unternehmens, die Förderung von Innovation durch Automatisierung, eine kontinuierliche Wertlieferung an den Kunden, reduzierte Produktionskosten, verbesserte Sicherheit und eine höhere Kundenzufriedenheit. Die verbesserte Zusammenarbeit zwischen den Teams ist ebenfalls ein wichtiger Faktor.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Die CI/CD-Pipeline für Web-Anwendungen: Aufbau und Visualisierung =&lt;br /&gt;
&amp;lt;p&amp;gt;Eine CI/CD-Pipeline ist eine Serie automatisierter Prozesse, die den Weg von der Code-Änderung bis zur Bereitstellung in der Produktion abbildet und manuelle Übergaben eliminiert. Sie ist das Kernstück der CI/CD-Implementierung.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Typische Phasen einer Pipeline: Build, Test, Deploy ===&lt;br /&gt;
&amp;lt;p&amp;gt;Eine typische CI/CD-Pipeline für Web-Anwendungen durchläuft mehrere Phasen, die aufeinander aufbauen:&amp;lt;/p&amp;gt;&lt;br /&gt;
*Build Stage (Erstellungsphase): Zu Beginn der Build-Phase wird der aktuelle Code aus einem Repository abgerufen. Dann erfolgt die Auflösung der Abhängigkeiten (z.B. mittels npm install für Node.js-Anwendungen, composer für PHP oder Maven für Java-Anwendungen), danach das Kompilieren (falls erforderlich) und das Verpacken der Anwendung in ausführbare Artefakte. Für Web-Anwendungen sind dies oft Docker-Images oder traditionelle Pakete wie JAR/WAR-Dateien. Auf auslieferbare .zip/.tar Dateien die auf einem Server entpackt werden, können Build-Artefakte sein.&lt;br /&gt;
&amp;lt;p&amp;gt;Ein wichtiges Prinzip ist &amp;quot;Build only once&amp;quot;: Das einmal erstellte Artefakt wird durch alle weiteren Phasen und Umgebungen bewegt.&amp;lt;/p&amp;gt;&lt;br /&gt;
*Test Stage (Testphase): Hier werden verschiedene automatisierte Tests ausgeführt, um die Qualität der Software sicherzustellen. Dazu gehören Unit-Tests, Integrationstests, funktionale Tests, End-to-End (E2E)-Tests, API-Tests, UI-Tests, Performancetests und Sicherheitstests. Schlagen Tests fehl, wird die Pipeline üblicherweise gestoppt und die Entwickler automatisch informiert.&lt;br /&gt;
*Deploy Stage (Bereitstellungsphase): In dieser Phase werden die erfolgreich gebauten und getesteten Artefakte in verschiedenen Umgebungen bereitgestellt, beginnend mit Staging- oder Testumgebungen bis hin zur Produktionsumgebung. Die Bereitstellung kann manuell angestoßen werden (Continuous Delivery) oder vollautomatisch erfolgen (Continuous Deployment). Hier können auch spezifische Deployment-Strategien wie Blue/Green oder Canary Releases zum Einsatz kommen.&lt;br /&gt;
&amp;lt;p&amp;gt;Nachgelagerte Phasen&amp;lt;/p&amp;gt;&lt;br /&gt;
*Cleanup: Dies umfasst das löschen, nicht mehr benötigter Dateien oder Verzeichnisse, die während der Deploy-Phase erstellt wurden sowie auch das herunterfahren oder löschen von (alten) Server-Nodes. Auch Routineaufgaben wie ein Cache-Warming, Indizierung oder Neustart benötigter Anwendungen ist möglich.&lt;br /&gt;
*Monitor/Verify: Nach dem Deployment ist die Überwachung der Anwendung in der Produktionsumgebung entscheidend. Dies umfasst das Tracking von Performance-Metriken (Antwortzeiten, Fehlerraten), Systemstabilität (CPU-, Speicher-, Netzwerkauslastung) und das Sammeln von Log-Daten zur Fehleranalyse und für das Nutzerverhalten. Diese Phase ist oft Teil von Continuous Delivery oder ein direkt anschließender Prozess.&lt;br /&gt;
&amp;lt;p&amp;gt;Die Sequenz von Build -&amp;amp;gt; Test -&amp;amp;gt; Deploy stellt sicher, dass jeder Schritt die Ausgabe des vorherigen Steps validiert. Die Automatisierung innerhalb jeder Phase gewährleistet Konsistenz und Geschwindigkeit. Das &amp;quot;Fail Fast&amp;quot;-Prinzip (Anhalten bei Fehlern) verhindert, dass fehlerhafter Code in nachfolgende Phasen gelangt, was Zeit und Ressourcen spart. Das &amp;quot;Build Once&amp;quot;-Prinzip stellt sicher, dass das, was getestet wird, auch das ist, was bereitgestellt wird, wodurch umgebungsspezifische Fehler reduziert werden.&amp;amp;nbsp;&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Werkzeuge und Technologien im CI/CD-Ökosystem =&lt;br /&gt;
&amp;lt;p&amp;gt;Das Ökosystem der CI/CD-Werkzeuge ist vielfältig und bietet Lösungen für unterschiedlichste Anforderungen und Unternehmensgrößen. Eine strategische Auswahl ist entscheidend für den Erfolg.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Überblick gängiger Tools ===&lt;br /&gt;
&amp;lt;p&amp;gt;Eine Vielzahl von Werkzeugen unterstützt die Implementierung von CI/CD-Pipelines für Web-Anwendungen. Zu den bekanntesten gehören:&amp;lt;/p&amp;gt;&lt;br /&gt;
*GitLab CI/CD: Tief in die GitLab-Plattform integriert, werden Pipelines über eine .gitlab-ci.yml-Datei im Repository definiert. GitLab CI bietet leistungsstarke Funktionen für die Web-App-Entwicklung, einschließlich integrierter Container-Registry und Review-Apps. Runner können selbst gehostet oder von GitLab bereitgestellt werden.&lt;br /&gt;
*Bitbucket Pipelines: Integriert in Bitbucket Cloud, nutzt eine bitbucket-pipelines.yml-Datei. Bietet gute Docker-Unterstützung und eignet sich besonders für Teams, die bereits die Atlassian-Produktpalette (Jira, Confluence) nutzen. Die Build-Umgebungen werden als Docker-Container ausgeführt.&lt;br /&gt;
*GitHub Actions: Direkt in GitHub integriert, werden Workflows über YAML-Dateien im .github/workflows-Verzeichnis definiert. Ein großer Marktplatz an wiederverwendbaren &amp;quot;Actions&amp;quot; ermöglicht flexible Anpassungen für diverse Web-Anwendungs-Szenarien. GitHub stellt gehostete Runner für Linux, Windows und macOS bereit, eigene Runner sind ebenfalls möglich.&lt;br /&gt;
*Jenkins: Ein sehr etabliertes, quelloffenes Automatisierungswerkzeug. Jenkins ist extrem erweiterbar durch ein riesiges Plugin-Ökosystem. Pipelines werden als Jenkinsfile (deklarativ oder skriptbasiert) definiert. Es kann komplex in der Einrichtung und Wartung sein, bietet aber enorme Flexibilität und Mächtigkeit.6 Jenkins benötigt in der Regel eine eigene Server-Infrastruktur für den Master und die Agents (Runner).&lt;br /&gt;
&amp;lt;p&amp;gt;Neben diesen gibt es weitere relevante Alternativen:&amp;lt;/p&amp;gt;&lt;br /&gt;
*CircleCI: Eine cloud-basierte CI/CD-Plattform, die Pipelines über eine config.yml-Datei konfiguriert. Bietet gute Docker-Unterstützung und eignet sich für Projekte unterschiedlicher Größe.&lt;br /&gt;
*Azure Pipelines (Teil von Azure DevOps): Bietet CI/CD-Dienste für jede Sprache und Plattform. Pipelines können über YAML oder einen klassischen grafischen Editor definiert werden. Starke Integration mit Azure Cloud-Diensten. Microsoft-gehostete und selbstgehostete Agents sind verfügbar.&lt;br /&gt;
*AWS CodePipeline: Ein vollständig verwalteter Continuous-Delivery-Dienst von Amazon Web Services. Er integriert sich nahtlos mit anderen AWS-Diensten wie AWS CodeCommit (Source), AWS CodeBuild (Build), AWS CodeDeploy (Deploy) sowie S3 und ECS für die Artefaktspeicherung und Ausführung von Web-Anwendungen.&lt;br /&gt;
*Travis CI: War historisch besonders populär für Open-Source-Projekte und ist bekannt für seine einfache Konfiguration mittels einer .travis.yml-Datei.&lt;br /&gt;
&lt;br /&gt;
=== Kriterien zur Auswahl geeigneter Werkzeuge ===&lt;br /&gt;
&amp;lt;p&amp;gt;Die Auswahl des passenden CI/CD-Werkzeugs ist eine strategische Entscheidung, die nicht nur die technische Ausführung, sondern auch die Produktivität des Teams, die Betriebskosten und die langfristige Wartbarkeit beeinflusst. Es geht nicht nur um einzelne Funktionen, sondern um die Passgenauigkeit des Werkzeugs zur übergeordneten IT-Strategie und den Fähigkeiten der Organisation. Folgende Kriterien sollten bei der Auswahl berücksichtigt werden:&amp;lt;/p&amp;gt;&lt;br /&gt;
*Integration mit dem Versionskontrollsystem (VCS): Eine nahtlose Integration mit dem genutzten VCS (z.B. Git, gehostet auf GitHub, GitLab, Bitbucket) ist fundamental. Werkzeuge, die tief in das VCS integriert sind, wie GitLab CI mit GitLab oder GitHub Actions mit GitHub, bieten oft eine reibungslosere Erfahrung und erfordern weniger Konfigurationsaufwand.31&lt;br /&gt;
*Skalierbarkeit und Performance: Das Werkzeug muss in der Lage sein, mit der Anzahl der Projekte, der Häufigkeit der Builds und der Komplexität der Pipelines zu skalieren.&lt;br /&gt;
*Unterstützung für Programmiersprachen und Frameworks: Es muss die spezifischen Technologien der Web-Anwendung unterstützen.&lt;br /&gt;
*Benutzerfreundlichkeit und Lernkurve: Die Komplexität des Werkzeugs und die Einarbeitungszeit für das Team sind wichtige Faktoren. Eine steile Lernkurve kann Produktivitätsgewinne zunichtemachen, wenn das Team nicht entsprechend geschult ist.&lt;br /&gt;
*Kosten: Die Kostenmodelle variieren stark (Open Source, Freemium, kommerzielle Lizenzen, nutzungsbasiert). Cloud-gehostete Lösungen haben andere Kostenstrukturen (einfachere Einrichtung, potenziell laufende Kosten) als selbstgehostete (mehr Kontrolle, mehr Wartungsaufwand). Der Total Cost of Ownership (TCO) sollte betrachtet werden.&lt;br /&gt;
*Community-Support und Plugin-Ökosystem: Besonders für Open-Source-Werkzeuge wie Jenkins ist eine aktive Community und ein breites Angebot an Plugins wichtig für Flexibilität und Problemlösung.&lt;br /&gt;
*Sicherheitsfunktionen und Compliance: Das Werkzeug sollte robuste Sicherheitsmechanismen für den Schutz von Code, Artefakten und Secrets bieten und die Einhaltung von Compliance-Richtlinien unterstützen.&lt;br /&gt;
*Vendor Lock-in: Die Abhängigkeit von einem bestimmten Anbieter und die Möglichkeit, bei Bedarf zu einem anderen Werkzeug zu wechseln, sollten bedacht werden.1&lt;br /&gt;
*Hosting-Optionen: Cloud-basiert, selbst-gehostet (On-Premise oder in eigener Cloud-Infrastruktur) oder Hybrid-Modelle.&lt;br /&gt;
&lt;br /&gt;
= Grundlegende Strategien und typische Schritte in CI/CD-Pipelines =&lt;br /&gt;
&amp;lt;p&amp;gt;Effektive CI/CD-Pipelines basieren auf durchdachten Strategien für Branching, Testing und Deployment, die eng miteinander verknüpft sind.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Branching-Strategien (z.B. GitFlow, Trunk-Based Development) und deren Einfluss auf CI/CD ===&lt;br /&gt;
&amp;lt;p&amp;gt;Die Wahl der Branching-Strategie im Versionskontrollsystem hat grundlegende Auswirkungen auf den Aufbau und die Effizienz der CI/CD-Pipeline und spiegelt die Philosophie einer Organisation hinsichtlich Release-Management und Risikotoleranz wider.&amp;lt;/p&amp;gt;&lt;br /&gt;
*GitFlow: Diese Strategie verwendet mehrere langlebige Branches wie main (oder master), develop, sowie temporäre feature, release und hotfix Branches. GitFlow bietet eine klare Struktur, insbesondere für Projekte mit geplanten Release-Zyklen. Allerdings kann es durch die längere Lebensdauer von Feature-Branches zu größeren und selteneren Integrationen kommen, was dem Prinzip der kontinuierlichen Integration entgegenstehen kann. Die Pipeline-Logik für GitFlow kann komplexer sein, da sie verschiedene Branch-Typen und deren spezifische Workflows (z.B. Merge-Strategien, Testumfänge) berücksichtigen muss.&lt;br /&gt;
*Trunk-Based Development (TBD): Bei TBD arbeiten Entwickler mit sehr kurzlebigen Feature-Branches, die häufig (mehrmals täglich) direkt in einen zentralen Haupt-Branch (den &amp;quot;Trunk&amp;quot;, oft main oder master) integriert werden. Dieser Ansatz steht in engem Einklang mit den Prinzipien von Continuous Integration, da er den Feedbackzyklus maximiert. Um unfertige Features im Trunk zu verwalten, werden häufig Feature Flags (oder Feature Toggles) eingesetzt, die es erlauben, Code in die Produktion zu deployen, ohne ihn für alle Nutzer freizuschalten. TBD wird oft als Voraussetzung für eine echte kontinuierliche Integration angesehen.&lt;br /&gt;
&amp;lt;p&amp;gt;Der Einfluss auf CI/CD ist signifikant: TBD führt im Allgemeinen zu einfacheren und schnelleren CI/CD-Pipelines, da kleine, häufige Integrationen weniger Konfliktpotenzial bergen und schneller verarbeitet werden können. GitFlows langlebige Branches können die Integration und das Feedback verzögern, was potenziell dem CI-Prinzip &amp;quot;früh und oft committen&amp;quot; widerspricht. TBD erzwingt eine häufige Integration in die Hauptlinie, was die Vorteile von CI maximiert, aber auch die Notwendigkeit schneller, zuverlässiger automatisierter Tests erhöht, um den Trunk stets &amp;quot;grün&amp;quot; (d.h. auslieferungsbereit) zu halten. Feature Flags in TBD entkoppeln das Deployment vom Release und ermöglichen es, Code kontinuierlich zusammenzuführen und bereitzustellen, auch wenn Funktionen noch nicht für den Benutzer bereit sind. Dies ist eine anspruchsvolle Technik, die eine hohe Deployment-Frequenz unterstützt.&amp;amp;nbsp;&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Automatisierte Teststrategien (Unit-, Integrations-, End-to-End-Tests) ===&lt;br /&gt;
&amp;lt;p&amp;gt;Eine umfassende, automatisierte Teststrategie ist das Rückgrat jeder zuverlässigen CI/CD-Pipeline. Sie stellt sicher, dass Fehler frühzeitig erkannt werden und die Qualität der Web-Anwendung kontinuierlich gewährleistet wird. Ein mehrschichtiger Ansatz, oft visualisiert als Testpyramide oder Testmatrix, ist hierbei gängige Praxis.&amp;lt;/p&amp;gt;&lt;br /&gt;
*Unit-Tests: Diese Tests validieren die kleinsten isolierbaren Code-Einheiten (z.B. Funktionen, Methoden, Klassen). Sie werden sehr häufig ausgeführt (idealweise bei jedem Commit), sind schnell und geben unmittelbares Feedback an die Entwickler.&amp;amp;nbsp;&lt;br /&gt;
*Integrationstests: Sie überprüfen das korrekte Zusammenspiel verschiedener Komponenten, Module oder Dienste einer Anwendung. Sie sind komplexer als Unit-Tests und decken Fehler in den Schnittstellen und Interaktionen auf.&lt;br /&gt;
*End-to-End (E2E)-Tests: Diese Tests simulieren vollständige Benutzer-Szenarien und validieren den gesamten Anwendungsfluss aus der Perspektive des Endnutzers. Sie sind am aufwendigsten zu erstellen und zu warten und haben die längste Ausführungszeit. Werkzeuge wie Selenium oder Cypress werden hier häufig eingesetzt.&lt;br /&gt;
*API-Tests: Fokussieren sich auf die direkte Überprüfung der Programmierschnittstellen (APIs) der Web-Anwendung, unabhängig von der Benutzeroberfläche.&lt;br /&gt;
*UI-Tests: Testen die grafische Benutzeroberfläche und deren Interaktionselemente. Sie sind oft Teil der E2E-Tests.&lt;br /&gt;
*Performancetests: Bewerten die Antwortzeiten, Stabilität und Skalierbarkeit der Web-Anwendung unter Last.7 Werkzeuge wie JMeter oder k6 sind hier verbreitet.&lt;br /&gt;
*Sicherheitstests: Umfassen statische (SAST) und dynamische (DAST) Anwendungssicherheitstests sowie Schwachstellen-Scans, um Sicherheitslücken frühzeitig zu identifizieren.&lt;br /&gt;
&amp;lt;p&amp;gt;Eine entscheidende Herausforderung ist die Balance zwischen Testabdeckung und der Geschwindigkeit der Pipeline. Zu viele langsame Tests können den Feedbackzyklus verlangsamen und die Akzeptanz von CI/CD behindern.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Deployment-Strategien (z.B. Blue/Green, Canary, Rolling Deployments) ===&lt;br /&gt;
&amp;lt;p&amp;gt;Deployment-Strategien definieren, wie neue Versionen einer Web-Anwendung in die Produktionsumgebung ausgerollt werden. Die Wahl der Strategie beeinflusst das Risiko, die Ausfallzeit und das Nutzererlebnis während des Updates. CI/CD-Werkzeuge spielen eine Schlüsselrolle bei der Automatisierung dieser Strategien.58&amp;lt;/p&amp;gt;&lt;br /&gt;
*Blue/Green Deployment: Hierbei werden zwei identische Produktionsumgebungen unterhalten: &amp;quot;Blue&amp;quot; (die aktuelle Live-Version) und &amp;quot;Green&amp;quot; (die neue Version). Die neue Version wird auf der Green-Umgebung bereitgestellt und getestet. Ist alles erfolgreich, wird der Traffic vom Load Balancer von Blue auf Green umgeschaltet. Vorteile sind minimale bis keine Ausfallzeit und ein sehr einfacher Rollback (Umschalten zurück auf Blue). Nachteilig sind die höheren Infrastrukturkosten für die doppelte Umgebung.&lt;br /&gt;
*Canary Release (Canary Deployment): Die neue Version wird zunächst nur einem kleinen Prozentsatz der Nutzer oder Server (&amp;quot;Canaries&amp;quot;) zur Verfügung gestellt. Das Verhalten und die Performance werden engmaschig überwacht. Bei positivem Ergebnis wird der Anteil der Nutzer, die die neue Version erhalten, schrittweise erhöht, bis alle Nutzer umgestellt sind. Diese Methode ermöglicht frühzeitiges Feedback und begrenzt den &amp;quot;Blast Radius&amp;quot; potenzieller Fehler.&lt;br /&gt;
*Rolling Deployment: Updates werden schrittweise auf den Instanzen der Produktionsumgebung ausgerollt. Eine Teilmenge der Server wird aktualisiert, überwacht, und dann folgt die nächste Teilmenge. Während des Rollouts können unterschiedliche Versionen der Anwendung gleichzeitig aktiv sein, was zu Kompatibilitätsüberlegungen führen kann.&lt;br /&gt;
*A/B Testing: Diese Strategie wird oft für das Testen neuer Features oder Designs verwendet, indem verschiedene Versionen (A und B) parallel an unterschiedliche Nutzersegmente ausgeliefert werden, um deren Performance anhand definierter Metriken zu vergleichen. Feature Flags sind hier ein wichtiges Hilfsmittel.&lt;br /&gt;
*Weitere Strategien:&lt;br /&gt;
*Big Bang Deployment: Alle Änderungen werden auf einmal für alle Nutzer ausgerollt. Dies ist die riskanteste Methode mit potenziell hohen Ausfallzeiten.&lt;br /&gt;
*Recreate Deployment: Die alte Version wird gestoppt und die neue Version wird komplett neu bereitgestellt. Dies führt zu Ausfallzeiten.&lt;br /&gt;
&amp;lt;p&amp;gt;Deployment-Strategien beeinflussen direkt Risiko, Ausfallzeiten und Nutzererfahrung.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Weiterführende Themen =&lt;br /&gt;
&amp;lt;p&amp;gt;Über die Grundlagen hinaus gewinnen im Kontext von CI/CD für Web-Anwendungen fortgeschrittene Konzepte an Bedeutung: Dazu zählen Infrastructure as Code, DevSecOps, Monitoring und Observability sowie die Messung des Erfolgs mittels Metriken.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Infrastructure as Code (IaC) und dessen Integration in CI/CD ===&lt;br /&gt;
&amp;lt;p&amp;gt;Infrastructure as Code (IaC) bezeichnet die Praxis, IT-Infrastruktur (Server, Netzwerke, Speicher, Datenbanken etc.) durch Code – typischerweise in Form von Konfigurationsdateien – zu definieren, zu provisionieren und zu verwalten, anstatt manuelle Prozesse zu nutzen. Wichtige Prinzipien von IaC sind Idempotenz (mehrmalige Ausführung führt zum selben Ergebnis), Versionierung der Infrastrukturbeschreibungen und die Automatisierung der Infrastrukturänderungen.&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;Die Vorteile von IaC sind vielfältig: Es ermöglicht Konsistenz über verschiedene Umgebungen hinweg, Wiederholbarkeit von Setups, einfache Skalierbarkeit, schnellere Deployments, eine Reduktion menschlicher Fehler und die Versionierung der Infrastruktur analog zum Anwendungscode.&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;Gängige Werkzeuge für IaC sind:&amp;lt;/p&amp;gt;&lt;br /&gt;
*Terraform: Ein deklaratives, Cloud-agnostisches Werkzeug von HashiCorp, das primär für die Provisionierung von Infrastrukturressourcen verwendet wird.&lt;br /&gt;
*Ansible: Ein prozedurales Werkzeug, das oft für Konfigurationsmanagement und Anwendungsdeployment auf bereits provisionierter Infrastruktur eingesetzt wird.&lt;br /&gt;
*Weitere Werkzeuge umfassen AWS CloudFormation, Azure Resource Manager (ARM) Templates oder Pulumi.&lt;br /&gt;
&amp;lt;p&amp;gt;Die Integration von IaC in CI/CD-Pipelines bedeutet, dass Änderungen an der Infrastruktur denselben rigorosen Prozess aus Versionierung, automatisierten Tests und kontrolliertem Deployment durchlaufen wie der Anwendungscode Dies stellt sicher, dass Entwicklungs-, Staging- und Produktionsumgebungen konsistent sind, was eine häufige Fehlerquelle eliminiert.&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;IaC erweitert die &amp;quot;Alles als Code&amp;quot;-Philosophie auf den gesamten Technologie-Stack und verbessert die Zuverlässigkeit und Geschwindigkeit der Bereitstellung von Web-Anwendungen. Manuelle Infrastrukturprovisionierung ist langsam und fehleranfällig. IaC hingegen ermöglicht die Definition, Versionierung und automatische Provisionierung der Infrastruktur. In CI/CD integriert bedeutet dies, dass die Anwendung, ihre Abhängigkeiten und die benötigte Infrastruktur gemeinsam bereitgestellt und validiert werden können. Diese holistische Automatisierung ist entscheidend für komplexe Web-Anwendungen, insbesondere solche, die Microservices oder Cloud-Plattformen nutzen, da sie sicherstellt, dass die Anwendung von der Entwicklung bis zur Produktion in einer bekannten, konsistenten und reproduzierbaren Umgebung läuft. Dies reduziert drastisch &amp;quot;Works on my machine&amp;quot;-Probleme und beschleunigt den gesamten Lieferzyklus.&amp;amp;nbsp;&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== DevSecOps: Sicherheit als integraler Bestandteil der Pipeline ===&lt;br /&gt;
&amp;lt;p&amp;gt;DevSecOps ist ein Ansatz, der Sicherheitspraktiken von Beginn an und während des gesamten DevOps-Lebenszyklus integriert, anstatt Sicherheit als nachgelagerten Schritt zu betrachten. Dies wird oft als &amp;quot;Shift Left&amp;quot;-Prinzip bezeichnet, bei dem Sicherheitsüberlegungen so früh wie möglich in den Entwicklungsprozess einfließen. Sicherheit wird dabei zur gemeinsamen Verantwortung von Entwicklungs-, Betriebs- und Sicherheitsteams.&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;Die Bedeutung von DevSecOps liegt darin, dass die frühzeitige Adressierung von Sicherheitsaspekten Kosten und Risiken reduziert. Traditionelle Sicherheitsmodelle, bei denen Sicherheitstests erst am Ende des Entwicklungszyklus stattfinden, können in agilen Umgebungen mit häufigen Releases zu Engpässen werden.&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;Die Integration von Sicherheit in die CI/CD-Pipeline erfolgt durch verschiedene automatisierte Maßnahmen:&amp;lt;/p&amp;gt;&lt;br /&gt;
*Static Application Security Testing (SAST): Analyse des Quellcodes auf bekannte Schwachstellenmuster, bevor die Anwendung kompiliert oder deployed wird. Werkzeuge wie Bandit, Semgrep, SonarQube oder Checkmarx können in die Build-Phase integriert werden.&lt;br /&gt;
*Dynamic Application Security Testing (DAST): Testen der laufenden Anwendung auf Schwachstellen, oft in einer Staging-Umgebung, indem typische Angriffsmuster simuliert werden. OWASP ZAP oder Burp Suite sind hier gängige Werkzeuge.&lt;br /&gt;
*Software Composition Analysis (SCA) / Dependency Scanning: Überprüfung von Drittanbieter-Bibliotheken und Abhängigkeiten auf bekannte Sicherheitslücken.&lt;br /&gt;
*Container Image Scanning: Scannen von Docker-Images auf bekannte Schwachstellen in den Basissystemen und installierten Paketen.&lt;br /&gt;
*Secrets Management: Sichere Verwaltung und Injektion von sensiblen Daten wie API-Schlüsseln, Passwörtern und Zertifikaten in die Pipeline und Anwendungen, ohne sie im Code zu speichern.&lt;br /&gt;
*Infrastructure Security: Überprüfung von IaC-Skripten auf Sicherheitsfehlkonfigurationen.&lt;br /&gt;
&amp;lt;p&amp;gt;Durch die Automatisierung von Sicherheitsprüfungen innerhalb der CI/CD-Pipeline können Unternehmen die Entwicklungsgeschwindigkeit beibehalten, ohne die Sicherheit zu kompromittieren, was für das Vertrauen in Web-Anwendungen unerlässlich ist. Dadurch können Schwachstellen behoben werden, wenn es am kostengünstigsten und einfachsten ist – während der Entwicklung. Die Automatisierung dieser Prüfungen gewährleistet Konsistenz und Abdeckung und reduziert die Wahrscheinlichkeit menschlicher Fehler.&amp;amp;nbsp;&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Monitoring, Logging und Observability zur Sicherstellung von Qualität und Performance ===&lt;br /&gt;
&amp;lt;p&amp;gt;Nach dem Deployment einer Web-Anwendung ist die kontinuierliche Überwachung ihrer Qualität und Performance unerlässlich. Hierbei unterscheidet man zwischen Monitoring, Logging und dem umfassenderen Konzept der Observability.&amp;lt;/p&amp;gt;&lt;br /&gt;
*Monitoring (Überwachung): Bezieht sich auf das Sammeln, Verarbeiten, Analysieren und Darstellen von vordefinierten Metriken, um den Zustand eines Systems zu verstehen und bekannte Probleme oder Anomalien zu erkennen. Es ist oft reaktiv und löst Alarme aus, wenn bestimmte Schwellwerte überschritten werden.&lt;br /&gt;
*Logging (Protokollierung): Umfasst das Erfassen von detaillierten, zeitgestempelten Aufzeichnungen von Ereignissen, Fehlern und Systemaktivitäten.23 Logs sind unerlässlich für die Fehlerdiagnose und das Verständnis des Systemverhaltens im Detail.&lt;br /&gt;
*Observability (Beobachtbarkeit): Geht über das reine Monitoring hinaus und beschreibt die Fähigkeit, aus den externen Outputs eines Systems Rückschlüsse auf dessen internen Zustand zu ziehen und auch unbekannte Probleme (&amp;quot;unknown unknowns&amp;quot;) zu untersuchen. Observability basiert typischerweise auf den drei Säulen: Logs, Metriken und Traces (verteilte Ablaufverfolgung). Sie ermöglicht eine proaktive Untersuchung des Systemverhaltens.&lt;br /&gt;
&amp;lt;p&amp;gt;Die Bedeutung im CI/CD-Kontext liegt darin, dass diese Praktiken unmittelbares Feedback über den Erfolg und die Auswirkungen von Deployments liefern. Sie helfen, Probleme in der Produktionsumgebung schnell zu erkennen und zu diagnostizieren, und die gewonnenen Erkenntnisse fließen zurück in die Optimierung der Pipeline und der Anwendung selbst. Gängige Werkzeuge sind Datadog, Prometheus mit Grafana, Splunk oder der ELK Stack (Elasticsearch, Logstash, Kibana).&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;Die Entwicklung vom Monitoring zur Observability im CI/CD-Kontext spiegelt die zunehmende Komplexität moderner Web-Anwendungen wider (z.B. Microservices). Observability ist entscheidend für die Aufrechterhaltung von Zuverlässigkeit und Performance in dynamischen, häufig aktualisierten Systemen. Einfaches Monitoring bekannter Metriken reicht für komplexe verteilte Systeme, bei denen Fehlermodi nicht immer vorhersagbar sind, oft nicht aus. Observability ermöglicht es Teams durch die Kombination von Metriken, Logs und Traces zu verstehen, warum etwas passiert, nicht nur, dass etwas nicht stimmt. In einer CI/CD-Umgebung mit häufigen Deployments ist die Fähigkeit, die Auswirkungen neuer Änderungen schnell zu diagnostizieren und zu verstehen, entscheidend für die Einhaltung des MTTR (Mean Time to Recovery). Dieses tiefere Verständnis ermöglicht eine effektivere Fehlerbehebung und eine kontinuierliche Verbesserung sowohl der Anwendung als auch der CI/CD-Pipeline selbst.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Relevante Metriken und KPIs zur Erfolgsmessung (z.B. DORA Metriken) ===&lt;br /&gt;
&amp;lt;p&amp;gt;Um die Effektivität von CI/CD-Prozessen und deren Beitrag zum Geschäftserfolg zu bewerten, ist die Messung anhand relevanter Metriken und Key Performance Indicators (KPIs) unerlässlich.&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;Die DORA-Metriken, benannt nach der DevOps Research and Assessment Gruppe, gelten als Industriestandard zur Messung der Leistungsfähigkeit von Software-Delivery-Prozessen:&amp;lt;/p&amp;gt;&lt;br /&gt;
*Deployment Frequency (DF) / Bereitstellungshäufigkeit: Wie oft wird Code erfolgreich in die Produktion überführt? Misst die Geschwindigkeit und Agilität des Teams.&lt;br /&gt;
*Lead Time for Changes (LT) / Durchlaufzeit für Änderungen: Wie lange dauert es von der Code-Commit bis zum erfolgreichen Deployment in die Produktion? Indikator für die Effizienz des gesamten Entwicklungsprozesses.&lt;br /&gt;
*Change Failure Rate (CFR) / Fehlerrate von Änderungen: Welcher Prozentsatz der Deployments führt zu Fehlern in der Produktion, die ein Eingreifen erfordern (z.B. Rollback, Hotfix)? Misst die Stabilität und Qualität der Releases.&lt;br /&gt;
*Mean Time to Recovery (MTTR) / Mittlere Wiederherstellungszeit: Wie lange dauert es im Durchschnitt, einen Dienst nach einem Ausfall in der Produktion wiederherzustellen? Zeigt die Resilienz des Systems und die Effektivität der Incident-Response-Prozesse.&lt;br /&gt;
&amp;lt;p&amp;gt;Neben den DORA-Metriken sind weitere KPIs relevant:&amp;lt;/p&amp;gt;&lt;br /&gt;
*Build-Dauer: Zeit für die Durchführung der Build-Phase.&lt;br /&gt;
*Testabdeckung (Test Coverage): Prozentsatz des Codes, der durch automatisierte Tests abgedeckt ist.&lt;br /&gt;
*Defect Escape Rate: Anzahl der Fehler, die erst in der Produktion entdeckt werden.&lt;br /&gt;
*Anwendungs-Performance-Metriken: Antwortzeiten, Fehlerraten der Anwendung.&lt;br /&gt;
*Infrastrukturauslastung und -kosten.&lt;br /&gt;
&lt;br /&gt;
= Herausforderungen, Anti-Patterns und Best Practices =&lt;br /&gt;
&amp;lt;p&amp;gt;Trotz der erheblichen Vorteile ist die Implementierung von CI/CD nicht ohne Hürden. Es gibt typische Herausforderungen und Anti-Patterns, denen jedoch mit etablierten Best Practices begegnet werden kann.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Herausforderungen und Anti-Patterns ===&lt;br /&gt;
&amp;lt;p&amp;gt;Zu den häufigsten Herausforderungen bei der CI/CD-Implementierung für Web-Anwendungen gehören Performance-Probleme in der Pipeline, mangelnde Teamkommunikation, Komplexität bei der Versionskontrolle und im Branching, fehlerhafte oder unzureichende automatisierte Tests, Sicherheitslücken, Skalierungsprobleme der Infrastruktur, schwierige Fehlersuche, häufige Build-Fehler, Probleme im Abhängigkeitsmanagement, Inkonsistenzen zwischen Umgebungen, überkomplizierte Pipelines, mangelnde Testabdeckung, Lücken im Monitoring und Reporting sowie ineffektive Rollback-Mechanismen. Ein signifikanter Faktor ist oft auch der Widerstand gegen Veränderungen in etablierten Prozessen und Kulturen.&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;Typische Anti-Patterns, die den Erfolg von CI/CD behindern, sind:&amp;lt;/p&amp;gt;&lt;br /&gt;
*Mangelnde Zusammenarbeit: Teams arbeiten weiterhin in Silos.&lt;br /&gt;
*Übermäßige Abhängigkeit von manuellen Prozessen: Wichtige Schritte in der Pipeline werden nicht automatisiert.&lt;br /&gt;
*Ignorieren von Sicherheit: Sicherheitsaspekte werden erst spät oder gar nicht berücksichtigt (Security as an Afterthought).&lt;br /&gt;
*Unzureichende Tests: Zu geringe Testabdeckung oder ineffektive Teststrategien.&lt;br /&gt;
*Widerstand gegen Veränderungen: Festhalten an alten Arbeitsweisen und Werkzeugen.&lt;br /&gt;
*Schlechtes Monitoring und Feedback: Fehlende Transparenz über den Zustand der Pipeline und der Deployments.&lt;br /&gt;
*Mehrfaches Bauen von Artefakten: Das gleiche Artefakt wird für verschiedene Stufen oder Umgebungen immer wieder neu gebaut, anstatt das einmal gebaute Artefakt weiterzureichen.&lt;br /&gt;
&lt;br /&gt;
=== Best Practices ===&lt;br /&gt;
&amp;lt;p&amp;gt;Um diesen Herausforderungen zu begegnen und Anti-Patterns zu vermeiden, haben sich folgende Best Practices etabliert 15:&amp;lt;/p&amp;gt;&lt;br /&gt;
*Früh und oft committen (Commit Early, Commit Often): Kleine, häufige Code-Änderungen erleichtern die Integration und Fehlersuche.&lt;br /&gt;
*Builds grün halten (Keep Builds Green): Fehlerhafte Builds sofort beheben, um die Pipeline nicht zu blockieren.&lt;br /&gt;
*Einmal bauen (Build Only Once): Das kompilierte Artefakt einmal erstellen und dieses durch alle Test- und Deployment-Stufen bewegen.&lt;br /&gt;
*Tests optimieren (Streamline Tests): Eine ausgewogene Teststrategie mit schnellen Feedbackzyklen entwickeln.&lt;br /&gt;
*Umgebungen sauber halten (Clean Environments): Testumgebungen regelmäßig zurücksetzen oder neu erstellen (IaC hilft hier).&lt;br /&gt;
*Pipeline sichern (Secure Your Pipeline): Sicherheitsmaßnahmen in jede Phase integrieren.&lt;br /&gt;
*Prozess einhalten (Stick to Your Process): Ausnahmen und manuelle Eingriffe minimieren.&lt;br /&gt;
*Pipeline überwachen und messen (Monitor and Measure Your Pipeline): Metriken nutzen, um Engpässe und Verbesserungspotenziale zu identifizieren.&lt;br /&gt;
*Teamarbeit fördern (Make it a Team Effort): CI/CD ist eine gemeinsame Verantwortung.&lt;br /&gt;
*Wiederverwendbare/Gemeinsame Pipelines nutzen (Use Shared Pipelines - DRY): Duplizierung von Pipeline-Logik vermeiden.15&lt;br /&gt;
*Progressive Delivery anwenden: Strategien wie Canary Releases oder Blue/Green Deployments nutzen, um Risiken zu minimieren.15&lt;br /&gt;
&lt;br /&gt;
= Fallbeispiele und Ausblick =&lt;br /&gt;
&amp;lt;p&amp;gt;Die erfolgreiche Implementierung von CI/CD hat bei vielen führenden web-zentrierten Unternehmen zu signifikanten Verbesserungen geführt.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Fallbeispiele ===&lt;br /&gt;
&lt;br /&gt;
*Netflix: Das Unternehmen transformierte seine Architektur hin zu Microservices und entwickelte mit Spinnaker eine eigene Open-Source Continuous Delivery Plattform. Durch den Einsatz von Chaos Engineering wird die Resilienz der Systeme kontinuierlich getestet. Ergebnis sind tausende von Deployments pro Tag, eine drastisch reduzierte Time-to-Market und hohe Systemstabilität.&lt;br /&gt;
*Etsy: Der Online-Marktplatz setzt auf eine umfangreiche Suite automatisierter Tests, Feature Toggles für kontrollierte Rollouts und eine vollautomatische Deployment-Pipeline. Dies führte zu einer Reduktion der Deployment-Zeiten von Stunden auf Minuten und einer deutlichen Steigerung der Agilität und Code-Qualität.&lt;br /&gt;
*Google: Nutzt eine Monorepo-Struktur und das Build-Tool Bazel, um Millionen von Builds und Tests täglich zu bewältigen. Canary Releases sind Standard zur Risikominimierung.&lt;br /&gt;
*Amazon: Verwendet ebenfalls eine Microservice-Architektur und AWS-eigene CI/CD-Werkzeuge wie AWS CodePipeline, um kontinuierliche Deployments über seine globale Plattform zu ermöglichen.&lt;br /&gt;
&amp;lt;p&amp;gt;Die Erfolgsgeschichten von CI/CD-Pionieren wie Netflix und Etsy zeigen, dass eine ausgereifte CI/CD-Praxis kein Endzustand ist, sondern eine kontinuierliche Reise der Verbesserung, Anpassung und oft auch der Entwicklung eigener Werkzeuge oder der signifikanten Anpassung bestehender Werkzeuge an einzigartige, groß angelegte Bedürfnisse.&amp;amp;nbsp;&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Ausblick ===&lt;br /&gt;
&amp;lt;p&amp;gt;Die Entwicklung im Bereich CI/CD bleibt dynamisch. Zukünftige Trends umfassen den verstärkten Einsatz von Künstlicher Intelligenz (KI) zur Optimierung von Pipelines (z.B. prädiktive Analysen für Testauswahl, automatische Fehlerdiagnose), die weitere Integration von Sicherheit als selbstverständlicher Bestandteil (DevSecOps wird zur Norm), der Aufstieg von Serverless CI/CD-Lösungen, die bedarfsgerecht skalieren und Kosten optimieren, sowie die Verbreitung von GitOps, bei dem Git als &amp;quot;Single Source of Truth&amp;quot; für die deklarative Beschreibung von Infrastruktur und Anwendungen dient und Änderungen automatisch synchronisiert werden. Die kontinuierliche Verbesserung und Anpassung von CI/CD-Praktiken wird für Unternehmen, die im Wettbewerb der Web-Anwendungsentwicklung bestehen wollen, von entscheidender Bedeutung bleiben.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;/div&gt;</summary>
		<author><name>Völkl Anna</name></author>
	</entry>
	<entry>
		<id>https://mediawiki.fernfh.ac.at/mediawiki/index.php?title=DevOps_and_Continuous_Integration_Continuous_Deployment&amp;diff=6580</id>
		<title>DevOps and Continuous Integration Continuous Deployment</title>
		<link rel="alternate" type="text/html" href="https://mediawiki.fernfh.ac.at/mediawiki/index.php?title=DevOps_and_Continuous_Integration_Continuous_Deployment&amp;diff=6580"/>
		<updated>2025-05-17T12:37:50Z</updated>

		<summary type="html">&lt;p&gt;Völkl Anna: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= DevOps und Continuous Integration/Continuous Deployment (CI/CD) für Web-Anwendungen =&lt;br /&gt;
== Einleitung: Die Rolle von DevOps und CI/CD in der modernen Web-Anwendungsentwicklung ==&lt;br /&gt;
&amp;lt;p&amp;gt;Die Entwicklung moderner Web-Anwendungen ist durch eine stetig steigende Komplexität und Dynamik gekennzeichnet. Unternehmen stehen vor der Herausforderung, qualitativ hochwertige Software immer schneller auf den Markt zu bringen, um wettbewerbsfähig zu bleiben und auf sich rasch ändernde Nutzeranforderungen reagieren zu können. In diesem Kontext haben sich DevOps und Continuous Integration/Continuous Deployment (CI/CD) als wichtige Methoden und Praktiken etabliert. Gitlab definiert DevOps folgendermaßen: “DevOps kombiniert Entwicklung (Dev; Development) und Betrieb (Ops; Operations), um die Effizienz, Geschwindigkeit und Sicherheit der Softwareentwicklung und -bereitstellung im Vergleich zu herkömmlichen Prozessen zu erhöhen. Ein flexiblerer Lebenszyklus der Softwareentwicklung führt zu einem Wettbewerbsvorteil für Unternehmen und ihre Kund(inn)en.” https://about.gitlab.com/de-de/topics/devops/#dev-ops-defined  &amp;lt;ref&amp;gt;https://about.gitlab.com/de-de/devops/&amp;lt;/ref&amp;gt; &amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;CI/CD sind die technischen Automatisierungsmechanismen. Die Kernherausforderung in der modernen Web-Entwicklung, nämlich die Balance zwischen Liefergeschwindigkeit, Qualität und Stabilität zu finden, wird durch DevOps und CI/CD direkt adressiert, indem Arbeitsabläufe optimiert und Schlüsselprozesse automatisiert werden.&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;Die Einführung von DevOps und CI/CD ist dabei nicht nur eine technische Aufrüstung, sondern eine strategische Notwendigkeit für Unternehmen, die Web-Anwendungen entwickeln. Die Marktanforderungen an Web-Anwendungen sind durch schnelle Feature-Entwicklungen und unmittelbares Nutzerfeedback geprägt. Traditionelle, isolierte Entwicklungsansätze stoßen hier schnell an ihre Grenzen und führen zu langsamen Release-Zyklen und potenziellen Qualitätsproblemen.&amp;amp;nbsp;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;Grundlagen: DevOps, Continuous Integration, Continuous Delivery und Continuous Deployment&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;Um die Rolle von CI/CD im Kontext von Web-Anwendungen vollständig zu erfassen, ist ein Verständnis der zugrundeliegenden Konzepte von DevOps sowie der spezifischen Praktiken von Continuous Integration, Continuous Delivery und Continuous Deployment unerlässlich.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Kernkonzepte und Ziele von DevOps und CI/CD ===&lt;br /&gt;
&amp;lt;p&amp;gt;DevOps wird als eine Kombination aus Praktiken und Werkzeugen definiert, die darauf abzielen, Anwendungen und Dienste mit hohem Automatisierungsgrad und Geschwindigkeit auszuliefern. Ein zentrales Element ist die Kombination von Entwicklungs- (Dev) und Betriebs- (Ops) Praktiken. Weitere wichtige Ziele von DevOps sind eine erhöhte Geschwindigkeit bei der Softwareauslieferung, schnelle Bereitstellung neuer Features, verbesserte Zuverlässigkeit und Skalierbarkeit der Systeme, eine optimierte Zusammenarbeit zwischen den Teams und die Integration von Sicherheitsmechanismen.&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;CI/CD steht für Continuous Integration und Continuous Delivery/Deployment und bezeichnet eine Reihe von Praktiken, die den Softwareentwicklungszyklus effizienter machen und beschleunigen sollen. Ziel ist es, Fehler zu vermeiden, einen kontinuierlichen Zyklus von Software-Updates aufrechtzuerhalten, die Komplexität zu verringern und die Effizienz zu steigern. CI/CD gilt als ein wesentlicher Bestandteil der DevOps-Methodik.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Abgrenzung und Zusammenspiel von Continuous Integration, Continuous Delivery und Continuous Deployment  ===&lt;br /&gt;
&amp;lt;p&amp;gt;Innerhalb des CI/CD-Spektrums gibt es wichtige Unterscheidungen zwischen Continuous Integration (CI), Continuous Delivery (CDel) und Continuous Deployment (CDep).&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;Da die Abkürzung CD sowohl für Continuous Integration, als auch für Continuous Delivery steht, werden manchmal auch die Begriffe CDel und CDep verwendet um eine bessere Unterscheidung zu ermöglichen.&amp;lt;/p&amp;gt;&lt;br /&gt;
*Continuous Integration (CI): Der Fokus von CI liegt auf dem häufigen Zusammenführen von Code-Änderungen verschiedener Entwickler in ein gemeinsames, zentrales Repository. Jede Integration löst automatisierte Build-Prozesse und Tests (insbesondere Unit- und Integrationstests) aus. Das Hauptziel ist schnelles Feedback an die Entwickler und die frühzeitige Erkennung von Integrationsproblemen und Fehlern.&lt;br /&gt;
*Continuous Delivery (CDel): Continuous Delivery erweitert CI, indem der Prozess der Softwarefreigabe bis hin zu verschiedenen Umgebungen (z.B. Staging- oder Testumgebungen) nach erfolgreichen Build- und Testphasen automatisiert wird. Die Software befindet sich dabei stets in einem auslieferbaren Zustand. Die endgültige Bereitstellung in der Produktionsumgebung erfordert jedoch typischerweise eine manuelle Genehmigung oder einen manuellen Anstoß. Ziel ist es, eine Codebasis zu haben, die jederzeit mit minimalem Aufwand in Produktion gebracht werden kann.&lt;br /&gt;
*Continuous Deployment (CDep): Continuous Deployment geht noch einen Schritt weiter als Continuous Delivery. Hierbei wird jede Code-Änderung, die alle automatisierten Tests erfolgreich durchlaufen hat, vollautomatisch und ohne manuellen Eingriff in die Produktionsumgebung ausgerollt. Das Ziel ist die automatische Freigabe von Updates direkt in die Produktivumgebung.&lt;br /&gt;
&amp;lt;p&amp;gt;2.3. Vorteile für die Web-Anwendungsentwicklung und den Geschäftswert&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;Die Implementierung von DevOps und CI/CD-Praktiken bietet eine Vielzahl von Vorteilen, sowohl auf technischer Ebene als auch im Hinblick auf den generierten Geschäftswert.&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;Zu den technischen Vorteilen zählen vor allem weniger Fehler im Produktivbetrieb, reduzierte Testkosten durch automatisierbare Tests und frühzeitige Fehlererkennung, schnellere Feedbackzyklen für Entwickler und eine insgesamt verbesserte Codequalität. Die Automatisierung von wiederkehrenden Aufgaben führt zu konsistenteren Ergebnissen und entlastet die Entwicklungsteams.&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;Diese technischen Verbesserungen schlagen sich direkt in signifikanten Geschäftsvorteilen nieder. Dazu gehören schnellere Deployments neuer Features und Updates, eine höhere Produktqualität, eine rasche Behebung von Problemen, gesteigerte Agilität des Unternehmens, die Förderung von Innovation durch Automatisierung, eine kontinuierliche Wertlieferung an den Kunden, reduzierte Produktionskosten, verbesserte Sicherheit und eine höhere Kundenzufriedenheit. Die verbesserte Zusammenarbeit zwischen den Teams ist ebenfalls ein wichtiger Faktor.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Die CI/CD-Pipeline für Web-Anwendungen: Aufbau und Visualisierung ==&lt;br /&gt;
&amp;lt;p&amp;gt;Eine CI/CD-Pipeline ist eine Serie automatisierter Prozesse, die den Weg von der Code-Änderung bis zur Bereitstellung in der Produktion abbildet und manuelle Übergaben eliminiert. Sie ist das Kernstück der CI/CD-Implementierung.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Typische Phasen einer Pipeline: Build, Test, Deploy ===&lt;br /&gt;
&amp;lt;p&amp;gt;Eine typische CI/CD-Pipeline für Web-Anwendungen durchläuft mehrere Phasen, die aufeinander aufbauen:&amp;lt;/p&amp;gt;&lt;br /&gt;
*Build Stage (Erstellungsphase): Zu Beginn der Build-Phase wird der aktuelle Code aus einem Repository abgerufen. Dann erfolgt die Auflösung der Abhängigkeiten (z.B. mittels npm install für Node.js-Anwendungen, composer für PHP oder Maven für Java-Anwendungen), danach das Kompilieren (falls erforderlich) und das Verpacken der Anwendung in ausführbare Artefakte. Für Web-Anwendungen sind dies oft Docker-Images oder traditionelle Pakete wie JAR/WAR-Dateien. Auf auslieferbare .zip/.tar Dateien die auf einem Server entpackt werden, können Build-Artefakte sein.&lt;br /&gt;
&amp;lt;p&amp;gt;Ein wichtiges Prinzip ist &amp;quot;Build only once&amp;quot;: Das einmal erstellte Artefakt wird durch alle weiteren Phasen und Umgebungen bewegt.&amp;lt;/p&amp;gt;&lt;br /&gt;
*Test Stage (Testphase): Hier werden verschiedene automatisierte Tests ausgeführt, um die Qualität der Software sicherzustellen. Dazu gehören Unit-Tests, Integrationstests, funktionale Tests, End-to-End (E2E)-Tests, API-Tests, UI-Tests, Performancetests und Sicherheitstests. Schlagen Tests fehl, wird die Pipeline üblicherweise gestoppt und die Entwickler automatisch informiert.&lt;br /&gt;
*Deploy Stage (Bereitstellungsphase): In dieser Phase werden die erfolgreich gebauten und getesteten Artefakte in verschiedenen Umgebungen bereitgestellt, beginnend mit Staging- oder Testumgebungen bis hin zur Produktionsumgebung. Die Bereitstellung kann manuell angestoßen werden (Continuous Delivery) oder vollautomatisch erfolgen (Continuous Deployment). Hier können auch spezifische Deployment-Strategien wie Blue/Green oder Canary Releases zum Einsatz kommen.&lt;br /&gt;
&amp;lt;p&amp;gt;Nachgelagerte Phasen&amp;lt;/p&amp;gt;&lt;br /&gt;
*Cleanup: Dies umfasst das löschen, nicht mehr benötigter Dateien oder Verzeichnisse, die während der Deploy-Phase erstellt wurden sowie auch das herunterfahren oder löschen von (alten) Server-Nodes. Auch Routineaufgaben wie ein Cache-Warming, Indizierung oder Neustart benötigter Anwendungen ist möglich.&lt;br /&gt;
*Monitor/Verify: Nach dem Deployment ist die Überwachung der Anwendung in der Produktionsumgebung entscheidend. Dies umfasst das Tracking von Performance-Metriken (Antwortzeiten, Fehlerraten), Systemstabilität (CPU-, Speicher-, Netzwerkauslastung) und das Sammeln von Log-Daten zur Fehleranalyse und für das Nutzerverhalten. Diese Phase ist oft Teil von Continuous Delivery oder ein direkt anschließender Prozess.&lt;br /&gt;
&amp;lt;p&amp;gt;Die Sequenz von Build -&amp;amp;gt; Test -&amp;amp;gt; Deploy stellt sicher, dass jeder Schritt die Ausgabe des vorherigen Steps validiert. Die Automatisierung innerhalb jeder Phase gewährleistet Konsistenz und Geschwindigkeit. Das &amp;quot;Fail Fast&amp;quot;-Prinzip (Anhalten bei Fehlern) verhindert, dass fehlerhafter Code in nachfolgende Phasen gelangt, was Zeit und Ressourcen spart. Das &amp;quot;Build Once&amp;quot;-Prinzip stellt sicher, dass das, was getestet wird, auch das ist, was bereitgestellt wird, wodurch umgebungsspezifische Fehler reduziert werden.&amp;amp;nbsp;&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Werkzeuge und Technologien im CI/CD-Ökosystem ==&lt;br /&gt;
&amp;lt;p&amp;gt;Das Ökosystem der CI/CD-Werkzeuge ist vielfältig und bietet Lösungen für unterschiedlichste Anforderungen und Unternehmensgrößen. Eine strategische Auswahl ist entscheidend für den Erfolg.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Überblick gängiger Tools ===&lt;br /&gt;
&amp;lt;p&amp;gt;Eine Vielzahl von Werkzeugen unterstützt die Implementierung von CI/CD-Pipelines für Web-Anwendungen. Zu den bekanntesten gehören:&amp;lt;/p&amp;gt;&lt;br /&gt;
*GitLab CI/CD: Tief in die GitLab-Plattform integriert, werden Pipelines über eine .gitlab-ci.yml-Datei im Repository definiert. GitLab CI bietet leistungsstarke Funktionen für die Web-App-Entwicklung, einschließlich integrierter Container-Registry und Review-Apps. Runner können selbst gehostet oder von GitLab bereitgestellt werden.&lt;br /&gt;
*Bitbucket Pipelines: Integriert in Bitbucket Cloud, nutzt eine bitbucket-pipelines.yml-Datei. Bietet gute Docker-Unterstützung und eignet sich besonders für Teams, die bereits die Atlassian-Produktpalette (Jira, Confluence) nutzen. Die Build-Umgebungen werden als Docker-Container ausgeführt.&lt;br /&gt;
*GitHub Actions: Direkt in GitHub integriert, werden Workflows über YAML-Dateien im .github/workflows-Verzeichnis definiert. Ein großer Marktplatz an wiederverwendbaren &amp;quot;Actions&amp;quot; ermöglicht flexible Anpassungen für diverse Web-Anwendungs-Szenarien. GitHub stellt gehostete Runner für Linux, Windows und macOS bereit, eigene Runner sind ebenfalls möglich.&lt;br /&gt;
*Jenkins: Ein sehr etabliertes, quelloffenes Automatisierungswerkzeug. Jenkins ist extrem erweiterbar durch ein riesiges Plugin-Ökosystem. Pipelines werden als Jenkinsfile (deklarativ oder skriptbasiert) definiert. Es kann komplex in der Einrichtung und Wartung sein, bietet aber enorme Flexibilität und Mächtigkeit.6 Jenkins benötigt in der Regel eine eigene Server-Infrastruktur für den Master und die Agents (Runner).&lt;br /&gt;
&amp;lt;p&amp;gt;Neben diesen gibt es weitere relevante Alternativen:&amp;lt;/p&amp;gt;&lt;br /&gt;
*CircleCI: Eine cloud-basierte CI/CD-Plattform, die Pipelines über eine config.yml-Datei konfiguriert. Bietet gute Docker-Unterstützung und eignet sich für Projekte unterschiedlicher Größe.&lt;br /&gt;
*Azure Pipelines (Teil von Azure DevOps): Bietet CI/CD-Dienste für jede Sprache und Plattform. Pipelines können über YAML oder einen klassischen grafischen Editor definiert werden. Starke Integration mit Azure Cloud-Diensten. Microsoft-gehostete und selbstgehostete Agents sind verfügbar.&lt;br /&gt;
*AWS CodePipeline: Ein vollständig verwalteter Continuous-Delivery-Dienst von Amazon Web Services. Er integriert sich nahtlos mit anderen AWS-Diensten wie AWS CodeCommit (Source), AWS CodeBuild (Build), AWS CodeDeploy (Deploy) sowie S3 und ECS für die Artefaktspeicherung und Ausführung von Web-Anwendungen.&lt;br /&gt;
*Travis CI: War historisch besonders populär für Open-Source-Projekte und ist bekannt für seine einfache Konfiguration mittels einer .travis.yml-Datei.&lt;br /&gt;
&lt;br /&gt;
=== Kriterien zur Auswahl geeigneter Werkzeuge ===&lt;br /&gt;
&amp;lt;p&amp;gt;Die Auswahl des passenden CI/CD-Werkzeugs ist eine strategische Entscheidung, die nicht nur die technische Ausführung, sondern auch die Produktivität des Teams, die Betriebskosten und die langfristige Wartbarkeit beeinflusst. Es geht nicht nur um einzelne Funktionen, sondern um die Passgenauigkeit des Werkzeugs zur übergeordneten IT-Strategie und den Fähigkeiten der Organisation. Folgende Kriterien sollten bei der Auswahl berücksichtigt werden:&amp;lt;/p&amp;gt;&lt;br /&gt;
*Integration mit dem Versionskontrollsystem (VCS): Eine nahtlose Integration mit dem genutzten VCS (z.B. Git, gehostet auf GitHub, GitLab, Bitbucket) ist fundamental. Werkzeuge, die tief in das VCS integriert sind, wie GitLab CI mit GitLab oder GitHub Actions mit GitHub, bieten oft eine reibungslosere Erfahrung und erfordern weniger Konfigurationsaufwand.31&lt;br /&gt;
*Skalierbarkeit und Performance: Das Werkzeug muss in der Lage sein, mit der Anzahl der Projekte, der Häufigkeit der Builds und der Komplexität der Pipelines zu skalieren.&lt;br /&gt;
*Unterstützung für Programmiersprachen und Frameworks: Es muss die spezifischen Technologien der Web-Anwendung unterstützen.&lt;br /&gt;
*Benutzerfreundlichkeit und Lernkurve: Die Komplexität des Werkzeugs und die Einarbeitungszeit für das Team sind wichtige Faktoren. Eine steile Lernkurve kann Produktivitätsgewinne zunichtemachen, wenn das Team nicht entsprechend geschult ist.&lt;br /&gt;
*Kosten: Die Kostenmodelle variieren stark (Open Source, Freemium, kommerzielle Lizenzen, nutzungsbasiert). Cloud-gehostete Lösungen haben andere Kostenstrukturen (einfachere Einrichtung, potenziell laufende Kosten) als selbstgehostete (mehr Kontrolle, mehr Wartungsaufwand). Der Total Cost of Ownership (TCO) sollte betrachtet werden.&lt;br /&gt;
*Community-Support und Plugin-Ökosystem: Besonders für Open-Source-Werkzeuge wie Jenkins ist eine aktive Community und ein breites Angebot an Plugins wichtig für Flexibilität und Problemlösung.&lt;br /&gt;
*Sicherheitsfunktionen und Compliance: Das Werkzeug sollte robuste Sicherheitsmechanismen für den Schutz von Code, Artefakten und Secrets bieten und die Einhaltung von Compliance-Richtlinien unterstützen.&lt;br /&gt;
*Vendor Lock-in: Die Abhängigkeit von einem bestimmten Anbieter und die Möglichkeit, bei Bedarf zu einem anderen Werkzeug zu wechseln, sollten bedacht werden.1&lt;br /&gt;
*Hosting-Optionen: Cloud-basiert, selbst-gehostet (On-Premise oder in eigener Cloud-Infrastruktur) oder Hybrid-Modelle.&lt;br /&gt;
&lt;br /&gt;
== Grundlegende Strategien und typische Schritte in CI/CD-Pipelines ==&lt;br /&gt;
&amp;lt;p&amp;gt;Effektive CI/CD-Pipelines basieren auf durchdachten Strategien für Branching, Testing und Deployment, die eng miteinander verknüpft sind.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Branching-Strategien (z.B. GitFlow, Trunk-Based Development) und deren Einfluss auf CI/CD ===&lt;br /&gt;
&amp;lt;p&amp;gt;Die Wahl der Branching-Strategie im Versionskontrollsystem hat grundlegende Auswirkungen auf den Aufbau und die Effizienz der CI/CD-Pipeline und spiegelt die Philosophie einer Organisation hinsichtlich Release-Management und Risikotoleranz wider.&amp;lt;/p&amp;gt;&lt;br /&gt;
*GitFlow: Diese Strategie verwendet mehrere langlebige Branches wie main (oder master), develop, sowie temporäre feature, release und hotfix Branches. GitFlow bietet eine klare Struktur, insbesondere für Projekte mit geplanten Release-Zyklen. Allerdings kann es durch die längere Lebensdauer von Feature-Branches zu größeren und selteneren Integrationen kommen, was dem Prinzip der kontinuierlichen Integration entgegenstehen kann. Die Pipeline-Logik für GitFlow kann komplexer sein, da sie verschiedene Branch-Typen und deren spezifische Workflows (z.B. Merge-Strategien, Testumfänge) berücksichtigen muss.&lt;br /&gt;
*Trunk-Based Development (TBD): Bei TBD arbeiten Entwickler mit sehr kurzlebigen Feature-Branches, die häufig (mehrmals täglich) direkt in einen zentralen Haupt-Branch (den &amp;quot;Trunk&amp;quot;, oft main oder master) integriert werden. Dieser Ansatz steht in engem Einklang mit den Prinzipien von Continuous Integration, da er den Feedbackzyklus maximiert. Um unfertige Features im Trunk zu verwalten, werden häufig Feature Flags (oder Feature Toggles) eingesetzt, die es erlauben, Code in die Produktion zu deployen, ohne ihn für alle Nutzer freizuschalten. TBD wird oft als Voraussetzung für eine echte kontinuierliche Integration angesehen.&lt;br /&gt;
&amp;lt;p&amp;gt;Der Einfluss auf CI/CD ist signifikant: TBD führt im Allgemeinen zu einfacheren und schnelleren CI/CD-Pipelines, da kleine, häufige Integrationen weniger Konfliktpotenzial bergen und schneller verarbeitet werden können. GitFlows langlebige Branches können die Integration und das Feedback verzögern, was potenziell dem CI-Prinzip &amp;quot;früh und oft committen&amp;quot; widerspricht. TBD erzwingt eine häufige Integration in die Hauptlinie, was die Vorteile von CI maximiert, aber auch die Notwendigkeit schneller, zuverlässiger automatisierter Tests erhöht, um den Trunk stets &amp;quot;grün&amp;quot; (d.h. auslieferungsbereit) zu halten. Feature Flags in TBD entkoppeln das Deployment vom Release und ermöglichen es, Code kontinuierlich zusammenzuführen und bereitzustellen, auch wenn Funktionen noch nicht für den Benutzer bereit sind. Dies ist eine anspruchsvolle Technik, die eine hohe Deployment-Frequenz unterstützt.&amp;amp;nbsp;&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Automatisierte Teststrategien (Unit-, Integrations-, End-to-End-Tests) ===&lt;br /&gt;
&amp;lt;p&amp;gt;Eine umfassende, automatisierte Teststrategie ist das Rückgrat jeder zuverlässigen CI/CD-Pipeline. Sie stellt sicher, dass Fehler frühzeitig erkannt werden und die Qualität der Web-Anwendung kontinuierlich gewährleistet wird. Ein mehrschichtiger Ansatz, oft visualisiert als Testpyramide oder Testmatrix, ist hierbei gängige Praxis.&amp;lt;/p&amp;gt;&lt;br /&gt;
*Unit-Tests: Diese Tests validieren die kleinsten isolierbaren Code-Einheiten (z.B. Funktionen, Methoden, Klassen). Sie werden sehr häufig ausgeführt (idealweise bei jedem Commit), sind schnell und geben unmittelbares Feedback an die Entwickler.&amp;amp;nbsp;&lt;br /&gt;
*Integrationstests: Sie überprüfen das korrekte Zusammenspiel verschiedener Komponenten, Module oder Dienste einer Anwendung. Sie sind komplexer als Unit-Tests und decken Fehler in den Schnittstellen und Interaktionen auf.&lt;br /&gt;
*End-to-End (E2E)-Tests: Diese Tests simulieren vollständige Benutzer-Szenarien und validieren den gesamten Anwendungsfluss aus der Perspektive des Endnutzers. Sie sind am aufwendigsten zu erstellen und zu warten und haben die längste Ausführungszeit. Werkzeuge wie Selenium oder Cypress werden hier häufig eingesetzt.&lt;br /&gt;
*API-Tests: Fokussieren sich auf die direkte Überprüfung der Programmierschnittstellen (APIs) der Web-Anwendung, unabhängig von der Benutzeroberfläche.&lt;br /&gt;
*UI-Tests: Testen die grafische Benutzeroberfläche und deren Interaktionselemente. Sie sind oft Teil der E2E-Tests.&lt;br /&gt;
*Performancetests: Bewerten die Antwortzeiten, Stabilität und Skalierbarkeit der Web-Anwendung unter Last.7 Werkzeuge wie JMeter oder k6 sind hier verbreitet.&lt;br /&gt;
*Sicherheitstests: Umfassen statische (SAST) und dynamische (DAST) Anwendungssicherheitstests sowie Schwachstellen-Scans, um Sicherheitslücken frühzeitig zu identifizieren.&lt;br /&gt;
&amp;lt;p&amp;gt;Eine entscheidende Herausforderung ist die Balance zwischen Testabdeckung und der Geschwindigkeit der Pipeline. Zu viele langsame Tests können den Feedbackzyklus verlangsamen und die Akzeptanz von CI/CD behindern.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Deployment-Strategien (z.B. Blue/Green, Canary, Rolling Deployments) ===&lt;br /&gt;
&amp;lt;p&amp;gt;Deployment-Strategien definieren, wie neue Versionen einer Web-Anwendung in die Produktionsumgebung ausgerollt werden. Die Wahl der Strategie beeinflusst das Risiko, die Ausfallzeit und das Nutzererlebnis während des Updates. CI/CD-Werkzeuge spielen eine Schlüsselrolle bei der Automatisierung dieser Strategien.58&amp;lt;/p&amp;gt;&lt;br /&gt;
*Blue/Green Deployment: Hierbei werden zwei identische Produktionsumgebungen unterhalten: &amp;quot;Blue&amp;quot; (die aktuelle Live-Version) und &amp;quot;Green&amp;quot; (die neue Version). Die neue Version wird auf der Green-Umgebung bereitgestellt und getestet. Ist alles erfolgreich, wird der Traffic vom Load Balancer von Blue auf Green umgeschaltet. Vorteile sind minimale bis keine Ausfallzeit und ein sehr einfacher Rollback (Umschalten zurück auf Blue). Nachteilig sind die höheren Infrastrukturkosten für die doppelte Umgebung.&lt;br /&gt;
*Canary Release (Canary Deployment): Die neue Version wird zunächst nur einem kleinen Prozentsatz der Nutzer oder Server (&amp;quot;Canaries&amp;quot;) zur Verfügung gestellt. Das Verhalten und die Performance werden engmaschig überwacht. Bei positivem Ergebnis wird der Anteil der Nutzer, die die neue Version erhalten, schrittweise erhöht, bis alle Nutzer umgestellt sind. Diese Methode ermöglicht frühzeitiges Feedback und begrenzt den &amp;quot;Blast Radius&amp;quot; potenzieller Fehler.&lt;br /&gt;
*Rolling Deployment: Updates werden schrittweise auf den Instanzen der Produktionsumgebung ausgerollt. Eine Teilmenge der Server wird aktualisiert, überwacht, und dann folgt die nächste Teilmenge. Während des Rollouts können unterschiedliche Versionen der Anwendung gleichzeitig aktiv sein, was zu Kompatibilitätsüberlegungen führen kann.&lt;br /&gt;
*A/B Testing: Diese Strategie wird oft für das Testen neuer Features oder Designs verwendet, indem verschiedene Versionen (A und B) parallel an unterschiedliche Nutzersegmente ausgeliefert werden, um deren Performance anhand definierter Metriken zu vergleichen. Feature Flags sind hier ein wichtiges Hilfsmittel.&lt;br /&gt;
*Weitere Strategien:&lt;br /&gt;
*Big Bang Deployment: Alle Änderungen werden auf einmal für alle Nutzer ausgerollt. Dies ist die riskanteste Methode mit potenziell hohen Ausfallzeiten.&lt;br /&gt;
*Recreate Deployment: Die alte Version wird gestoppt und die neue Version wird komplett neu bereitgestellt. Dies führt zu Ausfallzeiten.&lt;br /&gt;
&amp;lt;p&amp;gt;Deployment-Strategien beeinflussen direkt Risiko, Ausfallzeiten und Nutzererfahrung.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Weiterführende Themen ==&lt;br /&gt;
&amp;lt;p&amp;gt;Über die Grundlagen hinaus gewinnen im Kontext von CI/CD für Web-Anwendungen fortgeschrittene Konzepte an Bedeutung: Dazu zählen Infrastructure as Code, DevSecOps, Monitoring und Observability sowie die Messung des Erfolgs mittels Metriken.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Infrastructure as Code (IaC) und dessen Integration in CI/CD ===&lt;br /&gt;
&amp;lt;p&amp;gt;Infrastructure as Code (IaC) bezeichnet die Praxis, IT-Infrastruktur (Server, Netzwerke, Speicher, Datenbanken etc.) durch Code – typischerweise in Form von Konfigurationsdateien – zu definieren, zu provisionieren und zu verwalten, anstatt manuelle Prozesse zu nutzen. Wichtige Prinzipien von IaC sind Idempotenz (mehrmalige Ausführung führt zum selben Ergebnis), Versionierung der Infrastrukturbeschreibungen und die Automatisierung der Infrastrukturänderungen.&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;Die Vorteile von IaC sind vielfältig: Es ermöglicht Konsistenz über verschiedene Umgebungen hinweg, Wiederholbarkeit von Setups, einfache Skalierbarkeit, schnellere Deployments, eine Reduktion menschlicher Fehler und die Versionierung der Infrastruktur analog zum Anwendungscode.&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;Gängige Werkzeuge für IaC sind:&amp;lt;/p&amp;gt;&lt;br /&gt;
*Terraform: Ein deklaratives, Cloud-agnostisches Werkzeug von HashiCorp, das primär für die Provisionierung von Infrastrukturressourcen verwendet wird.&lt;br /&gt;
*Ansible: Ein prozedurales Werkzeug, das oft für Konfigurationsmanagement und Anwendungsdeployment auf bereits provisionierter Infrastruktur eingesetzt wird.&lt;br /&gt;
*Weitere Werkzeuge umfassen AWS CloudFormation, Azure Resource Manager (ARM) Templates oder Pulumi.&lt;br /&gt;
&amp;lt;p&amp;gt;Die Integration von IaC in CI/CD-Pipelines bedeutet, dass Änderungen an der Infrastruktur denselben rigorosen Prozess aus Versionierung, automatisierten Tests und kontrolliertem Deployment durchlaufen wie der Anwendungscode Dies stellt sicher, dass Entwicklungs-, Staging- und Produktionsumgebungen konsistent sind, was eine häufige Fehlerquelle eliminiert.&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;IaC erweitert die &amp;quot;Alles als Code&amp;quot;-Philosophie auf den gesamten Technologie-Stack und verbessert die Zuverlässigkeit und Geschwindigkeit der Bereitstellung von Web-Anwendungen. Manuelle Infrastrukturprovisionierung ist langsam und fehleranfällig. IaC hingegen ermöglicht die Definition, Versionierung und automatische Provisionierung der Infrastruktur. In CI/CD integriert bedeutet dies, dass die Anwendung, ihre Abhängigkeiten und die benötigte Infrastruktur gemeinsam bereitgestellt und validiert werden können. Diese holistische Automatisierung ist entscheidend für komplexe Web-Anwendungen, insbesondere solche, die Microservices oder Cloud-Plattformen nutzen, da sie sicherstellt, dass die Anwendung von der Entwicklung bis zur Produktion in einer bekannten, konsistenten und reproduzierbaren Umgebung läuft. Dies reduziert drastisch &amp;quot;Works on my machine&amp;quot;-Probleme und beschleunigt den gesamten Lieferzyklus.&amp;amp;nbsp;&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== DevSecOps: Sicherheit als integraler Bestandteil der Pipeline ===&lt;br /&gt;
&amp;lt;p&amp;gt;DevSecOps ist ein Ansatz, der Sicherheitspraktiken von Beginn an und während des gesamten DevOps-Lebenszyklus integriert, anstatt Sicherheit als nachgelagerten Schritt zu betrachten. Dies wird oft als &amp;quot;Shift Left&amp;quot;-Prinzip bezeichnet, bei dem Sicherheitsüberlegungen so früh wie möglich in den Entwicklungsprozess einfließen. Sicherheit wird dabei zur gemeinsamen Verantwortung von Entwicklungs-, Betriebs- und Sicherheitsteams.&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;Die Bedeutung von DevSecOps liegt darin, dass die frühzeitige Adressierung von Sicherheitsaspekten Kosten und Risiken reduziert. Traditionelle Sicherheitsmodelle, bei denen Sicherheitstests erst am Ende des Entwicklungszyklus stattfinden, können in agilen Umgebungen mit häufigen Releases zu Engpässen werden.&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;Die Integration von Sicherheit in die CI/CD-Pipeline erfolgt durch verschiedene automatisierte Maßnahmen:&amp;lt;/p&amp;gt;&lt;br /&gt;
*Static Application Security Testing (SAST): Analyse des Quellcodes auf bekannte Schwachstellenmuster, bevor die Anwendung kompiliert oder deployed wird. Werkzeuge wie Bandit, Semgrep, SonarQube oder Checkmarx können in die Build-Phase integriert werden.&lt;br /&gt;
*Dynamic Application Security Testing (DAST): Testen der laufenden Anwendung auf Schwachstellen, oft in einer Staging-Umgebung, indem typische Angriffsmuster simuliert werden. OWASP ZAP oder Burp Suite sind hier gängige Werkzeuge.&lt;br /&gt;
*Software Composition Analysis (SCA) / Dependency Scanning: Überprüfung von Drittanbieter-Bibliotheken und Abhängigkeiten auf bekannte Sicherheitslücken.&lt;br /&gt;
*Container Image Scanning: Scannen von Docker-Images auf bekannte Schwachstellen in den Basissystemen und installierten Paketen.&lt;br /&gt;
*Secrets Management: Sichere Verwaltung und Injektion von sensiblen Daten wie API-Schlüsseln, Passwörtern und Zertifikaten in die Pipeline und Anwendungen, ohne sie im Code zu speichern.&lt;br /&gt;
*Infrastructure Security: Überprüfung von IaC-Skripten auf Sicherheitsfehlkonfigurationen.&lt;br /&gt;
&amp;lt;p&amp;gt;Durch die Automatisierung von Sicherheitsprüfungen innerhalb der CI/CD-Pipeline können Unternehmen die Entwicklungsgeschwindigkeit beibehalten, ohne die Sicherheit zu kompromittieren, was für das Vertrauen in Web-Anwendungen unerlässlich ist. Dadurch können Schwachstellen behoben werden, wenn es am kostengünstigsten und einfachsten ist – während der Entwicklung. Die Automatisierung dieser Prüfungen gewährleistet Konsistenz und Abdeckung und reduziert die Wahrscheinlichkeit menschlicher Fehler.&amp;amp;nbsp;&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Monitoring, Logging und Observability zur Sicherstellung von Qualität und Performance ===&lt;br /&gt;
&amp;lt;p&amp;gt;Nach dem Deployment einer Web-Anwendung ist die kontinuierliche Überwachung ihrer Qualität und Performance unerlässlich. Hierbei unterscheidet man zwischen Monitoring, Logging und dem umfassenderen Konzept der Observability.&amp;lt;/p&amp;gt;&lt;br /&gt;
*Monitoring (Überwachung): Bezieht sich auf das Sammeln, Verarbeiten, Analysieren und Darstellen von vordefinierten Metriken, um den Zustand eines Systems zu verstehen und bekannte Probleme oder Anomalien zu erkennen. Es ist oft reaktiv und löst Alarme aus, wenn bestimmte Schwellwerte überschritten werden.&lt;br /&gt;
*Logging (Protokollierung): Umfasst das Erfassen von detaillierten, zeitgestempelten Aufzeichnungen von Ereignissen, Fehlern und Systemaktivitäten.23 Logs sind unerlässlich für die Fehlerdiagnose und das Verständnis des Systemverhaltens im Detail.&lt;br /&gt;
*Observability (Beobachtbarkeit): Geht über das reine Monitoring hinaus und beschreibt die Fähigkeit, aus den externen Outputs eines Systems Rückschlüsse auf dessen internen Zustand zu ziehen und auch unbekannte Probleme (&amp;quot;unknown unknowns&amp;quot;) zu untersuchen. Observability basiert typischerweise auf den drei Säulen: Logs, Metriken und Traces (verteilte Ablaufverfolgung). Sie ermöglicht eine proaktive Untersuchung des Systemverhaltens.&lt;br /&gt;
&amp;lt;p&amp;gt;Die Bedeutung im CI/CD-Kontext liegt darin, dass diese Praktiken unmittelbares Feedback über den Erfolg und die Auswirkungen von Deployments liefern. Sie helfen, Probleme in der Produktionsumgebung schnell zu erkennen und zu diagnostizieren, und die gewonnenen Erkenntnisse fließen zurück in die Optimierung der Pipeline und der Anwendung selbst. Gängige Werkzeuge sind Datadog, Prometheus mit Grafana, Splunk oder der ELK Stack (Elasticsearch, Logstash, Kibana).&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;Die Entwicklung vom Monitoring zur Observability im CI/CD-Kontext spiegelt die zunehmende Komplexität moderner Web-Anwendungen wider (z.B. Microservices). Observability ist entscheidend für die Aufrechterhaltung von Zuverlässigkeit und Performance in dynamischen, häufig aktualisierten Systemen. Einfaches Monitoring bekannter Metriken reicht für komplexe verteilte Systeme, bei denen Fehlermodi nicht immer vorhersagbar sind, oft nicht aus. Observability ermöglicht es Teams durch die Kombination von Metriken, Logs und Traces zu verstehen, warum etwas passiert, nicht nur, dass etwas nicht stimmt. In einer CI/CD-Umgebung mit häufigen Deployments ist die Fähigkeit, die Auswirkungen neuer Änderungen schnell zu diagnostizieren und zu verstehen, entscheidend für die Einhaltung des MTTR (Mean Time to Recovery). Dieses tiefere Verständnis ermöglicht eine effektivere Fehlerbehebung und eine kontinuierliche Verbesserung sowohl der Anwendung als auch der CI/CD-Pipeline selbst.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Relevante Metriken und KPIs zur Erfolgsmessung (z.B. DORA Metriken) ===&lt;br /&gt;
&amp;lt;p&amp;gt;Um die Effektivität von CI/CD-Prozessen und deren Beitrag zum Geschäftserfolg zu bewerten, ist die Messung anhand relevanter Metriken und Key Performance Indicators (KPIs) unerlässlich.&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;Die DORA-Metriken, benannt nach der DevOps Research and Assessment Gruppe, gelten als Industriestandard zur Messung der Leistungsfähigkeit von Software-Delivery-Prozessen:&amp;lt;/p&amp;gt;&lt;br /&gt;
*Deployment Frequency (DF) / Bereitstellungshäufigkeit: Wie oft wird Code erfolgreich in die Produktion überführt? Misst die Geschwindigkeit und Agilität des Teams.&lt;br /&gt;
*Lead Time for Changes (LT) / Durchlaufzeit für Änderungen: Wie lange dauert es von der Code-Commit bis zum erfolgreichen Deployment in die Produktion? Indikator für die Effizienz des gesamten Entwicklungsprozesses.&lt;br /&gt;
*Change Failure Rate (CFR) / Fehlerrate von Änderungen: Welcher Prozentsatz der Deployments führt zu Fehlern in der Produktion, die ein Eingreifen erfordern (z.B. Rollback, Hotfix)? Misst die Stabilität und Qualität der Releases.&lt;br /&gt;
*Mean Time to Recovery (MTTR) / Mittlere Wiederherstellungszeit: Wie lange dauert es im Durchschnitt, einen Dienst nach einem Ausfall in der Produktion wiederherzustellen? Zeigt die Resilienz des Systems und die Effektivität der Incident-Response-Prozesse.&lt;br /&gt;
&amp;lt;p&amp;gt;Neben den DORA-Metriken sind weitere KPIs relevant:&amp;lt;/p&amp;gt;&lt;br /&gt;
*Build-Dauer: Zeit für die Durchführung der Build-Phase.&lt;br /&gt;
*Testabdeckung (Test Coverage): Prozentsatz des Codes, der durch automatisierte Tests abgedeckt ist.&lt;br /&gt;
*Defect Escape Rate: Anzahl der Fehler, die erst in der Produktion entdeckt werden.&lt;br /&gt;
*Anwendungs-Performance-Metriken: Antwortzeiten, Fehlerraten der Anwendung.&lt;br /&gt;
*Infrastrukturauslastung und -kosten.&lt;br /&gt;
&lt;br /&gt;
== Herausforderungen, Anti-Patterns und Best Practices ==&lt;br /&gt;
&amp;lt;p&amp;gt;Trotz der erheblichen Vorteile ist die Implementierung von CI/CD nicht ohne Hürden. Es gibt typische Herausforderungen und Anti-Patterns, denen jedoch mit etablierten Best Practices begegnet werden kann.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Herausforderungen und Anti-Patterns ===&lt;br /&gt;
&amp;lt;p&amp;gt;Zu den häufigsten Herausforderungen bei der CI/CD-Implementierung für Web-Anwendungen gehören Performance-Probleme in der Pipeline, mangelnde Teamkommunikation, Komplexität bei der Versionskontrolle und im Branching, fehlerhafte oder unzureichende automatisierte Tests, Sicherheitslücken, Skalierungsprobleme der Infrastruktur, schwierige Fehlersuche, häufige Build-Fehler, Probleme im Abhängigkeitsmanagement, Inkonsistenzen zwischen Umgebungen, überkomplizierte Pipelines, mangelnde Testabdeckung, Lücken im Monitoring und Reporting sowie ineffektive Rollback-Mechanismen. Ein signifikanter Faktor ist oft auch der Widerstand gegen Veränderungen in etablierten Prozessen und Kulturen.&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;Typische Anti-Patterns, die den Erfolg von CI/CD behindern, sind:&amp;lt;/p&amp;gt;&lt;br /&gt;
*Mangelnde Zusammenarbeit: Teams arbeiten weiterhin in Silos.&lt;br /&gt;
*Übermäßige Abhängigkeit von manuellen Prozessen: Wichtige Schritte in der Pipeline werden nicht automatisiert.&lt;br /&gt;
*Ignorieren von Sicherheit: Sicherheitsaspekte werden erst spät oder gar nicht berücksichtigt (Security as an Afterthought).&lt;br /&gt;
*Unzureichende Tests: Zu geringe Testabdeckung oder ineffektive Teststrategien.&lt;br /&gt;
*Widerstand gegen Veränderungen: Festhalten an alten Arbeitsweisen und Werkzeugen.&lt;br /&gt;
*Schlechtes Monitoring und Feedback: Fehlende Transparenz über den Zustand der Pipeline und der Deployments.&lt;br /&gt;
*Mehrfaches Bauen von Artefakten: Das gleiche Artefakt wird für verschiedene Stufen oder Umgebungen immer wieder neu gebaut, anstatt das einmal gebaute Artefakt weiterzureichen.&lt;br /&gt;
&lt;br /&gt;
=== Best Practices ===&lt;br /&gt;
&amp;lt;p&amp;gt;Um diesen Herausforderungen zu begegnen und Anti-Patterns zu vermeiden, haben sich folgende Best Practices etabliert 15:&amp;lt;/p&amp;gt;&lt;br /&gt;
*Früh und oft committen (Commit Early, Commit Often): Kleine, häufige Code-Änderungen erleichtern die Integration und Fehlersuche.&lt;br /&gt;
*Builds grün halten (Keep Builds Green): Fehlerhafte Builds sofort beheben, um die Pipeline nicht zu blockieren.&lt;br /&gt;
*Einmal bauen (Build Only Once): Das kompilierte Artefakt einmal erstellen und dieses durch alle Test- und Deployment-Stufen bewegen.&lt;br /&gt;
*Tests optimieren (Streamline Tests): Eine ausgewogene Teststrategie mit schnellen Feedbackzyklen entwickeln.&lt;br /&gt;
*Umgebungen sauber halten (Clean Environments): Testumgebungen regelmäßig zurücksetzen oder neu erstellen (IaC hilft hier).&lt;br /&gt;
*Pipeline sichern (Secure Your Pipeline): Sicherheitsmaßnahmen in jede Phase integrieren.&lt;br /&gt;
*Prozess einhalten (Stick to Your Process): Ausnahmen und manuelle Eingriffe minimieren.&lt;br /&gt;
*Pipeline überwachen und messen (Monitor and Measure Your Pipeline): Metriken nutzen, um Engpässe und Verbesserungspotenziale zu identifizieren.&lt;br /&gt;
*Teamarbeit fördern (Make it a Team Effort): CI/CD ist eine gemeinsame Verantwortung.&lt;br /&gt;
*Wiederverwendbare/Gemeinsame Pipelines nutzen (Use Shared Pipelines - DRY): Duplizierung von Pipeline-Logik vermeiden.15&lt;br /&gt;
*Progressive Delivery anwenden: Strategien wie Canary Releases oder Blue/Green Deployments nutzen, um Risiken zu minimieren.15&lt;br /&gt;
&lt;br /&gt;
== Fallbeispiele und Ausblick ==&lt;br /&gt;
&amp;lt;p&amp;gt;Die erfolgreiche Implementierung von CI/CD hat bei vielen führenden web-zentrierten Unternehmen zu signifikanten Verbesserungen geführt.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Fallbeispiele ===&lt;br /&gt;
&lt;br /&gt;
*Netflix: Das Unternehmen transformierte seine Architektur hin zu Microservices und entwickelte mit Spinnaker eine eigene Open-Source Continuous Delivery Plattform. Durch den Einsatz von Chaos Engineering wird die Resilienz der Systeme kontinuierlich getestet. Ergebnis sind tausende von Deployments pro Tag, eine drastisch reduzierte Time-to-Market und hohe Systemstabilität.&lt;br /&gt;
*Etsy: Der Online-Marktplatz setzt auf eine umfangreiche Suite automatisierter Tests, Feature Toggles für kontrollierte Rollouts und eine vollautomatische Deployment-Pipeline. Dies führte zu einer Reduktion der Deployment-Zeiten von Stunden auf Minuten und einer deutlichen Steigerung der Agilität und Code-Qualität.&lt;br /&gt;
*Google: Nutzt eine Monorepo-Struktur und das Build-Tool Bazel, um Millionen von Builds und Tests täglich zu bewältigen. Canary Releases sind Standard zur Risikominimierung.&lt;br /&gt;
*Amazon: Verwendet ebenfalls eine Microservice-Architektur und AWS-eigene CI/CD-Werkzeuge wie AWS CodePipeline, um kontinuierliche Deployments über seine globale Plattform zu ermöglichen.&lt;br /&gt;
&amp;lt;p&amp;gt;Die Erfolgsgeschichten von CI/CD-Pionieren wie Netflix und Etsy zeigen, dass eine ausgereifte CI/CD-Praxis kein Endzustand ist, sondern eine kontinuierliche Reise der Verbesserung, Anpassung und oft auch der Entwicklung eigener Werkzeuge oder der signifikanten Anpassung bestehender Werkzeuge an einzigartige, groß angelegte Bedürfnisse.&amp;amp;nbsp;&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Ausblick ===&lt;br /&gt;
&amp;lt;p&amp;gt;Die Entwicklung im Bereich CI/CD bleibt dynamisch. Zukünftige Trends umfassen den verstärkten Einsatz von Künstlicher Intelligenz (KI) zur Optimierung von Pipelines (z.B. prädiktive Analysen für Testauswahl, automatische Fehlerdiagnose), die weitere Integration von Sicherheit als selbstverständlicher Bestandteil (DevSecOps wird zur Norm), der Aufstieg von Serverless CI/CD-Lösungen, die bedarfsgerecht skalieren und Kosten optimieren, sowie die Verbreitung von GitOps, bei dem Git als &amp;quot;Single Source of Truth&amp;quot; für die deklarative Beschreibung von Infrastruktur und Anwendungen dient und Änderungen automatisch synchronisiert werden. Die kontinuierliche Verbesserung und Anpassung von CI/CD-Praktiken wird für Unternehmen, die im Wettbewerb der Web-Anwendungsentwicklung bestehen wollen, von entscheidender Bedeutung bleiben.&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;/div&gt;</summary>
		<author><name>Völkl Anna</name></author>
	</entry>
	<entry>
		<id>https://mediawiki.fernfh.ac.at/mediawiki/index.php?title=DevOps_and_Continuous_Integration_Continuous_Deployment&amp;diff=6579</id>
		<title>DevOps and Continuous Integration Continuous Deployment</title>
		<link rel="alternate" type="text/html" href="https://mediawiki.fernfh.ac.at/mediawiki/index.php?title=DevOps_and_Continuous_Integration_Continuous_Deployment&amp;diff=6579"/>
		<updated>2025-05-17T12:36:00Z</updated>

		<summary type="html">&lt;p&gt;Völkl Anna: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= DevOps und Continuous Integration/Continuous Deployment (CI/CD) für Web-Anwendungen =&lt;br /&gt;
== Einleitung: Die Rolle von DevOps und CI/CD in der modernen Web-Anwendungsentwicklung ==&lt;br /&gt;
&amp;lt;p&amp;gt;Die Entwicklung moderner Web-Anwendungen ist durch eine stetig steigende Komplexität und Dynamik gekennzeichnet. Unternehmen stehen vor der Herausforderung, qualitativ hochwertige Software immer schneller auf den Markt zu bringen, um wettbewerbsfähig zu bleiben und auf sich rasch ändernde Nutzeranforderungen reagieren zu können. In diesem Kontext haben sich DevOps und Continuous Integration/Continuous Deployment (CI/CD) als wichtige Methoden und Praktiken etabliert. Gitlab definiert DevOps folgendermaßen: “DevOps kombiniert Entwicklung (Dev; Development) und Betrieb (Ops; Operations), um die Effizienz, Geschwindigkeit und Sicherheit der Softwareentwicklung und -bereitstellung im Vergleich zu herkömmlichen Prozessen zu erhöhen. Ein flexiblerer Lebenszyklus der Softwareentwicklung führt zu einem Wettbewerbsvorteil für Unternehmen und ihre Kund(inn)en.”https://about.gitlab.com/de-de/topics/devops/#dev-ops-defined  &amp;lt;ref&amp;gt; Referenz&amp;lt;/ref&amp;gt; &amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;CI/CD sind die technischen Automatisierungsmechanismen. Die Kernherausforderung in der modernen Web-Entwicklung, nämlich die Balance zwischen Liefergeschwindigkeit, Qualität und Stabilität zu finden, wird durch DevOps und CI/CD direkt adressiert, indem Arbeitsabläufe optimiert und Schlüsselprozesse automatisiert werden.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Die Einführung von DevOps und CI/CD ist dabei nicht nur eine technische Aufrüstung, sondern eine strategische Notwendigkeit für Unternehmen, die Web-Anwendungen entwickeln. Die Marktanforderungen an Web-Anwendungen sind durch schnelle Feature-Entwicklungen und unmittelbares Nutzerfeedback geprägt. Traditionelle, isolierte Entwicklungsansätze stoßen hier schnell an ihre Grenzen und führen zu langsamen Release-Zyklen und potenziellen Qualitätsproblemen.&amp;amp;nbsp;&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Grundlagen: DevOps, Continuous Integration, Continuous Delivery und Continuous Deployment&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Um die Rolle von CI/CD im Kontext von Web-Anwendungen vollständig zu erfassen, ist ein Verständnis der zugrundeliegenden Konzepte von DevOps sowie der spezifischen Praktiken von Continuous Integration, Continuous Delivery und Continuous Deployment unerlässlich.&amp;lt;/p&amp;gt;&lt;br /&gt;
=== Kernkonzepte und Ziele von DevOps und CI/CD ===&lt;br /&gt;
&amp;lt;p&amp;gt;DevOps wird als eine Kombination aus Praktiken und Werkzeugen definiert, die darauf abzielen, Anwendungen und Dienste mit hohem Automatisierungsgrad und Geschwindigkeit auszuliefern. Ein zentrales Element ist die Kombination von Entwicklungs- (Dev) und Betriebs- (Ops) Praktiken. Weitere wichtige Ziele von DevOps sind eine erhöhte Geschwindigkeit bei der Softwareauslieferung, schnelle Bereitstellung neuer Features, verbesserte Zuverlässigkeit und Skalierbarkeit der Systeme, eine optimierte Zusammenarbeit zwischen den Teams und die Integration von Sicherheitsmechanismen.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;CI/CD steht für Continuous Integration und Continuous Delivery/Deployment und bezeichnet eine Reihe von Praktiken, die den Softwareentwicklungszyklus effizienter machen und beschleunigen sollen. Ziel ist es, Fehler zu vermeiden, einen kontinuierlichen Zyklus von Software-Updates aufrechtzuerhalten, die Komplexität zu verringern und die Effizienz zu steigern. CI/CD gilt als ein wesentlicher Bestandteil der DevOps-Methodik.&amp;lt;/p&amp;gt;&lt;br /&gt;
=== Abgrenzung und Zusammenspiel von Continuous Integration, Continuous Delivery und Continuous Deployment  ===&lt;br /&gt;
&amp;lt;p&amp;gt;Innerhalb des CI/CD-Spektrums gibt es wichtige Unterscheidungen zwischen Continuous Integration (CI), Continuous Delivery (CDel) und Continuous Deployment (CDep).&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Da die Abkürzung CD sowohl für Continuous Integration, als auch für Continuous Delivery steht, werden manchmal auch die Begriffe CDel und CDep verwendet um eine bessere Unterscheidung zu ermöglichen.&amp;lt;/p&amp;gt;&lt;br /&gt;
*Continuous Integration (CI): Der Fokus von CI liegt auf dem häufigen Zusammenführen von Code-Änderungen verschiedener Entwickler in ein gemeinsames, zentrales Repository. Jede Integration löst automatisierte Build-Prozesse und Tests (insbesondere Unit- und Integrationstests) aus. Das Hauptziel ist schnelles Feedback an die Entwickler und die frühzeitige Erkennung von Integrationsproblemen und Fehlern.&lt;br /&gt;
*Continuous Delivery (CDel): Continuous Delivery erweitert CI, indem der Prozess der Softwarefreigabe bis hin zu verschiedenen Umgebungen (z.B. Staging- oder Testumgebungen) nach erfolgreichen Build- und Testphasen automatisiert wird. Die Software befindet sich dabei stets in einem auslieferbaren Zustand. Die endgültige Bereitstellung in der Produktionsumgebung erfordert jedoch typischerweise eine manuelle Genehmigung oder einen manuellen Anstoß. Ziel ist es, eine Codebasis zu haben, die jederzeit mit minimalem Aufwand in Produktion gebracht werden kann.&lt;br /&gt;
*Continuous Deployment (CDep): Continuous Deployment geht noch einen Schritt weiter als Continuous Delivery. Hierbei wird jede Code-Änderung, die alle automatisierten Tests erfolgreich durchlaufen hat, vollautomatisch und ohne manuellen Eingriff in die Produktionsumgebung ausgerollt. Das Ziel ist die automatische Freigabe von Updates direkt in die Produktivumgebung.&lt;br /&gt;
&amp;lt;p&amp;gt;2.3. Vorteile für die Web-Anwendungsentwicklung und den Geschäftswert&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Die Implementierung von DevOps und CI/CD-Praktiken bietet eine Vielzahl von Vorteilen, sowohl auf technischer Ebene als auch im Hinblick auf den generierten Geschäftswert.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Zu den technischen Vorteilen zählen vor allem weniger Fehler im Produktivbetrieb, reduzierte Testkosten durch automatisierbare Tests und frühzeitige Fehlererkennung, schnellere Feedbackzyklen für Entwickler und eine insgesamt verbesserte Codequalität. Die Automatisierung von wiederkehrenden Aufgaben führt zu konsistenteren Ergebnissen und entlastet die Entwicklungsteams.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Diese technischen Verbesserungen schlagen sich direkt in signifikanten Geschäftsvorteilen nieder. Dazu gehören schnellere Deployments neuer Features und Updates, eine höhere Produktqualität, eine rasche Behebung von Problemen, gesteigerte Agilität des Unternehmens, die Förderung von Innovation durch Automatisierung, eine kontinuierliche Wertlieferung an den Kunden, reduzierte Produktionskosten, verbesserte Sicherheit und eine höhere Kundenzufriedenheit. Die verbesserte Zusammenarbeit zwischen den Teams ist ebenfalls ein wichtiger Faktor.&amp;lt;/p&amp;gt;&lt;br /&gt;
== Die CI/CD-Pipeline für Web-Anwendungen: Aufbau und Visualisierung ==&lt;br /&gt;
&amp;lt;p&amp;gt;Eine CI/CD-Pipeline ist eine Serie automatisierter Prozesse, die den Weg von der Code-Änderung bis zur Bereitstellung in der Produktion abbildet und manuelle Übergaben eliminiert. Sie ist das Kernstück der CI/CD-Implementierung.&amp;lt;/p&amp;gt;&lt;br /&gt;
=== Typische Phasen einer Pipeline: Build, Test, Deploy ===&lt;br /&gt;
&amp;lt;p&amp;gt;Eine typische CI/CD-Pipeline für Web-Anwendungen durchläuft mehrere Phasen, die aufeinander aufbauen:&amp;lt;/p&amp;gt;&lt;br /&gt;
*Build Stage (Erstellungsphase): Zu Beginn der Build-Phase wird der aktuelle Code aus einem Repository abgerufen. Dann erfolgt die Auflösung der Abhängigkeiten (z.B. mittels npm install für Node.js-Anwendungen, composer für PHP oder Maven für Java-Anwendungen), danach das Kompilieren (falls erforderlich) und das Verpacken der Anwendung in ausführbare Artefakte. Für Web-Anwendungen sind dies oft Docker-Images oder traditionelle Pakete wie JAR/WAR-Dateien. Auf auslieferbare .zip/.tar Dateien die auf einem Server entpackt werden, können Build-Artefakte sein.&lt;br /&gt;
&amp;lt;p&amp;gt;Ein wichtiges Prinzip ist &amp;quot;Build only once&amp;quot;: Das einmal erstellte Artefakt wird durch alle weiteren Phasen und Umgebungen bewegt.&amp;lt;/p&amp;gt;&lt;br /&gt;
*Test Stage (Testphase): Hier werden verschiedene automatisierte Tests ausgeführt, um die Qualität der Software sicherzustellen. Dazu gehören Unit-Tests, Integrationstests, funktionale Tests, End-to-End (E2E)-Tests, API-Tests, UI-Tests, Performancetests und Sicherheitstests. Schlagen Tests fehl, wird die Pipeline üblicherweise gestoppt und die Entwickler automatisch informiert.&lt;br /&gt;
*Deploy Stage (Bereitstellungsphase): In dieser Phase werden die erfolgreich gebauten und getesteten Artefakte in verschiedenen Umgebungen bereitgestellt, beginnend mit Staging- oder Testumgebungen bis hin zur Produktionsumgebung. Die Bereitstellung kann manuell angestoßen werden (Continuous Delivery) oder vollautomatisch erfolgen (Continuous Deployment). Hier können auch spezifische Deployment-Strategien wie Blue/Green oder Canary Releases zum Einsatz kommen.&lt;br /&gt;
&amp;lt;p&amp;gt;Nachgelagerte Phasen&amp;lt;/p&amp;gt;&lt;br /&gt;
*Cleanup: Dies umfasst das löschen, nicht mehr benötigter Dateien oder Verzeichnisse, die während der Deploy-Phase erstellt wurden sowie auch das herunterfahren oder löschen von (alten) Server-Nodes. Auch Routineaufgaben wie ein Cache-Warming, Indizierung oder Neustart benötigter Anwendungen ist möglich.&lt;br /&gt;
*Monitor/Verify: Nach dem Deployment ist die Überwachung der Anwendung in der Produktionsumgebung entscheidend. Dies umfasst das Tracking von Performance-Metriken (Antwortzeiten, Fehlerraten), Systemstabilität (CPU-, Speicher-, Netzwerkauslastung) und das Sammeln von Log-Daten zur Fehleranalyse und für das Nutzerverhalten. Diese Phase ist oft Teil von Continuous Delivery oder ein direkt anschließender Prozess.&lt;br /&gt;
&amp;lt;p&amp;gt;Die Sequenz von Build -&amp;amp;gt; Test -&amp;amp;gt; Deploy stellt sicher, dass jeder Schritt die Ausgabe des vorherigen Steps validiert. Die Automatisierung innerhalb jeder Phase gewährleistet Konsistenz und Geschwindigkeit. Das &amp;quot;Fail Fast&amp;quot;-Prinzip (Anhalten bei Fehlern) verhindert, dass fehlerhafter Code in nachfolgende Phasen gelangt, was Zeit und Ressourcen spart. Das &amp;quot;Build Once&amp;quot;-Prinzip stellt sicher, dass das, was getestet wird, auch das ist, was bereitgestellt wird, wodurch umgebungsspezifische Fehler reduziert werden.&amp;amp;nbsp;&amp;lt;/p&amp;gt;&lt;br /&gt;
== Werkzeuge und Technologien im CI/CD-Ökosystem ==&lt;br /&gt;
&amp;lt;p&amp;gt;Das Ökosystem der CI/CD-Werkzeuge ist vielfältig und bietet Lösungen für unterschiedlichste Anforderungen und Unternehmensgrößen. Eine strategische Auswahl ist entscheidend für den Erfolg.&amp;lt;/p&amp;gt;&lt;br /&gt;
=== Überblick gängiger Tools ===&lt;br /&gt;
&amp;lt;p&amp;gt;Eine Vielzahl von Werkzeugen unterstützt die Implementierung von CI/CD-Pipelines für Web-Anwendungen. Zu den bekanntesten gehören:&amp;lt;/p&amp;gt;&lt;br /&gt;
*GitLab CI/CD: Tief in die GitLab-Plattform integriert, werden Pipelines über eine .gitlab-ci.yml-Datei im Repository definiert. GitLab CI bietet leistungsstarke Funktionen für die Web-App-Entwicklung, einschließlich integrierter Container-Registry und Review-Apps. Runner können selbst gehostet oder von GitLab bereitgestellt werden.&lt;br /&gt;
*Bitbucket Pipelines: Integriert in Bitbucket Cloud, nutzt eine bitbucket-pipelines.yml-Datei. Bietet gute Docker-Unterstützung und eignet sich besonders für Teams, die bereits die Atlassian-Produktpalette (Jira, Confluence) nutzen. Die Build-Umgebungen werden als Docker-Container ausgeführt.&lt;br /&gt;
*GitHub Actions: Direkt in GitHub integriert, werden Workflows über YAML-Dateien im .github/workflows-Verzeichnis definiert. Ein großer Marktplatz an wiederverwendbaren &amp;quot;Actions&amp;quot; ermöglicht flexible Anpassungen für diverse Web-Anwendungs-Szenarien. GitHub stellt gehostete Runner für Linux, Windows und macOS bereit, eigene Runner sind ebenfalls möglich.&lt;br /&gt;
*Jenkins: Ein sehr etabliertes, quelloffenes Automatisierungswerkzeug. Jenkins ist extrem erweiterbar durch ein riesiges Plugin-Ökosystem. Pipelines werden als Jenkinsfile (deklarativ oder skriptbasiert) definiert. Es kann komplex in der Einrichtung und Wartung sein, bietet aber enorme Flexibilität und Mächtigkeit.6 Jenkins benötigt in der Regel eine eigene Server-Infrastruktur für den Master und die Agents (Runner).&lt;br /&gt;
&amp;lt;p&amp;gt;Neben diesen gibt es weitere relevante Alternativen:&amp;lt;/p&amp;gt;&lt;br /&gt;
*CircleCI: Eine cloud-basierte CI/CD-Plattform, die Pipelines über eine config.yml-Datei konfiguriert. Bietet gute Docker-Unterstützung und eignet sich für Projekte unterschiedlicher Größe.&lt;br /&gt;
*Azure Pipelines (Teil von Azure DevOps): Bietet CI/CD-Dienste für jede Sprache und Plattform. Pipelines können über YAML oder einen klassischen grafischen Editor definiert werden. Starke Integration mit Azure Cloud-Diensten. Microsoft-gehostete und selbstgehostete Agents sind verfügbar.&lt;br /&gt;
*AWS CodePipeline: Ein vollständig verwalteter Continuous-Delivery-Dienst von Amazon Web Services. Er integriert sich nahtlos mit anderen AWS-Diensten wie AWS CodeCommit (Source), AWS CodeBuild (Build), AWS CodeDeploy (Deploy) sowie S3 und ECS für die Artefaktspeicherung und Ausführung von Web-Anwendungen.&lt;br /&gt;
*Travis CI: War historisch besonders populär für Open-Source-Projekte und ist bekannt für seine einfache Konfiguration mittels einer .travis.yml-Datei.&lt;br /&gt;
&lt;br /&gt;
=== Kriterien zur Auswahl geeigneter Werkzeuge ===&lt;br /&gt;
&amp;lt;p&amp;gt;Die Auswahl des passenden CI/CD-Werkzeugs ist eine strategische Entscheidung, die nicht nur die technische Ausführung, sondern auch die Produktivität des Teams, die Betriebskosten und die langfristige Wartbarkeit beeinflusst. Es geht nicht nur um einzelne Funktionen, sondern um die Passgenauigkeit des Werkzeugs zur übergeordneten IT-Strategie und den Fähigkeiten der Organisation. Folgende Kriterien sollten bei der Auswahl berücksichtigt werden:&amp;lt;/p&amp;gt;&lt;br /&gt;
*Integration mit dem Versionskontrollsystem (VCS): Eine nahtlose Integration mit dem genutzten VCS (z.B. Git, gehostet auf GitHub, GitLab, Bitbucket) ist fundamental. Werkzeuge, die tief in das VCS integriert sind, wie GitLab CI mit GitLab oder GitHub Actions mit GitHub, bieten oft eine reibungslosere Erfahrung und erfordern weniger Konfigurationsaufwand.31&lt;br /&gt;
*Skalierbarkeit und Performance: Das Werkzeug muss in der Lage sein, mit der Anzahl der Projekte, der Häufigkeit der Builds und der Komplexität der Pipelines zu skalieren.&lt;br /&gt;
*Unterstützung für Programmiersprachen und Frameworks: Es muss die spezifischen Technologien der Web-Anwendung unterstützen.&lt;br /&gt;
*Benutzerfreundlichkeit und Lernkurve: Die Komplexität des Werkzeugs und die Einarbeitungszeit für das Team sind wichtige Faktoren. Eine steile Lernkurve kann Produktivitätsgewinne zunichtemachen, wenn das Team nicht entsprechend geschult ist.&lt;br /&gt;
*Kosten: Die Kostenmodelle variieren stark (Open Source, Freemium, kommerzielle Lizenzen, nutzungsbasiert). Cloud-gehostete Lösungen haben andere Kostenstrukturen (einfachere Einrichtung, potenziell laufende Kosten) als selbstgehostete (mehr Kontrolle, mehr Wartungsaufwand). Der Total Cost of Ownership (TCO) sollte betrachtet werden.&lt;br /&gt;
*Community-Support und Plugin-Ökosystem: Besonders für Open-Source-Werkzeuge wie Jenkins ist eine aktive Community und ein breites Angebot an Plugins wichtig für Flexibilität und Problemlösung.&lt;br /&gt;
*Sicherheitsfunktionen und Compliance: Das Werkzeug sollte robuste Sicherheitsmechanismen für den Schutz von Code, Artefakten und Secrets bieten und die Einhaltung von Compliance-Richtlinien unterstützen.&lt;br /&gt;
*Vendor Lock-in: Die Abhängigkeit von einem bestimmten Anbieter und die Möglichkeit, bei Bedarf zu einem anderen Werkzeug zu wechseln, sollten bedacht werden.1&lt;br /&gt;
*Hosting-Optionen: Cloud-basiert, selbst-gehostet (On-Premise oder in eigener Cloud-Infrastruktur) oder Hybrid-Modelle.&lt;br /&gt;
&lt;br /&gt;
== Grundlegende Strategien und typische Schritte in CI/CD-Pipelines ==&lt;br /&gt;
&amp;lt;p&amp;gt;Effektive CI/CD-Pipelines basieren auf durchdachten Strategien für Branching, Testing und Deployment, die eng miteinander verknüpft sind.&amp;lt;/p&amp;gt;&lt;br /&gt;
=== Branching-Strategien (z.B. GitFlow, Trunk-Based Development) und deren Einfluss auf CI/CD ===&lt;br /&gt;
&amp;lt;p&amp;gt;Die Wahl der Branching-Strategie im Versionskontrollsystem hat grundlegende Auswirkungen auf den Aufbau und die Effizienz der CI/CD-Pipeline und spiegelt die Philosophie einer Organisation hinsichtlich Release-Management und Risikotoleranz wider.&amp;lt;/p&amp;gt;&lt;br /&gt;
*GitFlow: Diese Strategie verwendet mehrere langlebige Branches wie main (oder master), develop, sowie temporäre feature, release und hotfix Branches. GitFlow bietet eine klare Struktur, insbesondere für Projekte mit geplanten Release-Zyklen. Allerdings kann es durch die längere Lebensdauer von Feature-Branches zu größeren und selteneren Integrationen kommen, was dem Prinzip der kontinuierlichen Integration entgegenstehen kann. Die Pipeline-Logik für GitFlow kann komplexer sein, da sie verschiedene Branch-Typen und deren spezifische Workflows (z.B. Merge-Strategien, Testumfänge) berücksichtigen muss.&lt;br /&gt;
*Trunk-Based Development (TBD): Bei TBD arbeiten Entwickler mit sehr kurzlebigen Feature-Branches, die häufig (mehrmals täglich) direkt in einen zentralen Haupt-Branch (den &amp;quot;Trunk&amp;quot;, oft main oder master) integriert werden. Dieser Ansatz steht in engem Einklang mit den Prinzipien von Continuous Integration, da er den Feedbackzyklus maximiert. Um unfertige Features im Trunk zu verwalten, werden häufig Feature Flags (oder Feature Toggles) eingesetzt, die es erlauben, Code in die Produktion zu deployen, ohne ihn für alle Nutzer freizuschalten. TBD wird oft als Voraussetzung für eine echte kontinuierliche Integration angesehen.&lt;br /&gt;
&amp;lt;p&amp;gt;Der Einfluss auf CI/CD ist signifikant: TBD führt im Allgemeinen zu einfacheren und schnelleren CI/CD-Pipelines, da kleine, häufige Integrationen weniger Konfliktpotenzial bergen und schneller verarbeitet werden können. GitFlows langlebige Branches können die Integration und das Feedback verzögern, was potenziell dem CI-Prinzip &amp;quot;früh und oft committen&amp;quot; widerspricht. TBD erzwingt eine häufige Integration in die Hauptlinie, was die Vorteile von CI maximiert, aber auch die Notwendigkeit schneller, zuverlässiger automatisierter Tests erhöht, um den Trunk stets &amp;quot;grün&amp;quot; (d.h. auslieferungsbereit) zu halten. Feature Flags in TBD entkoppeln das Deployment vom Release und ermöglichen es, Code kontinuierlich zusammenzuführen und bereitzustellen, auch wenn Funktionen noch nicht für den Benutzer bereit sind. Dies ist eine anspruchsvolle Technik, die eine hohe Deployment-Frequenz unterstützt.&amp;amp;nbsp;&amp;lt;/p&amp;gt;&lt;br /&gt;
=== Automatisierte Teststrategien (Unit-, Integrations-, End-to-End-Tests) ===&lt;br /&gt;
&amp;lt;p&amp;gt;Eine umfassende, automatisierte Teststrategie ist das Rückgrat jeder zuverlässigen CI/CD-Pipeline. Sie stellt sicher, dass Fehler frühzeitig erkannt werden und die Qualität der Web-Anwendung kontinuierlich gewährleistet wird. Ein mehrschichtiger Ansatz, oft visualisiert als Testpyramide oder Testmatrix, ist hierbei gängige Praxis.&amp;lt;/p&amp;gt;&lt;br /&gt;
*Unit-Tests: Diese Tests validieren die kleinsten isolierbaren Code-Einheiten (z.B. Funktionen, Methoden, Klassen). Sie werden sehr häufig ausgeführt (idealweise bei jedem Commit), sind schnell und geben unmittelbares Feedback an die Entwickler.&amp;amp;nbsp;&lt;br /&gt;
*Integrationstests: Sie überprüfen das korrekte Zusammenspiel verschiedener Komponenten, Module oder Dienste einer Anwendung. Sie sind komplexer als Unit-Tests und decken Fehler in den Schnittstellen und Interaktionen auf.&lt;br /&gt;
*End-to-End (E2E)-Tests: Diese Tests simulieren vollständige Benutzer-Szenarien und validieren den gesamten Anwendungsfluss aus der Perspektive des Endnutzers. Sie sind am aufwendigsten zu erstellen und zu warten und haben die längste Ausführungszeit. Werkzeuge wie Selenium oder Cypress werden hier häufig eingesetzt.&lt;br /&gt;
*API-Tests: Fokussieren sich auf die direkte Überprüfung der Programmierschnittstellen (APIs) der Web-Anwendung, unabhängig von der Benutzeroberfläche.&lt;br /&gt;
*UI-Tests: Testen die grafische Benutzeroberfläche und deren Interaktionselemente. Sie sind oft Teil der E2E-Tests.&lt;br /&gt;
*Performancetests: Bewerten die Antwortzeiten, Stabilität und Skalierbarkeit der Web-Anwendung unter Last.7 Werkzeuge wie JMeter oder k6 sind hier verbreitet.&lt;br /&gt;
*Sicherheitstests: Umfassen statische (SAST) und dynamische (DAST) Anwendungssicherheitstests sowie Schwachstellen-Scans, um Sicherheitslücken frühzeitig zu identifizieren.&lt;br /&gt;
&amp;lt;p&amp;gt;Eine entscheidende Herausforderung ist die Balance zwischen Testabdeckung und der Geschwindigkeit der Pipeline. Zu viele langsame Tests können den Feedbackzyklus verlangsamen und die Akzeptanz von CI/CD behindern.&amp;lt;/p&amp;gt;&lt;br /&gt;
=== Deployment-Strategien (z.B. Blue/Green, Canary, Rolling Deployments) ===&lt;br /&gt;
&amp;lt;p&amp;gt;Deployment-Strategien definieren, wie neue Versionen einer Web-Anwendung in die Produktionsumgebung ausgerollt werden. Die Wahl der Strategie beeinflusst das Risiko, die Ausfallzeit und das Nutzererlebnis während des Updates. CI/CD-Werkzeuge spielen eine Schlüsselrolle bei der Automatisierung dieser Strategien.58&amp;lt;/p&amp;gt;&lt;br /&gt;
*Blue/Green Deployment: Hierbei werden zwei identische Produktionsumgebungen unterhalten: &amp;quot;Blue&amp;quot; (die aktuelle Live-Version) und &amp;quot;Green&amp;quot; (die neue Version). Die neue Version wird auf der Green-Umgebung bereitgestellt und getestet. Ist alles erfolgreich, wird der Traffic vom Load Balancer von Blue auf Green umgeschaltet. Vorteile sind minimale bis keine Ausfallzeit und ein sehr einfacher Rollback (Umschalten zurück auf Blue). Nachteilig sind die höheren Infrastrukturkosten für die doppelte Umgebung.&lt;br /&gt;
*Canary Release (Canary Deployment): Die neue Version wird zunächst nur einem kleinen Prozentsatz der Nutzer oder Server (&amp;quot;Canaries&amp;quot;) zur Verfügung gestellt. Das Verhalten und die Performance werden engmaschig überwacht. Bei positivem Ergebnis wird der Anteil der Nutzer, die die neue Version erhalten, schrittweise erhöht, bis alle Nutzer umgestellt sind. Diese Methode ermöglicht frühzeitiges Feedback und begrenzt den &amp;quot;Blast Radius&amp;quot; potenzieller Fehler.&lt;br /&gt;
*Rolling Deployment: Updates werden schrittweise auf den Instanzen der Produktionsumgebung ausgerollt. Eine Teilmenge der Server wird aktualisiert, überwacht, und dann folgt die nächste Teilmenge. Während des Rollouts können unterschiedliche Versionen der Anwendung gleichzeitig aktiv sein, was zu Kompatibilitätsüberlegungen führen kann.&lt;br /&gt;
*A/B Testing: Diese Strategie wird oft für das Testen neuer Features oder Designs verwendet, indem verschiedene Versionen (A und B) parallel an unterschiedliche Nutzersegmente ausgeliefert werden, um deren Performance anhand definierter Metriken zu vergleichen. Feature Flags sind hier ein wichtiges Hilfsmittel.&lt;br /&gt;
*Weitere Strategien:&lt;br /&gt;
&lt;br /&gt;
*Big Bang Deployment: Alle Änderungen werden auf einmal für alle Nutzer ausgerollt. Dies ist die riskanteste Methode mit potenziell hohen Ausfallzeiten.&lt;br /&gt;
*Recreate Deployment: Die alte Version wird gestoppt und die neue Version wird komplett neu bereitgestellt. Dies führt zu Ausfallzeiten.&lt;br /&gt;
&amp;lt;p&amp;gt;Deployment-Strategien beeinflussen direkt Risiko, Ausfallzeiten und Nutzererfahrung.&amp;lt;/p&amp;gt;&lt;br /&gt;
== Weiterführende Themen ==&lt;br /&gt;
&amp;lt;p&amp;gt;Über die Grundlagen hinaus gewinnen im Kontext von CI/CD für Web-Anwendungen fortgeschrittene Konzepte an Bedeutung: Dazu zählen Infrastructure as Code, DevSecOps, Monitoring und Observability sowie die Messung des Erfolgs mittels Metriken.&amp;lt;/p&amp;gt;&lt;br /&gt;
=== Infrastructure as Code (IaC) und dessen Integration in CI/CD ===&lt;br /&gt;
&amp;lt;p&amp;gt;Infrastructure as Code (IaC) bezeichnet die Praxis, IT-Infrastruktur (Server, Netzwerke, Speicher, Datenbanken etc.) durch Code – typischerweise in Form von Konfigurationsdateien – zu definieren, zu provisionieren und zu verwalten, anstatt manuelle Prozesse zu nutzen. Wichtige Prinzipien von IaC sind Idempotenz (mehrmalige Ausführung führt zum selben Ergebnis), Versionierung der Infrastrukturbeschreibungen und die Automatisierung der Infrastrukturänderungen.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Die Vorteile von IaC sind vielfältig: Es ermöglicht Konsistenz über verschiedene Umgebungen hinweg, Wiederholbarkeit von Setups, einfache Skalierbarkeit, schnellere Deployments, eine Reduktion menschlicher Fehler und die Versionierung der Infrastruktur analog zum Anwendungscode.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Gängige Werkzeuge für IaC sind:&amp;lt;/p&amp;gt;&lt;br /&gt;
*Terraform: Ein deklaratives, Cloud-agnostisches Werkzeug von HashiCorp, das primär für die Provisionierung von Infrastrukturressourcen verwendet wird.&lt;br /&gt;
*Ansible: Ein prozedurales Werkzeug, das oft für Konfigurationsmanagement und Anwendungsdeployment auf bereits provisionierter Infrastruktur eingesetzt wird.&lt;br /&gt;
*Weitere Werkzeuge umfassen AWS CloudFormation, Azure Resource Manager (ARM) Templates oder Pulumi.&lt;br /&gt;
&amp;lt;p&amp;gt;Die Integration von IaC in CI/CD-Pipelines bedeutet, dass Änderungen an der Infrastruktur denselben rigorosen Prozess aus Versionierung, automatisierten Tests und kontrolliertem Deployment durchlaufen wie der Anwendungscode Dies stellt sicher, dass Entwicklungs-, Staging- und Produktionsumgebungen konsistent sind, was eine häufige Fehlerquelle eliminiert.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;IaC erweitert die &amp;quot;Alles als Code&amp;quot;-Philosophie auf den gesamten Technologie-Stack und verbessert die Zuverlässigkeit und Geschwindigkeit der Bereitstellung von Web-Anwendungen. Manuelle Infrastrukturprovisionierung ist langsam und fehleranfällig. IaC hingegen ermöglicht die Definition, Versionierung und automatische Provisionierung der Infrastruktur. In CI/CD integriert bedeutet dies, dass die Anwendung, ihre Abhängigkeiten und die benötigte Infrastruktur gemeinsam bereitgestellt und validiert werden können. Diese holistische Automatisierung ist entscheidend für komplexe Web-Anwendungen, insbesondere solche, die Microservices oder Cloud-Plattformen nutzen, da sie sicherstellt, dass die Anwendung von der Entwicklung bis zur Produktion in einer bekannten, konsistenten und reproduzierbaren Umgebung läuft. Dies reduziert drastisch &amp;quot;Works on my machine&amp;quot;-Probleme und beschleunigt den gesamten Lieferzyklus.&amp;amp;nbsp;&amp;lt;/p&amp;gt;&lt;br /&gt;
=== DevSecOps: Sicherheit als integraler Bestandteil der Pipeline ===&lt;br /&gt;
&amp;lt;p&amp;gt;DevSecOps ist ein Ansatz, der Sicherheitspraktiken von Beginn an und während des gesamten DevOps-Lebenszyklus integriert, anstatt Sicherheit als nachgelagerten Schritt zu betrachten. Dies wird oft als &amp;quot;Shift Left&amp;quot;-Prinzip bezeichnet, bei dem Sicherheitsüberlegungen so früh wie möglich in den Entwicklungsprozess einfließen. Sicherheit wird dabei zur gemeinsamen Verantwortung von Entwicklungs-, Betriebs- und Sicherheitsteams.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Die Bedeutung von DevSecOps liegt darin, dass die frühzeitige Adressierung von Sicherheitsaspekten Kosten und Risiken reduziert. Traditionelle Sicherheitsmodelle, bei denen Sicherheitstests erst am Ende des Entwicklungszyklus stattfinden, können in agilen Umgebungen mit häufigen Releases zu Engpässen werden.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Die Integration von Sicherheit in die CI/CD-Pipeline erfolgt durch verschiedene automatisierte Maßnahmen:&amp;lt;/p&amp;gt;&lt;br /&gt;
*Static Application Security Testing (SAST): Analyse des Quellcodes auf bekannte Schwachstellenmuster, bevor die Anwendung kompiliert oder deployed wird. Werkzeuge wie Bandit, Semgrep, SonarQube oder Checkmarx können in die Build-Phase integriert werden.&lt;br /&gt;
*Dynamic Application Security Testing (DAST): Testen der laufenden Anwendung auf Schwachstellen, oft in einer Staging-Umgebung, indem typische Angriffsmuster simuliert werden. OWASP ZAP oder Burp Suite sind hier gängige Werkzeuge.&lt;br /&gt;
*Software Composition Analysis (SCA) / Dependency Scanning: Überprüfung von Drittanbieter-Bibliotheken und Abhängigkeiten auf bekannte Sicherheitslücken.&lt;br /&gt;
*Container Image Scanning: Scannen von Docker-Images auf bekannte Schwachstellen in den Basissystemen und installierten Paketen.&lt;br /&gt;
*Secrets Management: Sichere Verwaltung und Injektion von sensiblen Daten wie API-Schlüsseln, Passwörtern und Zertifikaten in die Pipeline und Anwendungen, ohne sie im Code zu speichern.&lt;br /&gt;
*Infrastructure Security: Überprüfung von IaC-Skripten auf Sicherheitsfehlkonfigurationen.&lt;br /&gt;
&amp;lt;p&amp;gt;Durch die Automatisierung von Sicherheitsprüfungen innerhalb der CI/CD-Pipeline können Unternehmen die Entwicklungsgeschwindigkeit beibehalten, ohne die Sicherheit zu kompromittieren, was für das Vertrauen in Web-Anwendungen unerlässlich ist. Dadurch können Schwachstellen behoben werden, wenn es am kostengünstigsten und einfachsten ist – während der Entwicklung. Die Automatisierung dieser Prüfungen gewährleistet Konsistenz und Abdeckung und reduziert die Wahrscheinlichkeit menschlicher Fehler.&amp;amp;nbsp;&amp;lt;/p&amp;gt;&lt;br /&gt;
=== Monitoring, Logging und Observability zur Sicherstellung von Qualität und Performance ===&lt;br /&gt;
&amp;lt;p&amp;gt;Nach dem Deployment einer Web-Anwendung ist die kontinuierliche Überwachung ihrer Qualität und Performance unerlässlich. Hierbei unterscheidet man zwischen Monitoring, Logging und dem umfassenderen Konzept der Observability.&amp;lt;/p&amp;gt;&lt;br /&gt;
*Monitoring (Überwachung): Bezieht sich auf das Sammeln, Verarbeiten, Analysieren und Darstellen von vordefinierten Metriken, um den Zustand eines Systems zu verstehen und bekannte Probleme oder Anomalien zu erkennen. Es ist oft reaktiv und löst Alarme aus, wenn bestimmte Schwellwerte überschritten werden.&lt;br /&gt;
*Logging (Protokollierung): Umfasst das Erfassen von detaillierten, zeitgestempelten Aufzeichnungen von Ereignissen, Fehlern und Systemaktivitäten.23 Logs sind unerlässlich für die Fehlerdiagnose und das Verständnis des Systemverhaltens im Detail.&lt;br /&gt;
*Observability (Beobachtbarkeit): Geht über das reine Monitoring hinaus und beschreibt die Fähigkeit, aus den externen Outputs eines Systems Rückschlüsse auf dessen internen Zustand zu ziehen und auch unbekannte Probleme (&amp;quot;unknown unknowns&amp;quot;) zu untersuchen. Observability basiert typischerweise auf den drei Säulen: Logs, Metriken und Traces (verteilte Ablaufverfolgung). Sie ermöglicht eine proaktive Untersuchung des Systemverhaltens.&lt;br /&gt;
&amp;lt;p&amp;gt;Die Bedeutung im CI/CD-Kontext liegt darin, dass diese Praktiken unmittelbares Feedback über den Erfolg und die Auswirkungen von Deployments liefern. Sie helfen, Probleme in der Produktionsumgebung schnell zu erkennen und zu diagnostizieren, und die gewonnenen Erkenntnisse fließen zurück in die Optimierung der Pipeline und der Anwendung selbst. Gängige Werkzeuge sind Datadog, Prometheus mit Grafana, Splunk oder der ELK Stack (Elasticsearch, Logstash, Kibana).&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Die Entwicklung vom Monitoring zur Observability im CI/CD-Kontext spiegelt die zunehmende Komplexität moderner Web-Anwendungen wider (z.B. Microservices). Observability ist entscheidend für die Aufrechterhaltung von Zuverlässigkeit und Performance in dynamischen, häufig aktualisierten Systemen. Einfaches Monitoring bekannter Metriken reicht für komplexe verteilte Systeme, bei denen Fehlermodi nicht immer vorhersagbar sind, oft nicht aus. Observability ermöglicht es Teams durch die Kombination von Metriken, Logs und Traces zu verstehen, warum etwas passiert, nicht nur, dass etwas nicht stimmt. In einer CI/CD-Umgebung mit häufigen Deployments ist die Fähigkeit, die Auswirkungen neuer Änderungen schnell zu diagnostizieren und zu verstehen, entscheidend für die Einhaltung des MTTR (Mean Time to Recovery). Dieses tiefere Verständnis ermöglicht eine effektivere Fehlerbehebung und eine kontinuierliche Verbesserung sowohl der Anwendung als auch der CI/CD-Pipeline selbst.&amp;lt;/p&amp;gt;&lt;br /&gt;
=== Relevante Metriken und KPIs zur Erfolgsmessung (z.B. DORA Metriken) ===&lt;br /&gt;
&amp;lt;p&amp;gt;Um die Effektivität von CI/CD-Prozessen und deren Beitrag zum Geschäftserfolg zu bewerten, ist die Messung anhand relevanter Metriken und Key Performance Indicators (KPIs) unerlässlich.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Die DORA-Metriken, benannt nach der DevOps Research and Assessment Gruppe, gelten als Industriestandard zur Messung der Leistungsfähigkeit von Software-Delivery-Prozessen:&amp;lt;/p&amp;gt;&lt;br /&gt;
*Deployment Frequency (DF) / Bereitstellungshäufigkeit: Wie oft wird Code erfolgreich in die Produktion überführt? Misst die Geschwindigkeit und Agilität des Teams.&lt;br /&gt;
*Lead Time for Changes (LT) / Durchlaufzeit für Änderungen: Wie lange dauert es von der Code-Commit bis zum erfolgreichen Deployment in die Produktion? Indikator für die Effizienz des gesamten Entwicklungsprozesses.&lt;br /&gt;
*Change Failure Rate (CFR) / Fehlerrate von Änderungen: Welcher Prozentsatz der Deployments führt zu Fehlern in der Produktion, die ein Eingreifen erfordern (z.B. Rollback, Hotfix)? Misst die Stabilität und Qualität der Releases.&lt;br /&gt;
*Mean Time to Recovery (MTTR) / Mittlere Wiederherstellungszeit: Wie lange dauert es im Durchschnitt, einen Dienst nach einem Ausfall in der Produktion wiederherzustellen? Zeigt die Resilienz des Systems und die Effektivität der Incident-Response-Prozesse.&lt;br /&gt;
&amp;lt;p&amp;gt;Neben den DORA-Metriken sind weitere KPIs relevant:&amp;lt;/p&amp;gt;&lt;br /&gt;
*Build-Dauer: Zeit für die Durchführung der Build-Phase.&lt;br /&gt;
*Testabdeckung (Test Coverage): Prozentsatz des Codes, der durch automatisierte Tests abgedeckt ist.&lt;br /&gt;
*Defect Escape Rate: Anzahl der Fehler, die erst in der Produktion entdeckt werden.&lt;br /&gt;
*Anwendungs-Performance-Metriken: Antwortzeiten, Fehlerraten der Anwendung.&lt;br /&gt;
*Infrastrukturauslastung und -kosten.&lt;br /&gt;
&lt;br /&gt;
== Herausforderungen, Anti-Patterns und Best Practices ==&lt;br /&gt;
&amp;lt;p&amp;gt;Trotz der erheblichen Vorteile ist die Implementierung von CI/CD nicht ohne Hürden. Es gibt typische Herausforderungen und Anti-Patterns, denen jedoch mit etablierten Best Practices begegnet werden kann.&amp;lt;/p&amp;gt;&lt;br /&gt;
=== Herausforderungen und Anti-Patterns ===&lt;br /&gt;
&amp;lt;p&amp;gt;Zu den häufigsten Herausforderungen bei der CI/CD-Implementierung für Web-Anwendungen gehören Performance-Probleme in der Pipeline, mangelnde Teamkommunikation, Komplexität bei der Versionskontrolle und im Branching, fehlerhafte oder unzureichende automatisierte Tests, Sicherheitslücken, Skalierungsprobleme der Infrastruktur, schwierige Fehlersuche, häufige Build-Fehler, Probleme im Abhängigkeitsmanagement, Inkonsistenzen zwischen Umgebungen, überkomplizierte Pipelines, mangelnde Testabdeckung, Lücken im Monitoring und Reporting sowie ineffektive Rollback-Mechanismen. Ein signifikanter Faktor ist oft auch der Widerstand gegen Veränderungen in etablierten Prozessen und Kulturen.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Typische Anti-Patterns, die den Erfolg von CI/CD behindern, sind:&amp;lt;/p&amp;gt;&lt;br /&gt;
*Mangelnde Zusammenarbeit: Teams arbeiten weiterhin in Silos.&lt;br /&gt;
*Übermäßige Abhängigkeit von manuellen Prozessen: Wichtige Schritte in der Pipeline werden nicht automatisiert.&lt;br /&gt;
*Ignorieren von Sicherheit: Sicherheitsaspekte werden erst spät oder gar nicht berücksichtigt (Security as an Afterthought).&lt;br /&gt;
*Unzureichende Tests: Zu geringe Testabdeckung oder ineffektive Teststrategien.&lt;br /&gt;
*Widerstand gegen Veränderungen: Festhalten an alten Arbeitsweisen und Werkzeugen.&lt;br /&gt;
*Schlechtes Monitoring und Feedback: Fehlende Transparenz über den Zustand der Pipeline und der Deployments.&lt;br /&gt;
*Mehrfaches Bauen von Artefakten: Das gleiche Artefakt wird für verschiedene Stufen oder Umgebungen immer wieder neu gebaut, anstatt das einmal gebaute Artefakt weiterzureichen.&lt;br /&gt;
&lt;br /&gt;
=== Best Practices ===&lt;br /&gt;
&amp;lt;p&amp;gt;Um diesen Herausforderungen zu begegnen und Anti-Patterns zu vermeiden, haben sich folgende Best Practices etabliert 15:&amp;lt;/p&amp;gt;&lt;br /&gt;
*Früh und oft committen (Commit Early, Commit Often): Kleine, häufige Code-Änderungen erleichtern die Integration und Fehlersuche.&lt;br /&gt;
*Builds grün halten (Keep Builds Green): Fehlerhafte Builds sofort beheben, um die Pipeline nicht zu blockieren.&lt;br /&gt;
*Einmal bauen (Build Only Once): Das kompilierte Artefakt einmal erstellen und dieses durch alle Test- und Deployment-Stufen bewegen.&lt;br /&gt;
*Tests optimieren (Streamline Tests): Eine ausgewogene Teststrategie mit schnellen Feedbackzyklen entwickeln.&lt;br /&gt;
*Umgebungen sauber halten (Clean Environments): Testumgebungen regelmäßig zurücksetzen oder neu erstellen (IaC hilft hier).&lt;br /&gt;
*Pipeline sichern (Secure Your Pipeline): Sicherheitsmaßnahmen in jede Phase integrieren.&lt;br /&gt;
*Prozess einhalten (Stick to Your Process): Ausnahmen und manuelle Eingriffe minimieren.&lt;br /&gt;
*Pipeline überwachen und messen (Monitor and Measure Your Pipeline): Metriken nutzen, um Engpässe und Verbesserungspotenziale zu identifizieren.&lt;br /&gt;
*Teamarbeit fördern (Make it a Team Effort): CI/CD ist eine gemeinsame Verantwortung.&lt;br /&gt;
*Wiederverwendbare/Gemeinsame Pipelines nutzen (Use Shared Pipelines - DRY): Duplizierung von Pipeline-Logik vermeiden.15&lt;br /&gt;
*Progressive Delivery anwenden: Strategien wie Canary Releases oder Blue/Green Deployments nutzen, um Risiken zu minimieren.15&lt;br /&gt;
&lt;br /&gt;
== Fallbeispiele und Ausblick ==&lt;br /&gt;
&amp;lt;p&amp;gt;Die erfolgreiche Implementierung von CI/CD hat bei vielen führenden web-zentrierten Unternehmen zu signifikanten Verbesserungen geführt.&amp;lt;/p&amp;gt;&lt;br /&gt;
=== Fallbeispiele ===&lt;br /&gt;
&lt;br /&gt;
*Netflix: Das Unternehmen transformierte seine Architektur hin zu Microservices und entwickelte mit Spinnaker eine eigene Open-Source Continuous Delivery Plattform. Durch den Einsatz von Chaos Engineering wird die Resilienz der Systeme kontinuierlich getestet. Ergebnis sind tausende von Deployments pro Tag, eine drastisch reduzierte Time-to-Market und hohe Systemstabilität.&lt;br /&gt;
*Etsy: Der Online-Marktplatz setzt auf eine umfangreiche Suite automatisierter Tests, Feature Toggles für kontrollierte Rollouts und eine vollautomatische Deployment-Pipeline. Dies führte zu einer Reduktion der Deployment-Zeiten von Stunden auf Minuten und einer deutlichen Steigerung der Agilität und Code-Qualität.&lt;br /&gt;
*Google: Nutzt eine Monorepo-Struktur und das Build-Tool Bazel, um Millionen von Builds und Tests täglich zu bewältigen. Canary Releases sind Standard zur Risikominimierung.&lt;br /&gt;
*Amazon: Verwendet ebenfalls eine Microservice-Architektur und AWS-eigene CI/CD-Werkzeuge wie AWS CodePipeline, um kontinuierliche Deployments über seine globale Plattform zu ermöglichen.&lt;br /&gt;
&amp;lt;p&amp;gt;Die Erfolgsgeschichten von CI/CD-Pionieren wie Netflix und Etsy zeigen, dass eine ausgereifte CI/CD-Praxis kein Endzustand ist, sondern eine kontinuierliche Reise der Verbesserung, Anpassung und oft auch der Entwicklung eigener Werkzeuge oder der signifikanten Anpassung bestehender Werkzeuge an einzigartige, groß angelegte Bedürfnisse.&amp;amp;nbsp;&amp;lt;/p&amp;gt;&lt;br /&gt;
=== Ausblick ===&lt;br /&gt;
&amp;lt;p&amp;gt;Die Entwicklung im Bereich CI/CD bleibt dynamisch. Zukünftige Trends umfassen den verstärkten Einsatz von Künstlicher Intelligenz (KI) zur Optimierung von Pipelines (z.B. prädiktive Analysen für Testauswahl, automatische Fehlerdiagnose), die weitere Integration von Sicherheit als selbstverständlicher Bestandteil (DevSecOps wird zur Norm), der Aufstieg von Serverless CI/CD-Lösungen, die bedarfsgerecht skalieren und Kosten optimieren, sowie die Verbreitung von GitOps, bei dem Git als &amp;quot;Single Source of Truth&amp;quot; für die deklarative Beschreibung von Infrastruktur und Anwendungen dient und Änderungen automatisch synchronisiert werden. Die kontinuierliche Verbesserung und Anpassung von CI/CD-Praktiken wird für Unternehmen, die im Wettbewerb der Web-Anwendungsentwicklung bestehen wollen, von entscheidender Bedeutung bleiben.&amp;lt;/p&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;/div&gt;</summary>
		<author><name>Völkl Anna</name></author>
	</entry>
	<entry>
		<id>https://mediawiki.fernfh.ac.at/mediawiki/index.php?title=DevOps_and_Continuous_Integration_Continuous_Deployment&amp;diff=6578</id>
		<title>DevOps and Continuous Integration Continuous Deployment</title>
		<link rel="alternate" type="text/html" href="https://mediawiki.fernfh.ac.at/mediawiki/index.php?title=DevOps_and_Continuous_Integration_Continuous_Deployment&amp;diff=6578"/>
		<updated>2025-05-17T12:31:23Z</updated>

		<summary type="html">&lt;p&gt;Völkl Anna: Die Seite wurde neu angelegt: „DevOps und Continuous Integration/Continuous Deployment (CI/CD) für Web-Anwendungen“&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;DevOps und Continuous Integration/Continuous Deployment (CI/CD) für Web-Anwendungen&lt;/div&gt;</summary>
		<author><name>Völkl Anna</name></author>
	</entry>
	<entry>
		<id>https://mediawiki.fernfh.ac.at/mediawiki/index.php?title=Datenbankintegration_(Webdevelopment)&amp;diff=6577</id>
		<title>Datenbankintegration (Webdevelopment)</title>
		<link rel="alternate" type="text/html" href="https://mediawiki.fernfh.ac.at/mediawiki/index.php?title=Datenbankintegration_(Webdevelopment)&amp;diff=6577"/>
		<updated>2025-05-16T20:53:49Z</updated>

		<summary type="html">&lt;p&gt;Völkl Anna: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Kapitel X: Datenbankintegration mit PHP und MySQL =&lt;br /&gt;
&lt;br /&gt;
== Einleitung: Warum Datenbanken in PHP-Webanwendungen? ==&lt;br /&gt;
&lt;br /&gt;
Moderne Webanwendungen sind selten statisch. Sie leben von dynamischen Inhalten, Benutzerinteraktionen und der Fähigkeit, Informationen über Sitzungen hinweg zu speichern und wieder abzurufen. Ob es sich um ein Content-Management-System (CMS), einen Online-Shop, ein soziales Netzwerk oder eine einfache Benutzerverwaltung handelt – die Persistenz von Daten ist entscheidend. Hier kommen Datenbanken ins Spiel.&lt;br /&gt;
&lt;br /&gt;
PHP allein, als serverseitige Skriptsprache, kann Daten nur für die Dauer der Ausführung eines Skripts im Arbeitsspeicher halten. Sobald das Skript beendet ist, gehen diese Informationen verloren. Um Daten dauerhaft zu speichern, zu organisieren, zu ändern und effizient abzurufen, benötigen PHP-Anwendungen ein externes Datenbanksystem.&lt;br /&gt;
&lt;br /&gt;
Eine der populärsten Datenbanklösungen im Webentwicklungsbereich, insbesondere in Kombination mit PHP, ist MySQL. Als relationales Datenbankmanagementsystem (RDBMS) bietet MySQL eine strukturierte Methode zur Datenspeicherung in Tabellen, die miteinander in Beziehung stehen können. Es ist ein Kernbestandteil vieler Webserver-Setups wie LAMP (Linux, Apache, MySQL, PHP) oder LEMP (Linux, Nginx, MySQL, PHP).&lt;br /&gt;
&lt;br /&gt;
Um die Brücke zwischen der PHP-Anwendung und der MySQL-Datenbank zu schlagen, stellt PHP spezielle Erweiterungen (Extensions) bereit. Diese Erweiterungen fungieren als Schnittstelle oder API (Application Programming Interface), die es PHP-Skripten ermöglichen, eine Verbindung zur Datenbank herzustellen, SQL-Befehle zu senden und die von der Datenbank zurückgegebenen Ergebnisse zu empfangen und zu verarbeiten.&lt;br /&gt;
&lt;br /&gt;
Dieses Kapitel führt in die Grundlagen der Datenbankintegration mit PHP und MySQL ein. Es enthält die verschiedenen verfügbaren PHP-Erweiterungen, wobei der Schwerpunkt auf den modernen und sicheren Ansätzen liegt: PDO (PHP Data Objects) und MySQLi (MySQL Improved). Es wird gezeigt, wie man Verbindungen aufbaut, grundlegende Datenbankoperationen – bekannt als CRUD (Create, Read, Update, Delete) – durchführt und wie man Daten sicher abfragt und manipuliert. Ein besonderes Augenmerk liegt dabei auf der Sicherheit, insbesondere auf der Vermeidung von SQL-Injection-Angriffen durch den Einsatz von Prepared Statements. Zum Abschluss erfolgt ein Überblick über nützliche PHP-Array-Funktionen zur Verarbeitung der abgerufenen Datenbankergebnisse.&lt;br /&gt;
&lt;br /&gt;
== Überblick über PHP-Datenbankerweiterungen ==&lt;br /&gt;
&lt;br /&gt;
Die Art und Weise, wie PHP mit Datenbanken interagiert, hat sich im Laufe der Zeit weiterentwickelt. Ursprünglich gab es für jede populäre Datenbank eine eigene spezifische Erweiterung. Für MySQL waren dies die &amp;lt;code&amp;gt;mysql_*&amp;lt;/code&amp;gt; Funktionen. Diese sind jedoch inzwischen veraltet und wurden durch modernere, flexiblere und sicherere Alternativen ersetzt. Heute stehen Entwicklern hauptsächlich zwei empfohlene Erweiterungen für die Arbeit mit MySQL zur Verfügung: PDO und MySQLi.&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;PDO (PHP Data Objects):&amp;#039;&amp;#039;&amp;#039; Eine allgemeine Datenzugriffs-Abstraktionsschicht, die mit verschiedenen Datenbanksystemen verwendet werden kann.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;MySQLi (MySQL Improved):&amp;#039;&amp;#039;&amp;#039; Eine Erweiterung, die speziell für die Arbeit mit MySQL entwickelt wurde und dessen spezifische Funktionen unterstützt.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;&amp;lt;code&amp;gt;mysql_*&amp;lt;/code&amp;gt; Funktionen:&amp;#039;&amp;#039;&amp;#039; Die ursprüngliche MySQL-Erweiterung, die seit PHP 7.0 entfernt wurde und nicht mehr verwendet werden sollte.&lt;br /&gt;
Im Folgenden werden diese drei Optionen detaillierter vorgestellt.&lt;br /&gt;
&lt;br /&gt;
=== PDO (PHP Data Objects) ===&lt;br /&gt;
&lt;br /&gt;
Die PDO-Erweiterung definiert eine leichtgewichtige und konsistente Schnittstelle für den Datenbankzugriff in PHP. Sie fungiert als &amp;#039;&amp;#039;Datenzugriffs-Abstraktionsschicht&amp;#039;&amp;#039;. Das bedeutet, dass Entwickler unabhängig von der verwendeten Datenbank dieselben PDO-Funktionen nutzen können, um Abfragen auszuführen und Daten abzurufen.[1, 2] Es ist jedoch wichtig zu verstehen, dass PDO &amp;#039;&amp;#039;keine&amp;#039;&amp;#039; Datenbankabstraktion im Sinne einer SQL-Dialekt-Übersetzung oder der Emulation fehlender Datenbankfeatures bietet. Um eine Verbindung zu einer spezifischen Datenbank wie MySQL herzustellen, benötigt PDO einen entsprechenden datenbankspezifischen Treiber, z.B. &amp;lt;code&amp;gt;pdo_mysql&amp;lt;/code&amp;gt;, der in der PHP-Konfiguration aktiviert sein muss.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Vorteile von PDO:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
*&amp;#039;&amp;#039;&amp;#039;Datenbankunabhängigkeit:&amp;#039;&amp;#039;&amp;#039; Der größte Vorteil von PDO ist die Möglichkeit, den Code für Datenbankinteraktionen weitgehend unverändert zu lassen, selbst wenn das zugrunde liegende Datenbanksystem gewechselt wird (z.B. von MySQL zu PostgreSQL). Dies erhöht die Portabilität und Flexibilität von PHP-Anwendungen erheblich.[2, 3]&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Sicherheit durch Prepared Statements:&amp;#039;&amp;#039;&amp;#039; PDO bietet eine robuste und konsistente Implementierung von Prepared Statements. Diese Technik ist der Goldstandard zur Verhinderung von SQL-Injection-Angriffen, da sie die SQL-Befehlsstruktur strikt von den übergebenen Daten trennt.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Benutzerfreundlichkeit und Konsistenz:&amp;#039;&amp;#039;&amp;#039; PDO stellt eine einheitliche API mit nützlichen Hilfsfunktionen bereit, wie z.B. vielfältige &amp;quot;Fetch Modes&amp;quot;, um die Struktur der abgerufenen Daten zu steuern.[2] Die konsistente Funktionsweise über verschiedene Datenbanktreiber hinweg vereinfacht die Entwicklung.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Moderne Fehlerbehandlung:&amp;#039;&amp;#039;&amp;#039; PDO verwendet standardmäßig Exceptions (&amp;lt;code&amp;gt;PDOException&amp;lt;/code&amp;gt;) zur Signalisierung von Fehlern. Dies ermöglicht eine strukturierte und moderne Fehlerbehandlung mittels &amp;lt;code&amp;gt;try...catch&amp;lt;/code&amp;gt;-Blöcken, was als Best Practice gilt.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Verbindungsaufbau mit PDO (Beispiel MySQL):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;DSN (Data Source Name):&amp;#039;&amp;#039;&amp;#039; Die Verbindungsparameter werden in einer Zeichenkette, dem DSN, übergeben. Für MySQL hat der DSN typischerweise das Format &amp;lt;code&amp;gt;mysql:host=hostname;dbname=datenbankname;charset=zeichensatz;unix_socket=pfad_zum_socket&amp;lt;/code&amp;gt;. Die Angabe des Zeichensatzes (z.B. &amp;lt;code&amp;gt;utf8mb4&amp;lt;/code&amp;gt;) ist wichtig für die korrekte Datenübertragung. Beispiel: &amp;lt;code&amp;gt;mysql:host=localhost;dbname=test;charset=utf8mb4&amp;lt;/code&amp;gt;.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;PDO-Konstruktor:&amp;#039;&amp;#039;&amp;#039; Eine Verbindung wird durch Instanziierung der PDO-Klasse hergestellt: &amp;lt;code&amp;gt;$pdo = new PDO($dsn, $username, $password, $options);&amp;lt;/code&amp;gt;. Die Parameter sind der DSN, der Datenbankbenutzername, das Passwort und ein optionales Array mit Treiberoptionen.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Verbindungsoptionen (&amp;lt;code&amp;gt;$options&amp;lt;/code&amp;gt;):&amp;#039;&amp;#039;&amp;#039; Über dieses Array können wichtige Verhaltensweisen von PDO gesteuert werden:&lt;br /&gt;
* &amp;lt;code&amp;gt;PDO::ATTR_ERRMODE =&amp;amp;gt; PDO::ERRMODE_EXCEPTION&amp;lt;/code&amp;gt;: Dies ist die empfohlene Einstellung für die Fehlerbehandlung. PDO wirft bei Fehlern eine &amp;lt;code&amp;gt;PDOException&amp;lt;/code&amp;gt;, die abgefangen werden kann.&lt;br /&gt;
* &amp;lt;code&amp;gt;PDO::ATTR_DEFAULT_FETCH_MODE =&amp;amp;gt; PDO::FETCH_ASSOC&amp;lt;/code&amp;gt;: Legt fest, dass Daten standardmäßig als assoziatives Array (&amp;lt;code&amp;gt;spaltenname =&amp;amp;gt; wert&amp;lt;/code&amp;gt;) zurückgegeben werden, was oft die Weiterverarbeitung erleichtert.&lt;br /&gt;
* &amp;lt;code&amp;gt;PDO::ATTR_EMULATE_PREPARES =&amp;amp;gt; false&amp;lt;/code&amp;gt;: Deaktiviert die Emulation von Prepared Statements durch PDO und zwingt die Verwendung nativer Prepared Statements des Datenbanktreibers. Dies gilt als die sicherste und oft performanteste Methode. Standardmäßig ist die Emulation aktiviert (&amp;lt;code&amp;gt;true&amp;lt;/code&amp;gt;). Obwohl PDO auch im emulierten Modus durch korrektes Escaping Schutz vor SQL Injection bietet, eliminiert die Deaktivierung potenzielle, wenn auch seltene, Sicherheitsrisiken, die in bestimmten Konstellationen (z.B. bei Verwendung exotischer Zeichensätze) auftreten könnten. Die explizite Deaktivierung ist daher eine wichtige Best Practice.&lt;br /&gt;
* &amp;lt;code&amp;gt;PDO::ATTR_PERSISTENT =&amp;amp;gt; true&amp;lt;/code&amp;gt;: Aktiviert persistente Datenbankverbindungen. Diese werden nicht am Ende des Skripts geschlossen, sondern in einem Pool gehalten und für nachfolgende Anfragen wiederverwendet. Dies kann die Performance von Webanwendungen verbessern, da der Overhead des Verbindungsaufbaus entfällt. Die Verwendung erfordert jedoch sorgfältige Überlegung, da der Zustand der Verbindung zwischen Anfragen bestehen bleiben kann.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Fehlerbehandlung:&amp;#039;&amp;#039;&amp;#039; Der Verbindungsaufbau sollte immer in einem &amp;lt;code&amp;gt;try...catch&amp;lt;/code&amp;gt;-Block erfolgen, um &amp;lt;code&amp;gt;PDOException&amp;lt;/code&amp;gt; abzufangen und angemessen darauf zu reagieren (z.B. Fehlermeldung loggen, alternative Aktion ausführen). Es ist kritisch, detaillierte Fehlermeldungen in Produktionsumgebungen nicht an den Benutzer auszugeben, da sie sensible Informationen enthalten könnten. Die PHP-Einstellung &amp;lt;code&amp;gt;display_errors&amp;lt;/code&amp;gt; sollte daher auf &amp;lt;code&amp;gt;0&amp;lt;/code&amp;gt; gesetzt sein.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Verbindung schließen:&amp;#039;&amp;#039;&amp;#039; Eine PDO-Verbindung bleibt für die Lebensdauer des PDO-Objekts aktiv. Um sie explizit zu schließen, müssen alle Referenzen auf das PDO-Objekt und alle davon abgeleiteten &amp;lt;code&amp;gt;PDOStatement&amp;lt;/code&amp;gt;-Objekte entfernt werden, typischerweise durch Zuweisung von &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt;. PHP schließt die Verbindung automatisch am Ende des Skriptlaufs, wenn dies nicht manuell geschieht.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Code-Beispiel (PDO-Verbindung zu MySQL):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;amp;lt;?php&lt;br /&gt;
$host = &amp;#039;localhost&amp;#039;;&lt;br /&gt;
$dbname = &amp;#039;meine_datenbank&amp;#039;;&lt;br /&gt;
$username = &amp;#039;mein_benutzer&amp;#039;;&lt;br /&gt;
$password = &amp;#039;mein_passwort&amp;#039;;&lt;br /&gt;
$charset = &amp;#039;utf8mb4&amp;#039;;&lt;br /&gt;
&lt;br /&gt;
$dsn = &amp;quot;mysql:host=$host;dbname=$dbname;charset=$charset&amp;quot;;&lt;br /&gt;
$options =;&lt;br /&gt;
&lt;br /&gt;
try {&lt;br /&gt;
 $pdo = new PDO($dsn, $username, $password, $options);&lt;br /&gt;
 // Erfolgreich verbunden, $pdo-Objekt kann nun verwendet werden.&lt;br /&gt;
 // echo &amp;quot;Verbindung erfolgreich hergestellt!&amp;quot;;&lt;br /&gt;
} catch (\PDOException $e) {&lt;br /&gt;
 // Fehler beim Verbindungsaufbau&lt;br /&gt;
 // In Produktion: Fehler loggen, statt ausgeben&lt;br /&gt;
 error_log(&amp;quot;Datenbankverbindungsfehler: &amp;quot;. $e---&amp;amp;gt;getMessage());&lt;br /&gt;
 die(&amp;quot;Es gab ein Problem mit der Datenbankverbindung. Bitte versuchen Sie es später erneut.&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//... hier Datenbankoperationen mit $pdo durchführen...&lt;br /&gt;
&lt;br /&gt;
// Verbindung explizit schließen (optional, da PHP dies am Skriptende tut)&lt;br /&gt;
// $pdo = null;&lt;br /&gt;
?&amp;amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Dieses Beispiel zeigt den empfohlenen Weg, eine PDO-Verbindung zu MySQL herzustellen, inklusive wichtiger Optionen für Fehlerbehandlung und Sicherheit sowie einem &amp;lt;code&amp;gt;try...catch&amp;lt;/code&amp;gt;-Block zum Abfangen von Verbindungsfehlern.&lt;br /&gt;
&lt;br /&gt;
=== MySQLi (MySQL Improved) ===&lt;br /&gt;
&lt;br /&gt;
Die MySQLi-Erweiterung ist, wie der Name schon sagt, speziell für die Interaktion mit MySQL-Datenbanken konzipiert und bietet Unterstützung für Funktionen, die ab MySQL Version 4.1 eingeführt wurden.[14, 15] Sie ermöglicht den Zugriff auf MySQL-spezifische Features, die über PDO möglicherweise nicht direkt verfügbar sind.[16, 15]&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Duale Schnittstelle:&amp;#039;&amp;#039;&amp;#039; Ein charakteristisches Merkmal von MySQLi ist die Unterstützung von zwei Programmierstilen:&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Prozedural:&amp;#039;&amp;#039;&amp;#039; Dieser Stil ähnelt den alten &amp;lt;code&amp;gt;mysql_*&amp;lt;/code&amp;gt; Funktionen. Die Funktionsnamen beginnen typischerweise mit &amp;lt;code&amp;gt;mysqli_&amp;lt;/code&amp;gt; (z.B. &amp;lt;code&amp;gt;mysqli_connect()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;mysqli_query()&amp;lt;/code&amp;gt;). Der Datenbankverbindungs-Handle (eine Ressource oder ein Objekt) muss dabei explizit als erster Parameter an die meisten Funktionen übergeben werden. Dieser Stil wird oft von Entwicklern bevorzugt, die von der alten &amp;lt;code&amp;gt;mysql&amp;lt;/code&amp;gt;-Erweiterung migrieren.&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Objektorientiert (OO):&amp;#039;&amp;#039;&amp;#039; Hier wird ein Objekt der &amp;lt;code&amp;gt;mysqli&amp;lt;/code&amp;gt;-Klasse erstellt, und Datenbankoperationen werden als Methoden dieses Objekts aufgerufen (z.B. &amp;lt;code&amp;gt;$mysqli-&amp;amp;gt;query()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;$mysqli-&amp;amp;gt;prepare()&amp;lt;/code&amp;gt;). Dieser Ansatz passt oft besser zu modernen PHP-Entwicklungspraktiken und vermeidet die Notwendigkeit, den Verbindungshandle ständig zu übergeben. Die offizielle PHP-Dokumentation für MySQLi ist primär im objektorientierten Stil verfasst. Es gibt keine nennenswerten Performance-Unterschiede zwischen den beiden Stilen; die Wahl ist meist eine Frage der persönlichen Präferenz oder Projektkonventionen.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Verbindungsaufbau mit MySQLi:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Prozedural:&amp;#039;&amp;#039;&amp;#039; Die Funktion &amp;lt;code&amp;gt;mysqli_connect()&amp;lt;/code&amp;gt; wird verwendet: &amp;lt;code&amp;gt;$link = mysqli_connect($host, $user, $pass, $db, $port, $socket);&amp;lt;/code&amp;gt;. Sie gibt bei Erfolg ein &amp;lt;code&amp;gt;mysqli&amp;lt;/code&amp;gt;-Objekt (oder &amp;lt;code&amp;gt;false&amp;lt;/code&amp;gt; bei Fehlern vor PHP 8.1) zurück. Fehler können mit &amp;lt;code&amp;gt;mysqli_connect_errno()&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;mysqli_connect_error()&amp;lt;/code&amp;gt; geprüft werden.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Objektorientiert:&amp;#039;&amp;#039;&amp;#039; Ein neues &amp;lt;code&amp;gt;mysqli&amp;lt;/code&amp;gt;-Objekt wird instanziiert: &amp;lt;code&amp;gt;$mysqli = new mysqli($host, $user, $pass, $db, $port, $socket);&amp;lt;/code&amp;gt;. Wichtig: Der Konstruktor gibt &amp;#039;&amp;#039;immer&amp;#039;&amp;#039; ein Objekt zurück, auch wenn die Verbindung fehlschlägt. Der Verbindungsstatus muss explizit über die Eigenschaften &amp;lt;code&amp;gt;$mysqli-&amp;amp;gt;connect_errno&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;$mysqli-&amp;amp;gt;connect_error&amp;lt;/code&amp;gt; geprüft werden.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Fortgeschrittene Verbindung:&amp;#039;&amp;#039;&amp;#039; Für feinere Kontrolle über Verbindungsoptionen (z.B. Setzen von Timeouts oder Initialisierungsbefehlen &amp;#039;&amp;#039;vor&amp;#039;&amp;#039; dem Verbindungsaufbau) können &amp;lt;code&amp;gt;mysqli_init()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;mysqli_options()&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;mysqli_real_connect()&amp;lt;/code&amp;gt; verwendet werden.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Fehlerbehandlung:&amp;#039;&amp;#039;&amp;#039; Neben der manuellen Prüfung von Fehlercodes und -meldungen ist die empfohlene Methode, MySQLi so zu konfigurieren, dass es bei Fehlern Exceptions wirft. Dies geschieht mit &amp;lt;code&amp;gt;mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);&amp;lt;/code&amp;gt;. Danach können Datenbankfehler elegant mit &amp;lt;code&amp;gt;try...catch&amp;lt;/code&amp;gt;-Blöcken behandelt werden, ähnlich wie bei PDO.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Verbindung schließen:&amp;#039;&amp;#039;&amp;#039; Prozedural mit &amp;lt;code&amp;gt;mysqli_close($link)&amp;lt;/code&amp;gt;, objektorientiert mit &amp;lt;code&amp;gt;$mysqli-&amp;amp;gt;close()&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Persistente Verbindungen:&amp;#039;&amp;#039;&amp;#039; MySQLi unterstützt ebenfalls persistente Verbindungen, die über PHP-Konfigurationseinstellungen (&amp;lt;code&amp;gt;mysqli.allow_persistent&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;mysqli.max_persistent&amp;lt;/code&amp;gt;) gesteuert werden. Standardmäßig setzt MySQLi den Zustand einer wiederverwendeten persistenten Verbindung zurück (via &amp;lt;code&amp;gt;mysqli::change_user()&amp;lt;/code&amp;gt;), was zwar für Konsistenz sorgt, aber auch Performance kosten kann.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Code-Beispiele (MySQLi-Verbindungen):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Objektorientiert (mit Exception-Handling):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;amp;lt;?php&lt;br /&gt;
$host = &amp;#039;localhost&amp;#039;;&lt;br /&gt;
$user = &amp;#039;mein_benutzer&amp;#039;;&lt;br /&gt;
$pass = &amp;#039;mein_passwort&amp;#039;;&lt;br /&gt;
$db = &amp;#039;meine_datenbank&amp;#039;;&lt;br /&gt;
&lt;br /&gt;
// Fehlerberichterstattung für Exceptions aktivieren&lt;br /&gt;
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);&lt;br /&gt;
&lt;br /&gt;
try {&lt;br /&gt;
 $mysqli = new mysqli($host, $user, $pass, $db);&lt;br /&gt;
 // Zeichensatz setzen (wichtig!)&lt;br /&gt;
 $mysqli---&amp;amp;gt;set_charset(&amp;#039;utf8mb4&amp;#039;);&lt;br /&gt;
 // Erfolgreich verbunden&lt;br /&gt;
 // echo &amp;quot;Verbindung erfolgreich (OO)! Host-Info: &amp;quot;. $mysqli-&amp;amp;gt;host_info;&lt;br /&gt;
} catch (mysqli_sql_exception $e) {&lt;br /&gt;
 // Fehler beim Verbindungsaufbau oder set_charset&lt;br /&gt;
 error_log(&amp;quot;MySQLi Verbindungsfehler: &amp;quot;. $e-&amp;amp;gt;getMessage());&lt;br /&gt;
 die(&amp;quot;Es gab ein Problem mit der Datenbankverbindung.&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//... Datenbankoperationen mit $mysqli...&lt;br /&gt;
&lt;br /&gt;
// Verbindung schließen&lt;br /&gt;
// $mysqli-&amp;amp;gt;close();&lt;br /&gt;
?&amp;amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Prozedural (mit Exception-Handling):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!--?php$host = &amp;#039;localhost&amp;#039;;&lt;br /&gt;
$user = &amp;#039;mein_benutzer&amp;#039;;&lt;br /&gt;
$pass = &amp;#039;mein_passwort&amp;#039;;&lt;br /&gt;
$db = &amp;#039;meine_datenbank&amp;#039;;&lt;br /&gt;
&lt;br /&gt;
// Fehlerberichterstattung für Exceptions aktivieren&lt;br /&gt;
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);&lt;br /&gt;
&lt;br /&gt;
try {&lt;br /&gt;
    $link = mysqli_connect($host, $user, $pass, $db);&lt;br /&gt;
    // Zeichensatz setzen&lt;br /&gt;
    mysqli_set_charset($link, &amp;#039;utf8mb4&amp;#039;);&lt;br /&gt;
    // Erfolgreich verbunden&lt;br /&gt;
    // echo &amp;quot;Verbindung erfolgreich (Prozedural)! Host-Info: &amp;quot;. mysqli_get_host_info($link);&lt;br /&gt;
} catch (mysqli_sql_exception $e) {&lt;br /&gt;
    // Fehler beim Verbindungsaufbau oder set_charset&lt;br /&gt;
    error_log(&amp;quot;MySQLi Verbindungsfehler: &amp;quot;. $e---&amp;gt;getMessage());&lt;br /&gt;
 die(&amp;quot;Es gab ein Problem mit der Datenbankverbindung.&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//... Datenbankoperationen mit $link...&lt;br /&gt;
&lt;br /&gt;
// Verbindung schließen&lt;br /&gt;
// mysqli_close($link);&lt;br /&gt;
?&amp;amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Bedeutung von &amp;lt;code&amp;gt;mysqlnd&amp;lt;/code&amp;gt;:&amp;#039;&amp;#039;&amp;#039; Die Benutzerfreundlichkeit, insbesondere bei der Arbeit mit Prepared Statements, wird stark durch den verwendeten MySQL-Treiber beeinflusst. Der &amp;#039;&amp;#039;&amp;#039;MySQL Native Driver (&amp;lt;code&amp;gt;mysqlnd&amp;lt;/code&amp;gt;)&amp;#039;&amp;#039;&amp;#039;, der seit PHP 5.4 standardmäßig enthalten ist, ist hier entscheidend. &amp;lt;code&amp;gt;mysqlnd&amp;lt;/code&amp;gt; stellt die Methode &amp;lt;code&amp;gt;get_result()&amp;lt;/code&amp;gt; (bzw. die Funktion &amp;lt;code&amp;gt;mysqli_stmt_get_result()&amp;lt;/code&amp;gt;) zur Verfügung. Diese Methode erlaubt es, nach dem Ausführen eines Prepared Statements ein Standard-&amp;lt;code&amp;gt;mysqli_result&amp;lt;/code&amp;gt;-Objekt zu erhalten, von dem dann wie gewohnt mit &amp;lt;code&amp;gt;fetch_assoc()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;fetch_all()&amp;lt;/code&amp;gt; etc. die Ergebnisse abgerufen werden können. Ohne &amp;lt;code&amp;gt;mysqlnd&amp;lt;/code&amp;gt; (bei Verwendung der älteren &amp;lt;code&amp;gt;libmysqlclient&amp;lt;/code&amp;gt;-Bibliothek) ist das Abrufen von Ergebnissen aus Prepared Statements deutlich umständlicher und erfordert die manuelle Bindung jeder einzelnen Ergebnisspalte an PHP-Variablen mittels &amp;lt;code&amp;gt;bind_result()&amp;lt;/code&amp;gt; und anschließendes &amp;lt;code&amp;gt;fetch()&amp;lt;/code&amp;gt;. Da &amp;lt;code&amp;gt;mysqlnd&amp;lt;/code&amp;gt; heute der Standard ist, konzentrieren sich moderne Tutorials und Beispiele meist auf die &amp;lt;code&amp;gt;get_result()&amp;lt;/code&amp;gt;-Methode, aber es ist wichtig, diesen Hintergrund zu kennen, falls man auf ältere Systeme oder Konfigurationen trifft.&lt;br /&gt;
&lt;br /&gt;
=== Veraltete &amp;lt;code&amp;gt;mysql_*&amp;lt;/code&amp;gt; Funktionen ===&lt;br /&gt;
&lt;br /&gt;
Die &amp;lt;code&amp;gt;mysql_*&amp;lt;/code&amp;gt; Funktionsfamilie (&amp;lt;code&amp;gt;mysql_connect&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;mysql_query&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;mysql_fetch_assoc&amp;lt;/code&amp;gt; etc.) war die ursprüngliche Methode, um in PHP mit MySQL-Datenbanken zu interagieren. Diese Erweiterung ist jedoch &amp;#039;&amp;#039;&amp;#039;stark veraltet&amp;#039;&amp;#039;&amp;#039; und sollte &amp;#039;&amp;#039;&amp;#039;unter keinen Umständen mehr für neue Projekte verwendet werden&amp;#039;&amp;#039;&amp;#039;.&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Status:&amp;#039;&amp;#039;&amp;#039; Die &amp;lt;code&amp;gt;mysql_*&amp;lt;/code&amp;gt; Erweiterung wurde in &amp;#039;&amp;#039;&amp;#039;PHP 5.5&amp;#039;&amp;#039;&amp;#039; offiziell als &amp;quot;deprecated&amp;quot; (veraltet) markiert, was bedeutet, dass ihre Verwendung ab dieser Version Warnungen (&amp;lt;code&amp;gt;E_DEPRECATED&amp;lt;/code&amp;gt;) erzeugte. Mit der Veröffentlichung von PHP 7.0 wurde die Erweiterung vollständig entfernt. PHP-Code, der diese Funktionen nutzt, führt auf PHP 7.0 oder neuer zu fatalen Fehlern und funktioniert nicht mehr.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Gründe für die Entfernung:&amp;#039;&amp;#039;&amp;#039; Es gab zwingende Gründe für diesen Schritt:&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Gravierende Sicherheitsrisiken:&amp;#039;&amp;#039;&amp;#039; Der Hauptgrund war die Anfälligkeit für SQL-Injection-Angriffe. Die &amp;lt;code&amp;gt;mysql_*&amp;lt;/code&amp;gt; Funktionen boten keinen eingebauten Mechanismus wie Prepared Statements. Entwickler mussten Benutzereingaben manuell mit &amp;lt;code&amp;gt;mysql_real_escape_string()&amp;lt;/code&amp;gt; &amp;quot;escapen&amp;quot;, eine Methode, die leicht vergessen oder falsch angewendet werden konnte, was zu weit verbreiteten Sicherheitslücken führte. Das Fehlen von Prepared Statements in dieser alten Erweiterung ist direkt ursächlich für die hohe Zahl an SQL-Injection-Schwachstellen in älteren PHP-Anwendungen.&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Fehlende Unterstützung moderner MySQL-Features:&amp;#039;&amp;#039;&amp;#039; Die Erweiterung wurde ursprünglich für MySQL Version 3.23 entwickelt und seitdem kaum weiterentwickelt.[29] Sie unterstützte viele wichtige Funktionen moderner MySQL-Versionen nicht, darunter Prepared Statements, Stored Procedures, Transaktionen, SSL-Verschlüsselung, Kompression, Multi-Statements und vollständige Zeichensatzunterstützung.&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Schlechte Wartbarkeit:&amp;#039;&amp;#039;&amp;#039; Der Code der Erweiterung war veraltet und wurde immer schwieriger zu warten und an neue Versionen der MySQL-Client-Bibliotheken anzupassen.&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Förderung unsicherer Praktiken:&amp;#039;&amp;#039;&amp;#039; Da viele alte Tutorials und Codebeispiele die &amp;lt;code&amp;gt;mysql_*&amp;lt;/code&amp;gt; Funktionen unsicher verwendeten, trug die fortgesetzte Verfügbarkeit zur Verbreitung schlechter und unsicherer Programmierpraktiken bei.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Verwendung heute:&amp;#039;&amp;#039;&amp;#039; Die klare Antwort lautet: &amp;#039;&amp;#039;&amp;#039;Nein&amp;#039;&amp;#039;&amp;#039;. Diese Funktionen existieren in modernen PHP-Versionen nicht mehr. Jede Anwendung, die sie noch verwendet, ist nicht nur unsicher, sondern auch inkompatibel mit aktueller PHP-Software. Eine Migration zu PDO oder MySQLi ist unumgänglich, um Sicherheit und Kompatibilität zu gewährleisten. Die erzwungene Migration durch die Entfernung in PHP 7.0 unterstreicht, wie wichtig es ist, Deprecation-Warnungen ernst zu nehmen und Code proaktiv zu aktualisieren, um zukünftige Probleme zu vermeiden.&lt;br /&gt;
&lt;br /&gt;
=== Tabelle 1: Vergleich der PHP-Datenbankerweiterungen für MySQL ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Merkmal !! PDO (PHP Data Objects) !! MySQLi (MySQL Improved) !! &amp;lt;code&amp;gt;mysql_*&amp;lt;/code&amp;gt; (Veraltet)&lt;br /&gt;
|-&amp;lt;br&amp;gt;&lt;br /&gt;
| aktiv=&amp;quot;&amp;quot; empfohlen=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| aktiv=&amp;quot;&amp;quot; empfohlen=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Entfernt (seit PHP 7.0) &lt;br /&gt;
|-&amp;lt;br&amp;gt;&lt;br /&gt;
| objektorientiert=&amp;quot;&amp;quot; oo=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| oo=&amp;quot;&amp;quot; prozedural=&amp;quot;&amp;quot; 17=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Prozedural&lt;br /&gt;
|-&amp;lt;br&amp;gt;&lt;br /&gt;
| multi-datenbank=&amp;quot;&amp;quot; mysql=&amp;quot;&amp;quot; pgsql=&amp;quot;&amp;quot; sqlite=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| nur=&amp;quot;&amp;quot; mysql=&amp;quot;&amp;quot; 15=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Nur MySQL&lt;br /&gt;
|-&amp;lt;br&amp;gt;&lt;br /&gt;
| ja=&amp;quot;&amp;quot; nativ=&amp;quot;&amp;quot; empfohlen=&amp;quot;&amp;quot; emulation=&amp;quot;&amp;quot; m=&amp;quot;&amp;quot; glich=&amp;quot;&amp;quot; 1=&amp;quot;&amp;quot; 11=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| ja=&amp;quot;&amp;quot; nativ=&amp;quot;&amp;quot; 25=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Nein &lt;br /&gt;
|- &lt;br /&gt;
| &amp;#039;&amp;#039;&amp;#039;SQL Injection Schutz&amp;#039;&amp;#039;&amp;#039; || sehr=&amp;quot;&amp;quot; gut=&amp;quot;&amp;quot; durch=&amp;quot;&amp;quot; prepared=&amp;quot;&amp;quot; statements=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Sehr gut (durch Prepared Statements) || Schlecht (manuelles Escaping nötig) &lt;br /&gt;
|-&amp;lt;br&amp;gt;&lt;br /&gt;
| gute=&amp;quot;&amp;quot; unterst=&amp;quot;&amp;quot; tzung=&amp;quot;&amp;quot; via=&amp;quot;&amp;quot; treiber=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Sehr gute Unterstützung (spezifisch) || Veraltet, viele Features fehlen&lt;br /&gt;
|-&amp;lt;br&amp;gt;&lt;br /&gt;
| hoch=&amp;quot;&amp;quot; 2=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| niedrig=&amp;quot;&amp;quot; mysql-spezifisch=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Niedrig&lt;br /&gt;
|-&amp;lt;br&amp;gt;&lt;br /&gt;
| exceptions=&amp;quot;&amp;quot; mwt-preservehtml=&amp;quot;&amp;quot; pdoexception=&amp;quot;&amp;quot; code=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| exceptions=&amp;quot;&amp;quot; mit=&amp;quot;&amp;quot; mwt-preservehtml=&amp;quot;&amp;quot; mysqli_report=&amp;quot;&amp;quot; code=&amp;quot;&amp;quot; fehlercodes=&amp;quot;&amp;quot; 18=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Fehlercodes / &amp;lt;code&amp;gt;mysql_error()&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
| &amp;#039;&amp;#039;&amp;#039;Nicht verwenden!&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Grundlegende Datenbankoperationen (CRUD) ==&lt;br /&gt;
&lt;br /&gt;
Nachdem wir die verschiedenen Erweiterungen kennengelernt haben, wenden wir uns nun den grundlegenden Operationen zu, die man typischerweise mit einer Datenbank durchführt. Diese werden oft mit dem Akronym CRUD zusammengefasst:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;C&amp;#039;&amp;#039;&amp;#039;reate: Neue Datensätze erstellen (SQL: &amp;lt;code&amp;gt;INSERT&amp;lt;/code&amp;gt;).&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;R&amp;#039;&amp;#039;&amp;#039;ead: Vorhandene Datensätze lesen/abfragen (SQL: &amp;lt;code&amp;gt;SELECT&amp;lt;/code&amp;gt;).&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;U&amp;#039;&amp;#039;&amp;#039;pdate: Bestehende Datensätze ändern (SQL: &amp;lt;code&amp;gt;UPDATE&amp;lt;/code&amp;gt;).&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;D&amp;#039;&amp;#039;&amp;#039;elete: Datensätze löschen (SQL: &amp;lt;code&amp;gt;DELETE&amp;lt;/code&amp;gt;).&lt;br /&gt;
In den folgenden Abschnitten werden wir sehen, wie diese Operationen mit den modernen Erweiterungen PDO und MySQLi umgesetzt werden, wobei wir durchgehend &amp;#039;&amp;#039;&amp;#039;Prepared Statements&amp;#039;&amp;#039;&amp;#039; verwenden, um die Sicherheit zu gewährleisten.&lt;br /&gt;
&lt;br /&gt;
=== Daten abfragen (SELECT) ===&lt;br /&gt;
&lt;br /&gt;
Das Abfragen von Daten ist eine der häufigsten Datenbankoperationen.&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;PDO:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Ohne Parameter:&amp;#039;&amp;#039;&amp;#039; Wenn die Abfrage keine variablen Teile enthält (z.B. keine &amp;lt;code&amp;gt;WHERE&amp;lt;/code&amp;gt;-Klausel mit Benutzereingaben), kann die &amp;lt;code&amp;gt;query()&amp;lt;/code&amp;gt;-Methode verwendet werden. Sie führt die SQL-Abfrage direkt aus und gibt ein &amp;lt;code&amp;gt;PDOStatement&amp;lt;/code&amp;gt;-Objekt zurück, das zur Ergebnisauswertung dient.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Beispiel: Alle Benutzer auswählen&lt;br /&gt;
$stmt = $pdo-&amp;amp;gt;query(&amp;quot;SELECT id, username FROM users&amp;quot;);&lt;br /&gt;
// $stmt kann nun zum Fetchen der Ergebnisse verwendet werden&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Mit Parametern (Prepared Statement):&amp;#039;&amp;#039;&amp;#039; Dies ist der Standardfall, wenn Benutzereingaben oder andere Variablen die Abfrage beeinflussen (z.B. in &amp;lt;code&amp;gt;WHERE&amp;lt;/code&amp;gt;- oder &amp;lt;code&amp;gt;LIMIT&amp;lt;/code&amp;gt;-Klauseln).&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Vorbereiten (&amp;lt;code&amp;gt;prepare&amp;lt;/code&amp;gt;):&amp;#039;&amp;#039;&amp;#039; Die SQL-Abfrage wird mit Platzhaltern (&amp;lt;code&amp;gt;?&amp;lt;/code&amp;gt; für positionale oder &amp;lt;code&amp;gt;:name&amp;lt;/code&amp;gt; für benannte Platzhalter) vorbereitet.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Beispiel: Benutzer mit bestimmter ID auswählen (positionaler Platzhalter)&lt;br /&gt;
$sql = &amp;quot;SELECT id, username, email FROM users WHERE id =?&amp;quot;;&lt;br /&gt;
$stmt = $pdo-&amp;amp;gt;prepare($sql);&lt;br /&gt;
&lt;br /&gt;
// Beispiel: Benutzer mit bestimmtem Status auswählen (benannter Platzhalter)&lt;br /&gt;
$sql_named = &amp;quot;SELECT id, username FROM users WHERE status = :status&amp;quot;;&lt;br /&gt;
$stmt_named = $pdo-&amp;amp;gt;prepare($sql_named);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
*# &amp;#039;&amp;#039;&amp;#039;Ausführen (&amp;lt;code&amp;gt;execute&amp;lt;/code&amp;gt;):&amp;#039;&amp;#039;&amp;#039; Die vorbereitete Anweisung wird mit den tatsächlichen Werten ausgeführt. Die Werte werden als Array an die &amp;lt;code&amp;gt;execute()&amp;lt;/code&amp;gt;-Methode übergeben. Bei positionalen Platzhaltern entspricht die Reihenfolge im Array der Reihenfolge der &amp;lt;code&amp;gt;?&amp;lt;/code&amp;gt; in der SQL-Abfrage. Bei benannten Platzhaltern wird ein assoziatives Array verwendet, dessen Schlüssel den Platzhalternamen entsprechen (mit oder ohne führenden Doppelpunkt).&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Ausführen für positionalen Platzhalter&lt;br /&gt;
$user_id = 123;&lt;br /&gt;
$stmt-&amp;amp;gt;execute([$user_id]);&lt;br /&gt;
&lt;br /&gt;
// Ausführen für benannten Platzhalter&lt;br /&gt;
$user_status = &amp;#039;active&amp;#039;;&lt;br /&gt;
$stmt_named-&amp;amp;gt;execute([&amp;#039;:status&amp;#039; =&amp;amp;gt; $user_status]);&lt;br /&gt;
// oder: $stmt_named-&amp;amp;gt;execute([&amp;#039;status&amp;#039; =&amp;amp;gt; $user_status]);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
*# &amp;#039;&amp;#039;&amp;#039;Alternative Bindung:&amp;#039;&amp;#039;&amp;#039; Optional können Parameter auch explizit vor dem &amp;lt;code&amp;gt;execute()&amp;lt;/code&amp;gt;-Aufruf mit &amp;lt;code&amp;gt;bindValue()&amp;lt;/code&amp;gt; (bindet einen Wert) oder &amp;lt;code&amp;gt;bindParam()&amp;lt;/code&amp;gt; (bindet eine Variable als Referenz) gebunden werden. Dies gibt mehr Kontrolle über den Datentyp (z.B. &amp;lt;code&amp;gt;PDO::PARAM_INT&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;PDO::PARAM_STR&amp;lt;/code&amp;gt;).&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$stmt-&amp;amp;gt;bindValue(1, $user_id, PDO::PARAM_INT); // Position 1, Wert von $user_id als Integer&lt;br /&gt;
$stmt-&amp;amp;gt;execute();&lt;br /&gt;
&lt;br /&gt;
$stmt_named-&amp;amp;gt;bindParam(&amp;#039;:status&amp;#039;, $user_status, PDO::PARAM_STR); // Platzhalter :status, Variable $user_status als String&lt;br /&gt;
$stmt_named-&amp;amp;gt;execute();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;MySQLi:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Ohne Parameter:&amp;#039;&amp;#039;&amp;#039; Ähnlich wie bei PDO kann bei Abfragen ohne variable Teile die &amp;lt;code&amp;gt;query()&amp;lt;/code&amp;gt;-Methode (OO-Stil: &amp;lt;code&amp;gt;$mysqli-&amp;amp;gt;query()&amp;lt;/code&amp;gt;) oder die &amp;lt;code&amp;gt;mysqli_query()&amp;lt;/code&amp;gt;-Funktion (prozeduraler Stil: &amp;lt;code&amp;gt;mysqli_query($link, &amp;quot;...&amp;quot;)&amp;lt;/code&amp;gt;) verwendet werden. Sie geben ein &amp;lt;code&amp;gt;mysqli_result&amp;lt;/code&amp;gt;-Objekt zurück.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// OO-Stil&lt;br /&gt;
$result = $mysqli-&amp;amp;gt;query(&amp;quot;SELECT id, username FROM users&amp;quot;);&lt;br /&gt;
// Prozedural&lt;br /&gt;
$result = mysqli_query($link, &amp;quot;SELECT id, username FROM users&amp;quot;);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Mit Parametern (Prepared Statement):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Vorbereiten (&amp;lt;code&amp;gt;prepare&amp;lt;/code&amp;gt;):&amp;#039;&amp;#039;&amp;#039; Die SQL-Abfrage wird mit &amp;lt;code&amp;gt;?&amp;lt;/code&amp;gt;-Platzhaltern vorbereitet. Im OO-Stil &amp;lt;code&amp;gt;$stmt = $mysqli-&amp;amp;gt;prepare(&amp;quot;...&amp;quot;)&amp;lt;/code&amp;gt;, prozedural &amp;lt;code&amp;gt;$stmt = mysqli_prepare($link, &amp;quot;...&amp;quot;)&amp;lt;/code&amp;gt;.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// OO-Stil&lt;br /&gt;
$sql = &amp;quot;SELECT id, username, email FROM users WHERE id =?&amp;quot;;&lt;br /&gt;
$stmt = $mysqli-&amp;amp;gt;prepare($sql);&lt;br /&gt;
// Prozedural&lt;br /&gt;
$sql_proc = &amp;quot;SELECT id, username, email FROM users WHERE id =?&amp;quot;;&lt;br /&gt;
$stmt_proc = mysqli_prepare($link, $sql_proc);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
**# &amp;#039;&amp;#039;&amp;#039;Parameter binden (&amp;lt;code&amp;gt;bind_param&amp;lt;/code&amp;gt;):&amp;#039;&amp;#039;&amp;#039; Die Variablen werden an die Platzhalter gebunden. Dies ist ein wesentlicher Unterschied zu PDOs &amp;lt;code&amp;gt;execute&amp;lt;/code&amp;gt;-Array-Bindung. &amp;lt;code&amp;gt;bind_param()&amp;lt;/code&amp;gt; erfordert einen String, der die Datentypen der zu bindenden Variablen angibt (z.B. &amp;quot;i&amp;quot; für Integer, &amp;quot;s&amp;quot; für String, &amp;quot;d&amp;quot; für Double/Float, &amp;quot;b&amp;quot; für Blob), gefolgt von den Variablen selbst.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$user_id = 123;&lt;br /&gt;
// OO-Stil&lt;br /&gt;
$stmt-&amp;amp;gt;bind_param(&amp;quot;i&amp;quot;, $user_id); // &amp;quot;i&amp;quot; für Integer&lt;br /&gt;
// Prozedural&lt;br /&gt;
mysqli_stmt_bind_param($stmt_proc, &amp;quot;i&amp;quot;, $user_id);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
**# &amp;#039;&amp;#039;&amp;#039;Ausführen (&amp;lt;code&amp;gt;execute&amp;lt;/code&amp;gt;):&amp;#039;&amp;#039;&amp;#039; Die vorbereitete Anweisung wird ausgeführt. OO-Stil: &amp;lt;code&amp;gt;$stmt-&amp;amp;gt;execute()&amp;lt;/code&amp;gt;, prozedural: &amp;lt;code&amp;gt;mysqli_stmt_execute($stmt)&amp;lt;/code&amp;gt;.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// OO-Stil&lt;br /&gt;
$stmt-&amp;amp;gt;execute();&lt;br /&gt;
// Prozedural&lt;br /&gt;
mysqli_stmt_execute($stmt_proc);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
**# &amp;#039;&amp;#039;&amp;#039;Ergebnis holen:&amp;#039;&amp;#039;&amp;#039; Um die Ergebnisse eines Prepared Statements in MySQLi zu erhalten, ist die Methode &amp;lt;code&amp;gt;get_result()&amp;lt;/code&amp;gt; (OO) bzw. &amp;lt;code&amp;gt;mysqli_stmt_get_result()&amp;lt;/code&amp;gt; (prozedural) am bequemsten. Sie gibt ein Standard-&amp;lt;code&amp;gt;mysqli_result&amp;lt;/code&amp;gt;-Objekt zurück, das dann mit den üblichen Fetch-Methoden verarbeitet werden kann. &amp;#039;&amp;#039;&amp;#039;Wichtig:&amp;#039;&amp;#039;&amp;#039; Diese Methode erfordert den &amp;lt;code&amp;gt;mysqlnd&amp;lt;/code&amp;gt;-Treiber.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// OO-Stil&lt;br /&gt;
$result = $stmt-&amp;amp;gt;get_result();&lt;br /&gt;
// Prozedural&lt;br /&gt;
$result_proc = mysqli_stmt_get_result($stmt_proc);&lt;br /&gt;
// $result / $result_proc kann nun mit fetch_assoc() etc. verwendet werden.&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Die Alternative ohne &amp;lt;code&amp;gt;mysqlnd&amp;lt;/code&amp;gt; ist &amp;lt;code&amp;gt;bind_result()&amp;lt;/code&amp;gt;, bei der jede Ergebnisspalte an eine PHP-Variable gebunden wird, und &amp;lt;code&amp;gt;fetch()&amp;lt;/code&amp;gt; iterativ aufgerufen wird, um die Variablen zu füllen.[25] Dies ist deutlich umständlicher.&lt;br /&gt;
&lt;br /&gt;
=== Ergebnisse abrufen und verarbeiten ===&lt;br /&gt;
&lt;br /&gt;
Nachdem eine &amp;lt;code&amp;gt;SELECT&amp;lt;/code&amp;gt;-Abfrage erfolgreich ausgeführt wurde, müssen die zurückgegebenen Daten aus dem Ergebnisobjekt (&amp;lt;code&amp;gt;PDOStatement&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;mysqli_result&amp;lt;/code&amp;gt;) abgerufen (&amp;quot;gefetched&amp;quot;) werden.&lt;br /&gt;
&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Zeilenweise Iteration (&amp;lt;code&amp;gt;fetch&amp;lt;/code&amp;gt; im Loop):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
Dies ist der klassische Weg, um Ergebnisse zu verarbeiten, insbesondere wenn jede Zeile einzeln behandelt werden muss oder wenn die Ergebnismenge sehr groß ist.&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;PDO:&amp;#039;&amp;#039;&amp;#039; Die &amp;lt;code&amp;gt;fetch()&amp;lt;/code&amp;gt;-Methode des &amp;lt;code&amp;gt;PDOStatement&amp;lt;/code&amp;gt;-Objekts wird wiederholt in einer &amp;lt;code&amp;gt;while&amp;lt;/code&amp;gt;-Schleife aufgerufen. &amp;lt;code&amp;gt;fetch()&amp;lt;/code&amp;gt; gibt bei jedem Aufruf die nächste Zeile zurück (im Format des eingestellten Fetch-Modus, z.B. &amp;lt;code&amp;gt;PDO::FETCH_ASSOC&amp;lt;/code&amp;gt;) und &amp;lt;code&amp;gt;false&amp;lt;/code&amp;gt; (oder &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt; je nach Modus/Version), wenn keine weiteren Zeilen vorhanden sind.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$stmt = $pdo-&amp;amp;gt;prepare(&amp;quot;SELECT name, email FROM users WHERE status =?&amp;quot;);&lt;br /&gt;
$stmt-&amp;amp;gt;execute([&amp;#039;active&amp;#039;]);&lt;br /&gt;
while ($row = $stmt-&amp;amp;gt;fetch(PDO::FETCH_ASSOC)) {&lt;br /&gt;
 echo &amp;quot;Name: &amp;quot;. htmlspecialchars($row[&amp;#039;name&amp;#039;]). &amp;quot;, E-Mail: &amp;quot;. htmlspecialchars($row[&amp;#039;email&amp;#039;]). &amp;quot;&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;MySQLi:&amp;#039;&amp;#039;&amp;#039; Die &amp;lt;code&amp;gt;fetch_assoc()&amp;lt;/code&amp;gt;-Methode (oder &amp;lt;code&amp;gt;fetch_row()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;fetch_object()&amp;lt;/code&amp;gt;, etc.) des &amp;lt;code&amp;gt;mysqli_result&amp;lt;/code&amp;gt;-Objekts wird in einer &amp;lt;code&amp;gt;while&amp;lt;/code&amp;gt;-Schleife verwendet. Sie gibt die nächste Zeile als Array (oder Objekt) zurück oder &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt;, wenn das Ende erreicht ist.[25, 15]&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// OO-Stil (nach $result = $stmt-&amp;amp;gt;get_result();)&lt;br /&gt;
while ($row = $result-&amp;amp;gt;fetch_assoc()) {&lt;br /&gt;
 echo &amp;quot;Name: &amp;quot;. htmlspecialchars($row[&amp;#039;name&amp;#039;]). &amp;quot;, E-Mail: &amp;quot;. htmlspecialchars($row[&amp;#039;email&amp;#039;]). &amp;quot;&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Prozedural (nach $result = mysqli_stmt_get_result($stmt);)&lt;br /&gt;
while ($row = mysqli_fetch_assoc($result)) {&lt;br /&gt;
 echo &amp;quot;Name: &amp;quot;. htmlspecialchars($row[&amp;#039;name&amp;#039;]). &amp;quot;, E-Mail: &amp;quot;. htmlspecialchars($row[&amp;#039;email&amp;#039;]). &amp;quot;&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Dieser Ansatz ist &amp;#039;&amp;#039;&amp;#039;speichereffizient&amp;#039;&amp;#039;&amp;#039;, da immer nur eine Zeile gleichzeitig im PHP-Speicher gehalten wird. Dies ist besonders wichtig bei Abfragen, die potenziell Tausende oder Millionen von Zeilen zurückgeben könnten.&lt;br /&gt;
&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Alle Ergebnisse auf einmal abrufen (&amp;lt;code&amp;gt;fetchAll&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;fetch_all&amp;lt;/code&amp;gt;):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
Für kleinere bis mittlere Ergebnismengen ist es oft bequemer, alle Zeilen auf einmal in ein PHP-Array zu laden.&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;PDO:&amp;#039;&amp;#039;&amp;#039; Die &amp;lt;code&amp;gt;fetchAll()&amp;lt;/code&amp;gt;-Methode des &amp;lt;code&amp;gt;PDOStatement&amp;lt;/code&amp;gt;-Objekts gibt ein Array zurück, das alle Ergebniszeilen enthält. Der Fetch-Modus (z.B. &amp;lt;code&amp;gt;PDO::FETCH_ASSOC&amp;lt;/code&amp;gt;) kann als Argument übergeben werden.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$stmt = $pdo-&amp;amp;gt;prepare(&amp;quot;SELECT name, email FROM users WHERE status =?&amp;quot;);&lt;br /&gt;
$stmt-&amp;amp;gt;execute([&amp;#039;active&amp;#039;]);&lt;br /&gt;
$results = $stmt-&amp;amp;gt;fetchAll(PDO::FETCH_ASSOC);&lt;br /&gt;
// $results ist nun ein Array von assoziativen Arrays&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;MySQLi:&amp;#039;&amp;#039;&amp;#039; Die &amp;lt;code&amp;gt;fetch_all()&amp;lt;/code&amp;gt;-Methode des &amp;lt;code&amp;gt;mysqli_result&amp;lt;/code&amp;gt;-Objekts (verfügbar mit &amp;lt;code&amp;gt;mysqlnd&amp;lt;/code&amp;gt;) tut dasselbe. Der Modus (&amp;lt;code&amp;gt;MYSQLI_ASSOC&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;MYSQLI_NUM&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;MYSQLI_BOTH&amp;lt;/code&amp;gt;) wird als Argument übergeben.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// OO-Stil (nach $result = $stmt-&amp;amp;gt;get_result();)&lt;br /&gt;
$results = $result-&amp;amp;gt;fetch_all(MYSQLI_ASSOC);&lt;br /&gt;
&lt;br /&gt;
// Prozedural (nach $result = mysqli_stmt_get_result($stmt);)&lt;br /&gt;
$results = mysqli_fetch_all($result, MYSQLI_ASSOC);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Der &amp;#039;&amp;#039;&amp;#039;Vorteil&amp;#039;&amp;#039;&amp;#039; dieser Methode liegt in der einfacheren Weiterverarbeitung des Ergebnis-Arrays mit Standard-PHP-Array-Funktionen wie &amp;lt;code&amp;gt;foreach&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;count&amp;lt;/code&amp;gt;. Der &amp;#039;&amp;#039;&amp;#039;Nachteil&amp;#039;&amp;#039;&amp;#039; ist der potenziell hohe Speicherverbrauch, da alle Daten gleichzeitig in den PHP-Speicher geladen werden. Bei sehr großen Ergebnismengen kann dies zu Speicherlimit-Fehlern führen.&lt;br /&gt;
&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Fetch-Modi / Fetch-Funktionen (Struktur der Ergebniszeile):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
Beide Erweiterungen bieten verschiedene Möglichkeiten, die Struktur der abgerufenen Datenzeilen zu bestimmen:&lt;br /&gt;
&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;PDO Fetch-Modi (Auswahl):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
***** &amp;lt;code&amp;gt;PDO::FETCH_ASSOC&amp;lt;/code&amp;gt;: Gibt ein assoziatives Array zurück (&amp;lt;code&amp;gt;[&amp;#039;spaltenname&amp;#039; =&amp;amp;gt; &amp;#039;wert&amp;#039;,... ]&amp;lt;/code&amp;gt;).[9, 33, 41, 42] Sehr gebräuchlich.&lt;br /&gt;
***** &amp;lt;code&amp;gt;PDO::FETCH_NUM&amp;lt;/code&amp;gt;: Gibt ein numerisch indiziertes Array zurück (&amp;lt;code&amp;gt;[0 =&amp;amp;gt; &amp;#039;wert1&amp;#039;, 1 =&amp;amp;gt; &amp;#039;wert2&amp;#039;,...]&amp;lt;/code&amp;gt;).&lt;br /&gt;
***** &amp;lt;code&amp;gt;PDO::FETCH_BOTH&amp;lt;/code&amp;gt; (Standard): Gibt ein Array zurück, das sowohl assoziative als auch numerische Indizes enthält. Verbraucht mehr Speicher.&lt;br /&gt;
***** &amp;lt;code&amp;gt;PDO::FETCH_OBJ&amp;lt;/code&amp;gt;: Gibt ein anonymes &amp;lt;code&amp;gt;stdClass&amp;lt;/code&amp;gt;-Objekt zurück, bei dem die Spaltennamen Eigenschaften sind (&amp;lt;code&amp;gt;$row-&amp;amp;gt;spaltenname&amp;lt;/code&amp;gt;).[9, 41, 42, 43]&lt;br /&gt;
***** &amp;lt;code&amp;gt;PDO::FETCH_CLASS&amp;lt;/code&amp;gt;: Erstellt eine Instanz einer angegebenen Klasse und weist die Spaltenwerte den Klasseneigenschaften zu.&lt;br /&gt;
***** &amp;lt;code&amp;gt;PDO::FETCH_COLUMN&amp;lt;/code&amp;gt;: Ruft nur den Wert einer einzelnen Spalte aus der nächsten Zeile ab.&lt;br /&gt;
***** &amp;lt;code&amp;gt;PDO::FETCH_LAZY&amp;lt;/code&amp;gt;: Eine Kombination aus &amp;lt;code&amp;gt;FETCH_BOTH&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;FETCH_OBJ&amp;lt;/code&amp;gt;, die Werte erst bei Zugriff lädt.&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;MySQLi Fetch-Funktionen (Auswahl):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
***** &amp;lt;code&amp;gt;fetch_assoc()&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;mysqli_fetch_assoc()&amp;lt;/code&amp;gt;: Gibt ein assoziatives Array zurück. Sehr gebräuchlich.&lt;br /&gt;
***** &amp;lt;code&amp;gt;fetch_row()&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;mysqli_fetch_row()&amp;lt;/code&amp;gt;: Gibt ein numerisch indiziertes Array zurück.&lt;br /&gt;
***** &amp;lt;code&amp;gt;fetch_array()&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;mysqli_fetch_array()&amp;lt;/code&amp;gt;: Gibt je nach übergebenem Modus (&amp;lt;code&amp;gt;MYSQLI_ASSOC&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;MYSQLI_NUM&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;MYSQLI_BOTH&amp;lt;/code&amp;gt;) ein assoziatives, numerisches oder beides enthaltendes Array zurück.&lt;br /&gt;
***** &amp;lt;code&amp;gt;fetch_object()&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;mysqli_fetch_object()&amp;lt;/code&amp;gt;: Gibt ein &amp;lt;code&amp;gt;stdClass&amp;lt;/code&amp;gt;-Objekt zurück oder eine Instanz einer angegebenen Klasse.[21, 15, 35]&lt;br /&gt;
***** &amp;lt;code&amp;gt;fetch_column()&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;mysqli_fetch_column()&amp;lt;/code&amp;gt;: Ruft den Wert einer einzelnen Spalte ab (erst ab PHP 8.1 verfügbar).&lt;br /&gt;
***** &amp;lt;code&amp;gt;fetch_all()&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;mysqli_fetch_all()&amp;lt;/code&amp;gt;: Ruft alle Zeilen auf einmal ab, Modus (&amp;lt;code&amp;gt;MYSQLI_ASSOC&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;MYSQLI_NUM&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;MYSQLI_BOTH&amp;lt;/code&amp;gt;) wählbar.&lt;br /&gt;
&lt;br /&gt;
=== Tabelle 2: Ausgewählte PDO Fetch-Modi ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Konstante !! Rückgabeformat !! Beschreibung&lt;br /&gt;
|- &lt;br /&gt;
| &amp;lt;code&amp;gt;PDO::FETCH_ASSOC&amp;lt;/code&amp;gt;&lt;br /&gt;
| assoziatives=&amp;quot;&amp;quot; array=&amp;quot;&amp;quot; mwt-preservehtml=&amp;quot;&amp;quot; | [&amp;quot; col&amp;#039;=&amp;quot;&amp;amp;gt;&amp;quot; &amp;#039;val&amp;#039;]&amp;amp;lt;=&amp;quot;&amp;quot; code&amp;amp;gt;&amp;#039;)=&amp;quot;&amp;quot; &amp;amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
| Gängigste Form, Spaltennamen als Schlüssel.&lt;br /&gt;
|- &lt;br /&gt;
| mwt-preservehtml=&amp;quot;&amp;quot; pdo::fetch_num=&amp;quot;&amp;quot; code=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| numerisches=&amp;quot;&amp;quot; array=&amp;quot;&amp;quot; mwt-preservehtml=&amp;quot;&amp;quot; 0=&amp;quot;&amp;quot; val=&amp;quot;&amp;quot; code=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Spaltenwerte über numerischen Index erreichbar.&lt;br /&gt;
|- &lt;br /&gt;
| mwt-preservehtml=&amp;quot;&amp;quot; pdo::fetch_both=&amp;quot;&amp;quot; code=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| array=&amp;quot;&amp;quot; mit=&amp;quot;&amp;quot; assoz=&amp;quot;&amp;quot; num=&amp;quot;&amp;quot; indizes=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Standard, enthält doppelte Informationen, höherer Speicherbedarf.&lt;br /&gt;
|- &lt;br /&gt;
| mwt-preservehtml=&amp;quot;&amp;quot; pdo::fetch_obj=&amp;quot;&amp;quot; code=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| anonymes=&amp;quot;&amp;quot; mwt-preservehtml=&amp;quot;&amp;quot; stdclass=&amp;quot;&amp;quot; code=&amp;quot;&amp;quot; -objekt=&amp;quot;&amp;quot; row-=&amp;quot;&amp;quot; col=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Spaltennamen werden zu Objekteigenschaften.&lt;br /&gt;
|- &lt;br /&gt;
| &amp;lt;code&amp;gt;PDO::FETCH_CLASS&amp;lt;/code&amp;gt; || objekt=&amp;quot;&amp;quot; einer=&amp;quot;&amp;quot; spezifischen=&amp;quot;&amp;quot; klasse=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Mappt Spalten auf Eigenschaften einer vordefinierten Klasse.&lt;br /&gt;
|- &lt;br /&gt;
| &amp;lt;code&amp;gt;PDO::FETCH_COLUMN&amp;lt;/code&amp;gt; || einzelner=&amp;quot;&amp;quot; wert=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Gibt nur den Wert der ersten (oder angegebenen) Spalte der nächsten Zeile zurück.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Tabelle 3: Ausgewählte MySQLi Fetch-Funktionen ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Funktion (OO / Prozedural) !! Rückgabeformat !! Beschreibung&lt;br /&gt;
|- &lt;br /&gt;
| mwt-preservehtml=&amp;quot;&amp;quot; fetch_assoc=&amp;quot;&amp;quot; code=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| assoziatives=&amp;quot;&amp;quot; array=&amp;quot;&amp;quot; mwt-preservehtml=&amp;quot;&amp;quot; col=&amp;quot;&amp;quot; val=&amp;quot;&amp;quot; code=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Gängigste Form, Spaltennamen als Schlüssel.&lt;br /&gt;
|- &lt;br /&gt;
| mwt-preservehtml=&amp;quot;&amp;quot; fetch_row=&amp;quot;&amp;quot; code=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| numerisches=&amp;quot;&amp;quot; array=&amp;quot;&amp;quot; mwt-preservehtml=&amp;quot;&amp;quot; 0=&amp;quot;&amp;quot; val=&amp;quot;&amp;quot; code=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Spaltenwerte über numerischen Index erreichbar.&lt;br /&gt;
|- &lt;br /&gt;
| mwt-preservehtml=&amp;quot;&amp;quot; fetch_array=&amp;quot;&amp;quot; code=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| array=&amp;quot;&amp;quot; assoz=&amp;quot;&amp;quot; num=&amp;quot;&amp;quot; oder=&amp;quot;&amp;quot; beide=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Flexibel je nach Modus (&amp;lt;code&amp;gt;MYSQLI_ASSOC&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;MYSQLI_NUM&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;MYSQLI_BOTH&amp;lt;/code&amp;gt;).&lt;br /&gt;
|- &lt;br /&gt;
| &amp;lt;code&amp;gt;fetch_object()&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;mysqli_fetch_object()&amp;lt;/code&amp;gt;&lt;br /&gt;
| mwt-preservehtml=&amp;quot;&amp;quot; | stdClass&amp;quot; -objekt=&amp;quot;&amp;quot; oder=&amp;quot;&amp;quot; spezifische=&amp;quot;&amp;quot; klasse=&amp;quot;&amp;quot; &amp;amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
| Spaltennamen werden zu Objekteigenschaften.&lt;br /&gt;
|- &lt;br /&gt;
| mwt-preservehtml=&amp;quot;&amp;quot; fetch_all=&amp;quot;&amp;quot; code=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| array=&amp;quot;&amp;quot; von=&amp;quot;&amp;quot; arrays=&amp;quot;&amp;quot; assoz=&amp;quot;&amp;quot; num=&amp;quot;&amp;quot; oder=&amp;quot;&amp;quot; beide=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Holt alle Ergebniszeilen auf einmal (benötigt &amp;lt;code&amp;gt;mysqlnd&amp;lt;/code&amp;gt;).&lt;br /&gt;
|- &lt;br /&gt;
| &amp;lt;code&amp;gt;fetch_column()&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;mysqli_fetch_column()&amp;lt;/code&amp;gt; || einzelner=&amp;quot;&amp;quot; wert=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Gibt nur den Wert der ersten (oder angegebenen) Spalte zurück (PHP 8.1+).&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Zugriff auf Spaltenwerte:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
Der Zugriff auf die Daten innerhalb einer abgerufenen Zeile hängt vom gewählten Fetch-Modus ab:&lt;br /&gt;
**** Assoziatives Array: &amp;lt;code&amp;gt;$row[&amp;#039;spaltenname&amp;#039;]&amp;lt;/code&amp;gt;&lt;br /&gt;
**** Numerisches Array: &amp;lt;code&amp;gt;$row&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;$row[1]&amp;lt;/code&amp;gt;,...&lt;br /&gt;
**** Objekt: &amp;lt;code&amp;gt;$row-&amp;amp;gt;spaltenname&amp;lt;/code&amp;gt;&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Iteration mit &amp;lt;code&amp;gt;foreach&amp;lt;/code&amp;gt;:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
Nachdem Ergebnisse mit &amp;lt;code&amp;gt;fetchAll()&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;fetch_all()&amp;lt;/code&amp;gt; in ein Array geladen wurden, ist &amp;lt;code&amp;gt;foreach&amp;lt;/code&amp;gt; die natürliche Wahl zur Iteration [37, 40, 44]:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$results = $stmt-&amp;amp;gt;fetchAll(PDO::FETCH_ASSOC);&lt;br /&gt;
foreach ($results as $row) {&lt;br /&gt;
 echo &amp;quot;Name: &amp;quot;. htmlspecialchars($row[&amp;#039;name&amp;#039;]). &amp;quot;&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Seit PHP 8 bietet PDO eine modernere Alternative, die die Lesbarkeit von &amp;lt;code&amp;gt;foreach&amp;lt;/code&amp;gt; mit der Speichereffizienz von &amp;lt;code&amp;gt;fetch()&amp;lt;/code&amp;gt; kombiniert, indem direkt über das &amp;lt;code&amp;gt;PDOStatement&amp;lt;/code&amp;gt;-Objekt iteriert wird:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Nur PHP 8+ mit PDO&lt;br /&gt;
$stmt = $pdo-&amp;amp;gt;query(&amp;quot;SELECT name, email FROM users&amp;quot;);&lt;br /&gt;
foreach ($stmt as $row) { // PDOStatement ist direkt traversierbar&lt;br /&gt;
 echo &amp;quot;Name: &amp;quot;. htmlspecialchars($row[&amp;#039;name&amp;#039;]). &amp;quot;&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Diese Methode ist besonders elegant und effizient für große Ergebnismengen in modernen PHP-Versionen.&lt;br /&gt;
&lt;br /&gt;
=== Daten manipulieren (INSERT, UPDATE, DELETE) ===&lt;br /&gt;
&lt;br /&gt;
Das Einfügen, Aktualisieren und Löschen von Daten sind kritische Operationen, da sie den Datenbestand verändern. Hier ist die Verwendung von &amp;#039;&amp;#039;&amp;#039;Prepared Statements zwingend erforderlich&amp;#039;&amp;#039;&amp;#039;, um die Integrität und Sicherheit der Datenbank zu gewährleisten, insbesondere wenn Benutzereingaben beteiligt sind.&lt;br /&gt;
&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;PDO:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;Vorbereiten (&amp;lt;code&amp;gt;prepare&amp;lt;/code&amp;gt;):&amp;#039;&amp;#039;&amp;#039; Die SQL-Anweisung (&amp;lt;code&amp;gt;INSERT&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;UPDATE&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;DELETE&amp;lt;/code&amp;gt;) wird mit Platzhaltern (&amp;lt;code&amp;gt;?&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;:name&amp;lt;/code&amp;gt;) für die einzufügenden/zu aktualisierenden Werte sowie für die Kriterien in &amp;lt;code&amp;gt;WHERE&amp;lt;/code&amp;gt;-Klauseln (bei &amp;lt;code&amp;gt;UPDATE&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;DELETE&amp;lt;/code&amp;gt;) vorbereitet.&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;Ausführen (&amp;lt;code&amp;gt;execute&amp;lt;/code&amp;gt;):&amp;#039;&amp;#039;&amp;#039; Die Anweisung wird mit einem Array ausgeführt, das die Werte für die Platzhalter enthält.&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;Ergebnis prüfen:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
***** &amp;#039;&amp;#039;Anzahl betroffener Zeilen:&amp;#039;&amp;#039; &amp;lt;code&amp;gt;$stmt-&amp;amp;gt;rowCount()&amp;lt;/code&amp;gt; gibt die Anzahl der durch &amp;lt;code&amp;gt;UPDATE&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;DELETE&amp;lt;/code&amp;gt; betroffenen Zeilen zurück. Bei &amp;lt;code&amp;gt;INSERT&amp;lt;/code&amp;gt; ist der Rückgabewert oft nicht zuverlässig oder standardisiert. Das Verhalten kann je nach Datenbanktreiber und Konfiguration (z.B. &amp;lt;code&amp;gt;PDO::MYSQL_ATTR_FOUND_ROWS&amp;lt;/code&amp;gt;) variieren.&lt;br /&gt;
***** &amp;#039;&amp;#039;ID des letzten eingefügten Datensatzes:&amp;#039;&amp;#039; Bei &amp;lt;code&amp;gt;INSERT&amp;lt;/code&amp;gt;-Anweisungen in Tabellen mit einer &amp;lt;code&amp;gt;AUTO_INCREMENT&amp;lt;/code&amp;gt;-Spalte liefert &amp;lt;code&amp;gt;$pdo-&amp;amp;gt;lastInsertId()&amp;lt;/code&amp;gt; die ID des neu eingefügten Datensatzes zurück.&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;Code-Beispiele (PDO):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!--?php// --- INSERT ---&lt;br /&gt;
$name = $_POST[&amp;#039;name&amp;#039;];&lt;br /&gt;
$email = $_POST[&amp;#039;email&amp;#039;];&lt;br /&gt;
$status = &amp;#039;pending&amp;#039;;&lt;br /&gt;
&lt;br /&gt;
$sql_insert = &amp;quot;INSERT INTO users (username, email, status) VALUES (:name, :email, :status)&amp;quot;;&lt;br /&gt;
$stmt_insert = $pdo---&amp;gt;prepare($sql_insert);&lt;br /&gt;
$stmt_insert-&amp;amp;gt;execute([&amp;#039;:name&amp;#039; =&amp;amp;gt; $name, &amp;#039;:email&amp;#039; =&amp;amp;gt; $email, &amp;#039;:status&amp;#039; =&amp;amp;gt; $status]);&lt;br /&gt;
$lastId = $pdo-&amp;amp;gt;lastInsertId();&lt;br /&gt;
echo &amp;quot;Neuer Benutzer mit ID $lastId eingefügt.&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
// --- UPDATE ---&lt;br /&gt;
$new_status = &amp;#039;active&amp;#039;;&lt;br /&gt;
$user_id_to_update = $lastId;&lt;br /&gt;
&lt;br /&gt;
$sql_update = &amp;quot;UPDATE users SET status = :status WHERE id = :id&amp;quot;;&lt;br /&gt;
$stmt_update = $pdo-&amp;amp;gt;prepare($sql_update);&lt;br /&gt;
$stmt_update-&amp;amp;gt;execute([&amp;#039;:status&amp;#039; =&amp;amp;gt; $new_status, &amp;#039;:id&amp;#039; =&amp;amp;gt; $user_id_to_update]);&lt;br /&gt;
$affectedRows = $stmt_update-&amp;amp;gt;rowCount();&lt;br /&gt;
echo &amp;quot;$affectedRows Benutzerstatus aktualisiert.&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
// --- DELETE ---&lt;br /&gt;
$user_id_to_delete = $lastId;&lt;br /&gt;
&lt;br /&gt;
$sql_delete = &amp;quot;DELETE FROM users WHERE id = :id&amp;quot;;&lt;br /&gt;
$stmt_delete = $pdo-&amp;amp;gt;prepare($sql_delete);&lt;br /&gt;
$stmt_delete-&amp;amp;gt;execute([&amp;#039;:id&amp;#039; =&amp;amp;gt; $user_id_to_delete]);&lt;br /&gt;
$affectedRowsDel = $stmt_delete-&amp;amp;gt;rowCount();&lt;br /&gt;
echo &amp;quot;$affectedRowsDel Benutzer gelöscht.&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
?&amp;amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;MySQLi:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;Vorbereiten (&amp;lt;code&amp;gt;prepare&amp;lt;/code&amp;gt;):&amp;#039;&amp;#039;&amp;#039; Die SQL-Anweisung wird mit &amp;lt;code&amp;gt;?&amp;lt;/code&amp;gt;-Platzhaltern vorbereitet (OO: &amp;lt;code&amp;gt;$mysqli-&amp;amp;gt;prepare()&amp;lt;/code&amp;gt;, Proc: &amp;lt;code&amp;gt;mysqli_prepare()&amp;lt;/code&amp;gt;).&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;Parameter binden (&amp;lt;code&amp;gt;bind_param&amp;lt;/code&amp;gt;):&amp;#039;&amp;#039;&amp;#039; Variablen werden explizit mit Typenangabe gebunden (OO: &amp;lt;code&amp;gt;$stmt-&amp;amp;gt;bind_param()&amp;lt;/code&amp;gt;, Proc: &amp;lt;code&amp;gt;mysqli_stmt_bind_param()&amp;lt;/code&amp;gt;). Die Notwendigkeit, den Datentyp für jede Variable anzugeben, ist ein wesentlicher Unterschied zur einfacheren Array-Übergabe bei PDOs &amp;lt;code&amp;gt;execute()&amp;lt;/code&amp;gt;.&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;Ausführen (&amp;lt;code&amp;gt;execute&amp;lt;/code&amp;gt;):&amp;#039;&amp;#039;&amp;#039; Die Anweisung wird ausgeführt (OO: &amp;lt;code&amp;gt;$stmt-&amp;amp;gt;execute()&amp;lt;/code&amp;gt;, Proc: &amp;lt;code&amp;gt;mysqli_stmt_execute()&amp;lt;/code&amp;gt;).[25, 36, 50, 15, 35]&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;Ergebnis prüfen:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
***** &amp;#039;&amp;#039;Anzahl betroffener Zeilen:&amp;#039;&amp;#039; Über die Eigenschaft &amp;lt;code&amp;gt;$mysqli-&amp;amp;gt;affected_rows&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;$stmt-&amp;amp;gt;affected_rows&amp;lt;/code&amp;gt; (OO) bzw. die Funktionen &amp;lt;code&amp;gt;mysqli_affected_rows($link)&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;mysqli_stmt_affected_rows($stmt)&amp;lt;/code&amp;gt; (Proc).&lt;br /&gt;
***** &amp;#039;&amp;#039;ID des letzten eingefügten Datensatzes:&amp;#039;&amp;#039; Über &amp;lt;code&amp;gt;$mysqli-&amp;amp;gt;insert_id&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;$stmt-&amp;amp;gt;insert_id&amp;lt;/code&amp;gt; (OO) bzw. &amp;lt;code&amp;gt;mysqli_insert_id($link)&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;mysqli_stmt_insert_id($stmt)&amp;lt;/code&amp;gt; (Proc). Die Verwendung von &amp;lt;code&amp;gt;$mysqli-&amp;amp;gt;insert_id&amp;lt;/code&amp;gt; wird oft als zuverlässiger angesehen, da sie auch nach dem Schließen des Statements funktioniert.&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;Code-Beispiele (MySQLi OO):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!--?php// --- INSERT ---&lt;br /&gt;
$name = $_POST[&amp;#039;name&amp;#039;];&lt;br /&gt;
$email = $_POST[&amp;#039;email&amp;#039;];&lt;br /&gt;
$status = &amp;#039;pending&amp;#039;;&lt;br /&gt;
&lt;br /&gt;
$sql_insert = &amp;quot;INSERT INTO users (username, email, status) VALUES (?,?,?)&amp;quot;;&lt;br /&gt;
$stmt_insert = $mysqli---&amp;gt;prepare($sql_insert);&lt;br /&gt;
// Typen: s=string, s=string, s=string&lt;br /&gt;
$stmt_insert-&amp;amp;gt;bind_param(&amp;quot;sss&amp;quot;, $name, $email, $status);&lt;br /&gt;
$stmt_insert-&amp;amp;gt;execute();&lt;br /&gt;
$lastId = $mysqli-&amp;amp;gt;insert_id;&lt;br /&gt;
echo &amp;quot;Neuer Benutzer mit ID $lastId eingefügt (MySQLi OO).&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
$stmt_insert-&amp;amp;gt;close(); // Statement schließen&lt;br /&gt;
&lt;br /&gt;
// --- UPDATE ---&lt;br /&gt;
$new_status = &amp;#039;active&amp;#039;;&lt;br /&gt;
$user_id_to_update = $lastId;&lt;br /&gt;
&lt;br /&gt;
$sql_update = &amp;quot;UPDATE users SET status =? WHERE id =?&amp;quot;;&lt;br /&gt;
$stmt_update = $mysqli-&amp;amp;gt;prepare($sql_update);&lt;br /&gt;
// Typen: s=string, i=integer&lt;br /&gt;
$stmt_update-&amp;amp;gt;bind_param(&amp;quot;si&amp;quot;, $new_status, $user_id_to_update);&lt;br /&gt;
$stmt_update-&amp;amp;gt;execute();&lt;br /&gt;
$affectedRows = $mysqli-&amp;amp;gt;affected_rows; // Oder $stmt_update-&amp;amp;gt;affected_rows vor close()&lt;br /&gt;
echo &amp;quot;$affectedRows Benutzerstatus aktualisiert (MySQLi OO).&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
$stmt_update-&amp;amp;gt;close();&lt;br /&gt;
&lt;br /&gt;
// --- DELETE ---&lt;br /&gt;
$user_id_to_delete = $lastId;&lt;br /&gt;
&lt;br /&gt;
$sql_delete = &amp;quot;DELETE FROM users WHERE id =?&amp;quot;;&lt;br /&gt;
$stmt_delete = $mysqli-&amp;amp;gt;prepare($sql_delete);&lt;br /&gt;
// Typ: i=integer&lt;br /&gt;
$stmt_delete-&amp;amp;gt;bind_param(&amp;quot;i&amp;quot;, $user_id_to_delete);&lt;br /&gt;
$stmt_delete-&amp;amp;gt;execute();&lt;br /&gt;
$affectedRowsDel = $mysqli-&amp;amp;gt;affected_rows;&lt;br /&gt;
echo &amp;quot;$affectedRowsDel Benutzer gelöscht (MySQLi OO).&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
$stmt_delete-&amp;amp;gt;close();&lt;br /&gt;
?&amp;amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;#039;&amp;#039;(Anmerkung: Prozedurale Beispiele folgen demselben Muster unter Verwendung der &amp;lt;code&amp;gt;mysqli_*&amp;lt;/code&amp;gt;-Funktionen wie &amp;lt;code&amp;gt;mysqli_prepare&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;mysqli_stmt_bind_param&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;mysqli_stmt_execute&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;mysqli_insert_id&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;mysqli_affected_rows&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;mysqli_stmt_close&amp;lt;/code&amp;gt;)&amp;#039;&amp;#039;.&lt;br /&gt;
&lt;br /&gt;
== Sicherheit: SQL Injection verstehen und verhindern ==&lt;br /&gt;
&lt;br /&gt;
Eines der größten Sicherheitsrisiken bei der Interaktion mit Datenbanken über Webanwendungen ist die SQL Injection (SQLi). Das Verständnis dieser Bedrohung und ihrer Abwehr ist für jeden PHP-Entwickler unerlässlich.&lt;br /&gt;
&lt;br /&gt;
=== SQL Injection ===&lt;br /&gt;
&lt;br /&gt;
SQL Injection ist eine Angriffstechnik, bei der ein Angreifer versucht, bösartigen oder manipulierten SQL-Code über Benutzereingabefelder einer Webanwendung (z.B. Formulare, URL-Parameter, HTTP-Header, Cookies) in die Datenbankabfragen einzuschleusen.&lt;br /&gt;
&lt;br /&gt;
Die Attacke funktioniert, indem Schwachstellen in der Anwendung ausgenutzt werden, bei denen Benutzereingaben direkt und ohne ausreichende Validierung oder Maskierung (Escaping) in SQL-Abfragestrings eingefügt (konkateniert) werden. Dadurch wird die notwendige Trennung zwischen dem eigentlichen SQL-Befehl (Kontrollebene) und den Daten (Datenebene) aufgehoben. Der Angreifer kann spezielle Zeichen (wie Anführungszeichen &amp;lt;code&amp;gt;&amp;#039;&amp;lt;/code&amp;gt;, Semikolons &amp;lt;code&amp;gt;;&amp;lt;/code&amp;gt; oder Kommentare &amp;lt;code&amp;gt;--&amp;lt;/code&amp;gt;) verwenden, um den ursprünglichen SQL-Befehl vorzeitig zu beenden, zu verändern oder um eigene, zusätzliche SQL-Befehle anzuhängen.&lt;br /&gt;
&lt;br /&gt;
Ein klassisches Beispiel ist eine Login-Abfrage, die etwa so aufgebaut ist:&lt;br /&gt;
&amp;lt;code&amp;gt;$sql = &amp;quot;SELECT * FROM users WHERE username = &amp;#039;&amp;quot;. $userInputUsername. &amp;quot;&amp;#039; AND password = &amp;#039;&amp;quot;. $userInputPassword. &amp;quot;&amp;#039;&amp;quot;;&amp;lt;/code&amp;gt;&lt;br /&gt;
Wenn ein Angreifer als Benutzernamen &amp;lt;code&amp;gt;&amp;#039; OR &amp;#039;1&amp;#039;=&amp;#039;1&amp;lt;/code&amp;gt; eingibt, wird die resultierende SQL-Abfrage zu:&lt;br /&gt;
&amp;lt;code&amp;gt;SELECT * FROM users WHERE username = &amp;#039;&amp;#039; OR &amp;#039;1&amp;#039;=&amp;#039;1&amp;#039; AND password = &amp;#039;...&amp;#039;&amp;lt;/code&amp;gt;&lt;br /&gt;
Da &amp;lt;code&amp;gt;&amp;#039;1&amp;#039;=&amp;#039;1&amp;#039;&amp;lt;/code&amp;gt; immer wahr ist, würde diese Abfrage (je nach genauer Struktur und Datenbank) möglicherweise alle Benutzer zurückgeben oder den Login ohne korrektes Passwort ermöglichen.&lt;br /&gt;
&lt;br /&gt;
=== Auswirkungen und Gefahren von SQL Injection ===&lt;br /&gt;
&lt;br /&gt;
Die potenziellen Folgen einer erfolgreichen SQL-Injection-Attacke sind gravierend und vielfältig:&lt;br /&gt;
&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Datendiebstahl:&amp;#039;&amp;#039;&amp;#039; Angreifer können auf sensible Daten zugreifen und diese auslesen, darunter Benutzerdaten, Passwörter (oft als Hashes, die aber geknackt werden können), Kreditkarteninformationen, Geschäftsgeheimnisse und andere vertrauliche Informationen.&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Datenmanipulation/-zerstörung:&amp;#039;&amp;#039;&amp;#039; Angreifer können bestehende Daten in der Datenbank verändern (z.B. Preise in einem Shop, Benutzerrollen) oder komplette Datensätze oder sogar Tabellen löschen (&amp;lt;code&amp;gt;DELETE&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;DROP TABLE&amp;lt;/code&amp;gt;).&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Umgehung der Authentifizierung/Identitätsdiebstahl:&amp;#039;&amp;#039;&amp;#039; Wie im Beispiel oben gezeigt, können Angreifer Login-Mechanismen umgehen und sich unautorisiert Zugang verschaffen, möglicherweise sogar mit administrativen Rechten.&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Denial of Service (DoS):&amp;#039;&amp;#039;&amp;#039; Durch das Löschen wichtiger Daten oder das Ausführen ressourcenintensiver Abfragen kann die Verfügbarkeit der Anwendung oder der Datenbank beeinträchtigt werden.&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Kompromittierung des Servers:&amp;#039;&amp;#039;&amp;#039; In manchen Fällen können Angreifer über SQL Injection Betriebssystembefehle auf dem Datenbankserver ausführen oder vollen administrativen Zugriff auf die Datenbank erlangen, was weitreichende Konsequenzen haben kann.&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Reputationsschaden und rechtliche Folgen:&amp;#039;&amp;#039;&amp;#039; Erfolgreiche Angriffe, insbesondere wenn sie zu Datenlecks führen, können das Vertrauen der Benutzer schwer beschädigen und zu empfindlichen Strafen gemäß Datenschutzgesetzen führen.&lt;br /&gt;
&lt;br /&gt;
=== Prävention durch Prepared Statements (Primäre Methode) ===&lt;br /&gt;
&lt;br /&gt;
Die mit Abstand wichtigste und effektivste Methode zur Verhinderung von SQL Injection ist die konsequente Verwendung von &amp;#039;&amp;#039;&amp;#039;Prepared Statements&amp;#039;&amp;#039;&amp;#039; (parametrisierten Abfragen).&lt;br /&gt;
&lt;br /&gt;
Das Grundprinzip von Prepared Statements ist die &amp;#039;&amp;#039;&amp;#039;strikte Trennung von SQL-Befehlsstruktur und den Daten (Parametern)&amp;#039;&amp;#039;&amp;#039;. Der Ablauf ist typischerweise zweistufig:&lt;br /&gt;
**# &amp;#039;&amp;#039;&amp;#039;Prepare (Vorbereiten):&amp;#039;&amp;#039;&amp;#039; Die SQL-Anweisung wird mit Platzhaltern anstelle der tatsächlichen Werte an den Datenbankserver gesendet. Der Server parst die Anweisung, prüft die Syntax und bereitet einen Ausführungsplan vor, ohne die endgültigen Daten zu kennen.&lt;br /&gt;
**# &amp;#039;&amp;#039;&amp;#039;Execute (Ausführen) mit Parameterbindung:&amp;#039;&amp;#039;&amp;#039; Die tatsächlichen Werte (Parameter) werden separat an den Server gesendet und an die vorbereiteten Platzhalter gebunden. Der Server führt nun die vorkompilierte Anweisung mit den sicher eingefügten Werten aus.&lt;br /&gt;
Der entscheidende Punkt ist, dass die Datenbank die separat gesendeten Parameter &amp;#039;&amp;#039;&amp;#039;immer als Datenwerte&amp;#039;&amp;#039;&amp;#039; behandelt, niemals als Teil des SQL-Befehls. Selbst wenn ein Parameter bösartigen SQL-Code enthält (z.B. &amp;lt;code&amp;gt;&amp;#039; OR &amp;#039;1&amp;#039;=&amp;#039;1&amp;lt;/code&amp;gt;), wird dieser nicht ausgeführt, sondern als einfacher String-Wert behandelt, der z.B. in eine Spalte eingefügt oder in einer &amp;lt;code&amp;gt;WHERE&amp;lt;/code&amp;gt;-Klausel verglichen wird. Diese Trennung macht Prepared Statements immun gegen SQL Injection.&lt;br /&gt;
&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Platzhalter:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;PDO:&amp;#039;&amp;#039;&amp;#039; Unterstützt sowohl positionale Platzhalter (&amp;lt;code&amp;gt;?&amp;lt;/code&amp;gt;) als auch benannte Platzhalter (&amp;lt;code&amp;gt;:name&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;:email&amp;lt;/code&amp;gt;, etc.).[5, 13] Benannte Platzhalter machen den Code oft lesbarer, besonders bei vielen Parametern.&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;MySQLi:&amp;#039;&amp;#039;&amp;#039; Unterstützt ausschließlich positionale Platzhalter (&amp;lt;code&amp;gt;?&amp;lt;/code&amp;gt;).&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Parameterbindung:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;PDO:&amp;#039;&amp;#039;&amp;#039; Bietet flexible Bindungsoptionen:&lt;br /&gt;
***** &amp;#039;&amp;#039;Implizit über &amp;lt;code&amp;gt;execute()&amp;lt;/code&amp;gt;-Array:&amp;#039;&amp;#039; Die einfachste und häufigste Methode. Ein Array mit den Werten wird an &amp;lt;code&amp;gt;execute()&amp;lt;/code&amp;gt; übergeben. PDO kümmert sich um das korrekte Escaping und die Typbehandlung (oft werden Werte sicher als Strings behandelt).&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$stmt-&amp;amp;gt;execute([$wert1, $wert2]); // für? Platzhalter&lt;br /&gt;
$stmt-&amp;amp;gt;execute([&amp;#039;:name&amp;#039; =&amp;amp;gt; $wert1, &amp;#039;:id&amp;#039; =&amp;amp;gt; $wert2]); // für :name Platzhalter&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
***** &amp;#039;&amp;#039;Explizit mit &amp;lt;code&amp;gt;bindValue()&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;bindParam()&amp;lt;/code&amp;gt;:&amp;#039;&amp;#039; Erlaubt die explizite Angabe des Datentyps (z.B. &amp;lt;code&amp;gt;PDO::PARAM_INT&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;PDO::PARAM_STR&amp;lt;/code&amp;gt;) für jeden Parameter, was in manchen Fällen für Klarheit oder spezifische Datenbankoptimierungen sorgen kann.[5, 13] &amp;lt;code&amp;gt;bindParam&amp;lt;/code&amp;gt; bindet eine Variable (ihr Wert wird erst bei &amp;lt;code&amp;gt;execute()&amp;lt;/code&amp;gt; gelesen), &amp;lt;code&amp;gt;bindValue&amp;lt;/code&amp;gt; bindet den aktuellen Wert einer Variablen oder einen Literalwert.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$stmt-&amp;amp;gt;bindValue(&amp;#039;:id&amp;#039;, $user_id, PDO::PARAM_INT);&lt;br /&gt;
$stmt-&amp;amp;gt;bindParam(&amp;#039;:name&amp;#039;, $user_name, PDO::PARAM_STR);&lt;br /&gt;
$stmt-&amp;amp;gt;execute();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;MySQLi:&amp;#039;&amp;#039;&amp;#039; Erfordert immer die explizite Bindung mittels &amp;lt;code&amp;gt;bind_param()&amp;lt;/code&amp;gt;.&lt;br /&gt;
***** &amp;lt;code&amp;gt;bind_param()&amp;lt;/code&amp;gt; (OO) / &amp;lt;code&amp;gt;mysqli_stmt_bind_param()&amp;lt;/code&amp;gt; (Proc): Nimmt als ersten Parameter einen String entgegen, der die Datentypen aller nachfolgend übergebenen Variablen definiert.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// OO-Stil&lt;br /&gt;
$stmt-&amp;amp;gt;bind_param(&amp;quot;ssi&amp;quot;, $name, $email, $id); // String, String, Integer&lt;br /&gt;
// Prozedural&lt;br /&gt;
mysqli_stmt_bind_param($stmt, &amp;quot;ssi&amp;quot;, $name, $email, $id);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
=== Tabelle 4: MySQLi &amp;lt;code&amp;gt;bind_param()&amp;lt;/code&amp;gt; Typen-String ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Typ-Buchstabe !! PHP-Datentyp (typisch) !! Beschreibung&lt;br /&gt;
|- &lt;br /&gt;
| mwt-preservehtml=&amp;quot;&amp;quot; i=&amp;quot;&amp;quot; code=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| integer=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Integer (Ganzzahl)&lt;br /&gt;
|- &lt;br /&gt;
| mwt-preservehtml=&amp;quot;&amp;quot; d=&amp;quot;&amp;quot; code=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| double=&amp;quot;&amp;quot; float=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Double (Gleitkommazahl)&lt;br /&gt;
|- &lt;br /&gt;
| mwt-preservehtml=&amp;quot;&amp;quot; s=&amp;quot;&amp;quot; code=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| string=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| String (Zeichenkette)&lt;br /&gt;
|- &lt;br /&gt;
| mwt-preservehtml=&amp;quot;&amp;quot; b=&amp;quot;&amp;quot; code=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| string=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| BLOB (Binary Large Object), als String gesendet&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Detaillierte Code-Beispiele zur Prävention:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
Die Beispiele für INSERT, UPDATE und DELETE in Abschnitt 3.3 demonstrieren bereits die korrekte Anwendung von Prepared Statements mit PDO und MySQLi zur Verhinderung von SQL Injection. Es ist entscheidend, dieses Muster konsequent anzuwenden, wann immer externe Daten in SQL-Abfragen einfließen.&lt;br /&gt;
&lt;br /&gt;
=== 4.4 Zusätzliche Schutzmaßnahmen (Defense-in-Depth) ===&lt;br /&gt;
&lt;br /&gt;
Obwohl Prepared Statements der wichtigste Schutz gegen SQL Injection sind, sollte Sicherheit immer als mehrschichtiges Konzept (&amp;quot;Defense-in-Depth&amp;quot;) betrachtet werden. Zusätzliche Maßnahmen erhöhen die Robustheit der Anwendung:&lt;br /&gt;
&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Eingabevalidierung und -bereinigung:&amp;#039;&amp;#039;&amp;#039; Überprüfen Sie &amp;#039;&amp;#039;alle&amp;#039;&amp;#039; Benutzereingaben serverseitig, bevor sie überhaupt in die Nähe der Datenbank kommen. Stellen Sie sicher, dass die Daten dem erwarteten Typ (Zahl, String, E-Mail-Format etc.) und Format entsprechen. Verwenden Sie dafür PHP-Filterfunktionen wie &amp;lt;code&amp;gt;filter_var()&amp;lt;/code&amp;gt; oder reguläre Ausdrücke. Dies ist eine wichtige zweite Verteidigungslinie, die ungültige oder unerwartete Daten frühzeitig abfängt. Es ersetzt jedoch &amp;#039;&amp;#039;nicht&amp;#039;&amp;#039; die Notwendigkeit von Prepared Statements.&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Prinzip der geringsten Rechte (Least Privilege):&amp;#039;&amp;#039;&amp;#039; Konfigurieren Sie den Datenbankbenutzer, den Ihre PHP-Anwendung verwendet, mit den absolut minimal notwendigen Berechtigungen. Wenn die Anwendung nur Daten lesen und schreiben muss, geben Sie ihr keine Rechte zum Ändern der Tabellenstruktur (&amp;lt;code&amp;gt;ALTER TABLE&amp;lt;/code&amp;gt;) oder zum Löschen von Tabellen (&amp;lt;code&amp;gt;DROP TABLE&amp;lt;/code&amp;gt;). Beschränken Sie den Zugriff auf die benötigten Datenbanken und Tabellen. Verwenden Sie niemals den &amp;lt;code&amp;gt;root&amp;lt;/code&amp;gt;- oder Admin-Benutzer der Datenbank für die Anwendung. Dies begrenzt den potenziellen Schaden, falls doch eine Schwachstelle ausgenutzt wird.&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Sichere Fehlerbehandlung:&amp;#039;&amp;#039;&amp;#039; Konfigurieren Sie PHP und Ihre Datenbankerweiterung so, dass detaillierte Fehlermeldungen (insbesondere solche, die SQL-Code oder Datenbankstrukturinformationen enthalten) niemals dem Endbenutzer angezeigt werden. Solche Informationen sind für Angreifer wertvoll. Loggen Sie Fehler stattdessen in eine sichere Datei auf dem Server, die nur für Administratoren zugänglich ist. Verwenden Sie &amp;lt;code&amp;gt;PDO::ERRMODE_EXCEPTION&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;mysqli_report&amp;lt;/code&amp;gt; für strukturierte Fehlerbehandlung.&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Output Escaping:&amp;#039;&amp;#039;&amp;#039; Obwohl nicht direkt zur Verhinderung von SQL Injection, ist es wichtig, Daten, die aus der Datenbank gelesen und im HTML-Kontext ausgegeben werden, korrekt zu escapen (z.B. mit &amp;lt;code&amp;gt;htmlspecialchars()&amp;lt;/code&amp;gt;), um Cross-Site Scripting (XSS)-Angriffe zu verhindern.&lt;br /&gt;
Die Kombination dieser Maßnahmen – mit Prepared Statements als Kernstück – bietet einen robusten Schutz gegen SQL Injection und andere verwandte Angriffe.&lt;br /&gt;
&lt;br /&gt;
== Nützliche PHP Array-Funktionen für Datenbankergebnisse ==&lt;br /&gt;
&lt;br /&gt;
Wenn Daten aus der Datenbank abgerufen werden, insbesondere mit Methoden wie &amp;lt;code&amp;gt;fetchAll()&amp;lt;/code&amp;gt; (PDO) oder &amp;lt;code&amp;gt;fetch_all()&amp;lt;/code&amp;gt; (MySQLi), liegen die Ergebnisse typischerweise als PHP-Array vor (meist ein Array von assoziativen Arrays, wobei jedes innere Array eine Ergebniszeile darstellt). PHP bietet eine Reihe nützlicher Funktionen zur Verarbeitung solcher Arrays.&lt;br /&gt;
&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Iteration mit &amp;lt;code&amp;gt;foreach&amp;lt;/code&amp;gt;:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
Dies ist die Standardmethode, um die einzelnen Zeilen und Spalten eines Ergebnis-Arrays zu durchlaufen.&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;Syntax:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Iteration über die Zeilen (jedes $row ist ein assoziatives Array)&lt;br /&gt;
foreach ($results as $row) {&lt;br /&gt;
 echo &amp;quot;ID: &amp;quot;. $row[&amp;#039;id&amp;#039;]. &amp;quot;, Name: &amp;quot;. htmlspecialchars($row[&amp;#039;name&amp;#039;]). &amp;quot;&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Iteration über Zeilen mit Zugriff auf den numerischen Index der Zeile&lt;br /&gt;
foreach ($results as $index =&amp;amp;gt; $row) {&lt;br /&gt;
 echo &amp;quot;Zeile &amp;quot;. $index. &amp;quot;: ID = &amp;quot;. $row[&amp;#039;id&amp;#039;]. &amp;quot;&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;Code-Beispiel:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!--?php// Annahme: $results enthält das Ergebnis von $stmt---&amp;gt;fetchAll(PDO::FETCH_ASSOC);&lt;br /&gt;
$results = [&amp;#039;id&amp;#039; =&amp;amp;gt; 1, &amp;#039;name&amp;#039; =&amp;amp;gt; &amp;#039;Alice&amp;#039;, &amp;#039;email&amp;#039; =&amp;amp;gt; &amp;#039;alice@example.com&amp;#039;],&lt;br /&gt;
 ;&lt;br /&gt;
&lt;br /&gt;
echo &amp;quot;&amp;lt;/syntaxhighlight&amp;gt;&amp;quot;;&lt;br /&gt;
foreach ($results as $row) {&lt;br /&gt;
 echo &amp;quot;&lt;br /&gt;
*Benutzer #&amp;quot;. $row[&amp;#039;id&amp;#039;]. &amp;quot;: &amp;quot;. htmlspecialchars($row[&amp;#039;name&amp;#039;]);&lt;br /&gt;
 echo &amp;quot; (&amp;quot;. htmlspecialchars($row[&amp;#039;email&amp;#039;]). &amp;quot;)&lt;br /&gt;
&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
echo &amp;quot;&amp;quot;;&lt;br /&gt;
?&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Zählen von Ergebnissen mit &amp;lt;code&amp;gt;count()&amp;lt;/code&amp;gt;:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
Die Funktion &amp;lt;code&amp;gt;count()&amp;lt;/code&amp;gt; gibt die Anzahl der Elemente in einem Array zurück. Wenn sie auf ein mit &amp;lt;code&amp;gt;fetchAll()&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;fetch_all()&amp;lt;/code&amp;gt; erzeugtes Ergebnis-Array angewendet wird, liefert sie die Anzahl der abgerufenen Zeilen.&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;Syntax:&amp;#039;&amp;#039;&amp;#039; &amp;lt;code&amp;gt;$anzahlZeilen = count($results);&amp;lt;/code&amp;gt;.&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;Wichtiger Hinweis:&amp;#039;&amp;#039;&amp;#039; &amp;lt;code&amp;gt;count()&amp;lt;/code&amp;gt; funktioniert nur zuverlässig für die Gesamtzahl der Zeilen, wenn &amp;#039;&amp;#039;alle&amp;#039;&amp;#039; Zeilen zuvor in ein Array geladen wurden (also nach &amp;lt;code&amp;gt;fetchAll&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;fetch_all&amp;lt;/code&amp;gt;). Wenn Sie Ergebnisse zeilenweise mit &amp;lt;code&amp;gt;fetch()&amp;lt;/code&amp;gt; in einer &amp;lt;code&amp;gt;while&amp;lt;/code&amp;gt;-Schleife verarbeiten, müssen Sie einen manuellen Zähler innerhalb der Schleife inkrementieren, um die Anzahl der verarbeiteten Zeilen zu ermitteln. Alternativ können &amp;lt;code&amp;gt;PDOStatement::rowCount()&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;mysqli_result::$num_rows&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;mysqli_stmt::$num_rows&amp;lt;/code&amp;gt; verwendet werden, um die Anzahl der von einer &amp;lt;code&amp;gt;SELECT&amp;lt;/code&amp;gt;-Abfrage zurückgegebenen Zeilen zu erhalten, &amp;#039;&amp;#039;bevor&amp;#039;&amp;#039; die Daten gefetched werden (das Verhalten von &amp;lt;code&amp;gt;rowCount&amp;lt;/code&amp;gt; kann jedoch bei anderen Anweisungstypen wie &amp;lt;code&amp;gt;UPDATE&amp;lt;/code&amp;gt; variieren).&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;Code-Beispiel:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!--?php&lt;br /&gt;
// Annahme: $results von fetchAll()&lt;br /&gt;
$results = [ /*... wie oben... */ ];&lt;br /&gt;
$anzahl = count($results);&lt;br /&gt;
echo &amp;quot;Anzahl der Benutzer (via fetchAll/count): $anzahl&amp;lt;br--&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
// Vergleich: Zählen bei zeilenweiser Verarbeitung&lt;br /&gt;
$stmt = $pdo-&amp;amp;gt;query(&amp;quot;SELECT id FROM users&amp;quot;); // Annahme: PDO-Statement&lt;br /&gt;
$manueller_zaehler = 0;&lt;br /&gt;
while ($row = $stmt-&amp;amp;gt;fetch()) {&lt;br /&gt;
 $manueller_zaehler++;&lt;br /&gt;
}&lt;br /&gt;
echo &amp;quot;Anzahl der Benutzer (via while/fetch): $manueller_zaehler&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
// Alternative: rowCount() nach SELECT (PDO)&lt;br /&gt;
$stmt_count = $pdo-&amp;amp;gt;query(&amp;quot;SELECT id FROM users&amp;quot;);&lt;br /&gt;
$rowCountResult = $stmt_count-&amp;amp;gt;rowCount(); // Kann bei manchen DBs/Treibern funktionieren&lt;br /&gt;
echo &amp;quot;Anzahl der Benutzer (via rowCount nach SELECT): $rowCountResult&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
?&amp;amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;(Optional) Weitere nützliche Array-Funktionen:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
**** &amp;lt;code&amp;gt;array_column(array $input, mixed $column_key [, mixed $index_key = null ])&amp;lt;/code&amp;gt;: Extrahiert alle Werte aus einer einzelnen Spalte des Ergebnis-Arrays und gibt sie als neues, eindimensionales Array zurück.[62] Sehr nützlich, um z.B. eine Liste aller Benutzer-IDs oder E-Mail-Adressen zu erhalten.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Annahme: $results enthält das Array von assoziativen Arrays&lt;br /&gt;
$alle_emails = array_column($results, &amp;#039;email&amp;#039;);&lt;br /&gt;
// $alle_emails ist nun [&amp;#039;alice@example.com&amp;#039;, &amp;#039;bob@example.com&amp;#039;]&lt;br /&gt;
print_r($alle_emails);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
**** &amp;lt;code&amp;gt;array_map(callable $callback, array $array1 [, array...$arrays ])&amp;lt;/code&amp;gt;: Wendet eine benutzerdefinierte Funktion (&amp;lt;code&amp;gt;callback&amp;lt;/code&amp;gt;) auf jedes Element eines Arrays an und gibt ein neues Array mit den Ergebnissen zurück.[63] Nützlich zur Transformation von Daten (z.B. Formatierung, Bereinigung).&lt;br /&gt;
**** &amp;lt;code&amp;gt;array_filter(array $array [, callable $callback [, int $flag = 0 ]])&amp;lt;/code&amp;gt;: Filtert Elemente eines Arrays basierend auf einer Callback-Funktion. Nützlich, um nur bestimmte Zeilen aus dem Ergebnis-Array zu behalten (obwohl dies oft effizienter direkt in der SQL-Abfrage geschieht).&lt;br /&gt;
**** &amp;lt;code&amp;gt;array_count_values(array $array)&amp;lt;/code&amp;gt;: Zählt, wie oft jeder eindeutige Wert in einem eindimensionalen Array vorkommt. Kann nützlich sein, wenn man z.B. mit &amp;lt;code&amp;gt;array_column&amp;lt;/code&amp;gt; eine Spalte extrahiert hat und die Häufigkeit bestimmter Werte (z.B. Status) wissen möchte.&lt;br /&gt;
Diese Funktionen, insbesondere &amp;lt;code&amp;gt;foreach&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;count&amp;lt;/code&amp;gt;, sind grundlegende Werkzeuge bei der Verarbeitung von Datenbankergebnissen in PHP.&lt;br /&gt;
&lt;br /&gt;
== Zusammenfassung &amp;amp; Best Practices ==&lt;br /&gt;
&lt;br /&gt;
Die Integration von Datenbanken ist ein zentraler Aspekt der PHP-Webentwicklung. Dieses Kapitel hat die Grundlagen für die Interaktion mit MySQL-Datenbanken gelegt. Hier sind die wichtigsten Punkte und Best Practices zusammengefasst:&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Wahl der Erweiterung:&amp;#039;&amp;#039;&amp;#039; Verwenden Sie ausschließlich die modernen Erweiterungen &amp;#039;&amp;#039;&amp;#039;PDO&amp;#039;&amp;#039;&amp;#039; oder &amp;#039;&amp;#039;&amp;#039;MySQLi&amp;#039;&amp;#039;&amp;#039;.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;PDO&amp;#039;&amp;#039;&amp;#039; wird generell für neue Projekte empfohlen, da es Datenbankunabhängigkeit bietet und eine konsistentere, oft als einfacher empfundene API (insbesondere bei der Parameterbindung) hat.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;MySQLi&amp;#039;&amp;#039;&amp;#039; ist eine gute Wahl, wenn ausschließlich mit MySQL gearbeitet wird und spezifische MySQL-Funktionen benötigt werden oder wenn eine prozedurale Schnittstelle bevorzugt wird.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Prepared Statements:&amp;#039;&amp;#039;&amp;#039; Dies ist die wichtigste Sicherheitsmaßnahme. Verwenden Sie Prepared Statements immer dann, wenn externe Daten (Benutzereingaben, URL-Parameter etc.) Teil einer SQL-Abfrage werden (insbesondere bei &amp;lt;code&amp;gt;WHERE&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;INSERT&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;UPDATE&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;DELETE&amp;lt;/code&amp;gt;). Sie verhindern SQL Injection zuverlässig durch die Trennung von Code und Daten.&lt;br /&gt;
&lt;br /&gt;
 &amp;#039;&amp;#039;&amp;#039;Sichere Fehlerbehandlung:&amp;#039;&amp;#039;&amp;#039; Konfigurieren Sie PDO (&amp;lt;code&amp;gt;PDO::ATTR_ERRMODE =&amp;amp;gt; PDO::ERRMODE_EXCEPTION&amp;lt;/code&amp;gt;) oder MySQLi (&amp;lt;code&amp;gt;mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT)&amp;lt;/code&amp;gt;) so, dass Fehler als Exceptions behandelt werden. Fangen Sie diese Exceptions ab und loggen Sie detaillierte Fehlermeldungen serverseitig, anstatt sie dem Benutzer anzuzeigen.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Input Validation:&amp;#039;&amp;#039;&amp;#039; Validieren Sie alle Benutzereingaben serverseitig auf Typ, Format und erlaubte Werte, bevor Sie sie an die Datenbank übergeben. Dies ist eine wichtige zusätzliche Schutzschicht.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Prinzip der geringsten Rechte (Least Privilege):&amp;#039;&amp;#039;&amp;#039; Der Datenbankbenutzer, den Ihre PHP-Anwendung verwendet, sollte nur die minimal notwendigen Berechtigungen für seine Aufgaben besitzen.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Sichere Zugangsdaten:&amp;#039;&amp;#039;&amp;#039; Speichern Sie Datenbank-Credentials (Hostname, Benutzername, Passwort) niemals direkt im PHP-Code oder in Dateien innerhalb des Web-Roots. Verwenden Sie stattdessen Konfigurationsdateien außerhalb des öffentlich zugänglichen Bereichs oder Umgebungsvariablen.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Code-Struktur:&amp;#039;&amp;#039;&amp;#039; Kapseln Sie Datenbankverbindungslogik und wiederkehrende Abfragen in Funktionen oder Klassen, um die Wartbarkeit und Wiederverwendbarkeit zu verbessern.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Effizienz:&amp;#039;&amp;#039;&amp;#039; Laden Sie große Ergebnismengen nicht komplett in den Speicher (&amp;lt;code&amp;gt;fetchAll&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;fetch_all&amp;lt;/code&amp;gt;). Verwenden Sie stattdessen zeilenweise Verarbeitung (&amp;lt;code&amp;gt;while&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;fetch&amp;lt;/code&amp;gt;) oder Iteratoren (PDO mit PHP 8+). Nutzen Sie persistente Verbindungen nur nach sorgfältiger Abwägung der Vor- und Nachteile.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Defense-in-Depth:&amp;#039;&amp;#039;&amp;#039; Betrachten Sie Sicherheit als Gesamtkonzept. Die Kombination aus der richtigen API-Wahl, konsequenten Prepared Statements, Eingabevalidierung, minimalen Rechten, sicherer Fehlerbehandlung, sicherer Speicherung von Zugangsdaten und regelmäßigen Software-Updates bildet eine robuste Verteidigung.&lt;/div&gt;</summary>
		<author><name>Völkl Anna</name></author>
	</entry>
	<entry>
		<id>https://mediawiki.fernfh.ac.at/mediawiki/index.php?title=Datenbankintegration_(Webdevelopment)&amp;diff=6576</id>
		<title>Datenbankintegration (Webdevelopment)</title>
		<link rel="alternate" type="text/html" href="https://mediawiki.fernfh.ac.at/mediawiki/index.php?title=Datenbankintegration_(Webdevelopment)&amp;diff=6576"/>
		<updated>2025-05-01T13:18:17Z</updated>

		<summary type="html">&lt;p&gt;Völkl Anna: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Kapitel X: Datenbankintegration mit PHP und MySQL =&lt;br /&gt;
&lt;br /&gt;
== Einleitung: Warum Datenbanken in PHP-Webanwendungen? ==&lt;br /&gt;
&lt;br /&gt;
Moderne Webanwendungen sind selten statisch. Sie leben von dynamischen Inhalten, Benutzerinteraktionen und der Fähigkeit, Informationen über Sitzungen hinweg zu speichern und wieder abzurufen. Ob es sich um ein Content-Management-System (CMS), einen Online-Shop, ein soziales Netzwerk oder eine einfache Benutzerverwaltung handelt – die Persistenz von Daten ist entscheidend. Hier kommen Datenbanken ins Spiel.&lt;br /&gt;
&lt;br /&gt;
PHP allein, als serverseitige Skriptsprache, kann Daten nur für die Dauer der Ausführung eines Skripts im Arbeitsspeicher halten. Sobald das Skript beendet ist, gehen diese Informationen verloren. Um Daten dauerhaft zu speichern, zu organisieren, zu ändern und effizient abzurufen, benötigen PHP-Anwendungen ein externes Datenbanksystem.&lt;br /&gt;
&lt;br /&gt;
Eine der populärsten Datenbanklösungen im Webentwicklungsbereich, insbesondere in Kombination mit PHP, ist MySQL. Als relationales Datenbankmanagementsystem (RDBMS) bietet MySQL eine strukturierte Methode zur Datenspeicherung in Tabellen, die miteinander in Beziehung stehen können. Es ist ein Kernbestandteil vieler Webserver-Setups wie LAMP (Linux, Apache, MySQL, PHP) oder LEMP (Linux, Nginx, MySQL, PHP).&lt;br /&gt;
&lt;br /&gt;
Um die Brücke zwischen der PHP-Anwendung und der MySQL-Datenbank zu schlagen, stellt PHP spezielle Erweiterungen (Extensions) bereit. Diese Erweiterungen fungieren als Schnittstelle oder API (Application Programming Interface), die es PHP-Skripten ermöglichen, eine Verbindung zur Datenbank herzustellen, SQL-Befehle zu senden und die von der Datenbank zurückgegebenen Ergebnisse zu empfangen und zu verarbeiten.&lt;br /&gt;
&lt;br /&gt;
Dieses Kapitel führt in die Grundlagen der Datenbankintegration mit PHP und MySQL ein. Es enthält die verschiedenen verfügbaren PHP-Erweiterungen, wobei der Schwerpunkt auf den modernen und sicheren Ansätzen liegt: PDO (PHP Data Objects) und MySQLi (MySQL Improved). Es wird gezeigt, wie man Verbindungen aufbaut, grundlegende Datenbankoperationen – bekannt als CRUD (Create, Read, Update, Delete) – durchführt und wie man Daten sicher abfragt und manipuliert. Ein besonderes Augenmerk liegt dabei auf der Sicherheit, insbesondere auf der Vermeidung von SQL-Injection-Angriffen durch den Einsatz von Prepared Statements. Zum Abschluss erfolgt ein Überblick über nützliche PHP-Array-Funktionen zur Verarbeitung der abgerufenen Datenbankergebnisse.&lt;br /&gt;
&lt;br /&gt;
== Überblick über PHP-Datenbankerweiterungen ==&lt;br /&gt;
&lt;br /&gt;
Die Art und Weise, wie PHP mit Datenbanken interagiert, hat sich im Laufe der Zeit weiterentwickelt. Ursprünglich gab es für jede populäre Datenbank eine eigene spezifische Erweiterung. Für MySQL waren dies die &amp;lt;code&amp;gt;mysql_*&amp;lt;/code&amp;gt; Funktionen. Diese sind jedoch inzwischen veraltet und wurden durch modernere, flexiblere und sicherere Alternativen ersetzt. Heute stehen Entwicklern hauptsächlich zwei empfohlene Erweiterungen für die Arbeit mit MySQL zur Verfügung: PDO und MySQLi.&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;PDO (PHP Data Objects):&amp;#039;&amp;#039;&amp;#039; Eine allgemeine Datenzugriffs-Abstraktionsschicht, die mit verschiedenen Datenbanksystemen verwendet werden kann.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;MySQLi (MySQL Improved):&amp;#039;&amp;#039;&amp;#039; Eine Erweiterung, die speziell für die Arbeit mit MySQL entwickelt wurde und dessen spezifische Funktionen unterstützt.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;&amp;lt;code&amp;gt;mysql_*&amp;lt;/code&amp;gt; Funktionen:&amp;#039;&amp;#039;&amp;#039; Die ursprüngliche MySQL-Erweiterung, die seit PHP 7.0 entfernt wurde und nicht mehr verwendet werden sollte.&lt;br /&gt;
Im Folgenden werden diese drei Optionen detaillierter vorgestellt.&lt;br /&gt;
&lt;br /&gt;
=== PDO (PHP Data Objects) ===&lt;br /&gt;
&lt;br /&gt;
Die PDO-Erweiterung definiert eine leichtgewichtige und konsistente Schnittstelle für den Datenbankzugriff in PHP. Sie fungiert als &amp;#039;&amp;#039;Datenzugriffs-Abstraktionsschicht&amp;#039;&amp;#039;. Das bedeutet, dass Entwickler unabhängig von der verwendeten Datenbank dieselben PDO-Funktionen nutzen können, um Abfragen auszuführen und Daten abzurufen.[1, 2] Es ist jedoch wichtig zu verstehen, dass PDO &amp;#039;&amp;#039;keine&amp;#039;&amp;#039; Datenbankabstraktion im Sinne einer SQL-Dialekt-Übersetzung oder der Emulation fehlender Datenbankfeatures bietet. Um eine Verbindung zu einer spezifischen Datenbank wie MySQL herzustellen, benötigt PDO einen entsprechenden datenbankspezifischen Treiber, z.B. &amp;lt;code&amp;gt;pdo_mysql&amp;lt;/code&amp;gt;, der in der PHP-Konfiguration aktiviert sein muss.[1]&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Vorteile von PDO:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
*&amp;#039;&amp;#039;&amp;#039;Datenbankunabhängigkeit:&amp;#039;&amp;#039;&amp;#039; Der größte Vorteil von PDO ist die Möglichkeit, den Code für Datenbankinteraktionen weitgehend unverändert zu lassen, selbst wenn das zugrunde liegende Datenbanksystem gewechselt wird (z.B. von MySQL zu PostgreSQL).[1, 2] Dies erhöht die Portabilität und Flexibilität von PHP-Anwendungen erheblich.[2, 3]&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Sicherheit durch Prepared Statements:&amp;#039;&amp;#039;&amp;#039; PDO bietet eine robuste und konsistente Implementierung von Prepared Statements. Diese Technik ist der Goldstandard zur Verhinderung von SQL-Injection-Angriffen, da sie die SQL-Befehlsstruktur strikt von den übergebenen Daten trennt.[1, 2, 4, 5, 6]&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Benutzerfreundlichkeit und Konsistenz:&amp;#039;&amp;#039;&amp;#039; PDO stellt eine einheitliche API mit nützlichen Hilfsfunktionen bereit, wie z.B. vielfältige &amp;quot;Fetch Modes&amp;quot;, um die Struktur der abgerufenen Daten zu steuern.[2] Die konsistente Funktionsweise über verschiedene Datenbanktreiber hinweg vereinfacht die Entwicklung.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Moderne Fehlerbehandlung:&amp;#039;&amp;#039;&amp;#039; PDO verwendet standardmäßig Exceptions (&amp;lt;code&amp;gt;PDOException&amp;lt;/code&amp;gt;) zur Signalisierung von Fehlern.[1, 2, 7] Dies ermöglicht eine strukturierte und moderne Fehlerbehandlung mittels &amp;lt;code&amp;gt;try...catch&amp;lt;/code&amp;gt;-Blöcken, was als Best Practice gilt.[1, 2]&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Verbindungsaufbau mit PDO (Beispiel MySQL):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;DSN (Data Source Name):&amp;#039;&amp;#039;&amp;#039; Die Verbindungsparameter werden in einer Zeichenkette, dem DSN, übergeben. Für MySQL hat der DSN typischerweise das Format &amp;lt;code&amp;gt;mysql:host=hostname;dbname=datenbankname;charset=zeichensatz;unix_socket=pfad_zum_socket&amp;lt;/code&amp;gt;. Die Angabe des Zeichensatzes (z.B. &amp;lt;code&amp;gt;utf8mb4&amp;lt;/code&amp;gt;) ist wichtig für die korrekte Datenübertragung.[1, 7] Beispiel: &amp;lt;code&amp;gt;mysql:host=localhost;dbname=test;charset=utf8mb4&amp;lt;/code&amp;gt;.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;PDO-Konstruktor:&amp;#039;&amp;#039;&amp;#039; Eine Verbindung wird durch Instanziierung der PDO-Klasse hergestellt: &amp;lt;code&amp;gt;$pdo = new PDO($dsn, $username, $password, $options);&amp;lt;/code&amp;gt;.[1, 7, 8] Die Parameter sind der DSN, der Datenbankbenutzername, das Passwort und ein optionales Array mit Treiberoptionen.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Verbindungsoptionen (&amp;lt;code&amp;gt;$options&amp;lt;/code&amp;gt;):&amp;#039;&amp;#039;&amp;#039; Über dieses Array können wichtige Verhaltensweisen von PDO gesteuert werden:&lt;br /&gt;
* &amp;lt;code&amp;gt;PDO::ATTR_ERRMODE =&amp;amp;gt; PDO::ERRMODE_EXCEPTION&amp;lt;/code&amp;gt;: Dies ist die empfohlene Einstellung für die Fehlerbehandlung. PDO wirft bei Fehlern eine &amp;lt;code&amp;gt;PDOException&amp;lt;/code&amp;gt;, die abgefangen werden kann.[1, 2, 4]&lt;br /&gt;
* &amp;lt;code&amp;gt;PDO::ATTR_DEFAULT_FETCH_MODE =&amp;amp;gt; PDO::FETCH_ASSOC&amp;lt;/code&amp;gt;: Legt fest, dass Daten standardmäßig als assoziatives Array (&amp;lt;code&amp;gt;spaltenname =&amp;amp;gt; wert&amp;lt;/code&amp;gt;) zurückgegeben werden, was oft die Weiterverarbeitung erleichtert.[1, 9]&lt;br /&gt;
* &amp;lt;code&amp;gt;PDO::ATTR_EMULATE_PREPARES =&amp;amp;gt; false&amp;lt;/code&amp;gt;: Deaktiviert die Emulation von Prepared Statements durch PDO und zwingt die Verwendung nativer Prepared Statements des Datenbanktreibers. Dies gilt als die sicherste und oft performanteste Methode.[1, 10, 9, 11] Standardmäßig ist die Emulation aktiviert (&amp;lt;code&amp;gt;true&amp;lt;/code&amp;gt;). Obwohl PDO auch im emulierten Modus durch korrektes Escaping Schutz vor SQL Injection bietet [12, 5, 13], eliminiert die Deaktivierung potenzielle, wenn auch seltene, Sicherheitsrisiken, die in bestimmten Konstellationen (z.B. bei Verwendung exotischer Zeichensätze wie GBK [11]) auftreten könnten. Die explizite Deaktivierung ist daher eine wichtige Best Practice.&lt;br /&gt;
* &amp;lt;code&amp;gt;PDO::ATTR_PERSISTENT =&amp;amp;gt; true&amp;lt;/code&amp;gt;: Aktiviert persistente Datenbankverbindungen. Diese werden nicht am Ende des Skripts geschlossen, sondern in einem Pool gehalten und für nachfolgende Anfragen wiederverwendet. Dies kann die Performance von Webanwendungen verbessern, da der Overhead des Verbindungsaufbaus entfällt. Die Verwendung erfordert jedoch sorgfältige Überlegung, da der Zustand der Verbindung zwischen Anfragen bestehen bleiben kann.[7]&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Fehlerbehandlung:&amp;#039;&amp;#039;&amp;#039; Der Verbindungsaufbau sollte immer in einem &amp;lt;code&amp;gt;try...catch&amp;lt;/code&amp;gt;-Block erfolgen, um &amp;lt;code&amp;gt;PDOException&amp;lt;/code&amp;gt; abzufangen und angemessen darauf zu reagieren (z.B. Fehlermeldung loggen, alternative Aktion ausführen).[1, 7] Es ist kritisch, detaillierte Fehlermeldungen in Produktionsumgebungen nicht an den Benutzer auszugeben, da sie sensible Informationen enthalten könnten. Die PHP-Einstellung &amp;lt;code&amp;gt;display_errors&amp;lt;/code&amp;gt; sollte daher auf &amp;lt;code&amp;gt;0&amp;lt;/code&amp;gt; gesetzt sein.[7]&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Verbindung schließen:&amp;#039;&amp;#039;&amp;#039; Eine PDO-Verbindung bleibt für die Lebensdauer des PDO-Objekts aktiv. Um sie explizit zu schließen, müssen alle Referenzen auf das PDO-Objekt und alle davon abgeleiteten &amp;lt;code&amp;gt;PDOStatement&amp;lt;/code&amp;gt;-Objekte entfernt werden, typischerweise durch Zuweisung von &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt;.[7] PHP schließt die Verbindung automatisch am Ende des Skriptlaufs, wenn dies nicht manuell geschieht.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Code-Beispiel (PDO-Verbindung zu MySQL):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
$host = &amp;#039;localhost&amp;#039;;&lt;br /&gt;
$dbname = &amp;#039;meine_datenbank&amp;#039;;&lt;br /&gt;
$username = &amp;#039;mein_benutzer&amp;#039;;&lt;br /&gt;
$password = &amp;#039;mein_passwort&amp;#039;;&lt;br /&gt;
$charset = &amp;#039;utf8mb4&amp;#039;;&lt;br /&gt;
&lt;br /&gt;
$dsn = &amp;quot;mysql:host=$host;dbname=$dbname;charset=$charset&amp;quot;;&lt;br /&gt;
$options =;&lt;br /&gt;
&lt;br /&gt;
try {&lt;br /&gt;
 $pdo = new PDO($dsn, $username, $password, $options);&lt;br /&gt;
 // Erfolgreich verbunden, $pdo-Objekt kann nun verwendet werden.&lt;br /&gt;
 // echo &amp;quot;Verbindung erfolgreich hergestellt!&amp;quot;;&lt;br /&gt;
} catch (\PDOException $e) {&lt;br /&gt;
 // Fehler beim Verbindungsaufbau&lt;br /&gt;
 // In Produktion: Fehler loggen, statt ausgeben&lt;br /&gt;
 error_log(&amp;quot;Datenbankverbindungsfehler: &amp;quot;. $e---&amp;amp;gt;getMessage());&lt;br /&gt;
 die(&amp;quot;Es gab ein Problem mit der Datenbankverbindung. Bitte versuchen Sie es später erneut.&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//... hier Datenbankoperationen mit $pdo durchführen...&lt;br /&gt;
&lt;br /&gt;
// Verbindung explizit schließen (optional, da PHP dies am Skriptende tut)&lt;br /&gt;
// $pdo = null;&lt;br /&gt;
?&amp;amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Dieses Beispiel zeigt den empfohlenen Weg, eine PDO-Verbindung zu MySQL herzustellen, inklusive wichtiger Optionen für Fehlerbehandlung und Sicherheit sowie einem &amp;lt;code&amp;gt;try...catch&amp;lt;/code&amp;gt;-Block zum Abfangen von Verbindungsfehlern.&lt;br /&gt;
&lt;br /&gt;
=== MySQLi (MySQL Improved) ===&lt;br /&gt;
&lt;br /&gt;
Die MySQLi-Erweiterung ist, wie der Name schon sagt, speziell für die Interaktion mit MySQL-Datenbanken konzipiert und bietet Unterstützung für Funktionen, die ab MySQL Version 4.1 eingeführt wurden.[14, 15] Sie ermöglicht den Zugriff auf MySQL-spezifische Features, die über PDO möglicherweise nicht direkt verfügbar sind.[16, 15]&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Duale Schnittstelle:&amp;#039;&amp;#039;&amp;#039; Ein charakteristisches Merkmal von MySQLi ist die Unterstützung von zwei Programmierstilen [17]:&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Prozedural:&amp;#039;&amp;#039;&amp;#039; Dieser Stil ähnelt den alten &amp;lt;code&amp;gt;mysql_*&amp;lt;/code&amp;gt; Funktionen. Die Funktionsnamen beginnen typischerweise mit &amp;lt;code&amp;gt;mysqli_&amp;lt;/code&amp;gt; (z.B. &amp;lt;code&amp;gt;mysqli_connect()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;mysqli_query()&amp;lt;/code&amp;gt;). Der Datenbankverbindungs-Handle (eine Ressource oder ein Objekt) muss dabei explizit als erster Parameter an die meisten Funktionen übergeben werden.[17] Dieser Stil wird oft von Entwicklern bevorzugt, die von der alten &amp;lt;code&amp;gt;mysql&amp;lt;/code&amp;gt;-Erweiterung migrieren.[17]&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Objektorientiert (OO):&amp;#039;&amp;#039;&amp;#039; Hier wird ein Objekt der &amp;lt;code&amp;gt;mysqli&amp;lt;/code&amp;gt;-Klasse erstellt, und Datenbankoperationen werden als Methoden dieses Objekts aufgerufen (z.B. &amp;lt;code&amp;gt;$mysqli-&amp;amp;gt;query()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;$mysqli-&amp;amp;gt;prepare()&amp;lt;/code&amp;gt;).[17] Dieser Ansatz passt oft besser zu modernen PHP-Entwicklungspraktiken und vermeidet die Notwendigkeit, den Verbindungshandle ständig zu übergeben. Die offizielle PHP-Dokumentation für MySQLi ist primär im objektorientierten Stil verfasst.[17] Es gibt keine nennenswerten Performance-Unterschiede zwischen den beiden Stilen; die Wahl ist meist eine Frage der persönlichen Präferenz oder Projektkonventionen.[17]&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Verbindungsaufbau mit MySQLi:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Prozedural:&amp;#039;&amp;#039;&amp;#039; Die Funktion &amp;lt;code&amp;gt;mysqli_connect()&amp;lt;/code&amp;gt; wird verwendet: &amp;lt;code&amp;gt;$link = mysqli_connect($host, $user, $pass, $db, $port, $socket);&amp;lt;/code&amp;gt;. Sie gibt bei Erfolg ein &amp;lt;code&amp;gt;mysqli&amp;lt;/code&amp;gt;-Objekt (oder &amp;lt;code&amp;gt;false&amp;lt;/code&amp;gt; bei Fehlern vor PHP 8.1) zurück.[17, 18] Fehler können mit &amp;lt;code&amp;gt;mysqli_connect_errno()&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;mysqli_connect_error()&amp;lt;/code&amp;gt; geprüft werden.[18]&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Objektorientiert:&amp;#039;&amp;#039;&amp;#039; Ein neues &amp;lt;code&amp;gt;mysqli&amp;lt;/code&amp;gt;-Objekt wird instanziiert: &amp;lt;code&amp;gt;$mysqli = new mysqli($host, $user, $pass, $db, $port, $socket);&amp;lt;/code&amp;gt;. Wichtig: Der Konstruktor gibt &amp;#039;&amp;#039;immer&amp;#039;&amp;#039; ein Objekt zurück, auch wenn die Verbindung fehlschlägt. Der Verbindungsstatus muss explizit über die Eigenschaften &amp;lt;code&amp;gt;$mysqli-&amp;amp;gt;connect_errno&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;$mysqli-&amp;amp;gt;connect_error&amp;lt;/code&amp;gt; geprüft werden.[17, 18]&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Fortgeschrittene Verbindung:&amp;#039;&amp;#039;&amp;#039; Für feinere Kontrolle über Verbindungsoptionen (z.B. Setzen von Timeouts oder Initialisierungsbefehlen &amp;#039;&amp;#039;vor&amp;#039;&amp;#039; dem Verbindungsaufbau) können &amp;lt;code&amp;gt;mysqli_init()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;mysqli_options()&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;mysqli_real_connect()&amp;lt;/code&amp;gt; verwendet werden.[18, 19]&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Fehlerbehandlung:&amp;#039;&amp;#039;&amp;#039; Neben der manuellen Prüfung von Fehlercodes und -meldungen ist die empfohlene Methode, MySQLi so zu konfigurieren, dass es bei Fehlern Exceptions wirft. Dies geschieht mit &amp;lt;code&amp;gt;mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);&amp;lt;/code&amp;gt;. Danach können Datenbankfehler elegant mit &amp;lt;code&amp;gt;try...catch&amp;lt;/code&amp;gt;-Blöcken behandelt werden, ähnlich wie bei PDO.[18, 20, 21, 22, 23, 24, 25, 26]&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Verbindung schließen:&amp;#039;&amp;#039;&amp;#039; Prozedural mit &amp;lt;code&amp;gt;mysqli_close($link)&amp;lt;/code&amp;gt;, objektorientiert mit &amp;lt;code&amp;gt;$mysqli-&amp;amp;gt;close()&amp;lt;/code&amp;gt;.[17, 18, 19]&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Persistente Verbindungen:&amp;#039;&amp;#039;&amp;#039; MySQLi unterstützt ebenfalls persistente Verbindungen, die über PHP-Konfigurationseinstellungen (&amp;lt;code&amp;gt;mysqli.allow_persistent&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;mysqli.max_persistent&amp;lt;/code&amp;gt;) gesteuert werden.[27] Standardmäßig setzt MySQLi den Zustand einer wiederverwendeten persistenten Verbindung zurück (via &amp;lt;code&amp;gt;mysqli::change_user()&amp;lt;/code&amp;gt;), was zwar für Konsistenz sorgt, aber auch Performance kosten kann.[27]&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Code-Beispiele (MySQLi-Verbindungen):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Objektorientiert (mit Exception-Handling):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
$host = &amp;#039;localhost&amp;#039;;&lt;br /&gt;
$user = &amp;#039;mein_benutzer&amp;#039;;&lt;br /&gt;
$pass = &amp;#039;mein_passwort&amp;#039;;&lt;br /&gt;
$db = &amp;#039;meine_datenbank&amp;#039;;&lt;br /&gt;
&lt;br /&gt;
// Fehlerberichterstattung für Exceptions aktivieren&lt;br /&gt;
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);&lt;br /&gt;
&lt;br /&gt;
try {&lt;br /&gt;
 $mysqli = new mysqli($host, $user, $pass, $db);&lt;br /&gt;
 // Zeichensatz setzen (wichtig!)&lt;br /&gt;
 $mysqli---&amp;amp;gt;set_charset(&amp;#039;utf8mb4&amp;#039;);&lt;br /&gt;
 // Erfolgreich verbunden&lt;br /&gt;
 // echo &amp;quot;Verbindung erfolgreich (OO)! Host-Info: &amp;quot;. $mysqli-&amp;amp;gt;host_info;&lt;br /&gt;
} catch (mysqli_sql_exception $e) {&lt;br /&gt;
 // Fehler beim Verbindungsaufbau oder set_charset&lt;br /&gt;
 error_log(&amp;quot;MySQLi Verbindungsfehler: &amp;quot;. $e-&amp;amp;gt;getMessage());&lt;br /&gt;
 die(&amp;quot;Es gab ein Problem mit der Datenbankverbindung.&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//... Datenbankoperationen mit $mysqli...&lt;br /&gt;
&lt;br /&gt;
// Verbindung schließen&lt;br /&gt;
// $mysqli-&amp;amp;gt;close();&lt;br /&gt;
?&amp;amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Prozedural (mit Exception-Handling):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!--?php$host = &amp;#039;localhost&amp;#039;;&lt;br /&gt;
$user = &amp;#039;mein_benutzer&amp;#039;;&lt;br /&gt;
$pass = &amp;#039;mein_passwort&amp;#039;;&lt;br /&gt;
$db = &amp;#039;meine_datenbank&amp;#039;;&lt;br /&gt;
&lt;br /&gt;
// Fehlerberichterstattung für Exceptions aktivieren&lt;br /&gt;
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);&lt;br /&gt;
&lt;br /&gt;
try {&lt;br /&gt;
    $link = mysqli_connect($host, $user, $pass, $db);&lt;br /&gt;
    // Zeichensatz setzen&lt;br /&gt;
    mysqli_set_charset($link, &amp;#039;utf8mb4&amp;#039;);&lt;br /&gt;
    // Erfolgreich verbunden&lt;br /&gt;
    // echo &amp;quot;Verbindung erfolgreich (Prozedural)! Host-Info: &amp;quot;. mysqli_get_host_info($link);&lt;br /&gt;
} catch (mysqli_sql_exception $e) {&lt;br /&gt;
    // Fehler beim Verbindungsaufbau oder set_charset&lt;br /&gt;
    error_log(&amp;quot;MySQLi Verbindungsfehler: &amp;quot;. $e---&amp;gt;getMessage());&lt;br /&gt;
 die(&amp;quot;Es gab ein Problem mit der Datenbankverbindung.&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//... Datenbankoperationen mit $link...&lt;br /&gt;
&lt;br /&gt;
// Verbindung schließen&lt;br /&gt;
// mysqli_close($link);&lt;br /&gt;
?&amp;amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Bedeutung von &amp;lt;code&amp;gt;mysqlnd&amp;lt;/code&amp;gt;:&amp;#039;&amp;#039;&amp;#039; Die Benutzerfreundlichkeit, insbesondere bei der Arbeit mit Prepared Statements, wird stark durch den verwendeten MySQL-Treiber beeinflusst. Der &amp;#039;&amp;#039;&amp;#039;MySQL Native Driver (&amp;lt;code&amp;gt;mysqlnd&amp;lt;/code&amp;gt;)&amp;#039;&amp;#039;&amp;#039;, der seit PHP 5.4 standardmäßig enthalten ist [16], ist hier entscheidend. &amp;lt;code&amp;gt;mysqlnd&amp;lt;/code&amp;gt; stellt die Methode &amp;lt;code&amp;gt;get_result()&amp;lt;/code&amp;gt; (bzw. die Funktion &amp;lt;code&amp;gt;mysqli_stmt_get_result()&amp;lt;/code&amp;gt;) zur Verfügung.[25, 15] Diese Methode erlaubt es, nach dem Ausführen eines Prepared Statements ein Standard-&amp;lt;code&amp;gt;mysqli_result&amp;lt;/code&amp;gt;-Objekt zu erhalten, von dem dann wie gewohnt mit &amp;lt;code&amp;gt;fetch_assoc()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;fetch_all()&amp;lt;/code&amp;gt; etc. die Ergebnisse abgerufen werden können.[25] Ohne &amp;lt;code&amp;gt;mysqlnd&amp;lt;/code&amp;gt; (bei Verwendung der älteren &amp;lt;code&amp;gt;libmysqlclient&amp;lt;/code&amp;gt;-Bibliothek) ist das Abrufen von Ergebnissen aus Prepared Statements deutlich umständlicher und erfordert die manuelle Bindung jeder einzelnen Ergebnisspalte an PHP-Variablen mittels &amp;lt;code&amp;gt;bind_result()&amp;lt;/code&amp;gt; und anschließendes &amp;lt;code&amp;gt;fetch()&amp;lt;/code&amp;gt;.[25] Da &amp;lt;code&amp;gt;mysqlnd&amp;lt;/code&amp;gt; heute der Standard ist, konzentrieren sich moderne Tutorials und Beispiele meist auf die &amp;lt;code&amp;gt;get_result()&amp;lt;/code&amp;gt;-Methode, aber es ist wichtig, diesen Hintergrund zu kennen, falls man auf ältere Systeme oder Konfigurationen trifft.&lt;br /&gt;
&lt;br /&gt;
=== Veraltete &amp;lt;code&amp;gt;mysql_*&amp;lt;/code&amp;gt; Funktionen ===&lt;br /&gt;
&lt;br /&gt;
Die &amp;lt;code&amp;gt;mysql_*&amp;lt;/code&amp;gt; Funktionsfamilie (&amp;lt;code&amp;gt;mysql_connect&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;mysql_query&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;mysql_fetch_assoc&amp;lt;/code&amp;gt; etc.) war die ursprüngliche Methode, um in PHP mit MySQL-Datenbanken zu interagieren. Diese Erweiterung ist jedoch &amp;#039;&amp;#039;&amp;#039;stark veraltet&amp;#039;&amp;#039;&amp;#039; und sollte &amp;#039;&amp;#039;&amp;#039;unter keinen Umständen mehr für neue Projekte verwendet werden&amp;#039;&amp;#039;&amp;#039;.&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Status:&amp;#039;&amp;#039;&amp;#039; Die &amp;lt;code&amp;gt;mysql_*&amp;lt;/code&amp;gt; Erweiterung wurde in &amp;#039;&amp;#039;&amp;#039;PHP 5.5&amp;#039;&amp;#039;&amp;#039; offiziell als &amp;quot;deprecated&amp;quot; (veraltet) markiert, was bedeutet, dass ihre Verwendung ab dieser Version Warnungen (&amp;lt;code&amp;gt;E_DEPRECATED&amp;lt;/code&amp;gt;) erzeugte.[28, 29] Mit der Veröffentlichung von &amp;#039;&amp;#039;&amp;#039;PHP 7.0&amp;#039;&amp;#039;&amp;#039; wurde die Erweiterung &amp;#039;&amp;#039;&amp;#039;vollständig entfernt&amp;#039;&amp;#039;&amp;#039;.[28, 29] PHP-Code, der diese Funktionen nutzt, führt auf PHP 7.0 oder neuer zu fatalen Fehlern und funktioniert nicht mehr.[28, 29]&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Gründe für die Entfernung:&amp;#039;&amp;#039;&amp;#039; Es gab zwingende Gründe für diesen Schritt:&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Gravierende Sicherheitsrisiken:&amp;#039;&amp;#039;&amp;#039; Der Hauptgrund war die Anfälligkeit für SQL-Injection-Angriffe. Die &amp;lt;code&amp;gt;mysql_*&amp;lt;/code&amp;gt; Funktionen boten keinen eingebauten Mechanismus wie Prepared Statements. Entwickler mussten Benutzereingaben manuell mit &amp;lt;code&amp;gt;mysql_real_escape_string()&amp;lt;/code&amp;gt; &amp;quot;escapen&amp;quot;, eine Methode, die leicht vergessen oder falsch angewendet werden konnte, was zu weit verbreiteten Sicherheitslücken führte.[29, 30] Das Fehlen von Prepared Statements in dieser alten Erweiterung ist direkt ursächlich für die hohe Zahl an SQL-Injection-Schwachstellen in älteren PHP-Anwendungen.[29]&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Fehlende Unterstützung moderner MySQL-Features:&amp;#039;&amp;#039;&amp;#039; Die Erweiterung wurde ursprünglich für MySQL Version 3.23 entwickelt und seitdem kaum weiterentwickelt.[29] Sie unterstützte viele wichtige Funktionen moderner MySQL-Versionen nicht, darunter Prepared Statements, Stored Procedures, Transaktionen, SSL-Verschlüsselung, Kompression, Multi-Statements und vollständige Zeichensatzunterstützung.[29]&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Schlechte Wartbarkeit:&amp;#039;&amp;#039;&amp;#039; Der Code der Erweiterung war veraltet und wurde immer schwieriger zu warten und an neue Versionen der MySQL-Client-Bibliotheken anzupassen.[29]&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Förderung unsicherer Praktiken:&amp;#039;&amp;#039;&amp;#039; Da viele alte Tutorials und Codebeispiele die &amp;lt;code&amp;gt;mysql_*&amp;lt;/code&amp;gt; Funktionen unsicher verwendeten, trug die fortgesetzte Verfügbarkeit zur Verbreitung schlechter und unsicherer Programmierpraktiken bei.[29]&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Verwendung heute:&amp;#039;&amp;#039;&amp;#039; Die klare Antwort lautet: &amp;#039;&amp;#039;&amp;#039;Nein&amp;#039;&amp;#039;&amp;#039;. Diese Funktionen existieren in modernen PHP-Versionen nicht mehr.[31, 30] Jede Anwendung, die sie noch verwendet, ist nicht nur unsicher, sondern auch inkompatibel mit aktueller PHP-Software. Eine Migration zu PDO oder MySQLi ist unumgänglich, um Sicherheit und Kompatibilität zu gewährleisten.[28, 29] Die erzwungene Migration durch die Entfernung in PHP 7.0 unterstreicht, wie wichtig es ist, Deprecation-Warnungen ernst zu nehmen und Code proaktiv zu aktualisieren, um zukünftige Probleme zu vermeiden.[29, 32]&lt;br /&gt;
&lt;br /&gt;
=== Tabelle 1: Vergleich der PHP-Datenbankerweiterungen für MySQL ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Merkmal !! PDO (PHP Data Objects) !! MySQLi (MySQL Improved) !! &amp;lt;code&amp;gt;mysql_*&amp;lt;/code&amp;gt; (Veraltet)&lt;br /&gt;
|-&amp;lt;br&amp;gt;&lt;br /&gt;
| aktiv=&amp;quot;&amp;quot; empfohlen=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| aktiv=&amp;quot;&amp;quot; empfohlen=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Entfernt (seit PHP 7.0) [29]&lt;br /&gt;
|-&amp;lt;br&amp;gt;&lt;br /&gt;
| objektorientiert=&amp;quot;&amp;quot; oo=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| oo=&amp;quot;&amp;quot; prozedural=&amp;quot;&amp;quot; 17=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Prozedural&lt;br /&gt;
|-&amp;lt;br&amp;gt;&lt;br /&gt;
| multi-datenbank=&amp;quot;&amp;quot; mysql=&amp;quot;&amp;quot; pgsql=&amp;quot;&amp;quot; sqlite=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| nur=&amp;quot;&amp;quot; mysql=&amp;quot;&amp;quot; 15=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Nur MySQL&lt;br /&gt;
|-&amp;lt;br&amp;gt;&lt;br /&gt;
| ja=&amp;quot;&amp;quot; nativ=&amp;quot;&amp;quot; empfohlen=&amp;quot;&amp;quot; emulation=&amp;quot;&amp;quot; m=&amp;quot;&amp;quot; glich=&amp;quot;&amp;quot; 1=&amp;quot;&amp;quot; 11=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| ja=&amp;quot;&amp;quot; nativ=&amp;quot;&amp;quot; 25=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Nein [29]&lt;br /&gt;
|- &lt;br /&gt;
| &amp;#039;&amp;#039;&amp;#039;SQL Injection Schutz&amp;#039;&amp;#039;&amp;#039; || sehr=&amp;quot;&amp;quot; gut=&amp;quot;&amp;quot; durch=&amp;quot;&amp;quot; prepared=&amp;quot;&amp;quot; statements=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Sehr gut (durch Prepared Statements) [25] || Schlecht (manuelles Escaping nötig) [29]&lt;br /&gt;
|-&amp;lt;br&amp;gt;&lt;br /&gt;
| gute=&amp;quot;&amp;quot; unterst=&amp;quot;&amp;quot; tzung=&amp;quot;&amp;quot; via=&amp;quot;&amp;quot; treiber=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Sehr gute Unterstützung (spezifisch) [15] || Veraltet, viele Features fehlen [29]&lt;br /&gt;
|-&amp;lt;br&amp;gt;&lt;br /&gt;
| hoch=&amp;quot;&amp;quot; 2=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| niedrig=&amp;quot;&amp;quot; mysql-spezifisch=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Niedrig&lt;br /&gt;
|-&amp;lt;br&amp;gt;&lt;br /&gt;
| exceptions=&amp;quot;&amp;quot; mwt-preservehtml=&amp;quot;&amp;quot; pdoexception=&amp;quot;&amp;quot; code=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| exceptions=&amp;quot;&amp;quot; mit=&amp;quot;&amp;quot; mwt-preservehtml=&amp;quot;&amp;quot; mysqli_report=&amp;quot;&amp;quot; code=&amp;quot;&amp;quot; fehlercodes=&amp;quot;&amp;quot; 18=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Fehlercodes / &amp;lt;code&amp;gt;mysql_error()&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
| &amp;#039;&amp;#039;&amp;#039;Nicht verwenden!&amp;#039;&amp;#039;&amp;#039; [29]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Grundlegende Datenbankoperationen (CRUD) ==&lt;br /&gt;
&lt;br /&gt;
Nachdem wir die verschiedenen Erweiterungen kennengelernt haben, wenden wir uns nun den grundlegenden Operationen zu, die man typischerweise mit einer Datenbank durchführt. Diese werden oft mit dem Akronym CRUD zusammengefasst:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;C&amp;#039;&amp;#039;&amp;#039;reate: Neue Datensätze erstellen (SQL: &amp;lt;code&amp;gt;INSERT&amp;lt;/code&amp;gt;).&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;R&amp;#039;&amp;#039;&amp;#039;ead: Vorhandene Datensätze lesen/abfragen (SQL: &amp;lt;code&amp;gt;SELECT&amp;lt;/code&amp;gt;).&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;U&amp;#039;&amp;#039;&amp;#039;pdate: Bestehende Datensätze ändern (SQL: &amp;lt;code&amp;gt;UPDATE&amp;lt;/code&amp;gt;).&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;D&amp;#039;&amp;#039;&amp;#039;elete: Datensätze löschen (SQL: &amp;lt;code&amp;gt;DELETE&amp;lt;/code&amp;gt;).&lt;br /&gt;
In den folgenden Abschnitten werden wir sehen, wie diese Operationen mit den modernen Erweiterungen PDO und MySQLi umgesetzt werden, wobei wir durchgehend &amp;#039;&amp;#039;&amp;#039;Prepared Statements&amp;#039;&amp;#039;&amp;#039; verwenden, um die Sicherheit zu gewährleisten.&lt;br /&gt;
&lt;br /&gt;
=== Daten abfragen (SELECT) ===&lt;br /&gt;
&lt;br /&gt;
Das Abfragen von Daten ist eine der häufigsten Datenbankoperationen.&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;PDO:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Ohne Parameter:&amp;#039;&amp;#039;&amp;#039; Wenn die Abfrage keine variablen Teile enthält (z.B. keine &amp;lt;code&amp;gt;WHERE&amp;lt;/code&amp;gt;-Klausel mit Benutzereingaben), kann die &amp;lt;code&amp;gt;query()&amp;lt;/code&amp;gt;-Methode verwendet werden. Sie führt die SQL-Abfrage direkt aus und gibt ein &amp;lt;code&amp;gt;PDOStatement&amp;lt;/code&amp;gt;-Objekt zurück, das zur Ergebnisauswertung dient.[2, 33]&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Beispiel: Alle Benutzer auswählen&lt;br /&gt;
$stmt = $pdo-&amp;amp;gt;query(&amp;quot;SELECT id, username FROM users&amp;quot;);&lt;br /&gt;
// $stmt kann nun zum Fetchen der Ergebnisse verwendet werden&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Mit Parametern (Prepared Statement):&amp;#039;&amp;#039;&amp;#039; Dies ist der Standardfall, wenn Benutzereingaben oder andere Variablen die Abfrage beeinflussen (z.B. in &amp;lt;code&amp;gt;WHERE&amp;lt;/code&amp;gt;- oder &amp;lt;code&amp;gt;LIMIT&amp;lt;/code&amp;gt;-Klauseln).&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Vorbereiten (&amp;lt;code&amp;gt;prepare&amp;lt;/code&amp;gt;):&amp;#039;&amp;#039;&amp;#039; Die SQL-Abfrage wird mit Platzhaltern (&amp;lt;code&amp;gt;?&amp;lt;/code&amp;gt; für positionale oder &amp;lt;code&amp;gt;:name&amp;lt;/code&amp;gt; für benannte Platzhalter) vorbereitet.[34, 33, 5]&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Beispiel: Benutzer mit bestimmter ID auswählen (positionaler Platzhalter)&lt;br /&gt;
$sql = &amp;quot;SELECT id, username, email FROM users WHERE id =?&amp;quot;;&lt;br /&gt;
$stmt = $pdo-&amp;amp;gt;prepare($sql);&lt;br /&gt;
&lt;br /&gt;
// Beispiel: Benutzer mit bestimmtem Status auswählen (benannter Platzhalter)&lt;br /&gt;
$sql_named = &amp;quot;SELECT id, username FROM users WHERE status = :status&amp;quot;;&lt;br /&gt;
$stmt_named = $pdo-&amp;amp;gt;prepare($sql_named);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
*# &amp;#039;&amp;#039;&amp;#039;Ausführen (&amp;lt;code&amp;gt;execute&amp;lt;/code&amp;gt;):&amp;#039;&amp;#039;&amp;#039; Die vorbereitete Anweisung wird mit den tatsächlichen Werten ausgeführt. Die Werte werden als Array an die &amp;lt;code&amp;gt;execute()&amp;lt;/code&amp;gt;-Methode übergeben. Bei positionalen Platzhaltern entspricht die Reihenfolge im Array der Reihenfolge der &amp;lt;code&amp;gt;?&amp;lt;/code&amp;gt; in der SQL-Abfrage. Bei benannten Platzhaltern wird ein assoziatives Array verwendet, dessen Schlüssel den Platzhalternamen entsprechen (mit oder ohne führenden Doppelpunkt).[34, 33, 5]&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Ausführen für positionalen Platzhalter&lt;br /&gt;
$user_id = 123;&lt;br /&gt;
$stmt-&amp;amp;gt;execute([$user_id]);&lt;br /&gt;
&lt;br /&gt;
// Ausführen für benannten Platzhalter&lt;br /&gt;
$user_status = &amp;#039;active&amp;#039;;&lt;br /&gt;
$stmt_named-&amp;amp;gt;execute([&amp;#039;:status&amp;#039; =&amp;amp;gt; $user_status]);&lt;br /&gt;
// oder: $stmt_named-&amp;amp;gt;execute([&amp;#039;status&amp;#039; =&amp;amp;gt; $user_status]);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
*# &amp;#039;&amp;#039;&amp;#039;Alternative Bindung:&amp;#039;&amp;#039;&amp;#039; Optional können Parameter auch explizit vor dem &amp;lt;code&amp;gt;execute()&amp;lt;/code&amp;gt;-Aufruf mit &amp;lt;code&amp;gt;bindValue()&amp;lt;/code&amp;gt; (bindet einen Wert) oder &amp;lt;code&amp;gt;bindParam()&amp;lt;/code&amp;gt; (bindet eine Variable als Referenz) gebunden werden. Dies gibt mehr Kontrolle über den Datentyp (z.B. &amp;lt;code&amp;gt;PDO::PARAM_INT&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;PDO::PARAM_STR&amp;lt;/code&amp;gt;).[5, 13]&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$stmt-&amp;amp;gt;bindValue(1, $user_id, PDO::PARAM_INT); // Position 1, Wert von $user_id als Integer&lt;br /&gt;
$stmt-&amp;amp;gt;execute();&lt;br /&gt;
&lt;br /&gt;
$stmt_named-&amp;amp;gt;bindParam(&amp;#039;:status&amp;#039;, $user_status, PDO::PARAM_STR); // Platzhalter :status, Variable $user_status als String&lt;br /&gt;
$stmt_named-&amp;amp;gt;execute();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;MySQLi:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Ohne Parameter:&amp;#039;&amp;#039;&amp;#039; Ähnlich wie bei PDO kann bei Abfragen ohne variable Teile die &amp;lt;code&amp;gt;query()&amp;lt;/code&amp;gt;-Methode (OO-Stil: &amp;lt;code&amp;gt;$mysqli-&amp;amp;gt;query()&amp;lt;/code&amp;gt;) oder die &amp;lt;code&amp;gt;mysqli_query()&amp;lt;/code&amp;gt;-Funktion (prozeduraler Stil: &amp;lt;code&amp;gt;mysqli_query($link, &amp;quot;...&amp;quot;)&amp;lt;/code&amp;gt;) verwendet werden. Sie geben ein &amp;lt;code&amp;gt;mysqli_result&amp;lt;/code&amp;gt;-Objekt zurück.[20, 25]&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// OO-Stil&lt;br /&gt;
$result = $mysqli-&amp;amp;gt;query(&amp;quot;SELECT id, username FROM users&amp;quot;);&lt;br /&gt;
// Prozedural&lt;br /&gt;
$result = mysqli_query($link, &amp;quot;SELECT id, username FROM users&amp;quot;);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Mit Parametern (Prepared Statement):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Vorbereiten (&amp;lt;code&amp;gt;prepare&amp;lt;/code&amp;gt;):&amp;#039;&amp;#039;&amp;#039; Die SQL-Abfrage wird mit &amp;lt;code&amp;gt;?&amp;lt;/code&amp;gt;-Platzhaltern vorbereitet. Im OO-Stil &amp;lt;code&amp;gt;$stmt = $mysqli-&amp;amp;gt;prepare(&amp;quot;...&amp;quot;)&amp;lt;/code&amp;gt;, prozedural &amp;lt;code&amp;gt;$stmt = mysqli_prepare($link, &amp;quot;...&amp;quot;)&amp;lt;/code&amp;gt;.[25, 35]&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// OO-Stil&lt;br /&gt;
$sql = &amp;quot;SELECT id, username, email FROM users WHERE id =?&amp;quot;;&lt;br /&gt;
$stmt = $mysqli-&amp;amp;gt;prepare($sql);&lt;br /&gt;
// Prozedural&lt;br /&gt;
$sql_proc = &amp;quot;SELECT id, username, email FROM users WHERE id =?&amp;quot;;&lt;br /&gt;
$stmt_proc = mysqli_prepare($link, $sql_proc);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
**# &amp;#039;&amp;#039;&amp;#039;Parameter binden (&amp;lt;code&amp;gt;bind_param&amp;lt;/code&amp;gt;):&amp;#039;&amp;#039;&amp;#039; Die Variablen werden an die Platzhalter gebunden. Dies ist ein wesentlicher Unterschied zu PDOs &amp;lt;code&amp;gt;execute&amp;lt;/code&amp;gt;-Array-Bindung. &amp;lt;code&amp;gt;bind_param()&amp;lt;/code&amp;gt; erfordert einen String, der die Datentypen der zu bindenden Variablen angibt (z.B. &amp;quot;i&amp;quot; für Integer, &amp;quot;s&amp;quot; für String, &amp;quot;d&amp;quot; für Double/Float, &amp;quot;b&amp;quot; für Blob), gefolgt von den Variablen selbst.[25, 35]&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$user_id = 123;&lt;br /&gt;
// OO-Stil&lt;br /&gt;
$stmt-&amp;amp;gt;bind_param(&amp;quot;i&amp;quot;, $user_id); // &amp;quot;i&amp;quot; für Integer&lt;br /&gt;
// Prozedural&lt;br /&gt;
mysqli_stmt_bind_param($stmt_proc, &amp;quot;i&amp;quot;, $user_id);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
**# &amp;#039;&amp;#039;&amp;#039;Ausführen (&amp;lt;code&amp;gt;execute&amp;lt;/code&amp;gt;):&amp;#039;&amp;#039;&amp;#039; Die vorbereitete Anweisung wird ausgeführt. OO-Stil: &amp;lt;code&amp;gt;$stmt-&amp;amp;gt;execute()&amp;lt;/code&amp;gt;, prozedural: &amp;lt;code&amp;gt;mysqli_stmt_execute($stmt)&amp;lt;/code&amp;gt;.[25, 36, 35]&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// OO-Stil&lt;br /&gt;
$stmt-&amp;amp;gt;execute();&lt;br /&gt;
// Prozedural&lt;br /&gt;
mysqli_stmt_execute($stmt_proc);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
**# &amp;#039;&amp;#039;&amp;#039;Ergebnis holen:&amp;#039;&amp;#039;&amp;#039; Um die Ergebnisse eines Prepared Statements in MySQLi zu erhalten, ist die Methode &amp;lt;code&amp;gt;get_result()&amp;lt;/code&amp;gt; (OO) bzw. &amp;lt;code&amp;gt;mysqli_stmt_get_result()&amp;lt;/code&amp;gt; (prozedural) am bequemsten. Sie gibt ein Standard-&amp;lt;code&amp;gt;mysqli_result&amp;lt;/code&amp;gt;-Objekt zurück, das dann mit den üblichen Fetch-Methoden verarbeitet werden kann. &amp;#039;&amp;#039;&amp;#039;Wichtig:&amp;#039;&amp;#039;&amp;#039; Diese Methode erfordert den &amp;lt;code&amp;gt;mysqlnd&amp;lt;/code&amp;gt;-Treiber.[25, 15]&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// OO-Stil&lt;br /&gt;
$result = $stmt-&amp;amp;gt;get_result();&lt;br /&gt;
// Prozedural&lt;br /&gt;
$result_proc = mysqli_stmt_get_result($stmt_proc);&lt;br /&gt;
// $result / $result_proc kann nun mit fetch_assoc() etc. verwendet werden.&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Die Alternative ohne &amp;lt;code&amp;gt;mysqlnd&amp;lt;/code&amp;gt; ist &amp;lt;code&amp;gt;bind_result()&amp;lt;/code&amp;gt;, bei der jede Ergebnisspalte an eine PHP-Variable gebunden wird, und &amp;lt;code&amp;gt;fetch()&amp;lt;/code&amp;gt; iterativ aufgerufen wird, um die Variablen zu füllen.[25] Dies ist deutlich umständlicher.&lt;br /&gt;
&lt;br /&gt;
=== Ergebnisse abrufen und verarbeiten ===&lt;br /&gt;
&lt;br /&gt;
Nachdem eine &amp;lt;code&amp;gt;SELECT&amp;lt;/code&amp;gt;-Abfrage erfolgreich ausgeführt wurde, müssen die zurückgegebenen Daten aus dem Ergebnisobjekt (&amp;lt;code&amp;gt;PDOStatement&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;mysqli_result&amp;lt;/code&amp;gt;) abgerufen (&amp;quot;gefetched&amp;quot;) werden.&lt;br /&gt;
&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Zeilenweise Iteration (&amp;lt;code&amp;gt;fetch&amp;lt;/code&amp;gt; im Loop):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
Dies ist der klassische Weg, um Ergebnisse zu verarbeiten, insbesondere wenn jede Zeile einzeln behandelt werden muss oder wenn die Ergebnismenge sehr groß ist.&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;PDO:&amp;#039;&amp;#039;&amp;#039; Die &amp;lt;code&amp;gt;fetch()&amp;lt;/code&amp;gt;-Methode des &amp;lt;code&amp;gt;PDOStatement&amp;lt;/code&amp;gt;-Objekts wird wiederholt in einer &amp;lt;code&amp;gt;while&amp;lt;/code&amp;gt;-Schleife aufgerufen. &amp;lt;code&amp;gt;fetch()&amp;lt;/code&amp;gt; gibt bei jedem Aufruf die nächste Zeile zurück (im Format des eingestellten Fetch-Modus, z.B. &amp;lt;code&amp;gt;PDO::FETCH_ASSOC&amp;lt;/code&amp;gt;) und &amp;lt;code&amp;gt;false&amp;lt;/code&amp;gt; (oder &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt; je nach Modus/Version), wenn keine weiteren Zeilen vorhanden sind.[2, 33]&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$stmt = $pdo-&amp;amp;gt;prepare(&amp;quot;SELECT name, email FROM users WHERE status =?&amp;quot;);&lt;br /&gt;
$stmt-&amp;amp;gt;execute([&amp;#039;active&amp;#039;]);&lt;br /&gt;
while ($row = $stmt-&amp;amp;gt;fetch(PDO::FETCH_ASSOC)) {&lt;br /&gt;
 echo &amp;quot;Name: &amp;quot;. htmlspecialchars($row[&amp;#039;name&amp;#039;]). &amp;quot;, E-Mail: &amp;quot;. htmlspecialchars($row[&amp;#039;email&amp;#039;]). &amp;quot;&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;MySQLi:&amp;#039;&amp;#039;&amp;#039; Die &amp;lt;code&amp;gt;fetch_assoc()&amp;lt;/code&amp;gt;-Methode (oder &amp;lt;code&amp;gt;fetch_row()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;fetch_object()&amp;lt;/code&amp;gt;, etc.) des &amp;lt;code&amp;gt;mysqli_result&amp;lt;/code&amp;gt;-Objekts wird in einer &amp;lt;code&amp;gt;while&amp;lt;/code&amp;gt;-Schleife verwendet. Sie gibt die nächste Zeile als Array (oder Objekt) zurück oder &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt;, wenn das Ende erreicht ist.[25, 15]&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// OO-Stil (nach $result = $stmt-&amp;amp;gt;get_result();)&lt;br /&gt;
while ($row = $result-&amp;amp;gt;fetch_assoc()) {&lt;br /&gt;
 echo &amp;quot;Name: &amp;quot;. htmlspecialchars($row[&amp;#039;name&amp;#039;]). &amp;quot;, E-Mail: &amp;quot;. htmlspecialchars($row[&amp;#039;email&amp;#039;]). &amp;quot;&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Prozedural (nach $result = mysqli_stmt_get_result($stmt);)&lt;br /&gt;
while ($row = mysqli_fetch_assoc($result)) {&lt;br /&gt;
 echo &amp;quot;Name: &amp;quot;. htmlspecialchars($row[&amp;#039;name&amp;#039;]). &amp;quot;, E-Mail: &amp;quot;. htmlspecialchars($row[&amp;#039;email&amp;#039;]). &amp;quot;&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Dieser Ansatz ist &amp;#039;&amp;#039;&amp;#039;speichereffizient&amp;#039;&amp;#039;&amp;#039;, da immer nur eine Zeile gleichzeitig im PHP-Speicher gehalten wird. Dies ist besonders wichtig bei Abfragen, die potenziell Tausende oder Millionen von Zeilen zurückgeben könnten.[37]&lt;br /&gt;
&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Alle Ergebnisse auf einmal abrufen (&amp;lt;code&amp;gt;fetchAll&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;fetch_all&amp;lt;/code&amp;gt;):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
Für kleinere bis mittlere Ergebnismengen ist es oft bequemer, alle Zeilen auf einmal in ein PHP-Array zu laden.&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;PDO:&amp;#039;&amp;#039;&amp;#039; Die &amp;lt;code&amp;gt;fetchAll()&amp;lt;/code&amp;gt;-Methode des &amp;lt;code&amp;gt;PDOStatement&amp;lt;/code&amp;gt;-Objekts gibt ein Array zurück, das alle Ergebniszeilen enthält.[33, 38] Der Fetch-Modus (z.B. &amp;lt;code&amp;gt;PDO::FETCH_ASSOC&amp;lt;/code&amp;gt;) kann als Argument übergeben werden.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$stmt = $pdo-&amp;amp;gt;prepare(&amp;quot;SELECT name, email FROM users WHERE status =?&amp;quot;);&lt;br /&gt;
$stmt-&amp;amp;gt;execute([&amp;#039;active&amp;#039;]);&lt;br /&gt;
$results = $stmt-&amp;amp;gt;fetchAll(PDO::FETCH_ASSOC);&lt;br /&gt;
// $results ist nun ein Array von assoziativen Arrays&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;MySQLi:&amp;#039;&amp;#039;&amp;#039; Die &amp;lt;code&amp;gt;fetch_all()&amp;lt;/code&amp;gt;-Methode des &amp;lt;code&amp;gt;mysqli_result&amp;lt;/code&amp;gt;-Objekts (verfügbar mit &amp;lt;code&amp;gt;mysqlnd&amp;lt;/code&amp;gt;) tut dasselbe. Der Modus (&amp;lt;code&amp;gt;MYSQLI_ASSOC&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;MYSQLI_NUM&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;MYSQLI_BOTH&amp;lt;/code&amp;gt;) wird als Argument übergeben.[25, 39]&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// OO-Stil (nach $result = $stmt-&amp;amp;gt;get_result();)&lt;br /&gt;
$results = $result-&amp;amp;gt;fetch_all(MYSQLI_ASSOC);&lt;br /&gt;
&lt;br /&gt;
// Prozedural (nach $result = mysqli_stmt_get_result($stmt);)&lt;br /&gt;
$results = mysqli_fetch_all($result, MYSQLI_ASSOC);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Der &amp;#039;&amp;#039;&amp;#039;Vorteil&amp;#039;&amp;#039;&amp;#039; dieser Methode liegt in der einfacheren Weiterverarbeitung des Ergebnis-Arrays mit Standard-PHP-Array-Funktionen wie &amp;lt;code&amp;gt;foreach&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;count&amp;lt;/code&amp;gt;.[40] Der &amp;#039;&amp;#039;&amp;#039;Nachteil&amp;#039;&amp;#039;&amp;#039; ist der potenziell hohe Speicherverbrauch, da alle Daten gleichzeitig in den PHP-Speicher geladen werden. Bei sehr großen Ergebnismengen kann dies zu Speicherlimit-Fehlern führen.[37]&lt;br /&gt;
&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Fetch-Modi / Fetch-Funktionen (Struktur der Ergebniszeile):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
Beide Erweiterungen bieten verschiedene Möglichkeiten, die Struktur der abgerufenen Datenzeilen zu bestimmen:&lt;br /&gt;
&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;PDO Fetch-Modi (Auswahl):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
***** &amp;lt;code&amp;gt;PDO::FETCH_ASSOC&amp;lt;/code&amp;gt;: Gibt ein assoziatives Array zurück (&amp;lt;code&amp;gt;[&amp;#039;spaltenname&amp;#039; =&amp;amp;gt; &amp;#039;wert&amp;#039;,... ]&amp;lt;/code&amp;gt;).[9, 33, 41, 42] Sehr gebräuchlich.&lt;br /&gt;
***** &amp;lt;code&amp;gt;PDO::FETCH_NUM&amp;lt;/code&amp;gt;: Gibt ein numerisch indiziertes Array zurück (&amp;lt;code&amp;gt;[0 =&amp;amp;gt; &amp;#039;wert1&amp;#039;, 1 =&amp;amp;gt; &amp;#039;wert2&amp;#039;,...]&amp;lt;/code&amp;gt;).[9, 41, 42]&lt;br /&gt;
***** &amp;lt;code&amp;gt;PDO::FETCH_BOTH&amp;lt;/code&amp;gt; (Standard): Gibt ein Array zurück, das sowohl assoziative als auch numerische Indizes enthält.[41, 42] Verbraucht mehr Speicher.&lt;br /&gt;
***** &amp;lt;code&amp;gt;PDO::FETCH_OBJ&amp;lt;/code&amp;gt;: Gibt ein anonymes &amp;lt;code&amp;gt;stdClass&amp;lt;/code&amp;gt;-Objekt zurück, bei dem die Spaltennamen Eigenschaften sind (&amp;lt;code&amp;gt;$row-&amp;amp;gt;spaltenname&amp;lt;/code&amp;gt;).[9, 41, 42, 43]&lt;br /&gt;
***** &amp;lt;code&amp;gt;PDO::FETCH_CLASS&amp;lt;/code&amp;gt;: Erstellt eine Instanz einer angegebenen Klasse und weist die Spaltenwerte den Klasseneigenschaften zu.[38, 41, 42, 43]&lt;br /&gt;
***** &amp;lt;code&amp;gt;PDO::FETCH_COLUMN&amp;lt;/code&amp;gt;: Ruft nur den Wert einer einzelnen Spalte aus der nächsten Zeile ab.[38]&lt;br /&gt;
***** &amp;lt;code&amp;gt;PDO::FETCH_LAZY&amp;lt;/code&amp;gt;: Eine Kombination aus &amp;lt;code&amp;gt;FETCH_BOTH&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;FETCH_OBJ&amp;lt;/code&amp;gt;, die Werte erst bei Zugriff lädt.[9, 41, 42]&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;MySQLi Fetch-Funktionen (Auswahl):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
***** &amp;lt;code&amp;gt;fetch_assoc()&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;mysqli_fetch_assoc()&amp;lt;/code&amp;gt;: Gibt ein assoziatives Array zurück.[17, 25, 39, 15] Sehr gebräuchlich.&lt;br /&gt;
***** &amp;lt;code&amp;gt;fetch_row()&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;mysqli_fetch_row()&amp;lt;/code&amp;gt;: Gibt ein numerisch indiziertes Array zurück.[39, 35]&lt;br /&gt;
***** &amp;lt;code&amp;gt;fetch_array()&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;mysqli_fetch_array()&amp;lt;/code&amp;gt;: Gibt je nach übergebenem Modus (&amp;lt;code&amp;gt;MYSQLI_ASSOC&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;MYSQLI_NUM&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;MYSQLI_BOTH&amp;lt;/code&amp;gt;) ein assoziatives, numerisches oder beides enthaltendes Array zurück.[39, 35]&lt;br /&gt;
***** &amp;lt;code&amp;gt;fetch_object()&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;mysqli_fetch_object()&amp;lt;/code&amp;gt;: Gibt ein &amp;lt;code&amp;gt;stdClass&amp;lt;/code&amp;gt;-Objekt zurück oder eine Instanz einer angegebenen Klasse.[21, 15, 35]&lt;br /&gt;
***** &amp;lt;code&amp;gt;fetch_column()&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;mysqli_fetch_column()&amp;lt;/code&amp;gt;: Ruft den Wert einer einzelnen Spalte ab (erst ab PHP 8.1 verfügbar).[24]&lt;br /&gt;
***** &amp;lt;code&amp;gt;fetch_all()&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;mysqli_fetch_all()&amp;lt;/code&amp;gt;: Ruft alle Zeilen auf einmal ab, Modus (&amp;lt;code&amp;gt;MYSQLI_ASSOC&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;MYSQLI_NUM&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;MYSQLI_BOTH&amp;lt;/code&amp;gt;) wählbar.[21, 25, 39]&lt;br /&gt;
&lt;br /&gt;
=== Tabelle 2: Ausgewählte PDO Fetch-Modi ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Konstante !! Rückgabeformat !! Beschreibung&lt;br /&gt;
|- &lt;br /&gt;
| &amp;lt;code&amp;gt;PDO::FETCH_ASSOC&amp;lt;/code&amp;gt;&lt;br /&gt;
| assoziatives=&amp;quot;&amp;quot; array=&amp;quot;&amp;quot; mwt-preservehtml=&amp;quot;&amp;quot; | [&amp;quot; col&amp;#039;=&amp;quot;&amp;amp;gt;&amp;quot; &amp;#039;val&amp;#039;]&amp;amp;lt;=&amp;quot;&amp;quot; code&amp;amp;gt;&amp;#039;)=&amp;quot;&amp;quot; &amp;amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
| Gängigste Form, Spaltennamen als Schlüssel.&lt;br /&gt;
|- &lt;br /&gt;
| mwt-preservehtml=&amp;quot;&amp;quot; pdo::fetch_num=&amp;quot;&amp;quot; code=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| numerisches=&amp;quot;&amp;quot; array=&amp;quot;&amp;quot; mwt-preservehtml=&amp;quot;&amp;quot; 0=&amp;quot;&amp;quot; val=&amp;quot;&amp;quot; code=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Spaltenwerte über numerischen Index erreichbar.&lt;br /&gt;
|- &lt;br /&gt;
| mwt-preservehtml=&amp;quot;&amp;quot; pdo::fetch_both=&amp;quot;&amp;quot; code=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| array=&amp;quot;&amp;quot; mit=&amp;quot;&amp;quot; assoz=&amp;quot;&amp;quot; num=&amp;quot;&amp;quot; indizes=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Standard, enthält doppelte Informationen, höherer Speicherbedarf.&lt;br /&gt;
|- &lt;br /&gt;
| mwt-preservehtml=&amp;quot;&amp;quot; pdo::fetch_obj=&amp;quot;&amp;quot; code=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| anonymes=&amp;quot;&amp;quot; mwt-preservehtml=&amp;quot;&amp;quot; stdclass=&amp;quot;&amp;quot; code=&amp;quot;&amp;quot; -objekt=&amp;quot;&amp;quot; row-=&amp;quot;&amp;quot; col=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Spaltennamen werden zu Objekteigenschaften.&lt;br /&gt;
|- &lt;br /&gt;
| &amp;lt;code&amp;gt;PDO::FETCH_CLASS&amp;lt;/code&amp;gt; || objekt=&amp;quot;&amp;quot; einer=&amp;quot;&amp;quot; spezifischen=&amp;quot;&amp;quot; klasse=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Mappt Spalten auf Eigenschaften einer vordefinierten Klasse.&lt;br /&gt;
|- &lt;br /&gt;
| &amp;lt;code&amp;gt;PDO::FETCH_COLUMN&amp;lt;/code&amp;gt; || einzelner=&amp;quot;&amp;quot; wert=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Gibt nur den Wert der ersten (oder angegebenen) Spalte der nächsten Zeile zurück.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Tabelle 3: Ausgewählte MySQLi Fetch-Funktionen ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Funktion (OO / Prozedural) !! Rückgabeformat !! Beschreibung&lt;br /&gt;
|- &lt;br /&gt;
| mwt-preservehtml=&amp;quot;&amp;quot; fetch_assoc=&amp;quot;&amp;quot; code=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| assoziatives=&amp;quot;&amp;quot; array=&amp;quot;&amp;quot; mwt-preservehtml=&amp;quot;&amp;quot; col=&amp;quot;&amp;quot; val=&amp;quot;&amp;quot; code=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Gängigste Form, Spaltennamen als Schlüssel.&lt;br /&gt;
|- &lt;br /&gt;
| mwt-preservehtml=&amp;quot;&amp;quot; fetch_row=&amp;quot;&amp;quot; code=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| numerisches=&amp;quot;&amp;quot; array=&amp;quot;&amp;quot; mwt-preservehtml=&amp;quot;&amp;quot; 0=&amp;quot;&amp;quot; val=&amp;quot;&amp;quot; code=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Spaltenwerte über numerischen Index erreichbar.&lt;br /&gt;
|- &lt;br /&gt;
| mwt-preservehtml=&amp;quot;&amp;quot; fetch_array=&amp;quot;&amp;quot; code=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| array=&amp;quot;&amp;quot; assoz=&amp;quot;&amp;quot; num=&amp;quot;&amp;quot; oder=&amp;quot;&amp;quot; beide=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Flexibel je nach Modus (&amp;lt;code&amp;gt;MYSQLI_ASSOC&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;MYSQLI_NUM&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;MYSQLI_BOTH&amp;lt;/code&amp;gt;).&lt;br /&gt;
|- &lt;br /&gt;
| &amp;lt;code&amp;gt;fetch_object()&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;mysqli_fetch_object()&amp;lt;/code&amp;gt;&lt;br /&gt;
| mwt-preservehtml=&amp;quot;&amp;quot; | stdClass&amp;quot; -objekt=&amp;quot;&amp;quot; oder=&amp;quot;&amp;quot; spezifische=&amp;quot;&amp;quot; klasse=&amp;quot;&amp;quot; &amp;amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
| Spaltennamen werden zu Objekteigenschaften.&lt;br /&gt;
|- &lt;br /&gt;
| mwt-preservehtml=&amp;quot;&amp;quot; fetch_all=&amp;quot;&amp;quot; code=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| array=&amp;quot;&amp;quot; von=&amp;quot;&amp;quot; arrays=&amp;quot;&amp;quot; assoz=&amp;quot;&amp;quot; num=&amp;quot;&amp;quot; oder=&amp;quot;&amp;quot; beide=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Holt alle Ergebniszeilen auf einmal (benötigt &amp;lt;code&amp;gt;mysqlnd&amp;lt;/code&amp;gt;).&lt;br /&gt;
|- &lt;br /&gt;
| &amp;lt;code&amp;gt;fetch_column()&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;mysqli_fetch_column()&amp;lt;/code&amp;gt; || einzelner=&amp;quot;&amp;quot; wert=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Gibt nur den Wert der ersten (oder angegebenen) Spalte zurück (PHP 8.1+).&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Zugriff auf Spaltenwerte:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
Der Zugriff auf die Daten innerhalb einer abgerufenen Zeile hängt vom gewählten Fetch-Modus ab:&lt;br /&gt;
**** Assoziatives Array: &amp;lt;code&amp;gt;$row[&amp;#039;spaltenname&amp;#039;]&amp;lt;/code&amp;gt;&lt;br /&gt;
**** Numerisches Array: &amp;lt;code&amp;gt;$row&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;$row[1]&amp;lt;/code&amp;gt;,...&lt;br /&gt;
**** Objekt: &amp;lt;code&amp;gt;$row-&amp;amp;gt;spaltenname&amp;lt;/code&amp;gt;&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Iteration mit &amp;lt;code&amp;gt;foreach&amp;lt;/code&amp;gt;:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
Nachdem Ergebnisse mit &amp;lt;code&amp;gt;fetchAll()&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;fetch_all()&amp;lt;/code&amp;gt; in ein Array geladen wurden, ist &amp;lt;code&amp;gt;foreach&amp;lt;/code&amp;gt; die natürliche Wahl zur Iteration [37, 40, 44]:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$results = $stmt-&amp;amp;gt;fetchAll(PDO::FETCH_ASSOC);&lt;br /&gt;
foreach ($results as $row) {&lt;br /&gt;
 echo &amp;quot;Name: &amp;quot;. htmlspecialchars($row[&amp;#039;name&amp;#039;]). &amp;quot;&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Seit PHP 8 bietet PDO eine modernere Alternative, die die Lesbarkeit von &amp;lt;code&amp;gt;foreach&amp;lt;/code&amp;gt; mit der Speichereffizienz von &amp;lt;code&amp;gt;fetch()&amp;lt;/code&amp;gt; kombiniert, indem direkt über das &amp;lt;code&amp;gt;PDOStatement&amp;lt;/code&amp;gt;-Objekt iteriert wird [37, 45]:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Nur PHP 8+ mit PDO&lt;br /&gt;
$stmt = $pdo-&amp;amp;gt;query(&amp;quot;SELECT name, email FROM users&amp;quot;);&lt;br /&gt;
foreach ($stmt as $row) { // PDOStatement ist direkt traversierbar&lt;br /&gt;
 echo &amp;quot;Name: &amp;quot;. htmlspecialchars($row[&amp;#039;name&amp;#039;]). &amp;quot;&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Diese Methode ist besonders elegant und effizient für große Ergebnismengen in modernen PHP-Versionen.&lt;br /&gt;
&lt;br /&gt;
=== Daten manipulieren (INSERT, UPDATE, DELETE) ===&lt;br /&gt;
&lt;br /&gt;
Das Einfügen, Aktualisieren und Löschen von Daten sind kritische Operationen, da sie den Datenbestand verändern. Hier ist die Verwendung von &amp;#039;&amp;#039;&amp;#039;Prepared Statements zwingend erforderlich&amp;#039;&amp;#039;&amp;#039;, um die Integrität und Sicherheit der Datenbank zu gewährleisten, insbesondere wenn Benutzereingaben beteiligt sind.&lt;br /&gt;
&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;PDO:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;Vorbereiten (&amp;lt;code&amp;gt;prepare&amp;lt;/code&amp;gt;):&amp;#039;&amp;#039;&amp;#039; Die SQL-Anweisung (&amp;lt;code&amp;gt;INSERT&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;UPDATE&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;DELETE&amp;lt;/code&amp;gt;) wird mit Platzhaltern (&amp;lt;code&amp;gt;?&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;:name&amp;lt;/code&amp;gt;) für die einzufügenden/zu aktualisierenden Werte sowie für die Kriterien in &amp;lt;code&amp;gt;WHERE&amp;lt;/code&amp;gt;-Klauseln (bei &amp;lt;code&amp;gt;UPDATE&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;DELETE&amp;lt;/code&amp;gt;) vorbereitet.[5, 46, 47, 48]&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;Ausführen (&amp;lt;code&amp;gt;execute&amp;lt;/code&amp;gt;):&amp;#039;&amp;#039;&amp;#039; Die Anweisung wird mit einem Array ausgeführt, das die Werte für die Platzhalter enthält.[5, 46, 47, 48]&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;Ergebnis prüfen:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
***** &amp;#039;&amp;#039;Anzahl betroffener Zeilen:&amp;#039;&amp;#039; &amp;lt;code&amp;gt;$stmt-&amp;amp;gt;rowCount()&amp;lt;/code&amp;gt; gibt die Anzahl der durch &amp;lt;code&amp;gt;UPDATE&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;DELETE&amp;lt;/code&amp;gt; betroffenen Zeilen zurück. Bei &amp;lt;code&amp;gt;INSERT&amp;lt;/code&amp;gt; ist der Rückgabewert oft nicht zuverlässig oder standardisiert.[8, 9, 5, 49] Das Verhalten kann je nach Datenbanktreiber und Konfiguration (z.B. &amp;lt;code&amp;gt;PDO::MYSQL_ATTR_FOUND_ROWS&amp;lt;/code&amp;gt;) variieren.[9]&lt;br /&gt;
***** &amp;#039;&amp;#039;ID des letzten eingefügten Datensatzes:&amp;#039;&amp;#039; Bei &amp;lt;code&amp;gt;INSERT&amp;lt;/code&amp;gt;-Anweisungen in Tabellen mit einer &amp;lt;code&amp;gt;AUTO_INCREMENT&amp;lt;/code&amp;gt;-Spalte liefert &amp;lt;code&amp;gt;$pdo-&amp;amp;gt;lastInsertId()&amp;lt;/code&amp;gt; die ID des neu eingefügten Datensatzes zurück.[8, 33, 48]&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;Code-Beispiele (PDO):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!--?php// --- INSERT ---&lt;br /&gt;
$name = $_POST[&amp;#039;name&amp;#039;];&lt;br /&gt;
$email = $_POST[&amp;#039;email&amp;#039;];&lt;br /&gt;
$status = &amp;#039;pending&amp;#039;;&lt;br /&gt;
&lt;br /&gt;
$sql_insert = &amp;quot;INSERT INTO users (username, email, status) VALUES (:name, :email, :status)&amp;quot;;&lt;br /&gt;
$stmt_insert = $pdo---&amp;gt;prepare($sql_insert);&lt;br /&gt;
$stmt_insert-&amp;amp;gt;execute([&amp;#039;:name&amp;#039; =&amp;amp;gt; $name, &amp;#039;:email&amp;#039; =&amp;amp;gt; $email, &amp;#039;:status&amp;#039; =&amp;amp;gt; $status]);&lt;br /&gt;
$lastId = $pdo-&amp;amp;gt;lastInsertId();&lt;br /&gt;
echo &amp;quot;Neuer Benutzer mit ID $lastId eingefügt.&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
// --- UPDATE ---&lt;br /&gt;
$new_status = &amp;#039;active&amp;#039;;&lt;br /&gt;
$user_id_to_update = $lastId;&lt;br /&gt;
&lt;br /&gt;
$sql_update = &amp;quot;UPDATE users SET status = :status WHERE id = :id&amp;quot;;&lt;br /&gt;
$stmt_update = $pdo-&amp;amp;gt;prepare($sql_update);&lt;br /&gt;
$stmt_update-&amp;amp;gt;execute([&amp;#039;:status&amp;#039; =&amp;amp;gt; $new_status, &amp;#039;:id&amp;#039; =&amp;amp;gt; $user_id_to_update]);&lt;br /&gt;
$affectedRows = $stmt_update-&amp;amp;gt;rowCount();&lt;br /&gt;
echo &amp;quot;$affectedRows Benutzerstatus aktualisiert.&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
// --- DELETE ---&lt;br /&gt;
$user_id_to_delete = $lastId;&lt;br /&gt;
&lt;br /&gt;
$sql_delete = &amp;quot;DELETE FROM users WHERE id = :id&amp;quot;;&lt;br /&gt;
$stmt_delete = $pdo-&amp;amp;gt;prepare($sql_delete);&lt;br /&gt;
$stmt_delete-&amp;amp;gt;execute([&amp;#039;:id&amp;#039; =&amp;amp;gt; $user_id_to_delete]);&lt;br /&gt;
$affectedRowsDel = $stmt_delete-&amp;amp;gt;rowCount();&lt;br /&gt;
echo &amp;quot;$affectedRowsDel Benutzer gelöscht.&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
?&amp;amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;MySQLi:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;Vorbereiten (&amp;lt;code&amp;gt;prepare&amp;lt;/code&amp;gt;):&amp;#039;&amp;#039;&amp;#039; Die SQL-Anweisung wird mit &amp;lt;code&amp;gt;?&amp;lt;/code&amp;gt;-Platzhaltern vorbereitet (OO: &amp;lt;code&amp;gt;$mysqli-&amp;amp;gt;prepare()&amp;lt;/code&amp;gt;, Proc: &amp;lt;code&amp;gt;mysqli_prepare()&amp;lt;/code&amp;gt;).[25, 50, 15, 35]&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;Parameter binden (&amp;lt;code&amp;gt;bind_param&amp;lt;/code&amp;gt;):&amp;#039;&amp;#039;&amp;#039; Variablen werden explizit mit Typenangabe gebunden (OO: &amp;lt;code&amp;gt;$stmt-&amp;amp;gt;bind_param()&amp;lt;/code&amp;gt;, Proc: &amp;lt;code&amp;gt;mysqli_stmt_bind_param()&amp;lt;/code&amp;gt;).[25, 50, 15, 35] Die Notwendigkeit, den Datentyp für jede Variable anzugeben, ist ein wesentlicher Unterschied zur einfacheren Array-Übergabe bei PDOs &amp;lt;code&amp;gt;execute()&amp;lt;/code&amp;gt;.&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;Ausführen (&amp;lt;code&amp;gt;execute&amp;lt;/code&amp;gt;):&amp;#039;&amp;#039;&amp;#039; Die Anweisung wird ausgeführt (OO: &amp;lt;code&amp;gt;$stmt-&amp;amp;gt;execute()&amp;lt;/code&amp;gt;, Proc: &amp;lt;code&amp;gt;mysqli_stmt_execute()&amp;lt;/code&amp;gt;).[25, 36, 50, 15, 35]&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;Ergebnis prüfen:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
***** &amp;#039;&amp;#039;Anzahl betroffener Zeilen:&amp;#039;&amp;#039; Über die Eigenschaft &amp;lt;code&amp;gt;$mysqli-&amp;amp;gt;affected_rows&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;$stmt-&amp;amp;gt;affected_rows&amp;lt;/code&amp;gt; (OO) bzw. die Funktionen &amp;lt;code&amp;gt;mysqli_affected_rows($link)&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;mysqli_stmt_affected_rows($stmt)&amp;lt;/code&amp;gt; (Proc).[18, 26, 15, 35]&lt;br /&gt;
***** &amp;#039;&amp;#039;ID des letzten eingefügten Datensatzes:&amp;#039;&amp;#039; Über &amp;lt;code&amp;gt;$mysqli-&amp;amp;gt;insert_id&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;$stmt-&amp;amp;gt;insert_id&amp;lt;/code&amp;gt; (OO) bzw. &amp;lt;code&amp;gt;mysqli_insert_id($link)&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;mysqli_stmt_insert_id($stmt)&amp;lt;/code&amp;gt; (Proc).[26, 15] Die Verwendung von &amp;lt;code&amp;gt;$mysqli-&amp;amp;gt;insert_id&amp;lt;/code&amp;gt; wird oft als zuverlässiger angesehen, da sie auch nach dem Schließen des Statements funktioniert.[15]&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;Code-Beispiele (MySQLi OO):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!--?php// --- INSERT ---&lt;br /&gt;
$name = $_POST[&amp;#039;name&amp;#039;];&lt;br /&gt;
$email = $_POST[&amp;#039;email&amp;#039;];&lt;br /&gt;
$status = &amp;#039;pending&amp;#039;;&lt;br /&gt;
&lt;br /&gt;
$sql_insert = &amp;quot;INSERT INTO users (username, email, status) VALUES (?,?,?)&amp;quot;;&lt;br /&gt;
$stmt_insert = $mysqli---&amp;gt;prepare($sql_insert);&lt;br /&gt;
// Typen: s=string, s=string, s=string&lt;br /&gt;
$stmt_insert-&amp;amp;gt;bind_param(&amp;quot;sss&amp;quot;, $name, $email, $status);&lt;br /&gt;
$stmt_insert-&amp;amp;gt;execute();&lt;br /&gt;
$lastId = $mysqli-&amp;amp;gt;insert_id;&lt;br /&gt;
echo &amp;quot;Neuer Benutzer mit ID $lastId eingefügt (MySQLi OO).&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
$stmt_insert-&amp;amp;gt;close(); // Statement schließen&lt;br /&gt;
&lt;br /&gt;
// --- UPDATE ---&lt;br /&gt;
$new_status = &amp;#039;active&amp;#039;;&lt;br /&gt;
$user_id_to_update = $lastId;&lt;br /&gt;
&lt;br /&gt;
$sql_update = &amp;quot;UPDATE users SET status =? WHERE id =?&amp;quot;;&lt;br /&gt;
$stmt_update = $mysqli-&amp;amp;gt;prepare($sql_update);&lt;br /&gt;
// Typen: s=string, i=integer&lt;br /&gt;
$stmt_update-&amp;amp;gt;bind_param(&amp;quot;si&amp;quot;, $new_status, $user_id_to_update);&lt;br /&gt;
$stmt_update-&amp;amp;gt;execute();&lt;br /&gt;
$affectedRows = $mysqli-&amp;amp;gt;affected_rows; // Oder $stmt_update-&amp;amp;gt;affected_rows vor close()&lt;br /&gt;
echo &amp;quot;$affectedRows Benutzerstatus aktualisiert (MySQLi OO).&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
$stmt_update-&amp;amp;gt;close();&lt;br /&gt;
&lt;br /&gt;
// --- DELETE ---&lt;br /&gt;
$user_id_to_delete = $lastId;&lt;br /&gt;
&lt;br /&gt;
$sql_delete = &amp;quot;DELETE FROM users WHERE id =?&amp;quot;;&lt;br /&gt;
$stmt_delete = $mysqli-&amp;amp;gt;prepare($sql_delete);&lt;br /&gt;
// Typ: i=integer&lt;br /&gt;
$stmt_delete-&amp;amp;gt;bind_param(&amp;quot;i&amp;quot;, $user_id_to_delete);&lt;br /&gt;
$stmt_delete-&amp;amp;gt;execute();&lt;br /&gt;
$affectedRowsDel = $mysqli-&amp;amp;gt;affected_rows;&lt;br /&gt;
echo &amp;quot;$affectedRowsDel Benutzer gelöscht (MySQLi OO).&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
$stmt_delete-&amp;amp;gt;close();&lt;br /&gt;
?&amp;amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;#039;&amp;#039;(Anmerkung: Prozedurale Beispiele folgen demselben Muster unter Verwendung der &amp;lt;code&amp;gt;mysqli_*&amp;lt;/code&amp;gt;-Funktionen wie &amp;lt;code&amp;gt;mysqli_prepare&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;mysqli_stmt_bind_param&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;mysqli_stmt_execute&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;mysqli_insert_id&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;mysqli_affected_rows&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;mysqli_stmt_close&amp;lt;/code&amp;gt;)&amp;#039;&amp;#039;.&lt;br /&gt;
&lt;br /&gt;
== Sicherheit: SQL Injection verstehen und verhindern ==&lt;br /&gt;
&lt;br /&gt;
Eines der größten Sicherheitsrisiken bei der Interaktion mit Datenbanken über Webanwendungen ist die SQL Injection (SQLi). Das Verständnis dieser Bedrohung und ihrer Abwehr ist für jeden PHP-Entwickler unerlässlich.&lt;br /&gt;
&lt;br /&gt;
=== SQL Injection ===&lt;br /&gt;
&lt;br /&gt;
SQL Injection ist eine Angriffstechnik, bei der ein Angreifer versucht, bösartigen oder manipulierten SQL-Code über Benutzereingabefelder einer Webanwendung (z.B. Formulare, URL-Parameter, HTTP-Header, Cookies) in die Datenbankabfragen einzuschleusen.[51, 52, 53, 54, 55, 56, 57]&lt;br /&gt;
&lt;br /&gt;
Die Attacke funktioniert, indem Schwachstellen in der Anwendung ausgenutzt werden, bei denen Benutzereingaben direkt und ohne ausreichende Validierung oder Maskierung (Escaping) in SQL-Abfragestrings eingefügt (konkateniert) werden.[5, 51, 55] Dadurch wird die notwendige Trennung zwischen dem eigentlichen SQL-Befehl (Kontrollebene) und den Daten (Datenebene) aufgehoben.[51] Der Angreifer kann spezielle Zeichen (wie Anführungszeichen &amp;lt;code&amp;gt;&amp;#039;&amp;lt;/code&amp;gt;, Semikolons &amp;lt;code&amp;gt;;&amp;lt;/code&amp;gt; oder Kommentare &amp;lt;code&amp;gt;--&amp;lt;/code&amp;gt;) verwenden, um den ursprünglichen SQL-Befehl vorzeitig zu beenden, zu verändern oder um eigene, zusätzliche SQL-Befehle anzuhängen.[51, 53]&lt;br /&gt;
&lt;br /&gt;
Ein klassisches Beispiel ist eine Login-Abfrage, die etwa so aufgebaut ist:&lt;br /&gt;
&amp;lt;code&amp;gt;$sql = &amp;quot;SELECT * FROM users WHERE username = &amp;#039;&amp;quot;. $userInputUsername. &amp;quot;&amp;#039; AND password = &amp;#039;&amp;quot;. $userInputPassword. &amp;quot;&amp;#039;&amp;quot;;&amp;lt;/code&amp;gt;&lt;br /&gt;
Wenn ein Angreifer als Benutzernamen &amp;lt;code&amp;gt;&amp;#039; OR &amp;#039;1&amp;#039;=&amp;#039;1&amp;lt;/code&amp;gt; eingibt, wird die resultierende SQL-Abfrage zu:&lt;br /&gt;
&amp;lt;code&amp;gt;SELECT * FROM users WHERE username = &amp;#039;&amp;#039; OR &amp;#039;1&amp;#039;=&amp;#039;1&amp;#039; AND password = &amp;#039;...&amp;#039;&amp;lt;/code&amp;gt;&lt;br /&gt;
Da &amp;lt;code&amp;gt;&amp;#039;1&amp;#039;=&amp;#039;1&amp;#039;&amp;lt;/code&amp;gt; immer wahr ist, würde diese Abfrage (je nach genauer Struktur und Datenbank) möglicherweise alle Benutzer zurückgeben oder den Login ohne korrektes Passwort ermöglichen.[5, 15, 55]&lt;br /&gt;
&lt;br /&gt;
=== Auswirkungen und Gefahren von SQL Injection ===&lt;br /&gt;
&lt;br /&gt;
Die potenziellen Folgen einer erfolgreichen SQL-Injection-Attacke sind gravierend und vielfältig:&lt;br /&gt;
&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Datendiebstahl:&amp;#039;&amp;#039;&amp;#039; Angreifer können auf sensible Daten zugreifen und diese auslesen, darunter Benutzerdaten, Passwörter (oft als Hashes, die aber geknackt werden können), Kreditkarteninformationen, Geschäftsgeheimnisse und andere vertrauliche Informationen.[51, 53, 56]&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Datenmanipulation/-zerstörung:&amp;#039;&amp;#039;&amp;#039; Angreifer können bestehende Daten in der Datenbank verändern (z.B. Preise in einem Shop, Benutzerrollen) oder komplette Datensätze oder sogar Tabellen löschen (&amp;lt;code&amp;gt;DELETE&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;DROP TABLE&amp;lt;/code&amp;gt;).[51, 53, 56]&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Umgehung der Authentifizierung/Identitätsdiebstahl:&amp;#039;&amp;#039;&amp;#039; Wie im Beispiel oben gezeigt, können Angreifer Login-Mechanismen umgehen und sich unautorisiert Zugang verschaffen, möglicherweise sogar mit administrativen Rechten.[51, 56]&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Denial of Service (DoS):&amp;#039;&amp;#039;&amp;#039; Durch das Löschen wichtiger Daten oder das Ausführen ressourcenintensiver Abfragen kann die Verfügbarkeit der Anwendung oder der Datenbank beeinträchtigt werden.[51, 56]&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Kompromittierung des Servers:&amp;#039;&amp;#039;&amp;#039; In manchen Fällen können Angreifer über SQL Injection Betriebssystembefehle auf dem Datenbankserver ausführen oder vollen administrativen Zugriff auf die Datenbank erlangen, was weitreichende Konsequenzen haben kann.[51, 55, 56]&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Reputationsschaden und rechtliche Folgen:&amp;#039;&amp;#039;&amp;#039; Erfolgreiche Angriffe, insbesondere wenn sie zu Datenlecks führen, können das Vertrauen der Benutzer schwer beschädigen und zu empfindlichen Strafen gemäß Datenschutzgesetzen führen.[53]&lt;br /&gt;
&lt;br /&gt;
=== Prävention durch Prepared Statements (Primäre Methode) ===&lt;br /&gt;
&lt;br /&gt;
Die mit Abstand wichtigste und effektivste Methode zur Verhinderung von SQL Injection ist die konsequente Verwendung von &amp;#039;&amp;#039;&amp;#039;Prepared Statements&amp;#039;&amp;#039;&amp;#039; (parametrisierten Abfragen).[4, 25, 5, 58, 35]&lt;br /&gt;
&lt;br /&gt;
Das Grundprinzip von Prepared Statements ist die &amp;#039;&amp;#039;&amp;#039;strikte Trennung von SQL-Befehlsstruktur und den Daten (Parametern)&amp;#039;&amp;#039;&amp;#039;.[4, 25, 5, 58, 35] Der Ablauf ist typischerweise zweistufig:&lt;br /&gt;
**# &amp;#039;&amp;#039;&amp;#039;Prepare (Vorbereiten):&amp;#039;&amp;#039;&amp;#039; Die SQL-Anweisung wird mit Platzhaltern anstelle der tatsächlichen Werte an den Datenbankserver gesendet. Der Server parst die Anweisung, prüft die Syntax und bereitet einen Ausführungsplan vor, ohne die endgültigen Daten zu kennen.&lt;br /&gt;
**# &amp;#039;&amp;#039;&amp;#039;Execute (Ausführen) mit Parameterbindung:&amp;#039;&amp;#039;&amp;#039; Die tatsächlichen Werte (Parameter) werden separat an den Server gesendet und an die vorbereiteten Platzhalter gebunden. Der Server führt nun die vorkompilierte Anweisung mit den sicher eingefügten Werten aus.&lt;br /&gt;
Der entscheidende Punkt ist, dass die Datenbank die separat gesendeten Parameter &amp;#039;&amp;#039;&amp;#039;immer als Datenwerte&amp;#039;&amp;#039;&amp;#039; behandelt, niemals als Teil des SQL-Befehls.[5, 15, 58] Selbst wenn ein Parameter bösartigen SQL-Code enthält (z.B. &amp;lt;code&amp;gt;&amp;#039; OR &amp;#039;1&amp;#039;=&amp;#039;1&amp;lt;/code&amp;gt;), wird dieser nicht ausgeführt, sondern als einfacher String-Wert behandelt, der z.B. in eine Spalte eingefügt oder in einer &amp;lt;code&amp;gt;WHERE&amp;lt;/code&amp;gt;-Klausel verglichen wird. Diese Trennung macht Prepared Statements immun gegen SQL Injection.&lt;br /&gt;
&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Platzhalter:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;PDO:&amp;#039;&amp;#039;&amp;#039; Unterstützt sowohl positionale Platzhalter (&amp;lt;code&amp;gt;?&amp;lt;/code&amp;gt;) als auch benannte Platzhalter (&amp;lt;code&amp;gt;:name&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;:email&amp;lt;/code&amp;gt;, etc.).[5, 13] Benannte Platzhalter machen den Code oft lesbarer, besonders bei vielen Parametern.&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;MySQLi:&amp;#039;&amp;#039;&amp;#039; Unterstützt ausschließlich positionale Platzhalter (&amp;lt;code&amp;gt;?&amp;lt;/code&amp;gt;).[25]&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Parameterbindung:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;PDO:&amp;#039;&amp;#039;&amp;#039; Bietet flexible Bindungsoptionen:&lt;br /&gt;
***** &amp;#039;&amp;#039;Implizit über &amp;lt;code&amp;gt;execute()&amp;lt;/code&amp;gt;-Array:&amp;#039;&amp;#039; Die einfachste und häufigste Methode. Ein Array mit den Werten wird an &amp;lt;code&amp;gt;execute()&amp;lt;/code&amp;gt; übergeben. PDO kümmert sich um das korrekte Escaping und die Typbehandlung (oft werden Werte sicher als Strings behandelt).[4, 12, 5, 13]&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$stmt-&amp;amp;gt;execute([$wert1, $wert2]); // für? Platzhalter&lt;br /&gt;
$stmt-&amp;amp;gt;execute([&amp;#039;:name&amp;#039; =&amp;amp;gt; $wert1, &amp;#039;:id&amp;#039; =&amp;amp;gt; $wert2]); // für :name Platzhalter&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
***** &amp;#039;&amp;#039;Explizit mit &amp;lt;code&amp;gt;bindValue()&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;bindParam()&amp;lt;/code&amp;gt;:&amp;#039;&amp;#039; Erlaubt die explizite Angabe des Datentyps (z.B. &amp;lt;code&amp;gt;PDO::PARAM_INT&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;PDO::PARAM_STR&amp;lt;/code&amp;gt;) für jeden Parameter, was in manchen Fällen für Klarheit oder spezifische Datenbankoptimierungen sorgen kann.[5, 13] &amp;lt;code&amp;gt;bindParam&amp;lt;/code&amp;gt; bindet eine Variable (ihr Wert wird erst bei &amp;lt;code&amp;gt;execute()&amp;lt;/code&amp;gt; gelesen), &amp;lt;code&amp;gt;bindValue&amp;lt;/code&amp;gt; bindet den aktuellen Wert einer Variablen oder einen Literalwert.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$stmt-&amp;amp;gt;bindValue(&amp;#039;:id&amp;#039;, $user_id, PDO::PARAM_INT);&lt;br /&gt;
$stmt-&amp;amp;gt;bindParam(&amp;#039;:name&amp;#039;, $user_name, PDO::PARAM_STR);&lt;br /&gt;
$stmt-&amp;amp;gt;execute();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;MySQLi:&amp;#039;&amp;#039;&amp;#039; Erfordert immer die explizite Bindung mittels &amp;lt;code&amp;gt;bind_param()&amp;lt;/code&amp;gt;.&lt;br /&gt;
***** &amp;lt;code&amp;gt;bind_param()&amp;lt;/code&amp;gt; (OO) / &amp;lt;code&amp;gt;mysqli_stmt_bind_param()&amp;lt;/code&amp;gt; (Proc): Nimmt als ersten Parameter einen String entgegen, der die Datentypen aller nachfolgend übergebenen Variablen definiert.[25, 50, 15, 58]&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// OO-Stil&lt;br /&gt;
$stmt-&amp;amp;gt;bind_param(&amp;quot;ssi&amp;quot;, $name, $email, $id); // String, String, Integer&lt;br /&gt;
// Prozedural&lt;br /&gt;
mysqli_stmt_bind_param($stmt, &amp;quot;ssi&amp;quot;, $name, $email, $id);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
=== Tabelle 4: MySQLi &amp;lt;code&amp;gt;bind_param()&amp;lt;/code&amp;gt; Typen-String ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Typ-Buchstabe !! PHP-Datentyp (typisch) !! Beschreibung&lt;br /&gt;
|- &lt;br /&gt;
| mwt-preservehtml=&amp;quot;&amp;quot; i=&amp;quot;&amp;quot; code=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| integer=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Integer (Ganzzahl)&lt;br /&gt;
|- &lt;br /&gt;
| mwt-preservehtml=&amp;quot;&amp;quot; d=&amp;quot;&amp;quot; code=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| double=&amp;quot;&amp;quot; float=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Double (Gleitkommazahl)&lt;br /&gt;
|- &lt;br /&gt;
| mwt-preservehtml=&amp;quot;&amp;quot; s=&amp;quot;&amp;quot; code=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| string=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| String (Zeichenkette)&lt;br /&gt;
|- &lt;br /&gt;
| mwt-preservehtml=&amp;quot;&amp;quot; b=&amp;quot;&amp;quot; code=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| string=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| BLOB (Binary Large Object), als String gesendet&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Detaillierte Code-Beispiele zur Prävention:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
Die Beispiele für INSERT, UPDATE und DELETE in Abschnitt 3.3 demonstrieren bereits die korrekte Anwendung von Prepared Statements mit PDO und MySQLi zur Verhinderung von SQL Injection. Es ist entscheidend, dieses Muster konsequent anzuwenden, wann immer externe Daten in SQL-Abfragen einfließen.&lt;br /&gt;
&lt;br /&gt;
=== 4.4 Zusätzliche Schutzmaßnahmen (Defense-in-Depth) ===&lt;br /&gt;
&lt;br /&gt;
Obwohl Prepared Statements der wichtigste Schutz gegen SQL Injection sind, sollte Sicherheit immer als mehrschichtiges Konzept (&amp;quot;Defense-in-Depth&amp;quot;) betrachtet werden.[5] Zusätzliche Maßnahmen erhöhen die Robustheit der Anwendung:&lt;br /&gt;
&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Eingabevalidierung und -bereinigung:&amp;#039;&amp;#039;&amp;#039; Überprüfen Sie &amp;#039;&amp;#039;alle&amp;#039;&amp;#039; Benutzereingaben serverseitig, bevor sie überhaupt in die Nähe der Datenbank kommen.[4, 59, 20, 5] Stellen Sie sicher, dass die Daten dem erwarteten Typ (Zahl, String, E-Mail-Format etc.) und Format entsprechen. Verwenden Sie dafür PHP-Filterfunktionen wie &amp;lt;code&amp;gt;filter_var()&amp;lt;/code&amp;gt; oder reguläre Ausdrücke.[4, 5] Dies ist eine wichtige zweite Verteidigungslinie, die ungültige oder unerwartete Daten frühzeitig abfängt. Es ersetzt jedoch &amp;#039;&amp;#039;nicht&amp;#039;&amp;#039; die Notwendigkeit von Prepared Statements.&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Prinzip der geringsten Rechte (Least Privilege):&amp;#039;&amp;#039;&amp;#039; Konfigurieren Sie den Datenbankbenutzer, den Ihre PHP-Anwendung verwendet, mit den absolut minimal notwendigen Berechtigungen.[20, 5] Wenn die Anwendung nur Daten lesen und schreiben muss, geben Sie ihr keine Rechte zum Ändern der Tabellenstruktur (&amp;lt;code&amp;gt;ALTER TABLE&amp;lt;/code&amp;gt;) oder zum Löschen von Tabellen (&amp;lt;code&amp;gt;DROP TABLE&amp;lt;/code&amp;gt;). Beschränken Sie den Zugriff auf die benötigten Datenbanken und Tabellen. Verwenden Sie niemals den &amp;lt;code&amp;gt;root&amp;lt;/code&amp;gt;- oder Admin-Benutzer der Datenbank für die Anwendung. Dies begrenzt den potenziellen Schaden, falls doch eine Schwachstelle ausgenutzt wird.&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Sichere Fehlerbehandlung:&amp;#039;&amp;#039;&amp;#039; Konfigurieren Sie PHP und Ihre Datenbankerweiterung so, dass detaillierte Fehlermeldungen (insbesondere solche, die SQL-Code oder Datenbankstrukturinformationen enthalten) niemals dem Endbenutzer angezeigt werden.[20, 5] Solche Informationen sind für Angreifer wertvoll. Loggen Sie Fehler stattdessen in eine sichere Datei auf dem Server, die nur für Administratoren zugänglich ist. Verwenden Sie &amp;lt;code&amp;gt;PDO::ERRMODE_EXCEPTION&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;mysqli_report&amp;lt;/code&amp;gt; für strukturierte Fehlerbehandlung.&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Output Escaping:&amp;#039;&amp;#039;&amp;#039; Obwohl nicht direkt zur Verhinderung von SQL Injection, ist es wichtig, Daten, die aus der Datenbank gelesen und im HTML-Kontext ausgegeben werden, korrekt zu escapen (z.B. mit &amp;lt;code&amp;gt;htmlspecialchars()&amp;lt;/code&amp;gt;), um Cross-Site Scripting (XSS)-Angriffe zu verhindern.&lt;br /&gt;
Die Kombination dieser Maßnahmen – mit Prepared Statements als Kernstück – bietet einen robusten Schutz gegen SQL Injection und andere verwandte Angriffe.&lt;br /&gt;
&lt;br /&gt;
== Nützliche PHP Array-Funktionen für Datenbankergebnisse ==&lt;br /&gt;
&lt;br /&gt;
Wenn Daten aus der Datenbank abgerufen werden, insbesondere mit Methoden wie &amp;lt;code&amp;gt;fetchAll()&amp;lt;/code&amp;gt; (PDO) oder &amp;lt;code&amp;gt;fetch_all()&amp;lt;/code&amp;gt; (MySQLi), liegen die Ergebnisse typischerweise als PHP-Array vor (meist ein Array von assoziativen Arrays, wobei jedes innere Array eine Ergebniszeile darstellt). PHP bietet eine Reihe nützlicher Funktionen zur Verarbeitung solcher Arrays.&lt;br /&gt;
&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Iteration mit &amp;lt;code&amp;gt;foreach&amp;lt;/code&amp;gt;:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
Dies ist die Standardmethode, um die einzelnen Zeilen und Spalten eines Ergebnis-Arrays zu durchlaufen.[37, 40, 44]&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;Syntax:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Iteration über die Zeilen (jedes $row ist ein assoziatives Array)&lt;br /&gt;
foreach ($results as $row) {&lt;br /&gt;
 echo &amp;quot;ID: &amp;quot;. $row[&amp;#039;id&amp;#039;]. &amp;quot;, Name: &amp;quot;. htmlspecialchars($row[&amp;#039;name&amp;#039;]). &amp;quot;&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Iteration über Zeilen mit Zugriff auf den numerischen Index der Zeile&lt;br /&gt;
foreach ($results as $index =&amp;amp;gt; $row) {&lt;br /&gt;
 echo &amp;quot;Zeile &amp;quot;. $index. &amp;quot;: ID = &amp;quot;. $row[&amp;#039;id&amp;#039;]. &amp;quot;&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;Code-Beispiel:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!--?php// Annahme: $results enthält das Ergebnis von $stmt---&amp;gt;fetchAll(PDO::FETCH_ASSOC);&lt;br /&gt;
$results = [&amp;#039;id&amp;#039; =&amp;amp;gt; 1, &amp;#039;name&amp;#039; =&amp;amp;gt; &amp;#039;Alice&amp;#039;, &amp;#039;email&amp;#039; =&amp;amp;gt; &amp;#039;alice@example.com&amp;#039;],&lt;br /&gt;
 ;&lt;br /&gt;
&lt;br /&gt;
echo &amp;quot;&amp;lt;/syntaxhighlight&amp;gt;&amp;quot;;&lt;br /&gt;
foreach ($results as $row) {&lt;br /&gt;
 echo &amp;quot;&lt;br /&gt;
*Benutzer #&amp;quot;. $row[&amp;#039;id&amp;#039;]. &amp;quot;: &amp;quot;. htmlspecialchars($row[&amp;#039;name&amp;#039;]);&lt;br /&gt;
 echo &amp;quot; (&amp;quot;. htmlspecialchars($row[&amp;#039;email&amp;#039;]). &amp;quot;)&lt;br /&gt;
&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
echo &amp;quot;&amp;quot;;&lt;br /&gt;
?&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Zählen von Ergebnissen mit &amp;lt;code&amp;gt;count()&amp;lt;/code&amp;gt;:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
Die Funktion &amp;lt;code&amp;gt;count()&amp;lt;/code&amp;gt; gibt die Anzahl der Elemente in einem Array zurück. Wenn sie auf ein mit &amp;lt;code&amp;gt;fetchAll()&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;fetch_all()&amp;lt;/code&amp;gt; erzeugtes Ergebnis-Array angewendet wird, liefert sie die Anzahl der abgerufenen Zeilen.[40, 60, 61]&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;Syntax:&amp;#039;&amp;#039;&amp;#039; &amp;lt;code&amp;gt;$anzahlZeilen = count($results);&amp;lt;/code&amp;gt;.[60]&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;Wichtiger Hinweis:&amp;#039;&amp;#039;&amp;#039; &amp;lt;code&amp;gt;count()&amp;lt;/code&amp;gt; funktioniert nur zuverlässig für die Gesamtzahl der Zeilen, wenn &amp;#039;&amp;#039;alle&amp;#039;&amp;#039; Zeilen zuvor in ein Array geladen wurden (also nach &amp;lt;code&amp;gt;fetchAll&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;fetch_all&amp;lt;/code&amp;gt;). Wenn Sie Ergebnisse zeilenweise mit &amp;lt;code&amp;gt;fetch()&amp;lt;/code&amp;gt; in einer &amp;lt;code&amp;gt;while&amp;lt;/code&amp;gt;-Schleife verarbeiten, müssen Sie einen manuellen Zähler innerhalb der Schleife inkrementieren, um die Anzahl der verarbeiteten Zeilen zu ermitteln.[40] Alternativ können &amp;lt;code&amp;gt;PDOStatement::rowCount()&amp;lt;/code&amp;gt; [8, 9] oder &amp;lt;code&amp;gt;mysqli_result::$num_rows&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;mysqli_stmt::$num_rows&amp;lt;/code&amp;gt; [26, 15] verwendet werden, um die Anzahl der von einer &amp;lt;code&amp;gt;SELECT&amp;lt;/code&amp;gt;-Abfrage zurückgegebenen Zeilen zu erhalten, &amp;#039;&amp;#039;bevor&amp;#039;&amp;#039; die Daten gefetched werden (das Verhalten von &amp;lt;code&amp;gt;rowCount&amp;lt;/code&amp;gt; kann jedoch bei anderen Anweisungstypen wie &amp;lt;code&amp;gt;UPDATE&amp;lt;/code&amp;gt; variieren).&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;Code-Beispiel:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!--?php&lt;br /&gt;
// Annahme: $results von fetchAll()&lt;br /&gt;
$results = [ /*... wie oben... */ ];&lt;br /&gt;
$anzahl = count($results);&lt;br /&gt;
echo &amp;quot;Anzahl der Benutzer (via fetchAll/count): $anzahl&amp;lt;br--&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
// Vergleich: Zählen bei zeilenweiser Verarbeitung&lt;br /&gt;
$stmt = $pdo-&amp;amp;gt;query(&amp;quot;SELECT id FROM users&amp;quot;); // Annahme: PDO-Statement&lt;br /&gt;
$manueller_zaehler = 0;&lt;br /&gt;
while ($row = $stmt-&amp;amp;gt;fetch()) {&lt;br /&gt;
 $manueller_zaehler++;&lt;br /&gt;
}&lt;br /&gt;
echo &amp;quot;Anzahl der Benutzer (via while/fetch): $manueller_zaehler&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
// Alternative: rowCount() nach SELECT (PDO)&lt;br /&gt;
$stmt_count = $pdo-&amp;amp;gt;query(&amp;quot;SELECT id FROM users&amp;quot;);&lt;br /&gt;
$rowCountResult = $stmt_count-&amp;amp;gt;rowCount(); // Kann bei manchen DBs/Treibern funktionieren&lt;br /&gt;
echo &amp;quot;Anzahl der Benutzer (via rowCount nach SELECT): $rowCountResult&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
?&amp;amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;(Optional) Weitere nützliche Array-Funktionen:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
**** &amp;lt;code&amp;gt;array_column(array $input, mixed $column_key [, mixed $index_key = null ])&amp;lt;/code&amp;gt;: Extrahiert alle Werte aus einer einzelnen Spalte des Ergebnis-Arrays und gibt sie als neues, eindimensionales Array zurück.[62] Sehr nützlich, um z.B. eine Liste aller Benutzer-IDs oder E-Mail-Adressen zu erhalten.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Annahme: $results enthält das Array von assoziativen Arrays&lt;br /&gt;
$alle_emails = array_column($results, &amp;#039;email&amp;#039;);&lt;br /&gt;
// $alle_emails ist nun [&amp;#039;alice@example.com&amp;#039;, &amp;#039;bob@example.com&amp;#039;]&lt;br /&gt;
print_r($alle_emails);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
**** &amp;lt;code&amp;gt;array_map(callable $callback, array $array1 [, array...$arrays ])&amp;lt;/code&amp;gt;: Wendet eine benutzerdefinierte Funktion (&amp;lt;code&amp;gt;callback&amp;lt;/code&amp;gt;) auf jedes Element eines Arrays an und gibt ein neues Array mit den Ergebnissen zurück.[63] Nützlich zur Transformation von Daten (z.B. Formatierung, Bereinigung).&lt;br /&gt;
**** &amp;lt;code&amp;gt;array_filter(array $array [, callable $callback [, int $flag = 0 ]])&amp;lt;/code&amp;gt;: Filtert Elemente eines Arrays basierend auf einer Callback-Funktion.[61] Nützlich, um nur bestimmte Zeilen aus dem Ergebnis-Array zu behalten (obwohl dies oft effizienter direkt in der SQL-Abfrage geschieht).&lt;br /&gt;
**** &amp;lt;code&amp;gt;array_count_values(array $array)&amp;lt;/code&amp;gt;: Zählt, wie oft jeder eindeutige Wert in einem eindimensionalen Array vorkommt.[63] Kann nützlich sein, wenn man z.B. mit &amp;lt;code&amp;gt;array_column&amp;lt;/code&amp;gt; eine Spalte extrahiert hat und die Häufigkeit bestimmter Werte (z.B. Status) wissen möchte.&lt;br /&gt;
Diese Funktionen, insbesondere &amp;lt;code&amp;gt;foreach&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;count&amp;lt;/code&amp;gt;, sind grundlegende Werkzeuge bei der Verarbeitung von Datenbankergebnissen in PHP.&lt;br /&gt;
&lt;br /&gt;
== Zusammenfassung &amp;amp; Best Practices ==&lt;br /&gt;
&lt;br /&gt;
Die Integration von Datenbanken ist ein zentraler Aspekt der PHP-Webentwicklung. Dieses Kapitel hat die Grundlagen für die Interaktion mit MySQL-Datenbanken gelegt. Hier sind die wichtigsten Punkte und Best Practices zusammengefasst:&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Wahl der Erweiterung:&amp;#039;&amp;#039;&amp;#039; Verwenden Sie ausschließlich die modernen Erweiterungen &amp;#039;&amp;#039;&amp;#039;PDO&amp;#039;&amp;#039;&amp;#039; oder &amp;#039;&amp;#039;&amp;#039;MySQLi&amp;#039;&amp;#039;&amp;#039;.[2, 29, 5, 15]&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;PDO&amp;#039;&amp;#039;&amp;#039; wird generell für neue Projekte empfohlen, da es Datenbankunabhängigkeit bietet und eine konsistentere, oft als einfacher empfundene API (insbesondere bei der Parameterbindung) hat.[16, 3, 64]&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;MySQLi&amp;#039;&amp;#039;&amp;#039; ist eine gute Wahl, wenn ausschließlich mit MySQL gearbeitet wird und spezifische MySQL-Funktionen benötigt werden oder wenn eine prozedurale Schnittstelle bevorzugt wird.[15]&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Prepared Statements:&amp;#039;&amp;#039;&amp;#039; Dies ist die wichtigste Sicherheitsmaßnahme. Verwenden Sie Prepared Statements immer dann, wenn externe Daten (Benutzereingaben, URL-Parameter etc.) Teil einer SQL-Abfrage werden (insbesondere bei &amp;lt;code&amp;gt;WHERE&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;INSERT&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;UPDATE&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;DELETE&amp;lt;/code&amp;gt;).[2, 25, 5] Sie verhindern SQL Injection zuverlässig durch die Trennung von Code und Daten.&lt;br /&gt;
&lt;br /&gt;
 &amp;#039;&amp;#039;&amp;#039;Sichere Fehlerbehandlung:&amp;#039;&amp;#039;&amp;#039; Konfigurieren Sie PDO (&amp;lt;code&amp;gt;PDO::ATTR_ERRMODE =&amp;amp;gt; PDO::ERRMODE_EXCEPTION&amp;lt;/code&amp;gt;) oder MySQLi (&amp;lt;code&amp;gt;mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT)&amp;lt;/code&amp;gt;) so, dass Fehler als Exceptions behandelt werden.[1, 18] Fangen Sie diese Exceptions ab und loggen Sie detaillierte Fehlermeldungen serverseitig, anstatt sie dem Benutzer anzuzeigen.[20, 5]&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Input Validation:&amp;#039;&amp;#039;&amp;#039; Validieren Sie alle Benutzereingaben serverseitig auf Typ, Format und erlaubte Werte, bevor Sie sie an die Datenbank übergeben. Dies ist eine wichtige zusätzliche Schutzschicht.[4, 5]&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Prinzip der geringsten Rechte (Least Privilege):&amp;#039;&amp;#039;&amp;#039; Der Datenbankbenutzer, den Ihre PHP-Anwendung verwendet, sollte nur die minimal notwendigen Berechtigungen für seine Aufgaben besitzen.[5]&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Sichere Zugangsdaten:&amp;#039;&amp;#039;&amp;#039; Speichern Sie Datenbank-Credentials (Hostname, Benutzername, Passwort) niemals direkt im PHP-Code oder in Dateien innerhalb des Web-Roots.[20, 65] Verwenden Sie stattdessen Konfigurationsdateien außerhalb des öffentlich zugänglichen Bereichs oder Umgebungsvariablen.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Code-Struktur:&amp;#039;&amp;#039;&amp;#039; Kapseln Sie Datenbankverbindungslogik und wiederkehrende Abfragen in Funktionen oder Klassen, um die Wartbarkeit und Wiederverwendbarkeit zu verbessern.[20]&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Effizienz:&amp;#039;&amp;#039;&amp;#039; Laden Sie große Ergebnismengen nicht komplett in den Speicher (&amp;lt;code&amp;gt;fetchAll&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;fetch_all&amp;lt;/code&amp;gt;). Verwenden Sie stattdessen zeilenweise Verarbeitung (&amp;lt;code&amp;gt;while&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;fetch&amp;lt;/code&amp;gt;) oder Iteratoren (PDO mit PHP 8+).[37, 40] Nutzen Sie persistente Verbindungen nur nach sorgfältiger Abwägung der Vor- und Nachteile.[7, 27]&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Defense-in-Depth:&amp;#039;&amp;#039;&amp;#039; Betrachten Sie Sicherheit als Gesamtkonzept. Die Kombination aus der richtigen API-Wahl, konsequenten Prepared Statements, Eingabevalidierung, minimalen Rechten, sicherer Fehlerbehandlung, sicherer Speicherung von Zugangsdaten und regelmäßigen Software-Updates bildet eine robuste Verteidigung.&lt;/div&gt;</summary>
		<author><name>Völkl Anna</name></author>
	</entry>
	<entry>
		<id>https://mediawiki.fernfh.ac.at/mediawiki/index.php?title=Datenbankintegration_(Webdevelopment)&amp;diff=6575</id>
		<title>Datenbankintegration (Webdevelopment)</title>
		<link rel="alternate" type="text/html" href="https://mediawiki.fernfh.ac.at/mediawiki/index.php?title=Datenbankintegration_(Webdevelopment)&amp;diff=6575"/>
		<updated>2025-05-01T13:13:00Z</updated>

		<summary type="html">&lt;p&gt;Völkl Anna: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Kapitel X: Datenbankintegration mit PHP und MySQL =&lt;br /&gt;
&lt;br /&gt;
== Einleitung: Warum Datenbanken in PHP-Webanwendungen? ==&lt;br /&gt;
&lt;br /&gt;
Moderne Webanwendungen sind selten statisch. Sie leben von dynamischen Inhalten, Benutzerinteraktionen und der Fähigkeit, Informationen über Sitzungen hinweg zu speichern und wieder abzurufen. Ob es sich um ein Content-Management-System (CMS), einen Online-Shop, ein soziales Netzwerk oder eine einfache Benutzerverwaltung handelt – die Persistenz von Daten ist entscheidend. Hier kommen Datenbanken ins Spiel.&lt;br /&gt;
&lt;br /&gt;
PHP allein, als serverseitige Skriptsprache, kann Daten nur für die Dauer der Ausführung eines Skripts im Arbeitsspeicher halten. Sobald das Skript beendet ist, gehen diese Informationen verloren. Um Daten dauerhaft zu speichern, zu organisieren, zu ändern und effizient abzurufen, benötigen PHP-Anwendungen ein externes Datenbanksystem.&lt;br /&gt;
&lt;br /&gt;
Eine der populärsten Datenbanklösungen im Webentwicklungsbereich, insbesondere in Kombination mit PHP, ist MySQL. Als relationales Datenbankmanagementsystem (RDBMS) bietet MySQL eine strukturierte Methode zur Datenspeicherung in Tabellen, die miteinander in Beziehung stehen können. Es ist ein Kernbestandteil vieler Webserver-Setups wie LAMP (Linux, Apache, MySQL, PHP) oder LEMP (Linux, Nginx, MySQL, PHP).&lt;br /&gt;
&lt;br /&gt;
Um die Brücke zwischen der PHP-Anwendung und der MySQL-Datenbank zu schlagen, stellt PHP spezielle Erweiterungen (Extensions) bereit. Diese Erweiterungen fungieren als Schnittstelle oder API (Application Programming Interface), die es PHP-Skripten ermöglichen, eine Verbindung zur Datenbank herzustellen, SQL-Befehle zu senden und die von der Datenbank zurückgegebenen Ergebnisse zu empfangen und zu verarbeiten.&lt;br /&gt;
&lt;br /&gt;
Dieses Kapitel führt in die Grundlagen der Datenbankintegration mit PHP und MySQL ein. Es enthält die verschiedenen verfügbaren PHP-Erweiterungen, wobei der Schwerpunkt auf den modernen und sicheren Ansätzen liegt: PDO (PHP Data Objects) und MySQLi (MySQL Improved). Es wird gezeigt, wie man Verbindungen aufbaut, grundlegende Datenbankoperationen – bekannt als CRUD (Create, Read, Update, Delete) – durchführt und wie man Daten sicher abfragt und manipuliert. Ein besonderes Augenmerk liegt dabei auf der Sicherheit, insbesondere auf der Vermeidung von SQL-Injection-Angriffen durch den Einsatz von Prepared Statements. Zum Abschluss erfolgt ein Überblick über nützliche PHP-Array-Funktionen zur Verarbeitung der abgerufenen Datenbankergebnisse.&lt;br /&gt;
&lt;br /&gt;
== Überblick über PHP-Datenbankerweiterungen ==&lt;br /&gt;
&lt;br /&gt;
Die Art und Weise, wie PHP mit Datenbanken interagiert, hat sich im Laufe der Zeit weiterentwickelt. Ursprünglich gab es für jede populäre Datenbank eine eigene spezifische Erweiterung. Für MySQL waren dies die &amp;lt;code&amp;gt;mysql_*&amp;lt;/code&amp;gt; Funktionen. Diese sind jedoch inzwischen veraltet und wurden durch modernere, flexiblere und sicherere Alternativen ersetzt. Heute stehen Entwicklern hauptsächlich zwei empfohlene Erweiterungen für die Arbeit mit MySQL zur Verfügung: PDO und MySQLi.&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;PDO (PHP Data Objects):&amp;#039;&amp;#039;&amp;#039; Eine allgemeine Datenzugriffs-Abstraktionsschicht, die mit verschiedenen Datenbanksystemen verwendet werden kann.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;MySQLi (MySQL Improved):&amp;#039;&amp;#039;&amp;#039; Eine Erweiterung, die speziell für die Arbeit mit MySQL entwickelt wurde und dessen spezifische Funktionen unterstützt.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;&amp;lt;code&amp;gt;mysql_*&amp;lt;/code&amp;gt; Funktionen:&amp;#039;&amp;#039;&amp;#039; Die ursprüngliche MySQL-Erweiterung, die seit PHP 7.0 entfernt wurde und nicht mehr verwendet werden sollte.&lt;br /&gt;
Im Folgenden werden diese drei Optionen detaillierter vorgestellt.&lt;br /&gt;
&lt;br /&gt;
=== PDO (PHP Data Objects) ===&lt;br /&gt;
&lt;br /&gt;
Die PDO-Erweiterung definiert eine leichtgewichtige und konsistente Schnittstelle für den Datenbankzugriff in PHP. Sie fungiert als &amp;#039;&amp;#039;Datenzugriffs-Abstraktionsschicht&amp;#039;&amp;#039;. Das bedeutet, dass Entwickler unabhängig von der verwendeten Datenbank dieselben PDO-Funktionen nutzen können, um Abfragen auszuführen und Daten abzurufen.[1, 2] Es ist jedoch wichtig zu verstehen, dass PDO &amp;#039;&amp;#039;keine&amp;#039;&amp;#039; Datenbankabstraktion im Sinne einer SQL-Dialekt-Übersetzung oder der Emulation fehlender Datenbankfeatures bietet. Um eine Verbindung zu einer spezifischen Datenbank wie MySQL herzustellen, benötigt PDO einen entsprechenden datenbankspezifischen Treiber, z.B. &amp;lt;code&amp;gt;pdo_mysql&amp;lt;/code&amp;gt;, der in der PHP-Konfiguration aktiviert sein muss.[1]&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Vorteile von PDO:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Datenbankunabhängigkeit:&amp;#039;&amp;#039;&amp;#039; Der größte Vorteil von PDO ist die Möglichkeit, den Code für Datenbankinteraktionen weitgehend unverändert zu lassen, selbst wenn das zugrunde liegende Datenbanksystem gewechselt wird (z.B. von MySQL zu PostgreSQL).[1, 2] Dies erhöht die Portabilität und Flexibilität von PHP-Anwendungen erheblich.[2, 3]&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Sicherheit durch Prepared Statements:&amp;#039;&amp;#039;&amp;#039; PDO bietet eine robuste und konsistente Implementierung von Prepared Statements. Diese Technik ist der Goldstandard zur Verhinderung von SQL-Injection-Angriffen, da sie die SQL-Befehlsstruktur strikt von den übergebenen Daten trennt.[1, 2, 4, 5, 6]&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Benutzerfreundlichkeit und Konsistenz:&amp;#039;&amp;#039;&amp;#039; PDO stellt eine einheitliche API mit nützlichen Hilfsfunktionen bereit, wie z.B. vielfältige &amp;quot;Fetch Modes&amp;quot;, um die Struktur der abgerufenen Daten zu steuern.[2] Die konsistente Funktionsweise über verschiedene Datenbanktreiber hinweg vereinfacht die Entwicklung.&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Moderne Fehlerbehandlung:&amp;#039;&amp;#039;&amp;#039; PDO verwendet standardmäßig Exceptions (&amp;lt;code&amp;gt;PDOException&amp;lt;/code&amp;gt;) zur Signalisierung von Fehlern.[1, 2, 7] Dies ermöglicht eine strukturierte und moderne Fehlerbehandlung mittels &amp;lt;code&amp;gt;try...catch&amp;lt;/code&amp;gt;-Blöcken, was als Best Practice gilt.[1, 2]&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Verbindungsaufbau mit PDO (Beispiel MySQL):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;DSN (Data Source Name):&amp;#039;&amp;#039;&amp;#039; Die Verbindungsparameter werden in einer Zeichenkette, dem DSN, übergeben. Für MySQL hat der DSN typischerweise das Format &amp;lt;code&amp;gt;mysql:host=hostname;dbname=datenbankname;charset=zeichensatz;unix_socket=pfad_zum_socket&amp;lt;/code&amp;gt;. Die Angabe des Zeichensatzes (z.B. &amp;lt;code&amp;gt;utf8mb4&amp;lt;/code&amp;gt;) ist wichtig für die korrekte Datenübertragung.[1, 7] Beispiel: &amp;lt;code&amp;gt;mysql:host=localhost;dbname=test;charset=utf8mb4&amp;lt;/code&amp;gt;.&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;PDO-Konstruktor:&amp;#039;&amp;#039;&amp;#039; Eine Verbindung wird durch Instanziierung der PDO-Klasse hergestellt: &amp;lt;code&amp;gt;$pdo = new PDO($dsn, $username, $password, $options);&amp;lt;/code&amp;gt;.[1, 7, 8] Die Parameter sind der DSN, der Datenbankbenutzername, das Passwort und ein optionales Array mit Treiberoptionen.&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Verbindungsoptionen (&amp;lt;code&amp;gt;$options&amp;lt;/code&amp;gt;):&amp;#039;&amp;#039;&amp;#039; Über dieses Array können wichtige Verhaltensweisen von PDO gesteuert werden:&lt;br /&gt;
*** &amp;lt;code&amp;gt;PDO::ATTR_ERRMODE =&amp;amp;gt; PDO::ERRMODE_EXCEPTION&amp;lt;/code&amp;gt;: Dies ist die empfohlene Einstellung für die Fehlerbehandlung. PDO wirft bei Fehlern eine &amp;lt;code&amp;gt;PDOException&amp;lt;/code&amp;gt;, die abgefangen werden kann.[1, 2, 4]&lt;br /&gt;
*** &amp;lt;code&amp;gt;PDO::ATTR_DEFAULT_FETCH_MODE =&amp;amp;gt; PDO::FETCH_ASSOC&amp;lt;/code&amp;gt;: Legt fest, dass Daten standardmäßig als assoziatives Array (&amp;lt;code&amp;gt;spaltenname =&amp;amp;gt; wert&amp;lt;/code&amp;gt;) zurückgegeben werden, was oft die Weiterverarbeitung erleichtert.[1, 9]&lt;br /&gt;
*** &amp;lt;code&amp;gt;PDO::ATTR_EMULATE_PREPARES =&amp;amp;gt; false&amp;lt;/code&amp;gt;: Deaktiviert die Emulation von Prepared Statements durch PDO und zwingt die Verwendung nativer Prepared Statements des Datenbanktreibers. Dies gilt als die sicherste und oft performanteste Methode.[1, 10, 9, 11] Standardmäßig ist die Emulation aktiviert (&amp;lt;code&amp;gt;true&amp;lt;/code&amp;gt;). Obwohl PDO auch im emulierten Modus durch korrektes Escaping Schutz vor SQL Injection bietet [12, 5, 13], eliminiert die Deaktivierung potenzielle, wenn auch seltene, Sicherheitsrisiken, die in bestimmten Konstellationen (z.B. bei Verwendung exotischer Zeichensätze wie GBK [11]) auftreten könnten. Die explizite Deaktivierung ist daher eine wichtige Best Practice.&lt;br /&gt;
*** &amp;lt;code&amp;gt;PDO::ATTR_PERSISTENT =&amp;amp;gt; true&amp;lt;/code&amp;gt;: Aktiviert persistente Datenbankverbindungen. Diese werden nicht am Ende des Skripts geschlossen, sondern in einem Pool gehalten und für nachfolgende Anfragen wiederverwendet. Dies kann die Performance von Webanwendungen verbessern, da der Overhead des Verbindungsaufbaus entfällt. Die Verwendung erfordert jedoch sorgfältige Überlegung, da der Zustand der Verbindung zwischen Anfragen bestehen bleiben kann.[7]&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Fehlerbehandlung:&amp;#039;&amp;#039;&amp;#039; Der Verbindungsaufbau sollte immer in einem &amp;lt;code&amp;gt;try...catch&amp;lt;/code&amp;gt;-Block erfolgen, um &amp;lt;code&amp;gt;PDOException&amp;lt;/code&amp;gt; abzufangen und angemessen darauf zu reagieren (z.B. Fehlermeldung loggen, alternative Aktion ausführen).[1, 7] Es ist kritisch, detaillierte Fehlermeldungen in Produktionsumgebungen nicht an den Benutzer auszugeben, da sie sensible Informationen enthalten könnten. Die PHP-Einstellung &amp;lt;code&amp;gt;display_errors&amp;lt;/code&amp;gt; sollte daher auf &amp;lt;code&amp;gt;0&amp;lt;/code&amp;gt; gesetzt sein.[7]&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Verbindung schließen:&amp;#039;&amp;#039;&amp;#039; Eine PDO-Verbindung bleibt für die Lebensdauer des PDO-Objekts aktiv. Um sie explizit zu schließen, müssen alle Referenzen auf das PDO-Objekt und alle davon abgeleiteten &amp;lt;code&amp;gt;PDOStatement&amp;lt;/code&amp;gt;-Objekte entfernt werden, typischerweise durch Zuweisung von &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt;.[7] PHP schließt die Verbindung automatisch am Ende des Skriptlaufs, wenn dies nicht manuell geschieht.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Code-Beispiel (PDO-Verbindung zu MySQL):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;amp;lt;?php&lt;br /&gt;
$host = &amp;#039;localhost&amp;#039;;&lt;br /&gt;
$dbname = &amp;#039;meine_datenbank&amp;#039;;&lt;br /&gt;
$username = &amp;#039;mein_benutzer&amp;#039;;&lt;br /&gt;
$password = &amp;#039;mein_passwort&amp;#039;;&lt;br /&gt;
$charset = &amp;#039;utf8mb4&amp;#039;;&lt;br /&gt;
&lt;br /&gt;
$dsn = &amp;quot;mysql:host=$host;dbname=$dbname;charset=$charset&amp;quot;;&lt;br /&gt;
$options =;&lt;br /&gt;
&lt;br /&gt;
try {&lt;br /&gt;
 $pdo = new PDO($dsn, $username, $password, $options);&lt;br /&gt;
 // Erfolgreich verbunden, $pdo-Objekt kann nun verwendet werden.&lt;br /&gt;
 // echo &amp;quot;Verbindung erfolgreich hergestellt!&amp;quot;;&lt;br /&gt;
} catch (\PDOException $e) {&lt;br /&gt;
 // Fehler beim Verbindungsaufbau&lt;br /&gt;
 // In Produktion: Fehler loggen, statt ausgeben&lt;br /&gt;
 error_log(&amp;quot;Datenbankverbindungsfehler: &amp;quot;. $e---&amp;amp;gt;getMessage());&lt;br /&gt;
 die(&amp;quot;Es gab ein Problem mit der Datenbankverbindung. Bitte versuchen Sie es später erneut.&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//... hier Datenbankoperationen mit $pdo durchführen...&lt;br /&gt;
&lt;br /&gt;
// Verbindung explizit schließen (optional, da PHP dies am Skriptende tut)&lt;br /&gt;
// $pdo = null;&lt;br /&gt;
?&amp;amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Dieses Beispiel zeigt den empfohlenen Weg, eine PDO-Verbindung zu MySQL herzustellen, inklusive wichtiger Optionen für Fehlerbehandlung und Sicherheit sowie einem &amp;lt;code&amp;gt;try...catch&amp;lt;/code&amp;gt;-Block zum Abfangen von Verbindungsfehlern.&lt;br /&gt;
&lt;br /&gt;
=== MySQLi (MySQL Improved) ===&lt;br /&gt;
&lt;br /&gt;
Die MySQLi-Erweiterung ist, wie der Name schon sagt, speziell für die Interaktion mit MySQL-Datenbanken konzipiert und bietet Unterstützung für Funktionen, die ab MySQL Version 4.1 eingeführt wurden.[14, 15] Sie ermöglicht den Zugriff auf MySQL-spezifische Features, die über PDO möglicherweise nicht direkt verfügbar sind.[16, 15]&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Duale Schnittstelle:&amp;#039;&amp;#039;&amp;#039; Ein charakteristisches Merkmal von MySQLi ist die Unterstützung von zwei Programmierstilen [17]:&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Prozedural:&amp;#039;&amp;#039;&amp;#039; Dieser Stil ähnelt den alten &amp;lt;code&amp;gt;mysql_*&amp;lt;/code&amp;gt; Funktionen. Die Funktionsnamen beginnen typischerweise mit &amp;lt;code&amp;gt;mysqli_&amp;lt;/code&amp;gt; (z.B. &amp;lt;code&amp;gt;mysqli_connect()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;mysqli_query()&amp;lt;/code&amp;gt;). Der Datenbankverbindungs-Handle (eine Ressource oder ein Objekt) muss dabei explizit als erster Parameter an die meisten Funktionen übergeben werden.[17] Dieser Stil wird oft von Entwicklern bevorzugt, die von der alten &amp;lt;code&amp;gt;mysql&amp;lt;/code&amp;gt;-Erweiterung migrieren.[17]&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Objektorientiert (OO):&amp;#039;&amp;#039;&amp;#039; Hier wird ein Objekt der &amp;lt;code&amp;gt;mysqli&amp;lt;/code&amp;gt;-Klasse erstellt, und Datenbankoperationen werden als Methoden dieses Objekts aufgerufen (z.B. &amp;lt;code&amp;gt;$mysqli-&amp;amp;gt;query()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;$mysqli-&amp;amp;gt;prepare()&amp;lt;/code&amp;gt;).[17] Dieser Ansatz passt oft besser zu modernen PHP-Entwicklungspraktiken und vermeidet die Notwendigkeit, den Verbindungshandle ständig zu übergeben. Die offizielle PHP-Dokumentation für MySQLi ist primär im objektorientierten Stil verfasst.[17] Es gibt keine nennenswerten Performance-Unterschiede zwischen den beiden Stilen; die Wahl ist meist eine Frage der persönlichen Präferenz oder Projektkonventionen.[17]&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Verbindungsaufbau mit MySQLi:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Prozedural:&amp;#039;&amp;#039;&amp;#039; Die Funktion &amp;lt;code&amp;gt;mysqli_connect()&amp;lt;/code&amp;gt; wird verwendet: &amp;lt;code&amp;gt;$link = mysqli_connect($host, $user, $pass, $db, $port, $socket);&amp;lt;/code&amp;gt;. Sie gibt bei Erfolg ein &amp;lt;code&amp;gt;mysqli&amp;lt;/code&amp;gt;-Objekt (oder &amp;lt;code&amp;gt;false&amp;lt;/code&amp;gt; bei Fehlern vor PHP 8.1) zurück.[17, 18] Fehler können mit &amp;lt;code&amp;gt;mysqli_connect_errno()&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;mysqli_connect_error()&amp;lt;/code&amp;gt; geprüft werden.[18]&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Objektorientiert:&amp;#039;&amp;#039;&amp;#039; Ein neues &amp;lt;code&amp;gt;mysqli&amp;lt;/code&amp;gt;-Objekt wird instanziiert: &amp;lt;code&amp;gt;$mysqli = new mysqli($host, $user, $pass, $db, $port, $socket);&amp;lt;/code&amp;gt;. Wichtig: Der Konstruktor gibt &amp;#039;&amp;#039;immer&amp;#039;&amp;#039; ein Objekt zurück, auch wenn die Verbindung fehlschlägt. Der Verbindungsstatus muss explizit über die Eigenschaften &amp;lt;code&amp;gt;$mysqli-&amp;amp;gt;connect_errno&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;$mysqli-&amp;amp;gt;connect_error&amp;lt;/code&amp;gt; geprüft werden.[17, 18]&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Fortgeschrittene Verbindung:&amp;#039;&amp;#039;&amp;#039; Für feinere Kontrolle über Verbindungsoptionen (z.B. Setzen von Timeouts oder Initialisierungsbefehlen &amp;#039;&amp;#039;vor&amp;#039;&amp;#039; dem Verbindungsaufbau) können &amp;lt;code&amp;gt;mysqli_init()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;mysqli_options()&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;mysqli_real_connect()&amp;lt;/code&amp;gt; verwendet werden.[18, 19]&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Fehlerbehandlung:&amp;#039;&amp;#039;&amp;#039; Neben der manuellen Prüfung von Fehlercodes und -meldungen ist die empfohlene Methode, MySQLi so zu konfigurieren, dass es bei Fehlern Exceptions wirft. Dies geschieht mit &amp;lt;code&amp;gt;mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);&amp;lt;/code&amp;gt;. Danach können Datenbankfehler elegant mit &amp;lt;code&amp;gt;try...catch&amp;lt;/code&amp;gt;-Blöcken behandelt werden, ähnlich wie bei PDO.[18, 20, 21, 22, 23, 24, 25, 26]&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Verbindung schließen:&amp;#039;&amp;#039;&amp;#039; Prozedural mit &amp;lt;code&amp;gt;mysqli_close($link)&amp;lt;/code&amp;gt;, objektorientiert mit &amp;lt;code&amp;gt;$mysqli-&amp;amp;gt;close()&amp;lt;/code&amp;gt;.[17, 18, 19]&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Persistente Verbindungen:&amp;#039;&amp;#039;&amp;#039; MySQLi unterstützt ebenfalls persistente Verbindungen, die über PHP-Konfigurationseinstellungen (&amp;lt;code&amp;gt;mysqli.allow_persistent&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;mysqli.max_persistent&amp;lt;/code&amp;gt;) gesteuert werden.[27] Standardmäßig setzt MySQLi den Zustand einer wiederverwendeten persistenten Verbindung zurück (via &amp;lt;code&amp;gt;mysqli::change_user()&amp;lt;/code&amp;gt;), was zwar für Konsistenz sorgt, aber auch Performance kosten kann.[27]&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Code-Beispiele (MySQLi-Verbindungen):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Objektorientiert (mit Exception-Handling):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;amp;lt;?php&lt;br /&gt;
$host = &amp;#039;localhost&amp;#039;;&lt;br /&gt;
$user = &amp;#039;mein_benutzer&amp;#039;;&lt;br /&gt;
$pass = &amp;#039;mein_passwort&amp;#039;;&lt;br /&gt;
$db = &amp;#039;meine_datenbank&amp;#039;;&lt;br /&gt;
&lt;br /&gt;
// Fehlerberichterstattung für Exceptions aktivieren&lt;br /&gt;
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);&lt;br /&gt;
&lt;br /&gt;
try {&lt;br /&gt;
 $mysqli = new mysqli($host, $user, $pass, $db);&lt;br /&gt;
 // Zeichensatz setzen (wichtig!)&lt;br /&gt;
 $mysqli---&amp;amp;gt;set_charset(&amp;#039;utf8mb4&amp;#039;);&lt;br /&gt;
 // Erfolgreich verbunden&lt;br /&gt;
 // echo &amp;quot;Verbindung erfolgreich (OO)! Host-Info: &amp;quot;. $mysqli-&amp;amp;gt;host_info;&lt;br /&gt;
} catch (mysqli_sql_exception $e) {&lt;br /&gt;
 // Fehler beim Verbindungsaufbau oder set_charset&lt;br /&gt;
 error_log(&amp;quot;MySQLi Verbindungsfehler: &amp;quot;. $e-&amp;amp;gt;getMessage());&lt;br /&gt;
 die(&amp;quot;Es gab ein Problem mit der Datenbankverbindung.&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//... Datenbankoperationen mit $mysqli...&lt;br /&gt;
&lt;br /&gt;
// Verbindung schließen&lt;br /&gt;
// $mysqli-&amp;amp;gt;close();&lt;br /&gt;
?&amp;amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Prozedural (mit Exception-Handling):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!--?php$host = &amp;#039;localhost&amp;#039;;&lt;br /&gt;
$user = &amp;#039;mein_benutzer&amp;#039;;&lt;br /&gt;
$pass = &amp;#039;mein_passwort&amp;#039;;&lt;br /&gt;
$db = &amp;#039;meine_datenbank&amp;#039;;&lt;br /&gt;
&lt;br /&gt;
// Fehlerberichterstattung für Exceptions aktivieren&lt;br /&gt;
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);&lt;br /&gt;
&lt;br /&gt;
try {&lt;br /&gt;
    $link = mysqli_connect($host, $user, $pass, $db);&lt;br /&gt;
    // Zeichensatz setzen&lt;br /&gt;
    mysqli_set_charset($link, &amp;#039;utf8mb4&amp;#039;);&lt;br /&gt;
    // Erfolgreich verbunden&lt;br /&gt;
    // echo &amp;quot;Verbindung erfolgreich (Prozedural)! Host-Info: &amp;quot;. mysqli_get_host_info($link);&lt;br /&gt;
} catch (mysqli_sql_exception $e) {&lt;br /&gt;
    // Fehler beim Verbindungsaufbau oder set_charset&lt;br /&gt;
    error_log(&amp;quot;MySQLi Verbindungsfehler: &amp;quot;. $e---&amp;gt;getMessage());&lt;br /&gt;
 die(&amp;quot;Es gab ein Problem mit der Datenbankverbindung.&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//... Datenbankoperationen mit $link...&lt;br /&gt;
&lt;br /&gt;
// Verbindung schließen&lt;br /&gt;
// mysqli_close($link);&lt;br /&gt;
?&amp;amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Bedeutung von &amp;lt;code&amp;gt;mysqlnd&amp;lt;/code&amp;gt;:&amp;#039;&amp;#039;&amp;#039; Die Benutzerfreundlichkeit, insbesondere bei der Arbeit mit Prepared Statements, wird stark durch den verwendeten MySQL-Treiber beeinflusst. Der &amp;#039;&amp;#039;&amp;#039;MySQL Native Driver (&amp;lt;code&amp;gt;mysqlnd&amp;lt;/code&amp;gt;)&amp;#039;&amp;#039;&amp;#039;, der seit PHP 5.4 standardmäßig enthalten ist [16], ist hier entscheidend. &amp;lt;code&amp;gt;mysqlnd&amp;lt;/code&amp;gt; stellt die Methode &amp;lt;code&amp;gt;get_result()&amp;lt;/code&amp;gt; (bzw. die Funktion &amp;lt;code&amp;gt;mysqli_stmt_get_result()&amp;lt;/code&amp;gt;) zur Verfügung.[25, 15] Diese Methode erlaubt es, nach dem Ausführen eines Prepared Statements ein Standard-&amp;lt;code&amp;gt;mysqli_result&amp;lt;/code&amp;gt;-Objekt zu erhalten, von dem dann wie gewohnt mit &amp;lt;code&amp;gt;fetch_assoc()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;fetch_all()&amp;lt;/code&amp;gt; etc. die Ergebnisse abgerufen werden können.[25] Ohne &amp;lt;code&amp;gt;mysqlnd&amp;lt;/code&amp;gt; (bei Verwendung der älteren &amp;lt;code&amp;gt;libmysqlclient&amp;lt;/code&amp;gt;-Bibliothek) ist das Abrufen von Ergebnissen aus Prepared Statements deutlich umständlicher und erfordert die manuelle Bindung jeder einzelnen Ergebnisspalte an PHP-Variablen mittels &amp;lt;code&amp;gt;bind_result()&amp;lt;/code&amp;gt; und anschließendes &amp;lt;code&amp;gt;fetch()&amp;lt;/code&amp;gt;.[25] Da &amp;lt;code&amp;gt;mysqlnd&amp;lt;/code&amp;gt; heute der Standard ist, konzentrieren sich moderne Tutorials und Beispiele meist auf die &amp;lt;code&amp;gt;get_result()&amp;lt;/code&amp;gt;-Methode, aber es ist wichtig, diesen Hintergrund zu kennen, falls man auf ältere Systeme oder Konfigurationen trifft.&lt;br /&gt;
&lt;br /&gt;
=== Veraltete &amp;lt;code&amp;gt;mysql_*&amp;lt;/code&amp;gt; Funktionen ===&lt;br /&gt;
&lt;br /&gt;
Die &amp;lt;code&amp;gt;mysql_*&amp;lt;/code&amp;gt; Funktionsfamilie (&amp;lt;code&amp;gt;mysql_connect&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;mysql_query&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;mysql_fetch_assoc&amp;lt;/code&amp;gt; etc.) war die ursprüngliche Methode, um in PHP mit MySQL-Datenbanken zu interagieren. Diese Erweiterung ist jedoch &amp;#039;&amp;#039;&amp;#039;stark veraltet&amp;#039;&amp;#039;&amp;#039; und sollte &amp;#039;&amp;#039;&amp;#039;unter keinen Umständen mehr für neue Projekte verwendet werden&amp;#039;&amp;#039;&amp;#039;.&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Status:&amp;#039;&amp;#039;&amp;#039; Die &amp;lt;code&amp;gt;mysql_*&amp;lt;/code&amp;gt; Erweiterung wurde in &amp;#039;&amp;#039;&amp;#039;PHP 5.5&amp;#039;&amp;#039;&amp;#039; offiziell als &amp;quot;deprecated&amp;quot; (veraltet) markiert, was bedeutet, dass ihre Verwendung ab dieser Version Warnungen (&amp;lt;code&amp;gt;E_DEPRECATED&amp;lt;/code&amp;gt;) erzeugte.[28, 29] Mit der Veröffentlichung von &amp;#039;&amp;#039;&amp;#039;PHP 7.0&amp;#039;&amp;#039;&amp;#039; wurde die Erweiterung &amp;#039;&amp;#039;&amp;#039;vollständig entfernt&amp;#039;&amp;#039;&amp;#039;.[28, 29] PHP-Code, der diese Funktionen nutzt, führt auf PHP 7.0 oder neuer zu fatalen Fehlern und funktioniert nicht mehr.[28, 29]&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Gründe für die Entfernung:&amp;#039;&amp;#039;&amp;#039; Es gab zwingende Gründe für diesen Schritt:&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Gravierende Sicherheitsrisiken:&amp;#039;&amp;#039;&amp;#039; Der Hauptgrund war die Anfälligkeit für SQL-Injection-Angriffe. Die &amp;lt;code&amp;gt;mysql_*&amp;lt;/code&amp;gt; Funktionen boten keinen eingebauten Mechanismus wie Prepared Statements. Entwickler mussten Benutzereingaben manuell mit &amp;lt;code&amp;gt;mysql_real_escape_string()&amp;lt;/code&amp;gt; &amp;quot;escapen&amp;quot;, eine Methode, die leicht vergessen oder falsch angewendet werden konnte, was zu weit verbreiteten Sicherheitslücken führte.[29, 30] Das Fehlen von Prepared Statements in dieser alten Erweiterung ist direkt ursächlich für die hohe Zahl an SQL-Injection-Schwachstellen in älteren PHP-Anwendungen.[29]&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Fehlende Unterstützung moderner MySQL-Features:&amp;#039;&amp;#039;&amp;#039; Die Erweiterung wurde ursprünglich für MySQL Version 3.23 entwickelt und seitdem kaum weiterentwickelt.[29] Sie unterstützte viele wichtige Funktionen moderner MySQL-Versionen nicht, darunter Prepared Statements, Stored Procedures, Transaktionen, SSL-Verschlüsselung, Kompression, Multi-Statements und vollständige Zeichensatzunterstützung.[29]&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Schlechte Wartbarkeit:&amp;#039;&amp;#039;&amp;#039; Der Code der Erweiterung war veraltet und wurde immer schwieriger zu warten und an neue Versionen der MySQL-Client-Bibliotheken anzupassen.[29]&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Förderung unsicherer Praktiken:&amp;#039;&amp;#039;&amp;#039; Da viele alte Tutorials und Codebeispiele die &amp;lt;code&amp;gt;mysql_*&amp;lt;/code&amp;gt; Funktionen unsicher verwendeten, trug die fortgesetzte Verfügbarkeit zur Verbreitung schlechter und unsicherer Programmierpraktiken bei.[29]&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Verwendung heute:&amp;#039;&amp;#039;&amp;#039; Die klare Antwort lautet: &amp;#039;&amp;#039;&amp;#039;Nein&amp;#039;&amp;#039;&amp;#039;. Diese Funktionen existieren in modernen PHP-Versionen nicht mehr.[31, 30] Jede Anwendung, die sie noch verwendet, ist nicht nur unsicher, sondern auch inkompatibel mit aktueller PHP-Software. Eine Migration zu PDO oder MySQLi ist unumgänglich, um Sicherheit und Kompatibilität zu gewährleisten.[28, 29] Die erzwungene Migration durch die Entfernung in PHP 7.0 unterstreicht, wie wichtig es ist, Deprecation-Warnungen ernst zu nehmen und Code proaktiv zu aktualisieren, um zukünftige Probleme zu vermeiden.[29, 32]&lt;br /&gt;
&lt;br /&gt;
=== Tabelle 1: Vergleich der PHP-Datenbankerweiterungen für MySQL ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Merkmal !! PDO (PHP Data Objects) !! MySQLi (MySQL Improved) !! &amp;lt;code&amp;gt;mysql_*&amp;lt;/code&amp;gt; (Veraltet)&lt;br /&gt;
|-&amp;lt;br&amp;gt;&lt;br /&gt;
| aktiv=&amp;quot;&amp;quot; empfohlen=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| aktiv=&amp;quot;&amp;quot; empfohlen=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Entfernt (seit PHP 7.0) [29]&lt;br /&gt;
|-&amp;lt;br&amp;gt;&lt;br /&gt;
| objektorientiert=&amp;quot;&amp;quot; oo=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| oo=&amp;quot;&amp;quot; prozedural=&amp;quot;&amp;quot; 17=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Prozedural&lt;br /&gt;
|-&amp;lt;br&amp;gt;&lt;br /&gt;
| multi-datenbank=&amp;quot;&amp;quot; mysql=&amp;quot;&amp;quot; pgsql=&amp;quot;&amp;quot; sqlite=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| nur=&amp;quot;&amp;quot; mysql=&amp;quot;&amp;quot; 15=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Nur MySQL&lt;br /&gt;
|-&amp;lt;br&amp;gt;&lt;br /&gt;
| ja=&amp;quot;&amp;quot; nativ=&amp;quot;&amp;quot; empfohlen=&amp;quot;&amp;quot; emulation=&amp;quot;&amp;quot; m=&amp;quot;&amp;quot; glich=&amp;quot;&amp;quot; 1=&amp;quot;&amp;quot; 11=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| ja=&amp;quot;&amp;quot; nativ=&amp;quot;&amp;quot; 25=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Nein [29]&lt;br /&gt;
|- &lt;br /&gt;
| &amp;#039;&amp;#039;&amp;#039;SQL Injection Schutz&amp;#039;&amp;#039;&amp;#039; || sehr=&amp;quot;&amp;quot; gut=&amp;quot;&amp;quot; durch=&amp;quot;&amp;quot; prepared=&amp;quot;&amp;quot; statements=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Sehr gut (durch Prepared Statements) [25] || Schlecht (manuelles Escaping nötig) [29]&lt;br /&gt;
|-&amp;lt;br&amp;gt;&lt;br /&gt;
| gute=&amp;quot;&amp;quot; unterst=&amp;quot;&amp;quot; tzung=&amp;quot;&amp;quot; via=&amp;quot;&amp;quot; treiber=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Sehr gute Unterstützung (spezifisch) [15] || Veraltet, viele Features fehlen [29]&lt;br /&gt;
|-&amp;lt;br&amp;gt;&lt;br /&gt;
| hoch=&amp;quot;&amp;quot; 2=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| niedrig=&amp;quot;&amp;quot; mysql-spezifisch=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Niedrig&lt;br /&gt;
|-&amp;lt;br&amp;gt;&lt;br /&gt;
| exceptions=&amp;quot;&amp;quot; mwt-preservehtml=&amp;quot;&amp;quot; pdoexception=&amp;quot;&amp;quot; code=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| exceptions=&amp;quot;&amp;quot; mit=&amp;quot;&amp;quot; mwt-preservehtml=&amp;quot;&amp;quot; mysqli_report=&amp;quot;&amp;quot; code=&amp;quot;&amp;quot; fehlercodes=&amp;quot;&amp;quot; 18=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Fehlercodes / &amp;lt;code&amp;gt;mysql_error()&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
| &amp;#039;&amp;#039;&amp;#039;Nicht verwenden!&amp;#039;&amp;#039;&amp;#039; [29]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Grundlegende Datenbankoperationen (CRUD) ==&lt;br /&gt;
&lt;br /&gt;
Nachdem wir die verschiedenen Erweiterungen kennengelernt haben, wenden wir uns nun den grundlegenden Operationen zu, die man typischerweise mit einer Datenbank durchführt. Diese werden oft mit dem Akronym CRUD zusammengefasst:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;C&amp;#039;&amp;#039;&amp;#039;reate: Neue Datensätze erstellen (SQL: &amp;lt;code&amp;gt;INSERT&amp;lt;/code&amp;gt;).&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;R&amp;#039;&amp;#039;&amp;#039;ead: Vorhandene Datensätze lesen/abfragen (SQL: &amp;lt;code&amp;gt;SELECT&amp;lt;/code&amp;gt;).&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;U&amp;#039;&amp;#039;&amp;#039;pdate: Bestehende Datensätze ändern (SQL: &amp;lt;code&amp;gt;UPDATE&amp;lt;/code&amp;gt;).&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;D&amp;#039;&amp;#039;&amp;#039;elete: Datensätze löschen (SQL: &amp;lt;code&amp;gt;DELETE&amp;lt;/code&amp;gt;).&lt;br /&gt;
In den folgenden Abschnitten werden wir sehen, wie diese Operationen mit den modernen Erweiterungen PDO und MySQLi umgesetzt werden, wobei wir durchgehend &amp;#039;&amp;#039;&amp;#039;Prepared Statements&amp;#039;&amp;#039;&amp;#039; verwenden, um die Sicherheit zu gewährleisten.&lt;br /&gt;
&lt;br /&gt;
=== Daten abfragen (SELECT) ===&lt;br /&gt;
&lt;br /&gt;
Das Abfragen von Daten ist eine der häufigsten Datenbankoperationen.&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;PDO:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Ohne Parameter:&amp;#039;&amp;#039;&amp;#039; Wenn die Abfrage keine variablen Teile enthält (z.B. keine &amp;lt;code&amp;gt;WHERE&amp;lt;/code&amp;gt;-Klausel mit Benutzereingaben), kann die &amp;lt;code&amp;gt;query()&amp;lt;/code&amp;gt;-Methode verwendet werden. Sie führt die SQL-Abfrage direkt aus und gibt ein &amp;lt;code&amp;gt;PDOStatement&amp;lt;/code&amp;gt;-Objekt zurück, das zur Ergebnisauswertung dient.[2, 33]&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Beispiel: Alle Benutzer auswählen&lt;br /&gt;
$stmt = $pdo-&amp;amp;gt;query(&amp;quot;SELECT id, username FROM users&amp;quot;);&lt;br /&gt;
// $stmt kann nun zum Fetchen der Ergebnisse verwendet werden&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Mit Parametern (Prepared Statement):&amp;#039;&amp;#039;&amp;#039; Dies ist der Standardfall, wenn Benutzereingaben oder andere Variablen die Abfrage beeinflussen (z.B. in &amp;lt;code&amp;gt;WHERE&amp;lt;/code&amp;gt;- oder &amp;lt;code&amp;gt;LIMIT&amp;lt;/code&amp;gt;-Klauseln).&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Vorbereiten (&amp;lt;code&amp;gt;prepare&amp;lt;/code&amp;gt;):&amp;#039;&amp;#039;&amp;#039; Die SQL-Abfrage wird mit Platzhaltern (&amp;lt;code&amp;gt;?&amp;lt;/code&amp;gt; für positionale oder &amp;lt;code&amp;gt;:name&amp;lt;/code&amp;gt; für benannte Platzhalter) vorbereitet.[34, 33, 5]&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Beispiel: Benutzer mit bestimmter ID auswählen (positionaler Platzhalter)&lt;br /&gt;
$sql = &amp;quot;SELECT id, username, email FROM users WHERE id =?&amp;quot;;&lt;br /&gt;
$stmt = $pdo-&amp;amp;gt;prepare($sql);&lt;br /&gt;
&lt;br /&gt;
// Beispiel: Benutzer mit bestimmtem Status auswählen (benannter Platzhalter)&lt;br /&gt;
$sql_named = &amp;quot;SELECT id, username FROM users WHERE status = :status&amp;quot;;&lt;br /&gt;
$stmt_named = $pdo-&amp;amp;gt;prepare($sql_named);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
*# &amp;#039;&amp;#039;&amp;#039;Ausführen (&amp;lt;code&amp;gt;execute&amp;lt;/code&amp;gt;):&amp;#039;&amp;#039;&amp;#039; Die vorbereitete Anweisung wird mit den tatsächlichen Werten ausgeführt. Die Werte werden als Array an die &amp;lt;code&amp;gt;execute()&amp;lt;/code&amp;gt;-Methode übergeben. Bei positionalen Platzhaltern entspricht die Reihenfolge im Array der Reihenfolge der &amp;lt;code&amp;gt;?&amp;lt;/code&amp;gt; in der SQL-Abfrage. Bei benannten Platzhaltern wird ein assoziatives Array verwendet, dessen Schlüssel den Platzhalternamen entsprechen (mit oder ohne führenden Doppelpunkt).[34, 33, 5]&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Ausführen für positionalen Platzhalter&lt;br /&gt;
$user_id = 123;&lt;br /&gt;
$stmt-&amp;amp;gt;execute([$user_id]);&lt;br /&gt;
&lt;br /&gt;
// Ausführen für benannten Platzhalter&lt;br /&gt;
$user_status = &amp;#039;active&amp;#039;;&lt;br /&gt;
$stmt_named-&amp;amp;gt;execute([&amp;#039;:status&amp;#039; =&amp;amp;gt; $user_status]);&lt;br /&gt;
// oder: $stmt_named-&amp;amp;gt;execute([&amp;#039;status&amp;#039; =&amp;amp;gt; $user_status]);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
*# &amp;#039;&amp;#039;&amp;#039;Alternative Bindung:&amp;#039;&amp;#039;&amp;#039; Optional können Parameter auch explizit vor dem &amp;lt;code&amp;gt;execute()&amp;lt;/code&amp;gt;-Aufruf mit &amp;lt;code&amp;gt;bindValue()&amp;lt;/code&amp;gt; (bindet einen Wert) oder &amp;lt;code&amp;gt;bindParam()&amp;lt;/code&amp;gt; (bindet eine Variable als Referenz) gebunden werden. Dies gibt mehr Kontrolle über den Datentyp (z.B. &amp;lt;code&amp;gt;PDO::PARAM_INT&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;PDO::PARAM_STR&amp;lt;/code&amp;gt;).[5, 13]&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$stmt-&amp;amp;gt;bindValue(1, $user_id, PDO::PARAM_INT); // Position 1, Wert von $user_id als Integer&lt;br /&gt;
$stmt-&amp;amp;gt;execute();&lt;br /&gt;
&lt;br /&gt;
$stmt_named-&amp;amp;gt;bindParam(&amp;#039;:status&amp;#039;, $user_status, PDO::PARAM_STR); // Platzhalter :status, Variable $user_status als String&lt;br /&gt;
$stmt_named-&amp;amp;gt;execute();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;MySQLi:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Ohne Parameter:&amp;#039;&amp;#039;&amp;#039; Ähnlich wie bei PDO kann bei Abfragen ohne variable Teile die &amp;lt;code&amp;gt;query()&amp;lt;/code&amp;gt;-Methode (OO-Stil: &amp;lt;code&amp;gt;$mysqli-&amp;amp;gt;query()&amp;lt;/code&amp;gt;) oder die &amp;lt;code&amp;gt;mysqli_query()&amp;lt;/code&amp;gt;-Funktion (prozeduraler Stil: &amp;lt;code&amp;gt;mysqli_query($link, &amp;quot;...&amp;quot;)&amp;lt;/code&amp;gt;) verwendet werden. Sie geben ein &amp;lt;code&amp;gt;mysqli_result&amp;lt;/code&amp;gt;-Objekt zurück.[20, 25]&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// OO-Stil&lt;br /&gt;
$result = $mysqli-&amp;amp;gt;query(&amp;quot;SELECT id, username FROM users&amp;quot;);&lt;br /&gt;
// Prozedural&lt;br /&gt;
$result = mysqli_query($link, &amp;quot;SELECT id, username FROM users&amp;quot;);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Mit Parametern (Prepared Statement):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Vorbereiten (&amp;lt;code&amp;gt;prepare&amp;lt;/code&amp;gt;):&amp;#039;&amp;#039;&amp;#039; Die SQL-Abfrage wird mit &amp;lt;code&amp;gt;?&amp;lt;/code&amp;gt;-Platzhaltern vorbereitet. Im OO-Stil &amp;lt;code&amp;gt;$stmt = $mysqli-&amp;amp;gt;prepare(&amp;quot;...&amp;quot;)&amp;lt;/code&amp;gt;, prozedural &amp;lt;code&amp;gt;$stmt = mysqli_prepare($link, &amp;quot;...&amp;quot;)&amp;lt;/code&amp;gt;.[25, 35]&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// OO-Stil&lt;br /&gt;
$sql = &amp;quot;SELECT id, username, email FROM users WHERE id =?&amp;quot;;&lt;br /&gt;
$stmt = $mysqli-&amp;amp;gt;prepare($sql);&lt;br /&gt;
// Prozedural&lt;br /&gt;
$sql_proc = &amp;quot;SELECT id, username, email FROM users WHERE id =?&amp;quot;;&lt;br /&gt;
$stmt_proc = mysqli_prepare($link, $sql_proc);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
**# &amp;#039;&amp;#039;&amp;#039;Parameter binden (&amp;lt;code&amp;gt;bind_param&amp;lt;/code&amp;gt;):&amp;#039;&amp;#039;&amp;#039; Die Variablen werden an die Platzhalter gebunden. Dies ist ein wesentlicher Unterschied zu PDOs &amp;lt;code&amp;gt;execute&amp;lt;/code&amp;gt;-Array-Bindung. &amp;lt;code&amp;gt;bind_param()&amp;lt;/code&amp;gt; erfordert einen String, der die Datentypen der zu bindenden Variablen angibt (z.B. &amp;quot;i&amp;quot; für Integer, &amp;quot;s&amp;quot; für String, &amp;quot;d&amp;quot; für Double/Float, &amp;quot;b&amp;quot; für Blob), gefolgt von den Variablen selbst.[25, 35]&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$user_id = 123;&lt;br /&gt;
// OO-Stil&lt;br /&gt;
$stmt-&amp;amp;gt;bind_param(&amp;quot;i&amp;quot;, $user_id); // &amp;quot;i&amp;quot; für Integer&lt;br /&gt;
// Prozedural&lt;br /&gt;
mysqli_stmt_bind_param($stmt_proc, &amp;quot;i&amp;quot;, $user_id);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
**# &amp;#039;&amp;#039;&amp;#039;Ausführen (&amp;lt;code&amp;gt;execute&amp;lt;/code&amp;gt;):&amp;#039;&amp;#039;&amp;#039; Die vorbereitete Anweisung wird ausgeführt. OO-Stil: &amp;lt;code&amp;gt;$stmt-&amp;amp;gt;execute()&amp;lt;/code&amp;gt;, prozedural: &amp;lt;code&amp;gt;mysqli_stmt_execute($stmt)&amp;lt;/code&amp;gt;.[25, 36, 35]&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// OO-Stil&lt;br /&gt;
$stmt-&amp;amp;gt;execute();&lt;br /&gt;
// Prozedural&lt;br /&gt;
mysqli_stmt_execute($stmt_proc);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
**# &amp;#039;&amp;#039;&amp;#039;Ergebnis holen:&amp;#039;&amp;#039;&amp;#039; Um die Ergebnisse eines Prepared Statements in MySQLi zu erhalten, ist die Methode &amp;lt;code&amp;gt;get_result()&amp;lt;/code&amp;gt; (OO) bzw. &amp;lt;code&amp;gt;mysqli_stmt_get_result()&amp;lt;/code&amp;gt; (prozedural) am bequemsten. Sie gibt ein Standard-&amp;lt;code&amp;gt;mysqli_result&amp;lt;/code&amp;gt;-Objekt zurück, das dann mit den üblichen Fetch-Methoden verarbeitet werden kann. &amp;#039;&amp;#039;&amp;#039;Wichtig:&amp;#039;&amp;#039;&amp;#039; Diese Methode erfordert den &amp;lt;code&amp;gt;mysqlnd&amp;lt;/code&amp;gt;-Treiber.[25, 15]&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// OO-Stil&lt;br /&gt;
$result = $stmt-&amp;amp;gt;get_result();&lt;br /&gt;
// Prozedural&lt;br /&gt;
$result_proc = mysqli_stmt_get_result($stmt_proc);&lt;br /&gt;
// $result / $result_proc kann nun mit fetch_assoc() etc. verwendet werden.&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Die Alternative ohne &amp;lt;code&amp;gt;mysqlnd&amp;lt;/code&amp;gt; ist &amp;lt;code&amp;gt;bind_result()&amp;lt;/code&amp;gt;, bei der jede Ergebnisspalte an eine PHP-Variable gebunden wird, und &amp;lt;code&amp;gt;fetch()&amp;lt;/code&amp;gt; iterativ aufgerufen wird, um die Variablen zu füllen.[25] Dies ist deutlich umständlicher.&lt;br /&gt;
&lt;br /&gt;
=== Ergebnisse abrufen und verarbeiten ===&lt;br /&gt;
&lt;br /&gt;
Nachdem eine &amp;lt;code&amp;gt;SELECT&amp;lt;/code&amp;gt;-Abfrage erfolgreich ausgeführt wurde, müssen die zurückgegebenen Daten aus dem Ergebnisobjekt (&amp;lt;code&amp;gt;PDOStatement&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;mysqli_result&amp;lt;/code&amp;gt;) abgerufen (&amp;quot;gefetched&amp;quot;) werden.&lt;br /&gt;
&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Zeilenweise Iteration (&amp;lt;code&amp;gt;fetch&amp;lt;/code&amp;gt; im Loop):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
Dies ist der klassische Weg, um Ergebnisse zu verarbeiten, insbesondere wenn jede Zeile einzeln behandelt werden muss oder wenn die Ergebnismenge sehr groß ist.&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;PDO:&amp;#039;&amp;#039;&amp;#039; Die &amp;lt;code&amp;gt;fetch()&amp;lt;/code&amp;gt;-Methode des &amp;lt;code&amp;gt;PDOStatement&amp;lt;/code&amp;gt;-Objekts wird wiederholt in einer &amp;lt;code&amp;gt;while&amp;lt;/code&amp;gt;-Schleife aufgerufen. &amp;lt;code&amp;gt;fetch()&amp;lt;/code&amp;gt; gibt bei jedem Aufruf die nächste Zeile zurück (im Format des eingestellten Fetch-Modus, z.B. &amp;lt;code&amp;gt;PDO::FETCH_ASSOC&amp;lt;/code&amp;gt;) und &amp;lt;code&amp;gt;false&amp;lt;/code&amp;gt; (oder &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt; je nach Modus/Version), wenn keine weiteren Zeilen vorhanden sind.[2, 33]&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$stmt = $pdo-&amp;amp;gt;prepare(&amp;quot;SELECT name, email FROM users WHERE status =?&amp;quot;);&lt;br /&gt;
$stmt-&amp;amp;gt;execute([&amp;#039;active&amp;#039;]);&lt;br /&gt;
while ($row = $stmt-&amp;amp;gt;fetch(PDO::FETCH_ASSOC)) {&lt;br /&gt;
 echo &amp;quot;Name: &amp;quot;. htmlspecialchars($row[&amp;#039;name&amp;#039;]). &amp;quot;, E-Mail: &amp;quot;. htmlspecialchars($row[&amp;#039;email&amp;#039;]). &amp;quot;&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;MySQLi:&amp;#039;&amp;#039;&amp;#039; Die &amp;lt;code&amp;gt;fetch_assoc()&amp;lt;/code&amp;gt;-Methode (oder &amp;lt;code&amp;gt;fetch_row()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;fetch_object()&amp;lt;/code&amp;gt;, etc.) des &amp;lt;code&amp;gt;mysqli_result&amp;lt;/code&amp;gt;-Objekts wird in einer &amp;lt;code&amp;gt;while&amp;lt;/code&amp;gt;-Schleife verwendet. Sie gibt die nächste Zeile als Array (oder Objekt) zurück oder &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt;, wenn das Ende erreicht ist.[25, 15]&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// OO-Stil (nach $result = $stmt-&amp;amp;gt;get_result();)&lt;br /&gt;
while ($row = $result-&amp;amp;gt;fetch_assoc()) {&lt;br /&gt;
 echo &amp;quot;Name: &amp;quot;. htmlspecialchars($row[&amp;#039;name&amp;#039;]). &amp;quot;, E-Mail: &amp;quot;. htmlspecialchars($row[&amp;#039;email&amp;#039;]). &amp;quot;&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Prozedural (nach $result = mysqli_stmt_get_result($stmt);)&lt;br /&gt;
while ($row = mysqli_fetch_assoc($result)) {&lt;br /&gt;
 echo &amp;quot;Name: &amp;quot;. htmlspecialchars($row[&amp;#039;name&amp;#039;]). &amp;quot;, E-Mail: &amp;quot;. htmlspecialchars($row[&amp;#039;email&amp;#039;]). &amp;quot;&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Dieser Ansatz ist &amp;#039;&amp;#039;&amp;#039;speichereffizient&amp;#039;&amp;#039;&amp;#039;, da immer nur eine Zeile gleichzeitig im PHP-Speicher gehalten wird. Dies ist besonders wichtig bei Abfragen, die potenziell Tausende oder Millionen von Zeilen zurückgeben könnten.[37]&lt;br /&gt;
&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Alle Ergebnisse auf einmal abrufen (&amp;lt;code&amp;gt;fetchAll&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;fetch_all&amp;lt;/code&amp;gt;):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
Für kleinere bis mittlere Ergebnismengen ist es oft bequemer, alle Zeilen auf einmal in ein PHP-Array zu laden.&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;PDO:&amp;#039;&amp;#039;&amp;#039; Die &amp;lt;code&amp;gt;fetchAll()&amp;lt;/code&amp;gt;-Methode des &amp;lt;code&amp;gt;PDOStatement&amp;lt;/code&amp;gt;-Objekts gibt ein Array zurück, das alle Ergebniszeilen enthält.[33, 38] Der Fetch-Modus (z.B. &amp;lt;code&amp;gt;PDO::FETCH_ASSOC&amp;lt;/code&amp;gt;) kann als Argument übergeben werden.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$stmt = $pdo-&amp;amp;gt;prepare(&amp;quot;SELECT name, email FROM users WHERE status =?&amp;quot;);&lt;br /&gt;
$stmt-&amp;amp;gt;execute([&amp;#039;active&amp;#039;]);&lt;br /&gt;
$results = $stmt-&amp;amp;gt;fetchAll(PDO::FETCH_ASSOC);&lt;br /&gt;
// $results ist nun ein Array von assoziativen Arrays&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;MySQLi:&amp;#039;&amp;#039;&amp;#039; Die &amp;lt;code&amp;gt;fetch_all()&amp;lt;/code&amp;gt;-Methode des &amp;lt;code&amp;gt;mysqli_result&amp;lt;/code&amp;gt;-Objekts (verfügbar mit &amp;lt;code&amp;gt;mysqlnd&amp;lt;/code&amp;gt;) tut dasselbe. Der Modus (&amp;lt;code&amp;gt;MYSQLI_ASSOC&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;MYSQLI_NUM&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;MYSQLI_BOTH&amp;lt;/code&amp;gt;) wird als Argument übergeben.[25, 39]&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// OO-Stil (nach $result = $stmt-&amp;amp;gt;get_result();)&lt;br /&gt;
$results = $result-&amp;amp;gt;fetch_all(MYSQLI_ASSOC);&lt;br /&gt;
&lt;br /&gt;
// Prozedural (nach $result = mysqli_stmt_get_result($stmt);)&lt;br /&gt;
$results = mysqli_fetch_all($result, MYSQLI_ASSOC);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Der &amp;#039;&amp;#039;&amp;#039;Vorteil&amp;#039;&amp;#039;&amp;#039; dieser Methode liegt in der einfacheren Weiterverarbeitung des Ergebnis-Arrays mit Standard-PHP-Array-Funktionen wie &amp;lt;code&amp;gt;foreach&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;count&amp;lt;/code&amp;gt;.[40] Der &amp;#039;&amp;#039;&amp;#039;Nachteil&amp;#039;&amp;#039;&amp;#039; ist der potenziell hohe Speicherverbrauch, da alle Daten gleichzeitig in den PHP-Speicher geladen werden. Bei sehr großen Ergebnismengen kann dies zu Speicherlimit-Fehlern führen.[37]&lt;br /&gt;
&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Fetch-Modi / Fetch-Funktionen (Struktur der Ergebniszeile):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
Beide Erweiterungen bieten verschiedene Möglichkeiten, die Struktur der abgerufenen Datenzeilen zu bestimmen:&lt;br /&gt;
&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;PDO Fetch-Modi (Auswahl):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
***** &amp;lt;code&amp;gt;PDO::FETCH_ASSOC&amp;lt;/code&amp;gt;: Gibt ein assoziatives Array zurück (&amp;lt;code&amp;gt;[&amp;#039;spaltenname&amp;#039; =&amp;amp;gt; &amp;#039;wert&amp;#039;,... ]&amp;lt;/code&amp;gt;).[9, 33, 41, 42] Sehr gebräuchlich.&lt;br /&gt;
***** &amp;lt;code&amp;gt;PDO::FETCH_NUM&amp;lt;/code&amp;gt;: Gibt ein numerisch indiziertes Array zurück (&amp;lt;code&amp;gt;[0 =&amp;amp;gt; &amp;#039;wert1&amp;#039;, 1 =&amp;amp;gt; &amp;#039;wert2&amp;#039;,...]&amp;lt;/code&amp;gt;).[9, 41, 42]&lt;br /&gt;
***** &amp;lt;code&amp;gt;PDO::FETCH_BOTH&amp;lt;/code&amp;gt; (Standard): Gibt ein Array zurück, das sowohl assoziative als auch numerische Indizes enthält.[41, 42] Verbraucht mehr Speicher.&lt;br /&gt;
***** &amp;lt;code&amp;gt;PDO::FETCH_OBJ&amp;lt;/code&amp;gt;: Gibt ein anonymes &amp;lt;code&amp;gt;stdClass&amp;lt;/code&amp;gt;-Objekt zurück, bei dem die Spaltennamen Eigenschaften sind (&amp;lt;code&amp;gt;$row-&amp;amp;gt;spaltenname&amp;lt;/code&amp;gt;).[9, 41, 42, 43]&lt;br /&gt;
***** &amp;lt;code&amp;gt;PDO::FETCH_CLASS&amp;lt;/code&amp;gt;: Erstellt eine Instanz einer angegebenen Klasse und weist die Spaltenwerte den Klasseneigenschaften zu.[38, 41, 42, 43]&lt;br /&gt;
***** &amp;lt;code&amp;gt;PDO::FETCH_COLUMN&amp;lt;/code&amp;gt;: Ruft nur den Wert einer einzelnen Spalte aus der nächsten Zeile ab.[38]&lt;br /&gt;
***** &amp;lt;code&amp;gt;PDO::FETCH_LAZY&amp;lt;/code&amp;gt;: Eine Kombination aus &amp;lt;code&amp;gt;FETCH_BOTH&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;FETCH_OBJ&amp;lt;/code&amp;gt;, die Werte erst bei Zugriff lädt.[9, 41, 42]&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;MySQLi Fetch-Funktionen (Auswahl):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
***** &amp;lt;code&amp;gt;fetch_assoc()&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;mysqli_fetch_assoc()&amp;lt;/code&amp;gt;: Gibt ein assoziatives Array zurück.[17, 25, 39, 15] Sehr gebräuchlich.&lt;br /&gt;
***** &amp;lt;code&amp;gt;fetch_row()&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;mysqli_fetch_row()&amp;lt;/code&amp;gt;: Gibt ein numerisch indiziertes Array zurück.[39, 35]&lt;br /&gt;
***** &amp;lt;code&amp;gt;fetch_array()&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;mysqli_fetch_array()&amp;lt;/code&amp;gt;: Gibt je nach übergebenem Modus (&amp;lt;code&amp;gt;MYSQLI_ASSOC&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;MYSQLI_NUM&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;MYSQLI_BOTH&amp;lt;/code&amp;gt;) ein assoziatives, numerisches oder beides enthaltendes Array zurück.[39, 35]&lt;br /&gt;
***** &amp;lt;code&amp;gt;fetch_object()&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;mysqli_fetch_object()&amp;lt;/code&amp;gt;: Gibt ein &amp;lt;code&amp;gt;stdClass&amp;lt;/code&amp;gt;-Objekt zurück oder eine Instanz einer angegebenen Klasse.[21, 15, 35]&lt;br /&gt;
***** &amp;lt;code&amp;gt;fetch_column()&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;mysqli_fetch_column()&amp;lt;/code&amp;gt;: Ruft den Wert einer einzelnen Spalte ab (erst ab PHP 8.1 verfügbar).[24]&lt;br /&gt;
***** &amp;lt;code&amp;gt;fetch_all()&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;mysqli_fetch_all()&amp;lt;/code&amp;gt;: Ruft alle Zeilen auf einmal ab, Modus (&amp;lt;code&amp;gt;MYSQLI_ASSOC&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;MYSQLI_NUM&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;MYSQLI_BOTH&amp;lt;/code&amp;gt;) wählbar.[21, 25, 39]&lt;br /&gt;
&lt;br /&gt;
=== Tabelle 2: Ausgewählte PDO Fetch-Modi ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Konstante !! Rückgabeformat !! Beschreibung&lt;br /&gt;
|- &lt;br /&gt;
| &amp;lt;code&amp;gt;PDO::FETCH_ASSOC&amp;lt;/code&amp;gt;&lt;br /&gt;
| assoziatives=&amp;quot;&amp;quot; array=&amp;quot;&amp;quot; mwt-preservehtml=&amp;quot;&amp;quot; | [&amp;quot; col&amp;#039;=&amp;quot;&amp;amp;gt;&amp;quot; &amp;#039;val&amp;#039;]&amp;amp;lt;=&amp;quot;&amp;quot; code&amp;amp;gt;&amp;#039;)=&amp;quot;&amp;quot; &amp;amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
| Gängigste Form, Spaltennamen als Schlüssel.&lt;br /&gt;
|- &lt;br /&gt;
| mwt-preservehtml=&amp;quot;&amp;quot; pdo::fetch_num=&amp;quot;&amp;quot; code=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| numerisches=&amp;quot;&amp;quot; array=&amp;quot;&amp;quot; mwt-preservehtml=&amp;quot;&amp;quot; 0=&amp;quot;&amp;quot; val=&amp;quot;&amp;quot; code=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Spaltenwerte über numerischen Index erreichbar.&lt;br /&gt;
|- &lt;br /&gt;
| mwt-preservehtml=&amp;quot;&amp;quot; pdo::fetch_both=&amp;quot;&amp;quot; code=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| array=&amp;quot;&amp;quot; mit=&amp;quot;&amp;quot; assoz=&amp;quot;&amp;quot; num=&amp;quot;&amp;quot; indizes=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Standard, enthält doppelte Informationen, höherer Speicherbedarf.&lt;br /&gt;
|- &lt;br /&gt;
| mwt-preservehtml=&amp;quot;&amp;quot; pdo::fetch_obj=&amp;quot;&amp;quot; code=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| anonymes=&amp;quot;&amp;quot; mwt-preservehtml=&amp;quot;&amp;quot; stdclass=&amp;quot;&amp;quot; code=&amp;quot;&amp;quot; -objekt=&amp;quot;&amp;quot; row-=&amp;quot;&amp;quot; col=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Spaltennamen werden zu Objekteigenschaften.&lt;br /&gt;
|- &lt;br /&gt;
| &amp;lt;code&amp;gt;PDO::FETCH_CLASS&amp;lt;/code&amp;gt; || objekt=&amp;quot;&amp;quot; einer=&amp;quot;&amp;quot; spezifischen=&amp;quot;&amp;quot; klasse=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Mappt Spalten auf Eigenschaften einer vordefinierten Klasse.&lt;br /&gt;
|- &lt;br /&gt;
| &amp;lt;code&amp;gt;PDO::FETCH_COLUMN&amp;lt;/code&amp;gt; || einzelner=&amp;quot;&amp;quot; wert=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Gibt nur den Wert der ersten (oder angegebenen) Spalte der nächsten Zeile zurück.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Tabelle 3: Ausgewählte MySQLi Fetch-Funktionen ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Funktion (OO / Prozedural) !! Rückgabeformat !! Beschreibung&lt;br /&gt;
|- &lt;br /&gt;
| mwt-preservehtml=&amp;quot;&amp;quot; fetch_assoc=&amp;quot;&amp;quot; code=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| assoziatives=&amp;quot;&amp;quot; array=&amp;quot;&amp;quot; mwt-preservehtml=&amp;quot;&amp;quot; col=&amp;quot;&amp;quot; val=&amp;quot;&amp;quot; code=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Gängigste Form, Spaltennamen als Schlüssel.&lt;br /&gt;
|- &lt;br /&gt;
| mwt-preservehtml=&amp;quot;&amp;quot; fetch_row=&amp;quot;&amp;quot; code=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| numerisches=&amp;quot;&amp;quot; array=&amp;quot;&amp;quot; mwt-preservehtml=&amp;quot;&amp;quot; 0=&amp;quot;&amp;quot; val=&amp;quot;&amp;quot; code=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Spaltenwerte über numerischen Index erreichbar.&lt;br /&gt;
|- &lt;br /&gt;
| mwt-preservehtml=&amp;quot;&amp;quot; fetch_array=&amp;quot;&amp;quot; code=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| array=&amp;quot;&amp;quot; assoz=&amp;quot;&amp;quot; num=&amp;quot;&amp;quot; oder=&amp;quot;&amp;quot; beide=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Flexibel je nach Modus (&amp;lt;code&amp;gt;MYSQLI_ASSOC&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;MYSQLI_NUM&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;MYSQLI_BOTH&amp;lt;/code&amp;gt;).&lt;br /&gt;
|- &lt;br /&gt;
| &amp;lt;code&amp;gt;fetch_object()&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;mysqli_fetch_object()&amp;lt;/code&amp;gt;&lt;br /&gt;
| mwt-preservehtml=&amp;quot;&amp;quot; | stdClass&amp;quot; -objekt=&amp;quot;&amp;quot; oder=&amp;quot;&amp;quot; spezifische=&amp;quot;&amp;quot; klasse=&amp;quot;&amp;quot; &amp;amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
| Spaltennamen werden zu Objekteigenschaften.&lt;br /&gt;
|- &lt;br /&gt;
| mwt-preservehtml=&amp;quot;&amp;quot; fetch_all=&amp;quot;&amp;quot; code=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| array=&amp;quot;&amp;quot; von=&amp;quot;&amp;quot; arrays=&amp;quot;&amp;quot; assoz=&amp;quot;&amp;quot; num=&amp;quot;&amp;quot; oder=&amp;quot;&amp;quot; beide=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Holt alle Ergebniszeilen auf einmal (benötigt &amp;lt;code&amp;gt;mysqlnd&amp;lt;/code&amp;gt;).&lt;br /&gt;
|- &lt;br /&gt;
| &amp;lt;code&amp;gt;fetch_column()&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;mysqli_fetch_column()&amp;lt;/code&amp;gt; || einzelner=&amp;quot;&amp;quot; wert=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Gibt nur den Wert der ersten (oder angegebenen) Spalte zurück (PHP 8.1+).&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Zugriff auf Spaltenwerte:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
Der Zugriff auf die Daten innerhalb einer abgerufenen Zeile hängt vom gewählten Fetch-Modus ab:&lt;br /&gt;
**** Assoziatives Array: &amp;lt;code&amp;gt;$row[&amp;#039;spaltenname&amp;#039;]&amp;lt;/code&amp;gt;&lt;br /&gt;
**** Numerisches Array: &amp;lt;code&amp;gt;$row&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;$row[1]&amp;lt;/code&amp;gt;,...&lt;br /&gt;
**** Objekt: &amp;lt;code&amp;gt;$row-&amp;amp;gt;spaltenname&amp;lt;/code&amp;gt;&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Iteration mit &amp;lt;code&amp;gt;foreach&amp;lt;/code&amp;gt;:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
Nachdem Ergebnisse mit &amp;lt;code&amp;gt;fetchAll()&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;fetch_all()&amp;lt;/code&amp;gt; in ein Array geladen wurden, ist &amp;lt;code&amp;gt;foreach&amp;lt;/code&amp;gt; die natürliche Wahl zur Iteration [37, 40, 44]:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$results = $stmt-&amp;amp;gt;fetchAll(PDO::FETCH_ASSOC);&lt;br /&gt;
foreach ($results as $row) {&lt;br /&gt;
 echo &amp;quot;Name: &amp;quot;. htmlspecialchars($row[&amp;#039;name&amp;#039;]). &amp;quot;&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Seit PHP 8 bietet PDO eine modernere Alternative, die die Lesbarkeit von &amp;lt;code&amp;gt;foreach&amp;lt;/code&amp;gt; mit der Speichereffizienz von &amp;lt;code&amp;gt;fetch()&amp;lt;/code&amp;gt; kombiniert, indem direkt über das &amp;lt;code&amp;gt;PDOStatement&amp;lt;/code&amp;gt;-Objekt iteriert wird [37, 45]:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Nur PHP 8+ mit PDO&lt;br /&gt;
$stmt = $pdo-&amp;amp;gt;query(&amp;quot;SELECT name, email FROM users&amp;quot;);&lt;br /&gt;
foreach ($stmt as $row) { // PDOStatement ist direkt traversierbar&lt;br /&gt;
 echo &amp;quot;Name: &amp;quot;. htmlspecialchars($row[&amp;#039;name&amp;#039;]). &amp;quot;&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Diese Methode ist besonders elegant und effizient für große Ergebnismengen in modernen PHP-Versionen.&lt;br /&gt;
&lt;br /&gt;
=== Daten manipulieren (INSERT, UPDATE, DELETE) ===&lt;br /&gt;
&lt;br /&gt;
Das Einfügen, Aktualisieren und Löschen von Daten sind kritische Operationen, da sie den Datenbestand verändern. Hier ist die Verwendung von &amp;#039;&amp;#039;&amp;#039;Prepared Statements zwingend erforderlich&amp;#039;&amp;#039;&amp;#039;, um die Integrität und Sicherheit der Datenbank zu gewährleisten, insbesondere wenn Benutzereingaben beteiligt sind.&lt;br /&gt;
&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;PDO:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;Vorbereiten (&amp;lt;code&amp;gt;prepare&amp;lt;/code&amp;gt;):&amp;#039;&amp;#039;&amp;#039; Die SQL-Anweisung (&amp;lt;code&amp;gt;INSERT&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;UPDATE&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;DELETE&amp;lt;/code&amp;gt;) wird mit Platzhaltern (&amp;lt;code&amp;gt;?&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;:name&amp;lt;/code&amp;gt;) für die einzufügenden/zu aktualisierenden Werte sowie für die Kriterien in &amp;lt;code&amp;gt;WHERE&amp;lt;/code&amp;gt;-Klauseln (bei &amp;lt;code&amp;gt;UPDATE&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;DELETE&amp;lt;/code&amp;gt;) vorbereitet.[5, 46, 47, 48]&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;Ausführen (&amp;lt;code&amp;gt;execute&amp;lt;/code&amp;gt;):&amp;#039;&amp;#039;&amp;#039; Die Anweisung wird mit einem Array ausgeführt, das die Werte für die Platzhalter enthält.[5, 46, 47, 48]&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;Ergebnis prüfen:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
***** &amp;#039;&amp;#039;Anzahl betroffener Zeilen:&amp;#039;&amp;#039; &amp;lt;code&amp;gt;$stmt-&amp;amp;gt;rowCount()&amp;lt;/code&amp;gt; gibt die Anzahl der durch &amp;lt;code&amp;gt;UPDATE&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;DELETE&amp;lt;/code&amp;gt; betroffenen Zeilen zurück. Bei &amp;lt;code&amp;gt;INSERT&amp;lt;/code&amp;gt; ist der Rückgabewert oft nicht zuverlässig oder standardisiert.[8, 9, 5, 49] Das Verhalten kann je nach Datenbanktreiber und Konfiguration (z.B. &amp;lt;code&amp;gt;PDO::MYSQL_ATTR_FOUND_ROWS&amp;lt;/code&amp;gt;) variieren.[9]&lt;br /&gt;
***** &amp;#039;&amp;#039;ID des letzten eingefügten Datensatzes:&amp;#039;&amp;#039; Bei &amp;lt;code&amp;gt;INSERT&amp;lt;/code&amp;gt;-Anweisungen in Tabellen mit einer &amp;lt;code&amp;gt;AUTO_INCREMENT&amp;lt;/code&amp;gt;-Spalte liefert &amp;lt;code&amp;gt;$pdo-&amp;amp;gt;lastInsertId()&amp;lt;/code&amp;gt; die ID des neu eingefügten Datensatzes zurück.[8, 33, 48]&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;Code-Beispiele (PDO):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!--?php// --- INSERT ---&lt;br /&gt;
$name = $_POST[&amp;#039;name&amp;#039;];&lt;br /&gt;
$email = $_POST[&amp;#039;email&amp;#039;];&lt;br /&gt;
$status = &amp;#039;pending&amp;#039;;&lt;br /&gt;
&lt;br /&gt;
$sql_insert = &amp;quot;INSERT INTO users (username, email, status) VALUES (:name, :email, :status)&amp;quot;;&lt;br /&gt;
$stmt_insert = $pdo---&amp;gt;prepare($sql_insert);&lt;br /&gt;
$stmt_insert-&amp;amp;gt;execute([&amp;#039;:name&amp;#039; =&amp;amp;gt; $name, &amp;#039;:email&amp;#039; =&amp;amp;gt; $email, &amp;#039;:status&amp;#039; =&amp;amp;gt; $status]);&lt;br /&gt;
$lastId = $pdo-&amp;amp;gt;lastInsertId();&lt;br /&gt;
echo &amp;quot;Neuer Benutzer mit ID $lastId eingefügt.&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
// --- UPDATE ---&lt;br /&gt;
$new_status = &amp;#039;active&amp;#039;;&lt;br /&gt;
$user_id_to_update = $lastId;&lt;br /&gt;
&lt;br /&gt;
$sql_update = &amp;quot;UPDATE users SET status = :status WHERE id = :id&amp;quot;;&lt;br /&gt;
$stmt_update = $pdo-&amp;amp;gt;prepare($sql_update);&lt;br /&gt;
$stmt_update-&amp;amp;gt;execute([&amp;#039;:status&amp;#039; =&amp;amp;gt; $new_status, &amp;#039;:id&amp;#039; =&amp;amp;gt; $user_id_to_update]);&lt;br /&gt;
$affectedRows = $stmt_update-&amp;amp;gt;rowCount();&lt;br /&gt;
echo &amp;quot;$affectedRows Benutzerstatus aktualisiert.&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
// --- DELETE ---&lt;br /&gt;
$user_id_to_delete = $lastId;&lt;br /&gt;
&lt;br /&gt;
$sql_delete = &amp;quot;DELETE FROM users WHERE id = :id&amp;quot;;&lt;br /&gt;
$stmt_delete = $pdo-&amp;amp;gt;prepare($sql_delete);&lt;br /&gt;
$stmt_delete-&amp;amp;gt;execute([&amp;#039;:id&amp;#039; =&amp;amp;gt; $user_id_to_delete]);&lt;br /&gt;
$affectedRowsDel = $stmt_delete-&amp;amp;gt;rowCount();&lt;br /&gt;
echo &amp;quot;$affectedRowsDel Benutzer gelöscht.&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
?&amp;amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;MySQLi:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;Vorbereiten (&amp;lt;code&amp;gt;prepare&amp;lt;/code&amp;gt;):&amp;#039;&amp;#039;&amp;#039; Die SQL-Anweisung wird mit &amp;lt;code&amp;gt;?&amp;lt;/code&amp;gt;-Platzhaltern vorbereitet (OO: &amp;lt;code&amp;gt;$mysqli-&amp;amp;gt;prepare()&amp;lt;/code&amp;gt;, Proc: &amp;lt;code&amp;gt;mysqli_prepare()&amp;lt;/code&amp;gt;).[25, 50, 15, 35]&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;Parameter binden (&amp;lt;code&amp;gt;bind_param&amp;lt;/code&amp;gt;):&amp;#039;&amp;#039;&amp;#039; Variablen werden explizit mit Typenangabe gebunden (OO: &amp;lt;code&amp;gt;$stmt-&amp;amp;gt;bind_param()&amp;lt;/code&amp;gt;, Proc: &amp;lt;code&amp;gt;mysqli_stmt_bind_param()&amp;lt;/code&amp;gt;).[25, 50, 15, 35] Die Notwendigkeit, den Datentyp für jede Variable anzugeben, ist ein wesentlicher Unterschied zur einfacheren Array-Übergabe bei PDOs &amp;lt;code&amp;gt;execute()&amp;lt;/code&amp;gt;.&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;Ausführen (&amp;lt;code&amp;gt;execute&amp;lt;/code&amp;gt;):&amp;#039;&amp;#039;&amp;#039; Die Anweisung wird ausgeführt (OO: &amp;lt;code&amp;gt;$stmt-&amp;amp;gt;execute()&amp;lt;/code&amp;gt;, Proc: &amp;lt;code&amp;gt;mysqli_stmt_execute()&amp;lt;/code&amp;gt;).[25, 36, 50, 15, 35]&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;Ergebnis prüfen:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
***** &amp;#039;&amp;#039;Anzahl betroffener Zeilen:&amp;#039;&amp;#039; Über die Eigenschaft &amp;lt;code&amp;gt;$mysqli-&amp;amp;gt;affected_rows&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;$stmt-&amp;amp;gt;affected_rows&amp;lt;/code&amp;gt; (OO) bzw. die Funktionen &amp;lt;code&amp;gt;mysqli_affected_rows($link)&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;mysqli_stmt_affected_rows($stmt)&amp;lt;/code&amp;gt; (Proc).[18, 26, 15, 35]&lt;br /&gt;
***** &amp;#039;&amp;#039;ID des letzten eingefügten Datensatzes:&amp;#039;&amp;#039; Über &amp;lt;code&amp;gt;$mysqli-&amp;amp;gt;insert_id&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;$stmt-&amp;amp;gt;insert_id&amp;lt;/code&amp;gt; (OO) bzw. &amp;lt;code&amp;gt;mysqli_insert_id($link)&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;mysqli_stmt_insert_id($stmt)&amp;lt;/code&amp;gt; (Proc).[26, 15] Die Verwendung von &amp;lt;code&amp;gt;$mysqli-&amp;amp;gt;insert_id&amp;lt;/code&amp;gt; wird oft als zuverlässiger angesehen, da sie auch nach dem Schließen des Statements funktioniert.[15]&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;Code-Beispiele (MySQLi OO):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!--?php// --- INSERT ---&lt;br /&gt;
$name = $_POST[&amp;#039;name&amp;#039;];&lt;br /&gt;
$email = $_POST[&amp;#039;email&amp;#039;];&lt;br /&gt;
$status = &amp;#039;pending&amp;#039;;&lt;br /&gt;
&lt;br /&gt;
$sql_insert = &amp;quot;INSERT INTO users (username, email, status) VALUES (?,?,?)&amp;quot;;&lt;br /&gt;
$stmt_insert = $mysqli---&amp;gt;prepare($sql_insert);&lt;br /&gt;
// Typen: s=string, s=string, s=string&lt;br /&gt;
$stmt_insert-&amp;amp;gt;bind_param(&amp;quot;sss&amp;quot;, $name, $email, $status);&lt;br /&gt;
$stmt_insert-&amp;amp;gt;execute();&lt;br /&gt;
$lastId = $mysqli-&amp;amp;gt;insert_id;&lt;br /&gt;
echo &amp;quot;Neuer Benutzer mit ID $lastId eingefügt (MySQLi OO).&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
$stmt_insert-&amp;amp;gt;close(); // Statement schließen&lt;br /&gt;
&lt;br /&gt;
// --- UPDATE ---&lt;br /&gt;
$new_status = &amp;#039;active&amp;#039;;&lt;br /&gt;
$user_id_to_update = $lastId;&lt;br /&gt;
&lt;br /&gt;
$sql_update = &amp;quot;UPDATE users SET status =? WHERE id =?&amp;quot;;&lt;br /&gt;
$stmt_update = $mysqli-&amp;amp;gt;prepare($sql_update);&lt;br /&gt;
// Typen: s=string, i=integer&lt;br /&gt;
$stmt_update-&amp;amp;gt;bind_param(&amp;quot;si&amp;quot;, $new_status, $user_id_to_update);&lt;br /&gt;
$stmt_update-&amp;amp;gt;execute();&lt;br /&gt;
$affectedRows = $mysqli-&amp;amp;gt;affected_rows; // Oder $stmt_update-&amp;amp;gt;affected_rows vor close()&lt;br /&gt;
echo &amp;quot;$affectedRows Benutzerstatus aktualisiert (MySQLi OO).&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
$stmt_update-&amp;amp;gt;close();&lt;br /&gt;
&lt;br /&gt;
// --- DELETE ---&lt;br /&gt;
$user_id_to_delete = $lastId;&lt;br /&gt;
&lt;br /&gt;
$sql_delete = &amp;quot;DELETE FROM users WHERE id =?&amp;quot;;&lt;br /&gt;
$stmt_delete = $mysqli-&amp;amp;gt;prepare($sql_delete);&lt;br /&gt;
// Typ: i=integer&lt;br /&gt;
$stmt_delete-&amp;amp;gt;bind_param(&amp;quot;i&amp;quot;, $user_id_to_delete);&lt;br /&gt;
$stmt_delete-&amp;amp;gt;execute();&lt;br /&gt;
$affectedRowsDel = $mysqli-&amp;amp;gt;affected_rows;&lt;br /&gt;
echo &amp;quot;$affectedRowsDel Benutzer gelöscht (MySQLi OO).&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
$stmt_delete-&amp;amp;gt;close();&lt;br /&gt;
?&amp;amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;#039;&amp;#039;(Anmerkung: Prozedurale Beispiele folgen demselben Muster unter Verwendung der &amp;lt;code&amp;gt;mysqli_*&amp;lt;/code&amp;gt;-Funktionen wie &amp;lt;code&amp;gt;mysqli_prepare&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;mysqli_stmt_bind_param&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;mysqli_stmt_execute&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;mysqli_insert_id&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;mysqli_affected_rows&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;mysqli_stmt_close&amp;lt;/code&amp;gt;)&amp;#039;&amp;#039;.&lt;br /&gt;
&lt;br /&gt;
== Sicherheit: SQL Injection verstehen und verhindern ==&lt;br /&gt;
&lt;br /&gt;
Eines der größten Sicherheitsrisiken bei der Interaktion mit Datenbanken über Webanwendungen ist die SQL Injection (SQLi). Das Verständnis dieser Bedrohung und ihrer Abwehr ist für jeden PHP-Entwickler unerlässlich.&lt;br /&gt;
&lt;br /&gt;
=== SQL Injection ===&lt;br /&gt;
&lt;br /&gt;
SQL Injection ist eine Angriffstechnik, bei der ein Angreifer versucht, bösartigen oder manipulierten SQL-Code über Benutzereingabefelder einer Webanwendung (z.B. Formulare, URL-Parameter, HTTP-Header, Cookies) in die Datenbankabfragen einzuschleusen.[51, 52, 53, 54, 55, 56, 57]&lt;br /&gt;
&lt;br /&gt;
Die Attacke funktioniert, indem Schwachstellen in der Anwendung ausgenutzt werden, bei denen Benutzereingaben direkt und ohne ausreichende Validierung oder Maskierung (Escaping) in SQL-Abfragestrings eingefügt (konkateniert) werden.[5, 51, 55] Dadurch wird die notwendige Trennung zwischen dem eigentlichen SQL-Befehl (Kontrollebene) und den Daten (Datenebene) aufgehoben.[51] Der Angreifer kann spezielle Zeichen (wie Anführungszeichen &amp;lt;code&amp;gt;&amp;#039;&amp;lt;/code&amp;gt;, Semikolons &amp;lt;code&amp;gt;;&amp;lt;/code&amp;gt; oder Kommentare &amp;lt;code&amp;gt;--&amp;lt;/code&amp;gt;) verwenden, um den ursprünglichen SQL-Befehl vorzeitig zu beenden, zu verändern oder um eigene, zusätzliche SQL-Befehle anzuhängen.[51, 53]&lt;br /&gt;
&lt;br /&gt;
Ein klassisches Beispiel ist eine Login-Abfrage, die etwa so aufgebaut ist:&lt;br /&gt;
&amp;lt;code&amp;gt;$sql = &amp;quot;SELECT * FROM users WHERE username = &amp;#039;&amp;quot;. $userInputUsername. &amp;quot;&amp;#039; AND password = &amp;#039;&amp;quot;. $userInputPassword. &amp;quot;&amp;#039;&amp;quot;;&amp;lt;/code&amp;gt;&lt;br /&gt;
Wenn ein Angreifer als Benutzernamen &amp;lt;code&amp;gt;&amp;#039; OR &amp;#039;1&amp;#039;=&amp;#039;1&amp;lt;/code&amp;gt; eingibt, wird die resultierende SQL-Abfrage zu:&lt;br /&gt;
&amp;lt;code&amp;gt;SELECT * FROM users WHERE username = &amp;#039;&amp;#039; OR &amp;#039;1&amp;#039;=&amp;#039;1&amp;#039; AND password = &amp;#039;...&amp;#039;&amp;lt;/code&amp;gt;&lt;br /&gt;
Da &amp;lt;code&amp;gt;&amp;#039;1&amp;#039;=&amp;#039;1&amp;#039;&amp;lt;/code&amp;gt; immer wahr ist, würde diese Abfrage (je nach genauer Struktur und Datenbank) möglicherweise alle Benutzer zurückgeben oder den Login ohne korrektes Passwort ermöglichen.[5, 15, 55]&lt;br /&gt;
&lt;br /&gt;
=== Auswirkungen und Gefahren von SQL Injection ===&lt;br /&gt;
&lt;br /&gt;
Die potenziellen Folgen einer erfolgreichen SQL-Injection-Attacke sind gravierend und vielfältig:&lt;br /&gt;
&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Datendiebstahl:&amp;#039;&amp;#039;&amp;#039; Angreifer können auf sensible Daten zugreifen und diese auslesen, darunter Benutzerdaten, Passwörter (oft als Hashes, die aber geknackt werden können), Kreditkarteninformationen, Geschäftsgeheimnisse und andere vertrauliche Informationen.[51, 53, 56]&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Datenmanipulation/-zerstörung:&amp;#039;&amp;#039;&amp;#039; Angreifer können bestehende Daten in der Datenbank verändern (z.B. Preise in einem Shop, Benutzerrollen) oder komplette Datensätze oder sogar Tabellen löschen (&amp;lt;code&amp;gt;DELETE&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;DROP TABLE&amp;lt;/code&amp;gt;).[51, 53, 56]&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Umgehung der Authentifizierung/Identitätsdiebstahl:&amp;#039;&amp;#039;&amp;#039; Wie im Beispiel oben gezeigt, können Angreifer Login-Mechanismen umgehen und sich unautorisiert Zugang verschaffen, möglicherweise sogar mit administrativen Rechten.[51, 56]&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Denial of Service (DoS):&amp;#039;&amp;#039;&amp;#039; Durch das Löschen wichtiger Daten oder das Ausführen ressourcenintensiver Abfragen kann die Verfügbarkeit der Anwendung oder der Datenbank beeinträchtigt werden.[51, 56]&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Kompromittierung des Servers:&amp;#039;&amp;#039;&amp;#039; In manchen Fällen können Angreifer über SQL Injection Betriebssystembefehle auf dem Datenbankserver ausführen oder vollen administrativen Zugriff auf die Datenbank erlangen, was weitreichende Konsequenzen haben kann.[51, 55, 56]&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Reputationsschaden und rechtliche Folgen:&amp;#039;&amp;#039;&amp;#039; Erfolgreiche Angriffe, insbesondere wenn sie zu Datenlecks führen, können das Vertrauen der Benutzer schwer beschädigen und zu empfindlichen Strafen gemäß Datenschutzgesetzen führen.[53]&lt;br /&gt;
&lt;br /&gt;
=== Prävention durch Prepared Statements (Primäre Methode) ===&lt;br /&gt;
&lt;br /&gt;
Die mit Abstand wichtigste und effektivste Methode zur Verhinderung von SQL Injection ist die konsequente Verwendung von &amp;#039;&amp;#039;&amp;#039;Prepared Statements&amp;#039;&amp;#039;&amp;#039; (parametrisierten Abfragen).[4, 25, 5, 58, 35]&lt;br /&gt;
&lt;br /&gt;
Das Grundprinzip von Prepared Statements ist die &amp;#039;&amp;#039;&amp;#039;strikte Trennung von SQL-Befehlsstruktur und den Daten (Parametern)&amp;#039;&amp;#039;&amp;#039;.[4, 25, 5, 58, 35] Der Ablauf ist typischerweise zweistufig:&lt;br /&gt;
**# &amp;#039;&amp;#039;&amp;#039;Prepare (Vorbereiten):&amp;#039;&amp;#039;&amp;#039; Die SQL-Anweisung wird mit Platzhaltern anstelle der tatsächlichen Werte an den Datenbankserver gesendet. Der Server parst die Anweisung, prüft die Syntax und bereitet einen Ausführungsplan vor, ohne die endgültigen Daten zu kennen.&lt;br /&gt;
**# &amp;#039;&amp;#039;&amp;#039;Execute (Ausführen) mit Parameterbindung:&amp;#039;&amp;#039;&amp;#039; Die tatsächlichen Werte (Parameter) werden separat an den Server gesendet und an die vorbereiteten Platzhalter gebunden. Der Server führt nun die vorkompilierte Anweisung mit den sicher eingefügten Werten aus.&lt;br /&gt;
Der entscheidende Punkt ist, dass die Datenbank die separat gesendeten Parameter &amp;#039;&amp;#039;&amp;#039;immer als Datenwerte&amp;#039;&amp;#039;&amp;#039; behandelt, niemals als Teil des SQL-Befehls.[5, 15, 58] Selbst wenn ein Parameter bösartigen SQL-Code enthält (z.B. &amp;lt;code&amp;gt;&amp;#039; OR &amp;#039;1&amp;#039;=&amp;#039;1&amp;lt;/code&amp;gt;), wird dieser nicht ausgeführt, sondern als einfacher String-Wert behandelt, der z.B. in eine Spalte eingefügt oder in einer &amp;lt;code&amp;gt;WHERE&amp;lt;/code&amp;gt;-Klausel verglichen wird. Diese Trennung macht Prepared Statements immun gegen SQL Injection.&lt;br /&gt;
&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Platzhalter:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;PDO:&amp;#039;&amp;#039;&amp;#039; Unterstützt sowohl positionale Platzhalter (&amp;lt;code&amp;gt;?&amp;lt;/code&amp;gt;) als auch benannte Platzhalter (&amp;lt;code&amp;gt;:name&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;:email&amp;lt;/code&amp;gt;, etc.).[5, 13] Benannte Platzhalter machen den Code oft lesbarer, besonders bei vielen Parametern.&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;MySQLi:&amp;#039;&amp;#039;&amp;#039; Unterstützt ausschließlich positionale Platzhalter (&amp;lt;code&amp;gt;?&amp;lt;/code&amp;gt;).[25]&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Parameterbindung:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;PDO:&amp;#039;&amp;#039;&amp;#039; Bietet flexible Bindungsoptionen:&lt;br /&gt;
***** &amp;#039;&amp;#039;Implizit über &amp;lt;code&amp;gt;execute()&amp;lt;/code&amp;gt;-Array:&amp;#039;&amp;#039; Die einfachste und häufigste Methode. Ein Array mit den Werten wird an &amp;lt;code&amp;gt;execute()&amp;lt;/code&amp;gt; übergeben. PDO kümmert sich um das korrekte Escaping und die Typbehandlung (oft werden Werte sicher als Strings behandelt).[4, 12, 5, 13]&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$stmt-&amp;amp;gt;execute([$wert1, $wert2]); // für? Platzhalter&lt;br /&gt;
$stmt-&amp;amp;gt;execute([&amp;#039;:name&amp;#039; =&amp;amp;gt; $wert1, &amp;#039;:id&amp;#039; =&amp;amp;gt; $wert2]); // für :name Platzhalter&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
***** &amp;#039;&amp;#039;Explizit mit &amp;lt;code&amp;gt;bindValue()&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;bindParam()&amp;lt;/code&amp;gt;:&amp;#039;&amp;#039; Erlaubt die explizite Angabe des Datentyps (z.B. &amp;lt;code&amp;gt;PDO::PARAM_INT&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;PDO::PARAM_STR&amp;lt;/code&amp;gt;) für jeden Parameter, was in manchen Fällen für Klarheit oder spezifische Datenbankoptimierungen sorgen kann.[5, 13] &amp;lt;code&amp;gt;bindParam&amp;lt;/code&amp;gt; bindet eine Variable (ihr Wert wird erst bei &amp;lt;code&amp;gt;execute()&amp;lt;/code&amp;gt; gelesen), &amp;lt;code&amp;gt;bindValue&amp;lt;/code&amp;gt; bindet den aktuellen Wert einer Variablen oder einen Literalwert.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$stmt-&amp;amp;gt;bindValue(&amp;#039;:id&amp;#039;, $user_id, PDO::PARAM_INT);&lt;br /&gt;
$stmt-&amp;amp;gt;bindParam(&amp;#039;:name&amp;#039;, $user_name, PDO::PARAM_STR);&lt;br /&gt;
$stmt-&amp;amp;gt;execute();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;MySQLi:&amp;#039;&amp;#039;&amp;#039; Erfordert immer die explizite Bindung mittels &amp;lt;code&amp;gt;bind_param()&amp;lt;/code&amp;gt;.&lt;br /&gt;
***** &amp;lt;code&amp;gt;bind_param()&amp;lt;/code&amp;gt; (OO) / &amp;lt;code&amp;gt;mysqli_stmt_bind_param()&amp;lt;/code&amp;gt; (Proc): Nimmt als ersten Parameter einen String entgegen, der die Datentypen aller nachfolgend übergebenen Variablen definiert.[25, 50, 15, 58]&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// OO-Stil&lt;br /&gt;
$stmt-&amp;amp;gt;bind_param(&amp;quot;ssi&amp;quot;, $name, $email, $id); // String, String, Integer&lt;br /&gt;
// Prozedural&lt;br /&gt;
mysqli_stmt_bind_param($stmt, &amp;quot;ssi&amp;quot;, $name, $email, $id);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
=== Tabelle 4: MySQLi &amp;lt;code&amp;gt;bind_param()&amp;lt;/code&amp;gt; Typen-String ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Typ-Buchstabe !! PHP-Datentyp (typisch) !! Beschreibung&lt;br /&gt;
|- &lt;br /&gt;
| mwt-preservehtml=&amp;quot;&amp;quot; i=&amp;quot;&amp;quot; code=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| integer=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Integer (Ganzzahl)&lt;br /&gt;
|- &lt;br /&gt;
| mwt-preservehtml=&amp;quot;&amp;quot; d=&amp;quot;&amp;quot; code=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| double=&amp;quot;&amp;quot; float=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Double (Gleitkommazahl)&lt;br /&gt;
|- &lt;br /&gt;
| mwt-preservehtml=&amp;quot;&amp;quot; s=&amp;quot;&amp;quot; code=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| string=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| String (Zeichenkette)&lt;br /&gt;
|- &lt;br /&gt;
| mwt-preservehtml=&amp;quot;&amp;quot; b=&amp;quot;&amp;quot; code=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| string=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| BLOB (Binary Large Object), als String gesendet&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Detaillierte Code-Beispiele zur Prävention:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
Die Beispiele für INSERT, UPDATE und DELETE in Abschnitt 3.3 demonstrieren bereits die korrekte Anwendung von Prepared Statements mit PDO und MySQLi zur Verhinderung von SQL Injection. Es ist entscheidend, dieses Muster konsequent anzuwenden, wann immer externe Daten in SQL-Abfragen einfließen.&lt;br /&gt;
&lt;br /&gt;
=== 4.4 Zusätzliche Schutzmaßnahmen (Defense-in-Depth) ===&lt;br /&gt;
&lt;br /&gt;
Obwohl Prepared Statements der wichtigste Schutz gegen SQL Injection sind, sollte Sicherheit immer als mehrschichtiges Konzept (&amp;quot;Defense-in-Depth&amp;quot;) betrachtet werden.[5] Zusätzliche Maßnahmen erhöhen die Robustheit der Anwendung:&lt;br /&gt;
&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Eingabevalidierung und -bereinigung:&amp;#039;&amp;#039;&amp;#039; Überprüfen Sie &amp;#039;&amp;#039;alle&amp;#039;&amp;#039; Benutzereingaben serverseitig, bevor sie überhaupt in die Nähe der Datenbank kommen.[4, 59, 20, 5] Stellen Sie sicher, dass die Daten dem erwarteten Typ (Zahl, String, E-Mail-Format etc.) und Format entsprechen. Verwenden Sie dafür PHP-Filterfunktionen wie &amp;lt;code&amp;gt;filter_var()&amp;lt;/code&amp;gt; oder reguläre Ausdrücke.[4, 5] Dies ist eine wichtige zweite Verteidigungslinie, die ungültige oder unerwartete Daten frühzeitig abfängt. Es ersetzt jedoch &amp;#039;&amp;#039;nicht&amp;#039;&amp;#039; die Notwendigkeit von Prepared Statements.&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Prinzip der geringsten Rechte (Least Privilege):&amp;#039;&amp;#039;&amp;#039; Konfigurieren Sie den Datenbankbenutzer, den Ihre PHP-Anwendung verwendet, mit den absolut minimal notwendigen Berechtigungen.[20, 5] Wenn die Anwendung nur Daten lesen und schreiben muss, geben Sie ihr keine Rechte zum Ändern der Tabellenstruktur (&amp;lt;code&amp;gt;ALTER TABLE&amp;lt;/code&amp;gt;) oder zum Löschen von Tabellen (&amp;lt;code&amp;gt;DROP TABLE&amp;lt;/code&amp;gt;). Beschränken Sie den Zugriff auf die benötigten Datenbanken und Tabellen. Verwenden Sie niemals den &amp;lt;code&amp;gt;root&amp;lt;/code&amp;gt;- oder Admin-Benutzer der Datenbank für die Anwendung. Dies begrenzt den potenziellen Schaden, falls doch eine Schwachstelle ausgenutzt wird.&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Sichere Fehlerbehandlung:&amp;#039;&amp;#039;&amp;#039; Konfigurieren Sie PHP und Ihre Datenbankerweiterung so, dass detaillierte Fehlermeldungen (insbesondere solche, die SQL-Code oder Datenbankstrukturinformationen enthalten) niemals dem Endbenutzer angezeigt werden.[20, 5] Solche Informationen sind für Angreifer wertvoll. Loggen Sie Fehler stattdessen in eine sichere Datei auf dem Server, die nur für Administratoren zugänglich ist. Verwenden Sie &amp;lt;code&amp;gt;PDO::ERRMODE_EXCEPTION&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;mysqli_report&amp;lt;/code&amp;gt; für strukturierte Fehlerbehandlung.&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Output Escaping:&amp;#039;&amp;#039;&amp;#039; Obwohl nicht direkt zur Verhinderung von SQL Injection, ist es wichtig, Daten, die aus der Datenbank gelesen und im HTML-Kontext ausgegeben werden, korrekt zu escapen (z.B. mit &amp;lt;code&amp;gt;htmlspecialchars()&amp;lt;/code&amp;gt;), um Cross-Site Scripting (XSS)-Angriffe zu verhindern.&lt;br /&gt;
Die Kombination dieser Maßnahmen – mit Prepared Statements als Kernstück – bietet einen robusten Schutz gegen SQL Injection und andere verwandte Angriffe.&lt;br /&gt;
&lt;br /&gt;
== Nützliche PHP Array-Funktionen für Datenbankergebnisse ==&lt;br /&gt;
&lt;br /&gt;
Wenn Daten aus der Datenbank abgerufen werden, insbesondere mit Methoden wie &amp;lt;code&amp;gt;fetchAll()&amp;lt;/code&amp;gt; (PDO) oder &amp;lt;code&amp;gt;fetch_all()&amp;lt;/code&amp;gt; (MySQLi), liegen die Ergebnisse typischerweise als PHP-Array vor (meist ein Array von assoziativen Arrays, wobei jedes innere Array eine Ergebniszeile darstellt). PHP bietet eine Reihe nützlicher Funktionen zur Verarbeitung solcher Arrays.&lt;br /&gt;
&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Iteration mit &amp;lt;code&amp;gt;foreach&amp;lt;/code&amp;gt;:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
Dies ist die Standardmethode, um die einzelnen Zeilen und Spalten eines Ergebnis-Arrays zu durchlaufen.[37, 40, 44]&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;Syntax:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Iteration über die Zeilen (jedes $row ist ein assoziatives Array)&lt;br /&gt;
foreach ($results as $row) {&lt;br /&gt;
 echo &amp;quot;ID: &amp;quot;. $row[&amp;#039;id&amp;#039;]. &amp;quot;, Name: &amp;quot;. htmlspecialchars($row[&amp;#039;name&amp;#039;]). &amp;quot;&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Iteration über Zeilen mit Zugriff auf den numerischen Index der Zeile&lt;br /&gt;
foreach ($results as $index =&amp;amp;gt; $row) {&lt;br /&gt;
 echo &amp;quot;Zeile &amp;quot;. $index. &amp;quot;: ID = &amp;quot;. $row[&amp;#039;id&amp;#039;]. &amp;quot;&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;Code-Beispiel:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!--?php// Annahme: $results enthält das Ergebnis von $stmt---&amp;gt;fetchAll(PDO::FETCH_ASSOC);&lt;br /&gt;
$results = [&amp;#039;id&amp;#039; =&amp;amp;gt; 1, &amp;#039;name&amp;#039; =&amp;amp;gt; &amp;#039;Alice&amp;#039;, &amp;#039;email&amp;#039; =&amp;amp;gt; &amp;#039;alice@example.com&amp;#039;],&lt;br /&gt;
 ;&lt;br /&gt;
&lt;br /&gt;
echo &amp;quot;&amp;lt;/syntaxhighlight&amp;gt;&amp;quot;;&lt;br /&gt;
foreach ($results as $row) {&lt;br /&gt;
 echo &amp;quot;&lt;br /&gt;
*Benutzer #&amp;quot;. $row[&amp;#039;id&amp;#039;]. &amp;quot;: &amp;quot;. htmlspecialchars($row[&amp;#039;name&amp;#039;]);&lt;br /&gt;
 echo &amp;quot; (&amp;quot;. htmlspecialchars($row[&amp;#039;email&amp;#039;]). &amp;quot;)&lt;br /&gt;
&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
echo &amp;quot;&amp;quot;;&lt;br /&gt;
?&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Zählen von Ergebnissen mit &amp;lt;code&amp;gt;count()&amp;lt;/code&amp;gt;:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
Die Funktion &amp;lt;code&amp;gt;count()&amp;lt;/code&amp;gt; gibt die Anzahl der Elemente in einem Array zurück. Wenn sie auf ein mit &amp;lt;code&amp;gt;fetchAll()&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;fetch_all()&amp;lt;/code&amp;gt; erzeugtes Ergebnis-Array angewendet wird, liefert sie die Anzahl der abgerufenen Zeilen.[40, 60, 61]&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;Syntax:&amp;#039;&amp;#039;&amp;#039; &amp;lt;code&amp;gt;$anzahlZeilen = count($results);&amp;lt;/code&amp;gt;.[60]&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;Wichtiger Hinweis:&amp;#039;&amp;#039;&amp;#039; &amp;lt;code&amp;gt;count()&amp;lt;/code&amp;gt; funktioniert nur zuverlässig für die Gesamtzahl der Zeilen, wenn &amp;#039;&amp;#039;alle&amp;#039;&amp;#039; Zeilen zuvor in ein Array geladen wurden (also nach &amp;lt;code&amp;gt;fetchAll&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;fetch_all&amp;lt;/code&amp;gt;). Wenn Sie Ergebnisse zeilenweise mit &amp;lt;code&amp;gt;fetch()&amp;lt;/code&amp;gt; in einer &amp;lt;code&amp;gt;while&amp;lt;/code&amp;gt;-Schleife verarbeiten, müssen Sie einen manuellen Zähler innerhalb der Schleife inkrementieren, um die Anzahl der verarbeiteten Zeilen zu ermitteln.[40] Alternativ können &amp;lt;code&amp;gt;PDOStatement::rowCount()&amp;lt;/code&amp;gt; [8, 9] oder &amp;lt;code&amp;gt;mysqli_result::$num_rows&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;mysqli_stmt::$num_rows&amp;lt;/code&amp;gt; [26, 15] verwendet werden, um die Anzahl der von einer &amp;lt;code&amp;gt;SELECT&amp;lt;/code&amp;gt;-Abfrage zurückgegebenen Zeilen zu erhalten, &amp;#039;&amp;#039;bevor&amp;#039;&amp;#039; die Daten gefetched werden (das Verhalten von &amp;lt;code&amp;gt;rowCount&amp;lt;/code&amp;gt; kann jedoch bei anderen Anweisungstypen wie &amp;lt;code&amp;gt;UPDATE&amp;lt;/code&amp;gt; variieren).&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;Code-Beispiel:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!--?php&lt;br /&gt;
// Annahme: $results von fetchAll()&lt;br /&gt;
$results = [ /*... wie oben... */ ];&lt;br /&gt;
$anzahl = count($results);&lt;br /&gt;
echo &amp;quot;Anzahl der Benutzer (via fetchAll/count): $anzahl&amp;lt;br--&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
// Vergleich: Zählen bei zeilenweiser Verarbeitung&lt;br /&gt;
$stmt = $pdo-&amp;amp;gt;query(&amp;quot;SELECT id FROM users&amp;quot;); // Annahme: PDO-Statement&lt;br /&gt;
$manueller_zaehler = 0;&lt;br /&gt;
while ($row = $stmt-&amp;amp;gt;fetch()) {&lt;br /&gt;
 $manueller_zaehler++;&lt;br /&gt;
}&lt;br /&gt;
echo &amp;quot;Anzahl der Benutzer (via while/fetch): $manueller_zaehler&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
// Alternative: rowCount() nach SELECT (PDO)&lt;br /&gt;
$stmt_count = $pdo-&amp;amp;gt;query(&amp;quot;SELECT id FROM users&amp;quot;);&lt;br /&gt;
$rowCountResult = $stmt_count-&amp;amp;gt;rowCount(); // Kann bei manchen DBs/Treibern funktionieren&lt;br /&gt;
echo &amp;quot;Anzahl der Benutzer (via rowCount nach SELECT): $rowCountResult&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
?&amp;amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;(Optional) Weitere nützliche Array-Funktionen:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
**** &amp;lt;code&amp;gt;array_column(array $input, mixed $column_key [, mixed $index_key = null ])&amp;lt;/code&amp;gt;: Extrahiert alle Werte aus einer einzelnen Spalte des Ergebnis-Arrays und gibt sie als neues, eindimensionales Array zurück.[62] Sehr nützlich, um z.B. eine Liste aller Benutzer-IDs oder E-Mail-Adressen zu erhalten.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Annahme: $results enthält das Array von assoziativen Arrays&lt;br /&gt;
$alle_emails = array_column($results, &amp;#039;email&amp;#039;);&lt;br /&gt;
// $alle_emails ist nun [&amp;#039;alice@example.com&amp;#039;, &amp;#039;bob@example.com&amp;#039;]&lt;br /&gt;
print_r($alle_emails);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
**** &amp;lt;code&amp;gt;array_map(callable $callback, array $array1 [, array...$arrays ])&amp;lt;/code&amp;gt;: Wendet eine benutzerdefinierte Funktion (&amp;lt;code&amp;gt;callback&amp;lt;/code&amp;gt;) auf jedes Element eines Arrays an und gibt ein neues Array mit den Ergebnissen zurück.[63] Nützlich zur Transformation von Daten (z.B. Formatierung, Bereinigung).&lt;br /&gt;
**** &amp;lt;code&amp;gt;array_filter(array $array [, callable $callback [, int $flag = 0 ]])&amp;lt;/code&amp;gt;: Filtert Elemente eines Arrays basierend auf einer Callback-Funktion.[61] Nützlich, um nur bestimmte Zeilen aus dem Ergebnis-Array zu behalten (obwohl dies oft effizienter direkt in der SQL-Abfrage geschieht).&lt;br /&gt;
**** &amp;lt;code&amp;gt;array_count_values(array $array)&amp;lt;/code&amp;gt;: Zählt, wie oft jeder eindeutige Wert in einem eindimensionalen Array vorkommt.[63] Kann nützlich sein, wenn man z.B. mit &amp;lt;code&amp;gt;array_column&amp;lt;/code&amp;gt; eine Spalte extrahiert hat und die Häufigkeit bestimmter Werte (z.B. Status) wissen möchte.&lt;br /&gt;
Diese Funktionen, insbesondere &amp;lt;code&amp;gt;foreach&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;count&amp;lt;/code&amp;gt;, sind grundlegende Werkzeuge bei der Verarbeitung von Datenbankergebnissen in PHP.&lt;br /&gt;
&lt;br /&gt;
== Zusammenfassung &amp;amp; Best Practices ==&lt;br /&gt;
&lt;br /&gt;
Die Integration von Datenbanken ist ein zentraler Aspekt der PHP-Webentwicklung. Dieses Kapitel hat die Grundlagen für die Interaktion mit MySQL-Datenbanken gelegt. Hier sind die wichtigsten Punkte und Best Practices zusammengefasst:&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Wahl der Erweiterung:&amp;#039;&amp;#039;&amp;#039; Verwenden Sie ausschließlich die modernen Erweiterungen &amp;#039;&amp;#039;&amp;#039;PDO&amp;#039;&amp;#039;&amp;#039; oder &amp;#039;&amp;#039;&amp;#039;MySQLi&amp;#039;&amp;#039;&amp;#039;.[2, 29, 5, 15]&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;PDO&amp;#039;&amp;#039;&amp;#039; wird generell für neue Projekte empfohlen, da es Datenbankunabhängigkeit bietet und eine konsistentere, oft als einfacher empfundene API (insbesondere bei der Parameterbindung) hat.[16, 3, 64]&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;MySQLi&amp;#039;&amp;#039;&amp;#039; ist eine gute Wahl, wenn ausschließlich mit MySQL gearbeitet wird und spezifische MySQL-Funktionen benötigt werden oder wenn eine prozedurale Schnittstelle bevorzugt wird.[15]&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Prepared Statements:&amp;#039;&amp;#039;&amp;#039; Dies ist die wichtigste Sicherheitsmaßnahme. Verwenden Sie Prepared Statements immer dann, wenn externe Daten (Benutzereingaben, URL-Parameter etc.) Teil einer SQL-Abfrage werden (insbesondere bei &amp;lt;code&amp;gt;WHERE&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;INSERT&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;UPDATE&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;DELETE&amp;lt;/code&amp;gt;).[2, 25, 5] Sie verhindern SQL Injection zuverlässig durch die Trennung von Code und Daten.&lt;br /&gt;
&lt;br /&gt;
 &amp;#039;&amp;#039;&amp;#039;Sichere Fehlerbehandlung:&amp;#039;&amp;#039;&amp;#039; Konfigurieren Sie PDO (&amp;lt;code&amp;gt;PDO::ATTR_ERRMODE =&amp;amp;gt; PDO::ERRMODE_EXCEPTION&amp;lt;/code&amp;gt;) oder MySQLi (&amp;lt;code&amp;gt;mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT)&amp;lt;/code&amp;gt;) so, dass Fehler als Exceptions behandelt werden.[1, 18] Fangen Sie diese Exceptions ab und loggen Sie detaillierte Fehlermeldungen serverseitig, anstatt sie dem Benutzer anzuzeigen.[20, 5]&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Input Validation:&amp;#039;&amp;#039;&amp;#039; Validieren Sie alle Benutzereingaben serverseitig auf Typ, Format und erlaubte Werte, bevor Sie sie an die Datenbank übergeben. Dies ist eine wichtige zusätzliche Schutzschicht.[4, 5]&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Prinzip der geringsten Rechte (Least Privilege):&amp;#039;&amp;#039;&amp;#039; Der Datenbankbenutzer, den Ihre PHP-Anwendung verwendet, sollte nur die minimal notwendigen Berechtigungen für seine Aufgaben besitzen.[5]&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Sichere Zugangsdaten:&amp;#039;&amp;#039;&amp;#039; Speichern Sie Datenbank-Credentials (Hostname, Benutzername, Passwort) niemals direkt im PHP-Code oder in Dateien innerhalb des Web-Roots.[20, 65] Verwenden Sie stattdessen Konfigurationsdateien außerhalb des öffentlich zugänglichen Bereichs oder Umgebungsvariablen.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Code-Struktur:&amp;#039;&amp;#039;&amp;#039; Kapseln Sie Datenbankverbindungslogik und wiederkehrende Abfragen in Funktionen oder Klassen, um die Wartbarkeit und Wiederverwendbarkeit zu verbessern.[20]&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Effizienz:&amp;#039;&amp;#039;&amp;#039; Laden Sie große Ergebnismengen nicht komplett in den Speicher (&amp;lt;code&amp;gt;fetchAll&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;fetch_all&amp;lt;/code&amp;gt;). Verwenden Sie stattdessen zeilenweise Verarbeitung (&amp;lt;code&amp;gt;while&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;fetch&amp;lt;/code&amp;gt;) oder Iteratoren (PDO mit PHP 8+).[37, 40] Nutzen Sie persistente Verbindungen nur nach sorgfältiger Abwägung der Vor- und Nachteile.[7, 27]&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Defense-in-Depth:&amp;#039;&amp;#039;&amp;#039; Betrachten Sie Sicherheit als Gesamtkonzept. Die Kombination aus der richtigen API-Wahl, konsequenten Prepared Statements, Eingabevalidierung, minimalen Rechten, sicherer Fehlerbehandlung, sicherer Speicherung von Zugangsdaten und regelmäßigen Software-Updates bildet eine robuste Verteidigung.&lt;/div&gt;</summary>
		<author><name>Völkl Anna</name></author>
	</entry>
	<entry>
		<id>https://mediawiki.fernfh.ac.at/mediawiki/index.php?title=Datenbankintegration_(Webdevelopment)&amp;diff=6574</id>
		<title>Datenbankintegration (Webdevelopment)</title>
		<link rel="alternate" type="text/html" href="https://mediawiki.fernfh.ac.at/mediawiki/index.php?title=Datenbankintegration_(Webdevelopment)&amp;diff=6574"/>
		<updated>2025-05-01T13:12:06Z</updated>

		<summary type="html">&lt;p&gt;Völkl Anna: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Kapitel X: Datenbankintegration mit PHP und MySQL =&lt;br /&gt;
&lt;br /&gt;
== Einleitung: Warum Datenbanken in PHP-Webanwendungen? ==&lt;br /&gt;
&lt;br /&gt;
Moderne Webanwendungen sind selten statisch. Sie leben von dynamischen Inhalten, Benutzerinteraktionen und der Fähigkeit, Informationen über Sitzungen hinweg zu speichern und wieder abzurufen. Ob es sich um ein Content-Management-System (CMS), einen Online-Shop, ein soziales Netzwerk oder eine einfache Benutzerverwaltung handelt – die Persistenz von Daten ist entscheidend. Hier kommen Datenbanken ins Spiel.&lt;br /&gt;
&lt;br /&gt;
PHP allein, als serverseitige Skriptsprache, kann Daten nur für die Dauer der Ausführung eines Skripts im Arbeitsspeicher halten. Sobald das Skript beendet ist, gehen diese Informationen verloren. Um Daten dauerhaft zu speichern, zu organisieren, zu ändern und effizient abzurufen, benötigen PHP-Anwendungen ein externes Datenbanksystem.&lt;br /&gt;
&lt;br /&gt;
Eine der populärsten Datenbanklösungen im Webentwicklungsbereich, insbesondere in Kombination mit PHP, ist MySQL. Als relationales Datenbankmanagementsystem (RDBMS) bietet MySQL eine strukturierte Methode zur Datenspeicherung in Tabellen, die miteinander in Beziehung stehen können. Es ist ein Kernbestandteil vieler Webserver-Setups wie LAMP (Linux, Apache, MySQL, PHP) oder LEMP (Linux, Nginx, MySQL, PHP).&lt;br /&gt;
&lt;br /&gt;
Um die Brücke zwischen der PHP-Anwendung und der MySQL-Datenbank zu schlagen, stellt PHP spezielle Erweiterungen (Extensions) bereit. Diese Erweiterungen fungieren als Schnittstelle oder API (Application Programming Interface), die es PHP-Skripten ermöglichen, eine Verbindung zur Datenbank herzustellen, SQL-Befehle zu senden und die von der Datenbank zurückgegebenen Ergebnisse zu empfangen und zu verarbeiten.&lt;br /&gt;
&lt;br /&gt;
Dieses Kapitel führt in die Grundlagen der Datenbankintegration mit PHP und MySQL ein. Es enthält die verschiedenen verfügbaren PHP-Erweiterungen, wobei der Schwerpunkt auf den modernen und sicheren Ansätzen liegt: PDO (PHP Data Objects) und MySQLi (MySQL Improved). Es wird gezeigt, wie man Verbindungen aufbaut, grundlegende Datenbankoperationen – bekannt als CRUD (Create, Read, Update, Delete) – durchführt und wie man Daten sicher abfragt und manipuliert. Ein besonderes Augenmerk liegt dabei auf der Sicherheit, insbesondere auf der Vermeidung von SQL-Injection-Angriffen durch den Einsatz von Prepared Statements. Zum Abschluss erfolgt ein Überblick über nützliche PHP-Array-Funktionen zur Verarbeitung der abgerufenen Datenbankergebnisse.&lt;br /&gt;
&lt;br /&gt;
== Überblick über PHP-Datenbankerweiterungen ==&lt;br /&gt;
&lt;br /&gt;
Die Art und Weise, wie PHP mit Datenbanken interagiert, hat sich im Laufe der Zeit weiterentwickelt. Ursprünglich gab es für jede populäre Datenbank eine eigene spezifische Erweiterung. Für MySQL waren dies die &amp;lt;code&amp;gt;mysql_*&amp;lt;/code&amp;gt; Funktionen. Diese sind jedoch inzwischen veraltet und wurden durch modernere, flexiblere und sicherere Alternativen ersetzt. Heute stehen Entwicklern hauptsächlich zwei empfohlene Erweiterungen für die Arbeit mit MySQL zur Verfügung: PDO und MySQLi.&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;PDO (PHP Data Objects):&amp;#039;&amp;#039;&amp;#039; Eine allgemeine Datenzugriffs-Abstraktionsschicht, die mit verschiedenen Datenbanksystemen verwendet werden kann.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;MySQLi (MySQL Improved):&amp;#039;&amp;#039;&amp;#039; Eine Erweiterung, die speziell für die Arbeit mit MySQL entwickelt wurde und dessen spezifische Funktionen unterstützt.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;&amp;lt;code&amp;gt;mysql_*&amp;lt;/code&amp;gt; Funktionen:&amp;#039;&amp;#039;&amp;#039; Die ursprüngliche MySQL-Erweiterung, die seit PHP 7.0 entfernt wurde und nicht mehr verwendet werden sollte.&lt;br /&gt;
Im Folgenden werden diese drei Optionen detaillierter vorgestellt.&lt;br /&gt;
&lt;br /&gt;
=== PDO (PHP Data Objects) ===&lt;br /&gt;
&lt;br /&gt;
Die PDO-Erweiterung definiert eine leichtgewichtige und konsistente Schnittstelle für den Datenbankzugriff in PHP. Sie fungiert als &amp;#039;&amp;#039;Datenzugriffs-Abstraktionsschicht&amp;#039;&amp;#039;. Das bedeutet, dass Entwickler unabhängig von der verwendeten Datenbank dieselben PDO-Funktionen nutzen können, um Abfragen auszuführen und Daten abzurufen.[1, 2] Es ist jedoch wichtig zu verstehen, dass PDO &amp;#039;&amp;#039;keine&amp;#039;&amp;#039; Datenbankabstraktion im Sinne einer SQL-Dialekt-Übersetzung oder der Emulation fehlender Datenbankfeatures bietet. Um eine Verbindung zu einer spezifischen Datenbank wie MySQL herzustellen, benötigt PDO einen entsprechenden datenbankspezifischen Treiber, z.B. &amp;lt;code&amp;gt;pdo_mysql&amp;lt;/code&amp;gt;, der in der PHP-Konfiguration aktiviert sein muss.[1]&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Vorteile von PDO:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Datenbankunabhängigkeit:&amp;#039;&amp;#039;&amp;#039; Der größte Vorteil von PDO ist die Möglichkeit, den Code für Datenbankinteraktionen weitgehend unverändert zu lassen, selbst wenn das zugrunde liegende Datenbanksystem gewechselt wird (z.B. von MySQL zu PostgreSQL).[1, 2] Dies erhöht die Portabilität und Flexibilität von PHP-Anwendungen erheblich.[2, 3]&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Sicherheit durch Prepared Statements:&amp;#039;&amp;#039;&amp;#039; PDO bietet eine robuste und konsistente Implementierung von Prepared Statements. Diese Technik ist der Goldstandard zur Verhinderung von SQL-Injection-Angriffen, da sie die SQL-Befehlsstruktur strikt von den übergebenen Daten trennt.[1, 2, 4, 5, 6]&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Benutzerfreundlichkeit und Konsistenz:&amp;#039;&amp;#039;&amp;#039; PDO stellt eine einheitliche API mit nützlichen Hilfsfunktionen bereit, wie z.B. vielfältige &amp;quot;Fetch Modes&amp;quot;, um die Struktur der abgerufenen Daten zu steuern.[2] Die konsistente Funktionsweise über verschiedene Datenbanktreiber hinweg vereinfacht die Entwicklung.&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Moderne Fehlerbehandlung:&amp;#039;&amp;#039;&amp;#039; PDO verwendet standardmäßig Exceptions (&amp;lt;code&amp;gt;PDOException&amp;lt;/code&amp;gt;) zur Signalisierung von Fehlern.[1, 2, 7] Dies ermöglicht eine strukturierte und moderne Fehlerbehandlung mittels &amp;lt;code&amp;gt;try...catch&amp;lt;/code&amp;gt;-Blöcken, was als Best Practice gilt.[1, 2]&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Verbindungsaufbau mit PDO (Beispiel MySQL):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;DSN (Data Source Name):&amp;#039;&amp;#039;&amp;#039; Die Verbindungsparameter werden in einer Zeichenkette, dem DSN, übergeben. Für MySQL hat der DSN typischerweise das Format &amp;lt;code&amp;gt;mysql:host=hostname;dbname=datenbankname;charset=zeichensatz;unix_socket=pfad_zum_socket&amp;lt;/code&amp;gt;. Die Angabe des Zeichensatzes (z.B. &amp;lt;code&amp;gt;utf8mb4&amp;lt;/code&amp;gt;) ist wichtig für die korrekte Datenübertragung.[1, 7] Beispiel: &amp;lt;code&amp;gt;mysql:host=localhost;dbname=test;charset=utf8mb4&amp;lt;/code&amp;gt;.&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;PDO-Konstruktor:&amp;#039;&amp;#039;&amp;#039; Eine Verbindung wird durch Instanziierung der PDO-Klasse hergestellt: &amp;lt;code&amp;gt;$pdo = new PDO($dsn, $username, $password, $options);&amp;lt;/code&amp;gt;.[1, 7, 8] Die Parameter sind der DSN, der Datenbankbenutzername, das Passwort und ein optionales Array mit Treiberoptionen.&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Verbindungsoptionen (&amp;lt;code&amp;gt;$options&amp;lt;/code&amp;gt;):&amp;#039;&amp;#039;&amp;#039; Über dieses Array können wichtige Verhaltensweisen von PDO gesteuert werden:&lt;br /&gt;
*** &amp;lt;code&amp;gt;PDO::ATTR_ERRMODE =&amp;amp;gt; PDO::ERRMODE_EXCEPTION&amp;lt;/code&amp;gt;: Dies ist die empfohlene Einstellung für die Fehlerbehandlung. PDO wirft bei Fehlern eine &amp;lt;code&amp;gt;PDOException&amp;lt;/code&amp;gt;, die abgefangen werden kann.[1, 2, 4]&lt;br /&gt;
*** &amp;lt;code&amp;gt;PDO::ATTR_DEFAULT_FETCH_MODE =&amp;amp;gt; PDO::FETCH_ASSOC&amp;lt;/code&amp;gt;: Legt fest, dass Daten standardmäßig als assoziatives Array (&amp;lt;code&amp;gt;spaltenname =&amp;amp;gt; wert&amp;lt;/code&amp;gt;) zurückgegeben werden, was oft die Weiterverarbeitung erleichtert.[1, 9]&lt;br /&gt;
*** &amp;lt;code&amp;gt;PDO::ATTR_EMULATE_PREPARES =&amp;amp;gt; false&amp;lt;/code&amp;gt;: Deaktiviert die Emulation von Prepared Statements durch PDO und zwingt die Verwendung nativer Prepared Statements des Datenbanktreibers. Dies gilt als die sicherste und oft performanteste Methode.[1, 10, 9, 11] Standardmäßig ist die Emulation aktiviert (&amp;lt;code&amp;gt;true&amp;lt;/code&amp;gt;). Obwohl PDO auch im emulierten Modus durch korrektes Escaping Schutz vor SQL Injection bietet [12, 5, 13], eliminiert die Deaktivierung potenzielle, wenn auch seltene, Sicherheitsrisiken, die in bestimmten Konstellationen (z.B. bei Verwendung exotischer Zeichensätze wie GBK [11]) auftreten könnten. Die explizite Deaktivierung ist daher eine wichtige Best Practice.&lt;br /&gt;
*** &amp;lt;code&amp;gt;PDO::ATTR_PERSISTENT =&amp;amp;gt; true&amp;lt;/code&amp;gt;: Aktiviert persistente Datenbankverbindungen. Diese werden nicht am Ende des Skripts geschlossen, sondern in einem Pool gehalten und für nachfolgende Anfragen wiederverwendet. Dies kann die Performance von Webanwendungen verbessern, da der Overhead des Verbindungsaufbaus entfällt. Die Verwendung erfordert jedoch sorgfältige Überlegung, da der Zustand der Verbindung zwischen Anfragen bestehen bleiben kann.[7]&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Fehlerbehandlung:&amp;#039;&amp;#039;&amp;#039; Der Verbindungsaufbau sollte immer in einem &amp;lt;code&amp;gt;try...catch&amp;lt;/code&amp;gt;-Block erfolgen, um &amp;lt;code&amp;gt;PDOException&amp;lt;/code&amp;gt; abzufangen und angemessen darauf zu reagieren (z.B. Fehlermeldung loggen, alternative Aktion ausführen).[1, 7] Es ist kritisch, detaillierte Fehlermeldungen in Produktionsumgebungen nicht an den Benutzer auszugeben, da sie sensible Informationen enthalten könnten. Die PHP-Einstellung &amp;lt;code&amp;gt;display_errors&amp;lt;/code&amp;gt; sollte daher auf &amp;lt;code&amp;gt;0&amp;lt;/code&amp;gt; gesetzt sein.[7]&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Verbindung schließen:&amp;#039;&amp;#039;&amp;#039; Eine PDO-Verbindung bleibt für die Lebensdauer des PDO-Objekts aktiv. Um sie explizit zu schließen, müssen alle Referenzen auf das PDO-Objekt und alle davon abgeleiteten &amp;lt;code&amp;gt;PDOStatement&amp;lt;/code&amp;gt;-Objekte entfernt werden, typischerweise durch Zuweisung von &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt;.[7] PHP schließt die Verbindung automatisch am Ende des Skriptlaufs, wenn dies nicht manuell geschieht.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Code-Beispiel (PDO-Verbindung zu MySQL):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;amp;lt;?php&lt;br /&gt;
$host = &amp;#039;localhost&amp;#039;;&lt;br /&gt;
$dbname = &amp;#039;meine_datenbank&amp;#039;;&lt;br /&gt;
$username = &amp;#039;mein_benutzer&amp;#039;;&lt;br /&gt;
$password = &amp;#039;mein_passwort&amp;#039;;&lt;br /&gt;
$charset = &amp;#039;utf8mb4&amp;#039;;&lt;br /&gt;
&lt;br /&gt;
$dsn = &amp;quot;mysql:host=$host;dbname=$dbname;charset=$charset&amp;quot;;&lt;br /&gt;
$options =;&lt;br /&gt;
&lt;br /&gt;
try {&lt;br /&gt;
 $pdo = new PDO($dsn, $username, $password, $options);&lt;br /&gt;
 // Erfolgreich verbunden, $pdo-Objekt kann nun verwendet werden.&lt;br /&gt;
 // echo &amp;quot;Verbindung erfolgreich hergestellt!&amp;quot;;&lt;br /&gt;
} catch (\PDOException $e) {&lt;br /&gt;
 // Fehler beim Verbindungsaufbau&lt;br /&gt;
 // In Produktion: Fehler loggen, statt ausgeben&lt;br /&gt;
 error_log(&amp;quot;Datenbankverbindungsfehler: &amp;quot;. $e---&amp;amp;gt;getMessage());&lt;br /&gt;
 die(&amp;quot;Es gab ein Problem mit der Datenbankverbindung. Bitte versuchen Sie es später erneut.&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//... hier Datenbankoperationen mit $pdo durchführen...&lt;br /&gt;
&lt;br /&gt;
// Verbindung explizit schließen (optional, da PHP dies am Skriptende tut)&lt;br /&gt;
// $pdo = null;&lt;br /&gt;
?&amp;amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Dieses Beispiel zeigt den empfohlenen Weg, eine PDO-Verbindung zu MySQL herzustellen, inklusive wichtiger Optionen für Fehlerbehandlung und Sicherheit sowie einem &amp;lt;code&amp;gt;try...catch&amp;lt;/code&amp;gt;-Block zum Abfangen von Verbindungsfehlern.&lt;br /&gt;
&lt;br /&gt;
=== MySQLi (MySQL Improved) ===&lt;br /&gt;
&lt;br /&gt;
Die MySQLi-Erweiterung ist, wie der Name schon sagt, speziell für die Interaktion mit MySQL-Datenbanken konzipiert und bietet Unterstützung für Funktionen, die ab MySQL Version 4.1 eingeführt wurden.[14, 15] Sie ermöglicht den Zugriff auf MySQL-spezifische Features, die über PDO möglicherweise nicht direkt verfügbar sind.[16, 15]&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Duale Schnittstelle:&amp;#039;&amp;#039;&amp;#039; Ein charakteristisches Merkmal von MySQLi ist die Unterstützung von zwei Programmierstilen [17]:&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Prozedural:&amp;#039;&amp;#039;&amp;#039; Dieser Stil ähnelt den alten &amp;lt;code&amp;gt;mysql_*&amp;lt;/code&amp;gt; Funktionen. Die Funktionsnamen beginnen typischerweise mit &amp;lt;code&amp;gt;mysqli_&amp;lt;/code&amp;gt; (z.B. &amp;lt;code&amp;gt;mysqli_connect()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;mysqli_query()&amp;lt;/code&amp;gt;). Der Datenbankverbindungs-Handle (eine Ressource oder ein Objekt) muss dabei explizit als erster Parameter an die meisten Funktionen übergeben werden.[17] Dieser Stil wird oft von Entwicklern bevorzugt, die von der alten &amp;lt;code&amp;gt;mysql&amp;lt;/code&amp;gt;-Erweiterung migrieren.[17]&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Objektorientiert (OO):&amp;#039;&amp;#039;&amp;#039; Hier wird ein Objekt der &amp;lt;code&amp;gt;mysqli&amp;lt;/code&amp;gt;-Klasse erstellt, und Datenbankoperationen werden als Methoden dieses Objekts aufgerufen (z.B. &amp;lt;code&amp;gt;$mysqli-&amp;amp;gt;query()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;$mysqli-&amp;amp;gt;prepare()&amp;lt;/code&amp;gt;).[17] Dieser Ansatz passt oft besser zu modernen PHP-Entwicklungspraktiken und vermeidet die Notwendigkeit, den Verbindungshandle ständig zu übergeben. Die offizielle PHP-Dokumentation für MySQLi ist primär im objektorientierten Stil verfasst.[17] Es gibt keine nennenswerten Performance-Unterschiede zwischen den beiden Stilen; die Wahl ist meist eine Frage der persönlichen Präferenz oder Projektkonventionen.[17]&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Verbindungsaufbau mit MySQLi:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Prozedural:&amp;#039;&amp;#039;&amp;#039; Die Funktion &amp;lt;code&amp;gt;mysqli_connect()&amp;lt;/code&amp;gt; wird verwendet: &amp;lt;code&amp;gt;$link = mysqli_connect($host, $user, $pass, $db, $port, $socket);&amp;lt;/code&amp;gt;. Sie gibt bei Erfolg ein &amp;lt;code&amp;gt;mysqli&amp;lt;/code&amp;gt;-Objekt (oder &amp;lt;code&amp;gt;false&amp;lt;/code&amp;gt; bei Fehlern vor PHP 8.1) zurück.[17, 18] Fehler können mit &amp;lt;code&amp;gt;mysqli_connect_errno()&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;mysqli_connect_error()&amp;lt;/code&amp;gt; geprüft werden.[18]&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Objektorientiert:&amp;#039;&amp;#039;&amp;#039; Ein neues &amp;lt;code&amp;gt;mysqli&amp;lt;/code&amp;gt;-Objekt wird instanziiert: &amp;lt;code&amp;gt;$mysqli = new mysqli($host, $user, $pass, $db, $port, $socket);&amp;lt;/code&amp;gt;. Wichtig: Der Konstruktor gibt &amp;#039;&amp;#039;immer&amp;#039;&amp;#039; ein Objekt zurück, auch wenn die Verbindung fehlschlägt. Der Verbindungsstatus muss explizit über die Eigenschaften &amp;lt;code&amp;gt;$mysqli-&amp;amp;gt;connect_errno&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;$mysqli-&amp;amp;gt;connect_error&amp;lt;/code&amp;gt; geprüft werden.[17, 18]&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Fortgeschrittene Verbindung:&amp;#039;&amp;#039;&amp;#039; Für feinere Kontrolle über Verbindungsoptionen (z.B. Setzen von Timeouts oder Initialisierungsbefehlen &amp;#039;&amp;#039;vor&amp;#039;&amp;#039; dem Verbindungsaufbau) können &amp;lt;code&amp;gt;mysqli_init()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;mysqli_options()&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;mysqli_real_connect()&amp;lt;/code&amp;gt; verwendet werden.[18, 19]&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Fehlerbehandlung:&amp;#039;&amp;#039;&amp;#039; Neben der manuellen Prüfung von Fehlercodes und -meldungen ist die empfohlene Methode, MySQLi so zu konfigurieren, dass es bei Fehlern Exceptions wirft. Dies geschieht mit &amp;lt;code&amp;gt;mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);&amp;lt;/code&amp;gt;. Danach können Datenbankfehler elegant mit &amp;lt;code&amp;gt;try...catch&amp;lt;/code&amp;gt;-Blöcken behandelt werden, ähnlich wie bei PDO.[18, 20, 21, 22, 23, 24, 25, 26]&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Verbindung schließen:&amp;#039;&amp;#039;&amp;#039; Prozedural mit &amp;lt;code&amp;gt;mysqli_close($link)&amp;lt;/code&amp;gt;, objektorientiert mit &amp;lt;code&amp;gt;$mysqli-&amp;amp;gt;close()&amp;lt;/code&amp;gt;.[17, 18, 19]&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Persistente Verbindungen:&amp;#039;&amp;#039;&amp;#039; MySQLi unterstützt ebenfalls persistente Verbindungen, die über PHP-Konfigurationseinstellungen (&amp;lt;code&amp;gt;mysqli.allow_persistent&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;mysqli.max_persistent&amp;lt;/code&amp;gt;) gesteuert werden.[27] Standardmäßig setzt MySQLi den Zustand einer wiederverwendeten persistenten Verbindung zurück (via &amp;lt;code&amp;gt;mysqli::change_user()&amp;lt;/code&amp;gt;), was zwar für Konsistenz sorgt, aber auch Performance kosten kann.[27]&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Code-Beispiele (MySQLi-Verbindungen):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Objektorientiert (mit Exception-Handling):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;amp;lt;?php&lt;br /&gt;
$host = &amp;#039;localhost&amp;#039;;&lt;br /&gt;
$user = &amp;#039;mein_benutzer&amp;#039;;&lt;br /&gt;
$pass = &amp;#039;mein_passwort&amp;#039;;&lt;br /&gt;
$db = &amp;#039;meine_datenbank&amp;#039;;&lt;br /&gt;
&lt;br /&gt;
// Fehlerberichterstattung für Exceptions aktivieren&lt;br /&gt;
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);&lt;br /&gt;
&lt;br /&gt;
try {&lt;br /&gt;
 $mysqli = new mysqli($host, $user, $pass, $db);&lt;br /&gt;
 // Zeichensatz setzen (wichtig!)&lt;br /&gt;
 $mysqli---&amp;amp;gt;set_charset(&amp;#039;utf8mb4&amp;#039;);&lt;br /&gt;
 // Erfolgreich verbunden&lt;br /&gt;
 // echo &amp;quot;Verbindung erfolgreich (OO)! Host-Info: &amp;quot;. $mysqli-&amp;amp;gt;host_info;&lt;br /&gt;
} catch (mysqli_sql_exception $e) {&lt;br /&gt;
 // Fehler beim Verbindungsaufbau oder set_charset&lt;br /&gt;
 error_log(&amp;quot;MySQLi Verbindungsfehler: &amp;quot;. $e-&amp;amp;gt;getMessage());&lt;br /&gt;
 die(&amp;quot;Es gab ein Problem mit der Datenbankverbindung.&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//... Datenbankoperationen mit $mysqli...&lt;br /&gt;
&lt;br /&gt;
// Verbindung schließen&lt;br /&gt;
// $mysqli-&amp;amp;gt;close();&lt;br /&gt;
?&amp;amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Prozedural (mit Exception-Handling):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!--?php$host = &amp;#039;localhost&amp;#039;;&lt;br /&gt;
$user = &amp;#039;mein_benutzer&amp;#039;;&lt;br /&gt;
$pass = &amp;#039;mein_passwort&amp;#039;;&lt;br /&gt;
$db = &amp;#039;meine_datenbank&amp;#039;;&lt;br /&gt;
&lt;br /&gt;
// Fehlerberichterstattung für Exceptions aktivieren&lt;br /&gt;
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);&lt;br /&gt;
&lt;br /&gt;
try {&lt;br /&gt;
    $link = mysqli_connect($host, $user, $pass, $db);&lt;br /&gt;
    // Zeichensatz setzen&lt;br /&gt;
    mysqli_set_charset($link, &amp;#039;utf8mb4&amp;#039;);&lt;br /&gt;
    // Erfolgreich verbunden&lt;br /&gt;
    // echo &amp;quot;Verbindung erfolgreich (Prozedural)! Host-Info: &amp;quot;. mysqli_get_host_info($link);&lt;br /&gt;
} catch (mysqli_sql_exception $e) {&lt;br /&gt;
    // Fehler beim Verbindungsaufbau oder set_charset&lt;br /&gt;
    error_log(&amp;quot;MySQLi Verbindungsfehler: &amp;quot;. $e---&amp;gt;getMessage());&lt;br /&gt;
 die(&amp;quot;Es gab ein Problem mit der Datenbankverbindung.&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//... Datenbankoperationen mit $link...&lt;br /&gt;
&lt;br /&gt;
// Verbindung schließen&lt;br /&gt;
// mysqli_close($link);&lt;br /&gt;
?&amp;amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Bedeutung von &amp;lt;code&amp;gt;mysqlnd&amp;lt;/code&amp;gt;:&amp;#039;&amp;#039;&amp;#039; Die Benutzerfreundlichkeit, insbesondere bei der Arbeit mit Prepared Statements, wird stark durch den verwendeten MySQL-Treiber beeinflusst. Der &amp;#039;&amp;#039;&amp;#039;MySQL Native Driver (&amp;lt;code&amp;gt;mysqlnd&amp;lt;/code&amp;gt;)&amp;#039;&amp;#039;&amp;#039;, der seit PHP 5.4 standardmäßig enthalten ist [16], ist hier entscheidend. &amp;lt;code&amp;gt;mysqlnd&amp;lt;/code&amp;gt; stellt die Methode &amp;lt;code&amp;gt;get_result()&amp;lt;/code&amp;gt; (bzw. die Funktion &amp;lt;code&amp;gt;mysqli_stmt_get_result()&amp;lt;/code&amp;gt;) zur Verfügung.[25, 15] Diese Methode erlaubt es, nach dem Ausführen eines Prepared Statements ein Standard-&amp;lt;code&amp;gt;mysqli_result&amp;lt;/code&amp;gt;-Objekt zu erhalten, von dem dann wie gewohnt mit &amp;lt;code&amp;gt;fetch_assoc()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;fetch_all()&amp;lt;/code&amp;gt; etc. die Ergebnisse abgerufen werden können.[25] Ohne &amp;lt;code&amp;gt;mysqlnd&amp;lt;/code&amp;gt; (bei Verwendung der älteren &amp;lt;code&amp;gt;libmysqlclient&amp;lt;/code&amp;gt;-Bibliothek) ist das Abrufen von Ergebnissen aus Prepared Statements deutlich umständlicher und erfordert die manuelle Bindung jeder einzelnen Ergebnisspalte an PHP-Variablen mittels &amp;lt;code&amp;gt;bind_result()&amp;lt;/code&amp;gt; und anschließendes &amp;lt;code&amp;gt;fetch()&amp;lt;/code&amp;gt;.[25] Da &amp;lt;code&amp;gt;mysqlnd&amp;lt;/code&amp;gt; heute der Standard ist, konzentrieren sich moderne Tutorials und Beispiele meist auf die &amp;lt;code&amp;gt;get_result()&amp;lt;/code&amp;gt;-Methode, aber es ist wichtig, diesen Hintergrund zu kennen, falls man auf ältere Systeme oder Konfigurationen trifft.&lt;br /&gt;
&lt;br /&gt;
=== Veraltete &amp;lt;code&amp;gt;mysql_*&amp;lt;/code&amp;gt; Funktionen ===&lt;br /&gt;
&lt;br /&gt;
Die &amp;lt;code&amp;gt;mysql_*&amp;lt;/code&amp;gt; Funktionsfamilie (&amp;lt;code&amp;gt;mysql_connect&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;mysql_query&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;mysql_fetch_assoc&amp;lt;/code&amp;gt; etc.) war die ursprüngliche Methode, um in PHP mit MySQL-Datenbanken zu interagieren. Diese Erweiterung ist jedoch &amp;#039;&amp;#039;&amp;#039;stark veraltet&amp;#039;&amp;#039;&amp;#039; und sollte &amp;#039;&amp;#039;&amp;#039;unter keinen Umständen mehr für neue Projekte verwendet werden&amp;#039;&amp;#039;&amp;#039;.&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Status:&amp;#039;&amp;#039;&amp;#039; Die &amp;lt;code&amp;gt;mysql_*&amp;lt;/code&amp;gt; Erweiterung wurde in &amp;#039;&amp;#039;&amp;#039;PHP 5.5&amp;#039;&amp;#039;&amp;#039; offiziell als &amp;quot;deprecated&amp;quot; (veraltet) markiert, was bedeutet, dass ihre Verwendung ab dieser Version Warnungen (&amp;lt;code&amp;gt;E_DEPRECATED&amp;lt;/code&amp;gt;) erzeugte.[28, 29] Mit der Veröffentlichung von &amp;#039;&amp;#039;&amp;#039;PHP 7.0&amp;#039;&amp;#039;&amp;#039; wurde die Erweiterung &amp;#039;&amp;#039;&amp;#039;vollständig entfernt&amp;#039;&amp;#039;&amp;#039;.[28, 29] PHP-Code, der diese Funktionen nutzt, führt auf PHP 7.0 oder neuer zu fatalen Fehlern und funktioniert nicht mehr.[28, 29]&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Gründe für die Entfernung:&amp;#039;&amp;#039;&amp;#039; Es gab zwingende Gründe für diesen Schritt:&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Gravierende Sicherheitsrisiken:&amp;#039;&amp;#039;&amp;#039; Der Hauptgrund war die Anfälligkeit für SQL-Injection-Angriffe. Die &amp;lt;code&amp;gt;mysql_*&amp;lt;/code&amp;gt; Funktionen boten keinen eingebauten Mechanismus wie Prepared Statements. Entwickler mussten Benutzereingaben manuell mit &amp;lt;code&amp;gt;mysql_real_escape_string()&amp;lt;/code&amp;gt; &amp;quot;escapen&amp;quot;, eine Methode, die leicht vergessen oder falsch angewendet werden konnte, was zu weit verbreiteten Sicherheitslücken führte.[29, 30] Das Fehlen von Prepared Statements in dieser alten Erweiterung ist direkt ursächlich für die hohe Zahl an SQL-Injection-Schwachstellen in älteren PHP-Anwendungen.[29]&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Fehlende Unterstützung moderner MySQL-Features:&amp;#039;&amp;#039;&amp;#039; Die Erweiterung wurde ursprünglich für MySQL Version 3.23 entwickelt und seitdem kaum weiterentwickelt.[29] Sie unterstützte viele wichtige Funktionen moderner MySQL-Versionen nicht, darunter Prepared Statements, Stored Procedures, Transaktionen, SSL-Verschlüsselung, Kompression, Multi-Statements und vollständige Zeichensatzunterstützung.[29]&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Schlechte Wartbarkeit:&amp;#039;&amp;#039;&amp;#039; Der Code der Erweiterung war veraltet und wurde immer schwieriger zu warten und an neue Versionen der MySQL-Client-Bibliotheken anzupassen.[29]&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Förderung unsicherer Praktiken:&amp;#039;&amp;#039;&amp;#039; Da viele alte Tutorials und Codebeispiele die &amp;lt;code&amp;gt;mysql_*&amp;lt;/code&amp;gt; Funktionen unsicher verwendeten, trug die fortgesetzte Verfügbarkeit zur Verbreitung schlechter und unsicherer Programmierpraktiken bei.[29]&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Verwendung heute:&amp;#039;&amp;#039;&amp;#039; Die klare Antwort lautet: &amp;#039;&amp;#039;&amp;#039;Nein&amp;#039;&amp;#039;&amp;#039;. Diese Funktionen existieren in modernen PHP-Versionen nicht mehr.[31, 30] Jede Anwendung, die sie noch verwendet, ist nicht nur unsicher, sondern auch inkompatibel mit aktueller PHP-Software. Eine Migration zu PDO oder MySQLi ist unumgänglich, um Sicherheit und Kompatibilität zu gewährleisten.[28, 29] Die erzwungene Migration durch die Entfernung in PHP 7.0 unterstreicht, wie wichtig es ist, Deprecation-Warnungen ernst zu nehmen und Code proaktiv zu aktualisieren, um zukünftige Probleme zu vermeiden.[29, 32]&lt;br /&gt;
&lt;br /&gt;
=== Tabelle 1: Vergleich der PHP-Datenbankerweiterungen für MySQL ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Merkmal !! PDO (PHP Data Objects) !! MySQLi (MySQL Improved) !! &amp;lt;code&amp;gt;mysql_*&amp;lt;/code&amp;gt; (Veraltet)&lt;br /&gt;
|-&amp;lt;br&amp;gt;&lt;br /&gt;
| aktiv=&amp;quot;&amp;quot; empfohlen=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| aktiv=&amp;quot;&amp;quot; empfohlen=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Entfernt (seit PHP 7.0) [29]&lt;br /&gt;
|-&amp;lt;br&amp;gt;&lt;br /&gt;
| objektorientiert=&amp;quot;&amp;quot; oo=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| oo=&amp;quot;&amp;quot; prozedural=&amp;quot;&amp;quot; 17=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Prozedural&lt;br /&gt;
|-&amp;lt;br&amp;gt;&lt;br /&gt;
| multi-datenbank=&amp;quot;&amp;quot; mysql=&amp;quot;&amp;quot; pgsql=&amp;quot;&amp;quot; sqlite=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| nur=&amp;quot;&amp;quot; mysql=&amp;quot;&amp;quot; 15=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Nur MySQL&lt;br /&gt;
|-&amp;lt;br&amp;gt;&lt;br /&gt;
| ja=&amp;quot;&amp;quot; nativ=&amp;quot;&amp;quot; empfohlen=&amp;quot;&amp;quot; emulation=&amp;quot;&amp;quot; m=&amp;quot;&amp;quot; glich=&amp;quot;&amp;quot; 1=&amp;quot;&amp;quot; 11=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| ja=&amp;quot;&amp;quot; nativ=&amp;quot;&amp;quot; 25=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Nein [29]&lt;br /&gt;
|- &lt;br /&gt;
| &amp;#039;&amp;#039;&amp;#039;SQL Injection Schutz&amp;#039;&amp;#039;&amp;#039; || sehr=&amp;quot;&amp;quot; gut=&amp;quot;&amp;quot; durch=&amp;quot;&amp;quot; prepared=&amp;quot;&amp;quot; statements=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Sehr gut (durch Prepared Statements) [25] || Schlecht (manuelles Escaping nötig) [29]&lt;br /&gt;
|-&amp;lt;br&amp;gt;&lt;br /&gt;
| gute=&amp;quot;&amp;quot; unterst=&amp;quot;&amp;quot; tzung=&amp;quot;&amp;quot; via=&amp;quot;&amp;quot; treiber=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Sehr gute Unterstützung (spezifisch) [15] || Veraltet, viele Features fehlen [29]&lt;br /&gt;
|-&amp;lt;br&amp;gt;&lt;br /&gt;
| hoch=&amp;quot;&amp;quot; 2=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| niedrig=&amp;quot;&amp;quot; mysql-spezifisch=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Niedrig&lt;br /&gt;
|-&amp;lt;br&amp;gt;&lt;br /&gt;
| exceptions=&amp;quot;&amp;quot; mwt-preservehtml=&amp;quot;&amp;quot; pdoexception=&amp;quot;&amp;quot; code=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| exceptions=&amp;quot;&amp;quot; mit=&amp;quot;&amp;quot; mwt-preservehtml=&amp;quot;&amp;quot; mysqli_report=&amp;quot;&amp;quot; code=&amp;quot;&amp;quot; fehlercodes=&amp;quot;&amp;quot; 18=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Fehlercodes / &amp;lt;code&amp;gt;mysql_error()&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
| &amp;#039;&amp;#039;&amp;#039;Nicht verwenden!&amp;#039;&amp;#039;&amp;#039; [29]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Grundlegende Datenbankoperationen (CRUD) ==&lt;br /&gt;
&lt;br /&gt;
Nachdem wir die verschiedenen Erweiterungen kennengelernt haben, wenden wir uns nun den grundlegenden Operationen zu, die man typischerweise mit einer Datenbank durchführt. Diese werden oft mit dem Akronym CRUD zusammengefasst:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;C&amp;#039;&amp;#039;&amp;#039;reate: Neue Datensätze erstellen (SQL: &amp;lt;code&amp;gt;INSERT&amp;lt;/code&amp;gt;).&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;R&amp;#039;&amp;#039;&amp;#039;ead: Vorhandene Datensätze lesen/abfragen (SQL: &amp;lt;code&amp;gt;SELECT&amp;lt;/code&amp;gt;).&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;U&amp;#039;&amp;#039;&amp;#039;pdate: Bestehende Datensätze ändern (SQL: &amp;lt;code&amp;gt;UPDATE&amp;lt;/code&amp;gt;).&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;D&amp;#039;&amp;#039;&amp;#039;elete: Datensätze löschen (SQL: &amp;lt;code&amp;gt;DELETE&amp;lt;/code&amp;gt;).&lt;br /&gt;
In den folgenden Abschnitten werden wir sehen, wie diese Operationen mit den modernen Erweiterungen PDO und MySQLi umgesetzt werden, wobei wir durchgehend &amp;#039;&amp;#039;&amp;#039;Prepared Statements&amp;#039;&amp;#039;&amp;#039; verwenden, um die Sicherheit zu gewährleisten.&lt;br /&gt;
&lt;br /&gt;
=== Daten abfragen (SELECT) ===&lt;br /&gt;
&lt;br /&gt;
Das Abfragen von Daten ist eine der häufigsten Datenbankoperationen.&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;PDO:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Ohne Parameter:&amp;#039;&amp;#039;&amp;#039; Wenn die Abfrage keine variablen Teile enthält (z.B. keine &amp;lt;code&amp;gt;WHERE&amp;lt;/code&amp;gt;-Klausel mit Benutzereingaben), kann die &amp;lt;code&amp;gt;query()&amp;lt;/code&amp;gt;-Methode verwendet werden. Sie führt die SQL-Abfrage direkt aus und gibt ein &amp;lt;code&amp;gt;PDOStatement&amp;lt;/code&amp;gt;-Objekt zurück, das zur Ergebnisauswertung dient.[2, 33]&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Beispiel: Alle Benutzer auswählen&lt;br /&gt;
$stmt = $pdo-&amp;amp;gt;query(&amp;quot;SELECT id, username FROM users&amp;quot;);&lt;br /&gt;
// $stmt kann nun zum Fetchen der Ergebnisse verwendet werden&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Mit Parametern (Prepared Statement):&amp;#039;&amp;#039;&amp;#039; Dies ist der Standardfall, wenn Benutzereingaben oder andere Variablen die Abfrage beeinflussen (z.B. in &amp;lt;code&amp;gt;WHERE&amp;lt;/code&amp;gt;- oder &amp;lt;code&amp;gt;LIMIT&amp;lt;/code&amp;gt;-Klauseln).&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Vorbereiten (&amp;lt;code&amp;gt;prepare&amp;lt;/code&amp;gt;):&amp;#039;&amp;#039;&amp;#039; Die SQL-Abfrage wird mit Platzhaltern (&amp;lt;code&amp;gt;?&amp;lt;/code&amp;gt; für positionale oder &amp;lt;code&amp;gt;:name&amp;lt;/code&amp;gt; für benannte Platzhalter) vorbereitet.[34, 33, 5]&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Beispiel: Benutzer mit bestimmter ID auswählen (positionaler Platzhalter)&lt;br /&gt;
$sql = &amp;quot;SELECT id, username, email FROM users WHERE id =?&amp;quot;;&lt;br /&gt;
$stmt = $pdo-&amp;amp;gt;prepare($sql);&lt;br /&gt;
&lt;br /&gt;
// Beispiel: Benutzer mit bestimmtem Status auswählen (benannter Platzhalter)&lt;br /&gt;
$sql_named = &amp;quot;SELECT id, username FROM users WHERE status = :status&amp;quot;;&lt;br /&gt;
$stmt_named = $pdo-&amp;amp;gt;prepare($sql_named);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
*# &amp;#039;&amp;#039;&amp;#039;Ausführen (&amp;lt;code&amp;gt;execute&amp;lt;/code&amp;gt;):&amp;#039;&amp;#039;&amp;#039; Die vorbereitete Anweisung wird mit den tatsächlichen Werten ausgeführt. Die Werte werden als Array an die &amp;lt;code&amp;gt;execute()&amp;lt;/code&amp;gt;-Methode übergeben. Bei positionalen Platzhaltern entspricht die Reihenfolge im Array der Reihenfolge der &amp;lt;code&amp;gt;?&amp;lt;/code&amp;gt; in der SQL-Abfrage. Bei benannten Platzhaltern wird ein assoziatives Array verwendet, dessen Schlüssel den Platzhalternamen entsprechen (mit oder ohne führenden Doppelpunkt).[34, 33, 5]&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Ausführen für positionalen Platzhalter&lt;br /&gt;
$user_id = 123;&lt;br /&gt;
$stmt-&amp;amp;gt;execute([$user_id]);&lt;br /&gt;
&lt;br /&gt;
// Ausführen für benannten Platzhalter&lt;br /&gt;
$user_status = &amp;#039;active&amp;#039;;&lt;br /&gt;
$stmt_named-&amp;amp;gt;execute([&amp;#039;:status&amp;#039; =&amp;amp;gt; $user_status]);&lt;br /&gt;
// oder: $stmt_named-&amp;amp;gt;execute([&amp;#039;status&amp;#039; =&amp;amp;gt; $user_status]);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
*# &amp;#039;&amp;#039;&amp;#039;Alternative Bindung:&amp;#039;&amp;#039;&amp;#039; Optional können Parameter auch explizit vor dem &amp;lt;code&amp;gt;execute()&amp;lt;/code&amp;gt;-Aufruf mit &amp;lt;code&amp;gt;bindValue()&amp;lt;/code&amp;gt; (bindet einen Wert) oder &amp;lt;code&amp;gt;bindParam()&amp;lt;/code&amp;gt; (bindet eine Variable als Referenz) gebunden werden. Dies gibt mehr Kontrolle über den Datentyp (z.B. &amp;lt;code&amp;gt;PDO::PARAM_INT&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;PDO::PARAM_STR&amp;lt;/code&amp;gt;).[5, 13]&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$stmt-&amp;amp;gt;bindValue(1, $user_id, PDO::PARAM_INT); // Position 1, Wert von $user_id als Integer&lt;br /&gt;
$stmt-&amp;amp;gt;execute();&lt;br /&gt;
&lt;br /&gt;
$stmt_named-&amp;amp;gt;bindParam(&amp;#039;:status&amp;#039;, $user_status, PDO::PARAM_STR); // Platzhalter :status, Variable $user_status als String&lt;br /&gt;
$stmt_named-&amp;amp;gt;execute();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;MySQLi:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Ohne Parameter:&amp;#039;&amp;#039;&amp;#039; Ähnlich wie bei PDO kann bei Abfragen ohne variable Teile die &amp;lt;code&amp;gt;query()&amp;lt;/code&amp;gt;-Methode (OO-Stil: &amp;lt;code&amp;gt;$mysqli-&amp;amp;gt;query()&amp;lt;/code&amp;gt;) oder die &amp;lt;code&amp;gt;mysqli_query()&amp;lt;/code&amp;gt;-Funktion (prozeduraler Stil: &amp;lt;code&amp;gt;mysqli_query($link, &amp;quot;...&amp;quot;)&amp;lt;/code&amp;gt;) verwendet werden. Sie geben ein &amp;lt;code&amp;gt;mysqli_result&amp;lt;/code&amp;gt;-Objekt zurück.[20, 25]&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// OO-Stil&lt;br /&gt;
$result = $mysqli-&amp;amp;gt;query(&amp;quot;SELECT id, username FROM users&amp;quot;);&lt;br /&gt;
// Prozedural&lt;br /&gt;
$result = mysqli_query($link, &amp;quot;SELECT id, username FROM users&amp;quot;);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Mit Parametern (Prepared Statement):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Vorbereiten (&amp;lt;code&amp;gt;prepare&amp;lt;/code&amp;gt;):&amp;#039;&amp;#039;&amp;#039; Die SQL-Abfrage wird mit &amp;lt;code&amp;gt;?&amp;lt;/code&amp;gt;-Platzhaltern vorbereitet. Im OO-Stil &amp;lt;code&amp;gt;$stmt = $mysqli-&amp;amp;gt;prepare(&amp;quot;...&amp;quot;)&amp;lt;/code&amp;gt;, prozedural &amp;lt;code&amp;gt;$stmt = mysqli_prepare($link, &amp;quot;...&amp;quot;)&amp;lt;/code&amp;gt;.[25, 35]&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// OO-Stil&lt;br /&gt;
$sql = &amp;quot;SELECT id, username, email FROM users WHERE id =?&amp;quot;;&lt;br /&gt;
$stmt = $mysqli-&amp;amp;gt;prepare($sql);&lt;br /&gt;
// Prozedural&lt;br /&gt;
$sql_proc = &amp;quot;SELECT id, username, email FROM users WHERE id =?&amp;quot;;&lt;br /&gt;
$stmt_proc = mysqli_prepare($link, $sql_proc);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
**# &amp;#039;&amp;#039;&amp;#039;Parameter binden (&amp;lt;code&amp;gt;bind_param&amp;lt;/code&amp;gt;):&amp;#039;&amp;#039;&amp;#039; Die Variablen werden an die Platzhalter gebunden. Dies ist ein wesentlicher Unterschied zu PDOs &amp;lt;code&amp;gt;execute&amp;lt;/code&amp;gt;-Array-Bindung. &amp;lt;code&amp;gt;bind_param()&amp;lt;/code&amp;gt; erfordert einen String, der die Datentypen der zu bindenden Variablen angibt (z.B. &amp;quot;i&amp;quot; für Integer, &amp;quot;s&amp;quot; für String, &amp;quot;d&amp;quot; für Double/Float, &amp;quot;b&amp;quot; für Blob), gefolgt von den Variablen selbst.[25, 35]&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$user_id = 123;&lt;br /&gt;
// OO-Stil&lt;br /&gt;
$stmt-&amp;amp;gt;bind_param(&amp;quot;i&amp;quot;, $user_id); // &amp;quot;i&amp;quot; für Integer&lt;br /&gt;
// Prozedural&lt;br /&gt;
mysqli_stmt_bind_param($stmt_proc, &amp;quot;i&amp;quot;, $user_id);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
**# &amp;#039;&amp;#039;&amp;#039;Ausführen (&amp;lt;code&amp;gt;execute&amp;lt;/code&amp;gt;):&amp;#039;&amp;#039;&amp;#039; Die vorbereitete Anweisung wird ausgeführt. OO-Stil: &amp;lt;code&amp;gt;$stmt-&amp;amp;gt;execute()&amp;lt;/code&amp;gt;, prozedural: &amp;lt;code&amp;gt;mysqli_stmt_execute($stmt)&amp;lt;/code&amp;gt;.[25, 36, 35]&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// OO-Stil&lt;br /&gt;
$stmt-&amp;amp;gt;execute();&lt;br /&gt;
// Prozedural&lt;br /&gt;
mysqli_stmt_execute($stmt_proc);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
**# &amp;#039;&amp;#039;&amp;#039;Ergebnis holen:&amp;#039;&amp;#039;&amp;#039; Um die Ergebnisse eines Prepared Statements in MySQLi zu erhalten, ist die Methode &amp;lt;code&amp;gt;get_result()&amp;lt;/code&amp;gt; (OO) bzw. &amp;lt;code&amp;gt;mysqli_stmt_get_result()&amp;lt;/code&amp;gt; (prozedural) am bequemsten. Sie gibt ein Standard-&amp;lt;code&amp;gt;mysqli_result&amp;lt;/code&amp;gt;-Objekt zurück, das dann mit den üblichen Fetch-Methoden verarbeitet werden kann. &amp;#039;&amp;#039;&amp;#039;Wichtig:&amp;#039;&amp;#039;&amp;#039; Diese Methode erfordert den &amp;lt;code&amp;gt;mysqlnd&amp;lt;/code&amp;gt;-Treiber.[25, 15]&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// OO-Stil&lt;br /&gt;
$result = $stmt-&amp;amp;gt;get_result();&lt;br /&gt;
// Prozedural&lt;br /&gt;
$result_proc = mysqli_stmt_get_result($stmt_proc);&lt;br /&gt;
// $result / $result_proc kann nun mit fetch_assoc() etc. verwendet werden.&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Die Alternative ohne &amp;lt;code&amp;gt;mysqlnd&amp;lt;/code&amp;gt; ist &amp;lt;code&amp;gt;bind_result()&amp;lt;/code&amp;gt;, bei der jede Ergebnisspalte an eine PHP-Variable gebunden wird, und &amp;lt;code&amp;gt;fetch()&amp;lt;/code&amp;gt; iterativ aufgerufen wird, um die Variablen zu füllen.[25] Dies ist deutlich umständlicher.&lt;br /&gt;
&lt;br /&gt;
=== Ergebnisse abrufen und verarbeiten ===&lt;br /&gt;
&lt;br /&gt;
Nachdem eine &amp;lt;code&amp;gt;SELECT&amp;lt;/code&amp;gt;-Abfrage erfolgreich ausgeführt wurde, müssen die zurückgegebenen Daten aus dem Ergebnisobjekt (&amp;lt;code&amp;gt;PDOStatement&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;mysqli_result&amp;lt;/code&amp;gt;) abgerufen (&amp;quot;gefetched&amp;quot;) werden.&lt;br /&gt;
&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Zeilenweise Iteration (&amp;lt;code&amp;gt;fetch&amp;lt;/code&amp;gt; im Loop):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
Dies ist der klassische Weg, um Ergebnisse zu verarbeiten, insbesondere wenn jede Zeile einzeln behandelt werden muss oder wenn die Ergebnismenge sehr groß ist.&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;PDO:&amp;#039;&amp;#039;&amp;#039; Die &amp;lt;code&amp;gt;fetch()&amp;lt;/code&amp;gt;-Methode des &amp;lt;code&amp;gt;PDOStatement&amp;lt;/code&amp;gt;-Objekts wird wiederholt in einer &amp;lt;code&amp;gt;while&amp;lt;/code&amp;gt;-Schleife aufgerufen. &amp;lt;code&amp;gt;fetch()&amp;lt;/code&amp;gt; gibt bei jedem Aufruf die nächste Zeile zurück (im Format des eingestellten Fetch-Modus, z.B. &amp;lt;code&amp;gt;PDO::FETCH_ASSOC&amp;lt;/code&amp;gt;) und &amp;lt;code&amp;gt;false&amp;lt;/code&amp;gt; (oder &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt; je nach Modus/Version), wenn keine weiteren Zeilen vorhanden sind.[2, 33]&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$stmt = $pdo-&amp;amp;gt;prepare(&amp;quot;SELECT name, email FROM users WHERE status =?&amp;quot;);&lt;br /&gt;
$stmt-&amp;amp;gt;execute([&amp;#039;active&amp;#039;]);&lt;br /&gt;
while ($row = $stmt-&amp;amp;gt;fetch(PDO::FETCH_ASSOC)) {&lt;br /&gt;
 echo &amp;quot;Name: &amp;quot;. htmlspecialchars($row[&amp;#039;name&amp;#039;]). &amp;quot;, E-Mail: &amp;quot;. htmlspecialchars($row[&amp;#039;email&amp;#039;]). &amp;quot;&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;MySQLi:&amp;#039;&amp;#039;&amp;#039; Die &amp;lt;code&amp;gt;fetch_assoc()&amp;lt;/code&amp;gt;-Methode (oder &amp;lt;code&amp;gt;fetch_row()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;fetch_object()&amp;lt;/code&amp;gt;, etc.) des &amp;lt;code&amp;gt;mysqli_result&amp;lt;/code&amp;gt;-Objekts wird in einer &amp;lt;code&amp;gt;while&amp;lt;/code&amp;gt;-Schleife verwendet. Sie gibt die nächste Zeile als Array (oder Objekt) zurück oder &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt;, wenn das Ende erreicht ist.[25, 15]&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// OO-Stil (nach $result = $stmt-&amp;amp;gt;get_result();)&lt;br /&gt;
while ($row = $result-&amp;amp;gt;fetch_assoc()) {&lt;br /&gt;
 echo &amp;quot;Name: &amp;quot;. htmlspecialchars($row[&amp;#039;name&amp;#039;]). &amp;quot;, E-Mail: &amp;quot;. htmlspecialchars($row[&amp;#039;email&amp;#039;]). &amp;quot;&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Prozedural (nach $result = mysqli_stmt_get_result($stmt);)&lt;br /&gt;
while ($row = mysqli_fetch_assoc($result)) {&lt;br /&gt;
 echo &amp;quot;Name: &amp;quot;. htmlspecialchars($row[&amp;#039;name&amp;#039;]). &amp;quot;, E-Mail: &amp;quot;. htmlspecialchars($row[&amp;#039;email&amp;#039;]). &amp;quot;&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Dieser Ansatz ist &amp;#039;&amp;#039;&amp;#039;speichereffizient&amp;#039;&amp;#039;&amp;#039;, da immer nur eine Zeile gleichzeitig im PHP-Speicher gehalten wird. Dies ist besonders wichtig bei Abfragen, die potenziell Tausende oder Millionen von Zeilen zurückgeben könnten.[37]&lt;br /&gt;
&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Alle Ergebnisse auf einmal abrufen (&amp;lt;code&amp;gt;fetchAll&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;fetch_all&amp;lt;/code&amp;gt;):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
Für kleinere bis mittlere Ergebnismengen ist es oft bequemer, alle Zeilen auf einmal in ein PHP-Array zu laden.&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;PDO:&amp;#039;&amp;#039;&amp;#039; Die &amp;lt;code&amp;gt;fetchAll()&amp;lt;/code&amp;gt;-Methode des &amp;lt;code&amp;gt;PDOStatement&amp;lt;/code&amp;gt;-Objekts gibt ein Array zurück, das alle Ergebniszeilen enthält.[33, 38] Der Fetch-Modus (z.B. &amp;lt;code&amp;gt;PDO::FETCH_ASSOC&amp;lt;/code&amp;gt;) kann als Argument übergeben werden.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$stmt = $pdo-&amp;amp;gt;prepare(&amp;quot;SELECT name, email FROM users WHERE status =?&amp;quot;);&lt;br /&gt;
$stmt-&amp;amp;gt;execute([&amp;#039;active&amp;#039;]);&lt;br /&gt;
$results = $stmt-&amp;amp;gt;fetchAll(PDO::FETCH_ASSOC);&lt;br /&gt;
// $results ist nun ein Array von assoziativen Arrays&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;MySQLi:&amp;#039;&amp;#039;&amp;#039; Die &amp;lt;code&amp;gt;fetch_all()&amp;lt;/code&amp;gt;-Methode des &amp;lt;code&amp;gt;mysqli_result&amp;lt;/code&amp;gt;-Objekts (verfügbar mit &amp;lt;code&amp;gt;mysqlnd&amp;lt;/code&amp;gt;) tut dasselbe. Der Modus (&amp;lt;code&amp;gt;MYSQLI_ASSOC&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;MYSQLI_NUM&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;MYSQLI_BOTH&amp;lt;/code&amp;gt;) wird als Argument übergeben.[25, 39]&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// OO-Stil (nach $result = $stmt-&amp;amp;gt;get_result();)&lt;br /&gt;
$results = $result-&amp;amp;gt;fetch_all(MYSQLI_ASSOC);&lt;br /&gt;
&lt;br /&gt;
// Prozedural (nach $result = mysqli_stmt_get_result($stmt);)&lt;br /&gt;
$results = mysqli_fetch_all($result, MYSQLI_ASSOC);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Der &amp;#039;&amp;#039;&amp;#039;Vorteil&amp;#039;&amp;#039;&amp;#039; dieser Methode liegt in der einfacheren Weiterverarbeitung des Ergebnis-Arrays mit Standard-PHP-Array-Funktionen wie &amp;lt;code&amp;gt;foreach&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;count&amp;lt;/code&amp;gt;.[40] Der &amp;#039;&amp;#039;&amp;#039;Nachteil&amp;#039;&amp;#039;&amp;#039; ist der potenziell hohe Speicherverbrauch, da alle Daten gleichzeitig in den PHP-Speicher geladen werden. Bei sehr großen Ergebnismengen kann dies zu Speicherlimit-Fehlern führen.[37]&lt;br /&gt;
&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Fetch-Modi / Fetch-Funktionen (Struktur der Ergebniszeile):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
Beide Erweiterungen bieten verschiedene Möglichkeiten, die Struktur der abgerufenen Datenzeilen zu bestimmen:&lt;br /&gt;
&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;PDO Fetch-Modi (Auswahl):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
***** &amp;lt;code&amp;gt;PDO::FETCH_ASSOC&amp;lt;/code&amp;gt;: Gibt ein assoziatives Array zurück (&amp;lt;code&amp;gt;[&amp;#039;spaltenname&amp;#039; =&amp;amp;gt; &amp;#039;wert&amp;#039;,... ]&amp;lt;/code&amp;gt;).[9, 33, 41, 42] Sehr gebräuchlich.&lt;br /&gt;
***** &amp;lt;code&amp;gt;PDO::FETCH_NUM&amp;lt;/code&amp;gt;: Gibt ein numerisch indiziertes Array zurück (&amp;lt;code&amp;gt;[0 =&amp;amp;gt; &amp;#039;wert1&amp;#039;, 1 =&amp;amp;gt; &amp;#039;wert2&amp;#039;,...]&amp;lt;/code&amp;gt;).[9, 41, 42]&lt;br /&gt;
***** &amp;lt;code&amp;gt;PDO::FETCH_BOTH&amp;lt;/code&amp;gt; (Standard): Gibt ein Array zurück, das sowohl assoziative als auch numerische Indizes enthält.[41, 42] Verbraucht mehr Speicher.&lt;br /&gt;
***** &amp;lt;code&amp;gt;PDO::FETCH_OBJ&amp;lt;/code&amp;gt;: Gibt ein anonymes &amp;lt;code&amp;gt;stdClass&amp;lt;/code&amp;gt;-Objekt zurück, bei dem die Spaltennamen Eigenschaften sind (&amp;lt;code&amp;gt;$row-&amp;amp;gt;spaltenname&amp;lt;/code&amp;gt;).[9, 41, 42, 43]&lt;br /&gt;
***** &amp;lt;code&amp;gt;PDO::FETCH_CLASS&amp;lt;/code&amp;gt;: Erstellt eine Instanz einer angegebenen Klasse und weist die Spaltenwerte den Klasseneigenschaften zu.[38, 41, 42, 43]&lt;br /&gt;
***** &amp;lt;code&amp;gt;PDO::FETCH_COLUMN&amp;lt;/code&amp;gt;: Ruft nur den Wert einer einzelnen Spalte aus der nächsten Zeile ab.[38]&lt;br /&gt;
***** &amp;lt;code&amp;gt;PDO::FETCH_LAZY&amp;lt;/code&amp;gt;: Eine Kombination aus &amp;lt;code&amp;gt;FETCH_BOTH&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;FETCH_OBJ&amp;lt;/code&amp;gt;, die Werte erst bei Zugriff lädt.[9, 41, 42]&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;MySQLi Fetch-Funktionen (Auswahl):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
***** &amp;lt;code&amp;gt;fetch_assoc()&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;mysqli_fetch_assoc()&amp;lt;/code&amp;gt;: Gibt ein assoziatives Array zurück.[17, 25, 39, 15] Sehr gebräuchlich.&lt;br /&gt;
***** &amp;lt;code&amp;gt;fetch_row()&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;mysqli_fetch_row()&amp;lt;/code&amp;gt;: Gibt ein numerisch indiziertes Array zurück.[39, 35]&lt;br /&gt;
***** &amp;lt;code&amp;gt;fetch_array()&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;mysqli_fetch_array()&amp;lt;/code&amp;gt;: Gibt je nach übergebenem Modus (&amp;lt;code&amp;gt;MYSQLI_ASSOC&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;MYSQLI_NUM&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;MYSQLI_BOTH&amp;lt;/code&amp;gt;) ein assoziatives, numerisches oder beides enthaltendes Array zurück.[39, 35]&lt;br /&gt;
***** &amp;lt;code&amp;gt;fetch_object()&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;mysqli_fetch_object()&amp;lt;/code&amp;gt;: Gibt ein &amp;lt;code&amp;gt;stdClass&amp;lt;/code&amp;gt;-Objekt zurück oder eine Instanz einer angegebenen Klasse.[21, 15, 35]&lt;br /&gt;
***** &amp;lt;code&amp;gt;fetch_column()&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;mysqli_fetch_column()&amp;lt;/code&amp;gt;: Ruft den Wert einer einzelnen Spalte ab (erst ab PHP 8.1 verfügbar).[24]&lt;br /&gt;
***** &amp;lt;code&amp;gt;fetch_all()&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;mysqli_fetch_all()&amp;lt;/code&amp;gt;: Ruft alle Zeilen auf einmal ab, Modus (&amp;lt;code&amp;gt;MYSQLI_ASSOC&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;MYSQLI_NUM&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;MYSQLI_BOTH&amp;lt;/code&amp;gt;) wählbar.[21, 25, 39]&lt;br /&gt;
&lt;br /&gt;
=== Tabelle 2: Ausgewählte PDO Fetch-Modi ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Konstante !! Rückgabeformat !! Beschreibung&lt;br /&gt;
|- &lt;br /&gt;
| &amp;lt;code&amp;gt;PDO::FETCH_ASSOC&amp;lt;/code&amp;gt;&lt;br /&gt;
| assoziatives=&amp;quot;&amp;quot; array=&amp;quot;&amp;quot; mwt-preservehtml=&amp;quot;&amp;quot; | [&amp;quot; col&amp;#039;=&amp;quot;&amp;amp;gt;&amp;quot; &amp;#039;val&amp;#039;]&amp;amp;lt;=&amp;quot;&amp;quot; code&amp;amp;gt;&amp;#039;)=&amp;quot;&amp;quot; &amp;amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
| Gängigste Form, Spaltennamen als Schlüssel.&lt;br /&gt;
|- &lt;br /&gt;
| mwt-preservehtml=&amp;quot;&amp;quot; pdo::fetch_num=&amp;quot;&amp;quot; code=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| numerisches=&amp;quot;&amp;quot; array=&amp;quot;&amp;quot; mwt-preservehtml=&amp;quot;&amp;quot; 0=&amp;quot;&amp;quot; val=&amp;quot;&amp;quot; code=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Spaltenwerte über numerischen Index erreichbar.&lt;br /&gt;
|- &lt;br /&gt;
| mwt-preservehtml=&amp;quot;&amp;quot; pdo::fetch_both=&amp;quot;&amp;quot; code=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| array=&amp;quot;&amp;quot; mit=&amp;quot;&amp;quot; assoz=&amp;quot;&amp;quot; num=&amp;quot;&amp;quot; indizes=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Standard, enthält doppelte Informationen, höherer Speicherbedarf.&lt;br /&gt;
|- &lt;br /&gt;
| mwt-preservehtml=&amp;quot;&amp;quot; pdo::fetch_obj=&amp;quot;&amp;quot; code=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| anonymes=&amp;quot;&amp;quot; mwt-preservehtml=&amp;quot;&amp;quot; stdclass=&amp;quot;&amp;quot; code=&amp;quot;&amp;quot; -objekt=&amp;quot;&amp;quot; row-=&amp;quot;&amp;quot; col=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Spaltennamen werden zu Objekteigenschaften.&lt;br /&gt;
|- &lt;br /&gt;
| &amp;lt;code&amp;gt;PDO::FETCH_CLASS&amp;lt;/code&amp;gt; || objekt=&amp;quot;&amp;quot; einer=&amp;quot;&amp;quot; spezifischen=&amp;quot;&amp;quot; klasse=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Mappt Spalten auf Eigenschaften einer vordefinierten Klasse.&lt;br /&gt;
|- &lt;br /&gt;
| &amp;lt;code&amp;gt;PDO::FETCH_COLUMN&amp;lt;/code&amp;gt; || einzelner=&amp;quot;&amp;quot; wert=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Gibt nur den Wert der ersten (oder angegebenen) Spalte der nächsten Zeile zurück.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Tabelle 3: Ausgewählte MySQLi Fetch-Funktionen ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Funktion (OO / Prozedural) !! Rückgabeformat !! Beschreibung&lt;br /&gt;
|- &lt;br /&gt;
| mwt-preservehtml=&amp;quot;&amp;quot; fetch_assoc=&amp;quot;&amp;quot; code=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| assoziatives=&amp;quot;&amp;quot; array=&amp;quot;&amp;quot; mwt-preservehtml=&amp;quot;&amp;quot; col=&amp;quot;&amp;quot; val=&amp;quot;&amp;quot; code=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Gängigste Form, Spaltennamen als Schlüssel.&lt;br /&gt;
|- &lt;br /&gt;
| mwt-preservehtml=&amp;quot;&amp;quot; fetch_row=&amp;quot;&amp;quot; code=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| numerisches=&amp;quot;&amp;quot; array=&amp;quot;&amp;quot; mwt-preservehtml=&amp;quot;&amp;quot; 0=&amp;quot;&amp;quot; val=&amp;quot;&amp;quot; code=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Spaltenwerte über numerischen Index erreichbar.&lt;br /&gt;
|- &lt;br /&gt;
| mwt-preservehtml=&amp;quot;&amp;quot; fetch_array=&amp;quot;&amp;quot; code=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| array=&amp;quot;&amp;quot; assoz=&amp;quot;&amp;quot; num=&amp;quot;&amp;quot; oder=&amp;quot;&amp;quot; beide=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Flexibel je nach Modus (&amp;lt;code&amp;gt;MYSQLI_ASSOC&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;MYSQLI_NUM&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;MYSQLI_BOTH&amp;lt;/code&amp;gt;).&lt;br /&gt;
|- &lt;br /&gt;
| &amp;lt;code&amp;gt;fetch_object()&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;mysqli_fetch_object()&amp;lt;/code&amp;gt;&lt;br /&gt;
| mwt-preservehtml=&amp;quot;&amp;quot; | stdClass&amp;quot; -objekt=&amp;quot;&amp;quot; oder=&amp;quot;&amp;quot; spezifische=&amp;quot;&amp;quot; klasse=&amp;quot;&amp;quot; &amp;amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
| Spaltennamen werden zu Objekteigenschaften.&lt;br /&gt;
|- &lt;br /&gt;
| mwt-preservehtml=&amp;quot;&amp;quot; fetch_all=&amp;quot;&amp;quot; code=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| array=&amp;quot;&amp;quot; von=&amp;quot;&amp;quot; arrays=&amp;quot;&amp;quot; assoz=&amp;quot;&amp;quot; num=&amp;quot;&amp;quot; oder=&amp;quot;&amp;quot; beide=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Holt alle Ergebniszeilen auf einmal (benötigt &amp;lt;code&amp;gt;mysqlnd&amp;lt;/code&amp;gt;).&lt;br /&gt;
|- &lt;br /&gt;
| &amp;lt;code&amp;gt;fetch_column()&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;mysqli_fetch_column()&amp;lt;/code&amp;gt; || einzelner=&amp;quot;&amp;quot; wert=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Gibt nur den Wert der ersten (oder angegebenen) Spalte zurück (PHP 8.1+).&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Zugriff auf Spaltenwerte:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
Der Zugriff auf die Daten innerhalb einer abgerufenen Zeile hängt vom gewählten Fetch-Modus ab:&lt;br /&gt;
**** Assoziatives Array: &amp;lt;code&amp;gt;$row[&amp;#039;spaltenname&amp;#039;]&amp;lt;/code&amp;gt;&lt;br /&gt;
**** Numerisches Array: &amp;lt;code&amp;gt;$row&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;$row[1]&amp;lt;/code&amp;gt;,...&lt;br /&gt;
**** Objekt: &amp;lt;code&amp;gt;$row-&amp;amp;gt;spaltenname&amp;lt;/code&amp;gt;&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Iteration mit &amp;lt;code&amp;gt;foreach&amp;lt;/code&amp;gt;:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
Nachdem Ergebnisse mit &amp;lt;code&amp;gt;fetchAll()&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;fetch_all()&amp;lt;/code&amp;gt; in ein Array geladen wurden, ist &amp;lt;code&amp;gt;foreach&amp;lt;/code&amp;gt; die natürliche Wahl zur Iteration [37, 40, 44]:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$results = $stmt-&amp;amp;gt;fetchAll(PDO::FETCH_ASSOC);&lt;br /&gt;
foreach ($results as $row) {&lt;br /&gt;
 echo &amp;quot;Name: &amp;quot;. htmlspecialchars($row[&amp;#039;name&amp;#039;]). &amp;quot;&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Seit PHP 8 bietet PDO eine modernere Alternative, die die Lesbarkeit von &amp;lt;code&amp;gt;foreach&amp;lt;/code&amp;gt; mit der Speichereffizienz von &amp;lt;code&amp;gt;fetch()&amp;lt;/code&amp;gt; kombiniert, indem direkt über das &amp;lt;code&amp;gt;PDOStatement&amp;lt;/code&amp;gt;-Objekt iteriert wird [37, 45]:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Nur PHP 8+ mit PDO&lt;br /&gt;
$stmt = $pdo-&amp;amp;gt;query(&amp;quot;SELECT name, email FROM users&amp;quot;);&lt;br /&gt;
foreach ($stmt as $row) { // PDOStatement ist direkt traversierbar&lt;br /&gt;
 echo &amp;quot;Name: &amp;quot;. htmlspecialchars($row[&amp;#039;name&amp;#039;]). &amp;quot;&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Diese Methode ist besonders elegant und effizient für große Ergebnismengen in modernen PHP-Versionen.&lt;br /&gt;
&lt;br /&gt;
=== Daten manipulieren (INSERT, UPDATE, DELETE) ===&lt;br /&gt;
&lt;br /&gt;
Das Einfügen, Aktualisieren und Löschen von Daten sind kritische Operationen, da sie den Datenbestand verändern. Hier ist die Verwendung von &amp;#039;&amp;#039;&amp;#039;Prepared Statements zwingend erforderlich&amp;#039;&amp;#039;&amp;#039;, um die Integrität und Sicherheit der Datenbank zu gewährleisten, insbesondere wenn Benutzereingaben beteiligt sind.&lt;br /&gt;
&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;PDO:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;Vorbereiten (&amp;lt;code&amp;gt;prepare&amp;lt;/code&amp;gt;):&amp;#039;&amp;#039;&amp;#039; Die SQL-Anweisung (&amp;lt;code&amp;gt;INSERT&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;UPDATE&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;DELETE&amp;lt;/code&amp;gt;) wird mit Platzhaltern (&amp;lt;code&amp;gt;?&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;:name&amp;lt;/code&amp;gt;) für die einzufügenden/zu aktualisierenden Werte sowie für die Kriterien in &amp;lt;code&amp;gt;WHERE&amp;lt;/code&amp;gt;-Klauseln (bei &amp;lt;code&amp;gt;UPDATE&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;DELETE&amp;lt;/code&amp;gt;) vorbereitet.[5, 46, 47, 48]&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;Ausführen (&amp;lt;code&amp;gt;execute&amp;lt;/code&amp;gt;):&amp;#039;&amp;#039;&amp;#039; Die Anweisung wird mit einem Array ausgeführt, das die Werte für die Platzhalter enthält.[5, 46, 47, 48]&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;Ergebnis prüfen:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
***** &amp;#039;&amp;#039;Anzahl betroffener Zeilen:&amp;#039;&amp;#039; &amp;lt;code&amp;gt;$stmt-&amp;amp;gt;rowCount()&amp;lt;/code&amp;gt; gibt die Anzahl der durch &amp;lt;code&amp;gt;UPDATE&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;DELETE&amp;lt;/code&amp;gt; betroffenen Zeilen zurück. Bei &amp;lt;code&amp;gt;INSERT&amp;lt;/code&amp;gt; ist der Rückgabewert oft nicht zuverlässig oder standardisiert.[8, 9, 5, 49] Das Verhalten kann je nach Datenbanktreiber und Konfiguration (z.B. &amp;lt;code&amp;gt;PDO::MYSQL_ATTR_FOUND_ROWS&amp;lt;/code&amp;gt;) variieren.[9]&lt;br /&gt;
***** &amp;#039;&amp;#039;ID des letzten eingefügten Datensatzes:&amp;#039;&amp;#039; Bei &amp;lt;code&amp;gt;INSERT&amp;lt;/code&amp;gt;-Anweisungen in Tabellen mit einer &amp;lt;code&amp;gt;AUTO_INCREMENT&amp;lt;/code&amp;gt;-Spalte liefert &amp;lt;code&amp;gt;$pdo-&amp;amp;gt;lastInsertId()&amp;lt;/code&amp;gt; die ID des neu eingefügten Datensatzes zurück.[8, 33, 48]&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;Code-Beispiele (PDO):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!--?php// --- INSERT ---&lt;br /&gt;
$name = $_POST[&amp;#039;name&amp;#039;];&lt;br /&gt;
$email = $_POST[&amp;#039;email&amp;#039;];&lt;br /&gt;
$status = &amp;#039;pending&amp;#039;;&lt;br /&gt;
&lt;br /&gt;
$sql_insert = &amp;quot;INSERT INTO users (username, email, status) VALUES (:name, :email, :status)&amp;quot;;&lt;br /&gt;
$stmt_insert = $pdo---&amp;gt;prepare($sql_insert);&lt;br /&gt;
$stmt_insert-&amp;amp;gt;execute([&amp;#039;:name&amp;#039; =&amp;amp;gt; $name, &amp;#039;:email&amp;#039; =&amp;amp;gt; $email, &amp;#039;:status&amp;#039; =&amp;amp;gt; $status]);&lt;br /&gt;
$lastId = $pdo-&amp;amp;gt;lastInsertId();&lt;br /&gt;
echo &amp;quot;Neuer Benutzer mit ID $lastId eingefügt.&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
// --- UPDATE ---&lt;br /&gt;
$new_status = &amp;#039;active&amp;#039;;&lt;br /&gt;
$user_id_to_update = $lastId;&lt;br /&gt;
&lt;br /&gt;
$sql_update = &amp;quot;UPDATE users SET status = :status WHERE id = :id&amp;quot;;&lt;br /&gt;
$stmt_update = $pdo-&amp;amp;gt;prepare($sql_update);&lt;br /&gt;
$stmt_update-&amp;amp;gt;execute([&amp;#039;:status&amp;#039; =&amp;amp;gt; $new_status, &amp;#039;:id&amp;#039; =&amp;amp;gt; $user_id_to_update]);&lt;br /&gt;
$affectedRows = $stmt_update-&amp;amp;gt;rowCount();&lt;br /&gt;
echo &amp;quot;$affectedRows Benutzerstatus aktualisiert.&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
// --- DELETE ---&lt;br /&gt;
$user_id_to_delete = $lastId;&lt;br /&gt;
&lt;br /&gt;
$sql_delete = &amp;quot;DELETE FROM users WHERE id = :id&amp;quot;;&lt;br /&gt;
$stmt_delete = $pdo-&amp;amp;gt;prepare($sql_delete);&lt;br /&gt;
$stmt_delete-&amp;amp;gt;execute([&amp;#039;:id&amp;#039; =&amp;amp;gt; $user_id_to_delete]);&lt;br /&gt;
$affectedRowsDel = $stmt_delete-&amp;amp;gt;rowCount();&lt;br /&gt;
echo &amp;quot;$affectedRowsDel Benutzer gelöscht.&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
?&amp;amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;MySQLi:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;Vorbereiten (&amp;lt;code&amp;gt;prepare&amp;lt;/code&amp;gt;):&amp;#039;&amp;#039;&amp;#039; Die SQL-Anweisung wird mit &amp;lt;code&amp;gt;?&amp;lt;/code&amp;gt;-Platzhaltern vorbereitet (OO: &amp;lt;code&amp;gt;$mysqli-&amp;amp;gt;prepare()&amp;lt;/code&amp;gt;, Proc: &amp;lt;code&amp;gt;mysqli_prepare()&amp;lt;/code&amp;gt;).[25, 50, 15, 35]&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;Parameter binden (&amp;lt;code&amp;gt;bind_param&amp;lt;/code&amp;gt;):&amp;#039;&amp;#039;&amp;#039; Variablen werden explizit mit Typenangabe gebunden (OO: &amp;lt;code&amp;gt;$stmt-&amp;amp;gt;bind_param()&amp;lt;/code&amp;gt;, Proc: &amp;lt;code&amp;gt;mysqli_stmt_bind_param()&amp;lt;/code&amp;gt;).[25, 50, 15, 35] Die Notwendigkeit, den Datentyp für jede Variable anzugeben, ist ein wesentlicher Unterschied zur einfacheren Array-Übergabe bei PDOs &amp;lt;code&amp;gt;execute()&amp;lt;/code&amp;gt;.&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;Ausführen (&amp;lt;code&amp;gt;execute&amp;lt;/code&amp;gt;):&amp;#039;&amp;#039;&amp;#039; Die Anweisung wird ausgeführt (OO: &amp;lt;code&amp;gt;$stmt-&amp;amp;gt;execute()&amp;lt;/code&amp;gt;, Proc: &amp;lt;code&amp;gt;mysqli_stmt_execute()&amp;lt;/code&amp;gt;).[25, 36, 50, 15, 35]&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;Ergebnis prüfen:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
***** &amp;#039;&amp;#039;Anzahl betroffener Zeilen:&amp;#039;&amp;#039; Über die Eigenschaft &amp;lt;code&amp;gt;$mysqli-&amp;amp;gt;affected_rows&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;$stmt-&amp;amp;gt;affected_rows&amp;lt;/code&amp;gt; (OO) bzw. die Funktionen &amp;lt;code&amp;gt;mysqli_affected_rows($link)&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;mysqli_stmt_affected_rows($stmt)&amp;lt;/code&amp;gt; (Proc).[18, 26, 15, 35]&lt;br /&gt;
***** &amp;#039;&amp;#039;ID des letzten eingefügten Datensatzes:&amp;#039;&amp;#039; Über &amp;lt;code&amp;gt;$mysqli-&amp;amp;gt;insert_id&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;$stmt-&amp;amp;gt;insert_id&amp;lt;/code&amp;gt; (OO) bzw. &amp;lt;code&amp;gt;mysqli_insert_id($link)&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;mysqli_stmt_insert_id($stmt)&amp;lt;/code&amp;gt; (Proc).[26, 15] Die Verwendung von &amp;lt;code&amp;gt;$mysqli-&amp;amp;gt;insert_id&amp;lt;/code&amp;gt; wird oft als zuverlässiger angesehen, da sie auch nach dem Schließen des Statements funktioniert.[15]&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;Code-Beispiele (MySQLi OO):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!--?php// --- INSERT ---&lt;br /&gt;
$name = $_POST[&amp;#039;name&amp;#039;];&lt;br /&gt;
$email = $_POST[&amp;#039;email&amp;#039;];&lt;br /&gt;
$status = &amp;#039;pending&amp;#039;;&lt;br /&gt;
&lt;br /&gt;
$sql_insert = &amp;quot;INSERT INTO users (username, email, status) VALUES (?,?,?)&amp;quot;;&lt;br /&gt;
$stmt_insert = $mysqli---&amp;gt;prepare($sql_insert);&lt;br /&gt;
// Typen: s=string, s=string, s=string&lt;br /&gt;
$stmt_insert-&amp;amp;gt;bind_param(&amp;quot;sss&amp;quot;, $name, $email, $status);&lt;br /&gt;
$stmt_insert-&amp;amp;gt;execute();&lt;br /&gt;
$lastId = $mysqli-&amp;amp;gt;insert_id;&lt;br /&gt;
echo &amp;quot;Neuer Benutzer mit ID $lastId eingefügt (MySQLi OO).&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
$stmt_insert-&amp;amp;gt;close(); // Statement schließen&lt;br /&gt;
&lt;br /&gt;
// --- UPDATE ---&lt;br /&gt;
$new_status = &amp;#039;active&amp;#039;;&lt;br /&gt;
$user_id_to_update = $lastId;&lt;br /&gt;
&lt;br /&gt;
$sql_update = &amp;quot;UPDATE users SET status =? WHERE id =?&amp;quot;;&lt;br /&gt;
$stmt_update = $mysqli-&amp;amp;gt;prepare($sql_update);&lt;br /&gt;
// Typen: s=string, i=integer&lt;br /&gt;
$stmt_update-&amp;amp;gt;bind_param(&amp;quot;si&amp;quot;, $new_status, $user_id_to_update);&lt;br /&gt;
$stmt_update-&amp;amp;gt;execute();&lt;br /&gt;
$affectedRows = $mysqli-&amp;amp;gt;affected_rows; // Oder $stmt_update-&amp;amp;gt;affected_rows vor close()&lt;br /&gt;
echo &amp;quot;$affectedRows Benutzerstatus aktualisiert (MySQLi OO).&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
$stmt_update-&amp;amp;gt;close();&lt;br /&gt;
&lt;br /&gt;
// --- DELETE ---&lt;br /&gt;
$user_id_to_delete = $lastId;&lt;br /&gt;
&lt;br /&gt;
$sql_delete = &amp;quot;DELETE FROM users WHERE id =?&amp;quot;;&lt;br /&gt;
$stmt_delete = $mysqli-&amp;amp;gt;prepare($sql_delete);&lt;br /&gt;
// Typ: i=integer&lt;br /&gt;
$stmt_delete-&amp;amp;gt;bind_param(&amp;quot;i&amp;quot;, $user_id_to_delete);&lt;br /&gt;
$stmt_delete-&amp;amp;gt;execute();&lt;br /&gt;
$affectedRowsDel = $mysqli-&amp;amp;gt;affected_rows;&lt;br /&gt;
echo &amp;quot;$affectedRowsDel Benutzer gelöscht (MySQLi OO).&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
$stmt_delete-&amp;amp;gt;close();&lt;br /&gt;
?&amp;amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;#039;&amp;#039;(Anmerkung: Prozedurale Beispiele folgen demselben Muster unter Verwendung der &amp;lt;code&amp;gt;mysqli_*&amp;lt;/code&amp;gt;-Funktionen wie &amp;lt;code&amp;gt;mysqli_prepare&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;mysqli_stmt_bind_param&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;mysqli_stmt_execute&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;mysqli_insert_id&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;mysqli_affected_rows&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;mysqli_stmt_close&amp;lt;/code&amp;gt;)&amp;#039;&amp;#039;.&lt;br /&gt;
&lt;br /&gt;
== Sicherheit: SQL Injection verstehen und verhindern ==&lt;br /&gt;
&lt;br /&gt;
Eines der größten Sicherheitsrisiken bei der Interaktion mit Datenbanken über Webanwendungen ist die SQL Injection (SQLi). Das Verständnis dieser Bedrohung und ihrer Abwehr ist für jeden PHP-Entwickler unerlässlich.&lt;br /&gt;
&lt;br /&gt;
=== SQL Injection ===&lt;br /&gt;
&lt;br /&gt;
SQL Injection ist eine Angriffstechnik, bei der ein Angreifer versucht, bösartigen oder manipulierten SQL-Code über Benutzereingabefelder einer Webanwendung (z.B. Formulare, URL-Parameter, HTTP-Header, Cookies) in die Datenbankabfragen einzuschleusen.[51, 52, 53, 54, 55, 56, 57]&lt;br /&gt;
&lt;br /&gt;
Die Attacke funktioniert, indem Schwachstellen in der Anwendung ausgenutzt werden, bei denen Benutzereingaben direkt und ohne ausreichende Validierung oder Maskierung (Escaping) in SQL-Abfragestrings eingefügt (konkateniert) werden.[5, 51, 55] Dadurch wird die notwendige Trennung zwischen dem eigentlichen SQL-Befehl (Kontrollebene) und den Daten (Datenebene) aufgehoben.[51] Der Angreifer kann spezielle Zeichen (wie Anführungszeichen &amp;lt;code&amp;gt;&amp;#039;&amp;lt;/code&amp;gt;, Semikolons &amp;lt;code&amp;gt;;&amp;lt;/code&amp;gt; oder Kommentare &amp;lt;code&amp;gt;--&amp;lt;/code&amp;gt;) verwenden, um den ursprünglichen SQL-Befehl vorzeitig zu beenden, zu verändern oder um eigene, zusätzliche SQL-Befehle anzuhängen.[51, 53]&lt;br /&gt;
&lt;br /&gt;
Ein klassisches Beispiel ist eine Login-Abfrage, die etwa so aufgebaut ist:&lt;br /&gt;
&amp;lt;code&amp;gt;$sql = &amp;quot;SELECT * FROM users WHERE username = &amp;#039;&amp;quot;. $userInputUsername. &amp;quot;&amp;#039; AND password = &amp;#039;&amp;quot;. $userInputPassword. &amp;quot;&amp;#039;&amp;quot;;&amp;lt;/code&amp;gt;&lt;br /&gt;
Wenn ein Angreifer als Benutzernamen &amp;lt;code&amp;gt;&amp;#039; OR &amp;#039;1&amp;#039;=&amp;#039;1&amp;lt;/code&amp;gt; eingibt, wird die resultierende SQL-Abfrage zu:&lt;br /&gt;
&amp;lt;code&amp;gt;SELECT * FROM users WHERE username = &amp;#039;&amp;#039; OR &amp;#039;1&amp;#039;=&amp;#039;1&amp;#039; AND password = &amp;#039;...&amp;#039;&amp;lt;/code&amp;gt;&lt;br /&gt;
Da &amp;lt;code&amp;gt;&amp;#039;1&amp;#039;=&amp;#039;1&amp;#039;&amp;lt;/code&amp;gt; immer wahr ist, würde diese Abfrage (je nach genauer Struktur und Datenbank) möglicherweise alle Benutzer zurückgeben oder den Login ohne korrektes Passwort ermöglichen.[5, 15, 55]&lt;br /&gt;
&lt;br /&gt;
=== Auswirkungen und Gefahren von SQL Injection ===&lt;br /&gt;
&lt;br /&gt;
Die potenziellen Folgen einer erfolgreichen SQL-Injection-Attacke sind gravierend und vielfältig:&lt;br /&gt;
&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Datendiebstahl:&amp;#039;&amp;#039;&amp;#039; Angreifer können auf sensible Daten zugreifen und diese auslesen, darunter Benutzerdaten, Passwörter (oft als Hashes, die aber geknackt werden können), Kreditkarteninformationen, Geschäftsgeheimnisse und andere vertrauliche Informationen.[51, 53, 56]&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Datenmanipulation/-zerstörung:&amp;#039;&amp;#039;&amp;#039; Angreifer können bestehende Daten in der Datenbank verändern (z.B. Preise in einem Shop, Benutzerrollen) oder komplette Datensätze oder sogar Tabellen löschen (&amp;lt;code&amp;gt;DELETE&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;DROP TABLE&amp;lt;/code&amp;gt;).[51, 53, 56]&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Umgehung der Authentifizierung/Identitätsdiebstahl:&amp;#039;&amp;#039;&amp;#039; Wie im Beispiel oben gezeigt, können Angreifer Login-Mechanismen umgehen und sich unautorisiert Zugang verschaffen, möglicherweise sogar mit administrativen Rechten.[51, 56]&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Denial of Service (DoS):&amp;#039;&amp;#039;&amp;#039; Durch das Löschen wichtiger Daten oder das Ausführen ressourcenintensiver Abfragen kann die Verfügbarkeit der Anwendung oder der Datenbank beeinträchtigt werden.[51, 56]&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Kompromittierung des Servers:&amp;#039;&amp;#039;&amp;#039; In manchen Fällen können Angreifer über SQL Injection Betriebssystembefehle auf dem Datenbankserver ausführen oder vollen administrativen Zugriff auf die Datenbank erlangen, was weitreichende Konsequenzen haben kann.[51, 55, 56]&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Reputationsschaden und rechtliche Folgen:&amp;#039;&amp;#039;&amp;#039; Erfolgreiche Angriffe, insbesondere wenn sie zu Datenlecks führen, können das Vertrauen der Benutzer schwer beschädigen und zu empfindlichen Strafen gemäß Datenschutzgesetzen führen.[53]&lt;br /&gt;
&lt;br /&gt;
=== Prävention durch Prepared Statements (Primäre Methode) ===&lt;br /&gt;
&lt;br /&gt;
Die mit Abstand wichtigste und effektivste Methode zur Verhinderung von SQL Injection ist die konsequente Verwendung von &amp;#039;&amp;#039;&amp;#039;Prepared Statements&amp;#039;&amp;#039;&amp;#039; (parametrisierten Abfragen).[4, 25, 5, 58, 35]&lt;br /&gt;
&lt;br /&gt;
Das Grundprinzip von Prepared Statements ist die &amp;#039;&amp;#039;&amp;#039;strikte Trennung von SQL-Befehlsstruktur und den Daten (Parametern)&amp;#039;&amp;#039;&amp;#039;.[4, 25, 5, 58, 35] Der Ablauf ist typischerweise zweistufig:&lt;br /&gt;
**# &amp;#039;&amp;#039;&amp;#039;Prepare (Vorbereiten):&amp;#039;&amp;#039;&amp;#039; Die SQL-Anweisung wird mit Platzhaltern anstelle der tatsächlichen Werte an den Datenbankserver gesendet. Der Server parst die Anweisung, prüft die Syntax und bereitet einen Ausführungsplan vor, ohne die endgültigen Daten zu kennen.&lt;br /&gt;
**# &amp;#039;&amp;#039;&amp;#039;Execute (Ausführen) mit Parameterbindung:&amp;#039;&amp;#039;&amp;#039; Die tatsächlichen Werte (Parameter) werden separat an den Server gesendet und an die vorbereiteten Platzhalter gebunden. Der Server führt nun die vorkompilierte Anweisung mit den sicher eingefügten Werten aus.&lt;br /&gt;
Der entscheidende Punkt ist, dass die Datenbank die separat gesendeten Parameter &amp;#039;&amp;#039;&amp;#039;immer als Datenwerte&amp;#039;&amp;#039;&amp;#039; behandelt, niemals als Teil des SQL-Befehls.[5, 15, 58] Selbst wenn ein Parameter bösartigen SQL-Code enthält (z.B. &amp;lt;code&amp;gt;&amp;#039; OR &amp;#039;1&amp;#039;=&amp;#039;1&amp;lt;/code&amp;gt;), wird dieser nicht ausgeführt, sondern als einfacher String-Wert behandelt, der z.B. in eine Spalte eingefügt oder in einer &amp;lt;code&amp;gt;WHERE&amp;lt;/code&amp;gt;-Klausel verglichen wird. Diese Trennung macht Prepared Statements immun gegen SQL Injection.&lt;br /&gt;
&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Platzhalter:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;PDO:&amp;#039;&amp;#039;&amp;#039; Unterstützt sowohl positionale Platzhalter (&amp;lt;code&amp;gt;?&amp;lt;/code&amp;gt;) als auch benannte Platzhalter (&amp;lt;code&amp;gt;:name&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;:email&amp;lt;/code&amp;gt;, etc.).[5, 13] Benannte Platzhalter machen den Code oft lesbarer, besonders bei vielen Parametern.&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;MySQLi:&amp;#039;&amp;#039;&amp;#039; Unterstützt ausschließlich positionale Platzhalter (&amp;lt;code&amp;gt;?&amp;lt;/code&amp;gt;).[25]&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Parameterbindung:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;PDO:&amp;#039;&amp;#039;&amp;#039; Bietet flexible Bindungsoptionen:&lt;br /&gt;
***** &amp;#039;&amp;#039;Implizit über &amp;lt;code&amp;gt;execute()&amp;lt;/code&amp;gt;-Array:&amp;#039;&amp;#039; Die einfachste und häufigste Methode. Ein Array mit den Werten wird an &amp;lt;code&amp;gt;execute()&amp;lt;/code&amp;gt; übergeben. PDO kümmert sich um das korrekte Escaping und die Typbehandlung (oft werden Werte sicher als Strings behandelt).[4, 12, 5, 13]&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$stmt-&amp;amp;gt;execute([$wert1, $wert2]); // für? Platzhalter&lt;br /&gt;
$stmt-&amp;amp;gt;execute([&amp;#039;:name&amp;#039; =&amp;amp;gt; $wert1, &amp;#039;:id&amp;#039; =&amp;amp;gt; $wert2]); // für :name Platzhalter&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
***** &amp;#039;&amp;#039;Explizit mit &amp;lt;code&amp;gt;bindValue()&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;bindParam()&amp;lt;/code&amp;gt;:&amp;#039;&amp;#039; Erlaubt die explizite Angabe des Datentyps (z.B. &amp;lt;code&amp;gt;PDO::PARAM_INT&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;PDO::PARAM_STR&amp;lt;/code&amp;gt;) für jeden Parameter, was in manchen Fällen für Klarheit oder spezifische Datenbankoptimierungen sorgen kann.[5, 13] &amp;lt;code&amp;gt;bindParam&amp;lt;/code&amp;gt; bindet eine Variable (ihr Wert wird erst bei &amp;lt;code&amp;gt;execute()&amp;lt;/code&amp;gt; gelesen), &amp;lt;code&amp;gt;bindValue&amp;lt;/code&amp;gt; bindet den aktuellen Wert einer Variablen oder einen Literalwert.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$stmt-&amp;amp;gt;bindValue(&amp;#039;:id&amp;#039;, $user_id, PDO::PARAM_INT);&lt;br /&gt;
$stmt-&amp;amp;gt;bindParam(&amp;#039;:name&amp;#039;, $user_name, PDO::PARAM_STR);&lt;br /&gt;
$stmt-&amp;amp;gt;execute();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;MySQLi:&amp;#039;&amp;#039;&amp;#039; Erfordert immer die explizite Bindung mittels &amp;lt;code&amp;gt;bind_param()&amp;lt;/code&amp;gt;.&lt;br /&gt;
***** &amp;lt;code&amp;gt;bind_param()&amp;lt;/code&amp;gt; (OO) / &amp;lt;code&amp;gt;mysqli_stmt_bind_param()&amp;lt;/code&amp;gt; (Proc): Nimmt als ersten Parameter einen String entgegen, der die Datentypen aller nachfolgend übergebenen Variablen definiert.[25, 50, 15, 58]&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// OO-Stil&lt;br /&gt;
$stmt-&amp;amp;gt;bind_param(&amp;quot;ssi&amp;quot;, $name, $email, $id); // String, String, Integer&lt;br /&gt;
// Prozedural&lt;br /&gt;
mysqli_stmt_bind_param($stmt, &amp;quot;ssi&amp;quot;, $name, $email, $id);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
=== Tabelle 4: MySQLi &amp;lt;code&amp;gt;bind_param()&amp;lt;/code&amp;gt; Typen-String ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Typ-Buchstabe !! PHP-Datentyp (typisch) !! Beschreibung&lt;br /&gt;
|- &lt;br /&gt;
| mwt-preservehtml=&amp;quot;&amp;quot; i=&amp;quot;&amp;quot; code=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| integer=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Integer (Ganzzahl)&lt;br /&gt;
|- &lt;br /&gt;
| mwt-preservehtml=&amp;quot;&amp;quot; d=&amp;quot;&amp;quot; code=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| double=&amp;quot;&amp;quot; float=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Double (Gleitkommazahl)&lt;br /&gt;
|- &lt;br /&gt;
| mwt-preservehtml=&amp;quot;&amp;quot; s=&amp;quot;&amp;quot; code=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| string=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| String (Zeichenkette)&lt;br /&gt;
|- &lt;br /&gt;
| mwt-preservehtml=&amp;quot;&amp;quot; b=&amp;quot;&amp;quot; code=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| string=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| BLOB (Binary Large Object), als String gesendet&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Detaillierte Code-Beispiele zur Prävention:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
Die Beispiele für INSERT, UPDATE und DELETE in Abschnitt 3.3 demonstrieren bereits die korrekte Anwendung von Prepared Statements mit PDO und MySQLi zur Verhinderung von SQL Injection. Es ist entscheidend, dieses Muster konsequent anzuwenden, wann immer externe Daten in SQL-Abfragen einfließen.&lt;br /&gt;
&lt;br /&gt;
=== 4.4 Zusätzliche Schutzmaßnahmen (Defense-in-Depth) ===&lt;br /&gt;
&lt;br /&gt;
Obwohl Prepared Statements der wichtigste Schutz gegen SQL Injection sind, sollte Sicherheit immer als mehrschichtiges Konzept (&amp;quot;Defense-in-Depth&amp;quot;) betrachtet werden.[5] Zusätzliche Maßnahmen erhöhen die Robustheit der Anwendung:&lt;br /&gt;
&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Eingabevalidierung und -bereinigung:&amp;#039;&amp;#039;&amp;#039; Überprüfen Sie &amp;#039;&amp;#039;alle&amp;#039;&amp;#039; Benutzereingaben serverseitig, bevor sie überhaupt in die Nähe der Datenbank kommen.[4, 59, 20, 5] Stellen Sie sicher, dass die Daten dem erwarteten Typ (Zahl, String, E-Mail-Format etc.) und Format entsprechen. Verwenden Sie dafür PHP-Filterfunktionen wie &amp;lt;code&amp;gt;filter_var()&amp;lt;/code&amp;gt; oder reguläre Ausdrücke.[4, 5] Dies ist eine wichtige zweite Verteidigungslinie, die ungültige oder unerwartete Daten frühzeitig abfängt. Es ersetzt jedoch &amp;#039;&amp;#039;nicht&amp;#039;&amp;#039; die Notwendigkeit von Prepared Statements.&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Prinzip der geringsten Rechte (Least Privilege):&amp;#039;&amp;#039;&amp;#039; Konfigurieren Sie den Datenbankbenutzer, den Ihre PHP-Anwendung verwendet, mit den absolut minimal notwendigen Berechtigungen.[20, 5] Wenn die Anwendung nur Daten lesen und schreiben muss, geben Sie ihr keine Rechte zum Ändern der Tabellenstruktur (&amp;lt;code&amp;gt;ALTER TABLE&amp;lt;/code&amp;gt;) oder zum Löschen von Tabellen (&amp;lt;code&amp;gt;DROP TABLE&amp;lt;/code&amp;gt;). Beschränken Sie den Zugriff auf die benötigten Datenbanken und Tabellen. Verwenden Sie niemals den &amp;lt;code&amp;gt;root&amp;lt;/code&amp;gt;- oder Admin-Benutzer der Datenbank für die Anwendung. Dies begrenzt den potenziellen Schaden, falls doch eine Schwachstelle ausgenutzt wird.&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Sichere Fehlerbehandlung:&amp;#039;&amp;#039;&amp;#039; Konfigurieren Sie PHP und Ihre Datenbankerweiterung so, dass detaillierte Fehlermeldungen (insbesondere solche, die SQL-Code oder Datenbankstrukturinformationen enthalten) niemals dem Endbenutzer angezeigt werden.[20, 5] Solche Informationen sind für Angreifer wertvoll. Loggen Sie Fehler stattdessen in eine sichere Datei auf dem Server, die nur für Administratoren zugänglich ist. Verwenden Sie &amp;lt;code&amp;gt;PDO::ERRMODE_EXCEPTION&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;mysqli_report&amp;lt;/code&amp;gt; für strukturierte Fehlerbehandlung.&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Output Escaping:&amp;#039;&amp;#039;&amp;#039; Obwohl nicht direkt zur Verhinderung von SQL Injection, ist es wichtig, Daten, die aus der Datenbank gelesen und im HTML-Kontext ausgegeben werden, korrekt zu escapen (z.B. mit &amp;lt;code&amp;gt;htmlspecialchars()&amp;lt;/code&amp;gt;), um Cross-Site Scripting (XSS)-Angriffe zu verhindern.&lt;br /&gt;
Die Kombination dieser Maßnahmen – mit Prepared Statements als Kernstück – bietet einen robusten Schutz gegen SQL Injection und andere verwandte Angriffe.&lt;br /&gt;
&lt;br /&gt;
== Nützliche PHP Array-Funktionen für Datenbankergebnisse ==&lt;br /&gt;
&lt;br /&gt;
Wenn Daten aus der Datenbank abgerufen werden, insbesondere mit Methoden wie &amp;lt;code&amp;gt;fetchAll()&amp;lt;/code&amp;gt; (PDO) oder &amp;lt;code&amp;gt;fetch_all()&amp;lt;/code&amp;gt; (MySQLi), liegen die Ergebnisse typischerweise als PHP-Array vor (meist ein Array von assoziativen Arrays, wobei jedes innere Array eine Ergebniszeile darstellt). PHP bietet eine Reihe nützlicher Funktionen zur Verarbeitung solcher Arrays.&lt;br /&gt;
&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Iteration mit &amp;lt;code&amp;gt;foreach&amp;lt;/code&amp;gt;:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
Dies ist die Standardmethode, um die einzelnen Zeilen und Spalten eines Ergebnis-Arrays zu durchlaufen.[37, 40, 44]&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;Syntax:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Iteration über die Zeilen (jedes $row ist ein assoziatives Array)&lt;br /&gt;
foreach ($results as $row) {&lt;br /&gt;
 echo &amp;quot;ID: &amp;quot;. $row[&amp;#039;id&amp;#039;]. &amp;quot;, Name: &amp;quot;. htmlspecialchars($row[&amp;#039;name&amp;#039;]). &amp;quot;&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Iteration über Zeilen mit Zugriff auf den numerischen Index der Zeile&lt;br /&gt;
foreach ($results as $index =&amp;amp;gt; $row) {&lt;br /&gt;
 echo &amp;quot;Zeile &amp;quot;. $index. &amp;quot;: ID = &amp;quot;. $row[&amp;#039;id&amp;#039;]. &amp;quot;&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;Code-Beispiel:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!--?php// Annahme: $results enthält das Ergebnis von $stmt---&amp;gt;fetchAll(PDO::FETCH_ASSOC);&lt;br /&gt;
$results = [&amp;#039;id&amp;#039; =&amp;amp;gt; 1, &amp;#039;name&amp;#039; =&amp;amp;gt; &amp;#039;Alice&amp;#039;, &amp;#039;email&amp;#039; =&amp;amp;gt; &amp;#039;alice@example.com&amp;#039;],&lt;br /&gt;
 ;&lt;br /&gt;
&lt;br /&gt;
echo &amp;quot;&amp;lt;/syntaxhighlight&amp;gt;&amp;quot;;&lt;br /&gt;
foreach ($results as $row) {&lt;br /&gt;
 echo &amp;quot;&lt;br /&gt;
*Benutzer #&amp;quot;. $row[&amp;#039;id&amp;#039;]. &amp;quot;: &amp;quot;. htmlspecialchars($row[&amp;#039;name&amp;#039;]);&lt;br /&gt;
 echo &amp;quot; (&amp;quot;. htmlspecialchars($row[&amp;#039;email&amp;#039;]). &amp;quot;)&lt;br /&gt;
&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
echo &amp;quot;&amp;quot;;&lt;br /&gt;
?&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Zählen von Ergebnissen mit &amp;lt;code&amp;gt;count()&amp;lt;/code&amp;gt;:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
Die Funktion &amp;lt;code&amp;gt;count()&amp;lt;/code&amp;gt; gibt die Anzahl der Elemente in einem Array zurück. Wenn sie auf ein mit &amp;lt;code&amp;gt;fetchAll()&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;fetch_all()&amp;lt;/code&amp;gt; erzeugtes Ergebnis-Array angewendet wird, liefert sie die Anzahl der abgerufenen Zeilen.[40, 60, 61]&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;Syntax:&amp;#039;&amp;#039;&amp;#039; &amp;lt;code&amp;gt;$anzahlZeilen = count($results);&amp;lt;/code&amp;gt;.[60]&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;Wichtiger Hinweis:&amp;#039;&amp;#039;&amp;#039; &amp;lt;code&amp;gt;count()&amp;lt;/code&amp;gt; funktioniert nur zuverlässig für die Gesamtzahl der Zeilen, wenn &amp;#039;&amp;#039;alle&amp;#039;&amp;#039; Zeilen zuvor in ein Array geladen wurden (also nach &amp;lt;code&amp;gt;fetchAll&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;fetch_all&amp;lt;/code&amp;gt;). Wenn Sie Ergebnisse zeilenweise mit &amp;lt;code&amp;gt;fetch()&amp;lt;/code&amp;gt; in einer &amp;lt;code&amp;gt;while&amp;lt;/code&amp;gt;-Schleife verarbeiten, müssen Sie einen manuellen Zähler innerhalb der Schleife inkrementieren, um die Anzahl der verarbeiteten Zeilen zu ermitteln.[40] Alternativ können &amp;lt;code&amp;gt;PDOStatement::rowCount()&amp;lt;/code&amp;gt; [8, 9] oder &amp;lt;code&amp;gt;mysqli_result::$num_rows&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;mysqli_stmt::$num_rows&amp;lt;/code&amp;gt; [26, 15] verwendet werden, um die Anzahl der von einer &amp;lt;code&amp;gt;SELECT&amp;lt;/code&amp;gt;-Abfrage zurückgegebenen Zeilen zu erhalten, &amp;#039;&amp;#039;bevor&amp;#039;&amp;#039; die Daten gefetched werden (das Verhalten von &amp;lt;code&amp;gt;rowCount&amp;lt;/code&amp;gt; kann jedoch bei anderen Anweisungstypen wie &amp;lt;code&amp;gt;UPDATE&amp;lt;/code&amp;gt; variieren).&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;Code-Beispiel:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!--?php&lt;br /&gt;
// Annahme: $results von fetchAll()&lt;br /&gt;
$results = [ /*... wie oben... */ ];&lt;br /&gt;
$anzahl = count($results);&lt;br /&gt;
echo &amp;quot;Anzahl der Benutzer (via fetchAll/count): $anzahl&amp;lt;br--&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
// Vergleich: Zählen bei zeilenweiser Verarbeitung&lt;br /&gt;
$stmt = $pdo-&amp;amp;gt;query(&amp;quot;SELECT id FROM users&amp;quot;); // Annahme: PDO-Statement&lt;br /&gt;
$manueller_zaehler = 0;&lt;br /&gt;
while ($row = $stmt-&amp;amp;gt;fetch()) {&lt;br /&gt;
 $manueller_zaehler++;&lt;br /&gt;
}&lt;br /&gt;
echo &amp;quot;Anzahl der Benutzer (via while/fetch): $manueller_zaehler&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
// Alternative: rowCount() nach SELECT (PDO)&lt;br /&gt;
$stmt_count = $pdo-&amp;amp;gt;query(&amp;quot;SELECT id FROM users&amp;quot;);&lt;br /&gt;
$rowCountResult = $stmt_count-&amp;amp;gt;rowCount(); // Kann bei manchen DBs/Treibern funktionieren&lt;br /&gt;
echo &amp;quot;Anzahl der Benutzer (via rowCount nach SELECT): $rowCountResult&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
?&amp;amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;(Optional) Weitere nützliche Array-Funktionen:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
**** &amp;lt;code&amp;gt;array_column(array $input, mixed $column_key [, mixed $index_key = null ])&amp;lt;/code&amp;gt;: Extrahiert alle Werte aus einer einzelnen Spalte des Ergebnis-Arrays und gibt sie als neues, eindimensionales Array zurück.[62] Sehr nützlich, um z.B. eine Liste aller Benutzer-IDs oder E-Mail-Adressen zu erhalten.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Annahme: $results enthält das Array von assoziativen Arrays&lt;br /&gt;
$alle_emails = array_column($results, &amp;#039;email&amp;#039;);&lt;br /&gt;
// $alle_emails ist nun [&amp;#039;alice@example.com&amp;#039;, &amp;#039;bob@example.com&amp;#039;]&lt;br /&gt;
print_r($alle_emails);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
**** &amp;lt;code&amp;gt;array_map(callable $callback, array $array1 [, array...$arrays ])&amp;lt;/code&amp;gt;: Wendet eine benutzerdefinierte Funktion (&amp;lt;code&amp;gt;callback&amp;lt;/code&amp;gt;) auf jedes Element eines Arrays an und gibt ein neues Array mit den Ergebnissen zurück.[63] Nützlich zur Transformation von Daten (z.B. Formatierung, Bereinigung).&lt;br /&gt;
**** &amp;lt;code&amp;gt;array_filter(array $array [, callable $callback [, int $flag = 0 ]])&amp;lt;/code&amp;gt;: Filtert Elemente eines Arrays basierend auf einer Callback-Funktion.[61] Nützlich, um nur bestimmte Zeilen aus dem Ergebnis-Array zu behalten (obwohl dies oft effizienter direkt in der SQL-Abfrage geschieht).&lt;br /&gt;
**** &amp;lt;code&amp;gt;array_count_values(array $array)&amp;lt;/code&amp;gt;: Zählt, wie oft jeder eindeutige Wert in einem eindimensionalen Array vorkommt.[63] Kann nützlich sein, wenn man z.B. mit &amp;lt;code&amp;gt;array_column&amp;lt;/code&amp;gt; eine Spalte extrahiert hat und die Häufigkeit bestimmter Werte (z.B. Status) wissen möchte.&lt;br /&gt;
Diese Funktionen, insbesondere &amp;lt;code&amp;gt;foreach&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;count&amp;lt;/code&amp;gt;, sind grundlegende Werkzeuge bei der Verarbeitung von Datenbankergebnissen in PHP.&lt;br /&gt;
&lt;br /&gt;
== Zusammenfassung &amp;amp; Best Practices ==&lt;br /&gt;
&lt;br /&gt;
Die Integration von Datenbanken ist ein zentraler Aspekt der PHP-Webentwicklung. Dieses Kapitel hat die Grundlagen für die Interaktion mit MySQL-Datenbanken gelegt. Hier sind die wichtigsten Punkte und Best Practices zusammengefasst:&lt;br /&gt;
&lt;br /&gt;
 &amp;#039;&amp;#039;&amp;#039;Wahl der Erweiterung:&amp;#039;&amp;#039;&amp;#039; Verwenden Sie ausschließlich die modernen Erweiterungen &amp;#039;&amp;#039;&amp;#039;PDO&amp;#039;&amp;#039;&amp;#039; oder &amp;#039;&amp;#039;&amp;#039;MySQLi&amp;#039;&amp;#039;&amp;#039;.[2, 29, 5, 15]&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;PDO&amp;#039;&amp;#039;&amp;#039; wird generell für neue Projekte empfohlen, da es Datenbankunabhängigkeit bietet und eine konsistentere, oft als einfacher empfundene API (insbesondere bei der Parameterbindung) hat.[16, 3, 64]&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;MySQLi&amp;#039;&amp;#039;&amp;#039; ist eine gute Wahl, wenn ausschließlich mit MySQL gearbeitet wird und spezifische MySQL-Funktionen benötigt werden oder wenn eine prozedurale Schnittstelle bevorzugt wird.[15]&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Prepared Statements:&amp;#039;&amp;#039;&amp;#039; Dies ist die wichtigste Sicherheitsmaßnahme. Verwenden Sie Prepared Statements immer dann, wenn externe Daten (Benutzereingaben, URL-Parameter etc.) Teil einer SQL-Abfrage werden (insbesondere bei &amp;lt;code&amp;gt;WHERE&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;INSERT&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;UPDATE&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;DELETE&amp;lt;/code&amp;gt;).[2, 25, 5] Sie verhindern SQL Injection zuverlässig durch die Trennung von Code und Daten.&lt;br /&gt;
 &amp;#039;&amp;#039;&amp;#039;Sichere Fehlerbehandlung:&amp;#039;&amp;#039;&amp;#039; Konfigurieren Sie PDO (&amp;lt;code&amp;gt;PDO::ATTR_ERRMODE =&amp;amp;gt; PDO::ERRMODE_EXCEPTION&amp;lt;/code&amp;gt;) oder MySQLi (&amp;lt;code&amp;gt;mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT)&amp;lt;/code&amp;gt;) so, dass Fehler als Exceptions behandelt werden.[1, 18] Fangen Sie diese Exceptions ab und loggen Sie detaillierte Fehlermeldungen serverseitig, anstatt sie dem Benutzer anzuzeigen.[20, 5]&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Input Validation:&amp;#039;&amp;#039;&amp;#039; Validieren Sie alle Benutzereingaben serverseitig auf Typ, Format und erlaubte Werte, bevor Sie sie an die Datenbank übergeben. Dies ist eine wichtige zusätzliche Schutzschicht.[4, 5]&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Prinzip der geringsten Rechte (Least Privilege):&amp;#039;&amp;#039;&amp;#039; Der Datenbankbenutzer, den Ihre PHP-Anwendung verwendet, sollte nur die minimal notwendigen Berechtigungen für seine Aufgaben besitzen.[5]&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Sichere Zugangsdaten:&amp;#039;&amp;#039;&amp;#039; Speichern Sie Datenbank-Credentials (Hostname, Benutzername, Passwort) niemals direkt im PHP-Code oder in Dateien innerhalb des Web-Roots.[20, 65] Verwenden Sie stattdessen Konfigurationsdateien außerhalb des öffentlich zugänglichen Bereichs oder Umgebungsvariablen.&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Code-Struktur:&amp;#039;&amp;#039;&amp;#039; Kapseln Sie Datenbankverbindungslogik und wiederkehrende Abfragen in Funktionen oder Klassen, um die Wartbarkeit und Wiederverwendbarkeit zu verbessern.[20]&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Effizienz:&amp;#039;&amp;#039;&amp;#039; Laden Sie große Ergebnismengen nicht komplett in den Speicher (&amp;lt;code&amp;gt;fetchAll&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;fetch_all&amp;lt;/code&amp;gt;). Verwenden Sie stattdessen zeilenweise Verarbeitung (&amp;lt;code&amp;gt;while&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;fetch&amp;lt;/code&amp;gt;) oder Iteratoren (PDO mit PHP 8+).[37, 40] Nutzen Sie persistente Verbindungen nur nach sorgfältiger Abwägung der Vor- und Nachteile.[7, 27]&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Defense-in-Depth:&amp;#039;&amp;#039;&amp;#039; Betrachten Sie Sicherheit als Gesamtkonzept. Die Kombination aus der richtigen API-Wahl, konsequenten Prepared Statements, Eingabevalidierung, minimalen Rechten, sicherer Fehlerbehandlung, sicherer Speicherung von Zugangsdaten und regelmäßigen Software-Updates bildet eine robuste Verteidigung.&lt;/div&gt;</summary>
		<author><name>Völkl Anna</name></author>
	</entry>
	<entry>
		<id>https://mediawiki.fernfh.ac.at/mediawiki/index.php?title=Datenbankintegration_(Webdevelopment)&amp;diff=6573</id>
		<title>Datenbankintegration (Webdevelopment)</title>
		<link rel="alternate" type="text/html" href="https://mediawiki.fernfh.ac.at/mediawiki/index.php?title=Datenbankintegration_(Webdevelopment)&amp;diff=6573"/>
		<updated>2025-05-01T12:49:26Z</updated>

		<summary type="html">&lt;p&gt;Völkl Anna: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Kapitel X: Datenbankintegration mit PHP und MySQL =&lt;br /&gt;
&lt;br /&gt;
== Einleitung: Warum Datenbanken in PHP-Webanwendungen? ==&lt;br /&gt;
&lt;br /&gt;
Moderne Webanwendungen sind selten statisch. Sie leben von dynamischen Inhalten, Benutzerinteraktionen und der Fähigkeit, Informationen über Sitzungen hinweg zu speichern und wieder abzurufen. Ob es sich um ein Content-Management-System (CMS), einen Online-Shop, ein soziales Netzwerk oder eine einfache Benutzerverwaltung handelt – die Persistenz von Daten ist entscheidend. Hier kommen Datenbanken ins Spiel.&lt;br /&gt;
&lt;br /&gt;
PHP allein, als serverseitige Skriptsprache, kann Daten nur für die Dauer der Ausführung eines Skripts im Arbeitsspeicher halten. Sobald das Skript beendet ist, gehen diese Informationen verloren. Um Daten dauerhaft zu speichern, zu organisieren, zu ändern und effizient abzurufen, benötigen PHP-Anwendungen ein externes Datenbanksystem.&lt;br /&gt;
&lt;br /&gt;
Eine der populärsten Datenbanklösungen im Webentwicklungsbereich, insbesondere in Kombination mit PHP, ist MySQL. Als relationales Datenbankmanagementsystem (RDBMS) bietet MySQL eine strukturierte Methode zur Datenspeicherung in Tabellen, die miteinander in Beziehung stehen können. Es ist ein Kernbestandteil vieler Webserver-Setups wie LAMP (Linux, Apache, MySQL, PHP) oder LEMP (Linux, Nginx, MySQL, PHP).&lt;br /&gt;
&lt;br /&gt;
Um die Brücke zwischen der PHP-Anwendung und der MySQL-Datenbank zu schlagen, stellt PHP spezielle Erweiterungen (Extensions) bereit. Diese Erweiterungen fungieren als Schnittstelle oder API (Application Programming Interface), die es PHP-Skripten ermöglichen, eine Verbindung zur Datenbank herzustellen, SQL-Befehle zu senden und die von der Datenbank zurückgegebenen Ergebnisse zu empfangen und zu verarbeiten.&lt;br /&gt;
&lt;br /&gt;
Dieses Kapitel führt in die Grundlagen der Datenbankintegration mit PHP und MySQL ein. Es enthält die verschiedenen verfügbaren PHP-Erweiterungen, wobei der Schwerpunkt auf den modernen und sicheren Ansätzen liegt: PDO (PHP Data Objects) und MySQLi (MySQL Improved). Es wird gezeigt, wie man Verbindungen aufbaut, grundlegende Datenbankoperationen – bekannt als CRUD (Create, Read, Update, Delete) – durchführt und wie man Daten sicher abfragt und manipuliert. Ein besonderes Augenmerk liegt dabei auf der Sicherheit, insbesondere auf der Vermeidung von SQL-Injection-Angriffen durch den Einsatz von Prepared Statements. Zum Abschluss erfolgt ein Überblick über nützliche PHP-Array-Funktionen zur Verarbeitung der abgerufenen Datenbankergebnisse.&lt;br /&gt;
&lt;br /&gt;
== Überblick über PHP-Datenbankerweiterungen ==&lt;br /&gt;
&lt;br /&gt;
Die Art und Weise, wie PHP mit Datenbanken interagiert, hat sich im Laufe der Zeit weiterentwickelt. Ursprünglich gab es für jede populäre Datenbank eine eigene spezifische Erweiterung. Für MySQL waren dies die &amp;lt;code&amp;gt;mysql_*&amp;lt;/code&amp;gt; Funktionen. Diese sind jedoch inzwischen veraltet und wurden durch modernere, flexiblere und sicherere Alternativen ersetzt. Heute stehen Entwicklern hauptsächlich zwei empfohlene Erweiterungen für die Arbeit mit MySQL zur Verfügung: PDO und MySQLi.&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;PDO (PHP Data Objects):&amp;#039;&amp;#039;&amp;#039; Eine allgemeine Datenzugriffs-Abstraktionsschicht, die mit verschiedenen Datenbanksystemen verwendet werden kann.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;MySQLi (MySQL Improved):&amp;#039;&amp;#039;&amp;#039; Eine Erweiterung, die speziell für die Arbeit mit MySQL entwickelt wurde und dessen spezifische Funktionen unterstützt.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;&amp;lt;code&amp;gt;mysql_*&amp;lt;/code&amp;gt; Funktionen:&amp;#039;&amp;#039;&amp;#039; Die ursprüngliche MySQL-Erweiterung, die seit PHP 7.0 entfernt wurde und nicht mehr verwendet werden sollte.&lt;br /&gt;
Im Folgenden werden diese drei Optionen detaillierter vorgestellt.&lt;br /&gt;
&lt;br /&gt;
=== PDO (PHP Data Objects) ===&lt;br /&gt;
&lt;br /&gt;
Die PDO-Erweiterung definiert eine leichtgewichtige und konsistente Schnittstelle für den Datenbankzugriff in PHP. Sie fungiert als &amp;#039;&amp;#039;Datenzugriffs-Abstraktionsschicht&amp;#039;&amp;#039;. Das bedeutet, dass Entwickler unabhängig von der verwendeten Datenbank dieselben PDO-Funktionen nutzen können, um Abfragen auszuführen und Daten abzurufen.[1, 2] Es ist jedoch wichtig zu verstehen, dass PDO &amp;#039;&amp;#039;keine&amp;#039;&amp;#039; Datenbankabstraktion im Sinne einer SQL-Dialekt-Übersetzung oder der Emulation fehlender Datenbankfeatures bietet. Um eine Verbindung zu einer spezifischen Datenbank wie MySQL herzustellen, benötigt PDO einen entsprechenden datenbankspezifischen Treiber, z.B. &amp;lt;code&amp;gt;pdo_mysql&amp;lt;/code&amp;gt;, der in der PHP-Konfiguration aktiviert sein muss.[1]&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Vorteile von PDO:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Datenbankunabhängigkeit:&amp;#039;&amp;#039;&amp;#039; Der größte Vorteil von PDO ist die Möglichkeit, den Code für Datenbankinteraktionen weitgehend unverändert zu lassen, selbst wenn das zugrunde liegende Datenbanksystem gewechselt wird (z.B. von MySQL zu PostgreSQL).[1, 2] Dies erhöht die Portabilität und Flexibilität von PHP-Anwendungen erheblich.[2, 3]&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Sicherheit durch Prepared Statements:&amp;#039;&amp;#039;&amp;#039; PDO bietet eine robuste und konsistente Implementierung von Prepared Statements. Diese Technik ist der Goldstandard zur Verhinderung von SQL-Injection-Angriffen, da sie die SQL-Befehlsstruktur strikt von den übergebenen Daten trennt.[1, 2, 4, 5, 6]&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Benutzerfreundlichkeit und Konsistenz:&amp;#039;&amp;#039;&amp;#039; PDO stellt eine einheitliche API mit nützlichen Hilfsfunktionen bereit, wie z.B. vielfältige &amp;quot;Fetch Modes&amp;quot;, um die Struktur der abgerufenen Daten zu steuern.[2] Die konsistente Funktionsweise über verschiedene Datenbanktreiber hinweg vereinfacht die Entwicklung.&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Moderne Fehlerbehandlung:&amp;#039;&amp;#039;&amp;#039; PDO verwendet standardmäßig Exceptions (&amp;lt;code&amp;gt;PDOException&amp;lt;/code&amp;gt;) zur Signalisierung von Fehlern.[1, 2, 7] Dies ermöglicht eine strukturierte und moderne Fehlerbehandlung mittels &amp;lt;code&amp;gt;try...catch&amp;lt;/code&amp;gt;-Blöcken, was als Best Practice gilt.[1, 2]&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Verbindungsaufbau mit PDO (Beispiel MySQL):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;DSN (Data Source Name):&amp;#039;&amp;#039;&amp;#039; Die Verbindungsparameter werden in einer Zeichenkette, dem DSN, übergeben. Für MySQL hat der DSN typischerweise das Format &amp;lt;code&amp;gt;mysql:host=hostname;dbname=datenbankname;charset=zeichensatz;unix_socket=pfad_zum_socket&amp;lt;/code&amp;gt;. Die Angabe des Zeichensatzes (z.B. &amp;lt;code&amp;gt;utf8mb4&amp;lt;/code&amp;gt;) ist wichtig für die korrekte Datenübertragung.[1, 7] Beispiel: &amp;lt;code&amp;gt;mysql:host=localhost;dbname=test;charset=utf8mb4&amp;lt;/code&amp;gt;.&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;PDO-Konstruktor:&amp;#039;&amp;#039;&amp;#039; Eine Verbindung wird durch Instanziierung der PDO-Klasse hergestellt: &amp;lt;code&amp;gt;$pdo = new PDO($dsn, $username, $password, $options);&amp;lt;/code&amp;gt;.[1, 7, 8] Die Parameter sind der DSN, der Datenbankbenutzername, das Passwort und ein optionales Array mit Treiberoptionen.&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Verbindungsoptionen (&amp;lt;code&amp;gt;$options&amp;lt;/code&amp;gt;):&amp;#039;&amp;#039;&amp;#039; Über dieses Array können wichtige Verhaltensweisen von PDO gesteuert werden:&lt;br /&gt;
*** &amp;lt;code&amp;gt;PDO::ATTR_ERRMODE =&amp;amp;gt; PDO::ERRMODE_EXCEPTION&amp;lt;/code&amp;gt;: Dies ist die empfohlene Einstellung für die Fehlerbehandlung. PDO wirft bei Fehlern eine &amp;lt;code&amp;gt;PDOException&amp;lt;/code&amp;gt;, die abgefangen werden kann.[1, 2, 4]&lt;br /&gt;
*** &amp;lt;code&amp;gt;PDO::ATTR_DEFAULT_FETCH_MODE =&amp;amp;gt; PDO::FETCH_ASSOC&amp;lt;/code&amp;gt;: Legt fest, dass Daten standardmäßig als assoziatives Array (&amp;lt;code&amp;gt;spaltenname =&amp;amp;gt; wert&amp;lt;/code&amp;gt;) zurückgegeben werden, was oft die Weiterverarbeitung erleichtert.[1, 9]&lt;br /&gt;
*** &amp;lt;code&amp;gt;PDO::ATTR_EMULATE_PREPARES =&amp;amp;gt; false&amp;lt;/code&amp;gt;: Deaktiviert die Emulation von Prepared Statements durch PDO und zwingt die Verwendung nativer Prepared Statements des Datenbanktreibers. Dies gilt als die sicherste und oft performanteste Methode.[1, 10, 9, 11] Standardmäßig ist die Emulation aktiviert (&amp;lt;code&amp;gt;true&amp;lt;/code&amp;gt;). Obwohl PDO auch im emulierten Modus durch korrektes Escaping Schutz vor SQL Injection bietet [12, 5, 13], eliminiert die Deaktivierung potenzielle, wenn auch seltene, Sicherheitsrisiken, die in bestimmten Konstellationen (z.B. bei Verwendung exotischer Zeichensätze wie GBK [11]) auftreten könnten. Die explizite Deaktivierung ist daher eine wichtige Best Practice.&lt;br /&gt;
*** &amp;lt;code&amp;gt;PDO::ATTR_PERSISTENT =&amp;amp;gt; true&amp;lt;/code&amp;gt;: Aktiviert persistente Datenbankverbindungen. Diese werden nicht am Ende des Skripts geschlossen, sondern in einem Pool gehalten und für nachfolgende Anfragen wiederverwendet. Dies kann die Performance von Webanwendungen verbessern, da der Overhead des Verbindungsaufbaus entfällt. Die Verwendung erfordert jedoch sorgfältige Überlegung, da der Zustand der Verbindung zwischen Anfragen bestehen bleiben kann.[7]&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Fehlerbehandlung:&amp;#039;&amp;#039;&amp;#039; Der Verbindungsaufbau sollte immer in einem &amp;lt;code&amp;gt;try...catch&amp;lt;/code&amp;gt;-Block erfolgen, um &amp;lt;code&amp;gt;PDOException&amp;lt;/code&amp;gt; abzufangen und angemessen darauf zu reagieren (z.B. Fehlermeldung loggen, alternative Aktion ausführen).[1, 7] Es ist kritisch, detaillierte Fehlermeldungen in Produktionsumgebungen nicht an den Benutzer auszugeben, da sie sensible Informationen enthalten könnten. Die PHP-Einstellung &amp;lt;code&amp;gt;display_errors&amp;lt;/code&amp;gt; sollte daher auf &amp;lt;code&amp;gt;0&amp;lt;/code&amp;gt; gesetzt sein.[7]&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Verbindung schließen:&amp;#039;&amp;#039;&amp;#039; Eine PDO-Verbindung bleibt für die Lebensdauer des PDO-Objekts aktiv. Um sie explizit zu schließen, müssen alle Referenzen auf das PDO-Objekt und alle davon abgeleiteten &amp;lt;code&amp;gt;PDOStatement&amp;lt;/code&amp;gt;-Objekte entfernt werden, typischerweise durch Zuweisung von &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt;.[7] PHP schließt die Verbindung automatisch am Ende des Skriptlaufs, wenn dies nicht manuell geschieht.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Code-Beispiel (PDO-Verbindung zu MySQL):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!--?php$host = &amp;#039;localhost&amp;#039;;&lt;br /&gt;
$dbname = &amp;#039;meine_datenbank&amp;#039;;&lt;br /&gt;
$username = &amp;#039;mein_benutzer&amp;#039;;&lt;br /&gt;
$password = &amp;#039;mein_passwort&amp;#039;;&lt;br /&gt;
$charset = &amp;#039;utf8mb4&amp;#039;;&lt;br /&gt;
&lt;br /&gt;
$dsn = &amp;quot;mysql:host=$host;dbname=$dbname;charset=$charset&amp;quot;;&lt;br /&gt;
$options =;&lt;br /&gt;
&lt;br /&gt;
try {&lt;br /&gt;
     $pdo = new PDO($dsn, $username, $password, $options);&lt;br /&gt;
     // Erfolgreich verbunden, $pdo-Objekt kann nun verwendet werden.&lt;br /&gt;
     // echo &amp;quot;Verbindung erfolgreich hergestellt!&amp;quot;;&lt;br /&gt;
} catch (\PDOException $e) {&lt;br /&gt;
     // Fehler beim Verbindungsaufbau&lt;br /&gt;
     // In Produktion: Fehler loggen, statt ausgeben&lt;br /&gt;
     error_log(&amp;quot;Datenbankverbindungsfehler: &amp;quot;. $e---&amp;gt;getMessage());&lt;br /&gt;
 die(&amp;quot;Es gab ein Problem mit der Datenbankverbindung. Bitte versuchen Sie es später erneut.&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//... hier Datenbankoperationen mit $pdo durchführen...&lt;br /&gt;
&lt;br /&gt;
// Verbindung explizit schließen (optional, da PHP dies am Skriptende tut)&lt;br /&gt;
// $pdo = null;&lt;br /&gt;
?&amp;amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Dieses Beispiel zeigt den empfohlenen Weg, eine PDO-Verbindung zu MySQL herzustellen, inklusive wichtiger Optionen für Fehlerbehandlung und Sicherheit sowie einem &amp;lt;code&amp;gt;try...catch&amp;lt;/code&amp;gt;-Block zum Abfangen von Verbindungsfehlern.&lt;br /&gt;
&lt;br /&gt;
=== MySQLi (MySQL Improved) ===&lt;br /&gt;
&lt;br /&gt;
Die MySQLi-Erweiterung ist, wie der Name schon sagt, speziell für die Interaktion mit MySQL-Datenbanken konzipiert und bietet Unterstützung für Funktionen, die ab MySQL Version 4.1 eingeführt wurden.[14, 15] Sie ermöglicht den Zugriff auf MySQL-spezifische Features, die über PDO möglicherweise nicht direkt verfügbar sind.[16, 15]&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Duale Schnittstelle:&amp;#039;&amp;#039;&amp;#039; Ein charakteristisches Merkmal von MySQLi ist die Unterstützung von zwei Programmierstilen [17]:&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Prozedural:&amp;#039;&amp;#039;&amp;#039; Dieser Stil ähnelt den alten &amp;lt;code&amp;gt;mysql_*&amp;lt;/code&amp;gt; Funktionen. Die Funktionsnamen beginnen typischerweise mit &amp;lt;code&amp;gt;mysqli_&amp;lt;/code&amp;gt; (z.B. &amp;lt;code&amp;gt;mysqli_connect()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;mysqli_query()&amp;lt;/code&amp;gt;). Der Datenbankverbindungs-Handle (eine Ressource oder ein Objekt) muss dabei explizit als erster Parameter an die meisten Funktionen übergeben werden.[17] Dieser Stil wird oft von Entwicklern bevorzugt, die von der alten &amp;lt;code&amp;gt;mysql&amp;lt;/code&amp;gt;-Erweiterung migrieren.[17]&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Objektorientiert (OO):&amp;#039;&amp;#039;&amp;#039; Hier wird ein Objekt der &amp;lt;code&amp;gt;mysqli&amp;lt;/code&amp;gt;-Klasse erstellt, und Datenbankoperationen werden als Methoden dieses Objekts aufgerufen (z.B. &amp;lt;code&amp;gt;$mysqli-&amp;amp;gt;query()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;$mysqli-&amp;amp;gt;prepare()&amp;lt;/code&amp;gt;).[17] Dieser Ansatz passt oft besser zu modernen PHP-Entwicklungspraktiken und vermeidet die Notwendigkeit, den Verbindungshandle ständig zu übergeben. Die offizielle PHP-Dokumentation für MySQLi ist primär im objektorientierten Stil verfasst.[17] Es gibt keine nennenswerten Performance-Unterschiede zwischen den beiden Stilen; die Wahl ist meist eine Frage der persönlichen Präferenz oder Projektkonventionen.[17]&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Verbindungsaufbau mit MySQLi:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Prozedural:&amp;#039;&amp;#039;&amp;#039; Die Funktion &amp;lt;code&amp;gt;mysqli_connect()&amp;lt;/code&amp;gt; wird verwendet: &amp;lt;code&amp;gt;$link = mysqli_connect($host, $user, $pass, $db, $port, $socket);&amp;lt;/code&amp;gt;. Sie gibt bei Erfolg ein &amp;lt;code&amp;gt;mysqli&amp;lt;/code&amp;gt;-Objekt (oder &amp;lt;code&amp;gt;false&amp;lt;/code&amp;gt; bei Fehlern vor PHP 8.1) zurück.[17, 18] Fehler können mit &amp;lt;code&amp;gt;mysqli_connect_errno()&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;mysqli_connect_error()&amp;lt;/code&amp;gt; geprüft werden.[18]&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Objektorientiert:&amp;#039;&amp;#039;&amp;#039; Ein neues &amp;lt;code&amp;gt;mysqli&amp;lt;/code&amp;gt;-Objekt wird instanziiert: &amp;lt;code&amp;gt;$mysqli = new mysqli($host, $user, $pass, $db, $port, $socket);&amp;lt;/code&amp;gt;. Wichtig: Der Konstruktor gibt &amp;#039;&amp;#039;immer&amp;#039;&amp;#039; ein Objekt zurück, auch wenn die Verbindung fehlschlägt. Der Verbindungsstatus muss explizit über die Eigenschaften &amp;lt;code&amp;gt;$mysqli-&amp;amp;gt;connect_errno&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;$mysqli-&amp;amp;gt;connect_error&amp;lt;/code&amp;gt; geprüft werden.[17, 18]&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Fortgeschrittene Verbindung:&amp;#039;&amp;#039;&amp;#039; Für feinere Kontrolle über Verbindungsoptionen (z.B. Setzen von Timeouts oder Initialisierungsbefehlen &amp;#039;&amp;#039;vor&amp;#039;&amp;#039; dem Verbindungsaufbau) können &amp;lt;code&amp;gt;mysqli_init()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;mysqli_options()&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;mysqli_real_connect()&amp;lt;/code&amp;gt; verwendet werden.[18, 19]&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Fehlerbehandlung:&amp;#039;&amp;#039;&amp;#039; Neben der manuellen Prüfung von Fehlercodes und -meldungen ist die empfohlene Methode, MySQLi so zu konfigurieren, dass es bei Fehlern Exceptions wirft. Dies geschieht mit &amp;lt;code&amp;gt;mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);&amp;lt;/code&amp;gt;. Danach können Datenbankfehler elegant mit &amp;lt;code&amp;gt;try...catch&amp;lt;/code&amp;gt;-Blöcken behandelt werden, ähnlich wie bei PDO.[18, 20, 21, 22, 23, 24, 25, 26]&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Verbindung schließen:&amp;#039;&amp;#039;&amp;#039; Prozedural mit &amp;lt;code&amp;gt;mysqli_close($link)&amp;lt;/code&amp;gt;, objektorientiert mit &amp;lt;code&amp;gt;$mysqli-&amp;amp;gt;close()&amp;lt;/code&amp;gt;.[17, 18, 19]&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Persistente Verbindungen:&amp;#039;&amp;#039;&amp;#039; MySQLi unterstützt ebenfalls persistente Verbindungen, die über PHP-Konfigurationseinstellungen (&amp;lt;code&amp;gt;mysqli.allow_persistent&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;mysqli.max_persistent&amp;lt;/code&amp;gt;) gesteuert werden.[27] Standardmäßig setzt MySQLi den Zustand einer wiederverwendeten persistenten Verbindung zurück (via &amp;lt;code&amp;gt;mysqli::change_user()&amp;lt;/code&amp;gt;), was zwar für Konsistenz sorgt, aber auch Performance kosten kann.[27]&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Code-Beispiele (MySQLi-Verbindungen):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Objektorientiert (mit Exception-Handling):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!--?php$host = &amp;#039;localhost&amp;#039;;&lt;br /&gt;
$user = &amp;#039;mein_benutzer&amp;#039;;&lt;br /&gt;
$pass = &amp;#039;mein_passwort&amp;#039;;&lt;br /&gt;
$db = &amp;#039;meine_datenbank&amp;#039;;&lt;br /&gt;
&lt;br /&gt;
// Fehlerberichterstattung für Exceptions aktivieren&lt;br /&gt;
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);&lt;br /&gt;
&lt;br /&gt;
try {&lt;br /&gt;
    $mysqli = new mysqli($host, $user, $pass, $db);&lt;br /&gt;
    // Zeichensatz setzen (wichtig!)&lt;br /&gt;
    $mysqli---&amp;gt;set_charset(&amp;#039;utf8mb4&amp;#039;);&lt;br /&gt;
 // Erfolgreich verbunden&lt;br /&gt;
 // echo &amp;quot;Verbindung erfolgreich (OO)! Host-Info: &amp;quot;. $mysqli-&amp;amp;gt;host_info;&lt;br /&gt;
} catch (mysqli_sql_exception $e) {&lt;br /&gt;
 // Fehler beim Verbindungsaufbau oder set_charset&lt;br /&gt;
 error_log(&amp;quot;MySQLi Verbindungsfehler: &amp;quot;. $e-&amp;amp;gt;getMessage());&lt;br /&gt;
 die(&amp;quot;Es gab ein Problem mit der Datenbankverbindung.&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//... Datenbankoperationen mit $mysqli...&lt;br /&gt;
&lt;br /&gt;
// Verbindung schließen&lt;br /&gt;
// $mysqli-&amp;amp;gt;close();&lt;br /&gt;
?&amp;amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Prozedural (mit Exception-Handling):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!--?php$host = &amp;#039;localhost&amp;#039;;&lt;br /&gt;
$user = &amp;#039;mein_benutzer&amp;#039;;&lt;br /&gt;
$pass = &amp;#039;mein_passwort&amp;#039;;&lt;br /&gt;
$db = &amp;#039;meine_datenbank&amp;#039;;&lt;br /&gt;
&lt;br /&gt;
// Fehlerberichterstattung für Exceptions aktivieren&lt;br /&gt;
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);&lt;br /&gt;
&lt;br /&gt;
try {&lt;br /&gt;
    $link = mysqli_connect($host, $user, $pass, $db);&lt;br /&gt;
    // Zeichensatz setzen&lt;br /&gt;
    mysqli_set_charset($link, &amp;#039;utf8mb4&amp;#039;);&lt;br /&gt;
    // Erfolgreich verbunden&lt;br /&gt;
    // echo &amp;quot;Verbindung erfolgreich (Prozedural)! Host-Info: &amp;quot;. mysqli_get_host_info($link);&lt;br /&gt;
} catch (mysqli_sql_exception $e) {&lt;br /&gt;
    // Fehler beim Verbindungsaufbau oder set_charset&lt;br /&gt;
    error_log(&amp;quot;MySQLi Verbindungsfehler: &amp;quot;. $e---&amp;gt;getMessage());&lt;br /&gt;
 die(&amp;quot;Es gab ein Problem mit der Datenbankverbindung.&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//... Datenbankoperationen mit $link...&lt;br /&gt;
&lt;br /&gt;
// Verbindung schließen&lt;br /&gt;
// mysqli_close($link);&lt;br /&gt;
?&amp;amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Bedeutung von &amp;lt;code&amp;gt;mysqlnd&amp;lt;/code&amp;gt;:&amp;#039;&amp;#039;&amp;#039; Die Benutzerfreundlichkeit, insbesondere bei der Arbeit mit Prepared Statements, wird stark durch den verwendeten MySQL-Treiber beeinflusst. Der &amp;#039;&amp;#039;&amp;#039;MySQL Native Driver (&amp;lt;code&amp;gt;mysqlnd&amp;lt;/code&amp;gt;)&amp;#039;&amp;#039;&amp;#039;, der seit PHP 5.4 standardmäßig enthalten ist [16], ist hier entscheidend. &amp;lt;code&amp;gt;mysqlnd&amp;lt;/code&amp;gt; stellt die Methode &amp;lt;code&amp;gt;get_result()&amp;lt;/code&amp;gt; (bzw. die Funktion &amp;lt;code&amp;gt;mysqli_stmt_get_result()&amp;lt;/code&amp;gt;) zur Verfügung.[25, 15] Diese Methode erlaubt es, nach dem Ausführen eines Prepared Statements ein Standard-&amp;lt;code&amp;gt;mysqli_result&amp;lt;/code&amp;gt;-Objekt zu erhalten, von dem dann wie gewohnt mit &amp;lt;code&amp;gt;fetch_assoc()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;fetch_all()&amp;lt;/code&amp;gt; etc. die Ergebnisse abgerufen werden können.[25] Ohne &amp;lt;code&amp;gt;mysqlnd&amp;lt;/code&amp;gt; (bei Verwendung der älteren &amp;lt;code&amp;gt;libmysqlclient&amp;lt;/code&amp;gt;-Bibliothek) ist das Abrufen von Ergebnissen aus Prepared Statements deutlich umständlicher und erfordert die manuelle Bindung jeder einzelnen Ergebnisspalte an PHP-Variablen mittels &amp;lt;code&amp;gt;bind_result()&amp;lt;/code&amp;gt; und anschließendes &amp;lt;code&amp;gt;fetch()&amp;lt;/code&amp;gt;.[25] Da &amp;lt;code&amp;gt;mysqlnd&amp;lt;/code&amp;gt; heute der Standard ist, konzentrieren sich moderne Tutorials und Beispiele meist auf die &amp;lt;code&amp;gt;get_result()&amp;lt;/code&amp;gt;-Methode, aber es ist wichtig, diesen Hintergrund zu kennen, falls man auf ältere Systeme oder Konfigurationen trifft.&lt;br /&gt;
&lt;br /&gt;
=== Veraltete &amp;lt;code&amp;gt;mysql_*&amp;lt;/code&amp;gt; Funktionen ===&lt;br /&gt;
&lt;br /&gt;
Die &amp;lt;code&amp;gt;mysql_*&amp;lt;/code&amp;gt; Funktionsfamilie (&amp;lt;code&amp;gt;mysql_connect&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;mysql_query&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;mysql_fetch_assoc&amp;lt;/code&amp;gt; etc.) war die ursprüngliche Methode, um in PHP mit MySQL-Datenbanken zu interagieren. Diese Erweiterung ist jedoch &amp;#039;&amp;#039;&amp;#039;stark veraltet&amp;#039;&amp;#039;&amp;#039; und sollte &amp;#039;&amp;#039;&amp;#039;unter keinen Umständen mehr für neue Projekte verwendet werden&amp;#039;&amp;#039;&amp;#039;.&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Status:&amp;#039;&amp;#039;&amp;#039; Die &amp;lt;code&amp;gt;mysql_*&amp;lt;/code&amp;gt; Erweiterung wurde in &amp;#039;&amp;#039;&amp;#039;PHP 5.5&amp;#039;&amp;#039;&amp;#039; offiziell als &amp;quot;deprecated&amp;quot; (veraltet) markiert, was bedeutet, dass ihre Verwendung ab dieser Version Warnungen (&amp;lt;code&amp;gt;E_DEPRECATED&amp;lt;/code&amp;gt;) erzeugte.[28, 29] Mit der Veröffentlichung von &amp;#039;&amp;#039;&amp;#039;PHP 7.0&amp;#039;&amp;#039;&amp;#039; wurde die Erweiterung &amp;#039;&amp;#039;&amp;#039;vollständig entfernt&amp;#039;&amp;#039;&amp;#039;.[28, 29] PHP-Code, der diese Funktionen nutzt, führt auf PHP 7.0 oder neuer zu fatalen Fehlern und funktioniert nicht mehr.[28, 29]&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Gründe für die Entfernung:&amp;#039;&amp;#039;&amp;#039; Es gab zwingende Gründe für diesen Schritt:&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Gravierende Sicherheitsrisiken:&amp;#039;&amp;#039;&amp;#039; Der Hauptgrund war die Anfälligkeit für SQL-Injection-Angriffe. Die &amp;lt;code&amp;gt;mysql_*&amp;lt;/code&amp;gt; Funktionen boten keinen eingebauten Mechanismus wie Prepared Statements. Entwickler mussten Benutzereingaben manuell mit &amp;lt;code&amp;gt;mysql_real_escape_string()&amp;lt;/code&amp;gt; &amp;quot;escapen&amp;quot;, eine Methode, die leicht vergessen oder falsch angewendet werden konnte, was zu weit verbreiteten Sicherheitslücken führte.[29, 30] Das Fehlen von Prepared Statements in dieser alten Erweiterung ist direkt ursächlich für die hohe Zahl an SQL-Injection-Schwachstellen in älteren PHP-Anwendungen.[29]&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Fehlende Unterstützung moderner MySQL-Features:&amp;#039;&amp;#039;&amp;#039; Die Erweiterung wurde ursprünglich für MySQL Version 3.23 entwickelt und seitdem kaum weiterentwickelt.[29] Sie unterstützte viele wichtige Funktionen moderner MySQL-Versionen nicht, darunter Prepared Statements, Stored Procedures, Transaktionen, SSL-Verschlüsselung, Kompression, Multi-Statements und vollständige Zeichensatzunterstützung.[29]&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Schlechte Wartbarkeit:&amp;#039;&amp;#039;&amp;#039; Der Code der Erweiterung war veraltet und wurde immer schwieriger zu warten und an neue Versionen der MySQL-Client-Bibliotheken anzupassen.[29]&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Förderung unsicherer Praktiken:&amp;#039;&amp;#039;&amp;#039; Da viele alte Tutorials und Codebeispiele die &amp;lt;code&amp;gt;mysql_*&amp;lt;/code&amp;gt; Funktionen unsicher verwendeten, trug die fortgesetzte Verfügbarkeit zur Verbreitung schlechter und unsicherer Programmierpraktiken bei.[29]&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Verwendung heute:&amp;#039;&amp;#039;&amp;#039; Die klare Antwort lautet: &amp;#039;&amp;#039;&amp;#039;Nein&amp;#039;&amp;#039;&amp;#039;. Diese Funktionen existieren in modernen PHP-Versionen nicht mehr.[31, 30] Jede Anwendung, die sie noch verwendet, ist nicht nur unsicher, sondern auch inkompatibel mit aktueller PHP-Software. Eine Migration zu PDO oder MySQLi ist unumgänglich, um Sicherheit und Kompatibilität zu gewährleisten.[28, 29] Die erzwungene Migration durch die Entfernung in PHP 7.0 unterstreicht, wie wichtig es ist, Deprecation-Warnungen ernst zu nehmen und Code proaktiv zu aktualisieren, um zukünftige Probleme zu vermeiden.[29, 32]&lt;br /&gt;
&lt;br /&gt;
=== Tabelle 1: Vergleich der PHP-Datenbankerweiterungen für MySQL ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Merkmal !! PDO (PHP Data Objects) !! MySQLi (MySQL Improved) !! &amp;lt;code&amp;gt;mysql_*&amp;lt;/code&amp;gt; (Veraltet)&lt;br /&gt;
|-&amp;lt;br&amp;gt;&lt;br /&gt;
| aktiv=&amp;quot;&amp;quot; empfohlen=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| aktiv=&amp;quot;&amp;quot; empfohlen=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Entfernt (seit PHP 7.0) [29]&lt;br /&gt;
|-&amp;lt;br&amp;gt;&lt;br /&gt;
| objektorientiert=&amp;quot;&amp;quot; oo=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| oo=&amp;quot;&amp;quot; prozedural=&amp;quot;&amp;quot; 17=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Prozedural&lt;br /&gt;
|-&amp;lt;br&amp;gt;&lt;br /&gt;
| multi-datenbank=&amp;quot;&amp;quot; mysql=&amp;quot;&amp;quot; pgsql=&amp;quot;&amp;quot; sqlite=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| nur=&amp;quot;&amp;quot; mysql=&amp;quot;&amp;quot; 15=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Nur MySQL&lt;br /&gt;
|-&amp;lt;br&amp;gt;&lt;br /&gt;
| ja=&amp;quot;&amp;quot; nativ=&amp;quot;&amp;quot; empfohlen=&amp;quot;&amp;quot; emulation=&amp;quot;&amp;quot; m=&amp;quot;&amp;quot; glich=&amp;quot;&amp;quot; 1=&amp;quot;&amp;quot; 11=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| ja=&amp;quot;&amp;quot; nativ=&amp;quot;&amp;quot; 25=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Nein [29]&lt;br /&gt;
|- &lt;br /&gt;
| &amp;#039;&amp;#039;&amp;#039;SQL Injection Schutz&amp;#039;&amp;#039;&amp;#039; || sehr=&amp;quot;&amp;quot; gut=&amp;quot;&amp;quot; durch=&amp;quot;&amp;quot; prepared=&amp;quot;&amp;quot; statements=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Sehr gut (durch Prepared Statements) [25] || Schlecht (manuelles Escaping nötig) [29]&lt;br /&gt;
|-&amp;lt;br&amp;gt;&lt;br /&gt;
| gute=&amp;quot;&amp;quot; unterst=&amp;quot;&amp;quot; tzung=&amp;quot;&amp;quot; via=&amp;quot;&amp;quot; treiber=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Sehr gute Unterstützung (spezifisch) [15] || Veraltet, viele Features fehlen [29]&lt;br /&gt;
|-&amp;lt;br&amp;gt;&lt;br /&gt;
| hoch=&amp;quot;&amp;quot; 2=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| niedrig=&amp;quot;&amp;quot; mysql-spezifisch=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Niedrig&lt;br /&gt;
|-&amp;lt;br&amp;gt;&lt;br /&gt;
| exceptions=&amp;quot;&amp;quot; mwt-preservehtml=&amp;quot;&amp;quot; pdoexception=&amp;quot;&amp;quot; code=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| exceptions=&amp;quot;&amp;quot; mit=&amp;quot;&amp;quot; mwt-preservehtml=&amp;quot;&amp;quot; mysqli_report=&amp;quot;&amp;quot; code=&amp;quot;&amp;quot; fehlercodes=&amp;quot;&amp;quot; 18=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Fehlercodes / &amp;lt;code&amp;gt;mysql_error()&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
| &amp;#039;&amp;#039;&amp;#039;Nicht verwenden!&amp;#039;&amp;#039;&amp;#039; [29]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Grundlegende Datenbankoperationen (CRUD) ==&lt;br /&gt;
&lt;br /&gt;
Nachdem wir die verschiedenen Erweiterungen kennengelernt haben, wenden wir uns nun den grundlegenden Operationen zu, die man typischerweise mit einer Datenbank durchführt. Diese werden oft mit dem Akronym CRUD zusammengefasst:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;C&amp;#039;&amp;#039;&amp;#039;reate: Neue Datensätze erstellen (SQL: &amp;lt;code&amp;gt;INSERT&amp;lt;/code&amp;gt;).&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;R&amp;#039;&amp;#039;&amp;#039;ead: Vorhandene Datensätze lesen/abfragen (SQL: &amp;lt;code&amp;gt;SELECT&amp;lt;/code&amp;gt;).&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;U&amp;#039;&amp;#039;&amp;#039;pdate: Bestehende Datensätze ändern (SQL: &amp;lt;code&amp;gt;UPDATE&amp;lt;/code&amp;gt;).&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;D&amp;#039;&amp;#039;&amp;#039;elete: Datensätze löschen (SQL: &amp;lt;code&amp;gt;DELETE&amp;lt;/code&amp;gt;).&lt;br /&gt;
In den folgenden Abschnitten werden wir sehen, wie diese Operationen mit den modernen Erweiterungen PDO und MySQLi umgesetzt werden, wobei wir durchgehend &amp;#039;&amp;#039;&amp;#039;Prepared Statements&amp;#039;&amp;#039;&amp;#039; verwenden, um die Sicherheit zu gewährleisten.&lt;br /&gt;
&lt;br /&gt;
=== Daten abfragen (SELECT) ===&lt;br /&gt;
&lt;br /&gt;
Das Abfragen von Daten ist eine der häufigsten Datenbankoperationen.&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;PDO:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Ohne Parameter:&amp;#039;&amp;#039;&amp;#039; Wenn die Abfrage keine variablen Teile enthält (z.B. keine &amp;lt;code&amp;gt;WHERE&amp;lt;/code&amp;gt;-Klausel mit Benutzereingaben), kann die &amp;lt;code&amp;gt;query()&amp;lt;/code&amp;gt;-Methode verwendet werden. Sie führt die SQL-Abfrage direkt aus und gibt ein &amp;lt;code&amp;gt;PDOStatement&amp;lt;/code&amp;gt;-Objekt zurück, das zur Ergebnisauswertung dient.[2, 33]&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Beispiel: Alle Benutzer auswählen&lt;br /&gt;
$stmt = $pdo-&amp;amp;gt;query(&amp;quot;SELECT id, username FROM users&amp;quot;);&lt;br /&gt;
// $stmt kann nun zum Fetchen der Ergebnisse verwendet werden&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Mit Parametern (Prepared Statement):&amp;#039;&amp;#039;&amp;#039; Dies ist der Standardfall, wenn Benutzereingaben oder andere Variablen die Abfrage beeinflussen (z.B. in &amp;lt;code&amp;gt;WHERE&amp;lt;/code&amp;gt;- oder &amp;lt;code&amp;gt;LIMIT&amp;lt;/code&amp;gt;-Klauseln).&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Vorbereiten (&amp;lt;code&amp;gt;prepare&amp;lt;/code&amp;gt;):&amp;#039;&amp;#039;&amp;#039; Die SQL-Abfrage wird mit Platzhaltern (&amp;lt;code&amp;gt;?&amp;lt;/code&amp;gt; für positionale oder &amp;lt;code&amp;gt;:name&amp;lt;/code&amp;gt; für benannte Platzhalter) vorbereitet.[34, 33, 5]&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Beispiel: Benutzer mit bestimmter ID auswählen (positionaler Platzhalter)&lt;br /&gt;
$sql = &amp;quot;SELECT id, username, email FROM users WHERE id =?&amp;quot;;&lt;br /&gt;
$stmt = $pdo-&amp;amp;gt;prepare($sql);&lt;br /&gt;
&lt;br /&gt;
// Beispiel: Benutzer mit bestimmtem Status auswählen (benannter Platzhalter)&lt;br /&gt;
$sql_named = &amp;quot;SELECT id, username FROM users WHERE status = :status&amp;quot;;&lt;br /&gt;
$stmt_named = $pdo-&amp;amp;gt;prepare($sql_named);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
*# &amp;#039;&amp;#039;&amp;#039;Ausführen (&amp;lt;code&amp;gt;execute&amp;lt;/code&amp;gt;):&amp;#039;&amp;#039;&amp;#039; Die vorbereitete Anweisung wird mit den tatsächlichen Werten ausgeführt. Die Werte werden als Array an die &amp;lt;code&amp;gt;execute()&amp;lt;/code&amp;gt;-Methode übergeben. Bei positionalen Platzhaltern entspricht die Reihenfolge im Array der Reihenfolge der &amp;lt;code&amp;gt;?&amp;lt;/code&amp;gt; in der SQL-Abfrage. Bei benannten Platzhaltern wird ein assoziatives Array verwendet, dessen Schlüssel den Platzhalternamen entsprechen (mit oder ohne führenden Doppelpunkt).[34, 33, 5]&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Ausführen für positionalen Platzhalter&lt;br /&gt;
$user_id = 123;&lt;br /&gt;
$stmt-&amp;amp;gt;execute([$user_id]);&lt;br /&gt;
&lt;br /&gt;
// Ausführen für benannten Platzhalter&lt;br /&gt;
$user_status = &amp;#039;active&amp;#039;;&lt;br /&gt;
$stmt_named-&amp;amp;gt;execute([&amp;#039;:status&amp;#039; =&amp;amp;gt; $user_status]);&lt;br /&gt;
// oder: $stmt_named-&amp;amp;gt;execute([&amp;#039;status&amp;#039; =&amp;amp;gt; $user_status]);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
*# &amp;#039;&amp;#039;&amp;#039;Alternative Bindung:&amp;#039;&amp;#039;&amp;#039; Optional können Parameter auch explizit vor dem &amp;lt;code&amp;gt;execute()&amp;lt;/code&amp;gt;-Aufruf mit &amp;lt;code&amp;gt;bindValue()&amp;lt;/code&amp;gt; (bindet einen Wert) oder &amp;lt;code&amp;gt;bindParam()&amp;lt;/code&amp;gt; (bindet eine Variable als Referenz) gebunden werden. Dies gibt mehr Kontrolle über den Datentyp (z.B. &amp;lt;code&amp;gt;PDO::PARAM_INT&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;PDO::PARAM_STR&amp;lt;/code&amp;gt;).[5, 13]&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$stmt-&amp;amp;gt;bindValue(1, $user_id, PDO::PARAM_INT); // Position 1, Wert von $user_id als Integer&lt;br /&gt;
$stmt-&amp;amp;gt;execute();&lt;br /&gt;
&lt;br /&gt;
$stmt_named-&amp;amp;gt;bindParam(&amp;#039;:status&amp;#039;, $user_status, PDO::PARAM_STR); // Platzhalter :status, Variable $user_status als String&lt;br /&gt;
$stmt_named-&amp;amp;gt;execute();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;MySQLi:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Ohne Parameter:&amp;#039;&amp;#039;&amp;#039; Ähnlich wie bei PDO kann bei Abfragen ohne variable Teile die &amp;lt;code&amp;gt;query()&amp;lt;/code&amp;gt;-Methode (OO-Stil: &amp;lt;code&amp;gt;$mysqli-&amp;amp;gt;query()&amp;lt;/code&amp;gt;) oder die &amp;lt;code&amp;gt;mysqli_query()&amp;lt;/code&amp;gt;-Funktion (prozeduraler Stil: &amp;lt;code&amp;gt;mysqli_query($link, &amp;quot;...&amp;quot;)&amp;lt;/code&amp;gt;) verwendet werden. Sie geben ein &amp;lt;code&amp;gt;mysqli_result&amp;lt;/code&amp;gt;-Objekt zurück.[20, 25]&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// OO-Stil&lt;br /&gt;
$result = $mysqli-&amp;amp;gt;query(&amp;quot;SELECT id, username FROM users&amp;quot;);&lt;br /&gt;
// Prozedural&lt;br /&gt;
$result = mysqli_query($link, &amp;quot;SELECT id, username FROM users&amp;quot;);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Mit Parametern (Prepared Statement):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Vorbereiten (&amp;lt;code&amp;gt;prepare&amp;lt;/code&amp;gt;):&amp;#039;&amp;#039;&amp;#039; Die SQL-Abfrage wird mit &amp;lt;code&amp;gt;?&amp;lt;/code&amp;gt;-Platzhaltern vorbereitet. Im OO-Stil &amp;lt;code&amp;gt;$stmt = $mysqli-&amp;amp;gt;prepare(&amp;quot;...&amp;quot;)&amp;lt;/code&amp;gt;, prozedural &amp;lt;code&amp;gt;$stmt = mysqli_prepare($link, &amp;quot;...&amp;quot;)&amp;lt;/code&amp;gt;.[25, 35]&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// OO-Stil&lt;br /&gt;
$sql = &amp;quot;SELECT id, username, email FROM users WHERE id =?&amp;quot;;&lt;br /&gt;
$stmt = $mysqli-&amp;amp;gt;prepare($sql);&lt;br /&gt;
// Prozedural&lt;br /&gt;
$sql_proc = &amp;quot;SELECT id, username, email FROM users WHERE id =?&amp;quot;;&lt;br /&gt;
$stmt_proc = mysqli_prepare($link, $sql_proc);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
**# &amp;#039;&amp;#039;&amp;#039;Parameter binden (&amp;lt;code&amp;gt;bind_param&amp;lt;/code&amp;gt;):&amp;#039;&amp;#039;&amp;#039; Die Variablen werden an die Platzhalter gebunden. Dies ist ein wesentlicher Unterschied zu PDOs &amp;lt;code&amp;gt;execute&amp;lt;/code&amp;gt;-Array-Bindung. &amp;lt;code&amp;gt;bind_param()&amp;lt;/code&amp;gt; erfordert einen String, der die Datentypen der zu bindenden Variablen angibt (z.B. &amp;quot;i&amp;quot; für Integer, &amp;quot;s&amp;quot; für String, &amp;quot;d&amp;quot; für Double/Float, &amp;quot;b&amp;quot; für Blob), gefolgt von den Variablen selbst.[25, 35]&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$user_id = 123;&lt;br /&gt;
// OO-Stil&lt;br /&gt;
$stmt-&amp;amp;gt;bind_param(&amp;quot;i&amp;quot;, $user_id); // &amp;quot;i&amp;quot; für Integer&lt;br /&gt;
// Prozedural&lt;br /&gt;
mysqli_stmt_bind_param($stmt_proc, &amp;quot;i&amp;quot;, $user_id);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
**# &amp;#039;&amp;#039;&amp;#039;Ausführen (&amp;lt;code&amp;gt;execute&amp;lt;/code&amp;gt;):&amp;#039;&amp;#039;&amp;#039; Die vorbereitete Anweisung wird ausgeführt. OO-Stil: &amp;lt;code&amp;gt;$stmt-&amp;amp;gt;execute()&amp;lt;/code&amp;gt;, prozedural: &amp;lt;code&amp;gt;mysqli_stmt_execute($stmt)&amp;lt;/code&amp;gt;.[25, 36, 35]&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// OO-Stil&lt;br /&gt;
$stmt-&amp;amp;gt;execute();&lt;br /&gt;
// Prozedural&lt;br /&gt;
mysqli_stmt_execute($stmt_proc);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
**# &amp;#039;&amp;#039;&amp;#039;Ergebnis holen:&amp;#039;&amp;#039;&amp;#039; Um die Ergebnisse eines Prepared Statements in MySQLi zu erhalten, ist die Methode &amp;lt;code&amp;gt;get_result()&amp;lt;/code&amp;gt; (OO) bzw. &amp;lt;code&amp;gt;mysqli_stmt_get_result()&amp;lt;/code&amp;gt; (prozedural) am bequemsten. Sie gibt ein Standard-&amp;lt;code&amp;gt;mysqli_result&amp;lt;/code&amp;gt;-Objekt zurück, das dann mit den üblichen Fetch-Methoden verarbeitet werden kann. &amp;#039;&amp;#039;&amp;#039;Wichtig:&amp;#039;&amp;#039;&amp;#039; Diese Methode erfordert den &amp;lt;code&amp;gt;mysqlnd&amp;lt;/code&amp;gt;-Treiber.[25, 15]&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// OO-Stil&lt;br /&gt;
$result = $stmt-&amp;amp;gt;get_result();&lt;br /&gt;
// Prozedural&lt;br /&gt;
$result_proc = mysqli_stmt_get_result($stmt_proc);&lt;br /&gt;
// $result / $result_proc kann nun mit fetch_assoc() etc. verwendet werden.&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Die Alternative ohne &amp;lt;code&amp;gt;mysqlnd&amp;lt;/code&amp;gt; ist &amp;lt;code&amp;gt;bind_result()&amp;lt;/code&amp;gt;, bei der jede Ergebnisspalte an eine PHP-Variable gebunden wird, und &amp;lt;code&amp;gt;fetch()&amp;lt;/code&amp;gt; iterativ aufgerufen wird, um die Variablen zu füllen.[25] Dies ist deutlich umständlicher.&lt;br /&gt;
&lt;br /&gt;
=== Ergebnisse abrufen und verarbeiten ===&lt;br /&gt;
&lt;br /&gt;
Nachdem eine &amp;lt;code&amp;gt;SELECT&amp;lt;/code&amp;gt;-Abfrage erfolgreich ausgeführt wurde, müssen die zurückgegebenen Daten aus dem Ergebnisobjekt (&amp;lt;code&amp;gt;PDOStatement&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;mysqli_result&amp;lt;/code&amp;gt;) abgerufen (&amp;quot;gefetched&amp;quot;) werden.&lt;br /&gt;
&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Zeilenweise Iteration (&amp;lt;code&amp;gt;fetch&amp;lt;/code&amp;gt; im Loop):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
Dies ist der klassische Weg, um Ergebnisse zu verarbeiten, insbesondere wenn jede Zeile einzeln behandelt werden muss oder wenn die Ergebnismenge sehr groß ist.&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;PDO:&amp;#039;&amp;#039;&amp;#039; Die &amp;lt;code&amp;gt;fetch()&amp;lt;/code&amp;gt;-Methode des &amp;lt;code&amp;gt;PDOStatement&amp;lt;/code&amp;gt;-Objekts wird wiederholt in einer &amp;lt;code&amp;gt;while&amp;lt;/code&amp;gt;-Schleife aufgerufen. &amp;lt;code&amp;gt;fetch()&amp;lt;/code&amp;gt; gibt bei jedem Aufruf die nächste Zeile zurück (im Format des eingestellten Fetch-Modus, z.B. &amp;lt;code&amp;gt;PDO::FETCH_ASSOC&amp;lt;/code&amp;gt;) und &amp;lt;code&amp;gt;false&amp;lt;/code&amp;gt; (oder &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt; je nach Modus/Version), wenn keine weiteren Zeilen vorhanden sind.[2, 33]&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$stmt = $pdo-&amp;amp;gt;prepare(&amp;quot;SELECT name, email FROM users WHERE status =?&amp;quot;);&lt;br /&gt;
$stmt-&amp;amp;gt;execute([&amp;#039;active&amp;#039;]);&lt;br /&gt;
while ($row = $stmt-&amp;amp;gt;fetch(PDO::FETCH_ASSOC)) {&lt;br /&gt;
 echo &amp;quot;Name: &amp;quot;. htmlspecialchars($row[&amp;#039;name&amp;#039;]). &amp;quot;, E-Mail: &amp;quot;. htmlspecialchars($row[&amp;#039;email&amp;#039;]). &amp;quot;&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;MySQLi:&amp;#039;&amp;#039;&amp;#039; Die &amp;lt;code&amp;gt;fetch_assoc()&amp;lt;/code&amp;gt;-Methode (oder &amp;lt;code&amp;gt;fetch_row()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;fetch_object()&amp;lt;/code&amp;gt;, etc.) des &amp;lt;code&amp;gt;mysqli_result&amp;lt;/code&amp;gt;-Objekts wird in einer &amp;lt;code&amp;gt;while&amp;lt;/code&amp;gt;-Schleife verwendet. Sie gibt die nächste Zeile als Array (oder Objekt) zurück oder &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt;, wenn das Ende erreicht ist.[25, 15]&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// OO-Stil (nach $result = $stmt-&amp;amp;gt;get_result();)&lt;br /&gt;
while ($row = $result-&amp;amp;gt;fetch_assoc()) {&lt;br /&gt;
 echo &amp;quot;Name: &amp;quot;. htmlspecialchars($row[&amp;#039;name&amp;#039;]). &amp;quot;, E-Mail: &amp;quot;. htmlspecialchars($row[&amp;#039;email&amp;#039;]). &amp;quot;&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Prozedural (nach $result = mysqli_stmt_get_result($stmt);)&lt;br /&gt;
while ($row = mysqli_fetch_assoc($result)) {&lt;br /&gt;
 echo &amp;quot;Name: &amp;quot;. htmlspecialchars($row[&amp;#039;name&amp;#039;]). &amp;quot;, E-Mail: &amp;quot;. htmlspecialchars($row[&amp;#039;email&amp;#039;]). &amp;quot;&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Dieser Ansatz ist &amp;#039;&amp;#039;&amp;#039;speichereffizient&amp;#039;&amp;#039;&amp;#039;, da immer nur eine Zeile gleichzeitig im PHP-Speicher gehalten wird. Dies ist besonders wichtig bei Abfragen, die potenziell Tausende oder Millionen von Zeilen zurückgeben könnten.[37]&lt;br /&gt;
&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Alle Ergebnisse auf einmal abrufen (&amp;lt;code&amp;gt;fetchAll&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;fetch_all&amp;lt;/code&amp;gt;):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
Für kleinere bis mittlere Ergebnismengen ist es oft bequemer, alle Zeilen auf einmal in ein PHP-Array zu laden.&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;PDO:&amp;#039;&amp;#039;&amp;#039; Die &amp;lt;code&amp;gt;fetchAll()&amp;lt;/code&amp;gt;-Methode des &amp;lt;code&amp;gt;PDOStatement&amp;lt;/code&amp;gt;-Objekts gibt ein Array zurück, das alle Ergebniszeilen enthält.[33, 38] Der Fetch-Modus (z.B. &amp;lt;code&amp;gt;PDO::FETCH_ASSOC&amp;lt;/code&amp;gt;) kann als Argument übergeben werden.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$stmt = $pdo-&amp;amp;gt;prepare(&amp;quot;SELECT name, email FROM users WHERE status =?&amp;quot;);&lt;br /&gt;
$stmt-&amp;amp;gt;execute([&amp;#039;active&amp;#039;]);&lt;br /&gt;
$results = $stmt-&amp;amp;gt;fetchAll(PDO::FETCH_ASSOC);&lt;br /&gt;
// $results ist nun ein Array von assoziativen Arrays&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;MySQLi:&amp;#039;&amp;#039;&amp;#039; Die &amp;lt;code&amp;gt;fetch_all()&amp;lt;/code&amp;gt;-Methode des &amp;lt;code&amp;gt;mysqli_result&amp;lt;/code&amp;gt;-Objekts (verfügbar mit &amp;lt;code&amp;gt;mysqlnd&amp;lt;/code&amp;gt;) tut dasselbe. Der Modus (&amp;lt;code&amp;gt;MYSQLI_ASSOC&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;MYSQLI_NUM&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;MYSQLI_BOTH&amp;lt;/code&amp;gt;) wird als Argument übergeben.[25, 39]&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// OO-Stil (nach $result = $stmt-&amp;amp;gt;get_result();)&lt;br /&gt;
$results = $result-&amp;amp;gt;fetch_all(MYSQLI_ASSOC);&lt;br /&gt;
&lt;br /&gt;
// Prozedural (nach $result = mysqli_stmt_get_result($stmt);)&lt;br /&gt;
$results = mysqli_fetch_all($result, MYSQLI_ASSOC);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Der &amp;#039;&amp;#039;&amp;#039;Vorteil&amp;#039;&amp;#039;&amp;#039; dieser Methode liegt in der einfacheren Weiterverarbeitung des Ergebnis-Arrays mit Standard-PHP-Array-Funktionen wie &amp;lt;code&amp;gt;foreach&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;count&amp;lt;/code&amp;gt;.[40] Der &amp;#039;&amp;#039;&amp;#039;Nachteil&amp;#039;&amp;#039;&amp;#039; ist der potenziell hohe Speicherverbrauch, da alle Daten gleichzeitig in den PHP-Speicher geladen werden. Bei sehr großen Ergebnismengen kann dies zu Speicherlimit-Fehlern führen.[37]&lt;br /&gt;
&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Fetch-Modi / Fetch-Funktionen (Struktur der Ergebniszeile):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
Beide Erweiterungen bieten verschiedene Möglichkeiten, die Struktur der abgerufenen Datenzeilen zu bestimmen:&lt;br /&gt;
&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;PDO Fetch-Modi (Auswahl):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
***** &amp;lt;code&amp;gt;PDO::FETCH_ASSOC&amp;lt;/code&amp;gt;: Gibt ein assoziatives Array zurück (&amp;lt;code&amp;gt;[&amp;#039;spaltenname&amp;#039; =&amp;amp;gt; &amp;#039;wert&amp;#039;,... ]&amp;lt;/code&amp;gt;).[9, 33, 41, 42] Sehr gebräuchlich.&lt;br /&gt;
***** &amp;lt;code&amp;gt;PDO::FETCH_NUM&amp;lt;/code&amp;gt;: Gibt ein numerisch indiziertes Array zurück (&amp;lt;code&amp;gt;[0 =&amp;amp;gt; &amp;#039;wert1&amp;#039;, 1 =&amp;amp;gt; &amp;#039;wert2&amp;#039;,...]&amp;lt;/code&amp;gt;).[9, 41, 42]&lt;br /&gt;
***** &amp;lt;code&amp;gt;PDO::FETCH_BOTH&amp;lt;/code&amp;gt; (Standard): Gibt ein Array zurück, das sowohl assoziative als auch numerische Indizes enthält.[41, 42] Verbraucht mehr Speicher.&lt;br /&gt;
***** &amp;lt;code&amp;gt;PDO::FETCH_OBJ&amp;lt;/code&amp;gt;: Gibt ein anonymes &amp;lt;code&amp;gt;stdClass&amp;lt;/code&amp;gt;-Objekt zurück, bei dem die Spaltennamen Eigenschaften sind (&amp;lt;code&amp;gt;$row-&amp;amp;gt;spaltenname&amp;lt;/code&amp;gt;).[9, 41, 42, 43]&lt;br /&gt;
***** &amp;lt;code&amp;gt;PDO::FETCH_CLASS&amp;lt;/code&amp;gt;: Erstellt eine Instanz einer angegebenen Klasse und weist die Spaltenwerte den Klasseneigenschaften zu.[38, 41, 42, 43]&lt;br /&gt;
***** &amp;lt;code&amp;gt;PDO::FETCH_COLUMN&amp;lt;/code&amp;gt;: Ruft nur den Wert einer einzelnen Spalte aus der nächsten Zeile ab.[38]&lt;br /&gt;
***** &amp;lt;code&amp;gt;PDO::FETCH_LAZY&amp;lt;/code&amp;gt;: Eine Kombination aus &amp;lt;code&amp;gt;FETCH_BOTH&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;FETCH_OBJ&amp;lt;/code&amp;gt;, die Werte erst bei Zugriff lädt.[9, 41, 42]&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;MySQLi Fetch-Funktionen (Auswahl):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
***** &amp;lt;code&amp;gt;fetch_assoc()&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;mysqli_fetch_assoc()&amp;lt;/code&amp;gt;: Gibt ein assoziatives Array zurück.[17, 25, 39, 15] Sehr gebräuchlich.&lt;br /&gt;
***** &amp;lt;code&amp;gt;fetch_row()&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;mysqli_fetch_row()&amp;lt;/code&amp;gt;: Gibt ein numerisch indiziertes Array zurück.[39, 35]&lt;br /&gt;
***** &amp;lt;code&amp;gt;fetch_array()&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;mysqli_fetch_array()&amp;lt;/code&amp;gt;: Gibt je nach übergebenem Modus (&amp;lt;code&amp;gt;MYSQLI_ASSOC&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;MYSQLI_NUM&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;MYSQLI_BOTH&amp;lt;/code&amp;gt;) ein assoziatives, numerisches oder beides enthaltendes Array zurück.[39, 35]&lt;br /&gt;
***** &amp;lt;code&amp;gt;fetch_object()&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;mysqli_fetch_object()&amp;lt;/code&amp;gt;: Gibt ein &amp;lt;code&amp;gt;stdClass&amp;lt;/code&amp;gt;-Objekt zurück oder eine Instanz einer angegebenen Klasse.[21, 15, 35]&lt;br /&gt;
***** &amp;lt;code&amp;gt;fetch_column()&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;mysqli_fetch_column()&amp;lt;/code&amp;gt;: Ruft den Wert einer einzelnen Spalte ab (erst ab PHP 8.1 verfügbar).[24]&lt;br /&gt;
***** &amp;lt;code&amp;gt;fetch_all()&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;mysqli_fetch_all()&amp;lt;/code&amp;gt;: Ruft alle Zeilen auf einmal ab, Modus (&amp;lt;code&amp;gt;MYSQLI_ASSOC&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;MYSQLI_NUM&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;MYSQLI_BOTH&amp;lt;/code&amp;gt;) wählbar.[21, 25, 39]&lt;br /&gt;
&lt;br /&gt;
=== Tabelle 2: Ausgewählte PDO Fetch-Modi ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Konstante !! Rückgabeformat !! Beschreibung&lt;br /&gt;
|- &lt;br /&gt;
| &amp;lt;code&amp;gt;PDO::FETCH_ASSOC&amp;lt;/code&amp;gt;&lt;br /&gt;
| assoziatives=&amp;quot;&amp;quot; array=&amp;quot;&amp;quot; mwt-preservehtml=&amp;quot;&amp;quot; | [&amp;quot; col&amp;#039;=&amp;quot;&amp;amp;gt;&amp;quot; &amp;#039;val&amp;#039;]&amp;amp;lt;=&amp;quot;&amp;quot; code&amp;amp;gt;&amp;#039;)=&amp;quot;&amp;quot; &amp;amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
| Gängigste Form, Spaltennamen als Schlüssel.&lt;br /&gt;
|- &lt;br /&gt;
| mwt-preservehtml=&amp;quot;&amp;quot; pdo::fetch_num=&amp;quot;&amp;quot; code=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| numerisches=&amp;quot;&amp;quot; array=&amp;quot;&amp;quot; mwt-preservehtml=&amp;quot;&amp;quot; 0=&amp;quot;&amp;quot; val=&amp;quot;&amp;quot; code=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Spaltenwerte über numerischen Index erreichbar.&lt;br /&gt;
|- &lt;br /&gt;
| mwt-preservehtml=&amp;quot;&amp;quot; pdo::fetch_both=&amp;quot;&amp;quot; code=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| array=&amp;quot;&amp;quot; mit=&amp;quot;&amp;quot; assoz=&amp;quot;&amp;quot; num=&amp;quot;&amp;quot; indizes=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Standard, enthält doppelte Informationen, höherer Speicherbedarf.&lt;br /&gt;
|- &lt;br /&gt;
| mwt-preservehtml=&amp;quot;&amp;quot; pdo::fetch_obj=&amp;quot;&amp;quot; code=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| anonymes=&amp;quot;&amp;quot; mwt-preservehtml=&amp;quot;&amp;quot; stdclass=&amp;quot;&amp;quot; code=&amp;quot;&amp;quot; -objekt=&amp;quot;&amp;quot; row-=&amp;quot;&amp;quot; col=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Spaltennamen werden zu Objekteigenschaften.&lt;br /&gt;
|- &lt;br /&gt;
| &amp;lt;code&amp;gt;PDO::FETCH_CLASS&amp;lt;/code&amp;gt; || objekt=&amp;quot;&amp;quot; einer=&amp;quot;&amp;quot; spezifischen=&amp;quot;&amp;quot; klasse=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Mappt Spalten auf Eigenschaften einer vordefinierten Klasse.&lt;br /&gt;
|- &lt;br /&gt;
| &amp;lt;code&amp;gt;PDO::FETCH_COLUMN&amp;lt;/code&amp;gt; || einzelner=&amp;quot;&amp;quot; wert=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Gibt nur den Wert der ersten (oder angegebenen) Spalte der nächsten Zeile zurück.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Tabelle 3: Ausgewählte MySQLi Fetch-Funktionen ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Funktion (OO / Prozedural) !! Rückgabeformat !! Beschreibung&lt;br /&gt;
|- &lt;br /&gt;
| mwt-preservehtml=&amp;quot;&amp;quot; fetch_assoc=&amp;quot;&amp;quot; code=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| assoziatives=&amp;quot;&amp;quot; array=&amp;quot;&amp;quot; mwt-preservehtml=&amp;quot;&amp;quot; col=&amp;quot;&amp;quot; val=&amp;quot;&amp;quot; code=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Gängigste Form, Spaltennamen als Schlüssel.&lt;br /&gt;
|- &lt;br /&gt;
| mwt-preservehtml=&amp;quot;&amp;quot; fetch_row=&amp;quot;&amp;quot; code=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| numerisches=&amp;quot;&amp;quot; array=&amp;quot;&amp;quot; mwt-preservehtml=&amp;quot;&amp;quot; 0=&amp;quot;&amp;quot; val=&amp;quot;&amp;quot; code=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Spaltenwerte über numerischen Index erreichbar.&lt;br /&gt;
|- &lt;br /&gt;
| mwt-preservehtml=&amp;quot;&amp;quot; fetch_array=&amp;quot;&amp;quot; code=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| array=&amp;quot;&amp;quot; assoz=&amp;quot;&amp;quot; num=&amp;quot;&amp;quot; oder=&amp;quot;&amp;quot; beide=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Flexibel je nach Modus (&amp;lt;code&amp;gt;MYSQLI_ASSOC&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;MYSQLI_NUM&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;MYSQLI_BOTH&amp;lt;/code&amp;gt;).&lt;br /&gt;
|- &lt;br /&gt;
| &amp;lt;code&amp;gt;fetch_object()&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;mysqli_fetch_object()&amp;lt;/code&amp;gt;&lt;br /&gt;
| mwt-preservehtml=&amp;quot;&amp;quot; | stdClass&amp;quot; -objekt=&amp;quot;&amp;quot; oder=&amp;quot;&amp;quot; spezifische=&amp;quot;&amp;quot; klasse=&amp;quot;&amp;quot; &amp;amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
| Spaltennamen werden zu Objekteigenschaften.&lt;br /&gt;
|- &lt;br /&gt;
| mwt-preservehtml=&amp;quot;&amp;quot; fetch_all=&amp;quot;&amp;quot; code=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| array=&amp;quot;&amp;quot; von=&amp;quot;&amp;quot; arrays=&amp;quot;&amp;quot; assoz=&amp;quot;&amp;quot; num=&amp;quot;&amp;quot; oder=&amp;quot;&amp;quot; beide=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Holt alle Ergebniszeilen auf einmal (benötigt &amp;lt;code&amp;gt;mysqlnd&amp;lt;/code&amp;gt;).&lt;br /&gt;
|- &lt;br /&gt;
| &amp;lt;code&amp;gt;fetch_column()&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;mysqli_fetch_column()&amp;lt;/code&amp;gt; || einzelner=&amp;quot;&amp;quot; wert=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Gibt nur den Wert der ersten (oder angegebenen) Spalte zurück (PHP 8.1+).&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Zugriff auf Spaltenwerte:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
Der Zugriff auf die Daten innerhalb einer abgerufenen Zeile hängt vom gewählten Fetch-Modus ab:&lt;br /&gt;
**** Assoziatives Array: &amp;lt;code&amp;gt;$row[&amp;#039;spaltenname&amp;#039;]&amp;lt;/code&amp;gt;&lt;br /&gt;
**** Numerisches Array: &amp;lt;code&amp;gt;$row&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;$row[1]&amp;lt;/code&amp;gt;,...&lt;br /&gt;
**** Objekt: &amp;lt;code&amp;gt;$row-&amp;amp;gt;spaltenname&amp;lt;/code&amp;gt;&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Iteration mit &amp;lt;code&amp;gt;foreach&amp;lt;/code&amp;gt;:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
Nachdem Ergebnisse mit &amp;lt;code&amp;gt;fetchAll()&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;fetch_all()&amp;lt;/code&amp;gt; in ein Array geladen wurden, ist &amp;lt;code&amp;gt;foreach&amp;lt;/code&amp;gt; die natürliche Wahl zur Iteration [37, 40, 44]:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$results = $stmt-&amp;amp;gt;fetchAll(PDO::FETCH_ASSOC);&lt;br /&gt;
foreach ($results as $row) {&lt;br /&gt;
 echo &amp;quot;Name: &amp;quot;. htmlspecialchars($row[&amp;#039;name&amp;#039;]). &amp;quot;&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Seit PHP 8 bietet PDO eine modernere Alternative, die die Lesbarkeit von &amp;lt;code&amp;gt;foreach&amp;lt;/code&amp;gt; mit der Speichereffizienz von &amp;lt;code&amp;gt;fetch()&amp;lt;/code&amp;gt; kombiniert, indem direkt über das &amp;lt;code&amp;gt;PDOStatement&amp;lt;/code&amp;gt;-Objekt iteriert wird [37, 45]:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Nur PHP 8+ mit PDO&lt;br /&gt;
$stmt = $pdo-&amp;amp;gt;query(&amp;quot;SELECT name, email FROM users&amp;quot;);&lt;br /&gt;
foreach ($stmt as $row) { // PDOStatement ist direkt traversierbar&lt;br /&gt;
 echo &amp;quot;Name: &amp;quot;. htmlspecialchars($row[&amp;#039;name&amp;#039;]). &amp;quot;&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Diese Methode ist besonders elegant und effizient für große Ergebnismengen in modernen PHP-Versionen.&lt;br /&gt;
&lt;br /&gt;
=== Daten manipulieren (INSERT, UPDATE, DELETE) ===&lt;br /&gt;
&lt;br /&gt;
Das Einfügen, Aktualisieren und Löschen von Daten sind kritische Operationen, da sie den Datenbestand verändern. Hier ist die Verwendung von &amp;#039;&amp;#039;&amp;#039;Prepared Statements zwingend erforderlich&amp;#039;&amp;#039;&amp;#039;, um die Integrität und Sicherheit der Datenbank zu gewährleisten, insbesondere wenn Benutzereingaben beteiligt sind.&lt;br /&gt;
&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;PDO:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;Vorbereiten (&amp;lt;code&amp;gt;prepare&amp;lt;/code&amp;gt;):&amp;#039;&amp;#039;&amp;#039; Die SQL-Anweisung (&amp;lt;code&amp;gt;INSERT&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;UPDATE&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;DELETE&amp;lt;/code&amp;gt;) wird mit Platzhaltern (&amp;lt;code&amp;gt;?&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;:name&amp;lt;/code&amp;gt;) für die einzufügenden/zu aktualisierenden Werte sowie für die Kriterien in &amp;lt;code&amp;gt;WHERE&amp;lt;/code&amp;gt;-Klauseln (bei &amp;lt;code&amp;gt;UPDATE&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;DELETE&amp;lt;/code&amp;gt;) vorbereitet.[5, 46, 47, 48]&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;Ausführen (&amp;lt;code&amp;gt;execute&amp;lt;/code&amp;gt;):&amp;#039;&amp;#039;&amp;#039; Die Anweisung wird mit einem Array ausgeführt, das die Werte für die Platzhalter enthält.[5, 46, 47, 48]&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;Ergebnis prüfen:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
***** &amp;#039;&amp;#039;Anzahl betroffener Zeilen:&amp;#039;&amp;#039; &amp;lt;code&amp;gt;$stmt-&amp;amp;gt;rowCount()&amp;lt;/code&amp;gt; gibt die Anzahl der durch &amp;lt;code&amp;gt;UPDATE&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;DELETE&amp;lt;/code&amp;gt; betroffenen Zeilen zurück. Bei &amp;lt;code&amp;gt;INSERT&amp;lt;/code&amp;gt; ist der Rückgabewert oft nicht zuverlässig oder standardisiert.[8, 9, 5, 49] Das Verhalten kann je nach Datenbanktreiber und Konfiguration (z.B. &amp;lt;code&amp;gt;PDO::MYSQL_ATTR_FOUND_ROWS&amp;lt;/code&amp;gt;) variieren.[9]&lt;br /&gt;
***** &amp;#039;&amp;#039;ID des letzten eingefügten Datensatzes:&amp;#039;&amp;#039; Bei &amp;lt;code&amp;gt;INSERT&amp;lt;/code&amp;gt;-Anweisungen in Tabellen mit einer &amp;lt;code&amp;gt;AUTO_INCREMENT&amp;lt;/code&amp;gt;-Spalte liefert &amp;lt;code&amp;gt;$pdo-&amp;amp;gt;lastInsertId()&amp;lt;/code&amp;gt; die ID des neu eingefügten Datensatzes zurück.[8, 33, 48]&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;Code-Beispiele (PDO):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!--?php// --- INSERT ---&lt;br /&gt;
$name = $_POST[&amp;#039;name&amp;#039;];&lt;br /&gt;
$email = $_POST[&amp;#039;email&amp;#039;];&lt;br /&gt;
$status = &amp;#039;pending&amp;#039;;&lt;br /&gt;
&lt;br /&gt;
$sql_insert = &amp;quot;INSERT INTO users (username, email, status) VALUES (:name, :email, :status)&amp;quot;;&lt;br /&gt;
$stmt_insert = $pdo---&amp;gt;prepare($sql_insert);&lt;br /&gt;
$stmt_insert-&amp;amp;gt;execute([&amp;#039;:name&amp;#039; =&amp;amp;gt; $name, &amp;#039;:email&amp;#039; =&amp;amp;gt; $email, &amp;#039;:status&amp;#039; =&amp;amp;gt; $status]);&lt;br /&gt;
$lastId = $pdo-&amp;amp;gt;lastInsertId();&lt;br /&gt;
echo &amp;quot;Neuer Benutzer mit ID $lastId eingefügt.&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
// --- UPDATE ---&lt;br /&gt;
$new_status = &amp;#039;active&amp;#039;;&lt;br /&gt;
$user_id_to_update = $lastId;&lt;br /&gt;
&lt;br /&gt;
$sql_update = &amp;quot;UPDATE users SET status = :status WHERE id = :id&amp;quot;;&lt;br /&gt;
$stmt_update = $pdo-&amp;amp;gt;prepare($sql_update);&lt;br /&gt;
$stmt_update-&amp;amp;gt;execute([&amp;#039;:status&amp;#039; =&amp;amp;gt; $new_status, &amp;#039;:id&amp;#039; =&amp;amp;gt; $user_id_to_update]);&lt;br /&gt;
$affectedRows = $stmt_update-&amp;amp;gt;rowCount();&lt;br /&gt;
echo &amp;quot;$affectedRows Benutzerstatus aktualisiert.&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
// --- DELETE ---&lt;br /&gt;
$user_id_to_delete = $lastId;&lt;br /&gt;
&lt;br /&gt;
$sql_delete = &amp;quot;DELETE FROM users WHERE id = :id&amp;quot;;&lt;br /&gt;
$stmt_delete = $pdo-&amp;amp;gt;prepare($sql_delete);&lt;br /&gt;
$stmt_delete-&amp;amp;gt;execute([&amp;#039;:id&amp;#039; =&amp;amp;gt; $user_id_to_delete]);&lt;br /&gt;
$affectedRowsDel = $stmt_delete-&amp;amp;gt;rowCount();&lt;br /&gt;
echo &amp;quot;$affectedRowsDel Benutzer gelöscht.&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
?&amp;amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;MySQLi:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;Vorbereiten (&amp;lt;code&amp;gt;prepare&amp;lt;/code&amp;gt;):&amp;#039;&amp;#039;&amp;#039; Die SQL-Anweisung wird mit &amp;lt;code&amp;gt;?&amp;lt;/code&amp;gt;-Platzhaltern vorbereitet (OO: &amp;lt;code&amp;gt;$mysqli-&amp;amp;gt;prepare()&amp;lt;/code&amp;gt;, Proc: &amp;lt;code&amp;gt;mysqli_prepare()&amp;lt;/code&amp;gt;).[25, 50, 15, 35]&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;Parameter binden (&amp;lt;code&amp;gt;bind_param&amp;lt;/code&amp;gt;):&amp;#039;&amp;#039;&amp;#039; Variablen werden explizit mit Typenangabe gebunden (OO: &amp;lt;code&amp;gt;$stmt-&amp;amp;gt;bind_param()&amp;lt;/code&amp;gt;, Proc: &amp;lt;code&amp;gt;mysqli_stmt_bind_param()&amp;lt;/code&amp;gt;).[25, 50, 15, 35] Die Notwendigkeit, den Datentyp für jede Variable anzugeben, ist ein wesentlicher Unterschied zur einfacheren Array-Übergabe bei PDOs &amp;lt;code&amp;gt;execute()&amp;lt;/code&amp;gt;.&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;Ausführen (&amp;lt;code&amp;gt;execute&amp;lt;/code&amp;gt;):&amp;#039;&amp;#039;&amp;#039; Die Anweisung wird ausgeführt (OO: &amp;lt;code&amp;gt;$stmt-&amp;amp;gt;execute()&amp;lt;/code&amp;gt;, Proc: &amp;lt;code&amp;gt;mysqli_stmt_execute()&amp;lt;/code&amp;gt;).[25, 36, 50, 15, 35]&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;Ergebnis prüfen:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
***** &amp;#039;&amp;#039;Anzahl betroffener Zeilen:&amp;#039;&amp;#039; Über die Eigenschaft &amp;lt;code&amp;gt;$mysqli-&amp;amp;gt;affected_rows&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;$stmt-&amp;amp;gt;affected_rows&amp;lt;/code&amp;gt; (OO) bzw. die Funktionen &amp;lt;code&amp;gt;mysqli_affected_rows($link)&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;mysqli_stmt_affected_rows($stmt)&amp;lt;/code&amp;gt; (Proc).[18, 26, 15, 35]&lt;br /&gt;
***** &amp;#039;&amp;#039;ID des letzten eingefügten Datensatzes:&amp;#039;&amp;#039; Über &amp;lt;code&amp;gt;$mysqli-&amp;amp;gt;insert_id&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;$stmt-&amp;amp;gt;insert_id&amp;lt;/code&amp;gt; (OO) bzw. &amp;lt;code&amp;gt;mysqli_insert_id($link)&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;mysqli_stmt_insert_id($stmt)&amp;lt;/code&amp;gt; (Proc).[26, 15] Die Verwendung von &amp;lt;code&amp;gt;$mysqli-&amp;amp;gt;insert_id&amp;lt;/code&amp;gt; wird oft als zuverlässiger angesehen, da sie auch nach dem Schließen des Statements funktioniert.[15]&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;Code-Beispiele (MySQLi OO):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!--?php// --- INSERT ---&lt;br /&gt;
$name = $_POST[&amp;#039;name&amp;#039;];&lt;br /&gt;
$email = $_POST[&amp;#039;email&amp;#039;];&lt;br /&gt;
$status = &amp;#039;pending&amp;#039;;&lt;br /&gt;
&lt;br /&gt;
$sql_insert = &amp;quot;INSERT INTO users (username, email, status) VALUES (?,?,?)&amp;quot;;&lt;br /&gt;
$stmt_insert = $mysqli---&amp;gt;prepare($sql_insert);&lt;br /&gt;
// Typen: s=string, s=string, s=string&lt;br /&gt;
$stmt_insert-&amp;amp;gt;bind_param(&amp;quot;sss&amp;quot;, $name, $email, $status);&lt;br /&gt;
$stmt_insert-&amp;amp;gt;execute();&lt;br /&gt;
$lastId = $mysqli-&amp;amp;gt;insert_id;&lt;br /&gt;
echo &amp;quot;Neuer Benutzer mit ID $lastId eingefügt (MySQLi OO).&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
$stmt_insert-&amp;amp;gt;close(); // Statement schließen&lt;br /&gt;
&lt;br /&gt;
// --- UPDATE ---&lt;br /&gt;
$new_status = &amp;#039;active&amp;#039;;&lt;br /&gt;
$user_id_to_update = $lastId;&lt;br /&gt;
&lt;br /&gt;
$sql_update = &amp;quot;UPDATE users SET status =? WHERE id =?&amp;quot;;&lt;br /&gt;
$stmt_update = $mysqli-&amp;amp;gt;prepare($sql_update);&lt;br /&gt;
// Typen: s=string, i=integer&lt;br /&gt;
$stmt_update-&amp;amp;gt;bind_param(&amp;quot;si&amp;quot;, $new_status, $user_id_to_update);&lt;br /&gt;
$stmt_update-&amp;amp;gt;execute();&lt;br /&gt;
$affectedRows = $mysqli-&amp;amp;gt;affected_rows; // Oder $stmt_update-&amp;amp;gt;affected_rows vor close()&lt;br /&gt;
echo &amp;quot;$affectedRows Benutzerstatus aktualisiert (MySQLi OO).&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
$stmt_update-&amp;amp;gt;close();&lt;br /&gt;
&lt;br /&gt;
// --- DELETE ---&lt;br /&gt;
$user_id_to_delete = $lastId;&lt;br /&gt;
&lt;br /&gt;
$sql_delete = &amp;quot;DELETE FROM users WHERE id =?&amp;quot;;&lt;br /&gt;
$stmt_delete = $mysqli-&amp;amp;gt;prepare($sql_delete);&lt;br /&gt;
// Typ: i=integer&lt;br /&gt;
$stmt_delete-&amp;amp;gt;bind_param(&amp;quot;i&amp;quot;, $user_id_to_delete);&lt;br /&gt;
$stmt_delete-&amp;amp;gt;execute();&lt;br /&gt;
$affectedRowsDel = $mysqli-&amp;amp;gt;affected_rows;&lt;br /&gt;
echo &amp;quot;$affectedRowsDel Benutzer gelöscht (MySQLi OO).&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
$stmt_delete-&amp;amp;gt;close();&lt;br /&gt;
?&amp;amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;#039;&amp;#039;(Anmerkung: Prozedurale Beispiele folgen demselben Muster unter Verwendung der &amp;lt;code&amp;gt;mysqli_*&amp;lt;/code&amp;gt;-Funktionen wie &amp;lt;code&amp;gt;mysqli_prepare&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;mysqli_stmt_bind_param&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;mysqli_stmt_execute&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;mysqli_insert_id&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;mysqli_affected_rows&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;mysqli_stmt_close&amp;lt;/code&amp;gt;)&amp;#039;&amp;#039;.&lt;br /&gt;
&lt;br /&gt;
== Sicherheit: SQL Injection verstehen und verhindern ==&lt;br /&gt;
&lt;br /&gt;
Eines der größten Sicherheitsrisiken bei der Interaktion mit Datenbanken über Webanwendungen ist die SQL Injection (SQLi). Das Verständnis dieser Bedrohung und ihrer Abwehr ist für jeden PHP-Entwickler unerlässlich.&lt;br /&gt;
&lt;br /&gt;
=== SQL Injection ===&lt;br /&gt;
&lt;br /&gt;
SQL Injection ist eine Angriffstechnik, bei der ein Angreifer versucht, bösartigen oder manipulierten SQL-Code über Benutzereingabefelder einer Webanwendung (z.B. Formulare, URL-Parameter, HTTP-Header, Cookies) in die Datenbankabfragen einzuschleusen.[51, 52, 53, 54, 55, 56, 57]&lt;br /&gt;
&lt;br /&gt;
Die Attacke funktioniert, indem Schwachstellen in der Anwendung ausgenutzt werden, bei denen Benutzereingaben direkt und ohne ausreichende Validierung oder Maskierung (Escaping) in SQL-Abfragestrings eingefügt (konkateniert) werden.[5, 51, 55] Dadurch wird die notwendige Trennung zwischen dem eigentlichen SQL-Befehl (Kontrollebene) und den Daten (Datenebene) aufgehoben.[51] Der Angreifer kann spezielle Zeichen (wie Anführungszeichen &amp;lt;code&amp;gt;&amp;#039;&amp;lt;/code&amp;gt;, Semikolons &amp;lt;code&amp;gt;;&amp;lt;/code&amp;gt; oder Kommentare &amp;lt;code&amp;gt;--&amp;lt;/code&amp;gt;) verwenden, um den ursprünglichen SQL-Befehl vorzeitig zu beenden, zu verändern oder um eigene, zusätzliche SQL-Befehle anzuhängen.[51, 53]&lt;br /&gt;
&lt;br /&gt;
Ein klassisches Beispiel ist eine Login-Abfrage, die etwa so aufgebaut ist:&lt;br /&gt;
&amp;lt;code&amp;gt;$sql = &amp;quot;SELECT * FROM users WHERE username = &amp;#039;&amp;quot;. $userInputUsername. &amp;quot;&amp;#039; AND password = &amp;#039;&amp;quot;. $userInputPassword. &amp;quot;&amp;#039;&amp;quot;;&amp;lt;/code&amp;gt;&lt;br /&gt;
Wenn ein Angreifer als Benutzernamen &amp;lt;code&amp;gt;&amp;#039; OR &amp;#039;1&amp;#039;=&amp;#039;1&amp;lt;/code&amp;gt; eingibt, wird die resultierende SQL-Abfrage zu:&lt;br /&gt;
&amp;lt;code&amp;gt;SELECT * FROM users WHERE username = &amp;#039;&amp;#039; OR &amp;#039;1&amp;#039;=&amp;#039;1&amp;#039; AND password = &amp;#039;...&amp;#039;&amp;lt;/code&amp;gt;&lt;br /&gt;
Da &amp;lt;code&amp;gt;&amp;#039;1&amp;#039;=&amp;#039;1&amp;#039;&amp;lt;/code&amp;gt; immer wahr ist, würde diese Abfrage (je nach genauer Struktur und Datenbank) möglicherweise alle Benutzer zurückgeben oder den Login ohne korrektes Passwort ermöglichen.[5, 15, 55]&lt;br /&gt;
&lt;br /&gt;
=== Auswirkungen und Gefahren von SQL Injection ===&lt;br /&gt;
&lt;br /&gt;
Die potenziellen Folgen einer erfolgreichen SQL-Injection-Attacke sind gravierend und vielfältig:&lt;br /&gt;
&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Datendiebstahl:&amp;#039;&amp;#039;&amp;#039; Angreifer können auf sensible Daten zugreifen und diese auslesen, darunter Benutzerdaten, Passwörter (oft als Hashes, die aber geknackt werden können), Kreditkarteninformationen, Geschäftsgeheimnisse und andere vertrauliche Informationen.[51, 53, 56]&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Datenmanipulation/-zerstörung:&amp;#039;&amp;#039;&amp;#039; Angreifer können bestehende Daten in der Datenbank verändern (z.B. Preise in einem Shop, Benutzerrollen) oder komplette Datensätze oder sogar Tabellen löschen (&amp;lt;code&amp;gt;DELETE&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;DROP TABLE&amp;lt;/code&amp;gt;).[51, 53, 56]&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Umgehung der Authentifizierung/Identitätsdiebstahl:&amp;#039;&amp;#039;&amp;#039; Wie im Beispiel oben gezeigt, können Angreifer Login-Mechanismen umgehen und sich unautorisiert Zugang verschaffen, möglicherweise sogar mit administrativen Rechten.[51, 56]&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Denial of Service (DoS):&amp;#039;&amp;#039;&amp;#039; Durch das Löschen wichtiger Daten oder das Ausführen ressourcenintensiver Abfragen kann die Verfügbarkeit der Anwendung oder der Datenbank beeinträchtigt werden.[51, 56]&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Kompromittierung des Servers:&amp;#039;&amp;#039;&amp;#039; In manchen Fällen können Angreifer über SQL Injection Betriebssystembefehle auf dem Datenbankserver ausführen oder vollen administrativen Zugriff auf die Datenbank erlangen, was weitreichende Konsequenzen haben kann.[51, 55, 56]&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Reputationsschaden und rechtliche Folgen:&amp;#039;&amp;#039;&amp;#039; Erfolgreiche Angriffe, insbesondere wenn sie zu Datenlecks führen, können das Vertrauen der Benutzer schwer beschädigen und zu empfindlichen Strafen gemäß Datenschutzgesetzen führen.[53]&lt;br /&gt;
&lt;br /&gt;
=== Prävention durch Prepared Statements (Primäre Methode) ===&lt;br /&gt;
&lt;br /&gt;
Die mit Abstand wichtigste und effektivste Methode zur Verhinderung von SQL Injection ist die konsequente Verwendung von &amp;#039;&amp;#039;&amp;#039;Prepared Statements&amp;#039;&amp;#039;&amp;#039; (parametrisierten Abfragen).[4, 25, 5, 58, 35]&lt;br /&gt;
&lt;br /&gt;
Das Grundprinzip von Prepared Statements ist die &amp;#039;&amp;#039;&amp;#039;strikte Trennung von SQL-Befehlsstruktur und den Daten (Parametern)&amp;#039;&amp;#039;&amp;#039;.[4, 25, 5, 58, 35] Der Ablauf ist typischerweise zweistufig:&lt;br /&gt;
**# &amp;#039;&amp;#039;&amp;#039;Prepare (Vorbereiten):&amp;#039;&amp;#039;&amp;#039; Die SQL-Anweisung wird mit Platzhaltern anstelle der tatsächlichen Werte an den Datenbankserver gesendet. Der Server parst die Anweisung, prüft die Syntax und bereitet einen Ausführungsplan vor, ohne die endgültigen Daten zu kennen.&lt;br /&gt;
**# &amp;#039;&amp;#039;&amp;#039;Execute (Ausführen) mit Parameterbindung:&amp;#039;&amp;#039;&amp;#039; Die tatsächlichen Werte (Parameter) werden separat an den Server gesendet und an die vorbereiteten Platzhalter gebunden. Der Server führt nun die vorkompilierte Anweisung mit den sicher eingefügten Werten aus.&lt;br /&gt;
Der entscheidende Punkt ist, dass die Datenbank die separat gesendeten Parameter &amp;#039;&amp;#039;&amp;#039;immer als Datenwerte&amp;#039;&amp;#039;&amp;#039; behandelt, niemals als Teil des SQL-Befehls.[5, 15, 58] Selbst wenn ein Parameter bösartigen SQL-Code enthält (z.B. &amp;lt;code&amp;gt;&amp;#039; OR &amp;#039;1&amp;#039;=&amp;#039;1&amp;lt;/code&amp;gt;), wird dieser nicht ausgeführt, sondern als einfacher String-Wert behandelt, der z.B. in eine Spalte eingefügt oder in einer &amp;lt;code&amp;gt;WHERE&amp;lt;/code&amp;gt;-Klausel verglichen wird. Diese Trennung macht Prepared Statements immun gegen SQL Injection.&lt;br /&gt;
&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Platzhalter:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;PDO:&amp;#039;&amp;#039;&amp;#039; Unterstützt sowohl positionale Platzhalter (&amp;lt;code&amp;gt;?&amp;lt;/code&amp;gt;) als auch benannte Platzhalter (&amp;lt;code&amp;gt;:name&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;:email&amp;lt;/code&amp;gt;, etc.).[5, 13] Benannte Platzhalter machen den Code oft lesbarer, besonders bei vielen Parametern.&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;MySQLi:&amp;#039;&amp;#039;&amp;#039; Unterstützt ausschließlich positionale Platzhalter (&amp;lt;code&amp;gt;?&amp;lt;/code&amp;gt;).[25]&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Parameterbindung:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;PDO:&amp;#039;&amp;#039;&amp;#039; Bietet flexible Bindungsoptionen:&lt;br /&gt;
***** &amp;#039;&amp;#039;Implizit über &amp;lt;code&amp;gt;execute()&amp;lt;/code&amp;gt;-Array:&amp;#039;&amp;#039; Die einfachste und häufigste Methode. Ein Array mit den Werten wird an &amp;lt;code&amp;gt;execute()&amp;lt;/code&amp;gt; übergeben. PDO kümmert sich um das korrekte Escaping und die Typbehandlung (oft werden Werte sicher als Strings behandelt).[4, 12, 5, 13]&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$stmt-&amp;amp;gt;execute([$wert1, $wert2]); // für? Platzhalter&lt;br /&gt;
$stmt-&amp;amp;gt;execute([&amp;#039;:name&amp;#039; =&amp;amp;gt; $wert1, &amp;#039;:id&amp;#039; =&amp;amp;gt; $wert2]); // für :name Platzhalter&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
***** &amp;#039;&amp;#039;Explizit mit &amp;lt;code&amp;gt;bindValue()&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;bindParam()&amp;lt;/code&amp;gt;:&amp;#039;&amp;#039; Erlaubt die explizite Angabe des Datentyps (z.B. &amp;lt;code&amp;gt;PDO::PARAM_INT&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;PDO::PARAM_STR&amp;lt;/code&amp;gt;) für jeden Parameter, was in manchen Fällen für Klarheit oder spezifische Datenbankoptimierungen sorgen kann.[5, 13] &amp;lt;code&amp;gt;bindParam&amp;lt;/code&amp;gt; bindet eine Variable (ihr Wert wird erst bei &amp;lt;code&amp;gt;execute()&amp;lt;/code&amp;gt; gelesen), &amp;lt;code&amp;gt;bindValue&amp;lt;/code&amp;gt; bindet den aktuellen Wert einer Variablen oder einen Literalwert.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$stmt-&amp;amp;gt;bindValue(&amp;#039;:id&amp;#039;, $user_id, PDO::PARAM_INT);&lt;br /&gt;
$stmt-&amp;amp;gt;bindParam(&amp;#039;:name&amp;#039;, $user_name, PDO::PARAM_STR);&lt;br /&gt;
$stmt-&amp;amp;gt;execute();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;MySQLi:&amp;#039;&amp;#039;&amp;#039; Erfordert immer die explizite Bindung mittels &amp;lt;code&amp;gt;bind_param()&amp;lt;/code&amp;gt;.&lt;br /&gt;
***** &amp;lt;code&amp;gt;bind_param()&amp;lt;/code&amp;gt; (OO) / &amp;lt;code&amp;gt;mysqli_stmt_bind_param()&amp;lt;/code&amp;gt; (Proc): Nimmt als ersten Parameter einen String entgegen, der die Datentypen aller nachfolgend übergebenen Variablen definiert.[25, 50, 15, 58]&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// OO-Stil&lt;br /&gt;
$stmt-&amp;amp;gt;bind_param(&amp;quot;ssi&amp;quot;, $name, $email, $id); // String, String, Integer&lt;br /&gt;
// Prozedural&lt;br /&gt;
mysqli_stmt_bind_param($stmt, &amp;quot;ssi&amp;quot;, $name, $email, $id);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
=== Tabelle 4: MySQLi &amp;lt;code&amp;gt;bind_param()&amp;lt;/code&amp;gt; Typen-String ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Typ-Buchstabe !! PHP-Datentyp (typisch) !! Beschreibung&lt;br /&gt;
|- &lt;br /&gt;
| mwt-preservehtml=&amp;quot;&amp;quot; i=&amp;quot;&amp;quot; code=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| integer=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Integer (Ganzzahl)&lt;br /&gt;
|- &lt;br /&gt;
| mwt-preservehtml=&amp;quot;&amp;quot; d=&amp;quot;&amp;quot; code=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| double=&amp;quot;&amp;quot; float=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Double (Gleitkommazahl)&lt;br /&gt;
|- &lt;br /&gt;
| mwt-preservehtml=&amp;quot;&amp;quot; s=&amp;quot;&amp;quot; code=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| string=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| String (Zeichenkette)&lt;br /&gt;
|- &lt;br /&gt;
| mwt-preservehtml=&amp;quot;&amp;quot; b=&amp;quot;&amp;quot; code=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| string=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| BLOB (Binary Large Object), als String gesendet&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Detaillierte Code-Beispiele zur Prävention:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
Die Beispiele für INSERT, UPDATE und DELETE in Abschnitt 3.3 demonstrieren bereits die korrekte Anwendung von Prepared Statements mit PDO und MySQLi zur Verhinderung von SQL Injection. Es ist entscheidend, dieses Muster konsequent anzuwenden, wann immer externe Daten in SQL-Abfragen einfließen.&lt;br /&gt;
&lt;br /&gt;
=== 4.4 Zusätzliche Schutzmaßnahmen (Defense-in-Depth) ===&lt;br /&gt;
&lt;br /&gt;
Obwohl Prepared Statements der wichtigste Schutz gegen SQL Injection sind, sollte Sicherheit immer als mehrschichtiges Konzept (&amp;quot;Defense-in-Depth&amp;quot;) betrachtet werden.[5] Zusätzliche Maßnahmen erhöhen die Robustheit der Anwendung:&lt;br /&gt;
&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Eingabevalidierung und -bereinigung:&amp;#039;&amp;#039;&amp;#039; Überprüfen Sie &amp;#039;&amp;#039;alle&amp;#039;&amp;#039; Benutzereingaben serverseitig, bevor sie überhaupt in die Nähe der Datenbank kommen.[4, 59, 20, 5] Stellen Sie sicher, dass die Daten dem erwarteten Typ (Zahl, String, E-Mail-Format etc.) und Format entsprechen. Verwenden Sie dafür PHP-Filterfunktionen wie &amp;lt;code&amp;gt;filter_var()&amp;lt;/code&amp;gt; oder reguläre Ausdrücke.[4, 5] Dies ist eine wichtige zweite Verteidigungslinie, die ungültige oder unerwartete Daten frühzeitig abfängt. Es ersetzt jedoch &amp;#039;&amp;#039;nicht&amp;#039;&amp;#039; die Notwendigkeit von Prepared Statements.&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Prinzip der geringsten Rechte (Least Privilege):&amp;#039;&amp;#039;&amp;#039; Konfigurieren Sie den Datenbankbenutzer, den Ihre PHP-Anwendung verwendet, mit den absolut minimal notwendigen Berechtigungen.[20, 5] Wenn die Anwendung nur Daten lesen und schreiben muss, geben Sie ihr keine Rechte zum Ändern der Tabellenstruktur (&amp;lt;code&amp;gt;ALTER TABLE&amp;lt;/code&amp;gt;) oder zum Löschen von Tabellen (&amp;lt;code&amp;gt;DROP TABLE&amp;lt;/code&amp;gt;). Beschränken Sie den Zugriff auf die benötigten Datenbanken und Tabellen. Verwenden Sie niemals den &amp;lt;code&amp;gt;root&amp;lt;/code&amp;gt;- oder Admin-Benutzer der Datenbank für die Anwendung. Dies begrenzt den potenziellen Schaden, falls doch eine Schwachstelle ausgenutzt wird.&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Sichere Fehlerbehandlung:&amp;#039;&amp;#039;&amp;#039; Konfigurieren Sie PHP und Ihre Datenbankerweiterung so, dass detaillierte Fehlermeldungen (insbesondere solche, die SQL-Code oder Datenbankstrukturinformationen enthalten) niemals dem Endbenutzer angezeigt werden.[20, 5] Solche Informationen sind für Angreifer wertvoll. Loggen Sie Fehler stattdessen in eine sichere Datei auf dem Server, die nur für Administratoren zugänglich ist. Verwenden Sie &amp;lt;code&amp;gt;PDO::ERRMODE_EXCEPTION&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;mysqli_report&amp;lt;/code&amp;gt; für strukturierte Fehlerbehandlung.&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Output Escaping:&amp;#039;&amp;#039;&amp;#039; Obwohl nicht direkt zur Verhinderung von SQL Injection, ist es wichtig, Daten, die aus der Datenbank gelesen und im HTML-Kontext ausgegeben werden, korrekt zu escapen (z.B. mit &amp;lt;code&amp;gt;htmlspecialchars()&amp;lt;/code&amp;gt;), um Cross-Site Scripting (XSS)-Angriffe zu verhindern.&lt;br /&gt;
Die Kombination dieser Maßnahmen – mit Prepared Statements als Kernstück – bietet einen robusten Schutz gegen SQL Injection und andere verwandte Angriffe.&lt;br /&gt;
&lt;br /&gt;
== Nützliche PHP Array-Funktionen für Datenbankergebnisse ==&lt;br /&gt;
&lt;br /&gt;
Wenn Daten aus der Datenbank abgerufen werden, insbesondere mit Methoden wie &amp;lt;code&amp;gt;fetchAll()&amp;lt;/code&amp;gt; (PDO) oder &amp;lt;code&amp;gt;fetch_all()&amp;lt;/code&amp;gt; (MySQLi), liegen die Ergebnisse typischerweise als PHP-Array vor (meist ein Array von assoziativen Arrays, wobei jedes innere Array eine Ergebniszeile darstellt). PHP bietet eine Reihe nützlicher Funktionen zur Verarbeitung solcher Arrays.&lt;br /&gt;
&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Iteration mit &amp;lt;code&amp;gt;foreach&amp;lt;/code&amp;gt;:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
Dies ist die Standardmethode, um die einzelnen Zeilen und Spalten eines Ergebnis-Arrays zu durchlaufen.[37, 40, 44]&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;Syntax:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Iteration über die Zeilen (jedes $row ist ein assoziatives Array)&lt;br /&gt;
foreach ($results as $row) {&lt;br /&gt;
 echo &amp;quot;ID: &amp;quot;. $row[&amp;#039;id&amp;#039;]. &amp;quot;, Name: &amp;quot;. htmlspecialchars($row[&amp;#039;name&amp;#039;]). &amp;quot;&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Iteration über Zeilen mit Zugriff auf den numerischen Index der Zeile&lt;br /&gt;
foreach ($results as $index =&amp;amp;gt; $row) {&lt;br /&gt;
 echo &amp;quot;Zeile &amp;quot;. $index. &amp;quot;: ID = &amp;quot;. $row[&amp;#039;id&amp;#039;]. &amp;quot;&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;Code-Beispiel:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!--?php// Annahme: $results enthält das Ergebnis von $stmt---&amp;gt;fetchAll(PDO::FETCH_ASSOC);&lt;br /&gt;
$results = [&amp;#039;id&amp;#039; =&amp;amp;gt; 1, &amp;#039;name&amp;#039; =&amp;amp;gt; &amp;#039;Alice&amp;#039;, &amp;#039;email&amp;#039; =&amp;amp;gt; &amp;#039;alice@example.com&amp;#039;],&lt;br /&gt;
 ;&lt;br /&gt;
&lt;br /&gt;
echo &amp;quot;&amp;lt;/syntaxhighlight&amp;gt;&amp;quot;;&lt;br /&gt;
foreach ($results as $row) {&lt;br /&gt;
 echo &amp;quot;&lt;br /&gt;
*Benutzer #&amp;quot;. $row[&amp;#039;id&amp;#039;]. &amp;quot;: &amp;quot;. htmlspecialchars($row[&amp;#039;name&amp;#039;]);&lt;br /&gt;
 echo &amp;quot; (&amp;quot;. htmlspecialchars($row[&amp;#039;email&amp;#039;]). &amp;quot;)&lt;br /&gt;
&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
echo &amp;quot;&amp;quot;;&lt;br /&gt;
?&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Zählen von Ergebnissen mit &amp;lt;code&amp;gt;count()&amp;lt;/code&amp;gt;:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
Die Funktion &amp;lt;code&amp;gt;count()&amp;lt;/code&amp;gt; gibt die Anzahl der Elemente in einem Array zurück. Wenn sie auf ein mit &amp;lt;code&amp;gt;fetchAll()&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;fetch_all()&amp;lt;/code&amp;gt; erzeugtes Ergebnis-Array angewendet wird, liefert sie die Anzahl der abgerufenen Zeilen.[40, 60, 61]&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;Syntax:&amp;#039;&amp;#039;&amp;#039; &amp;lt;code&amp;gt;$anzahlZeilen = count($results);&amp;lt;/code&amp;gt;.[60]&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;Wichtiger Hinweis:&amp;#039;&amp;#039;&amp;#039; &amp;lt;code&amp;gt;count()&amp;lt;/code&amp;gt; funktioniert nur zuverlässig für die Gesamtzahl der Zeilen, wenn &amp;#039;&amp;#039;alle&amp;#039;&amp;#039; Zeilen zuvor in ein Array geladen wurden (also nach &amp;lt;code&amp;gt;fetchAll&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;fetch_all&amp;lt;/code&amp;gt;). Wenn Sie Ergebnisse zeilenweise mit &amp;lt;code&amp;gt;fetch()&amp;lt;/code&amp;gt; in einer &amp;lt;code&amp;gt;while&amp;lt;/code&amp;gt;-Schleife verarbeiten, müssen Sie einen manuellen Zähler innerhalb der Schleife inkrementieren, um die Anzahl der verarbeiteten Zeilen zu ermitteln.[40] Alternativ können &amp;lt;code&amp;gt;PDOStatement::rowCount()&amp;lt;/code&amp;gt; [8, 9] oder &amp;lt;code&amp;gt;mysqli_result::$num_rows&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;mysqli_stmt::$num_rows&amp;lt;/code&amp;gt; [26, 15] verwendet werden, um die Anzahl der von einer &amp;lt;code&amp;gt;SELECT&amp;lt;/code&amp;gt;-Abfrage zurückgegebenen Zeilen zu erhalten, &amp;#039;&amp;#039;bevor&amp;#039;&amp;#039; die Daten gefetched werden (das Verhalten von &amp;lt;code&amp;gt;rowCount&amp;lt;/code&amp;gt; kann jedoch bei anderen Anweisungstypen wie &amp;lt;code&amp;gt;UPDATE&amp;lt;/code&amp;gt; variieren).&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;Code-Beispiel:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!--?php&lt;br /&gt;
// Annahme: $results von fetchAll()&lt;br /&gt;
$results = [ /*... wie oben... */ ];&lt;br /&gt;
$anzahl = count($results);&lt;br /&gt;
echo &amp;quot;Anzahl der Benutzer (via fetchAll/count): $anzahl&amp;lt;br--&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
// Vergleich: Zählen bei zeilenweiser Verarbeitung&lt;br /&gt;
$stmt = $pdo-&amp;amp;gt;query(&amp;quot;SELECT id FROM users&amp;quot;); // Annahme: PDO-Statement&lt;br /&gt;
$manueller_zaehler = 0;&lt;br /&gt;
while ($row = $stmt-&amp;amp;gt;fetch()) {&lt;br /&gt;
 $manueller_zaehler++;&lt;br /&gt;
}&lt;br /&gt;
echo &amp;quot;Anzahl der Benutzer (via while/fetch): $manueller_zaehler&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
// Alternative: rowCount() nach SELECT (PDO)&lt;br /&gt;
$stmt_count = $pdo-&amp;amp;gt;query(&amp;quot;SELECT id FROM users&amp;quot;);&lt;br /&gt;
$rowCountResult = $stmt_count-&amp;amp;gt;rowCount(); // Kann bei manchen DBs/Treibern funktionieren&lt;br /&gt;
echo &amp;quot;Anzahl der Benutzer (via rowCount nach SELECT): $rowCountResult&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
?&amp;amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;(Optional) Weitere nützliche Array-Funktionen:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
**** &amp;lt;code&amp;gt;array_column(array $input, mixed $column_key [, mixed $index_key = null ])&amp;lt;/code&amp;gt;: Extrahiert alle Werte aus einer einzelnen Spalte des Ergebnis-Arrays und gibt sie als neues, eindimensionales Array zurück.[62] Sehr nützlich, um z.B. eine Liste aller Benutzer-IDs oder E-Mail-Adressen zu erhalten.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Annahme: $results enthält das Array von assoziativen Arrays&lt;br /&gt;
$alle_emails = array_column($results, &amp;#039;email&amp;#039;);&lt;br /&gt;
// $alle_emails ist nun [&amp;#039;alice@example.com&amp;#039;, &amp;#039;bob@example.com&amp;#039;]&lt;br /&gt;
print_r($alle_emails);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
**** &amp;lt;code&amp;gt;array_map(callable $callback, array $array1 [, array...$arrays ])&amp;lt;/code&amp;gt;: Wendet eine benutzerdefinierte Funktion (&amp;lt;code&amp;gt;callback&amp;lt;/code&amp;gt;) auf jedes Element eines Arrays an und gibt ein neues Array mit den Ergebnissen zurück.[63] Nützlich zur Transformation von Daten (z.B. Formatierung, Bereinigung).&lt;br /&gt;
**** &amp;lt;code&amp;gt;array_filter(array $array [, callable $callback [, int $flag = 0 ]])&amp;lt;/code&amp;gt;: Filtert Elemente eines Arrays basierend auf einer Callback-Funktion.[61] Nützlich, um nur bestimmte Zeilen aus dem Ergebnis-Array zu behalten (obwohl dies oft effizienter direkt in der SQL-Abfrage geschieht).&lt;br /&gt;
**** &amp;lt;code&amp;gt;array_count_values(array $array)&amp;lt;/code&amp;gt;: Zählt, wie oft jeder eindeutige Wert in einem eindimensionalen Array vorkommt.[63] Kann nützlich sein, wenn man z.B. mit &amp;lt;code&amp;gt;array_column&amp;lt;/code&amp;gt; eine Spalte extrahiert hat und die Häufigkeit bestimmter Werte (z.B. Status) wissen möchte.&lt;br /&gt;
Diese Funktionen, insbesondere &amp;lt;code&amp;gt;foreach&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;count&amp;lt;/code&amp;gt;, sind grundlegende Werkzeuge bei der Verarbeitung von Datenbankergebnissen in PHP.&lt;br /&gt;
&lt;br /&gt;
== Zusammenfassung &amp;amp; Best Practices ==&lt;br /&gt;
&lt;br /&gt;
Die Integration von Datenbanken ist ein zentraler Aspekt der PHP-Webentwicklung. Dieses Kapitel hat die Grundlagen für die Interaktion mit MySQL-Datenbanken gelegt. Hier sind die wichtigsten Punkte und Best Practices zusammengefasst:&lt;br /&gt;
&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Wahl der Erweiterung:&amp;#039;&amp;#039;&amp;#039; Verwenden Sie ausschließlich die modernen Erweiterungen &amp;#039;&amp;#039;&amp;#039;PDO&amp;#039;&amp;#039;&amp;#039; oder &amp;#039;&amp;#039;&amp;#039;MySQLi&amp;#039;&amp;#039;&amp;#039;.[2, 29, 5, 15]&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;PDO&amp;#039;&amp;#039;&amp;#039; wird generell für neue Projekte empfohlen, da es Datenbankunabhängigkeit bietet und eine konsistentere, oft als einfacher empfundene API (insbesondere bei der Parameterbindung) hat.[16, 3, 64]&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;MySQLi&amp;#039;&amp;#039;&amp;#039; ist eine gute Wahl, wenn ausschließlich mit MySQL gearbeitet wird und spezifische MySQL-Funktionen benötigt werden oder wenn eine prozedurale Schnittstelle bevorzugt wird.[15]&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Prepared Statements:&amp;#039;&amp;#039;&amp;#039; Dies ist die wichtigste Sicherheitsmaßnahme. Verwenden Sie Prepared Statements immer dann, wenn externe Daten (Benutzereingaben, URL-Parameter etc.) Teil einer SQL-Abfrage werden (insbesondere bei &amp;lt;code&amp;gt;WHERE&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;INSERT&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;UPDATE&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;DELETE&amp;lt;/code&amp;gt;).[2, 25, 5] Sie verhindern SQL Injection zuverlässig durch die Trennung von Code und Daten.&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Sichere Fehlerbehandlung:&amp;#039;&amp;#039;&amp;#039; Konfigurieren Sie PDO (&amp;lt;code&amp;gt;PDO::ATTR_ERRMODE =&amp;amp;gt; PDO::ERRMODE_EXCEPTION&amp;lt;/code&amp;gt;) oder MySQLi (&amp;lt;code&amp;gt;mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT)&amp;lt;/code&amp;gt;) so, dass Fehler als Exceptions behandelt werden.[1, 18] Fangen Sie diese Exceptions ab und loggen Sie detaillierte Fehlermeldungen serverseitig, anstatt sie dem Benutzer anzuzeigen.[20, 5]&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Input Validation:&amp;#039;&amp;#039;&amp;#039; Validieren Sie alle Benutzereingaben serverseitig auf Typ, Format und erlaubte Werte, bevor Sie sie an die Datenbank übergeben. Dies ist eine wichtige zusätzliche Schutzschicht.[4, 5]&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Prinzip der geringsten Rechte (Least Privilege):&amp;#039;&amp;#039;&amp;#039; Der Datenbankbenutzer, den Ihre PHP-Anwendung verwendet, sollte nur die minimal notwendigen Berechtigungen für seine Aufgaben besitzen.[5]&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Sichere Zugangsdaten:&amp;#039;&amp;#039;&amp;#039; Speichern Sie Datenbank-Credentials (Hostname, Benutzername, Passwort) niemals direkt im PHP-Code oder in Dateien innerhalb des Web-Roots.[20, 65] Verwenden Sie stattdessen Konfigurationsdateien außerhalb des öffentlich zugänglichen Bereichs oder Umgebungsvariablen.&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Code-Struktur:&amp;#039;&amp;#039;&amp;#039; Kapseln Sie Datenbankverbindungslogik und wiederkehrende Abfragen in Funktionen oder Klassen, um die Wartbarkeit und Wiederverwendbarkeit zu verbessern.[20]&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Effizienz:&amp;#039;&amp;#039;&amp;#039; Laden Sie große Ergebnismengen nicht komplett in den Speicher (&amp;lt;code&amp;gt;fetchAll&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;fetch_all&amp;lt;/code&amp;gt;). Verwenden Sie stattdessen zeilenweise Verarbeitung (&amp;lt;code&amp;gt;while&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;fetch&amp;lt;/code&amp;gt;) oder Iteratoren (PDO mit PHP 8+).[37, 40] Nutzen Sie persistente Verbindungen nur nach sorgfältiger Abwägung der Vor- und Nachteile.[7, 27]&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Defense-in-Depth:&amp;#039;&amp;#039;&amp;#039; Betrachten Sie Sicherheit als Gesamtkonzept. Die Kombination aus der richtigen API-Wahl, konsequenten Prepared Statements, Eingabevalidierung, minimalen Rechten, sicherer Fehlerbehandlung, sicherer Speicherung von Zugangsdaten und regelmäßigen Software-Updates bildet eine robuste Verteidigung.&lt;/div&gt;</summary>
		<author><name>Völkl Anna</name></author>
	</entry>
	<entry>
		<id>https://mediawiki.fernfh.ac.at/mediawiki/index.php?title=Datenbankintegration_(Webdevelopment)&amp;diff=6572</id>
		<title>Datenbankintegration (Webdevelopment)</title>
		<link rel="alternate" type="text/html" href="https://mediawiki.fernfh.ac.at/mediawiki/index.php?title=Datenbankintegration_(Webdevelopment)&amp;diff=6572"/>
		<updated>2025-05-01T12:46:54Z</updated>

		<summary type="html">&lt;p&gt;Völkl Anna: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Kapitel X: Datenbankintegration mit PHP und MySQL =&lt;br /&gt;
&lt;br /&gt;
== Einleitung: Warum Datenbanken in PHP-Webanwendungen? ==&lt;br /&gt;
&lt;br /&gt;
Moderne Webanwendungen sind selten statisch. Sie leben von dynamischen Inhalten, Benutzerinteraktionen und der Fähigkeit, Informationen über Sitzungen hinweg zu speichern und wieder abzurufen. Ob es sich um ein Content-Management-System (CMS), einen Online-Shop, ein soziales Netzwerk oder eine einfache Benutzerverwaltung handelt – die Persistenz von Daten ist entscheidend. Hier kommen Datenbanken ins Spiel.&lt;br /&gt;
&lt;br /&gt;
PHP allein, als serverseitige Skriptsprache, kann Daten nur für die Dauer der Ausführung eines Skripts im Arbeitsspeicher halten. Sobald das Skript beendet ist, gehen diese Informationen verloren. Um Daten dauerhaft zu speichern, zu organisieren, zu ändern und effizient abzurufen, benötigen PHP-Anwendungen ein externes Datenbanksystem.&lt;br /&gt;
&lt;br /&gt;
Eine der populärsten Datenbanklösungen im Webentwicklungsbereich, insbesondere in Kombination mit PHP, ist MySQL. Als relationales Datenbankmanagementsystem (RDBMS) bietet MySQL eine strukturierte Methode zur Datenspeicherung in Tabellen, die miteinander in Beziehung stehen können. Es ist ein Kernbestandteil vieler Webserver-Setups wie LAMP (Linux, Apache, MySQL, PHP) oder LEMP (Linux, Nginx, MySQL, PHP).&lt;br /&gt;
&lt;br /&gt;
Um die Brücke zwischen der PHP-Anwendung und der MySQL-Datenbank zu schlagen, stellt PHP spezielle Erweiterungen (Extensions) bereit. Diese Erweiterungen fungieren als Schnittstelle oder API (Application Programming Interface), die es PHP-Skripten ermöglichen, eine Verbindung zur Datenbank herzustellen, SQL-Befehle (Structured Query Language Queries) zu senden und die von der Datenbank zurückgegebenen Ergebnisse zu empfangen und zu verarbeiten.&lt;br /&gt;
&lt;br /&gt;
Dieses Kapitel führt Sie in die Grundlagen der Datenbankintegration mit PHP und MySQL ein. Sie lernen die verschiedenen verfügbaren PHP-Erweiterungen kennen, wobei der Schwerpunkt auf den modernen und sicheren Ansätzen liegt: PDO (PHP Data Objects) und MySQLi (MySQL Improved). Wir werden untersuchen, wie man Verbindungen aufbaut, grundlegende Datenbankoperationen – bekannt als CRUD (Create, Read, Update, Delete) – durchführt und wie man Daten sicher abfragt und manipuliert. Ein besonderes Augenmerk liegt dabei auf der Sicherheit, insbesondere auf der Vermeidung von SQL-Injection-Angriffen durch den Einsatz von Prepared Statements. Schließlich betrachten wir nützliche PHP-Array-Funktionen zur Verarbeitung der abgerufenen Datenbankergebnisse.&lt;br /&gt;
&lt;br /&gt;
== Überblick über PHP-Datenbankerweiterungen ==&lt;br /&gt;
&lt;br /&gt;
Die Art und Weise, wie PHP mit Datenbanken interagiert, hat sich im Laufe der Zeit weiterentwickelt. Ursprünglich gab es für jede populäre Datenbank eine eigene spezifische Erweiterung. Für MySQL waren dies die &amp;lt;code&amp;gt;mysql_*&amp;lt;/code&amp;gt; Funktionen. Diese sind jedoch inzwischen veraltet und wurden durch modernere, flexiblere und sicherere Alternativen ersetzt. Heute stehen Entwicklern hauptsächlich zwei empfohlene Erweiterungen für die Arbeit mit MySQL zur Verfügung: PDO und MySQLi.&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;PDO (PHP Data Objects):&amp;#039;&amp;#039;&amp;#039; Eine allgemeine Datenzugriffs-Abstraktionsschicht, die mit verschiedenen Datenbanksystemen verwendet werden kann.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;MySQLi (MySQL Improved):&amp;#039;&amp;#039;&amp;#039; Eine Erweiterung, die speziell für die Arbeit mit MySQL entwickelt wurde und dessen spezifische Funktionen unterstützt.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;&amp;lt;code&amp;gt;mysql_*&amp;lt;/code&amp;gt; Funktionen:&amp;#039;&amp;#039;&amp;#039; Die ursprüngliche MySQL-Erweiterung, die seit PHP 7.0 entfernt wurde und nicht mehr verwendet werden sollte.&lt;br /&gt;
Im Folgenden werden diese drei Optionen detaillierter vorgestellt.&lt;br /&gt;
&lt;br /&gt;
=== PDO (PHP Data Objects) ===&lt;br /&gt;
&lt;br /&gt;
Die PDO-Erweiterung definiert eine leichtgewichtige und konsistente Schnittstelle für den Datenbankzugriff in PHP. Sie fungiert als &amp;#039;&amp;#039;Datenzugriffs-Abstraktionsschicht&amp;#039;&amp;#039;. Das bedeutet, dass Entwickler unabhängig von der verwendeten Datenbank dieselben PDO-Funktionen nutzen können, um Abfragen auszuführen und Daten abzurufen.[1, 2] Es ist jedoch wichtig zu verstehen, dass PDO &amp;#039;&amp;#039;keine&amp;#039;&amp;#039; Datenbankabstraktion im Sinne einer SQL-Dialekt-Übersetzung oder der Emulation fehlender Datenbankfeatures bietet. Um eine Verbindung zu einer spezifischen Datenbank wie MySQL herzustellen, benötigt PDO einen entsprechenden datenbankspezifischen Treiber, z.B. &amp;lt;code&amp;gt;pdo_mysql&amp;lt;/code&amp;gt;, der in der PHP-Konfiguration aktiviert sein muss.[1]&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Vorteile von PDO:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Datenbankunabhängigkeit:&amp;#039;&amp;#039;&amp;#039; Der größte Vorteil von PDO ist die Möglichkeit, den Code für Datenbankinteraktionen weitgehend unverändert zu lassen, selbst wenn das zugrunde liegende Datenbanksystem gewechselt wird (z.B. von MySQL zu PostgreSQL).[1, 2] Dies erhöht die Portabilität und Flexibilität von PHP-Anwendungen erheblich.[2, 3]&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Sicherheit durch Prepared Statements:&amp;#039;&amp;#039;&amp;#039; PDO bietet eine robuste und konsistente Implementierung von Prepared Statements. Diese Technik ist der Goldstandard zur Verhinderung von SQL-Injection-Angriffen, da sie die SQL-Befehlsstruktur strikt von den übergebenen Daten trennt.[1, 2, 4, 5, 6]&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Benutzerfreundlichkeit und Konsistenz:&amp;#039;&amp;#039;&amp;#039; PDO stellt eine einheitliche API mit nützlichen Hilfsfunktionen bereit, wie z.B. vielfältige &amp;quot;Fetch Modes&amp;quot;, um die Struktur der abgerufenen Daten zu steuern.[2] Die konsistente Funktionsweise über verschiedene Datenbanktreiber hinweg vereinfacht die Entwicklung.&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Moderne Fehlerbehandlung:&amp;#039;&amp;#039;&amp;#039; PDO verwendet standardmäßig Exceptions (&amp;lt;code&amp;gt;PDOException&amp;lt;/code&amp;gt;) zur Signalisierung von Fehlern.[1, 2, 7] Dies ermöglicht eine strukturierte und moderne Fehlerbehandlung mittels &amp;lt;code&amp;gt;try...catch&amp;lt;/code&amp;gt;-Blöcken, was als Best Practice gilt.[1, 2]&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Verbindungsaufbau mit PDO (Beispiel MySQL):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;DSN (Data Source Name):&amp;#039;&amp;#039;&amp;#039; Die Verbindungsparameter werden in einer Zeichenkette, dem DSN, übergeben. Für MySQL hat der DSN typischerweise das Format &amp;lt;code&amp;gt;mysql:host=hostname;dbname=datenbankname;charset=zeichensatz;unix_socket=pfad_zum_socket&amp;lt;/code&amp;gt;. Die Angabe des Zeichensatzes (z.B. &amp;lt;code&amp;gt;utf8mb4&amp;lt;/code&amp;gt;) ist wichtig für die korrekte Datenübertragung.[1, 7] Beispiel: &amp;lt;code&amp;gt;mysql:host=localhost;dbname=test;charset=utf8mb4&amp;lt;/code&amp;gt;.&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;PDO-Konstruktor:&amp;#039;&amp;#039;&amp;#039; Eine Verbindung wird durch Instanziierung der PDO-Klasse hergestellt: &amp;lt;code&amp;gt;$pdo = new PDO($dsn, $username, $password, $options);&amp;lt;/code&amp;gt;.[1, 7, 8] Die Parameter sind der DSN, der Datenbankbenutzername, das Passwort und ein optionales Array mit Treiberoptionen.&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Verbindungsoptionen (&amp;lt;code&amp;gt;$options&amp;lt;/code&amp;gt;):&amp;#039;&amp;#039;&amp;#039; Über dieses Array können wichtige Verhaltensweisen von PDO gesteuert werden:&lt;br /&gt;
*** &amp;lt;code&amp;gt;PDO::ATTR_ERRMODE =&amp;amp;gt; PDO::ERRMODE_EXCEPTION&amp;lt;/code&amp;gt;: Dies ist die empfohlene Einstellung für die Fehlerbehandlung. PDO wirft bei Fehlern eine &amp;lt;code&amp;gt;PDOException&amp;lt;/code&amp;gt;, die abgefangen werden kann.[1, 2, 4]&lt;br /&gt;
*** &amp;lt;code&amp;gt;PDO::ATTR_DEFAULT_FETCH_MODE =&amp;amp;gt; PDO::FETCH_ASSOC&amp;lt;/code&amp;gt;: Legt fest, dass Daten standardmäßig als assoziatives Array (&amp;lt;code&amp;gt;spaltenname =&amp;amp;gt; wert&amp;lt;/code&amp;gt;) zurückgegeben werden, was oft die Weiterverarbeitung erleichtert.[1, 9]&lt;br /&gt;
*** &amp;lt;code&amp;gt;PDO::ATTR_EMULATE_PREPARES =&amp;amp;gt; false&amp;lt;/code&amp;gt;: Deaktiviert die Emulation von Prepared Statements durch PDO und zwingt die Verwendung nativer Prepared Statements des Datenbanktreibers. Dies gilt als die sicherste und oft performanteste Methode.[1, 10, 9, 11] Standardmäßig ist die Emulation aktiviert (&amp;lt;code&amp;gt;true&amp;lt;/code&amp;gt;). Obwohl PDO auch im emulierten Modus durch korrektes Escaping Schutz vor SQL Injection bietet [12, 5, 13], eliminiert die Deaktivierung potenzielle, wenn auch seltene, Sicherheitsrisiken, die in bestimmten Konstellationen (z.B. bei Verwendung exotischer Zeichensätze wie GBK [11]) auftreten könnten. Die explizite Deaktivierung ist daher eine wichtige Best Practice.&lt;br /&gt;
*** &amp;lt;code&amp;gt;PDO::ATTR_PERSISTENT =&amp;amp;gt; true&amp;lt;/code&amp;gt;: Aktiviert persistente Datenbankverbindungen. Diese werden nicht am Ende des Skripts geschlossen, sondern in einem Pool gehalten und für nachfolgende Anfragen wiederverwendet. Dies kann die Performance von Webanwendungen verbessern, da der Overhead des Verbindungsaufbaus entfällt. Die Verwendung erfordert jedoch sorgfältige Überlegung, da der Zustand der Verbindung zwischen Anfragen bestehen bleiben kann.[7]&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Fehlerbehandlung:&amp;#039;&amp;#039;&amp;#039; Der Verbindungsaufbau sollte immer in einem &amp;lt;code&amp;gt;try...catch&amp;lt;/code&amp;gt;-Block erfolgen, um &amp;lt;code&amp;gt;PDOException&amp;lt;/code&amp;gt; abzufangen und angemessen darauf zu reagieren (z.B. Fehlermeldung loggen, alternative Aktion ausführen).[1, 7] Es ist kritisch, detaillierte Fehlermeldungen in Produktionsumgebungen nicht an den Benutzer auszugeben, da sie sensible Informationen enthalten könnten. Die PHP-Einstellung &amp;lt;code&amp;gt;display_errors&amp;lt;/code&amp;gt; sollte daher auf &amp;lt;code&amp;gt;0&amp;lt;/code&amp;gt; gesetzt sein.[7]&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Verbindung schließen:&amp;#039;&amp;#039;&amp;#039; Eine PDO-Verbindung bleibt für die Lebensdauer des PDO-Objekts aktiv. Um sie explizit zu schließen, müssen alle Referenzen auf das PDO-Objekt und alle davon abgeleiteten &amp;lt;code&amp;gt;PDOStatement&amp;lt;/code&amp;gt;-Objekte entfernt werden, typischerweise durch Zuweisung von &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt;.[7] PHP schließt die Verbindung automatisch am Ende des Skriptlaufs, wenn dies nicht manuell geschieht.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Code-Beispiel (PDO-Verbindung zu MySQL):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!--?php$host = &amp;#039;localhost&amp;#039;;&lt;br /&gt;
$dbname = &amp;#039;meine_datenbank&amp;#039;;&lt;br /&gt;
$username = &amp;#039;mein_benutzer&amp;#039;;&lt;br /&gt;
$password = &amp;#039;mein_passwort&amp;#039;;&lt;br /&gt;
$charset = &amp;#039;utf8mb4&amp;#039;;&lt;br /&gt;
&lt;br /&gt;
$dsn = &amp;quot;mysql:host=$host;dbname=$dbname;charset=$charset&amp;quot;;&lt;br /&gt;
$options =;&lt;br /&gt;
&lt;br /&gt;
try {&lt;br /&gt;
     $pdo = new PDO($dsn, $username, $password, $options);&lt;br /&gt;
     // Erfolgreich verbunden, $pdo-Objekt kann nun verwendet werden.&lt;br /&gt;
     // echo &amp;quot;Verbindung erfolgreich hergestellt!&amp;quot;;&lt;br /&gt;
} catch (\PDOException $e) {&lt;br /&gt;
     // Fehler beim Verbindungsaufbau&lt;br /&gt;
     // In Produktion: Fehler loggen, statt ausgeben&lt;br /&gt;
     error_log(&amp;quot;Datenbankverbindungsfehler: &amp;quot;. $e---&amp;gt;getMessage());&lt;br /&gt;
 die(&amp;quot;Es gab ein Problem mit der Datenbankverbindung. Bitte versuchen Sie es später erneut.&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//... hier Datenbankoperationen mit $pdo durchführen...&lt;br /&gt;
&lt;br /&gt;
// Verbindung explizit schließen (optional, da PHP dies am Skriptende tut)&lt;br /&gt;
// $pdo = null;&lt;br /&gt;
?&amp;amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Dieses Beispiel zeigt den empfohlenen Weg, eine PDO-Verbindung zu MySQL herzustellen, inklusive wichtiger Optionen für Fehlerbehandlung und Sicherheit sowie einem &amp;lt;code&amp;gt;try...catch&amp;lt;/code&amp;gt;-Block zum Abfangen von Verbindungsfehlern.&lt;br /&gt;
&lt;br /&gt;
=== MySQLi (MySQL Improved) ===&lt;br /&gt;
&lt;br /&gt;
Die MySQLi-Erweiterung ist, wie der Name schon sagt, speziell für die Interaktion mit MySQL-Datenbanken konzipiert und bietet Unterstützung für Funktionen, die ab MySQL Version 4.1 eingeführt wurden.[14, 15] Sie ermöglicht den Zugriff auf MySQL-spezifische Features, die über PDO möglicherweise nicht direkt verfügbar sind.[16, 15]&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Duale Schnittstelle:&amp;#039;&amp;#039;&amp;#039; Ein charakteristisches Merkmal von MySQLi ist die Unterstützung von zwei Programmierstilen [17]:&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Prozedural:&amp;#039;&amp;#039;&amp;#039; Dieser Stil ähnelt den alten &amp;lt;code&amp;gt;mysql_*&amp;lt;/code&amp;gt; Funktionen. Die Funktionsnamen beginnen typischerweise mit &amp;lt;code&amp;gt;mysqli_&amp;lt;/code&amp;gt; (z.B. &amp;lt;code&amp;gt;mysqli_connect()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;mysqli_query()&amp;lt;/code&amp;gt;). Der Datenbankverbindungs-Handle (eine Ressource oder ein Objekt) muss dabei explizit als erster Parameter an die meisten Funktionen übergeben werden.[17] Dieser Stil wird oft von Entwicklern bevorzugt, die von der alten &amp;lt;code&amp;gt;mysql&amp;lt;/code&amp;gt;-Erweiterung migrieren.[17]&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Objektorientiert (OO):&amp;#039;&amp;#039;&amp;#039; Hier wird ein Objekt der &amp;lt;code&amp;gt;mysqli&amp;lt;/code&amp;gt;-Klasse erstellt, und Datenbankoperationen werden als Methoden dieses Objekts aufgerufen (z.B. &amp;lt;code&amp;gt;$mysqli-&amp;amp;gt;query()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;$mysqli-&amp;amp;gt;prepare()&amp;lt;/code&amp;gt;).[17] Dieser Ansatz passt oft besser zu modernen PHP-Entwicklungspraktiken und vermeidet die Notwendigkeit, den Verbindungshandle ständig zu übergeben. Die offizielle PHP-Dokumentation für MySQLi ist primär im objektorientierten Stil verfasst.[17] Es gibt keine nennenswerten Performance-Unterschiede zwischen den beiden Stilen; die Wahl ist meist eine Frage der persönlichen Präferenz oder Projektkonventionen.[17]&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Verbindungsaufbau mit MySQLi:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Prozedural:&amp;#039;&amp;#039;&amp;#039; Die Funktion &amp;lt;code&amp;gt;mysqli_connect()&amp;lt;/code&amp;gt; wird verwendet: &amp;lt;code&amp;gt;$link = mysqli_connect($host, $user, $pass, $db, $port, $socket);&amp;lt;/code&amp;gt;. Sie gibt bei Erfolg ein &amp;lt;code&amp;gt;mysqli&amp;lt;/code&amp;gt;-Objekt (oder &amp;lt;code&amp;gt;false&amp;lt;/code&amp;gt; bei Fehlern vor PHP 8.1) zurück.[17, 18] Fehler können mit &amp;lt;code&amp;gt;mysqli_connect_errno()&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;mysqli_connect_error()&amp;lt;/code&amp;gt; geprüft werden.[18]&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Objektorientiert:&amp;#039;&amp;#039;&amp;#039; Ein neues &amp;lt;code&amp;gt;mysqli&amp;lt;/code&amp;gt;-Objekt wird instanziiert: &amp;lt;code&amp;gt;$mysqli = new mysqli($host, $user, $pass, $db, $port, $socket);&amp;lt;/code&amp;gt;. Wichtig: Der Konstruktor gibt &amp;#039;&amp;#039;immer&amp;#039;&amp;#039; ein Objekt zurück, auch wenn die Verbindung fehlschlägt. Der Verbindungsstatus muss explizit über die Eigenschaften &amp;lt;code&amp;gt;$mysqli-&amp;amp;gt;connect_errno&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;$mysqli-&amp;amp;gt;connect_error&amp;lt;/code&amp;gt; geprüft werden.[17, 18]&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Fortgeschrittene Verbindung:&amp;#039;&amp;#039;&amp;#039; Für feinere Kontrolle über Verbindungsoptionen (z.B. Setzen von Timeouts oder Initialisierungsbefehlen &amp;#039;&amp;#039;vor&amp;#039;&amp;#039; dem Verbindungsaufbau) können &amp;lt;code&amp;gt;mysqli_init()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;mysqli_options()&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;mysqli_real_connect()&amp;lt;/code&amp;gt; verwendet werden.[18, 19]&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Fehlerbehandlung:&amp;#039;&amp;#039;&amp;#039; Neben der manuellen Prüfung von Fehlercodes und -meldungen ist die empfohlene Methode, MySQLi so zu konfigurieren, dass es bei Fehlern Exceptions wirft. Dies geschieht mit &amp;lt;code&amp;gt;mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);&amp;lt;/code&amp;gt;. Danach können Datenbankfehler elegant mit &amp;lt;code&amp;gt;try...catch&amp;lt;/code&amp;gt;-Blöcken behandelt werden, ähnlich wie bei PDO.[18, 20, 21, 22, 23, 24, 25, 26]&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Verbindung schließen:&amp;#039;&amp;#039;&amp;#039; Prozedural mit &amp;lt;code&amp;gt;mysqli_close($link)&amp;lt;/code&amp;gt;, objektorientiert mit &amp;lt;code&amp;gt;$mysqli-&amp;amp;gt;close()&amp;lt;/code&amp;gt;.[17, 18, 19]&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Persistente Verbindungen:&amp;#039;&amp;#039;&amp;#039; MySQLi unterstützt ebenfalls persistente Verbindungen, die über PHP-Konfigurationseinstellungen (&amp;lt;code&amp;gt;mysqli.allow_persistent&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;mysqli.max_persistent&amp;lt;/code&amp;gt;) gesteuert werden.[27] Standardmäßig setzt MySQLi den Zustand einer wiederverwendeten persistenten Verbindung zurück (via &amp;lt;code&amp;gt;mysqli::change_user()&amp;lt;/code&amp;gt;), was zwar für Konsistenz sorgt, aber auch Performance kosten kann.[27]&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Code-Beispiele (MySQLi-Verbindungen):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Objektorientiert (mit Exception-Handling):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!--?php$host = &amp;#039;localhost&amp;#039;;&lt;br /&gt;
$user = &amp;#039;mein_benutzer&amp;#039;;&lt;br /&gt;
$pass = &amp;#039;mein_passwort&amp;#039;;&lt;br /&gt;
$db = &amp;#039;meine_datenbank&amp;#039;;&lt;br /&gt;
&lt;br /&gt;
// Fehlerberichterstattung für Exceptions aktivieren&lt;br /&gt;
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);&lt;br /&gt;
&lt;br /&gt;
try {&lt;br /&gt;
    $mysqli = new mysqli($host, $user, $pass, $db);&lt;br /&gt;
    // Zeichensatz setzen (wichtig!)&lt;br /&gt;
    $mysqli---&amp;gt;set_charset(&amp;#039;utf8mb4&amp;#039;);&lt;br /&gt;
 // Erfolgreich verbunden&lt;br /&gt;
 // echo &amp;quot;Verbindung erfolgreich (OO)! Host-Info: &amp;quot;. $mysqli-&amp;amp;gt;host_info;&lt;br /&gt;
} catch (mysqli_sql_exception $e) {&lt;br /&gt;
 // Fehler beim Verbindungsaufbau oder set_charset&lt;br /&gt;
 error_log(&amp;quot;MySQLi Verbindungsfehler: &amp;quot;. $e-&amp;amp;gt;getMessage());&lt;br /&gt;
 die(&amp;quot;Es gab ein Problem mit der Datenbankverbindung.&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//... Datenbankoperationen mit $mysqli...&lt;br /&gt;
&lt;br /&gt;
// Verbindung schließen&lt;br /&gt;
// $mysqli-&amp;amp;gt;close();&lt;br /&gt;
?&amp;amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Prozedural (mit Exception-Handling):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!--?php$host = &amp;#039;localhost&amp;#039;;&lt;br /&gt;
$user = &amp;#039;mein_benutzer&amp;#039;;&lt;br /&gt;
$pass = &amp;#039;mein_passwort&amp;#039;;&lt;br /&gt;
$db = &amp;#039;meine_datenbank&amp;#039;;&lt;br /&gt;
&lt;br /&gt;
// Fehlerberichterstattung für Exceptions aktivieren&lt;br /&gt;
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);&lt;br /&gt;
&lt;br /&gt;
try {&lt;br /&gt;
    $link = mysqli_connect($host, $user, $pass, $db);&lt;br /&gt;
    // Zeichensatz setzen&lt;br /&gt;
    mysqli_set_charset($link, &amp;#039;utf8mb4&amp;#039;);&lt;br /&gt;
    // Erfolgreich verbunden&lt;br /&gt;
    // echo &amp;quot;Verbindung erfolgreich (Prozedural)! Host-Info: &amp;quot;. mysqli_get_host_info($link);&lt;br /&gt;
} catch (mysqli_sql_exception $e) {&lt;br /&gt;
    // Fehler beim Verbindungsaufbau oder set_charset&lt;br /&gt;
    error_log(&amp;quot;MySQLi Verbindungsfehler: &amp;quot;. $e---&amp;gt;getMessage());&lt;br /&gt;
 die(&amp;quot;Es gab ein Problem mit der Datenbankverbindung.&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//... Datenbankoperationen mit $link...&lt;br /&gt;
&lt;br /&gt;
// Verbindung schließen&lt;br /&gt;
// mysqli_close($link);&lt;br /&gt;
?&amp;amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Bedeutung von &amp;lt;code&amp;gt;mysqlnd&amp;lt;/code&amp;gt;:&amp;#039;&amp;#039;&amp;#039; Die Benutzerfreundlichkeit, insbesondere bei der Arbeit mit Prepared Statements, wird stark durch den verwendeten MySQL-Treiber beeinflusst. Der &amp;#039;&amp;#039;&amp;#039;MySQL Native Driver (&amp;lt;code&amp;gt;mysqlnd&amp;lt;/code&amp;gt;)&amp;#039;&amp;#039;&amp;#039;, der seit PHP 5.4 standardmäßig enthalten ist [16], ist hier entscheidend. &amp;lt;code&amp;gt;mysqlnd&amp;lt;/code&amp;gt; stellt die Methode &amp;lt;code&amp;gt;get_result()&amp;lt;/code&amp;gt; (bzw. die Funktion &amp;lt;code&amp;gt;mysqli_stmt_get_result()&amp;lt;/code&amp;gt;) zur Verfügung.[25, 15] Diese Methode erlaubt es, nach dem Ausführen eines Prepared Statements ein Standard-&amp;lt;code&amp;gt;mysqli_result&amp;lt;/code&amp;gt;-Objekt zu erhalten, von dem dann wie gewohnt mit &amp;lt;code&amp;gt;fetch_assoc()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;fetch_all()&amp;lt;/code&amp;gt; etc. die Ergebnisse abgerufen werden können.[25] Ohne &amp;lt;code&amp;gt;mysqlnd&amp;lt;/code&amp;gt; (bei Verwendung der älteren &amp;lt;code&amp;gt;libmysqlclient&amp;lt;/code&amp;gt;-Bibliothek) ist das Abrufen von Ergebnissen aus Prepared Statements deutlich umständlicher und erfordert die manuelle Bindung jeder einzelnen Ergebnisspalte an PHP-Variablen mittels &amp;lt;code&amp;gt;bind_result()&amp;lt;/code&amp;gt; und anschließendes &amp;lt;code&amp;gt;fetch()&amp;lt;/code&amp;gt;.[25] Da &amp;lt;code&amp;gt;mysqlnd&amp;lt;/code&amp;gt; heute der Standard ist, konzentrieren sich moderne Tutorials und Beispiele meist auf die &amp;lt;code&amp;gt;get_result()&amp;lt;/code&amp;gt;-Methode, aber es ist wichtig, diesen Hintergrund zu kennen, falls man auf ältere Systeme oder Konfigurationen trifft.&lt;br /&gt;
&lt;br /&gt;
=== Veraltete &amp;lt;code&amp;gt;mysql_*&amp;lt;/code&amp;gt; Funktionen ===&lt;br /&gt;
&lt;br /&gt;
Die &amp;lt;code&amp;gt;mysql_*&amp;lt;/code&amp;gt; Funktionsfamilie (&amp;lt;code&amp;gt;mysql_connect&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;mysql_query&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;mysql_fetch_assoc&amp;lt;/code&amp;gt; etc.) war die ursprüngliche Methode, um in PHP mit MySQL-Datenbanken zu interagieren. Diese Erweiterung ist jedoch &amp;#039;&amp;#039;&amp;#039;stark veraltet&amp;#039;&amp;#039;&amp;#039; und sollte &amp;#039;&amp;#039;&amp;#039;unter keinen Umständen mehr für neue Projekte verwendet werden&amp;#039;&amp;#039;&amp;#039;.&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Status:&amp;#039;&amp;#039;&amp;#039; Die &amp;lt;code&amp;gt;mysql_*&amp;lt;/code&amp;gt; Erweiterung wurde in &amp;#039;&amp;#039;&amp;#039;PHP 5.5&amp;#039;&amp;#039;&amp;#039; offiziell als &amp;quot;deprecated&amp;quot; (veraltet) markiert, was bedeutet, dass ihre Verwendung ab dieser Version Warnungen (&amp;lt;code&amp;gt;E_DEPRECATED&amp;lt;/code&amp;gt;) erzeugte.[28, 29] Mit der Veröffentlichung von &amp;#039;&amp;#039;&amp;#039;PHP 7.0&amp;#039;&amp;#039;&amp;#039; wurde die Erweiterung &amp;#039;&amp;#039;&amp;#039;vollständig entfernt&amp;#039;&amp;#039;&amp;#039;.[28, 29] PHP-Code, der diese Funktionen nutzt, führt auf PHP 7.0 oder neuer zu fatalen Fehlern und funktioniert nicht mehr.[28, 29]&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Gründe für die Entfernung:&amp;#039;&amp;#039;&amp;#039; Es gab zwingende Gründe für diesen Schritt:&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Gravierende Sicherheitsrisiken:&amp;#039;&amp;#039;&amp;#039; Der Hauptgrund war die Anfälligkeit für SQL-Injection-Angriffe. Die &amp;lt;code&amp;gt;mysql_*&amp;lt;/code&amp;gt; Funktionen boten keinen eingebauten Mechanismus wie Prepared Statements. Entwickler mussten Benutzereingaben manuell mit &amp;lt;code&amp;gt;mysql_real_escape_string()&amp;lt;/code&amp;gt; &amp;quot;escapen&amp;quot;, eine Methode, die leicht vergessen oder falsch angewendet werden konnte, was zu weit verbreiteten Sicherheitslücken führte.[29, 30] Das Fehlen von Prepared Statements in dieser alten Erweiterung ist direkt ursächlich für die hohe Zahl an SQL-Injection-Schwachstellen in älteren PHP-Anwendungen.[29]&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Fehlende Unterstützung moderner MySQL-Features:&amp;#039;&amp;#039;&amp;#039; Die Erweiterung wurde ursprünglich für MySQL Version 3.23 entwickelt und seitdem kaum weiterentwickelt.[29] Sie unterstützte viele wichtige Funktionen moderner MySQL-Versionen nicht, darunter Prepared Statements, Stored Procedures, Transaktionen, SSL-Verschlüsselung, Kompression, Multi-Statements und vollständige Zeichensatzunterstützung.[29]&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Schlechte Wartbarkeit:&amp;#039;&amp;#039;&amp;#039; Der Code der Erweiterung war veraltet und wurde immer schwieriger zu warten und an neue Versionen der MySQL-Client-Bibliotheken anzupassen.[29]&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Förderung unsicherer Praktiken:&amp;#039;&amp;#039;&amp;#039; Da viele alte Tutorials und Codebeispiele die &amp;lt;code&amp;gt;mysql_*&amp;lt;/code&amp;gt; Funktionen unsicher verwendeten, trug die fortgesetzte Verfügbarkeit zur Verbreitung schlechter und unsicherer Programmierpraktiken bei.[29]&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Verwendung heute:&amp;#039;&amp;#039;&amp;#039; Die klare Antwort lautet: &amp;#039;&amp;#039;&amp;#039;Nein&amp;#039;&amp;#039;&amp;#039;. Diese Funktionen existieren in modernen PHP-Versionen nicht mehr.[31, 30] Jede Anwendung, die sie noch verwendet, ist nicht nur unsicher, sondern auch inkompatibel mit aktueller PHP-Software. Eine Migration zu PDO oder MySQLi ist unumgänglich, um Sicherheit und Kompatibilität zu gewährleisten.[28, 29] Die erzwungene Migration durch die Entfernung in PHP 7.0 unterstreicht, wie wichtig es ist, Deprecation-Warnungen ernst zu nehmen und Code proaktiv zu aktualisieren, um zukünftige Probleme zu vermeiden.[29, 32]&lt;br /&gt;
&lt;br /&gt;
=== Tabelle 1: Vergleich der PHP-Datenbankerweiterungen für MySQL ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Merkmal !! PDO (PHP Data Objects) !! MySQLi (MySQL Improved) !! &amp;lt;code&amp;gt;mysql_*&amp;lt;/code&amp;gt; (Veraltet)&lt;br /&gt;
|-&amp;lt;br&amp;gt;&lt;br /&gt;
| aktiv=&amp;quot;&amp;quot; empfohlen=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| aktiv=&amp;quot;&amp;quot; empfohlen=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Entfernt (seit PHP 7.0) [29]&lt;br /&gt;
|-&amp;lt;br&amp;gt;&lt;br /&gt;
| objektorientiert=&amp;quot;&amp;quot; oo=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| oo=&amp;quot;&amp;quot; prozedural=&amp;quot;&amp;quot; 17=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Prozedural&lt;br /&gt;
|-&amp;lt;br&amp;gt;&lt;br /&gt;
| multi-datenbank=&amp;quot;&amp;quot; mysql=&amp;quot;&amp;quot; pgsql=&amp;quot;&amp;quot; sqlite=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| nur=&amp;quot;&amp;quot; mysql=&amp;quot;&amp;quot; 15=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Nur MySQL&lt;br /&gt;
|-&amp;lt;br&amp;gt;&lt;br /&gt;
| ja=&amp;quot;&amp;quot; nativ=&amp;quot;&amp;quot; empfohlen=&amp;quot;&amp;quot; emulation=&amp;quot;&amp;quot; m=&amp;quot;&amp;quot; glich=&amp;quot;&amp;quot; 1=&amp;quot;&amp;quot; 11=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| ja=&amp;quot;&amp;quot; nativ=&amp;quot;&amp;quot; 25=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Nein [29]&lt;br /&gt;
|- &lt;br /&gt;
| &amp;#039;&amp;#039;&amp;#039;SQL Injection Schutz&amp;#039;&amp;#039;&amp;#039; || sehr=&amp;quot;&amp;quot; gut=&amp;quot;&amp;quot; durch=&amp;quot;&amp;quot; prepared=&amp;quot;&amp;quot; statements=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Sehr gut (durch Prepared Statements) [25] || Schlecht (manuelles Escaping nötig) [29]&lt;br /&gt;
|-&amp;lt;br&amp;gt;&lt;br /&gt;
| gute=&amp;quot;&amp;quot; unterst=&amp;quot;&amp;quot; tzung=&amp;quot;&amp;quot; via=&amp;quot;&amp;quot; treiber=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Sehr gute Unterstützung (spezifisch) [15] || Veraltet, viele Features fehlen [29]&lt;br /&gt;
|-&amp;lt;br&amp;gt;&lt;br /&gt;
| hoch=&amp;quot;&amp;quot; 2=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| niedrig=&amp;quot;&amp;quot; mysql-spezifisch=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Niedrig&lt;br /&gt;
|-&amp;lt;br&amp;gt;&lt;br /&gt;
| exceptions=&amp;quot;&amp;quot; mwt-preservehtml=&amp;quot;&amp;quot; pdoexception=&amp;quot;&amp;quot; code=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| exceptions=&amp;quot;&amp;quot; mit=&amp;quot;&amp;quot; mwt-preservehtml=&amp;quot;&amp;quot; mysqli_report=&amp;quot;&amp;quot; code=&amp;quot;&amp;quot; fehlercodes=&amp;quot;&amp;quot; 18=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Fehlercodes / &amp;lt;code&amp;gt;mysql_error()&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
| &amp;#039;&amp;#039;&amp;#039;Nicht verwenden!&amp;#039;&amp;#039;&amp;#039; [29]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Grundlegende Datenbankoperationen (CRUD) ==&lt;br /&gt;
&lt;br /&gt;
Nachdem wir die verschiedenen Erweiterungen kennengelernt haben, wenden wir uns nun den grundlegenden Operationen zu, die man typischerweise mit einer Datenbank durchführt. Diese werden oft mit dem Akronym CRUD zusammengefasst:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;C&amp;#039;&amp;#039;&amp;#039;reate: Neue Datensätze erstellen (SQL: &amp;lt;code&amp;gt;INSERT&amp;lt;/code&amp;gt;).&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;R&amp;#039;&amp;#039;&amp;#039;ead: Vorhandene Datensätze lesen/abfragen (SQL: &amp;lt;code&amp;gt;SELECT&amp;lt;/code&amp;gt;).&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;U&amp;#039;&amp;#039;&amp;#039;pdate: Bestehende Datensätze ändern (SQL: &amp;lt;code&amp;gt;UPDATE&amp;lt;/code&amp;gt;).&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;D&amp;#039;&amp;#039;&amp;#039;elete: Datensätze löschen (SQL: &amp;lt;code&amp;gt;DELETE&amp;lt;/code&amp;gt;).&lt;br /&gt;
In den folgenden Abschnitten werden wir sehen, wie diese Operationen mit den modernen Erweiterungen PDO und MySQLi umgesetzt werden, wobei wir durchgehend &amp;#039;&amp;#039;&amp;#039;Prepared Statements&amp;#039;&amp;#039;&amp;#039; verwenden, um die Sicherheit zu gewährleisten.&lt;br /&gt;
&lt;br /&gt;
=== Daten abfragen (SELECT) ===&lt;br /&gt;
&lt;br /&gt;
Das Abfragen von Daten ist eine der häufigsten Datenbankoperationen.&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;PDO:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Ohne Parameter:&amp;#039;&amp;#039;&amp;#039; Wenn die Abfrage keine variablen Teile enthält (z.B. keine &amp;lt;code&amp;gt;WHERE&amp;lt;/code&amp;gt;-Klausel mit Benutzereingaben), kann die &amp;lt;code&amp;gt;query()&amp;lt;/code&amp;gt;-Methode verwendet werden. Sie führt die SQL-Abfrage direkt aus und gibt ein &amp;lt;code&amp;gt;PDOStatement&amp;lt;/code&amp;gt;-Objekt zurück, das zur Ergebnisauswertung dient.[2, 33]&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Beispiel: Alle Benutzer auswählen&lt;br /&gt;
$stmt = $pdo-&amp;amp;gt;query(&amp;quot;SELECT id, username FROM users&amp;quot;);&lt;br /&gt;
// $stmt kann nun zum Fetchen der Ergebnisse verwendet werden&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Mit Parametern (Prepared Statement):&amp;#039;&amp;#039;&amp;#039; Dies ist der Standardfall, wenn Benutzereingaben oder andere Variablen die Abfrage beeinflussen (z.B. in &amp;lt;code&amp;gt;WHERE&amp;lt;/code&amp;gt;- oder &amp;lt;code&amp;gt;LIMIT&amp;lt;/code&amp;gt;-Klauseln).&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Vorbereiten (&amp;lt;code&amp;gt;prepare&amp;lt;/code&amp;gt;):&amp;#039;&amp;#039;&amp;#039; Die SQL-Abfrage wird mit Platzhaltern (&amp;lt;code&amp;gt;?&amp;lt;/code&amp;gt; für positionale oder &amp;lt;code&amp;gt;:name&amp;lt;/code&amp;gt; für benannte Platzhalter) vorbereitet.[34, 33, 5]&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Beispiel: Benutzer mit bestimmter ID auswählen (positionaler Platzhalter)&lt;br /&gt;
$sql = &amp;quot;SELECT id, username, email FROM users WHERE id =?&amp;quot;;&lt;br /&gt;
$stmt = $pdo-&amp;amp;gt;prepare($sql);&lt;br /&gt;
&lt;br /&gt;
// Beispiel: Benutzer mit bestimmtem Status auswählen (benannter Platzhalter)&lt;br /&gt;
$sql_named = &amp;quot;SELECT id, username FROM users WHERE status = :status&amp;quot;;&lt;br /&gt;
$stmt_named = $pdo-&amp;amp;gt;prepare($sql_named);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
*# &amp;#039;&amp;#039;&amp;#039;Ausführen (&amp;lt;code&amp;gt;execute&amp;lt;/code&amp;gt;):&amp;#039;&amp;#039;&amp;#039; Die vorbereitete Anweisung wird mit den tatsächlichen Werten ausgeführt. Die Werte werden als Array an die &amp;lt;code&amp;gt;execute()&amp;lt;/code&amp;gt;-Methode übergeben. Bei positionalen Platzhaltern entspricht die Reihenfolge im Array der Reihenfolge der &amp;lt;code&amp;gt;?&amp;lt;/code&amp;gt; in der SQL-Abfrage. Bei benannten Platzhaltern wird ein assoziatives Array verwendet, dessen Schlüssel den Platzhalternamen entsprechen (mit oder ohne führenden Doppelpunkt).[34, 33, 5]&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Ausführen für positionalen Platzhalter&lt;br /&gt;
$user_id = 123;&lt;br /&gt;
$stmt-&amp;amp;gt;execute([$user_id]);&lt;br /&gt;
&lt;br /&gt;
// Ausführen für benannten Platzhalter&lt;br /&gt;
$user_status = &amp;#039;active&amp;#039;;&lt;br /&gt;
$stmt_named-&amp;amp;gt;execute([&amp;#039;:status&amp;#039; =&amp;amp;gt; $user_status]);&lt;br /&gt;
// oder: $stmt_named-&amp;amp;gt;execute([&amp;#039;status&amp;#039; =&amp;amp;gt; $user_status]);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
*# &amp;#039;&amp;#039;&amp;#039;Alternative Bindung:&amp;#039;&amp;#039;&amp;#039; Optional können Parameter auch explizit vor dem &amp;lt;code&amp;gt;execute()&amp;lt;/code&amp;gt;-Aufruf mit &amp;lt;code&amp;gt;bindValue()&amp;lt;/code&amp;gt; (bindet einen Wert) oder &amp;lt;code&amp;gt;bindParam()&amp;lt;/code&amp;gt; (bindet eine Variable als Referenz) gebunden werden. Dies gibt mehr Kontrolle über den Datentyp (z.B. &amp;lt;code&amp;gt;PDO::PARAM_INT&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;PDO::PARAM_STR&amp;lt;/code&amp;gt;).[5, 13]&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$stmt-&amp;amp;gt;bindValue(1, $user_id, PDO::PARAM_INT); // Position 1, Wert von $user_id als Integer&lt;br /&gt;
$stmt-&amp;amp;gt;execute();&lt;br /&gt;
&lt;br /&gt;
$stmt_named-&amp;amp;gt;bindParam(&amp;#039;:status&amp;#039;, $user_status, PDO::PARAM_STR); // Platzhalter :status, Variable $user_status als String&lt;br /&gt;
$stmt_named-&amp;amp;gt;execute();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;MySQLi:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Ohne Parameter:&amp;#039;&amp;#039;&amp;#039; Ähnlich wie bei PDO kann bei Abfragen ohne variable Teile die &amp;lt;code&amp;gt;query()&amp;lt;/code&amp;gt;-Methode (OO-Stil: &amp;lt;code&amp;gt;$mysqli-&amp;amp;gt;query()&amp;lt;/code&amp;gt;) oder die &amp;lt;code&amp;gt;mysqli_query()&amp;lt;/code&amp;gt;-Funktion (prozeduraler Stil: &amp;lt;code&amp;gt;mysqli_query($link, &amp;quot;...&amp;quot;)&amp;lt;/code&amp;gt;) verwendet werden. Sie geben ein &amp;lt;code&amp;gt;mysqli_result&amp;lt;/code&amp;gt;-Objekt zurück.[20, 25]&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// OO-Stil&lt;br /&gt;
$result = $mysqli-&amp;amp;gt;query(&amp;quot;SELECT id, username FROM users&amp;quot;);&lt;br /&gt;
// Prozedural&lt;br /&gt;
$result = mysqli_query($link, &amp;quot;SELECT id, username FROM users&amp;quot;);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Mit Parametern (Prepared Statement):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Vorbereiten (&amp;lt;code&amp;gt;prepare&amp;lt;/code&amp;gt;):&amp;#039;&amp;#039;&amp;#039; Die SQL-Abfrage wird mit &amp;lt;code&amp;gt;?&amp;lt;/code&amp;gt;-Platzhaltern vorbereitet. Im OO-Stil &amp;lt;code&amp;gt;$stmt = $mysqli-&amp;amp;gt;prepare(&amp;quot;...&amp;quot;)&amp;lt;/code&amp;gt;, prozedural &amp;lt;code&amp;gt;$stmt = mysqli_prepare($link, &amp;quot;...&amp;quot;)&amp;lt;/code&amp;gt;.[25, 35]&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// OO-Stil&lt;br /&gt;
$sql = &amp;quot;SELECT id, username, email FROM users WHERE id =?&amp;quot;;&lt;br /&gt;
$stmt = $mysqli-&amp;amp;gt;prepare($sql);&lt;br /&gt;
// Prozedural&lt;br /&gt;
$sql_proc = &amp;quot;SELECT id, username, email FROM users WHERE id =?&amp;quot;;&lt;br /&gt;
$stmt_proc = mysqli_prepare($link, $sql_proc);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
**# &amp;#039;&amp;#039;&amp;#039;Parameter binden (&amp;lt;code&amp;gt;bind_param&amp;lt;/code&amp;gt;):&amp;#039;&amp;#039;&amp;#039; Die Variablen werden an die Platzhalter gebunden. Dies ist ein wesentlicher Unterschied zu PDOs &amp;lt;code&amp;gt;execute&amp;lt;/code&amp;gt;-Array-Bindung. &amp;lt;code&amp;gt;bind_param()&amp;lt;/code&amp;gt; erfordert einen String, der die Datentypen der zu bindenden Variablen angibt (z.B. &amp;quot;i&amp;quot; für Integer, &amp;quot;s&amp;quot; für String, &amp;quot;d&amp;quot; für Double/Float, &amp;quot;b&amp;quot; für Blob), gefolgt von den Variablen selbst.[25, 35]&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$user_id = 123;&lt;br /&gt;
// OO-Stil&lt;br /&gt;
$stmt-&amp;amp;gt;bind_param(&amp;quot;i&amp;quot;, $user_id); // &amp;quot;i&amp;quot; für Integer&lt;br /&gt;
// Prozedural&lt;br /&gt;
mysqli_stmt_bind_param($stmt_proc, &amp;quot;i&amp;quot;, $user_id);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
**# &amp;#039;&amp;#039;&amp;#039;Ausführen (&amp;lt;code&amp;gt;execute&amp;lt;/code&amp;gt;):&amp;#039;&amp;#039;&amp;#039; Die vorbereitete Anweisung wird ausgeführt. OO-Stil: &amp;lt;code&amp;gt;$stmt-&amp;amp;gt;execute()&amp;lt;/code&amp;gt;, prozedural: &amp;lt;code&amp;gt;mysqli_stmt_execute($stmt)&amp;lt;/code&amp;gt;.[25, 36, 35]&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// OO-Stil&lt;br /&gt;
$stmt-&amp;amp;gt;execute();&lt;br /&gt;
// Prozedural&lt;br /&gt;
mysqli_stmt_execute($stmt_proc);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
**# &amp;#039;&amp;#039;&amp;#039;Ergebnis holen:&amp;#039;&amp;#039;&amp;#039; Um die Ergebnisse eines Prepared Statements in MySQLi zu erhalten, ist die Methode &amp;lt;code&amp;gt;get_result()&amp;lt;/code&amp;gt; (OO) bzw. &amp;lt;code&amp;gt;mysqli_stmt_get_result()&amp;lt;/code&amp;gt; (prozedural) am bequemsten. Sie gibt ein Standard-&amp;lt;code&amp;gt;mysqli_result&amp;lt;/code&amp;gt;-Objekt zurück, das dann mit den üblichen Fetch-Methoden verarbeitet werden kann. &amp;#039;&amp;#039;&amp;#039;Wichtig:&amp;#039;&amp;#039;&amp;#039; Diese Methode erfordert den &amp;lt;code&amp;gt;mysqlnd&amp;lt;/code&amp;gt;-Treiber.[25, 15]&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// OO-Stil&lt;br /&gt;
$result = $stmt-&amp;amp;gt;get_result();&lt;br /&gt;
// Prozedural&lt;br /&gt;
$result_proc = mysqli_stmt_get_result($stmt_proc);&lt;br /&gt;
// $result / $result_proc kann nun mit fetch_assoc() etc. verwendet werden.&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Die Alternative ohne &amp;lt;code&amp;gt;mysqlnd&amp;lt;/code&amp;gt; ist &amp;lt;code&amp;gt;bind_result()&amp;lt;/code&amp;gt;, bei der jede Ergebnisspalte an eine PHP-Variable gebunden wird, und &amp;lt;code&amp;gt;fetch()&amp;lt;/code&amp;gt; iterativ aufgerufen wird, um die Variablen zu füllen.[25] Dies ist deutlich umständlicher.&lt;br /&gt;
&lt;br /&gt;
=== Ergebnisse abrufen und verarbeiten ===&lt;br /&gt;
&lt;br /&gt;
Nachdem eine &amp;lt;code&amp;gt;SELECT&amp;lt;/code&amp;gt;-Abfrage erfolgreich ausgeführt wurde, müssen die zurückgegebenen Daten aus dem Ergebnisobjekt (&amp;lt;code&amp;gt;PDOStatement&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;mysqli_result&amp;lt;/code&amp;gt;) abgerufen (&amp;quot;gefetched&amp;quot;) werden.&lt;br /&gt;
&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Zeilenweise Iteration (&amp;lt;code&amp;gt;fetch&amp;lt;/code&amp;gt; im Loop):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
Dies ist der klassische Weg, um Ergebnisse zu verarbeiten, insbesondere wenn jede Zeile einzeln behandelt werden muss oder wenn die Ergebnismenge sehr groß ist.&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;PDO:&amp;#039;&amp;#039;&amp;#039; Die &amp;lt;code&amp;gt;fetch()&amp;lt;/code&amp;gt;-Methode des &amp;lt;code&amp;gt;PDOStatement&amp;lt;/code&amp;gt;-Objekts wird wiederholt in einer &amp;lt;code&amp;gt;while&amp;lt;/code&amp;gt;-Schleife aufgerufen. &amp;lt;code&amp;gt;fetch()&amp;lt;/code&amp;gt; gibt bei jedem Aufruf die nächste Zeile zurück (im Format des eingestellten Fetch-Modus, z.B. &amp;lt;code&amp;gt;PDO::FETCH_ASSOC&amp;lt;/code&amp;gt;) und &amp;lt;code&amp;gt;false&amp;lt;/code&amp;gt; (oder &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt; je nach Modus/Version), wenn keine weiteren Zeilen vorhanden sind.[2, 33]&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$stmt = $pdo-&amp;amp;gt;prepare(&amp;quot;SELECT name, email FROM users WHERE status =?&amp;quot;);&lt;br /&gt;
$stmt-&amp;amp;gt;execute([&amp;#039;active&amp;#039;]);&lt;br /&gt;
while ($row = $stmt-&amp;amp;gt;fetch(PDO::FETCH_ASSOC)) {&lt;br /&gt;
 echo &amp;quot;Name: &amp;quot;. htmlspecialchars($row[&amp;#039;name&amp;#039;]). &amp;quot;, E-Mail: &amp;quot;. htmlspecialchars($row[&amp;#039;email&amp;#039;]). &amp;quot;&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;MySQLi:&amp;#039;&amp;#039;&amp;#039; Die &amp;lt;code&amp;gt;fetch_assoc()&amp;lt;/code&amp;gt;-Methode (oder &amp;lt;code&amp;gt;fetch_row()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;fetch_object()&amp;lt;/code&amp;gt;, etc.) des &amp;lt;code&amp;gt;mysqli_result&amp;lt;/code&amp;gt;-Objekts wird in einer &amp;lt;code&amp;gt;while&amp;lt;/code&amp;gt;-Schleife verwendet. Sie gibt die nächste Zeile als Array (oder Objekt) zurück oder &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt;, wenn das Ende erreicht ist.[25, 15]&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// OO-Stil (nach $result = $stmt-&amp;amp;gt;get_result();)&lt;br /&gt;
while ($row = $result-&amp;amp;gt;fetch_assoc()) {&lt;br /&gt;
 echo &amp;quot;Name: &amp;quot;. htmlspecialchars($row[&amp;#039;name&amp;#039;]). &amp;quot;, E-Mail: &amp;quot;. htmlspecialchars($row[&amp;#039;email&amp;#039;]). &amp;quot;&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Prozedural (nach $result = mysqli_stmt_get_result($stmt);)&lt;br /&gt;
while ($row = mysqli_fetch_assoc($result)) {&lt;br /&gt;
 echo &amp;quot;Name: &amp;quot;. htmlspecialchars($row[&amp;#039;name&amp;#039;]). &amp;quot;, E-Mail: &amp;quot;. htmlspecialchars($row[&amp;#039;email&amp;#039;]). &amp;quot;&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Dieser Ansatz ist &amp;#039;&amp;#039;&amp;#039;speichereffizient&amp;#039;&amp;#039;&amp;#039;, da immer nur eine Zeile gleichzeitig im PHP-Speicher gehalten wird. Dies ist besonders wichtig bei Abfragen, die potenziell Tausende oder Millionen von Zeilen zurückgeben könnten.[37]&lt;br /&gt;
&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Alle Ergebnisse auf einmal abrufen (&amp;lt;code&amp;gt;fetchAll&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;fetch_all&amp;lt;/code&amp;gt;):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
Für kleinere bis mittlere Ergebnismengen ist es oft bequemer, alle Zeilen auf einmal in ein PHP-Array zu laden.&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;PDO:&amp;#039;&amp;#039;&amp;#039; Die &amp;lt;code&amp;gt;fetchAll()&amp;lt;/code&amp;gt;-Methode des &amp;lt;code&amp;gt;PDOStatement&amp;lt;/code&amp;gt;-Objekts gibt ein Array zurück, das alle Ergebniszeilen enthält.[33, 38] Der Fetch-Modus (z.B. &amp;lt;code&amp;gt;PDO::FETCH_ASSOC&amp;lt;/code&amp;gt;) kann als Argument übergeben werden.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$stmt = $pdo-&amp;amp;gt;prepare(&amp;quot;SELECT name, email FROM users WHERE status =?&amp;quot;);&lt;br /&gt;
$stmt-&amp;amp;gt;execute([&amp;#039;active&amp;#039;]);&lt;br /&gt;
$results = $stmt-&amp;amp;gt;fetchAll(PDO::FETCH_ASSOC);&lt;br /&gt;
// $results ist nun ein Array von assoziativen Arrays&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;MySQLi:&amp;#039;&amp;#039;&amp;#039; Die &amp;lt;code&amp;gt;fetch_all()&amp;lt;/code&amp;gt;-Methode des &amp;lt;code&amp;gt;mysqli_result&amp;lt;/code&amp;gt;-Objekts (verfügbar mit &amp;lt;code&amp;gt;mysqlnd&amp;lt;/code&amp;gt;) tut dasselbe. Der Modus (&amp;lt;code&amp;gt;MYSQLI_ASSOC&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;MYSQLI_NUM&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;MYSQLI_BOTH&amp;lt;/code&amp;gt;) wird als Argument übergeben.[25, 39]&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// OO-Stil (nach $result = $stmt-&amp;amp;gt;get_result();)&lt;br /&gt;
$results = $result-&amp;amp;gt;fetch_all(MYSQLI_ASSOC);&lt;br /&gt;
&lt;br /&gt;
// Prozedural (nach $result = mysqli_stmt_get_result($stmt);)&lt;br /&gt;
$results = mysqli_fetch_all($result, MYSQLI_ASSOC);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Der &amp;#039;&amp;#039;&amp;#039;Vorteil&amp;#039;&amp;#039;&amp;#039; dieser Methode liegt in der einfacheren Weiterverarbeitung des Ergebnis-Arrays mit Standard-PHP-Array-Funktionen wie &amp;lt;code&amp;gt;foreach&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;count&amp;lt;/code&amp;gt;.[40] Der &amp;#039;&amp;#039;&amp;#039;Nachteil&amp;#039;&amp;#039;&amp;#039; ist der potenziell hohe Speicherverbrauch, da alle Daten gleichzeitig in den PHP-Speicher geladen werden. Bei sehr großen Ergebnismengen kann dies zu Speicherlimit-Fehlern führen.[37]&lt;br /&gt;
&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Fetch-Modi / Fetch-Funktionen (Struktur der Ergebniszeile):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
Beide Erweiterungen bieten verschiedene Möglichkeiten, die Struktur der abgerufenen Datenzeilen zu bestimmen:&lt;br /&gt;
&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;PDO Fetch-Modi (Auswahl):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
***** &amp;lt;code&amp;gt;PDO::FETCH_ASSOC&amp;lt;/code&amp;gt;: Gibt ein assoziatives Array zurück (&amp;lt;code&amp;gt;[&amp;#039;spaltenname&amp;#039; =&amp;amp;gt; &amp;#039;wert&amp;#039;,... ]&amp;lt;/code&amp;gt;).[9, 33, 41, 42] Sehr gebräuchlich.&lt;br /&gt;
***** &amp;lt;code&amp;gt;PDO::FETCH_NUM&amp;lt;/code&amp;gt;: Gibt ein numerisch indiziertes Array zurück (&amp;lt;code&amp;gt;[0 =&amp;amp;gt; &amp;#039;wert1&amp;#039;, 1 =&amp;amp;gt; &amp;#039;wert2&amp;#039;,...]&amp;lt;/code&amp;gt;).[9, 41, 42]&lt;br /&gt;
***** &amp;lt;code&amp;gt;PDO::FETCH_BOTH&amp;lt;/code&amp;gt; (Standard): Gibt ein Array zurück, das sowohl assoziative als auch numerische Indizes enthält.[41, 42] Verbraucht mehr Speicher.&lt;br /&gt;
***** &amp;lt;code&amp;gt;PDO::FETCH_OBJ&amp;lt;/code&amp;gt;: Gibt ein anonymes &amp;lt;code&amp;gt;stdClass&amp;lt;/code&amp;gt;-Objekt zurück, bei dem die Spaltennamen Eigenschaften sind (&amp;lt;code&amp;gt;$row-&amp;amp;gt;spaltenname&amp;lt;/code&amp;gt;).[9, 41, 42, 43]&lt;br /&gt;
***** &amp;lt;code&amp;gt;PDO::FETCH_CLASS&amp;lt;/code&amp;gt;: Erstellt eine Instanz einer angegebenen Klasse und weist die Spaltenwerte den Klasseneigenschaften zu.[38, 41, 42, 43]&lt;br /&gt;
***** &amp;lt;code&amp;gt;PDO::FETCH_COLUMN&amp;lt;/code&amp;gt;: Ruft nur den Wert einer einzelnen Spalte aus der nächsten Zeile ab.[38]&lt;br /&gt;
***** &amp;lt;code&amp;gt;PDO::FETCH_LAZY&amp;lt;/code&amp;gt;: Eine Kombination aus &amp;lt;code&amp;gt;FETCH_BOTH&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;FETCH_OBJ&amp;lt;/code&amp;gt;, die Werte erst bei Zugriff lädt.[9, 41, 42]&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;MySQLi Fetch-Funktionen (Auswahl):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
***** &amp;lt;code&amp;gt;fetch_assoc()&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;mysqli_fetch_assoc()&amp;lt;/code&amp;gt;: Gibt ein assoziatives Array zurück.[17, 25, 39, 15] Sehr gebräuchlich.&lt;br /&gt;
***** &amp;lt;code&amp;gt;fetch_row()&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;mysqli_fetch_row()&amp;lt;/code&amp;gt;: Gibt ein numerisch indiziertes Array zurück.[39, 35]&lt;br /&gt;
***** &amp;lt;code&amp;gt;fetch_array()&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;mysqli_fetch_array()&amp;lt;/code&amp;gt;: Gibt je nach übergebenem Modus (&amp;lt;code&amp;gt;MYSQLI_ASSOC&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;MYSQLI_NUM&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;MYSQLI_BOTH&amp;lt;/code&amp;gt;) ein assoziatives, numerisches oder beides enthaltendes Array zurück.[39, 35]&lt;br /&gt;
***** &amp;lt;code&amp;gt;fetch_object()&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;mysqli_fetch_object()&amp;lt;/code&amp;gt;: Gibt ein &amp;lt;code&amp;gt;stdClass&amp;lt;/code&amp;gt;-Objekt zurück oder eine Instanz einer angegebenen Klasse.[21, 15, 35]&lt;br /&gt;
***** &amp;lt;code&amp;gt;fetch_column()&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;mysqli_fetch_column()&amp;lt;/code&amp;gt;: Ruft den Wert einer einzelnen Spalte ab (erst ab PHP 8.1 verfügbar).[24]&lt;br /&gt;
***** &amp;lt;code&amp;gt;fetch_all()&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;mysqli_fetch_all()&amp;lt;/code&amp;gt;: Ruft alle Zeilen auf einmal ab, Modus (&amp;lt;code&amp;gt;MYSQLI_ASSOC&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;MYSQLI_NUM&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;MYSQLI_BOTH&amp;lt;/code&amp;gt;) wählbar.[21, 25, 39]&lt;br /&gt;
&lt;br /&gt;
=== Tabelle 2: Ausgewählte PDO Fetch-Modi ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Konstante !! Rückgabeformat !! Beschreibung&lt;br /&gt;
|- &lt;br /&gt;
| &amp;lt;code&amp;gt;PDO::FETCH_ASSOC&amp;lt;/code&amp;gt;&lt;br /&gt;
| assoziatives=&amp;quot;&amp;quot; array=&amp;quot;&amp;quot; mwt-preservehtml=&amp;quot;&amp;quot; | [&amp;quot; col&amp;#039;=&amp;quot;&amp;amp;gt;&amp;quot; &amp;#039;val&amp;#039;]&amp;amp;lt;=&amp;quot;&amp;quot; code&amp;amp;gt;&amp;#039;)=&amp;quot;&amp;quot; &amp;amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
| Gängigste Form, Spaltennamen als Schlüssel.&lt;br /&gt;
|- &lt;br /&gt;
| mwt-preservehtml=&amp;quot;&amp;quot; pdo::fetch_num=&amp;quot;&amp;quot; code=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| numerisches=&amp;quot;&amp;quot; array=&amp;quot;&amp;quot; mwt-preservehtml=&amp;quot;&amp;quot; 0=&amp;quot;&amp;quot; val=&amp;quot;&amp;quot; code=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Spaltenwerte über numerischen Index erreichbar.&lt;br /&gt;
|- &lt;br /&gt;
| mwt-preservehtml=&amp;quot;&amp;quot; pdo::fetch_both=&amp;quot;&amp;quot; code=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| array=&amp;quot;&amp;quot; mit=&amp;quot;&amp;quot; assoz=&amp;quot;&amp;quot; num=&amp;quot;&amp;quot; indizes=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Standard, enthält doppelte Informationen, höherer Speicherbedarf.&lt;br /&gt;
|- &lt;br /&gt;
| mwt-preservehtml=&amp;quot;&amp;quot; pdo::fetch_obj=&amp;quot;&amp;quot; code=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| anonymes=&amp;quot;&amp;quot; mwt-preservehtml=&amp;quot;&amp;quot; stdclass=&amp;quot;&amp;quot; code=&amp;quot;&amp;quot; -objekt=&amp;quot;&amp;quot; row-=&amp;quot;&amp;quot; col=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Spaltennamen werden zu Objekteigenschaften.&lt;br /&gt;
|- &lt;br /&gt;
| &amp;lt;code&amp;gt;PDO::FETCH_CLASS&amp;lt;/code&amp;gt; || objekt=&amp;quot;&amp;quot; einer=&amp;quot;&amp;quot; spezifischen=&amp;quot;&amp;quot; klasse=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Mappt Spalten auf Eigenschaften einer vordefinierten Klasse.&lt;br /&gt;
|- &lt;br /&gt;
| &amp;lt;code&amp;gt;PDO::FETCH_COLUMN&amp;lt;/code&amp;gt; || einzelner=&amp;quot;&amp;quot; wert=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Gibt nur den Wert der ersten (oder angegebenen) Spalte der nächsten Zeile zurück.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Tabelle 3: Ausgewählte MySQLi Fetch-Funktionen ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Funktion (OO / Prozedural) !! Rückgabeformat !! Beschreibung&lt;br /&gt;
|- &lt;br /&gt;
| mwt-preservehtml=&amp;quot;&amp;quot; fetch_assoc=&amp;quot;&amp;quot; code=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| assoziatives=&amp;quot;&amp;quot; array=&amp;quot;&amp;quot; mwt-preservehtml=&amp;quot;&amp;quot; col=&amp;quot;&amp;quot; val=&amp;quot;&amp;quot; code=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Gängigste Form, Spaltennamen als Schlüssel.&lt;br /&gt;
|- &lt;br /&gt;
| mwt-preservehtml=&amp;quot;&amp;quot; fetch_row=&amp;quot;&amp;quot; code=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| numerisches=&amp;quot;&amp;quot; array=&amp;quot;&amp;quot; mwt-preservehtml=&amp;quot;&amp;quot; 0=&amp;quot;&amp;quot; val=&amp;quot;&amp;quot; code=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Spaltenwerte über numerischen Index erreichbar.&lt;br /&gt;
|- &lt;br /&gt;
| mwt-preservehtml=&amp;quot;&amp;quot; fetch_array=&amp;quot;&amp;quot; code=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| array=&amp;quot;&amp;quot; assoz=&amp;quot;&amp;quot; num=&amp;quot;&amp;quot; oder=&amp;quot;&amp;quot; beide=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Flexibel je nach Modus (&amp;lt;code&amp;gt;MYSQLI_ASSOC&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;MYSQLI_NUM&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;MYSQLI_BOTH&amp;lt;/code&amp;gt;).&lt;br /&gt;
|- &lt;br /&gt;
| &amp;lt;code&amp;gt;fetch_object()&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;mysqli_fetch_object()&amp;lt;/code&amp;gt;&lt;br /&gt;
| mwt-preservehtml=&amp;quot;&amp;quot; | stdClass&amp;quot; -objekt=&amp;quot;&amp;quot; oder=&amp;quot;&amp;quot; spezifische=&amp;quot;&amp;quot; klasse=&amp;quot;&amp;quot; &amp;amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
| Spaltennamen werden zu Objekteigenschaften.&lt;br /&gt;
|- &lt;br /&gt;
| mwt-preservehtml=&amp;quot;&amp;quot; fetch_all=&amp;quot;&amp;quot; code=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| array=&amp;quot;&amp;quot; von=&amp;quot;&amp;quot; arrays=&amp;quot;&amp;quot; assoz=&amp;quot;&amp;quot; num=&amp;quot;&amp;quot; oder=&amp;quot;&amp;quot; beide=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Holt alle Ergebniszeilen auf einmal (benötigt &amp;lt;code&amp;gt;mysqlnd&amp;lt;/code&amp;gt;).&lt;br /&gt;
|- &lt;br /&gt;
| &amp;lt;code&amp;gt;fetch_column()&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;mysqli_fetch_column()&amp;lt;/code&amp;gt; || einzelner=&amp;quot;&amp;quot; wert=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Gibt nur den Wert der ersten (oder angegebenen) Spalte zurück (PHP 8.1+).&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Zugriff auf Spaltenwerte:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
Der Zugriff auf die Daten innerhalb einer abgerufenen Zeile hängt vom gewählten Fetch-Modus ab:&lt;br /&gt;
**** Assoziatives Array: &amp;lt;code&amp;gt;$row[&amp;#039;spaltenname&amp;#039;]&amp;lt;/code&amp;gt;&lt;br /&gt;
**** Numerisches Array: &amp;lt;code&amp;gt;$row&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;$row[1]&amp;lt;/code&amp;gt;,...&lt;br /&gt;
**** Objekt: &amp;lt;code&amp;gt;$row-&amp;amp;gt;spaltenname&amp;lt;/code&amp;gt;&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Iteration mit &amp;lt;code&amp;gt;foreach&amp;lt;/code&amp;gt;:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
Nachdem Ergebnisse mit &amp;lt;code&amp;gt;fetchAll()&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;fetch_all()&amp;lt;/code&amp;gt; in ein Array geladen wurden, ist &amp;lt;code&amp;gt;foreach&amp;lt;/code&amp;gt; die natürliche Wahl zur Iteration [37, 40, 44]:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$results = $stmt-&amp;amp;gt;fetchAll(PDO::FETCH_ASSOC);&lt;br /&gt;
foreach ($results as $row) {&lt;br /&gt;
 echo &amp;quot;Name: &amp;quot;. htmlspecialchars($row[&amp;#039;name&amp;#039;]). &amp;quot;&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Seit PHP 8 bietet PDO eine modernere Alternative, die die Lesbarkeit von &amp;lt;code&amp;gt;foreach&amp;lt;/code&amp;gt; mit der Speichereffizienz von &amp;lt;code&amp;gt;fetch()&amp;lt;/code&amp;gt; kombiniert, indem direkt über das &amp;lt;code&amp;gt;PDOStatement&amp;lt;/code&amp;gt;-Objekt iteriert wird [37, 45]:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Nur PHP 8+ mit PDO&lt;br /&gt;
$stmt = $pdo-&amp;amp;gt;query(&amp;quot;SELECT name, email FROM users&amp;quot;);&lt;br /&gt;
foreach ($stmt as $row) { // PDOStatement ist direkt traversierbar&lt;br /&gt;
 echo &amp;quot;Name: &amp;quot;. htmlspecialchars($row[&amp;#039;name&amp;#039;]). &amp;quot;&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Diese Methode ist besonders elegant und effizient für große Ergebnismengen in modernen PHP-Versionen.&lt;br /&gt;
&lt;br /&gt;
=== Daten manipulieren (INSERT, UPDATE, DELETE) ===&lt;br /&gt;
&lt;br /&gt;
Das Einfügen, Aktualisieren und Löschen von Daten sind kritische Operationen, da sie den Datenbestand verändern. Hier ist die Verwendung von &amp;#039;&amp;#039;&amp;#039;Prepared Statements zwingend erforderlich&amp;#039;&amp;#039;&amp;#039;, um die Integrität und Sicherheit der Datenbank zu gewährleisten, insbesondere wenn Benutzereingaben beteiligt sind.&lt;br /&gt;
&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;PDO:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;Vorbereiten (&amp;lt;code&amp;gt;prepare&amp;lt;/code&amp;gt;):&amp;#039;&amp;#039;&amp;#039; Die SQL-Anweisung (&amp;lt;code&amp;gt;INSERT&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;UPDATE&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;DELETE&amp;lt;/code&amp;gt;) wird mit Platzhaltern (&amp;lt;code&amp;gt;?&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;:name&amp;lt;/code&amp;gt;) für die einzufügenden/zu aktualisierenden Werte sowie für die Kriterien in &amp;lt;code&amp;gt;WHERE&amp;lt;/code&amp;gt;-Klauseln (bei &amp;lt;code&amp;gt;UPDATE&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;DELETE&amp;lt;/code&amp;gt;) vorbereitet.[5, 46, 47, 48]&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;Ausführen (&amp;lt;code&amp;gt;execute&amp;lt;/code&amp;gt;):&amp;#039;&amp;#039;&amp;#039; Die Anweisung wird mit einem Array ausgeführt, das die Werte für die Platzhalter enthält.[5, 46, 47, 48]&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;Ergebnis prüfen:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
***** &amp;#039;&amp;#039;Anzahl betroffener Zeilen:&amp;#039;&amp;#039; &amp;lt;code&amp;gt;$stmt-&amp;amp;gt;rowCount()&amp;lt;/code&amp;gt; gibt die Anzahl der durch &amp;lt;code&amp;gt;UPDATE&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;DELETE&amp;lt;/code&amp;gt; betroffenen Zeilen zurück. Bei &amp;lt;code&amp;gt;INSERT&amp;lt;/code&amp;gt; ist der Rückgabewert oft nicht zuverlässig oder standardisiert.[8, 9, 5, 49] Das Verhalten kann je nach Datenbanktreiber und Konfiguration (z.B. &amp;lt;code&amp;gt;PDO::MYSQL_ATTR_FOUND_ROWS&amp;lt;/code&amp;gt;) variieren.[9]&lt;br /&gt;
***** &amp;#039;&amp;#039;ID des letzten eingefügten Datensatzes:&amp;#039;&amp;#039; Bei &amp;lt;code&amp;gt;INSERT&amp;lt;/code&amp;gt;-Anweisungen in Tabellen mit einer &amp;lt;code&amp;gt;AUTO_INCREMENT&amp;lt;/code&amp;gt;-Spalte liefert &amp;lt;code&amp;gt;$pdo-&amp;amp;gt;lastInsertId()&amp;lt;/code&amp;gt; die ID des neu eingefügten Datensatzes zurück.[8, 33, 48]&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;Code-Beispiele (PDO):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!--?php// --- INSERT ---&lt;br /&gt;
$name = $_POST[&amp;#039;name&amp;#039;];&lt;br /&gt;
$email = $_POST[&amp;#039;email&amp;#039;];&lt;br /&gt;
$status = &amp;#039;pending&amp;#039;;&lt;br /&gt;
&lt;br /&gt;
$sql_insert = &amp;quot;INSERT INTO users (username, email, status) VALUES (:name, :email, :status)&amp;quot;;&lt;br /&gt;
$stmt_insert = $pdo---&amp;gt;prepare($sql_insert);&lt;br /&gt;
$stmt_insert-&amp;amp;gt;execute([&amp;#039;:name&amp;#039; =&amp;amp;gt; $name, &amp;#039;:email&amp;#039; =&amp;amp;gt; $email, &amp;#039;:status&amp;#039; =&amp;amp;gt; $status]);&lt;br /&gt;
$lastId = $pdo-&amp;amp;gt;lastInsertId();&lt;br /&gt;
echo &amp;quot;Neuer Benutzer mit ID $lastId eingefügt.&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
// --- UPDATE ---&lt;br /&gt;
$new_status = &amp;#039;active&amp;#039;;&lt;br /&gt;
$user_id_to_update = $lastId;&lt;br /&gt;
&lt;br /&gt;
$sql_update = &amp;quot;UPDATE users SET status = :status WHERE id = :id&amp;quot;;&lt;br /&gt;
$stmt_update = $pdo-&amp;amp;gt;prepare($sql_update);&lt;br /&gt;
$stmt_update-&amp;amp;gt;execute([&amp;#039;:status&amp;#039; =&amp;amp;gt; $new_status, &amp;#039;:id&amp;#039; =&amp;amp;gt; $user_id_to_update]);&lt;br /&gt;
$affectedRows = $stmt_update-&amp;amp;gt;rowCount();&lt;br /&gt;
echo &amp;quot;$affectedRows Benutzerstatus aktualisiert.&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
// --- DELETE ---&lt;br /&gt;
$user_id_to_delete = $lastId;&lt;br /&gt;
&lt;br /&gt;
$sql_delete = &amp;quot;DELETE FROM users WHERE id = :id&amp;quot;;&lt;br /&gt;
$stmt_delete = $pdo-&amp;amp;gt;prepare($sql_delete);&lt;br /&gt;
$stmt_delete-&amp;amp;gt;execute([&amp;#039;:id&amp;#039; =&amp;amp;gt; $user_id_to_delete]);&lt;br /&gt;
$affectedRowsDel = $stmt_delete-&amp;amp;gt;rowCount();&lt;br /&gt;
echo &amp;quot;$affectedRowsDel Benutzer gelöscht.&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
?&amp;amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;MySQLi:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;Vorbereiten (&amp;lt;code&amp;gt;prepare&amp;lt;/code&amp;gt;):&amp;#039;&amp;#039;&amp;#039; Die SQL-Anweisung wird mit &amp;lt;code&amp;gt;?&amp;lt;/code&amp;gt;-Platzhaltern vorbereitet (OO: &amp;lt;code&amp;gt;$mysqli-&amp;amp;gt;prepare()&amp;lt;/code&amp;gt;, Proc: &amp;lt;code&amp;gt;mysqli_prepare()&amp;lt;/code&amp;gt;).[25, 50, 15, 35]&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;Parameter binden (&amp;lt;code&amp;gt;bind_param&amp;lt;/code&amp;gt;):&amp;#039;&amp;#039;&amp;#039; Variablen werden explizit mit Typenangabe gebunden (OO: &amp;lt;code&amp;gt;$stmt-&amp;amp;gt;bind_param()&amp;lt;/code&amp;gt;, Proc: &amp;lt;code&amp;gt;mysqli_stmt_bind_param()&amp;lt;/code&amp;gt;).[25, 50, 15, 35] Die Notwendigkeit, den Datentyp für jede Variable anzugeben, ist ein wesentlicher Unterschied zur einfacheren Array-Übergabe bei PDOs &amp;lt;code&amp;gt;execute()&amp;lt;/code&amp;gt;.&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;Ausführen (&amp;lt;code&amp;gt;execute&amp;lt;/code&amp;gt;):&amp;#039;&amp;#039;&amp;#039; Die Anweisung wird ausgeführt (OO: &amp;lt;code&amp;gt;$stmt-&amp;amp;gt;execute()&amp;lt;/code&amp;gt;, Proc: &amp;lt;code&amp;gt;mysqli_stmt_execute()&amp;lt;/code&amp;gt;).[25, 36, 50, 15, 35]&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;Ergebnis prüfen:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
***** &amp;#039;&amp;#039;Anzahl betroffener Zeilen:&amp;#039;&amp;#039; Über die Eigenschaft &amp;lt;code&amp;gt;$mysqli-&amp;amp;gt;affected_rows&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;$stmt-&amp;amp;gt;affected_rows&amp;lt;/code&amp;gt; (OO) bzw. die Funktionen &amp;lt;code&amp;gt;mysqli_affected_rows($link)&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;mysqli_stmt_affected_rows($stmt)&amp;lt;/code&amp;gt; (Proc).[18, 26, 15, 35]&lt;br /&gt;
***** &amp;#039;&amp;#039;ID des letzten eingefügten Datensatzes:&amp;#039;&amp;#039; Über &amp;lt;code&amp;gt;$mysqli-&amp;amp;gt;insert_id&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;$stmt-&amp;amp;gt;insert_id&amp;lt;/code&amp;gt; (OO) bzw. &amp;lt;code&amp;gt;mysqli_insert_id($link)&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;mysqli_stmt_insert_id($stmt)&amp;lt;/code&amp;gt; (Proc).[26, 15] Die Verwendung von &amp;lt;code&amp;gt;$mysqli-&amp;amp;gt;insert_id&amp;lt;/code&amp;gt; wird oft als zuverlässiger angesehen, da sie auch nach dem Schließen des Statements funktioniert.[15]&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;Code-Beispiele (MySQLi OO):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!--?php// --- INSERT ---&lt;br /&gt;
$name = $_POST[&amp;#039;name&amp;#039;];&lt;br /&gt;
$email = $_POST[&amp;#039;email&amp;#039;];&lt;br /&gt;
$status = &amp;#039;pending&amp;#039;;&lt;br /&gt;
&lt;br /&gt;
$sql_insert = &amp;quot;INSERT INTO users (username, email, status) VALUES (?,?,?)&amp;quot;;&lt;br /&gt;
$stmt_insert = $mysqli---&amp;gt;prepare($sql_insert);&lt;br /&gt;
// Typen: s=string, s=string, s=string&lt;br /&gt;
$stmt_insert-&amp;amp;gt;bind_param(&amp;quot;sss&amp;quot;, $name, $email, $status);&lt;br /&gt;
$stmt_insert-&amp;amp;gt;execute();&lt;br /&gt;
$lastId = $mysqli-&amp;amp;gt;insert_id;&lt;br /&gt;
echo &amp;quot;Neuer Benutzer mit ID $lastId eingefügt (MySQLi OO).&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
$stmt_insert-&amp;amp;gt;close(); // Statement schließen&lt;br /&gt;
&lt;br /&gt;
// --- UPDATE ---&lt;br /&gt;
$new_status = &amp;#039;active&amp;#039;;&lt;br /&gt;
$user_id_to_update = $lastId;&lt;br /&gt;
&lt;br /&gt;
$sql_update = &amp;quot;UPDATE users SET status =? WHERE id =?&amp;quot;;&lt;br /&gt;
$stmt_update = $mysqli-&amp;amp;gt;prepare($sql_update);&lt;br /&gt;
// Typen: s=string, i=integer&lt;br /&gt;
$stmt_update-&amp;amp;gt;bind_param(&amp;quot;si&amp;quot;, $new_status, $user_id_to_update);&lt;br /&gt;
$stmt_update-&amp;amp;gt;execute();&lt;br /&gt;
$affectedRows = $mysqli-&amp;amp;gt;affected_rows; // Oder $stmt_update-&amp;amp;gt;affected_rows vor close()&lt;br /&gt;
echo &amp;quot;$affectedRows Benutzerstatus aktualisiert (MySQLi OO).&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
$stmt_update-&amp;amp;gt;close();&lt;br /&gt;
&lt;br /&gt;
// --- DELETE ---&lt;br /&gt;
$user_id_to_delete = $lastId;&lt;br /&gt;
&lt;br /&gt;
$sql_delete = &amp;quot;DELETE FROM users WHERE id =?&amp;quot;;&lt;br /&gt;
$stmt_delete = $mysqli-&amp;amp;gt;prepare($sql_delete);&lt;br /&gt;
// Typ: i=integer&lt;br /&gt;
$stmt_delete-&amp;amp;gt;bind_param(&amp;quot;i&amp;quot;, $user_id_to_delete);&lt;br /&gt;
$stmt_delete-&amp;amp;gt;execute();&lt;br /&gt;
$affectedRowsDel = $mysqli-&amp;amp;gt;affected_rows;&lt;br /&gt;
echo &amp;quot;$affectedRowsDel Benutzer gelöscht (MySQLi OO).&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
$stmt_delete-&amp;amp;gt;close();&lt;br /&gt;
?&amp;amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;#039;&amp;#039;(Anmerkung: Prozedurale Beispiele folgen demselben Muster unter Verwendung der &amp;lt;code&amp;gt;mysqli_*&amp;lt;/code&amp;gt;-Funktionen wie &amp;lt;code&amp;gt;mysqli_prepare&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;mysqli_stmt_bind_param&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;mysqli_stmt_execute&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;mysqli_insert_id&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;mysqli_affected_rows&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;mysqli_stmt_close&amp;lt;/code&amp;gt;)&amp;#039;&amp;#039;.&lt;br /&gt;
&lt;br /&gt;
== Sicherheit: SQL Injection verstehen und verhindern ==&lt;br /&gt;
&lt;br /&gt;
Eines der größten Sicherheitsrisiken bei der Interaktion mit Datenbanken über Webanwendungen ist die SQL Injection (SQLi). Das Verständnis dieser Bedrohung und ihrer Abwehr ist für jeden PHP-Entwickler unerlässlich.&lt;br /&gt;
&lt;br /&gt;
=== SQL Injection ===&lt;br /&gt;
&lt;br /&gt;
SQL Injection ist eine Angriffstechnik, bei der ein Angreifer versucht, bösartigen oder manipulierten SQL-Code über Benutzereingabefelder einer Webanwendung (z.B. Formulare, URL-Parameter, HTTP-Header, Cookies) in die Datenbankabfragen einzuschleusen.[51, 52, 53, 54, 55, 56, 57]&lt;br /&gt;
&lt;br /&gt;
Die Attacke funktioniert, indem Schwachstellen in der Anwendung ausgenutzt werden, bei denen Benutzereingaben direkt und ohne ausreichende Validierung oder Maskierung (Escaping) in SQL-Abfragestrings eingefügt (konkateniert) werden.[5, 51, 55] Dadurch wird die notwendige Trennung zwischen dem eigentlichen SQL-Befehl (Kontrollebene) und den Daten (Datenebene) aufgehoben.[51] Der Angreifer kann spezielle Zeichen (wie Anführungszeichen &amp;lt;code&amp;gt;&amp;#039;&amp;lt;/code&amp;gt;, Semikolons &amp;lt;code&amp;gt;;&amp;lt;/code&amp;gt; oder Kommentare &amp;lt;code&amp;gt;--&amp;lt;/code&amp;gt;) verwenden, um den ursprünglichen SQL-Befehl vorzeitig zu beenden, zu verändern oder um eigene, zusätzliche SQL-Befehle anzuhängen.[51, 53]&lt;br /&gt;
&lt;br /&gt;
Ein klassisches Beispiel ist eine Login-Abfrage, die etwa so aufgebaut ist:&lt;br /&gt;
&amp;lt;code&amp;gt;$sql = &amp;quot;SELECT * FROM users WHERE username = &amp;#039;&amp;quot;. $userInputUsername. &amp;quot;&amp;#039; AND password = &amp;#039;&amp;quot;. $userInputPassword. &amp;quot;&amp;#039;&amp;quot;;&amp;lt;/code&amp;gt;&lt;br /&gt;
Wenn ein Angreifer als Benutzernamen &amp;lt;code&amp;gt;&amp;#039; OR &amp;#039;1&amp;#039;=&amp;#039;1&amp;lt;/code&amp;gt; eingibt, wird die resultierende SQL-Abfrage zu:&lt;br /&gt;
&amp;lt;code&amp;gt;SELECT * FROM users WHERE username = &amp;#039;&amp;#039; OR &amp;#039;1&amp;#039;=&amp;#039;1&amp;#039; AND password = &amp;#039;...&amp;#039;&amp;lt;/code&amp;gt;&lt;br /&gt;
Da &amp;lt;code&amp;gt;&amp;#039;1&amp;#039;=&amp;#039;1&amp;#039;&amp;lt;/code&amp;gt; immer wahr ist, würde diese Abfrage (je nach genauer Struktur und Datenbank) möglicherweise alle Benutzer zurückgeben oder den Login ohne korrektes Passwort ermöglichen.[5, 15, 55]&lt;br /&gt;
&lt;br /&gt;
=== Auswirkungen und Gefahren von SQL Injection ===&lt;br /&gt;
&lt;br /&gt;
Die potenziellen Folgen einer erfolgreichen SQL-Injection-Attacke sind gravierend und vielfältig:&lt;br /&gt;
&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Datendiebstahl:&amp;#039;&amp;#039;&amp;#039; Angreifer können auf sensible Daten zugreifen und diese auslesen, darunter Benutzerdaten, Passwörter (oft als Hashes, die aber geknackt werden können), Kreditkarteninformationen, Geschäftsgeheimnisse und andere vertrauliche Informationen.[51, 53, 56]&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Datenmanipulation/-zerstörung:&amp;#039;&amp;#039;&amp;#039; Angreifer können bestehende Daten in der Datenbank verändern (z.B. Preise in einem Shop, Benutzerrollen) oder komplette Datensätze oder sogar Tabellen löschen (&amp;lt;code&amp;gt;DELETE&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;DROP TABLE&amp;lt;/code&amp;gt;).[51, 53, 56]&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Umgehung der Authentifizierung/Identitätsdiebstahl:&amp;#039;&amp;#039;&amp;#039; Wie im Beispiel oben gezeigt, können Angreifer Login-Mechanismen umgehen und sich unautorisiert Zugang verschaffen, möglicherweise sogar mit administrativen Rechten.[51, 56]&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Denial of Service (DoS):&amp;#039;&amp;#039;&amp;#039; Durch das Löschen wichtiger Daten oder das Ausführen ressourcenintensiver Abfragen kann die Verfügbarkeit der Anwendung oder der Datenbank beeinträchtigt werden.[51, 56]&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Kompromittierung des Servers:&amp;#039;&amp;#039;&amp;#039; In manchen Fällen können Angreifer über SQL Injection Betriebssystembefehle auf dem Datenbankserver ausführen oder vollen administrativen Zugriff auf die Datenbank erlangen, was weitreichende Konsequenzen haben kann.[51, 55, 56]&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Reputationsschaden und rechtliche Folgen:&amp;#039;&amp;#039;&amp;#039; Erfolgreiche Angriffe, insbesondere wenn sie zu Datenlecks führen, können das Vertrauen der Benutzer schwer beschädigen und zu empfindlichen Strafen gemäß Datenschutzgesetzen führen.[53]&lt;br /&gt;
&lt;br /&gt;
=== Prävention durch Prepared Statements (Primäre Methode) ===&lt;br /&gt;
&lt;br /&gt;
Die mit Abstand wichtigste und effektivste Methode zur Verhinderung von SQL Injection ist die konsequente Verwendung von &amp;#039;&amp;#039;&amp;#039;Prepared Statements&amp;#039;&amp;#039;&amp;#039; (parametrisierten Abfragen).[4, 25, 5, 58, 35]&lt;br /&gt;
&lt;br /&gt;
Das Grundprinzip von Prepared Statements ist die &amp;#039;&amp;#039;&amp;#039;strikte Trennung von SQL-Befehlsstruktur und den Daten (Parametern)&amp;#039;&amp;#039;&amp;#039;.[4, 25, 5, 58, 35] Der Ablauf ist typischerweise zweistufig:&lt;br /&gt;
**# &amp;#039;&amp;#039;&amp;#039;Prepare (Vorbereiten):&amp;#039;&amp;#039;&amp;#039; Die SQL-Anweisung wird mit Platzhaltern anstelle der tatsächlichen Werte an den Datenbankserver gesendet. Der Server parst die Anweisung, prüft die Syntax und bereitet einen Ausführungsplan vor, ohne die endgültigen Daten zu kennen.&lt;br /&gt;
**# &amp;#039;&amp;#039;&amp;#039;Execute (Ausführen) mit Parameterbindung:&amp;#039;&amp;#039;&amp;#039; Die tatsächlichen Werte (Parameter) werden separat an den Server gesendet und an die vorbereiteten Platzhalter gebunden. Der Server führt nun die vorkompilierte Anweisung mit den sicher eingefügten Werten aus.&lt;br /&gt;
Der entscheidende Punkt ist, dass die Datenbank die separat gesendeten Parameter &amp;#039;&amp;#039;&amp;#039;immer als Datenwerte&amp;#039;&amp;#039;&amp;#039; behandelt, niemals als Teil des SQL-Befehls.[5, 15, 58] Selbst wenn ein Parameter bösartigen SQL-Code enthält (z.B. &amp;lt;code&amp;gt;&amp;#039; OR &amp;#039;1&amp;#039;=&amp;#039;1&amp;lt;/code&amp;gt;), wird dieser nicht ausgeführt, sondern als einfacher String-Wert behandelt, der z.B. in eine Spalte eingefügt oder in einer &amp;lt;code&amp;gt;WHERE&amp;lt;/code&amp;gt;-Klausel verglichen wird. Diese Trennung macht Prepared Statements immun gegen SQL Injection.&lt;br /&gt;
&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Platzhalter:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;PDO:&amp;#039;&amp;#039;&amp;#039; Unterstützt sowohl positionale Platzhalter (&amp;lt;code&amp;gt;?&amp;lt;/code&amp;gt;) als auch benannte Platzhalter (&amp;lt;code&amp;gt;:name&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;:email&amp;lt;/code&amp;gt;, etc.).[5, 13] Benannte Platzhalter machen den Code oft lesbarer, besonders bei vielen Parametern.&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;MySQLi:&amp;#039;&amp;#039;&amp;#039; Unterstützt ausschließlich positionale Platzhalter (&amp;lt;code&amp;gt;?&amp;lt;/code&amp;gt;).[25]&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Parameterbindung:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;PDO:&amp;#039;&amp;#039;&amp;#039; Bietet flexible Bindungsoptionen:&lt;br /&gt;
***** &amp;#039;&amp;#039;Implizit über &amp;lt;code&amp;gt;execute()&amp;lt;/code&amp;gt;-Array:&amp;#039;&amp;#039; Die einfachste und häufigste Methode. Ein Array mit den Werten wird an &amp;lt;code&amp;gt;execute()&amp;lt;/code&amp;gt; übergeben. PDO kümmert sich um das korrekte Escaping und die Typbehandlung (oft werden Werte sicher als Strings behandelt).[4, 12, 5, 13]&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$stmt-&amp;amp;gt;execute([$wert1, $wert2]); // für? Platzhalter&lt;br /&gt;
$stmt-&amp;amp;gt;execute([&amp;#039;:name&amp;#039; =&amp;amp;gt; $wert1, &amp;#039;:id&amp;#039; =&amp;amp;gt; $wert2]); // für :name Platzhalter&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
***** &amp;#039;&amp;#039;Explizit mit &amp;lt;code&amp;gt;bindValue()&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;bindParam()&amp;lt;/code&amp;gt;:&amp;#039;&amp;#039; Erlaubt die explizite Angabe des Datentyps (z.B. &amp;lt;code&amp;gt;PDO::PARAM_INT&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;PDO::PARAM_STR&amp;lt;/code&amp;gt;) für jeden Parameter, was in manchen Fällen für Klarheit oder spezifische Datenbankoptimierungen sorgen kann.[5, 13] &amp;lt;code&amp;gt;bindParam&amp;lt;/code&amp;gt; bindet eine Variable (ihr Wert wird erst bei &amp;lt;code&amp;gt;execute()&amp;lt;/code&amp;gt; gelesen), &amp;lt;code&amp;gt;bindValue&amp;lt;/code&amp;gt; bindet den aktuellen Wert einer Variablen oder einen Literalwert.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$stmt-&amp;amp;gt;bindValue(&amp;#039;:id&amp;#039;, $user_id, PDO::PARAM_INT);&lt;br /&gt;
$stmt-&amp;amp;gt;bindParam(&amp;#039;:name&amp;#039;, $user_name, PDO::PARAM_STR);&lt;br /&gt;
$stmt-&amp;amp;gt;execute();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;MySQLi:&amp;#039;&amp;#039;&amp;#039; Erfordert immer die explizite Bindung mittels &amp;lt;code&amp;gt;bind_param()&amp;lt;/code&amp;gt;.&lt;br /&gt;
***** &amp;lt;code&amp;gt;bind_param()&amp;lt;/code&amp;gt; (OO) / &amp;lt;code&amp;gt;mysqli_stmt_bind_param()&amp;lt;/code&amp;gt; (Proc): Nimmt als ersten Parameter einen String entgegen, der die Datentypen aller nachfolgend übergebenen Variablen definiert.[25, 50, 15, 58]&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// OO-Stil&lt;br /&gt;
$stmt-&amp;amp;gt;bind_param(&amp;quot;ssi&amp;quot;, $name, $email, $id); // String, String, Integer&lt;br /&gt;
// Prozedural&lt;br /&gt;
mysqli_stmt_bind_param($stmt, &amp;quot;ssi&amp;quot;, $name, $email, $id);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
=== Tabelle 4: MySQLi &amp;lt;code&amp;gt;bind_param()&amp;lt;/code&amp;gt; Typen-String ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Typ-Buchstabe !! PHP-Datentyp (typisch) !! Beschreibung&lt;br /&gt;
|- &lt;br /&gt;
| mwt-preservehtml=&amp;quot;&amp;quot; i=&amp;quot;&amp;quot; code=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| integer=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Integer (Ganzzahl)&lt;br /&gt;
|- &lt;br /&gt;
| mwt-preservehtml=&amp;quot;&amp;quot; d=&amp;quot;&amp;quot; code=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| double=&amp;quot;&amp;quot; float=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Double (Gleitkommazahl)&lt;br /&gt;
|- &lt;br /&gt;
| mwt-preservehtml=&amp;quot;&amp;quot; s=&amp;quot;&amp;quot; code=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| string=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| String (Zeichenkette)&lt;br /&gt;
|- &lt;br /&gt;
| mwt-preservehtml=&amp;quot;&amp;quot; b=&amp;quot;&amp;quot; code=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| string=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| BLOB (Binary Large Object), als String gesendet&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Detaillierte Code-Beispiele zur Prävention:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
Die Beispiele für INSERT, UPDATE und DELETE in Abschnitt 3.3 demonstrieren bereits die korrekte Anwendung von Prepared Statements mit PDO und MySQLi zur Verhinderung von SQL Injection. Es ist entscheidend, dieses Muster konsequent anzuwenden, wann immer externe Daten in SQL-Abfragen einfließen.&lt;br /&gt;
&lt;br /&gt;
=== 4.4 Zusätzliche Schutzmaßnahmen (Defense-in-Depth) ===&lt;br /&gt;
&lt;br /&gt;
Obwohl Prepared Statements der wichtigste Schutz gegen SQL Injection sind, sollte Sicherheit immer als mehrschichtiges Konzept (&amp;quot;Defense-in-Depth&amp;quot;) betrachtet werden.[5] Zusätzliche Maßnahmen erhöhen die Robustheit der Anwendung:&lt;br /&gt;
&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Eingabevalidierung und -bereinigung:&amp;#039;&amp;#039;&amp;#039; Überprüfen Sie &amp;#039;&amp;#039;alle&amp;#039;&amp;#039; Benutzereingaben serverseitig, bevor sie überhaupt in die Nähe der Datenbank kommen.[4, 59, 20, 5] Stellen Sie sicher, dass die Daten dem erwarteten Typ (Zahl, String, E-Mail-Format etc.) und Format entsprechen. Verwenden Sie dafür PHP-Filterfunktionen wie &amp;lt;code&amp;gt;filter_var()&amp;lt;/code&amp;gt; oder reguläre Ausdrücke.[4, 5] Dies ist eine wichtige zweite Verteidigungslinie, die ungültige oder unerwartete Daten frühzeitig abfängt. Es ersetzt jedoch &amp;#039;&amp;#039;nicht&amp;#039;&amp;#039; die Notwendigkeit von Prepared Statements.&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Prinzip der geringsten Rechte (Least Privilege):&amp;#039;&amp;#039;&amp;#039; Konfigurieren Sie den Datenbankbenutzer, den Ihre PHP-Anwendung verwendet, mit den absolut minimal notwendigen Berechtigungen.[20, 5] Wenn die Anwendung nur Daten lesen und schreiben muss, geben Sie ihr keine Rechte zum Ändern der Tabellenstruktur (&amp;lt;code&amp;gt;ALTER TABLE&amp;lt;/code&amp;gt;) oder zum Löschen von Tabellen (&amp;lt;code&amp;gt;DROP TABLE&amp;lt;/code&amp;gt;). Beschränken Sie den Zugriff auf die benötigten Datenbanken und Tabellen. Verwenden Sie niemals den &amp;lt;code&amp;gt;root&amp;lt;/code&amp;gt;- oder Admin-Benutzer der Datenbank für die Anwendung. Dies begrenzt den potenziellen Schaden, falls doch eine Schwachstelle ausgenutzt wird.&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Sichere Fehlerbehandlung:&amp;#039;&amp;#039;&amp;#039; Konfigurieren Sie PHP und Ihre Datenbankerweiterung so, dass detaillierte Fehlermeldungen (insbesondere solche, die SQL-Code oder Datenbankstrukturinformationen enthalten) niemals dem Endbenutzer angezeigt werden.[20, 5] Solche Informationen sind für Angreifer wertvoll. Loggen Sie Fehler stattdessen in eine sichere Datei auf dem Server, die nur für Administratoren zugänglich ist. Verwenden Sie &amp;lt;code&amp;gt;PDO::ERRMODE_EXCEPTION&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;mysqli_report&amp;lt;/code&amp;gt; für strukturierte Fehlerbehandlung.&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Output Escaping:&amp;#039;&amp;#039;&amp;#039; Obwohl nicht direkt zur Verhinderung von SQL Injection, ist es wichtig, Daten, die aus der Datenbank gelesen und im HTML-Kontext ausgegeben werden, korrekt zu escapen (z.B. mit &amp;lt;code&amp;gt;htmlspecialchars()&amp;lt;/code&amp;gt;), um Cross-Site Scripting (XSS)-Angriffe zu verhindern.&lt;br /&gt;
Die Kombination dieser Maßnahmen – mit Prepared Statements als Kernstück – bietet einen robusten Schutz gegen SQL Injection und andere verwandte Angriffe.&lt;br /&gt;
&lt;br /&gt;
== Nützliche PHP Array-Funktionen für Datenbankergebnisse ==&lt;br /&gt;
&lt;br /&gt;
Wenn Daten aus der Datenbank abgerufen werden, insbesondere mit Methoden wie &amp;lt;code&amp;gt;fetchAll()&amp;lt;/code&amp;gt; (PDO) oder &amp;lt;code&amp;gt;fetch_all()&amp;lt;/code&amp;gt; (MySQLi), liegen die Ergebnisse typischerweise als PHP-Array vor (meist ein Array von assoziativen Arrays, wobei jedes innere Array eine Ergebniszeile darstellt). PHP bietet eine Reihe nützlicher Funktionen zur Verarbeitung solcher Arrays.&lt;br /&gt;
&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Iteration mit &amp;lt;code&amp;gt;foreach&amp;lt;/code&amp;gt;:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
Dies ist die Standardmethode, um die einzelnen Zeilen und Spalten eines Ergebnis-Arrays zu durchlaufen.[37, 40, 44]&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;Syntax:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Iteration über die Zeilen (jedes $row ist ein assoziatives Array)&lt;br /&gt;
foreach ($results as $row) {&lt;br /&gt;
 echo &amp;quot;ID: &amp;quot;. $row[&amp;#039;id&amp;#039;]. &amp;quot;, Name: &amp;quot;. htmlspecialchars($row[&amp;#039;name&amp;#039;]). &amp;quot;&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Iteration über Zeilen mit Zugriff auf den numerischen Index der Zeile&lt;br /&gt;
foreach ($results as $index =&amp;amp;gt; $row) {&lt;br /&gt;
 echo &amp;quot;Zeile &amp;quot;. $index. &amp;quot;: ID = &amp;quot;. $row[&amp;#039;id&amp;#039;]. &amp;quot;&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;Code-Beispiel:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!--?php// Annahme: $results enthält das Ergebnis von $stmt---&amp;gt;fetchAll(PDO::FETCH_ASSOC);&lt;br /&gt;
$results = [&amp;#039;id&amp;#039; =&amp;amp;gt; 1, &amp;#039;name&amp;#039; =&amp;amp;gt; &amp;#039;Alice&amp;#039;, &amp;#039;email&amp;#039; =&amp;amp;gt; &amp;#039;alice@example.com&amp;#039;],&lt;br /&gt;
 ;&lt;br /&gt;
&lt;br /&gt;
echo &amp;quot;&amp;lt;/syntaxhighlight&amp;gt;&amp;quot;;&lt;br /&gt;
foreach ($results as $row) {&lt;br /&gt;
 echo &amp;quot;&lt;br /&gt;
*Benutzer #&amp;quot;. $row[&amp;#039;id&amp;#039;]. &amp;quot;: &amp;quot;. htmlspecialchars($row[&amp;#039;name&amp;#039;]);&lt;br /&gt;
 echo &amp;quot; (&amp;quot;. htmlspecialchars($row[&amp;#039;email&amp;#039;]). &amp;quot;)&lt;br /&gt;
&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
echo &amp;quot;&amp;quot;;&lt;br /&gt;
?&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Zählen von Ergebnissen mit &amp;lt;code&amp;gt;count()&amp;lt;/code&amp;gt;:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
Die Funktion &amp;lt;code&amp;gt;count()&amp;lt;/code&amp;gt; gibt die Anzahl der Elemente in einem Array zurück. Wenn sie auf ein mit &amp;lt;code&amp;gt;fetchAll()&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;fetch_all()&amp;lt;/code&amp;gt; erzeugtes Ergebnis-Array angewendet wird, liefert sie die Anzahl der abgerufenen Zeilen.[40, 60, 61]&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;Syntax:&amp;#039;&amp;#039;&amp;#039; &amp;lt;code&amp;gt;$anzahlZeilen = count($results);&amp;lt;/code&amp;gt;.[60]&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;Wichtiger Hinweis:&amp;#039;&amp;#039;&amp;#039; &amp;lt;code&amp;gt;count()&amp;lt;/code&amp;gt; funktioniert nur zuverlässig für die Gesamtzahl der Zeilen, wenn &amp;#039;&amp;#039;alle&amp;#039;&amp;#039; Zeilen zuvor in ein Array geladen wurden (also nach &amp;lt;code&amp;gt;fetchAll&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;fetch_all&amp;lt;/code&amp;gt;). Wenn Sie Ergebnisse zeilenweise mit &amp;lt;code&amp;gt;fetch()&amp;lt;/code&amp;gt; in einer &amp;lt;code&amp;gt;while&amp;lt;/code&amp;gt;-Schleife verarbeiten, müssen Sie einen manuellen Zähler innerhalb der Schleife inkrementieren, um die Anzahl der verarbeiteten Zeilen zu ermitteln.[40] Alternativ können &amp;lt;code&amp;gt;PDOStatement::rowCount()&amp;lt;/code&amp;gt; [8, 9] oder &amp;lt;code&amp;gt;mysqli_result::$num_rows&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;mysqli_stmt::$num_rows&amp;lt;/code&amp;gt; [26, 15] verwendet werden, um die Anzahl der von einer &amp;lt;code&amp;gt;SELECT&amp;lt;/code&amp;gt;-Abfrage zurückgegebenen Zeilen zu erhalten, &amp;#039;&amp;#039;bevor&amp;#039;&amp;#039; die Daten gefetched werden (das Verhalten von &amp;lt;code&amp;gt;rowCount&amp;lt;/code&amp;gt; kann jedoch bei anderen Anweisungstypen wie &amp;lt;code&amp;gt;UPDATE&amp;lt;/code&amp;gt; variieren).&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;Code-Beispiel:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!--?php&lt;br /&gt;
// Annahme: $results von fetchAll()&lt;br /&gt;
$results = [ /*... wie oben... */ ];&lt;br /&gt;
$anzahl = count($results);&lt;br /&gt;
echo &amp;quot;Anzahl der Benutzer (via fetchAll/count): $anzahl&amp;lt;br--&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
// Vergleich: Zählen bei zeilenweiser Verarbeitung&lt;br /&gt;
$stmt = $pdo-&amp;amp;gt;query(&amp;quot;SELECT id FROM users&amp;quot;); // Annahme: PDO-Statement&lt;br /&gt;
$manueller_zaehler = 0;&lt;br /&gt;
while ($row = $stmt-&amp;amp;gt;fetch()) {&lt;br /&gt;
 $manueller_zaehler++;&lt;br /&gt;
}&lt;br /&gt;
echo &amp;quot;Anzahl der Benutzer (via while/fetch): $manueller_zaehler&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
// Alternative: rowCount() nach SELECT (PDO)&lt;br /&gt;
$stmt_count = $pdo-&amp;amp;gt;query(&amp;quot;SELECT id FROM users&amp;quot;);&lt;br /&gt;
$rowCountResult = $stmt_count-&amp;amp;gt;rowCount(); // Kann bei manchen DBs/Treibern funktionieren&lt;br /&gt;
echo &amp;quot;Anzahl der Benutzer (via rowCount nach SELECT): $rowCountResult&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
?&amp;amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;(Optional) Weitere nützliche Array-Funktionen:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
**** &amp;lt;code&amp;gt;array_column(array $input, mixed $column_key [, mixed $index_key = null ])&amp;lt;/code&amp;gt;: Extrahiert alle Werte aus einer einzelnen Spalte des Ergebnis-Arrays und gibt sie als neues, eindimensionales Array zurück.[62] Sehr nützlich, um z.B. eine Liste aller Benutzer-IDs oder E-Mail-Adressen zu erhalten.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Annahme: $results enthält das Array von assoziativen Arrays&lt;br /&gt;
$alle_emails = array_column($results, &amp;#039;email&amp;#039;);&lt;br /&gt;
// $alle_emails ist nun [&amp;#039;alice@example.com&amp;#039;, &amp;#039;bob@example.com&amp;#039;]&lt;br /&gt;
print_r($alle_emails);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
**** &amp;lt;code&amp;gt;array_map(callable $callback, array $array1 [, array...$arrays ])&amp;lt;/code&amp;gt;: Wendet eine benutzerdefinierte Funktion (&amp;lt;code&amp;gt;callback&amp;lt;/code&amp;gt;) auf jedes Element eines Arrays an und gibt ein neues Array mit den Ergebnissen zurück.[63] Nützlich zur Transformation von Daten (z.B. Formatierung, Bereinigung).&lt;br /&gt;
**** &amp;lt;code&amp;gt;array_filter(array $array [, callable $callback [, int $flag = 0 ]])&amp;lt;/code&amp;gt;: Filtert Elemente eines Arrays basierend auf einer Callback-Funktion.[61] Nützlich, um nur bestimmte Zeilen aus dem Ergebnis-Array zu behalten (obwohl dies oft effizienter direkt in der SQL-Abfrage geschieht).&lt;br /&gt;
**** &amp;lt;code&amp;gt;array_count_values(array $array)&amp;lt;/code&amp;gt;: Zählt, wie oft jeder eindeutige Wert in einem eindimensionalen Array vorkommt.[63] Kann nützlich sein, wenn man z.B. mit &amp;lt;code&amp;gt;array_column&amp;lt;/code&amp;gt; eine Spalte extrahiert hat und die Häufigkeit bestimmter Werte (z.B. Status) wissen möchte.&lt;br /&gt;
Diese Funktionen, insbesondere &amp;lt;code&amp;gt;foreach&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;count&amp;lt;/code&amp;gt;, sind grundlegende Werkzeuge bei der Verarbeitung von Datenbankergebnissen in PHP.&lt;br /&gt;
&lt;br /&gt;
== Zusammenfassung &amp;amp; Best Practices ==&lt;br /&gt;
&lt;br /&gt;
Die Integration von Datenbanken ist ein zentraler Aspekt der PHP-Webentwicklung. Dieses Kapitel hat die Grundlagen für die Interaktion mit MySQL-Datenbanken gelegt. Hier sind die wichtigsten Punkte und Best Practices zusammengefasst:&lt;br /&gt;
&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Wahl der Erweiterung:&amp;#039;&amp;#039;&amp;#039; Verwenden Sie ausschließlich die modernen Erweiterungen &amp;#039;&amp;#039;&amp;#039;PDO&amp;#039;&amp;#039;&amp;#039; oder &amp;#039;&amp;#039;&amp;#039;MySQLi&amp;#039;&amp;#039;&amp;#039;.[2, 29, 5, 15]&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;PDO&amp;#039;&amp;#039;&amp;#039; wird generell für neue Projekte empfohlen, da es Datenbankunabhängigkeit bietet und eine konsistentere, oft als einfacher empfundene API (insbesondere bei der Parameterbindung) hat.[16, 3, 64]&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;MySQLi&amp;#039;&amp;#039;&amp;#039; ist eine gute Wahl, wenn ausschließlich mit MySQL gearbeitet wird und spezifische MySQL-Funktionen benötigt werden oder wenn eine prozedurale Schnittstelle bevorzugt wird.[15]&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Prepared Statements:&amp;#039;&amp;#039;&amp;#039; Dies ist die wichtigste Sicherheitsmaßnahme. Verwenden Sie Prepared Statements immer dann, wenn externe Daten (Benutzereingaben, URL-Parameter etc.) Teil einer SQL-Abfrage werden (insbesondere bei &amp;lt;code&amp;gt;WHERE&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;INSERT&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;UPDATE&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;DELETE&amp;lt;/code&amp;gt;).[2, 25, 5] Sie verhindern SQL Injection zuverlässig durch die Trennung von Code und Daten.&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Sichere Fehlerbehandlung:&amp;#039;&amp;#039;&amp;#039; Konfigurieren Sie PDO (&amp;lt;code&amp;gt;PDO::ATTR_ERRMODE =&amp;amp;gt; PDO::ERRMODE_EXCEPTION&amp;lt;/code&amp;gt;) oder MySQLi (&amp;lt;code&amp;gt;mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT)&amp;lt;/code&amp;gt;) so, dass Fehler als Exceptions behandelt werden.[1, 18] Fangen Sie diese Exceptions ab und loggen Sie detaillierte Fehlermeldungen serverseitig, anstatt sie dem Benutzer anzuzeigen.[20, 5]&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Input Validation:&amp;#039;&amp;#039;&amp;#039; Validieren Sie alle Benutzereingaben serverseitig auf Typ, Format und erlaubte Werte, bevor Sie sie an die Datenbank übergeben. Dies ist eine wichtige zusätzliche Schutzschicht.[4, 5]&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Prinzip der geringsten Rechte (Least Privilege):&amp;#039;&amp;#039;&amp;#039; Der Datenbankbenutzer, den Ihre PHP-Anwendung verwendet, sollte nur die minimal notwendigen Berechtigungen für seine Aufgaben besitzen.[5]&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Sichere Zugangsdaten:&amp;#039;&amp;#039;&amp;#039; Speichern Sie Datenbank-Credentials (Hostname, Benutzername, Passwort) niemals direkt im PHP-Code oder in Dateien innerhalb des Web-Roots.[20, 65] Verwenden Sie stattdessen Konfigurationsdateien außerhalb des öffentlich zugänglichen Bereichs oder Umgebungsvariablen.&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Code-Struktur:&amp;#039;&amp;#039;&amp;#039; Kapseln Sie Datenbankverbindungslogik und wiederkehrende Abfragen in Funktionen oder Klassen, um die Wartbarkeit und Wiederverwendbarkeit zu verbessern.[20]&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Effizienz:&amp;#039;&amp;#039;&amp;#039; Laden Sie große Ergebnismengen nicht komplett in den Speicher (&amp;lt;code&amp;gt;fetchAll&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;fetch_all&amp;lt;/code&amp;gt;). Verwenden Sie stattdessen zeilenweise Verarbeitung (&amp;lt;code&amp;gt;while&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;fetch&amp;lt;/code&amp;gt;) oder Iteratoren (PDO mit PHP 8+).[37, 40] Nutzen Sie persistente Verbindungen nur nach sorgfältiger Abwägung der Vor- und Nachteile.[7, 27]&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Defense-in-Depth:&amp;#039;&amp;#039;&amp;#039; Betrachten Sie Sicherheit als Gesamtkonzept. Die Kombination aus der richtigen API-Wahl, konsequenten Prepared Statements, Eingabevalidierung, minimalen Rechten, sicherer Fehlerbehandlung, sicherer Speicherung von Zugangsdaten und regelmäßigen Software-Updates bildet eine robuste Verteidigung.&lt;/div&gt;</summary>
		<author><name>Völkl Anna</name></author>
	</entry>
	<entry>
		<id>https://mediawiki.fernfh.ac.at/mediawiki/index.php?title=Datenbankintegration_(Webdevelopment)&amp;diff=6571</id>
		<title>Datenbankintegration (Webdevelopment)</title>
		<link rel="alternate" type="text/html" href="https://mediawiki.fernfh.ac.at/mediawiki/index.php?title=Datenbankintegration_(Webdevelopment)&amp;diff=6571"/>
		<updated>2025-05-01T10:27:21Z</updated>

		<summary type="html">&lt;p&gt;Völkl Anna: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Kapitel X: Datenbankintegration mit PHP und MySQL =&lt;br /&gt;
&lt;br /&gt;
== (1) Einleitung: Warum Datenbanken in PHP-Webanwendungen? ==&lt;br /&gt;
&lt;br /&gt;
Moderne Webanwendungen sind selten statisch. Sie leben von dynamischen Inhalten, Benutzerinteraktionen und der Fähigkeit, Informationen über Sitzungen hinweg zu speichern und wieder abzurufen. Ob es sich um ein Content-Management-System (CMS), einen Online-Shop, ein soziales Netzwerk oder eine einfache Benutzerverwaltung handelt – die Persistenz von Daten ist entscheidend. Hier kommen Datenbanken ins Spiel.&lt;br /&gt;
&lt;br /&gt;
PHP allein, als serverseitige Skriptsprache, kann Daten nur für die Dauer der Ausführung eines Skripts im Arbeitsspeicher halten. Sobald das Skript beendet ist, gehen diese Informationen verloren. Um Daten dauerhaft zu speichern, zu organisieren, zu ändern und effizient abzurufen, benötigen PHP-Anwendungen ein externes Datenbanksystem.&lt;br /&gt;
&lt;br /&gt;
Eine der populärsten Datenbanklösungen im Webentwicklungsbereich, insbesondere in Kombination mit PHP, ist MySQL. Als relationales Datenbankmanagementsystem (RDBMS) bietet MySQL eine strukturierte Methode zur Datenspeicherung in Tabellen, die miteinander in Beziehung stehen können. Es ist ein Kernbestandteil vieler Webserver-Setups wie LAMP (Linux, Apache, MySQL, PHP) oder LEMP (Linux, Nginx, MySQL, PHP).&lt;br /&gt;
&lt;br /&gt;
Um die Brücke zwischen der PHP-Anwendung und der MySQL-Datenbank zu schlagen, stellt PHP spezielle Erweiterungen (Extensions) bereit. Diese Erweiterungen fungieren als Schnittstelle oder API (Application Programming Interface), die es PHP-Skripten ermöglichen, eine Verbindung zur Datenbank herzustellen, SQL-Befehle (Structured Query Language Queries) zu senden und die von der Datenbank zurückgegebenen Ergebnisse zu empfangen und zu verarbeiten.&lt;br /&gt;
&lt;br /&gt;
Dieses Kapitel führt Sie in die Grundlagen der Datenbankintegration mit PHP und MySQL ein. Sie lernen die verschiedenen verfügbaren PHP-Erweiterungen kennen, wobei der Schwerpunkt auf den modernen und sicheren Ansätzen liegt: PDO (PHP Data Objects) und MySQLi (MySQL Improved). Wir werden untersuchen, wie man Verbindungen aufbaut, grundlegende Datenbankoperationen – bekannt als CRUD (Create, Read, Update, Delete) – durchführt und wie man Daten sicher abfragt und manipuliert. Ein besonderes Augenmerk liegt dabei auf der Sicherheit, insbesondere auf der Vermeidung von SQL-Injection-Angriffen durch den Einsatz von Prepared Statements. Schließlich betrachten wir nützliche PHP-Array-Funktionen zur Verarbeitung der abgerufenen Datenbankergebnisse.&lt;br /&gt;
&lt;br /&gt;
== (2) Überblick über PHP-Datenbankerweiterungen ==&lt;br /&gt;
&lt;br /&gt;
Die Art und Weise, wie PHP mit Datenbanken interagiert, hat sich im Laufe der Zeit weiterentwickelt. Ursprünglich gab es für jede populäre Datenbank eine eigene spezifische Erweiterung. Für MySQL waren dies die &amp;lt;code&amp;gt;mysql_*&amp;lt;/code&amp;gt; Funktionen. Diese sind jedoch inzwischen veraltet und wurden durch modernere, flexiblere und sicherere Alternativen ersetzt. Heute stehen Entwicklern hauptsächlich zwei empfohlene Erweiterungen für die Arbeit mit MySQL zur Verfügung: PDO und MySQLi.&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;PDO (PHP Data Objects):&amp;#039;&amp;#039;&amp;#039; Eine allgemeine Datenzugriffs-Abstraktionsschicht, die mit verschiedenen Datenbanksystemen verwendet werden kann.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;MySQLi (MySQL Improved):&amp;#039;&amp;#039;&amp;#039; Eine Erweiterung, die speziell für die Arbeit mit MySQL entwickelt wurde und dessen spezifische Funktionen unterstützt.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;&amp;lt;code&amp;gt;mysql_*&amp;lt;/code&amp;gt; Funktionen:&amp;#039;&amp;#039;&amp;#039; Die ursprüngliche MySQL-Erweiterung, die seit PHP 7.0 entfernt wurde und nicht mehr verwendet werden sollte.&lt;br /&gt;
Im Folgenden werden diese drei Optionen detaillierter vorgestellt.&lt;br /&gt;
&lt;br /&gt;
=== 2.1 PDO (PHP Data Objects) ===&lt;br /&gt;
&lt;br /&gt;
Die PDO-Erweiterung definiert eine leichtgewichtige und konsistente Schnittstelle für den Datenbankzugriff in PHP. Sie fungiert als &amp;#039;&amp;#039;Datenzugriffs-Abstraktionsschicht&amp;#039;&amp;#039;. Das bedeutet, dass Entwickler unabhängig von der verwendeten Datenbank dieselben PDO-Funktionen nutzen können, um Abfragen auszuführen und Daten abzurufen.[1, 2] Es ist jedoch wichtig zu verstehen, dass PDO &amp;#039;&amp;#039;keine&amp;#039;&amp;#039; Datenbankabstraktion im Sinne einer SQL-Dialekt-Übersetzung oder der Emulation fehlender Datenbankfeatures bietet. Um eine Verbindung zu einer spezifischen Datenbank wie MySQL herzustellen, benötigt PDO einen entsprechenden datenbankspezifischen Treiber, z.B. &amp;lt;code&amp;gt;pdo_mysql&amp;lt;/code&amp;gt;, der in der PHP-Konfiguration aktiviert sein muss.[1]&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Vorteile von PDO:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Datenbankunabhängigkeit:&amp;#039;&amp;#039;&amp;#039; Der größte Vorteil von PDO ist die Möglichkeit, den Code für Datenbankinteraktionen weitgehend unverändert zu lassen, selbst wenn das zugrunde liegende Datenbanksystem gewechselt wird (z.B. von MySQL zu PostgreSQL).[1, 2] Dies erhöht die Portabilität und Flexibilität von PHP-Anwendungen erheblich.[2, 3]&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Sicherheit durch Prepared Statements:&amp;#039;&amp;#039;&amp;#039; PDO bietet eine robuste und konsistente Implementierung von Prepared Statements. Diese Technik ist der Goldstandard zur Verhinderung von SQL-Injection-Angriffen, da sie die SQL-Befehlsstruktur strikt von den übergebenen Daten trennt.[1, 2, 4, 5, 6]&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Benutzerfreundlichkeit und Konsistenz:&amp;#039;&amp;#039;&amp;#039; PDO stellt eine einheitliche API mit nützlichen Hilfsfunktionen bereit, wie z.B. vielfältige &amp;quot;Fetch Modes&amp;quot;, um die Struktur der abgerufenen Daten zu steuern.[2] Die konsistente Funktionsweise über verschiedene Datenbanktreiber hinweg vereinfacht die Entwicklung.&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Moderne Fehlerbehandlung:&amp;#039;&amp;#039;&amp;#039; PDO verwendet standardmäßig Exceptions (&amp;lt;code&amp;gt;PDOException&amp;lt;/code&amp;gt;) zur Signalisierung von Fehlern.[1, 2, 7] Dies ermöglicht eine strukturierte und moderne Fehlerbehandlung mittels &amp;lt;code&amp;gt;try...catch&amp;lt;/code&amp;gt;-Blöcken, was als Best Practice gilt.[1, 2]&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Verbindungsaufbau mit PDO (Beispiel MySQL):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;DSN (Data Source Name):&amp;#039;&amp;#039;&amp;#039; Die Verbindungsparameter werden in einer Zeichenkette, dem DSN, übergeben. Für MySQL hat der DSN typischerweise das Format &amp;lt;code&amp;gt;mysql:host=hostname;dbname=datenbankname;charset=zeichensatz;unix_socket=pfad_zum_socket&amp;lt;/code&amp;gt;. Die Angabe des Zeichensatzes (z.B. &amp;lt;code&amp;gt;utf8mb4&amp;lt;/code&amp;gt;) ist wichtig für die korrekte Datenübertragung.[1, 7] Beispiel: &amp;lt;code&amp;gt;mysql:host=localhost;dbname=test;charset=utf8mb4&amp;lt;/code&amp;gt;.&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;PDO-Konstruktor:&amp;#039;&amp;#039;&amp;#039; Eine Verbindung wird durch Instanziierung der PDO-Klasse hergestellt: &amp;lt;code&amp;gt;$pdo = new PDO($dsn, $username, $password, $options);&amp;lt;/code&amp;gt;.[1, 7, 8] Die Parameter sind der DSN, der Datenbankbenutzername, das Passwort und ein optionales Array mit Treiberoptionen.&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Verbindungsoptionen (&amp;lt;code&amp;gt;$options&amp;lt;/code&amp;gt;):&amp;#039;&amp;#039;&amp;#039; Über dieses Array können wichtige Verhaltensweisen von PDO gesteuert werden:&lt;br /&gt;
*** &amp;lt;code&amp;gt;PDO::ATTR_ERRMODE =&amp;amp;gt; PDO::ERRMODE_EXCEPTION&amp;lt;/code&amp;gt;: Dies ist die empfohlene Einstellung für die Fehlerbehandlung. PDO wirft bei Fehlern eine &amp;lt;code&amp;gt;PDOException&amp;lt;/code&amp;gt;, die abgefangen werden kann.[1, 2, 4]&lt;br /&gt;
*** &amp;lt;code&amp;gt;PDO::ATTR_DEFAULT_FETCH_MODE =&amp;amp;gt; PDO::FETCH_ASSOC&amp;lt;/code&amp;gt;: Legt fest, dass Daten standardmäßig als assoziatives Array (&amp;lt;code&amp;gt;spaltenname =&amp;amp;gt; wert&amp;lt;/code&amp;gt;) zurückgegeben werden, was oft die Weiterverarbeitung erleichtert.[1, 9]&lt;br /&gt;
*** &amp;lt;code&amp;gt;PDO::ATTR_EMULATE_PREPARES =&amp;amp;gt; false&amp;lt;/code&amp;gt;: Deaktiviert die Emulation von Prepared Statements durch PDO und zwingt die Verwendung nativer Prepared Statements des Datenbanktreibers. Dies gilt als die sicherste und oft performanteste Methode.[1, 10, 9, 11] Standardmäßig ist die Emulation aktiviert (&amp;lt;code&amp;gt;true&amp;lt;/code&amp;gt;). Obwohl PDO auch im emulierten Modus durch korrektes Escaping Schutz vor SQL Injection bietet [12, 5, 13], eliminiert die Deaktivierung potenzielle, wenn auch seltene, Sicherheitsrisiken, die in bestimmten Konstellationen (z.B. bei Verwendung exotischer Zeichensätze wie GBK [11]) auftreten könnten. Die explizite Deaktivierung ist daher eine wichtige Best Practice.&lt;br /&gt;
*** &amp;lt;code&amp;gt;PDO::ATTR_PERSISTENT =&amp;amp;gt; true&amp;lt;/code&amp;gt;: Aktiviert persistente Datenbankverbindungen. Diese werden nicht am Ende des Skripts geschlossen, sondern in einem Pool gehalten und für nachfolgende Anfragen wiederverwendet. Dies kann die Performance von Webanwendungen verbessern, da der Overhead des Verbindungsaufbaus entfällt. Die Verwendung erfordert jedoch sorgfältige Überlegung, da der Zustand der Verbindung zwischen Anfragen bestehen bleiben kann.[7]&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Fehlerbehandlung:&amp;#039;&amp;#039;&amp;#039; Der Verbindungsaufbau sollte immer in einem &amp;lt;code&amp;gt;try...catch&amp;lt;/code&amp;gt;-Block erfolgen, um &amp;lt;code&amp;gt;PDOException&amp;lt;/code&amp;gt; abzufangen und angemessen darauf zu reagieren (z.B. Fehlermeldung loggen, alternative Aktion ausführen).[1, 7] Es ist kritisch, detaillierte Fehlermeldungen in Produktionsumgebungen nicht an den Benutzer auszugeben, da sie sensible Informationen enthalten könnten. Die PHP-Einstellung &amp;lt;code&amp;gt;display_errors&amp;lt;/code&amp;gt; sollte daher auf &amp;lt;code&amp;gt;0&amp;lt;/code&amp;gt; gesetzt sein.[7]&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Verbindung schließen:&amp;#039;&amp;#039;&amp;#039; Eine PDO-Verbindung bleibt für die Lebensdauer des PDO-Objekts aktiv. Um sie explizit zu schließen, müssen alle Referenzen auf das PDO-Objekt und alle davon abgeleiteten &amp;lt;code&amp;gt;PDOStatement&amp;lt;/code&amp;gt;-Objekte entfernt werden, typischerweise durch Zuweisung von &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt;.[7] PHP schließt die Verbindung automatisch am Ende des Skriptlaufs, wenn dies nicht manuell geschieht.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Code-Beispiel (PDO-Verbindung zu MySQL):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!--?php$host = &amp;#039;localhost&amp;#039;;&lt;br /&gt;
$dbname = &amp;#039;meine_datenbank&amp;#039;;&lt;br /&gt;
$username = &amp;#039;mein_benutzer&amp;#039;;&lt;br /&gt;
$password = &amp;#039;mein_passwort&amp;#039;;&lt;br /&gt;
$charset = &amp;#039;utf8mb4&amp;#039;;&lt;br /&gt;
&lt;br /&gt;
$dsn = &amp;quot;mysql:host=$host;dbname=$dbname;charset=$charset&amp;quot;;&lt;br /&gt;
$options =;&lt;br /&gt;
&lt;br /&gt;
try {&lt;br /&gt;
     $pdo = new PDO($dsn, $username, $password, $options);&lt;br /&gt;
     // Erfolgreich verbunden, $pdo-Objekt kann nun verwendet werden.&lt;br /&gt;
     // echo &amp;quot;Verbindung erfolgreich hergestellt!&amp;quot;;&lt;br /&gt;
} catch (\PDOException $e) {&lt;br /&gt;
     // Fehler beim Verbindungsaufbau&lt;br /&gt;
     // In Produktion: Fehler loggen, statt ausgeben&lt;br /&gt;
     error_log(&amp;quot;Datenbankverbindungsfehler: &amp;quot;. $e---&amp;gt;getMessage());&lt;br /&gt;
 die(&amp;quot;Es gab ein Problem mit der Datenbankverbindung. Bitte versuchen Sie es später erneut.&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//... hier Datenbankoperationen mit $pdo durchführen...&lt;br /&gt;
&lt;br /&gt;
// Verbindung explizit schließen (optional, da PHP dies am Skriptende tut)&lt;br /&gt;
// $pdo = null;&lt;br /&gt;
?&amp;amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Dieses Beispiel zeigt den empfohlenen Weg, eine PDO-Verbindung zu MySQL herzustellen, inklusive wichtiger Optionen für Fehlerbehandlung und Sicherheit sowie einem &amp;lt;code&amp;gt;try...catch&amp;lt;/code&amp;gt;-Block zum Abfangen von Verbindungsfehlern.&lt;br /&gt;
&lt;br /&gt;
=== 2.2 MySQLi (MySQL Improved) ===&lt;br /&gt;
&lt;br /&gt;
Die MySQLi-Erweiterung ist, wie der Name schon sagt, speziell für die Interaktion mit MySQL-Datenbanken konzipiert und bietet Unterstützung für Funktionen, die ab MySQL Version 4.1 eingeführt wurden.[14, 15] Sie ermöglicht den Zugriff auf MySQL-spezifische Features, die über PDO möglicherweise nicht direkt verfügbar sind.[16, 15]&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Duale Schnittstelle:&amp;#039;&amp;#039;&amp;#039; Ein charakteristisches Merkmal von MySQLi ist die Unterstützung von zwei Programmierstilen [17]:&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Prozedural:&amp;#039;&amp;#039;&amp;#039; Dieser Stil ähnelt den alten &amp;lt;code&amp;gt;mysql_*&amp;lt;/code&amp;gt; Funktionen. Die Funktionsnamen beginnen typischerweise mit &amp;lt;code&amp;gt;mysqli_&amp;lt;/code&amp;gt; (z.B. &amp;lt;code&amp;gt;mysqli_connect()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;mysqli_query()&amp;lt;/code&amp;gt;). Der Datenbankverbindungs-Handle (eine Ressource oder ein Objekt) muss dabei explizit als erster Parameter an die meisten Funktionen übergeben werden.[17] Dieser Stil wird oft von Entwicklern bevorzugt, die von der alten &amp;lt;code&amp;gt;mysql&amp;lt;/code&amp;gt;-Erweiterung migrieren.[17]&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Objektorientiert (OO):&amp;#039;&amp;#039;&amp;#039; Hier wird ein Objekt der &amp;lt;code&amp;gt;mysqli&amp;lt;/code&amp;gt;-Klasse erstellt, und Datenbankoperationen werden als Methoden dieses Objekts aufgerufen (z.B. &amp;lt;code&amp;gt;$mysqli-&amp;amp;gt;query()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;$mysqli-&amp;amp;gt;prepare()&amp;lt;/code&amp;gt;).[17] Dieser Ansatz passt oft besser zu modernen PHP-Entwicklungspraktiken und vermeidet die Notwendigkeit, den Verbindungshandle ständig zu übergeben. Die offizielle PHP-Dokumentation für MySQLi ist primär im objektorientierten Stil verfasst.[17] Es gibt keine nennenswerten Performance-Unterschiede zwischen den beiden Stilen; die Wahl ist meist eine Frage der persönlichen Präferenz oder Projektkonventionen.[17]&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Verbindungsaufbau mit MySQLi:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Prozedural:&amp;#039;&amp;#039;&amp;#039; Die Funktion &amp;lt;code&amp;gt;mysqli_connect()&amp;lt;/code&amp;gt; wird verwendet: &amp;lt;code&amp;gt;$link = mysqli_connect($host, $user, $pass, $db, $port, $socket);&amp;lt;/code&amp;gt;. Sie gibt bei Erfolg ein &amp;lt;code&amp;gt;mysqli&amp;lt;/code&amp;gt;-Objekt (oder &amp;lt;code&amp;gt;false&amp;lt;/code&amp;gt; bei Fehlern vor PHP 8.1) zurück.[17, 18] Fehler können mit &amp;lt;code&amp;gt;mysqli_connect_errno()&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;mysqli_connect_error()&amp;lt;/code&amp;gt; geprüft werden.[18]&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Objektorientiert:&amp;#039;&amp;#039;&amp;#039; Ein neues &amp;lt;code&amp;gt;mysqli&amp;lt;/code&amp;gt;-Objekt wird instanziiert: &amp;lt;code&amp;gt;$mysqli = new mysqli($host, $user, $pass, $db, $port, $socket);&amp;lt;/code&amp;gt;. Wichtig: Der Konstruktor gibt &amp;#039;&amp;#039;immer&amp;#039;&amp;#039; ein Objekt zurück, auch wenn die Verbindung fehlschlägt. Der Verbindungsstatus muss explizit über die Eigenschaften &amp;lt;code&amp;gt;$mysqli-&amp;amp;gt;connect_errno&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;$mysqli-&amp;amp;gt;connect_error&amp;lt;/code&amp;gt; geprüft werden.[17, 18]&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Fortgeschrittene Verbindung:&amp;#039;&amp;#039;&amp;#039; Für feinere Kontrolle über Verbindungsoptionen (z.B. Setzen von Timeouts oder Initialisierungsbefehlen &amp;#039;&amp;#039;vor&amp;#039;&amp;#039; dem Verbindungsaufbau) können &amp;lt;code&amp;gt;mysqli_init()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;mysqli_options()&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;mysqli_real_connect()&amp;lt;/code&amp;gt; verwendet werden.[18, 19]&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Fehlerbehandlung:&amp;#039;&amp;#039;&amp;#039; Neben der manuellen Prüfung von Fehlercodes und -meldungen ist die empfohlene Methode, MySQLi so zu konfigurieren, dass es bei Fehlern Exceptions wirft. Dies geschieht mit &amp;lt;code&amp;gt;mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);&amp;lt;/code&amp;gt;. Danach können Datenbankfehler elegant mit &amp;lt;code&amp;gt;try...catch&amp;lt;/code&amp;gt;-Blöcken behandelt werden, ähnlich wie bei PDO.[18, 20, 21, 22, 23, 24, 25, 26]&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Verbindung schließen:&amp;#039;&amp;#039;&amp;#039; Prozedural mit &amp;lt;code&amp;gt;mysqli_close($link)&amp;lt;/code&amp;gt;, objektorientiert mit &amp;lt;code&amp;gt;$mysqli-&amp;amp;gt;close()&amp;lt;/code&amp;gt;.[17, 18, 19]&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Persistente Verbindungen:&amp;#039;&amp;#039;&amp;#039; MySQLi unterstützt ebenfalls persistente Verbindungen, die über PHP-Konfigurationseinstellungen (&amp;lt;code&amp;gt;mysqli.allow_persistent&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;mysqli.max_persistent&amp;lt;/code&amp;gt;) gesteuert werden.[27] Standardmäßig setzt MySQLi den Zustand einer wiederverwendeten persistenten Verbindung zurück (via &amp;lt;code&amp;gt;mysqli::change_user()&amp;lt;/code&amp;gt;), was zwar für Konsistenz sorgt, aber auch Performance kosten kann.[27]&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Code-Beispiele (MySQLi-Verbindungen):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Objektorientiert (mit Exception-Handling):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!--?php$host = &amp;#039;localhost&amp;#039;;&lt;br /&gt;
$user = &amp;#039;mein_benutzer&amp;#039;;&lt;br /&gt;
$pass = &amp;#039;mein_passwort&amp;#039;;&lt;br /&gt;
$db = &amp;#039;meine_datenbank&amp;#039;;&lt;br /&gt;
&lt;br /&gt;
// Fehlerberichterstattung für Exceptions aktivieren&lt;br /&gt;
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);&lt;br /&gt;
&lt;br /&gt;
try {&lt;br /&gt;
    $mysqli = new mysqli($host, $user, $pass, $db);&lt;br /&gt;
    // Zeichensatz setzen (wichtig!)&lt;br /&gt;
    $mysqli---&amp;gt;set_charset(&amp;#039;utf8mb4&amp;#039;);&lt;br /&gt;
 // Erfolgreich verbunden&lt;br /&gt;
 // echo &amp;quot;Verbindung erfolgreich (OO)! Host-Info: &amp;quot;. $mysqli-&amp;amp;gt;host_info;&lt;br /&gt;
} catch (mysqli_sql_exception $e) {&lt;br /&gt;
 // Fehler beim Verbindungsaufbau oder set_charset&lt;br /&gt;
 error_log(&amp;quot;MySQLi Verbindungsfehler: &amp;quot;. $e-&amp;amp;gt;getMessage());&lt;br /&gt;
 die(&amp;quot;Es gab ein Problem mit der Datenbankverbindung.&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//... Datenbankoperationen mit $mysqli...&lt;br /&gt;
&lt;br /&gt;
// Verbindung schließen&lt;br /&gt;
// $mysqli-&amp;amp;gt;close();&lt;br /&gt;
?&amp;amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Prozedural (mit Exception-Handling):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!--?php$host = &amp;#039;localhost&amp;#039;;&lt;br /&gt;
$user = &amp;#039;mein_benutzer&amp;#039;;&lt;br /&gt;
$pass = &amp;#039;mein_passwort&amp;#039;;&lt;br /&gt;
$db = &amp;#039;meine_datenbank&amp;#039;;&lt;br /&gt;
&lt;br /&gt;
// Fehlerberichterstattung für Exceptions aktivieren&lt;br /&gt;
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);&lt;br /&gt;
&lt;br /&gt;
try {&lt;br /&gt;
    $link = mysqli_connect($host, $user, $pass, $db);&lt;br /&gt;
    // Zeichensatz setzen&lt;br /&gt;
    mysqli_set_charset($link, &amp;#039;utf8mb4&amp;#039;);&lt;br /&gt;
    // Erfolgreich verbunden&lt;br /&gt;
    // echo &amp;quot;Verbindung erfolgreich (Prozedural)! Host-Info: &amp;quot;. mysqli_get_host_info($link);&lt;br /&gt;
} catch (mysqli_sql_exception $e) {&lt;br /&gt;
    // Fehler beim Verbindungsaufbau oder set_charset&lt;br /&gt;
    error_log(&amp;quot;MySQLi Verbindungsfehler: &amp;quot;. $e---&amp;gt;getMessage());&lt;br /&gt;
 die(&amp;quot;Es gab ein Problem mit der Datenbankverbindung.&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//... Datenbankoperationen mit $link...&lt;br /&gt;
&lt;br /&gt;
// Verbindung schließen&lt;br /&gt;
// mysqli_close($link);&lt;br /&gt;
?&amp;amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Bedeutung von &amp;lt;code&amp;gt;mysqlnd&amp;lt;/code&amp;gt;:&amp;#039;&amp;#039;&amp;#039; Die Benutzerfreundlichkeit, insbesondere bei der Arbeit mit Prepared Statements, wird stark durch den verwendeten MySQL-Treiber beeinflusst. Der &amp;#039;&amp;#039;&amp;#039;MySQL Native Driver (&amp;lt;code&amp;gt;mysqlnd&amp;lt;/code&amp;gt;)&amp;#039;&amp;#039;&amp;#039;, der seit PHP 5.4 standardmäßig enthalten ist [16], ist hier entscheidend. &amp;lt;code&amp;gt;mysqlnd&amp;lt;/code&amp;gt; stellt die Methode &amp;lt;code&amp;gt;get_result()&amp;lt;/code&amp;gt; (bzw. die Funktion &amp;lt;code&amp;gt;mysqli_stmt_get_result()&amp;lt;/code&amp;gt;) zur Verfügung.[25, 15] Diese Methode erlaubt es, nach dem Ausführen eines Prepared Statements ein Standard-&amp;lt;code&amp;gt;mysqli_result&amp;lt;/code&amp;gt;-Objekt zu erhalten, von dem dann wie gewohnt mit &amp;lt;code&amp;gt;fetch_assoc()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;fetch_all()&amp;lt;/code&amp;gt; etc. die Ergebnisse abgerufen werden können.[25] Ohne &amp;lt;code&amp;gt;mysqlnd&amp;lt;/code&amp;gt; (bei Verwendung der älteren &amp;lt;code&amp;gt;libmysqlclient&amp;lt;/code&amp;gt;-Bibliothek) ist das Abrufen von Ergebnissen aus Prepared Statements deutlich umständlicher und erfordert die manuelle Bindung jeder einzelnen Ergebnisspalte an PHP-Variablen mittels &amp;lt;code&amp;gt;bind_result()&amp;lt;/code&amp;gt; und anschließendes &amp;lt;code&amp;gt;fetch()&amp;lt;/code&amp;gt;.[25] Da &amp;lt;code&amp;gt;mysqlnd&amp;lt;/code&amp;gt; heute der Standard ist, konzentrieren sich moderne Tutorials und Beispiele meist auf die &amp;lt;code&amp;gt;get_result()&amp;lt;/code&amp;gt;-Methode, aber es ist wichtig, diesen Hintergrund zu kennen, falls man auf ältere Systeme oder Konfigurationen trifft.&lt;br /&gt;
&lt;br /&gt;
=== 2.3 Veraltete &amp;lt;code&amp;gt;mysql_*&amp;lt;/code&amp;gt; Funktionen ===&lt;br /&gt;
&lt;br /&gt;
Die &amp;lt;code&amp;gt;mysql_*&amp;lt;/code&amp;gt; Funktionsfamilie (&amp;lt;code&amp;gt;mysql_connect&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;mysql_query&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;mysql_fetch_assoc&amp;lt;/code&amp;gt; etc.) war die ursprüngliche Methode, um in PHP mit MySQL-Datenbanken zu interagieren. Diese Erweiterung ist jedoch &amp;#039;&amp;#039;&amp;#039;stark veraltet&amp;#039;&amp;#039;&amp;#039; und sollte &amp;#039;&amp;#039;&amp;#039;unter keinen Umständen mehr für neue Projekte verwendet werden&amp;#039;&amp;#039;&amp;#039;.&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Status:&amp;#039;&amp;#039;&amp;#039; Die &amp;lt;code&amp;gt;mysql_*&amp;lt;/code&amp;gt; Erweiterung wurde in &amp;#039;&amp;#039;&amp;#039;PHP 5.5&amp;#039;&amp;#039;&amp;#039; offiziell als &amp;quot;deprecated&amp;quot; (veraltet) markiert, was bedeutet, dass ihre Verwendung ab dieser Version Warnungen (&amp;lt;code&amp;gt;E_DEPRECATED&amp;lt;/code&amp;gt;) erzeugte.[28, 29] Mit der Veröffentlichung von &amp;#039;&amp;#039;&amp;#039;PHP 7.0&amp;#039;&amp;#039;&amp;#039; wurde die Erweiterung &amp;#039;&amp;#039;&amp;#039;vollständig entfernt&amp;#039;&amp;#039;&amp;#039;.[28, 29] PHP-Code, der diese Funktionen nutzt, führt auf PHP 7.0 oder neuer zu fatalen Fehlern und funktioniert nicht mehr.[28, 29]&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Gründe für die Entfernung:&amp;#039;&amp;#039;&amp;#039; Es gab zwingende Gründe für diesen Schritt:&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Gravierende Sicherheitsrisiken:&amp;#039;&amp;#039;&amp;#039; Der Hauptgrund war die Anfälligkeit für SQL-Injection-Angriffe. Die &amp;lt;code&amp;gt;mysql_*&amp;lt;/code&amp;gt; Funktionen boten keinen eingebauten Mechanismus wie Prepared Statements. Entwickler mussten Benutzereingaben manuell mit &amp;lt;code&amp;gt;mysql_real_escape_string()&amp;lt;/code&amp;gt; &amp;quot;escapen&amp;quot;, eine Methode, die leicht vergessen oder falsch angewendet werden konnte, was zu weit verbreiteten Sicherheitslücken führte.[29, 30] Das Fehlen von Prepared Statements in dieser alten Erweiterung ist direkt ursächlich für die hohe Zahl an SQL-Injection-Schwachstellen in älteren PHP-Anwendungen.[29]&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Fehlende Unterstützung moderner MySQL-Features:&amp;#039;&amp;#039;&amp;#039; Die Erweiterung wurde ursprünglich für MySQL Version 3.23 entwickelt und seitdem kaum weiterentwickelt.[29] Sie unterstützte viele wichtige Funktionen moderner MySQL-Versionen nicht, darunter Prepared Statements, Stored Procedures, Transaktionen, SSL-Verschlüsselung, Kompression, Multi-Statements und vollständige Zeichensatzunterstützung.[29]&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Schlechte Wartbarkeit:&amp;#039;&amp;#039;&amp;#039; Der Code der Erweiterung war veraltet und wurde immer schwieriger zu warten und an neue Versionen der MySQL-Client-Bibliotheken anzupassen.[29]&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Förderung unsicherer Praktiken:&amp;#039;&amp;#039;&amp;#039; Da viele alte Tutorials und Codebeispiele die &amp;lt;code&amp;gt;mysql_*&amp;lt;/code&amp;gt; Funktionen unsicher verwendeten, trug die fortgesetzte Verfügbarkeit zur Verbreitung schlechter und unsicherer Programmierpraktiken bei.[29]&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Verwendung heute:&amp;#039;&amp;#039;&amp;#039; Die klare Antwort lautet: &amp;#039;&amp;#039;&amp;#039;Nein&amp;#039;&amp;#039;&amp;#039;. Diese Funktionen existieren in modernen PHP-Versionen nicht mehr.[31, 30] Jede Anwendung, die sie noch verwendet, ist nicht nur unsicher, sondern auch inkompatibel mit aktueller PHP-Software. Eine Migration zu PDO oder MySQLi ist unumgänglich, um Sicherheit und Kompatibilität zu gewährleisten.[28, 29] Die erzwungene Migration durch die Entfernung in PHP 7.0 unterstreicht, wie wichtig es ist, Deprecation-Warnungen ernst zu nehmen und Code proaktiv zu aktualisieren, um zukünftige Probleme zu vermeiden.[29, 32]&lt;br /&gt;
&lt;br /&gt;
=== Tabelle 1: Vergleich der PHP-Datenbankerweiterungen für MySQL ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Merkmal !! PDO (PHP Data Objects) !! MySQLi (MySQL Improved) !! &amp;lt;code&amp;gt;mysql_*&amp;lt;/code&amp;gt; (Veraltet)&lt;br /&gt;
|-&amp;lt;br&amp;gt;&lt;br /&gt;
| aktiv=&amp;quot;&amp;quot; empfohlen=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| aktiv=&amp;quot;&amp;quot; empfohlen=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Entfernt (seit PHP 7.0) [29]&lt;br /&gt;
|-&amp;lt;br&amp;gt;&lt;br /&gt;
| objektorientiert=&amp;quot;&amp;quot; oo=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| oo=&amp;quot;&amp;quot; prozedural=&amp;quot;&amp;quot; 17=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Prozedural&lt;br /&gt;
|-&amp;lt;br&amp;gt;&lt;br /&gt;
| multi-datenbank=&amp;quot;&amp;quot; mysql=&amp;quot;&amp;quot; pgsql=&amp;quot;&amp;quot; sqlite=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| nur=&amp;quot;&amp;quot; mysql=&amp;quot;&amp;quot; 15=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Nur MySQL&lt;br /&gt;
|-&amp;lt;br&amp;gt;&lt;br /&gt;
| ja=&amp;quot;&amp;quot; nativ=&amp;quot;&amp;quot; empfohlen=&amp;quot;&amp;quot; emulation=&amp;quot;&amp;quot; m=&amp;quot;&amp;quot; glich=&amp;quot;&amp;quot; 1=&amp;quot;&amp;quot; 11=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| ja=&amp;quot;&amp;quot; nativ=&amp;quot;&amp;quot; 25=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Nein [29]&lt;br /&gt;
|- &lt;br /&gt;
| &amp;#039;&amp;#039;&amp;#039;SQL Injection Schutz&amp;#039;&amp;#039;&amp;#039; || sehr=&amp;quot;&amp;quot; gut=&amp;quot;&amp;quot; durch=&amp;quot;&amp;quot; prepared=&amp;quot;&amp;quot; statements=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Sehr gut (durch Prepared Statements) [25] || Schlecht (manuelles Escaping nötig) [29]&lt;br /&gt;
|-&amp;lt;br&amp;gt;&lt;br /&gt;
| gute=&amp;quot;&amp;quot; unterst=&amp;quot;&amp;quot; tzung=&amp;quot;&amp;quot; via=&amp;quot;&amp;quot; treiber=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Sehr gute Unterstützung (spezifisch) [15] || Veraltet, viele Features fehlen [29]&lt;br /&gt;
|-&amp;lt;br&amp;gt;&lt;br /&gt;
| hoch=&amp;quot;&amp;quot; 2=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| niedrig=&amp;quot;&amp;quot; mysql-spezifisch=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Niedrig&lt;br /&gt;
|-&amp;lt;br&amp;gt;&lt;br /&gt;
| exceptions=&amp;quot;&amp;quot; mwt-preservehtml=&amp;quot;&amp;quot; pdoexception=&amp;quot;&amp;quot; code=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| exceptions=&amp;quot;&amp;quot; mit=&amp;quot;&amp;quot; mwt-preservehtml=&amp;quot;&amp;quot; mysqli_report=&amp;quot;&amp;quot; code=&amp;quot;&amp;quot; fehlercodes=&amp;quot;&amp;quot; 18=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Fehlercodes / &amp;lt;code&amp;gt;mysql_error()&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
| &amp;#039;&amp;#039;&amp;#039;Nicht verwenden!&amp;#039;&amp;#039;&amp;#039; [29]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== (3) Grundlegende Datenbankoperationen (CRUD) ==&lt;br /&gt;
&lt;br /&gt;
Nachdem wir die verschiedenen Erweiterungen kennengelernt haben, wenden wir uns nun den grundlegenden Operationen zu, die man typischerweise mit einer Datenbank durchführt. Diese werden oft mit dem Akronym CRUD zusammengefasst:&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;C&amp;#039;&amp;#039;&amp;#039;reate: Neue Datensätze erstellen (SQL: &amp;lt;code&amp;gt;INSERT&amp;lt;/code&amp;gt;).&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;R&amp;#039;&amp;#039;&amp;#039;ead: Vorhandene Datensätze lesen/abfragen (SQL: &amp;lt;code&amp;gt;SELECT&amp;lt;/code&amp;gt;).&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;U&amp;#039;&amp;#039;&amp;#039;pdate: Bestehende Datensätze ändern (SQL: &amp;lt;code&amp;gt;UPDATE&amp;lt;/code&amp;gt;).&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;D&amp;#039;&amp;#039;&amp;#039;elete: Datensätze löschen (SQL: &amp;lt;code&amp;gt;DELETE&amp;lt;/code&amp;gt;).&lt;br /&gt;
In den folgenden Abschnitten werden wir sehen, wie diese Operationen mit den modernen Erweiterungen PDO und MySQLi umgesetzt werden, wobei wir durchgehend &amp;#039;&amp;#039;&amp;#039;Prepared Statements&amp;#039;&amp;#039;&amp;#039; verwenden, um die Sicherheit zu gewährleisten.&lt;br /&gt;
&lt;br /&gt;
=== 3.1 Daten abfragen (SELECT) ===&lt;br /&gt;
&lt;br /&gt;
Das Abfragen von Daten ist eine der häufigsten Datenbankoperationen.&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;PDO:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Ohne Parameter:&amp;#039;&amp;#039;&amp;#039; Wenn die Abfrage keine variablen Teile enthält (z.B. keine &amp;lt;code&amp;gt;WHERE&amp;lt;/code&amp;gt;-Klausel mit Benutzereingaben), kann die &amp;lt;code&amp;gt;query()&amp;lt;/code&amp;gt;-Methode verwendet werden. Sie führt die SQL-Abfrage direkt aus und gibt ein &amp;lt;code&amp;gt;PDOStatement&amp;lt;/code&amp;gt;-Objekt zurück, das zur Ergebnisauswertung dient.[2, 33]&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Beispiel: Alle Benutzer auswählen&lt;br /&gt;
$stmt = $pdo-&amp;amp;gt;query(&amp;quot;SELECT id, username FROM users&amp;quot;);&lt;br /&gt;
// $stmt kann nun zum Fetchen der Ergebnisse verwendet werden&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Mit Parametern (Prepared Statement):&amp;#039;&amp;#039;&amp;#039; Dies ist der Standardfall, wenn Benutzereingaben oder andere Variablen die Abfrage beeinflussen (z.B. in &amp;lt;code&amp;gt;WHERE&amp;lt;/code&amp;gt;- oder &amp;lt;code&amp;gt;LIMIT&amp;lt;/code&amp;gt;-Klauseln).&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Vorbereiten (&amp;lt;code&amp;gt;prepare&amp;lt;/code&amp;gt;):&amp;#039;&amp;#039;&amp;#039; Die SQL-Abfrage wird mit Platzhaltern (&amp;lt;code&amp;gt;?&amp;lt;/code&amp;gt; für positionale oder &amp;lt;code&amp;gt;:name&amp;lt;/code&amp;gt; für benannte Platzhalter) vorbereitet.[34, 33, 5]&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Beispiel: Benutzer mit bestimmter ID auswählen (positionaler Platzhalter)&lt;br /&gt;
$sql = &amp;quot;SELECT id, username, email FROM users WHERE id =?&amp;quot;;&lt;br /&gt;
$stmt = $pdo-&amp;amp;gt;prepare($sql);&lt;br /&gt;
&lt;br /&gt;
// Beispiel: Benutzer mit bestimmtem Status auswählen (benannter Platzhalter)&lt;br /&gt;
$sql_named = &amp;quot;SELECT id, username FROM users WHERE status = :status&amp;quot;;&lt;br /&gt;
$stmt_named = $pdo-&amp;amp;gt;prepare($sql_named);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
*# &amp;#039;&amp;#039;&amp;#039;Ausführen (&amp;lt;code&amp;gt;execute&amp;lt;/code&amp;gt;):&amp;#039;&amp;#039;&amp;#039; Die vorbereitete Anweisung wird mit den tatsächlichen Werten ausgeführt. Die Werte werden als Array an die &amp;lt;code&amp;gt;execute()&amp;lt;/code&amp;gt;-Methode übergeben. Bei positionalen Platzhaltern entspricht die Reihenfolge im Array der Reihenfolge der &amp;lt;code&amp;gt;?&amp;lt;/code&amp;gt; in der SQL-Abfrage. Bei benannten Platzhaltern wird ein assoziatives Array verwendet, dessen Schlüssel den Platzhalternamen entsprechen (mit oder ohne führenden Doppelpunkt).[34, 33, 5]&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Ausführen für positionalen Platzhalter&lt;br /&gt;
$user_id = 123;&lt;br /&gt;
$stmt-&amp;amp;gt;execute([$user_id]);&lt;br /&gt;
&lt;br /&gt;
// Ausführen für benannten Platzhalter&lt;br /&gt;
$user_status = &amp;#039;active&amp;#039;;&lt;br /&gt;
$stmt_named-&amp;amp;gt;execute([&amp;#039;:status&amp;#039; =&amp;amp;gt; $user_status]);&lt;br /&gt;
// oder: $stmt_named-&amp;amp;gt;execute([&amp;#039;status&amp;#039; =&amp;amp;gt; $user_status]);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
*# &amp;#039;&amp;#039;&amp;#039;Alternative Bindung:&amp;#039;&amp;#039;&amp;#039; Optional können Parameter auch explizit vor dem &amp;lt;code&amp;gt;execute()&amp;lt;/code&amp;gt;-Aufruf mit &amp;lt;code&amp;gt;bindValue()&amp;lt;/code&amp;gt; (bindet einen Wert) oder &amp;lt;code&amp;gt;bindParam()&amp;lt;/code&amp;gt; (bindet eine Variable als Referenz) gebunden werden. Dies gibt mehr Kontrolle über den Datentyp (z.B. &amp;lt;code&amp;gt;PDO::PARAM_INT&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;PDO::PARAM_STR&amp;lt;/code&amp;gt;).[5, 13]&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$stmt-&amp;amp;gt;bindValue(1, $user_id, PDO::PARAM_INT); // Position 1, Wert von $user_id als Integer&lt;br /&gt;
$stmt-&amp;amp;gt;execute();&lt;br /&gt;
&lt;br /&gt;
$stmt_named-&amp;amp;gt;bindParam(&amp;#039;:status&amp;#039;, $user_status, PDO::PARAM_STR); // Platzhalter :status, Variable $user_status als String&lt;br /&gt;
$stmt_named-&amp;amp;gt;execute();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;MySQLi:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Ohne Parameter:&amp;#039;&amp;#039;&amp;#039; Ähnlich wie bei PDO kann bei Abfragen ohne variable Teile die &amp;lt;code&amp;gt;query()&amp;lt;/code&amp;gt;-Methode (OO-Stil: &amp;lt;code&amp;gt;$mysqli-&amp;amp;gt;query()&amp;lt;/code&amp;gt;) oder die &amp;lt;code&amp;gt;mysqli_query()&amp;lt;/code&amp;gt;-Funktion (prozeduraler Stil: &amp;lt;code&amp;gt;mysqli_query($link, &amp;quot;...&amp;quot;)&amp;lt;/code&amp;gt;) verwendet werden. Sie geben ein &amp;lt;code&amp;gt;mysqli_result&amp;lt;/code&amp;gt;-Objekt zurück.[20, 25]&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// OO-Stil&lt;br /&gt;
$result = $mysqli-&amp;amp;gt;query(&amp;quot;SELECT id, username FROM users&amp;quot;);&lt;br /&gt;
// Prozedural&lt;br /&gt;
$result = mysqli_query($link, &amp;quot;SELECT id, username FROM users&amp;quot;);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Mit Parametern (Prepared Statement):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
** &amp;#039;&amp;#039;&amp;#039;Vorbereiten (&amp;lt;code&amp;gt;prepare&amp;lt;/code&amp;gt;):&amp;#039;&amp;#039;&amp;#039; Die SQL-Abfrage wird mit &amp;lt;code&amp;gt;?&amp;lt;/code&amp;gt;-Platzhaltern vorbereitet. Im OO-Stil &amp;lt;code&amp;gt;$stmt = $mysqli-&amp;amp;gt;prepare(&amp;quot;...&amp;quot;)&amp;lt;/code&amp;gt;, prozedural &amp;lt;code&amp;gt;$stmt = mysqli_prepare($link, &amp;quot;...&amp;quot;)&amp;lt;/code&amp;gt;.[25, 35]&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// OO-Stil&lt;br /&gt;
$sql = &amp;quot;SELECT id, username, email FROM users WHERE id =?&amp;quot;;&lt;br /&gt;
$stmt = $mysqli-&amp;amp;gt;prepare($sql);&lt;br /&gt;
// Prozedural&lt;br /&gt;
$sql_proc = &amp;quot;SELECT id, username, email FROM users WHERE id =?&amp;quot;;&lt;br /&gt;
$stmt_proc = mysqli_prepare($link, $sql_proc);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
**# &amp;#039;&amp;#039;&amp;#039;Parameter binden (&amp;lt;code&amp;gt;bind_param&amp;lt;/code&amp;gt;):&amp;#039;&amp;#039;&amp;#039; Die Variablen werden an die Platzhalter gebunden. Dies ist ein wesentlicher Unterschied zu PDOs &amp;lt;code&amp;gt;execute&amp;lt;/code&amp;gt;-Array-Bindung. &amp;lt;code&amp;gt;bind_param()&amp;lt;/code&amp;gt; erfordert einen String, der die Datentypen der zu bindenden Variablen angibt (z.B. &amp;quot;i&amp;quot; für Integer, &amp;quot;s&amp;quot; für String, &amp;quot;d&amp;quot; für Double/Float, &amp;quot;b&amp;quot; für Blob), gefolgt von den Variablen selbst.[25, 35]&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$user_id = 123;&lt;br /&gt;
// OO-Stil&lt;br /&gt;
$stmt-&amp;amp;gt;bind_param(&amp;quot;i&amp;quot;, $user_id); // &amp;quot;i&amp;quot; für Integer&lt;br /&gt;
// Prozedural&lt;br /&gt;
mysqli_stmt_bind_param($stmt_proc, &amp;quot;i&amp;quot;, $user_id);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
**# &amp;#039;&amp;#039;&amp;#039;Ausführen (&amp;lt;code&amp;gt;execute&amp;lt;/code&amp;gt;):&amp;#039;&amp;#039;&amp;#039; Die vorbereitete Anweisung wird ausgeführt. OO-Stil: &amp;lt;code&amp;gt;$stmt-&amp;amp;gt;execute()&amp;lt;/code&amp;gt;, prozedural: &amp;lt;code&amp;gt;mysqli_stmt_execute($stmt)&amp;lt;/code&amp;gt;.[25, 36, 35]&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// OO-Stil&lt;br /&gt;
$stmt-&amp;amp;gt;execute();&lt;br /&gt;
// Prozedural&lt;br /&gt;
mysqli_stmt_execute($stmt_proc);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
**# &amp;#039;&amp;#039;&amp;#039;Ergebnis holen:&amp;#039;&amp;#039;&amp;#039; Um die Ergebnisse eines Prepared Statements in MySQLi zu erhalten, ist die Methode &amp;lt;code&amp;gt;get_result()&amp;lt;/code&amp;gt; (OO) bzw. &amp;lt;code&amp;gt;mysqli_stmt_get_result()&amp;lt;/code&amp;gt; (prozedural) am bequemsten. Sie gibt ein Standard-&amp;lt;code&amp;gt;mysqli_result&amp;lt;/code&amp;gt;-Objekt zurück, das dann mit den üblichen Fetch-Methoden verarbeitet werden kann. &amp;#039;&amp;#039;&amp;#039;Wichtig:&amp;#039;&amp;#039;&amp;#039; Diese Methode erfordert den &amp;lt;code&amp;gt;mysqlnd&amp;lt;/code&amp;gt;-Treiber.[25, 15]&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// OO-Stil&lt;br /&gt;
$result = $stmt-&amp;amp;gt;get_result();&lt;br /&gt;
// Prozedural&lt;br /&gt;
$result_proc = mysqli_stmt_get_result($stmt_proc);&lt;br /&gt;
// $result / $result_proc kann nun mit fetch_assoc() etc. verwendet werden.&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Die Alternative ohne &amp;lt;code&amp;gt;mysqlnd&amp;lt;/code&amp;gt; ist &amp;lt;code&amp;gt;bind_result()&amp;lt;/code&amp;gt;, bei der jede Ergebnisspalte an eine PHP-Variable gebunden wird, und &amp;lt;code&amp;gt;fetch()&amp;lt;/code&amp;gt; iterativ aufgerufen wird, um die Variablen zu füllen.[25] Dies ist deutlich umständlicher.&lt;br /&gt;
&lt;br /&gt;
=== 3.2 Ergebnisse abrufen und verarbeiten ===&lt;br /&gt;
&lt;br /&gt;
Nachdem eine &amp;lt;code&amp;gt;SELECT&amp;lt;/code&amp;gt;-Abfrage erfolgreich ausgeführt wurde, müssen die zurückgegebenen Daten aus dem Ergebnisobjekt (&amp;lt;code&amp;gt;PDOStatement&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;mysqli_result&amp;lt;/code&amp;gt;) abgerufen (&amp;quot;gefetched&amp;quot;) werden.&lt;br /&gt;
&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Zeilenweise Iteration (&amp;lt;code&amp;gt;fetch&amp;lt;/code&amp;gt; im Loop):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
Dies ist der klassische Weg, um Ergebnisse zu verarbeiten, insbesondere wenn jede Zeile einzeln behandelt werden muss oder wenn die Ergebnismenge sehr groß ist.&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;PDO:&amp;#039;&amp;#039;&amp;#039; Die &amp;lt;code&amp;gt;fetch()&amp;lt;/code&amp;gt;-Methode des &amp;lt;code&amp;gt;PDOStatement&amp;lt;/code&amp;gt;-Objekts wird wiederholt in einer &amp;lt;code&amp;gt;while&amp;lt;/code&amp;gt;-Schleife aufgerufen. &amp;lt;code&amp;gt;fetch()&amp;lt;/code&amp;gt; gibt bei jedem Aufruf die nächste Zeile zurück (im Format des eingestellten Fetch-Modus, z.B. &amp;lt;code&amp;gt;PDO::FETCH_ASSOC&amp;lt;/code&amp;gt;) und &amp;lt;code&amp;gt;false&amp;lt;/code&amp;gt; (oder &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt; je nach Modus/Version), wenn keine weiteren Zeilen vorhanden sind.[2, 33]&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$stmt = $pdo-&amp;amp;gt;prepare(&amp;quot;SELECT name, email FROM users WHERE status =?&amp;quot;);&lt;br /&gt;
$stmt-&amp;amp;gt;execute([&amp;#039;active&amp;#039;]);&lt;br /&gt;
while ($row = $stmt-&amp;amp;gt;fetch(PDO::FETCH_ASSOC)) {&lt;br /&gt;
 echo &amp;quot;Name: &amp;quot;. htmlspecialchars($row[&amp;#039;name&amp;#039;]). &amp;quot;, E-Mail: &amp;quot;. htmlspecialchars($row[&amp;#039;email&amp;#039;]). &amp;quot;&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;MySQLi:&amp;#039;&amp;#039;&amp;#039; Die &amp;lt;code&amp;gt;fetch_assoc()&amp;lt;/code&amp;gt;-Methode (oder &amp;lt;code&amp;gt;fetch_row()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;fetch_object()&amp;lt;/code&amp;gt;, etc.) des &amp;lt;code&amp;gt;mysqli_result&amp;lt;/code&amp;gt;-Objekts wird in einer &amp;lt;code&amp;gt;while&amp;lt;/code&amp;gt;-Schleife verwendet. Sie gibt die nächste Zeile als Array (oder Objekt) zurück oder &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt;, wenn das Ende erreicht ist.[25, 15]&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// OO-Stil (nach $result = $stmt-&amp;amp;gt;get_result();)&lt;br /&gt;
while ($row = $result-&amp;amp;gt;fetch_assoc()) {&lt;br /&gt;
 echo &amp;quot;Name: &amp;quot;. htmlspecialchars($row[&amp;#039;name&amp;#039;]). &amp;quot;, E-Mail: &amp;quot;. htmlspecialchars($row[&amp;#039;email&amp;#039;]). &amp;quot;&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Prozedural (nach $result = mysqli_stmt_get_result($stmt);)&lt;br /&gt;
while ($row = mysqli_fetch_assoc($result)) {&lt;br /&gt;
 echo &amp;quot;Name: &amp;quot;. htmlspecialchars($row[&amp;#039;name&amp;#039;]). &amp;quot;, E-Mail: &amp;quot;. htmlspecialchars($row[&amp;#039;email&amp;#039;]). &amp;quot;&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Dieser Ansatz ist &amp;#039;&amp;#039;&amp;#039;speichereffizient&amp;#039;&amp;#039;&amp;#039;, da immer nur eine Zeile gleichzeitig im PHP-Speicher gehalten wird. Dies ist besonders wichtig bei Abfragen, die potenziell Tausende oder Millionen von Zeilen zurückgeben könnten.[37]&lt;br /&gt;
&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Alle Ergebnisse auf einmal abrufen (&amp;lt;code&amp;gt;fetchAll&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;fetch_all&amp;lt;/code&amp;gt;):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
Für kleinere bis mittlere Ergebnismengen ist es oft bequemer, alle Zeilen auf einmal in ein PHP-Array zu laden.&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;PDO:&amp;#039;&amp;#039;&amp;#039; Die &amp;lt;code&amp;gt;fetchAll()&amp;lt;/code&amp;gt;-Methode des &amp;lt;code&amp;gt;PDOStatement&amp;lt;/code&amp;gt;-Objekts gibt ein Array zurück, das alle Ergebniszeilen enthält.[33, 38] Der Fetch-Modus (z.B. &amp;lt;code&amp;gt;PDO::FETCH_ASSOC&amp;lt;/code&amp;gt;) kann als Argument übergeben werden.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$stmt = $pdo-&amp;amp;gt;prepare(&amp;quot;SELECT name, email FROM users WHERE status =?&amp;quot;);&lt;br /&gt;
$stmt-&amp;amp;gt;execute([&amp;#039;active&amp;#039;]);&lt;br /&gt;
$results = $stmt-&amp;amp;gt;fetchAll(PDO::FETCH_ASSOC);&lt;br /&gt;
// $results ist nun ein Array von assoziativen Arrays&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;MySQLi:&amp;#039;&amp;#039;&amp;#039; Die &amp;lt;code&amp;gt;fetch_all()&amp;lt;/code&amp;gt;-Methode des &amp;lt;code&amp;gt;mysqli_result&amp;lt;/code&amp;gt;-Objekts (verfügbar mit &amp;lt;code&amp;gt;mysqlnd&amp;lt;/code&amp;gt;) tut dasselbe. Der Modus (&amp;lt;code&amp;gt;MYSQLI_ASSOC&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;MYSQLI_NUM&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;MYSQLI_BOTH&amp;lt;/code&amp;gt;) wird als Argument übergeben.[25, 39]&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// OO-Stil (nach $result = $stmt-&amp;amp;gt;get_result();)&lt;br /&gt;
$results = $result-&amp;amp;gt;fetch_all(MYSQLI_ASSOC);&lt;br /&gt;
&lt;br /&gt;
// Prozedural (nach $result = mysqli_stmt_get_result($stmt);)&lt;br /&gt;
$results = mysqli_fetch_all($result, MYSQLI_ASSOC);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Der &amp;#039;&amp;#039;&amp;#039;Vorteil&amp;#039;&amp;#039;&amp;#039; dieser Methode liegt in der einfacheren Weiterverarbeitung des Ergebnis-Arrays mit Standard-PHP-Array-Funktionen wie &amp;lt;code&amp;gt;foreach&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;count&amp;lt;/code&amp;gt;.[40] Der &amp;#039;&amp;#039;&amp;#039;Nachteil&amp;#039;&amp;#039;&amp;#039; ist der potenziell hohe Speicherverbrauch, da alle Daten gleichzeitig in den PHP-Speicher geladen werden. Bei sehr großen Ergebnismengen kann dies zu Speicherlimit-Fehlern führen.[37]&lt;br /&gt;
&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Fetch-Modi / Fetch-Funktionen (Struktur der Ergebniszeile):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
Beide Erweiterungen bieten verschiedene Möglichkeiten, die Struktur der abgerufenen Datenzeilen zu bestimmen:&lt;br /&gt;
&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;PDO Fetch-Modi (Auswahl):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
***** &amp;lt;code&amp;gt;PDO::FETCH_ASSOC&amp;lt;/code&amp;gt;: Gibt ein assoziatives Array zurück (&amp;lt;code&amp;gt;[&amp;#039;spaltenname&amp;#039; =&amp;amp;gt; &amp;#039;wert&amp;#039;,... ]&amp;lt;/code&amp;gt;).[9, 33, 41, 42] Sehr gebräuchlich.&lt;br /&gt;
***** &amp;lt;code&amp;gt;PDO::FETCH_NUM&amp;lt;/code&amp;gt;: Gibt ein numerisch indiziertes Array zurück (&amp;lt;code&amp;gt;[0 =&amp;amp;gt; &amp;#039;wert1&amp;#039;, 1 =&amp;amp;gt; &amp;#039;wert2&amp;#039;,...]&amp;lt;/code&amp;gt;).[9, 41, 42]&lt;br /&gt;
***** &amp;lt;code&amp;gt;PDO::FETCH_BOTH&amp;lt;/code&amp;gt; (Standard): Gibt ein Array zurück, das sowohl assoziative als auch numerische Indizes enthält.[41, 42] Verbraucht mehr Speicher.&lt;br /&gt;
***** &amp;lt;code&amp;gt;PDO::FETCH_OBJ&amp;lt;/code&amp;gt;: Gibt ein anonymes &amp;lt;code&amp;gt;stdClass&amp;lt;/code&amp;gt;-Objekt zurück, bei dem die Spaltennamen Eigenschaften sind (&amp;lt;code&amp;gt;$row-&amp;amp;gt;spaltenname&amp;lt;/code&amp;gt;).[9, 41, 42, 43]&lt;br /&gt;
***** &amp;lt;code&amp;gt;PDO::FETCH_CLASS&amp;lt;/code&amp;gt;: Erstellt eine Instanz einer angegebenen Klasse und weist die Spaltenwerte den Klasseneigenschaften zu.[38, 41, 42, 43]&lt;br /&gt;
***** &amp;lt;code&amp;gt;PDO::FETCH_COLUMN&amp;lt;/code&amp;gt;: Ruft nur den Wert einer einzelnen Spalte aus der nächsten Zeile ab.[38]&lt;br /&gt;
***** &amp;lt;code&amp;gt;PDO::FETCH_LAZY&amp;lt;/code&amp;gt;: Eine Kombination aus &amp;lt;code&amp;gt;FETCH_BOTH&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;FETCH_OBJ&amp;lt;/code&amp;gt;, die Werte erst bei Zugriff lädt.[9, 41, 42]&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;MySQLi Fetch-Funktionen (Auswahl):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
***** &amp;lt;code&amp;gt;fetch_assoc()&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;mysqli_fetch_assoc()&amp;lt;/code&amp;gt;: Gibt ein assoziatives Array zurück.[17, 25, 39, 15] Sehr gebräuchlich.&lt;br /&gt;
***** &amp;lt;code&amp;gt;fetch_row()&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;mysqli_fetch_row()&amp;lt;/code&amp;gt;: Gibt ein numerisch indiziertes Array zurück.[39, 35]&lt;br /&gt;
***** &amp;lt;code&amp;gt;fetch_array()&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;mysqli_fetch_array()&amp;lt;/code&amp;gt;: Gibt je nach übergebenem Modus (&amp;lt;code&amp;gt;MYSQLI_ASSOC&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;MYSQLI_NUM&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;MYSQLI_BOTH&amp;lt;/code&amp;gt;) ein assoziatives, numerisches oder beides enthaltendes Array zurück.[39, 35]&lt;br /&gt;
***** &amp;lt;code&amp;gt;fetch_object()&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;mysqli_fetch_object()&amp;lt;/code&amp;gt;: Gibt ein &amp;lt;code&amp;gt;stdClass&amp;lt;/code&amp;gt;-Objekt zurück oder eine Instanz einer angegebenen Klasse.[21, 15, 35]&lt;br /&gt;
***** &amp;lt;code&amp;gt;fetch_column()&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;mysqli_fetch_column()&amp;lt;/code&amp;gt;: Ruft den Wert einer einzelnen Spalte ab (erst ab PHP 8.1 verfügbar).[24]&lt;br /&gt;
***** &amp;lt;code&amp;gt;fetch_all()&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;mysqli_fetch_all()&amp;lt;/code&amp;gt;: Ruft alle Zeilen auf einmal ab, Modus (&amp;lt;code&amp;gt;MYSQLI_ASSOC&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;MYSQLI_NUM&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;MYSQLI_BOTH&amp;lt;/code&amp;gt;) wählbar.[21, 25, 39]&lt;br /&gt;
&lt;br /&gt;
=== Tabelle 2: Ausgewählte PDO Fetch-Modi ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Konstante !! Rückgabeformat !! Beschreibung&lt;br /&gt;
|- &lt;br /&gt;
| &amp;lt;code&amp;gt;PDO::FETCH_ASSOC&amp;lt;/code&amp;gt;&lt;br /&gt;
| assoziatives=&amp;quot;&amp;quot; array=&amp;quot;&amp;quot; mwt-preservehtml=&amp;quot;&amp;quot; | [&amp;quot; col&amp;#039;=&amp;quot;&amp;amp;gt;&amp;quot; &amp;#039;val&amp;#039;]&amp;amp;lt;=&amp;quot;&amp;quot; code&amp;amp;gt;&amp;#039;)=&amp;quot;&amp;quot; &amp;amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
| Gängigste Form, Spaltennamen als Schlüssel.&lt;br /&gt;
|- &lt;br /&gt;
| mwt-preservehtml=&amp;quot;&amp;quot; pdo::fetch_num=&amp;quot;&amp;quot; code=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| numerisches=&amp;quot;&amp;quot; array=&amp;quot;&amp;quot; mwt-preservehtml=&amp;quot;&amp;quot; 0=&amp;quot;&amp;quot; val=&amp;quot;&amp;quot; code=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Spaltenwerte über numerischen Index erreichbar.&lt;br /&gt;
|- &lt;br /&gt;
| mwt-preservehtml=&amp;quot;&amp;quot; pdo::fetch_both=&amp;quot;&amp;quot; code=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| array=&amp;quot;&amp;quot; mit=&amp;quot;&amp;quot; assoz=&amp;quot;&amp;quot; num=&amp;quot;&amp;quot; indizes=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Standard, enthält doppelte Informationen, höherer Speicherbedarf.&lt;br /&gt;
|- &lt;br /&gt;
| mwt-preservehtml=&amp;quot;&amp;quot; pdo::fetch_obj=&amp;quot;&amp;quot; code=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| anonymes=&amp;quot;&amp;quot; mwt-preservehtml=&amp;quot;&amp;quot; stdclass=&amp;quot;&amp;quot; code=&amp;quot;&amp;quot; -objekt=&amp;quot;&amp;quot; row-=&amp;quot;&amp;quot; col=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Spaltennamen werden zu Objekteigenschaften.&lt;br /&gt;
|- &lt;br /&gt;
| &amp;lt;code&amp;gt;PDO::FETCH_CLASS&amp;lt;/code&amp;gt; || objekt=&amp;quot;&amp;quot; einer=&amp;quot;&amp;quot; spezifischen=&amp;quot;&amp;quot; klasse=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Mappt Spalten auf Eigenschaften einer vordefinierten Klasse.&lt;br /&gt;
|- &lt;br /&gt;
| &amp;lt;code&amp;gt;PDO::FETCH_COLUMN&amp;lt;/code&amp;gt; || einzelner=&amp;quot;&amp;quot; wert=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Gibt nur den Wert der ersten (oder angegebenen) Spalte der nächsten Zeile zurück.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Tabelle 3: Ausgewählte MySQLi Fetch-Funktionen ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Funktion (OO / Prozedural) !! Rückgabeformat !! Beschreibung&lt;br /&gt;
|- &lt;br /&gt;
| mwt-preservehtml=&amp;quot;&amp;quot; fetch_assoc=&amp;quot;&amp;quot; code=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| assoziatives=&amp;quot;&amp;quot; array=&amp;quot;&amp;quot; mwt-preservehtml=&amp;quot;&amp;quot; col=&amp;quot;&amp;quot; val=&amp;quot;&amp;quot; code=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Gängigste Form, Spaltennamen als Schlüssel.&lt;br /&gt;
|- &lt;br /&gt;
| mwt-preservehtml=&amp;quot;&amp;quot; fetch_row=&amp;quot;&amp;quot; code=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| numerisches=&amp;quot;&amp;quot; array=&amp;quot;&amp;quot; mwt-preservehtml=&amp;quot;&amp;quot; 0=&amp;quot;&amp;quot; val=&amp;quot;&amp;quot; code=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Spaltenwerte über numerischen Index erreichbar.&lt;br /&gt;
|- &lt;br /&gt;
| mwt-preservehtml=&amp;quot;&amp;quot; fetch_array=&amp;quot;&amp;quot; code=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| array=&amp;quot;&amp;quot; assoz=&amp;quot;&amp;quot; num=&amp;quot;&amp;quot; oder=&amp;quot;&amp;quot; beide=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Flexibel je nach Modus (&amp;lt;code&amp;gt;MYSQLI_ASSOC&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;MYSQLI_NUM&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;MYSQLI_BOTH&amp;lt;/code&amp;gt;).&lt;br /&gt;
|- &lt;br /&gt;
| &amp;lt;code&amp;gt;fetch_object()&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;mysqli_fetch_object()&amp;lt;/code&amp;gt;&lt;br /&gt;
| mwt-preservehtml=&amp;quot;&amp;quot; | stdClass&amp;quot; -objekt=&amp;quot;&amp;quot; oder=&amp;quot;&amp;quot; spezifische=&amp;quot;&amp;quot; klasse=&amp;quot;&amp;quot; &amp;amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
| Spaltennamen werden zu Objekteigenschaften.&lt;br /&gt;
|- &lt;br /&gt;
| mwt-preservehtml=&amp;quot;&amp;quot; fetch_all=&amp;quot;&amp;quot; code=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| array=&amp;quot;&amp;quot; von=&amp;quot;&amp;quot; arrays=&amp;quot;&amp;quot; assoz=&amp;quot;&amp;quot; num=&amp;quot;&amp;quot; oder=&amp;quot;&amp;quot; beide=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Holt alle Ergebniszeilen auf einmal (benötigt &amp;lt;code&amp;gt;mysqlnd&amp;lt;/code&amp;gt;).&lt;br /&gt;
|- &lt;br /&gt;
| &amp;lt;code&amp;gt;fetch_column()&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;mysqli_fetch_column()&amp;lt;/code&amp;gt; || einzelner=&amp;quot;&amp;quot; wert=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Gibt nur den Wert der ersten (oder angegebenen) Spalte zurück (PHP 8.1+).&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Zugriff auf Spaltenwerte:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
Der Zugriff auf die Daten innerhalb einer abgerufenen Zeile hängt vom gewählten Fetch-Modus ab:&lt;br /&gt;
**** Assoziatives Array: &amp;lt;code&amp;gt;$row[&amp;#039;spaltenname&amp;#039;]&amp;lt;/code&amp;gt;&lt;br /&gt;
**** Numerisches Array: &amp;lt;code&amp;gt;$row&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;$row[1]&amp;lt;/code&amp;gt;,...&lt;br /&gt;
**** Objekt: &amp;lt;code&amp;gt;$row-&amp;amp;gt;spaltenname&amp;lt;/code&amp;gt;&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Iteration mit &amp;lt;code&amp;gt;foreach&amp;lt;/code&amp;gt;:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
Nachdem Ergebnisse mit &amp;lt;code&amp;gt;fetchAll()&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;fetch_all()&amp;lt;/code&amp;gt; in ein Array geladen wurden, ist &amp;lt;code&amp;gt;foreach&amp;lt;/code&amp;gt; die natürliche Wahl zur Iteration [37, 40, 44]:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$results = $stmt-&amp;amp;gt;fetchAll(PDO::FETCH_ASSOC);&lt;br /&gt;
foreach ($results as $row) {&lt;br /&gt;
 echo &amp;quot;Name: &amp;quot;. htmlspecialchars($row[&amp;#039;name&amp;#039;]). &amp;quot;&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Seit PHP 8 bietet PDO eine modernere Alternative, die die Lesbarkeit von &amp;lt;code&amp;gt;foreach&amp;lt;/code&amp;gt; mit der Speichereffizienz von &amp;lt;code&amp;gt;fetch()&amp;lt;/code&amp;gt; kombiniert, indem direkt über das &amp;lt;code&amp;gt;PDOStatement&amp;lt;/code&amp;gt;-Objekt iteriert wird [37, 45]:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Nur PHP 8+ mit PDO&lt;br /&gt;
$stmt = $pdo-&amp;amp;gt;query(&amp;quot;SELECT name, email FROM users&amp;quot;);&lt;br /&gt;
foreach ($stmt as $row) { // PDOStatement ist direkt traversierbar&lt;br /&gt;
 echo &amp;quot;Name: &amp;quot;. htmlspecialchars($row[&amp;#039;name&amp;#039;]). &amp;quot;&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Diese Methode ist besonders elegant und effizient für große Ergebnismengen in modernen PHP-Versionen.&lt;br /&gt;
&lt;br /&gt;
=== 3.3 Daten manipulieren (INSERT, UPDATE, DELETE) ===&lt;br /&gt;
&lt;br /&gt;
Das Einfügen, Aktualisieren und Löschen von Daten sind kritische Operationen, da sie den Datenbestand verändern. Hier ist die Verwendung von &amp;#039;&amp;#039;&amp;#039;Prepared Statements zwingend erforderlich&amp;#039;&amp;#039;&amp;#039;, um die Integrität und Sicherheit der Datenbank zu gewährleisten, insbesondere wenn Benutzereingaben beteiligt sind.&lt;br /&gt;
&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;PDO:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;Vorbereiten (&amp;lt;code&amp;gt;prepare&amp;lt;/code&amp;gt;):&amp;#039;&amp;#039;&amp;#039; Die SQL-Anweisung (&amp;lt;code&amp;gt;INSERT&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;UPDATE&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;DELETE&amp;lt;/code&amp;gt;) wird mit Platzhaltern (&amp;lt;code&amp;gt;?&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;:name&amp;lt;/code&amp;gt;) für die einzufügenden/zu aktualisierenden Werte sowie für die Kriterien in &amp;lt;code&amp;gt;WHERE&amp;lt;/code&amp;gt;-Klauseln (bei &amp;lt;code&amp;gt;UPDATE&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;DELETE&amp;lt;/code&amp;gt;) vorbereitet.[5, 46, 47, 48]&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;Ausführen (&amp;lt;code&amp;gt;execute&amp;lt;/code&amp;gt;):&amp;#039;&amp;#039;&amp;#039; Die Anweisung wird mit einem Array ausgeführt, das die Werte für die Platzhalter enthält.[5, 46, 47, 48]&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;Ergebnis prüfen:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
***** &amp;#039;&amp;#039;Anzahl betroffener Zeilen:&amp;#039;&amp;#039; &amp;lt;code&amp;gt;$stmt-&amp;amp;gt;rowCount()&amp;lt;/code&amp;gt; gibt die Anzahl der durch &amp;lt;code&amp;gt;UPDATE&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;DELETE&amp;lt;/code&amp;gt; betroffenen Zeilen zurück. Bei &amp;lt;code&amp;gt;INSERT&amp;lt;/code&amp;gt; ist der Rückgabewert oft nicht zuverlässig oder standardisiert.[8, 9, 5, 49] Das Verhalten kann je nach Datenbanktreiber und Konfiguration (z.B. &amp;lt;code&amp;gt;PDO::MYSQL_ATTR_FOUND_ROWS&amp;lt;/code&amp;gt;) variieren.[9]&lt;br /&gt;
***** &amp;#039;&amp;#039;ID des letzten eingefügten Datensatzes:&amp;#039;&amp;#039; Bei &amp;lt;code&amp;gt;INSERT&amp;lt;/code&amp;gt;-Anweisungen in Tabellen mit einer &amp;lt;code&amp;gt;AUTO_INCREMENT&amp;lt;/code&amp;gt;-Spalte liefert &amp;lt;code&amp;gt;$pdo-&amp;amp;gt;lastInsertId()&amp;lt;/code&amp;gt; die ID des neu eingefügten Datensatzes zurück.[8, 33, 48]&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;Code-Beispiele (PDO):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!--?php// --- INSERT ---&lt;br /&gt;
$name = $_POST[&amp;#039;name&amp;#039;];&lt;br /&gt;
$email = $_POST[&amp;#039;email&amp;#039;];&lt;br /&gt;
$status = &amp;#039;pending&amp;#039;;&lt;br /&gt;
&lt;br /&gt;
$sql_insert = &amp;quot;INSERT INTO users (username, email, status) VALUES (:name, :email, :status)&amp;quot;;&lt;br /&gt;
$stmt_insert = $pdo---&amp;gt;prepare($sql_insert);&lt;br /&gt;
$stmt_insert-&amp;amp;gt;execute([&amp;#039;:name&amp;#039; =&amp;amp;gt; $name, &amp;#039;:email&amp;#039; =&amp;amp;gt; $email, &amp;#039;:status&amp;#039; =&amp;amp;gt; $status]);&lt;br /&gt;
$lastId = $pdo-&amp;amp;gt;lastInsertId();&lt;br /&gt;
echo &amp;quot;Neuer Benutzer mit ID $lastId eingefügt.&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
// --- UPDATE ---&lt;br /&gt;
$new_status = &amp;#039;active&amp;#039;;&lt;br /&gt;
$user_id_to_update = $lastId;&lt;br /&gt;
&lt;br /&gt;
$sql_update = &amp;quot;UPDATE users SET status = :status WHERE id = :id&amp;quot;;&lt;br /&gt;
$stmt_update = $pdo-&amp;amp;gt;prepare($sql_update);&lt;br /&gt;
$stmt_update-&amp;amp;gt;execute([&amp;#039;:status&amp;#039; =&amp;amp;gt; $new_status, &amp;#039;:id&amp;#039; =&amp;amp;gt; $user_id_to_update]);&lt;br /&gt;
$affectedRows = $stmt_update-&amp;amp;gt;rowCount();&lt;br /&gt;
echo &amp;quot;$affectedRows Benutzerstatus aktualisiert.&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
// --- DELETE ---&lt;br /&gt;
$user_id_to_delete = $lastId;&lt;br /&gt;
&lt;br /&gt;
$sql_delete = &amp;quot;DELETE FROM users WHERE id = :id&amp;quot;;&lt;br /&gt;
$stmt_delete = $pdo-&amp;amp;gt;prepare($sql_delete);&lt;br /&gt;
$stmt_delete-&amp;amp;gt;execute([&amp;#039;:id&amp;#039; =&amp;amp;gt; $user_id_to_delete]);&lt;br /&gt;
$affectedRowsDel = $stmt_delete-&amp;amp;gt;rowCount();&lt;br /&gt;
echo &amp;quot;$affectedRowsDel Benutzer gelöscht.&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
?&amp;amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;MySQLi:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;Vorbereiten (&amp;lt;code&amp;gt;prepare&amp;lt;/code&amp;gt;):&amp;#039;&amp;#039;&amp;#039; Die SQL-Anweisung wird mit &amp;lt;code&amp;gt;?&amp;lt;/code&amp;gt;-Platzhaltern vorbereitet (OO: &amp;lt;code&amp;gt;$mysqli-&amp;amp;gt;prepare()&amp;lt;/code&amp;gt;, Proc: &amp;lt;code&amp;gt;mysqli_prepare()&amp;lt;/code&amp;gt;).[25, 50, 15, 35]&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;Parameter binden (&amp;lt;code&amp;gt;bind_param&amp;lt;/code&amp;gt;):&amp;#039;&amp;#039;&amp;#039; Variablen werden explizit mit Typenangabe gebunden (OO: &amp;lt;code&amp;gt;$stmt-&amp;amp;gt;bind_param()&amp;lt;/code&amp;gt;, Proc: &amp;lt;code&amp;gt;mysqli_stmt_bind_param()&amp;lt;/code&amp;gt;).[25, 50, 15, 35] Die Notwendigkeit, den Datentyp für jede Variable anzugeben, ist ein wesentlicher Unterschied zur einfacheren Array-Übergabe bei PDOs &amp;lt;code&amp;gt;execute()&amp;lt;/code&amp;gt;.&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;Ausführen (&amp;lt;code&amp;gt;execute&amp;lt;/code&amp;gt;):&amp;#039;&amp;#039;&amp;#039; Die Anweisung wird ausgeführt (OO: &amp;lt;code&amp;gt;$stmt-&amp;amp;gt;execute()&amp;lt;/code&amp;gt;, Proc: &amp;lt;code&amp;gt;mysqli_stmt_execute()&amp;lt;/code&amp;gt;).[25, 36, 50, 15, 35]&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;Ergebnis prüfen:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
***** &amp;#039;&amp;#039;Anzahl betroffener Zeilen:&amp;#039;&amp;#039; Über die Eigenschaft &amp;lt;code&amp;gt;$mysqli-&amp;amp;gt;affected_rows&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;$stmt-&amp;amp;gt;affected_rows&amp;lt;/code&amp;gt; (OO) bzw. die Funktionen &amp;lt;code&amp;gt;mysqli_affected_rows($link)&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;mysqli_stmt_affected_rows($stmt)&amp;lt;/code&amp;gt; (Proc).[18, 26, 15, 35]&lt;br /&gt;
***** &amp;#039;&amp;#039;ID des letzten eingefügten Datensatzes:&amp;#039;&amp;#039; Über &amp;lt;code&amp;gt;$mysqli-&amp;amp;gt;insert_id&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;$stmt-&amp;amp;gt;insert_id&amp;lt;/code&amp;gt; (OO) bzw. &amp;lt;code&amp;gt;mysqli_insert_id($link)&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;mysqli_stmt_insert_id($stmt)&amp;lt;/code&amp;gt; (Proc).[26, 15] Die Verwendung von &amp;lt;code&amp;gt;$mysqli-&amp;amp;gt;insert_id&amp;lt;/code&amp;gt; wird oft als zuverlässiger angesehen, da sie auch nach dem Schließen des Statements funktioniert.[15]&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;Code-Beispiele (MySQLi OO):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!--?php// --- INSERT ---&lt;br /&gt;
$name = $_POST[&amp;#039;name&amp;#039;];&lt;br /&gt;
$email = $_POST[&amp;#039;email&amp;#039;];&lt;br /&gt;
$status = &amp;#039;pending&amp;#039;;&lt;br /&gt;
&lt;br /&gt;
$sql_insert = &amp;quot;INSERT INTO users (username, email, status) VALUES (?,?,?)&amp;quot;;&lt;br /&gt;
$stmt_insert = $mysqli---&amp;gt;prepare($sql_insert);&lt;br /&gt;
// Typen: s=string, s=string, s=string&lt;br /&gt;
$stmt_insert-&amp;amp;gt;bind_param(&amp;quot;sss&amp;quot;, $name, $email, $status);&lt;br /&gt;
$stmt_insert-&amp;amp;gt;execute();&lt;br /&gt;
$lastId = $mysqli-&amp;amp;gt;insert_id;&lt;br /&gt;
echo &amp;quot;Neuer Benutzer mit ID $lastId eingefügt (MySQLi OO).&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
$stmt_insert-&amp;amp;gt;close(); // Statement schließen&lt;br /&gt;
&lt;br /&gt;
// --- UPDATE ---&lt;br /&gt;
$new_status = &amp;#039;active&amp;#039;;&lt;br /&gt;
$user_id_to_update = $lastId;&lt;br /&gt;
&lt;br /&gt;
$sql_update = &amp;quot;UPDATE users SET status =? WHERE id =?&amp;quot;;&lt;br /&gt;
$stmt_update = $mysqli-&amp;amp;gt;prepare($sql_update);&lt;br /&gt;
// Typen: s=string, i=integer&lt;br /&gt;
$stmt_update-&amp;amp;gt;bind_param(&amp;quot;si&amp;quot;, $new_status, $user_id_to_update);&lt;br /&gt;
$stmt_update-&amp;amp;gt;execute();&lt;br /&gt;
$affectedRows = $mysqli-&amp;amp;gt;affected_rows; // Oder $stmt_update-&amp;amp;gt;affected_rows vor close()&lt;br /&gt;
echo &amp;quot;$affectedRows Benutzerstatus aktualisiert (MySQLi OO).&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
$stmt_update-&amp;amp;gt;close();&lt;br /&gt;
&lt;br /&gt;
// --- DELETE ---&lt;br /&gt;
$user_id_to_delete = $lastId;&lt;br /&gt;
&lt;br /&gt;
$sql_delete = &amp;quot;DELETE FROM users WHERE id =?&amp;quot;;&lt;br /&gt;
$stmt_delete = $mysqli-&amp;amp;gt;prepare($sql_delete);&lt;br /&gt;
// Typ: i=integer&lt;br /&gt;
$stmt_delete-&amp;amp;gt;bind_param(&amp;quot;i&amp;quot;, $user_id_to_delete);&lt;br /&gt;
$stmt_delete-&amp;amp;gt;execute();&lt;br /&gt;
$affectedRowsDel = $mysqli-&amp;amp;gt;affected_rows;&lt;br /&gt;
echo &amp;quot;$affectedRowsDel Benutzer gelöscht (MySQLi OO).&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
$stmt_delete-&amp;amp;gt;close();&lt;br /&gt;
?&amp;amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;#039;&amp;#039;(Anmerkung: Prozedurale Beispiele folgen demselben Muster unter Verwendung der &amp;lt;code&amp;gt;mysqli_*&amp;lt;/code&amp;gt;-Funktionen wie &amp;lt;code&amp;gt;mysqli_prepare&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;mysqli_stmt_bind_param&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;mysqli_stmt_execute&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;mysqli_insert_id&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;mysqli_affected_rows&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;mysqli_stmt_close&amp;lt;/code&amp;gt;)&amp;#039;&amp;#039;.&lt;br /&gt;
&lt;br /&gt;
== (4) Sicherheit: SQL Injection verstehen und verhindern ==&lt;br /&gt;
&lt;br /&gt;
Eines der größten Sicherheitsrisiken bei der Interaktion mit Datenbanken über Webanwendungen ist die SQL Injection (SQLi). Das Verständnis dieser Bedrohung und ihrer Abwehr ist für jeden PHP-Entwickler unerlässlich.&lt;br /&gt;
&lt;br /&gt;
=== 4.1 Was ist SQL Injection? ===&lt;br /&gt;
&lt;br /&gt;
SQL Injection ist eine Angriffstechnik, bei der ein Angreifer versucht, bösartigen oder manipulierten SQL-Code über Benutzereingabefelder einer Webanwendung (z.B. Formulare, URL-Parameter, HTTP-Header, Cookies) in die Datenbankabfragen einzuschleusen.[51, 52, 53, 54, 55, 56, 57]&lt;br /&gt;
&lt;br /&gt;
Die Attacke funktioniert, indem Schwachstellen in der Anwendung ausgenutzt werden, bei denen Benutzereingaben direkt und ohne ausreichende Validierung oder Maskierung (Escaping) in SQL-Abfragestrings eingefügt (konkateniert) werden.[5, 51, 55] Dadurch wird die notwendige Trennung zwischen dem eigentlichen SQL-Befehl (Kontrollebene) und den Daten (Datenebene) aufgehoben.[51] Der Angreifer kann spezielle Zeichen (wie Anführungszeichen &amp;lt;code&amp;gt;&amp;#039;&amp;lt;/code&amp;gt;, Semikolons &amp;lt;code&amp;gt;;&amp;lt;/code&amp;gt; oder Kommentare &amp;lt;code&amp;gt;--&amp;lt;/code&amp;gt;) verwenden, um den ursprünglichen SQL-Befehl vorzeitig zu beenden, zu verändern oder um eigene, zusätzliche SQL-Befehle anzuhängen.[51, 53]&lt;br /&gt;
&lt;br /&gt;
Ein klassisches Beispiel ist eine Login-Abfrage, die etwa so aufgebaut ist:&lt;br /&gt;
&amp;lt;code&amp;gt;$sql = &amp;quot;SELECT * FROM users WHERE username = &amp;#039;&amp;quot;. $userInputUsername. &amp;quot;&amp;#039; AND password = &amp;#039;&amp;quot;. $userInputPassword. &amp;quot;&amp;#039;&amp;quot;;&amp;lt;/code&amp;gt;&lt;br /&gt;
Wenn ein Angreifer als Benutzernamen &amp;lt;code&amp;gt;&amp;#039; OR &amp;#039;1&amp;#039;=&amp;#039;1&amp;lt;/code&amp;gt; eingibt, wird die resultierende SQL-Abfrage zu:&lt;br /&gt;
&amp;lt;code&amp;gt;SELECT * FROM users WHERE username = &amp;#039;&amp;#039; OR &amp;#039;1&amp;#039;=&amp;#039;1&amp;#039; AND password = &amp;#039;...&amp;#039;&amp;lt;/code&amp;gt;&lt;br /&gt;
Da &amp;lt;code&amp;gt;&amp;#039;1&amp;#039;=&amp;#039;1&amp;#039;&amp;lt;/code&amp;gt; immer wahr ist, würde diese Abfrage (je nach genauer Struktur und Datenbank) möglicherweise alle Benutzer zurückgeben oder den Login ohne korrektes Passwort ermöglichen.[5, 15, 55]&lt;br /&gt;
&lt;br /&gt;
=== 4.2 Warum ist SQL Injection gefährlich? (Auswirkungen) ===&lt;br /&gt;
&lt;br /&gt;
Die potenziellen Folgen einer erfolgreichen SQL-Injection-Attacke sind gravierend und vielfältig:&lt;br /&gt;
&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Datendiebstahl:&amp;#039;&amp;#039;&amp;#039; Angreifer können auf sensible Daten zugreifen und diese auslesen, darunter Benutzerdaten, Passwörter (oft als Hashes, die aber geknackt werden können), Kreditkarteninformationen, Geschäftsgeheimnisse und andere vertrauliche Informationen.[51, 53, 56]&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Datenmanipulation/-zerstörung:&amp;#039;&amp;#039;&amp;#039; Angreifer können bestehende Daten in der Datenbank verändern (z.B. Preise in einem Shop, Benutzerrollen) oder komplette Datensätze oder sogar Tabellen löschen (&amp;lt;code&amp;gt;DELETE&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;DROP TABLE&amp;lt;/code&amp;gt;).[51, 53, 56]&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Umgehung der Authentifizierung/Identitätsdiebstahl:&amp;#039;&amp;#039;&amp;#039; Wie im Beispiel oben gezeigt, können Angreifer Login-Mechanismen umgehen und sich unautorisiert Zugang verschaffen, möglicherweise sogar mit administrativen Rechten.[51, 56]&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Denial of Service (DoS):&amp;#039;&amp;#039;&amp;#039; Durch das Löschen wichtiger Daten oder das Ausführen ressourcenintensiver Abfragen kann die Verfügbarkeit der Anwendung oder der Datenbank beeinträchtigt werden.[51, 56]&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Kompromittierung des Servers:&amp;#039;&amp;#039;&amp;#039; In manchen Fällen können Angreifer über SQL Injection Betriebssystembefehle auf dem Datenbankserver ausführen oder vollen administrativen Zugriff auf die Datenbank erlangen, was weitreichende Konsequenzen haben kann.[51, 55, 56]&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Reputationsschaden und rechtliche Folgen:&amp;#039;&amp;#039;&amp;#039; Erfolgreiche Angriffe, insbesondere wenn sie zu Datenlecks führen, können das Vertrauen der Benutzer schwer beschädigen und zu empfindlichen Strafen gemäß Datenschutzgesetzen führen.[53]&lt;br /&gt;
&lt;br /&gt;
=== 4.3 Prävention durch Prepared Statements (Primäre Methode) ===&lt;br /&gt;
&lt;br /&gt;
Die mit Abstand &amp;#039;&amp;#039;&amp;#039;wichtigste und effektivste Methode&amp;#039;&amp;#039;&amp;#039; zur Verhinderung von SQL Injection ist die konsequente Verwendung von &amp;#039;&amp;#039;&amp;#039;Prepared Statements&amp;#039;&amp;#039;&amp;#039; (parametrisierten Abfragen).[4, 25, 5, 58, 35]&lt;br /&gt;
&lt;br /&gt;
Das Grundprinzip von Prepared Statements ist die &amp;#039;&amp;#039;&amp;#039;strikte Trennung von SQL-Befehlsstruktur und den Daten (Parametern)&amp;#039;&amp;#039;&amp;#039;.[4, 25, 5, 58, 35] Der Ablauf ist typischerweise zweistufig:&lt;br /&gt;
**# &amp;#039;&amp;#039;&amp;#039;Prepare (Vorbereiten):&amp;#039;&amp;#039;&amp;#039; Die SQL-Anweisung wird mit Platzhaltern anstelle der tatsächlichen Werte an den Datenbankserver gesendet. Der Server parst die Anweisung, prüft die Syntax und bereitet einen Ausführungsplan vor, ohne die endgültigen Daten zu kennen.&lt;br /&gt;
**# &amp;#039;&amp;#039;&amp;#039;Execute (Ausführen) mit Parameterbindung:&amp;#039;&amp;#039;&amp;#039; Die tatsächlichen Werte (Parameter) werden separat an den Server gesendet und an die vorbereiteten Platzhalter gebunden. Der Server führt nun die vorkompilierte Anweisung mit den sicher eingefügten Werten aus.&lt;br /&gt;
Der entscheidende Punkt ist, dass die Datenbank die separat gesendeten Parameter &amp;#039;&amp;#039;&amp;#039;immer als Datenwerte&amp;#039;&amp;#039;&amp;#039; behandelt, niemals als Teil des SQL-Befehls.[5, 15, 58] Selbst wenn ein Parameter bösartigen SQL-Code enthält (z.B. &amp;lt;code&amp;gt;&amp;#039; OR &amp;#039;1&amp;#039;=&amp;#039;1&amp;lt;/code&amp;gt;), wird dieser nicht ausgeführt, sondern als einfacher String-Wert behandelt, der z.B. in eine Spalte eingefügt oder in einer &amp;lt;code&amp;gt;WHERE&amp;lt;/code&amp;gt;-Klausel verglichen wird. Diese Trennung macht Prepared Statements immun gegen SQL Injection.&lt;br /&gt;
&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Platzhalter:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;PDO:&amp;#039;&amp;#039;&amp;#039; Unterstützt sowohl positionale Platzhalter (&amp;lt;code&amp;gt;?&amp;lt;/code&amp;gt;) als auch benannte Platzhalter (&amp;lt;code&amp;gt;:name&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;:email&amp;lt;/code&amp;gt;, etc.).[5, 13] Benannte Platzhalter machen den Code oft lesbarer, besonders bei vielen Parametern.&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;MySQLi:&amp;#039;&amp;#039;&amp;#039; Unterstützt ausschließlich positionale Platzhalter (&amp;lt;code&amp;gt;?&amp;lt;/code&amp;gt;).[25]&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Parameterbindung:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;PDO:&amp;#039;&amp;#039;&amp;#039; Bietet flexible Bindungsoptionen:&lt;br /&gt;
***** &amp;#039;&amp;#039;Implizit über &amp;lt;code&amp;gt;execute()&amp;lt;/code&amp;gt;-Array:&amp;#039;&amp;#039; Die einfachste und häufigste Methode. Ein Array mit den Werten wird an &amp;lt;code&amp;gt;execute()&amp;lt;/code&amp;gt; übergeben. PDO kümmert sich um das korrekte Escaping und die Typbehandlung (oft werden Werte sicher als Strings behandelt).[4, 12, 5, 13]&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$stmt-&amp;amp;gt;execute([$wert1, $wert2]); // für? Platzhalter&lt;br /&gt;
$stmt-&amp;amp;gt;execute([&amp;#039;:name&amp;#039; =&amp;amp;gt; $wert1, &amp;#039;:id&amp;#039; =&amp;amp;gt; $wert2]); // für :name Platzhalter&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
***** &amp;#039;&amp;#039;Explizit mit &amp;lt;code&amp;gt;bindValue()&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;bindParam()&amp;lt;/code&amp;gt;:&amp;#039;&amp;#039; Erlaubt die explizite Angabe des Datentyps (z.B. &amp;lt;code&amp;gt;PDO::PARAM_INT&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;PDO::PARAM_STR&amp;lt;/code&amp;gt;) für jeden Parameter, was in manchen Fällen für Klarheit oder spezifische Datenbankoptimierungen sorgen kann.[5, 13] &amp;lt;code&amp;gt;bindParam&amp;lt;/code&amp;gt; bindet eine Variable (ihr Wert wird erst bei &amp;lt;code&amp;gt;execute()&amp;lt;/code&amp;gt; gelesen), &amp;lt;code&amp;gt;bindValue&amp;lt;/code&amp;gt; bindet den aktuellen Wert einer Variablen oder einen Literalwert.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$stmt-&amp;amp;gt;bindValue(&amp;#039;:id&amp;#039;, $user_id, PDO::PARAM_INT);&lt;br /&gt;
$stmt-&amp;amp;gt;bindParam(&amp;#039;:name&amp;#039;, $user_name, PDO::PARAM_STR);&lt;br /&gt;
$stmt-&amp;amp;gt;execute();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;MySQLi:&amp;#039;&amp;#039;&amp;#039; Erfordert immer die explizite Bindung mittels &amp;lt;code&amp;gt;bind_param()&amp;lt;/code&amp;gt;.&lt;br /&gt;
***** &amp;lt;code&amp;gt;bind_param()&amp;lt;/code&amp;gt; (OO) / &amp;lt;code&amp;gt;mysqli_stmt_bind_param()&amp;lt;/code&amp;gt; (Proc): Nimmt als ersten Parameter einen String entgegen, der die Datentypen aller nachfolgend übergebenen Variablen definiert.[25, 50, 15, 58]&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// OO-Stil&lt;br /&gt;
$stmt-&amp;amp;gt;bind_param(&amp;quot;ssi&amp;quot;, $name, $email, $id); // String, String, Integer&lt;br /&gt;
// Prozedural&lt;br /&gt;
mysqli_stmt_bind_param($stmt, &amp;quot;ssi&amp;quot;, $name, $email, $id);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
=== Tabelle 4: MySQLi &amp;lt;code&amp;gt;bind_param()&amp;lt;/code&amp;gt; Typen-String ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Typ-Buchstabe !! PHP-Datentyp (typisch) !! Beschreibung&lt;br /&gt;
|- &lt;br /&gt;
| mwt-preservehtml=&amp;quot;&amp;quot; i=&amp;quot;&amp;quot; code=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| integer=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Integer (Ganzzahl)&lt;br /&gt;
|- &lt;br /&gt;
| mwt-preservehtml=&amp;quot;&amp;quot; d=&amp;quot;&amp;quot; code=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| double=&amp;quot;&amp;quot; float=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Double (Gleitkommazahl)&lt;br /&gt;
|- &lt;br /&gt;
| mwt-preservehtml=&amp;quot;&amp;quot; s=&amp;quot;&amp;quot; code=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| string=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| String (Zeichenkette)&lt;br /&gt;
|- &lt;br /&gt;
| mwt-preservehtml=&amp;quot;&amp;quot; b=&amp;quot;&amp;quot; code=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| string=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| BLOB (Binary Large Object), als String gesendet&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Detaillierte Code-Beispiele zur Prävention:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
Die Beispiele für INSERT, UPDATE und DELETE in Abschnitt 3.3 demonstrieren bereits die korrekte Anwendung von Prepared Statements mit PDO und MySQLi zur Verhinderung von SQL Injection. Es ist entscheidend, dieses Muster konsequent anzuwenden, wann immer externe Daten in SQL-Abfragen einfließen.&lt;br /&gt;
&lt;br /&gt;
=== 4.4 Zusätzliche Schutzmaßnahmen (Defense-in-Depth) ===&lt;br /&gt;
&lt;br /&gt;
Obwohl Prepared Statements der wichtigste Schutz gegen SQL Injection sind, sollte Sicherheit immer als mehrschichtiges Konzept (&amp;quot;Defense-in-Depth&amp;quot;) betrachtet werden.[5] Zusätzliche Maßnahmen erhöhen die Robustheit der Anwendung:&lt;br /&gt;
&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Eingabevalidierung und -bereinigung:&amp;#039;&amp;#039;&amp;#039; Überprüfen Sie &amp;#039;&amp;#039;alle&amp;#039;&amp;#039; Benutzereingaben serverseitig, bevor sie überhaupt in die Nähe der Datenbank kommen.[4, 59, 20, 5] Stellen Sie sicher, dass die Daten dem erwarteten Typ (Zahl, String, E-Mail-Format etc.) und Format entsprechen. Verwenden Sie dafür PHP-Filterfunktionen wie &amp;lt;code&amp;gt;filter_var()&amp;lt;/code&amp;gt; oder reguläre Ausdrücke.[4, 5] Dies ist eine wichtige zweite Verteidigungslinie, die ungültige oder unerwartete Daten frühzeitig abfängt. Es ersetzt jedoch &amp;#039;&amp;#039;nicht&amp;#039;&amp;#039; die Notwendigkeit von Prepared Statements.&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Prinzip der geringsten Rechte (Least Privilege):&amp;#039;&amp;#039;&amp;#039; Konfigurieren Sie den Datenbankbenutzer, den Ihre PHP-Anwendung verwendet, mit den absolut minimal notwendigen Berechtigungen.[20, 5] Wenn die Anwendung nur Daten lesen und schreiben muss, geben Sie ihr keine Rechte zum Ändern der Tabellenstruktur (&amp;lt;code&amp;gt;ALTER TABLE&amp;lt;/code&amp;gt;) oder zum Löschen von Tabellen (&amp;lt;code&amp;gt;DROP TABLE&amp;lt;/code&amp;gt;). Beschränken Sie den Zugriff auf die benötigten Datenbanken und Tabellen. Verwenden Sie niemals den &amp;lt;code&amp;gt;root&amp;lt;/code&amp;gt;- oder Admin-Benutzer der Datenbank für die Anwendung. Dies begrenzt den potenziellen Schaden, falls doch eine Schwachstelle ausgenutzt wird.&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Sichere Fehlerbehandlung:&amp;#039;&amp;#039;&amp;#039; Konfigurieren Sie PHP und Ihre Datenbankerweiterung so, dass detaillierte Fehlermeldungen (insbesondere solche, die SQL-Code oder Datenbankstrukturinformationen enthalten) niemals dem Endbenutzer angezeigt werden.[20, 5] Solche Informationen sind für Angreifer wertvoll. Loggen Sie Fehler stattdessen in eine sichere Datei auf dem Server, die nur für Administratoren zugänglich ist. Verwenden Sie &amp;lt;code&amp;gt;PDO::ERRMODE_EXCEPTION&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;mysqli_report&amp;lt;/code&amp;gt; für strukturierte Fehlerbehandlung.&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Output Escaping:&amp;#039;&amp;#039;&amp;#039; Obwohl nicht direkt zur Verhinderung von SQL Injection, ist es wichtig, Daten, die aus der Datenbank gelesen und im HTML-Kontext ausgegeben werden, korrekt zu escapen (z.B. mit &amp;lt;code&amp;gt;htmlspecialchars()&amp;lt;/code&amp;gt;), um Cross-Site Scripting (XSS)-Angriffe zu verhindern.&lt;br /&gt;
Die Kombination dieser Maßnahmen – mit Prepared Statements als Kernstück – bietet einen robusten Schutz gegen SQL Injection und andere verwandte Angriffe.&lt;br /&gt;
&lt;br /&gt;
== (5) Nützliche PHP Array-Funktionen für Datenbankergebnisse ==&lt;br /&gt;
&lt;br /&gt;
Wenn Daten aus der Datenbank abgerufen werden, insbesondere mit Methoden wie &amp;lt;code&amp;gt;fetchAll()&amp;lt;/code&amp;gt; (PDO) oder &amp;lt;code&amp;gt;fetch_all()&amp;lt;/code&amp;gt; (MySQLi), liegen die Ergebnisse typischerweise als PHP-Array vor (meist ein Array von assoziativen Arrays, wobei jedes innere Array eine Ergebniszeile darstellt). PHP bietet eine Reihe nützlicher Funktionen zur Verarbeitung solcher Arrays.&lt;br /&gt;
&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Iteration mit &amp;lt;code&amp;gt;foreach&amp;lt;/code&amp;gt;:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
Dies ist die Standardmethode, um die einzelnen Zeilen und Spalten eines Ergebnis-Arrays zu durchlaufen.[37, 40, 44]&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;Syntax:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Iteration über die Zeilen (jedes $row ist ein assoziatives Array)&lt;br /&gt;
foreach ($results as $row) {&lt;br /&gt;
 echo &amp;quot;ID: &amp;quot;. $row[&amp;#039;id&amp;#039;]. &amp;quot;, Name: &amp;quot;. htmlspecialchars($row[&amp;#039;name&amp;#039;]). &amp;quot;&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Iteration über Zeilen mit Zugriff auf den numerischen Index der Zeile&lt;br /&gt;
foreach ($results as $index =&amp;amp;gt; $row) {&lt;br /&gt;
 echo &amp;quot;Zeile &amp;quot;. $index. &amp;quot;: ID = &amp;quot;. $row[&amp;#039;id&amp;#039;]. &amp;quot;&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;Code-Beispiel:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!--?php// Annahme: $results enthält das Ergebnis von $stmt---&amp;gt;fetchAll(PDO::FETCH_ASSOC);&lt;br /&gt;
$results = [&amp;#039;id&amp;#039; =&amp;amp;gt; 1, &amp;#039;name&amp;#039; =&amp;amp;gt; &amp;#039;Alice&amp;#039;, &amp;#039;email&amp;#039; =&amp;amp;gt; &amp;#039;alice@example.com&amp;#039;],&lt;br /&gt;
 ;&lt;br /&gt;
&lt;br /&gt;
echo &amp;quot;&amp;lt;/syntaxhighlight&amp;gt;&amp;quot;;&lt;br /&gt;
foreach ($results as $row) {&lt;br /&gt;
 echo &amp;quot;&lt;br /&gt;
*Benutzer #&amp;quot;. $row[&amp;#039;id&amp;#039;]. &amp;quot;: &amp;quot;. htmlspecialchars($row[&amp;#039;name&amp;#039;]);&lt;br /&gt;
 echo &amp;quot; (&amp;quot;. htmlspecialchars($row[&amp;#039;email&amp;#039;]). &amp;quot;)&lt;br /&gt;
&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
echo &amp;quot;&amp;quot;;&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Zählen von Ergebnissen mit &amp;lt;code&amp;gt;count()&amp;lt;/code&amp;gt;:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
Die Funktion &amp;lt;code&amp;gt;count()&amp;lt;/code&amp;gt; gibt die Anzahl der Elemente in einem Array zurück. Wenn sie auf ein mit &amp;lt;code&amp;gt;fetchAll()&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;fetch_all()&amp;lt;/code&amp;gt; erzeugtes Ergebnis-Array angewendet wird, liefert sie die Anzahl der abgerufenen Zeilen.[40, 60, 61]&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;Syntax:&amp;#039;&amp;#039;&amp;#039; &amp;lt;code&amp;gt;$anzahlZeilen = count($results);&amp;lt;/code&amp;gt;.[60]&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;Wichtiger Hinweis:&amp;#039;&amp;#039;&amp;#039; &amp;lt;code&amp;gt;count()&amp;lt;/code&amp;gt; funktioniert nur zuverlässig für die Gesamtzahl der Zeilen, wenn &amp;#039;&amp;#039;alle&amp;#039;&amp;#039; Zeilen zuvor in ein Array geladen wurden (also nach &amp;lt;code&amp;gt;fetchAll&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;fetch_all&amp;lt;/code&amp;gt;). Wenn Sie Ergebnisse zeilenweise mit &amp;lt;code&amp;gt;fetch()&amp;lt;/code&amp;gt; in einer &amp;lt;code&amp;gt;while&amp;lt;/code&amp;gt;-Schleife verarbeiten, müssen Sie einen manuellen Zähler innerhalb der Schleife inkrementieren, um die Anzahl der verarbeiteten Zeilen zu ermitteln.[40] Alternativ können &amp;lt;code&amp;gt;PDOStatement::rowCount()&amp;lt;/code&amp;gt; [8, 9] oder &amp;lt;code&amp;gt;mysqli_result::$num_rows&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;mysqli_stmt::$num_rows&amp;lt;/code&amp;gt; [26, 15] verwendet werden, um die Anzahl der von einer &amp;lt;code&amp;gt;SELECT&amp;lt;/code&amp;gt;-Abfrage zurückgegebenen Zeilen zu erhalten, &amp;#039;&amp;#039;bevor&amp;#039;&amp;#039; die Daten gefetched werden (das Verhalten von &amp;lt;code&amp;gt;rowCount&amp;lt;/code&amp;gt; kann jedoch bei anderen Anweisungstypen wie &amp;lt;code&amp;gt;UPDATE&amp;lt;/code&amp;gt; variieren).&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;Code-Beispiel:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;!--?php&lt;br /&gt;
// Annahme: $results von fetchAll()&lt;br /&gt;
$results = [ /*... wie oben... */ ];&lt;br /&gt;
$anzahl = count($results);&lt;br /&gt;
echo &amp;quot;Anzahl der Benutzer (via fetchAll/count): $anzahl&amp;lt;br--&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
// Vergleich: Zählen bei zeilenweiser Verarbeitung&lt;br /&gt;
$stmt = $pdo-&amp;amp;gt;query(&amp;quot;SELECT id FROM users&amp;quot;); // Annahme: PDO-Statement&lt;br /&gt;
$manueller_zaehler = 0;&lt;br /&gt;
while ($row = $stmt-&amp;amp;gt;fetch()) {&lt;br /&gt;
 $manueller_zaehler++;&lt;br /&gt;
}&lt;br /&gt;
echo &amp;quot;Anzahl der Benutzer (via while/fetch): $manueller_zaehler&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
// Alternative: rowCount() nach SELECT (PDO)&lt;br /&gt;
$stmt_count = $pdo-&amp;amp;gt;query(&amp;quot;SELECT id FROM users&amp;quot;);&lt;br /&gt;
$rowCountResult = $stmt_count-&amp;amp;gt;rowCount(); // Kann bei manchen DBs/Treibern funktionieren&lt;br /&gt;
echo &amp;quot;Anzahl der Benutzer (via rowCount nach SELECT): $rowCountResult&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
?&amp;amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;(Optional) Weitere nützliche Array-Funktionen:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
**** &amp;lt;code&amp;gt;array_column(array $input, mixed $column_key [, mixed $index_key = null ])&amp;lt;/code&amp;gt;: Extrahiert alle Werte aus einer einzelnen Spalte des Ergebnis-Arrays und gibt sie als neues, eindimensionales Array zurück.[62] Sehr nützlich, um z.B. eine Liste aller Benutzer-IDs oder E-Mail-Adressen zu erhalten.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Annahme: $results enthält das Array von assoziativen Arrays&lt;br /&gt;
$alle_emails = array_column($results, &amp;#039;email&amp;#039;);&lt;br /&gt;
// $alle_emails ist nun [&amp;#039;alice@example.com&amp;#039;, &amp;#039;bob@example.com&amp;#039;]&lt;br /&gt;
print_r($alle_emails);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
**** &amp;lt;code&amp;gt;array_map(callable $callback, array $array1 [, array...$arrays ])&amp;lt;/code&amp;gt;: Wendet eine benutzerdefinierte Funktion (&amp;lt;code&amp;gt;callback&amp;lt;/code&amp;gt;) auf jedes Element eines Arrays an und gibt ein neues Array mit den Ergebnissen zurück.[63] Nützlich zur Transformation von Daten (z.B. Formatierung, Bereinigung).&lt;br /&gt;
**** &amp;lt;code&amp;gt;array_filter(array $array [, callable $callback [, int $flag = 0 ]])&amp;lt;/code&amp;gt;: Filtert Elemente eines Arrays basierend auf einer Callback-Funktion.[61] Nützlich, um nur bestimmte Zeilen aus dem Ergebnis-Array zu behalten (obwohl dies oft effizienter direkt in der SQL-Abfrage geschieht).&lt;br /&gt;
**** &amp;lt;code&amp;gt;array_count_values(array $array)&amp;lt;/code&amp;gt;: Zählt, wie oft jeder eindeutige Wert in einem eindimensionalen Array vorkommt.[63] Kann nützlich sein, wenn man z.B. mit &amp;lt;code&amp;gt;array_column&amp;lt;/code&amp;gt; eine Spalte extrahiert hat und die Häufigkeit bestimmter Werte (z.B. Status) wissen möchte.&lt;br /&gt;
Diese Funktionen, insbesondere &amp;lt;code&amp;gt;foreach&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;count&amp;lt;/code&amp;gt;, sind grundlegende Werkzeuge bei der Verarbeitung von Datenbankergebnissen in PHP.&lt;br /&gt;
&lt;br /&gt;
== (6) Zusammenfassung und Best Practices ==&lt;br /&gt;
&lt;br /&gt;
Die Integration von Datenbanken ist ein zentraler Aspekt der PHP-Webentwicklung. Dieses Kapitel hat die Grundlagen für die Interaktion mit MySQL-Datenbanken gelegt. Hier sind die wichtigsten Punkte und Best Practices zusammengefasst:&lt;br /&gt;
&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Wahl der Erweiterung:&amp;#039;&amp;#039;&amp;#039; Verwenden Sie ausschließlich die modernen Erweiterungen &amp;#039;&amp;#039;&amp;#039;PDO&amp;#039;&amp;#039;&amp;#039; oder &amp;#039;&amp;#039;&amp;#039;MySQLi&amp;#039;&amp;#039;&amp;#039;.[2, 29, 5, 15]&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;PDO&amp;#039;&amp;#039;&amp;#039; wird generell für neue Projekte empfohlen, da es Datenbankunabhängigkeit bietet und eine konsistentere, oft als einfacher empfundene API (insbesondere bei der Parameterbindung) hat.[16, 3, 64]&lt;br /&gt;
**** &amp;#039;&amp;#039;&amp;#039;MySQLi&amp;#039;&amp;#039;&amp;#039; ist eine gute Wahl, wenn ausschließlich mit MySQL gearbeitet wird und spezifische MySQL-Funktionen benötigt werden oder wenn eine prozedurale Schnittstelle bevorzugt wird.[15]&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Vermeidung von &amp;lt;code&amp;gt;mysql_*&amp;lt;/code&amp;gt;:&amp;#039;&amp;#039;&amp;#039; Die alten &amp;lt;code&amp;gt;mysql_*&amp;lt;/code&amp;gt; Funktionen sind seit PHP 7.0 entfernt und &amp;#039;&amp;#039;&amp;#039;dürfen nicht mehr verwendet werden&amp;#039;&amp;#039;&amp;#039;. Sie sind unsicher und inkompatibel.[28, 29]&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Prepared Statements:&amp;#039;&amp;#039;&amp;#039; Dies ist die &amp;#039;&amp;#039;&amp;#039;wichtigste Sicherheitsmaßnahme&amp;#039;&amp;#039;&amp;#039;. Verwenden Sie Prepared Statements &amp;#039;&amp;#039;&amp;#039;immer&amp;#039;&amp;#039;&amp;#039; dann, wenn externe Daten (Benutzereingaben, URL-Parameter etc.) Teil einer SQL-Abfrage werden (insbesondere bei &amp;lt;code&amp;gt;WHERE&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;INSERT&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;UPDATE&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;DELETE&amp;lt;/code&amp;gt;).[2, 25, 5] Sie verhindern SQL Injection zuverlässig durch die Trennung von Code und Daten.&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Sichere Fehlerbehandlung:&amp;#039;&amp;#039;&amp;#039; Konfigurieren Sie PDO (&amp;lt;code&amp;gt;PDO::ATTR_ERRMODE =&amp;amp;gt; PDO::ERRMODE_EXCEPTION&amp;lt;/code&amp;gt;) oder MySQLi (&amp;lt;code&amp;gt;mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT)&amp;lt;/code&amp;gt;) so, dass Fehler als Exceptions behandelt werden.[1, 18] Fangen Sie diese Exceptions ab und loggen Sie detaillierte Fehlermeldungen serverseitig, anstatt sie dem Benutzer anzuzeigen.[20, 5]&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Input Validation:&amp;#039;&amp;#039;&amp;#039; Validieren Sie alle Benutzereingaben serverseitig auf Typ, Format und erlaubte Werte, bevor Sie sie an die Datenbank übergeben. Dies ist eine wichtige zusätzliche Schutzschicht.[4, 5]&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Prinzip der geringsten Rechte (Least Privilege):&amp;#039;&amp;#039;&amp;#039; Der Datenbankbenutzer, den Ihre PHP-Anwendung verwendet, sollte nur die minimal notwendigen Berechtigungen für seine Aufgaben besitzen.[5]&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Sichere Zugangsdaten:&amp;#039;&amp;#039;&amp;#039; Speichern Sie Datenbank-Credentials (Hostname, Benutzername, Passwort) niemals direkt im PHP-Code oder in Dateien innerhalb des Web-Roots.[20, 65] Verwenden Sie stattdessen Konfigurationsdateien außerhalb des öffentlich zugänglichen Bereichs oder Umgebungsvariablen.&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Code-Struktur:&amp;#039;&amp;#039;&amp;#039; Kapseln Sie Datenbankverbindungslogik und wiederkehrende Abfragen in Funktionen oder Klassen, um die Wartbarkeit und Wiederverwendbarkeit zu verbessern.[20]&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Effizienz:&amp;#039;&amp;#039;&amp;#039; Laden Sie große Ergebnismengen nicht komplett in den Speicher (&amp;lt;code&amp;gt;fetchAll&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;fetch_all&amp;lt;/code&amp;gt;). Verwenden Sie stattdessen zeilenweise Verarbeitung (&amp;lt;code&amp;gt;while&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;fetch&amp;lt;/code&amp;gt;) oder Iteratoren (PDO mit PHP 8+).[37, 40] Nutzen Sie persistente Verbindungen nur nach sorgfältiger Abwägung der Vor- und Nachteile.[7, 27]&lt;br /&gt;
*** &amp;#039;&amp;#039;&amp;#039;Defense-in-Depth:&amp;#039;&amp;#039;&amp;#039; Betrachten Sie Sicherheit als Gesamtkonzept. Die Kombination aus der richtigen API-Wahl, konsequenten Prepared Statements, Eingabevalidierung, minimalen Rechten, sicherer Fehlerbehandlung, sicherer Speicherung von Zugangsdaten und regelmäßigen Software-Updates bildet eine robuste Verteidigung.&lt;/div&gt;</summary>
		<author><name>Völkl Anna</name></author>
	</entry>
	<entry>
		<id>https://mediawiki.fernfh.ac.at/mediawiki/index.php?title=Datenbankintegration_(Webdevelopment)&amp;diff=6570</id>
		<title>Datenbankintegration (Webdevelopment)</title>
		<link rel="alternate" type="text/html" href="https://mediawiki.fernfh.ac.at/mediawiki/index.php?title=Datenbankintegration_(Webdevelopment)&amp;diff=6570"/>
		<updated>2025-05-01T10:26:34Z</updated>

		<summary type="html">&lt;p&gt;Völkl Anna: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Kapitel X: Datenbankintegration mit PHP und MySQL =&lt;br /&gt;
&lt;br /&gt;
== (1) Einleitung: Warum Datenbanken in PHP-Webanwendungen? ==&lt;br /&gt;
&lt;br /&gt;
Moderne Webanwendungen sind selten statisch. Sie leben von dynamischen Inhalten, Benutzerinteraktionen und der Fähigkeit, Informationen über Sitzungen hinweg zu speichern und wieder abzurufen. Ob es sich um ein Content-Management-System (CMS), einen Online-Shop, ein soziales Netzwerk oder eine einfache Benutzerverwaltung handelt – die Persistenz von Daten ist entscheidend. Hier kommen Datenbanken ins Spiel.&lt;br /&gt;
&lt;br /&gt;
PHP allein, als serverseitige Skriptsprache, kann Daten nur für die Dauer der Ausführung eines Skripts im Arbeitsspeicher halten. Sobald das Skript beendet ist, gehen diese Informationen verloren. Um Daten dauerhaft zu speichern, zu organisieren, zu ändern und effizient abzurufen, benötigen PHP-Anwendungen ein externes Datenbanksystem.&lt;br /&gt;
&lt;br /&gt;
Eine der populärsten Datenbanklösungen im Webentwicklungsbereich, insbesondere in Kombination mit PHP, ist MySQL. Als relationales Datenbankmanagementsystem (RDBMS) bietet MySQL eine strukturierte Methode zur Datenspeicherung in Tabellen, die miteinander in Beziehung stehen können. Es ist ein Kernbestandteil vieler Webserver-Setups wie LAMP (Linux, Apache, MySQL, PHP) oder LEMP (Linux, Nginx, MySQL, PHP).&lt;br /&gt;
&lt;br /&gt;
Um die Brücke zwischen der PHP-Anwendung und der MySQL-Datenbank zu schlagen, stellt PHP spezielle Erweiterungen (Extensions) bereit. Diese Erweiterungen fungieren als Schnittstelle oder API (Application Programming Interface), die es PHP-Skripten ermöglichen, eine Verbindung zur Datenbank herzustellen, SQL-Befehle (Structured Query Language Queries) zu senden und die von der Datenbank zurückgegebenen Ergebnisse zu empfangen und zu verarbeiten.&lt;br /&gt;
&lt;br /&gt;
Dieses Kapitel führt Sie in die Grundlagen der Datenbankintegration mit PHP und MySQL ein. Sie lernen die verschiedenen verfügbaren PHP-Erweiterungen kennen, wobei der Schwerpunkt auf den modernen und sicheren Ansätzen liegt: PDO (PHP Data Objects) und MySQLi (MySQL Improved). Wir werden untersuchen, wie man Verbindungen aufbaut, grundlegende Datenbankoperationen – bekannt als CRUD (Create, Read, Update, Delete) – durchführt und wie man Daten sicher abfragt und manipuliert. Ein besonderes Augenmerk liegt dabei auf der Sicherheit, insbesondere auf der Vermeidung von SQL-Injection-Angriffen durch den Einsatz von Prepared Statements. Schließlich betrachten wir nützliche PHP-Array-Funktionen zur Verarbeitung der abgerufenen Datenbankergebnisse.&lt;br /&gt;
&lt;br /&gt;
== (2) Überblick über PHP-Datenbankerweiterungen ==&lt;br /&gt;
&lt;br /&gt;
Die Art und Weise, wie PHP mit Datenbanken interagiert, hat sich im Laufe der Zeit weiterentwickelt. Ursprünglich gab es für jede populäre Datenbank eine eigene spezifische Erweiterung. Für MySQL waren dies die &amp;lt;code&amp;gt;mysql_*&amp;lt;/code&amp;gt; Funktionen. Diese sind jedoch inzwischen veraltet und wurden durch modernere, flexiblere und sicherere Alternativen ersetzt. Heute stehen Entwicklern hauptsächlich zwei empfohlene Erweiterungen für die Arbeit mit MySQL zur Verfügung: PDO und MySQLi.&lt;br /&gt;
&lt;br /&gt;
*   &amp;#039;&amp;#039;&amp;#039;PDO (PHP Data Objects):&amp;#039;&amp;#039;&amp;#039; Eine allgemeine Datenzugriffs-Abstraktionsschicht, die mit verschiedenen Datenbanksystemen verwendet werden kann.&lt;br /&gt;
*   &amp;#039;&amp;#039;&amp;#039;MySQLi (MySQL Improved):&amp;#039;&amp;#039;&amp;#039; Eine Erweiterung, die speziell für die Arbeit mit MySQL entwickelt wurde und dessen spezifische Funktionen unterstützt.&lt;br /&gt;
*   &amp;#039;&amp;#039;&amp;#039;&amp;lt;code&amp;gt;mysql_*&amp;lt;/code&amp;gt; Funktionen:&amp;#039;&amp;#039;&amp;#039; Die ursprüngliche MySQL-Erweiterung, die seit PHP 7.0 entfernt wurde und nicht mehr verwendet werden sollte.&lt;br /&gt;
Im Folgenden werden diese drei Optionen detaillierter vorgestellt.&lt;br /&gt;
&lt;br /&gt;
=== 2.1 PDO (PHP Data Objects) ===&lt;br /&gt;
&lt;br /&gt;
Die PDO-Erweiterung definiert eine leichtgewichtige und konsistente Schnittstelle für den Datenbankzugriff in PHP. Sie fungiert als &amp;#039;&amp;#039;Datenzugriffs-Abstraktionsschicht&amp;#039;&amp;#039;. Das bedeutet, dass Entwickler unabhängig von der verwendeten Datenbank dieselben PDO-Funktionen nutzen können, um Abfragen auszuführen und Daten abzurufen.[1, 2] Es ist jedoch wichtig zu verstehen, dass PDO &amp;#039;&amp;#039;keine&amp;#039;&amp;#039; Datenbankabstraktion im Sinne einer SQL-Dialekt-Übersetzung oder der Emulation fehlender Datenbankfeatures bietet. Um eine Verbindung zu einer spezifischen Datenbank wie MySQL herzustellen, benötigt PDO einen entsprechenden datenbankspezifischen Treiber, z.B. &amp;lt;code&amp;gt;pdo_mysql&amp;lt;/code&amp;gt;, der in der PHP-Konfiguration aktiviert sein muss.[1]&lt;br /&gt;
&lt;br /&gt;
*   &amp;#039;&amp;#039;&amp;#039;Vorteile von PDO:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
**   &amp;#039;&amp;#039;&amp;#039;Datenbankunabhängigkeit:&amp;#039;&amp;#039;&amp;#039; Der größte Vorteil von PDO ist die Möglichkeit, den Code für Datenbankinteraktionen weitgehend unverändert zu lassen, selbst wenn das zugrunde liegende Datenbanksystem gewechselt wird (z.B. von MySQL zu PostgreSQL).[1, 2] Dies erhöht die Portabilität und Flexibilität von PHP-Anwendungen erheblich.[2, 3]&lt;br /&gt;
**   &amp;#039;&amp;#039;&amp;#039;Sicherheit durch Prepared Statements:&amp;#039;&amp;#039;&amp;#039; PDO bietet eine robuste und konsistente Implementierung von Prepared Statements. Diese Technik ist der Goldstandard zur Verhinderung von SQL-Injection-Angriffen, da sie die SQL-Befehlsstruktur strikt von den übergebenen Daten trennt.[1, 2, 4, 5, 6]&lt;br /&gt;
**   &amp;#039;&amp;#039;&amp;#039;Benutzerfreundlichkeit und Konsistenz:&amp;#039;&amp;#039;&amp;#039; PDO stellt eine einheitliche API mit nützlichen Hilfsfunktionen bereit, wie z.B. vielfältige &amp;quot;Fetch Modes&amp;quot;, um die Struktur der abgerufenen Daten zu steuern.[2] Die konsistente Funktionsweise über verschiedene Datenbanktreiber hinweg vereinfacht die Entwicklung.&lt;br /&gt;
**   &amp;#039;&amp;#039;&amp;#039;Moderne Fehlerbehandlung:&amp;#039;&amp;#039;&amp;#039; PDO verwendet standardmäßig Exceptions (&amp;lt;code&amp;gt;PDOException&amp;lt;/code&amp;gt;) zur Signalisierung von Fehlern.[1, 2, 7] Dies ermöglicht eine strukturierte und moderne Fehlerbehandlung mittels &amp;lt;code&amp;gt;try...catch&amp;lt;/code&amp;gt;-Blöcken, was als Best Practice gilt.[1, 2]&lt;br /&gt;
*   &amp;#039;&amp;#039;&amp;#039;Verbindungsaufbau mit PDO (Beispiel MySQL):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
**   &amp;#039;&amp;#039;&amp;#039;DSN (Data Source Name):&amp;#039;&amp;#039;&amp;#039; Die Verbindungsparameter werden in einer Zeichenkette, dem DSN, übergeben. Für MySQL hat der DSN typischerweise das Format &amp;lt;code&amp;gt;mysql:host=hostname;dbname=datenbankname;charset=zeichensatz;unix_socket=pfad_zum_socket&amp;lt;/code&amp;gt;. Die Angabe des Zeichensatzes (z.B. &amp;lt;code&amp;gt;utf8mb4&amp;lt;/code&amp;gt;) ist wichtig für die korrekte Datenübertragung.[1, 7] Beispiel: &amp;lt;code&amp;gt;mysql:host=localhost;dbname=test;charset=utf8mb4&amp;lt;/code&amp;gt;.&lt;br /&gt;
**   &amp;#039;&amp;#039;&amp;#039;PDO-Konstruktor:&amp;#039;&amp;#039;&amp;#039; Eine Verbindung wird durch Instanziierung der PDO-Klasse hergestellt: &amp;lt;code&amp;gt;$pdo = new PDO($dsn, $username, $password, $options);&amp;lt;/code&amp;gt;.[1, 7, 8] Die Parameter sind der DSN, der Datenbankbenutzername, das Passwort und ein optionales Array mit Treiberoptionen.&lt;br /&gt;
**   &amp;#039;&amp;#039;&amp;#039;Verbindungsoptionen (&amp;lt;code&amp;gt;$options&amp;lt;/code&amp;gt;):&amp;#039;&amp;#039;&amp;#039; Über dieses Array können wichtige Verhaltensweisen von PDO gesteuert werden:&lt;br /&gt;
***   &amp;lt;code&amp;gt;PDO::ATTR_ERRMODE =&amp;amp;gt; PDO::ERRMODE_EXCEPTION&amp;lt;/code&amp;gt;: Dies ist die empfohlene Einstellung für die Fehlerbehandlung. PDO wirft bei Fehlern eine &amp;lt;code&amp;gt;PDOException&amp;lt;/code&amp;gt;, die abgefangen werden kann.[1, 2, 4]&lt;br /&gt;
***   &amp;lt;code&amp;gt;PDO::ATTR_DEFAULT_FETCH_MODE =&amp;amp;gt; PDO::FETCH_ASSOC&amp;lt;/code&amp;gt;: Legt fest, dass Daten standardmäßig als assoziatives Array (&amp;lt;code&amp;gt;spaltenname =&amp;amp;gt; wert&amp;lt;/code&amp;gt;) zurückgegeben werden, was oft die Weiterverarbeitung erleichtert.[1, 9]&lt;br /&gt;
***   &amp;lt;code&amp;gt;PDO::ATTR_EMULATE_PREPARES =&amp;amp;gt; false&amp;lt;/code&amp;gt;: Deaktiviert die Emulation von Prepared Statements durch PDO und zwingt die Verwendung nativer Prepared Statements des Datenbanktreibers. Dies gilt als die sicherste und oft performanteste Methode.[1, 10, 9, 11] Standardmäßig ist die Emulation aktiviert (&amp;lt;code&amp;gt;true&amp;lt;/code&amp;gt;). Obwohl PDO auch im emulierten Modus durch korrektes Escaping Schutz vor SQL Injection bietet [12, 5, 13], eliminiert die Deaktivierung potenzielle, wenn auch seltene, Sicherheitsrisiken, die in bestimmten Konstellationen (z.B. bei Verwendung exotischer Zeichensätze wie GBK [11]) auftreten könnten. Die explizite Deaktivierung ist daher eine wichtige Best Practice.&lt;br /&gt;
***   &amp;lt;code&amp;gt;PDO::ATTR_PERSISTENT =&amp;amp;gt; true&amp;lt;/code&amp;gt;: Aktiviert persistente Datenbankverbindungen. Diese werden nicht am Ende des Skripts geschlossen, sondern in einem Pool gehalten und für nachfolgende Anfragen wiederverwendet. Dies kann die Performance von Webanwendungen verbessern, da der Overhead des Verbindungsaufbaus entfällt. Die Verwendung erfordert jedoch sorgfältige Überlegung, da der Zustand der Verbindung zwischen Anfragen bestehen bleiben kann.[7]&lt;br /&gt;
**   &amp;#039;&amp;#039;&amp;#039;Fehlerbehandlung:&amp;#039;&amp;#039;&amp;#039; Der Verbindungsaufbau sollte immer in einem &amp;lt;code&amp;gt;try...catch&amp;lt;/code&amp;gt;-Block erfolgen, um &amp;lt;code&amp;gt;PDOException&amp;lt;/code&amp;gt; abzufangen und angemessen darauf zu reagieren (z.B. Fehlermeldung loggen, alternative Aktion ausführen).[1, 7] Es ist kritisch, detaillierte Fehlermeldungen in Produktionsumgebungen nicht an den Benutzer auszugeben, da sie sensible Informationen enthalten könnten. Die PHP-Einstellung &amp;lt;code&amp;gt;display_errors&amp;lt;/code&amp;gt; sollte daher auf &amp;lt;code&amp;gt;0&amp;lt;/code&amp;gt; gesetzt sein.[7]&lt;br /&gt;
**   &amp;#039;&amp;#039;&amp;#039;Verbindung schließen:&amp;#039;&amp;#039;&amp;#039; Eine PDO-Verbindung bleibt für die Lebensdauer des PDO-Objekts aktiv. Um sie explizit zu schließen, müssen alle Referenzen auf das PDO-Objekt und alle davon abgeleiteten &amp;lt;code&amp;gt;PDOStatement&amp;lt;/code&amp;gt;-Objekte entfernt werden, typischerweise durch Zuweisung von &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt;.[7] PHP schließt die Verbindung automatisch am Ende des Skriptlaufs, wenn dies nicht manuell geschieht.&lt;br /&gt;
*   &amp;#039;&amp;#039;&amp;#039;Code-Beispiel (PDO-Verbindung zu MySQL):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php$host = &amp;#039;localhost&amp;#039;;&lt;br /&gt;
$dbname = &amp;#039;meine_datenbank&amp;#039;;&lt;br /&gt;
$username = &amp;#039;mein_benutzer&amp;#039;;&lt;br /&gt;
$password = &amp;#039;mein_passwort&amp;#039;;&lt;br /&gt;
$charset = &amp;#039;utf8mb4&amp;#039;;&lt;br /&gt;
&lt;br /&gt;
$dsn = &amp;quot;mysql:host=$host;dbname=$dbname;charset=$charset&amp;quot;;&lt;br /&gt;
$options =;&lt;br /&gt;
&lt;br /&gt;
try {&lt;br /&gt;
     $pdo = new PDO($dsn, $username, $password, $options);&lt;br /&gt;
     // Erfolgreich verbunden, $pdo-Objekt kann nun verwendet werden.&lt;br /&gt;
     // echo &amp;quot;Verbindung erfolgreich hergestellt!&amp;quot;;&lt;br /&gt;
} catch (\PDOException $e) {&lt;br /&gt;
     // Fehler beim Verbindungsaufbau&lt;br /&gt;
     // In Produktion: Fehler loggen, statt ausgeben&lt;br /&gt;
     error_log(&amp;quot;Datenbankverbindungsfehler: &amp;quot;. $e-&amp;gt;getMessage());&lt;br /&gt;
     die(&amp;quot;Es gab ein Problem mit der Datenbankverbindung. Bitte versuchen Sie es später erneut.&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//... hier Datenbankoperationen mit $pdo durchführen...&lt;br /&gt;
&lt;br /&gt;
// Verbindung explizit schließen (optional, da PHP dies am Skriptende tut)&lt;br /&gt;
// $pdo = null;&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Dieses Beispiel zeigt den empfohlenen Weg, eine PDO-Verbindung zu MySQL herzustellen, inklusive wichtiger Optionen für Fehlerbehandlung und Sicherheit sowie einem &amp;lt;code&amp;gt;try...catch&amp;lt;/code&amp;gt;-Block zum Abfangen von Verbindungsfehlern.&lt;br /&gt;
&lt;br /&gt;
=== 2.2 MySQLi (MySQL Improved) ===&lt;br /&gt;
&lt;br /&gt;
Die MySQLi-Erweiterung ist, wie der Name schon sagt, speziell für die Interaktion mit MySQL-Datenbanken konzipiert und bietet Unterstützung für Funktionen, die ab MySQL Version 4.1 eingeführt wurden.[14, 15] Sie ermöglicht den Zugriff auf MySQL-spezifische Features, die über PDO möglicherweise nicht direkt verfügbar sind.[16, 15]&lt;br /&gt;
&lt;br /&gt;
*   &amp;#039;&amp;#039;&amp;#039;Duale Schnittstelle:&amp;#039;&amp;#039;&amp;#039; Ein charakteristisches Merkmal von MySQLi ist die Unterstützung von zwei Programmierstilen [17]:&lt;br /&gt;
**   &amp;#039;&amp;#039;&amp;#039;Prozedural:&amp;#039;&amp;#039;&amp;#039; Dieser Stil ähnelt den alten &amp;lt;code&amp;gt;mysql_*&amp;lt;/code&amp;gt; Funktionen. Die Funktionsnamen beginnen typischerweise mit &amp;lt;code&amp;gt;mysqli_&amp;lt;/code&amp;gt; (z.B. &amp;lt;code&amp;gt;mysqli_connect()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;mysqli_query()&amp;lt;/code&amp;gt;). Der Datenbankverbindungs-Handle (eine Ressource oder ein Objekt) muss dabei explizit als erster Parameter an die meisten Funktionen übergeben werden.[17] Dieser Stil wird oft von Entwicklern bevorzugt, die von der alten &amp;lt;code&amp;gt;mysql&amp;lt;/code&amp;gt;-Erweiterung migrieren.[17]&lt;br /&gt;
**   &amp;#039;&amp;#039;&amp;#039;Objektorientiert (OO):&amp;#039;&amp;#039;&amp;#039; Hier wird ein Objekt der &amp;lt;code&amp;gt;mysqli&amp;lt;/code&amp;gt;-Klasse erstellt, und Datenbankoperationen werden als Methoden dieses Objekts aufgerufen (z.B. &amp;lt;code&amp;gt;$mysqli-&amp;amp;gt;query()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;$mysqli-&amp;amp;gt;prepare()&amp;lt;/code&amp;gt;).[17] Dieser Ansatz passt oft besser zu modernen PHP-Entwicklungspraktiken und vermeidet die Notwendigkeit, den Verbindungshandle ständig zu übergeben. Die offizielle PHP-Dokumentation für MySQLi ist primär im objektorientierten Stil verfasst.[17] Es gibt keine nennenswerten Performance-Unterschiede zwischen den beiden Stilen; die Wahl ist meist eine Frage der persönlichen Präferenz oder Projektkonventionen.[17]&lt;br /&gt;
*   &amp;#039;&amp;#039;&amp;#039;Verbindungsaufbau mit MySQLi:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
**   &amp;#039;&amp;#039;&amp;#039;Prozedural:&amp;#039;&amp;#039;&amp;#039; Die Funktion &amp;lt;code&amp;gt;mysqli_connect()&amp;lt;/code&amp;gt; wird verwendet: &amp;lt;code&amp;gt;$link = mysqli_connect($host, $user, $pass, $db, $port, $socket);&amp;lt;/code&amp;gt;. Sie gibt bei Erfolg ein &amp;lt;code&amp;gt;mysqli&amp;lt;/code&amp;gt;-Objekt (oder &amp;lt;code&amp;gt;false&amp;lt;/code&amp;gt; bei Fehlern vor PHP 8.1) zurück.[17, 18] Fehler können mit &amp;lt;code&amp;gt;mysqli_connect_errno()&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;mysqli_connect_error()&amp;lt;/code&amp;gt; geprüft werden.[18]&lt;br /&gt;
**   &amp;#039;&amp;#039;&amp;#039;Objektorientiert:&amp;#039;&amp;#039;&amp;#039; Ein neues &amp;lt;code&amp;gt;mysqli&amp;lt;/code&amp;gt;-Objekt wird instanziiert: &amp;lt;code&amp;gt;$mysqli = new mysqli($host, $user, $pass, $db, $port, $socket);&amp;lt;/code&amp;gt;. Wichtig: Der Konstruktor gibt &amp;#039;&amp;#039;immer&amp;#039;&amp;#039; ein Objekt zurück, auch wenn die Verbindung fehlschlägt. Der Verbindungsstatus muss explizit über die Eigenschaften &amp;lt;code&amp;gt;$mysqli-&amp;amp;gt;connect_errno&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;$mysqli-&amp;amp;gt;connect_error&amp;lt;/code&amp;gt; geprüft werden.[17, 18]&lt;br /&gt;
**   &amp;#039;&amp;#039;&amp;#039;Fortgeschrittene Verbindung:&amp;#039;&amp;#039;&amp;#039; Für feinere Kontrolle über Verbindungsoptionen (z.B. Setzen von Timeouts oder Initialisierungsbefehlen &amp;#039;&amp;#039;vor&amp;#039;&amp;#039; dem Verbindungsaufbau) können &amp;lt;code&amp;gt;mysqli_init()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;mysqli_options()&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;mysqli_real_connect()&amp;lt;/code&amp;gt; verwendet werden.[18, 19]&lt;br /&gt;
**   &amp;#039;&amp;#039;&amp;#039;Fehlerbehandlung:&amp;#039;&amp;#039;&amp;#039; Neben der manuellen Prüfung von Fehlercodes und -meldungen ist die empfohlene Methode, MySQLi so zu konfigurieren, dass es bei Fehlern Exceptions wirft. Dies geschieht mit &amp;lt;code&amp;gt;mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);&amp;lt;/code&amp;gt;. Danach können Datenbankfehler elegant mit &amp;lt;code&amp;gt;try...catch&amp;lt;/code&amp;gt;-Blöcken behandelt werden, ähnlich wie bei PDO.[18, 20, 21, 22, 23, 24, 25, 26]&lt;br /&gt;
**   &amp;#039;&amp;#039;&amp;#039;Verbindung schließen:&amp;#039;&amp;#039;&amp;#039; Prozedural mit &amp;lt;code&amp;gt;mysqli_close($link)&amp;lt;/code&amp;gt;, objektorientiert mit &amp;lt;code&amp;gt;$mysqli-&amp;amp;gt;close()&amp;lt;/code&amp;gt;.[17, 18, 19]&lt;br /&gt;
**   &amp;#039;&amp;#039;&amp;#039;Persistente Verbindungen:&amp;#039;&amp;#039;&amp;#039; MySQLi unterstützt ebenfalls persistente Verbindungen, die über PHP-Konfigurationseinstellungen (&amp;lt;code&amp;gt;mysqli.allow_persistent&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;mysqli.max_persistent&amp;lt;/code&amp;gt;) gesteuert werden.[27] Standardmäßig setzt MySQLi den Zustand einer wiederverwendeten persistenten Verbindung zurück (via &amp;lt;code&amp;gt;mysqli::change_user()&amp;lt;/code&amp;gt;), was zwar für Konsistenz sorgt, aber auch Performance kosten kann.[27]&lt;br /&gt;
*   &amp;#039;&amp;#039;&amp;#039;Code-Beispiele (MySQLi-Verbindungen):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
**   &amp;#039;&amp;#039;&amp;#039;Objektorientiert (mit Exception-Handling):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php$host = &amp;#039;localhost&amp;#039;;&lt;br /&gt;
$user = &amp;#039;mein_benutzer&amp;#039;;&lt;br /&gt;
$pass = &amp;#039;mein_passwort&amp;#039;;&lt;br /&gt;
$db = &amp;#039;meine_datenbank&amp;#039;;&lt;br /&gt;
&lt;br /&gt;
// Fehlerberichterstattung für Exceptions aktivieren&lt;br /&gt;
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);&lt;br /&gt;
&lt;br /&gt;
try {&lt;br /&gt;
    $mysqli = new mysqli($host, $user, $pass, $db);&lt;br /&gt;
    // Zeichensatz setzen (wichtig!)&lt;br /&gt;
    $mysqli-&amp;gt;set_charset(&amp;#039;utf8mb4&amp;#039;);&lt;br /&gt;
    // Erfolgreich verbunden&lt;br /&gt;
    // echo &amp;quot;Verbindung erfolgreich (OO)! Host-Info: &amp;quot;. $mysqli-&amp;gt;host_info;&lt;br /&gt;
} catch (mysqli_sql_exception $e) {&lt;br /&gt;
    // Fehler beim Verbindungsaufbau oder set_charset&lt;br /&gt;
    error_log(&amp;quot;MySQLi Verbindungsfehler: &amp;quot;. $e-&amp;gt;getMessage());&lt;br /&gt;
    die(&amp;quot;Es gab ein Problem mit der Datenbankverbindung.&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//... Datenbankoperationen mit $mysqli...&lt;br /&gt;
&lt;br /&gt;
// Verbindung schließen&lt;br /&gt;
// $mysqli-&amp;gt;close();&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
**   &amp;#039;&amp;#039;&amp;#039;Prozedural (mit Exception-Handling):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php$host = &amp;#039;localhost&amp;#039;;&lt;br /&gt;
$user = &amp;#039;mein_benutzer&amp;#039;;&lt;br /&gt;
$pass = &amp;#039;mein_passwort&amp;#039;;&lt;br /&gt;
$db = &amp;#039;meine_datenbank&amp;#039;;&lt;br /&gt;
&lt;br /&gt;
// Fehlerberichterstattung für Exceptions aktivieren&lt;br /&gt;
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);&lt;br /&gt;
&lt;br /&gt;
try {&lt;br /&gt;
    $link = mysqli_connect($host, $user, $pass, $db);&lt;br /&gt;
    // Zeichensatz setzen&lt;br /&gt;
    mysqli_set_charset($link, &amp;#039;utf8mb4&amp;#039;);&lt;br /&gt;
    // Erfolgreich verbunden&lt;br /&gt;
    // echo &amp;quot;Verbindung erfolgreich (Prozedural)! Host-Info: &amp;quot;. mysqli_get_host_info($link);&lt;br /&gt;
} catch (mysqli_sql_exception $e) {&lt;br /&gt;
    // Fehler beim Verbindungsaufbau oder set_charset&lt;br /&gt;
    error_log(&amp;quot;MySQLi Verbindungsfehler: &amp;quot;. $e-&amp;gt;getMessage());&lt;br /&gt;
    die(&amp;quot;Es gab ein Problem mit der Datenbankverbindung.&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//... Datenbankoperationen mit $link...&lt;br /&gt;
&lt;br /&gt;
// Verbindung schließen&lt;br /&gt;
// mysqli_close($link);&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*   &amp;#039;&amp;#039;&amp;#039;Bedeutung von &amp;lt;code&amp;gt;mysqlnd&amp;lt;/code&amp;gt;:&amp;#039;&amp;#039;&amp;#039; Die Benutzerfreundlichkeit, insbesondere bei der Arbeit mit Prepared Statements, wird stark durch den verwendeten MySQL-Treiber beeinflusst. Der &amp;#039;&amp;#039;&amp;#039;MySQL Native Driver (&amp;lt;code&amp;gt;mysqlnd&amp;lt;/code&amp;gt;)&amp;#039;&amp;#039;&amp;#039;, der seit PHP 5.4 standardmäßig enthalten ist [16], ist hier entscheidend. &amp;lt;code&amp;gt;mysqlnd&amp;lt;/code&amp;gt; stellt die Methode &amp;lt;code&amp;gt;get_result()&amp;lt;/code&amp;gt; (bzw. die Funktion &amp;lt;code&amp;gt;mysqli_stmt_get_result()&amp;lt;/code&amp;gt;) zur Verfügung.[25, 15] Diese Methode erlaubt es, nach dem Ausführen eines Prepared Statements ein Standard-&amp;lt;code&amp;gt;mysqli_result&amp;lt;/code&amp;gt;-Objekt zu erhalten, von dem dann wie gewohnt mit &amp;lt;code&amp;gt;fetch_assoc()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;fetch_all()&amp;lt;/code&amp;gt; etc. die Ergebnisse abgerufen werden können.[25] Ohne &amp;lt;code&amp;gt;mysqlnd&amp;lt;/code&amp;gt; (bei Verwendung der älteren &amp;lt;code&amp;gt;libmysqlclient&amp;lt;/code&amp;gt;-Bibliothek) ist das Abrufen von Ergebnissen aus Prepared Statements deutlich umständlicher und erfordert die manuelle Bindung jeder einzelnen Ergebnisspalte an PHP-Variablen mittels &amp;lt;code&amp;gt;bind_result()&amp;lt;/code&amp;gt; und anschließendes &amp;lt;code&amp;gt;fetch()&amp;lt;/code&amp;gt;.[25] Da &amp;lt;code&amp;gt;mysqlnd&amp;lt;/code&amp;gt; heute der Standard ist, konzentrieren sich moderne Tutorials und Beispiele meist auf die &amp;lt;code&amp;gt;get_result()&amp;lt;/code&amp;gt;-Methode, aber es ist wichtig, diesen Hintergrund zu kennen, falls man auf ältere Systeme oder Konfigurationen trifft.&lt;br /&gt;
&lt;br /&gt;
=== 2.3 Veraltete &amp;lt;code&amp;gt;mysql_*&amp;lt;/code&amp;gt; Funktionen ===&lt;br /&gt;
&lt;br /&gt;
Die &amp;lt;code&amp;gt;mysql_*&amp;lt;/code&amp;gt; Funktionsfamilie (&amp;lt;code&amp;gt;mysql_connect&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;mysql_query&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;mysql_fetch_assoc&amp;lt;/code&amp;gt; etc.) war die ursprüngliche Methode, um in PHP mit MySQL-Datenbanken zu interagieren. Diese Erweiterung ist jedoch &amp;#039;&amp;#039;&amp;#039;stark veraltet&amp;#039;&amp;#039;&amp;#039; und sollte &amp;#039;&amp;#039;&amp;#039;unter keinen Umständen mehr für neue Projekte verwendet werden&amp;#039;&amp;#039;&amp;#039;.&lt;br /&gt;
&lt;br /&gt;
*   &amp;#039;&amp;#039;&amp;#039;Status:&amp;#039;&amp;#039;&amp;#039; Die &amp;lt;code&amp;gt;mysql_*&amp;lt;/code&amp;gt; Erweiterung wurde in &amp;#039;&amp;#039;&amp;#039;PHP 5.5&amp;#039;&amp;#039;&amp;#039; offiziell als &amp;quot;deprecated&amp;quot; (veraltet) markiert, was bedeutet, dass ihre Verwendung ab dieser Version Warnungen (&amp;lt;code&amp;gt;E_DEPRECATED&amp;lt;/code&amp;gt;) erzeugte.[28, 29] Mit der Veröffentlichung von &amp;#039;&amp;#039;&amp;#039;PHP 7.0&amp;#039;&amp;#039;&amp;#039; wurde die Erweiterung &amp;#039;&amp;#039;&amp;#039;vollständig entfernt&amp;#039;&amp;#039;&amp;#039;.[28, 29] PHP-Code, der diese Funktionen nutzt, führt auf PHP 7.0 oder neuer zu fatalen Fehlern und funktioniert nicht mehr.[28, 29]&lt;br /&gt;
*   &amp;#039;&amp;#039;&amp;#039;Gründe für die Entfernung:&amp;#039;&amp;#039;&amp;#039; Es gab zwingende Gründe für diesen Schritt:&lt;br /&gt;
**   &amp;#039;&amp;#039;&amp;#039;Gravierende Sicherheitsrisiken:&amp;#039;&amp;#039;&amp;#039; Der Hauptgrund war die Anfälligkeit für SQL-Injection-Angriffe. Die &amp;lt;code&amp;gt;mysql_*&amp;lt;/code&amp;gt; Funktionen boten keinen eingebauten Mechanismus wie Prepared Statements. Entwickler mussten Benutzereingaben manuell mit &amp;lt;code&amp;gt;mysql_real_escape_string()&amp;lt;/code&amp;gt; &amp;quot;escapen&amp;quot;, eine Methode, die leicht vergessen oder falsch angewendet werden konnte, was zu weit verbreiteten Sicherheitslücken führte.[29, 30] Das Fehlen von Prepared Statements in dieser alten Erweiterung ist direkt ursächlich für die hohe Zahl an SQL-Injection-Schwachstellen in älteren PHP-Anwendungen.[29]&lt;br /&gt;
**   &amp;#039;&amp;#039;&amp;#039;Fehlende Unterstützung moderner MySQL-Features:&amp;#039;&amp;#039;&amp;#039; Die Erweiterung wurde ursprünglich für MySQL Version 3.23 entwickelt und seitdem kaum weiterentwickelt.[29] Sie unterstützte viele wichtige Funktionen moderner MySQL-Versionen nicht, darunter Prepared Statements, Stored Procedures, Transaktionen, SSL-Verschlüsselung, Kompression, Multi-Statements und vollständige Zeichensatzunterstützung.[29]&lt;br /&gt;
**   &amp;#039;&amp;#039;&amp;#039;Schlechte Wartbarkeit:&amp;#039;&amp;#039;&amp;#039; Der Code der Erweiterung war veraltet und wurde immer schwieriger zu warten und an neue Versionen der MySQL-Client-Bibliotheken anzupassen.[29]&lt;br /&gt;
**   &amp;#039;&amp;#039;&amp;#039;Förderung unsicherer Praktiken:&amp;#039;&amp;#039;&amp;#039; Da viele alte Tutorials und Codebeispiele die &amp;lt;code&amp;gt;mysql_*&amp;lt;/code&amp;gt; Funktionen unsicher verwendeten, trug die fortgesetzte Verfügbarkeit zur Verbreitung schlechter und unsicherer Programmierpraktiken bei.[29]&lt;br /&gt;
*   &amp;#039;&amp;#039;&amp;#039;Verwendung heute:&amp;#039;&amp;#039;&amp;#039; Die klare Antwort lautet: &amp;#039;&amp;#039;&amp;#039;Nein&amp;#039;&amp;#039;&amp;#039;. Diese Funktionen existieren in modernen PHP-Versionen nicht mehr.[31, 30] Jede Anwendung, die sie noch verwendet, ist nicht nur unsicher, sondern auch inkompatibel mit aktueller PHP-Software. Eine Migration zu PDO oder MySQLi ist unumgänglich, um Sicherheit und Kompatibilität zu gewährleisten.[28, 29] Die erzwungene Migration durch die Entfernung in PHP 7.0 unterstreicht, wie wichtig es ist, Deprecation-Warnungen ernst zu nehmen und Code proaktiv zu aktualisieren, um zukünftige Probleme zu vermeiden.[29, 32]&lt;br /&gt;
&lt;br /&gt;
=== Tabelle 1: Vergleich der PHP-Datenbankerweiterungen für MySQL ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Merkmal !! PDO (PHP Data Objects) !! MySQLi (MySQL Improved) !! &amp;lt;code&amp;gt;mysql_*&amp;lt;/code&amp;gt; (Veraltet)&lt;br /&gt;
|- &amp;lt;td &amp;gt;&amp;lt;br&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
| aktiv=&amp;quot;&amp;quot; empfohlen=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| aktiv=&amp;quot;&amp;quot; empfohlen=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Entfernt (seit PHP 7.0) [29]&lt;br /&gt;
|- &amp;lt;td &amp;gt;&amp;lt;br&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
| objektorientiert=&amp;quot;&amp;quot; oo=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| oo=&amp;quot;&amp;quot; prozedural=&amp;quot;&amp;quot; 17=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Prozedural&lt;br /&gt;
|- &amp;lt;td &amp;gt;&amp;lt;br&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
| multi-datenbank=&amp;quot;&amp;quot; mysql=&amp;quot;&amp;quot; pgsql=&amp;quot;&amp;quot; sqlite=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| nur=&amp;quot;&amp;quot; mysql=&amp;quot;&amp;quot; 15=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Nur MySQL&lt;br /&gt;
|- &amp;lt;td &amp;gt;&amp;lt;br&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
| ja=&amp;quot;&amp;quot; nativ=&amp;quot;&amp;quot; empfohlen=&amp;quot;&amp;quot; emulation=&amp;quot;&amp;quot; m=&amp;quot;&amp;quot; glich=&amp;quot;&amp;quot; 1=&amp;quot;&amp;quot; 11=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| ja=&amp;quot;&amp;quot; nativ=&amp;quot;&amp;quot; 25=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Nein [29]&lt;br /&gt;
|- &lt;br /&gt;
| &amp;#039;&amp;#039;&amp;#039;SQL Injection Schutz&amp;#039;&amp;#039;&amp;#039; || sehr=&amp;quot;&amp;quot; gut=&amp;quot;&amp;quot; durch=&amp;quot;&amp;quot; prepared=&amp;quot;&amp;quot; statements=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Sehr gut (durch Prepared Statements) [25] || Schlecht (manuelles Escaping nötig) [29]&lt;br /&gt;
|- &amp;lt;td &amp;gt;&amp;lt;br&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
| gute=&amp;quot;&amp;quot; unterst=&amp;quot;&amp;quot; tzung=&amp;quot;&amp;quot; via=&amp;quot;&amp;quot; treiber=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Sehr gute Unterstützung (spezifisch) [15] || Veraltet, viele Features fehlen [29]&lt;br /&gt;
|- &amp;lt;td &amp;gt;&amp;lt;br&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
| hoch=&amp;quot;&amp;quot; 2=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| niedrig=&amp;quot;&amp;quot; mysql-spezifisch=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Niedrig&lt;br /&gt;
|- &amp;lt;td &amp;gt;&amp;lt;br&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
| exceptions=&amp;quot;&amp;quot; mwt-preservehtml=&amp;quot;&amp;quot; pdoexception=&amp;quot;&amp;quot; code=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| exceptions=&amp;quot;&amp;quot; mit=&amp;quot;&amp;quot; mwt-preservehtml=&amp;quot;&amp;quot; mysqli_report=&amp;quot;&amp;quot; code=&amp;quot;&amp;quot; fehlercodes=&amp;quot;&amp;quot; 18=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Fehlercodes / &amp;lt;code&amp;gt;mysql_error()&amp;lt;/code&amp;gt;&lt;br /&gt;
|- &amp;lt;td &amp;gt;&amp;lt;br&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;td standard=&amp;quot;&amp;quot; f=&amp;quot;&amp;quot; r=&amp;quot;&amp;quot; neue=&amp;quot;&amp;quot; projekte=&amp;quot;&amp;quot; &amp;gt;&amp;lt;br&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;td gut=&amp;quot;&amp;quot; f=&amp;quot;&amp;quot; r=&amp;quot;&amp;quot; reine=&amp;quot;&amp;quot; mysql-projekte=&amp;quot;&amp;quot; &amp;gt;&amp;lt;br&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
| &amp;#039;&amp;#039;&amp;#039;Nicht verwenden!&amp;#039;&amp;#039;&amp;#039; [29]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== (3) Grundlegende Datenbankoperationen (CRUD) ==&lt;br /&gt;
&lt;br /&gt;
Nachdem wir die verschiedenen Erweiterungen kennengelernt haben, wenden wir uns nun den grundlegenden Operationen zu, die man typischerweise mit einer Datenbank durchführt. Diese werden oft mit dem Akronym CRUD zusammengefasst:&lt;br /&gt;
&lt;br /&gt;
*   &amp;#039;&amp;#039;&amp;#039;C&amp;#039;&amp;#039;&amp;#039;reate: Neue Datensätze erstellen (SQL: &amp;lt;code&amp;gt;INSERT&amp;lt;/code&amp;gt;).&lt;br /&gt;
*   &amp;#039;&amp;#039;&amp;#039;R&amp;#039;&amp;#039;&amp;#039;ead: Vorhandene Datensätze lesen/abfragen (SQL: &amp;lt;code&amp;gt;SELECT&amp;lt;/code&amp;gt;).&lt;br /&gt;
*   &amp;#039;&amp;#039;&amp;#039;U&amp;#039;&amp;#039;&amp;#039;pdate: Bestehende Datensätze ändern (SQL: &amp;lt;code&amp;gt;UPDATE&amp;lt;/code&amp;gt;).&lt;br /&gt;
*   &amp;#039;&amp;#039;&amp;#039;D&amp;#039;&amp;#039;&amp;#039;elete: Datensätze löschen (SQL: &amp;lt;code&amp;gt;DELETE&amp;lt;/code&amp;gt;).&lt;br /&gt;
In den folgenden Abschnitten werden wir sehen, wie diese Operationen mit den modernen Erweiterungen PDO und MySQLi umgesetzt werden, wobei wir durchgehend &amp;#039;&amp;#039;&amp;#039;Prepared Statements&amp;#039;&amp;#039;&amp;#039; verwenden, um die Sicherheit zu gewährleisten.&lt;br /&gt;
&lt;br /&gt;
=== 3.1 Daten abfragen (SELECT) ===&lt;br /&gt;
&lt;br /&gt;
Das Abfragen von Daten ist eine der häufigsten Datenbankoperationen.&lt;br /&gt;
&lt;br /&gt;
*   &amp;#039;&amp;#039;&amp;#039;PDO:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
**   &amp;#039;&amp;#039;&amp;#039;Ohne Parameter:&amp;#039;&amp;#039;&amp;#039; Wenn die Abfrage keine variablen Teile enthält (z.B. keine &amp;lt;code&amp;gt;WHERE&amp;lt;/code&amp;gt;-Klausel mit Benutzereingaben), kann die &amp;lt;code&amp;gt;query()&amp;lt;/code&amp;gt;-Methode verwendet werden. Sie führt die SQL-Abfrage direkt aus und gibt ein &amp;lt;code&amp;gt;PDOStatement&amp;lt;/code&amp;gt;-Objekt zurück, das zur Ergebnisauswertung dient.[2, 33]&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Beispiel: Alle Benutzer auswählen&lt;br /&gt;
$stmt = $pdo-&amp;gt;query(&amp;quot;SELECT id, username FROM users&amp;quot;);&lt;br /&gt;
// $stmt kann nun zum Fetchen der Ergebnisse verwendet werden&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
**   &amp;#039;&amp;#039;&amp;#039;Mit Parametern (Prepared Statement):&amp;#039;&amp;#039;&amp;#039; Dies ist der Standardfall, wenn Benutzereingaben oder andere Variablen die Abfrage beeinflussen (z.B. in &amp;lt;code&amp;gt;WHERE&amp;lt;/code&amp;gt;- oder &amp;lt;code&amp;gt;LIMIT&amp;lt;/code&amp;gt;-Klauseln).&lt;br /&gt;
*   &amp;#039;&amp;#039;&amp;#039;Vorbereiten (&amp;lt;code&amp;gt;prepare&amp;lt;/code&amp;gt;):&amp;#039;&amp;#039;&amp;#039; Die SQL-Abfrage wird mit Platzhaltern (&amp;lt;code&amp;gt;?&amp;lt;/code&amp;gt; für positionale oder &amp;lt;code&amp;gt;:name&amp;lt;/code&amp;gt; für benannte Platzhalter) vorbereitet.[34, 33, 5]&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Beispiel: Benutzer mit bestimmter ID auswählen (positionaler Platzhalter)&lt;br /&gt;
$sql = &amp;quot;SELECT id, username, email FROM users WHERE id =?&amp;quot;;&lt;br /&gt;
$stmt = $pdo-&amp;gt;prepare($sql);&lt;br /&gt;
&lt;br /&gt;
// Beispiel: Benutzer mit bestimmtem Status auswählen (benannter Platzhalter)&lt;br /&gt;
$sql_named = &amp;quot;SELECT id, username FROM users WHERE status = :status&amp;quot;;&lt;br /&gt;
$stmt_named = $pdo-&amp;gt;prepare($sql_named);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
*#   &amp;#039;&amp;#039;&amp;#039;Ausführen (&amp;lt;code&amp;gt;execute&amp;lt;/code&amp;gt;):&amp;#039;&amp;#039;&amp;#039; Die vorbereitete Anweisung wird mit den tatsächlichen Werten ausgeführt. Die Werte werden als Array an die &amp;lt;code&amp;gt;execute()&amp;lt;/code&amp;gt;-Methode übergeben. Bei positionalen Platzhaltern entspricht die Reihenfolge im Array der Reihenfolge der &amp;lt;code&amp;gt;?&amp;lt;/code&amp;gt; in der SQL-Abfrage. Bei benannten Platzhaltern wird ein assoziatives Array verwendet, dessen Schlüssel den Platzhalternamen entsprechen (mit oder ohne führenden Doppelpunkt).[34, 33, 5]&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Ausführen für positionalen Platzhalter&lt;br /&gt;
$user_id = 123;&lt;br /&gt;
$stmt-&amp;gt;execute([$user_id]);&lt;br /&gt;
&lt;br /&gt;
// Ausführen für benannten Platzhalter&lt;br /&gt;
$user_status = &amp;#039;active&amp;#039;;&lt;br /&gt;
$stmt_named-&amp;gt;execute([&amp;#039;:status&amp;#039; =&amp;gt; $user_status]);&lt;br /&gt;
// oder: $stmt_named-&amp;gt;execute([&amp;#039;status&amp;#039; =&amp;gt; $user_status]);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
*#   &amp;#039;&amp;#039;&amp;#039;Alternative Bindung:&amp;#039;&amp;#039;&amp;#039; Optional können Parameter auch explizit vor dem &amp;lt;code&amp;gt;execute()&amp;lt;/code&amp;gt;-Aufruf mit &amp;lt;code&amp;gt;bindValue()&amp;lt;/code&amp;gt; (bindet einen Wert) oder &amp;lt;code&amp;gt;bindParam()&amp;lt;/code&amp;gt; (bindet eine Variable als Referenz) gebunden werden. Dies gibt mehr Kontrolle über den Datentyp (z.B. &amp;lt;code&amp;gt;PDO::PARAM_INT&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;PDO::PARAM_STR&amp;lt;/code&amp;gt;).[5, 13]&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$stmt-&amp;gt;bindValue(1, $user_id, PDO::PARAM_INT); // Position 1, Wert von $user_id als Integer&lt;br /&gt;
$stmt-&amp;gt;execute();&lt;br /&gt;
&lt;br /&gt;
$stmt_named-&amp;gt;bindParam(&amp;#039;:status&amp;#039;, $user_status, PDO::PARAM_STR); // Platzhalter :status, Variable $user_status als String&lt;br /&gt;
$stmt_named-&amp;gt;execute();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
**   &amp;#039;&amp;#039;&amp;#039;MySQLi:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
***   &amp;#039;&amp;#039;&amp;#039;Ohne Parameter:&amp;#039;&amp;#039;&amp;#039; Ähnlich wie bei PDO kann bei Abfragen ohne variable Teile die &amp;lt;code&amp;gt;query()&amp;lt;/code&amp;gt;-Methode (OO-Stil: &amp;lt;code&amp;gt;$mysqli-&amp;amp;gt;query()&amp;lt;/code&amp;gt;) oder die &amp;lt;code&amp;gt;mysqli_query()&amp;lt;/code&amp;gt;-Funktion (prozeduraler Stil: &amp;lt;code&amp;gt;mysqli_query($link, &amp;quot;...&amp;quot;)&amp;lt;/code&amp;gt;) verwendet werden. Sie geben ein &amp;lt;code&amp;gt;mysqli_result&amp;lt;/code&amp;gt;-Objekt zurück.[20, 25]&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// OO-Stil&lt;br /&gt;
$result = $mysqli-&amp;gt;query(&amp;quot;SELECT id, username FROM users&amp;quot;);&lt;br /&gt;
// Prozedural&lt;br /&gt;
$result = mysqli_query($link, &amp;quot;SELECT id, username FROM users&amp;quot;);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
***   &amp;#039;&amp;#039;&amp;#039;Mit Parametern (Prepared Statement):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
**   &amp;#039;&amp;#039;&amp;#039;Vorbereiten (&amp;lt;code&amp;gt;prepare&amp;lt;/code&amp;gt;):&amp;#039;&amp;#039;&amp;#039; Die SQL-Abfrage wird mit &amp;lt;code&amp;gt;?&amp;lt;/code&amp;gt;-Platzhaltern vorbereitet. Im OO-Stil &amp;lt;code&amp;gt;$stmt = $mysqli-&amp;amp;gt;prepare(&amp;quot;...&amp;quot;)&amp;lt;/code&amp;gt;, prozedural &amp;lt;code&amp;gt;$stmt = mysqli_prepare($link, &amp;quot;...&amp;quot;)&amp;lt;/code&amp;gt;.[25, 35]&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// OO-Stil&lt;br /&gt;
$sql = &amp;quot;SELECT id, username, email FROM users WHERE id =?&amp;quot;;&lt;br /&gt;
$stmt = $mysqli-&amp;gt;prepare($sql);&lt;br /&gt;
// Prozedural&lt;br /&gt;
$sql_proc = &amp;quot;SELECT id, username, email FROM users WHERE id =?&amp;quot;;&lt;br /&gt;
$stmt_proc = mysqli_prepare($link, $sql_proc);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
**#   &amp;#039;&amp;#039;&amp;#039;Parameter binden (&amp;lt;code&amp;gt;bind_param&amp;lt;/code&amp;gt;):&amp;#039;&amp;#039;&amp;#039; Die Variablen werden an die Platzhalter gebunden. Dies ist ein wesentlicher Unterschied zu PDOs &amp;lt;code&amp;gt;execute&amp;lt;/code&amp;gt;-Array-Bindung. &amp;lt;code&amp;gt;bind_param()&amp;lt;/code&amp;gt; erfordert einen String, der die Datentypen der zu bindenden Variablen angibt (z.B. &amp;quot;i&amp;quot; für Integer, &amp;quot;s&amp;quot; für String, &amp;quot;d&amp;quot; für Double/Float, &amp;quot;b&amp;quot; für Blob), gefolgt von den Variablen selbst.[25, 35]&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$user_id = 123;&lt;br /&gt;
// OO-Stil&lt;br /&gt;
$stmt-&amp;gt;bind_param(&amp;quot;i&amp;quot;, $user_id); // &amp;quot;i&amp;quot; für Integer&lt;br /&gt;
// Prozedural&lt;br /&gt;
mysqli_stmt_bind_param($stmt_proc, &amp;quot;i&amp;quot;, $user_id);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
**#   &amp;#039;&amp;#039;&amp;#039;Ausführen (&amp;lt;code&amp;gt;execute&amp;lt;/code&amp;gt;):&amp;#039;&amp;#039;&amp;#039; Die vorbereitete Anweisung wird ausgeführt. OO-Stil: &amp;lt;code&amp;gt;$stmt-&amp;amp;gt;execute()&amp;lt;/code&amp;gt;, prozedural: &amp;lt;code&amp;gt;mysqli_stmt_execute($stmt)&amp;lt;/code&amp;gt;.[25, 36, 35]&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// OO-Stil&lt;br /&gt;
$stmt-&amp;gt;execute();&lt;br /&gt;
// Prozedural&lt;br /&gt;
mysqli_stmt_execute($stmt_proc);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
**#   &amp;#039;&amp;#039;&amp;#039;Ergebnis holen:&amp;#039;&amp;#039;&amp;#039; Um die Ergebnisse eines Prepared Statements in MySQLi zu erhalten, ist die Methode &amp;lt;code&amp;gt;get_result()&amp;lt;/code&amp;gt; (OO) bzw. &amp;lt;code&amp;gt;mysqli_stmt_get_result()&amp;lt;/code&amp;gt; (prozedural) am bequemsten. Sie gibt ein Standard-&amp;lt;code&amp;gt;mysqli_result&amp;lt;/code&amp;gt;-Objekt zurück, das dann mit den üblichen Fetch-Methoden verarbeitet werden kann. &amp;#039;&amp;#039;&amp;#039;Wichtig:&amp;#039;&amp;#039;&amp;#039; Diese Methode erfordert den &amp;lt;code&amp;gt;mysqlnd&amp;lt;/code&amp;gt;-Treiber.[25, 15]&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// OO-Stil&lt;br /&gt;
$result = $stmt-&amp;gt;get_result();&lt;br /&gt;
// Prozedural&lt;br /&gt;
$result_proc = mysqli_stmt_get_result($stmt_proc);&lt;br /&gt;
// $result / $result_proc kann nun mit fetch_assoc() etc. verwendet werden.&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Die Alternative ohne &amp;lt;code&amp;gt;mysqlnd&amp;lt;/code&amp;gt; ist &amp;lt;code&amp;gt;bind_result()&amp;lt;/code&amp;gt;, bei der jede Ergebnisspalte an eine PHP-Variable gebunden wird, und &amp;lt;code&amp;gt;fetch()&amp;lt;/code&amp;gt; iterativ aufgerufen wird, um die Variablen zu füllen.[25] Dies ist deutlich umständlicher.&lt;br /&gt;
&lt;br /&gt;
=== 3.2 Ergebnisse abrufen und verarbeiten ===&lt;br /&gt;
&lt;br /&gt;
Nachdem eine &amp;lt;code&amp;gt;SELECT&amp;lt;/code&amp;gt;-Abfrage erfolgreich ausgeführt wurde, müssen die zurückgegebenen Daten aus dem Ergebnisobjekt (&amp;lt;code&amp;gt;PDOStatement&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;mysqli_result&amp;lt;/code&amp;gt;) abgerufen (&amp;quot;gefetched&amp;quot;) werden.&lt;br /&gt;
&lt;br /&gt;
***   &amp;#039;&amp;#039;&amp;#039;Zeilenweise Iteration (&amp;lt;code&amp;gt;fetch&amp;lt;/code&amp;gt; im Loop):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
Dies ist der klassische Weg, um Ergebnisse zu verarbeiten, insbesondere wenn jede Zeile einzeln behandelt werden muss oder wenn die Ergebnismenge sehr groß ist.&lt;br /&gt;
****   &amp;#039;&amp;#039;&amp;#039;PDO:&amp;#039;&amp;#039;&amp;#039; Die &amp;lt;code&amp;gt;fetch()&amp;lt;/code&amp;gt;-Methode des &amp;lt;code&amp;gt;PDOStatement&amp;lt;/code&amp;gt;-Objekts wird wiederholt in einer &amp;lt;code&amp;gt;while&amp;lt;/code&amp;gt;-Schleife aufgerufen. &amp;lt;code&amp;gt;fetch()&amp;lt;/code&amp;gt; gibt bei jedem Aufruf die nächste Zeile zurück (im Format des eingestellten Fetch-Modus, z.B. &amp;lt;code&amp;gt;PDO::FETCH_ASSOC&amp;lt;/code&amp;gt;) und &amp;lt;code&amp;gt;false&amp;lt;/code&amp;gt; (oder &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt; je nach Modus/Version), wenn keine weiteren Zeilen vorhanden sind.[2, 33]&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$stmt = $pdo-&amp;gt;prepare(&amp;quot;SELECT name, email FROM users WHERE status =?&amp;quot;);&lt;br /&gt;
$stmt-&amp;gt;execute([&amp;#039;active&amp;#039;]);&lt;br /&gt;
while ($row = $stmt-&amp;gt;fetch(PDO::FETCH_ASSOC)) {&lt;br /&gt;
    echo &amp;quot;Name: &amp;quot;. htmlspecialchars($row[&amp;#039;name&amp;#039;]). &amp;quot;, E-Mail: &amp;quot;. htmlspecialchars($row[&amp;#039;email&amp;#039;]). &amp;quot;&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
****   &amp;#039;&amp;#039;&amp;#039;MySQLi:&amp;#039;&amp;#039;&amp;#039; Die &amp;lt;code&amp;gt;fetch_assoc()&amp;lt;/code&amp;gt;-Methode (oder &amp;lt;code&amp;gt;fetch_row()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;fetch_object()&amp;lt;/code&amp;gt;, etc.) des &amp;lt;code&amp;gt;mysqli_result&amp;lt;/code&amp;gt;-Objekts wird in einer &amp;lt;code&amp;gt;while&amp;lt;/code&amp;gt;-Schleife verwendet. Sie gibt die nächste Zeile als Array (oder Objekt) zurück oder &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt;, wenn das Ende erreicht ist.[25, 15]&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// OO-Stil (nach $result = $stmt-&amp;gt;get_result();)&lt;br /&gt;
while ($row = $result-&amp;gt;fetch_assoc()) {&lt;br /&gt;
    echo &amp;quot;Name: &amp;quot;. htmlspecialchars($row[&amp;#039;name&amp;#039;]). &amp;quot;, E-Mail: &amp;quot;. htmlspecialchars($row[&amp;#039;email&amp;#039;]). &amp;quot;&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Prozedural (nach $result = mysqli_stmt_get_result($stmt);)&lt;br /&gt;
while ($row = mysqli_fetch_assoc($result)) {&lt;br /&gt;
    echo &amp;quot;Name: &amp;quot;. htmlspecialchars($row[&amp;#039;name&amp;#039;]). &amp;quot;, E-Mail: &amp;quot;. htmlspecialchars($row[&amp;#039;email&amp;#039;]). &amp;quot;&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Dieser Ansatz ist &amp;#039;&amp;#039;&amp;#039;speichereffizient&amp;#039;&amp;#039;&amp;#039;, da immer nur eine Zeile gleichzeitig im PHP-Speicher gehalten wird. Dies ist besonders wichtig bei Abfragen, die potenziell Tausende oder Millionen von Zeilen zurückgeben könnten.[37]&lt;br /&gt;
&lt;br /&gt;
***   &amp;#039;&amp;#039;&amp;#039;Alle Ergebnisse auf einmal abrufen (&amp;lt;code&amp;gt;fetchAll&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;fetch_all&amp;lt;/code&amp;gt;):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
Für kleinere bis mittlere Ergebnismengen ist es oft bequemer, alle Zeilen auf einmal in ein PHP-Array zu laden.&lt;br /&gt;
****   &amp;#039;&amp;#039;&amp;#039;PDO:&amp;#039;&amp;#039;&amp;#039; Die &amp;lt;code&amp;gt;fetchAll()&amp;lt;/code&amp;gt;-Methode des &amp;lt;code&amp;gt;PDOStatement&amp;lt;/code&amp;gt;-Objekts gibt ein Array zurück, das alle Ergebniszeilen enthält.[33, 38] Der Fetch-Modus (z.B. &amp;lt;code&amp;gt;PDO::FETCH_ASSOC&amp;lt;/code&amp;gt;) kann als Argument übergeben werden.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$stmt = $pdo-&amp;gt;prepare(&amp;quot;SELECT name, email FROM users WHERE status =?&amp;quot;);&lt;br /&gt;
$stmt-&amp;gt;execute([&amp;#039;active&amp;#039;]);&lt;br /&gt;
$results = $stmt-&amp;gt;fetchAll(PDO::FETCH_ASSOC);&lt;br /&gt;
// $results ist nun ein Array von assoziativen Arrays&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
****   &amp;#039;&amp;#039;&amp;#039;MySQLi:&amp;#039;&amp;#039;&amp;#039; Die &amp;lt;code&amp;gt;fetch_all()&amp;lt;/code&amp;gt;-Methode des &amp;lt;code&amp;gt;mysqli_result&amp;lt;/code&amp;gt;-Objekts (verfügbar mit &amp;lt;code&amp;gt;mysqlnd&amp;lt;/code&amp;gt;) tut dasselbe. Der Modus (&amp;lt;code&amp;gt;MYSQLI_ASSOC&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;MYSQLI_NUM&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;MYSQLI_BOTH&amp;lt;/code&amp;gt;) wird als Argument übergeben.[25, 39]&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// OO-Stil (nach $result = $stmt-&amp;gt;get_result();)&lt;br /&gt;
$results = $result-&amp;gt;fetch_all(MYSQLI_ASSOC);&lt;br /&gt;
&lt;br /&gt;
// Prozedural (nach $result = mysqli_stmt_get_result($stmt);)&lt;br /&gt;
$results = mysqli_fetch_all($result, MYSQLI_ASSOC);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Der &amp;#039;&amp;#039;&amp;#039;Vorteil&amp;#039;&amp;#039;&amp;#039; dieser Methode liegt in der einfacheren Weiterverarbeitung des Ergebnis-Arrays mit Standard-PHP-Array-Funktionen wie &amp;lt;code&amp;gt;foreach&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;count&amp;lt;/code&amp;gt;.[40] Der &amp;#039;&amp;#039;&amp;#039;Nachteil&amp;#039;&amp;#039;&amp;#039; ist der potenziell hohe Speicherverbrauch, da alle Daten gleichzeitig in den PHP-Speicher geladen werden. Bei sehr großen Ergebnismengen kann dies zu Speicherlimit-Fehlern führen.[37]&lt;br /&gt;
&lt;br /&gt;
***   &amp;#039;&amp;#039;&amp;#039;Fetch-Modi / Fetch-Funktionen (Struktur der Ergebniszeile):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
Beide Erweiterungen bieten verschiedene Möglichkeiten, die Struktur der abgerufenen Datenzeilen zu bestimmen:&lt;br /&gt;
&lt;br /&gt;
****   &amp;#039;&amp;#039;&amp;#039;PDO Fetch-Modi (Auswahl):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
*****   &amp;lt;code&amp;gt;PDO::FETCH_ASSOC&amp;lt;/code&amp;gt;: Gibt ein assoziatives Array zurück (&amp;lt;code&amp;gt;[&amp;#039;spaltenname&amp;#039; =&amp;amp;gt; &amp;#039;wert&amp;#039;,... ]&amp;lt;/code&amp;gt;).[9, 33, 41, 42] Sehr gebräuchlich.&lt;br /&gt;
*****   &amp;lt;code&amp;gt;PDO::FETCH_NUM&amp;lt;/code&amp;gt;: Gibt ein numerisch indiziertes Array zurück (&amp;lt;code&amp;gt;[0 =&amp;amp;gt; &amp;#039;wert1&amp;#039;, 1 =&amp;amp;gt; &amp;#039;wert2&amp;#039;,...]&amp;lt;/code&amp;gt;).[9, 41, 42]&lt;br /&gt;
*****   &amp;lt;code&amp;gt;PDO::FETCH_BOTH&amp;lt;/code&amp;gt; (Standard): Gibt ein Array zurück, das sowohl assoziative als auch numerische Indizes enthält.[41, 42] Verbraucht mehr Speicher.&lt;br /&gt;
*****   &amp;lt;code&amp;gt;PDO::FETCH_OBJ&amp;lt;/code&amp;gt;: Gibt ein anonymes &amp;lt;code&amp;gt;stdClass&amp;lt;/code&amp;gt;-Objekt zurück, bei dem die Spaltennamen Eigenschaften sind (&amp;lt;code&amp;gt;$row-&amp;amp;gt;spaltenname&amp;lt;/code&amp;gt;).[9, 41, 42, 43]&lt;br /&gt;
*****   &amp;lt;code&amp;gt;PDO::FETCH_CLASS&amp;lt;/code&amp;gt;: Erstellt eine Instanz einer angegebenen Klasse und weist die Spaltenwerte den Klasseneigenschaften zu.[38, 41, 42, 43]&lt;br /&gt;
*****   &amp;lt;code&amp;gt;PDO::FETCH_COLUMN&amp;lt;/code&amp;gt;: Ruft nur den Wert einer einzelnen Spalte aus der nächsten Zeile ab.[38]&lt;br /&gt;
*****   &amp;lt;code&amp;gt;PDO::FETCH_LAZY&amp;lt;/code&amp;gt;: Eine Kombination aus &amp;lt;code&amp;gt;FETCH_BOTH&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;FETCH_OBJ&amp;lt;/code&amp;gt;, die Werte erst bei Zugriff lädt.[9, 41, 42]&lt;br /&gt;
****   &amp;#039;&amp;#039;&amp;#039;MySQLi Fetch-Funktionen (Auswahl):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
*****   &amp;lt;code&amp;gt;fetch_assoc()&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;mysqli_fetch_assoc()&amp;lt;/code&amp;gt;: Gibt ein assoziatives Array zurück.[17, 25, 39, 15] Sehr gebräuchlich.&lt;br /&gt;
*****   &amp;lt;code&amp;gt;fetch_row()&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;mysqli_fetch_row()&amp;lt;/code&amp;gt;: Gibt ein numerisch indiziertes Array zurück.[39, 35]&lt;br /&gt;
*****   &amp;lt;code&amp;gt;fetch_array()&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;mysqli_fetch_array()&amp;lt;/code&amp;gt;: Gibt je nach übergebenem Modus (&amp;lt;code&amp;gt;MYSQLI_ASSOC&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;MYSQLI_NUM&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;MYSQLI_BOTH&amp;lt;/code&amp;gt;) ein assoziatives, numerisches oder beides enthaltendes Array zurück.[39, 35]&lt;br /&gt;
*****   &amp;lt;code&amp;gt;fetch_object()&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;mysqli_fetch_object()&amp;lt;/code&amp;gt;: Gibt ein &amp;lt;code&amp;gt;stdClass&amp;lt;/code&amp;gt;-Objekt zurück oder eine Instanz einer angegebenen Klasse.[21, 15, 35]&lt;br /&gt;
*****   &amp;lt;code&amp;gt;fetch_column()&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;mysqli_fetch_column()&amp;lt;/code&amp;gt;: Ruft den Wert einer einzelnen Spalte ab (erst ab PHP 8.1 verfügbar).[24]&lt;br /&gt;
*****   &amp;lt;code&amp;gt;fetch_all()&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;mysqli_fetch_all()&amp;lt;/code&amp;gt;: Ruft alle Zeilen auf einmal ab, Modus (&amp;lt;code&amp;gt;MYSQLI_ASSOC&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;MYSQLI_NUM&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;MYSQLI_BOTH&amp;lt;/code&amp;gt;) wählbar.[21, 25, 39]&lt;br /&gt;
&lt;br /&gt;
=== Tabelle 2: Ausgewählte PDO Fetch-Modi ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Konstante !! Rückgabeformat !! Beschreibung&lt;br /&gt;
|- &lt;br /&gt;
|  &amp;lt;code&amp;gt;PDO::FETCH_ASSOC&amp;lt;/code&amp;gt;&lt;br /&gt;
| assoziatives=&amp;quot;&amp;quot; array=&amp;quot;&amp;quot; mwt-preservehtml=&amp;quot;&amp;quot; | [&amp;quot; col&amp;#039;=&amp;quot;&amp;amp;gt;&amp;quot; &amp;#039;val&amp;#039;]&amp;amp;lt;=&amp;quot;&amp;quot; code&amp;amp;gt;&amp;#039;)=&amp;quot;&amp;quot; &amp;amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
| Gängigste Form, Spaltennamen als Schlüssel.&lt;br /&gt;
|- &lt;br /&gt;
| mwt-preservehtml=&amp;quot;&amp;quot; pdo::fetch_num=&amp;quot;&amp;quot; code=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| numerisches=&amp;quot;&amp;quot; array=&amp;quot;&amp;quot; mwt-preservehtml=&amp;quot;&amp;quot; 0=&amp;quot;&amp;quot; val=&amp;quot;&amp;quot; code=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Spaltenwerte über numerischen Index erreichbar.&lt;br /&gt;
|- &lt;br /&gt;
| mwt-preservehtml=&amp;quot;&amp;quot; pdo::fetch_both=&amp;quot;&amp;quot; code=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| array=&amp;quot;&amp;quot; mit=&amp;quot;&amp;quot; assoz=&amp;quot;&amp;quot; num=&amp;quot;&amp;quot; indizes=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Standard, enthält doppelte Informationen, höherer Speicherbedarf.&lt;br /&gt;
|- &lt;br /&gt;
| mwt-preservehtml=&amp;quot;&amp;quot; pdo::fetch_obj=&amp;quot;&amp;quot; code=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| anonymes=&amp;quot;&amp;quot; mwt-preservehtml=&amp;quot;&amp;quot; stdclass=&amp;quot;&amp;quot; code=&amp;quot;&amp;quot; -objekt=&amp;quot;&amp;quot; row-=&amp;quot;&amp;quot; col=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Spaltennamen werden zu Objekteigenschaften.&lt;br /&gt;
|- &lt;br /&gt;
|  &amp;lt;code&amp;gt;PDO::FETCH_CLASS&amp;lt;/code&amp;gt; || objekt=&amp;quot;&amp;quot; einer=&amp;quot;&amp;quot; spezifischen=&amp;quot;&amp;quot; klasse=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Mappt Spalten auf Eigenschaften einer vordefinierten Klasse.&lt;br /&gt;
|- &lt;br /&gt;
|  &amp;lt;code&amp;gt;PDO::FETCH_COLUMN&amp;lt;/code&amp;gt; || einzelner=&amp;quot;&amp;quot; wert=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Gibt nur den Wert der ersten (oder angegebenen) Spalte der nächsten Zeile zurück.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Tabelle 3: Ausgewählte MySQLi Fetch-Funktionen ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Funktion (OO / Prozedural) !! Rückgabeformat !! Beschreibung&lt;br /&gt;
|- &lt;br /&gt;
| mwt-preservehtml=&amp;quot;&amp;quot; fetch_assoc=&amp;quot;&amp;quot; code=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| assoziatives=&amp;quot;&amp;quot; array=&amp;quot;&amp;quot; mwt-preservehtml=&amp;quot;&amp;quot; col=&amp;quot;&amp;quot; val=&amp;quot;&amp;quot; code=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Gängigste Form, Spaltennamen als Schlüssel.&lt;br /&gt;
|- &lt;br /&gt;
| mwt-preservehtml=&amp;quot;&amp;quot; fetch_row=&amp;quot;&amp;quot; code=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| numerisches=&amp;quot;&amp;quot; array=&amp;quot;&amp;quot; mwt-preservehtml=&amp;quot;&amp;quot; 0=&amp;quot;&amp;quot; val=&amp;quot;&amp;quot; code=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Spaltenwerte über numerischen Index erreichbar.&lt;br /&gt;
|- &lt;br /&gt;
| mwt-preservehtml=&amp;quot;&amp;quot; fetch_array=&amp;quot;&amp;quot; code=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| array=&amp;quot;&amp;quot; assoz=&amp;quot;&amp;quot; num=&amp;quot;&amp;quot; oder=&amp;quot;&amp;quot; beide=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Flexibel je nach Modus (&amp;lt;code&amp;gt;MYSQLI_ASSOC&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;MYSQLI_NUM&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;MYSQLI_BOTH&amp;lt;/code&amp;gt;).&lt;br /&gt;
|- &lt;br /&gt;
|  &amp;lt;code&amp;gt;fetch_object()&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;mysqli_fetch_object()&amp;lt;/code&amp;gt;&lt;br /&gt;
| mwt-preservehtml=&amp;quot;&amp;quot; | stdClass&amp;quot; -objekt=&amp;quot;&amp;quot; oder=&amp;quot;&amp;quot; spezifische=&amp;quot;&amp;quot; klasse=&amp;quot;&amp;quot; &amp;amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
| Spaltennamen werden zu Objekteigenschaften.&lt;br /&gt;
|- &lt;br /&gt;
| mwt-preservehtml=&amp;quot;&amp;quot; fetch_all=&amp;quot;&amp;quot; code=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| array=&amp;quot;&amp;quot; von=&amp;quot;&amp;quot; arrays=&amp;quot;&amp;quot; assoz=&amp;quot;&amp;quot; num=&amp;quot;&amp;quot; oder=&amp;quot;&amp;quot; beide=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Holt alle Ergebniszeilen auf einmal (benötigt &amp;lt;code&amp;gt;mysqlnd&amp;lt;/code&amp;gt;).&lt;br /&gt;
|- &lt;br /&gt;
|  &amp;lt;code&amp;gt;fetch_column()&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;mysqli_fetch_column()&amp;lt;/code&amp;gt; || einzelner=&amp;quot;&amp;quot; wert=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Gibt nur den Wert der ersten (oder angegebenen) Spalte zurück (PHP 8.1+).&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
***   &amp;#039;&amp;#039;&amp;#039;Zugriff auf Spaltenwerte:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
Der Zugriff auf die Daten innerhalb einer abgerufenen Zeile hängt vom gewählten Fetch-Modus ab:&lt;br /&gt;
****   Assoziatives Array: &amp;lt;code&amp;gt;$row[&amp;#039;spaltenname&amp;#039;]&amp;lt;/code&amp;gt;&lt;br /&gt;
****   Numerisches Array: &amp;lt;code&amp;gt;$row&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;$row[1]&amp;lt;/code&amp;gt;,...&lt;br /&gt;
****   Objekt: &amp;lt;code&amp;gt;$row-&amp;amp;gt;spaltenname&amp;lt;/code&amp;gt;&lt;br /&gt;
***   &amp;#039;&amp;#039;&amp;#039;Iteration mit &amp;lt;code&amp;gt;foreach&amp;lt;/code&amp;gt;:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
Nachdem Ergebnisse mit &amp;lt;code&amp;gt;fetchAll()&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;fetch_all()&amp;lt;/code&amp;gt; in ein Array geladen wurden, ist &amp;lt;code&amp;gt;foreach&amp;lt;/code&amp;gt; die natürliche Wahl zur Iteration [37, 40, 44]:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$results = $stmt-&amp;gt;fetchAll(PDO::FETCH_ASSOC);&lt;br /&gt;
foreach ($results as $row) {&lt;br /&gt;
    echo &amp;quot;Name: &amp;quot;. htmlspecialchars($row[&amp;#039;name&amp;#039;]). &amp;quot;&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Seit PHP 8 bietet PDO eine modernere Alternative, die die Lesbarkeit von &amp;lt;code&amp;gt;foreach&amp;lt;/code&amp;gt; mit der Speichereffizienz von &amp;lt;code&amp;gt;fetch()&amp;lt;/code&amp;gt; kombiniert, indem direkt über das &amp;lt;code&amp;gt;PDOStatement&amp;lt;/code&amp;gt;-Objekt iteriert wird [37, 45]:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Nur PHP 8+ mit PDO&lt;br /&gt;
$stmt = $pdo-&amp;gt;query(&amp;quot;SELECT name, email FROM users&amp;quot;);&lt;br /&gt;
foreach ($stmt as $row) { // PDOStatement ist direkt traversierbar&lt;br /&gt;
    echo &amp;quot;Name: &amp;quot;. htmlspecialchars($row[&amp;#039;name&amp;#039;]). &amp;quot;&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Diese Methode ist besonders elegant und effizient für große Ergebnismengen in modernen PHP-Versionen.&lt;br /&gt;
&lt;br /&gt;
=== 3.3 Daten manipulieren (INSERT, UPDATE, DELETE) ===&lt;br /&gt;
&lt;br /&gt;
Das Einfügen, Aktualisieren und Löschen von Daten sind kritische Operationen, da sie den Datenbestand verändern. Hier ist die Verwendung von &amp;#039;&amp;#039;&amp;#039;Prepared Statements zwingend erforderlich&amp;#039;&amp;#039;&amp;#039;, um die Integrität und Sicherheit der Datenbank zu gewährleisten, insbesondere wenn Benutzereingaben beteiligt sind.&lt;br /&gt;
&lt;br /&gt;
***   &amp;#039;&amp;#039;&amp;#039;PDO:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
****   &amp;#039;&amp;#039;&amp;#039;Vorbereiten (&amp;lt;code&amp;gt;prepare&amp;lt;/code&amp;gt;):&amp;#039;&amp;#039;&amp;#039; Die SQL-Anweisung (&amp;lt;code&amp;gt;INSERT&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;UPDATE&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;DELETE&amp;lt;/code&amp;gt;) wird mit Platzhaltern (&amp;lt;code&amp;gt;?&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;:name&amp;lt;/code&amp;gt;) für die einzufügenden/zu aktualisierenden Werte sowie für die Kriterien in &amp;lt;code&amp;gt;WHERE&amp;lt;/code&amp;gt;-Klauseln (bei &amp;lt;code&amp;gt;UPDATE&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;DELETE&amp;lt;/code&amp;gt;) vorbereitet.[5, 46, 47, 48]&lt;br /&gt;
****   &amp;#039;&amp;#039;&amp;#039;Ausführen (&amp;lt;code&amp;gt;execute&amp;lt;/code&amp;gt;):&amp;#039;&amp;#039;&amp;#039; Die Anweisung wird mit einem Array ausgeführt, das die Werte für die Platzhalter enthält.[5, 46, 47, 48]&lt;br /&gt;
****   &amp;#039;&amp;#039;&amp;#039;Ergebnis prüfen:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
*****   &amp;#039;&amp;#039;Anzahl betroffener Zeilen:&amp;#039;&amp;#039; &amp;lt;code&amp;gt;$stmt-&amp;amp;gt;rowCount()&amp;lt;/code&amp;gt; gibt die Anzahl der durch &amp;lt;code&amp;gt;UPDATE&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;DELETE&amp;lt;/code&amp;gt; betroffenen Zeilen zurück. Bei &amp;lt;code&amp;gt;INSERT&amp;lt;/code&amp;gt; ist der Rückgabewert oft nicht zuverlässig oder standardisiert.[8, 9, 5, 49] Das Verhalten kann je nach Datenbanktreiber und Konfiguration (z.B. &amp;lt;code&amp;gt;PDO::MYSQL_ATTR_FOUND_ROWS&amp;lt;/code&amp;gt;) variieren.[9]&lt;br /&gt;
*****   &amp;#039;&amp;#039;ID des letzten eingefügten Datensatzes:&amp;#039;&amp;#039; Bei &amp;lt;code&amp;gt;INSERT&amp;lt;/code&amp;gt;-Anweisungen in Tabellen mit einer &amp;lt;code&amp;gt;AUTO_INCREMENT&amp;lt;/code&amp;gt;-Spalte liefert &amp;lt;code&amp;gt;$pdo-&amp;amp;gt;lastInsertId()&amp;lt;/code&amp;gt; die ID des neu eingefügten Datensatzes zurück.[8, 33, 48]&lt;br /&gt;
****   &amp;#039;&amp;#039;&amp;#039;Code-Beispiele (PDO):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php// --- INSERT ---&lt;br /&gt;
$name = $_POST[&amp;#039;name&amp;#039;];&lt;br /&gt;
$email = $_POST[&amp;#039;email&amp;#039;];&lt;br /&gt;
$status = &amp;#039;pending&amp;#039;;&lt;br /&gt;
&lt;br /&gt;
$sql_insert = &amp;quot;INSERT INTO users (username, email, status) VALUES (:name, :email, :status)&amp;quot;;&lt;br /&gt;
$stmt_insert = $pdo-&amp;gt;prepare($sql_insert);&lt;br /&gt;
$stmt_insert-&amp;gt;execute([&amp;#039;:name&amp;#039; =&amp;gt; $name, &amp;#039;:email&amp;#039; =&amp;gt; $email, &amp;#039;:status&amp;#039; =&amp;gt; $status]);&lt;br /&gt;
$lastId = $pdo-&amp;gt;lastInsertId();&lt;br /&gt;
echo &amp;quot;Neuer Benutzer mit ID $lastId eingefügt.&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
// --- UPDATE ---&lt;br /&gt;
$new_status = &amp;#039;active&amp;#039;;&lt;br /&gt;
$user_id_to_update = $lastId;&lt;br /&gt;
&lt;br /&gt;
$sql_update = &amp;quot;UPDATE users SET status = :status WHERE id = :id&amp;quot;;&lt;br /&gt;
$stmt_update = $pdo-&amp;gt;prepare($sql_update);&lt;br /&gt;
$stmt_update-&amp;gt;execute([&amp;#039;:status&amp;#039; =&amp;gt; $new_status, &amp;#039;:id&amp;#039; =&amp;gt; $user_id_to_update]);&lt;br /&gt;
$affectedRows = $stmt_update-&amp;gt;rowCount();&lt;br /&gt;
echo &amp;quot;$affectedRows Benutzerstatus aktualisiert.&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
// --- DELETE ---&lt;br /&gt;
$user_id_to_delete = $lastId;&lt;br /&gt;
&lt;br /&gt;
$sql_delete = &amp;quot;DELETE FROM users WHERE id = :id&amp;quot;;&lt;br /&gt;
$stmt_delete = $pdo-&amp;gt;prepare($sql_delete);&lt;br /&gt;
$stmt_delete-&amp;gt;execute([&amp;#039;:id&amp;#039; =&amp;gt; $user_id_to_delete]);&lt;br /&gt;
$affectedRowsDel = $stmt_delete-&amp;gt;rowCount();&lt;br /&gt;
echo &amp;quot;$affectedRowsDel Benutzer gelöscht.&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
***   &amp;#039;&amp;#039;&amp;#039;MySQLi:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
****   &amp;#039;&amp;#039;&amp;#039;Vorbereiten (&amp;lt;code&amp;gt;prepare&amp;lt;/code&amp;gt;):&amp;#039;&amp;#039;&amp;#039; Die SQL-Anweisung wird mit &amp;lt;code&amp;gt;?&amp;lt;/code&amp;gt;-Platzhaltern vorbereitet (OO: &amp;lt;code&amp;gt;$mysqli-&amp;amp;gt;prepare()&amp;lt;/code&amp;gt;, Proc: &amp;lt;code&amp;gt;mysqli_prepare()&amp;lt;/code&amp;gt;).[25, 50, 15, 35]&lt;br /&gt;
****   &amp;#039;&amp;#039;&amp;#039;Parameter binden (&amp;lt;code&amp;gt;bind_param&amp;lt;/code&amp;gt;):&amp;#039;&amp;#039;&amp;#039; Variablen werden explizit mit Typenangabe gebunden (OO: &amp;lt;code&amp;gt;$stmt-&amp;amp;gt;bind_param()&amp;lt;/code&amp;gt;, Proc: &amp;lt;code&amp;gt;mysqli_stmt_bind_param()&amp;lt;/code&amp;gt;).[25, 50, 15, 35] Die Notwendigkeit, den Datentyp für jede Variable anzugeben, ist ein wesentlicher Unterschied zur einfacheren Array-Übergabe bei PDOs &amp;lt;code&amp;gt;execute()&amp;lt;/code&amp;gt;.&lt;br /&gt;
****   &amp;#039;&amp;#039;&amp;#039;Ausführen (&amp;lt;code&amp;gt;execute&amp;lt;/code&amp;gt;):&amp;#039;&amp;#039;&amp;#039; Die Anweisung wird ausgeführt (OO: &amp;lt;code&amp;gt;$stmt-&amp;amp;gt;execute()&amp;lt;/code&amp;gt;, Proc: &amp;lt;code&amp;gt;mysqli_stmt_execute()&amp;lt;/code&amp;gt;).[25, 36, 50, 15, 35]&lt;br /&gt;
****   &amp;#039;&amp;#039;&amp;#039;Ergebnis prüfen:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
*****   &amp;#039;&amp;#039;Anzahl betroffener Zeilen:&amp;#039;&amp;#039; Über die Eigenschaft &amp;lt;code&amp;gt;$mysqli-&amp;amp;gt;affected_rows&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;$stmt-&amp;amp;gt;affected_rows&amp;lt;/code&amp;gt; (OO) bzw. die Funktionen &amp;lt;code&amp;gt;mysqli_affected_rows($link)&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;mysqli_stmt_affected_rows($stmt)&amp;lt;/code&amp;gt; (Proc).[18, 26, 15, 35]&lt;br /&gt;
*****   &amp;#039;&amp;#039;ID des letzten eingefügten Datensatzes:&amp;#039;&amp;#039; Über &amp;lt;code&amp;gt;$mysqli-&amp;amp;gt;insert_id&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;$stmt-&amp;amp;gt;insert_id&amp;lt;/code&amp;gt; (OO) bzw. &amp;lt;code&amp;gt;mysqli_insert_id($link)&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;mysqli_stmt_insert_id($stmt)&amp;lt;/code&amp;gt; (Proc).[26, 15] Die Verwendung von &amp;lt;code&amp;gt;$mysqli-&amp;amp;gt;insert_id&amp;lt;/code&amp;gt; wird oft als zuverlässiger angesehen, da sie auch nach dem Schließen des Statements funktioniert.[15]&lt;br /&gt;
****   &amp;#039;&amp;#039;&amp;#039;Code-Beispiele (MySQLi OO):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php// --- INSERT ---&lt;br /&gt;
$name = $_POST[&amp;#039;name&amp;#039;];&lt;br /&gt;
$email = $_POST[&amp;#039;email&amp;#039;];&lt;br /&gt;
$status = &amp;#039;pending&amp;#039;;&lt;br /&gt;
&lt;br /&gt;
$sql_insert = &amp;quot;INSERT INTO users (username, email, status) VALUES (?,?,?)&amp;quot;;&lt;br /&gt;
$stmt_insert = $mysqli-&amp;gt;prepare($sql_insert);&lt;br /&gt;
// Typen: s=string, s=string, s=string&lt;br /&gt;
$stmt_insert-&amp;gt;bind_param(&amp;quot;sss&amp;quot;, $name, $email, $status);&lt;br /&gt;
$stmt_insert-&amp;gt;execute();&lt;br /&gt;
$lastId = $mysqli-&amp;gt;insert_id;&lt;br /&gt;
echo &amp;quot;Neuer Benutzer mit ID $lastId eingefügt (MySQLi OO).&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
$stmt_insert-&amp;gt;close(); // Statement schließen&lt;br /&gt;
&lt;br /&gt;
// --- UPDATE ---&lt;br /&gt;
$new_status = &amp;#039;active&amp;#039;;&lt;br /&gt;
$user_id_to_update = $lastId;&lt;br /&gt;
&lt;br /&gt;
$sql_update = &amp;quot;UPDATE users SET status =? WHERE id =?&amp;quot;;&lt;br /&gt;
$stmt_update = $mysqli-&amp;gt;prepare($sql_update);&lt;br /&gt;
// Typen: s=string, i=integer&lt;br /&gt;
$stmt_update-&amp;gt;bind_param(&amp;quot;si&amp;quot;, $new_status, $user_id_to_update);&lt;br /&gt;
$stmt_update-&amp;gt;execute();&lt;br /&gt;
$affectedRows = $mysqli-&amp;gt;affected_rows; // Oder $stmt_update-&amp;gt;affected_rows vor close()&lt;br /&gt;
echo &amp;quot;$affectedRows Benutzerstatus aktualisiert (MySQLi OO).&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
$stmt_update-&amp;gt;close();&lt;br /&gt;
&lt;br /&gt;
// --- DELETE ---&lt;br /&gt;
$user_id_to_delete = $lastId;&lt;br /&gt;
&lt;br /&gt;
$sql_delete = &amp;quot;DELETE FROM users WHERE id =?&amp;quot;;&lt;br /&gt;
$stmt_delete = $mysqli-&amp;gt;prepare($sql_delete);&lt;br /&gt;
// Typ: i=integer&lt;br /&gt;
$stmt_delete-&amp;gt;bind_param(&amp;quot;i&amp;quot;, $user_id_to_delete);&lt;br /&gt;
$stmt_delete-&amp;gt;execute();&lt;br /&gt;
$affectedRowsDel = $mysqli-&amp;gt;affected_rows;&lt;br /&gt;
echo &amp;quot;$affectedRowsDel Benutzer gelöscht (MySQLi OO).&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
$stmt_delete-&amp;gt;close();&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&amp;#039;&amp;#039;(Anmerkung: Prozedurale Beispiele folgen demselben Muster unter Verwendung der &amp;lt;code&amp;gt;mysqli_*&amp;lt;/code&amp;gt;-Funktionen wie &amp;lt;code&amp;gt;mysqli_prepare&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;mysqli_stmt_bind_param&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;mysqli_stmt_execute&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;mysqli_insert_id&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;mysqli_affected_rows&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;mysqli_stmt_close&amp;lt;/code&amp;gt;)&amp;#039;&amp;#039;.&lt;br /&gt;
&lt;br /&gt;
== (4) Sicherheit: SQL Injection verstehen und verhindern ==&lt;br /&gt;
&lt;br /&gt;
Eines der größten Sicherheitsrisiken bei der Interaktion mit Datenbanken über Webanwendungen ist die SQL Injection (SQLi). Das Verständnis dieser Bedrohung und ihrer Abwehr ist für jeden PHP-Entwickler unerlässlich.&lt;br /&gt;
&lt;br /&gt;
=== 4.1 Was ist SQL Injection? ===&lt;br /&gt;
&lt;br /&gt;
SQL Injection ist eine Angriffstechnik, bei der ein Angreifer versucht, bösartigen oder manipulierten SQL-Code über Benutzereingabefelder einer Webanwendung (z.B. Formulare, URL-Parameter, HTTP-Header, Cookies) in die Datenbankabfragen einzuschleusen.[51, 52, 53, 54, 55, 56, 57]&lt;br /&gt;
&lt;br /&gt;
Die Attacke funktioniert, indem Schwachstellen in der Anwendung ausgenutzt werden, bei denen Benutzereingaben direkt und ohne ausreichende Validierung oder Maskierung (Escaping) in SQL-Abfragestrings eingefügt (konkateniert) werden.[5, 51, 55] Dadurch wird die notwendige Trennung zwischen dem eigentlichen SQL-Befehl (Kontrollebene) und den Daten (Datenebene) aufgehoben.[51] Der Angreifer kann spezielle Zeichen (wie Anführungszeichen &amp;lt;code&amp;gt;&amp;#039;&amp;lt;/code&amp;gt;, Semikolons &amp;lt;code&amp;gt;;&amp;lt;/code&amp;gt; oder Kommentare &amp;lt;code&amp;gt;--&amp;lt;/code&amp;gt;) verwenden, um den ursprünglichen SQL-Befehl vorzeitig zu beenden, zu verändern oder um eigene, zusätzliche SQL-Befehle anzuhängen.[51, 53]&lt;br /&gt;
&lt;br /&gt;
Ein klassisches Beispiel ist eine Login-Abfrage, die etwa so aufgebaut ist:&lt;br /&gt;
&amp;lt;code&amp;gt;$sql = &amp;quot;SELECT * FROM users WHERE username = &amp;#039;&amp;quot;. $userInputUsername. &amp;quot;&amp;#039; AND password = &amp;#039;&amp;quot;. $userInputPassword. &amp;quot;&amp;#039;&amp;quot;;&amp;lt;/code&amp;gt;&lt;br /&gt;
Wenn ein Angreifer als Benutzernamen &amp;lt;code&amp;gt;&amp;#039; OR &amp;#039;1&amp;#039;=&amp;#039;1&amp;lt;/code&amp;gt; eingibt, wird die resultierende SQL-Abfrage zu:&lt;br /&gt;
&amp;lt;code&amp;gt;SELECT * FROM users WHERE username = &amp;#039;&amp;#039; OR &amp;#039;1&amp;#039;=&amp;#039;1&amp;#039; AND password = &amp;#039;...&amp;#039;&amp;lt;/code&amp;gt;&lt;br /&gt;
Da &amp;lt;code&amp;gt;&amp;#039;1&amp;#039;=&amp;#039;1&amp;#039;&amp;lt;/code&amp;gt; immer wahr ist, würde diese Abfrage (je nach genauer Struktur und Datenbank) möglicherweise alle Benutzer zurückgeben oder den Login ohne korrektes Passwort ermöglichen.[5, 15, 55]&lt;br /&gt;
&lt;br /&gt;
=== 4.2 Warum ist SQL Injection gefährlich? (Auswirkungen) ===&lt;br /&gt;
&lt;br /&gt;
Die potenziellen Folgen einer erfolgreichen SQL-Injection-Attacke sind gravierend und vielfältig:&lt;br /&gt;
&lt;br /&gt;
***   &amp;#039;&amp;#039;&amp;#039;Datendiebstahl:&amp;#039;&amp;#039;&amp;#039; Angreifer können auf sensible Daten zugreifen und diese auslesen, darunter Benutzerdaten, Passwörter (oft als Hashes, die aber geknackt werden können), Kreditkarteninformationen, Geschäftsgeheimnisse und andere vertrauliche Informationen.[51, 53, 56]&lt;br /&gt;
***   &amp;#039;&amp;#039;&amp;#039;Datenmanipulation/-zerstörung:&amp;#039;&amp;#039;&amp;#039; Angreifer können bestehende Daten in der Datenbank verändern (z.B. Preise in einem Shop, Benutzerrollen) oder komplette Datensätze oder sogar Tabellen löschen (&amp;lt;code&amp;gt;DELETE&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;DROP TABLE&amp;lt;/code&amp;gt;).[51, 53, 56]&lt;br /&gt;
***   &amp;#039;&amp;#039;&amp;#039;Umgehung der Authentifizierung/Identitätsdiebstahl:&amp;#039;&amp;#039;&amp;#039; Wie im Beispiel oben gezeigt, können Angreifer Login-Mechanismen umgehen und sich unautorisiert Zugang verschaffen, möglicherweise sogar mit administrativen Rechten.[51, 56]&lt;br /&gt;
***   &amp;#039;&amp;#039;&amp;#039;Denial of Service (DoS):&amp;#039;&amp;#039;&amp;#039; Durch das Löschen wichtiger Daten oder das Ausführen ressourcenintensiver Abfragen kann die Verfügbarkeit der Anwendung oder der Datenbank beeinträchtigt werden.[51, 56]&lt;br /&gt;
***   &amp;#039;&amp;#039;&amp;#039;Kompromittierung des Servers:&amp;#039;&amp;#039;&amp;#039; In manchen Fällen können Angreifer über SQL Injection Betriebssystembefehle auf dem Datenbankserver ausführen oder vollen administrativen Zugriff auf die Datenbank erlangen, was weitreichende Konsequenzen haben kann.[51, 55, 56]&lt;br /&gt;
***   &amp;#039;&amp;#039;&amp;#039;Reputationsschaden und rechtliche Folgen:&amp;#039;&amp;#039;&amp;#039; Erfolgreiche Angriffe, insbesondere wenn sie zu Datenlecks führen, können das Vertrauen der Benutzer schwer beschädigen und zu empfindlichen Strafen gemäß Datenschutzgesetzen führen.[53]&lt;br /&gt;
&lt;br /&gt;
=== 4.3 Prävention durch Prepared Statements (Primäre Methode) ===&lt;br /&gt;
&lt;br /&gt;
Die mit Abstand &amp;#039;&amp;#039;&amp;#039;wichtigste und effektivste Methode&amp;#039;&amp;#039;&amp;#039; zur Verhinderung von SQL Injection ist die konsequente Verwendung von &amp;#039;&amp;#039;&amp;#039;Prepared Statements&amp;#039;&amp;#039;&amp;#039; (parametrisierten Abfragen).[4, 25, 5, 58, 35]&lt;br /&gt;
&lt;br /&gt;
Das Grundprinzip von Prepared Statements ist die &amp;#039;&amp;#039;&amp;#039;strikte Trennung von SQL-Befehlsstruktur und den Daten (Parametern)&amp;#039;&amp;#039;&amp;#039;.[4, 25, 5, 58, 35] Der Ablauf ist typischerweise zweistufig:&lt;br /&gt;
**#   &amp;#039;&amp;#039;&amp;#039;Prepare (Vorbereiten):&amp;#039;&amp;#039;&amp;#039; Die SQL-Anweisung wird mit Platzhaltern anstelle der tatsächlichen Werte an den Datenbankserver gesendet. Der Server parst die Anweisung, prüft die Syntax und bereitet einen Ausführungsplan vor, ohne die endgültigen Daten zu kennen.&lt;br /&gt;
**#   &amp;#039;&amp;#039;&amp;#039;Execute (Ausführen) mit Parameterbindung:&amp;#039;&amp;#039;&amp;#039; Die tatsächlichen Werte (Parameter) werden separat an den Server gesendet und an die vorbereiteten Platzhalter gebunden. Der Server führt nun die vorkompilierte Anweisung mit den sicher eingefügten Werten aus.&lt;br /&gt;
Der entscheidende Punkt ist, dass die Datenbank die separat gesendeten Parameter &amp;#039;&amp;#039;&amp;#039;immer als Datenwerte&amp;#039;&amp;#039;&amp;#039; behandelt, niemals als Teil des SQL-Befehls.[5, 15, 58] Selbst wenn ein Parameter bösartigen SQL-Code enthält (z.B. &amp;lt;code&amp;gt;&amp;#039; OR &amp;#039;1&amp;#039;=&amp;#039;1&amp;lt;/code&amp;gt;), wird dieser nicht ausgeführt, sondern als einfacher String-Wert behandelt, der z.B. in eine Spalte eingefügt oder in einer &amp;lt;code&amp;gt;WHERE&amp;lt;/code&amp;gt;-Klausel verglichen wird. Diese Trennung macht Prepared Statements immun gegen SQL Injection.&lt;br /&gt;
&lt;br /&gt;
***   &amp;#039;&amp;#039;&amp;#039;Platzhalter:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
****   &amp;#039;&amp;#039;&amp;#039;PDO:&amp;#039;&amp;#039;&amp;#039; Unterstützt sowohl positionale Platzhalter (&amp;lt;code&amp;gt;?&amp;lt;/code&amp;gt;) als auch benannte Platzhalter (&amp;lt;code&amp;gt;:name&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;:email&amp;lt;/code&amp;gt;, etc.).[5, 13] Benannte Platzhalter machen den Code oft lesbarer, besonders bei vielen Parametern.&lt;br /&gt;
****   &amp;#039;&amp;#039;&amp;#039;MySQLi:&amp;#039;&amp;#039;&amp;#039; Unterstützt ausschließlich positionale Platzhalter (&amp;lt;code&amp;gt;?&amp;lt;/code&amp;gt;).[25]&lt;br /&gt;
***   &amp;#039;&amp;#039;&amp;#039;Parameterbindung:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
****   &amp;#039;&amp;#039;&amp;#039;PDO:&amp;#039;&amp;#039;&amp;#039; Bietet flexible Bindungsoptionen:&lt;br /&gt;
*****   &amp;#039;&amp;#039;Implizit über &amp;lt;code&amp;gt;execute()&amp;lt;/code&amp;gt;-Array:&amp;#039;&amp;#039; Die einfachste und häufigste Methode. Ein Array mit den Werten wird an &amp;lt;code&amp;gt;execute()&amp;lt;/code&amp;gt; übergeben. PDO kümmert sich um das korrekte Escaping und die Typbehandlung (oft werden Werte sicher als Strings behandelt).[4, 12, 5, 13]&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$stmt-&amp;gt;execute([$wert1, $wert2]); // für? Platzhalter&lt;br /&gt;
$stmt-&amp;gt;execute([&amp;#039;:name&amp;#039; =&amp;gt; $wert1, &amp;#039;:id&amp;#039; =&amp;gt; $wert2]); // für :name Platzhalter&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
*****   &amp;#039;&amp;#039;Explizit mit &amp;lt;code&amp;gt;bindValue()&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;bindParam()&amp;lt;/code&amp;gt;:&amp;#039;&amp;#039; Erlaubt die explizite Angabe des Datentyps (z.B. &amp;lt;code&amp;gt;PDO::PARAM_INT&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;PDO::PARAM_STR&amp;lt;/code&amp;gt;) für jeden Parameter, was in manchen Fällen für Klarheit oder spezifische Datenbankoptimierungen sorgen kann.[5, 13] &amp;lt;code&amp;gt;bindParam&amp;lt;/code&amp;gt; bindet eine Variable (ihr Wert wird erst bei &amp;lt;code&amp;gt;execute()&amp;lt;/code&amp;gt; gelesen), &amp;lt;code&amp;gt;bindValue&amp;lt;/code&amp;gt; bindet den aktuellen Wert einer Variablen oder einen Literalwert.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$stmt-&amp;gt;bindValue(&amp;#039;:id&amp;#039;, $user_id, PDO::PARAM_INT);&lt;br /&gt;
$stmt-&amp;gt;bindParam(&amp;#039;:name&amp;#039;, $user_name, PDO::PARAM_STR);&lt;br /&gt;
$stmt-&amp;gt;execute();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
****   &amp;#039;&amp;#039;&amp;#039;MySQLi:&amp;#039;&amp;#039;&amp;#039; Erfordert immer die explizite Bindung mittels &amp;lt;code&amp;gt;bind_param()&amp;lt;/code&amp;gt;.&lt;br /&gt;
*****   &amp;lt;code&amp;gt;bind_param()&amp;lt;/code&amp;gt; (OO) / &amp;lt;code&amp;gt;mysqli_stmt_bind_param()&amp;lt;/code&amp;gt; (Proc): Nimmt als ersten Parameter einen String entgegen, der die Datentypen aller nachfolgend übergebenen Variablen definiert.[25, 50, 15, 58]&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// OO-Stil&lt;br /&gt;
$stmt-&amp;gt;bind_param(&amp;quot;ssi&amp;quot;, $name, $email, $id); // String, String, Integer&lt;br /&gt;
// Prozedural&lt;br /&gt;
mysqli_stmt_bind_param($stmt, &amp;quot;ssi&amp;quot;, $name, $email, $id);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
=== Tabelle 4: MySQLi &amp;lt;code&amp;gt;bind_param()&amp;lt;/code&amp;gt; Typen-String ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Typ-Buchstabe !! PHP-Datentyp (typisch) !! Beschreibung&lt;br /&gt;
|- &lt;br /&gt;
| mwt-preservehtml=&amp;quot;&amp;quot; i=&amp;quot;&amp;quot; code=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| integer=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Integer (Ganzzahl)&lt;br /&gt;
|- &lt;br /&gt;
| mwt-preservehtml=&amp;quot;&amp;quot; d=&amp;quot;&amp;quot; code=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| double=&amp;quot;&amp;quot; float=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Double (Gleitkommazahl)&lt;br /&gt;
|- &lt;br /&gt;
| mwt-preservehtml=&amp;quot;&amp;quot; s=&amp;quot;&amp;quot; code=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| string=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| String (Zeichenkette)&lt;br /&gt;
|- &lt;br /&gt;
| mwt-preservehtml=&amp;quot;&amp;quot; b=&amp;quot;&amp;quot; code=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| string=&amp;quot;&amp;quot; |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| BLOB (Binary Large Object), als String gesendet&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
***   &amp;#039;&amp;#039;&amp;#039;Detaillierte Code-Beispiele zur Prävention:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
Die Beispiele für INSERT, UPDATE und DELETE in Abschnitt 3.3 demonstrieren bereits die korrekte Anwendung von Prepared Statements mit PDO und MySQLi zur Verhinderung von SQL Injection. Es ist entscheidend, dieses Muster konsequent anzuwenden, wann immer externe Daten in SQL-Abfragen einfließen.&lt;br /&gt;
&lt;br /&gt;
=== 4.4 Zusätzliche Schutzmaßnahmen (Defense-in-Depth) ===&lt;br /&gt;
&lt;br /&gt;
Obwohl Prepared Statements der wichtigste Schutz gegen SQL Injection sind, sollte Sicherheit immer als mehrschichtiges Konzept (&amp;quot;Defense-in-Depth&amp;quot;) betrachtet werden.[5] Zusätzliche Maßnahmen erhöhen die Robustheit der Anwendung:&lt;br /&gt;
&lt;br /&gt;
***   &amp;#039;&amp;#039;&amp;#039;Eingabevalidierung und -bereinigung:&amp;#039;&amp;#039;&amp;#039; Überprüfen Sie &amp;#039;&amp;#039;alle&amp;#039;&amp;#039; Benutzereingaben serverseitig, bevor sie überhaupt in die Nähe der Datenbank kommen.[4, 59, 20, 5] Stellen Sie sicher, dass die Daten dem erwarteten Typ (Zahl, String, E-Mail-Format etc.) und Format entsprechen. Verwenden Sie dafür PHP-Filterfunktionen wie &amp;lt;code&amp;gt;filter_var()&amp;lt;/code&amp;gt; oder reguläre Ausdrücke.[4, 5] Dies ist eine wichtige zweite Verteidigungslinie, die ungültige oder unerwartete Daten frühzeitig abfängt. Es ersetzt jedoch &amp;#039;&amp;#039;nicht&amp;#039;&amp;#039; die Notwendigkeit von Prepared Statements.&lt;br /&gt;
***   &amp;#039;&amp;#039;&amp;#039;Prinzip der geringsten Rechte (Least Privilege):&amp;#039;&amp;#039;&amp;#039; Konfigurieren Sie den Datenbankbenutzer, den Ihre PHP-Anwendung verwendet, mit den absolut minimal notwendigen Berechtigungen.[20, 5] Wenn die Anwendung nur Daten lesen und schreiben muss, geben Sie ihr keine Rechte zum Ändern der Tabellenstruktur (&amp;lt;code&amp;gt;ALTER TABLE&amp;lt;/code&amp;gt;) oder zum Löschen von Tabellen (&amp;lt;code&amp;gt;DROP TABLE&amp;lt;/code&amp;gt;). Beschränken Sie den Zugriff auf die benötigten Datenbanken und Tabellen. Verwenden Sie niemals den &amp;lt;code&amp;gt;root&amp;lt;/code&amp;gt;- oder Admin-Benutzer der Datenbank für die Anwendung. Dies begrenzt den potenziellen Schaden, falls doch eine Schwachstelle ausgenutzt wird.&lt;br /&gt;
***   &amp;#039;&amp;#039;&amp;#039;Sichere Fehlerbehandlung:&amp;#039;&amp;#039;&amp;#039; Konfigurieren Sie PHP und Ihre Datenbankerweiterung so, dass detaillierte Fehlermeldungen (insbesondere solche, die SQL-Code oder Datenbankstrukturinformationen enthalten) niemals dem Endbenutzer angezeigt werden.[20, 5] Solche Informationen sind für Angreifer wertvoll. Loggen Sie Fehler stattdessen in eine sichere Datei auf dem Server, die nur für Administratoren zugänglich ist. Verwenden Sie &amp;lt;code&amp;gt;PDO::ERRMODE_EXCEPTION&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;mysqli_report&amp;lt;/code&amp;gt; für strukturierte Fehlerbehandlung.&lt;br /&gt;
***   &amp;#039;&amp;#039;&amp;#039;Output Escaping:&amp;#039;&amp;#039;&amp;#039; Obwohl nicht direkt zur Verhinderung von SQL Injection, ist es wichtig, Daten, die aus der Datenbank gelesen und im HTML-Kontext ausgegeben werden, korrekt zu escapen (z.B. mit &amp;lt;code&amp;gt;htmlspecialchars()&amp;lt;/code&amp;gt;), um Cross-Site Scripting (XSS)-Angriffe zu verhindern.&lt;br /&gt;
Die Kombination dieser Maßnahmen – mit Prepared Statements als Kernstück – bietet einen robusten Schutz gegen SQL Injection und andere verwandte Angriffe.&lt;br /&gt;
&lt;br /&gt;
== (5) Nützliche PHP Array-Funktionen für Datenbankergebnisse ==&lt;br /&gt;
&lt;br /&gt;
Wenn Daten aus der Datenbank abgerufen werden, insbesondere mit Methoden wie &amp;lt;code&amp;gt;fetchAll()&amp;lt;/code&amp;gt; (PDO) oder &amp;lt;code&amp;gt;fetch_all()&amp;lt;/code&amp;gt; (MySQLi), liegen die Ergebnisse typischerweise als PHP-Array vor (meist ein Array von assoziativen Arrays, wobei jedes innere Array eine Ergebniszeile darstellt). PHP bietet eine Reihe nützlicher Funktionen zur Verarbeitung solcher Arrays.&lt;br /&gt;
&lt;br /&gt;
***   &amp;#039;&amp;#039;&amp;#039;Iteration mit &amp;lt;code&amp;gt;foreach&amp;lt;/code&amp;gt;:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
Dies ist die Standardmethode, um die einzelnen Zeilen und Spalten eines Ergebnis-Arrays zu durchlaufen.[37, 40, 44]&lt;br /&gt;
****   &amp;#039;&amp;#039;&amp;#039;Syntax:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Iteration über die Zeilen (jedes $row ist ein assoziatives Array)&lt;br /&gt;
foreach ($results as $row) {&lt;br /&gt;
    echo &amp;quot;ID: &amp;quot;. $row[&amp;#039;id&amp;#039;]. &amp;quot;, Name: &amp;quot;. htmlspecialchars($row[&amp;#039;name&amp;#039;]). &amp;quot;&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Iteration über Zeilen mit Zugriff auf den numerischen Index der Zeile&lt;br /&gt;
foreach ($results as $index =&amp;gt; $row) {&lt;br /&gt;
    echo &amp;quot;Zeile &amp;quot;. $index. &amp;quot;: ID = &amp;quot;. $row[&amp;#039;id&amp;#039;]. &amp;quot;&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
****   &amp;#039;&amp;#039;&amp;#039;Code-Beispiel:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php// Annahme: $results enthält das Ergebnis von $stmt-&amp;gt;fetchAll(PDO::FETCH_ASSOC);&lt;br /&gt;
$results = [&amp;#039;id&amp;#039; =&amp;gt; 1, &amp;#039;name&amp;#039; =&amp;gt; &amp;#039;Alice&amp;#039;, &amp;#039;email&amp;#039; =&amp;gt; &amp;#039;alice@example.com&amp;#039;],&lt;br /&gt;
   ;&lt;br /&gt;
&lt;br /&gt;
echo &amp;quot;&amp;lt;ul&amp;gt;&amp;quot;;&lt;br /&gt;
foreach ($results as $row) {&lt;br /&gt;
    echo &amp;quot;&amp;lt;li&amp;gt;Benutzer #&amp;quot;. $row[&amp;#039;id&amp;#039;]. &amp;quot;: &amp;quot;. htmlspecialchars($row[&amp;#039;name&amp;#039;]);&lt;br /&gt;
    echo &amp;quot; (&amp;quot;. htmlspecialchars($row[&amp;#039;email&amp;#039;]). &amp;quot;)&amp;lt;/li&amp;gt;&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
echo &amp;quot;&amp;lt;/ul&amp;gt;&amp;quot;;&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
***   &amp;#039;&amp;#039;&amp;#039;Zählen von Ergebnissen mit &amp;lt;code&amp;gt;count()&amp;lt;/code&amp;gt;:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
Die Funktion &amp;lt;code&amp;gt;count()&amp;lt;/code&amp;gt; gibt die Anzahl der Elemente in einem Array zurück. Wenn sie auf ein mit &amp;lt;code&amp;gt;fetchAll()&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;fetch_all()&amp;lt;/code&amp;gt; erzeugtes Ergebnis-Array angewendet wird, liefert sie die Anzahl der abgerufenen Zeilen.[40, 60, 61]&lt;br /&gt;
****   &amp;#039;&amp;#039;&amp;#039;Syntax:&amp;#039;&amp;#039;&amp;#039; &amp;lt;code&amp;gt;$anzahlZeilen = count($results);&amp;lt;/code&amp;gt;.[60]&lt;br /&gt;
****   &amp;#039;&amp;#039;&amp;#039;Wichtiger Hinweis:&amp;#039;&amp;#039;&amp;#039; &amp;lt;code&amp;gt;count()&amp;lt;/code&amp;gt; funktioniert nur zuverlässig für die Gesamtzahl der Zeilen, wenn &amp;#039;&amp;#039;alle&amp;#039;&amp;#039; Zeilen zuvor in ein Array geladen wurden (also nach &amp;lt;code&amp;gt;fetchAll&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;fetch_all&amp;lt;/code&amp;gt;). Wenn Sie Ergebnisse zeilenweise mit &amp;lt;code&amp;gt;fetch()&amp;lt;/code&amp;gt; in einer &amp;lt;code&amp;gt;while&amp;lt;/code&amp;gt;-Schleife verarbeiten, müssen Sie einen manuellen Zähler innerhalb der Schleife inkrementieren, um die Anzahl der verarbeiteten Zeilen zu ermitteln.[40] Alternativ können &amp;lt;code&amp;gt;PDOStatement::rowCount()&amp;lt;/code&amp;gt; [8, 9] oder &amp;lt;code&amp;gt;mysqli_result::$num_rows&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;mysqli_stmt::$num_rows&amp;lt;/code&amp;gt; [26, 15] verwendet werden, um die Anzahl der von einer &amp;lt;code&amp;gt;SELECT&amp;lt;/code&amp;gt;-Abfrage zurückgegebenen Zeilen zu erhalten, &amp;#039;&amp;#039;bevor&amp;#039;&amp;#039; die Daten gefetched werden (das Verhalten von &amp;lt;code&amp;gt;rowCount&amp;lt;/code&amp;gt; kann jedoch bei anderen Anweisungstypen wie &amp;lt;code&amp;gt;UPDATE&amp;lt;/code&amp;gt; variieren).&lt;br /&gt;
****   &amp;#039;&amp;#039;&amp;#039;Code-Beispiel:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
// Annahme: $results von fetchAll()&lt;br /&gt;
$results = [ /*... wie oben... */ ];&lt;br /&gt;
$anzahl = count($results);&lt;br /&gt;
echo &amp;quot;Anzahl der Benutzer (via fetchAll/count): $anzahl&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
// Vergleich: Zählen bei zeilenweiser Verarbeitung&lt;br /&gt;
$stmt = $pdo-&amp;gt;query(&amp;quot;SELECT id FROM users&amp;quot;); // Annahme: PDO-Statement&lt;br /&gt;
$manueller_zaehler = 0;&lt;br /&gt;
while ($row = $stmt-&amp;gt;fetch()) {&lt;br /&gt;
    $manueller_zaehler++;&lt;br /&gt;
}&lt;br /&gt;
echo &amp;quot;Anzahl der Benutzer (via while/fetch): $manueller_zaehler&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
// Alternative: rowCount() nach SELECT (PDO)&lt;br /&gt;
$stmt_count = $pdo-&amp;gt;query(&amp;quot;SELECT id FROM users&amp;quot;);&lt;br /&gt;
$rowCountResult = $stmt_count-&amp;gt;rowCount(); // Kann bei manchen DBs/Treibern funktionieren&lt;br /&gt;
echo &amp;quot;Anzahl der Benutzer (via rowCount nach SELECT): $rowCountResult&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
***   &amp;#039;&amp;#039;&amp;#039;(Optional) Weitere nützliche Array-Funktionen:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
****   &amp;lt;code&amp;gt;array_column(array $input, mixed $column_key [, mixed $index_key = null ])&amp;lt;/code&amp;gt;: Extrahiert alle Werte aus einer einzelnen Spalte des Ergebnis-Arrays und gibt sie als neues, eindimensionales Array zurück.[62] Sehr nützlich, um z.B. eine Liste aller Benutzer-IDs oder E-Mail-Adressen zu erhalten.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Annahme: $results enthält das Array von assoziativen Arrays&lt;br /&gt;
$alle_emails = array_column($results, &amp;#039;email&amp;#039;);&lt;br /&gt;
// $alle_emails ist nun [&amp;#039;alice@example.com&amp;#039;, &amp;#039;bob@example.com&amp;#039;]&lt;br /&gt;
print_r($alle_emails);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
****   &amp;lt;code&amp;gt;array_map(callable $callback, array $array1 [, array...$arrays ])&amp;lt;/code&amp;gt;: Wendet eine benutzerdefinierte Funktion (&amp;lt;code&amp;gt;callback&amp;lt;/code&amp;gt;) auf jedes Element eines Arrays an und gibt ein neues Array mit den Ergebnissen zurück.[63] Nützlich zur Transformation von Daten (z.B. Formatierung, Bereinigung).&lt;br /&gt;
****   &amp;lt;code&amp;gt;array_filter(array $array [, callable $callback [, int $flag = 0 ]])&amp;lt;/code&amp;gt;: Filtert Elemente eines Arrays basierend auf einer Callback-Funktion.[61] Nützlich, um nur bestimmte Zeilen aus dem Ergebnis-Array zu behalten (obwohl dies oft effizienter direkt in der SQL-Abfrage geschieht).&lt;br /&gt;
****   &amp;lt;code&amp;gt;array_count_values(array $array)&amp;lt;/code&amp;gt;: Zählt, wie oft jeder eindeutige Wert in einem eindimensionalen Array vorkommt.[63] Kann nützlich sein, wenn man z.B. mit &amp;lt;code&amp;gt;array_column&amp;lt;/code&amp;gt; eine Spalte extrahiert hat und die Häufigkeit bestimmter Werte (z.B. Status) wissen möchte.&lt;br /&gt;
Diese Funktionen, insbesondere &amp;lt;code&amp;gt;foreach&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;count&amp;lt;/code&amp;gt;, sind grundlegende Werkzeuge bei der Verarbeitung von Datenbankergebnissen in PHP.&lt;br /&gt;
&lt;br /&gt;
== (6) Zusammenfassung und Best Practices ==&lt;br /&gt;
&lt;br /&gt;
Die Integration von Datenbanken ist ein zentraler Aspekt der PHP-Webentwicklung. Dieses Kapitel hat die Grundlagen für die Interaktion mit MySQL-Datenbanken gelegt. Hier sind die wichtigsten Punkte und Best Practices zusammengefasst:&lt;br /&gt;
&lt;br /&gt;
***   &amp;#039;&amp;#039;&amp;#039;Wahl der Erweiterung:&amp;#039;&amp;#039;&amp;#039; Verwenden Sie ausschließlich die modernen Erweiterungen &amp;#039;&amp;#039;&amp;#039;PDO&amp;#039;&amp;#039;&amp;#039; oder &amp;#039;&amp;#039;&amp;#039;MySQLi&amp;#039;&amp;#039;&amp;#039;.[2, 29, 5, 15]&lt;br /&gt;
****   &amp;#039;&amp;#039;&amp;#039;PDO&amp;#039;&amp;#039;&amp;#039; wird generell für neue Projekte empfohlen, da es Datenbankunabhängigkeit bietet und eine konsistentere, oft als einfacher empfundene API (insbesondere bei der Parameterbindung) hat.[16, 3, 64]&lt;br /&gt;
****   &amp;#039;&amp;#039;&amp;#039;MySQLi&amp;#039;&amp;#039;&amp;#039; ist eine gute Wahl, wenn ausschließlich mit MySQL gearbeitet wird und spezifische MySQL-Funktionen benötigt werden oder wenn eine prozedurale Schnittstelle bevorzugt wird.[15]&lt;br /&gt;
***   &amp;#039;&amp;#039;&amp;#039;Vermeidung von &amp;lt;code&amp;gt;mysql_*&amp;lt;/code&amp;gt;:&amp;#039;&amp;#039;&amp;#039; Die alten &amp;lt;code&amp;gt;mysql_*&amp;lt;/code&amp;gt; Funktionen sind seit PHP 7.0 entfernt und &amp;#039;&amp;#039;&amp;#039;dürfen nicht mehr verwendet werden&amp;#039;&amp;#039;&amp;#039;. Sie sind unsicher und inkompatibel.[28, 29]&lt;br /&gt;
***   &amp;#039;&amp;#039;&amp;#039;Prepared Statements:&amp;#039;&amp;#039;&amp;#039; Dies ist die &amp;#039;&amp;#039;&amp;#039;wichtigste Sicherheitsmaßnahme&amp;#039;&amp;#039;&amp;#039;. Verwenden Sie Prepared Statements &amp;#039;&amp;#039;&amp;#039;immer&amp;#039;&amp;#039;&amp;#039; dann, wenn externe Daten (Benutzereingaben, URL-Parameter etc.) Teil einer SQL-Abfrage werden (insbesondere bei &amp;lt;code&amp;gt;WHERE&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;INSERT&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;UPDATE&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;DELETE&amp;lt;/code&amp;gt;).[2, 25, 5] Sie verhindern SQL Injection zuverlässig durch die Trennung von Code und Daten.&lt;br /&gt;
***   &amp;#039;&amp;#039;&amp;#039;Sichere Fehlerbehandlung:&amp;#039;&amp;#039;&amp;#039; Konfigurieren Sie PDO (&amp;lt;code&amp;gt;PDO::ATTR_ERRMODE =&amp;amp;gt; PDO::ERRMODE_EXCEPTION&amp;lt;/code&amp;gt;) oder MySQLi (&amp;lt;code&amp;gt;mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT)&amp;lt;/code&amp;gt;) so, dass Fehler als Exceptions behandelt werden.[1, 18] Fangen Sie diese Exceptions ab und loggen Sie detaillierte Fehlermeldungen serverseitig, anstatt sie dem Benutzer anzuzeigen.[20, 5]&lt;br /&gt;
***   &amp;#039;&amp;#039;&amp;#039;Input Validation:&amp;#039;&amp;#039;&amp;#039; Validieren Sie alle Benutzereingaben serverseitig auf Typ, Format und erlaubte Werte, bevor Sie sie an die Datenbank übergeben. Dies ist eine wichtige zusätzliche Schutzschicht.[4, 5]&lt;br /&gt;
***   &amp;#039;&amp;#039;&amp;#039;Prinzip der geringsten Rechte (Least Privilege):&amp;#039;&amp;#039;&amp;#039; Der Datenbankbenutzer, den Ihre PHP-Anwendung verwendet, sollte nur die minimal notwendigen Berechtigungen für seine Aufgaben besitzen.[5]&lt;br /&gt;
***   &amp;#039;&amp;#039;&amp;#039;Sichere Zugangsdaten:&amp;#039;&amp;#039;&amp;#039; Speichern Sie Datenbank-Credentials (Hostname, Benutzername, Passwort) niemals direkt im PHP-Code oder in Dateien innerhalb des Web-Roots.[20, 65] Verwenden Sie stattdessen Konfigurationsdateien außerhalb des öffentlich zugänglichen Bereichs oder Umgebungsvariablen.&lt;br /&gt;
***   &amp;#039;&amp;#039;&amp;#039;Code-Struktur:&amp;#039;&amp;#039;&amp;#039; Kapseln Sie Datenbankverbindungslogik und wiederkehrende Abfragen in Funktionen oder Klassen, um die Wartbarkeit und Wiederverwendbarkeit zu verbessern.[20]&lt;br /&gt;
***   &amp;#039;&amp;#039;&amp;#039;Effizienz:&amp;#039;&amp;#039;&amp;#039; Laden Sie große Ergebnismengen nicht komplett in den Speicher (&amp;lt;code&amp;gt;fetchAll&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;fetch_all&amp;lt;/code&amp;gt;). Verwenden Sie stattdessen zeilenweise Verarbeitung (&amp;lt;code&amp;gt;while&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;fetch&amp;lt;/code&amp;gt;) oder Iteratoren (PDO mit PHP 8+).[37, 40] Nutzen Sie persistente Verbindungen nur nach sorgfältiger Abwägung der Vor- und Nachteile.[7, 27]&lt;br /&gt;
***   &amp;#039;&amp;#039;&amp;#039;Defense-in-Depth:&amp;#039;&amp;#039;&amp;#039; Betrachten Sie Sicherheit als Gesamtkonzept. Die Kombination aus der richtigen API-Wahl, konsequenten Prepared Statements, Eingabevalidierung, minimalen Rechten, sicherer Fehlerbehandlung, sicherer Speicherung von Zugangsdaten und regelmäßigen Software-Updates bildet eine robuste Verteidigung.&lt;br /&gt;
Für Studierende ist es entscheidend, diese Konzepte nicht nur theoretisch zu verstehen, sondern durch praktische Übungen zu festigen. Implementieren Sie die gezeigten Code-Beispiele, experimentieren Sie mit verschiedenen Abfragen und Fetch-Modi und machen Sie sich die Sicherheitsprinzipien von Anfang an zur Gewohnheit. Eine sichere und effiziente Datenbankinteraktion ist ein Grundpfeiler professioneller PHP-Entwicklung.&lt;/div&gt;</summary>
		<author><name>Völkl Anna</name></author>
	</entry>
	<entry>
		<id>https://mediawiki.fernfh.ac.at/mediawiki/index.php?title=Datenbankintegration_(Webdevelopment)&amp;diff=6569</id>
		<title>Datenbankintegration (Webdevelopment)</title>
		<link rel="alternate" type="text/html" href="https://mediawiki.fernfh.ac.at/mediawiki/index.php?title=Datenbankintegration_(Webdevelopment)&amp;diff=6569"/>
		<updated>2025-05-01T10:24:58Z</updated>

		<summary type="html">&lt;p&gt;Völkl Anna: Die Seite wurde neu angelegt: „= Kapitel X: Datenbankintegration mit PHP und MySQL =  == (1) Einleitung: Warum Datenbanken in PHP-Webanwendungen? ==  = Moderne Webanwendungen sind selten statisch. Sie leben von dynamischen Inhalten, Benutzerinteraktionen und der Fähigkeit, Informationen über Sitzungen hinweg zu speichern und wieder abzurufen. Ob es sich um ein Content-Management-System (CMS), einen Online-Shop, ein soziales Netzwerk oder eine einfache Benutzerverwaltung handelt – d…“&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Kapitel X: Datenbankintegration mit PHP und MySQL =&lt;br /&gt;
&lt;br /&gt;
== (1) Einleitung: Warum Datenbanken in PHP-Webanwendungen? ==&lt;br /&gt;
&lt;br /&gt;
= Moderne Webanwendungen sind selten statisch. Sie leben von dynamischen Inhalten, Benutzerinteraktionen und der Fähigkeit, Informationen über Sitzungen hinweg zu speichern und wieder abzurufen. Ob es sich um ein Content-Management-System (CMS), einen Online-Shop, ein soziales Netzwerk oder eine einfache Benutzerverwaltung handelt – die Persistenz von Daten ist entscheidend. Hier kommen Datenbanken ins Spiel.&lt;br /&gt;
&lt;br /&gt;
PHP allein, als serverseitige Skriptsprache, kann Daten nur für die Dauer der Ausführung eines Skripts im Arbeitsspeicher halten. Sobald das Skript beendet ist, gehen diese Informationen verloren. Um Daten dauerhaft zu speichern, zu organisieren, zu ändern und effizient abzurufen, benötigen PHP-Anwendungen ein externes Datenbanksystem.&lt;br /&gt;
&lt;br /&gt;
Eine der populärsten Datenbanklösungen im Webentwicklungsbereich, insbesondere in Kombination mit PHP, ist MySQL. Als relationales Datenbankmanagementsystem (RDBMS) bietet MySQL eine strukturierte Methode zur Datenspeicherung in Tabellen, die miteinander in Beziehung stehen können. Es ist ein Kernbestandteil vieler Webserver-Setups wie LAMP (Linux, Apache, MySQL, PHP) oder LEMP (Linux, Nginx, MySQL, PHP).&lt;br /&gt;
&lt;br /&gt;
Um die Brücke zwischen der PHP-Anwendung und der MySQL-Datenbank zu schlagen, stellt PHP spezielle Erweiterungen (Extensions) bereit. Diese Erweiterungen fungieren als Schnittstelle oder API (Application Programming Interface), die es PHP-Skripten ermöglichen, eine Verbindung zur Datenbank herzustellen, SQL-Befehle (Structured Query Language Queries) zu senden und die von der Datenbank zurückgegebenen Ergebnisse zu empfangen und zu verarbeiten.&lt;br /&gt;
&lt;br /&gt;
Dieses Kapitel führt Sie in die Grundlagen der Datenbankintegration mit PHP und MySQL ein. Sie lernen die verschiedenen verfügbaren PHP-Erweiterungen kennen, wobei der Schwerpunkt auf den modernen und sicheren Ansätzen liegt: PDO (PHP Data Objects) und MySQLi (MySQL Improved). Wir werden untersuchen, wie man Verbindungen aufbaut, grundlegende Datenbankoperationen – bekannt als CRUD (Create, Read, Update, Delete) – durchführt und wie man Daten sicher abfragt und manipuliert. Ein besonderes Augenmerk liegt dabei auf der Sicherheit, insbesondere auf der Vermeidung von SQL-Injection-Angriffen durch den Einsatz von Prepared Statements. Schließlich betrachten wir nützliche PHP-Array-Funktionen zur Verarbeitung der abgerufenen Datenbankergebnisse. =&lt;br /&gt;
&lt;br /&gt;
== (2) Überblick über PHP-Datenbankerweiterungen ==&lt;br /&gt;
&lt;br /&gt;
= Die Art und Weise, wie PHP mit Datenbanken interagiert, hat sich im Laufe der Zeit weiterentwickelt. Ursprünglich gab es für jede populäre Datenbank eine eigene spezifische Erweiterung. Für MySQL waren dies die &amp;lt;code&amp;gt;mysql_*&amp;lt;/code&amp;gt; Funktionen. Diese sind jedoch inzwischen veraltet und wurden durch modernere, flexiblere und sicherere Alternativen ersetzt. Heute stehen Entwicklern hauptsächlich zwei empfohlene Erweiterungen für die Arbeit mit MySQL zur Verfügung: PDO und MySQLi. =&lt;br /&gt;
&lt;br /&gt;
*   &amp;#039;&amp;#039;&amp;#039;PDO (PHP Data Objects):&amp;#039;&amp;#039;&amp;#039; Eine allgemeine Datenzugriffs-Abstraktionsschicht, die mit verschiedenen Datenbanksystemen verwendet werden kann.&lt;br /&gt;
*   &amp;#039;&amp;#039;&amp;#039;MySQLi (MySQL Improved):&amp;#039;&amp;#039;&amp;#039; Eine Erweiterung, die speziell für die Arbeit mit MySQL entwickelt wurde und dessen spezifische Funktionen unterstützt.&lt;br /&gt;
*   &amp;#039;&amp;#039;&amp;#039;&amp;lt;code&amp;gt;mysql_*&amp;lt;/code&amp;gt; Funktionen:&amp;#039;&amp;#039;&amp;#039; Die ursprüngliche MySQL-Erweiterung, die seit PHP 7.0 entfernt wurde und nicht mehr verwendet werden sollte.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
= Im Folgenden werden diese drei Optionen detaillierter vorgestellt. =&lt;br /&gt;
&lt;br /&gt;
=== 2.1 PDO (PHP Data Objects) ===&lt;br /&gt;
&lt;br /&gt;
= Die PDO-Erweiterung definiert eine leichtgewichtige und konsistente Schnittstelle für den Datenbankzugriff in PHP. Sie fungiert als &amp;#039;&amp;#039;Datenzugriffs-Abstraktionsschicht&amp;#039;&amp;#039;. Das bedeutet, dass Entwickler unabhängig von der verwendeten Datenbank dieselben PDO-Funktionen nutzen können, um Abfragen auszuführen und Daten abzurufen.[1, 2] Es ist jedoch wichtig zu verstehen, dass PDO &amp;#039;&amp;#039;keine&amp;#039;&amp;#039; Datenbankabstraktion im Sinne einer SQL-Dialekt-Übersetzung oder der Emulation fehlender Datenbankfeatures bietet. Um eine Verbindung zu einer spezifischen Datenbank wie MySQL herzustellen, benötigt PDO einen entsprechenden datenbankspezifischen Treiber, z.B. &amp;lt;code&amp;gt;pdo_mysql&amp;lt;/code&amp;gt;, der in der PHP-Konfiguration aktiviert sein muss.[1] =&lt;br /&gt;
&lt;br /&gt;
*   &amp;#039;&amp;#039;&amp;#039;Vorteile von PDO:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
**   &amp;#039;&amp;#039;&amp;#039;Datenbankunabhängigkeit:&amp;#039;&amp;#039;&amp;#039; Der größte Vorteil von PDO ist die Möglichkeit, den Code für Datenbankinteraktionen weitgehend unverändert zu lassen, selbst wenn das zugrunde liegende Datenbanksystem gewechselt wird (z.B. von MySQL zu PostgreSQL).[1, 2] Dies erhöht die Portabilität und Flexibilität von PHP-Anwendungen erheblich.[2, 3]&lt;br /&gt;
**   &amp;#039;&amp;#039;&amp;#039;Sicherheit durch Prepared Statements:&amp;#039;&amp;#039;&amp;#039; PDO bietet eine robuste und konsistente Implementierung von Prepared Statements. Diese Technik ist der Goldstandard zur Verhinderung von SQL-Injection-Angriffen, da sie die SQL-Befehlsstruktur strikt von den übergebenen Daten trennt.[1, 2, 4, 5, 6]&lt;br /&gt;
**   &amp;#039;&amp;#039;&amp;#039;Benutzerfreundlichkeit und Konsistenz:&amp;#039;&amp;#039;&amp;#039; PDO stellt eine einheitliche API mit nützlichen Hilfsfunktionen bereit, wie z.B. vielfältige &amp;quot;Fetch Modes&amp;quot;, um die Struktur der abgerufenen Daten zu steuern.[2] Die konsistente Funktionsweise über verschiedene Datenbanktreiber hinweg vereinfacht die Entwicklung.&lt;br /&gt;
**   &amp;#039;&amp;#039;&amp;#039;Moderne Fehlerbehandlung:&amp;#039;&amp;#039;&amp;#039; PDO verwendet standardmäßig Exceptions (&amp;lt;code&amp;gt;PDOException&amp;lt;/code&amp;gt;) zur Signalisierung von Fehlern.[1, 2, 7] Dies ermöglicht eine strukturierte und moderne Fehlerbehandlung mittels &amp;lt;code&amp;gt;try...catch&amp;lt;/code&amp;gt;-Blöcken, was als Best Practice gilt.[1, 2]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=  =&lt;br /&gt;
*   &amp;#039;&amp;#039;&amp;#039;Verbindungsaufbau mit PDO (Beispiel MySQL):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
**   &amp;#039;&amp;#039;&amp;#039;DSN (Data Source Name):&amp;#039;&amp;#039;&amp;#039; Die Verbindungsparameter werden in einer Zeichenkette, dem DSN, übergeben. Für MySQL hat der DSN typischerweise das Format &amp;lt;code&amp;gt;mysql:host=hostname;dbname=datenbankname;charset=zeichensatz;unix_socket=pfad_zum_socket&amp;lt;/code&amp;gt;. Die Angabe des Zeichensatzes (z.B. &amp;lt;code&amp;gt;utf8mb4&amp;lt;/code&amp;gt;) ist wichtig für die korrekte Datenübertragung.[1, 7] Beispiel: &amp;lt;code&amp;gt;mysql:host=localhost;dbname=test;charset=utf8mb4&amp;lt;/code&amp;gt;.&lt;br /&gt;
**   &amp;#039;&amp;#039;&amp;#039;PDO-Konstruktor:&amp;#039;&amp;#039;&amp;#039; Eine Verbindung wird durch Instanziierung der PDO-Klasse hergestellt: &amp;lt;code&amp;gt;$pdo = new PDO($dsn, $username, $password, $options);&amp;lt;/code&amp;gt;.[1, 7, 8] Die Parameter sind der DSN, der Datenbankbenutzername, das Passwort und ein optionales Array mit Treiberoptionen.&lt;br /&gt;
**   &amp;#039;&amp;#039;&amp;#039;Verbindungsoptionen (&amp;lt;code&amp;gt;$options&amp;lt;/code&amp;gt;):&amp;#039;&amp;#039;&amp;#039; Über dieses Array können wichtige Verhaltensweisen von PDO gesteuert werden:&lt;br /&gt;
***   &amp;lt;code&amp;gt;PDO::ATTR_ERRMODE =&amp;amp;gt; PDO::ERRMODE_EXCEPTION&amp;lt;/code&amp;gt;: Dies ist die empfohlene Einstellung für die Fehlerbehandlung. PDO wirft bei Fehlern eine &amp;lt;code&amp;gt;PDOException&amp;lt;/code&amp;gt;, die abgefangen werden kann.[1, 2, 4]&lt;br /&gt;
***   &amp;lt;code&amp;gt;PDO::ATTR_DEFAULT_FETCH_MODE =&amp;amp;gt; PDO::FETCH_ASSOC&amp;lt;/code&amp;gt;: Legt fest, dass Daten standardmäßig als assoziatives Array (&amp;lt;code&amp;gt;spaltenname =&amp;amp;gt; wert&amp;lt;/code&amp;gt;) zurückgegeben werden, was oft die Weiterverarbeitung erleichtert.[1, 9]&lt;br /&gt;
***   &amp;lt;code&amp;gt;PDO::ATTR_EMULATE_PREPARES =&amp;amp;gt; false&amp;lt;/code&amp;gt;: Deaktiviert die Emulation von Prepared Statements durch PDO und zwingt die Verwendung nativer Prepared Statements des Datenbanktreibers. Dies gilt als die sicherste und oft performanteste Methode.[1, 10, 9, 11] Standardmäßig ist die Emulation aktiviert (&amp;lt;code&amp;gt;true&amp;lt;/code&amp;gt;). Obwohl PDO auch im emulierten Modus durch korrektes Escaping Schutz vor SQL Injection bietet [12, 5, 13], eliminiert die Deaktivierung potenzielle, wenn auch seltene, Sicherheitsrisiken, die in bestimmten Konstellationen (z.B. bei Verwendung exotischer Zeichensätze wie GBK [11]) auftreten könnten. Die explizite Deaktivierung ist daher eine wichtige Best Practice.&lt;br /&gt;
***   &amp;lt;code&amp;gt;PDO::ATTR_PERSISTENT =&amp;amp;gt; true&amp;lt;/code&amp;gt;: Aktiviert persistente Datenbankverbindungen. Diese werden nicht am Ende des Skripts geschlossen, sondern in einem Pool gehalten und für nachfolgende Anfragen wiederverwendet. Dies kann die Performance von Webanwendungen verbessern, da der Overhead des Verbindungsaufbaus entfällt. Die Verwendung erfordert jedoch sorgfältige Überlegung, da der Zustand der Verbindung zwischen Anfragen bestehen bleiben kann.[7]&lt;br /&gt;
**   &amp;#039;&amp;#039;&amp;#039;Fehlerbehandlung:&amp;#039;&amp;#039;&amp;#039; Der Verbindungsaufbau sollte immer in einem &amp;lt;code&amp;gt;try...catch&amp;lt;/code&amp;gt;-Block erfolgen, um &amp;lt;code&amp;gt;PDOException&amp;lt;/code&amp;gt; abzufangen und angemessen darauf zu reagieren (z.B. Fehlermeldung loggen, alternative Aktion ausführen).[1, 7] Es ist kritisch, detaillierte Fehlermeldungen in Produktionsumgebungen nicht an den Benutzer auszugeben, da sie sensible Informationen enthalten könnten. Die PHP-Einstellung &amp;lt;code&amp;gt;display_errors&amp;lt;/code&amp;gt; sollte daher auf &amp;lt;code&amp;gt;0&amp;lt;/code&amp;gt; gesetzt sein.[7]&lt;br /&gt;
**   &amp;#039;&amp;#039;&amp;#039;Verbindung schließen:&amp;#039;&amp;#039;&amp;#039; Eine PDO-Verbindung bleibt für die Lebensdauer des PDO-Objekts aktiv. Um sie explizit zu schließen, müssen alle Referenzen auf das PDO-Objekt und alle davon abgeleiteten &amp;lt;code&amp;gt;PDOStatement&amp;lt;/code&amp;gt;-Objekte entfernt werden, typischerweise durch Zuweisung von &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt;.[7] PHP schließt die Verbindung automatisch am Ende des Skriptlaufs, wenn dies nicht manuell geschieht.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=  =&lt;br /&gt;
*   &amp;#039;&amp;#039;&amp;#039;Code-Beispiel (PDO-Verbindung zu MySQL):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php$host = &amp;#039;localhost&amp;#039;;&lt;br /&gt;
$dbname = &amp;#039;meine_datenbank&amp;#039;;&lt;br /&gt;
$username = &amp;#039;mein_benutzer&amp;#039;;&lt;br /&gt;
$password = &amp;#039;mein_passwort&amp;#039;;&lt;br /&gt;
$charset = &amp;#039;utf8mb4&amp;#039;;&lt;br /&gt;
&lt;br /&gt;
$dsn = &amp;quot;mysql:host=$host;dbname=$dbname;charset=$charset&amp;quot;;&lt;br /&gt;
$options =;&lt;br /&gt;
&lt;br /&gt;
try {&lt;br /&gt;
     $pdo = new PDO($dsn, $username, $password, $options);&lt;br /&gt;
     // Erfolgreich verbunden, $pdo-Objekt kann nun verwendet werden.&lt;br /&gt;
     // echo &amp;quot;Verbindung erfolgreich hergestellt!&amp;quot;;&lt;br /&gt;
} catch (\PDOException $e) {&lt;br /&gt;
     // Fehler beim Verbindungsaufbau&lt;br /&gt;
     // In Produktion: Fehler loggen, statt ausgeben&lt;br /&gt;
     error_log(&amp;quot;Datenbankverbindungsfehler: &amp;quot;. $e-&amp;gt;getMessage());&lt;br /&gt;
     die(&amp;quot;Es gab ein Problem mit der Datenbankverbindung. Bitte versuchen Sie es später erneut.&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//... hier Datenbankoperationen mit $pdo durchführen...&lt;br /&gt;
&lt;br /&gt;
// Verbindung explizit schließen (optional, da PHP dies am Skriptende tut)&lt;br /&gt;
// $pdo = null;&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
= Dieses Beispiel zeigt den empfohlenen Weg, eine PDO-Verbindung zu MySQL herzustellen, inklusive wichtiger Optionen für Fehlerbehandlung und Sicherheit sowie einem &amp;lt;code&amp;gt;try...catch&amp;lt;/code&amp;gt;-Block zum Abfangen von Verbindungsfehlern. =&lt;br /&gt;
&lt;br /&gt;
=== 2.2 MySQLi (MySQL Improved) ===&lt;br /&gt;
&lt;br /&gt;
= Die MySQLi-Erweiterung ist, wie der Name schon sagt, speziell für die Interaktion mit MySQL-Datenbanken konzipiert und bietet Unterstützung für Funktionen, die ab MySQL Version 4.1 eingeführt wurden.[14, 15] Sie ermöglicht den Zugriff auf MySQL-spezifische Features, die über PDO möglicherweise nicht direkt verfügbar sind.[16, 15] =&lt;br /&gt;
&lt;br /&gt;
*   &amp;#039;&amp;#039;&amp;#039;Duale Schnittstelle:&amp;#039;&amp;#039;&amp;#039; Ein charakteristisches Merkmal von MySQLi ist die Unterstützung von zwei Programmierstilen [17]:&lt;br /&gt;
**   &amp;#039;&amp;#039;&amp;#039;Prozedural:&amp;#039;&amp;#039;&amp;#039; Dieser Stil ähnelt den alten &amp;lt;code&amp;gt;mysql_*&amp;lt;/code&amp;gt; Funktionen. Die Funktionsnamen beginnen typischerweise mit &amp;lt;code&amp;gt;mysqli_&amp;lt;/code&amp;gt; (z.B. &amp;lt;code&amp;gt;mysqli_connect()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;mysqli_query()&amp;lt;/code&amp;gt;). Der Datenbankverbindungs-Handle (eine Ressource oder ein Objekt) muss dabei explizit als erster Parameter an die meisten Funktionen übergeben werden.[17] Dieser Stil wird oft von Entwicklern bevorzugt, die von der alten &amp;lt;code&amp;gt;mysql&amp;lt;/code&amp;gt;-Erweiterung migrieren.[17]&lt;br /&gt;
**   &amp;#039;&amp;#039;&amp;#039;Objektorientiert (OO):&amp;#039;&amp;#039;&amp;#039; Hier wird ein Objekt der &amp;lt;code&amp;gt;mysqli&amp;lt;/code&amp;gt;-Klasse erstellt, und Datenbankoperationen werden als Methoden dieses Objekts aufgerufen (z.B. &amp;lt;code&amp;gt;$mysqli-&amp;amp;gt;query()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;$mysqli-&amp;amp;gt;prepare()&amp;lt;/code&amp;gt;).[17] Dieser Ansatz passt oft besser zu modernen PHP-Entwicklungspraktiken und vermeidet die Notwendigkeit, den Verbindungshandle ständig zu übergeben. Die offizielle PHP-Dokumentation für MySQLi ist primär im objektorientierten Stil verfasst.[17] Es gibt keine nennenswerten Performance-Unterschiede zwischen den beiden Stilen; die Wahl ist meist eine Frage der persönlichen Präferenz oder Projektkonventionen.[17]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=  =&lt;br /&gt;
*   &amp;#039;&amp;#039;&amp;#039;Verbindungsaufbau mit MySQLi:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
**   &amp;#039;&amp;#039;&amp;#039;Prozedural:&amp;#039;&amp;#039;&amp;#039; Die Funktion &amp;lt;code&amp;gt;mysqli_connect()&amp;lt;/code&amp;gt; wird verwendet: &amp;lt;code&amp;gt;$link = mysqli_connect($host, $user, $pass, $db, $port, $socket);&amp;lt;/code&amp;gt;. Sie gibt bei Erfolg ein &amp;lt;code&amp;gt;mysqli&amp;lt;/code&amp;gt;-Objekt (oder &amp;lt;code&amp;gt;false&amp;lt;/code&amp;gt; bei Fehlern vor PHP 8.1) zurück.[17, 18] Fehler können mit &amp;lt;code&amp;gt;mysqli_connect_errno()&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;mysqli_connect_error()&amp;lt;/code&amp;gt; geprüft werden.[18]&lt;br /&gt;
**   &amp;#039;&amp;#039;&amp;#039;Objektorientiert:&amp;#039;&amp;#039;&amp;#039; Ein neues &amp;lt;code&amp;gt;mysqli&amp;lt;/code&amp;gt;-Objekt wird instanziiert: &amp;lt;code&amp;gt;$mysqli = new mysqli($host, $user, $pass, $db, $port, $socket);&amp;lt;/code&amp;gt;. Wichtig: Der Konstruktor gibt &amp;#039;&amp;#039;immer&amp;#039;&amp;#039; ein Objekt zurück, auch wenn die Verbindung fehlschlägt. Der Verbindungsstatus muss explizit über die Eigenschaften &amp;lt;code&amp;gt;$mysqli-&amp;amp;gt;connect_errno&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;$mysqli-&amp;amp;gt;connect_error&amp;lt;/code&amp;gt; geprüft werden.[17, 18]&lt;br /&gt;
**   &amp;#039;&amp;#039;&amp;#039;Fortgeschrittene Verbindung:&amp;#039;&amp;#039;&amp;#039; Für feinere Kontrolle über Verbindungsoptionen (z.B. Setzen von Timeouts oder Initialisierungsbefehlen &amp;#039;&amp;#039;vor&amp;#039;&amp;#039; dem Verbindungsaufbau) können &amp;lt;code&amp;gt;mysqli_init()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;mysqli_options()&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;mysqli_real_connect()&amp;lt;/code&amp;gt; verwendet werden.[18, 19]&lt;br /&gt;
**   &amp;#039;&amp;#039;&amp;#039;Fehlerbehandlung:&amp;#039;&amp;#039;&amp;#039; Neben der manuellen Prüfung von Fehlercodes und -meldungen ist die empfohlene Methode, MySQLi so zu konfigurieren, dass es bei Fehlern Exceptions wirft. Dies geschieht mit &amp;lt;code&amp;gt;mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);&amp;lt;/code&amp;gt;. Danach können Datenbankfehler elegant mit &amp;lt;code&amp;gt;try...catch&amp;lt;/code&amp;gt;-Blöcken behandelt werden, ähnlich wie bei PDO.[18, 20, 21, 22, 23, 24, 25, 26]&lt;br /&gt;
**   &amp;#039;&amp;#039;&amp;#039;Verbindung schließen:&amp;#039;&amp;#039;&amp;#039; Prozedural mit &amp;lt;code&amp;gt;mysqli_close($link)&amp;lt;/code&amp;gt;, objektorientiert mit &amp;lt;code&amp;gt;$mysqli-&amp;amp;gt;close()&amp;lt;/code&amp;gt;.[17, 18, 19]&lt;br /&gt;
**   &amp;#039;&amp;#039;&amp;#039;Persistente Verbindungen:&amp;#039;&amp;#039;&amp;#039; MySQLi unterstützt ebenfalls persistente Verbindungen, die über PHP-Konfigurationseinstellungen (&amp;lt;code&amp;gt;mysqli.allow_persistent&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;mysqli.max_persistent&amp;lt;/code&amp;gt;) gesteuert werden.[27] Standardmäßig setzt MySQLi den Zustand einer wiederverwendeten persistenten Verbindung zurück (via &amp;lt;code&amp;gt;mysqli::change_user()&amp;lt;/code&amp;gt;), was zwar für Konsistenz sorgt, aber auch Performance kosten kann.[27]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=  =&lt;br /&gt;
*   &amp;#039;&amp;#039;&amp;#039;Code-Beispiele (MySQLi-Verbindungen):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=  =&lt;br /&gt;
**   &amp;#039;&amp;#039;&amp;#039;Objektorientiert (mit Exception-Handling):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php$host = &amp;#039;localhost&amp;#039;;&lt;br /&gt;
$user = &amp;#039;mein_benutzer&amp;#039;;&lt;br /&gt;
$pass = &amp;#039;mein_passwort&amp;#039;;&lt;br /&gt;
$db = &amp;#039;meine_datenbank&amp;#039;;&lt;br /&gt;
&lt;br /&gt;
// Fehlerberichterstattung für Exceptions aktivieren&lt;br /&gt;
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);&lt;br /&gt;
&lt;br /&gt;
try {&lt;br /&gt;
    $mysqli = new mysqli($host, $user, $pass, $db);&lt;br /&gt;
    // Zeichensatz setzen (wichtig!)&lt;br /&gt;
    $mysqli-&amp;gt;set_charset(&amp;#039;utf8mb4&amp;#039;);&lt;br /&gt;
    // Erfolgreich verbunden&lt;br /&gt;
    // echo &amp;quot;Verbindung erfolgreich (OO)! Host-Info: &amp;quot;. $mysqli-&amp;gt;host_info;&lt;br /&gt;
} catch (mysqli_sql_exception $e) {&lt;br /&gt;
    // Fehler beim Verbindungsaufbau oder set_charset&lt;br /&gt;
    error_log(&amp;quot;MySQLi Verbindungsfehler: &amp;quot;. $e-&amp;gt;getMessage());&lt;br /&gt;
    die(&amp;quot;Es gab ein Problem mit der Datenbankverbindung.&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//... Datenbankoperationen mit $mysqli...&lt;br /&gt;
&lt;br /&gt;
// Verbindung schließen&lt;br /&gt;
// $mysqli-&amp;gt;close();&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=  =&lt;br /&gt;
**   &amp;#039;&amp;#039;&amp;#039;Prozedural (mit Exception-Handling):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php$host = &amp;#039;localhost&amp;#039;;&lt;br /&gt;
$user = &amp;#039;mein_benutzer&amp;#039;;&lt;br /&gt;
$pass = &amp;#039;mein_passwort&amp;#039;;&lt;br /&gt;
$db = &amp;#039;meine_datenbank&amp;#039;;&lt;br /&gt;
&lt;br /&gt;
// Fehlerberichterstattung für Exceptions aktivieren&lt;br /&gt;
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);&lt;br /&gt;
&lt;br /&gt;
try {&lt;br /&gt;
    $link = mysqli_connect($host, $user, $pass, $db);&lt;br /&gt;
    // Zeichensatz setzen&lt;br /&gt;
    mysqli_set_charset($link, &amp;#039;utf8mb4&amp;#039;);&lt;br /&gt;
    // Erfolgreich verbunden&lt;br /&gt;
    // echo &amp;quot;Verbindung erfolgreich (Prozedural)! Host-Info: &amp;quot;. mysqli_get_host_info($link);&lt;br /&gt;
} catch (mysqli_sql_exception $e) {&lt;br /&gt;
    // Fehler beim Verbindungsaufbau oder set_charset&lt;br /&gt;
    error_log(&amp;quot;MySQLi Verbindungsfehler: &amp;quot;. $e-&amp;gt;getMessage());&lt;br /&gt;
    die(&amp;quot;Es gab ein Problem mit der Datenbankverbindung.&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
//... Datenbankoperationen mit $link...&lt;br /&gt;
&lt;br /&gt;
// Verbindung schließen&lt;br /&gt;
// mysqli_close($link);&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=  =&lt;br /&gt;
*   &amp;#039;&amp;#039;&amp;#039;Bedeutung von &amp;lt;code&amp;gt;mysqlnd&amp;lt;/code&amp;gt;:&amp;#039;&amp;#039;&amp;#039; Die Benutzerfreundlichkeit, insbesondere bei der Arbeit mit Prepared Statements, wird stark durch den verwendeten MySQL-Treiber beeinflusst. Der &amp;#039;&amp;#039;&amp;#039;MySQL Native Driver (&amp;lt;code&amp;gt;mysqlnd&amp;lt;/code&amp;gt;)&amp;#039;&amp;#039;&amp;#039;, der seit PHP 5.4 standardmäßig enthalten ist [16], ist hier entscheidend. &amp;lt;code&amp;gt;mysqlnd&amp;lt;/code&amp;gt; stellt die Methode &amp;lt;code&amp;gt;get_result()&amp;lt;/code&amp;gt; (bzw. die Funktion &amp;lt;code&amp;gt;mysqli_stmt_get_result()&amp;lt;/code&amp;gt;) zur Verfügung.[25, 15] Diese Methode erlaubt es, nach dem Ausführen eines Prepared Statements ein Standard-&amp;lt;code&amp;gt;mysqli_result&amp;lt;/code&amp;gt;-Objekt zu erhalten, von dem dann wie gewohnt mit &amp;lt;code&amp;gt;fetch_assoc()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;fetch_all()&amp;lt;/code&amp;gt; etc. die Ergebnisse abgerufen werden können.[25] Ohne &amp;lt;code&amp;gt;mysqlnd&amp;lt;/code&amp;gt; (bei Verwendung der älteren &amp;lt;code&amp;gt;libmysqlclient&amp;lt;/code&amp;gt;-Bibliothek) ist das Abrufen von Ergebnissen aus Prepared Statements deutlich umständlicher und erfordert die manuelle Bindung jeder einzelnen Ergebnisspalte an PHP-Variablen mittels &amp;lt;code&amp;gt;bind_result()&amp;lt;/code&amp;gt; und anschließendes &amp;lt;code&amp;gt;fetch()&amp;lt;/code&amp;gt;.[25] Da &amp;lt;code&amp;gt;mysqlnd&amp;lt;/code&amp;gt; heute der Standard ist, konzentrieren sich moderne Tutorials und Beispiele meist auf die &amp;lt;code&amp;gt;get_result()&amp;lt;/code&amp;gt;-Methode, aber es ist wichtig, diesen Hintergrund zu kennen, falls man auf ältere Systeme oder Konfigurationen trifft.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=  =&lt;br /&gt;
=== 2.3 Veraltete &amp;lt;code&amp;gt;mysql_*&amp;lt;/code&amp;gt; Funktionen ===&lt;br /&gt;
&lt;br /&gt;
= Die &amp;lt;code&amp;gt;mysql_*&amp;lt;/code&amp;gt; Funktionsfamilie (&amp;lt;code&amp;gt;mysql_connect&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;mysql_query&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;mysql_fetch_assoc&amp;lt;/code&amp;gt; etc.) war die ursprüngliche Methode, um in PHP mit MySQL-Datenbanken zu interagieren. Diese Erweiterung ist jedoch &amp;#039;&amp;#039;&amp;#039;stark veraltet&amp;#039;&amp;#039;&amp;#039; und sollte &amp;#039;&amp;#039;&amp;#039;unter keinen Umständen mehr für neue Projekte verwendet werden&amp;#039;&amp;#039;&amp;#039;. =&lt;br /&gt;
&lt;br /&gt;
*   &amp;#039;&amp;#039;&amp;#039;Status:&amp;#039;&amp;#039;&amp;#039; Die &amp;lt;code&amp;gt;mysql_*&amp;lt;/code&amp;gt; Erweiterung wurde in &amp;#039;&amp;#039;&amp;#039;PHP 5.5&amp;#039;&amp;#039;&amp;#039; offiziell als &amp;quot;deprecated&amp;quot; (veraltet) markiert, was bedeutet, dass ihre Verwendung ab dieser Version Warnungen (&amp;lt;code&amp;gt;E_DEPRECATED&amp;lt;/code&amp;gt;) erzeugte.[28, 29] Mit der Veröffentlichung von &amp;#039;&amp;#039;&amp;#039;PHP 7.0&amp;#039;&amp;#039;&amp;#039; wurde die Erweiterung &amp;#039;&amp;#039;&amp;#039;vollständig entfernt&amp;#039;&amp;#039;&amp;#039;.[28, 29] PHP-Code, der diese Funktionen nutzt, führt auf PHP 7.0 oder neuer zu fatalen Fehlern und funktioniert nicht mehr.[28, 29]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=  =&lt;br /&gt;
*   &amp;#039;&amp;#039;&amp;#039;Gründe für die Entfernung:&amp;#039;&amp;#039;&amp;#039; Es gab zwingende Gründe für diesen Schritt:&lt;br /&gt;
**   &amp;#039;&amp;#039;&amp;#039;Gravierende Sicherheitsrisiken:&amp;#039;&amp;#039;&amp;#039; Der Hauptgrund war die Anfälligkeit für SQL-Injection-Angriffe. Die &amp;lt;code&amp;gt;mysql_*&amp;lt;/code&amp;gt; Funktionen boten keinen eingebauten Mechanismus wie Prepared Statements. Entwickler mussten Benutzereingaben manuell mit &amp;lt;code&amp;gt;mysql_real_escape_string()&amp;lt;/code&amp;gt; &amp;quot;escapen&amp;quot;, eine Methode, die leicht vergessen oder falsch angewendet werden konnte, was zu weit verbreiteten Sicherheitslücken führte.[29, 30] Das Fehlen von Prepared Statements in dieser alten Erweiterung ist direkt ursächlich für die hohe Zahl an SQL-Injection-Schwachstellen in älteren PHP-Anwendungen.[29]&lt;br /&gt;
**   &amp;#039;&amp;#039;&amp;#039;Fehlende Unterstützung moderner MySQL-Features:&amp;#039;&amp;#039;&amp;#039; Die Erweiterung wurde ursprünglich für MySQL Version 3.23 entwickelt und seitdem kaum weiterentwickelt.[29] Sie unterstützte viele wichtige Funktionen moderner MySQL-Versionen nicht, darunter Prepared Statements, Stored Procedures, Transaktionen, SSL-Verschlüsselung, Kompression, Multi-Statements und vollständige Zeichensatzunterstützung.[29]&lt;br /&gt;
**   &amp;#039;&amp;#039;&amp;#039;Schlechte Wartbarkeit:&amp;#039;&amp;#039;&amp;#039; Der Code der Erweiterung war veraltet und wurde immer schwieriger zu warten und an neue Versionen der MySQL-Client-Bibliotheken anzupassen.[29]&lt;br /&gt;
**   &amp;#039;&amp;#039;&amp;#039;Förderung unsicherer Praktiken:&amp;#039;&amp;#039;&amp;#039; Da viele alte Tutorials und Codebeispiele die &amp;lt;code&amp;gt;mysql_*&amp;lt;/code&amp;gt; Funktionen unsicher verwendeten, trug die fortgesetzte Verfügbarkeit zur Verbreitung schlechter und unsicherer Programmierpraktiken bei.[29]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=  =&lt;br /&gt;
*   &amp;#039;&amp;#039;&amp;#039;Verwendung heute:&amp;#039;&amp;#039;&amp;#039; Die klare Antwort lautet: &amp;#039;&amp;#039;&amp;#039;Nein&amp;#039;&amp;#039;&amp;#039;. Diese Funktionen existieren in modernen PHP-Versionen nicht mehr.[31, 30] Jede Anwendung, die sie noch verwendet, ist nicht nur unsicher, sondern auch inkompatibel mit aktueller PHP-Software. Eine Migration zu PDO oder MySQLi ist unumgänglich, um Sicherheit und Kompatibilität zu gewährleisten.[28, 29] Die erzwungene Migration durch die Entfernung in PHP 7.0 unterstreicht, wie wichtig es ist, Deprecation-Warnungen ernst zu nehmen und Code proaktiv zu aktualisieren, um zukünftige Probleme zu vermeiden.[29, 32]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=  =&lt;br /&gt;
=== Tabelle 1: Vergleich der PHP-Datenbankerweiterungen für MySQL ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Merkmal !! PDO (PHP Data Objects) !! MySQLi (MySQL Improved) !! &amp;lt;code&amp;gt;mysql_*&amp;lt;/code&amp;gt; (Veraltet)&lt;br /&gt;
|- &amp;lt;td &amp;gt;&amp;lt;br&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
|&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
|&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Entfernt (seit PHP 7.0) [29]&lt;br /&gt;
|- &amp;lt;td &amp;gt;&amp;lt;br&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
|&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
|&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Prozedural&lt;br /&gt;
|- &amp;lt;td &amp;gt;&amp;lt;br&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
|&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
|&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Nur MySQL&lt;br /&gt;
|- &amp;lt;td &amp;gt;&amp;lt;br&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
|&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
|&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Nein [29]&lt;br /&gt;
|- &lt;br /&gt;
| &amp;#039;&amp;#039;&amp;#039;SQL Injection Schutz&amp;#039;&amp;#039;&amp;#039; ||&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Sehr gut (durch Prepared Statements) [25] || Schlecht (manuelles Escaping nötig) [29]&lt;br /&gt;
|- &amp;lt;td &amp;gt;&amp;lt;br&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
|&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Sehr gute Unterstützung (spezifisch) [15] || Veraltet, viele Features fehlen [29]&lt;br /&gt;
|- &amp;lt;td &amp;gt;&amp;lt;br&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
|&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
|&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Niedrig&lt;br /&gt;
|- &amp;lt;td &amp;gt;&amp;lt;br&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
| |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Fehlercodes / &amp;lt;code&amp;gt;mysql_error()&amp;lt;/code&amp;gt;&lt;br /&gt;
|- &amp;lt;td &amp;gt;&amp;lt;br&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;td &amp;gt;&amp;lt;br&amp;gt;&amp;lt;/td&amp;gt;&amp;lt;td &amp;gt;&amp;lt;br&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
| &amp;#039;&amp;#039;&amp;#039;Nicht verwenden!&amp;#039;&amp;#039;&amp;#039; [29]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=  =&lt;br /&gt;
== (3) Grundlegende Datenbankoperationen (CRUD) ==&lt;br /&gt;
&lt;br /&gt;
= Nachdem wir die verschiedenen Erweiterungen kennengelernt haben, wenden wir uns nun den grundlegenden Operationen zu, die man typischerweise mit einer Datenbank durchführt. Diese werden oft mit dem Akronym CRUD zusammengefasst: =&lt;br /&gt;
&lt;br /&gt;
*   &amp;#039;&amp;#039;&amp;#039;C&amp;#039;&amp;#039;&amp;#039;reate: Neue Datensätze erstellen (SQL: &amp;lt;code&amp;gt;INSERT&amp;lt;/code&amp;gt;).&lt;br /&gt;
*   &amp;#039;&amp;#039;&amp;#039;R&amp;#039;&amp;#039;&amp;#039;ead: Vorhandene Datensätze lesen/abfragen (SQL: &amp;lt;code&amp;gt;SELECT&amp;lt;/code&amp;gt;).&lt;br /&gt;
*   &amp;#039;&amp;#039;&amp;#039;U&amp;#039;&amp;#039;&amp;#039;pdate: Bestehende Datensätze ändern (SQL: &amp;lt;code&amp;gt;UPDATE&amp;lt;/code&amp;gt;).&lt;br /&gt;
*   &amp;#039;&amp;#039;&amp;#039;D&amp;#039;&amp;#039;&amp;#039;elete: Datensätze löschen (SQL: &amp;lt;code&amp;gt;DELETE&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
= In den folgenden Abschnitten werden wir sehen, wie diese Operationen mit den modernen Erweiterungen PDO und MySQLi umgesetzt werden, wobei wir durchgehend &amp;#039;&amp;#039;&amp;#039;Prepared Statements&amp;#039;&amp;#039;&amp;#039; verwenden, um die Sicherheit zu gewährleisten. =&lt;br /&gt;
&lt;br /&gt;
=== 3.1 Daten abfragen (SELECT) ===&lt;br /&gt;
&lt;br /&gt;
= Das Abfragen von Daten ist eine der häufigsten Datenbankoperationen. =&lt;br /&gt;
&lt;br /&gt;
*   &amp;#039;&amp;#039;&amp;#039;PDO:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
**   &amp;#039;&amp;#039;&amp;#039;Ohne Parameter:&amp;#039;&amp;#039;&amp;#039; Wenn die Abfrage keine variablen Teile enthält (z.B. keine &amp;lt;code&amp;gt;WHERE&amp;lt;/code&amp;gt;-Klausel mit Benutzereingaben), kann die &amp;lt;code&amp;gt;query()&amp;lt;/code&amp;gt;-Methode verwendet werden. Sie führt die SQL-Abfrage direkt aus und gibt ein &amp;lt;code&amp;gt;PDOStatement&amp;lt;/code&amp;gt;-Objekt zurück, das zur Ergebnisauswertung dient.[2, 33]&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Beispiel: Alle Benutzer auswählen&lt;br /&gt;
$stmt = $pdo-&amp;gt;query(&amp;quot;SELECT id, username FROM users&amp;quot;);&lt;br /&gt;
// $stmt kann nun zum Fetchen der Ergebnisse verwendet werden&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
***   &amp;#039;&amp;#039;&amp;#039;Mit Parametern (Prepared Statement):&amp;#039;&amp;#039;&amp;#039; Dies ist der Standardfall, wenn Benutzereingaben oder andere Variablen die Abfrage beeinflussen (z.B. in &amp;lt;code&amp;gt;WHERE&amp;lt;/code&amp;gt;- oder &amp;lt;code&amp;gt;LIMIT&amp;lt;/code&amp;gt;-Klauseln).&lt;br /&gt;
**   &amp;#039;&amp;#039;&amp;#039;Vorbereiten (&amp;lt;code&amp;gt;prepare&amp;lt;/code&amp;gt;):&amp;#039;&amp;#039;&amp;#039; Die SQL-Abfrage wird mit Platzhaltern (&amp;lt;code&amp;gt;?&amp;lt;/code&amp;gt; für positionale oder &amp;lt;code&amp;gt;:name&amp;lt;/code&amp;gt; für benannte Platzhalter) vorbereitet.[34, 33, 5]&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Beispiel: Benutzer mit bestimmter ID auswählen (positionaler Platzhalter)&lt;br /&gt;
$sql = &amp;quot;SELECT id, username, email FROM users WHERE id =?&amp;quot;;&lt;br /&gt;
$stmt = $pdo-&amp;gt;prepare($sql);&lt;br /&gt;
&lt;br /&gt;
// Beispiel: Benutzer mit bestimmtem Status auswählen (benannter Platzhalter)&lt;br /&gt;
$sql_named = &amp;quot;SELECT id, username FROM users WHERE status = :status&amp;quot;;&lt;br /&gt;
$stmt_named = $pdo-&amp;gt;prepare($sql_named);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
**#   &amp;#039;&amp;#039;&amp;#039;Ausführen (&amp;lt;code&amp;gt;execute&amp;lt;/code&amp;gt;):&amp;#039;&amp;#039;&amp;#039; Die vorbereitete Anweisung wird mit den tatsächlichen Werten ausgeführt. Die Werte werden als Array an die &amp;lt;code&amp;gt;execute()&amp;lt;/code&amp;gt;-Methode übergeben. Bei positionalen Platzhaltern entspricht die Reihenfolge im Array der Reihenfolge der &amp;lt;code&amp;gt;?&amp;lt;/code&amp;gt; in der SQL-Abfrage. Bei benannten Platzhaltern wird ein assoziatives Array verwendet, dessen Schlüssel den Platzhalternamen entsprechen (mit oder ohne führenden Doppelpunkt).[34, 33, 5]&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Ausführen für positionalen Platzhalter&lt;br /&gt;
$user_id = 123;&lt;br /&gt;
$stmt-&amp;gt;execute([$user_id]);&lt;br /&gt;
&lt;br /&gt;
// Ausführen für benannten Platzhalter&lt;br /&gt;
$user_status = &amp;#039;active&amp;#039;;&lt;br /&gt;
$stmt_named-&amp;gt;execute([&amp;#039;:status&amp;#039; =&amp;gt; $user_status]);&lt;br /&gt;
// oder: $stmt_named-&amp;gt;execute([&amp;#039;status&amp;#039; =&amp;gt; $user_status]);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
**#   &amp;#039;&amp;#039;&amp;#039;Alternative Bindung:&amp;#039;&amp;#039;&amp;#039; Optional können Parameter auch explizit vor dem &amp;lt;code&amp;gt;execute()&amp;lt;/code&amp;gt;-Aufruf mit &amp;lt;code&amp;gt;bindValue()&amp;lt;/code&amp;gt; (bindet einen Wert) oder &amp;lt;code&amp;gt;bindParam()&amp;lt;/code&amp;gt; (bindet eine Variable als Referenz) gebunden werden. Dies gibt mehr Kontrolle über den Datentyp (z.B. &amp;lt;code&amp;gt;PDO::PARAM_INT&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;PDO::PARAM_STR&amp;lt;/code&amp;gt;).[5, 13]&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$stmt-&amp;gt;bindValue(1, $user_id, PDO::PARAM_INT); // Position 1, Wert von $user_id als Integer&lt;br /&gt;
$stmt-&amp;gt;execute();&lt;br /&gt;
&lt;br /&gt;
$stmt_named-&amp;gt;bindParam(&amp;#039;:status&amp;#039;, $user_status, PDO::PARAM_STR); // Platzhalter :status, Variable $user_status als String&lt;br /&gt;
$stmt_named-&amp;gt;execute();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
***   &amp;#039;&amp;#039;&amp;#039;MySQLi:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
****   &amp;#039;&amp;#039;&amp;#039;Ohne Parameter:&amp;#039;&amp;#039;&amp;#039; Ähnlich wie bei PDO kann bei Abfragen ohne variable Teile die &amp;lt;code&amp;gt;query()&amp;lt;/code&amp;gt;-Methode (OO-Stil: &amp;lt;code&amp;gt;$mysqli-&amp;amp;gt;query()&amp;lt;/code&amp;gt;) oder die &amp;lt;code&amp;gt;mysqli_query()&amp;lt;/code&amp;gt;-Funktion (prozeduraler Stil: &amp;lt;code&amp;gt;mysqli_query($link, &amp;quot;...&amp;quot;)&amp;lt;/code&amp;gt;) verwendet werden. Sie geben ein &amp;lt;code&amp;gt;mysqli_result&amp;lt;/code&amp;gt;-Objekt zurück.[20, 25]&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// OO-Stil&lt;br /&gt;
$result = $mysqli-&amp;gt;query(&amp;quot;SELECT id, username FROM users&amp;quot;);&lt;br /&gt;
// Prozedural&lt;br /&gt;
$result = mysqli_query($link, &amp;quot;SELECT id, username FROM users&amp;quot;);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
****   &amp;#039;&amp;#039;&amp;#039;Mit Parametern (Prepared Statement):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
***   &amp;#039;&amp;#039;&amp;#039;Vorbereiten (&amp;lt;code&amp;gt;prepare&amp;lt;/code&amp;gt;):&amp;#039;&amp;#039;&amp;#039; Die SQL-Abfrage wird mit &amp;lt;code&amp;gt;?&amp;lt;/code&amp;gt;-Platzhaltern vorbereitet. Im OO-Stil &amp;lt;code&amp;gt;$stmt = $mysqli-&amp;amp;gt;prepare(&amp;quot;...&amp;quot;)&amp;lt;/code&amp;gt;, prozedural &amp;lt;code&amp;gt;$stmt = mysqli_prepare($link, &amp;quot;...&amp;quot;)&amp;lt;/code&amp;gt;.[25, 35]&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// OO-Stil&lt;br /&gt;
$sql = &amp;quot;SELECT id, username, email FROM users WHERE id =?&amp;quot;;&lt;br /&gt;
$stmt = $mysqli-&amp;gt;prepare($sql);&lt;br /&gt;
// Prozedural&lt;br /&gt;
$sql_proc = &amp;quot;SELECT id, username, email FROM users WHERE id =?&amp;quot;;&lt;br /&gt;
$stmt_proc = mysqli_prepare($link, $sql_proc);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
***#   &amp;#039;&amp;#039;&amp;#039;Parameter binden (&amp;lt;code&amp;gt;bind_param&amp;lt;/code&amp;gt;):&amp;#039;&amp;#039;&amp;#039; Die Variablen werden an die Platzhalter gebunden. Dies ist ein wesentlicher Unterschied zu PDOs &amp;lt;code&amp;gt;execute&amp;lt;/code&amp;gt;-Array-Bindung. &amp;lt;code&amp;gt;bind_param()&amp;lt;/code&amp;gt; erfordert einen String, der die Datentypen der zu bindenden Variablen angibt (z.B. &amp;quot;i&amp;quot; für Integer, &amp;quot;s&amp;quot; für String, &amp;quot;d&amp;quot; für Double/Float, &amp;quot;b&amp;quot; für Blob), gefolgt von den Variablen selbst.[25, 35]&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$user_id = 123;&lt;br /&gt;
// OO-Stil&lt;br /&gt;
$stmt-&amp;gt;bind_param(&amp;quot;i&amp;quot;, $user_id); // &amp;quot;i&amp;quot; für Integer&lt;br /&gt;
// Prozedural&lt;br /&gt;
mysqli_stmt_bind_param($stmt_proc, &amp;quot;i&amp;quot;, $user_id);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
***#   &amp;#039;&amp;#039;&amp;#039;Ausführen (&amp;lt;code&amp;gt;execute&amp;lt;/code&amp;gt;):&amp;#039;&amp;#039;&amp;#039; Die vorbereitete Anweisung wird ausgeführt. OO-Stil: &amp;lt;code&amp;gt;$stmt-&amp;amp;gt;execute()&amp;lt;/code&amp;gt;, prozedural: &amp;lt;code&amp;gt;mysqli_stmt_execute($stmt)&amp;lt;/code&amp;gt;.[25, 36, 35]&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// OO-Stil&lt;br /&gt;
$stmt-&amp;gt;execute();&lt;br /&gt;
// Prozedural&lt;br /&gt;
mysqli_stmt_execute($stmt_proc);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
***#   &amp;#039;&amp;#039;&amp;#039;Ergebnis holen:&amp;#039;&amp;#039;&amp;#039; Um die Ergebnisse eines Prepared Statements in MySQLi zu erhalten, ist die Methode &amp;lt;code&amp;gt;get_result()&amp;lt;/code&amp;gt; (OO) bzw. &amp;lt;code&amp;gt;mysqli_stmt_get_result()&amp;lt;/code&amp;gt; (prozedural) am bequemsten. Sie gibt ein Standard-&amp;lt;code&amp;gt;mysqli_result&amp;lt;/code&amp;gt;-Objekt zurück, das dann mit den üblichen Fetch-Methoden verarbeitet werden kann. &amp;#039;&amp;#039;&amp;#039;Wichtig:&amp;#039;&amp;#039;&amp;#039; Diese Methode erfordert den &amp;lt;code&amp;gt;mysqlnd&amp;lt;/code&amp;gt;-Treiber.[25, 15]&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// OO-Stil&lt;br /&gt;
$result = $stmt-&amp;gt;get_result();&lt;br /&gt;
// Prozedural&lt;br /&gt;
$result_proc = mysqli_stmt_get_result($stmt_proc);&lt;br /&gt;
// $result / $result_proc kann nun mit fetch_assoc() etc. verwendet werden.&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Die Alternative ohne&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;mysqlnd&amp;lt;/code&amp;gt;ist&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;bind_result()&amp;lt;/code&amp;gt;, bei der jede Ergebnisspalte an eine PHP-Variable gebunden wird, und&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;fetch()&amp;lt;/code&amp;gt;iterativ aufgerufen wird, um die Variablen zu füllen.[25] Dies ist deutlich umständlicher.&lt;br /&gt;
&lt;br /&gt;
=== 3.2 Ergebnisse abrufen und verarbeiten ===&lt;br /&gt;
&lt;br /&gt;
Nachdem eine&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;SELECT&amp;lt;/code&amp;gt;-Abfrage erfolgreich ausgeführt wurde, müssen die zurückgegebenen Daten aus dem Ergebnisobjekt (&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;PDOStatement&amp;lt;/code&amp;gt;oder&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;mysqli_result&amp;lt;/code&amp;gt;) abgerufen (&amp;quot;gefetched&amp;quot;) werden.&lt;br /&gt;
&lt;br /&gt;
****   &amp;#039;&amp;#039;&amp;#039;Zeilenweise Iteration (&amp;lt;code&amp;gt;fetch&amp;lt;/code&amp;gt; im Loop):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
Dies ist der klassische Weg, um Ergebnisse zu verarbeiten, insbesondere wenn jede Zeile einzeln behandelt werden muss oder wenn die Ergebnismenge sehr groß ist.&lt;br /&gt;
*****   &amp;#039;&amp;#039;&amp;#039;PDO:&amp;#039;&amp;#039;&amp;#039; Die &amp;lt;code&amp;gt;fetch()&amp;lt;/code&amp;gt;-Methode des &amp;lt;code&amp;gt;PDOStatement&amp;lt;/code&amp;gt;-Objekts wird wiederholt in einer &amp;lt;code&amp;gt;while&amp;lt;/code&amp;gt;-Schleife aufgerufen. &amp;lt;code&amp;gt;fetch()&amp;lt;/code&amp;gt; gibt bei jedem Aufruf die nächste Zeile zurück (im Format des eingestellten Fetch-Modus, z.B. &amp;lt;code&amp;gt;PDO::FETCH_ASSOC&amp;lt;/code&amp;gt;) und &amp;lt;code&amp;gt;false&amp;lt;/code&amp;gt; (oder &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt; je nach Modus/Version), wenn keine weiteren Zeilen vorhanden sind.[2, 33]&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$stmt = $pdo-&amp;gt;prepare(&amp;quot;SELECT name, email FROM users WHERE status =?&amp;quot;);&lt;br /&gt;
$stmt-&amp;gt;execute([&amp;#039;active&amp;#039;]);&lt;br /&gt;
while ($row = $stmt-&amp;gt;fetch(PDO::FETCH_ASSOC)) {&lt;br /&gt;
    echo &amp;quot;Name: &amp;quot;. htmlspecialchars($row[&amp;#039;name&amp;#039;]). &amp;quot;, E-Mail: &amp;quot;. htmlspecialchars($row[&amp;#039;email&amp;#039;]). &amp;quot;&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
*****   &amp;#039;&amp;#039;&amp;#039;MySQLi:&amp;#039;&amp;#039;&amp;#039; Die &amp;lt;code&amp;gt;fetch_assoc()&amp;lt;/code&amp;gt;-Methode (oder &amp;lt;code&amp;gt;fetch_row()&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;fetch_object()&amp;lt;/code&amp;gt;, etc.) des &amp;lt;code&amp;gt;mysqli_result&amp;lt;/code&amp;gt;-Objekts wird in einer &amp;lt;code&amp;gt;while&amp;lt;/code&amp;gt;-Schleife verwendet. Sie gibt die nächste Zeile als Array (oder Objekt) zurück oder &amp;lt;code&amp;gt;null&amp;lt;/code&amp;gt;, wenn das Ende erreicht ist.[25, 15]&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// OO-Stil (nach $result = $stmt-&amp;gt;get_result();)&lt;br /&gt;
while ($row = $result-&amp;gt;fetch_assoc()) {&lt;br /&gt;
    echo &amp;quot;Name: &amp;quot;. htmlspecialchars($row[&amp;#039;name&amp;#039;]). &amp;quot;, E-Mail: &amp;quot;. htmlspecialchars($row[&amp;#039;email&amp;#039;]). &amp;quot;&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Prozedural (nach $result = mysqli_stmt_get_result($stmt);)&lt;br /&gt;
while ($row = mysqli_fetch_assoc($result)) {&lt;br /&gt;
    echo &amp;quot;Name: &amp;quot;. htmlspecialchars($row[&amp;#039;name&amp;#039;]). &amp;quot;, E-Mail: &amp;quot;. htmlspecialchars($row[&amp;#039;email&amp;#039;]). &amp;quot;&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Dieser Ansatz ist&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;speichereffizient&amp;#039;&amp;#039;&amp;#039;, da immer nur eine Zeile gleichzeitig im PHP-Speicher gehalten wird. Dies ist besonders wichtig bei Abfragen, die potenziell Tausende oder Millionen von Zeilen zurückgeben könnten.[37]&lt;br /&gt;
&lt;br /&gt;
****   &amp;#039;&amp;#039;&amp;#039;Alle Ergebnisse auf einmal abrufen (&amp;lt;code&amp;gt;fetchAll&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;fetch_all&amp;lt;/code&amp;gt;):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
Für kleinere bis mittlere Ergebnismengen ist es oft bequemer, alle Zeilen auf einmal in ein PHP-Array zu laden.&lt;br /&gt;
*****   &amp;#039;&amp;#039;&amp;#039;PDO:&amp;#039;&amp;#039;&amp;#039; Die &amp;lt;code&amp;gt;fetchAll()&amp;lt;/code&amp;gt;-Methode des &amp;lt;code&amp;gt;PDOStatement&amp;lt;/code&amp;gt;-Objekts gibt ein Array zurück, das alle Ergebniszeilen enthält.[33, 38] Der Fetch-Modus (z.B. &amp;lt;code&amp;gt;PDO::FETCH_ASSOC&amp;lt;/code&amp;gt;) kann als Argument übergeben werden.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$stmt = $pdo-&amp;gt;prepare(&amp;quot;SELECT name, email FROM users WHERE status =?&amp;quot;);&lt;br /&gt;
$stmt-&amp;gt;execute([&amp;#039;active&amp;#039;]);&lt;br /&gt;
$results = $stmt-&amp;gt;fetchAll(PDO::FETCH_ASSOC);&lt;br /&gt;
// $results ist nun ein Array von assoziativen Arrays&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
*****   &amp;#039;&amp;#039;&amp;#039;MySQLi:&amp;#039;&amp;#039;&amp;#039; Die &amp;lt;code&amp;gt;fetch_all()&amp;lt;/code&amp;gt;-Methode des &amp;lt;code&amp;gt;mysqli_result&amp;lt;/code&amp;gt;-Objekts (verfügbar mit &amp;lt;code&amp;gt;mysqlnd&amp;lt;/code&amp;gt;) tut dasselbe. Der Modus (&amp;lt;code&amp;gt;MYSQLI_ASSOC&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;MYSQLI_NUM&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;MYSQLI_BOTH&amp;lt;/code&amp;gt;) wird als Argument übergeben.[25, 39]&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// OO-Stil (nach $result = $stmt-&amp;gt;get_result();)&lt;br /&gt;
$results = $result-&amp;gt;fetch_all(MYSQLI_ASSOC);&lt;br /&gt;
&lt;br /&gt;
// Prozedural (nach $result = mysqli_stmt_get_result($stmt);)&lt;br /&gt;
$results = mysqli_fetch_all($result, MYSQLI_ASSOC);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Der&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Vorteil&amp;#039;&amp;#039;&amp;#039;dieser Methode liegt in der einfacheren Weiterverarbeitung des Ergebnis-Arrays mit Standard-PHP-Array-Funktionen wie&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;foreach&amp;lt;/code&amp;gt;oder&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;count&amp;lt;/code&amp;gt;.[40] Der&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Nachteil&amp;#039;&amp;#039;&amp;#039;ist der potenziell hohe Speicherverbrauch, da alle Daten gleichzeitig in den PHP-Speicher geladen werden. Bei sehr großen Ergebnismengen kann dies zu Speicherlimit-Fehlern führen.[37]&lt;br /&gt;
&lt;br /&gt;
****   &amp;#039;&amp;#039;&amp;#039;Fetch-Modi / Fetch-Funktionen (Struktur der Ergebniszeile):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
Beide Erweiterungen bieten verschiedene Möglichkeiten, die Struktur der abgerufenen Datenzeilen zu bestimmen:&lt;br /&gt;
&lt;br /&gt;
*****   &amp;#039;&amp;#039;&amp;#039;PDO Fetch-Modi (Auswahl):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
******   &amp;lt;code&amp;gt;PDO::FETCH_ASSOC&amp;lt;/code&amp;gt;: Gibt ein assoziatives Array zurück (&amp;lt;code&amp;gt;[&amp;#039;spaltenname&amp;#039; =&amp;amp;gt; &amp;#039;wert&amp;#039;,... ]&amp;lt;/code&amp;gt;).[9, 33, 41, 42] Sehr gebräuchlich.&lt;br /&gt;
******   &amp;lt;code&amp;gt;PDO::FETCH_NUM&amp;lt;/code&amp;gt;: Gibt ein numerisch indiziertes Array zurück (&amp;lt;code&amp;gt;[0 =&amp;amp;gt; &amp;#039;wert1&amp;#039;, 1 =&amp;amp;gt; &amp;#039;wert2&amp;#039;,...]&amp;lt;/code&amp;gt;).[9, 41, 42]&lt;br /&gt;
******   &amp;lt;code&amp;gt;PDO::FETCH_BOTH&amp;lt;/code&amp;gt; (Standard): Gibt ein Array zurück, das sowohl assoziative als auch numerische Indizes enthält.[41, 42] Verbraucht mehr Speicher.&lt;br /&gt;
******   &amp;lt;code&amp;gt;PDO::FETCH_OBJ&amp;lt;/code&amp;gt;: Gibt ein anonymes &amp;lt;code&amp;gt;stdClass&amp;lt;/code&amp;gt;-Objekt zurück, bei dem die Spaltennamen Eigenschaften sind (&amp;lt;code&amp;gt;$row-&amp;amp;gt;spaltenname&amp;lt;/code&amp;gt;).[9, 41, 42, 43]&lt;br /&gt;
******   &amp;lt;code&amp;gt;PDO::FETCH_CLASS&amp;lt;/code&amp;gt;: Erstellt eine Instanz einer angegebenen Klasse und weist die Spaltenwerte den Klasseneigenschaften zu.[38, 41, 42, 43]&lt;br /&gt;
******   &amp;lt;code&amp;gt;PDO::FETCH_COLUMN&amp;lt;/code&amp;gt;: Ruft nur den Wert einer einzelnen Spalte aus der nächsten Zeile ab.[38]&lt;br /&gt;
******   &amp;lt;code&amp;gt;PDO::FETCH_LAZY&amp;lt;/code&amp;gt;: Eine Kombination aus &amp;lt;code&amp;gt;FETCH_BOTH&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;FETCH_OBJ&amp;lt;/code&amp;gt;, die Werte erst bei Zugriff lädt.[9, 41, 42]&lt;br /&gt;
&lt;br /&gt;
*****   &amp;#039;&amp;#039;&amp;#039;MySQLi Fetch-Funktionen (Auswahl):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
******   &amp;lt;code&amp;gt;fetch_assoc()&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;mysqli_fetch_assoc()&amp;lt;/code&amp;gt;: Gibt ein assoziatives Array zurück.[17, 25, 39, 15] Sehr gebräuchlich.&lt;br /&gt;
******   &amp;lt;code&amp;gt;fetch_row()&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;mysqli_fetch_row()&amp;lt;/code&amp;gt;: Gibt ein numerisch indiziertes Array zurück.[39, 35]&lt;br /&gt;
******   &amp;lt;code&amp;gt;fetch_array()&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;mysqli_fetch_array()&amp;lt;/code&amp;gt;: Gibt je nach übergebenem Modus (&amp;lt;code&amp;gt;MYSQLI_ASSOC&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;MYSQLI_NUM&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;MYSQLI_BOTH&amp;lt;/code&amp;gt;) ein assoziatives, numerisches oder beides enthaltendes Array zurück.[39, 35]&lt;br /&gt;
******   &amp;lt;code&amp;gt;fetch_object()&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;mysqli_fetch_object()&amp;lt;/code&amp;gt;: Gibt ein &amp;lt;code&amp;gt;stdClass&amp;lt;/code&amp;gt;-Objekt zurück oder eine Instanz einer angegebenen Klasse.[21, 15, 35]&lt;br /&gt;
******   &amp;lt;code&amp;gt;fetch_column()&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;mysqli_fetch_column()&amp;lt;/code&amp;gt;: Ruft den Wert einer einzelnen Spalte ab (erst ab PHP 8.1 verfügbar).[24]&lt;br /&gt;
******   &amp;lt;code&amp;gt;fetch_all()&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;mysqli_fetch_all()&amp;lt;/code&amp;gt;: Ruft alle Zeilen auf einmal ab, Modus (&amp;lt;code&amp;gt;MYSQLI_ASSOC&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;MYSQLI_NUM&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;MYSQLI_BOTH&amp;lt;/code&amp;gt;) wählbar.[21, 25, 39]&lt;br /&gt;
&lt;br /&gt;
=== Tabelle 2: Ausgewählte PDO Fetch-Modi ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Konstante !! Rückgabeformat !! Beschreibung&lt;br /&gt;
|- &lt;br /&gt;
|  &amp;lt;code&amp;gt;PDO::FETCH_ASSOC&amp;lt;/code&amp;gt;&lt;br /&gt;
| | [&amp;quot; col&amp;#039;=&amp;quot;&amp;amp;gt;&amp;quot; &amp;#039;val&amp;#039;]&amp;amp;lt;=&amp;quot;&amp;quot; code&amp;amp;gt;&amp;#039;)=&amp;quot;&amp;quot; &amp;amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
| Gängigste Form, Spaltennamen als Schlüssel.&lt;br /&gt;
|- &lt;br /&gt;
| |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Spaltenwerte über numerischen Index erreichbar.&lt;br /&gt;
|- &lt;br /&gt;
| |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
|&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Standard, enthält doppelte Informationen, höherer Speicherbedarf.&lt;br /&gt;
|- &lt;br /&gt;
| |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Spaltennamen werden zu Objekteigenschaften.&lt;br /&gt;
|- &lt;br /&gt;
|  &amp;lt;code&amp;gt;PDO::FETCH_CLASS&amp;lt;/code&amp;gt; ||&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Mappt Spalten auf Eigenschaften einer vordefinierten Klasse.&lt;br /&gt;
|- &lt;br /&gt;
|  &amp;lt;code&amp;gt;PDO::FETCH_COLUMN&amp;lt;/code&amp;gt; ||&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Gibt nur den Wert der ersten (oder angegebenen) Spalte der nächsten Zeile zurück.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Tabelle 3: Ausgewählte MySQLi Fetch-Funktionen ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Funktion (OO / Prozedural) !! Rückgabeformat !! Beschreibung&lt;br /&gt;
|- &lt;br /&gt;
| |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Gängigste Form, Spaltennamen als Schlüssel.&lt;br /&gt;
|- &lt;br /&gt;
| |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Spaltenwerte über numerischen Index erreichbar.&lt;br /&gt;
|- &lt;br /&gt;
| |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
|&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Flexibel je nach Modus (&amp;lt;code&amp;gt;MYSQLI_ASSOC&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;MYSQLI_NUM&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;MYSQLI_BOTH&amp;lt;/code&amp;gt;).&lt;br /&gt;
|- &lt;br /&gt;
|  &amp;lt;code&amp;gt;fetch_object()&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;mysqli_fetch_object()&amp;lt;/code&amp;gt;&lt;br /&gt;
| | stdClass&amp;quot; -objekt=&amp;quot;&amp;quot; oder=&amp;quot;&amp;quot; spezifische=&amp;quot;&amp;quot; klasse=&amp;quot;&amp;quot; &amp;amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
| Spaltennamen werden zu Objekteigenschaften.&lt;br /&gt;
|- &lt;br /&gt;
| |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
|&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Holt alle Ergebniszeilen auf einmal (benötigt &amp;lt;code&amp;gt;mysqlnd&amp;lt;/code&amp;gt;).&lt;br /&gt;
|- &lt;br /&gt;
|  &amp;lt;code&amp;gt;fetch_column()&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;mysqli_fetch_column()&amp;lt;/code&amp;gt; ||&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Gibt nur den Wert der ersten (oder angegebenen) Spalte zurück (PHP 8.1+).&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
****   &amp;#039;&amp;#039;&amp;#039;Zugriff auf Spaltenwerte:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
Der Zugriff auf die Daten innerhalb einer abgerufenen Zeile hängt vom gewählten Fetch-Modus ab:&lt;br /&gt;
*****   Assoziatives Array: &amp;lt;code&amp;gt;$row[&amp;#039;spaltenname&amp;#039;]&amp;lt;/code&amp;gt;&lt;br /&gt;
*****   Numerisches Array: &amp;lt;code&amp;gt;$row&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;$row[1]&amp;lt;/code&amp;gt;,...&lt;br /&gt;
*****   Objekt: &amp;lt;code&amp;gt;$row-&amp;amp;gt;spaltenname&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
****   &amp;#039;&amp;#039;&amp;#039;Iteration mit &amp;lt;code&amp;gt;foreach&amp;lt;/code&amp;gt;:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
Nachdem Ergebnisse mit&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;fetchAll()&amp;lt;/code&amp;gt;oder&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;fetch_all()&amp;lt;/code&amp;gt;in ein Array geladen wurden, ist&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;foreach&amp;lt;/code&amp;gt;die natürliche Wahl zur Iteration [37, 40, 44]:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$results = $stmt-&amp;gt;fetchAll(PDO::FETCH_ASSOC);&lt;br /&gt;
foreach ($results as $row) {&lt;br /&gt;
    echo &amp;quot;Name: &amp;quot;. htmlspecialchars($row[&amp;#039;name&amp;#039;]). &amp;quot;&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Seit PHP 8 bietet PDO eine modernere Alternative, die die Lesbarkeit von&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;foreach&amp;lt;/code&amp;gt;mit der Speichereffizienz von&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;fetch()&amp;lt;/code&amp;gt;kombiniert, indem direkt über das&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;PDOStatement&amp;lt;/code&amp;gt;-Objekt iteriert wird [37, 45]:&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Nur PHP 8+ mit PDO&lt;br /&gt;
$stmt = $pdo-&amp;gt;query(&amp;quot;SELECT name, email FROM users&amp;quot;);&lt;br /&gt;
foreach ($stmt as $row) { // PDOStatement ist direkt traversierbar&lt;br /&gt;
    echo &amp;quot;Name: &amp;quot;. htmlspecialchars($row[&amp;#039;name&amp;#039;]). &amp;quot;&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
Diese Methode ist besonders elegant und effizient für große Ergebnismengen in modernen PHP-Versionen.&lt;br /&gt;
&lt;br /&gt;
=== 3.3 Daten manipulieren (INSERT, UPDATE, DELETE) ===&lt;br /&gt;
&lt;br /&gt;
Das Einfügen, Aktualisieren und Löschen von Daten sind kritische Operationen, da sie den Datenbestand verändern. Hier ist die Verwendung von&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Prepared Statements zwingend erforderlich&amp;#039;&amp;#039;&amp;#039;, um die Integrität und Sicherheit der Datenbank zu gewährleisten, insbesondere wenn Benutzereingaben beteiligt sind.&lt;br /&gt;
&lt;br /&gt;
****   &amp;#039;&amp;#039;&amp;#039;PDO:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
*****   &amp;#039;&amp;#039;&amp;#039;Vorbereiten (&amp;lt;code&amp;gt;prepare&amp;lt;/code&amp;gt;):&amp;#039;&amp;#039;&amp;#039; Die SQL-Anweisung (&amp;lt;code&amp;gt;INSERT&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;UPDATE&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;DELETE&amp;lt;/code&amp;gt;) wird mit Platzhaltern (&amp;lt;code&amp;gt;?&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;:name&amp;lt;/code&amp;gt;) für die einzufügenden/zu aktualisierenden Werte sowie für die Kriterien in &amp;lt;code&amp;gt;WHERE&amp;lt;/code&amp;gt;-Klauseln (bei &amp;lt;code&amp;gt;UPDATE&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;DELETE&amp;lt;/code&amp;gt;) vorbereitet.[5, 46, 47, 48]&lt;br /&gt;
*****   &amp;#039;&amp;#039;&amp;#039;Ausführen (&amp;lt;code&amp;gt;execute&amp;lt;/code&amp;gt;):&amp;#039;&amp;#039;&amp;#039; Die Anweisung wird mit einem Array ausgeführt, das die Werte für die Platzhalter enthält.[5, 46, 47, 48]&lt;br /&gt;
*****   &amp;#039;&amp;#039;&amp;#039;Ergebnis prüfen:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
******   &amp;#039;&amp;#039;Anzahl betroffener Zeilen:&amp;#039;&amp;#039; &amp;lt;code&amp;gt;$stmt-&amp;amp;gt;rowCount()&amp;lt;/code&amp;gt; gibt die Anzahl der durch &amp;lt;code&amp;gt;UPDATE&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;DELETE&amp;lt;/code&amp;gt; betroffenen Zeilen zurück. Bei &amp;lt;code&amp;gt;INSERT&amp;lt;/code&amp;gt; ist der Rückgabewert oft nicht zuverlässig oder standardisiert.[8, 9, 5, 49] Das Verhalten kann je nach Datenbanktreiber und Konfiguration (z.B. &amp;lt;code&amp;gt;PDO::MYSQL_ATTR_FOUND_ROWS&amp;lt;/code&amp;gt;) variieren.[9]&lt;br /&gt;
******   &amp;#039;&amp;#039;ID des letzten eingefügten Datensatzes:&amp;#039;&amp;#039; Bei &amp;lt;code&amp;gt;INSERT&amp;lt;/code&amp;gt;-Anweisungen in Tabellen mit einer &amp;lt;code&amp;gt;AUTO_INCREMENT&amp;lt;/code&amp;gt;-Spalte liefert &amp;lt;code&amp;gt;$pdo-&amp;amp;gt;lastInsertId()&amp;lt;/code&amp;gt; die ID des neu eingefügten Datensatzes zurück.[8, 33, 48]&lt;br /&gt;
*****   &amp;#039;&amp;#039;&amp;#039;Code-Beispiele (PDO):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php// --- INSERT ---&lt;br /&gt;
$name = $_POST[&amp;#039;name&amp;#039;];&lt;br /&gt;
$email = $_POST[&amp;#039;email&amp;#039;];&lt;br /&gt;
$status = &amp;#039;pending&amp;#039;;&lt;br /&gt;
&lt;br /&gt;
$sql_insert = &amp;quot;INSERT INTO users (username, email, status) VALUES (:name, :email, :status)&amp;quot;;&lt;br /&gt;
$stmt_insert = $pdo-&amp;gt;prepare($sql_insert);&lt;br /&gt;
$stmt_insert-&amp;gt;execute([&amp;#039;:name&amp;#039; =&amp;gt; $name, &amp;#039;:email&amp;#039; =&amp;gt; $email, &amp;#039;:status&amp;#039; =&amp;gt; $status]);&lt;br /&gt;
$lastId = $pdo-&amp;gt;lastInsertId();&lt;br /&gt;
echo &amp;quot;Neuer Benutzer mit ID $lastId eingefügt.&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
// --- UPDATE ---&lt;br /&gt;
$new_status = &amp;#039;active&amp;#039;;&lt;br /&gt;
$user_id_to_update = $lastId;&lt;br /&gt;
&lt;br /&gt;
$sql_update = &amp;quot;UPDATE users SET status = :status WHERE id = :id&amp;quot;;&lt;br /&gt;
$stmt_update = $pdo-&amp;gt;prepare($sql_update);&lt;br /&gt;
$stmt_update-&amp;gt;execute([&amp;#039;:status&amp;#039; =&amp;gt; $new_status, &amp;#039;:id&amp;#039; =&amp;gt; $user_id_to_update]);&lt;br /&gt;
$affectedRows = $stmt_update-&amp;gt;rowCount();&lt;br /&gt;
echo &amp;quot;$affectedRows Benutzerstatus aktualisiert.&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
// --- DELETE ---&lt;br /&gt;
$user_id_to_delete = $lastId;&lt;br /&gt;
&lt;br /&gt;
$sql_delete = &amp;quot;DELETE FROM users WHERE id = :id&amp;quot;;&lt;br /&gt;
$stmt_delete = $pdo-&amp;gt;prepare($sql_delete);&lt;br /&gt;
$stmt_delete-&amp;gt;execute([&amp;#039;:id&amp;#039; =&amp;gt; $user_id_to_delete]);&lt;br /&gt;
$affectedRowsDel = $stmt_delete-&amp;gt;rowCount();&lt;br /&gt;
echo &amp;quot;$affectedRowsDel Benutzer gelöscht.&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
****   &amp;#039;&amp;#039;&amp;#039;MySQLi:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
*****   &amp;#039;&amp;#039;&amp;#039;Vorbereiten (&amp;lt;code&amp;gt;prepare&amp;lt;/code&amp;gt;):&amp;#039;&amp;#039;&amp;#039; Die SQL-Anweisung wird mit &amp;lt;code&amp;gt;?&amp;lt;/code&amp;gt;-Platzhaltern vorbereitet (OO: &amp;lt;code&amp;gt;$mysqli-&amp;amp;gt;prepare()&amp;lt;/code&amp;gt;, Proc: &amp;lt;code&amp;gt;mysqli_prepare()&amp;lt;/code&amp;gt;).[25, 50, 15, 35]&lt;br /&gt;
*****   &amp;#039;&amp;#039;&amp;#039;Parameter binden (&amp;lt;code&amp;gt;bind_param&amp;lt;/code&amp;gt;):&amp;#039;&amp;#039;&amp;#039; Variablen werden explizit mit Typenangabe gebunden (OO: &amp;lt;code&amp;gt;$stmt-&amp;amp;gt;bind_param()&amp;lt;/code&amp;gt;, Proc: &amp;lt;code&amp;gt;mysqli_stmt_bind_param()&amp;lt;/code&amp;gt;).[25, 50, 15, 35] Die Notwendigkeit, den Datentyp für jede Variable anzugeben, ist ein wesentlicher Unterschied zur einfacheren Array-Übergabe bei PDOs &amp;lt;code&amp;gt;execute()&amp;lt;/code&amp;gt;.&lt;br /&gt;
*****   &amp;#039;&amp;#039;&amp;#039;Ausführen (&amp;lt;code&amp;gt;execute&amp;lt;/code&amp;gt;):&amp;#039;&amp;#039;&amp;#039; Die Anweisung wird ausgeführt (OO: &amp;lt;code&amp;gt;$stmt-&amp;amp;gt;execute()&amp;lt;/code&amp;gt;, Proc: &amp;lt;code&amp;gt;mysqli_stmt_execute()&amp;lt;/code&amp;gt;).[25, 36, 50, 15, 35]&lt;br /&gt;
*****   &amp;#039;&amp;#039;&amp;#039;Ergebnis prüfen:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
******   &amp;#039;&amp;#039;Anzahl betroffener Zeilen:&amp;#039;&amp;#039; Über die Eigenschaft &amp;lt;code&amp;gt;$mysqli-&amp;amp;gt;affected_rows&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;$stmt-&amp;amp;gt;affected_rows&amp;lt;/code&amp;gt; (OO) bzw. die Funktionen &amp;lt;code&amp;gt;mysqli_affected_rows($link)&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;mysqli_stmt_affected_rows($stmt)&amp;lt;/code&amp;gt; (Proc).[18, 26, 15, 35]&lt;br /&gt;
******   &amp;#039;&amp;#039;ID des letzten eingefügten Datensatzes:&amp;#039;&amp;#039; Über &amp;lt;code&amp;gt;$mysqli-&amp;amp;gt;insert_id&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;$stmt-&amp;amp;gt;insert_id&amp;lt;/code&amp;gt; (OO) bzw. &amp;lt;code&amp;gt;mysqli_insert_id($link)&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;mysqli_stmt_insert_id($stmt)&amp;lt;/code&amp;gt; (Proc).[26, 15] Die Verwendung von &amp;lt;code&amp;gt;$mysqli-&amp;amp;gt;insert_id&amp;lt;/code&amp;gt; wird oft als zuverlässiger angesehen, da sie auch nach dem Schließen des Statements funktioniert.[15]&lt;br /&gt;
*****   &amp;#039;&amp;#039;&amp;#039;Code-Beispiele (MySQLi OO):&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php// --- INSERT ---&lt;br /&gt;
$name = $_POST[&amp;#039;name&amp;#039;];&lt;br /&gt;
$email = $_POST[&amp;#039;email&amp;#039;];&lt;br /&gt;
$status = &amp;#039;pending&amp;#039;;&lt;br /&gt;
&lt;br /&gt;
$sql_insert = &amp;quot;INSERT INTO users (username, email, status) VALUES (?,?,?)&amp;quot;;&lt;br /&gt;
$stmt_insert = $mysqli-&amp;gt;prepare($sql_insert);&lt;br /&gt;
// Typen: s=string, s=string, s=string&lt;br /&gt;
$stmt_insert-&amp;gt;bind_param(&amp;quot;sss&amp;quot;, $name, $email, $status);&lt;br /&gt;
$stmt_insert-&amp;gt;execute();&lt;br /&gt;
$lastId = $mysqli-&amp;gt;insert_id;&lt;br /&gt;
echo &amp;quot;Neuer Benutzer mit ID $lastId eingefügt (MySQLi OO).&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
$stmt_insert-&amp;gt;close(); // Statement schließen&lt;br /&gt;
&lt;br /&gt;
// --- UPDATE ---&lt;br /&gt;
$new_status = &amp;#039;active&amp;#039;;&lt;br /&gt;
$user_id_to_update = $lastId;&lt;br /&gt;
&lt;br /&gt;
$sql_update = &amp;quot;UPDATE users SET status =? WHERE id =?&amp;quot;;&lt;br /&gt;
$stmt_update = $mysqli-&amp;gt;prepare($sql_update);&lt;br /&gt;
// Typen: s=string, i=integer&lt;br /&gt;
$stmt_update-&amp;gt;bind_param(&amp;quot;si&amp;quot;, $new_status, $user_id_to_update);&lt;br /&gt;
$stmt_update-&amp;gt;execute();&lt;br /&gt;
$affectedRows = $mysqli-&amp;gt;affected_rows; // Oder $stmt_update-&amp;gt;affected_rows vor close()&lt;br /&gt;
echo &amp;quot;$affectedRows Benutzerstatus aktualisiert (MySQLi OO).&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
$stmt_update-&amp;gt;close();&lt;br /&gt;
&lt;br /&gt;
// --- DELETE ---&lt;br /&gt;
$user_id_to_delete = $lastId;&lt;br /&gt;
&lt;br /&gt;
$sql_delete = &amp;quot;DELETE FROM users WHERE id =?&amp;quot;;&lt;br /&gt;
$stmt_delete = $mysqli-&amp;gt;prepare($sql_delete);&lt;br /&gt;
// Typ: i=integer&lt;br /&gt;
$stmt_delete-&amp;gt;bind_param(&amp;quot;i&amp;quot;, $user_id_to_delete);&lt;br /&gt;
$stmt_delete-&amp;gt;execute();&lt;br /&gt;
$affectedRowsDel = $mysqli-&amp;gt;affected_rows;&lt;br /&gt;
echo &amp;quot;$affectedRowsDel Benutzer gelöscht (MySQLi OO).&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
$stmt_delete-&amp;gt;close();&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;(Anmerkung: Prozedurale Beispiele folgen demselben Muster unter Verwendung der &amp;lt;code&amp;gt;mysqli_*&amp;lt;/code&amp;gt;-Funktionen wie &amp;lt;code&amp;gt;mysqli_prepare&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;mysqli_stmt_bind_param&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;mysqli_stmt_execute&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;mysqli_insert_id&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;mysqli_affected_rows&amp;lt;/code&amp;gt; und &amp;lt;code&amp;gt;mysqli_stmt_close&amp;lt;/code&amp;gt;)&amp;#039;&amp;#039;.&lt;br /&gt;
&lt;br /&gt;
== (4) Sicherheit: SQL Injection verstehen und verhindern ==&lt;br /&gt;
&lt;br /&gt;
Eines der größten Sicherheitsrisiken bei der Interaktion mit Datenbanken über Webanwendungen ist die SQL Injection (SQLi). Das Verständnis dieser Bedrohung und ihrer Abwehr ist für jeden PHP-Entwickler unerlässlich.&lt;br /&gt;
&lt;br /&gt;
=== 4.1 Was ist SQL Injection? ===&lt;br /&gt;
&lt;br /&gt;
SQL Injection ist eine Angriffstechnik, bei der ein Angreifer versucht, bösartigen oder manipulierten SQL-Code über Benutzereingabefelder einer Webanwendung (z.B. Formulare, URL-Parameter, HTTP-Header, Cookies) in die Datenbankabfragen einzuschleusen.[51, 52, 53, 54, 55, 56, 57]&lt;br /&gt;
&lt;br /&gt;
Die Attacke funktioniert, indem Schwachstellen in der Anwendung ausgenutzt werden, bei denen Benutzereingaben direkt und ohne ausreichende Validierung oder Maskierung (Escaping) in SQL-Abfragestrings eingefügt (konkateniert) werden.[5, 51, 55] Dadurch wird die notwendige Trennung zwischen dem eigentlichen SQL-Befehl (Kontrollebene) und den Daten (Datenebene) aufgehoben.[51] Der Angreifer kann spezielle Zeichen (wie Anführungszeichen&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;#039;&amp;lt;/code&amp;gt;, Semikolons&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;;&amp;lt;/code&amp;gt;oder Kommentare&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;--&amp;lt;/code&amp;gt;) verwenden, um den ursprünglichen SQL-Befehl vorzeitig zu beenden, zu verändern oder um eigene, zusätzliche SQL-Befehle anzuhängen.[51, 53]&lt;br /&gt;
&lt;br /&gt;
Ein klassisches Beispiel ist eine Login-Abfrage, die etwa so aufgebaut ist:&lt;br /&gt;
&amp;lt;code&amp;gt;$sql = &amp;quot;SELECT * FROM users WHERE username = &amp;#039;&amp;quot;. $userInputUsername. &amp;quot;&amp;#039; AND password = &amp;#039;&amp;quot;. $userInputPassword. &amp;quot;&amp;#039;&amp;quot;;&amp;lt;/code&amp;gt;&lt;br /&gt;
Wenn ein Angreifer als Benutzernamen&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;#039; OR &amp;#039;1&amp;#039;=&amp;#039;1&amp;lt;/code&amp;gt;eingibt, wird die resultierende SQL-Abfrage zu:&lt;br /&gt;
&amp;lt;code&amp;gt;SELECT * FROM users WHERE username = &amp;#039;&amp;#039; OR &amp;#039;1&amp;#039;=&amp;#039;1&amp;#039; AND password = &amp;#039;...&amp;#039;&amp;lt;/code&amp;gt;&lt;br /&gt;
Da&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;#039;1&amp;#039;=&amp;#039;1&amp;#039;&amp;lt;/code&amp;gt;immer wahr ist, würde diese Abfrage (je nach genauer Struktur und Datenbank) möglicherweise alle Benutzer zurückgeben oder den Login ohne korrektes Passwort ermöglichen.[5, 15, 55]&lt;br /&gt;
&lt;br /&gt;
=== 4.2 Warum ist SQL Injection gefährlich? (Auswirkungen) ===&lt;br /&gt;
&lt;br /&gt;
Die potenziellen Folgen einer erfolgreichen SQL-Injection-Attacke sind gravierend und vielfältig:&lt;br /&gt;
&lt;br /&gt;
****   &amp;#039;&amp;#039;&amp;#039;Datendiebstahl:&amp;#039;&amp;#039;&amp;#039; Angreifer können auf sensible Daten zugreifen und diese auslesen, darunter Benutzerdaten, Passwörter (oft als Hashes, die aber geknackt werden können), Kreditkarteninformationen, Geschäftsgeheimnisse und andere vertrauliche Informationen.[51, 53, 56]&lt;br /&gt;
****   &amp;#039;&amp;#039;&amp;#039;Datenmanipulation/-zerstörung:&amp;#039;&amp;#039;&amp;#039; Angreifer können bestehende Daten in der Datenbank verändern (z.B. Preise in einem Shop, Benutzerrollen) oder komplette Datensätze oder sogar Tabellen löschen (&amp;lt;code&amp;gt;DELETE&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;DROP TABLE&amp;lt;/code&amp;gt;).[51, 53, 56]&lt;br /&gt;
****   &amp;#039;&amp;#039;&amp;#039;Umgehung der Authentifizierung/Identitätsdiebstahl:&amp;#039;&amp;#039;&amp;#039; Wie im Beispiel oben gezeigt, können Angreifer Login-Mechanismen umgehen und sich unautorisiert Zugang verschaffen, möglicherweise sogar mit administrativen Rechten.[51, 56]&lt;br /&gt;
****   &amp;#039;&amp;#039;&amp;#039;Denial of Service (DoS):&amp;#039;&amp;#039;&amp;#039; Durch das Löschen wichtiger Daten oder das Ausführen ressourcenintensiver Abfragen kann die Verfügbarkeit der Anwendung oder der Datenbank beeinträchtigt werden.[51, 56]&lt;br /&gt;
****   &amp;#039;&amp;#039;&amp;#039;Kompromittierung des Servers:&amp;#039;&amp;#039;&amp;#039; In manchen Fällen können Angreifer über SQL Injection Betriebssystembefehle auf dem Datenbankserver ausführen oder vollen administrativen Zugriff auf die Datenbank erlangen, was weitreichende Konsequenzen haben kann.[51, 55, 56]&lt;br /&gt;
****   &amp;#039;&amp;#039;&amp;#039;Reputationsschaden und rechtliche Folgen:&amp;#039;&amp;#039;&amp;#039; Erfolgreiche Angriffe, insbesondere wenn sie zu Datenlecks führen, können das Vertrauen der Benutzer schwer beschädigen und zu empfindlichen Strafen gemäß Datenschutzgesetzen führen.[53]&lt;br /&gt;
&lt;br /&gt;
=== 4.3 Prävention durch Prepared Statements (Primäre Methode) ===&lt;br /&gt;
&lt;br /&gt;
Die mit Abstand&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;wichtigste und effektivste Methode&amp;#039;&amp;#039;&amp;#039;zur Verhinderung von SQL Injection ist die konsequente Verwendung von&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Prepared Statements&amp;#039;&amp;#039;&amp;#039;(parametrisierten Abfragen).[4, 25, 5, 58, 35]&lt;br /&gt;
&lt;br /&gt;
Das Grundprinzip von Prepared Statements ist die&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;strikte Trennung von SQL-Befehlsstruktur und den Daten (Parametern)&amp;#039;&amp;#039;&amp;#039;.[4, 25, 5, 58, 35] Der Ablauf ist typischerweise zweistufig:&lt;br /&gt;
***#   &amp;#039;&amp;#039;&amp;#039;Prepare (Vorbereiten):&amp;#039;&amp;#039;&amp;#039; Die SQL-Anweisung wird mit Platzhaltern anstelle der tatsächlichen Werte an den Datenbankserver gesendet. Der Server parst die Anweisung, prüft die Syntax und bereitet einen Ausführungsplan vor, ohne die endgültigen Daten zu kennen.&lt;br /&gt;
***#   &amp;#039;&amp;#039;&amp;#039;Execute (Ausführen) mit Parameterbindung:&amp;#039;&amp;#039;&amp;#039; Die tatsächlichen Werte (Parameter) werden separat an den Server gesendet und an die vorbereiteten Platzhalter gebunden. Der Server führt nun die vorkompilierte Anweisung mit den sicher eingefügten Werten aus.&lt;br /&gt;
&lt;br /&gt;
Der entscheidende Punkt ist, dass die Datenbank die separat gesendeten Parameter&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;immer als Datenwerte&amp;#039;&amp;#039;&amp;#039;behandelt, niemals als Teil des SQL-Befehls.[5, 15, 58] Selbst wenn ein Parameter bösartigen SQL-Code enthält (z.B.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&amp;#039; OR &amp;#039;1&amp;#039;=&amp;#039;1&amp;lt;/code&amp;gt;), wird dieser nicht ausgeführt, sondern als einfacher String-Wert behandelt, der z.B. in eine Spalte eingefügt oder in einer&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;WHERE&amp;lt;/code&amp;gt;-Klausel verglichen wird. Diese Trennung macht Prepared Statements immun gegen SQL Injection.&lt;br /&gt;
&lt;br /&gt;
****   &amp;#039;&amp;#039;&amp;#039;Platzhalter:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
*****   &amp;#039;&amp;#039;&amp;#039;PDO:&amp;#039;&amp;#039;&amp;#039; Unterstützt sowohl positionale Platzhalter (&amp;lt;code&amp;gt;?&amp;lt;/code&amp;gt;) als auch benannte Platzhalter (&amp;lt;code&amp;gt;:name&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;:email&amp;lt;/code&amp;gt;, etc.).[5, 13] Benannte Platzhalter machen den Code oft lesbarer, besonders bei vielen Parametern.&lt;br /&gt;
*****   &amp;#039;&amp;#039;&amp;#039;MySQLi:&amp;#039;&amp;#039;&amp;#039; Unterstützt ausschließlich positionale Platzhalter (&amp;lt;code&amp;gt;?&amp;lt;/code&amp;gt;).[25]&lt;br /&gt;
&lt;br /&gt;
****   &amp;#039;&amp;#039;&amp;#039;Parameterbindung:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
*****   &amp;#039;&amp;#039;&amp;#039;PDO:&amp;#039;&amp;#039;&amp;#039; Bietet flexible Bindungsoptionen:&lt;br /&gt;
******   &amp;#039;&amp;#039;Implizit über &amp;lt;code&amp;gt;execute()&amp;lt;/code&amp;gt;-Array:&amp;#039;&amp;#039; Die einfachste und häufigste Methode. Ein Array mit den Werten wird an &amp;lt;code&amp;gt;execute()&amp;lt;/code&amp;gt; übergeben. PDO kümmert sich um das korrekte Escaping und die Typbehandlung (oft werden Werte sicher als Strings behandelt).[4, 12, 5, 13]&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$stmt-&amp;gt;execute([$wert1, $wert2]); // für? Platzhalter&lt;br /&gt;
$stmt-&amp;gt;execute([&amp;#039;:name&amp;#039; =&amp;gt; $wert1, &amp;#039;:id&amp;#039; =&amp;gt; $wert2]); // für :name Platzhalter&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
******   &amp;#039;&amp;#039;Explizit mit &amp;lt;code&amp;gt;bindValue()&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;bindParam()&amp;lt;/code&amp;gt;:&amp;#039;&amp;#039; Erlaubt die explizite Angabe des Datentyps (z.B. &amp;lt;code&amp;gt;PDO::PARAM_INT&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;PDO::PARAM_STR&amp;lt;/code&amp;gt;) für jeden Parameter, was in manchen Fällen für Klarheit oder spezifische Datenbankoptimierungen sorgen kann.[5, 13] &amp;lt;code&amp;gt;bindParam&amp;lt;/code&amp;gt; bindet eine Variable (ihr Wert wird erst bei &amp;lt;code&amp;gt;execute()&amp;lt;/code&amp;gt; gelesen), &amp;lt;code&amp;gt;bindValue&amp;lt;/code&amp;gt; bindet den aktuellen Wert einer Variablen oder einen Literalwert.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
$stmt-&amp;gt;bindValue(&amp;#039;:id&amp;#039;, $user_id, PDO::PARAM_INT);&lt;br /&gt;
$stmt-&amp;gt;bindParam(&amp;#039;:name&amp;#039;, $user_name, PDO::PARAM_STR);&lt;br /&gt;
$stmt-&amp;gt;execute();&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
*****   &amp;#039;&amp;#039;&amp;#039;MySQLi:&amp;#039;&amp;#039;&amp;#039; Erfordert immer die explizite Bindung mittels &amp;lt;code&amp;gt;bind_param()&amp;lt;/code&amp;gt;.&lt;br /&gt;
******   &amp;lt;code&amp;gt;bind_param()&amp;lt;/code&amp;gt; (OO) / &amp;lt;code&amp;gt;mysqli_stmt_bind_param()&amp;lt;/code&amp;gt; (Proc): Nimmt als ersten Parameter einen String entgegen, der die Datentypen aller nachfolgend übergebenen Variablen definiert.[25, 50, 15, 58]&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// OO-Stil&lt;br /&gt;
$stmt-&amp;gt;bind_param(&amp;quot;ssi&amp;quot;, $name, $email, $id); // String, String, Integer&lt;br /&gt;
// Prozedural&lt;br /&gt;
mysqli_stmt_bind_param($stmt, &amp;quot;ssi&amp;quot;, $name, $email, $id);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
=== Tabelle 4: MySQLi &amp;lt;code&amp;gt;bind_param()&amp;lt;/code&amp;gt; Typen-String ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
! Typ-Buchstabe !! PHP-Datentyp (typisch) !! Beschreibung&lt;br /&gt;
|- &lt;br /&gt;
| |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
|&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Integer (Ganzzahl)&lt;br /&gt;
|- &lt;br /&gt;
| |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
|&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| Double (Gleitkommazahl)&lt;br /&gt;
|- &lt;br /&gt;
| |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
|&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| String (Zeichenkette)&lt;br /&gt;
|- &lt;br /&gt;
| |&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
|&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
| BLOB (Binary Large Object), als String gesendet&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
****   &amp;#039;&amp;#039;&amp;#039;Detaillierte Code-Beispiele zur Prävention:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
Die Beispiele für INSERT, UPDATE und DELETE in Abschnitt 3.3 demonstrieren bereits die korrekte Anwendung von Prepared Statements mit PDO und MySQLi zur Verhinderung von SQL Injection. Es ist entscheidend, dieses Muster konsequent anzuwenden, wann immer externe Daten in SQL-Abfragen einfließen.&lt;br /&gt;
&lt;br /&gt;
=== 4.4 Zusätzliche Schutzmaßnahmen (Defense-in-Depth) ===&lt;br /&gt;
&lt;br /&gt;
Obwohl Prepared Statements der wichtigste Schutz gegen SQL Injection sind, sollte Sicherheit immer als mehrschichtiges Konzept (&amp;quot;Defense-in-Depth&amp;quot;) betrachtet werden.[5] Zusätzliche Maßnahmen erhöhen die Robustheit der Anwendung:&lt;br /&gt;
&lt;br /&gt;
****   &amp;#039;&amp;#039;&amp;#039;Eingabevalidierung und -bereinigung:&amp;#039;&amp;#039;&amp;#039; Überprüfen Sie &amp;#039;&amp;#039;alle&amp;#039;&amp;#039; Benutzereingaben serverseitig, bevor sie überhaupt in die Nähe der Datenbank kommen.[4, 59, 20, 5] Stellen Sie sicher, dass die Daten dem erwarteten Typ (Zahl, String, E-Mail-Format etc.) und Format entsprechen. Verwenden Sie dafür PHP-Filterfunktionen wie &amp;lt;code&amp;gt;filter_var()&amp;lt;/code&amp;gt; oder reguläre Ausdrücke.[4, 5] Dies ist eine wichtige zweite Verteidigungslinie, die ungültige oder unerwartete Daten frühzeitig abfängt. Es ersetzt jedoch &amp;#039;&amp;#039;nicht&amp;#039;&amp;#039; die Notwendigkeit von Prepared Statements.&lt;br /&gt;
****   &amp;#039;&amp;#039;&amp;#039;Prinzip der geringsten Rechte (Least Privilege):&amp;#039;&amp;#039;&amp;#039; Konfigurieren Sie den Datenbankbenutzer, den Ihre PHP-Anwendung verwendet, mit den absolut minimal notwendigen Berechtigungen.[20, 5] Wenn die Anwendung nur Daten lesen und schreiben muss, geben Sie ihr keine Rechte zum Ändern der Tabellenstruktur (&amp;lt;code&amp;gt;ALTER TABLE&amp;lt;/code&amp;gt;) oder zum Löschen von Tabellen (&amp;lt;code&amp;gt;DROP TABLE&amp;lt;/code&amp;gt;). Beschränken Sie den Zugriff auf die benötigten Datenbanken und Tabellen. Verwenden Sie niemals den &amp;lt;code&amp;gt;root&amp;lt;/code&amp;gt;- oder Admin-Benutzer der Datenbank für die Anwendung. Dies begrenzt den potenziellen Schaden, falls doch eine Schwachstelle ausgenutzt wird.&lt;br /&gt;
****   &amp;#039;&amp;#039;&amp;#039;Sichere Fehlerbehandlung:&amp;#039;&amp;#039;&amp;#039; Konfigurieren Sie PHP und Ihre Datenbankerweiterung so, dass detaillierte Fehlermeldungen (insbesondere solche, die SQL-Code oder Datenbankstrukturinformationen enthalten) niemals dem Endbenutzer angezeigt werden.[20, 5] Solche Informationen sind für Angreifer wertvoll. Loggen Sie Fehler stattdessen in eine sichere Datei auf dem Server, die nur für Administratoren zugänglich ist. Verwenden Sie &amp;lt;code&amp;gt;PDO::ERRMODE_EXCEPTION&amp;lt;/code&amp;gt; oder &amp;lt;code&amp;gt;mysqli_report&amp;lt;/code&amp;gt; für strukturierte Fehlerbehandlung.&lt;br /&gt;
****   &amp;#039;&amp;#039;&amp;#039;Output Escaping:&amp;#039;&amp;#039;&amp;#039; Obwohl nicht direkt zur Verhinderung von SQL Injection, ist es wichtig, Daten, die aus der Datenbank gelesen und im HTML-Kontext ausgegeben werden, korrekt zu escapen (z.B. mit &amp;lt;code&amp;gt;htmlspecialchars()&amp;lt;/code&amp;gt;), um Cross-Site Scripting (XSS)-Angriffe zu verhindern.&lt;br /&gt;
&lt;br /&gt;
Die Kombination dieser Maßnahmen – mit Prepared Statements als Kernstück – bietet einen robusten Schutz gegen SQL Injection und andere verwandte Angriffe.&lt;br /&gt;
&lt;br /&gt;
== (5) Nützliche PHP Array-Funktionen für Datenbankergebnisse ==&lt;br /&gt;
&lt;br /&gt;
Wenn Daten aus der Datenbank abgerufen werden, insbesondere mit Methoden wie&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;fetchAll()&amp;lt;/code&amp;gt;(PDO) oder&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;fetch_all()&amp;lt;/code&amp;gt;(MySQLi), liegen die Ergebnisse typischerweise als PHP-Array vor (meist ein Array von assoziativen Arrays, wobei jedes innere Array eine Ergebniszeile darstellt). PHP bietet eine Reihe nützlicher Funktionen zur Verarbeitung solcher Arrays.&lt;br /&gt;
&lt;br /&gt;
****   &amp;#039;&amp;#039;&amp;#039;Iteration mit &amp;lt;code&amp;gt;foreach&amp;lt;/code&amp;gt;:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
Dies ist die Standardmethode, um die einzelnen Zeilen und Spalten eines Ergebnis-Arrays zu durchlaufen.[37, 40, 44]&lt;br /&gt;
*****   &amp;#039;&amp;#039;&amp;#039;Syntax:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Iteration über die Zeilen (jedes $row ist ein assoziatives Array)&lt;br /&gt;
foreach ($results as $row) {&lt;br /&gt;
    echo &amp;quot;ID: &amp;quot;. $row[&amp;#039;id&amp;#039;]. &amp;quot;, Name: &amp;quot;. htmlspecialchars($row[&amp;#039;name&amp;#039;]). &amp;quot;&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Iteration über Zeilen mit Zugriff auf den numerischen Index der Zeile&lt;br /&gt;
foreach ($results as $index =&amp;gt; $row) {&lt;br /&gt;
    echo &amp;quot;Zeile &amp;quot;. $index. &amp;quot;: ID = &amp;quot;. $row[&amp;#039;id&amp;#039;]. &amp;quot;&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
*****   &amp;#039;&amp;#039;&amp;#039;Code-Beispiel:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php// Annahme: $results enthält das Ergebnis von $stmt-&amp;gt;fetchAll(PDO::FETCH_ASSOC);&lt;br /&gt;
$results = [&amp;#039;id&amp;#039; =&amp;gt; 1, &amp;#039;name&amp;#039; =&amp;gt; &amp;#039;Alice&amp;#039;, &amp;#039;email&amp;#039; =&amp;gt; &amp;#039;alice@example.com&amp;#039;],&lt;br /&gt;
   ;&lt;br /&gt;
&lt;br /&gt;
echo &amp;quot;&amp;lt;ul&amp;gt;&amp;quot;;&lt;br /&gt;
foreach ($results as $row) {&lt;br /&gt;
    echo &amp;quot;&amp;lt;li&amp;gt;Benutzer #&amp;quot;. $row[&amp;#039;id&amp;#039;]. &amp;quot;: &amp;quot;. htmlspecialchars($row[&amp;#039;name&amp;#039;]);&lt;br /&gt;
    echo &amp;quot; (&amp;quot;. htmlspecialchars($row[&amp;#039;email&amp;#039;]). &amp;quot;)&amp;lt;/li&amp;gt;&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
echo &amp;quot;&amp;lt;/ul&amp;gt;&amp;quot;;&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
****   &amp;#039;&amp;#039;&amp;#039;Zählen von Ergebnissen mit &amp;lt;code&amp;gt;count()&amp;lt;/code&amp;gt;:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
Die Funktion&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;count()&amp;lt;/code&amp;gt;gibt die Anzahl der Elemente in einem Array zurück. Wenn sie auf ein mit&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;fetchAll()&amp;lt;/code&amp;gt;oder&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;fetch_all()&amp;lt;/code&amp;gt;erzeugtes Ergebnis-Array angewendet wird, liefert sie die Anzahl der abgerufenen Zeilen.[40, 60, 61]&lt;br /&gt;
*****   &amp;#039;&amp;#039;&amp;#039;Syntax:&amp;#039;&amp;#039;&amp;#039; &amp;lt;code&amp;gt;$anzahlZeilen = count($results);&amp;lt;/code&amp;gt;.[60]&lt;br /&gt;
*****   &amp;#039;&amp;#039;&amp;#039;Wichtiger Hinweis:&amp;#039;&amp;#039;&amp;#039; &amp;lt;code&amp;gt;count()&amp;lt;/code&amp;gt; funktioniert nur zuverlässig für die Gesamtzahl der Zeilen, wenn &amp;#039;&amp;#039;alle&amp;#039;&amp;#039; Zeilen zuvor in ein Array geladen wurden (also nach &amp;lt;code&amp;gt;fetchAll&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;fetch_all&amp;lt;/code&amp;gt;). Wenn Sie Ergebnisse zeilenweise mit &amp;lt;code&amp;gt;fetch()&amp;lt;/code&amp;gt; in einer &amp;lt;code&amp;gt;while&amp;lt;/code&amp;gt;-Schleife verarbeiten, müssen Sie einen manuellen Zähler innerhalb der Schleife inkrementieren, um die Anzahl der verarbeiteten Zeilen zu ermitteln.[40] Alternativ können &amp;lt;code&amp;gt;PDOStatement::rowCount()&amp;lt;/code&amp;gt; [8, 9] oder &amp;lt;code&amp;gt;mysqli_result::$num_rows&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;mysqli_stmt::$num_rows&amp;lt;/code&amp;gt; [26, 15] verwendet werden, um die Anzahl der von einer &amp;lt;code&amp;gt;SELECT&amp;lt;/code&amp;gt;-Abfrage zurückgegebenen Zeilen zu erhalten, &amp;#039;&amp;#039;bevor&amp;#039;&amp;#039; die Daten gefetched werden (das Verhalten von &amp;lt;code&amp;gt;rowCount&amp;lt;/code&amp;gt; kann jedoch bei anderen Anweisungstypen wie &amp;lt;code&amp;gt;UPDATE&amp;lt;/code&amp;gt; variieren).&lt;br /&gt;
*****   &amp;#039;&amp;#039;&amp;#039;Code-Beispiel:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
// Annahme: $results von fetchAll()&lt;br /&gt;
$results = [ /*... wie oben... */ ];&lt;br /&gt;
$anzahl = count($results);&lt;br /&gt;
echo &amp;quot;Anzahl der Benutzer (via fetchAll/count): $anzahl&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
// Vergleich: Zählen bei zeilenweiser Verarbeitung&lt;br /&gt;
$stmt = $pdo-&amp;gt;query(&amp;quot;SELECT id FROM users&amp;quot;); // Annahme: PDO-Statement&lt;br /&gt;
$manueller_zaehler = 0;&lt;br /&gt;
while ($row = $stmt-&amp;gt;fetch()) {&lt;br /&gt;
    $manueller_zaehler++;&lt;br /&gt;
}&lt;br /&gt;
echo &amp;quot;Anzahl der Benutzer (via while/fetch): $manueller_zaehler&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
// Alternative: rowCount() nach SELECT (PDO)&lt;br /&gt;
$stmt_count = $pdo-&amp;gt;query(&amp;quot;SELECT id FROM users&amp;quot;);&lt;br /&gt;
$rowCountResult = $stmt_count-&amp;gt;rowCount(); // Kann bei manchen DBs/Treibern funktionieren&lt;br /&gt;
echo &amp;quot;Anzahl der Benutzer (via rowCount nach SELECT): $rowCountResult&amp;lt;br&amp;gt;&amp;quot;;&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
****   &amp;#039;&amp;#039;&amp;#039;(Optional) Weitere nützliche Array-Funktionen:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
*****   &amp;lt;code&amp;gt;array_column(array $input, mixed $column_key [, mixed $index_key = null ])&amp;lt;/code&amp;gt;: Extrahiert alle Werte aus einer einzelnen Spalte des Ergebnis-Arrays und gibt sie als neues, eindimensionales Array zurück.[62] Sehr nützlich, um z.B. eine Liste aller Benutzer-IDs oder E-Mail-Adressen zu erhalten.&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
// Annahme: $results enthält das Array von assoziativen Arrays&lt;br /&gt;
$alle_emails = array_column($results, &amp;#039;email&amp;#039;);&lt;br /&gt;
// $alle_emails ist nun [&amp;#039;alice@example.com&amp;#039;, &amp;#039;bob@example.com&amp;#039;]&lt;br /&gt;
print_r($alle_emails);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
*****   &amp;lt;code&amp;gt;array_map(callable $callback, array $array1 [, array...$arrays ])&amp;lt;/code&amp;gt;: Wendet eine benutzerdefinierte Funktion (&amp;lt;code&amp;gt;callback&amp;lt;/code&amp;gt;) auf jedes Element eines Arrays an und gibt ein neues Array mit den Ergebnissen zurück.[63] Nützlich zur Transformation von Daten (z.B. Formatierung, Bereinigung).&lt;br /&gt;
*****   &amp;lt;code&amp;gt;array_filter(array $array [, callable $callback [, int $flag = 0 ]])&amp;lt;/code&amp;gt;: Filtert Elemente eines Arrays basierend auf einer Callback-Funktion.[61] Nützlich, um nur bestimmte Zeilen aus dem Ergebnis-Array zu behalten (obwohl dies oft effizienter direkt in der SQL-Abfrage geschieht).&lt;br /&gt;
*****   &amp;lt;code&amp;gt;array_count_values(array $array)&amp;lt;/code&amp;gt;: Zählt, wie oft jeder eindeutige Wert in einem eindimensionalen Array vorkommt.[63] Kann nützlich sein, wenn man z.B. mit &amp;lt;code&amp;gt;array_column&amp;lt;/code&amp;gt; eine Spalte extrahiert hat und die Häufigkeit bestimmter Werte (z.B. Status) wissen möchte.&lt;br /&gt;
&lt;br /&gt;
Diese Funktionen, insbesondere&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;foreach&amp;lt;/code&amp;gt;und&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;count&amp;lt;/code&amp;gt;, sind grundlegende Werkzeuge bei der Verarbeitung von Datenbankergebnissen in PHP.&lt;br /&gt;
&lt;br /&gt;
== (6) Zusammenfassung und Best Practices ==&lt;br /&gt;
&lt;br /&gt;
Die Integration von Datenbanken ist ein zentraler Aspekt der PHP-Webentwicklung. Dieses Kapitel hat die Grundlagen für die Interaktion mit MySQL-Datenbanken gelegt. Hier sind die wichtigsten Punkte und Best Practices zusammengefasst:&lt;br /&gt;
&lt;br /&gt;
***   &amp;#039;&amp;#039;&amp;#039;Wahl der Erweiterung:&amp;#039;&amp;#039;&amp;#039; Verwenden Sie ausschließlich die modernen Erweiterungen &amp;#039;&amp;#039;&amp;#039;PDO&amp;#039;&amp;#039;&amp;#039; oder &amp;#039;&amp;#039;&amp;#039;MySQLi&amp;#039;&amp;#039;&amp;#039;.[2, 29, 5, 15]&lt;br /&gt;
****   &amp;#039;&amp;#039;&amp;#039;PDO&amp;#039;&amp;#039;&amp;#039; wird generell für neue Projekte empfohlen, da es Datenbankunabhängigkeit bietet und eine konsistentere, oft als einfacher empfundene API (insbesondere bei der Parameterbindung) hat.[16, 3, 64]&lt;br /&gt;
****   &amp;#039;&amp;#039;&amp;#039;MySQLi&amp;#039;&amp;#039;&amp;#039; ist eine gute Wahl, wenn ausschließlich mit MySQL gearbeitet wird und spezifische MySQL-Funktionen benötigt werden oder wenn eine prozedurale Schnittstelle bevorzugt wird.[15]&lt;br /&gt;
***   &amp;#039;&amp;#039;&amp;#039;Vermeidung von &amp;lt;code&amp;gt;mysql_*&amp;lt;/code&amp;gt;:&amp;#039;&amp;#039;&amp;#039; Die alten &amp;lt;code&amp;gt;mysql_*&amp;lt;/code&amp;gt; Funktionen sind seit PHP 7.0 entfernt und &amp;#039;&amp;#039;&amp;#039;dürfen nicht mehr verwendet werden&amp;#039;&amp;#039;&amp;#039;. Sie sind unsicher und inkompatibel.[28, 29]&lt;br /&gt;
***   &amp;#039;&amp;#039;&amp;#039;Prepared Statements:&amp;#039;&amp;#039;&amp;#039; Dies ist die &amp;#039;&amp;#039;&amp;#039;wichtigste Sicherheitsmaßnahme&amp;#039;&amp;#039;&amp;#039;. Verwenden Sie Prepared Statements &amp;#039;&amp;#039;&amp;#039;immer&amp;#039;&amp;#039;&amp;#039; dann, wenn externe Daten (Benutzereingaben, URL-Parameter etc.) Teil einer SQL-Abfrage werden (insbesondere bei &amp;lt;code&amp;gt;WHERE&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;INSERT&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;UPDATE&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;DELETE&amp;lt;/code&amp;gt;).[2, 25, 5] Sie verhindern SQL Injection zuverlässig durch die Trennung von Code und Daten.&lt;br /&gt;
***   &amp;#039;&amp;#039;&amp;#039;Sichere Fehlerbehandlung:&amp;#039;&amp;#039;&amp;#039; Konfigurieren Sie PDO (&amp;lt;code&amp;gt;PDO::ATTR_ERRMODE =&amp;amp;gt; PDO::ERRMODE_EXCEPTION&amp;lt;/code&amp;gt;) oder MySQLi (&amp;lt;code&amp;gt;mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT)&amp;lt;/code&amp;gt;) so, dass Fehler als Exceptions behandelt werden.[1, 18] Fangen Sie diese Exceptions ab und loggen Sie detaillierte Fehlermeldungen serverseitig, anstatt sie dem Benutzer anzuzeigen.[20, 5]&lt;br /&gt;
***   &amp;#039;&amp;#039;&amp;#039;Input Validation:&amp;#039;&amp;#039;&amp;#039; Validieren Sie alle Benutzereingaben serverseitig auf Typ, Format und erlaubte Werte, bevor Sie sie an die Datenbank übergeben. Dies ist eine wichtige zusätzliche Schutzschicht.[4, 5]&lt;br /&gt;
***   &amp;#039;&amp;#039;&amp;#039;Prinzip der geringsten Rechte (Least Privilege):&amp;#039;&amp;#039;&amp;#039; Der Datenbankbenutzer, den Ihre PHP-Anwendung verwendet, sollte nur die minimal notwendigen Berechtigungen für seine Aufgaben besitzen.[5]&lt;br /&gt;
***   &amp;#039;&amp;#039;&amp;#039;Sichere Zugangsdaten:&amp;#039;&amp;#039;&amp;#039; Speichern Sie Datenbank-Credentials (Hostname, Benutzername, Passwort) niemals direkt im PHP-Code oder in Dateien innerhalb des Web-Roots.[20, 65] Verwenden Sie stattdessen Konfigurationsdateien außerhalb des öffentlich zugänglichen Bereichs oder Umgebungsvariablen.&lt;br /&gt;
***   &amp;#039;&amp;#039;&amp;#039;Code-Struktur:&amp;#039;&amp;#039;&amp;#039; Kapseln Sie Datenbankverbindungslogik und wiederkehrende Abfragen in Funktionen oder Klassen, um die Wartbarkeit und Wiederverwendbarkeit zu verbessern.[20]&lt;br /&gt;
***   &amp;#039;&amp;#039;&amp;#039;Effizienz:&amp;#039;&amp;#039;&amp;#039; Laden Sie große Ergebnismengen nicht komplett in den Speicher (&amp;lt;code&amp;gt;fetchAll&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;fetch_all&amp;lt;/code&amp;gt;). Verwenden Sie stattdessen zeilenweise Verarbeitung (&amp;lt;code&amp;gt;while&amp;lt;/code&amp;gt;/&amp;lt;code&amp;gt;fetch&amp;lt;/code&amp;gt;) oder Iteratoren (PDO mit PHP 8+).[37, 40] Nutzen Sie persistente Verbindungen nur nach sorgfältiger Abwägung der Vor- und Nachteile.[7, 27]&lt;br /&gt;
***   &amp;#039;&amp;#039;&amp;#039;Defense-in-Depth:&amp;#039;&amp;#039;&amp;#039; Betrachten Sie Sicherheit als Gesamtkonzept. Die Kombination aus der richtigen API-Wahl, konsequenten Prepared Statements, Eingabevalidierung, minimalen Rechten, sicherer Fehlerbehandlung, sicherer Speicherung von Zugangsdaten und regelmäßigen Software-Updates bildet eine robuste Verteidigung.&lt;/div&gt;</summary>
		<author><name>Völkl Anna</name></author>
	</entry>
	<entry>
		<id>https://mediawiki.fernfh.ac.at/mediawiki/index.php?title=Einf%C3%BChrung_in_PHP_und_Webentwicklung&amp;diff=6568</id>
		<title>Einführung in PHP und Webentwicklung</title>
		<link rel="alternate" type="text/html" href="https://mediawiki.fernfh.ac.at/mediawiki/index.php?title=Einf%C3%BChrung_in_PHP_und_Webentwicklung&amp;diff=6568"/>
		<updated>2025-04-01T21:10:53Z</updated>

		<summary type="html">&lt;p&gt;Völkl Anna: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=== Webentwicklung: Grundlagen und Architektur ===&lt;br /&gt;
==== Das Client-Server-Modell ====&lt;br /&gt;
Das Client-Server-Modell ist eine grundlegende Architektur der modernen Webentwicklung. Es beschreibt die Interaktion zwischen zwei Parteien: einem Client, der eine Anfrage stellt, und einem Server, der auf diese Anfrage antwortet.&lt;br /&gt;
Ein Server stellt Dienste, Ressourcen oder Informationen bereit. Im Kontext der Webentwicklung ist ein Webserver ein Programm, das Webseiten, APIs oder andere Inhalte an Clients ausliefert. Beispiele für Webserver-Software sind Apache, Nginx und IIS (Internet Information Services).&lt;br /&gt;
Ein Client ist ein Gerät oder eine Anwendung, die diese Ressourcen vom Server anfordert. Dies kann ein Webbrowser wie Chrome, Firefox oder Microsoft Edge sein, aber auch eine mobile App oder ein anderes Programm, das über das Internet kommuniziert.&lt;br /&gt;
Ein Beispiel aus dem Alltag: Wenn Sie eine Webseite in Ihrem Browser aufrufen, fungiert der Browser als Client. Der Webserver, der die Inhalte dieser Webseite hostet, ist der Server. Der Browser schickt eine Anfrage (Request) an den Server, und der Server antwortet mit der angeforderten Seite (Response).&lt;br /&gt;
==== Der Request-Response-Zyklus ====&lt;br /&gt;
Der Request-Response-Zyklus beschreibt die Kommunikation zwischen Client und Server in einem Websystem. Dieser Zyklus läuft in folgenden Schritten ab:&lt;br /&gt;
Client sendet eine Anfrage (Request): Der Benutzer gibt eine URL in den Browser ein oder klickt auf einen Link. Der Browser (Client) erstellt eine HTTP-Anfrage an den entsprechenden Server.&lt;br /&gt;
Server verarbeitet die Anfrage: Der Server empfängt die Anfrage, verarbeitet sie und sucht nach den angeforderten Ressourcen (z. B. HTML-Dateien, Bilder, Datenbankinhalte).&lt;br /&gt;
Server sendet eine Antwort (Response): Nachdem die Anfrage verarbeitet wurde, sendet der Server eine HTTP-Antwort zurück an den Client. Diese Antwort enthält den Statuscode (z. B. 200 OK oder 404 Not Found) und die angeforderten Inhalte.&lt;br /&gt;
Client zeigt die Antwort an: Der Browser des Clients interpretiert die empfangenen Inhalte und zeigt die Webseite an.&lt;br /&gt;
Beispiel:&lt;br /&gt;
Der Client (Browser) fordert die Webseite https://www.fernfh.ac.at an.&lt;br /&gt;
Der Server empfängt die Anfrage, der Webserver verarbeitet die Anfrage, sucht die passende Datei und schickt diese zurück.&lt;br /&gt;
Der Browser zeigt die Webseite an.&lt;br /&gt;
&lt;br /&gt;
==== Bestandteile einer Webanwendung ====&lt;br /&gt;
===== Webserver =====&lt;br /&gt;
Ein Webserver ist eine Software, die HTTP-Anfragen verarbeitet und Webseiten oder andere Ressourcen bereitstellt. Die beiden am häufigsten verwendeten Webserver sind Apache und Nginx:&lt;br /&gt;
Apache: Ein weit verbreiteter Open-Source-Webserver, der für seine Flexibilität und umfangreiche Konfigurationsmöglichkeiten bekannt ist. Er unterstützt Module zur Erweiterung von Funktionen wie URL-Rewrites oder Authentifizierung.&lt;br /&gt;
Nginx: Ein moderner, leistungsstarker Webserver, der für seine hohe Geschwindigkeit und geringe Ressourcenbelastung geschätzt wird. Nginx eignet sich besonders gut für den Einsatz als Reverse Proxy und zur Lastverteilung.&lt;br /&gt;
Beide Serverprogramme können sowohl statische Inhalte (HTML, CSS, Bilder) als auch dynamische Inhalte (PHP-Seiten, APIs) bereitstellen.&lt;br /&gt;
===== Datenbanken =====&lt;br /&gt;
Webserver arbeiten oft mit Datenbanken zusammen, um dynamische Inhalte bereitzustellen. Während der Webserver die Anfrage verarbeitet, ruft er oft zusätzliche Daten aus einer Datenbank ab. Diese Datenbanken speichern Informationen wie Benutzerdaten, Produktkataloge oder Artikel und ermöglichen es, Webseiteninhalte dynamisch zu generieren.&lt;br /&gt;
Zu den gängigen Datenbankmanagementsystemen zählen:&lt;br /&gt;
MySQL/MariaDB: Häufig in Kombination mit PHP verwendet, besonders im E-Commerce.&lt;br /&gt;
PostgreSQL: Ein leistungsstarkes Open-Source-System mit erweiterten Funktionen.&lt;br /&gt;
SQLite: Eine leichtgewichtige Datenbank für kleinere Projekte.&lt;br /&gt;
Ein typisches Szenario in einer Webanwendung ist:&lt;br /&gt;
Der Client fordert eine Produktseite an.&lt;br /&gt;
Der Webserver verarbeitet die Anfrage.&lt;br /&gt;
Der Webserver ruft die Produktinformationen aus der Datenbank ab.&lt;br /&gt;
Der Server generiert die HTML-Seite mit den Daten und sendet sie an den Client zurück.&lt;br /&gt;
Das Zusammenspiel zwischen Webservern und Datenbanken ermöglicht es, große, dynamische E-Commerce-Plattformen wie Magento effizient zu betreiben.&lt;br /&gt;
1.1.2.3 Web-Anwendung&lt;br /&gt;
Der Kern der Anwendung, oft mit Frameworks wie Laravel, Symfony oder Magento entwickelt.&lt;br /&gt;
&lt;br /&gt;
===== Erweiterte Funktionen =====&lt;br /&gt;
&lt;br /&gt;
;OpCache&lt;br /&gt;
:Der PHP OPcache ist ein Caching-Mechanismus, der bereits kompilierte PHP-Skripte im Speicher hält, um die Ausführungszeit zu verkürzen und die Serverlast zu reduzieren. Dadurch wird vermieden, dass PHP-Dateien bei jedem Request erneut geparst und kompiliert werden, was die Performance von Webanwendungen erheblich steigert.&lt;br /&gt;
;CDN&lt;br /&gt;
:Verteilt statische Inhalte (Bilder, CSS, JS) auf mehrere Server weltweit, um die Ladezeiten zu verbessern.&lt;br /&gt;
;Varnish&lt;br /&gt;
:Reverse Proxy und Caching-Lösung, die dynamische Inhalte zwischenspeichern kann, um die Performance zu verbessern.&lt;br /&gt;
;Redis&lt;br /&gt;
:In-Memory-Datenbank, die als Cache, Session-Store oder zur Message-Queue verwendet wird.&lt;br /&gt;
;Memcached&lt;br /&gt;
:Einfacher In-Memory-Cache, um häufig genutzte Daten schneller bereitzustellen.&lt;br /&gt;
;Load Balancer&lt;br /&gt;
:Verteilt eingehende Anfragen auf mehrere Server, um Last zu verteilen und Ausfallsicherheit zu gewährleisten. Beispiele: HAProxy, AWS Elastic Load Balancing.&lt;br /&gt;
;Queue-System&lt;br /&gt;
:Verarbeitet Aufgaben asynchron, um die Hauptanwendung zu entlasten. Beispiele: RabbitMQ, Kafka.&lt;br /&gt;
;API Gateway&lt;br /&gt;
:Bietet eine zentrale Schnittstelle für externe APIs und sorgt für Sicherheit, Monitoring und Load Balancing. Beispiele: Kong, Apigee.&lt;br /&gt;
;Service Worker&lt;br /&gt;
:Wird im Browser verwendet, um Offline-Funktionalität und Caching zu ermöglichen. Besonders wichtig für Progressive Web Apps (PWAs).&lt;br /&gt;
;Monitoring &amp;amp; Logging&lt;br /&gt;
:Überwacht die Performance der Anwendung und speichert Logs. Beispiele: Grafana, Kibana, Prometheus.&lt;br /&gt;
;Firewall/WAF (Web Application Firewall)&lt;br /&gt;
:Schützt die Webanwendung vor Angriffen wie SQL-Injection, XSS und anderen Webbedrohungen. Beispiele: AWS WAF, Cloudflare WAF.&lt;br /&gt;
;Container &amp;amp; Orchestrierung&lt;br /&gt;
:Container-Technologien (z. B. Docker) und Orchestrierungstools (z. B. Kubernetes) ermöglichen eine flexible und skalierbare Bereitstellung.&lt;br /&gt;
;CI/CD-Pipeline&lt;br /&gt;
:Automatisiert den Build- und Deployment-Prozess. Beispiele: GitLab CI, Jenkins, GitHub Actions.&lt;br /&gt;
;Identity Provider (IdP)&lt;br /&gt;
:Verwaltet Benutzeridentitäten und ermöglicht Single Sign-On (SSO). Beispiele: Keycloak, Okta, Auth0.&lt;br /&gt;
;Message Broker&lt;br /&gt;
:Vermittelt Nachrichten zwischen verschiedenen Diensten. Beispiele: RabbitMQ, Apache Kafka.&lt;br /&gt;
;Edge Functions&lt;br /&gt;
:Kleine Funktionen, die nahe am Client ausgeführt werden, um die Latenz zu verringern. Beispiele: Cloudflare Workers, AWS Lambda@Edge.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== HTTP und HTTPS ===&lt;br /&gt;
Das Hypertext Transfer Protocol (HTTP) ist das Fundament der Kommunikation im World Wide Web. Es beschreibt, wie Clients (z. B. Webbrowser) mit Servern interagieren, um Webseiten und andere Ressourcen abzurufen. HTTPS ist die sichere Version von HTTP und nutzt Verschlüsselungstechnologien wie SSL/TLS, um die Kommunikation vor Manipulation und Abhören zu schützen.&lt;br /&gt;
==== Das Hypertext Transfer Protocol ====&lt;br /&gt;
HTTP ist ein textbasiertes Protokoll, das den Austausch von Informationen zwischen einem Client und einem Server regelt. Es arbeitet nach dem Request-Response-Prinzip: Der Client stellt eine Anfrage (Request), und der Server liefert eine Antwort (Response) zurück.&lt;br /&gt;
Merkmale von HTTP:&lt;br /&gt;
Verbindungslos: Jede Anfrage wird unabhängig behandelt. Es gibt keine dauerhafte Verbindung zwischen Client und Server.&lt;br /&gt;
Statusbasiert: Der Server antwortet immer mit einem Statuscode, der den Erfolg oder Misserfolg der Anfrage angibt.&lt;br /&gt;
Anwendungsprotokoll: HTTP wird auf der Anwendungsschicht genutzt und baut auf Protokollen wie TCP/IP auf.&lt;br /&gt;
Ein typisches Beispiel für eine HTTP-Anfrage ist das Abrufen einer Webseite. Wenn ein Benutzer eine URL in den Browser eingibt, sendet der Browser eine HTTP-Anfrage an den Server, der die gewünschte Seite bereitstellt.&lt;br /&gt;
===== HTTP-Methoden =====&lt;br /&gt;
HTTP bietet verschiedene Methoden, die angeben, welche Art von Aktion der Client auf dem Server ausführen möchte. Die vier wichtigsten Methoden sind:&lt;br /&gt;
&lt;br /&gt;
====== GET ======&lt;br /&gt;
Wird verwendet, um Daten vom Server abzurufen.&lt;br /&gt;
Hat keine Nebenwirkungen auf den Server (idempotent).&lt;br /&gt;
Beispiel: Abrufen einer Webseite oder eines Bildes.&amp;lt;pre&amp;gt;GET /index.html HTTP/1.1&amp;lt;br&amp;gt;Host: example.com&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== POST ======&lt;br /&gt;
Wird verwendet, um Daten an den Server zu senden (z. B. Formulareinsendungen).&lt;br /&gt;
Kann neue Ressourcen erstellen oder bestehende aktualisieren.&lt;br /&gt;
Beispiel: Ein Benutzer sendet ein Kontaktformular ab.&amp;lt;pre&amp;gt;POST /contact HTTP/1.1&amp;lt;br&amp;gt;Host: example.com&amp;lt;br&amp;gt;Content-Type: application/x-www-form-urlencoded&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;name=John&amp;amp;message=Hello&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== PUT ======&lt;br /&gt;
Wird verwendet, um eine Ressource auf dem Server zu erstellen oder zu ersetzen.&lt;br /&gt;
Beispiel: Hochladen oder Aktualisieren einer Datei.&amp;lt;pre&amp;gt;PUT /file.txt HTTP/1.1&lt;br /&gt;
&lt;br /&gt;
Host: example.com&lt;br /&gt;
&lt;br /&gt;
Content-Type: text/plain&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;This is a file content.&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====== DELETE ======&lt;br /&gt;
Wird verwendet, um eine Ressource vom Server zu löschen.&lt;br /&gt;
Beispiel: Löschen einer Datei oder eines Eintrags.&amp;lt;pre&amp;gt;DELETE /file.txt HTTP/1.1&lt;br /&gt;
&lt;br /&gt;
Host: example.com&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Andere wichtige Methoden sind HEAD, OPTIONS und PATCH, die je nach Anwendungsszenario genutzt werden.&lt;br /&gt;
&lt;br /&gt;
===== HTTP Statuscodes =====&lt;br /&gt;
HTTP-Statuscodes sind dreistellige Zahlen, die angeben, wie der Server die Anfrage des Clients verarbeitet hat &amp;lt;ref&amp;gt;HTTP response status codes (https://developer.mozilla.org/en-US/docs/Web/HTTP/Status) . Sie sind in fünf Kategorien unterteilt:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
1xx Informationelle Antworten&lt;br /&gt;
&lt;br /&gt;
2xx Erfolgreiche Anfragen&lt;br /&gt;
&lt;br /&gt;
3xx Umleitungen&lt;br /&gt;
&lt;br /&gt;
4xx Clientfehler&lt;br /&gt;
&lt;br /&gt;
5xx Serverfehler&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Wichtige Statuscodes:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
200 OK: Die Anfrage war erfolgreich. Die angeforderten Daten werden zurückgegeben.&lt;br /&gt;
&lt;br /&gt;
301 Moved Permanently: Die angeforderte Ressource wurde dauerhaft an eine neue URL verschoben.&lt;br /&gt;
&lt;br /&gt;
302 Found: Temporäre Weiterleitung.&lt;br /&gt;
&lt;br /&gt;
404 Not Found: Die angeforderte Ressource wurde nicht gefunden.&lt;br /&gt;
&lt;br /&gt;
500 Internal Server Error: Ein allgemeiner Fehler auf dem Server.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
===== Bedeutung von HTTPS und SSL/TLS =====&lt;br /&gt;
====== HTTPS (Hypertext Transfer Protocol Secure) ======&lt;br /&gt;
HTTPS ist die sichere Version von HTTP. Es verwendet Verschlüsselung, um die Kommunikation zwischen Client und Server vor Abhören und Manipulation zu schützen. Die Verschlüsselung erfolgt mithilfe von SSL (Secure Sockets Layer) bzw. TLS (Transport Layer Security).&lt;br /&gt;
Vorteile von HTTPS:&lt;br /&gt;
Vertraulichkeit: Die übertragenen Daten sind verschlüsselt und können nicht von Dritten eingesehen werden.&lt;br /&gt;
Integrität: Es wird sichergestellt, dass die Daten während der Übertragung nicht manipuliert wurden.&lt;br /&gt;
Authentizität: Der Client kann sicherstellen, dass er mit dem richtigen Server kommuniziert.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Request-Verarbeitung ====&lt;br /&gt;
&amp;lt;/ref&amp;gt;&amp;lt;p data-start=&amp;quot;185&amp;quot; data-end=&amp;quot;498&amp;quot;&amp;gt;Wenn ein Client – sei es ein Notebook, Smartphone, Desktop-PC oder ein anderer Server – eine Webanwendung aufruft, wird zunächst ein HTTP-Request an den Webserver gesendet. Diese Anfrage kann sich auf die Startseite (die sogenannte &amp;quot;Main Page&amp;quot;), eine Unterseite oder auch einen spezifischen API-Endpunkt beziehen.&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p data-start=&amp;quot;500&amp;quot; data-end=&amp;quot;856&amp;quot;&amp;gt;Der Webserver empfängt diesen Request und analysiert ihn auf Basis seiner Konfiguration. Je nach Art der angeforderten Ressource unterscheidet sich das weitere Vorgehen: Handelt es sich um eine statische Datei, beispielsweise ein Bild, ein CSS-Stylesheet oder ein JavaScript-File, so wird diese direkt und ohne weitere Verarbeitung vom Server ausgeliefert.&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p data-start=&amp;quot;858&amp;quot; data-end=&amp;quot;1336&amp;quot;&amp;gt;Wird hingegen eine dynamische Ressource angefragt – typischerweise ein PHP-Skript –, so übergibt der Webserver die Ausführung an den konfigurierten PHP-Interpreter. Dieser verarbeitet das Skript schrittweise. Im Rahmen dieser Verarbeitung kann es notwendig sein, Daten aus einer Datenbank, beispielsweise MySQL, abzurufen. Dies ist etwa dann der Fall, wenn die dynamisch generierte Seite Produktinformationen anzeigen soll, wie es in typischen E-Commerce-Anwendungen üblich ist.&amp;amp;nbsp; Sobald die benötigten Daten durch den PHP-Interpreter aus der Datenbank – etwa MySQL – abgerufen wurden, erfolgt deren Weiterverarbeitung innerhalb des PHP-Skripts. Dabei werden die Daten strukturiert, inhaltlich aufbereitet und schließlich in ein HTML-Dokument eingebettet, um eine für den Client interpretierbare Darstellung zu ermöglichen.&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p data-start=&amp;quot;524&amp;quot; data-end=&amp;quot;874&amp;quot;&amp;gt;Nach Abschluss dieser Verarbeitung wird die vollständige HTTP-Antwort – bestehend aus HTML (und gegebenenfalls eingebetteten oder verlinkten CSS- und JavaScript-Ressourcen) – an den Webserver zurückgegeben. Der Webserver wiederum überträgt diese Antwort an den anfragenden Client, sodass dort die fertige Web-Seite im Browser dargestellt werden kann.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[file:SED501-Webserver-Requests.png|600px|center|thumb|Requestverarbeitung]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Tools ====&lt;br /&gt;
==== Web Developer Tools ====&lt;br /&gt;
Die [https://developer.chrome.com/ Chrome Developer Tools (DevTools)] (&amp;quot;Developer Toolbar&amp;quot;)  sind ein mächtes Werkzeug für Entwickler*innen und ein integraler Bestandteil moderner Webentwicklung. Sie ermöglichen eine tiefgehende Analyse, Inspektion und Optimierung von Webseiten direkt im Browser. Sie sind in Google Chrome sowie in Chromium-basierten Browsern (z. B. Microsoft Edge) integriert und stellen ein unverzichtbares Werkzeug für Frontend- und Backend-Entwickler dar.&lt;br /&gt;
=== 1. Zugriff und grundlegender Aufbau ===&lt;br /&gt;
&amp;lt;p data-start=&amp;quot;718&amp;quot; data-end=&amp;quot;1038&amp;quot;&amp;gt;Die Developer Tools können in Chrome durch einen Rechtsklick auf eine Seite und Auswahl von &amp;lt;em data-start=&amp;quot;810&amp;quot; data-end=&amp;quot;825&amp;quot;&amp;gt;„Untersuchen“&amp;lt;/em&amp;gt; oder über die Taste &amp;lt;code data-start=&amp;quot;846&amp;quot; data-end=&amp;quot;851&amp;quot;&amp;gt;F12&amp;lt;/code&amp;gt; bzw. &amp;lt;code data-start=&amp;quot;857&amp;quot; data-end=&amp;quot;878&amp;quot;&amp;gt;Strg + Umschalt + I&amp;lt;/code&amp;gt; (bzw. &amp;lt;code data-start=&amp;quot;885&amp;quot; data-end=&amp;quot;890&amp;quot;&amp;gt;Cmd&amp;lt;/code&amp;gt; auf macOS) geöffnet werden. Die Benutzeroberfläche ist in mehrere Bereiche unterteilt, von denen jeder spezifische Aspekte der Webseite analysiert:&amp;lt;/p&amp;gt;&lt;br /&gt;
*&amp;lt;p data-start=&amp;quot;1042&amp;quot; data-end=&amp;quot;1054&amp;quot;&amp;gt;&amp;lt;strong data-start=&amp;quot;1042&amp;quot; data-end=&amp;quot;1054&amp;quot;&amp;gt;Elements&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
*&amp;lt;p data-start=&amp;quot;1057&amp;quot; data-end=&amp;quot;1068&amp;quot;&amp;gt;&amp;lt;strong data-start=&amp;quot;1057&amp;quot; data-end=&amp;quot;1068&amp;quot;&amp;gt;Console&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
*&amp;lt;p data-start=&amp;quot;1071&amp;quot; data-end=&amp;quot;1082&amp;quot;&amp;gt;&amp;lt;strong data-start=&amp;quot;1071&amp;quot; data-end=&amp;quot;1082&amp;quot;&amp;gt;Sources&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
*&amp;lt;p data-start=&amp;quot;1085&amp;quot; data-end=&amp;quot;1096&amp;quot;&amp;gt;&amp;lt;strong data-start=&amp;quot;1085&amp;quot; data-end=&amp;quot;1096&amp;quot;&amp;gt;Network&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
*&amp;lt;p data-start=&amp;quot;1099&amp;quot; data-end=&amp;quot;1114&amp;quot;&amp;gt;&amp;lt;strong data-start=&amp;quot;1099&amp;quot; data-end=&amp;quot;1114&amp;quot;&amp;gt;Performance&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
*&amp;lt;p data-start=&amp;quot;1117&amp;quot; data-end=&amp;quot;1127&amp;quot;&amp;gt;&amp;lt;strong data-start=&amp;quot;1117&amp;quot; data-end=&amp;quot;1127&amp;quot;&amp;gt;Memory&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
*&amp;lt;p data-start=&amp;quot;1130&amp;quot; data-end=&amp;quot;1145&amp;quot;&amp;gt;&amp;lt;strong data-start=&amp;quot;1130&amp;quot; data-end=&amp;quot;1145&amp;quot;&amp;gt;Application&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
*&amp;lt;p data-start=&amp;quot;1148&amp;quot; data-end=&amp;quot;1160&amp;quot;&amp;gt;&amp;lt;strong data-start=&amp;quot;1148&amp;quot; data-end=&amp;quot;1160&amp;quot;&amp;gt;Security&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
*&amp;lt;p data-start=&amp;quot;1163&amp;quot; data-end=&amp;quot;1177&amp;quot;&amp;gt;&amp;lt;strong data-start=&amp;quot;1163&amp;quot; data-end=&amp;quot;1177&amp;quot;&amp;gt;Lighthouse&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p data-start=&amp;quot;4991&amp;quot; data-end=&amp;quot;5488&amp;quot;&amp;gt;Die Chrome Developer Tools sind in der Praxis unverzichtbar für die Fehlersuche und Qualitätskontrolle. Sie ermöglichen es, in Echtzeit auf das Verhalten von Webseiten zu reagieren, Styles und Layouts zu debuggen, Speicherzustände zu analysieren und die Kommunikation mit dem Server zu prüfen. Besonders im Zusammenspiel mit serverseitigen Technologien wie PHP oder datenbankbasierten Architekturen bieten sie wertvolle Einsichten in das Zusammenspiel zwischen Frontend und Backend.&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p data-start=&amp;quot;5490&amp;quot; data-end=&amp;quot;5781&amp;quot;&amp;gt;Die effektive Nutzung der DevTools ist somit eine Schlüsselkompetenz für Webentwicklerinnen und -entwickler, insbesondere im Kontext komplexer und interaktiver Anwendungen. Eine fundierte Kenntnis ihrer Funktionalitäten trägt maßgeblich zur Effizienz und Qualität der Entwicklungsarbeit bei.&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p data-start=&amp;quot;1179&amp;quot; data-end=&amp;quot;1260&amp;quot;&amp;gt;Im Folgenden werden die wichtigsten Bereiche und ihre Funktionalitäten erläutert.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Überblick über zentrale Funktionen ===&lt;br /&gt;
==== Elements: DOM-Inspektion und Live-Manipulation ====&lt;br /&gt;
&amp;lt;p data-start=&amp;quot;1367&amp;quot; data-end=&amp;quot;1535&amp;quot;&amp;gt;Der &amp;lt;em data-start=&amp;quot;1371&amp;quot; data-end=&amp;quot;1381&amp;quot;&amp;gt;Elements&amp;lt;/em&amp;gt;-Tab zeigt die aktuelle Struktur des Document Object Models (DOM) an, das der Browser zur Darstellung der Webseite generiert hat. Es ermöglicht die Inspektion und Bearbeitung der DOM-Struktur einer Webseite in Echtzeit. Hier können HTML-Elemente untersucht, CSS-Regeln direkt verändert und das Layout sowie das Box-Modell eines Elements visualisiert werden. Dies ist insbesondere bei der Behebung von Darstellungsfehlern oder beim gezielten Testen von Styling-Anpassungen hilfreich. Darüber hinaus lassen sich Event-Listener anzeigen und Pseudoklassen wie &amp;lt;code data-start=&amp;quot;1744&amp;quot; data-end=&amp;quot;1752&amp;quot;&amp;gt;:hover&amp;lt;/code&amp;gt; simulieren.&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p data-start=&amp;quot;1766&amp;quot; data-end=&amp;quot;1866&amp;quot;&amp;gt;Diese Funktionalitäten sind besonders hilfreich zur Fehleranalyse bei Layout- und Styling-Problemen.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Console: Debugging und Logging ====&lt;br /&gt;
&amp;lt;p data-start=&amp;quot;1909&amp;quot; data-end=&amp;quot;1985&amp;quot;&amp;gt;Die &amp;lt;em data-start=&amp;quot;1770&amp;quot; data-end=&amp;quot;1779&amp;quot;&amp;gt;Console&amp;lt;/em&amp;gt; stellt ein interaktives Interface für JavaScript dar. Sie ermöglicht das Ausführen von JavaScript-Befehlen während der Laufzeit und zeigt Fehlermeldungen, Warnungen oder benutzerdefinierte Log-Ausgaben an. Entwicklerinnen und Entwickler können die Konsole nutzen, um schnell auf DOM-Elemente zuzugreifen, Funktionen zu testen oder Probleme im Verhalten von Skripten zu identifizieren. Auch komplexere Debugging-Szenarien lassen sich mit der Konsole realisieren, insbesondere in Kombination mit Breakpoints.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Sources: Debugging und Quellcode-Analyse ====&lt;br /&gt;
&amp;lt;p data-start=&amp;quot;2405&amp;quot; data-end=&amp;quot;2491&amp;quot;&amp;gt;Im &amp;lt;em data-start=&amp;quot;2291&amp;quot; data-end=&amp;quot;2300&amp;quot;&amp;gt;Sources&amp;lt;/em&amp;gt;-Tab steht der Quellcode im Fokus. Hier können JavaScript-Dateien durchsucht und analysiert, Breakpoints gesetzt und Watch-Expressions definiert werden. Der Call Stack, die aktuellen Scope-Variablen sowie die Möglichkeit zur schrittweisen Ausführung des Codes erlauben ein präzises Debugging komplexer Client-seitiger Logik. Unterstützt wird dies durch sogenannte Sourcemaps, welche die Zuordnung zwischen komprimierten oder transpilieren Skripten (z. B. aus TypeScript) und dem ursprünglichen Quellcode ermöglichen. Das hilft bei der gezielten Analyse komplexer Client-seitiger Logik.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Network: Analyse von HTTP-Requests ====&lt;br /&gt;
&amp;lt;p data-start=&amp;quot;2838&amp;quot; data-end=&amp;quot;2907&amp;quot;&amp;gt;Der &amp;lt;em data-start=&amp;quot;2842&amp;quot; data-end=&amp;quot;2851&amp;quot;&amp;gt;Network&amp;lt;/em&amp;gt;-Tab zeigt sämtliche HTTP-Requests und -Responses, die während des Ladens und der Ausführung einer Webseite stattfinden. Hier lassen sich Details wie HTTP-Methoden, Header, Statuscodes und Antwortzeiten einsehen. Das Einsehen der Request-Daten ist beispielsweise beim Debuggen von Formularen und AJAX-Requests hilfreich, um die gesendeten Daten anzuzeigen. Mit Hilfe des Waterfall-Diagramms können zeitliche Zusammenhänge zwischen einzelnen Anfragen nachvollzogen werden. Darüber hinaus besteht die Möglichkeit, Netzwerkbedingungen künstlich zu simulieren (z. B. langsame Mobilfunkverbindungen), um das Verhalten der Anwendung unter realistischen Bedingungen zu testen.&amp;amp;nbsp;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p data-start=&amp;quot;3146&amp;quot; data-end=&amp;quot;3251&amp;quot;&amp;gt;Diese Informationen sind essenziell für Performance-Optimierung und Fehlerdiagnose bei API-Kommunikation.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Performance: Laufzeitanalyse und Rendering ====&lt;br /&gt;
&amp;lt;p data-start=&amp;quot;3306&amp;quot; data-end=&amp;quot;3429&amp;quot;&amp;gt;Der Bereich &amp;lt;em data-start=&amp;quot;3438&amp;quot; data-end=&amp;quot;3451&amp;quot;&amp;gt;Performance&amp;lt;/em&amp;gt; erlaubt eine detaillierte Aufzeichnung und Analyse der Laufzeitverhalten einer Webseite. Während eines Recordings werden Informationen über Render-Zyklen, Scripting-Zeiten, Layout-Operationen und Repaints gesammelt. Diese Daten helfen dabei, Performance-Engpässe zu identifizieren und gezielt zu optimieren, beispielsweise durch Reduktion unnötiger DOM-Manipulationen oder die Entlastung des Main Threads.&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p data-start=&amp;quot;3535&amp;quot; data-end=&amp;quot;3617&amp;quot;&amp;gt;Diese Daten helfen dabei, Engpässe und ineffiziente DOM-Updates zu identifizieren.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Application: Web Storage und Service Worker ====&lt;br /&gt;
Der &amp;lt;em data-start=&amp;quot;3863&amp;quot; data-end=&amp;quot;3876&amp;quot;&amp;gt;Application&amp;lt;/em&amp;gt;-Tab gibt Einblick in clientseitige Speichertechnologien wie Local Storage, Session Storage, IndexedDB und Cookies. Darüber hinaus lassen sich hier Service Worker untersuchen, welche im Kontext von Progressive Web Apps (PWA) für Offline-Funktionalität und Hintergrundsynchronisation zuständig sind. Auch das App-Manifest einer installierbaren Webanwendung kann hier eingesehen werden.&lt;br /&gt;
==== Security: TLS- und Zertifikatsinformationen ====&lt;br /&gt;
&amp;lt;p data-start=&amp;quot;4055&amp;quot; data-end=&amp;quot;4128&amp;quot;&amp;gt;Ein weiterer wichtiger Aspekt ist die Sicherheit der Webanwendung, die im &amp;lt;em data-start=&amp;quot;4336&amp;quot; data-end=&amp;quot;4346&amp;quot;&amp;gt;Security&amp;lt;/em&amp;gt;-Tab thematisiert wird. Dieser zeigt, ob die Seite über HTTPS geladen wurde, welches TLS-Protokoll verwendet wird, welche Zertifikate zur Absicherung eingesetzt werden und ob potenzielle Schwachstellen wie „Mixed Content“ (unsichere Ressourcen über HTTP) vorliegen.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Lighthouse: Automatisierte Audits ====&lt;br /&gt;
&amp;lt;p data-start=&amp;quot;4466&amp;quot; data-end=&amp;quot;4477&amp;quot;&amp;gt;Der &amp;lt;em data-start=&amp;quot;4636&amp;quot; data-end=&amp;quot;4648&amp;quot;&amp;gt;Lighthouse&amp;lt;/em&amp;gt;-Tab bietet ein leistungsstarkes Analysewerkzeug, mit dem eine Webanwendung automatisiert hinsichtlich Performance, Barrierefreiheit, Best Practices, Suchmaschinenfreundlichkeit (SEO) und PWA-Konformität bewertet werden kann. Die dabei erzeugten Berichte enthalten nicht nur Bewertungen, sondern auch konkrete Handlungsempfehlungen zur Optimierung.&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p data-start=&amp;quot;4569&amp;quot; data-end=&amp;quot;4668&amp;quot;&amp;gt;Die Berichte enthalten konkrete Optimierungsvorschläge und ermöglichen objektive Qualitätsanalysen.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Beispiel anhand der DevTools: Webserver erkennen&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Sofern der Webservername im Respone Header mitgeschickt wird, ist dieser im &amp;quot;Netzwerk&amp;quot; Tab nach Auswahl einer Quelle (Hauptdokument, Bild, CSS oder JavaScript-Datei, die direkt vom Server geladen wird) erkennbar.&lt;br /&gt;
&lt;br /&gt;
Oftmals wird der Webserver auch entfernt und nicht im Response Header mitgeschickt, um die Angriffsoberfläche für Schwachstellen zu minimieren.&lt;br /&gt;
&lt;br /&gt;
[[file:SED501-Developer-Toolbar.png|600px|center|thumb|Web Developer Toolbar]]&lt;br /&gt;
== PHP ==&lt;br /&gt;
&amp;lt;p&amp;gt;PHP (rekursives Akronym für PHP: Hypertext Preprocessor) ist eine weit verbreitete Programmiersprache für Web-Anwendungen.&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;Die Programmiersprache ist weit verbreitet und wird auf mehr als 75% aller Internetseiten (mit Serverseitiger Programmiersprache) verwendet.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;p&amp;gt;PHP geht auf ein Projekt von Rasmus Lerdorf aus dem Jahr 1994 zurück. Damals wurde eine Sammlung von Skripten unter dem Namen&amp;amp;nbsp; “Personal Home Page Tools” veröffentlicht.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;p&amp;gt;https://www.php.net/manual/en/history.php.php&amp;amp;nbsp;&amp;amp;nbsp;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;Derzeit (Stand Jänner 2025) ist die Version PHP 8.4 aktuell.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Web-Application Stack&amp;lt;br&amp;gt;===&lt;br /&gt;
&amp;lt;p&amp;gt;PHP Anwendungen laufen üblicherweise in einer Umgebung mit Linux, Apache und MySQL (“LAMP-Stack”) oder Linux, Nginx und MySQL (“LEMP-Stack”). Der LAMP/LEMP-Stack enthält dabei alle Bestandteile, den eine Web-Anwendung benötigt: Betriebssystem, Webserver, Skript-Interpreter und Datenbank.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;p&amp;gt;Betriebssystem: Linux&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;Webserver: Apache oder Nginx&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;Skript-Interpreter: PHP&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;Datenbank: MySQL&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;p&amp;gt;PHP kann aber auch unter Windows als auch MacOS nativ laufen.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;p&amp;gt;Lokale Entwicklung&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;Damit eine PHP Anwendung lokal (auf dem Notebook) entwickelt werden kann, benötigt man eine lauffähige Umgebung entsprechend dem LAMP oder LEMP-Stack.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;p&amp;gt;Folgende Möglichkeiten gibt es:&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;Nativ:&amp;lt;/p&amp;gt;&lt;br /&gt;
*Webserver, PHP und Datenbank laufen auf dem nativen Betriebssystem&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;p&amp;gt;Virtualisierte Umgebung:&amp;lt;/p&amp;gt;&lt;br /&gt;
*Webserver, PHP und Datenbank laufen innerhalb einer eigenen virtuellen Maschine (zB VirtualBox)&lt;br /&gt;
*Webserver, PHP und Datenbank laufen als containerisierte Umgebung (Docker)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;p&amp;gt;Es gibt eine Vielzahl an Vorlagen für virtuelle Entwicklungsmaschinen oder Container-Umgebungen.&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;Eine, der bekanntesten und populärsten Umgebungen ist ddev.&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;https://ddev.com/&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;https://ddev.com/get-started/&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;p&amp;gt;Damit können Web-Projekte schnell und einfach lokal lauffähig gemacht werden.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
#DDEV installieren&lt;br /&gt;
&amp;lt;p&amp;gt;https://ddev.com/get-started/&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
#ddev config&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;p&amp;gt;➜&amp;amp;nbsp; playground mkdir mywebdevproject&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;➜&amp;amp;nbsp; playground cd mywebdevproject&amp;amp;nbsp;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;➜&amp;amp;nbsp; mywebdevproject ddev config&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;Creating a new DDEV project config in the current directory (/home/annavoelkl/playground/mywebdevproject)&amp;amp;nbsp;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;Once completed, your configuration will be written to /home/annavoelkl/playground/mywebdevproject/.ddev/config.yaml&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;&amp;lt;br&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;Project name (mywebdevproject):&amp;amp;nbsp;&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;p&amp;gt;The docroot is the directory from which your site is served.&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;This is a relative path from your project root at /home/annavoelkl/playground/mywebdevproject&amp;amp;nbsp;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;You may leave this value blank if your site files are in the project root&amp;amp;nbsp;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;Docroot Location (current directory):&amp;amp;nbsp;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;Found a php codebase at /home/annavoelkl/playground/mywebdevproject.&amp;amp;nbsp;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;Project Type [backdrop, cakephp, craftcms, django4, drupal, drupal6, drupal7, laravel, magento, magento2, php, python, shopware6, silverstripe, typo3, wordpress] (php): php&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;Configuration complete. You may now run &amp;#039;ddev start&amp;#039;.&amp;amp;nbsp;&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;lt;p&amp;gt;&amp;lt;br&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
#DDEV Projekt starten: ddev start&lt;br /&gt;
##Die Container werden automatisch heruntergeladen und gestartet&lt;br /&gt;
#Projekt aufrufen https://mywebdevproject.ddev.site &lt;br /&gt;
##Noch gibt es hier nichts zu sehen&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
=== Das erste Programm ===&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;lt;p&amp;gt;Quelle: https://w3techs.com/technologies/overview/programming_language&amp;amp;nbsp;&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;p&amp;gt;helloworld.php&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| style=&amp;quot;height: 253px;&amp;quot; width=&amp;quot;407&amp;quot; &lt;br /&gt;
|- &lt;br /&gt;
| style=&amp;quot;width: 405px;&amp;quot; |&amp;lt;p&amp;gt;&amp;amp;lt;!DOCTYPE html&amp;amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;&amp;amp;lt;html&amp;amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;lt;head&amp;amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;lt;title&amp;amp;gt;FernFH - Web Development&amp;amp;lt;/title&amp;amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;lt;/head&amp;amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;lt;body&amp;amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;lt;?php echo &amp;quot;Hallo Welt!&amp;quot;; ?&amp;amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;lt;/body&amp;amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;&amp;amp;lt;/html&amp;amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;br&amp;gt;&amp;lt;br&amp;gt;Grundlagen ===&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
==== Variablen ====&lt;br /&gt;
&amp;lt;p&amp;gt;Variablen werden in PHP durch ein Dollar-Zeichen ($) gefolgt vom Namen der Variable dargestellt. Bei Variablennamen wird zwischen Groß- und Kleinschreibung unterschieden (case-sensitive).&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;Ein gültiger Variablenname beginnt mit einem Buchstaben (A-Z, a-z oder die Bytes von 128 bis 255) oder einem Unterstrich (&amp;quot;_&amp;quot;), gefolgt von einer beliebigen Anzahl von Buchstaben, Zahlen oder Unterstrichen. Als regulärer Ausdruck (Regular Expression) würde das wie folgt ausgedrückt: ^[a-zA-Z_\x80-\xff][a-zA-Z0-9_\x80-\xff]*$&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;p&amp;gt;Der Geltungsbereich einer Variablen ergibt sich aus dem Zusammenhang, in dem sie definiert wurde. PHP hat einen Funktionsbereich und einen globalen Bereich. Jede Variable, die außerhalb einer Funktion definiert wird, ist auf den globalen Bereich beschränkt. Wenn eine Datei eingebunden wird, erbt der darin enthaltene Code den Variablenbereich der Zeile, in der die Einbindung erfolgt.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
==== Kontrollstrukturen ====&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;p&amp;gt;Verfügbare Kontrollstrukturen in PHP sind:&amp;lt;/p&amp;gt;&lt;br /&gt;
*if&lt;br /&gt;
*else&lt;br /&gt;
*elseif/else if&lt;br /&gt;
*while&lt;br /&gt;
*do-while&lt;br /&gt;
*for&lt;br /&gt;
*foreach&lt;br /&gt;
*break / continue&lt;br /&gt;
*switch / match&lt;br /&gt;
*declare&lt;br /&gt;
*return&lt;br /&gt;
*require / include&lt;br /&gt;
*require_once / include_once&lt;br /&gt;
*goto&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
== Die echo-Anweisung in PHP ==&lt;br /&gt;
&amp;lt;p&amp;gt;Die echo-Anweisung ist ein fundamentales Sprachkonstrukt in PHP, das verwendet wird, um eine oder mehrere Ausdrücke auszugeben.3 Im Gegensatz zu Funktionen wie print() erzeugt echo keinen Rückgabewert und gilt daher als effizienter für die unmittelbare Ausgabe von Text.3 Die grundlegende Syntax von echo kann in verschiedenen Formen auftreten, wobei die gebräuchlichsten echo string1, string2,..., stringN; oder echo(string...$expressions): void sind.3 Da echo ein Sprachkonstrukt und keine eigentliche Funktion ist, können die Klammern nach dem Schlüsselwort oft weggelassen werden.3&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Die Ausgabe von einfachem Text mit echo ist denkbar einfach. Strings können entweder in einfache (&amp;#039;) oder doppelte (&amp;quot;) Anführungszeichen eingeschlossen werden.3 Beispielsweise würde der Code echo &amp;#039;Hallo Welt!&amp;#039;; oder echo &amp;quot;PHP ist toll!&amp;quot;; den jeweiligen Text direkt im Webbrowser des Nutzers anzeigen.3&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Ebenso unkompliziert ist die Ausgabe von Variablenwerten. Hat man eine Variable deklariert und ihr einen Wert zugewiesen, kann dieser Wert mit echo ausgegeben werden. Ein einfaches Beispiel wäre: $name = &amp;quot;Peter&amp;quot;; echo $name;. Dieser Code würde den Wert der Variablen $name, also &amp;quot;Peter&amp;quot;, ausgeben.3 Die direkte Ausgabe von Variablen mit echo ist ein Kernbestandteil der dynamischen Inhaltsgenerierung in PHP, da es ermöglicht, Daten, die im PHP-Code verarbeitet werden, auf der Webseite anzuzeigen.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Obwohl echo die primäre Methode zur Ausgabe in PHP ist, gibt es eine ähnliche Anweisung namens print. print erfüllt einen ähnlichen Zweck, nämlich die Ausgabe von Text, unterscheidet sich aber in einigen wesentlichen Punkten von echo.4 Ein wesentlicher Unterschied ist, dass print immer einen Integer-Wert von 1 zurückgibt, während echo keinen Rückgabewert besitzt.4 Dies macht print in bestimmten Situationen nützlich, in denen ein Rückgabewert innerhalb eines Ausdrucks benötigt wird. Ein weiterer Unterschied besteht darin, dass print nur ein einzelnes Argument (einen einzelnen String) akzeptieren kann, während echo mehrere durch Kommas getrennte Argumente verarbeiten kann.4 In Bezug auf die Geschwindigkeit wird echo im Allgemeinen als etwas schneller als print angesehen. Die folgende Tabelle fasst die Hauptunterschiede zwischen echo und print zusammen:&amp;lt;/p&amp;gt;&amp;lt;div&amp;gt;&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;p&amp;gt;Feature&amp;lt;/p&amp;gt;&lt;br /&gt;
| &amp;lt;p&amp;gt;echo&amp;lt;/p&amp;gt;&lt;br /&gt;
| &amp;lt;p&amp;gt;print&amp;lt;/p&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;p&amp;gt;Geschwindigkeit&amp;lt;/p&amp;gt;&lt;br /&gt;
| &amp;lt;p&amp;gt;Schneller&amp;lt;/p&amp;gt;&lt;br /&gt;
| &amp;lt;p&amp;gt;Langsamer&amp;lt;/p&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;p&amp;gt;Rückgabewert&amp;lt;/p&amp;gt;&lt;br /&gt;
| &amp;lt;p&amp;gt;Keiner&amp;lt;/p&amp;gt;&lt;br /&gt;
| &amp;lt;p&amp;gt;1 (Integer)&amp;lt;/p&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;p&amp;gt;Mehrere Argumente&amp;lt;/p&amp;gt;&lt;br /&gt;
| &amp;lt;p&amp;gt;Ja&amp;lt;/p&amp;gt;&lt;br /&gt;
| &amp;lt;p&amp;gt;Nein&amp;lt;/p&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/div&amp;gt;&amp;lt;p&amp;gt;In den meisten Fällen wird echo aufgrund seiner höheren Geschwindigkeit und der Möglichkeit, mehrere Argumente zu verarbeiten, bevorzugt. print kann jedoch in Situationen sinnvoll sein, in denen der Rückgabewert von 1 in einem Ausdruck verwendet werden soll.&amp;lt;/p&amp;gt;&lt;br /&gt;
== Kombinierte Ausgabe von Text und Variablen ==&lt;br /&gt;
&amp;lt;p&amp;gt;In der praktischen Webentwicklung ist es oft notwendig, statischen Text mit dynamischen Variablenwerten zu kombinieren, um informative und personalisierte Ausgaben zu erzeugen. PHP bietet hierfür verschiedene Methoden an. Eine gängige Methode ist die Verwendung des Verkettungsoperators, der in PHP durch einen Punkt (.) dargestellt wird.3 Dieser Operator erlaubt es, mehrere Strings und Variablen miteinander zu verbinden, um einen einzigen Ausgabestring zu bilden. Ein typisches Beispiel hierfür wäre:&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;$begruessung = &amp;quot;Hallo&amp;quot;;&amp;lt;br&amp;gt;$name = &amp;quot;Peter&amp;quot;;&amp;lt;br&amp;gt;echo $begruessung. &amp;quot;, &amp;quot;. $name. &amp;quot;!&amp;quot;;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;lt;/pre&amp;gt;&amp;lt;p&amp;gt;In diesem Beispiel werden die Variable $begruessung, ein Komma mit einem Leerzeichen als String und die Variable $name sowie ein Ausrufezeichen als String durch den Punkt-Operator miteinander verkettet und anschließend mit echo ausgegeben. Diese Methode bietet eine klare und explizite Möglichkeit, verschiedene Teile einer Ausgabe zusammenzufügen und ermöglicht eine präzise Kontrolle über die Formatierung.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Eine weitere sehr praktische Methode zur kombinierten Ausgabe von Text und Variablen ist die direkte Einbettung von Variablen in doppelt-Anführungszeichen.4 Innerhalb eines Strings, der in doppelten Anführungszeichen steht, erkennt PHP Variablennamen und ersetzt diese automatisch durch ihre entsprechenden Werte. Das obige Beispiel könnte also auch wie folgt geschrieben werden:&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;p&amp;gt;PHP&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;lt;p&amp;gt;$name = &amp;quot;PHP&amp;quot;;&amp;lt;br&amp;gt;echo &amp;quot;Ich liebe $name!&amp;quot;;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Hier wird die Variable $name direkt in den String eingebettet, und PHP sorgt dafür, dass bei der Ausgabe anstelle von $name der Wert der Variablen, also &amp;quot;PHP&amp;quot;, eingesetzt wird. Diese Syntax ist oft lesbarer und weniger umständlich als die Verwendung des Verkettungsoperators, insbesondere wenn mehrere Variablen in einen längeren Text eingebettet werden sollen.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Es ist wichtig zu beachten, dass die direkte Variablenersetzung nur innerhalb von doppelt-Anführungszeichen funktioniert. Bei Strings, die in einfachen Anführungszeichen stehen, werden Variablennamen als reiner Text behandelt und nicht durch ihre Werte ersetzt.4&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;In Fällen, in denen der Variablenname direkt an anderen Text anschließt und es zu einer Mehrdeutigkeit kommen könnte, bietet PHP die Möglichkeit, die Variable in geschweifte Klammern zu setzen.8 Dies hilft dem PHP-Interpreter, den Variablennamen eindeutig zu identifizieren. Ein Beispiel hierfür wäre:&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;p&amp;gt;PHP&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;lt;p&amp;gt;$version = 7;&amp;lt;br&amp;gt;echo &amp;quot;PHP {$version} ist super!&amp;quot;;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Hier stellen die geschweiften Klammern um $version sicher, dass PHP die Variable korrekt erkennt und nicht versucht, eine Variable namens $version ist zu finden.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Zusätzlich zu diesen Methoden bietet PHP eine Kurzschreibweise für die echo-Anweisung, die besonders in HTML-Templates nützlich ist. Die Syntax &amp;amp;lt;?= Ausdruck?&amp;amp;gt; ist eine verkürzte Form von &amp;amp;lt;?php echo Ausdruck;?&amp;amp;gt;.3 Sie ermöglicht es, den Wert einer Variablen oder eines Ausdrucks direkt in den HTML-Code einzufügen, ohne die vollständigen PHP-Tags verwenden zu müssen. Ein Beispiel hierfür wäre:&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;p&amp;gt;PHP&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;lt;p&amp;gt;$value = 10;&amp;lt;br&amp;gt;?&amp;amp;gt;&amp;lt;br&amp;gt;Der Wert ist: &amp;amp;lt;?= $value?&amp;amp;gt;.&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Diese Kurzschreibweise trägt zu einem aufgeräumteren Code bei, insbesondere wenn es darum geht, dynamische Inhalte in HTML-Strukturen darzustellen.&amp;lt;/p&amp;gt;&lt;br /&gt;
== Weitere wichtige PHP-Funktionen für den Einstieg ==&lt;br /&gt;
&amp;lt;p&amp;gt;Über die echo-Anweisung hinaus gibt es eine Reihe weiterer PHP-Funktionen, die für Anfänger von großer Bedeutung sind, um grundlegende Programmieraufgaben zu bewältigen.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Die Funktionen isset() und empty() spielen eine zentrale Rolle bei der Überprüfung von Variablen.9 isset($variable) prüft, ob eine Variable deklariert wurde und einen anderen Wert als null besitzt. Dies ist besonders wichtig bei der Verarbeitung von Formulardaten, um sicherzustellen, dass ein Feld tatsächlich übermittelt wurde, oder um zu vermeiden, dass auf nicht definierte Variablen zugegriffen wird, was zu Fehlern führen könnte. Im Gegensatz dazu überprüft empty($variable), ob eine Variable als &amp;quot;leer&amp;quot; betrachtet wird. Dies ist der Fall, wenn die Variable nicht gesetzt ist, den Wert null, 0, einen leeren String (&amp;quot;&amp;quot;), false oder ein leeres Array besitzt. empty() ist daher sehr nützlich für die Validierung von Benutzereingaben, um beispielsweise sicherzustellen, dass Pflichtfelder ausgefüllt wurden.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Die Funktion count() ist unerlässlich, wenn man mit Arrays arbeitet.9 Sie gibt die Anzahl der Elemente zurück, die sich in einem Array befinden. Dies ist häufig notwendig, um Schleifen zu steuern, beispielsweise um alle Elemente eines Arrays zu verarbeiten, oder um die Größe einer Datensammlung zu ermitteln.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Obwohl nicht explizit in allen Top-Funktionen-Listen erwähnt, sind strlen() und trim() grundlegende Funktionen für die Arbeit mit Strings. strlen(&amp;quot;Hallo&amp;quot;) gibt die Länge des Strings zurück, in diesem Fall 5. Dies ist wichtig für die Validierung von Texteingaben, beispielsweise um sicherzustellen, dass ein Passwort eine bestimmte Mindestlänge hat. trim(&amp;quot; Text mit Leerzeichen &amp;quot;) entfernt Leerzeichen und andere Whitespace-Zeichen am Anfang und Ende eines Strings und gibt den bereinigten String zurück. Dies ist besonders nützlich, um Benutzereingaben zu normalisieren und unerwünschte Leerzeichen zu entfernen, die zu Fehlern führen könnten.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Für die Verarbeitung von Strings, die in Arrays umgewandelt oder aus Arrays zusammengesetzt werden müssen, sind explode() und implode() sehr hilfreich.9 explode(delimiter, string) teilt einen String anhand eines angegebenen Trennzeichens in ein Array von Substrings auf. Dies ist beispielsweise nützlich, um eine durch Kommas getrennte Liste in einzelne Werte zu zerlegen. Umgekehrt verbindet implode(glue, array) die Elemente eines Arrays zu einem einzigen String, wobei optional ein Trennzeichen (glue) zwischen den Elementen eingefügt werden kann. Dies ist praktisch, um beispielsweise eine Liste von Wörtern in einem Array zu einem Satz zusammenzufügen.&amp;lt;/p&amp;gt;&lt;br /&gt;
== PHP Arrays: Grundlagen und Erstellung ==&lt;br /&gt;
&amp;lt;p&amp;gt;Ein Array in PHP ist eine strukturierte Datensammlung, die es ermöglicht, mehrere Werte unter einem einzigen Namen zu speichern und zu verwalten.14 Tatsächlich kann ein PHP-Array als eine geordnete Map betrachtet werden, die Werte mit Schlüsseln assoziiert.16 Diese Struktur ist äußerst vielseitig und kann in PHP als herkömmliches Array, als Liste (Vektor), als Hashtabelle, als Dictionary, als Collection, als Stack oder als Queue fungieren.15 Diese Flexibilität macht Arrays zu einem unverzichtbaren Werkzeug in der PHP-Programmierung.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Grundsätzlich unterscheidet man in PHP zwischen zwei Haupttypen von Arrays: indizierte Arrays und assoziative Arrays.14 Indizierte Arrays verwenden numerische Indizes, um auf die einzelnen Werte im Array zuzugreifen. Diese Indizes beginnen in PHP traditionell bei 0 für das erste Element und werden für jedes weitere Element fortlaufend um eins erhöht.14 Assoziative Arrays hingegen verwenden benannte Schlüssel (die entweder Strings oder Integers sein können), um die in ihnen gespeicherten Werte zu identifizieren und abzurufen.14 Dies ermöglicht es, Daten anhand von aussagekräftigen Bezeichnungen zu speichern und zu verwalten, was die Lesbarkeit und Wartbarkeit des Codes verbessern kann.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Numerisch indizierte Arrays können auf verschiedene Weisen erstellt werden. Eine traditionelle Methode ist die Verwendung der array()-Sprachkonstruktion.14 Dabei werden die gewünschten Werte als durch Kommas getrennte Argumente an array() übergeben:&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;p&amp;gt;PHP&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;lt;p&amp;gt;$farben = array(&amp;quot;rot&amp;quot;, &amp;quot;grün&amp;quot;, &amp;quot;blau&amp;quot;);&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Seit PHP Version 5.4 gibt es eine kürzere und oft bevorzugte Syntax zur Erstellung von Arrays, die als Short Array Syntax bekannt ist. Hierbei werden die Array-Elemente einfach in eckige Klammern `` eingeschlossen 15:&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;p&amp;gt;PHP&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;lt;p&amp;gt;$zahlen = [1, 2, 3];&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Beide Methoden führen zum gleichen Ergebnis: einem indizierten Array, bei dem &amp;quot;rot&amp;quot; den Index 0, &amp;quot;grün&amp;quot; den Index 1 und &amp;quot;blau&amp;quot; den Index 2 hat, bzw. bei $zahlen entsprechend 1, 2 und 3.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Assoziative Arrays werden ebenfalls mit array() oder der Kurzschreibweise `` erstellt, wobei hier die Schlüssel und Wertepaare mit dem Operator =&amp;amp;gt; verbunden werden 14:&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;p&amp;gt;PHP&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;lt;p&amp;gt;$alter = array(&amp;quot;Peter&amp;quot; =&amp;amp;gt; 35, &amp;quot;Lisa&amp;quot; =&amp;amp;gt; 32);&amp;lt;br&amp;gt;$noten =;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;In diesen Beispielen sind &amp;quot;Peter&amp;quot; und &amp;quot;Lisa&amp;quot; bzw. &amp;quot;John&amp;quot; und &amp;quot;Sally&amp;quot; die Schlüssel, und 35, 32, 91 und 94 die zugehörigen Werte. Assoziative Arrays eignen sich besonders gut, um Datensätze zu repräsentieren, bei denen jedes Element eine eindeutige Bezeichnung hat, wie beispielsweise Benutzerinformationen oder Konfigurationseinstellungen.&amp;lt;/p&amp;gt;&lt;br /&gt;
== PHP Arrays: Verwendung und Zugriff auf Elemente ==&lt;br /&gt;
&amp;lt;p&amp;gt;Der Zugriff auf die Elemente eines PHP-Arrays erfolgt über ihren numerischen Index (bei indizierten Arrays) oder ihren benannten Schlüssel (bei assoziativen Arrays). Die Syntax hierfür ist in beiden Fällen die gleiche: dem Array-Namen folgen eckige Klammern, in denen der gewünschte Index oder Schlüssel angegeben wird.14&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Bei einem numerisch indizierten Array wie $farben = [&amp;quot;rot&amp;quot;, &amp;quot;grün&amp;quot;, &amp;quot;blau&amp;quot;]; kann auf das erste Element (&amp;quot;rot&amp;quot;) mit $farben, auf das zweite Element (&amp;quot;grün&amp;quot;) mit $farben1 und auf das dritte Element (&amp;quot;blau&amp;quot;) mit $farben2 zugegriffen werden.14&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Bei einem assoziativen Array wie $alter = [&amp;quot;Peter&amp;quot; =&amp;amp;gt; 35, &amp;quot;Lisa&amp;quot; =&amp;amp;gt; 32]; kann auf das Alter von Peter mit $alter[&amp;quot;Peter&amp;quot;] (was 35 zurückgibt) und auf das Alter von Lisa mit $alter[&amp;quot;Lisa&amp;quot;] (was 32 zurückgibt) zugegriffen werden.14&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;PHP-Arrays sind dynamisch, was bedeutet, dass ihre Elemente nach der Erstellung geändert werden können. Um den Wert eines vorhandenen Array-Elements zu ändern, weist man dem entsprechenden Index oder Schlüssel einfach einen neuen Wert zu 17:&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;p&amp;gt;PHP&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;lt;p&amp;gt;$farben = &amp;quot;gelb&amp;quot;; // Das erste Element von $farben ist nun &amp;quot;gelb&amp;quot;&amp;lt;br&amp;gt;$alter[&amp;quot;Peter&amp;quot;] = 36; // Das Alter von Peter in $alter ist nun 36&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Ebenso einfach ist das Hinzufügen neuer Elemente zu einem Array. Bei einem numerisch indizierten Array kann ein neues Element am Ende hinzugefügt werden, indem man dem Array-Namen leere eckige Klammern `` folgen lässt und dann den zuzuweisenden Wert angibt 14:&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;p&amp;gt;PHP&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;lt;p&amp;gt;$zahlen = [1, 2, 3];&amp;lt;br&amp;gt;$zahlen = 4; // Das Array $zahlen enthält nun [1, 2, 3, 4]&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Bei einem assoziativen Array fügt man ein neues Schlüssel-Wert-Paar hinzu, indem man einen neuen Schlüssel in den eckigen Klammern angibt und ihm einen Wert zuweist 17:&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;p&amp;gt;PHP&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;lt;p&amp;gt;$noten =;&amp;lt;br&amp;gt;$noten = 88; // Das Array $noten enthält nun&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Diese Flexibilität bei der Verwendung und Modifikation von Array-Elementen macht PHP-Arrays zu einem mächtigen Werkzeug für die Datenverwaltung.&amp;lt;/p&amp;gt;&lt;br /&gt;
== PHP Arrays: Durchlaufen und Ausgeben ==&lt;br /&gt;
&amp;lt;p&amp;gt;Um alle Elemente eines Arrays zu verarbeiten oder auszugeben, ist es notwendig, das Array zu durchlaufen. PHP bietet hierfür verschiedene Schleifenkonstrukte an.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Für numerisch indizierte Arrays, bei denen die Schlüssel fortlaufende Integer-Werte sind, kann die traditionelle for-Schleife verwendet werden.19 Dabei wird ein Zähler initialisiert, der so lange inkrementiert wird, bis er die Anzahl der Array-Elemente erreicht hat. Innerhalb der Schleife kann dann über den aktuellen Wert des Zählers als Index auf das jeweilige Array-Element zugegriffen werden. Ein Beispiel hierfür wäre:&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;p&amp;gt;PHP&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;lt;p&amp;gt;$farben = [&amp;quot;rot&amp;quot;, &amp;quot;grün&amp;quot;, &amp;quot;blau&amp;quot;];&amp;lt;br&amp;gt;for ($i = 0; $i &amp;amp;lt; count($farben); $i++) {&amp;lt;br&amp;gt;&amp;amp;nbsp; &amp;amp;nbsp; echo $farben[$i]. &amp;quot; &amp;quot;;&amp;lt;br&amp;gt;}&amp;lt;br&amp;gt;// Ausgabe: rot grün blau&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Obwohl die for-Schleife für indizierte Arrays funktioniert, ist die foreach-Schleife in PHP die vielseitigere und empfohlene Methode, um Arrays jeglicher Art zu durchlaufen, sowohl indizierte als auch assoziative.19 Die foreach-Schleife bietet zwei Hauptsyntaxformen. Die erste ermöglicht den direkten Zugriff auf die Werte des Arrays:&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;p&amp;gt;PHP&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;lt;p&amp;gt;foreach ($array as $value) {&amp;lt;br&amp;gt;&amp;amp;nbsp; &amp;amp;nbsp; // Code, der mit jedem Wert des Arrays ausgeführt wird&amp;lt;br&amp;gt;}&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Die zweite Form erlaubt den Zugriff sowohl auf den Schlüssel als auch auf den Wert jedes Elements:&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;p&amp;gt;PHP&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;lt;p&amp;gt;foreach ($array as $key =&amp;amp;gt; $value) {&amp;lt;br&amp;gt;&amp;amp;nbsp; &amp;amp;nbsp; // Code, der mit jedem Schlüssel-Wert-Paar des Arrays ausgeführt wird&amp;lt;br&amp;gt;}&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Ein Beispiel für die Verwendung von foreach mit einem assoziativen Array wäre:&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;p&amp;gt;PHP&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;lt;p&amp;gt;$alter = [&amp;quot;Peter&amp;quot; =&amp;amp;gt; 35, &amp;quot;Lisa&amp;quot; =&amp;amp;gt; 32];&amp;lt;br&amp;gt;foreach ($alter as $name =&amp;amp;gt; $jahr) {&amp;lt;br&amp;gt;&amp;amp;nbsp; &amp;amp;nbsp; echo $name. &amp;quot;: &amp;quot;. $jahr. &amp;quot;&amp;amp;lt;br&amp;amp;gt;&amp;quot;;&amp;lt;br&amp;gt;}&amp;lt;br&amp;gt;// Ausgabe:&amp;lt;br&amp;gt;// Peter: 35&amp;lt;br&amp;gt;// Lisa: 32&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Für Entwicklungs- und Debugging-Zwecke ist es oft nützlich, den gesamten Inhalt eines Arrays auf einmal auszugeben. PHP bietet hierfür die Funktion print_r() an.9 print_r($array) gibt eine für Menschen lesbare Darstellung der Struktur und der Werte des Arrays aus. Bei komplexen oder mehrdimensionalen Arrays ist dies besonders hilfreich, um den Überblick zu behalten. Oft wird die Ausgabe von print_r() in &amp;amp;lt;pre&amp;amp;gt;-Tags eingeschlossen, um die Formatierung im Browser zu verbessern.24&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Eine weitere Funktion zur Ausgabe von Array-Inhalten ist var_dump().23 Im Gegensatz zu print_r() liefert var_dump() detailliertere Informationen über die Array-Elemente, einschließlich ihres Datentyps und ihrer Länge. Auch var_dump() wird primär für Debugging-Zwecke verwendet, wenn es darum geht, die genaue Beschaffenheit der in einem Array gespeicherten Daten zu untersuchen.&amp;lt;/p&amp;gt;&lt;br /&gt;
== Wichtige PHP Array-Funktionen ==&lt;br /&gt;
&amp;lt;p&amp;gt;PHP bietet eine Vielzahl von Funktionen, die speziell für die Arbeit mit Arrays entwickelt wurden und die Manipulation und Verarbeitung von Array-Daten erheblich erleichtern.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Es gibt Funktionen, um Elemente zu einem Array hinzuzufügen oder daraus zu entfernen. array_push($array, $value1, $value2,...) fügt ein oder mehrere Elemente am Ende eines Arrays hinzu.14 array_pop($array) entfernt das letzte Element aus einem Array und gibt es zurück.14 Um Elemente am Anfang eines Arrays zu bearbeiten, gibt es array_shift($array), das das erste Element entfernt und zurückgibt 17, und array_unshift($array, $value1, $value2,...), das ein oder mehrere Elemente am Anfang des Arrays einfügt.17&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Für das Zusammenführen von Arrays steht die Funktion array_merge($array1, $array2,...) zur Verfügung, die zwei oder mehr Arrays zu einem neuen Array kombiniert.9 Dabei ist zu beachten, dass numerische Schlüssel neu indiziert werden, während gleichnamige String-Schlüssel durch die Werte des später genannten Arrays überschrieben werden.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Um die Schlüssel oder die Werte eines Arrays zu erhalten, können die Funktionen array_keys($array) 11 und array_values($array) 11 verwendet werden. array_keys() gibt ein Array mit allen Schlüsseln des übergebenen Arrays zurück, während array_values() ein Array mit allen Werten in der Reihenfolge ihrer Indizes erzeugt.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Zum Suchen innerhalb von Arrays gibt es in_array($needle, $haystack), das prüft, ob ein bestimmter Wert ($needle) in einem Array ($haystack) vorhanden ist 9, und array_search($needle, $haystack), das nach einem Wert sucht und den entsprechenden Schlüssel zurückgibt, falls der Wert gefunden wird.27&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Weitere nützliche Array-Funktionen sind array_unique($array), das doppelte Werte aus einem Array entfernt 11, array_filter($array, $callback), das Elemente eines Arrays anhand einer benutzerdefinierten Callback-Funktion filtert 11, und array_map($callback, $array), das eine benutzerdefinierte Callback-Funktion auf jedes Element eines Arrays anwendet und ein neues Array mit den Ergebnissen zurückgibt.11&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Die folgende Tabelle fasst einige der wichtigsten PHP Array-Funktionen zusammen:&amp;lt;/p&amp;gt;&amp;lt;div&amp;gt;&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;p&amp;gt;Funktion&amp;lt;/p&amp;gt;&lt;br /&gt;
| &amp;lt;p&amp;gt;Beschreibung&amp;lt;/p&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;p&amp;gt;array_push()&amp;lt;/p&amp;gt;&lt;br /&gt;
| &amp;lt;p&amp;gt;Fügt ein oder mehrere Elemente am Ende eines Arrays hinzu.&amp;lt;/p&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;p&amp;gt;array_pop()&amp;lt;/p&amp;gt;&lt;br /&gt;
| &amp;lt;p&amp;gt;Entfernt und gibt das letzte Element eines Arrays zurück.&amp;lt;/p&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;p&amp;gt;array_shift()&amp;lt;/p&amp;gt;&lt;br /&gt;
| &amp;lt;p&amp;gt;Entfernt und gibt das erste Element eines Arrays zurück.&amp;lt;/p&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;p&amp;gt;array_unshift()&amp;lt;/p&amp;gt;&lt;br /&gt;
| &amp;lt;p&amp;gt;Fügt ein oder mehrere Elemente am Anfang eines Arrays hinzu.&amp;lt;/p&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;p&amp;gt;array_merge()&amp;lt;/p&amp;gt;&lt;br /&gt;
| &amp;lt;p&amp;gt;Vereinigt ein oder mehrere Arrays zu einem neuen Array.&amp;lt;/p&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;p&amp;gt;array_keys()&amp;lt;/p&amp;gt;&lt;br /&gt;
| &amp;lt;p&amp;gt;Gibt alle oder einen Teil der Schlüssel eines Arrays zurück.&amp;lt;/p&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;p&amp;gt;array_values()&amp;lt;/p&amp;gt;&lt;br /&gt;
| &amp;lt;p&amp;gt;Gibt alle Werte eines Arrays mit numerischer Indizierung zurück.&amp;lt;/p&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;p&amp;gt;in_array()&amp;lt;/p&amp;gt;&lt;br /&gt;
| &amp;lt;p&amp;gt;Prüft, ob ein Wert im Array vorhanden ist.&amp;lt;/p&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;p&amp;gt;array_search()&amp;lt;/p&amp;gt;&lt;br /&gt;
| &amp;lt;p&amp;gt;Sucht nach einem Wert im Array und gibt den entsprechenden Schlüssel zurück.&amp;lt;/p&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;p&amp;gt;array_unique()&amp;lt;/p&amp;gt;&lt;br /&gt;
| &amp;lt;p&amp;gt;Entfernt doppelte Werte aus einem Array.&amp;lt;/p&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;p&amp;gt;array_filter()&amp;lt;/p&amp;gt;&lt;br /&gt;
| &amp;lt;p&amp;gt;Filtert Elemente eines Arrays mithilfe einer Callback-Funktion.&amp;lt;/p&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;p&amp;gt;array_map()&amp;lt;/p&amp;gt;&lt;br /&gt;
| &amp;lt;p&amp;gt;Wendet eine Callback-Funktion auf jedes Element eines Arrays an.&amp;lt;/p&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;ref&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;=== Sessions &amp;amp; Cookies === ==== session.gc_maxlifetime ==== === Beschreibung: ===&amp;lt;/ref&amp;gt;&amp;lt;p&amp;gt;Gibt die Lebensdauer einer PHP-Session in Sekunden an. Nach Ablauf dieser Zeit wird die Session gelöscht.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Standardwert: === *1440 Sekunden (24 Minuten) === Beispiel: ===&amp;lt;p&amp;gt;session.gc_maxlifetime = 3600&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Empfehlung: === *Erhöhen Sie den Wert, wenn Benutzer länger eingeloggt bleiben sollen. *Passen Sie den Wert an die Anforderungen Ihrer Anwendung an. &amp;lt;br&amp;gt;=== Mailversand === === Passwort-Hashing ===&amp;lt;p&amp;gt;Sicheres Hashing von Passwörtern&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;https://www.php.net/manual/de/faq.passwords.php&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Dateioperationen ===&amp;lt;p&amp;gt;https://www.php.net/manual/de/ref.filesystem.php&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;=== URL-Aufrufe / curl ===&amp;lt;p&amp;gt;cURL in PHP ist eine Bibliothek, die es ermöglicht, HTTP-Anfragen von einem PHP-Skript aus an andere Server zu senden und deren Antworten zu verarbeiten. Es wird verwendet, um Daten aus externen APIs abzurufen, Dateien herunterzuladen, Formulare auf anderen Webseiten auszufüllen oder mit REST-APIs zu kommunizieren. Mit cURL kann man verschiedene Protokolle wie HTTP, HTTPS, FTP und mehr nutzen, was es zu einem flexiblen, mächtigen und wichtigen Werkzeug für den Datenaustausch zwischen Servern macht.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;https://www.php.net/manual/de/ref.curl.php&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;lt;p&amp;gt;&amp;amp;lt;?php&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;// 1. cURL-Session initialisieren&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;$curl = curl_init();&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;p&amp;gt;// 2. URL der API setzen&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;$url = &amp;quot;https://api.open-meteo.com/v1/forecast?latitude=47.81&amp;amp;longitude=16.24&amp;amp;daily=temperature_2m_max,temperature_2m_min&amp;amp;current_weather=true&amp;amp;forecast_days=1&amp;quot;;&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;p&amp;gt;// 3. Optionen für die cURL-Session setzen&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;curl_setopt($curl, CURLOPT_URL, $url);&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;p&amp;gt;// 4. Anfrage ausführen und Antwort speichern&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;$response = curl_exec($curl);&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;p&amp;gt;// 5. cURL-Session schließen&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;curl_close($curl);&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;p&amp;gt;// 6. JSON-Antwort in ein PHP-Array umwandeln&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;$data = json_decode($response, true);&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;p&amp;gt;// 7. Wetterdaten ausgeben&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;$currentTemp = $data[&amp;#039;current_weather&amp;#039;][&amp;#039;temperature&amp;#039;];&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;$maxTemp = $data[&amp;#039;daily&amp;#039;][&amp;#039;temperature_2m_max&amp;#039;][0];&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;$minTemp = $data[&amp;#039;daily&amp;#039;][&amp;#039;temperature_2m_min&amp;#039;][0];&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;p&amp;gt;echo &amp;quot;Aktuelle Temperatur: &amp;quot; . $currentTemp . &amp;quot;°C\n&amp;quot;;&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;echo &amp;quot;Tageshöchsttemperatur: &amp;quot; . $maxTemp . &amp;quot;°C\n&amp;quot;;&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;echo &amp;quot;Tagestiefsttemperatur: &amp;quot; . $minTemp . &amp;quot;°C\n&amp;quot;;&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;?&amp;amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;=== Erklärung der wichtigen Schritte: === #curl_init() – Initialisiert die cURL-Session. #curl_setopt() – Setzt Optionen: #*CURLOPT_URL – Die API-URL mit den Parametern. #*CURLOPT_RETURNTRANSFER – Gibt die Antwort als String zurück. #curl_exec() – Führt die Anfrage aus und speichert die Antwort. #json_decode() – Wandelt die JSON-Antwort in ein PHP-Array um. #Ausgabe der Daten – Greift auf die Temperaturdaten zu und gibt sie aus. &amp;lt;br&amp;gt;&amp;lt;p&amp;gt;Beispielausgabe&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Aktuelle Temperatur: 5.3°C&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Tageshöchsttemperatur: 10.1°C&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Tagestiefsttemperatur: 2.4°C&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;=== JSON === &amp;lt;br&amp;gt;= Wichtige PHP-Konfigurationswerte =&amp;lt;p&amp;gt;Die PHP-Konfiguration beeinflusst die Funktionsweise und Performance von PHP-Anwendungen. Die Konfigurationswerte werden in der Datei php.ini festgelegt und können je nach Serverumgebung angepasst werden. Hier sind die wichtigsten Konfigurationswerte, die häufig bei der Optimierung von PHP-Anwendungen eine Rolle spielen.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Die Anpassung der PHP-Konfigurationswerte ist ein wichtiger Schritt, um die Performance und Sicherheit von Webanwendungen zu gewährleisten. Je nach Anwendungsszenario können die Werte angepasst werden, um Speicherprobleme, Zeitüberschreitungen oder Upload-Beschränkungen zu verhindern.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;Die Konfiguration von PHP ist über das zentrale Konfigurationsfile php.ini möglich, beispielsweise in /etc/php/8.2/fpm Die PHP Konfiguration kann wie folgt angezeigt werden: php.ini Datei anzeigen Über die Kommandozeile (direkt auf dem Webserver): php -i Via PHP Skript: &amp;amp;lt;? phpinfo(); ?&amp;amp;gt; Zu beachten ist, dass einige PHP Konfigurationswerte, die in der php.ini eingetragen sind, beim Apache Webserver pro Verzeichnis mit einem .htaccess Konfigurationsfile überschreiben werden können. Bei Nginx existiert ein solches File nicht, hier muss immer die zentrale Konfiguration aktualisiert werden. === memory_limit ===&amp;lt;p&amp;gt;Dieser Wert legt fest, wie viel Speicher eine PHP-Skriptdatei maximal nutzen darf. Ein zu niedriger Wert kann dazu führen, dass komplexe Skripte nicht vollständig ausgeführt werden.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Standardwert: 128M (128 Megabyte)&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Beispiel: ===&amp;lt;p&amp;gt;memory_limit = 256M&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Empfehlung: === *Erhöhen Sie den Wert, wenn Ihre Anwendung viele Daten verarbeitet, z. B. bei Bildbearbeitung oder großen Datenbankabfragen. *Setzen Sie einen vernünftigen Grenzwert, um eine übermäßige Speichernutzung zu verhindern.&amp;lt;p class=&amp;quot;mwt-heading&amp;quot;&amp;gt;&amp;#039;&amp;#039;&amp;#039;Was sind empfohlene Werte für memory_limit?&amp;#039;&amp;#039;&amp;#039;&amp;lt;/p&amp;gt;&amp;lt;div&amp;gt;{| |- |&amp;lt;p&amp;gt;Anwendungstyp&amp;lt;/p&amp;gt;|&amp;lt;p&amp;gt;Empfehlung&amp;lt;/p&amp;gt;|- |&amp;lt;p&amp;gt;Kleine Website&amp;lt;/p&amp;gt;|&amp;lt;p&amp;gt;64M - 128M&amp;lt;/p&amp;gt;|- |&amp;lt;p&amp;gt;Durchschnittliche App&amp;lt;/p&amp;gt;|&amp;lt;p&amp;gt;256M - 512M&amp;lt;/p&amp;gt;|- |&amp;lt;p&amp;gt;E-Commerce (z.B. Magento, Shopware,...)&amp;lt;/p&amp;gt;|&amp;lt;p&amp;gt;512M - 1G+&amp;lt;/p&amp;gt;|- |&amp;lt;p&amp;gt;API / Backend mit vielen Daten&amp;lt;/p&amp;gt;|&amp;lt;p&amp;gt;512M - 2G+&amp;lt;/p&amp;gt;|}&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
=== max_execution_time ===&lt;br /&gt;
&amp;lt;p&amp;gt;Dieser Wert bestimmt, wie lange ein PHP-Skript maximal laufen darf, bevor es abgebrochen wird. Er wird in Sekunden angegeben.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Standardwert: 30 Sekunden&amp;lt;/p&amp;gt;&amp;lt;p class=&amp;quot;mwt-heading&amp;quot;&amp;gt;&amp;#039;&amp;#039;&amp;#039;Beispiel:&amp;#039;&amp;#039;&amp;#039;&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;max_execution_time = 60&amp;lt;/p&amp;gt;&amp;lt;p class=&amp;quot;mwt-heading&amp;quot;&amp;gt;&amp;#039;&amp;#039;&amp;#039;Empfehlung:&amp;#039;&amp;#039;&amp;#039;&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*Für einfache Webanwendungen reicht ein niedriger Wert. *Bei rechenintensiven Prozessen (z. B. Datenmigrationen) kann es sinnvoll sein, den Wert temporär zu erhöhen.&lt;br /&gt;
=== post_max_size ===&lt;br /&gt;
&amp;lt;p&amp;gt;Dieser Wert gibt die maximale Größe von Daten an, die per POST-Methode gesendet werden können. Er beeinflusst Formularübermittlungen und Datei-Uploads.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Standardwert: 8M (8 Megabyte)&amp;lt;/p&amp;gt;&amp;lt;p class=&amp;quot;mwt-heading&amp;quot;&amp;gt;&amp;#039;&amp;#039;&amp;#039;Beispiel:&amp;#039;&amp;#039;&amp;#039;&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;post_max_size = 20M&amp;lt;/p&amp;gt;&amp;lt;p class=&amp;quot;mwt-heading&amp;quot;&amp;gt;&amp;#039;&amp;#039;&amp;#039;Empfehlung:&amp;#039;&amp;#039;&amp;#039;&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*Erhöhen Sie diesen Wert, wenn Ihre Anwendung große Dateien hochladen muss. *Der Wert sollte immer größer als upload_max_filesize sein.&lt;br /&gt;
=== upload_max_filesize ===&lt;br /&gt;
&amp;lt;p&amp;gt;Dieser Wert legt die maximale Größe für hochgeladene Dateien fest.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Standardwert: 2M (2 Megabyte)&amp;lt;/p&amp;gt;&amp;lt;p class=&amp;quot;mwt-heading&amp;quot;&amp;gt;&amp;#039;&amp;#039;&amp;#039;Beispiel:&amp;#039;&amp;#039;&amp;#039;&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;upload_max_filesize = 10M&amp;lt;/p&amp;gt;&amp;lt;p class=&amp;quot;mwt-heading&amp;quot;&amp;gt;&amp;#039;&amp;#039;&amp;#039;Empfehlung:&amp;#039;&amp;#039;&amp;#039;&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*Passen Sie diesen Wert entsprechend den Anforderungen Ihrer Anwendung an. *In Kombination mit post_max_size verwenden.&amp;lt;p&amp;gt;max_input_vars&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Dieser Wert gibt an, wie viele Eingabevariablen (z. B. aus Formularen) ein PHP-Skript verarbeiten kann. Ein zu niedriger Wert kann dazu führen, dass große Formulare nicht korrekt verarbeitet werden.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Standardwert: 1000&amp;lt;/p&amp;gt;&amp;lt;p class=&amp;quot;mwt-heading&amp;quot;&amp;gt;&amp;#039;&amp;#039;&amp;#039;Beispiel:&amp;#039;&amp;#039;&amp;#039;&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;max_input_vars = 2000&amp;lt;/p&amp;gt;&amp;lt;p class=&amp;quot;mwt-heading&amp;quot;&amp;gt;&amp;#039;&amp;#039;&amp;#039;Empfehlung:&amp;#039;&amp;#039;&amp;#039;&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*Erhöhen Sie diesen Wert bei großen Formularen oder umfangreichen Konfigurationsdaten. *Ein zu hoher Wert kann die Performance negativ beeinflussen.&lt;br /&gt;
=== display_errors ===&lt;br /&gt;
&amp;lt;p&amp;gt;Legt fest, ob PHP-Fehlermeldungen direkt im Browser angezeigt werden.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Standardwert: Off (in Produktionsumgebungen)&amp;lt;/p&amp;gt;&amp;lt;p class=&amp;quot;mwt-heading&amp;quot;&amp;gt;&amp;#039;&amp;#039;&amp;#039;Beispiel:&amp;#039;&amp;#039;&amp;#039;&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;display_errors = On&amp;lt;/p&amp;gt;&amp;lt;p class=&amp;quot;mwt-heading&amp;quot;&amp;gt;&amp;#039;&amp;#039;&amp;#039;Empfehlung:&amp;#039;&amp;#039;&amp;#039;&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*In Entwicklungsumgebungen: Aktivieren Sie diese Option, um Fehler direkt zu sehen. *In Produktionsumgebungen: Deaktivieren Sie diese Option, um sensible Informationen nicht preiszugeben.&lt;br /&gt;
=== error_reporting ===&lt;br /&gt;
&amp;lt;p&amp;gt;Definiert, welche Arten von Fehlern gemeldet werden sollen.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Standardwert: E_ALL (zeigt alle Fehler an)&amp;lt;/p&amp;gt;&amp;lt;p class=&amp;quot;mwt-heading&amp;quot;&amp;gt;&amp;#039;&amp;#039;&amp;#039;Beispiel:&amp;#039;&amp;#039;&amp;#039;&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;error_reporting = E_ALL &amp;amp; ~E_NOTICE&amp;lt;/p&amp;gt;&amp;lt;p class=&amp;quot;mwt-heading&amp;quot;&amp;gt;&amp;#039;&amp;#039;&amp;#039;Empfehlung:&amp;#039;&amp;#039;&amp;#039;&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*In Entwicklungsumgebungen: Nutzen Sie E_ALL, um alle Fehler zu sehen. *In Produktionsumgebungen: Blenden Sie unnötige Meldungen aus, z. B. mit ~E_NOTICE.&lt;br /&gt;
=== file_uploads ===&lt;br /&gt;
&amp;lt;p&amp;gt;Legt fest, ob das Hochladen von Dateien in PHP erlaubt ist.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;Standardwert: On&amp;lt;/p&amp;gt;&amp;lt;p class=&amp;quot;mwt-heading&amp;quot;&amp;gt;&amp;#039;&amp;#039;&amp;#039;Beispiel:&amp;#039;&amp;#039;&amp;#039;&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;file_uploads = On&amp;lt;/p&amp;gt;&amp;lt;p class=&amp;quot;mwt-heading&amp;quot;&amp;gt;&amp;#039;&amp;#039;&amp;#039;Empfehlung:&amp;#039;&amp;#039;&amp;#039;&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*Aktivieren Sie diese Option, wenn Ihre Anwendung Datei-Uploads unterstützt. *Deaktivieren Sie sie, wenn keine Datei-Uploads benötigt werden, um die Sicherheit zu erhöhen. [[file:SED501-Developer-Toolbar.png|600px|center|thumb|Web Developer Toolbar]]&lt;/div&gt;</summary>
		<author><name>Völkl Anna</name></author>
	</entry>
	<entry>
		<id>https://mediawiki.fernfh.ac.at/mediawiki/index.php?title=Datei:Sed602-git-staging-area.png&amp;diff=6567</id>
		<title>Datei:Sed602-git-staging-area.png</title>
		<link rel="alternate" type="text/html" href="https://mediawiki.fernfh.ac.at/mediawiki/index.php?title=Datei:Sed602-git-staging-area.png&amp;diff=6567"/>
		<updated>2025-03-29T13:43:42Z</updated>

		<summary type="html">&lt;p&gt;Völkl Anna: Git Staging Area: Mit git add werden die Änderungen des lokalen Verzeichnisses in die Staging Area übernommen. Von dort werden alle gewünschten permanenten Änderungen mit git commit in das (lokale) Repository übernommen und sind dauerhaft gespeichert&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Beschreibung ==&lt;br /&gt;
Git Staging Area: Mit git add werden die Änderungen des lokalen Verzeichnisses in die Staging Area übernommen. Von dort werden alle gewünschten permanenten Änderungen mit git commit in das (lokale) Repository übernommen und sind dauerhaft gespeichert.&lt;br /&gt;
&lt;br /&gt;
Quelle: https://git-scm.com/about/staging-area&lt;br /&gt;
https://git-scm.com/images/about/index1@2x.png&lt;/div&gt;</summary>
		<author><name>Völkl Anna</name></author>
	</entry>
	<entry>
		<id>https://mediawiki.fernfh.ac.at/mediawiki/index.php?title=Version_Control_und_Zusammenarbeit&amp;diff=6566</id>
		<title>Version Control und Zusammenarbeit</title>
		<link rel="alternate" type="text/html" href="https://mediawiki.fernfh.ac.at/mediawiki/index.php?title=Version_Control_und_Zusammenarbeit&amp;diff=6566"/>
		<updated>2025-03-29T14:17:42Z</updated>

		<summary type="html">&lt;p&gt;Völkl Anna: Die Seite wurde neu angelegt: „= Versionskontrolle mit Git =  == Einführung in die Versionskontrolle ==  Versionskontrolle beschreibt technische Systeme und Verfahren, die entwickelt wurden, um Änderungen an Dateien oder ganzen Projekten systematisch zu verwalten, zu dokumentieren und transparent nachvollziehbar zu machen. Gerade in der Softwareentwicklung ist die Versionskontrolle von entscheidender Bedeutung, da sie Entwickler dabei unterstützt, sämtliche Modifikationen an einem…“&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Versionskontrolle mit Git =&lt;br /&gt;
&lt;br /&gt;
== Einführung in die Versionskontrolle ==&lt;br /&gt;
&lt;br /&gt;
Versionskontrolle beschreibt technische Systeme und Verfahren, die entwickelt wurden, um Änderungen an Dateien oder ganzen Projekten systematisch zu verwalten, zu dokumentieren und transparent nachvollziehbar zu machen. Gerade in der Softwareentwicklung ist die Versionskontrolle von entscheidender Bedeutung, da sie Entwickler dabei unterstützt, sämtliche Modifikationen an einem Projekt präzise festzuhalten und jederzeit auf frühere Zustände des Projektes zugreifen zu können. Dabei speichert ein Versionskontrollsystem nicht nur den aktuellen Zustand einer Datei oder eines Projektes, sondern archiviert auch die komplette Historie aller zuvor vorgenommenen Änderungen. Entwickler können somit exakt nachvollziehen, wer, wann und warum bestimmte Modifikationen vorgenommen hat, was die Wartbarkeit und Qualität des Codes erheblich verbessert.&lt;br /&gt;
&lt;br /&gt;
=== Warum Versionskontrolle wichtig ist ===&lt;br /&gt;
&lt;br /&gt;
Die Bedeutung der Versionskontrolle zeigt sich sowohl auf individueller Ebene als auch im Kontext von Entwicklungsteams sehr deutlich. Für einzelne Entwickler ist es enorm hilfreich, die Entwicklungshistorie nachvollziehen zu können, um bei auftretenden Fehlern schnell zu früheren Versionen zurückkehren und entsprechende Korrekturen zielgerichtet vornehmen zu können. Dadurch lassen sich Fehler schneller beheben, und die Produktivität steigt erheblich.&lt;br /&gt;
&lt;br /&gt;
Im Kontext der Teamarbeit ist die Versionskontrolle sogar unverzichtbar. Sie stellt sicher, dass mehrere Entwickler parallel an verschiedenen Teilen eines Projekts arbeiten können, ohne dass Änderungen verloren gehen oder Konflikte unkontrolliert entstehen. Versionskontrollsysteme ermöglichen es Teammitgliedern, effizient zusammenzuarbeiten, indem sie Änderungen übersichtlich bündeln, Konflikte automatisch erkennen und klare Mechanismen zur Konfliktlösung bereitstellen. Die Versionskontrolle sorgt außerdem dafür, dass alle Teammitglieder jederzeit auf den aktuellsten Stand des Projekts zugreifen können, wodurch Doppelarbeiten und Missverständnisse vermieden werden. Darüber hinaus bietet sie eine gemeinsame Code-Basis, welche die Kontinuität des Entwicklungsprozesses sicherstellt und somit langfristig eine höhere Codequalität und Stabilität gewährleistet. Zusammengefasst verbessert Versionskontrolle somit die Transparenz, Zusammenarbeit und Effizienz in Softwareentwicklungsprojekten deutlich.&lt;br /&gt;
&lt;br /&gt;
=== Historische Perspektive ===&lt;br /&gt;
&lt;br /&gt;
In den frühen Anfängen der Softwareentwicklung war Versionskontrolle vor allem ein manueller Prozess, bei dem Entwickler regelmäßig Sicherungskopien ihrer Dateien auf lokalen Datenträgern anlegten. Dieser Ansatz war jedoch fehleranfällig und führte schnell zu Unübersichtlichkeit, insbesondere wenn Teams gemeinsam an einem Projekt arbeiteten.&lt;br /&gt;
&lt;br /&gt;
Mit der zunehmenden Komplexität von Softwareprojekten entstand der Bedarf nach strukturierteren Systemen. Dies führte zur Entwicklung zentralisierter Versionskontrollsysteme wie CVS (Concurrent Versions System) und später SVN (Subversion). Diese Systeme erlaubten es mehreren Entwicklern, Änderungen zentral auf einem Server abzulegen, wodurch erstmals ein strukturierter und nachvollziehbarer Änderungsverlauf möglich wurde. Allerdings waren diese Systeme stark abhängig von einem einzigen zentralen Server, was schnell zu Engpässen und einem Single Point of Failure führte. Zudem erforderte jede Operation, wie beispielsweise das Betrachten der Historie oder das Erstellen von Branches, Kommunikation mit dem zentralen Server, was Arbeitsprozesse verlangsamen konnte.&lt;br /&gt;
&lt;br /&gt;
Die Einführung verteilter Versionskontrollsysteme wie Git revolutionierte diesen Bereich grundlegend. Git wurde im Jahr 2005 von Linus Torvalds entwickelt, um die kollaborative Entwicklung des Linux-Kernels effizienter zu gestalten. Git ermöglicht jedem Entwickler, eine vollständige Kopie des gesamten Repositorys lokal auf dem eigenen Rechner zu speichern und eigenständig zu verwalten. Jeder Entwickler kann somit unabhängig vom zentralen Server Änderungen verfolgen, Branches erstellen und lokale Commits durchführen. Diese dezentrale Arbeitsweise reduziert Abhängigkeiten, erhöht die Sicherheit gegen Datenverlust und verbessert die allgemeine Performance des Versionskontrollprozesses erheblich.&lt;br /&gt;
&lt;br /&gt;
=== Vorteile von Git ===&lt;br /&gt;
&lt;br /&gt;
Git bietet eine Reihe entscheidender Vorteile gegenüber traditionellen zentralisierten Systemen:&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Dezentrale Arbeitsweise&amp;#039;&amp;#039;&amp;#039;: Einer der größten Vorteile von Git ist die dezentrale Struktur. Jeder Entwickler besitzt eine vollständige lokale Kopie des Repositorys einschließlich der gesamten Historie, wodurch die Abhängigkeit von einem zentralen Server drastisch reduziert wird. Selbst wenn der zentrale Server ausfallen sollte, kann weiter lokal gearbeitet werden, und Datenverluste werden minimiert.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Bessere Rückverfolgbarkeit&amp;#039;&amp;#039;&amp;#039;: Git speichert detaillierte Informationen zu jeder Änderung einschließlich Autor, Datum und Beschreibung. Dies erleichtert es enorm, die Herkunft von Fehlern nachzuvollziehen, gezielt zu beheben und den Überblick über den Entwicklungsverlauf zu behalten.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Vereinfachte und beschleunigte Zusammenarbeit&amp;#039;&amp;#039;&amp;#039;: Durch dezentrale Repositories und robuste Mechanismen zur Konfliktauflösung ist es Teams möglich, effizient und gleichzeitig an unterschiedlichen Features oder Projektteilen zu arbeiten. Branches erlauben es Entwicklern, isolierte Entwicklungsumgebungen zu schaffen und später Änderungen kontrolliert zusammenzuführen.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Flexibilität und Unterstützung agiler Methoden&amp;#039;&amp;#039;&amp;#039;: Git unterstützt explizit agile Entwicklungsmethoden wie Scrum oder Kanban. Die schnelle Erstellung von Branches und deren einfache Verwaltung ermöglichen es Teams, flexibel auf neue Anforderungen oder Änderungen im Projektverlauf zu reagieren und in kurzen Iterationen zu arbeiten. Diese Flexibilität ist essenziell für moderne Softwareentwicklungsprozesse und verbessert langfristig Qualität und Produktivität.&lt;br /&gt;
&lt;br /&gt;
== Grundlagen von Git ==&lt;br /&gt;
&lt;br /&gt;
=== Was ist Git? ===&lt;br /&gt;
&lt;br /&gt;
Git ist ein verteiltes Versionskontrollsystem, das 2005 von Linus Torvalds entwickelt wurde. Anders als zentralisierte Systeme wie SVN erlaubt Git jedem Entwickler, eine vollständige Kopie des Projektes lokal zu halten.&lt;br /&gt;
&lt;br /&gt;
=== Git im Vergleich zu SVN ===&lt;br /&gt;
&lt;br /&gt;
Git unterscheidet sich von SVN primär durch seine dezentrale Architektur. Während SVN jede Operation zentral speichert, kann Git lokal Commit-Vorgänge ausführen. Dies steigert die Performance erheblich und ermöglicht parallele, isolierte Entwicklungsstränge (Branches).&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Vor- und Nachteile:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
* Git: Hohe Geschwindigkeit, lokale Historie, komplexere Lernkurve.&lt;br /&gt;
* SVN: Einfacheres Konzept, zentrale Verwaltung, langsamere Operationen.&lt;br /&gt;
&lt;br /&gt;
=== Grundlegende Konzepte ===&lt;br /&gt;
&lt;br /&gt;
Ein &amp;#039;&amp;#039;&amp;#039;Repository&amp;#039;&amp;#039;&amp;#039; ist der zentrale Speicherort für alle Projektdaten, der sowohl lokal auf dem Rechner des Entwicklers als auch auf einem entfernten Server (remote) liegen kann. Es umfasst den kompletten Satz an Dateien, deren Änderungsverläufe und Metadaten.&lt;br /&gt;
&lt;br /&gt;
Die &amp;#039;&amp;#039;&amp;#039;Staging-Area&amp;#039;&amp;#039;&amp;#039; ist ein Zwischenbereich, der Änderungen sammelt, bevor diese fest in das Repository aufgenommen werden. Durch die Staging-Area können Entwickler*innen entscheiden, welche der vorgenommenen Änderungen in den nächsten Commit aufgenommen werden sollen und welche nicht. Somit ermöglicht sie eine detaillierte Kontrolle und bessere Organisation der zu speichernden Modifikationen.&lt;br /&gt;
&lt;br /&gt;
[[file:Sed602-git-staging-area.png|300px|thumb]]&lt;br /&gt;
&lt;br /&gt;
Ein &amp;#039;&amp;#039;&amp;#039;Commit&amp;#039;&amp;#039;&amp;#039; stellt eine Momentaufnahme (Snapshot) des gesamten Projekts zu einem bestimmten Zeitpunkt dar. Jeder Commit enthält alle Informationen zu Änderungen, die seit dem letzten Commit durchgeführt wurden. Commits dienen als sichere Referenzpunkte, zu denen jederzeit zurückgekehrt werden kann.&lt;br /&gt;
&lt;br /&gt;
Ein &amp;#039;&amp;#039;&amp;#039;Branch&amp;#039;&amp;#039;&amp;#039; bezeichnet eine Abzweigung vom Hauptentwicklungszweig (meistens &amp;quot;main&amp;quot; oder &amp;quot;master&amp;quot; genannt), um parallel und unabhängig an neuen Funktionen oder Bugfixes zu arbeiten. Dies erlaubt es Entwicklern, Änderungen zunächst isoliert zu entwickeln und ausgiebig zu testen, bevor sie zurück in den Hauptzweig integriert werden.&lt;br /&gt;
&lt;br /&gt;
Ein &amp;#039;&amp;#039;&amp;#039;Merge&amp;#039;&amp;#039;&amp;#039; ist der Prozess des Zusammenführens zweier oder mehrerer Entwicklungszweige (Branches). Dies geschieht typischerweise, nachdem eine bestimmte Entwicklung abgeschlossen ist und in die Hauptlinie integriert werden soll. Beim Merge werden alle Änderungen miteinander kombiniert, wobei Git Konflikte automatisch erkennt und die Entwickler gegebenenfalls zur manuellen Konfliktlösung auffordert.&lt;br /&gt;
&lt;br /&gt;
== Arbeiten mit Git – Die Grundlagen ==&lt;br /&gt;
&lt;br /&gt;
=== Installation und Konfiguration ===&lt;br /&gt;
&lt;br /&gt;
Git ist plattformunabhängig und kann auf Windows, MacOS und Linux installiert werden. Grundlegende Konfiguration erfolgt über:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;bash&amp;quot;&amp;gt;&lt;br /&gt;
git config --global user.name &amp;quot;Name&amp;quot;&lt;br /&gt;
git config --global user.email &amp;quot;Email&amp;quot;&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Grundlegende Befehle ===&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;git init&amp;#039;&amp;#039;&amp;#039;: Erstellt ein neues lokales Repository.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;git clone&amp;#039;&amp;#039;&amp;#039;: Klont ein remote Repository lokal.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;git add&amp;#039;&amp;#039;&amp;#039;: Fügt Dateien zur Staging-Area hinzu.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;git commit&amp;#039;&amp;#039;&amp;#039;: Speichert Änderungen.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;git push&amp;#039;&amp;#039;&amp;#039;: Lädt lokale Commits in ein remote Repository.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;git pull&amp;#039;&amp;#039;&amp;#039;: Lädt Änderungen vom remote Repository herunter.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;git branch&amp;#039;&amp;#039;&amp;#039;: Erstellt und verwaltet Branches.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;git merge&amp;#039;&amp;#039;&amp;#039;: Führt Branches zusammen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
git init&lt;br /&gt;
&lt;br /&gt;
git clone&lt;br /&gt;
&lt;br /&gt;
git add&lt;br /&gt;
===== git commit =====&lt;br /&gt;
&amp;lt;p data-pm-slice=&amp;quot;1 1 []&amp;quot;&amp;gt;Ein Git &amp;#039;&amp;#039;Commit&amp;#039;&amp;#039; besteht aus mehreren wichtigen Komponenten:&amp;lt;/p&amp;gt;&lt;br /&gt;
*&amp;lt;p&amp;gt;Dem eigentlichen Inhalt der Änderungen (den Dateien und deren Änderungen).&amp;lt;/p&amp;gt;&lt;br /&gt;
*&amp;lt;p&amp;gt;Metadaten, wie Autor und Zeitstempel.&amp;lt;/p&amp;gt;&lt;br /&gt;
*&amp;lt;p&amp;gt;Einer aussagekräftigen Commit-Nachricht: Hier ist eine Unterscheidung in Betreff und Body möglich&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;Jeder Commit erhält eine eindeutige Identifikation in Form eines &amp;#039;&amp;#039;Hash-Wertes&amp;#039;&amp;#039;. Ein solcher Hash ist ein SHA-1-Wert, der aus den Inhalten und Metadaten des Commits erzeugt wird. Der Hash stellt sicher, dass jede Version des Projektes eindeutig und unveränderlich dokumentiert ist. Da der Hash durch die Inhalte eindeutig definiert ist, kann man sicherstellen, dass Änderungen nicht unbemerkt verändert werden können.&amp;lt;/p&amp;gt;&amp;lt;p data-pm-slice=&amp;quot;1 1 []&amp;quot;&amp;gt;&amp;#039;&amp;#039;&amp;#039;Beispiel&amp;#039;&amp;#039;&amp;#039;&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Eingabe:&lt;br /&gt;
&lt;br /&gt;
$git commit -m &amp;quot;Update frontend-theme configuration&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Ausgabe:&amp;lt;br&amp;gt;[main 6ce05a1] Update frontend-theme configuration&amp;lt;br&amp;gt;&amp;amp;nbsp;1 file changed, 0 insertions(+), 0 deletions(-)&amp;lt;br&amp;gt;&amp;amp;nbsp;create mode 100644 config.xml&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Aus dieser Rückgabe von Git lassen sich folgende Dinge ablesen:&lt;br /&gt;
*&amp;lt;p&amp;gt;Das Commit wurde im main Branch erstellt&amp;lt;/p&amp;gt;&lt;br /&gt;
*&amp;lt;p&amp;gt;Commit Hash ID: 6ce05a1&amp;lt;/p&amp;gt;&lt;br /&gt;
*Commit-Nachricht &amp;quot;Update frontend-theme configuration&amp;quot;&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Best Practise&amp;#039;&amp;#039;&amp;#039;&amp;lt;p data-start=&amp;quot;824&amp;quot; data-end=&amp;quot;914&amp;quot;&amp;gt;In der Software-Entwicklung gibt es allgemein akzeptierte Best Practices für Git-Commit-Nachrichten:&amp;lt;/p&amp;gt;&lt;br /&gt;
#&amp;lt;p data-start=&amp;quot;919&amp;quot; data-end=&amp;quot;964&amp;quot;&amp;gt;&amp;lt;strong data-start=&amp;quot;919&amp;quot; data-end=&amp;quot;964&amp;quot;&amp;gt;Maximal 50 Zeichen Betreff&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
#&amp;lt;p data-start=&amp;quot;968&amp;quot; data-end=&amp;quot;1029&amp;quot;&amp;gt;&amp;lt;strong data-start=&amp;quot;968&amp;quot; data-end=&amp;quot;1029&amp;quot;&amp;gt;Nur der erste Buchstabe wird groß geschrieben&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
#&amp;lt;p data-start=&amp;quot;1033&amp;quot; data-end=&amp;quot;1082&amp;quot;&amp;gt;&amp;lt;strong data-start=&amp;quot;1033&amp;quot; data-end=&amp;quot;1082&amp;quot;&amp;gt;Kein Punkt am Ende&amp;lt;/strong&amp;gt;&amp;lt;br&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
#&amp;lt;p data-start=&amp;quot;1160&amp;quot; data-end=&amp;quot;1223&amp;quot;&amp;gt;&amp;lt;strong data-start=&amp;quot;1160&amp;quot; data-end=&amp;quot;1223&amp;quot;&amp;gt;Halte den Nachrichtentext (Body) bei maximal 72 Zeichen pro Zeile&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
#&amp;lt;p data-start=&amp;quot;1227&amp;quot; data-end=&amp;quot;1275&amp;quot;&amp;gt;&amp;lt;strong data-start=&amp;quot;1227&amp;quot; data-end=&amp;quot;1275&amp;quot;&amp;gt;Verwende die imperative (auffordernde) Form&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
#&amp;lt;p data-start=&amp;quot;1279&amp;quot; data-end=&amp;quot;1339&amp;quot;&amp;gt;&amp;lt;strong data-start=&amp;quot;1279&amp;quot; data-end=&amp;quot;1339&amp;quot;&amp;gt;Beschreibe, was getan wurde und warum – aber nicht, wie&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
In vielen Firmen gibt es außerdem noch den Zusatz, dass zu Beginn (oder am Ende) des Commits die Ticket-Nummer aus dem Ticketing-System steht.&lt;br /&gt;
&lt;br /&gt;
Beispiel&lt;br /&gt;
&lt;br /&gt;
$git commit -m &amp;quot;[PRO-123] Update frontend-theme configuration&amp;quot; -m &amp;quot;Use hyvä theme for b2c store due to accessibility improvements&amp;quot;&amp;lt;p data-start=&amp;quot;1388&amp;quot; data-end=&amp;quot;1486&amp;quot;&amp;gt;Es gibt zwei wesentliche Gründe, warum die Git-Betreffzeile auf 50 Zeichen begrenzt werden sollte:&amp;lt;/p&amp;gt;&amp;lt;p data-start=&amp;quot;1488&amp;quot; data-end=&amp;quot;1633&amp;quot;&amp;gt;Erstens können andere Entwickler so leichter und schneller den Zweck eines Commits erkennen, wenn sie sich durch zahlreiche Commits durchkämpfen.&amp;lt;/p&amp;gt;&amp;lt;p data-start=&amp;quot;1635&amp;quot; data-end=&amp;quot;1790&amp;quot;&amp;gt;Zweitens zwingt diese Praxis Entwickler dazu, sich intensiv Gedanken darüber zu machen, was genau sie gerade getan haben, und dies prägnant zu formulieren.&amp;lt;/p&amp;gt;&amp;lt;p data-start=&amp;quot;1792&amp;quot; data-end=&amp;quot;2101&amp;quot;&amp;gt;Falls es einem Entwickler schwerfällt, den Zweck eines Commits in 50 oder weniger Zeichen zu beschreiben, deutet das oft darauf hin, dass er nicht häufig genug committet, zu viele Änderungen auf einmal vornimmt oder nicht klar versteht, welches Problem er lösen oder welche Funktionalität er erstellen möchte.&amp;lt;/p&amp;gt;&amp;lt;p data-start=&amp;quot;2103&amp;quot; data-end=&amp;quot;2246&amp;quot;&amp;gt;Wer schrittweise entwickelt und regelmäßig committet, sollte keine Probleme haben, die Commit-Nachricht in höchstens 50 Zeichen zu beschreiben.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
git push&lt;br /&gt;
&lt;br /&gt;
git pull&lt;br /&gt;
&lt;br /&gt;
git branch&lt;br /&gt;
&lt;br /&gt;
git merge&lt;br /&gt;
=== Verwaltung von Konflikten ===&lt;br /&gt;
&lt;br /&gt;
Bei gleichzeitigen Änderungen treten Konflikte auf, die Git markiert und deren manuelle Auflösung nötig ist. Dies erfolgt meist direkt im Quellcode.&lt;br /&gt;
&lt;br /&gt;
== Erweiterte Git-Konzepte ==&lt;br /&gt;
&lt;br /&gt;
=== Branches und Merging Strategien ===&lt;br /&gt;
&lt;br /&gt;
Beliebte Strategien sind der Gitflow-Workflow (mit festen Branch-Strukturen für Entwicklung, Feature, Release, Hotfix), GitHub Flow (einfachere Struktur, direkte Integration) und GitLab Flow (angepasst auf Continuous Delivery).&lt;br /&gt;
&lt;br /&gt;
=== Git-Hooks und Automatisierung ===&lt;br /&gt;
&lt;br /&gt;
Hooks ermöglichen automatisierte Prozesse wie Tests vor einem Commit oder automatische Deployments. Hooks verbessern Qualität und Konsistenz im Entwicklungsprozess.&lt;br /&gt;
&lt;br /&gt;
=== .gitignore, Tags und Releases ===&lt;br /&gt;
&lt;br /&gt;
Die &amp;lt;code&amp;gt;.gitignore&amp;lt;/code&amp;gt;-Datei erlaubt das gezielte Ausschließen von Dateien aus der Versionskontrolle. Tags markieren spezifische Versionen und erleichtern das Release-Management.&lt;br /&gt;
&lt;br /&gt;
== Git Plattformen und Kollaboration ==&lt;br /&gt;
&lt;br /&gt;
=== Überblick der Plattformen ===&lt;br /&gt;
&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;GitHub&amp;#039;&amp;#039;&amp;#039;: Einfache Nutzung, große Community, starke Integration mit Drittanbieter-Tools.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;GitLab&amp;#039;&amp;#039;&amp;#039;: Fokus auf DevOps und CI/CD, integrierte Pipelines.&lt;br /&gt;
* &amp;#039;&amp;#039;&amp;#039;Bitbucket&amp;#039;&amp;#039;&amp;#039;: Beliebt bei Unternehmen, eng integriert mit Atlassian-Tools (Jira, Confluence).&lt;br /&gt;
&lt;br /&gt;
=== Code-Reviews und Pull Requests ===&lt;br /&gt;
&lt;br /&gt;
Pull Requests ermöglichen das gezielte Prüfen von Änderungen und erleichtern den Austausch im Team. Code-Reviews verbessern Qualität und reduzieren Fehler.&lt;br /&gt;
&lt;br /&gt;
=== Integration von Tools ===&lt;br /&gt;
&lt;br /&gt;
Integration von Git mit Tools wie Jira oder Jenkins verbessert Projektmanagement und automatisiert Builds, Tests und Deployments.&lt;br /&gt;
&lt;br /&gt;
== Git in der Wirtschaftsinformatik ==&lt;br /&gt;
&lt;br /&gt;
=== Anwendungsfälle in Unternehmen ===&lt;br /&gt;
&lt;br /&gt;
Git unterstützt agile Entwicklungsmethoden und ermöglicht effizientere Zusammenarbeit in Teams. Besonders für DevOps und CI/CD ist Git essenziell.&lt;br /&gt;
&lt;br /&gt;
=== Git für Infrastruktur und Dokumentation ===&lt;br /&gt;
&lt;br /&gt;
Git wird auch für Infrastruktur-Code (z.B. Terraform, Kubernetes) und Dokumentationsprojekte genutzt, was Transparenz und Nachvollziehbarkeit erhöht.&lt;br /&gt;
&lt;br /&gt;
=== Open Source und Lizenzen ===&lt;br /&gt;
&lt;br /&gt;
Open-Source-Komponenten sind essenziell in Unternehmensprojekten. Lizenzverwaltung mit Tools zur automatisierten Prüfung (z.B. FOSSA) schützt vor rechtlichen Risiken.&lt;br /&gt;
&lt;br /&gt;
=== Sicherheit in Git ===&lt;br /&gt;
&lt;br /&gt;
Die sichere Verwaltung sensibler Daten in Repositories ist kritisch. Git-Server sollten durch Zugriffskontrollen und Verschlüsselung abgesichert werden.&lt;br /&gt;
&lt;br /&gt;
=== Git in Cloud-Umgebungen ===&lt;br /&gt;
&lt;br /&gt;
Cloud-Plattformen wie AWS, Azure und Google Cloud bieten integrierte Lösungen wie AWS CodeCommit oder Azure Repos zur Versionskontrolle und Integration in Cloud-basierte CI/CD-Pipelines. Dadurch entstehen neue Möglichkeiten zur Zusammenarbeit und Automatisierung.&lt;/div&gt;</summary>
		<author><name>Völkl Anna</name></author>
	</entry>
	<entry>
		<id>https://mediawiki.fernfh.ac.at/mediawiki/index.php?title=Einf%C3%BChrung_in_PHP_und_Webentwicklung&amp;diff=6565</id>
		<title>Einführung in PHP und Webentwicklung</title>
		<link rel="alternate" type="text/html" href="https://mediawiki.fernfh.ac.at/mediawiki/index.php?title=Einf%C3%BChrung_in_PHP_und_Webentwicklung&amp;diff=6565"/>
		<updated>2025-03-30T15:10:05Z</updated>

		<summary type="html">&lt;p&gt;Völkl Anna: PHP&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=== Webentwicklung: Grundlagen und Architektur ===&lt;br /&gt;
==== Das Client-Server-Modell ====&lt;br /&gt;
Das Client-Server-Modell ist eine grundlegende Architektur der modernen Webentwicklung. Es beschreibt die Interaktion zwischen zwei Parteien: einem Client, der eine Anfrage stellt, und einem Server, der auf diese Anfrage antwortet.&lt;br /&gt;
Ein Server stellt Dienste, Ressourcen oder Informationen bereit. Im Kontext der Webentwicklung ist ein Webserver ein Programm, das Webseiten, APIs oder andere Inhalte an Clients ausliefert. Beispiele für Webserver-Software sind Apache, Nginx und IIS (Internet Information Services).&lt;br /&gt;
Ein Client ist ein Gerät oder eine Anwendung, die diese Ressourcen vom Server anfordert. Dies kann ein Webbrowser wie Chrome, Firefox oder Microsoft Edge sein, aber auch eine mobile App oder ein anderes Programm, das über das Internet kommuniziert.&lt;br /&gt;
Ein Beispiel aus dem Alltag: Wenn Sie eine Webseite in Ihrem Browser aufrufen, fungiert der Browser als Client. Der Webserver, der die Inhalte dieser Webseite hostet, ist der Server. Der Browser schickt eine Anfrage (Request) an den Server, und der Server antwortet mit der angeforderten Seite (Response).&lt;br /&gt;
==== Der Request-Response-Zyklus ====&lt;br /&gt;
Der Request-Response-Zyklus beschreibt die Kommunikation zwischen Client und Server in einem Websystem. Dieser Zyklus läuft in folgenden Schritten ab:&lt;br /&gt;
Client sendet eine Anfrage (Request): Der Benutzer gibt eine URL in den Browser ein oder klickt auf einen Link. Der Browser (Client) erstellt eine HTTP-Anfrage an den entsprechenden Server.&lt;br /&gt;
Server verarbeitet die Anfrage: Der Server empfängt die Anfrage, verarbeitet sie und sucht nach den angeforderten Ressourcen (z. B. HTML-Dateien, Bilder, Datenbankinhalte).&lt;br /&gt;
Server sendet eine Antwort (Response): Nachdem die Anfrage verarbeitet wurde, sendet der Server eine HTTP-Antwort zurück an den Client. Diese Antwort enthält den Statuscode (z. B. 200 OK oder 404 Not Found) und die angeforderten Inhalte.&lt;br /&gt;
Client zeigt die Antwort an: Der Browser des Clients interpretiert die empfangenen Inhalte und zeigt die Webseite an.&lt;br /&gt;
Beispiel:&lt;br /&gt;
Der Client (Browser) fordert die Webseite https://www.fernfh.ac.at an.&lt;br /&gt;
Der Server empfängt die Anfrage, der Webserver verarbeitet die Anfrage, sucht die passende Datei und schickt diese zurück.&lt;br /&gt;
Der Browser zeigt die Webseite an.&lt;br /&gt;
&lt;br /&gt;
==== Bestandteile einer Webanwendung ====&lt;br /&gt;
===== Webserver =====&lt;br /&gt;
Ein Webserver ist eine Software, die HTTP-Anfragen verarbeitet und Webseiten oder andere Ressourcen bereitstellt. Die beiden am häufigsten verwendeten Webserver sind Apache und Nginx:&lt;br /&gt;
Apache: Ein weit verbreiteter Open-Source-Webserver, der für seine Flexibilität und umfangreiche Konfigurationsmöglichkeiten bekannt ist. Er unterstützt Module zur Erweiterung von Funktionen wie URL-Rewrites oder Authentifizierung.&lt;br /&gt;
Nginx: Ein moderner, leistungsstarker Webserver, der für seine hohe Geschwindigkeit und geringe Ressourcenbelastung geschätzt wird. Nginx eignet sich besonders gut für den Einsatz als Reverse Proxy und zur Lastverteilung.&lt;br /&gt;
Beide Serverprogramme können sowohl statische Inhalte (HTML, CSS, Bilder) als auch dynamische Inhalte (PHP-Seiten, APIs) bereitstellen.&lt;br /&gt;
===== Datenbanken =====&lt;br /&gt;
Webserver arbeiten oft mit Datenbanken zusammen, um dynamische Inhalte bereitzustellen. Während der Webserver die Anfrage verarbeitet, ruft er oft zusätzliche Daten aus einer Datenbank ab. Diese Datenbanken speichern Informationen wie Benutzerdaten, Produktkataloge oder Artikel und ermöglichen es, Webseiteninhalte dynamisch zu generieren.&lt;br /&gt;
Zu den gängigen Datenbankmanagementsystemen zählen:&lt;br /&gt;
MySQL/MariaDB: Häufig in Kombination mit PHP verwendet, besonders im E-Commerce.&lt;br /&gt;
PostgreSQL: Ein leistungsstarkes Open-Source-System mit erweiterten Funktionen.&lt;br /&gt;
SQLite: Eine leichtgewichtige Datenbank für kleinere Projekte.&lt;br /&gt;
Ein typisches Szenario in einer Webanwendung ist:&lt;br /&gt;
Der Client fordert eine Produktseite an.&lt;br /&gt;
Der Webserver verarbeitet die Anfrage.&lt;br /&gt;
Der Webserver ruft die Produktinformationen aus der Datenbank ab.&lt;br /&gt;
Der Server generiert die HTML-Seite mit den Daten und sendet sie an den Client zurück.&lt;br /&gt;
Das Zusammenspiel zwischen Webservern und Datenbanken ermöglicht es, große, dynamische E-Commerce-Plattformen wie Magento effizient zu betreiben.&lt;br /&gt;
1.1.2.3 Web-Anwendung&lt;br /&gt;
Der Kern der Anwendung, oft mit Frameworks wie Laravel, Symfony oder Magento entwickelt.&lt;br /&gt;
&lt;br /&gt;
===== Erweiterte Funktionen =====&lt;br /&gt;
&lt;br /&gt;
;OpCache&lt;br /&gt;
:Der PHP OPcache ist ein Caching-Mechanismus, der bereits kompilierte PHP-Skripte im Speicher hält, um die Ausführungszeit zu verkürzen und die Serverlast zu reduzieren. Dadurch wird vermieden, dass PHP-Dateien bei jedem Request erneut geparst und kompiliert werden, was die Performance von Webanwendungen erheblich steigert.&lt;br /&gt;
;CDN&lt;br /&gt;
:Verteilt statische Inhalte (Bilder, CSS, JS) auf mehrere Server weltweit, um die Ladezeiten zu verbessern.&lt;br /&gt;
;Varnish&lt;br /&gt;
:Reverse Proxy und Caching-Lösung, die dynamische Inhalte zwischenspeichern kann, um die Performance zu verbessern.&lt;br /&gt;
;Redis&lt;br /&gt;
:In-Memory-Datenbank, die als Cache, Session-Store oder zur Message-Queue verwendet wird.&lt;br /&gt;
;Memcached&lt;br /&gt;
:Einfacher In-Memory-Cache, um häufig genutzte Daten schneller bereitzustellen.&lt;br /&gt;
;Load Balancer&lt;br /&gt;
:Verteilt eingehende Anfragen auf mehrere Server, um Last zu verteilen und Ausfallsicherheit zu gewährleisten. Beispiele: HAProxy, AWS Elastic Load Balancing.&lt;br /&gt;
;Queue-System&lt;br /&gt;
:Verarbeitet Aufgaben asynchron, um die Hauptanwendung zu entlasten. Beispiele: RabbitMQ, Kafka.&lt;br /&gt;
;API Gateway&lt;br /&gt;
:Bietet eine zentrale Schnittstelle für externe APIs und sorgt für Sicherheit, Monitoring und Load Balancing. Beispiele: Kong, Apigee.&lt;br /&gt;
;Service Worker&lt;br /&gt;
:Wird im Browser verwendet, um Offline-Funktionalität und Caching zu ermöglichen. Besonders wichtig für Progressive Web Apps (PWAs).&lt;br /&gt;
;Monitoring &amp;amp; Logging&lt;br /&gt;
:Überwacht die Performance der Anwendung und speichert Logs. Beispiele: Grafana, Kibana, Prometheus.&lt;br /&gt;
;Firewall/WAF (Web Application Firewall)&lt;br /&gt;
:Schützt die Webanwendung vor Angriffen wie SQL-Injection, XSS und anderen Webbedrohungen. Beispiele: AWS WAF, Cloudflare WAF.&lt;br /&gt;
;Container &amp;amp; Orchestrierung&lt;br /&gt;
:Container-Technologien (z. B. Docker) und Orchestrierungstools (z. B. Kubernetes) ermöglichen eine flexible und skalierbare Bereitstellung.&lt;br /&gt;
;CI/CD-Pipeline&lt;br /&gt;
:Automatisiert den Build- und Deployment-Prozess. Beispiele: GitLab CI, Jenkins, GitHub Actions.&lt;br /&gt;
;Identity Provider (IdP)&lt;br /&gt;
:Verwaltet Benutzeridentitäten und ermöglicht Single Sign-On (SSO). Beispiele: Keycloak, Okta, Auth0.&lt;br /&gt;
;Message Broker&lt;br /&gt;
:Vermittelt Nachrichten zwischen verschiedenen Diensten. Beispiele: RabbitMQ, Apache Kafka.&lt;br /&gt;
;Edge Functions&lt;br /&gt;
:Kleine Funktionen, die nahe am Client ausgeführt werden, um die Latenz zu verringern. Beispiele: Cloudflare Workers, AWS Lambda@Edge.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== HTTP und HTTPS ===&lt;br /&gt;
Das Hypertext Transfer Protocol (HTTP) ist das Fundament der Kommunikation im World Wide Web. Es beschreibt, wie Clients (z. B. Webbrowser) mit Servern interagieren, um Webseiten und andere Ressourcen abzurufen. HTTPS ist die sichere Version von HTTP und nutzt Verschlüsselungstechnologien wie SSL/TLS, um die Kommunikation vor Manipulation und Abhören zu schützen.&lt;br /&gt;
==== Das Hypertext Transfer Protocol ====&lt;br /&gt;
HTTP ist ein textbasiertes Protokoll, das den Austausch von Informationen zwischen einem Client und einem Server regelt. Es arbeitet nach dem Request-Response-Prinzip: Der Client stellt eine Anfrage (Request), und der Server liefert eine Antwort (Response) zurück.&lt;br /&gt;
Merkmale von HTTP:&lt;br /&gt;
Verbindungslos: Jede Anfrage wird unabhängig behandelt. Es gibt keine dauerhafte Verbindung zwischen Client und Server.&lt;br /&gt;
Statusbasiert: Der Server antwortet immer mit einem Statuscode, der den Erfolg oder Misserfolg der Anfrage angibt.&lt;br /&gt;
Anwendungsprotokoll: HTTP wird auf der Anwendungsschicht genutzt und baut auf Protokollen wie TCP/IP auf.&lt;br /&gt;
Ein typisches Beispiel für eine HTTP-Anfrage ist das Abrufen einer Webseite. Wenn ein Benutzer eine URL in den Browser eingibt, sendet der Browser eine HTTP-Anfrage an den Server, der die gewünschte Seite bereitstellt.&lt;br /&gt;
===== HTTP-Methoden =====&lt;br /&gt;
HTTP bietet verschiedene Methoden, die angeben, welche Art von Aktion der Client auf dem Server ausführen möchte. Die vier wichtigsten Methoden sind:&lt;br /&gt;
&lt;br /&gt;
====== GET ======&lt;br /&gt;
Wird verwendet, um Daten vom Server abzurufen.&lt;br /&gt;
Hat keine Nebenwirkungen auf den Server (idempotent).&lt;br /&gt;
Beispiel: Abrufen einer Webseite oder eines Bildes.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;GET /index.html HTTP/1.1&amp;lt;br&amp;gt;Host: example.com&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
====== POST ======&lt;br /&gt;
Wird verwendet, um Daten an den Server zu senden (z. B. Formulareinsendungen).&lt;br /&gt;
Kann neue Ressourcen erstellen oder bestehende aktualisieren.&lt;br /&gt;
Beispiel: Ein Benutzer sendet ein Kontaktformular ab.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;POST /contact HTTP/1.1&amp;lt;br&amp;gt;Host: example.com&amp;lt;br&amp;gt;Content-Type: application/x-www-form-urlencoded&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;name=John&amp;amp;message=Hello&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
====== PUT ======&lt;br /&gt;
Wird verwendet, um eine Ressource auf dem Server zu erstellen oder zu ersetzen.&lt;br /&gt;
Beispiel: Hochladen oder Aktualisieren einer Datei.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;PUT /file.txt HTTP/1.1&lt;br /&gt;
&lt;br /&gt;
Host: example.com&lt;br /&gt;
&lt;br /&gt;
Content-Type: text/plain&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;This is a file content.&amp;lt;/pre&amp;gt;&lt;br /&gt;
====== DELETE ======&lt;br /&gt;
Wird verwendet, um eine Ressource vom Server zu löschen.&lt;br /&gt;
Beispiel: Löschen einer Datei oder eines Eintrags.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;DELETE /file.txt HTTP/1.1&lt;br /&gt;
&lt;br /&gt;
Host: example.com&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Andere wichtige Methoden sind HEAD, OPTIONS und PATCH, die je nach Anwendungsszenario genutzt werden.&lt;br /&gt;
&lt;br /&gt;
===== HTTP Statuscodes =====&lt;br /&gt;
HTTP-Statuscodes sind dreistellige Zahlen, die angeben, wie der Server die Anfrage des Clients verarbeitet hat  &amp;lt;ref&amp;gt;HTTP response status codes&amp;lt;/ref&amp;gt;  (https://developer.mozilla.org/en-US/docs/Web/HTTP/Status) . Sie sind in fünf Kategorien unterteilt:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
1xx Informationelle Antworten&lt;br /&gt;
&lt;br /&gt;
2xx Erfolgreiche Anfragen&lt;br /&gt;
&lt;br /&gt;
3xx Umleitungen&lt;br /&gt;
&lt;br /&gt;
4xx Clientfehler&lt;br /&gt;
&lt;br /&gt;
5xx Serverfehler&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Wichtige Statuscodes:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
200 OK: Die Anfrage war erfolgreich. Die angeforderten Daten werden zurückgegeben.&lt;br /&gt;
&lt;br /&gt;
301 Moved Permanently: Die angeforderte Ressource wurde dauerhaft an eine neue URL verschoben.&lt;br /&gt;
&lt;br /&gt;
302 Found: Temporäre Weiterleitung.&lt;br /&gt;
&lt;br /&gt;
404 Not Found: Die angeforderte Ressource wurde nicht gefunden.&lt;br /&gt;
&lt;br /&gt;
500 Internal Server Error: Ein allgemeiner Fehler auf dem Server.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
===== Bedeutung von HTTPS und SSL/TLS =====&lt;br /&gt;
====== HTTPS (Hypertext Transfer Protocol Secure) ======&lt;br /&gt;
HTTPS ist die sichere Version von HTTP. Es verwendet Verschlüsselung, um die Kommunikation zwischen Client und Server vor Abhören und Manipulation zu schützen. Die Verschlüsselung erfolgt mithilfe von SSL (Secure Sockets Layer) bzw. TLS (Transport Layer Security).&lt;br /&gt;
Vorteile von HTTPS:&lt;br /&gt;
Vertraulichkeit: Die übertragenen Daten sind verschlüsselt und können nicht von Dritten eingesehen werden.&lt;br /&gt;
Integrität: Es wird sichergestellt, dass die Daten während der Übertragung nicht manipuliert wurden.&lt;br /&gt;
Authentizität: Der Client kann sicherstellen, dass er mit dem richtigen Server kommuniziert.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Request-Verarbeitung ====&lt;br /&gt;
&amp;lt;p data-start=&amp;quot;185&amp;quot; data-end=&amp;quot;498&amp;quot;&amp;gt;Wenn ein Client – sei es ein Notebook, Smartphone, Desktop-PC oder ein anderer Server – eine Webanwendung aufruft, wird zunächst ein HTTP-Request an den Webserver gesendet. Diese Anfrage kann sich auf die Startseite (die sogenannte &amp;quot;Main Page&amp;quot;), eine Unterseite oder auch einen spezifischen API-Endpunkt beziehen.&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p data-start=&amp;quot;500&amp;quot; data-end=&amp;quot;856&amp;quot;&amp;gt;Der Webserver empfängt diesen Request und analysiert ihn auf Basis seiner Konfiguration. Je nach Art der angeforderten Ressource unterscheidet sich das weitere Vorgehen: Handelt es sich um eine statische Datei, beispielsweise ein Bild, ein CSS-Stylesheet oder ein JavaScript-File, so wird diese direkt und ohne weitere Verarbeitung vom Server ausgeliefert.&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p data-start=&amp;quot;858&amp;quot; data-end=&amp;quot;1336&amp;quot;&amp;gt;Wird hingegen eine dynamische Ressource angefragt – typischerweise ein PHP-Skript –, so übergibt der Webserver die Ausführung an den konfigurierten PHP-Interpreter. Dieser verarbeitet das Skript schrittweise. Im Rahmen dieser Verarbeitung kann es notwendig sein, Daten aus einer Datenbank, beispielsweise MySQL, abzurufen. Dies ist etwa dann der Fall, wenn die dynamisch generierte Seite Produktinformationen anzeigen soll, wie es in typischen E-Commerce-Anwendungen üblich ist.&amp;amp;nbsp; Sobald die benötigten Daten durch den PHP-Interpreter aus der Datenbank – etwa MySQL – abgerufen wurden, erfolgt deren Weiterverarbeitung innerhalb des PHP-Skripts. Dabei werden die Daten strukturiert, inhaltlich aufbereitet und schließlich in ein HTML-Dokument eingebettet, um eine für den Client interpretierbare Darstellung zu ermöglichen.&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p data-start=&amp;quot;524&amp;quot; data-end=&amp;quot;874&amp;quot;&amp;gt;Nach Abschluss dieser Verarbeitung wird die vollständige HTTP-Antwort – bestehend aus HTML (und gegebenenfalls eingebetteten oder verlinkten CSS- und JavaScript-Ressourcen) – an den Webserver zurückgegeben. Der Webserver wiederum überträgt diese Antwort an den anfragenden Client, sodass dort die fertige Web-Seite im Browser dargestellt werden kann.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[file:SED501-Webserver-Requests.png|600px|center|thumb|Requestverarbeitung]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Tools ====&lt;br /&gt;
==== Web Developer Tools ====&lt;br /&gt;
Die [https://developer.chrome.com/ Chrome Developer Tools (DevTools)] (&amp;quot;Developer Toolbar&amp;quot;)&amp;amp;nbsp; sind ein mächtes Werkzeug für Entwickler*innen und ein integraler Bestandteil moderner Webentwicklung. Sie ermöglichen eine tiefgehende Analyse, Inspektion und Optimierung von Webseiten direkt im Browser. Sie sind in Google Chrome sowie in Chromium-basierten Browsern (z. B. Microsoft Edge) integriert und stellen ein unverzichtbares Werkzeug für Frontend- und Backend-Entwickler dar.&lt;br /&gt;
=== 1. Zugriff und grundlegender Aufbau ===&lt;br /&gt;
&amp;lt;p data-start=&amp;quot;718&amp;quot; data-end=&amp;quot;1038&amp;quot;&amp;gt;Die Developer Tools können in Chrome durch einen Rechtsklick auf eine Seite und Auswahl von &amp;lt;em data-start=&amp;quot;810&amp;quot; data-end=&amp;quot;825&amp;quot;&amp;gt;„Untersuchen“&amp;lt;/em&amp;gt; oder über die Taste &amp;lt;code data-start=&amp;quot;846&amp;quot; data-end=&amp;quot;851&amp;quot;&amp;gt;F12&amp;lt;/code&amp;gt; bzw. &amp;lt;code data-start=&amp;quot;857&amp;quot; data-end=&amp;quot;878&amp;quot;&amp;gt;Strg + Umschalt + I&amp;lt;/code&amp;gt; (bzw. &amp;lt;code data-start=&amp;quot;885&amp;quot; data-end=&amp;quot;890&amp;quot;&amp;gt;Cmd&amp;lt;/code&amp;gt; auf macOS) geöffnet werden. Die Benutzeroberfläche ist in mehrere Bereiche unterteilt, von denen jeder spezifische Aspekte der Webseite analysiert:&amp;lt;/p&amp;gt;&lt;br /&gt;
*&amp;lt;p data-start=&amp;quot;1042&amp;quot; data-end=&amp;quot;1054&amp;quot;&amp;gt;&amp;lt;strong data-start=&amp;quot;1042&amp;quot; data-end=&amp;quot;1054&amp;quot;&amp;gt;Elements&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
*&amp;lt;p data-start=&amp;quot;1057&amp;quot; data-end=&amp;quot;1068&amp;quot;&amp;gt;&amp;lt;strong data-start=&amp;quot;1057&amp;quot; data-end=&amp;quot;1068&amp;quot;&amp;gt;Console&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
*&amp;lt;p data-start=&amp;quot;1071&amp;quot; data-end=&amp;quot;1082&amp;quot;&amp;gt;&amp;lt;strong data-start=&amp;quot;1071&amp;quot; data-end=&amp;quot;1082&amp;quot;&amp;gt;Sources&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
*&amp;lt;p data-start=&amp;quot;1085&amp;quot; data-end=&amp;quot;1096&amp;quot;&amp;gt;&amp;lt;strong data-start=&amp;quot;1085&amp;quot; data-end=&amp;quot;1096&amp;quot;&amp;gt;Network&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
*&amp;lt;p data-start=&amp;quot;1099&amp;quot; data-end=&amp;quot;1114&amp;quot;&amp;gt;&amp;lt;strong data-start=&amp;quot;1099&amp;quot; data-end=&amp;quot;1114&amp;quot;&amp;gt;Performance&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
*&amp;lt;p data-start=&amp;quot;1117&amp;quot; data-end=&amp;quot;1127&amp;quot;&amp;gt;&amp;lt;strong data-start=&amp;quot;1117&amp;quot; data-end=&amp;quot;1127&amp;quot;&amp;gt;Memory&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
*&amp;lt;p data-start=&amp;quot;1130&amp;quot; data-end=&amp;quot;1145&amp;quot;&amp;gt;&amp;lt;strong data-start=&amp;quot;1130&amp;quot; data-end=&amp;quot;1145&amp;quot;&amp;gt;Application&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
*&amp;lt;p data-start=&amp;quot;1148&amp;quot; data-end=&amp;quot;1160&amp;quot;&amp;gt;&amp;lt;strong data-start=&amp;quot;1148&amp;quot; data-end=&amp;quot;1160&amp;quot;&amp;gt;Security&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
*&amp;lt;p data-start=&amp;quot;1163&amp;quot; data-end=&amp;quot;1177&amp;quot;&amp;gt;&amp;lt;strong data-start=&amp;quot;1163&amp;quot; data-end=&amp;quot;1177&amp;quot;&amp;gt;Lighthouse&amp;lt;/strong&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p data-start=&amp;quot;4991&amp;quot; data-end=&amp;quot;5488&amp;quot;&amp;gt;Die Chrome Developer Tools sind in der Praxis unverzichtbar für die Fehlersuche und Qualitätskontrolle. Sie ermöglichen es, in Echtzeit auf das Verhalten von Webseiten zu reagieren, Styles und Layouts zu debuggen, Speicherzustände zu analysieren und die Kommunikation mit dem Server zu prüfen. Besonders im Zusammenspiel mit serverseitigen Technologien wie PHP oder datenbankbasierten Architekturen bieten sie wertvolle Einsichten in das Zusammenspiel zwischen Frontend und Backend.&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p data-start=&amp;quot;5490&amp;quot; data-end=&amp;quot;5781&amp;quot;&amp;gt;Die effektive Nutzung der DevTools ist somit eine Schlüsselkompetenz für Webentwicklerinnen und -entwickler, insbesondere im Kontext komplexer und interaktiver Anwendungen. Eine fundierte Kenntnis ihrer Funktionalitäten trägt maßgeblich zur Effizienz und Qualität der Entwicklungsarbeit bei.&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p data-start=&amp;quot;1179&amp;quot; data-end=&amp;quot;1260&amp;quot;&amp;gt;Im Folgenden werden die wichtigsten Bereiche und ihre Funktionalitäten erläutert.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Überblick über zentrale Funktionen ===&lt;br /&gt;
==== Elements: DOM-Inspektion und Live-Manipulation ====&lt;br /&gt;
&amp;lt;p data-start=&amp;quot;1367&amp;quot; data-end=&amp;quot;1535&amp;quot;&amp;gt;Der &amp;lt;em data-start=&amp;quot;1371&amp;quot; data-end=&amp;quot;1381&amp;quot;&amp;gt;Elements&amp;lt;/em&amp;gt;-Tab zeigt die aktuelle Struktur des Document Object Models (DOM) an, das der Browser zur Darstellung der Webseite generiert hat. Es ermöglicht die Inspektion und Bearbeitung der DOM-Struktur einer Webseite in Echtzeit. Hier können HTML-Elemente untersucht, CSS-Regeln direkt verändert und das Layout sowie das Box-Modell eines Elements visualisiert werden. Dies ist insbesondere bei der Behebung von Darstellungsfehlern oder beim gezielten Testen von Styling-Anpassungen hilfreich. Darüber hinaus lassen sich Event-Listener anzeigen und Pseudoklassen wie &amp;lt;code data-start=&amp;quot;1744&amp;quot; data-end=&amp;quot;1752&amp;quot;&amp;gt;:hover&amp;lt;/code&amp;gt; simulieren.&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p data-start=&amp;quot;1766&amp;quot; data-end=&amp;quot;1866&amp;quot;&amp;gt;Diese Funktionalitäten sind besonders hilfreich zur Fehleranalyse bei Layout- und Styling-Problemen.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Console: Debugging und Logging ====&lt;br /&gt;
&amp;lt;p data-start=&amp;quot;1909&amp;quot; data-end=&amp;quot;1985&amp;quot;&amp;gt;Die &amp;lt;em data-start=&amp;quot;1770&amp;quot; data-end=&amp;quot;1779&amp;quot;&amp;gt;Console&amp;lt;/em&amp;gt; stellt ein interaktives Interface für JavaScript dar. Sie ermöglicht das Ausführen von JavaScript-Befehlen während der Laufzeit und zeigt Fehlermeldungen, Warnungen oder benutzerdefinierte Log-Ausgaben an. Entwicklerinnen und Entwickler können die Konsole nutzen, um schnell auf DOM-Elemente zuzugreifen, Funktionen zu testen oder Probleme im Verhalten von Skripten zu identifizieren. Auch komplexere Debugging-Szenarien lassen sich mit der Konsole realisieren, insbesondere in Kombination mit Breakpoints.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Sources: Debugging und Quellcode-Analyse ====&lt;br /&gt;
&amp;lt;p data-start=&amp;quot;2405&amp;quot; data-end=&amp;quot;2491&amp;quot;&amp;gt;Im &amp;lt;em data-start=&amp;quot;2291&amp;quot; data-end=&amp;quot;2300&amp;quot;&amp;gt;Sources&amp;lt;/em&amp;gt;-Tab steht der Quellcode im Fokus. Hier können JavaScript-Dateien durchsucht und analysiert, Breakpoints gesetzt und Watch-Expressions definiert werden. Der Call Stack, die aktuellen Scope-Variablen sowie die Möglichkeit zur schrittweisen Ausführung des Codes erlauben ein präzises Debugging komplexer Client-seitiger Logik. Unterstützt wird dies durch sogenannte Sourcemaps, welche die Zuordnung zwischen komprimierten oder transpilieren Skripten (z. B. aus TypeScript) und dem ursprünglichen Quellcode ermöglichen. Das hilft bei der gezielten Analyse komplexer Client-seitiger Logik.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Network: Analyse von HTTP-Requests ====&lt;br /&gt;
&amp;lt;p data-start=&amp;quot;2838&amp;quot; data-end=&amp;quot;2907&amp;quot;&amp;gt;Der &amp;lt;em data-start=&amp;quot;2842&amp;quot; data-end=&amp;quot;2851&amp;quot;&amp;gt;Network&amp;lt;/em&amp;gt;-Tab zeigt sämtliche HTTP-Requests und -Responses, die während des Ladens und der Ausführung einer Webseite stattfinden. Hier lassen sich Details wie HTTP-Methoden, Header, Statuscodes und Antwortzeiten einsehen. Das Einsehen der Request-Daten ist beispielsweise beim Debuggen von Formularen und AJAX-Requests hilfreich, um die gesendeten Daten anzuzeigen. Mit Hilfe des Waterfall-Diagramms können zeitliche Zusammenhänge zwischen einzelnen Anfragen nachvollzogen werden. Darüber hinaus besteht die Möglichkeit, Netzwerkbedingungen künstlich zu simulieren (z. B. langsame Mobilfunkverbindungen), um das Verhalten der Anwendung unter realistischen Bedingungen zu testen.&amp;amp;nbsp;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p data-start=&amp;quot;3146&amp;quot; data-end=&amp;quot;3251&amp;quot;&amp;gt;Diese Informationen sind essenziell für Performance-Optimierung und Fehlerdiagnose bei API-Kommunikation.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Performance: Laufzeitanalyse und Rendering ====&lt;br /&gt;
&amp;lt;p data-start=&amp;quot;3306&amp;quot; data-end=&amp;quot;3429&amp;quot;&amp;gt;Der Bereich &amp;lt;em data-start=&amp;quot;3438&amp;quot; data-end=&amp;quot;3451&amp;quot;&amp;gt;Performance&amp;lt;/em&amp;gt; erlaubt eine detaillierte Aufzeichnung und Analyse der Laufzeitverhalten einer Webseite. Während eines Recordings werden Informationen über Render-Zyklen, Scripting-Zeiten, Layout-Operationen und Repaints gesammelt. Diese Daten helfen dabei, Performance-Engpässe zu identifizieren und gezielt zu optimieren, beispielsweise durch Reduktion unnötiger DOM-Manipulationen oder die Entlastung des Main Threads.&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p data-start=&amp;quot;3535&amp;quot; data-end=&amp;quot;3617&amp;quot;&amp;gt;Diese Daten helfen dabei, Engpässe und ineffiziente DOM-Updates zu identifizieren.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Application: Web Storage und Service Worker ====&lt;br /&gt;
Der &amp;lt;em data-start=&amp;quot;3863&amp;quot; data-end=&amp;quot;3876&amp;quot;&amp;gt;Application&amp;lt;/em&amp;gt;-Tab gibt Einblick in clientseitige Speichertechnologien wie Local Storage, Session Storage, IndexedDB und Cookies. Darüber hinaus lassen sich hier Service Worker untersuchen, welche im Kontext von Progressive Web Apps (PWA) für Offline-Funktionalität und Hintergrundsynchronisation zuständig sind. Auch das App-Manifest einer installierbaren Webanwendung kann hier eingesehen werden.&lt;br /&gt;
==== Security: TLS- und Zertifikatsinformationen ====&lt;br /&gt;
&amp;lt;p data-start=&amp;quot;4055&amp;quot; data-end=&amp;quot;4128&amp;quot;&amp;gt;Ein weiterer wichtiger Aspekt ist die Sicherheit der Webanwendung, die im &amp;lt;em data-start=&amp;quot;4336&amp;quot; data-end=&amp;quot;4346&amp;quot;&amp;gt;Security&amp;lt;/em&amp;gt;-Tab thematisiert wird. Dieser zeigt, ob die Seite über HTTPS geladen wurde, welches TLS-Protokoll verwendet wird, welche Zertifikate zur Absicherung eingesetzt werden und ob potenzielle Schwachstellen wie „Mixed Content“ (unsichere Ressourcen über HTTP) vorliegen.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Lighthouse: Automatisierte Audits ====&lt;br /&gt;
&amp;lt;p data-start=&amp;quot;4466&amp;quot; data-end=&amp;quot;4477&amp;quot;&amp;gt;Der &amp;lt;em data-start=&amp;quot;4636&amp;quot; data-end=&amp;quot;4648&amp;quot;&amp;gt;Lighthouse&amp;lt;/em&amp;gt;-Tab bietet ein leistungsstarkes Analysewerkzeug, mit dem eine Webanwendung automatisiert hinsichtlich Performance, Barrierefreiheit, Best Practices, Suchmaschinenfreundlichkeit (SEO) und PWA-Konformität bewertet werden kann. Die dabei erzeugten Berichte enthalten nicht nur Bewertungen, sondern auch konkrete Handlungsempfehlungen zur Optimierung.&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p data-start=&amp;quot;4569&amp;quot; data-end=&amp;quot;4668&amp;quot;&amp;gt;Die Berichte enthalten konkrete Optimierungsvorschläge und ermöglichen objektive Qualitätsanalysen.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Beispiel anhand der DevTools: Webserver erkennen&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
Sofern der Webservername im Respone Header mitgeschickt wird, ist dieser im &amp;quot;Netzwerk&amp;quot; Tab nach Auswahl einer Quelle (Hauptdokument, Bild, CSS oder JavaScript-Datei, die direkt vom Server geladen wird) erkennbar.&lt;br /&gt;
&lt;br /&gt;
Oftmals wird der Webserver auch entfernt und nicht im Response Header mitgeschickt, um die Angriffsoberfläche für Schwachstellen zu minimieren.&lt;br /&gt;
&lt;br /&gt;
[[file:SED501-Developer-Toolbar.png|600px|center|thumb|Web Developer Toolbar]]&lt;br /&gt;
== PHP ==&lt;br /&gt;
&amp;lt;p&amp;gt;PHP (rekursives Akronym für PHP: Hypertext Preprocessor) ist eine weit verbreitete Programmiersprache für Web-Anwendungen.&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;Die Programmiersprache ist weit verbreitet und wird auf mehr als 75% aller Internetseiten (mit Serverseitiger Programmiersprache) verwendet.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;p&amp;gt;PHP geht auf ein Projekt von Rasmus Lerdorf aus dem Jahr 1994 zurück. Damals wurde eine Sammlung von Skripten unter dem Namen&amp;amp;nbsp; “Personal Home Page Tools” veröffentlicht.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;p&amp;gt;https://www.php.net/manual/en/history.php.php&amp;amp;nbsp;&amp;amp;nbsp;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;Derzeit (Stand Jänner 2025) ist die Version PHP 8.4 aktuell.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Web-Application Stack&amp;lt;br&amp;gt; ===&lt;br /&gt;
&amp;lt;p&amp;gt;PHP Anwendungen laufen üblicherweise in einer Umgebung mit Linux, Apache und MySQL (“LAMP-Stack”) oder Linux, Nginx und MySQL (“LEMP-Stack”). Der LAMP/LEMP-Stack enthält dabei alle Bestandteile, den eine Web-Anwendung benötigt: Betriebssystem, Webserver, Skript-Interpreter und Datenbank.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;p&amp;gt;Betriebssystem: Linux&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;Webserver: Apache oder Nginx&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;Skript-Interpreter: PHP&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;Datenbank: MySQL&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;p&amp;gt;PHP kann aber auch unter Windows als auch MacOS nativ laufen.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;p&amp;gt;Lokale Entwicklung&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;Damit eine PHP Anwendung lokal (auf dem Notebook) entwickelt werden kann, benötigt man eine lauffähige Umgebung entsprechend dem LAMP oder LEMP-Stack.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;p&amp;gt;Folgende Möglichkeiten gibt es:&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;Nativ:&amp;lt;/p&amp;gt;&lt;br /&gt;
*Webserver, PHP und Datenbank laufen auf dem nativen Betriebssystem&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;p&amp;gt;Virtualisierte Umgebung:&amp;lt;/p&amp;gt;&lt;br /&gt;
*Webserver, PHP und Datenbank laufen innerhalb einer eigenen virtuellen Maschine (zB VirtualBox)&lt;br /&gt;
*Webserver, PHP und Datenbank laufen als containerisierte Umgebung (Docker)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;p&amp;gt;Es gibt eine Vielzahl an Vorlagen für virtuelle Entwicklungsmaschinen oder Container-Umgebungen.&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;Eine, der bekanntesten und populärsten Umgebungen ist ddev.&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;https://ddev.com/&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;https://ddev.com/get-started/&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;p&amp;gt;Damit können Web-Projekte schnell und einfach lokal lauffähig gemacht werden.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
#DDEV installieren&lt;br /&gt;
&amp;lt;p&amp;gt;https://ddev.com/get-started/&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
#ddev config&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;p&amp;gt;➜&amp;amp;nbsp; playground mkdir mywebdevproject&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;➜&amp;amp;nbsp; playground cd mywebdevproject&amp;amp;nbsp;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;➜&amp;amp;nbsp; mywebdevproject ddev config&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;Creating a new DDEV project config in the current directory (/home/annavoelkl/playground/mywebdevproject)&amp;amp;nbsp;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;Once completed, your configuration will be written to /home/annavoelkl/playground/mywebdevproject/.ddev/config.yaml&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;&amp;lt;br&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;Project name (mywebdevproject):&amp;amp;nbsp;&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;p&amp;gt;The docroot is the directory from which your site is served.&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;This is a relative path from your project root at /home/annavoelkl/playground/mywebdevproject&amp;amp;nbsp;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;You may leave this value blank if your site files are in the project root&amp;amp;nbsp;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;Docroot Location (current directory):&amp;amp;nbsp;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;Found a php codebase at /home/annavoelkl/playground/mywebdevproject.&amp;amp;nbsp;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;Project Type [backdrop, cakephp, craftcms, django4, drupal, drupal6, drupal7, laravel, magento, magento2, php, python, shopware6, silverstripe, typo3, wordpress] (php): php&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;Configuration complete. You may now run &amp;#039;ddev start&amp;#039;.&amp;amp;nbsp;&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;lt;p&amp;gt;&amp;lt;br&amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
#DDEV Projekt starten: ddev start&lt;br /&gt;
##Die Container werden automatisch heruntergeladen und gestartet&lt;br /&gt;
#Projekt aufrufen https://mywebdevproject.ddev.site&amp;amp;nbsp;&lt;br /&gt;
##Noch gibt es hier nichts zu sehen&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
=== Das erste Programm ===&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;lt;p&amp;gt;Quelle: https://w3techs.com/technologies/overview/programming_language&amp;amp;nbsp;&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;p&amp;gt;helloworld.php&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{| style=&amp;quot;height: 253px;&amp;quot; width=&amp;quot;407&amp;quot; &lt;br /&gt;
|- &lt;br /&gt;
| style=&amp;quot;width: 405px;&amp;quot; | &amp;lt;p&amp;gt;&amp;amp;lt;!DOCTYPE html&amp;amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;&amp;amp;lt;html&amp;amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;lt;head&amp;amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;lt;title&amp;amp;gt;FernFH - Web Development&amp;amp;lt;/title&amp;amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;lt;/head&amp;amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;lt;body&amp;amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;lt;?php echo &amp;quot;Hallo Welt!&amp;quot;; ?&amp;amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;nbsp;&amp;amp;lt;/body&amp;amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;&amp;amp;lt;/html&amp;amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;br&amp;gt;&amp;lt;br&amp;gt;Grundlagen ===&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
==== Variablen ====&lt;br /&gt;
&amp;lt;p&amp;gt;Variablen werden in PHP durch ein Dollar-Zeichen ($) gefolgt vom Namen der Variable dargestellt. Bei Variablennamen wird zwischen Groß- und Kleinschreibung unterschieden (case-sensitive).&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;Ein gültiger Variablenname beginnt mit einem Buchstaben (A-Z, a-z oder die Bytes von 128 bis 255) oder einem Unterstrich (&amp;quot;_&amp;quot;), gefolgt von einer beliebigen Anzahl von Buchstaben, Zahlen oder Unterstrichen. Als regulärer Ausdruck (Regular Expression) würde das wie folgt ausgedrückt: ^[a-zA-Z_\x80-\xff][a-zA-Z0-9_\x80-\xff]*$&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;p&amp;gt;Der Geltungsbereich einer Variablen ergibt sich aus dem Zusammenhang, in dem sie definiert wurde. PHP hat einen Funktionsbereich und einen globalen Bereich. Jede Variable, die außerhalb einer Funktion definiert wird, ist auf den globalen Bereich beschränkt. Wenn eine Datei eingebunden wird, erbt der darin enthaltene Code den Variablenbereich der Zeile, in der die Einbindung erfolgt.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
==== Kontrollstrukturen ====&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;p&amp;gt;Verfügbare Kontrollstrukturen in PHP sind:&amp;lt;/p&amp;gt;&lt;br /&gt;
*if&lt;br /&gt;
*else&lt;br /&gt;
*elseif/else if&lt;br /&gt;
*while&lt;br /&gt;
*do-while&lt;br /&gt;
*for&lt;br /&gt;
*foreach&lt;br /&gt;
*break / continue&lt;br /&gt;
*switch / match&lt;br /&gt;
*declare&lt;br /&gt;
*return&lt;br /&gt;
*require / include&lt;br /&gt;
*require_once / include_once&lt;br /&gt;
*goto&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
=== Sessions &amp;amp; Cookies ===&lt;br /&gt;
==== session.gc_maxlifetime ====&lt;br /&gt;
=== Beschreibung: ===&lt;br /&gt;
&amp;lt;p&amp;gt;Gibt die Lebensdauer einer PHP-Session in Sekunden an. Nach Ablauf dieser Zeit wird die Session gelöscht.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Standardwert: ===&lt;br /&gt;
&lt;br /&gt;
*1440 Sekunden (24 Minuten)&lt;br /&gt;
&lt;br /&gt;
=== Beispiel: ===&lt;br /&gt;
&amp;lt;p&amp;gt;session.gc_maxlifetime = 3600&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Empfehlung: ===&lt;br /&gt;
&lt;br /&gt;
*Erhöhen Sie den Wert, wenn Benutzer länger eingeloggt bleiben sollen.&lt;br /&gt;
*Passen Sie den Wert an die Anforderungen Ihrer Anwendung an.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
=== Mailversand ===&lt;br /&gt;
=== Passwort-Hashing ===&lt;br /&gt;
&amp;lt;p&amp;gt;Sicheres Hashing von Passwörtern&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;https://www.php.net/manual/de/faq.passwords.php&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Dateioperationen ===&lt;br /&gt;
&amp;lt;p&amp;gt;https://www.php.net/manual/de/ref.filesystem.php&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
=== URL-Aufrufe / curl ===&lt;br /&gt;
&amp;lt;p&amp;gt;cURL in PHP ist eine Bibliothek, die es ermöglicht, HTTP-Anfragen von einem PHP-Skript aus an andere Server zu senden und deren Antworten zu verarbeiten. Es wird verwendet, um Daten aus externen APIs abzurufen, Dateien herunterzuladen, Formulare auf anderen Webseiten auszufüllen oder mit REST-APIs zu kommunizieren. Mit cURL kann man verschiedene Protokolle wie HTTP, HTTPS, FTP und mehr nutzen, was es zu einem flexiblen, mächtigen und wichtigen Werkzeug für den Datenaustausch zwischen Servern macht.&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;https://www.php.net/manual/de/ref.curl.php&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;lt;p&amp;gt;&amp;amp;lt;?php&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;// 1. cURL-Session initialisieren&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;$curl = curl_init();&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;p&amp;gt;// 2. URL der API setzen&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;$url = &amp;quot;https://api.open-meteo.com/v1/forecast?latitude=47.81&amp;amp;longitude=16.24&amp;amp;daily=temperature_2m_max,temperature_2m_min&amp;amp;current_weather=true&amp;amp;forecast_days=1&amp;quot;;&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;p&amp;gt;// 3. Optionen für die cURL-Session setzen&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;curl_setopt($curl, CURLOPT_URL, $url);&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;p&amp;gt;// 4. Anfrage ausführen und Antwort speichern&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;$response = curl_exec($curl);&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;p&amp;gt;// 5. cURL-Session schließen&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;curl_close($curl);&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;p&amp;gt;// 6. JSON-Antwort in ein PHP-Array umwandeln&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;$data = json_decode($response, true);&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;p&amp;gt;// 7. Wetterdaten ausgeben&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;$currentTemp = $data[&amp;#039;current_weather&amp;#039;][&amp;#039;temperature&amp;#039;];&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;$maxTemp = $data[&amp;#039;daily&amp;#039;][&amp;#039;temperature_2m_max&amp;#039;][0];&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;$minTemp = $data[&amp;#039;daily&amp;#039;][&amp;#039;temperature_2m_min&amp;#039;][0];&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;p&amp;gt;echo &amp;quot;Aktuelle Temperatur: &amp;quot; . $currentTemp . &amp;quot;°C\n&amp;quot;;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;echo &amp;quot;Tageshöchsttemperatur: &amp;quot; . $maxTemp . &amp;quot;°C\n&amp;quot;;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;echo &amp;quot;Tagestiefsttemperatur: &amp;quot; . $minTemp . &amp;quot;°C\n&amp;quot;;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;?&amp;amp;gt;&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
=== Erklärung der wichtigen Schritte: ===&lt;br /&gt;
&lt;br /&gt;
#curl_init() – Initialisiert die cURL-Session.&lt;br /&gt;
#curl_setopt() – Setzt Optionen:&lt;br /&gt;
#*CURLOPT_URL – Die API-URL mit den Parametern.&lt;br /&gt;
#*CURLOPT_RETURNTRANSFER – Gibt die Antwort als String zurück.&lt;br /&gt;
#curl_exec() – Führt die Anfrage aus und speichert die Antwort.&lt;br /&gt;
#json_decode() – Wandelt die JSON-Antwort in ein PHP-Array um.&lt;br /&gt;
#Ausgabe der Daten – Greift auf die Temperaturdaten zu und gibt sie aus.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;p&amp;gt;Beispielausgabe&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;Aktuelle Temperatur: 5.3°C&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;Tageshöchsttemperatur: 10.1°C&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;Tagestiefsttemperatur: 2.4°C&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
=== JSON ===&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
= Wichtige PHP-Konfigurationswerte =&lt;br /&gt;
&amp;lt;p&amp;gt;Die PHP-Konfiguration beeinflusst die Funktionsweise und Performance von PHP-Anwendungen. Die Konfigurationswerte werden in der Datei php.ini festgelegt und können je nach Serverumgebung angepasst werden. Hier sind die wichtigsten Konfigurationswerte, die häufig bei der Optimierung von PHP-Anwendungen eine Rolle spielen.&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;Die Anpassung der PHP-Konfigurationswerte ist ein wichtiger Schritt, um die Performance und Sicherheit von Webanwendungen zu gewährleisten. Je nach Anwendungsszenario können die Werte angepasst werden, um Speicherprobleme, Zeitüberschreitungen oder Upload-Beschränkungen zu verhindern.&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Die Konfiguration von PHP ist über das zentrale Konfigurationsfile php.ini möglich, beispielsweise in /etc/php/8.2/fpm&lt;br /&gt;
&lt;br /&gt;
Die PHP Konfiguration kann wie folgt angezeigt werden:&lt;br /&gt;
&lt;br /&gt;
php.ini Datei anzeigen&lt;br /&gt;
&lt;br /&gt;
Über die Kommandozeile (direkt auf dem Webserver): php -i&lt;br /&gt;
&lt;br /&gt;
Via PHP Skript: &amp;amp;lt;? phpinfo(); ?&amp;amp;gt;&lt;br /&gt;
&lt;br /&gt;
Zu beachten ist, dass einige PHP Konfigurationswerte, die in der php.ini eingetragen sind, beim Apache Webserver pro Verzeichnis mit einem .htaccess Konfigurationsfile überschreiben werden können. Bei Nginx existiert ein solches File nicht, hier muss immer die zentrale Konfiguration aktualisiert werden.&lt;br /&gt;
=== memory_limit ===&lt;br /&gt;
&amp;lt;p&amp;gt;Dieser Wert legt fest, wie viel Speicher eine PHP-Skriptdatei maximal nutzen darf. Ein zu niedriger Wert kann dazu führen, dass komplexe Skripte nicht vollständig ausgeführt werden.&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;Standardwert: 128M (128 Megabyte)&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Beispiel: ===&lt;br /&gt;
&amp;lt;p&amp;gt;memory_limit = 256M&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Empfehlung: ===&lt;br /&gt;
&lt;br /&gt;
*Erhöhen Sie den Wert, wenn Ihre Anwendung viele Daten verarbeitet, z. B. bei Bildbearbeitung oder großen Datenbankabfragen.&lt;br /&gt;
*Setzen Sie einen vernünftigen Grenzwert, um eine übermäßige Speichernutzung zu verhindern.&lt;br /&gt;
&amp;lt;p class=&amp;quot;mwt-heading&amp;quot; &amp;gt;&amp;#039;&amp;#039;&amp;#039;Was sind empfohlene Werte für memory_limit?&amp;#039;&amp;#039;&amp;#039;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;div&amp;gt;&lt;br /&gt;
{|&lt;br /&gt;
|- &lt;br /&gt;
|  &amp;lt;p&amp;gt;Anwendungstyp&amp;lt;/p&amp;gt;&lt;br /&gt;
|  &amp;lt;p&amp;gt;Empfehlung&amp;lt;/p&amp;gt;&lt;br /&gt;
|- &lt;br /&gt;
|  &amp;lt;p&amp;gt;Kleine Website&amp;lt;/p&amp;gt;&lt;br /&gt;
|  &amp;lt;p&amp;gt;64M - 128M&amp;lt;/p&amp;gt;&lt;br /&gt;
|- &lt;br /&gt;
|  &amp;lt;p&amp;gt;Durchschnittliche App&amp;lt;/p&amp;gt;&lt;br /&gt;
|  &amp;lt;p&amp;gt;256M - 512M&amp;lt;/p&amp;gt;&lt;br /&gt;
|- &lt;br /&gt;
|  &amp;lt;p&amp;gt;E-Commerce (z.B. Magento, Shopware,...)&amp;lt;/p&amp;gt;&lt;br /&gt;
|  &amp;lt;p&amp;gt;512M - 1G+&amp;lt;/p&amp;gt;&lt;br /&gt;
|- &lt;br /&gt;
|  &amp;lt;p&amp;gt;API / Backend mit vielen Daten&amp;lt;/p&amp;gt;&lt;br /&gt;
|  &amp;lt;p&amp;gt;512M - 2G+&amp;lt;/p&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;h3 class=&amp;quot;mwt-heading&amp;quot; &amp;gt;max_execution_time&amp;lt;/h3&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;Dieser Wert bestimmt, wie lange ein PHP-Skript maximal laufen darf, bevor es abgebrochen wird. Er wird in Sekunden angegeben.&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;Standardwert: 30 Sekunden&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p class=&amp;quot;mwt-heading&amp;quot; &amp;gt;&amp;#039;&amp;#039;&amp;#039;Beispiel:&amp;#039;&amp;#039;&amp;#039;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;max_execution_time = 60&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p class=&amp;quot;mwt-heading&amp;quot; &amp;gt;&amp;#039;&amp;#039;&amp;#039;Empfehlung:&amp;#039;&amp;#039;&amp;#039;&amp;lt;/p&amp;gt;&lt;br /&gt;
*Für einfache Webanwendungen reicht ein niedriger Wert.&lt;br /&gt;
*Bei rechenintensiven Prozessen (z. B. Datenmigrationen) kann es sinnvoll sein, den Wert temporär zu erhöhen.&lt;br /&gt;
&amp;lt;h3 class=&amp;quot;mwt-heading&amp;quot; &amp;gt;post_max_size&amp;lt;/h3&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;Dieser Wert gibt die maximale Größe von Daten an, die per POST-Methode gesendet werden können. Er beeinflusst Formularübermittlungen und Datei-Uploads.&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;Standardwert: 8M (8 Megabyte)&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p class=&amp;quot;mwt-heading&amp;quot; &amp;gt;&amp;#039;&amp;#039;&amp;#039;Beispiel:&amp;#039;&amp;#039;&amp;#039;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;post_max_size = 20M&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p class=&amp;quot;mwt-heading&amp;quot; &amp;gt;&amp;#039;&amp;#039;&amp;#039;Empfehlung:&amp;#039;&amp;#039;&amp;#039;&amp;lt;/p&amp;gt;&lt;br /&gt;
*Erhöhen Sie diesen Wert, wenn Ihre Anwendung große Dateien hochladen muss.&lt;br /&gt;
*Der Wert sollte immer größer als upload_max_filesize sein.&lt;br /&gt;
&amp;lt;h3 class=&amp;quot;mwt-heading&amp;quot; &amp;gt;upload_max_filesize&amp;lt;/h3&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;Dieser Wert legt die maximale Größe für hochgeladene Dateien fest.&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;Standardwert: 2M (2 Megabyte)&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p class=&amp;quot;mwt-heading&amp;quot; &amp;gt;&amp;#039;&amp;#039;&amp;#039;Beispiel:&amp;#039;&amp;#039;&amp;#039;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;upload_max_filesize = 10M&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p class=&amp;quot;mwt-heading&amp;quot; &amp;gt;&amp;#039;&amp;#039;&amp;#039;Empfehlung:&amp;#039;&amp;#039;&amp;#039;&amp;lt;/p&amp;gt;&lt;br /&gt;
*Passen Sie diesen Wert entsprechend den Anforderungen Ihrer Anwendung an.&lt;br /&gt;
*In Kombination mit post_max_size verwenden.&lt;br /&gt;
&amp;lt;p&amp;gt;max_input_vars&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;Dieser Wert gibt an, wie viele Eingabevariablen (z. B. aus Formularen) ein PHP-Skript verarbeiten kann. Ein zu niedriger Wert kann dazu führen, dass große Formulare nicht korrekt verarbeitet werden.&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;Standardwert: 1000&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p class=&amp;quot;mwt-heading&amp;quot; &amp;gt;&amp;#039;&amp;#039;&amp;#039;Beispiel:&amp;#039;&amp;#039;&amp;#039;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;max_input_vars = 2000&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p class=&amp;quot;mwt-heading&amp;quot; &amp;gt;&amp;#039;&amp;#039;&amp;#039;Empfehlung:&amp;#039;&amp;#039;&amp;#039;&amp;lt;/p&amp;gt;&lt;br /&gt;
*Erhöhen Sie diesen Wert bei großen Formularen oder umfangreichen Konfigurationsdaten.&lt;br /&gt;
*Ein zu hoher Wert kann die Performance negativ beeinflussen.&lt;br /&gt;
&amp;lt;h3 class=&amp;quot;mwt-heading&amp;quot; &amp;gt;display_errors&amp;lt;/h3&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;Legt fest, ob PHP-Fehlermeldungen direkt im Browser angezeigt werden.&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;Standardwert: Off (in Produktionsumgebungen)&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p class=&amp;quot;mwt-heading&amp;quot; &amp;gt;&amp;#039;&amp;#039;&amp;#039;Beispiel:&amp;#039;&amp;#039;&amp;#039;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;display_errors = On&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p class=&amp;quot;mwt-heading&amp;quot; &amp;gt;&amp;#039;&amp;#039;&amp;#039;Empfehlung:&amp;#039;&amp;#039;&amp;#039;&amp;lt;/p&amp;gt;&lt;br /&gt;
*In Entwicklungsumgebungen: Aktivieren Sie diese Option, um Fehler direkt zu sehen.&lt;br /&gt;
*In Produktionsumgebungen: Deaktivieren Sie diese Option, um sensible Informationen nicht preiszugeben.&lt;br /&gt;
&amp;lt;h3 class=&amp;quot;mwt-heading&amp;quot; &amp;gt;error_reporting&amp;lt;/h3&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;Definiert, welche Arten von Fehlern gemeldet werden sollen.&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;Standardwert: E_ALL (zeigt alle Fehler an)&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p class=&amp;quot;mwt-heading&amp;quot; &amp;gt;&amp;#039;&amp;#039;&amp;#039;Beispiel:&amp;#039;&amp;#039;&amp;#039;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;error_reporting = E_ALL &amp;amp; ~E_NOTICE&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p class=&amp;quot;mwt-heading&amp;quot; &amp;gt;&amp;#039;&amp;#039;&amp;#039;Empfehlung:&amp;#039;&amp;#039;&amp;#039;&amp;lt;/p&amp;gt;&lt;br /&gt;
*In Entwicklungsumgebungen: Nutzen Sie E_ALL, um alle Fehler zu sehen.&lt;br /&gt;
*In Produktionsumgebungen: Blenden Sie unnötige Meldungen aus, z. B. mit ~E_NOTICE.&lt;br /&gt;
&amp;lt;h3 class=&amp;quot;mwt-heading&amp;quot; &amp;gt;file_uploads&amp;lt;/h3&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;Legt fest, ob das Hochladen von Dateien in PHP erlaubt ist.&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;Standardwert: On&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p class=&amp;quot;mwt-heading&amp;quot; &amp;gt;&amp;#039;&amp;#039;&amp;#039;Beispiel:&amp;#039;&amp;#039;&amp;#039;&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p&amp;gt;file_uploads = On&amp;lt;/p&amp;gt;&lt;br /&gt;
&amp;lt;p class=&amp;quot;mwt-heading&amp;quot; &amp;gt;&amp;#039;&amp;#039;&amp;#039;Empfehlung:&amp;#039;&amp;#039;&amp;#039;&amp;lt;/p&amp;gt;&lt;br /&gt;
*Aktivieren Sie diese Option, wenn Ihre Anwendung Datei-Uploads unterstützt.&lt;br /&gt;
*Deaktivieren Sie sie, wenn keine Datei-Uploads benötigt werden, um die Sicherheit zu erhöhen.&lt;br /&gt;
[[file:SED501-Developer-Toolbar.png|600px|center|thumb|Web Developer Toolbar]]&lt;/div&gt;</summary>
		<author><name>Völkl Anna</name></author>
	</entry>
	<entry>
		<id>https://mediawiki.fernfh.ac.at/mediawiki/index.php?title=Datei:SED501-Developer-Toolbar.png&amp;diff=6559</id>
		<title>Datei:SED501-Developer-Toolbar.png</title>
		<link rel="alternate" type="text/html" href="https://mediawiki.fernfh.ac.at/mediawiki/index.php?title=Datei:SED501-Developer-Toolbar.png&amp;diff=6559"/>
		<updated>2025-03-07T20:38:52Z</updated>

		<summary type="html">&lt;p&gt;Völkl Anna: Chrome Developer Toolbar&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Beschreibung ==&lt;br /&gt;
Chrome Developer Toolbar&lt;/div&gt;</summary>
		<author><name>Völkl Anna</name></author>
	</entry>
	<entry>
		<id>https://mediawiki.fernfh.ac.at/mediawiki/index.php?title=Datei:SED501-Webserver-Requests.png&amp;diff=6558</id>
		<title>Datei:SED501-Webserver-Requests.png</title>
		<link rel="alternate" type="text/html" href="https://mediawiki.fernfh.ac.at/mediawiki/index.php?title=Datei:SED501-Webserver-Requests.png&amp;diff=6558"/>
		<updated>2025-03-07T20:38:15Z</updated>

		<summary type="html">&lt;p&gt;Völkl Anna: Ablauf einer Anfrage einer Webanwendung&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Beschreibung ==&lt;br /&gt;
Ablauf einer Anfrage einer Webanwendung&lt;/div&gt;</summary>
		<author><name>Völkl Anna</name></author>
	</entry>
	<entry>
		<id>https://mediawiki.fernfh.ac.at/mediawiki/index.php?title=Architektur-Design_f%C3%BCr_Webanwendungen&amp;diff=6557</id>
		<title>Architektur-Design für Webanwendungen</title>
		<link rel="alternate" type="text/html" href="https://mediawiki.fernfh.ac.at/mediawiki/index.php?title=Architektur-Design_f%C3%BCr_Webanwendungen&amp;diff=6557"/>
		<updated>2025-03-11T21:10:08Z</updated>

		<summary type="html">&lt;p&gt;Völkl Anna: Die Seite wurde neu angelegt: „== Einleitung == Dieses Studienheft vermittelt fortgeschrittene Konzepte im Bereich Web Software Engineering. Die behandelten Themen umfassen Architektur-Design, Versionskontrolle, DevOps, Testing und Performance-Optimierung.  Moderne Webanwendungen sind zunehmend komplex und erfordern ein tiefgehendes Verständnis von Softwarearchitektur, Skalierbarkeit, Deployment-Techniken und Sicherheitsstrategien. Dieses Studienheft soll einen umfassenden Überblick…“&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Einleitung ==&lt;br /&gt;
Dieses Studienheft vermittelt fortgeschrittene Konzepte im Bereich Web Software Engineering. Die behandelten Themen umfassen Architektur-Design, Versionskontrolle, DevOps, Testing und Performance-Optimierung. &lt;br /&gt;
Moderne Webanwendungen sind zunehmend komplex und erfordern ein tiefgehendes Verständnis von Softwarearchitektur, Skalierbarkeit, Deployment-Techniken und Sicherheitsstrategien. Dieses Studienheft soll einen umfassenden Überblick über diese Themen geben und dabei aktuelle Best Practices aufzeigen.&lt;br /&gt;
&lt;br /&gt;
== Einführung und Überblick ==&lt;br /&gt;
=== Bedeutung des Web Software Engineering ===&lt;br /&gt;
Das Web Software Engineering umfasst die Planung, Entwicklung, Bereitstellung und Wartung moderner Webanwendungen. Dabei spielen verschiedene Faktoren wie Architektur, Sicherheit, Skalierbarkeit und Performance eine entscheidende Rolle. Die Evolution des Internets hat die Anforderungen an Webanwendungen stetig erhöht, sodass Entwickler eine Vielzahl von Technologien und Best Practices nutzen müssen.&lt;br /&gt;
&lt;br /&gt;
Moderne Webanwendungen müssen hohe Anforderungen erfüllen, darunter:&lt;br /&gt;
==== Skalierbarkeit ====&lt;br /&gt;
Skalierbarkeit bezieht sich auf die Fähigkeit, die Leistung einer Anwendung proportional zur steigenden Last zu erhöhen. Dies ist von entscheidender Bedeutung, um eine reibungslose Benutzererfahrung sicherzustellen, auch wenn die Anzahl der Benutzer oder die Datenmenge stark ansteigt. Eine effektive Architektur muss sowohl horizontale als auch vertikale Skalierung ermöglichen.&lt;br /&gt;
Webanwendungen müssen so entwickelt werden, dass sie mit steigender Nutzerzahl und Anfragenlast wachsen können. Dies erfordert den Einsatz von Load Balancern, Caching-Techniken und verteilten Datenbanken. Ein Cloud-basiertes System kann beispielsweise über Auto-Scaling-Mechanismen automatisch Ressourcen hinzufügen, wenn die Last zunimmt.&lt;br /&gt;
&lt;br /&gt;
===== Horizontale Skalierung=====&lt;br /&gt;
Horizontale Skalierung bedeutet, dass die Anwendung durch das Hinzufügen weiterer Server erweitert wird. Jeder Server in diesem Szenario arbeitet unabhängig und kann die Last gemeinsam bewältigen. Load Balancer spielen hierbei eine entscheidende Rolle, indem sie den eingehenden Traffic gleichmäßig auf die verfügbaren Server verteilen. Diese Art der Skalierung ist besonders nützlich für Anwendungen, die eine hohe Verfügbarkeit benötigen, da der Ausfall eines Servers die Anwendung nicht vollständig beeinträchtigt.&lt;br /&gt;
&lt;br /&gt;
===== Vertikale Skalierung=====&lt;br /&gt;
Vertikale Skalierung hingegen umfasst die Aufrüstung der vorhandenen Server mit mehr Ressourcen wie CPU, Speicher oder Festplattenspeicher. Diese Methode ist einfacher zu implementieren, hat aber natürliche Grenzen, da es irgendwann nicht mehr möglich oder kosteneffizient ist, einen einzelnen Server weiter aufzurüsten.&lt;br /&gt;
&lt;br /&gt;
Die Wahl der Skalierungsstrategie hängt von den spezifischen Anforderungen der Anwendung, den Kostenüberlegungen und den erwarteten Wachstumsmustern ab. In vielen Fällen wird eine Kombination aus horizontaler und vertikaler Skalierung verwendet, um die Vorteile beider Ansätze zu nutzen.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Sicherheit ====&lt;br /&gt;
Ein weiterer kritischer Aspekt ist die Sicherheit. Webanwendungen sind anfällig für eine Vielzahl von Sicherheitsbedrohungen, die sich ständig weiterentwickeln. Diese Bedrohungen können zu Datenverlust, unbefugtem Zugriff auf sensible Informationen, Beeinträchtigungen der Verfügbarkeit der Anwendung und Reputationsschäden führen. Daher ist ein robustes Sicherheitskonzept unerlässlich, um sensible Daten zu schützen und die Integrität der Anwendung zu gewährleisten. &lt;br /&gt;
&lt;br /&gt;
* Sichere Authentifizierung und Autorisierung: Diese Prozesse stellen sicher, dass nur autorisierte Benutzer auf die Anwendung und ihre Ressourcen zugreifen können. Authentifizierung überprüft die Identität eines Benutzers (z. B. durch Benutzername und Passwort, Zwei-Faktor-Authentifizierung), während Autorisierung bestimmt, welche Aktionen ein authentifizierter Benutzer ausführen darf.&lt;br /&gt;
* Eingabevalidierung: Diese Technik verhindert, dass bösartige Eingaben (z. B. SQL-Injection, Cross-Site-Scripting) die Anwendung beeinträchtigen. Eingaben von Benutzern müssen immer auf ihre Gültigkeit und Sicherheit überprüft werden, bevor sie verarbeitet werden.&lt;br /&gt;
* Verschlüsselung von Daten: Daten sollten sowohl bei der Übertragung (z. B. mit HTTPS) als auch bei der Speicherung (z. B. Datenbankverschlüsselung) verschlüsselt werden, um sie vor unbefugtem Zugriff zu schützen.&lt;br /&gt;
* Sichere Session-Verwaltung: Sessions müssen sicher verwaltet werden, um Session-Hijacking und andere Angriffe zu verhindern.&lt;br /&gt;
* Regelmäßige Sicherheitsaudits und Penetrationstests: Diese Maßnahmen helfen, Schwachstellen in der Anwendung zu identifizieren und zu beheben, bevor sie von Angreifern ausgenutzt werden können.&lt;br /&gt;
&lt;br /&gt;
==== Performance ====&lt;br /&gt;
Die Performance einer Webanwendung ist ein entscheidender Faktor für die Benutzerzufriedenheit und den Erfolg der Anwendung. Benutzer erwarten schnelle Ladezeiten und eine reibungslose Benutzererfahrung. Langsame Anwendungen können zu Frustration, geringerer Benutzerbindung, höheren Absprungraten und letztendlich zu geschäftlichen Verlusten führen. Performance-Optimierung ist ein kontinuierlicher Prozess, der alle Ebenen der Anwendung betrifft, von der Datenbankabfrage bis zur Darstellung im Browser.&lt;br /&gt;
Es umfasst unter anderem auch Themen wie Bildoptimierung, minimierte JavaScript-Dateien und Content Delivery Networks (CDNs). &lt;br /&gt;
&lt;br /&gt;
===== Caching ===== &lt;br /&gt;
Caching ist eine der wichtigsten Techniken zur Verbesserung der Performance. Dabei werden häufig abgerufene Daten im Arbeitsspeicher oder auf der Festplatte gespeichert, um den Zugriff zu beschleunigen. Caching kann auf verschiedenen Ebenen eingesetzt werden:&lt;br /&gt;
* Browser-Caching: Statische Ressourcen wie Bilder, CSS-Dateien und JavaScript-Dateien können im Browser des Benutzers gespeichert werden, um sie bei nachfolgenden Besuchen der Seite nicht erneut herunterladen zu müssen.&lt;br /&gt;
* Server-Caching: Daten können auf dem Server im Arbeitsspeicher (z. B. mit Redis oder Memcached) oder auf der Festplatte gespeichert werden, um den Zugriff auf die Datenbank zu reduzieren.&lt;br /&gt;
* Content Delivery Networks (CDNs): CDNs sind verteilte Netzwerke von Servern, die statische Inhalte wie Bilder, CSS-Dateien und JavaScript-Dateien auf Servern in der Nähe der Benutzer speichern und bereitstellen. Dies reduziert die Latenz und verbessert die Ladezeiten.&lt;br /&gt;
&lt;br /&gt;
===== Datenbankabfragen ===== &lt;br /&gt;
Optimierung von Datenbankabfragen: Ineffiziente Datenbankabfragen können ein erheblicher Engpass für die Performance sein. Es ist wichtig, Datenbankabfragen zu optimieren, um die Anzahl der Abfragen zu minimieren, die Menge der abgerufenen Daten zu reduzieren und die Indizierung effektiv zu nutzen.&lt;br /&gt;
&lt;br /&gt;
===== HTTP Anfragen ===== &lt;br /&gt;
Minimierung von HTTP-Anfragen: Jede HTTP-Anfrage verursacht Overhead. Es ist wichtig, die Anzahl der HTTP-Anfragen zu minimieren, indem man z. B. CSS- und JavaScript-Dateien zusammenfasst und Bilder optimiert.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Wartbarkeit ====&lt;br /&gt;
Schließlich ist die Wartbarkeit ein wichtiger Aspekt des Architekturdesigns. Webanwendungen sind oft langfristige Projekte, die regelmäßig aktualisiert und erweitert werden müssen, um neue Funktionen hinzuzufügen, Fehler zu beheben und sich an sich ändernde Anforderungen anzupassen. Eine modulare und gut strukturierte Architektur erleichtert die Wartung und reduziert das Risiko von Fehlern.&lt;br /&gt;
&lt;br /&gt;
* Modularität: Die Anwendung sollte in unabhängige Module oder Komponenten unterteilt werden, die jeweils eine spezifische Funktion erfüllen. Dies erleichtert die Entwicklung, das Testen und die Wartung der Anwendung.&lt;br /&gt;
* Lose Kopplung: Module sollten so wenig wie möglich voneinander abhängig sein. Dies ermöglicht es, Module unabhängig voneinander zu ändern oder zu ersetzen, ohne andere Teile der Anwendung zu beeinträchtigen.&lt;br /&gt;
* Hohe Kohäsion: Jedes Modul sollte eine klare und spezifische Funktion haben. Alle Elemente innerhalb eines Moduls sollten eng miteinander verbunden sein und zur Erfüllung dieser Funktion beitragen.&lt;br /&gt;
* Geeignete Programmiersprachen und Frameworks: Die Wahl der richtigen Programmiersprachen und Frameworks kann die Wartbarkeit der Anwendung erheblich beeinflussen. Es ist wichtig, Sprachen und Frameworks zu wählen, die gut dokumentiert sind, eine große Community haben und die Entwicklung von modularen und wartbaren Anwendungen unterstützen.&lt;br /&gt;
* Umfassende Dokumentation: Eine gute Dokumentation ist unerlässlich für die Wartbarkeit einer Anwendung. Die Dokumentation sollte die Architektur der Anwendung, die Funktion der einzelnen Module, die APIs und andere wichtige Informationen beschreiben.&lt;br /&gt;
* Automatisierte Tests: Automatisierte Tests (z. B. Unit-Tests, Integrationstests, End-to-End-Tests) helfen, Fehler frühzeitig zu erkennen und sicherzustellen, dass Änderungen an der Anwendung keine unerwarteten Nebenwirkungen haben.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Architektur-Design für Webanwendungen ==&lt;br /&gt;
=== Hosting-Modelle ===&lt;br /&gt;
Die Wahl des richtigen Hosting-Modells hat einen entscheidenden Einfluss auf die Kosten, Leistung und Skalierbarkeit einer Webanwendung. Es gibt verschiedene Hosting-Modelle, die jeweils ihre eigenen Vor- und Nachteile haben.&lt;br /&gt;
&lt;br /&gt;
==== On-Premise (Root-Server) ====&lt;br /&gt;
Diese traditionelle Hosting-Methode bietet die vollständige Kontrolle über die Hardware und die Konfiguration der Server. Unternehmen, die sich für On-Premise entscheiden, sind für Wartung, Sicherheitsmaßnahmen und Infrastrukturverwaltung selbst verantwortlich. Dies bietet den Vorteil einer maßgeschneiderten Konfiguration und Datenschutzkontrolle, erfordert aber erheblichen personellen und finanziellen Aufwand. Ein großes Unternehmen mit hohen Sicherheitsanforderungen kann beispielsweise ein eigenes Rechenzentrum betreiben, um vollständige Kontrolle über seine Daten zu behalten.&lt;br /&gt;
&lt;br /&gt;
* Vorteile: Volle Kontrolle über die Infrastruktur, hohe Sicherheit (bei richtiger Konfiguration), Möglichkeit zur Anpassung an spezifische Anforderungen.&lt;br /&gt;
* Nachteile: Hohe Kosten für Hardware, Software, Personal und Wartung, Verantwortung für die gesamte Infrastruktur, begrenzte Skalierbarkeit (im Vergleich zu Cloud-Lösungen).&lt;br /&gt;
&lt;br /&gt;
==== Cloud Computing ====&lt;br /&gt;
Cloud-Dienste ermöglichen eine flexible Skalierung und eine vereinfachte Verwaltung der Infrastruktur. Anbieter wie Amazon Web Services (AWS), Microsoft Azure oder Google Cloud bieten unterschiedliche Lösungen für Infrastructure-as-a-Service (IaaS), Platform-as-a-Service (PaaS) und Software-as-a-Service (SaaS). Die Vorteile der Cloud sind unter anderem reduzierte Wartungskosten, hohe Skalierbarkeit und globale Verfügbarkeit. Beispielsweise kann ein Start-up mit schwankendem Nutzeraufkommen seine Serverkapazitäten dynamisch anpassen, um Kosten zu sparen.&lt;br /&gt;
&lt;br /&gt;
* Vorteile: Hohe Skalierbarkeit und Flexibilität, Kosteneffizienz (Pay-as-you-go-Modelle), Reduzierung der Kosten für die Infrastrukturverwaltung, Zugriff auf eine Vielzahl von Diensten und Ressourcen.&lt;br /&gt;
* Nachteile: Weniger Kontrolle über die Infrastruktur (im Vergleich zu On-Premise), Abhängigkeit vom Cloud-Anbieter, potenzielle Sicherheitsrisiken (bei Fehlkonfigurationen).&lt;br /&gt;
&lt;br /&gt;
==== Serverless Computing ==== &lt;br /&gt;
Bei Serverless Computing werden Ressourcen nur bei Bedarf bereitgestellt, wodurch Kosten gespart und die Skalierbarkeit verbessert wird. Serverless-Architekturen nutzen Dienste wie AWS Lambda oder Google Cloud Functions, um einzelne Funktionen auszuführen, ohne dass ein kompletter Server dauerhaft laufen muss. Ein typisches Beispiel wäre eine serverlose API, die nur aktiv wird, wenn eine Anfrage eingeht, anstatt durchgehend einen Server bereitzustellen.&lt;br /&gt;
&lt;br /&gt;
* Vorteile: Extreme Skalierbarkeit, Kosteneffizienz (Pay-per-use-Modelle), keine Serververwaltung erforderlich, schnelle Entwicklung und Bereitstellung.&lt;br /&gt;
* Nachteile: Begrenzte Kontrolle über die Umgebung, potenzielle Vendor-Lock-in, Komplexität bei der Fehlersuche und beim Debugging, Einschränkungen bei der Laufzeit und den verfügbaren Ressourcen.&lt;br /&gt;
&lt;br /&gt;
==== Verteilte Architekturen ==== &lt;br /&gt;
Verteilte Architekturen ermöglichen es, die Last einer Webanwendung auf mehrere Server zu verteilen. Dies erhöht die Skalierbarkeit, Ausfallsicherheit und Performance der Anwendung.&lt;br /&gt;
* Microservices-Architektur: Eine Architektur, bei der die Anwendung in kleine, unabhängige Services unterteilt wird, die jeweils eine spezifische Funktion erfüllen.&lt;br /&gt;
* Containerisierung und Orchestrierung: Technologien wie Docker und Kubernetes ermöglichen es, Anwendungen in Containern zu verpacken und diese Container auf mehreren Servern zu orchestrieren.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Datenbanken und Caching ===&lt;br /&gt;
Die Wahl der richtigen Datenbank ist entscheidend für die Leistung und Skalierbarkeit einer Webanwendung. Es gibt verschiedene Arten von Datenbanken, die jeweils ihre eigenen Stärken und Schwächen haben.&lt;br /&gt;
&lt;br /&gt;
====  SQL-Datenbanken==== &lt;br /&gt;
Relationale Datenbanken (RDBMS) wie MySQL, PostgreSQL und MariaDB nutzen strukturierte Tabellen, um Daten zu speichern. Sie bieten ACID-Transaktionen (Atomicity, Consistency, Isolation, Durability) und eignen sich für Anwendungen mit klar definierten Datenstrukturen. Ein Online-Shop mit einem Inventarsystem könnte beispielsweise eine SQL-Datenbank verwenden, um Produktinformationen und Bestellungen effizient zu verwalten.&lt;br /&gt;
&lt;br /&gt;
* Vorteile: Hohe Datenintegrität, ACID-Eigenschaften (Atomicity, Consistency, Isolation, Durability), gut geeignet für strukturierte Daten und komplexe Abfragen.&lt;br /&gt;
* Nachteile: Schwieriger zu skalieren (im Vergleich zu NoSQL-Datenbanken), weniger flexibel bei der Speicherung unstrukturierter Daten.&lt;br /&gt;
&lt;br /&gt;
==== NoSQL-Datenbanken==== &lt;br /&gt;
NoSQL-Datenbanken wie MongoDB und CouchDB sind schemalos und ermöglichen eine flexible Speicherung von Daten. Diese Datenbanken sind besonders für große, verteilte Systeme und Anwendungen mit dynamischen Datenstrukturen geeignet. Ein Social-Media-Netzwerk könnte beispielsweise NoSQL verwenden, um Nutzerprofile, Posts und Interaktionen effizient zu speichern.&lt;br /&gt;
&lt;br /&gt;
* Vorteile: Hohe Skalierbarkeit und Flexibilität, gut geeignet für unstrukturierte oder semistrukturierte Daten, hohe Verfügbarkeit.&lt;br /&gt;
* Nachteile: Geringere Datenintegrität (im Vergleich zu RDBMS), weniger Unterstützung für komplexe Abfragen, ACID-Eigenschaften sind nicht immer garantiert.&lt;br /&gt;
&lt;br /&gt;
==== In-Memory-Caches==== &lt;br /&gt;
Caching verbessert die Leistung einer Webanwendung erheblich, indem häufig abgerufene Daten zwischengespeichert werden. Lösungen wie Redis und Memcached speichern Daten im Arbeitsspeicher und reduzieren so die Last auf die Datenbank. Ein Beispiel wäre ein Nachrichtenportal, das häufig gelesene Artikel in Redis speichert, um die Ladezeiten zu minimieren und die Serverlast zu reduzieren.&lt;br /&gt;
&lt;br /&gt;
==== Verteilte Datenbankarchitekturen==== &lt;br /&gt;
Verteilte Datenbanken ermöglichen es, große Datenmengen effizient über mehrere Server hinweg zu speichern und zu verarbeiten. Sie sind besonders für Anwendungen mit globaler Reichweite von Vorteil. Systeme wie Google Spanner, Cassandra oder CockroachDB bieten horizontale Skalierbarkeit und hohe Verfügbarkeit. Ein Online-Shop mit Millionen von Nutzern weltweit könnte eine verteilte Datenbank verwenden, um schnelle Reaktionszeiten unabhängig vom Standort der Nutzer sicherzustellen.&lt;/div&gt;</summary>
		<author><name>Völkl Anna</name></author>
	</entry>
	<entry>
		<id>https://mediawiki.fernfh.ac.at/mediawiki/index.php?title=Einf%C3%BChrung_in_PHP_und_Webentwicklung&amp;diff=6556</id>
		<title>Einführung in PHP und Webentwicklung</title>
		<link rel="alternate" type="text/html" href="https://mediawiki.fernfh.ac.at/mediawiki/index.php?title=Einf%C3%BChrung_in_PHP_und_Webentwicklung&amp;diff=6556"/>
		<updated>2025-03-07T21:30:54Z</updated>

		<summary type="html">&lt;p&gt;Völkl Anna: Initiale Version&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=== Webentwicklung: Grundlagen und Architektur ===&lt;br /&gt;
==== Das Client-Server-Modell ====&lt;br /&gt;
Das Client-Server-Modell ist eine grundlegende Architektur der modernen Webentwicklung. Es beschreibt die Interaktion zwischen zwei Parteien: einem Client, der eine Anfrage stellt, und einem Server, der auf diese Anfrage antwortet.&lt;br /&gt;
Ein Server stellt Dienste, Ressourcen oder Informationen bereit. Im Kontext der Webentwicklung ist ein Webserver ein Programm, das Webseiten, APIs oder andere Inhalte an Clients ausliefert. Beispiele für Webserver-Software sind Apache, Nginx und IIS (Internet Information Services).&lt;br /&gt;
Ein Client ist ein Gerät oder eine Anwendung, die diese Ressourcen vom Server anfordert. Dies kann ein Webbrowser wie Chrome, Firefox oder Microsoft Edge sein, aber auch eine mobile App oder ein anderes Programm, das über das Internet kommuniziert.&lt;br /&gt;
Ein Beispiel aus dem Alltag: Wenn Sie eine Webseite in Ihrem Browser aufrufen, fungiert der Browser als Client. Der Webserver, der die Inhalte dieser Webseite hostet, ist der Server. Der Browser schickt eine Anfrage (Request) an den Server, und der Server antwortet mit der angeforderten Seite (Response).&lt;br /&gt;
==== Der Request-Response-Zyklus ====&lt;br /&gt;
Der Request-Response-Zyklus beschreibt die Kommunikation zwischen Client und Server in einem Websystem. Dieser Zyklus läuft in folgenden Schritten ab:&lt;br /&gt;
Client sendet eine Anfrage (Request): Der Benutzer gibt eine URL in den Browser ein oder klickt auf einen Link. Der Browser (Client) erstellt eine HTTP-Anfrage an den entsprechenden Server.&lt;br /&gt;
Server verarbeitet die Anfrage: Der Server empfängt die Anfrage, verarbeitet sie und sucht nach den angeforderten Ressourcen (z. B. HTML-Dateien, Bilder, Datenbankinhalte).&lt;br /&gt;
Server sendet eine Antwort (Response): Nachdem die Anfrage verarbeitet wurde, sendet der Server eine HTTP-Antwort zurück an den Client. Diese Antwort enthält den Statuscode (z. B. 200 OK oder 404 Not Found) und die angeforderten Inhalte.&lt;br /&gt;
Client zeigt die Antwort an: Der Browser des Clients interpretiert die empfangenen Inhalte und zeigt die Webseite an.&lt;br /&gt;
Beispiel:&lt;br /&gt;
Der Client (Browser) fordert die Webseite https://www.fernfh.ac.at an.&lt;br /&gt;
Der Server empfängt die Anfrage, der Webserver verarbeitet die Anfrage, sucht die passende Datei und schickt diese zurück.&lt;br /&gt;
Der Browser zeigt die Webseite an.&lt;br /&gt;
&lt;br /&gt;
==== Bestandteile einer Webanwendung ====&lt;br /&gt;
===== Webserver =====&lt;br /&gt;
Ein Webserver ist eine Software, die HTTP-Anfragen verarbeitet und Webseiten oder andere Ressourcen bereitstellt. Die beiden am häufigsten verwendeten Webserver sind Apache und Nginx:&lt;br /&gt;
Apache: Ein weit verbreiteter Open-Source-Webserver, der für seine Flexibilität und umfangreiche Konfigurationsmöglichkeiten bekannt ist. Er unterstützt Module zur Erweiterung von Funktionen wie URL-Rewrites oder Authentifizierung.&lt;br /&gt;
Nginx: Ein moderner, leistungsstarker Webserver, der für seine hohe Geschwindigkeit und geringe Ressourcenbelastung geschätzt wird. Nginx eignet sich besonders gut für den Einsatz als Reverse Proxy und zur Lastverteilung.&lt;br /&gt;
Beide Serverprogramme können sowohl statische Inhalte (HTML, CSS, Bilder) als auch dynamische Inhalte (PHP-Seiten, APIs) bereitstellen.&lt;br /&gt;
===== Datenbanken =====&lt;br /&gt;
Webserver arbeiten oft mit Datenbanken zusammen, um dynamische Inhalte bereitzustellen. Während der Webserver die Anfrage verarbeitet, ruft er oft zusätzliche Daten aus einer Datenbank ab. Diese Datenbanken speichern Informationen wie Benutzerdaten, Produktkataloge oder Artikel und ermöglichen es, Webseiteninhalte dynamisch zu generieren.&lt;br /&gt;
Zu den gängigen Datenbankmanagementsystemen zählen:&lt;br /&gt;
MySQL/MariaDB: Häufig in Kombination mit PHP verwendet, besonders im E-Commerce.&lt;br /&gt;
PostgreSQL: Ein leistungsstarkes Open-Source-System mit erweiterten Funktionen.&lt;br /&gt;
SQLite: Eine leichtgewichtige Datenbank für kleinere Projekte.&lt;br /&gt;
Ein typisches Szenario in einer Webanwendung ist:&lt;br /&gt;
Der Client fordert eine Produktseite an.&lt;br /&gt;
Der Webserver verarbeitet die Anfrage.&lt;br /&gt;
Der Webserver ruft die Produktinformationen aus der Datenbank ab.&lt;br /&gt;
Der Server generiert die HTML-Seite mit den Daten und sendet sie an den Client zurück.&lt;br /&gt;
Das Zusammenspiel zwischen Webservern und Datenbanken ermöglicht es, große, dynamische E-Commerce-Plattformen wie Magento effizient zu betreiben.&lt;br /&gt;
1.1.2.3 Web-Anwendung&lt;br /&gt;
Der Kern der Anwendung, oft mit Frameworks wie Laravel, Symfony oder Magento entwickelt.&lt;br /&gt;
&lt;br /&gt;
===== Erweiterte Funktionen =====&lt;br /&gt;
&lt;br /&gt;
;OpCache&lt;br /&gt;
:Der PHP OPcache ist ein Caching-Mechanismus, der bereits kompilierte PHP-Skripte im Speicher hält, um die Ausführungszeit zu verkürzen und die Serverlast zu reduzieren. Dadurch wird vermieden, dass PHP-Dateien bei jedem Request erneut geparst und kompiliert werden, was die Performance von Webanwendungen erheblich steigert.&lt;br /&gt;
;CDN&lt;br /&gt;
:Verteilt statische Inhalte (Bilder, CSS, JS) auf mehrere Server weltweit, um die Ladezeiten zu verbessern.&lt;br /&gt;
;Varnish&lt;br /&gt;
:Reverse Proxy und Caching-Lösung, die dynamische Inhalte zwischenspeichern kann, um die Performance zu verbessern.&lt;br /&gt;
;Redis&lt;br /&gt;
:In-Memory-Datenbank, die als Cache, Session-Store oder zur Message-Queue verwendet wird.&lt;br /&gt;
;Memcached&lt;br /&gt;
:Einfacher In-Memory-Cache, um häufig genutzte Daten schneller bereitzustellen.&lt;br /&gt;
;Load Balancer&lt;br /&gt;
:Verteilt eingehende Anfragen auf mehrere Server, um Last zu verteilen und Ausfallsicherheit zu gewährleisten. Beispiele: HAProxy, AWS Elastic Load Balancing.&lt;br /&gt;
;Queue-System&lt;br /&gt;
:Verarbeitet Aufgaben asynchron, um die Hauptanwendung zu entlasten. Beispiele: RabbitMQ, Kafka.&lt;br /&gt;
;API Gateway&lt;br /&gt;
:Bietet eine zentrale Schnittstelle für externe APIs und sorgt für Sicherheit, Monitoring und Load Balancing. Beispiele: Kong, Apigee.&lt;br /&gt;
;Service Worker&lt;br /&gt;
:Wird im Browser verwendet, um Offline-Funktionalität und Caching zu ermöglichen. Besonders wichtig für Progressive Web Apps (PWAs).&lt;br /&gt;
;Monitoring &amp;amp; Logging&lt;br /&gt;
:Überwacht die Performance der Anwendung und speichert Logs. Beispiele: Grafana, Kibana, Prometheus.&lt;br /&gt;
;Firewall/WAF (Web Application Firewall)&lt;br /&gt;
:Schützt die Webanwendung vor Angriffen wie SQL-Injection, XSS und anderen Webbedrohungen. Beispiele: AWS WAF, Cloudflare WAF.&lt;br /&gt;
;Container &amp;amp; Orchestrierung&lt;br /&gt;
:Container-Technologien (z. B. Docker) und Orchestrierungstools (z. B. Kubernetes) ermöglichen eine flexible und skalierbare Bereitstellung.&lt;br /&gt;
;CI/CD-Pipeline&lt;br /&gt;
:Automatisiert den Build- und Deployment-Prozess. Beispiele: GitLab CI, Jenkins, GitHub Actions.&lt;br /&gt;
;Identity Provider (IdP)&lt;br /&gt;
:Verwaltet Benutzeridentitäten und ermöglicht Single Sign-On (SSO). Beispiele: Keycloak, Okta, Auth0.&lt;br /&gt;
;Message Broker&lt;br /&gt;
:Vermittelt Nachrichten zwischen verschiedenen Diensten. Beispiele: RabbitMQ, Apache Kafka.&lt;br /&gt;
;Edge Functions&lt;br /&gt;
:Kleine Funktionen, die nahe am Client ausgeführt werden, um die Latenz zu verringern. Beispiele: Cloudflare Workers, AWS Lambda@Edge.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== HTTP und HTTPS ===&lt;br /&gt;
Das Hypertext Transfer Protocol (HTTP) ist das Fundament der Kommunikation im World Wide Web. Es beschreibt, wie Clients (z. B. Webbrowser) mit Servern interagieren, um Webseiten und andere Ressourcen abzurufen. HTTPS ist die sichere Version von HTTP und nutzt Verschlüsselungstechnologien wie SSL/TLS, um die Kommunikation vor Manipulation und Abhören zu schützen.&lt;br /&gt;
==== Das Hypertext Transfer Protocol ====&lt;br /&gt;
HTTP ist ein textbasiertes Protokoll, das den Austausch von Informationen zwischen einem Client und einem Server regelt. Es arbeitet nach dem Request-Response-Prinzip: Der Client stellt eine Anfrage (Request), und der Server liefert eine Antwort (Response) zurück.&lt;br /&gt;
Merkmale von HTTP:&lt;br /&gt;
Verbindungslos: Jede Anfrage wird unabhängig behandelt. Es gibt keine dauerhafte Verbindung zwischen Client und Server.&lt;br /&gt;
Statusbasiert: Der Server antwortet immer mit einem Statuscode, der den Erfolg oder Misserfolg der Anfrage angibt.&lt;br /&gt;
Anwendungsprotokoll: HTTP wird auf der Anwendungsschicht genutzt und baut auf Protokollen wie TCP/IP auf.&lt;br /&gt;
Ein typisches Beispiel für eine HTTP-Anfrage ist das Abrufen einer Webseite. Wenn ein Benutzer eine URL in den Browser eingibt, sendet der Browser eine HTTP-Anfrage an den Server, der die gewünschte Seite bereitstellt.&lt;br /&gt;
===== HTTP-Methoden =====&lt;br /&gt;
HTTP bietet verschiedene Methoden, die angeben, welche Art von Aktion der Client auf dem Server ausführen möchte. Die vier wichtigsten Methoden sind:&lt;br /&gt;
&lt;br /&gt;
====== GET ======&lt;br /&gt;
Wird verwendet, um Daten vom Server abzurufen.&lt;br /&gt;
Hat keine Nebenwirkungen auf den Server (idempotent).&lt;br /&gt;
Beispiel: Abrufen einer Webseite oder eines Bildes.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;GET /index.html HTTP/1.1&amp;lt;br&amp;gt;Host: example.com&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
====== POST ======&lt;br /&gt;
Wird verwendet, um Daten an den Server zu senden (z. B. Formulareinsendungen).&lt;br /&gt;
Kann neue Ressourcen erstellen oder bestehende aktualisieren.&lt;br /&gt;
Beispiel: Ein Benutzer sendet ein Kontaktformular ab.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;POST /contact HTTP/1.1&amp;lt;br&amp;gt;Host: example.com&amp;lt;br&amp;gt;Content-Type: application/x-www-form-urlencoded&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;name=John&amp;amp;message=Hello&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
====== PUT ======&lt;br /&gt;
Wird verwendet, um eine Ressource auf dem Server zu erstellen oder zu ersetzen.&lt;br /&gt;
Beispiel: Hochladen oder Aktualisieren einer Datei.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;PUT /file.txt HTTP/1.1&lt;br /&gt;
&lt;br /&gt;
Host: example.com&lt;br /&gt;
&lt;br /&gt;
Content-Type: text/plain&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;This is a file content.&amp;lt;/pre&amp;gt;&lt;br /&gt;
====== DELETE ======&lt;br /&gt;
Wird verwendet, um eine Ressource vom Server zu löschen.&lt;br /&gt;
Beispiel: Löschen einer Datei oder eines Eintrags.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;DELETE /file.txt HTTP/1.1&lt;br /&gt;
&lt;br /&gt;
Host: example.com&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Andere wichtige Methoden sind HEAD, OPTIONS und PATCH, die je nach Anwendungsszenario genutzt werden.&lt;br /&gt;
&lt;br /&gt;
===== HTTP Statuscodes =====&lt;br /&gt;
HTTP-Statuscodes sind dreistellige Zahlen, die angeben, wie der Server die Anfrage des Clients verarbeitet hat  &amp;lt;ref&amp;gt;HTTP response status codes&amp;lt;/ref&amp;gt;  (https://developer.mozilla.org/en-US/docs/Web/HTTP/Status) . Sie sind in fünf Kategorien unterteilt:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
1xx Informationelle Antworten&lt;br /&gt;
&lt;br /&gt;
2xx Erfolgreiche Anfragen&lt;br /&gt;
&lt;br /&gt;
3xx Umleitungen&lt;br /&gt;
&lt;br /&gt;
4xx Clientfehler&lt;br /&gt;
&lt;br /&gt;
5xx Serverfehler&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Wichtige Statuscodes:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
200 OK: Die Anfrage war erfolgreich. Die angeforderten Daten werden zurückgegeben.&lt;br /&gt;
&lt;br /&gt;
301 Moved Permanently: Die angeforderte Ressource wurde dauerhaft an eine neue URL verschoben.&lt;br /&gt;
&lt;br /&gt;
302 Found: Temporäre Weiterleitung.&lt;br /&gt;
&lt;br /&gt;
404 Not Found: Die angeforderte Ressource wurde nicht gefunden.&lt;br /&gt;
&lt;br /&gt;
500 Internal Server Error: Ein allgemeiner Fehler auf dem Server.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
===== Bedeutung von HTTPS und SSL/TLS =====&lt;br /&gt;
====== HTTPS (Hypertext Transfer Protocol Secure) ======&lt;br /&gt;
HTTPS ist die sichere Version von HTTP. Es verwendet Verschlüsselung, um die Kommunikation zwischen Client und Server vor Abhören und Manipulation zu schützen. Die Verschlüsselung erfolgt mithilfe von SSL (Secure Sockets Layer) bzw. TLS (Transport Layer Security).&lt;br /&gt;
Vorteile von HTTPS:&lt;br /&gt;
Vertraulichkeit: Die übertragenen Daten sind verschlüsselt und können nicht von Dritten eingesehen werden.&lt;br /&gt;
Integrität: Es wird sichergestellt, dass die Daten während der Übertragung nicht manipuliert wurden.&lt;br /&gt;
Authentizität: Der Client kann sicherstellen, dass er mit dem richtigen Server kommuniziert.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Webserver ====&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;&amp;lt;code&amp;gt;[[SED501-Webserver-Requests.png]]&amp;lt;/code&amp;gt;&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
==== Tools ====&lt;br /&gt;
==== Web Developer Toolbar ====&lt;br /&gt;
Die Chrome Developer Toolbar sind ein mächtes Werkzeug für Entwickler*innen innerhalb des Chrome-Browsers.&amp;amp;nbsp;&lt;br /&gt;
===== Webserver erkennen =====&lt;br /&gt;
Sofern der Webservername im Respone Header mitgeschickt wird, ist dieser im &amp;quot;Netzwerk&amp;quot; Tab nach Auswahl einer Quelle (Hauptdokument, Bild, CSS oder JavaScript-Datei, die direkt vom Server geladen wird) erkennbar.&lt;br /&gt;
&lt;br /&gt;
Oftmals wird der Webserver auch entfernt und nicht im Response Header mitgeschickt, um die Angriffsoberfläche für Schwachstellen zu minimieren.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;&amp;lt;code&amp;gt;[[SED501-Developer-Toolbar.png]]&amp;lt;/code&amp;gt;&amp;#039;&amp;#039;&amp;#039;&lt;/div&gt;</summary>
		<author><name>Völkl Anna</name></author>
	</entry>
	<entry>
		<id>https://mediawiki.fernfh.ac.at/mediawiki/index.php?title=Software_Entwicklung&amp;diff=6555</id>
		<title>Software Entwicklung</title>
		<link rel="alternate" type="text/html" href="https://mediawiki.fernfh.ac.at/mediawiki/index.php?title=Software_Entwicklung&amp;diff=6555"/>
		<updated>2025-03-06T23:16:42Z</updated>

		<summary type="html">&lt;p&gt;Völkl Anna: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[Frontend Development in 2024 - Gesamt|Frontend Development]]&lt;br /&gt;
&lt;br /&gt;
[[Web Software Quality Assurance|Web Software Quality Assurance]]&lt;br /&gt;
&lt;br /&gt;
[[Web Development|Web Development]]&lt;br /&gt;
&lt;br /&gt;
[[Advanced Topics in Web Software Engineering|Advanced Topics in Web Software Engineering]]&lt;/div&gt;</summary>
		<author><name>Völkl Anna</name></author>
	</entry>
</feed>