前回に引き続き、オブジェクト仕様の変更について解説します。PHP5になってからオブジェクト指向プログラミング機能が強化され、Javaなどのオブジェクト思考言語に慣れたユーザにはより使いやすい言語仕様を持つようになりました。まれとは思いますが、PHP5からの新しい機能であるインターフェースも移行時に問題となる可能性も考えられるので、定義済みインターフェースも紹介しています。インターフェースとはメソッド定義がない抽象クラスのようなものです。よく分からない場合はプロパティ・メソッドの中身がないクラスのようなものと考えてください。
特に記述がない場合、PHP4はPHP4.4.x、PHP5はPHP5.2.xを意味します。
定義済みクラス
モジュールをロードするとクラスが定義される場合があるので、環境により定義済みクラスは異なります。ここではデフォルトのXAMMP for Windows 1.6.2のPHP4とPHP5の定義済みクラスの一部を紹介します。
ここでは省略していますが、get_declared_classes関数の戻り値を参照すると、PHP5には例外クラス、リフレクションAPI関連クラス、XML関連クラスなどが定義されていることが分かります。
PHP5からはInterfaceもサポートしています。このためInterfaceとして定義された名前もクラス名として利用できません。
PHP5のデフォルトの定義済みクラスで、最も注意が必要と思われるクラス名はDateTimeクラスです。
どのクラス/インターフェース名でも同じですが、定義済みクラス名またはインターフェース名を利用しようとすると、Fatalエラーとなりスクリプトの実行が停止します。同じクラス名を定義している場合、クラス名を変更しなければなりません。
クラス/メソッド名の取得
クラス名を返すget_class/get_parent_class関数、メソッド名を返すget_class_methodsは、定義された際の名前をそのまま返すように仕様が変更されました。つまりPHP4ではクラス/メソッド名はすべて小文字に変換されてから返されましたが、PHP5では小文字への変換は行われません。
PHP4用のコードをPHP5で動作させるには
と大文字を小文字に変換します。
PHPが自動的に設定する__CLASS___, __METHOD__, __FUNCTION__定数も定義されたままの値を持つように変更されています。
インスタンスとクラス名の比較
PHP4にはインスタンスがどのようなクラスから生成されているか確認するis_a関数、is_subclass_of関数が用意されていました。
PHP5でもis_a関数、is_subclass_of関数は利用できますが、新しくinstanceof演算子が追加されました。instanceof演算子はis_a関数とis_subclass_of関数を合わせた動作をします。
get_class関数と異なりクラス名の比較は大文字・小文字を無視して行います。PHP5でのis_a関数、is_subclass_of関数の利用は推奨されていません。しかし、PHP5への移行だけであればコードの書き換えは必要ありません。将来的にはinstanceof演算子への書き換えは行ったほうがよいでしょう。
メソッド定義
PHP5からメソッドへのアクセスにpublic, protected, privateのアクセス制御が可能になりました。
PHP5への移行だけであればコードの書き換えは必要ありません。エラーが発生することもありません。
プロパティ定義
PHP4からPHP5への移行の障害となるため、PHP5.1.3から新しく設けられたエラーレベルE_STRICTを設定してもvar宣言でプロパティを宣言してもエラーを発生しなくなりました。現在のPHP5ではvar宣言はpublic宣言と同等に扱われます。
PHP5への移行には書き換えの必要はありません。現在のPHP5ではエラーが発生することもありません。
空のオブジェクト
PHP4のオブジェクト変数はプロパティが定義されていない場合、
とするとTRUEが返ってきました。PHP5のオブジェクト変数はempty($object)で常にFALSEと評価されます。
オブジェクトプロパティの有無を調べるにはget_object_vars関数を利用します。
最初の回で解説済みですが、PHPのバージョンをチェックするために言語仕様の違いで確認する必要はありません。PHP_VERSION定数とcompare_version関数で確認できます。
配列形式のプロパティアクセス
古いPHPではオブジェクト変数のプロパティは配列のようにアクセスできましたが、現在のPHP4、PHP5ではアクセスできません。さらに、PHP5ではFatalエラーになり実行が停止します。
オブジェクトの比較
PHP5からオブジェクト変数がハンドルで取り扱われるようになり、オブジェクト変数を比較するとPHP4とPHP5で結果が異なる場合があります。
PHP4、PHP5の両方ともデータ型を考慮しない比較演算子"=="が利用された場合、プロパティを比較しすべて値が同じ場合はTRUEを返します。PHP5でデータ型を考慮する比較演算子"==="を利用した場合、ハンドルで識別されるのでPHP4とPHP5で比較の結果が異なります。
PHP5でPHP4と同じ動作にするには、cloneで別のオブジェクト変数として
とすればよいように思えますが、===演算子でFALSEと評価されるので
の部分の実行結果が異なってしまいます。cloneを利用すると$foo1と$foo2は別のオブジェクト変数になり、PHP5ではecho文は実行されません。
PHP4のコードでオブジェクトが代入でコピーされることを前提に記述されているコードの場合、修正が必要な個所を見つけることが難しい場合も多いです。
次回以降で詳しく解説しますが、php.iniのzend.ze1_compatibility_mode設定はオブジェクトの代入と比較の動作に影響します。
クラスの結合
PHP4まではクラスとオブジェクトを結合させるaggregate関数がサポートされていましたが、PHP5からはサポートされません。JavaScriptやRubyのように既存のオブジェクトを拡張できたので利用されていた方もいるかもしれません。残念ながら代替する方法はPHP5に用意されていません。PHP5のクラスはJavaなどの静的な言語と同様に設計・実装しなければなりません。
クラス定義は静的な定義のみ可能ですが、プロパティの追加はいつでも行えます。この仕様に変更はありません。
インターフェースを利用する場合の定義順序
インターフェースはPHP5からの新しい機能なので、PHP4からPHP5への移行時に問題になることはないです。しかし、動的言語としては直感的に分かりづらい動作をするので解説しておきます。
PHP5からクラスAPIのインターフェースを定義するinterfaceが導入されました。interfaceとはどのようなメソッドを実装しなければならないか定義する機能で、クラスのAPI(インターフェース)を定義するために利用します。
interfaceはメソッドの中身を定義できない抽象クラスのようなものに見えますが、制約があります。インターフェースを利用するにはあらかじめinterfaceを定義しておかなければ、実行時にFatalエラーになります。
インターフェースを利用する場合、インターフェース定義とその定義を実装するクラスは利用される前に定義されていなければなりません。前の例をプログラマの意図通りに動作させるコードは以下のようになります。
__toStringの呼び出し
PHP5からオブジェクト変数が文字列として利用された場合、__toStringメソッドが呼び出されるようになりました。__toStringメソッドが未定義の場合、補足可能なFatalエラー例外が発生します。
まとめ
今回はオブジェクトの仕様変更で最も大きな変更であったオブジェクト変数のハンドル化や、PHP5で追加されたオブジェクト指向プログラミングサポート機能とPHP4のオブジェクト指向プログラミングサポート機能の違いを解説しました。
次回はモジュール関連の違いについて解説します。