回答:
PHPマニュアルの「レイトスタティックバインディング」を必ずお読みください。ただし、簡単にまとめておきます。
基本的に、self
キーワードは継承の同じルールに従っていないという事実に要約されます。 self
常にそれが使用されるクラスに解決されます。つまり、親クラスでメソッドを作成し、それを子クラスから呼び出すと、self
期待どおりに子を参照できなくなります。
レイトスタティックバインディングでは、static
キーワードの新しい使用方法が導入され、この特定の欠点に対処しています。を使用static
すると、最初に使用したクラス、つまり ランタイムクラスに「バインド」します。
これらは、その背後にある2つの基本的な概念です。方法self
、parent
およびstatic
動作中の操作static
は微妙な場合があるため、詳細に進むのではなく、マニュアルページの例を検討することを強くお勧めします。各キーワードの基本を理解したら、どのような結果が得られるかを確認するには、例が非常に必要です。
self
キーワードは継承のルールに従っていません。self
常に、それが使用されるクラスに解決されます。」-これはself
、非静的メソッドの場合と同様に、を介して子オブジェクトから親の静的メソッドを呼び出せないという意味ではありません。あなたは多分正しいことを意味するかもしれませんが、それを言い換える必要があります。static::
代わりにを使用してどのメンバーを参照するかを決定できるので、子が同じ名前のメンバーを持っている場合にのみ、すべてが本当に重要です。
PHPから:Late Static Bindings-Manual:
PHP 5.3.0以降、PHPは遅延静的バインディングと呼ばれる機能を実装しています。これは、静的継承のコンテキストで呼び出されたクラスを参照するために使用できます。
レイトスタティックバインディングは、実行時に最初に呼び出されたクラスを参照するキーワードを導入することで、この制限を解決しようとします。...新しいキーワードを導入せず、
static
予約済みの使用を決定しました。
例を見てみましょう:
<?php
class Car
{
public static function run()
{
return static::getName();
}
private static function getName()
{
return 'Car';
}
}
class Toyota extends Car
{
public static function getName()
{
return 'Toyota';
}
}
echo Car::run(); // Output: Car
echo Toyota::run(); // Output: Toyota
?>
レイトスタティックバインディングは、最後の「非転送呼び出し」で指定されたクラスを格納することで機能します。静的メソッド呼び出しの場合、これは明示的に名前が付けられたクラスです(通常は
::
演算子の左側にあるクラス)。非静的メソッド呼び出しの場合、それはオブジェクトのクラスです。「転送コールは、」によって導入された静的なの一つでありself::
、parent::
、static::
、、または、クラス階層に上がる場合forward_static_call()
。この関数get_called_class()
を使用して、呼び出されたクラスの名前の文字列を取得し、static::
そのスコープを紹介できます。
明確な動作はありません。
次のコードは「alphabeta」を生成します。
class alpha {
function classname(){
return __CLASS__;
}
function selfname(){
return self::classname();
}
function staticname(){
return static::classname();
}
}
class beta extends alpha {
function classname(){
return __CLASS__;
}
}
$beta = new beta();
echo $beta->selfname(); // Output: alpha
echo $beta->staticname(); // Output: beta
ただし、クラス名関数の宣言をベータクラスから削除すると、結果として「alphaalpha」が得られます。
「PHPマスターが最先端のコードを書く」という本から引用しています。
レイトスタティックバインディングは、php 5.3で導入された機能です。これにより、親クラスから静的メソッドを継承し、呼び出される子クラスを参照できます。
つまり、静的メソッドを持つ抽象クラスを作成し、self :: method()の代わりにstatic :: method()表記を使用して子クラスの具象実装を参照できます 。
公式のphpドキュメントもご覧ください。http: //php.net/manual/en/language.oop5.late-static-bindings.php
レイトスタティックバインディングを説明する最も明確な方法は、簡単な例です。以下の2つのクラス定義を見て、読んでください。
class Vehicle {
public static function invokeDriveByStatic() {
return static::drive(); // Late Static Binding
}
public static function invokeStopBySelf() {
return self::stop(); // NOT Late Static Binding
}
private static function drive(){
return "I'm driving a VEHICLE";
}
private static function stop(){
return "I'm stopping a VEHICLE";
}
}
class Car extends Vehicle {
protected static function drive(){
return "I'm driving a CAR";
}
private static function stop(){
return "I'm stopping a CAR";
}
}
親クラス(車両)と子クラス(車)があります。親クラスには2つのパブリックメソッドがあります。
invokeDriveByStatic
invokeStopBySelf
親クラスには、2つのプライベートメソッドもあります。
drive
stop
子クラスは2つのメソッドをオーバーライドします。
drive
stop
次に、パブリックメソッドを呼び出します。
invokeDriveByStatic
invokeStopBySelf
自問してください:どのクラスがinvokeDriveByStatic
/を呼び出しますinvokeStopBySelf
か?親または子クラス?
以下を見てください:
// This is NOT Late Static Binding
// Parent class invokes from Parent. In this case Vehicle.
echo Vehicle::invokeDriveByStatic(); // I'm driving a VEHICLE
echo Vehicle::invokeStopBySelf(); // I'm stopping a VEHICLE
// !!! This is Late Static Binding !!!!
// Child class invokes an inherited method from Parent.
// Child class = Car, Inherited method = invokeDriveByStatic().
// The inherited method invokes a method that is overridden by the Child class.
// Overridden method = drive()
echo Car::invokeDriveByStatic(); // I'm driving a CAR
// This is NOT Late Static Binding
// Child class invokes an inherited method from Parent.
// The inherited method invokes a method inside the Vehicle context.
echo Car::invokeStopBySelf(); // I'm stopping a VEHICLE
static
キーワードはシングルトンデザインパターンで使用されています。リンクを参照してください:https : //refactoring.guru/design-patterns/singleton/php/example
違いを示す最も簡単な例。
注:self :: $ c
class A
{
static $c = 7;
public static function getVal()
{
return self::$c;
}
}
class B extends A
{
static $c = 8;
}
B::getVal(); // 7
遅い静的バインディング、static :: $ cに注意
class A
{
static $c = 7;
public static function getVal()
{
return static::$c;
}
}
class B extends A
{
static $c = 8;
}
B::getVal(); // 8
また、子クラスの静的変数を更新するかどうかも確認してください。子Bが子Cを更新する、この(やや)予期しない結果が見つかりました。
class A{
protected static $things;
}
class B extends A {
public static function things(){
static::$things[1] = 'Thing B';
return static::$things;
}
}
class C extends A{
public static function things(){
static::$things[2] = 'Thing C';
return static::$things;
}
}
print_r(C::things());
// Array (
// [2] => Thing C
// )
B::things();
print_r(C::things());
// Array (
// [2] => Thing C
// [1] => Thing B
// )
たとえば、各子クラスで同じ変数を宣言することで修正できます。
class C extends A{
protected static $things; // add this and B will not interfere!
public static function things(){
static::$things[2] = 'Thing C';
return static::$things;
}
}