Cosa sono esattamente i binding statici tardivi in ​​PHP?


Risposte:


198

Devi assolutamente leggere i binding statici tardivi nel manuale di PHP. Tuttavia, proverò a darti un breve riepilogo.

Fondamentalmente, si riduce al fatto che la selfparola chiave non segue le stesse regole di eredità. selfsi risolve sempre nella classe in cui viene utilizzato. Ciò significa che se si crea un metodo in una classe genitore e lo si chiama da una classe figlio, selfnon si farà riferimento al figlio come ci si potrebbe aspettare.

L'associazione statica tardiva introduce un nuovo uso per la staticparola chiave, che risolve questo particolare difetto. Quando lo usi static, rappresenta la classe in cui lo usi per la prima volta, ad es. si "lega" alla classe di runtime.

Questi sono i due concetti di base alla base. Il modo in cui self, parente staticfunziona quando staticè in gioco può essere sottile, così piuttosto che andare per maggiori dettagli, vi consiglio vivamente di studiare gli esempi delle pagine di manuale. Una volta comprese le basi di ogni parola chiave, gli esempi sono abbastanza necessari per vedere che tipo di risultati otterrai.


ho trovato questo articolo davvero utile e descrittivo, dai un'occhiata [link] ( techflirt.com/tutorials/oop-in-php/late-static-binding.html )
Sadegh Shaikhi,

"... la selfparola chiave non segue le regole dell'ereditarietà. Si selfrisolve sempre nella classe in cui viene utilizzata." - Il che non significa che non puoi chiamare il metodo statico di un genitore da un oggetto figlio tramite self, proprio come con i metodi non statici. Forse intendi la cosa giusta, ma dovresti riformularla. Importa davvero solo una volta che i bambini hanno nomi identici poiché puoi quindi decidere a quali fare riferimento usando static::invece.
DanMan,

81

Da PHP: attacchi statici tardivi - Manuale :

A partire da PHP 5.3.0, PHP implementa una funzione chiamata associazione statica tardiva che può essere utilizzata per fare riferimento alla classe chiamata nel contesto dell'ereditarietà statica.

L'associazione statica tardiva tenta di risolvere tale limitazione introducendo una parola chiave che fa riferimento alla classe inizialmente chiamata in fase di esecuzione. ... Si è deciso di non introdurre una nuova parola chiave, ma piuttosto di utilizzare staticquella già riservata.

Vediamo un esempio:

<?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
?>

I binding statici tardivi funzionano memorizzando la classe denominata nell'ultima "chiamata non inoltro". In caso di chiamate a metodi statici, questa è la classe esplicitamente denominata (di solito quella a sinistra ::dell'operatore); in caso di chiamate a metodi non statici, è la classe dell'oggetto. Una "chiamata" è uno statico che viene introdotto da self::, parent::, static::, o, se salendo nella gerarchia delle classi, forward_static_call(). La funzione get_called_class()può essere utilizzata per recuperare una stringa con il nome della classe chiamata e static::introduce il suo ambito.


1
Questo post è all'80% circa una copia integrale dell'articolo php.net senza marcatori di citazione.
Woodrow Shigeru,

22

Non c'è un comportamento molto evidente:

Il codice seguente produce "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

Tuttavia, se rimuoviamo la dichiarazione della funzione classname dalla classe beta, otteniamo "alphaalpha" come risultato.


1
Molto bella. La stessa cosa è mostrata nel manuale di PHP, ma questo è molto più chiaro. Per riferimento: php.net/manual/en/language.oop5.late-static-bindings.php (vedi ex. 4)
musicin3d

11

Sto citando dal libro: "PHP Master scrive codice all'avanguardia".

Il binding statico tardivo era una funzionalità introdotta con php 5.3. Ci consente di ereditare metodi statici da una classe genitore e di fare riferimento alla classe figlio chiamata.

Questo significa che puoi avere una classe astratta con metodi statici e fare riferimento alle implementazioni concrete della classe figlio usando la notazione static :: method () invece di self :: method ().

Sentiti libero di dare un'occhiata anche alla documentazione ufficiale di php: http://php.net/manual/en/language.oop5.late-static-bindings.php


Il modo più chiaro per spiegare il legame statico tardivo è con un semplice esempio. Dai un'occhiata alle due seguenti definizioni di classe e continua a leggere.

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";
    }
}

Vediamo una classe genitore (veicolo) e una classe figlio (auto). La classe genitore ha 2 metodi pubblici:

  • invokeDriveByStatic
  • invokeStopBySelf

La classe genitore ha anche 2 metodi privati:

  • drive
  • stop

La classe Child ignora 2 metodi:

  • drive
  • stop

Ora invociamo i metodi pubblici:

  • invokeDriveByStatic
  • invokeStopBySelf

Chiediti: quale classe invoca invokeDriveByStatic/ invokeStopBySelf? La classe genitore o figlio?

Dai un'occhiata qui sotto:

// 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

La staticparola chiave viene utilizzata in un modello di progettazione Singleton. Vedi link: https://refactoring.guru/design-patterns/singleton/php/example


7

L'esempio più semplice per mostrare la differenza.
Nota, self :: $ c

class A
{
    static $c = 7;

    public static function getVal()
    {
        return self::$c;
    }
}

class B extends A
{
    static $c = 8;
}

B::getVal(); // 7

Associazione statica tardiva, nota statica :: $ c

class A
{
    static $c = 7;

    public static function getVal()
    {
        return static::$c;
    }
}

class B extends A
{
    static $c = 8;
}

B::getVal(); // 8

4

Per esempio:

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

Guardandolo da un "perché dovrei usarlo?" prospettiva, è fondamentalmente un modo per cambiare il contesto dal quale viene interpretato / eseguito il metodo statico.

Con self, il contesto è quello in cui è stato definito il metodo in origine. Con static, è quello da cui lo chiami.


1

Inoltre, controlla se aggiorni le variabili statiche nelle classi secondarie. Ho trovato questo (in qualche modo) risultato inaspettato in cui il bambino B aggiorna il bambino 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
// )

Puoi risolverlo dichiarando la stessa variabile in ogni classe figlio, ad esempio:

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;        
    }
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.