Slide 1

Slide 1 text

百科事典の責務分割 PIXIV SPRING BOOT CAMP 2022 技術基盤(Webフレームワーク) pixiv Inc. usuyuki 2022.3.17

Slide 2

Slide 2 text

2 自己紹介 ● アイコンの背景色とpixivのカラーが似ている ● 鳥取県米子市生、島根県浜田市育ち ● コーヒーが好き ● しぐれうい先生を推しています (イラストレーターとしても、VTuberとしても) ● なんちゃってPHPer ○ Laravelをよく使います ● 心ばかりの ○ 自然言語処理 ○ インフラ環境構築 ○ JSやPythonなど usuyuki 宇都宮大学

Slide 3

Slide 3 text

3 ピクシブ百科事典の 改善 メンターの tadsan インターンの usuyuki usuyukiの力上昇 メンターのtadsan

Slide 4

Slide 4 text

‘ピクシブ百科事典はアニメやマンガ、ゲームからデザイン・アートまで あらゆる言葉・現象・文化・作品を解説するみんなでつくる百科事典です。 ’ 4 ピクシブ百科事典 記事数 42万 ※日本語版 月間PV 1億 ※日本語版

Slide 5

Slide 5 text

2009年来からのPHP製 独自フレームワーク 5 ピクシブ百科事典

Slide 6

Slide 6 text

6 8日間で行ったこと 大きく分けると2つ

Slide 7

Slide 7 text

7 8日間で行ったこと ※イメージ PHP フレームワーク アプリケーション

Slide 8

Slide 8 text

8 8日間で行ったこと ※イメージ PHP フレームワーク アプリケーション ルーティングを middlewareへ

Slide 9

Slide 9 text

9 8日間で行ったこと ※イメージ PHP フレームワーク アプリケーション /mypageのADR化

Slide 10

Slide 10 text

10 フレームワークのお話 ※イメージ PHP フレームワーク アプリケーション

Slide 11

Slide 11 text

11 やったこと ルーティングをmiddlewareへ

Slide 12

Slide 12 text

12 全体の流れ ページの閲覧 index.php 種々のミドルウェア Dispatcher ControllerまたはAction Model tplファイル ● 田代砲やNGワードを弾く ● HTTPSへのリダイレクト ● セッションの初期化

Slide 13

Slide 13 text

13 全体の流れ ページの閲覧 index.php 種々のミドルウェア Dispatcher ControllerまたはAction Model tplファイル URLを元に対応するクラスやメ ソッドを引っ張り出す

Slide 14

Slide 14 text

14 全体の流れ ページの閲覧 index.php 種々のミドルウェア Dispatcher ControllerまたはAction Model tplファイル 実際の処理 (記事の登録や呼び出し )

Slide 15

Slide 15 text

15 全体の流れ ページの閲覧 index.php 種々のミドルウェア Dispatcher ControllerまたはAction Model tplファイル

Slide 16

Slide 16 text

16 何が嬉しいの? 外部依存削減 & 責務分割

Slide 17

Slide 17 text

17 外部依存削減 外部依存削減

Slide 18

Slide 18 text

18 外部依存削減 その前に

Slide 19

Slide 19 text

19 PSR PSR

Slide 20

Slide 20 text

20 PSR PHP Standard Recommendationd PSR

Slide 21

Slide 21 text

21 PSR ピクシブ百科事典はPSR準拠に

Slide 22

Slide 22 text

22 PSR-7 HTTP Message Interface リクエストやレスポンスの インターフェイスなどが定義されている!

Slide 23

Slide 23 text

23 PSR-7 HTTP Message Interface そしてイミュータブル(不変)!

Slide 24

Slide 24 text

//どこかのファイル $_POST[‘title’] = ”任意の値”; PHPのスーパーグローバル変数 24 //どこかのファイル $title = $_POST[‘title’];//何が来るか分からない... 値の代入(任意の場所で任意の回数) 値の取り出し(グローバル変数)

Slide 25

Slide 25 text

$request = $request->withAttribute(RouteDispatchHandler::class, $new_handler); PSR-7準拠 25 public function handle(ServerRequestInterface $request) { $request->getAttribute(RouteDispatchHandler::class, $new_handler); 値の代入(1回限り) 値の取り出し(引数より頂戴できる)

Slide 26

Slide 26 text

26 こうすることで... これまで これから session_user()//関数内で$_SESSION['user']が呼ばれる public function hanlde(ServerRequestInterface $request) { //中略 $request->getAttribute(‘user’)

Slide 27

Slide 27 text

27 外部依存削減 外部依存削減

Slide 28

Slide 28 text

28 責務分割 責務分割

Slide 29

Slide 29 text

29 Dispatcher これまで ● httpならリダイレクト、セッションの初期化、 ● urlから対応づくControllerやActionのクラスとメソッドを見つける ● 問題のあるタイトルや意図しないものを弾く ● 該当するクラスのメソッドを呼び出す ● 該当するクラスのメソッドを呼び出す これから

Slide 30

Slide 30 text

30 PSR-15 HTTP Handlers middlewareやhandlerに関しての インターフェイス

Slide 31

Slide 31 text

class RoutingMiddleware implements MiddlewareInterface { インターフェイスを具象化したものを実装 31 class IndexAction implements RequestHandlerInterface { Mypage/IndexAction RoutingMiddleware

Slide 32

Slide 32 text

32 PSR-15 middleware middleware HTTPリクエスト HTTPレスポンス

Slide 33

Slide 33 text

33 PSR-15 middleware middleware HTTPリクエスト HTTPレスポンス

Slide 34

Slide 34 text

34 責務分割 middlewareへ切り出した

Slide 35

Slide 35 text

35 従来 https://dic.pixiv.net/a/%E3%81%97%E3%81%90%E3%82%8C%E3%81%86%E3%81%84 dispatcher RouteConfigPC URLに対してどれを 呼び出すかが列挙されている 実際の処理(Controller,Action) を呼び出す! 種々のmiddleware

Slide 36

Slide 36 text

36 今回の実装 https://dic.pixiv.net/a/%E3%81%97%E3%81%90%E3%82%8C%E3%81%86%E3%81%84 dispatcher RouteConfigPC URLに対してどれを 呼び出すかが列挙されている RoutingMiddleware 実際の処理を呼び出す RouteDispatchHandler 該当する処理を見つけ出し、 RouteDispatchHandlerを$request に包む 実際の処理を準備する 種々のmiddleware 種々のmiddleware

Slide 37

Slide 37 text

//色々な処理... $new_handler = new RouteDispatchHandler($route, $controller, $this->response_factory, $this->stream_factory, $this->system_clock, $this->views_dir); $request = $request->withAttribute(RouteDispatchHandler::class, $new_handler); return $handler->handle($request); RoutingMiddleware 37

Slide 38

Slide 38 text

//色々な処理... $handler = $request->getAttribute(RouteDispatchHandler::class); assert($handler instanceof RouteDispatchHandler); return $handler->handle($request); Dispatcher 38

Slide 39

Slide 39 text

//色々な処理... $handler = $request->getAttribute(RouteDispatchHandler::class); assert($handler instanceof RouteDispatchHandler); return $handler->handle($request); Dispatcher 39

Slide 40

Slide 40 text

Dispatcherの責務を減らす 40 237行 80行

Slide 41

Slide 41 text

41 PSR middleware HTTPリクエスト HTTPレスポンス PSR-7 ServerRequestInterface PSR-7 ResponseInterface PSR-15 RequestHandlerInterface PSR-15 MiddlewareInterface

Slide 42

Slide 42 text

42 テストが書ける ● 途中に謎の要素を取り込まない! ● 責務が分割されている 単体で完結してるので テストが書ける!

Slide 43

Slide 43 text

43 アプリケーションのお話 ※イメージ PHP フレームワーク アプリケーション

Slide 44

Slide 44 text

44 やったこと /mypageのADR(風味)化

Slide 45

Slide 45 text

45 MVC Model View Controller レスポンス リクエスト

Slide 46

Slide 46 text

46 ADR Domain Responder Action レスポンス リクエスト HTTPレスポンス作成 メソッド1つのみ (今回はDomain風味)

Slide 47

Slide 47 text

if (!is_login()) { return $this->createRedirectResponse('/login', ['return_to' => (string)$this->request->getUri()]); } $user = session_user(); $this->set('user', $user); [$checklist, $checklist_count] = ChecklistModel::findByUserIDWithArticle($user->user_id, 0, 5); //続く... 47 旧:UserController mypageメソッド

Slide 48

Slide 48 text

public function handle(ServerRequestInterface $request): ResponseInterface { $user = $request->getAttribute('user'); if ($user instanceof NotLoggedIn) { return $this->responder->redirectForLogin((string)$request->getUri()); } //中略 return $this->responder->emitHtml($checklist, $created_articles, $activity, $access, $related_articles); 新:Mypage/IndexAction handleメソッド 48

Slide 49

Slide 49 text

public function emitHtml(array $checklist, array $created_articles, array $activity, array $access, array $related_articles): ResponseInterface { $html = $this->html_factory->render($this->views_dir . '/user/mypage', [ 'checklist' => $checklist, //略 ]); return $this->response_factory->createResponse(200) ->withBody($this->stream_factory->createStream($html)); } 新:Mypage/IndexResponder 49

Slide 50

Slide 50 text

50 DI Mypage/IndexActionでResponderをDI! 返し方をActionは知らなくて良い ※抽象に依存という点では厳密には不十分

Slide 51

Slide 51 text

/** @var IndexResponder */ private $responder; public function __construct(IndexResponder $responder) { $this->responder = $responder; } Mypage/IndexAction のコンストラクタ 51

Slide 52

Slide 52 text

public function emitHtml(array $checklist, array $created_articles, array $activity, array $access, array $related_articles): ResponseInterface { $html = $this->html_factory->render($this->views_dir . '/user/mypage', [ 'checklist' => $checklist, //略 ]); return $this->response_factory->createResponse(200) ->withBody($this->stream_factory->createStream($html)); } 新:Mypage/IndexResponder 52

Slide 53

Slide 53 text

public function handle(ServerRequestInterface $request): ResponseInterface { $user = $request->getAttribute('user'); if ($user instanceof NotLoggedIn) { return $this->responder->redirectForLogin((string)$request->getUri()); } //中略 return $this->responder->emitHtml($checklist, $created_articles, $activity, $access, $related_articles); Mypage/IndexAction handleメソッド 53

Slide 54

Slide 54 text

54 まとめ ● ルーティング周りのmiddleware移行(PSR準拠) ● /mypageのADR(風味)化 ● これらのテストを作成

Slide 55

Slide 55 text

55 書ききれなかったけど学んだこと…… ● PHPStanでの静的解析 ● PHPDocの威力 ○ ジェネリックス ● テストの疎結合 ● staticMock ● autowire ● git周りの知見 ● PSRの仕様に関して ● 名前空間 ● オートロード ● PHPの関数呼び出し順による最適化 ● クリーンとイージーに関して ● interface,trait,abstract ● 安定度抽象度等価原則に関して ● デプロイ環境に関して などなど...

Slide 56

Slide 56 text

56 まとめ この春  ピクシブで   圧倒的猛者になりました。

Slide 57

Slide 57 text

57 謝辞 tadsanさん、kamikoさん pixiv事業本部webエンジニアリングチームの皆様、 障害対応してくださった皆様、 インターンの手筈整えてくださった皆様、 充実した8日間をありがとうございました。

Slide 58

Slide 58 text

58 ピクシブ百科事典の 改善 メンターの tadsan インターンの usuyuki usuyukiの力上昇 全体を通して

Slide 59

Slide 59 text

59 ピクシブ百科事典の 改善 メンターの tadsan インターンの usuyuki usuyukiの力上昇 全体を通して

Slide 60

Slide 60 text

60 おわり ご清聴ありがとうございました!