2015年3月8日日曜日

PHP 好きとか嫌いとかじゃなくて、「危なっかしい」んだよな

小生はアンチPHPですが、「好きか嫌いか」で言われるとどっちでもありません。副流煙みたいなものですよ。故に「嫌いだからPHP使わないんだろ!」とか言われると心外です。 そもそもPHPが好きって有り得るのか?原作者自身が歯ブラシと言ってる代物なのに?

日経ソフトウエア4月号でPHPの紹介記事がありました。
  • XAMPPのお陰で導入が楽
  • HTML文書にPHPのコードを書き込める
  • 変数を宣言しなくてよい
  • XAMPPにはMySQLが入ってる(のデータベース管理システムを簡単に使える
  • IDEも入手しやすくなってきた

これら自体は否定はしないし、PHPの魅力ではあるでしょう。しかし、これらの殆どがバグを産む原因でもあるというジレンマ。

PHPの原作者自身が「歯ブラシだ」と言ってるのでそれ以上を望むのは無意味でしょう。便利さが全て。歯ブラシは頑固な汚れを取るのにも重宝します。

PHPが致命的にマズイと思ってる理由は、現状では次の点です。

HTMLの「間」に書く言語であることがマズイ。


これのお陰で「Webアプリケーションがササッと作れる」それは否定しない。しかしそれは工夫しないと非常に読みにくいプログラムが出来上がる。別の側面から言えば、「標準出力に出力する方法が多すぎる」

  • include/requireでHTMLファイルを読む
  • echo等
  • PHPタグを一旦閉じて開ける
どいつもこいつも見にくく、そして「取りあえずソレで動いてしまう」が故に「テンプレートエンジンの利用に踏みきれない」理由でもある。

「Webアプリケーション」なら致命的になることはあまりありません。それなりに動く。ところがRPCサーバを作ろうとすると途端に邪魔になります。warningメッセージがぼろぼろレスポンスに混ざるので、リクエスト側プログラムがparseに失敗する。

display_errors=0にしとけって?その通り。そしてそれはPHPの標準設定が初心者向きであることを意味しています。

include先にPHPプログラムを書く場合は、PHPタグを「閉じない」場合が大多数でしょう。うっかり閉じると、その直後の改行を勝手にレスポンスに入れちゃって、リクエスト側プログラムがparseに失敗する。

前述の通り「include先が「PHPプログラムである」こと」をPHPは既定してません。HTMLでもテキストファイルでも構わないのです。故にぱっと見何のためのincludeだか判らないことになります。もちろんフツウに作ればそんな馬鹿なことにはなりません。でも駄目プログラマがそうすることをPHPは止めません。

しかもこの仕様のお陰で、PHP の include/requireは、他の言語とくらべてもかなり独特の挙動をします。「コンパイル時にソースプログラムをincludeの位置に展開してる」わけでは無い」のです。

実はPHPのバイトコードに、include命令があります。includeは実行時展開なのです。そりゃそうだ。でなければファイル名に変数を使われた時に対応できなくなる。 これは動的にHTMLを切り替える時に必要な挙動です。アッタマイイデスネ。

以上の小生の主張を、裏付けてくれるかのようなソースがありました。
かの有名なWordPressそのcaptchaプラグイン。

http://plugins.svn.wordpress.org/captcha/tags/2011.2.11/captcha.php

いつの間にかPHPが終わって、いつの間にかHTMLが始まってる。
それだけが原因では無いでしょうが、そりゃセキュリティ問題も出るでしょうよと

http://www.itmedia.co.jp/enterprise/articles/1503/03/news092.html

勝手に型変換するのがマズイ

PHP7.0で漸く改善しました。

変数の中身型をあえて特定しないスタイルをダックタイピングとか読んだりしますが、PHPの不味さはソコではない。勝手に型変換をするところです。数値と文字列を読み替える程度ならまだいいでしょう。クラスや配列まで相互変換できてしまうのです。

http://php.net/manual/ja/language.types.type-juggling.php

型の間でキャストを行う際の動作は、必ずしも明確ではありません。 詳細については、以下の節を参照ください。
ここ、笑うところですよ。プログラム言語の挙動を明確に定義できないとか、どういうことかって話よ。

初心者向きとしては悪くない挙動です。中級者になると途端に裏切られる。トリプルイコールを徹底するのも手ですが、今度は型違いでエラーすら起こらなくなるので、よほど慎重に書かないと迷宮入りが早くなる可能性がある。

関数呼び出しの引数が足りなくてもwarningにしかならないのがマズイ

PHP7.1で漸く解消しました。

Warningにしかならない。仮引数にはシレっとnullが入る上に、これは0にもfalseにも空文字にも勝手にキャストし得る値です。

PHPのWarningは、他の言語であればFatalであるものが殆どです。この点を踏まえて、set_error_handlerで自前のハンドラを用意して選別して、場合によっては例外として投げ直して死なすのが「プログラマの生活の知恵」ってところでしょう。こんな使いにくい歯ブラシがありますか?

関数のタイプヒントには極力array/クラス名を指定するべき。これであれば引数が足りない場合にCatchable fatal Exceptionになります。

スーパーグローバル変数のお陰で、外部からの入力をプログラムの奥で何時でも参照できるのがマズイ

典型的な例を発見

Analysis of the Fancybox-For-WordPress Vulnerability
 http://blog.sucuri.net/2015/02/analysis-of-the-fancybox-for-wordpress-vulnerability.html

WordPress本体が頑張っても、プラグインがろくにチェックもせずに$_REQUESTを使ってればこうなるという好例。

もちろん $_REQUESTとかを一旦unsetして再定義すれば、非スーパーグローバルになるので比較的安全に扱えます、が、そこまでさせるなって話ですよ。


何れの項目に関しても、声を大にして言いたいのは、

いや、そこは例外吐いて死んどけよ。

(もちろんHTMLの話は違いますよ。いちいち言わなくても判りますよね :) )
そもそもphp.iniとかで expertmode = trueとか出きるようにしとけと。

PHPの本質的なヤバさは、
  • 駄目プログラムを書くと、他の部分が容易に巻き添えを食うこと
  • それを防ぐのコストが非常に面倒くさいこと
グローバル変数が、名前空間を貫通することに気づいたときは吹きましたよ。関数名/クラス名のためだけかよ orz この為に迷宮入りしたこともあり。



と言うわけで、「嫌ってる」んじゃなくて「嫌がってる」んだよ!と言おうと思ったけど、どうやら前者は気分で、後者は態度を表現するだけの違いらしい。あまり変わらなかった。