web-dev-qa-db-ja.com

PHPの遅延静的バインディングとは正確には何ですか?

PHPの遅延静的バインディングとは正確には何ですか?

123
Sarfraz

必ず読む必要があります Late Static Bindings PHPマニュアル。しかし、私はあなたに簡単な要約を与えようとします。

基本的に、selfキーワードは同じ継承ルールに従っていないという事実に要約されます。 selfは、常にそれが使用されるクラスに解決されます。つまり、親クラスでメソッドを作成して子クラスから呼び出すと、selfは期待どおりに子を参照しません。

遅延静的バインディングは、この特定の欠点に対処するstaticキーワードの新しい使用法を導入します。 staticを使用する場合、最初に使用するクラスを表します。ランタイムクラスに「バインド」します。

これらは、その背後にある2つの基本概念です。 selfが動作しているときにparentstaticおよびstaticが動作する方法は微妙である可能性があるため、より詳細に説明するのではなく、強くマニュアルページの例を調べることをお勧めします。各キーワードの基本を理解したら、どのような結果が得られるかを確認するために例が非常に必要です。

185
zombat

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::はそのスコープを導入します。

79
Mrinmoy Ghoshal

あまり明らかな動作はありません:

次のコードは「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'が取得されます。

22
Jokerius

私は本から引用しています:「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

10
Julian

違いを示す最も単純な例。
注、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
7

「なぜこれを使用するのか?」パースペクティブ、それは基本的に静的メソッドが解釈/実行されているコンテキストを変更する方法です。

selfを使用すると、コンテキストはメソッドを最初に定義したコンテキストになります。 staticを使用すると、呼び出し元の名前になります。

4
DanMan

例えば:

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();
4
Petah

また、子クラスの静的変数を更新するかどうかにも注意してください。子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;        
    }
}
1
Frank Forte