Menu
Coddy logo textTech

Binding statico tardivo

Fa parte della sezione Programmazione orientata agli oggetti del percorso PHP di Coddy. Lezione 36 di 91.

Quando usi self:: in una classe genitore, si riferisce sempre alla classe in cui è scritto, non alla classe che viene effettivamente utilizzata. Questo può causare comportamenti imprevisti nell'ereditarietà. Il late static binding risolve questo problema usando la parola chiave static::.

Considera questo esempio in cui self:: non funziona come previsto:

<?php
class ParentClass {
    public static function create() {
        return new self();
    }
    
    public static function getClassName() {
        return self::class;
    }
}

class ChildClass extends ParentClass {}

$obj = ChildClass::create();
echo get_class($obj) . "\n";
echo ChildClass::getClassName();

Output:

ParentClass
ParentClass

Anche se abbiamo chiamato ChildClass::create(), abbiamo ottenuto un oggetto ParentClass perché self:: viene risolto in fase di compilazione. Ora usiamo invece static:::

<?php
class ParentClass {
    public static function create() {
        return new static();
    }
    
    public static function getClassName() {
        return static::class;
    }
}

class ChildClass extends ParentClass {}

$obj = ChildClass::create();
echo get_class($obj) . "\n";
echo ChildClass::getClassName();

Output:

ChildClass
ChildClass

Con static::, PHP aspetta fino al runtime per determinare quale classe usare. Questo si chiama binding "ritardato" perché la decisione viene posticipata. È essenziale per i metodi factory e per qualsiasi metodo statico che dovrebbe rispettare l'ereditarietà.

Punto chiave: Usa static:: invece di self:: quando vuoi che le classi figlie ereditino correttamente e personalizzino il comportamento statico.

challenge icon

Sfida

Facile

Creiamo un sistema di registrazione dei modelli che dimostri la potenza del binding statico tardivo. Creerai una classe base Model con un metodo factory che le classi figlie possono ereditare e, grazie a static::, ogni classe figlia creerà correttamente istanze di sé stessa anziché della classe genitore.

Organizzerai il codice in quattro file:

  • Model.php: crea una classe base Model con una proprietà statica protetta $tableName impostata su "models". Aggiungi un metodo statico create() che usa new static() per restituire una nuova istanza della classe che lo ha chiamato. Aggiungi un altro metodo statico getTable() che restituisce static::$tableName. Infine, aggiungi un metodo describe() che restituisce "[ClassName] from table [tableName]" usando static::class e static::$tableName.
  • User.php: crea una classe User che estende Model. Includi il file genitore. Sovrascrivi la proprietà statica $tableName impostandola su "users". La classe eredita tutti i metodi da Model senza alcuna modifica.
  • Product.php: crea una classe Product che estende Model. Includi il file genitore. Sovrascrivi la proprietà statica $tableName impostandola su "products". Anche in questo caso, la classe eredita tutti i metodi dalla classe genitore.
  • main.php: includi sia il file User sia il file Product. Usa il metodo factory ereditato create() per creare istanze sia di User sia di Product. Per ogni istanza, chiama describe() e stampa il risultato su una riga dedicata. Poi stampa il nome della tabella per ciascuna classe usando User::getTable() e Product::getTable(), ciascuno su una riga dedicata.

La particolarità è che i metodi create() e getTable() sono definiti solo nella classe genitore Model, eppure funzionano correttamente con le classi figlie perché static:: rimanda il binding fino al runtime. Quando chiami User::create(), PHP crea un'istanza di User, non un'istanza di Model, perché static:: fa riferimento alla classe che è stata effettivamente chiamata.

L'output dovrebbe mostrare quattro righe: la descrizione dell'istanza User, la descrizione dell'istanza Product, il nome della tabella User e il nome della tabella Product.

Provalo tu

<?php

require_once 'User.php';
require_once 'Product.php';

// TODO: Usa il metodo factory ereditato create() per creare un'istanza di User

// TODO: Usa il metodo factory ereditato create() per creare un'istanza di Product

// TODO: Chiama describe() sull'istanza di User e stampa il risultato

// TODO: Chiama describe() sull'istanza di Product e stampa il risultato

// TODO: Stampa User::getTable()

// TODO: Stampa Product::getTable()

?>
quiz iconMettiti alla prova

Questa lezione include un breve quiz. Inizia la lezione per rispondere e tenere traccia dei tuoi progressi.

Tutte le lezioni di Programmazione orientata agli oggetti

Esercitati da solo: Compilatore PHP online