PHP
PhpStorm
Phalcon
Zephir
PHPExtension

【独断と偏見】C#erでも許せるかも知れないフルスタックPHP

More than 1 year has passed since last update.

なにかと dis られる言語 PHP ですが、dis られる要因としては

  • 利用者が多い(少なければそもそも話題にならない)
  • 言語自体に開発者を導くコンパスがない
  • 結果的に負の遺産が多く築かれる

ということがあると考えています。
PHP をうまく使ってご飯を食べている身としていくら dis られようと何ら弊害はないのですが、PHP は OSS で安定性・拡張性・言語自体の開発の活発さを保っておりコミュニティもとても活発な言語なので、今回は「PHP を使うとしたらこうやるといいよ」という方向性で書いてみようと思います。

あくまで個人の主観に基づいた記事です。ご了承下さい。
あくまでウェブアプリケーションを開発することを想定しています。 (それ以外の用途があるとは思いませんが)

その前に、なぜ PHP を使うのか

PHP は書けば動くから簡単だよ という罠

この言葉を耳にしたのは専門学校時代です。幸い私は、Java や UML の授業を通してオブジェクト指向や MVC の概念を理解することが出来ました。これらの概念は Java や C# の前提となっており、Java や C# を学べば嫌でも習得することになります。日本語を学ぶ際に日本の文化や常識が密接に関わってくるのと同様です。

対して PHP は、本当に書けば動きます。オブジェクト指向の機能を持っていますがそれはごく最近のことで、前提ではありません。テンプレートエンジンに始まった PHP の歴史に取り残されたコードはいくらでも見つけることができます。また、それだけ多くの常識が生まれてしまい、死ぬことができずに彷徨っているというのが PHP の現状です。

「書けば動く」というのは本当に罠でしかありません。「書けば伝わる」日本語がないのと同じです。我々が国語を学んだのと同じように、意図を表現することを繰り返していかない限り、ほぼ使い捨てのプログラムを書いては引き継ぎ担当者や1年後の自分の首を絞めることになります。「動けばいい」という人にはできる限りの手を尽くして近寄らないようにすべきです。

それでも PHP を選択するメリットがあるとき、PHP は裏切らない

PHP は純粋に言語でしかありません。例えば C# なら .NET という強力なバックボーンがあるし、WCF, WPF, ASP.NET などのフレームワークがあります。PHP は開発効率を高め意図を表現するための武器を用意してくれません。また動的型付け言語という特性上、開発時の型安全性を維持する仕組みも提供してくれません。

ただし PHP は拡張できます。Pear や PHPExtension により不足している武器を開発者が補う事ができます。MVC の概念を実現したり、関数型プログラミングのエッセンスを追加することができます。開発者が拡張のためのコストを抑えられるようになれば、ライセンス料金の必要な ASP.NET よりも低価格で、一定の品質と効率を保つことができるようになります。

要約するとこういうことです。
PHP という案内役のいない言語を使わざるをえない場合、最も使いやすい状態にできるかどうかにかかっている。

ということで、とにかく PHP を C#er でも許せるくらい使える言語に仕上げるためのパーティーメンバーを独断と偏見に基づき紹介していきます。

member.1 統合開発環境

PhpStorm を使いましょう。

なんといってもあの R# を生み出した JetBrains 製の IDE です。
強力な静的解析機能により、ある程度の型安全性の維持を支援してくれます。

UI が英語なのはデメリットかもしれませんが、これくらいの英語でへばっているようでは PHP はやっていられません。
Node.js の開発にも使えますし、有料ではありますが正直安いと思います。

PHPdoc は絶対

PhpStorm の力を最大限引き出すためにも、PHPdoc コメントを書くことを必須と考えるべきです。
PhpStorm はある程度自動的に型推論してくれますが、PHPdoc を記述することで明示的に伝えることができます。

PHPdoc例
/**
 * Class BlogEntry ブログ記事
 */
class BlogEntry extends Model {

    /** @type string[] 記事のタグリスト */
    private tags = [];

    /**
     * タグを追加する
     * @param string $tag
     * @return self $this
     */
    public addTag($tag)
    {
        $this->tags[] = $tag; // ← 警告なし
        $this->tags[] = 12; // ← string でないので警告される
        return $this;
    }
}

$entry = new BlogEntry();
$entry
    ->addTag("foo") // ← 警告なし BlogEntry なので自動的に補完してくれる
    ->addTag("bar") // ← 警告なし 戻り値が self なのでメソッドチェインもうまく補完してくれる
    ->addTag(40);   // ← string でないので警告される

そもそも PHPDoc が嘘を付いていたら全てが水の泡です。
関数やメソッドを書くときは、まず意図と入出力定義を PHPDoc として記します。
意図や入出力定義に相違が生まれる場合は直ちに修正します。

「PHPdoc 書く前提なのか」的なブコメを賜りましたが、基本的にどの言語でも *doc コメントを書かない人とは一緒にお仕事したくないですね。静的型付け言語で敢えて書かなくても意図が明確な場合は書かなくてもいいと思いますが、「意図を示すシンプルな命名と、意図を補う *doc コメント」というペアと、その意図に反しないロジックというのが現代のプログラミングと言っても過言ではないと思います。理想だと思いますか?それがプログラマの仕事じゃないんですか?

*doc をメンテする時間よりも、意図をコードから読み解いたり嘘に騙されて骨を折る時間のほうがよっぽど無駄だと思いませんか?

少々熱くなってしまいましたが、PhpStorm で PHPdoc を書くのはすごくラクです。フィールドやメソッドを書いてからその上に /** って入力して Enter を打てば宣言通りに自動入力されるので、あとは意図と型情報を追記してあげるだけで済みます。また宣言と相違があれば警告してくれるので、警告は無視せずかならず修正してあげましょう。

member.2 Web Application Framework

Phalcon PHP Framework

バカ速い・自由・スタンダードで軽快なMVC ということで虜になっています。
Phalcon2 からは PHPExtension 専用開発言語 Zephir の登場により Phalcon 自体の開発も加速されるでしょう。
(公式サイト は既に Phalcon2 に移行しているとのことです。)

バカ速い

Phalcon はほぼ全ての機能を PHPExtension として提供しています。
ルーティング, テンプレートエンジン, O/Rマッパー, DI などなど、それら全てが include によるオーバーヘッドを受けずに利用することができる上、ネイティブで動作することでパフォーマンスは他の WAF の追随を許しません。

自由

PHPExtension で実装されているがゆえに融通が効かないのかとおもいきや、ほとんどの機能に対してインターセプトできるように設計されています。
例えば Redis を使ってセキュリティを自前で実装する、なんてことも素直にできます。
標準搭載テンプレートエンジン Volt にもフィルタや関数を自由に追加できます。

スタンダードで軽快な MVC

Model を素直なオブジェクトとして記述できる。

Model は基本的に DB テーブルと1:1で紐付き、Symfony で言う Entity と Repository の機能を持ちます。
付属する phalcon コマンドにより、テーブル定義から自動的に Model を生成できます。
このとき public なフィールドをもつオブジェクトとして生成するか、フィールドに対する getter/setter を自動実装するかを選択することができます。フィールドの隠蔽により、素直なドメイン駆動設計を実現できます。
単純なオブジェクトなので、独自の継承関係を構築しても大元が \Phalcon\Mvc\Model なら素直に動きます。

※MongoDB に対応した Collection というものもあります。

Controller Model 共に基本的なイベントシステムを備えている

Controller なら初期化時、ルート実行前、ルート実行後
Model ならコンストラクト時や各種永続化イベントに対応しており、
それぞれのタイミングで任意のロジックを実行できます。

あなたが信じればいつでもそこに DI は在る

Phalcon において DI は開発者が明示的に inject する必要がありません。
たとえば Symfony では、当たり前ですが DI コンテナを inject されないオブジェクトでは DI コンテナにアクセスすることはできません。(これを憂いて static に DI への参照を持たせたりすることでテストがやりづらくなってしまいます。)

しかし Phalcon では、DI はサービス定義phpで定義するだけで、Phalcon\DI\Injectable を継承する全てのオブジェクトでアクセスできるようになるのです。

class Hoge extends \Phalcon\DI\Injectable {

    public function foo()
    {
        $di = $this->getDI(); // 何故かこれで必ず DI コンテナにアクセスできる
        $config = $di->get('config');
        $logger = $di->get('logger');
        $logger->info($config->application->id);
    }
}

もちろんあとから明示的に inject することもできるので、テストに影響することはありません。

PHP に向き合う度に付き合わされる諸問題とお別れするためにも

MySQL と接続するのにどの方法を使うのか、文字化け対策はどうするのか、メールアドレスのバリデーションは...そんなことは上級者であればあるほど考えるべきことではありません。ましてや他の言語を使う度に思い出すなんてバカバカしい!Phalcon のサービスレイヤで一度解決したら、もう忘れてしまいましょう。

member.3 その他のライブラリ、言語拡張

LINQ がなければ Ginq を使えばいいじゃない

PHP の LINQ 実装の中で最もオススメです。
作者さんは日本人で、Ginq でググると情報もそれなりに出てきます。
関連: Phalcon Model::find の結果を Ginq にする方法

プリミティブ型(string, array, etc)が使いづらい?拡張メソッドだ!

scalar_objects を使うとプリミティブ型にメソッドを生やすことができます。

拡張メソッド

Redis につなぐなら PhpRedis

Phalcon には PhpRedis に対応した追加モジュールがあります。
サービス定義を変えるだけで、セッションやキャッシュを Redis に担当させることができます。
サービスコンテナに PhpRedis 自体を配置すれば、DI によりいつでもどこでも Redis にアクセスできます。

Nuget 的なアレ

PearPackagist(composer) があります。
Packagist は Github と連携することで、自分で作ったライブラリを登録することもできます。
上記 Ginq も Packagist に登録されています

詳しくは PHP The Right Way - 依存関係の管理 を読むといいと思います。

member.4 複雑で速度が要求されるアルゴリズムは Zephir で実装

なんか PHP を dis るときにやたら「こんなアルゴリズムも満足に実装できない」とかいうのがあって、全くそのとおりだなと思います。
Zephir は Phalcon の件でもちらっと書きましたが、既にそこそこ書けるレベルになっています。
複雑なアルゴリズムは PHP で書くものではないと思いますので、Zephir で書いて呼び出すのがいいんじゃないでしょうか。


(ほとんど Phalcon の宣伝になってしまいましたが)こんなところです。
あとは必要なモノを PearPackagist で探すなりってところで、PHP が苦ではなくなってくるはずです。
なにが苦かって、やっぱりここまで自前で揃えてやらなきゃならないってことですね。

ここまでやった時の C# と比較したデメリットは、

  • シンタックスシュガー弱い(プロパティ構文とかラムダ式とか)
  • ジェネリクスが無い
  • 並列処理ができない

くらいだと思います。
それでもこれでクライアントサイド書くわけじゃないので、これくらいなら許せるかな、って感じで PHP 書いてます。

以上です。