====== Slim4 コンセプト ====== Version 4.5.0 --- //[[http://www.y2sunlight.com|y2sunlight]] 2020-09-23// [[slim:top|Slim に戻る]] 関連記事 * [[slim:4:install|Slim4 インストール]] * Slim4 コンセプト * [[slim:4:request|Slim4 リクエスト]] * [[slim:4:response|Slim4 レスポンス]] * [[slim:4:routing|Slim4 ルーティング]] * [[slim:4:middleware|Slim4 ミドルウェア]] * [[slim:4:cookbook|Slim4 クックブック]] 本章は以下のサイトの **Concepts** のセクションを翻訳し若干の補足を加えたのもです。 * https://www.slimframework.com/docs/v4/ ----- ===== アプリケーションのライフサイクル ===== ==== 1.インスタンス化 ==== まず、''Slim\App'' クラスをインスタンス化します。これはSlimアプリケーションオブジェクトです。インスタンス化の中で、Slimはアプリケーションの依存関係ごとにデフォルトのサービスを登録します。 \\ ==== 2.ルート定義 ==== 次に、アプリケーションインスタンスの ''get()''、''post()''、''put()''、''delete()''、''patch()''、''head()''、および ''options()'' ルーティングメソッドを使用してルートを定義します。これらのインスタンスメソッドは、アプリケーションのルーターオブジェクトにルートを登録します。各ルーティングメソッドは Route インスタンスを返すため、Route インスタンスのメソッドをすぐに呼び出して、ミドルウェアを追加したり、名前を割り当てたりできます。 \\ ==== 3.アプリケーション ランナー==== 3番目に、アプリケーションインスタンスの ''run()'' メソッドを呼び出します。このメソッドは、次のプロセスを開始します。 === A. ミドルウェア・スタックに入る === ''run()'' メソッドは、アプリケーションのミドルウェア・スタックを内側に横断し始めます。これは、ミドルウェア層の[[https://www.slimframework.com/docs/v4/images/middleware.png|同心円構造]]で、Slimアプリケーションの実行前(および実行後)に Environment、Request、および Responseオブジェクトを受信(またはオプションで操作)します。Slimアプリケーションは、同心円状のミドルウェア構造の最内層にあります。各ミドルウェア層は、最外層から内側に向かって呼び出されていきます。 === B. アプリケーションを起動する === the Not Found or Not Allowed handler is invoked. ''run()'' メソッドが最も内側のミドルウェア層に到達すると、アプリケーションのインスタンスを呼び出し、現在のHTTPリクエストを適切なアプリケーションのルート(route)オブジェクトにディスパッチします。ルート(route)がHTTPメソッドとURIに一致する場合、ルート(route)のミドルウェアと呼び出し可能(callable)オブジェクトが呼び出されます。一致するルートが見つからない場合は、NotFound または NotAllowed ハンドラーが呼び出されます。 === C. ミドルウェア・スタックを出る === アプリケーションのディスパッチプロセスが完了すると、各ミドルウェアレイヤーは、最も内側のレイヤーから始めて、外側に向かって制御を取り戻します。 === D. HTTPレスポンスを送信する === 最も外側のミドルウェア層が制御を引き渡した後、アプリケーションのインスタンスはHTTPレスポンスを準備し、シリアル化し、そして返します。HTTPレスポンスヘッダーはPHPのネイティブな ''header()'' メソッドで設定され、HTTPレスポンスボディーは現在の出力バッファーに出力されます。 \\ ===== PSR-7と値オブジェクト ===== Slimは、Request オブジェクトと Response オブジェクトの [[https://github.com/php-fig/http-message|PSR-7]] インターフェイスをサポートしています。これにより、任意のPSR-7実装を使用できるようになるので、Slimはフレキシブルなものになります。例えば、''GuzzleHttp\Psr7\CachingStream'' のインスタンス、または ''GuzzleHttp\Psr7\stream_for()'' 関数によって返される任意のインスタンスを返すことができます。 Slimは、箱から出してすぐに機能するように、独自のPSR-7実装を提供します。但し、SlimのデフォルトのPSR-7オブジェクトをサードパーティの実装に自由に置き換えることができます。アプリケーションコンテナのrequest と response サービスをオーバーライドするだけで、それぞれ ''Psr\Http\Message\ServerRequestInterface'' と ''Psr\Http\Message\ResponseInterface'' のインスタンスが返されます。 > [[https://www.php-fig.org/psr/psr-7/|PSR-7]]の邦訳は[[psr:psr7|こちら]]にあります。 \\ ===== ミドルウェア ===== Slimアプリケーションの前後にコードを実行して、必要に応じてRequestオブジェクトとResponseオブジェクトを操作できます。これはミドルウェアと呼ばれます。なぜこれが望まれるのでしょうか?おそらく、CSRFからアプリを保護したいからでしょうか。また、アプリを実行する前にリクエストを認証したい場合もあります。ミドルウェアはこれらのシナリオに最適です。 \\ ==== ミドルウェアとは何か ? ==== ミドルウェアは、[[https://www.php-fig.org/psr/psr-15/|PSR-15]] ミドルウェアインターフェイスを実装します。 - ''Psr\Http\Message\ServerRequestInterface'' --- PSR-7 リクエストオブジェクト - ''Psr\Http\Server\RequestHandlerInterface'' --- PSR-15 リクエストハンドラオブジェクト これらのオブジェクトを使えば、適切なことは何でもできます。唯一の難しい要件は、ミドルウェアが ''Psr\Http\Message\ResponseInterface'' のインスタンスを返さなければならないことです( ''MUST'' )。 各ミドルウェアは、次のミドルウェアをインボークして、引数としてリクエストオブジェクトとレスポンスオブジェクトを渡す必要があります( ''SHOULD'' )。 > [[https://www.php-fig.org/psr/psr-15/|PSR-15]]の邦訳は[[psr:psr15|ここ]]にあります。 \\ ==== ミドルウェアはどのように機能するか ? ==== フレームワークが異なれば、ミドルウェアの使用方法も異なります。Slimは、コアアプリケーションを囲む同心円状のレイヤーとしてミドルウェアを追加します。新しいミドルウェア層はそれぞれ、既存のミドルウェア層を囲みます。同心円構造は、ミドルウェア層が追加されると外側に拡張します。 追加された最後のミドルウェア層が最初に実行されます。 Slimアプリケーションを実行すると、Requestオブジェクトはミドルウェア構造を外側から内側に横断します。最初に最も外側のミドルウェアに入り、そして次に外側のミドルウェアに入り(というように)、最終的にSlimアプリケーション自体に到達します。Slimアプリケーションが適切なルート(route)をディスパッチした後、結果のResponseオブジェクトがSlimアプリケーションを終了し、ミドルウェア構造を内側から外に横断していきます。最後に、最終的なResponseオブジェクトは、最も外側のミドルウェアを出て、生のHTTPレスポンスにシリアル化され、HTTPクライアントに返されます。ミドルウェアのプロセスフローを示す図は次のとおりです。 {{https://www.slimframework.com/docs/v4/images/middleware.png?400x400}} \\ ==== ミドルウェアを作成するにはどうすればよいか? ==== ミドルウェアは、2つの引数を受け入れる呼び出し可能オブジェクト(callable)です。それは ''Request'' オブジェクトと ''RequestHandler'' オブジェクトです。各ミドルウェアは、''Psr\Http\Message\ResponseInterface'' のインスタンスを返さなければなりません(MUST)。 === クロージャーのミドルウェアの例 === このサンプルのミドルウェアはクロージャです。 handle($request); $existingContent = (string) $response->getBody(); $response = new Response(); $response->getBody()->write('BEFORE' . $existingContent); return $response; }; $afterMiddleware = function ($request, $handler) { $response = $handler->handle($request); $response->getBody()->write('AFTER'); return $response; }; $app->add($beforeMiddleware); $app->add($afterMiddleware); // ... $app->run(); === 呼び出し可能なクラスのミドルウェアの例 === このサンプルミドルウェアは、__invoke() マジックメソッドを実装する呼び出し可能な(invokable)クラスです。 handle($request); $existingContent = (string) $response->getBody(); $response = new Response(); $response->getBody()->write('BEFORE' . $existingContent); return $response; } } handle($request); $response->getBody()->write('AFTER'); return $response; } } これらのクラスをミドルウェアとして使用するには、 ''$app'' のルートマッピングメソッド get()、post()、put()、patch()、delete()、options()、any()、または group()の後に 関数チェーンとして ''add(new ExampleMiddleware());'' を使用でき、以下のコードでは、これらの内の一つを含んでいます。 add(new ExampleMiddleware()); // Add Middleware On Route $app->get('/', function () { ... })->add(new ExampleMiddleware()); // Add Middleware On Group $app->group('/', function () { ... })->add(new ExampleMiddleware()); // ... $app->run(); \\ ==== ミドルウェアを追加するにはどうすればよい? ==== ミドルウェアは、全てのSlimアプリケーション、個別のSlimアプリケーションルート、またはルートグループに追加できます。全てのシナリオは同じミドルウェアを受け入れ、同じミドルウェアインターフェースを実装します。 === アプリケーションミドルウェア=== アプリケーションミドルウェアは、全ての着信HTTPリクエストに対してインボークされます。Slimアプリケーションインスタンスの ''add()'' メソッドを使用してアプリケーションミドルウェアを追加します。この例では、上記のクロージャミドルウェアの例を追加します。 add(function (Request $request, RequestHandler $handler) { $response = $handler->handle($request); $existingContent = (string) $response->getBody(); $response = new Response(); $response->getBody()->write('BEFORE ' . $existingContent); return $response; }); $app->add(function (Request $request, RequestHandler $handler) { $response = $handler->handle($request); $response->getBody()->write(' AFTER'); return $response; }); $app->get('/', function (Request $request, Response $response, $args) { $response->getBody()->write('Hello World'); return $response; }); $app->run(); これにより、次のHTTP応答本文が出力されます: BEFORE Hello World AFTER === ルートミドルウェア === ルートミドルウェアは、そのルートが現在のHTTPリクエストメソッドとURIに一致する場合にのみインボークされます。ルートミドルウェアは、Slimアプリケーションのルーティングメソッド(''get()'' や ''post()'' など)を呼び出した直後に指定されます。各ルーティングメソッドは ''\Slim\Route'' のインスタンスを返し、このクラスはSlimアプリケーションインスタンスと同じミドルウェアインターフェイスを提供します。''Route'' インスタンスの ''add()'' メソッドを使用して、ミドルウェアを ''Route'' に追加します。この例では、上記のクロージャミドルウェアの例を追加します。 handle($request); $response->getBody()->write('World'); return $response; }; $app->get('/', function (Request $request, Response $response, $args) { $response->getBody()->write('Hello '); return $response; })->add($mw); $app->run(); これにより、次のHTTPレスポンスボディーが出力されます: Hello World === グループミドルウェア === アプリケーション全体、及びミドルウェアを受け入れることができる標準的なルート(route)に加えて、''group()'' マルチルート定義機能も、内部的に個別のルート(route)に許可されています。ルートグループミドルウェアは、そのルートがグループから定義されたHTTPリクエストメソッド及びURIの1つと一致する場合にのみ呼び出されます。コールバック内でミドルウェアを追加するには、''group()'' メソッドの後に ''add()'' をチェーンすることによってグループ全体のミドルウェアを設定します。 以下は、URLハンドラーのグループでコールバックミドルウェアを利用するサンプルアプリケーションです。 get('/', function (Request $request, Response $response) { $response->getBody()->write('Hello World'); return $response; }); $app->group('/utils', function (RouteCollectorProxy $group) { $group->get('/date', function (Request $request, Response $response) { $response->getBody()->write(date('Y-m-d H:i:s')); return $response; }); $group->get('/time', function (Request $request, Response $response) { $response->getBody()->write((string)time()); return $response; }); })->add(function (Request $request, RequestHandler $handler) use ($app) { $response = $handler->handle($request); $dateOrTime = (string) $response->getBody(); $response = $app->getResponseFactory()->createResponse(); $response->getBody()->write('It is now ' . $dateOrTime . '. Enjoy!'); return $response; }); $app->run(); ''/utils/date'' メソッドを呼び出すと、次のような文字列が出力されます。 It is now 2015-07-06 03:11:01. Enjoy! ''/utils/time'' にアクセスすると、次のような文字列が出力されます。 It is now 1436148762. Enjoy! ただし、''/''(domain-root)にアクセスすると、ミドルウェアが割り当てられていないため、次の出力が生成されると思います。 Hello World === ミドルウェアから変数を渡す === ミドルウェアから変数を渡す最も簡単な方法は、リクエストの属性を使用することです。 ミドルウェアでの変数の設定: $request = $request->withAttribute('foo', 'bar'); その変数をルートコールバックで取得する: $foo = $request->getAttribute('foo'); \\ ==== 利用可能なミドルウェアを見つける ==== 要求を満たすPSR-15ミドルウェアクラスがすでに作成されている場合があります。検索する非公式のリストをいくつか示します。 * [[https://github.com/slimphp/Slim/wiki/Middleware-for-Slim-Framework-v4.x|Middleware for Slim Framework v4.x wiki]] * [[https://github.com/middlewares/awesome-psr15-middlewares|middlewares/awesome-psr15-middlewares]] \\ ===== 依存性コンテナ― ===== Slimは、オプションの依存性コンテナーを使用して、アプリケーションの依存関係を準備、管理、および注入します。Slimは、[[https://php-di.org/doc/frameworks/slim.html|PHP-DI]] のような [[https://www.php-fig.org/psr/psr-11/|PSR-11]] を実装するコンテナーをサポートします。 > PSR-11の邦訳は[[psr:psr11|こちら]]にあります。 \\ ==== PHP-DIでの使用例 ==== 依存関係コンテナを必ず提供する必要があるとは限りません。但し、そうする場合は、''App'' を作成する前に、コンテナのインスタンスを ''AppFactory'' に与える必要があります。 そして、コンテナにサービスを追加します: $container->set('myService', function () { $settings = [...]; return new MyService($settings); }); 次のように、明示的にコンテナからからだけでなく、Slimアプリケーションルートの内からサービスをフェッチできます: /** * Example GET route * * @param ServerRequestInterface $request PSR-7 request * @param ResponseInterface $response PSR-7 response * @param array $args Route parameters * * @return ResponseInterface */ $app->get('/foo', function (Request $request, Response $response, $args) { $myService = $this->get('myService'); // ...do something with $myService... return $response; }); コンテナを使用する前にサービスがコンテナに存在するかどうかをテストするには、次のように ''has()'' メソッドを使用します: /** * Example GET route * * @param ServerRequestInterface $request PSR-7 request * @param ResponseInterface $response PSR-7 response * @param array $args Route parameters * * @return ResponseInterface */ $app->get('/foo', function (Request $request, Response $response, $args) { if ($this->has('myService')) { $myService = $this->get('myService'); } return $response; }); \\ ==== コンテナを介したアプリケーションの構成 ==== コンテナに依存性が既に定義されている ''App'' を作成する場合は、''AppFactory::createFromContainer()'' メソッドが使用できます。 **例題** set(ResponseFactoryInterface::class, function (ContainerInterface $container) { return new MyResponseFactory(...); }); // Configure the application via container $app = AppFactory::createFromContainer($container); // ... $app->run(); サポートされているアプリの依存関係は次のとおりです: * Psr\Http\Message\ResponseFactoryInterface * Slim\Interfaces\CallableResolverInterface * Slim\Interfaces\RouteCollectorInterface * Slim\Interfaces\RouteResolverInterface * Slim\Interfaces\MiddlewareDispatcherInterface \\