Existe algum trabalho decente sobre a falta de genéricos do PHP que permita que a inspeção de código estático detecte a consistência do tipo?
Eu tenho uma classe abstrata, que quero subclassificar e também impor que um dos métodos mude de um parâmetro de um tipo para um parâmetro que é uma subclasse desse parâmetro.
abstract class AbstractProcessor {
abstract function processItem(Item $item);
}
class WoodProcessor extends AbstractProcessor {
function processItem(WoodItem $item){}
}
Isso não é permitido no PHP porque está alterando a assinatura dos métodos, o que não é permitido. Com os genéricos de estilo Java, você pode fazer algo como:
abstract class AbstractProcessor<T> {
abstract function processItem(T $item);
}
class WoodProcessor extends AbstractProcessor<WoodItem> {
function processItem(WoodItem $item);
}
Mas obviamente o PHP não suporta isso.
Google para este problema, as pessoas sugerem usar instanceof
para verificar erros em tempo de execução, por exemplo
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");
}
}
}
Mas isso só funciona em tempo de execução, não permite que você inspecione seu código quanto a erros usando análise estática - então existe alguma maneira sensata de lidar com isso no PHP?
Um exemplo mais completo do 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
}
}
Como estou passando apenas um Item
para new ProcessedWoodItem($item)
e ele espera um WoodItem como parâmetro, a inspeção do código sugere que há um erro.
fonte
Respostas:
Você pode usar métodos sem argumentos, documentando os parâmetros com doc-blocks:
Não vou dizer que acho que é uma boa ideia.
Seu problema é que você tem um contexto com um número variável de membros - em vez de tentar forçá-los como argumentos, uma idéia melhor e mais preparada para o futuro é introduzir um tipo de contexto para transportar todos os argumentos possíveis, para que o argumento lista nunca precisa mudar.
Igual a:
Os métodos estáticos de fábrica são opcionais, é claro - mas podem ser úteis, se apenas certas combinações específicas de membros produzirem um contexto significativo. Nesse caso, você também pode declarar
__construct()
como protegido / privado.fonte