Slide 1

Slide 1 text

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

Slide 2

Slide 2 text

• 発表内容の性質上コードを示すことが多く文字が小さくて読めない可能 性があります • 資料の URL を共有してありますので、 必要に応じて手元で資料を確認しながら聴講、視聴してください • 対象としたフレームワーク、およびバージョンは以下のとおりです • CakePHP 4.4 • Laravel 10.0 • Slim 4.11 • Symfony 6.2 • 各公式ドキュメントに則り、通常の Web アプリケーション開発用途で インストールした場合を想定しています おことわり

Slide 3

Slide 3 text

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 目次(※時間は目安です)

Slide 4

Slide 4 text

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 目次(※時間は目安です)

Slide 5

Slide 5 text

• 多くの場合、我々は Web アプリケーションフレームワーク(以下、 FW)を利用して開発している • 一方で単一の PHP ファイルでも Web ページを表示することはできる • 書き方は全く異なっているのに、どうして同じ PHP で動くのだろう? はじめに

Slide 6

Slide 6 text

と、本題に入る前に 基礎のおさらいをします

Slide 7

Slide 7 text

Web アプリケーション(サーバサイド)がやっていること クライアント サーバ

Slide 8

Slide 8 text

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 リクエスト

Slide 9

Slide 9 text

Web アプリケーション(サーバサイド)がやっていること クライアント サーバ リクエスト内容に基づ く処理の実行

Slide 10

Slide 10 text

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 : : : : クライアント サーバ 処理の結果に基づくレ スポンスを生成、送信 HTTP レスポンス

Slide 11

Slide 11 text

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 : : : : HTTP レスポンス リクエスト内容に基づ く処理の実行 処理の結果に基づくレ スポンスを生成、送信

Slide 12

Slide 12 text

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 目次(※時間は目安です)

Slide 13

Slide 13 text

こんな Web ページの表示を考える 入力 & フォーム送信

Slide 14

Slide 14 text

• 例として Slim を取り上げます • 他の FW でも大まかな流れは同じようなものだと思います • 説明の目的上、ORM は使っていません • 使っていようがいまいが説明したいことには関係ありません • 各種設定(依存関係の注入含む)は適切になされているものとします FW を使って実装する場合 - 前提条件

Slide 15

Slide 15 text

FW を使って実装する場合(Slim の例) get('/search-products', SearchProducts::class); };

Slide 16

Slide 16 text

FW を使って実装する場合(Slim の例) 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')); } }

Slide 17

Slide 17 text

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

Slide 18

Slide 18 text

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'] ?? '';

Slide 19

Slide 19 text

FW を使って実装する場合(Slim の例) 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'));

Slide 20

Slide 20 text

FW を使わずに実装する場合 prepare('SELECT `name`, `price` FROM `products` WHERE `name` LIKE ?'); $stmt->execute(['%' . $q . '%']); // .php ファイル内に HTML を記述したり、echo を使ったりすることで出力 ?> 商品名 価格 fetch()): ?> ドキュメントルート

Slide 21

Slide 21 text

FW を使わずに実装する場合 prepare('SELECT `name`, `price` FROM `products` WHERE `name` LIKE ?'); $stmt->execute(['%' . $q . '%']); // .php ファイル内に HTML を記述したり、echo を使ったりすることで出力 ?> 商品名 価格 fetch()): ?> // 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, );

Slide 22

Slide 22 text

FW を使わずに実装する場合 prepare('SELECT `name`, `price` FROM `products` WHERE `name` LIKE ?'); $stmt->execute(['%' . $q . '%']); // .php ファイル内に HTML を記述したり、echo を使ったりすることで出力 ?> 商品名 価格 fetch()): ?> // $_GET, $_POST 等で入力を受け取る $q = $_GET['q'] ?? '';

Slide 23

Slide 23 text

FW を使わずに実装する場合 prepare('SELECT `name`, `price` FROM `products` WHERE `name` LIKE ?'); $stmt->execute(['%' . $q . '%']); // .php ファイル内に HTML を記述したり、echo を使ったりすることで出力 ?> 商品名 価格 fetch()): ?>

Slide 24

Slide 24 text

FW を使わずに実装する場合 prepare('SELECT `name`, `price` FROM `products` WHERE `name` LIKE ?'); $stmt->execute(['%' . $q . '%']); // .php ファイル内に HTML を記述したり、echo を使ったりすることで出力 ?> 商品名 価格 fetch()): ?> // .php ファイル内に HTML を記述したり、echo を使ったりすることで出力 ?> : :

Slide 25

Slide 25 text

異なる点 FW あり FW なし URL ルーティングを記述 ファイルパスがそのまま 依存関係 インスタンスが渡される include, require パラメータの受け取り ServerRequestInterface を介する $_GET, $_POST 出力 ResponseInterface を返す HTML を直接記述, echo 等

Slide 26

Slide 26 text

異なる点 - URL FW あり FW なし return function (App $app) { $app->get('/search-products', SearchProducts::class); }; ドキュメントルート routes.php に書いたパス ドキュメントルートからのファイルパス

Slide 27

Slide 27 text

異なる点 - 依存関係 // 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 なし

Slide 28

Slide 28 text

異なる点 - パラメータの受け取り // ServerRequestInterface を介して入力を受け取る $q = $request->getQueryParams()['q'] ?? ''; // $_GET, $_POST 等で入力を受け取る $q = $_GET['q'] ?? ''; FW あり FW なし

Slide 29

Slide 29 text

異なる点 - 出力 // echo などは使わず、ResponseInterface のインスタンスを返す return Twig::fromRequest($request) ->render($response, 'search-products.html', compact('q', 'products' // .php ファイル内に HTML を記述したり、echo を使ったりすることで出力 ?> : : FW あり FW なし

Slide 30

Slide 30 text

• PHP-FIG(Framework Interop Group) という団体によって PSR(PHP Standard Recommendation)として定められているもの • 仕様と interface の定義が提供されている (参考)ServerRequestInterface と ResponseInterface https://www.php-fig.org/ https://github.com/php-fig

Slide 31

Slide 31 text

本トークでは以降、(正しい表現とは言えないが) • 「ServerRequestInterface を実装したクラスのインスタンス」のことを 「Request のインスタンス」あるいは単に「Request」 • 「ResponseInterface を実装したクラスのインスタンス」のことを 「Response のインスタンス」あるいは単に「Response」 と表記する/呼ぶこととします。 (参考)ServerRequestInterface と ResponseInterface

Slide 32

Slide 32 text

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

Slide 33

Slide 33 text

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

Slide 34

Slide 34 text

Apache や Nginx の設定が各ドキュメントに書かれている • https://book.cakephp.org/4/ja/installation.html#url-rewriting • https://laravel.com/docs/10.x/deployment#server-configuration • https://www.slimframework.com/docs/v4/start/web-servers.html • https://symfony.com/doc/current/setup/web_server_configuration. html すべてのリクエストを index.php でハンドリングしている

Slide 35

Slide 35 text

すべてのリクエストを 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 の仕組みがわかる!

Slide 36

Slide 36 text

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 目次(※時間は目安です)

Slide 37

Slide 37 text

CakePHP の index.php https://github.com/cakephp/app/blob/4.4.2/webroot/index.php emit($server->run()); ※スペースの都合でライセンス表記等を省略しています

Slide 38

Slide 38 text

CakePHP の index.php emit($server->run()); // Check platform requirements require dirname(__DIR__) . '/config/requirements.php';

Slide 39

Slide 39 text

CakePHP の index.php emit($server->run()); require dirname(__DIR__) . '/vendor/autoload.php';

Slide 40

Slide 40 text

(参考)Composer による autoloading について https://docs.google.com/presentation/d/1pVTSdNuq5mySj7jCIhMuv8eTPtWXBuXzk7y 6HHJ9khM/edit?usp=sharing

Slide 41

Slide 41 text

CakePHP の index.php emit($server->run()); // Bind your application to the server. $server = new Server(new Application(dirname(__DIR__) . '/config')); Server Application 各種設定 DI、ルーティング、 ミドルウェア......

Slide 42

Slide 42 text

CakePHP の index.php emit($server->run()); // Run the request/response through the application and emit the response. $server->emit($server->run());

Slide 43

Slide 43 text

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 のインスタンスを生成

Slide 44

Slide 44 text

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 を返す

Slide 45

Slide 45 text

CakePHP の index.php emit($server->run()); // Run the request/response through the application and emit the response. $server->emit($server->run());

Slide 46

Slide 46 text

\Cake\Http\Server::emit() https://github.com/cakephp/cakephp/blob/4.4.11/src/Http/Server.php public function emit(ResponseInterface $response, ?EmitterInterface $emitter = null): void { if (!$emitter) { $emitter = new ResponseEmitter(); } $emitter->emit($response); }

Slide 47

Slide 47 text

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

Slide 48

Slide 48 text

\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 している!

Slide 49

Slide 49 text

• サーバにインストールされている依存関係のチェック • Composer の autoload.php の読み込み • Application インスタンスの生成 • 依存関係(DI コンテナ)、ルーティング、ミドルウェア等の反映 • Server インスタンスの生成 • 上記 Application を受け取る • 処理を実行する(run) • 生成された Request を受け取り Response を返す • レスポンスを送信する(emit) • Response のインスタンスをもとに 実際に HTTP ヘッダ、ボディ(HTML, JSON 等)を出力する CakePHP の index.php の処理の流れまとめ

Slide 50

Slide 50 text

(参考)CakePHP 公式ドキュメント https://book.cakephp.org/4/ja/intro.html#request-cycle

Slide 51

Slide 51 text

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 目次(※時間は目安です)

Slide 52

Slide 52 text

Laravel の index.php 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

Slide 53

Slide 53 text

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

Slide 54

Slide 54 text

make(Kernel::class); $response = $kernel->handle( $request = Request::capture() )->send(); $kernel->terminate($request, $response); Laravel の index.php require __DIR__.'/../vendor/autoload.php';

Slide 55

Slide 55 text

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、ルーティング、 ミドルウェア......

Slide 56

Slide 56 text

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 のインスタンスを生成

Slide 57

Slide 57 text

\Illuminate\Http\Request::capture() https://github.com/laravel/framework/blob/v10.0.3/src/Illuminate/ Http/Request.php public static function capture() { static::enableHttpMethodParameterOverride(); return static::createFromBase(SymfonyRequest::createFromGlobals()); } \Symfony\Component\HttpFoundation\Request

Slide 58

Slide 58 text

make(Kernel::class); $response = $kernel->handle( $request = Request::capture() )->send(); $kernel->terminate($request, $response); Laravel の index.php $response = $kernel->handle( $request = Request::capture() )->send();

Slide 59

Slide 59 text

\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

Slide 60

Slide 60 text

make(Kernel::class); $response = $kernel->handle( $request = Request::capture() )->send(); $kernel->terminate($request, $response); Laravel の index.php $response = $kernel->handle( $request = Request::capture() )->send();

Slide 61

Slide 61 text

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

Slide 62

Slide 62 text

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 している!

Slide 63

Slide 63 text

• メンテナンスモードのチェック • Composer の autoload.php の読み込み • Kernel インスタンスの生成 • 依存関係(DI コンテナ)、ルーティング、ミドルウェア等の反映 • 処理を実行する(handle) • 生成された Request を受け取り Response を返す • レスポンスを送る(send) • Response のインスタンスをもとに 実際に HTTP ヘッダ、ボディ(HTML, JSON 等)を出力する Laravel の index.php の処理の流れまとめ

Slide 64

Slide 64 text

(参考)Laravel 公式ドキュメント https://laravel.com/docs/10.x/lifecycle

Slide 65

Slide 65 text

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 目次(※時間は目安です)

Slide 66

Slide 66 text

Slim の index.php :
 https://github.com/slimphp/Slim-Skeleton/blob/4.5.0/public/index.php 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); :


Slide 67

Slide 67 text

Slim の index.php 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';

Slide 68

Slide 68 text

Slim の index.php 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 コンテナ

Slide 69

Slide 69 text

// Instantiate PHP-DI ContainerBuilder $containerBuilder = new ContainerBuilder(); Slim の index.php 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 コンテナ

Slide 70

Slide 70 text

Slim の index.php 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 コンテナ

Slide 71

Slide 71 text

Slim の index.php 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 コンテナ

Slide 72

Slide 72 text

// 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 コンテナ ルーティング、 ミドルウェア

Slide 73

Slide 73 text

// 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 ※エラーログや最低限のミドルウェア等の設定

Slide 74

Slide 74 text

// 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 のインスタンスを生成

Slide 75

Slide 75 text

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

Slide 76

Slide 76 text

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 を返す

Slide 77

Slide 77 text

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

Slide 78

Slide 78 text

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

Slide 79

Slide 79 text

\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 している!

Slide 80

Slide 80 text

• Composer の autoload.php の読み込み • App インスタンスの生成 • DI コンテナを構築し、受け取る • ミドルウェアやルーティングの反映 • エラーログや最低限のミドルウェア等の設定 • Request インスタンスの生成 • 処理を実行する(handle) • 生成された Request を受け取り Response を返す • レスポンスを送信する(emit) • Response のインスタンスをもとに 実際に HTTP ヘッダ、ボディ(HTML, JSON 等)を出力する Slim の index.php の処理の流れまとめ

Slide 81

Slide 81 text

(参考)Slim 公式ドキュメント https://www.slimframework.com/docs/v4/concepts/life-cycle.html

Slide 82

Slide 82 text

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 目次(※時間は目安です)

Slide 83

Slide 83 text

Symfony の index.php https://github.com/symfony/recipes/blob/main/symfony/framework- bundle/6.2/public/index.php

Slide 84

Slide 84 text

autoload_runtime.php https://github.com/symfony/runtime/blob/v6.2.5/Internal/autoload_ runtime.template(←はテンプレート) dirname(__DIR__, 1), ]); [$app, $args] = $runtime ->getResolver($app) ->resolve(); $app = $app(...$args); exit( $runtime ->getRunner($app) ->run() );

Slide 85

Slide 85 text

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

Slide 86

Slide 86 text

autoload_runtime.php dirname(__DIR__, 1), ]); [$app, $args] = $runtime ->getResolver($app) ->resolve(); $app = $app(...$args); exit( $runtime ->getRunner($app) ->run() ); $app = require $_SERVER['SCRIPT_FILENAME']; = index.php

Slide 87

Slide 87 text

Symfony の index.php https://github.com/symfony/recipes/blob/main/symfony/framework- bundle/6.2/public/index.php

Slide 88

Slide 88 text

autoload_runtime.php 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

Slide 89

Slide 89 text

autoload_runtime.php 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

Slide 90

Slide 90 text

autoload_runtime.php dirname(__DIR__, 1), ]); [$app, $args] = $runtime ->getResolver($app) ->resolve(); $app = $app(...$args); exit( $runtime ->getRunner($app) ->run() ); exit( $runtime ->getRunner($app) ->run() );

Slide 91

Slide 91 text

\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 のインスタンスを生成

Slide 92

Slide 92 text

autoload_runtime.php dirname(__DIR__, 1), ]); [$app, $args] = $runtime ->getResolver($app) ->resolve(); $app = $app(...$args); exit( $runtime ->getRunner($app) ->run() ); exit( $runtime ->getRunner($app) ->run() );

Slide 93

Slide 93 text

\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 を返す

Slide 94

Slide 94 text

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

Slide 95

Slide 95 text

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

Slide 96

Slide 96 text

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 している!

Slide 97

Slide 97 text

• Composer の autoload.php の読み込み • Kernel のインスタンス生成 • 依存関係(DI コンテナ)、ルーティング、ミドルウェア等の反映 • Runner インスタンスの生成、処理の実行 • 上記 Kernel を受け取る • 同時に Request インスタンスを生成 • 処理を実行する(handle) • 生成された Request を受け取り Response を返す • レスポンスを送信する(send) • Response のインスタンスをもとに 実際に HTTP ヘッダ、ボディ(HTML, JSON 等)を出力する Symfony の index.php の処理の流れまとめ

Slide 98

Slide 98 text

(参考)Symfony 公式ドキュメント https://symfony.com/doc/current/components/http_kernel.html

Slide 99

Slide 99 text

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 目次(※時間は目安です)

Slide 100

Slide 100 text

各 FW の index.php の処理の流れ再掲

Slide 101

Slide 101 text

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

Slide 102

Slide 102 text

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

Slide 103

Slide 103 text

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

Slide 104

Slide 104 text

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

Slide 105

Slide 105 text

各 FW の index.php の処理の流れ再掲

Slide 106

Slide 106 text

各 FW の index.php の処理の流れ再掲 依存関係、ルーティング、 ミドルウェア等の読み込み、反映

Slide 107

Slide 107 text

Web アプリケーション(サーバサイド)がやっていること クライアント サーバ アプリケーションの準備 · autoload · 設定等の読み込み Application index.php

Slide 108

Slide 108 text

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

Slide 109

Slide 109 text

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

Slide 110

Slide 110 text

各 FW の index.php の処理の流れ再掲 処理の実行 (Request → Response)

Slide 111

Slide 111 text

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

Slide 112

Slide 112 text

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

Slide 113

Slide 113 text

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

Slide 114

Slide 114 text

各 FW の index.php の処理の流れ再掲 レスポンスの送信

Slide 115

Slide 115 text

Application Web アプリケーション(サーバサイド)がやっていること サーバ クライアント Request Response リクエスト内容に基づ く処理の実行 index.php

Slide 116

Slide 116 text

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 : : : : HTTP レスポンス 処理の結果に基づくレ スポンスを生成、送信 Response Application index.php

Slide 117

Slide 117 text

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

Slide 118

Slide 118 text

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

Slide 119

Slide 119 text

各 FW の index.php の処理の流れ再掲

Slide 120

Slide 120 text

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

Slide 121

Slide 121 text

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

Slide 122

Slide 122 text

(参考) https://tadsan.fanbox.cc/posts/3674121

Slide 123

Slide 123 text

(参考) https://tadsan.fanbox.cc/posts/3674121

Slide 124

Slide 124 text

• すべてのリクエストを index.php でハンドリングしている • 概ね共通した流れがある • FW を使っていてもいなくても、同じ PHP が解釈できるソースコード • 根本のところでは同じような処理が書かれている まとめ

Slide 125

Slide 125 text

FW の仕組みを知っているとデバッグや拡張の役に立つ https://docs.google.com/presentation/d/1kA4ItJdHamg8tSdxsUhGgOwtpXr-erSocu8GLFkGO VE/edit?usp=sharing

Slide 126

Slide 126 text

FW の仕組みを知っているとデバッグや拡張の役に立つ https://docs.google.com/presentation/d/1kA4ItJdHamg8tSdxsUhGgOwtpXr-erSocu8GLFkGO VE/edit?usp=sharing

Slide 127

Slide 127 text

Day 0(前夜祭)の FW に関するセッションふたつ https://fortee.jp/phperkaigi-2023/proposal/db787adc-c855-4114-99b6-8c6958d47b42

Slide 128

Slide 128 text

Day 0(前夜祭)の FW に関するセッションふたつ https://fortee.jp/phperkaigi-2023/proposal/2466b395-cc29-479a-a90a-395de6528028

Slide 129

Slide 129 text

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 目次(※時間は目安です)

Slide 130

Slide 130 text

岡田 正平/おかしょい Twitter: @okashoi GitHub: @okashoi 所属:株式会社ウィルゲート 登壇: 寄稿: PHPerKaigi 2023 シルバースポンサー

Slide 131

Slide 131 text

ウィルゲートは PHPerKaigi を応援しています! 開発組織について https://speakerdeck.com/willgate/willgate-engineering-team 廊下、アンカンファレンス、懇親会等でお話しましょう!!