遅延静的束縛 (Late Static Bindings)

PHP には、遅延静的束縛と呼ばれる機能が搭載されています。 これを使用すると、静的継承のコンテキストで呼び出し元のクラスを参照できるようになります。

より正確に言うと、遅延静的束縛は直近の "非転送コール" のクラス名を保存します。 staticメソッドの場合、これは明示的に指定されたクラス (通常は :: 演算子の左側に書かれたもの) となります。staticメソッド以外の場合は、そのオブジェクトのクラスとなります。 "転送コール" とは、self::parent::static:: による :: 演算子を使ったコール。 あるいはクラス階層の中での forward_static_call() によるコールのことです。 get_called_class() 関数を使うとコール元のクラス名を文字列で取得できます。 static:: はこのクラスのスコープとなります。

この "遅延静的束縛" という機能名は、内部動作を考慮してつけられたものです。 "遅延束縛 (Late binding)" の由来は、メソッドを定義しているクラス名を使用しても static:: の解決ができないことによります。 その代わりに、実行時情報をもとに解決するようになります。 "静的束縛 (static binding)" の由来は、 staticメソッドのコールに使用できることによります (ただし、staticメソッド以外でも使用可能です)。

self:: の制限

self:: あるいは __CLASS__ による現在のクラスへの静的参照は、 そのメソッドが属するクラス (つまり、 そのメソッドが定義されているクラス) に解決されます。

例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 を使用しています。

例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 

注意:

静的でないコンテキストでは、呼び出されるクラスはそのオブジェクトのクラスと同じになります。 $this-> は private メソッドを同じスコープからコールしようとするので、 static:: を使うと異なる結果となります。もうひとつの相違点は、 static:: はstaticプロパティしか参照できないということです。

例3 非静的コンテキストにおける static:: の使用法

<?php
class A {
private function
foo() {
echo
"success!\n";
}
public function
test() {
$this->foo();
static::
foo();
}
}

class
B extends A {

}

class
C extends A {
private function
foo() {

}
}

$b = new B();
$b->test();
$c = new C();
$c->test(); //fails
?>

上の例の出力は以下となります。

 success! success! success! Fatal error: Call to private method C::foo() from context 'A' in /tmp/test.php on line 9 

注意:

遅延静的束縛の解決は、:: を使ったコールが代替なしに完全に解決された時点で終了します。 一方、parent::self:: といったキーワードを使用するコールは、コール元の情報を転送します。

例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();
parent::foo();
self::foo();
}

public static function
who() {
echo
__CLASS__."\n";
}
}
class
C extends B {
public static function
who() {
echo
__CLASS__."\n";
}
}

C::test();
?>

上の例の出力は以下となります。

 A C C 
To Top