Upgrade to Pro — share decks privately, control downloads, hide ads and more …

いろいろなフレームワークの仕組みを index.php から読み解こう / index.php...

いろいろなフレームワークの仕組みを index.php から読み解こう / index.php of each framework

2023/03/23, 24, 25 開催「PHPerKaigi 2023」(https://phpcon.php.gr.jp/2021/)の発表資料です

Shohei Okada

March 25, 2023
Tweet

More Decks by Shohei Okada

Other Decks in Programming

Transcript

  1. 1. はじめに 01:00 〜 04:00 2. FW を使った/使ってないコードの比較 04:00 〜

    10:00 3. CakePHP の index.php 10:00 〜 15:00 4. Laravel の index.php 15:00 〜 20:00 5. Slim の index.php 20:00 〜 25:00 6. Symfony の index.php 25:00 〜 30:00 7. 各 FW の共通点 〜問いへの答え〜 30:00 〜 34:00 8. 自己紹介 34:00 〜 35:00 目次(※時間は目安です)
  2. 1. はじめに 01:00 〜 04:00 2. FW を使った/使ってないコードの比較 04:00 〜

    10:00 3. CakePHP の index.php 10:00 〜 15:00 4. Laravel の index.php 15:00 〜 20:00 5. Slim の index.php 20:00 〜 25:00 6. Symfony の index.php 25:00 〜 30:00 7. 各 FW の共通点 〜問いへの答え〜 30:00 〜 34:00 8. 自己紹介 34:00 〜 35:00 目次(※時間は目安です)
  3. • 多くの場合、我々は Web アプリケーションフレームワーク(以下、 FW)を利用して開発している • 一方で単一の PHP ファイルでも Web

    ページを表示することはできる • 書き方は全く異なっているのに、どうして同じ PHP で動くのだろう? はじめに
  4. Web アプリケーション(サーバサイド)がやっていること サーバ GET /search-products HTTP/1.1 Accept: text/html Accept-Encoding: gzip,

    deflate Accept-Language: ja,en-US;q=0.9,en;q=0.8 Connection: keep-alive : : クライアント HTTP リクエスト
  5. Web アプリケーション(サーバサイド)がやっていること HTTP/1.1 200 OK Date: Sat, 25 Mar 2023

    02:15:00 GMT Server: Apache/2.4.54 (Debian) X-Powered-By: PHP/8.1.16 : : <!DOCTYPE html> <html> : : クライアント サーバ 処理の結果に基づくレ スポンスを生成、送信 HTTP レスポンス
  6. Web アプリケーション(サーバサイド)がやっていること サーバ GET /search-products HTTP/1.1 Accept: text/html Accept-Encoding: gzip,

    deflate Accept-Language: ja,en-US;q=0.9,en;q=0.8 Connection: keep-alive : : クライアント HTTP リクエスト HTTP/1.1 200 OK Date: Sat, 25 Mar 2023 02:15:00 GMT Server: Apache/2.4.54 (Debian) X-Powered-By: PHP/8.1.16 : : <!DOCTYPE html> <html> : : HTTP レスポンス リクエスト内容に基づ く処理の実行 処理の結果に基づくレ スポンスを生成、送信
  7. 1. はじめに 01:00 〜 04:00 2. FW を使った/使ってないコードの比較 04:00 〜

    10:00 3. CakePHP の index.php 10:00 〜 15:00 4. Laravel の index.php 15:00 〜 20:00 5. Slim の index.php 20:00 〜 25:00 6. Symfony の index.php 25:00 〜 30:00 7. 各 FW の共通点 〜問いへの答え〜 30:00 〜 34:00 8. 自己紹介 34:00 〜 35:00 目次(※時間は目安です)
  8. • 例として Slim を取り上げます • 他の FW でも大まかな流れは同じようなものだと思います • 説明の目的上、ORM

    は使っていません • 使っていようがいまいが説明したいことには関係ありません • 各種設定(依存関係の注入含む)は適切になされているものとします FW を使って実装する場合 - 前提条件
  9. FW を使って実装する場合(Slim の例) <?php namespace App\Application\Actions; use PDO; use Psr\Http\Message\ResponseInterface

    as Response; use Psr\Http\Message\ServerRequestInterface as Request; use Slim\Views\Twig; class SearchProducts { // PDO のインスタンスはコンストラクタに渡される public function __construct(private readonly PDO $pdo) { } public function __invoke(Request $request, Response $response): Response { // ServerRequestInterface を介して入力を受け取る $q = $request->getQueryParams()['q'] ?? ''; $stmt = $this->pdo->prepare('SELECT name, price FROM products WHERE name LIKE ?'); $stmt->execute(['%' . $q . '%']); $products = $stmt->fetchAll(); // echo などは使わず、 ResponseInterface のインスタンスを返す return Twig::fromRequest($request) ->render($response, 'search-products.html', compact('q', 'products')); } }
  10. <?php namespace App\Application\Actions; use PDO; use Psr\Http\Message\ResponseInterface as Response; use

    Psr\Http\Message\ServerRequestInterface as Request; use Slim\Views\Twig; class SearchProducts { // PDO のインスタンスはコンストラクタに渡される public function __construct(private readonly PDO $pdo) { } public function __invoke(Request $request, Response $response): Response { // ServerRequestInterface を介して入力を受け取る $q = $request->getQueryParams()['q'] ?? ''; $stmt = $this->pdo->prepare('SELECT name, price FROM products WHERE name LIKE ?'); $stmt->execute(['%' . $q . '%']); $products = $stmt->fetchAll(); // echo などは使わず、 ResponseInterface のインスタンスを返す return Twig::fromRequest($request) ->render($response, 'search-products.html', compact('q', 'products')); } } FW を使って実装する場合(Slim の例) // PDO のインスタンスはコンストラクタに渡される public function __construct(private readonly PDO $pdo) { }
  11. <?php namespace App\Application\Actions; use PDO; use Psr\Http\Message\ResponseInterface as Response; use

    Psr\Http\Message\ServerRequestInterface as Request; use Slim\Views\Twig; class SearchProducts { // PDO のインスタンスはコンストラクタに渡される public function __construct(private readonly PDO $pdo) { } public function __invoke(Request $request, Response $response): Response { // ServerRequestInterface を介して入力を受け取る $q = $request->getQueryParams()['q'] ?? ''; $stmt = $this->pdo->prepare('SELECT name, price FROM products WHERE name LIKE ?'); $stmt->execute(['%' . $q . '%']); $products = $stmt->fetchAll(); // echo などは使わず、 ResponseInterface のインスタンスを返す return Twig::fromRequest($request) ->render($response, 'search-products.html', compact('q', 'products')); } } FW を使って実装する場合(Slim の例) // ServerRequestInterface を介して入力を受け取る $q = $request->getQueryParams()['q'] ?? '';
  12. FW を使って実装する場合(Slim の例) <?php namespace App\Application\Actions; use PDO; use Psr\Http\Message\ResponseInterface

    as Response; use Psr\Http\Message\ServerRequestInterface as Request; use Slim\Views\Twig; class SearchProducts { // PDO のインスタンスはコンストラクタに渡される public function __construct(private readonly PDO $pdo) { } public function __invoke(Request $request, Response $response): Response { // ServerRequestInterface を介して入力を受け取る $q = $request->getQueryParams()['q'] ?? ''; $stmt = $this->pdo->prepare('SELECT name, price FROM products WHERE name LIKE ?'); $stmt->execute(['%' . $q . '%']); $products = $stmt->fetchAll(); // echo などは使わず、 ResponseInterface のインスタンスを返す return Twig::fromRequest($request) ->render($response, 'search-products.html', compact('q', 'products')); } } // echo などは使わず、ResponseInterface のインスタンスを返す return Twig::fromRequest($request) ->render($response, 'search-products.html', compact('q', 'products'));
  13. FW を使わずに実装する場合 <?php // include, require 等で他のファイルのコードを読み込む require_once __DIR__ .

    '/../config/database.php'; $pdo = new PDO( sprintf('mysql:host=%s;dbname=%s', MYSQL_HOST, MYSQL_DATABASE), MYSQL_USER, MYSQL_PASSWORD, ); // $_GET, $_POST 等で入力を受け取る $q = $_GET['q'] ?? ''; $stmt = $pdo->prepare('SELECT `name`, `price` FROM `products` WHERE `name` LIKE ?'); $stmt->execute(['%' . $q . '%']); // .php ファイル内に HTML を記述したり、echo を使ったりすることで出力 ?><!DOCTYPE html> <html> <body> <form> <input name="q" value="<?php echo $q ?>"> <input type="submit"> </form> <table> <thead> <tr> <th>商品名</th> <th>価格</th> </tr> </thead> <tbody> <?php while ($row = $stmt->fetch()): ?> <tr> <th><?php echo htmlspecialchars($row['name']); ?></th> <th><?php echo $row['price']; ?></th> </tr> <?php endwhile; ?> </tbody> </table> </body> </html> ドキュメントルート
  14. FW を使わずに実装する場合 <?php // include, require 等で他のファイルのコードを読み込む require_once __DIR__ .

    '/../config/database.php'; $pdo = new PDO( sprintf('mysql:host=%s;dbname=%s', MYSQL_HOST, MYSQL_DATABASE), MYSQL_USER, MYSQL_PASSWORD, ); // $_GET, $_POST 等で入力を受け取る $q = $_GET['q'] ?? ''; $stmt = $pdo->prepare('SELECT `name`, `price` FROM `products` WHERE `name` LIKE ?'); $stmt->execute(['%' . $q . '%']); // .php ファイル内に HTML を記述したり、echo を使ったりすることで出力 ?><!DOCTYPE html> <html> <body> <form> <input name="q" value="<?php echo $q ?>"> <input type="submit"> </form> <table> <thead> <tr> <th>商品名</th> <th>価格</th> </tr> </thead> <tbody> <?php while ($row = $stmt->fetch()): ?> <tr> <th><?php echo htmlspecialchars($row['name']); ?></th> <th><?php echo $row['price']; ?></th> </tr> <?php endwhile; ?> </tbody> </table> </body> </html> // include, require 等で他のファイルのコードを読み込む require_once __DIR__ . '/../config/database.php'; $pdo = new PDO( sprintf('mysql:host=%s;dbname=%s', MYSQL_HOST, MYSQL_DATABASE), MYSQL_USER, MYSQL_PASSWORD, );
  15. FW を使わずに実装する場合 <?php // include, require 等で他のファイルのコードを読み込む require_once __DIR__ .

    '/../config/database.php'; $pdo = new PDO( sprintf('mysql:host=%s;dbname=%s', MYSQL_HOST, MYSQL_DATABASE), MYSQL_USER, MYSQL_PASSWORD, ); // $_GET, $_POST 等で入力を受け取る $q = $_GET['q'] ?? ''; $stmt = $pdo->prepare('SELECT `name`, `price` FROM `products` WHERE `name` LIKE ?'); $stmt->execute(['%' . $q . '%']); // .php ファイル内に HTML を記述したり、echo を使ったりすることで出力 ?><!DOCTYPE html> <html> <body> <form> <input name="q" value="<?php echo $q ?>"> <input type="submit"> </form> <table> <thead> <tr> <th>商品名</th> <th>価格</th> </tr> </thead> <tbody> <?php while ($row = $stmt->fetch()): ?> <tr> <th><?php echo htmlspecialchars($row['name']); ?></th> <th><?php echo $row['price']; ?></th> </tr> <?php endwhile; ?> </tbody> </table> </body> </html> // $_GET, $_POST 等で入力を受け取る $q = $_GET['q'] ?? '';
  16. FW を使わずに実装する場合 <?php // include, require 等で他のファイルのコードを読み込む require_once __DIR__ .

    '/../config/database.php'; $pdo = new PDO( sprintf('mysql:host=%s;dbname=%s', MYSQL_HOST, MYSQL_DATABASE), MYSQL_USER, MYSQL_PASSWORD, ); // $_GET, $_POST 等で入力を受け取る $q = $_GET['q'] ?? ''; $stmt = $pdo->prepare('SELECT `name`, `price` FROM `products` WHERE `name` LIKE ?'); $stmt->execute(['%' . $q . '%']); // .php ファイル内に HTML を記述したり、echo を使ったりすることで出力 ?><!DOCTYPE html> <html> <body> <form> <input name="q" value="<?php echo $q ?>"> <input type="submit"> </form> <table> <thead> <tr> <th>商品名</th> <th>価格</th> </tr> </thead> <tbody> <?php while ($row = $stmt->fetch()): ?> <tr> <th><?php echo htmlspecialchars($row['name']); ?></th> <th><?php echo $row['price']; ?></th> </tr> <?php endwhile; ?> </tbody> </table> </body> </html>
  17. FW を使わずに実装する場合 <?php // include, require 等で他のファイルのコードを読み込む require_once __DIR__ .

    '/../config/database.php'; $pdo = new PDO( sprintf('mysql:host=%s;dbname=%s', MYSQL_HOST, MYSQL_DATABASE), MYSQL_USER, MYSQL_PASSWORD, ); // $_GET, $_POST 等で入力を受け取る $q = $_GET['q'] ?? ''; $stmt = $pdo->prepare('SELECT `name`, `price` FROM `products` WHERE `name` LIKE ?'); $stmt->execute(['%' . $q . '%']); // .php ファイル内に HTML を記述したり、echo を使ったりすることで出力 ?><!DOCTYPE html> <html> <body> <form> <input name="q" value="<?php echo $q ?>"> <input type="submit"> </form> <table> <thead> <tr> <th>商品名</th> <th>価格</th> </tr> </thead> <tbody> <?php while ($row = $stmt->fetch()): ?> <tr> <th><?php echo htmlspecialchars($row['name']); ?></th> <th><?php echo $row['price']; ?></th> </tr> <?php endwhile; ?> </tbody> </table> </body> </html> // .php ファイル内に HTML を記述したり、echo を使ったりすることで出力 ?><!DOCTYPE html> <html> <body> <form> <input name="q" value="<?php echo $q ?>"> <input type="submit"> </form> : :
  18. 異なる点 FW あり FW なし URL ルーティングを記述 ファイルパスがそのまま 依存関係 インスタンスが渡される

    include, require パラメータの受け取り ServerRequestInterface を介する $_GET, $_POST 出力 ResponseInterface を返す HTML を直接記述, echo 等
  19. 異なる点 - URL FW あり FW なし return function (App

    $app) { $app->get('/search-products', SearchProducts::class); }; ドキュメントルート routes.php に書いたパス ドキュメントルートからのファイルパス
  20. 異なる点 - 依存関係 // PDO のインスタンスはコンストラクタに渡される public function __construct(private readonly

    PDO $pdo) { } // include, require 等で他のファイルのコードを読み込む require_once __DIR__ . '/../config/database.php'; $pdo = new PDO( sprintf('mysql:host=%s;dbname=%s', MYSQL_HOST, MYSQL_DATABASE), MYSQL_USER, MYSQL_PASSWORD, ); FW あり FW なし
  21. 異なる点 - 出力 // echo などは使わず、ResponseInterface のインスタンスを返す return Twig::fromRequest($request) ->render($response,

    'search-products.html', compact('q', 'products' // .php ファイル内に HTML を記述したり、echo を使ったりすることで出力 ?><!DOCTYPE html> <html> <body> <form> <input name="q" value="<?php echo $q ?>"> <input type="submit"> </form> : : FW あり FW なし
  22. • PHP-FIG(Framework Interop Group) という団体によって PSR(PHP Standard Recommendation)として定められているもの • 仕様と

    interface の定義が提供されている (参考)ServerRequestInterface と ResponseInterface https://www.php-fig.org/ https://github.com/php-fig
  23. • URL と対応する PHP ファイルがなくても 404 エラーにならないのか • HTTP リクエストを受け取ると指定のクラスのメソッドが呼ばれるのか

    • $_GET, $_POST を使わずにパラメータにアクセスできるのか • echo 等をしていないのに HTTP レスポンスを返せるのか 問い:FW を使って実装すると、どうして
  24. • URL と対応する PHP ファイルがなくても 404 エラーにならないのか • HTTP リクエストを受け取ると指定のクラスのメソッドが呼ばれるのか

    • $_GET, $_POST を使わずにパラメータにアクセスできるのか • echo 等をしていないのに HTTP レスポンスを返せるのか → 各 FW の index.php を読み解くことで答えが見えてくる! 問い:FW を使って実装すると、どうして
  25. すべてのリクエストを index.php でハンドリングしている Apache(.htacess) RewriteEngine On RewriteCond %{REQUEST_FILENAME} !-f RewriteRule

    ^ index.php [L] Nginx index index.php; location / try_files $uri /index.php$is_args$args; } location ~ \.php$ { include fastcgi_params; fastcgi_pass unix:/var/run/php/php-fpm.sock; fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; } どの URL へのリクエストでも index.php が実行される → index.php を読み解くと  FW の仕組みがわかる!
  26. 1. はじめに 01:00 〜 04:00 2. FW を使った/使ってないコードの比較 04:00 〜

    10:00 3. CakePHP の index.php 10:00 〜 15:00 4. Laravel の index.php 15:00 〜 20:00 5. Slim の index.php 20:00 〜 25:00 6. Symfony の index.php 25:00 〜 30:00 7. 各 FW の共通点 〜問いへの答え〜 30:00 〜 34:00 8. 自己紹介 34:00 〜 35:00 目次(※時間は目安です)
  27. CakePHP の index.php https://github.com/cakephp/app/blob/4.4.2/webroot/index.php <?php // Check platform requirements require

    dirname(__DIR__) . '/config/requirements.php'; // For built-in server if (PHP_SAPI === 'cli-server') { $_SERVER['PHP_SELF'] = '/' . basename(__FILE__); $url = parse_url(urldecode($_SERVER['REQUEST_URI'])); $file = __DIR__ . $url['path']; if (strpos($url['path'], '..') === false && strpos($url['path'], '.') !== false && is_file($file)) { return false; } } require dirname(__DIR__) . '/vendor/autoload.php'; use App\Application; use Cake\Http\Server; // Bind your application to the server. $server = new Server(new Application(dirname(__DIR__) . '/config')); // Run the request/response through the application and emit the response. $server->emit($server->run()); ※スペースの都合でライセンス表記等を省略しています
  28. CakePHP の index.php <?php // Check platform requirements require dirname(__DIR__)

    . '/config/requirements.php'; // For built-in server if (PHP_SAPI === 'cli-server') { $_SERVER['PHP_SELF'] = '/' . basename(__FILE__); $url = parse_url(urldecode($_SERVER['REQUEST_URI'])); $file = __DIR__ . $url['path']; if (strpos($url['path'], '..') === false && strpos($url['pa return false; } } require dirname(__DIR__) . '/vendor/autoload.php'; use App\Application; use Cake\Http\Server; // Bind your application to the server. $server = new Server(new Application(dirname(__DIR__) . '/conf // Run the request/response through the application and emit t $server->emit($server->run()); // Check platform requirements require dirname(__DIR__) . '/config/requirements.php';
  29. CakePHP の index.php <?php // Check platform requirements require dirname(__DIR__)

    . '/config/requirements.php'; // For built-in server if (PHP_SAPI === 'cli-server') { $_SERVER['PHP_SELF'] = '/' . basename(__FILE__); $url = parse_url(urldecode($_SERVER['REQUEST_URI'])); $file = __DIR__ . $url['path']; if (strpos($url['path'], '..') === false && strpos($url['pa return false; } } require dirname(__DIR__) . '/vendor/autoload.php'; use App\Application; use Cake\Http\Server; // Bind your application to the server. $server = new Server(new Application(dirname(__DIR__) . '/conf // Run the request/response through the application and emit t $server->emit($server->run()); require dirname(__DIR__) . '/vendor/autoload.php';
  30. CakePHP の index.php <?php // Check platform requirements require dirname(__DIR__)

    . '/config/requirements.php'; // For built-in server if (PHP_SAPI === 'cli-server') { $_SERVER['PHP_SELF'] = '/' . basename(__FILE__); $url = parse_url(urldecode($_SERVER['REQUEST_URI'])); $file = __DIR__ . $url['path']; if (strpos($url['path'], '..') === false && strpos($url['pa return false; } } require dirname(__DIR__) . '/vendor/autoload.php'; use App\Application; use Cake\Http\Server; // Bind your application to the server. $server = new Server(new Application(dirname(__DIR__) . '/conf // Run the request/response through the application and emit t $server->emit($server->run()); // Bind your application to the server. $server = new Server(new Application(dirname(__DIR__) . '/config')); Server Application 各種設定 DI、ルーティング、 ミドルウェア......
  31. CakePHP の index.php <?php // Check platform requirements require dirname(__DIR__)

    . '/config/requirements.php'; // For built-in server if (PHP_SAPI === 'cli-server') { $_SERVER['PHP_SELF'] = '/' . basename(__FILE__); $url = parse_url(urldecode($_SERVER['REQUEST_URI'])); $file = __DIR__ . $url['path']; if (strpos($url['path'], '..') === false && strpos($url['pa return false; } } require dirname(__DIR__) . '/vendor/autoload.php'; use App\Application; use Cake\Http\Server; // Bind your application to the server. $server = new Server(new Application(dirname(__DIR__) . '/conf // Run the request/response through the application and emit t $server->emit($server->run()); // Run the request/response through the application and emit the response. $server->emit($server->run());
  32. public function run( ?ServerRequestInterface $request = null, ?MiddlewareQueue $middlewareQueue =

    null ): ResponseInterface { $this->bootstrap(); $request = $request ?: ServerRequestFactory::fromGlobals(); $middleware = $this->app->middleware($middlewareQueue ?? new MiddlewareQueue()); if ($this->app instanceof PluginApplicationInterface) { $middleware = $this->app->pluginMiddleware($middleware); } $this->dispatchEvent('Server.buildMiddleware', ['middleware' => $middleware]); $response = $this->runner->run($middleware, $request, $this->app); if ($request instanceof ServerRequest) { $request->getSession()->close(); } return $response; } \Cake\Http\Server::run() https://github.com/cakephp/cakephp/blob/4.4.11/src/Http/Server.php Request のインスタンスを生成
  33. public function run( ?ServerRequestInterface $request = null, ?MiddlewareQueue $middlewareQueue =

    null ): ResponseInterface { $this->bootstrap(); $request = $request ?: ServerRequestFactory::fromGlobals(); $middleware = $this->app->middleware($middlewareQueue ?? new MiddlewareQueue()); if ($this->app instanceof PluginApplicationInterface) { $middleware = $this->app->pluginMiddleware($middleware); } $this->dispatchEvent('Server.buildMiddleware', ['middleware' => $middleware]); $response = $this->runner->run($middleware, $request, $this->app); if ($request instanceof ServerRequest) { $request->getSession()->close(); } return $response; } \Cake\Http\Server::run() https://github.com/cakephp/cakephp/blob/4.4.11/src/Http/Server.php Request を受け取って Response を返す
  34. CakePHP の index.php <?php // Check platform requirements require dirname(__DIR__)

    . '/config/requirements.php'; // For built-in server if (PHP_SAPI === 'cli-server') { $_SERVER['PHP_SELF'] = '/' . basename(__FILE__); $url = parse_url(urldecode($_SERVER['REQUEST_URI'])); $file = __DIR__ . $url['path']; if (strpos($url['path'], '..') === false && strpos($url['pa return false; } } require dirname(__DIR__) . '/vendor/autoload.php'; use App\Application; use Cake\Http\Server; // Bind your application to the server. $server = new Server(new Application(dirname(__DIR__) . '/conf // Run the request/response through the application and emit t $server->emit($server->run()); // Run the request/response through the application and emit the response. $server->emit($server->run());
  35. \Cake\Http\ResponseEmitter::emit() https://github.com/cakephp/cakephp/blob/4.4.11/src/Http/Respons eEmitter.php public function emit(ResponseInterface $response): bool { $file

    = ''; $line = 0; if (headers_sent($file, $line)) { $message = "Unable to emit headers. Headers sent in file=$file line=$line"; trigger_error($message, E_USER_WARNING); } $this->emitStatusLine($response); $this->emitHeaders($response); $this->flush(); $range = $this->parseContentRange($response->getHeaderLine('Content-Range')); if (is_array($range)) { $this->emitBodyRange($range, $response); } else { $this->emitBody($response); } if (function_exists('fastcgi_finish_request')) { fastcgi_finish_request(); } return true; }
  36. \Cake\Http\ResponseEmitter::emitBody() https://github.com/cakephp/cakephp/blob/4.4.11/src/Http/Respons eEmitter.php protected function emitBody(ResponseInterface $response): void { if

    (in_array($response->getStatusCode(), [204, 304], true)) { return; } $body = $response->getBody(); if (!$body->isSeekable()) { echo $body; return; } $body->rewind(); while (!$body->eof()) { echo $body->read($this->maxBufferLength); } } 奥底まで辿ると echo している!
  37. • サーバにインストールされている依存関係のチェック • Composer の autoload.php の読み込み • Application インスタンスの生成

    • 依存関係(DI コンテナ)、ルーティング、ミドルウェア等の反映 • Server インスタンスの生成 • 上記 Application を受け取る • 処理を実行する(run) • 生成された Request を受け取り Response を返す • レスポンスを送信する(emit) • Response のインスタンスをもとに 実際に HTTP ヘッダ、ボディ(HTML, JSON 等)を出力する CakePHP の index.php の処理の流れまとめ
  38. 1. はじめに 01:00 〜 04:00 2. FW を使った/使ってないコードの比較 04:00 〜

    10:00 3. CakePHP の index.php 10:00 〜 15:00 4. Laravel の index.php 15:00 〜 20:00 5. Slim の index.php 20:00 〜 25:00 6. Symfony の index.php 25:00 〜 30:00 7. 各 FW の共通点 〜問いへの答え〜 30:00 〜 34:00 8. 自己紹介 34:00 〜 35:00 目次(※時間は目安です)
  39. Laravel の index.php <?php use Illuminate\Contracts\Http\Kernel; use Illuminate\Http\Request; define('LARAVEL_START', microtime(true));

    if (file_exists($maintenance = __DIR__.'/../storage/framework/maintenance.php')) { require $maintenance; } require __DIR__.'/../vendor/autoload.php'; $app = require_once __DIR__.'/../bootstrap/app.php'; $kernel = $app->make(Kernel::class); $response = $kernel->handle( $request = Request::capture() )->send(); $kernel->terminate($request, $response); ※スペースの都合でコメントを省略しています https://github.com/laravel/laravel/blob/v10.0.2/public/index.php
  40. <?php use Illuminate\Contracts\Http\Kernel; use Illuminate\Http\Request; define('LARAVEL_START', microtime(true)); if (file_exists($maintenance =

    __DIR__.'/../storage/framework/mainten require $maintenance; } require __DIR__.'/../vendor/autoload.php'; $app = require_once __DIR__.'/../bootstrap/app.php'; $kernel = $app->make(Kernel::class); $response = $kernel->handle( $request = Request::capture() )->send(); $kernel->terminate($request, $response); Laravel の index.php if (file_exists($maintenance = __DIR__.'/../storage/framework/maintenance.php')) { require $maintenance; }
  41. <?php use Illuminate\Contracts\Http\Kernel; use Illuminate\Http\Request; define('LARAVEL_START', microtime(true)); if (file_exists($maintenance =

    __DIR__.'/../storage/framework/mainten require $maintenance; } require __DIR__.'/../vendor/autoload.php'; $app = require_once __DIR__.'/../bootstrap/app.php'; $kernel = $app->make(Kernel::class); $response = $kernel->handle( $request = Request::capture() )->send(); $kernel->terminate($request, $response); Laravel の index.php require __DIR__.'/../vendor/autoload.php';
  42. <?php use Illuminate\Contracts\Http\Kernel; use Illuminate\Http\Request; define('LARAVEL_START', microtime(true)); if (file_exists($maintenance =

    __DIR__.'/../storage/framework/mainten require $maintenance; } require __DIR__.'/../vendor/autoload.php'; $app = require_once __DIR__.'/../bootstrap/app.php'; $kernel = $app->make(Kernel::class); $response = $kernel->handle( $request = Request::capture() )->send(); $kernel->terminate($request, $response); Laravel の index.php App Kernel 各種設定 $app = require_once __DIR__.'/../bootstrap/app.php'; $kernel = $app->make(Kernel::class); DI、ルーティング、 ミドルウェア......
  43. <?php use Illuminate\Contracts\Http\Kernel; use Illuminate\Http\Request; define('LARAVEL_START', microtime(true)); if (file_exists($maintenance =

    __DIR__.'/../storage/framework/mainten require $maintenance; } require __DIR__.'/../vendor/autoload.php'; $app = require_once __DIR__.'/../bootstrap/app.php'; $kernel = $app->make(Kernel::class); $response = $kernel->handle( $request = Request::capture() )->send(); $kernel->terminate($request, $response); Laravel の index.php $response = $kernel->handle( $request = Request::capture() )->send(); Request のインスタンスを生成
  44. <?php use Illuminate\Contracts\Http\Kernel; use Illuminate\Http\Request; define('LARAVEL_START', microtime(true)); if (file_exists($maintenance =

    __DIR__.'/../storage/framework/mainten require $maintenance; } require __DIR__.'/../vendor/autoload.php'; $app = require_once __DIR__.'/../bootstrap/app.php'; $kernel = $app->make(Kernel::class); $response = $kernel->handle( $request = Request::capture() )->send(); $kernel->terminate($request, $response); Laravel の index.php $response = $kernel->handle( $request = Request::capture() )->send();
  45. \Illuminate\Foundation\Http\Kernel::handle() https://github.com/laravel/framework/blob/v10.0.3/src/Illuminate/F oundation/Http/Kernel.php public function handle($request) { $this->requestStartedAt = Carbon::now();

    try { $request->enableHttpMethodParameterOverride(); $response = $this->sendRequestThroughRouter($request); } catch (Throwable $e) { $this->reportException($e); $response = $this->renderException($request, $e); } $this->app['events']->dispatch( new RequestHandled($request, $response) ); return $response; } Request を受け取って Response を返す \Symfony\Component\HttpFoundation\Response
  46. <?php use Illuminate\Contracts\Http\Kernel; use Illuminate\Http\Request; define('LARAVEL_START', microtime(true)); if (file_exists($maintenance =

    __DIR__.'/../storage/framework/mainten require $maintenance; } require __DIR__.'/../vendor/autoload.php'; $app = require_once __DIR__.'/../bootstrap/app.php'; $kernel = $app->make(Kernel::class); $response = $kernel->handle( $request = Request::capture() )->send(); $kernel->terminate($request, $response); Laravel の index.php $response = $kernel->handle( $request = Request::capture() )->send();
  47. public function send(): static { $this->sendHeaders(); $this->sendContent(); if (\function_exists('fastcgi_finish_request')) {

    fastcgi_finish_request(); } elseif (\function_exists('litespeed_finish_request')) { litespeed_finish_request(); } elseif (!\in_array(\PHP_SAPI, ['cli', 'phpdbg'], true)) { static::closeOutputBuffers(0, true); flush(); } return $this; } \Symfony\Component\HttpFoundation\Response::send() https://github.com/symfony/http-foundation/blob/v6.2.6/Response.php
  48. public function sendContent(): static { echo $this->content; return $this; }

    \Symfony\Component\HttpFoundation\Response::sendContent() https://github.com/symfony/http-foundation/blob/v6.2.6/Response.php 奥底まで辿ると echo している!
  49. • メンテナンスモードのチェック • Composer の autoload.php の読み込み • Kernel インスタンスの生成

    • 依存関係(DI コンテナ)、ルーティング、ミドルウェア等の反映 • 処理を実行する(handle) • 生成された Request を受け取り Response を返す • レスポンスを送る(send) • Response のインスタンスをもとに 実際に HTTP ヘッダ、ボディ(HTML, JSON 等)を出力する Laravel の index.php の処理の流れまとめ
  50. 1. はじめに 01:00 〜 04:00 2. FW を使った/使ってないコードの比較 04:00 〜

    10:00 3. CakePHP の index.php 10:00 〜 15:00 4. Laravel の index.php 15:00 〜 20:00 5. Slim の index.php 20:00 〜 25:00 6. Symfony の index.php 25:00 〜 30:00 7. 各 FW の共通点 〜問いへの答え〜 30:00 〜 34:00 8. 自己紹介 34:00 〜 35:00 目次(※時間は目安です)
  51. Slim の index.php :
 https://github.com/slimphp/Slim-Skeleton/blob/4.5.0/public/index.php <?php declare(strict_types=1); use App\Application\Handlers\HttpErrorHandler; use

    App\Application\Handlers\ShutdownHandler; use App\Application\ResponseEmitter\ResponseEmitter; use App\Application\Settings\SettingsInterface; use DI\ContainerBuilder; use Slim\Factory\AppFactory; use Slim\Factory\ServerRequestCreatorFactory; require __DIR__ . '/../vendor/autoload.php'; // Instantiate PHP-DI ContainerBuilder $containerBuilder = new ContainerBuilder(); if (false) { // Should be set to true in production $containerBuilder->enableCompilation(__DIR__ . '/../var/cache'); } // Set up settings $settings = require __DIR__ . '/../app/settings.php'; $settings($containerBuilder); // Set up dependencies $dependencies = require __DIR__ . '/../app/dependencies.php'; $dependencies($containerBuilder); // Set up repositories $repositories = require __DIR__ . '/../app/repositories.php'; $repositories($containerBuilder); // Build PHP-DI Container instance $container = $containerBuilder->build(); // Instantiate the app AppFactory::setContainer($container); $app = AppFactory::create(); $callableResolver = $app->getCallableResolver(); // Register middleware $middleware = require __DIR__ . '/../app/middleware.php'; $middleware($app); // Register routes $routes = require __DIR__ . '/../app/routes.php'; $routes($app); /** @var SettingsInterface $settings */ $settings = $container->get(SettingsInterface::class); $displayErrorDetails = $settings->get('displayErrorDetails'); $logError = $settings->get('logError'); $logErrorDetails = $settings->get('logErrorDetails'); // Create Request object from globals $serverRequestCreator = ServerRequestCreatorFactory::create(); $request = $serverRequestCreator->createServerRequestFromGlobals(); // Create Error Handler $responseFactory = $app->getResponseFactory(); $errorHandler = new HttpErrorHandler($callableResolver, $responseFactory); // Create Shutdown Handler $shutdownHandler = new ShutdownHandler($request, $errorHandler, $displayErrorDetails); register_shutdown_function($shutdownHandler); // Add Routing Middleware $app->addRoutingMiddleware(); // Add Body Parsing Middleware $app->addBodyParsingMiddleware(); // Add Error Middleware $errorMiddleware = $app->addErrorMiddleware($displayErrorDetails, $logError, $logErrorDetails); $errorMiddleware->setDefaultErrorHandler($errorHandler); // Run App & Emit Response $response = $app->handle($request); $responseEmitter = new ResponseEmitter(); $responseEmitter->emit($response); :

  52. Slim の index.php <?php declare(strict_types=1); use App\Application\Handlers\HttpErrorHandler; use App\Application\Handlers\ShutdownHandler; use

    App\Application\ResponseEmitter\ResponseEmitter; use App\Application\Settings\SettingsInterface; use DI\ContainerBuilder; use Slim\Factory\AppFactory; use Slim\Factory\ServerRequestCreatorFactory; require __DIR__ . '/../vendor/autoload.php'; // Instantiate PHP-DI ContainerBuilder $containerBuilder = new ContainerBuilder(); if (false) { // Should be set to true in production $containerBuilder->enableCompilation(__DIR__ . '/../var/cache'); } // Set up settings $settings = require __DIR__ . '/../app/settings.php'; $settings($containerBuilder); // Set up dependencies $dependencies = require __DIR__ . '/../app/dependencies.php'; $dependencies($containerBuilder); // Set up repositories $repositories = require __DIR__ . '/../app/repositories.php'; $repositories($containerBuilder); // Build PHP-DI Container instance $container = $containerBuilder->build(); // Instantiate the app AppFactory::setContainer($container); $app = AppFactory::create(); $callableResolver = $app->getCallableResolver(); require __DIR__ . '/../vendor/autoload.php';
  53. Slim の index.php <?php declare(strict_types=1); use App\Application\Handlers\HttpErrorHandler; use App\Application\Handlers\ShutdownHandler; use

    App\Application\ResponseEmitter\ResponseEmitter; use App\Application\Settings\SettingsInterface; use DI\ContainerBuilder; use Slim\Factory\AppFactory; use Slim\Factory\ServerRequestCreatorFactory; require __DIR__ . '/../vendor/autoload.php'; // Instantiate PHP-DI ContainerBuilder $containerBuilder = new ContainerBuilder(); if (false) { // Should be set to true in production $containerBuilder->enableCompilation(__DIR__ . '/../var/cache'); } // Set up settings $settings = require __DIR__ . '/../app/settings.php'; $settings($containerBuilder); // Set up dependencies $dependencies = require __DIR__ . '/../app/dependencies.php'; $dependencies($containerBuilder); // Set up repositories $repositories = require __DIR__ . '/../app/repositories.php'; $repositories($containerBuilder); // Build PHP-DI Container instance $container = $containerBuilder->build(); // Instantiate the app AppFactory::setContainer($container); $app = AppFactory::create(); $callableResolver = $app->getCallableResolver(); Container DI コンテナ
  54. // Instantiate PHP-DI ContainerBuilder $containerBuilder = new ContainerBuilder(); Slim の

    index.php <?php declare(strict_types=1); use App\Application\Handlers\HttpErrorHandler; use App\Application\Handlers\ShutdownHandler; use App\Application\ResponseEmitter\ResponseEmitter; use App\Application\Settings\SettingsInterface; use DI\ContainerBuilder; use Slim\Factory\AppFactory; use Slim\Factory\ServerRequestCreatorFactory; require __DIR__ . '/../vendor/autoload.php'; // Instantiate PHP-DI ContainerBuilder $containerBuilder = new ContainerBuilder(); if (false) { // Should be set to true in production $containerBuilder->enableCompilation(__DIR__ . '/../var/cache'); } // Set up settings $settings = require __DIR__ . '/../app/settings.php'; $settings($containerBuilder); // Set up dependencies $dependencies = require __DIR__ . '/../app/dependencies.php'; $dependencies($containerBuilder); // Set up repositories $repositories = require __DIR__ . '/../app/repositories.php'; $repositories($containerBuilder); // Build PHP-DI Container instance $container = $containerBuilder->build(); // Instantiate the app AppFactory::setContainer($container); $app = AppFactory::create(); $callableResolver = $app->getCallableResolver(); Container DI コンテナ
  55. Slim の index.php <?php declare(strict_types=1); use App\Application\Handlers\HttpErrorHandler; use App\Application\Handlers\ShutdownHandler; use

    App\Application\ResponseEmitter\ResponseEmitter; use App\Application\Settings\SettingsInterface; use DI\ContainerBuilder; use Slim\Factory\AppFactory; use Slim\Factory\ServerRequestCreatorFactory; require __DIR__ . '/../vendor/autoload.php'; // Instantiate PHP-DI ContainerBuilder $containerBuilder = new ContainerBuilder(); if (false) { // Should be set to true in production $containerBuilder->enableCompilation(__DIR__ . '/../var/cache'); } // Set up settings $settings = require __DIR__ . '/../app/settings.php'; $settings($containerBuilder); // Set up dependencies $dependencies = require __DIR__ . '/../app/dependencies.php'; $dependencies($containerBuilder); // Set up repositories $repositories = require __DIR__ . '/../app/repositories.php'; $repositories($containerBuilder); // Build PHP-DI Container instance $container = $containerBuilder->build(); // Instantiate the app AppFactory::setContainer($container); $app = AppFactory::create(); $callableResolver = $app->getCallableResolver(); Container DI コンテナ
  56. Slim の index.php <?php declare(strict_types=1); use App\Application\Handlers\HttpErrorHandler; use App\Application\Handlers\ShutdownHandler; use

    App\Application\ResponseEmitter\ResponseEmitter; use App\Application\Settings\SettingsInterface; use DI\ContainerBuilder; use Slim\Factory\AppFactory; use Slim\Factory\ServerRequestCreatorFactory; require __DIR__ . '/../vendor/autoload.php'; // Instantiate PHP-DI ContainerBuilder $containerBuilder = new ContainerBuilder(); if (false) { // Should be set to true in production $containerBuilder->enableCompilation(__DIR__ . '/../var/cache'); } // Set up settings $settings = require __DIR__ . '/../app/settings.php'; $settings($containerBuilder); // Set up dependencies $dependencies = require __DIR__ . '/../app/dependencies.php'; $dependencies($containerBuilder); // Set up repositories $repositories = require __DIR__ . '/../app/repositories.php'; $repositories($containerBuilder); // Build PHP-DI Container instance $container = $containerBuilder->build(); // Instantiate the app AppFactory::setContainer($container); $app = AppFactory::create(); $callableResolver = $app->getCallableResolver(); // Instantiate the app AppFactory::setContainer($container); $app = AppFactory::create(); $callableResolver = $app->getCallableResolver(); App Container DI コンテナ
  57. // Register middleware $middleware = require __DIR__ . '/../app/middleware.php'; $middleware($app);

    // Register routes $routes = require __DIR__ . '/../app/routes.php'; $routes($app); /** @var SettingsInterface $settings */ $settings = $container->get(SettingsInterface::class); $displayErrorDetails = $settings->get('displayErrorDetails'); $logError = $settings->get('logError'); $logErrorDetails = $settings->get('logErrorDetails'); // Create Request object from globals $serverRequestCreator = ServerRequestCreatorFactory::create(); $request = $serverRequestCreator->createServerRequestFromGlobals(); // Create Error Handler $responseFactory = $app->getResponseFactory(); $errorHandler = new HttpErrorHandler($callableResolver, $responseFactory); // Create Shutdown Handler $shutdownHandler = new ShutdownHandler($request, $errorHandler, $displayErr register_shutdown_function($shutdownHandler); // Add Routing Middleware $app->addRoutingMiddleware(); // Add Body Parsing Middleware $app->addBodyParsingMiddleware(); // Add Error Middleware $errorMiddleware = $app->addErrorMiddleware($displayErrorDetails, $logError $errorMiddleware->setDefaultErrorHandler($errorHandler); // Run App & Emit Response $response = $app->handle($request); $responseEmitter = new ResponseEmitter(); $responseEmitter->emit($response); Slim の index.php // Register middleware $middleware = require __DIR__ . '/../app/middleware.php'; $middleware($app); // Register routes $routes = require __DIR__ . '/../app/routes.php'; $routes($app); App Container DI コンテナ ルーティング、 ミドルウェア
  58. // Register middleware $middleware = require __DIR__ . '/../app/middleware.php'; $middleware($app);

    // Register routes $routes = require __DIR__ . '/../app/routes.php'; $routes($app); /** @var SettingsInterface $settings */ $settings = $container->get(SettingsInterface::class); $displayErrorDetails = $settings->get('displayErrorDetails'); $logError = $settings->get('logError'); $logErrorDetails = $settings->get('logErrorDetails'); // Create Request object from globals $serverRequestCreator = ServerRequestCreatorFactory::create(); $request = $serverRequestCreator->createServerRequestFromGlobals(); // Create Error Handler $responseFactory = $app->getResponseFactory(); $errorHandler = new HttpErrorHandler($callableResolver, $responseFactory); // Create Shutdown Handler $shutdownHandler = new ShutdownHandler($request, $errorHandler, $displayErr register_shutdown_function($shutdownHandler); // Add Routing Middleware $app->addRoutingMiddleware(); // Add Body Parsing Middleware $app->addBodyParsingMiddleware(); // Add Error Middleware $errorMiddleware = $app->addErrorMiddleware($displayErrorDetails, $logError $errorMiddleware->setDefaultErrorHandler($errorHandler); // Run App & Emit Response $response = $app->handle($request); $responseEmitter = new ResponseEmitter(); $responseEmitter->emit($response); Slim の index.php ※エラーログや最低限のミドルウェア等の設定
  59. // Register middleware $middleware = require __DIR__ . '/../app/middleware.php'; $middleware($app);

    // Register routes $routes = require __DIR__ . '/../app/routes.php'; $routes($app); /** @var SettingsInterface $settings */ $settings = $container->get(SettingsInterface::class); $displayErrorDetails = $settings->get('displayErrorDetails'); $logError = $settings->get('logError'); $logErrorDetails = $settings->get('logErrorDetails'); // Create Request object from globals $serverRequestCreator = ServerRequestCreatorFactory::create(); $request = $serverRequestCreator->createServerRequestFromGlobals(); // Create Error Handler $responseFactory = $app->getResponseFactory(); $errorHandler = new HttpErrorHandler($callableResolver, $responseFactory); // Create Shutdown Handler $shutdownHandler = new ShutdownHandler($request, $errorHandler, $displayErr register_shutdown_function($shutdownHandler); // Add Routing Middleware $app->addRoutingMiddleware(); // Add Body Parsing Middleware $app->addBodyParsingMiddleware(); // Add Error Middleware $errorMiddleware = $app->addErrorMiddleware($displayErrorDetails, $logError $errorMiddleware->setDefaultErrorHandler($errorHandler); // Run App & Emit Response $response = $app->handle($request); $responseEmitter = new ResponseEmitter(); $responseEmitter->emit($response); Slim の index.php // Create Request object from globals $serverRequestCreator = ServerRequestCreatorFactory::create(); $request = $serverRequestCreator->createServerRequestFromGlobals(); Request のインスタンスを生成
  60. // Register middleware $middleware = require __DIR__ . '/../app/middleware.php'; $middleware($app);

    // Register routes $routes = require __DIR__ . '/../app/routes.php'; $routes($app); /** @var SettingsInterface $settings */ $settings = $container->get(SettingsInterface::class); $displayErrorDetails = $settings->get('displayErrorDetails'); $logError = $settings->get('logError'); $logErrorDetails = $settings->get('logErrorDetails'); // Create Request object from globals $serverRequestCreator = ServerRequestCreatorFactory::create(); $request = $serverRequestCreator->createServerRequestFromGlobals(); // Create Error Handler $responseFactory = $app->getResponseFactory(); $errorHandler = new HttpErrorHandler($callableResolver, $responseFactory); // Create Shutdown Handler $shutdownHandler = new ShutdownHandler($request, $errorHandler, $displayErr register_shutdown_function($shutdownHandler); // Add Routing Middleware $app->addRoutingMiddleware(); // Add Body Parsing Middleware $app->addBodyParsingMiddleware(); // Add Error Middleware $errorMiddleware = $app->addErrorMiddleware($displayErrorDetails, $logError $errorMiddleware->setDefaultErrorHandler($errorHandler); // Run App & Emit Response $response = $app->handle($request); $responseEmitter = new ResponseEmitter(); $responseEmitter->emit($response); Slim の index.php // Run App & Emit Response $response = $app->handle($request); $responseEmitter = new ResponseEmitter(); $responseEmitter->emit($response);
  61. public function handle(ServerRequestInterface $request): ResponseInterface { $response = $this->middlewareDispatcher->handle($request); /**

    * This is to be in compliance with RFC 2616, Section 9. * If the incoming request method is HEAD, we need to ensure that the response body * is empty as the request may fall back on a GET route handler due to FastRoute's * routing logic which could potentially append content to the response body * https://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.4 */ $method = strtoupper($request->getMethod()); if ($method === 'HEAD') { $emptyBody = $this->responseFactory->createResponse()->getBody(); return $response->withBody($emptyBody); } return $response; } \Slim\App::handle() https://github.com/slimphp/Slim/blob/4.11.0/Slim/App.php Request を受け取って Response を返す
  62. // Register middleware $middleware = require __DIR__ . '/../app/middleware.php'; $middleware($app);

    // Register routes $routes = require __DIR__ . '/../app/routes.php'; $routes($app); /** @var SettingsInterface $settings */ $settings = $container->get(SettingsInterface::class); $displayErrorDetails = $settings->get('displayErrorDetails'); $logError = $settings->get('logError'); $logErrorDetails = $settings->get('logErrorDetails'); // Create Request object from globals $serverRequestCreator = ServerRequestCreatorFactory::create(); $request = $serverRequestCreator->createServerRequestFromGlobals(); // Create Error Handler $responseFactory = $app->getResponseFactory(); $errorHandler = new HttpErrorHandler($callableResolver, $responseFactory); // Create Shutdown Handler $shutdownHandler = new ShutdownHandler($request, $errorHandler, $displayErr register_shutdown_function($shutdownHandler); // Add Routing Middleware $app->addRoutingMiddleware(); // Add Body Parsing Middleware $app->addBodyParsingMiddleware(); // Add Error Middleware $errorMiddleware = $app->addErrorMiddleware($displayErrorDetails, $logError $errorMiddleware->setDefaultErrorHandler($errorHandler); // Run App & Emit Response $response = $app->handle($request); $responseEmitter = new ResponseEmitter(); $responseEmitter->emit($response); Slim の index.php // Run App & Emit Response $response = $app->handle($request); $responseEmitter = new ResponseEmitter(); $responseEmitter->emit($response);
  63. public function emit(ResponseInterface $response): void { $isEmpty = $this->isResponseEmpty($response); if

    (headers_sent() === false) { $this->emitHeaders($response); // Set the status _after_ the headers, because of PHP's "helpful" behavior with location headers. // See https://github.com/slimphp/Slim/issues/1730 $this->emitStatusLine($response); } if (!$isEmpty) { $this->emitBody($response); } } \Slim\ResponseEmitter::emit() https://github.com/slimphp/Slim/blob/4.11.0/Slim/ResponseEmitter.php
  64. \Slim\ResponseEmitter::emitBody() https://github.com/slimphp/Slim/blob/4.11.0/Slim/ResponseEmitter.php private function emitBody(ResponseInterface $response): void { $body =

    $response->getBody(); if ($body->isSeekable()) { $body->rewind(); } $amountToRead = (int) $response->getHeaderLine('Content-Length'); if (!$amountToRead) { $amountToRead = $body->getSize(); } if ($amountToRead) { while ($amountToRead > 0 && !$body->eof()) { $length = min($this->responseChunkSize, $amountToRead); $data = $body->read($length); echo $data; $amountToRead -= strlen($data); if (connection_status() !== CONNECTION_NORMAL) { break; } } } else { while (!$body->eof()) { echo $body->read($this->responseChunkSize); if (connection_status() !== CONNECTION_NORMAL) { break; } } } } echo $data; echo $body->read($this->responseChunkSize); 奥底まで辿ると echo している!
  65. • Composer の autoload.php の読み込み • App インスタンスの生成 • DI

    コンテナを構築し、受け取る • ミドルウェアやルーティングの反映 • エラーログや最低限のミドルウェア等の設定 • Request インスタンスの生成 • 処理を実行する(handle) • 生成された Request を受け取り Response を返す • レスポンスを送信する(emit) • Response のインスタンスをもとに 実際に HTTP ヘッダ、ボディ(HTML, JSON 等)を出力する Slim の index.php の処理の流れまとめ
  66. 1. はじめに 01:00 〜 04:00 2. FW を使った/使ってないコードの比較 04:00 〜

    10:00 3. CakePHP の index.php 10:00 〜 15:00 4. Laravel の index.php 15:00 〜 20:00 5. Slim の index.php 20:00 〜 25:00 6. Symfony の index.php 25:00 〜 30:00 7. 各 FW の共通点 〜問いへの答え〜 30:00 〜 34:00 8. 自己紹介 34:00 〜 35:00 目次(※時間は目安です)
  67. autoload_runtime.php https://github.com/symfony/runtime/blob/v6.2.5/Internal/autoload_ runtime.template(←はテンプレート) <?php // autoload_runtime.php @generated by Symfony Runtime

    if (true === (require_once __DIR__.'/autoload.php') || empty($_SERVER['SCRIPT_FILENAME'])) { return; } $app = require $_SERVER['SCRIPT_FILENAME']; if (!is_object($app)) { throw new TypeError(sprintf('Invalid return value: callable object expected, "%s" returned from "%s".', get_debug_type($app), $_SERVER['SCRIPT_FILENAME'])); } $runtime = $_SERVER['APP_RUNTIME'] ?? $_ENV['APP_RUNTIME'] ?? 'Symfony\\Component\\Runtime\\SymfonyRuntime'; $runtime = new $runtime(($_SERVER['APP_RUNTIME_OPTIONS'] ?? $_ENV['APP_RUNTIME_OPTIONS'] ?? []) + [ 'project_dir' => dirname(__DIR__, 1), ]); [$app, $args] = $runtime ->getResolver($app) ->resolve(); $app = $app(...$args); exit( $runtime ->getRunner($app) ->run() );
  68. autoload_runtime.php <?php // autoload_runtime.php @generated by Symfony Runtime if (true

    === (require_once __DIR__.'/autoload.php') || empty($_SERVER['SCRIPT_FILENAME'])) { return; } $app = require $_SERVER['SCRIPT_FILENAME']; if (!is_object($app)) { throw new TypeError(sprintf('Invalid return value: callable object expected, "%s" returned from "%s".', get_d $_SERVER['SCRIPT_FILENAME'])); } $runtime = $_SERVER['APP_RUNTIME'] ?? $_ENV['APP_RUNTIME'] ?? 'Symfony\\Component\\Runtime\\SymfonyRuntime'; $runtime = new $runtime(($_SERVER['APP_RUNTIME_OPTIONS'] ?? $_ENV['APP_RUNTIME_OPTIONS'] ?? []) + [ 'project_dir' => dirname(__DIR__, 1), ]); [$app, $args] = $runtime ->getResolver($app) ->resolve(); $app = $app(...$args); exit( $runtime ->getRunner($app) ->run() ); if (true === (require_once __DIR__.'/autoload.php') || empty($_SERVER['SCRIPT_FILENAME'])) { return; }
  69. autoload_runtime.php <?php // autoload_runtime.php @generated by Symfony Runtime if (true

    === (require_once __DIR__.'/autoload.php') || empty($_SERVER['SCRIPT_FILENAME'])) { return; } $app = require $_SERVER['SCRIPT_FILENAME']; if (!is_object($app)) { throw new TypeError(sprintf('Invalid return value: callable object expected, "%s" returned from "%s".', get_d $_SERVER['SCRIPT_FILENAME'])); } $runtime = $_SERVER['APP_RUNTIME'] ?? $_ENV['APP_RUNTIME'] ?? 'Symfony\\Component\\Runtime\\SymfonyRuntime'; $runtime = new $runtime(($_SERVER['APP_RUNTIME_OPTIONS'] ?? $_ENV['APP_RUNTIME_OPTIONS'] ?? []) + [ 'project_dir' => dirname(__DIR__, 1), ]); [$app, $args] = $runtime ->getResolver($app) ->resolve(); $app = $app(...$args); exit( $runtime ->getRunner($app) ->run() ); $app = require $_SERVER['SCRIPT_FILENAME']; = index.php
  70. Symfony の index.php https://github.com/symfony/recipes/blob/main/symfony/framework- bundle/6.2/public/index.php <?php use App\Kernel; require_once dirname(__DIR__).'/vendor/autoload_runtime.php';

    return function (array $context) { return new Kernel($context['APP_ENV'], (bool) $context['APP_DEBUG']); }; Kernel 各種設定 DI、ルーティング、 ミドルウェア......
  71. autoload_runtime.php <?php // autoload_runtime.php @generated by Symfony Runtime if (true

    === (require_once __DIR__.'/autoload.php') || empty($_SERVER['SCRIPT_FILENAME'])) { return; } $app = require $_SERVER['SCRIPT_FILENAME']; if (!is_object($app)) { throw new TypeError(sprintf('Invalid return value: callable object expected, "%s" returned from "%s".', get_d $_SERVER['SCRIPT_FILENAME'])); } $runtime = $_SERVER['APP_RUNTIME'] ?? $_ENV['APP_RUNTIME'] ?? 'Symfony\\Component\\Runtime\\SymfonyRuntime'; $runtime = new $runtime(($_SERVER['APP_RUNTIME_OPTIONS'] ?? $_ENV['APP_RUNTIME_OPTIONS'] ?? []) + [ 'project_dir' => dirname(__DIR__, 1), ]); [$app, $args] = $runtime ->getResolver($app) ->resolve(); $app = $app(...$args); exit( $runtime ->getRunner($app) ->run() ); $runtime = $_SERVER['APP_RUNTIME'] ?? $_ENV['APP_RUNTIME'] ?? 'Symfony\\Component\\Runtime\\SymfonyRuntime'; $runtime = new $runtime(($_SERVER['APP_RUNTIME_OPTIONS'] ?? $_ENV['APP_RUNTIME_OPTIONS'] ?? []) + [ 'project_dir' => dirname(__DIR__, 1), ]); [$app, $args] = $runtime ->getResolver($app) ->resolve(); $app = $app(...$args); Kernel 各種設定 DI、ルーティング、 ミドルウェア...... Runtime
  72. autoload_runtime.php <?php // autoload_runtime.php @generated by Symfony Runtime if (true

    === (require_once __DIR__.'/autoload.php') || empty($_SERVER['SCRIPT_FILENAME'])) { return; } $app = require $_SERVER['SCRIPT_FILENAME']; if (!is_object($app)) { throw new TypeError(sprintf('Invalid return value: callable object expected, "%s" returned from "%s".', get_d $_SERVER['SCRIPT_FILENAME'])); } $runtime = $_SERVER['APP_RUNTIME'] ?? $_ENV['APP_RUNTIME'] ?? 'Symfony\\Component\\Runtime\\SymfonyRuntime'; $runtime = new $runtime(($_SERVER['APP_RUNTIME_OPTIONS'] ?? $_ENV['APP_RUNTIME_OPTIONS'] ?? []) + [ 'project_dir' => dirname(__DIR__, 1), ]); [$app, $args] = $runtime ->getResolver($app) ->resolve(); $app = $app(...$args); exit( $runtime ->getRunner($app) ->run() ); exit( $runtime ->getRunner($app) ->run() ); Kernel 各種設定 DI、ルーティング、 ミドルウェア...... Runtime Runner
  73. autoload_runtime.php <?php // autoload_runtime.php @generated by Symfony Runtime if (true

    === (require_once __DIR__.'/autoload.php') || empty($_SERVER['SCRIPT_FILENAME'])) { return; } $app = require $_SERVER['SCRIPT_FILENAME']; if (!is_object($app)) { throw new TypeError(sprintf('Invalid return value: callable object expected, "%s" returned from "%s".', get_d $_SERVER['SCRIPT_FILENAME'])); } $runtime = $_SERVER['APP_RUNTIME'] ?? $_ENV['APP_RUNTIME'] ?? 'Symfony\\Component\\Runtime\\SymfonyRuntime'; $runtime = new $runtime(($_SERVER['APP_RUNTIME_OPTIONS'] ?? $_ENV['APP_RUNTIME_OPTIONS'] ?? []) + [ 'project_dir' => dirname(__DIR__, 1), ]); [$app, $args] = $runtime ->getResolver($app) ->resolve(); $app = $app(...$args); exit( $runtime ->getRunner($app) ->run() ); exit( $runtime ->getRunner($app) ->run() );
  74. \Symfony\Component\Runtime\SymfonyRuntime::getRunner() https://github.com/symfony/runtime/blob/v6.2.5/SymfonyRuntime.php public function getRunner(?object $application): RunnerInterface { if ($application

    instanceof HttpKernelInterface) { return new HttpKernelRunner($application, Request::createFromGlobals()); } : Request のインスタンスを生成
  75. autoload_runtime.php <?php // autoload_runtime.php @generated by Symfony Runtime if (true

    === (require_once __DIR__.'/autoload.php') || empty($_SERVER['SCRIPT_FILENAME'])) { return; } $app = require $_SERVER['SCRIPT_FILENAME']; if (!is_object($app)) { throw new TypeError(sprintf('Invalid return value: callable object expected, "%s" returned from "%s".', get_d $_SERVER['SCRIPT_FILENAME'])); } $runtime = $_SERVER['APP_RUNTIME'] ?? $_ENV['APP_RUNTIME'] ?? 'Symfony\\Component\\Runtime\\SymfonyRuntime'; $runtime = new $runtime(($_SERVER['APP_RUNTIME_OPTIONS'] ?? $_ENV['APP_RUNTIME_OPTIONS'] ?? []) + [ 'project_dir' => dirname(__DIR__, 1), ]); [$app, $args] = $runtime ->getResolver($app) ->resolve(); $app = $app(...$args); exit( $runtime ->getRunner($app) ->run() ); exit( $runtime ->getRunner($app) ->run() );
  76. \Symfony\Component\Runtime\Resolver\HttpKernelRunner::run() https://github.com/symfony/runtime/blob/v6.2.5/Runner/Symfony/H ttpKernelRunner.php public function run(): int { $response =

    $this->kernel->handle($this->request); $response->send(); if ($this->kernel instanceof TerminableInterface) { $this->kernel->terminate($this->request, $response); } return 0; } Request を受け取って Response を返す
  77. \Symfony\Component\Runtime\Resolver\HttpKernelRunner::run() https://github.com/symfony/runtime/blob/v6.2.5/Runner/Symfony/H ttpKernelRunner.php public function run(): int { $response =

    $this->kernel->handle($this->request); $response->send(); if ($this->kernel instanceof TerminableInterface) { $this->kernel->terminate($this->request, $response); } return 0; }
  78. public function send(): static { $this->sendHeaders(); $this->sendContent(); if (\function_exists('fastcgi_finish_request')) {

    fastcgi_finish_request(); } elseif (\function_exists('litespeed_finish_request')) { litespeed_finish_request(); } elseif (!\in_array(\PHP_SAPI, ['cli', 'phpdbg'], true)) { static::closeOutputBuffers(0, true); flush(); } return $this; } \Symfony\Component\HttpFoundation\Response::send() https://github.com/symfony/http-foundation/blob/v6.2.6/Response.php
  79. public function sendContent(): static { echo $this->content; return $this; }

    \Symfony\Component\HttpFoundation\Response::sendContent() https://github.com/symfony/http-foundation/blob/v6.2.6/Response.php 奥底まで辿ると echo している!
  80. • Composer の autoload.php の読み込み • Kernel のインスタンス生成 • 依存関係(DI

    コンテナ)、ルーティング、ミドルウェア等の反映 • Runner インスタンスの生成、処理の実行 • 上記 Kernel を受け取る • 同時に Request インスタンスを生成 • 処理を実行する(handle) • 生成された Request を受け取り Response を返す • レスポンスを送信する(send) • Response のインスタンスをもとに 実際に HTTP ヘッダ、ボディ(HTML, JSON 等)を出力する Symfony の index.php の処理の流れまとめ
  81. 1. はじめに 01:00 〜 04:00 2. FW を使った/使ってないコードの比較 04:00 〜

    10:00 3. CakePHP の index.php 10:00 〜 15:00 4. Laravel の index.php 15:00 〜 20:00 5. Slim の index.php 20:00 〜 25:00 6. Symfony の index.php 25:00 〜 30:00 7. 各 FW の共通点 〜問いへの答え〜 30:00 〜 34:00 8. 自己紹介 34:00 〜 35:00 目次(※時間は目安です)
  82. • URL と対応する PHP ファイルがなくても 404 エラーにならないのか • HTTP リクエストを受け取ると指定のクラスのメソッドが呼ばれるのか

    • $_GET, $_POST を使わずにパラメータにアクセスできるのか • echo 等をしていないのに HTTP レスポンスを返せるのか 問い:FW を使って実装すると、どうして
  83. ✅ URL と対応する PHP ファイルがなくても 404 エラーにならないのか • HTTP リクエストを受け取ると指定のクラスのメソッドが呼ばれるのか

    • $_GET, $_POST を使わずにパラメータにアクセスできるのか • echo 等をしていないのに HTTP レスポンスを返せるのか → すべてのリクエストを index.php でハンドリングしているから  (Apache や Nginx 等でそのように設定) 問い:FW を使って実装すると、どうして
  84. Web アプリケーション(サーバサイド)がやっていること サーバ GET /search-products HTTP/1.1 Accept: text/html Accept-Encoding: gzip,

    deflate Accept-Language: ja,en-US;q=0.9,en;q=0.8 Connection: keep-alive : : クライアント HTTP リクエスト index.php
  85. ✅ URL と対応する PHP ファイルがなくても 404 エラーにならないのか • HTTP リクエストを受け取ると指定のクラスのメソッドが呼ばれるのか

    • $_GET, $_POST を使わずにパラメータにアクセスできるのか • echo 等をしていないのに HTTP レスポンスを返せるのか 問い:FW を使って実装すると、どうして
  86. ✅ URL と対応する PHP ファイルがなくても 404 エラーにならないのか ✅ HTTP リクエストを受け取ると指定のクラスのメソッドが呼ばれるのか

    • $_GET, $_POST を使わずにパラメータにアクセスできるのか • echo 等をしていないのに HTTP レスポンスを返せるのか → 記述したルーティングを読み込んでアプリケーションを構築しているから  (依存関係やミドルウェアなども) 問い:FW を使って実装すると、どうして
  87. ✅ URL と対応する PHP ファイルがなくても 404 エラーにならないのか ✅ HTTP リクエストを受け取ると指定のクラスのメソッドが呼ばれるのか

    • $_GET, $_POST を使わずにパラメータにアクセスできるのか • echo 等をしていないのに HTTP レスポンスを返せるのか 問い:FW を使って実装すると、どうして
  88. Web アプリケーション(サーバサイド)がやっていること サーバ GET /search-products HTTP/1.1 Accept: text/html Accept-Encoding: gzip,

    deflate Accept-Language: ja,en-US;q=0.9,en;q=0.8 Connection: keep-alive : : クライアント HTTP リクエスト Request Application index.php
  89. ✅ URL と対応する PHP ファイルがなくても 404 エラーにならないのか ✅ HTTP リクエストを受け取ると指定のクラスのメソッドが呼ばれるのか

    ✅ $_GET, $_POST を使わずにパラメータにアクセスできるのか • echo 等をしていないのに HTTP レスポンスを返せるのか → FW 内部で($_GET, $_POST 等をもとに)Request オブジェクトに   詰めているから 問い:FW を使って実装すると、どうして
  90. ✅ URL と対応する PHP ファイルがなくても 404 エラーにならないのか ✅ HTTP リクエストを受け取ると指定のクラスのメソッドが呼ばれるのか

    ✅ $_GET, $_POST を使わずにパラメータにアクセスできるのか • echo 等をしていないのに HTTP レスポンスを返せるのか 問い:FW を使って実装すると、どうして
  91. Web アプリケーション(サーバサイド)がやっていること サーバ クライアント HTTP/1.1 200 OK Date: Sat, 25

    Mar 2023 02:15:00 GMT Server: Apache/2.4.54 (Debian) X-Powered-By: PHP/8.1.16 : : <!DOCTYPE html> <html> : : HTTP レスポンス 処理の結果に基づくレ スポンスを生成、送信 Response Application index.php
  92. ✅ URL と対応する PHP ファイルがなくても 404 エラーにならないのか ✅ HTTP リクエストを受け取ると指定のクラスのメソッドが呼ばれるのか

    ✅ $_GET, $_POST を使わずにパラメータにアクセスできるのか ✅ echo 等をしていないのに HTTP レスポンスを返せるのか → Response オブジェクトに詰められた情報をもとに   FW 内部で echo 等しているから 問い:FW を使って実装すると、どうして
  93. ✅ URL と対応する PHP ファイルがなくても 404 エラーにならないのか ✅ HTTP リクエストを受け取ると指定のクラスのメソッドが呼ばれるのか

    ✅ $_GET, $_POST を使わずにパラメータにアクセスできるのか ✅ echo 等をしていないのに HTTP レスポンスを返せるのか → 各 FW の index.php を読み解いたことで答えが見えた! 問い:FW を使って実装すると、どうして
  94. index.php Web アプリケーション(サーバサイド)がやっていること サーバ クライアント HTTP リクエスト HTTP レスポンス リクエスト内容に基づ

    く処理の実行 処理の結果に基づくレ スポンスを生成、送信 HTTP/1.1 200 OK Date: Sat, 25 Mar 2023 02:15:00 GMT Server: Apache/2.4.54 (Debian) X-Powered-By: PHP/8.1.16 : : <!DOCTYPE html> <html> : : Application Response GET /search-products HTTP/1.1 Accept: text/html Accept-Encoding: gzip, deflate Accept-Language: ja,en-US;q=0.9,en;q=0.8 Connection: keep-alive : : Request
  95. index.php Web アプリケーション(サーバサイド)がやっていること サーバ クライアント HTTP リクエスト HTTP レスポンス リクエスト内容に基づ

    く処理の実行 処理の結果に基づくレ スポンスを生成、送信 HTTP/1.1 200 OK Date: Sat, 25 Mar 2023 02:15:00 GMT Server: Apache/2.4.54 (Debian) X-Powered-By: PHP/8.1.16 : : <!DOCTYPE html> <html> : : Application Response GET /search-products HTTP/1.1 Accept: text/html Accept-Encoding: gzip, deflate Accept-Language: ja,en-US;q=0.9,en;q=0.8 Connection: keep-alive : : Request
  96. • すべてのリクエストを index.php でハンドリングしている • 概ね共通した流れがある • FW を使っていてもいなくても、同じ PHP

    が解釈できるソースコード • 根本のところでは同じような処理が書かれている まとめ
  97. 1. はじめに 01:00 〜 04:00 2. FW を使った/使ってないコードの比較 04:00 〜

    10:00 3. CakePHP の index.php 10:00 〜 15:00 4. Laravel の index.php 15:00 〜 20:00 5. Slim の index.php 20:00 〜 25:00 6. Symfony の index.php 25:00 〜 30:00 7. 各 FW の共通点 〜問いへの答え〜 30:00 〜 34:00 8. 自己紹介 34:00 〜 35:00 目次(※時間は目安です)