Schlankes Mikroframework

Am 25. April 2019 erblickte die neue alpha Hauptversion des Slim- Mikroframeworks das Licht der Welt und am 18. Mai wurde sie zur beta Version. Ich schlage vor, dass Sie sich mit der neuen Version vertraut machen.


Unter dem Schnitt:


  • Über die Neuerungen des Frameworks
  • Schreiben einer einfachen Anwendung auf Slim-4
  • Über Slim und PhpStorm Friendship

Neu in Slim 4


Wichtige Neuerungen im Vergleich zu Version 3:


  • Die Mindestversion von PHP ist 7.1;
  • Unterstützung für PSR-15 (Middleware);
  • Die Implementierung von http-Nachrichten wurde entfernt. Installieren Sie eine PSR-7-kompatible Bibliothek und verwenden Sie sie.
  • Pickelabhängigkeit entfernt. Installieren Sie Ihren bevorzugten PSR-11-kompatiblen Container und verwenden Sie ihn.
  • Die Möglichkeit, Ihren Router zu verwenden (Bisher war es nicht möglich, FastRoute aufzugeben).
  • Die Implementierung der Fehlerbehandlung wurde geändert.
  • Geänderte Implementierung der Antwortausgabe;
  • Factory hinzugefügt, um eine Instanz der Anwendung zu erstellen;
  • Einstellungen entfernt;
  • Slim setzt default_mimetype nicht mehr auf eine leere Zeichenfolge, daher müssen Sie diese selbst in der php.ini oder in Ihrer Anwendung mit ini_set('default_mimetype', '') installieren.
  • Der Anwendungsanforderungshandler akzeptiert jetzt nur das Anforderungsobjekt (in der alten Version hat er Anforderungs- und Antwortobjekte akzeptiert).

Wie erstelle ich jetzt eine Anwendung?


In der dritten Version sah das Erstellen der Anwendung ungefähr so ​​aus:


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

Jetzt akzeptiert der Anwendungskonstruktor die folgenden Parameter:


ParameterTypErforderlichBeschreibung
$ responseFactoryPsr\Http\Message\ResponseFactoryInterfacejaPSR-17-kompatibler Server http Request Factory
$ container\Psr\Container\ContainerInterfaceNeinAbhängigkeitscontainer
$ callableResolver\Slim\Interfaces\CallableResolverInterfaceNeinRückruf-Handler
$ routeCollector\Slim\Interfaces\RouteCollectorInterfaceNeinRouter
$ routeResolver\Slim\Interfaces\RouteResolverInterfaceNeinHandler für Routing-Ergebnisse

Sie können jetzt auch die statische create der Application Factory \Slim\Factory\AppFactory .
Diese Methode akzeptiert dieselben Parameter wie die Eingabe, nur alle sind optional.


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

Gib mir 404 Fehler zurück!


Wenn wir versuchen, eine nicht vorhandene Seite zu öffnen, erhalten wir einen Antwortcode von 500 und nicht von 404 . Damit Fehler korrekt verarbeitet werden, müssen Sie \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


Middleware sollte jetzt eine PSR-15-Implementierung sein. Ausnahmsweise können Sie Funktionen übergeben, die Signatur muss jedoch mit der process() -Methode der Schnittstelle \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(); 

Die Signatur ($request, $response, $next) nicht mehr unterstützt


Wie lebe ich ohne Einstellungen?


Sie können ohne Einstellungen leben. Die bereitgestellten Tools helfen uns dabei.


httpVersion und responseChunkSize


Die httpVersion war für die Ausgabe der Protokollversion in der Antwort verantwortlich.
Die Einstellung responseChunkSize bestimmte die Größe jedes Chunks, der aus dem Antworttext gelesen wurde, wenn er an den Browser gesendet wurde.


Diese Funktionen können nun dem Sender der Antwort zugewiesen werden.


Emitter schreiben


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

Wir verbinden uns mit der Anwendung


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


Diese Einstellung ermöglichte das Ein- und Ausschalten der Ausgangspufferung. Werte einstellen:


  • false - Die Pufferung ist deaktiviert (alle Aufrufe von echo , print werden ignoriert).
  • 'append' - Alle Aufrufe von echo und print werden nach dem Antworttext hinzugefügt
  • 'prepend' - Alle Aufrufe von echo und print werden vor dem Antworttext hinzugefügt

\Slim\Middleware\OutputBufferingMiddleware Entwickler schlagen vor, diese Option durch \Slim\Middleware\OutputBufferingMiddleware zu \Slim\Middleware\OutputBufferingMiddleware , in deren Konstruktor eine PSR-17-kompatible Stream-Factory und ein Modus übergeben werden, der dem append oder prepend gleich sein kann


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


Diese Einstellung ermöglichte es, die aktuelle Route vom Anforderungsobjekt in der Middleware abzurufen


Ein Ersatz wird bereitgestellt \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


Die Einstellung erlaubt die Anzeige von Fehlerdetails. Beim Debuggen erleichtert es das Leben.


\Slim\Middleware\ErrorMiddleware du dich an \Slim\Middleware\ErrorMiddleware ? Hier wird es uns helfen!


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


Mit dieser Einstellung konnte das automatische Hinzufügen des Content-Length Headers mit dem Wert des Datenvolumens im Antworttext aktiviert / deaktiviert werden


Ersetzt die Middleware-Option \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


Jetzt können Sie die Router-Cache-Datei direkt installieren


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

Erstellen einer Anwendung auf Slim-4


Um das Framework genauer zu betrachten, werden wir eine kleine Anwendung schreiben.


Die Anwendung hat die folgenden Routen:


  • /hello/{name} - Begrüßungsseite;
  • / - Weiterleiten auf die Seite /hello/world
  • Andere Routen geben eine benutzerdefinierte Seite mit 404-Fehlern zurück.

Die Logik wird in den Controllern sein, wir werden die Seite durch die Twig Template Engine rendern
Fügen Sie als Bonus eine Konsolenanwendung hinzu, die auf der Symfony Console- Komponente basiert, mit einem Befehl, der eine Liste von Routen anzeigt


Schritt 0. Abhängigkeiten installieren


Wir werden brauchen:



Ich habe Ultra-Lite / Container als Abhängigkeitscontainer ausgewählt, als leicht, präzise und konform mit dem Standard.
PSR-7 und PSR-17 Slim-Entwickler bieten slim / psr7 in einem Paket. Wir werden es benutzen


Es wird davon ausgegangen, dass der Composer- Paketmanager bereits installiert ist.

Wir erstellen einen Ordner für das Projekt ( /path/to/project wird als Beispiel verwendet) und gehen dorthin.


Fügen Sie die Datei composer.json mit dem folgenden Inhalt zum Projekt hinzu:


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

und führen Sie den Befehl aus


 composer install 

Jetzt haben wir alle notwendigen Pakete und der Klassen-Autoloader ist konfiguriert.


Wenn wir mit git arbeiten , fügen Sie die .gitignore Datei hinzu und fügen Sie dort das vendor (und ggf. das Verzeichnis Ihrer IDE).


 /.idea/* /vendor/* 

Ich verwende die PhpStorm- IDE und stolz darauf . Für eine komfortable Entwicklung ist es Zeit, sich mit dem Container und der IDE anzufreunden.
Erstellen .phpstorm.meta.php im Stammverzeichnis des Projekts die Datei .phpstorm.meta.php und schreiben Sie dort den folgenden Code:


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

Dieser Code teilt der IDE mit, dass die Methode get() für ein Objekt, das die Schnittstelle \Psr\Container\ContainerInterface implementiert, ein Klassenobjekt oder eine Schnittstellenimplementierung \Psr\Container\ContainerInterface , deren Name im Parameter übergeben wird.


Schritt 1. Anwendungsframework


Fügen Sie die Verzeichnisse hinzu:


  • app - Anwendungscode. Wir werden unseren Namespace für den Klassen-Autoloader damit verbinden.
  • bin - Verzeichnis für das Konsolendienstprogramm;
  • config - hier sind die Anwendungskonfigurationsdateien;
  • public - ein im Web geöffnetes Verzeichnis (Anwendungseinstiegspunkt, Stile, js, Bilder usw.);
  • template - template ;
  • var ist das Verzeichnis für verschiedene Dateien. Protokolle, Cache, lokaler Speicher usw.

Und die Dateien:


  • config/app.ini - die Hauptanwendungskonfiguration;
  • config/app.local.ini - Konfiguration für local Umgebung;
  • app/Support/CommandMap.php - Zuordnung von Konsolenanwendungsbefehlen zum verzögerten Laden.
  • app/Support/Config.php - Konfigurationsklasse (damit die IDE weiß, welche Konfigurationen wir haben).
  • app/Support/NotFoundHandler.php - 404 Fehlerbehandlungsklasse.
  • app/Support/ServiceProviderInterface.php - Die Schnittstelle des Dienstanbieters.
  • app/Provider/AppProvider.php - Der Hauptanbieter der Anwendung.
  • bootstrap.php - Container-Assembly;
  • bin/console - Einstiegspunkt der Konsolenanwendung;
  • public/index.php - Einstiegspunkt der Webanwendung.

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

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

Oh ja, es ist immer noch schön, Umgebungskonfigurationen aus dem Repository auszuschließen. Schließlich kann es Erscheinungen / Passwörter geben. Der Cache ist ebenfalls ausgeschlossen.


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

Jetzt können Sie PhpStorm beibringen, zu verstehen, welche Schlüssel Schlüssel haben und welchen Typ sie haben.


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

Wir haben das Routing in den Container verschoben, damit wir damit arbeiten können, ohne das Objekt \Slim\App zu initialisieren.


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; 

Behälter / Konsole
 #!/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 wird empfohlen, dieser Datei die Berechtigung zur Ausführung zu erteilen


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

Überprüfung
Starten Sie die Konsolenanwendung:


 ./bin/console 

Als Antwort sollte das Begrüßungsfenster für die symfony/console mit zwei verfügbaren Befehlen symfony/console werden - help und 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 

Starten Sie nun den Webserver.


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

Und öffnen Sie eine beliebige URL auf localhost: 8080.
Alle Anfragen sollten eine Antwort mit dem Code 404 und einem leeren Text zurückgeben.
Dies geschieht, weil wir keine Routen aufgelistet haben.


Es bleibt uns überlassen, das Rendern zu verbinden, Vorlagen, Controller zu schreiben und Routen festzulegen.


Schritt 2. Rendern


Fügen Sie die template/layout.twig . Dies ist die Grundvorlage für alle Seiten.


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> 

Fügen Sie eine template/hello.twig eine Begrüßungsseitenvorlage 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 %} 

Und die Fehlerseitenvorlage 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 %} 

Fügen Sie die Rendering-Provider- 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; }); } } 

Schalten Sie den Provider im Bootstrap ein


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

Fügen Sie dem 404-Fehlerhandler ein Rendering hinzu


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/de452834/


All Articles