¿Existe algún trabajo decente en torno a la falta de genéricos de PHP que permitan la inspección de código estático para detectar la consistencia de tipo?
Tengo una clase abstracta, que quiero subclasificar y también imponer que uno de los métodos cambie de tomar un parámetro de un tipo, a tomar un parámetro que es una subclase de ese parámetro.
abstract class AbstractProcessor {
abstract function processItem(Item $item);
}
class WoodProcessor extends AbstractProcessor {
function processItem(WoodItem $item){}
}
Esto no está permitido en PHP porque está cambiando la firma de métodos que no está permitido. Con los genéricos de estilo Java podrías hacer algo como:
abstract class AbstractProcessor<T> {
abstract function processItem(T $item);
}
class WoodProcessor extends AbstractProcessor<WoodItem> {
function processItem(WoodItem $item);
}
Pero obviamente PHP no los admite.
Google para este problema, la gente sugiere usar instanceof
para verificar errores en tiempo de ejecución, por ejemplo
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");
}
}
}
Pero eso solo funciona en tiempo de ejecución, no le permite inspeccionar su código en busca de errores utilizando análisis estático, entonces, ¿hay alguna forma sensata de manejar esto en PHP?
Un ejemplo más completo del problema es:
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
}
}
Debido a que paso solo un Item
to new ProcessedWoodItem($item)
y espera un WoodItem como parámetro, la inspección del código sugiere que hay un error.
fuente
Respuestas:
Puede usar métodos sin argumentos, documentando los parámetros con doc-blocks en su lugar:
Sin embargo, no voy a decir que creo que es una buena idea.
Su problema es que tiene un contexto con un número variable de miembros; en lugar de tratar de forzarlos como argumentos, una idea mejor y más a prueba de futuro es introducir un tipo de contexto para llevar todos los argumentos posibles, de modo que el argumento La lista nunca necesita cambiar.
Al igual que:
Los métodos de fábrica estáticos son opcionales, por supuesto, pero podrían ser útiles si solo ciertas combinaciones específicas de miembros producen un contexto significativo. Si es así, es posible que también desee declararlo
__construct()
como protegido / privado.fuente