Slide 1

Slide 1 text

$BLF1)1ͷ಺෦࣮૷ ͔Βཧղ͢Δ143

Slide 2

Slide 2 text

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

Slide 3

Slide 3 text

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']); …… } }

Slide 4

Slide 4 text

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']); …… } }

Slide 5

Slide 5 text

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として規定

Slide 6

Slide 6 text

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

Slide 7

Slide 7 text

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

Slide 8

Slide 8 text

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

Slide 9

Slide 9 text

143ͬͯʁ

Slide 10

Slide 10 text

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

Slide 11

Slide 11 text

Confidential © 2018 for LANCERS, Inc. All Rights Reserved 11

Slide 12

Slide 12 text

Confidential © 2018 for LANCERS, Inc. All Rights Reserved 12

Slide 13

Slide 13 text

Confidential © 2018 for LANCERS, Inc. All Rights Reserved 13

Slide 14

Slide 14 text

Confidential © 2018 for LANCERS, Inc. All Rights Reserved 14

Slide 15

Slide 15 text

143ͷओு

Slide 16

Slide 16 text

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

Slide 17

Slide 17 text

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

Slide 18

Slide 18 text

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

Slide 19

Slide 19 text

$BLFͷ಺෦࣮૷ΛݟͯΈΔ 3FRVFTUฤ

Slide 20

Slide 20 text

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

Slide 21

Slide 21 text

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

Slide 22

Slide 22 text

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

Slide 23

Slide 23 text

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

Slide 24

Slide 24 text

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

Slide 25

Slide 25 text

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

Slide 26

Slide 26 text

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

Slide 27

Slide 27 text

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

Slide 28

Slide 28 text

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

Slide 29

Slide 29 text

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

Slide 30

Slide 30 text

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

Slide 31

Slide 31 text

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

Slide 32

Slide 32 text

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

Slide 33

Slide 33 text

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

Slide 34

Slide 34 text

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

Slide 35

Slide 35 text

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

Slide 36

Slide 36 text

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

Slide 37

Slide 37 text

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

Slide 38

Slide 38 text

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

Slide 39

Slide 39 text

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

Slide 40

Slide 40 text

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

Slide 41

Slide 41 text

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

Slide 42

Slide 42 text

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

Slide 43

Slide 43 text

࠷ޙʹ

Slide 44

Slide 44 text

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

Slide 45

Slide 45 text

Confidential © 2018 for LANCERS, Inc. All Rights Reserved 45 ありがとうございました︕

Slide 46

Slide 46 text

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

Slide 47

Slide 47 text

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