Microframework delgado

El 25 de abril de 2019, la nueva versión principal alpha del microframework Slim vio la luz del día, y el 18 de mayo se convirtió en beta . Le sugiero que se familiarice con la nueva versión.


Debajo del corte:


  • Sobre las innovaciones del marco
  • Escribir una aplicación simple en Slim-4
  • Sobre Slim y PhpStorm Friendship

Nuevo en Slim 4


Innovaciones clave en comparación con la versión 3:


  • La versión mínima de PHP es 7.1;
  • Soporte para PSR-15 (Middleware);
  • Se eliminó la implementación de mensajes http. Instale cualquier biblioteca compatible con PSR-7 y úsela;
  • Se eliminó la dependencia de espinillas . Instale su contenedor compatible con PSR-11 favorito y úselo;
  • La capacidad de usar su enrutador (Anteriormente, no era posible abandonar FastRoute );
  • Se modificó la implementación del manejo de errores;
  • Se modificó la implementación de la salida de respuesta;
  • Fábrica agregada para crear una instancia de la aplicación;
  • Configuraciones eliminadas;
  • Slim ya no establece default_mimetype en una cadena vacía, por lo que debe instalarlo usted mismo en php.ini o en su aplicación usando ini_set('default_mimetype', '') ;
  • El controlador de solicitud de aplicación ahora solo acepta el objeto de solicitud (en la versión anterior, aceptaba objetos de solicitud y respuesta).

¿Cómo crear una aplicación ahora?


En la tercera versión, la creación de la aplicación se parecía a esto:


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

Ahora el constructor de la aplicación acepta los siguientes parámetros:


ParámetroTipoRequeridoDescripción
$ responseFactoryPsr\Http\Message\ResponseFactoryInterfacesiServidor PSR-17 compatible con fábrica de solicitud http
$ contenedor\Psr\Container\ContainerInterfacenoContenedor de dependencia
$ callableResolver\Slim\Interfaces\CallableResolverInterfacenoControlador de devolución de llamada
$ routeCollector\Slim\Interfaces\RouteCollectorInterfacenoEnrutador
$ routeResolver\Slim\Interfaces\RouteResolverInterfacenoControlador de resultados de enrutamiento

También ahora puede utilizar el método de create estática de la aplicación factory \Slim\Factory\AppFactory .
Este método acepta los mismos parámetros como entrada, solo todos son opcionales.


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

¡Devuélveme errores 404!


Si intentamos abrir una página inexistente, obtenemos un código de respuesta de 500 , no 404 . Para que los errores se procesen correctamente, debe conectar \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


El middleware ahora debería ser una implementación de PSR-15. Como excepción, puede pasar funciones, pero la firma debe coincidir con el método process() de la interfaz \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 firma ($request, $response, $next) ya no es compatible


¿Cómo vivir sin ajustes?


Puedes vivir sin ajustes. Las herramientas proporcionadas nos ayudarán con esto.


httpVersion and responseChunkSize


La httpVersion fue responsable de generar la versión del protocolo en la respuesta.
La configuración responseChunkSize determinó el tamaño de cada fragmento leído del cuerpo de la respuesta cuando se envió al navegador.


Ahora estas funciones pueden asignarse al emisor de la respuesta.


Emisor de escritura


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

Nos conectamos a la aplicación


 <?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


Esta configuración permitió activar / desactivar la memoria intermedia de salida. Establecer valores:


  • false : el almacenamiento en búfer está desactivado (se ignoran todas las llamadas al echo , print declaraciones de print ).
  • 'append' : todas las llamadas al echo , print declaraciones de print se agregan después del cuerpo de la respuesta
  • 'prepend' : todas las llamadas al echo , print declaraciones de print se agregan antes del cuerpo de respuesta

\Slim\Middleware\OutputBufferingMiddleware desarrolladores de \Slim\Middleware\OutputBufferingMiddleware proponen reemplazar esta opción con \Slim\Middleware\OutputBufferingMiddleware , en cuyo constructor prepend una fábrica de flujo compatible con PSR-17 y un modo que puede ser igual de append o 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


Esta configuración hizo posible obtener la ruta actual del objeto de solicitud en el middleware


Se proporciona un reemplazo \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


La configuración permitió mostrar detalles del error. Al depurar, hace la vida más fácil.


¿Recuerdas \Slim\Middleware\ErrorMiddleware ? ¡Aquí nos ayudará!


 <?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


Esta configuración permitió habilitar / deshabilitar la adición automática del encabezado Content-Length con el valor del volumen de datos en el cuerpo de la respuesta


Reemplaza la opción de 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


Ahora puede instalar directamente el archivo de caché del enrutador


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

Crear una aplicación en Slim-4


Para echar un vistazo más de cerca al marco, escribiremos una pequeña aplicación.


La aplicación tendrá las siguientes rutas:


  • /hello/{name} - página de bienvenida;
  • / - redirigir a la página /hello/world
  • Otras rutas devolverán una página personalizada con errores 404.

La lógica estará en los controladores, renderizaremos la página a través del motor de plantillas Twig
Como beneficio adicional, agregue una aplicación de consola basada en el componente Consola de Symfony con un comando que muestra una lista de rutas


Paso 0. Instalar dependencias


Necesitaremos:



Seleccioné ultra-lite / container como el contenedor de dependencia, como ligero, conciso y compatible con el estándar.
Los desarrolladores de PSR-7 y PSR-17 Slim proporcionan slim / psr7 en un solo paquete. Lo usaremos


Se supone que el administrador de paquetes Composer ya está instalado.

Creamos una carpeta para el proyecto ( /path/to/project se utilizará como ejemplo) y vamos a él.


Agregue el archivo composer.json al proyecto con los siguientes contenidos:


 { "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" } } } 

y ejecuta el comando


 composer install 

Ahora tenemos todos los paquetes necesarios y el autocargador de clase está configurado.


Si trabajamos con git , agregue el archivo .gitignore y agregue el directorio de vendor allí (y el directorio de su IDE si es necesario)


 /.idea/* /vendor/* 

Estoy usando el IDE de PhpStorm y orgulloso de ello . Para un desarrollo cómodo, es hora de hacer amigos con el contenedor y el IDE.
En la raíz del proyecto, cree el archivo .phpstorm.meta.php y escriba el siguiente código allí:


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

Este código le indicará al IDE que para un objeto que implementa la interfaz \Psr\Container\ContainerInterface , el método get() devolverá un objeto de clase o una implementación de interfaz cuyo nombre se pasa en el parámetro.


Paso 1. Marco de aplicación


Agregue los directorios:


  • app : código de aplicación. Conectaremos nuestro espacio de nombres para el cargador automático de clase;
  • bin - directorio para la utilidad de la consola;
  • config : aquí estarán los archivos de configuración de la aplicación;
  • public : un directorio abierto en la web (punto de entrada de la aplicación, estilos, js, imágenes, etc.);
  • template - directorio de template ;
  • var es el directorio para varios archivos. Registros, caché, almacenamiento local, etc.

Y los archivos:


  • config/app.ini : la configuración principal de la aplicación;
  • config/app.local.ini : configuración para local entorno local ;
  • app/Support/CommandMap.php : asignación de comandos de aplicaciones de consola para carga diferida.
  • app/Support/Config.php - Clase de configuración (para que el IDE sepa qué configuraciones tenemos).
  • app/Support/NotFoundHandler.php : clase de controlador de errores 404.
  • app/Support/ServiceProviderInterface.php : la interfaz del proveedor de servicios.
  • app/Provider/AppProvider.php : el proveedor principal de la aplicación.
  • bootstrap.php - ensamblaje del contenedor;
  • bin/console : punto de entrada de la aplicación de consola;
  • public/index.php : punto de entrada de la aplicación web.

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

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

Ah, sí, aún es bueno excluir configuraciones de entorno del repositorio. Después de todo, puede haber apariencias / contraseñas. El caché también está excluido.


.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; } } 

aplicación / Soporte / 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; } } 

aplicación / Soporte / 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; } } 

Ahora puede enseñarle a PhpStorm a comprender qué teclas tienen claves y de qué tipo son.


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

Movimos la ruta al contenedor para poder trabajar con ella sin inicializar el objeto \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; 

bin / consola
 #!/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()); 

Es recomendable dar permiso a este archivo para ejecutar


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

Verificación
Inicie la aplicación de consola:


 ./bin/console 

En respuesta, la ventana de bienvenida para el componente symfony/console debería aparecer con dos comandos disponibles: help y 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 

Ahora inicie el servidor web.


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

Y abra cualquier url en localhost: 8080.
Todas las solicitudes deben devolver una respuesta con el código 404 y un cuerpo vacío.
Esto sucede porque no tenemos rutas enumeradas.


Nos queda por conectar el render, escribir plantillas, controladores y establecer rutas.


Paso 2. Renderizar


Agregue la plantilla template/layout.twig . Esta es la plantilla básica para todas las páginas.


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> 

Agregar una template/hello.twig página de bienvenida 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 %} 

Y la template/err404.twig página de error 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 %} 

Agregue la app/Provider/RenderProvider.php proveedor de representación app/Provider/RenderProvider.php


aplicación / Proveedor / 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; }); } } 

Encienda el proveedor en bootstrap


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

Agregue un render al controlador de errores 404


aplicación / Soporte / 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/452834/


All Articles