Learn

Apprendre à Propos de Flight

Flight est un framework rapide, simple et extensible pour PHP. Il est très polyvalent et peut être utilisé pour construire tous types d'applications web. Il est conçu avec la simplicité à l'esprit et est écrit d'une manière facile à comprendre et à utiliser.

Concepts Importants du Framework

Pourquoi un Framework?

Voici un court article sur pourquoi vous devriez utiliser un framework. Il est important de comprendre les avantages de l'utilisation d'un framework avant de commencer à en utiliser un.

De plus, un excellent tutoriel a été créé par @lubiana. Bien qu'il n'aille pas en détail sur Flight spécifiquement, ce guide vous aidera à comprendre certains des principaux concepts entourant un framework et pourquoi ils sont bénéfiques à utiliser. Vous pouvez trouver le tutoriel ici.

Sujets Principaux

Chargement Automatique

Apprenez à charger automatiquement vos propres classes dans votre application.

Routage

Apprenez à gérer les itinéraires pour votre application web. Cela inclut également la regroupement des routes, les paramètres des routes et les middleware.

Middleware

Apprenez à utiliser les middleware pour filtrer les requêtes et réponses dans votre application.

Requêtes

Apprenez à gérer les requêtes et 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.

Étendre Flight

Apprenez à étendre le framework en ajoutant vos propres méthodes et classes.

Événements et Filtrage

Apprenez à utiliser le système d'événements pour ajouter des hooks à vos méthodes et aux méthodes internes du framework.

Conteneur d'Injection de Dépendances

Apprenez à utiliser les conteneurs d'injection de dépendances (DIC) pour gérer les dépendances de votre application.

API du Framework

Renseignez-vous sur les méthodes principales du framework.

Migration vers v3

La compatibilité ascendante a été en grande partie maintenue, mais il y a quelques changements dont vous devriez être conscient lors de la migration de la v2 à la v3.

Dépannage

Il y a des problèmes courants auxquels vous pourriez être confrontés lors de l'utilisation de Flight. Cette page vous aidera à résoudre ces problèmes.

Learn/stopping

Arrêt

Vous pouvez arrêter le cadre à tout moment en appelant la méthode halt:

Flight::halt();

Vous pouvez également spécifier un code d'état HTTP et un message facultatif:

Flight::halt(200, 'Je reviens bientôt...');

Appeler halt permettra de supprimer tout contenu de réponse jusqu'à ce point. Si vous souhaitez arrêter le cadre et afficher la réponse actuelle, utilisez la méthode stop:

Flight::stop();

Learn/errorhandling

Gestion des erreurs

Erreurs et exceptions

Toutes les erreurs et exceptions sont capturées par Flight et transmises à la méthode error. Le comportement par défaut est d'envoyer une réponse générique d'erreur de serveur interne HTTP 500 avec des informations sur l'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 enregistrées sur le serveur web. Vous pouvez activer cela en modifiant la configuration:

Flight::set('flight.log_errors', true);

Non trouvé

Lorsqu'une URL ne peut être trouvée, Flight appelle la méthode notFound. Le comportement par défaut est d'envoyer une réponse HTTP 404 Non trouvé avec un message simple.

Vous pouvez remplacer ce comportement pour vos propres besoins:

Flight::map('notFound', function () {
  // Gérer non trouvé
});

Learn/migrating_to_v3

Migration vers v3

La compatibilité ascendante a été en grande partie maintenue, mais il y a quelques changements dont vous devez être conscient lorsque vous migrez de v2 à v3.

Comportement du tampon de sortie (3.5.0)

La mise en tampon de sortie 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) casse le modèle MVC. Ce changement vise à être plus en phase avec le modèle MVC et à rendre le framework plus prévisible et plus facile à utiliser.

En v2, la mise en tampon de sortie était gérée de manière à ce qu'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 en réalité peut-être pas. Cependant, si vous faites un écho de contenu en dehors des rappels et des contrôleurs (par exemple dans un crochet), vous risquez probablement de rencontrer des problèmes. Émettre du contenu dans des crochets, et avant que le framework ne s'exécute réellement, a pu fonctionner dans le passé, mais cela ne fonctionnera pas à l'avenir.

Où vous pourriez rencontrer des problèmes

// index.php
require 'vendor/autoload.php';

// juste un exemple
define('HEURE_DE_DEBUT', microtime(true));

function bonjour() {
    echo 'Bonjour le monde';
}

Flight::map('bonjour', 'bonjour');
Flight::after('bonjour', function(){
    // cela fonctionnera en fait
    echo '<p>Cette phrase Bonjour le monde vous est offerte par la lettre "B"</p>';
});

Flight::before('start', function(){
    // des choses comme ça provoqueront une erreur
    echo '<html><head><title>Ma page</title></head><body>';
});

Flight::route('/', function(){
    // cela va en fait bien
    echo 'Bonjour le monde';

    // Ceci devrait également bien fonctionner
    Flight::bonjour();
});

Flight::after('start', function(){
    // cela provoquera une erreur
    echo '<div>Votre page s'est chargée en '.(microtime(true) - HEURE_DE_DEBUT).' secondes</div></body></html>';
});

Activer le comportement de rendu v2

Pouvez-vous toujours garder votre ancien code tel qu'il est sans le réécrire 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 sur true. Cela vous permettra de continuer à utiliser l'ancien comportement de rendu, mais il est recommandé de le corriger pour l'avenir. Dans la v4 du framework, cela sera supprimé.

// index.php
require 'vendor/autoload.php';

Flight::set('flight.v2.output_buffering', true);

Flight::before('start', function(){
    // Maintenant cela fonctionnera très bien
    echo '<html><head><title>Ma page</title></head><body>';
});

// plus de code

Changements du Dispatcher (3.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 pas 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 de manière plus facile. Si vous devez invoquer une méthode similaire à la façon dont Dispatcher le faisait, vous pouvez utiliser manuellement quelque chose comme $result = $class->$method(...$params); ou call_user_func_array() à la place.

Learn/configuration

Configuration

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);

Paramètres de configuration disponibles

Voici la liste de tous les paramètres de configuration disponibles :

Variables

Flight vous permet de sauvegarder des variables afin qu'elles puissent être utilisées n'importe où dans votre application.

// Sauvegarde de votre variable
Flight::set('id', 123);

// Ailleurs dans votre application
$id = Flight::get('id');

Pour vérifier 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();

Flight utilise également des variables à des fins de configuration.

Flight::set('flight.log_errors', true);

Gestion des erreurs

Erreurs et Exceptions

Toutes les erreurs et exceptions sont capturées par Flight et transmises à la méthode error. Le comportement par défaut est d'envoyer une réponse générique HTTP 500 Internal Server Error avec des informations sur l'erreur.

Vous pouvez remplacer ce comportement selon vos besoins :

Flight::map('error', function (Throwable $error) {
  // Gérer l'erreur
  echo $error->getTraceAsString();
});

Par défaut, les erreurs ne sont pas enregistrées dans le serveur web. Vous pouvez activer cela en modifiant la configuration :

Flight::set('flight.log_errors', true);

Non trouvé

Lorsqu'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 selon vos besoins :

Flight::map('notFound', function () {
  // Gérer le non trouvé
});

Learn/security

Sécurité

La sécurité est primordiale lorsqu'il s'agit d'applications web. Vous voulez vous assurer que votre application est sécurisée et que les données de vos utilisateurs sont en sécurité. Flight offre un certain nombre de fonctionnalités pour vous aider à sécuriser vos applications web.

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 détournement de clic, les attaques XSS et autres. 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.

Ajouter Manuellement

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 détournement de clic
Flight::response()->header('X-Frame-Options', 'SAMEORIGIN');

// Définir l'en-tête Content-Security-Policy pour prévenir les attaques XSS
// Remarque : cet en-tête peut devenir très complexe, vous voudrez
// 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 les attaques 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 les informations de référence 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 les API qui peuvent être utilisées
Flight::response()->header('Permissions-Policy', 'geolocation=()');

Ces en-têtes peuvent être ajoutés en haut de vos fichiers bootstrap.php ou index.php.

Ajouter 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=()');
});

Ajouter en tant que Middleware

Vous pouvez également les ajouter en tant que classe de middleware. C'est une bonne façon de garder votre code propre et organisé.

// app/middleware/SecurityHeadersMiddleware.php

namespace app\middleware;

class SecurityHeadersMiddleware
{
    public function before(array $params): void
    {
        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=()');
    }
}

// index.php ou partout où vous avez vos routes
// À noter, ce groupe à 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
// ceci uniquement à des routes spécifiques.
Flight::group('', function(Router $router) {
    $router->get('/users', [ 'UserController', 'getUsers' ]);
    // plus de routes
}, [ new SecurityHeadersMiddleware() ]);

Cross Site Request Forgery (CSRF)

La falsification de requête intersite (CSRF) est un type d'attaque où un site web malveillant peut faire envoyer une requête au site web de l'utilisateur par le biais de son navigateur. 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 mettre en œuvre le vôtre en utilisant un middleware.

Configuration

Tout 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 lorsque le formulaire est soumis.

// 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'ayez attaché à Flight)
// Vous n'avez besoin de générer qu'un seul jeton par session (afin qu'il fonctionne
// à travers 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)) );
}
<!-- Utiliser le jeton CSRF dans votre formulaire -->
<form method="post">
    <input type="hidden" name="csrf_token" value="<?= Flight::session()->get('csrf_token') ?>">
    <!-- autres champs de formulaire -->
</form>

Utilisation de Latte

Vous pouvez également définir une fonction personnalisée pour afficher le jeton CSRF dans vos templates Latte.

// Définir une fonction personnalisée pour afficher le jeton CSRF
// Remarque: la vue a été configurée avec Latte comme moteur de vue
Flight::view()->addFunction('csrf', function() {
    $csrfToken = Flight::session()->get('csrf_token');
    return new \Latte\Runtime\Html('<input type="hidden" name="csrf_token" value="' . $csrfToken . '">');
});

Et maintenant dans vos templates Latte, vous pouvez utiliser la fonction csrf() pour afficher le jeton CSRF.

<form method="post">
    {csrf()}
    <!-- autres champs de formulaire -->
</form>

Court et simple, n'est-ce pas ?

Vérifier le Jeton CSRF

Vous pouvez vérifier le jeton CSRF en utilisant des filtres d'événement :

// 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 vous pouvez utiliser une classe de middleware :

// app/middleware/CsrfMiddleware.php

namespace app\middleware;

class CsrfMiddleware
{
    public function before(array $params): void
    {
        if(Flight::request()->method == 'POST') {
            $token = Flight::request()->data->csrf_token;
            if($token !== Flight::session()->get('csrf_token')) {
                Flight::halt(403, 'Jeton CSRF invalide');
            }
        }
    }
}

// index.php ou partout où vous avez vos routes
Flight::group('', function(Router $router) {
    $router->get('/users', [ 'UserController', 'getUsers' ]);
    // plus de routes
}, [ new CsrfMiddleware() ]);

Cross Site Scripting (XSS)

La contamination de script intersite (XSS) est un type d'attaque où un site web malveillant peut injecter du code dans votre site web. La plupart de ces opportunités viennent des valeurs des formulaires que vos utilisateurs rempliront. Vous ne devriez jamais faire confiance à la sortie de vos utilisateurs ! Supposez toujours qu'ils sont les meilleurs hackeurs du monde. Ils peuvent injecter du code JavaScript ou 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 de vue de Flight, vous pouvez facilement échapper la sortie pour prévenir les attaques XSS.

// Supposons que l'utilisateur est ingénieux et essaye d'utiliser ceci comme nom
$name = '<script>alert("XSS")</script>';

// Cela échappera la sortie
Flight::view()->set('name', $name);
// Cela produira : &lt;script&gt;alert(&quot;XSS&quot;)&lt;/script&gt;

// Si vous utilisez quelque chose comme Latte enregistré en tant que classe de vue, cela échappera également automatiquement cela.
Flight::view()->render('template', ['name' => $name]);

Injection SQL

L'injection SQL 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 devriez jamais faire confiance à l'entrée de vos utilisateurs ! Supposons toujours qu'ils sont à l'affût. Vous pouvez utiliser des instructions préparées dans vos objets PDO pour prévenir les injections SQL.

// En supposant que vous avez Flight::db() enregistré en tant qu'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, ceci peut facilement être fait en une seule 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 espaces réservés ?
$statement = Flight::db()->fetchAll('SELECT * FROM users WHERE username = ?', [ $username ]);

// Promettez juste que vous ne ferez jamais JAMAIS quelque chose comme ça...
$users = Flight::db()->fetchAll("SELECT * FROM users WHERE username = '{$username}' LIMIT 5");
// parce que que se passe-t-il si $username = "' OR 1=1; -- "; 
// Après la construction de la requête elle ressemble à cela
// 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 par injection SQL très courante qui renverra tous les utilisateurs.

CORS

Le partage de ressources inter-origines (CORS) est un mécanisme qui permet à de nombreuses ressources (par ex., polices, JavaScript, etc.) sur une page web d'être demandées à partir d'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 pour s'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 partout où vous avez vos routes
$CorsUtil = new CorsUtil();
Flight::before('start', [ $CorsUtil, 'setupCors' ]);

Conclusion

La sécurité est primordiale et il est important de veiller à ce que vos applications web soient sécurisées. Flight offre un certain nombre de fonctionnalités pour vous aider à sécuriser vos applications web, mais il est important de rester vigilant et de tout mettre en œuvre pour protéger les données de vos utilisateurs. Supposez toujours le pire et ne faites jamais confiance à l'entrée de vos utilisateurs. Échappez toujours la sortie et utilisez des instructions préparées pour prévenir les injections SQL. Utilisez toujours des middlewares pour protéger vos routes contre les attaques CSRF et CORS. Si vous faites tout cela, vous serez bien parti pour construire des applications web sécurisées.

Learn/overriding

Substitution

Flight vous permet de substituer sa fonctionnalité par défaut pour répondre à vos propres besoins, sans avoir à modifier aucun code.

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 substituer ce comportement en utilisant la méthode map :

Flight::map('notFound', function() {
  // Afficher une page d'erreur 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 :

// Enregistrer votre classe personnalisée
Flight::register('router', MaClasseRouter::class);

// Lorsque Flight charge l'instance du routeur, il chargera votre classe
$monrouteur = Flight::router();

Cependant, les méthodes du framework telles que map et register ne peuvent pas être substituées. Vous obtiendrez une erreur si vous essayez de le faire.

Learn/routing

Routage

Remarque : Vous souhaitez en savoir plus sur le routage ? Consultez la page "pourquoi un framework ?" pour une explication plus approfondie.

Le routage de base dans Flight est réalisé en faisant correspondre un modèle d'URL à une fonction de rappel ou à un tableau d'une classe et d'une méthode.

Flight::route('/', function(){
    echo 'bonjour le monde !';
});

Les routes sont appariées dans l'ordre où elles sont définies. La première route à correspondre à une requête sera invoquée.

Rappels/Fonctions

Le rappel peut être n'importe quel objet appelable. Ainsi, vous pouvez utiliser une fonction régulière :

function bonjour(){
    echo 'bonjour le monde !';
}

Flight::route('/', 'bonjour');

Classes

Vous pouvez également utiliser une méthode statique d'une classe :

class Salutation {
    public static function bonjour() {
        echo 'bonjour le monde !';
    }
}

Flight::route('/', [ 'Salutation','bonjour' ]);

Ou en créant d'abord un objet puis en appelant la méthode :


// Greeting.php
class Salutation
{
    public function __construct() {
        $this->name = 'Jean Dupont';
    }

    public function bonjour() {
        echo "Bonjour, {$this->name}!";
    }
}

// index.php
$salutation = new Salutation();

Flight::route('/', [ $salutation, 'bonjour' ]);
// Vous pouvez également le faire sans créer l'objet en premier
// Remarque : Aucun argument ne sera injecté dans le constructeur
Flight::route('/', [ 'Salutation', 'bonjour' ]);

Injection de Dépendances via DIC (Container d'Injection de Dépendances)

Si vous souhaitez 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, ou 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 Salutation
{
    protected PdoWrapper $pdoWrapper;
    public function __construct(PdoWrapper $pdoWrapper) {
        $this->pdoWrapper = $pdoWrapper;
    }

    public function bonjour(int $id) {
        // faire quelque chose avec $this->pdoWrapper
        $name = $this->pdoWrapper->fetchField("SELECT nom FROM utilisateurs WHERE id = ?", [ $id ]);
        echo "Bonjour, le monde ! Mon nom est {$name} !";
    }
}

// index.php

// Configurer le conteneur avec les paramètres dont vous avez besoin
// Consultez la page d'injection de dépendances pour plus d'informations sur PSR-11
$dice = new \Dice\Dice();

// N'oubliez pas de réaffecter la variable avec '$dice = '!!!!!
$dice = $dice->addRule('flight\database\PdoWrapper', [
    'shared' => true,
    'constructParams' => [ 
        'mysql:host=localhost;nom_bd=test', 
        'root',
        'mot_de_passe'
    ]
]);

// Enregistrer le gestionnaire de conteneur
Flight::registerContainerHandler(function($class, $params) use ($dice) {
    return $dice->create($class, $params);
});

// Routes comme d'habitude
Flight::route('/bonjour/@id', [ 'Salutation', 'bonjour' ]);
// ou
Flight::route('/bonjour/@id', 'Salutation->bonjour');
// ou
Flight::route('/bonjour/@id', 'Salutation::bonjour');

Flight::start();

Routage par Méthode

Par défaut, les modèles de route sont appariés contre 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 'J'ai reçu une requête GET.';
});

Flight::route('POST /', function () {
  echo 'J'ai reçu une requête POST.';
});

// 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 vers un seul rappel en utilisant un délimiteur | :

Flight::route('GET|POST /', function () {
  echo 'J'ai reçu une requête GET ou POST.';
});

De plus, vous pouvez obtenir l'objet Router qui possède certaines méthodes d'aide que vous pouvez utiliser :


$router = Flight::router();

// mappe toutes les méthodes
$router->map('/', function() {
    echo 'bonjour le monde!';
});

// Requête GET
$router->get('/utilisateurs', function() {
    echo 'utilisateurs';
});
// $router->post();
// $router->put();
// $router->delete();
// $router->patch();

Expressions Régulières

Vous pouvez utiliser des expressions régulières dans vos routes :

Flight::route('/utilisateur/[0-9]+', function () {
  // Cela correspondra à /utilisateur/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 transmis à votre fonction de rappel.

Flight::route('/@nom/@id', function (string $nom, string $id) {
  echo "bonjour, $nom ($id)!";
});

Vous pouvez également inclure des expressions régulières avec vos paramètres nommés en utilisant le délimiteur : :

Flight::route('/@nom/@id:[0-9]{3}', function (string $nom, string $id) {
  // Cela correspondra à /bob/123
  // Mais ne correspondra pas à /bob/12345
});

Remarque : Il n'est pas possible de faire correspondre les groupes regex () avec des paramètres nommés. :'(

Paramètres Optionnels

Vous pouvez spécifier des paramètres nommés optionnels pour la correspondance en enveloppant les segments entre parenthèses.

Flight::route(
  '/blog(/@annee(/@mois(/@jour)))',
  function(?string $annee, ?string $mois, ?string $jour) {
    // Cela correspondra aux URL suivantes :
    // /blog/2012/12/10
    // /blog/2012/12
    // /blog/2012
    // /blog
  }
);

Tous les paramètres optionnels qui ne sont pas mis en correspondance seront transmis en tant que NULL.

Jokers

La correspondance est effectuée uniquement sur des segments d'URL individuels. Si vous souhaitez faire correspondre plusieurs segments, vous pouvez utiliser le joker *.

Flight::route('/blog/*', function () {
  // Cela correspondra à /blog/2000/02/01
});

Pour faire correspondre toutes les demandes à un seul rappel, vous pouvez faire :

Flight::route('*', function () {
  // Faire quelque chose
});

Passage

Vous pouvez passer l'exécution à la route correspondante suivante en renvoyant true à partir de votre fonction de rappel.

Flight::route('/utilisateur/@nom', function (string $nom) {
  // Vérifiez une certaine condition
  if ($nom !== "Bob") {
    // Continuer vers la route suivante
    return true;
  }
});

Flight::route('/utilisateur/*', function () {
  // Cela sera appelé
});

Alias de Route

Vous pouvez attribuer un alias à une route, de sorte que l'URL puisse être générée dynamiquement plus tard dans votre code (comme un modèle par exemple).

Flight::route('/utilisateurs/@id', function($id) { echo 'utilisateur:'.$id; }, false, 'vue_utilisateur');

// plus tard dans le code quelque part
Flight::getUrl('vue_utilisateur', [ 'id' => 5 ]); // retournera '/utilisateurs/5'

Cela est particulièrement utile si votre URL change. Dans l'exemple ci-dessus, disons que les utilisateurs ont été déplacés vers /admin/utilisateurs/@id à la place. Avec l'alias, vous n'avez pas à changer partout où vous référencez l'alias car l'alias retournera maintenant /admin/utilisateurs/5 comme dans l'exemple ci-dessus.

L'aliasing de route fonctionne également en groupe :

Flight::group('/utilisateurs', function() {
    Flight::route('/@id', function($id) { echo 'utilisateur:'.$id; }, false, 'vue_utilisateur');
});

// plus tard dans le code quelque part
Flight::getUrl('vue_utilisateur', [ 'id' => 5 ]); // retournera '/utilisateurs/5'

Informations sur la Route

Si vous souhaitez inspecter les informations de correspondance de la route, vous pouvez demander que l'objet route soit transmis à votre fonction de rappel en passant true comme troisième paramètre dans la méthode de route. L'objet route sera toujours le dernier paramètre transmis à votre fonction de rappel.

Flight::route('/', function(\flight\net\Route $route) {
  // Tableau des méthodes HTTP correspondant
  $route->methods;

  // Tableau des paramètres nommés
  $route->params;

  // Expression régulière de correspondance
  $route->regex;

  // Contient le contenu de tout '*' utilisé dans le modèle d'URL
  $route->splat;

  // Affiche le chemin d'URL....si vous en avez vraiment besoin
  $route->pattern;

  // Affiche quels middleware sont assignés à ceci
  $route->middleware;

  // Affiche l'alias assigné à cette route
  $route->alias;
}, true);

Regrouper des Routes

Il peut arriver que vous souhaitiez regrouper des routes associées ensemble (comme /api/v1). Vous pouvez le faire en utilisant la méthode group :

Flight::group('/api/v1', function () {
  Flight::route('/utilisateurs', function () {
    // Correspond à /api/v1/utilisateurs
  });

  Flight::route('/articles', function () {
    // Correspond à /api/v1/articles
  });
});

Vous pouvez même imbriquer des groupes de groupes :

Flight::group('/api', function () {
  Flight::group('/v1', function () {
    // Flight::get() obtient des variables, il ne définit pas une route ! Voir le contexte de l'objet ci-dessous
    Flight::route('GET /utilisateurs', function () {
      // Correspond à GET /api/v1/utilisateurs
    });

    Flight::post('/articles', function () {
      // Correspond à POST /api/v1/articles
    });

    Flight::put('/articles/1', function () {
      // Correspond à PUT /api/v1/articles
    });
  });
  Flight::group('/v2', function () {

    // Flight::get() obtient des variables, il ne définit pas une route ! Voir le contexte de l'objet ci-dessous
    Flight::route('GET /utilisateurs', function () {
      // Correspond à GET /api/v2/utilisateurs
    });
  });
});

Regroupement avec Contexte d'Objet

Vous pouvez toujours utiliser le regroupement de routes avec l'objet Engine de la manière suivante :

$app = new \flight\Engine();
$app->group('/api/v1', function (Router $router) {

  // utilisez la variable $router
  $router->get('/utilisateurs', function () {
    // Correspond à GET /api/v1/utilisateurs
  });

  $router->post('/articles', function () {
    // Correspond à POST /api/v1/articles
  });
});

Flux

Vous pouvez désormais diffuser des réponses au client en utilisant la méthode streamWithHeaders(). Cela est utile pour envoyer de gros fichiers, des processus longs ou générer de grandes réponses. Diffuser une route est géré un peu différemment qu'une route régulière.

Remarque : La diffusion de réponses n'est disponible que si vous avez défini flight.v2.output_buffering sur false.

Flux 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 toutes les méthodes manuellement avant de renvoyer quelque chose au client. Cela se fait avec la fonction header() de PHP ou la méthode Flight::response()->setRealHeader().

Flight::route('/@nomfichier', function($nomfichier) {

    // évidemment, vous devez nettoyer le chemin et tout.
    $nomFichierSecurise = basename($nomfichier);

    // Si vous avez des en-têtes supplémentaires à définir ici après l'exécution de la route
    // vous devez les définir avant de renvoyer quoi que ce soit en sortie.
    // Ils doivent tous être un appel brut à la fonction header() ou 
    // un appel à la méthode Flight::response()->setRealHeader()
    header('Content-Disposition: attachment; filename="'.$nomFichierSecurise.'"');
    // ou
    Flight::response()->setRealHeader('Content-Disposition', 'attachment; filename="'.$nomFichierSecurise.'"');

    $donneesFichier = file_get_contents('/chemin/vers/les/fichiers/'.$nomFichierSecurise);

    // Gestion des erreurs et autres
    if(empty($donneesFichier)) {
        Flight::halt(404, 'Fichier non trouvé');
    }

    // définir manuellement la longueur du contenu si vous le souhaitez
    header('Content-Length: '.filesize($nomfichier));

    // Diffuser les données vers le client
    echo $donneesFichier;

// C'est la ligne magique ici
})->stream();

Flux 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('/utilisateurs-en-flux', function() {

    // vous pouvez ajouter d'autres en-têtes que vous souhaitez ici
    // vous devez simplement utiliser header() ou Flight::response()->setRealHeader()

    // cependant vous obtenez vos données, juste à titre d'exemple...
    $utilisateurs_stmt = Flight::db()->query("SELECT id, prenom, nom FROM utilisateurs");

    echo '{';
    $nombreUtilisateurs = count($utilisateurs);
    while($utilisateur = $users_stmt->fetch(PDO::FETCH_ASSOC)) {
        echo json_encode($utilisateur);
        if(--$nombreUtilisateurs > 0) {
            echo ',';
        }

        // Ceci est nécessaire 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="utilisateurs.json"',
    // code d'état optionnel, par défaut à 200
    'status' => 200
]);

Learn/variables

Variables

Flight permet de sauvegarder des variables afin qu'elles puissent être utilisées n'importe où dans votre application.

// Sauvegardez votre variable
Flight::set('id', 123);

// Ailleurs dans votre application
$id = Flight::get('id');

Pour vérifier 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();

Flight utilise également des variables à des fins de configuration.

Flight::set('flight.log_errors', true);

Learn/dependency_injection_container

Conteneur d'injection de dépendances

Introduction

Le Conteneur d'injection de dépendances (DIC) est un outil puissant qui vous permet de gérer les dépendances de votre application. C'est un concept clé dans les frameworks PHP modernes et est utilisé pour gérer l'instanciation et la configuration des objets. Quelques exemples de bibliothèques DIC sont : Dice, Pimple, PHP-DI et league/container.

Un DIC est un moyen sophistiqué de dire qu'il vous permet de créer et de gérer vos classes dans un emplacement centralisé. C'est utile lorsque vous avez besoin de passer le même objet à plusieurs classes (comme vos contrôleurs). Un exemple simple pourrait aider à mieux comprendre cela.

Exemple de base

L'ancienne manière de faire ressemblait à ceci :


require 'vendor/autoload.php';

// classe pour gérer les utilisateurs de la base de données
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());
    }
}

$User = new UserController(new PDO('mysql:host=localhost;dbname=test', 'user', 'pass'));
Flight::route('/user/@id', [ $UserController, 'view' ]);

Flight::start();

Vous pouvez voir dans le code ci-dessus que nous créons un nouvel objet PDO et le passons à notre classe UserController. C'est bien pour une petite application, mais à mesure que votre application grandit, vous constaterez que vous créez le même objet PDO à plusieurs endroits. C'est là qu'un DIC s'avère utile.

Voici le même exemple utilisant un DIC (en utilisant Dice) :


require 'vendor/autoload.php';

// même classe que ci-dessus. Rien n'a changé
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());
    }
}

// créer un nouveau conteneur
$container = new \Dice\Dice;
// n'oubliez pas de le réassigner 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' ]
]);

// Cela enregistre le gestionnaire de conteneur pour que Flight sache l'utiliser.
Flight::registerContainerHandler(function($class, $params) use ($container) {
    return $container->create($class, $params);
});

// maintenant nous pouvons utiliser le conteneur pour créer notre UserController
Flight::route('/user/@id', [ 'UserController', 'view' ]);
// ou alternativement vous pouvez définir la route comme ceci
Flight::route('/user/@id', 'UserController->view');
// ou
Flight::route('/user/@id', 'UserController::view');

Flight::start();

Je parie que vous pouvez penser qu'il y a beaucoup de code supplémentaire ajouté à l'exemple. La magie opère lorsque vous avez un autre contrôleur qui a besoin de l'objet PDO.


// Si tous vos contrôleurs ont un constructeur qui a besoin d'un objet PDO
// chacune des routes ci-dessous l'injectera automatiquement !!!
Flight::route('/company/@id', 'CompanyController->view');
Flight::route('/organization/@id', 'OrganizationController->view');
Flight::route('/category/@id', 'CategoryController->view');
Flight::route('/settings', 'SettingsController->view');

Le bonus supplémentaire d'utiliser un DIC est que les tests unitaires deviennent beaucoup plus faciles. Vous pouvez créer un objet simulé et le passer à votre classe. C'est un énorme avantage lorsque vous rédigez des tests pour votre application!

PSR-11

Flight peut également utiliser n'importe quel conteneur compatible avec PSR-11. Cela signifie que vous pouvez utiliser n'importe un conteneur qui implémente l'interface PSR-11. Voici un exemple utilisant le conteneur PSR-11 de League :


require 'vendor/autoload.php';

// même classe UserController que ci-dessus

$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();

Bien que cela puisse être un peu plus verbeux que l'exemple Dice précédent, cela fonctionne tout de même avec les mêmes avantages!

Gestionnaire DIC personnalisé

Vous pouvez également créer votre propre gestionnaire DIC. C'est utile si vous avez un conteneur personnalisé que vous voulez utiliser qui n'est pas PSR-11 (Dice). Consultez l' exemple de base pour savoir comment faire cela.

De plus, il y a quelques valeurs par défaut utiles qui faciliteront votre vie lors de l'utilisation de Flight.

Instance du moteur

Si vous utilisez l'instance Engine dans vos contrôleurs/middleware, voici comment vous la configureriez :


// Quelque part dans votre fichier d'amorçage
$engine = Flight::app();

$container = new \Dice\Dice;
$container = $container->addRule('*', [
    'substitutions' => [
        // C'est ici que vous passez l'instance
        Engine::class => $engine
    ]
]);

$engine->registerContainerHandler(function($class, $params) use ($container) {
    return $container->create($class, $params);
});

// Maintenant vous pouvez utiliser l'instance Engine dans vos contrôleurs/middleware

class MyController {
    public function __construct(Engine $app) {
        $this->app = $app;
    }

    public function index() {
        $this->app->render('index');
    }
}

Ajout d'autres classes

Si vous avez d'autres classes que vous voulez ajouter au conteneur, avec Dice c'est facile car elles seront automatiquement résolues par le conteneur. Voici un exemple :


$container = new \Dice\Dice;
// Si vous n'avez pas besoin d'injecter quoi que ce soit dans votre classe
// vous n'avez pas besoin de définir quoi que ce soit !
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');

Learn/middleware

Middleware de Route

Flight prend en charge le middleware de route et de groupe de route. Le middleware est une fonction qui est exécutée avant (ou après) le rappel de la route. C'est un excellent moyen d'ajouter des vérifications d'authentification API dans votre code, ou de valider si l'utilisateur a la permission d'accéder à la route.

Middleware Basique

Voici un exemple basique:

// Si vous fournissez uniquement une fonction anonyme, elle sera exécutée avant le rappel de la route.
// Il n'y a pas de fonctions de middleware "après" sauf pour les classes (voir ci-dessous)
Flight::route('/chemin', function() { echo 'Je suis là!'; })->addMiddleware(function() {
    echo 'Middleware en premier!';
});

Flight::start();

// Cela affichera "Middleware en premier! Je suis là!"

Il y a quelques notes très importantes sur le middleware que vous devez connaître avant de les utiliser:

Classes de Middleware

Le middleware peut également être enregistré en tant que classe. Si vous avez besoin de la fonctionnalité "après", vous devez utiliser une classe.

class MyMiddleware {
    public function before($params) {
        echo 'Middleware en premier!';
    }

    public function after($params) {
        echo 'Middleware en dernier!';
    }
}

$MyMiddleware = new MyMiddleware();
Flight::route('/chemin', function() { echo 'Je suis là!'; })->addMiddleware($MyMiddleware); // également ->addMiddleware([ $MyMiddleware, $MyMiddleware2 ]);

Flight::start();

// Cela affichera "Middleware en premier! Je suis là! Middleware en dernier!"

Gestion des Erreurs de Middleware

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 quelques options à votre disposition:

  1. Vous pouvez renvoyer false depuis la fonction de middleware et Flight renverra automatiquement une erreur 403 Forbidden, mais sans personnalisation.
  2. Vous pouvez rediriger l'utilisateur vers une page de connexion en utilisant Flight::redirect().
  3. Vous pouvez créer une erreur personnalisée dans le middleware et arrêter l'exécution de la route.

Exemple Basique

Voici un exemple simple avec un return false;:

class MyMiddleware {
    public function before($params) {
        if (isset($_SESSION['user']) === false) {
            return false;
        }

        // puisque c'est vrai, tout continue normalement
    }
}

Exemple de Redirection

Voici un exemple de redirection de l'utilisateur vers une page de connexion:

class MyMiddleware {
    public function before($params) {
        if (isset($_SESSION['user']) === false) {
            Flight::redirect('/login');
            exit;
        }
    }
}

Exemple d'Erreur Personnalisée

Disons que vous devez renvoyer une erreur JSON car vous créez une API. Vous pouvez le faire comme ceci:

class MyMiddleware {
    public function before($params) {
        $authorization = Flight::request()->headers['Authorization'];
        if(empty($authorization)) {
            Flight::json(['error' => 'Vous devez être connecté pour accéder à cette page.'], 403);
            exit;
            // ou
            Flight::halt(403, json_encode(['error' => 'Vous devez être connecté pour accéder à cette page.']);
        }
    }
}

Groupement de Middleware

Vous pouvez ajouter un groupe de route, et alors chaque route de ce groupe aura également le même middleware. C'est utile si vous devez regrouper un ensemble de routes avec un middleware d'Auth pour vérifier la clé API dans l'en-tête.


// ajouté à la fin de la méthode de groupe
Flight::group('/api', function() {

    // Cette route en apparence "vide" correspondra en réalité à /api
    Flight::route('', function() { echo 'api'; }, false, 'api');
    Flight::route('/utilisateurs', function() { echo 'utilisateurs'; }, false, 'utilisateurs');
    Flight::route('/utilisateurs/@id', function($id) { echo 'utilisateur:'.$id; }, false, 'vue_utilisateur');
}, [ new ApiAuthMiddleware() ]);

Si vous souhaitez appliquer un middleware global à toutes vos routes, vous pouvez ajouter un groupe "vide":


// ajouté à la fin de la méthode de groupe
Flight::group('', function() {
    Flight::route('/utilisateurs', function() { echo 'utilisateurs'; }, false, 'utilisateurs');
    Flight::route('/utilisateurs/@id', function($id) { echo 'utilisateur:'.$id; }, false, 'vue_utilisateur');
}, [ new ApiAuthMiddleware() ]);

Learn/filtering

Filtrage

Flight vous permet de filtrer les méthodes avant et après leur appel. Il n'y a pas de crochets prédéfinis que vous devez mémoriser. Vous pouvez filtrer n'importe quelle des méthodes par défaut du framework ainsi que n'importe quelle méthode personnalisée que vous avez mappée.

Une fonction de filtre ressemble à ceci :

function (array &$params, string &$output): bool {
  // Code de filtre
}

En utilisant les variables passées en paramètre, 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 {
  // Faire 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 {
  // Faire quelque chose
});

Vous pouvez ajouter autant de filtres que vous le souhaitez à 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 :

// Mapper une méthode personnalisée
Flight::map('hello', function (string $name) {
  return "Bonjour, $name!";
});

// Ajouter un filtre avant
Flight::before('hello', function (array &$params, string &$output): bool {
  // Manipuler le paramètre
  $params[0] = 'Fred';
  return true;
});

// Ajouter un filtre après
Flight::after('hello', function (array &$params, string &$output): bool {
  // Manipuler la sortie
  $output .= " Passez une bonne journée !";
  return true;
});

// Appeler la méthode personnalisée
echo Flight::hello('Bob');

Cela devrait afficher :

Bonjour Fred ! Passez une bonne journée !

Si vous avez défini plusieurs filtres, vous pouvez interrompre la chaîne en renvoyant false dans l'une de vos fonctions de filtre :

Flight::before('start', function (array &$params, string &$output): bool {
  echo 'un';
  return true;
});

Flight::before('start', function (array &$params, string &$output): bool {
  echo 'deux';

  // Cela mettra fin à la chaîne
  return false;
});

// Cela ne sera pas appelé
Flight::before('start', function (array &$params, string &$output): bool {
  echo 'trois';
  return true;
});

Notez que les méthodes de base telles que map et register ne peuvent pas être filtrées car elles sont appelées directement et n'exécutent pas de manière dynamique.

Learn/requests

Requêtes

Flight encapsule la demande HTTP dans un objet unique, qui peut être accédé en faisant:

$request = Flight::request();

L'objet de demande fournit les propriétés suivantes:

Vous pouvez accéder aux propriétés query, data, cookies et files sous forme de tableaux ou d'objets.

Ainsi, pour obtenir un paramètre de chaîne de requête, vous pouvez faire:

$id = Flight::request()->query['id'];

Ou vous pouvez faire:

$id = Flight::request()->query->id;

Corps brut de la requête

Pour obtenir le corps brut de la demande HTTP, par exemple lors du traitement de demandes PUT, vous pouvez faire:

$body = Flight::request()->getBody();

Entrée JSON

Si vous envoyez une demande avec le type application/json et les données {"id": 123} ils seront disponibles à partir de la propriété data:

$id = Flight::request()->data->id;

Accès à $_SERVEUR

Il existe un raccourci disponible pour accéder au tableau $_SERVEUR via la méthode getVar():


$host = Flight::request()->getVar['HTTP_HOST'];

Accès aux en-têtes de la demande

Vous pouvez accéder aux en-têtes de la demande en utilisant la méthode getHeader() ou getHeaders():


// Peut-être avez-vous besoin de l'en-tête Authorization
$host = Flight::request()->getHeader('Authorization');

// Si vous devez récupérer tous les en-têtes
$headers = Flight::request()->getHeaders();

Learn/frameworkmethods

Méthodes du Cadre

Flight est conçu pour être facile à utiliser et à comprendre. Ce qui suit est l'ensemble complet des méthodes du cadre. Il se compose de méthodes centrales, 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 remplacées.

Méthodes Principales

Flight::map(string $name, callable $callback, bool $pass_route = false) // Crée une méthode de cadre personnalisée.
Flight::register(string $name, string $class, array $params = [], ?callable $callback = null) // Enregistre une classe à une méthode de cadre.
Flight::before(string $name, callable $callback) // Ajoute un filtre avant une méthode de cadre.
Flight::after(string $name, callable $callback) // Ajoute un filtre après une méthode de cadre.
Flight::path(string $path) // Ajoute un chemin pour le chargement automatique des classes.
Flight::get(string $key) // Obtient une variable.
Flight::set(string $key, mixed $value) // Définit une variable.
Flight::has(string $key) // Vérifie si une variable est définie.
Flight::clear(array|string $key = []) // Efface une variable.
Flight::init() // Initialise le cadre à ses réglages par défaut.
Flight::app() // Obtient l'instance de l'objet d'application

Méthodes Extensibles

Flight::start() // Lance le cadre.
Flight::stop() // Arrête le cadre et envoie une réponse.
Flight::halt(int $code = 200, string $message = '') // Arrête le cadre avec un code d'état et un message facultatif.
Flight::route(string $pattern, callable $callback, bool $pass_route = false) // Associe un modèle d'URL à un rappel.
Flight::group(string $pattern, callable $callback) // Crée un regroupement pour les URL, le modèle doit être une chaîne de caractères.
Flight::redirect(string $url, int $code) // Redirige vers une autre URL.
Flight::render(string $file, array $data, ?string $key = null) // Rend un fichier de modèle.
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 une mise en cache HTTP ETag.
Flight::lastModified(int $time) // Effectue une mise en cache HTTP modifiée récemment.
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.

Toutes les méthodes personnalisées ajoutées avec map et register peuvent également être filtrées.

Learn/api

Méthodes de l'API du cadre

Flight est conçu pour être facile à utiliser et à comprendre. Ce qui suit est l'ensemble complet des méthodes pour le cadre. Il se compose de méthodes de base, 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 remplacées.

Méthodes de base

Ces méthodes sont fondamentales pour le cadre et ne peuvent pas être remplacées.

Flight::map(string $nom, callable $rappel, bool $pass_route = false) // Crée une méthode de cadre personnalisée.
Flight::register(string $nom, string $classe, array $params = [], ?callable $rappel = null) // Enregistre une classe dans une méthode de cadre.
Flight::unregister(string $nom) // Désenregistre une classe d'une méthode de cadre.
Flight::before(string $nom, callable $rappel) // Ajoute un filtre avant une méthode de cadre.
Flight::after(string $nom, callable $rappel) // Ajoute un filtre après une méthode de cadre.
Flight::path(string $chemin) // Ajoute un chemin pour le chargement automatique des classes.
Flight::get(string $clé) // Obtient une variable.
Flight::set(string $clé, mixed $valeur) // Définit une variable.
Flight::has(string $clé) // Vérifie si une variable est définie.
Flight::clear(array|string $clé = []) // Efface une variable.
Flight::init() // Initialise le cadre avec 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 cadre.
Flight::stop() // Arrête le cadre et envoie une réponse.
Flight::halt(int $code = 200, string $message = '') // Arrête le cadre avec un code d'état et un message facultatifs.
Flight::route(string $motif, callable $rappel, bool $pass_route = false, string $alias = '') // Associe un motif URL à un rappel.
Flight::post(string $motif, callable $rappel, bool $pass_route = false, string $alias = '') // Associe un motif URL de requête POST à un rappel.
Flight::put(string $motif, callable $rappel, bool $pass_route = false, string $alias = '') // Associe un motif URL de requête PUT à un rappel.
Flight::patch(string $motif, callable $rappel, bool $pass_route = false, string $alias = '') // Associe un motif URL de requête PATCH à un rappel.
Flight::delete(string $motif, callable $rappel, bool $pass_route = false, string $alias = '') // Associe un motif URL de requête DELETE à un rappel.
Flight::group(string $motif, callable $rappel) // Crée un regroupement pour les URL, le motif doit être une chaîne.
Flight::getUrl(string $nom, 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::render(string $fichier, array $données, ?string $clé = null) // Rend un fichier de modèle.
Flight::error(Throwable $erreur) // Envoie une réponse HTTP 500.
Flight::notFound() // Envoie une réponse HTTP 404.
Flight::etag(string $id, string $type = 'chaîne') // Effectue la mise en cache HTTP ETag.
Flight::lastModified(int $temps) // Effectue la mise en cache HTTP de dernière modification.
Flight::json(mixed $donnees, int $code = 200, bool $encoder = true, string $jeu_caractères = 'utf8', int $option) // Envoie une réponse JSON.
Flight::jsonp(mixed $donnees, string $param = 'jsonp', int $code = 200, bool $encoder = true, string $jeu_caractères = 'utf8', int $option) // Envoie une réponse JSONP.

Toutes les méthodes personnalisées ajoutées avec map et register peuvent également être filtrées.

Learn/why_frameworks

Pourquoi un Framework?

Certains programmeurs s'opposent vigoureusement à 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 des points valides à faire valoir sur les inconvénients de l'utilisation des frameworks. Cependant, il existe également de nombreux avantages à utiliser des frameworks.

Raisons d'utiliser un Framework

Voici quelques raisons pour lesquelles vous pourriez envisager d'utiliser un framework:

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 importants 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 des applications web rapidement et facilement. Si vous êtes nouveau aux frameworks, Flight est un excellent framework pour débutants pour commencer. Il vous aidera à découvrir les avantages de l'utilisation des frameworks sans vous submerger de 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 le cœur du framework Flight, mais qu'est-ce 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 des choses différentes en fonction de l'URL demandée. Par exemple, vous pourriez vouloir afficher le profil d'un utilisateur lorsqu'il visite /utilisateur/1234, mais afficher une liste de tous les utilisateurs lorsqu'il visite /utilisateurs. Tout cela se fait via le routage.

Cela pourrait fonctionner de cette manière:

Et pourquoi est-ce important?

Avoir un routeur centralisé approprié peut réellement rendre votre vie beaucoup plus facile! Cela peut être difficile à voir au début. Voici quelques raisons pour lesquelles:

Je suis sûr que vous êtes familier avec la méthode script par script de créer un site web. Vous pourriez avoir un fichier appelé index.php qui contient des if pour vérifier l'URL, 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 vite devenir chaotique. Le système de routage de Flight est une manière beaucoup plus organisée et puissante de gérer le routage.

Cela?


// /utilisateur/vue_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 cela?


// index.php
Flight::route('/utilisateur/@id', [ 'UserController', 'voirProfilUtilisateur' ]);
Flight::route('/utilisateur/@id/editer', [ 'UserController', 'editerProfilUtilisateur' ]);

// Peut-être dans votre app/controllers/UserController.php
class UserController {
    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 requête d'un navigateur de l'utilisateur, la traite, puis renvoie une réponse. C'est ainsi que vous pouvez construire des applications web qui font des choses comme afficher le profil d'un utilisateur, permettre à un utilisateur de se connecter ou permettre à un utilisateur de publier un nouvel article de blog.

Demandes

Une demande est ce que le navigateur de l'utilisateur envoie à votre serveur lorsqu'il visite votre site web. Cette demande contient des informations sur ce que l'utilisateur souhaite faire. Par exemple, elle pourrait 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 modifier 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 renvoie au navigateur de l'utilisateur lorsqu'il visite votre site web. Cette réponse contient des informations sur ce que votre serveur souhaite faire. Par exemple, elle pourrait 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 du HTML, du JSON, ou un fichier. Flight vous aide à générer automatiquement certaines parties de la réponse pour faciliter les choses, mais au final vous avez le contrôle sur ce que vous renvoyez à l'utilisateur.

Learn/httpcaching

Mise en cache HTTP

Flight offre une prise en charge intégrée pour la mise en cache au niveau HTTP. Si la condition de mise en cache est remplie, Flight renverra une réponse HTTP 304 Non modifié. La prochaine fois que
le client demande la même ressource, il sera invité à utiliser sa version mise en cache localement.

Dernière modification

Vous pouvez utiliser la méthode lastModified et transmettre 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 d'utiliser sa mise en cache jusqu'à ce que que la valeur de la dernière modification soit modifiée.

Flight::route('/actualites', function () {
  Flight::lastModified(1234567890);
  echo 'Ce contenu sera mis en cache.';
});

ETag

La mise en cache ETag est similaire à Dernière modification, sauf que vous pouvez spécifier n'importe quel identifiant que vous souhaitez pour la ressource :

Flight::route('/actualites', function () {
  Flight::etag('mon-identifiant-unique');
  echo 'Ce contenu sera mis en cache.';
});

Gardez à l'esprit que l'appel à lastModified ou etag définira et vérifiera à la fois la valeur de la mise en cache. Si la valeur de mise en cache est la même entre les demandes, Flight enverra immédiatement une réponse HTTP 304 et arrêtera le traitement.

Learn/responses

Réponses

Flight aide à générer une partie des en-têtes de réponse pour vous, mais vous avez le contrôle sur ce que vous renvoyez à l'utilisateur. Parfois, vous pouvez accéder directement à l'objet Response, mais la plupart du temps, vous utiliserez l'instance Flight pour renvoyer une réponse.

Envoi d'une réponse de base

Flight utilise ob_start() pour mettre en mémoire tampon la sortie. Cela signifie que vous pouvez utiliser echo ou print pour envoyer une réponse à l'utilisateur et Flight le capturera et le renverra à l'utilisateur avec les en-têtes appropriés.


// Cela enverra "Bonjour, monde !" au navigateur de l'utilisateur
Flight::route('/', function() {
    echo "Bonjour, monde !";
});

// HTTP/1.1 200 OK
// Content-Type: text/html
//
// Bonjour, monde !

En alternative, vous pouvez appeler la méthode write() pour ajouter au corps également.


// Cela enverra "Bonjour, monde !" au navigateur de l'utilisateur
Flight::route('/', function() {
    // verbeux, mais fait le travail parfois lorsque vous en avez besoin
    Flight::response()->write("Bonjour, monde!");

    // 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();
});

Codes d'état

Vous pouvez définir le code d'état de la réponse en utilisant la méthode status :

Flight::route('/@id', function($id) {
    if ($id == 123) {
        Flight::response()->status(200);
        echo "Bonjour, monde !";
    } else {
        Flight::response()->status(403);
        echo "Interdit";
    }
});

Si vous voulez obtenir le code d'état actuel, vous pouvez utiliser la méthode status sans arguments :

Flight::response()->status(); // 200

Exécution d'un rappel sur le corps de la réponse

Vous pouvez exécuter un rappel sur le corps de la réponse en utilisant la méthode addResponseBodyCallback :

Flight::route('/utilisateurs', function() {
    $db = Flight::db();
    $users = $db->fetchAll("SELECT * FROM users");
    Flight::render('users_table', ['users' => $users]);
});

// Cela comprimera 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. Puisque cela peut accepter n'importe callable, cela peut accepter un tableau de classe [ $class, 'méthode' ], une clôture $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.

Remarque : 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 voulez que cela s'applique uniquement à une route spécifique, vous pouvez ajouter le rappel dans la route elle-même :

Flight::route('/utilisateurs', function() {
    $db = Flight::db();
    $users = $db->fetchAll("SELECT * FROM users");
    Flight::render('users_table', ['users' => $users]);

    // Cela comprimera uniquement la réponse pour cette route
    Flight::response()->addResponseBodyCallback(function($body) {
        return gzencode($body, 9);
    });
});

Option de Middleware

Vous pouvez également utiliser un middleware pour appliquer le rappel à toutes les routes via le middleware :

// MinifyMiddleware.php
class MinifyMiddleware {
    public function before() {
        Flight::response()->addResponseBodyCallback(function($body) {
            // Il s'agit d'un exemple
            return $this->minify($body);
        });
    }

    protected function minify(string $body): string {
        // minifier le corps
        return $body;
    }
}

// index.php
Flight::group('/utilisateurs', function() {
    Flight::route('', function() { /* ... */ });
    Flight::route('/@id', function($id) { /* ... */ });
}, [ new MinifyMiddleware() ]);

Définition d'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 "Bonjour, monde !" au navigateur de l'utilisateur en texte brut
Flight::route('/', function() {
    Flight::response()->header('Content-Type', 'text/plain');
    echo "Bonjour, monde !";
});

JSON

Flight fournit un support pour l'envoi de réponses JSON et JSONP. Pour envoyer une réponse JSON vous transmettez des données à encoder en JSON :

Flight::json(['id' => 123]);

JSON avec Code d'État

Vous pouvez également transmettre un code d'état en tant que deuxième argument :

Flight::json(['id' => 123], 201);

JSON avec Joli Affichage

Vous pouvez également transmettre un argument à la dernière position pour activer un joli affichage :

Flight::json(['id' => 123], 200, true, 'utf-8', JSON_PRETTY_PRINT);

Si vous modifiez les options transmises à Flight::json() et que vous souhaitez une syntaxe plus simple, vous pouvez simplement remapper la méthode JSON :

Flight::map('json', function($data, $code = 200, $options = 0) {
    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

Si vous voulez envoyer une réponse JSON et arrêter l'exécution, vous pouvez utiliser la méthode jsonHalt. Cela 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 actuel du corps et arrêter l'exécution.

Flight::route('/utilisateurs', function() {
    $authorized = someAuthorizationCheck();
    // Vérifier si l'utilisateur est autorisé
    if ($authorized === false) {
        Flight::jsonHalt(['error' => 'Non autorisé'], 401);
    }

    // Continuer avec le reste de la route
});

JSONP

Pour les requêtes JSONP, vous pouvez éventuellement transmettre le nom du paramètre de requête que vous utilisez pour définir votre fonction de rappel :

Flight::jsonp(['id' => 123], 'q');

Donc, en faisant une requête GET en utilisant ?q=my_func, vous devriez recevoir la sortie :

my_func({"id":123});

Si vous ne transmettez pas de nom de paramètre de requête, il sera par défaut jsonp.

Redirection vers une autre URL

Vous pouvez rediriger la requête actuelle en utilisant la méthode redirect() et en transmettant une nouvelle URL :

Flight::redirect('/nouvel/emplacement');

Par défaut, Flight envoie un code d'état HTTP 303 ("Voir Autre"). Vous pouvez également définir facultativement un code personnalisé :

Flight::redirect('/nouvel/emplacement', 401);

Arrêt

Vous pouvez arrêter le framework à tout moment en appelant la méthode halt :

Flight::halt();

Vous pouvez également spécifier un code d'état HTTP optionnel et un message :

Flight::halt(200, 'Je reviens dans un instant...');

Appeler halt effacera tout le contenu de la réponse jusqu'à ce point. Si vous voulez arrêter le framework et afficher la réponse actuelle, utilisez la méthode stop :

Flight::stop();

Mise en cache HTTP

Flight offre une prise en charge intégrée pour la mise en cache au niveau HTTP. Si la condition de mise en cache est remplie, Flight renverra une réponse 304 Non modifiée HTTP. La prochaine fois que le client demandera la même ressource, celui-ci 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 lui transmettre un temps de mise en cache.


// Cela mettra en cache la réponse pendant 5 minutes
Flight::route('/actualites', function () {
  Flight::response()->cache(time() + 300);
  echo 'Ce contenu sera mis en cache.';
});

// Alternativement, vous pouvez utiliser une chaîne que vous transmettriez
// à la méthode strtotime()
Flight::route('/actualites', function () {
  Flight::response()->cache('+5 minutes');
  echo 'Ce contenu sera mis en cache.';
});

Last-Modified

Vous pouvez utiliser la méthode lastModified et lui transmettre 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 sa mise en cache jusqu'à ce que la valeur de la dernière modification soit modifiée.

Flight::route('/actualites', function () {
  Flight::lastModified(1234567890);
  echo 'Ce contenu sera mis en cache.';
});

ETag

La mise en cache ETag est similaire à Last-Modified, sauf que vous pouvez spécifier n'importe quel identifiant vous voulez pour la ressource :

Flight::route('/actualites', function () {
  Flight::etag('mon-identifiant-unique');
  echo 'Ce contenu sera mis en cache.';
});

Gardez à l'esprit qu'appeler lastModified ou etag définira et vérifiera la valeur de mise en cache. Si la valeur de mise en cache est la même entre les requêtes, Flight enverra immédiatement une réponse HTTP 304 et arrêtera le traitement.

Learn/frameworkinstance

Instance de Framework

Au lieu d'exécuter Flight en tant que classe statique globale, vous pouvez éventuellement l'exécuter en tant qu'instance d'objet.

require 'flight/autoload.php';

$app = Flight::app();

$app->route('/', function () {
  echo 'hello world!';
});

$app->start();

Donc, au lieu d'appeler la méthode statique, vous appelleriez la méthode d'instance avec le même nom sur l'objet Engine.

Learn/redirects

Redirection

Vous pouvez rediriger la requête actuelle en utilisant la méthode redirect et en passant une nouvelle URL :

Flight::redirect('/nouvel/emplacement');

Par défaut, Flight envoie un code d'état HTTP 303. Vous pouvez éventuellement définir un code personnalisé :

Flight::redirect('/nouvel/emplacement', 401);

Learn/views

Vues

Flight fournit par défaut quelques fonctionnalités de base de templating. Pour afficher un template de vue, appelez la méthode render avec le nom du fichier template et des données de template optionnelles :

Flight::render('hello.php', ['name' => 'Bob']);

Les données de template que vous transmettez sont automatiquement injectées dans le template et peuvent être référencées comme une variable locale. Les fichiers de template sont simplement des fichiers PHP. Si le contenu du fichier template 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 désormais disponible dans toutes vos vues. Vous pouvez simplement faire :

Flight::render('hello');

Notez que lors de la spécification du nom du template dans la méthode render, vous pouvez omettre l'extension .php.

Par défaut, Flight recherchera un répertoire views pour les fichiers de template. Vous pouvez définir un chemin alternatif pour vos templates en configurant ce qui suit :

Flight::set('flight.views.path', '/chemin/vers/views');

Mises en page

Il est courant pour les sites web d'avoir un seul fichier de template de mise en page avec un contenu interchangeable. Pour afficher le contenu à utiliser dans une mise en page, vous pouvez transmettre un paramètre optionnel à la méthode render.

Flight::render('header', ['heading' => 'Hello'], 'headerContent');
Flight::render('body', ['body' => 'World'], 'bodyContent');

Votre vue aura ensuite des variables enregistrées appelées headerContent et bodyContent. Vous pouvez ensuite afficher votre mise en page en faisant :

Flight::render('layout', ['title' => 'Page d\'accueil']);

Si les fichiers de template 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>Page d'accueil</title>
  </head>
  <body>
    <h1>Hello</h1>
    <div>World</div>
  </body>
</html>

Vues Personnalisées

Flight vous permet de remplacer simplement le moteur de vue par défaut en enregistrant votre propre classe de vue. Voici comment vous utiliseriez le moteur de template Smarty pour vos vues :

// Charger la bibliothèque Smarty
require './Smarty/libs/Smarty.class.php';

// Enregistrer Smarty en tant que classe de vue
// Transmettre également une fonction de rappel 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/');
});

// Assigner des données de template
Flight::view()->assign('name', 'Bob');

// Afficher le template
Flight::view()->display('hello.tpl');

Pour plus de complétude, vous devriez également remplacer la méthode de rendu par défaut de Flight :

Flight::map('render', function(string $template, array $data): void {
  Flight::view()->assign($data);
  Flight::view()->display($template);
});

Learn/templates

Vues

Flight fournit par défaut certaines fonctionnalités de base de templating.

Si vous avez besoin de fonctionnalités de templating plus complexes, consultez les exemples de Smarty et Latte dans la section Vues Personnalisées.

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 du modèle que vous transmettez 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. Ainsi, vous pouvez simplement faire :

Flight::render('hello');

Notez que lors de la spécification du nom du modèle dans la méthode render, vous pouvez omettre l'extension .php.

Par défaut, Flight recherchera 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', '/chemin/vers/vues');

Dispositions

Il est courant que les sites web aient un seul fichier de modèle de disposition avec du contenu interchangeant. Pour rendre le contenu à utiliser dans une disposition, vous pouvez transmettre 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 disposition en faisant :

Flight::render('layout', ['title' => 'Page d'accueil']);

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>Page d'accueil</title>
  </head>
  <body>
    <h1>Hello</h1>
    <div>World</div>
  </body>
</html>

Vues Personnalisées

Flight vous permet de remplacer le moteur de vue par défaut simplement en enregistrant votre propre classe de vue.

Smarty

Voici comment vous utiliseriez le moteur de template Smarty pour vos vues :

// Charger la bibliothèque Smarty
require './Smarty/libs/Smarty.class.php';

// Enregistrer Smarty en tant que classe de vue
// Passe également une fonction de rappel pour configurer Smarty lors du chargement
Flight::register('view', Smarty::class, [], function (Smarty $smarty) {
  $smarty->setTemplateDir('./templates/');
  $smarty->setCompileDir('./templates_c/');
  $smarty->setConfigDir('./config/');
});

// Attribuer des données de modèle
Flight::view()->assign('name', 'Bob');

// Afficher le modèle
Flight::view()->display('hello.tpl');

Pour plus de complétude, vous devriez également remplacer la méthode de rendu par défaut de Flight :

Flight::map('render', function(string $template, array $data): void {
  Flight::view()->assign($data);
  Flight::view()->display($template);
});

Latte

Voici comment vous utiliseriez le moteur de template Latte pour vos vues :


// Enregistrer Latte en tant que classe de vue
// Passe également une fonction de rappel pour configurer Latte lors du chargement
Flight::register('view', Latte\Engine::class, [], function (Latte\Engine $latte) {
  // C'est ici que Latte mettra en cache vos modèles pour accélérer les choses
    // Une chose intéressante à propos de Latte est qu'il rafraîchit automatiquement votre
    // cache lorsque vous apportez des modifications à vos modèles !
    $latte->setTempDirectory(__DIR__ . '/../cache/');

    // Indiquez à Latte où se trouvera le répertoire racine de vos vues.
    $latte->setLoader(new \Latte\Loaders\FileLoader(__DIR__ . '/../views/'));
});

// Et emballez-le pour que vous puissiez utiliser correctement Flight::render()
Flight::map('render', function(string $template, array $data): void {
  // C'est comme si $latte_engine->render($template, $data);
  echo Flight::view()->render($template, $data);
});

Learn/extending

Extension

Le framework a été conçu pour être 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 remplacer des classes et des méthodes existantes.

Si vous recherchez un DIC (Conteneur d'Injection de Dépendance), rendez-vous sur la page du Conteneur d'Injection de Dépendance.

Mapping des Méthodes

Pour mapper votre propre méthode personnalisée, vous utilisez la fonction map :

// Mapper votre méthode
Flight::map('bonjour', function (string $nom) {
  echo "bonjour $nom!";
});

// Appeler votre méthode personnalisée
Flight::bonjour('Bob');

Cela est utilisé davantage lorsque vous devez transmettre des variables à votre méthode pour obtenir une valeur attendue. Utiliser la méthode register() comme ci-dessous est davantage pour transmettre une configuration, puis appeler votre classe préconfigurée.

Enregistrement des Classes

Pour enregistrer votre propre classe et la configurer, vous utilisez la fonction register :

// Enregistrer votre classe
Flight::register('utilisateur', User::class);

// Obtenir une instance de votre classe
$user = Flight::utilisateur();

La méthode register vous permet également de transmettre 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 transmettant un tableau supplémentaire. Voici un exemple de chargement d'une connexion à une base de données :

// Enregistrer une classe avec des paramètres de constructeur
Flight::register('bd', PDO::class, ['mysql:host=localhost;dbname=test', 'utilisateur', 'mot depasse']);

// Obtenir une instance de votre classe
// Cela créera un objet avec les paramètres définis
//
// new PDO('mysql:host=localhost;dbname=test','utilisateur','mot depasse');
//
$bd = Flight::bd();

// et si vous en aviez besoin plus tard dans votre code, vous appelez simplement à nouveau la même méthode
class SomeController {
  public function __construct() {
    $this->bd = Flight::bd();
  }
}

Si vous transmettez un paramètre de rappel supplémentaire, il sera exécuté immédiatement après la construction de la classe. Cela vous permet d'effectuer des procédures de configuration pour votre nouvel objet. La fonction de rappel prend un paramètre, une instance du nouvel objet.

// L'objet construit sera passé au rappel
Flight::register(
  'bd',
  PDO::class,
  ['mysql:host=localhost;dbname=test', 'utilisateur', 'mot depasse'],
  function (PDO $bd) {
    $bd->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, transmettez simplement false en tant que paramètre :

// Instance partagée de la classe
$partagé = Flight::bd();

// Nouvelle instance de la classe
$nouveau = Flight::bd(false);

Gardez à l'esprit que les méthodes mappées priment 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.

Remplacement des Méthodes du Framework

Flight vous permet de remplacer sa fonctionnalité par défaut pour répondre à vos besoins, sans avoir à modifier de code.

Par exemple, lorsque Flight ne peut pas faire correspondre une URL à une route, il appelle la méthode notFound qui renvoie une réponse générique HTTP 404. Vous pouvez remplacer ce comportement en utilisant la méthode map :

Flight::map('notFound', function() {
  // Afficher une page d'erreur 404 personnalisée
  inclure 'erreurs/404.html';
});

Flight vous permet également de remplacer des composants principaux du framework. Par exemple, vous pouvez remplacer la classe de Route par défaut par votre propre classe personnalisée :

// Enregistrer votre classe personnalisée
Flight::register('router', MyRouter::class);

// Lorsque Flight charge l'instance de Route, il chargera votre classe
$monroutage = Flight::router();

Cependant, les méthodes du framework telles que map et register ne peuvent pas être remplacées. Vous obtiendrez une erreur si vous essayez de le faire.

Learn/json

JSON

Flight fournit un support pour l'envoi de réponses JSON et JSONP. Pour envoyer une réponse JSON, vous transmettez des données à encoder en JSON:

Flight::json(['id' => 123]);

Pour les requêtes JSONP, vous pouvez éventuellement spécifier le nom du paramètre de requête que vous utilisez pour définir votre fonction de rappel:

Flight::jsonp(['id' => 123], 'q');

Ainsi, 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 spécifiez pas de nom de paramètre de requête, il sera par défaut jsonp.

Learn/autoloading

Chargement automatique

Le chargement automatique est un concept en PHP où vous spécifiez un répertoire ou des répertoires à partir desquels charger des classes. Cela est bien plus avantageux que d'utiliser require ou include pour charger des classes. C'est également une exigence pour utiliser des paquets Composer.

Par défaut, toute classe Flight est chargée automatiquement pour vous grâce à Composer. Cependant, si vous souhaitez charger automatiquement vos propres classes, vous pouvez utiliser la méthode Flight::path pour spécifier un répertoire à partir duquel charger des classes.

Exemple Basique

Supposons que nous avons une arborescence de répertoires comme suit :

# Chemin d'exemple
/home/user/project/my-flight-project/
├── app
│   ├── cache
│   ├── config
│   ├── controllers - contient les contrôleurs de ce projet
│   ├── translations
│   ├── UTILS - contient des classes uniquement pour cette application (tout en majuscules à des fins d'exemple ultérieur)
│   └── views
└── public
    └── css
    └── js
    └── index.php

Vous avez peut-être remarqué que c'est la même structure de fichiers que ce site de documentation.

Vous pouvez spécifier chaque répertoire à charger à partir de cette manière :


/**
 * 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 chargées automatiquement sont recommandées d'être en Pascal Case (chaque mot est en majuscule, pas d'espaces)
// À partir de la version 3.7.2, vous pouvez utiliser Pascal_Snake_Case pour vos noms de classe en exécutant Loader::setV2ClassLoading(false);
class MyController {

    public function index() {
        // faire quelque chose
    }
}

Espaces de noms

Si vous avez des espaces de noms, 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 le document root 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 faites attention aux commentaires pour des informations importantes.

/**
 * app/controllers/MyController.php
 */

// les espaces de noms sont requis
// les espaces de noms sont les mêmes que la structure du répertoire
// les espaces de noms doivent suivre la même casse que la structure du répertoire
// les espaces de noms et les répertoires ne peuvent pas avoir de traits de soulignement (sauf si Loader::setV2ClassLoading(false) est défini)
namespace app\controllers;

// Toutes les classes chargées automatiquement sont recommandées d'être en Pascal Case (chaque mot est en majuscule, pas d'espaces)
// À partir de la version 3.7.2, vous pouvez utiliser Pascal_Snake_Case pour vos noms de classe en exécutant Loader::setV2ClassLoading(false);
class MyController {

    public function index() {
        // faire quelque chose
    }
}

Et si vous vouliez charger automatiquement une classe dans votre répertoire utils, vous feriez essentiellement la même chose :


/**
 * app/UTILS/ArrayHelperUtil.php
 */

// l'espace de noms doit correspondre à la structure du répertoire et à la casse (notez que le répertoire UTILS est tout en majuscules
//     comme dans l'arborescence des fichiers ci-dessus)
namespace app\UTILS;

class ArrayHelperUtil {

    public function changeArrayCase(array $array) {
        // faire quelque chose
    }
}

Traits de soulignement dans les Noms de Classe

À partir de la version 3.7.2, vous pouvez utiliser Pascal_Snake_Case pour vos noms de classe en exécutant Loader::setV2ClassLoading(false);. Cela vous permettra d'utiliser des traits de soulignement dans vos noms de classe. Ce n'est pas recommandé, mais c'est disponible pour ceux qui en ont besoin.


/**
 * 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
    }
}

Learn/troubleshooting

Dépannage

Cette page vous aidera à résoudre les problèmes courants auxquels vous pourriez être confrontés lors de l'utilisation de Flight.

Problèmes Courants

Erreur 404 Non Trouvé ou Comportement de Route Inattendu

Si vous rencontrez une erreur 404 Non Trouvé (mais vous jurez sur votre vie qu'elle est vraiment là et que ce n'est pas une faute de frappe), cela pourrait en fait être un problème lié au fait que vous renvoyez une valeur dans le point de terminaison de votre route au lieu de simplement l'afficher. La raison pour cela est intentionnel mais pourrait surprendre certains développeurs.


Flight::route('/bonjour', function(){
    // Cela pourrait causer une erreur 404 Non Trouvé
    return 'Bonjour le monde';
});

// Ce que vous voulez probablement
Flight::route('/bonjour', function(){
    echo 'Bonjour le monde';
});

La raison pour cela est due à un mécanisme spécial intégré au routeur qui gère la sortie de retour comme un signal pour "passer à la route suivante". Vous pouvez voir ce comportement documenté dans la section Routing.

Install

Installation

Téléchargement des fichiers.

Si vous utilisez Composer, vous pouvez exécuter la commande suivante :

composer require flightphp/core

OU vous pouvez télécharger les fichiers directement et les extraire vers votre répertoire web.

Configuration de votre serveur Web.

Apache

Pour Apache, modifiez votre fichier .htaccess avec ce qui suit :

RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php [QSA,L]

Remarque : Si vous devez utiliser flight dans un sous-répertoire, ajoutez la ligne RewriteBase /sous-repertoire/ juste après RewriteEngine On.

Remarque : Si vous souhaitez protéger tous les fichiers serveur, comme un fichier de base de données ou d'environnement. Ajoutez ceci à votre fichier .htaccess :

RewriteEngine On
RewriteRule ^(.*)$ index.php

Nginx

Pour Nginx, ajoutez ce qui suit à votre déclaration du serveur :

serveur {
  location / {
    try_files $uri $uri/ /index.php;
  }
}

Créez votre fichier index.php.

<?php

// Si vous utilisez Composer, exigez l'autoload.
require 'vendor/autoload.php';
// si vous n'utilisez pas Composer, chargez le framework directement
// require 'flight/Flight.php';

// Ensuite, définissez une route et attribuez une fonction pour gérer la requête.
Flight::route('/', function () {
  echo 'hello world!';
});

// Enfin, démarrez le framework.
Flight::start();

License

Le MIT License (MIT)

Copyright © 2023 @mikecao, @n0nag0n

La permission est hereby accordée, gratuitement, à toute personne obtenant une copie de ce logiciel et de la documentation associée les fichiers (le "Logiciel"), pour traiter dans le Logiciel sans limitation, y compris sans limitation les droits d'utilisation, de copie, de modification, de fusion, de publication, de distribution, de sous-licence et/ou de vente des copies du Logiciel, et de permettre à toute personne à qui le Logiciel est fourni de le faire, sous réserve des conditions suivantes:

L'avis de droit d'auteur ci-dessus et cet avis d'autorisation doivent être inclus dans toutes les copies ou parties substantielles du Logiciel.

LE LOGICIEL EST FOURNI "TEL QUEL", SANS GARANTIE D'AUCUNE SORTE, EXPRESSE OU IMPLICITE, Y COMPRIS MAIS SANS S'Y LIMITER LES GARANTIES DE QUALITE MARCHANDE, D'ADEQUATION A UNE FIN PARTICULIERE ET NONCONTREFAÇON. EN AUCUN CAS LES AUTEURS OU LE DROIT D'AUTEUR LES TITULAIRES NE PEUVENT ÊTRE TENUS RESPONSABLES POUR TOUTE RECLAMATION, DOMMAGES OU AUTRES RESPONSABILITES, QU'IL S'AGISSE D'UNE ACTION CONTRACTUELLE, DÉLIT OU AUTRE, SURVENANT DE, HORS DE OU EN RELATION AVEC LE LOGICIEL OU L'UTILISATION OU L' AUTRES TRANSACTIONS DANS LE LOGICIEL.

About

Qu'est-ce que Flight ?

Flight est un framework rapide, simple et extensible pour PHP. Il est assez polyvalent et peut être utilisé pour construire tout type d'application web. Il est conçu en gardant à l'esprit la simplicité et est écrit d'une manière facile à comprendre et à utiliser.

Flight est un excellent framework pour les débutants qui sont nouveaux en PHP et veulent apprendre à construire des applications web. C'est aussi un excellent framework pour les développeurs expérimentés qui veulent plus de contrôle sur leurs applications web. Il est conçu pour construire facilement une API RESTful, une application web simple ou une application web complexe.

Démarrage rapide

<?php

// si installé avec Composer
require 'vendor/autoload.php';
// ou si installé manuellement par fichier zip
// require 'flight/Flight.php';

Flight::route('/', function() {
  echo 'bonjour le monde!';
});

Flight::route('/json', function() {
  Flight::json(['hello' => 'world']);
});

Flight::start();

Assez simple, n'est-ce pas ? Apprenez-en plus sur Flight dans la documentation !

Application squelette/modèle

Il existe une application exemple qui peut vous aider à démarrer avec le Framework Flight. Rendez-vous sur flightphp/skeleton pour des instructions sur la façon de démarrer ! Vous pouvez également visiter la page des exemples pour vous inspirer sur certaines choses que vous pouvez faire avec Flight.

Communauté

Nous sommes sur Matrix ! Discutez avec nous sur #flight-php-framework:matrix.org.

Contribution

Il y a deux façons de contribuer à Flight :

  1. Vous pouvez contribuer au framework principal en visitant le dépôt principal.
  2. Vous pouvez contribuer à la documentation. Ce site de documentation est hébergé sur Github. Si vous remarquez une erreur ou souhaitez améliorer quelque chose, n'hésitez pas à le corriger et à soumettre une pull request ! Nous essayons de rester à jour, mais les mises à jour et les traductions de langues sont les bienvenues.

Conditions requises

Flight nécessite PHP 7.4 ou supérieur.

Remarque : PHP 7.4 est pris en charge car, au moment de la rédaction actuelle (2024), PHP 7.4 est la version par défaut pour certaines distributions Linux LTS. Forcer un passage à PHP >8 causerait beaucoup de maux de tête pour ces utilisateurs. Le framework prend également 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

Wruczek/PHP-File-Cache

Classe de mise en cache en fichier PHP légère, simple et autonome

Avantages

Installation

Installez via composer :

composer require wruczek/php-file-cache

Utilisation

L'utilisation est assez simple.

use Wruczek\PhpFileCache\PhpFileCache;

$app = Flight::app();

// Vous passez le répertoire dans lequel le cache sera stocké dans le constructeur
$app->register('cache', PhpFileCache::class, [ __DIR__ . '/../cache/' ], function(PhpFileCache $cache) {

    // Cela garantit que le cache n'est utilisé que en mode production
    // ENVIRONMENT est une constante définie dans votre fichier d'amorçage ou ailleurs dans votre application
    $cache->setDevMode(ENVIRONMENT === 'développement');
});

Ensuite, vous pouvez l'utiliser dans votre code comme ceci :


// Obtenir l'instance du 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->retrieve('simple-cache-test');
if(empty($data)) {
    $data = date("H:i:s");
    $cache->store('simple-cache-test', $data, 10); // 10 secondes
}

Documentation

Visitez https://github.com/Wruczek/PHP-File-Cache pour obtenir la documentation complète et assurez-vous de consulter le dossier examples.

Awesome-plugins/index

Super plugins

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 pris en charge par l'équipe Flight et d'autres sont des bibliothèques micro/lite pour vous aider à démarrer.

Mise en cache

La mise en cache est un excellent moyen 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.

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.

Bases de données

Les bases de données sont essentielles pour la plupart des applications. C'est ainsi que vous stockez et récupérez des données. Certaines bibliothèques de bases de données ne sont que des enveloppes pour écrire des requêtes et d'autres sont des ORM complets.

Session

Les sessions ne sont pas vraiment utiles pour les API mais pour le développement d'une application web, les sessions peuvent être cruciales pour maintenir l'état et les informations de connexion.

Modèles

Les modèles sont essentiels pour toute application web avec une interface utilisateur. Il existe un certain nombre de moteurs de modèles qui peuvent être utilisés avec Flight.

Contribution

Vous avez un plugin que vous aimeriez partager? Soumettez une pull request pour l'ajouter à la liste!

Awesome-plugins/pdo_wrapper

Classe d'aide PdoWrapper PDO

Flight est livré avec une classe d'aide pour PDO. Il vous permet d'interroger facilement votre base de données avec toute la bizarrerie préparée/execute/fetchAll(). Cela simplifie grandement la façon dont vous pouvez interroger votre base de données. Chaque résultat de ligne est renvoyé sous forme de classe Collection de Flight qui vous permet d'accéder à vos données via une syntaxe de tableau ou une syntaxe d'objet.

Enregistrer la classe d'aide PDO

// Enregistrer la classe d'aide PDO
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
    ]
]);

Utilisation

Cet objet étend PDO, donc toutes les méthodes normales de PDO sont disponibles. Les méthodes suivantes sont ajoutées pour faciliter l'interrogation de la base de données :

runQuery(string $sql, array $params = []): PDOStatement

Utilisez ceci pour les INSERT, les UPDATES, ou si vous envisagez d'utiliser un SELECT dans une boucle while

$db = Flight::db();
$statement = $db->runQuery("SELECT * FROM table WHERE something = ?", [ $something ]);
while($row = $statement->fetch()) {
    // ...
}

// Ou pour écrire dans la base de données
$db->runQuery("INSERT INTO table (name) VALUES (?)", [ $name ]);
$db->runQuery("UPDATE table SET name = ? WHERE id = ?", [ $name, $id ]);

fetchField(string $sql, array $params = []): mixed

Extraie le premier champ de la requête

$db = Flight::db();
$count = $db->fetchField("SELECT COUNT(*) FROM table WHERE something = ?", [ $something ]);

fetchRow(string $sql, array $params = []): array

Récupère une ligne de la requête

$db = Flight::db();
$row = $db->fetchRow("SELECT id, name FROM table WHERE id = ?", [ $id ]);
echo $row['name'];
// or
echo $row->name;

fetchAll(string $sql, array $params = []): array

Récupère toutes les lignes de la requête

$db = Flight::db();
$rows = $db->fetchAll("SELECT id, name FROM table WHERE something = ?", [ $something ]);
foreach($rows as $row) {
    echo $row['name'];
    // or
    echo $row->name;
}

Remarque avec la syntaxe IN()

Il existe également un wrapper utile pour les déclarations IN(). Vous pouvez simplement passer un point d'interrogation unique en guise de paramètre pour IN() puis un tableau de valeurs. Voici un exemple de ce à quoi cela pourrait ressembler :

$db = Flight::db();
$name = 'Bob';
$company_ids = [1,2,3,4,5];
$rows = $db->fetchAll("SELECT id, name FROM table WHERE name = ? AND company_id IN (?)", [ $name, $company_ids ]);

Exemple complet

// Exemple de route et comment vous utiliseriez ce wrapper
Flight::route('/utilisateurs', 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'];
        // ou 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() pour aider (assurez-vous que IN est en majuscules)
    $users = Flight::db()->fetchAll('SELECT * FROM users WHERE id IN (?)', [[1,2,3,4,5]]);
    // vous pourriez aussi faire ceci
    $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();

});

Awesome-plugins/session

Ghostff/Session

Gestionnaire de sessions PHP (non bloquant, flash, segment, chiffrement de session). Utilise PHP open_ssl pour le chiffrement / déchiffrement facultatif des données de session. Prise en charge des fichiers, MySQL, Redis et Memcached.

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 savoir plus sur d'autres paramètres dans le Lisez-moi Github.


use Ghostff\Session\Session;

require 'vendor/autoload.php';

$app = Flight::app();

$app->register('session', Session::class);

// une chose à se rappeler 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 comment vous pourriez utiliser ceci.

Flight::route('POST /login', function() {
    $session = Flight::session();

    // faites votre logique de connexion ici
    // valider le mot de passe, etc.

    // si la connexion réussit
    $session->set('is_logged_in', true);
    $session->set('user', $user);

    // chaque fois que vous écrivez dans la session, vous devez la valider délibérément.
    $session->commit();
});

// Cette vérification pourrait être dans la logique de page restreinte, ou enveloppée dans un middleware.
Flight::route('/some-restricted-page', function() {
    $session = Flight::session();

    if(!$session->get('is_logged_in')) {
        Flight::redirect('/login');
    }

    // faites 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 comment vous pourriez utiliser ceci.


use Ghostff\Session\Session;

require 'vendor/autoload.php';

$app = Flight::app();

// définir un chemin personnalisé vers votre fichier de configuration de session et donnez-lui une chaîne aléatoire pour l'ID de session
$app->register('session', Session::class, [ 'chemin/vers/session_config.php', bin2hex(random_bytes(32)) ], function(Session $session) {
        // ou vous pouvez remplacer manuellement les options de configuration
        $session->updateConfiguration([
            // si vous souhaitez stocker vos données de session dans une base de données (utile si vous voulez quelque chose comme "déconnectez-moi de tous les appareils" fonctionnalité)
            Session::CONFIG_DRIVER        => Ghostff\Session\Drivers\MySql::class,
            Session::CONFIG_ENCRYPT_DATA  => true,
            Session::CONFIG_SALT_KEY      => hash('sha256', 'mon-super-sel-S3CR3T'), // veuillez le changer pour quelque chose d'autre
            Session::CONFIG_AUTO_COMMIT   => true, // faites-le seulement si c'est nécessaire et / ou s'il est difficile de valider () 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 le dns PDO par exemple (mysql:host=...;dbname=...)
                'hôte'      => '127.0.0.1',         # Hôte de la base de données
                'nom_db'   => 'ma_base_de_données_app',   # Nom de la base de données
                'table_db'  => 'sessions',          # Table de la base de données
                'utilisateur_db'   => 'root',              # Nom d'utilisateur de la base de données
                'mot_de_passe_db'   => '',                  # Mot de passe de la base de données
                'connexion_persistante'=> false,          # Évitez les frais généraux de l'établissement d'une nouvelle connexion à chaque fois qu'un script doit parler à une base de données, ce qui donne une application Web plus rapide. TROUVER LE DERRIÈRE PAR VOUS-MÊME
            ]
        ]);
    }
);

Aide ! Mes données de session ne persistent pas !

Définissez-vous vos données de session et elles ne persistent pas entre les requêtes ? Vous pourriez avoir 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();

    // faites votre logique de connexion ici
    // valider le mot de passe, etc.

    // si la connexion réussit
    $session->set('is_logged_in', true);
    $session->set('user', $user);

    // chaque fois 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, vous devez 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, [ 'chemin/vers/session_config.php', bin2hex(random_bytes(32)) ], function(Session $session) {
        $session->updateConfiguration([
            Session::CONFIG_AUTO_COMMIT   => true,
        ]);
    }
);

En outre, 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 Lisez-moi Github pour une documentation complète. Les options de configuration sont bien documentées dans le fichier default_config.php lui-même. Le code est facile à comprendre si vous voulez examiner ce package vous-même.

Awesome-plugins/runway

Piste

Piste 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 plus encore. Il est basé sur l'excellente bibliothèque adhocore/php-cli.

Installation

Installer avec composer.

composer require flightphp/runway

Configuration de base

La première fois que vous exécutez Piste, 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 quelques configurations nécessaires pour que Piste fonctionne correctement.

Utilisation

Piste possède un certain nombre de commandes que vous pouvez utiliser pour gérer votre application Flight. Il existe deux façons faciles d'utiliser Piste.

  1. Si vous utilisez le projet squelette, vous pouvez exécuter php runway [commande] depuis la racine de votre projet.
  2. Si vous utilisez Piste en tant que package installé via composer, vous pouvez exécuter vendor/bin/runway [commande] depuis la racine de votre projet.

Pour n'importe quelle commande, vous pouvez passer 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

Basé sur 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 MonControleur

Générer un modèle Active Record

Basé sur 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 utilisateurs

Si par exemple vous avez la table users avec le schéma suivant : id, nom, email, créé_le, mis_à_jour_le, un fichier similaire au suivant sera créé dans le fichier app/records/UtilisateurRecord.php :

<?php

declare(strict_types=1);

namespace app\records;

/**
 * Classe Active Record pour la table des utilisateurs.
 * @link https://docs.flightphp.com/awesome-plugins/active-record
 * 
 * @property int $id
 * @property string $nom
 * @property string $email
 * @property string $créé_le
 * @property string $mis_à_jour_le
 */
class UtilisateurRecord extends \flight\ActiveRecord
{
    /**
     * @var array $relations Définit les relations pour le modèle
     *   https://docs.flightphp.com/awesome-plugins/active-record#relationships
     */
    protected array $relations = [];

    /**
     * Constructeur
     * @param mixed $connexionBaseDeDonnées La connexion à la base de données
     */
    public function __construct($connexionBaseDeDonnées)
    {
        parent::__construct($connexionBaseDeDonnées, 'users');
    }
}

Afficher toutes les routes

Cela affichera toutes les routes actuellement enregistrées avec Flight.

php runway routes

Si vous souhaitez uniquement voir des routes spécifiques, vous pouvez passer 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.

Personnalisation de Piste

Si vous créez un package pour Flight, ou si vous souhaitez 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/package.

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 CommandeExemple extends AbstractBaseCommand
{
    /**
     * Construire
     *
     * @param array<string,mixed> $config Configuration JSON de .runway-config.json
     */
    public function __construct(array $config)
    {
        parent::__construct('make:example', 'Créer un exemple pour la documentation', $config);
        $this->argument('<gif-amusant>', 'Le nom du gif amusant');
    }

    /**
     * Exécute la fonction
     *
     * @return void
     */
    public function execute(string $contrôleur)
    {
        $io = $this->app()->io();

        $io->info('Création de l\'exemple...');

        // Faire quelque chose ici

        $io->ok('Exemple créé !');
    }
}

Consultez la Documentation 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

Tracy Flight Panel Extensions

Ceci est un ensemble d'extensions pour rendre le travail avec Flight un peu plus enrichissant.

Ceci est le panneau

Barre de vol

Et chaque panneau affiche des informations très utiles sur votre application!

Données de vol Base de données de vol Requête de vol

Installation

Exécutez composer require flightphp/tracy-extensions --dev et c'est parti!

Configuration

Il y a très peu de configuration à faire pour démarrer. Vous devrez initialiser le débogueur Tracy avant d'utiliser ceci 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
// enveloppeur 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 normale
$pdo = new PdoQueryCapture('sqlite:test.db', 'user', 'pass');
// ou si vous attachez ceci au framework Flight
Flight::register('db', PdoQueryCapture::class, ['sqlite:test.db', 'user', 'pass']);
// maintenant chaque fois que vous exécutez une requête, il capturera le temps, la requête et les paramètres

// Cela connecte les points
if(Debugger::$showBar === true) {
    // Cela doit être faux sinon Tracy ne peut pas réellement afficher :(
    Flight::set('flight.content_length', false);
    new TracyExtensionLoader(Flight::app());
}

// plus de code

Flight::start();

Configuration Additionnelle

Données de Session

Si vous avez un gestionnaire de session personnalisé (tel que ghostff/session), vous pouvez transmettre n'importe quel tableau de données de session à Tracy et il les affichera automatiquement pour vous. Vous les transmettez avec la clé session_data dans le deuxième paramètre du constructeur TracyExtensionLoader.


use Ghostff\Session\Session;

require 'vendor/autoload.php';

$app = Flight::app();

$app->register('session', Session::class);

if(Debugger::$showBar === true) {
    // Cela doit être faux sinon Tracy ne peut pas réellement afficher :(
    Flight::set('flight.content_length', false);
    new TracyExtensionLoader(Flight::app(), [ 'session_data' => Flight::session()->getAll() ]);
}

// routes et autres choses...

Flight::start();

Latte

Si vous avez Latte installé dans votre projet, vous pouvez utiliser le panneau Latte pour analyser vos modèles. Vous pouvez transmettre l'instance de Latte au constructeur TracyExtensionLoader avec la clé latte dans le deuxième paramètre.



use Latte\Engine;

require 'vendor/autoload.php';

$app = Flight::app();

$app->register('latte', Engine::class, [], function($latte) {
    $latte->setTempDirectory(__DIR__ . '/temp');

    // c'est là que vous ajoutez le panneau Latte à Tracy
    $latte->addExtension(new Latte\Bridges\Tracy\TracyExtension);
});

if(Debugger::$showBar === true) {
    // Cela doit être faux sinon Tracy ne peut pas réellement afficher :(
    Flight::set('flight.content_length', false);
    new TracyExtensionLoader(Flight::app());
}

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.

Awesome-plugins/active_record

Flight Active Record

Un active record est la mise en correspondance d'une entité de base de données avec un objet PHP. Pour le dire simplement, si vous avez une table users dans votre base de données, vous pouvez "traduire" une ligne dans cette table en une classe User et un objet $user dans votre base de code. Voir exemple de base.

Exemple de Base

Supposons que vous avez 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 au singulier
 * 
 * Il est fortement recommandé d'ajouter les propriétés de la table en commentaires ici
 * 
 * @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 façon
        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 utiliseriez probablement une vraie connexion de base de données

// pour mysql
$database_connection = new PDO('mysql:host=localhost;dbname=test_db&charset=utf8bm4', 'nom d'utilisateur', 'mot de passe');

// ou mysqli
$database_connection = new mysqli('localhost', 'nom d'utilisateur', 'mot de passe', 'test_db');
// ou mysqli avec une création non basée sur les objets
$database_connection = mysqli_connect('localhost', 'nom d'utilisateur', 'mot de passe', 'test_db');

$user = new User($database_connection);
$user->name = 'Bobby Tables';
$user->password = password_hash('un mot de passe sympa');
$user->insert();
// ou $user->save();

echo $user->id; // 1

$user->name = 'Joseph Mamma';
$user->password = password_hash('encore un mot de passe sympa!!!');
$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 d'ajouter un nouvel utilisateur ! Maintenant qu'il y a une ligne utilisateur dans la base de données, comment la récupérez-vous ?

$user->find(1); // trouve l'id = 1 dans la base de données et le retourne.
echo $user->name; // 'Bobby Tables'

Et si vous voulez trouver tous les utilisateurs ?

$users = $user->findAll();

Et pour une certaine condition ?

$users = $user->like('name', '%mamma%')->findAll();

Voyez comme c'est amusant ? Installons-le et commençons !

Installation

Installez simplement avec Composer

composer require flightphp/active-record 

Utilisation

Cela peut être utilisé en tant que bibliothèque autonome ou avec le Framework PHP Flight. Complètement à vous de décider.

Autonome

Assurez-vous simplement de passer une connexion PDO au constructeur.

$pdo_connection = new PDO('sqlite:test.db'); // ceci est juste un exemple, vous utiliseriez probablement une vraie connexion de base de données

$User = new User($pdo_connection);

Framework PHP Flight

Si vous utilisez le Framework PHP Flight, vous pouvez enregistrer la classe ActiveRecord en tant que service (mais vous n'êtes honnêtement pas obligé de le faire).

Flight::register('user', 'User', [ $pdo_connection ]);

// puis vous pouvez l'utiliser de cette façon dans un contrôleur, une fonction, etc.

Flight::user()->find(1);

Fonctions CRUD

find($id = null) : boolean|ActiveRecord

Trouve un enregistrement et l'assigne à l'objet actuel. Si vous passez un $id de quelque sorte, il effectuera une recherche sur la clé primaire avec cette valeur. S'il n'y a rien de passé, il trouvera juste le premier enregistrement dans la table.

En outre, vous pouvez passer d'autres méthodes auxiliaires pour interroger votre table.

// trouver un enregistrement avec des 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)

Renvoie 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 = 'démo';
$user->password = md5('démo');
$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 l'insertion de deux manières.

$user = new User($pdo_connection, [ 'primaryKey' => 'uuid' ]);
$user->uuid = 'quelques-uuid';
$user->name = 'démo';
$user->password = md5('démo');
$user->insert(); // ou $user->save();

ou vous pouvez faire générer automatiquement la clé primaire pour vous grâce aux é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 clé primaire 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 l'insertion, elle sera définie sur le rowid et la base de données la générera pour vous, mais elle ne persistera pas car ce champ peut ne pas exister dans votre table. C'est pourquoi il est recommandé d'utiliser l'événement pour gérer automatiquement cela pour vous.

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 = 'démo';
$user->password = md5('démo');
$user->save();

Remarque : Si vous avez des relations définies dans la classe, elles seront récursivement enregistrées si elles ont été définies, instanciées et ont des données à mettre à jour. (v0.4.0 et supérieur)

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 font référence 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'e-mail est considéré comme "sale" car il a été modifié.
$user->update();
// maintenant il n'y a pas de données sales car elles ont été mises à jour et persistées dans la base de données

$user->password = password_hash()'un nouveau mot de passe'); // maintenant il est sale
$user->dirty(); // en ne passant rien, toutes les entrées sales seront effacées.
$user->update(); // rien ne sera mis à jour car rien n'a été capturé comme sale.

$user->dirty([ 'name' => 'quelque chose', 'password' => password_hash('un mot de passe différent') ]);
$user->update(); // à la fois le nom et le mot de passe sont mis à jour.

copyFrom(array $data): ActiveRecord (v0.4.0)

C'est un alias pour la méthode dirty(). C'est un peu plus clair ce que vous faites.

$user->copyFrom([ 'name' => 'quelque chose', 'password' => password_hash('un mot de passe différent') ]);
$user->update(); // à la fois le nom et le mot de passe sont mis à jour.

isDirty(): boolean (v0.4.0)

Renvoie 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 bien à utiliser dans des comportements de boucle. Si vous passez true, il réinitialisera également les données de la 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 feuille 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é généré et l'utiliser à des fins de débogage.

Méthodes de Requête SQL

select(string $champ1 [, string $champ2 ... ])

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 $nom_table, string $condition_jointure)

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 $conditions_where)

Vous pouvez définir des arguments where personnalisés (vous ne pouvez pas définir de paramètres dans cette instruction where)

$user->where('id=1 AND name="démo"')->find();

Note 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 CECI !!! Cela est susceptible de ce que l'on appelle des attaques par injection SQL. Il y a beaucoup d'articles en ligne, veuillez chercher "attaques par injection sql php" sur Google et vous trouverez beaucoup d'articles sur ce sujet. La bonne manière de gérer cela avec cette bibliothèque est au lieu de cette méthode where(), vous feriez quelque chose comme $user->eq('id', $id)->eq('name', $name)->find(); Si vous devez absolument le faire, la bibliothèque PDO a $pdo->quote($var) pour l'échapper pour vous. Seulement après avoir utilisé quote(), vous pouvez l'utiliser dans une instruction where().

group(string $group_by_statement)/groupBy(string $group_by_statement)

Groupe vos résultats selon une condition particulière.

$user->select('COUNT(*) as count')->groupBy('name')->findAll();

order(string $order_by_statement)/orderBy(string $order_by_statement)

Trie la requête retournée d'une certaine manière.

$user->orderBy('name DESC')->find();

limit(string $limit)/limit(int $offset, int $limit)

Limite le nombre d'enregistrements retournés. Si un second int est donné, il sera décalage, limite exactement comme en SQL.

$user->orderby('name DESC')->limit(0, 10)->findAll();

Conditions WHERE

equal(string $field, mixed $value) / eq(string $field, mixed $value)

field = $value

$user->eq('id', 1)->find();

notEqual(string $field, mixed $value) / ne(string $field, mixed $value)

field <> $value

$user->ne('id', 1)->find();

isNull(string $field)

field IS NULL

$user->isNull('id')->find();

isNotNull(string $field) / notNull(string $field)

field IS NOT NULL

$user->isNotNull('id')->find();

greaterThan(string $field, mixed $value) / gt(string $field, mixed $value)

field > $value

$user->gt('id', 1)->find();

lessThan(string $field, mixed $value) / lt(string $field, mixed $value)

field < $value

$user->lt('id', 1)->find();

greaterThanOrEqual(string $field, mixed $value) / ge(string $field, mixed $value) / gte(string $field, mixed $value)

field >= $value

$user->ge('id', 1)->find();

lessThanOrEqual(string $field, mixed $value) / le(string $field, mixed $value) / lte(string $field, mixed $value)

field <= $value

$user->le('id', 1)->find();

like(string $field, mixed $value) / notLike(string $field, mixed $value)

field LIKE $value ou field NOT LIKE $value

$user->like('name', 'de')->find();

in(string $field, array $values) / notIn(string $field, array $values)

field IN($value) ou field NOT IN($value)

$user->in('id', [1, 2])->find();

between(string $field, array $values)

field BETWEEN $value AND $value1

$user->between('id', [1, 2])->find();

Relations

Vous pouvez définir plusieurs types de relations en utilisant cette bibliothèque. Vous pouvez définir des relations un->plusieurs et un->un entre les tables. Cela nécessite une configuration supplémentaire dans la classe au préalable.

Le réglage de l'array $relations n'est pas difficile, mais deviner correctement la syntaxe peut être déroutant.

protected array $relations = [
    // vous pouvez nommer la clé comme vous le souhaitez. Le nom de l'ActiveRecord est probablement bon. Ex : utilisateur, 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
        // en fonction du type de relation
        // self::HAS_ONE = la clé étrangère qui fait référence à la jointure
        // self::HAS_MANY = la clé étrangère qui fait référence à la jointure
        // self::BELONGS_TO = la clé locale qui fait référence à la jointure
        'cle_locale_ou_etrangere',
        // juste pour information, cela se joint également uniquement à la clé primaire du modèle "autre"

        // facultatif
        [ '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))

        // facultatif
        'nom_reference_arriere' // c'est si vous voulez réfé# Flight Active Record

Un enregistrement actif est une mise en correspondance d'une entité de base de données avec un objet PHP. Pour le dire simplement, 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 `$utilisateur` dans votre base de code. Voir [exemple de base](#exemple-de-base).

## Exemple de Base

Supposons que vous avez la table suivante :

```sql
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 les propriétés de la table en commentaires ici
 * 
 * @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 façon
        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 utiliseriez probablement une vraie connexion de base de données

// pour mysql
$database_connection = new PDO('mysql:host=localhost;dbname=test_db&charset=utf8bm4', 'nom d'utilisateur', 'mot de passe');

// ou mysqli
$database_connection = new mysqli('localhost', 'nom d'utilisateur', 'mot de passe', 'test_db');
// ou mysqli avec une création non basée sur les objets
$database_connection = mysqli_connect('localhost', 'nom d'utilisateur', 'mot de passe', 'test_db');

$user = new User($database_connection);
$user->name = 'Bobby Tables';
$user->password = password_hash('un mot de passe sympa');
$user->insert();
// ou $user->save();

echo $user->id; // 1

$user->name = 'Joseph Mamma';
$user->password = password_hash('encore un mot de passe sympa!!!');
$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 d'ajouter un nouvel utilisateur ! Maintenant qu'il y a une ligne utilisateur dans la base de données, comment la récupérez-vous ?

$user->find(1); // trouve l'id = 1 dans la base de données et le retourne.
echo $user->name; // 'Bobby Tables'

Et si vous voulez trouver tous les utilisateurs ?

$users = $user->findAll();

Et pour une certaine condition ?

$users = $user->like('name', '%mamma%')->findAll();

Voyez comme c'est amusant ? Installons-le et commençons !

Installation

Installez simplement avec Composer

composer require flightphp/active-record 

Utilisation

Cela peut être utilisé en tant que bibliothèque autonome ou avec le Framework PHP Flight. Complètement à vous de décider.

Autonome

Assurez-vous simplement de passer une connexion PDO au constructeur.

$pdo_connection = new PDO('sqlite:test.db'); // ceci est juste un exemple, vous utiliseriez probablement une vraie connexion de base de données

$User = new User($pdo_connection);

Framework PHP Flight

Si vous utilisez le Framework PHP Flight, vous pouvez enregistrer la classe ActiveRecord en tant que service (mais vous n'êtes honnêtement pas obligé de le faire).

Flight::register('user', 'User', [ $pdo_connection ]);

// puis vous pouvez l'utiliser de cette façon dans un contrôleur, une fonction, etc.

Flight::user()->find(1);

Fonctions CRUD

find($id = null) : boolean|ActiveRecord

Trouve un enregistrement et l'assigne à l'objet actuel. Si vous passez un $id de quelque sorte, il effectuera une recherche sur la clé primaire avec cette valeur. S'il n'y a rien de passé, il trouvera juste le premier enregistrement dans la table.

En outre, vous pouvez passer d'autres méthodes auxiliaires pour interroger votre table.

// trouver un enregistrement avec des 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)

Renvoie 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 = 'démo';
$user->password = md5('démo');
$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 l'insertion de deux manières.

$user = new User($pdo_connection, [ 'primaryKey' => 'uuid' ]);
$user->uuid = 'quelques-uuid';
$user->name = 'démo';
$user->password = md5('démo');
$user->insert(); // ou $user->save();

ou vous pouvez faire générer automatiquement la clé primaire pour vous grâce aux é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 clé primaire 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 l'insertion, elle sera définie sur le rowid et la base de données la générera pour vous, mais elle ne persistera pas car ce champ peut ne pas exister dans votre table. C'est pourquoi il est recommandé d'utiliser l'événement pour gérer automatiquement cela pour vous.

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 = 'démo';
$user->password = md5('démo');
$user->save();

Remarque : Si vous avez des relations définies dans la classe, elles seront récursivement enregistrées si elles ont été définies, instanciées et ont des données à mettre à jour. (v0.4.0 et supérieur)

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 font référence 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'e-mail est considéré comme "sale" car il a été modifié.
$user->update();
// maintenant il n'y a pas de données sales car elles ont été mises à jour et persistées dans la base de données

$user->password = password_hash()'un nouveau mot de passe'); // maintenant il est sale
$user->dirty(); // en ne passant rien, toutes les entrées sales seront effacées.
$user->update(); // rien ne sera mis à jour car rien n'a été capturé comme sale.

$user->dirty([ 'name' => 'quelque chose', 'password' => password_hash('un mot de passe différent') ]);
$user->update(); // à la fois le nom et le mot de passe sont mis à jour.

copyFrom(array $data): ActiveRecord (v0.4.0)

C'est un alias pour la méthode dirty(). C'est un peu plus clair ce que vous faites.

$user->copyFrom([ 'name' => 'quelque chose', 'password' => password_hash('un mot de passe différent') ]);
$user->update(); // à la fois le nom et le mot de passe sont mis à jour.

isDirty(): boolean (v0.4.0)

Renvoie 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 bien à utiliser dans des comportements de boucle. Si vous passez true, il réinitialisera également les données de la 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 feuille 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é généré et l'utiliser à des fins de débogage.

Méthodes de Requête SQL

select(string $champ1 [, string $champ2 ... ])

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 $nom_table, string $condition_jointure)

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 $conditions_where)

Vous pouvez définir des arguments where personnalisés (vous ne pouvez pas définir de paramètres dans cette instruction where)

$user->where('id=1 AND name="démo"')->find();

Note 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 CECI !!! Cela est susceptible de ce que l'on appelle des attaques par injection SQL. Il y a beaucoup d'articles en ligne, veuillez chercher "attaques par injection sql php" sur Google et vous trouverez beaucoup d'articles sur ce sujet. La bonne manière de gérer cela avec cette bibliothèque est au lieu de cette méthode where(), vous feriez quelque chose comme $user->eq('id', $id)->eq('name', $name)->find(); Si vous devez absolument le faire, la bibliothèque PDO a $pdo->quote($var) pour l'échapper pour vous. Seulement après avoir utilisé quote(), vous pouvez l'utiliser dans une instruction where().

group(string $group_by_statement)/groupBy(string $group_by_statement)

Groupe vos résultats selon une condition particulière.

$user->select('COUNT(*) as count')->groupBy('name')->findAll();

order(string $order_by_statement)/orderBy(string $order_by_statement)

Trie la requête retournée d'une certaine manière.

$user->orderBy('name DESC')->find();

limit(string $limit)/limit(int $offset, int $limit)

Limite le nombre d'enregistrements retournés. Si un second int est donné, il sera décalage, limite exactement comme en SQL.

$user->orderby('name DESC')->limit(0, 10)->findAll();

Conditions WHERE

equal(string $field, mixed $value) / eq(string $field, mixed $value)

field = $value

$user->eq('id', 1)->find();

notEqual(string $field, mixed $value) / ne(string $field, mixed $value)

field <> $value

$user->ne('id', 1)->find();

isNull(string $field)

field IS NULL

$user->isNull('id')->find();

isNotNull(string $field) / notNull(string $field)

field IS NOT NULL

$user->isNotNull('id')->find();

greaterThan(string $field, mixed $value) / gt(string $field, mixed $value)

field > $value

$user->gt('id', 1)->find();

lessThan(string $field, mixed $value) / lt(string $field, mixed $value)

field < $value

$user->lt('id', 1)->find();

greaterThanOrEqual(string $field, mixed $value) / ge(string $field, mixed $value) / gte(string $field, mixed $value)

field >= $value

$user->ge('id', 1)->find();

lessThanOrEqual(string $field, mixed $value) / le(string $field, mixed $value) / lte(string $field, mixed $value)

field <= $value

$user->le('id', 1)->find();

like(string $field, mixed $value) / notLike(string $field, mixed $value)

field LIKE $value ou field NOT LIKE $value

$user->like('name', 'de')->find();

in(string $field, array $values) / notIn(string $field, array $values)

field IN($value) ou field NOT IN($value)

$user->in('id', [1, 2])->find();

between(string $field, array $values)

field BETWEEN $value AND $value1

$user->between('id', [1, 2])->find();

Relations

Vous pouvez définir plusieurs types de relations en utilisant cette bibliothèque. Vous pouvez définir des relations un->plusieurs et un->un entre les tables. Cela nécessite une configuration supplémentaire dans la classe au préalable.

Le réglage de l'array $relations n'est pas difficile, mais deviner correctement la syntaxe peut être déroutant.

protected array $relations = [
    // vous pouvez nommer la clé comme vous le souhaitez. Le nom de l'ActiveRecord est probablement bon. Ex : utilisateur, 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
        // en fonction du type de relation
        // self::HAS_ONE = la clé étrangère qui fait référence à la jointure
        // self::HAS_MANY = la clé étrangère qui fait référence à la jointure
        // self::BELONGS_TO = la clé locale qui fait référence à la jointure
        'cle_locale_ou_etrangere',
        // juste pour information, cela se joint également uniquement à la clé primaire du modèle "autre"

        // facultatif
        [ '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))

        // facultatif
        'nom_reference_arriere' // c'est sivous voulez référencer cette relation en arrière. 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 que les références sont configurées, vous pouvez les utiliser très facilement !

$user = new User($pdo_connection);

// trouver le plus récent utilisateur.
$user->notNull('id')->orderBy('id desc')->find();

// obtenir des contacts en utilisant la relation:
foreach($user->contacts as $contact) {
    echo $contact->id;
}

// ou nous pouvons y aller dans l'autre sens.
$contact = new Contact();

// trouver un contact
$contact->find();

// obtenir un utilisateur en utilisant la relation:
echo $contact->user->name; // c'est le nom de l'utilisateur

Vraiment cool, non?

Configuration de Données Personnalisées

Parfois, vous devez attacher quelque chose d'unique à votre ActiveRecord tel qu'un calcul personnalisé qui pourrait être plus facile à attacher simplement à l'objet qui serait ensuite passé à un modèle, par exemple.

setCustomData(string $champ, mixed $valeur)

Vous attachez les données personnalisées avec la méthode setCustomData().

$user->setCustomData('page_view_count', $nombre_de_vues_page);

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 très utiles pour configurer automatiquement des données pour vous.

onConstruct(ActiveRecord $ActiveRecord, array &config)

Cela est vraiment utile si vous avez besoin de définir une connexion par défaut par exemple.

// 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 utile uniquement 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) {
        // exécuter toujours id >= 0 si c'est votre truc
        $self->gte('id', 0); 
    } 
}

afterFind(ActiveRecord $ActiveRecord)

Celui-ci est probablement plus utile si vous avez toujours besoin d'exécuter une certaine logique à chaque fois que cet enregistrement est récupéré. Avez-vous besoin de décrypter 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écryptage de quelque chose
        $self->secret = votrefonctiondecrypt($self->secret, votre_clé);

        // 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 utile uniquement 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) {
        // exécuter toujours id >= 0 si c'est votre truc
        $self->gte('id', 0); 
    } 
}

afterFindAll(array<int,ActiveRecord> $results)

Similaire à afterFind() mais vous pouvez le faire pour tous les enregistrements !

class User extends flight\ActiveRecord {

    public function __construct($database_connection)
    {
        parent::__construct($database_connection, 'users');
    }

    protected function afterFindAll(array $results) {

        foreach($results as $self) {
            // faire quelque chose de sympa comme afterFind()
        }
    } 
}

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éfinir certaines 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 avez-vous un cas d'utilisation pour modifier 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 ce que vous voulez
        Flight::cache()->set('most_recent_insert_id', $self->id);
        // ou autre chose....
    } 
}

beforeUpdate(ActiveRecord $ActiveRecord)

Vraiment utile si vous avez besoin de définir des valeurs par défaut à chaque mise à jour.

class User extends flight\ActiveRecord {

    public function __construct($database_connection)
    {
        parent::__construct($database_connection, 'users');
    }

    protected function beforeInsert(self $self) {
        // définir certaines valeurs par défaut
        if(!$self->updated_date) {
            $self->updated_date = gmdate('Y-m-d');
        }
    } 
}

afterUpdate(ActiveRecord $ActiveRecord)

Peut-être avez-vous un cas d'utilisation pour modifier des données après leur mise à jour ?

class User extends flight\ActiveRecord {

    public function __construct($database_connection)
    {
        parent::__construct($database_connection, 'users');
    }

    protected function afterInsert(self $self) {
        // vous faites ce que vous voulez
        Flight::cache()->set('most_recently_updated_user_id', $self->id);
        // ou autre chose....
    } 
}

beforeSave(ActiveRecord $ActiveRecord)/afterSave(ActiveRecord $ActiveRecord)

Ceci est utile si vous voulez que des événements se produisent à la fois lors de l'insertion ou de la mise à jour. Je vous épargne l'explication longue, 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 sais pas ce que vous voulez faire ici, mais pas de jugements ici ! Faites-le !

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 de la Connexion à 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 devez actualiser la connexion à la base de données, par exemple si vous exécutez un script CLI à long terme et que vous 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 faites-le. :D

Configuration

Lorsque vous contribuez, assurez-vous d'exécuter composer test-coverage pour maintenir une couverture de test à 100% (ce n'est pas une couverture de test unitaire réelle, mais plus des tests d'intégration).

Assurez-vous également d'exécuter composer beautify et composer phpcs pour corriger les éventuelles

Awesome-plugins/latte

Latte

Latte est un moteur de template complet, très facile à utiliser et qui 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.

Installation

Installer avec composer.

composer require latte/latte

Configuration de base

Il existe quelques options de configuration de base pour commencer. Vous pouvez en savoir plus à leur sujet dans la Documentation de Latte.


use Latte\Engine as LatteEngine;

require 'vendor/autoload.php';

$app = Flight::app();

$app->register('latte', LatteEngine::class, [], function(LatteEngine $latte) use ($app) {

    // C'est ici que Latte mettra en cache vos modèles pour accélérer les choses
    // Une chose intéressante à propos de Latte est qu'il rafraîchit automatiquement
    // votre cache lorsque vous apportez des modifications à vos modèles !
    $latte->setTempDirectory(__DIR__ . '/../cache/');

    // Indiquez à Latte où se trouvera le répertoire racine de vos vues.
    $latte->setLoader(new \Latte\Loaders\FileLoader($app->get('flight.views.path')));
});

Exemple de mise en page simple

Voici un exemple simple d'un fichier de mise en page. C'est le fichier qui enveloppera toutes vos autres vues.

<!-- app/views/layout.latte -->
<!doctype html>
<html lang="fr">
    <head>
        <title>{$title ? $title . ' - '}My 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 le magique ici -->
            {block content}{/block}
        </div>
        <div id="footer">
            &copy; Droit d'auteur
        </div>
    </body>
</html>

Et maintenant nous avons votre fichier qui sera rendu à l'intérieur de ce bloc de contenu :

<!-- app/views/home.latte -->
<!-- Cela dit à Latte que ce fichier est "à l'intérieur" du fichier layout.latte -->
{extends layout.latte}

<!-- Ceci est le contenu qui sera rendu à l'intérieur de la mise en page dans le bloc de contenu -->
{block content}
    <h1>Page d'accueil</h1>
    <p>Bienvenue sur mon application !</p>
{/block}

Ensuite, lorsque vous allez rendre ceci à l'intérieur de votre fonction ou contrôleur, vous feriez quelque chose comme ceci :

// route simple
Flight::route('/', function () {
    Flight::latte()->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::latte()->render('home.latte', [
            'title' => 'Page d'accueil'
        ]);
    }
}

Consultez la Documentation de Latte pour plus d'informations sur comment utiliser Latte à son plein potentiel!

Awesome-plugins/awesome_plugins

Extensions Géniaux

Flight est incroyablement extensible. Il existe plusieurs extensions qui peuvent être utilisées pour ajouter des fonctionnalités à votre application Flight. Certaines sont officiellement prises en charge par l'équipe Flight et d'autres sont des bibliothèques micro/lite pour vous aider à démarrer.

Mise en cache

La mise en cache est un excellent moyen de accélérer votre application. Il existe plusieurs bibliothèques de mise en cache qui peuvent être utilisées avec Flight.

Interface en ligne de commande (CLI)

Les applications CLI sont un excellent moyen d'interagir avec votre application. Vous pouvez les utiliser pour générer des contrôleurs, afficher toutes les routes, et plus encore.

Cookies

Les cookies sont un excellent moyen de stocker de petites quantités de données côté client. Ils peuvent être utilisés pour stocker les préférences des utilisateurs, les paramètres de l'application, et plus encore.

Débogage

Le débogage est crucial lorsque vous développez dans votre environnement local. Il existe quelques extensions qui peuvent améliorer votre expérience de débogage.

Bases de données

Les bases de données sont essentielles pour la plupart des applications. C'est ainsi que vous stockez et récupérez des 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.

Chiffrement

Le chiffrement est crucial pour toute application qui stocke des données sensibles. Chiffrer et déchiffrer les données n'est pas très 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 la commettre à votre dépôt de code.

Session

Les sessions ne sont pas vraiment utiles pour les API, mais pour développer une application web, les sessions peuvent être cruciales pour maintenir l'état et les informations de connexion.

Modèles

Les modèles sont essentiels pour toute application web avec une UI. Il existe plusieurs moteurs de modèles qui peuvent être utilisés avec Flight.

Contribution

Vous avez un plugin que vous aimeriez partager ? Soumettez une pull request pour l'ajouter à la liste !

Examples

Besoin d'un démarrage rapide?

Vous avez deux options pour commencer avec Flight:

Besoin d'Inspiration?

Bien que ceux-ci ne soient pas officiellement sponsorisés par l'équipe Flight, ils pourraient vous donner des idées sur la manière de structurer vos propres projets construits avec Flight!

Envie de Partager Votre Propre Exemple?

Si vous avez un projet que vous souhaitez partager, veuillez soumettre une pull request pour l'ajouter à cette liste!