Learn/flight_vs_laravel
Flight vs Laravel
Qu'est-ce que Laravel ?
Laravel est un framework complet qui possède toutes les fonctionnalités avancées et un écosystème axé sur les développeurs impressionnant, mais au prix d'une performance et d'une complexité élevées. L'objectif de Laravel est que le développeur atteigne le plus haut niveau de productivité et que les tâches courantes soient facilitées. Laravel est un excellent choix pour les développeurs qui souhaitent construire une application web complète et d'entreprise. Cela implique toutefois certains compromis, notamment en termes de performance et de complexité. Apprendre les bases de Laravel peut être facile, mais acquérir une maîtrise du framework peut prendre du temps.
Il existe également de nombreux modules Laravel, ce qui fait que les développeurs ont souvent l'impression que la seule façon de résoudre les problèmes est d'utiliser ces modules, alors qu'en réalité, il serait possible d'utiliser une autre bibliothèque ou d'écrire son propre code.
Avantages par rapport à Flight
- Laravel dispose d'un énorme écosystème de développeurs et de modules qui peuvent être utilisés pour résoudre les problèmes courants.
- Laravel possède un ORM complet qui peut être utilisé pour interagir avec votre base de données.
- Laravel offre une quantité folle de documentation et de tutoriels qui peuvent être utilisés pour apprendre le framework. Cela peut être positif pour plonger dans les détails ou négatif parce qu'il y a tant de choses à parcourir.
- Laravel inclut un système d'authentification intégré qui peut être utilisé pour sécuriser votre application.
- Laravel propose des podcasts, des conférences, des réunions, des vidéos et d'autres ressources qui peuvent être utilisées pour apprendre le framework.
- Laravel est conçu pour un développeur expérimenté qui cherche à construire une application web complète et d'entreprise.
Inconvénients par rapport à Flight
- Laravel a beaucoup plus d'éléments sous le capot que Flight. Cela entraîne un coût dramatique en termes de performance. Consultez les benchmarks TechEmpower pour plus d'informations.
- Flight est conçu pour un développeur qui cherche à construire une application web légère, rapide et facile à utiliser.
- Flight privilégie la simplicité et la facilité d'utilisation.
- L'une des fonctionnalités principales de Flight est qu'il fait de son mieux pour maintenir la compatibilité descendante. Laravel provoque beaucoup de frustrations entre les versions majeures.
- Flight est destiné aux développeurs qui s'aventurent pour la première fois dans le monde des frameworks.
- Flight n'a aucune dépendance, alors que Laravel a un nombre atroce de dépendances
- Flight peut également gérer des applications de niveau entreprise, mais il n'a pas autant de code boilerplate que Laravel. Cela nécessitera également plus de discipline de la part du développeur pour garder les choses organisées et bien structurées.
- Flight donne au développeur plus de contrôle sur l'application, alors que Laravel cache une multitude de magie en arrière-plan qui peut être frustrante.
Learn/migrating_to_v3
Migration vers v3
La compatibilité arrière a été maintenue dans l'ensemble, mais il y a certains changements dont vous devriez être conscient lors de la migration de v2 vers v3. Il y a des changements qui entraient trop en conflit avec les patrons de conception, donc certains ajustements ont dû être faits.
Comportement de mise en tampon de sortie
v3.5.0
Output buffering est le processus par lequel la sortie générée par un script PHP est stockée dans un tampon (interne à PHP) avant d'être envoyée au client. Cela vous permet de modifier la sortie avant qu'elle ne soit envoyée au client.
Dans une application MVC, le Contrôleur est le « gestionnaire » et il gère ce que fait la vue. Avoir une sortie générée en dehors du contrôleur (ou dans le cas de Flight, parfois une fonction anonyme) rompt le patron MVC. Ce changement vise à être plus en ligne avec le patron MVC et à rendre le framework plus prévisible et plus facile à utiliser.
En v2, la mise en tampon de sortie était gérée d'une manière où elle ne fermait pas de manière cohérente son propre tampon de sortie, ce qui rendait les tests unitaires et le streaming plus difficiles. Pour la majorité des utilisateurs, ce changement ne vous affectera peut-être pas réellement. Cependant, si vous affichez du contenu en dehors des callables et des contrôleurs (par exemple dans un hook), vous risquez d'avoir des problèmes. Afficher du contenu dans les hooks, et avant que le framework ne s'exécute réellement, pouvait fonctionner dans le passé, mais cela ne fonctionnera plus à l'avenir.
Où vous pourriez avoir des problèmes
// index.php
require 'vendor/autoload.php';
// just an example
define('START_TIME', microtime(true));
function hello() {
echo 'Hello World';
}
Flight::map('hello', 'hello');
Flight::after('hello', function(){
// this will actually be fine
echo '<p>This Hello World phrase was brought to you by the letter "H"</p>';
});
Flight::before('start', function(){
// things like this will cause an error
echo '<html><head><title>My Page</title></head><body>';
});
Flight::route('/', function(){
// this is actually just fine
echo 'Hello World';
// This should be just fine as well
Flight::hello();
});
Flight::after('start', function(){
// this will cause an error
echo '<div>Your page loaded in '.(microtime(true) - START_TIME).' seconds</div></body></html>';
});
Activation du comportement de rendu v2
Pouvez-vous encore garder votre ancien code tel quel sans faire de réécriture pour le faire fonctionner avec v3 ? Oui, vous le pouvez ! Vous pouvez activer le comportement de rendu v2 en définissant l'option de configuration flight.v2.output_buffering
à true
. Cela vous permettra de continuer à utiliser l'ancien comportement de rendu, mais il est recommandé de le corriger à l'avenir. En v4 du framework, cela sera supprimé.
// index.php
require 'vendor/autoload.php';
Flight::set('flight.v2.output_buffering', true);
Flight::before('start', function(){
// Now this will be just fine
echo '<html><head><title>My Page</title></head><body>';
});
// more code
Changements du Dispatcher
v3.7.0
Si vous avez directement appelé des méthodes statiques pour Dispatcher
telles que Dispatcher::invokeMethod()
, Dispatcher::execute()
, etc., vous devrez mettre à jour votre code pour ne plus appeler directement ces méthodes. Dispatcher
a été converti pour être plus orienté objet afin que les Conteneurs d'Injection de Dépendances puissent être utilisés plus facilement. Si vous avez besoin d'invoquer une méthode de manière similaire à ce que faisait Dispatcher, vous pouvez manuellement utiliser quelque chose comme $result = $class->$method(...$params);
ou call_user_func_array()
à la place.
Changements de halt()
stop()
redirect()
et error()
v3.10.0
Le comportement par défaut avant 3.10.0 était d'effacer à la fois les en-têtes et le corps de la réponse. Cela a été changé pour ne vider que le corps de la réponse. Si vous avez besoin d'effacer également les en-têtes, vous pouvez utiliser Flight::response()->clear()
.
Learn/configuration
Configuration
Aperçu
Flight fournit un moyen simple de configurer divers aspects du framework pour répondre aux besoins de votre application. Certains sont définis par défaut, mais vous pouvez les remplacer au besoin. Vous pouvez également définir vos propres variables pour les utiliser dans toute votre application.
Comprendre
Vous pouvez personnaliser certains comportements de Flight en définissant des valeurs de configuration
via la méthode set
.
Flight::set('flight.log_errors', true);
Dans le fichier app/config/config.php
, vous pouvez voir toutes les variables de configuration par défaut disponibles.
Utilisation de base
Options de configuration Flight
Voici une liste de toutes les options de configuration disponibles :
- flight.base_url
?string
- Remplacer l'URL de base de la requête si Flight s'exécute dans un sous-répertoire. (par défaut : null) - flight.case_sensitive
bool
- Correspondance sensible à la casse pour les URL. (par défaut : false) - flight.handle_errors
bool
- Permettre à Flight de gérer toutes les erreurs en interne. (par défaut : true)- Si vous voulez que Flight gère les erreurs au lieu du comportement par défaut de PHP, cela doit être true.
- Si vous avez Tracy installé, vous voulez définir cela à false pour que Tracy puisse gérer les erreurs.
- Si vous avez le plugin APM installé, vous voulez définir cela à true pour que l'APM puisse journaliser les erreurs.
- flight.log_errors
bool
- Journaliser les erreurs dans le fichier de journal des erreurs du serveur web. (par défaut : false)- Si vous avez Tracy installé, Tracy journalisera les erreurs en fonction des configurations de Tracy, pas de cette configuration.
- flight.views.path
string
- Répertoire contenant les fichiers de modèle de vue. (par défaut : ./views) - flight.views.extension
string
- Extension des fichiers de modèle de vue. (par défaut : .php) - flight.content_length
bool
- Définir l'en-têteContent-Length
. (par défaut : true)- Si vous utilisez Tracy, cela doit être défini à false pour que Tracy puisse s'afficher correctement.
- flight.v2.output_buffering
bool
- Utiliser le tamponnage de sortie legacy. Voir migration vers v3. (par défaut : false)
Configuration du chargeur
Il y a en outre une autre option de configuration pour le chargeur. Cela vous permettra
de charger automatiquement les classes avec _
dans le nom de la classe.
// Activer le chargement de classes avec des underscores
// Par défaut true
Loader::$v2ClassLoading = false;
Variables
Flight vous permet de sauvegarder des variables pour qu'elles puissent être utilisées n'importe où dans votre application.
// Sauvegarder votre variable
Flight::set('id', 123);
// Ailleurs dans votre application
$id = Flight::get('id');
Pour voir si une variable a été définie, vous pouvez faire :
if (Flight::has('id')) {
// Faire quelque chose
}
Vous pouvez effacer une variable en faisant :
// Efface la variable id
Flight::clear('id');
// Efface toutes les variables
Flight::clear();
Note : Le fait que vous puissiez définir une variable ne signifie pas que vous devriez le faire. Utilisez cette fonctionnalité avec parcimonie. La raison est que tout ce qui est stocké ici devient une variable globale. Les variables globales sont mauvaises car elles peuvent être modifiées de n'importe où dans votre application, rendant difficile la traque des bugs. De plus, cela peut compliquer des choses comme les tests unitaires.
Erreurs et exceptions
Toutes les erreurs et exceptions sont capturées par Flight et passées à la méthode error
.
si flight.handle_errors
est défini à true.
Le comportement par défaut est d'envoyer une réponse générique HTTP 500 Internal Server Error
avec des informations d'erreur.
Vous pouvez remplacer ce comportement pour vos propres besoins :
Flight::map('error', function (Throwable $error) {
// Gérer l'erreur
echo $error->getTraceAsString();
});
Par défaut, les erreurs ne sont pas journalisées sur le serveur web. Vous pouvez activer cela en modifiant la configuration :
Flight::set('flight.log_errors', true);
404 Non trouvé
Quand une URL ne peut pas être trouvée, Flight appelle la méthode notFound
. Le comportement
par défaut est d'envoyer une réponse HTTP 404 Not Found
avec un message simple.
Vous pouvez remplacer ce comportement pour vos propres besoins :
Flight::map('notFound', function () {
// Gérer non trouvé
});
Voir aussi
- Étendre Flight - Comment étendre et personnaliser les fonctionnalités de base de Flight.
- Tests unitaires - Comment écrire des tests unitaires pour votre application Flight.
- Tracy - Un plugin pour la gestion avancée des erreurs et le débogage.
- Extensions Tracy - Extensions pour intégrer Tracy avec Flight.
- APM - Un plugin pour la surveillance des performances de l'application et le suivi des erreurs.
Dépannage
- Si vous avez des problèmes pour trouver toutes les valeurs de votre configuration, vous pouvez faire
var_dump(Flight::get());
Journal des modifications
- v3.5.0 - Ajout de la configuration pour
flight.v2.output_buffering
pour supporter le comportement de tamponnage de sortie legacy. - v2.0 - Configurations de base ajoutées.
Learn/ai
IA & Expérience Développeur avec Flight
Aperçu
Flight facilite le renforcement de vos projets PHP avec des outils alimentés par l'IA et des flux de travail modernes pour les développeurs. Avec des commandes intégrées pour se connecter aux fournisseurs de LLM (Large Language Model) et générer des instructions de codage IA spécifiques au projet, Flight vous aide, vous et votre équipe, à tirer le meilleur parti des assistants IA comme GitHub Copilot, Cursor et Windsurf.
Compréhension
Les assistants de codage IA sont les plus utiles lorsqu'ils comprennent le contexte, les conventions et les objectifs de votre projet. Les aides IA de Flight vous permettent de :
- Connecter votre projet à des fournisseurs de LLM populaires (OpenAI, Grok, Claude, etc.)
- Générer et mettre à jour des instructions spécifiques au projet pour les outils IA, afin que tout le monde reçoive une aide cohérente et pertinente
- Maintenir votre équipe alignée et productive, avec moins de temps passé à expliquer le contexte
Ces fonctionnalités sont intégrées au CLI principal de Flight et au projet de démarrage officiel flightphp/skeleton.
Utilisation de Base
Configuration des Identifiants LLM
La commande ai:init
vous guide à travers la connexion de votre projet à un fournisseur de LLM.
php runway ai:init
Vous serez invité à :
- Choisir votre fournisseur (OpenAI, Grok, Claude, etc.)
- Entrer votre clé API
- Définir l'URL de base et le nom du modèle
Cela crée un fichier .runway-creds.json
à la racine de votre projet (et s'assure qu'il est dans votre .gitignore
).
Exemple :
Welcome to AI Init!
Which LLM API do you want to use? [1] openai, [2] grok, [3] claude: 1
Enter the base URL for the LLM API [https://api.openai.com]:
Enter your API key for openai: sk-...
Enter the model name you want to use (e.g. gpt-4, claude-3-opus, etc) [gpt-4o]:
Credentials saved to .runway-creds.json
Génération d'Instructions IA Spécifiques au Projet
La commande ai:generate-instructions
vous aide à créer ou mettre à jour des instructions pour les assistants de codage IA, adaptées à votre projet.
php runway ai:generate-instructions
Vous répondrez à quelques questions sur votre projet (description, base de données, templating, sécurité, taille de l'équipe, etc.). Flight utilise votre fournisseur de LLM pour générer les instructions, puis les écrit dans :
.github/copilot-instructions.md
(pour GitHub Copilot).cursor/rules/project-overview.mdc
(pour Cursor).windsurfrules
(pour Windsurf)
Exemple :
Please describe what your project is for? My awesome API
What database are you planning on using? MySQL
What HTML templating engine will you plan on using (if any)? latte
Is security an important element of this project? (y/n) y
...
AI instructions updated successfully.
Maintenant, vos outils IA fourniront des suggestions plus intelligentes et plus pertinentes basées sur les besoins réels de votre projet.
Utilisation Avancée
- Vous pouvez personnaliser l'emplacement de vos fichiers d'identifiants ou d'instructions en utilisant des options de commande (voir
--help
pour chaque commande). - Les aides IA sont conçues pour fonctionner avec n'importe quel fournisseur de LLM qui prend en charge les API compatibles avec OpenAI.
- Si vous souhaitez mettre à jour vos instructions au fur et à mesure que votre projet évolue, relancez simplement
ai:generate-instructions
et répondez aux invites à nouveau.
Voir Aussi
- Flight Skeleton – Le démarrage officiel avec intégration IA
- Runway CLI – Plus d'informations sur l'outil CLI qui alimente ces commandes
Dépannage
- Si vous voyez "Missing .runway-creds.json", exécutez d'abord
php runway ai:init
. - Assurez-vous que votre clé API est valide et a accès au modèle sélectionné.
- Si les instructions ne se mettent pas à jour, vérifiez les permissions des fichiers dans votre répertoire de projet.
Journal des Modifications
- v3.16.0 – Ajout des commandes CLI
ai:init
etai:generate-instructions
pour l'intégration IA.
Learn/unit_testing_and_solid_principles
Cet article a été publié à l'origine sur Airpair en 2015. Tout le crédit revient à Airpair et à Brian Fenton, qui a rédigé cet article à l'origine, bien que le site web ne soit plus disponible et que l'article n'existe plus que dans la Wayback Machine. Cet article a été ajouté au site à des fins d'apprentissage et d'éducation pour la communauté PHP dans son ensemble.
1 Configuration et installation
1.1 Garder à jour
Disons-le dès le début : un nombre déprimant d'installations PHP en production ne sont pas à jour ou ne le restent pas. Que ce soit dû à des restrictions d'hébergement partagé, à des paramètres par défaut que personne ne modifie, ou à un manque de temps/budget pour les tests de mise à niveau, les binaires PHP ont tendance à être laissés de côté. Une pratique exemplaire claire qui mérite plus d'attention est d'utiliser toujours une version actuelle de PHP (5.6.x au moment de cet article). De plus, il est important de planifier des mises à niveau régulières de PHP lui-même ainsi que de toute extension ou bibliothèque de fournisseurs que vous utilisez. Les mises à niveau vous apportent de nouvelles fonctionnalités de langage, une vitesse améliorée, une utilisation de mémoire réduite et des mises à jour de sécurité. Plus vous mettez à niveau fréquemment, moins le processus est douloureux.
1.2 Définir des paramètres par défaut sensés
PHP fait un travail décent pour définir de bons paramètres par défaut avec ses fichiers php.ini.development et php.ini.production, mais nous pouvons faire mieux. Par exemple, ils ne définissent pas de fuseau horaire pour nous. Cela a du sens du point de vue de la distribution, mais sans cela, PHP générera une erreur E_WARNING chaque fois que nous appelons une fonction liée à la date/heure. Voici quelques paramètres recommandés :
- date.timezone - choisissez parmi la liste des fuseaux horaires pris en charge
- session.savepath - si nous utilisons des fichiers pour les sessions et non un autre gestionnaire de sauvegarde, définissez-le sur quelque chose en dehors de /tmp. Laisser cela sur /tmp peut être risqué dans un environnement d'hébergement partagé car /tmp_ a généralement des permissions larges. Même avec le bit collant défini, quiconque ayant accès à lister le contenu de ce répertoire peut connaître tous vos identifiants de session actifs.
- session.cookie_secure - une évidence, activez-le si vous servez votre code PHP via HTTPS.
- session.cookie_httponly - définissez-le pour empêcher les cookies de session PHP d'être accessibles via JavaScript
- Plus... utilisez un outil comme iniscan pour tester votre configuration contre les vulnérabilités courantes
1.3 Extensions
Il est également une bonne idée de désactiver (ou au moins de ne pas activer) les extensions que vous n'utiliserez pas, comme les pilotes de base de données. Pour voir ce qui est activé, exécutez la commande phpinfo()
ou allez dans une ligne de commande et exécutez ceci.
$ php -i
Les informations sont les mêmes, mais phpinfo() ajoute un formatage HTML. La version CLI est plus facile à rediriger vers grep pour trouver des informations spécifiques. Ex.
$ php -i | grep error_log
Une mise en garde de cette méthode : il est possible d'avoir des paramètres PHP différents s'appliquant à la version orientée web et à la version CLI.
2 Utiliser Composer
Cela peut surprendre, mais l'une des meilleures pratiques pour écrire du PHP moderne est d'en écrire moins. Bien que ce soit vrai que l'un des meilleurs moyens de bien programmer est de le faire, il y a un grand nombre de problèmes qui ont déjà été résolus dans l'espace PHP, comme le routage, les bibliothèques de validation d'entrée de base, la conversion d'unités, les couches d'abstraction de base de données, etc... Allez simplement sur Packagist et explorez. Vous constaterez probablement que des parties significatives du problème que vous essayez de résoudre ont déjà été écrites et testées.
Bien qu'il soit tentant d'écrire tout le code vous-même (et il n'y a rien de mal à écrire votre propre framework ou bibliothèque comme une expérience d'apprentissage), vous devriez lutter contre ces sentiments de "Pas Inventé Ici" et vous épargner beaucoup de temps et de maux de tête. Suivez plutôt la doctrine de PIE - Fierement Inventé Ailleurs. De plus, si vous choisissez d'écrire votre propre élément, ne le publiez pas à moins qu'il ne fasse quelque chose de significativement différent ou meilleur que les offres existantes.
Composer est un gestionnaire de paquets pour PHP, similaire à pip en Python, gem en Ruby et npm en Node. Il vous permet de définir un fichier JSON qui liste les dépendances de votre code, et il essaiera de résoudre ces exigences en téléchargeant et en installant les paquets de code nécessaires.
2.1 Installer Composer
Nous supposons que c'est un projet local, donc installons une instance de Composer juste pour le projet actuel. Naviguez vers votre répertoire de projet et exécutez ceci :
$ curl -sS https://getcomposer.org/installer | php
Gardez à l'esprit que rediriger n'importe quel téléchargement directement vers un interpréteur de script (sh, ruby, php, etc.) est un risque de sécurité, donc lisez le code d'installation et assurez-vous d'être à l'aise avec cela avant d'exécuter une commande comme celle-ci.
Pour des raisons de commodité (si vous préférez taper composer install
plutôt que php composer.phar install
), vous pouvez utiliser cette commande pour installer une copie unique de composer globalement :
$ mv composer.phar /usr/local/bin/composer
$ chmod +x composer
Vous devrez peut-être exécuter celles-ci avec sudo
en fonction de vos permissions de fichier.
2.2 Utiliser Composer
Composer a deux catégories principales de dépendances qu'il peut gérer : "require" et "require-dev". Les dépendances listées comme "require" sont installées partout, mais les dépendances "require-dev" ne sont installées que lorsqu'elles sont spécifiquement demandées. Celles-ci sont généralement des outils pour lorsque le code est en développement actif, tels que PHP_CodeSniffer. La ligne ci-dessous montre un exemple de comment installer Guzzle, une bibliothèque HTTP populaire.
$ php composer.phar require guzzle/guzzle
Pour installer un outil juste à des fins de développement, ajoutez le drapeau --dev
:
$ php composer.phar require --dev 'sebastian/phpcpd'
Cela installe PHP Copy-Paste Detector, un autre outil de qualité de code en tant que dépendance de développement seulement.
2.3 Installer vs mettre à jour
Lorsque nous exécutons composer install
pour la première fois, il installera les bibliothèques et leurs dépendances dont nous avons besoin, en fonction du fichier composer.json. Une fois cela fait, composer crée un fichier de verrouillage, appelé composer.lock. Ce fichier contient une liste des dépendances que composer a trouvées pour nous et leurs versions exactes, avec des hachages. Ensuite, toute fois future que nous exécutons composer install
, il regardera dans le fichier de verrouillage et installera ces versions exactes.
composer update
est un peu différent. Il ignorera le fichier composer.lock (s'il est présent) et essaiera de trouver les versions les plus récentes de chacune des dépendances qui satisfont encore les contraintes dans composer.json. Il écrira ensuite un nouveau fichier composer.lock une fois terminé.
2.4 Autoload
À la fois composer install
et composer update
généreront un autoload pour nous qui indique à PHP où trouver tous les fichiers nécessaires pour utiliser les bibliothèques que nous venons d'installer. Pour l'utiliser, ajoutez simplement cette ligne (généralement à un fichier de bootstrap qui s'exécute à chaque requête) :
require 'vendor/autoload.php';
3 Suivre les bons principes de conception
3.1 SOLID
SOLID est un mnémonique pour nous rappeler cinq principes clés dans une bonne conception de logiciel orienté objet.
3.1.1 S - Principe de responsabilité unique
Cela stipule que les classes ne devraient avoir qu'une seule responsabilité, ou dit autrement, elles ne devraient avoir qu'une seule raison de changer. Cela correspond bien à la philosophie Unix de nombreux petits outils, en faisant une chose bien. Les classes qui ne font qu'une chose sont beaucoup plus faciles à tester et à déboguer, et elles sont moins susceptibles de vous surprendre. Vous ne voulez pas qu'un appel de méthode à une classe Validator mette à jour des enregistrements de base de données. Voici un exemple d'une violation de SRP, comme on en voit couramment dans une application basée sur le modèle ActiveRecord.
class Person extends Model
{
public $name;
public $birthDate;
protected $preferences;
public function getPreferences() {}
public function save() {}
}
Donc c'est un modèle d'entité assez basique. L'une de ces choses n'appartient pas ici cependant. La seule responsabilité d'un modèle d'entité devrait être le comportement lié à l'entité qu'il représente, il ne devrait pas être responsable de sa propre persistance.
class Person extends Model
{
public $name;
public $birthDate;
protected $preferences;
public function getPreferences() {}
}
class DataStore
{
public function save(Model $model) {}
}
Ceci est meilleur. Le modèle Person est de retour à une seule chose, et le comportement de sauvegarde a été déplacé vers un objet de persistance. Notez également que j'ai seulement indiqué le type sur Model, pas sur Person. Nous y reviendrons lorsque nous arriverons aux parties L et D de SOLID.
3.1.2 O - Principe ouvert-fermé
Il y a un test génial pour cela qui résume assez bien ce principe : pensez à une fonctionnalité à implémenter, probablement la plus récente sur laquelle vous avez travaillé ou que vous travaillez. Pouvez-vous implémenter cette fonctionnalité dans votre base de code existante UNIQUEMENT en ajoutant de nouvelles classes et sans modifier aucune classe existante dans votre système ? Votre code de configuration et de câblage obtient un peu de passe, mais dans la plupart des systèmes, cela est étonnamment difficile. Vous devez vous appuyer beaucoup sur le dispatch polymorphe et la plupart des bases de code ne sont pas configurées pour cela. Si vous êtes intéressé, il y a une bonne conférence Google sur YouTube à propos du polymorphisme et de l'écriture de code sans Ifs qui approfondit le sujet. En bonus, la conférence est donnée par Miško Hevery, que beaucoup connaissent comme le créateur de AngularJs.
3.1.3 L - Principe de substitution de Liskov
Ce principe est nommé d'après Barbara Liskov, et est imprimé ci-dessous :
"Les objets dans un programme devraient être remplaçables par des instances de leurs sous-types sans altérer la correction de ce programme."
Cela a l'air bien et tout, mais c'est plus clairement illustré avec un exemple.
abstract class Shape
{
public function getHeight();
public function setHeight($height);
public function getLength();
public function setLength($length);
}
Ceci va représenter notre forme basique à quatre côtés. Rien de fantaisiste ici.
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;
}
}
Voici notre première forme, le Carré. Une forme assez directe, non ? Vous pouvez supposer qu'il y a un constructeur où nous définissons les dimensions, mais vous voyez ici de cette implémentation que la longueur et la hauteur seront toujours les mêmes. Les carrés sont comme ça.
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;
}
}
Donc ici nous avons une forme différente. Elle a toujours les mêmes signatures de méthodes, c'est toujours une forme à quatre côtés, mais si nous commençons à essayer de les utiliser à la place les unes des autres ? Maintenant, tout d'un coup, si nous changeons la hauteur de notre Shape, nous ne pouvons plus supposer que la longueur de notre forme correspondra. Nous avons violé le contrat que nous avions avec l'utilisateur lorsque nous lui avons donné notre forme Carré.
Ceci est un exemple classique d'une violation du LSP et nous avons besoin de ce type de principe pour tirer le meilleur parti d'un système de types. Même le typage duck ne nous dira pas si le comportement sous-jacent est différent, et comme nous ne pouvons pas le savoir sans le voir se casser, il est préférable de s'assurer qu'il ne l'est pas au départ.
3.1.3 I - Principe de ségrégation d'interface
Ce principe dit de favoriser de nombreuses interfaces petites et granulaires par rapport à une grande. Les interfaces devraient être basées sur le comportement plutôt que sur "c'est l'une de ces classes". Pensez aux interfaces qui viennent avec PHP. Traversable, Countable, Serializable, des choses comme ça. Elles annoncent les capacités que l'objet possède, pas ce qu'il hérite. Donc gardez vos interfaces petites. Vous ne voulez pas qu'une interface ait 30 méthodes, 3 est un objectif bien meilleur.
3.1.4 D - Principe d'inversion des dépendances
Vous avez probablement entendu parler de cela dans d'autres endroits qui parlaient de l'injection de dépendances, mais l'inversion des dépendances et l'injection de dépendances ne sont pas tout à fait la même chose. L'inversion des dépendances est vraiment juste un moyen de dire que vous devriez dépendre des abstractions dans votre système et non de ses détails. Que signifie cela pour vous au quotidien ?
N'utilisez pas directement mysqli_query() partout dans votre code, utilisez quelque chose comme DataStore->query() à la place.
Le cœur de ce principe est en fait sur les abstractions. Il s'agit plus de dire "utilisez un adaptateur de base de données" au lieu de dépendre d'appels directs à des choses comme mysqli_query. Si vous utilisez directement mysqli_query dans la moitié de vos classes, vous liez tout directement à votre base de données. Rien pour ou contre MySQL ici, mais si vous utilisez mysqli_query, ce type de détail de bas niveau devrait être caché dans un seul endroit et cette fonctionnalité devrait ensuite être exposée via un wrapper générique.
Maintenant je sais que c'est un exemple un peu usé si vous y pensez, car le nombre de fois où vous allez complètement changer votre moteur de base de données après que votre produit soit en production est très, très faible. Je l'ai choisi parce que je pensais que les gens seraient familiers avec l'idée de leur propre code. De plus, même si vous avez une base de données à laquelle vous restez fidèle, cet objet wrapper abstrait vous permet de corriger les bogues, de changer le comportement ou d'implémenter des fonctionnalités que vous souhaitez que votre base de données choisie ait. Il rend également les tests unitaires possibles là où les appels de bas niveau ne le feraient pas.
4 Exercices d'objets
Ceci n'est pas un plongeon complet dans ces principes, mais les deux premiers sont faciles à retenir, apportent une bonne valeur et peuvent être appliqués immédiatement à presque n'importe quelle base de code.
4.1 Pas plus d'un niveau d'indentation par méthode
Ceci est un moyen utile de penser à décomposer les méthodes en morceaux plus petits, laissant un code plus clair et plus auto-documenté. Plus vous avez de niveaux d'indentation, plus la méthode fait de choses et plus d'état vous devez suivre dans votre tête pendant que vous travaillez avec.
Tout de suite, je sais que les gens vont s'opposer à cela, mais ceci n'est qu'une ligne directrice/règle heuristique, pas une règle dure et rapide. Je ne m'attends pas à ce que quiconque fasse respecter les règles PHP_CodeSniffer pour cela (bien que des gens l'aient fait).
Parcourons rapidement un échantillon de ce à quoi cela pourrait ressembler :
public function transformToCsv($data)
{
$csvLines = array();
$csvLines[] = implode(',', array_keys($data[0]));
foreach ($data as $row) {
if (!$row) {
continue;
}
$csvLines[] = implode(',', $row);
}
return $csvLines;
}
Bien que ce ne soit pas un code terrible (il est techniquement correct, testable, etc.), nous pouvons faire beaucoup plus pour le rendre clair. Comment réduirions-nous les niveaux d'imbrication ici ?
Nous savons que nous devons simplifier énormément le contenu de la boucle foreach (ou l'enlever complètement), donc commençons par là.
if (!$row) {
continue;
}
Cette première partie est facile. Tout ce qu'elle fait, c'est ignorer les lignes vides. Nous pouvons contourner cela en utilisant une fonction PHP intégrée avant même d'arriver à la boucle.
$data = array_filter($data);
foreach ($data as $row) {
$csvLines[] = implode(',', $row);
}
Nous avons maintenant notre seul niveau d'imbrication. Mais en regardant cela, tout ce que nous faisons, c'est appliquer une fonction à chaque élément d'un tableau. Nous n'avons même pas besoin de la boucle foreach pour cela.
$data = array_filter($data);
$csvLines = array_map(function($row) {
return implode(',', $row);
}, $data);
Maintenant nous n'avons plus d'imbrication du tout, et le code sera probablement plus rapide car nous faisons toute la boucle avec des fonctions C natives au lieu de PHP. Nous devons cependant nous livrer à un peu de tricherie pour passer la virgule à implode
, donc on pourrait argumenter que s'arrêter à l'étape précédente est beaucoup plus compréhensible.
4.2 Essayer de ne pas utiliser else
Ceci traite vraiment de deux idées principales. La première est les déclarations de retour multiples d'une méthode. Si vous avez assez d'informations pour prendre une décision sur le résultat de la méthode, allez-y et prenez cette décision et retournez. La seconde est une idée connue sous le nom de clauses de garde. Celles-ci sont essentiellement des vérifications de validation combinées à des retours précoces, généralement près du sommet d'une méthode. Laissez-moi vous montrer ce que je veux dire.
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;
}
Donc c'est assez direct, cela ajoute 3 entiers ensemble et retourne le résultat, ou null
si l'un des paramètres n'est pas un entier. En ignorant le fait que nous pourrions combiner toutes ces vérifications sur une seule ligne avec des opérateurs ET, je pense que vous pouvez voir comment la structure if/else imbriquée rend le code plus difficile à suivre. Maintenant regardez cet exemple à la place.
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;
}
Pour moi, cet exemple est beaucoup plus facile à suivre. Ici, nous utilisons des clauses de garde pour vérifier nos affirmations initiales sur les paramètres que nous passons et quittons immédiatement la méthode s'ils ne passent pas. Nous n'avons plus non plus la variable intermédiaire pour suivre la somme tout au long de la méthode. Dans ce cas, nous avons vérifié que nous sommes déjà sur le chemin heureux et nous pouvons simplement faire ce que nous sommes venus faire. Encore une fois, nous pourrions faire toutes ces vérifications dans un seul if
, mais le principe devrait être clair.
5 Tests unitaires
Les tests unitaires sont la pratique d'écriture de petits tests qui vérifient le comportement dans votre code. Ils sont presque toujours écrits dans le même langage que le code (dans ce cas PHP) et sont destinés à être assez rapides pour s'exécuter à tout moment. Ils sont extrêmement précieux en tant qu'outil pour améliorer votre code. En plus des avantages évidents de s'assurer que votre code fait ce que vous pensez qu'il fait, les tests unitaires peuvent fournir un retour de conception très utile. Si un morceau de code est difficile à tester, il souligne souvent des problèmes de conception. Ils vous donnent également un filet de sécurité contre les régressions, et cela vous permet de refactoriser beaucoup plus souvent et d'évoluer vers une conception plus propre.
5.1 Outils
Il y a plusieurs outils de tests unitaires dans PHP, mais de loin le plus courant est PHPUnit. Vous pouvez l'installer en téléchargeant un fichier PHAR directement, ou en l'installant avec composer. Comme nous utilisons composer pour tout le reste, nous montrerons cette méthode. De plus, comme PHPUnit n'est probablement pas destiné à être déployé en production, nous pouvons l'installer en tant que dépendance de développement avec la commande suivante :
composer require --dev phpunit/phpunit
5.2 Les tests sont une spécification
Le rôle le plus important des tests unitaires dans votre code est de fournir une spécification exécutable de ce que le code est censé faire. Même si le code de test est erroné, ou si le code a des bogues, la connaissance de ce que le système est censé faire est inestimable.
5.3 Écrivez vos tests en premier
Si vous avez eu la chance de voir un ensemble de tests écrits avant le code et un écrit après que le code soit terminé, ils sont étonnamment différents. Les tests "après" sont beaucoup plus préoccupés par les détails d'implémentation de la classe et s'assurer qu'ils ont une bonne couverture de lignes, alors que les tests "avant" sont plus sur la vérification du comportement externe souhaité. C'est vraiment ce qui nous intéresse avec les tests unitaires de toute façon, c'est de s'assurer que la classe exhibe le bon comportement. Les tests axés sur l'implémentation rendent en fait le refactoring plus difficile car ils se cassent si les internes des classes changent, et vous venez de vous coûter les avantages de la dissimulation d'informations de la POO.
5.4 Ce qui fait un bon test unitaire
Les bons tests unitaires partagent beaucoup des caractéristiques suivantes :
- Rapide - devrait s'exécuter en millisecondes.
- Pas d'accès réseau - devrait pouvoir désactiver le sans fil/débrancher et tous les tests passent encore.
- Accès limité au système de fichiers - cela ajoute à la vitesse et à la flexibilité si le code est déployé dans d'autres environnements.
- Pas d'accès à la base de données - évite les activités coûteuses de configuration et de démontage.
- Tester une chose à la fois - un test unitaire ne devrait avoir qu'une seule raison d'échouer.
- Bien nommé - voir 5.2 ci-dessus.
- Principalement des objets factices - les seuls "vrais" objets dans les tests unitaires devraient être l'objet que nous testons et les objets de valeur simples. Le reste devrait être une forme de test double
Il y a des raisons d'aller à l'encontre de certaines de celles-ci, mais en tant que lignes directrices générales, elles vous serviront bien.
5.5 Quand les tests sont douloureux
Les tests unitaires vous forcent à ressentir la douleur d'une mauvaise conception dès le début - Michael Feathers
Lorsque vous écrivez des tests unitaires, vous vous forcez à utiliser réellement la classe pour accomplir des choses. Si vous écrivez des tests à la fin, ou pire encore, si vous jetez simplement le code par-dessus le mur pour que QA ou qui que ce soit écrive des tests, vous ne recevez aucun retour sur la façon dont la classe se comporte réellement. Si nous écrivons des tests, et que la classe est une vraie douleur à utiliser, nous le découvrirons pendant que nous l'écrivons, ce qui est presque le moment le moins cher pour le corriger.
Si une classe est difficile à tester, c'est un défaut de conception. Différents défauts se manifestent de différentes manières cependant. Si vous devez faire beaucoup de moqueries, votre classe a probablement trop de dépendances, ou vos méthodes font trop. Plus vous avez de configuration pour chaque test, plus il est probable que vos méthodes font trop. Si vous devez écrire des scénarios de test vraiment compliqués pour exercer un comportement, les méthodes de la classe font probablement trop. Si vous devez creuser à l'intérieur d'une foule de méthodes privées et d'état pour tester des choses, peut-être qu'une autre classe essaie de sortir. Les tests unitaires sont très bons pour exposer les "classes iceberg" où 80% de ce que la classe fait est caché dans du code protégé ou privé. J'étais autrefois un grand fan de rendre autant que possible protégé, mais maintenant j'ai réalisé que je rendais simplement mes classes individuelles responsables de trop, et la vraie solution était de diviser la classe en morceaux plus petits.
Écrit par Brian Fenton - Brian Fenton est un développeur PHP depuis 8 ans dans le Midwest et la Bay Area, actuellement chez Thismoment. Il se concentre sur l'artisanat du code et les principes de conception. Blog sur www.brianfenton.us, Twitter sur @brianfenton. Quand il n'est pas occupé à être un père, il aime la nourriture, la bière, les jeux et l'apprentissage.
Learn/security
Sécurité
Aperçu
La sécurité est une priorité majeure pour les applications web. Vous devez vous assurer que votre application est sécurisée et que les données de vos utilisateurs sont protégées. Flight fournit un certain nombre de fonctionnalités pour vous aider à sécuriser vos applications web.
Compréhension
Il existe un certain nombre de menaces de sécurité courantes dont vous devez être conscient lors de la construction d'applications web. Parmi les menaces les plus courantes, on trouve :
- Cross Site Request Forgery (CSRF)
- Cross Site Scripting (XSS)
- SQL Injection
- Cross Origin Resource Sharing (CORS)
Templates aident avec XSS en échappant la sortie par défaut, afin que vous n'ayez pas à vous en souvenir. Sessions peuvent aider avec CSRF en stockant un jeton CSRF dans la session de l'utilisateur comme indiqué ci-dessous. L'utilisation de requêtes préparées avec PDO peut aider à prévenir les attaques par injection SQL (ou en utilisant les méthodes pratiques dans la classe PdoWrapper). CORS peut être géré avec un simple hook avant que Flight::start()
ne soit appelé.
Toutes ces méthodes fonctionnent ensemble pour aider à garder vos applications web sécurisées. Il devrait toujours être à l'avant-plan de votre esprit d'apprendre et de comprendre les meilleures pratiques de sécurité.
Utilisation de base
En-têtes
Les en-têtes HTTP sont l'un des moyens les plus simples de sécuriser vos applications web. Vous pouvez utiliser des en-têtes pour prévenir le clickjacking, XSS et d'autres attaques. Il existe plusieurs façons d'ajouter ces en-têtes à votre application.
Deux excellents sites web pour vérifier la sécurité de vos en-têtes sont securityheaders.com et observatory.mozilla.org. Après avoir configuré le code ci-dessous, vous pouvez facilement vérifier que vos en-têtes fonctionnent avec ces deux sites.
Ajout manuel
Vous pouvez ajouter manuellement ces en-têtes en utilisant la méthode header
sur l'objet Flight\Response
.
// Définir l'en-tête X-Frame-Options pour prévenir le clickjacking
Flight::response()->header('X-Frame-Options', 'SAMEORIGIN');
// Définir l'en-tête Content-Security-Policy pour prévenir XSS
// Note : cet en-tête peut devenir très complexe, vous devrez donc
// consulter des exemples sur internet pour votre application
Flight::response()->header("Content-Security-Policy", "default-src 'self'");
// Définir l'en-tête X-XSS-Protection pour prévenir XSS
Flight::response()->header('X-XSS-Protection', '1; mode=block');
// Définir l'en-tête X-Content-Type-Options pour prévenir le sniffing MIME
Flight::response()->header('X-Content-Type-Options', 'nosniff');
// Définir l'en-tête Referrer-Policy pour contrôler la quantité d'informations de référent envoyées
Flight::response()->header('Referrer-Policy', 'no-referrer-when-downgrade');
// Définir l'en-tête Strict-Transport-Security pour forcer HTTPS
Flight::response()->header('Strict-Transport-Security', 'max-age=31536000; includeSubDomains; preload');
// Définir l'en-tête Permissions-Policy pour contrôler les fonctionnalités et API utilisables
Flight::response()->header('Permissions-Policy', 'geolocation=()');
Ces en-têtes peuvent être ajoutés en haut de vos fichiers routes.php
ou index.php
.
Ajout en tant que filtre
Vous pouvez également les ajouter dans un filtre/hook comme suit :
// Ajouter les en-têtes dans un filtre
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=()');
});
Ajout en tant que middleware
Vous pouvez également les ajouter en tant que classe middleware, ce qui offre la plus grande flexibilité pour choisir les routes auxquelles les appliquer. En général, ces en-têtes devraient être appliqués à toutes les réponses HTML et API.
// 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 ou là où vous avez vos routes
// FYI, ce groupe de chaîne vide agit comme un middleware global pour
// toutes les routes. Bien sûr, vous pourriez faire la même chose et simplement ajouter
// cela uniquement à des routes spécifiques.
Flight::group('', function(Router $router) {
$router->get('/users', [ 'UserController', 'getUsers' ]);
// plus de routes
}, [ SecurityHeadersMiddleware::class ]);
Cross Site Request Forgery (CSRF)
Cross Site Request Forgery (CSRF) est un type d'attaque où un site web malveillant peut faire en sorte que le navigateur d'un utilisateur envoie une requête à votre site web. Cela peut être utilisé pour effectuer des actions sur votre site web sans la connaissance de l'utilisateur. Flight ne fournit pas de mécanisme de protection CSRF intégré, mais vous pouvez facilement implémenter le vôtre en utilisant un middleware.
Configuration
D'abord, vous devez générer un jeton CSRF et le stocker dans la session de l'utilisateur. Vous pouvez ensuite utiliser ce jeton dans vos formulaires et le vérifier quand le formulaire est soumis. Nous utiliserons le plugin flightphp/session pour gérer les sessions.
// Générer un jeton CSRF et le stocker dans la session de l'utilisateur
// (en supposant que vous ayez créé un objet session et l'avez attaché à Flight)
// voir la documentation des sessions pour plus d'informations
Flight::register('session', flight\Session::class);
// Vous n'avez besoin de générer qu'un seul jeton par session (afin qu'il fonctionne
// sur plusieurs onglets et requêtes pour le même utilisateur)
if(Flight::session()->get('csrf_token') === null) {
Flight::session()->set('csrf_token', bin2hex(random_bytes(32)) );
}
Utilisation du template Flight PHP par défaut
<!-- Utiliser le jeton CSRF dans votre formulaire -->
<form method="post">
<input type="hidden" name="csrf_token" value="<?= Flight::session()->get('csrf_token') ?>">
<!-- autres champs du formulaire -->
</form>
Utilisation de Latte
Vous pouvez également définir une fonction personnalisée pour afficher le jeton CSRF dans vos templates Latte.
Flight::map('render', function(string $template, array $data, ?string $block): void {
$latte = new Latte\Engine;
// autres configurations...
// Définir une fonction personnalisée pour afficher le jeton CSRF
$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);
});
Et maintenant dans vos templates Latte, vous pouvez utiliser la fonction csrf()
pour afficher le jeton CSRF.
<form method="post">
{csrf()}
<!-- autres champs du formulaire -->
</form>
Vérifier le jeton CSRF
Vous pouvez vérifier le jeton CSRF en utilisant plusieurs méthodes.
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, 'Jeton CSRF invalide');
}
}
}
}
// index.php ou là où vous avez vos routes
use app\middlewares\CsrfMiddleware;
Flight::group('', function(Router $router) {
$router->get('/users', [ 'UserController', 'getUsers' ]);
// plus de routes
}, [ CsrfMiddleware::class ]);
Filtres d'événements
// Ce middleware vérifie si la requête est une requête POST et si c'est le cas, il vérifie si le jeton CSRF est valide
Flight::before('start', function() {
if(Flight::request()->method == 'POST') {
// capturer le jeton csrf des valeurs du formulaire
$token = Flight::request()->data->csrf_token;
if($token !== Flight::session()->get('csrf_token')) {
Flight::halt(403, 'Jeton CSRF invalide');
// ou pour une réponse JSON
Flight::jsonHalt(['error' => 'Jeton CSRF invalide'], 403);
}
}
});
Cross Site Scripting (XSS)
Cross Site Scripting (XSS) est un type d'attaque où une entrée de formulaire malveillante peut injecter du code dans votre site web. La plupart de ces opportunités proviennent des valeurs de formulaire que vos utilisateurs finaux rempliront. Vous ne devez jamais faire confiance à la sortie de vos utilisateurs ! Supposez toujours qu'ils sont les meilleurs hackers au monde. Ils peuvent injecter du JavaScript ou du HTML malveillant dans votre page. Ce code peut être utilisé pour voler des informations à vos utilisateurs ou effectuer des actions sur votre site web. En utilisant la classe view de Flight ou un autre moteur de templating comme Latte, vous pouvez facilement échapper la sortie pour prévenir les attaques XSS.
// Supposons que l'utilisateur soit astucieux et essaie d'utiliser ceci comme nom
$name = '<script>alert("XSS")</script>';
// Cela échappera la sortie
Flight::view()->set('name', $name);
// Cela affichera : <script>alert("XSS")</script>
// Si vous utilisez quelque chose comme Latte enregistré comme votre classe view, il échappera automatiquement cela.
Flight::view()->render('template', ['name' => $name]);
SQL Injection
SQL Injection est un type d'attaque où un utilisateur malveillant peut injecter du code SQL dans votre base de données. Cela peut être utilisé pour voler des informations
de votre base de données ou effectuer des actions sur votre base de données. Encore une fois, vous ne devez jamais faire confiance à l'entrée de vos utilisateurs ! Supposez toujours qu'ils en ont après votre peau. L'utilisation de requêtes préparées dans vos objets PDO
préviendra les injections SQL.
// En supposant que vous ayez Flight::db() enregistré comme votre objet PDO
$statement = Flight::db()->prepare('SELECT * FROM users WHERE username = :username');
$statement->execute([':username' => $username]);
$users = $statement->fetchAll();
// Si vous utilisez la classe PdoWrapper, cela peut être fait facilement en une ligne
$users = Flight::db()->fetchAll('SELECT * FROM users WHERE username = :username', [ 'username' => $username ]);
// Vous pouvez faire la même chose avec un objet PDO avec des placeholders ?
$statement = Flight::db()->fetchAll('SELECT * FROM users WHERE username = ?', [ $username ]);
Exemple non sécurisé
Voici pourquoi nous utilisons des requêtes préparées SQL pour nous protéger d'exemples innocents comme celui-ci :
// l'utilisateur final remplit un formulaire web.
// pour la valeur du formulaire, le hacker met quelque chose comme ceci :
$username = "' OR 1=1; -- ";
$sql = "SELECT * FROM users WHERE username = '$username' LIMIT 5";
$users = Flight::db()->fetchAll($sql);
// Après que la requête soit construite, cela ressemble à ceci
// SELECT * FROM users WHERE username = '' OR 1=1; -- LIMIT 5
// Cela semble étrange, mais c'est une requête valide qui fonctionnera. En fait,
// c'est une attaque d'injection SQL très courante qui renverra tous les utilisateurs.
var_dump($users); // cela dumpera tous les utilisateurs dans la base de données, pas seulement le nom d'utilisateur unique
CORS
Cross-Origin Resource Sharing (CORS) est un mécanisme qui permet à de nombreuses ressources (par exemple, polices, JavaScript, etc.) sur une page web d'être
requises depuis un autre domaine en dehors du domaine d'origine de la ressource. Flight n'a pas de fonctionnalité intégrée,
mais cela peut facilement être géré avec un hook à exécuter avant que la méthode Flight::start()
ne soit appelée.
// 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
{
// personnalisez vos hôtes autorisés ici.
$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 ou là où vous avez vos routes
$CorsUtil = new CorsUtil();
// Cela doit être exécuté avant que start ne s'exécute.
Flight::before('start', [ $CorsUtil, 'setupCors' ]);
Gestion des erreurs
Masquez les détails d'erreurs sensibles en production pour éviter de divulguer des informations aux attaquants. En production, enregistrez les erreurs au lieu de les afficher avec display_errors
défini à 0
.
// Dans votre bootstrap.php ou index.php
// ajoutez ceci à votre app/config/config.php
$environment = ENVIRONMENT;
if ($environment === 'production') {
ini_set('display_errors', 0); // Désactiver l'affichage des erreurs
ini_set('log_errors', 1); // Enregistrer les erreurs à la place
ini_set('error_log', '/path/to/error.log');
}
// Dans vos routes ou contrôleurs
// Utilisez Flight::halt() pour des réponses d'erreur contrôlées
Flight::halt(403, 'Accès refusé');
Assainissement des entrées
Ne faites jamais confiance à l'entrée utilisateur. Assainissez-la en utilisant filter_var avant de la traiter pour empêcher les données malveillantes de s'infiltrer.
// Supposons une requête $_POST avec $_POST['input'] et $_POST['email']
// Assainir une entrée chaîne
$clean_input = filter_var(Flight::request()->data->input, FILTER_SANITIZE_STRING);
// Assainir un email
$clean_email = filter_var(Flight::request()->data->email, FILTER_SANITIZE_EMAIL);
Hachage des mots de passe
Stockez les mots de passe de manière sécurisée et vérifiez-les en toute sécurité en utilisant les fonctions intégrées de PHP comme password_hash et password_verify. Les mots de passe ne doivent jamais être stockés en texte clair, ni chiffrés avec des méthodes réversibles. Le hachage garantit que même si votre base de données est compromise, les mots de passe réels restent protégés.
$password = Flight::request()->data->password;
// Hacher un mot de passe lors du stockage (par exemple, pendant l'inscription)
$hashed_password = password_hash($password, PASSWORD_DEFAULT);
// Vérifier un mot de passe (par exemple, pendant la connexion)
if (password_verify($password, $stored_hash)) {
// Le mot de passe correspond
}
Limitation de taux
Protégez contre les attaques par force brute ou les attaques par déni de service en limitant les taux de requêtes avec un cache.
// En supposant que vous ayez flightphp/cache installé et enregistré
// Utilisation de flightphp/cache dans un filtre
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, 'Trop de requêtes');
}
$cache->set($key, $attempts + 1, 60); // Réinitialiser après 60 secondes
});
Voir aussi
- Sessions - Comment gérer les sessions utilisateur de manière sécurisée.
- Templates - Utilisation des templates pour échapper automatiquement la sortie et prévenir XSS.
- PDO Wrapper - Interactions simplifiées avec la base de données avec des requêtes préparées.
- Middleware - Comment utiliser le middleware pour simplifier l'ajout d'en-têtes de sécurité.
- Responses - Comment personnaliser les réponses HTTP avec des en-têtes sécurisés.
- Requests - Comment gérer et assainir l'entrée utilisateur.
- filter_var - Fonction PHP pour l'assainissement des entrées.
- password_hash - Fonction PHP pour le hachage sécurisé des mots de passe.
- password_verify - Fonction PHP pour vérifier les mots de passe hachés.
Dépannage
- Reportez-vous à la section "Voir aussi" ci-dessus pour des informations de dépannage liées aux problèmes avec les composants du Framework Flight.
Journal des modifications
- v3.1.0 - Ajout de sections sur CORS, Gestion des erreurs, Assainissement des entrées, Hachage des mots de passe, et Limitation de taux.
- v2.0 - Ajout d'échappement pour les vues par défaut pour prévenir XSS.
Learn/routing
Routage
Aperçu
Le routage dans Flight PHP mappe les motifs d'URL sur des fonctions de rappel ou des méthodes de classe, permettant une gestion rapide et simple des requêtes. Il est conçu pour un overhead minimal, une utilisation conviviale pour les débutants, et une extensibilité sans dépendances externes.
Comprendre
Le routage est le mécanisme central qui connecte les requêtes HTTP à la logique de votre application dans Flight. En définissant des routes, vous spécifiez comment différentes URL déclenchent du code spécifique, que ce soit par des fonctions, des méthodes de classe ou des actions de contrôleur. Le système de routage de Flight est flexible, supportant des motifs basiques, des paramètres nommés, des expressions régulières, et des fonctionnalités avancées comme l'injection de dépendances et le routage de ressources. Cette approche garde votre code organisé et facile à maintenir, tout en restant rapide et simple pour les débutants et extensible pour les utilisateurs avancés.
Note : Vous voulez en savoir plus sur le routage ? Consultez la page "pourquoi un framework ?" pour une explication plus approfondie.
Utilisation de base
Définir une route simple
Le routage de base dans Flight se fait en associant un motif d'URL à une fonction de rappel ou à un tableau d'une classe et d'une méthode.
Flight::route('/', function(){
echo 'hello world!';
});
Les routes sont associées dans l'ordre où elles sont définies. La première route qui correspond à une requête sera invoquée.
Utiliser des fonctions comme rappels
Le rappel peut être n'importe quel objet qui est invocable. Vous pouvez donc utiliser une fonction régulière :
function hello() {
echo 'hello world!';
}
Flight::route('/', 'hello');
Utiliser des classes et des méthodes comme contrôleur
Vous pouvez également utiliser une méthode (statique ou non) d'une classe :
class GreetingController {
public function hello() {
echo 'hello world!';
}
}
Flight::route('/', [ 'GreetingController','hello' ]);
// ou
Flight::route('/', [ GreetingController::class, 'hello' ]); // méthode préférée
// ou
Flight::route('/', [ 'GreetingController::hello' ]);
// ou
Flight::route('/', [ 'GreetingController->hello' ]);
Ou en créant d'abord un objet puis en appelant la méthode :
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' ]);
Note : Par défaut, lorsqu'un contrôleur est appelé dans le framework, la classe
flight\Engine
est toujours injectée sauf si vous spécifiez via un conteneur d'injection de dépendances
Routage spécifique à la méthode
Par défaut, les motifs de route sont associés à toutes les méthodes de requête. Vous pouvez répondre à des méthodes spécifiques en plaçant un identifiant avant l'URL.
Flight::route('GET /', function () {
echo 'I received a GET request.';
});
Flight::route('POST /', function () {
echo 'I received a POST request.';
});
// Vous ne pouvez pas utiliser Flight::get() pour les routes car c'est une méthode
// pour obtenir des variables, pas pour créer une route.
Flight::post('/', function() { /* code */ });
Flight::patch('/', function() { /* code */ });
Flight::put('/', function() { /* code */ });
Flight::delete('/', function() { /* code */ });
Vous pouvez également mapper plusieurs méthodes à un seul rappel en utilisant un délimiteur |
:
Flight::route('GET|POST /', function () {
echo 'I received either a GET or a POST request.';
});
Gestion spéciale pour les requêtes HEAD et OPTIONS
Flight fournit une gestion intégrée pour les requêtes HTTP HEAD
et OPTIONS
:
Requêtes HEAD
- Les requêtes HEAD sont traitées comme des requêtes
GET
, mais Flight supprime automatiquement le corps de la réponse avant de l'envoyer au client. - Cela signifie que vous pouvez définir une route pour
GET
, et les requêtes HEAD vers la même URL ne renverront que les en-têtes (pas de contenu), comme attendu par les standards HTTP.
Flight::route('GET /info', function() {
echo 'This is some info!';
});
// Une requête HEAD vers /info renverra les mêmes en-têtes, mais pas de corps.
Requêtes OPTIONS
Les requêtes OPTIONS
sont automatiquement gérées par Flight pour toute route définie.
- Lorsqu'une requête OPTIONS est reçue, Flight répond avec un statut
204 No Content
et un en-têteAllow
listant toutes les méthodes HTTP supportées pour cette route. - Vous n'avez pas besoin de définir une route séparée pour OPTIONS.
// Pour une route définie comme :
Flight::route('GET|POST /users', function() { /* ... */ });
// Une requête OPTIONS vers /users répondra avec :
//
// Status: 204 No Content
// Allow: GET, POST, HEAD, OPTIONS
Utiliser l'objet Router
De plus, vous pouvez obtenir l'objet Router qui dispose de méthodes d'aide pour votre utilisation :
$router = Flight::router();
// mappe toutes les méthodes comme Flight::route()
$router->map('/', function() {
echo 'hello world!';
});
// requête GET
$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 */});
Expressions régulières (Regex)
Vous pouvez utiliser des expressions régulières dans vos routes :
Flight::route('/user/[0-9]+', function () {
// Cela correspondra à /user/1234
});
Bien que cette méthode soit disponible, il est recommandé d'utiliser des paramètres nommés, ou des paramètres nommés avec des expressions régulières, car ils sont plus lisibles et plus faciles à maintenir.
Paramètres nommés
Vous pouvez spécifier des paramètres nommés dans vos routes qui seront passés à votre fonction de rappel. Ceci est plus pour la lisibilité de la route que pour toute autre chose. Veuillez voir la section ci-dessous sur l'avertissement important.
Flight::route('/@name/@id', function (string $name, string $id) {
echo "hello, $name ($id)!";
});
Vous pouvez également inclure des expressions régulières avec vos paramètres nommés en utilisant le délimiteur :
:
Flight::route('/@name/@id:[0-9]{3}', function (string $name, string $id) {
// Cela correspondra à /bob/123
// Mais ne correspondra pas à /bob/12345
});
Note : La correspondance de groupes regex
()
avec des paramètres positionnels n'est pas supportée. Ex ::'\(
Avertissement important
Bien que dans l'exemple ci-dessus, il semble que @name
soit directement lié à la variable $name
, ce n'est pas le cas. L'ordre des paramètres dans la fonction de rappel détermine ce qui lui est passé. Si vous inversiez l'ordre des paramètres dans la fonction de rappel, les variables seraient également inversées. Voici un exemple :
Flight::route('/@name/@id', function (string $id, string $name) {
echo "hello, $name ($id)!";
});
Et si vous alliez à l'URL suivante : /bob/123
, la sortie serait hello, 123 (bob)!
.
Soyez prudent lorsque vous configurez vos routes et vos fonctions de rappel !
Paramètres optionnels
Vous pouvez spécifier des paramètres nommés qui sont optionnels pour la correspondance en enveloppant les segments entre parenthèses.
Flight::route(
'/blog(/@year(/@month(/@day)))',
function(?string $year, ?string $month, ?string $day) {
// Cela correspondra aux URL suivantes :
// /blog/2012/12/10
// /blog/2012/12
// /blog/2012
// /blog
}
);
Tout paramètre optionnel qui n'est pas associé sera passé en tant que NULL
.
Routage avec joker
La correspondance n'est faite que sur des segments d'URL individuels. Si vous voulez associer plusieurs segments, vous pouvez utiliser le joker *
.
Flight::route('/blog/*', function () {
// Cela correspondra à /blog/2000/02/01
});
Pour router toutes les requêtes vers un seul rappel, vous pouvez faire :
Flight::route('*', function () {
// Faites quelque chose
});
Gestionnaire 404 Non trouvé
Par défaut, si une URL ne peut pas être trouvée, Flight enverra une réponse HTTP 404 Not Found
qui est très simple et basique.
Si vous voulez avoir une réponse 404 plus personnalisée, vous pouvez mapper votre propre méthode notFound
:
Flight::map('notFound', function() {
$url = Flight::request()->url;
// Vous pourriez aussi utiliser Flight::render() avec un modèle personnalisé.
$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();
});
Gestionnaire Méthode non trouvée
Par défaut, si une URL est trouvée mais que la méthode n'est pas autorisée, Flight enverra une réponse HTTP 405 Method Not Allowed
qui est très simple et basique (Ex : Method Not Allowed. Allowed Methods are: GET, POST). Elle inclura également un en-tête Allow
avec les méthodes autorisées pour cette URL.
Si vous voulez avoir une réponse 405 plus personnalisée, vous pouvez mapper votre propre méthode methodNotFound
:
use flight\net\Route;
Flight::map('methodNotFound', function(Route $route) {
$url = Flight::request()->url;
$methods = implode(', ', $route->methods);
// Vous pourriez aussi utiliser Flight::render() avec un modèle personnalisé.
$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();
});
Utilisation avancée
Injection de dépendances dans les routes
Si vous voulez utiliser l'injection de dépendances via un conteneur (PSR-11, PHP-DI, Dice, etc.), le seul type de routes où cela est disponible est soit en créant directement l'objet vous-même et en utilisant le conteneur pour créer votre objet, soit en utilisant des chaînes pour définir la classe et la méthode à appeler. Vous pouvez consulter la page Injection de dépendances pour plus d'informations.
Voici un exemple rapide :
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
// Configurez le conteneur avec les paramètres dont vous avez besoin
// Voir la page Injection de dépendances pour plus d'informations sur PSR-11
$dice = new \Dice\Dice();
// N'oubliez pas de réassigner la variable avec '$dice = '!!!!!
$dice = $dice->addRule('flight\database\PdoWrapper', [
'shared' => true,
'constructParams' => [
'mysql:host=localhost;dbname=test',
'root',
'password'
]
]);
// Enregistrez le gestionnaire de conteneur
Flight::registerContainerHandler(function($class, $params) use ($dice) {
return $dice->create($class, $params);
});
// Routes comme d'habitude
Flight::route('/hello/@id', [ 'Greeting', 'hello' ]);
// ou
Flight::route('/hello/@id', 'Greeting->hello');
// ou
Flight::route('/hello/@id', 'Greeting::hello');
Flight::start();
Passer l'exécution à la route suivante
Déprécié
Vous pouvez passer l'exécution à la route correspondante suivante en retournant true
depuis votre fonction de rappel.
Flight::route('/user/@name', function (string $name) {
// Vérifiez une condition
if ($name !== "Bob") {
// Continuez vers la route suivante
return true;
}
});
Flight::route('/user/*', function () {
// Cela sera appelé
});
Il est maintenant recommandé d'utiliser middleware pour gérer des cas d'utilisation complexes comme celui-ci.
Aliasing de route
En assignant un alias à une route, vous pouvez plus tard appeler cet alias dans votre application de manière dynamique pour qu'il soit généré plus tard dans votre code (ex : un lien dans un modèle HTML, ou générer une URL de redirection).
Flight::route('/users/@id', function($id) { echo 'user:'.$id; }, false, 'user_view');
// ou
Flight::route('/users/@id', function($id) { echo 'user:'.$id; })->setAlias('user_view');
// plus tard dans le code quelque part
class UserController {
public function update() {
// code pour sauvegarder l'utilisateur...
$id = $user['id']; // 5 par exemple
$redirectUrl = Flight::getUrl('user_view', [ 'id' => $id ]); // renverra '/users/5'
Flight::redirect($redirectUrl);
}
}
Ceci est particulièrement utile si votre URL change. Dans l'exemple ci-dessus, supposons que users ait été déplacé vers /admin/users/@id
à la place.
Avec l'aliasing en place pour la route, vous n'avez plus besoin de trouver toutes les anciennes URL dans votre code et de les changer car l'alias renverra maintenant /admin/users/5
comme dans l'exemple ci-dessus.
L'aliasing de route fonctionne encore dans les groupes :
Flight::group('/users', function() {
Flight::route('/@id', function($id) { echo 'user:'.$id; }, false, 'user_view');
// ou
Flight::route('/@id', function($id) { echo 'user:'.$id; })->setAlias('user_view');
});
Inspection des informations de route
Si vous voulez inspecter les informations de la route correspondante, il y a 2 façons de faire ceci :
- Vous pouvez utiliser une propriété
executedRoute
sur l'objetFlight::router()
. - Vous pouvez demander que l'objet route soit passé à votre rappel en passant
true
comme troisième paramètre dans la méthode route. L'objet route sera toujours le dernier paramètre passé à votre fonction de rappel.
executedRoute
Flight::route('/', function() {
$route = Flight::router()->executedRoute;
// Faites quelque chose avec $route
// Tableau des méthodes HTTP associées
$route->methods;
// Tableau des paramètres nommés
$route->params;
// Expression régulière correspondante
$route->regex;
// Contient le contenu de tout '*' utilisé dans le motif d'URL
$route->splat;
// Montre le chemin d'URL....si vous en avez vraiment besoin
$route->pattern;
// Montre le middleware assigné à ceci
$route->middleware;
// Montre l'alias assigné à cette route
$route->alias;
});
Note : La propriété
executedRoute
ne sera définie qu'après qu'une route ait été exécutée. Si vous essayez d'y accéder avant qu'une route ait été exécutée, elle seraNULL
. Vous pouvez aussi utiliser executedRoute dans middleware !
Passer true
à la définition de route
Flight::route('/', function(\flight\net\Route $route) {
// Tableau des méthodes HTTP associées
$route->methods;
// Tableau des paramètres nommés
$route->params;
// Expression régulière correspondante
$route->regex;
// Contient le contenu de tout '*' utilisé dans le motif d'URL
$route->splat;
// Montre le chemin d'URL....si vous en avez vraiment besoin
$route->pattern;
// Montre le middleware assigné à ceci
$route->middleware;
// Montre l'alias assigné à cette route
$route->alias;
}, true);// <-- Ce paramètre true est ce qui rend cela possible
Groupement de routes et Middleware
Il peut y avoir des moments où vous voulez grouper des routes liées ensemble (comme /api/v1
).
Vous pouvez le faire en utilisant la méthode group
:
Flight::group('/api/v1', function () {
Flight::route('/users', function () {
// Correspond à /api/v1/users
});
Flight::route('/posts', function () {
// Correspond à /api/v1/posts
});
});
Vous pouvez même imbriquer des groupes de groupes :
Flight::group('/api', function () {
Flight::group('/v1', function () {
// Flight::get() obtient des variables, cela ne définit pas une route ! Voir le contexte objet ci-dessous
Flight::route('GET /users', function () {
// Correspond à GET /api/v1/users
});
Flight::post('/posts', function () {
// Correspond à POST /api/v1/posts
});
Flight::put('/posts/1', function () {
// Correspond à PUT /api/v1/posts
});
});
Flight::group('/v2', function () {
// Flight::get() obtient des variables, cela ne définit pas une route ! Voir le contexte objet ci-dessous
Flight::route('GET /users', function () {
// Correspond à GET /api/v2/users
});
});
});
Groupement avec contexte objet
Vous pouvez toujours utiliser le groupement de routes avec l'objet Engine
de la manière suivante :
$app = Flight::app();
$app->group('/api/v1', function (Router $router) {
// utilisez la variable $router
$router->get('/users', function () {
// Correspond à GET /api/v1/users
});
$router->post('/posts', function () {
// Correspond à POST /api/v1/posts
});
});
Note : C'est la méthode préférée pour définir des routes et des groupes avec l'objet
$router
.
Groupement avec Middleware
Vous pouvez également assigner un middleware à un groupe de routes :
Flight::group('/api/v1', function () {
Flight::route('/users', function () {
// Correspond à /api/v1/users
});
}, [ MyAuthMiddleware::class ]); // ou [ new MyAuthMiddleware() ] si vous voulez utiliser une instance
Voir plus de détails sur la page group middleware.
Routage de ressources
Vous pouvez créer un ensemble de routes pour une ressource en utilisant la méthode resource
. Cela créera un ensemble de routes pour une ressource qui suit les conventions RESTful.
Pour créer une ressource, faites ceci :
Flight::resource('/users', UsersController::class);
Et ce qui se passera en arrière-plan est qu'il créera les routes suivantes :
[
'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'
]
Et votre contrôleur utilisera les méthodes suivantes :
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
{
}
}
Note : Vous pouvez visualiser les routes nouvellement ajoutées avec
runway
en exécutantphp runway routes
.
Personnalisation des routes de ressources
Il y a quelques options pour configurer les routes de ressources.
Alias de base
Vous pouvez configurer l'aliasBase
. Par défaut, l'alias est la dernière partie de l'URL spécifiée.
Par exemple, /users/
résulterait en un aliasBase
de users
. Lorsque ces routes sont créées, les alias sont users.index
, users.create
, etc. Si vous voulez changer l'alias, définissez l'aliasBase
à la valeur que vous voulez.
Flight::resource('/users', UsersController::class, [ 'aliasBase' => 'user' ]);
Only et Except
Vous pouvez également spécifier quelles routes vous voulez créer en utilisant les options only
et except
.
// Liste blanche seulement ces méthodes et liste noire le reste
Flight::resource('/users', UsersController::class, [ 'only' => [ 'index', 'show' ] ]);
// Liste noire seulement ces méthodes et liste blanche le reste
Flight::resource('/users', UsersController::class, [ 'except' => [ 'create', 'store', 'edit', 'update', 'destroy' ] ]);
Ce sont essentiellement des options de liste blanche et liste noire pour que vous puissiez spécifier quelles routes vous voulez créer.
Middleware
Vous pouvez également spécifier un middleware à exécuter sur chacune des routes créées par la méthode resource
.
Flight::resource('/users', UsersController::class, [ 'middleware' => [ MyAuthMiddleware::class ] ]);
Réponses en streaming
Vous pouvez maintenant diffuser des réponses au client en utilisant stream()
ou streamWithHeaders()
.
Ceci est utile pour envoyer de grands fichiers, des processus à longue durée, ou générer de grandes réponses.
Le streaming d'une route est géré un peu différemment qu'une route régulière.
Note : Les réponses en streaming ne sont disponibles que si vous avez
flight.v2.output_buffering
défini àfalse
.
Stream avec en-têtes manuels
Vous pouvez diffuser une réponse au client en utilisant la méthode stream()
sur une route. Si vous
faites cela, vous devez définir tous les en-têtes manuellement avant de sortir quoi que ce soit vers le client.
Ceci se fait avec la fonction php header()
ou la méthode Flight::response()->setRealHeader()
.
Flight::route('/@filename', function($filename) {
$response = Flight::response();
// évidemment vous devriez sanitiser le chemin et tout ça.
$fileNameSafe = basename($filename);
// Si vous avez des en-têtes supplémentaires à définir ici après que la route ait été exécutée
// vous devez les définir avant que quoi que ce soit ne soit échoé.
// Ils doivent tous être un appel brut à la fonction header() ou
// un appel à Flight::response()->setRealHeader()
header('Content-Disposition: attachment; filename="'.$fileNameSafe.'"');
// ou
$response->setRealHeader('Content-Disposition: attachment; filename="'.$fileNameSafe.'"');
$filePath = '/some/path/to/files/'.$fileNameSafe;
if (!is_readable($filePath)) {
Flight::halt(404, 'File not found');
}
// définissez manuellement la longueur du contenu si vous le souhaitez
header('Content-Length: '.filesize($filePath));
// ou
$response->setRealHeader('Content-Length: '.filesize($filePath));
// Diffusez le fichier au client au fur et à mesure qu'il est lu
readfile($filePath);
// C'est la ligne magique ici
})->stream();
Stream avec en-têtes
Vous pouvez également utiliser la méthode streamWithHeaders()
pour définir les en-têtes avant de commencer le streaming.
Flight::route('/stream-users', function() {
// vous pouvez ajouter n'importe quels en-têtes supplémentaires que vous voulez ici
// vous devez juste utiliser header() ou Flight::response()->setRealHeader()
// cependant que vous tirez vos données, juste comme exemple...
$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 ',';
}
// Ceci est requis pour envoyer les données au client
ob_flush();
}
echo '}';
// C'est ainsi que vous définirez les en-têtes avant de commencer le streaming.
})->streamWithHeaders([
'Content-Type' => 'application/json',
'Content-Disposition' => 'attachment; filename="users.json"',
// code de statut optionnel, par défaut 200
'status' => 200
]);
Voir aussi
- Middleware - Utiliser du middleware avec des routes pour l'authentification, la journalisation, etc.
- Injection de dépendances - Simplifier la création et la gestion d'objets dans les routes.
- Pourquoi un framework ? - Comprendre les avantages d'utiliser un framework comme Flight.
- Extension - Comment étendre Flight avec votre propre fonctionnalité incluant la méthode
notFound
. - php.net: preg_match - Fonction PHP pour la correspondance d'expressions régulières.
Dépannage
- Les paramètres de route sont associés par ordre, pas par nom. Assurez-vous que l'ordre des paramètres du rappel correspond à la définition de la route.
- Utiliser
Flight::get()
ne définit pas une route ; utilisezFlight::route('GET /...')
pour le routage ou le contexte objet Router dans les groupes (ex.$router->get(...)
). - La propriété executedRoute n'est définie qu'après l'exécution d'une route ; elle est NULL avant l'exécution.
- Le streaming nécessite que la fonctionnalité de tampon de sortie legacy de Flight soit désactivée (
flight.v2.output_buffering = false
). - Pour l'injection de dépendances, seules certaines définitions de routes supportent l'instanciation basée sur conteneur.
404 Non trouvé ou comportement de route inattendu
Si vous voyez une erreur 404 Non trouvé (mais vous jurez sur votre vie que c'est vraiment là et que ce n'est pas une faute de frappe), cela pourrait en fait être un problème avec le fait que vous retournez une valeur dans votre point de terminaison de route au lieu de simplement l'échoer. La raison de cela est intentionnelle mais pourrait surprendre certains développeurs.
Flight::route('/hello', function(){
// Cela pourrait causer une erreur 404 Non trouvé
return 'Hello World';
});
// Ce que vous voulez probablement
Flight::route('/hello', function(){
echo 'Hello World';
});
La raison de cela est en raison d'un mécanisme spécial intégré au routeur qui gère la sortie de retour comme un signal pour "aller à la route suivante". Vous pouvez voir le comportement documenté dans la section Routage.
Journal des modifications
- v3 : Ajout du routage de ressources, de l'aliasing de route, et du support de streaming, groupes de routes, et support de middleware.
- v1 : La grande majorité des fonctionnalités de base disponibles.
Learn/learn
Découvrir Flight
Flight est un framework rapide, simple et extensible pour PHP. Il est très polyvalent et peut être utilisé pour construire tout type d'application web. Il est conçu avec la simplicité en tête et écrit de manière à être facile à comprendre et à utiliser.
Note : Vous verrez des exemples qui utilisent
Flight::
comme une variable statique et d'autres qui utilisent l'objet Engine$app->
. Les deux fonctionnent de manière interchangeable avec l'autre.$app
et$this->app
dans un contrôleur/middleware est l'approche recommandée par l'équipe Flight.
Composants de base
Routage
Apprenez à gérer les routes pour votre application web. Cela inclut également le groupement de routes, les paramètres de route et les middleware.
Middleware
Apprenez à utiliser les middleware pour filtrer les requêtes et les réponses dans votre application.
Autoloading
Apprenez à charger automatiquement vos propres classes dans votre application.
Requêtes
Apprenez à gérer les requêtes et les réponses dans votre application.
Réponses
Apprenez à envoyer des réponses à vos utilisateurs.
Modèles HTML
Apprenez à utiliser le moteur de vue intégré pour rendre vos modèles HTML.
Sécurité
Apprenez à sécuriser votre application contre les menaces de sécurité courantes.
Configuration
Apprenez à configurer le framework pour votre application.
Gestionnaire d'événements
Apprenez à utiliser le système d'événements pour ajouter des événements personnalisés à votre application.
Extension de Flight
Apprenez à étendre le framework en ajoutant vos propres méthodes et classes.
Crochets de méthodes et filtrage
Apprenez à ajouter des crochets d'événements à vos méthodes et aux méthodes internes du framework.
Conteneur d'injection de dépendances (DIC)
Apprenez à utiliser les conteneurs d'injection de dépendances (DIC) pour gérer les dépendances de votre application.
Classes utilitaires
Collections
Les collections sont utilisées pour stocker des données et y accéder comme un tableau ou un objet pour plus de simplicité.
Wrapper JSON
Cela comprend quelques fonctions simples pour rendre l'encodage et le décodage de votre JSON cohérents.
Wrapper PDO
PDO peut parfois causer plus de maux de tête que nécessaire. Cette classe wrapper simple peut rendre l'interaction avec votre base de données significativement plus facile.
Gestionnaire de fichiers téléchargés
Une classe simple pour aider à gérer les fichiers téléchargés et à les déplacer vers un emplacement permanent.
Concepts importants
Pourquoi un framework ?
Voici un court article sur pourquoi vous devriez utiliser un framework. Il est une bonne idée de comprendre les avantages d'utiliser un framework avant de commencer à en utiliser un.
De plus, un excellent tutoriel a été créé par @lubiana. Bien qu'il n'entre pas dans les détails spécifiques sur Flight, ce guide vous aidera à comprendre certains des concepts majeurs entourant un framework et pourquoi ils sont bénéfiques à utiliser. Vous pouvez trouver le tutoriel ici.
Flight comparé à d'autres frameworks
Si vous migrez d'un autre framework comme Laravel, Slim, Fat-Free ou Symfony vers Flight, cette page vous aidera à comprendre les différences entre les deux.
Autres sujets
Tests unitaires
Suivez ce guide pour apprendre à tester vos unités de code Flight de manière solide.
IA et expérience développeur
Apprenez comment Flight fonctionne avec les outils d'IA et les flux de travail modernes des développeurs pour vous aider à coder plus rapidement et plus intelligemment.
Migration v2 -> v3
La compatibilité descendante a été maintenue dans l'ensemble, mais il y a certains changements dont vous devriez être conscient lors de la migration de v2 à v3.
Learn/unit_testing
Tests unitaires
Aperçu
Les tests unitaires dans Flight vous aident à vous assurer que votre application se comporte comme prévu, à détecter les bogues tôt et à rendre votre codebase plus facile à maintenir. Flight est conçu pour fonctionner sans heurts avec PHPUnit, le framework de test PHP le plus populaire.
Comprendre
Les tests unitaires vérifient le comportement de petites parties de votre application (comme les contrôleurs ou les services) de manière isolée. Dans Flight, cela signifie tester comment vos routes, contrôleurs et logique répondent à différents inputs — sans dépendre d'un état global ou de services externes réels.
Principes clés :
- Tester le comportement, pas l'implémentation : Concentrez-vous sur ce que fait votre code, pas sur comment il le fait.
- Éviter l'état global : Utilisez l'injection de dépendances au lieu de
Flight::set()
ouFlight::get()
. - Simuler les services externes : Remplacez les éléments comme les bases de données ou les envois d'e-mails par des doubles de test.
- Garder les tests rapides et focalisés : Les tests unitaires ne doivent pas interagir avec de vraies bases de données ou APIs.
Utilisation de base
Configuration de PHPUnit
- Installez PHPUnit avec Composer :
composer require --dev phpunit/phpunit
- Créez un répertoire
tests
à la racine de votre projet. - Ajoutez un script de test à votre
composer.json
:"scripts": { "test": "phpunit --configuration phpunit.xml" }
- Créez un fichier
phpunit.xml
:<?xml version="1.0" encoding="UTF-8"?> <phpunit bootstrap="vendor/autoload.php"> <testsuites> <testsuite name="Flight Tests"> <directory>tests</directory> </testsuite> </testsuites> </phpunit>
Maintenant, vous pouvez exécuter vos tests avec composer test
.
Tester un gestionnaire de route simple
Supposons que vous ayez une route qui valide un e-mail :
// 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']);
}
}
Un test simple pour ce contrôleur :
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']);
}
}
Conseils :
- Simulez les données POST en utilisant
$app->request()->data
. - Évitez d'utiliser les méthodes statiques
Flight::
dans vos tests — utilisez l'instance$app
.
Utiliser l'injection de dépendances pour des contrôleurs testables
Injectez les dépendances (comme la base de données ou l'envoi d'e-mails) dans vos contrôleurs pour les rendre faciles à simuler dans les tests :
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']);
}
}
Et un test avec des simulations :
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);
}
}
Utilisation avancée
- Simulation : Utilisez les simulations intégrées de PHPUnit ou des classes anonymes pour remplacer les dépendances.
- Tester les contrôleurs directement : Instanciez les contrôleurs avec un nouveau
Engine
et simulez les dépendances. - Éviter la sur-simulation : Laissez la logique réelle s'exécuter quand c'est possible ; ne simulez que les services externes.
Voir aussi
- Guide des tests unitaires - Un guide complet sur les meilleures pratiques pour les tests unitaires.
- Conteneur d'injection de dépendances - Comment utiliser les DIC pour gérer les dépendances et améliorer la testabilité.
- Extension - Comment ajouter vos propres aides ou surcharger les classes de base.
- Wrapper PDO - Simplifie les interactions avec la base de données et est plus facile à simuler dans les tests.
- Requêtes - Gestion des requêtes HTTP dans Flight.
- Réponses - Envoi de réponses aux utilisateurs.
- Tests unitaires et principes SOLID - Apprenez comment les principes SOLID peuvent améliorer vos tests unitaires.
Dépannage
- Évitez d'utiliser l'état global (
Flight::set()
,$_SESSION
, etc.) dans votre code et vos tests. - Si vos tests sont lents, vous écrivez peut-être des tests d'intégration — simulez les services externes pour garder les tests unitaires rapides.
- Si la configuration des tests est complexe, envisagez de refactoriser votre code pour utiliser l'injection de dépendances.
Journal des modifications
- v3.15.0 - Ajout d'exemples pour l'injection de dépendances et la simulation.
Learn/flight_vs_symfony
Vol Flight vs Symfony
Qu'est-ce que Symfony?
Symfony est un ensemble de composants PHP réutilisables et un framework PHP pour les projets web.
La base standard sur laquelle les meilleures applications PHP sont construites. Choisissez l'un des 50 composants autonomes disponibles pour vos propres applications.
Accélérez la création et la maintenance de vos applications web PHP. Mettez fin aux tâches de codage répétitives et profitez du pouvoir de contrôler votre code.
Avantages par rapport à Flight
- Symfony a un écosystème énorme de développeurs et de modules pouvant être utilisés pour résoudre les problèmes courants.
- Symfony dispose d'un ORM complet (Doctrine) qui peut être utilisé pour interagir avec votre base de données.
- Symfony dispose d'une grande quantité de documentation et de tutoriels pouvant être utilisés pour apprendre le framework.
- Symfony propose des podcasts, des conférences, des réunions, des vidéos et d'autres ressources pouvant être utilisés pour apprendre le framework.
- Symfony est destiné à un développeur expérimenté qui cherche à construire une application web d'entreprise complète.
Inconvénients par rapport à Flight
- Symfony a beaucoup plus de fonctionnalités sous le capot que Flight. Cela se fait à un coût dramatique en termes de performances. Consultez les tests de TechEmpower pour plus d'informations.
- Flight est destiné à un développeur cherchant à construire une application web légère, rapide et facile à utiliser.
- Flight est axé sur la simplicité et la facilité d'utilisation.
- L'une des fonctionnalités principales de Flight est de faire de son mieux pour maintenir la compatibilité ascendante.
- Flight n'a aucune dépendance, tandis que Symfony a tout un tas de dépendances
- Flight est destiné aux développeurs qui s'aventurent dans le monde des frameworks pour la première fois.
- Flight peut également gérer des applications de niveau entreprise, mais il n'a pas autant d'exemples et de tutoriels que Symfony. Cela demandera également plus de discipline de la part du développeur pour maintenir l'organisation et la structure.
- Flight donne au développeur plus de contrôle sur l'application, tandis que Symfony peut introduire de la magie en coulisses.
Learn/flight_vs_another_framework
Comparaison de Flight à un autre Framework
Si vous migrez d'un autre framework tel que Laravel, Slim, Fat-Free, ou Symfony vers Flight, cette page vous aidera à comprendre les différences entre les deux.
Laravel
Laravel est un framework complet qui possède toutes les fonctionnalités et une incroyable écosystème axé sur les développeurs, mais cela a un coût en termes de performances et de complexité.
Voir la comparaison entre Laravel et Flight.
Slim
Slim est un micro-framework similaire à Flight. Il est conçu pour être léger et facile à utiliser, mais peut être un peu plus complexe que Flight.
Voir la comparaison entre Slim et Flight.
Fat-Free
Fat-Free est un framework full-stack dans un package beaucoup plus petit. Bien qu'il possède tous les outils nécessaires, il a une architecture des données qui peut rendre certains projets plus complexes qu'ils ne le devraient.
Voir la comparaison entre Fat-Free et Flight.
Symfony
Symfony est un framework modulaire de niveau entreprise conçu pour être flexible et scalable. Pour les petits projets ou les nouveaux développeurs, Symfony peut être un peu intimidant.
Voir la comparaison entre Symfony et Flight.
Learn/pdo_wrapper
Classe d'Aide PDO PdoWrapper
Aperçu
La classe PdoWrapper
dans Flight est un assistant convivial pour travailler avec les bases de données en utilisant PDO. Elle simplifie les tâches courantes de base de données, ajoute des méthodes pratiques pour récupérer les résultats, et retourne les résultats sous forme de Collections pour un accès facile. Elle supporte également la journalisation des requêtes et la surveillance des performances de l'application (APM) pour des cas d'utilisation avancés.
Comprendre
Travailler avec les bases de données en PHP peut être un peu verbeux, surtout en utilisant PDO directement. PdoWrapper
étend PDO et ajoute des méthodes qui rendent les requêtes, la récupération et la gestion des résultats beaucoup plus faciles. Au lieu de jongler avec des instructions préparées et des modes de récupération, vous obtenez des méthodes simples pour les tâches courantes, et chaque ligne est retournée sous forme de Collection, afin que vous puissiez utiliser la notation tableau ou objet.
Vous pouvez enregistrer PdoWrapper
en tant que service partagé dans Flight, puis l'utiliser n'importe où dans votre application via Flight::db()
.
Utilisation de Base
Enregistrement de l'Aideur PDO
D'abord, enregistrez la classe PdoWrapper
avec 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
]
]);
Maintenant, vous pouvez utiliser Flight::db()
n'importe où pour obtenir votre connexion à la base de données.
Exécution de Requêtes
runQuery()
function runQuery(string $sql, array $params = []): PDOStatement
Utilisez ceci pour les INSERT, UPDATE, ou lorsque vous voulez récupérer les résultats manuellement :
$db = Flight::db();
$statement = $db->runQuery("SELECT * FROM users WHERE status = ?", ['active']);
while ($row = $statement->fetch()) {
// $row est un tableau
}
Vous pouvez également l'utiliser pour les écritures :
$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
Obtenez une seule valeur de la base de données :
$count = Flight::db()->fetchField("SELECT COUNT(*) FROM users WHERE status = ?", ['active']);
fetchRow()
function fetchRow(string $sql, array $params = []): Collection
Obtenez une seule ligne sous forme de Collection (accès tableau/objet) :
$user = Flight::db()->fetchRow("SELECT * FROM users WHERE id = ?", [123]);
echo $user['name'];
// ou
echo $user->name;
fetchAll()
function fetchAll(string $sql, array $params = []): array<Collection>
Obtenez toutes les lignes sous forme de tableau de Collections :
$users = Flight::db()->fetchAll("SELECT * FROM users WHERE status = ?", ['active']);
foreach ($users as $user) {
echo $user['name'];
// ou
echo $user->name;
}
Utilisation des Lieux-Tenants IN()
Vous pouvez utiliser un seul ?
dans une clause IN()
et passer un tableau ou une chaîne séparée par des virgules :
$ids = [1, 2, 3];
$users = Flight::db()->fetchAll("SELECT * FROM users WHERE id IN (?)", [$ids]);
// ou
$users = Flight::db()->fetchAll("SELECT * FROM users WHERE id IN (?)", ['1,2,3']);
Utilisation Avancée
Journalisation des Requêtes & APM
Si vous voulez suivre les performances des requêtes, activez le suivi APM lors de l'enregistrement :
Flight::register('db', \flight\database\PdoWrapper::class, [
'mysql:host=localhost;dbname=cool_db_name', 'user', 'pass', [/* options */], true // dernier paramètre active APM
]);
Après l'exécution des requêtes, vous pouvez les journaliser manuellement, mais l'APM les journalisera automatiquement si activé :
Flight::db()->logQueries();
Cela déclenchera un événement (flight.db.queries
) avec les métriques de connexion et de requête, que vous pouvez écouter en utilisant le système d'événements de Flight.
Exemple Complet
Flight::route('/users', function () {
// Obtenir tous les utilisateurs
$users = Flight::db()->fetchAll('SELECT * FROM users');
// Diffuser tous les utilisateurs
$statement = Flight::db()->runQuery('SELECT * FROM users');
while ($user = $statement->fetch()) {
echo $user['name'];
}
// Obtenir un seul utilisateur
$user = Flight::db()->fetchRow('SELECT * FROM users WHERE id = ?', [123]);
// Obtenir une seule valeur
$count = Flight::db()->fetchField('SELECT COUNT(*) FROM users');
// Syntaxe spéciale IN()
$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']);
// Insérer un nouvel utilisateur
Flight::db()->runQuery("INSERT INTO users (name, email) VALUES (?, ?)", ['Bob', 'bob@example.com']);
$insert_id = Flight::db()->lastInsertId();
// Mettre à jour un utilisateur
Flight::db()->runQuery("UPDATE users SET name = ? WHERE id = ?", ['Bob', 123]);
// Supprimer un utilisateur
Flight::db()->runQuery("DELETE FROM users WHERE id = ?", [123]);
// Obtenir le nombre de lignes affectées
$statement = Flight::db()->runQuery("UPDATE users SET name = ? WHERE name = ?", ['Bob', 'Sally']);
$affected_rows = $statement->rowCount();
});
Voir Aussi
- Collections - Apprenez comment utiliser la classe Collection pour un accès facile aux données.
Dépannage
- Si vous obtenez une erreur concernant la connexion à la base de données, vérifiez votre DSN, nom d'utilisateur, mot de passe et options.
- Toutes les lignes sont retournées sous forme de Collections—si vous avez besoin d'un tableau simple, utilisez
$collection->getData()
. - Pour les requêtes
IN (?)
, assurez-vous de passer un tableau ou une chaîne séparée par des virgules.
Journal des Modifications
- v3.2.0 - Publication initiale de PdoWrapper avec les méthodes de requête et de récupération de base.
Learn/dependency_injection_container
Conteneur d'injection de dépendances
Aperçu
Le Conteneur d'injection de dépendances (DIC) est une amélioration puissante qui vous permet de gérer les dépendances de votre application.
Comprendre
L'injection de dépendances (DI) est un concept clé dans les frameworks PHP modernes et est utilisée pour gérer l'instanciation et la configuration des objets. Certains exemples de bibliothèques DIC sont : flightphp/container, Dice, Pimple, PHP-DI, et league/container.
Un DIC est une façon élégante de vous permettre de créer et de gérer vos classes dans un emplacement centralisé. Cela est utile lorsque vous devez passer le même objet à plusieurs classes (comme vos contrôleurs ou middleware par exemple).
Utilisation de base
L'ancienne façon de faire les choses pourrait ressembler à ceci :
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();
Vous pouvez voir dans le code ci-dessus que nous créons un nouvel objet PDO
et le passons
à notre classe UserController
. Cela est correct pour une petite application, mais à mesure que
votre application grandit, vous constaterez que vous créez ou passez le même objet PDO
à plusieurs endroits. C'est là qu'un DIC entre en jeu de manière pratique.
Voici le même exemple en utilisant un DIC (avec 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();
Je parie que vous pensez peut-être qu'il y a beaucoup de code supplémentaire ajouté à l'exemple.
La magie vient quand vous avez un autre contrôleur qui a besoin de l'objet PDO
.
// 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' ]);
L'avantage supplémentaire d'utiliser un DIC est que les tests unitaires deviennent beaucoup plus faciles. Vous pouvez créer un objet mock et le passer à votre classe. C'est un énorme avantage lorsque vous écrivez des tests pour votre application !
Créer un gestionnaire DIC centralisé
Vous pouvez créer un gestionnaire DIC centralisé dans votre fichier de services en étendant votre application. Voici un exemple :
// 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 dispose d'un plugin qui fournit un conteneur simple conforme à PSR-11 que vous pouvez utiliser pour gérer votre injection de dépendances. Voici un exemple rapide de son utilisation :
// 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();
Utilisation avancée de flightphp/container
Vous pouvez également résoudre les dépendances de manière récursive. Voici un exemple :
<?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
Vous pouvez également créer votre propre gestionnaire DIC. Cela est utile si vous avez un conteneur personnalisé que vous souhaitez utiliser qui n'est pas conforme à PSR-11 (Dice). Voir la section d'utilisation de base pour savoir comment faire.
De plus, il y a des valeurs par défaut utiles qui rendront votre vie plus facile lors de l'utilisation de Flight.
Instance Engine
Si vous utilisez l'instance Engine
dans vos contrôleurs/middleware, voici
comment vous la configureriez :
// 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');
}
}
Ajouter d'autres classes
Si vous avez d'autres classes que vous souhaitez ajouter au conteneur, avec Dice c'est facile car elles seront automatiquement résolues par le conteneur. Voici un exemple :
$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 peut également utiliser n'importe quel conteneur conforme à PSR-11. Cela signifie que vous pouvez utiliser n'importe quel conteneur qui implémente l'interface PSR-11. Voici un exemple en utilisant le conteneur PSR-11 de League :
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();
Cela peut être un peu plus verbeux que l'exemple précédent avec Dice, cela accomplit toujours la tâche avec les mêmes avantages !
Voir aussi
- Extending Flight - Apprenez comment vous pouvez ajouter l'injection de dépendances à vos propres classes en étendant le framework.
- Configuration - Apprenez comment configurer Flight pour votre application.
- Routing - Apprenez comment définir des routes pour votre application et comment l'injection de dépendances fonctionne avec les contrôleurs.
- Middleware - Apprenez comment créer du middleware pour votre application et comment l'injection de dépendances fonctionne avec le middleware.
Dépannage
- Si vous rencontrez des problèmes avec votre conteneur, assurez-vous de passer les noms de classes corrects au conteneur.
Journal des modifications
- v3.7.0 - Ajout de la possibilité d'enregistrer un gestionnaire DIC avec Flight.
Learn/middleware
Middleware
Aperçu
Flight prend en charge les middlewares de route et de groupe de routes. Le middleware est une partie de votre application où le code est exécuté avant (ou après) le rappel de la route. C'est une excellente façon d'ajouter des vérifications d'authentification API dans votre code, ou de valider que l'utilisateur a la permission d'accéder à la route.
Comprendre
Les middlewares peuvent grandement simplifier votre application. Au lieu d'une inheritance de classes abstraites complexes ou de substitutions de méthodes, les middlewares vous permettent de contrôler vos routes en assignant votre logique d'application personnalisée. Vous pouvez penser aux middlewares comme à un sandwich. Vous avez du pain à l'extérieur, et puis des couches de garnitures comme de la laitue, des tomates, de la viande et du fromage. Imaginez que chaque requête est comme prendre une bouchée du sandwich où vous mangez les couches extérieures en premier et progressez vers le cœur.
Voici une visualisation de la façon dont les middlewares fonctionnent. Ensuite, nous vous montrerons un exemple pratique de son fonctionnement.
Requête utilisateur à l'URL /api ---->
Middleware->before() exécuté ----->
Fonction callable/méthode attachée à /api exécutée et réponse générée ------>
Middleware->after() exécuté ----->
L'utilisateur reçoit la réponse du serveur
Et voici un exemple pratique :
L'utilisateur navigue vers l'URL /dashboard
LoggedInMiddleware->before() s'exécute
before() vérifie une session connectée valide
si oui, ne rien faire et continuer l'exécution
si non, rediriger l'utilisateur vers /login
Fonction callable/méthode attachée à /api exécutée et réponse générée
LoggedInMiddleware->after() n'a rien de défini donc il laisse l'exécution continuer
L'utilisateur reçoit le HTML du tableau de bord du serveur
Ordre d'exécution
Les fonctions de middleware sont exécutées dans l'ordre où elles sont ajoutées à la route. L'exécution est similaire à la façon dont Slim Framework gère cela.
Les méthodes before()
sont exécutées dans l'ordre d'ajout, et les méthodes after()
sont exécutées en ordre inverse.
Ex : Middleware1->before(), Middleware2->before(), Middleware2->after(), Middleware1->after().
Utilisation de base
Vous pouvez utiliser des middlewares comme n'importe quelle méthode callable, y compris une fonction anonyme ou une classe (recommandé)
Fonction anonyme
Voici un exemple simple :
Flight::route('/path', function() { echo ' Here I am!'; })->addMiddleware(function() {
echo 'Middleware first!';
});
Flight::start();
// Cela affichera "Middleware first! Here I am!"
Note : Lorsque vous utilisez une fonction anonyme, la seule méthode interprétée est une méthode
before()
. Vous ne pouvez pas définir un comportementafter()
avec une classe anonyme.
Utilisation de classes
Les middlewares peuvent (et devraient) être enregistrés comme une classe. Si vous avez besoin de la fonctionnalité "after", vous devez utiliser une classe.
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);
// aussi ->addMiddleware([ $MyMiddleware, $MyMiddleware2 ]);
Flight::start();
// Cela affichera "Middleware first! Here I am! Middleware last!"
Vous pouvez aussi simplement définir le nom de la classe de middleware et elle instanciera la classe.
Flight::route('/path', function() { echo ' Here I am! '; })->addMiddleware(MyMiddleware::class);
Note : Si vous passez simplement le nom du middleware, il sera automatiquement exécuté par le conteneur d'injection de dépendances et le middleware sera exécuté avec les paramètres dont il a besoin. Si vous n'avez pas de conteneur d'injection de dépendances enregistré, il passera par défaut l'instance
flight\Engine
dans le__construct(Engine $app)
.
Utilisation de routes avec paramètres
Si vous avez besoin de paramètres de votre route, ils seront passés dans un seul tableau à votre fonction de middleware. (function($params) { ... }
ou public function before($params) { ... }
). La raison en est que vous pouvez structurer vos paramètres en groupes et dans certains de ces groupes, vos paramètres peuvent apparaître dans un ordre différent, ce qui casserait la fonction de middleware en se référant au mauvais paramètre. De cette façon, vous pouvez y accéder par nom au lieu de position.
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 peut ou non être passé
$jobId = $params['jobId'] ?? 0;
// peut-être que s'il n'y a pas d'ID de job, vous n'avez pas besoin de rechercher quoi que ce soit.
if($jobId === 0) {
return;
}
// effectuer une recherche de quelque sorte dans votre base de données
$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) {
// Ce groupe ci-dessous obtient toujours le middleware parent
// Mais les paramètres sont passés dans un seul tableau
// dans le middleware.
$router->group('/job/@jobId', function(Router $router) {
$router->get('', [ JobController::class, 'view' ]);
$router->put('', [ JobController::class, 'update' ]);
$router->delete('', [ JobController::class, 'delete' ]);
// plus de routes...
});
}, [ RouteSecurityMiddleware::class ]);
Groupement de routes avec middleware
Vous pouvez ajouter un groupe de routes, et puis chaque route dans ce groupe aura le même middleware. C'est utile si vous avez besoin de grouper un tas de routes par un middleware Auth pour vérifier la clé API dans l'en-tête.
// ajouté à la fin de la méthode group
Flight::group('/api', function() {
// Cette route "vide" correspondra en fait à /api
Flight::route('', function() { echo 'api'; }, false, 'api');
// Cela correspondra à /api/users
Flight::route('/users', function() { echo 'users'; }, false, 'users');
// Cela correspondra à /api/users/1234
Flight::route('/users/@id', function($id) { echo 'user:'.$id; }, false, 'user_view');
}, [ new ApiAuthMiddleware() ]);
Si vous voulez appliquer un middleware global à toutes vos routes, vous pouvez ajouter un groupe "vide" :
// ajouté à la fin de la méthode group
Flight::group('', function() {
// C'est toujours /users
Flight::route('/users', function() { echo 'users'; }, false, 'users');
// Et cela reste /users/1234
Flight::route('/users/@id', function($id) { echo 'user:'.$id; }, false, 'user_view');
}, [ ApiAuthMiddleware::class ]); // ou [ new ApiAuthMiddleware() ], c'est la même chose
Cas d'utilisation courants
Validation de clé API
Si vous vouliez protéger vos routes /api
en vérifiant que la clé API est correcte, vous pouvez facilement gérer cela avec un middleware.
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);
// effectuer une recherche dans votre base de données pour la clé api
$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' ]);
// plus de routes...
}, [ ApiMiddleware::class ]);
Maintenant toutes vos routes API sont protégées par ce middleware de validation de clé API que vous avez configuré ! Si vous ajoutez plus de routes dans le groupe de routeur, elles auront instantanément la même protection !
Validation de connexion
Voulez-vous protéger certaines routes pour qu'elles ne soient disponibles que pour les utilisateurs connectés ? Cela peut facilement être réalisé avec un middleware !
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' ]);
// plus de routes...
}, [ LoggedInMiddleware::class ]);
Validation de paramètres de route
Voulez-vous protéger vos utilisateurs en changeant les valeurs dans l'URL pour accéder à des données qu'ils ne devraient pas ? Cela peut être résolu avec un middleware !
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'];
// effectuer une recherche de quelque sorte dans votre base de données
$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' ]);
// plus de routes...
}, [ RouteSecurityMiddleware::class ]);
Gestion de l'exécution des middlewares
Disons que vous avez un middleware d'authentification et que vous voulez rediriger l'utilisateur vers une page de connexion s'il n'est pas authentifié. Vous avez plusieurs options à votre disposition :
- Vous pouvez retourner false de la fonction middleware et Flight retournera automatiquement une erreur 403 Forbidden, mais sans personnalisation.
- Vous pouvez rediriger l'utilisateur vers une page de connexion en utilisant
Flight::redirect()
. - Vous pouvez créer une erreur personnalisée dans le middleware et arrêter l'exécution de la route.
Simple et direct
Voici un exemple simple de return false;
:
class MyMiddleware {
public function before($params) {
$hasUserKey = Flight::session()->exists('user');
if ($hasUserKey === false) {
return false;
}
// puisque c'est vrai, tout continue simplement
}
}
Exemple de redirection
Voici un exemple de redirection de l'utilisateur vers une page de connexion :
class MyMiddleware {
public function before($params) {
$hasUserKey = Flight::session()->exists('user');
if ($hasUserKey === false) {
Flight::redirect('/login');
exit;
}
}
}
Exemple d'erreur personnalisée
Disons que vous avez besoin de lancer une erreur JSON parce que vous construisez une API. Vous pouvez faire cela comme ceci :
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);
// ou
Flight::json(['error' => 'You must be logged in to access this page.'], 403);
exit;
// ou
Flight::halt(403, json_encode(['error' => 'You must be logged in to access this page.']);
}
}
}
Voir aussi
- Routing - Comment mapper les routes vers les contrôleurs et rendre les vues.
- Requests - Comprendre comment gérer les requêtes entrantes.
- Responses - Comment personnaliser les réponses HTTP.
- Dependency Injection - Simplifier la création et la gestion d'objets dans les routes.
- Why a Framework? - Comprendre les avantages d'utiliser un framework comme Flight.
- Middleware Execution Strategy Example
Dépannage
- Si vous avez une redirection dans votre middleware, mais que votre application ne semble pas rediriger, assurez-vous d'ajouter une instruction
exit;
dans votre middleware.
Changelog
- v3.1: Ajout du support pour les middlewares.
Learn/filtering
Filtrage
Aperçu
Flight vous permet de filtrer les méthodes mappées avant et après leur appel.
Comprendre
Il n'y a pas de hooks prédéfinis que vous devez mémoriser. Vous pouvez filtrer n'importe laquelle des méthodes par défaut du framework ainsi que n'importe quelles méthodes personnalisées que vous avez mappées.
Une fonction de filtre ressemble à ceci :
/**
* @param array $params Les paramètres passés à la méthode filtrée.
* @param string $output (v2 buffering de sortie uniquement) La sortie de la méthode filtrée.
* @return bool Retournez true/vide ou ne retournez rien pour continuer la chaîne, false pour interrompre la chaîne.
*/
function (array &$params, string &$output): bool {
// Code de filtre
}
En utilisant les variables passées, vous pouvez manipuler les paramètres d'entrée et/ou la sortie.
Vous pouvez faire exécuter un filtre avant une méthode en faisant :
Flight::before('start', function (array &$params, string &$output): bool {
// Faites quelque chose
});
Vous pouvez faire exécuter un filtre après une méthode en faisant :
Flight::after('start', function (array &$params, string &$output): bool {
// Faites quelque chose
});
Vous pouvez ajouter autant de filtres que vous voulez à n'importe quelle méthode. Ils seront appelés dans l'ordre où ils sont déclarés.
Voici un exemple du processus de filtrage :
// Mappez une méthode personnalisée
Flight::map('hello', function (string $name) {
return "Hello, $name!";
});
// Ajoutez un filtre avant
Flight::before('hello', function (array &$params, string &$output): bool {
// Manipulez le paramètre
$params[0] = 'Fred';
return true;
});
// Ajoutez un filtre après
Flight::after('hello', function (array &$params, string &$output): bool {
// Manipulez la sortie
$output .= " Have a nice day!";
return true;
});
// Invoquez la méthode personnalisée
echo Flight::hello('Bob');
Cela devrait afficher :
Hello Fred! Have a nice day!
Si vous avez défini plusieurs filtres, vous pouvez interrompre la chaîne en retournant false
dans l'une de vos fonctions de filtre :
Flight::before('start', function (array &$params, string &$output): bool {
echo 'one';
return true;
});
Flight::before('start', function (array &$params, string &$output): bool {
echo 'two';
// Cela mettra fin à la chaîne
return false;
});
// Ceci ne sera pas appelé
Flight::before('start', function (array &$params, string &$output): bool {
echo 'three';
return true;
});
Note : Les méthodes de base telles que
map
etregister
ne peuvent pas être filtrées car elles sont appelées directement et non invoquées dynamiquement. Voir Extending Flight pour plus d'informations.
Voir aussi
Dépannage
- Assurez-vous de retourner
false
depuis vos fonctions de filtre si vous voulez que la chaîne s'arrête. Si vous ne retournez rien, la chaîne continuera.
Journal des modifications
- v2.0 - Version initiale.
Learn/requests
Requêtes
Aperçu
Flight encapsule la requête HTTP dans un seul objet, qui peut être accédé en faisant :
$request = Flight::request();
Compréhension
Les requêtes HTTP sont l'un des aspects fondamentaux à comprendre concernant le cycle de vie HTTP. Un utilisateur effectue une action dans un navigateur web ou un client HTTP, et ils envoient une série d'en-têtes, de corps, d'URL, etc. vers votre projet. Vous pouvez capturer ces en-têtes (le langage du navigateur, le type de compression qu'ils peuvent gérer, l'agent utilisateur, etc.) et capturer le corps et l'URL qui sont envoyés à votre application Flight. Ces requêtes sont essentielles pour que votre application comprenne quoi faire ensuite.
Utilisation de base
PHP possède plusieurs super globales incluant $_GET
, $_POST
, $_REQUEST
, $_SERVER
, $_FILES
, et $_COOKIE
. Flight abstrait ces éléments en Collections pratiques. Vous pouvez accéder aux propriétés query
, data
, cookies
, et files
en tant qu'arrays ou objets.
Note : Il est FORTEMENT déconseillé d'utiliser ces super globales dans votre projet et elles doivent être référencées via l'objet
request()
.Note : Il n'y a pas d'abstraction disponible pour
$_ENV
.
$_GET
Vous pouvez accéder à l'array $_GET
via la propriété query
:
// GET /search?keyword=something
Flight::route('/search', function(){
$keyword = Flight::request()->query['keyword'];
// ou
$keyword = Flight::request()->query->keyword;
echo "Vous recherchez : $keyword";
// interroger une base de données ou autre chose avec le $keyword
});
$_POST
Vous pouvez accéder à l'array $_POST
via la propriété data
:
Flight::route('POST /submit', function(){
$name = Flight::request()->data['name'];
$email = Flight::request()->data['email'];
// ou
$name = Flight::request()->data->name;
$email = Flight::request()->data->email;
echo "Vous avez soumis : $name, $email";
// sauvegarder dans une base de données ou autre chose avec le $name et $email
});
$_COOKIE
Vous pouvez accéder à l'array $_COOKIE
via la propriété cookies
:
Flight::route('GET /login', function(){
$savedLogin = Flight::request()->cookies['myLoginCookie'];
// ou
$savedLogin = Flight::request()->cookies->myLoginCookie;
// vérifier s'il est vraiment sauvegardé ou non et si oui, les connecter automatiquement
if($savedLogin) {
Flight::redirect('/dashboard');
return;
}
});
Pour de l'aide sur la définition de nouvelles valeurs de cookies, voir overclokk/cookie
$_SERVER
Il existe un raccourci disponible pour accéder à l'array $_SERVER
via la méthode getVar()
:
$host = Flight::request()->getVar('HTTP_HOST');
$_FILES
Vous pouvez accéder aux fichiers téléchargés via la propriété files
:
// accès brut à la propriété $_FILES. Voir ci-dessous pour l'approche recommandée
$uploadedFile = Flight::request()->files['myFile'];
// ou
$uploadedFile = Flight::request()->files->myFile;
Voir Uploaded File Handler pour plus d'informations.
Traitement des téléchargements de fichiers
v3.12.0
Vous pouvez traiter les téléchargements de fichiers en utilisant le framework avec quelques méthodes d'aide. Cela se résume essentiellement à extraire les données de fichier de la requête et à les déplacer vers un nouvel emplacement.
Flight::route('POST /upload', function(){
// Si vous aviez un champ d'entrée comme <input type="file" name="myFile">
$uploadedFileData = Flight::request()->getUploadedFiles();
$uploadedFile = $uploadedFileData['myFile'];
$uploadedFile->moveTo('/path/to/uploads/' . $uploadedFile->getClientFilename());
});
Si vous avez plusieurs fichiers téléchargés, vous pouvez les parcourir en boucle :
Flight::route('POST /upload', function(){
// Si vous aviez un champ d'entrée comme <input type="file" name="myFiles[]">
$uploadedFiles = Flight::request()->getUploadedFiles()['myFiles'];
foreach ($uploadedFiles as $uploadedFile) {
$uploadedFile->moveTo('/path/to/uploads/' . $uploadedFile->getClientFilename());
}
});
Note de sécurité : Validez et nettoyez toujours les entrées utilisateur, surtout lors du traitement des téléchargements de fichiers. Validez toujours le type d'extensions que vous autorisez à être téléchargées, mais vous devriez également valider les "octets magiques" du fichier pour vous assurer qu'il s'agit réellement du type de fichier que l'utilisateur prétend. Il existe des articles et bibliothèques disponibles pour vous aider avec cela.
Corps de la requête
Pour obtenir le corps brut de la requête HTTP, par exemple lors du traitement de requêtes POST/PUT, vous pouvez faire :
Flight::route('POST /users/xml', function(){
$xmlBody = Flight::request()->getBody();
// faire quelque chose avec le XML qui a été envoyé.
});
Corps JSON
Si vous recevez une requête avec le type de contenu application/json
et les données d'exemple {"id": 123}
elle sera disponible via la propriété data
:
$id = Flight::request()->data->id;
En-têtes de la requête
Vous pouvez accéder aux en-têtes de la requête en utilisant la méthode getHeader()
ou getHeaders()
:
// Peut-être que vous avez besoin de l'en-tête Authorization
$host = Flight::request()->getHeader('Authorization');
// ou
$host = Flight::request()->header('Authorization');
// Si vous devez récupérer tous les en-têtes
$headers = Flight::request()->getHeaders();
// ou
$headers = Flight::request()->headers();
Méthode de la requête
Vous pouvez accéder à la méthode de la requête en utilisant la propriété method
ou la méthode getMethod()
:
$method = Flight::request()->method; // en réalité peuplé par getMethod()
$method = Flight::request()->getMethod();
Note : La méthode getMethod()
récupère d'abord la méthode depuis $_SERVER['REQUEST_METHOD']
, puis elle peut être écrasée
par $_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE']
si elle existe ou $_REQUEST['_method']
si elle existe.
Propriétés de l'objet requête
L'objet requête fournit les propriétés suivantes :
- body - Le corps brut de la requête HTTP
- url - L'URL demandée
- base - Le sous-répertoire parent de l'URL
- method - La méthode de la requête (GET, POST, PUT, DELETE)
- referrer - L'URL de référence
- ip - L'adresse IP du client
- ajax - Si la requête est une requête AJAX
- scheme - Le protocole du serveur (http, https)
- user_agent - Les informations du navigateur
- type - Le type de contenu
- length - La longueur du contenu
- query - Les paramètres de la chaîne de requête
- data - Les données POST ou les données JSON
- cookies - Les données des cookies
- files - Les fichiers téléchargés
- secure - Si la connexion est sécurisée
- accept - Les paramètres HTTP accept
- proxy_ip - L'adresse IP proxy du client. Parcourt l'array
$_SERVER
pourHTTP_CLIENT_IP
,HTTP_X_FORWARDED_FOR
,HTTP_X_FORWARDED
,HTTP_X_CLUSTER_CLIENT_IP
,HTTP_FORWARDED_FOR
,HTTP_FORWARDED
dans cet ordre. - host - Le nom d'hôte de la requête
- servername - Le SERVER_NAME depuis
$_SERVER
Méthodes d'aide
Il existe quelques méthodes d'aide pour assembler des parties d'une URL, ou gérer certains en-têtes.
URL complète
Vous pouvez accéder à l'URL complète de la requête en utilisant la méthode getFullUrl()
:
$url = Flight::request()->getFullUrl();
// https://example.com/some/path?foo=bar
URL de base
Vous pouvez accéder à l'URL de base en utilisant la méthode getBaseUrl()
:
// http://example.com/path/to/something/cool?query=yes+thanks
$url = Flight::request()->getBaseUrl();
// https://example.com
// Notez, pas de barre oblique finale.
Analyse de requête
Vous pouvez passer une URL à la méthode parseQuery()
pour analyser la chaîne de requête en un array associatif :
$query = Flight::request()->parseQuery('https://example.com/some/path?foo=bar');
// ['foo' => 'bar']
Négociation des types de contenu acceptés
v3.17.2
Vous pouvez utiliser la méthode negotiateContentType()
pour déterminer le meilleur type de contenu à répondre en fonction de l'en-tête Accept
envoyé par le client.
// Exemple d'en-tête Accept : text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
// Le code ci-dessous définit ce que vous supportez.
$availableTypes = ['application/json', 'application/xml'];
$typeToServe = Flight::request()->negotiateContentType($availableTypes);
if ($typeToServe === 'application/json') {
// Servir une réponse JSON
} elseif ($typeToServe === 'application/xml') {
// Servir une réponse XML
} else {
// Par défaut, utiliser quelque chose d'autre ou lever une erreur
}
Note : Si aucun des types disponibles n'est trouvé dans l'en-tête
Accept
, la méthode renverranull
. Si aucun en-têteAccept
n'est défini, la méthode renverra le premier type dans le array$availableTypes
.
Voir aussi
- Routing - Voir comment mapper les routes aux contrôleurs et rendre les vues.
- Responses - Comment personnaliser les réponses HTTP.
- Why a Framework? - Comment les requêtes s'intègrent dans le tableau global.
- Collections - Travailler avec des collections de données.
- Uploaded File Handler - Gérer les téléchargements de fichiers.
Dépannage
request()->ip
etrequest()->proxy_ip
peuvent être différents si votre serveur web est derrière un proxy, un équilibreur de charge, etc.
Journal des modifications
- v3.17.2 - Ajout de negotiateContentType()
- v3.12.0 - Ajout de la capacité à gérer les téléchargements de fichiers via l'objet requête.
- v1.0 - Version initiale.
Learn/why_frameworks
Pourquoi un Framework?
Certains programmeurs sont farouchement opposés à l'utilisation de frameworks. Ils soutiennent que les frameworks sont gonflés, lents et difficiles à apprendre. Ils disent que les frameworks sont inutiles et que vous pouvez écrire un meilleur code sans eux. Il y a certainement quelques points valides à faire valoir sur les inconvénients de l'utilisation des frameworks. Cependant, il y a aussi de nombreux avantages à utiliser des frameworks.
Raisons d'utiliser un Framework
Voici quelques raisons pour lesquelles vous pourriez envisager d'utiliser un framework:
- Développement Rapide: Les frameworks offrent beaucoup de fonctionnalités prêtes à l'emploi. Cela signifie que vous pouvez développer des applications web plus rapidement. Vous n'avez pas à écrire autant de code car le framework fournit une grande partie des fonctionnalités dont vous avez besoin.
- Cohérence: Les frameworks offrent une manière cohérente de faire les choses. Cela facilite votre compréhension du fonctionnement du code et facilite la compréhension de votre code par d'autres développeurs. Si vous le faites script par script, vous pourriez perdre de la cohérence entre les scripts, surtout si vous travaillez avec une équipe de développeurs.
- Sécurité: Les frameworks proposent des fonctionnalités de sécurité qui aident à protéger vos applications web contre les menaces courantes en matière de sécurité. Cela signifie que vous n'avez pas à vous soucier autant de la sécurité car le framework s'occupe en grande partie de cela pour vous.
- Communauté: Les frameworks ont de grandes communautés de développeurs qui contribuent au framework. Cela signifie que vous pouvez obtenir de l'aide d'autres développeurs lorsque vous avez des questions ou des problèmes. Cela signifie également qu'il y a beaucoup de ressources disponibles pour vous aider à apprendre comment utiliser le framework.
- Meilleures Pratiques: Les frameworks sont construits en utilisant les meilleures pratiques. Cela signifie que vous pouvez apprendre du framework et utiliser les mêmes meilleures pratiques dans votre propre code. Cela peut vous aider à devenir un meilleur programmeur. Parfois, vous ne savez pas ce que vous ne savez pas et cela peut vous jouer des tours à la fin.
- Extensibilité: Les frameworks sont conçus pour être étendus. Cela signifie que vous pouvez ajouter votre propre fonctionnalité au framework. Cela vous permet de construire des applications web adaptées à vos besoins spécifiques.
Flight est un micro-framework. Cela signifie qu'il est petit et léger. Il ne fournit pas autant de fonctionnalités que des frameworks plus grands comme Laravel ou Symfony. Cependant, il fournit une grande partie des fonctionnalités dont vous avez besoin pour construire des applications web. Il est également facile à apprendre et à utiliser. Cela en fait un bon choix pour construire rapidement et facilement des applications web. Si vous êtes novice en frameworks, Flight est un excellent framework pour débutants. Cela vous aidera à découvrir les avantages de l'utilisation des frameworks sans vous submerger avec trop de complexité. Après avoir acquis de l'expérience avec Flight, il sera plus facile de passer à des frameworks plus complexes comme Laravel ou Symfony, cependant Flight peut toujours vous aider à créer une application robuste et réussie.
Qu'est-ce que le Routage?
Le routage est au cœur du framework Flight, mais qu'est-ce que c'est exactement? Le routage est le processus de prendre une URL et de la faire correspondre à une fonction spécifique dans votre code.
C'est ainsi que vous pouvez faire en sorte que votre site Web fasse différentes choses en fonction de l'URL demandée. Par exemple, vous pouvez vouloir afficher le profil d'un utilisateur lorsqu'il
visite /utilisateur/1234
, mais affichez une liste de tous les utilisateurs lorsqu'ils visitent /utilisateurs
. Tout cela se fait grâce au routage.
Cela pourrait fonctionner un peu comme ceci:
- Un utilisateur se rend sur votre navigateur et tape
http://exemple.com/utilisateur/1234
. - Le serveur reçoit la demande, examine l'URL et la transmet à votre code d'application Flight.
- Disons que dans votre code Flight vous avez quelque chose comme
Flight::route('/utilisateur/@id', [ 'ControlleurUtilisateur', 'voirProfilUtilisateur' ]);
. Votre code d'application Flight examine l'URL et constate qu'elle correspond à une route que vous avez définie, puis exécute le code que vous avez défini pour cette route. - Le routeur de Flight exécutera ensuite et appellera la méthode
voirProfilUtilisateur($id)
dans la classeControlleurUtilisateur
, en passant le1234
en tant qu'argument$id
dans la méthode. - Le code dans votre méthode
voirProfilUtilisateur()
s'exécutera alors et fera ce que vous lui avez dit de faire. Vous pourriez finir par afficher un peu de HTML pour la page de profil de l'utilisateur, ou s'il s'agit d'une API RESTful, vous pourriez afficher une réponse JSON avec les informations de l'utilisateur. - Flight emballe tout cela joliment, génère les en-têtes de réponse et les envoie de retour au navigateur de l'utilisateur.
- L'utilisateur est comblé de joie et se fait un câlin chaleureux!
Et Pourquoi est-ce Important?
Avoir un routeur centralisé approprié peut réellement rendre votre vie beaucoup plus facile! Il peut être difficile de le voir au début. Voici quelques raisons:
- Routage Centralisé: Vous pouvez regrouper tous vos itinéraires en un seul endroit. Cela facilite la visualisation de tous les itinéraires que vous avez et de ce qu'ils font. Cela facilite également leur modification si nécessaire.
- Paramètres de Route: Vous pouvez utiliser des paramètres de route pour transmettre des données à vos méthodes de route. C'est un excellent moyen de garder votre code propre et organisé.
- Groupes de Routes: Vous pouvez regrouper des itinéraires ensemble. C'est idéal pour maintenir votre code organisé et pour appliquer middleware à un groupe d'itinéraires.
- Alias de Route: Vous pouvez attribuer un alias à un itinéraire, de sorte que l'URL puisse être générée dynamiquement plus tard dans votre code (comme un modèle par exemple). Ex : au lieu de coder en dur
/utilisateur/1234
dans votre code, vous pourriez plutôt référencer l'aliasvue_utilisateur
et transmettre l'id
en tant que paramètre. C'est merveilleux au cas où vous décideriez de le changer en/admin/utilisateur/1234
plus tard. Vous n'aurez pas à changer toutes vos URLs codées en dur, juste l'URL attachée à la route. - Middlewares de Route: Vous pouvez ajouter des middlewares à vos itinéraires. Les middlewares sont incroyablement efficaces pour ajouter des comportements spécifiques à votre application, comme l'authentification qu'un certain utilisateur peut accéder à un itinéraire ou à un groupe d'itinéraires.
Je suis sûr que vous êtes familier avec la manière scriptée de créer un site web. Vous pourriez avoir un fichier appelé index.php
qui comporte une multitude de déclarations if
pour vérifier l'URL et puis exécuter une fonction spécifique en fonction de l'URL. C'est une forme de routage, mais ce n'est pas très organisé et cela peut rapidement devenir incontrôlable. Le système de routage de Flight est une méthode beaucoup plus organisée et puissante pour gérer le routage.
Comme ceci ?
// /utilisateur/voir_profil.php?id=1234
if ($_GET['id']) {
$id = $_GET['id'];
voirProfilUtilisateur($id);
}
// /utilisateur/editer_profil.php?id=1234
if ($_GET['id']) {
$id = $_GET['id'];
editerProfilUtilisateur($id);
}
// etc...
Ou comme cela ?
// index.php
Flight::route('/utilisateur/@id', [ 'ControlleurUtilisateur', 'voirProfilUtilisateur' ]);
Flight::route('/utilisateur/@id/editer', [ 'ControlleurUtilisateur', 'editerProfilUtilisateur' ]);
// Peut-être dans votre app/controllers/ControlleurUtilisateur.php
class ControlleurUtilisateur {
public function voirProfilUtilisateur($id) {
// faire quelque chose
}
public function editerProfilUtilisateur($id) {
// faire quelque chose
}
}
Espérons que vous commencez à voir les avantages d'utiliser un système de routage centralisé. C'est beaucoup plus facile à gérer et à comprendre à long terme!
Demandes et Réponses
Flight fournit un moyen simple et facile de gérer les demandes et les réponses. C'est le cœur de ce qu'un framework web fait. Il prend une demande de navigateur d'utilisateur, la traite, puis envoie une réponse. C'est ainsi que vous pouvez construire des applications web qui font des choses comme afficher un profil d'utilisateur, permettre à un utilisateur de se connecter, ou permettre à un utilisateur de poster un nouveau billet de blog.
Demandes
Une demande est ce que le navigateur de l'utilisateur envoie à votre serveur lorsqu'ils visitent votre site. Cette demande contient des informations sur ce que l'utilisateur veut faire. Par exemple, elle peut contenir des informations sur l'URL que l'utilisateur souhaite visiter, les données que l'utilisateur souhaite envoyer à votre serveur, ou le type de données que l'utilisateur souhaite recevoir de votre serveur. Il est important de savoir qu'une demande est en lecture seule. Vous ne pouvez pas changer la demande, mais vous pouvez la lire.
Flight fournit un moyen simple d'accéder aux informations sur la demande. Vous pouvez accéder aux informations sur la demande en utilisant la méthode Flight::request()
Cette méthode renvoie un objet Request
qui contient des informations sur la demande. Vous pouvez utiliser cet objet pour accéder aux informations sur la demande,
telles que l'URL, la méthode, ou les données que l'utilisateur a envoyées à votre serveur.
Réponses
Une réponse est ce que votre serveur envoie à nouveau au navigateur de l'utilisateur lorsqu'ils visitent votre site. Cette réponse contient des informations sur ce que votre serveur veut faire. Par exemple, elle peut contenir des informations sur le type de données que votre serveur souhaite envoyer à l'utilisateur, le type de données que votre serveur souhaite recevoir de l'utilisateur, ou le type de données que votre serveur souhaite stocker sur l'ordinateur de l'utilisateur.
Flight fournit un moyen simple d'envoyer une réponse au navigateur de l'utilisateur. Vous pouvez envoyer une réponse en utilisant la méthode Flight::response()
Cette méthode prend un objet Response
en argument et envoie la réponse au navigateur de l'utilisateur. Vous pouvez utiliser cet objet pour envoyer une réponse au
navigateur de l'utilisateur, telle que HTML, JSON ou un fichier. Flight vous aide à générer automatiquement certaines parties de la réponse pour faciliter les choses,
mais finalement vous avez le contrôle sur ce que vous renvoyez à l'utilisateur.
Learn/responses
Réponses
Aperçu
Flight aide à générer une partie des en-têtes de réponse pour vous, mais vous conservez la plupart du contrôle sur ce que vous renvoyez à l'utilisateur. La plupart du temps, vous accédez directement à l'objet response()
, mais Flight propose des méthodes d'assistance pour définir certains en-têtes de réponse pour vous.
Comprendre
Après que l'utilisateur a envoyé sa requête à votre application, vous devez générer une réponse appropriée pour lui. Ils vous ont envoyé des informations comme la langue qu'ils préfèrent, s'ils peuvent gérer certains types de compression, leur agent utilisateur, etc., et après avoir traité tout cela, il est temps de leur renvoyer une réponse appropriée. Cela peut consister à définir des en-têtes, à sortir un corps de HTML ou de JSON pour eux, ou à les rediriger vers une page.
Utilisation de base
Envoyer un corps de réponse
Flight utilise ob_start()
pour tamponner la sortie. Cela signifie que vous pouvez utiliser echo
ou print
pour envoyer une réponse à l'utilisateur et que Flight la capturera et la renverra à l'utilisateur avec les en-têtes appropriés.
// Cela enverra "Hello, World!" au navigateur de l'utilisateur
Flight::route('/', function() {
echo "Hello, World!";
});
// HTTP/1.1 200 OK
// Content-Type: text/html
//
// Hello, World!
En alternative, vous pouvez appeler la méthode write()
pour ajouter au corps également.
// Cela enverra "Hello, World!" au navigateur de l'utilisateur
Flight::route('/', function() {
// verbeux, mais cela fait le travail parfois quand vous en avez besoin
Flight::response()->write("Hello, World!");
// si vous voulez récupérer le corps que vous avez défini à ce stade
// vous pouvez le faire comme ceci
$body = Flight::response()->getBody();
});
JSON
Flight fournit un support pour envoyer des réponses JSON et JSONP. Pour envoyer une réponse JSON, vous passez des données à encoder en JSON :
Flight::route('/@companyId/users', function(int $companyId) {
// d'une manière ou d'une autre, extrayez vos utilisateurs d'une base de données par exemple
$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"}, /* plus d'utilisateurs */ ]
Note : Par défaut, Flight enverra un en-tête
Content-Type: application/json
avec la réponse. Il utilisera également les drapeauxJSON_THROW_ON_ERROR
etJSON_UNESCAPED_SLASHES
lors de l'encodage du JSON.
JSON avec code de statut
Vous pouvez également passer un code de statut en tant que deuxième argument :
Flight::json(['id' => 123], 201);
JSON avec impression formatée
Vous pouvez également passer un argument à la dernière position pour activer l'impression formatée :
Flight::json(['id' => 123], 200, true, 'utf-8', JSON_PRETTY_PRINT);
Changer l'ordre des arguments JSON
Flight::json()
est une méthode très ancienne, mais l'objectif de Flight est de maintenir la compatibilité arrière
pour les projets. C'est en fait très simple si vous voulez refaire l'ordre des arguments pour utiliser une syntaxe plus simple,
vous pouvez simplement remapper la méthode JSON comme n'importe quelle autre méthode Flight :
Flight::map('json', function($data, $code = 200, $options = 0) {
// maintenant vous n'avez pas besoin de `true, 'utf-8'` quand vous utilisez la méthode json() !
Flight::_json($data, $code, true, 'utf-8', $options);
}
// Et maintenant cela peut être utilisé comme ceci
Flight::json(['id' => 123], 200, JSON_PRETTY_PRINT);
JSON et arrêt de l'exécution
v3.10.0
Si vous voulez envoyer une réponse JSON et arrêter l'exécution, vous pouvez utiliser la méthode jsonHalt()
.
C'est utile pour les cas où vous vérifiez peut-être un type d'autorisation et si
l'utilisateur n'est pas autorisé, vous pouvez envoyer une réponse JSON immédiatement, effacer le contenu du corps existant
et arrêter l'exécution.
Flight::route('/users', function() {
$authorized = someAuthorizationCheck();
// Vérifier si l'utilisateur est autorisé
if($authorized === false) {
Flight::jsonHalt(['error' => 'Unauthorized'], 401);
// pas de sortie ; nécessaire ici.
}
// Continuer avec le reste de la route
});
Avant v3.10.0, vous deviez faire quelque chose comme ceci :
Flight::route('/users', function() {
$authorized = someAuthorizationCheck();
// Vérifier si l'utilisateur est autorisé
if($authorized === false) {
Flight::halt(401, json_encode(['error' => 'Unauthorized']));
}
// Continuer avec le reste de la route
});
Effacer un corps de réponse
Si vous voulez effacer le corps de réponse, vous pouvez utiliser la méthode clearBody
:
Flight::route('/', function() {
if($someCondition) {
Flight::response()->write("Hello, World!");
} else {
Flight::response()->clearBody();
}
});
Le cas d'utilisation ci-dessus n'est probablement pas courant, cependant il pourrait être plus courant si cela était utilisé dans un middleware.
Exécuter un rappel sur le corps de réponse
Vous pouvez exécuter un rappel sur le corps de réponse en utilisant la méthode addResponseBodyCallback
:
Flight::route('/users', function() {
$db = Flight::db();
$users = $db->fetchAll("SELECT * FROM users");
Flight::render('users_table', ['users' => $users]);
});
// Cela gzippera toutes les réponses pour n'importe quelle route
Flight::response()->addResponseBodyCallback(function($body) {
return gzencode($body, 9);
});
Vous pouvez ajouter plusieurs rappels et ils seront exécutés dans l'ordre où ils ont été ajoutés. Comme cela peut accepter n'importe quel appelable, il peut accepter un tableau de classe [ $class, 'method' ]
, une closure $strReplace = function($body) { str_replace('hi', 'there', $body); };
, ou un nom de fonction 'minify'
si vous aviez une fonction pour minifier votre code HTML par exemple.
Note : Les rappels de route ne fonctionneront pas si vous utilisez l'option de configuration flight.v2.output_buffering
.
Rappel de route spécifique
Si vous vouliez que cela ne s'applique qu'à une route spécifique, vous pourriez ajouter le rappel dans la route elle-même :
Flight::route('/users', function() {
$db = Flight::db();
$users = $db->fetchAll("SELECT * FROM users");
Flight::render('users_table', ['users' => $users]);
// Cela gzippera seulement la réponse pour cette route
Flight::response()->addResponseBodyCallback(function($body) {
return gzencode($body, 9);
});
});
Option Middleware
Vous pouvez également utiliser le middleware pour appliquer le rappel à toutes les routes via le middleware :
// MinifyMiddleware.php
class MinifyMiddleware {
public function before() {
// Appliquer le rappel ici sur l'objet response().
Flight::response()->addResponseBodyCallback(function($body) {
return $this->minify($body);
});
}
protected function minify(string $body): string {
// minifier le corps d'une manière ou d'une autre
return $body;
}
}
// index.php
Flight::group('/users', function() {
Flight::route('', function() { /* ... */ });
Flight::route('/@id', function($id) { /* ... */ });
}, [ new MinifyMiddleware() ]);
Codes de statut
Vous pouvez définir le code de statut de la réponse en utilisant la méthode status
:
Flight::route('/@id', function($id) {
if($id == 123) {
Flight::response()->status(200);
echo "Hello, World!";
} else {
Flight::response()->status(403);
echo "Forbidden";
}
});
Si vous voulez obtenir le code de statut actuel, vous pouvez utiliser la méthode status
sans aucun argument :
Flight::response()->status(); // 200
Définir un en-tête de réponse
Vous pouvez définir un en-tête tel que le type de contenu de la réponse en utilisant la méthode header
:
// Cela enverra "Hello, World!" au navigateur de l'utilisateur en texte brut
Flight::route('/', function() {
Flight::response()->header('Content-Type', 'text/plain');
// ou
Flight::response()->setHeader('Content-Type', 'text/plain');
echo "Hello, World!";
});
Redirection
Vous pouvez rediriger la requête actuelle en utilisant la méthode redirect()
et en passant
un nouvel URL :
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; // ceci est nécessaire pour que la fonctionnalité ci-dessous ne s'exécute pas
}
// ajouter le nouvel utilisateur...
Flight::db()->runQuery("INSERT INTO users ....");
Flight::redirect('/admin/dashboard');
});
Note : Par défaut, Flight envoie un code de statut HTTP 303 ("See Other"). Vous pouvez optionnellement définir un code personnalisé :
Flight::redirect('/new/location', 301); // permanent
Arrêter l'exécution de la route
Vous pouvez arrêter le framework et sortir immédiatement à n'importe quel point en appelant la méthode halt
:
Flight::halt();
Vous pouvez également spécifier un code de statut HTTP
et un message optionnels :
Flight::halt(200, 'Be right back...');
Appeler halt
rejettera tout contenu de réponse jusqu'à ce point et arrêtera toute exécution.
Si vous voulez arrêter le framework et sortir la réponse actuelle, utilisez la méthode stop
:
Flight::stop($httpStatusCode = null);
Note :
Flight::stop()
a un comportement étrange tel qu'il sortira la réponse mais continuera à exécuter votre script ce qui pourrait ne pas être ce que vous voulez. Vous pouvez utiliserexit
oureturn
après avoir appeléFlight::stop()
pour empêcher une exécution supplémentaire, mais il est généralement recommandé d'utiliserFlight::halt()
.
Cela sauvegardera la clé et la valeur de l'en-tête dans l'objet de réponse. À la fin du cycle de vie de la requête, il construira les en-têtes et enverra une réponse.
Utilisation avancée
Envoyer un en-tête immédiatement
Il peut y avoir des moments où vous devez faire quelque chose de personnalisé avec l'en-tête et vous devez envoyer l'en-tête
sur cette ligne même de code avec laquelle vous travaillez. Si vous définissez une route streamée,
c'est ce dont vous auriez besoin. Cela est réalisable via response()->setRealHeader()
.
Flight::route('/', function() {
Flight::response()->setRealHeader('Content-Type: text/plain');
echo 'Streaming response...';
sleep(5);
echo 'Done!';
})->stream();
JSONP
Pour les requêtes JSONP, vous pouvez optionnellement passer le nom du paramètre de requête que vous utilisez pour définir votre fonction de rappel :
Flight::jsonp(['id' => 123], 'q');
Donc, lors de l'envoi d'une requête GET en utilisant ?q=my_func
, vous devriez recevoir la sortie :
my_func({"id":123});
Si vous ne passez pas de nom de paramètre de requête, il passera par défaut à jsonp
.
Note : Si vous utilisez encore des requêtes JSONP en 2025 et au-delà, rejoignez le chat et dites-nous pourquoi ! Nous adorons entendre de bonnes histoires de batailles/horreurs !
Effacer les données de réponse
Vous pouvez effacer le corps de réponse et les en-têtes en utilisant la méthode clear()
. Cela effacera
tout en-tête assigné à la réponse, effacera le corps de réponse, et définira le code de statut à 200
.
Flight::response()->clear();
Effacer seulement le corps de réponse
Si vous voulez seulement effacer le corps de réponse, vous pouvez utiliser la méthode clearBody()
:
// Cela gardera toujours les en-têtes définis sur l'objet response().
Flight::response()->clearBody();
Mise en cache HTTP
Flight fournit un support intégré pour la mise en cache au niveau HTTP. Si la condition de mise en cache
est remplie, Flight renverra une réponse HTTP 304 Not Modified
. La prochaine fois que le
client demandera la même ressource, il sera invité à utiliser sa version mise en cache localement.
Mise en cache au niveau de la route
Si vous voulez mettre en cache toute votre réponse, vous pouvez utiliser la méthode cache()
et passer le temps de mise en cache.
// Cela mettra en cache la réponse pendant 5 minutes
Flight::route('/news', function () {
Flight::response()->cache(time() + 300);
echo 'This content will be cached.';
});
// Alternativement, vous pouvez utiliser une chaîne que vous passeriez
// à la méthode strtotime()
Flight::route('/news', function () {
Flight::response()->cache('+5 minutes');
echo 'This content will be cached.';
});
Last-Modified
Vous pouvez utiliser la méthode lastModified
et passer un horodatage UNIX pour définir la date
et l'heure à laquelle une page a été modifiée pour la dernière fois. Le client continuera à utiliser son cache jusqu'à
ce que la valeur de dernière modification change.
Flight::route('/news', function () {
Flight::lastModified(1234567890);
echo 'This content will be cached.';
});
ETag
La mise en cache ETag
est similaire à Last-Modified
, sauf que vous pouvez spécifier n'importe quel identifiant que vous
voulez pour la ressource :
Flight::route('/news', function () {
Flight::etag('my-unique-id');
echo 'This content will be cached.';
});
Gardez à l'esprit que l'appel à lastModified
ou etag
définira et vérifiera tous les deux
la valeur de cache. Si la valeur de cache est la même entre les requêtes, Flight enverra immédiatement
une réponse HTTP 304
et arrêtera le traitement.
Télécharger un fichier
v3.12.0
Il y a une méthode d'assistance pour streamer un fichier vers l'utilisateur final. Vous pouvez utiliser la méthode download
et passer le chemin.
Flight::route('/download', function () {
Flight::download('/path/to/file.txt');
// À partir de v3.17.1, vous pouvez spécifier un nom de fichier personnalisé pour le téléchargement
Flight::download('/path/to/file.txt', 'custom_name.txt');
});
Voir aussi
- Routing - Comment mapper les routes aux contrôleurs et rendre les vues.
- Requests - Comprendre comment gérer les requêtes entrantes.
- Middleware - Utiliser le middleware avec les routes pour l'authentification, la journalisation, etc.
- Pourquoi un Framework ? - Comprendre les avantages d'utiliser un framework comme Flight.
- Extending - Comment étendre Flight avec votre propre fonctionnalité.
Dépannage
- Si vous avez des problèmes avec les redirections qui ne fonctionnent pas, assurez-vous d'ajouter un
return;
à la méthode. stop()
ethalt()
ne sont pas la même chose.halt()
arrêtera l'exécution immédiatement, tandis questop()
permettra à l'exécution de continuer.
Journal des modifications
- v3.17.1 - Ajout de
$fileName
à la méthodedownloadFile()
. - v3.12.0 - Ajout de la méthode d'assistance downloadFile.
- v3.10.0 - Ajout de
jsonHalt
. - v1.0 - Version initiale.
Learn/events
Gestionnaire d'événements
à partir de la v3.15.0
Aperçu
Les événements vous permettent d'enregistrer et de déclencher des comportements personnalisés dans votre application. Avec l'ajout de Flight::onEvent()
et Flight::triggerEvent()
, vous pouvez maintenant vous accrocher à des moments clés du cycle de vie de votre application ou définir vos propres événements (comme les notifications et les e-mails) pour rendre votre code plus modulaire et extensible. Ces méthodes font partie des méthodes mappables de Flight, ce qui signifie que vous pouvez surcharger leur comportement pour répondre à vos besoins.
Comprendre
Les événements vous permettent de séparer les différentes parties de votre application afin qu'elles ne dépendent pas trop les unes des autres. Cette séparation — souvent appelée découplage — rend votre code plus facile à mettre à jour, à étendre ou à déboguer. Au lieu d'écrire tout en un gros bloc, vous pouvez diviser votre logique en pièces plus petites et indépendantes qui répondent à des actions spécifiques (événements).
Imaginez que vous construisez une application de blog :
- Quand un utilisateur publie un commentaire, vous pourriez vouloir :
- Sauvegarder le commentaire dans la base de données.
- Envoyer un e-mail au propriétaire du blog.
- Enregistrer l'action pour la sécurité.
Sans événements, vous entasseriez tout cela dans une seule fonction. Avec les événements, vous pouvez le diviser : une partie sauvegarde le commentaire, une autre déclenche un événement comme 'comment.posted'
, et des écouteurs séparés gèrent l'e-mail et l'enregistrement. Cela garde votre code plus propre et vous permet d'ajouter ou de supprimer des fonctionnalités (comme les notifications) sans toucher à la logique principale.
Cas d'utilisation courants
Dans la plupart des cas, les événements sont bons pour des choses qui sont optionnelles, mais pas une partie absolument essentielle de votre système. Par exemple, les éléments suivants sont bons à avoir, mais si elles échouent pour une raison quelconque, votre application devrait toujours fonctionner :
- Enregistrement : Enregistrer des actions comme les connexions ou les erreurs sans encombrer votre code principal.
- Notifications : Envoyer des e-mails ou des alertes quand quelque chose se produit.
- Mises à jour de cache : Actualiser les caches ou notifier d'autres systèmes des changements.
Cependant, supposons que vous ayez une fonctionnalité de mot de passe oublié. Cela devrait faire partie de votre fonctionnalité principale et ne pas être un événement car si cet e-mail ne part pas, votre utilisateur ne peut pas réinitialiser son mot de passe et utiliser votre application.
Utilisation de base
Le système d'événements de Flight est construit autour de deux méthodes principales : Flight::onEvent()
pour enregistrer les écouteurs d'événements et Flight::triggerEvent()
pour déclencher les événements. Voici comment vous pouvez les utiliser :
Enregistrement des écouteurs d'événements
Pour écouter un événement, utilisez Flight::onEvent()
. Cette méthode vous permet de définir ce qui doit se produire quand un événement se produit.
Flight::onEvent(string $event, callable $callback): void
$event
: Un nom pour votre événement (par exemple,'user.login'
).$callback
: La fonction à exécuter quand l'événement est déclenché.
Vous "souscrivez" à un événement en indiquant à Flight ce qu'il faut faire quand cela se produit. Le rappel peut accepter des arguments passés depuis le déclencheur d'événement.
Le système d'événements de Flight est synchrone, ce qui signifie que chaque écouteur d'événement est exécuté en séquence, l'un après l'autre. Quand vous déclenchez un événement, tous les écouteurs enregistrés pour cet événement s'exécuteront jusqu'à leur achèvement avant que votre code continue. Il est important de comprendre cela car cela diffère des systèmes d'événements asynchrones où les écouteurs pourraient s'exécuter en parallèle ou plus tard.
Exemple simple
Flight::onEvent('user.login', function ($username) {
echo "Welcome back, $username!";
// you can send an email if the login is from a new location
});
Ici, quand l'événement 'user.login'
est déclenché, il saluera l'utilisateur par son nom et pourrait aussi inclure une logique pour envoyer un e-mail si nécessaire.
Note : Le rappel peut être une fonction, une fonction anonyme, ou une méthode d'une classe.
Déclenchement des événements
Pour faire se produire un événement, utilisez Flight::triggerEvent()
. Cela indique à Flight d'exécuter tous les écouteurs enregistrés pour cet événement, en passant les données que vous fournissez.
Flight::triggerEvent(string $event, ...$args): void
$event
: Le nom de l'événement que vous déclenchez (doit correspondre à un événement enregistré)....$args
: Arguments optionnels à envoyer aux écouteurs (peut être n'importe quel nombre d'arguments).
Exemple simple
$username = 'alice';
Flight::triggerEvent('user.login', $username);
Cela déclenche l'événement 'user.login'
et envoie 'alice'
à l'écouteur que nous avons défini plus tôt, ce qui affichera : Welcome back, alice!
.
- Si aucun écouteur n'est enregistré, rien ne se passe — votre application ne se cassera pas.
- Utilisez l'opérateur de propagation (
...
) pour passer plusieurs arguments de manière flexible.
Arrêt des événements
Si un écouteur retourne false
, aucun écouteur supplémentaire pour cet événement ne sera exécuté. Cela vous permet d'arrêter la chaîne d'événements basée sur des conditions spécifiques. Souvenez-vous, l'ordre des écouteurs compte, car le premier à retourner false
arrêtera les autres.
Exemple :
Flight::onEvent('user.login', function ($username) {
if (isBanned($username)) {
logoutUser($username);
return false; // Stops subsequent listeners
}
});
Flight::onEvent('user.login', function ($username) {
sendWelcomeEmail($username); // this is never sent
});
Surcharge des méthodes d'événements
Flight::onEvent()
et Flight::triggerEvent()
sont disponibles pour être étendus, ce qui signifie que vous pouvez redéfinir leur fonctionnement. C'est idéal pour les utilisateurs avancés qui veulent personnaliser le système d'événements, comme ajouter de l'enregistrement ou changer la façon dont les événements sont dispatchés.
Exemple : Personnalisation de onEvent
Flight::map('onEvent', function (string $event, callable $callback) {
// Log every event registration
error_log("New event listener added for: $event");
// Call the default behavior (assuming an internal event system)
Flight::_onEvent($event, $callback);
});
Maintenant, chaque fois que vous enregistrez un événement, il l'enregistre avant de procéder.
Pourquoi surcharger ?
- Ajouter du débogage ou de la surveillance.
- Restreindre les événements dans certains environnements (par exemple, désactiver en test).
- Intégrer avec une bibliothèque d'événements différente.
Où placer vos événements
Si vous êtes nouveau aux concepts d'événements dans votre projet, vous pourriez vous demander : où est-ce que j'enregistre tous ces événements dans mon application ? La simplicité de Flight signifie qu'il n'y a pas de règle stricte — vous pouvez les placer où cela a du sens pour votre projet. Cependant, les garder organisés vous aide à maintenir votre code au fur et à mesure que votre application grandit. Voici quelques options pratiques et meilleures pratiques, adaptées à la nature légère de Flight :
Option 1 : Dans votre index.php
principal
Pour les petites applications ou les prototypes rapides, vous pouvez enregistrer les événements directement dans votre fichier index.php
aux côtés de vos routes. Cela garde tout au même endroit, ce qui est bien quand la simplicité est votre priorité.
require 'vendor/autoload.php';
// Register events
Flight::onEvent('user.login', function ($username) {
error_log("$username logged in at " . date('Y-m-d H:i:s'));
});
// Define routes
Flight::route('/login', function () {
$username = 'bob';
Flight::triggerEvent('user.login', $username);
echo "Logged in!";
});
Flight::start();
- Avantages : Simple, pas de fichiers supplémentaires, idéal pour les petits projets.
- Inconvénients : Peut devenir désordonné au fur et à mesure que votre application grandit avec plus d'événements et de routes.
Option 2 : Un fichier events.php
séparé
Pour une application légèrement plus grande, envisagez de déplacer les enregistrements d'événements dans un fichier dédié comme app/config/events.php
. Incluez ce fichier dans votre index.php
avant vos routes. Cela imite la façon dont les routes sont souvent organisées dans app/config/routes.php
dans les projets Flight.
// 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();
- Avantages : Garde
index.php
focalisé sur le routage, organise les événements logiquement, facile à trouver et à modifier. - Inconvénients : Ajoute un peu de structure, ce qui pourrait sembler excessif pour des applications très petites.
Option 3 : Près de l'endroit où ils sont déclenchés
Une autre approche est d'enregistrer les événements près de l'endroit où ils sont déclenchés, comme à l'intérieur d'un contrôleur ou d'une définition de route. Cela fonctionne bien si un événement est spécifique à une partie de votre application.
Flight::route('/signup', function () {
// Register event here
Flight::onEvent('user.registered', function ($email) {
echo "Welcome email sent to $email!";
});
$email = 'jane@example.com';
Flight::triggerEvent('user.registered', $email);
echo "Signed up!";
});
- Avantages : Garde le code lié ensemble, bon pour des fonctionnalités isolées.
- Inconvénients : Disperse les enregistrements d'événements, rendant plus difficile de voir tous les événements d'un coup ; risque d'enregistrements dupliqués si pas prudent.
Meilleure pratique pour Flight
- Commencez simple : Pour les petites applications, placez les événements dans
index.php
. C'est rapide et s'aligne avec le minimalisme de Flight. - Grandissez intelligemment : Au fur et à mesure que votre application s'étend (par exemple, plus de 5-10 événements), utilisez un fichier
app/config/events.php
. C'est un pas naturel, comme organiser les routes, et garde votre code ordonné sans ajouter de frameworks complexes. - Évitez la sur-ingénierie : Ne créez pas une classe ou un répertoire "gestionnaire d'événements" complet sauf si votre application devient énorme — Flight prospère sur la simplicité, alors gardez-le léger.
Astuce : Groupez par objectif
Dans events.php
, groupez les événements liés (par exemple, tous les événements liés aux utilisateurs ensemble) avec des commentaires pour plus de clarté :
// 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");
});
Cette structure s'adapte bien et reste conviviale pour les débutants.
Exemples du monde réel
Parcourons quelques scénarios du monde réel pour montrer comment fonctionnent les événements et pourquoi ils sont utiles.
Exemple 1 : Enregistrement d'une connexion utilisateur
// Step 1: Register a listener
Flight::onEvent('user.login', function ($username) {
$time = date('Y-m-d H:i:s');
error_log("$username logged in at $time");
});
// Step 2: Trigger it in your app
Flight::route('/login', function () {
$username = 'bob'; // Pretend this comes from a form
Flight::triggerEvent('user.login', $username);
echo "Hi, $username!";
});
Pourquoi c'est utile : Le code de connexion n'a pas besoin de savoir pour l'enregistrement — il déclenche juste l'événement. Vous pouvez plus tard ajouter plus d'écouteurs (par exemple, envoyer un e-mail de bienvenue) sans changer la route.
Exemple 2 : Notification de nouveaux utilisateurs
// Listener for new registrations
Flight::onEvent('user.registered', function ($email, $name) {
// Simulate sending an email
echo "Email sent to $email: Welcome, $name!";
});
// Trigger it when someone signs up
Flight::route('/signup', function () {
$email = 'jane@example.com';
$name = 'Jane';
Flight::triggerEvent('user.registered', $email, $name);
echo "Thanks for signing up!";
});
Pourquoi c'est utile : La logique d'inscription se concentre sur la création de l'utilisateur, tandis que l'événement gère les notifications. Vous pourriez ajouter plus d'écouteurs (par exemple, enregistrer l'inscription) plus tard.
Exemple 3 : Vider un cache
// Listener to clear a cache
Flight::onEvent('page.updated', function ($pageId) {
// if using the flightphp/cache plugin
Flight::cache()->delete("page_$pageId");
echo "Cache cleared for page $pageId.";
});
// Trigger when a page is edited
Flight::route('/edit-page/(@id)', function ($pageId) {
// Pretend we updated the page
Flight::triggerEvent('page.updated', $pageId);
echo "Page $pageId updated.";
});
Pourquoi c'est utile : Le code d'édition ne se soucie pas du cache — il signale juste la mise à jour. D'autres parties de l'application peuvent réagir comme nécessaire.
Meilleures pratiques
- Nommez les événements clairement : Utilisez des noms spécifiques comme
'user.login'
ou'page.updated'
pour qu'il soit évident ce qu'ils font. - Gardez les écouteurs simples : Ne mettez pas de tâches lentes ou complexes dans les écouteurs — gardez votre application rapide.
- Testez vos événements : Déclenchez-les manuellement pour vous assurer que les écouteurs fonctionnent comme prévu.
- Utilisez les événements sagement : Ils sont excellents pour le découplage, mais trop d'événements peuvent rendre votre code difficile à suivre — utilisez-les quand cela a du sens.
Le système d'événements dans Flight PHP, avec Flight::onEvent()
et Flight::triggerEvent()
, vous donne une façon simple mais puissante de construire des applications flexibles. En permettant à différentes parties de votre application de communiquer via des événements, vous pouvez garder votre code organisé, réutilisable et facile à étendre. Que vous enregistriez des actions, envoyiez des notifications ou gériez des mises à jour, les événements vous aident à le faire sans emmêler votre logique. De plus, avec la possibilité de surcharger ces méthodes, vous avez la liberté d'adapter le système à vos besoins. Commencez petit avec un seul événement, et regardez comment cela transforme la structure de votre application !
Événements intégrés
Flight PHP vient avec quelques événements intégrés que vous pouvez utiliser pour vous accrocher au cycle de vie du framework. Ces événements sont déclenchés à des points spécifiques du cycle requête/réponse, vous permettant d'exécuter une logique personnalisée quand certaines actions se produisent.
Liste des événements intégrés
- flight.request.received :
function(Request $request)
Déclenché quand une requête est reçue, analysée et traitée. - flight.error :
function(Throwable $exception)
Déclenché quand une erreur se produit pendant le cycle de vie de la requête. - flight.redirect :
function(string $url, int $status_code)
Déclenché quand une redirection est initiée. - flight.cache.checked :
function(string $cache_key, bool $hit, float $executionTime)
Déclenché quand le cache est vérifié pour une clé spécifique et si c'est un hit ou un miss de cache. - flight.middleware.before :
function(Route $route)
Déclenché après l'exécution du middleware before. - flight.middleware.after :
function(Route $route)
Déclenché après l'exécution du middleware after. - flight.middleware.executed :
function(Route $route, $middleware, string $method, float $executionTime)
Déclenché après l'exécution de n'importe quel middleware - flight.route.matched :
function(Route $route)
Déclenché quand une route est matched, mais pas encore exécutée. - flight.route.executed :
function(Route $route, float $executionTime)
Déclenché après l'exécution et le traitement d'une route.$executionTime
est le temps qu'il a fallu pour exécuter la route (appeler le contrôleur, etc.). - flight.view.rendered :
function(string $template_file_path, float $executionTime)
Déclenché après le rendu d'une vue.$executionTime
est le temps qu'il a fallu pour rendre le template. Note : Si vous surchargez la méthoderender
, vous devrez re-déclencher cet événement. - flight.response.sent :
function(Response $response, float $executionTime)
Déclenché après l'envoi d'une réponse au client.$executionTime
est le temps qu'il a fallu pour construire la réponse.
Voir aussi
- Extending Flight - Comment étendre et personnaliser les fonctionnalités principales de Flight.
- Cache - Exemple d'utilisation d'événements pour vider le cache quand une page est mise à jour.
Dépannage
- Si vous ne voyez pas vos écouteurs d'événements être appelés, assurez-vous de les enregistrer avant de déclencher les événements. L'ordre d'enregistrement compte.
Journal des changements
- v3.15.0 - Ajout des événements à Flight.
Learn/templates
Vues et modèles HTML
Aperçu
Flight fournit par défaut une fonctionnalité de base de templating HTML. Le templating est une façon très efficace de déconnecter la logique de votre application de votre couche de présentation.
Compréhension
Lorsque vous construisez une application, vous aurez probablement du HTML que vous souhaiterez renvoyer à l'utilisateur final. PHP en soi est un langage de templating, mais il est très facile d'intégrer de la logique métier comme des appels à la base de données, des appels API, etc., dans votre fichier HTML, rendant le test et le découplage très difficiles. En poussant les données dans un modèle et en laissant le modèle se rendre lui-même, il devient beaucoup plus facile de découpler et de tester votre code en unités. Vous nous remercierez si vous utilisez des modèles !
Utilisation de base
Flight vous permet de remplacer le moteur de vue par défaut simplement en enregistrant votre propre classe de vue. Faites défiler vers le bas pour voir des exemples sur l'utilisation de Smarty, Latte, Blade, et plus encore !
Latte
recommandé
Voici comment utiliser le moteur de templates Latte pour vos vues.
Installation
composer require latte/latte
Configuration de base
L'idée principale est de surcharger la méthode render
pour utiliser Latte au lieu du renderer PHP par défaut.
// surcharge la méthode render pour utiliser latte au lieu du renderer PHP par défaut
Flight::map('render', function(string $template, array $data, ?string $block): void {
$latte = new Latte\Engine;
// Où latte stocke spécifiquement son cache
$latte->setTempDirectory(__DIR__ . '/../cache/');
$finalPath = Flight::get('flight.views.path') . $template;
$latte->render($finalPath, $data, $block);
});
Utilisation de Latte dans Flight
Maintenant que vous pouvez rendre avec Latte, vous pouvez faire quelque chose comme ceci :
<!-- 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
]);
});
Lorsque vous visitez /Bob
dans votre navigateur, la sortie serait :
<html>
<head>
<title>Home Page - My App</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<h1>Hello, Bob!</h1>
</body>
</html>
Lecture supplémentaire
Un exemple plus complexe d'utilisation de Latte avec des mises en page est montré dans la section awesome plugins de cette documentation.
Vous pouvez en apprendre plus sur les capacités complètes de Latte, y compris la traduction et les capacités linguistiques, en lisant la documentation officielle.
Moteur de vue intégré
déprécié
Note : Bien que cela reste la fonctionnalité par défaut et qu'elle fonctionne encore techniquement.
Pour afficher un modèle de vue, appelez la méthode render
avec le nom
du fichier de modèle et des données de modèle optionnelles :
Flight::render('hello.php', ['name' => 'Bob']);
Les données de modèle que vous passez sont automatiquement injectées dans le modèle et peuvent
être référencées comme une variable locale. Les fichiers de modèle sont simplement des fichiers PHP. Si le
contenu du fichier de modèle hello.php
est :
Hello, <?= $name ?>!
La sortie serait :
Hello, Bob!
Vous pouvez également définir manuellement des variables de vue en utilisant la méthode set :
Flight::view()->set('name', 'Bob');
La variable name
est maintenant disponible dans toutes vos vues. Vous pouvez donc simplement faire :
Flight::render('hello');
Notez que lorsque vous spécifiez le nom du modèle dans la méthode render, vous pouvez
omettre l'extension .php
.
Par défaut, Flight cherchera un répertoire views
pour les fichiers de modèle. Vous pouvez
définir un chemin alternatif pour vos modèles en configurant ce qui suit :
Flight::set('flight.views.path', '/path/to/views');
Mises en page
Il est courant pour les sites web d'avoir un seul fichier de modèle de mise en page avec un contenu
interchangeable. Pour rendre du contenu à utiliser dans une mise en page, vous pouvez passer un paramètre
optionnel à la méthode render
.
Flight::render('header', ['heading' => 'Hello'], 'headerContent');
Flight::render('body', ['body' => 'World'], 'bodyContent');
Votre vue aura alors des variables sauvegardées appelées headerContent
et bodyContent
.
Vous pouvez ensuite rendre votre mise en page en faisant :
Flight::render('layout', ['title' => 'Home Page']);
Si les fichiers de modèle ressemblent à ceci :
header.php
:
<h1><?= $heading ?></h1>
body.php
:
<div><?= $body ?></div>
layout.php
:
<html>
<head>
<title><?= $title ?></title>
</head>
<body>
<?= $headerContent ?>
<?= $bodyContent ?>
</body>
</html>
La sortie serait :
<html>
<head>
<title>Home Page</title>
</head>
<body>
<h1>Hello</h1>
<div>World</div>
</body>
</html>
Smarty
Voici comment utiliser le moteur de templates Smarty pour vos vues :
// Charge la bibliothèque Smarty
require './Smarty/libs/Smarty.class.php';
// Enregistre Smarty comme classe de vue
// Passe également une fonction de callback pour configurer Smarty au chargement
Flight::register('view', Smarty::class, [], function (Smarty $smarty) {
$smarty->setTemplateDir('./templates/');
$smarty->setCompileDir('./templates_c/');
$smarty->setConfigDir('./config/');
$smarty->setCacheDir('./cache/');
});
// Assigne les données de modèle
Flight::view()->assign('name', 'Bob');
// Affiche le modèle
Flight::view()->display('hello.tpl');
Pour plus de complétude, vous devriez également surcharger la méthode render par défaut de Flight :
Flight::map('render', function(string $template, array $data): void {
Flight::view()->assign($data);
Flight::view()->display($template);
});
Blade
Voici comment utiliser le moteur de templates Blade pour vos vues :
D'abord, vous devez installer la bibliothèque BladeOne via Composer :
composer require eftec/bladeone
Ensuite, vous pouvez configurer BladeOne comme classe de vue dans Flight :
<?php
// Charge la bibliothèque BladeOne
use eftec\bladeone\BladeOne;
// Enregistre BladeOne comme classe de vue
// Passe également une fonction de callback pour configurer BladeOne au chargement
Flight::register('view', BladeOne::class, [], function (BladeOne $blade) {
$views = __DIR__ . '/../views';
$cache = __DIR__ . '/../cache';
$blade->setPath($views);
$blade->setCompiledPath($cache);
});
// Assigne les données de modèle
Flight::view()->share('name', 'Bob');
// Affiche le modèle
echo Flight::view()->run('hello', []);
Pour plus de complétude, vous devriez également surcharger la méthode render par défaut de Flight :
<?php
Flight::map('render', function(string $template, array $data): void {
echo Flight::view()->run($template, $data);
});
Dans cet exemple, le fichier de modèle hello.blade.php pourrait ressembler à ceci :
<?php
Hello, {{ $name }}!
La sortie serait :
Hello, Bob!
Voir aussi
- Extending - Comment surcharger la méthode
render
pour utiliser un moteur de template différent. - Routing - Comment mapper des routes vers des contrôleurs et rendre des vues.
- Responses - Comment personnaliser les réponses HTTP.
- Why a Framework? - Comment les modèles s'intègrent dans l'ensemble.
Dépannage
- Si vous avez une redirection dans votre middleware, mais que votre application ne semble pas rediriger, assurez-vous d'ajouter une instruction
exit;
dans votre middleware.
Changelog
- v2.0 - Version initiale.
Learn/collections
Collections
Aperçu
La classe Collection
dans Flight est un utilitaire pratique pour gérer des ensembles de données. Elle vous permet d'accéder et de manipuler les données en utilisant à la fois la notation tableau et la notation objet, rendant votre code plus propre et plus flexible.
Compréhension
Une Collection
est essentiellement un wrapper autour d'un tableau, mais avec des pouvoirs supplémentaires. Vous pouvez l'utiliser comme un tableau, itérer dessus, compter ses éléments, et même accéder aux éléments comme s'ils étaient des propriétés d'objet. Cela est particulièrement utile lorsque vous souhaitez passer des données structurées dans votre application, ou lorsque vous voulez rendre votre code un peu plus lisible.
Les Collections implémentent plusieurs interfaces PHP :
ArrayAccess
(pour utiliser la syntaxe tableau)Iterator
(pour itérer avecforeach
)Countable
(pour utilisercount()
)JsonSerializable
(pour convertir facilement en JSON)
Utilisation de base
Créer une Collection
Vous pouvez créer une collection en passant simplement un tableau à son constructeur :
use flight\util\Collection;
$data = [
'name' => 'Flight',
'version' => 3,
'features' => ['routing', 'views', 'extending']
];
$collection = new Collection($data);
Accéder aux éléments
Vous pouvez accéder aux éléments en utilisant soit la notation tableau, soit la notation objet :
// Notation tableau
echo $collection['name']; // Sortie : FlightPHP
// Notation objet
echo $collection->version; // Sortie : 3
Si vous essayez d'accéder à une clé qui n'existe pas, vous obtiendrez null
au lieu d'une erreur.
Définir des éléments
Vous pouvez définir des éléments en utilisant l'une ou l'autre notation :
// Notation tableau
$collection['author'] = 'Mike Cao';
// Notation objet
$collection->license = 'MIT';
Vérifier et supprimer des éléments
Vérifiez si un élément existe :
if (isset($collection['name'])) {
// Faites quelque chose
}
if (isset($collection->version)) {
// Faites quelque chose
}
Supprimez un élément :
unset($collection['author']);
unset($collection->license);
Itérer sur une Collection
Les Collections sont itérables, vous pouvez donc les utiliser dans une boucle foreach
:
foreach ($collection as $key => $value) {
echo "$key: $value\n";
}
Compter les éléments
Vous pouvez compter le nombre d'éléments dans une collection :
echo count($collection); // Sortie : 4
Obtenir toutes les clés ou toutes les données
Obtenez toutes les clés :
$keys = $collection->keys(); // ['name', 'version', 'features', 'license']
Obtenez toutes les données sous forme de tableau :
$data = $collection->getData();
Vider la Collection
Supprimez tous les éléments :
$collection->clear();
Sérialisation JSON
Les Collections peuvent être facilement converties en JSON :
echo json_encode($collection);
// Sortie : {"name":"FlightPHP","version":3,"features":["routing","views","extending"],"license":"MIT"}
Utilisation avancée
Vous pouvez remplacer entièrement le tableau de données interne si nécessaire :
$collection->setData(['foo' => 'bar']);
Les Collections sont particulièrement utiles lorsque vous souhaitez passer des données structurées entre les composants, ou lorsque vous voulez fournir une interface plus orientée objet pour les données de tableau.
Voir aussi
- Requests - Apprenez à gérer les requêtes HTTP et comment les collections peuvent être utilisées pour gérer les données de requête.
- PDO Wrapper - Apprenez à utiliser le wrapper PDO dans Flight et comment les collections peuvent être utilisées pour gérer les résultats de base de données.
Dépannage
- Si vous essayez d'accéder à une clé qui n'existe pas, vous obtiendrez
null
au lieu d'une erreur. - N'oubliez pas que les collections ne sont pas récursives : les tableaux imbriqués ne sont pas automatiquement convertis en collections.
- Si vous devez réinitialiser la collection, utilisez
$collection->clear()
ou$collection->setData([])
.
Journal des modifications
- v3.0 - Amélioration des indications de type et support de PHP 8+.
- v1.0 - Première publication de la classe Collection.
Learn/flight_vs_fat_free
Flight vs Fat-Free
Qu'est-ce que Fat-Free ?
Fat-Free (affectueusement connu sous le nom de F3) est un micro-framework PHP puissant mais facile à utiliser, conçu pour vous aider à créer des applications web dynamiques et robustes - rapidement !
Flight se compare à Fat-Free de nombreuses manières et est probablement le cousin le plus proche en termes de fonctionnalités et de simplicité. Fat-Free possède beaucoup de fonctionnalités que Flight n'a pas, mais il a aussi beaucoup de fonctionnalités que Flight possède. Fat-Free commence à montrer son âge et n'est plus aussi populaire qu'avant.
Les mises à jour deviennent moins fréquentes et la communauté n'est plus aussi active qu'autrefois. Le code est suffisamment simple, mais parfois le manque de discipline syntaxique peut le rendre difficile à lire et à comprendre. Il fonctionne pour PHP 8.3, mais le code lui-même ressemble encore à celui de PHP 5.3.
Avantages par rapport à Flight
- Fat-Free a quelques étoiles de plus sur GitHub que Flight.
- Fat-Free dispose d'une documentation décente, mais elle manque de clarté dans certains domaines.
- Fat-Free propose quelques ressources éparses comme des tutoriels YouTube et des articles en ligne qui peuvent être utilisés pour apprendre le framework.
- Fat-Free intègre quelques plugins utiles qui sont parfois pratiques.
- Fat-Free dispose d'un ORM intégré appelé Mapper qui peut être utilisé pour interagir avec votre base de données. Flight propose active-record.
- Fat-Free intègre des Sessions, du Cache et de la localisation. Flight nécessite l'utilisation de bibliothèques tierces, mais cela est couvert dans la documentation.
- Fat-Free dispose d'un petit groupe de plugins créés par la communauté qui peuvent être utilisés pour étendre le framework. Flight en couvre certains dans les pages documentation et exemples.
- Fat-Free, comme Flight, n'a aucune dépendance.
- Fat-Free, comme Flight, vise à donner au développeur le contrôle sur son application et une expérience de développement simple.
- Fat-Free maintient la compatibilité arrière comme Flight (en partie parce que les mises à jour deviennent moins fréquentes).
- Fat-Free, comme Flight, est destiné aux développeurs qui s'aventurent pour la première fois dans le monde des frameworks.
- Fat-Free dispose d'un moteur de templates intégré plus robuste que celui de Flight. Flight recommande Latte pour cela.
- Fat-Free propose une commande CLI unique de type "route" qui permet de créer des applications CLI au sein de Fat-Free lui-même et de la traiter comme une requête
GET
. Flight y parvient avec runway.
Inconvénients par rapport à Flight
- Fat-Free dispose de quelques tests d'implémentation et même de sa propre classe de test qui est très basique. Cependant, elle n'est pas testée à 100 % par unités comme Flight.
- Vous devez utiliser un moteur de recherche comme Google pour rechercher réellement sur le site de documentation.
- Flight propose un mode sombre sur son site de documentation. (mic drop)
- Fat-Free a certains modules qui sont lamentablement non maintenus.
- Flight dispose d'un PdoWrapper simple qui est un peu plus simple que la classe
DB\SQL
intégrée de Fat-Free. - Flight propose un plugin de permissions qui peut être utilisé pour sécuriser votre application. Fat-Free nécessite l'utilisation d'une bibliothèque tierce.
- Flight dispose d'un ORM appelé active-record qui ressemble plus à un ORM que le Mapper de Fat-Free.
L'avantage supplémentaire de
active-record
est que vous pouvez définir des relations entre les enregistrements pour des jointures automatiques, tandis que le Mapper de Fat-Free nécessite de créer des vues SQL. - Étonnamment, Fat-Free n'a pas d'espace de noms racine. Flight est namespace tout au long pour éviter les collisions avec votre propre code.
La classe
Cache
est la plus grande offenseuse ici. - Fat-Free n'a pas de middleware. À la place, il y a des hooks
beforeroute
etafterroute
qui peuvent être utilisés pour filtrer les requêtes et réponses dans les contrôleurs. - Fat-Free ne peut pas grouper les routes.
- Fat-Free dispose d'un gestionnaire de conteneur d'injection de dépendances, mais la documentation est incroyablement sparse sur la façon de l'utiliser.
- Le débogage peut devenir un peu délicat car tout est essentiellement stocké dans ce qu'on appelle le
HIVE
Learn/extending
Extension
Aperçu
Flight est conçu pour être un framework extensible. Le framework est livré avec un ensemble de méthodes et de composants par défaut, mais il vous permet de mapper vos propres méthodes, d'enregistrer vos propres classes, ou même de surcharger les classes et méthodes existantes.
Compréhension
Il existe 2 façons d'étendre la fonctionnalité de Flight :
- Mappage de méthodes - Cela est utilisé pour créer des méthodes personnalisées simples que vous pouvez appeler depuis n'importe où dans votre application. Elles sont généralement utilisées pour des fonctions utilitaires que vous souhaitez pouvoir appeler depuis n'importe où dans votre code.
- Enregistrement de classes - Cela est utilisé pour enregistrer vos propres classes avec Flight. Cela est généralement utilisé pour des classes qui ont des dépendances ou qui nécessitent une configuration.
Vous pouvez également surcharger les méthodes du framework existantes pour modifier leur comportement par défaut afin de mieux répondre aux besoins de votre projet.
Si vous recherchez un DIC (Dependency Injection Container), passez à la page Dependency Injection Container.
Utilisation de base
Surcharge des méthodes du framework
Flight vous permet de surcharger sa fonctionnalité par défaut pour répondre à vos propres besoins, sans avoir à modifier le code. Vous pouvez voir toutes les méthodes que vous pouvez surcharger ci-dessous.
Par exemple, lorsque Flight ne peut pas faire correspondre une URL à une route, il invoque la méthode notFound
qui envoie une réponse générique HTTP 404
. Vous pouvez surcharger ce comportement
en utilisant la méthode map
:
Flight::map('notFound', function() {
// Afficher une page 404 personnalisée
include 'errors/404.html';
});
Flight vous permet également de remplacer les composants principaux du framework. Par exemple, vous pouvez remplacer la classe Router par défaut par votre propre classe personnalisée :
// créer votre classe Router personnalisée
class MyRouter extends \flight\net\Router {
// surcharger les méthodes ici
// par exemple, un raccourci pour les requêtes GET pour supprimer
// la fonctionnalité de passage de route
public function get($pattern, $callback, $alias = '') {
return parent::get($pattern, $callback, false, $alias);
}
}
// Enregistrer votre classe personnalisée
Flight::register('router', MyRouter::class);
// Lorsque Flight charge l'instance Router, il chargera votre classe
$myRouter = Flight::router();
$myRouter->get('/hello', function() {
echo "Hello World!";
}, 'hello_alias');
Cependant, les méthodes du framework comme map
et register
ne peuvent pas être surchargées. Vous obtiendrez
une erreur si vous essayez de le faire (voir encore ci-dessous pour une liste des méthodes).
Méthodes du framework mappables
Voici l'ensemble complet des méthodes pour le framework. Il se compose de méthodes principales, qui sont des méthodes statiques régulières, et de méthodes extensibles, qui sont des méthodes mappées qui peuvent être filtrées ou surchargées.
Méthodes principales
Ces méthodes sont essentielles au framework et ne peuvent pas être surchargées.
Flight::map(string $name, callable $callback, bool $pass_route = false) // Crée une méthode personnalisée du framework.
Flight::register(string $name, string $class, array $params = [], ?callable $callback = null) // Enregistre une classe à une méthode du framework.
Flight::unregister(string $name) // Désenregistre une classe d'une méthode du framework.
Flight::before(string $name, callable $callback) // Ajoute un filtre avant une méthode du framework.
Flight::after(string $name, callable $callback) // Ajoute un filtre après une méthode du framework.
Flight::path(string $path) // Ajoute un chemin pour l'autoloading des classes.
Flight::get(string $key) // Obtient une variable définie par Flight::set().
Flight::set(string $key, mixed $value) // Définit une variable dans le moteur Flight.
Flight::has(string $key) // Vérifie si une variable est définie.
Flight::clear(array|string $key = []) // Efface une variable.
Flight::init() // Initialise le framework à ses paramètres par défaut.
Flight::app() // Obtient l'instance de l'objet application
Flight::request() // Obtient l'instance de l'objet requête
Flight::response() // Obtient l'instance de l'objet réponse
Flight::router() // Obtient l'instance de l'objet routeur
Flight::view() // Obtient l'instance de l'objet vue
Méthodes extensibles
Flight::start() // Démarre le framework.
Flight::stop() // Arrête le framework et envoie une réponse.
Flight::halt(int $code = 200, string $message = '') // Arrête le framework avec un code de statut et un message optionnels.
Flight::route(string $pattern, callable $callback, bool $pass_route = false, string $alias = '') // Mappe un motif d'URL à un callback.
Flight::post(string $pattern, callable $callback, bool $pass_route = false, string $alias = '') // Mappe un motif d'URL de requête POST à un callback.
Flight::put(string $pattern, callable $callback, bool $pass_route = false, string $alias = '') // Mappe un motif d'URL de requête PUT à un callback.
Flight::patch(string $pattern, callable $callback, bool $pass_route = false, string $alias = '') // Mappe un motif d'URL de requête PATCH à un callback.
Flight::delete(string $pattern, callable $callback, bool $pass_route = false, string $alias = '') // Mappe un motif d'URL de requête DELETE à un callback.
Flight::group(string $pattern, callable $callback) // Crée un groupement pour les URLs, le motif doit être une chaîne.
Flight::getUrl(string $name, array $params = []) // Génère une URL basée sur un alias de route.
Flight::redirect(string $url, int $code) // Redirige vers une autre URL.
Flight::download(string $filePath) // Télécharge un fichier.
Flight::render(string $file, array $data, ?string $key = null) // Rend un fichier de template.
Flight::error(Throwable $error) // Envoie une réponse HTTP 500.
Flight::notFound() // Envoie une réponse HTTP 404.
Flight::etag(string $id, string $type = 'string') // Effectue le cache HTTP ETag.
Flight::lastModified(int $time) // Effectue le cache HTTP de dernière modification.
Flight::json(mixed $data, int $code = 200, bool $encode = true, string $charset = 'utf8', int $option) // Envoie une réponse JSON.
Flight::jsonp(mixed $data, string $param = 'jsonp', int $code = 200, bool $encode = true, string $charset = 'utf8', int $option) // Envoie une réponse JSONP.
Flight::jsonHalt(mixed $data, int $code = 200, bool $encode = true, string $charset = 'utf8', int $option) // Envoie une réponse JSON et arrête le framework.
Flight::onEvent(string $event, callable $callback) // Enregistre un écouteur d'événement.
Flight::triggerEvent(string $event, ...$args) // Déclenche un événement.
Toute méthode personnalisée ajoutée avec map
et register
peut également être filtrée. Pour des exemples sur la façon de filtrer ces méthodes, voir le guide Filtering Methods.
Classes du framework extensibles
Il existe plusieurs classes dont vous pouvez surcharger la fonctionnalité en les étendant et en enregistrant votre propre classe. Ces classes sont :
Flight::app() // Classe Application - étendre la classe flight\Engine
Flight::request() // Classe Requête - étendre la classe flight\net\Request
Flight::response() // Classe Réponse - étendre la classe flight\net\Response
Flight::router() // Classe Routeur - étendre la classe flight\net\Router
Flight::view() // Classe Vue - étendre la classe flight\template\View
Flight::eventDispatcher() // Classe Dispatch d'événements - étendre la classe flight\core\Dispatcher
Mappage de méthodes personnalisées
Pour mapper votre propre méthode personnalisée simple, vous utilisez la fonction map
:
// Mapper votre méthode
Flight::map('hello', function (string $name) {
echo "hello $name!";
});
// Appeler votre méthode personnalisée
Flight::hello('Bob');
Bien qu'il soit possible de créer des méthodes personnalisées simples, il est recommandé de simplement créer des fonctions standard en PHP. Cela offre l'autocomplétion dans les IDE et est plus facile à lire. L'équivalent du code ci-dessus serait :
function hello(string $name) {
echo "hello $name!";
}
hello('Bob');
Cela est utilisé plus souvent lorsque vous devez passer des variables dans votre méthode pour obtenir une
valeur attendue. Utiliser la méthode register()
comme ci-dessous est plus pour passer une configuration
et ensuite appeler votre classe pré-configurée.
Enregistrement de classes personnalisées
Pour enregistrer votre propre classe et la configurer, vous utilisez la fonction register
. L'avantage que cela a sur map() est que vous pouvez réutiliser la même classe lorsque vous appelez cette fonction (ce qui serait utile avec Flight::db()
pour partager la même instance).
// Enregistrer votre classe
Flight::register('user', User::class);
// Obtenir une instance de votre classe
$user = Flight::user();
La méthode register vous permet également de passer des paramètres au constructeur de votre classe. Ainsi, lorsque vous chargez votre classe personnalisée, elle sera pré-initialisée. Vous pouvez définir les paramètres du constructeur en passant un tableau supplémentaire. Voici un exemple de chargement d'une connexion à la base de données :
// Enregistrer la classe avec des paramètres de constructeur
Flight::register('db', PDO::class, ['mysql:host=localhost;dbname=test', 'user', 'pass']);
// Obtenir une instance de votre classe
// Cela créera un objet avec les paramètres définis
//
// new PDO('mysql:host=localhost;dbname=test','user','pass');
//
$db = Flight::db();
// et si vous en avez besoin plus tard dans votre code, vous appelez simplement la même méthode
class SomeController {
public function __construct() {
$this->db = Flight::db();
}
}
Si vous passez un paramètre de callback supplémentaire, il sera exécuté immédiatement après la construction de la classe. Cela vous permet d'effectuer toute procédure de configuration pour votre nouvel objet. La fonction de callback prend un paramètre, une instance du nouvel objet.
// Le callback recevra l'objet qui a été construit
Flight::register(
'db',
PDO::class,
['mysql:host=localhost;dbname=test', 'user', 'pass'],
function (PDO $db) {
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
}
);
Par défaut, chaque fois que vous chargez votre classe, vous obtiendrez une instance partagée.
Pour obtenir une nouvelle instance d'une classe, passez simplement false
en tant que paramètre :
// Instance partagée de la classe
$shared = Flight::db();
// Nouvelle instance de la classe
$new = Flight::db(false);
Note : Gardez à l'esprit que les méthodes mappées ont la priorité sur les classes enregistrées. Si vous déclarez les deux en utilisant le même nom, seule la méthode mappée sera invoquée.
Exemples
Voici quelques exemples de la façon dont vous pouvez étendre Flight avec une fonctionnalité qui n'est pas intégrée au noyau.
Journalisation
Flight n'a pas de système de journalisation intégré, cependant, il est vraiment facile d'utiliser une bibliothèque de journalisation avec Flight. Voici un exemple utilisant la bibliothèque Monolog :
// services.php
// Enregistrer le logger avec 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));
});
Maintenant qu'il est enregistré, vous pouvez l'utiliser dans votre application :
// Dans votre contrôleur ou route
Flight::log()->warning('This is a warning message');
Cela journalisera un message dans le fichier de log que vous avez spécifié. Et si vous voulez journaliser quelque chose quand une
erreur se produit ? Vous pouvez utiliser la méthode error
:
// Dans votre contrôleur ou route
Flight::map('error', function(Throwable $ex) {
Flight::log()->error($ex->getMessage());
// Afficher votre page d'erreur personnalisée
include 'errors/500.html';
});
Vous pourriez également créer un système APM (Application Performance Monitoring) de base
en utilisant les méthodes before
et after
:
// Dans votre fichier services.php
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');
// Vous pourriez également ajouter vos en-têtes de requête ou de réponse
// pour les journaliser également (soyez prudent car cela serait beaucoup de
// données si vous avez beaucoup de requêtes)
Flight::log()->info('Request Headers: ' . json_encode(Flight::request()->headers));
Flight::log()->info('Response Headers: ' . json_encode(Flight::response()->headers));
});
Mise en cache
Flight n'a pas de système de mise en cache intégré, cependant, il est vraiment facile d'utiliser une bibliothèque de mise en cache avec Flight. Voici un exemple utilisant la bibliothèque PHP File Cache :
// services.php
// Enregistrer le cache avec Flight
Flight::register('cache', \flight\Cache::class, [ __DIR__ . '/../cache/' ], function(\flight\Cache $cache) {
$cache->setDevMode(ENVIRONMENT === 'development');
});
Maintenant qu'il est enregistré, vous pouvez l'utiliser dans votre application :
// Dans votre contrôleur ou route
$data = Flight::cache()->get('my_cache_key');
if (empty($data)) {
// Effectuer un traitement pour obtenir les données
$data = [ 'some' => 'data' ];
Flight::cache()->set('my_cache_key', $data, 3600); // cache pour 1 heure
}
Instanciation facile d'objets DIC
Si vous utilisez un DIC (Dependency Injection Container) dans votre application, vous pouvez utiliser Flight pour vous aider à instancier vos objets. Voici un exemple utilisant la bibliothèque Dice :
// services.php
// créer un nouveau conteneur
$container = new \Dice\Dice;
// n'oubliez pas de le réassigner à lui-même comme ci-dessous !
$container = $container->addRule('PDO', [
// shared signifie que le même objet sera retourné à chaque fois
'shared' => true,
'constructParams' => ['mysql:host=localhost;dbname=test', 'user', 'pass' ]
]);
// maintenant nous pouvons créer une méthode mappable pour créer n'importe quel objet.
Flight::map('make', function($class, $params = []) use ($container) {
return $container->create($class, $params);
});
// Cela enregistre le gestionnaire de conteneur pour que Flight sache l'utiliser pour les contrôleurs/middleware
Flight::registerContainerHandler(function($class, $params) {
Flight::make($class, $params);
});
// disons que nous avons la classe d'exemple suivante qui prend un objet PDO dans le constructeur
class EmailCron {
protected PDO $pdo;
public function __construct(PDO $pdo) {
$this->pdo = $pdo;
}
public function send() {
// code qui envoie un email
}
}
// Et enfin vous pouvez créer des objets en utilisant l'injection de dépendances
$emailCron = Flight::make(EmailCron::class);
$emailCron->send();
Génial, non ?
Voir aussi
- Dependency Injection Container - Comment utiliser un DIC avec Flight.
- File Cache - Exemple d'utilisation d'une bibliothèque de mise en cache avec Flight.
Dépannage
- Rappelez-vous que les méthodes mappées ont la priorité sur les classes enregistrées. Si vous déclarez les deux en utilisant le même nom, seule la méthode mappée sera invoquée.
Journal des modifications
- v2.0 - Version initiale.
Learn/json
Wrapper JSON
Aperçu
La classe Json
dans Flight fournit une façon simple et cohérente d'encoder et de décoder des données JSON dans votre application. Elle enveloppe les fonctions JSON natives de PHP avec une meilleure gestion des erreurs et certains paramètres par défaut utiles, rendant plus facile et plus sûr de travailler avec JSON.
Comprendre
Travailler avec JSON est très courant dans les applications PHP modernes, surtout lors de la construction d'API ou de la gestion de requêtes AJAX. La classe Json
centralise tout l'encodage et le décodage JSON, afin que vous n'ayez pas à vous soucier de cas limites étranges ou d'erreurs cryptiques des fonctions intégrées de PHP.
Fonctionnalités clés :
- Gestion cohérente des erreurs (lève des exceptions en cas d'échec)
- Options par défaut pour l'encodage/décodage (comme les barres obliques non échappées)
- Méthodes utilitaires pour l'impression formatée et la validation
Utilisation de base
Encodage des données en JSON
Pour convertir des données PHP en une chaîne JSON, utilisez Json::encode()
:
use flight\util\Json;
$data = [
'framework' => 'Flight',
'version' => 3,
'features' => ['routing', 'views', 'extending']
];
$json = Json::encode($data);
echo $json;
// Output: {"framework":"Flight","version":3,"features":["routing","views","extending"]}
Si l'encodage échoue, vous obtiendrez une exception avec un message d'erreur utile.
Impression formatée
Voulez-vous que votre JSON soit lisible par un humain ? Utilisez prettyPrint()
:
echo Json::prettyPrint($data);
/*
{
"framework": "Flight",
"version": 3,
"features": [
"routing",
"views",
"extending"
]
}
*/
Décodage de chaînes JSON
Pour convertir une chaîne JSON en données PHP, utilisez Json::decode()
:
$json = '{"framework":"Flight","version":3}';
$data = Json::decode($json);
echo $data->framework; // Output: Flight
Si vous voulez un tableau associatif au lieu d'un objet, passez true
comme second argument :
$data = Json::decode($json, true);
echo $data['framework']; // Output: Flight
Si le décodage échoue, vous obtiendrez une exception avec un message d'erreur clair.
Validation de JSON
Vérifiez si une chaîne est un JSON valide :
if (Json::isValid($json)) {
// C'est valide !
} else {
// Pas un JSON valide
}
Obtenir la dernière erreur
Si vous voulez vérifier le dernier message d'erreur JSON (des fonctions PHP natives) :
$error = Json::getLastError();
if ($error !== '') {
echo "Last JSON error: $error";
}
Utilisation avancée
Vous pouvez personnaliser les options d'encodage et de décodage si vous avez besoin de plus de contrôle (voir les options de json_encode de PHP) :
// Encoder avec l'option JSON_HEX_TAG
$json = Json::encode($data, JSON_HEX_TAG);
// Décoder avec une profondeur personnalisée
$data = Json::decode($json, false, 1024);
Voir aussi
- Collections - Pour travailler avec des données structurées qui peuvent être facilement converties en JSON.
- Configuration - Comment configurer votre application Flight.
- Extending - Comment ajouter vos propres utilitaires ou surcharger les classes de base.
Dépannage
- Si l'encodage ou le décodage échoue, une exception est levée — enveloppez vos appels dans try/catch si vous voulez gérer les erreurs gracieusement.
- Si vous obtenez des résultats inattendus, vérifiez vos données pour des références circulaires ou des caractères non-UTF8.
- Utilisez
Json::isValid()
pour vérifier si une chaîne est un JSON valide avant de la décoder.
Journal des modifications
- v3.16.0 - Ajout de la classe utilitaire wrapper JSON.
Learn/flight_vs_slim
Flight vs Slim
Qu'est-ce que Slim ?
Slim est un micro-framework PHP qui vous aide à écrire rapidement des applications web simples mais puissantes et des API.
Beaucoup d'inspirations pour certaines fonctionnalités de la version 3 de Flight proviennent en fait de Slim. La regroupement des routes et l'exécution du middleware dans un ordre spécifique sont deux fonctionnalités inspirées de Slim. Slim v3 est sorti avec un accent sur la simplicité, mais il y a eu des avis mitigés concernant la v4.
Avantages par rapport à Flight
- Slim dispose d'une communauté plus large de développeurs, qui à leur tour créent des modules pratiques pour vous aider à ne pas réinventer la roue.
- Slim suit de nombreuses interfaces et normes courantes dans la communauté PHP, ce qui augmente l'interopérabilité.
- Slim a une documentation décente et des tutoriels qui peuvent être utilisés pour apprendre le framework (rien de comparable à Laravel ou Symfony cependant).
- Slim offre divers ressources comme des tutoriels YouTube et des articles en ligne qui peuvent être utilisés pour apprendre le framework.
- Slim vous permet d'utiliser les composants que vous voulez pour gérer les fonctionnalités de routage de base, car il est conforme à PSR-7.
Inconvénients par rapport à Flight
- Étonnamment, Slim n'est pas aussi rapide que vous pourriez le penser pour un micro-framework. Consultez les benchmarks TechEmpower pour plus d'informations.
- Flight est conçu pour un développeur qui cherche à construire une application web légère, rapide et facile à utiliser.
- Flight n'a aucune dépendance, alors que Slim a quelques dépendances que vous devez installer.
- Flight est conçu pour la simplicité et la facilité d'utilisation.
- L'une des fonctionnalités principales de Flight est qu'il fait de son mieux pour maintenir la compatibilité arrière. Le passage de Slim v3 à v4 a été un changement cassant.
- Flight est destiné aux développeurs qui s'aventurent pour la première fois dans le monde des frameworks.
- Flight peut également gérer des applications de niveau entreprise, mais il n'a pas autant d'exemples et de tutoriels que Slim. Cela nécessitera également plus de discipline de la part du développeur pour garder les choses organisées et bien structurées.
- Flight donne au développeur plus de contrôle sur l'application, alors que Slim peut introduire de la magie en coulisses.
- Flight dispose d'un simple PdoWrapper qui peut être utilisé pour interagir avec votre base de données. Slim nécessite l'utilisation d'une bibliothèque tierce.
- Flight a un plugin permissions qui peut être utilisé pour sécuriser votre application. Slim nécessite l'utilisation d'une bibliothèque tierce.
- Flight a un ORM appelé active-record qui peut être utilisé pour interagir avec votre base de données. Slim nécessite l'utilisation d'une bibliothèque tierce.
- Flight a une application CLI appelée runway qui peut être utilisée pour exécuter votre application depuis la ligne de commande. Slim n'en a pas.
Learn/autoloading
Autoloading
Aperçu
L'autoloading est un concept en PHP où vous spécifiez un répertoire ou des répertoires pour charger les classes. Cela est beaucoup plus avantageux que d'utiliser require
ou include
pour charger les classes. C'est également une exigence pour utiliser les packages Composer.
Compréhension
Par défaut, toute classe Flight
est autoloadée automatiquement pour vous grâce à Composer. Cependant, si vous souhaitez autoloader vos propres classes, vous pouvez utiliser la méthode Flight::path()
pour spécifier un répertoire à partir duquel charger les classes.
L'utilisation d'un autoloader peut aider à simplifier votre code de manière significative. Au lieu d'avoir des fichiers qui commencent par une multitude d'instructions include
ou require
en haut pour capturer toutes les classes utilisées dans ce fichier, vous pouvez au contraire appeler dynamiquement vos classes et elles seront incluses automatiquement.
Utilisation de base
Supposons que nous ayons un arbre de répertoires comme suit :
# Exemple de chemin
/home/user/project/my-flight-project/
├── app
│ ├── cache
│ ├── config
│ ├── controllers - contient les contrôleurs pour ce projet
│ ├── translations
│ ├── UTILS - contient les classes pour cette application uniquement (tout en majuscules exprès pour un exemple plus tard)
│ └── views
└── public
└── css
└── js
└── index.php
Vous avez peut-être remarqué que c'est la même structure de fichiers que celle de ce site de documentation.
Vous pouvez spécifier chaque répertoire à charger comme ceci :
/**
* public/index.php
*/
// Ajouter un chemin à l'autoloader
Flight::path(__DIR__.'/../app/controllers/');
Flight::path(__DIR__.'/../app/utils/');
/**
* app/controllers/MyController.php
*/
// pas de namespace requis
// Toutes les classes autoloadées sont recommandées d'être en Pascal Case (chaque mot en majuscule, sans espaces)
class MyController {
public function index() {
// faire quelque chose
}
}
Espaces de noms
Si vous avez des namespaces, il devient en fait très facile de les implémenter. Vous devriez utiliser la méthode Flight::path()
pour spécifier le répertoire racine (pas la racine du document ou le dossier public/
) de votre application.
/**
* public/index.php
*/
// Ajouter un chemin à l'autoloader
Flight::path(__DIR__.'/../');
Maintenant, voici à quoi pourrait ressembler votre contrôleur. Regardez l'exemple ci-dessous, mais prêtez attention aux commentaires pour des informations importantes.
/**
* app/controllers/MyController.php
*/
// les namespaces sont requis
// les namespaces sont les mêmes que la structure de répertoires
// les namespaces doivent suivre la même casse que la structure de répertoires
// les namespaces et les répertoires ne peuvent pas avoir de tirets bas (sauf si Loader::setV2ClassLoading(false) est défini)
namespace app\controllers;
// Toutes les classes autoloadées sont recommandées d'être en Pascal Case (chaque mot en majuscule, sans espaces)
// À partir de 3.7.2, vous pouvez utiliser Pascal_Snake_Case pour les noms de vos classes en exécutant Loader::setV2ClassLoading(false);
class MyController {
public function index() {
// faire quelque chose
}
}
Et si vous vouliez autoloader une classe dans votre répertoire utils, vous feriez essentiellement la même chose :
/**
* app/UTILS/ArrayHelperUtil.php
*/
// le namespace doit correspondre à la structure de répertoires et à la casse (notez que le répertoire UTILS est tout en majuscules
// comme dans l'arbre de fichiers ci-dessus)
namespace app\UTILS;
class ArrayHelperUtil {
public function changeArrayCase(array $array) {
// faire quelque chose
}
}
Tirets bas dans les noms de classes
À partir de 3.7.2, vous pouvez utiliser Pascal_Snake_Case pour les noms de vos classes en exécutant Loader::setV2ClassLoading(false);
.
Cela vous permettra d'utiliser des tirets bas dans les noms de vos classes.
Cela n'est pas recommandé, mais c'est disponible pour ceux qui en ont besoin.
use flight\core\Loader;
/**
* public/index.php
*/
// Ajouter un chemin à l'autoloader
Flight::path(__DIR__.'/../app/controllers/');
Flight::path(__DIR__.'/../app/utils/');
Loader::setV2ClassLoading(false);
/**
* app/controllers/My_Controller.php
*/
// pas de namespace requis
class My_Controller {
public function index() {
// faire quelque chose
}
}
Voir aussi
- Routing - Comment mapper les routes aux contrôleurs et rendre les vues.
- Pourquoi un Framework ? - Comprendre les avantages d'utiliser un framework comme Flight.
Dépannage
- Si vous ne parvenez pas à comprendre pourquoi vos classes avec namespace ne sont pas trouvées, rappelez-vous d'utiliser
Flight::path()
vers le répertoire racine de votre projet, pas votre répertoireapp/
ousrc/
ou équivalent.
Classe non trouvée (autoloading ne fonctionne pas)
Il pourrait y avoir plusieurs raisons pour cela. Ci-dessous, quelques exemples, mais assurez-vous également de consulter la section autoloading.
Nom de fichier incorrect
Le plus courant est que le nom de la classe ne correspond pas au nom du fichier.
Si vous avez une classe nommée MyClass
, alors le fichier devrait s'appeler MyClass.php
. Si vous avez une classe nommée MyClass
et que le fichier s'appelle myclass.php
alors l'autoloader ne pourra pas la trouver.
Namespace incorrect
Si vous utilisez des namespaces, alors le namespace devrait correspondre à la structure de répertoires.
// ...code...
// si votre MyController est dans le répertoire app/controllers et qu'il est namespacé
// cela ne fonctionnera pas.
Flight::route('/hello', 'MyController->hello');
// vous devrez choisir l'une de ces options
Flight::route('/hello', 'app\controllers\MyController->hello');
// ou si vous avez une instruction use en haut
use app\controllers\MyController;
Flight::route('/hello', [ MyController::class, 'hello' ]);
// peut aussi être écrit
Flight::route('/hello', MyController::class.'->hello');
// ou encore...
Flight::route('/hello', [ 'app\controllers\MyController', 'hello' ]);
path()
non défini
Dans l'application squelette, cela est défini dans le fichier config.php
, mais pour que vos classes soient trouvées, vous devez vous assurer que la méthode path()
est définie (probablement vers la racine de votre répertoire) avant de l'utiliser.
// Ajouter un chemin à l'autoloader
Flight::path(__DIR__.'/../');
Journal des modifications
- v3.7.2 - Vous pouvez utiliser Pascal_Snake_Case pour les noms de vos classes en exécutant
Loader::setV2ClassLoading(false);
- v2.0 - Fonctionnalité d'autoload ajoutée.
Learn/uploaded_file
Gestionnaire de Fichiers Téléversés
Aperçu
La classe UploadedFile
dans Flight facilite et sécurise la gestion des téléversements de fichiers dans votre application. Elle encapsule les détails du processus de téléversement de fichiers de PHP, vous offrant une façon simple et orientée objet d'accéder aux informations sur les fichiers et de déplacer les fichiers téléversés.
Compréhension
Lorsque un utilisateur téléverse un fichier via un formulaire, PHP stocke les informations sur le fichier dans le superglobal $_FILES
. Dans Flight, vous interagissez rarement directement avec $_FILES
. Au lieu de cela, l'objet Request
de Flight (accessible via Flight::request()
) fournit une méthode getUploadedFiles()
qui retourne un tableau d'objets UploadedFile
, rendant la gestion des fichiers beaucoup plus pratique et robuste.
La classe UploadedFile
fournit des méthodes pour :
- Obtenir le nom de fichier original, le type MIME, la taille et l'emplacement temporaire
- Vérifier les erreurs de téléversement
- Déplacer le fichier téléversé vers un emplacement permanent
Cette classe vous aide à éviter les pièges courants avec les téléversements de fichiers, comme la gestion des erreurs ou le déplacement sécurisé des fichiers.
Utilisation de Base
Accès aux Fichiers Téléversés depuis une Requête
La façon recommandée d'accéder aux fichiers téléversés est via l'objet de requête :
Flight::route('POST /upload', function() {
// Pour un champ de formulaire nommé <input type="file" name="myFile">
$uploadedFiles = Flight::request()->getUploadedFiles();
$file = $uploadedFiles['myFile'];
// Maintenant vous pouvez utiliser les méthodes de UploadedFile
if ($file->getError() === UPLOAD_ERR_OK) {
$file->moveTo('/path/to/uploads/' . $file->getClientFilename());
echo "Fichier téléversé avec succès !";
} else {
echo "Échec du téléversement : " . $file->getError();
}
});
Gestion de Téléversements Multiples de Fichiers
Si votre formulaire utilise name="myFiles[]"
pour des téléversements multiples, vous obtiendrez un tableau d'objets UploadedFile
:
Flight::route('POST /upload', function() {
// Pour un champ de formulaire nommé <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 "Téléversé : " . $file->getClientFilename() . "<br>";
} else {
echo "Échec du téléversement : " . $file->getClientFilename() . "<br>";
}
}
});
Création Manuelle d'une Instance UploadedFile
Généralement, vous ne créerez pas un UploadedFile
manuellement, mais vous le pouvez si nécessaire :
use flight\net\UploadedFile;
$file = new UploadedFile(
$_FILES['myfile']['name'],
$_FILES['myfile']['type'],
$_FILES['myfile']['size'],
$_FILES['myfile']['tmp_name'],
$_FILES['myfile']['error']
);
Accès aux Informations sur le Fichier
Vous pouvez facilement obtenir les détails sur le fichier téléversé :
echo $file->getClientFilename(); // Nom de fichier original depuis l'ordinateur de l'utilisateur
echo $file->getClientMediaType(); // Type MIME (par ex., image/png)
echo $file->getSize(); // Taille du fichier en octets
echo $file->getTempName(); // Chemin temporaire du fichier sur le serveur
echo $file->getError(); // Code d'erreur de téléversement (0 signifie pas d'erreur)
Déplacement du Fichier Téléversé
Après avoir validé le fichier, déplacez-le vers un emplacement permanent :
try {
$file->moveTo('/path/to/uploads/' . $file->getClientFilename());
echo "Fichier téléversé avec succès !";
} catch (Exception $e) {
echo "Échec du téléversement : " . $e->getMessage();
}
La méthode moveTo()
lancera une exception si quelque chose se passe mal (comme une erreur de téléversement ou un problème de permissions).
Gestion des Erreurs de Téléversement
S'il y a eu un problème pendant le téléversement, vous pouvez obtenir un message d'erreur lisible par un humain :
if ($file->getError() !== UPLOAD_ERR_OK) {
// Vous pouvez utiliser le code d'erreur ou capturer l'exception de moveTo()
echo "Il y a eu une erreur lors du téléversement du fichier.";
}
Voir Aussi
- Requests - Apprenez comment accéder aux fichiers téléversés depuis les requêtes HTTP et voyez plus d'exemples de téléversement de fichiers.
- Configuration - Comment configurer les limites de téléversement et les répertoires dans PHP.
- Extending - Comment personnaliser ou étendre les classes principales de Flight.
Dépannage
- Vérifiez toujours
$file->getError()
avant de déplacer le fichier. - Assurez-vous que votre répertoire de téléversement est accessible en écriture par le serveur web.
- Si
moveTo()
échoue, vérifiez le message d'exception pour les détails. - Les paramètres
upload_max_filesize
etpost_max_size
de PHP peuvent limiter les téléversements de fichiers. - Pour les téléversements multiples de fichiers, bouclez toujours à travers le tableau d'objets
UploadedFile
.
Journal des Modifications
- v3.12.0 - Ajout de la classe
UploadedFile
à l'objet de requête pour une gestion plus facile des fichiers.
Guides/unit_testing
Tests unitaires dans Flight PHP avec PHPUnit
Ce guide présente les tests unitaires dans Flight PHP en utilisant PHPUnit, destiné aux débutants qui souhaitent comprendre pourquoi les tests unitaires sont importants et comment les appliquer de manière pratique. Nous nous concentrerons sur les tests de comportement — s'assurer que votre application fait ce que vous attendez, comme envoyer un e-mail ou sauvegarder un enregistrement — plutôt que sur des calculs triviaux. Nous commencerons par un simple gestionnaire de routes et progresserons vers un contrôleur plus complexe, en intégrant l'injection de dépendances [/learn/dependency-injection-container) (DI) et la simulation de services tiers.
Pourquoi effectuer des tests unitaires ?
Les tests unitaires garantissent que votre code se comporte comme prévu, en détectant les bogues avant qu'ils n'atteignent la production. Ils sont particulièrement précieux dans Flight, où le routage léger et la flexibilité peuvent entraîner des interactions complexes. Pour les développeurs solos ou les équipes, les tests unitaires servent de filet de sécurité, documentent le comportement attendu et préviennent les régressions lorsque vous revenez sur le code plus tard. Ils améliorent également la conception : un code difficile à tester signale souvent des classes trop complexes ou trop fortement couplées.
Contrairement à des exemples simplistes (par exemple, tester x * y = z
), nous nous concentrerons sur des comportements du monde réel, tels que la validation d'entrée, la sauvegarde de données ou le déclenchement d'actions comme les e-mails. Notre objectif est de rendre les tests accessibles et significatifs.
Principes directeurs généraux
- Tester le comportement, pas l'implémentation : Concentrez-vous sur les résultats (par exemple, « e-mail envoyé » ou « enregistrement sauvegardé ») plutôt que sur les détails internes. Cela rend les tests robustes face aux refactorisations.
- Arrêtez d'utiliser
Flight::
: Les méthodes statiques de Flight sont terriblement pratiques, mais rendent les tests difficiles. Vous devriez vous habituer à utiliser la variable$app
de$app = Flight::app();
.$app
possède toutes les mêmes méthodes queFlight::
. Vous pourrez toujours utiliser$app->route()
ou$this->app->json()
dans votre contrôleur, etc. Vous devriez également utiliser le vrai routeur Flight avec$router = $app->router()
et ensuite vous pouvez utiliser$router->get()
,$router->post()
,$router->group()
, etc. Voir Routage. - Gardez les tests rapides : Des tests rapides encouragent une exécution fréquente. Évitez les opérations lentes comme les appels à la base de données dans les tests unitaires. Si vous avez un test lent, c'est un signe que vous écrivez un test d'intégration, pas un test unitaire. Les tests d'intégration sont ceux où vous impliquerez réellement des bases de données réelles, des appels HTTP réels, l'envoi d'e-mails réels, etc. Ils ont leur place, mais ils sont lents et peuvent être instables, ce qui signifie qu'ils échouent parfois pour une raison inconnue.
- Utilisez des noms descriptifs : Les noms des tests doivent décrire clairement le comportement testé. Cela améliore la lisibilité et la maintenabilité.
- Évitez les globales comme la peste : Minimisez l'utilisation de
$app->set()
et$app->get()
, car elles agissent comme un état global, nécessitant des simulations dans chaque test. Privilégiez l'injection de dépendances ou un conteneur DI (voir Conteneur d'injection de dépendances). Même l'utilisation de la méthode$app->map()
est techniquement une « globale » et devrait être évitée au profit de l'injection de dépendances. Utilisez une bibliothèque de session comme flightphp/session afin de pouvoir simuler l'objet session dans vos tests. Ne appelez pas$_SESSION
directement dans votre code car cela injecte une variable globale dans votre code, rendant le test difficile. - Utilisez l'injection de dépendances : Injectez les dépendances (par exemple,
PDO
, expéditeurs d'e-mails) dans les contrôleurs pour isoler la logique et simplifier la simulation. Si vous avez une classe avec trop de dépendances, envisagez de la refactoriser en classes plus petites, chacune ayant une seule responsabilité suivant les principes SOLID. - Simulez les services tiers : Simulez les bases de données, les clients HTTP (cURL) ou les services d'e-mail pour éviter les appels externes. Testez une ou deux couches en profondeur, mais laissez votre logique principale s'exécuter. Par exemple, si votre application envoie un message texte, vous NE VOULEZ PAS vraiment envoyer un message texte à chaque exécution de vos tests car ces frais s'accumuleront (et ce sera plus lent). Au lieu de cela, simulez le service de message texte et vérifiez simplement que votre code a appelé le service de message texte avec les bons paramètres.
- Visez une couverture élevée, pas la perfection : Une couverture de ligne à 100 % est bonne, mais cela ne signifie pas réellement que tout dans votre code est testé comme il le devrait (allez-y et recherchez la couverture de branche/chemin dans PHPUnit). Priorisez les comportements critiques (par exemple, l'inscription d'utilisateur, les réponses API et la capture des réponses échouées).
- Utilisez des contrôleurs pour les routes : Dans vos définitions de routes, utilisez des contrôleurs et non des fermetures. L'instance
flight\Engine $app
est injectée dans chaque contrôleur via le constructeur par défaut. Dans les tests, utilisez$app = new Flight\Engine()
pour instancier Flight dans un test, injectez-la dans votre contrôleur et appelez directement les méthodes (par exemple,$controller->register()
). Voir Extension de Flight et Routage. - Choisissez un style de simulation et tenez-vous-y : PHPUnit prend en charge plusieurs styles de simulation (par exemple, prophecy, simulations intégrées), ou vous pouvez utiliser des classes anonymes qui ont leurs propres avantages comme l'autocomplétion de code, la rupture si vous changez la définition de la méthode, etc. Soyez simplement cohérent dans vos tests. Voir Objets simulés de PHPUnit.
- Utilisez la visibilité
protected
pour les méthodes/propriétés que vous voulez tester dans les sous-classes : Cela vous permet de les surcharger dans les sous-classes de test sans les rendre publiques, ce qui est particulièrement utile pour les simulations de classes anonymes.
Configuration de PHPUnit
Tout d'abord, configurez PHPUnit dans votre projet Flight PHP en utilisant Composer pour des tests faciles. Voir le guide de démarrage de PHPUnit pour plus de détails.
-
Dans le répertoire de votre projet, exécutez :
composer require --dev phpunit/phpunit
Cela installe la dernière version de PHPUnit en tant que dépendance de développement.
-
Créez un répertoire
tests
à la racine de votre projet pour les fichiers de test. -
Ajoutez un script de test à
composer.json
pour plus de commodité :// autre contenu de composer.json "scripts": { "test": "phpunit --configuration phpunit.xml" }
-
Créez un fichier
phpunit.xml
à la racine :<?xml version="1.0" encoding="UTF-8"?> <phpunit bootstrap="vendor/autoload.php"> <testsuites> <testsuite name="Flight Tests"> <directory>tests</directory> </testsuite> </testsuites> </phpunit>
Maintenant, lorsque vos tests sont construits, vous pouvez exécuter composer test
pour exécuter les tests.
Test d'un gestionnaire de route simple
Commençons par une route de base [/learn/routing] qui valide l'entrée e-mail d'un utilisateur. Nous testerons son comportement : renvoyer un message de succès pour les e-mails valides et une erreur pour les invalides. Pour la validation d'e-mail, nous utilisons 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' => 'Invalid email'];
} else {
$responseArray = ['status' => 'success', 'message' => 'Valid email'];
}
$this->app->json($responseArray);
}
}
Pour tester cela, créez un fichier de test. Voir Tests unitaires et principes SOLID pour plus d'informations sur la structuration des tests :
// tests/UserControllerTest.php
use PHPUnit\Framework\TestCase;
use Flight;
use flight\Engine;
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('Valid email', $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('Invalid email', $output['message']);
}
}
Points clés :
- Nous simulons les données POST en utilisant la classe request. N'utilisez pas les globales comme
$_POST
,$_GET
, etc. car cela complique les tests (vous devez toujours réinitialiser ces valeurs ou d'autres tests pourraient échouer). - Tous les contrôleurs auront par défaut l'instance
flight\Engine
injectée en eux même sans conteneur DIC configuré. Cela rend beaucoup plus facile de tester directement les contrôleurs. - Il n'y a aucune utilisation de
Flight::
, rendant le code plus facile à tester. - Les tests vérifient le comportement : statut et message corrects pour les e-mails valides/invalides.
Exécutez composer test
pour vérifier que la route se comporte comme prévu. Pour plus d'informations sur les requêtes et les réponses dans Flight, voir les documents pertinents.
Utilisation de l'injection de dépendances pour des contrôleurs testables
Pour des scénarios plus complexes, utilisez l'injection de dépendances [/learn/dependency-injection-container) (DI) pour rendre les contrôleurs testables. Évitez les globales de Flight (par exemple, Flight::set()
, Flight::map()
, Flight::register()
) car elles agissent comme un état global, nécessitant des simulations pour chaque test. Au lieu de cela, utilisez le conteneur DI de Flight, DICE, PHP-DI ou une injection manuelle DI.
Utilisons flight\database\PdoWrapper
au lieu de PDO brut. Ce wrapper est beaucoup plus facile à simuler et à tester unitairement !
Voici un contrôleur qui sauvegarde un utilisateur dans une base de données et envoie un e-mail de bienvenue :
use flight\database\PdoWrapper;
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' => 'Invalid email']);
}
$this->db->runQuery('INSERT INTO users (email) VALUES (?)', [$email]);
$this->mailer->sendWelcome($email);
return $this->app->json(['status' => 'success', 'message' => 'User registered']);
}
}
Points clés :
- Le contrôleur dépend d'une instance
PdoWrapper
et d'unMailerInterface
(un service d'e-mail tiers fictif). - Les dépendances sont injectées via le constructeur, évitant les globales.
Test du contrôleur avec des simulations
Maintenant, testons le comportement de UserController
: validation des e-mails, sauvegarde en base de données et envoi d'e-mails. Nous simulerons la base de données et l'expéditeur pour isoler le contrôleur.
// tests/UserControllerDICTest.php
use PHPUnit\Framework\TestCase;
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('User registered', $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('Should not be called');
}
};
$mockMailer = new class implements MailerInterface {
public $sentEmail = null;
public function sendWelcome($email): bool {
throw new Exception('Should not be called');
}
};
$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('Invalid email', $result['message']);
}
}
Points clés :
- Nous simulons
PdoWrapper
etMailerInterface
pour éviter les appels réels à la base de données ou aux e-mails. - Les tests vérifient le comportement : les e-mails valides déclenchent des insertions en base de données et des envois d'e-mails ; les e-mails invalides sautent les deux.
- Simulez les dépendances tierces (par exemple,
PdoWrapper
,MailerInterface
), en laissant la logique du contrôleur s'exécuter.
Simulation excessive
Faites attention à ne pas simuler trop de votre code. Laissez-moi vous donner un exemple ci-dessous sur pourquoi cela pourrait être une mauvaise chose en utilisant notre UserController
. Nous changerons cette vérification en une méthode appelée isEmailValid
(en utilisant filter_var
) et les autres ajouts en une méthode séparée appelée 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' => 'Invalid email']);
}
$this->registerUser($email);
$this->app->json(['status' => 'success', 'message' => 'User registered']);
}
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);
}
}
Et maintenant le test unitaire sursimulé qui ne teste en réalité rien :
use PHPUnit\Framework\TestCase;
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('User registered', $result['message']);
}
}
Hourra, nous avons des tests unitaires et ils passent ! Mais attendez, que se passe-t-il si je change réellement le fonctionnement interne de isEmailValid
ou registerUser
? Mes tests passeront toujours parce que j'ai simulé toute la fonctionnalité. Laissez-moi vous montrer ce que je veux dire.
// 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;
}
}
Si j'exécute mes tests unitaires ci-dessus, ils passent toujours ! Mais parce que je n'ai pas testé pour le comportement (en laissant une partie du code s'exécuter réellement), j'ai potentiellement codé un bogue en attente de se produire en production. Le test devrait être modifié pour prendre en compte le nouveau comportement, et aussi l'opposé quand le comportement n'est pas ce que nous attendons.
Exemple complet
Vous pouvez trouver un exemple complet d'un projet Flight PHP avec des tests unitaires sur GitHub : n0nag0n/flight-unit-tests-guide. Pour une compréhension plus approfondie, voir Tests unitaires et principes SOLID.
Pièges courants
- Sur-simulation : Ne simulez pas chaque dépendance ; laissez une partie de la logique (par exemple, la validation du contrôleur) s'exécuter pour tester un comportement réel. Voir Tests unitaires et principes SOLID.
- État global : L'utilisation intensive de variables PHP globales (par exemple,
$_SESSION
,$_COOKIE
) rend les tests fragiles. Idem pourFlight::
. Refactorez pour passer explicitement les dépendances. - Configuration complexe : Si la configuration des tests est fastidieuse, votre classe peut avoir trop de dépendances ou de responsabilités violant les principes SOLID.
Évolutivité avec les tests unitaires
Les tests unitaires brillent dans les projets plus grands ou lors de la reprise de code après des mois. Ils documentent le comportement et détectent les régressions, vous évitant de réapprendre votre application. Pour les devs solos, testez les chemins critiques (par exemple, inscription utilisateur, traitement des paiements). Pour les équipes, les tests assurent un comportement cohérent à travers les contributions. Voir Pourquoi les frameworks ? pour plus d'informations sur les avantages d'utiliser des frameworks et des tests.
Contribuez vos propres conseils de test au dépôt de documentation Flight PHP !
Écrit par n0nag0n 2025
Guides/blog
Créer un blog simple avec Flight PHP
Ce guide vous accompagne pour créer un blog de base en utilisant le framework Flight PHP. Vous allez configurer un projet, définir des routes, gérer des publications avec JSON et les afficher avec le moteur de templates Latte, tout en mettant en avant la simplicité et la flexibilité de Flight. À la fin, vous aurez un blog fonctionnel avec une page d'accueil, des pages de publication individuelles et un formulaire de création.
Prérequis
- PHP 7.4+ : Installé sur votre système.
- Composer : Pour la gestion des dépendances.
- Éditeur de texte : Tout éditeur comme VS Code ou PHPStorm.
- Connaissances de base en PHP et développement web.
Étape 1 : Configurer votre projet
Commencez par créer un nouveau répertoire de projet et installez Flight via Composer.
-
Créer un répertoire :
mkdir flight-blog cd flight-blog
-
Installer Flight :
composer require flightphp/core
-
Créer un répertoire public : Flight utilise un point d'entrée unique (
index.php
). Créez un dossierpublic/
pour cela :mkdir public
-
Base
index.php
: Créezpublic/index.php
avec une route simple « hello world » :<?php require '../vendor/autoload.php'; Flight::route('/', function () { echo 'Bonjour, Flight !'; }); Flight::start();
-
Exécuter le serveur intégré : Testez votre configuration avec le serveur de développement de PHP :
php -S localhost:8000 -t public/
Visitez
http://localhost:8000
pour voir « Bonjour, Flight ! ».
Étape 2 : Organiser la structure de votre projet
Pour une configuration propre, structurez votre projet comme ceci :
flight-blog/
├── app/
│ ├── config/
│ └── views/
├── data/
├── public/
│ └── index.php
├── vendor/
└── composer.json
app/config/
: Fichiers de configuration (ex. : événements, routes).app/views/
: Templates pour le rendu des pages.data/
: Fichier JSON pour stocker les articles du blog.public/
: Racine web avecindex.php
.
Étape 3 : Installer et configurer Latte
Latte est un moteur de templates léger qui s'intègre bien avec Flight.
-
Installer Latte :
composer require latte/latte
-
Configurer Latte dans Flight : Mettez à jour
public/index.php
pour enregistrer Latte comme moteur de vues :<?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' => 'Mon Blog']); }); Flight::start();
-
Créer un template de mise en page : Dans
app/views/layout.latte
:<!DOCTYPE html> <html> <head> <title>{$title}</title> </head> <body> <header> <h1>Mon Blog</h1> <nav> <a href="/">Accueil</a> | <a href="/create">Créer un Article</a> </nav> </header> <main> {block content}{/block} </main> <footer> <p>© {date('Y')} Blog Flight</p> </footer> </body> </html>
-
Créer un template d'accueil : Dans
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}
Redémarrez le serveur si vous en êtes sorti et visitez
http://localhost:8000
pour voir la page rendue. -
Créer un fichier de données :
Utilisez un fichier JSON pour simuler une base de données pour plus de simplicité.
Dans
data/posts.json
:[ { "slug": "premier-article", "title": "Mon Premier Article", "content": "Ceci est mon tout premier article de blog avec Flight PHP !" } ]
Étape 4 : Définir les routes
Séparez vos routes dans un fichier de configuration pour une meilleure organisation.
-
Créer
routes.php
: Dansapp/config/routes.php
:<?php Flight::route('/', function () { Flight::view()->render('home.latte', ['title' => 'Mon Blog']); }); Flight::route('/post/@slug', function ($slug) { Flight::view()->render('post.latte', ['title' => 'Article : ' . $slug, 'slug' => $slug]); }); Flight::route('GET /create', function () { Flight::view()->render('create.latte', ['title' => 'Créer un Article']); });
-
Mettre à jour
index.php
: Incluez le fichier des routes :<?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();
Étape 5 : Stocker et récupérer des articles de blog
Ajoutez les méthodes pour charger et sauvegarder des articles.
-
Ajouter une méthode Posts : Dans
index.php
, ajoutez une méthode pour charger des articles :Flight::map('posts', function () { $file = __DIR__ . '/../data/posts.json'; return json_decode(file_get_contents($file), true); });
-
Mettre à jour les routes : Modifiez
app/config/routes.php
pour utiliser les articles :<?php Flight::route('/', function () { $posts = Flight::posts(); Flight::view()->render('home.latte', [ 'title' => 'Mon 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' => 'Créer un Article']); });
Étape 6 : Créer des templates
Mettez à jour vos templates pour afficher des articles.
-
Page de l'article (
app/views/post.latte
) :{extends 'layout.latte'} {block content} <h2>{$post['title']}</h2> <div class="post-content"> <p>{$post['content']}</p> </div> {/block}
Étape 7 : Ajouter la création d'articles
Gérez la soumission du formulaire pour ajouter de nouveaux articles.
-
Formulaire de création (
app/views/create.latte
) :{extends 'layout.latte'} {block content} <h2>{$title}</h2> <form method="POST" action="/create"> <div class="form-group"> <label for="title">Titre :</label> <input type="text" name="title" id="title" required> </div> <div class="form-group"> <label for="content">Contenu :</label> <textarea name="content" id="content" required></textarea> </div> <button type="submit">Sauvegarder l'Article</button> </form> {/block}
-
Ajouter une route POST : Dans
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('/'); });
-
Testez-le :
- Visitez
http://localhost:8000/create
. - Soumettez un nouvel article (par exemple, « Deuxième Article » avec un peu de contenu).
- Vérifiez la page d'accueil pour voir qu'il est répertorié.
- Visitez
Étape 8 : Améliorer avec la gestion des erreurs
Surchargez la méthode notFound
pour une meilleure expérience 404.
Dans index.php
:
Flight::map('notFound', function () {
Flight::view()->render('404.latte', ['title' => 'Page Non Trouvée']);
});
Créez app/views/404.latte
:
{extends 'layout.latte'}
{block content}
<h2>404 - {$title}</h2>
<p>Désolé, cette page n'existe pas !</p>
{/block}
Prochaines étapes
- Ajouter du style : Utilisez le CSS dans vos templates pour un meilleur rendu.
- Base de données : Remplacez
posts.json
par une base de données comme SQLite en utilisantPdoWrapper
. - Validation : Ajoutez des vérifications pour les slugs en double ou les entrées vides.
- Middleware : Implémentez l'authentification pour la création d'articles.
Conclusion
Vous avez construit un blog simple avec Flight PHP ! Ce guide démontre des fonctionnalités clés comme le routage, le rendu de templates avec Latte et la gestion des soumissions de formulaires, le tout en gardant les choses légères. Explorez la documentation de Flight pour des fonctionnalités plus avancées afin de faire progresser votre blog !
License
La Licence MIT (MIT)
=====================
Droits d'auteur © `2024` `@mikecao, @n0nag0n`
La permission est accordée, gratuitement, à toute personne
obtenant une copie de ce logiciel et de la documentation associée
(fichiers du “Logiciel”), d'utiliser le Logiciel sans aucune
restriction, y compris, sans s'y limiter, les droits d'utiliser,
de copier, de modifier, de fusionner, de publier, de distribuer,
de concéder sous licence et/ou de vendre des copies du Logiciel,
et de permettre aux personnes auxquelles le Logiciel est fourni
de le faire, sous réserve des conditions suivantes :
L'avis de droit d'auteur ci-dessus et le présent avis de
permission doivent être inclus dans toutes les copies ou les
parties substantielles du Logiciel.
LE LOGICIEL EST FOURNI “TEL QUEL”, SANS GARANTIE D'AUCUNE
SORTES, EXPRESSE OU IMPLICITE, Y COMPRIS, MAIS SANS S'Y LIMITER,
LES GARANTIES DE QUALITÉ MARCHANDE, D'ADÉQUATION À UN USAGE
PARTICULIER ET D'ABSENCE DE CONTREFAÇON. EN AUCUN CAS, LES
AUTEURS OU LES TITULAIRES DES DROITS D'AUTEUR NE SAURAIENT ÊTRE
TENUS RESPONSABLES DE TOUTE RÉCLAMATION, DOMMAGE OU AUTRE
RESPONSABILITÉ, QUE CE SOIT DANS UNE ACTION DE CONTRAT, DE TORT
OU AUTRE, DÉCOULANT DE, HORS DE OU EN RELATION AVEC LE LOGICIEL
OU L'UTILISATION OU D'AUTRES TRANSACTIONS DANS LE LOGICIEL.
About
Cadre PHP Flight
Flight est un framework rapide, simple et extensible pour PHP, conçu pour les développeurs qui veulent accomplir les choses rapidement, sans complications. Que vous construisiez une application web classique, une API ultra-rapide, ou que vous expérimentiez avec les derniers outils alimentés par l'IA, la petite empreinte et la conception directe de Flight en font un choix parfait. Flight est destiné à être léger, mais il peut aussi gérer les exigences d'une architecture d'entreprise.
Pourquoi choisir Flight ?
- Accessible aux débutants : Flight est un excellent point de départ pour les nouveaux développeurs PHP. Sa structure claire et sa syntaxe simple vous aident à apprendre le développement web sans vous perdre dans du code boilerplate.
- Aimé par les professionnels : Les développeurs expérimentés adorent Flight pour sa flexibilité et son contrôle. Vous pouvez passer d'un prototype minuscule à une application complète sans changer de framework.
- Compatible avec l'IA : La surcharge minimale et l'architecture propre de Flight en font un outil idéal pour intégrer des outils et des API d'IA. Que vous construisiez des chatbots intelligents, des tableaux de bord pilotés par l'IA, ou que vous souhaitiez simplement expérimenter, Flight s'efface pour que vous puissiez vous concentrer sur l'essentiel. L'application squelette est fournie avec des fichiers d'instructions pré-construits pour les principaux assistants de codage d'IA dès la sortie de la boîte ! En savoir plus sur l'utilisation de l'IA avec Flight
Aperçu vidéo
Démarrage rapide
Pour une installation rapide et basique, installez-le avec Composer :
composer require flightphp/core
Ou vous pouvez télécharger un zip du dépôt ici. Ensuite, vous aurez un fichier index.php
de base comme suit :
<?php
// si installé avec composer
require 'vendor/autoload.php';
// ou si installé manuellement par fichier zip
// require 'flight/Flight.php';
Flight::route('/', function() {
echo 'hello world!';
});
Flight::route('/json', function() {
Flight::json([
'hello' => 'world'
]);
});
Flight::start();
C'est tout ! Vous avez une application Flight de base. Vous pouvez maintenant exécuter ce fichier avec php -S localhost:8000
et visiter http://localhost:8000
dans votre navigateur pour voir le résultat.
Application squelette/modèle
Il y a un exemple d'application pour vous aider à démarrer votre projet avec Flight. Elle dispose d'une mise en page structurée, de configurations de base toutes prêtes et gère les scripts Composer dès le départ ! Jetez un œil à flightphp/skeleton pour un projet prêt à l'emploi, ou visitez la page exemples pour trouver de l'inspiration. Vous voulez voir comment l'IA s'intègre ? Explorez des exemples alimentés par l'IA.
Installation de l'application squelette
C'est facile !
# Créez le nouveau projet
composer create-project flightphp/skeleton my-project/
# Entrez dans le répertoire de votre nouveau projet
cd my-project/
# Lancez le serveur de développement local pour commencer immédiatement !
composer start
Cela créera la structure du projet, configurera les fichiers dont vous avez besoin, et vous serez prêt à partir !
Performances élevées
Flight est l'un des frameworks PHP les plus rapides du marché. Son cœur léger signifie moins de surcharge et plus de vitesse, parfait pour les applications traditionnelles et les projets modernes alimentés par l'IA. Vous pouvez voir tous les benchmarks sur TechEmpower.
Voici le benchmark ci-dessous avec d'autres frameworks PHP populaires.
Framework | Reqs/sec en texte brut | Reqs/sec en JSON |
---|---|---|
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 et l'IA
Curieux de savoir comment il gère l'IA ? Découvrez comment Flight facilite le travail avec votre LLM de codage préféré !
Communauté
Nous sommes sur Matrix Chat
Et Discord
Contribution
Il y a deux façons de contribuer à Flight :
- Contribuez au framework principal en visitant le dépôt principal.
- Aidez à améliorer les docs ! Ce site de documentation est hébergé sur Github. Si vous repérez une erreur ou souhaitez améliorer quelque chose, n'hésitez pas à soumettre une pull request. Nous adorons les mises à jour et les nouvelles idées, surtout autour de l'IA et des nouvelles technologies !
Exigences
Flight nécessite PHP 7.4 ou une version supérieure.
Note : PHP 7.4 est pris en charge car, au moment de l'écriture (2024), PHP 7.4 est la version par défaut pour certaines distributions Linux LTS. Forcer un passage à PHP >8 causerait beaucoup de problèmes pour ces utilisateurs. Le framework prend aussi en charge PHP >8.
Licence
Flight est publié sous la licence MIT.
Awesome-plugins/php_cookie
Cookies
overclokk/cookie est une bibliothèque simple pour gérer les cookies au sein de votre application.
Installation
L'installation est simple avec composer.
composer require overclokk/cookie
Utilisation
L'utilisation est aussi simple que d'enregistrer une nouvelle méthode sur la classe Flight.
use Overclokk\Cookie\Cookie;
/*
* Définissez dans votre fichier bootstrap ou public/index.php
*/
Flight::register('cookie', Cookie::class);
/**
* ExampleController.php
*/
class ExampleController {
public function login() {
// Définir un cookie
// vous voudrez que ce soit faux pour obtenir une nouvelle instance
// utilisez le commentaire ci-dessous si vous souhaitez l'autocomplétion
/** @var \Overclokk\Cookie\Cookie $cookie */
$cookie = Flight::cookie(false);
$cookie->set(
'stay_logged_in', // nom du cookie
'1', // la valeur que vous souhaitez définir
86400, // nombre de secondes pendant lesquelles le cookie doit durer
'/', // chemin où le cookie sera disponible
'example.com', // domaine où le cookie sera disponible
true, // le cookie ne sera transmis que sur une connexion sécurisée HTTPS
true // le cookie ne sera disponible que via le protocole HTTP
);
// éventuellement, si vous voulez conserver les valeurs par défaut
// et avoir un moyen rapide de définir un cookie pour une longue durée
$cookie->forever('stay_logged_in', '1');
}
public function home() {
// Vérifiez si vous avez le cookie
if (Flight::cookie()->has('stay_logged_in')) {
// placez-les dans la zone du tableau de bord par exemple.
Flight::redirect('/dashboard');
}
}
}
Awesome-plugins/php_encryption
Chiffrement PHP
defuse/php-encryption est une bibliothèque qui peut être utilisée pour chiffrer et déchiffrer des données. Il est assez simple de commencer à chiffrer et déchiffrer des données. Ils ont un didacticiel qui aide à expliquer les bases de l'utilisation de la bibliothèque ainsi que les implications importantes en matière de sécurité concernant le chiffrement.
Installation
L'installation est simple avec Composer.
composer require defuse/php-encryption
Configuration
Ensuite, vous devrez générer une clé de chiffrement.
vendor/bin/generate-defuse-key
Cela affichera une clé que vous devrez conserver en sécurité. Vous pourriez conserver la clé dans votre fichier app/config/config.php
dans le tableau en bas du fichier. Ce n'est pas l'endroit parfait, mais c'est au moins quelque chose.
Utilisation
Maintenant que vous avez la bibliothèque et une clé de chiffrement, vous pouvez commencer à chiffrer et déchiffrer des données.
use Defuse\Crypto\Crypto;
use Defuse\Crypto\Key;
/*
* Défini dans votre fichier bootstrap ou public/index.php
*/
// Méthode de chiffrement
Flight::map('encrypt', function($raw_data) {
$encryption_key = /* $config['encryption_key'] or a file_get_contents of where you put the key */;
return Crypto::encrypt($raw_data, Key::loadFromAsciiSafeString($encryption_key));
});
// Méthode de déchiffrement
Flight::map('decrypt', function($encrypted_data) {
$encryption_key = /* $config['encryption_key'] or a file_get_contents of where you put the key */;
try {
$raw_data = Crypto::decrypt($encrypted_data, Key::loadFromAsciiSafeString($encryption_key));
} catch (Defuse\Crypto\Exception\WrongKeyOrModifiedCiphertextException $ex) {
// Une attaque ! Soit la mauvaise clé a été chargée, soit le texte chiffré a
// changé depuis sa création -- corrompu dans la base de données ou
// intentionnellement modifié par Eve essayant de mener une attaque.
// ... gérer ce cas de manière adaptée à votre application ...
}
return $raw_data;
});
Flight::route('/encrypt', function() {
$encrypted_data = Flight::encrypt('Ceci est un secret');
echo $encrypted_data;
});
Flight::route('/decrypt', function() {
$encrypted_data = '...'; // Obtenir les données chiffrées de quelque part
$decrypted_data = Flight::decrypt($encrypted_data);
echo $decrypted_data;
});
Awesome-plugins/php_file_cache
flightphp/cache
Classe de mise en cache PHP légère, simple et autonome en fichier, dérivée de Wruczek/PHP-File-Cache
Avantages
- Légère, autonome et simple
- Tout le code dans un seul fichier - pas de pilotes inutiles.
- Sécurisée - chaque fichier de cache généré a un en-tête PHP avec die, rendant l'accès direct impossible même si quelqu'un connaît le chemin et que votre serveur n'est pas configuré correctement
- Bien documentée et testée
- Gère la concurrence correctement via flock
- Supporte PHP 7.4+
- Gratuite sous licence MIT
Ce site de documentation utilise cette bibliothèque pour mettre en cache chaque page !
Cliquez ici pour voir le code.
Installation
Installez via composer :
composer require flightphp/cache
Utilisation
L'utilisation est assez simple. Cela enregistre un fichier de cache dans le répertoire de cache.
use flight\Cache;
$app = Flight::app();
// Vous passez le répertoire où le cache sera stocké dans le constructeur
$app->register('cache', Cache::class, [ __DIR__ . '/../cache/' ], function(Cache $cache) {
// Cela garantit que le cache n'est utilisé que en mode production
// ENVIRONMENT est une constante définie dans votre fichier bootstrap ou ailleurs dans votre application
$cache->setDevMode(ENVIRONMENT === 'development');
});
Obtenir une valeur de cache
Vous utilisez la méthode get()
pour obtenir une valeur mise en cache. Si vous voulez une méthode pratique qui rafraîchira le cache s'il est expiré, vous pouvez utiliser refreshIfExpired()
.
// Obtenir l'instance de cache
$cache = Flight::cache();
$data = $cache->refreshIfExpired('simple-cache-test', function () {
return date("H:i:s"); // retourner les données à mettre en cache
}, 10); // 10 secondes
// ou
$data = $cache->get('simple-cache-test');
if(empty($data)) {
$data = date("H:i:s");
$cache->set('simple-cache-test', $data, 10); // 10 secondes
}
Stocker une valeur de cache
Vous utilisez la méthode set()
pour stocker une valeur dans le cache.
Flight::cache()->set('simple-cache-test', 'my cached data', 10); // 10 secondes
Effacer une valeur de cache
Vous utilisez la méthode delete()
pour effacer une valeur dans le cache.
Flight::cache()->delete('simple-cache-test');
Vérifier si une valeur de cache existe
Vous utilisez la méthode exists()
pour vérifier si une valeur existe dans le cache.
if(Flight::cache()->exists('simple-cache-test')) {
// faire quelque chose
}
Vider le cache
Vous utilisez la méthode flush()
pour vider l'ensemble du cache.
Flight::cache()->flush();
Extraire les métadonnées avec le cache
Si vous voulez extraire les horodatages et autres métadonnées sur une entrée de cache, assurez-vous de passer true
comme paramètre correct.
$data = $cache->refreshIfExpired("simple-cache-meta-test", function () {
echo "Refreshing data!" . PHP_EOL;
return date("H:i:s"); // retourner les données à mettre en cache
}, 10, true); // true = retourner avec métadonnées
// ou
$data = $cache->get("simple-cache-meta-test", true); // true = retourner avec métadonnées
/*
Exemple d'élément mis en cache récupéré avec métadonnées :
{
"time":1511667506, <-- horodatage unix de sauvegarde
"expire":10, <-- temps d'expiration en secondes
"data":"04:38:26", <-- données désérialisées
"permanent":false
}
En utilisant les métadonnées, nous pouvons, par exemple, calculer quand l'élément a été sauvegardé ou quand il expire
Nous pouvons aussi accéder aux données elles-mêmes avec la clé "data"
*/
$expiresin = ($data["time"] + $data["expire"]) - time(); // obtenir l'horodatage unix quand les données expirent et soustraire l'horodatage actuel
$cacheddate = $data["data"]; // nous accédons aux données elles-mêmes avec la clé "data"
echo "Dernière sauvegarde de cache : $cacheddate, expire dans $expiresin secondes";
Documentation
Visitez https://github.com/flightphp/cache pour voir le code. Assurez-vous de consulter le dossier examples pour des façons supplémentaires d'utiliser le cache.
Awesome-plugins/permissions
FlightPHP/Autorisations
Il s'agit d'un module d'autorisations qui peut être utilisé dans vos projets si vous avez plusieurs rôles dans votre application et que chaque rôle a une fonctionnalité légèrement différente. Ce module vous permet de définir des autorisations pour chaque rôle, puis de vérifier si l'utilisateur actuel a l'autorisation d'accéder à une certaine page ou d'effectuer une certaine action.
Cliquez ici pour accéder au dépôt sur GitHub.
Installation
Exécutez composer require flightphp/permissions
et c'est parti!
Utilisation
Tout d'abord, vous devez configurer vos autorisations, puis vous indiquez à votre application ce que signifient les autorisations. En fin de compte, vous vérifierez vos autorisations avec $Permissions->has()
, ->can()
, ou is()
. has()
et can()
ont la même fonctionnalité, mais sont nommées différemment pour rendre votre code plus lisible.
Exemple de base
Supposons que vous ayez une fonctionnalité dans votre application qui vérifie si un utilisateur est connecté. Vous pouvez créer un objet d'autorisations comme ceci:
// index.php
require 'vendor/autoload.php';
// some code
// then you probably have something that tells you who the current role is of the person
// likely you have something where you pull the current role
// from a session variable which defines this
// after someone logs in, otherwise they will have a 'guest' or 'public' role.
$current_role = 'admin';
// setup permissions
$permission = new \flight\Permission($current_role);
$permission->defineRule('loggedIn', function($current_role) {
return $current_role !== 'guest';
});
// You'll probably want to persist this object in Flight somewhere
Flight::set('permission', $permission);
Ensuite, dans un contrôleur quelque part, vous pourriez avoir quelque chose comme ceci.
<?php
// some controller
class SomeController {
public function someAction() {
$permission = Flight::get('permission');
if ($permission->has('loggedIn')) {
// do something
} else {
// do something else
}
}
}
Vous pouvez également utiliser ceci pour suivre s'ils ont l'autorisation de faire quelque chose dans votre application. Par exemple, si vous avez un moyen pour les utilisateurs d'interagir avec des publications sur votre logiciel, vous pouvez vérifier s'ils ont l'autorisation d'effectuer certaines actions.
$current_role = 'admin';
// setup permissions
$permission = new \flight\Permission($current_role);
$permission->defineRule('post', function($current_role) {
if($current_role === 'admin') {
$permissions = ['create', 'read', 'update', 'delete'];
} else if($current_role === 'editor') {
$permissions = ['create', 'read', 'update'];
} else if($current_role === 'author') {
$permissions = ['create', 'read'];
} else if($current_role === 'contributor') {
$permissions = ['create'];
} else {
$permissions = [];
}
return $permissions;
});
Flight::set('permission', $permission);
Ensuite, dans un contrôleur quelque part...
class PostController {
public function create() {
$permission = Flight::get('permission');
if ($permission->can('post.create')) {
// do something
} else {
// do something else
}
}
}
Injection de dépendances
Vous pouvez injecter des dépendances dans la fonction de fermeture qui définit les autorisations. C'est utile si vous avez un type de bascule, d'identifiant ou tout autre point de données que vous voulez vérifier. Le même principe s'applique aux appels de type Classe->Méthode, sauf que vous définissez les arguments dans la méthode.
Fonctions de fermeture
$Permission->defineRule('order', function(string $current_role, MyDependency $MyDependency = null) {
// ... code
});
// dans votre fichier de contrôleur
public function createOrder() {
$MyDependency = Flight::myDependency();
$permission = Flight::get('permission');
if ($permission->can('order.create', $MyDependency)) {
// do something
} else {
// do something else
}
}
Classes
namespace MyApp;
class Permissions {
public function order(string $current_role, MyDependency $MyDependency = null) {
// ... code
}
}
Raccourci pour définir des autorisations avec des classes
Vous pouvez également utiliser des classes pour définir vos autorisations. C'est utile si vous avez beaucoup d'autorisations et que vous voulez garder votre code propre. Vous pouvez faire quelque chose comme ceci:
<?php
// code de démarrage
$Permissions = new \flight\Permission($current_role);
$Permissions->defineRule('order', 'MyApp\Permissions->order');
// myapp/Permissions.php
namespace MyApp;
class Permissions {
public function order(string $current_role, int $user_id) {
// En supposant que vous avez configuré cela au préalable
/** @var \flight\database\PdoWrapper $db */
$db = Flight::db();
$allowed_permissions = [ 'read' ]; // tout le monde peut consulter une commande
if($current_role === 'manager') {
$allowed_permissions[] = 'create'; // les gestionnaires peuvent créer des commandes
}
$some_special_toggle_from_db = $db->fetchField('SELECT some_special_toggle FROM settings WHERE id = ?', [ $user_id ]);
if($some_special_toggle_from_db) {
$allowed_permissions[] = 'update'; // si l'utilisateur a une bascule spéciale, il peut mettre à jour des commandes
}
if($current_role === 'admin') {
$allowed_permissions[] = 'delete'; // les administrateurs peuvent supprimer des commandes
}
return $allowed_permissions;
}
}
L'astuce est que vous pouvez également utiliser un raccourci (qui peut également être mis en cache!!!) où vous dites simplement à la classe d'autorisations de mapper toutes les méthodes d'une classe en autorisations. Ainsi, si vous avez une méthode nommée order()
et une méthode nommée company()
, cela sera automatiquement cartographié pour que vous puissiez simplement exécuter $Permissions->has('order.read')
ou $Permissions->has('company.read')
et cela fonctionnera. Définir cela est très difficile, donc suivez moi ici. Vous avez juste besoin de faire ceci:
Créez la classe des autorisations que vous souhaitez regrouper.
class MyPermissions {
public function order(string $current_role, int $order_id = 0): array {
// code pour déterminer les autorisations
return $permissions_array;
}
public function company(string $current_role, int $company_id): array {
// code pour déterminer les autorisations
return $permissions_array;
}
}
Puis rendez les autorisations découvrables en utilisant cette bibliothèque.
$Permissions = new \flight\Permission($current_role);
$Permissions->defineRulesFromClassMethods(MyApp\Permissions::class);
Flight::set('permissions', $Permissions);
Enfin, appelez l'autorisation dans votre code pour vérifier si l'utilisateur est autorisé à effectuer une autorisation donnée.
class SomeController {
public function createOrder() {
if(Flight::get('permissions')->can('order.create') === false) {
die('Vous ne pouvez pas créer une commande. Désolé!');
}
}
}
Mise en cache
Pour activer la mise en cache, consultez la bibliothèque simple wruczak/phpfilecache. Un exemple pour l'activer est ci-dessous.
// cet $app peut faire partie de votre code, ou
// vous pouvez simplement passer null et il
// récupérera de Flight::app() dans le constructeur
$app = Flight::app();
// Pour l'instant, cela accepte cela comme cache de fichier. D'autres peuvent facilement
// être ajoutés à l'avenir.
$Cache = new Wruczek\PhpFileCache\PhpFileCache;
$Permissions = new \flight\Permission($current_role, $app, $Cache);
$Permissions->defineRulesFromClassMethods(MyApp\Permissions::class, 3600); // 3600 est le nombre de secondes pendant lesquels conserver cette mise en cache. Laissez ceci vide pour ne pas utiliser la mise en cache
Awesome-plugins/simple_job_queue
File d'attente de travail simple
La file d'attente de travail simple est une bibliothèque qui peut être utilisée pour traiter des travaux de manière asynchrone. Elle peut être utilisée avec beanstalkd, MySQL/MariaDB, SQLite et PostgreSQL.
Installation
composer require n0nag0n/simple-job-queue
Utilisation
Pour que cela fonctionne, vous devez avoir un moyen d'ajouter des travaux à la file d'attente et un moyen de traiter les travaux (un travailleur). Ci-dessous, des exemples de la façon d'ajouter un travail à la file d'attente et de traiter le travail.
Ajout à Flight
Ajouter cela à Flight est simple et se fait en utilisant la méthode register()
. Ci-dessous un exemple de la façon d'ajouter cela à Flight.
<?php
require 'vendor/autoload.php';
// Changez ['mysql'] en ['beanstalkd'] si vous voulez utiliser beanstalkd
Flight::register('queue', n0nag0n\Job_Queue::class, ['mysql'], function($Job_Queue) {
// si vous avez déjà une connexion PDO sur Flight::db();
$Job_Queue->addQueueConnection(Flight::db());
// ou si vous utilisez beanstalkd/Pheanstalk
$pheanstalk = Pheanstalk\Pheanstalk::create('127.0.0.1');
$Job_Queue->addQueueConnection($pheanstalk);
});
Ajout d'un nouveau travail
Lorsque vous ajoutez un travail, vous devez spécifier un pipeline (file d'attente). Cela est comparable à un canal dans RabbitMQ ou un tube dans beanstalkd.
<?php
Flight::queue()->selectPipeline('send_important_emails');
Flight::queue()->addJob(json_encode([ 'something' => 'that', 'ends' => 'up', 'a' => 'string' ]));
Exécution d'un travailleur
Voici un exemple de fichier sur comment exécuter un travailleur.
<?php
require 'vendor/autoload.php';
$Job_Queue = new n0nag0n\Job_Queue('mysql');
// Connexion PDO
$PDO = new PDO('mysql:dbname=testdb;host=127.0.0.1', 'user', 'pass');
$Job_Queue->addQueueConnection($PDO);
// ou si vous utilisez beanstalkd/Pheanstalk
$pheanstalk = Pheanstalk\Pheanstalk::create('127.0.0.1');
$Job_Queue->addQueueConnection($pheanstalk);
$Job_Queue->watchPipeline('send_important_emails');
while(true) {
$job = $Job_Queue->getNextJobAndReserve();
// ajustez selon ce qui vous fait dormir mieux la nuit (pour les files d'attente de base de données uniquement, beanstalkd n'a pas besoin de cette instruction conditionnelle)
if(empty($job)) {
usleep(500000);
continue;
}
echo "Traitement de {$job['id']}\n";
$payload = json_decode($job['payload'], true);
try {
$result = doSomethingThatDoesSomething($payload);
if($result === true) {
$Job_Queue->deleteJob($job);
} else {
// cela le retire de la file d'attente prête et le met dans une autre file d'attente qui peut être récupérée et "kickée" plus tard.
$Job_Queue->buryJob($job);
}
} catch(Exception $e) {
$Job_Queue->buryJob($job);
}
}
Gestion des processus longs avec Supervisord
Supervisord est un système de contrôle de processus qui garantit que vos processus travailleurs restent en cours d'exécution en continu. Voici un guide plus complet sur la façon de le configurer avec votre travailleur Simple Job Queue :
Installation de Supervisord
# Sur Ubuntu/Debian
sudo apt-get install supervisor
# Sur CentOS/RHEL
sudo yum install supervisor
# Sur macOS avec Homebrew
brew install supervisor
Création d'un script de travailleur
Tout d'abord, enregistrez votre code de travailleur dans un fichier PHP dédié :
<?php
require 'vendor/autoload.php';
$Job_Queue = new n0nag0n\Job_Queue('mysql');
// Connexion PDO
$PDO = new PDO('mysql:dbname=your_database;host=127.0.0.1', 'username', 'password');
$Job_Queue->addQueueConnection($PDO);
// Définir le pipeline à surveiller
$Job_Queue->watchPipeline('send_important_emails');
// Journaliser le début du travailleur
echo date('Y-m-d H:i:s') . " - Travailleur démarré\n";
while(true) {
$job = $Job_Queue->getNextJobAndReserve();
if(empty($job)) {
usleep(500000); // Dormir pendant 0,5 secondes
continue;
}
echo date('Y-m-d H:i:s') . " - Traitement du travail {$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') . " - Travail {$job['id']} terminé avec succès\n";
} else {
$Job_Queue->buryJob($job);
echo date('Y-m-d H:i:s') . " - Travail {$job['id']} échoué, enterré\n";
}
} catch(Exception $e) {
$Job_Queue->buryJob($job);
echo date('Y-m-d H:i:s') . " - Exception lors du traitement du travail {$job['id']}: {$e->getMessage()}\n";
}
}
Configuration de Supervisord
Créez un fichier de configuration pour votre travailleur :
[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
Options de configuration clés :
command
: La commande pour exécuter votre travailleurdirectory
: Répertoire de travail pour le travailleurautostart
: Démarrer automatiquement lorsque supervisord démarreautorestart
: Redémarrer automatiquement si le processus se terminestartretries
: Nombre de fois à réessayer de démarrer s'il échouestderr_logfile
/stdout_logfile
: Emplacements des fichiers journauxuser
: Utilisateur système pour exécuter le processusnumprocs
: Nombre d'instances du travailleur à exécuterprocess_name
: Format de nommage pour plusieurs processus de travailleur
Gestion des travailleurs avec Supervisorctl
Après avoir créé ou modifié la configuration :
# Recharger la configuration du superviseur
sudo supervisorctl reread
sudo supervisorctl update
# Contrôler des processus de travailleur spécifiques
sudo supervisorctl start email_worker:*
sudo supervisorctl stop email_worker:*
sudo supervisorctl restart email_worker:*
sudo supervisorctl status email_worker:*
Exécution de plusieurs pipelines
Pour plusieurs pipelines, créez des fichiers de travailleurs et des configurations distincts :
[program:email_worker]
command=php /path/to/email_worker.php
# ... autres configurations ...
[program:notification_worker]
command=php /path/to/notification_worker.php
# ... autres configurations ...
Surveillance et journaux
Vérifiez les journaux pour surveiller l'activité du travailleur :
# Voir les journaux
sudo tail -f /var/log/simple_job_queue.log
# Vérifier le statut
sudo supervisorctl status
Cette configuration garantit que vos travailleurs de tâches continuent d'exécuter même après des plantages, des redémarrages de serveur ou d'autres problèmes, rendant votre système de file d'attente fiable pour les environnements de production.
Awesome-plugins/n0nag0n_wordpress
Intégration WordPress : n0nag0n/wordpress-integration-for-flight-framework
Voulez-vous utiliser Flight PHP dans votre site WordPress ? Ce plugin rend cela très simple ! Avec n0nag0n/wordpress-integration-for-flight-framework
, vous pouvez exécuter une application Flight complète directement aux côtés de votre installation WordPress—parfait pour créer des API personnalisées, des microservices ou même des applications complètes sans quitter l'environnement WordPress.
Que fait-il ?
- Intègre de manière transparente Flight PHP avec WordPress
- Redirige les requêtes vers Flight ou WordPress en fonction des motifs d'URL
- Organisez votre code avec des contrôleurs, des modèles et des vues (MVC)
- Configurez facilement la structure de dossiers recommandée de Flight
- Utilisez la connexion à la base de données de WordPress ou la vôtre
- Ajustez finement l'interaction entre Flight et WordPress
- Interface d'administration simple pour la configuration
Installation
- Téléchargez le dossier
flight-integration
dans votre répertoire/wp-content/plugins/
. - Activez le plugin dans l'administration WordPress (menu Plugins).
- Allez dans Paramètres > Flight Framework pour configurer le plugin.
- Définissez le chemin du vendeur vers votre installation Flight (ou utilisez Composer pour installer Flight).
- Configurez le chemin de votre dossier d'application et créez la structure de dossiers (le plugin peut vous aider à cela !).
- Commencez à construire votre application Flight !
Exemples d'utilisation
Exemple de route basique
Dans votre fichier app/config/routes.php
:
Flight::route('GET /api/hello', function() {
Flight::json(['message' => 'Hello World!']);
});
Exemple de contrôleur
Créez un contrôleur dans app/controllers/ApiController.php
:
namespace app\controllers;
use Flight;
class ApiController {
public function getUsers() {
// Vous pouvez utiliser les fonctions WordPress à l'intérieur de Flight !
$users = get_users();
$result = [];
foreach($users as $user) {
$result[] = [
'id' => $user->ID,
'name' => $user->display_name,
'email' => $user->user_email
];
}
Flight::json($result);
}
}
Puis, dans votre routes.php
:
Flight::route('GET /api/users', [app\controllers\ApiController::class, 'getUsers']);
FAQ
Q: Dois-je connaître Flight pour utiliser ce plugin ?
A: Oui, ceci s'adresse aux développeurs qui souhaitent utiliser Flight au sein de WordPress. Une connaissance de base du routage et de la gestion des requêtes de Flight est recommandée.
Q: Cela va-t-il ralentir mon site WordPress ?
A: Non ! Le plugin ne traite que les requêtes qui correspondent à vos routes Flight. Toutes les autres requêtes vont à WordPress comme d'habitude.
Q: Puis-je utiliser les fonctions WordPress dans mon application Flight ?
A: Absolument ! Vous avez un accès complet à toutes les fonctions, hooks et variables globales de WordPress depuis vos routes et contrôleurs Flight.
Q: Comment créer des routes personnalisées ?
A: Définissez vos routes dans le fichier config/routes.php
de votre dossier d'application. Consultez le fichier exemple créé par le générateur de structure de dossiers pour des exemples.
Journal des modifications
1.0.0
Version initiale.
Pour plus d'informations, consultez le GitHub repo.
Awesome-plugins/ghost_session
Ghostff/Session
Gestionnaire de sessions PHP (non-bloquant, flash, segment, chiffrement de session). Utilise PHP open_ssl pour le chiffrement/déchiffrement optionnel des données de session. Prend en charge File, MySQL, Redis et Memcached.
Cliquez ici pour voir le code.
Installation
Installez avec composer.
composer require ghostff/session
Configuration de base
Vous n'êtes pas obligé de passer quoi que ce soit pour utiliser les paramètres par défaut avec votre session. Vous pouvez en lire plus sur les paramètres dans le Github Readme.
use Ghostff\Session\Session;
require 'vendor/autoload.php';
$app = Flight::app();
$app->register('session', Session::class);
// une chose à retenir est que vous devez valider votre session à chaque chargement de page
// ou vous devrez exécuter auto_commit dans votre configuration.
Exemple simple
Voici un exemple simple de la façon dont vous pourriez utiliser cela.
Flight::route('POST /login', function() {
$session = Flight::session();
// effectuez votre logique de connexion ici
// valider le mot de passe, etc.
// si la connexion est réussie
$session->set('is_logged_in', true);
$session->set('user', $user);
// à tout moment que vous écrivez dans la session, vous devez la valider délibérément.
$session->commit();
});
// Cette vérification pourrait se faire dans la logique de la page restreinte, ou être enveloppée dans un middleware.
Flight::route('/some-restricted-page', function() {
$session = Flight::session();
if(!$session->get('is_logged_in')) {
Flight::redirect('/login');
}
// effectuez votre logique de page restreinte ici
});
// la version middleware
Flight::route('/some-restricted-page', function() {
// logique de page régulière
})->addMiddleware(function() {
$session = Flight::session();
if(!$session->get('is_logged_in')) {
Flight::redirect('/login');
}
});
Exemple plus complexe
Voici un exemple plus complexe de la façon dont vous pourriez utiliser cela.
use Ghostff\Session\Session;
require 'vendor/autoload.php';
$app = Flight::app();
// définissez un chemin personnalisé vers votre fichier de configuration de session en tant que premier argument
// ou donnez-lui le tableau personnalisé
$app->register('session', Session::class, [
[
// si vous voulez stocker vos données de session dans une base de données (utile pour quelque chose comme, "me déconnecter de tous les appareils" fonctionnalité)
Session::CONFIG_DRIVER => Ghostff\Session\Drivers\MySql::class,
Session::CONFIG_ENCRYPT_DATA => true,
Session::CONFIG_SALT_KEY => hash('sha256', 'my-super-S3CR3T-salt'), // veuillez changer cela pour quelque chose d'autre
Session::CONFIG_AUTO_COMMIT => true, // ne le faites que si c'est nécessaire et/ou si c'est difficile de faire commit() sur votre session.
// de plus, vous pourriez faire Flight::after('start', function() { Flight::session()->commit(); });
Session::CONFIG_MYSQL_DS => [
'driver' => 'mysql', # Pilote de base de données pour PDO dns ex.(mysql:host=...;dbname=...)
'host' => '127.0.0.1', # Hôte de la base de données
'db_name' => 'my_app_database', # Nom de la base de données
'db_table' => 'sessions', # Table de la base de données
'db_user' => 'root', # Nom d'utilisateur de la base de données
'db_pass' => '', # Mot de passe de la base de données
'persistent_conn'=> false, # Éviter le surcoût d'établir une nouvelle connexion à chaque fois qu'un script doit communiquer avec une base de données, ce qui accélère l'application web. TROUVEZ LE DESSUS VOUS-MÊME
]
]
]);
Aide ! Mes données de session ne persistent pas !
Vous configurez vos données de session et elles ne persistent pas entre les requêtes ? Vous avez peut-être oublié de valider vos données de session. Vous pouvez le faire en appelant $session->commit()
après avoir défini vos données de session.
Flight::route('POST /login', function() {
$session = Flight::session();
// effectuez votre logique de connexion ici
// valider le mot de passe, etc.
// si la connexion est réussie
$session->set('is_logged_in', true);
$session->set('user', $user);
// à tout moment que vous écrivez dans la session, vous devez la valider délibérément.
$session->commit();
});
L'autre moyen d'y remédier est, lorsque vous configurez votre service de session, de définir auto_commit
sur true
dans votre configuration. Cela validera automatiquement vos données de session après chaque requête.
$app->register('session', Session::class, [ 'path/to/session_config.php', bin2hex(random_bytes(32)) ], function(Session $session) {
$session->updateConfiguration([
Session::CONFIG_AUTO_COMMIT => true,
]);
}
);
De plus, vous pourriez faire Flight::after('start', function() { Flight::session()->commit(); });
pour valider vos données de session après chaque requête.
Documentation
Visitez le Github Readme pour la documentation complète. Les options de configuration sont bien documentées dans le fichier default_config.php lui-même. Le code est simple à comprendre si vous vouliez parcourir ce package vous-même.
Awesome-plugins/async
Async
Async est un petit package pour le framework Flight qui vous permet d'exécuter vos applications Flight dans des serveurs et des runtimes asynchrones comme Swoole, AdapterMan, ReactPHP, Amp, RoadRunner, Workerman, etc. Par défaut, il inclut des adaptateurs pour Swoole et AdapterMan.
L'objectif : développer et déboguer avec PHP-FPM (ou le serveur intégré) et passer à Swoole (ou un autre pilote asynchrone) pour la production avec des changements minimaux.
Exigences
- PHP 7.4 ou supérieur
- Framework Flight 3.16.1 ou supérieur
- Extension Swoole
Installation
Installez via Composer :
composer require flightphp/async
Si vous prévoyez d'exécuter avec Swoole, installez l'extension :
# en utilisant pecl
pecl install swoole
# ou openswoole
pecl install openswoole
# ou avec un gestionnaire de paquets (exemple Debian/Ubuntu)
sudo apt-get install php-swoole
Exemple rapide avec Swoole
Voici ci-dessous une configuration minimale qui montre comment supporter à la fois PHP-FPM (ou le serveur intégré) et Swoole en utilisant le même code source.
Fichiers dont vous aurez besoin dans votre projet :
- index.php
- swoole_server.php
- SwooleServerDriver.php
index.php
Ce fichier est un simple interrupteur qui force l'application à s'exécuter en mode PHP pour le développement.
// index.php
<?php
define('NOT_SWOOLE', true);
include 'swoole_server.php';
swoole_server.php
Ce fichier initialise votre application Flight et démarrera le pilote Swoole lorsque NOT_SWOOLE n'est pas défini.
// 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
Un pilote concis montrant comment relier les requêtes Swoole à Flight en utilisant AsyncBridge et les adaptateurs Swoole.
// 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();
}
}
Exécution du serveur
- Développement (serveur intégré PHP / PHP-FPM) :
- php -S localhost:8000 (ou ajoutez -t public/ si votre index est dans public/)
- Production (Swoole) :
- php swoole_server.php
Astuce : Pour la production, utilisez un proxy inverse (Nginx) devant Swoole pour gérer TLS, les fichiers statiques et l'équilibrage de charge.
Notes de configuration
Le pilote Swoole expose plusieurs options de configuration :
- worker_num : nombre de processus workers
- max_request : requêtes par worker avant redémarrage
- enable_coroutine : utilisation des coroutines pour la concurrence
- buffer_output_size : taille du tampon de sortie
Ajustez ces paramètres en fonction des ressources de votre hôte et des schémas de trafic.
Gestion des erreurs
AsyncBridge traduit les erreurs Flight en réponses HTTP appropriées. Vous pouvez également ajouter une gestion d'erreurs au niveau des routes :
$app->route('/*', function() use ($app) {
try {
// route logic
} catch (Exception $e) {
$app->response()->status(500);
$app->json(['error' => $e->getMessage()]);
}
});
AdapterMan et autres runtimes
AdapterMan est supporté en tant qu'adaptateur de runtime alternatif. Le package est conçu pour être adaptable — ajouter ou utiliser d'autres adaptateurs suit généralement le même schéma : convertir la requête/réponse du serveur en requête/réponse Flight via AsyncBridge et les adaptateurs spécifiques au runtime.
Awesome-plugins/migrations
Migrations
Une migration pour votre projet suit toutes les modifications de la base de données impliquées dans votre projet. byjg/php-migration est une bibliothèque centrale très utile pour vous aider à démarrer.
Installing
PHP Library
Si vous souhaitez utiliser uniquement la bibliothèque PHP dans votre projet :
composer require "byjg/migration"
Command Line Interface
L'interface de ligne de commande est autonome et ne nécessite pas d'installation avec votre projet.
Vous pouvez l'installer globalement et créer un lien symbolique
composer require "byjg/migration-cli"
Veuillez visiter byjg/migration-cli pour obtenir plus d'informations sur Migration CLI.
Bases de données prises en charge
Base de données | Pilote | Chaîne de connexion |
---|---|---|
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 |
Comment ça fonctionne ?
La migration de base de données utilise le SQL PUR pour gérer la version de la base de données. Pour faire fonctionner, vous devez :
- Créer les scripts SQL
- Gérer en utilisant la ligne de commande ou l'API.
Les scripts SQL
Les scripts sont divisés en trois ensemble de scripts :
- Le script BASE contient TOUS les commandes SQL pour créer une base de données fraîche ;
- Les scripts UP contiennent toutes les commandes de migration SQL pour "augmenter" la version de la base de données ;
- Les scripts DOWN contiennent toutes les commandes de migration SQL pour "reculer" ou revenir à la version de la base de données ;
Le répertoire des scripts est :
<root dir>
|
+-- base.sql
|
+-- /migrations
|
+-- /up
|
+-- 00001.sql
+-- 00002.sql
+-- /down
|
+-- 00000.sql
+-- 00001.sql
- "base.sql" est le script de base
- Le dossier "up" contient les scripts pour migrer vers la version supérieure. Par exemple : 00002.sql est le script pour faire passer la base de données de la version '1' à '2'.
- Le dossier "down" contient les scripts pour migrer vers la version inférieure. Par exemple : 00001.sql est le script pour faire passer la base de données de la version '2' à '1'. Le dossier "down" est optionnel.
Environnement de développement multi
Si vous travaillez avec plusieurs développeurs et plusieurs branches, il est trop difficile de déterminer quel est le prochain numéro.
Dans ce cas, vous avez le suffixe "-dev" après le numéro de version.
Voyez le scénario :
- Le développeur 1 crée une branche et la version la plus récente est par exemple 42.
- Le développeur 2 crée une branche en même temps et a le même numéro de version de base de données.
Dans les deux cas, les développeurs créeront un fichier appelé 43-dev.sql. Les deux développeurs migreront VERS LE HAUT et VERS LE BAS sans problème et votre version locale sera 43.
Mais le développeur 1 a fusionné vos modifications et créé une version finale 43.sql (git mv 43-dev.sql 43.sql
). Si le développeur 2 met à jour sa branche locale, il aura un fichier 43.sql (du dev 1) et son fichier 43-dev.sql.
S'il essaie de migrer VERS LE HAUT ou VERS LE BAS, le script de migration ne pourra pas se faire et l'alertera qu'il y a DEUX versions 43. Dans ce cas, le développeur 2 devra mettre à jour son fichier pour faire 44-dev.sql et continuer à travailler jusqu'à ce que les modifications soient fusionnées et génèrent une version finale.
Utiliser l'API PHP et l'intégrer dans vos projets
L'utilisation de base est
- Créer une connexion à un objet ConnectionManagement. Pour plus d'informations, voir le composant "byjg/anydataset"
- Créer un objet Migration avec cette connexion et le dossier où les scripts SQL sont situés.
- Utiliser la commande appropriée pour "réinitialiser", "augmenter" ou "reculer" les scripts de migration.
Voici un exemple :
<?php
// Créer l'URI de connexion
// Voir plus : https://github.com/byjg/anydataset#connection-based-on-uri
$connectionUri = new \ByJG\Util\Uri('mysql://migrateuser:migratepwd@localhost/migratedatabase');
// Enregistrer les bases de données ou les bases de données qui peuvent gérer cette URI :
\ByJG\DbMigration\Migration::registerDatabase(\ByJG\DbMigration\Database\MySqlDatabase::class);
// Créer l'instance Migration
$migration = new \ByJG\DbMigration\Migration($connectionUri, '.');
// Ajouter une fonction de rappel de progression pour recevoir des informations sur l'exécution
$migration->addCallbackProgress(function ($action, $currentVersion, $fileInfo) {
echo "$action, $currentVersion, ${fileInfo['description']}\n";
});
// Restaurer la base de données en utilisant le script "base.sql"
// et exécuter TOUS les scripts existants pour augmenter la version de la base de données à la dernière version
$migration->reset();
// Exécuter TOUS les scripts existants pour augmenter ou diminuer la version de la base de données
// depuis la version actuelle jusqu'au numéro de version ;
// Si le numéro de version n'est pas spécifié, effectuez une migration jusqu'à la dernière version de base de données
$migration->update($version = null);
L'objet Migration contrôle la version de la base de données.
Création d'un contrôle de version dans votre projet
<?php
// Enregistrer les bases de données ou les bases de données qui peuvent gérer cette URI :
\ByJG\DbMigration\Migration::registerDatabase(\ByJG\DbMigration\Database\MySqlDatabase::class);
// Créer l'instance Migration
$migration = new \ByJG\DbMigration\Migration($connectionUri, '.');
// Cette commande va créer la table de version dans votre base de données
$migration->createVersion();
Obtenir la version actuelle
<?php
$migration->getCurrentVersion();
Ajouter un rappel pour contrôler la progression
<?php
$migration->addCallbackProgress(function ($command, $version, $fileInfo) {
echo "Exécution de la commande : $command à la version $version - ${fileInfo['description']}, ${fileInfo['exists']}, ${fileInfo['file']}, ${fileInfo['checksum']}\n";
});
Obtenir l'instance du pilote de la base de données
<?php
$migration->getDbDriver();
Pour l'utiliser, veuillez visiter : https://github.com/byjg/anydataset-db
Éviter la migration partielle (non disponible pour MySQL)
Une migration partielle se produit lorsque le script de migration est interrompu au milieu du processus en raison d'une erreur ou d'une interruption manuelle.
La table de migration sera avec le statut partiel en cours
ou partiel en bas
et doit être corrigée manuellement avant de pouvoir migrer à nouveau.
Pour éviter cette situation, vous pouvez spécifier que la migration sera exécutée dans un contexte transactionnel.
Si le script de migration échoue, la transaction sera annulée et la table de migration sera marquée comme complète
et
la version sera la version immédiatement précédente avant le script qui a causé l'erreur.
Pour activer cette fonctionnalité, vous devez appeler la méthode withTransactionEnabled
en passant true
comme paramètre :
<?php
$migration->withTransactionEnabled(true);
REMARQUE : Cette fonctionnalité n'est pas disponible pour MySQL car elle ne prend pas en charge les commandes DDL à l'intérieur d'une transaction. Si vous utilisez cette méthode avec MySQL, la migration l'ignorera silencieusement. Plus d'infos : https://dev.mysql.com/doc/refman/8.0/en/cannot-roll-back.html
Conseils pour écrire des migrations SQL pour Postgres
À propos de la création de déclencheurs et de fonctions SQL
-- DO
CREATE FUNCTION emp_stamp() RETURNS trigger AS $emp_stamp$
BEGIN
-- Vérifier que empname et salary sont fournis
IF NEW.empname IS NULL THEN
RAISE EXCEPTION 'empname ne peut pas être nul'; -- peu importe que ces commentaires soient vides ou non
END IF; --
IF NEW.salary IS NULL THEN
RAISE EXCEPTION '% ne peut pas avoir de salaire nul', NEW.empname; --
END IF; --
-- Qui travaille pour nous quand ils doivent le payer ?
IF NEW.salary < 0 THEN
RAISE EXCEPTION '% ne peut pas avoir un salaire négatif', NEW.empname; --
END IF; --
-- Se souvenir de qui a changé la paie quand
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
-- Vérifier que empname et salary sont fournis
IF NEW.empname IS NULL THEN
RAISE EXCEPTION 'empname ne peut pas être nul';
END IF;
IF NEW.salary IS NULL THEN
RAISE EXCEPTION '% ne peut pas avoir de salaire nul', NEW.empname;
END IF;
-- Qui travaille pour nous quand ils doivent le payer ?
IF NEW.salary < 0 THEN
RAISE EXCEPTION '% ne peut pas avoir un salaire négatif', NEW.empname;
END IF;
-- Se souvenir de qui a changé la paie quand
NEW.last_date := current_timestamp;
NEW.last_user := current_user;
RETURN NEW;
END;
$emp_stamp$ LANGUAGE plpgsql;
Puisque le niveau d'abstraction de base de données PDO
ne peut pas exécuter des lots d'instructions SQL,
lorsque byjg/migration
lit un fichier de migration, il doit diviser tout le contenu du fichier SQL aux points-virgules et exécuter les instructions une par une. Cependant, il existe un type d'instruction qui peut avoir plusieurs points-virgules dans son corps : les fonctions.
Afin de pouvoir analyser les fonctions correctement, byjg/migration
2.1.0 a commencé à diviser les fichiers de migration à la séquence point-virgule + EOL
au lieu de simplement le point-virgule. De cette façon, si vous ajoutez un commentaire vide après chaque point-virgule interne d'une définition de fonction, byjg/migration
pourra l'analyser.
Malheureusement, si vous oubliez d'ajouter l'un de ces commentaires, la bibliothèque divisera l'instruction CREATE FUNCTION
en plusieurs parties et la migration échouera.
Éviter le caractère deux-points (:
)
-- 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
);
Puisque PDO
utilise le caractère deux-points pour préfixer les paramètres nommés dans les instructions préparées, son utilisation le perturbera dans d'autres contextes.
Par exemple, les instructions PostgreSQL peuvent utiliser ::
pour convertir des valeurs entre types. D'autre part, PDO
lira cela comme un paramètre nommé invalide dans un contexte invalide et échouera lorsqu'il essaiera de l'exécuter.
Le seul moyen de corriger cette incohérence est d'éviter complètement l'utilisation de double points (dans ce cas, PostgreSQL à également une syntaxe alternative : CAST(value AS type)
).
Utiliser un éditeur SQL
Enfin, écrire des migrations SQL manuelles peut être épuisant, mais c'est beaucoup plus facile si vous utilisez un éditeur capable de comprendre la syntaxe SQL, d'offrir la saisie semi-automatique, d'explorer votre schéma de base de données actuel et/ou de reformater automatiquement votre code.
Gérer différentes migrations dans un seul schéma
Si vous devez créer différents scripts de migration et des versions dans le même schéma, cela est possible mais trop risqué et je ne recommande pas du tout.
Pour ce faire, vous devez créer différentes "tables de migration" en passant le paramètre au constructeur.
<?php
$migration = new \ByJG\DbMigration\Migration("db:/uri", "/path", true, "NEW_MIGRATION_TABLE_NAME");
Pour des raisons de sécurité, cette fonctionnalité n'est pas disponible en ligne de commande, mais vous pouvez utiliser la variable d'environnement
MIGRATION_VERSION
pour stocker le nom.
Nous recommandons vraiment de ne pas utiliser cette fonctionnalité. La recommandation est une migration pour un schéma.
Exécution des tests unitaires
Des tests unitaires de base peuvent être exécutés par :
vendor/bin/phpunit
Exécution des tests de base de données
Exécuter des tests d'intégration nécessite que vous ayez les bases de données en marche. Nous avons fourni un docker-compose.yml
de base et vous
pouvez l'utiliser pour démarrer les bases de données pour les tests.
Exécution des bases de données
docker-compose up -d postgres mysql mssql
Exécuter les tests
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*
En option, vous pouvez définir l'hôte et le mot de passe utilisés par les tests unitaires
export MYSQL_TEST_HOST=localhost # par défaut localhost
export MYSQL_PASSWORD=newpassword # utilisez '.' si vous souhaitez avoir un mot de passe nul
export PSQL_TEST_HOST=localhost # par défaut localhost
export PSQL_PASSWORD=newpassword # utilisez '.' si vous souhaitez avoir un mot de passe nul
export MSSQL_TEST_HOST=localhost # par défaut localhost
export MSSQL_PASSWORD=Pa55word
export SQLITE_TEST_HOST=/tmp/test.db # par défaut /tmp/test.db
Awesome-plugins/session
FlightPHP Session - Gestionnaire de sessions léger basé sur des fichiers
Ceci est un gestionnaire de sessions léger basé sur des fichiers, plugin pour le Flight PHP Framework. Il fournit une solution simple mais puissante pour gérer les sessions, avec des fonctionnalités comme des lectures de sessions non bloquantes, un chiffrement optionnel, une fonctionnalité d'auto-commit et un mode test pour le développement. Les données de session sont stockées dans des fichiers, ce qui en fait un choix idéal pour les applications qui n'ont pas besoin d'une base de données.
Si vous souhaitez utiliser une base de données, consultez le plugin ghostff/session qui propose de nombreuses fonctionnalités similaires mais avec un backend de base de données.
Visitez le dépôt Github pour le code source complet et les détails.
Installation
Installez le plugin via Composer :
composer require flightphp/session
Utilisation de base
Voici un exemple simple de l'utilisation du plugin flightphp/session
dans votre application Flight :
require 'vendor/autoload.php';
use flight\Session;
$app = Flight::app();
// Enregistrer le service de session
$app->register('session', Session::class);
// Exemple de route avec utilisation de session
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'); // Affiche : johndoe
echo $session->get('preferences', 'default_theme'); // Affiche : default_theme
if ($session->get('user_id')) {
Flight::json(['message' => 'L\'utilisateur est connecté !', 'user_id' => $session->get('user_id')]);
}
});
Flight::route('/logout', function() {
$session = Flight::session();
$session->clear(); // Effacer toutes les données de session
Flight::json(['message' => 'Déconnexion réussie']);
});
Flight::start();
Points clés
- Non-Blocking : Utilise
read_and_close
pour le démarrage de session par défaut, évitant les problèmes de verrouillage de session. - Auto-Commit : Activé par défaut, les modifications sont enregistrées automatiquement à l'arrêt, sauf si désactivé.
- Stockage de fichiers : Les sessions sont stockées dans le répertoire temporaire du système sous
/flight_sessions
par défaut.
Configuration
Vous pouvez personnaliser le gestionnaire de sessions en passant un tableau d'options lors de l'enregistrement :
// Oui, c'est un double tableau :)
$app->register('session', Session::class, [ [
'save_path' => '/custom/path/to/sessions', // Répertoire pour les fichiers de session
'prefix' => 'myapp_', // Préfixe pour les fichiers de session
'encryption_key' => 'a-secure-32-byte-key-here', // Activer le chiffrement (32 octets recommandés pour AES-256-CBC)
'auto_commit' => false, // Désactiver l'auto-commit pour un contrôle manuel
'start_session' => true, // Démarrer la session automatiquement (par défaut : true)
'test_mode' => false, // Activer le mode test pour le développement
'serialization' => 'json', // Méthode de sérialisation : 'json' (par défaut) ou 'php' (léguée)
] ]);
Options de configuration
Option | Description | Valeur par défaut |
---|---|---|
save_path |
Répertoire où les fichiers de session sont stockés | sys_get_temp_dir() . '/flight_sessions' |
prefix |
Préfixe pour le fichier de session enregistré | sess_ |
encryption_key |
Clé pour le chiffrement AES-256-CBC (optionnel) | null (pas de chiffrement) |
auto_commit |
Enregistrement automatique des données de session à l'arrêt | true |
start_session |
Démarrer la session automatiquement | true |
test_mode |
Exécuter en mode test sans affecter les sessions PHP | false |
test_session_id |
ID de session personnalisé pour le mode test (optionnel) | Généré aléatoirement s'il n'est pas défini |
serialization |
Méthode de sérialisation : 'json' (par défaut, sécurisé) ou 'php' (léguée, permet les objets) | 'json' |
Modes de sérialisation
Par défaut, cette bibliothèque utilise la sérialisation JSON pour les données de session, ce qui est sécurisé et empêche les vulnérabilités d'injection d'objets PHP. Si vous devez stocker des objets PHP dans la session (non recommandé pour la plupart des applications), vous pouvez choisir la sérialisation PHP léguée :
'serialization' => 'json'
(par défaut) :- Seuls les tableaux et les primitives sont autorisés dans les données de session.
- Plus sécurisé : immunisé contre l'injection d'objets PHP.
- Les fichiers sont préfixés avec
J
(JSON pur) ouF
(JSON chiffré).
'serialization' => 'php'
:- Permet de stocker des objets PHP (à utiliser avec précaution).
- Les fichiers sont préfixés avec
P
(sérialisation PHP pure) ouE
(sérialisation PHP chiffrée).
Note : Si vous utilisez la sérialisation JSON, tenter de stocker un objet lèvera une exception.
Utilisation avancée
Commit manuel
Si vous désactivez l'auto-commit, vous devez manuellement enregistrer les modifications :
$app->register('session', Session::class, ['auto_commit' => false]);
Flight::route('/update', function() {
$session = Flight::session();
$session->set('key', 'value');
$session->commit(); // Enregistrer explicitement les modifications
});
Sécurité des sessions avec chiffrement
Activez le chiffrement pour les données sensibles :
$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'); // Chiffré automatiquement
echo $session->get('credit_card'); // Déchiffré lors de la récupération
});
Régénération de session
Régénérez l'ID de session pour des raisons de sécurité (par exemple, après la connexion) :
Flight::route('/post-login', function() {
$session = Flight::session();
$session->regenerate(); // Nouvel ID, conserver les données
// OU
$session->regenerate(true); // Nouvel ID, supprimer les anciennes données
});
Exemple de middleware
Protégez les routes avec une authentification basée sur les sessions :
Flight::route('/admin', function() {
Flight::json(['message' => 'Bienvenue sur le panneau d\'administration']);
})->addMiddleware(function() {
$session = Flight::session();
if (!$session->get('is_admin')) {
Flight::halt(403, 'Accès refusé');
}
});
Ceci est juste un exemple simple de l'utilisation dans un middleware. Pour un exemple plus approfondi, consultez la documentation sur le middleware.
Méthodes
La classe Session
fournit ces méthodes :
set(string $key, $value)
: Stocke une valeur dans la session.get(string $key, $default = null)
: Récupère une valeur, avec une valeur par défaut optionnelle si la clé n'existe pas.delete(string $key)
: Supprime une clé spécifique de la session.clear()
: Supprime toutes les données de session, mais conserve le même nom de fichier pour la session.commit()
: Enregistre les données de session actuelles sur le système de fichiers.id()
: Retourne l'ID de session actuel.regenerate(bool $deleteOldFile = false)
: Régénère l'ID de session, y compris la création d'un nouveau fichier de session, en conservant toutes les anciennes données et le ancien fichier reste sur le système. Si$deleteOldFile
esttrue
, l'ancien fichier de session est supprimé.destroy(string $id)
: Détruit une session par ID et supprime le fichier de session du système. Ceci fait partie de l'interfaceSessionHandlerInterface
et$id
est requis. L'utilisation typique serait$session->destroy($session->id())
.getAll()
: Retourne toutes les données de la session actuelle.
Toutes les méthodes sauf get()
et id()
renvoient l'instance Session
pour la chaîne.
Pourquoi utiliser ce plugin ?
- Lightweight : Pas de dépendances externes – juste des fichiers.
- Non-Blocking : Évite le verrouillage de session avec
read_and_close
par défaut. - Secure : Prend en charge le chiffrement AES-256-CBC pour les données sensibles.
- Flexible : Options d'auto-commit, mode test et contrôle manuel.
- Flight-Native : Conçu spécifiquement pour le framework Flight.
Détails techniques
- Format de stockage : Les fichiers de session sont préfixés avec
sess_
et stockés dans lesave_path
configuré. Les préfixes de contenu de fichier :J
: JSON pur (par défaut, sans chiffrement)F
: JSON chiffré (par défaut avec chiffrement)P
: Sérialisation PHP pure (léguée, sans chiffrement)E
: Sérialisation PHP chiffrée (léguée avec chiffrement)
- Chiffrement : Utilise AES-256-CBC avec un IV aléatoire par écriture de session lorsqu'une
encryption_key
est fournie. Le chiffrement fonctionne pour les modes de sérialisation JSON et PHP. - Sérialisation : JSON est la méthode par défaut et la plus sécurisée. La sérialisation PHP est disponible pour une utilisation léguée/avancée, mais elle est moins sécurisée.
- Collecte des déchets : Implémente
SessionHandlerInterface::gc()
de PHP pour nettoyer les sessions expirées.
Contribution
Les contributions sont les bienvenues ! Forkez le dépôt, apportez vos modifications et soumettez une pull request. Signalez les bogues ou suggérez des fonctionnalités via le tracker d'issues Github.
Licence
Ce plugin est sous licence MIT. Consultez le dépôt Github pour plus de détails.
Awesome-plugins/runway
Piste
Runway est une application CLI qui vous aide à gérer vos applications Flight. Il peut générer des contrôleurs, afficher toutes les routes, et bien plus encore. Il est basé sur l'excellente bibliothèque adhocore/php-cli.
Cliquez ici pour voir le code.
Installation
Installez avec composer.
composer require flightphp/runway
Configuration de Base
La première fois que vous exécutez Runway, il vous guidera à travers un processus de configuration et créera un fichier de configuration .runway.json
à la racine de votre projet. Ce fichier contiendra certaines configurations nécessaires pour que Runway fonctionne correctement.
Utilisation
Runway dispose de plusieurs commandes que vous pouvez utiliser pour gérer votre application Flight. Il y a deux façons faciles d'utiliser Runway.
- Si vous utilisez le projet de base, vous pouvez exécuter
php runway [commande]
depuis la racine de votre projet. - Si vous utilisez Runway comme un paquet installé via composer, vous pouvez exécuter
vendor/bin/runway [commande]
depuis la racine de votre projet.
Pour n'importe quelle commande, vous pouvez ajouter le drapeau --help
pour obtenir plus d'informations sur comment utiliser la commande.
php runway routes --help
Voici quelques exemples :
Générer un Contrôleur
Selon la configuration dans votre fichier .runway.json
, l'emplacement par défaut générera un contrôleur pour vous dans le répertoire app/controllers/
.
php runway make:controller MyController
Générer un Modèle Active Record
Selon la configuration dans votre fichier .runway.json
, l'emplacement par défaut générera un contrôleur pour vous dans le répertoire app/records/
.
php runway make:record users
Par exemple, si vous avez la table users
avec le schéma suivant : id
, name
, email
, created_at
, updated_at
, un fichier similaire au suivant sera créé dans le fichier app/records/UserRecord.php
:
<?php
declare(strict_types=1);
namespace app\records;
/**
* Class ActiveRecord pour la table des utilisateurs.
* @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
* // vous pouvez également ajouter des relations ici une fois que vous les définissez dans le tableau $relations
* @property CompanyRecord $company Exemple d'une relation
*/
class UserRecord extends \flight\ActiveRecord
{
/**
* @var array $relations Définir les relations pour le modèle
* https://docs.flightphp.com/awesome-plugins/active-record#relationships
*/
protected array $relations = [];
/**
* Constructeur
* @param mixed $databaseConnection La connexion à la base de données
*/
public function __construct($databaseConnection)
{
parent::__construct($databaseConnection, 'users');
}
}
Afficher Toutes les Routes
Cela affichera toutes les routes actuellement enregistrées avec Flight.
php runway routes
Si vous souhaitez uniquement visualiser des routes spécifiques, vous pouvez ajouter un drapeau pour filtrer les routes.
# Afficher uniquement les routes GET
php runway routes --get
# Afficher uniquement les routes POST
php runway routes --post
# etc.
Personnaliser Runway
Si vous êtes en train de créer un paquet pour Flight, ou si vous voulez ajouter vos propres commandes personnalisées dans votre projet, vous pouvez le faire en créant un répertoire src/commands/
, flight/commands/
, app/commands/
, ou commands/
pour votre projet/paquet.
Pour créer une commande, vous étendez simplement la classe AbstractBaseCommand
et implémentez au minimum une méthode __construct
et une méthode execute
.
<?php
declare(strict_types=1);
namespace flight\commands;
class ExampleCommand extends AbstractBaseCommand
{
/**
* Constructeur
*
* @param array<string,mixed> $config Config JSON de .runway-config.json
*/
public function __construct(array $config)
{
parent::__construct('make:example', 'Crée un exemple pour la documentation', $config);
$this->argument('<funny-gif>', 'Le nom du gif drôle');
}
/**
* Exécute la fonction
*
* @return void
*/
public function execute(string $controller)
{
$io = $this->app()->io();
$io->info('Création de l\'exemple...');
// Faites quelque chose ici
$io->ok('Exemple créé !');
}
}
Consultez la Documentation de adhocore/php-cli pour plus d'informations sur la façon de créer vos propres commandes personnalisées dans votre application Flight !
Awesome-plugins/tracy_extensions
Extensions du panneau Tracy Flight
Ceci est un ensemble d'extensions pour rendre le travail avec Flight un peu plus riche.
- Flight - Analyser toutes les variables Flight.
- Base de données - Analyser toutes les requêtes qui ont été exécutées sur la page (si vous initialisez correctement la connexion à la base de données)
- Requête - Analyser toutes les variables
$_SERVER
et examiner toutes les charges globales ($_GET
,$_POST
,$_FILES
) - Session - Analyser toutes les variables
$_SESSION
si les sessions sont actives.
Voici le panneau
Et chaque panneau affiche des informations très utiles sur votre application !
Cliquez ici pour voir le code.
Installation
Exécutez composer require flightphp/tracy-extensions --dev
et vous êtes prêt !
Configuration
Il y a très peu de configuration à faire pour démarrer cela. Vous devrez initialiser le débogueur Tracy avant d'utiliser cela https://tracy.nette.org/en/guide :
<?php
use Tracy\Debugger;
use flight\debug\tracy\TracyExtensionLoader;
// code de démarrage
require __DIR__ . '/vendor/autoload.php';
Debugger::enable();
// Vous devrez peut-être spécifier votre environnement avec Debugger::enable(Debugger::DEVELOPMENT)
// si vous utilisez des connexions à la base de données dans votre application, il y a un
// wrapper PDO requis à utiliser UNIQUEMENT EN DÉVELOPPEMENT (pas en production s'il vous plaît !)
// Il a les mêmes paramètres qu'une connexion PDO régulière
$pdo = new PdoQueryCapture('sqlite:test.db', 'user', 'pass');
// ou si vous l'attachez au framework Flight
Flight::register('db', PdoQueryCapture::class, ['sqlite:test.db', 'user', 'pass']);
// maintenant, chaque fois que vous exécutez une requête, elle capturera le temps, la requête et les paramètres
// Cela connecte les points
if(Debugger::$showBar === true) {
// Cela doit être false sinon Tracy ne peut pas réellement rendre :(
Flight::set('flight.content_length', false);
new TracyExtensionLoader(Flight::app());
}
// plus de code
Flight::start();
Configuration supplémentaire
Données de session
Si vous avez un gestionnaire de session personnalisé (comme ghostff/session), vous pouvez passer n'importe quel tableau de données de session à Tracy et il l'affichera automatiquement pour vous. Vous le passez avec la clé session_data
dans le deuxième paramètre du constructeur TracyExtensionLoader
.
use Ghostff\Session\Session;
// ou utilisez flight\Session;
require 'vendor/autoload.php';
$app = Flight::app();
$app->register('session', Session::class);
if(Debugger::$showBar === true) {
// Cela doit être false sinon Tracy ne peut pas réellement rendre :(
Flight::set('flight.content_length', false);
new TracyExtensionLoader(Flight::app(), [ 'session_data' => Flight::session()->getAll() ]);
}
// routes et autres choses...
Flight::start();
Latte
PHP 8.1+ est requis pour cette section.
Si vous avez Latte installé dans votre projet, Tracy a une intégration native avec Latte pour analyser vos templates. Vous enregistrez simplement l'extension avec votre instance Latte.
require 'vendor/autoload.php';
$app = Flight::app();
$app->map('render', function($template, $data, $block = null) {
$latte = new Latte\Engine;
// autres configurations...
// n'ajouter l'extension que si la barre de débogage Tracy est activée
if(Debugger::$showBar === true) {
// c'est ici que vous ajoutez le panneau Latte à Tracy
$latte->addExtension(new Latte\Bridges\Tracy\TracyExtension);
}
$latte->render($template, $data, $block);
});
Awesome-plugins/apm
Documentation FlightPHP APM
Bienvenue dans FlightPHP APM—votre coach personnel de performance pour votre application ! Ce guide est votre feuille de route pour configurer, utiliser et maîtriser la Surveillance de la Performance des Applications (APM) avec FlightPHP. Que vous traquiez des requêtes lentes ou que vous souhaitiez simplement vous enthousiasmer pour des graphiques de latence, nous avons tout ce qu'il vous faut. Rendons votre application plus rapide, vos utilisateurs plus heureux, et vos sessions de débogage plus fluides !
Découvrez une démo du tableau de bord pour le site Flight Docs.
Pourquoi l'APM est important
Imaginez ceci : votre application est un restaurant animé. Sans moyen de suivre la durée des commandes ou les endroits où la cuisine ralentit, vous devinez pourquoi les clients partent mécontents. L'APM est votre sous-chef—il surveille chaque étape, des requêtes entrantes aux requêtes de base de données, et signale tout ce qui vous ralentit. Les pages lentes font fuir les utilisateurs (des études disent que 53 % rebondissent si un site prend plus de 3 secondes à charger !), et l'APM vous aide à attraper ces problèmes avant qu'ils ne piquent. C'est une tranquillité d'esprit proactive—moins de moments « pourquoi c'est cassé ? », plus de victoires « regardez comme ça tourne bien ! ».
Installation
Commencez avec Composer :
composer require flightphp/apm
Vous aurez besoin de :
- PHP 7.4+ : Nous garde compatible avec les distributions Linux LTS tout en supportant le PHP moderne.
- FlightPHP Core v3.15+ : Le framework léger que nous boostons.
Bases de données prises en charge
FlightPHP APM prend actuellement en charge les bases de données suivantes pour stocker les métriques :
- SQLite3 : Simple, basée sur fichier, et idéale pour le développement local ou les petites applications. Option par défaut dans la plupart des configurations.
- MySQL/MariaDB : Idéale pour les projets plus grands ou les environnements de production où vous avez besoin d'un stockage robuste et évolutif.
Vous pouvez choisir le type de base de données lors de l'étape de configuration (voir ci-dessous). Assurez-vous que votre environnement PHP a les extensions nécessaires installées (par exemple, pdo_sqlite
ou pdo_mysql
).
Pour commencer
Voici votre guide étape par étape vers l'excellence APM :
1. Enregistrer l'APM
Ajoutez ceci dans votre index.php
ou un fichier services.php
pour commencer le suivi :
use flight\apm\logger\LoggerFactory;
use flight\Apm;
$ApmLogger = LoggerFactory::create(__DIR__ . '/../../.runway-config.json');
$Apm = new Apm($ApmLogger);
$Apm->bindEventsToFlightInstance($app);
// Si vous ajoutez une connexion à la base de données
// Doit être PdoWrapper ou PdoQueryCapture de Tracy Extensions
$pdo = new PdoWrapper('mysql:host=localhost;dbname=example', 'user', 'pass', null, true); // <-- True requis pour activer le suivi dans l'APM.
$Apm->addPdoConnection($pdo);
Que se passe-t-il ici ?
LoggerFactory::create()
récupère votre configuration (plus de détails bientôt) et configure un logger—SQLite par défaut.Apm
est la star—il écoute les événements de Flight (requêtes, routes, erreurs, etc.) et collecte les métriques.bindEventsToFlightInstance($app)
lie tout à votre application Flight.
Astuce Pro : Échantillonnage Si votre application est occupée, logger chaque requête pourrait surcharger les choses. Utilisez un taux d'échantillonnage (0.0 à 1.0) :
$Apm = new Apm($ApmLogger, 0.1); // Logue 10 % des requêtes
Cela garde les performances vives tout en vous donnant des données solides.
2. Le configurer
Exécutez ceci pour créer votre .runway-config.json
:
php vendor/bin/runway apm:init
Que fait ceci ?
- Lance un assistant qui demande d'où viennent les métriques brutes (source) et où vont les données traitées (destination).
- Par défaut, c'est SQLite—par exemple,
sqlite:/tmp/apm_metrics.sqlite
pour la source, une autre pour la destination. - Vous obtiendrez une configuration comme :
{ "apm": { "source_type": "sqlite", "source_db_dsn": "sqlite:/tmp/apm_metrics.sqlite", "storage_type": "sqlite", "dest_db_dsn": "sqlite:/tmp/apm_metrics_processed.sqlite" } }
Ce processus demandera également si vous voulez exécuter les migrations pour cette configuration. Si vous l'installez pour la première fois, la réponse est oui.
Pourquoi deux emplacements ? Les métriques brutes s'accumulent vite (pensez à des logs non filtrés). Le worker les traite en une destination structurée pour le tableau de bord. Cela garde les choses en ordre !
3. Traiter les métriques avec le Worker
Le worker transforme les métriques brutes en données prêtes pour le tableau de bord. Exécutez-le une fois :
php vendor/bin/runway apm:worker
Que fait-il ?
- Lit depuis votre source (par exemple,
apm_metrics.sqlite
). - Traite jusqu'à 100 métriques (taille de lot par défaut) dans votre destination.
- S'arrête quand c'est fait ou s'il n'y a plus de métriques.
Le garder en cours Pour les applications en direct, vous voudrez un traitement continu. Voici vos options :
-
Mode Daemon :
php vendor/bin/runway apm:worker --daemon
Tourne indéfiniment, traitant les métriques au fur et à mesure. Idéal pour le dev ou les petites configurations.
-
Crontab : Ajoutez ceci à votre crontab (
crontab -e
) :* * * * * php /path/to/project/vendor/bin/runway apm:worker
S'exécute toutes les minutes—parfait pour la production.
-
Tmux/Screen : Démarrez une session détachable :
tmux new -s apm-worker php vendor/bin/runway apm:worker --daemon # Ctrl+B, puis D pour détacher ; `tmux attach -t apm-worker` pour se reconnecter
Le garde en cours même si vous vous déconnectez.
-
Ajustements personnalisés :
php vendor/bin/runway apm:worker --batch_size 50 --max_messages 1000 --timeout 300
--batch_size 50
: Traite 50 métriques à la fois.--max_messages 1000
: S'arrête après 1000 métriques.--timeout 300
: Quitte après 5 minutes.
Pourquoi s'embêter ? Sans le worker, votre tableau de bord est vide. C'est le pont entre les logs bruts et les insights actionnables.
4. Lancer le Tableau de Bord
Voyez les signes vitaux de votre application :
php vendor/bin/runway apm:dashboard
Qu'est-ce que c'est ?
- Lance un serveur PHP à
http://localhost:8001/apm/dashboard
. - Affiche les logs de requêtes, les routes lentes, les taux d'erreur, et plus.
Le personnaliser :
php vendor/bin/runway apm:dashboard --host 0.0.0.0 --port 8080 --php-path=/usr/local/bin/php
--host 0.0.0.0
: Accessible depuis n'importe quel IP (pratique pour la visualisation à distance).--port 8080
: Utilisez un port différent si 8001 est pris.--php-path
: Pointez vers PHP s'il n'est pas dans votre PATH.
Ouvrez l'URL dans votre navigateur et explorez !
Mode Production
Pour la production, vous devrez peut-être essayer quelques techniques pour faire tourner le tableau de bord, car il y a probablement des pare-feu et d'autres mesures de sécurité en place. Voici quelques options :
- Utiliser un Reverse Proxy : Configurez Nginx ou Apache pour rediriger les requêtes vers le tableau de bord.
- Tunnel SSH : Si vous pouvez vous connecter en SSH au serveur, utilisez
ssh -L 8080:localhost:8001 youruser@yourserver
pour tunneler le tableau de bord vers votre machine locale. - VPN : Si votre serveur est derrière un VPN, connectez-vous-y et accédez directement au tableau de bord.
- Configurer le Pare-feu : Ouvrez le port 8001 pour votre IP ou le réseau du serveur. (ou quel que soit le port que vous avez défini).
- Configurer Apache/Nginx : Si vous avez un serveur web devant votre application, vous pouvez le configurer pour un domaine ou un sous-domaine. Si vous faites cela, vous définirez la racine des documents à
/path/to/your/project/vendor/flightphp/apm/dashboard
Voulez-vous un tableau de bord différent ?
Vous pouvez construire votre propre tableau de bord si vous le souhaitez ! Regardez le répertoire vendor/flightphp/apm/src/apm/presenter pour des idées sur la façon de présenter les données pour votre propre tableau de bord !
Fonctionnalités du Tableau de Bord
Le tableau de bord est votre quartier général APM—voici ce que vous verrez :
- Log des Requêtes : Chaque requête avec horodatage, URL, code de réponse, et temps total. Cliquez sur « Détails » pour les middlewares, requêtes, et erreurs.
- Requêtes les Plus Lentes : Top 5 des requêtes qui monopolisent le temps (par exemple, « /api/heavy » à 2,5 s).
- Routes les Plus Lentes : Top 5 des routes par temps moyen—idéal pour repérer les patterns.
- Taux d'Erreur : Pourcentage de requêtes qui échouent (par exemple, 2,3 % de 500).
- Percentiles de Latence : 95e (p95) et 99e (p99) temps de réponse—connaissez vos scénarios pires cas.
- Graphique des Codes de Réponse : Visualisez les 200, 404, 500 au fil du temps.
- Requêtes/Middlewares Longs : Top 5 des appels de base de données lents et des couches de middleware.
- Taux de Cache Hit/Miss : À quelle fréquence votre cache sauve la mise.
Extras :
- Filtrez par « Dernière Heure », « Dernier Jour », ou « Dernière Semaine ».
- Basculez en mode sombre pour ces sessions tardives.
Exemple :
Une requête vers /users
pourrait montrer :
- Temps Total : 150 ms
- Middleware :
AuthMiddleware->handle
(50 ms) - Requête :
SELECT * FROM users
(80 ms) - Cache : Hit sur
user_list
(5 ms)
Ajout d'Événements Personnalisés
Suivez n'importe quoi—comme un appel API ou un processus de paiement :
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
]));
Où apparaît-il ? Dans les détails de requête du tableau de bord sous « Événements Personnalisés »—extensible avec un formatage JSON joli.
Cas d'Utilisation :
$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
]));
Maintenant, vous verrez si cet API ralentit votre application !
Surveillance de la Base de Données
Suivez les requêtes PDO comme ceci :
use flight\database\PdoWrapper;
$pdo = new PdoWrapper('sqlite:/path/to/db.sqlite', null, null, null, true); // <-- True requis pour activer le suivi dans l'APM.
$Apm->addPdoConnection($pdo);
Ce Que Vous Obtenez :
- Texte de la requête (par exemple,
SELECT * FROM users WHERE id = ?
) - Temps d'exécution (par exemple, 0,015 s)
- Nombre de lignes (par exemple, 42)
Attention :
- Optionnel : Sautez ceci si vous n'avez pas besoin de suivi DB.
- PdoWrapper Seulement : Le PDO de base n'est pas encore accroché—restez à l'écoute !
- Avertissement de Performance : Logger chaque requête sur un site lourd en DB peut ralentir les choses. Utilisez l'échantillonnage (
$Apm = new Apm($ApmLogger, 0.1)
) pour alléger la charge.
Exemple de Sortie :
- Requête :
SELECT name FROM products WHERE price > 100
- Temps : 0,023 s
- Lignes : 15
Options du Worker
Ajustez le worker à votre goût :
--timeout 300
: S'arrête après 5 minutes—bon pour les tests.--max_messages 500
: Limite à 500 métriques—le garde fini.--batch_size 200
: Traite 200 à la fois—équilibre vitesse et mémoire.--daemon
: Tourne non-stop—idéal pour la surveillance en direct.
Exemple :
php vendor/bin/runway apm:worker --daemon --batch_size 100 --timeout 3600
Tourne pendant une heure, traitant 100 métriques à la fois.
ID de Requête dans l'Application
Chaque requête a un ID de requête unique pour le suivi. Vous pouvez utiliser cet ID dans votre application pour corréler les logs et les métriques. Par exemple, vous pouvez ajouter l'ID de requête à une page d'erreur :
Flight::map('error', function($message) {
// Obtenez l'ID de requête depuis l'en-tête de réponse X-Flight-Request-Id
$requestId = Flight::response()->getHeader('X-Flight-Request-Id');
// De plus, vous pourriez le récupérer depuis la variable Flight
// Cette méthode ne fonctionnera pas bien sur swoole ou d'autres plateformes asynchrones.
// $requestId = Flight::get('apm.request_id');
echo "Erreur : $message (ID de Requête : $requestId)";
});
Mise à Niveau
Si vous mettez à niveau vers une version plus récente de l'APM, il est possible qu'il y ait des migrations de base de données à exécuter. Vous pouvez le faire en exécutant la commande suivante :
php vendor/bin/runway apm:migrate
Ceci exécutera toutes les migrations nécessaires pour mettre à jour le schéma de la base de données vers la dernière version.
Note : Si votre base de données APM est grande en taille, ces migrations pourraient prendre un certain temps à s'exécuter. Vous pourriez vouloir exécuter cette commande pendant les heures creuses.
Purgage des Anciennes Données
Pour garder votre base de données en ordre, vous pouvez purger les anciennes données. C'est particulièrement utile si vous exécutez une application occupée et que vous voulez garder la taille de la base de données gérable. Vous pouvez le faire en exécutant la commande suivante :
php vendor/bin/runway apm:purge
Ceci supprimera toutes les données plus anciennes que 30 jours de la base de données. Vous pouvez ajuster le nombre de jours en passant une valeur différente à l'option --days
:
php vendor/bin/runway apm:purge --days 7
Ceci supprimera toutes les données plus anciennes que 7 jours de la base de données.
Dépannage
Coincé ? Essayez ceci :
-
Pas de Données dans le Tableau de Bord ?
- Le worker tourne-t-il ? Vérifiez
ps aux | grep apm:worker
. - Les chemins de configuration correspondent-ils ? Vérifiez que les DSN dans
.runway-config.json
pointent vers des fichiers réels. - Exécutez
php vendor/bin/runway apm:worker
manuellement pour traiter les métriques en attente.
- Le worker tourne-t-il ? Vérifiez
-
Erreurs du Worker ?
- Jetez un œil à vos fichiers SQLite (par exemple,
sqlite3 /tmp/apm_metrics.sqlite "SELECT * FROM apm_metrics_log LIMIT 5"
). - Vérifiez les logs PHP pour les traces de pile.
- Jetez un œil à vos fichiers SQLite (par exemple,
-
Le Tableau de Bord Ne Démarre Pas ?
- Port 8001 utilisé ? Utilisez
--port 8080
. - PHP non trouvé ? Utilisez
--php-path /usr/bin/php
. - Pare-feu bloquant ? Ouvrez le port ou utilisez
--host localhost
.
- Port 8001 utilisé ? Utilisez
-
Trop Lent ?
- Baissez le taux d'échantillonnage :
$Apm = new Apm($ApmLogger, 0.05)
(5 %). - Réduisez la taille du lot :
--batch_size 20
.
- Baissez le taux d'échantillonnage :
-
Pas de Suivi des Exceptions/Erreurs ?
- Si vous avez Tracy activé pour votre projet, il remplacera la gestion d'erreurs de Flight. Vous devrez désactiver Tracy et vous assurer que
Flight::set('flight.handle_errors', true);
est défini.
- Si vous avez Tracy activé pour votre projet, il remplacera la gestion d'erreurs de Flight. Vous devrez désactiver Tracy et vous assurer que
-
Pas de Suivi des Requêtes de Base de Données ?
- Assurez-vous d'utiliser
PdoWrapper
pour vos connexions à la base de données. - Assurez-vous de passer
true
comme dernier argument dans le constructeur.
- Assurez-vous d'utiliser
Awesome-plugins/tracy
Tracy
Tracy est un gestionnaire d'erreurs incroyable qui peut être utilisé avec Flight. Il dispose de plusieurs panneaux qui peuvent vous aider à déboguer votre application. Il est également très facile à étendre et à ajouter vos propres panneaux. L'équipe de Flight a créé quelques panneaux spécifiquement pour les projets Flight avec le plugin flightphp/tracy-extensions.
Installation
Installez avec composer. Et vous voudrez en fait installer ceci sans la version dev car Tracy est livré avec un composant de gestion des erreurs de production.
composer require tracy/tracy
Configuration de base
Il existe quelques options de configuration de base pour commencer. Vous pouvez en savoir plus à leur sujet dans la Documentation de Tracy.
require 'vendor/autoload.php';
use Tracy\Debugger;
// Activer Tracy
Debugger::enable();
// Debugger::enable(Debugger::DEVELOPMENT) // parfois vous devez être explicite (également Debugger::PRODUCTION)
// Debugger::enable('23.75.345.200'); // vous pouvez également fournir un tableau d'adresses IP
// C'est là que les erreurs et exceptions seront journalisées. Assurez-vous que ce répertoire existe et est accessible en écriture.
Debugger::$logDirectory = __DIR__ . '/../log/';
Debugger::$strictMode = true; // afficher toutes les erreurs
// Debugger::$strictMode = E_ALL & ~E_DEPRECATED & ~E_USER_DEPRECATED; // toutes les erreurs sauf les avis obsolètes
if (Debugger::$showBar) {
$app->set('flight.content_length', false); // si la barre de débogage est visible, alors la longueur du contenu ne peut pas être définie par Flight
// Ceci est spécifique à l'extension Tracy pour Flight si vous l'avez incluse
// sinon mettez en commentaire.
new TracyExtensionLoader($app);
}
Conseils utiles
Lorsque vous déboguez votre code, il y a quelques fonctions très utiles pour afficher des données pour vous.
bdump($var)
- Cela affichera la variable dans la barre de Tracy dans un panneau séparé.dumpe($var)
- Cela affichera la variable puis s'arrêtera immédiatement.
Awesome-plugins/active_record
Flight Active Record
Un enregistrement actif est une cartographie d'une entité de base de données à un objet PHP. En d'autres termes, si vous avez une table utilisateurs dans votre base de données, vous pouvez "traduire" une ligne dans cette table en une classe User
et un objet $user
dans votre code. Voir exemple de base.
Cliquez ici pour le dépôt sur GitHub.
Exemple de Base
Assumons que vous ayez la table suivante :
CREATE TABLE users (
id INTEGER PRIMARY KEY,
name TEXT,
password TEXT
);
Maintenant, vous pouvez configurer une nouvelle classe pour représenter cette table :
/**
* Une classe ActiveRecord est généralement singulière
*
* Il est fortement recommandé d'ajouter ici les propriétés de la table en tant que commentaires
*
* @property int $id
* @property string $name
* @property string $password
*/
class User extends flight\ActiveRecord {
public function __construct($database_connection)
{
// vous pouvez le définir de cette manière
parent::__construct($database_connection, 'users');
// ou de cette manière
parent::__construct($database_connection, null, [ 'table' => 'users']);
}
}
Maintenant, regardez la magie opérer !
// pour sqlite
$database_connection = new PDO('sqlite:test.db'); // ceci est juste un exemple, vous devriez probablement utiliser une vraie connexion à une base de données
// pour mysql
$database_connection = new PDO('mysql:host=localhost;dbname=test_db&charset=utf8bm4', 'username', 'password');
// ou mysqli
$database_connection = new mysqli('localhost', 'username', 'password', 'test_db');
// ou mysqli avec création non basée sur des objets
$database_connection = mysqli_connect('localhost', 'username', 'password', 'test_db');
$user = new User($database_connection);
$user->name = 'Bobby Tables';
$user->password = password_hash('some cool password');
$user->insert();
// ou $user->save();
echo $user->id; // 1
$user->name = 'Joseph Mamma';
$user->password = password_hash('some cool password again!!!');
$user->insert();
// impossible d'utiliser $user->save() ici sinon il pensera que c'est une mise à jour !
echo $user->id; // 2
Et c'était aussi simple que cela d'ajouter un nouvel utilisateur ! Maintenant qu'il y a une ligne d'utilisateur dans la base de données, comment la récupérer ?
$user->find(1); // trouver id = 1 dans la base de données et le renvoyer.
echo $user->name; // 'Bobby Tables'
Et que se passe-t-il si vous souhaitez trouver tous les utilisateurs ?
$users = $user->findAll();
Que dire d'une certaine condition ?
$users = $user->like('name', '%mamma%')->findAll();
Voyez comme c'est amusant ? Installons-le et commençons !
Installation
Il suffit d'installer avec Composer
composer require flightphp/active-record
Usage
Cela peut être utilisé comme une bibliothèque autonome ou avec le Framework PHP Flight. Complètement à vous de choisir.
Autonome
Il suffit de vous assurer que vous passez une connexion PDO au constructeur.
$pdo_connection = new PDO('sqlite:test.db'); // ceci est juste un exemple, vous devriez probablement utiliser une vraie connexion à une base de données
$User = new User($pdo_connection);
Vous ne voulez pas toujours définir votre connexion à la base de données dans le constructeur ? Consultez Gestion des Connexions à la Base de Données pour d'autres idées !
Enregistrer en tant que méthode dans Flight
Si vous utilisez le Framework PHP Flight, vous pouvez enregistrer la classe ActiveRecord en tant que service, mais vous n'êtes vraiment pas obligé.
Flight::register('user', 'User', [ $pdo_connection ]);
// puis vous pouvez l'utiliser comme ceci dans un contrôleur, une fonction, etc.
Flight::user()->find(1);
Méthodes runway
runway est un outil CLI pour Flight qui a une commande personnalisée pour cette bibliothèque.
# Usage
php runway make:record database_table_name [class_name]
# Exemple
php runway make:record users
Cela créera une nouvelle classe dans le répertoire app/records/
sous le nom UserRecord.php
avec le contenu suivant :
<?php
declare(strict_types=1);
namespace app\records;
/**
* Classe ActiveRecord pour la table des utilisateurs.
* @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 Définir les relations pour le modèle
* https://docs.flightphp.com/awesome-plugins/active-record#relationships
*/
protected array $relations = [
// 'relation_name' => [ self::HAS_MANY, 'RelatedClass', 'foreign_key' ],
];
/**
* Constructeur
* @param mixed $databaseConnection La connexion à la base de données
*/
public function __construct($databaseConnection)
{
parent::__construct($databaseConnection, 'users');
}
}
Fonctions CRUD
find($id = null) : boolean|ActiveRecord
Trouver un enregistrement et l'assigner à l'objet actuel. Si vous passez un $id
de quelque nature que ce soit, il effectuera une recherche sur la clé primaire avec cette valeur. Si rien n'est passé, il trouvera simplement le premier enregistrement dans la table.
De plus, vous pouvez lui passer d'autres méthodes d'aide pour interroger votre table.
// trouver un enregistrement avec certaines conditions au préalable
$user->notNull('password')->orderBy('id DESC')->find();
// trouver un enregistrement par un id spécifique
$id = 123;
$user->find($id);
findAll(): array<int,ActiveRecord>
Trouve tous les enregistrements dans la table que vous spécifiez.
$user->findAll();
isHydrated(): boolean
(v0.4.0)
Retourne true
si l'enregistrement actuel a été hydraté (récupéré de la base de données).
$user->find(1);
// si un enregistrement est trouvé avec des données...
$user->isHydrated(); // true
insert(): boolean|ActiveRecord
Insère l'enregistrement actuel dans la base de données.
$user = new User($pdo_connection);
$user->name = 'demo';
$user->password = md5('demo');
$user->insert();
Clés Primaires Basées sur du Texte
Si vous avez une clé primaire basée sur du texte (comme un UUID), vous pouvez définir la valeur de la clé primaire avant d'insérer de deux manières.
$user = new User($pdo_connection, [ 'primaryKey' => 'uuid' ]);
$user->uuid = 'some-uuid';
$user->name = 'demo';
$user->password = md5('demo');
$user->insert(); // ou $user->save();
ou vous pouvez laisser la clé primaire être générée automatiquement pour vous via des événements.
class User extends flight\ActiveRecord {
public function __construct($database_connection)
{
parent::__construct($database_connection, 'users', [ 'primaryKey' => 'uuid' ]);
// vous pouvez également définir la primaryKey de cette manière au lieu de l'array ci-dessus.
$this->primaryKey = 'uuid';
}
protected function beforeInsert(self $self) {
$self->uuid = uniqid(); // ou comme vous avez besoin de générer vos identifiants uniques
}
}
Si vous ne définissez pas la clé primaire avant d'insérer, elle sera définie sur le rowid
et la
base de données la générera pour vous, mais elle ne sera pas persistante car ce champ peut ne pas exister
dans votre table. C'est pourquoi il est recommandé d'utiliser l'événement pour gérer cela automatiquement.
update(): boolean|ActiveRecord
Met à jour l'enregistrement actuel dans la base de données.
$user->greaterThan('id', 0)->orderBy('id desc')->find();
$user->email = 'test@example.com';
$user->update();
save(): boolean|ActiveRecord
Insère ou met à jour l'enregistrement actuel dans la base de données. Si l'enregistrement a un id, il sera mis à jour, sinon il sera inséré.
$user = new User($pdo_connection);
$user->name = 'demo';
$user->password = md5('demo');
$user->save();
Remarque : Si vous avez des relations définies dans la classe, elles seront sauvegardées de manière récursive si elles ont été définies, instanciées et ont des données "sales" à mettre à jour. (v0.4.0 et plus)
delete(): boolean
Supprime l'enregistrement actuel de la base de données.
$user->gt('id', 0)->orderBy('id desc')->find();
$user->delete();
Vous pouvez également supprimer plusieurs enregistrements en exécutant une recherche au préalable.
$user->like('name', 'Bob%')->delete();
dirty(array $dirty = []): ActiveRecord
Les données "sales" se réfèrent aux données qui ont été modifiées dans un enregistrement.
$user->greaterThan('id', 0)->orderBy('id desc')->find();
// rien n'est "sale" à ce stade.
$user->email = 'test@example.com'; // maintenant l'email est considéré comme "sale" puisqu'il a changé.
$user->update();
// maintenant il n'y a pas de données qui sont sales car elles ont été mises à jour et persistées dans la base de données
$user->password = password_hash()'newpassword'); // maintenant c'est sale
$user->dirty(); // ne rien passer effacera toutes les entrées sales.
$user->update(); // rien ne sera mis à jour car rien n'a été capturé comme sale.
$user->dirty([ 'name' => 'something', 'password' => password_hash('a different password') ]);
$user->update(); // à la fois le nom et le mot de passe sont mis à jour.
copyFrom(array $data): ActiveRecord
(v0.4.0)
Ceci est un alias pour la méthode dirty()
. C'est un peu plus clair ce que vous faites.
$user->copyFrom([ 'name' => 'something', 'password' => password_hash('a different password') ]);
$user->update(); // à la fois le nom et le mot de passe sont mis à jour.
isDirty(): boolean
(v0.4.0)
Retourne true
si l'enregistrement actuel a été modifié.
$user->greaterThan('id', 0)->orderBy('id desc')->find();
$user->email = 'test@email.com';
$user->isDirty(); // true
reset(bool $include_query_data = true): ActiveRecord
Réinitialise l'enregistrement actuel à son état initial. C'est vraiment bon à utiliser dans des comportements de type boucle.
Si vous passez true
, il réinitialisera également les données de requête qui ont été utilisées pour trouver l'objet actuel (comportement par défaut).
$users = $user->greaterThan('id', 0)->orderBy('id desc')->find();
$user_company = new UserCompany($pdo_connection);
foreach($users as $user) {
$user_company->reset(); // commencez avec une ardoise propre
$user_company->user_id = $user->id;
$user_company->company_id = $some_company_id;
$user_company->insert();
}
getBuiltSql(): string
(v0.4.1)
Après avoir exécuté une méthode find()
, findAll()
, insert()
, update()
ou save()
, vous pouvez obtenir le SQL qui a été construit et l'utiliser à des fins de débogage.
Méthodes de Requête SQL
select(string $field1 [, string $field2 ... ])
Vous pouvez sélectionner uniquement quelques-unes des colonnes d'une table si vous le souhaitez (c'est plus performant sur des tables très larges avec de nombreuses colonnes)
$user->select('id', 'name')->find();
from(string $table)
Vous pouvez techniquement choisir une autre table aussi ! Pourquoi pas ?!
$user->select('id', 'name')->from('user')->find();
join(string $table_name, string $join_condition)
Vous pouvez même joindre à une autre table dans la base de données.
$user->join('contacts', 'contacts.user_id = users.id')->find();
where(string $where_conditions)
Vous pouvez définir quelques arguments where personnalisés (vous ne pouvez pas définir de paramètres dans cette déclaration where)
$user->where('id=1 AND name="demo"')->find();
Remarque de Sécurité - Vous pourriez être tenté de faire quelque chose comme $user->where("id = '{$id}' AND name = '{$name}'")->find();
. S'il vous plaît, NE FAITES PAS CELA !!! Cela est susceptible de ce qu'on appelle des attaques par injection SQL. Il existe de nombreux articles en ligne, s'il vous plaît recherchez "sql injection attacks php" et vous trouverez beaucoup d'articles sur ce sujet. La manière appropriée de gérer cela avec cette bibliothèque est qu'au lieu de cette méthode where()
, vous feriez quelque chose de plus comme $user->eq('id', $id)->eq('name', $name)->find();
Si vous devez absolument faire cela, la bibliothèque PDO
a $pdo->quote($var)
pour l'échapper pour vous. Ce n'est qu'après avoir utilisé quote()
que vous pouvez l'utiliser dans une déclaration where()
.
group(string $group_by_statement)/groupBy(string $group_by_statement)
Groupez vos résultats par une condition particulière.
$user->select('COUNT(*) as count')->groupBy('name')->findAll();
order(string $order_by_statement)/orderBy(string $order_by_statement)
Trier la requête retournée d'une certaine manière.
$user->orderBy('name DESC')->find();
limit(string $limit)/limit(int $offset, int $limit)
Limitez le nombre d'enregistrements retournés. Si un second int est donné, il sera décalé, limite tout comme en SQL.
$user->orderby('name DESC')->limit(0, 10)->findAll();
CONDITIONS WHERE
equal(string $field, mixed $value) / eq(string $field, mixed $value)
Où field = $value
$user->eq('id', 1)->find();
notEqual(string $field, mixed $value) / ne(string $field, mixed $value)
Où field <> $value
$user->ne('id', 1)->find();
isNull(string $field)
Où field IS NULL
$user->isNull('id')->find();
isNotNull(string $field) / notNull(string $field)
Où field IS NOT NULL
$user->isNotNull('id')->find();
greaterThan(string $field, mixed $value) / gt(string $field, mixed $value)
Où field > $value
$user->gt('id', 1)->find();
lessThan(string $field, mixed $value) / lt(string $field, mixed $value)
Où field < $value
$user->lt('id', 1)->find();
greaterThanOrEqual(string $field, mixed $value) / ge(string $field, mixed $value) / gte(string $field, mixed $value)
Où field >= $value
$user->ge('id', 1)->find();
lessThanOrEqual(string $field, mixed $value) / le(string $field, mixed $value) / lte(string $field, mixed $value)
Où field <= $value
$user->le('id', 1)->find();
like(string $field, mixed $value) / notLike(string $field, mixed $value)
Où field LIKE $value
ou field NOT LIKE $value
$user->like('name', 'de')->find();
in(string $field, array $values) / notIn(string $field, array $values)
Où field IN($value)
ou field NOT IN($value)
$user->in('id', [1, 2])->find();
between(string $field, array $values)
Où field BETWEEN $value AND $value1
$user->between('id', [1, 2])->find();
Conditions OR
Il est possible d'envelopper vos conditions dans une déclaration OR. Cela se fait soit avec les méthodes startWrap()
et endWrap()
, soit en remplissant le 3ème paramètre de la condition après le champ et la valeur.
// Méthode 1
$user->eq('id', 1)->startWrap()->eq('name', 'demo')->or()->eq('name', 'test')->endWrap('OR')->find();
// Cela s'évaluera à `id = 1 AND (name = 'demo' OR name = 'test')`
// Méthode 2
$user->eq('id', 1)->eq('name', 'demo', 'OR')->find();
// Cela s'évaluera à `id = 1 OR name = 'demo'`
Relations
Vous pouvez définir plusieurs types de relations à l'aide de cette bibliothèque. Vous pouvez établir des relations un->plusieurs et un->un entre des tables. Cela nécessite une petite configuration supplémentaire dans la classe au préalable.
Définir le tableau $relations
n'est pas difficile, mais deviner la syntaxe correcte peut être déroutant.
protected array $relations = [
// vous pouvez nommer la clé comme bon vous semble. Le nom de l'ActiveRecord est probablement bon. Ex : user, contact, client
'user' => [
// requis
// self::HAS_MANY, self::HAS_ONE, self::BELONGS_TO
self::HAS_ONE, // c'est le type de relation
// requis
'Some_Class', // c'est la classe ActiveRecord "autre" à laquelle cela fera référence
// requis
// selon le type de relation
// self::HAS_ONE = la clé étrangère qui référence la jointure
// self::HAS_MANY = la clé étrangère qui référence la jointure
// self::BELONGS_TO = la clé locale qui référence la jointure
'local_or_foreign_key',
// juste pour info, cela ne joint également qu'à la clé primaire du modèle "autre"
// optionnel
[ 'eq' => [ 'client_id', 5 ], 'select' => 'COUNT(*) as count', 'limit' 5 ], // conditions supplémentaires que vous souhaitez lors de la jointure de la relation
// $record->eq('client_id', 5)->select('COUNT(*) as count')->limit(5))
// optionnel
'back_reference_name' // c'est si vous voulez faire référence à cette relation à elle-même Ex : $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');
}
}
Maintenant, nous avons les références configurées afin que nous puissions les utiliser très facilement !
$user = new User($pdo_connection);
// trouver l'utilisateur le plus récent.
$user->notNull('id')->orderBy('id desc')->find();
// obtenir les contacts en utilisant la relation :
foreach($user->contacts as $contact) {
echo $contact->id;
}
// ou nous pouvons aller dans l'autre sens.
$contact = new Contact();
// trouver un contact
$contact->find();
// obtenir l'utilisateur en utilisant la relation :
echo $contact->user->name; // c'est le nom de l'utilisateur
Assez cool, non ?
Définir des Données Personnalisées
Parfois, vous pourriez avoir besoin d'attacher quelque chose d'unique à votre ActiveRecord, comme un calcul personnalisé qui serait plus facile à attacher à l'objet qui serait ensuite passé à un modèle, par exemple.
setCustomData(string $field, mixed $value)
Vous attachez les données personnalisées avec la méthode setCustomData()
.
$user->setCustomData('page_view_count', $page_view_count);
Et ensuite, vous le référencez simplement comme une propriété d'objet normale.
echo $user->page_view_count;
Événements
Une autre fonctionnalité super géniale de cette bibliothèque concerne les événements. Les événements sont déclenchés à certains moments en fonction de certaines méthodes que vous appelez. Ils sont très utiles pour vous aider à configurer des données automatiquement.
onConstruct(ActiveRecord $ActiveRecord, array &config)
Cela est vraiment utile si vous avez besoin de définir une connexion par défaut ou quelque chose comme cela.
// index.php ou bootstrap.php
Flight::register('db', 'PDO', [ 'sqlite:test.db' ]);
//
//
//
// User.php
class User extends flight\ActiveRecord {
protected function onConstruct(self $self, array &$config) { // n'oubliez pas la référence &
// vous pourriez faire cela pour définir automatiquement la connexion
$config['connection'] = Flight::db();
// ou ceci
$self->transformAndPersistConnection(Flight::db());
// Vous pouvez également définir le nom de la table de cette manière.
$config['table'] = 'users';
}
}
beforeFind(ActiveRecord $ActiveRecord)
Cela est probablement seulement utile si vous avez besoin d'une manipulation de requête chaque fois.
class User extends flight\ActiveRecord {
public function __construct($database_connection)
{
parent::__construct($database_connection, 'users');
}
protected function beforeFind(self $self) {
// toujours exécuter id >= 0 si c'est votre truc
$self->gte('id', 0);
}
}
afterFind(ActiveRecord $ActiveRecord)
Celui-ci est probablement plus utile si vous devez toujours exécuter une logique chaque fois que cet enregistrement est récupéré. Avez-vous besoin de déchiffrer quelque chose ? Avez-vous besoin d'exécuter une requête de comptage personnalisée chaque fois (pas performant mais peu importe) ?
class User extends flight\ActiveRecord {
public function __construct($database_connection)
{
parent::__construct($database_connection, 'users');
}
protected function afterFind(self $self) {
// déchiffrer quelque chose
$self->secret = yourDecryptFunction($self->secret, $some_key);
// peut-être stocker quelque chose de personnalisé, comme une requête ???
$self->setCustomData('view_count', $self->select('COUNT(*) count')->from('user_views')->eq('user_id', $self->id)['count'];
}
}
beforeFindAll(ActiveRecord $ActiveRecord)
Cela est probablement seulement utile si vous avez besoin d'une manipulation de requête chaque fois.
class User extends flight\ActiveRecord {
public function __construct($database_connection)
{
parent::__construct($database_connection, 'users');
}
protected function beforeFindAll(self $self) {
// toujours exécuter id >= 0 si c'est votre truc
$self->gte('id', 0);
}
}
afterFindAll(array<int,ActiveRecord> $results)
Semblable à afterFind()
mais vous pouvez le faire pour tous les enregistrements à la place !
class User extends flight\ActiveRecord {
public function __construct($database_connection)
{
parent::__construct($database_connection, 'users');
}
protected function afterFindAll(array $results) {
foreach($results as $self) {
// faites quelque chose de cool comme aprèsFind()
}
}
}
beforeInsert(ActiveRecord $ActiveRecord)
Vraiment utile si vous avez besoin de définir des valeurs par défaut à chaque fois.
class User extends flight\ActiveRecord {
public function __construct($database_connection)
{
parent::__construct($database_connection, 'users');
}
protected function beforeInsert(self $self) {
// définissez des valeurs par défaut
if(!$self->created_date) {
$self->created_date = gmdate('Y-m-d');
}
if(!$self->password) {
$self->password = password_hash((string) microtime(true));
}
}
}
afterInsert(ActiveRecord $ActiveRecord)
Peut-être que vous avez un cas d'utilisation pour changer des données après leur insertion ?
class User extends flight\ActiveRecord {
public function __construct($database_connection)
{
parent::__construct($database_connection, 'users');
}
protected function afterInsert(self $self) {
// vous faites comme bon vous semble
Flight::cache()->set('most_recent_insert_id', $self->id);
// ou quoi que ce soit d'autre....
}
}
beforeUpdate(ActiveRecord $ActiveRecord)
Vraiment utile si vous avez besoin de définir des valeurs par défaut chaque fois qu'une mise à jour a lieu.
class User extends flight\ActiveRecord {
public function __construct($database_connection)
{
parent::__construct($database_connection, 'users');
}
protected function beforeInsert(self $self) {
// définir des valeurs par défaut
if(!$self->updated_date) {
$self->updated_date = gmdate('Y-m-d');
}
}
}
afterUpdate(ActiveRecord $ActiveRecord)
Peut-être que vous avez un cas d'utilisation pour changer des données après qu'elles aient été mises à jour ?
class User extends flight\ActiveRecord {
public function __construct($database_connection)
{
parent::__construct($database_connection, 'users');
}
protected function afterInsert(self $self) {
// vous faites comme bon vous semble
Flight::cache()->set('most_recently_updated_user_id', $self->id);
// ou quoi que ce soit d'autre....
}
}
beforeSave(ActiveRecord $ActiveRecord)/afterSave(ActiveRecord $ActiveRecord)
Ceci est utile si vous souhaitez que des événements se produisent lorsque des insertions ou des mises à jour ont lieu. Je vous épargne une longue explication, mais je suis sûr que vous pouvez deviner ce que c'est.
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)
Je ne suis pas sûr de ce que vous voudriez faire ici, mais pas de jugement ! Foncez !
class User extends flight\ActiveRecord {
public function __construct($database_connection)
{
parent::__construct($database_connection, 'users');
}
protected function beforeDelete(self $self) {
echo 'Il était un brave soldat... :cry-face:';
}
}
Gestion des Connexions à la Base de Données
Lorsque vous utilisez cette bibliothèque, vous pouvez définir la connexion à la base de données de plusieurs manières. Vous pouvez définir la connexion dans le constructeur, vous pouvez la définir via une variable de configuration $config['connection']
ou vous pouvez la définir via setDatabaseConnection()
(v0.4.1).
$pdo_connection = new PDO('sqlite:test.db'); // par exemple
$user = new User($pdo_connection);
// ou
$user = new User(null, [ 'connection' => $pdo_connection ]);
// ou
$user = new User();
$user->setDatabaseConnection($pdo_connection);
Si vous souhaitez éviter de toujours définir un $database_connection
chaque fois que vous appelez un enregistrement actif, il existe des moyens d'y parvenir !
// index.php ou bootstrap.php
// Réglez cela en tant que classe enregistrée dans 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);
}
}
// Et maintenant, aucun argument requis !
$user = new User();
Remarque : Si vous prévoyez des tests unitaires, faire de cette façon peut ajouter quelques défis aux tests unitaires, mais globalement parce que vous pouvez injecter votre connexion avec
setDatabaseConnection()
ou$config['connection']
, cela n'est pas trop mauvais.
Si vous devez rafraîchir la connexion à la base de données, par exemple si vous exécutez un script CLI longue durée et devez rafraîchir la connexion de temps en temps, vous pouvez réinitialiser la connexion avec $your_record->setDatabaseConnection($pdo_connection)
.
Contribuer
S'il vous plaît le faire. :D
Configuration
Lorsque vous contribuez, assurez-vous d'exécuter composer test-coverage
pour maintenir une couverture de test de 100 % (ceci n'est pas une véritable couverture de test unitaire, plus comme un test d'intégration).
Assurez-vous également d'exécuter composer beautify
et composer phpcs
pour corriger les erreurs de linting.
Licence
MIT
Awesome-plugins/latte
Latte
Latte est un moteur de templating complet qui est très facile à utiliser et qui ressemble plus à une syntaxe PHP que Twig ou Smarty. Il est également très facile à étendre et à ajouter vos propres filtres et fonctions.
Installation
Installez avec composer.
composer require latte/latte
Configuration de base
Il existe quelques options de configuration de base pour commencer. Vous pouvez en lire plus à leur sujet dans la Documentation Latte.
require 'vendor/autoload.php';
$app = Flight::app();
$app->map('render', function(string $template, array $data, ?string $block): void {
$latte = new Latte\Engine;
// Où latte stocke spécifiquement son cache
$latte->setTempDirectory(__DIR__ . '/../cache/');
$finalPath = Flight::get('flight.views.path') . $template;
$latte->render($finalPath, $data, $block);
});
Exemple simple de layout
Voici un exemple simple d'un fichier de layout. C'est le fichier qui sera utilisé pour envelopper toutes vos autres vues.
<!-- app/views/layout.latte -->
<!doctype html>
<html lang="en">
<head>
<title>{$title ? $title . ' - '}Mon App</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<header>
<nav>
<!-- vos éléments de navigation ici -->
</nav>
</header>
<div id="content">
<!-- C'est la magie ici -->
{block content}{/block}
</div>
<div id="footer">
© Copyright
</div>
</body>
</html>
Et maintenant, nous avons votre fichier qui va s'afficher à l'intérieur de ce bloc de contenu :
<!-- app/views/home.latte -->
<!-- Ceci indique à Latte que ce fichier est "à l'intérieur" du fichier layout.latte -->
{extends layout.latte}
<!-- C'est le contenu qui sera affiché à l'intérieur du layout dans le bloc de contenu -->
{block content}
<h1>Page d'accueil</h1>
<p>Bienvenue dans mon app !</p>
{/block}
Puis, lorsque vous allez afficher cela dans votre fonction ou votre contrôleur, vous feriez quelque chose comme ceci :
// route simple
Flight::route('/', function () {
Flight::render('home.latte', [
'title' => 'Page d\'accueil'
]);
});
// ou si vous utilisez un contrôleur
Flight::route('/', [HomeController::class, 'index']);
// HomeController.php
class HomeController
{
public function index()
{
Flight::render('home.latte', [
'title' => 'Page d\'accueil'
]);
}
}
Consultez la Documentation Latte pour plus d'informations sur la façon d'utiliser Latte à son plein potentiel !
Débogage avec Tracy
PHP 8.1+ est requis pour cette section.
Vous pouvez également utiliser Tracy pour vous aider à déboguer vos fichiers de template Latte directement ! Si vous avez déjà installé Tracy, vous devez ajouter l'extension Latte à Tracy.
// services.php
use Tracy\Debugger;
$app->map('render', function(string $template, array $data, ?string $block): void {
$latte = new Latte\Engine;
// Où latte stocke spécifiquement son cache
$latte->setTempDirectory(__DIR__ . '/../cache/');
$finalPath = Flight::get('flight.views.path') . $template;
// Ceci n'ajoutera l'extension que si la barre de débogage Tracy est activée
if (Debugger::$showBar === true) {
// c'est ici que vous ajoutez le panneau Latte à Tracy
$latte->addExtension(new Latte\Bridges\Tracy\TracyExtension);
}
$latte->render($finalPath, $data, $block);
});
Awesome-plugins/awesome_plugins
Plugins Géniaux
Flight est incroyablement extensible. Il existe un certain nombre de plugins qui peuvent être utilisés pour ajouter des fonctionnalités à votre application Flight. Certains sont officiellement supportés par l'équipe Flight et d'autres sont des bibliothèques micro/lite pour vous aider à démarrer.
Documentation API
La documentation API est cruciale pour toute API. Elle aide les développeurs à comprendre comment interagir avec votre API et ce qu'ils peuvent attendre en retour. Il existe quelques outils disponibles pour vous aider à générer la documentation API pour vos projets Flight.
- FlightPHP OpenAPI Generator - Article de blog écrit par Daniel Schreiber sur la façon d'utiliser la spécification OpenAPI avec FlightPHP pour construire votre API en adoptant une approche API first.
- SwaggerUI - Swagger UI est un excellent outil pour vous aider à générer la documentation API pour vos projets Flight. Il est très facile à utiliser et peut être personnalisé selon vos besoins. C'est la bibliothèque PHP pour vous aider à générer la documentation Swagger.
Surveillance des Performances des Applications (APM)
La surveillance des performances des applications (APM) est cruciale pour toute application. Elle vous aide à comprendre comment votre application performe et où se trouvent les goulots d'étranglement. Il existe un certain nombre d'outils APM qui peuvent être utilisés avec Flight.
- officiel flightphp/apm - Flight APM est une bibliothèque APM simple qui peut être utilisée pour surveiller vos applications Flight. Elle peut être utilisée pour surveiller les performances de votre application et vous aider à identifier les goulots d'étranglement.
Asynchrone
Flight est déjà un framework rapide, mais lui ajouter un turbo le rend encore plus amusant (et challenging) !
- flightphp/async - Bibliothèque officielle Flight Async. Cette bibliothèque est une façon simple d'ajouter un traitement asynchrone à votre application. Elle utilise Swoole/Openswoole en arrière-plan pour fournir une façon simple et efficace d'exécuter des tâches de manière asynchrone.
Autorisation/Permissions
L'autorisation et les permissions sont cruciales pour toute application qui nécessite des contrôles sur qui peut accéder à quoi.
- officiel flightphp/permissions - Bibliothèque officielle Flight Permissions. Cette bibliothèque est une façon simple d'ajouter des permissions au niveau utilisateur et application à votre application.
Mise en Cache
La mise en cache est une excellente façon d'accélérer votre application. Il existe un certain nombre de bibliothèques de mise en cache qui peuvent être utilisées avec Flight.
- officiel flightphp/cache - Classe PHP légère, simple et autonome pour la mise en cache en fichier
CLI
Les applications CLI sont une excellente façon d'interagir avec votre application. Vous pouvez les utiliser pour générer des contrôleurs, afficher toutes les routes, et plus encore.
- officiel flightphp/runway - Runway est une application CLI qui vous aide à gérer vos applications Flight.
Cookies
Les cookies sont une excellente façon de stocker de petites quantités de données côté client. Ils peuvent être utilisés pour stocker les préférences utilisateur, les paramètres d'application, et plus encore.
- overclokk/cookie - PHP Cookie est une bibliothèque PHP qui fournit une façon simple et efficace de gérer les cookies.
Débogage
Le débogage est crucial lorsque vous développez dans votre environnement local. Il existe quelques plugins qui peuvent améliorer votre expérience de débogage.
- tracy/tracy - C'est un gestionnaire d'erreurs complet qui peut être utilisé avec Flight. Il dispose d'un certain nombre de panneaux qui peuvent vous aider à déboguer votre application. Il est également très facile à étendre et à ajouter vos propres panneaux.
- officiel flightphp/tracy-extensions - Utilisé avec le gestionnaire d'erreurs Tracy, ce plugin ajoute quelques panneaux supplémentaires pour aider au débogage spécifiquement pour les projets Flight.
Bases de Données
Les bases de données sont au cœur de la plupart des applications. C'est ainsi que vous stockez et récupérez les données. Certaines bibliothèques de bases de données sont simplement des wrappers pour écrire des requêtes et d'autres sont des ORMs complets.
- officiel flightphp/core PdoWrapper - Wrapper PDO officiel Flight qui fait partie du cœur. C'est un wrapper simple pour aider à simplifier le processus d'écriture et d'exécution des requêtes. Ce n'est pas un ORM.
- officiel flightphp/active-record - ORM/Mapper ActiveRecord officiel Flight. Excellente petite bibliothèque pour récupérer et stocker facilement des données dans votre base de données.
- byjg/php-migration - Plugin pour suivre toutes les modifications de base de données pour votre projet.
Chiffrement
Le chiffrement est crucial pour toute application qui stocke des données sensibles. Chiffrer et déchiffrer les données n'est pas terriblement difficile, mais stocker correctement la clé de chiffrement peut être difficile. La chose la plus importante est de ne jamais stocker votre clé de chiffrement dans un répertoire public ou de l'engager dans votre dépôt de code.
- defuse/php-encryption - C'est une bibliothèque qui peut être utilisée pour chiffrer et déchiffrer des données. Se mettre en route est assez simple pour commencer à chiffrer et déchiffrer des données.
File d'Attente de Tâches
Les files d'attente de tâches sont vraiment utiles pour traiter les tâches de manière asynchrone. Cela peut être l'envoi d'emails, le traitement d'images, ou tout ce qui n'a pas besoin d'être fait en temps réel.
- n0nag0n/simple-job-queue - Simple Job Queue est une bibliothèque qui peut être utilisée pour traiter les tâches de manière asynchrone. Elle peut être utilisée avec beanstalkd, MySQL/MariaDB, SQLite, et PostgreSQL.
Session
Les sessions ne sont pas vraiment utiles pour les API, mais pour construire une application web, les sessions peuvent être cruciales pour maintenir l'état et les informations de connexion.
- officiel flightphp/session - Bibliothèque officielle Flight Session. C'est une bibliothèque de session simple qui peut être utilisée pour stocker et récupérer les données de session. Elle utilise la gestion de session intégrée de PHP.
- Ghostff/Session - Gestionnaire de Session PHP (non-bloquant, flash, segment, chiffrement de session). Utilise PHP open_ssl pour le chiffrement/déchiffrement optionnel des données de session.
Modélisation
La modélisation est au cœur de toute application web avec une interface utilisateur. Il existe un certain nombre de moteurs de modélisation qui peuvent être utilisés avec Flight.
- déprécié flightphp/core View - C'est un moteur de modélisation très basique qui fait partie du cœur. Il n'est pas recommandé de l'utiliser si vous avez plus de quelques pages dans votre projet.
- latte/latte - Latte est un moteur de modélisation complet qui est très facile à utiliser et se rapproche plus de la syntaxe PHP que Twig ou Smarty. Il est également très facile à étendre et à ajouter vos propres filtres et fonctions.
Intégration WordPress
Vous voulez utiliser Flight dans votre projet WordPress ? Il y a un plugin pratique pour cela !
- n0nag0n/wordpress-integration-for-flight-framework - Ce plugin WordPress vous permet d'exécuter Flight directement aux côtés de WordPress. C'est parfait pour ajouter des API personnalisées, des microservices, ou même des applications complètes à votre site WordPress en utilisant le framework Flight. Super utile si vous voulez le meilleur des deux mondes !
Contribution
Vous avez un plugin que vous aimeriez partager ? Soumettez une pull request pour l'ajouter à la liste !
Media
Médias
Nous avons essayé de recenser ce que nous pouvions des différents types de médias disponibles sur Internet concernant Flight. Voir ci-dessous pour différents ressources que vous pouvez utiliser pour en apprendre plus sur Flight.
Articles et Rédactions
- Unit Testing and SOLID Principles par Brian Fenton (2015 ?)
- PHP Web Framework Flight par ojambo (2025)
- Define, Generate, and Implement: An API-First Approach with OpenAPI Generator and FlightPHP par Daniel Schreiber (2025)
- Best PHP Micro Frameworks for 2024 par n0nag0n (2024)
- Creating a RESTful API with Flight Framework par n0nag0n (2024)
- Building a Simple Blog with Flight Part 2 par n0nag0n (2024)
- Building a Simple Blog with Flight Part 1 par n0nag0n (2024)
- 🚀 Build a Simple CRUD API in PHP with the Flight Framework par soheil-khaledabadi (2024)
- Building a PHP Web Application with the Flight Micro-framework par Arthur C. Codex (2023)
- Best PHP Frameworks for Web Development in 2024 par Ravikiran A S (2023)
- Top 12 PHP Frameworks: A Comprehensive Guide for 2023 par marketing kbk (2023)
- 5 PHP Frameworks You've (Probably) Never Heard of par n0nag0n (2022)
- 12 top PHP frameworks for web developers to consider in 2023 par Anna Monus (2022)
- The Best PHP Microframeworks on a Cloud Server par Shahzeb Ahmed (2021)
- PHP framework: Top 15 powerful ones for your web development par AHT Tech (2020)
- Easy PHP Routing with FlightPHP par Lucas Conceição (2019)
- Trying Out New PHP Framework (Flight) par Leon (2017)
- Setting up FlightPHP to work with Backbonejs par Timothy Tocci (2015)
Vidéos et Tutoriels
- Build a Flight PHP App with MVC & MariaDB in 10 Minutes! (Beginner Friendly) par ojamboshop (2025)
- Create a REST API for IoT Devices Using PHP & FlightPHP - ESP32 API par IoT Craft Hub (2024)
- PHP Flight Framework Simple Introductory Video par n0nag0n (2024)
- Set header HTTP code in Flightphp (3 Solutions!!) par Roel Van de Paar (2024)
- PHP Flight Framework Tutorial. Super easy API Project! par n0nag0n (2022)
- Aplicación web CRUD con php y mysql y bootstrap usando flight par Devlopteca - Oscar Uh (2021)
- DevOps & SysAdmins: Lighttpd rewrite rule for Flight PHP microframework par Roel Van de Paar (2021)
- Tutorial REST API Flight PHP #PART2 INSERT TABLE Info #Code (Tagalog) par Info Singkat Official (2020)
- Tutorial REST API Flight PHP #PART1 Info #Code (Tagalog) par Info Singkat Official (2020)
- How To Create JSON REST API IN PHP - Part 2 par Codewife (2018)
- How To Create JSON REST API IN PHP - Part 1 par Codewife (2018)
- Teste Micro Frameworks PHP - Flight PHP, Lumen, Slim 3 e Laravel par Codemarket (2016)
- Tutorial 1 Flight PHP - Instalación par absagg (2014)
- Tutorial 2 Flight PHP - Route parte 1 par absagg (2014)
Manque-t-il quelque chose ?
Manquons-nous quelque chose que vous avez écrit ou enregistré ? Faites-le nous savoir avec un issue ou une pull request !
Examples
Besoin d'un démarrage rapide ?
Vous avez deux options pour démarrer un nouveau projet Flight :
- Full Skeleton Boilerplate : Un exemple plus complet avec des contrôleurs et des vues.
- Single File Skeleton Boilerplate : Un fichier unique qui inclut tout ce dont vous avez besoin pour exécuter votre application dans un seul fichier simple.
Exemples contributés par la communauté :
- flightravel : FlightPHP avec des répertoires Laravel, avec des outils PHP + GH Actions
- fleact - Un kit de démarrage FlightPHP avec intégration ReactJS.
- flastro - Un kit de démarrage FlightPHP avec intégration Astro.
- velt - Velt est un modèle de démarrage Svelte rapide et facile avec un backend FlightPHP.
Besoin d'inspiration ?
Bien que ces exemples ne soient pas officiellement sponsorisés par l'équipe Flight, ils pourraient vous donner des idées sur la façon de structurer vos propres projets construits avec Flight !
- Ivox Car Rental - Ivox Car Rental est une application web de location de voitures monopage, adaptée aux mobiles, construite avec PHP (FlightPHP), JavaScript et MySQL. Elle prend en charge l'inscription des utilisateurs, la navigation et la réservation de voitures, tandis que les administrateurs peuvent gérer les voitures, les utilisateurs et les réservations. L'application propose une API REST, une authentification JWT et un design responsive pour une expérience de location moderne.
- Decay - Flight v3 avec HTMX et SleekDB, tout sur les zombies ! (Démo)
- Flight Example Blog - Flight v3 avec Middleware, Controllers, Active Record et Latte.
- Flight CRUD RESTful API - Projet d'API CRUD simple utilisant le framework Flight, qui fournit une structure de base pour que les nouveaux utilisateurs configurent rapidement une application PHP avec des opérations CRUD et une connectivité à la base de données. Le projet démontre comment utiliser Flight pour le développement d'API RESTful, ce qui en fait un outil d'apprentissage idéal pour les débutants et un kit de démarrage utile pour les développeurs plus expérimentés.
- Flight School Management System - Flight v3
- Paste Bin with Comments - Flight v3
- Basic Skeleton App
- Example Wiki
- The IT-Innovator PHP Framework Application
- LittleEducationalCMS (Espagnol)
- Italian Yellow Pages API
- Generic Content Management System (avec....très peu de documentation)
- A tiny php framework based on Flight and medoo.
- Example MVC Application
- Production ready Flight Boilerplate - Framework d'authentification prêt pour la production qui vous fait économiser des semaines de développement. Fonctionnalités de sécurité de niveau entreprise : 2FA/TOTP, intégration LDAP, SSO Azure, limitation de taux intelligente, empreinte de session, protection contre les attaques par force brute, tableau de bord d'analyse de sécurité, journalisation d'audit complète et contrôle d'accès basé sur les rôles granulaire.
Voulez-vous partager votre propre exemple ?
Si vous avez un projet que vous souhaitez partager, veuillez soumettre une pull request pour l'ajouter à cette liste !
Install/install
Instructions d'installation
Il y a quelques prérequis de base avant de pouvoir installer Flight. En particulier, vous devrez :
- Installer PHP sur votre système
- Installer Composer pour une meilleure expérience de développement.
Installation de base
Si vous utilisez Composer, vous pouvez exécuter la commande suivante :
composer require flightphp/core
Cela ne placera que les fichiers principaux de Flight sur votre système. Vous devrez définir la structure du projet, l'agencement, les dépendances, les configurations, le chargement automatique, etc. Cette méthode garantit qu'aucune autre dépendance n'est installée en dehors de Flight.
Vous pouvez également télécharger les fichiers directement et les extraire dans votre répertoire web.
Installation recommandée
Il est fortement recommandé de commencer avec l'application flightphp/skeleton pour tout nouveau projet. L'installation est un jeu d'enfant.
composer create-project flightphp/skeleton my-project/
Cela configurera la structure de votre projet, configurera le chargement automatique avec des espaces de noms, mettra en place une configuration, et fournira d'autres outils comme Tracy, Extensions Tracy, et Runway
Configurer votre serveur web
Serveur de développement PHP intégré
C'est de loin la façon la plus simple de démarrer. Vous pouvez utiliser le serveur intégré pour exécuter votre application et même utiliser SQLite pour une base de données (tant que sqlite3 est installé sur votre système) sans avoir besoin de grand-chose ! Exécutez simplement la commande suivante une fois que PHP est installé :
php -S localhost:8000
# ou avec l'application skeleton
composer start
Puis ouvrez votre navigateur et allez à http://localhost:8000
.
Si vous souhaitez définir le répertoire racine de documents de votre projet comme un répertoire différent (Ex : votre projet est ~/myproject
, mais votre répertoire racine de documents est ~/myproject/public/
), vous pouvez exécuter la commande suivante une fois dans le répertoire ~/myproject
:
php -S localhost:8000 -t public/
# avec l'application skeleton, cela est déjà configuré
composer start
Puis ouvrez votre navigateur et allez à http://localhost:8000
.
Apache
Assurez-vous qu'Apache est déjà installé sur votre système. Sinon, recherchez sur Google comment installer Apache sur votre système.
Pour Apache, modifiez votre fichier .htaccess
avec le suivant :
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php [QSA,L]
Note : Si vous devez utiliser flight dans un sous-répertoire, ajoutez la ligne
RewriteBase /subdir/
juste aprèsRewriteEngine On
.Note : Si vous souhaitez protéger tous les fichiers du serveur, comme un fichier de base de données ou env. Mettez cela dans votre fichier
.htaccess
:
RewriteEngine On
RewriteRule ^(.*)$ index.php
Nginx
Assurez-vous que Nginx est déjà installé sur votre système. Sinon, recherchez sur Google comment installer Nginx sur votre système.
Pour Nginx, ajoutez le suivant à votre déclaration de serveur :
server {
location / {
try_files $uri $uri/ /index.php;
}
}
Créer votre fichier index.php
Si vous effectuez une installation de base, vous devrez avoir du code pour commencer.
<?php
// Si vous utilisez Composer, incluez le chargeur automatique.
require 'vendor/autoload.php';
// si vous n'utilisez pas Composer, chargez le framework directement
// require 'flight/Flight.php';
// Ensuite, définissez une route et assignez une fonction pour gérer la requête.
Flight::route('/', function () {
echo 'hello world!';
});
// Enfin, démarrez le framework.
Flight::start();
Avec l'application skeleton, cela est déjà configuré et géré dans votre fichier app/config/routes.php
. Les services sont configurés dans app/config/services.php
Installer PHP
Si vous avez déjà php
installé sur votre système, passez ces instructions et allez à la section de téléchargement
macOS
Installer PHP en utilisant Homebrew
-
Installer Homebrew (si ce n'est pas déjà installé) :
- Ouvrez le Terminal et exécutez :
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
- Ouvrez le Terminal et exécutez :
-
Installer PHP :
- Installer la dernière version :
brew install php
- Pour installer une version spécifique, par exemple, PHP 8.1 :
brew tap shivammathur/php brew install shivammathur/php/php@8.1
- Installer la dernière version :
-
Changer entre les versions de PHP :
- Délier la version actuelle et lier la version souhaitée :
brew unlink php brew link --overwrite --force php@8.1
- Vérifier la version installée :
php -v
- Délier la version actuelle et lier la version souhaitée :
Windows 10/11
Installer PHP manuellement
-
Télécharger PHP :
- Visitez PHP for Windows et téléchargez la dernière version ou une version spécifique (par ex., 7.4, 8.0) sous forme de fichier zip non thread-safe.
-
Extraire PHP :
- Extrayez le fichier zip téléchargé dans
C:\php
.
- Extrayez le fichier zip téléchargé dans
-
Ajouter PHP au PATH système :
- Allez dans Propriétés du système > Variables d'environnement.
- Sous Variables système, trouvez Path et cliquez sur Modifier.
- Ajoutez le chemin
C:\php
(ou l'endroit où vous avez extrait PHP). - Cliquez sur OK pour fermer toutes les fenêtres.
-
Configurer PHP :
- Copiez
php.ini-development
versphp.ini
. - Modifiez
php.ini
pour configurer PHP selon vos besoins (par ex., définirextension_dir
, activer les extensions).
- Copiez
-
Vérifier l'installation de PHP :
- Ouvrez l'Invite de commandes et exécutez :
php -v
- Ouvrez l'Invite de commandes et exécutez :
Installer plusieurs versions de PHP
-
Répétez les étapes ci-dessus pour chaque version, en les plaçant dans un répertoire séparé (par ex.,
C:\php7
,C:\php8
). -
Changer entre les versions en ajustant la variable PATH système pour pointer vers le répertoire de la version souhaitée.
Ubuntu (20.04, 22.04, etc.)
Installer PHP en utilisant apt
-
Mettre à jour les listes de paquets :
- Ouvrez le Terminal et exécutez :
sudo apt update
- Ouvrez le Terminal et exécutez :
-
Installer PHP :
- Installer la dernière version de PHP :
sudo apt install php
- Pour installer une version spécifique, par exemple, PHP 8.1 :
sudo apt install php8.1
- Installer la dernière version de PHP :
-
Installer des modules supplémentaires (optionnel) :
- Par exemple, pour installer le support MySQL :
sudo apt install php8.1-mysql
- Par exemple, pour installer le support MySQL :
-
Changer entre les versions de PHP :
- Utilisez
update-alternatives
:sudo update-alternatives --set php /usr/bin/php8.1
- Utilisez
-
Vérifier la version installée :
- Exécutez :
php -v
- Exécutez :
Rocky Linux
Installer PHP en utilisant yum/dnf
-
Activer le dépôt EPEL :
- Ouvrez le Terminal et exécutez :
sudo dnf install epel-release
- Ouvrez le Terminal et exécutez :
-
Installer le dépôt Remi :
- Exécutez :
sudo dnf install https://rpms.remirepo.net/enterprise/remi-release-8.rpm sudo dnf module reset php
- Exécutez :
-
Installer PHP :
- Pour installer la version par défaut :
sudo dnf install php
- Pour installer une version spécifique, par exemple, PHP 7.4 :
sudo dnf module install php:remi-7.4
- Pour installer la version par défaut :
-
Changer entre les versions de PHP :
- Utilisez la commande de module
dnf
:sudo dnf module reset php sudo dnf module enable php:remi-8.0 sudo dnf install php
- Utilisez la commande de module
-
Vérifier la version installée :
- Exécutez :
php -v
- Exécutez :
Notes générales
- Pour les environnements de développement, il est important de configurer les paramètres PHP selon les exigences de votre projet.
- Lors du changement de versions de PHP, assurez-vous que toutes les extensions PHP pertinentes sont installées pour la version spécifique que vous prévoyez d'utiliser.
- Redémarrez votre serveur web (Apache, Nginx, etc.) après avoir changé de version de PHP ou mis à jour les configurations pour appliquer les changements.
Guides
Guides
Flight PHP est conçu pour être simple et puissant, et nos guides vous aideront à construire des applications du monde réel étape par étape. Ces tutoriels pratiques vous guident à travers des projets complets pour démontrer comment Flight peut être utilisé efficacement.
Guides officiels
Construire un blog
Apprenez à créer une application de blog fonctionnelle avec Flight PHP. Ce guide vous guide à travers :
- Configuration d'une structure de projet
- Travail avec des templates en utilisant Latte
- Mise en œuvre de routes pour les articles
- Stockage et récupération de données
- Gestion des soumissions de formulaires
- Gestion de base des erreurs
Ce tutoriel est parfait pour les débutants qui veulent voir comment toutes les pièces s'emboîtent dans une application réelle.
Tests unitaires et principes SOLID
Ce guide couvre les fondamentaux des tests unitaires dans les applications Flight PHP. Il inclut :
- Configuration de PHPUnit
- Écriture de code testable en utilisant les principes SOLID
- Mocking des dépendances
- Pièges courants à éviter
- Échelle de vos tests au fur et à mesure que votre application grandit Ce tutoriel est idéal pour les développeurs cherchant à améliorer la qualité et la maintenabilité du code.
Guides non officiels
Bien que ces guides ne soient pas officiellement maintenus par l'équipe Flight, ce sont des ressources précieuses créées par la communauté. Ils couvrent divers sujets et cas d'utilisation, fournissant des insights supplémentaires sur l'utilisation de Flight PHP.
Créer une API RESTful avec Flight Framework
Ce guide vous guide à travers la création d'une API RESTful en utilisant le framework Flight PHP. Il couvre les bases de la configuration d'une API, de la définition de routes et du retour de réponses JSON.
Construire un simple blog
Ce guide vous guide à travers la création d'un blog de base en utilisant le framework Flight PHP. Il comporte en fait 2 parties : une pour couvrir les bases et l'autre pour aborder des sujets plus avancés et des raffinements pour un blog prêt pour la production.
- Construire un simple blog avec Flight - Partie 1 - Pour débuter avec un simple blog.
- Construire un simple blog avec Flight - Partie 2 - Raffiner le blog pour la production.
Construire une API Pokémon en PHP : Un guide pour débutants
Ce guide amusant vous guide à travers la création d'une simple API Pokémon en utilisant Flight PHP. Il couvre les bases de la configuration d'une API, de la définition de routes et du retour de réponses JSON.
Contribuer
Vous avez une idée pour un guide ? Vous avez trouvé une erreur ? Nous accueillons les contributions ! Nos guides sont maintenus dans le dépôt de documentation FlightPHP.
Si vous avez construit quelque chose d'intéressant avec Flight et que vous souhaitez le partager en tant que guide, veuillez soumettre une pull request. Partager vos connaissances aide la communauté Flight à grandir.
À la recherche de la documentation API ?
Si vous cherchez des informations spécifiques sur les fonctionnalités et méthodes principales de Flight, consultez la section Apprendre de notre documentation.