Você está na página 1de 5

Utilizando Annotations com PHP Introduo Voc sabe o que so anotaes?

Se voc ainda no ouviu falar de annotations no sabe o que est perdendo. Para quem no sabe ainda, anotaes so etiquetas com informaes relevantes dentro de um bloco de comentrio no qual voc escreve metadados sobre alguma classe, mtodo ou mesmo atributos de classe para que se possa, em tempo de execuo resgatar esses metadados e trabalh-los de acordo com a sua necessidade. Eu testei o Addendum, uma biblioteca que estende a ReflectionClass e que funciona muito bem, para quem deseja anotar ao estilo dela. Porm, como eu teria que estender a classe Addendum e ainda teria que modificar seu namespace, isso no seria vivel para mim. A soluo para isso? Brincar um pouquinho com expresses regulares e a famlia de classes Reflection do PHP para resolver o meu problema. Pensando nas expresses regulares Se voc j leu uma classe documentada no padro PHPDoc ou JavaDoc j viu que existe um padro de criao desses comentrios. Eles, na maioria das IDEs, so reconhecidos e para alguns at servem de artifcio para que o code insight funcione, buscando essas informaes de comentrios, negritando nomes ou tipos de variveis e essas coisas. Um dos casos mais legais para servir de exemplo seria o caso de um ORM (ObjectRelation Mapping). H pouco tempo, eu e mais dois amigos concordamos em tocar um projeto de um ORM juntos. Esse ORM baseava-se na sintaxe do django e ao documentarmos pareceu um desafio bem legal. Pensamos nas anotaes e tudo mais para que pudssemos dividir as tarefas e uma delas era de criar classes baseadas nos tipos de dados que queramos para que elas fossem instanciadas nos atributos das classes. Ao abrir o cdigo de uma classe e vermos a sua documentao, ns podemos ver tags como @author, @since, @version etc. As anotaes tem o mesmo padro, com a diferena que podem ser personalizadas de acordo com a necessidade. No caso de um ORM a primeira que podemos falar seria algo como @table. Essa tag, claramente, nos informaria qual a tabela do banco seria a fonte de dados para essa classe. Veja um pequeno exemplo:
<? namespace Teste; /** * Classe que manipula a tabela de pessoas

* @author Evaldo Barbosa * @table=tb_pessoa */ class Pessoa { private $id; private $nome; } ?>

Veja, como falado acima, temos tags que so padro (como @author). Nesse nosso caso escrevemos a tag @table para informar qual a tabela seria a fonte dos dados. Outras sintaxes, principalmente quando formos anotar dados sobre atributos ser mostrados, pois podem necessitar de atributos multivalorados. Veja abaixo como podemos anotar os dois atributos (id e nome) da classe Pessoa mostrada acima:
<? namespace Teste; /** * Classe que manipula a tabela de pessoas * @author Evaldo Barbosa * @table=tb_pessoa */ class Pessoa { /** * @column(type=serial,sequence=tb_pessoa_id_seq); */ private $id; /** * @column(type=string,notnull=true,size=80); */ private $nome; } ?>

Veja que a tag @column pode ter seu valor type diferente para cada atributo. Um type serial para um campo serial, ou seja, uma sequence. No caso do mysql isso conhecido como auto_increment. O outro tipo (type), string, tem outros atributos como notnull (campo no nulo) e size (tamanho do campo). Com esses exemplos j podemos detectar alguns padres de anotaes e a partir dai comearmos a implementar as expresses regulares para apanhar essas anotaes. Como fazer e onde testar Expresses regulares so muito comuns em todas as linguagens e no PHP no diferente. Nas suas ltimas verses aposentamos algumas funes como ereg, eregi, ereg_replace e eregi_replace. Usaremos em nossos exemplos a funo preg_match_all que

serve melhor para esse propsito. De acordo com o escrito na sua documentao, existente no site php.net sob o endereo http://php.net/manual/en/function.preg-match-all.php, vemos o seguinte: int preg_match_all ( string $pattern , string $subject [, array &$matches [, int $flags = PREG_PATTERN_ORDER [, int $offset = 0 ]]] ), onde: $pattern: a nossa expresso regular, ou seja, a string que simboliza o padro a ser reconhecido $subject: o texto a ser pesquisado em busca do padro $pattern. No nosso caso ser o comentrio da classe ou do atributo; $matches: uma varivel do tipo array que receber todas as ocorrncias de $pattern em $subject. Se voc usa linux pode baixar o kiki para realizar os testes de expresses regulares. Se no, pode usar sites do http://regexpal.com/ ou http://www.solmetra.com/scripts/regex/ (esse ltimo mais alinhado nossa funo preg_math_all, pois voc pode escolher usar a mesma funo). O kiki , para o gnome, a sua melhor opo, nem precisa estar conectado. Sabendo disso, vamos ao prximo passo, que criar as expresses regulares para os nossos padres. Reconhecendo as suas anotaes Temos @table e @column, ambas tags que precisaramos no nosso para mapear essa classe, e cada uma com uma sintaxe diferente. Vejamos cada uma separadamente at encontrar cada padro. Para a nossa anotao de tabela, temos @table=tb_pessoa, que tem o seu padro representado por: @ + tag + = + valor. Para esse padro a nossa expresso regular ser a seguinte: @[\w]+[ ]{0,1}=[ ]{0,1}[\w]+, onde: [\w]+ a string que d nome tag; [ ]{0,1} um espao que pode ou no existir antes do =; = separa os dois termos (tag e valor) [ ]{0,1} um espao que pode ou no existir depois do =; [\w]+ a string que atribui valor anotao; J para a tag de coluna ns temos um padro um pouco diferente:

@tag + ( + valor + ). E a expresso regular para isso pode ser: @[\w]+[ ]{0,1}\([\w=, \.\-<>:]+, onde: [\w]+ a string que d nome tag; [ ]{0,1} um espao que pode ou no existir antes do =; \( escapa a abertura parnteses [ ]{0,1} um espao que pode ou no existir depois do =; [\w]+ a string que atribui valor anotao; \( escapa o fechamento parnteses. Para separar os valores dentro dos parnteses voc pode usar expresses regulares, explode ou outra funo que seja necessria. No momento vamos focar somente em encontrar as tags e descobrir seus valores de forma mais bruta. Para que usemos a expresso regular tanto para @table quanto para @column, vamos uni-las com o caracter | que concatena as duas formando um s $pattern. A implementao em PHP disso ficaria como no bloco abaixo:
<? $tags = array(); $pattern = '/@[\w]+[ ]{0,1}=[ ]{0,1}[\w]+|@[\w]+[ ]{0,1}\ ([\w=, \.\-<>:]+/'; $subject = '/** * Chave primria * @type= integer * @notnull = true * @size=255 * @meta (atributo1=valor1, atributo2=valor2) * @meta1 (atributo3=valor3) */'; preg_match_all( $pattern, $subject, $tags ); ?>

Como obter os comentrios e extrair as anotaes Em PHP usamos as classes de Reflection para obter os comentrios de classes, mtodos e atributos. Para um caso como o nosso devemos usar duas dessas classes: ReflectionClasse e ReflectionProperty. Extrair os comentrios de uma classe a tarefa de ReflectionClass. Ela uma classe muito fcil de usar e s precisa de um parmetro para que possamos comear seu uso. Vejamos:
<? ?> $reflection = ReflectionClass('Person'); $doc = $reflection->getDocComent();

Agora que j instanciamos ReflectionClass, podemos tranquilamente obter os comentrios de seus atributos instanciando ReflectionProperty como a seguir:
<? $reflection = ReflectionClass('Person'); $doc = $reflection->getDocComment(); $prop = ReflectionProperty($reflection,'id'); $comment_id = $prop->getDocComment(); ?>

No cdigo acima instanciamos ReflectionProperty e a partir dai obtemos os comentrios do atributo id na varivel $comment_id. Nesse momento j temos a faca e o queijo na mo, ou melhor, as expresses regulares e os comentrios que devem ser submetidos funo preg_match_all e nada mais nos falta. Concluso Anotaes em cdigo so coisas muito legais de se ver. Elas organizam, informam e ainda podem servir de metadados para que possamos reaproveitar melhor nossos cdigos, nos propiciando uma maior produtividade. Os cdigos acima foram parte da minha base de testes para reconhecer as expresses regulares das tags mostradas acima, mas no pense que outros padres no podem surgir. Eles, tanto para mim quanto para qualquer outro programador, podem aparecer diante de uma necessidade especfica. Tome a liberdade de testar outros padres. Eu pesquisei essa soluo porque outra (Addendum) no era exatamente o que eu precisava para o momento. Mais uma vez vemos aquela mxima de que A necessidade a me da inveno. Os cdigos aqui dispostos esto em uma classe que esto disponveis para a comunidade no endereo http://github.com/evaldobarbosa. Baixe, brinque, modifique, use. No se esquea de me informar se tiver algum outro padro interessante. Se soluo for interessante e voc quiser falar mais sobre, estou no @evaldobarbosa.