Im Internet kursieren viele veraltete Informationen, die PHP-Anfänger verwirren und so zu schlechten Praktiken und unsicherem Code führen. PHP - aber richtig ist ein leicht verständliches Nachschlagewerk zu gängigen PHP-Programmierstandards, Links zu maßgeblichen Tutorials im Internet und Informationen zu den aktuellen Best-Practices der Autoren.
Es gibt keine allgemeingültige Herangehensweise PHP zu verwenden. Diese Website möchte PHP-Anfängern Themen näherbringen, die sie möglicherweise erst entdecken, wenn es zu spät ist, und erfahrenen Profis neue Ideen zu Themen vermitteln, die sie seit Jahren unreflektiert bearbeiten. Diese Website gibt Dir keine Anweisungen zu den zu verwendenden Tools, sondern bietet Vorschläge für verschiedene Optionen und erläutert nach Möglichkeit die Unterschiede in Ansatz und Anwendungsfall.
Dieses Dokument wird fortlaufend aktualisiert und mit hilfreichen Informationen und Beispielen ergänzt, sobald diese verfügbar sind.
Das Original, PHP the right way wurde in viele Sprachen übersetzt:
Diese Übersetzung ins Deutsche wurde mit Hilfe translate.google.com von Henrik Pantle aka skipper-henrik besorgt. Verbesserungen und Vorschläge bitte via github.
Die neueste Version von PHP - aber richtig ist in der englischen Originalversion als PHP: The Right Way auch in den Formaten PDF, EPUB und MOBI verfügbar. Zu Leanpub
Hilf mit, diese Website zur besten Ressource für neue PHP-Programmierer zu machen! Contribute on GitHub
Wenn Du PHP neu ausprobieren möchtest, starte mit der aktuellen stabilen Version PHP 8.4. PHP 8.x bietet gegenüber den älteren Versionen 7.x und 5.x viele neue Funktionen. Die Engine wurde weitgehend neu entwickelt, und PHP ist jetzt noch schneller als ältere Versionen. PHP 8 ist ein umfassendes Update der Sprache und enthält viele neue Funktionen und Optimierungen.
Du solltest versuchen, schnellstmöglich auf die neueste stabile Version zu aktualisieren – PHP 7.4 ist bereits veraltet. Das Upgrade ist einfach, da es kaum Probleme mit der Abwärtskompatibilität gibt PHP 8.0, PHP 8.1, PHP 8.2, PHP 8.3, PHP 8.4. Wenn Du nicht sicher bist, in welcher Version eine Funktion oder ein Feature enthalten ist, kannst Du die PHP-Dokumentation auf der Website php.net einsehen.
Mit PHP 5.4 oder neuer kannst Du PHP lernen, ohne einen kompletten, vollwertigen Webserver installieren und konfigurieren zu müssen. Um den Server zu starten, führe den folgenden Befehl im Terminal aus dem Root-Verzeichnis Deines Projekts aus:
> php -S localhost:8000
macOS 12 (Monterey) und neuere Versionen enthalten kein vorinstalliertes PHP mehr. Frühere macOS-Versionen enthalten PHP, liegen aber hinter der neuesten stabilen Version zurück. Es gibt mehrere Möglichkeiten, die neueste PHP-Version unter macOS zu installieren.
Homebrew ist ein Paketmanager für macOS, mit dem Du PHP und verschiedene Erweiterungen einfach installieren kannst. Das Homebrew-Core-Repository bietet „Formeln“ für PHP 8.1, 8.2, 8.3 und 8.4. Installiere die neueste Version mit diesem Befehl:
brew install php
Du kannst zwischen Homebrew-PHP-Versionen wechseln, indem Du Ihre PATH
Variable änderst. Alternativ kannst Du brew-php-switcher, um PHP-Versionen automatisch zu wechseln.
Du kannst auch manuell zwischen PHP-Versionen wechseln, indem Du die Verknüpfung aufhebst und die gewünschte Version verknüpfst:
brew unlink php
brew link --overwrite php@8.2
brew unlink php
brew link --overwrite php@8.3
Das MacPorts-Projekt ist eine Open-Source-Community-Initiative zur Entwicklung eines benutzerfreundlichen Systems zum Kompilieren, Installieren und Aktualisieren von Open-Source-Software auf Befehlszeilen-, X11- oder Aqua-Basis auf dem macOS-Betriebssystem.
MacPorts unterstützt vorkompilierte Binärdateien, sodass Du nicht jede Abhängigkeit aus den Quell-Tarball-Dateien neu kompilieren musst. Dies rettet Dein Leben, wenn auf Deinem System kein Paket installiert ist.
An diesem Punkt kannst Du php54
, php55
, php56
, php70
, php71
, php72
, php73
, php74
, php80
, php81
, php82
oder php83
mit dem Befehl port install
installieren, zum Beispiel:
sudo port install php74
sudo port install php83
Und Du kannst den select
Befehl ausführen, um Dein aktives PHP zu wechseln:
sudo port select --set php php83
phpbrew ist ein Tool zur Installation und Verwaltung mehrerer PHP-Versionen. Dies ist besonders nützlich, wenn zwei verschiedene Anwendungen/Projekte unterschiedliche PHP-Versionen erfordern und Du keine virtuellen Maschinen verwendest.
Eine weitere beliebte Option ist php-osx.liip.ch, das einfache Installationsmethoden für die Versionen 5.3 bis 7.3 bietet. Die von Apple installierten PHP-Binärdateien werden dabei nicht überschrieben, sondern alles an einem separaten Ort (/usr/local/php5) installiert.
Eine weitere Möglichkeit, die installierte PHP-Version zu kontrollieren, besteht darin, sie selbst zu kompilieren. Stelle in diesem Fall sicher, dass Du entweder Xcode oder Apples Ersatz “Command Line Tools for XCode” installiert hast, der im Apple Developer Center heruntergeladen werden kann.
Die oben aufgeführten Lösungen verarbeiten hauptsächlich PHP selbst und bieten keine Sachen wie Apache, Nginx oder einen SQL-Server. All-in-One-Lösungen wie MAMP und XAMPP installieren diese Softwarekomponenten für Dich und verknüpfen sie miteinander. Die einfache Einrichtung geht jedoch auf Kosten der Flexibilität.
Du kannst die Binärdateien von windows.php.net/download herunterladen. Nach der PHP-Extraktion empfiehlt es sich, den PATH auf das Stammverzeichnis Deines PHP-Ordners (wo sich php.exe befindet) zu setzen, damit Du PHP von überall ausführen Kannst.
Für Schulungen und die lokale Entwicklung Kannst Du den integrierten Webserver mit PHP 5.4+ nutzen, sodass Du Dich nicht um die Konfiguration kümmern musst. Wenn Du eine All-in-One-Lösung mit vollwertigem Webserver und MySQL wünschst, helfen Dir Tools wie XAMPP, EasyPHP, OpenServer und WAMP, eine Windows-Entwicklungsumgebung schnell zum Laufen zu bringen. Allerdings unterscheiden sich diese Tools etwas von der Produktionsumgebung. Achte daher auf Umgebungsunterschiede, wenn Du unter Windows arbeitest und unter Linux deployst.
Wenn Du Dein Produktionssystem unter Windows betreibst, bietet IIS7 die stabilste und leistungsstärkste Lösung. Mit phpmanager (einem GUI-Plugin für IIS7) kannst Du PHP einfach konfigurieren und verwalten. IIS7 verfügt über integriertes FastCGI und ist sofort einsatzbereit. Du musst lediglich PHP als Handler konfigurieren. Für Support und weitere Ressourcen gibt es einen eigenen Bereich für PHP auf iis.net.
Wenn Du Deine Anwendung in unterschiedlichen Umgebungen in Entwicklung und Produktion ausführst, kann es beim Live-Einsatz zu ungewöhnlichen Fehlern kommen. Wenn Du unter Windows entwickeln und unter Linux (oder einem anderen Betriebssystem als Windows) deployst, solltest Du den Einsatz einer virtuellen Maschine in Betracht ziehen.
Chris Tankersley hat einen sehr hilfreichen Blog-Beitrag darüber verfasst, welche Tools er für die PHP-Entwicklung unter Windows verwendet.
Die meisten GNU/Linux-Distributionen enthalten PHP aus den offiziellen Repositorien, diese Pakete liegen jedoch meist etwas hinter der aktuellen stabilen Version zurück. Es gibt mehrere Möglichkeiten, neuere PHP-Versionen für solche Distributionen zu erhalten.
Für Ubuntu und Debian-basierte GNU/Linux-Distributionen werden die besten Alternativen für native Pakete beispielsweise von Ondřej Surý bereitgestellt und gepflegt. Dies geschieht über sein Personal Package Archive (PPA) für Ubuntu und DPA/bikeshed für Debian. Anweisungen dazu findest Du weiter unten.
Für Ubuntu-Distributionen bietet das PPA von Ondřej Surý unterstützte PHP-Versionen sowie zahlreiche PECL-Erweiterungen. Um dieses PPA zu Deinem System hinzuzufügen, führst Du folgende Schritte im Terminal aus:
Füge zunächst das PPA mit dem folgenden Befehl zu den Softwarequellen Deines Systems hinzu:
sudo add-apt-repository ppa:ondrej/php
Aktualisier nach dem Hinzufügen des PPA die Paketliste Deines Systems:
sudo apt update
Dadurch wird sichergestellt, dass Dein System auf die neuesten im PPA verfügbaren PHP-Pakete zugreifen und diese installieren kann.
Für Debian-basierte Distributionen stellt Ondřej Surý auch einen bikeshed (Debian-Äquivalent eines PPA) zur Verfügung. Um den Bikeshed Deinem System hinzuzufügen und zu aktualisieren, gehe wie folgt vor:
Stelle sicher, dass Du Root-Zugriff hast. Andernfalls musst Du möglicherweise sudo
für die folgenden Befehle verwenden.
Aktualisiere die Paketliste Deines Systems:
sudo apt-get update
Installiere lsb-release
, ca-certificates
, und curl
:
sudo apt-get -y install lsb-release ca-certificates curl
Lade den Signaturschlüssel für das Repository herunter:
sudo curl -sSLo /usr/share/keyrings/deb.sury.org-php.gpg https://packages.sury.org/php/apt.gpg
Füge das Repository zu den Softwarequellen Deines Systems hinzu:
sudo sh -c 'echo "deb [signed-by=/usr/share/keyrings/deb.sury.org-php.gpg] https://packages.sury.org/php/ $(lsb_release -sc) main" > /etc/apt/sources.list.d/php.list'
Aktualisiere abschließend die Paketliste Deines Systems erneut:
sudo apt-get update
Mit diesen Schritten kann Ihr System die neuesten PHP-Pakete aus dem Bikeshed installieren.
Auf RPM-basierten Distributionen (CentOS, Fedora, RHEL usw.) kannst Du Remi’s RPM repository verwenden , um die neueste PHP-Version zu installieren oder mehrere PHP-Versionen gleichzeitig verfügbar zu haben.
Zum Konfigurieren Ihrer RPM-basierten Distribution steht ein Konfigurationsassistent zur Verfügung.
Abgesehen davon kannst Du immer Container verwenden oder den PHP-Quellcode von Grund auf neu kompilieren.
Eine häufige Frage von Webentwicklern lautet: “Wo speichere ich meine Zeug?“ Über die Jahre lautete die Antwort immer wieder: „Dort wo das DocumentRoot
ist.“ Obwohl diese Antwort nicht vollständig ist, bietet sie einen guten Ausgangspunkt.
Aus Sicherheitsgründen sollten Konfigurationsdateien für Besucher einer Website unzugänglich sein. Daher werden öffentliche Skripte in einem öffentlichen Verzeichnis und private Konfigurationen und Daten außerhalb dieses Verzeichnisses gespeichert.
Jedes Team, jedes CMS oder jedes Framework verwendet eine Standardverzeichnisstruktur. Wenn man jedoch ein Projekt alleine startet, kann die Wahl der richtigen Dateistruktur ganz schön herausfordernd sein.
Paul M. Jones hat die gängigen Vorgehensweisen von Zehntausenden von GitHub-Projekten im PHP-Bereich umfassend untersucht. Basierend auf dieser Forschung hat er eine standardisierte Datei- und Verzeichnisstruktur entwickelt: Das Standard PHP Package Skeleton. In dieser Verzeichnisstruktur sollte DocumentRoot
auf public/
verweisen, Unit-Tests sollten im Verzeichnis tests/
abgelegt sein und Drittanbieterbibliotheken, wie sie von composer installiert wurden, gehören ins vendor/
-Verzeichnis. Für andere Dateien und Verzeichnisse ist es für Projektbeteiligte am sinnvollsten, sich an das Standard PHP Package Skeleton zu halten.
Die PHP-Community ist groß und vielfältig und besteht aus unzähligen Bibliotheken, Frameworks und Komponenten. PHP-Entwickler wählen häufig mehrere davon aus und kombinieren sie in einem Projekt. Es ist wichtig, dass der PHP-Code (so nah wie möglich) einem gemeinsamen Codestil folgt, damit Entwickler verschiedene Bibliotheken für ihre Projekte problemlos kombinieren können.
Die Framework Interop Group hat eine Reihe von Stilempfehlungen vorgeschlagen und verabschiedet. Nicht alle beziehen sich auf den Codestil, aber PSR-1, PSR-12, PSR-4 and PER Coding Style machen es. Diese Empfehlungen stellen lediglich eine Reihe von Regeln dar, die von vielen Projekten wie Drupal, Zend, Symfony, Laravel, CakePHP, phpBB, AWS SDK, FuelPHP, Lithium usw. übernommen werden. Du kannst diese Empfehlungen für Deine eigenen Projekte verwenden oder Deinen persönlichen Stil beibehalten.
Idealerweise schreibst Du PHP-Code, der einem bekannten Standard entspricht. Dies kann eine beliebige Kombination von PSRs oder einer der Codierungsstandards von PEAR oder Zend sein. So können andere Entwickler Ihren Code problemlos lesen und bearbeiten, und Anwendungen, die die Komponenten implementieren, gewährleisten Konsistenz auch bei der Verwendung von viel Drittanbieter-Code.
Du kannst PHP_CodeSniffer verwenden, um Code anhand einer dieser Empfehlungen zu überprüfen, und Plugins für Texteditoren wie Sublime Text, um Feedback in Echtzeit zu erhalten.
Du kannst das Code-Layout automatisch korrigieren, indem Du eines der folgenden Tools verwendest:
Und Du kannst phpcs
manuell von der Shell aus ausführen:
phpcs -sw –standard=PSR1 file.php
Es werden Fehler angezeigt und deren Behebung beschrieben.
Es kann auch hilfreich sein, den phpcs
Befehl in einen Git-Pre-Commit-Hook mit dem --filter=GitStaged
CLI-Argument einzubinden.
So kann Code, der gegen den gewählten Standard verstößt, erst dann in das Repository gelangen, wenn diese Verstöße behoben wurden.
Wenn Du über PHP_CodeSniffer verfügst, kannst Du die von ihm gemeldeten Code-Layoutprobleme automatisch mit dem PHP Code Beautifier and Fixer beheben.
phpcbf -w --standard=PSR1 file.php
Alternativ kannst Du den PHP Coding Standards Fixer verwenden. Dieser zeigt Dir, welche Fehler die Codestruktur vor der Behebung aufwies.
php-cs-fixer fix -v --rules=@PSR1 file.php
Für alle Symbolnamen und die Code-Infrastruktur wird Englisch bevorzugt. Kommentare können in jeder Sprache verfasst werden, die für alle aktuellen und zukünftigen Beteiligten, die an der Codebasis arbeiten, leicht verständlich ist.
Eine gute ergänzende Ressource zum Schreiben von sauberem PHP-Code ist Clean Code PHP.
PHP ist eine flexible, dynamische Sprache, die eine Vielzahl von Programmiertechniken unterstützt. Sie hat sich im Laufe der Jahre dramatisch weiterentwickelt, insbesondere durch die Einführung eines soliden objektorientierten Modells in PHP 5.0 (2004), anonymer Funktionen und Namespaces in PHP 5.3 (2009) und Traits in PHP 5.4 (2012).
PHP verfügt über einen sehr umfassenden Satz objektorientierter Programmierfunktionen, darunter Unterstützung für Klassen, abstrakte Klassen, Schnittstellen, Vererbung, Konstruktoren, Klonen, Ausnahmen und mehr.
PHP unterstützt First-Class-Funktionen, d. h., eine Funktion kann einer Variable zugewiesen werden. Sowohl benutzerdefinierte als auch integrierte Funktionen können von einer Variable referenziert und dynamisch aufgerufen werden. Funktionen können als Argumente an andere Funktionen übergeben werden (eine Feature namens Higher-order Functions) und Funktionen können andere Funktionen zurückgeben.
Rekursion, eine Feature, die es einer Funktion ermöglicht, sich selbst aufzurufen, wird von der Sprache unterstützt, der Großteil des PHP-Codes konzentriert sich jedoch auf Iteration.
Neue anonyme Funktionen (mit Unterstützung für Closures) sind seit PHP 5.3 (2009) vorhanden.
PHP 5.4 hat die Möglichkeit hinzugefügt, Closures an den Gültigkeitsbereich eines Objekts zu binden und hat außerdem die Unterstützung für aufrufbare Funktionen verbessert, sodass diese in fast allen Fällen austauschbar mit anonymen Funktionen verwendet werden können.
call_user_func_array()
PHP unterstützt verschiedene Formen der Metaprogrammierung durch Mechanismen wie die Reflection API und Magic Methods. Es gibt viele Magic Methods wie __get()
, __set()
, __clone()
, __toString()
, __invoke()
, usw., die es Entwicklern ermöglichen, sich in das Klassenverhalten einzuklinken. Ruby-Entwickler bemängeln oft, dass PHP method_missing
fehle , aber es ist als __call()
and __callStatic()
verfügbar.
Wie bereits erwähnt, gibt es in der PHP-Community viele Entwickler, die viel Code erstellen. Das bedeutet, dass der PHP-Code einer Bibliothek möglicherweise denselben Klassennamen wie eine andere verwendet. Wenn beide Bibliotheken im selben Namespace verwendet werden, kollidieren sie und verursachen Probleme.
Namespaces lösen dieses Problem. Wie im PHP-Referenzhandbuch beschrieben, lassen sich Namespaces mit Betriebssystemverzeichnissen vergleichen, welche für die Dateien einen Namensraum anlegen; zwei Dateien mit gleichem Namen können in unterschiedlichen Verzeichnissen koexistieren. Ebenso können zwei PHP-Klassen mit gleichem Namen in unterschiedlichen PHP-Namespaces koexistieren. So einfach ist das.
Es ist wichtig, dass Du Deinem Code einen Namespace zuweist, damit er von anderen Entwicklern so verwendet werden kann, dass keine Konflikte mit anderen Bibliotheken befürchtet werden müssen.
Eine empfohlene Möglichkeit zur Verwendung von Namespaces wird in PSR-4 beschrieben . Ziel ist die Bereitstellung einer Standardkonvention für Dateien, Klassen und Namespaces, um Plug-and-Play-Code zu ermöglichen.
Im Oktober 2014 hat die PHP-FIG den vorherigen Autoloading-Standard PSR-0 als veraltet markiert . Sowohl PSR-0 als auch PSR-4 sind weiterhin problemlos nutzbar. Letzterer erfordert PHP 5.3, daher implementieren viele reine PHP 5.2-Projekte PSR-0.
Wenn Sie einen Autoloader-Standard für eine neue Anwendung oder ein neues Paket verwenden möchten, sehen Sie sich PSR-4 an.
Die SPL ist im Lieferumfang von PHP enthalten und bietet eine Sammlung von Klassen und Schnittstellen. Sie besteht hauptsächlich aus häufig benötigten Datenstrukturklassen (Stack, Queue, Heap usw.) und Iteratoren, die diese Datenstrukturen durchlaufen können, oder Ihren eigenen Klassen, die SPL-Schnittstellen implementieren.
PHP wurde zum Schreiben von Webanwendungen entwickelt, eignet sich aber auch zum Skripting von CLI-Programmen. PHP-CLI-Programme können helfen, gängige Aufgaben wie Tests, Bereitstellung und Anwendungsadministration zu automatisieren.
PHP-CLI-Programme sind leistungsstark, da Du den Code Deiner App direkt verwenden kannst, ohne dafür eine Web-GUI erstellen und sichern zu müssen. Achte jedoch darauf, Deine PHP-CLI-Skripte niemals in Deinem public web root zu platzieren!
Versuche PHP über Ihre Befehlszeile auszuführen:
> php -i
Die -i
Option zeigt Deine PHP-Konfiguration an, genau wie die Funktion phpinfo()
.
Diese -a
Option bietet eine interaktive Shell, ähnlich der IRB von Ruby oder der interaktiven Shell von Python. Darüber hinaus gibt es eine Reihe weiterer nützlicher command line options (Kommandozeilenoptionen) .
Schreiben wir ein einfaches “Hello, $name”-CLI-Programm. Erstelle zum Ausprobieren eine Datei mit dem Namen hello.php
, wie unten dargestellt.
<?php
if ($argc !== 2) {
echo "Usage: php hello.php <name>" . PHP_EOL;
exit(1);
}
$name = $argv[1];
echo "Hello, $name" . PHP_EOL;
PHP richtet zwei spezielle Variablen basierend auf den Argumenten ein, mit denen Dein Skript ausgeführt wird. $argc
ist eine Integer-Variable, die die Anzahl der Argumente enthält , und $argv
ist eine Array-Variable, die den Wert jedes Arguments enthält. Das erste Argument ist immer der Name Deiner PHP-Skriptdatei, in diesem Fall hello.php
.
Der exit()
Ausdruck wird mit einer Zahl ungleich Null verwendet, um die Shell darüber zu informieren, dass der Befehl fehlgeschlagen ist. Häufig verwendete Exit-Codes finden Sie hier .
So führen Sie unser obiges Skript über die Befehlszeile aus:
> php hello.php
Usage: php hello.php <name>
> php hello.php world
Hello, world
Eines der nützlichsten Tools in der Softwareentwicklung ist ein geeigneter Debugger. Er ermöglicht es Dir, die Ausführung Deines Codes zu verfolgen und den Inhalt des Stacks zu überwachen. Xdebug, der PHP-Debugger, kann von verschiedenen IDEs genutzt werden, um Breakpoints und Stack-Inspektionen bereitzustellen. Außerdem erlaubt er Tools wie PHPUnit und KCacheGrind das Ausführen von Code-Coverage-Analysen und Code-Profiling.
Du findest Dich in einer Zwickmühle wieder, mit var_dump()
/print_r()
findest Du noch immer keine Lösung - vielleicht solltest Du jetzt den Debugger verwenden.
Die Installation von Xdebug kann schwierig sein, aber eine seiner wichtigsten Funktionen ist “Remote Debugging” – wenn Du Code lokal entwickelst und ihn dann in einer VM oder auf einem anderen Server testest, ist Remote Debugging die Funktion, die Du sofort aktivieren möchtest.
Normalerweise änderst Du Deinen Apache VHost oder Deine .htaccess-Datei mit diesen Werten:
php_value xdebug.remote_host 192.168.?.?
php_value xdebug.remote_port 9000
“Remote-Host” und “Remote-Port” entsprechen Deinem lokalen Computer und dem Port, auf dem Deine IDE hört. Anschließend musst Du Deine IDE nur noch in den Verbindungsmodus versetzen und die URL laden:
http://your-website.example.com/index.php?XDEBUG_SESSION_START=1
Deine IDE fängt jetzt den aktuellen Status ab, während das Skript ausgeführt wird, sodass Du Haltepunkte festlegen und die Werte im Speicher überprüfen kannst.
Grafische Debugger erleichtern das schrittweise Durchlaufen von Code, das Überprüfen von Variablen und die Auswertung von Code in der Live-Laufzeitumgebung. Viele IDEs bieten integrierte oder Plugin-basierte Unterstützung für grafisches Debugging mit Xdebug. MacGDBp ist eine kostenlose, Open-Source- und eigenständige Xdebug-GUI für macOS.
Es gibt eine riesige Auswahl an PHP-Libraries, Frameworks und Komponenten. Ihr Projekt wird wahrscheinlich mehrere davon verwenden – dies sind Projektabhängigkeiten (Dependencies). PHP bot bisher keine gute Möglichkeit, diese Projektabhängigkeiten zu verwalten. Selbst bei manueller Verwaltung mussten Sie sich immer noch um Autoloader kümmern. Das ist kein Problem mehr.
Derzeit gibt es zwei wichtige Paketverwaltungssysteme für PHP: Composer und PEAR. Composer ist derzeit der beliebteste Paketmanager für PHP, PEAR war jedoch lange Zeit der am häufigsten verwendete Paketmanager. Es ist ratsam, die Geschichte von PEAR zu kennen, da Du möglicherweise auch dann noch Referenzen darauf findest, auch wenn Du es nie verwendest.
Composer ist der empfohlene Abhängigkeitsmanager für PHP. Listen Sie die Abhängigkeiten Ihres Projekts in einer composer.json
Datei auf. Mit wenigen einfachen Befehlen lädt Composer die Abhängigkeiten Deines Projekts herunter und richtet das autoloading für Dich ein. Composer entspricht NPM in der Node.js-Welt oder Bundler in der Ruby-Welt.
Es gibt eine Vielzahl von PHP-Bibliotheken, die mit Composer kompatibel sind und in Ihrem Projekt eingesetzt werden können. Diese “packages” finden Sie auf Packagist, dem offiziellen Repository für Composer-kompatible PHP-Bibliotheken.
Der sicherste Weg, Composer herunterzuladen, ist den offiziellen Anweisungen zu folgen.
Dadurch wird sichergestellt, dass das Installationsprogramm nicht beschädigt oder manipuliert ist. Das Installationsprogramm installiert eine composer.phar
Binärdatei in Deinem aktuellen Arbeitsverzeichnis.
Wir empfehlen, Composer global zu installieren (z. B. eine einzelne Kopie in /usr/local/bin
). Führe dazu als Nächstes diesen Befehl aus:
mv composer.phar /usr/local/bin/composer
Hinweis: Wenn das oben genannte Verfahren aufgrund von Berechtigungen fehlschlägt, verwende das sudo
Präfix
Um einen lokal installierten Composer auszuführen, verwende php composer.phar
, global ist es einfach composer
.
Für Windows-Benutzer ist die einfachste Möglichkeit um los zu legen die Verwendung des ComposerSetup -Installationsprogramms, das eine globale Installation durchführt und Dein $PATH
so einrichtet, dass Du einfach composer
aus jedem Verzeichnis in Ihrer Befehlszeile aufrufen kannst.
Composer speichert die Abhängigkeiten Ihres Projekts in einer Datei namens composer.json
. Du kannst diese manuell verwalten oder Composer selbst verwenden. Der composer require
Befehl fügt eine Projektabhängigkeit hinzu und falls Du keine composer.json
Datei hast, wird eine erstellt. Hier ist ein Beispiel, das Twig als Dependency Deines Projekts hinzufügt.
composer require twig/twig:^2.0
Alternativ führt Sie der composer init
Befehl durch die Erstellung einer vollständigen composer.json
Datei für Dein Projekt. Sobald Du Deine composer.json
Datei erstellt hast, kannst Du Composer anweisen, Deine Dependencies herunterzuladen und im vendor/
Verzeichnis zu installieren. Dies gilt auch für heruntergeladene Projekte, die bereits eine composer.json
Datei bereitstellen:
composer install
Fügen Sie als Nächstes diese Zeile zur primären PHP-Datei Ihrer Anwendung hinzu. Dadurch wird PHP angewiesen, den Autoloader von Composer für Ihre Projektabhängigkeiten zu verwenden.
<?php
require 'vendor/autoload.php';
Jetzt können Sie Ihre Dependencies verwenden, diese werden bei Bedarf automatisch geladen.
Composer erstellt eine Datei namens composer.lock
, in der die genaue Version jedes heruntergeladenen Pakets gespeichert ist, als Du zum ersten Mal composer install
ausgeführt hattest. Wenn Du Dein Projekt mit anderen teilst, stelle sicher, dass die composer.lock
Datei enthalten ist, damit dein Kollege beim Ausführen von composer install
die gleichen Versionen erhält wie Du. Um Ihre Abhängigkeiten zu aktualisieren, führe composer update
aus. Verwende composer update
nicht beim Deployen, sondern nur composer install
. Sonst bekommst Du in der Produktion womöglich unterschiedliche Paketversionen.
Dies ist besonders nützlich, wenn Du Deine Versionsanforderungen flexibel definierst. Beispielsweise hat die Versionsanforderung von ~1.8
die Bedeutung “alles, was neuer als 1.8.0
, aber kleiner als 2.0.x-dev
ist”. Du kannst auch das Platzhalterzeichen *
wie in 1.8.*
verwenden .
Der composer update
Befehl aktualisiert nun alle Deine Abhängigkeiten auf die neueste Version, die den von Deinen definierten Einschränkungen entspricht.
Um Benachrichtigungen über neue Versions-Veröffentlichungen zu erhalten, kannst Du Dich bei libraries.io anmelden, einem Webdienst, der Abhängigkeiten überwachen und Dir Benachrichtigungen zu Aktualisierungen senden kann.
Der Local PHP Security Checker ist ein Befehlszeilentool, das Deine composer.lock
Datei untersucht und Dir mitteilt, ob Du eine Deiner Abhängigkeiten aktualisieren musst.
The Local PHP Security Checker is a command-line tool, which will examine your composer.lock
file and tell you if you need to update any of your dependencies.
Composer kann auch globale Abhängigkeiten und deren Binärdateien verarbeiten. Die Bedienung ist unkompliziert: Du musst Deinem Befehl lediglich das Präfix global
voranstellen. Wenn Du beispielsweise PHPUnit installieren und global verfügbar machen möchtest, führe den folgenden Befehl aus:
composer global require phpunit/phpunit
Dadurch wird ein ~/.composer
Ordner erstellt, in dem Deine globalen Abhängigkeiten gespeichert sind. Um die Binärdateien der installierten Pakete überall verfügbar zu haben, füge den ~/.composer/vendor/bin
Ordner anschließend Deiner $PATH
Variable hinzu.
Ein altbewährter Paketmanager, der von einigen PHP-Entwicklern nach wie vor geschätzt wird, ist PEAR Er verhält sich ähnlich wie Composer, weist aber einige bemerkenswerte Unterschiede auf.
PEAR erfordert für jedes Paket eine spezifische Struktur. Das bedeutet, dass der Autor des Pakets es für die Verwendung mit PEAR vorbereiten muss. Der Gebrauch eines Projekts, das nicht für die Verwendung mit PEAR vorbereitet wurde, ist unmöglich.
PEAR installiert Pakete global, d. h. nach der Installation stehen sie allen Projekten auf dem Server zur Verfügung. Dies ist sinnvoll, wenn viele Projekte dasselbe Paket mit derselben Version verwenden, kann aber zu Problemen führen, wenn Versionskonflikte zwischen zwei Projekten auftreten.
Du kannst PEAR installieren, indem Du den .phar
Installer herunterlädst und ausführst. Die PEAR-Dokumentation enthält detaillierte Installationsanweisungen für jedes Betriebssystem.
Wenn Du Linux nutzt, kannst Du auch einen Blick in den Paketmanager Deiner Distribution werfen. Debian und Ubuntu verfügen beispielsweise über ein apt php-pear
-Paket. (apt: Advanced Package Tool)
Wenn das Package in der PEAR packages list gelistet ist, kannst Du es installieren, indem Du den offiziellen Namen angibst:
pear install foo
Wenn das Paket auf einem anderen Kanal gehostet wird, musst Du zuerst den Kanal entdecken/discovern
und ihn bei der Installation angeben. Weitere Informationen zu diesem Thema findest Du in der Dokumentation zur Kanalverwendung Using channel docs.
Wenn Sie Composer bereits verwenden und zusätzlich auch PEAR-Code installieren möchten, können Sie Composer zur Verwaltung Ihrer PEAR-Abhängigkeiten verwenden. PEAR-Repositorys werden von Composer Version 2 nicht mehr direkt unterstützt. Du Musst daher manuell ein Repository hinzufügen, um PEAR-Pakete zu installieren:
{
"repositories": [
{
"type": "package",
"package": {
"name": "pear2/pear2-http-request",
"version": "2.5.1",
"dist": {
"url": "https://github.com/pear2/HTTP_Request/archive/refs/heads/master.zip",
"type": "zip"
}
}
}
],
"require": {
"pear2/pear2-http-request": "*"
},
"autoload": {
"psr-4": {"PEAR2\\HTTP\\": "vendor/pear2/pear2-http-request/src/HTTP/"}
}
}
Der erste Abschnitt "repositories"
teilt Composer mit, dass das Pear-Repository “initialisiert” (oder in der PEAR-Terminologie “discover”) werden soll. Anschließend stellt der require
-Abschnitt dem Paketnamen ein Präfix voran:
pear-channel/package
Das “pear” Präfix ist fest codiert, um Konflikte zu vermeiden. Da ein Pear-Kanal beispielsweise mit dem vendor-Mamen eines anderen Pakets identisch sein könnte, kann der Kurzname des Kanals (oder die vollständige URL) verwendet werden, um anzugeben, in welchem Kanal sich das Paket befindet.
Wenn dieser Code installiert ist, steht er in Deinem Vendor-Verzeichnis und automatisch über den Composer-Autoloader zur Verfügung:
vendor/pear2/pear2-http-request/pear2/HTTP/Request.php
Um dieses PEAR-Paket zu verwenden, verwise einfach folgendermaßen darauf:
<?php
require __DIR__ . '/vendor/autoload.php';
use PEAR2\HTTP\Request;
$request = new Request();
PHP ist eine umfangreiche Sprache, die es Programmierern aller Erfahrungsstufen ermöglicht, nicht nur schnell, sondern auch effizient Code zu schreiben. Als Fortgeschrittene vergessen wir jedoch oft die Grundlagen, die wir anfangs gelernt haben (oder übersehen sie), und neigen zu Abkürzungen und/oder schlechten Angewohnheiten. Um diesem häufigen Problem entgegenzuwirken, soll dieser Abschnitt Programmierern die grundlegenden Programmierpraktiken in PHP in Erinnerung rufen.
PHP verfügt über die Klasse DateTime, die Sie beim Lesen, Schreiben, Vergleichen oder Berechnen von Datum und Uhrzeit unterstützt. Neben DateTime bietet PHP viele weitere Funktionen für Datum und Uhrzeit. Für die meisten gängigen Anwendungen bietet DateTime eine praktische objektorientierte Schnittstelle. DateTime kann Zeitzonen verarbeiten, dies geht jedoch über den Rahmen dieser kurzen Einführung hinaus.
Um mit DateTime zu arbeiten, konvertiere das Rohdaten und -zeiten mit der createFromFormat()
Factory-Methode in ein Objekt oder rufe mit new DateTime
das aktuelle Datum und die aktuelle Uhrzeit ab. Verwende die format()
-Methode, um DateTime für die Ausgabe wieder in einen String umzuwandeln.
<?php
$raw = '22. 11. 1968';
$start = DateTime::createFromFormat('d. m. Y', $raw);
echo 'Start date: ' . $start->format('Y-m-d') . PHP_EOL;
Berechnungen mit DateTime sind mit der Klasse DateInterval möglich. DateTime verfügt über Methoden wie add()
und sub()
, die ein DateInterval als Argument akzeptieren.
Schreibe keinen Code, der jeden Tag die gleiche Anzahl von Sekunden erwartet. Sowohl Sommerzeit- als auch Zeitzonenänderungen würden diese Annahme widerlegen. Verwende stattdessen Datumsintervalle. Um die Datumsdifferenz zu berechnen, verwenden Sie die diff()
-Methode. Sie gibt ein neues DateInterval zurück, das sehr einfach darzustellen ist.
<?php
// create a copy of $start and add one month and 6 days
$end = clone $start;
$end->add(new DateInterval('P1M6D'));
$diff = $end->diff($start);
echo 'Difference: ' . $diff->format('%m month, %d days (total: %a days)') . PHP_EOL;
// Difference: 1 month, 6 days (total: 37 days)
Sie können Standardvergleiche für DateTime-Objekte verwenden:
<?php
if ($start < $end) {
echo "Start is before the end!" . PHP_EOL;}
Ein letztes Beispiel zur Veranschaulichung der DatePeriod-Klasse. Sie wird verwendet, um wiederkehrende Ereignisse zu durchlaufen. Sie kann zwei DateTime-Objekte (Start und End) sowie das Intervall annehmen, für das alle dazwischenliegenden Ereignisse zurückgegeben werden.
<?php
// output all thursdays between $start and $end
$periodInterval = DateInterval::createFromDateString('first thursday');
$periodIterator = new DatePeriod($start, $periodInterval, $end, DatePeriod::EXCLUDE_START_DATE);
foreach ($periodIterator as $date) {
// output each date in the period
echo $date->format('Y-m-d') . ' ';
}
Eine beliebte PHP-API-Erweiterung ist Carbon. Sie übernimmt alle Funktionen der DateTime-Klasse und erfordert daher nur minimale Codeänderungen. Zu den zusätzlichen Funktionen gehören Lokalisierungsunterstützung, weitere Möglichkeiten zum Addieren, Subtrahieren und Formatieren eines DateTime-Objekts sowie die Möglichkeit, Ihren Code durch die Simulation eines Datums und einer Uhrzeit Ihrer Wahl zu testen.
Beim Erstellen Deine Anwendung ist es hilfreich, allgemeine Muster im Code und für die Gesamtstruktur Deines Projekts zu verwenden. Die Verwendung allgemeiner Muster erleichtert die Verwaltung Deines Codes erheblich und ermöglicht anderen Entwicklern ein schnelles Verständnis wie in Deinem Code alles miteinander zusammenhängt.
Wenn Du ein Framework verwendest, basiert der Großteil des übergeordneten Codes und der Projektstruktur auf diesem Framework. Daher werden dann viele Musterentscheidungen für Dich getroffen. Du musst jedoch weiterhin die besten Muster für Deinen Code auswählen, wenn dieser auf dem Framework basiert. Wenn Du hingegen kein Framework zur Erstellung Deiner Anwendung verwendest, musst Du Muster finden, die am besten zu Art und Größe Deiner Anwendung passen.
Weitere Informationen zu PHP-Entwurfsmustern und funktionierende Beispiele findest Du unter:
Dieser Abschnitt wurde ursprünglich von Alex Cabal für PHP Best Practices geschrieben und diente als Grundlage für unsere eigenen UTF-8-Empfehlungen.
PHP unterstützt Unicode derzeit nicht auf niedriger Ebene. Es gibt Möglichkeiten, die korrekte Verarbeitung von UTF-8-Strings sicherzustellen, aber das ist nicht einfach und erfordert die Auseinandersetzung mit fast allen Ebenen der Webanwendung, von HTML über SQL bis hin zu PHP. Wir geben hier eine kurze, praktische Zusammenfassung.
Die grundlegenden String-Operationen, wie das Verketten zweier Strings und das Zuweisen von Strings zu Variablen, erfordern für UTF-8 keine besonderen Anforderungen.
Die meisten String-Funktionen, wie strpos()
und strlen()
, erfordern jedoch besondere Aufmerksamkeit. Diese Funktionen haben oft ein mb_*
Gegenstück, beispielsweise mb_strpos()
and mb_strlen()
. Diesemb_*
-Strings werden Dir über die Multibyte String Extension zur Verfügung gestellt und sind speziell für die Verarbeitung von Unicode-Strings konzipiert.
Du musst die mb_*
-Funktionen immer dann verwenden, wenn Du mit einer Unicode-Zeichenfolge arbeitest. Wenn Du substr()
auf eine UTF-8-Zeichenfolge anwendet,
besteht eine grosse Möglichkeit, dass das Ergebnis einige unleserliche Halbzeichen enthält. Die korrekte Funktion wäre das Multibyte-Gegenstück mb_substr()
.
Das Schwierige ist, immer daran zu denken, die mb_*
-Funktionen zu verwenden. Wenn Du es auch nur einmal vergisst, besteht die Gefahr, dass Deine Unicode-Zeichenfolge bei der weiteren Verarbeitung verstümmelt wird.
Nicht alle String-Funktionen haben ein mb_*
-Gegenstück. Wenn es für Ihre Aufgabe keins gibt, haben Sie möglicherweise Pech gehabt.
Du solltest diemb_internal_encoding()
-Funktion am Anfang jedes PHP-Skripts (oder am Anfang Ihres globalen Include-Skripts) verwenden und die mb_http_output()
-Funktion direkt dahinter,
wenn Dein Skript an einen Browser ausgegeben wird. Die explizite Definition der Zeichenkettenkodierung in jedem Skript erspart Dir später viel Ärger.
Darüber hinaus verfügen viele PHP-Funktionen, die mit Zeichenfolgen arbeiten, über einen optionalen Parameter, mit dem Sie die Zeichenkodierung festlegen können.
Geben Sie bei dieser Option immer explizit UTF-8 an.
Beispielsweise bietet htmlentities()
eine Option für die Zeichenkodierung, und Du solltest bei der Verarbeitung solcher Zeichenfolgen immer UTF-8 angeben. Beachte, dass ab PHP 5.4.0 UTF-8 die Standardkodierung für htmlentities()
und htmlspecialchars()
ist.
Abschießend, wenn Du eine verteilte Anwendung erstellst und nicht sicher bist, ob die mbstring
-Erweiterung verwendbar ist, solltest Du das Composer-Paket symfony/polyfill-mbstring verwenden.
Dieses wird mbstring
bei Verfügbarkeit verwenden und greift andernfalls auf Nicht-UTF-8-Funktionen zurück.
Wenn Dein PHP-Skript auf MySQL zugreift, besteht die Möglichkeit, dass Deine Strings als Nicht-UTF-8-Strings in der Datenbank gespeichert werden, selbst wenn Sie alle oben genannten Vorsichtsmaßnahmen befolgen.
Um sicherzustellen, dass Dein Strings von PHP in UTF-8 an MySQL übertragen werden, stelle sicher, dass Ihre Datenbank und Tabellen auf den utf8mb4
entsprechenden Zeichensatz und die entsprechende Sortierung eingestellt sind und dass Sie den utf8mb4
Zeichensatz im PDO-Connection-String verwendest. Siehe untensthender Beispielcode. Das ist äußerst wichtig.
Beachte, dass Du für die vollständige UTF-8-Unterstützung den utf8mb4
-Zeichensatz verwenden musst, nicht den utf8
-Zeichensatz! Weitere Informationen findest Du im Abschnitt „Weitere Informationen“.
Verwende die mb_http_output()
-Funktion, um sicherzustellen, dass Dein PHP-Skript wirklich UTF-8-Zeichenfolgen an Deinen Browser ausgibt.
Der Browser muss dann durch die HTTP-Response informiert werden, dass diese Seite als UTF-8 betrachtet werden soll. Heutzutage ist es üblich, den Zeichensatz im HTTP-Response-Header wie folgt festzulegen:
<?php
header('Content-Type: text/html; charset=UTF-8')
Der bisherige Ansatz hierfür bestand darin, das charset <meta>
-Tag in das <head>
-Tag Ihrer Seite aufzunehmen.
<?php
// Tell PHP that we're using UTF-8 strings until the end of the script
mb_internal_encoding('UTF-8');
$utf_set = ini_set('default_charset', 'utf-8');
if (!$utf_set) {
throw new Exception('could not set default_charset to utf-8, please ensure it\'s set on your system!');
}
// Tell PHP that we'll be outputting UTF-8 to the browser
mb_http_output('UTF-8');
// Our UTF-8 test string
$string = 'Êl síla erin lû e-govaned vîn.';
// Transform the string in some way with a multibyte function
// Note how we cut the string at a non-Ascii character for demonstration purposes
$string = mb_substr($string, 0, 15);
// Connect to a database to store the transformed string
// See the PDO example in this document for more information
// Note the `charset=utf8mb4` in the Data Source Name (DSN)
$link = new PDO(
'mysql:host=your-hostname;dbname=your-db;charset=utf8mb4',
'your-username',
'your-password',
array(
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_PERSISTENT => false
)
);
// Store our transformed string as UTF-8 in our database
// Your DB and tables are in the utf8mb4 character set and collation, right?
$handle = $link->prepare('insert into ElvishSentences (Id, Body, Priority) values (default, :body, :priority)');
$handle->bindParam(':body', $string, PDO::PARAM_STR);
$priority = 45;
$handle->bindParam(':priority', $priority, PDO::PARAM_INT); // explicitly tell pdo to expect an int
$handle->execute();
// Retrieve the string we just stored to prove it was stored correctly
$handle = $link->prepare('select * from ElvishSentences where Id = :id');
$id = 7;
$handle->bindParam(':id', $id, PDO::PARAM_INT);
$handle->execute();
// Store the result into an object that we'll output later in our HTML
// This object won't kill your memory because it fetches the data Just-In-Time to
$result = $handle->fetchAll(\PDO::FETCH_OBJ);
// An example wrapper to allow you to escape data to html
function escape_to_html($dirty){
echo htmlspecialchars($dirty, ENT_QUOTES, 'UTF-8');
}
header('Content-Type: text/html; charset=UTF-8'); // Unnecessary if your default_charset is set to utf-8 already
?><!doctype html>
<html>
<head>
<meta charset="UTF-8">
<title>UTF-8 test page</title>
</head>
<body>
<?php
foreach($result as $row){
escape_to_html($row->Body); // This should correctly output our transformed UTF-8 string to the browser
}
?>
</body>
</html>
Disclaimer für Neulinge: i18n und l10n sind Numeronyme, eine Art Abkürzung, bei der Zahlen zum Verkürzen von Wörtern verwendet werden – in unserem Fall wird Internationalisierung zu i18n und Lokalisierung zu l10n.
Zunächst müssen wir diese beiden ähnlichen Konzepte und andere damit zusammenhängende Dinge definieren:
Der einfachste Weg, PHP-Software zu internationalisieren, ist die Verwendung von Array-Dateien und deren Strings in Vorlagen wie z.B. <h1><?=$TRANS['title_about_page']?></h1>
.
Dieser Weg ist jedoch für ernsthafte Projekte kaum zu empfehlen, da er im Laufe der Zeit einige Wartungsprobleme mit sich bringt – einige davon können bereits zu Beginn auftreten, wie z. B. die Pluralisierung.
Versuchen Sie diesen Weg daher bitte nicht, wenn Ihr Projekt mehr als ein paar Seiten umfasst.
Der klassischste Weg und oft als Referenz für i18n und l10n verwendet, ist ein Unix-Tool namens gettext
.
Es stammt aus dem Jahr 1995 und ist noch immer eine vollständige Implementierung für die Übersetzung von Software. Es ist einfach zu bedienen und bietet leistungsstarke Tools.
Wir werden hier Gettext besprechen. Damit Sie sich nicht mit der Kommandozeile herumschlagen müssen, stellen wir Ihnen eine praktische GUI-Anwendung vor, mit der Sie Ihren l10n-Quellcode einfach aktualisieren können.
Es gibt gängige Bibliotheken, die Gettext und andere i18n-Implementierungen unterstützen. Einige davon sind möglicherweise einfacher zu installieren oder bieten zusätzliche Funktionen oder i18n-Dateiformate. In diesem Dokument konzentrieren wir uns auf die Tools, die im PHP-Kern enthalten sind. Zur Vervollständigung listen wir hier weitere auf:
gettext
-Befehl nicht nativ unterstützt) und kann auch in andere Formate als .mo/.po
-Dateien exportieren. Dies ist nützlich,
wenn Sie Ihre Übersetzungsdateien in andere Systemkomponenten, beispielsweise eine JavaScript-Schnittstelle, integrieren müssen.strtr()
-Nutzung.Andere Frameworks enthalten auch i18n-Module, diese sind jedoch außerhalb ihrer Codebasen nicht verfügbar:
@lang
Helper für Vorlagendateien.Intl
unterstützt Array-, Gettext- und datenbankbasierte Übersetzungen und enthält einen Nachrichtenextractor.
Die Intl-Erweiterung ist seit PHP 5.3 verfügbar und basiert auf dem ICU-Projekt.
Dadurch kann Yii leistungsstarke Ersetzungen ausführen, beispielsweise Zahlen ausschreiben und Datum, Zeit, Intervalle, Währungsen und Ordinalzahlen formatieren.Wenn Du Dich für eine der Bibliotheken entscheidest, die keine Extractors bereitstellen, möchtst Du möglicherweise die Gettext-Formate verwenden, sodass Du die ursprüngliche Gettext-Toolchain (einschließlich Poedit) verwenden kannst, wie im Rest des Kapitels beschrieben.
Möglicherweise musst Du Gettext und die zugehörige PHP-Bibliothek mithilfe Deines Paketmanagers (z. B. apt-get
or yum
) installieren.
Aktiviere die Funktion nach der Installation, indem Du extension=gettext.so
(Linux/Unix) oder extension=php_gettext.dll
(Windows) zu Deiner php.ini
hinzufügst.
Auch hier verwenden wir Poedit zum Erstellen von Übersetzungsdateien. Du wirst es wahrscheinlich im Paketmanager Deines Systems finden; es ist für Unix, macOS und Windows verfügbar und kann auch kostenlos auf deren Website heruntergeladen werden.
Bei der Arbeit mit gettext arbeitest Du üblicherweise mit drei Dateien. Die wichtigsten sind PO- (Portable Object) und MO- (Machine Object) Dateien. Erstere ist eine Liste lesbarer “übersetzter Objekte” und letztere die entsprechende Binärdatei, die gettext bei der Lokalisierung interpretiert. Es gibt außerdem eine POT-Datei (Template), die alle vorhandenen Schlüssel aus Deinen Quelldateien enthält und als Leitfaden zum Generieren und Aktualisieren aller PO-Dateien dient. Diese Template-Dateien sind nicht zwingend erforderlich: Je nach verwendetem l10n-Tool reichen PO/MO-Dateien aus. Du benötigst immer ein Paar PO/MO-Dateien pro Sprache und Region, aber nur eine POT-Datei pro Domänen.
In großen Projekten kann es vorkommen, dass Übersetzungen getrennt werden müssen, wenn dieselben Wörter in einem bestimmten Kontext unterschiedliche Bedeutungen haben. In diesen Fällen werden sie in verschiedene Domänen aufgeteilt. Dabei handelt es sich im Wesentlichen um benannte Gruppen von POT-/PO-/MO-Dateien, wobei der Dateiname die jeweilige Übersetzungsdomäne ist. Kleine und mittelgroße Projekte verwenden der Einfachheit halber meist nur eine Domäne; ihr Name ist beliebig, wir verwenden für unsere Codebeispiele jedoch “main”. In Symfony-Projekten werden Domänen beispielsweise verwendet, um die Übersetzungen für Validierungsmeldungen zu trennen.
Ein Gebietsschema ist einfach ein Code, der eine Version einer Sprache identifiziert. Es wird gemäß den Spezifikationen ISO 639-1 und ISO 3166-1 alpha-2 definiert: Zwei Kleinbuchstaben für die Sprache, optional gefolgt von einem Unterstrich und zwei Großbuchstaben, die den Länder- oder Regionalcode kennzeichnen. Für seltene Sprachen werden drei Buchstaben verwendet.
Für manche Sprechende mag der Länderteil überflüssig erscheinen. Tatsächlich haben einige Sprachen in verschiedenen Ländern Dialekte, wie zum Beispiel Österreichisches Deutsch (de_AT
) oder brasilianisches Portugiesisch (pt_BR
).
Der zweite Teil dient der Unterscheidung zwischen diesen Dialekten – fehlt er, wird er als “generische” oder “hybride” Version der Sprache angesehen.
Um Gettext verwenden zu können, benötigen wir eine bestimmte Ordnerstruktur. Wähle zunächst ein beliebiges Stammverzeichnis für Deine l10n-Dateien in Deinem Quell-Repository.
Darin findest Du einen Ordner für jedes benötigte Gebietsschema und einen festen LC_MESSAGES
Ordner für alle PO/MO-Paare. Beispiel:
<project root>
├─ src/
├─ templates/
└─ locales/
├─ forum.pot
├─ site.pot
├─ de/
│ └─ LC_MESSAGES/
│ ├─ forum.mo
│ ├─ forum.po
│ ├─ site.mo
│ └─ site.po
├─ es_ES/
│ └─ LC_MESSAGES/
│ └─ ...
├─ fr/
│ └─ ...
├─ pt_BR/
│ └─ ...
└─ pt_PT/
└─ ...
Wie bereits in der Einleitung erwähnt, können verschiedene Sprachen unterschiedliche Pluralregeln haben.
Gettext erspart uns jedoch auch dieses Problem. Beim Erstellen einer neuen .po
-Datei musst Du die Pluralregeln für die jeweilige Sprache angeben, und übersetzte Teile, die pluralsensitiv sind haben für jede dieser Regeln eine andere Form.
Beim Aufruf von Gettext im Code musst Du die Nummer des Satzes angeben, und Gettext ermittelt die korrekte Form – bei Bedarf sogar mithilfe von String-Ersetzungen.
Pluralregeln enthalten die Anzahl der verfügbaren Pluralformen und einen Boole’schen Test auf n
, der definiert, in welche Regel die angegebene Zahl fällt (beginnend mit 0). Beispiel:
nplurals=1; plural=0
- nur eine Regelnplurals=2; plural=(n != 1);
- zwei Regeln: Erste wenn N eins ist, andernfalls die zweite Regelnplurals=2; plural=(n > 1);
- zwei Regeln: Zweite, wenn N größer als eins ist, andernfalls erste RegelNachdem Du nun die Grundlagen der Pluralregeln verstanden hast (und falls nicht, sieh Dir bitte eine ausführlichere Erklärung im LingoHub-Tutorial an), möchtst Du vielleicht die benötigten Regeln aus einer Liste kopieren, anstatt sie von Hand zu schreiben.
Wenn Du Gettext aufrufst, um Sätze mit Zählern zu lokalisieren, musst Du auch die zugehörige Nummer angeben. Gettext ermittelt die anzuwendende Regel und verwendet die korrekte lokalisierte Version.
Für jede definierte Pluralregel musst Du einen anderen Satz in die .po
-Datei aufnehmen.
Nach all der Theorie kommen wir nun zur Praxis. Hier ist ein Ausschnitt einer .po
-Datei – achte nicht auf das Format, sondern auf den Gesamtinhalt. Wie Du ihn einfach bearbeiten kannst, erfährst Du später:
msgid ""
msgstr ""
"Language: pt_BR\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
msgid "We are now translating some strings"
msgstr "Nós estamos traduzindo algumas strings agora"
msgid "Hello %1$s! Your last visit was on %2$s"
msgstr "Olá %1$s! Sua última visita foi em %2$s"
msgid "Only one unread message"
msgid_plural "%d unread messages"
msgstr[0] "Só uma mensagem não lida"
msgstr[1] "%d mensagens não lidas"
Der erste Abschnitt funktioniert wie eine Kopfzeile, wobei insbesondere msgid
und msgstr
leer sind. Er beschreibt die Dateikodierung, Pluralformen und andere weniger relevante Dinge.
Der zweite Abschnitt übersetzt eine einfache Zeichenfolge vom Englischen in brasilianisches Portugiesisch und der dritte macht dasselbe, nutzt jedoch die Zeichenfolgenersetzung aus sprintf
, sodass die Übersetzung den Benutzernamen und das Besuchsdatum enthalten kann.
Der letzte Abschnitt ist ein Beispiel für Pluralformen, wobei die Singular- und Pluralversion im Englischen als msgid
und die entsprechenden Übersetzungen als msgstr
0 und 1 angezeigt werden (entsprechend der durch die Pluralregel vorgegebenen Zahl).
Auch hier wird die Zeichenfolgenersetzung verwendet, sodass die Zahl mithilfe %d
direkt im Satz angezeigt wird.
Die Pluralformen bestehen immer aus zwei msgid
(Singular und Plural). Es wird daher empfohlen, keine komplexe Sprache als Übersetzungsquelle zu verwenden.
Wie Du vielleicht bemerkt hast, verwenden wir als Quell-ID den englischen Originalsatz. Diese msgid
wird in allen Deinen.po
-Dateien verwendet. Das bedeutet, dass andere Sprachen dasselbe Format und dieselben msgid
-Felder, aber übersetzte msgstr
-Zeilen haben.
Wenn wir über Übersetzungsschlüssel sprechen, gibt es zwei verschiedene “Schulen”:
msgidals echter Satz . Auch wenn Teile der Software in einer bestimmten Sprache nicht übersetzt sind, behält der angezeigte Schlüssel eine gewisse Bedeutung. Beispiel: Wenn Du selbtst vom Englischen ins Spanische übersetzst, aber Hilfe beim Übersetzen ins Französische brauchst, veröffentlichst Du die neue Seite eben mit fehlenden französischen Sätzen, und Teile der Website werden stattdessen auf Englisch angezeigt. es ist für den Übersetzer viel einfacher zu verstehen, was vor sich geht, und auf der Grundlage des eine richtige Übersetzung anzufertigen msgid;
Sie erhalten „kostenloses“ L10n für eine Sprache – die Quellsprache. Der einzige Nachteil: Wenn Sie den eigentlichen Text ändern möchten, müssen Sie ihn msgidin mehreren Sprachdateien ersetzen.
msgid
als echter Satz.
Die Hauptvorteile sind:
msgid
eine richtige Übersetzung anzufertigen;msgid
in mehreren Sprachdateien ersetzen.msgid
als eindeutiger, strukturierter Schlüssel.
Er beschreibt strukturiert die Rolle des Satzes in der Anwendung und enthält das Template oder den Teil, in dem sich der String anstelle Deines Inhalts befindet.
en.po
-Datei, welche die Übersetzer lesen können, um zu verstehen, was sie in die fr.po
-Datei schreiben sollen.top_menu.welcome
anstatt Hello there, User!
auf der o.g. unübersetzten französischen Page).
Das ist gut dafür, dass die Übersetzung vor der Veröffentlichung abgeschlossen sein muss – schlecht ist es jedoch, da Übersetzungsprobleme das Nutzererlebnis erheblich beeinträchtigen würden
Einige Libraries bieten jedoch die Möglichkeit, eine bestimmte Sprache als “Fallback” festzulegen, was ein ähnliches Verhalten wie der obige Ansatz hat.Das Gettext-Handbuch bevorzugt den ersten Ansatz, da dieser im Allgemeinen für Übersetzer und Benutzer im Problemfall einfacher ist. So werden wir auch hier vorgehen. Die Symfony-Dokumentation bevorzugt jedoch die schlüsselwortbasierte Übersetzung, um unabhängige Änderungen aller Übersetzungen zu ermöglichen, ohne dass auch die Templates davon betroffen sind.
In einer typischen Anwendung verwendest Du Gettext-Funktionen beim Schreiben statischer Texte auf Deinen Seiten. Diese Sätze erscheinen dann in .po
-Dateien, werden übersetzt, in .mo
-Dateien kompiliert und anschließend von Gettext beim Rendern der eigentlichen Benutzeroberfläche verwendet. Lassen Sie uns daher die bisherigen Ausführungen in einem Schritt-für-Schritt-Beispiel zusammenfassen:
<?php include 'i18n_setup.php' ?>
<div id="header">
<h1><?=sprintf(gettext('Welcome, %s!'), $name)?></h1>
<!-- code indented this way only for legibility -->
<?php if ($unread): ?>
<h2><?=sprintf(
ngettext('Only one unread message',
'%d unread messages',
$unread),
$unread)?>
</h2>
<?php endif ?>
</div>
<h1><?=gettext('Introduction')?></h1>
<p><?=gettext('We\'re now translating some strings')?></p>
gettext()übersetzt einfach ein msgid in das entsprechende Element msgstr für eine bestimmte Sprache. Es gibt auch eine Kurzfunktion _(), die auf die gleiche Weise funktioniert. ngettext() macht dasselbe, aber mit Pluralregeln; Es gibt auch dgettext()und dngettext(), mit denen Du die Domäne für einen einzelnen Anruf überschreiben kannst. Mehr zur Domänenkonfiguration im nächsten Beispiel.
gettext()
übersetzt einfach ein msgid
in das entsprechende msgstr
-Element für eine bestimmte Sprache. Es gibt auch eine Kurzfunktion _()
, die genauso funktioniert;ngettext()
macht dasselbe, aber mit Pluralregeln;dgettext()
und dngettext()
, mit denen Du die Domäne für einen einzelnen Anruf überschreiben kannst. Mehr zur Domänenkonfiguration im nächsten Beispiel.i18n_setup.php
wie oben), wählt das richtige Gebietsschemas und Konfiguration von Gettext aus.<?php
/**
* Verifies if the given $locale is supported in the project
* @param string $locale
* @return bool
*/
function valid($locale) {
return in_array($locale, ['en_US', 'en', 'pt_BR', 'pt', 'es_ES', 'es']);
}
//setting the source/default locale, for informational purposes
$lang = 'en_US';
if (isset($_GET['lang']) && valid($_GET['lang'])) {
// the locale can be changed through the query-string
$lang = $_GET['lang']; //you should sanitize this!
setcookie('lang', $lang); //it's stored in a cookie so it can be reused
} elseif (isset($_COOKIE['lang']) && valid($_COOKIE['lang'])) {
// if the cookie is present instead, let's just keep it
$lang = $_COOKIE['lang']; //you should sanitize this!
} elseif (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
// default: look for the languages the browser says the user accepts
$langs = explode(',', $_SERVER['HTTP_ACCEPT_LANGUAGE']);
array_walk($langs, function (&$lang) { $lang = strtr(strtok($lang, ';'), ['-' => '_']); });
foreach ($langs as $browser_lang) {
if (valid($browser_lang)) {
$lang = $browser_lang;
break;
}
}
}
// here we define the global system locale given the found language
putenv("LANG=$lang");
// this might be useful for date functions (LC_TIME) or money formatting (LC_MONETARY), for instance
setlocale(LC_ALL, $lang);
// this will make Gettext look for ../locales/<lang>/LC_MESSAGES/main.mo
bindtextdomain('main', '../locales');
// indicates in what encoding the file should be read
bind_textdomain_codeset('main', 'UTF-8');
// if your application has additional domains, as cited before, you should bind them here as well
bindtextdomain('forum', '../locales');
bind_textdomain_codeset('forum', 'UTF-8');
// here we indicate the default domain the gettext() calls will respond to
textdomain('main');
// this would look for the string in forum.mo instead of main.mo
// echo dgettext('forum', 'Welcome back!');
?>
Einer der großen Vorteile von Gettext gegenüber benutzerdefinierten Framework-i18n-Paketen ist sein umfangreiches und leistungsstarkes Dateiformat. “Oh Mann, das ist ziemlich schwer zu verstehen und manuell zu bearbeiten - ein simples Array wäre echt leichter!” Mach’ Dir keine Sorgen: Anwendungen wie Poedit helfen Dir dabei – und zwar sehr. Du kannst das Programm von der Website herunterladen; es ist kostenlos und für alle Plattformen verfügbar. Es ist ein recht einfach zu bedienendes und gleichzeitig sehr leistungsstarkes Tool, das alle Funktionen von Gettext nutzt. Diese Anleitung basiert auf PoEdit 1.8.
Wählen Sie im ersten Durchgang “File > New…” aus dem Menü. Du wirst direkt nach der Sprache gefragt: Hier kannst Du die gewünschte Sprache auswählen/filtern oder das oben erwähnte Format wie en_US
oder pt_BR
nutzen.
Speichere die Datei nun – in der ebenfalls beschriebenen Verzeichnisstruktur. Klicke anschließend auf “Extract from sources” und konfiguriere hier verschiedene Einstellungen für die Extraktions- und Übersetzungsaufgaben. Diese findest Du später unter “Catalog > Properties”:
Quellpfade: Hier musst Du alle Ordner des Projekts angeben, in dem gettext()
(und verwandte) aufgerufen werden. Dies sind normalerweise Deine Templates-/View-Ordner. Dies ist die einzige obligatorische Einstellung;
gettext()
-Funktionsaufrufe in verschiedenen Programmiersprachen, aber Du kannst auch eigene Übersetzungsfunktionen erstellen. Hier fügst Du die anderen Methoden hinzu. Dies wird später im Abschnitt “Tipps” erläutert.Nachdem Sie diese Punkte festgelegt haben, durchsucht PoEdit Ihre Quelldateien nach allen Lokalisierungsaufrufen. Nach jedem Scan zeigt PoEdit eine Zusammenfassung der gefundenen und entfernten Einträge an. Neue Einträge werden in die Übersetzungstabelle eingefügt, und Du fängst mit der Eingabe der lokalisierten Versionen der Strings an. Speichere die Datei, und eine .mo-Datei wird im selben Ordner (neu) kompiliert. Und schon ist Dein Projekt internationalisiert.
Wie Du vielleicht schon bemerkt hast, gibt es zwei Haupttypen lokalisierter Zeichenfolgen: Einfache und solche mit Pluralformen. Erstere bestehen lediglich aus zwei Feldern: Quelle und lokalisierter String. Der Quellstring kann nicht geändert werden, da Gettext/Poedit keine Möglichkeit zur Änderung Deiner Quelldateien bietet. Du musst die Quelle selbst ändern und die Dateien erneut scannen. Tipp: Klicke mit der rechten Maustaste auf eine Übersetzungszeile, um Hinweise zu den Quelldateien und Zeilen zu erhalten, in denen dieser String verwendet wird. Zeichenfolgen mit Pluralformen hingegen enthalten zwei Felder zur Anzeige der beiden Quellzeichenfolgen und Registerkarten zur Konfiguration der verschiedenen Endformen.
Wenn Du Deine Quellen ändern und die Übersetzungen aktualisieren musst, klicke einfach auf “Aktualisieren”. Poedit scannt den Code erneut, entfernt nicht vorhandene Einträge, verbindet geänderte und fügt neue hinzu. Es kann auch versuchen, einige Übersetzungen aufgrund Deiner anderen Übersetzungen zu erraten. Diese Versuche und die geänderten Einträge werden mit einem “Fuzzy”-Marker markiert, der auf eine Überprüfung hinweist und in der Liste goldgelb angezeigt wird. Dies ist auch nützlich, wenn Du ein Übersetzungsteam hast und jemand versucht, etwas zu schreiben, bei dem er sich nicht sicher ist: Markiereie einfach “Fuzzy”, und jemand anderes überprüft später.
Abschliessend empfehlen wir , “Ansicht > Nicht übersetzte Einträge zuerst” aktiviert zu lassen, da dies sehr hilft, keine Einträge zu vergessen. Von diesem Menü aus kannst Du auch Teile der Benutzeroberfläche öffnen, in denen Du bei Bedarf Kontextinformationen für Übersetzer hinterlassen knnst.
Wenn Du PHP als Modul auf Apache (mod_php
) ausführst, können Probleme mit dem caching der .mo
-Datei auftreten. Dies geschieht beim ersten Lesen. Um die Datei zu aktualisieren, ist möglicherweise ein Serverneustart erforderlich. Unter Nginx und PHP5 genügen in der Regel ein paar Seitenaktualisierungen, um den Übersetzungscache zu aktualisieren. Unter PHP7 ist dies selten erforderlich.
Viele bevorzugen, da es einfacher zu verwenden ist, _()
anstatt gettext()
. Viele benutzerdefinierte i18n-Bibliotheken von Frameworks verwenden ebenfalls etwas Ähnliches wie t()
, um übersetzten Code zu verkürzen.
Dies ist jedoch die einzige Funktion mit einer Verknüpfung. Du kannst Deinem Projekt weitere hinzufügen, z. B. __()
oder _n()
für ngettext()
, oder vielleicht eine ausgefallene _r()
-Funktion, die gettext()
und sprintf()
Aufrufe verbindet.
Andere Bibliotheken, wie z. B. Gettext von php-gettext, bieten ebenfalls solche Hilfsfunktionen.
In diesen Fällen musst Du Gettext mitteilen, wie die Strings aus diesen neuen Funktionen extrahiert werden sollen. Keine Sorge, es ist ganz einfach.
Es handelt sich lediglich um ein Feld in der .po
-Datei bzw. eine Einstellung in Poedit. Im Editor finden Sie diese Option unter “Katalog > Eigenschaften > Quellschlüsselwörter”. Denken Sie daran: Gettext kennt bereits die Standardfunktionen für viele Sprachen. Sei also nicht beunruhigt, wenn die Liste leer erscheint. Du musst dort die Spezifikationen Deiner neuen Funktionen in einem bestimmten Format einfügen:
t()
erstellst, gibt es einfach die Übersetzung für einen String zurück, die Du als t
angeben kannst. Gettext weiß, dass das einzige Funktionsargument der zu übersetzende String ist.__('one user', '%d users', $number)
, wäre die Spezifikation __:1,2
, was bedeutet, dass die erste Form das erste Argument und die zweite Form das zweite Argument ist.
Wenn Ihre Zahl stattdessen das erste Argument ist, wäre die Spezifikation __:2,3
, was bedeutet, dass die erste Form das zweite Argument ist, und so weiter.Nachdem Du diese neuen Regeln in die .po
-Datei aufgenommen hast, werden Deine neuen Zeichenfolgen durch einen neuen Scan genauso einfach wie zuvor eingefügt.
From Wikipedia:
Dependency Injection ist ein Software-Entwurfsmuster, welches fest codierte Abhängigkeiten ersetzt und deren Änderung sowohl zur Laufzeit als auch zur Kompilierzeit ermöglicht.
Dieses Zitat lässt das Konzept viel komplizierter klingen, als es tatsächlich ist. Dependency Injection stellt einer Komponente ihre Abhängigkeiten entweder durch Konstruktor-Injektion, Methodenaufrufe oder das Setzen von Eigenschaften zur Verfügung. So einfach ist das.
Wir können das Konzept anhand eines einfachen, aber naiven Beispiels demonstrieren.
Hier haben wir eine Database
-Klasse, die einen Adapter benötigt, um mit der Datenbank zu kommunizieren. Wir instanziieren den Adapter im Konstruktor und erstellen eine fest Abhängigkeit. Dies erschwert das Testen und bedeutet, dass die Database
-Klasse sehr eng an den Adapter gekoppelt ist.
<?php
namespace Database;
class Database
{
protected $adapter;
public function __construct()
{
$this->adapter = new MySqlAdapter;
}
}
class MysqlAdapter {}
Dieser Code kann umgestaltet werden, um Dependency Injection zu verwenden und so die Abhängigkeit zu lockern. Hier injizieren wir die Abhängigkeit in einen Konstruktor und nutzen die constructor property promotion, sodass sie als Eigenschaft in der gesamten Klasse verfügbar ist:
<?php
namespace Database;
class Database
{
public function __construct(protected MySqlAdapter $adapter)
{
}
}
class MysqlAdapter {}
Jetzt geben wir der Database
-Klasse ihre Abhängigkeit, anstatt sie selbst zu erstellen. Wir könnten sogar eine Methode erstellen, die ein Argument der dependency akzeptiert und es entsprechend festlegt. Wenn die $adapter
-Eigenschaft public
wäre, könnten wir sie direkt festlegen.
Wenn Sie schon einmal etwas über Dependency Injection gelesen haben, sind Ihnen wahrscheinlich die Begriffe “Inversion of Control” oder “Dependency Inversion Principle” begegnet . Dependency Injection löst komplexe Probleme.
Inversion of Control bedeutet, wie der Name schon sagt, die “Umkehrung der Kontrolle” eines Systems, indem die organisatorische Steuerung vollständig von unseren Objekten getrennt gehalten wird. Im Sinne von Dependency Injection bedeutet dies, unsere Abhängigkeiten zu lockern, indem wir sie an anderer Stelle im System steuern und instanziieren.
PHP-Frameworks setzen seit Jahren auf Inversion of Control. Es stellte sich jedoch die Frage, welchen Teil der Steuerung wir invertieren und wohin? Beispielsweise stellen MVC-Frameworks üblicherweise ein Superobjekt oder einen Basiscontroller bereit, den andere Controller erweitern müssen, um auf dessen Abhängigkeiten zugreifen zu können. Dies ist Inversion of Control. Anstatt Abhängigkeiten zu lösen, werden sie bei dieser Methode jedoch einfach verschoben.
Mithilfe der Abhängigkeitsinjektion können wir dieses Problem eleganter lösen, indem wir nur die Abhängigkeiten injizieren, die wir brauchen, wenn wir sie brauchen, ohne dass überhaupt fest codierte Abhängigkeiten erforderlich sind.
SOLID ist ein Akronym für die ersten fünf Prinzipien des objektorientierten Designs (OOD) von Robert C. Martin und steht für:
DasSingle Responsibility Prinzip befasst sich mit Akteuren und High-Level-Architektur. Es besagt: “Eine Klasse sollte nur einen Grund haben, sich zu ändern.” Das bedeutet, dass jede Klasse ausschließlich für einen Teil der von der Software bereitgestellten Funktionalität verantwortlich sein sollte. Der größte Vorteil dieses Ansatzes ist die verbesserte Wiederverwendbarkeit von Code. Indem wir unsere Klasse nur für eine Funktion konzipieren, können wir sie in jedem anderen Programm verwenden (oder wiederverwenden), ohne sie ändern zu müssen.
Das Open/Closed-Prinzip befasst sich mit Klassendesign und Funktionserweiterungen. Es besagt: “Software-Entitäten (Klassen, Module, Funktionen usw.) sollten für Erweiterungen offen, aber für Modifikationen geschlossen sein.” Wir sollten also unsere Module, Klassen und Funktionen so gestalten, dass wir bei Bedarf an neuer Funktionalität den bestehenden Code nicht ändern, sondern neuen Code schreiben, der vom vorhandenen Code genutzt wird. In der Praxis bedeutet das, wir schreiben Klassen, welche Schnittstellen implementieren und einhalten, und dann Typ-Hinweise dafür erstellen anstelle spezifische Klassen.
Der größte Vorteil dieses Ansatzes besteht darin, dass wir unseren Code ganz einfach um Funktionalität erweitern können, ohne den bestehenden Code ändern zu müssen. Dadurch verkürzen wir die QA-Zeit (QA: Quality Assurance). Das Risiko negativer Auswirkungen auf die Applikation wird erheblich reduziert. Wir können neuen Code schneller und zuverlässiger bereitstellen.
Das Liskovsche Substitutionsprinzip befasst sich mit Subtypisierung und Vererbung. Es besagt: “Unterklassen dürfen niemals die Typdefinitionen der übergeordneten Klasse brechen.” Oder, mit Robert C. Martins Worten: “Subtypen müssen durch ihre Basistypen substituierbar sein.”
Wenn wir zum Beispiel eine FileInterface
-Schnittstelle haben, die eine embed()
-Methode FileInterface
definiert, und wir Audio
- und Video
-Klassen haben, die beide die FileInterface
-Schnittstelle implementieren, können wir davon ausgehen, dass die Verwendung der embed()
-Methode immer das gewünschte Ergebnis liefert.
Wenn wir später einePDF
-Klasse oder eine Gist
-Klasse erstellen, die die FileInterface
-Schnittstelle implementiert, wissen und verstehen wir bereits, was die embed()
-Methode bewirkt.
Der größte Vorteil dieses Ansatzes besteht darin, dass wir flexible und leicht konfigurierbare Programme erstellen können, da wir beim Ändern eines Objekts eines Typs (z. B. FileInterface
) in ein anderes, nichts weiter im Programm abändern müssen.
Das Interface-Segregation-Prinzip (ISP) befasst sich mit der Kommunikation zwischen Geschäftslogik und Clients. Es besagt: “Kein Client sollte gezwungen werden, sich auf Methoden zu verlassen, die er nicht nutzt.” Das bedeutet: Anstatt einer einzigen monolithischen Schnittstelle, die alle konformen Klassen implementieren muss, sollten wir stattdessen eine Reihe kleinerer, konzeptspezifischer Schnittstellen bereitstellen, von denen eine konforme Klasse dann eine oder mehrere davon implementiert.
Beispielsweise wäre eine Auto
oder Bus
-Klasse an einer steuerrad()
-Methode interessiert, eine Motorrad
oder Dreirad
-Klasse jedoch nicht. Umgekehrt wäre eine Motorrad
oder Dreirad
-Klasse an einer lenkstange()
-Methode interessiert, eine Auto
oder Bus
-Klasse jedoch nicht.
Es ist nicht erforderlich, dass alle diese Vehikel sowohl steuerrad()
als auch lenkstange()
unterstützen , daher sollten wir die Quellschnittstelle aufteilen.
Das Prinzip der Abhängigkeitsumkehrung (Dependency-Inversion-Prinzip) entfernt Hardlinks zwischen diskreten Klassen, um durch die Übergabe einer anderen Klasse neue Funktionalitäten zu nutzen. Es besagt: “Verlasse dich auf Abstraktionen. Verlasse dich nicht auf Konkretionen.” Vereinfacht ausgedrückt: Unsere Abhängigkeiten (dependencies) sollen Schnittstellen/Verträge oder abstrakte Klassen anstatt konkreter Implementierungen sein. Wir können das obige Beispiel leicht umgestalten, um diesem Prinzip zu folgen.
<?php
namespace Database;
class Database
{
public function __construct(protected AdapterInterface $adapter)
{
}
}
interface AdapterInterface {}
class MysqlAdapter implements AdapterInterface {}
Dass die Database
-Klasse nun von einer Schnittstelle und nicht von einer Konkretion abhängt, hat mehrere Vorteile.
Nehmen wir an, wir arbeiten in einem Team und ein Kollege arbeitet gerade am Adapter. In unserem ersten Beispiel müssten wir warten, bis der Kollege den Adapter fertiggestellt hat, bevor wir ihn für unsere Unit-Tests simulieren können. Da die Abhängigkeit nun eine Schnittstelle/ein Vertrag ist, können wir diese Schnittstelle problemlos simulieren, da wir wissen, dass unser Kollege den Adapter basierend auf diesem Vertrag erstellt.
Ein noch größerer Vorteil dieser Methode ist die deutlich bessere Skalierbarkeit unseres Codes. Sollten wir uns nach einem Jahr für die Migration zu einem anderen Datenbanktyp entscheiden, können wir einen Adapter schreiben, der die ursprüngliche Schnittstelle implementiert und diese stattdessen einfügt (inject). Ein weiteres Refactoring ist nicht erforderlich, da wir sicherstellen können, dass der Adapter den von der Schnittstelle festgelegten Vertrag einhält.
Das Erste, was Du über Dependency Injection Container wissen solltest, ist, dass sie nicht dasselbe sind wie Dependency Injection. Ein Container ist ein praktisches Dienstprogramm zur Implementierung von Dependency Injection. Er kann jedoch missbraucht werden, um ein Anti-Pattern, Service Location, zu implementieren. Das Einfügen eines DI-Containers als Service Locator in Ihre Klassen erzeugt möglicherweise eine stärkere Abhängigkeit vom Container als die zu ersetzende Abhängigkeit. Dadurch wird Dein Code auch deutlich weniger transparent und letztendlich schwieriger zu testen.
Die meisten modernen Frameworks verfügen über einen eigenen Dependency Injection Container, der es Ihnen ermöglicht, Ihre Abhängigkeiten durch Konfiguration miteinander zu verknüpfen. In der Praxis bedeutet dies, dass Du Anwendungscode schreiben kannst, der so sauber und entkoppelt ist wie das Framework, auf dem er basiert.
Dein PHP-Code verwendet häufig eine Datenbank, um Informationen zu speichern. Du hast verschiedene Möglichkeiten, eine Verbindung zu Deiner Datenbank herzustellen und mit ihr zu interagieren. Bis PHP 5.1.0 wurde die Verwendung nativer Treiber wie mysqli, pgsql, mssql usw. empfohlen.
Native Treiber sind ideal, wenn Du in ihrer Anwendung nur eine Datenbank verwendest. Wenn Du jedoch beispielsweise MySQL und ein wenig MSSQL verwendest oder eine Verbindung zu einer Oracle-Datenbank herstellen musst, kannst Du nicht dieselben Treiber verwenden. Du musst für jede Datenbank eine brandneue API erlernen – und das kann nervig sein.
Die mysql-Erweiterung für PHP ist unglaublich alt und wurde durch zwei andere Erweiterungen ersetzt:
Die Entwicklung von mysql wurde nicht nur schon vor langer Zeit eingestellt, sondern es wurde in PHP 7.0 auch offiziell entfernt.
Um nicht in den php.ini
-Einstellungen nach dem verwendeten Modul suchen zu müssen, können Sie in Ihrem bevorzugten Editor nach mysql_*
suchen. Wenn Funktionen wie mysql_connect()
und mysql_query()
angezeigt werden, wird mysql
verwendet.
Auch wenn Sie PHP 7.x oder höher noch nicht verwenden, führt ein nicht frühzeitiges Upgrade zu größeren Schwierigkeiten, wenn das PHP-Upgrade tatsächlich erfolgt. Am besten ersetzen Sie mysql in Ihren Anwendungen zügig durch mysqli or PDO, um später nicht in Eile zu geraten.
Generell sollte mysql nicht mehr verwendet werden.
Wenn Sie von mysql auf mysqli aktualisieren, hüten Sie sich vor oberflächlichen Upgrade-Anleitungen, die vorschlagen, einfach mysql_*
mit mysqli_*
zu ersetzen.
Dies ist nicht nur eine grobe Vereinfachung, sondern lässt auch die Vorteile von mysqli außer Acht, wie z. B. die Parameterbindung, welche auch in PDO verfügbar ist.
PHP Data Objects (PDO) ist eine Bibliothek zur abstrakten Datenbankverbindung, welche eine gemeinsame Schnittstelle für die Kommunikation mit vielen verschiedenen Datenbanken bietet und seit Version 5.1.0 bestandteil von PHP ist. Beispielsweise können Sie im Wesentlichen identischen Code für die Schnittstelle zu MySQL oder SQLite verwenden:
<?php
// PDO + MySQL
$pdo = new PDO('mysql:host=example.com;dbname=database', 'user', 'password');
$statement = $pdo->query("SELECT some_field FROM some_table");
$row = $statement->fetch(PDO::FETCH_ASSOC);
echo htmlentities($row['some_field']);
// PDO + SQLite
$pdo = new PDO('sqlite:/path/db/foo.sqlite');
$statement = $pdo->query("SELECT some_field FROM some_table");
$row = $statement->fetch(PDO::FETCH_ASSOC);
echo htmlentities($row['some_field']);
PDO übersetzt nicht Ihre SQL-Abfragen und emuliert auch keine fehlenden Funktionen. Es dient lediglich zur Verbindung mit verschiedenen Datenbanktypen mit derselben API.
Noch wichtiger ist, dass Du mit PDO
fremde Eingaben (z. B. IDs) sicher in Deine SQL-Abfragen einfügen kannst, ohne Dir über SQL-Injection-Angriffe auf die Datenbank Gedanken machen zu müssen. Dies ist mithilfe von PDO-Anweisungen und gebundenen Parametern möglich.
Nehmen wir an, ein PHP-Skript erhält eine numerische ID als Abfrageparameter. Diese ID soll verwendet werden, um einen Benutzerdatensatz aus einer Datenbank abzurufen. Folgendermaßen solle man es nicht machen:
<?php
$pdo = new PDO('sqlite:/path/db/users.db');
$pdo->query("SELECT name FROM users WHERE id = " . $_GET['id']); // <-- NO!
Das ist schrecklicher Code. Du fügst einen rohen Abfrage-Parameter in eine SQL-Abfrage ein.
Das führt im Handumdrehen zu Hackerangriffen, die sich SQL-Injection nennen.
Stell Dir vor, ein Hacker übergibt einen erfinderischen id
-Parameter, indem er eine URL wie http://domain.com/?id=1%3BDELETE+FROM+users
aufruft.
Dadurch wird die Variable $_GET['id']
auf 1;DELETE
FROM users
gesetzt, was alle Ihre Benutzer löscht!
Du solltest stattdessen die ID-Eingabe mit PDO-gebundenen Parametern bereinigen.
<?php
$pdo = new PDO('sqlite:/path/db/users.db');
$stmt = $pdo->prepare('SELECT name FROM users WHERE id = :id');
$id = filter_input(INPUT_GET, 'id', FILTER_SANITIZE_NUMBER_INT); // <-- filter your data first (see [Data Filtering](#data_filtering)), especially important for INSERT, UPDATE, etc.
$stmt->bindParam(':id', $id, PDO::PARAM_INT); // <-- Automatically sanitized for SQL by PDO
$stmt->execute();
Dies ist der korrekte Code. Er verwendet einen gebundenen Parameter in einer PDO-Anweisung. Dies verhindert den Eintrag der Fremdeingabe-ID in die Datenbank und schützt so vor potenziellen SQL-Injection-Angriffen.
Bei Schreibvorgängen wie INSERT oder UPDATE ist es besonders wichtig, zunächst die Daten zu filtern und für andere Zwecke (z. B. Entfernung von HTML-Tags, JavaScript usw.) zu bereinigen. PDO bereinigt die Daten nur für SQL, nicht für Deine Anwendung.
Beachte auch, dass Datenbankverbindungen Ressourcen verbrauchen. Es kam schon vor, dass Ressourcen erschöpft waren, wenn Verbindungen nicht implizit geschlossen wurden. Dies war jedoch in anderen Sprachen häufiger der Fall. Mit PDO kannst Du die Verbindung implizit schließen, indem Du das Objekt destroyst und sicherstellst, dass alle verbleibenden Referenzen darauf gelöscht, d. h. auf NULL gesetzt werden. Wenn Du das nicht explizit machst, schließt PHP die Verbindung automatisch, wenn Dein Skript endet – es sei denn, Du verwendest persistente Verbindungen.
Wenn Entwickler anfangen, PHP zu lernen, vermischen sie häufig ihre Datenbankinteraktion mit ihrer Präsentationslogik und verwenden Code, der beispielsweise so aussehen könnte:
<ul>
<?php
foreach ($db->query('SELECT * FROM table') as $row) {
echo "<li>".$row['field1']." - ".$row['field1']."</li>";
}
?>
</ul>
Dies ist aus vielerlei Gründen eine schlechte Vorgehensweise, vor allem, weil es schwierig zu debuggen, zu testen und zu lesen ist und weil es zu einer Ausgabe vieler Felder kommt, wenn man hier keine Limits festlegt.
Zwar gibt es hierfür viele andere Lösungen – je nachdem, ob Sie OOP oder funktionale Programmierung bevorzugen –, doch muss es ein gewisses Element der Trennung geben.
Betrachte den grundlegendsten Schritt:
<?php
function getAllFoos($db) {
return $db->query('SELECT * FROM table');
}
$results = getAllFoos($db);
foreach ($results as $row) {
echo "<li>".$row['field1']." - ".$row['field1']."</li>"; // BAD!!
}
Das ist ein guter Anfang. Speichere diese beiden Elemente in zwei verschiedene Dateien und Du erhältst eine saubere Trennung.
Erstelle eine Klasse, in die Du diese Methode einfügst, und Du hast ein „Modell“. Erstelle eine einfache
.php
-Datei, in welche Du die Präsentationslogik einfügst, und Du erhälst eine „View (Ansicht)“,
die fast MVC entspricht – einer gängigen OOP-Architektur für die meisten Frameworks.
foo.php
<?php
$db = new PDO('mysql:host=localhost;dbname=testdb;charset=utf8mb4', 'username', 'password');
// Make your model available
include 'models/FooModel.php';
// Create an instance
$fooModel = new FooModel($db);
// Get the list of Foos
$fooList = $fooModel->getAllFoos();
// Show the view
include 'views/foo-list.php';
models/FooModel.php
<?php
class FooModel
{
public function __construct(protected PDO $db)
{
}
public function getAllFoos() {
return $this->db->query('SELECT * FROM table');
}
}
views/foo-list.php
<?php foreach ($fooList as $row): ?>
<li><?= $row['field1'] ?> - <?= $row['field1'] ?></li>
<?php endforeach ?>
Dies entspricht im Wesentlichen dem, was die meisten modernen Frameworks tun, wenn auch etwas manueller. Du musst dies möglicherweise nicht jedes Mal tun, aber eine zu starke Vermischung von Präsentationslogik und Datenbankinteraktion kann ein echtes Problem darstellen, wenn Du Deine Anwendung Unit-Tests unterziehen möchtest.
Viele Frameworks bieten eine eigene Abstraktionsschicht, die auf PDO aufsetzen kann, aber nicht muss. Diese emulieren oft Funktionen für ein Datenbanksystem, die in einem anderen fehlen, indem sie Ihre Abfragen in PHP-Methoden einbetten und Dir so eine echte Datenbankabstraktion anstelle der von PDO bereitgestellten Verbindungsabstraktion bieten. Dies bedeutet natürlich einen gewissen Mehraufwand, aber wenn Du eine portable Anwendung erstellst, die sowohl mit MySQL, PostgreSQL und auch SQLite kompatibel sein muss, lohnt sich dieser Mehraufwand im Interesse der Code-Sauberkeit.
Einige Abstraktionsebenen wurden unter Verwendung der PSR-0 bzw. PSR-4 Namespace-Standards erstellt und können daher in jeder beliebigen Anwendung installiert werden:
Templates bieten eine praktische Möglichkeit, Deine Controller- und Domänenlogik von Deiner Präsentationslogik zu trennen. Templates enthalten typischerweise das HTML Deiner Anwendung, können aber auch für andere Formate wie XML verwendet werden. Vorlagen werden oft als „Views (Ansichten)“ bezeichnet und bilden einen Teil der zweiten Komponente des Model-View-Controller (MVC) Software-Architektur-Pattern.
Der Hauptvorteil von Templates liegt in der klaren Trennung zwischen der Präsentationslogik und dem Rest Deiner Anwendung. Templates sind ausschließlich für die Anzeige formatierter Inhalte zuständig. Du verwendest sie nicht für die Datensuche, Persistenz oder andere komplexere Aufgaben. Dies führt zu saubererem, besser lesbarem Code, was besonders in einer Teamumgebung hilfreich ist, in der Entwickler am serverseitigen Code (Controller, Modelle) und Designer am clientseitigen Code (Markup) arbeiten.
Vorlagen verbessern außerdem die Organisation von Präsentationscode. Sie werden üblicherweise in einem Ordner „Views“ abgelegt und jeweils in einer eigenen Datei definiert. Dieser Ansatz fördert die Wiederverwendung von Code, indem größere Codeblöcke in kleinere, wiederverwendbare Teile, sogenannte Partials, zerlegt werden. So können beispielsweise die Kopf- und Fußzeile Deiner Site jeweils als Template definiert und vor und nach jedem Page Template eingefügt werden.
Je nach verwendeter Bibliothek können Templates mehr Sicherheit bieten, indem sie benutzergenerierten Inhalt automatisch maskieren. Einige Bibliotheken bieten sogar Sandboxing an, bei dem Vorlagendesigner nur auf Whitelist-Variablen und -Funktionen zugreifen können.
Einfache PHP-Templates sind Vorlagen, die nativen PHP-Code verwenden. Sie bieten sich an, da PHP selbst eine Template Sprache ist. Das bedeutet, dass Du PHP-Code in anderen Code, beispielsweise HTML, integrieren kannst. Dies ist für PHP-Entwickler von Vorteil, da sie keine neue Syntax erlernen müssen, die ihnen zur Verfügung stehenden Funktionen kennen und ihre Code-Editoren bereits über integrierte PHP-Syntaxhervorhebung und Autovervollständigung verfügen. Außerdem sind einfache PHP-Templates in der Regel sehr schnell, da keine Kompilierungsphase erforderlich ist.
Jedes moderne PHP-Framework verwendet ein Template-System, wobei die meisten standardmäßig reines PHP verwenden. Außerhalb von Frameworks erleichtern Bibliotheken wie Plates oder Aura.View die Arbeit mit reinen PHP-Templates, indem sie moderne Template-Funktionen wie Vererbung, Layouts und Erweiterungen bieten.
unter Verwendung der Plates library:
<?php // user_profile.php ?>
<?php $this->insert('header', ['title' => 'User Profile']) ?>
<h1>User Profile</h1>
<p>Hello, <?=$this->escape($name)?></p>
<?php $this->insert('footer') ?>
unter Verwendung der Plates library:
<?php // template.php ?>
<html>
<head>
<title><?=$title?></title>
</head>
<body>
<main>
<?=$this->section('content')?>
</main>
</body>
</html>
<?php // user_profile.php ?>
<?php $this->layout('template', ['title' => 'User Profile']) ?>
<h1>User Profile</h1>
<p>Hello, <?=$this->escape($name)?></p>
Obwohl sich PHP zu einer ausgereiften, objektorientierten Sprache entwickelte, hat es sich als Template-Sprache kaum verbessert. Kompilierte Templates wie Twig, Brainy, or Smarty* füllen diese Lücke mit einer neuen, speziell auf Template-Entwicklung ausgerichteten Syntax. Von automatischem Escapen, über Vererbung bis hin zu vereinfachten Kontrollstrukturen sind kompilierte Templates einfacher zu schreiben, übersichtlicher zu lesen und sicherer in der Anwendung. Kompilierte Templates können sogar sprachübergreifend genutzt werden, wofür Mustache ein gutes Beispiel ist. Da diese Templates kompiliert werden müssen, kommt es zu leichten Performance-Einbußen, die jedoch bei korrektem Caching minimal sind.
*Smarty bietet zwar automatisches Escapen, diese Funktion ist jedoch standardmäßig NICHT aktiviert.
unter Verwendung der Twig library.
{% include 'header.html' with {'title': 'User Profile'} %}
<h1>User Profile</h1>
<p>Hello, {{ name }}</p>
{% include 'footer.html' %}
unter Verwendung der Twig library.
// template.html
<html>
<head>
<title>{% block title %}{% endblock %}</title>
</head>
<body>
<main>
{% block content %}{% endblock %}
</main>
</body>
</html>
// user_profile.html
{% extends "template.html" %}
{% block title %}User Profile{% endblock %}
{% block content %}
<h1>User Profile</h1>
<p>Hello, {{ name }}</p>
{% endblock %}
In vielen “exception-heavy” Programmiersprachen wird bei jedem Fehler eine Exception ausgelöst. Dies ist sicherlich eine praktikable Vorgehensweise, PHP hingegen ist “exception-light”. Obwohl es Exceptions gibt und der Kernel diese immer mehr bei der Arbeit mit Objekten verwendet, versucht PHP selbst größtenteils, das Ausführen unabhängig vom Ereignis fortzusetzen. Ausnahme: Es tritt ein schwerwiegender Fehler (fatal error) auf.
Zum Beispiel:
$ php -a
php > echo $foo;
Notice: Undefined variable: foo in php shell code on line 1
Dies ist nur ein Hinweisfehler, PHP macht problemlos weiter. Dies kann für Benutzer von “exception-heavy” Sprachen verwirrend sein, da beispielsweise der Verweis auf eine fehlende Variable in Python eine Exception auslöst:
$ python
>>> print foo
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'foo' is not defined
Der einzige wirkliche Unterschied besteht darin, dass Python bei jeder Kleinigkeit ausflippt, sodass Entwickler absolut sicher sein können, dass jedes potenzielle Problem oder jeder Grenzfall erkannt wird, während PHP die Verarbeitung fortsetzt, bis etwas Extremes passiert. In diesem Fall wird ein Fehler ausgegeben und gemeldet.
PHP kennt mehrere Schweregrade für Fehler. Die drei häufigsten Meldungstypen sind Fehler, Hinweise und Warnungen. Diese haben unterschiedliche FehlerstufenE_ERROR
, E_NOTICE
, und E_WARNING
.
Fehler (Error) sind schwerwiegende Laufzeitfehler, die in der Regel durch Fehler im Code verursacht werden und behoben werden müssen, da sie die Ausführung von PHP verhindern.
Hinweise (Notice) sind ratsame Warnmeldungen, die durch Code verursacht werden, der während der Skriptausführung Probleme verursachen kann (die Ausführung wird nicht angehalten).
Warnungen sind nicht schwerwiegende Fehler; die Ausführung des Skripts wird nicht angehalten.
Eine weitere Art von Fehlermeldungen, die zur Kompilierzeit gemeldet werden, sind E_STRICT
-Meldungen.
Diese Meldungen dienen dazu, Änderungen an Deinem Code vorzuschlagen, um optimale Interoperabilität und Vorwärtskompatibilität mit zukünftigen PHP-Versionen zu gewährleisten.
Die Fehlerberichterstattung kann über PHP-Einstellungen und/oder PHP-Funktionsaufrufe geändert werden.
Mit der integrierten PHP-Funktion error_reporting()
kannst Du die Fehlerstufe für die Dauer der Skriptausführung festlegen, indem Du eine der vordefinierten Fehlerstufenkonstanten übergibst.
Wenn Du also nur Fehler und Warnungen, aber keine Hinweise sehen möchtest, kannst Du Folgendes konfigurieren:
<?php
error_reporting(E_ERROR | E_WARNING);
Du kannst auch steuern, ob Fehler auf dem Bildschirm angezeigt (für die Entwicklung) oder ausgeblendet und protokolliert werden (für die Produktion). Weitere Informationen hierzu findest Du im Abschnitt Error Reporting(Fehlerberichterstattung).
Du kannst PHP auch anweisen, bestimmte Fehler mit dem Fehlerkontrolloperator @
zu unterdrücken.
Setzen Sie diesen Operator an den Anfang eines Ausdrucks, und alle Fehler, die eine direkte Folge des Ausdrucks sind, werden unterdrückt.
<?php
echo @$foo['bar'];
Dieser Code gibt $foo['bar']
aus, wenn es existiert, aber einfach einen Nullwert und keine Ausgabe,
wenn die Variable $foo
oder der Schlüssel (key) 'bar'
nicht existiert.
Ohne den Fehlerkontrolloperator könnte dieser Ausdruck eine Fehler PHP Notice: Undefined variable: foo
oder
einen Fehler PHP Notice: Undefined index: bar
erzeugen.
Dies mag zwar eine gute Idee sein, bringt aber einige unerwünschte Nachteile mit sich.
PHP verarbeitet Ausdrücke mit einem @
weniger performant als Ausdrücke ohne ein @
.
Vorzeitige Optimierung mag die Ursache aller Programmier-Diskussionen sein, aber wenn die Performance
für Deine Anwendung/Bibliothek besonders wichtig ist, musst Du die Auswirkungen des Fehlerkontrolloperators auf die Performance verstehen.
Zweitens schluckt der Fehlerkontrolloperator den Fehler vollständig. Der Fehler wird weder angezeigt noch ins Fehlerprotokoll geschrieben. Außerdem gibt es in Standard-/Produktions-PHP-Systemen keine Möglichkeit, den Fehlerkontrolloperator abzuschalten. Auch wenn Sie mit der Annahme, dass der angezeigte Fehler harmlos ist, richtig liegen, wird ein anderer, weniger harmloser Fehler genauso leise sein und damit unauffindbar bleiben.
Wenn es eine Möglichkeit gibt, den Fehlerunterdrückungsoperator zu vermeiden, solltest Du das in Betracht ziehen. Unser obiger Code könnte beispielsweise wie folgt umgeschrieben werden:
<?php
// Null Coalescing Operator
echo $foo['bar'] ?? '';
Ein Beispiel, bei dem Fehlerunterdrückung sinnvoll sein kann, ist, wenn fopen()
eine zu ladende Datei nicht finden kann.
Sie könnten vor dem Laden prüfen, ob die Datei existiert.
Wird die Datei jedoch nach der Prüfung und vor dem Laden gelöscht(was unmöglich klingt, aber passieren kann),
gibt fopen()
false zurück und löst einen Fehler aus.
Dies ist eigentlich ein Problem, das PHP lösen sollte, aber in diesem Fall erscheint Fehlerunterdrückung als die einzig praktikable Lösung.
Wie bereits erwähnt, gibt es in einem Standard-PHP-System keine Möglichkeit, den Fehlerkontrolloperator zu deaktivieren.
Xdebug verfügt jedoch über einexdebug.scream
ini-Einstellung, welche den Fehlerkontrolloperator deaktiviert.
Du kannst dies über Ihre php.ini
-Datei wie folgt einstellen.
xdebug.scream = On
Du kannst diesen Wert auch zur Laufzeit mit der ini_set
-Funktion festlegen
<?php
ini_set('xdebug.scream', '1')
Dies ist besonders nützlich, wenn Du Code debuggst und vermutest, dass ein informativer Fehler unterdrückt wurde. Verwende „scream“ mit Vorsicht und als temporäres Debugging-Tool. Viele PHP-Bibliotheken funktionieren möglicherweise nicht, wenn der Fehlerkontrolloperator deaktiviert ist.
PHP ist durchaus in der Lage, eine “ausnahmelastige” Programmiersprache zu sein.
Für die Umstellung sind nur wenige Codezeilen erforderlich.
Grundsätzlich kannst Du Deine “Fehler” als “Ausnahmen” mithilfe der ErrorException
-Klasse auslösen,
welche die Exception
-klasse erweitert.
Dies ist eine gängige Praxis, welche von vielen modernen Frameworks wie Symfony und Laravel implementiert wird. Im Debug-Modus (oder Dev-Modus) zeigen beide Frameworks einen schönen und sauberen Stack-Trace (Stapelverfolgung) an.
Es sind auch einige Pakete für eine bessere Fehler- und Ausnahmebehandlung und Fehler-Berichterstattung verfügbar. Wiez.B. Whoops!, das mit der Standardinstallation von Laravel ausgeliefert wird und in jedem Framework verwendet werden kann.
Indem Du Fehler in der Entwicklung als Exceptions auslöst, kannst Du sie besser verarbeiten als das üblich. Wenn während der Entwicklung eine Exception auftritt, kannst Du diese in eine Catch-Anweisung mit spezifischen Anweisungen zum Umgang mit der Situation einbetten. Jede sofort abgefangene Exception macht Deine Anwendung ein wenig robuster.
Weitere Informationen hierzu und Einzelheiten zur Verwendung von ErrorException
bei der Fehlerbehandlung finden Sie unter ErrorException Klasse.
Ausnahmen sind ein Standardbestandteil der meisten gängigen Programmiersprachen, werden von PHP-Programmierern jedoch oft übersehen. Sprachen wie Ruby enthalten extrem viele Ausnahmen. Wenn also etwas schiefgeht, z. B. eine fehlgeschlagene HTTP-Anfrage, eine fehlerhafte DB-Abfrage oder sogar ein nicht gefundenes Bild-Asset, wird von Ruby (oder den verwendeten Gems) eine Ausnahme auf dem Bildschirm ausgegeben, sodass Sie sofort wissen, dass ein Fehler vorliegt.
PHP selbst ist diesbezüglich recht nachlässig, und ein Aufruf von file_get_contents()
führt in der Regel nur zu einem FALSE
und einer Warnung.
Viele ältere PHP-Frameworks wie CodeIgniter geben lediglich ein “false” zurück, protokollieren eine Meldung in ihren proprietären Protokollen und ermöglichen Dir möglicherweise die Verwendung einer Methode wie, $this->upload->get_error()
um zu sehen, was schiefgelaufen ist.
Das Problem hierbei ist, dass Du nach einem Fehler suchen und die Dokumentation überprüfen musst, um die Fehlermethode für diese Klasse zu ermitteln, anstatt sie deutlich sichtbar zu machen.
Ein weiteres Problem ist, wenn Klassen automatisch einen Fehler ausgeben und den Prozess beenden. Dadurch wird verhindert, dass andere Entwickler diesen Fehler dynamisch behandeln können. Exceptions sollten ausgelöst werden, um einen Entwickler auf einen Fehler aufmerksam zu machen. Dieser kann dann entscheiden, wie er damit umgeht. Beispiel:
<?php
$email = new Fuel\Email;
$email->subject('My Subject');
$email->body('How the heck are you?');
$email->to('guy@example.com', 'Some Guy');
try
{
$email->send();
}
catch(Fuel\Email\ValidationFailedException $e)
{
// The validation failed
}
catch(Fuel\Email\SendingFailedException $e)
{
// The driver could not send the email
}
finally
{
// Executed regardless of whether an exception has been thrown, and before normal execution resumes
}
Die generische Exception
-Klasse bietet dem Entwickler nur sehr wenig Debugging-Kontext.
Um dies zu beheben, kann jedoch durch Unterklassenbildung der generischen Exception
-Klasse ein spezialisierter Exception
-Typ erstellt werden:
<?php
class ValidationException extends Exception {}
Dies bedeutet, dass Du mehrere Catch-Blöcke hinzufügen und verschiedene Exceptions unterschiedlich behandeln kannst. Dies kann zur Erstellung vieler benutzerdefinierter Ausnahmen führen, von denen einige mithilfe der in der SPL-Erweiterung bereitgestellten SPL-Ausnahmen hätten vermieden werden können .
Wenn Du beispielsweise die Magic-Method __call()
verwendest und eine ungültige Methode angefordert wird,
kannst Du einfach – anstatt eine vage Standard-Exception auszulösen oder eine benutzerdefinierte Exception nur hierfür zu erstellen – könntest Du einfach throw new BadMethodCallException;
.
Die beste Ressource, die ich zum Thema PHP-Sicherheit gefunden habe, ist The 2018 Guide to Building Secure PHP Software von Paragon Initiative.
Für jeden PHP-Entwickler ist es sehr wichtig , die Grundlagen der Sicherheit von Webanwendungen zu erlernen, die sich in eine Handvoll allgemeiner Themen unterteilen lassen:
Code-Daten-Trennung. Wenn Daten als Code ausgeführt werden, kommt es zu SQL-Injection, Cross-Site-Scripting, lokaler/Remote-Dateieinbindung usw. Wenn Code als Daten gedruckt wird, kommt es zu Informationslecks (Offenlegung des Quellcodes oder im Fall von C-Programmen genügend Informationen, um ASLR zu umgehen ).
Es gibt Kriminelle, die Deine Webanwendung ausnutzen wollen. Treffe daher unbedingt die notwendigen Vorkehrungen, um die Sicherheit Deiner Webanwendung zu erhöhen. Glücklicherweise haben die Experten von The Open Web Application Security Project eine umfassende Liste bekannter Sicherheitsprobleme und Schutzmaßnahmen zusammengestellt. Diese Lektüre ist Pflichtlektüre für sicherheitsbewusste Entwickler. Survive The Deep End: PHP Security von Padraic Brady ist ein weiterer guter Leitfaden zur Sicherheit von Webanwendungen für PHP.
Irgendwann erstellt jeder eine PHP-Anwendung, die auf Benutzeranmeldung basiert. Benutzernamen und Passwörter werden in einer Datenbank gespeichert und später zur Authentifizierung der Benutzer bei der Anmeldung verwendet.
Es ist wichtig, dass wir Passwörter vor dem Speichern ordnungsgemäß hashen. Hashing und Verschlüsselung sind zwei sehr unterschiedliche Dinge, die oft verwechselt werden.
Hashing ist eine unumkehrbare Einwegfunktion. Es entsteht eine Zeichenfolge mit fester Länge, die nicht rückgängig gemacht werden kann. Du kannst also einen Hash mit einem anderen vergleichen, um festzustellen, ob beide aus derselben Quellzeichenfolge stammen, aber Du kannst die ursprüngliche Zeichenfolge nicht wieder ermitteln. Wenn Passwörter nicht gehasht werden und ein unbefugter Dritter auf Deine Datenbank Zugriff erlangt, sind alle Benutzerkonten kompromittiert!
Im Gegensatz zum Hashing ist die Verschlüsselung umkehrbar (vorausgesetzt, Du verfügen über den Schlüssel). Verschlüsselung ist in anderen Bereichen nützlich, stellt jedoch keine gute Strategie für die sichere Speicherung von Passwörtern dar.
Passwörter sollten außerdem individuell salted (gesalzen werden), indem jedem Passwort vor dem Hashing eine zufällige Zeichenfolge hinzugefügt wird. Dies verhindert Wörterbuchangriffe und die Verwendung von “Rainbow Tables” (eine umgekehrte Liste kryptografischer Hashes für gängige Passwörter).
Hashing und Salting sind von entscheidender Bedeutung, da Benutzer häufig dasselbe Passwort für mehrere Dienste verwenden und die Passwortqualität schlecht sein kann.
Darüber hinaus solltest Du einen speziellen Passwort-Hashing-Algorithmus anstelle einer schnellen, universellen kryptografischen Hash-Funktion (z. B. SHA256) verwenden. Die kurze Liste der zulässigen Passwort-Hashing-Algorithmen (Stand: Juni 2018) lautet:
Glücklicherweise ist dies heutzutage mit PHP einfach.
Hashing von Paswprtern mit password_hash
password_hash()
wurde mit PHP 5.5 eingeführt.
Derzeit verwendet es BCrypt, den derzeit stärksten von PHP unterstützten Algorithmus.
Es wird jedoch in Zukunft aktualisiert, um bei Bedarf weitere Algorithmen zu unterstützen.
Die password_compat
-Bibliothek wurde erstellt, um die Vorwärtskompatibilität für PHP >= 5.3.7 zu gewährleisten.
Im Folgenden hashen wir eine Zeichenfolge und vergleichen den Hash dann mit einer neuen Zeichenfolge. Da unsere beiden Quellzeichenfolgen unterschiedlich sind (‘secret-password’ vs. ‘bad-password’), schlägt diese Anmeldung fehl.
<?php
require 'password.php';
$passwordHash = password_hash('secret-password', PASSWORD_DEFAULT);
if (password_verify('bad-password', $passwordHash)) {
// Correct Password
} else {
// Wrong password
}
password_hash()
nimmt Dir das Salting des Passworts ab. Das Salt wird zusammen mit dem Algorithmus und den “Kosten” als Teil des Hashs gespeichert. password_verify()
extrahiert dies, um zu bestimmen, wie das Passwort überprüft werden soll, sodass Sie kein separates Datenbankfeld zum Speichern der Salts benötigt wird.
Vertrauen Sie niemals (niemals) fremden Eingaben in Ihrem PHP-Code.
(All user input is evil, until proved otherwise.)
Überprüfe und validiere fremde Eingaben immer, bevor Du sie im Code verwendest.
Die Funktionen filter_var()
und filter_input()
können Text bereinigen und Textformate (z. B. E-Mail-Adressen) validieren.
Fremdeingaben können alles sein:$_GET
oder $_POST
Formulareingabedaten, einige Werte in der $_SERVER
Superglobalen oder der HTTP-Anforderungstext über fopen('php://input', 'r')
.
Denke daran, dass Fremdeingaben nicht auf vom Benutzer übermittelte Formulardaten beschränkt sind.
Hoch- und heruntergeladene Dateien, Sitzungswerte, Cookie-Daten und Daten von Webdiensten Dritter sind ebenfalls Fremdeingaben.
Obwohl Fremddaten gespeichert, kombiniert und später abgerufen werden können, handelt es sich dennoch um Fremdeingaben. Frage Dich jedes Mal, wenn Du Daten verarbeitest, ausgibst, verkettest oder in Deinen Code einbindest, ob die Daten richtig gefiltert sind und ob ihnen wirklich vertraut werden kann.
Daten können je nach Zweck unterschiedlich gefiltert werden.
Wenn beispielsweise ungefilterte Fremdeingaben in die HTML-Seitenausgabe übernommen werden, können diese HTML und JavaScript auf Deiner Website ausführen!
Dies wird als Cross-Site-Scripting (XSS) bezeichnet und kann ein sehr gefährlicher Angriff sein.
Eine Möglichkeit, XSS zu vermeiden, besteht darin, alle benutzergenerierten Daten vor der Ausgabe auf Deienr Seite zu bereinigen, indem Du HTML-Tags mit derstrip_tags()
-Funktion entfernst oder Zeichen mit besonderer Bedeutung mit den Funktionen htmlentities()
oder htmlspecialchars()
in die entsprechenden HTML-Entitäten maskieren lässt.
Ein weiteres Beispiel ist die Übergabe von auszuführenden Optionen über die Befehlszeile.
Dies kann äußerst gefährlich sein (und ist normalerweise keine gute Idee), aber Du kannst die integrierte escapeshellarg()
-Funktion verwenden, um die Argumente des ausgeführten Befehls zu bereinigen.
Ein letztes Beispiel ist die Entgegennahme von Fremdeingaben, um eine Datei aus dem Dateisystem zu laden.
Dies kann ausgenutzt werden, indem der Dateiname in einen Dateipfad geändert wird. Sie müssen "/"
, "../"
, Nullbytes oder andere Zeichen aus dem Dateipfad entfernen,
damit keine versteckten, nicht öffentlichen oder sensiblen Dateien geladen werden können.
Durch die Sanitization werden ungültige oder unsichere Zeichen aus der Fremdeingabe entfernt (oder entzogen).
Beispielsweise solltest Du fremde Eingaben bereinigen, bevor Du sie in HTML einbindes oder in eine reine SQL-Abfrage einfügst. Wenn Du gebundene Parameter mit PDO verwendest, wird die Eingabe automatisch bereinigt.
Manchmal ist es erforderlich, beim Einfügen in die HTML-Seite einige sichere HTML-Tags in der Eingabe zuzulassen. Dies ist sehr schwierig und wird oft durch die Verwendung anderer, eingeschränkterer Formatierungen wie Markdown oder BBCode vermieden, obwohl es hierfür Whitelist-Bibliotheken wie HTML Purifier gibt.
Daten von Benutzern oder anderen nicht vertrauenswürdigen Quellen zu unserialize()
ist gefährlich.
Böswillige Benutzer können dadurch Objekte (mit benutzerdefinierten Eigenschaften) instanziieren, deren Destruktoren ausgeführt werden, auch wenn die Objekte selbst nicht verwendet werden.
Vermeide daher die Deserialisierung nicht vertrauenswürdiger Daten.
Verwende ein sicheres, standardmäßiges Datenaustauschformat wie JSON (via json_decode
und json_encode
), wenn Du serialisierte Daten an den Benutzer übergeben musst.
Durch die Validierung wird sichergestellt, dass die Eingabe Deinen Erwartungen entspricht. Beispielsweise solltest Du bei eine E-Mail-Adresse, eine Telefonnummer oder das Alter validieren, wenn Du eine Benutzer-Registrierung verarbeitest.
Beim Erstellen von Konfigurationsdateien für Deine Anwendungen empfehlen Best Practices, eine der folgenden Methoden zu befolgen:
.php
. Dadurch wird sichergestellt, dass das Skript auch bei direktem Zugriff nicht als einfacher Text ausgegeben wird.HINWEIS: Ab PHP 5.4.0 wurde die register_globals
Einstellung entfernt und kann nicht mehr verwendet werden. Dieser Abschnitt dient lediglich als Warnung für alle, die eine ältere Anwendung aktualisieren.
Wenn die Konfigurationseinstellung register_globals
aktiviert ist, werden verschiedene Variablentypen (einschließlich Variablen aus $_POST
, $_GET
und $_REQUEST
) im globalen Bereich Ihrer Anwendung verfügbar.
Dies kann leicht zu Sicherheitsproblemen führen, da Deine Anwendung nicht effektiv erkennen kann, woher die Daten stammen.
Beispielsweise wäre $_GET['foo']
über $foo
verfügbar, wodurch deklarierte Variablen überschrieben werden können.
Wenn Sie PHP < 5.4.0 verwenden, stellen Sie sicher, dass register_globals
deaktiviert d.h. off ist.
Die Fehlerprotokollierung kann hilfreich sein, um Problemstellen in Deiner Anwendung zu finden, kann aber auch Informationen über die Struktur Deiner Anwendung nach außen offen legen. Um Deine Anwendung effektiv vor Problemen zu schützen, welche durch die Ausgabe dieser Meldungen verursacht werden könnten, musst Du Deinen Server in der Entwicklung (dev) anders konfigurieren als in der Produktion (live).
Um alle möglichen Fehler während der Entwicklung anzuzeigen, konfiguriere die folgenden Einstellungen in Deiner php.ini
:
display_errors = On
display_startup_errors = On
error_reporting = -1
log_errors = On
Die Übergabe des Wertes
-1
zeigt alle möglichen Fehler an, auch wenn in zukünftigen PHP-Versionen neue Ebenen und Konstanten hinzugefügt werden. DieE_ALL
-Konstante verhält sich ab PHP 5.4 genau so. - php.net
Die E_STRICT
Fehlerstufenkonstante (error level constant) wurde in PHP 5.3.0 eingeführt und ist nicht Teil von E_ALL
. Wurde jedoch in 5.4.0 Teil von E_ALL
. Was bedeutet das? In Bezug auf die Meldung aller möglichen Fehler in Version 5.3 bedeutet dies, dass Du entweder -1
oder E_ALL | E_STRICT
verwenden musst.
Melden aller möglichen Fehler, je nach PHP-Version
-1
oder E_ALL
-1
oder E_ALL | E_STRICT
-1
oder E_ALL
Um Fehler in Deiner Produktionsumgebung zu verbergen, konfiguriere Deine php.ini
wie folgt:
display_errors = Off
display_startup_errors = Off
error_reporting = E_ALL
log_errors = On
Mit diesen Einstellungen werden Fehler in der Produktion weiterhin in den Fehlerprotokollen des Webservers protokolliert, dem Benutzer jedoch nicht angezeigt. Weitere Informationen zu diesen Einstellungen findest Du im PHP-Handbuch:
Das Schreiben automatisierter Tests für Deinen PHP-Code gilt als bewährte Methode und führt zu gut gebauten Anwendungen. Automatisierte Tests sind ein hervorragendes Werkzeug, um sicherzustellen, dass Deine Anwendung beim Ändern oder Hinzufügen neuer Funktionen nicht abstürzt. Tests sollten daher nicht ignoriert werden.
Für PHP stehen verschiedene Arten von Testtools (oder Frameworks) zur Verfügung, die unterschiedliche Ansätze verwenden. Alle versuchen, manuelle Tests und die Notwendigkeit großer Qualitäts-Sicherungs-Teams zu vermeiden, nur um sicherzustellen, dass Änderungen am Code die vorhandene Funktionalität nicht beeinträchtigen werden.
Aus Wikipedia (dt.):
Testgetriebene Entwicklung (auch testgesteuerte Programmierung; englisch test first development oder test-driven development, TDD) ist eine Methode, die häufig bei der agilen Entwicklung von Computerprogrammen eingesetzt wird. Bei der testgetriebenen Entwicklung erstellt der Programmierer Softwaretests konsequent vor den zu testenden Komponenten. Kent Beck, der als Entwickler bzw. „Wiederentdecker“ dieser Technik gilt, erklärte 2003, dass TDD einfache Designs fördert und Vertrauen schafft.
Es gibt verschiedene Arten von Tests, die Du für Deine Anwendung durchführen kannst:
Unit-Tests sind ein Programmieransatz, der sicherstellt, dass Funktionen, Klassen und Methoden vom Zeitpunkt der Erstellung bis zum Ende des Entwicklungszyklus wie erwartet funktionieren. Durch die Überprüfung entgegengenommenre bzw. ausgegebener Werte verschiedener Funktionen und Methoden stellst Du sicher, dass die interne Logik korrekt funktioniert. Mithilfe von Dependency Injection und der Erstellung von „Mock“-Klassen und Stubs kannst Du die korrekte Verwendung von Abhängigkeiten überprüfen und so eine noch bessere Testabdeckung erzielen.
Beim Erstellen einer Klasse oder Funktion solltest Du für jedes erforderliche Verhalten einen Unit-Test erstellen.
Stelle grundsätzlich sicher, dass bei ungültigen Argumenten Fehler auftreten und bei gültigen Argumenten die Funktion gewährleistet ist.
So stellst Du sicher, dass bei späteren Änderungen an dieser Klasse oder Funktion die alte Funktionalität weiterhin wie erwartet funktioniert.
Die einzige Alternative hierzu wäre var_dump()
in einer test.php, was jedoch keine Möglichkeit darstellt, eine Anwendung zu erstellen – egal ob groß oder klein.
Ein weiterer Nutzen von Unit-Tests ist die Mitarbeit an Open Source. Wenn Du einen Test schreibst, der fehlerhafte Funktionalität zeigt (z. B. scheitert), diesen dann behebst und nachweisen kannst, dass der Test erfolgreich war, ist die Wahrscheinlichkeit, dass Patches akzeptiert werden, deutlich höher. Wenn Du ein Projekt betreibst, das Pull Requests akzeptiert, solltest Du dies als Voraussetzung vorschlagen.
PHPUnit ist das De-facto-Testframework zum Schreiben von Unit-Tests für PHP-Anwendungen. Es gibt jedoch mehrere Alternativen:
Aus Wikipedia:
Der Begriff Integrationstest bezeichnet in der Softwareentwicklung eine aufeinander abgestimmte Reihe von Einzeltests, die dazu dienen, verschiedene voneinander abhängige Komponenten eines komplexen Systems im Zusammenspiel miteinander zu testen. Die erstmals im gemeinsamen Kontext zu testenden Komponenten haben im Idealfall jeweilige Modultests erfolgreich bestanden und sind für sich isoliert fehlerfrei funktionsfähig.
Viele der gleichen Tools, die für Unit-Tests verwendet werden können, können auch für Integrationstests verwendet werden, weil viele gleiche Prinzipien verwendet werden.
Funktionstests, auch als Abnahmetests bezeichnet, bestehen aus der Verwendung von Tools zur Erstellung automatisierter Tests, die Deine Anwendung tatsächlich nutzen, anstatt nur zu überprüfen, ob einzelne Codeeinheiten korrekt funktionieren und miteinander kommunizieren können. Diese Tools arbeiten in der Regel mit realen Daten und simulieren tatsächliche Benutzer der Anwendung.
Es gibt zwei verschiedene Arten von BDD: SpecBDD und StoryBDD. SpecBDD konzentriert sich auf das technische Verhalten des Codes, während StoryBDD sich auf das geschäftliche Verhalten oder die Interaktion von Funktionen konzentriert. PHP bietet Frameworks für beide Arten von BDD.
Mit StoryBDD schreibst Du human-readable stories, die das Verhalten Deiner Anwendung beschreiben. Diese Geschichten können dann als Tests mit Deinrer Anwendung ausgeführt werden. Das in PHP-Anwendungen für StoryBDD verwendete Framework ist Behat, welches von Ruby’s Cucumber-Projekt inspiriert ist und die Gherkin-DSL zur Beschreibung des Funktionsverhaltens implementiert (DSL: Domain Specific Language).
Mit SpecBDD schreibst Du Spezifikationen, die beschreiben, wie sich Dein eigentlicher Code verhalten soll. Anstatt eine Funktion oder Methode zu testen, beschreibst Du, wie sich diese Funktion oder Methode verhalten soll. PHP bietet hierfür das PHPSpec - Framework. Dieses Framework ist vom RSpec project für Ruby inspiriert.
Neben individuellen Test- und verhaltensgesteuerten Frameworks gibt es auch eine Reihe generischer Frameworks und Hilfsbibliotheken, die für jeden bevorzugten Ansatz nützlich sind..
PHP-Anwendungen können auf verschiedene Weise auf Produktionservern bereitgestellt und ausgeführt werden.
PaaS bietet die erforderliche System- und Netzwerkarchitektur für die Ausführung von PHP-Anwendungen im Web. Dies bedeutet, dass für den Start von PHP-Anwendungen oder PHP-Frameworks kaum oder gar keine Konfiguration erforderlich ist.
PaaS hat sich in letzter Zeit zu einer beliebten Methode für die Bereitstellung, das Hosting und die Skalierung von PHP-Anwendungen jeder Größe entwickelt. Eine Liste von PHP PaaS-Anbietern (Platform as a Service) findest Du in unserem Ressourcenbereich.
Wenn Du mit Systemadministration vertraut bist oder daran interessiert bist, diese zu erlernen, bieten Dir virtuelle oder dedizierte Server die vollständige Kontrolle über die Produktionsumgebung Deiner Anwendung.
PHP lässt sich über den integrierten FastCGI Process Manager (FPM) hervorragend mit nginx, einem schlanken, leistungsstarken Webserver kombinieren. Es benötigt weniger Speicher als Apache und kann mehrere gleichzeitige Anfragen besser verarbeiten. Dies ist besonders wichtig auf virtuellen Servern mit begrenztem Speicher.
PHP und Apache verbindet eine lange gemeinsame Geschichte. Apache ist vielfältig konfigurierbar und verfügt über zahlreiche Module zur Erweiterung der Funktionalität. Es ist eine beliebte Wahl für Shared Server und bietet eine einfache Einrichtung für PHP-Frameworks und Open-Source-Anwendungen wie z.B. WordPress. Leider verbraucht Apache standardmäßig mehr Ressourcen als nginx und kann nicht so viele Besucher gleichzeitig verarbeiten.
Apache bietet verschiedene Konfigurationsmöglichkeiten für PHP.
Die gängigste und einfachste ist das Prefork-MPM mit mod_php
.
Es ist zwar nicht die speichereffizienteste, aber die einfachste Lösung.
Dies ist wahrscheinlich die beste Wahl, wenn Du Dich nicht zu sehr mit der Serveradministration befassen möchtest.
Beachte, dass bei Verwendung von mod_php
unbedingt das Prefork-MPM verwenden MUSST.
Wenn Du die Leistung und Stabilität von Apache steigern möchtest,
kannst Du alternativ dasselbe FPM-System wie nginx nutzen und das worker MPM oder event MPM mit mod_fastcgi
oder mod_fcgid
ausführen.
Diese Konfiguration ist deutlich speichereffizienter und schneller, erfordert aber mehr Aufwand bei der Einrichtung.
Wenn Du Apache 2.4 oder höher ausführst, kannst Du mod_proxy_fcgi
verwenden. Damit erziehlst Du eine großartige Leistung zu erzielen, die auch noch einfach einzurichten ist.
Wenn Du manuelle Datenbankschemaänderungen vornimmst oder Deine Tests manuell ausführst, bevor Du Deine Dateien (manuell) aktualisierst, denke noch einmal darüber nach! Mit jeder zusätzlichen manuellen Aufgabe, die für die Bereitstellung einer neuen App-Version erforderlich ist, steigt das Risiko potenziell schwerwiegender Fehler. Ob einfaches Update, umfassender Build-Prozess oder kontinuierliche Integrationsstrategie – Build-Automatisierung ist Dein bester Freund.
Zu den Aufgaben, die Du möglicherweise automatisieren möchtest, gehören:
Bereitstellungstools können als eine Sammlung von Skripten beschrieben werden, die allgemeine Aufgaben der Softwarebereitstellung übernehmen. Das Bereitstellungstool ist kein Teil Deiner Software, sondern wirkt von ‘außen’ auf Deine Software ein.
Es gibt viele Open-Source-Tools, die Dich bei der Build-Automatisierung und Bereitstellung unterstützen. Einige sind in PHP geschrieben, andere nicht. Das sollte Dich nicht davon abhalten, sie zu nutzen, wenn sie für die jeweilige Aufgabe besser geeignet sind. Hier einige Beispiele:
Phing steuert Deinen Packaging-, Bereitstellungs- und Testprozess aus einer XML-Build-Datei heraus. Phing (basierend auf Apache Ant) bietet eine Vielzahl von Aufgaben, die üblicherweise für die Installation oder Aktualisierung einer Webanwendung erforderlich sind, und kann um zusätzliche, in PHP geschriebene, benutzerdefinierte Aufgaben erweitert werden. Es ist ein solides und robustes Tool und existiert schon seit langer Zeit. Aufgrund der Art und Weise, wie es mit der Konfiguration (XML-Dateien) umgeht, könnte es jedoch etwas altmodisch wirken.
Capistrano ist ein System für fortgeschrittene Programmierer, um Befehle strukturiert und wiederholbar auf einem oder mehreren Remote-Rechnern auszuführen. Es ist für die Bereitstellung von Ruby-on-Rails-Anwendungen vorkonfiguriert, Du kannst damit jedoch auch PHP-Systeme erfolgreich deployen. Die erfolgreiche Nutzung von Capistrano setzt fundierte Kenntnisse in Ruby und Rake voraus.
Ansistrano umfasst mehrere Ansible-Rollen zur einfachen Verwaltung des Bereitstellungsprozesses (Deployment und Rollback) für Skriptanwendungen wie PHP, Python und Ruby. Es handelt sich um eine Ansible-Portierung für Capistrano. Es wird bereits von zahlreichen PHP-Unternehmen eingesetzt.
Deployer ist ein in PHP geschriebenes Deployment-Tool. Es ist einfach und funktional. Zu den Funktionen gehören die parallele Ausführung von Aufgaben, atomares Deployment und die Wahrung der Serverkonsistenz. Es stehen Rezepte für gängige Aufgaben für Symfony, Laravel, Zend Framework und Yii zur Verfügung. Younes Rafies Artikel Easy Deployment of PHP Applications with Deployer bietet ein hervorragendes Tutorial zur Bereitstellung Deiner Anwendung mit diesem Tool.
Magallanes ist ein weiteres in PHP geschriebenes Tool mit einfacher Konfiguration in YAML-Dateien. Es unterstützt mehrere Server und Umgebungen, atomare Bereitstellung und verfügt über einige integrierte Aufgaben, die Du für gängige Tools und Frameworks nutzen kannst.
Die Verwaltung und Konfiguration von Servern kann bei vielen Servern eine gewaltige Aufgabe sein. Es gibt Tools, mit denen Du Deine Infrastruktur automatisieren und sicherstellen kannst, dass Du die richtigen Server hast und diese richtig konfiguriert sind. Diese lassen sich häufig in die Verwaltung von Instanzen größerer Cloud-Hosting-Anbieter (Amazon Web Services, Heroku, DigitalOcean usw.) integrieren, was die Skalierung einer Anwendung erheblich erleichtert.
Ansible ist ein Tool zur Verwaltung Deiner Infrastruktur über YAML-Dateien. Der Einstieg ist einfach und ermöglicht die Verwaltung komplexer und umfangreicher Anwendungen. Es gibt eine API zur Verwaltung von Cloud-Instanzen und ermöglicht deren Verwaltung über ein dynamisches Inventar mithilfe bestimmter Tools.
Puppet ist ein Tool mit eigener Sprache und eigenen Dateitypen zur Verwaltung von Servern und Konfigurationen. Es kann in einem Master/Client-Setup oder im “master-less” Modus verwendet werden. Im Master/Client-Modus fragen die Clients in festgelegten Intervallen die zentralen Master nach neuen Konfigurationen ab und aktualisieren sich bei Bedarf selbst. Im Master-losen Modus kannst Du Änderungen an Deine Knoten per push übertragen.
Chef ist ein leistungsstarkes Ruby-basiertes Systemintegrations-Framework, mit dem Du Deine gesamte Serverumgebung oder virtuelle Boxen erstellen kannst. Es lässt sich über den Dienst OpsWorks gut in Amazon Web Services integrieren.
Continuous Integration ist eine Softwareentwicklungspraxis, bei der Teammitglieder ihre Arbeit häufig integrieren. In der Regel integriert jede Person mindestens täglich, was zu mehreren Integrationen pro Tag führt. Viele Teams stellen fest, dass dieser Ansatz die Integrationsprobleme deutlich reduziert und es dem Team ermöglicht, schneller zusammenhängende Software zu entwickeln.
– Martin Fowler
Es gibt verschiedene Möglichkeiten, Continuous Integration für PHP zu implementieren. Travis CI hat hervorragende Arbeit geleistet, um Continuous Integration auch für kleine Projekte zu ermöglichen. Travis CI ist ein gehosteter Continuous-Integration-Dienst. Er lässt sich in GitHub integrieren und bietet Unterstützung für viele Sprachen, darunter auch PHP. GitHub bietet Continuous-Integration-Workflows mit GitHub Actions.
Wenn Du Deine Anwendung in unterschiedlichen Entwicklungs- und Produktionsumgebungen ausführst,können bei der Live-Schaltung merkwürdige Fehler auftreten. Außerdem ist es schwierig, verschiedene Entwicklungsumgebungen mit der gleichen Version für alle verwendeten Bibliotheken auf dem neuesten Stand zu halten, wenn Du mit einem Entwicklerteam arbeitest.
Wenn Du unter Windows entwickelst und unter Linux (oder anderen Nicht-Windows-Systemen) deployst oder im Team entwickelst, solltest Du den Einsatz einer virtuellen Maschine in Betracht ziehen. Das klingt kompliziert, aber neben den bekannten Virtualisierungsumgebungen wie VMware oder VirtualBox gibt es zusätzliche Tools, mit denen Du in wenigen einfachen Schritten eine virtuelle Umgebung einrichten kannst.
Vagrant (dt. Landstreicher) unterstützt Dich beim Aufbau Deiner virtuellen Boxen auf den bekannten virtuellen Umgebungen und konfiguriert diese Umgebungen anhand einer einzigen Konfigurationsdatei. Diese Boxen können manuell eingerichtet werden oder Du nutzt “Provisioning”-Software wie Puppet oder Chef, die das für Dich übernimmt. Die Provisionierung der Basisbox stellt sicher, dass mehrere Boxen identisch eingerichtet sind und macht die Pflege komplizierter Setup-Befehlslisten überflüssig. Du kannst Deine Basisbox auch “zerstören” und ohne viele manuelle Schritte neu erstellen, was eine einfache Neuinstallation ermöglicht.
Vagrant erstellt Ordner zum Teilen Deines Codes zwischen Deinem Host und Deiner virtuellen Maschine. Das bedeutet, dass Du Deine Dateien auf Deiner Hostmaschine erstellen und bearbeiten und den Code dann in Deiner virtuellen Maschine ausführen kannst.
Docker – eine leichtgewichtige Alternative zu einer vollständigen virtuellen Maschine – heißt so, weil es sich hier um “Container” handelt. Ein Container ist ein Baustein, der im einfachsten Fall eine bestimmte Aufgabe erfüllt, z.B. den Betrieb eines Webservers. Ein “Image” ist das Paket, mit dem Du den Container erstellst – Docker verfügt über ein ganzes Repository mit vielen Containern.
Eine typische LAMP-Anwendung kann drei Container umfassen: einen Webserver, einen PHP-FPM-Prozess und MySQL. Wie bei freigegebenen Ordnern in Vagrant kannst Du Deine Anwendungsdateien dort belassen, wo sie sind und Docker mitteilen, wo sie zu liegen.
Du kannst Container über die Befehlszeile generieren (siehe Beispiel unten)
oder zur Vereinfachung der Wartung eine docker-compose.yml
-Datei für Dein Projekt erstellen, in welche Du schreibst, welche Container erstellt werden sollen und wie sie miteinander kommunizieren.
Docker kann hilfreich sein, wenn Du mehrere Websites entwickeln und die Trennung durch die Installation jeder Website auf einer eigenen virtuellen Maschine wünschst, aber nicht über den nötigen Speicherplatz oder die Zeit verfügst, alles auf dem neuesten Stand zu halten. Es ist effizient: Installation und Downloads sind schneller, Du musst nur eine Kopie jedes Images speichern, egal wie oft es verwendet wird. Container benötigen weniger RAM und teilen sich denselben Betriebssystemkernel, sodass Du mehrere Server gleichzeitig laufen lassen kannst. Das Stoppen und Starten dauert nur wenige Sekunden, ohne dass Du auf einen vollständigen Server-Boot warten musst.
Nach der Installation von Docker auf Deinem Rechner kannst Du einen Webserver mit einem einzigen Befehl starten.
Der folgende Befehl lädt eine voll funktionsfähige Apache-Installation mit der neuesten PHP-Version herunter. Setze /path/to/your/php/files
als Document Root und sie wird unter http://localhost:8080
angezeigt
docker run -d --name my-php-webserver -p 8080:80 -v /path/to/your/php/files:/var/www/html/ php:apache
Dadurch wird Dein Container initialisiert und gestartet. -d
lässt ihn im Hintergrund laufen.
Um ihn zu stoppen und zu starten, führe einfachdocker stop my-php-webserver
bzw. docker start my-php-webserver
aus (die anderen Parameter werden nicht erneut benötigt).
Der obige Befehl zeigt eine schnelle Möglichkeit, einen einfachen Server zu starten. Es gibt noch viel mehr Möglichkeiten (und Tausende vorgefertigte Images im Docker Hub). Nimm Dir Zeit, die Terminologie zu lernen und das Docker-Benutzerhandbuch zu lesen, um das Beste heraus zu holen. Führe NICHT irgendwelchen, heruntergeladenen Code aus, ohne dessen Sicherheit zu prüfen – inoffizielle Images verfügen möglicherweise nicht über die neuesten Sicherheitspatches. Bleibe im Zweifelsfall bei den official repositiories.
Die Site PHPDocker.io generiert automatisch alle Dateien, die Du für einen voll funktionsfähigen LAMP/LEMP-Stack benötigst, einschließlich der von Dir gewählten PHP-Version und Erweiterungen.
PHP ist an sich ziemlich schnell, aber beim Herstellen von Remoteverbindungen, Laden von Dateien usw. können Engpässe auftreten. Glücklicherweise stehen verschiedene Tools zur Verfügung, mit denen Du bestimmte Teile Deiner Anwendung beschleunigen oder die Anzahl der Ausführungen dieser verschiedenen zeitaufwändigen Aufgaben reduzieren kannst.
Wenn eine PHP-Datei ausgeführt wird, muss sie zunächst in Opcodes (Maschinensprachenanweisungen für die CPU) kompiliert werden. Wenn der Quellcode unverändert bleibt, sind die Opcodes gleich, sodass dieser Kompilierungsschritt eine Verschwendung von CPU-Ressourcen darstellt.
Ein Opcode-Cache verhindert redundante Kompilierung, indem er Opcodes im Speicher hält und bei aufeinanderfolgenden Aufrufen wiederverwendet. Normalerweise wird zuerst die Signatur oder der Änderungszeitpunkt der Datei überprüft, falls Änderungen vorgenommen wurden.
Ein Opcode-Cache kann die Geschwindigkeit Ihrer Anwendung deutlich steigern.
Seit PHP 5.5 ist Zend OPcache integriert.
Abhängig von Deinem PHP-Paket / Deiner PHP-Distribution ist er in der Regel standardmäßig aktiviert.
Überprüfe opcache.enable und die Ausgabe von phpinfo()
, um sicherzugehen.
Für frühere Versionen gibt es eine PECL-Erweiterung.
Lies’ mehr über opcode caches:
Manchmal ist es sinnvoll, einzelne Objekte im Code zwischenzuspeichern, z.B. bei Daten, deren Beschaffung aufwendig ist, oder bei Datenbankaufrufen, bei denen sich das Ergebnis wahrscheinlich nicht ändert. Mithilfe von Objekt-Caching-Software kannst Du diese Daten im Speicher halten und später extrem schnell darauf zugreifen. Wenn Du diese Elemente nach dem Abruf zwischenspreicherst und für nachfolgende Anfragen direkt aus dem Cache abrufst, erzielst Du eine deutliche Leistungssteigerung und reduzierst die Belastung Deiner Datenbankserver.
Viele der gängigen Bytecode-Caching-Lösungen ermöglichen auch das Zwischenspeichern benutzerdefinierter Daten. Es gibt also noch mehr Gründe, diese zu nutzen. Sowohl APCu als auch WinCache bieten APIs zum Speichern von Daten aus Deinem PHP-Code im Speichercache.
Die am häufigsten verwendeten Speicherobjekt-Caching-Systeme sind APCu und Memcached. APCu eignet sich hervorragend für das Objekt-Caching. Es verfügt über eine einfache API zum Hinzufügen eigener Daten zum Speichercache und ist sehr einfach einzurichten und zu verwenden. Die einzige echte Einschränkung von APCu besteht darin, dass es an den Server gebunden ist, auf dem es installiert ist. Memcached hingegen wird als separater Dienst installiert und ist über das Netzwerk zugänglich. Das bedeutet, dass Objekte in einem superschnellen Datenspeicher an einem zentralen Ort gespeichert werden und viele verschiedene Systeme darauf zugreifen können.
Beachte, dass die gemeinsame Nutzung des Caches durch PHP-Prozesse von der PHP-Nutzung abhängt. Wenn Du PHP über FastCGI Process Manager (PHP-FPM) ausführst, wird der Cache von allen Prozessen aller Pools gemeinsam genutzt. Wenn Du PHP als (Fast-)CGI-Anwendung auf Deinem Webserver ausführst, wird der Cache nicht gemeinsam genutzt, d. h. jeder PHP-Prozess verfügt über eigene APCu-Daten. Wenn Du PHP über die Kommandozeile ausführst, wird der Cache nicht gemeinsam genutzt und existiert nur für die Dauer des Befehls. Berücksichtige daher Deine Situation und Ziele. Du solltest stattdessen die Verwendung von Memcached in Betracht ziehen, da es nicht an die PHP-Prozesse gebunden ist.
In einer Netzwerkkonfiguration übertrifft APCu Memcached in der Regel hinsichtlich der Zugriffsgeschwindigkeit, aber Memcached lässt sich jedoch schneller und weiter skalieren. Wenn Du Deine Anwendung nicht auf mehreren Servern ausführst oder die zusätzlichen Funktionen von Memcached nicht benötigst, ist APCu wahrscheinlich die beste Wahl für das Objekt-Caching.
Beispiellogik mit APCu:
<?php
// check if there is data saved as 'expensive_data' in cache
$data = apcu_fetch('expensive_data');
if ($data === false) {
// data is not in cache; save result of expensive call for later use
apcu_add('expensive_data', $data = get_expensive_data());
}
print_r($data);
PHPDoc ist ein informeller Standard zum Kommentieren von PHP-Code. Es stehen zahlreiche verschiedene Tags zur Verfügung. Die vollständige Liste der Tags und Beispiele finden Sie im PHPDoc-Manual.
Unten siehst Du ein Beispiel, wie Du eine Klasse mit einigen Methoden dokumentieren kannst;
<?php
/**
* @author A Name <a.name@example.com>
* @link https://docs.phpdoc.org/
*/
class DateTimeHelper
{
/**
* @param mixed $anything Anything that we can convert to a \DateTime object
*
* @throws \InvalidArgumentException
*
* @return \DateTime
*/
public function dateTimeFromAnything($anything)
{
$type = gettype($anything);
switch ($type) {
// Some code that tries to return a \DateTime object
}
throw new \InvalidArgumentException(
"Failed Converting param of type '{$type}' to DateTime object"
);
}
/**
* @param mixed $date Anything that we can convert to a \DateTime object
*
* @return void
*/
public function printISO8601Date($date)
{
echo $this->dateTimeFromAnything($date)->format('c');
}
/**
* @param mixed $date Anything that we can convert to a \DateTime object
*/
public function printRFC2822Date($date)
{
echo $this->dateTimeFromAnything($date)->format('r');
}
}
Die Dokumentation der gesamten Klasse enthält die Tags @author und @link. Der @author-Tag dient zur Dokumentation des Autors des Codes und kann zur Dokumentation mehrerer Autoren wiederholt werden. Der @link-Tag dient zur Verlinkung auf eine Website und weist auf eine Beziehung zwischen der Website und dem Code hin.
Innerhalb der Klasse verfügt die erste Methode über ein @param-Tag, welcher Typ, Name und Beschreibung des an die Methode übergebenen Parameters dokumentiert. Zusätzlich verfügt sie über die Tags @return und @throws zur Dokumentation des Rückgabetyps sowie aller Exceptions, die ausgelöst werden können.
Die zweite und dritte Methode sind sehr ähnlich und verwenden wie die erste Methode nur einen @param-Tag.
Der wesentliche Unterschied zwischen dem Doc-Block der zweiten und dritten Methode ist die Einbeziehung bzw. der Ausschluss des @return-Tags.
@return void
weist explizit darauf hin, dass keine Rückgabe erfolgt; das Weglassen der @return void
Anweisung führt historisch ebenfalls zur gleichen Aktion (keine Rückgabe).
Es ist schwierig, interessante und kompetente Mitglieder der PHP-Community zu finden, wenn man gerade erst anfängt. Eine kurze Liste der PHP-Community-Mitglieder findest Du hier:
PaaS: Platform as a Service
Anstatt das Rad neu zu erfinden, verwenden viele PHP-Entwickler Frameworks zum Erstellen von Webanwendungen. Frameworks abstrahieren viele der grundlegenden Probleme und bieten hilfreiche, benutzerfreundliche Schnittstellen für die Erledigung gängiger Aufgaben.
Du musst nicht für jedes Projekt ein Framework verwenden. Manchmal ist einfaches PHP die richtige Lösung. Wenn Du jedoch ein Framework benötigst, stehen Dir drei Haupttypen zur Verfügung:
Mikroframeworks sind im Wesentlichen Wrapper, um eine HTTP-Anfrage schnellstmöglich an einen Callback, Controller, eine Methode usw. weiterzuleiten. Manchmal enthalten sie zusätzliche Bibliotheken zur Unterstützung der Entwicklung, wie z. B. einfache Datenbank-Wrapper und Ähnliches. Sie werden vor allem zum Erstellen von Remote-HTTP-Diensten verwendet.
Viele Frameworks erweitern die Funktionen eines Micro-Frameworks um eine beträchtliche Anzahl weiterer Funktionen. Diese werden als Full-Stack-Frameworks bezeichnet. Diese werden häufig mit ORMs, Authentifizierungspaketen usw. gebündelt.
Komponentenbasierte Frameworks sind Sammlungen spezialisierter Bibliotheken für einen einzigen Zweck. Unterschiedliche komponentenbasierte Frameworks können zusammen verwendet werden, um ein Micro- oder Full-Stack-Framework zu erstellen.
Wie bereits erwähnt, sind “Komponenten” ein weiterer Ansatz für das Ziel, gemeinsamen Code zu erstellen, zu verteilen und zu implementieren. Es gibt verschiedene Komponenten-Repositories, die beiden wichtigsten sind:
Beide Repositories haben Befehlszeilentools, um die Installations- und Upgradeprozesse zu unterstützen. Diese werden im Abschnitt Dependency Management ausführlicher erläutert .
Es gibt auch komponentenbasierte Frameworks und Komponentenanbieter, die überhaupt kein Framework anbieten. Diese Projekte bieten eine weitere Quelle für Pakete, die im Idealfall wenig bis keine Abhängigkeiten von anderen Paketen oder bestimmten Frameworks aufweisen.
Du kannst beispielsweise das FuelPHP Validation package verwenden, ohne das FuelPHP-Framework selbst verwenden zu müssen.
Die Illuminate-Komponenten von Laravel werden weiter vom Laravel-Framework entkoppelt werden. Derzeit sind oben nur die Komponenten aufgelistet, die am besten vom Laravel-Framework entkoppelt sind.
Du kannst wöchentliche Newsletter abonnieren, um über neue Bibliotheken, aktuelle Nachrichten, Veranstaltungen und allgemeine Ankündigungen sowie gelegentlich veröffentlichte zusätzliche Ressourcen auf dem Laufenden zu bleiben:
Es gibt auch Weeklies auf anderen Plattformen, die Dich interessieren könnten; hier ist eine Liste einiger.
Es gibt viele PHP-Bücher; leider sind einige mittlerweile recht alt und nicht mehr aktuell. Vermeid insbesondere Bücher zu “PHP 6”, einer Version, die es nie geben wird. Die nächste Hauptversion von PHP nach 5.6 war “PHP 7”; unter anderem aus diesen Gründen.
Dieser Abschnitt soll ein lebendiges Dokument für empfohlene Bücher zur PHP-Entwicklung im Allgemeinen sein. Wenn Du möchtest, dass Dein Buch hinzugefügt wird, sende einen Pull Request (PR) und es wird auf Relevanz geprüft.
Die PHP-Community ist ebenso vielfältig wie groß, und ihre Mitglieder unterstützen gerne neue PHP-Programmierer. Trete Deiner lokalen PHP-Benutzergruppe (PUG) bei oder besuche größere PHP-Konferenzen, um mehr über die hier gezeigten Best Practices zu erfahren. Du kannst im IRC im #phpc-Kanal auf irc.libera.chat abhängen und @phpc folgen, auf Discord, Mastodon oder X. Geh’ raus, lerne neue Entwickler kennen, erfahre mehr über neue Themen und schließe vor allem neue Freundschaften! Weitere Community-Ressourcen sind StackOverflow.
Wenn Du in einer größeren Stadt wohnst, gibt es wahrscheinlich eine PHP User Group in Deiner Nähe.
Du findest Deine lokale PUG ganz einfach unter PHP.ug.
Alternativ kannst du Meetup.com oder eine Suche php user group in der Nähe
Deiner bevorzugten Suchmaschine verwenden; z.B google. Wohnst Du in einer Kleinstadt, gibt es möglicherweise keine lokale PUG. Gründe dann einfach selbst eine!
Besonders hervorzuheben sind zwei globale Benutzergruppen: NomadPHP und PHPWomen. NomadPHP bietet zweimal monatlich Online-Benutzergruppentreffen mit Vorträgen einiger der besten Redner der PHP-Community an. PHPWomen ist eine nicht-exklusive Benutzergruppe, die sich ursprünglich an Frauen in der PHP-Welt richtete. Die Mitgliedschaft steht allen offen, die eine vielfältigere Community unterstützen. PHPWomen bietet ein Netzwerk für Unterstützung, Mentoring und Weiterbildung und fördert generell die Schaffung einer frauenfreundlichen und professionellen Atmosphäre.
Die PHP-Community veranstaltet außerdem größere regionale und nationale Konferenzen in vielen Ländern weltweit. Bei diesen größeren Veranstaltungen sprechen in der Regel bekannte Mitglieder der PHP-Community. Dies ist eine großartige Gelegenheit, direkt von Branchenführern zu lernen.
Der ElePHPant ist das schöne Maskottchen des PHP-Projekts mit einem Elefanten im Design. Es wurde ursprünglich 1998 von Vincent Pontier – dem geistigen Vater von Tausenden von elePHPants weltweit – für das PHP-Projekt entworfen. Zehn Jahre später entstanden auch niedliche Plüschelefanten. Heute sind die elePHPants auf vielen PHP-Konferenzen und bei vielen PHP-Entwicklern an ihren Computern für Spaß und zur Inspiration präsent.