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
48
$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.9k
APMをちゃんと使おうとしたら、いつのまにか独自gemを作っていた話
kokuyouwind
0
790
RBS meets LLMs - Type inference using LLM
kokuyouwind
0
840
オンラインボードゲームを作りたい人生だった
kokuyouwind
0
530
1年間本番運用してわかった、スタートアップこそAWS Copilot CLIを使うべきNつの理由
kokuyouwind
2
11k
なるべく楽したいAWSセキュリティ
kokuyouwind
1
78
Railsパフォーマンス・チューニング入門
kokuyouwind
0
310
Rubyパターンマッチに闇の力が備わり最強に見える
kokuyouwind
0
120
Slackワークフロー活用術
kokuyouwind
0
120
Other Decks in Programming
See All in Programming
技術同人誌をMCP Serverにしてみた
74th
1
450
PipeCDのプラグイン化で目指すところ
warashi
1
220
WindowInsetsだってテストしたい
ryunen344
1
200
Railsアプリケーションと パフォーマンスチューニング ー 秒間5万リクエストの モバイルオーダーシステムを支える事例 ー Rubyセミナー 大阪
falcon8823
4
1k
エンジニア向け採用ピッチ資料
inusan
0
170
Is Xcode slowly dying out in 2025?
uetyo
1
240
関数型まつりレポート for JuliaTokai #22
antimon2
0
160
ふつうの技術スタックでアート作品を作ってみる
akira888
0
220
Systèmes distribués, pour le meilleur et pour le pire - BreizhCamp 2025 - Conférence
slecache
0
110
datadog dash 2025 LLM observability for reliability and stability
ivry_presentationmaterials
0
280
新メンバーも今日から大活躍!SREが支えるスケールし続ける組織のオンボーディング
honmarkhunt
0
100
#QiitaBash MCPのセキュリティ
ryosukedtomita
0
460
Featured
See All Featured
Helping Users Find Their Own Way: Creating Modern Search Experiences
danielanewman
29
2.7k
Bash Introduction
62gerente
614
210k
Improving Core Web Vitals using Speculation Rules API
sergeychernyshev
17
950
The Success of Rails: Ensuring Growth for the Next 100 Years
eileencodes
45
7.5k
Building an army of robots
kneath
306
45k
The MySQL Ecosystem @ GitHub 2015
samlambert
251
13k
Rebuilding a faster, lazier Slack
samanthasiow
82
9.1k
CSS Pre-Processors: Stylus, Less & Sass
bermonpainter
357
30k
JavaScript: Past, Present, and Future - NDC Porto 2020
reverentgeek
48
5.4k
Building Flexible Design Systems
yeseniaperezcruz
328
39k
Why Our Code Smells
bkeepers
PRO
337
57k
How To Stay Up To Date on Web Technology
chriscoyier
790
250k
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