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
32
$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
6k
APMをちゃんと使おうとしたら、いつのまにか独自gemを作っていた話
kokuyouwind
0
710
RBS meets LLMs - Type inference using LLM
kokuyouwind
0
790
オンラインボードゲームを作りたい人生だった
kokuyouwind
0
440
1年間本番運用してわかった、スタートアップこそAWS Copilot CLIを使うべきNつの理由
kokuyouwind
2
11k
なるべく楽したいAWSセキュリティ
kokuyouwind
1
57
Railsパフォーマンス・チューニング入門
kokuyouwind
0
250
Rubyパターンマッチに闇の力が備わり最強に見える
kokuyouwind
0
82
Slackワークフロー活用術
kokuyouwind
0
87
Other Decks in Programming
See All in Programming
法律の脱レガシーに学ぶフロントエンド刷新
oguemon
4
620
為你自己學 Python
eddie
0
540
Amazon ECS とマイクロサービスから考えるシステム構成
hiyanger
1
180
盆栽転じて家具となる / Bonsai and Furnitures
aereal
0
2.2k
Fixstars高速化コンテスト2024準優勝解法
eijirou
0
200
Immutable ActiveRecord
megane42
0
120
AWS Lambda functions with C# 用の Dev Container Template を作ってみた件
mappie_kochi
0
220
AWSマネコンに複数のアカウントで入れるようになりました
yuhta28
2
150
Vue.jsでiOSアプリを作る方法
hal_spidernight
0
120
ISUCON14公式反省会LT: 社内ISUCONの話
astj
PRO
0
140
Rubyでつくるパケットキャプチャツール
ydah
0
530
オニオンアーキテクチャを使って、 Unityと.NETでコードを共有する
soi013
0
390
Featured
See All Featured
Building a Modern Day E-commerce SEO Strategy
aleyda
38
7.1k
Gamification - CAS2011
davidbonilla
80
5.1k
Building a Scalable Design System with Sketch
lauravandoore
460
33k
[Rails World 2023 - Day 1 Closing Keynote] - The Magic of Rails
eileencodes
33
2k
How to Create Impact in a Changing Tech Landscape [PerfNow 2023]
tammyeverts
49
2.2k
Rails Girls Zürich Keynote
gr2m
94
13k
[RailsConf 2023 Opening Keynote] The Magic of Rails
eileencodes
28
9.2k
Keith and Marios Guide to Fast Websites
keithpitt
410
22k
Optimizing for Happiness
mojombo
376
70k
Principles of Awesome APIs and How to Build Them.
keavy
126
17k
Designing Dashboards & Data Visualisations in Web Apps
destraynor
231
52k
Into the Great Unknown - MozCon
thekraken
34
1.6k
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