PHP - The Wrong Way とは何だったのか

これは PHP Advent Calendar 2017 - Qiita の12日目の記事です。

わたくし先月、PHP - The Wrong Way を日本語に翻訳しました。

www.phpthewrongway.com

衝撃の扉絵はご覧いただけましたでしょうか。

f:id:tanakahisateru:20171211151812p:plain

これは2017年の Symfony Live London で Laravel を笑い者にしている様子 を描いた風刺画です。

うそですけどね。

でもどうですか、そういう絵だといえばそう見えませんか? ロンドンでの事件は Laravel と Symfony の間にある摩擦ですが、それと相似な関係は、ユーザーコミュニティ全体と Laravel のようなものとの間にこそ、より深刻な状態で横たわっているのではないでしょうか。このロンドンのブーメランは、自分の翻訳進捗にずいぶん影響しました。

実を言うと自分は、あまり多くの人に PHP - The Wrong Way の翻訳を読んでもらいたいとは思っていません。この種の不幸な摩擦のサンプルとして、文脈を超えた配慮をする力と器のある人に届き、考えかたのきっかけにしてもらいたかったのです。

そりゃもう、誰にでも読んでもらいたい文書としては、こんなものよりはるかに PHP: The Right Way のほうが有益です。過剰に見えるにしても、少なくとも Right Way には技術的には嘘は書かれていません。

ということは? ええ、そのとおり、Wrong Way は技術情報として、著者の妄想にしかない嘘の事実が書かれているのです。いえ、むしろその大半が事実にもとづかない、無知ゆえの言い訳のようなもので出来上がっています。

英文でこのドキュメントを最初に読んだ時点で思ったのは、日本で言ういわゆるポエムのスタイルに近いということでした。まだコモディティ化していないパラダイムに関して、部分理解 (曲解?) から想像を膨らませたオレオレ理論を語りはじめるアレです。(どこかのサイトではアカウントごと抹殺されるほどの大罪とされていますね)

とはいえ、多言語に翻訳されて GitHub でメンテされているんだし、そのためにドメイン名まで取ったんだから、何かひとつぐらい得るものがあるにちがいない、というのが翻訳のそもそもの動機でした。それで、何が得られたかって? ええ、ある意味、期待以上のものが得られましたよ。それは何かというのはちょっと後にして、まずはきっちり批判しなければなりません。

このウェブサイトは、PHP プログラミングに関する現実的な見解を示すために作られました。流行りのトレンド、理論、学問的な教示ではなく、経験と実践の帰着を書き記した視点です。

そうですか、それでその「経験と実践」は具体的には誰の? 何の? データはどこから?

繰り返し登場する「現実の」「現場では」といった言い回しですが、いくら読み進めても、十分といえる事例は出てきません。脳内べき論ばかりが繰り返されます。また、どのトレンドと学術理論がどのように間違っているか、いっこうに証明されません。たとえばここ。

今日のプログラマの多くは、健全なプログラミングの基本原則を完全に無視し

モダンな人たちのどの方法論が、基本原則の何を、どのように無視しているのか、なぜその無視が問題なのか、まったくわかりません。いきなり無条件に「流行っているものは間違っているはずだ (大衆は陰謀に騙されている)」みたいな導入です。これじゃカルトですね。

技術トレンドのほうには、少なくともそれを支える人がいて、共通の問題意識のバックボーンがあります。学術論文というのは、客観的な推論と実証データをともなう「科学的」なスタイルが必要です。いずれも、「誰が」「何を」説明しているのか明らかになっています。

理性的な議論の土俵において、これがもっとも目につく不正です。しかも著者は匿名です。いくら心からの主張でも、一度でも嘘をつくと全体が信用されなくなります。もし本当に議論に集中できるようにと匿名にしていたのだとしても、これでは逆効果です。口から出まかせを言っているから名前も名乗れないのだろうと言われても仕方ありません。

もし「これは私のエッセイですが、きっと皆さん薄々思っていることと重なるでしょう」みたいな話であれば、信用を失うことはなかったんでしょうけど。

(... え、おまえも「うそですけどね」って最初に書たじゃん? ああ、これは所詮エッセイですから)

RedditPHP コミュニティを長く見ているという、Jon LeMaitre さんからの返信は、主にそういった内容のことが繰り返されています。「だから誰の話だよ」って。

medium.com

These people seem to work tirelessly at getting other people to follow their way of doing things.

It’s unclear who “these people” are as the author never actually provides names or even examples of these propagations. It’s a nebulous complaint as abstract as the very abstractions lamented by the author further into the article.

この人は Laravel のユーザーみたいですね。他の指摘内容をざっとかいつまんでみます:

  • 自作ライブラリのセットでも、いつもそれ使うって決めてるならすでにフレームワークじゃん
  • Djang / Rails の成り立ちについては完全に理解が間違ってるよ
  • 無駄に層を積んで複雑にしてるとかそれ strtotime() 見ても同じこと言える?
  • 普通の案件ならスケールしないのはフレームワークのせいじゃなくて人材の(ゲフンゲフン
  • PHPは関数型をサポートしていますってどこが? リフレクティブってそんなパラダイム知らないけど?
  • OOP が好きだ嫌いだで Composer のクラスローダー使わない手はないでしょ、PSR-0/4 は嫌でライブラリは使ってもいいとかいまどきナンセンスにもほどが...
  • セキュアプログラミングのやつだけは読む価値ある

Wrong Way とは逆の意味で (当たり前すぎて) 翻訳が辛いので、だれかお願いします。たぶんこの返信のほうがよっぽど、一般的には価値のある文書なので。

たしかに、Wrong Way は見出しのレベルではいいこと書いてそうに見えちゃうので、知識がない人が見れば「そうかPHPはそんなことになってるのか、人知れず真実を見抜いているとは立派な」ってふうになるじゃないですか。そんでダメな人ほど「ほらやっぱり所詮はPHPの連中は、いくら有名だって言ってもろくなもんじゃない。オブジェクト指向フレームワークも標準規約も無視していいじゃん。真面目にやってなくてよかった」って、ますますダメなほうへ行くでしょ。

よく考えてください、知識がある人が技を極めたあと体制側の欺瞞を知って戻ってきてダークヒーローになったとかなら、こんな稚拙な間違いだらけになるはずないじゃないですか。経験豊かな人が PHP に教訓を授けてくれた? いやいや、他の言語文化で多くの経験をしているならなおさら、PHP で起きていることを多角的に理解できないとおかしいでしょ。これ PHP の初歩しか実務経験がなくて、何か仕事で嫌なことがあったんだなとプロファイリングしたほうが辻褄が合います。

だいたい日本のエンジニア、外人が書いたってだけで信用しすぎなんですよ。ちょっとぐらい「これはやばいwww」「ポエム乙w」「放射脳の臭いがする」的なはてブが付くと思ってたら、

はてなブックマーク - PHP - The Wrong Way f:id:tanakahisateru:20171211173554p:plain

そして星ですかそうですか... むしろ理解を示しちゃいかん派の代表選手として、これ日本語で紹介させていただいたつもりなのですよ、こちらとしては、えぇ。

Wrong Way : Always trust foreigners than your neighbors.

自分からはさらに 2 点指摘させてください。(もっとあるけど書ききれないので)

ひとつめ。

すべての汎用 PHP フレームワークはクソ!

誰も必要としていないのは、万能なフレームワークです。みんながみんな一般的な問題を抱えているわけではなく、みな非常に特殊な問題を抱え解決しようと試みています。

– ラスマス・ラードフ

ちょっとまてコラ !

この引用をフレームワークプロダクトを否定するのに使うのは、あまりにもラスマスに失礼じゃないか? え?!

これは、PHP Frameworks Day 2013 の公演で質疑応答の回答の抜粋で、まさにその質問と回答が YouTube で公開されています。

www.youtube.com

回答いきなり "They all sucks" で会場大爆笑です。すばらしいつかみネタでした。そして、次のように続いています。

"So, while all these sucks, everyone needs a framework, while everyone doesn't need a general purpose framework. Nobody has the general problem..."

見比べると、間にあった一節がチョッキンされていますね。「ま、ぜんぶクソだけどさ、みんな何かフレームワークは必要としている、でも、なんでも万能フレームワークじゃない」だいぶ意味が違いますね。だいたい、PHP Frameworks Day に来てフレームワーク全否定するとか、どんだけ空気読めんのかって話になっちゃいますよ、ありえいないでしょ。

ラスマスが言いたかったのは、そりゃ畑違いの人からしたら、目的の異なるフレームワークは役に立たないしクソって言われるのわかるよ、つまり PHP には汎用で万能なフレームワークなんてニーズは実はなくて、みんなそれぞれいろんなレイヤーで PHP を使っている、だからフレームワークは多様だし、たとえ誰かがアレはクソだこっちのほうが優れていると言っていたとしても、問題を共有する人たちにとっては、他人にはわからない大事な価値観があって、PHP はその多様性を支える言語なんだと、そういうことじゃないですか?

このスタンスは本当に素晴らしく、フレームワークの各コミュニティの関係に良い影響を与えただろうと思っています。同じようなことを日本の PHP カンファレンスでも言ってました。たしか「この会場に来られている皆さんはクレイジーなこととお思いかもしれませんが、PHP7 は WordPress のパフォーマンス向上を非常に重視しています」と。

これを、既成のフレームワークを否定して自作主義を後押しする文脈で引用するのは、もしわざとなら、PHPer の風上にも置けやしねぇってもんですよ、ほんとに。他も、ちょっと引用のニュアンス違うんじゃないかと思う箇所もあったけど、とにかくこれが飛び抜けてひどいですね。

あと、「誰が何を言っているかは問題ではない」と後から FAQ で言うのなら、有名人の引用を挟むのはチートですよね。違うでしょ。誰が何を言ったかは、その発言の背景が非常に重要です。そこもっかい考えて、ラスマス動画を拝みましょう。

ふたつめ。

そして以前のプログラマーフレームワーク A またはフレームワーク B を使っていたら人日が節約できたのにと、泣き言を言いだします。

これはプロのプログラマーのメンタリティではありません。こんなこと誰もしません。

は?

プレーン PHP で書かれたコードを2年以上かけてどうにかフレームワーク化した件で、メンバーがどれだけ前の外注に呪いの言葉を吐いたかって話、しようか?

確かに、恐ろしいコード、おそらくはじめから設計されていなかったコード、またはクライアントが書き直しを選択したくなくて何度も自己成長しているであろうコード、といったものは存在し、コードが悪すぎてもうあなたには手に負えないとしても、どんなフレームワークもこのような状況を防いでくれはしません。これはよくあるプログラムの自然な成長プロセスです。とにかく、どんな種類のフレームワークも結局は断片化してしまいます。

恐ろしいスパゲッティコードが存在することは確かですが、誰も恐ろしいスパゲッティコードを意図的に作りはしません。場合によっては経験不足の結果かもしれませんし、クライアントのせいで開発の途中で何度か仕様を変更したというのはしょっちゅうですが、その両方のいずれの場合でも、フレームワークを使用したところで結果はスパゲッティコードになります。また、どれだけオブジェクト指向パラダイムが使用されても、結果はやはりスパゲッティコードになります。

プログラマとして、私たちがみな回避しようとするこのような状況ですが、これは普通のことで、これは プログラミングの芸術 で、これは プログラマであること が意味するもののひとつなのです!

読みにくいわ!

いやこれ、翻訳力なくて申し訳ないんですが、原文でもこんな雰囲気で... 誤読させようとしてるのかなって感じです。ようするに「他人のコードを恐れること」の章は、ソフトウェア・エンジニアリングの全面否定です。

  • 率直に言ってやばいコード
  • 設計なしでスタートする開発
  • 顧客がリライト工数を嫌がって秘伝のタレ化
  • 初心者に書かせてスパゲッティ化
  • 顧客が途中で仕様変更を連発

これが「よくあるプログラムの自然な成長プロセス」であり、「回避しようとする状況」ではなく「普通のこと」で、「プログラマーとはつまりこういう意味の職業」だそうです。ありがとうございました素晴らしい社畜っぷりですね。

フレームワークオブジェクト指向では救済できないって...? いや、そういう問題に対して、もっとも救済の手を差し伸べてくれているのがこいつらですけど。現代ソフトウェアが複雑さを破綻させずに済んでいるのは、その歴史にオブジェクト指向があったおかげです。複雑さに根性は無効、という当たり前の人道的なエンジニアリングマインドは、オブジェクト指向とともに育ったと自分は認識していますが。

ていうか...

率直に言ってやばいコード → オブジェクト群をフォルダで区切り、別リポジトリにするなどして相互に不可侵に。で、片方に溜まった負債は早くリファクタリングで返済しましょう。今だと、やばいコードには PHPStan を CI 回すのが効きます。PhpStorm の黄色はコミット通しちゃだめです。

設計なしでスタートする開発 → 書く前に設計しないとかありえないので早く逃げて。設計というのはドキュメントを書くことじゃなくて、作ろうとしているものが何であるかという概念を決めること、どのように実現するかの仮説検証のことですよ。材木を買ってきてなんとなく釘を打っているうちに犬小屋っぽくなってきたから犬を入れようみたいなものを開発と呼ぶなら、幼稚園児にレゴで遊ばせておけばいいのです。小学生でも夏休みの工作はまず計画からやるでしょう。

顧客がリライト工数を嫌がって秘伝のタレ化 → タレの消費期限を見積もって、それ以上の契約では事故っても無保証かつ新規と同じ見積もり基準で請求します、を通す。借金してでもデッドラインまでに工数捻出したほうが有利と顧客にわからせる。こうしないと、病気になると知っていながら厳しいことは教えない医者と同じで、言う事言って聞かない相手は (目先の損に見えても、あなたに実力があるなら長い目で見て得だから) 切っていくぐらいがちょうどいいです。

初心者に書かせてスパゲッティ化 → スパゲッティになるほど広いスコープを初心者に任せるの、完全にマネジメントのミスだし経験者の指導ミスですね。まずは、誰の目にも十分と思えるエキスパートを雇い、初心者の技能を一定水準まで引き上げましょう。

顧客が途中で仕様変更を連発 → スパゲッティ化するレベルの変更要求が起きた時点で、これまでの分をフリーズして再見積。見積工数と検証の手間もリセット。当たり前。逆に、問題なくイテレーション回せる範囲の変更で品質が下がるようでは、そもそも部分部分への責務分配の設計に問題があります。骨格を変えるならビル工事は中止、だからって内装を変更したら傾くビルは欠陥建築。

やれやれ... これが「経験と実践」の正体なのかと。ウォーターフォールさえない原始時代ですかね。なんら改善しようという模索なしに、受け入れろですか。受け入れるつらみを後に残さないようにではなく、将来の世代にも不幸の連鎖を続けさせろと。それどこの泥舟大企業のサラリーマンの話ですかね。(泥舟でも大企業ならマシなほうか。零細の大企業病は目も当てられないですよ)

そういう現実がある、はしょうがない、けど、何もしないのがプロだとかそういう言説は、だったら黙ってろこっちのやる気削ぐなとしか。議論する気がないのはどっちですかっていうね。

知識的なことはいくらか間違うかもしれないから目をつぶっても、ここで根本的なメンタリティに問題がある証拠がある以上、もうだめです。アウト。

Wrong Way: PHP - Wrong Way itself

我慢ならないのでひとこと言わせてもらいますよ。

まずは素直に学んでください。オブジェクト指向のセオリー の中でも、パッケージ原則に登場する「単方向依存」という概念は特に重要です。既成のフレームワークによる制約レベルを下げたいなら、依存の向きに最大限の注意を払ってください。何も考えていないとすぐに依存方向が双方向になって結合が密になります。呼ぶ側と呼ばれる側の間に、依存関係反転原則が成り立つ箇所がないかを見つけなければなりません。デザインパターンと言えるかどうかわかりませんが、抽象度の高いライブラリが共通して持っている様式は、とても参考になります。依存の向きの設計には高い能力が求められるわりに、費用対効果の高い仕事とは見られません。なので、プロジェクト規模が大きくなれば、惰性で書き足し開発せずに、機能と層を分け、できるだけ制約の強い既成のフレームワークに一方的に依存できる箇所を抜き出してください。価値の高いプログラムを書くために、プログラムを書かない選択をするのです。

オブジェクト指向デザインパターンフレームワークも、考えずに使うものではなく、むしろ、考える材料として常にストックしておいてください。もし、ドメインモデルが貧血になろうと今回はトランザクションスクリプトが適している、という答えが出たのなら、それはそれでかまいません。それが本当の意味での "Not Always" ということです。見向きもしない、見なくていいから学びもしない、というのは "Always Not" です。

「あらゆる箇所をオブジェクト指向で設計する必要はない」と「すべての領域でオブジェクト指向が役に立たない」を区別できない種類の人は、かわいそうですが、そもそもプログラミング仕事の適性がないと言わざるをえません。どうせ人間はそういうレトリックを区別できないからごまかせると考えている確信犯もナメすぎててダメです。

はー

さてそろそろ皆さん、Wrong Way を読んで笑ったり引いたりできるようになってきたでしょうか。じゃあもう一歩だけ、私の話に付き合ってもらいますよ。

いまみなさんは、ロンドンで Laravel を笑った Symfony ユーザーと同じポジションにいます。笑えば笑うほど、彼らは頑なに殻にこもるのです。なぜなら、正しいかどうかではなく、ある方法で「やった! 自分にもできた」「初めて違和感ない感じだった」という原体験を得た者は、その技術ブランドに強くアイデンティティを持って行かれます。手続き型でうまくやれる人ほど、関数型を理解するのが困難といった現象と同じです。これは不思議なことに、技術の優劣とは関係がありません。

Symfony から言わせれば、app() を通じて気軽にシングルトンにアクセスできるのは邪道の極みです。本来なくても成り立つインターフェースが、Laravel を構成するものとしてひとつのパッケージに収まっていて分離できないのも、スジがいいとは言えません。よりカジュアルで浅いように見せかけながら、パフォーマンスは Symfony よりもかなり落ちています。原理原則に忠実という意味ではよほど Symfony のほうがシンプルで整然としているのに、あれで Beautiful とかいう看板を掲げて、なんやねん言うたもん勝ちかと不愉快になるのもわかります。

おっと Laravel のひと、ここで技術的な言い訳をしたら Wrong Way と同じですよ。まずは素直に、なるほどそれはごもっとも、精進します。と返しましょう。それから、けれどどちらがより多くの人に価値を届けられたかでいえば、けして悪い仕事はしてないと思うんですよ。まあ、フランス人はアメリカを不愉快と思うのはわかりますよ、それはしょうがないよね。どうですか。

(ちなみに誰が言ったかという文脈はとても大事で、Yii ユーザーの自分は「こっちの方が先にロケット飛ばしたのにアメリカちくしょう」と、フランスよりよっぽどこじらせてるロシア側なんで)

そう来れば、こんどは良い意味での笑いが取れるんじゃないですか。この器がラスマスの "They all sucks" だったと思うんですね。ちくしょうバカにしやがって、ってムキになると、技術の優劣が自意識に直結しててカッとなるやつで、負けを認めてるのと同じです。フレームワーク使ってなかったとか異常者だなんて誰も言ってないのに、そういう種類のコンプレックスを抱えてしまいまうやつですよ。

そういう目で、Wrong Way の一派に対して何ができるか、どのようにアドバイスできるか、互いの価値を対等にシェアし合えるか、もっと真剣に考えないといけないように思います。

自分の偏見で申し訳ないのですが、PHP のコミュニティって、ここが平均だろうという線を引くと、その線から大きく外れた 2 つのクラスタが形成される感じがあります。たんに有能/無能という話ではなくて、たとえば、「爆発的ヒットの自社サービスで働く人たち」と「多重下請けの底辺でスーパー火消しやる人たち」だとか、それこそ「ユースケースむちゃくちゃ多い複合システムでセキュアコーディングを矯正してくれるフレームワークが重要」と「フレームワークを使ってシングルサイトを密結合で速くローンチできないと競争力を失う」の対立、だとか。

対立するクラスタは、互いに相手クラスタの底辺を見て「あいつらはバカだ」と言って自分を慰めるんですね。もしかしたら、本当に実務的な理由で Laravel を選んだ人が、用もないのに Symfony が偉いとイキって使う初心者を見てトホホと思うかもしれません。わけもわからずオールインワンなフレームワークに振り回される初心者を見て、Zend 1 の頃から必要最小限のライブラリを入れ替えては代謝してきたシステムのベテランは、今はライブラリ豊富にあるからちょっと落ち着いて自分で組んでみる練習をしてからでも遅くないぞ、と感じているかもしれません。そんな関係なんだから、そもそも絶対的な優劣も、あいつらは神秘主義者だと相手を否定する動機もないんですよね。(PHPPHP を揶揄する人の使う言語の関係も、ずっとそうだったでしょ?)

だいたい、歯ブラシ (=PHP) の使い方でケンカするなんて、おまえら夫婦かよってことですわ。たまには相手の気持ちに先回りして、何かサプライズプレゼントでも買ってやりませんか。

あ、そういえばだいぶ Pinoco をほったらかしにしてたっけ...

フレームワークによる制約とオブジェクト指向の学習コストに対処するには、できないできないと怒ったりわめいたりするんじゃなくて、たとえば、オブジェクト指向を駆使してオブジェクト指向の要らないフレームワークを作ってやるというアプローチもありますよね。

Right Way: As a programmer, create, share and act before rant.

以上、期待以上のものを見つけてしまったというポエムでした。ここ、はてなブログ! イエー!

PhpStorm ユーザーのための IntelliJ IDEA ハンズオン のすすめ

IntelliJ IDEA ハンズオン」、献本いただきました。著者であり、JetBrains 代理店サムライズムの代表でもある山本祐介さん、ありがとうございます。

f:id:tanakahisateru:20171105040830j:plain

http://amzn.asia/0ELFU8Qamzn.asia

JetBrains の IDE 製品は大好物で、普段からとてもお世話になっています。ということで、さっそく読ませてもらいました。

読み始めて真っ先に思ったのは、これ PHPer でも買って全然損ないな、です。

現在の PHP は、文法的にも、技術文化的にも、非常に Java に似た言語となりました。なので、Java を中心に解説されている本だとはいえ、その解説の半分以上は PHP と PhpStorm でも活かせる内容だと感じました。

本書は二部構成になっていて、第一部はこんな感じ...

  1. HTMLとCSSJavaScriptの記述を通じて操作感を練習
  2. 思いつくかぎり最短のキーストロークでエディタ操作、クラス実装
  3. xUnit系単体テストとその実行 + デバッガの操作
  4. Gitをベースにバージョン管理機能の説明
  5. DBのGUIフロントエンドの説明

どうですかこの Java 感のなさ。どの言語ででも応用できますよね。とくに PHP は、Java に似ているおかげですごく無駄がない感じ。

で、この調子で書かれた第一部が、全ページ構成のだいたい 65% を食っています。残りの 35% が第二部、そっちはわりと IntelliJJava に固有な内容になっていきます。

とはいっても、あえて IntelliJ に他の言語のプラグインを入れて使っている人なら、プロジェクトとモジュールの概念や、各言語専用 IDE の機能と Ultimate 用プラグインの関係を把握しないとなので、本当に全編 Java 固有 (というか Java EE) な章は全 10 章中の 1 章だけでした。

これでこの値段なら、ということで買って損はない一冊ですどうぞ。

PhpStormでやるならこう

ってまあ、こんだけ勧めていざ PHP だと話違うやんって苦情出るといけないので、買った人のために、PHP での差分を補足しますね。まだ読んでない人には意味わからんかもですが、買ってからまた来てください。

この本の内容、 PhpStorm でもっともギャップが大きいのは、「MavenやGradle」と書かれたところが「Composer」に置き換わることと、「JUnit」が「PHPUnit」になる部分、そう、基本的なプロジェクト構成です。

前準備

WordPress 等の Composer を使わない文化でないかぎり、まず PHP composer.json support プラグインをインストールしておきましょう。Plugins Browse repositories... から composer で検索します。

f:id:tanakahisateru:20171105023328p:plain

プロジェクト作成

f:id:tanakahisateru:20171105041457p:plain

新規プロジェクト作成では、CLI Interpreter を指定します。初めてPhpStormを起動したときは、ここに選択するものがないと思います。となりの ... ボタンから、自分の PHP のインストールパスを定義するウィンドウで少なくともひとつのインストール先を指定してください。

f:id:tanakahisateru:20171105041920p:plain

空のプロジェクトができたら Terminal を Option + F12 で開きます。ここは、OS標準のコマンドラインシェルと同じです。PHPJava よりも CLI に触れる機会が多いので、ぜひ活用してください。

f:id:tanakahisateru:20171105042226p:plain

Composer を使って PHPUnit を使えるようにします。こうすると、もっともお手軽に composer.json ファイルと PHPUnit が準備できます。

f:id:tanakahisateru:20171105042355p:plain

出来上がった composer.json を開いて、autoload を追加します。先にインストールしたプラグインのおかげで、Composer のキーワードが補完できるようになっています。

    "autoload": {
        "psr-4": {
            "Acme\\FizzBuzz\\": "src"
        }
    },
    "autoload-dev": {
        "psr-4": {
            "Acme\\FizzBuzz\\": "tests"
        }
    },

f:id:tanakahisateru:20171105042625p:plain

うまく動いてくれれば、インテンションアクション (豆電球 / Option + ENTER) から、srctests のフォルダを作成することができます。

ここでいったんプロジェクトを閉じて、再度開きます。

プロジェクト設定

本当は、再起動せずにプロジェクト設定を続けることはできるのですが、あえて、PhpStormの「それなりのcomposer.json があるプロジェクトなら、開いたらすぐに自動設定してくれる」機能を発動させてみます。

f:id:tanakahisateru:20171105043304p:plain

開くとこんなメッセージが表示されるので、すかさず Initialize をクリックします。

f:id:tanakahisateru:20171105043351p:plain

Synchronize IDE Settings with composer.json にチェック、OKします。すると...

f:id:tanakahisateru:20171105043526p:plain

また何か勝手に動作したようなメッセージが。これは、composer.jsonautoload名前空間ディレクトリを対応付けし、PHPUnitの設定も済ませた、という意味です。

f:id:tanakahisateru:20171105043944p:plain

ポップアップで操作した Composer の設定はここにあります。

f:id:tanakahisateru:20171105043748p:plain

ディレクトリ設定はここです。ソースとテストに自動で十分な設定が行われています。

f:id:tanakahisateru:20171105044040p:plain

PHPUnit はここです。Composer でインストールしたものを自動的に認識しています。

「開き直してダイアログでOKしただけ」で、勝手にいろいろできてしまいました。便利ですが、何が起こっているのかわからなくなる恐れもあります。もし「あれ?」と思ったら、主にこのあたりの設定を確認すればいい、と理解してください。

autoload を書き換えたのに composer install を実行していなかったので、このままではまだ自分の書くクラスがロードされません。PhpStorm にやらせましょう。composer.jsonコンテキストメニューから...

f:id:tanakahisateru:20171105044549p:plain

f:id:tanakahisateru:20171105044628p:plain

FizzBuzz実装

f:id:tanakahisateru:20171105044722p:plain

Cmd + N で src 下にクラスを作成しようと思います。ファイルの種類を選択するポップアップが出ている状態で、おもむろに class のようにタイプしてみてください。

f:id:tanakahisateru:20171105044846p:plain

何文字か入力すると、選択項目が絞り込まれます。

この「ポップアップリストからの突然のタイピング」はいろいろな箇所で使えて便利な技です。最近使用したファイル (Cmd + E) やクラスの Structure ポップアップ (Cmd + F12) で素早く目的の項目にアクセスできます。バージョン管理メニュー (Ctrl + V) やリファクタリングメニュー (Ctrl + T) でも有効です。

f:id:tanakahisateru:20171105045405p:plain

プロジェクト設定ができているので、クラス作成ダイアログでは、ディレクトリと名前空間が正しく埋まっています。(うまく設定できていなければ、このダイアログで名前空間が空白になります)

f:id:tanakahisateru:20171105045628p:plain

クラスから Cmd + Shift + T でテストを作成/行き来できるのは Java と同じです。

f:id:tanakahisateru:20171105045727p:plain

PHPUnit 6+ format にチェック。

f:id:tanakahisateru:20171105045814p:plain

f:id:tanakahisateru:20171105045909p:plain

PHPUnit のコードだと認識すれば Cmd + N に PHPUnit Test Method が追加されます。

f:id:tanakahisateru:20171105045933p:plain

現在は test プレフィクス形式ですが、近いバージョンアップで、ここで生成されるコードの雛形をカスタマイズできるようになります。そうなれば @test アノテーション形式のテストも可能です。

テストの実行

JUnit の場合は、テストケースのメソッドにアイコンが表示されますが、PHPUnit には現在アイコン表示がありません。しかし、クラス名やメソッド名を右クリックすれば、実行したいテストだけを実行することができます。

f:id:tanakahisateru:20171105050343p:plain

f:id:tanakahisateru:20171105050348p:plain

Run と Debug を選択することができます。もし PHPXdebug がインストールされていれば、本書の Javaデバッグと同様に、ブレークポイントからステップ実行できます。(ただし PHP には、スタックフレームを巻き戻すようなすごい機能はありません)

f:id:tanakahisateru:20171105050422p:plain

また、phpunit.xml があれば、その右クリックから簡単に、テストスイートすべてを実行できます。

f:id:tanakahisateru:20171105050810p:plain

もともと PHPUnit は、JUnit と異なり、CLI しか持っていません。にもかかわらず、その実行結果をうまく JUnit と同じような GUI で表現してくれます。CLI の出力はそのまま読め、また、表示されているファイルパス + 行番号は、クリックしてすぐそこに移動することができます。

その他

Java では psvm -> TAB であの面倒な記述の public static void main(...) メソッドを作成しましたが、PHP でも pubsfpublic static function () { ... } と展開できます。ほか、pubfprif といった Live Template が最初から定義してあります。

f:id:tanakahisateru:20171105051753p:plain

__DIR__ を使った場合のファイルパスの補完が便利です。また、実在しなければ警告となり、インテンションアクションで後から作成、もできます。

f:id:tanakahisateru:20171105051926p:plain

f:id:tanakahisateru:20171105051933p:plain

Web サーバーのドキュメントルートにあたるディレクトリを Resource Root としておくと、URL を補完でき、また、実在しない場合の警告を活かすことができます。

f:id:tanakahisateru:20171105052443p:plain

これは IntelliJ も共通ですが、バージョン管理中に変更のあったファイルは、コミットダイアログを出すまでもなく、Show Diff (Cmd + D) で大きなウィンドウに差分表示されます。もちろんこれの右側も編集できるエディタです。これ、単に個人的に、コミットダイアログを出さずに済むので気に入っててオススメですよってだけです。

本の章立ての中では目立たないですが、(S)FTP クライアントと REST クライアントの紹介、ありがたいです。見落とさないでください、PHPer ならいつかきっと役に立ちますよ。

IntelliJ IDEA Ultiname の Java 開発者向け機能は、値段だけのことはあって、公式が非常に充実していますが、PhpStorm は、サードパーティプラグインがかなり活躍します。各言語の中で、PhpStormはPHPの方言みたいなののプラグインが、もっとも多いんじゃないでしょうか。なので、自分のフレームワークや重視したい技術キーワードで、まずはいちどプラグインを検索してみてください。


差分でわかりにくいって人には、少し古いですが、PhpStorm 2016.3 入門動画を作りました - なんたらノート第三期ベータ が参考になるかもしれません。カバーする領域だいたいかぶってます。

...で

まあじっさい、編集も設定も、こんなにスムーズに使えることはないです。わりと毎回試行錯誤だったり、無駄な操作してたりです。

もし「うわ... こんなにうまくいかない」と思っても、最初はぜんぜん大丈夫。とにかく意図したとおりにコードを入力できさえすれば、それだけで十分で、あと IntelliJ は勝手に役立ってくれてて、いつの間にか助けてくれてるんで。

でもせっかく高い買い物したんなら、あとたった2894円だけ出して近道行ったほうが、お得じゃないでしょうか。ライセンスの有効期限もあることだし。

PhpBenchバージョンの参照ベンチマークを書いてみた

これ

tanakahisateru.hatenablog.jp

だいぶ古くなって、PHP7の事情とか反映できていないのもあって、計測し直してみるかと… で、そのときたまたま GitHub - phpbench/phpbench: PHP Benchmarking framework を使ったら、これが思いのほかよかった。というわけで GitHub に上げました。

github.com

PhpBench はベンチマークフレームワークです。基本、PHPUnit のように、ベンチマーク用のクラスに各シナリオを表すメソッドを書けばOK、あとは CLI から vendor/bin/phpbench run ... で引数にオプションいろいろ指定して実行。

ここがよかった 1

setUp() 的なメソッドをアノテーションで指定できます。事前条件の準備がベンチマーク結果に影響しないよう、逃がせるのがいいです。

<?php
/**
 * @BeforeMethods({"init"})
 */
class PhpReferenceBench01Nop
{
    protected $bigArray;

    public function init()
    {
        $this->bigArray = range(1, 100000);
    }

他に、PHPUnit@dataProvider のような ParamProviders もありました。複数パターンやるとき使えるますね。

ここがよかった2

ウォームアップや反復回数の指定が充実していました。

コマンドライン--rev=100 --iterations=10 とすれば、100回分の処理時間を10セットやった平均、みたいにお手軽にやれます。

が、それだと「これ、やってること浅いから多めにやって平均取りたいな」「これ単価が高いから他と同じ回数やるのはしんどい」といった知識が残らないので、メソッドのアノテーションでこうしています。

<?php
class PhpReferenceBench01Nop
{
    /**
     * @Warmup(10)
     * @Revs(100)
     * @Iterations(10)
     */
    public function benchNoRef()
    {
        $result = PhpReference::nopNoRef($this->bigArray);
        unset($result);
    }
  • @Warmup(10) = メモリ断片化やコードキャッシュの影響を受けないよう、10回素振りしてから計測に入る
  • @Revs(100) = 100回分の合計を1回として評価する
  • @Iterations(10) = 10セットやってその平均/最高/最悪を取る

以前のものは、100万件のデータで1回でした。今回はデータを10万件に下げ、その代わり、Revsに100や50を指定して結果を安定させました。ロジックに影響することなく、実行のされ方 を独立して調整できるのが嬉しいですね。

ここがよかった3

結果表示の調整が簡単。Dockerを使って7と5を同時にやりたかったので、自分はPHPのバージョンを表示し、逆に、誤差の出にくいベンチなので平均実行時間以外を隠しました。こんな感じ https://github.com/tanakahisateru/php-ref-bench/blob/master/phpbench.json#L4

f:id:tanakahisateru:20170508191238p:plain

ていうか… シーケンシャルな配列のPHP5と7の差、変更時のコピー速度で約9倍、メモリでざっと3倍です。無駄コピーもなくなってる。どうしよう…参照のほうが遅いでしょって言えない…

Vagrantプロバイダの共有フォルダ性能比較

Vagrant を使って仮想環境で開発するさい、共有フォルダ (Vagrantfile の synced_folder 設定) が仮想マシンの通常のフォルダに比べてものすごく遅いという問題があります。そこで、現実的な PHP のアプリケーション (実際に運用しているWebサイト) を使って、実際に共有フォルダの影響がどのくらいあるかという調査をしてみました。

編集したソースコードの同期を簡単にするには、やはり共有フォルダを使うのが一般的でしょう。変更するたびに手動でファイル転送する方法だと、面倒なうえ同期ズレが心配です。ですが、PHP はリクエストのたびにソースファイルをスキャンします。つまり、十分に速いファイルシステムにソースが置かれていないと、毎回その I/O のペナルティを受けることになります。

(他の言語ラインタイムだと、いちどメモリ上にコードが乗ってしまうと、以後プログラムをロードするためにファイルシステムにアクセスする必要はほとんどありません。この話はあくまで PHP でとくに影響が強いという話です)

自分は、とくに意識しなくても共有フォルダが速い Parallels プロバイダを使っていたのですが、Parallels は 11 以降、年間サブスクリプションで購入する Pro バージョン以上でないと、Vagrant を使えない仕様になってしまいました。他の人にも買ってもらえば済むという簡単な話では済まなくなったので、仮想化環境を再検討しようと思い、VirtualBoxVMware を、NFS を併用した場合も含めて比較してみました。

構成

  • CPU x2
  • メモリ 1.5GB
  • ランタイム = PHP 7.0
  • Web = Nginx 1.10 + php-fpm
  • DB = MySQL 5.6
  • キャッシュ = Redis 3.2

Web フレームワークは Yii 2.0 です。本来どのあたりの性能かというのはこちらを参考に: GitHub - kenjis/php-framework-benchmark: PHP Framework Benchmark

MySQL との I/O が重くなるビューは、HTMLの部分レンダリング結果を Redis にキャッシュしています。とはいえ、MySQL にはそこそこ (8 〜 20 クエリ/ページ) アクセスがあります。MySQL のストレージは VM の中にあります。他に、水平分散したとき Web サーバー間で共有すべきデータは Redis にあります。が、ログ等の挙動に影響しないデータのは一部共有フォルダに書き込んでいます。

結果

f:id:tanakahisateru:20170313135727p:plain

  • Front Page = おすすめコンテンツ系のリンクありのトップ
  • Nginx Single File = どのページでも参照しているCSS
  • Privacy Policy / Contact (SSL) = あまり I/O のないページ、SSLの有無で比較するため
  • Contents List = 主だったコンテンツの一覧、Pagination でスクロール
  • Contents Detail = とても複雑なコンテンツ詳細、キャッシュががんばってる

f:id:tanakahisateru:20170313142738p:plain

f:id:tanakahisateru:20170313142809p:plain

wrk で計測。単位は秒間レスポンス数です。静的ファイル配信だけは桁が違うので別のグラフにしました。

完全に予想外だったのが VMware のデフォルト共有フォルダです。基本性能 Parallels と遜色ない商用VMと思いきや、素の共有フォルダの性能は VirtualBox より遅くなりました。Nginx に CSS を要求して秒間30しか出ません。とんでもない遅さですね。

この結果、共有フォルダを NFS にしているかどうかが支配的だということがわかりました。ParallelsNFS を使うかどうかに関係なく同程度の性能が出ているので、はじめから Mac OSAPI を使わずに、NFS 相当のことをしているのだというふうに推測できます。

NFS を使ったパターンの中で比べると、やはり VirtualBox はやや劣ります。が、誰でも無償で使えるものという汎用性で考えると、十分な性能になっていると思います。あまり CPU を使わない Nginx の静的ファイル配信では大きく差を空けられていますが、HTMLページのレスポンスを考えれば、秒間1000もいらないですしね。

結論

というわけで、Mac の PHPer が求めていたのは有料製品ならではの VM チューニング、ではなく、単に共有フォルダの NFS 化だったというお話でした。

NFS - Synced Folders - Vagrant by HashiCorp

Vagrantのshare folderが遅い時はNFSを使うといいです - Qiita

とはいえ、MS 系ブラウザでもフロントエンドのテストをすることは多々あるでしょうし、MacWindows をストレスなく動かせるものは準備しておくのが良いでしょう。やりたい人は、それと同じ製品でサーバーの仮想化までやってしまってもいいかもしれませんが、それはオプションと考えておくのが無難です。

Mac で開発する PHPer のパターン、こんな感じに落ち着くでしょうか。

まあ、PHP 以外の、秒間1000レスポンスの世界であれば、商用VMの性能影響が大きくなるので、また違った指針になるかもしれませんが。

PhpStorm 2016.3 の例外インスペクションどうしたらいい?

こんな記事を書いたんですが、正直、PhpStorm で RuntimeException の後処理がうまく表現できなくて困っています。

qiita.com

Runtime も含んでどんな例外でもそのままスローだと @throws \Exception が必要って言われる。主語がでかすぎてやだ。 f:id:tanakahisateru:20161208192012p:plain

Java は構文的に、例外の捕捉忘れがないことが明確にわかるので、この catch には RuntimeException レベルのものしか来ないはずってことがハッキリわかる。なので同じことやっても throws Exception が必要とは言われない。

irof.hateblo.jp

いろふさん thanks

横流しするだけなのに throw と書くからそこに反応するの? ってことで、try-finally で書いてみた。そしたら今度は到達しない行があることを認識してくれない。これバグ?

f:id:tanakahisateru:20161208192006p:plain

どうも try-catch の構文があってようやく大域脱出があることを認識してくれる。けど IDE が未成熟なせいで回避するために実効力のあるコードに余計なものが入るのはいや。(doc コメントならしかたないけど)

f:id:tanakahisateru:20161208193443p:plain

究極的にはこれが正解? しんどい。

f:id:tanakahisateru:20161208192005p:plain

うーん、せっかくあるこれオフにするか、それとも... f:id:tanakahisateru:20161208201854p:plain

f:id:tanakahisateru:20161208202153p:plain

ちゃうねん、Exception 型の throw は RuntimeException | LogicException を意図した可能性があるから 単に無視して欲しい。@throws \Exception なんて書いても、PHP だと誰も幸せにならないから。

うーん... https://youtrack.jetbrains.com/issues/WI?q=throws%20%23Bug

まあ、アプリケーションレベルでは RuntimeException まわりのややこしい問題扱う時点で負けで、それは横断的な関心として分離するのが正解なんで、今のややこしい状況は逆に関心分離のチャンスかもですけどね。

f:id:tanakahisateru:20161208204837p:plain

PhpStorm 2016.3 入門動画を作りました

祝 PhpStorm 2016.3 リリース 2016/11/24、な感じのネタです。

PhpStorm 2016.3 is now released! | PhpStorm Blog

先日、株式会社ロックオン 様の社内勉強会に招待されて、PhpStorm のワザをいろいろ共有してきました。EC-CUBE3のソースを開いてインスペクションかけては「あ、ここ型検査効いてませんね...」「依存はmixed型で取ってくるんじゃなくて型を持ったフィールドに注入しないと...」とかひどいことやりました申し訳。

そのとき簡単な PHP のプログラムを通しで作るデモを雑に作っていったんですが、これ実はいい出来なんじゃないかということで、ちゃんと仕上げて公開したいなと思い...

というわけで、約40分の PhpStorm 初心者向け (PHP 初心者とは言っていない) ライブコーディングですどうぞ。


PhpStorm 2016.3 live coding demo

動画でやっていることはだいたいここに文字で書いてあります。説明は 2016.2 用ですがご容赦。

おまけで Xdebug も...


PhpStorm 2016 3 xdebug

余談: EAP で作業してたらちょうどその最中に PhpStorm 2016.3 正式リリースの通知出てちょっとーってなるつらみとうれしみ。

PHPのGCは循環参照を回収できる

PHPで親子関係のオブジェクトが相互に参照を持つ ($parent->children がありかつ $child->parent がある) ケースの話をしていたとき、循環参照の話題が出たのでふと気になって調査してみました。

結論からいうと、PHPは5.2まで、単純な参照カウンタ方式のGCのみを採用していました。5.3からは、参照カウンタ方式に加えて、循環参照を回収するGCも併用するようになりました。

PHP: 循環の収集 - Manual

PHPの変数は、基本的には参照カウンタが0になった時点でメモリを解放します。が、それだけでは、循環参照があると0までカウンタが落ち切らない変数が発生します。かといって、毎回循環参照をチェックするとパフォーマンス低下が発生します。そこで、GC監視下の変数が一定数 (コンパイル時のGC_ROOT_BUFFER_MAX_ENTRIES定数、通常は1万) を超えたときに初めて、循環参照を片付け始めるという戦略を取ります。

この循環参照用のガベージコレクタを使うかどうかは、 php.ini または gc_enable() / gc_disable() で切り替えられます。普通は有効にすべきですが、試しに無効にしてみて、循環参照がどうなるかを確認してみました。

CLIで実行時に php.ini の設定を一部変更して比較。この結果をグラフにしてみました。

f:id:tanakahisateru:20160628150429p:plain

循環参照GCを無効にすると、いちど確保されたメモリがいつまで経っても解放されないのがわかります。有効だと、だいたい4.5MBほど使ったところで循環参照がGCされて、650KBまで落ちています。

長時間かかるバッチ処理などで、複雑なOOPをすると参照カウンタが落ちないんじゃないかと心配する必要があったのは5.2までの話でした。今はもう忘れていいですよ、という確認でした。