PHPの遅延静的バインディングとは正確には何ですか?
必ず読む必要があります Late Static Bindings PHPマニュアル。しかし、私はあなたに簡単な要約を与えようとします。
基本的に、self
キーワードは同じ継承ルールに従っていないという事実に要約されます。 self
は、常にそれが使用されるクラスに解決されます。つまり、親クラスでメソッドを作成して子クラスから呼び出すと、self
は期待どおりに子を参照しません。
遅延静的バインディングは、この特定の欠点に対処するstatic
キーワードの新しい使用法を導入します。 static
を使用する場合、最初に使用するクラスを表します。ランタイムクラスに「バインド」します。
これらは、その背後にある2つの基本概念です。 self
が動作しているときにparent
、static
およびstatic
が動作する方法は微妙である可能性があるため、より詳細に説明するのではなく、強くマニュアルページの例を調べることをお勧めします。各キーワードの基本を理解したら、どのような結果が得られるかを確認するために例が非常に必要です。
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
?>
late static bindings
は、最後の「非転送呼び出し」で指定されたクラスを保存することにより機能します。静的メソッド呼び出しの場合、これは明示的に名前が付けられたクラスです(通常は::演算子の左側にあります)。非静的メソッド呼び出しの場合、それはオブジェクトのクラスです。
「転送呼び出し」とは、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
ただし、ベータクラスからclassname関数の宣言を削除すると、結果として 'alphaalpha'が取得されます。
私は本から引用しています:「PHPマスターは最先端のコードを書きます」。
遅延静的バインディングは、PHP 5.3で導入された機能です。これにより、親クラスから静的メソッドを継承し、呼び出される子クラスを参照できます。
これは、静的メソッドを持つ抽象クラスを作成し、static :: method()表記の代わりに、子クラスの具体的な実装を参照できることを意味しますself :: 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
「なぜこれを使用するのか?」パースペクティブ、それは基本的に静的メソッドが解釈/実行されているコンテキストを変更する方法です。
self
を使用すると、コンテキストはメソッドを最初に定義したコンテキストになります。 static
を使用すると、呼び出し元の名前になります。
例えば:
abstract class Builder {
public static function build() {
return new static;
}
}
class Member extends Builder {
public function who_am_i() {
echo 'Member';
}
}
Member::build()->who_am_i();
また、子クラスの静的変数を更新するかどうかにも注意してください。子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;
}
}