この記事を読むのに必要な時間は約 7 分です。
PHP で、可変長引数を持つコンストラクタを、子クラスでオーバーライドする時に困ったので、解消法?回避法?をメモしておこうと思います。
ベストな方法ではないかも知れませんが、役に立てれば幸いです。
こうして何とかなった (多分)
とりあえず、以下のようにしたらエラーは出ませんでした。
class Extended extends Base{ public function __construct(){ call_user_func_array( array($this, "parent::__construct"), func_get_args() ); /* 追加処理 */ } }
ポイント
- call_user_func_array() を利用することで、func_get_args() の配列をバラして引数に渡すことができます。
- call_user_func_array() での parent の呼び出し方はこうなるそうです。
PHP 5.6 以降であれば、__construct(…$params) で、func_get_args() の代わりに $params を渡すことも可能ですが、PHP 5.6 未満では利用できないため、func_get_args() が無難かと思います。
こんなことを解決してくれます
- 親クラスのメソッドがどんな引数を取るか知らなくても、オーバーライドできるようになります
親クラスのメソッドが引数を取ることが無い場合や、内容を把握している場合には不要な方法かと思います。
こんな人に役立つかも知れません
- フレームワークを利用していて、継承したクラスで親クラスのメソッドをオーバーライドして処理を追加したい
- コンストラクタの呼び出しはフレームワークが行うため、どんな引数を渡されて呼び出されるか知らない
私が困ったのは、こんな時
CakePHP で、Helper クラスを作成し、コンストラクタで処理を追加したかったので、何も考えず下記のようにしたら怒られました。
class SomeHelper extends AppHelper{ public function __construct(){ parent::__construct(); /* hogehoge */ } }
以下のようなメッセージが出てしまいました。
Warning (4096): Argument 1 passed to Helper::__construct() must be an instance of View, none given, called in /path/to/cake/app/View/Helper/SomeHelper.php on line 13 and defined [CORE/Cake/View/Helper.php, line 173]
View を AppHelper コンストラクタの引数1 に渡せ、ということっぽいですが、何を渡されて呼ばれるものか、そんなん知りませぬ。
ましてや、親クラスで、引数2 が任意引数 (デフォルト値付き) で定義されていたりしたら、引数1つだけ渡すようでは、予期せぬ動作をしかねません。子クラスでの 引数2 の定義も複雑になりそうです。
このままでは、コンストラクタをオーバーライドするには、以下の制限を受けてしまいます。
- 親クラスのコンストラクタの引数の定義を知る
- 親クラスのコンストラクタの引数が変わった場合は合わせて修正する
ソースを読めば前者は分かりそうですが、毎度そこまでしたくはないため、何とかしてみよう、というわけです。
苦労したところ
配列を親クラスのコンストラクタに対して、引数として並べて渡す方法が難点でした。
子クラスで、引数を可変長引数として受け取ることは難しくありません。func_get_args() や、PHP 5.6 以降であれば、引数定義で … を使えば配列で受け取れます。
しかし、それを以下のように、引数として渡しても「複数の引数」ではなく、「1つの配列」として渡してしまいます。
/* これじゃダメ */ parent::__construct( func_get_args() );
例えば、以下のようなことになります。
class Base{ public function __construct($param1, $param2 = null){ } } class Extended extends Base{ public function __construct(){ parent::__construct( func_get_args() ); } } $extended = new Extended( 1, 2 ); /* Base::__construct() は以下を受け取ります $param1 -> array( 1, 2 ) $param2 -> null */
これを解決してくれるのが、call_user_func_array() でした。
第1引数に呼び出したい クラス、メソッド名 の配列を、第2引数にメソッドに渡す引数を配列で、この関数に渡すことで、配列を引数に展開して渡してくれます。
その他の使い方などは、 PHP マニュアル 公式サイト をご覧下さい。
今回は以上です。
[pn-amzn-cakephp]