※当サイトの記事には、広告・プロモーションが含まれます。

PHP5.3から追加された遅延静的束縛(Late Static Bindings)

『遅延性的束縛(Late Static Bindings)』ってネーミングは、インパクトがすごいですが。

qiita.com

⇧  上記サイト様が分かりやすいかと。

maeharin.hatenablog.com

⇧  上記サイト様が、詳しく解説してくれてます。

 

静的な呼び出しで起こる問題

「遅延静的束縛は、継承されたクラスのメソッドなどを親クラスで解決するための機能です。」 と『パーフェクトPHP』という本には書いてありました。

 

 ⇩  遅延静的束縛については下記サイトへ

[メモ]PHP5.3 遅延静的束縛 - Qiita

遅延静的束縛は何が嬉しいのか-暁

 ⇩  PHP5.3で新しくなったことについては下記サイトへ

PHP V5.3 では何が新しいのか: 第 1 回 オブジェクト・インターフェースに関する変更

 

遅延静的束縛 (Late Static Bindings)

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

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

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

self:: の制限

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

例1 self:: の使用例 

<?php
class {
    public static function 
who() {
        echo 
__CLASS__;
    }
    public static function 
test() {
        
self::who();
    }
}

class 
extends {
    public static function 
who() {
        echo 
__CLASS__;
    }
}

B::test();
?>

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

A

遅延静的束縛の使用法

遅延静的束縛は、この制限を解決するためのキーワードを導入し、 実行時に最初にコールされたクラスを参照するようにしています。
このキーワードを使用すると、先ほどの例における test( ) から B を参照できるようになります。
このキーワードは新たに追加したものではなく、すでに予約済みである static を使用しています。

例2 static:: のシンプルな使用法

<?php
class {
    public static function 
who() {
        echo 
__CLASS__;
    }
    public static function 
test() {
        static::
who(); // これで、遅延静的束縛が行われます
    
}
}

class 
extends {
    public static function 
who() {
        echo 
__CLASS__;
    }
}

B::test();
?>

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

B

注意:

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

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

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

class 
extends {
   
/* foo() が B にコピーされるので、メソッドのスコープは A のままとなり、
    * コールは成功します */
}

class 
extends {
    private function 
foo() {
        
/* もとのメソッドが置き換えられるので、新しいメソッドのスコープは C となります */
    
}
}

$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 {
    public static function 
foo() {
        static::
who();
    }

    public static function 
who() {
        echo 
__CLASS__."\n";
    }
}

class 
extends {
    public static function 
test() {
        
A::foo();
        
parent::foo();
        
self::foo();
    }

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

C::test();
?>

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

A
C
C

 

非転送コールと転送コールと遅延静的束縛

名前 說明
非転送コール Bar::foo()や$bar->foo()といったクラス名(もしくはオブジェクト)を明示した呼び出し
転送コール self:: parent:: static::もしくはクラス階層の中での forward_static_call() での呼び出し
遅延静的束縛 非転送コール時に、明示されたクラス名(もしくはオブジェクトのクラス名)を保持する機能

『self::』だと制限があるので『static::』を使って解決、この時の『static::』が『遅延静的束縛』みたいです

親クラスで、メソッドなどを『self::』で静的に呼び出す記述があったりすると、継承先の子クラスでも、この『self』が親クラスとなってしまうようです。

例:『self::』を用いた静的呼び出し

<?php
class Foo {

// __CLASS__は実行中のクラス名を返すので、Foo

    public static function who() {
        echo 
__CLASS__.' Hello! ';
    }

// Fooのwho()を呼び出す
    public static function 
hello() {
        
self::who();
    }
}

class 
Bar extends Foo {

// __CLASS__は実行中のクラス名を返すので、Barと思いきや、

/* self::`あるいは`CLASS`による現在のクラスへの 静的参照は、そのメソッドが属するクラス (つまり、そのメソッドが定義されているクラス) に解決されます。 */
    public static function 
who() {
        echo 
__CLASS__.' Hello!!!!!! ';
    }
}
// 非転送コールの実行、結果はFoo Hello!
Bar::hello();
?>

 

PHPマニュアルで、

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

ということらしく、これを解決してくれるのが、『static::』つまり『遅延静的束縛』らしいです。

例:『static::』を用いた静的呼び出し

<?php
class Foo {

// __CLASS__は実行中のクラス名を返すので、Foo

    public static function who() {
        echo 
__CLASS__.' Hello! ';
    }

// static::で直近の非転送コールを保持し、そのwho()を呼び出す
    public static function 
hello() {
        
static::who();
    }
}

class 
Bar extends Foo {

/* who()はstatic::により直近の非転送コールを保持。呼び出されるまで__CLASS__がFooのままか、Barになるか分からない。 */
    public static function 
who() {
        echo 
__CLASS__.' Hello!!!!!! ';
    }
}
// 非転送コールの実行、結果はBar Hello!!!!!!
Bar::hello();
?>

 

『遅延静的束縛』は、非転送コールが実行された段階で、静的メンバを決定できるってことなのかな、たぶんと思いました。

静的メンバを未定の状態でバインドしておいて、非転送コールが実行されることで遅れて静的メンバが定義されるような。

内田樹氏が言っていた

『・レヴィナスにおいて、
「被造物である」とは「贈り物をすでに受け取ってしまった」という「贈与についての絶対的遅れ」を意味しています。

(中略)レヴィ=ストロースによれば、
むしろ「反対給付義務から逃れることができないもの」というのが種としての「人間」についての究極的な定義なのです。

・「感謝をしたいのだけれど、誰に感謝してよいかわからない。

なぜなら、
『誰に感謝してよいのかわからない』という事実こそが人間が『世界の創造に遅れて到着したこと』の証拠であり、その事実に対して人間は『感謝したい』と思っているのである」
という順逆のねじれた仕方で人間の人間性=宗教性は構成されているからです。』

内田樹、釈徹宗「いきなりはじめる仏教入門」より抜粋

みたいな絶対的遅れってことを少しイメージしました。

相変わらず、PHPマニュアル難しいっす。

 

今回はこのへんで。