Slide 1

Slide 1 text

フレームワークの内部構造を 理解するためにフレームワークを 作ってみることにした PHP Conference Japan 2021 @jdkfx

Slide 2

Slide 2 text

質問などについて Discord #track2-1-a-build-framework にて 余裕を持ったトークにしておりますので,沢山質問などに答えて いきたいと思っております よろしくお願いします

Slide 3

Slide 3 text

スライドについて Discord,Twitterなどで事前に共有済みです コード部分などが小さくなってしまう可能性があるので 共有済みスライドも活用ください

Slide 4

Slide 4 text

田添春樹 広島工業大学 情報学部 平日は学生をしています 読書と映画と音楽を嗜んでます @jdkfx(Twitter,GitHub)

Slide 5

Slide 5 text

No content

Slide 6

Slide 6 text

本トークの内容 「自作phpフレームワーク」

Slide 7

Slide 7 text

No content

Slide 8

Slide 8 text

https://jdkfx.com/blog/building-a-php-framework

Slide 9

Slide 9 text

このトークで持ち帰ってほしいこと 技術に対するネガティブな感情などを感じてしまったときは 一度何もない状態に立ち戻り,組み立てつつ学習を進めてみる

Slide 10

Slide 10 text

このトークで語らないこと MVCのどこが素晴らしいか(もしくはその反対) フレームワークの内部構造についての解説 オブジェクト指向の有用性(もしくはその反対)

Slide 11

Slide 11 text

アジェンダ 1. 動機と目的 2. 理解と実装 3. まとめ

Slide 12

Slide 12 text

アジェンダ 1. 動機と目的 2. 理解と実装 3. まとめ

Slide 13

Slide 13 text

フレームワークを作る前 普段はLaravelのアプリケーションや簡単なスクリプトを書く程度

Slide 14

Slide 14 text

フレームワークを作る前 コントローラーに任意の処理を書くだけで,ソースコードまで 見ることは少ない

Slide 15

Slide 15 text

フレームワークを作る前 フルスクラッチでの開発経験はなく,既存のコードを触る程度

Slide 16

Slide 16 text

フレームワークを作る前 簡単なスクリプトを書くことや,仕事で関わった案件の レガシーなソースコードの簡単な修正など

Slide 17

Slide 17 text

フレームワークを作る前 オブジェクト指向や大量のソースコードを見ることに対する恐怖感

Slide 18

Slide 18 text

フレームワークを作る前 ソースコードを見るといっても何処から見ていいか分からない そのため,闇雲にソースコードを行き来して迷子になることが多い

Slide 19

Slide 19 text

闇雲に読んでも到底理解できるはずもなく, 自身のフレームワークに対する理解度や ソースコードを読む力,技術力の不足に気が付く

Slide 20

Slide 20 text

そこで,イチから自作フレームワークを作り, ソースコードを読む力,技術力の不足を補うことを考える

Slide 21

Slide 21 text

動機 フレームワークを利用するうえで内部構造について理解するため

Slide 22

Slide 22 text

動機 フレームワークの内部構造やどんな仕組みで処理を行っているか 知ることでフレームワークに対する考え方が変わると考えた

Slide 23

Slide 23 text

目的Ⅰ オブジェクト指向への理解を深めるため

Slide 24

Slide 24 text

目的Ⅰ オブジェクト指向についてなんとなく理解はしているが 実際に多用する場面がなかったため,その壁打ちとして

Slide 25

Slide 25 text

目的Ⅱ 自身のphpを扱う力をもっと養うため

Slide 26

Slide 26 text

目的Ⅱ 自身の技量を試す教材としては丁度いい

Slide 27

Slide 27 text

アジェンダ 1. 動機と目的 2. 理解と実装 3. まとめ

Slide 28

Slide 28 text

実装する構成について モデル,ビュー,コントローラを持つもの 将来,個人のプロダクトに用いる前提

Slide 29

Slide 29 text

https://speakerdeck.com/tenjuu99/what-mvc-is

Slide 30

Slide 30 text

モデル 選び抜かれてシンプルにされ, 意図的に組み立てられた知識の表現形式 個人の持つメンタルモデルが概念モデルを経て, ドメインモデルへと変換されたもの ドメインモデルとはドメイン(目的)を 情報処理システム(手段)によってモデル化したもの

Slide 31

Slide 31 text

ビュー 表示部分やフォームなどの入出力を担当 モデルに保存されているデータを表示

Slide 32

Slide 32 text

コントローラ モデルとビューの間で仲介役のような役割を行う モデルにリクエストされたデータを受け渡し, レスポンスされたデータをビューに受け渡す

Slide 33

Slide 33 text

https://speakerdeck.com/uzulla/gui-tutekita-ping-cheng-zui-hou-falseoreorehuremuwakufalsezuo-rifang

Slide 34

Slide 34 text

実装過程 モデルとindex.phpの実装 コントローラの実装 POSTの受け取り ビューの実装 ルーティングの実装 受け取ったデータの保存

Slide 35

Slide 35 text

モデルとindex.phpの実装 if (file_exists('./models/' . $contents . '.php')) { include('./models/' . $contents . '.php'); $className = "models¥¥" . $contents; $class = new $className(); if ($_SERVER['REQUEST_METHOD'] == "GET") { $response = $class->index($contents); if (!is_null($response)) { if(is_array($response)){ extract($response); } } } } index.phpの抜粋

Slide 36

Slide 36 text

モデルとindex.phpの実装 namespace models; class Blog { public function index($params) : array { return array('str' => 'My name is jdkfx.'); } } Models/Blog.phpの抜粋

Slide 37

Slide 37 text

モデルとindex.phpの実装 index.phpをコントローラとして担当させる ‘/blog’というパスにアクセスするとBlogモデルがあるか確認し モデルに記述された内容を返すような仕様 GETしか受け取ることができない

Slide 38

Slide 38 text

コントローラの実装 class BlogController { public function __construct(){} public function index() { $blog = new blog(); $response = $blog->index(); if (!is_null($response)) { if(is_array($response)){ extract($response); } } return $response; } public function create() { return “新しい記事を作成するページです.”; } } Controllers/BlogController.phpの抜粋

Slide 39

Slide 39 text

コントローラの実装 if (file_exists('./controllers/' . $fullpath[1] . '_controller.php')) { include('./controllers/' . $fullpath[1] . '_controller.php'); $conName = "controllers¥¥" . $fullpath[1] . "_controller"; $con = new $conName(); if (empty($fullpath[2])) { $response = $con->index(); if (file_exists('./views/' . $contents . '.php')) { include('./views/' . $contents . '.php'); } } else { $conFunc = $fullpath[2]; if (method_exists($con, $conFunc)) { $response = $con->$conFunc(); echo $response; } else { include('./views/error.php'); } } } src/index.phpの抜粋

Slide 40

Slide 40 text

コントローラの実装 URLに記述された内容からモデルとコントローラを呼び出す 例: ‘/blog’にアクセスするとブログ一覧ページを表示 ‘/blog/create’にアクセスするとブログ作成ページを表示

Slide 41

Slide 41 text

POSTの受け取り if (file_exists('./controllers/' . $fullpath[1] . '_controller.php')) { include('./controllers/' . $fullpath[1] . '_controller.php’); // http://localhost/blog/ などの場合 if (empty($fullpath[2])) { $response = $con->index(); if (file_exists('./views/' . $contents . '.php')) { include('./views/' . $contents . '.php'); } // http://localhost/blog/create などの場合かつ POST される場合 } else if ($_SERVER['REQUEST_METHOD'] == 'POST') { $conFunc = $fullpath[2]; $request = $_POST; if (method_exists($con, $conFunc)) { $response = $con->post($request); } else { include('./views/error.php'); } // 次スライドに続く src/index.phpの抜粋

Slide 42

Slide 42 text

POSTの受け取り // 前スライドの続き // http://localhost/blog/create などの場合 } else { $conFunc = $fullpath[2]; if (method_exists($con, $conFunc)) { $response = $con->$conFunc(); } else { include('./views/error.php'); } } } src/index.phpの抜粋

Slide 43

Slide 43 text

POSTの受け取り $_SERVER[‘REQUEST_METHOD‘] でPOSTであれば $_POSTの内容を受け取る

Slide 44

Slide 44 text

ビューの実装 class View { public function __construct(){} public function pages($filename, $dvalue = null) { $response = $dvalue; include __DIR__ . "/../Views/" . $filename . ".php"; } } Templates/View.phpの抜粋

Slide 45

Slide 45 text

ビューの実装 URLに記述された内容からそれぞれのビューファイルを呼び出す 例: ‘/blog’にアクセスすると blog.phpの内容を表示 ‘/blog/create’にアクセスするとcreate.phpの内容を表示

Slide 46

Slide 46 text

ルーティングの実装 class Router { // 省略 public function response($request) { try { $controllerName = "App¥¥Controllers¥¥" . $this->routes[$request]['controller']; $controller = new $controllerName(); $controllerAction = $this->routes[$request]['action']; if ($this->routes[$request]['method'] === 'GET') { $controller->$controllerAction(); } else if ($this->routes[$request]['method'] === 'POST') { $controller->$controllerAction($_POST); } else { throw new ¥Exception('error!'); } } catch (¥Exception $e) { error_log($e->getFile() . $e->getLine() . $e->getMessage()); } } } Routers/Router.phpの抜粋

Slide 47

Slide 47 text

ルーティングの実装 $pattern = [ '/' => [ 'method' => 'GET', 'controller' => 'HomeController', 'action' => 'index', ], '/blog' => [ 'method' => 'GET', 'controller' => 'BlogController', 'action' => 'index', ], // 省略 ]; $router = new Router($pattern); $router->response($_SERVER['REQUEST_URI']); html/index.phpの抜粋

Slide 48

Slide 48 text

ルーティングの実装 $pattern配列にmethod,controller,actionを定義し, ルーティングを行うように実装 index.phpで行っていたコントローラなどの読み込み処理を Routerクラスで行うように置き換えて実装

Slide 49

Slide 49 text

受け取ったデータの保存 class model { public $pdo; public function __construct() { $dsn = 'mysql:dbname=blog;host=phrame_mysql_1'; $user = 'root'; $password = 'password'; $query = "CREATE TABLE IF NOT EXISTS blog.posts ( id INT(11) NOT NULL auto_increment PRIMARY KEY, title VARCHAR(20), messages VARCHAR(50) ) DEFAULT CHARSET=utf8"; // 次ページに続く Models/Model.phpの抜粋

Slide 50

Slide 50 text

受け取ったデータの保存 // 前ページの続き try { $this->pdo = new PDO($dsn, $user, $password); $this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); $this->pdo->query($query); } catch (PDOException $e) { error_log($e->getMessage()); exit(); } } } Models/Model.phpの抜粋

Slide 51

Slide 51 text

受け取ったデータの保存 Model.phpのコンストラクタでMySQLへの接続を行い テーブルを作成する Model内でblogなど,すべてのコンテンツのモデルに関する テーブルを作成しているので将来的なことを考えると改善点

Slide 52

Slide 52 text

受け取ったデータの保存 public function index() : array { $query = "SELECT * FROM blog.posts"; try { $stmt = $this->pdo->query($query); $response = $stmt->fetchAll(PDO::FETCH_ASSOC); } catch (Exception $e) { error_log($e->getMessage()); exit(); } return $response; } Models/Blog.phpの抜粋

Slide 53

Slide 53 text

受け取ったデータの保存 public function store($request) { $query = "INSERT INTO blog.posts (title, messages) VALUES (?, ?)"; try { $stmt = $this->pdo->prepare($query); $response = $stmt->execute(array($request["title"], $request["messages"])); } catch (Exception $e) { error_log($e->getMessage()); exit(); } } Models/Blog.phpの抜粋

Slide 54

Slide 54 text

受け取ったデータの保存 blogなど,コンテンツのモデル内でクエリを実行し, 表示やデータの保存を行う

Slide 55

Slide 55 text

全体の構造について 責務をそれぞれに 分担させることが可能に

Slide 56

Slide 56 text

アジェンダ 1. 動機と目的 2. 理解と実装 3. まとめ

Slide 57

Slide 57 text

フレームワークを自作してみて MVCのフレームワークの内部構造についての理解が深くなる フレームワークのソースコードなどに対する恐怖感を取り払える オブジェクト指向について断片的ではあるが,理解が深くなる

Slide 58

Slide 58 text

内部構造についての理解 今回実装した内容は断片的で,かつ,初心者が悩みながら 作ったものではあるが,MVCというアーキテクチャとそれに準拠 したフレームワークの開発について内部構造とともに理解を 深めながら開発を行った

Slide 59

Slide 59 text

ソースコードに対する恐怖感 どのコードを読めばいいのか分からないという問題が よく発生していたが,継承元であるコードや呼ばれている コードなど,「phpでの呼び出しのお作法」を知識として得ることが 出来たのでソースコードに対する恐怖感は前よりも感じられない

Slide 60

Slide 60 text

ソースコードに対する恐怖感 ドキュメントを読んでも分からなければ,実装を見ればよいという 助言をもとに実装を読むようになり,一層ソースコードを読む 重要性が感じられるようになる

Slide 61

Slide 61 text

実装をする中で感じたこと ソースコードを読むことができず,ネガティブな感情を抱いていた 時もあったが,そういった感情などを感じてしまったときは 一度何もない状態に立ち戻り,組み立てつつ学習を進めてみる ことで体系的に,かつ,自分の理解度に合わせて学習を進められた

Slide 62

Slide 62 text

再掲:このトークで持ち帰ってほしいこと 技術に対するネガティブな感情などを感じてしまったときは 一度何もない状態に立ち戻り,組み立てつつ学習を進めてみる

Slide 63

Slide 63 text

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

Slide 64

Slide 64 text

引用 MVCとはなにか/What MVC is https://speakerdeck.com/tenjuu99/what-mvc-is 帰ってきた!平成最後のオレオレフレームワークの作り方 https://speakerdeck.com/uzulla/gui-tutekita-ping- cheng-zui-hou-falseoreorehuremuwakufalsezuo- rifang

Slide 65

Slide 65 text

質問などありましたらお願いします!