C'è qualche soluzione decente alla mancanza di generici di PHP che consenta all'ispezione del codice statico di rilevare la coerenza del tipo?
Ho una classe astratta, che voglio sottoclassare e far valere anche che uno dei metodi cambia dall'acquisizione di un parametro di un tipo, all'adozione di un parametro che è una sottoclasse di quel parametro.
abstract class AbstractProcessor {
abstract function processItem(Item $item);
}
class WoodProcessor extends AbstractProcessor {
function processItem(WoodItem $item){}
}
Questo non è consentito in PHP perché sta cambiando la firma dei metodi che non è consentita. Con i generici in stile Java potresti fare qualcosa del tipo:
abstract class AbstractProcessor<T> {
abstract function processItem(T $item);
}
class WoodProcessor extends AbstractProcessor<WoodItem> {
function processItem(WoodItem $item);
}
Ma ovviamente PHP non supporta quelli.
Google per questo problema, le persone suggeriscono di utilizzare instanceof
per controllare gli errori in fase di esecuzione ad es
class WoodProcessor extends AbstractProcessor {
function processItem(Item $item){
if (!($item instanceof WoodItem)) {
throw new \InvalidArgumentException(
"item of class ".get_class($item)." is not a WoodItem");
}
}
}
Ma funziona solo in fase di runtime, non ti consente di ispezionare il tuo codice per individuare errori utilizzando l'analisi statica - quindi esiste un modo ragionevole di gestirlo in PHP?
Un esempio più completo del problema è:
class StoneItem extends Item{}
class WoodItem extends Item{}
class WoodProcessedItem extends ProcessedItem {
function __construct(WoodItem $woodItem){}
}
class StoneProcessedItem extends ProcessedItem{
function __construct(StoneItem $stoneItem){}
}
abstract class AbstractProcessor {
abstract function processItem(Item $item);
function processAndBoxItem(Box $box, Item $item) {
$processedItem = $this->processItem($item);
$box->insertItem($item);
}
//Lots of other functions that can call processItem
}
class WoodProcessor extends AbstractProcessor {
function processItem(Item $item) {
return new ProcessedWoodItem($item); //This has an inspection error
}
}
class StoneProcessor extends AbstractProcessor {
function processItem(Item $item) {
return new ProcessedStoneItem($item);//This has an inspection error
}
}
Poiché sto passando solo un Item
to new ProcessedWoodItem($item)
e si aspetta un WoodItem come parametro, l'ispezione del codice suggerisce che c'è un errore.