Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
フレームワークの内部構造を理解するためにフレームワークを作ってみることにした / phpc...
Search
Haruki Tazoe
October 02, 2021
1.3k
2
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
フレームワークの内部構造を 理解するためにフレームワークを 作ってみることにした / phpcon-2021
Haruki Tazoe
October 02, 2021
More Decks by Haruki Tazoe
See All by Haruki Tazoe
クロスアカウント環境におけるAWS S3バケットへのアクセスで詰まっていた
jdkfx
0
76
PHPのプロセス制御について
jdkfx
0
64
ちょっとした「翻訳ブーム」はこうして始まった
jdkfx
0
380
フロントエンドのチューニングに挑戦してみる
jdkfx
0
190
ドキュメント翻訳で学ぶ新しい言語仕様・機能
jdkfx
1
470
Featured
See All Featured
Connecting the Dots Between Site Speed, User Experience & Your Business [WebExpo 2025]
tammyeverts
11
940
Reflections from 52 weeks, 52 projects
jeffersonlam
356
21k
Exploring anti-patterns in Rails
aemeredith
3
400
Keith and Marios Guide to Fast Websites
keithpitt
413
23k
The Web Performance Landscape in 2024 [PerfNow 2024]
tammyeverts
12
1.2k
Bridging the Design Gap: How Collaborative Modelling removes blockers to flow between stakeholders and teams @FastFlow conf
baasie
0
580
Jess Joyce - The Pitfalls of Following Frameworks
techseoconnect
PRO
1
160
Mobile First: as difficult as doing things right
swwweet
225
10k
Lessons Learnt from Crawling 1000+ Websites
charlesmeaden
PRO
1
1.3k
Why Our Code Smells
bkeepers
PRO
340
58k
Balancing Empowerment & Direction
lara
6
1.1k
Designing for Performance
lara
611
70k
Transcript
フレームワークの内部構造を 理解するためにフレームワークを 作ってみることにした PHP Conference Japan 2021 @jdkfx
質問などについて Discord #track2-1-a-build-framework にて 余裕を持ったトークにしておりますので,沢山質問などに答えて いきたいと思っております よろしくお願いします
スライドについて Discord,Twitterなどで事前に共有済みです コード部分などが小さくなってしまう可能性があるので 共有済みスライドも活用ください
田添春樹 広島工業大学 情報学部 平日は学生をしています 読書と映画と音楽を嗜んでます @jdkfx(Twitter,GitHub)
None
本トークの内容 「自作phpフレームワーク」
None
https://jdkfx.com/blog/building-a-php-framework
このトークで持ち帰ってほしいこと 技術に対するネガティブな感情などを感じてしまったときは 一度何もない状態に立ち戻り,組み立てつつ学習を進めてみる
このトークで語らないこと MVCのどこが素晴らしいか(もしくはその反対) フレームワークの内部構造についての解説 オブジェクト指向の有用性(もしくはその反対)
アジェンダ 1. 動機と目的 2. 理解と実装 3. まとめ
アジェンダ 1. 動機と目的 2. 理解と実装 3. まとめ
フレームワークを作る前 普段はLaravelのアプリケーションや簡単なスクリプトを書く程度
フレームワークを作る前 コントローラーに任意の処理を書くだけで,ソースコードまで 見ることは少ない
フレームワークを作る前 フルスクラッチでの開発経験はなく,既存のコードを触る程度
フレームワークを作る前 簡単なスクリプトを書くことや,仕事で関わった案件の レガシーなソースコードの簡単な修正など
フレームワークを作る前 オブジェクト指向や大量のソースコードを見ることに対する恐怖感
フレームワークを作る前 ソースコードを見るといっても何処から見ていいか分からない そのため,闇雲にソースコードを行き来して迷子になることが多い
闇雲に読んでも到底理解できるはずもなく, 自身のフレームワークに対する理解度や ソースコードを読む力,技術力の不足に気が付く
そこで,イチから自作フレームワークを作り, ソースコードを読む力,技術力の不足を補うことを考える
動機 フレームワークを利用するうえで内部構造について理解するため
動機 フレームワークの内部構造やどんな仕組みで処理を行っているか 知ることでフレームワークに対する考え方が変わると考えた
目的Ⅰ オブジェクト指向への理解を深めるため
目的Ⅰ オブジェクト指向についてなんとなく理解はしているが 実際に多用する場面がなかったため,その壁打ちとして
目的Ⅱ 自身のphpを扱う力をもっと養うため
目的Ⅱ 自身の技量を試す教材としては丁度いい
アジェンダ 1. 動機と目的 2. 理解と実装 3. まとめ
実装する構成について モデル,ビュー,コントローラを持つもの 将来,個人のプロダクトに用いる前提
https://speakerdeck.com/tenjuu99/what-mvc-is
モデル 選び抜かれてシンプルにされ, 意図的に組み立てられた知識の表現形式 個人の持つメンタルモデルが概念モデルを経て, ドメインモデルへと変換されたもの ドメインモデルとはドメイン(目的)を 情報処理システム(手段)によってモデル化したもの
ビュー 表示部分やフォームなどの入出力を担当 モデルに保存されているデータを表示
コントローラ モデルとビューの間で仲介役のような役割を行う モデルにリクエストされたデータを受け渡し, レスポンスされたデータをビューに受け渡す
https://speakerdeck.com/uzulla/gui-tutekita-ping-cheng-zui-hou-falseoreorehuremuwakufalsezuo-rifang
実装過程 モデルとindex.phpの実装 コントローラの実装 POSTの受け取り ビューの実装 ルーティングの実装 受け取ったデータの保存
モデルと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の抜粋
モデルとindex.phpの実装 namespace models; class Blog { public function index($params) :
array { return array('str' => 'My name is jdkfx.'); } } Models/Blog.phpの抜粋
モデルとindex.phpの実装 index.phpをコントローラとして担当させる ‘/blog’というパスにアクセスするとBlogモデルがあるか確認し モデルに記述された内容を返すような仕様 GETしか受け取ることができない
コントローラの実装 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の抜粋
コントローラの実装 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の抜粋
コントローラの実装 URLに記述された内容からモデルとコントローラを呼び出す 例: ‘/blog’にアクセスするとブログ一覧ページを表示 ‘/blog/create’にアクセスするとブログ作成ページを表示
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の抜粋
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の抜粋
POSTの受け取り $_SERVER[‘REQUEST_METHOD‘] でPOSTであれば $_POSTの内容を受け取る
ビューの実装 class View { public function __construct(){} public function pages($filename,
$dvalue = null) { $response = $dvalue; include __DIR__ . "/../Views/" . $filename . ".php"; } } Templates/View.phpの抜粋
ビューの実装 URLに記述された内容からそれぞれのビューファイルを呼び出す 例: ‘/blog’にアクセスすると blog.phpの内容を表示 ‘/blog/create’にアクセスするとcreate.phpの内容を表示
ルーティングの実装 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の抜粋
ルーティングの実装 $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の抜粋
ルーティングの実装 $pattern配列にmethod,controller,actionを定義し, ルーティングを行うように実装 index.phpで行っていたコントローラなどの読み込み処理を Routerクラスで行うように置き換えて実装
受け取ったデータの保存 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の抜粋
受け取ったデータの保存 // 前ページの続き 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の抜粋
受け取ったデータの保存 Model.phpのコンストラクタでMySQLへの接続を行い テーブルを作成する Model内でblogなど,すべてのコンテンツのモデルに関する テーブルを作成しているので将来的なことを考えると改善点
受け取ったデータの保存 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の抜粋
受け取ったデータの保存 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の抜粋
受け取ったデータの保存 blogなど,コンテンツのモデル内でクエリを実行し, 表示やデータの保存を行う
全体の構造について 責務をそれぞれに 分担させることが可能に
アジェンダ 1. 動機と目的 2. 理解と実装 3. まとめ
フレームワークを自作してみて MVCのフレームワークの内部構造についての理解が深くなる フレームワークのソースコードなどに対する恐怖感を取り払える オブジェクト指向について断片的ではあるが,理解が深くなる
内部構造についての理解 今回実装した内容は断片的で,かつ,初心者が悩みながら 作ったものではあるが,MVCというアーキテクチャとそれに準拠 したフレームワークの開発について内部構造とともに理解を 深めながら開発を行った
ソースコードに対する恐怖感 どのコードを読めばいいのか分からないという問題が よく発生していたが,継承元であるコードや呼ばれている コードなど,「phpでの呼び出しのお作法」を知識として得ることが 出来たのでソースコードに対する恐怖感は前よりも感じられない
ソースコードに対する恐怖感 ドキュメントを読んでも分からなければ,実装を見ればよいという 助言をもとに実装を読むようになり,一層ソースコードを読む 重要性が感じられるようになる
実装をする中で感じたこと ソースコードを読むことができず,ネガティブな感情を抱いていた 時もあったが,そういった感情などを感じてしまったときは 一度何もない状態に立ち戻り,組み立てつつ学習を進めてみる ことで体系的に,かつ,自分の理解度に合わせて学習を進められた
再掲:このトークで持ち帰ってほしいこと 技術に対するネガティブな感情などを感じてしまったときは 一度何もない状態に立ち戻り,組み立てつつ学習を進めてみる
ご清聴ありがとうございました!
引用 MVCとはなにか/What MVC is https://speakerdeck.com/tenjuu99/what-mvc-is 帰ってきた!平成最後のオレオレフレームワークの作り方 https://speakerdeck.com/uzulla/gui-tutekita-ping- cheng-zui-hou-falseoreorehuremuwakufalsezuo- rifang
質問などありましたらお願いします!