PHPを再勉強中。
PHPでは親クラスの静的メソッドを子クラスでオーバーライドすることができます。
でもちょっと普通に期待する動作とは違うので注意が必要みたい。
サンプル
<?php
# 親クラス
class MyParent
{
static function foo()
{
return "Parent";
}
static function bar()
{
echo self::foo() . PHP_EOL;
}
}
# 子クラス
class MyChild extends MyParent
{
# 親クラスのメソッドをオーバーライド
static function foo()
{
return "Child";
}
}
MyParent::bar(); # <= "Parent"と出力してほしい
MyChild::bar(); # <= "Child"と出力してほしい
親クラスの静的メソッドfoo()
を子クラスでオーバーライドしています。foo()
は親クラスで定義された静的メソッドbar()
から呼ばれます。静的でない通常のメソッドであれば、子クラスからbar()
を呼び出せば、bar()
からはオーバーライドされた子クラスのfoo()
が仮想メソッドとして呼ばれます。しかし静的メソッドに対して同様のことを実行すると、期待はずれの結果になります。
実行結果
$ php test1.php
Parent
Parent
子クラスでオーバーライドしたfoo()が呼ばれず、親クラスのfoo()が呼ばれています。
子クラスから親クラスの静的メソッドを呼び出した場合、そのメソッドは親クラスのコンテキスト(selfが親クラスを指す)で実行されてしまうようです。そのため、子クラスから呼び出した親クラスの静的メソッド内で子クラスでオーバーライドされたメソッドを呼び出そうとしても、仮想メソッドとしては機能せず親クラスで定義されたものが呼ばれる結果になります。
PHP(5.3移行)にはこれを回避するために、遅延静的束縛という仕組みが用意されています。
参考 : 遅延静的束縛 (Late Static Bindings)
上のプログラムを遅延静的束縛を利用したものに書き換えてみます。
<?php
# 親クラス
class MyParent
{
static function foo()
{
return "Parent";
}
static function bar()
{
echo static::foo() . PHP_EOL;
}
}
# 子クラス
class MyChild extends MyParent
{
# 親クラスのメソッドをオーバーライド
static function foo()
{
return "Child";
}
}
MyParent::bar(); # <= "Parent"と出力してほしい
MyChild::bar(); # <= "Child"と出力してほしい
bar()
内でfoo()
を呼び出すさい、静的メンバを参照するさいのself
をstatic
と書き換えただけです。(このself
やstatic
というのは定数? 構文?よく判らない)
static::hogehoge
とすると定義されたクラスではなく呼び出されたクラスのコンテキストでhogehoge
が探索されるようになります。これを遅延静的束縛という(らしいです)。
実行結果
$ php test2.php
Parent
Child
期待通りの結果になりました。