Upgrade to Pro — share decks privately, control downloads, hide ads and more …

$nl ~僕が見た”最高”のPHPコード~

$nl ~僕が見た”最高”のPHPコード~

NL名古屋で発表したときのスライドです。

本当は10分の発表枠だったんですが、ぶっちぎりでオーバーした挙句予備スライドまで話しました。

内容については…お察し下さい。

kokuyouwind

April 16, 2016
Tweet

More Decks by kokuyouwind

Other Decks in Programming

Transcript

  1. $ whoami 黒曜 / 株式会社Misoca に転職して半年 現職: Ruby on Rails/React

    前職: PHP/Scala 趣味: OCaml @kokuyouwind http://kokuyouwind.com/a
  2. 構成 /core /model - MVC のM /logic - 共有ロジック /for_xxx

    /public - web 公開ディレクトリ /controller - MVC のC /view - MVC のV /template - Smarty テンプレート http://kokuyouwind.com/a
  3. 構成 /core /model - MVC のM /logic - 共有ロジック /for_xxx

    /public - web 公開ディレクトリ /controller - MVC のC /view - MVC のV /template - Smarty テンプレート http://kokuyouwind.com/a
  4. 構成 /core /model - MVC のM /logic - 共有ロジック /for_xxx

    /public - web 公開ディレクトリ /controller - MVC のC メイン処理を書く場所 /view - MVC のV /template - Smarty テンプレート http://kokuyouwind.com/a
  5. 構成 /core /model - MVC のM /logic - 共有ロジック /for_xxx

    /public - web 公開ディレクトリ /controller - MVC のC メイン処理を書く場所 /view - MVC のV 謎 /template - Smarty テンプレート http://kokuyouwind.com/a
  6. model MVC のM… ではない! 配列を上⼿く扱うためのクラス ドメインモデルのデータはすべて配列で扱う (PHP の配列は連想配列) DB との⼊出⼒も扱う

    PDO でクエリを直接投げる 独⾃のクエリビルダーが4 種類くらいある どこからでも$this->getModel('Name') で取れる http://kokuyouwind.com/a
  7. 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
  8. 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
  9. 構成 /core /model - MVC のM 配列の加⼯とDB 処理 /logic -

    共有ロジック /for_xxx /public - web 公開ディレクトリ /controller - MVC のC メイン処理を書く場所 /view - MVC のV 謎 /template - Smarty テンプレート http://kokuyouwind.com/a
  10. logic 共有ロジック… ではないものがほとんど ⼤体はcontroller の中身を分割したもの バージョン移⾏の際に、controller の処理を まるまるコピーしlogic クラスができたりした controller

    でできることはlogic でもmodel でもできる たまにlogic やmodel から直接view に渡す値を 設定していてビビる どこからでも$this->getLogic('Name') で取れる http://kokuyouwind.com/a
  11. 構成 /core /model - MVC のM 配列の加⼯とDB 処理 /logic -

    共有ロジック メイン処理を書く場所2 /for_xxx /public - web 公開ディレクトリ /controller - MVC のC メイン処理を書く場所 /view - MVC のV 謎 /template - Smarty テンプレート http://kokuyouwind.com/a
  12. " 最⾼" のPHP コード 単⼀のコンテンツを表示するページのController メイン処理のメソッドが3000 ⾏くらい 1 メソッドで3000 ⾏

    うち2000 ⾏くらいがcatch 節 5 段くらいにネストした100 項⽬くらいの配列を構築 これが1 つのコンテンツを表す とりあえずvar_dump してデバッグする 5 環境くらいにfork して、それぞれ独⾃に進化している http://kokuyouwind.com/a
  13. 変数名が酷い <?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
  14. 変数名が酷い // ... 続き // 間に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
  15. 例外駆動プログラミング <?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
  16. 他に⾯⽩かったコード バグにバグが重なって上⼿く動いているコード 1 ⽇が 60 * 60 * 60 *

    24 秒ある 1 週間が 8 ⽇ある コメントと返り値が真逆 関数名は block 「過負荷ブロックされたらfalse が返ります」 実際は過負荷ブロック時にtrue が返っていた for が3 つネストした中に「continue $n; 」 100 ⾏くらいあるメソッド先頭に「return false; 」 http://kokuyouwind.com/a
  17. 構成 /core /model - MVC のM /logic - 共有ロジック /for_xxx

    /public - web 公開ディレクトリ /controller - MVC のC /view - MVC のV /template - Smarty テンプレート http://kokuyouwind.com/a
  18. public web からのエントリポイント apache でDirectory として指定される Controller をkick するphp ファイルが配置される

    URL routes の数だけファイルが置かれる Router? なにそれおいしいの? 画像/js/css などの静的ファイルもここに配置される http://kokuyouwind.com/a
  19. 構成 /core /model - MVC のM /logic - 共有ロジック /for_xxx

    /public - web 公開ディレクトリ /controller - MVC のC メイン処理を書く場所 /view - MVC のV 謎 /template - Smarty テンプレート http://kokuyouwind.com/a