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
34
$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.1k
APMをちゃんと使おうとしたら、いつのまにか独自gemを作っていた話
kokuyouwind
0
720
RBS meets LLMs - Type inference using LLM
kokuyouwind
0
790
オンラインボードゲームを作りたい人生だった
kokuyouwind
0
450
1年間本番運用してわかった、スタートアップこそAWS Copilot CLIを使うべきNつの理由
kokuyouwind
2
11k
なるべく楽したいAWSセキュリティ
kokuyouwind
1
59
Railsパフォーマンス・チューニング入門
kokuyouwind
0
260
Rubyパターンマッチに闇の力が備わり最強に見える
kokuyouwind
0
86
Slackワークフロー活用術
kokuyouwind
0
89
Other Decks in Programming
See All in Programming
Grafana Cloudとソラカメ
devoc
0
170
DROBEの生成AI活用事例 with AWS
ippey
0
130
富山発の個人開発サービスで日本中の学校の業務を改善した話
krpk1900
4
390
さいきょうのレイヤードアーキテクチャについて考えてみた
yahiru
3
750
社内フレームワークとその依存性解決 / in-house framework and its dependency management
vvakame
1
560
Honoをフロントエンドで使う 3つのやり方
yusukebe
7
3.3k
『GO』アプリ データ基盤のログ収集システムコスト削減
mot_techtalk
0
120
法律の脱レガシーに学ぶフロントエンド刷新
oguemon
5
740
Domain-Driven Transformation
hschwentner
2
1.9k
Pulsar2 を雰囲気で使ってみよう
anoken
0
240
Formの複雑さに立ち向かう
bmthd
1
850
バックエンドのためのアプリ内課金入門 (サブスク編)
qnighy
8
1.8k
Featured
See All Featured
Optimising Largest Contentful Paint
csswizardry
34
3.1k
Art, The Web, and Tiny UX
lynnandtonic
298
20k
Improving Core Web Vitals using Speculation Rules API
sergeychernyshev
9
440
StorybookのUI Testing Handbookを読んだ
zakiyama
28
5.5k
How GitHub (no longer) Works
holman
314
140k
Bootstrapping a Software Product
garrettdimon
PRO
306
110k
Why Our Code Smells
bkeepers
PRO
336
57k
Build The Right Thing And Hit Your Dates
maggiecrowley
34
2.5k
jQuery: Nuts, Bolts and Bling
dougneiner
63
7.6k
Easily Structure & Communicate Ideas using Wireframe
afnizarnur
193
16k
Evolution of real-time – Irina Nazarova, EuRuKo, 2024
irinanazarova
6
550
Helping Users Find Their Own Way: Creating Modern Search Experiences
danielanewman
29
2.4k
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