Help us understand the problem. What is going on with this article?

モダンPHPアンチパターン

More than 5 years have passed since last update.

アンチパターンなので、見出しの内容はすべてバッドノウハウです。

前に書いたやつ

関係ないけどこれも: シェル、ターミナル、コンソール、コマンドライン

追記: 本文中でとりあげた「怖い話」について、ちゃんと説明しました
PHP - namespaceとBOMに何の関係があるのさ - Qiita

ファイルの最後に?>を書く

PHPコードは<?phpで始まり?>で締める。それがPHPの常識(キリッ

……そんなことはもう綺麗さっぱり忘れよう。PHPはテンプレートエンジンではあるが、Webアプリケーションを書く上では、もはやテンプレートエンジンとしての機能は求められなくなりつつある。

不要な?>を書いてはいけない理由は明確で、<?php … ?>の外にあるコードは出力されてしまうからだ。つまり、誰かが?>の直後に余分な改行を入れてしまったら、わかりにくいタイミングで意図しない改行が出力されることになる。

もしあなたの会社のプロジェクトに不要な?>が書かれたコードがあるならば全滅させよう今すぐに。いま問題がなくても将来問題になるかもしれないので後顧の憂は断つべし。

php.netのリファレンス(例:implode)のサンプルコードには几帳面に?>が書いてあるのは良くないと思ってる。これのせいでPHPコードの最後に?>を書きたがるひとが絶えないのでは?

配列はarray()で作る

PHPのarray()については、各位複雑な感情があるだろう。

特にほかのプログラミング言語から移ってきたひとは「array()? 関数呼び出しかな?」→「えっ何これ気持ちわるい」→「まあPHPだし普通だよ」と思考の変革を迫られたことだろう。

もう無理はしなくていい。

$before = array(
    array(
        'en' => 'apple',
        'ja' => 'りんご',
    ),
    array(
        'en' => 'orange',
        'ja' => 'おれんじ',
    ),
);

$after = [
    [
        'en' => 'apple',
        'ja' => 'りんご',
    ],
    [
        'en' => 'orange',
        'ja' => 'おれんじ',
    ],
];

2012年3月にShort Array Syntaxが追加されたPHP 5.4.0がリリースされ、人類はうんざりするようなarray()から解き放たれた。PHP 5.3はもう居ないんだ1

php-short-array-syntax-converterを利用すれば、既存コードのArray Syntaxを一気に置換することが可能です。

PHPの安定版は5.4

人類の魂に平穏をもたらしてくれたPHP 5.4系も、9月14日をもってセキュリティサポートが終了される。

Branch Initial Release Active Support Until Security Support Until
5.4 1 Mar 2012 / 3 years, 6 months ago 14 Sep 2014 / 1 year ago 14 Sep 2015 / 8 days ago
5.5 20 Jun 2013 / 2 years, 2 months ago 10 Jul 2015 / 2 months ago 10 Jul 2016 / in 9 months
5.6 28 Aug 2014 / 1 year ago 28 Aug 2016 / in 11 months 28 Aug 2017 / in 1 year, 11 months

(PHP: Supported Versions 2015年9月22日時点の内容を引用、未来の内容は将来的に変更されることもありうる)

PHP 5.4のサポートは終了した。今後、PHPの開発グループからセキュリティフィックスがリリースされることはない。

PHPのライブラリ管理はPear

残念ながらPear系のコミュニティリポジトリは壊滅しました2

PEARリポジトリは古い、PEAR2夢に溢れてたけどリポジトリが少なすぎる、Open PEARはみんな寄り付かなかった…

Packagistは最近アクティブなPHPライブラリのリポジトリ、Composerはライブラリの依存関係を管理するパッケージマネージャーである。

いはゆる「モダンPHP」のライブラリはComposer+Packagistのエコシステムに依存するものが多い。

rubygems.orgやnpmなどと比較してもPackagistは後発なだけあって、Gitなどのバージョン管理などと良く統合されてる。GitHubやBitBucketなどのAPIと連携し、それ以外のサービスや独自サーバーのSubversion/Mercurial/Gitから公開することも可能。

ComposerはPHPライブラリのみをサポートするので、C拡張をインストールすることはできず(依存関係を明示することは可能です)、Composer/PackagistはPECLを置換するものではない。PECLは現役。

&参照(リファレンス)を使った処理は効率的

嘘だッ!!!!!!11

PHPの言語機能のなかでもわかりにくく、説明しにくく、誤解されやすい最たるもので、地雷原のように罠がある。

リファレンスの罠を避けながら慎重にコーディングすることも不可能ではありませんが、バグが混入しにくいPHPコーディングを志向したいならば、小手先のテクニックを覚えるよりも あらゆる&演算子を排斥するべき です。

古いPHPにおいては、値渡しよりもリファレンスを使った方が僅かにパフォーマンス面で優れた時代もあったようです(伝聞)。しかし、PHP処理系の最適化の積み重ねで差は埋まり、一般的なケースでは速度やメモリ使用量での優位性はありません。そして、都市伝説だけが残りました。

foreachとリファレンス

リファレンスを使ったコードは基本的に悪手ですが、「多段ネストした配列の内部を多重foreachしながら内容を書換する」ようなコードでは、リファレンスを使った方がシンプルで綺麗なコードに見える… ことが ないわけでもない です。

しかしそれは錯覚に過ぎません。多重ループのコードは本質的に複雑でわかりにくいので、リファレンスを利用する以外のリファクタリングを検討すべき機会です。

パフォーマンス

冒頭で「嘘だッ」と書きましたが、正確には配列の要素数や内包するデータ量、ループ内での書換回数などによっては、リファレンスを使った方が高速に動作することもありえる。

しかし、パフォーマンスチューニングの鉄則は 推測するな、計測せよ である。「いっぱいループするし、なんとなく速くなりそうな気がする」ではいけない。チューニングは計測して問題を確認して、初めて着手する価値がある。

その箇所がボトルネックではなく、リファレンスの方が「ちょっぴり速い」程度であれば、安全なコードを捨てて高速化する価値は乏しいだろう。あるいは、ごく稀にしか遅くならないような場合にどうやってパフォーマンスチューニングするかはケースバイケースである。

namespaceを利用しない

名前空間の効能について今回の記事では詳説しない。しないが、ひとつ身の毛もよだつ怖い話を紹介しよう。

これは伝聞ですが、むかしとある有名ブラウザゲームのAPIをハックするひとが居りました。あるときJSONパーサーでパースできないAPIが存在することに気がつきました。先頭に奇妙なバイトが含まれてたのです。よく見ると、それはBOMだったのです…3 ぞぞーっ

PHP: 名前空間 - Manualの関連記事をよく読んでいただきたい。

追記: 詳しく説明を書きました
PHP - namespaceとBOMに何の関係があるのさ - Qiita

ファイルごとにrequire/includeを書く

だってめんどくさいじゃんry

めんどくさいのはさておいて、require/include/require_once/include_onceは書いた瞬間にファイルが読み込まれるのに対して、オートローダーを利用した場合はuseを列挙したとしても、 そのクラスが実行に必要になるまでファイルは読み込まれない

ファイルが読み込まれること自体は些細といへば些細だが、依存関係に嵌まることが減らせるのはメリットだ。また、Composerでインストールしたライブラリははじめからオートローダーに登録されてるので、自分でinclude_onceなどを書いて読み込む必要はない。

基本的にSPLオートローダーを利用すれば良いのだけれど、それすらも直接利用せずにComposerのオートローダーを利用するのが簡単です。事情があってComposerを利用できない場合や、変則的なプロジェクト構成でComposerのオートローダーの適用が容易ではない場合4は、PHP Autoload Builder(theseer/Autoload)を利用するのが便利。

可変変数(variable variables)

ええと、これあんまり使ってるひと居ない気がするのだけれど…

$hello = "world";
$$hello = "cup";

echo "{$hello} {$world}", PHP_EOL;
// world cup

説明をすると、$helloの中身は"world"なので、$$hello$worldと同じ意味になる、って寸法なんだけど…

使ったらめんどくさいことになるのは目に見えてるのでやめよう、以上のことはないです。

可変プロパティ

可変プロパティも、基本的には利用すべきではない5

error_reporting(~E_NOTICE)はべんり

や、やめろー やめてくれーっ

これを実行すると、未定義変数を参照するとnullになったり、配列の未定義インデックスを参照しようとするとnullが返ってきたりする。

未定義インデックスの参照はRubyではふつうのことだけど、それすらも個人的には好きではないし、バグの温床だと考へる。

それはそれとして、NOTICEが出ないように安全に書くには…

// PHP 5.6
$x = isset($ary['x']) ? $ary['x'] : 123;


// PHP 7.0
$x = $ary['x'] ?? 123;

PHP 5.6めんどくさいし、同じことを二度書かなくちゃいけなくて逆に危なそう… PHP 7.0では人道的な短さで書けるようになるので、各位PHP 7.0がリリースされたらすみやかにプロダクション投入できるように、積極的に根回しがんばろう。

PHPはテンプレートエンジン

PHPのソースコードでは<?php ?> で囲まれた部分以外は標準出力される。

PHPは最初期からHTMLに埋め込む形式で利用されてきた。しかし、現代のPHPアプリケーションではセキュリティやテスタビリティの面など、いくつもの問題から推奨できない。

awesome-php #Templatingには8つのテンプレートエンジンが紹介されてる。

  • Twig - A comprehensive templating language.
  • Twig Cache Extension - A template fragment cache library for Twig.
  • Mustache - A PHP implementation of the Mustache template language.
  • Phly Mustache - Another PHP implementation of the Mustache template language.
  • MtHaml - A PHP implementation of the HAML template language.
  • PHPTAL - A PHP implementation of the TAL templating language.
  • Plates - A native PHP templating library.
  • Lex - A lightweight template parser.

ごく小規模なWebアプリケーション6であれば利用することもないではないが、ある程度の複雑さと更新頻度があるならばセキュリティのリスクなどを勘案すれば候補に挙がらないだろう。

コンストラクタはクラス名のメソッド

なんかもうめんどくさいし、古いスタイルなのでやめよう(提案)

<?php

class Hoge
{
    public function Hoge()
    {
    }
}

詳しくはPHP: コンストラクタとデストラクタ - Manualを参照のこと。代りに__construct()を定義する。

古いスタイルのコンストラクタはPHP 7.0で非推奨になり、将来のバージョンでは削除されることが予告されてる。

PHPだからURLは.php

クールなURIは変わらない -- Style Guide for Online Hypertextは1998年にティム・バーナーズ・リーが書いた文章だ。もしあなたが一般ユーザー向けのWebサービスを運営してるならば、URI設計は直視しなければいけない課題だ。

(逆に、クローズドなAPIなら恰好悪いURLでも特に関係ないだろう)

あなたのWebサービスがPHPで書かれたか、Perlで書かれたか、はたまたJavaかC#かBashスクリプトか、といった情報は利用者には一切関りのないことだ。攻撃者のための判断材料にすらなるかもしれない7

http://example.com/hoge.phpのようなURLのページの多くは、Webアプリケーションフレームワークを採用したものではないだろう。

.phpをURLに含まないWebアプリを開発するにはフレームワークに移行するか、すくなくともルーターを導入する必要があるだろう。それを痛みを伴ふだろうし、ひょっとするとPHPを捨てる決断を迫ることになるのかもしれない。

長くユーザーに提供していくサービスならば8、いつかこの問題とも向き合ってほしい。

あとがき

古いバージョンのPHPとバッドノウハウを捨てさって、新しいPHPで生きていきたいですね ヾ(〃><)ノ゙


  1. まだPHP 5.3以前を使ってる業務現場が数多く存在したり、独自セキュリティパッチ適用を謳って旧バージョンのPHPをサポートするベンダーがあることは承知して居ります… 

  2. PHPUnitは2014年末にpearのサポートを終了した End of Life for PEAR Installation Method · sebastianbergmann/phpunit Wiki 

  3. Unicodeにおいて、先頭バイトにあってエンコーディングを明確にするためのデータのこと。UTF-8には基本的に不要だが、Windowsの一部のテキストエディタにはUTF-8にBOMを付ける奇癖があることは著名 

  4. 複数のプロジェクトがひとつのGitリポジトリに同居し、それまでの経緯からクラス構成に一部重複のあるものがある。namespace/クラス名がユニークにならない場合、PSR-4は利用できない 

  5. 今回の記事では触れないが、ごくわずかな例外もある 

  6. pixiv.meはフォームなくクエリパラメータ受け取らない単機能のWebサイトで、HTMLとJavaScriptを含めて100行未満の1ファイルのPHP。 

  7. ピクシブなどはPHPとRailsを採用してることは公言してるし、知られても問題ないように十分な対策をとるのが最善であることは言ふまでもない 

  8. membar_illust.php… けふんけふん。 

tadsan
僕に警備する自宅をください。Emacs初心者。Rubyist。 全ての投稿された記事は別段の表記がない限りはCC 3.0 BY-SA https://creativecommons.org/licenses/by-sa/3.0/deed.ja で二次利用できます。 記事中に含まれる全てのコードスニペットの著作権は抛棄するので、煮るなり焼くなりお好きにどうぞ。
https://tadsan.github.io/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away