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

CakePHPの内部実装 から理解するPSR-7

CakePHPの内部実装 から理解するPSR-7

kubo ayumu

June 30, 2022
Tweet

More Decks by kubo ayumu

Other Decks in Programming

Transcript

  1. Confidential © 2018 for LANCERS, Inc. All Rights Reserved 2

    2 久保 路(くぼ あゆむ) ・LancersのQAチームに所属 ・CakePHP2→4へのバージョンアップ プロジェクトに参画中 ・クラフトビールが好き
  2. Confidential © 2018 for LANCERS, Inc. All Rights Reserved 3

    3 public function add() { if ($this->request->is('post')) { $inputParams = $this->request->getData(); $user = $this->fetchTable('Users')->get($inputParams['id']); …… } }
  3. Confidential © 2018 for LANCERS, Inc. All Rights Reserved 4

    4 public function add() { if ($this->request->is('post')) { $inputParams = $this->request->getData(); $user = $this->fetchTable('Users')->get($inputParams['id']); …… } }
  4. Confidential © 2018 for LANCERS, Inc. All Rights Reserved 5

    5 public function add() { if ($this->request->is('post')) { $inputParams = $this->request->getData(); $user = $this->fetchTable('Users')->get($inputParams['id']); …… } } ServerRequestのインスタンスであり 仕様はPHPFIGという団体がPSRとして規定
  5. Confidential © 2018 for LANCERS, Inc. All Rights Reserved 6

    今⽇はこの$this->requestを ちょっと深掘ってみます
  6. Confidential © 2018 for LANCERS, Inc. All Rights Reserved 7

    7 今⽇持ち帰っていただきたいこと ・PSRとは⼀体何なのか ・PSR-7ではどんなインターフェースを定めているのか ・HTTPリクエストとレスポンスは フレームワーク内でどのように扱われているのか
  7. Confidential © 2018 for LANCERS, Inc. All Rights Reserved 8

    8 1 PSRって? 2 PSR-7の主張 3 Cakeの内部実装を見てみる(Request編) 4 最後に アジェンダ
  8. Confidential © 2018 for LANCERS, Inc. All Rights Reserved 10

    • PHP-FIGが定める規約 (PHPフレームワーク相互運⽤グループ) • ⽬的 ◦ 共通の仕様を定める ◦ 相互運⽤性を⾼める • 具体例 ◦ インターフェースの仕様 ◦ コーディング規約 ◦ etc… PSRとは
  9. Confidential © 2018 for LANCERS, Inc. All Rights Reserved 16

    • HTTPメッセージ(リクエスト・レスポンス)に関する インターフェースを定める • リクエストからレスポンスへを⽣成する処理の流れで はなく、それ⾃⾝の仕様を定める(流れはPSR15が定 めている) PSR-7とは
  10. Confidential © 2018 for LANCERS, Inc. All Rights Reserved 17

    • インターフェース定義 PSR-7とは Psr¥Http¥MessageInterface Psr¥Http¥RequestInterface Psr¥Http¥ServerRequestInterface Psr¥Http¥ResponseInterface etc: StreamInterface, UriInterface, UploadInterface
  11. Confidential © 2018 for LANCERS, Inc. All Rights Reserved 18

    PSR-7とは «interface» Message «interface» Request «interface» Response «interface» ServerRequest extends extends extends
  12. Confidential © 2018 for LANCERS, Inc. All Rights Reserved 20

    «interface» Message «interface» Request «interface» Response «interface» ServerRequest extends extends extends
  13. Confidential © 2018 for LANCERS, Inc. All Rights Reserved 21

    «interface» Message «interface» Request «interface» Response «interface» ServerRequest extends extends extends
  14. Confidential © 2018 for LANCERS, Inc. All Rights Reserved 22

    • クライアントからサーバーへの要求とサーバーからクライアント への応答で構成され、それぞれに共通するメソッドを定義する • メソッド例 ◦ getProtocolVersion ◦ getHeader ◦ getBody ◦ etc.. • 継承インターフェース ◦ RequestInterface ◦ ResponseInterface MessageInterface
  15. Confidential © 2018 for LANCERS, Inc. All Rights Reserved 23

    «interface» Message «interface» Request «interface» Response «interface» ServerRequest extends extends extends
  16. Confidential © 2018 for LANCERS, Inc. All Rights Reserved 24

    • クライアントが送るリクエストの構成を定義する • リクエストターゲットを取得するためのメソッドやそれを⽤いて 新しいインスタンスを作成するためのメソッドが⽤意されている • メソッド例 ◦ getRequestTarget ◦ getMethod ◦ getUri • 継承インターフェース ◦ ServerRequestInterface RequestInterface
  17. Confidential © 2018 for LANCERS, Inc. All Rights Reserved 25

    «interface» Message «interface» Request «interface» Response «interface» ServerRequest extends extends extends
  18. Confidential © 2018 for LANCERS, Inc. All Rights Reserved 26

    • サーバーが受け取るリクエストの構成を定義する • RequestInterfaceの拡張であり、リクエストに関連するサーバ情 報が備わっている($_SERVERの内容など) • メソッド例 ◦ getServerParams ◦ getCookieParams ◦ getQueryParams • 実装先 ◦ cakephp/src/Http/ServerRequest.php ServerRequestInterface
  19. Confidential © 2018 for LANCERS, Inc. All Rights Reserved 27

    cakephp/src/Http/ServerRequest.php CakePHPでの実例(ServerRequest実装先) /** * A class that helps wrap Request information and particulars about a single request. * Provides methods commonly used to introspect on the request headers and request body. */ class ServerRequest implements ServerRequestInterface { protected $params = [ 'plugin' => null, 'controller' => null, 'action' => null, '_ext' => null, 'pass' => [], ]; public function __construct(array $config = []) { $config += [ 'params' => $this->params, 'query' => [], 'post' => [], 'files' => [], 'cookies' => [], 'environment' => [], 'url' => '', 'uri' => null, 'base' => '', 'webroot' => '', 'input' => null, ]; $this->_setConfig($config); }
  20. Confidential © 2018 for LANCERS, Inc. All Rights Reserved 28

    cakephp/src/Http/ServerRequest.php CakePHPでの実例(ServerRequest実装先) /** * A class that helps wrap Request information and particulars about a single request. * Provides methods commonly used to introspect on the request headers and request body. */ class ServerRequest implements ServerRequestInterface { protected $params = [ 'plugin' => null, 'controller' => null, 'action' => null, '_ext' => null, 'pass' => [], ]; public function __construct(array $config = []) { $config += [ 'params' => $this->params, 'query' => [], 'post' => [], 'files' => [], 'cookies' => [], 'environment' => [], 'url' => '', 'uri' => null, 'base' => '', 'webroot' => '', 'input' => null, ]; $this->_setConfig($config); }
  21. Confidential © 2018 for LANCERS, Inc. All Rights Reserved 29

    どこでどのようにServerRequetの インスタンスが⽣成されている︖
  22. Confidential © 2018 for LANCERS, Inc. All Rights Reserved 30

    cakephp/src/Http/Server.php CakePHPでの実例(ServerRequest⽣成の過 程) public function run(?ServerRequestInterface $request = null,?MiddlewareQueue $middlewareQueue = null): ResponseInterface { $this->bootstrap(); $request = $request ?: ServerRequestFactory::fromGlobals(); $middleware = $this->app->middleware($middleware Queue ?? new Middleware Queue()); if ($this->app instanceof PluginApplicationInterface) { $middleware = $this->app->plugin Middleware($middleware); } $this->dispatchEvent('Server.buildMiddleware', ['middleware' => $middleware]); $response = $this->runner->run($middleware, $request, $this->app); if ($request instanceof ServerRequest) { $request>getSession()->close(); } return $response; }
  23. Confidential © 2018 for LANCERS, Inc. All Rights Reserved 31

    cakephp/src/Http/Server.php CakePHPでの実例(ServerRequest⽣成の過 程) public function run(?ServerRequestInterface $request = null,?MiddlewareQueue $middlewareQueue = null): ResponseInterface { $this->bootstrap(); $request = $request ?: ServerRequestFactory::fromGlobals(); $middleware = $this->app->middleware($middleware Queue ?? new Middleware Queue()); if ($this->app instanceof PluginApplicationInterface) { $middleware = $this->app->plugin Middleware($middleware); } $this->dispatchEvent('Server.buildMiddleware', ['middleware' => $middleware]); $response = $this->runner->run($middleware, $request, $this->app); if ($request instanceof ServerRequest) { $request>getSession()->close(); } return $response; }
  24. Confidential © 2018 for LANCERS, Inc. All Rights Reserved 32

    cakephp/src/Http/ServerRequestFactory.php CakePHPでの実例(ServerRequest⽣成の過程) public static function fromGlobals( ?array $server = null, ?array $query = null, ?array $parsedBody = null, ?array $cookies = null, ?array $files = null ): ServerRequest { $server = normalizeServer($server ?: $_SERVER); $uri = static::createUri($server); /** @psalm-suppress NoInterfaceProperties */ $sessionConfig = (array)Configure::read('Session') + [ 'defaults' => 'php', 'cookiePath' => $uri->webroot, ]; $session = Session::create($sessionConfig); /** @psalm-suppress NoInterfaceProperties */ $request = new ServerRequest([ 'environment' => $server, 'uri' => $uri, 'cookies' => $cookie ?: $_COOKIE, 'query' => $query ?: $_GET, 'webroot' => $uri->webroot, 'base' => $uri->base, 'session' => $session, 'input' => $server['CAKEPHP_INPUT'] ?? null, ]); $request = static::marshalBodyAndRequestMethod($parsedBody ?? $_POST, $request); $request = static::marshalFiles($files ?? $_FILES, $request); return $request; }
  25. Confidential © 2018 for LANCERS, Inc. All Rights Reserved 33

    cakephp/src/Http/ServerRequestFactory.php CakePHPでの実例(ServerRequest⽣成の過程) public static function fromGlobals( ?array $server = null, ?array $query = null, ?array $parsedBody = null, ?array $cookies = null, ?array $files = null ): ServerRequest { $server = normalizeServer($server ?: $_SERVER); $uri = static::createUri($server); /** @psalm-suppress NoInterfaceProperties */ $sessionConfig = (array)Configure::read('Session') + [ 'defaults' => 'php', 'cookiePath' => $uri->webroot, ]; $session = Session::create($sessionConfig); /** @psalm-suppress NoInterfaceProperties */ $request = new ServerRequest([ 'environment' => $server, 'uri' => $uri, 'cookies' => $cookie ?: $_COOKIE, 'query' => $query ?: $_GET, 'webroot' => $uri->webroot, 'base' => $uri->base, 'session' => $session, 'input' => $server['CAKEPHP_INPUT'] ?? null, ]); $request = static::marshalBodyAndRequestMethod($parsedBody ?? $_POST, $request); $request = static::marshalFiles($files ?? $_FILES, $request); return $request; }
  26. Confidential © 2018 for LANCERS, Inc. All Rights Reserved 34

    cakephp/src/Http/ServerRequestFactory.php CakePHPでの実例(ServerRequest⽣成の過程) public static function fromGlobals( ?array $server = null, ?array $query = null, ?array $parsedBody = null, ?array $cookies = null, ?array $files = null ): ServerRequest { $server = normalizeServer($server ?: $_SERVER); $uri = static::createUri($server); /** @psalm-suppress NoInterfaceProperties */ $sessionConfig = (array)Configure::read('Session') + [ 'defaults' => 'php', 'cookiePath' => $uri->webroot, ]; $session = Session::create($sessionConfig); /** @psalm-suppress NoInterfaceProperties */ $request = new ServerRequest([ 'environment' => $server, 'uri' => $uri, 'cookies' => $cookie ?: $_COOKIE, 'query' => $query ?: $_GET, 'webroot' => $uri->webroot, 'base' => $uri->base, 'session' => $session, 'input' => $server['CAKEPHP_INPUT'] ?? null, ]); $request = static::marshalBodyAndRequestMethod($parsedBody ?? $_POST, $request); $request = static::marshalFiles($files ?? $_FILES, $request); return $request; }
  27. Confidential © 2018 for LANCERS, Inc. All Rights Reserved 35

    アプリケーションやミドルウェアを通して リクエストからレスポンスを⽣成する過程で、 リクエストやサーバー情報が詰まった スーパーグローバル変数からServerRequestオブ ジェクトを⽣成している CakePHPでの実例(ServerRequest⽣成の過 程)
  28. Confidential © 2018 for LANCERS, Inc. All Rights Reserved 36

    どんな感じでServerRequetの インスタンスを使っている︖
  29. Confidential © 2018 for LANCERS, Inc. All Rights Reserved 37

    cakephp/src/Controller/ControllerFactory.php CakePHPでの実例 public function getControllerClass(ServerRequest $request): ?string { $pluginPath = ''; $namespace = 'Controller'; $controller = $request->getParam('controller', ''); if ($request->getParam('plugin')) { $pluginPath = $request->getParam('plugin') . '.'; } if ($request->getParam('prefix')) { $prefix = $request->getParam('prefix'); …… /** @var class-string<¥Cake¥Controller¥Controller>|null */ return App::className($pluginPath . $controller, $namespace, 'Controller'); }
  30. Confidential © 2018 for LANCERS, Inc. All Rights Reserved 38

    cakephp/src/Controller/ControllerFactory.php CakePHPでの実例 public function getControllerClass(ServerRequest $request): ?string { $pluginPath = ''; $namespace = 'Controller'; $controller = $request->getParam('controller', ''); if ($request->getParam('plugin')) { $pluginPath = $request->getParam('plugin') . '.'; } if ($request->getParam('prefix')) { $prefix = $request->getParam('prefix'); …… /** @var class-string<¥Cake¥Controller¥Controller>|null */ return App::className($pluginPath . $controller, $namespace, 'Controller'); }
  31. Confidential © 2018 for LANCERS, Inc. All Rights Reserved 39

    アプリケーション実⾏の際に様々なクラスで ServerRequestオブジェクトのプロパティか ら欲しい情報を取得している CakePHPでの実例
  32. Confidential © 2018 for LANCERS, Inc. All Rights Reserved 40

    必要な時にグローバル変数から 直接読み込めばいいんじゃないの︖
  33. Confidential © 2018 for LANCERS, Inc. All Rights Reserved 41

    スーパーグローバル変数の使⽤には デメリットがある
  34. Confidential © 2018 for LANCERS, Inc. All Rights Reserved 42

    スーパーグローバル変数 ・変更可能なので別の場所で書き換えられる可能性がある ・テストがしづらい ServerRequestオブジェクト ・不変であるので安⼼ ・状態を付加する場合はwith~関数を使って新しくインスタ ンスを⽣成する ・テストの時もインスタンス作るだけでOK CakePHPでの実例
  35. Confidential © 2018 for LANCERS, Inc. All Rights Reserved 44

    ・PSRとは共通の仕様を定めるための規約であり、 PSR-7ではリクエストとレスポンスの仕様を定めている ・RequestInterfaceではクライアントの送るリクエストを定義し、 ServerRequestInterfaceはサーバーが受け取るリクエストを定義する ・フレームワーク内はServerRequestはグローバル変数をもとに⽣成され、 様々なクラスで適切にそのクラスのプロパティが読み込まれている ・内部実装追いかけるの楽しい まとめ
  36. Confidential © 2018 for LANCERS, Inc. All Rights Reserved 46

    • HyperTextTransferProtcolの略 • TCPベースの通信プロトコル(それ⾃⾝はレイヤーで⾔うとアプ リケーション層のプロトコル) • HTTP リクエストとは、HTTPで指定されたフォーマットで記述 された(クライアント側からの)要求 • HTTP レスポンスとは、HTTPで指定されたフォーマットで記述 された(サーバー側からの)応答 • 上記をまとめてHTTPメッセージと⾔う HTTPとは
  37. Confidential © 2018 for LANCERS, Inc. All Rights Reserved 47

    • クラスが持つべきメソッドの名前や型の定義のみされている (すなわち構成が定義されている。以降その構成をまとめて 「型」と呼ぶ) • その型に従って(implementして)クラスを⽤意することを 「インターフェースを実装する」と⾔う • 同じインターフェースを実装している異なるクラスが作成でき、 それらが交換可能となる(なぜなら型が同じであるから) インターフェースとは