Microframe mince

Le 25 avril 2019, la nouvelle version alpha majeure du microframework Slim a vu le jour, et le 18 mai elle est passée en beta . Je vous suggère de vous familiariser avec la nouvelle version.


Sous la coupe:


  • Ă€ propos des innovations du cadre
  • Écrire une application simple sur Slim-4
  • Ă€ propos de l'amitiĂ© Slim et PhpStorm

Nouveau dans Slim 4


Innovations clés par rapport à la version 3:


  • La version minimale de PHP est 7.1;
  • Prise en charge de PSR-15 (middleware);
  • Suppression de l'implĂ©mentation des messages http. Installez n'importe quelle bibliothèque compatible PSR-7 et utilisez-la;
  • Suppression de la dĂ©pendance Pimple . Installez votre conteneur compatible PSR-11 prĂ©fĂ©rĂ© et utilisez-le;
  • La possibilitĂ© d'utiliser votre routeur (Auparavant, il n'Ă©tait pas possible d'abandonner FastRoute );
  • Modification de l'implĂ©mentation de la gestion des erreurs;
  • Modification de l'implĂ©mentation de la sortie de rĂ©ponse;
  • Ajout d'une fabrique pour crĂ©er une instance de l'application;
  • Paramètres supprimĂ©s;
  • Slim ne dĂ©finit plus default_mimetype sur une chaĂ®ne vide, vous devez donc l'installer vous-mĂŞme dans php.ini ou dans votre application en utilisant ini_set('default_mimetype', '') ;
  • Le gestionnaire de demande d'application accepte dĂ©sormais uniquement l'objet de demande (dans l'ancienne version, il acceptait les objets de demande et de rĂ©ponse).

Comment créer une application maintenant?


Dans la troisième version, la création de l'application ressemblait à ceci:


 <?php use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Message\ResponseInterface; use Slim\App; require 'vendor/autoload.php'; $settings = [ 'addContentLengthHeader' => false, ]; $app = new App(['settings' => $settings]); $app->get('/hello/{name}', function (ServerRequestInterface $request, ResponseInterface $response, array $args) { $name = $args['name']; $response->getBody()->write("Hello, $name"); return $response; }); $app->run(); 

Le constructeur d'application accepte maintenant les paramètres suivants:


ParamètreTapezRequisLa description
$ responseFactoryPsr\Http\Message\ResponseFactoryInterfaceouiUsine de demande http de serveur compatible PSR-17
$ container\Psr\Container\ContainerInterfacenonConteneur de dépendance
$ callableResolver\Slim\Interfaces\CallableResolverInterfacenonGestionnaire de rappel
$ routeCollector\Slim\Interfaces\RouteCollectorInterfacenonRouteur
$ routeResolver\Slim\Interfaces\RouteResolverInterfacenonGestionnaire de résultats de routage

Vous pouvez également utiliser la méthode de create statique de l'application factory \Slim\Factory\AppFactory .
Cette méthode accepte les mêmes paramètres en entrée, seuls tous sont facultatifs.


 <?php use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Message\ResponseInterface; use Slim\Factory\AppFactory; require 'vendor/autoload.php'; $app = AppFactory::create(); $app->get('/hello/{name}', function (ServerRequestInterface $request, ResponseInterface $response) { $name = $request->getAttribute('name'); $response->getBody()->write("Hello, $name"); return $response; }); $app->run(); 

Rendez-moi 404 erreurs!


Si nous essayons d'ouvrir une page inexistante, nous obtenons un code de réponse de 500 , pas 404 . Pour que les erreurs soient traitées correctement, vous devez vous connecter \Slim\Middleware\ErrorMiddleware


 <?php use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Message\ResponseInterface; use Slim\Factory\AppFactory; use Slim\Middleware\ErrorMiddleware; require 'vendor/autoload.php'; $app = AppFactory::create(); $middleware = new ErrorMiddleware( $app->getCallableResolver(), $app->getResponseFactory(), false, false, false ); $app->add($middleware); $app->get('/hello/{name}', function (ServerRequestInterface $request, ResponseInterface $response) { $name = $request->getAttribute('name'); $response->getBody()->write("Hello, $name"); return $response; }); $app->run(); 

Middleware


Le middleware devrait désormais être une implémentation PSR-15. À titre d'exception, vous pouvez transmettre des fonctions, mais la signature doit correspondre à la méthode process() de l' \Psr\Http\Server\MiddlewareInterface


 <?php use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Message\ResponseInterface; use Psr\Http\Server\RequestHandlerInterface; use Slim\Factory\AppFactory; require 'vendor/autoload.php'; $app = AppFactory::create(); $app->add(function (ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface { $response = $handler->handle($request); return $response->withHeader('Content-Type', 'application/json'); }); // ...     $app->run(); 

La signature ($request, $response, $next) n'est plus prise en charge


Comment vivre sans paramètres?


Vous pouvez vivre sans paramètres. Les outils fournis nous y aideront.


httpVersion et responseChunkSize


Le httpVersion était responsable de la sortie de la version du protocole dans la réponse.
Le paramètre responseChunkSize déterminé la taille de chaque bloc lu dans le corps de la réponse lors de son envoi au navigateur.


Maintenant, ces fonctions peuvent être attribuées à l'émetteur de la réponse.


Écrire l'émetteur


 <?php // /src/ResponseEmitter.php namespace App; use Psr\Http\Message\ResponseInterface; use Slim\ResponseEmitter as SlimResponseEmitter; class ResponseEmitter extends SlimResponseEmitter { private $protocolVersion; public function __construct(string $protocolVersion = '1.1', int $responseChunkSize = 4096) { $this->protocolVersion = $protocolVersion; parent::__construct($responseChunkSize); } public function emit(ResponseInterface $response) : void{ parent::emit($response->withProtocolVersion($this->protocolVersion)); } } 

Nous nous connectons Ă  l'application


 <?php use App\ResponseEmitter; use Slim\Factory\AppFactory; require 'vendor/autoload.php'; $app = AppFactory::create(); $serverRequestFactory = \Slim\Factory\ServerRequestCreatorFactory::create(); $request = $serverRequestFactory->createServerRequestFromGlobals(); // ...     $response = $app->handle($request); $emitter = new ResponseEmitter('2.0', 4096); $emitter->emit($response); 

outputBuffering


Ce paramètre a permis d'activer / désactiver la mise en mémoire tampon de sortie. Définition des valeurs:


  • false - la mise en mĂ©moire tampon est dĂ©sactivĂ©e (tous les appels Ă  l' echo , print instructions d' print sont ignorĂ©s).
  • 'append' - tous les appels Ă  l' echo , print instructions print sont ajoutĂ©es après le corps de la rĂ©ponse
  • 'prepend' - tous les appels Ă  l' echo , print instructions print sont ajoutĂ©es avant le corps de la rĂ©ponse

\Slim\Middleware\OutputBufferingMiddleware développeurs de \Slim\Middleware\OutputBufferingMiddleware proposent de remplacer cette option par le \Slim\Middleware\OutputBufferingMiddleware , dans le constructeur duquel une fabrique de flux compatible PSR-17 et un mode pouvant être égal à append ou à append prepend


 <?php use Slim\Factory\AppFactory; use Slim\Factory\Psr17\SlimPsr17Factory; use Slim\Middleware\OutputBufferingMiddleware; require 'vendor/autoload.php'; $app = AppFactory::create(); $middleware = new OutputBufferingMiddleware(SlimPsr17Factory::getStreamFactory(), OutputBufferingMiddleware::APPEND); $app->add($middleware); // ...     $app->run(); 

determineRouteBeforeAppMiddleware


Ce paramètre a permis d'obtenir l'itinéraire actuel à partir de l'objet requête dans le middleware


Un remplacement est fourni \Slim\Middleware\RoutingMiddleware


 <?php use Slim\Factory\AppFactory; use Slim\Middleware\RoutingMiddleware; require 'vendor/autoload.php'; $app = AppFactory::create(); $middleware = new RoutingMiddleware($app->getRouteResolver()); $app->add($middleware); // ...     $app->run(); 

displayErrorDetails


Le paramètre permettait d'afficher les détails de l'erreur. Lors du débogage, cela facilite la vie.


Rappelez-vous \Slim\Middleware\ErrorMiddleware ? Ici, cela nous aidera!


 <?php use Slim\Factory\AppFactory; use Slim\Middleware\ErrorMiddleware; require 'vendor/autoload.php'; $app = AppFactory::create(); $middleware = new ErrorMiddleware( $app->getCallableResolver(), $app->getResponseFactory(), true, //        false, //   false //    ); $app->add($middleware); // ...     $app->run(); 

addContentLengthHeader


Ce paramètre a permis d'activer / désactiver l'ajout automatique de l'en Content-Length tête Content-Length avec la valeur du volume de données dans le corps de la réponse


Remplace l'option middleware \Slim\Middleware\ContentLengthMiddleware


 <?php use Slim\Factory\AppFactory; use Slim\Middleware\ContentLengthMiddleware; require 'vendor/autoload.php'; $app = AppFactory::create(); $middleware = new ContentLengthMiddleware(); $app->add($middleware); // ...     $app->run(); 

routerCacheFile


Vous pouvez maintenant installer directement le fichier cache du routeur


 <?php use Slim\Factory\AppFactory; require 'vendor/autoload.php'; $app = AppFactory::create(); $app->getRouteCollector()->setCacheFile('/path/to/cache/router.php'); // ...     $app->run(); 

Création d'une application sur Slim-4


Pour regarder de plus près le framework, nous allons écrire une petite application.


L'application aura les itinéraires suivants:


  • /hello/{name} - page d'accueil;
  • / - rediriger vers la page /hello/world
  • Les autres itinĂ©raires renverront une page personnalisĂ©e avec 404 erreurs.

La logique sera dans les contrôleurs, nous rendrons la page via le moteur de modèle Twig
En bonus, ajoutez une application console basée sur le composant Symfony Console avec une commande qui affiche une liste de routes


Étape 0. Installation des dépendances


Nous aurons besoin de:



J'ai sélectionné ultra-lite / container comme conteneur de dépendance, léger, concis et conforme à la norme.
Les développeurs PSR-7 et PSR-17 Slim fournissent slim / psr7 dans un seul paquet. Nous allons l'utiliser


Il est supposé que le gestionnaire de packages Composer est déjà installé.

Nous créons un dossier pour le projet ( /path/to/project sera utilisé comme exemple) et y allons.


Ajoutez le fichier composer.json au projet avec le contenu suivant:


 { "require": { "php": ">=7.1", "slim/slim": "4.0.0-beta", "slim/psr7": "~0.3", "ultra-lite/container": "^6.2", "symfony/console": "^4.2", "twig/twig": "^2.10" }, "autoload": { "psr-4": { "App\\": "app" } } } 

et exécutez la commande


 composer install 

Nous avons maintenant tous les packages nécessaires et le chargeur automatique de classe est configuré.


Si nous travaillons avec git , ajoutez le fichier .gitignore et ajoutez-y le répertoire du vendor (et le répertoire de votre IDE si nécessaire)


 /.idea/* /vendor/* 

J'utilise l'IDE PhpStorm et fier de ça . Pour un développement confortable, il est temps de se faire des amis avec le conteneur et l'IDE.
À la racine du projet, créez le fichier .phpstorm.meta.php et écrivez-y le code suivant:


 <?php // .phpstorm.meta.php namespace PHPSTORM_META { override( \Psr\Container\ContainerInterface::get(0), map([ '' => '@', ]) ); } 

Ce code indiquera à l'EDI que pour un objet qui implémente l' \Psr\Container\ContainerInterface , la méthode get() retournera un objet de classe ou une implémentation d'interface dont le nom est passé dans le paramètre.


Étape 1. Cadre d'application


Ajoutez les répertoires:


  • app - code d'application. Nous allons y connecter notre espace de noms pour le chargeur automatique de classe;
  • bin - rĂ©pertoire pour l'utilitaire de console;
  • config - voici les fichiers de configuration de l'application;
  • public - un rĂ©pertoire ouvert sur le Web (point d'entrĂ©e d'application, styles, js, images, etc.);
  • template - rĂ©pertoire de template ;
  • var est le rĂ©pertoire de divers fichiers. Journaux, cache, stockage local, etc.

Et les fichiers:


  • config/app.ini - la configuration principale de l'application;
  • config/app.local.ini - configuration pour local environnement local ;
  • app/Support/CommandMap.php - mappage des commandes d'application de la console pour le chargement paresseux.
  • app/Support/Config.php - Classe de configuration (pour que l'EDI sache quelles configurations nous avons).
  • app/Support/NotFoundHandler.php - Classe de gestionnaire d'erreurs 404.
  • app/Support/ServiceProviderInterface.php - L'interface du fournisseur de services.
  • app/Provider/AppProvider.php - Le principal fournisseur de l'application.
  • bootstrap.php - assemblage de conteneur;
  • bin/console - point d'entrĂ©e de l'application console;
  • public/index.php - point d'entrĂ©e de l'application Web.

config / app.ini
 ;   slim.debug=Off ;   templates.dir=template ;   templates.cache=var/cache/template 

config / app.local.ini
 ;        .       ;        slim.debug=On ;       templates.cache= 

Oh oui, il est toujours agréable d'exclure les configurations d'environnement du référentiel. Après tout, il peut y avoir des apparences / mots de passe. Le cache est également exclu.


.gitignore
 /.idea/* /config/* /vendor/* /var/cache/* !/config/app.ini !/var/cache/.gitkeep 

app / Support / CommandMap.php
 <?php // app/Support/CommandMap.php namespace App\Support; class CommandMap { /** *  .   =>    * @var string[] */ private $map = []; public function set(string $name, string $value) { $this->map[$name] = $value; } /** * @return string[] */ public function getMap() { return $this->map; } } 

app / Support / Config.php
 <?php // app/Support/Config.php namespace App\Support; class Config { /** * @var string[] */ private $config = []; public function __construct(string $dir, string $env, string $root) { if (!is_dir($dir)) return; /* *    */ $config = (array)parse_ini_file($dir . DIRECTORY_SEPARATOR . 'app.ini', false); /* *      */ $environmentConfigFile = $dir . DIRECTORY_SEPARATOR . 'app.' . $env . '.ini'; if (is_readable($environmentConfigFile)) { $config = array_replace_recursive($config, (array)parse_ini_file($environmentConfigFile, false)); } /* * ,      */ $dirs = ['templates.dir', 'templates.cache']; foreach ($config as $name=>$value) { $this->config[$name] = $value; } /* *      */ foreach ($dirs as $parameter) { $value = $config[$parameter]; if (mb_strpos($value, '/') === 0) { continue; } if (empty($value)) { $this->config[$parameter] = null; continue; } $this->config[$parameter] = $root . DIRECTORY_SEPARATOR . $value; } } public function get(string $name) { return array_key_exists($name, $this->config) ? $this->config[$name] : null; } } 

app / Support / NotFoundHandler.php
 <?php // app/Support/NotFoundHandler.php namespace App\Support; use Psr\Http\Message\ResponseFactoryInterface; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; use Slim\Interfaces\ErrorHandlerInterface; use Throwable; class NotFoundHandler implements ErrorHandlerInterface { private $factory; public function __construct(ResponseFactoryInterface $factory) { $this->factory = $factory; } /** * @param ServerRequestInterface $request * @param Throwable $exception * @param bool $displayErrorDetails * @param bool $logErrors * @param bool $logErrorDetails * @return ResponseInterface */ public function __invoke(ServerRequestInterface $request, Throwable $exception, bool $displayErrorDetails, bool $logErrors, bool $logErrorDetails): ResponseInterface { $response = $this->factory->createResponse(404); return $response; } } 

Vous pouvez maintenant apprendre à PhpStorm à comprendre quelles clés ont des clés et de quel type elles sont.


.phpstorm.meta.php
 <?php // .phpstorm.meta.php namespace PHPSTORM_META { override( \Psr\Container\ContainerInterface::get(0), map([ '' => '@', ]) ); override( \App\Support\Config::get(0), map([ 'slim.debug' => 'bool', 'templates.dir' => 'string|false', 'templates.cache' => 'string|false', ]) ); } 

app / Support / ServiceProviderInterface.php
 <?php // app/Support/ServiceProviderInterface.php namespace App\Support; use UltraLite\Container\Container; interface ServiceProviderInterface { public function register(Container $container); } 

app / Provider / AppProvider.php
 <?php // app/Provider/AppProvider.php namespace App\Provider; use App\Support\CommandMap; use App\Support\Config; use App\Support\NotFoundHandler; use App\Support\ServiceProviderInterface; use Psr\Container\ContainerInterface; use Psr\Http\Message\ResponseFactoryInterface; use Slim\CallableResolver; use Slim\Exception\HttpNotFoundException; use Slim\Interfaces\CallableResolverInterface; use Slim\Interfaces\RouteCollectorInterface; use Slim\Interfaces\RouteResolverInterface; use Slim\Middleware\ErrorMiddleware; use Slim\Middleware\RoutingMiddleware; use Slim\Psr7\Factory\ResponseFactory; use Slim\Routing\RouteCollector; use Slim\Routing\RouteResolver; use UltraLite\Container\Container; class AppProvider implements ServiceProviderInterface { public function register(Container $container) { /* *     */ $container->set(CommandMap::class, function () { return new CommandMap(); }); /* *   http- */ $container->set(ResponseFactory::class, function () { return new ResponseFactory(); }); /* *    http-   */ $container->set(ResponseFactoryInterface::class, function (ContainerInterface $container) { return $container->get(ResponseFactory::class); }); /* *     */ $container->set(CallableResolver::class, function (ContainerInterface $container) { return new CallableResolver($container); }); /* *        */ $container->set(CallableResolverInterface::class, function (ContainerInterface $container) { return $container->get(CallableResolver::class); }); /* *   */ $container->set(RouteCollector::class, function (ContainerInterface $container) { $router = new RouteCollector( $container->get(ResponseFactoryInterface::class), $container->get(CallableResolverInterface::class), $container ); return $router; }); /* *      */ $container->set(RouteCollectorInterface::class, function (ContainerInterface $container) { return $container->get(RouteCollector::class); }); /* *     */ $container->set(RouteResolver::class, function (ContainerInterface $container) { return new RouteResolver($container->get(RouteCollectorInterface::class)); }); /* *        */ $container->set(RouteResolverInterface::class, function (ContainerInterface $container) { return $container->get(RouteResolver::class); }); /* *    404 */ $container->set(NotFoundHandler::class, function (ContainerInterface $container) { return new NotFoundHandler($container->get(ResponseFactoryInterface::class)); }); /* *  middleware   */ $container->set(ErrorMiddleware::class, function (ContainerInterface $container) { $middleware = new ErrorMiddleware( $container->get(CallableResolverInterface::class), $container->get(ResponseFactoryInterface::class), $container->get(Config::class)->get('slim.debug'), true, true); $middleware->setErrorHandler(HttpNotFoundException::class, $container->get(NotFoundHandler::class)); return $middleware; }); /* *  middleware  */ $container->set(RoutingMiddleware::class, function (ContainerInterface $container) { return new RoutingMiddleware($container->get(RouteResolverInterface::class)); }); } } 

Nous avons déplacé le routage vers le conteneur afin de pouvoir travailler avec lui sans initialiser l'objet \Slim\App .


bootstrap.php
 <?php // bootstrap.php require_once __DIR__ . DIRECTORY_SEPARATOR . 'vendor' . DIRECTORY_SEPARATOR . 'autoload.php'; use App\Support\ServiceProviderInterface; use App\Provider\AppProvider; use App\Support\Config; use UltraLite\Container\Container; /* *   */ $env = getenv('APP_ENV'); if (!$env) $env = 'local'; /* *   */ $config = new Config(__DIR__ . DIRECTORY_SEPARATOR . 'config', $env, __DIR__); /* *  - */ $providers = [ AppProvider::class, ]; /* *    */ $container = new Container([ Config::class => function () use ($config) { return $config;}, ]); /* *   */ foreach ($providers as $className) { if (!class_exists($className)) { /** @noinspection PhpUnhandledExceptionInspection */ throw new Exception('Provider ' . $className . ' not found'); } $provider = new $className; if (!$provider instanceof ServiceProviderInterface) { /** @noinspection PhpUnhandledExceptionInspection */ throw new Exception($className . ' has not provider'); } $provider->register($container); } /* *   */ return $container; 

bac / console
 #!/usr/bin/env php <?php // bin/console use App\Support\CommandMap; use Psr\Container\ContainerInterface; use Symfony\Component\Console\Application; use Symfony\Component\Console\CommandLoader\ContainerCommandLoader; use Symfony\Component\Console\Input\ArgvInput; use Symfony\Component\Console\Output\ConsoleOutput; /** @var ContainerInterface $container */ $container = require dirname(__DIR__) . DIRECTORY_SEPARATOR . 'bootstrap.php'; $loader = new ContainerCommandLoader($container, $container->get(CommandMap::class)->getMap()); $app = new Application(); $app->setCommandLoader($loader); /** @noinspection PhpUnhandledExceptionInspection */ $app->run(new ArgvInput(), new ConsoleOutput()); 

Il est conseillé de donner à ce fichier l'autorisation d'exécuter


 chmod +x ./bin/console 

public / index.php
 <?php // public/index.php use Psr\Container\ContainerInterface; use Psr\Http\Message\ResponseFactoryInterface; use Slim\App; use Slim\Interfaces\CallableResolverInterface; use Slim\Interfaces\RouteCollectorInterface; use Slim\Interfaces\RouteResolverInterface; use Slim\Middleware\ErrorMiddleware; use Slim\Middleware\RoutingMiddleware; use Slim\Psr7\Factory\ServerRequestFactory; /** @var ContainerInterface $container */ $container = require dirname(__DIR__) . DIRECTORY_SEPARATOR . 'bootstrap.php'; $request = ServerRequestFactory::createFromGlobals(); Slim\Factory\AppFactory::create(); $app = new App( $container->get(ResponseFactoryInterface::class), $container, $container->get(CallableResolverInterface::class), $container->get(RouteCollectorInterface::class), $container->get(RouteResolverInterface::class) ); $app->add($container->get(RoutingMiddleware::class)); $app->add($container->get(ErrorMiddleware::class)); $app->run($request); 

Vérification
Lancez l'application console:


 ./bin/console 

En réponse, la fenêtre de bienvenue du composant symfony/console devrait apparaître avec deux commandes disponibles - help et list .


 Console Tool Usage: command [options] [arguments] Options: -h, --help Display this help message -q, --quiet Do not output any message -V, --version Display this application version --ansi Force ANSI output --no-ansi Disable ANSI output -n, --no-interaction Do not ask any interactive question -v|vv|vvv, --verbose Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug Available commands: help Displays help for a command list Lists commands 

Démarrez maintenant le serveur Web.


 php -S localhost:8080 -t public public/index.php 

Et ouvrez n'importe quelle URL sur localhost: 8080.
Toutes les demandes doivent renvoyer une réponse avec le code 404 et un corps vide.
Cela se produit car nous n'avons aucun itinéraire répertorié.


Il nous reste à connecter le rendu, écrire des modèles, des contrôleurs et définir des routes.


Étape 2. Rendu


Ajoutez le modèle template/layout.twig . Il s'agit du modèle de base pour toutes les pages.


template / layout.twig
 {# template/layout.twig #} <!DOCTYPE html> <html lang="en"> <head> <title>{% block title %}Slim demo{% endblock %}</title> </head> <body> {% block content %}{% endblock %} </body> </html> 

Ajouter un template/hello.twig page d'accueil template/hello.twig


template / hello.twig
 {# template/hello.twig #} {% extends 'layout.twig' %} {% block title %}Slim demo::hello, {{ name }}{% endblock %} {% block content %} <h1>Welcome!</h1> <p>Hello, {{ name }}!</p> {% endblock %} 

Et le template/err404.twig page d'erreur template/err404.twig


template / err404.twig
 {# template/err404.twig #} {% extends 'layout.twig' %} {% block title %}Slim demo::not found{% endblock %} {% block content %} <h1>Error!</h1> <p>Page not found =(</p> {% endblock %} 

Ajoutez l' app/Provider/RenderProvider.php fournisseur de rendu app/Provider/RenderProvider.php


app / Provider / RenderProvider.php
 <?php // app/Provider/RenderProvider.php namespace App\Provider; use App\Support\Config; use App\Support\ServiceProviderInterface; use Psr\Container\ContainerInterface; use Twig\Environment; use Twig\Loader\FilesystemLoader; use UltraLite\Container\Container; class RenderProvider implements ServiceProviderInterface { public function register(Container $container) { $container->set(Environment::class, function (ContainerInterface $container) { $config = $container->get(Config::class); $loader = new FilesystemLoader($config->get('templates.dir')); $cache = $config->get('templates.cache'); $options = [ 'cache' => empty($cache) ? false : $cache, ]; $twig = new Environment($loader, $options); return $twig; }); } } 

Activer le fournisseur dans bootstrap


bootstrap.php
 <?php // bootstrap.php // ... use App\Provider\RenderProvider; // ... $providers = [ // ... RenderProvider::class, // ... ]; // ... 

Ajouter un rendu au gestionnaire d'erreurs 404


app / Support / NotFoundHandler.php (DIFF)
 --- a/app/Support/NotFoundHandler.php +++ b/app/Support/NotFoundHandler.php @@ -8,15 +8,22 @@ use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; use Slim\Interfaces\ErrorHandlerInterface; use Throwable; +use Twig\Environment; +use Twig\Error\LoaderError; +use Twig\Error\RuntimeError; +use Twig\Error\SyntaxError; class NotFoundHandler implements ErrorHandlerInterface { private $factory; - public function __construct(ResponseFactoryInterface $factory) + private $render; + + public function __construct(ResponseFactoryInterface $factory, Environment $render) { $this->factory = $factory; + $this->render = $render; } /** @@ -26,10 +33,14 @@ class NotFoundHandler implements ErrorHandlerInterface * @param bool $logErrors * @param bool $logErrorDetails * @return ResponseInterface + * @throws LoaderError + * @throws RuntimeError + * @throws SyntaxError */ public function __invoke(ServerRequestInterface $request, Throwable $exception, bool $displayErrorDetails, bool $logErrors, bool $logErrorDetails): ResponseInterface { $response = $this->factory->createResponse(404); + $response->getBody()->write($this->render->render('err404.twig')); return $response; } } 

app / Provider / AppProvider.php (DIFF)
 --- a/app/Provider/AppProvider.php +++ b/app/Provider/AppProvider.php @@ -19,6 +19,7 @@ use Slim\Middleware\RoutingMiddleware; use Slim\Psr7\Factory\ResponseFactory; use Slim\Routing\RouteCollector; use Slim\Routing\RouteResolver; +use Twig\Environment; use UltraLite\Container\Container; class AppProvider implements ServiceProviderInterface @@ -99,7 +100,7 @@ class AppProvider implements ServiceProviderInterface *    404 */ $container->set(NotFoundHandler::class, function (ContainerInterface $container) { - return new NotFoundHandler($container->get(ResponseFactoryInterface::class)); + return new NotFoundHandler($container->get(ResponseFactoryInterface::class), $container->get(Environment::class)); }); /* 

404 .


3.



2:


  • app/Controller/HomeController.php —
  • app/Controller/HelloController.php —

( URL ), — ( html)


app/Controller/HomeController.php
 <?php // app/Controller/HomeController.php namespace App\Controller; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; use Slim\Interfaces\RouteParserInterface; class HomeController { /** * @var RouteParserInterface */ private $router; public function __construct(RouteParserInterface $router) { $this->router = $router; } public function index(ServerRequestInterface $request, ResponseInterface $response) { $uri = $this->router->fullUrlFor($request->getUri(), 'hello', ['name' => 'world']); return $response ->withStatus(301) ->withHeader('location', $uri); } } 

app/Controller/HelloController.php
 <?php // app/Controller/HelloController.php namespace App\Controller; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; use Twig\Environment as Render; use Twig\Error\LoaderError; use Twig\Error\RuntimeError; use Twig\Error\SyntaxError; class HelloController { /** * @var Render */ private $render; public function __construct(Render $render) { $this->render = $render; } /** * @param ServerRequestInterface $request * @param ResponseInterface $response * @return ResponseInterface * @throws LoaderError * @throws RuntimeError * @throws SyntaxError */ public function show(ServerRequestInterface $request, ResponseInterface $response) { $response->getBody()->write($this->render->render('hello.twig', ['name' => $request->getAttribute('name')])); return $response; } } 

,


app/Provider/WebProvider.php
 <?php // app/Provider/WebProvider.php namespace App\Provider; use App\Controller\HelloController; use App\Controller\HomeController; use App\Support\ServiceProviderInterface; use Psr\Container\ContainerInterface; use Slim\Interfaces\RouteCollectorInterface; use Slim\Interfaces\RouteCollectorProxyInterface; use Twig\Environment; use UltraLite\Container\Container; class WebProvider implements ServiceProviderInterface { public function register(Container $container) { /* *   */ $container->set(HomeController::class, function (ContainerInterface $container) { return new HomeController($container->get(RouteCollectorInterface::class)->getRouteParser()); }); $container->set(HelloController::class, function (ContainerInterface $container) { return new HelloController($container->get(Environment::class)); }); /* *   */ $router = $container->get(RouteCollectorInterface::class); $router->group('/', function(RouteCollectorProxyInterface $router) { $router->get('', HomeController::class . ':index')->setName('index'); $router->get('hello/{name}', HelloController::class . ':show')->setName('hello'); }); } } 


bootstrap.php
 <?php // bootstrap.php // ... use App\Provider\WebProvider; // ... $providers = [ // ... WebProvider::class, // ... ]; // ... 

- ( )...


 php -S localhost:8080 -t public public/index.php 

… http://localhost:8080 , http://localhost:8080/hello/world


world'.
http://localhost:8080/hello/ivan ivan'.


, , http://localhost:8080/helo/world 404 .


4.


route:list


app/Command/RouteListCommand.php
 <?php // app/Command/RouteListCommand.php namespace App\Command; use Slim\Interfaces\RouteCollectorInterface; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; class RouteListCommand extends Command { /* *    ,        */ const NAME = 'route:list'; /** * @var RouteCollectorInterface */ private $router; public function __construct(RouteCollectorInterface $router) { $this->router = $router; parent::__construct(); } protected function configure() { $this->setName(self::NAME) ->setDescription('List of routes.') ->setHelp('List of routes.') ; } protected function execute(InputInterface $input, OutputInterface $output) { $io = new SymfonyStyle($input, $output); $io->title('Routes'); $rows = []; $routes = $this->router->getRoutes(); if (!$routes) { $io->text('Routes list is empty'); return 0; } foreach ($routes as $route) { $rows[] = [ 'path' => $route->getPattern(), 'methods' => implode(', ', $route->getMethods()), 'name' => $route->getName(), 'handler' => $route->getCallable(), ]; } $io->table( ['Route', 'Methods', 'Name', 'Handler'], $rows ); return 0; } } 

,


app/Provider/CommandProvider.php
 <?php // app/Provider/CommandProvider.php namespace App\Provider; use App\Command\RouteListCommand; use App\Support\CommandMap; use App\Support\ServiceProviderInterface; use Psr\Container\ContainerInterface; use Slim\Interfaces\RouteCollectorInterface; use UltraLite\Container\Container; class CommandProvider implements ServiceProviderInterface { public function register(Container $container) { /* *       */ $container->set(RouteListCommand::class, function (ContainerInterface $container) { return new RouteListCommand($container->get(RouteCollectorInterface::class)); }); /* *        */ $container->get(CommandMap::class)->set(RouteListCommand::NAME, RouteListCommand::class); } } 


bootstrap.php
 <?php // bootstrap.php // ... use App\Provider\CommandProvider; // ... $providers = [ // ... CommandProvider::class, // ... ]; // ... 

...


 ./bin/console route:list 

… :


 Routes ====== --------------- --------- ------- ------------------------------------- Route Methods Name Handler --------------- --------- ------- ------------------------------------- / GET index App\Controller\HomeController:index /hello/{name} GET hello App\Controller\HelloController:show --------------- --------- ------- ------------------------------------- 

, , !


, Slim — routes.php ( ), . — , , .


Source: https://habr.com/ru/post/fr452834/


All Articles