Learn/flight_vs_laravel
Flight vs Laravel
Was ist Laravel?
Laravel ist ein vollständiges Framework mit allen Klingeln und Pfiffen und einem beeindruckenden, auf Entwickler fokussierten Ökosystem, aber zu Lasten von Leistung und Komplexität. Das Ziel von Laravel ist es, dass der Entwickler das höchste Maß an Produktivität erreicht und gängige Aufgaben einfach macht. Laravel ist eine großartige Wahl für Entwickler, die eine vollständige, unternehmensorientierte Webanwendung aufbauen möchten. Das geht mit einigen Kompromissen einher, speziell in Bezug auf Leistung und Komplexität. Der Einstieg in Laravel kann einfach sein, aber die Meisterschaft im Framework zu erlangen, kann einige Zeit in Anspruch nehmen.
Es gibt auch so viele Laravel-Module, dass Entwickler oft das Gefühl haben, der einzige Weg, Probleme zu lösen, sei die Nutzung dieser Module, obwohl man tatsächlich einfach eine andere Bibliothek verwenden oder eigenen Code schreiben könnte.
Vorteile im Vergleich zu Flight
- Laravel hat ein riesiges Ökosystem aus Entwicklern und Modulen, das für die Lösung gängiger Probleme genutzt werden kann.
- Laravel hat ein vollständiges ORM, das für die Interaktion mit Ihrer Datenbank verwendet werden kann.
- Laravel hat eine verrückte Menge an Dokumentation und Tutorials, die für das Lernen des Frameworks genutzt werden können. Das kann gut sein, um in die Details einzutauchen, oder schlecht, weil es einfach so viel zu durchgehen gibt.
- Laravel hat ein integriertes Authentifizierungssystem, das zur Sicherung Ihrer Anwendung verwendet werden kann.
- Laravel hat Podcasts, Konferenzen, Meetings, Videos und andere Ressourcen, die für das Lernen des Frameworks genutzt werden können.
- Laravel ist auf einen erfahrenen Entwickler ausgerichtet, der eine vollständige, unternehmensorientierte Webanwendung aufbauen möchte.
Nachteile im Vergleich zu Flight
- Laravel hat viel mehr unter der Haube als Flight. Das geht mit einem dramatischen Kosten in Bezug auf Leistung einher. Schauen Sie sich die TechEmpower-Benchmarks für weitere Informationen an.
- Flight ist auf einen Entwickler ausgerichtet, der eine leichte, schnelle und einfach zu bedienende Webanwendung aufbauen möchte.
- Flight ist auf Einfachheit und Benutzerfreundlichkeit ausgerichtet.
- Eine der Kernfunktionen von Flight ist, dass es sein Bestes tut, um Abwärtskompatibilität zu wahren. Laravel verursacht viel Frustration zwischen großen Versionen.
- Flight ist für Entwickler gedacht, die zum ersten Mal in die Welt der Frameworks eintauchen.
- Flight hat keine Abhängigkeiten, wohingegen Laravel eine abscheuliche Menge an Abhängigkeiten hat
- Flight kann auch unternehmensorientierte Anwendungen umsetzen, hat aber nicht so viel Boilerplate-Code wie Laravel. Es erfordert auch mehr Disziplin vom Entwickler, um Dinge organisiert und gut strukturiert zu halten.
- Flight gibt dem Entwickler mehr Kontrolle über die Anwendung, wohingegen Laravel massenhaft Magie im Hintergrund hat, die frustrierend sein kann.
Learn/migrating_to_v3
Migration zu v3
Die Abwärtskompatibilität wurde größtenteils beibehalten, aber es gibt einige Änderungen, die Sie beachten sollten, wenn Sie von v2 zu v3 migrieren. Es gibt einige Änderungen, die zu sehr mit Designmustern kollidiert sind, sodass Anpassungen vorgenommen werden mussten.
Verhalten des Output Buffering
v3.5.0
Output buffering ist der Prozess, bei dem die Ausgabe, die von einem PHP-Skript generiert wird, in einem Puffer (intern in PHP) gespeichert wird, bevor sie an den Client gesendet wird. Dies ermöglicht es Ihnen, die Ausgabe zu modifizieren, bevor sie an den Client gesendet wird.
In einer MVC-Anwendung ist der Controller der "Manager" und er verwaltet, was die View tut. Ausgaben, die außerhalb des Controllers generiert werden (oder im Fall von Flight manchmal eine anonyme Funktion), brechen das MVC-Muster. Diese Änderung dient dazu, mehr im Einklang mit dem MVC-Muster zu sein und das Framework vorhersehbarer und einfacher zu bedienen zu machen.
In v2 wurde das Output Buffering so gehandhabt, dass es seinen eigenen Output-Puffer nicht konsistent schloss, was Unit-Tests und Streaming schwieriger machte. Für die Mehrheit der Nutzer könnte diese Änderung Sie tatsächlich nicht beeinflussen. Wenn Sie jedoch Inhalte außerhalb von Callables und Controllern ausgeben (z. B. in einem Hook), stoßen Sie wahrscheinlich auf Probleme. Das Ausgeben von Inhalten in Hooks und vor der tatsächlichen Ausführung des Frameworks hat in der Vergangenheit möglicherweise funktioniert, wird aber künftig nicht mehr funktionieren.
Wo Sie Probleme haben könnten
// index.php
require 'vendor/autoload.php';
// nur ein Beispiel
define('START_TIME', microtime(true));
function hello() {
echo 'Hello World';
}
Flight::map('hello', 'hello');
Flight::after('hello', function(){
// das wird tatsächlich in Ordnung sein
echo '<p>This Hello World phrase was brought to you by the letter "H"</p>';
});
Flight::before('start', function(){
// Dinge wie das werden einen Fehler verursachen
echo '<html><head><title>My Page</title></head><body>';
});
Flight::route('/', function(){
// das ist tatsächlich in Ordnung
echo 'Hello World';
// Das sollte auch in Ordnung sein
Flight::hello();
});
Flight::after('start', function(){
// das wird einen Fehler verursachen
echo '<div>Your page loaded in '.(microtime(true) - START_TIME).' seconds</div></body></html>';
});
Aktivieren des v2-Rendering-Verhaltens
Können Sie Ihren alten Code so lassen, wie er ist, ohne eine Umstellung durchzuführen, um ihn mit v3 kompatibel zu machen? Ja, das können Sie! Sie können das v2-Rendering-Verhalten aktivieren, indem Sie die Konfigurationsoption flight.v2.output_buffering
auf true
setzen. Dies ermöglicht es Ihnen, das alte Rendering-Verhalten weiterhin zu verwenden, aber es wird empfohlen, es künftig zu beheben. In v4 des Frameworks wird dies entfernt werden.
// index.php
require 'vendor/autoload.php';
Flight::set('flight.v2.output_buffering', true);
Flight::before('start', function(){
// Jetzt wird das in Ordnung sein
echo '<html><head><title>My Page</title></head><body>';
});
// mehr Code
Änderungen am Dispatcher
v3.7.0
Wenn Sie statische Methoden für Dispatcher
direkt aufgerufen haben, wie z. B. Dispatcher::invokeMethod()
, Dispatcher::execute()
usw., müssen Sie Ihren Code aktualisieren, um diese Methoden nicht mehr direkt aufzurufen. Dispatcher
wurde zu einem objektorientierteren Ansatz umgewandelt, damit Dependency Injection Container einfacher verwendet werden können. Wenn Sie eine Methode ähnlich wie der Dispatcher aufrufen müssen, können Sie manuell etwas wie $result = $class->$method(...$params);
oder call_user_func_array()
verwenden.
Änderungen an halt()
stop()
redirect()
und error()
v3.10.0
Das Standardverhalten vor 3.10.0 war, sowohl die Header als auch den Response-Body zu löschen. Dies wurde geändert, sodass nur noch der Response-Body gelöscht wird. Wenn Sie auch die Header löschen müssen, können Sie Flight::response()->clear()
verwenden.
Learn/configuration
Konfiguration
Überblick
Flight bietet eine einfache Möglichkeit, verschiedene Aspekte des Frameworks an die Bedürfnisse Ihrer Anwendung anzupassen. Einige werden standardmäßig festgelegt, aber Sie können sie bei Bedarf überschreiben. Sie können auch eigene Variablen festlegen, die in Ihrer gesamten Anwendung verwendet werden können.
Verständnis
Sie können bestimmte Verhaltensweisen von Flight anpassen, indem Sie Konfigurationswerte über die set
-Methode festlegen.
Flight::set('flight.log_errors', true);
In der Datei app/config/config.php
können Sie alle standardmäßigen Konfigurationsvariablen sehen, die Ihnen zur Verfügung stehen.
Grundlegende Verwendung
Flight-Konfigurationsoptionen
Die folgende Liste enthält alle verfügbaren Konfigurationseinstellungen:
- flight.base_url
?string
- Überschreibt die Basis-URL der Anfrage, wenn Flight in einem Unterverzeichnis läuft. (Standard: null) - flight.case_sensitive
bool
- Groß-/Kleinschreibungssensible Übereinstimmung für URLs. (Standard: false) - flight.handle_errors
bool
- Erlaubt Flight, alle Fehler intern zu behandeln. (Standard: true)- Wenn Sie möchten, dass Flight Fehler anstelle des standardmäßigen PHP-Verhaltens behandelt, muss dies auf true gesetzt werden.
- Wenn Sie Tracy installiert haben, sollten Sie dies auf false setzen, damit Tracy Fehler behandeln kann.
- Wenn Sie das APM-Plugin installiert haben, sollten Sie dies auf true setzen, damit das APM die Fehler protokollieren kann.
- flight.log_errors
bool
- Fehler in die Fehlerprotokolldatei des Webservers protokollieren. (Standard: false)- Wenn Sie Tracy installiert haben, protokolliert Tracy Fehler basierend auf den Tracy-Konfigurationen, nicht basierend auf dieser Konfiguration.
- flight.views.path
string
- Verzeichnis, das View-Template-Dateien enthält. (Standard: ./views) - flight.views.extension
string
- Dateierweiterung für View-Template-Dateien. (Standard: .php) - flight.content_length
bool
- DenContent-Length
-Header setzen. (Standard: true)- Wenn Sie Tracy verwenden, muss dies auf false gesetzt werden, damit Tracy korrekt gerendert werden kann.
- flight.v2.output_buffering
bool
- Legacy-Output-Buffering verwenden. Siehe Migration zu v3. (Standard: false)
Loader-Konfiguration
Es gibt zusätzlich eine weitere Konfigurationseinstellung für den Loader. Dies ermöglicht es Ihnen, Klassen mit _
im Klassennamen automatisch zu laden.
// Aktiviere Klassenladen mit Unterstrichen
// Standardmäßig true
Loader::$v2ClassLoading = false;
Variablen
Flight ermöglicht es Ihnen, Variablen zu speichern, damit sie überall in Ihrer Anwendung verwendet werden können.
// Speichern Sie Ihre Variable
Flight::set('id', 123);
// An anderer Stelle in Ihrer Anwendung
$id = Flight::get('id');
Um zu überprüfen, ob eine Variable gesetzt wurde, können Sie Folgendes tun:
if (Flight::has('id')) {
// Etwas tun
}
Sie können eine Variable löschen, indem Sie Folgendes tun:
// Löscht die id-Variable
Flight::clear('id');
// Löscht alle Variablen
Flight::clear();
Hinweis: Nur weil Sie eine Variable setzen können, bedeutet das nicht, dass Sie es tun sollten. Verwenden Sie diese Funktion sparsam. Der Grund dafür ist, dass alles, was hier gespeichert wird, zu einer globalen Variable wird. Globale Variablen sind schlecht, weil sie von überall in Ihrer Anwendung geändert werden können, was es schwierig macht, Fehler zu finden. Zusätzlich kann dies Dinge wie Unit-Testing komplizieren.
Fehler und Ausnahmen
Alle Fehler und Ausnahmen werden von Flight abgefangen und an die error
-Methode weitergeleitet, wenn flight.handle_errors
auf true gesetzt ist.
Das standardmäßige Verhalten ist das Senden einer generischen HTTP 500 Internal Server Error
-Antwort mit einigen Fehlerinformationen.
Sie können dieses Verhalten für Ihre eigenen Bedürfnisse überschreiben:
Flight::map('error', function (Throwable $error) {
// Fehler behandeln
echo $error->getTraceAsString();
});
Standardmäßig werden Fehler nicht im Webserver protokolliert. Sie können dies aktivieren, indem Sie die Konfiguration ändern:
Flight::set('flight.log_errors', true);
404 Nicht gefunden
Wenn eine URL nicht gefunden werden kann, ruft Flight die notFound
-Methode auf. Das standardmäßige Verhalten ist das Senden einer HTTP 404 Not Found
-Antwort mit einer einfachen Nachricht.
Sie können dieses Verhalten für Ihre eigenen Bedürfnisse überschreiben:
Flight::map('notFound', function () {
// Nicht gefunden behandeln
});
Siehe auch
- Flight erweitern - Wie Sie die Kernfunktionalität von Flight erweitern und anpassen können.
- Unit-Testing - Wie Sie Unit-Tests für Ihre Flight-Anwendung schreiben.
- Tracy - Ein Plugin für erweiterte Fehlerbehandlung und Debugging.
- Tracy-Erweiterungen - Erweiterungen zur Integration von Tracy mit Flight.
- APM - Ein Plugin für Anwendungsleistungsüberwachung und Fehlerverfolgung.
Fehlerbehebung
- Wenn Sie Probleme haben, alle Werte Ihrer Konfiguration herauszufinden, können Sie
var_dump(Flight::get());
ausführen.
Änderungsprotokoll
- v3.5.0 - Konfiguration für
flight.v2.output_buffering
hinzugefügt, um das Legacy-Output-Buffering-Verhalten zu unterstützen. - v2.0 - Kernkonfigurationen hinzugefügt.
Learn/ai
KI & Entwicklererfahrung mit Flight
Überblick
Flight erleichtert es, Ihre PHP-Projekte mit KI-gestützten Tools und modernen Entwickler-Workflows zu superchargen. Mit integrierten Befehlen zum Verbinden mit LLM-Anbietern (Large Language Model) und zum Generieren projektspezifischer KI-Codierungsanweisungen hilft Flight Ihnen und Ihrem Team, das Maximum aus KI-Assistenten wie GitHub Copilot, Cursor und Windsurf herauszuholen.
Verständnis
KI-Codierungsassistenten sind am hilfreichsten, wenn sie den Kontext, die Konventionen und die Ziele Ihres Projekts verstehen. Die KI-Hilfsprogramme von Flight ermöglichen es Ihnen:
- Ihr Projekt mit beliebten LLM-Anbietern zu verbinden (OpenAI, Grok, Claude usw.)
- Projektspezifische Anweisungen für KI-Tools zu generieren und zu aktualisieren, damit alle konsistente, relevante Hilfe erhalten
- Ihr Team ausgerichtet und produktiv zu halten, mit weniger Zeit für die Erklärung des Kontexts
Diese Funktionen sind in die Kern-CLI von Flight und das offizielle flightphp/skeleton Starter-Projekt integriert.
Grundlegende Verwendung
Einrichten von LLM-Zugangsdaten
Der Befehl ai:init
führt Sie durch den Prozess, Ihr Projekt mit einem LLM-Anbieter zu verbinden.
php runway ai:init
Sie werden aufgefordert:
- Ihren Anbieter auszuwählen (OpenAI, Grok, Claude usw.)
- Ihren API-Schlüssel einzugeben
- Die Basis-URL und den Modellnamen festzulegen
Dies erstellt eine .runway-creds.json
-Datei im Stammverzeichnis Ihres Projekts (und stellt sicher, dass sie in Ihrer .gitignore
ist).
Beispiel:
Willkommen bei AI Init!
Welchen LLM-API möchten Sie verwenden? [1] openai, [2] grok, [3] claude: 1
Geben Sie die Basis-URL für die LLM-API ein [https://api.openai.com]:
Geben Sie Ihren API-Schlüssel für openai ein: sk-...
Geben Sie den Modellnamen ein, den Sie verwenden möchten (z. B. gpt-4, claude-3-opus usw.) [gpt-4o]:
Zugangsdaten in .runway-creds.json gespeichert
Generieren projektspezifischer KI-Anweisungen
Der Befehl ai:generate-instructions
hilft Ihnen, Anweisungen für KI-Codierungsassistenten zu erstellen oder zu aktualisieren, die auf Ihr Projekt zugeschnitten sind.
php runway ai:generate-instructions
Sie beantworten ein paar Fragen zu Ihrem Projekt (Beschreibung, Datenbank, Templating, Sicherheit, Teamgröße usw.). Flight verwendet Ihren LLM-Anbieter, um Anweisungen zu generieren, und schreibt sie dann in:
.github/copilot-instructions.md
(für GitHub Copilot).cursor/rules/project-overview.mdc
(für Cursor).windsurfrules
(für Windsurf)
Beispiel:
Beschreiben Sie bitte, wofür Ihr Projekt gedacht ist? Meine tolle API
Welche Datenbank planen Sie zu verwenden? MySQL
Welchen HTML-Templating-Engine planen Sie zu verwenden (falls zutreffend)? latte
Ist Sicherheit ein wichtiger Aspekt dieses Projekts? (y/n) y
...
KI-Anweisungen erfolgreich aktualisiert.
Nun geben Ihre KI-Tools intelligentere, relevantere Vorschläge basierend auf den tatsächlichen Bedürfnissen Ihres Projekts.
Erweiterte Verwendung
- Sie können den Speicherort Ihrer Zugangsdaten- oder Anweisungsdateien mit Befehlsoptionen anpassen (siehe
--help
für jeden Befehl). - Die KI-Hilfsprogramme sind so konzipiert, dass sie mit jedem LLM-Anbieter funktionieren, der OpenAI-kompatible APIs unterstützt.
- Wenn Sie Ihre Anweisungen aktualisieren möchten, während sich Ihr Projekt weiterentwickelt, führen Sie einfach
ai:generate-instructions
erneut aus und beantworten Sie die Prompts erneut.
Siehe auch
- Flight Skeleton – Das offizielle Starter-Projekt mit KI-Integration
- Runway CLI – Mehr über das CLI-Tool, das diese Befehle antreibt
Fehlerbehebung
- Wenn Sie "Missing .runway-creds.json" sehen, führen Sie zuerst
php runway ai:init
aus. - Stellen Sie sicher, dass Ihr API-Schlüssel gültig ist und Zugriff auf das ausgewählte Modell hat.
- Wenn Anweisungen nicht aktualisiert werden, überprüfen Sie die Dateiberechtigungen in Ihrem Projektverzeichnis.
Änderungsprotokoll
- v3.16.0 – Hinzugefügt: CLI-Befehle
ai:init
undai:generate-instructions
für KI-Integration.
Learn/unit_testing_and_solid_principles
Dieser Artikel wurde ursprünglich 2015 auf Airpair veröffentlicht. Alle Credits gehen an Airpair und Brian Fenton, der den Artikel ursprünglich geschrieben hat, obwohl die Website nicht mehr verfügbar ist und der Artikel nur in der Wayback Machine existiert. Dieser Artikel wurde der Seite zu Lern- und Bildungszwecken für die PHP-Community hinzugefügt.
1 Einrichtung und Konfiguration
1.1 Aktuell bleiben
Lassen Sie uns das von Anfang an klären – eine deprimierend kleine Anzahl von PHP-Installationen in der Praxis ist aktuell oder wird aktuell gehalten. Ob das auf Einschränkungen bei Shared-Hosting, Standardeinstellungen, die niemand ändert, oder auf fehlender Zeit/Budget für Upgradetests zurückzuführen ist, die bescheidenen PHP-Binaries werden oft zurückgelassen. Eine klare Best Practice, die mehr Betonung verdient, ist daher, immer eine aktuelle Version von PHP zu verwenden (5.6.x zum Zeitpunkt dieses Artikels). Darüber hinaus ist es wichtig, regelmäßige Upgrades sowohl von PHP selbst als auch von Erweiterungen oder Vendor-Bibliotheken durchzuführen. Upgrades bringen neue Sprachfunktionen, verbesserte Geschwindigkeit, geringeren Speicherverbrauch und Sicherheitsupdates. Je häufiger Sie upgraden, desto weniger schmerzhaft wird der Prozess.
1.2 Sinnvolle Standardeinstellungen
PHP macht einen anständigen Job, gute Standardeinstellungen mit seinen Dateien php.ini.development und php.ini.production vorzunehmen, aber wir können es besser machen. Zum einen legen sie keine Datums-/Zeitzone für uns fest. Das ergibt Sinn aus Sicht der Distribution, aber ohne eine wird PHP einen E_WARNING-Fehler auslösen, wannever wir eine datums-/zeitbezogene Funktion aufrufen. Hier sind einige empfohlene Einstellungen:
- date.timezone – wählen Sie aus der Liste der unterstützten Zeitzonen
- session.savepath – wenn wir Dateien für Sessions und nicht einen anderen Save-Handler verwenden, legen Sie das auf etwas außerhalb von /tmp fest. Wenn das als /tmp belassen wird, kann das in einer Shared-Hosting-Umgebung riskant sein, da /tmp_ in der Regel weit offene Berechtigungen hat. Sogar mit dem Sticky-Bit gesetzt, kann jeder mit Zugriff auf den Inhalt dieses Verzeichnisses alle aktiven Session-IDs erfahren.
- session.cookie_secure – das ist ein No-Brainer, schalten Sie das ein, wenn Sie Ihren PHP-Code über HTTPS servieren.
- session.cookie_httponly – stellen Sie das ein, um PHP-Session-Cookies vor dem Zugriff über JavaScript zu schützen
- Mehr... verwenden Sie ein Tool wie iniscan, um Ihre Konfiguration auf häufige Schwachstellen zu testen
1.3 Erweiterungen
Es ist auch eine gute Idee, Erweiterungen zu deaktivieren (oder zumindest nicht zu aktivieren), die Sie nicht verwenden, wie Datenbank-Treiber. Um zu sehen, was aktiviert ist, führen Sie den phpinfo()
-Befehl aus oder gehen Sie zur Kommandozeile und führen Sie das aus.
$ php -i
Die Informationen sind die gleichen, aber phpinfo() hat HTML-Formatierung hinzugefügt. Die CLI-Version ist einfacher zu pipen und mit grep zu filtern, um spezifische Informationen zu finden. Zum Beispiel:
$ php -i | grep error_log
Ein Haken bei dieser Methode: Es ist möglich, dass unterschiedliche PHP-Einstellungen für die webseitige Version und die CLI-Version gelten.
2 Composer verwenden
Das könnte überraschen, aber eine der besten Praktiken für modernes PHP-Schreiben ist, weniger davon zu schreiben. Obwohl es wahr ist, dass man, um gut zu programmieren, programmieren muss, gibt es eine große Anzahl von Problemen, die im PHP-Bereich bereits gelöst wurden, wie Routing, grundlegende Input-Validierungsbibliotheken, Einheitenumwandlung, Datenbank-Abstraktionsschichten usw. Schauen Sie einfach auf Packagist und stöbern Sie herum. Sie werden wahrscheinlich feststellen, dass erhebliche Teile des Problems, das Sie lösen möchten, bereits geschrieben und getestet wurden.
Obwohl es verlockend ist, den gesamten Code selbst zu schreiben (und es ist nichts Falsches daran, Ihren eigenen Framework oder Ihre eigene Bibliothek als Lernerfahrung zu schreiben), sollten Sie gegen diese Gefühle von „Nicht von mir erfunden“ ankämpfen und sich Zeit und Kopfschmerzen sparen. Folgen Sie stattdessen der Doktrin von PIE – Proudly Invented Elsewhere. Und wenn Sie sich entscheiden, Ihr eigenes Etwas zu schreiben, veröffentlichen Sie es nicht, es sei denn, es tut etwas signifikant anderes oder Besseres als bestehende Angebote.
Composer ist ein Paketmanager für PHP, ähnlich wie pip in Python, gem in Ruby und npm in Node. Es ermöglicht Ihnen, eine JSON-Datei zu definieren, die die Abhängigkeiten Ihres Codes auflistet, und es wird versuchen, diese Anforderungen zu erledigen, indem es die notwendigen Code-Bundles herunterlädt und installiert.
2.1 Composer installieren
Wir gehen davon aus, dass dies ein lokales Projekt ist, also installieren wir eine Instanz von Composer nur für das aktuelle Projekt. Navigieren Sie zu Ihrem Projektverzeichnis und führen Sie das aus:
$ curl -sS https://getcomposer.org/installer | php
Beachten Sie, dass das Pipen eines Downloads direkt in einen Skript-Interpreter (sh, ruby, php usw.) ein Sicherheitsrisiko darstellt, also lesen Sie den Installationscode und stellen Sie sicher, dass Sie damit einverstanden sind, bevor Sie einen solchen Befehl ausführen.
Aus Gründen der Bequemlichkeit (wenn Sie lieber composer install
tippen als php composer.phar install
), können Sie diesen Befehl verwenden, um eine einzelne Kopie von Composer global zu installieren:
$ mv composer.phar /usr/local/bin/composer
$ chmod +x composer
Sie müssen diese möglicherweise mit sudo
ausführen, je nach Ihren Dateiberechtigungen.
2.2 Composer verwenden
Composer hat zwei Hauptkategorien von Abhängigkeiten, die es verwalten kann: „require“ und „require-dev“. Abhängigkeiten, die als „require“ aufgelistet sind, werden überall installiert, aber „require-dev“-Abhängigkeiten werden nur installiert, wenn sie explizit angefordert werden. Normalerweise handelt es sich dabei um Tools für die aktive Entwicklung, wie PHP_CodeSniffer. Die Zeile unten zeigt ein Beispiel, wie man Guzzle installiert, eine beliebte HTTP-Bibliothek.
$ php composer.phar require guzzle/guzzle
Um ein Tool nur für Entwicklungszwecke zu installieren, fügen Sie die --dev
-Flag hinzu:
$ php composer.phar require --dev 'sebastian/phpcpd'
Das installiert PHP Copy-Paste Detector, ein weiteres Code-Qualitäts-Tool als Entwicklungs-abhängigkeit.
2.3 Install vs. Update
Wenn wir composer install
das erste Mal ausführen, installiert es alle Bibliotheken und ihre Abhängigkeiten, basierend auf der composer.json-Datei. Wenn das erledigt ist, erstellt Composer eine Lock-Datei, passend benannt composer.lock. Diese Datei enthält eine Liste der Abhängigkeiten, die Composer für uns gefunden hat, und ihre genauen Versionen mit Hashes. Jedes Mal, wenn wir composer install
in Zukunft ausführen, schaut es in die Lock-Datei und installiert genau diese Versionen.
composer update
ist ein bisschen anders. Es ignoriert die composer.lock-Datei (falls vorhanden) und versucht, die neuesten Versionen jeder Abhängigkeit zu finden, die immer noch den Einschränkungen in composer.json entsprechen. Es schreibt dann eine neue composer.lock-Datei, wenn es fertig ist.
2.4 Autoloading
Sowohl composer install
als auch composer update
generieren einen Autoloader für uns, der PHP sagt, wo es alle notwendigen Dateien für die Bibliotheken findet, die wir gerade installiert haben. Um ihn zu verwenden, fügen Sie einfach diese Zeile hinzu (normalerweise zu einer Bootstrap-Datei, die bei jeder Anfrage ausgeführt wird):
require 'vendor/autoload.php';
3 Gute Designprinzipien befolgen
3.1 SOLID
SOLID ist ein Akronym, das uns an fünf Schlüsselprinzipien im guten objektorientierten Software-Design erinnert.
3.1.1 S - Single Responsibility Principle
Das besagt, dass Klassen nur eine Verantwortung haben sollten, oder anders ausgedrückt, sie sollten nur einen Grund zum Ändern haben. Das passt gut zur Unix-Philosophie von vielen kleinen Tools, die eine Sache gut machen. Klassen, die nur eine Sache tun, sind viel einfacher zu testen und zu debuggen und überraschen Sie weniger. Sie wollen nicht, dass ein Methodenaufruf zu einer Validator-Klasse DB-Datensätze aktualisiert. Hier ist ein Beispiel für eine Verletzung des SRP, wie man es in einer Anwendung basierend auf dem ActiveRecord-Pattern häufig sieht.
class Person extends Model
{
public $name;
public $birthDate;
protected $preferences;
public function getPreferences() {}
public function save() {}
}
Das ist ein ziemlich grundlegendes Entity-Modell. Eines dieser Dinge gehört hier nicht hin. Die einzige Verantwortung eines Entity-Modells sollte das Verhalten sein, das mit der Entität zusammenhängt, die es repräsentiert, es sollte nicht für seine eigene Persistenz verantwortlich sein.
class Person extends Model
{
public $name;
public $birthDate;
protected $preferences;
public function getPreferences() {}
}
class DataStore
{
public function save(Model $model) {}
}
Das ist besser. Das Person-Modell ist wieder nur eine Sache, und das Save-Verhalten wurde zu einem Persistenz-Objekt verschoben. Beachten Sie auch, dass ich nur auf Model getippt habe, nicht auf Person. Wir kommen darauf zurück, wenn wir zu den L- und D-Teilen von SOLID kommen.
3.1.2 O - Open Closed Principle
Es gibt einen tollen Test dafür, der ziemlich genau zusammenfasst, worum es bei diesem Prinzip geht: Denken Sie an eine Funktion, die Sie implementieren sollen, wahrscheinlich die neueste, an der Sie gearbeitet haben oder arbeiten. Können Sie diese Funktion in Ihrem bestehenden Codebasis SOLELY implementieren, indem Sie neue Klassen hinzufügen und keine bestehenden Klassen in Ihrem System ändern? Ihre Konfiguration und Verkabelungscode bekommt ein bisschen Nachsicht, aber in den meisten Systemen ist das überraschend schwierig. Sie müssen sich stark auf polymorphe Dispatch verlassen und die meisten Codebasen sind nicht dafür eingerichtet. Wenn Sie daran interessiert sind, gibt es einen guten Google-Talk auf YouTube über Polymorphismus und Code-Schreiben ohne Ifs, der das weiter ausführt. Als Bonus wird der Talk von Miško Hevery gehalten, den viele als den Erfinder von AngularJs kennen.
3.1.3 L - Liskov Substitution Principle
Dieses Prinzip ist nach Barbara Liskov benannt und lautet wie folgt:
„Objekte in einem Programm sollten durch Instanzen ihrer Untertypen ersetzbar sein, ohne die Korrektheit dieses Programms zu ändern.“
Das klingt alles gut und schön, aber es wird klarer illustriert mit einem Beispiel.
abstract class Shape
{
public function getHeight();
public function setHeight($height);
public function getLength();
public function setLength($length);
}
Das wird unsere grundlegende vierseitige Form darstellen. Nichts Ausgefallenes hier.
class Square extends Shape
{
protected $size;
public function getHeight() {
return $this->size;
}
public function setHeight($height) {
$this->size = $height;
}
public function getLength() {
return $this->size;
}
public function setLength($length) {
$this->size = $length;
}
}
Hier ist unsere erste Form, das Quadrat. Eine ziemlich unkomplizierte Form, oder? Sie können annehmen, dass es einen Konstruktor gibt, in dem wir die Dimensionen festlegen, aber Sie sehen hier aus dieser Implementierung, dass Länge und Höhe immer gleich sein werden. Quadrate sind einfach so.
class Rectangle extends Shape
{
protected $height;
protected $length;
public function getHeight() {
return $this->height;
}
public function setHeight($height) {
$this->height = $height;
}
public function getLength() {
return $this->length;
}
public function setLength($length) {
$this->length = $length;
}
}
Also haben wir hier eine andere Form. Sie hat immer noch die gleichen Methodensignaturen, es ist immer noch eine vierseitige Form, aber was, wenn wir anfangen, sie gegeneinander zu verwenden? Plötzlich, wenn wir die Höhe unserer Shape ändern, können wir nicht mehr annehmen, dass die Länge unserer Shape übereinstimmt. Wir haben den Vertrag verletzt, den wir mit dem Benutzer hatten, als wir ihm unsere Square-Form gaben.
Das ist ein Lehrbuchbeispiel für eine Verletzung des LSP, und wir brauchen ein solches Prinzip, um das Beste aus einem Typsystem zu machen. Sogar Duck Typing wird uns nicht sagen, ob das zugrunde liegende Verhalten anders ist, und da wir das nicht wissen können, ohne dass es bricht, ist es am besten, sicherzustellen, dass es nicht anders ist.
3.1.3 I - Interface Segregation Principle
Dieses Prinzip sagt, dass man vielen kleinen, feingliedrigen Interfaces den Vorzug geben sollte, im Vergleich zu einem großen. Interfaces sollten auf Verhalten basieren und nicht auf „es ist eine dieser Klassen“. Denken Sie an Interfaces, die mit PHP kommen. Traversable, Countable, Serializable, Dinge wie das. Sie werben für Fähigkeiten, die das Objekt besitzt, nicht für das, wovon es erbt. Halten Sie Ihre Interfaces also klein. Sie wollen kein Interface mit 30 Methoden darauf, 3 ist ein viel besseres Ziel.
3.1.4 D - Dependency Inversion Principle
Sie haben das wahrscheinlich an anderen Stellen gehört, die über Dependency Injection gesprochen haben, aber Dependency Inversion und Dependency Injection sind nicht ganz dasselbe. Dependency Inversion ist wirklich nur eine Möglichkeit zu sagen, dass Sie auf Abstraktionen in Ihrem System und nicht auf seine Details angewiesen sein sollten. Was bedeutet das für Sie im Alltag?
Verwenden Sie nicht direkt mysqli_query() überall in Ihrem Code, verwenden Sie stattdessen etwas wie DataStore->query().
Der Kern dieses Prinzips geht eigentlich um Abstraktionen. Es geht mehr darum zu sagen „verwenden Sie einen Datenbank-Adapter“, anstatt auf direkte Aufrufe wie mysqli_query zu vertrauen. Wenn Sie mysqli_query direkt in der Hälfte Ihrer Klassen verwenden, binden Sie alles direkt an Ihre Datenbank. Nichts für oder gegen MySQL hier, aber wenn Sie mysqli_query verwenden, sollte diese Art von niedrigstufigem Detail in nur einem Ort versteckt werden und dann diese Funktionalität über eine generische Wrapper freigegeben werden.
Ich weiß, das ist ein bisschen ein abgedroschener Beispiel, wenn man drüber nachdenkt, weil die Anzahl der Male, in denen Sie Ihren Datenbank-Engine vollständig ändern werden, nachdem Ihr Produkt in Produktion ist, sehr, sehr niedrig ist. Ich habe es gewählt, weil ich dachte, die Leute wären mit der Idee aus ihrem eigenen Code vertraut. Auch, selbst wenn Sie eine Datenbank haben, bei der Sie bleiben, ermöglicht Ihnen dieses abstrakte Wrapper-Objekt, Fehler zu beheben, Verhalten zu ändern oder Funktionen zu implementieren, die Sie sich von Ihrer gewählten Datenbank wünschen. Es macht auch Unit-Testing möglich, wo niedrigstufige Aufrufe das nicht tun würden.
4 Object Calisthenics
Das ist kein voller Einstieg in diese Prinzipien, aber die ersten zwei sind leicht zu merken, bieten guten Wert und können sofort auf fast jeden Codebase angewendet werden.
4.1 Nicht mehr als eine Ebene der Einrückung pro Methode
Das ist eine hilfreiche Möglichkeit, Methoden in kleinere Chunks zu zerlegen, was zu Code führt, der klarer und selbstdokumentierender ist. Je mehr Ebenen der Einrückung Sie haben, desto mehr tut die Methode und desto mehr Zustand müssen Sie im Kopf behalten, während Sie damit arbeiten.
Sofort weiß ich, dass Leute dagegen einwenden werden, aber das ist nur eine Richtlinie/Heuristik, keine harte und schnelle Regel. Ich erwarte nicht, dass jemand PHP_CodeSniffer-Regeln dafür durchsetzt (obwohl Leute das getan haben).
Lassen Sie uns ein schnelles Beispiel durchgehen, wie das aussehen könnte:
public function transformToCsv($data)
{
$csvLines = array();
$csvLines[] = implode(',', array_keys($data[0]));
foreach ($data as $row) {
if (!$row) {
continue;
}
$csvLines[] = implode(',', $row);
}
return $csvLines;
}
Obwohl das technisch korrekter, testbarer usw. Code ist, können wir viel mehr tun, um das klarer zu machen. Wie reduzieren wir die Ebenen der Verschachtelung hier?
Wir wissen, dass wir den Inhalt der foreach-Schleife stark vereinfachen müssen (oder sie ganz entfernen), also beginnen wir da.
if (!$row) {
continue;
}
Das erste Bit ist einfach. Das ignoriert nur leere Zeilen. Wir können diesen gesamten Prozess abkürzen, indem wir eine eingebaute PHP-Funktion verwenden, bevor wir überhaupt zur Schleife kommen.
$data = array_filter($data);
foreach ($data as $row) {
$csvLines[] = implode(',', $row);
}
Jetzt haben wir unsere einzelne Ebene der Verschachtelung. Aber wenn man sich das ansieht, tun wir nichts anderes, als eine Funktion auf jedes Element in einem Array anzuwenden. Wir brauchen nicht einmal die foreach-Schleife dafür.
$data = array_filter($data);
$csvLines = array_map(function($row) {
return implode(',', $row);
}, $data);
Jetzt haben wir gar keine Verschachtelung mehr, und der Code wird wahrscheinlich schneller sein, da wir alle Schleifen mit nativen C-Funktionen anstelle von PHP machen. Wir müssen ein bisschen Trickserei betreiben, um das Komma an implode
zu übergeben, also könnte man argumentieren, dass der Stopp beim vorherigen Schritt viel verständlicher ist.
4.2 Versuchen Sie, else
nicht zu verwenden
Das behandelt wirklich zwei Hauptideen. Die erste ist mehrere Return-Anweisungen aus einer Methode. Wenn Sie genug Informationen haben, um eine Entscheidung über das Ergebnis der Methode zu treffen, treffen Sie diese Entscheidung und returnen Sie. Die zweite ist eine Idee, die als Guard Clauses bekannt ist. Das sind im Wesentlichen Validierungsprüfungen kombiniert mit frühen Returns, normalerweise ganz oben in einer Methode. Lassen Sie mich Ihnen zeigen, was ich meine.
public function addThreeInts($first, $second, $third) {
if (is_int($first)) {
if (is_int($second)) {
if (is_int($third)) {
$sum = $first + $second + $third;
} else {
return null;
}
} else {
return null;
}
} else {
return null;
}
return $sum;
}
Das ist wieder ziemlich unkompliziert, es addiert 3 Integers und gibt das Ergebnis zurück oder null
, wenn irgendeiner der Parameter kein Integer ist. Wenn man davon absieht, dass wir all diese Prüfungen in eine einzelne Zeile mit AND-Operatoren kombinieren könnten, denke ich, Sie können sehen, wie die verschachtelte if/else-Struktur den Code schwerer zu folgen macht. Schauen Sie sich stattdessen dieses Beispiel an.
public function addThreeInts($first, $second, $third) {
if (!is_int($first)) {
return null;
}
if (!is_int($second)) {
return null;
}
if (!is_int($third)) {
return null;
}
return $first + $second + $third;
}
Für mich ist dieses Beispiel viel einfacher zu folgen. Hier verwenden wir Guard Clauses, um unsere anfänglichen Annahmen über die Parameter zu überprüfen und die Methode sofort zu verlassen, wenn sie nicht bestehen. Wir haben auch keine Zwischenvariable mehr, um die Summe durch die Methode zu verfolgen. In diesem Fall haben wir überprüft, dass wir bereits auf dem happy path sind, und können einfach tun, wofür wir hier sind. Wieder könnten wir all diese Prüfungen in einem if
machen, aber das Prinzip sollte klar sein.
5 Unit-Testing
Unit-Testing ist die Praxis, kleine Tests zu schreiben, die Verhalten in Ihrem Code überprüfen. Sie werden fast immer in derselben Sprache wie der Code (in diesem Fall PHP) geschrieben und sind so schnell gedacht, dass sie jederzeit ausgeführt werden können. Sie sind extrem wertvoll als Tool, um Ihren Code zu verbessern. Neben den offensichtlichen Vorteilen, sicherzustellen, dass Ihr Code tut, was Sie denken, dass er tut, kann Unit-Testing auch sehr nützliches Design-Feedback geben. Wenn ein Stück Code schwer zu testen ist, zeigt das oft Designprobleme auf. Sie geben Ihnen auch ein Sicherheitsnetz gegen Regressionen und ermöglichen es Ihnen, viel öfter zu refactorisieren und Ihren Code zu einer saubereren Design zu entwickeln.
5.1 Tools
Es gibt mehrere Unit-Testing-Tools in PHP, aber mit Abstand das häufigste ist PHPUnit. Sie können es installieren, indem Sie eine PHAR-Datei direkt herunterladen oder es mit Composer installieren. Da wir Composer für alles andere verwenden, zeigen wir diese Methode. Da PHPUnit wahrscheinlich nicht in Produktion deployed wird, können wir es als Dev-Abhängigkeit mit dem folgenden Befehl installieren:
composer require --dev phpunit/phpunit
5.2 Tests sind eine Spezifikation
Die wichtigste Rolle von Unit-Tests in Ihrem Code ist es, eine ausführbare Spezifikation zu bieten, was der Code tun soll. Selbst wenn der Testcode falsch ist oder der Code Fehler hat, ist das Wissen, was das System soll tun, unbezahlbar.
5.3 Schreiben Sie Ihre Tests zuerst
Wenn Sie die Chance hatten, einen Satz Tests zu sehen, der vor dem Code geschrieben wurde, und einen, der nach dem Code geschrieben wurde, sind sie auffallend unterschiedlich. Die „nach“-Tests sind viel mehr mit den Implementierungsdetails der Klasse beschäftigt und stellen sicher, dass sie gute Zeilenumfänge haben, während die „vor“-Tests mehr darum gehen, das gewünschte externe Verhalten zu überprüfen. Das ist wirklich das, was uns mit Unit-Tests interessiert, nämlich sicherzustellen, dass die Klasse das richtige Verhalten zeigt. Auf Implementierung fokussierte Tests machen Refactoring tatsächlich schwieriger, weil sie brechen, wenn die Interna der Klassen ändern, und Sie haben sich gerade die Vorteile der Informationsversteckung in OOP gekostet.
5.4 Was ein guter Unit-Test ausmacht
Gute Unit-Tests teilen viele der folgenden Merkmale:
- Schnell – sollte in Millisekunden laufen.
- Kein Netzwerkzugriff – sollte in der Lage sein, Wireless auszuschalten/unstecken und alle Tests bestehen lassen.
- Begrenzter Dateisystemzugriff – das trägt zur Geschwindigkeit und Flexibilität bei, wenn Code in andere Umgebungen deployed wird.
- Kein Datenbankzugriff – vermeidet kostspielige Setup- und Teardown-Aktivitäten.
- Testen Sie nur eine Sache auf einmal – ein Unit-Test sollte nur einen Grund zum Scheitern haben.
- Gut benannt – siehe 5.2 oben.
- Meist Fake-Objekte – die einzigen „realen“ Objekte in Unit-Tests sollten das Objekt sein, das wir testen, und einfache Value-Objekte. Der Rest sollte eine Form von Test Double sein.
Es gibt Gründe, gegen einige davon zu gehen, aber als allgemeine Richtlinien werden sie Ihnen gut dienen.
5.5 Wenn Testing schmerzhaft ist
Unit-Testing zwingt Sie, den Schmerz eines schlechten Designs vorneweg zu spüren – Michael Feathers
Wenn Sie Unit-Tests schreiben, zwingen Sie sich, die Klasse tatsächlich zu verwenden, um Dinge zu erledigen. Wenn Sie Tests am Ende schreiben oder, schlimmer noch, den Code einfach über die Wand für QA oder wen auch immer werfen, um Tests zu schreiben, bekommen Sie kein Feedback darüber, wie sich die Klasse tatsächlich verhält. Wenn wir Tests schreiben und die Klasse ein echtes Problem ist, finden wir das heraus, während wir sie schreiben, was fast die günstigste Zeit ist, es zu beheben.
Wenn eine Klasse schwer zu testen ist, ist das ein Designfehler. Verschiedene Fehler manifestieren sich auf unterschiedliche Weisen. Wenn Sie eine Menge Mocking machen müssen, hat Ihre Klasse wahrscheinlich zu viele Abhängigkeiten oder Ihre Methoden tun zu viel. Je mehr Setup Sie für jeden Test machen müssen, desto wahrscheinlicher ist es, dass Ihre Methoden zu viel tun. Wenn Sie wirklich komplizierte Test-Szenarien schreiben müssen, um Verhalten auszuführen, tun die Methoden der Klasse wahrscheinlich zu viel. Wenn Sie in eine Menge privater Methoden und Zustände eintauchen müssen, um Dinge zu testen, versucht vielleicht eine andere Klasse herauszukommen. Unit-Testing ist sehr gut darin, „Eisberg-Klassen“ aufzudecken, bei denen 80% dessen, was die Klasse tut, in geschütztem oder privatem Code versteckt ist. Ich war früher ein großer Fan davon, so viel wie möglich geschützt zu machen, aber jetzt habe ich erkannt, dass ich nur meine individuellen Klassen für zu viel verantwortlich gemacht habe, und die echte Lösung war, die Klasse in kleinere Stücke zu zerlegen.
Geschrieben von Brian Fenton – Brian Fenton ist seit 8 Jahren PHP-Entwickler im Mittleren Westen und in der Bay Area, derzeit bei Thismoment. Er konzentriert sich auf Code-Craftsmanship und Designprinzipien. Blog auf www.brianfenton.us, Twitter unter @brianfenton. Wenn er nicht beschäftigt ist, Vater zu sein, genießt er Essen, Bier, Gaming und Lernen.
Learn/security
Sicherheit
Überblick
Sicherheit ist ein großes Thema, wenn es um Webanwendungen geht. Sie wollen sicherstellen, dass Ihre Anwendung sicher ist und dass die Daten Ihrer Benutzer geschützt sind. Flight bietet eine Reihe von Funktionen, um Ihnen bei der Absicherung Ihrer Webanwendungen zu helfen.
Verständnis
Es gibt eine Reihe gängiger Sicherheitsbedrohungen, auf die Sie achten sollten, wenn Sie Webanwendungen entwickeln. Einige der häufigsten Bedrohungen umfassen:
- Cross Site Request Forgery (CSRF)
- Cross Site Scripting (XSS)
- SQL Injection
- Cross Origin Resource Sharing (CORS)
Templates helfen bei XSS, indem sie die Ausgabe standardmäßig escapen, sodass Sie sich nicht daran erinnern müssen, das zu tun. Sessions können bei CSRF helfen, indem sie ein CSRF-Token in der Benutzersitzung speichern, wie unten beschrieben. Die Verwendung vorbereiteter Anweisungen mit PDO kann SQL-Injection-Angriffe verhindern (oder die bequemen Methoden in der PdoWrapper-Klasse). CORS kann mit einem einfachen Hook gehandhabt werden, bevor Flight::start()
aufgerufen wird.
All diese Methoden arbeiten zusammen, um Ihre Webanwendungen sicher zu halten. Es sollte immer im Vordergrund Ihres Geistes stehen, Sicherheitsbest Practices zu lernen und zu verstehen.
Grundlegende Verwendung
Header
HTTP-Header sind eine der einfachsten Möglichkeiten, Ihre Webanwendungen abzusichern. Sie können Header verwenden, um Clickjacking, XSS und andere Angriffe zu verhindern. Es gibt mehrere Möglichkeiten, diese Header zu Ihrer Anwendung hinzuzufügen.
Zwei großartige Websites, um die Sicherheit Ihrer Header zu überprüfen, sind securityheaders.com und observatory.mozilla.org. Nachdem Sie den unten stehenden Code eingerichtet haben, können Sie leicht überprüfen, ob Ihre Header mit diesen zwei Websites funktionieren.
Manuell hinzufügen
Sie können diese Header manuell hinzufügen, indem Sie die header
-Methode auf dem Flight\Response
-Objekt verwenden.
// Setze den X-Frame-Options-Header, um Clickjacking zu verhindern
Flight::response()->header('X-Frame-Options', 'SAMEORIGIN');
// Setze den Content-Security-Policy-Header, um XSS zu verhindern
// Hinweis: Dieser Header kann sehr komplex werden, daher möchten Sie
// Beispiele im Internet für Ihre Anwendung konsultieren
Flight::response()->header("Content-Security-Policy", "default-src 'self'");
// Setze den X-XSS-Protection-Header, um XSS zu verhindern
Flight::response()->header('X-XSS-Protection', '1; mode=block');
// Setze den X-Content-Type-Options-Header, um MIME-Sniffing zu verhindern
Flight::response()->header('X-Content-Type-Options', 'nosniff');
// Setze den Referrer-Policy-Header, um zu steuern, wie viel Referrer-Informationen gesendet werden
Flight::response()->header('Referrer-Policy', 'no-referrer-when-downgrade');
// Setze den Strict-Transport-Security-Header, um HTTPS zu erzwingen
Flight::response()->header('Strict-Transport-Security', 'max-age=31536000; includeSubDomains; preload');
// Setze den Permissions-Policy-Header, um zu steuern, welche Features und APIs verwendet werden können
Flight::response()->header('Permissions-Policy', 'geolocation=()');
Diese können oben in Ihren routes.php
- oder index.php
-Dateien hinzugefügt werden.
Als Filter hinzufügen
Sie können sie auch in einem Filter/Hook wie dem Folgenden hinzufügen:
// Füge die Header in einem Filter hinzu
Flight::before('start', function() {
Flight::response()->header('X-Frame-Options', 'SAMEORIGIN');
Flight::response()->header("Content-Security-Policy", "default-src 'self'");
Flight::response()->header('X-XSS-Protection', '1; mode=block');
Flight::response()->header('X-Content-Type-Options', 'nosniff');
Flight::response()->header('Referrer-Policy', 'no-referrer-when-downgrade');
Flight::response()->header('Strict-Transport-Security', 'max-age=31536000; includeSubDomains; preload');
Flight::response()->header('Permissions-Policy', 'geolocation=()');
});
Als Middleware hinzufügen
Sie können sie auch als Middleware-Klasse hinzufügen, die die größte Flexibilität bietet, für welche Routen dies angewendet werden soll. Im Allgemeinen sollten diese Header auf alle HTML- und API-Antworten angewendet werden.
// app/middlewares/SecurityHeadersMiddleware.php
namespace app\middlewares;
use flight\Engine;
class SecurityHeadersMiddleware
{
protected Engine $app;
public function __construct(Engine $app)
{
$this->app = $app;
}
public function before(array $params): void
{
$response = $this->app->response();
$response->header('X-Frame-Options', 'SAMEORIGIN');
$response->header("Content-Security-Policy", "default-src 'self'");
$response->header('X-XSS-Protection', '1; mode=block');
$response->header('X-Content-Type-Options', 'nosniff');
$response->header('Referrer-Policy', 'no-referrer-when-downgrade');
$response->header('Strict-Transport-Security', 'max-age=31536000; includeSubDomains; preload');
$response->header('Permissions-Policy', 'geolocation=()');
}
}
// index.php oder wo immer Sie Ihre Routen haben
// FYI: Diese leere String-Gruppe wirkt als globales Middleware für
// alle Routen. Natürlich könnten Sie dasselbe tun und dies nur zu
// spezifischen Routen hinzufügen.
Flight::group('', function(Router $router) {
$router->get('/users', [ 'UserController', 'getUsers' ]);
// mehr Routen
}, [ SecurityHeadersMiddleware::class ]);
Cross Site Request Forgery (CSRF)
Cross Site Request Forgery (CSRF) ist ein Angriffstyp, bei dem eine bösartige Website den Browser eines Benutzers dazu bringen kann, eine Anfrage an Ihre Website zu senden. Dies kann verwendet werden, um Aktionen auf Ihrer Website ohne das Wissen des Benutzers auszuführen. Flight bietet keinen integrierten CSRF-Schutzmechanismus, aber Sie können Ihren eigenen leicht mit Middleware implementieren.
Einrichtung
Zuerst müssen Sie ein CSRF-Token generieren und es in der Benutzersitzung speichern. Sie können dann dieses Token in Ihren Formularen verwenden und es überprüfen, wenn das Formular abgeschickt wird. Wir verwenden das flightphp/session-Plugin, um Sitzungen zu verwalten.
// Generiere ein CSRF-Token und speichere es in der Benutzersitzung
// (angenommen, Sie haben ein Session-Objekt erstellt und es an Flight angehängt)
// siehe die Session-Dokumentation für weitere Informationen
Flight::register('session', flight\Session::class);
// Sie müssen nur ein einzelnes Token pro Sitzung generieren (damit es über
// mehrere Tabs und Anfragen für denselben Benutzer funktioniert)
if(Flight::session()->get('csrf_token') === null) {
Flight::session()->set('csrf_token', bin2hex(random_bytes(32)) );
}
Verwendung der standardmäßigen PHP Flight Template
<!-- Verwende das CSRF-Token in deinem Formular -->
<form method="post">
<input type="hidden" name="csrf_token" value="<?= Flight::session()->get('csrf_token') ?>">
<!-- andere Formularfelder -->
</form>
Verwendung von Latte
Sie können auch eine benutzerdefinierte Funktion setzen, um das CSRF-Token in Ihren Latte-Templates auszugeben.
Flight::map('render', function(string $template, array $data, ?string $block): void {
$latte = new Latte\Engine;
// andere Konfigurationen...
// Setze eine benutzerdefinierte Funktion, um das CSRF-Token auszugeben
$latte->addFunction('csrf', function() {
$csrfToken = Flight::session()->get('csrf_token');
return new \Latte\Runtime\Html('<input type="hidden" name="csrf_token" value="' . $csrfToken . '">');
});
$latte->render($finalPath, $data, $block);
});
Und jetzt können Sie in Ihren Latte-Templates die csrf()
-Funktion verwenden, um das CSRF-Token auszugeben.
<form method="post">
{csrf()}
<!-- andere Formularfelder -->
</form>
CSRF-Token überprüfen
Sie können das CSRF-Token mit mehreren Methoden überprüfen.
Middleware
// app/middlewares/CsrfMiddleware.php
namespace app\middleware;
use flight\Engine;
class CsrfMiddleware
{
protected Engine $app;
public function __construct(Engine $app)
{
$this->app = $app;
}
public function before(array $params): void
{
if($this->app->request()->method == 'POST') {
$token = $this->app->request()->data->csrf_token;
if($token !== $this->app->session()->get('csrf_token')) {
$this->app->halt(403, 'Ungültiges CSRF-Token');
}
}
}
}
// index.php oder wo immer Sie Ihre Routen haben
use app\middlewares\CsrfMiddleware;
Flight::group('', function(Router $router) {
$router->get('/users', [ 'UserController', 'getUsers' ]);
// mehr Routen
}, [ CsrfMiddleware::class ]);
Event-Filter
// Dieses Middleware überprüft, ob die Anfrage eine POST-Anfrage ist und ob sie es ist, überprüft es, ob das CSRF-Token gültig ist
Flight::before('start', function() {
if(Flight::request()->method == 'POST') {
// erfasse das CSRF-Token aus den Formularwerten
$token = Flight::request()->data->csrf_token;
if($token !== Flight::session()->get('csrf_token')) {
Flight::halt(403, 'Ungültiges CSRF-Token');
// oder für eine JSON-Antwort
Flight::jsonHalt(['error' => 'Ungültiges CSRF-Token'], 403);
}
}
});
Cross Site Scripting (XSS)
Cross Site Scripting (XSS) ist ein Angriffstyp, bei dem eine bösartige Formulareingabe Code in Ihre Website injizieren kann. Die meisten dieser Möglichkeiten stammen von Formularwerten, die Ihre Endbenutzer ausfüllen werden. Sie sollten niemals der Ausgabe Ihrer Benutzer vertrauen! Nehmen Sie immer an, dass alle von ihnen die besten Hacker der Welt sind. Sie können bösartigen JavaScript- oder HTML-Code in Ihre Seite injizieren. Dieser Code kann verwendet werden, um Informationen von Ihren Benutzern zu stehlen oder Aktionen auf Ihrer Website auszuführen. Mit der View-Klasse von Flight oder einem anderen Templating-Engine wie Latte können Sie die Ausgabe leicht escapen, um XSS-Angriffe zu verhindern.
// Nehmen wir an, der Benutzer ist clever und versucht, dies als seinen Namen zu verwenden
$name = '<script>alert("XSS")</script>';
// Dies wird die Ausgabe escapen
Flight::view()->set('name', $name);
// Dies wird ausgeben: <script>alert("XSS")</script>
// Wenn Sie etwas wie Latte als Ihre View-Klasse registriert haben, wird es dies auch automatisch escapen.
Flight::view()->render('template', ['name' => $name]);
SQL Injection
SQL Injection ist ein Angriffstyp, bei dem ein bösartiger Benutzer SQL-Code in Ihre Datenbank injizieren kann. Dies kann verwendet werden, um Informationen aus Ihrer Datenbank zu stehlen oder Aktionen auf Ihrer Datenbank auszuführen. Wiederum sollten Sie niemals Eingaben von Ihren Benutzern vertrauen! Nehmen Sie immer an, dass sie blutrünstig sind. Die Verwendung vorbereiteter Anweisungen in Ihren PDO
-Objekten wird SQL-Injection verhindern.
// Angenommen, Sie haben Flight::db() als Ihr PDO-Objekt registriert
$statement = Flight::db()->prepare('SELECT * FROM users WHERE username = :username');
$statement->execute([':username' => $username]);
$users = $statement->fetchAll();
// Wenn Sie die PdoWrapper-Klasse verwenden, kann dies leicht in einer Zeile erledigt werden
$users = Flight::db()->fetchAll('SELECT * FROM users WHERE username = :username', [ 'username' => $username ]);
// Sie können dasselbe mit einem PDO-Objekt mit ?-Platzhaltern tun
$statement = Flight::db()->fetchAll('SELECT * FROM users WHERE username = ?', [ $username ]);
Unsicheres Beispiel
Das Folgende ist der Grund, warum wir SQL-vorbereitete Anweisungen verwenden, um vor unschuldigen Beispielen wie dem unten zu schützen:
// Endbenutzer füllt ein Webformular aus.
// Für den Wert des Formulars gibt der Hacker etwas wie dies ein:
$username = "' OR 1=1; -- ";
$sql = "SELECT * FROM users WHERE username = '$username' LIMIT 5";
$users = Flight::db()->fetchAll($sql);
// Nachdem die Abfrage aufgebaut ist, sieht sie so aus
// SELECT * FROM users WHERE username = '' OR 1=1; -- LIMIT 5
// Es sieht seltsam aus, aber es ist eine gültige Abfrage, die funktioniert. Tatsächlich
// ist es ein sehr häufiger SQL-Injection-Angriff, der alle Benutzer zurückgibt.
var_dump($users); // dies wird alle Benutzer in der Datenbank ausgeben, nicht nur den einen einzelnen Benutzernamen
CORS
Cross-Origin Resource Sharing (CORS) ist ein Mechanismus, der es vielen Ressourcen (z. B. Schriftarten, JavaScript usw.) auf einer Webseite ermöglicht, von einer anderen Domain außerhalb der Domain angefordert zu werden, von der die Ressource stammt. Flight hat keine integrierte Funktionalität, aber dies kann leicht mit einem Hook gehandhabt werden, der vor dem Aufruf der Flight::start()
-Methode ausgeführt wird.
// app/utils/CorsUtil.php
namespace app\utils;
class CorsUtil
{
public function set(array $params): void
{
$request = Flight::request();
$response = Flight::response();
if ($request->getVar('HTTP_ORIGIN') !== '') {
$this->allowOrigins();
$response->header('Access-Control-Allow-Credentials', 'true');
$response->header('Access-Control-Max-Age', '86400');
}
if ($request->method === 'OPTIONS') {
if ($request->getVar('HTTP_ACCESS_CONTROL_REQUEST_METHOD') !== '') {
$response->header(
'Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, PATCH, OPTIONS, HEAD'
);
}
if ($request->getVar('HTTP_ACCESS_CONTROL_REQUEST_HEADERS') !== '') {
$response->header(
"Access-Control-Allow-Headers",
$request->getVar('HTTP_ACCESS_CONTROL_REQUEST_HEADERS')
);
}
$response->status(200);
$response->send();
exit;
}
}
private function allowOrigins(): void
{
// Passen Sie Ihre erlaubten Hosts hier an.
$allowed = [
'capacitor://localhost',
'ionic://localhost',
'http://localhost',
'http://localhost:4200',
'http://localhost:8080',
'http://localhost:8100',
];
$request = Flight::request();
if (in_array($request->getVar('HTTP_ORIGIN'), $allowed, true) === true) {
$response = Flight::response();
$response->header("Access-Control-Allow-Origin", $request->getVar('HTTP_ORIGIN'));
}
}
}
// index.php oder wo immer Sie Ihre Routen haben
$CorsUtil = new CorsUtil();
// Dies muss vor dem Ausführen von start ausgeführt werden.
Flight::before('start', [ $CorsUtil, 'setupCors' ]);
Fehlerbehandlung
Verbergen Sie sensible Fehlerdetails in der Produktion, um das Austreten von Informationen an Angreifer zu vermeiden. In der Produktion protokollieren Sie Fehler anstatt sie anzuzeigen, mit display_errors
auf 0
gesetzt.
// In Ihrer bootstrap.php oder index.php
// Fügen Sie dies zu Ihrer app/config/config.php hinzu
$environment = ENVIRONMENT;
if ($environment === 'production') {
ini_set('display_errors', 0); // Deaktiviere Fehlanzeige
ini_set('log_errors', 1); // Protokolliere Fehler stattdessen
ini_set('error_log', '/path/to/error.log');
}
// In Ihren Routen oder Controllern
// Verwenden Sie Flight::halt() für kontrollierte Fehlerantworten
Flight::halt(403, 'Zugriff verweigert');
Eingabe-Sanitization
Vertrauen Sie niemals Benutzereingaben. Sanitieren Sie sie mit filter_var, bevor Sie sie verarbeiten, um zu verhindern, dass bösartige Daten eindringen.
// Nehmen wir an, eine $_POST-Anfrage mit $_POST['input'] und $_POST['email']
// Sanitisiere eine String-Eingabe
$clean_input = filter_var(Flight::request()->data->input, FILTER_SANITIZE_STRING);
// Sanitisiere eine E-Mail
$clean_email = filter_var(Flight::request()->data->email, FILTER_SANITIZE_EMAIL);
Passwort-Hashing
Speichern Sie Passwörter sicher und verifizieren Sie sie sicher mit PHPs integrierten Funktionen wie password_hash und password_verify. Passwörter sollten niemals im Klartext gespeichert werden, noch sollten sie mit reversiblen Methoden verschlüsselt werden. Hashing stellt sicher, dass selbst wenn Ihre Datenbank kompromittiert wird, die tatsächlichen Passwörter geschützt bleiben.
$password = Flight::request()->data->password;
// Hash ein Passwort beim Speichern (z. B. während der Registrierung)
$hashed_password = password_hash($password, PASSWORD_DEFAULT);
// Verifiziere ein Passwort (z. B. während des Logins)
if (password_verify($password, $stored_hash)) {
// Passwort stimmt überein
}
Ratenbegrenzung
Schützen Sie vor Brute-Force-Angriffen oder Denial-of-Service-Angriffen, indem Sie Anfrageraten mit einem Cache begrenzen.
// Angenommen, Sie haben flightphp/cache installiert und registriert
// Verwendung von flightphp/cache in einem Filter
Flight::before('start', function() {
$cache = Flight::cache();
$ip = Flight::request()->ip;
$key = "rate_limit_{$ip}";
$attempts = (int) $cache->retrieve($key);
if ($attempts >= 10) {
Flight::halt(429, 'Zu viele Anfragen');
}
$cache->set($key, $attempts + 1, 60); // Zurücksetzen nach 60 Sekunden
});
Siehe auch
- Sessions - Wie man Benutzersitzungen sicher verwaltet.
- Templates - Verwendung von Templates, um Ausgabe automatisch zu escapen und XSS zu verhindern.
- PDO Wrapper - Vereinfachte Datenbankinteraktionen mit vorbereiteten Anweisungen.
- Middleware - Wie man Middleware verwendet, um den Prozess des Hinzufügens von Sicherheits-Headern zu vereinfachen.
- Responses - Wie man HTTP-Antworten mit sicheren Headern anpasst.
- Requests - Wie man Benutzereingaben handhabt und sanitisiert.
- filter_var - PHP-Funktion für Eingabe-Sanitization.
- password_hash - PHP-Funktion für sicheres Passwort-Hashing.
- password_verify - PHP-Funktion zum Verifizieren gehasheter Passwörter.
Fehlerbehebung
- Beziehen Sie sich auf den Abschnitt "Siehe auch" oben für Fehlerbehebungsinformationen im Zusammenhang mit Problemen mit Komponenten des Flight Frameworks.
Änderungsprotokoll
- v3.1.0 - Hinzugefügte Abschnitte zu CORS, Fehlerbehandlung, Eingabe-Sanitization, Passwort-Hashing und Ratenbegrenzung.
- v2.0 - Hinzugefügtes Escaping für Standard-Views, um XSS zu verhindern.
Learn/routing
Routing
Übersicht
Routing in Flight PHP ordnet URL-Muster Callback-Funktionen oder Klassenmethoden zu, um schnelle und einfache Anfragenverarbeitung zu ermöglichen. Es ist für minimale Overhead, benutzerfreundliche Nutzung für Anfänger und Erweiterbarkeit ohne externe Abhängigkeiten konzipiert.
Verständnis
Routing ist der Kernmechanismus, der HTTP-Anfragen mit der Anwendungslogik in Flight verbindet. Durch das Definieren von Routen spezifizieren Sie, wie verschiedene URLs spezifischen Code auslösen, sei es durch Funktionen, Klassenmethoden oder Controller-Aktionen. Das Routing-System von Flight ist flexibel und unterstützt grundlegende Muster, benannte Parameter, reguläre Ausdrücke sowie erweiterte Funktionen wie Dependency Injection und ressourcenorientiertes Routing. Dieser Ansatz hält Ihren Code organisiert und leicht wartbar, während er für Anfänger schnell und einfach bleibt und für fortgeschrittene Benutzer erweiterbar ist.
Hinweis: Möchten Sie mehr über Routing erfahren? Schauen Sie auf der Seite "why a framework?" für eine detailliertere Erklärung nach.
Grundlegende Nutzung
Definieren einer einfachen Route
Grundlegendes Routing in Flight erfolgt durch das Abgleichen eines URL-Musters mit einer Callback-Funktion oder einem Array aus einer Klasse und Methode.
Flight::route('/', function(){
echo 'hello world!';
});
Routen werden in der Reihenfolge abgeglichen, in der sie definiert werden. Die erste Route, die zu einer Anfrage passt, wird ausgeführt.
Verwendung von Funktionen als Callbacks
Der Callback kann jedes aufrufbare Objekt sein. Sie können also eine normale Funktion verwenden:
function hello() {
echo 'hello world!';
}
Flight::route('/', 'hello');
Verwendung von Klassen und Methoden als Controller
Sie können auch eine Methode (statisch oder nicht) einer Klasse verwenden:
class GreetingController {
public function hello() {
echo 'hello world!';
}
}
Flight::route('/', [ 'GreetingController','hello' ]);
// oder
Flight::route('/', [ GreetingController::class, 'hello' ]); // bevorzugte Methode
// oder
Flight::route('/', [ 'GreetingController::hello' ]);
// oder
Flight::route('/', [ 'GreetingController->hello' ]);
Oder indem Sie zuerst ein Objekt erstellen und dann die Methode aufrufen:
use flight\Engine;
// GreetingController.php
class GreetingController
{
protected Engine $app
public function __construct(Engine $app) {
$this->app = $app;
$this->name = 'John Doe';
}
public function hello() {
echo "Hello, {$this->name}!";
}
}
// index.php
$app = Flight::app();
$greeting = new GreetingController($app);
Flight::route('/', [ $greeting, 'hello' ]);
Hinweis: Standardmäßig wird beim Aufruf eines Controllers im Framework die Klasse
flight\Engine
immer injiziert, es sei denn, Sie spezifizieren es über einen Dependency Injection Container.
Methode-spezifisches Routing
Standardmäßig werden Routenmuster gegen alle Anfragemethoden abgeglichen. Sie können auf spezifische Methoden reagieren, indem Sie einen Bezeichner vor die URL stellen.
Flight::route('GET /', function () {
echo 'I received a GET request.';
});
Flight::route('POST /', function () {
echo 'I received a POST request.';
});
// Sie können Flight::get() nicht für Routen verwenden, da dies eine Methode ist,
// um Variablen zu erhalten, nicht um eine Route zu erstellen.
Flight::post('/', function() { /* code */ });
Flight::patch('/', function() { /* code */ });
Flight::put('/', function() { /* code */ });
Flight::delete('/', function() { /* code */ });
Sie können auch mehrere Methoden auf einen einzigen Callback abbilden, indem Sie den |
-Trenner verwenden:
Flight::route('GET|POST /', function () {
echo 'I received either a GET or a POST request.';
});
Spezielle Behandlung für HEAD- und OPTIONS-Anfragen
Flight bietet integrierte Behandlung für HEAD
- und OPTIONS
-HTTP-Anfragen:
HEAD-Anfragen
- HEAD-Anfragen werden genauso wie
GET
-Anfragen behandelt, aber Flight entfernt automatisch den Response-Body, bevor er an den Client gesendet wird. - Das bedeutet, Sie können eine Route für
GET
definieren, und HEAD-Anfragen an dieselbe URL geben nur Header zurück (keinen Inhalt), wie es HTTP-Standards erwarten.
Flight::route('GET /info', function() {
echo 'This is some info!';
});
// Eine HEAD-Anfrage an /info gibt dieselben Header zurück, aber keinen Body.
OPTIONS-Anfragen
OPTIONS
-Anfragen werden automatisch von Flight für jede definierte Route behandelt.
- Wenn eine OPTIONS-Anfrage empfangen wird, antwortet Flight mit einem Status
204 No Content
und einemAllow
-Header, der alle unterstützten HTTP-Methoden für diese Route auflistet. - Sie müssen keine separate Route für OPTIONS definieren.
// Für eine Route, die als:
Flight::route('GET|POST /users', function() { /* ... */ });
// Eine OPTIONS-Anfrage an /users antwortet mit:
//
// Status: 204 No Content
// Allow: GET, POST, HEAD, OPTIONS
Verwendung des Router-Objekts
Zusätzlich können Sie das Router-Objekt abrufen, das einige Hilfsmethoden für Sie bietet:
$router = Flight::router();
// mapped alle Methoden genau wie Flight::route()
$router->map('/', function() {
echo 'hello world!';
});
// GET-Anfrage
$router->get('/users', function() {
echo 'users';
});
$router->post('/users', function() { /* code */});
$router->put('/users/update/@id', function() { /* code */});
$router->delete('/users/@id', function() { /* code */});
$router->patch('/users/@id', function() { /* code */});
Reguläre Ausdrücke (Regex)
Sie können reguläre Ausdrücke in Ihren Routen verwenden:
Flight::route('/user/[0-9]+', function () {
// Dies passt zu /user/1234
});
Obwohl diese Methode verfügbar ist, wird empfohlen, benannte Parameter oder benannte Parameter mit regulären Ausdrücken zu verwenden, da sie lesbarer und einfacher zu warten sind.
Benannte Parameter
Sie können benannte Parameter in Ihren Routen spezifizieren, die an Ihre Callback-Funktion weitergegeben werden. Dies dient hauptsächlich der Lesbarkeit der Route. Bitte lesen Sie den Abschnitt unten zu wichtigen Einschränkungen.
Flight::route('/@name/@id', function (string $name, string $id) {
echo "hello, $name ($id)!";
});
Sie können auch reguläre Ausdrücke mit Ihren benannten Parametern kombinieren, indem Sie den :
-Trenner verwenden:
Flight::route('/@name/@id:[0-9]{3}', function (string $name, string $id) {
// Dies passt zu /bob/123
// Passt aber nicht zu /bob/12345
});
Hinweis: Das Abgleichen von Regex-Gruppen
()
mit positionsbasierten Parametern wird nicht unterstützt. Beispiel::'\(
Wichtige Einschränkung
Während im obigen Beispiel @name
direkt mit der Variable $name
verknüpft zu sein scheint, ist das nicht der Fall. Die Reihenfolge der Parameter in der Callback-Funktion bestimmt, was weitergegeben wird. Wenn Sie die Reihenfolge der Parameter in der Callback-Funktion umkehren würden, würden auch die Variablen umgekehrt. Hier ein Beispiel:
Flight::route('/@name/@id', function (string $id, string $name) {
echo "hello, $name ($id)!";
});
Und wenn Sie zur folgenden URL gehen: /bob/123
, wäre die Ausgabe hello, 123 (bob)!
.
Seien Sie vorsichtig, wenn Sie Ihre Routen und Callback-Funktionen einrichten!
Optionale Parameter
Sie können benannte Parameter spezifizieren, die optional für das Abgleichen sind, indem Sie Segmente in Klammern setzen.
Flight::route(
'/blog(/@year(/@month(/@day)))',
function(?string $year, ?string $month, ?string $day) {
// Dies passt zu den folgenden URLs:
// /blog/2012/12/10
// /blog/2012/12
// /blog/2012
// /blog
}
);
Optionale Parameter, die nicht abgeglichen werden, werden als NULL
weitergegeben.
Wildcard-Routing
Das Abgleichen erfolgt nur auf einzelne URL-Segmente. Wenn Sie mehrere Segmente abgleichen möchten, können Sie den *
-Wildcard verwenden.
Flight::route('/blog/*', function () {
// Dies passt zu /blog/2000/02/01
});
Um alle Anfragen an einen einzigen Callback zu routen, können Sie Folgendes tun:
Flight::route('*', function () {
// Etwas tun
});
404 Not Found Handler
Standardmäßig sendet Flight bei einer nicht gefundenen URL eine sehr einfache und schlichte HTTP 404 Not Found
-Antwort.
Wenn Sie eine personalisiertere 404-Antwort haben möchten, können Sie Ihre eigene notFound
-Methode mappen:
Flight::map('notFound', function() {
$url = Flight::request()->url;
// Sie könnten auch Flight::render() mit einer benutzerdefinierten Vorlage verwenden.
$output = <<<HTML
<h1>My Custom 404 Not Found</h1>
<h3>The page you have requested {$url} could not be found.</h3>
HTML;
$this->response()
->clearBody()
->status(404)
->write($output)
->send();
});
Method Not Found Handler
Standardmäßig sendet Flight bei einer gefundenen URL, aber nicht erlaubten Methode, eine sehr einfache und schlichte HTTP 405 Method Not Allowed
-Antwort (z. B. Method Not Allowed. Allowed Methods are: GET, POST). Es enthält auch einen Allow
-Header mit den erlaubten Methoden für diese URL.
Wenn Sie eine personalisiertere 405-Antwort haben möchten, können Sie Ihre eigene methodNotFound
-Methode mappen:
use flight\net\Route;
Flight::map('methodNotFound', function(Route $route) {
$url = Flight::request()->url;
$methods = implode(', ', $route->methods);
// Sie könnten auch Flight::render() mit einer benutzerdefinierten Vorlage verwenden.
$output = <<<HTML
<h1>My Custom 405 Method Not Allowed</h1>
<h3>The method you have requested for {$url} is not allowed.</h3>
<p>Allowed Methods are: {$methods}</p>
HTML;
$this->response()
->clearBody()
->status(405)
->setHeader('Allow', $methods)
->write($output)
->send();
});
Erweiterte Nutzung
Dependency Injection in Routen
Wenn Sie Dependency Injection über einen Container (PSR-11, PHP-DI, Dice usw.) verwenden möchten, ist der einzige Routentyp, bei dem dies verfügbar ist, entweder das direkte Erstellen des Objekts selbst und die Verwendung des Containers, um Ihr Objekt zu erstellen, oder Sie können Strings verwenden, um die Klasse und Methode zum Aufrufen zu definieren. Sie können zur Seite Dependency Injection für weitere Informationen gehen.
Hier ein kurzes Beispiel:
use flight\database\PdoWrapper;
// Greeting.php
class Greeting
{
protected PdoWrapper $pdoWrapper;
public function __construct(PdoWrapper $pdoWrapper) {
$this->pdoWrapper = $pdoWrapper;
}
public function hello(int $id) {
// do something with $this->pdoWrapper
$name = $this->pdoWrapper->fetchField("SELECT name FROM users WHERE id = ?", [ $id ]);
echo "Hello, world! My name is {$name}!";
}
}
// index.php
// Setup the container with whatever params you need
// See the Dependency Injection page for more information on PSR-11
$dice = new \Dice\Dice();
// Don't forget to reassign the variable with '$dice = '!!!!!
$dice = $dice->addRule('flight\database\PdoWrapper', [
'shared' => true,
'constructParams' => [
'mysql:host=localhost;dbname=test',
'root',
'password'
]
]);
// Register the container handler
Flight::registerContainerHandler(function($class, $params) use ($dice) {
return $dice->create($class, $params);
});
// Routes like normal
Flight::route('/hello/@id', [ 'Greeting', 'hello' ]);
// or
Flight::route('/hello/@id', 'Greeting->hello');
// or
Flight::route('/hello/@id', 'Greeting::hello');
Flight::start();
Ausführung an nächste Route weitergeben
Veraltet
Sie können die Ausführung an die nächste passende Route weitergeben, indem Sie true
aus Ihrer Callback-Funktion zurückgeben.
Flight::route('/user/@name', function (string $name) {
// Check some condition
if ($name !== "Bob") {
// Continue to next route
return true;
}
});
Flight::route('/user/*', function () {
// This will get called
});
Es wird nun empfohlen, Middleware für komplexe Anwendungsfälle wie diesen zu verwenden.
Route-Aliasing
Durch das Zuweisen eines Aliases zu einer Route können Sie diesen Alias später dynamisch in Ihrer App aufrufen, um ihn später in Ihrem Code zu generieren (z. B. ein Link in einer HTML-Vorlage oder das Generieren einer Redirect-URL).
Flight::route('/users/@id', function($id) { echo 'user:'.$id; }, false, 'user_view');
// or
Flight::route('/users/@id', function($id) { echo 'user:'.$id; })->setAlias('user_view');
// later in code somewhere
class UserController {
public function update() {
// code to save user...
$id = $user['id']; // 5 for example
$redirectUrl = Flight::getUrl('user_view', [ 'id' => $id ]); // will return '/users/5'
Flight::redirect($redirectUrl);
}
}
Dies ist besonders hilfreich, wenn sich Ihre URL ändert. Im obigen Beispiel, sagen wir, dass Benutzer zu /admin/users/@id
verschoben wurden.
Mit Aliasing vorhanden für die Route müssen Sie nicht mehr alle alten URLs in Ihrem Code finden und ändern, da der Alias nun /admin/users/5
zurückgibt, wie im obigen Beispiel.
Route-Aliasing funktioniert auch in Gruppen:
Flight::group('/users', function() {
Flight::route('/@id', function($id) { echo 'user:'.$id; }, false, 'user_view');
// or
Flight::route('/@id', function($id) { echo 'user:'.$id; })->setAlias('user_view');
});
Überprüfen von Routeninformationen
Wenn Sie die passende Routeninformation überprüfen möchten, gibt es 2 Wege, dies zu tun:
- Sie können die
executedRoute
-Eigenschaft auf demFlight::router()
-Objekt verwenden. - Sie können das Routenobjekt anfordern, das an Ihren Callback weitergegeben wird, indem Sie
true
als dritten Parameter in der Routenmethode übergeben. Das Routenobjekt wird immer der letzte Parameter sein, der an Ihre Callback-Funktion weitergegeben wird.
executedRoute
Flight::route('/', function() {
$route = Flight::router()->executedRoute;
// Do something with $route
// Array of HTTP methods matched against
$route->methods;
// Array of named parameters
$route->params;
// Matching regular expression
$route->regex;
// Contains the contents of any '*' used in the URL pattern
$route->splat;
// Shows the url path....if you really need it
$route->pattern;
// Shows what middleware is assigned to this
$route->middleware;
// Shows the alias assigned to this route
$route->alias;
});
Hinweis: Die
executedRoute
-Eigenschaft wird nur gesetzt, nachdem eine Route ausgeführt wurde. Wenn Sie versuchen, darauf zuzugreifen, bevor eine Route ausgeführt wurde, ist sieNULL
. Sie können executedRoute auch in Middleware verwenden!
true
in Routendefinition übergeben
Flight::route('/', function(\flight\net\Route $route) {
// Array of HTTP methods matched against
$route->methods;
// Array of named parameters
$route->params;
// Matching regular expression
$route->regex;
// Contains the contents of any '*' used in the URL pattern
$route->splat;
// Shows the url path....if you really need it
$route->pattern;
// Shows what middleware is assigned to this
$route->middleware;
// Shows the alias assigned to this route
$route->alias;
}, true);// <-- Dieser true-Parameter ist das, was das bewirkt
Routengruppierung und Middleware
Es kann vorkommen, dass Sie verwandte Routen gruppieren möchten (z. B. /api/v1
).
Sie können dies tun, indem Sie die group
-Methode verwenden:
Flight::group('/api/v1', function () {
Flight::route('/users', function () {
// Matches /api/v1/users
});
Flight::route('/posts', function () {
// Matches /api/v1/posts
});
});
Sie können sogar Gruppen von Gruppen verschachteln:
Flight::group('/api', function () {
Flight::group('/v1', function () {
// Flight::get() gets variables, it doesn't set a route! See object context below
Flight::route('GET /users', function () {
// Matches GET /api/v1/users
});
Flight::post('/posts', function () {
// Matches POST /api/v1/posts
});
Flight::put('/posts/1', function () {
// Matches PUT /api/v1/posts
});
});
Flight::group('/v2', function () {
// Flight::get() gets variables, it doesn't set a route! See object context below
Flight::route('GET /users', function () {
// Matches GET /api/v2/users
});
});
});
Gruppierung mit Objektkontext
Sie können die Routengruppierung immer noch mit dem Engine
-Objekt auf folgende Weise verwenden:
$app = Flight::app();
$app->group('/api/v1', function (Router $router) {
// user the $router variable
$router->get('/users', function () {
// Matches GET /api/v1/users
});
$router->post('/posts', function () {
// Matches POST /api/v1/posts
});
});
Hinweis: Dies ist die bevorzugte Methode, um Routen und Gruppen mit dem
$router
-Objekt zu definieren.
Gruppierung mit Middleware
Sie können auch Middleware einer Gruppe von Routen zuweisen:
Flight::group('/api/v1', function () {
Flight::route('/users', function () {
// Matches /api/v1/users
});
}, [ MyAuthMiddleware::class ]); // oder [ new MyAuthMiddleware() ], wenn Sie eine Instanz verwenden möchten
Weitere Details finden Sie auf der Seite group middleware.
Ressourcen-Routing
Sie können eine Reihe von Routen für eine Ressource mit der resource
-Methode erstellen. Dies erstellt eine Reihe von Routen für eine Ressource, die den RESTful-Konventionen folgt.
Um eine Ressource zu erstellen, tun Sie Folgendes:
Flight::resource('/users', UsersController::class);
Und was im Hintergrund passiert, ist, dass es die folgenden Routen erstellt:
[
'index' => 'GET /users',
'create' => 'GET /users/create',
'store' => 'POST /users',
'show' => 'GET /users/@id',
'edit' => 'GET /users/@id/edit',
'update' => 'PUT /users/@id',
'destroy' => 'DELETE /users/@id'
]
Und Ihr Controller verwendet die folgenden Methoden:
class UsersController
{
public function index(): void
{
}
public function show(string $id): void
{
}
public function create(): void
{
}
public function store(): void
{
}
public function edit(string $id): void
{
}
public function update(string $id): void
{
}
public function destroy(string $id): void
{
}
}
Hinweis: Sie können die neu hinzugefügten Routen mit
runway
anzeigen, indem Siephp runway routes
ausführen.
Anpassen von Ressourcen-Routen
Es gibt einige Optionen, um die Ressourcen-Routen zu konfigurieren.
Alias Base
Sie können die aliasBase
konfigurieren. Standardmäßig ist der Alias der letzte Teil der angegebenen URL.
Zum Beispiel würde /users/
zu einem aliasBase
von users
führen. Wenn diese Routen erstellt werden,
sind die Aliase users.index
, users.create
usw. Wenn Sie den Alias ändern möchten, setzen Sie die aliasBase
auf den gewünschten Wert.
Flight::resource('/users', UsersController::class, [ 'aliasBase' => 'user' ]);
Only und Except
Sie können auch spezifizieren, welche Routen Sie erstellen möchten, indem Sie die Optionen only
und except
verwenden.
// Whitelist only these methods and blacklist the rest
Flight::resource('/users', UsersController::class, [ 'only' => [ 'index', 'show' ] ]);
// Blacklist only these methods and whitelist the rest
Flight::resource('/users', UsersController::class, [ 'except' => [ 'create', 'store', 'edit', 'update', 'destroy' ] ]);
Dies sind im Wesentlichen Whitelist- und Blacklist-Optionen, damit Sie spezifizieren können, welche Routen Sie erstellen möchten.
Middleware
Sie können auch Middleware spezifizieren, die auf jeder der von der resource
-Methode erstellten Routen ausgeführt wird.
Flight::resource('/users', UsersController::class, [ 'middleware' => [ MyAuthMiddleware::class ] ]);
Streaming-Antworten
Sie können nun Antworten an den Client streamen, indem Sie stream()
oder streamWithHeaders()
verwenden.
Dies ist nützlich für das Senden großer Dateien, lang laufender Prozesse oder das Generieren großer Antworten.
Das Streamen einer Route wird etwas anders gehandhabt als eine normale Route.
Hinweis: Streaming-Antworten sind nur verfügbar, wenn Sie
flight.v2.output_buffering
auffalse
gesetzt haben.
Stream mit manuellen Headern
Sie können eine Antwort an den Client streamen, indem Sie die stream()
-Methode auf einer Route verwenden. Wenn Sie
das tun, müssen Sie alle Header manuell setzen, bevor Sie etwas an den Client ausgeben.
Dies geschieht mit der header()
-PHP-Funktion oder der Flight::response()->setRealHeader()
-Methode.
Flight::route('/@filename', function($filename) {
$response = Flight::response();
// obviously you would sanitize the path and whatnot.
$fileNameSafe = basename($filename);
// If you have additional headers to set here after the route has executed
// you must define them before anything is echoed out.
// They must all be a raw call to the header() function or
// a call to Flight::response()->setRealHeader()
header('Content-Disposition: attachment; filename="'.$fileNameSafe.'"');
// or
$response->setRealHeader('Content-Disposition: attachment; filename="'.$fileNameSafe.'"');
$filePath = '/some/path/to/files/'.$fileNameSafe;
if (!is_readable($filePath)) {
Flight::halt(404, 'File not found');
}
// manually set the content length if you'd like
header('Content-Length: '.filesize($filePath));
// or
$response->setRealHeader('Content-Length: '.filesize($filePath));
// Stream the file to the client as it's read
readfile($filePath);
// This is the magic line here
})->stream();
Stream mit Headern
Sie können auch die streamWithHeaders()
-Methode verwenden, um die Header zu setzen, bevor Sie mit dem Streamen beginnen.
Flight::route('/stream-users', function() {
// you can add any additional headers you want here
// you just must use header() or Flight::response()->setRealHeader()
// however you pull your data, just as an example...
$users_stmt = Flight::db()->query("SELECT id, first_name, last_name FROM users");
echo '{';
$user_count = count($users);
while($user = $users_stmt->fetch(PDO::FETCH_ASSOC)) {
echo json_encode($user);
if(--$user_count > 0) {
echo ',';
}
// This is required to send the data to the client
ob_flush();
}
echo '}';
// This is how you'll set the headers before you start streaming.
})->streamWithHeaders([
'Content-Type' => 'application/json',
'Content-Disposition' => 'attachment; filename="users.json"',
// optional status code, defaults to 200
'status' => 200
]);
Siehe auch
- Middleware - Verwendung von Middleware mit Routen für Authentifizierung, Logging usw.
- Dependency Injection - Vereinfachung der Objekterstellung und -verwaltung in Routen.
- Why a Framework? - Verständnis der Vorteile der Verwendung eines Frameworks wie Flight.
- Extending - Wie man Flight mit eigener Funktionalität erweitert, einschließlich der
notFound
-Methode. - php.net: preg_match - PHP-Funktion für reguläre Ausdrucksabgleichung.
Fehlerbehebung
- Routenparameter werden nach Reihenfolge abgeglichen, nicht nach Namen. Stellen Sie sicher, dass die Parameterreihenfolge des Callbacks zur Routendefinition passt.
- Die Verwendung von
Flight::get()
definiert keine Route; verwenden SieFlight::route('GET /...')
für Routing oder den Router-Objektkontext in Gruppen (z. B.$router->get(...)
). - Die executedRoute-Eigenschaft wird nur nach der Ausführung einer Route gesetzt; sie ist NULL vor der Ausführung.
- Streaming erfordert, dass die Legacy-Flight-Output-Buffering-Funktionalität deaktiviert ist (
flight.v2.output_buffering = false
). - Für Dependency Injection unterstützen nur bestimmte Routendefinitionen containerbasierte Instanziierung.
404 Not Found oder unerwartetes Routenverhalten
Wenn Sie einen 404 Not Found-Fehler sehen (aber Sie schwören bei Ihrem Leben, dass er wirklich da ist und es kein Tippfehler ist), könnte dies tatsächlich ein Problem damit sein, dass Sie einen Wert in Ihrem Routen-Endpunkt zurückgeben, anstatt ihn nur auszugeben. Der Grund dafür ist absichtlich, könnte aber einige Entwickler überraschen.
Flight::route('/hello', function(){
// This might cause a 404 Not Found error
return 'Hello World';
});
// What you probably want
Flight::route('/hello', function(){
echo 'Hello World';
});
Der Grund dafür ist ein spezieller Mechanismus, der in den Router integriert ist und die Rückgabeausgabe als Signal behandelt, um "zur nächsten Route zu gehen". Sie können das Verhalten in dem Abschnitt Routing dokumentiert sehen.
Changelog
- v3: Hinzugefügt Ressourcen-Routing, Route-Aliasing und Streaming-Unterstützung, Routengruppen und Middleware-Unterstützung.
- v1: Überwiegender Teil der grundlegenden Funktionen verfügbar.
Learn/learn
Über Flight lernen
Flight ist ein schnelles, einfaches, erweiterbares Framework für PHP. Es ist sehr vielseitig und kann für die Erstellung aller Arten von Webanwendungen verwendet werden. Es ist mit dem Gedanken an Einfachheit gebaut und auf eine Weise geschrieben, die leicht zu verstehen und zu verwenden ist.
Hinweis: Sie werden Beispiele sehen, die
Flight::
als statische Variable verwenden, und einige, die das Engine-Objekt$app->
verwenden. Beide funktionieren austauschbar mit dem anderen.$app
und$this->app
in einem Controller/Middleware ist der empfohlene Ansatz vom Flight-Team.
Kernkomponenten
Routing
Lernen Sie, wie Sie Routen für Ihre Webanwendung verwalten. Dies umfasst auch das Gruppieren von Routen, Routenparameter und Middleware.
Middleware
Lernen Sie, wie Sie Middleware verwenden, um Anfragen und Antworten in Ihrer Anwendung zu filtern.
Autoloading
Lernen Sie, wie Sie Ihre eigenen Klassen in Ihrer Anwendung autoloaden.
Requests
Lernen Sie, wie Sie Anfragen und Antworten in Ihrer Anwendung handhaben.
Responses
Lernen Sie, wie Sie Antworten an Ihre Benutzer senden.
HTML Templates
Lernen Sie, wie Sie den integrierten View-Engine verwenden, um Ihre HTML-Templates zu rendern.
Security
Lernen Sie, wie Sie Ihre Anwendung vor gängigen Sicherheitsbedrohungen schützen.
Configuration
Lernen Sie, wie Sie das Framework für Ihre Anwendung konfigurieren.
Event Manager
Lernen Sie, wie Sie das Event-System verwenden, um benutzerdefinierte Events zu Ihrer Anwendung hinzuzufügen.
Extending Flight
Lernen Sie, wie Sie das Framework erweitern, indem Sie eigene Methoden und Klassen hinzufügen.
Method Hooks and Filtering
Lernen Sie, wie Sie Event-Hooks zu Ihren Methoden und internen Framework-Methoden hinzufügen.
Dependency Injection Container (DIC)
Lernen Sie, wie Sie Dependency-Injection-Container (DIC) verwenden, um die Abhängigkeiten Ihrer Anwendung zu verwalten.
Utility-Klassen
Collections
Collections werden verwendet, um Daten zu speichern und sie als Array oder als Objekt zugänglich zu machen, um die Bedienung zu erleichtern.
JSON Wrapper
Dies bietet einige einfache Funktionen, um das Encodieren und Decodieren von JSON konsistent zu gestalten.
PDO Wrapper
PDO kann manchmal mehr Kopfschmerzen verursachen als notwendig. Diese einfache Wrapper-Klasse kann die Interaktion mit Ihrer Datenbank erheblich erleichtern.
Uploaded File Handler
Eine einfache Klasse, die hilft, hochgeladene Dateien zu verwalten und sie an einen permanenten Speicherort zu verschieben.
Wichtige Konzepte
Warum ein Framework?
Hier ist ein kurzer Artikel darüber, warum Sie ein Framework verwenden sollten. Es ist eine gute Idee, die Vorteile der Verwendung eines Frameworks zu verstehen, bevor Sie eines einsetzen.
Zusätzlich wurde ein exzellentes Tutorial von @lubiana erstellt. Obwohl es nicht insbesondere auf Flight eingeht, hilft diese Anleitung Ihnen, einige der wichtigsten Konzepte rund um ein Framework zu verstehen und warum sie nützlich sind. Sie finden das Tutorial hier.
Flight im Vergleich zu anderen Frameworks
Wenn Sie von einem anderen Framework wie Laravel, Slim, Fat-Free oder Symfony zu Flight migrieren, hilft diese Seite Ihnen, die Unterschiede zwischen den beiden zu verstehen.
Andere Themen
Unit Testing
Folgen Sie dieser Anleitung, um zu lernen, wie Sie Ihren Flight-Code mit Unit-Tests robust machen.
AI & Developer Experience
Lernen Sie, wie Flight mit AI-Tools und modernen Developer-Workflows zusammenarbeitet, um Ihnen zu helfen, schneller und smarter zu coden.
Migrating v2 -> v3
Rückwärtskompatibilität wurde größtenteils beibehalten, aber es gibt einige Änderungen, die Sie bei der Migration von v2 zu v3 beachten sollten.
Learn/unit_testing
Unit Testing
Übersicht
Unit Testing in Flight hilft Ihnen, sicherzustellen, dass Ihre Anwendung wie erwartet funktioniert, Fehler frühzeitig zu erkennen und Ihren Codebase leichter wartbar zu machen. Flight ist so konzipiert, dass es reibungslos mit PHPUnit zusammenarbeitet, dem beliebtesten PHP-Testing-Framework.
Verständnis
Unit-Tests überprüfen das Verhalten kleiner Teile Ihrer Anwendung (wie Controller oder Services) isoliert. In Flight bedeutet das, zu testen, wie Ihre Routen, Controller und Logik auf verschiedene Eingaben reagieren – ohne auf globalen Zustand oder echte externe Services angewiesen zu sein.
Wichtige Prinzipien:
- Verhalten testen, nicht Implementierung: Konzentrieren Sie sich darauf, was Ihr Code tut, nicht wie er es tut.
- Globalen Zustand vermeiden: Verwenden Sie Dependency Injection anstelle von
Flight::set()
oderFlight::get()
. - Externe Services mocken: Ersetzen Sie Dinge wie Datenbanken oder Mailer durch Test-Doubles.
- Tests schnell und fokussiert halten: Unit-Tests sollten keine echten Datenbanken oder APIs ansprechen.
Grundlegende Verwendung
PHPUnit einrichten
- Installieren Sie PHPUnit mit Composer:
composer require --dev phpunit/phpunit
- Erstellen Sie ein
tests
-Verzeichnis im Root-Verzeichnis Ihres Projekts. - Fügen Sie ein Test-Skript zu Ihrer
composer.json
hinzu:"scripts": { "test": "phpunit --configuration phpunit.xml" }
- Erstellen Sie eine
phpunit.xml
-Datei:<?xml version="1.0" encoding="UTF-8"?> <phpunit bootstrap="vendor/autoload.php"> <testsuites> <testsuite name="Flight Tests"> <directory>tests</directory> </testsuite> </testsuites> </phpunit>
Nun können Sie Ihre Tests mit composer test
ausführen.
Testen eines einfachen Route-Handlers
Nehmen Sie an, Sie haben eine Route, die eine E-Mail validiert:
// index.php
$app->route('POST /register', [ UserController::class, 'register' ]);
// UserController.php
class UserController {
protected $app;
public function __construct(flight\Engine $app) {
$this->app = $app;
}
public function register() {
$email = $this->app->request()->data->email;
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
return $this->app->json(['status' => 'error', 'message' => 'Invalid email']);
}
return $this->app->json(['status' => 'success', 'message' => 'Valid email']);
}
}
Ein einfacher Test für diesen Controller:
use PHPUnit\Framework\TestCase;
use flight\Engine;
class UserControllerTest extends TestCase {
public function testValidEmailReturnsSuccess() {
$app = new Engine();
$app->request()->data->email = 'test@example.com';
$controller = new UserController($app);
$controller->register();
$response = $app->response()->getBody();
$output = json_decode($response, true);
$this->assertEquals('success', $output['status']);
$this->assertEquals('Valid email', $output['message']);
}
public function testInvalidEmailReturnsError() {
$app = new Engine();
$app->request()->data->email = 'invalid-email';
$controller = new UserController($app);
$controller->register();
$response = $app->response()->getBody();
$output = json_decode($response, true);
$this->assertEquals('error', $output['status']);
$this->assertEquals('Invalid email', $output['message']);
}
}
Tipps:
- Simulieren Sie POST-Daten mit
$app->request()->data
. - Vermeiden Sie die Verwendung von
Flight::
-Statiken in Ihren Tests – verwenden Sie die$app
-Instanz.
Dependency Injection für testbare Controller verwenden
Injizieren Sie Abhängigkeiten (wie die Datenbank oder den Mailer) in Ihre Controller, um sie in Tests leicht zu mocken:
use flight\database\PdoWrapper;
class UserController {
protected $app;
protected $db;
protected $mailer;
public function __construct($app, $db, $mailer) {
$this->app = $app;
$this->db = $db;
$this->mailer = $mailer;
}
public function register() {
$email = $this->app->request()->data->email;
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
return $this->app->json(['status' => 'error', 'message' => 'Invalid email']);
}
$this->db->runQuery('INSERT INTO users (email) VALUES (?)', [$email]);
$this->mailer->sendWelcome($email);
return $this->app->json(['status' => 'success', 'message' => 'User registered']);
}
}
Und ein Test mit Mocks:
use PHPUnit\Framework\TestCase;
class UserControllerDICTest extends TestCase {
public function testValidEmailSavesAndSendsEmail() {
$mockDb = $this->createMock(flight\database\PdoWrapper::class);
$mockDb->method('runQuery')->willReturn(true);
$mockMailer = new class {
public $sentEmail = null;
public function sendWelcome($email) { $this->sentEmail = $email; return true; }
};
$app = new flight\Engine();
$app->request()->data->email = 'test@example.com';
$controller = new UserController($app, $mockDb, $mockMailer);
$controller->register();
$response = $app->response()->getBody();
$result = json_decode($response, true);
$this->assertEquals('success', $result['status']);
$this->assertEquals('User registered', $result['message']);
$this->assertEquals('test@example.com', $mockMailer->sentEmail);
}
}
Erweiterte Verwendung
- Mocking: Verwenden Sie die integrierten Mocks von PHPUnit oder anonyme Klassen, um Abhängigkeiten zu ersetzen.
- Controller direkt testen: Instanziieren Sie Controller mit einer neuen
Engine
und mocken Sie Abhängigkeiten. - Übermäßiges Mocking vermeiden: Lassen Sie echte Logik laufen, wo möglich; mocken Sie nur externe Services.
Siehe auch
- Unit Testing Guide - Ein umfassender Leitfaden zu Best Practices für Unit Testing.
- Dependency Injection Container - Wie man DICs verwendet, um Abhängigkeiten zu verwalten und die Testbarkeit zu verbessern.
- Extending - Wie man eigene Helfer hinzufügt oder Kernklassen überschreibt.
- PDO Wrapper - Vereinfacht Datenbankinteraktionen und ist leichter in Tests zu mocken.
- Requests - Behandlung von HTTP-Anfragen in Flight.
- Responses - Versenden von Antworten an Benutzer.
- Unit Testing and SOLID Principles - Lernen Sie, wie SOLID-Prinzipien Ihre Unit-Tests verbessern können.
Fehlerbehebung
- Vermeiden Sie die Verwendung von globalem Zustand (
Flight::set()
,$_SESSION
usw.) in Ihrem Code und Tests. - Wenn Ihre Tests langsam sind, schreiben Sie möglicherweise Integrationstests – mocken Sie externe Services, um Unit-Tests schnell zu halten.
- Wenn die Testeinrichtung komplex ist, überlegen Sie, Ihren Code umzustrukturieren, um Dependency Injection zu verwenden.
Changelog
- v3.15.0 - Beispiele für Dependency Injection und Mocking hinzugefügt.
Learn/flight_vs_symfony
Flight vs Symfony
Was ist Symfony?
Symfony ist eine Reihe von wiederverwendbaren PHP-Komponenten und ein PHP-Framework für Webprojekte.
Das Standardfundament, auf dem die besten PHP-Anwendungen aufgebaut sind. Wählen Sie eine der 50 eigenständigen Komponenten für Ihre eigenen Anwendungen aus.
Beschleunigen Sie die Erstellung und Wartung Ihrer PHP-Webanwendungen. Beenden Sie wiederholende Codieraufgaben und genießen Sie die Kontrolle über Ihren Code.
Vor- und Nachteile im Vergleich zu Flight
Vorteile im Vergleich zu Flight
- Symfony hat ein riesiges Ökosystem von Entwicklern und Modulen, die zur Lösung von üblichen Problemen genutzt werden können.
- Symfony hat ein vollständiges ORM (Doctrine), das zur Interaktion mit Ihrer Datenbank verwendet werden kann.
- Symfony hat eine große Menge an Dokumentation und Tutorials, die zur Erkundung des Frameworks verwendet werden können.
- Symfony verfügt über Podcasts, Konferenzen, Treffen, Videos und weitere Ressourcen, die zur Erkundung des Frameworks genutzt werden können.
- Symfony richtet sich an erfahrene Entwickler, die eine umfangreiche Enterprise-Webanwendung erstellen möchten.
Nachteile im Vergleich zu Flight
- Symfony hat viel mehr im Hintergrund laufen als Flight. Dies geht zu einem dramatischen Kostenaufwand hinsichtlich der Leistung einher. Weitere Informationen finden Sie in den TechEmpower-Benchmarks.
- Flight richtet sich an Entwickler, die eine leichtgewichtige, schnelle und benutzerfreundliche Webanwendung erstellen möchten.
- Flight konzentriert sich auf Einfachheit und Benutzerfreundlichkeit.
- Eine der Kernfunktionen von Flight besteht darin, die Abwärtskompatibilität zu wahren.
- Flight hat keine Abhängigkeiten, während Symfony eine Vielzahl von Abhängigkeiten hat.
- Flight ist für Entwickler gedacht, die sich zum ersten Mal in die Welt der Frameworks begeben.
- Flight kann auch Unternehmensanwendungen erstellen, verfügt jedoch nicht über so viele Beispiele und Tutorials wie Symfony. Es erfordert auch mehr Disziplin seitens des Entwicklers, um die Dinge organisiert und gut strukturiert zu halten.
- Flight gibt dem Entwickler mehr Kontrolle über die Anwendung, während Symfony im Hintergrund einige Magie einfließen lassen kann.
Learn/flight_vs_another_framework
Vergleich von Flight mit einem anderen Framework
Wenn Sie von einem anderen Framework wie Laravel, Slim, Fat-Free oder Symfony zu Flight migrieren, hilft Ihnen diese Seite, die Unterschiede zwischen den beiden zu verstehen.
Laravel
Laravel ist ein funktionsreiches Framework mit allen Extras und einem erstaunlichen, auf Entwickler ausgerichteten Ökosystem, aber zu einem Preis in Leistung und Komplexität.
Sehen Sie den Vergleich zwischen Laravel und Flight.
Slim
Slim ist ein Micro-Framework, das Flight ähnelt. Es ist darauf ausgelegt, leichtgewichtig und einfach zu bedienen zu sein, kann aber etwas komplexer sein als Flight.
Sehen Sie den Vergleich zwischen Slim und Flight.
Fat-Free
Fat-Free ist ein Full-Stack-Framework in einem viel kleineren Paket. Obwohl es alle Werkzeuge im Werkzeugkasten hat, hat es eine Datenarchitektur, die einige Projekte komplexer machen kann, als sie sein müssen.
Sehen Sie den Vergleich zwischen Fat-Free und Flight.
Symfony
Symfony ist ein modulares Enterprise-Level-Framework, das darauf ausgelegt ist, flexibel und skalierbar zu sein. Für kleinere Projekte oder neuere Entwickler kann Symfony etwas überwältigend sein.
Sehen Sie den Vergleich zwischen Symfony und Flight.
Learn/pdo_wrapper
PdoWrapper PDO-Hilfsklasse
Überblick
Die PdoWrapper
-Klasse in Flight ist eine benutzerfreundliche Hilfsklasse für die Arbeit mit Datenbanken unter Verwendung von PDO. Sie vereinfacht gängige Datenbankaufgaben, fügt nützliche Methoden zum Abrufen von Ergebnissen hinzu und gibt Ergebnisse als Collections zurück, um einen einfachen Zugriff zu ermöglichen. Sie unterstützt außerdem Protokollierung von Abfragen und Überwachung der Anwendungsleistung (APM) für fortgeschrittene Anwendungsfälle.
Verständnis
Die Arbeit mit Datenbanken in PHP kann etwas umständlich sein, insbesondere bei der direkten Verwendung von PDO. PdoWrapper
erweitert PDO und fügt Methoden hinzu, die das Abfragen, Abrufen und Behandeln von Ergebnissen erheblich erleichtern. Statt mit vorbereiteten Anweisungen und Abrufmodi zu jonglieren, erhalten Sie einfache Methoden für gängige Aufgaben, und jede Zeile wird als Collection zurückgegeben, sodass Sie Array- oder Objektnotation verwenden können.
Sie können PdoWrapper
als geteilten Dienst in Flight registrieren und es dann überall in Ihrer App über Flight::db()
verwenden.
Grundlegende Verwendung
Registrieren der PDO-Hilfsklasse
Zuerst registrieren Sie die PdoWrapper
-Klasse bei Flight:
Flight::register('db', \flight\database\PdoWrapper::class, [
'mysql:host=localhost;dbname=cool_db_name', 'user', 'pass', [
PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES \'utf8mb4\'',
PDO::ATTR_EMULATE_PREPARES => false,
PDO::ATTR_STRINGIFY_FETCHES => false,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC
]
]);
Nun können Sie Flight::db()
überall verwenden, um Ihre Datenbankverbindung zu erhalten.
Ausführen von Abfragen
runQuery()
function runQuery(string $sql, array $params = []): PDOStatement
Verwenden Sie dies für INSERTs, UPDATEs oder wenn Sie Ergebnisse manuell abrufen möchten:
$db = Flight::db();
$statement = $db->runQuery("SELECT * FROM users WHERE status = ?", ['active']);
while ($row = $statement->fetch()) {
// $row is an array
}
Sie können es auch für Schreibvorgänge verwenden:
$db->runQuery("INSERT INTO users (name) VALUES (?)", ['Alice']);
$db->runQuery("UPDATE users SET name = ? WHERE id = ?", ['Bob', 1]);
fetchField()
function fetchField(string $sql, array $params = []): mixed
Einen einzelnen Wert aus der Datenbank abrufen:
$count = Flight::db()->fetchField("SELECT COUNT(*) FROM users WHERE status = ?", ['active']);
fetchRow()
function fetchRow(string $sql, array $params = []): Collection
Eine einzelne Zeile als Collection (Array-/Objektzugriff) abrufen:
$user = Flight::db()->fetchRow("SELECT * FROM users WHERE id = ?", [123]);
echo $user['name'];
// or
echo $user->name;
fetchAll()
function fetchAll(string $sql, array $params = []): array<Collection>
Alle Zeilen als Array von Collections abrufen:
$users = Flight::db()->fetchAll("SELECT * FROM users WHERE status = ?", ['active']);
foreach ($users as $user) {
echo $user['name'];
// or
echo $user->name;
}
Verwendung von IN()
-Platzhaltern
Sie können einen einzelnen ?
-Platzhalter in einer IN()
-Klausel verwenden und ein Array oder einen komma-getrennten String übergeben:
$ids = [1, 2, 3];
$users = Flight::db()->fetchAll("SELECT * FROM users WHERE id IN (?)", [$ids]);
// or
$users = Flight::db()->fetchAll("SELECT * FROM users WHERE id IN (?)", ['1,2,3']);
Fortgeschrittene Verwendung
Abfrageprotokollierung & APM
Wenn Sie die Abfrageleistung verfolgen möchten, aktivieren Sie die APM-Überwachung bei der Registrierung:
Flight::register('db', \flight\database\PdoWrapper::class, [
'mysql:host=localhost;dbname=cool_db_name', 'user', 'pass', [/* options */], true // letzter Parameter aktiviert APM
]);
Nach dem Ausführen von Abfragen können Sie sie manuell protokollieren, aber das APM protokolliert sie automatisch, wenn es aktiviert ist:
Flight::db()->logQueries();
Dies löst ein Ereignis (flight.db.queries
) mit Verbindungs- und Abfragemetriken aus, das Sie mit dem Ereignissystem von Flight abhören können.
Vollständiges Beispiel
Flight::route('/users', function () {
// Get all users
$users = Flight::db()->fetchAll('SELECT * FROM users');
// Stream all users
$statement = Flight::db()->runQuery('SELECT * FROM users');
while ($user = $statement->fetch()) {
echo $user['name'];
}
// Get a single user
$user = Flight::db()->fetchRow('SELECT * FROM users WHERE id = ?', [123]);
// Get a single value
$count = Flight::db()->fetchField('SELECT COUNT(*) FROM users');
// Special IN() syntax
$users = Flight::db()->fetchAll('SELECT * FROM users WHERE id IN (?)', [[1,2,3,4,5]]);
$users = Flight::db()->fetchAll('SELECT * FROM users WHERE id IN (?)', ['1,2,3,4,5']);
// Insert a new user
Flight::db()->runQuery("INSERT INTO users (name, email) VALUES (?, ?)", ['Bob', 'bob@example.com']);
$insert_id = Flight::db()->lastInsertId();
// Update a user
Flight::db()->runQuery("UPDATE users SET name = ? WHERE id = ?", ['Bob', 123]);
// Delete a user
Flight::db()->runQuery("DELETE FROM users WHERE id = ?", [123]);
// Get the number of affected rows
$statement = Flight::db()->runQuery("UPDATE users SET name = ? WHERE name = ?", ['Bob', 'Sally']);
$affected_rows = $statement->rowCount();
});
Siehe auch
- Collections - Erfahren Sie, wie Sie die Collection-Klasse für einen einfachen Datenzugriff verwenden.
Fehlerbehebung
- Wenn Sie einen Fehler bezüglich der Datenbankverbindung erhalten, überprüfen Sie Ihre DSN, Benutzername, Passwort und Optionen.
- Alle Zeilen werden als Collections zurückgegeben – wenn Sie ein einfaches Array benötigen, verwenden Sie
$collection->getData()
. - Für
IN (?)
-Abfragen stellen Sie sicher, dass Sie ein Array oder einen komma-getrennten String übergeben.
Änderungsprotokoll
- v3.2.0 - Erste Veröffentlichung von PdoWrapper mit grundlegenden Abfrage- und Abrufmethoden.
Learn/dependency_injection_container
Dependency Injection Container
Overview
Der Dependency Injection Container (DIC) ist eine leistungsstarke Erweiterung, die es Ihnen ermöglicht, die Abhängigkeiten Ihrer Anwendung zu verwalten.
Understanding
Dependency Injection (DI) ist ein zentrales Konzept in modernen PHP-Frameworks und wird verwendet, um die Instanziierung und Konfiguration von Objekten zu verwalten. Einige Beispiele für DIC-Bibliotheken sind: flightphp/container, Dice, Pimple, PHP-DI, und league/container.
Ein DIC ist eine elegante Möglichkeit, Ihre Klassen an einem zentralen Ort zu erstellen und zu verwalten. Dies ist nützlich, wenn Sie dasselbe Objekt an mehrere Klassen weitergeben müssen (z. B. an Ihre Controller oder Middleware).
Basic Usage
Die alte Methode könnte so aussehen:
require 'vendor/autoload.php';
// class to manage users from the database
class UserController {
protected PDO $pdo;
public function __construct(PDO $pdo) {
$this->pdo = $pdo;
}
public function view(int $id) {
$stmt = $this->pdo->prepare('SELECT * FROM users WHERE id = :id');
$stmt->execute(['id' => $id]);
print_r($stmt->fetch());
}
}
// in your routes.php file
$db = new PDO('mysql:host=localhost;dbname=test', 'user', 'pass');
$UserController = new UserController($db);
Flight::route('/user/@id', [ $UserController, 'view' ]);
// other UserController routes...
Flight::start();
Aus dem obigen Code können Sie sehen, dass wir ein neues PDO
-Objekt erstellen und es an unsere UserController
-Klasse weitergeben. Das ist für eine kleine Anwendung in Ordnung, aber wenn Ihre Anwendung wächst, werden Sie feststellen, dass Sie dasselbe PDO
-Objekt an mehreren Stellen erstellen oder weitergeben. Hier kommt ein DIC ins Spiel.
Hier ist dasselbe Beispiel mit einem DIC (unter Verwendung von Dice):
require 'vendor/autoload.php';
// same class as above. Nothing changed
class UserController {
protected PDO $pdo;
public function __construct(PDO $pdo) {
$this->pdo = $pdo;
}
public function view(int $id) {
$stmt = $this->pdo->prepare('SELECT * FROM users WHERE id = :id');
$stmt->execute(['id' => $id]);
print_r($stmt->fetch());
}
}
// create a new container
$container = new \Dice\Dice;
// add a rule to tell the container how to create a PDO object
// don't forget to reassign it to itself like below!
$container = $container->addRule('PDO', [
// shared means that the same object will be returned each time
'shared' => true,
'constructParams' => ['mysql:host=localhost;dbname=test', 'user', 'pass' ]
]);
// This registers the container handler so Flight knows to use it.
Flight::registerContainerHandler(function($class, $params) use ($container) {
return $container->create($class, $params);
});
// now we can use the container to create our UserController
Flight::route('/user/@id', [ UserController::class, 'view' ]);
Flight::start();
Ich wette, Sie denken, dass eine Menge zusätzlicher Code zum Beispiel hinzugefügt wurde. Die Magie entsteht, wenn Sie einen anderen Controller haben, der das PDO
-Objekt benötigt.
// If all your controllers have a constructor that needs a PDO object
// each of the routes below will automatically have it injected!!!
Flight::route('/company/@id', [ CompanyController::class, 'view' ]);
Flight::route('/organization/@id', [ OrganizationController::class, 'view' ]);
Flight::route('/category/@id', [ CategoryController::class, 'view' ]);
Flight::route('/settings', [ SettingsController::class, 'view' ]);
Der zusätzliche Vorteil der Nutzung eines DIC ist, dass Unit-Testing viel einfacher wird. Sie können ein Mock-Objekt erstellen und es an Ihre Klasse weitergeben. Das ist ein großer Vorteil, wenn Sie Tests für Ihre Anwendung schreiben!
Creating a centralized DIC handler
Sie können einen zentralen DIC-Handler in Ihrer Services-Datei erstellen, indem Sie Ihre App erweitern. Hier ist ein Beispiel:
// services.php
// create a new container
$container = new \Dice\Dice;
// don't forget to reassign it to itself like below!
$container = $container->addRule('PDO', [
// shared means that the same object will be returned each time
'shared' => true,
'constructParams' => ['mysql:host=localhost;dbname=test', 'user', 'pass' ]
]);
// now we can create a mappable method to create any object.
Flight::map('make', function($class, $params = []) use ($container) {
return $container->create($class, $params);
});
// This registers the container handler so Flight knows to use it for controllers/middleware
Flight::registerContainerHandler(function($class, $params) {
Flight::make($class, $params);
});
// lets say we have the following sample class that takes a PDO object in the constructor
class EmailCron {
protected PDO $pdo;
public function __construct(PDO $pdo) {
$this->pdo = $pdo;
}
public function send() {
// code that sends an email
}
}
// And finally you can create objects using dependency injection
$emailCron = Flight::make(EmailCron::class);
$emailCron->send();
flightphp/container
Flight hat ein Plugin, das einen einfachen PSR-11-konformen Container bereitstellt, den Sie zur Handhabung Ihrer Dependency Injection verwenden können. Hier ist ein schnelles Beispiel, wie Sie es verwenden:
// index.php for example
require 'vendor/autoload.php';
use flight\Container;
$container = new Container;
$container->set(PDO::class, fn(): PDO => new PDO('sqlite::memory:'));
Flight::registerContainerHandler([$container, 'get']);
class TestController {
private PDO $pdo;
function __construct(PDO $pdo) {
$this->pdo = $pdo;
}
function index() {
var_dump($this->pdo);
// will output this correctly!
}
}
Flight::route('GET /', [TestController::class, 'index']);
Flight::start();
Advanced Usage of flightphp/container
Sie können auch Abhängigkeiten rekursiv auflösen. Hier ist ein Beispiel:
<?php
require 'vendor/autoload.php';
use flight\Container;
class User {}
interface UserRepository {
function find(int $id): ?User;
}
class PdoUserRepository implements UserRepository {
private PDO $pdo;
function __construct(PDO $pdo) {
$this->pdo = $pdo;
}
function find(int $id): ?User {
// Implementation ...
return null;
}
}
$container = new Container;
$container->set(PDO::class, static fn(): PDO => new PDO('sqlite::memory:'));
$container->set(UserRepository::class, PdoUserRepository::class);
$userRepository = $container->get(UserRepository::class);
var_dump($userRepository);
/*
object(PdoUserRepository)#4 (1) {
["pdo":"PdoUserRepository":private]=>
object(PDO)#3 (0) {
}
}
*/
DICE
Sie können auch Ihren eigenen DIC-Handler erstellen. Das ist nützlich, wenn Sie einen benutzerdefinierten Container verwenden möchten, der nicht PSR-11 ist (Dice). Siehe den Abschnitt basic usage für die Vorgehensweise.
Zusätzlich gibt es einige hilfreiche Standardeinstellungen, die Ihr Leben mit Flight erleichtern.
Engine Instance
Wenn Sie die Engine
-Instanz in Ihren Controllern/Middleware verwenden, hier ist, wie Sie sie konfigurieren würden:
// Somewhere in your bootstrap file
$engine = Flight::app();
$container = new \Dice\Dice;
$container = $container->addRule('*', [
'substitutions' => [
// This is where you pass in the instance
Engine::class => $engine
]
]);
$engine->registerContainerHandler(function($class, $params) use ($container) {
return $container->create($class, $params);
});
// Now you can use the Engine instance in your controllers/middleware
class MyController {
public function __construct(Engine $app) {
$this->app = $app;
}
public function index() {
$this->app->render('index');
}
}
Adding Other Classes
Wenn Sie andere Klassen zum Container hinzufügen möchten, ist das mit Dice einfach, da sie automatisch vom Container aufgelöst werden. Hier ist ein Beispiel:
$container = new \Dice\Dice;
// If you don't need to inject any dependencies into your classes
// you don't need to define anything!
Flight::registerContainerHandler(function($class, $params) use ($container) {
return $container->create($class, $params);
});
class MyCustomClass {
public function parseThing() {
return 'thing';
}
}
class UserController {
protected MyCustomClass $MyCustomClass;
public function __construct(MyCustomClass $MyCustomClass) {
$this->MyCustomClass = $MyCustomClass;
}
public function index() {
echo $this->MyCustomClass->parseThing();
}
}
Flight::route('/user', 'UserController->index');
PSR-11
Flight kann auch jeden PSR-11-konformen Container verwenden. Das bedeutet, dass Sie jeden Container verwenden können, der die PSR-11-Schnittstelle implementiert. Hier ist ein Beispiel mit Leagues PSR-11-Container:
require 'vendor/autoload.php';
// same UserController class as above
$container = new \League\Container\Container();
$container->add(UserController::class)->addArgument(PdoWrapper::class);
$container->add(PdoWrapper::class)
->addArgument('mysql:host=localhost;dbname=test')
->addArgument('user')
->addArgument('pass');
Flight::registerContainerHandler($container);
Flight::route('/user', [ 'UserController', 'view' ]);
Flight::start();
Das kann etwas ausführlicher sein als das vorherige Dice-Beispiel, es erledigt dennoch die Aufgabe mit denselben Vorteilen!
See Also
- Extending Flight - Lernen Sie, wie Sie Dependency Injection zu Ihren eigenen Klassen hinzufügen können, indem Sie das Framework erweitern.
- Configuration - Lernen Sie, wie Sie Flight für Ihre Anwendung konfigurieren.
- Routing - Lernen Sie, wie Sie Routen für Ihre Anwendung definieren und wie Dependency Injection mit Controllern funktioniert.
- Middleware - Lernen Sie, wie Sie Middleware für Ihre Anwendung erstellen und wie Dependency Injection mit Middleware funktioniert.
Troubleshooting
- Wenn Sie Probleme mit Ihrem Container haben, stellen Sie sicher, dass Sie die korrekten Klassennamen an den Container weitergeben.
Changelog
- v3.7.0 - Hinzugefügt: Möglichkeit, einen DIC-Handler zu Flight zu registrieren.
Learn/middleware
Middleware
Überblick
Flight unterstützt Route- und Gruppen-Route-Middleware. Middleware ist ein Teil Ihrer Anwendung, in dem Code ausgeführt wird, bevor (oder nach) dem Route-Callback. Dies ist eine großartige Möglichkeit, API-Authentifizierungsprüfungen in Ihrem Code hinzuzufügen oder zu überprüfen, ob der Benutzer die Berechtigung hat, auf die Route zuzugreifen.
Verständnis
Middleware kann Ihre App erheblich vereinfachen. Anstatt komplexer abstrakter Klassenvererbung oder Methoden-Überschreibungen ermöglicht Middleware Ihnen, Ihre Routen zu steuern, indem Sie Ihre benutzerdefinierte App-Logik zuweisen. Sie können Middleware wie ein Sandwich betrachten. Sie haben Brot außen und dann Schichten von Zutaten wie Salat, Tomaten, Fleisch und Käse. Stellen Sie sich vor, jede Anfrage ist wie ein Bissen des Sandwiches, bei dem Sie zuerst die äußeren Schichten essen und zum Kern vordringen.
Hier ist eine visuelle Darstellung, wie Middleware funktioniert. Dann zeigen wir Ihnen ein praktisches Beispiel, wie dies funktioniert.
Benutzeranfrage an URL /api ---->
Middleware->before() ausgeführt ----->
Callable/ Methode an /api ausgeführt und Antwort generiert ------>
Middleware->after() ausgeführt ----->
Benutzer erhält Antwort vom Server
Und hier ist ein praktisches Beispiel:
Benutzer navigiert zu URL /dashboard
LoggedInMiddleware->before() wird ausgeführt
before() prüft auf gültige angemeldete Sitzung
wenn ja, nichts tun und Ausführung fortsetzen
wenn nein, Benutzer zu /login umleiten
Callable/ Methode an /api ausgeführt und Antwort generiert
LoggedInMiddleware->after() hat nichts definiert, also lässt es die Ausführung fortfahren
Benutzer erhält Dashboard-HTML vom Server
Ausführungsreihenfolge
Middleware-Funktionen werden in der Reihenfolge ausgeführt, in der sie der Route hinzugefügt werden. Die Ausführung ähnelt der Art und Weise, wie Slim Framework dies handhabt.
before()
-Methoden werden in der Reihenfolge ausgeführt, in der sie hinzugefügt wurden, und after()
-Methoden werden in umgekehrter Reihenfolge ausgeführt.
Beispiel: Middleware1->before(), Middleware2->before(), Middleware2->after(), Middleware1->after().
Grundlegende Verwendung
Sie können Middleware als jede aufrufbare Methode verwenden, einschließlich einer anonymen Funktion oder einer Klasse (empfohlen).
Anonyme Funktion
Hier ist ein einfaches Beispiel:
Flight::route('/path', function() { echo ' Here I am!'; })->addMiddleware(function() {
echo 'Middleware first!';
});
Flight::start();
// Dies wird "Middleware first! Here I am!" ausgeben
Hinweis: Bei der Verwendung einer anonymen Funktion wird nur eine
before()
-Methode interpretiert. Sie können keinafter()
-Verhalten mit einer anonymen Klasse definieren.
Verwendung von Klassen
Middleware kann (und sollte) als Klasse registriert werden. Wenn Sie die "after"-Funktionalität benötigen, müssen Sie eine Klasse verwenden.
class MyMiddleware {
public function before($params) {
echo 'Middleware first!';
}
public function after($params) {
echo 'Middleware last!';
}
}
$MyMiddleware = new MyMiddleware();
Flight::route('/path', function() { echo ' Here I am! '; })->addMiddleware($MyMiddleware);
// auch ->addMiddleware([ $MyMiddleware, $MyMiddleware2 ]);
Flight::start();
// Dies wird "Middleware first! Here I am! Middleware last!" anzeigen
Sie können auch nur den Klassenname der Middleware definieren, und sie wird die Klasse instanziieren.
Flight::route('/path', function() { echo ' Here I am! '; })->addMiddleware(MyMiddleware::class);
Hinweis: Wenn Sie nur den Namen der Middleware übergeben, wird sie automatisch vom Dependency Injection Container ausgeführt, und die Middleware wird mit den Parametern ausgeführt, die sie benötigt. Wenn kein Dependency Injection Container registriert ist, wird standardmäßig die
flight\Engine
-Instanz in den__construct(Engine $app)
übergeben.
Verwendung von Routen mit Parametern
Wenn Sie Parameter aus Ihrer Route benötigen, werden sie in einem einzelnen Array an Ihre Middleware-Funktion übergeben. (function($params) { ... }
oder public function before($params) { ... }
). Der Grund dafür ist, dass Sie Ihre Parameter in Gruppen strukturieren können und in einigen dieser Gruppen Ihre Parameter möglicherweise in einer anderen Reihenfolge erscheinen, was die Middleware-Funktion durch Verweis auf den falschen Parameter kaputt machen würde. Auf diese Weise können Sie sie nach Namen anstelle der Position zugreifen.
use flight\Engine;
class RouteSecurityMiddleware {
protected Engine $app;
public function __construct(Engine $app) {
$this->app = $app;
}
public function before(array $params) {
$clientId = $params['clientId'];
// jobId kann übergeben oder nicht übergeben werden
$jobId = $params['jobId'] ?? 0;
// vielleicht wenn es keine Job-ID gibt, müssen Sie nichts nachschlagen.
if($jobId === 0) {
return;
}
// Führen Sie eine Suche in Ihrer Datenbank durch
$isValid = !!$this->app->db()->fetchField("SELECT 1 FROM client_jobs WHERE client_id = ? AND job_id = ?", [ $clientId, $jobId ]);
if($isValid !== true) {
$this->app->halt(400, 'You are blocked, muahahaha!');
}
}
}
// routes.php
$router->group('/client/@clientId/job/@jobId', function(Router $router) {
// Diese Gruppe unten erhält immer noch die Parent-Middleware
// Aber die Parameter werden in einem einzelnen Array
// in der Middleware übergeben.
$router->group('/job/@jobId', function(Router $router) {
$router->get('', [ JobController::class, 'view' ]);
$router->put('', [ JobController::class, 'update' ]);
$router->delete('', [ JobController::class, 'delete' ]);
// mehr Routen...
});
}, [ RouteSecurityMiddleware::class ]);
Gruppierung von Routen mit Middleware
Sie können eine Route-Gruppe hinzufügen, und dann wird jede Route in dieser Gruppe dieselbe Middleware haben. Dies ist nützlich, wenn Sie eine Menge von Routen gruppieren müssen, z. B. mit einer Auth-Middleware, um den API-Schlüssel im Header zu prüfen.
// am Ende der group-Methode hinzugefügt
Flight::group('/api', function() {
// Diese "leere" Route passt tatsächlich zu /api
Flight::route('', function() { echo 'api'; }, false, 'api');
// Dies passt zu /api/users
Flight::route('/users', function() { echo 'users'; }, false, 'users');
// Dies passt zu /api/users/1234
Flight::route('/users/@id', function($id) { echo 'user:'.$id; }, false, 'user_view');
}, [ new ApiAuthMiddleware() ]);
Wenn Sie eine globale Middleware auf alle Ihre Routen anwenden möchten, können Sie eine "leere" Gruppe hinzufügen:
// am Ende der group-Methode hinzugefügt
Flight::group('', function() {
// Dies ist immer noch /users
Flight::route('/users', function() { echo 'users'; }, false, 'users');
// Und dies ist immer noch /users/1234
Flight::route('/users/@id', function($id) { echo 'user:'.$id; }, false, 'user_view');
}, [ ApiAuthMiddleware::class ]); // oder [ new ApiAuthMiddleware() ], dasselbe
Häufige Anwendungsfälle
API-Schlüssel-Validierung
Wenn Sie Ihre /api
-Routen schützen möchten, indem Sie überprüfen, ob der API-Schlüssel korrekt ist, können Sie das leicht mit Middleware handhaben.
use flight\Engine;
class ApiMiddleware {
protected Engine $app;
public function __construct(Engine $app) {
$this->app = $app;
}
public function before(array $params) {
$authorizationHeader = $this->app->request()->getHeader('Authorization');
$apiKey = str_replace('Bearer ', '', $authorizationHeader);
// Führen Sie eine Suche in Ihrer Datenbank für den API-Schlüssel durch
$apiKeyHash = hash('sha256', $apiKey);
$hasValidApiKey = !!$this->db()->fetchField("SELECT 1 FROM api_keys WHERE hash = ? AND valid_date >= NOW()", [ $apiKeyHash ]);
if($hasValidApiKey !== true) {
$this->app->jsonHalt(['error' => 'Invalid API Key']);
}
}
}
// routes.php
$router->group('/api', function(Router $router) {
$router->get('/users', [ ApiController::class, 'getUsers' ]);
$router->get('/companies', [ ApiController::class, 'getCompanies' ]);
// mehr Routen...
}, [ ApiMiddleware::class ]);
Jetzt sind alle Ihre API-Routen durch diese API-Schlüssel-Validierungs-Middleware geschützt, die Sie eingerichtet haben! Wenn Sie mehr Routen in die Router-Gruppe einfügen, erhalten sie sofort denselben Schutz!
Anmeldungs-Validierung
Möchten Sie einige Routen schützen, damit sie nur für angemeldete Benutzer verfügbar sind? Das kann leicht mit Middleware erreicht werden!
use flight\Engine;
class LoggedInMiddleware {
protected Engine $app;
public function __construct(Engine $app) {
$this->app = $app;
}
public function before(array $params) {
$session = $this->app->session();
if($session->get('logged_in') !== true) {
$this->app->redirect('/login');
exit;
}
}
}
// routes.php
$router->group('/admin', function(Router $router) {
$router->get('/dashboard', [ DashboardController::class, 'index' ]);
$router->get('/clients', [ ClientController::class, 'index' ]);
// mehr Routen...
}, [ LoggedInMiddleware::class ]);
Route-Parameter-Validierung
Möchten Sie Ihre Benutzer schützen, indem Sie verhindern, dass sie Werte in der URL ändern, um auf Daten zuzugreifen, die sie nicht sollten? Das kann mit Middleware gelöst werden!
use flight\Engine;
class RouteSecurityMiddleware {
protected Engine $app;
public function __construct(Engine $app) {
$this->app = $app;
}
public function before(array $params) {
$clientId = $params['clientId'];
$jobId = $params['jobId'];
// Führen Sie eine Suche in Ihrer Datenbank durch
$isValid = !!$this->app->db()->fetchField("SELECT 1 FROM client_jobs WHERE client_id = ? AND job_id = ?", [ $clientId, $jobId ]);
if($isValid !== true) {
$this->app->halt(400, 'You are blocked, muahahaha!');
}
}
}
// routes.php
$router->group('/client/@clientId/job/@jobId', function(Router $router) {
$router->get('', [ JobController::class, 'view' ]);
$router->put('', [ JobController::class, 'update' ]);
$router->delete('', [ JobController::class, 'delete' ]);
// mehr Routen...
}, [ RouteSecurityMiddleware::class ]);
Handhabung der Middleware-Ausführung
Sagen wir, Sie haben eine Auth-Middleware und möchten den Benutzer auf eine Login-Seite umleiten, wenn er nicht authentifiziert ist. Sie haben ein paar Optionen zur Verfügung:
- Sie können false von der Middleware-Funktion zurückgeben, und Flight gibt automatisch einen 403 Forbidden-Fehler zurück, aber ohne Anpassung.
- Sie können den Benutzer auf eine Login-Seite umleiten mit
Flight::redirect()
. - Sie können einen benutzerdefinierten Fehler in der Middleware erstellen und die Ausführung der Route stoppen.
Einfach und Unkompliziert
Hier ist ein einfaches return false;
-Beispiel:
class MyMiddleware {
public function before($params) {
$hasUserKey = Flight::session()->exists('user');
if ($hasUserKey === false) {
return false;
}
// da es wahr ist, läuft alles einfach weiter
}
}
Umleitungs-Beispiel
Hier ist ein Beispiel für die Umleitung des Benutzers auf eine Login-Seite:
class MyMiddleware {
public function before($params) {
$hasUserKey = Flight::session()->exists('user');
if ($hasUserKey === false) {
Flight::redirect('/login');
exit;
}
}
}
Benutzerdefiniertes Fehler-Beispiel
Sagen wir, Sie müssen einen JSON-Fehler werfen, weil Sie eine API bauen. Sie können das so tun:
class MyMiddleware {
public function before($params) {
$authorization = Flight::request()->getHeader('Authorization');
if(empty($authorization)) {
Flight::jsonHalt(['error' => 'You must be logged in to access this page.'], 403);
// oder
Flight::json(['error' => 'You must be logged in to access this page.'], 403);
exit;
// oder
Flight::halt(403, json_encode(['error' => 'You must be logged in to access this page.']));
}
}
}
Siehe auch
- Routing - Wie man Routen zu Controllern zuweist und Views rendert.
- Requests - Verständnis, wie man eingehende Anfragen handhabt.
- Responses - Wie man HTTP-Antworten anpasst.
- Dependency Injection - Vereinfachung der Objekterstellung und -verwaltung in Routen.
- Warum ein Framework? - Verständnis der Vorteile der Verwendung eines Frameworks wie Flight.
- Middleware-Ausführungsstrategie-Beispiel
Fehlerbehebung
- Wenn Sie eine Umleitung in Ihrer Middleware haben, aber Ihre App scheint nicht umzuleiten, stellen Sie sicher, dass Sie eine
exit;
-Anweisung in Ihrer Middleware hinzufügen.
Änderungsprotokoll
- v3.1: Unterstützung für Middleware hinzugefügt.
Learn/filtering
Filtering
Überblick
Flight ermöglicht es Ihnen, gemappte Methoden vor und nach ihrem Aufruf zu filtern.
Verständnis
Es gibt keine vordefinierten Hooks, die Sie merken müssen. Sie können alle Standard-Framework-Methoden sowie alle benutzerdefinierten Methoden filtern, die Sie gemappt haben.
Eine Filterfunktion sieht so aus:
/**
* @param array $params Die an die gefilterte Methode übergebenen Parameter.
* @param string $output (nur v2 Output Buffering) Die Ausgabe der gefilterten Methode.
* @return bool Geben Sie true/void zurück oder geben Sie nichts zurück, um die Kette fortzusetzen, false, um die Kette zu unterbrechen.
*/
function (array &$params, string &$output): bool {
// Filtercode
}
Mit den übergebenen Variablen können Sie die Eingabeparameter und/oder die Ausgabe manipulieren.
Sie können einen Filter vor einer Methode ausführen, indem Sie Folgendes tun:
Flight::before('start', function (array &$params, string &$output): bool {
// Etwas tun
});
Sie können einen Filter nach einer Methode ausführen, indem Sie Folgendes tun:
Flight::after('start', function (array &$params, string &$output): bool {
// Etwas tun
});
Sie können so viele Filter wie gewünscht zu jeder Methode hinzufügen. Sie werden in der Reihenfolge aufgerufen, in der sie deklariert wurden.
Hier ist ein Beispiel für den Filterprozess:
// Eine benutzerdefinierte Methode mappen
Flight::map('hello', function (string $name) {
return "Hello, $name!";
});
// Einen Before-Filter hinzufügen
Flight::before('hello', function (array &$params, string &$output): bool {
// Den Parameter manipulieren
$params[0] = 'Fred';
return true;
});
// Einen After-Filter hinzufügen
Flight::after('hello', function (array &$params, string &$output): bool {
// Die Ausgabe manipulieren
$output .= " Have a nice day!";
return true;
});
// Die benutzerdefinierte Methode aufrufen
echo Flight::hello('Bob');
Dies sollte anzeigen:
Hello Fred! Have a nice day!
Wenn Sie mehrere Filter definiert haben, können Sie die Kette unterbrechen, indem Sie false
in einer Ihrer Filterfunktionen zurückgeben:
Flight::before('start', function (array &$params, string &$output): bool {
echo 'one';
return true;
});
Flight::before('start', function (array &$params, string &$output): bool {
echo 'two';
// Dies beendet die Kette
return false;
});
// Dies wird nicht aufgerufen
Flight::before('start', function (array &$params, string &$output): bool {
echo 'three';
return true;
});
Hinweis: Kernmethoden wie
map
undregister
können nicht gefiltert werden, da sie direkt aufgerufen und nicht dynamisch aufgerufen werden. Siehe Erweiterung von Flight für weitere Informationen.
Siehe auch
Fehlerbehebung
- Stellen Sie sicher, dass Sie
false
aus Ihren Filterfunktionen zurückgeben, wenn Sie möchten, dass die Kette stoppt. Wenn Sie nichts zurückgeben, wird die Kette fortgesetzt.
Changelog
- v2.0 - Erste Veröffentlichung.
Learn/requests
Requests
Übersicht
Flight kapselt die HTTP-Anfrage in ein einzelnes Objekt, das wie folgt zugänglich ist:
$request = Flight::request();
Verständnis
HTTP-Anfragen sind eines der Kernaspekte, die man über den HTTP-Lebenszyklus verstehen muss. Ein Benutzer führt eine Aktion in einem Webbrowser oder einem HTTP-Client aus, und sie senden eine Reihe von Headern, Body, URL usw. an Ihr Projekt. Sie können diese Header (die Sprache des Browsers, welche Art von Kompression sie handhaben können, den User-Agent usw.) erfassen und den Body sowie die URL, die an Ihre Flight-Anwendung gesendet wird, erfassen. Diese Anfragen sind essenziell, damit Ihre App versteht, was als Nächstes zu tun ist.
Grundlegende Verwendung
PHP hat mehrere Super-Globalen, einschließlich $_GET
, $_POST
, $_REQUEST
, $_SERVER
, $_FILES
und $_COOKIE
. Flight abstrahiert diese in handliche Collections. Sie können die Eigenschaften query
, data
, cookies
und files
als Arrays oder Objekte zugreifen.
Hinweis: Es wird STRONGLICH davon abgeraten, diese Super-Globalen in Ihrem Projekt zu verwenden, und sie sollten über das
request()
-Objekt referenziert werden.Hinweis: Es gibt keine Abstraktion für
$_ENV
verfügbar.
$_GET
Sie können das $_GET
-Array über die Eigenschaft query
zugreifen:
// GET /search?keyword=something
Flight::route('/search', function(){
$keyword = Flight::request()->query['keyword'];
// oder
$keyword = Flight::request()->query->keyword;
echo "You are searching for: $keyword";
// query a database or something else with the $keyword
});
$_POST
Sie können das $_POST
-Array über die Eigenschaft data
zugreifen:
Flight::route('POST /submit', function(){
$name = Flight::request()->data['name'];
$email = Flight::request()->data['email'];
// oder
$name = Flight::request()->data->name;
$email = Flight::request()->data->email;
echo "You submitted: $name, $email";
// save to a database or something else with the $name and $email
});
$_COOKIE
Sie können das $_COOKIE
-Array über die Eigenschaft cookies
zugreifen:
Flight::route('GET /login', function(){
$savedLogin = Flight::request()->cookies['myLoginCookie'];
// oder
$savedLogin = Flight::request()->cookies->myLoginCookie;
// check if it's really saved or not and if it is auto log them in
if($savedLogin) {
Flight::redirect('/dashboard');
return;
}
});
Für Hilfe beim Setzen neuer Cookie-Werte siehe overclokk/cookie
$_SERVER
Es gibt einen Shortcut, um das $_SERVER
-Array über die Methode getVar()
zugreifen:
$host = Flight::request()->getVar('HTTP_HOST');
$_FILES
Sie können hochgeladene Dateien über die Eigenschaft files
zugreifen:
// raw access to $_FILES property. See below for recommended approach
$uploadedFile = Flight::request()->files['myFile'];
// oder
$uploadedFile = Flight::request()->files->myFile;
Siehe Uploaded File Handler für mehr Infos.
Verarbeiten von Datei-Uploads
v3.12.0
Sie können Datei-Uploads mit dem Framework mithilfe einiger Hilfsmethoden verarbeiten. Es kommt im Wesentlichen darauf an, die Dateidaten aus der Anfrage zu ziehen und sie an einen neuen Ort zu verschieben.
Flight::route('POST /upload', function(){
// If you had an input field like <input type="file" name="myFile">
$uploadedFileData = Flight::request()->getUploadedFiles();
$uploadedFile = $uploadedFileData['myFile'];
$uploadedFile->moveTo('/path/to/uploads/' . $uploadedFile->getClientFilename());
});
Wenn Sie mehrere Dateien hochgeladen haben, können Sie durch sie iterieren:
Flight::route('POST /upload', function(){
// If you had an input field like <input type="file" name="myFiles[]">
$uploadedFiles = Flight::request()->getUploadedFiles()['myFiles'];
foreach ($uploadedFiles as $uploadedFile) {
$uploadedFile->moveTo('/path/to/uploads/' . $uploadedFile->getClientFilename());
}
});
Sicherheitshinweis: Validieren und sanitieren Sie immer Benutzereingaben, insbesondere bei Datei-Uploads. Validieren Sie immer den Typ der Erweiterungen, die Sie zum Hochladen erlauben, aber Sie sollten auch die "Magic Bytes" der Datei validieren, um sicherzustellen, dass es tatsächlich der Typ der Datei ist, den der Benutzer angibt. Es gibt Artikel und Bibliotheken, die dabei helfen.
Request Body
Um den rohen HTTP-Request-Body zu erhalten, z. B. bei POST/PUT-Anfragen, können Sie Folgendes tun:
Flight::route('POST /users/xml', function(){
$xmlBody = Flight::request()->getBody();
// do something with the XML that was sent.
});
JSON Body
Wenn Sie eine Anfrage mit dem Content-Type application/json
und den Beispieldaten {"id": 123}
erhalten, ist sie über die Eigenschaft data
verfügbar:
$id = Flight::request()->data->id;
Request Headers
Sie können Request-Header mit der Methode getHeader()
oder getHeaders()
zugreifen:
// Maybe you need Authorization header
$host = Flight::request()->getHeader('Authorization');
// oder
$host = Flight::request()->header('Authorization');
// If you need to grab all headers
$headers = Flight::request()->getHeaders();
// oder
$headers = Flight::request()->headers();
Request Method
Sie können die Request-Methode über die Eigenschaft method
oder die Methode getMethod()
zugreifen:
$method = Flight::request()->method; // actually populated by getMethod()
$method = Flight::request()->getMethod();
Hinweis: Die Methode getMethod()
zieht zunächst die Methode aus $_SERVER['REQUEST_METHOD']
, dann kann sie durch $_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE']
überschrieben werden, falls vorhanden, oder $_REQUEST['_method']
, falls vorhanden.
Eigenschaften des Request-Objekts
Das Request-Objekt stellt die folgenden Eigenschaften bereit:
- body - Der rohe HTTP-Request-Body
- url - Die angeforderte URL
- base - Das übergeordnete Unterverzeichnis der URL
- method - Die Request-Methode (GET, POST, PUT, DELETE)
- referrer - Die Referrer-URL
- ip - IP-Adresse des Clients
- ajax - Ob es sich um eine AJAX-Anfrage handelt
- scheme - Das Server-Protokoll (http, https)
- user_agent - Browser-Informationen
- type - Der Content-Type
- length - Die Content-Länge
- query - Query-String-Parameter
- data - Post-Daten oder JSON-Daten
- cookies - Cookie-Daten
- files - Hochgeladene Dateien
- secure - Ob die Verbindung sicher ist
- accept - HTTP-Accept-Parameter
- proxy_ip - Proxy-IP-Adresse des Clients. Scannt das
$_SERVER
-Array nachHTTP_CLIENT_IP
,HTTP_X_FORWARDED_FOR
,HTTP_X_FORWARDED
,HTTP_X_CLUSTER_CLIENT_IP
,HTTP_FORWARDED_FOR
,HTTP_FORWARDED
in dieser Reihenfolge. - host - Der Request-Hostname
- servername - Der SERVER_NAME aus
$_SERVER
Hilfsmethoden
Es gibt ein paar Hilfsmethoden, um Teile einer URL zusammenzusetzen oder mit bestimmten Headern umzugehen.
Volle URL
Sie können die volle Request-URL mit der Methode getFullUrl()
zugreifen:
$url = Flight::request()->getFullUrl();
// https://example.com/some/path?foo=bar
Basis-URL
Sie können die Basis-URL mit der Methode getBaseUrl()
zugreifen:
// http://example.com/path/to/something/cool?query=yes+thanks
$url = Flight::request()->getBaseUrl();
// https://example.com
// Notice, no trailing slash.
Query-Parsing
Sie können eine URL an die Methode parseQuery()
übergeben, um den Query-String in ein assoziatives Array zu parsen:
$query = Flight::request()->parseQuery('https://example.com/some/path?foo=bar');
// ['foo' => 'bar']
Verhandeln von Content-Accept-Types
v3.17.2
Sie können die Methode negotiateContentType()
verwenden, um den besten Content-Type für die Antwort basierend auf dem vom Client gesendeten Accept
-Header zu bestimmen.
// Example Accept header: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
// The below defines what you support.
$availableTypes = ['application/json', 'application/xml'];
$typeToServe = Flight::request()->negotiateContentType($availableTypes);
if ($typeToServe === 'application/json') {
// Serve JSON response
} elseif ($typeToServe === 'application/xml') {
// Serve XML response
} else {
// Default to something else or throw an error
}
Hinweis: Wenn keiner der verfügbaren Typen im
Accept
-Header gefunden wird, gibt die Methodenull
zurück. Wenn keinAccept
-Header definiert ist, gibt die Methode den ersten Typ im$availableTypes
-Array zurück.
Siehe auch
- Routing - So ordnen Sie Routen Controllern zu und rendern Views.
- Responses - So passen Sie HTTP-Antworten an.
- Warum ein Framework? - Wie Anfragen in das große Ganze passen.
- Collections - Arbeiten mit Datensammlungen.
- Uploaded File Handler - Behandeln von Datei-Uploads.
Fehlerbehebung
request()->ip
undrequest()->proxy_ip
können unterschiedlich sein, wenn Ihr Webserver hinter einem Proxy, Load Balancer usw. steht.
Changelog
- v3.17.2 - Added negotiateContentType()
- v3.12.0 - Added ability to handle file uploads through the request object.
- v1.0 - Initial release.
Learn/why_frameworks
Warum ein Framework?
Einige Programmierer sind vehement gegen die Verwendung von Frameworks. Sie argumentieren, dass Frameworks aufgebläht, langsam und schwer zu erlernen sind. Sie sagen, dass Frameworks unnötig sind und dass man besseren Code ohne sie schreiben kann. Es gibt sicherlich einige überzeugende Argumente gegen die Verwendung von Frameworks vorzubringen. Allerdings gibt es auch viele Vorteile bei der Verwendung von Frameworks.
Gründe für die Verwendung eines Frameworks
Hier sind ein paar Gründe, warum Sie in Betracht ziehen sollten, ein Framework zu verwenden:
- Schnelle Entwicklung: Frameworks bieten von Haus aus viel Funktionalität. Das bedeutet, dass Sie Webanwendungen schneller erstellen können. Sie müssen nicht so viel Code schreiben, da das Framework einen Großteil der Funktionalität bereitstellt, die Sie benötigen.
- Konsistenz: Frameworks bieten eine konsistente Möglichkeit der Durchführung von Aufgaben. Dies erleichtert es Ihnen zu verstehen, wie der Code funktioniert, und anderen Entwicklern, Ihren Code zu verstehen. Wenn Sie es Script für Script haben, könnte die Konsistenz zwischen den Skripts verloren gehen, besonders wenn Sie mit einem Team von Entwicklern arbeiten.
- Sicherheit: Frameworks bieten Sicherheitsfunktionen, die Ihre Webanwendungen vor gängigen Sicherheitsbedrohungen schützen. Das bedeutet, dass Sie sich nicht so sehr um die Sicherheit kümmern müssen, da das Framework einen Großteil davon für Sie erledigt.
- Gemeinschaft: Frameworks haben große Entwickler-Communities, die zum Framework beitragen. Das bedeutet, dass Sie Hilfe von anderen Entwicklern erhalten können, wenn Sie Fragen oder Probleme haben. Es bedeutet auch, dass es viele Ressourcen gibt, die Ihnen helfen können, zu lernen, wie das Framework verwendet wird.
- Beste Praktiken: Frameworks werden unter Verwendung von besten Praktiken erstellt. Das bedeutet, dass Sie vom Framework lernen und die gleichen besten Praktiken in Ihrem eigenen Code verwenden können. Dies kann Ihnen helfen, ein besserer Programmierer zu werden. Manchmal weiß man nicht, was man nicht weiß, und das kann einem am Ende schaden.
- Erweiterbarkeit: Frameworks sind darauf ausgelegt, erweitert zu werden. Das bedeutet, dass Sie Ihre eigene Funktionalität zum Framework hinzufügen können. Dies ermöglicht es Ihnen, Webanwendungen zu erstellen, die auf Ihre spezifischen Bedürfnisse zugeschnitten sind.
Flight ist ein Mikro-Framework. Das bedeutet, dass es klein und leichtgewichtig ist. Es bietet nicht so viele Funktionen wie größere Frameworks wie Laravel oder Symfony. Allerdings bietet es viele der Funktionen, die Sie benötigen, um Webanwendungen zu erstellen. Es ist auch einfach zu erlernen und zu verwenden. Das macht es zu einer guten Wahl, um Webanwendungen schnell und einfach zu erstellen. Wenn Sie neu in der Welt der Frameworks sind, ist Flight ein großartiges Anfänger-Framework, mit dem Sie beginnen können. Es hilft Ihnen, die Vorteile der Verwendung von Frameworks kennenzulernen, ohne Sie mit zu viel Komplexität zu überfordern. Nachdem Sie etwas Erfahrung mit Flight gesammelt haben, wird es einfacher sein, auf komplexere Frameworks wie Laravel oder Symfony umzusteigen, Flight kann jedoch immer noch eine erfolgreiche robuste Anwendung ermöglichen.
Was ist Routing?
Routing ist der Kern des Flight Frameworks, aber was genau ist das? Routing ist der Prozess, bei dem eine URL genommen und mit einer bestimmten Funktion in Ihrem Code abgeglichen wird.
So können Sie Ihre Website basierend auf der angeforderten URL unterschiedliche Dinge tun lassen. Zum Beispiel möchten Sie möglicherweise das Profil eines Benutzers anzeigen, wenn er
/user/1234
besucht, aber eine Liste aller Benutzer anzeigen, wenn er /users
besucht. All dies geschieht durch Routing.
Es könnte etwa so funktionieren:
- Ein Benutzer geht in Ihren Browser und gibt
http://beispiel.com/user/1234
ein. - Der Server empfängt die Anfrage, betrachtet die URL und leitet sie an Ihren Flight-Anwendungscode weiter.
- Angenommen, in Ihrem Flight-Code haben Sie so etwas wie
Flight::route('/user/@id', [ 'BenutzerController', 'BenutzerprofilAnzeigen' ]);
. Ihr Flight-Anwendungscode betrachtet die URL und erkennt, dass sie mit einem von Ihnen definierten Routen übereinstimmt, und führt dann den für diese Route definierten Code aus. - Der Flight-Router wird dann den
BenutzerprofilAnzeigen($id)
-Methode in derBenutzerController
-Klasse aufrufen und die1234
als$id
-Argument an die Methode übergeben. - Der Code in Ihrer
BenutzerprofilAnzeigen()
-Methode wird dann ausgeführt und das tun, was Sie ihm gesagt haben. Sie können z. B. HTML für die Profilseite des Benutzers ausgeben oder wenn es sich um eine RESTful API handelt, können Sie eine JSON-Antwort mit den Benutzerinformationen ausgeben. - Flight fasst das Ganze in eine hübsche Schleife, generiert die Antwortheader und sendet sie an den Browser des Benutzers zurück.
- Der Benutzer ist erfüllt und verteilt sich selbst eine herzliche Umarmung!
Und warum ist das wichtig?
Eine ordnungsgemäß zentralisierte Routerung kann tatsächlich Ihr Leben dramatisch vereinfachen! Es kann nur anfangs schwer zu erkennen sein. Hier sind ein paar Gründe:
- Zentralisierte Routerung: Sie können alle Ihre Routen an einem Ort aufbewahren. Dies erleichtert es Ihnen zu sehen, welche Routen Sie haben und was sie tun. Es erleichtert auch das Ändern, wenn Sie müssen.
- Routenparameter: Sie können Routenparameter verwenden, um Daten an Ihre Routenmethoden zu übergeben. Dies ist eine großartige Möglichkeit, Ihren Code sauber und organisiert zu halten.
- Routengruppen: Sie können Routen zusammenfassen. Dies ist großartig, um Ihren Code organisiert zu halten und Middleware auf eine Gruppe von Routen anzuwenden.
- Routenaliasing: Sie können einer Route einen Alias zuweisen, damit die URL später in Ihrem Code dynamisch generiert werden kann (z. B. wie eine Vorlage). Zum Beispiel, anstatt
/user/1234
fest im Code zu codieren, könnten Sie stattdessen auf den Aliasuser_view
verweisen und dieid
als Parameter übergeben. Das ist großartig, falls Sie sich später dazu entscheiden, sie auf/admin/user/1234
zu ändern. Sie müssen nicht alle fest codierten URLs ändern, sondern nur die URL, die der Route zugeordnet ist. - Routen-Middleware: Sie können Middleware zu Ihren Routen hinzufügen. Middleware ist unglaublich stark darin, spezifische Verhaltensweisen zu Ihrer Anwendung hinzuzufügen, wie z. B. die Authentifizierung, dass ein bestimmter Benutzer auf eine Route oder eine Gruppe von Routen zugreifen kann.
Sie sind sicherlich vertraut mit dem Script für Script-Weg, eine Website zu erstellen. Sie könnten eine Datei namens index.php
haben, die eine Reihe von if
-Anweisungen enthält, um die URL zu überprüfen und dann eine bestimmte Funktion auf der Grundlage der URL auszuführen. Dies ist eine Form der Routenführung, aber sie ist nicht sehr organisiert und kann schnell außer Kontrolle geraten. Flights Routensystem ist eine viel organisiertere und leistungsfähigere Art, die Routenführung zu handhaben.
Dies hier?
// /user/view_profile.php?id=1234
if ($_GET['id']) {
$id = $_GET['id'];
viewUserProfile($id);
}
// /user/edit_profile.php?id=1234
if ($_GET['id']) {
$id = $_GET['id'];
editUserProfile($id);
}
// etc...
Oder das hier?
// index.php
Flight::route('/user/@id', [ 'BenutzerController', 'BenutzerprofilAnzeigen' ]);
Flight::route('/user/@id/edit', [ 'BenutzerController', 'BenutzerprofilBearbeiten' ]);
// Vielleicht in Ihrem app/controllers/UserController.php
class UserController {
public function viewUserProfile($id) {
// Mach etwas
}
public function editUserProfile($id) {
// Mach etwas
}
}
Hoffentlich erkennen Sie langsam die Vorteile der Verwendung eines zentralisierten Routingsystems. Es ist viel einfacher zu verwalten und zu verstehen auf lange Sicht!
Anfragen und Antworten
Flight bietet eine einfache und unkomplizierte Möglichkeit, Anfragen und Antworten zu bearbeiten. Dies ist der Kern dessen, was ein Web-Framework macht. Es nimmt eine Anfrage von einem Benutzerbrowser entgegen, verarbeitet sie und sendet dann eine Antwort zurück. So können Sie Webanwendungen erstellen, die Dinge wie das Anzeigen eines Benutzerprofils, das Einloggen eines Benutzers oder das Posten eines neuen Blogposts ermöglichen.
Anfragen
Eine Anfrage ist das, was der Browser eines Benutzers an Ihren Server sendet, wenn er Ihre Website besucht. Diese Anfrage enthält Informationen darüber, was der Benutzer tun möchte. Zum Beispiel könnte sie Informationen darüber enthalten, welche URL der Benutzer besuchen möchte, welche Daten der Benutzer an Ihren Server senden möchte oder welche Art von Daten der Benutzer von Ihrem Server erhalten möchte. Es ist wichtig zu wissen, dass eine Anfrage schreibgeschützt ist. Sie können die Anfrage nicht ändern, aber Sie können daraus lesen.
Flight bietet eine einfache Möglichkeit, Informationen zur Anfrage abzurufen. Sie können über die Methode Flight::request()
Informationen zur Anfrage abrufen. Diese Methode gibt ein Request
-Objekt zurück, das Informationen zur Anfrage enthält. Mit diesem Objekt können Sie Informationen zur Anfrage abrufen, wie die URL, die Methode oder die Daten, die der Benutzer an Ihren Server gesendet hat.
Antworten
Eine Antwort ist das, was Ihr Server an den Browser eines Benutzers zurücksendet, wenn er Ihre Website besucht. Diese Antwort enthält Informationen darüber, was Ihr Server tun möchte. Zum Beispiel könnte es Informationen darüber enthalten, welche Art von Daten Ihr Server an den Benutzer senden möchte, welche Art von Daten Ihr Server von dem Benutzer erhalten möchte oder welche Art von Daten Ihr Server auf dem Computer des Benutzers speichern möchte.
Flight bietet eine einfache Möglichkeit, eine Antwort an den Browser eines Benutzers zu senden. Sie können eine Antwort mit der Methode Flight::response()
senden. Diese Methode nimmt ein Response
-Objekt als Argument und sendet die Antwort an den Browser des Benutzers.
Sie können dieses Objekt verwenden, um eine Antwort an den Browser des Benutzers zu senden, wie z. B. HTML, JSON oder eine Datei. Flight hilft Ihnen dabei, einige Teile der Antwort automatisch zu generieren, um die Dinge zu vereinfachen, aber letztendlich haben Sie die Kontrolle darüber, was Sie dem Benutzer zurücksenden.
Learn/responses
Responses
Überblick
Flight hilft dabei, Teile der Response-Header für Sie zu generieren, aber Sie haben die meiste Kontrolle darüber, was Sie an den Benutzer zurücksenden. Meistens greifen Sie direkt auf das response()
-Objekt zu, aber Flight bietet einige Hilfsmethoden, um einige der Response-Header für Sie zu setzen.
Verständnis
Nachdem der Benutzer seine request-Anfrage an Ihre Anwendung gesendet hat, müssen Sie eine angemessene Response für sie generieren. Sie haben Ihnen Informationen wie die bevorzugte Sprache, ob sie bestimmte Kompressionstypen handhaben können, ihren User Agent usw. gesendet, und nach der Verarbeitung von allem ist es Zeit, ihnen eine angemessene Response zurückzusenden. Dies kann das Setzen von Headern, das Ausgeben eines HTML- oder JSON-Bodys für sie oder das Weiterleiten zu einer Seite sein.
Grundlegende Verwendung
Senden eines Response-Bodys
Flight verwendet ob_start()
, um die Ausgabe zu puffern. Das bedeutet, Sie können echo
oder print
verwenden, um eine Response an den Benutzer zu senden, und Flight wird sie erfassen und mit den entsprechenden Headern an den Benutzer zurücksenden.
// Dies sendet "Hello, World!" an den Browser des Benutzers
Flight::route('/', function() {
echo "Hello, World!";
});
// HTTP/1.1 200 OK
// Content-Type: text/html
//
// Hello, World!
Als Alternative können Sie die write()
-Methode aufrufen, um zum Body hinzuzufügen.
// Dies sendet "Hello, World!" an den Browser des Benutzers
Flight::route('/', function() {
// ausführlich, aber erledigt den Job manchmal, wenn Sie es brauchen
Flight::response()->write("Hello, World!");
// wenn Sie den Body abrufen möchten, den Sie zu diesem Zeitpunkt gesetzt haben
// können Sie das so tun
$body = Flight::response()->getBody();
});
JSON
Flight bietet Unterstützung für das Senden von JSON- und JSONP-Responses. Um eine JSON-Response zu senden, geben Sie einige Daten weiter, die JSON-kodiert werden sollen:
Flight::route('/@companyId/users', function(int $companyId) {
// holen Sie irgendwie Ihre Benutzer aus einer Datenbank, z.B.
$users = Flight::db()->fetchAll("SELECT id, first_name, last_name FROM users WHERE company_id = ?", [ $companyId ]);
Flight::json($users);
});
// [{"id":1,"first_name":"Bob","last_name":"Jones"}, /* mehr Benutzer */ ]
Hinweis: Standardmäßig sendet Flight einen
Content-Type: application/json
-Header mit der Response. Es verwendet auch die FlagsJSON_THROW_ON_ERROR
undJSON_UNESCAPED_SLASHES
beim Kodieren des JSON.
JSON mit Statuscode
Sie können auch einen Statuscode als zweiten Argument übergeben:
Flight::json(['id' => 123], 201);
JSON mit Pretty Print
Sie können auch ein Argument an die letzte Position übergeben, um Pretty Printing zu aktivieren:
Flight::json(['id' => 123], 200, true, 'utf-8', JSON_PRETTY_PRINT);
Ändern der JSON-Argument-Reihenfolge
Flight::json()
ist eine sehr veraltete Methode, aber das Ziel von Flight ist es, die Abwärtskompatibilität für Projekte aufrechtzuerhalten. Es ist eigentlich sehr einfach, wenn Sie die Reihenfolge der Argumente neu gestalten möchten, um eine einfachere Syntax zu verwenden, können Sie die JSON-Methode einfach neu zuordnen wie jede andere Flight-Methode:
Flight::map('json', function($data, $code = 200, $options = 0) {
// jetzt müssen Sie nicht mehr `true, 'utf-8'` verwenden, wenn Sie die json()-Methode nutzen!
Flight::_json($data, $code, true, 'utf-8', $options);
}
// Und jetzt kann sie so verwendet werden
Flight::json(['id' => 123], 200, JSON_PRETTY_PRINT);
JSON und Stoppen der Ausführung
v3.10.0
Wenn Sie eine JSON-Response senden und die Ausführung stoppen möchten, können Sie die jsonHalt()
-Methode verwenden. Dies ist nützlich für Fälle, in denen Sie auf eine Art von Autorisierung prüfen und wenn der Benutzer nicht autorisiert ist, können Sie sofort eine JSON-Response senden, den bestehenden Body-Inhalt löschen und die Ausführung stoppen.
Flight::route('/users', function() {
$authorized = someAuthorizationCheck();
// Prüfen, ob der Benutzer autorisiert ist
if($authorized === false) {
Flight::jsonHalt(['error' => 'Unauthorized'], 401);
// kein exit; hier benötigt.
}
// Mit dem Rest der Route fortfahren
});
Vor v3.10.0 hätten Sie etwas wie das tun müssen:
Flight::route('/users', function() {
$authorized = someAuthorizationCheck();
// Prüfen, ob der Benutzer autorisiert ist
if($authorized === false) {
Flight::halt(401, json_encode(['error' => 'Unauthorized']));
}
// Mit dem Rest der Route fortfahren
});
Löschen eines Response-Bodys
Wenn Sie den Response-Body löschen möchten, können Sie die clearBody
-Methode verwenden:
Flight::route('/', function() {
if($someCondition) {
Flight::response()->write("Hello, World!");
} else {
Flight::response()->clearBody();
}
});
Der obige Anwendungsfall ist wahrscheinlich nicht üblich, könnte aber häufiger vorkommen, wenn dies in einem Middleware verwendet wird.
Ausführen eines Callbacks auf dem Response-Body
Sie können einen Callback auf dem Response-Body ausführen, indem Sie die addResponseBodyCallback
-Methode verwenden:
Flight::route('/users', function() {
$db = Flight::db();
$users = $db->fetchAll("SELECT * FROM users");
Flight::render('users_table', ['users' => $users]);
});
// Dies wird alle Responses für jede Route gzippen
Flight::response()->addResponseBodyCallback(function($body) {
return gzencode($body, 9);
});
Sie können mehrere Callbacks hinzufügen, und sie werden in der Reihenfolge ausgeführt, in der sie hinzugefügt wurden. Da dies jede callable akzeptieren kann, kann es ein Klassen-Array [ $class, 'method' ]
, eine Closure $strReplace = function($body) { str_replace('hi', 'there', $body); };
oder einen Funktionsnamen 'minify'
akzeptieren, wenn Sie z.B. eine Funktion haben, um Ihren HTML-Code zu minimieren.
Hinweis: Route-Callbacks funktionieren nicht, wenn Sie die Konfigurationsoption flight.v2.output_buffering
verwenden.
Spezifischer Route-Callback
Wenn Sie möchten, dass dies nur auf eine spezifische Route angewendet wird, können Sie den Callback direkt in der Route hinzufügen:
Flight::route('/users', function() {
$db = Flight::db();
$users = $db->fetchAll("SELECT * FROM users");
Flight::render('users_table', ['users' => $users]);
// Dies wird nur die Response für diese Route gzippen
Flight::response()->addResponseBodyCallback(function($body) {
return gzencode($body, 9);
});
});
Middleware-Option
Sie können auch Middleware verwenden, um den Callback auf alle Routes über Middleware anzuwenden:
// MinifyMiddleware.php
class MinifyMiddleware {
public function before() {
// Wenden Sie den Callback hier auf das response()-Objekt an.
Flight::response()->addResponseBodyCallback(function($body) {
return $this->minify($body);
});
}
protected function minify(string $body): string {
// minimieren Sie den Body irgendwie
return $body;
}
}
// index.php
Flight::group('/users', function() {
Flight::route('', function() { /* ... */ });
Flight::route('/@id', function($id) { /* ... */ });
}, [ new MinifyMiddleware() ]);
Statuscodes
Sie können den Statuscode der Response mit der status
-Methode setzen:
Flight::route('/@id', function($id) {
if($id == 123) {
Flight::response()->status(200);
echo "Hello, World!";
} else {
Flight::response()->status(403);
echo "Forbidden";
}
});
Wenn Sie den aktuellen Statuscode abrufen möchten, können Sie die status
-Methode ohne Argumente verwenden:
Flight::response()->status(); // 200
Setzen eines Response-Headers
Sie können einen Header wie den Content-Type der Response mit der header
-Methode setzen:
// Dies sendet "Hello, World!" an den Browser des Benutzers als reinen Text
Flight::route('/', function() {
Flight::response()->header('Content-Type', 'text/plain');
// oder
Flight::response()->setHeader('Content-Type', 'text/plain');
echo "Hello, World!";
});
Weiterleitung
Sie können die aktuelle Anfrage weiterleiten, indem Sie die redirect()
-Methode verwenden und eine neue URL übergeben:
Flight::route('/login', function() {
$username = Flight::request()->data->username;
$password = Flight::request()->data->password;
$passwordConfirm = Flight::request()->data->password_confirm;
if($password !== $passwordConfirm) {
Flight::redirect('/new/location');
return; // dies ist notwendig, damit die Funktionalität unten nicht ausgeführt wird
}
// fügen Sie den neuen Benutzer hinzu...
Flight::db()->runQuery("INSERT INTO users ....");
Flight::redirect('/admin/dashboard');
});
Hinweis: Standardmäßig sendet Flight einen HTTP 303 ("See Other")-Statuscode. Sie können optional einen benutzerdefinierten Code setzen:
Flight::redirect('/new/location', 301); // permanent
Stoppen der Route-Ausführung
Sie können das Framework stoppen und sofort beenden, indem Sie die halt
-Methode aufrufen:
Flight::halt();
Sie können auch einen optionalen HTTP
-Statuscode und eine Nachricht angeben:
Flight::halt(200, 'Be right back...');
Das Aufrufen von halt
verwirft alle Response-Inhalte bis zu diesem Punkt und stoppt die gesamte Ausführung. Wenn Sie das Framework stoppen und die aktuelle Response ausgeben möchten, verwenden Sie die stop
-Methode:
Flight::stop($httpStatusCode = null);
Hinweis:
Flight::stop()
hat einiges seltsames Verhalten, wie z.B. dass es die Response ausgibt, aber die Ausführung Ihres Skripts fortsetzt, was möglicherweise nicht das ist, was Sie wollen. Sie könnenexit
oderreturn
nach dem Aufruf vonFlight::stop()
verwenden, um weitere Ausführung zu verhindern, aber es wird im Allgemeinen empfohlen,Flight::halt()
zu verwenden.
Dies speichert den Header-Schlüssel und -Wert im Response-Objekt. Am Ende des Request-Lebenszyklus wird es die Header aufbauen und eine Response senden.
Erweiterte Verwendung
Sofortiges Senden eines Headers
Es kann Fälle geben, in denen Sie etwas Benutzerdefiniertes mit dem Header tun müssen und den Header in genau dieser Code-Zeile senden müssen, an der Sie arbeiten. Wenn Sie eine streamed route setzen, ist das, was Sie brauchen. Das ist durch response()->setRealHeader()
erreichbar.
Flight::route('/', function() {
Flight::response()->setRealHeader('Content-Type: text/plain');
echo 'Streaming response...';
sleep(5);
echo 'Done!';
})->stream();
JSONP
Für JSONP-Anfragen können Sie optional den Query-Parameter-Namen übergeben, den Sie verwenden, um Ihre Callback-Funktion zu definieren:
Flight::jsonp(['id' => 123], 'q');
Also, wenn Sie eine GET-Anfrage mit ?q=my_func
stellen, sollten Sie die Ausgabe erhalten:
my_func({"id":123});
Wenn Sie keinen Query-Parameter-Namen übergeben, wird standardmäßig jsonp
verwendet.
Hinweis: Wenn Sie 2025 und später immer noch JSONP-Anfragen verwenden, springen Sie in den Chat und erzählen Sie uns warum! Wir lieben es, gute Kampf-/Horror-Geschichten zu hören!
Löschen von Response-Daten
Sie können den Response-Body und Header löschen, indem Sie die clear()
-Methode verwenden. Dies löscht alle der Response zugewiesenen Header, löscht den Response-Body und setzt den Statuscode auf 200
.
Flight::response()->clear();
Nur Response-Body löschen
Wenn Sie nur den Response-Body löschen möchten, können Sie die clearBody()
-Methode verwenden:
// Dies behält immer noch alle auf dem response()-Objekt gesetzten Header.
Flight::response()->clearBody();
HTTP-Caching
Flight bietet integrierte Unterstützung für HTTP-Level-Caching. Wenn die Caching-Bedingung erfüllt ist, wird Flight eine HTTP 304 Not Modified
-Response zurückgeben. Beim nächsten Mal, wenn der Client dieselbe Ressource anfordert, wird er aufgefordert, seine lokal gecachte Version zu verwenden.
Route-Level-Caching
Wenn Sie Ihre gesamte Response cachen möchten, können Sie die cache()
-Methode verwenden und eine Cache-Zeit übergeben.
// Dies cached die Response für 5 Minuten
Flight::route('/news', function () {
Flight::response()->cache(time() + 300);
echo 'This content will be cached.';
});
// Alternativ können Sie einen String verwenden, den Sie an die strtotime()-Methode übergeben würden
Flight::route('/news', function () {
Flight::response()->cache('+5 minutes');
echo 'This content will be cached.';
});
Last-Modified
Sie können die lastModified
-Methode verwenden und einen UNIX-Timestamp übergeben, um das Datum und die Zeit zu setzen, zu der eine Seite zuletzt geändert wurde. Der Client wird sein Cache weiterhin verwenden, bis der Last-Modified-Wert geändert wird.
Flight::route('/news', function () {
Flight::lastModified(1234567890);
echo 'This content will be cached.';
});
ETag
ETag
-Caching ist ähnlich wie Last-Modified
, außer dass Sie jede ID für die Ressource angeben können, die Sie möchten:
Flight::route('/news', function () {
Flight::etag('my-unique-id');
echo 'This content will be cached.';
});
Beachten Sie, dass das Aufrufen von entweder lastModified
oder etag
beide den Cache-Wert setzt und prüft. Wenn der Cache-Wert zwischen den Anfragen gleich ist, wird Flight sofort eine HTTP 304
-Response senden und die Verarbeitung stoppen.
Herunterladen einer Datei
v3.12.0
Es gibt eine Hilfsmethode, um eine Datei an den Endbenutzer zu streamen. Sie können die download
-Methode verwenden und den Pfad übergeben.
Flight::route('/download', function () {
Flight::download('/path/to/file.txt');
// Ab v3.17.1 können Sie einen benutzerdefinierten Dateinamen für das Download angeben
Flight::download('/path/to/file.txt', 'custom_name.txt');
});
Siehe auch
- Routing - Wie man Routes auf Controller abbildet und Views rendert.
- Requests - Verständnis, wie man eingehende Anfragen handhabt.
- Middleware - Verwendung von Middleware mit Routes für Authentifizierung, Logging usw.
- Warum ein Framework? - Verständnis der Vorteile der Verwendung eines Frameworks wie Flight.
- Erweitern - Wie man Flight mit eigener Funktionalität erweitert.
Fehlerbehebung
- Wenn Sie Probleme mit nicht funktionierenden Weiterleitungen haben, stellen Sie sicher, dass Sie ein
return;
zur Methode hinzufügen. stop()
undhalt()
sind nicht dasselbe.halt()
stoppt die Ausführung sofort, währendstop()
die Ausführung fortsetzt.
Changelog
- v3.17.1 -
$fileName
zudownloadFile()
-Methode hinzugefügt. - v3.12.0 -
downloadFile
-Hilfsmethode hinzugefügt. - v3.10.0 -
jsonHalt
hinzugefügt. - v1.0 - Erste Veröffentlichung.
Learn/events
Event Manager
ab v3.15.0
Überblick
Events ermöglichen es Ihnen, benutzerdefiniertes Verhalten in Ihrer Anwendung zu registrieren und auszulösen. Mit der Ergänzung von Flight::onEvent()
und Flight::triggerEvent()
können Sie nun in Schlüssel-Momente des Lebenszyklus Ihrer App eingreifen oder eigene Events definieren (wie Benachrichtigungen und E-Mails), um Ihren Code modularer und erweiterbarer zu machen. Diese Methoden sind Teil der mappbaren Methoden von Flight, was bedeutet, dass Sie ihr Verhalten nach Bedarf überschreiben können.
Verständnis
Events erlauben es Ihnen, verschiedene Teile Ihrer Anwendung zu trennen, damit sie nicht zu stark voneinander abhängen. Diese Trennung – oft als Entkopplung bezeichnet – macht Ihren Code einfacher zu aktualisieren, zu erweitern oder zu debuggen. Anstatt alles in einem großen Block zu schreiben, können Sie Ihre Logik in kleinere, unabhängige Teile aufteilen, die auf spezifische Aktionen (Events) reagieren.
Stellen Sie sich vor, Sie bauen eine Blog-App:
- Wenn ein Benutzer einen Kommentar postet, möchten Sie möglicherweise:
- Den Kommentar in der Datenbank speichern.
- Eine E-Mail an den Blog-Besitzer senden.
- Die Aktion für Sicherheitszwecke protokollieren.
Ohne Events würden Sie all das in eine Funktion packen. Mit Events können Sie es aufteilen: Ein Teil speichert den Kommentar, ein anderer löst ein Event wie 'comment.posted'
aus, und separate Listener handhaben die E-Mail und das Protokollieren. Das hält Ihren Code sauberer und ermöglicht es Ihnen, Funktionen (wie Benachrichtigungen) hinzuzufügen oder zu entfernen, ohne die Kernlogik zu berühren.
Häufige Anwendungsfälle
In den meisten Fällen eignen sich Events für Dinge, die optional sind, aber nicht zwingend ein absoluter Kernteil Ihres Systems. Zum Beispiel sind die Folgenden gut zu haben, aber wenn sie aus irgendeinem Grund fehlschlagen, sollte Ihre Anwendung immer noch funktionieren:
- Protokollierung: Aktionen wie Logins oder Fehler protokollieren, ohne den Hauptcode zu überladen.
- Benachrichtigungen: E-Mails oder Warnungen senden, wenn etwas passiert.
- Cache-Updates: Caches aktualisieren oder andere Systeme über Änderungen benachrichtigen.
Angenommen jedoch, Sie haben eine „Passwort vergessen“-Funktion. Diese sollte Teil Ihrer Kernfunktionalität sein und kein Event, da wenn diese E-Mail nicht versendet wird, der Benutzer sein Passwort nicht zurücksetzen und Ihre Anwendung nicht nutzen kann.
Grundlegende Verwendung
Das Event-System von Flight basiert auf zwei Hauptmethoden: Flight::onEvent()
zum Registrieren von Event-Listenern und Flight::triggerEvent()
zum Auslösen von Events. Hier ist, wie Sie sie verwenden können:
Registrieren von Event-Listenern
Um auf ein Event zu hören, verwenden Sie Flight::onEvent()
. Diese Methode ermöglicht es Ihnen, zu definieren, was passieren soll, wenn ein Event auftritt.
Flight::onEvent(string $event, callable $callback): void
$event
: Ein Name für Ihr Event (z. B.'user.login'
).$callback
: Die Funktion, die ausgeführt wird, wenn das Event ausgelöst wird.
Sie „abonnieren“ ein Event, indem Sie Flight mitteilen, was es tun soll, wenn es passiert. Der Callback kann Argumente akzeptieren, die vom Event-Auslöser übergeben werden.
Das Event-System von Flight ist synchron, was bedeutet, dass jeder Event-Listener nacheinander ausgeführt wird. Wenn Sie ein Event auslösen, werden alle registrierten Listener für dieses Event vollständig ausgeführt, bevor Ihr Code fortfährt. Dies ist wichtig zu verstehen, da es sich von asynchronen Event-Systemen unterscheidet, bei denen Listener parallel oder zu einem späteren Zeitpunkt ausgeführt werden könnten.
Einfaches Beispiel
Flight::onEvent('user.login', function ($username) {
echo "Willkommen zurück, $username!";
// Sie können eine E-Mail senden, wenn der Login von einem neuen Standort kommt
});
Hier, wenn das 'user.login'
-Event ausgelöst wird, begrüßt es den Benutzer namentlich und könnte auch Logik enthalten, um eine E-Mail zu senden, falls nötig.
Hinweis: Der Callback kann eine Funktion, eine anonyme Funktion oder eine Methode aus einer Klasse sein.
Auslösen von Events
Um ein Event auszulösen, verwenden Sie Flight::triggerEvent()
. Dies weist Flight an, alle für dieses Event registrierten Listener auszuführen und dabei alle von Ihnen bereitgestellten Daten weiterzuleiten.
Flight::triggerEvent(string $event, ...$args): void
$event
: Der Name des Events, das Sie auslösen (muss zu einem registrierten Event passen)....$args
: Optionale Argumente, die an die Listener gesendet werden (kann jede Anzahl von Argumenten sein).
Einfaches Beispiel
$username = 'alice';
Flight::triggerEvent('user.login', $username);
Dies löst das 'user.login'
-Event aus und sendet 'alice'
an den Listener, den wir zuvor definiert haben, was ausgibt: Willkommen zurück, alice!
.
- Wenn keine Listener registriert sind, passiert nichts – Ihre App bricht nicht ab.
- Verwenden Sie den Spread-Operator (
...
), um mehrere Argumente flexibel zu übergeben.
Stoppen von Events
Wenn ein Listener false
zurückgibt, werden keine weiteren Listener für dieses Event ausgeführt. Dies ermöglicht es Ihnen, die Event-Kette basierend auf spezifischen Bedingungen zu stoppen. Denken Sie daran, dass die Reihenfolge der Listener wichtig ist, da der erste, der false
zurückgibt, den Rest stoppt.
Beispiel:
Flight::onEvent('user.login', function ($username) {
if (isBanned($username)) {
logoutUser($username);
return false; // Stoppt nachfolgende Listener
}
});
Flight::onEvent('user.login', function ($username) {
sendWelcomeEmail($username); // Dies wird nie gesendet
});
Überschreiben von Event-Methoden
Flight::onEvent()
und Flight::triggerEvent()
können erweitert werden, was bedeutet, dass Sie definieren können, wie sie funktionieren. Das ist großartig für fortgeschrittene Benutzer, die das Event-System anpassen möchten, z. B. durch Hinzufügen von Protokollierung oder Änderung der Event-Verteilung.
Beispiel: Anpassen von onEvent
Flight::map('onEvent', function (string $event, callable $callback) {
// Protokolliere jede Event-Registrierung
error_log("Neuer Event-Listener hinzugefügt für: $event");
// Rufe das Standardverhalten auf (angenommen ein internes Event-System)
Flight::_onEvent($event, $callback);
});
Jetzt wird jedes Mal, wenn Sie ein Event registrieren, protokolliert, bevor es fortgesetzt wird.
Warum überschreiben?
- Debugging oder Überwachung hinzufügen.
- Events in bestimmten Umgebungen einschränken (z. B. in Tests deaktivieren).
- Mit einer anderen Event-Bibliothek integrieren.
Wo Events platzieren
Wenn Sie neu in den Event-Konzepten in Ihrem Projekt sind, fragen Sie sich vielleicht: Wo registriere ich all diese Events in meiner App? Die Einfachheit von Flight bedeutet, dass es keine strenge Regel gibt – Sie können sie überall platzieren, wo es für Ihr Projekt Sinn macht. Allerdings hilft es, sie organisiert zu halten, um Ihren Code zu pflegen, wenn Ihre App wächst. Hier sind einige praktische Optionen und Best Practices, angepasst an die leichte Natur von Flight:
Option 1: In Ihrer Haupt-index.php
Für kleine Apps oder schnelle Prototypen können Sie Events direkt in Ihrer index.php
-Datei neben Ihren Routen registrieren. Das hält alles an einem Ort, was in Ordnung ist, wenn Einfachheit Ihre Priorität ist.
require 'vendor/autoload.php';
// Events registrieren
Flight::onEvent('user.login', function ($username) {
error_log("$username logged in at " . date('Y-m-d H:i:s'));
});
// Routen definieren
Flight::route('/login', function () {
$username = 'bob';
Flight::triggerEvent('user.login', $username);
echo "Logged in!";
});
Flight::start();
- Vorteile: Einfach, keine extra Dateien, super für kleine Projekte.
- Nachteile: Kann unübersichtlich werden, wenn Ihre App mit mehr Events und Routen wächst.
Option 2: Eine separate events.php
-Datei
Für eine etwas größere App ziehen Sie in Erwägung, Event-Registrierungen in eine dedizierte Datei wie app/config/events.php
zu verschieben. Schließen Sie diese Datei in Ihrer index.php
vor Ihren Routen ein. Das ahmt nach, wie Routen oft in app/config/routes.php
in Flight-Projekten organisiert werden.
// app/config/events.php
Flight::onEvent('user.login', function ($username) {
error_log("$username logged in at " . date('Y-m-d H:i:s'));
});
Flight::onEvent('user.registered', function ($email, $name) {
echo "Email sent to $email: Welcome, $name!";
});
// index.php
require 'vendor/autoload.php';
require 'app/config/events.php';
Flight::route('/login', function () {
$username = 'bob';
Flight::triggerEvent('user.login', $username);
echo "Logged in!";
});
Flight::start();
- Vorteile: Hält
index.php
auf Routing fokussiert, organisiert Events logisch, einfach zu finden und zu bearbeiten. - Nachteile: Fügt eine kleine Struktur hinzu, die für sehr kleine Apps übertrieben wirken könnte.
Option 3: Nahe dem Auslöseort
Ein anderer Ansatz ist, Events nahe dem Ort zu registrieren, an dem sie ausgelöst werden, z. B. in einem Controller oder Routen-Definition. Das funktioniert gut, wenn ein Event spezifisch für einen Teil Ihrer App ist.
Flight::route('/signup', function () {
// Event hier registrieren
Flight::onEvent('user.registered', function ($email) {
echo "Welcome email sent to $email!";
});
$email = 'jane@example.com';
Flight::triggerEvent('user.registered', $email);
echo "Signed up!";
});
- Vorteile: Hält verwandten Code zusammen, gut für isolierte Features.
- Nachteile: Verteilt Event-Registrierungen, was es schwieriger macht, alle Events auf einen Blick zu sehen; Risiko von Duplikaten, wenn nicht vorsichtig.
Best Practice für Flight
- Einfach starten: Für winzige Apps platzieren Sie Events in
index.php
. Es ist schnell und passt zu Flights Minimalismus. - Intelligent wachsen: Wenn Ihre App expandiert (z. B. mehr als 5-10 Events), verwenden Sie eine
app/config/events.php
-Datei. Es ist ein natürlicher Schritt, wie die Organisation von Routen, und hält Ihren Code ordentlich, ohne komplexe Frameworks hinzuzufügen. - Über-Engineering vermeiden: Erstellen Sie keine vollständige „Event-Manager“-Klasse oder -Verzeichnis, es sei denn, Ihre App wird riesig – Flight lebt von Einfachheit, also halten Sie es leichtgewichtig.
Tipp: Nach Zweck gruppieren
In events.php
gruppieren Sie verwandte Events (z. B. alle benutzerbezogenen Events zusammen) mit Kommentaren für Klarheit:
// app/config/events.php
// User Events
Flight::onEvent('user.login', function ($username) {
error_log("$username logged in");
});
Flight::onEvent('user.registered', function ($email) {
echo "Welcome to $email!";
});
// Page Events
Flight::onEvent('page.updated', function ($pageId) {
Flight::cache()->delete("page_$pageId");
});
Diese Struktur skaliert gut und bleibt anfängerfreundlich.
Beispiele aus der Praxis
Lassen Sie uns einige reale Szenarien durchgehen, um zu zeigen, wie Events funktionieren und warum sie hilfreich sind.
Beispiel 1: Protokollieren eines Benutzer-Logins
// Schritt 1: Einen Listener registrieren
Flight::onEvent('user.login', function ($username) {
$time = date('Y-m-d H:i:s');
error_log("$username logged in at $time");
});
// Schritt 2: Es in Ihrer App auslösen
Flight::route('/login', function () {
$username = 'bob'; // Stellen Sie sich vor, das kommt aus einem Formular
Flight::triggerEvent('user.login', $username);
echo "Hi, $username!";
});
Warum nützlich: Der Login-Code muss nichts über Protokollierung wissen – er löst nur das Event aus. Sie können später mehr Listener hinzufügen (z. B. eine Willkommens-E-Mail senden), ohne die Route zu ändern.
Beispiel 2: Benachrichtigen über neue Benutzer
// Listener für neue Registrierungen
Flight::onEvent('user.registered', function ($email, $name) {
// Simuliere das Senden einer E-Mail
echo "Email sent to $email: Welcome, $name!";
});
// Auslösen, wenn jemand registriert
Flight::route('/signup', function () {
$email = 'jane@example.com';
$name = 'Jane';
Flight::triggerEvent('user.registered', $email, $name);
echo "Thanks for signing up!";
});
Warum nützlich: Die Registrierungslogik konzentriert sich auf die Erstellung des Benutzers, während das Event Benachrichtigungen handhabt. Sie könnten später mehr Listener hinzufügen (z. B. die Registrierung protokollieren).
Beispiel 3: Cache leeren
// Listener zum Leeren eines Caches
Flight::onEvent('page.updated', function ($pageId) {
// Wenn Sie das flightphp/cache-Plugin verwenden
Flight::cache()->delete("page_$pageId");
echo "Cache cleared for page $pageId.";
});
// Auslösen, wenn eine Seite bearbeitet wird
Flight::route('/edit-page/(@id)', function ($pageId) {
// Stellen Sie sich vor, wir haben die Seite aktualisiert
Flight::triggerEvent('page.updated', $pageId);
echo "Page $pageId updated.";
});
Warum nützlich: Der Bearbeitungscode kümmert sich nicht um Caching – er signalisiert nur die Aktualisierung. Andere Teile der App können entsprechend reagieren.
Best Practices
- Events klar benennen: Verwenden Sie spezifische Namen wie
'user.login'
oder'page.updated'
, damit klar ist, was sie tun. - Listener einfach halten: Legen Sie keine langsamen oder komplexen Aufgaben in Listener – halten Sie Ihre App schnell.
- Ihre Events testen: Lösen Sie sie manuell aus, um sicherzustellen, dass Listener wie erwartet funktionieren.
- Events weise verwenden: Sie sind großartig für Entkopplung, aber zu viele können Ihren Code schwer nachvollziehbar machen – verwenden Sie sie, wenn es Sinn macht.
Das Event-System in Flight PHP mit Flight::onEvent()
und Flight::triggerEvent()
bietet Ihnen eine einfache, aber leistungsstarke Möglichkeit, flexible Anwendungen zu bauen. Indem verschiedene Teile Ihrer App durch Events miteinander kommunizieren, können Sie Ihren Code organisiert, wiederverwendbar und einfach erweiterbar halten. Ob Sie Aktionen protokollieren, Benachrichtigungen senden oder Updates verwalten – Events helfen Ihnen dabei, ohne Ihre Logik zu verknüpfen. Und mit der Möglichkeit, diese Methoden zu überschreiben, haben Sie die Freiheit, das System an Ihre Bedürfnisse anzupassen. Starten Sie klein mit einem einzelnen Event und beobachten Sie, wie es die Struktur Ihrer App verändert!
Eingebauten Events
Flight PHP kommt mit einigen eingebauten Events, die Sie verwenden können, um in den Lebenszyklus des Frameworks einzugreifen. Diese Events werden an spezifischen Punkten im Request/Response-Zyklus ausgelöst und ermöglichen es Ihnen, benutzerdefinierte Logik auszuführen, wenn bestimmte Aktionen auftreten.
Liste der eingebauten Events
- flight.request.received:
function(Request $request)
Wird ausgelöst, wenn eine Anfrage empfangen, geparst und verarbeitet wird. - flight.error:
function(Throwable $exception)
Wird ausgelöst, wenn ein Fehler während des Request-Lebenszyklus auftritt. - flight.redirect:
function(string $url, int $status_code)
Wird ausgelöst, wenn eine Weiterleitung initiiert wird. - flight.cache.checked:
function(string $cache_key, bool $hit, float $executionTime)
Wird ausgelöst, wenn der Cache für einen spezifischen Schlüssel überprüft wird und ob es ein Treffer oder Fehlschlag war. - flight.middleware.before:
function(Route $route)
Wird ausgelöst, nachdem das Before-Middleware ausgeführt wurde. - flight.middleware.after:
function(Route $route)
Wird ausgelöst, nachdem das After-Middleware ausgeführt wurde. - flight.middleware.executed:
function(Route $route, $middleware, string $method, float $executionTime)
Wird ausgelöst, nachdem ein beliebiges Middleware ausgeführt wurde. - flight.route.matched:
function(Route $route)
Wird ausgelöst, wenn eine Route übereinstimmt, aber noch nicht ausgeführt wird. - flight.route.executed:
function(Route $route, float $executionTime)
Wird ausgelöst, nachdem eine Route ausgeführt und verarbeitet wurde.$executionTime
ist die Zeit, die es gedauert hat, die Route auszuführen (Controller aufrufen usw.). - flight.view.rendered:
function(string $template_file_path, float $executionTime)
Wird ausgelöst, nachdem eine View gerendert wurde.$executionTime
ist die Zeit, die es gedauert hat, das Template zu rendern. Hinweis: Wenn Sie dierender
-Methode überschreiben, müssen Sie dieses Event erneut auslösen. - flight.response.sent:
function(Response $response, float $executionTime)
Wird ausgelöst, nachdem eine Response an den Client gesendet wurde.$executionTime
ist die Zeit, die es gedauert hat, die Response zu erstellen.
Siehe auch
- Erweitern von Flight - Wie Sie die Kernfunktionalität von Flight erweitern und anpassen.
- Cache - Beispiel für die Verwendung von Events, um Cache zu leeren, wenn eine Seite aktualisiert wird.
Fehlerbehebung
- Wenn Ihre Event-Listener nicht aufgerufen werden, stellen Sie sicher, dass Sie sie vor dem Auslösen der Events registrieren. Die Reihenfolge der Registrierung ist wichtig.
Änderungsprotokoll
- v3.15.0 - Events zu Flight hinzugefügt.
Learn/templates
HTML-Ansichten und Vorlagen
Überblick
Flight bietet standardmäßig einige grundlegende Funktionen für HTML-Templating. Templating ist eine sehr effektive Methode, um die Anwendungslogik von der Präsentationsschicht zu trennen.
Verständnis
Wenn Sie eine Anwendung erstellen, haben Sie wahrscheinlich HTML, das Sie an den Endbenutzer zurückliefern möchten. PHP ist an sich eine Templating-Sprache, aber es ist sehr einfach, Geschäftslogik wie Datenbankaufrufe, API-Aufrufe usw. in Ihre HTML-Datei zu integrieren und das Testen und Entkoppeln zu einem sehr schwierigen Prozess zu machen. Indem Sie Daten in eine Vorlage schieben und die Vorlage sich selbst rendern lassen, wird es viel einfacher, Ihren Code zu entkoppeln und Unit-Tests durchzuführen. Sie werden uns dankbar sein, wenn Sie Vorlagen verwenden!
Grundlegende Verwendung
Flight ermöglicht es Ihnen, den Standard-View-Engine einfach zu ersetzen, indem Sie Ihre eigene View-Klasse registrieren. Scrollen Sie nach unten, um Beispiele zu sehen, wie Sie Smarty, Latte, Blade und mehr verwenden können!
Latte
empfohlen
Hier ist, wie Sie den Latte Template-Engine für Ihre Ansichten verwenden würden.
Installation
composer require latte/latte
Grundlegende Konfiguration
Die Hauptidee ist, dass Sie die render
-Methode überschreiben, um Latte anstelle des Standard-PHP-Renders zu verwenden.
// überschreiben der render-Methode, um Latte anstelle des Standard-PHP-Renders zu verwenden
Flight::map('render', function(string $template, array $data, ?string $block): void {
$latte = new Latte\Engine;
// Wo Latte speziell seinen Cache speichert
$latte->setTempDirectory(__DIR__ . '/../cache/');
$finalPath = Flight::get('flight.views.path') . $template;
$latte->render($finalPath, $data, $block);
});
Verwendung von Latte in Flight
Jetzt, da Sie mit Latte rendern können, können Sie etwas wie das tun:
<!-- app/views/home.latte -->
<html>
<head>
<title>{$title ? $title . ' - '}My App</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<h1>Hello, {$name}!</h1>
</body>
</html>
// routes.php
Flight::route('/@name', function ($name) {
Flight::render('home.latte', [
'title' => 'Home Page',
'name' => $name
]);
});
Wenn Sie /Bob
in Ihrem Browser besuchen, wäre die Ausgabe:
<html>
<head>
<title>Home Page - My App</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<h1>Hello, Bob!</h1>
</body>
</html>
Weiterführende Lektüre
Ein komplexeres Beispiel zur Verwendung von Latte mit Layouts wird im Abschnitt awesome plugins dieser Dokumentation gezeigt.
Sie können mehr über die vollen Fähigkeiten von Latte, einschließlich Übersetzung und Sprachfähigkeiten, erfahren, indem Sie die offizielle Dokumentation lesen.
Eingebauter View-Engine
veraltet
Hinweis: Obwohl dies immer noch die Standardfunktionalität ist und technisch noch funktioniert.
Um eine View-Vorlage anzuzeigen, rufen Sie die render
-Methode mit dem Namen
der Vorlagendatei und optionalen Vorlagendaten auf:
Flight::render('hello.php', ['name' => 'Bob']);
Die Vorlagendaten, die Sie übergeben, werden automatisch in die Vorlage injiziert und können
wie eine lokale Variable referenziert werden. Vorlagendateien sind einfach PHP-Dateien. Wenn der
Inhalt der hello.php
-Vorlagendatei so aussieht:
Hello, <?= $name ?>!
Wäre die Ausgabe:
Hello, Bob!
Sie können View-Variablen auch manuell mit der set
-Methode festlegen:
Flight::view()->set('name', 'Bob');
Die Variable name
ist jetzt in allen Ihren Views verfügbar. Also können Sie einfach tun:
Flight::render('hello');
Beachten Sie, dass beim Angabe des Namens der Vorlage in der render
-Methode die
.php
-Erweiterung weggelassen werden kann.
Standardmäßig sucht Flight nach einem views
-Verzeichnis für Vorlagendateien. Sie können
einen alternativen Pfad für Ihre Vorlagen festlegen, indem Sie die folgende Konfiguration setzen:
Flight::set('flight.views.path', '/path/to/views');
Layouts
Es ist üblich, dass Websites eine einzige Layout-Vorlagendatei mit austauschbarem
Inhalt haben. Um Inhalt zu rendern, der in einem Layout verwendet werden soll, können Sie einen optionalen
Parameter an die render
-Methode übergeben.
Flight::render('header', ['heading' => 'Hello'], 'headerContent');
Flight::render('body', ['body' => 'World'], 'bodyContent');
Ihre View wird dann gespeicherte Variablen namens headerContent
und bodyContent
haben.
Sie können dann Ihr Layout rendern, indem Sie tun:
Flight::render('layout', ['title' => 'Home Page']);
Wenn die Vorlagendateien so aussehen:
header.php
:
<h1><?= $heading ?></h1>
body.php
:
<div><?= $body ?></div>
layout.php
:
<html>
<head>
<title><?= $title ?></title>
</head>
<body>
<?= $headerContent ?>
<?= $bodyContent ?>
</body>
</html>
Wäre die Ausgabe:
<html>
<head>
<title>Home Page</title>
</head>
<body>
<h1>Hello</h1>
<div>World</div>
</body>
</html>
Smarty
Hier ist, wie Sie den Smarty Template-Engine für Ihre Ansichten verwenden würden:
// Laden der Smarty-Bibliothek
require './Smarty/libs/Smarty.class.php';
// Registrieren von Smarty als View-Klasse
// Auch Übergeben einer Callback-Funktion, um Smarty beim Laden zu konfigurieren
Flight::register('view', Smarty::class, [], function (Smarty $smarty) {
$smarty->setTemplateDir('./templates/');
$smarty->setCompileDir('./templates_c/');
$smarty->setConfigDir('./config/');
$smarty->setCacheDir('./cache/');
});
// Zuweisen von Vorlagendaten
Flight::view()->assign('name', 'Bob');
// Anzeigen der Vorlage
Flight::view()->display('hello.tpl');
Zur Vollständigkeit sollten Sie auch die Standard-render
-Methode von Flight überschreiben:
Flight::map('render', function(string $template, array $data): void {
Flight::view()->assign($data);
Flight::view()->display($template);
});
Blade
Hier ist, wie Sie den Blade Template-Engine für Ihre Ansichten verwenden würden:
Zuerst müssen Sie die BladeOne-Bibliothek über Composer installieren:
composer require eftec/bladeone
Dann können Sie BladeOne als View-Klasse in Flight konfigurieren:
<?php
// Laden der BladeOne-Bibliothek
use eftec\bladeone\BladeOne;
// Registrieren von BladeOne als View-Klasse
// Auch Übergeben einer Callback-Funktion, um BladeOne beim Laden zu konfigurieren
Flight::register('view', BladeOne::class, [], function (BladeOne $blade) {
$views = __DIR__ . '/../views';
$cache = __DIR__ . '/../cache';
$blade->setPath($views);
$blade->setCompiledPath($cache);
});
// Zuweisen von Vorlagendaten
Flight::view()->share('name', 'Bob');
// Anzeigen der Vorlage
echo Flight::view()->run('hello', []);
Zur Vollständigkeit sollten Sie auch die Standard-render
-Methode von Flight überschreiben:
<?php
Flight::map('render', function(string $template, array $data): void {
echo Flight::view()->run($template, $data);
});
In diesem Beispiel könnte die Datei hello.blade.php so aussehen:
<?php
Hello, {{ $name }}!
Die Ausgabe wäre:
Hello, Bob!
Siehe auch
- Erweitern - Wie man die
render
-Methode überschreibt, um einen anderen Template-Engine zu verwenden. - Routing - Wie man Routen zu Controllern zuweist und Views rendert.
- Responses - Wie man HTTP-Antworten anpasst.
- Warum ein Framework? - Wie Vorlagen ins Gesamtbild passen.
Fehlerbehebung
- Wenn Sie eine Weiterleitung in Ihrem Middleware haben, aber Ihre App scheint nicht weiterzuleiten, stellen Sie sicher, dass Sie eine
exit;
-Anweisung in Ihrem Middleware hinzufügen.
Changelog
- v2.0 - Erste Veröffentlichung.
Learn/collections
Collections
Überblick
Die Collection
-Klasse in Flight ist ein nützliches Hilfsprogramm zum Verwalten von Datensätzen. Sie ermöglicht den Zugriff und die Manipulation von Daten mit Array- und Objekt-Notation, was Ihren Code sauberer und flexibler macht.
Verständnis
Eine Collection
ist im Wesentlichen eine Umhüllung um ein Array, aber mit zusätzlichen Fähigkeiten. Sie können sie wie ein Array verwenden, darüber iterieren, die Anzahl ihrer Elemente zählen und sogar auf Elemente zugreifen, als wären sie Objekteigenschaften. Dies ist besonders nützlich, wenn Sie strukturierte Daten in Ihrer App weitergeben möchten oder Ihren Code lesbarer gestalten wollen.
Collections implementieren mehrere PHP-Schnittstellen:
ArrayAccess
(damit Sie Array-Syntax verwenden können)Iterator
(damit Sie mitforeach
iterieren können)Countable
(damit Siecount()
verwenden können)JsonSerializable
(damit Sie einfach in JSON umwandeln können)
Grundlegende Verwendung
Erstellen einer Collection
Sie können eine Collection erstellen, indem Sie einfach ein Array an ihren Konstruktor übergeben:
use flight\util\Collection;
$data = [
'name' => 'Flight',
'version' => 3,
'features' => ['routing', 'views', 'extending']
];
$collection = new Collection($data);
Zugriff auf Elemente
Sie können auf Elemente mit Array- oder Objekt-Notation zugreifen:
// Array-Notation
echo $collection['name']; // Ausgabe: FlightPHP
// Objekt-Notation
echo $collection->version; // Ausgabe: 3
Wenn Sie versuchen, auf einen Schlüssel zuzugreifen, der nicht existiert, erhalten Sie null
anstelle eines Fehlers.
Setzen von Elementen
Sie können Elemente mit beiden Notationen setzen:
// Array-Notation
$collection['author'] = 'Mike Cao';
// Objekt-Notation
$collection->license = 'MIT';
Überprüfen und Entfernen von Elementen
Überprüfen Sie, ob ein Element existiert:
if (isset($collection['name'])) {
// Etwas tun
}
if (isset($collection->version)) {
// Etwas tun
}
Entfernen Sie ein Element:
unset($collection['author']);
unset($collection->license);
Iterieren über eine Collection
Collections sind iterierbar, sodass Sie sie in einer foreach
-Schleife verwenden können:
foreach ($collection as $key => $value) {
echo "$key: $value\n";
}
Zählen von Elementen
Sie können die Anzahl der Elemente in einer Collection zählen:
echo count($collection); // Ausgabe: 4
Alle Schlüssel oder Daten abrufen
Alle Schlüssel abrufen:
$keys = $collection->keys(); // ['name', 'version', 'features', 'license']
Alle Daten als Array abrufen:
$data = $collection->getData();
Collection leeren
Alle Elemente entfernen:
$collection->clear();
JSON-Serialisierung
Collections können einfach in JSON umgewandelt werden:
echo json_encode($collection);
// Ausgabe: {"name":"FlightPHP","version":3,"features":["routing","views","extending"],"license":"MIT"}
Erweiterte Verwendung
Sie können das interne Daten-Array vollständig ersetzen, falls benötigt:
$collection->setData(['foo' => 'bar']);
Collections sind besonders nützlich, wenn Sie strukturierte Daten zwischen Komponenten weitergeben möchten oder eine objektorientiertere Schnittstelle für Array-Daten bereitstellen wollen.
Siehe auch
- Requests - Erfahren Sie, wie Sie HTTP-Anfragen handhaben und wie Collections zur Verwaltung von Anfragedaten verwendet werden können.
- PDO Wrapper - Erfahren Sie, wie Sie den PDO-Wrapper in Flight verwenden und wie Collections zur Verwaltung von Datenbankergebnissen genutzt werden können.
Fehlerbehebung
- Wenn Sie versuchen, auf einen nicht existierenden Schlüssel zuzugreifen, erhalten Sie
null
anstelle eines Fehlers. - Denken Sie daran, dass Collections nicht rekursiv sind: Verschachtelte Arrays werden nicht automatisch in Collections umgewandelt.
- Wenn Sie die Collection zurücksetzen müssen, verwenden Sie
$collection->clear()
oder$collection->setData([])
.
Änderungsprotokoll
- v3.0 - Verbesserte Typ-Hinweise und Unterstützung für PHP 8+.
- v1.0 - Erste Veröffentlichung der Collection-Klasse.
Learn/flight_vs_fat_free
Flight vs Fat-Free
Was ist Fat-Free?
Fat-Free (liebenswürdig bekannt als F3) ist ein leistungsstarkes, aber einfach zu bedienendes PHP-Micro-Framework, das Ihnen hilft, dynamische und robuste Web-Anwendungen – schnell – zu erstellen!
Flight vergleicht sich mit Fat-Free in vielerlei Hinsicht und ist wahrscheinlich der nächste Verwandte in Bezug auf Funktionen und Einfachheit. Fat-Free hat eine Menge Funktionen, die Flight nicht hat, aber es hat auch viele Funktionen, die Flight hat. Fat-Free zeigt langsam sein Alter und ist nicht mehr so beliebt wie früher.
Updates werden seltener, und die Community ist nicht mehr so aktiv wie früher. Der Code ist einfach genug, aber manchmal kann der Mangel an Syntax-Disziplin es schwierig machen, ihn zu lesen und zu verstehen. Es funktioniert für PHP 8.3, aber der Code selbst sieht immer noch so aus, als würde er in PHP 5.3 leben.
Vorteile im Vergleich zu Flight
- Fat-Free hat ein paar mehr Sterne auf GitHub als Flight.
- Fat-Free hat eine ordentliche Dokumentation, aber es fehlt in einigen Bereichen an Klarheit.
- Fat-Free hat einige knappe Ressourcen wie YouTube-Tutorials und Online-Artikel, die verwendet werden können, um das Framework zu lernen.
- Fat-Free hat einige hilfreiche Plugins integriert, die manchmal nützlich sind.
- Fat-Free hat ein integriertes ORM namens Mapper, das verwendet werden kann, um mit Ihrer Datenbank zu interagieren. Flight hat active-record.
- Fat-Free hat Sessions, Caching und Lokalisierung integriert. Flight erfordert die Verwendung von Drittanbieter-Bibliotheken, aber es wird in der Dokumentation abgedeckt.
- Fat-Free hat eine kleine Gruppe von von der Community erstellten Plugins, die verwendet werden können, um das Framework zu erweitern. Flight hat einige in der Dokumentation und Beispiele Seiten abgedeckt.
- Fat-Free hat wie Flight keine Abhängigkeiten.
- Fat-Free ist wie Flight darauf ausgerichtet, dem Entwickler Kontrolle über seine Anwendung und ein einfaches Entwicklererlebnis zu geben.
- Fat-Free erhält wie Flight die Abwärtskompatibilität (teilweise, weil Updates seltener werden seltener).
- Fat-Free ist wie Flight für Entwickler gedacht, die zum ersten Mal in die Welt der Frameworks eintauchen.
- Fat-Free hat einen integrierten Template-Engine, der robuster ist als Flight's Template-Engine. Flight empfiehlt Latte, um dies zu erreichen.
- Fat-Free hat einen einzigartigen CLI-Befehl vom Typ "route", mit dem Sie CLI-Apps innerhalb von Fat-Free selbst erstellen und ihn wie eine
GET
-Anfrage behandeln können. Flight erreicht dies mit runway.
Nachteile im Vergleich zu Flight
- Fat-Free hat einige Implementierungstests und sogar eine eigene Test-Klasse, die sehr basisch ist. Allerdings ist es nicht zu 100 % Unit-getestet wie Flight.
- Sie müssen eine Suchmaschine wie Google verwenden, um die Dokumentationsseite tatsächlich zu durchsuchen.
- Flight hat Dark Mode auf ihrer Dokumentationsseite. (Mic Drop)
- Fat-Free hat einige Module, die erbärmlich unmaintained sind.
- Flight hat einen einfachen PdoWrapper, der etwas einfacher ist als Fat-Free's integrierte
DB\SQL
-Klasse. - Flight hat ein Permissions-Plugin, das verwendet werden kann, um Ihre Anwendung zu sichern. Fat-Free erfordert die Verwendung einer Drittanbieter-Bibliothek.
- Flight hat ein ORM namens active-record, das sich mehr wie ein ORM anfühlt als Fat-Free's Mapper.
Der zusätzliche Vorteil von
active-record
ist, dass Sie Beziehungen zwischen Datensätzen definieren können für automatische Joins, wo Fat-Free's Mapper erfordert, dass Sie SQL-Views erstellen. - Erstaunlicherweise hat Fat-Free keinen Root-Namespace. Flight ist namespaced bis zum Ende, um nicht mit Ihrem eigenen Code zu kollidieren.
Die
Cache
-Klasse ist hier der größte Übeltäter. - Fat-Free hat kein Middleware. Stattdessen gibt es
beforeroute
- undafterroute
-Hooks, die verwendet werden können, um Anfragen und Antworten in Controllern zu filtern. - Fat-Free kann Routen nicht gruppieren.
- Fat-Free hat einen Dependency-Injection-Container-Handler, aber die Dokumentation ist unglaublich spärlich darüber, wie man ihn verwendet.
- Debugging kann etwas knifflig werden, da im Wesentlichen alles in dem gespeichert wird, was als
HIVE
bezeichnet wird.
Learn/extending
Erweitern
Überblick
Flight ist so konzipiert, dass es ein erweiterbares Framework ist. Das Framework kommt mit einer Reihe von Standardmethoden und -komponenten, erlaubt es Ihnen jedoch, Ihre eigenen Methoden zuzuordnen, Ihre eigenen Klassen zu registrieren oder sogar bestehende Klassen und Methoden zu überschreiben.
Verständnis
Es gibt 2 Wege, wie Sie die Funktionalität von Flight erweitern können:
- Methoden zuordnen - Dies wird verwendet, um einfache benutzerdefinierte Methoden zu erstellen, die Sie von überall in Ihrer Anwendung aufrufen können. Diese werden typischerweise für Hilfsfunktionen verwendet, die Sie von überall in Ihrem Code aufrufen möchten.
- Klassen registrieren - Dies wird verwendet, um Ihre eigenen Klassen bei Flight zu registrieren. Dies wird typischerweise für Klassen verwendet, die Abhängigkeiten haben oder Konfiguration erfordern.
Sie können auch bestehende Framework-Methoden überschreiben, um ihr Standardverhalten zu ändern, um den Bedürfnissen Ihres Projekts besser zu entsprechen.
Wenn Sie nach einem DIC (Dependency Injection Container) suchen, schauen Sie auf der Dependency Injection Container-Seite vorbei.
Grundlegende Verwendung
Framework-Methoden überschreiben
Flight erlaubt es Ihnen, seine Standardfunktionalität zu überschreiben, um Ihren eigenen Bedürfnissen zu entsprechen, ohne Code zu modifizieren. Sie können alle überschreibbaren Methoden unten ansehen.
Zum Beispiel ruft Flight, wenn es eine URL nicht einer Route zuordnen kann, die notFound
-Methode auf, die eine generische HTTP 404
-Antwort sendet. Sie können dieses Verhalten überschreiben, indem Sie die map
-Methode verwenden:
Flight::map('notFound', function() {
// Anzeigen einer benutzerdefinierten 404-Seite
include 'errors/404.html';
});
Flight erlaubt es Ihnen auch, Kernkomponenten des Frameworks zu ersetzen. Zum Beispiel können Sie die Standard-Router-Klasse durch Ihre eigene benutzerdefinierte Klasse ersetzen:
// Erstellen Sie Ihre benutzerdefinierte Router-Klasse
class MyRouter extends \flight\net\Router {
// Methoden hier überschreiben
// Zum Beispiel eine Abkürzung für GET-Anfragen, um die
// Pass-Route-Funktion zu entfernen
public function get($pattern, $callback, $alias = '') {
return parent::get($pattern, $callback, false, $alias);
}
}
// Registrieren Sie Ihre benutzerdefinierte Klasse
Flight::register('router', MyRouter::class);
// Wenn Flight die Router-Instanz lädt, wird Ihre Klasse geladen
$myRouter = Flight::router();
$myRouter->get('/hello', function() {
echo "Hello World!";
}, 'hello_alias');
Framework-Methoden wie map
und register
können jedoch nicht überschrieben werden. Sie erhalten einen Fehler, wenn Sie es versuchen (sehen Sie wieder unten für eine Liste der Methoden).
Zuordbare Framework-Methoden
Das Folgende ist die vollständige Menge der Methoden für das Framework. Es besteht aus Kernmethoden, die reguläre statische Methoden sind, und erweiterbaren Methoden, die zugeordnete Methoden sind, die gefiltert oder überschrieben werden können.
Kernmethoden
Diese Methoden sind zentral für das Framework und können nicht überschrieben werden.
Flight::map(string $name, callable $callback, bool $pass_route = false) // Erstellt eine benutzerdefinierte Framework-Methode.
Flight::register(string $name, string $class, array $params = [], ?callable $callback = null) // Registriert eine Klasse für eine Framework-Methode.
Flight::unregister(string $name) // Entregistriert eine Klasse für eine Framework-Methode.
Flight::before(string $name, callable $callback) // Fügt einen Filter vor einer Framework-Methode hinzu.
Flight::after(string $name, callable $callback) // Fügt einen Filter nach einer Framework-Methode hinzu.
Flight::path(string $path) // Fügt einen Pfad für das Autoloading von Klassen hinzu.
Flight::get(string $key) // Holt eine Variable, die von Flight::set() gesetzt wurde.
Flight::set(string $key, mixed $value) // Setzt eine Variable im Flight-Engine.
Flight::has(string $key) // Überprüft, ob eine Variable gesetzt ist.
Flight::clear(array|string $key = []) // Löscht eine Variable.
Flight::init() // Initialisiert das Framework mit seinen Standardeinstellungen.
Flight::app() // Holt die Anwendungsobjekt-Instanz
Flight::request() // Holt die Request-Objekt-Instanz
Flight::response() // Holt die Response-Objekt-Instanz
Flight::router() // Holt die Router-Objekt-Instanz
Flight::view() // Holt die View-Objekt-Instanz
Erweiterbare Methoden
Flight::start() // Startet das Framework.
Flight::stop() // Stoppt das Framework und sendet eine Antwort.
Flight::halt(int $code = 200, string $message = '') // Stoppt das Framework mit einem optionalen Statuscode und einer Nachricht.
Flight::route(string $pattern, callable $callback, bool $pass_route = false, string $alias = '') // Ordnet ein URL-Muster einem Callback zu.
Flight::post(string $pattern, callable $callback, bool $pass_route = false, string $alias = '') // Ordnet ein POST-Request-URL-Muster einem Callback zu.
Flight::put(string $pattern, callable $callback, bool $pass_route = false, string $alias = '') // Ordnet ein PUT-Request-URL-Muster einem Callback zu.
Flight::patch(string $pattern, callable $callback, bool $pass_route = false, string $alias = '') // Ordnet ein PATCH-Request-URL-Muster einem Callback zu.
Flight::delete(string $pattern, callable $callback, bool $pass_route = false, string $alias = '') // Ordnet ein DELETE-Request-URL-Muster einem Callback zu.
Flight::group(string $pattern, callable $callback) // Erstellt Gruppierungen für URLs, das Muster muss ein String sein.
Flight::getUrl(string $name, array $params = []) // Generiert eine URL basierend auf einem Route-Alias.
Flight::redirect(string $url, int $code) // Leitet zu einer anderen URL um.
Flight::download(string $filePath) // Lädt eine Datei herunter.
Flight::render(string $file, array $data, ?string $key = null) // Rendert eine Template-Datei.
Flight::error(Throwable $error) // Sendet eine HTTP-500-Antwort.
Flight::notFound() // Sendet eine HTTP-404-Antwort.
Flight::etag(string $id, string $type = 'string') // Führt ETag-HTTP-Caching durch.
Flight::lastModified(int $time) // Führt letztes-Änderungs-HTTP-Caching durch.
Flight::json(mixed $data, int $code = 200, bool $encode = true, string $charset = 'utf8', int $option) // Sendet eine JSON-Antwort.
Flight::jsonp(mixed $data, string $param = 'jsonp', int $code = 200, bool $encode = true, string $charset = 'utf8', int $option) // Sendet eine JSONP-Antwort.
Flight::jsonHalt(mixed $data, int $code = 200, bool $encode = true, string $charset = 'utf8', int $option) // Sendet eine JSON-Antwort und stoppt das Framework.
Flight::onEvent(string $event, callable $callback) // Registriert einen Event-Listener.
Flight::triggerEvent(string $event, ...$args) // Löst ein Event aus.
Jede benutzerdefinierte Methode, die mit map
und register
hinzugefügt wurde, kann auch gefiltert werden. Für Beispiele, wie man diese Methoden filtert, siehe die Filtering Methods-Anleitung.
Erweiterbare Framework-Klassen
Es gibt mehrere Klassen, deren Funktionalität Sie durch Erweiterung und Registrierung Ihrer eigenen Klasse überschreiben können. Diese Klassen sind:
Flight::app() // Anwendungsklasse - erweitern Sie die flight\Engine-Klasse
Flight::request() // Request-Klasse - erweitern Sie die flight\net\Request-Klasse
Flight::response() // Response-Klasse - erweitern Sie die flight\net\Response-Klasse
Flight::router() // Router-Klasse - erweitern Sie die flight\net\Router-Klasse
Flight::view() // View-Klasse - erweitern Sie die flight\template\View-Klasse
Flight::eventDispatcher() // Event-Dispatcher-Klasse - erweitern Sie die flight\core\Dispatcher-Klasse
Benutzerdefinierte Methoden zuordnen
Um Ihre eigene einfache benutzerdefinierte Methode zuzuordnen, verwenden Sie die map
-Funktion:
// Ordnen Sie Ihre Methode zu
Flight::map('hello', function (string $name) {
echo "hello $name!";
});
// Rufen Sie Ihre benutzerdefinierte Methode auf
Flight::hello('Bob');
Während es möglich ist, einfache benutzerdefinierte Methoden zu erstellen, wird empfohlen, einfach Standardfunktionen in PHP zu erstellen. Dies hat Autovervollständigung in IDEs und ist einfacher zu lesen. Das Äquivalent des obigen Codes wäre:
function hello(string $name) {
echo "hello $name!";
}
hello('Bob');
Dies wird mehr verwendet, wenn Sie Variablen in Ihre Methode übergeben müssen, um einen erwarteten Wert zu erhalten. Die Verwendung der register()
-Methode wie unten ist mehr für das Übergeben von Konfiguration und dann das Aufrufen Ihrer vorkonfigurierten Klasse.
Benutzerdefinierte Klassen registrieren
Um Ihre eigene Klasse zu registrieren und sie zu konfigurieren, verwenden Sie die register
-Funktion. Der Vorteil, den dies gegenüber map() hat, ist, dass Sie dieselbe Klasse wiederverwenden können, wenn Sie diese Funktion aufrufen (wäre hilfreich mit Flight::db()
, um dieselbe Instanz zu teilen).
// Registrieren Sie Ihre Klasse
Flight::register('user', User::class);
// Holen Sie eine Instanz Ihrer Klasse
$user = Flight::user();
Die register-Methode erlaubt es Ihnen auch, Parameter an den Konstruktor Ihrer Klasse zu übergeben. Wenn Sie also Ihre benutzerdefinierte Klasse laden, wird sie voreingestellt initialisiert. Sie können die Konstruktor-Parameter definieren, indem Sie ein zusätzliches Array übergeben. Hier ist ein Beispiel für das Laden einer Datenbankverbindung:
// Klasse mit Konstruktor-Parametern registrieren
Flight::register('db', PDO::class, ['mysql:host=localhost;dbname=test', 'user', 'pass']);
// Holen Sie eine Instanz Ihrer Klasse
// Dies wird ein Objekt mit den definierten Parametern erstellen
//
// new PDO('mysql:host=localhost;dbname=test','user','pass');
//
$db = Flight::db();
// und wenn Sie es später in Ihrem Code benötigen, rufen Sie einfach dieselbe Methode erneut auf
class SomeController {
public function __construct() {
$this->db = Flight::db();
}
}
Wenn Sie einen zusätzlichen Callback-Parameter übergeben, wird er unmittelbar nach der Klassenkonstruktion ausgeführt. Dies erlaubt es Ihnen, alle Einrichtungsverfahren für Ihr neues Objekt durchzuführen. Die Callback-Funktion nimmt einen Parameter: eine Instanz des neuen Objekts.
// Der Callback wird das konstruierte Objekt übergeben
Flight::register(
'db',
PDO::class,
['mysql:host=localhost;dbname=test', 'user', 'pass'],
function (PDO $db) {
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
}
);
Standardmäßig erhalten Sie bei jedem Laden Ihrer Klasse eine geteilte Instanz.
Um eine neue Instanz einer Klasse zu erhalten, übergeben Sie einfach false
als Parameter:
// Geteilte Instanz der Klasse
$shared = Flight::db();
// Neue Instanz der Klasse
$new = Flight::db(false);
Hinweis: Beachten Sie, dass zugeordnete Methoden Vorrang vor registrierten Klassen haben. Wenn Sie beide mit demselben Namen deklarieren, wird nur die zugeordnete Methode aufgerufen.
Beispiele
Hier sind einige Beispiele, wie Sie Flight mit Funktionalität erweitern können, die nicht im Kern integriert ist.
Logging
Flight hat kein integriertes Logging-System, es ist jedoch wirklich einfach, eine Logging-Bibliothek mit Flight zu verwenden. Hier ist ein Beispiel mit der Monolog-Bibliothek:
// services.php
// Registrieren Sie den Logger bei Flight
Flight::register('log', Monolog\Logger::class, [ 'name' ], function(Monolog\Logger $log) {
$log->pushHandler(new Monolog\Handler\StreamHandler('path/to/your.log', Monolog\Logger::WARNING));
});
Nun, da es registriert ist, können Sie es in Ihrer Anwendung verwenden:
// In Ihrem Controller oder Route
Flight::log()->warning('This is a warning message');
Dies wird eine Nachricht in die von Ihnen angegebene Log-Datei schreiben. Was, wenn Sie etwas protokollieren möchten, wenn ein Fehler auftritt? Sie können die error
-Methode verwenden:
// In Ihrem Controller oder Route
Flight::map('error', function(Throwable $ex) {
Flight::log()->error($ex->getMessage());
// Zeigen Sie Ihre benutzerdefinierte Fehlerseite an
include 'errors/500.html';
});
Sie könnten auch ein einfaches APM (Application Performance Monitoring)-System mit den before
- und after
-Methoden erstellen:
// In Ihrer services.php-Datei
Flight::before('start', function() {
Flight::set('start_time', microtime(true));
});
Flight::after('start', function() {
$end = microtime(true);
$start = Flight::get('start_time');
Flight::log()->info('Request '.Flight::request()->url.' took ' . round($end - $start, 4) . ' seconds');
// Sie könnten auch Ihre Request- oder Response-Header hinzufügen
// um sie zu protokollieren (seien Sie vorsichtig, da dies eine
// Menge Daten sein würde, wenn Sie viele Anfragen haben)
Flight::log()->info('Request Headers: ' . json_encode(Flight::request()->headers));
Flight::log()->info('Response Headers: ' . json_encode(Flight::response()->headers));
});
Caching
Flight hat kein integriertes Caching-System, es ist jedoch wirklich einfach, eine Caching-Bibliothek mit Flight zu verwenden. Hier ist ein Beispiel mit der PHP File Cache-Bibliothek:
// services.php
// Registrieren Sie den Cache bei Flight
Flight::register('cache', \flight\Cache::class, [ __DIR__ . '/../cache/' ], function(\flight\Cache $cache) {
$cache->setDevMode(ENVIRONMENT === 'development');
});
Nun, da es registriert ist, können Sie es in Ihrer Anwendung verwenden:
// In Ihrem Controller oder Route
$data = Flight::cache()->get('my_cache_key');
if (empty($data)) {
// Führen Sie einige Verarbeitung durch, um die Daten zu erhalten
$data = [ 'some' => 'data' ];
Flight::cache()->set('my_cache_key', $data, 3600); // Cache für 1 Stunde
}
Einfache DIC-Objekt-Instantiierung
Wenn Sie einen DIC (Dependency Injection Container) in Ihrer Anwendung verwenden, können Sie Flight verwenden, um Ihnen bei der Instantiierung Ihrer Objekte zu helfen. Hier ist ein Beispiel mit der Dice-Bibliothek:
// services.php
// Erstellen Sie einen neuen Container
$container = new \Dice\Dice;
// Vergessen Sie nicht, ihn sich selbst zuzuweisen wie unten!
$container = $container->addRule('PDO', [
// shared bedeutet, dass dasselbe Objekt jedes Mal zurückgegeben wird
'shared' => true,
'constructParams' => ['mysql:host=localhost;dbname=test', 'user', 'pass' ]
]);
// Nun können wir eine zuordbare Methode erstellen, um jedes Objekt zu erstellen.
Flight::map('make', function($class, $params = []) use ($container) {
return $container->create($class, $params);
});
// Dies registriert den Container-Handler, damit Flight weiß, dass er ihn für Controller/Middleware verwendet
Flight::registerContainerHandler(function($class, $params) {
Flight::make($class, $params);
});
// Sagen wir, wir haben die folgende Beispielklasse, die ein PDO-Objekt im Konstruktor nimmt
class EmailCron {
protected PDO $pdo;
public function __construct(PDO $pdo) {
$this->pdo = $pdo;
}
public function send() {
// Code, der eine E-Mail sendet
}
}
// Und schließlich können Sie Objekte mit Dependency Injection erstellen
$emailCron = Flight::make(EmailCron::class);
$emailCron->send();
Cool, oder?
Siehe auch
- Dependency Injection Container - Wie man einen DIC mit Flight verwendet.
- File Cache - Beispiel für die Verwendung einer Caching-Bibliothek mit Flight.
Fehlerbehebung
- Denken Sie daran, dass zugeordnete Methoden Vorrang vor registrierten Klassen haben. Wenn Sie beide mit demselben Namen deklarieren, wird nur die zugeordnete Methode aufgerufen.
Änderungsprotokoll
- v2.0 - Erste Veröffentlichung.
Learn/json
JSON Wrapper
Übersicht
Die Json
-Klasse in Flight bietet eine einfache, konsistente Möglichkeit, JSON-Daten in Ihrer Anwendung zu kodieren und zu dekodieren. Sie umhüllt die nativen JSON-Funktionen von PHP mit besserer Fehlerbehandlung und einigen hilfreichen Standardeinstellungen, was die Arbeit mit JSON einfacher und sicherer macht.
Verständnis
Die Arbeit mit JSON ist in modernen PHP-Apps sehr üblich, insbesondere beim Aufbau von APIs oder der Behandlung von AJAX-Anfragen. Die Json
-Klasse zentralisiert alle Ihre JSON-Kodierungen und -Dekodierungen, sodass Sie sich keine Gedanken über seltsame Randfälle oder kryptische Fehler aus den integrierten Funktionen von PHP machen müssen.
Wichtige Funktionen:
- Konsistente Fehlerbehandlung (wirft Ausnahmen bei Fehlern)
- Standardoptionen für Kodierung/Dekodierung (wie unentfesselte Schrägstriche)
- Hilfsmethoden für schöne Ausgabe und Validierung
Grundlegende Verwendung
Daten zu JSON kodieren
Um PHP-Daten in einen JSON-String umzuwandeln, verwenden Sie Json::encode()
:
use flight\util\Json;
$data = [
'framework' => 'Flight',
'version' => 3,
'features' => ['routing', 'views', 'extending']
];
$json = Json::encode($data);
echo $json;
// Ausgabe: {"framework":"Flight","version":3,"features":["routing","views","extending"]}
Falls die Kodierung fehlschlägt, erhalten Sie eine Ausnahme mit einer hilfreichen Fehlermeldung.
Schöne Ausgabe
Möchten Sie, dass Ihr JSON lesbar für Menschen ist? Verwenden Sie prettyPrint()
:
echo Json::prettyPrint($data);
/*
{
"framework": "Flight",
"version": 3,
"features": [
"routing",
"views",
"extending"
]
}
*/
JSON-Strings dekodieren
Um einen JSON-String zurück in PHP-Daten umzuwandeln, verwenden Sie Json::decode()
:
$json = '{"framework":"Flight","version":3}';
$data = Json::decode($json);
echo $data->framework; // Ausgabe: Flight
Wenn Sie ein assoziatives Array anstelle eines Objekts möchten, übergeben Sie true
als zweiten Argument:
$data = Json::decode($json, true);
echo $data['framework']; // Ausgabe: Flight
Falls die Dekodierung fehlschlägt, erhalten Sie eine Ausnahme mit einer klaren Fehlermeldung.
JSON validieren
Überprüfen Sie, ob ein String gültiges JSON ist:
if (Json::isValid($json)) {
// Es ist gültig!
} else {
// Kein gültiges JSON
}
Letzten Fehler abrufen
Wenn Sie die letzte JSON-Fehlermeldung überprüfen möchten (aus den nativen PHP-Funktionen):
$error = Json::getLastError();
if ($error !== '') {
echo "Letzter JSON-Fehler: $error";
}
Erweiterte Verwendung
Sie können Kodierungs- und Dekodierungsoptionen anpassen, wenn Sie mehr Kontrolle benötigen (siehe PHP's json_encode-Optionen):
// Kodieren mit HEX_TAG-Option
$json = Json::encode($data, JSON_HEX_TAG);
// Dekodieren mit benutzerdefinierter Tiefe
$data = Json::decode($json, false, 1024);
Siehe auch
- Collections - Für die Arbeit mit strukturierten Daten, die leicht in JSON umgewandelt werden können.
- Configuration - Wie Sie Ihre Flight-App konfigurieren.
- Extending - Wie Sie eigene Hilfsmethoden hinzufügen oder Kernklassen überschreiben.
Fehlerbehebung
- Wenn die Kodierung oder Dekodierung fehlschlägt, wird eine Ausnahme geworfen – umschließen Sie Ihre Aufrufe mit try/catch, wenn Sie Fehler elegant handhaben möchten.
- Wenn Sie unerwartete Ergebnisse erhalten, überprüfen Sie Ihre Daten auf zirkuläre Referenzen oder Nicht-UTF8-Zeichen.
- Verwenden Sie
Json::isValid()
, um zu überprüfen, ob ein String gültiges JSON ist, bevor Sie dekodieren.
Änderungsprotokoll
- v3.16.0 - JSON-Wrapper-Hilfsklasse hinzugefügt.
Learn/flight_vs_slim
Flight vs Slim
Was ist Slim?
Slim ist ein PHP-Micro-Framework, das Ihnen hilft, schnell einfache, aber leistungsstarke Webanwendungen und APIs zu schreiben.
Viel Inspiration für einige der v3-Funktionen von Flight stammt tatsächlich von Slim. Das Gruppieren von Routen und das Ausführen von Middleware in einer spezifischen Reihenfolge sind zwei Funktionen, die von Slim inspiriert wurden. Slim v3 wurde mit dem Fokus auf Einfachheit veröffentlicht, aber es gibt gemischte Bewertungen bezüglich v4.
Vorteile im Vergleich zu Flight
- Slim hat eine größere Community von Entwicklern, die im Gegenzug nützliche Module erstellen, um zu vermeiden, das Rad neu zu erfinden.
- Slim folgt vielen Interfaces und Standards, die in der PHP-Community üblich sind, was die Interoperabilität erhöht.
- Slim hat anständige Dokumentation und Tutorials, die verwendet werden können, um das Framework zu lernen (nichts im Vergleich zu Laravel oder Symfony).
- Slim hat verschiedene Ressourcen wie YouTube-Tutorials und Online-Artikel, die verwendet werden können, um das Framework zu lernen.
- Slim lässt Sie beliebige Komponenten verwenden, um die Kern-Routing-Funktionen zu handhaben, da es PSR-7-konform ist.
Nachteile im Vergleich zu Flight
- Überraschenderweise ist Slim nicht so schnell, wie man für ein Micro-Framework denken würde. Sehen Sie sich die TechEmpower-Benchmarks für weitere Informationen an.
- Flight ist auf Entwickler ausgerichtet, die eine leichte, schnelle und einfach zu bedienende Webanwendung erstellen möchten.
- Flight hat keine Abhängigkeiten, während Slim einige Abhängigkeiten hat, die Sie installieren müssen.
- Flight ist auf Einfachheit und Benutzerfreundlichkeit ausgerichtet.
- Eine der Kernfunktionen von Flight ist, dass es sein Bestes tut, um Abwärtskompatibilität zu wahren. Slim v3 zu v4 war eine Breaking Change.
- Flight ist für Entwickler gedacht, die zum ersten Mal in die Welt der Frameworks eintauchen.
- Flight kann auch Enterprise-Level-Anwendungen umsetzen, hat aber nicht so viele Beispiele und Tutorials wie Slim. Es erfordert auch mehr Disziplin vom Entwickler, um Dinge organisiert und gut strukturiert zu halten.
- Flight gibt dem Entwickler mehr Kontrolle über die Anwendung, während Slim hinter den Kulissen etwas Magie einbauen kann.
- Flight hat einen einfachen PdoWrapper, der verwendet werden kann, um mit Ihrer Datenbank zu interagieren. Slim erfordert die Verwendung einer Drittanbieter-Bibliothek.
- Flight hat ein Permissions-Plugin, das verwendet werden kann, um Ihre Anwendung zu sichern. Slim erfordert die Verwendung einer Drittanbieter-Bibliothek.
- Flight hat ein ORM namens active-record, das verwendet werden kann, um mit Ihrer Datenbank zu interagieren. Slim erfordert die Verwendung einer Drittanbieter-Bibliothek.
- Flight hat eine CLI-Anwendung namens runway, die verwendet werden kann, um Ihre Anwendung von der Kommandozeile aus auszuführen. Slim hat das nicht.
Learn/autoloading
Autoloading
Überblick
Autoloading ist ein Konzept in PHP, bei dem Sie ein Verzeichnis oder Verzeichnisse angeben, aus denen Klassen geladen werden. Dies ist viel vorteilhafter als die Verwendung von require
oder include
, um Klassen zu laden. Es ist auch eine Voraussetzung für die Verwendung von Composer-Paketen.
Verständnis
Standardmäßig wird jede Flight
-Klasse automatisch für Sie autogeladen dank Composer. Wenn Sie jedoch Ihre eigenen Klassen autoladen möchten, können Sie die Methode Flight::path()
verwenden, um ein Verzeichnis anzugeben, aus dem Klassen geladen werden.
Die Verwendung eines Autoloaders kann Ihren Code auf erhebliche Weise vereinfachen. Anstatt dass Dateien mit einer Vielzahl von include
- oder require
-Anweisungen am Anfang beginnen, um alle in dieser Datei verwendeten Klassen zu erfassen, können Sie stattdessen Ihre Klassen dynamisch aufrufen, und sie werden automatisch eingeschlossen.
Grundlegende Verwendung
Nehmen wir an, wir haben eine Verzeichnisstruktur wie die folgende:
# Beispielpfad
/home/user/project/my-flight-project/
├── app
│ ├── cache
│ ├── config
│ ├── controllers - enthält die Controller für dieses Projekt
│ ├── translations
│ ├── UTILS - enthält Klassen nur für diese Anwendung (dies ist absichtlich in Großbuchstaben für ein späteres Beispiel)
│ └── views
└── public
└── css
└── js
└── index.php
Sie haben vielleicht bemerkt, dass dies die gleiche Dateistruktur wie diese Dokumentationsseite ist.
Sie können jedes Verzeichnis zum Laden wie folgt angeben:
/**
* public/index.php
*/
// Einen Pfad zum Autoloader hinzufügen
Flight::path(__DIR__.'/../app/controllers/');
Flight::path(__DIR__.'/../app/utils/');
/**
* app/controllers/MyController.php
*/
// Kein Namespacing erforderlich
// Alle autogeladenen Klassen sollten im Pascal Case sein (jedes Wort großgeschrieben, keine Leerzeichen)
class MyController {
public function index() {
// etwas tun
}
}
Namespaces
Wenn Sie Namespaces haben, wird es tatsächlich sehr einfach, dies zu implementieren. Sie sollten die Methode Flight::path()
verwenden, um das Stammverzeichnis (nicht das Dokumentenroot oder das public/
-Verzeichnis) Ihrer Anwendung anzugeben.
/**
* public/index.php
*/
// Einen Pfad zum Autoloader hinzufügen
Flight::path(__DIR__.'/../');
Nun könnte Ihr Controller so aussehen. Schauen Sie sich das Beispiel unten an, aber achten Sie auf die Kommentare für wichtige Informationen.
/**
* app/controllers/MyController.php
*/
// Namespaces sind erforderlich
// Namespaces sind identisch mit der Verzeichnisstruktur
// Namespaces müssen die gleiche Schreibweise wie die Verzeichnisstruktur haben
// Namespaces und Verzeichnisse dürfen keine Unterstriche enthalten (es sei denn, Loader::setV2ClassLoading(false) ist gesetzt)
namespace app\controllers;
// Alle autogeladenen Klassen sollten im Pascal Case sein (jedes Wort großgeschrieben, keine Leerzeichen)
// Ab 3.7.2 können Sie Pascal_Snake_Case für Ihre Klassennamen verwenden, indem Sie Loader::setV2ClassLoading(false) ausführen;
class MyController {
public function index() {
// etwas tun
}
}
Und wenn Sie eine Klasse in Ihrem utils-Verzeichnis autoladen möchten, würden Sie im Wesentlichen dasselbe tun:
/**
* app/UTILS/ArrayHelperUtil.php
*/
// Namespace muss der Verzeichnisstruktur und Schreibweise entsprechen (beachten Sie, dass das UTILS-Verzeichnis in Großbuchstaben ist
// wie im Dateibaum oben)
namespace app\UTILS;
class ArrayHelperUtil {
public function changeArrayCase(array $array) {
// etwas tun
}
}
Unterstriche in Klassennamen
Ab 3.7.2 können Sie Pascal_Snake_Case für Ihre Klassennamen verwenden, indem Sie Loader::setV2ClassLoading(false);
ausführen.
Dies ermöglicht die Verwendung von Unterstrichen in Ihren Klassennamen.
Dies wird nicht empfohlen, ist aber für diejenigen verfügbar, die es benötigen.
use flight\core\Loader;
/**
* public/index.php
*/
// Einen Pfad zum Autoloader hinzufügen
Flight::path(__DIR__.'/../app/controllers/');
Flight::path(__DIR__.'/../app/utils/');
Loader::setV2ClassLoading(false);
/**
* app/controllers/My_Controller.php
*/
// Kein Namespacing erforderlich
class My_Controller {
public function index() {
// etwas tun
}
}
Siehe auch
- Routing - Wie man Routen zu Controllern abbildet und Views rendert.
- Warum ein Framework? - Das Verständnis der Vorteile der Verwendung eines Frameworks wie Flight.
Fehlerbehebung
- Wenn Sie nicht herausfinden können, warum Ihre namespaced Klassen nicht gefunden werden, erinnern Sie sich daran,
Flight::path()
zum Stammverzeichnis in Ihrem Projekt zu verwenden, nicht zu Ihremapp/
- odersrc/
-Verzeichnis oder Äquivalent.
Klasse nicht gefunden (Autoloading funktioniert nicht)
Es könnte ein paar Gründe dafür geben, dass dies nicht passiert. Unten sind einige Beispiele, aber stellen Sie sicher, dass Sie auch den Abschnitt autoloading überprüfen.
Falscher Dateiname
Der häufigste Grund ist, dass der Klassenname nicht zum Dateinamen passt.
Wenn Sie eine Klasse namens MyClass
haben, sollte die Datei MyClass.php
heißen. Wenn Sie eine Klasse namens MyClass
haben und die Datei myclass.php
heißt,
kann der Autoloader sie nicht finden.
Falscher Namespace
Wenn Sie Namespaces verwenden, sollte der Namespace der Verzeichnisstruktur entsprechen.
// ...code...
// wenn Ihr MyController im app/controllers-Verzeichnis ist und namespaced
// das wird nicht funktionieren.
Flight::route('/hello', 'MyController->hello');
// Sie müssen eine dieser Optionen wählen
Flight::route('/hello', 'app\controllers\MyController->hello');
// oder wenn Sie oben eine use-Anweisung haben
use app\controllers\MyController;
Flight::route('/hello', [ MyController::class, 'hello' ]);
// kann auch so geschrieben werden
Flight::route('/hello', MyController::class.'->hello');
// auch...
Flight::route('/hello', [ 'app\controllers\MyController', 'hello' ]);
path()
nicht definiert
In der Skeleton-App wird dies in der config.php
-Datei definiert, aber damit Ihre Klassen gefunden werden, müssen Sie sicherstellen, dass die path()
-Methode definiert ist (wahrscheinlich zum Stammverzeichnis Ihres Verzeichnisses), bevor Sie sie verwenden.
// Einen Pfad zum Autoloader hinzufügen
Flight::path(__DIR__.'/../');
Änderungsprotokoll
- v3.7.2 - Sie können Pascal_Snake_Case für Ihre Klassennamen verwenden, indem Sie
Loader::setV2ClassLoading(false);
ausführen - v2.0 - Autoload-Funktionalität hinzugefügt.
Learn/uploaded_file
Uploaded File Handler
Übersicht
Die UploadedFile
-Klasse in Flight erleichtert es, Datei-Uploads in Ihrer Anwendung sicher und einfach zu handhaben. Sie umschließt die Details des PHP-Datei-Upload-Prozesses und bietet Ihnen eine einfache, objektorientierte Möglichkeit, auf Dateiinformationen zuzugreifen und hochgeladene Dateien zu verschieben.
Verständnis
Wenn ein Benutzer eine Datei über ein Formular hochlädt, speichert PHP Informationen über die Datei in der $_FILES
-Superglobal. In Flight interagieren Sie selten direkt mit $_FILES
. Stattdessen stellt das Request
-Objekt von Flight (erreichbar über Flight::request()
) eine Methode getUploadedFiles()
bereit, die ein Array von UploadedFile
-Objekten zurückgibt, was den Datei-Handling viel bequemer und robuster macht.
Die UploadedFile
-Klasse bietet Methoden zum:
- Abrufen des ursprünglichen Dateinamens, MIME-Typs, der Größe und des temporären Speicherorts
- Überprüfen auf Upload-Fehler
- Verschieben der hochgeladenen Datei an einen permanenten Speicherort
Diese Klasse hilft Ihnen, gängige Fallstricke bei Datei-Uploads zu vermeiden, wie z. B. das Handhaben von Fehlern oder das sichere Verschieben von Dateien.
Grundlegende Verwendung
Zugriff auf hochgeladene Dateien aus einer Anfrage
Der empfohlene Weg, um auf hochgeladene Dateien zuzugreifen, ist über das Request-Objekt:
Flight::route('POST /upload', function() {
// Für ein Formularfeld namens <input type="file" name="myFile">
$uploadedFiles = Flight::request()->getUploadedFiles();
$file = $uploadedFiles['myFile'];
// Nun können Sie die UploadedFile-Methoden verwenden
if ($file->getError() === UPLOAD_ERR_OK) {
$file->moveTo('/path/to/uploads/' . $file->getClientFilename());
echo "Datei erfolgreich hochgeladen!";
} else {
echo "Upload fehlgeschlagen: " . $file->getError();
}
});
Handhabung mehrerer Datei-Uploads
Wenn Ihr Formular name="myFiles[]"
für mehrere Uploads verwendet, erhalten Sie ein Array von UploadedFile
-Objekten:
Flight::route('POST /upload', function() {
// Für ein Formularfeld namens <input type="file" name="myFiles[]">
$uploadedFiles = Flight::request()->getUploadedFiles();
foreach ($uploadedFiles['myFiles'] as $file) {
if ($file->getError() === UPLOAD_ERR_OK) {
$file->moveTo('/path/to/uploads/' . $file->getClientFilename());
echo "Hochgeladen: " . $file->getClientFilename() . "<br>";
} else {
echo "Upload fehlgeschlagen: " . $file->getClientFilename() . "<br>";
}
}
});
Manuelles Erstellen einer UploadedFile-Instanz
Normalerweise erstellen Sie keine UploadedFile
manuell, aber Sie können es tun, wenn es benötigt wird:
use flight\net\UploadedFile;
$file = new UploadedFile(
$_FILES['myfile']['name'],
$_FILES['myfile']['type'],
$_FILES['myfile']['size'],
$_FILES['myfile']['tmp_name'],
$_FILES['myfile']['error']
);
Zugriff auf Dateiinformationen
Sie können leicht Details über die hochgeladene Datei abrufen:
echo $file->getClientFilename(); // Ursprünglicher Dateiname vom Computer des Benutzers
echo $file->getClientMediaType(); // MIME-Typ (z. B. image/png)
echo $file->getSize(); // Dateigröße in Bytes
echo $file->getTempName(); // Temporärer Dateipfad auf dem Server
echo $file->getError(); // Upload-Fehlercode (0 bedeutet kein Fehler)
Verschieben der hochgeladenen Datei
Nach der Validierung der Datei verschieben Sie sie an einen permanenten Speicherort:
try {
$file->moveTo('/path/to/uploads/' . $file->getClientFilename());
echo "Datei erfolgreich hochgeladen!";
} catch (Exception $e) {
echo "Upload fehlgeschlagen: " . $e->getMessage();
}
Die moveTo()
-Methode wirft eine Exception, wenn etwas schiefgeht (wie ein Upload-Fehler oder ein Berechtigungsproblem).
Handhabung von Upload-Fehlern
Wenn es während des Uploads ein Problem gab, können Sie eine lesbare Fehlermeldung abrufen:
if ($file->getError() !== UPLOAD_ERR_OK) {
// Sie können den Fehlercode verwenden oder die Exception von moveTo() abfangen
echo "Es gab einen Fehler beim Hochladen der Datei.";
}
Siehe auch
- Requests - Erfahren Sie, wie Sie auf hochgeladene Dateien aus HTTP-Anfragen zugreifen und sehen Sie weitere Beispiele für Datei-Uploads.
- Configuration - Wie Sie Upload-Limits und Verzeichnisse in PHP konfigurieren.
- Extending - Wie Sie die Kernklassen von Flight anpassen oder erweitern.
Fehlerbehebung
- Überprüfen Sie immer
$file->getError()
, bevor Sie die Datei verschieben. - Stellen Sie sicher, dass Ihr Upload-Verzeichnis vom Webserver beschreibbar ist.
- Wenn
moveTo()
fehlschlägt, überprüfen Sie die Exception-Nachricht auf Details. - Die PHP-Einstellungen
upload_max_filesize
undpost_max_size
können Datei-Uploads einschränken. - Für mehrere Datei-Uploads iterieren Sie immer durch das Array der
UploadedFile
-Objekte.
Changelog
- v3.12.0 -
UploadedFile
-Klasse zum Request-Objekt hinzugefügt für einfacheres Datei-Handling.
Guides/unit_testing
Unit Testing in Flight PHP mit PHPUnit
Dieser Leitfaden führt Unit Testing in Flight PHP mit PHPUnit ein, gerichtet an Anfänger, die verstehen möchten, warum Unit Testing wichtig ist und wie man es praktisch anwendet. Wir konzentrieren uns auf das Testen von Verhalten – das Sicherstellen, dass Ihre Anwendung das tut, was Sie erwarten, wie das Senden einer E-Mail oder das Speichern eines Datensatzes – anstelle von trivialen Berechnungen. Wir beginnen mit einem einfachen Route Handler und gehen zu einem komplexeren Controller über, unter Einbeziehung von Dependency Injection (DI) und dem Mocken von Drittanbieter-Services.
Warum Unit Tests?
Unit Testing stellt sicher, dass Ihr Code wie erwartet verhält, und fängt Bugs ab, bevor sie in die Produktion gelangen. Es ist besonders wertvoll in Flight, wo leichte Routing und Flexibilität zu komplexen Interaktionen führen können. Für Solo-Entwickler oder Teams dienen Unit Tests als Sicherheitsnetz, dokumentieren erwartetes Verhalten und verhindern Regressionen, wenn Sie später Code erneut betrachten. Sie verbessern auch das Design: Schwierig zu testender Code signalisiert oft übermäßig komplexe oder eng gekoppelte Klassen.
Im Gegensatz zu simplistischen Beispielen (z. B. Testen von x * y = z
) konzentrieren wir uns auf reale Verhaltensweisen, wie die Validierung von Eingaben, das Speichern von Daten oder das Auslösen von Aktionen wie E-Mails. Unser Ziel ist es, Testing zugänglich und sinnvoll zu machen.
Allgemeine Leitprinzipien
- Verhalten testen, nicht Implementierung: Konzentrieren Sie sich auf Ergebnisse (z. B. „E-Mail gesendet“ oder „Datensatz gespeichert“) anstelle interner Details. Das macht Tests robust gegenüber Refactoring.
- Vermeiden Sie
Flight::
: Flights statische Methoden sind bequem, machen Testing aber schwierig. Gewöhnen Sie sich daran, die$app
-Variable aus$app = Flight::app();
zu verwenden.$app
hat alle Methoden, dieFlight::
hat. Sie können immer noch$app->route()
oder$this->app->json()
in Ihrem Controller usw. verwenden. Verwenden Sie auch den echten Flight-Router mit$router = $app->router()
und dann können Sie$router->get()
,$router->post()
,$router->group()
usw. nutzen. Siehe Routing. - Halten Sie Tests schnell: Schnelle Tests fördern häufige Ausführung. Vermeiden Sie langsame Operationen wie Datenbankaufrufe in Unit Tests. Wenn Sie einen langsamen Test haben, ist das ein Zeichen, dass Sie einen Integration Test schreiben, keinen Unit Test. Integration Tests beinhalten echte Datenbanken, echte HTTP-Aufrufe, echtes E-Mail-Versenden usw. Sie haben ihren Platz, sind aber langsam und können unzuverlässig sein, was bedeutet, dass sie manchmal aus unbekannten Gründen fehlschlagen.
- Verwenden Sie beschreibende Namen: Testnamen sollten das getestete Verhalten klar beschreiben. Das verbessert Lesbarkeit und Wartbarkeit.
- Vermeiden Sie Globals wie die Pest: Minimieren Sie die Nutzung von
$app->set()
und$app->get()
, da sie wie globaler Zustand wirken und in jedem Test Mocks erfordern. Bevorzugen Sie DI oder einen DI-Container (siehe Dependency Injection Container). Sogar die Verwendung der Methode$app->map()
ist technisch „global“ und sollte zugunsten von DI vermieden werden. Verwenden Sie eine Session-Bibliothek wie flightphp/session, damit Sie das Session-Objekt in Ihren Tests mocken können. Rufen Sie$_SESSION
nicht direkt in Ihrem Code auf, da das eine globale Variable in Ihren Code injiziert und das Testing erschwert. - Verwenden Sie Dependency Injection: Injizieren Sie Abhängigkeiten (z. B.
PDO
, Mailer) in Controller, um Logik zu isolieren und das Mocken zu vereinfachen. Wenn eine Klasse zu viele Abhängigkeiten hat, überlegen Sie, sie in kleinere Klassen umzustrukturieren, die jeweils eine einzige Verantwortung haben und den SOLID-Prinzipien folgen. - Mocken Sie Drittanbieter-Services: Mocken Sie Datenbanken, HTTP-Clients (cURL) oder E-Mail-Services, um externe Aufrufe zu vermeiden. Testen Sie eine oder zwei Ebenen tief, lassen Sie aber Ihre Kernlogik laufen. Zum Beispiel, wenn Ihre App eine SMS sendet, wollen Sie NICHT wirklich eine SMS bei jedem Testlauf senden, da die Kosten steigen (und es langsamer wird). Stattdessen mocken Sie den SMS-Service und überprüfen nur, ob Ihr Code den SMS-Service mit den richtigen Parametern aufgerufen hat.
- Streben Sie hohe Abdeckung an, nicht Perfektion: 100% Zeilenabdeckung ist gut, bedeutet aber nicht, dass alles in Ihrem Code so getestet wird, wie es sein sollte (recherchieren Sie Branch/Path Coverage in PHPUnit). Priorisieren Sie kritische Verhaltensweisen (z. B. Benutzerregistrierung, API-Antworten und das Erfassen fehlgeschlagener Antworten).
- Verwenden Sie Controller für Routes: In Ihren Route-Definitionen verwenden Sie Controller, keine Closures. Die
flight\Engine $app
wird standardmäßig über den Konstruktor in jeden Controller injiziert. In Tests verwenden Sie$app = new Flight\Engine()
, um Flight innerhalb eines Tests zu instanziieren, injizieren Sie es in Ihren Controller und rufen Methoden direkt auf (z. B.$controller->register()
). Siehe Extending Flight und Routing. - Wählen Sie einen Mocking-Stil und halten Sie sich daran: PHPUnit unterstützt mehrere Mocking-Stile (z. B. prophecy, eingebaute Mocks), oder Sie können anonyme Klassen verwenden, die eigene Vorteile haben wie Code-Vervollständigung, Brechen, wenn Sie die Methodendefinition ändern usw. Seien Sie einfach konsistent in Ihren Tests. Siehe PHPUnit Mock Objects.
- Verwenden Sie
protected
Sichtbarkeit für Methoden/Eigenschaften, die Sie in Subklassen testen möchten: Das erlaubt es, sie in Test-Subklassen zu überschreiben, ohne sie public zu machen, was besonders nützlich für anonyme Klassen-Mocks ist.
Einrichten von PHPUnit
Richten Sie zuerst PHPUnit in Ihrem Flight PHP-Projekt mit Composer ein, für einfaches Testing. Siehe den PHPUnit Getting Started Guide für mehr Details.
-
In Ihrem Projektverzeichnis ausführen:
composer require --dev phpunit/phpunit
Das installiert die neueste PHPUnit als Entwicklungsabhängigkeit.
-
Erstellen Sie ein
tests
-Verzeichnis in der Wurzel Ihres Projekts für Testdateien. -
Fügen Sie ein Test-Skript zu
composer.json
für Bequemlichkeit hinzu:// andere composer.json-Inhalte "scripts": { "test": "phpunit --configuration phpunit.xml" }
-
Erstellen Sie eine
phpunit.xml
-Datei in der Wurzel:<?xml version="1.0" encoding="UTF-8"?> <phpunit bootstrap="vendor/autoload.php"> <testsuites> <testsuite name="Flight Tests"> <directory>tests</directory> </testsuite> </testsuites> </phpunit>
Nun können Sie, wenn Ihre Tests aufgebaut sind, composer test
ausführen, um Tests auszuführen.
Testen eines einfachen Route Handlers
Lassen Sie uns mit einer grundlegenden Route beginnen, die die E-Mail-Eingabe eines Benutzers validiert. Wir testen ihr Verhalten: Rückgabe einer Erfolgsnachricht für gültige E-Mails und einer Fehlermeldung für ungültige. Für die E-Mail-Validierung verwenden wir filter_var
.
// index.php
$app->route('POST /register', [ UserController::class, 'register' ]);
// UserController.php
class UserController {
protected $app;
public function __construct(flight\Engine $app) {
$this->app = $app;
}
public function register() {
$email = $this->app->request()->data->email;
$responseArray = [];
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
$responseArray = ['status' => 'error', 'message' => 'Ungültige E-Mail'];
} else {
$responseArray = ['status' => 'success', 'message' => 'Gültige E-Mail'];
}
$this->app->json($responseArray);
}
}
Um das zu testen, erstellen Sie eine Testdatei. Siehe Unit Testing and SOLID Principles für mehr über die Strukturierung von Tests:
// tests/UserControllerTest.php
use PHPUnit\Framework\TestCase;
use Flight;
use flight\Engine;
// Kommentar: Testklasse für UserController
class UserControllerTest extends TestCase {
public function testValidEmailReturnsSuccess() {
$app = new Engine();
$request = $app->request();
$request->data->email = 'test@example.com'; // Simulate POST data
$UserController = new UserController($app);
$UserController->register($request->data->email);
$response = $app->response()->getBody();
$output = json_decode($response, true);
$this->assertEquals('success', $output['status']);
$this->assertEquals('Gültige E-Mail', $output['message']);
}
public function testInvalidEmailReturnsError() {
$app = new Engine();
$request = $app->request();
$request->data->email = 'invalid-email'; // Simulate POST data
$UserController = new UserController($app);
$UserController->register($request->data->email);
$response = $app->response()->getBody();
$output = json_decode($response, true);
$this->assertEquals('error', $output['status']);
$this->assertEquals('Ungültige E-Mail', $output['message']);
}
}
Wichtige Punkte:
- Wir simulieren POST-Daten mit der Request-Klasse. Verwenden Sie keine Globals wie
$_POST
,$_GET
usw., da das Testing komplizierter macht (Sie müssen diese Werte immer zurücksetzen, oder andere Tests könnten fehlschlagen). - Alle Controller haben standardmäßig die
flight\Engine
-Instanz injiziert, auch ohne einen DIC-Container einzurichten. Das macht es viel einfacher, Controller direkt zu testen. - Es gibt keine
Flight::
-Nutzung, was den Code einfacher testbar macht. - Tests überprüfen Verhalten: Korrekter Status und Nachricht für gültige/ungültige E-Mails.
Führen Sie composer test
aus, um zu überprüfen, ob die Route wie erwartet verhält. Für mehr über Requests und Responses in Flight siehe die relevanten Docs.
Verwendung von Dependency Injection für testbare Controller
Für komplexere Szenarien verwenden Sie Dependency Injection (DI), um Controller testbar zu machen. Vermeiden Sie Flights Globals (z. B. Flight::set()
, Flight::map()
, Flight::register()
), da sie wie globaler Zustand wirken und Mocks für jeden Test erfordern. Stattdessen verwenden Sie Flights DI-Container, DICE, PHP-DI oder manuelles DI.
Lassen Sie uns flight\database\PdoWrapper
anstelle von raw PDO verwenden. Dieser Wrapper ist viel einfacher zu mocken und für Unit Tests!
Hier ist ein Controller, der einen Benutzer in eine Datenbank speichert und eine Willkommens-E-Mail sendet:
use flight\database\PdoWrapper;
// Kommentar: Controller mit DI für Datenbank und Mailer
class UserController {
protected $app;
protected $db;
protected $mailer;
public function __construct(Engine $app, PdoWrapper $db, MailerInterface $mailer) {
$this->app = $app;
$this->db = $db;
$this->mailer = $mailer;
}
public function register() {
$email = $this->app->request()->data->email;
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
// adding the return here helps unit testing to stop execution
return $this->app->jsonHalt(['status' => 'error', 'message' => 'Ungültige E-Mail']);
}
$this->db->runQuery('INSERT INTO users (email) VALUES (?)', [$email]);
$this->mailer->sendWelcome($email);
return $this->app->json(['status' => 'success', 'message' => 'Benutzer registriert']);
}
}
Wichtige Punkte:
- Der Controller hängt von einer
PdoWrapper
-Instanz und einerMailerInterface
ab (ein fingierter Drittanbieter-E-Mail-Service). - Abhängigkeiten werden über den Konstruktor injiziert, Globals werden vermieden.
Testen des Controllers mit Mocks
Nun testen wir das Verhalten des UserController
: Validierung von E-Mails, Speichern in der Datenbank und Senden von E-Mails. Wir mocken die Datenbank und den Mailer, um den Controller zu isolieren.
// tests/UserControllerDICTest.php
use PHPUnit\Framework\TestCase;
// Kommentar: Testklasse mit DI und Mocks
class UserControllerDICTest extends TestCase {
public function testValidEmailSavesAndSendsEmail() {
// Sometimes mixing mocking styles is necessary
// Here we use PHPUnit's built-in mock for PDOStatement
$statementMock = $this->createMock(PDOStatement::class);
$statementMock->method('execute')->willReturn(true);
// Using an anonymous class to mock PdoWrapper
$mockDb = new class($statementMock) extends PdoWrapper {
protected $statementMock;
public function __construct($statementMock) {
$this->statementMock = $statementMock;
}
// When we mock it this way, we are not really making a database call.
// We can further setup this to alter the PDOStatement mock to simulate failures, etc.
public function runQuery(string $sql, array $params = []): PDOStatement {
return $this->statementMock;
}
};
$mockMailer = new class implements MailerInterface {
public $sentEmail = null;
public function sendWelcome($email): bool {
$this->sentEmail = $email;
return true;
}
};
$app = new Engine();
$app->request()->data->email = 'test@example.com';
$controller = new UserControllerDIC($app, $mockDb, $mockMailer);
$controller->register();
$response = $app->response()->getBody();
$result = json_decode($response, true);
$this->assertEquals('success', $result['status']);
$this->assertEquals('Benutzer registriert', $result['message']);
$this->assertEquals('test@example.com', $mockMailer->sentEmail);
}
public function testInvalidEmailSkipsSaveAndEmail() {
$mockDb = new class() extends PdoWrapper {
// An empty constructor bypasses the parent constructor
public function __construct() {}
public function runQuery(string $sql, array $params = []): PDOStatement {
throw new Exception('Sollte nicht aufgerufen werden');
}
};
$mockMailer = new class implements MailerInterface {
public $sentEmail = null;
public function sendWelcome($email): bool {
throw new Exception('Sollte nicht aufgerufen werden');
}
};
$app = new Engine();
$app->request()->data->email = 'invalid-email';
// Need to map jsonHalt to avoid exiting
$app->map('jsonHalt', function($data) use ($app) {
$app->json($data, 400);
});
$controller = new UserControllerDIC($app, $mockDb, $mockMailer);
$controller->register();
$response = $app->response()->getBody();
$result = json_decode($response, true);
$this->assertEquals('error', $result['status']);
$this->assertEquals('Ungültige E-Mail', $result['message']);
}
}
Wichtige Punkte:
- Wir mocken
PdoWrapper
undMailerInterface
, um echte Datenbank- oder E-Mail-Aufrufe zu vermeiden. - Tests überprüfen Verhalten: Gültige E-Mails lösen Datenbank-Inserts und E-Mail-Versand aus; ungültige E-Mails überspringen beides.
- Mocken Sie Drittanbieter-Abhängigkeiten (z. B.
PdoWrapper
,MailerInterface
) und lassen Sie die Logik des Controllers laufen.
Zu viel Mocken
Seien Sie vorsichtig, nicht zu viel von Ihrem Code zu mocken. Lassen Sie mich ein Beispiel unten geben, warum das schlecht sein könnte, mit unserem UserController
. Wir ändern diese Überprüfung in eine Methode namens isEmailValid
(mit filter_var
) und die anderen neuen Ergänzungen in eine separate Methode namens registerUser
.
use flight\database\PdoWrapper;
use flight\Engine;
// UserControllerDICV2.php
class UserControllerDICV2 {
protected $app;
protected $db;
protected $mailer;
public function __construct(Engine $app, PdoWrapper $db, MailerInterface $mailer) {
$this->app = $app;
$this->db = $db;
$this->mailer = $mailer;
}
public function register() {
$email = $this->app->request()->data->email;
if (!$this->isEmailValid($email)) {
// adding the return here helps unit testing to stop execution
return $this->app->jsonHalt(['status' => 'error', 'message' => 'Ungültige E-Mail']);
}
$this->registerUser($email);
$this->app->json(['status' => 'success', 'message' => 'Benutzer registriert']);
}
protected function isEmailValid($email) {
return filter_var($email, FILTER_VALIDATE_EMAIL) !== false;
}
protected function registerUser($email) {
$this->db->runQuery('INSERT INTO users (email) VALUES (?)', [$email]);
$this->mailer->sendWelcome($email);
}
}
Und nun der übermäßig gemockte Unit Test, der eigentlich nichts testet:
use PHPUnit\Framework\TestCase;
// Kommentar: Übermäßig gemockter Test, der nichts überprüft
class UserControllerTest extends TestCase {
public function testValidEmailSavesAndSendsEmail() {
$app = new Engine();
$app->request()->data->email = 'test@example.com';
// we are skipping the extra dependency injection here cause it's "easy"
$controller = new class($app) extends UserControllerDICV2 {
protected $app;
// Bypass the deps in the construct
public function __construct($app) {
$this->app = $app;
}
// We'll just force this to be valid.
protected function isEmailValid($email) {
return true; // Always return true, bypassing real validation
}
// Bypass the actual DB and mailer calls
protected function registerUser($email) {
return false;
}
};
$controller->register();
$response = $app->response()->getBody();
$result = json_decode($response, true);
$this->assertEquals('success', $result['status']);
$this->assertEquals('Benutzer registriert', $result['message']);
}
}
Hurra, wir haben Unit Tests und sie laufen! Aber warte, was, wenn ich tatsächlich die internen Funktionen von isEmailValid
oder registerUser
ändere? Meine Tests laufen immer noch, weil ich alle Funktionalität gemockt habe. Lassen Sie mich zeigen, was ich meine.
// UserControllerDICV2.php
class UserControllerDICV2 {
// ... other methods ...
protected function isEmailValid($email) {
// Changed logic
$validEmail = filter_var($email, FILTER_VALIDATE_EMAIL) !== false;
// Now it should only have a specific domain
$validDomain = strpos($email, '@example.com') !== false;
return $validEmail && $validDomain;
}
}
Wenn ich meine obigen Unit Tests ausführe, laufen sie immer noch! Aber weil ich nicht für Verhalten getestet habe (tatsächlich etwas Code laufen lassen), habe ich potenziell einen Bug kodiert, der in der Produktion auftritt. Der Test sollte für das neue Verhalten angepasst werden und auch für den umgekehrten Fall, wenn das Verhalten nicht das ist, was wir erwarten.
Vollständiges Beispiel
Sie finden ein vollständiges Beispiel eines Flight PHP-Projekts mit Unit Tests auf GitHub: n0nag0n/flight-unit-tests-guide. Für tieferes Verständnis siehe Unit Testing and SOLID Principles.
Häufige Fallstricke
- Über-Mocken: Mocken Sie nicht jede Abhängigkeit; lassen Sie etwas Logik (z. B. Controller-Validierung) laufen, um echtes Verhalten zu testen. Siehe Unit Testing and SOLID Principles.
- Globaler Zustand: Die starke Nutzung globaler PHP-Variablen (z. B.
$_SESSION
,$_COOKIE
) macht Tests spröde. Dasselbe gilt fürFlight::
. Refactoren Sie, um Abhängigkeiten explizit zu übergeben. - Komplexe Einrichtung: Wenn die Testeinrichtung umständlich ist, hat Ihre Klasse möglicherweise zu viele Abhängigkeiten oder Verantwortlichkeiten, die den SOLID-Prinzipien verletzen.
Skalieren mit Unit Tests
Unit Tests glänzen in größeren Projekten oder beim Wiederbesuch von Code nach Monaten. Sie dokumentieren Verhalten und fangen Regressionen ab, sparen Ihnen das erneute Lernen Ihrer App. Für Solo-Entwickler testen Sie kritische Pfade (z. B. Benutzeranmeldung, Zahlungsabwicklung). Für Teams stellen Tests konsistentes Verhalten über Beiträge hinweg sicher. Siehe Why Frameworks? für mehr über die Vorteile von Frameworks und Tests.
Beitragen Sie Ihre eigenen Testing-Tipps zum Flight PHP-Dokumentations-Repository!
Geschrieben von n0nag0n 2025
Guides/blog
Erstellen eines einfachen Blogs mit Flight PHP
Diese Anleitung führt Sie durch die Erstellung eines einfachen Blogs mit dem Flight PHP-Framework. Sie richten ein Projekt ein, definieren Routen, verwalten Beiträge mit JSON und rendern sie mit der Latte-Template-Engine – alles zeigt die Einfachheit und Flexibilität von Flight. Am Ende haben Sie einen funktionalen Blog mit einer Startseite, individuellen Beitragsseiten und einem Erstellungsformular.
Voraussetzungen
- PHP 7.4+: Auf Ihrem System installiert.
- Composer: Für das Abhängigkeitsmanagement.
- Texteditor: Jeder Editor wie VS Code oder PHPStorm.
- Grundkenntnisse in PHP und Webentwicklung.
Schritt 1: Richten Sie Ihr Projekt ein
Beginnen Sie damit, ein neues Projektverzeichnis zu erstellen und Flight über Composer zu installieren.
-
Verzeichnis erstellen:
mkdir flight-blog cd flight-blog
-
Flight installieren:
composer require flightphp/core
-
Ein öffentliches Verzeichnis erstellen: Flight verwendet einen einzigen Einstiegspunkt (
index.php
). Erstellen Sie einenpublic/
-Ordner dafür:mkdir public
-
Basis
index.php
: Erstellen Siepublic/index.php
mit einer einfachen „Hallo Welt“-Route:<?php require '../vendor/autoload.php'; Flight::route('/', function () { echo 'Hallo, Flight!'; }); Flight::start();
-
Den integrierten Server ausführen: Testen Sie Ihre Einrichtung mit dem Entwicklungsserver von PHP:
php -S localhost:8000 -t public/
Besuchen Sie
http://localhost:8000
, um „Hallo, Flight!“ zu sehen.
Schritt 2: Organisieren Sie Ihre Projektstruktur
Für eine saubere Einrichtung strukturieren Sie Ihr Projekt wie folgt:
flight-blog/
├── app/
│ ├── config/
│ └── views/
├── data/
├── public/
│ └── index.php
├── vendor/
└── composer.json
app/config/
: Konfigurationsdateien (z. B. Ereignisse, Routen).app/views/
: Vorlagen zum Rendern von Seiten.data/
: JSON-Datei zum Speichern von Blog-Beiträgen.public/
: Web-Stamm mitindex.php
.
Schritt 3: Installieren und Konfigurieren von Latte
Latte ist eine leichtgewichtige Template-Engine, die gut mit Flight integriert.
-
Latte installieren:
composer require latte/latte
-
Latte in Flight konfigurieren: Aktualisieren Sie
public/index.php
, um Latte als Ansicht-Engine zu registrieren:<?php require '../vendor/autoload.php'; use Latte\Engine; Flight::register('view', Engine::class, [], function ($latte) { $latte->setTempDirectory(__DIR__ . '/../cache/'); $latte->setLoader(new \Latte\Loaders\FileLoader(__DIR__ . '/../app/views/')); }); Flight::route('/', function () { Flight::view()->render('home.latte', ['title' => 'Mein Blog']); }); Flight::start();
-
Eine Layout-Vorlage erstellen: In
app/views/layout.latte
:<!DOCTYPE html> <html> <head> <title>{$title}</title> </head> <body> <header> <h1>Mein Blog</h1> <nav> <a href="/">Startseite</a> | <a href="/create">Beitrag erstellen</a> </nav> </header> <main> {block content}{/block} </main> <footer> <p>© {date('Y')} Flight Blog</p> </footer> </body> </html>
-
Eine Startvorlage erstellen: In
app/views/home.latte
:{extends 'layout.latte'} {block content} <h2>{$title}</h2> <ul> {foreach $posts as $post} <li><a href="/post/{$post['slug']}">{$post['title']}</a></li> {/foreach} </ul> {/block}
Starten Sie den Server neu, wenn Sie ihn verlassen haben und besuchen Sie
http://localhost:8000
, um die gerenderte Seite zu sehen. -
Eine Datendatei erstellen:
Verwenden Sie eine JSON-Datei, um eine Datenbank zur Vereinfachung zu simulieren.
In data/posts.json
:
[
{
"slug": "first-post",
"title": "Mein erster Beitrag",
"content": "Dies ist mein allererster Blogbeitrag mit Flight PHP!"
}
]
Schritt 4: Routen definieren
Trennen Sie Ihre Routen in eine Konfigurationsdatei für eine bessere Organisation.
- Erstellen Sie
routes.php
: Inapp/config/routes.php
:<?php Flight::route('/', function () { Flight::view()->render('home.latte', ['title' => 'Mein Blog']); });
Flight::route('/post/@slug', function ($slug) { Flight::view()->render('post.latte', ['title' => 'Beitrag: ' . $slug, 'slug' => $slug]); });
Flight::route('GET /create', function () { Flight::view()->render('create.latte', ['title' => 'Beitrag erstellen']); });
2. **Aktualisieren Sie `index.php`**:
Integrieren Sie die Routen-Datei:
```php
<?php
require '../vendor/autoload.php';
use Latte\Engine;
Flight::register('view', Engine::class, [], function ($latte) {
$latte->setTempDirectory(__DIR__ . '/../cache/');
$latte->setLoader(new \Latte\Loaders\FileLoader(__DIR__ . '/../app/views/'));
});
require '../app/config/routes.php';
Flight::start();
Schritt 5: Blog-Beiträge speichern und abrufen
Fügen Sie die Methoden zum Laden und Speichern von Beiträgen hinzu.
-
Eine Posts-Methode hinzufügen: In
index.php
, fügen Sie eine Methode hinzu, um Beiträge zu laden:Flight::map('posts', function () { $file = __DIR__ . '/../data/posts.json'; return json_decode(file_get_contents($file), true); });
-
Routen aktualisieren: Ändern Sie
app/config/routes.php
, um Beiträge zu verwenden:<?php Flight::route('/', function () { $posts = Flight::posts(); Flight::view()->render('home.latte', [ 'title' => 'Mein Blog', 'posts' => $posts ]); });
Flight::route('/post/@slug', function ($slug) { $posts = Flight::posts(); $post = array_filter($posts, fn($p) => $p['slug'] === $slug); $post = reset($post) ?: null; if (!$post) { Flight::notFound(); return; } Flight::view()->render('post.latte', [ 'title' => $post['title'], 'post' => $post ]); });
Flight::route('GET /create', function () { Flight::view()->render('create.latte', ['title' => 'Beitrag erstellen']); });
## Schritt 6: Vorlagen erstellen
Aktualisieren Sie Ihre Vorlagen, um Beiträge anzuzeigen.
1. **Beitragsseite (`app/views/post.latte`)**:
```html
{extends 'layout.latte'}
{block content}
<h2>{$post['title']}</h2>
<div class="post-content">
<p>{$post['content']}</p>
</div>
{/block}
Schritt 7: Beitragserstellung hinzufügen
Behandeln Sie die Formularübermittlung zum Hinzufügen neuer Beiträge.
-
Erstellungsformular (
app/views/create.latte
):{extends 'layout.latte'} {block content} <h2>{$title}</h2> <form method="POST" action="/create"> <div class="form-group"> <label for="title">Titel:</label> <input type="text" name="title" id="title" required> </div> <div class="form-group"> <label for="content">Inhalt:</label> <textarea name="content" id="content" required></textarea> </div> <button type="submit">Beitrag speichern</button> </form> {/block}
-
POST-Route hinzufügen: In
app/config/routes.php
:Flight::route('POST /create', function () { $request = Flight::request(); $title = $request->data['title']; $content = $request->data['content']; $slug = strtolower(str_replace(' ', '-', $title)); $posts = Flight::posts(); $posts[] = ['slug' => $slug, 'title' => $title, 'content' => $content]; file_put_contents(__DIR__ . '/../../data/posts.json', json_encode($posts, JSON_PRETTY_PRINT)); Flight::redirect('/'); });
-
Testen Sie es:
- Besuchen Sie
http://localhost:8000/create
. - Reichen Sie einen neuen Beitrag ein (z. B. „Zweiter Beitrag“ mit etwas Inhalt).
- Überprüfen Sie die Startseite, um zu sehen, dass er aufgeführt ist.
- Besuchen Sie
Schritt 8: Mit Fehlerbehandlung verbessern
Überschreiben Sie die notFound
-Methode für ein besseres 404-Erlebnis.
In index.php
:
Flight::map('notFound', function () {
Flight::view()->render('404.latte', ['title' => 'Seite nicht gefunden']);
});
Erstellen Sie app/views/404.latte
:
{extends 'layout.latte'}
{block content}
<h2>404 - {$title}</h2>
<p>Entschuldigung, diese Seite existiert nicht!</p>
{/block}
Nächste Schritte
- Styling hinzufügen: Verwenden Sie CSS in Ihren Vorlagen für ein besseres Aussehen.
- Datenbank: Ersetzen Sie
posts.json
durch eine Datenbank wie SQLite mitPdoWrapper
. - Validierung: Fügen Sie Prüfungen auf doppelte Slugs oder leere Eingaben hinzu.
- Middleware: Implementieren Sie die Authentifizierung für die Erstellung von Beiträgen.
Fazit
Sie haben einen einfachen Blog mit Flight PHP erstellt! Diese Anleitung zeigt die grundlegenden Funktionen wie Routing, das Rendern von Vorlagen mit Latte und die Handhabung von Formularübermittlungen – und das alles, während es leichtgewichtig bleibt. Erkunden Sie die Dokumentation von Flight für weitere fortgeschrittene Funktionen, um Ihren Blog weiter auszubauen!
License
Die MIT-Lizenz (MIT)
Urheberrecht © 2024
@mikecao, @n0nag0n
Hiermit wird unentgeltlich jeder Person, die eine Kopie der Software und der zugehörigen Dokumentationsdateien (die "Software") erhält, die Erlaubnis erteilt, uneingeschränkt mit der Software zu handeln, einschließlich und ohne Einschränkung der Rechte zur Nutzung, Veränderung, Zusammenführung, Veröffentlichung, Verbreitung, Unterlizenzierung und/oder Verkauf von Kopien der Software, und Personen, denen die Software überlassen wird, zu gestatten, dies zu tun, unter den folgenden Bedingungen:
Der obige Urheberrechtsvermerk und dieser Genehmigungsvermerk sind in allen Kopien oder wesentlichen Teilen der Software enthalten.
DIE SOFTWARE WIRD "WIE BESEHEN" BEREITGESTELLT, OHNE JEGLICHE GARANTIE, AUSDRÜCKLICH ODER IMPLIZIERT, EINSCHLIEßLICH DER GARANTIE DER MARKTFÄHIGKEIT, DER EIGNUNG FÜR EINEN BESTIMMTEN ZWECK UND DER NICHTVERLETZUNG. IN KEINEM FALL HAFTEN DIE AUTOREN ODER COPYRIGHT-INHABER FÜR ANSPRÜCHE, SCHÄDEN ODER ANDERE HAFTUNGEN, OB IN EINER VERTRAGS- ODER DELIKTSKLAGE, DIE AUS ODER IN VERBINDUNG MIT DER SOFTWARE ODER DER VERWENDUNG ODER ANDEREN GESCHÄFTEN MIT DER SOFTWARE ENTSTEHEN.
About
Flight PHP-Framework
Flight ist ein schnelles, simples, erweiterbares Framework für PHP – entwickelt für Entwickler, die Dinge schnell erledigen wollen, ohne Aufwand. Ob Sie eine klassische Web-App, eine blitzschnelle API oder mit den neuesten KI-gestützten Tools experimentieren, Flights geringer Fußabdruck und geradliniges Design machen es zur perfekten Wahl. Flight ist darauf ausgelegt, schlank zu sein, kann aber auch Anforderungen an eine Enterprise-Architektur erfüllen.
Warum Flight wählen?
- Anfängerfreundlich: Flight ist ein toller Einstiegspunkt für neue PHP-Entwickler. Seine klare Struktur und einfache Syntax helfen Ihnen, Web-Entwicklung zu lernen, ohne in Boilerplate-Code zu versinken.
- Geliebt von Profis: Erfahrene Entwickler lieben Flight für seine Flexibilität und Kontrolle. Sie können von einem kleinen Prototypen zu einer vollwertigen App skalieren, ohne das Framework zu wechseln.
- KI-Freundlich: Flights minimale Overhead und saubere Architektur machen es ideal für die Integration von KI-Tools und APIs. Ob Sie smarte Chatbots, KI-gesteuerte Dashboards bauen oder einfach experimentieren wollen, Flight hält sich zurück, damit Sie sich auf das Wesentliche konzentrieren können. Die skeleton app enthält vorkonfigurierte Anweisungsdateien für die großen KI-Coding-Assistenten! Mehr erfahren über die Nutzung von KI mit Flight
Videoubersicht
Schnellstart
Für eine schnelle, grundlegende Installation installieren Sie es mit Composer:
composer require flightphp/core
Oder laden Sie ein ZIP des Repos hier herunter. Dann haben Sie eine grundlegende index.php
-Datei wie folgt:
<?php
// wenn mit Composer installiert
require 'vendor/autoload.php';
// oder wenn manuell per ZIP-Datei installiert
// require 'flight/Flight.php';
Flight::route('/', function() {
echo 'hello world!';
});
Flight::route('/json', function() {
Flight::json([
'hello' => 'world'
]);
});
Flight::start();
Das war's! Sie haben eine grundlegende Flight-Anwendung. Führen Sie diese Datei mit php -S localhost:8000
aus und besuchen Sie http://localhost:8000
in Ihrem Browser, um die Ausgabe zu sehen.
Skeleton/Boilerplate-App
Es gibt ein Beispiel-App, um Ihr Projekt mit Flight zu starten. Sie hat eine strukturierte Layout, grundlegende Konfigurationen und behandelt Composer-Skripte direkt ab dem Start! Schauen Sie sich flightphp/skeleton für ein fertiges Projekt an, oder besuchen Sie die examples-Seite für Inspiration. Wollen Sie sehen, wie KI passt? Erkunden Sie KI-gestützte Beispiele.
Installation der Skeleton-App
Sehr einfach!
# Erstellen Sie das neue Projekt
composer create-project flightphp/skeleton my-project/
# Gehen Sie in Ihr neues Projektverzeichnis
cd my-project/
# Starten Sie den lokalen Dev-Server, um sofort loszulegen!
composer start
Es erstellt die Projektstruktur, richtet die Dateien ein, und Sie sind bereit!
Hohe Leistung
Flight ist eines der schnellsten PHP-Frameworks da draußen. Sein leichtes Kern bedeutet weniger Overhead und mehr Geschwindigkeit – perfekt für traditionelle Apps und moderne KI-gestützte Projekte. Sie können alle Benchmarks auf TechEmpower sehen.
Sehen Sie sich den Benchmark unten mit einigen anderen beliebten PHP-Frameworks an.
Framework | Plaintext Reqs/sec | JSON Reqs/sec |
---|---|---|
Flight | 190,421 | 182,491 |
Yii | 145,749 | 131,434 |
Fat-Free | 139,238 | 133,952 |
Slim | 89,588 | 87,348 |
Phalcon | 95,911 | 87,675 |
Symfony | 65,053 | 63,237 |
Lumen | 40,572 | 39,700 |
Laravel | 26,657 | 26,901 |
CodeIgniter | 20,628 | 19,901 |
Flight und KI
Neugierig, wie es mit KI umgeht? Entdecken Sie, wie Flight die Arbeit mit Ihrem favorisierten Coding-LLM einfach macht!
Community
Wir sind im Matrix Chat
Und Discord
Beitrag
Es gibt zwei Wege, wie Sie zu Flight beitragen können:
- Tragen Sie zum Kern-Framework bei, indem Sie das core repository besuchen.
- Helfen Sie, die Docs zu verbessern! Diese Dokumentations-Website ist auf Github gehostet. Wenn Sie einen Fehler entdecken oder etwas verbessern wollen, reichen Sie gerne einen Pull-Request ein. Wir lieben Updates und neue Ideen – besonders rund um KI und neue Technologien!
Anforderungen
Flight erfordert PHP 7.4 oder höher.
Hinweis: PHP 7.4 wird unterstützt, weil zum Zeitpunkt der Erstellung (2024) PHP 7.4 die Standardversion für einige LTS-Linux-Distributionen ist. Eine Zwangsumstellung auf PHP >8 würde für diese Benutzer Probleme verursachen. Das Framework unterstützt auch PHP >8.
Lizenz
Flight wird unter der MIT-Lizenz veröffentlicht.
Awesome-plugins/php_cookie
Cookies
overclokk/cookie ist eine einfache Bibliothek zum Verwalten von Cookies in Ihrer App.
Installation
Die Installation ist mit Composer einfach.
composer require overclokk/cookie
Verwendung
Die Verwendung ist so einfach wie das Registrieren einer neuen Methode in der Flight-Klasse.
use Overclokk\Cookie\Cookie;
/*
* Setzen Sie dies in Ihrer Bootstrap- oder public/index.php-Datei
*/
Flight::register('cookie', Cookie::class);
/**
* ExampleController.php
*/
class ExampleController {
public function login() {
// Setze ein Cookie
// Sie möchten, dass dies falsch ist, damit Sie eine neue Instanz erhalten
// verwenden Sie den folgenden Kommentar, wenn Sie eine Autovervollständigung wünschen
/** @var \Overclokk\Cookie\Cookie $cookie */
$cookie = Flight::cookie(false);
$cookie->set(
'stay_logged_in', // Name des Cookies
'1', // der Wert, den Sie setzen möchten
86400, // Anzahl der Sekunden, die das Cookie dauern soll
'/', // Pfad, auf dem das Cookie verfügbar sein wird
'example.com', // Domain, auf der das Cookie verfügbar sein wird
true, // das Cookie wird nur über eine sichere HTTPS-Verbindung übertragen
true // das Cookie ist nur über das HTTP-Protokoll verfügbar
);
// optional, wenn Sie die Standardwerte beibehalten und eine schnelle Möglichkeit haben möchten, ein Cookie lange zu setzen
$cookie->forever('stay_logged_in', '1');
}
public function home() {
// Überprüfen Sie, ob Sie das Cookie haben
if (Flight::cookie()->has('stay_logged_in')) {
// bringe sie z.B. in den Dashboard-Bereich.
Flight::redirect('/dashboard');
}
}
}
Awesome-plugins/php_encryption
PHP-Verschlüsselung
defuse/php-encryption ist eine Bibliothek, die zum Verschlüsseln und Entschlüsseln von Daten verwendet werden kann. Das Einrichten und Starten ist ziemlich einfach, um mit der Verschlüsselung und Entschlüsselung von Daten zu beginnen. Sie haben ein großartiges Tutorial, das dabei hilft, die Grundlagen zur Verwendung der Bibliothek sowie wichtige Sicherheitsaspekte in Bezug auf Verschlüsselung zu erklären.
Installation
Die Installation ist einfach mit Composer.
composer require defuse/php-encryption
Einrichtung
Dann müssen Sie einen Verschlüsselungsschlüssel generieren.
vendor/bin/generate-defuse-key
Das wird einen Schlüssel ausgeben, den Sie sicher aufbewahren müssen. Sie könnten den Schlüssel in Ihrer app/config/config.php
-Datei im Array am Ende der Datei aufbewahren. Auch wenn es nicht der perfekte Ort ist, ist es zumindest etwas.
Verwendung
Nun, da Sie die Bibliothek und einen Verschlüsselungsschlüssel haben, können Sie damit beginnen, Daten zu verschlüsseln und zu entschlüsseln.
use Defuse\Crypto\Crypto;
use Defuse\Crypto\Key;
/*
* In Ihrer Bootstrap- oder public/index.php-Datei festlegen
*/
// Verschlüsselungsmethode
Flight::map('encrypt', function($rohdaten) {
$verschlüsselungsschlüssel = /* $config['encryption_key'] oder ein file_get_contents davon, wo Sie den Schlüssel platziert haben */;
return Crypto::encrypt($rohdaten, Key::loadFromAsciiSafeString($verschlüsselungsschlüssel));
});
// Entschlüsselungsmethode
Flight::map('decrypt', function($verschlüsselte_daten) {
$verschlüsselungsschlüssel = /* $config['encryption_key'] oder ein file_get_contents davon, wo Sie den Schlüssel platziert haben */;
try {
$rohdaten = Crypto::decrypt($verschlüsselte_daten, Key::loadFromAsciiSafeString($verschlüsselungsschlüssel));
} catch (Defuse\Crypto\Exception\WrongKeyOrModifiedCiphertextException $ex) {
// Ein Angriff! Entweder der falsche Schlüssel wurde geladen oder der Geheimtext hat sich seit seiner Erstellung geändert -- entweder in der Datenbank korrupt oder absichtlich von Eve modifiziert, um einen Angriff durchzuführen.
// ... diesen Fall auf eine Art und Weise behandeln, die für Ihre Anwendung geeignet ist ...
}
return $rohdaten;
});
Flight::route('/encrypt', function() {
$verschlüsselte_daten = Flight::encrypt('Das ist ein Geheimnis');
echo $verschlüsselte_daten;
});
Flight::route('/decrypt', function() {
$verschlüsselte_daten = '...'; // Verschlüsselte Daten von irgendwoher erhalten
$entschlüsselte_daten = Flight::decrypt($verschlüsselte_daten);
echo $entschlüsselte_daten;
});
Awesome-plugins/php_file_cache
flightphp/cache
Leichte, einfache und eigenständige PHP-In-Datei-Caching-Klasse, geforkt von Wruczek/PHP-File-Cache
Vorteile
- Leicht, eigenständig und einfach
- Aller Code in einer Datei - keine sinnlosen Treiber.
- Sicher - jede generierte Cache-Datei hat einen PHP-Header mit die, was direkten Zugriff unmöglich macht, selbst wenn jemand den Pfad kennt und Ihr Server nicht richtig konfiguriert ist
- Gut dokumentiert und getestet
- Behandelt Konkurrenz richtig über flock
- Unterstützt PHP 7.4+
- Kostenlos unter einer MIT-Lizenz
Diese Dokumentationsseite verwendet diese Bibliothek, um jede der Seiten zu cachen!
Klicken Sie hier, um den Code anzusehen.
Installation
Installieren Sie über composer:
composer require flightphp/cache
Verwendung
Die Verwendung ist ziemlich unkompliziert. Dies speichert eine Cache-Datei im Cache-Verzeichnis.
use flight\Cache;
$app = Flight::app();
// Sie übergeben das Verzeichnis, in dem der Cache gespeichert wird, an den Konstruktor
$app->register('cache', Cache::class, [ __DIR__ . '/../cache/' ], function(Cache $cache) {
// Dies stellt sicher, dass der Cache nur im Produktionsmodus verwendet wird
// ENVIRONMENT ist eine Konstante, die in Ihrer Bootstrap-Datei oder anderswo in Ihrer App gesetzt wird
$cache->setDevMode(ENVIRONMENT === 'development');
});
Einen Cache-Wert abrufen
Sie verwenden die get()
-Methode, um einen gecachten Wert abzurufen. Wenn Sie eine Bequemlichkeitsmethode wollen, die den Cache erneuert, wenn er abgelaufen ist, können Sie refreshIfExpired()
verwenden.
// Cache-Instanz abrufen
$cache = Flight::cache();
$data = $cache->refreshIfExpired('simple-cache-test', function () {
return date("H:i:s"); // return data to be cached
}, 10); // 10 Sekunden
// oder
$data = $cache->get('simple-cache-test');
if(empty($data)) {
$data = date("H:i:s");
$cache->set('simple-cache-test', $data, 10); // 10 Sekunden
}
Einen Cache-Wert speichern
Sie verwenden die set()
-Methode, um einen Wert im Cache zu speichern.
Flight::cache()->set('simple-cache-test', 'my cached data', 10); // 10 Sekunden
Einen Cache-Wert löschen
Sie verwenden die delete()
-Methode, um einen Wert im Cache zu löschen.
Flight::cache()->delete('simple-cache-test');
Überprüfen, ob ein Cache-Wert existiert
Sie verwenden die exists()
-Methode, um zu überprüfen, ob ein Wert im Cache existiert.
if(Flight::cache()->exists('simple-cache-test')) {
// etwas tun
}
Den Cache leeren
Sie verwenden die flush()
-Methode, um den gesamten Cache zu leeren.
Flight::cache()->flush();
Metadaten mit Cache abrufen
Wenn Sie Timestamps und andere Metadaten zu einem Cache-Eintrag abrufen möchten, stellen Sie sicher, dass Sie true
als korrekten Parameter übergeben.
$data = $cache->refreshIfExpired("simple-cache-meta-test", function () {
echo "Refreshing data!" . PHP_EOL;
return date("H:i:s"); // return data to be cached
}, 10, true); // true = return with metadata
// oder
$data = $cache->get("simple-cache-meta-test", true); // true = return with metadata
/*
Beispiel für einen gecachten Eintrag, der mit Metadaten abgerufen wird:
{
"time":1511667506, <-- save unix timestamp
"expire":10, <-- expire time in seconds
"data":"04:38:26", <-- unserialized data
"permanent":false
}
Mit Metadaten können wir z. B. berechnen, wann der Eintrag gespeichert wurde oder wann er abläuft
Wir können auch auf die Daten selbst mit dem "data"-Schlüssel zugreifen
*/
$expiresin = ($data["time"] + $data["expire"]) - time(); // get unix timestamp when data expires and subtract current timestamp from it
$cacheddate = $data["data"]; // we access the data itself with the "data" key
echo "Latest cache save: $cacheddate, expires in $expiresin seconds";
Dokumentation
Besuchen Sie https://github.com/flightphp/cache, um den Code anzusehen. Stellen Sie sicher, dass Sie den examples-Ordner für zusätzliche Möglichkeiten zur Verwendung des Caches ansehen.
Awesome-plugins/permissions
FlightPHP/Berechtigungen
Dies ist ein Berechtigungsmodul, das in Ihren Projekten verwendet werden kann, wenn Sie mehrere Rollen in Ihrer App haben und jede Rolle eine etwas andere Funktionalität hat. Mit diesem Modul können Sie Berechtigungen für jede Rolle definieren und dann überprüfen, ob der aktuelle Benutzer die Berechtigung hat, auf eine bestimmte Seite zuzugreifen oder eine bestimmte Aktion auszuführen.
Klicken Sie hier für das Repository auf GitHub.
Installation
Führen Sie composer require flightphp/permissions
aus und los geht's!
Verwendung
Zuerst müssen Sie Ihre Berechtigungen einrichten, dann teilen Sie Ihrer App mit, was die Berechtigungen bedeuten. Letztendlich prüfen Sie Ihre Berechtigungen mit $Permissions->has()
, ->can()
oder is()
. has()
und can()
haben die gleiche Funktionalität, sind jedoch unterschiedlich benannt, um Ihren Code leichter lesbar zu machen.
Grundbeispiel
Angenommen, Sie haben in Ihrer Anwendung eine Funktion, die überprüft, ob ein Benutzer angemeldet ist. Sie können ein Berechtigungsobjekt wie folgt erstellen:
// index.php
require 'vendor/autoload.php';
// etwas Code
// dann haben Sie wahrscheinlich etwas, das Ihnen mitteilt, was die aktuelle Rolle der Person ist
// wahrscheinlich haben Sie etwas, bei dem Sie die aktuelle Rolle abrufen
// aus einer Session-Variablen, die dies definiert
// nachdem sich jemand angemeldet hat, andernfalls haben sie die Rolle 'Gast' oder 'Öffentlich'.
$current_role = 'admin';
// Berechtigungen einrichten
$permission = new \flight\Permission($current_role);
$permission->defineRule('eingeloggt', function($current_role) {
return $current_role !== 'gast';
});
// Sie werden dieses Objekt wahrscheinlich in Flight persistieren wollen
Flight::set('berechtigung', $permission);
Dann in einem Controller irgendwo haben Sie möglicherweise so etwas.
<?php
// irgendein Controller
class EinController {
public function eineAktion() {
$permission = Flight::get('berechtigung');
if ($permission->has('eingeloggt')) {
// etwas machen
} else {
// etwas anderes machen
}
}
}
Sie können dies auch verwenden, um zu verfolgen, ob sie Berechtigungen haben, um in Ihrer Anwendung etwas zu tun. Zum Beispiel, wenn Sie eine Möglichkeit haben, dass Benutzer Beiträge in Ihrer Software interagieren können, können Sie überprüfen, ob sie Berechtigungen haben, bestimmte Aktionen auszuführen.
$current_role = 'admin';
// Berechtigungen einrichten
$permission = new \flight\Permission($current_role);
$permission->defineRule('beitrag', function($current_role) {
if($current_role === 'admin') {
$permissions = ['erstellen', 'lesen', 'aktualisieren', 'löschen'];
} else if($current_role === 'editor') {
$permissions = ['erstellen', 'lesen', 'aktualisieren'];
} else if($current_role === 'autor') {
$permissions = ['erstellen', 'lesen'];
} else if($current_role === 'mitwirkender') {
$permissions = ['erstellen'];
} else {
$permissions = [];
}
return $permissions;
});
Flight::set('berechtigung', $permission);
Dann irgendwo in einem Controller...
class BeitragController {
public function erstellen() {
$permission = Flight::get('berechtigung');
if ($permission->can('beitrag.erstellen')) {
// etwas machen
} else {
// etwas anderes machen
}
}
}
Abhängigkeiten injizieren
Sie können Abhängigkeiten in den Closure einfügen, die die Berechtigungen definieren. Dies ist nützlich, wenn Sie eine Art Schalter, ID oder einen anderen Datenpunkt haben, gegen den Sie prüfen möchten. Das Gleiche gilt für Klassen->Methoden-Aufrufe, außer dass Sie die Argumente in der Methode definieren.
Closures
$Permission->defineRule('bestellung', function(string $current_role, MyDependency $MyDependency = null) {
// ... code
});
// in Ihrer Controllerdatei
public function bestellungErstellen() {
$MyDependency = Flight::myDependency();
$permission = Flight::get('berechtigung');
if ($permission->can('bestellung.erstellen', $MyDependency)) {
// etwas machen
} else {
// etwas anderes machen
}
}
Klassen
namespace MeinApp;
class Berechtigungen {
public function bestellung(string $current_role, MyDependency $MyDependency = null) {
// ... code
}
}
Verknüpfung zum Setzen von Berechtigungen mit Klassen
Sie können auch Klassen verwenden, um Ihre Berechtigungen zu definieren. Dies ist nützlich, wenn Sie viele Berechtigungen haben und Ihren Code sauber halten möchten. Sie können etwas Ähnliches wie folgt tun:
<?php
// Startcode
$Berechtigungen = new \flight\Permission($current_role);
$Berechtigungen->defineRule('bestellung', 'MeinApp\Berechtigungen->bestellung');
// myapp/Berechtigungen.php
namespace MeinApp;
class Berechtigungen {
public function bestellung(string $current_role, int $benutzer_id) {
// Annehmen, dass Sie dies im Voraus eingerichtet haben
/** @var \flight\database\PdoWrapper $db */
$db = Flight::db();
$erlaubte_berechtigungen = [ 'lesen' ]; // jeder kann eine Bestellung einsehen
if($current_role === 'manager') {
$erlaubte_berechtigungen[] = 'erstellen'; // Manager können Bestellungen erstellen
}
$ein_anderer_spezieller_schalter_aus_db = $db->fetchField('SELECT ein_anderer_spezieller_schalter FROM einstellungen WHERE id = ?', [ $benutzer_id ]);
if($ein_anderer_spezieller_schalter_aus_db) {
$erlaubte_berechtigungen[] = 'aktualisieren'; // Wenn der Benutzer einen speziellen Schalter hat, kann er Bestellungen aktualisieren
}
if($current_role === 'admin') {
$erlaubte_berechtigungen[] = 'löschen'; // Admins können Bestellungen löschen
}
return $erlaubte_berechtigungen;
}
}
Der interessante Teil ist, dass es auch eine Abkürzung gibt, die Sie verwenden können (die auch zwischengespeichert werden kann!!!), bei der Sie der Berechtigungsklasse einfach sagen, alle Methoden in einer Klasse in Berechtigungen zu kartieren. Also, wenn Sie eine Methode namens bestellung()
und eine Methode namens unternehmen()
haben, werden diese automatisch zugeordnet, sodass Sie einfach $Berechtigungen->has('bestellung.lesen')
oder $Berechtigungen->has('unternehmen.lesen')
ausführen können und es funktioniert. Das Definieren davon ist sehr schwierig, bleiben Sie also bei mir hier. Sie müssen nur dies tun:
Erstellen Sie die Berechtigungsklasse, die Sie zusammenfassen möchten.
class MeineBerechtigungen {
public function bestellung(string $current_role, int $bestellungs_id = 0): array {
// Code zur Bestimmung von Berechtigungen
return $berechtigungen_array;
}
public function unternehmen(string $current_role, int $unternehmen_id): array {
// Code zur Bestimmung von Berechtigungen
return $berechtigungen_array;
}
}
Dann machen Sie die Berechtigungen mit Hilfe dieser Bibliothek auffindbar.
$Berechtigungen = new \flight\Permission($current_role);
$Berechtigungen->defineRulesFromClassMethods(MeineApp\Berechtigungen::class);
Flight::set('berechtigungen', $Berechtigungen);
Rufen Sie schließlich die Berechtigung in Ihrem Code auf, um zu überprüfen, ob der Benutzer berechtigt ist, eine bestimmte Berechtigung auszuführen.
class EinController {
public function bestellungErstellen() {
if(Flight::get('berechtigungen')->can('bestellung.erstellen') === false) {
die('Sie können keine Bestellung erstellen. Entschuldigung!');
}
}
}
Zwischenspeicherung
Um die Zwischenspeicherung zu aktivieren, sehen Sie sich die einfache wruczak/phpfilecache Bibliothek an. Ein Beispiel zur Aktivierung finden Sie unten.
// dieses $app kann Teil Ihres Codes sein oder
// Sie können einfach null übergeben und es wird
// aus Flight::app() im Konstruktor abgerufen
$app = Flight::app();
// Derzeit akzeptiert es dies als Dateipuffer. Andere können in Zukunft leicht hinzugefügt werden.
$Cache = new Wruczek\PhpFileCache\PhpFileCache;
$Berechtigungen = new \flight\Permission($current_role, $app, $Cache);
$Berechtigungen->defineRulesFromClassMethods(MeineApp\Berechtigungen::class, 3600); // 3600 gibt an, wie viele Sekunden diese Zwischenspeicherung gültig ist. Lassen Sie dies weg, um die Zwischenspeicherung nicht zu verwenden
Awesome-plugins/simple_job_queue
Einfache Job-Warteschlange
Die einfache Job-Warteschlange ist eine Bibliothek, die verwendet werden kann, um Jobs asynchron zu verarbeiten. Sie kann mit beanstalkd, MySQL/MariaDB, SQLite und PostgreSQL verwendet werden.
Installation
composer require n0nag0n/simple-job-queue
Verwendung
Damit dies funktioniert, benötigen Sie eine Möglichkeit, Jobs zur Warteschlange hinzuzufügen, und eine Möglichkeit, die Jobs zu verarbeiten (einen Worker). Im Folgenden finden Sie Beispiele, wie man einen Job zur Warteschlange hinzufügt und wie man den Job verarbeitet.
Hinzufügen zu Flight
Das Hinzufügen dieses Codes zu Flight ist einfach und erfolgt mit der Methode register()
. Unten finden Sie ein Beispiel, wie Sie dies zu Flight hinzufügen.
<?php
require 'vendor/autoload.php';
// Ändern Sie ['mysql'] in ['beanstalkd'], wenn Sie beanstalkd verwenden möchten
Flight::register('queue', n0nag0n\Job_Queue::class, ['mysql'], function($Job_Queue) {
// Wenn Sie bereits eine PDO-Verbindung zu Flight::db() haben;
$Job_Queue->addQueueConnection(Flight::db());
// Oder wenn Sie beanstalkd/Pheanstalk verwenden
$pheanstalk = Pheanstalk\Pheanstalk::create('127.0.0.1');
$Job_Queue->addQueueConnection($pheanstalk);
});
Einen neuen Job hinzufügen
Wenn Sie einen Job hinzufügen, müssen Sie eine Pipeline (Warteschlange) angeben. Dies ist vergleichbar mit einem Kanal in RabbitMQ oder einem Tube in beanstalkd.
<?php
Flight::queue()->selectPipeline('send_important_emails');
Flight::queue()->addJob(json_encode([ 'something' => 'that', 'ends' => 'up', 'a' => 'string' ]));
Einen Worker ausführen
Hier ist eine Beispieldatei, wie man einen Worker ausführt.
<?php
require 'vendor/autoload.php';
$Job_Queue = new n0nag0n\Job_Queue('mysql');
// PDO-Verbindung
$PDO = new PDO('mysql:dbname=testdb;host=127.0.0.1', 'user', 'pass');
$Job_Queue->addQueueConnection($PDO);
// Oder wenn Sie beanstalkd/Pheanstalk verwenden
$pheanstalk = Pheanstalk\Pheanstalk::create('127.0.0.1');
$Job_Queue->addQueueConnection($pheanstalk);
$Job_Queue->watchPipeline('send_important_emails');
while(true) {
$job = $Job_Queue->getNextJobAndReserve();
// Passen Sie es an, was Ihnen nachts besser schlafen lässt (nur für Datenbankwarteschlangen, beanstalkd benötigt diese if-Anweisung nicht)
if(empty($job)) {
usleep(500000);
continue;
}
echo "Verarbeite {$job['id']}\n";
$payload = json_decode($job['payload'], true);
try {
$result = doSomethingThatDoesSomething($payload);
if($result === true) {
$Job_Queue->deleteJob($job);
} else {
// Dies entfernt es aus der bereitstehenden Warteschlange und legt es in eine andere Warteschlange, die später aufgegriffen und "getreten" werden kann.
$Job_Queue->buryJob($job);
}
} catch(Exception $e) {
$Job_Queue->buryJob($job);
}
}
Lange Prozesse mit Supervisord verwalten
Supervisord ist ein Prozesskontrollsystem, das sicherstellt, dass Ihre Worker-Prozesse kontinuierlich laufen. Hier ist eine umfassendere Anleitung, wie Sie es mit Ihrem einfachen Job-Queue-Worker einrichten:
Supervisord installieren
# Auf Ubuntu/Debian
sudo apt-get install supervisor
# Auf CentOS/RHEL
sudo yum install supervisor
# Auf macOS mit Homebrew
brew install supervisor
Erstellen eines Worker-Skripts
Zuerst speichern Sie Ihren Worker-Code in einer dedizierten PHP-Datei:
<?php
require 'vendor/autoload.php';
$Job_Queue = new n0nag0n\Job_Queue('mysql');
// PDO-Verbindung
$PDO = new PDO('mysql:dbname=your_database;host=127.0.0.1', 'username', 'password');
$Job_Queue->addQueueConnection($PDO);
// Setzen Sie die Pipeline, die überwacht werden soll
$Job_Queue->watchPipeline('send_important_emails');
// Protokolliere den Start des Workers
echo date('Y-m-d H:i:s') . " - Worker gestartet\n";
while(true) {
$job = $Job_Queue->getNextJobAndReserve();
if(empty($job)) {
usleep(500000); // Schlafen für 0,5 Sekunden
continue;
}
echo date('Y-m-d H:i:s') . " - Verarbeite Job {$job['id']}\n";
$payload = json_decode($job['payload'], true);
try {
$result = doSomethingThatDoesSomething($payload);
if($result === true) {
$Job_Queue->deleteJob($job);
echo date('Y-m-d H:i:s') . " - Job {$job['id']} erfolgreich abgeschlossen\n";
} else {
$Job_Queue->buryJob($job);
echo date('Y-m-d H:i:s') . " - Job {$job['id']} fehlgeschlagen, beerdigt\n";
}
} catch(Exception $e) {
$Job_Queue->buryJob($job);
echo date('Y-m-d H:i:s') . " - Ausnahme bei der Verarbeitung des Jobs {$job['id']}: {$e->getMessage()}\n";
}
}
Konfigurieren von Supervisord
Erstellen Sie eine Konfigurationsdatei für Ihren Worker:
[program:email_worker]
command=php /path/to/worker.php
directory=/path/to/project
autostart=true
autorestart=true
startretries=3
stderr_logfile=/var/log/simple_job_queue_err.log
stdout_logfile=/var/log/simple_job_queue.log
user=www-data
numprocs=2
process_name=%(program_name)s_%(process_num)02d
Wichtige Konfigurationsoptionen:
command
: Der Befehl zum Ausführen Ihres Workersdirectory
: Arbeitsverzeichnis für den Workerautostart
: Automatisch starten, wenn supervisord startetautorestart
: Automatisch neu starten, wenn der Prozess beendet wirdstartretries
: Anzahl der Versuche, den Start zu wiederholen, wenn er fehlschlägtstderr_logfile
/stdout_logfile
: Speicherorte der Protokolldateienuser
: Systembenutzer, unter dem der Prozess ausgeführt werden sollnumprocs
: Anzahl der auszuführenden Worker-Instanzenprocess_name
: Namensformat für mehrere Worker-Prozesse
Worker mit Supervisorctl verwalten
Nach dem Erstellen oder Modifizieren der Konfiguration:
# Supervisor-Konfiguration neu laden
sudo supervisorctl reread
sudo supervisorctl update
# Steuerung spezifischer Worker-Prozesse
sudo supervisorctl start email_worker:*
sudo supervisorctl stop email_worker:*
sudo supervisorctl restart email_worker:*
sudo supervisorctl status email_worker:*
Mehrere Pipelines ausführen
Für mehrere Pipelines erstellen Sie separate Worker-Dateien und Konfigurationen:
[program:email_worker]
command=php /path/to/email_worker.php
# ... andere Konfigurationen ...
[program:notification_worker]
command=php /path/to/notification_worker.php
# ... andere Konfigurationen ...
Überwachen und Protokolle
Überprüfen Sie die Protokolle, um die Aktivität der Worker zu überwachen:
# Protokolle anzeigen
sudo tail -f /var/log/simple_job_queue.log
# Status überprüfen
sudo supervisorctl status
Dieses Setup sorgt dafür, dass Ihre Job-Worker auch nach Abstürzen, Serverneustarts oder anderen Problemen weiterlaufen, was Ihr Warteschlangensystem zuverlässig für Produktionsumgebungen macht.
Awesome-plugins/n0nag0n_wordpress
WordPress-Integration: n0nag0n/wordpress-integration-for-flight-framework
Möchten Sie Flight PHP in Ihrer WordPress-Site verwenden? Dieses Plugin macht es zum Kinderspiel! Mit n0nag0n/wordpress-integration-for-flight-framework
können Sie eine vollständige Flight-Anwendung direkt neben Ihrer WordPress-Installation ausführen – ideal zum Erstellen von benutzerdefinierten APIs, Mikroservices oder sogar vollwertigen Anwendungen, ohne WordPress zu verlassen.
Was tut es?
- Integriert Flight PHP nahtlos mit WordPress
- Leitet Anfragen an Flight oder WordPress basierend auf URL-Mustern weiter
- Organisieren Sie Ihren Code mit Controllern, Modellen und Ansichten (MVC)
- Richten Sie die empfohlene Flight-Ordnerstruktur einfach ein
- Verwenden Sie die WordPress-Datenbankverbindung oder Ihre eigene
- Feinabstimmung der Interaktion zwischen Flight und WordPress
- Einfache Admin-Oberfläche für die Konfiguration
Installation
- Laden Sie den Ordner
flight-integration
in Ihr/wp-content/plugins/
-Verzeichnis hoch. - Aktivieren Sie das Plugin im WordPress-Admin-Bereich (Plugins-Menü).
- Gehen Sie zu Einstellungen > Flight Framework, um das Plugin zu konfigurieren.
- Legen Sie den Pfad zum Vendor-Ordner Ihrer Flight-Installation fest (oder verwenden Sie Composer, um Flight zu installieren).
- Konfigurieren Sie den Pfad zu Ihrem App-Ordner und erstellen Sie die Ordnerstruktur (das Plugin kann Ihnen dabei helfen!).
- Starten Sie mit der Erstellung Ihrer Flight-Anwendung!
Nutzungsbeispiele
Einfaches Route-Beispiel
In Ihrer Datei app/config/routes.php
:
Flight::route('GET /api/hello', function() {
Flight::json(['message' => 'Hello World!']);
});
Controller-Beispiel
Erstellen Sie einen Controller in app/controllers/ApiController.php
:
namespace app\controllers;
use Flight;
class ApiController {
public function getUsers() {
// Sie können WordPress-Funktionen in Flight verwenden!
$users = get_users();
$result = [];
foreach($users as $user) {
$result[] = [
'id' => $user->ID,
'name' => $user->display_name,
'email' => $user->user_email
];
}
Flight::json($result);
}
}
Dann in Ihrer routes.php
:
Flight::route('GET /api/users', [app\controllers\ApiController::class, 'getUsers']);
FAQ
F: Muss ich Flight kennen, um dieses Plugin zu verwenden?
A: Ja, dies ist für Entwickler, die Flight innerhalb von WordPress nutzen möchten. Grundkenntnisse von Flights Routing und Anfrageverarbeitung werden empfohlen.
F: Wird dies meine WordPress-Site verlangsamen?
A: Nein! Das Plugin verarbeitet nur Anfragen, die zu Ihren Flight-Routen passen. Alle anderen Anfragen werden wie üblich an WordPress weitergeleitet.
F: Kann ich WordPress-Funktionen in meiner Flight-Anwendung verwenden?
A: Absolut! Sie haben vollen Zugriff auf alle WordPress-Funktionen, Hooks und Globals innerhalb Ihrer Flight-Routen und Controller.
F: Wie erstelle ich benutzerdefinierte Routes?
A: Definieren Sie Ihre Routes in der Datei config/routes.php
in Ihrem App-Ordner. Schauen Sie sich die Beispieldatei an, die vom Ordnerstruktur-Generator erstellt wird.
Changelog
1.0.0
Erstveröffentlichung.
Für mehr Infos schauen Sie sich das GitHub-Repo an.
Awesome-plugins/ghost_session
Ghostff/Session
PHP-Sitzungsmanager (nicht blockierend, Flash, Segment, Sitzungsverschlüsselung). Verwendet PHP open_ssl für optionale Verschlüsselung/Entschlüsselung von Sitzungsdaten. Unterstützt File, MySQL, Redis und Memcached.
Klicken Sie hier, um den Code anzusehen.
Installation
Installieren Sie mit Composer.
composer require ghostff/session
Basic Configuration
Sie müssen nichts übergeben, um die Standardeinstellungen für Ihre Sitzung zu verwenden. Sie können mehr über Einstellungen in der Github Readme nachlesen.
use Ghostff\Session\Session;
require 'vendor/autoload.php';
$app = Flight::app();
$app->register('session', Session::class);
// eine Sache, die man sich merken sollte, ist, dass Sie Ihre Sitzung bei jedem Seitenaufruf committen müssen
// oder Sie müssen auto_commit in Ihrer Konfiguration ausführen.
Simple Example
Hier ist ein einfaches Beispiel, wie Sie das verwenden könnten.
Flight::route('POST /login', function() {
$session = Flight::session();
// führen Sie hier Ihre Login-Logik aus
// Passwort validieren usw.
// wenn der Login erfolgreich ist
$session->set('is_logged_in', true);
$session->set('user', $user);
// immer wenn Sie in die Sitzung schreiben, müssen Sie sie explizit committen.
$session->commit();
});
// Diese Überprüfung könnte in der Logik der eingeschränkten Seite erfolgen oder mit Middleware umgeben sein.
Flight::route('/some-restricted-page', function() {
$session = Flight::session();
if(!$session->get('is_logged_in')) {
Flight::redirect('/login');
}
// führen Sie hier Ihre Logik für die eingeschränkte Seite aus
});
// die Middleware-Version
Flight::route('/some-restricted-page', function() {
// reguläre Seitenslogik
})->addMiddleware(function() {
$session = Flight::session();
if(!$session->get('is_logged_in')) {
Flight::redirect('/login');
}
});
More Complex Example
Hier ist ein komplexeres Beispiel, wie Sie das verwenden könnten.
use Ghostff\Session\Session;
require 'vendor/autoload.php';
$app = Flight::app();
// geben Sie als ersten Argument einen benutzerdefinierten Pfad zu Ihrer Sitzungskonfigurationsdatei an
// oder geben Sie das benutzerdefinierte Array
$app->register('session', Session::class, [
[
// wenn Sie Ihre Sitzungsdaten in einer Datenbank speichern möchten (gut für Funktionen wie "mich von allen Geräten abmelden")
Session::CONFIG_DRIVER => Ghostff\Session\Drivers\MySql::class,
Session::CONFIG_ENCRYPT_DATA => true,
Session::CONFIG_SALT_KEY => hash('sha256', 'my-super-S3CR3T-salt'), // bitte ändern Sie das zu etwas anderem
Session::CONFIG_AUTO_COMMIT => true, // tun Sie das nur, wenn es erforderlich ist und/oder es schwierig ist, commit() für Ihre Sitzung aufzurufen.
// zusätzlich könnten Sie Flight::after('start', function() { Flight::session()->commit(); }); machen.
Session::CONFIG_MYSQL_DS => [
'driver' => 'mysql', # Database driver for PDO dns eg(mysql:host=...;dbname=...)
'host' => '127.0.0.1', # Database host
'db_name' => 'my_app_database', # Database name
'db_table' => 'sessions', # Database table
'db_user' => 'root', # Database username
'db_pass' => '', # Database password
'persistent_conn'=> false, # Vermeiden Sie den Aufwand, eine neue Verbindung bei jedem Skriptaufruf herzustellen, was zu einer schnelleren Web-Anwendung führt. FINDEN SIE DIE NACHTEILE SELBST
]
]
]);
Help! My Session Data is Not Persisting!
Setzen Sie Ihre Sitzungsdaten und sie persistieren nicht zwischen Anfragen? Sie haben vielleicht vergessen, Ihre Sitzungsdaten zu committen. Sie können das tun, indem Sie $session->commit()
aufrufen, nachdem Sie Ihre Sitzungsdaten gesetzt haben.
Flight::route('POST /login', function() {
$session = Flight::session();
// führen Sie hier Ihre Login-Logik aus
// Passwort validieren usw.
// wenn der Login erfolgreich ist
$session->set('is_logged_in', true);
$session->set('user', $user);
// immer wenn Sie in die Sitzung schreiben, müssen Sie sie explizit committen.
$session->commit();
});
Die andere Möglichkeit, das zu umgehen, ist, wenn Sie Ihren Sitzungsdienst einrichten, auto_commit
in Ihrer Konfiguration auf true
setzen. Das wird Ihre Sitzungsdaten automatisch nach jeder Anfrage committen.
$app->register('session', Session::class, [ 'path/to/session_config.php', bin2hex(random_bytes(32)) ], function(Session $session) {
$session->updateConfiguration([
Session::CONFIG_AUTO_COMMIT => true,
]);
}
);
Zusätzlich könnten Sie Flight::after('start', function() { Flight::session()->commit(); });
machen, um Ihre Sitzungsdaten nach jeder Anfrage zu committen.
Documentation
Besuchen Sie die Github Readme für die vollständige Dokumentation. Die Konfigurationsoptionen sind gut dokumentiert in der default_config.php-Datei selbst. Der Code ist einfach zu verstehen, wenn Sie dieses Paket selbst durchsehen möchten.
Awesome-plugins/async
Async
Async ist ein kleines Paket für das Flight-Framework, das es Ihnen ermöglicht, Ihre Flight-Apps in asynchronen Servern und Runtimes wie Swoole, AdapterMan, ReactPHP, Amp, RoadRunner, Workerman usw. auszuführen. Out of the box enthält es Adapter für Swoole und AdapterMan.
Das Ziel: Entwickeln und Debuggen mit PHP-FPM (oder dem integrierten Server) und Wechseln zu Swoole (oder einem anderen asynchronen Treiber) für die Produktion mit minimalen Änderungen.
Requirements
- PHP 7.4 oder höher
- Flight-Framework 3.16.1 oder höher
- Swoole-Erweiterung
Installation
Installieren Sie es über Composer:
composer require flightphp/async
Falls Sie mit Swoole ausführen möchten, installieren Sie die Erweiterung:
# using pecl
pecl install swoole
# or openswoole
pecl install openswoole
# or with a package manager (Debian/Ubuntu example)
sudo apt-get install php-swoole
Quick Swoole example
Unten ist eine minimale Einrichtung zu sehen, die zeigt, wie Sie sowohl PHP-FPM (oder den integrierten Server) als auch Swoole mit demselben Codebase unterstützen können.
Dateien, die Sie in Ihrem Projekt benötigen:
- index.php
- swoole_server.php
- SwooleServerDriver.php
index.php
Diese Datei ist ein einfacher Schalter, der die App im PHP-Modus für die Entwicklung erzwingt.
// index.php
<?php
define('NOT_SWOOLE', true);
include 'swoole_server.php';
swoole_server.php
Diese Datei bootstrapt Ihre Flight-App und startet den Swoole-Treiber, wenn NOT_SWOOLE nicht definiert ist.
// swoole_server.php
<?php
require_once __DIR__ . '/vendor/autoload.php';
$app = Flight::app();
$app->route('/', function() use ($app) {
$app->json(['hello' => 'world']);
});
if (!defined('NOT_SWOOLE')) {
// Require the SwooleServerDriver class when running in Swoole mode.
require_once __DIR__ . '/SwooleServerDriver.php';
Swoole\Runtime::enableCoroutine();
$Swoole_Server = new SwooleServerDriver('127.0.0.1', 9501, $app);
$Swoole_Server->start();
} else {
$app->start();
}
SwooleServerDriver.php
Ein knapper Treiber, der zeigt, wie man Swoole-Anfragen in Flight über die AsyncBridge und die Swoole-Adapter überbrückt.
// SwooleServerDriver.php
<?php
use flight\adapter\SwooleAsyncRequest;
use flight\adapter\SwooleAsyncResponse;
use flight\AsyncBridge;
use flight\Engine;
use Swoole\HTTP\Server as SwooleServer;
use Swoole\HTTP\Request as SwooleRequest;
use Swoole\HTTP\Response as SwooleResponse;
class SwooleServerDriver {
protected $Swoole;
protected $app;
public function __construct(string $host, int $port, Engine $app) {
$this->Swoole = new SwooleServer($host, $port);
$this->app = $app;
$this->setDefault();
$this->bindWorkerEvents();
$this->bindHttpEvent();
}
protected function setDefault() {
$this->Swoole->set([
'daemonize' => false,
'dispatch_mode' => 1,
'max_request' => 8000,
'open_tcp_nodelay' => true,
'reload_async' => true,
'max_wait_time' => 60,
'enable_reuse_port' => true,
'enable_coroutine' => true,
'http_compression' => false,
'enable_static_handler' => true,
'document_root' => __DIR__,
'static_handler_locations' => ['/css', '/js', '/images', '/.well-known'],
'buffer_output_size' => 4 * 1024 * 1024,
'worker_num' => 4,
]);
$app = $this->app;
$app->map('stop', function (?int $code = null) use ($app) {
if ($code !== null) {
$app->response()->status($code);
}
});
}
protected function bindHttpEvent() {
$app = $this->app;
$AsyncBridge = new AsyncBridge($app);
$this->Swoole->on('Start', function(SwooleServer $server) {
echo "Swoole http server is started at http://127.0.0.1:9501\n";
});
$this->Swoole->on('Request', function (SwooleRequest $request, SwooleResponse $response) use ($AsyncBridge) {
$SwooleAsyncRequest = new SwooleAsyncRequest($request);
$SwooleAsyncResponse = new SwooleAsyncResponse($response);
$AsyncBridge->processRequest($SwooleAsyncRequest, $SwooleAsyncResponse);
$response->end();
gc_collect_cycles();
});
}
protected function bindWorkerEvents() {
$createPools = function() {
// create worker-specific connection pools here
};
$closePools = function() {
// close pools / cleanup here
};
$this->Swoole->on('WorkerStart', $createPools);
$this->Swoole->on('WorkerStop', $closePools);
$this->Swoole->on('WorkerError', $closePools);
}
public function start() {
$this->Swoole->start();
}
}
Running the server
- Entwicklung (PHP integrierter Server / PHP-FPM):
- php -S localhost:8000 (oder fügen Sie -t public/ hinzu, wenn Ihr index in public/ liegt)
- Produktion (Swoole):
- php swoole_server.php
Tipp: Für die Produktion verwenden Sie einen Reverse-Proxy (Nginx) vor Swoole, um TLS, statische Dateien und Lastverteilung zu handhaben.
Configuration notes
Der Swoole-Treiber stellt mehrere Konfigurationsoptionen zur Verfügung:
- worker_num: Anzahl der Worker-Prozesse
- max_request: Anfragen pro Worker vor dem Neustart
- enable_coroutine: Coroutines für Parallelität verwenden
- buffer_output_size: Ausgabepuffer-Größe
Passen Sie diese an Ihre Host-Ressourcen und Traffic-Muster an.
Error handling
AsyncBridge übersetzt Flight-Fehler in korrekte HTTP-Antworten. Sie können auch fehlerbehandlung auf Routenebene hinzufügen:
$app->route('/*', function() use ($app) {
try {
// route logic
} catch (Exception $e) {
$app->response()->status(500);
$app->json(['error' => $e->getMessage()]);
}
});
AdapterMan and other runtimes
AdapterMan wird als alternativer Runtime-Adapter unterstützt. Das Paket ist so konzipiert, dass es anpassbar ist – das Hinzufügen oder Verwenden anderer Adapter folgt im Allgemeinen demselben Muster: Konvertieren Sie die Server-Anfrage/Antwort in die Flight-Anfrage/Antwort über die AsyncBridge und die runtime-spezifischen Adapter.
Awesome-plugins/migrations
Migrationen
Eine Migration für Ihr Projekt verfolgt alle Datenbankänderungen, die mit Ihrem Projekt verbunden sind. byjg/php-migration ist eine wirklich hilfreiche Kernbibliothek, um Ihnen den Einstieg zu erleichtern.
Installation
PHP-Bibliothek
Wenn Sie nur die PHP-Bibliothek in Ihrem Projekt verwenden möchten:
composer require "byjg/migration"
Befehlszeilenschnittstelle
Die Befehlszeilenschnittstelle ist eigenständig und erfordert keine Installation mit Ihrem Projekt.
Sie können global installieren und einen symbolischen Link erstellen.
composer require "byjg/migration-cli"
Bitte besuchen Sie byjg/migration-cli für weitere Informationen zur Migration CLI.
Unterstützte Datenbanken
Datenbank | Treiber | Verbindungszeichenfolge |
---|---|---|
Sqlite | pdo_sqlite | sqlite:///path/to/file |
MySql/MariaDb | pdo_mysql | mysql://username:password@hostname:port/database |
Postgres | pdo_pgsql | pgsql://username:password@hostname:port/database |
Sql Server | pdo_dblib, pdo_sysbase Linux | dblib://username:password@hostname:port/database |
Sql Server | pdo_sqlsrv Windows | sqlsrv://username:password@hostname:port/database |
Wie funktioniert es?
Die Datenbankmigration verwendet reines SQL, um das Datenbankversioning zu verwalten. Um es zum Laufen zu bringen, müssen Sie:
- Die SQL-Skripte erstellen
- Verwenden Sie die Befehlszeile oder die API.
Die SQL-Skripte
Die Skripte sind in drei Gruppen unterteilt:
- Das BASIS-Skript enthält alle SQL-Befehle zum Erstellen einer frischen Datenbank;
- Die UP-Skripte enthalten alle SQL-Migrationsbefehle, um die Datenbankversion "hoch" zu setzen;
- Die DOWN-Skripte enthalten alle SQL-Migrationsbefehle, um die Datenbankversion "herunter" zu setzen oder zurückzusetzen;
Das Verzeichnis der Skripte ist :
<root dir>
|
+-- base.sql
|
+-- /migrations
|
+-- /up
|
+-- 00001.sql
+-- 00002.sql
+-- /down
|
+-- 00000.sql
+-- 00001.sql
- "base.sql" ist das Basisskript
- Der "up"-Ordner enthält die Skripte für das Hochsetzen der Version. Zum Beispiel: 00002.sql ist das Skript, um die Datenbank von Version '1' auf '2' zu migrieren.
- Der "down"-Ordner enthält die Skripte für das Heruntersetzen der Version. Zum Beispiel: 00001.sql ist das Skript, um die Datenbank von Version '2' auf '1' zu migrieren. Der "down"-Ordner ist optional.
Multi-Entwicklungsumgebung
Wenn Sie mit mehreren Entwicklern und mehreren Branches arbeiten, ist es schwierig zu bestimmen, welche Nummer die nächste ist.
In diesem Fall haben Sie das Suffix "-dev" nach der Versionsnummer.
Sehen Sie sich das Szenario an:
- Entwickler 1 erstellt einen Branch und die aktuellste Version ist z.B. 42.
- Entwickler 2 erstellt gleichzeitig einen Branch und hat die gleiche Datenbankversionsnummer.
In beiden Fällen werden die Entwickler eine Datei mit dem Namen 43-dev.sql erstellen. Beide Entwickler werden ohne Probleme hoch und runter migrieren können, und Ihre lokale Version wird 43 sein.
Aber Entwickler 1 hat seine Änderungen zusammengeführt und eine endgültige Version 43.sql erstellt (git mv 43-dev.sql 43.sql
). Wenn Entwickler 2 seinen lokalen Branch aktualisiert, hat er eine Datei 43.sql (von dev 1) und seine Datei 43-dev.sql.
Wenn er versucht, hoch oder runter zu migrieren,
wird das Migrationsskript abgebrochen und ihn warnen, dass es zwei Versionen 43 gibt. In diesem Fall muss Entwickler 2 seine Datei in 44-dev.sql aktualisieren und weiterarbeiten, bis er die Änderungen zusammenführt und eine endgültige Version generiert.
Verwendung der PHP-API und Integration in Ihre Projekte
Die grundlegende Verwendung ist
- Erstellen Sie eine Verbindung zu einem ConnectionManagement-Objekt. Für weitere Informationen siehe die Komponente "byjg/anydataset".
- Erstellen Sie ein Migrationsobjekt mit dieser Verbindung und dem Ordner, in dem sich die SQL-Skripte befinden.
- Verwenden Sie den richtigen Befehl für "reset", "up" oder "down" der Migrationsskripte.
Sehen Sie sich ein Beispiel an:
<?php
// Erstellen Sie die Verbindungs-URI
// Weitere Informationen: https://github.com/byjg/anydataset#connection-based-on-uri
$connectionUri = new \ByJG\Util\Uri('mysql://migrateuser:migratepwd@localhost/migratedatabase');
// Registrieren Sie die Datenbank oder Datenbanken, die diese URI verarbeiten können:
\ByJG\DbMigration\Migration::registerDatabase(\ByJG\DbMigration\Database\MySqlDatabase::class);
// Erstellen Sie die Migrationsinstanz
$migration = new \ByJG\DbMigration\Migration($connectionUri, '.');
// Fügen Sie eine Callback-Fortschrittsfunktion hinzu, um Informationen von der Ausführung zu erhalten
$migration->addCallbackProgress(function ($action, $currentVersion, $fileInfo) {
echo "$action, $currentVersion, ${fileInfo['description']}\n";
});
// Stellen Sie die Datenbank mit dem "base.sql"-Skript wieder her
// und führen Sie ALLE vorhandenen Skripte aus, um die Datenbankversion auf die neueste Version zu bringen
$migration->reset();
// Führen Sie ALLE vorhandenen Skripte für hoch oder runter die Datenbankversion aus
// von der aktuellen Version bis zur $version-Nummer;
// Wenn die Versionsnummer nicht angegeben ist, migrieren Sie bis zur letzten Datenbankversion
$migration->update($version = null);
Das Migrationsobjekt steuert die Datenbankversion.
Erstellen einer Versionskontrolle in Ihrem Projekt
<?php
// Registrieren Sie die Datenbank oder Datenbanken, die diese URI verarbeiten können:
\ByJG\DbMigration\Migration::registerDatabase(\ByJG\DbMigration\Database\MySqlDatabase::class);
// Erstellen Sie die Migrationsinstanz
$migration = new \ByJG\DbMigration\Migration($connectionUri, '.');
// Dieser Befehl erstellt die Versions-Tabelle in Ihrer Datenbank
$migration->createVersion();
Aktuelle Version abrufen
<?php
$migration->getCurrentVersion();
Callback zum Steuern des Fortschritts hinzufügen
<?php
$migration->addCallbackProgress(function ($command, $version, $fileInfo) {
echo "Befehl ausführen: $command bei Version $version - ${fileInfo['description']}, ${fileInfo['exists']}, ${fileInfo['file']}, ${fileInfo['checksum']}\n";
});
Instanz des Db-Treibers abrufen
<?php
$migration->getDbDriver();
Um es zu verwenden, besuchen Sie bitte: https://github.com/byjg/anydataset-db
Teilweise Migration vermeiden (nicht verfügbar für MySQL)
Eine partielle Migration ist, wenn das Migrationsskript in der Mitte des Prozesses aufgrund eines Fehlers oder einer manuellen Unterbrechung unterbrochen wird.
Die Migrationstabelle hat den Status partial up
oder partial down
und muss manuell behoben werden, bevor sie wieder migrieren kann.
Um diese Situation zu vermeiden, können Sie angeben, dass die Migration in einem Transaktionskontext ausgeführt wird.
Wenn das Migrationsskript fehlschlägt, wird die Transaktion zurückgesetzt und die Migrationstabelle wird als complete
markiert und
die Version wird die unmittelbar vorherige Version vor dem Skript sein, das den Fehler verursacht hat.
Um diese Funktion zu aktivieren, müssen Sie die Methode withTransactionEnabled
aufrufen und true
als Parameter übergeben:
<?php
$migration->withTransactionEnabled(true);
HINWEIS: Diese Funktion ist nicht für MySQL verfügbar, da es DDL-Befehle innerhalb einer Transaktion nicht unterstützt. Wenn Sie diese Methode mit MySQL verwenden, ignoriert die Migration sie stillschweigend. Weitere Informationen: https://dev.mysql.com/doc/refman/8.0/en/cannot-roll-back.html
Tipps zum Schreiben von SQL-Migrationen für Postgres
Zur Erstellung von Triggern und SQL-Funktionen
-- DO
CREATE FUNCTION emp_stamp() RETURNS trigger AS $emp_stamp$
BEGIN
-- Überprüfen, ob empname und salary angegeben sind
IF NEW.empname IS NULL THEN
RAISE EXCEPTION 'empname darf nicht null sein'; -- es ist egal, ob diese Kommentare leer sind oder nicht
END IF; --
IF NEW.salary IS NULL THEN
RAISE EXCEPTION '% darf kein null Gehalt haben', NEW.empname; --
END IF; --
-- Wer arbeitet für uns, wenn sie dafür bezahlen müssen?
IF NEW.salary < 0 THEN
RAISE EXCEPTION '% darf kein negatives Gehalt haben', NEW.empname; --
END IF; --
-- Merken Sie, wer die Gehaltsliste wann geändert hat
NEW.last_date := current_timestamp; --
NEW.last_user := current_user; --
RETURN NEW; --
END; --
$emp_stamp$ LANGUAGE plpgsql;
-- DON'T
CREATE FUNCTION emp_stamp() RETURNS trigger AS $emp_stamp$
BEGIN
-- Überprüfen, ob empname und salary angegeben sind
IF NEW.empname IS NULL THEN
RAISE EXCEPTION 'empname darf nicht null sein';
END IF;
IF NEW.salary IS NULL THEN
RAISE EXCEPTION '% darf kein null Gehalt haben', NEW.empname;
END IF;
-- Wer arbeitet für uns, wenn sie dafür bezahlen müssen?
IF NEW.salary < 0 THEN
RAISE EXCEPTION '% darf kein negatives Gehalt haben', NEW.empname;
END IF;
-- Merken Sie, wer die Gehaltsliste wann geändert hat
NEW.last_date := current_timestamp;
NEW.last_user := current_user;
RETURN NEW;
END;
$emp_stamp$ LANGUAGE plpgsql;
Da die PDO
-Datenbank-Abstraktionsschicht keine Batchverarbeitungen von SQL-Anweisungen ausführen kann,
muss byjg/migration
, wenn es eine Migrationsdatei liest, den gesamten Inhalt der SQL-Datei an den Semikolons aufteilen und die Anweisungen einzeln ausführen. Es gibt jedoch eine Art von
Anweisung, die mehrere Semikolons in ihrem Körper haben kann: Funktionen.
Um Funktionen korrekt parsen zu können, begann byjg/migration
2.1.0 damit, Migrationsdateien an der
Semikolon + EOL
-Sequenz anstelle nur am Semikolon aufzuteilen. Auf diese Weise kann byjg/migration
sie parsen, wenn Sie nach jedem inneren Semikolon einer Funktionsdefinition einen leeren Kommentar anhängen.
Leider wird die Bibliothek die CREATE FUNCTION
-Anweisung in mehrere Teile aufteilen und die Migration wird fehlschlagen, wenn Sie vergessen, diese Kommentare hinzuzufügen.
Vermeidung des Doppelpunktzeichens (:
)
-- DO
CREATE TABLE bookings (
booking_id UUID PRIMARY KEY,
booked_at TIMESTAMPTZ NOT NULL CHECK (CAST(booked_at AS DATE) <= check_in),
check_in DATE NOT NULL
);
-- DON'T
CREATE TABLE bookings (
booking_id UUID PRIMARY KEY,
booked_at TIMESTAMPTZ NOT NULL CHECK (booked_at::DATE <= check_in),
check_in DATE NOT NULL
);
Da PDO
das Doppelpunkt-Zeichen verwendet, um benannte Parameter in vorbereiteten Anweisungen zu kennzeichnen, führt die Verwendung zu Problemen in anderen Kontexten.
Zum Beispiel können PostgreSQL-Anweisungen ::
verwenden, um Werte zwischen Typen zu casten. Auf der anderen Seite wird PDO
dies als ungültigen benannten Parameter in einem ungültigen Kontext behandeln und fehlschlagen, wenn er versucht, es auszuführen.
Die einzige Möglichkeit, diese Inkonsistenz zu beheben, besteht darin, Doppelpunkte ganz zu vermeiden (in diesem Fall hat PostgreSQL auch eine alternative
Syntax: CAST(value AS type)
).
Verwenden Sie einen SQL-Editor
Abschließend kann das Schreiben manueller SQL-Migrationen mühsam sein, aber es ist deutlich einfacher, wenn Sie einen Editor verwenden, der die SQL-Syntax versteht, Autovervollständigung bietet, Ihr aktuelles Datenbankschema inspiziert und/oder Ihren Code automatisch formatiert.
Umgang mit unterschiedlichen Migrationen innerhalb eines Schemas
Wenn Sie unterschiedliche Migrationsskripte und Versionen innerhalb desselben Schemas erstellen müssen, ist dies möglich, aber es ist zu riskant und ich empfehle es nicht.
Um dies zu tun, müssen Sie unterschiedliche "Migrationstabellen" erstellen, indem Sie den Parameter an den Konstruktor übergeben.
<?php
$migration = new \ByJG\DbMigration\Migration("db:/uri", "/path", true, "NEUER_MIGRATIONSTABELLENNAME");
Aus Sicherheitsgründen ist diese Funktion nicht über die Befehlszeile verfügbar, aber Sie können die Umgebungsvariable
MIGRATION_VERSION
verwenden, um den Namen zu speichern.
Wir empfehlen dringend, diese Funktion nicht zu verwenden. Die Empfehlung ist, eine Migration für ein Schema durchzuführen.
Ausführen von Unit-Tests
Basis-Unit-Tests können ausgeführt werden mit:
vendor/bin/phpunit
Ausführen von Datenbanktests
Integrationstests erfordern, dass Sie die Datenbanken online und verfügbar haben. Wir haben ein einfaches docker-compose.yml
bereitgestellt, und Sie
können es verwenden, um die Datenbanken für Tests zu starten.
Ausführen der Datenbanken
docker-compose up -d postgres mysql mssql
Führen Sie die Tests aus
vendor/bin/phpunit
vendor/bin/phpunit tests/SqliteDatabase*
vendor/bin/phpunit tests/MysqlDatabase*
vendor/bin/phpunit tests/PostgresDatabase*
vendor/bin/phpunit tests/SqlServerDblibDatabase*
vendor/bin/phpunit tests/SqlServerSqlsrvDatabase*
Optional können Sie den Host und das Passwort, das von den Unit-Tests verwendet wird, festlegen.
export MYSQL_TEST_HOST=localhost # standardmäßig localhost
export MYSQL_PASSWORD=newpassword # verwenden Sie '.' wenn Sie ein leeres Passwort haben möchten
export PSQL_TEST_HOST=localhost # standardmäßig localhost
export PSQL_PASSWORD=newpassword # verwenden Sie '.' wenn Sie ein leeres Passwort haben möchten
export MSSQL_TEST_HOST=localhost # standardmäßig localhost
export MSSQL_PASSWORD=Pa55word
export SQLITE_TEST_HOST=/tmp/test.db # standardmäßig /tmp/test.db
Awesome-plugins/session
FlightPHP Session - Leichtgewichtiger Dateibasierter Session-Handler
Dies ist ein leichtgewichtiger, dateibasisierter Session-Handler-Plugin für das Flight PHP Framework. Es bietet eine einfache, aber leistungsstarke Lösung zur Verwaltung von Sessions, mit Funktionen wie nicht blockierendem Lesen von Sessions, optionaler Verschlüsselung, Auto-Commit-Funktionalität und einem Testmodus für die Entwicklung. Session-Daten werden in Dateien gespeichert, was es ideal für Anwendungen macht, die keine Datenbank benötigen.
Falls du eine Datenbank verwenden möchtest, schaue dir das ghostff/session Plugin an, das viele der gleichen Funktionen bietet, aber mit einer Datenbank-Backend.
Besuche das Github-Repository für den vollständigen Quellcode und Details.
Installation
Installiere das Plugin über Composer:
composer require flightphp/session
Grundlegende Nutzung
Hier ist ein einfaches Beispiel, wie du das flightphp/session
-Plugin in deiner Flight-Anwendung verwendest:
require 'vendor/autoload.php';
use flight\Session;
$app = Flight::app();
// Registriere den Session-Dienst
$app->register('session', Session::class);
// Beispiel-Route mit Session-Nutzung
Flight::route('/login', function() {
$session = Flight::session();
$session->set('user_id', 123);
$session->set('username', 'johndoe');
$session->set('is_admin', false);
echo $session->get('username'); // Gibt aus: johndoe
echo $session->get('preferences', 'default_theme'); // Gibt aus: default_theme
if ($session->get('user_id')) {
Flight::json(['message' => 'User is logged in!', 'user_id' => $session->get('user_id')]);
}
});
Flight::route('/logout', function() {
$session = Flight::session();
$session->clear(); // Löschung aller Session-Daten
Flight::json(['message' => 'Logged out successfully']);
});
Flight::start();
Wichtige Punkte
- Nicht blockierend: Verwende
read_and_close
standardmäßig, um Probleme mit Session-Sperrungen zu vermeiden. - Auto-Commit: Standardmäßig aktiviert, sodass Änderungen automatisch beim Herunterfahren gespeichert werden, es sei denn, es wird deaktiviert.
- Dateispeicherung: Sessions werden standardmäßig im System-Temp-Verzeichnis unter
/flight_sessions
gespeichert.
Konfiguration
Du kannst den Session-Handler anpassen, indem du ein Array von Optionen beim Registrieren übergeben:
// Ja, es ist ein doppeltes Array :)
$app->register('session', Session::class, [ [
'save_path' => '/custom/path/to/sessions', // Verzeichnis für Session-Dateien
'prefix' => 'myapp_', // Präfix für Session-Dateien
'encryption_key' => 'a-secure-32-byte-key-here', // Verschlüsselung aktivieren (32 Bytes empfohlen für AES-256-CBC)
'auto_commit' => false, // Auto-Commit deaktivieren für manuelle Kontrolle
'start_session' => true, // Session automatisch starten (Standard: true)
'test_mode' => false, // Testmodus für die Entwicklung aktivieren
'serialization' => 'json', // Serialisierungs-Methode: 'json' (Standard) oder 'php' (Legacy)
] ]);
Konfigurationsoptionen
Option | Beschreibung | Standardwert |
---|---|---|
save_path |
Verzeichnis, in dem Session-Dateien gespeichert werden | sys_get_temp_dir() . '/flight_sessions' |
prefix |
Präfix für die gespeicherte Session-Datei | sess_ |
encryption_key |
Schlüssel für AES-256-CBC-Verschlüsselung (optional) | null (keine Verschlüsselung) |
auto_commit |
Automatische Speicherung von Session-Daten beim Herunterfahren | true |
start_session |
Session automatisch starten | true |
test_mode |
Im Testmodus ausführen, ohne PHP-Sessions zu beeinflussen | false |
test_session_id |
Benutzerdefinierte Session-ID für den Testmodus (optional) | Zufällig generiert, wenn nicht gesetzt |
serialization |
Serialisierungs-Methode: 'json' (Standard, sicher) oder 'php' (Legacy, erlaubt Objekte) | 'json' |
Serialisierungsmodi
Standardmäßig verwendet diese Bibliothek JSON-Serialisierung für Session-Daten, was sicher ist und PHP-Objekt-Injektions-Schwachstellen verhindert. Wenn du PHP-Objekte in der Session speichern musst (nicht empfohlen für die meisten Apps), kannst du auf die Legacy-PHP-Serialisierung umschalten:
'serialization' => 'json'
(Standard):- Nur Arrays und Primitive sind in Session-Daten erlaubt.
- Sicherer: Immun gegen PHP-Objekt-Injection.
- Dateien werden mit
J
(einfaches JSON) oderF
(verschlüsseltes JSON) präfixiert.
'serialization' => 'php'
:- Erlaubt das Speichern von PHP-Objekten (mit Vorsicht verwenden).
- Dateien werden mit
P
(einfaches PHP-Serialisieren) oderE
(verschlüsseltes PHP-Serialisieren) präfixiert.
Hinweis: Wenn du JSON-Serialisierung verwendest, wirft das Versuch, ein Objekt zu speichern, eine Ausnahme.
Erweiterte Nutzung
Manuelles Commit
Wenn du Auto-Commit deaktivierst, musst du Änderungen manuell speichern:
$app->register('session', Session::class, ['auto_commit' => false]);
Flight::route('/update', function() {
$session = Flight::session();
$session->set('key', 'value');
$session->commit(); // Änderungen explizit speichern
});
Session-Sicherheit mit Verschlüsselung
Aktiviere Verschlüsselung für sensible Daten:
$app->register('session', Session::class, [
'encryption_key' => 'your-32-byte-secret-key-here'
]);
Flight::route('/secure', function() {
$session = Flight::session();
$session->set('credit_card', '4111-1111-1111-1111'); // Wird automatisch verschlüsselt
echo $session->get('credit_card'); // Wird beim Abruf entschlüsselt
});
Session-Regeneration
Regeneriere die Session-ID für Sicherheit (z. B. nach dem Login):
Flight::route('/post-login', function() {
$session = Flight::session();
$session->regenerate(); // Neue ID, Daten behalten
// ODER
$session->regenerate(true); // Neue ID, alte Daten löschen
});
Middleware-Beispiel
Schütze Routen mit sessionbasierter Authentifizierung:
Flight::route('/admin', function() {
Flight::json(['message' => 'Welcome to the admin panel']);
})->addMiddleware(function() {
$session = Flight::session();
if (!$session->get('is_admin')) {
Flight::halt(403, 'Access denied');
}
});
Dies ist nur ein einfaches Beispiel für die Nutzung in Middleware. Für ein detaillierteres Beispiel, siehe die Middleware-Dokumentation.
Methoden
Die Session
-Klasse bietet diese Methoden:
set(string $key, $value)
: Speichert einen Wert in der Session.get(string $key, $default = null)
: Ruft einen Wert ab, mit einem optionalen Standardwert, falls der Schlüssel nicht existiert.delete(string $key)
: Entfernt einen bestimmten Schlüssel aus der Session.clear()
: Löscht alle Session-Daten, behält aber den gleichen Dateinamen für die Session.commit()
: Speichert die aktuellen Session-Daten im Dateisystem.id()
: Gibt die aktuelle Session-ID zurück.regenerate(bool $deleteOldFile = false)
: Regeneriert die Session-ID inklusive Erstellen einer neuen Session-Datei, behält alle alten Daten und die alte Datei bleibt im System. Wenn$deleteOldFile
true
ist, wird die alte Session-Datei gelöscht.destroy(string $id)
: Zerstört eine Session anhand der ID und löscht die Session-Datei aus dem System. Dies ist Teil desSessionHandlerInterface
und$id
ist erforderlich. Typische Nutzung wäre$session->destroy($session->id())
.getAll()
: Gibt alle Daten der aktuellen Session zurück.
Alle Methoden außer get()
und id()
geben die Session
-Instanz für Kettenaufrufe zurück.
Warum dieses Plugin verwenden?
- Leichtgewichtig: Keine externen Abhängigkeiten – nur Dateien.
- Nicht blockierend: Vermeidet Session-Sperrungen mit
read_and_close
standardmäßig. - Sicher: Unterstützt AES-256-CBC-Verschlüsselung für sensible Daten.
- Flexibel: Optionen für Auto-Commit, Testmodus und manuelle Kontrolle.
- Flight-Native: Speziell für das Flight-Framework entwickelt.
Technische Details
- Speicherformat: Session-Dateien werden mit
sess_
präfixiert und im konfiguriertensave_path
gespeichert. Dateiinhalts-Präfixe:J
: Einfaches JSON (Standard, keine Verschlüsselung)F
: Verschlüsseltes JSON (Standard mit Verschlüsselung)P
: Einfaches PHP-Serialisieren (Legacy, keine Verschlüsselung)E
: Verschlüsseltes PHP-Serialisieren (Legacy mit Verschlüsselung)
- Verschlüsselung: Verwende AES-256-CBC mit einem zufälligen IV pro Session-Schreibvorgang, wenn ein
encryption_key
angegeben ist. Verschlüsselung funktioniert für beide JSON- und PHP-Serialisierungsmodi. - Serialisierung: JSON ist die Standard- und sicherste Methode. PHP-Serialisierung ist für Legacy/fortgeschrittene Nutzung verfügbar, ist aber weniger sicher.
- Garbage Collection: Implementiert PHP’s
SessionHandlerInterface::gc()
, um abgelaufene Sessions zu bereinigen.
Beitrag
Beiträge sind willkommen! Forke das Repository, mache deine Änderungen und reiche einen Pull-Request ein. Melde Fehler oder schlage Features über den Github-Issue-Tracker vor.
Lizenz
Dieses Plugin ist unter der MIT-Lizenz lizenziert. Siehe das Github-Repository für Details.
Awesome-plugins/runway
Startbahn
Startbahn ist eine CLI-Anwendung, die Ihnen dabei hilft, Ihre Flight-Anwendungen zu verwalten. Sie kann Controller generieren, alle Routen anzeigen und mehr. Sie basiert auf der ausgezeichneten adhocore/php-cli Bibliothek.
Klicken Sie hier, um den Code anzusehen.
Installation
Mit Composer installieren.
composer require flightphp/startbahn
Grundkonfiguration
Das erste Mal, wenn Sie Startbahn ausführen, wird es Sie durch einen Einrichtungsprozess führen und eine .runway.json-Konfigurationsdatei im Stammverzeichnis Ihres Projekts erstellen. Diese Datei wird einige notwendige Konfigurationen enthalten, damit Startbahn ordnungsgemäß funktioniert.
Verwendung
Startbahn hat mehrere Befehle, die Sie verwenden können, um Ihre Flight-Anwendung zu verwalten. Es gibt zwei einfache Möglichkeiten, Startbahn zu verwenden.
- Wenn Sie das Grundgerüstprojekt verwenden, können Sie
php startbahn [Befehl]
im Stammverzeichnis Ihres Projekts ausführen. - Wenn Sie Startbahn als über Composer installiertes Paket verwenden, können Sie
vendor/bin/startbahn [Befehl]
im Stammverzeichnis Ihres Projekts ausführen.
Für jeden Befehl können Sie die --help
-Flagge übergeben, um weitere Informationen zur Verwendung des Befehls zu erhalten.
php startbahn routes --help
Hier sind ein paar Beispiele:
Einen Controller generieren
Basierend auf der Konfiguration in Ihrer .runway.json-Datei wird der Standardort einen Controller für Sie im app/controllers/
Verzeichnis generieren.
php startbahn make:controller MeinController
Einen Aktiven Datensatz-Model generieren
Basierend auf der Konfiguration in Ihrer .runway.json-Datei wird der Standardort einen Controller für Sie im app/records/
Verzeichnis generieren.
php startbahn make:record benutzer
Wenn Sie zum Beispiel die benutzer
Tabelle mit dem folgenden Schema haben: id
, name
, email
, created_at
, updated_at
, wird eine Datei ähnlich der folgenden in der app/records/BenutzerDatensatz.php
erstellt:
<?php
declare(strict_types=1);
namespace app\records;
/**
* ActiveRecord-Klasse für die benutzer Tabelle.
* @link https://docs.flightphp.com/awesome-plugins/active-record
*
* @property int $id
* @property string $name
* @property string $email
* @property string $created_at
* @property string $updated_at
* // Sie können hier auch Beziehungen hinzufügen, sobald Sie sie im $relations-Array definieren
* @property CompanyRecord $company Beispiel für eine Beziehung
*/
class BenutzerDatensatz erstreckt sich über \flight\ActiveRecord
{
/**
* @var array $relations Setzen Sie die Beziehungen für das Modell
* https://docs.flightphp.com/awesome-plugins/active-record#relationships
*/
protected array $relations = [];
/**
* Konstruktor
* @param gemischt $datenbankverbindung Die Verbindung zur Datenbank
*/
public function __construct($datenbankverbindung)
{
parent::__construct($datenbankverbindung, 'benutzer');
}
}
Alle Routen anzeigen
Dies wird alle Routen anzeigen, die derzeit bei Flight registriert sind.
php startbahn routes
Wenn Sie nur bestimmte Routen anzeigen möchten, können Sie eine Flagge übergeben, um die Routen zu filtern.
# Nur GET-Routen anzeigen
php startbahn routes --get
# Nur POST-Routen anzeigen
php startbahn routes --post
# usw.
Anpassen von Startbahn
Wenn Sie entweder ein Paket für Flight erstellen oder Ihre eigenen benutzerdefinierten Befehle in Ihr Projekt integrieren möchten, können Sie dies tun, indem Sie ein src/befehle/
, flight/befehle/
, app/befehle/
oder befehle/
Verzeichnis für Ihr Projekt/Paket erstellen.
Um einen Befehl zu erstellen, erweitern Sie einfach die Klasse AbstractBaseCommand
und implementieren Sie mindestens eine __construct
-Methode und eine ausführen
-Methode.
<?php
declare(strict_types=1);
namespace flight\commands;
Klasse BeispielBefehl erweitert AbstractBaseCommand
{
/**
* Konstruieren
*
* @param array<string, gemischt> $konfig JSON-Konfiguration aus .runway-config.json
*/
public function __construct(array $konfig)
{
parent::__construct('make:beispiel', 'Erstellt ein Beispiel für die Dokumentation', $konfig);
$this->argument('<lustiges-gif>', 'Der Name des lustigen Gifs');
}
/**
* Führt die Funktion aus
*
* @return Leer
*/
public function execute(string $controller)
{
$io = $this->app()->io();
$io->info('Beispiel wird erstellt...');
// Hier etwas machen
$io->ok('Beispiel erstellt!');
}
}
Siehe die adhocore/php-cli Dokumentation für weitere Informationen, wie Sie Ihre eigenen benutzerdefinierten Befehle in Ihre Flight-Anwendung integrieren können!
Awesome-plugins/tracy_extensions
Tracy Flight Panel Extensions
Dies ist eine Sammlung von Erweiterungen, um die Arbeit mit Flight etwas reicher zu gestalten.
- Flight - Analysiert alle Flight-Variablen.
- Database - Analysiert alle Abfragen, die auf der Seite ausgeführt wurden (wenn Sie die Datenbankverbindung korrekt initialisieren)
- Request - Analysiert alle
$_SERVER
-Variablen und untersucht alle globalen Payloads ($_GET
,$_POST
,$_FILES
) - Session - Analysiert alle
$_SESSION
-Variablen, wenn Sessions aktiv sind.
Dies ist das Panel
Und jedes Panel zeigt sehr hilfreiche Informationen über Ihre Anwendung an!
Klicken Sie hier, um den Code anzusehen.
Installation
Führen Sie composer require flightphp/tracy-extensions --dev
aus und Sie sind startklar!
Configuration
Es gibt sehr wenig Konfiguration, die Sie vornehmen müssen, um damit zu beginnen. Sie müssen den Tracy-Debugger vor der Verwendung dieser https://tracy.nette.org/en/guide initialisieren:
<?php
use Tracy\Debugger;
use flight\debug\tracy\TracyExtensionLoader;
// bootstrap code
require __DIR__ . '/vendor/autoload.php';
Debugger::enable();
// You may need to specify your environment with Debugger::enable(Debugger::DEVELOPMENT)
// if you use database connections in your app, there is a
// required PDO wrapper to use ONLY IN DEVELOPMENT (not production please!)
// It has the same parameters as a regular PDO connection
$pdo = new PdoQueryCapture('sqlite:test.db', 'user', 'pass');
// or if you attach this to the Flight framework
Flight::register('db', PdoQueryCapture::class, ['sqlite:test.db', 'user', 'pass']);
// now whenever you make a query it will capture the time, query, and parameters
// This connects the dots
if(Debugger::$showBar === true) {
// This needs to be false or Tracy can't actually render :(
Flight::set('flight.content_length', false);
new TracyExtensionLoader(Flight::app());
}
// more code
Flight::start();
Additional Configuration
Session Data
If you have a custom session handler (such as ghostff/session), you can pass any array of session data to Tracy and it will automatically output it for you. You pass it in with the session_data
key in the second parameter of the TracyExtensionLoader
constructor.
use Ghostff\Session\Session;
// or use flight\Session;
require 'vendor/autoload.php';
$app = Flight::app();
$app->register('session', Session::class);
if(Debugger::$showBar === true) {
// This needs to be false or Tracy can't actually render :(
Flight::set('flight.content_length', false);
new TracyExtensionLoader(Flight::app(), [ 'session_data' => Flight::session()->getAll() ]);
}
// routes and other things...
Flight::start();
Latte
PHP 8.1+ is required for this section.
If you have Latte installed in your project, Tracy has a native integration with Latte to analyze your templates. You simple register the extension with your Latte instance.
require 'vendor/autoload.php';
$app = Flight::app();
$app->map('render', function($template, $data, $block = null) {
$latte = new Latte\Engine;
// other configurations...
// only add the extension if Tracy Debug Bar is enabled
if(Debugger::$showBar === true) {
// this is where you add the Latte Panel to Tracy
$latte->addExtension(new Latte\Bridges\Tracy\TracyExtension);
}
$latte->render($template, $data, $block);
});
Awesome-plugins/apm
FlightPHP APM Dokumentation
Willkommen bei FlightPHP APM – dem persönlichen Performance-Coach für Ihre App! Dieser Leitfaden ist Ihre Roadmap zur Einrichtung, Nutzung und Beherrschung der Application Performance Monitoring (APM) mit FlightPHP. Ob Sie langsame Anfragen aufspüren oder einfach nur Latency-Diagramme analysieren möchten, wir haben Sie abgedeckt. Lassen Sie uns Ihre App schneller machen, Ihre Nutzer glücklicher und Ihre Debugging-Sitzungen zu einem Kinderspiel!
Sehen Sie sich eine Demo des Dashboards für die Flight Docs Site an.
Warum APM wichtig ist
Stellen Sie sich vor: Ihre App ist ein volles Restaurant. Ohne eine Möglichkeit, zu verfolgen, wie lange Bestellungen dauern oder wo die Küche stockt, raten Sie, warum Kunden unzufrieden gehen. APM ist Ihr Sous-Chef – es beobachtet jeden Schritt, von eingehenden Anfragen bis zu Datenbankabfragen, und markiert alles, was Sie verlangsamt. Langsame Seiten verlieren Nutzer (Studien sagen, 53 % verlassen die Seite, wenn sie mehr als 3 Sekunden zum Laden braucht!), und APM hilft Ihnen, diese Probleme bevor sie schmerzen zu erkennen. Es ist proaktive Seelenruhe – weniger „Warum ist das kaputt?“-Momente, mehr „Schau, wie reibungslos das läuft!“-Erfolge.
Installation
Beginnen Sie mit Composer:
composer require flightphp/apm
Sie benötigen:
- PHP 7.4+: Hält uns kompatibel mit LTS Linux-Distributionen, während es modernes PHP unterstützt.
- FlightPHP Core v3.15+: Das leichte Framework, das wir boosten.
Unterstützte Datenbanken
FlightPHP APM unterstützt derzeit die folgenden Datenbanken zur Speicherung von Metriken:
- SQLite3: Einfach, dateibasiert und ideal für lokale Entwicklung oder kleine Apps. Standardoption in den meisten Setups.
- MySQL/MariaDB: Ideal für größere Projekte oder Produktionsumgebungen, in denen Sie robuste, skalierbare Speicherung benötigen.
Sie können Ihren Datenbanktyp während des Konfigurationsschritts wählen (siehe unten). Stellen Sie sicher, dass Ihre PHP-Umgebung die notwendigen Erweiterungen installiert hat (z. B. pdo_sqlite
oder pdo_mysql
).
Erste Schritte
Hier ist Ihr Schritt-für-Schritt zu APM-Großartigkeit:
1. Registrieren Sie die APM
Fügen Sie das in Ihre index.php
oder eine services.php
-Datei ein, um mit dem Tracking zu beginnen:
use flight\apm\logger\LoggerFactory;
use flight\Apm;
$ApmLogger = LoggerFactory::create(__DIR__ . '/../../.runway-config.json');
$Apm = new Apm($ApmLogger);
$Apm->bindEventsToFlightInstance($app);
// Wenn Sie eine Datenbankverbindung hinzufügen
// Muss PdoWrapper oder PdoQueryCapture aus Tracy Extensions sein
$pdo = new PdoWrapper('mysql:host=localhost;dbname=example', 'user', 'pass', null, true); // <-- True erforderlich, um Tracking in der APM zu aktivieren.
$Apm->addPdoConnection($pdo);
Was passiert hier?
LoggerFactory::create()
greift auf Ihre Konfiguration zu (mehr dazu bald) und richtet einen Logger ein – standardmäßig SQLite.Apm
ist der Star – es hört auf Flights Events (Anfragen, Routen, Fehler usw.) und sammelt Metriken.bindEventsToFlightInstance($app)
verbindet alles mit Ihrer Flight-App.
Pro-Tipp: Sampling Wenn Ihre App beschäftigt ist, könnte das Loggen jeder Anfrage die Dinge überlasten. Verwenden Sie eine Sample-Rate (0.0 bis 1.0):
$Apm = new Apm($ApmLogger, 0.1); // Protokolliert 10 % der Anfragen
Das hält die Performance knackig, während es Ihnen dennoch solide Daten liefert.
2. Konfigurieren Sie es
Führen Sie das aus, um Ihre .runway-config.json
zu erstellen:
php vendor/bin/runway apm:init
Was macht das?
- Startet einen Wizard, der fragt, wo rohe Metriken herkommen (Quelle) und wo verarbeitete Daten hingehen (Ziel).
- Standard ist SQLite – z. B.
sqlite:/tmp/apm_metrics.sqlite
für die Quelle, eine andere für das Ziel. - Sie erhalten eine Konfiguration wie:
{ "apm": { "source_type": "sqlite", "source_db_dsn": "sqlite:/tmp/apm_metrics.sqlite", "storage_type": "sqlite", "dest_db_dsn": "sqlite:/tmp/apm_metrics_processed.sqlite" } }
Dieser Prozess fragt auch, ob Sie die Migrationen für dieses Setup ausführen möchten. Wenn Sie das zum ersten Mal einrichten, lautet die Antwort ja.
Warum zwei Orte? Rohe Metriken häufen sich schnell an (denken Sie an ungefilterte Logs). Der Worker verarbeitet sie in ein strukturiertes Ziel für das Dashboard. Hält alles ordentlich!
3. Metriken mit dem Worker verarbeiten
Der Worker verwandelt rohe Metriken in dashboard-bereite Daten. Führen Sie ihn einmal aus:
php vendor/bin/runway apm:worker
Was macht er?
- Liest aus Ihrer Quelle (z. B.
apm_metrics.sqlite
). - Verarbeitet bis zu 100 Metriken (Standard-Batch-Größe) in Ihr Ziel.
- Stoppt, wenn fertig oder keine Metriken mehr da sind.
Am Laufen halten Für Live-Apps möchten Sie kontinuierliche Verarbeitung. Hier sind Ihre Optionen:
-
Daemon-Modus:
php vendor/bin/runway apm:worker --daemon
Läuft ewig und verarbeitet Metriken, sobald sie kommen. Gut für Dev oder kleine Setups.
-
Crontab: Fügen Sie das zu Ihrer Crontab hinzu (
crontab -e
):* * * * * php /path/to/project/vendor/bin/runway apm:worker
Feuert jede Minute – perfekt für Produktion.
-
Tmux/Screen: Starten Sie eine abtrennbare Sitzung:
tmux new -s apm-worker php vendor/bin/runway apm:worker --daemon # Ctrl+B, dann D zum Abtrennen; `tmux attach -t apm-worker` zum Wiederverbinden
Hält es am Laufen, auch wenn Sie ausloggen.
-
Benutzerdefinierte Anpassungen:
php vendor/bin/runway apm:worker --batch_size 50 --max_messages 1000 --timeout 300
--batch_size 50
: Verarbeitet 50 Metriken auf einmal.--max_messages 1000
: Stoppt nach 1000 Metriken.--timeout 300
: Beendet nach 5 Minuten.
Warum die Mühe? Ohne den Worker ist Ihr Dashboard leer. Es ist die Brücke zwischen rohen Logs und handlungsrelevanten Erkenntnissen.
4. Dashboard starten
Sehen Sie die Vitalwerte Ihrer App:
php vendor/bin/runway apm:dashboard
Was ist das?
- Startet einen PHP-Server unter
http://localhost:8001/apm/dashboard
. - Zeigt Anfragen-Logs, langsame Routen, Fehlerquoten und mehr.
Anpassen:
php vendor/bin/runway apm:dashboard --host 0.0.0.0 --port 8080 --php-path=/usr/local/bin/php
--host 0.0.0.0
: Erreichbar von jeder IP (praktisch für Fernzugriff).--port 8080
: Verwenden Sie einen anderen Port, wenn 8001 belegt ist.--php-path
: Zeigen Sie auf PHP, wenn es nicht in Ihrem PATH ist.
Öffnen Sie die URL in Ihrem Browser und erkunden Sie!
Produktionsmodus
Für die Produktion müssen Sie möglicherweise einige Techniken ausprobieren, um das Dashboard zum Laufen zu bringen, da wahrscheinlich Firewalls und andere Sicherheitsmaßnahmen im Spiel sind. Hier sind ein paar Optionen:
- Reverse Proxy verwenden: Richten Sie Nginx oder Apache ein, um Anfragen an das Dashboard weiterzuleiten.
- SSH-Tunnel: Wenn Sie per SSH auf den Server zugreifen können, verwenden Sie
ssh -L 8080:localhost:8001 youruser@yourserver
, um das Dashboard zu Ihrem lokalen Rechner zu tunneln. - VPN: Wenn Ihr Server hinter einem VPN ist, verbinden Sie sich damit und greifen Sie direkt auf das Dashboard zu.
- Firewall konfigurieren: Öffnen Sie Port 8001 für Ihre IP oder das Netzwerk des Servers. (Oder welchen Port Sie auch eingestellt haben).
- Apache/Nginx konfigurieren: Wenn Sie einen Webserver vor Ihrer Anwendung haben, können Sie ihn für eine Domain oder Subdomain konfigurieren. Wenn Sie das tun, setzen Sie das Document Root auf
/path/to/your/project/vendor/flightphp/apm/dashboard
.
Wollen Sie ein anderes Dashboard?
Sie können Ihr eigenes Dashboard bauen, wenn Sie möchten! Schauen Sie in das Verzeichnis vendor/flightphp/apm/src/apm/presenter
für Ideen, wie Sie die Daten für Ihr eigenes Dashboard präsentieren können!
Dashboard-Funktionen
Das Dashboard ist Ihr APM-Hauptquartier – hier ist, was Sie sehen werden:
- Anfragen-Log: Jede Anfrage mit Zeitstempel, URL, Response-Code und Gesamtzeit. Klicken Sie auf „Details“ für Middleware, Abfragen und Fehler.
- Langsamste Anfragen: Top 5 Anfragen, die Zeit fressen (z. B. „/api/heavy“ bei 2,5 s).
- Langsamste Routen: Top 5 Routen nach durchschnittlicher Zeit – super zum Erkennen von Mustern.
- Fehlerquote: Prozentsatz fehlgeschlagener Anfragen (z. B. 2,3 % 500er).
- Latenz-Percentile: 95. (p95) und 99. (p99) Response-Zeiten – kennen Sie Ihre Worst-Case-Szenarien.
- Response-Code-Diagramm: Visualisieren Sie 200er, 404er, 500er über die Zeit.
- Lange Abfragen/Middleware: Top 5 langsame Datenbankaufrufe und Middleware-Schichten.
- Cache-Treffer/Verfehlung: Wie oft Ihr Cache den Tag rettet.
Extras:
- Filtern nach „Letzte Stunde“, „Letzter Tag“ oder „Letzte Woche“.
- Umschalten auf Dark Mode für nächtliche Sessions.
Beispiel:
Eine Anfrage an /users
könnte zeigen:
- Gesamtzeit: 150 ms
- Middleware:
AuthMiddleware->handle
(50 ms) - Abfrage:
SELECT * FROM users
(80 ms) - Cache: Treffer bei
user_list
(5 ms)
Hinzufügen benutzerdefinierter Events
Verfolgen Sie alles – wie einen API-Aufruf oder Zahlungsprozess:
use flight\apm\CustomEvent;
$app->eventDispatcher()->trigger('apm.custom', new CustomEvent('api_call', [
'endpoint' => 'https://api.example.com/users',
'response_time' => 0.25,
'status' => 200
]));
Wo erscheint es? In den Anfragen-Details des Dashboards unter „Custom Events“ – erweiterbar mit hübscher JSON-Formatierung.
Anwendungsfall:
$start = microtime(true);
$apiResponse = file_get_contents('https://api.example.com/data');
$app->eventDispatcher()->trigger('apm.custom', new CustomEvent('external_api', [
'url' => 'https://api.example.com/data',
'time' => microtime(true) - $start,
'success' => $apiResponse !== false
]));
Jetzt sehen Sie, ob diese API Ihre App herunterzieht!
Datenbank-Monitoring
Verfolgen Sie PDO-Abfragen so:
use flight\database\PdoWrapper;
$pdo = new PdoWrapper('sqlite:/path/to/db.sqlite', null, null, null, true); // <-- True erforderlich, um Tracking in der APM zu aktivieren.
$Apm->addPdoConnection($pdo);
Was Sie bekommen:
- Abfragetext (z. B.
SELECT * FROM users WHERE id = ?
) - Ausführungszeit (z. B. 0,015 s)
- Zeilenanzahl (z. B. 42)
Achtung:
- Optional: Überspringen Sie das, wenn Sie kein DB-Tracking brauchen.
- Nur PdoWrapper: Core PDO ist noch nicht integriert – bleiben Sie dran!
- Performance-Warnung: Das Loggen jeder Abfrage auf einer DB-lastigen Site kann Dinge verlangsamen. Verwenden Sie Sampling (
$Apm = new Apm($ApmLogger, 0.1)
), um die Last zu reduzieren.
Beispiel-Ausgabe:
- Abfrage:
SELECT name FROM products WHERE price > 100
- Zeit: 0,023 s
- Zeilen: 15
Worker-Optionen
Passen Sie den Worker nach Ihrem Geschmack an:
--timeout 300
: Stoppt nach 5 Minuten – gut für Tests.--max_messages 500
: Begrenzt auf 500 Metriken – hält es endlich.--batch_size 200
: Verarbeitet 200 auf einmal – balanciert Geschwindigkeit und Speicher.--daemon
: Läuft non-stop – ideal für Live-Monitoring.
Beispiel:
php vendor/bin/runway apm:worker --daemon --batch_size 100 --timeout 3600
Läuft eine Stunde, verarbeitet 100 Metriken auf einmal.
Request ID in der App
Jede Anfrage hat eine eindeutige Request ID für das Tracking. Sie können diese ID in Ihrer App verwenden, um Logs und Metriken zu korrelieren. Zum Beispiel können Sie die Request ID auf einer Fehlerseite hinzufügen:
Flight::map('error', function($message) {
// Holen Sie die Request ID aus dem Response-Header X-Flight-Request-Id
$requestId = Flight::response()->getHeader('X-Flight-Request-Id');
// Zusätzlich könnten Sie sie aus der Flight-Variable holen
// Diese Methode funktioniert nicht gut in Swoole oder anderen asynchronen Plattformen.
// $requestId = Flight::get('apm.request_id');
echo "Fehler: $message (Request ID: $requestId)";
});
Upgrade
Wenn Sie auf eine neuere Version der APM upgraden, besteht die Möglichkeit, dass Datenbank-Migrationen ausgeführt werden müssen. Sie können das tun, indem Sie den folgenden Befehl ausführen:
php vendor/bin/runway apm:migrate
Das führt alle benötigten Migrationen aus, um das Datenbankschema auf die neueste Version zu aktualisieren.
Hinweis: Wenn Ihre APM-Datenbank groß ist, können diese Migrationen einige Zeit in Anspruch nehmen. Sie möchten diesen Befehl vielleicht während der Nebenzeiten ausführen.
Alte Daten bereinigen
Um Ihre Datenbank ordentlich zu halten, können Sie alte Daten bereinigen. Das ist besonders nützlich, wenn Sie eine beschäftigte App betreiben und die Datenbankgröße handhabbar halten möchten. Sie können das tun, indem Sie den folgenden Befehl ausführen:
php vendor/bin/runway apm:purge
Das entfernt alle Daten, die älter als 30 Tage sind, aus der Datenbank. Sie können die Anzahl der Tage anpassen, indem Sie einen anderen Wert an die --days
-Option übergeben:
php vendor/bin/runway apm:purge --days 7
Das entfernt alle Daten, die älter als 7 Tage sind, aus der Datenbank.
Fehlerbehebung
Feststecken? Probieren Sie diese aus:
-
Kein Dashboard-Daten?
- Läuft der Worker? Überprüfen Sie
ps aux | grep apm:worker
. - Stimmen die Konfigurationspfade? Überprüfen Sie, ob die DSNs in
.runway-config.json
auf echte Dateien zeigen. - Führen Sie
php vendor/bin/runway apm:worker
manuell aus, um ausstehende Metriken zu verarbeiten.
- Läuft der Worker? Überprüfen Sie
-
Worker-Fehler?
- Schauen Sie in Ihre SQLite-Dateien (z. B.
sqlite3 /tmp/apm_metrics.sqlite "SELECT * FROM apm_metrics_log LIMIT 5"
). - Überprüfen Sie PHP-Logs auf Stack-Traces.
- Schauen Sie in Ihre SQLite-Dateien (z. B.
-
Dashboard startet nicht?
- Port 8001 belegt? Verwenden Sie
--port 8080
. - PHP nicht gefunden? Verwenden Sie
--php-path /usr/bin/php
. - Firewall blockiert? Öffnen Sie den Port oder verwenden Sie
--host localhost
.
- Port 8001 belegt? Verwenden Sie
-
Zu langsam?
- Senken Sie die Sample-Rate:
$Apm = new Apm($ApmLogger, 0.05)
(5 %). - Reduzieren Sie die Batch-Größe:
--batch_size 20
.
- Senken Sie die Sample-Rate:
-
Keine Ausnahmen/Fehler getrackt?
- Wenn Sie Tracy für Ihr Projekt aktiviert haben, überschreibt es die Fehlerbehandlung von Flight. Sie müssen Tracy deaktivieren und sicherstellen, dass
Flight::set('flight.handle_errors', true);
gesetzt ist.
- Wenn Sie Tracy für Ihr Projekt aktiviert haben, überschreibt es die Fehlerbehandlung von Flight. Sie müssen Tracy deaktivieren und sicherstellen, dass
-
Datenbankabfragen nicht getrackt?
- Stellen Sie sicher, dass Sie
PdoWrapper
für Ihre Datenbankverbindungen verwenden. - Vergewissern Sie sich, dass Sie das letzte Argument im Konstruktor auf
true
setzen.
- Stellen Sie sicher, dass Sie
Awesome-plugins/tracy
Tracy
Tracy ist ein erstaunlicher Fehlerhandler, der mit Flight verwendet werden kann. Es verfügt über eine Reihe von Panels, die Ihnen bei der Fehlerbehebung Ihrer Anwendung helfen können. Es ist auch sehr einfach zu erweitern und eigene Panels hinzuzufügen. Das Flight-Team hat speziell für Flight-Projekte mit dem flightphp/tracy-extensions Plugin einige Panels erstellt.
Installation
Installiere es mit Composer. Und in der Tat möchten Sie dies ohne die Entwicklerversion installieren, da Tracy mit einem Produktionsfehlerbehandlungskomponente geliefert wird.
composer require tracy/tracy
Grundkonfiguration
Es gibt einige grundlegende Konfigurationsoptionen, um loszulegen. Weitere Informationen dazu finden Sie in der Tracy-Dokumentation.
require 'vendor/autoload.php';
use Tracy\Debugger;
// Aktiviere Tracy
Debugger::enable();
// Debugger::enable(Debugger::DEVELOPMENT) // Manchmal müssen Sie explizit sein (auch Debugger::PRODUCTION)
// Debugger::enable('23.75.345.200'); // Sie können auch ein Array von IP-Adressen bereitstellen
// Hier werden Fehler und Ausnahmen protokolliert. Stellen Sie sicher, dass dieses Verzeichnis vorhanden ist und beschreibbar ist.
Debugger::$logDirectory = __DIR__ . '/../log/';
Debugger::$strictMode = true; // alle Fehler anzeigen
// Debugger::$strictMode = E_ALL & ~E_DEPRECATED & ~E_USER_DEPRECATED; // alle Fehler außer veralteten Hinweisen
if (Debugger::$showBar) {
$app->set('flight.content_length', false); // Wenn die Debugger-Leiste sichtbar ist, kann die Inhaltslänge nicht von Flight festgelegt werden
// Dies ist spezifisch für die Tracy-Erweiterung für Flight, wenn Sie diese eingeschlossen haben
// Andernfalls kommentieren Sie dies aus.
new TracyExtensionLoader($app);
}
Hilfreiche Tipps
Wenn Sie Ihren Code debuggen, gibt es einige sehr hilfreiche Funktionen, um Daten für Sie auszugeben.
bdump($var)
- Dies gibt die Variable in der Tracy-Leiste in einem separaten Panel aus.dumpe($var)
- Dies gibt die Variable aus und beendet dann sofort.
Awesome-plugins/active_record
Flight Aktive Datensätze
Ein aktiver Datensatz ist die Zuordnung einer Datenbankentität zu einem PHP-Objekt. Einfach gesagt, wenn Sie eine Tabelle für Benutzer in Ihrer Datenbank haben, können Sie eine Zeile in dieser Tabelle in eine User
-Klasse und ein $user
-Objekt in Ihrem Code übersetzen. Siehe ein einfaches Beispiel.
Klicken Sie hier für das Repository auf GitHub.
Einfaches Beispiel
Angenommen, Sie haben die folgende Tabelle:
CREATE TABLE users (
id INTEGER PRIMARY KEY,
name TEXT,
password TEXT
);
Jetzt können Sie eine neue Klasse einrichten, um diese Tabelle darzustellen:
/**
* Eine ActiveRecord-Klasse ist normalerweise im Singular
*
* Es wird dringend empfohlen, die Eigenschaften der Tabelle hier als Kommentare hinzuzufügen
*
* @property int $id
* @property string $name
* @property string $password
*/
class User extends flight\ActiveRecord {
public function __construct($database_connection)
{
// so können Sie es einrichten
parent::__construct($database_connection, 'users');
// oder so
parent::__construct($database_connection, null, [ 'table' => 'users']);
}
}
Jetzt beobachten Sie, wie die Magie geschieht!
// für sqlite
$database_connection = new PDO('sqlite:test.db'); // dies ist nur ein Beispiel, Sie würden wahrscheinlich eine echte Datenbankverbindung verwenden
// für mysql
$database_connection = new PDO('mysql:host=localhost;dbname=test_db&charset=utf8bm4', 'username', 'password');
// oder mysqli
$database_connection = new mysqli('localhost', 'username', 'password', 'test_db');
// oder mysqli mit nicht objektbasierter Erstellung
$database_connection = mysqli_connect('localhost', 'username', 'password', 'test_db');
$user = new User($database_connection);
$user->name = 'Bobby Tables';
$user->password = password_hash('ein cooles Passwort');
$user->insert();
// oder $user->save();
echo $user->id; // 1
$user->name = 'Joseph Mamma';
$user->password = password_hash('ein anderes cooles Passwort!!!');
$user->insert();
// $user->save() kann hier nicht verwendet werden, da es als Update betrachtet wird!
echo $user->id; // 2
Und es war so einfach, einen neuen Benutzer hinzuzufügen! Jetzt, wo es eine Benutzerzeile in der Datenbank gibt, wie ziehen Sie sie heraus?
$user->find(1); // finde id = 1 in der Datenbank und gebe sie zurück.
echo $user->name; // 'Bobby Tables'
Und was ist, wenn Sie alle Benutzer finden möchten?
$users = $user->findAll();
Wie wäre es mit einer bestimmten Bedingung?
$users = $user->like('name', '%mamma%')->findAll();
Sehen Sie, wie viel Spaß das macht? Lassen Sie uns das installieren und loslegen!
Installation
Einfach mit Composer installieren
composer require flightphp/active-record
Verwendung
Dies kann als eigenständige Bibliothek oder mit dem Flight PHP Framework verwendet werden. Vollständig Ihnen überlassen.
Eigenständig
Stellen Sie sicher, dass Sie eine PDO-Verbindung an den Konstruktor weitergeben.
$pdo_connection = new PDO('sqlite:test.db'); // dies ist nur ein Beispiel, Sie würden wahrscheinlich eine echte Datenbankverbindung verwenden
$User = new User($pdo_connection);
Möchten Sie nicht immer Ihre Datenbankverbindung im Konstruktor einstellen? Siehe Datenbankverbindungsmanagement für weitere Ideen!
Als Methode in Flight registrieren
Wenn Sie das Flight PHP Framework verwenden, können Sie die ActiveRecord-Klasse als Dienst registrieren, aber das müssen Sie ehrlich gesagt nicht.
Flight::register('user', 'User', [ $pdo_connection ]);
// Dann können Sie es so in einem Controller, einer Funktion usw. verwenden.
Flight::user()->find(1);
runway
Methoden
runway ist ein CLI-Tool für Flight, das einen benutzerdefinierten Befehl für diese Bibliothek hat.
# Verwendung
php runway make:record database_table_name [class_name]
# Beispiel
php runway make:record users
Dies wird eine neue Klasse im Verzeichnis app/records/
als UserRecord.php
mit folgendem Inhalt erstellen:
<?php
declare(strict_types=1);
namespace app\records;
/**
* ActiveRecord-Klasse für die Benutzertabelle.
* @link https://docs.flightphp.com/awesome-plugins/active-record
*
* @property int $id
* @property string $username
* @property string $email
* @property string $password_hash
* @property string $created_dt
*/
class UserRecord extends \flight\ActiveRecord
{
/**
* @var array $relations Legen Sie die Beziehungen für das Modell fest
* https://docs.flightphp.com/awesome-plugins/active-record#relationships
*/
protected array $relations = [
// 'relation_name' => [ self::HAS_MANY, 'RelatedClass', 'foreign_key' ],
];
/**
* Konstruktor
* @param mixed $databaseConnection Die Verbindung zur Datenbank
*/
public function __construct($databaseConnection)
{
parent::__construct($databaseConnection, 'users');
}
}
CRUD-Funktionen
find($id = null) : boolean|ActiveRecord
Findet einen Datensatz und weist ihn dem aktuellen Objekt zu. Wenn Sie eine $id
irgendeiner Art übergeben, wird eine Abfrage mit dem Primärschlüssel mit diesem Wert durchgeführt. Wenn nichts übergeben wird, wird einfach der erste Datensatz in der Tabelle gefunden.
Zusätzlich können Sie ihm andere Hilfsmethoden übergeben, um Ihre Tabelle abzufragen.
// finde einen Datensatz mit vorher festgelegten Bedingungen
$user->notNull('password')->orderBy('id DESC')->find();
// finde einen Datensatz nach einer bestimmten id
$id = 123;
$user->find($id);
findAll(): array<int,ActiveRecord>
Findet alle Datensätze in der von Ihnen angegebenen Tabelle.
$user->findAll();
isHydrated(): boolean
(v0.4.0)
Gibt true
zurück, wenn der aktuelle Datensatz hydratisiert (aus der Datenbank abgerufen) wurde.
$user->find(1);
// wenn ein Datensatz mit Daten gefunden wird...
$user->isHydrated(); // true
insert(): boolean|ActiveRecord
Fügt den aktuellen Datensatz in die Datenbank ein.
$user = new User($pdo_connection);
$user->name = 'demo';
$user->password = md5('demo');
$user->insert();
Textbasierte Primärschlüssel
Wenn Sie einen textbasierten Primärschlüssel (wie eine UUID) haben, können Sie den Primärschlüsselwert vor dem Einfügen auf eine von zwei Arten festlegen.
$user = new User($pdo_connection, [ 'primaryKey' => 'uuid' ]);
$user->uuid = 'some-uuid';
$user->name = 'demo';
$user->password = md5('demo');
$user->insert(); // oder $user->save();
oder Sie können den Primärschlüssel automatisch durch Ereignisse generieren lassen.
class User extends flight\ActiveRecord {
public function __construct($database_connection)
{
parent::__construct($database_connection, 'users', [ 'primaryKey' => 'uuid' ]);
// Sie können auch so anstelle des obigen Arrays den Primärschlüssel festlegen.
$this->primaryKey = 'uuid';
}
protected function beforeInsert(self $self) {
$self->uuid = uniqid(); // oder wie auch immer Sie Ihre einzigartigen IDs generieren müssen
}
}
Wenn Sie den Primärschlüssel vor dem Einfügen nicht festlegen, wird er auf den rowid
gesetzt und die Datenbank wird ihn für Sie generieren, aber es wird nicht gespeichert, da dieses Feld möglicherweise nicht in Ihrer Tabelle vorhanden ist. Aus diesem Grund wird empfohlen, das Ereignis zu verwenden, um dies automatisch für Sie zu verwalten.
update(): boolean|ActiveRecord
Aktualisiert den aktuellen Datensatz in der Datenbank.
$user->greaterThan('id', 0)->orderBy('id desc')->find();
$user->email = 'test@example.com';
$user->update();
save(): boolean|ActiveRecord
Fügt den aktuellen Datensatz in die Datenbank ein oder aktualisiert ihn. Wenn der Datensatz eine ID hat, wird er aktualisiert, andernfalls wird er eingefügt.
$user = new User($pdo_connection);
$user->name = 'demo';
$user->password = md5('demo');
$user->save();
Hinweis: Wenn Sie Beziehungen, die in der Klasse definiert sind, haben, werden diese ebenfalls rekursiv gespeichert, wenn sie definiert, instanziiert und überarbeitete Daten zum Aktualisieren aufweisen. (v0.4.0 und höher)
delete(): boolean
Löscht den aktuellen Datensatz aus der Datenbank.
$user->gt('id', 0)->orderBy('id desc')->find();
$user->delete();
Sie können auch mehrere Datensätze löschen, indem Sie zuvor eine Suche durchführen.
$user->like('name', 'Bob%')->delete();
dirty(array $dirty = []): ActiveRecord
"Schmutzige" Daten bezieht sich auf die Daten, die in einem Datensatz geändert wurden.
$user->greaterThan('id', 0)->orderBy('id desc')->find();
// zu diesem Zeitpunkt ist nichts "schmutzig".
$user->email = 'test@example.com'; // jetzt wird die E-Mail als "schmutzig" betrachtet, da sie geändert wurde.
$user->update();
// jetzt gibt es keine Daten, die schmutzig sind, da sie aktualisiert und in der Datenbank gespeichert wurden
$user->password = password_hash('newpassword'); // jetzt ist dies schmutzig
$user->dirty(); // Nichts zu übergebendes löscht alle schmutzigen Einträge.
$user->update(); // nichts wird aktualisiert, da nichts als schmutzig erfasst wurde.
$user->dirty([ 'name' => 'etwas', 'password' => password_hash('ein anderes Passwort') ]);
$user->update(); // sowohl Name als auch Passwort werden aktualisiert.
copyFrom(array $data): ActiveRecord
(v0.4.0)
Dies ist ein Alias für die Methode dirty()
. Es ist etwas klarer, was Sie tun.
$user->copyFrom([ 'name' => 'etwas', 'password' => password_hash('ein anderes Passwort') ]);
$user->update(); // sowohl Name als auch Passwort werden aktualisiert.
isDirty(): boolean
(v0.4.0)
Gibt true
zurück, wenn der aktuelle Datensatz geändert wurde.
$user->greaterThan('id', 0)->orderBy('id desc')->find();
$user->email = 'test@email.com';
$user->isDirty(); // true
reset(bool $include_query_data = true): ActiveRecord
Setzt den aktuellen Datensatz auf seinen ursprünglichen Zustand zurück. Dies ist wirklich gut in Schleifenverhalten zu verwenden.
Wenn Sie true
übergeben, werden auch die Abfragedaten zurückgesetzt, die verwendet wurden, um das aktuelle Objekt zu finden (Standardverhalten).
$users = $user->greaterThan('id', 0)->orderBy('id desc')->find();
$user_company = new UserCompany($pdo_connection);
foreach($users as $user) {
$user_company->reset(); // beginne mit einem sauberen Zustand
$user_company->user_id = $user->id;
$user_company->company_id = $some_company_id;
$user_company->insert();
}
getBuiltSql(): string
(v0.4.1)
Nachdem Sie eine find()
, findAll()
, insert()
, update()
oder save()
-Methode ausgeführt haben, können Sie das SQL abrufen, das erstellt wurde und für Debugging-Zwecke verwenden.
SQL-Abfragemethoden
select(string $field1 [, string $field2 ... ])
Sie können nur einige der Spalten in einer Tabelle auswählen, wenn Sie möchten (es ist leistungsfähiger bei wirklich breiten Tabellen mit vielen Spalten)
$user->select('id', 'name')->find();
from(string $table)
Technisch können Sie auch eine andere Tabelle wählen! Warum nicht?!
$user->select('id', 'name')->from('user')->find();
join(string $table_name, string $join_condition)
Sie können sogar eine andere Tabelle in der Datenbank verknüpfen.
$user->join('contacts', 'contacts.user_id = users.id')->find();
where(string $where_conditions)
Sie können einige benutzerdefinierte WHERE-Argumente festlegen (Sie können keine Parameter in dieser WHERE-Anweisung festlegen)
$user->where('id=1 AND name="demo"')->find();
Sicherheitsnotiz - Sie könnten versucht sein, etwas wie $user->where("id = '{$id}' AND name = '{$name}'")->find();
zu tun. BITTE MACHEN SIE DAS NICHT!!! Dies ist anfällig für das, was als SQL-Injection-Angriffe bekannt ist. Es gibt viele Artikel online, bitte googeln Sie "sql injection attacks php" und Sie werden viele Artikel zu diesem Thema finden. Der richtige Weg, dies mit dieser Bibliothek zu behandeln, besteht darin, anstelle dieser where()
-Methode etwas mehr wie $user->eq('id', $id)->eq('name', $name)->find();
zu tun. Wenn Sie dies absolut tun müssen, hat die PDO
-Bibliothek $pdo->quote($var)
, um es für Sie zu escapen. Erst nachdem Sie quote()
verwenden, können Sie es in einer where()
-Anweisung verwenden.
group(string $group_by_statement)/groupBy(string $group_by_statement)
Gruppieren Sie Ihre Ergebnisse nach einer bestimmten Bedingung.
$user->select('COUNT(*) as count')->groupBy('name')->findAll();
order(string $order_by_statement)/orderBy(string $order_by_statement)
Sortieren Sie die zurückgegebene Abfrage auf eine bestimmte Weise.
$user->orderBy('name DESC')->find();
limit(string $limit)/limit(int $offset, int $limit)
Begrenzen Sie die Anzahl der zurückgegebenen Datensätze. Wenn eine zweite ganze Zahl angegeben wird, wird sie wie in SQL offset und limit.
$user->orderby('name DESC')->limit(0, 10)->findAll();
WHERE-Bedingungen
equal(string $field, mixed $value) / eq(string $field, mixed $value)
Wo field = $value
$user->eq('id', 1)->find();
notEqual(string $field, mixed $value) / ne(string $field, mixed $value)
Wo field <> $value
$user->ne('id', 1)->find();
isNull(string $field)
Wo field IS NULL
$user->isNull('id')->find();
isNotNull(string $field) / notNull(string $field)
Wo field IS NOT NULL
$user->isNotNull('id')->find();
greaterThan(string $field, mixed $value) / gt(string $field, mixed $value)
Wo field > $value
$user->gt('id', 1)->find();
lessThan(string $field, mixed $value) / lt(string $field, mixed $value)
Wo field < $value
$user->lt('id', 1)->find();
greaterThanOrEqual(string $field, mixed $value) / ge(string $field, mixed $value) / gte(string $field, mixed $value)
Wo field >= $value
$user->ge('id', 1)->find();
lessThanOrEqual(string $field, mixed $value) / le(string $field, mixed $value) / lte(string $field, mixed $value)
Wo field <= $value
$user->le('id', 1)->find();
like(string $field, mixed $value) / notLike(string $field, mixed $value)
Wo field LIKE $value
oder field NOT LIKE $value
$user->like('name', 'de')->find();
in(string $field, array $values) / notIn(string $field, array $values)
Wo field IN($value)
oder field NOT IN($value)
$user->in('id', [1, 2])->find();
between(string $field, array $values)
Wo field BETWEEN $value AND $value1
$user->between('id', [1, 2])->find();
ODER-Bedingungen
Es ist möglich, Ihre Bedingungen in einer ODER-Anweisung einzuwickeln. Dies erfolgt entweder durch die Methoden startWrap()
und endWrap()
oder indem Sie den 3. Parameter der Bedingung nach dem Feld und dem Wert ausfüllen.
// Methode 1
$user->eq('id', 1)->startWrap()->eq('name', 'demo')->or()->eq('name', 'test')->endWrap('OR')->find();
// Dies wird ausgewertet zu `id = 1 AND (name = 'demo' OR name = 'test')`
// Methode 2
$user->eq('id', 1)->eq('name', 'demo', 'OR')->find();
// Dies wird ausgewertet zu `id = 1 OR name = 'demo'`
Beziehungen
Sie können mit dieser Bibliothek mehrere Arten von Beziehungen festlegen. Sie können Eins-zu-viele- und Eins-zu-eins-Beziehungen zwischen Tabellen festlegen. Dies erfordert eine kleine zusätzliche Einrichtung in der Klasse im Voraus.
Das Festlegen des $relations
-Arrays ist nicht schwer, aber die richtige Syntax zu erraten, kann verwirrend sein.
protected array $relations = [
// Sie können den Schlüssel benennen, wie Sie möchten. Der Name des ActiveRecord ist wahrscheinlich gut. Beispiel: user, contact, client
'user' => [
// erforderlich
// self::HAS_MANY, self::HAS_ONE, self::BELONGS_TO
self::HAS_ONE, // dies ist der Beziehungstyp
// erforderlich
'Some_Class', // dies ist die "andere" ActiveRecord-Klasse, auf die verwiesen wird
// erforderlich
// abhängig vom Beziehungstyp
// self::HAS_ONE = der Fremdschlüssel, der auf den Join verweist
// self::HAS_MANY = der Fremdschlüssel, der auf den Join verweist
// self::BELONGS_TO = der lokale Schlüssel, der auf den Join verweist
'local_or_foreign_key',
// nur zur Info, dies verbindet sich ebenfalls nur mit dem Primärschlüssel des "anderen" Modells
// optional
[ 'eq' => [ 'client_id', 5 ], 'select' => 'COUNT(*) as count', 'limit' 5 ], // zusätzliche Bedingungen, die Sie beim Verknüpfen der Beziehung möchten
// $record->eq('client_id', 5)->select('COUNT(*) as count')->limit(5))
// optional
'back_reference_name' // dies ist, wenn Sie diese Beziehung wieder auf sich selbst zurückverweisen möchten, z. B.: $user->contact->user;
];
]
class User extends ActiveRecord{
protected array $relations = [
'contacts' => [ self::HAS_MANY, Contact::class, 'user_id' ],
'contact' => [ self::HAS_ONE, Contact::class, 'user_id' ],
];
public function __construct($database_connection)
{
parent::__construct($database_connection, 'users');
}
}
class Contact extends ActiveRecord{
protected array $relations = [
'user' => [ self::BELONGS_TO, User::class, 'user_id' ],
'user_with_backref' => [ self::BELONGS_TO, User::class, 'user_id', [], 'contact' ],
];
public function __construct($database_connection)
{
parent::__construct($database_connection, 'contacts');
}
}
Jetzt haben wir die Referenzen eingerichtet, sodass wir sie sehr einfach verwenden können!
$user = new User($pdo_connection);
// finde den aktuellsten Benutzer.
$user->notNull('id')->orderBy('id desc')->find();
// hole Kontakte mithilfe der Beziehung:
foreach($user->contacts as $contact) {
echo $contact->id;
}
// oder wir können es andersherum machen.
$contact = new Contact();
// finde einen Kontakt
$contact->find();
// hole Benutzer mithilfe der Beziehung:
echo $contact->user->name; // dies ist der Benutzername
Ganz schön cool, oder?
Benutzerdefinierte Daten festlegen
Manchmal müssen Sie etwas Einzigartiges an Ihrem ActiveRecord anhängen, z. B. eine benutzerdefinierte Berechnung, die es einfacher machen könnte, einfach an das Objekt anzuhängen, das dann an beispielsweise eine Vorlage übergeben wird.
setCustomData(string $field, mixed $value)
Sie hängen die benutzerdefinierten Daten mit der Methode setCustomData()
an.
$user->setCustomData('page_view_count', $page_view_count);
Und dann können Sie einfach auf sie zugreifen wie auf eine normale Objekt-Eigenschaft.
echo $user->page_view_count;
Ereignisse
Eine weitere großartige Funktion dieser Bibliothek sind die Ereignisse. Ereignisse werden zu bestimmten Zeiten ausgelöst, basierend auf bestimmten Methoden, die Sie aufrufen. Sie sind sehr hilfreich, um automatisch Daten für Sie einzurichten.
onConstruct(ActiveRecord $ActiveRecord, array &config)
Dies ist wirklich hilfreich, wenn Sie eine Standardverbindung oder so etwas festlegen müssen.
// index.php oder bootstrap.php
Flight::register('db', 'PDO', [ 'sqlite:test.db' ]);
//
//
//
// User.php
class User extends flight\ActiveRecord {
protected function onConstruct(self $self, array &$config) { // vergessen Sie nicht, die &-Referenz
// Sie könnten dies tun, um die Verbindung automatisch festzulegen
$config['connection'] = Flight::db();
// oder dies
$self->transformAndPersistConnection(Flight::db());
// Sie können auch den Tabellennamen auf diese Weise festlegen.
$config['table'] = 'users';
}
}
beforeFind(ActiveRecord $ActiveRecord)
Dies ist wahrscheinlich nur nützlich, wenn Sie jede Abfrage bei Bedarf manipulieren müssen.
class User extends flight\ActiveRecord {
public function __construct($database_connection)
{
parent::__construct($database_connection, 'users');
}
protected function beforeFind(self $self) {
// immer id >= 0 ausführen, wenn das Ihre Vorliebe ist
$self->gte('id', 0);
}
}
afterFind(ActiveRecord $ActiveRecord)
Dies ist wahrscheinlich nützlicher, wenn Sie immer eine Logik ausführen müssen, jedes Mal, wenn dieser Datensatz abgerufen wird. Müssen Sie etwas entschlüsseln? Müssen Sie jedes Mal eine benutzerdefinierte Zählabfrage ausführen (nicht leistungsfähig, aber was soll's)?
class User extends flight\ActiveRecord {
public function __construct($database_connection)
{
parent::__construct($database_connection, 'users');
}
protected function afterFind(self $self) {
// etwas entschlüsseln
$self->secret = yourDecryptFunction($self->secret, $some_key);
// vielleicht etwas Benutzerdefiniertes speichern wie eine Abfrage???
$self->setCustomData('view_count', $self->select('COUNT(*) count')->from('user_views')->eq('user_id', $self->id)['count']);
}
}
beforeFindAll(ActiveRecord $ActiveRecord)
Dies ist wahrscheinlich nur nützlich, wenn Sie jede Abfrage bei Bedarf manipulieren müssen.
class User extends flight\ActiveRecord {
public function __construct($database_connection)
{
parent::__construct($database_connection, 'users');
}
protected function beforeFindAll(self $self) {
// immer id >= 0 ausführen, wenn das Ihre Vorliebe ist
$self->gte('id', 0);
}
}
afterFindAll(array<int,ActiveRecord> $results)
Ähnlich wie afterFind()
, aber Sie können es auf alle Datensätze anwenden!
class User extends flight\ActiveRecord {
public function __construct($database_connection)
{
parent::__construct($database_connection, 'users');
}
protected function afterFindAll(array $results) {
foreach($results as $self) {
// etwas Cooles tun wie bei afterFind()
}
}
}
beforeInsert(ActiveRecord $ActiveRecord)
Sehr hilfreich, wenn Sie jedes Mal Standardwerte festlegen müssen.
class User extends flight\ActiveRecord {
public function __construct($database_connection)
{
parent::__construct($database_connection, 'users');
}
protected function beforeInsert(self $self) {
// einige sinnvolle Standardwerte festlegen
if(!$self->created_date) {
$self->created_date = gmdate('Y-m-d');
}
if(!$self->password) {
$self->password = password_hash((string) microtime(true));
}
}
}
afterInsert(ActiveRecord $ActiveRecord)
Vielleicht haben Sie einen Benutzerfall, um Daten nach dem Einfügen zu ändern?
class User extends flight\ActiveRecord {
public function __construct($database_connection)
{
parent::__construct($database_connection, 'users');
}
protected function afterInsert(self $self) {
// machen Sie, was Sie möchten
Flight::cache()->set('most_recent_insert_id', $self->id);
// oder was auch immer....
}
}
beforeUpdate(ActiveRecord $ActiveRecord)
Sehr hilfreich, wenn Sie jedes Mal Standardwerte festlegen müssen, wenn eine Aktualisierung erfolgt.
class User extends flight\ActiveRecord {
public function __construct($database_connection)
{
parent::__construct($database_connection, 'users');
}
protected function beforeInsert(self $self) {
// einige sinnvolle Standardwerte festlegen
if(!$self->updated_date) {
$self->updated_date = gmdate('Y-m-d');
}
}
}
afterUpdate(ActiveRecord $ActiveRecord)
Vielleicht haben Sie einen Benutzerfall, um Daten nach der Aktualisierung zu ändern?
class User extends flight\ActiveRecord {
public function __construct($database_connection)
{
parent::__construct($database_connection, 'users');
}
protected function afterInsert(self $self) {
// machen Sie, was Sie möchten
Flight::cache()->set('most_recently_updated_user_id', $self->id);
// oder was auch immer....
}
}
beforeSave(ActiveRecord $ActiveRecord)/afterSave(ActiveRecord $ActiveRecord)
Dies ist nützlich, wenn Sie Ereignisse auslösen möchten, sowohl wenn Einfügungen als auch Aktualisierungen erfolgen. Ich spare Ihnen die lange Erklärung, aber ich bin mir sicher, dass Sie erraten können, was es ist.
class User extends flight\ActiveRecord {
public function __construct($database_connection)
{
parent::__construct($database_connection, 'users');
}
protected function beforeSave(self $self) {
$self->last_updated = gmdate('Y-m-d H:i:s');
}
}
beforeDelete(ActiveRecord $ActiveRecord)/afterDelete(ActiveRecord $ActiveRecord)
Ich bin mir nicht sicher, was Sie hier tun möchten, aber hier wird nicht geurteilt! Legen Sie los!
class User extends flight\ActiveRecord {
public function __construct($database_connection)
{
parent::__construct($database_connection, 'users');
}
protected function beforeDelete(self $self) {
echo 'Er war ein tapferer Soldat... :cry-face:';
}
}
Verwaltung der Datenbankverbindung
Wenn Sie diese Bibliothek verwenden, können Sie die Datenbankverbindung auf mehrere Arten festlegen. Sie können die Verbindung im Konstruktor festlegen, Sie können sie über eine Konfigurationsvariable $config['connection']
festlegen oder Sie können sie über setDatabaseConnection()
(v0.4.1) festlegen.
$pdo_connection = new PDO('sqlite:test.db'); // zum Beispiel
$user = new User($pdo_connection);
// oder
$user = new User(null, [ 'connection' => $pdo_connection ]);
// oder
$user = new User();
$user->setDatabaseConnection($pdo_connection);
Wenn Sie vermeiden möchten, jedes Mal eine $database_connection
festzulegen, wenn Sie einen aktiven Datensatz aufrufen, gibt es Möglichkeiten, dies zu umgehen!
// index.php oder bootstrap.php
// Setzen Sie dies als registrierte Klasse in Flight
Flight::register('db', 'PDO', [ 'sqlite:test.db' ]);
// User.php
class User extends flight\ActiveRecord {
public function __construct(array $config = [])
{
$database_connection = $config['connection'] ?? Flight::db();
parent::__construct($database_connection, 'users', $config);
}
}
// Und jetzt, keine Argumente erforderlich!
$user = new User();
Hinweis: Wenn Sie planen, Unit-Tests durchzuführen, kann es einige Herausforderungen beim Testen mit dieser Methode geben, aber insgesamt ist es nicht zu schlecht, da Sie Ihre Verbindung mit
setDatabaseConnection()
oder$config['connection']
injizieren können.
Wenn Sie die Datenbankverbindung aktualisieren müssen, z. B. wenn Sie ein lang laufendes CLI-Skript ausführen und die Verbindung von Zeit zu Zeit aktualisieren müssen, können Sie die Verbindung mit $your_record->setDatabaseConnection($pdo_connection)
erneut festlegen.
Beitragen
Bitte tun Sie das. :D
Einrichtung
Wenn Sie einen Beitrag leisten, stellen Sie sicher, dass Sie composer test-coverage
ausführen, um 100 % Testabdeckung aufrechtzuerhalten (das ist keine echte Unit-Testabdeckung, mehr wie Integrationstests).
Stellen Sie auch sicher, dass Sie composer beautify
und composer phpcs
ausführen, um alle Linting-Fehler zu beheben.
Lizenz
MIT
Awesome-plugins/latte
Latte
Latte ist ein vollwertiges Templating-Engine, das sehr einfach zu bedienen ist und näher an der PHP-Syntax liegt als Twig oder Smarty. Es ist auch sehr einfach zu erweitern und eigene Filter und Funktionen hinzuzufügen.
Installation
Installieren Sie es mit Composer.
composer require latte/latte
Grundlegende Konfiguration
Es gibt einige grundlegende Konfigurationsoptionen, um zu starten. Sie können mehr darüber in der Latte-Dokumentation lesen.
require 'vendor/autoload.php';
$app = Flight::app();
$app->map('render', function(string $template, array $data, ?string $block): void {
$latte = new Latte\Engine;
// Wo Latte speziell seinen Cache speichert
$latte->setTempDirectory(__DIR__ . '/../cache/');
$finalPath = Flight::get('flight.views.path') . $template;
$latte->render($finalPath, $data, $block);
});
Einfaches Layout-Beispiel
Hier ist ein einfaches Beispiel für eine Layout-Datei. Dies ist die Datei, die verwendet wird, um alle Ihre anderen Views zu umschließen.
<!-- app/views/layout.latte -->
<!doctype html>
<html lang="en">
<head>
<title>{$title ? $title . ' - '}My App</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<header>
<nav>
<!-- Ihre Navigations-Elemente hier -->
</nav>
</header>
<div id="content">
<!-- Hier liegt die Magie -->
{block content}{/block}
</div>
<div id="footer">
© Copyright
</div>
</body>
</html>
Und jetzt haben wir Ihre Datei, die in diesem Content-Block gerendert wird:
<!-- app/views/home.latte -->
<!-- Dies teilt Latte mit, dass diese Datei "innerhalb" der layout.latte-Datei liegt -->
{extends layout.latte}
<!-- Dies ist der Inhalt, der innerhalb des Layouts im Content-Block gerendert wird -->
{block content}
<h1>Startseite</h1>
<p>Willkommen in meiner App!</p>
{/block}
Wenn Sie dies in Ihrer Funktion oder Ihrem Controller rendern, würden Sie etwas Ähnliches tun:
// Einfache Route
Flight::route('/', function () {
Flight::render('home.latte', [
'title' => 'Startseite'
]);
});
// Oder wenn Sie einen Controller verwenden
Flight::route('/', [HomeController::class, 'index']);
// HomeController.php
class HomeController
{
public function index()
{
Flight::render('home.latte', [
'title' => 'Startseite'
]);
}
}
Sehen Sie sich die Latte-Dokumentation für weitere Informationen an, wie Sie Latte in vollem Umfang nutzen können!
Debugging mit Tracy
PHP 8.1+ ist für diesen Abschnitt erforderlich.
Sie können auch Tracy verwenden, um Ihre Latte-Template-Dateien direkt aus der Box heraus zu debuggen! Wenn Sie Tracy bereits installiert haben, müssen Sie die Latte-Erweiterung zu Tracy hinzufügen.
// services.php
use Tracy\Debugger;
$app->map('render', function(string $template, array $data, ?string $block): void {
$latte = new Latte\Engine;
// Wo Latte speziell seinen Cache speichert
$latte->setTempDirectory(__DIR__ . '/../cache/');
$finalPath = Flight::get('flight.views.path') . $template;
// Dies fügt die Erweiterung nur hinzu, wenn die Tracy-Debug-Bar aktiviert ist
if (Debugger::$showBar === true) {
// Hier fügen Sie das Latte-Panel zu Tracy hinzu
$latte->addExtension(new Latte\Bridges\Tracy\TracyExtension);
}
$latte->render($finalPath, $data, $block);
});
Awesome-plugins/awesome_plugins
Tolle Plugins
Flight ist unglaublich erweiterbar. Es gibt eine Reihe von Plugins, die verwendet werden können, um Funktionalität zu Ihrer Flight-Anwendung hinzuzufügen. Einige werden offiziell vom Flight-Team unterstützt, und andere sind Micro/Lite-Bibliotheken, um Ihnen den Einstieg zu erleichtern.
API-Dokumentation
API-Dokumentation ist entscheidend für jede API. Sie hilft Entwicklern zu verstehen, wie sie mit Ihrer API interagieren können und was sie als Rückgabe erwarten können. Es gibt einige Tools, die Ihnen helfen können, API-Dokumentation für Ihre Flight-Projekte zu generieren.
- FlightPHP OpenAPI Generator - Blog-Beitrag von Daniel Schreiber darüber, wie man die OpenAPI-Spezifikation mit FlightPHP verwendet, um Ihre API mit einem API-First-Ansatz aufzubauen.
- SwaggerUI - Swagger UI ist ein großartiges Tool, um API-Dokumentation für Ihre Flight-Projekte zu generieren. Es ist sehr einfach zu verwenden und kann an Ihre Bedürfnisse angepasst werden. Dies ist die PHP-Bibliothek, die Ihnen hilft, die Swagger-Dokumentation zu generieren.
Application Performance Monitoring (APM)
Application Performance Monitoring (APM) ist entscheidend für jede Anwendung. Es hilft Ihnen zu verstehen, wie Ihre Anwendung performt und wo die Engpässe liegen. Es gibt eine Reihe von APM-Tools, die mit Flight verwendet werden können.
- offiziell flightphp/apm - Flight APM ist eine einfache APM-Bibliothek, die verwendet werden kann, um Ihre Flight-Anwendungen zu überwachen. Sie kann verwendet werden, um die Performance Ihrer Anwendung zu überwachen und Ihnen zu helfen, Engpässe zu identifizieren.
Async
Flight ist bereits ein schnelles Framework, aber es mit einem Turbo-Motor auszustatten macht alles noch spaßiger (und herausfordernder)!
- flightphp/async - Offizielle Flight Async-Bibliothek. Diese Bibliothek ist eine einfache Möglichkeit, asynchrone Verarbeitung zu Ihrer Anwendung hinzuzufügen. Sie verwendet Swoole/Openswoole im Hintergrund, um eine einfache und effektive Möglichkeit zu bieten, Aufgaben asynchron auszuführen.
Autorisierung/Berechtigungen
Autorisierung und Berechtigungen sind entscheidend für jede Anwendung, die Steuerungen benötigt, um festzulegen, wer auf was zugreifen kann.
- offiziell flightphp/permissions - Offizielle Flight Permissions-Bibliothek. Diese Bibliothek ist eine einfache Möglichkeit, Benutzer- und Anwendungsebene-Berechtigungen zu Ihrer Anwendung hinzuzufügen.
Caching
Caching ist eine großartige Möglichkeit, Ihre Anwendung zu beschleunigen. Es gibt eine Reihe von Caching-Bibliotheken, die mit Flight verwendet werden können.
- offiziell flightphp/cache - Leichte, einfache und eigenständige PHP-in-File-Caching-Klasse
CLI
CLI-Anwendungen sind eine großartige Möglichkeit, mit Ihrer Anwendung zu interagieren. Sie können sie verwenden, um Controller zu generieren, alle Routen anzuzeigen und mehr.
- offiziell flightphp/runway - Runway ist eine CLI-Anwendung, die Ihnen hilft, Ihre Flight-Anwendungen zu verwalten.
Cookies
Cookies sind eine großartige Möglichkeit, kleine Datenmengen auf der Client-Seite zu speichern. Sie können verwendet werden, um Benutzereinstellungen, Anwendungseinstellungen und mehr zu speichern.
- overclokk/cookie - PHP Cookie ist eine PHP-Bibliothek, die eine einfache und effektive Möglichkeit bietet, Cookies zu verwalten.
Debugging
Debugging ist entscheidend, wenn Sie in Ihrer lokalen Umgebung entwickeln. Es gibt einige Plugins, die Ihr Debugging-Erlebnis verbessern können.
- tracy/tracy - Dies ist ein vollständiges Fehlerbehandlungstool, das mit Flight verwendet werden kann. Es hat eine Reihe von Panels, die Ihnen helfen können, Ihre Anwendung zu debuggen. Es ist auch sehr einfach zu erweitern und eigene Panels hinzuzufügen.
- offiziell flightphp/tracy-extensions - In Verbindung mit dem Tracy Fehlerbehandlungstool fügt dieses Plugin einige zusätzliche Panels hinzu, um das Debugging speziell für Flight-Projekte zu erleichtern.
Datenbanken
Datenbanken sind der Kern der meisten Anwendungen. So speichern und rufen Sie Daten ab. Einige Datenbankbibliotheken sind einfach Wrapper, um Abfragen zu schreiben, und einige sind vollwertige ORMs.
- offiziell flightphp/core PdoWrapper - Offizieller Flight PDO-Wrapper, der zum Kern gehört. Dies ist ein einfacher Wrapper, der den Prozess des Schreibens und Ausführens von Abfragen vereinfacht. Es handelt sich nicht um ein ORM.
- offiziell flightphp/active-record - Offizielles Flight ActiveRecord ORM/Mapper. Tolle kleine Bibliothek, um Daten einfach in Ihrer Datenbank abzurufen und zu speichern.
- byjg/php-migration - Plugin, um alle Datenbankänderungen für Ihr Projekt zu verfolgen.
Verschlüsselung
Verschlüsselung ist entscheidend für jede Anwendung, die sensible Daten speichert. Das Verschlüsseln und Entschlüsseln der Daten ist nicht besonders schwer, aber das ordnungsgemäße Speichern des Verschlüsselungsschlüssels kann sein schwierig. Das Wichtigste ist, Ihren Verschlüsselungsschlüssel niemals in einem öffentlichen Verzeichnis zu speichern oder ihn in Ihr Code-Repository zu committen.
- defuse/php-encryption - Dies ist eine Bibliothek, die verwendet werden kann, um Daten zu verschlüsseln und zu entschlüsseln. Der Einstieg ist ziemlich einfach, um mit dem Verschlüsseln und Entschlüsseln von Daten zu beginnen.
Job Queue
Job-Warteschlangen sind wirklich hilfreich, um Aufgaben asynchron zu verarbeiten. Das kann das Versenden von E-Mails, die Verarbeitung von Bildern oder alles sein, was nicht in Echtzeit erledigt werden muss.
- n0nag0n/simple-job-queue - Simple Job Queue ist eine Bibliothek, die verwendet werden kann, um Jobs asynchron zu verarbeiten. Sie kann mit beanstalkd, MySQL/MariaDB, SQLite und PostgreSQL verwendet werden.
Session
Sessions sind für APIs nicht wirklich nützlich, aber beim Aufbau einer Web-Anwendung können Sessions entscheidend für die Aufrechterhaltung des Zustands und Login-Informationen sein.
- offiziell flightphp/session - Offizielle Flight Session-Bibliothek. Dies ist eine einfache Session-Bibliothek, die verwendet werden kann, um Session-Daten zu speichern und abzurufen. Sie verwendet die integrierte Session-Behandlung von PHP.
- Ghostff/Session - PHP Session Manager (nicht-blockierend, Flash, Segment, Session-Verschlüsselung). Verwendet PHP open_ssl für optionale Verschlüsselung/Entschlüsselung von Session-Daten.
Templating
Templating ist der Kern jeder Web-Anwendung mit einer UI. Es gibt eine Reihe von Templating-Engines, die mit Flight verwendet werden können.
- veraltet flightphp/core View - Dies ist eine sehr grundlegende Templating-Engine, die zum Kern gehört. Es wird nicht empfohlen, sie zu verwenden, wenn Ihr Projekt mehr als ein paar Seiten hat.
- latte/latte - Latte ist eine vollständige Templating-Engine, die sehr einfach zu verwenden ist und sich näher an der PHP-Syntax anfühlt als Twig oder Smarty. Sie ist auch sehr einfach zu erweitern und eigene Filter und Funktionen hinzuzufügen.
WordPress-Integration
Möchten Sie Flight in Ihrem WordPress-Projekt verwenden? Es gibt ein praktisches Plugin dafür!
- n0nag0n/wordpress-integration-for-flight-framework - Dieses WordPress-Plugin ermöglicht es Ihnen, Flight direkt neben WordPress auszuführen. Es ist perfekt, um benutzerdefinierte APIs, Microservices oder sogar vollständige Apps zu Ihrer WordPress-Site mit dem Flight-Framework hinzuzufügen. Super nützlich, wenn Sie das Beste aus beiden Welten wollen!
Beitrag
Haben Sie ein Plugin, das Sie teilen möchten? Reichen Sie einen Pull Request ein, um es zur Liste hinzuzufügen!
Media
Medien
Wir haben versucht, so viel wie möglich von den verschiedenen Medientypen im Internet rund um Flight zu finden. Siehe unten für verschiedene Ressourcen, die Sie nutzen können, um mehr über Flight zu erfahren.
Artikel und Berichte
- Unit Testing and SOLID Principles von Brian Fenton (2015?)
- PHP Web Framework Flight von ojambo (2025)
- Define, Generate, and Implement: An API-First Approach with OpenAPI Generator and FlightPHP von Daniel Schreiber (2025)
- Best PHP Micro Frameworks for 2024 von n0nag0n (2024)
- Creating a RESTful API with Flight Framework von n0nag0n (2024)
- Building a Simple Blog with Flight Part 2 von n0nag0n (2024)
- Building a Simple Blog with Flight Part 1 von n0nag0n (2024)
- 🚀 Build a Simple CRUD API in PHP with the Flight Framework von soheil-khaledabadi (2024)
- Building a PHP Web Application with the Flight Micro-framework von Arthur C. Codex (2023)
- Best PHP Frameworks for Web Development in 2024 von Ravikiran A S (2023)
- Top 12 PHP Frameworks: A Comprehensive Guide for 2023 von marketing kbk (2023)
- 5 PHP Frameworks You've (Probably) Never Heard of von n0nag0n (2022)
- 12 top PHP frameworks for web developers to consider in 2023 von Anna Monus (2022)
- The Best PHP Microframeworks on a Cloud Server von Shahzeb Ahmed (2021)
- PHP framework: Top 15 powerful ones for your web development von AHT Tech (2020)
- Easy PHP Routing with FlightPHP von Lucas Conceição (2019)
- Trying Out New PHP Framework (Flight) von Leon (2017)
- Setting up FlightPHP to work with Backbonejs von Timothy Tocci (2015)
Videos und Tutorials
- Build a Flight PHP App with MVC & MariaDB in 10 Minutes! (Beginner Friendly) von ojamboshop (2025)
- Create a REST API for IoT Devices Using PHP & FlightPHP - ESP32 API von IoT Craft Hub (2024)
- PHP Flight Framework Simple Introductory Video von n0nag0n (2024)
- Set header HTTP code in Flightphp (3 Solutions!!) von Roel Van de Paar (2024)
- PHP Flight Framework Tutorial. Super easy API Project! von n0nag0n (2022)
- Aplicación web CRUD con php y mysql y bootstrap usando flight von Devlopteca - Oscar Uh (2021)
- DevOps & SysAdmins: Lighttpd rewrite rule for Flight PHP microframework von Roel Van de Paar (2021)
- Tutorial REST API Flight PHP #PART2 INSERT TABLE Info #Code (Tagalog) von Info Singkat Official (2020)
- Tutorial REST API Flight PHP #PART1 Info #Code (Tagalog) von Info Singkat Official (2020)
- How To Create JSON REST API IN PHP - Part 2 von Codewife (2018)
- How To Create JSON REST API IN PHP - Part 1 von Codewife (2018)
- Teste Micro Frameworks PHP - Flight PHP, Lumen, Slim 3 e Laravel von Codemarket (2016)
- Tutorial 1 Flight PHP - Instalación von absagg (2014)
- Tutorial 2 Flight PHP - Route parte 1 von absagg (2014)
Fehlt etwas?
Fehlt etwas, das Sie geschrieben oder aufgenommen haben? Lassen Sie es uns mit einem Issue oder Pull Request wissen!
Examples
Schneller Einstieg?
Sie haben zwei Optionen, um mit einem neuen Flight-Projekt zu starten:
- Full Skeleton Boilerplate: Ein umfassenderes Beispiel mit Controllern und Views.
- Single File Skeleton Boilerplate: Eine einzelne Datei, die alles enthält, was Sie benötigen, um Ihre App in einer einfachen einzigen Datei auszuführen.
Community-beigetragene Beispiele:
- flightravel: FlightPHP mit Laravel-Verzeichnissen, mit PHP-Tools + GH Actions
- fleact - Ein FlightPHP-Starter-Kit mit ReactJS-Integration.
- flastro - Ein FlightPHP-Starter-Kit mit Astro-Integration.
- velt - Velt ist eine schnelle und einfache Svelte-Starter-Vorlage mit einem FlightPHP-Backend.
Brauchen Sie Inspiration?
Obwohl diese nicht offiziell von dem Flight-Team gesponsert werden, könnten sie Ihnen Ideen geben, wie Sie Ihre eigenen Projekte strukturieren, die mit Flight aufgebaut sind!
- Ivox Car Rental - Ivox Car Rental ist eine einseitige, mobilfreundliche Autovermietungs-Web-Anwendung, die mit PHP (FlightPHP), JavaScript und MySQL aufgebaut wurde. Sie unterstützt Benutzerregistrierung, Durchsuchen und Buchen von Autos, während Admins Autos, Benutzer und Buchungen verwalten können. Die App verfügt über eine REST-API, JWT-Authentifizierung und ein responsives Design für ein modernes Vermietungserlebnis.
- Decay - Flight v3 mit HTMX und SleekDB, alles über Zombies! (Demo)
- Flight Example Blog - Flight v3 mit Middleware, Controllern, Active Record und Latte.
- Flight CRUD RESTful API - Einfaches CRUD-API-Projekt mit dem Flight-Framework, das eine grundlegende Struktur für neue Benutzer bietet, um schnell eine PHP-Anwendung mit CRUD-Operationen und Datenbankverbindung einzurichten. Das Projekt demonstriert, wie man Flight für die RESTful-API-Entwicklung verwendet, was es zu einem idealen Lernwerkzeug für Anfänger und einem nützlichen Starter-Kit für erfahrene Entwickler macht.
- Flight School Management System - Flight v3
- Paste Bin with Comments - Flight v3
- Basic Skeleton App
- Example Wiki
- The IT-Innovator PHP Framework Application
- LittleEducationalCMS (Spanish)
- Italian Yellow Pages API
- Generic Content Management System (with....very little documentation)
- A tiny php framework based on Flight and medoo.
- Example MVC Application
- Production ready Flight Boilerplate - Produktionsreifes Authentifizierungs-Framework, das Ihnen Wochen der Entwicklung erspart. Es bietet unternehmensklasse Sicherheit: 2FA/TOTP, LDAP-Integration, Azure SSO, intelligente Ratenbegrenzung, Sitzungs-Fingerprinting, Schutz vor Brute-Force-Angriffen, Sicherheits-Analytics-Dashboard, umfassende Audit-Protokollierung und granulare rollenbasierte Zugriffssteuerung.
Möchten Sie Ihr eigenes Beispiel teilen?
Wenn Sie ein Projekt haben, das Sie teilen möchten, reichen Sie bitte einen Pull Request ein, um es zu dieser Liste hinzuzufügen!
Install/install
Installationsanweisungen
Es gibt einige grundlegende Voraussetzungen, bevor Sie Flight installieren können. Insbesondere benötigen Sie:
- Installieren Sie PHP auf Ihrem System
- Installieren Sie Composer für das beste Entwicklererlebnis.
Basisinstallation
Wenn Sie Composer verwenden, können Sie den folgenden Befehl ausführen:
composer require flightphp/core
Dies platziert nur die Flight-Kern-Dateien auf Ihrem System. Sie müssen die Projektstruktur definieren, Layout, Abhängigkeiten, Konfigurationen, Autoloading usw. Diese Methode stellt sicher, dass keine anderen Abhängigkeiten außer Flight installiert werden.
Sie können die Dateien auch direkt herunterladen und sie in Ihr Web-Verzeichnis extrahieren.
Empfohlene Installation
Es wird dringend empfohlen, mit der flightphp/skeleton-App für alle neuen Projekte zu beginnen. Die Installation ist kinderleicht.
composer create-project flightphp/skeleton my-project/
Dies richtet Ihre Projektstruktur ein, konfiguriert Autoloading mit Namespaces, richtet eine Konfiguration ein und stellt andere Tools wie Tracy, Tracy Extensions und Runway bereit.
Konfigurieren Sie Ihren Webserver
Eingebauter PHP-Entwicklungsserver
Dies ist bei weitem der einfachste Weg, um loszulegen. Sie können den eingebauten Server verwenden, um Ihre Anwendung auszuführen, und sogar SQLite für eine Datenbank verwenden (solange sqlite3 auf Ihrem System installiert ist) und fast nichts anderes benötigen! Führen Sie einfach den folgenden Befehl aus, sobald PHP installiert ist:
php -S localhost:8000
# oder mit der Skeleton-App
composer start
Öffnen Sie dann Ihren Browser und gehen Sie zu http://localhost:8000
.
Wenn Sie das Dokumentenroot Ihres Projekts zu einem anderen Verzeichnis machen möchten (z. B. Ihr Projekt ist ~/myproject
, aber Ihr Dokumentenroot ist ~/myproject/public/
), können Sie den folgenden Befehl ausführen, sobald Sie sich im ~/myproject
-Verzeichnis befinden:
php -S localhost:8000 -t public/
# mit der Skeleton-App ist dies bereits konfiguriert
composer start
Öffnen Sie dann Ihren Browser und gehen Sie zu http://localhost:8000
.
Apache
Stellen Sie sicher, dass Apache bereits auf Ihrem System installiert ist. Falls nicht, googeln Sie, wie Sie Apache auf Ihrem System installieren.
Für Apache bearbeiten Sie Ihre .htaccess
-Datei mit dem Folgenden:
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php [QSA,L]
Hinweis: Wenn Sie Flight in einem Unterverzeichnis verwenden müssen, fügen Sie die Zeile hinzu
RewriteBase /subdir/
direkt nachRewriteEngine On
.Hinweis: Wenn Sie alle Serverdateien schützen möchten, wie eine db- oder env-Datei. Fügen Sie dies in Ihre
.htaccess
-Datei ein:
RewriteEngine On
RewriteRule ^(.*)$ index.php
Nginx
Stellen Sie sicher, dass Nginx bereits auf Ihrem System installiert ist. Falls nicht, googeln Sie, wie Sie Nginx auf Ihrem System installieren.
Für Nginx fügen Sie das Folgende zu Ihrer Server-Deklaration hinzu:
server {
location / {
try_files $uri $uri/ /index.php;
}
}
Erstellen Sie Ihre index.php
-Datei
Wenn Sie eine Basisinstallation durchführen, benötigen Sie etwas Code, um zu starten.
<?php
// Wenn Sie Composer verwenden, laden Sie den Autoloader.
// if you're using Composer, require the autoloader.
require 'vendor/autoload.php';
// wenn Sie Composer nicht verwenden, laden Sie das Framework direkt
// if you're not using Composer, load the framework directly
// require 'flight/Flight.php';
// Definieren Sie dann eine Route und weisen Sie eine Funktion zu, um die Anfrage zu handhaben.
Flight::route('/', function () {
echo 'hello world!';
});
// Starten Sie schließlich das Framework.
Flight::start();
Mit der Skeleton-App ist dies bereits konfiguriert und in Ihrer app/config/routes.php
-Datei gehandhabt. Dienste werden in app/config/services.php
konfiguriert.
PHP installieren
Wenn Sie bereits php
auf Ihrem System installiert haben, überspringen Sie diese Anweisungen und gehen Sie zum Download-Bereich.
macOS
PHP mit Homebrew installieren
-
Installieren Sie Homebrew (falls noch nicht installiert):
- Öffnen Sie das Terminal und führen Sie aus:
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
- Öffnen Sie das Terminal und führen Sie aus:
-
Installieren Sie PHP:
- Installieren Sie die neueste Version:
brew install php
- Um eine spezifische Version zu installieren, z. B. PHP 8.1:
brew tap shivammathur/php brew install shivammathur/php/php@8.1
- Installieren Sie die neueste Version:
-
Wechseln Sie zwischen PHP-Versionen:
- Verknüpfen Sie die aktuelle Version und verknüpfen Sie die gewünschte Version:
brew unlink php brew link --overwrite --force php@8.1
- Überprüfen Sie die installierte Version:
php -v
- Verknüpfen Sie die aktuelle Version und verknüpfen Sie die gewünschte Version:
Windows 10/11
PHP manuell installieren
-
PHP herunterladen:
- Besuchen Sie PHP for Windows und laden Sie die neueste oder eine spezifische Version (z. B. 7.4, 8.0) als nicht-thread-sichere ZIP-Datei herunter.
-
PHP extrahieren:
- Extrahieren Sie die heruntergeladene ZIP-Datei nach
C:\php
.
- Extrahieren Sie die heruntergeladene ZIP-Datei nach
-
PHP zum System-PATH hinzufügen:
- Gehen Sie zu Systemeigenschaften > Umgebungsvariablen.
- Unter Systemvariablen finden Sie Path und klicken Sie auf Bearbeiten.
- Fügen Sie den Pfad
C:\php
(oder wo Sie PHP extrahiert haben) hinzu. - Klicken Sie auf OK, um alle Fenster zu schließen.
-
PHP konfigurieren:
- Kopieren Sie
php.ini-development
zuphp.ini
. - Bearbeiten Sie
php.ini
, um PHP wie benötigt zu konfigurieren (z. B.extension_dir
einstellen, Erweiterungen aktivieren).
- Kopieren Sie
-
PHP-Installation überprüfen:
- Öffnen Sie die Eingabeaufforderung und führen Sie aus:
php -v
- Öffnen Sie die Eingabeaufforderung und führen Sie aus:
Mehrere Versionen von PHP installieren
-
Wiederholen Sie die obigen Schritte für jede Version und platzieren Sie jede in einem separaten Verzeichnis (z. B.
C:\php7
,C:\php8
). -
Wechseln Sie zwischen Versionen, indem Sie die System-PATH-Variable anpassen, um auf das gewünschte Versionsverzeichnis zu verweisen.
Ubuntu (20.04, 22.04 usw.)
PHP mit apt installieren
-
Paketlisten aktualisieren:
- Öffnen Sie das Terminal und führen Sie aus:
sudo apt update
- Öffnen Sie das Terminal und führen Sie aus:
-
PHP installieren:
- Installieren Sie die neueste PHP-Version:
sudo apt install php
- Um eine spezifische Version zu installieren, z. B. PHP 8.1:
sudo apt install php8.1
- Installieren Sie die neueste PHP-Version:
-
Zusätzliche Module installieren (optional):
- Zum Beispiel, um MySQL-Unterstützung zu installieren:
sudo apt install php8.1-mysql
- Zum Beispiel, um MySQL-Unterstützung zu installieren:
-
Wechseln Sie zwischen PHP-Versionen:
- Verwenden Sie
update-alternatives
:sudo update-alternatives --set php /usr/bin/php8.1
- Verwenden Sie
-
Die installierte Version überprüfen:
- Führen Sie aus:
php -v
- Führen Sie aus:
Rocky Linux
PHP mit yum/dnf installieren
-
Aktivieren Sie das EPEL-Repository:
- Öffnen Sie das Terminal und führen Sie aus:
sudo dnf install epel-release
- Öffnen Sie das Terminal und führen Sie aus:
-
Installieren Sie das Remi-Repository:
- Führen Sie aus:
sudo dnf install https://rpms.remirepo.net/enterprise/remi-release-8.rpm sudo dnf module reset php
- Führen Sie aus:
-
PHP installieren:
- Um die Standardversion zu installieren:
sudo dnf install php
- Um eine spezifische Version zu installieren, z. B. PHP 7.4:
sudo dnf module install php:remi-7.4
- Um die Standardversion zu installieren:
-
Wechseln Sie zwischen PHP-Versionen:
- Verwenden Sie den
dnf
-Modulbefehl:sudo dnf module reset php sudo dnf module enable php:remi-8.0 sudo dnf install php
- Verwenden Sie den
-
Die installierte Version überprüfen:
- Führen Sie aus:
php -v
- Führen Sie aus:
Allgemeine Hinweise
- Für Entwicklungsumgebungen ist es wichtig, PHP-Einstellungen gemäß den Anforderungen Ihres Projekts zu konfigurieren.
- Beim Wechseln von PHP-Versionen stellen Sie sicher, dass alle relevanten PHP-Erweiterungen für die spezifische Version installiert sind, die Sie verwenden möchten.
- Starten Sie Ihren Webserver (Apache, Nginx usw.) neu, nachdem Sie PHP-Versionen gewechselt oder Konfigurationen aktualisiert haben, um die Änderungen anzuwenden.
Guides
Anleitungen
Flight PHP ist so konzipiert, dass es einfach und doch leistungsstark ist, und unsere Anleitungen helfen Ihnen, reale Anwendungen schrittweise zu erstellen. Diese praktischen Tutorials führen Sie durch vollständige Projekte, um zu demonstrieren, wie Flight effektiv eingesetzt werden kann.
Offizielle Anleitungen
Erstellen eines Blogs
Lernen Sie, wie Sie eine funktionale Blog-Anwendung mit Flight PHP erstellen. Diese Anleitung führt Sie durch:
- Einrichten einer Projektstruktur
- Arbeiten mit Vorlagen unter Verwendung von Latte
- Implementieren von Routen für Beiträge
- Speichern und Abrufen von Daten
- Behandlung von Formularübermittlungen
- Grundlegende Fehlerbehandlung
Dieses Tutorial eignet sich perfekt für Anfänger, die sehen möchten, wie alle Teile in einer realen Anwendung zusammenpassen.
Einheitstests und SOLID-Prinzipien
Diese Anleitung behandelt die Grundlagen der Einheitstests in Flight PHP-Anwendungen. Dazu gehören:
- Einrichten von PHPUnit
- Schreiben von testbarem Code unter Verwendung der SOLID-Prinzipien
- Simulieren von Abhängigkeiten
- Häufige Fehlerfallen, die vermieden werden sollten
- Skalieren Ihrer Tests, wenn Ihre Anwendung wächst Dieses Tutorial ist ideal für Entwickler, die die Codequalität und -wartbarkeit verbessern möchten.
Unoffizielle Anleitungen
Obwohl diese Anleitungen nicht offiziell vom Flight-Team gepflegt werden, sind sie wertvolle Ressourcen, die von der Community erstellt wurden. Sie decken verschiedene Themen und Anwendungsfälle ab und bieten zusätzliche Einblicke in die Nutzung von Flight PHP.
Erstellen einer RESTful API mit Flight Framework
Diese Anleitung führt Sie durch das Erstellen einer RESTful API unter Verwendung des Flight PHP-Frameworks. Sie behandelt die Grundlagen der API-Einrichtung, Definieren von Routen und Rückgabe von JSON-Antworten.
Erstellen eines einfachen Blogs
Diese Anleitung führt Sie durch das Erstellen eines grundlegenden Blogs unter Verwendung des Flight PHP-Frameworks. Es gibt tatsächlich zwei Teile: Einen für die Grundlagen und einen weiteren für fortgeschrittene Themen und Verbesserungen für einen produktionsreifen Blog.
- Erstellen eines einfachen Blogs mit Flight - Teil 1 - Einstieg in einen einfachen Blog.
- Erstellen eines einfachen Blogs mit Flight - Teil 2 - Verfeinern des Blogs für die Produktion.
Erstellen einer Pokémon API in PHP: Ein Leitfaden für Anfänger
Diese unterhaltsame Anleitung führt Sie durch das Erstellen einer einfachen Pokémon API unter Verwendung von Flight PHP. Sie behandelt die Grundlagen der API-Einrichtung, Definieren von Routen und Rückgabe von JSON-Antworten.
Beitrag leisten
Haben Sie eine Idee für eine Anleitung? Einen Fehler gefunden? Wir freuen uns über Beiträge! Unsere Anleitungen werden im FlightPHP-Dokumentationsrepository gepflegt.
Falls Sie etwas Interessantes mit Flight erstellt haben und es als Anleitung teilen möchten, reichen Sie bitte einen Pull-Request ein. Das Teilen Ihres Wissens hilft der Flight-Community zu wachsen.
Suche nach API-Dokumentation?
Falls Sie spezifische Informationen zu Flights Kernfunktionen und Methoden suchen, schauen Sie in den Learn-Bereich unserer Dokumentation.