遅延静的束縛 (Late Static Bindings)
PHP 5.3.0 以降、PHP に遅延静的束縛と呼ばれる機能が搭載されます。 これを使用すると、静的継承のコンテキストで呼び出し元のクラスを参照できるようになります。
この "遅延静的束縛" という機能名は、内部動作を考慮してつけられたものです。 "遅延束縛 (Late binding)" の由来は、メソッドを定義しているクラス名を使用しても static:: の解決ができなくなったことによります。 その代わりに、実行時情報をもとに解決するようになります。 "静的束縛 (static binding)" の由来は、 静的メソッドのコールに使用できることによります (ただし、静的メソッド以外でも使用可能です)。
self:: の制限
self:: あるいは __CLASS__ による現在のクラスへの静的参照は、 そのメソッドが属するクラス (つまり、 そのメソッドが定義されているクラス) に解決されます。
Example#1 self:: の使用例
<?php
class A {
public static function who() {
echo __CLASS__;
}
public static function test() {
self::who();
}
}
class B extends A {
public static function who() {
echo __CLASS__;
}
}
B::test();
?>
上の例の出力は以下となります。
A
遅延静的束縛の使用法
遅延静的束縛は、この制限を解決するためのキーワードを導入し、 実行時に最初にコールされたクラスを参照するようにしています。 このキーワードを使用すると、先ほどの例における test() から B を参照できるようになります。 このキーワードは新たに追加したものではなく、すでに予約済みである static を使用しています。
Example#2 static:: のシンプルな使用法
<?php
class A {
public static function who() {
echo __CLASS__;
}
public static function test() {
static::who(); // これで、遅延静的束縛が行われます
}
}
class B extends A {
public static function who() {
echo __CLASS__;
}
}
B::test();
?>
上の例の出力は以下となります。
B
注意: static:: の動作は、静的メソッドにおいては $this と異なります! $this-> は継承規則に従いますが、 static:: は従いません。 この違いについては、後ほど詳しく説明します。
Example#3 非静的コンテキストにおける static:: の使用法
<?php
class TestChild extends TestParent {
public function __construct() {
static::who();
}
public function test() {
$o = new TestParent();
}
public static function who() {
echo __CLASS__."\n";
}
}
class TestParent {
public function __construct() {
static::who();
}
public static function who() {
echo __CLASS__."\n";
}
}
$o = new TestChild;
$o->test();
?>
上の例の出力は以下となります。
TestChild TestParent
注意: 遅延静的束縛の解決は、静的コールが代替なしに完全に解決された時点で終了します。
Example#4 完全に解決された静的コール
<?php
class A {
public static function foo() {
static::who();
}
public static function who() {
echo __CLASS__."\n";
}
}
class B extends A {
public static function test() {
A::foo();
}
public static function who() {
echo __CLASS__."\n";
}
}
B::test();
?>::test();
?>上の例の出力は以下となります。
A
特殊な場合
PHP でメソッドをコールするには、さまざまな方法があります。 たとえばコールバックやマジックメソッドなどもそのひとつです。 遅延静的束縛は実行時の情報にもとづいて解決を行うので、 このように特殊な場合には予期せぬ結果となる可能性があります。
Example#5 マジックメソッド内における遅延静的束縛
<?php
class A {
protected static function who() {
echo __CLASS__."\n";
}
public function __get($var) {
return static::who();
}
}
class B extends A {
protected static function who() {
echo __CLASS__."\n";
}
}
$b = new B;
$b->foo;
?>
上の例の出力は以下となります。
B