超PHPerになろう

Enjoy PHP Programming

PSRについて話したPHPカンファレンス仙台2019 #phpconsen

PHPカンファレンス仙台2019の参加者としての感想はMediumに書きました。

medium.com

さて、ここではPHPコードの実装者としての感想を書きます。

github.com

2019年1月31日10時20分 追記

真にPSRとHTTPの関係について学びたい型は、PHPカンファレンス2018の田中ひさてるさんの発表を読みましょう。

スライド

先に資料を紹介します。

実際は31分で121枚を話すという格好だったので説明不足だった点が多々ありますね。

PSRとPHP-FIGについて

だいたい去年「PSRの誤解」に書いた通りのことを話しました。

qiita.com

実装方針について

  • Composerのオートロード機能によってロードできるファイル構造にすること
  • PHP 7.3以上をターゲットにすること
  • PhanとPHPStanによる静的解析ができること

ほかにもいくつか気をつけてたことがあった気がしますが忘れました。PhpStormは使ってないです。 (PHPStanの方がエラーをいっぱい見付けてくれるので)

一方でPhanとPHPStanは実装方式が異なり、どちらかでしか検出されないような問題もちょくちょくありました。うれしいですね。

PSR-0について

わぁいPSR-0。あかりオートロードだいすき。

PSR-0といったら@Hiraku先生の記事が有名です。

blog.tojiru.net

まあ0だし、実装した順番もスライドを作った順番も最初です。廃止済みですけど。出落ち。

私自身は実用/非実用に拘らず実際何度もクラスローダーを実装してきたわけですが、PHPを構成するもののなかでも非常におもしろい領域であります。

遅延評価によるクラスロード機構は単純なファイルロード機能を提供するに留まらず、Go! AOPフレームワークなどはメタプログラミングによるアスペクト志向プログラミングの基盤を提供します。

さて実装についてですが、オーソドックスにディレクトリをセットするだけの方式でした。パフォーマンスはまったく気にしてないです。

PSR-1とPSR-2について

PSR-1(Basic Coding Standard)とPSR-2(Coding Style Guide)は、どちらもコーディング標準の勧告ですが、何が異なるのでしょうか。

PSR-1はPHPコードを相互運用するために必要な標準コーディング要素だとされます。つまりこれには、改行コードはUTF-8でBOMを含んではいけないこと、出力やPHP設定変更は宣言の読み込みとは厳格に区別されるべきこと、そしてオートロード可能であるべきことが含まれます。

一方でPSR-2は「このガイドによる利益は規則自体ではなく、規則を共有すること」それによって「複数の作者が認知の摩擦を減らすこと」にあるとされます。

そんなわけで、今回の発表にあたって私が提示したのはコーディングガイドとPHP-CS-Fixerのルールです。

私はどちらかといったらコードフォーマッタアンチなのですが(と、けっこう発言してる)、久々にやってみたら意外とたのしかったですね。

PSR-3について

実装順的には最後でした。Loggerも何度か実装したしMonologの拡張もやってたので。

そういや説明が抜けてましたが、出力フォーマットは俗に改行区切りのJSONと呼ばれるやつです。

こういうのはやっぱり何度か実装してて、WEB+DB PRESS Vol. 96あたりにも書きました。

サンプルコードはGitHubでも公開したので興味があったら読んでね。

github.com

PSR-4について

いままで忘れてたのですが、そういやComposerの話は2017年に既にやってました。

今回はPSR-0とは趣向を変えて、せっかくなのでクラスにしました。

PSR-5について

今回は実装してません。最後にちょっとだけ触れました。

くどいようだけど何度も書いたよ!!

gihyo.jp qiita.com qiita.com

WEB+DB PRESS総集編にも収録されてるので、買って読んでね。

https://amzn.to/2DGsvOmamzn.to

あと、僕も2016年頃にPHPDocとか実行時情報をとってきていい感じにするやつを実装しようとしてたんですけど、PHPStanとかPhpactorがいい感じだったので最近手をつけてないですね。

github.com

おもしろオレオレCUIアプリのサンプルコードとしては結構おもしろいかもしれませんね。

PSR-6について

うーん… うーん… みゃーん。

PSR-7について

今回は実装してない。

qiita.com qiita.com

実装自体はやればできるのは知ってる。

PSR-8について

今回は実装を見送った。

qiita.com

PSR-9とPHP-10について

これなんでPHPの勧告として発効しようと思ったの 誰が喜ぶのかよくわからないし、誰も喜ばないだろうから ABANDONED (放棄) になったのかと。暇なら提案の経緯の調査とか、提案内容についていろいろ考察してみたい気もするけど、今回は余裕がなかった。

PSR-11について

実装してないけど、発表当日の朝に読んでた。提案されてるInterfaceの範囲では ContainerInterfaceget()has() を持ってるだけなんだけど、例外に ContainerExceptionInterface を実装したクラスを送出することを定めて一貫したエラーハンドリングができるようになって嬉しい! ってのが提案の肝なのだと理解。PSR-11メタドキュメントまで読むと、いろいろ共通要素を議論したけどここまでしかまとまらなかったんだなって感じがして赴き深い。

マジックメソッド__get() とか __isset() が定義に含まれてなかったのは、やっぱり、うーん。[REVIEW] PSR-11 Container Interfaceのトピックは長くて全部は読んでないんだけど、このやりとりの後ならPHP-FIGに嫌気が差すのも、まあ、わかる。 これってそこまでして相互運用したいものだったの?

PSR-12について

現在DRAFTだけど、ずっと作業は進んでるみたい。PSR-2以降に追加された文法や、PSR-2では言及されなかったケースについての定義もある。

PSR-2で言及されなかったパターン、こんなの。

<?php

do {
    // structure body;
} while (
    $expr1
    && $expr2
);

あと、PSR-12では名前空間の深度についても言及がある。

Compound namespaces with a depth of more than two MUST NOT be used. Therefore the following is the maximum compounding depth allowed:

<?php

use Vendor\Package\SomeNamespace\{
    SubnamespaceOne\ClassA,
    SubnamespaceOne\ClassB,
    SubnamespaceTwo\ClassY,
    ClassZ,
};

And the following would not be allowed:

<?php

use Vendor\Package\SomeNamespace\{
    SubnamespaceOne\AnotherNamespace\ClassA,
    SubnamespaceOne\ClassB,
    ClassZ,
};

せ、攻めるなあ。

PSR-13について

PSRの最悪中の最悪。こんなのに賛成票を投じた奴は誰だと叫びたくなる。

RFC 5988 - Web Linkingの抽象化だってのはわかる。わかるよ。

www.php-fig.org

isTemplated() って何だこれ? RFC6570 URL Templateは可能性があるし混ぜたくなったのはわかるよ? 私だってURL組み立てのロジックはいろいろ書いてきたし、URL Templateのユースケースだってすぐに想像がつく。でもなんでこれを型レベルで分けるんじゃなくてオブジェクトの状態として持たせようとした? 提案者自身の概念実装のはずのCrell/HtmlModel: Domain value objects for modeling HTML pagesだって使ってないじゃねえか。「たぶんあったら便利だと思うから仕様に入れておこう」って発想は本当に危険で、このせいでPSR-13がゴミになってしまったと感じる。

PSR-13の数少ない実用的な利用者はSymfony\WebLink)なのだけれど、ここでは isTemplated() をこのように処理する。

HttpHeaderSerializer.php#L35-L38

        foreach ($links as $link) {
            if ($link->isTemplated()) {
                continue;
            }

テンプレートだったら完全に無視!!

そりゃそーだ。 テンプレートに当て嵌められるべき変数なんて、ここでは誰も知らないんだから……。冷静になってみてほしいんだけど、ほんと $link->isTemplated() === true だったときに何かできるパターンなんて本当にあるの?

それにまして、PHP-FIGが提供するphp-fig/link-utilパッケージの中でもlink-util/TemplatedHrefTrait.phpの実装がつらい。

    private function hrefIsTemplated($href)
    {
        return strpos($href, '{') !== false ||strpos($href, '}') !== false;
    }

これは新卒がやったらていねいに優しく諭すけど、この実行時の雑すぎるパースでの判定はとても危険。プロが書くコードじゃない、とまで言いたい。

そのほかこの仕様の是非を検討すると、さまざまなRFCや標準、PHPの実装までもが登場してきて非常に厄介なので、ここではこの程度で収めておきます。

PSR-14について

おもしろそうだけどPSR-13に深入りしてたらどんどん時間が溶けてしまったので全然把握してない。

PSR-15について

まだ実装してない。シングルパスのミドルウェアなのは知ってる。

PSR-16について

簡単っぽいから実装するつもりだったけどPSR-13に深入りしてたら時間が溶けたので、まだ実装してない。

PSR-17とPSR-18について

どっちもHTTP関連だけど、案の定僕はまだ実装してない。ふしぎなことにPSR-18には(PHP-FIGを抜けたはずの)GuzzleのJeremy LindblomとMark Sagi-Kazarが作業グループに参加してそう。PSR-17には参加してなそう。ふしぎ。

PSR-19について

PSR-5から分離された。どうなるんだろう、わからん。

まとめ

雑にまとめたけど、めっちゃ徒労感がある。ぜいたくな時間を送ってしまった@tadsanにFANBOXで課金して励ましのおたよりを送ろう。

www.pixiv.net