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
$nl ~僕が見た”最高”のPHPコード~
Search
kokuyouwind
April 16, 2016
Programming
0
36
$nl ~僕が見た”最高”のPHPコード~
NL名古屋
で発表したときのスライドです。
本当は10分の発表枠だったんですが、ぶっちぎりでオーバーした挙句予備スライドまで話しました。
内容については…お察し下さい。
kokuyouwind
April 16, 2016
Tweet
Share
More Decks by kokuyouwind
See All by kokuyouwind
Let's use LLMs from Ruby 〜 Refine RBS types using LLM 〜
kokuyouwind
0
6.2k
APMをちゃんと使おうとしたら、いつのまにか独自gemを作っていた話
kokuyouwind
0
730
RBS meets LLMs - Type inference using LLM
kokuyouwind
0
800
オンラインボードゲームを作りたい人生だった
kokuyouwind
0
470
1年間本番運用してわかった、スタートアップこそAWS Copilot CLIを使うべきNつの理由
kokuyouwind
2
11k
なるべく楽したいAWSセキュリティ
kokuyouwind
1
64
Railsパフォーマンス・チューニング入門
kokuyouwind
0
270
Rubyパターンマッチに闇の力が備わり最強に見える
kokuyouwind
0
93
Slackワークフロー活用術
kokuyouwind
0
93
Other Decks in Programming
See All in Programming
はじめての Go * WASM * OCR
sgash708
1
130
良いコードレビューとは
danimal141
10
9.2k
Jakarta EE meets AI
ivargrimstad
0
780
複数のAWSアカウントから横断で 利用する Lambda Authorizer の作り方
tc3jp
0
130
React 19アップデートのために必要なこと
uhyo
8
1.6k
From the Wild into the Clouds - Laravel Meetup Talk
neverything
0
190
PEPCは何を変えようとしていたのか
ken7253
3
320
機能が複雑化しても 頼りになる FactoryBotの話
tamikof
1
260
Swift Testingのモチベを上げたい
stoticdev
2
220
「個人開発マネタイズ大全」が教えてくれたこと
bani24884
1
310
Amazon Bedrockマルチエージェントコラボレーションを諦めてLangGraphに入門してみた
akihisaikeda
1
180
5分で理解する SOLID 原則 #phpcon_nagoya
shogogg
1
430
Featured
See All Featured
StorybookのUI Testing Handbookを読んだ
zakiyama
28
5.5k
JavaScript: Past, Present, and Future - NDC Porto 2020
reverentgeek
47
5.2k
The Cult of Friendly URLs
andyhume
78
6.2k
CoffeeScript is Beautiful & I Never Want to Write Plain JavaScript Again
sstephenson
160
15k
Designing Dashboards & Data Visualisations in Web Apps
destraynor
231
53k
How STYLIGHT went responsive
nonsquared
99
5.4k
How To Stay Up To Date on Web Technology
chriscoyier
790
250k
Practical Orchestrator
shlominoach
186
10k
Raft: Consensus for Rubyists
vanstee
137
6.8k
Six Lessons from altMBA
skipperchong
27
3.6k
Fontdeck: Realign not Redesign
paulrobertlloyd
83
5.4k
Facilitating Awesome Meetings
lara
53
6.3k
Transcript
$nl 〜僕が⾒た" 最⾼" のPHP コード〜 黒曜 @kokuyouwind http://kokuyouwind.com/a
▲ スライドのリンクは常に表示されています スライドめくりもリンクされます http://kokuyouwind.com/a
$ whoami 黒曜 / 株式会社Misoca に転職して半年 現職: Ruby on Rails/React
前職: PHP/Scala 趣味: OCaml @kokuyouwind http://kokuyouwind.com/a
宣伝 プログラマ向けの謎解きゲームを作りました http://kokuyouwind.com/a
宣伝 Github で公開中 「プログラマ 脱出ゲーム」でググると多分出てくる http://kokuyouwind.com/b http://kokuyouwind.com/a
本題 http://kokuyouwind.com/a
RubyKaigi2015 http://kokuyouwind.com/a
http://kokuyouwind.com/a
http://kokuyouwind.com/a
私が⼈⽣で⾒た 最悪のRuby コード http://kokuyouwind.com/a
http://kokuyouwind.com/a
聞きに⾏った http://kokuyouwind.com/a
感想 実際、割と酷かった に動画がある 教訓もあり、良い発表だった RubyKaigi のセッションページ http://kokuyouwind.com/a
⼀つ思ったこと http://kokuyouwind.com/a
⾃分が⾒た PHP コードは これより酷かった! ( 主観) http://kokuyouwind.com/a
$nl 〜僕が⾒た" 最⾼" のPHP コード〜 黒曜 @kokuyouwind http://kokuyouwind.com/a
構成 http://kokuyouwind.com/a
独⾃フレームワーク! 構成 http://kokuyouwind.com/a
構成 /core /model - MVC のM /logic - 共有ロジック /for_xxx
/public - web 公開ディレクトリ /controller - MVC のC /view - MVC のV /template - Smarty テンプレート http://kokuyouwind.com/a
構成 /core /model - MVC のM /logic - 共有ロジック /for_xxx
/public - web 公開ディレクトリ /controller - MVC のC /view - MVC のV /template - Smarty テンプレート http://kokuyouwind.com/a
controller MVC のC… ではない! ⼤体のメイン処理を書く場所 変数名が⼤体ひどい 後述する 例外駆動プログラミングという技法がフル活⽤される 後述する とりあえずひどい
後述する http://kokuyouwind.com/a
構成 /core /model - MVC のM /logic - 共有ロジック /for_xxx
/public - web 公開ディレクトリ /controller - MVC のC メイン処理を書く場所 /view - MVC のV /template - Smarty テンプレート http://kokuyouwind.com/a
view MVC のV… ではない! 謎 そもそもテンプレートは別にある controller から受け取った値を表示⽤に加⼯する? ⼤体は受け流すだけ たまにネストのレベルを変えてて悶絶する
できることはController とほぼ⼀緒 たまにすごい処理が書いてあってビビる http://kokuyouwind.com/a
構成 /core /model - MVC のM /logic - 共有ロジック /for_xxx
/public - web 公開ディレクトリ /controller - MVC のC メイン処理を書く場所 /view - MVC のV 謎 /template - Smarty テンプレート http://kokuyouwind.com/a
model MVC のM… ではない! 配列を上⼿く扱うためのクラス ドメインモデルのデータはすべて配列で扱う (PHP の配列は連想配列) DB との⼊出⼒も扱う
PDO でクエリを直接投げる 独⾃のクエリビルダーが4 種類くらいある どこからでも$this->getModel('Name') で取れる http://kokuyouwind.com/a
model <?php class UserModel extends HogeFWModel { public function get($id,
$ignore_delete = false) { $table = $this->get_table_name($id); // ソフトシャーディング $query = "SELECT * FROM $table WHERE id = ?"; if (!$ignore_delete) { // deleted が未来のことがあるので、IS NULL だけではだめ $query .= "AND (deleted IS NULL OR deleted <= '$(data('Y-m-d h:i:s') } $records = $this->db->execute($query, $id); if ($records === false) { // 厳密にfalse ならSQL 接続エラー throw new SQLException('SQL 接続エラーです'); } elseif (!$records) { // クエリは成功したが中身が空 throw new NotFoundException(' ユーザーが⾒つかりませんでした'); } $user = $records[0]; // 他に必要な情報を補完する return $this->appendInfo($user); } } http://kokuyouwind.com/a
model // こんな感じで使う // call はメソッドの呼び出し結果をキャッシュする関数 $this->cache->call('User', 'get', $id); //
call には配列形式で渡しても良い $um = $this-> getModel('User'); $this->cache->call([$um, 'get'], $id); // 返ってくるのは配列( というかハッシュ) ['id' => '758', 'name' => ' 黒曜', 'registered' => '2016-04-16 15:50:00', // ⼤抵MySQL 形式 'last_login' => 1460789400, // たまにUnixTime が⼊っている 'attributes' => [ 'belongs' => [ 'Misoca' => ['id' => '331'] // どんどんネストする ], 'is_penalty' => 'none', // なぜか真偽値ではなく⽂字列 ], 'append_info' => [ // 3 段くらいネストしたなんらかの情報 ] ] http://kokuyouwind.com/a
構成 /core /model - MVC のM 配列の加⼯とDB 処理 /logic -
共有ロジック /for_xxx /public - web 公開ディレクトリ /controller - MVC のC メイン処理を書く場所 /view - MVC のV 謎 /template - Smarty テンプレート http://kokuyouwind.com/a
logic 共有ロジック… ではないものがほとんど ⼤体はcontroller の中身を分割したもの バージョン移⾏の際に、controller の処理を まるまるコピーしlogic クラスができたりした controller
でできることはlogic でもmodel でもできる たまにlogic やmodel から直接view に渡す値を 設定していてビビる どこからでも$this->getLogic('Name') で取れる http://kokuyouwind.com/a
構成 /core /model - MVC のM 配列の加⼯とDB 処理 /logic -
共有ロジック メイン処理を書く場所2 /for_xxx /public - web 公開ディレクトリ /controller - MVC のC メイン処理を書く場所 /view - MVC のV 謎 /template - Smarty テンプレート http://kokuyouwind.com/a
" 最⾼" のPHP コード http://kokuyouwind.com/a
" 最⾼" のPHP コード 単⼀のコンテンツを表示するページのController メイン処理のメソッドが3000 ⾏くらい 1 メソッドで3000 ⾏
うち2000 ⾏くらいがcatch 節 5 段くらいにネストした100 項⽬くらいの配列を構築 これが1 つのコンテンツを表す とりあえずvar_dump してデバッグする 5 環境くらいにfork して、それぞれ独⾃に進化している http://kokuyouwind.com/a
2 ⽂字変数 http://kokuyouwind.com/a
変数名が酷い <?php class ContentController extends HogeFWController { public function execute()
{ // ⼤体2 ⽂字変数 $cm = $this->getModel('Content'); $nl = $this->getLogic('Normalize'); $cont = $this->cache->call([$cm, 'get'], $this->getAttribute('i $cont = $nl->norm($cont); // 間に300 ⾏くらい挟まる if($has_body) { $cont = $nl->normBody($cont); } // 間に500 ⾏くらい if ($need_next) { // さっきの$nl を上書きする $nl = $this->getLogic('NextContent'); $content['next'] = $nl->calc($content); } // ... まだ続きます http://kokuyouwind.com/a
変数名が酷い // ... 続き // 間に300 ⾏くらい // さっきの分岐を通ってると死ぬバグ $content['append']
= $nl->normAppend($content); // 間に200 ⾏くらい if ($need_extra) { // 明らかに$nl じゃないけど$nl を上書きする $nl = $this->getLogic('Extra'); $content['extra'] = $nl->compute($content); } // 間に300 ⾏くらい // view にcontent を受け渡す(Model, Logic からも可能) $this->setAttribute('content', $content); return; } } http://kokuyouwind.com/a
例外駆動プログラミング http://kokuyouwind.com/a
例外駆動プログラミング <?php class ContentController extends HogeFWController { public function execute()
{ try { $cm = $this->getModel('Content'); $content = $this->cache->call($cm, 'get', $this->getAttribute if(!this->user) { throw new NotLoginException(); } // ログイン時、コンテンツありの処理が1000 ⾏くらい続く } catch (NotLoginException $e) { // ⾮ログイン時の処理 if ($cm->isAllowGuest($content)) { // ⾮ログイン時、コンテンツ閲覧可の処理が800 ⾏くらい続く // うち600 ⾏くらいはログイン時処理のコピペ } else { // view/content_error_view を描画する return 'content_error'; } } } } http://kokuyouwind.com/a
http://kokuyouwind.com/a
どうしてこうなったか そもそも2007 年ごろから作られ始めたコード PHP5.2 の頃で名前空間すらない モダンで実績のあるフレームワークもあまりない 初期は数名での開発だった レビュー⽂化のないカウボーイプログラミング テスト⽂化もない 新機能や企画優先で継ぎ⾜し実装が横⾏した
品質より納期を優先するスケジュール 追加された機能が消せず、膨れる⼀⽅ http://kokuyouwind.com/a
どうやって戦っていたか レビュー⽂化が根付き始めた時期の⼊社だった 当時はsvn でFisheye/Crucible を使っていた PHPUnit も先輩が広めていた 複雑すぎてテスト不可能な部分が多すぎた 新規に書くコードはテストを書く、という共通認識 ⼿作業確認によるリファクタリング(
リスク⾼) grep ⼒を⾼めて物理で殴る 最近はScala で書きなおそうとしている( はず) 転職したため現状は謎 http://kokuyouwind.com/a
教訓 機能追加偏重の⽂化は保守不能なコードを⽣み出しうる 無論、ユーザーに価値を届けることは⼤事 うまくバランスを取ることが重要 レビュー⽂化、テスト⽂化で品質を保つ スピードを上げ過ぎない「⾜枷」としての機能 無理な納期から開発チームを守る防⽕壁が必要 機能もコードも「捨てられる」ように作る 犠牲的アーキテクチャ 機能テスト
http://kokuyouwind.com/a
予備スライド http://kokuyouwind.com/a
他に⾯⽩かったコード バグにバグが重なって上⼿く動いているコード 1 ⽇が 60 * 60 * 60 *
24 秒ある 1 週間が 8 ⽇ある コメントと返り値が真逆 関数名は block 「過負荷ブロックされたらfalse が返ります」 実際は過負荷ブロック時にtrue が返っていた for が3 つネストした中に「continue $n; 」 100 ⾏くらいあるメソッド先頭に「return false; 」 http://kokuyouwind.com/a
構成 /core /model - MVC のM /logic - 共有ロジック /for_xxx
/public - web 公開ディレクトリ /controller - MVC のC /view - MVC のV /template - Smarty テンプレート http://kokuyouwind.com/a
public web からのエントリポイント apache でDirectory として指定される Controller をkick するphp ファイルが配置される
URL routes の数だけファイルが置かれる Router? なにそれおいしいの? 画像/js/css などの静的ファイルもここに配置される http://kokuyouwind.com/a
構成 /core /model - MVC のM /logic - 共有ロジック /for_xxx
/public - web 公開ディレクトリ /controller - MVC のC メイン処理を書く場所 /view - MVC のV 謎 /template - Smarty テンプレート http://kokuyouwind.com/a
template Smarty のテンプレートを置く場所 ネストが⾮常に深い 3 重if の中身が数⼗⾏とかはざら ロジック分離? なにそれ美味し(ry 多⾔語対応の影響でさらにカオスになった
⽇本語以外では出さない、みたいな分岐が そこら中にある http://kokuyouwind.com/a