Métodos Compartilhados no PHP

Artigo que apresenta um recurso curioso do PHP, que permite compartilhar métodos entre classes sem realizar herança direta.

PHP oferece um recurso bastante curioso, que não sei se é parte do Paradigma Orientado a Objetos, mas que tem lá suas utilidades. Chamei de "Método Compartilhado" por não saber se esta característica tem nome.

Basicamente, a ideia é existir uma classe (chamaremos de "X") com um método e esse método manipula atributos que nem foram declarados na própria classe. Então outra classe (chamaremos de "Y"), sem estender a classe "X", tem um método que invoca o tal método da classe "X" de maneira estática. O comportamento visto em PHP é que o "$this" usado no método da classe "X" irá manipular o objeto instanciado da classe "Y". Estranho, né!? Porém, a visibilidade dos atributos é respeitada, portanto, a classe "X" não consegue ver atributos privados ou protegidos da classe "Y" de maneira direta.

Em que isso pode ser útil? Talvez criar métodos genéricos, que são utilizados por diferentes classes sem a necessidade da extensão. Vamos a um exemplo com a ajudinha de reflection, para contornar a visibilidade de atributos privados:

abstract class generico {

    /**
     * Metodo generico que incrementa um atributo de qualquer classe.
     * @param string $atributo Nome do atributo
     * @return void
     */
    public function incrementar($atributo) {

        // Usando reflexao para burlar o acesso ao atributo
        $rc = new ReflectionClass(get_class($this));
        if (!$rc->hasProperty($atributo)) {
            throw new InvalidArgumentException('Atributo nao existe: '.$atributo, 1);
        }

        // Obter reflexao do atributo
        $ra = $rc->getProperty($atributo);

        // Torna-lo acessivel (publico)
        $ra->setAccessible(true);

        // Incrementar o valor e defini-lo no objeto
        $valor = $ra->getValue($this) + 1;
        $ra->setValue($this, $valor);

        // Torna-lo inacessivel novamente
        $ra->setAccessible(false);
    }
}

final class exemplo {
    private $x = 1;

    /**
     * Incrementa o valor do atributo "x"
     * @return void
     */
    public function teste() {
        generico::incrementar('x');
    }

    /**
     * Obtem o valor do atributo "x"
     * @return int
     */
    public function getX() {
        return $this->x;
    }
}


final class exemplo2 {
    private $y = 1;

    /**
     * Incrementa o valor do atributo "y"
     * @return void
     */
    public function teste() {
        generico::incrementar('y');
    }


    /**
     * Obtem o valor do atributo "y"
     * @return int
     */
    public function getY() {
        return $this->y;
    }
}

Agora vamos usar as classes criadas:

$e1 = new exemplo();
$e1->teste();
echo $e1->getX(); // imprime 2

$e2 = new exemplo2();
$e2->teste();
echo $e2->getY(); // imprime 2

Observe que o método incrementar da classe generico é um método genérico, já que consegue manipular qualquer classe, desde que seja informado o atributo que deseja-se incrementar (e o atributo exista). A lógica do método (incrementar um valor inteiro) foi usada em duas classes diferentes (exemplo e exemplo2).

Ou seja, sem usar extensão, conseguimos generalizar uma lógica que foi usada em duas classes. De fato, foi preciso chamar o método genérico explicitamente nas duas classes, porém, isso pode ser contornado e/ou explorado através do método mágico __call. Este método mágico do PHP é chamado toda vez que se invoca um método que não é explicito da classe e nem foi herdado.

0 comentários