quarta-feira, 20 de maio de 2009

Reflections Entendidas e Estendidas

PHP Reflections são recursos muito interessante que se você ainda não conhece precisa conhecer agora. O princípio é basicamente o seguinte: Você pode executar métodos que lhe trazem detalhamento sobre o funcionamento de arquivos / classes / funções que existam no seu código em execução.


/**
* Apenas um pequeno exemplo
*
/

class exemplo
{}

$objReflection = new ReflectionClass( $strClass );
print $objReflection->getClassName();
print $objReflection->getDocComment
();



Utilizando reflections é possível, por exemplo, se listar os parametros de um método ou função, podendo obter o nome, tipo e valor padrão, se houver, se cada um deles. Esses recursos podem ser extremamente úteis para possibilitar um debuger melhor, como se pode ver na ferramenta de backtrace do Renan de Lima.

Mas, as vezes, para poder se fazer funcionalidades realmente avançadas pode-se precisar d estender a classe das notações. Isso pode se tornar algo um tanto trabalhoso de se fazer tendo em vista as referencias cruzadas.

Por exemplo:


class teste{

}


class novaReflection extends ReflectionClass{}
$objNovo =
new novaReflection( "teste" );


if ( $objNovo->getMethod( 'vai' )->getDeclaringClass() instanceofnovaReflection )

{

print "bem que você gostaria.";
}

else

{
print "problema dificil de resolver";
}


Ao se tentar pegar a classe de reflexão da classe que contém o método ‘vai’ o php não retornou a classe estendida ‘novaReflection’ mas a classe ReflectionClass original. A referencia Elemento->getFilho()->getPai() falha não retornando um objeto da mesma classe do Elemento.

Essa situação problema se replica em todas as classes do Reflection. E, na idéia de se facilitar a criação de estensões para classes de reflection eu lhes ofereço o pacote de ExtendedReflection. Esse pacote de classes são estensões das classes do Reflection, mas com métodos chaves que, ao serem sobrepostos tornam fácil o processo de criação de estensões das classes dos reflections.

Ficou confuso? Então vamos a um exemplo:
Ao invés de se estender diretamente a classe do ReflectionClass você irá estender a classe ExtendedReflectionClass e deverá sobrescrever os seguintes métodos:

  • createExtendedReflectionClass
  • createExtendedReflectionProperty
  • createExtendedReflectionMethod

Estes métodos servem para converter um objeto do pacote Reflection nativo em uma objeto do pacote estendido de Reflection, tornando os links internos válidos.

Um pacote estendido para demonstrar isso é o Code Reflection que estende o pacote ExtendedReflection adicionando métodos novos nas classes de reflection o que torna possível se obter os códigos php dos objetos refletidos.

note em especial os seguintes métodos:

/**
* Make the recursive calls and indirectly call return the extended reflection object and not
* a native reflection property.
*
* @see ExtendedReflectionClass::createExtendedReflectionProperty( ReflectionProperty )
* @param ReflectionProperty $objOriginalReflectionClass
* @return CodeReflectionProperty
*/

protecte function createExtendedReflectionProperty ( ReflectionProperty $objOriginalReflectionProperty )
{
return new CodeReflectionProperty( $this->getName() , $objOriginalReflectionProperty->getName() );
}

/**
* Make the recursive calls and indirectly call return the extended reflection object and not
* a native reflection method.
*
* @see ExtendedReflectionClass::createExtendedReflectionMethod( ReflectionMethod )
* @param ReflectionMethod $objOriginalReflectionClass
* @return CodeReflectionMethod
*/

protected function createExtendedReflectionMethod ( ReflectionMethod $objOriginalReflectionMethod )
{
return new CodeReflectionMethod ( $this->getName() , $objOriginalReflectionMethod->getName() );
}


Todas as referencias a objetos do pacote do reflection externos passaram antes, necessariamente, por esses métodos, que convertem o objeto reflection nativo no objeto reflection estendido.


Deste modo você tem a flexibilidade de estender uma classe com a riqueza dos recursos de uma classe de reflexão sem complicação, podendo gerar produtos bem legais como estamos fazendo no CodeToDiagram

2 comentários:

marcelo_bezerra_df disse...

Legal o post.

Alguns detalhes que merecem, eu não diria correção, mas sim pequenos (possíveis) ajustes:

- riquesa não seria com z?

- quando voce falar de estender, como verbo, é com S mesmo. Mas quando disser "estensões" , será que não se escreveria com X?

Fora isso, é algo que pode ser útil pra muita gente. Já pensou em postar/comunicar isso em listas especializadas e outros sites, além do seu blog?

Blaine disse...

Riqueza corrigida! =D OOOpps...

"Extender" é neologismo, necessário talvez mas na dúvida deixarei o "estender" por enquanto. Mas a idéia é essa mesmo de "extends".

Já pensei em postar isso em outros lugares sim, mas antes disso pretendo criar tutoriais de cada um desses pacotes em português e em inglês, passar todos eles num teste unitário, documentar 100% a API, etc. No caso desse pacote já está documentado, mas ainda não está 100% testado e sem tutoriais legais. E normalmente eu não posto em listas mas faço o link no próprio site do php naquele fórum a respeito de uma funcionalidade. Devo fazer isso em breve.

Claro que se, em alguma das listas que eu assino, alguém apresentar um problema nesse escopo eu encaminharei a solução para o mesmo.