Back-End

4 set, 2014

Usando código XML em scripts PHP com XHP – Parte 01

Publicidade

Os scripts PHP tipicamente resultam em saída de código no formato HTML, normalmente interpretada por um navegador. Uma vez que existem muitas variáveis em scripts PHP, o código em si pode parecer meio enigmático, como pode ser visto na listagem 1. O código presente na listagem 1 despeja o conteúdo da variável $url em uma tag como um atributo do elemento href, construindo, assim, um link para o site da ADMIN magazine. As pessoas que mais sofrem com essa sopa de letrinhas são desenvolvedores de sistemas, de modo que têm dificuldade em identificar as variáveis mesmo em modelos simples de estrutura de scripts.

Figura 1: XHP gerando um simples link HTML.
Figura 1: XHP gera um link HTML link.

Listagem 1: um link simples em PHP

01 <?php
02 $url = 'http://www.admin‐magazine.com';
03 ?>
04 
05 <html><head><title></title></head><body>
06 <a href="<?php echo $url; ?>" >ADMIN Magazine</a>
07 </body></html>

A extensão do PHP chamada XHP torna o código mais fácil de ler, permitindo que sejam adicionados apenas o código HTML ao script e que sejam incorporadas as variáveis do PHP, como mostrado na listagem 2. O XHP converte XML (e, portanto, HTML) para blocos de código em PHP com expressões válidas. O resultado é uma notação curta, que diminui a taxa de erro e auxilia os programadores a manter uma visão geral mais organizada do código.

O XHP processa principalmente código XML e não processa corretamente algumas tags HTML por falta de padronização. Por exemplo, echo <img src = {$ file}>; gera um erro porque a tag <img> não foi fechada corretamente. O seguinte funciona: echo <img src = {$ arquivo} />;. Além disso, o XHP dedica uma atenção meticulosa às tags de início e fim aplicadas corretamente. Um <h1> Olá Mundo! </ h2>, por exemplo, faz com que o processamento do código pare. Em geral, só poderá ser visualizada uma página em branco.  No entanto, o XHP acrescenta itens que estejam faltando em atributos (por exemplo, aspas para a tag <href> exibida na listagem 2).

Listagem 2: um link simples com XHP

01 <?php
02 require 'init.php';
03 $url = 'http://www.admin‐magazine.com';
04 
05 echo <html><head><title>Test</title></head><body>
06
07 <a href={$url} >ADMIN Magazine</a> </body></html>;
08 ?>

Armadilhas

Para que o script da listagem 2 funcione, é necessário integrar o arquivo init.php através de um require no código-fonte do XHP, que por sua vez, vai pegar as dependências: core.php e html.php. Em última instância, você será obrigado a fornecer todos os três arquivos diretamente no código do seu aplicativo web. A extensão XHP simplesmente avalia a sintaxe XML; os arquivos PHP só foram mencionados, pois são necessários para que todo o resto funcione. Esse grupo de três arquivos reside tanto no subdiretório php-lib do arquivo fonte quanto pode ser vinculado diretamente do GitHub. A listagem 2 deliberadamente omite a definição sobre o tipo de documento. A linha a seguir causaria um erro:

echo <!DOCTYPE html>;

Isso informa ao elemento especial x:doctype que é um documento HTML5. Se o conteúdo entre <x:doctype> e </ x:doctype> for encapsulado, o XHP adicionará automaticamente o tipo de documento <! DOCTYPE html> no script. A listagem 3 mostra um exemplo (figura 2) .

Figura 2: o XHP substitui o elemento x:doctype por um tipo de documento de definição HTML (veja Listagem 3).
Figura 2: o XHP substitui o elemento x:doctype por HTML5 no tipo de definição do documento (veja Listagem 3).

Listagem 3: doctype HTML5

01 echo <x:doctype>
02  <html><head><title>Title</title></head>
03  <body>Hello World!</body>
04  </html>
05 </x:doctype>;

Será necessário estar atento ao ponto e vírgula final. É fácil esquecê-lo durante a programação, especialmente se o código HTML abrange várias linhas, como mostrado na listagem 3. Se o conteúdo presente dentro das chaves { } for interpretado pelo XHP como um código PHP completo, este não poderá conter simplesmente uma variável (como em PHP). Assim, digitando:

echo <p>{1+1}</p>;

retornará o valor 2 no navegador.

Caracteres de escape

Na listagem 4, um usuário poderia facilmente digitar o código HTML em um determinado campo e, em seguida, injetar esse código na página. No entanto, o XHP possui um mecanismo que desarma automaticamente códigos executáveis XML e códigos HTML, escapando-os com entidades de proteção. Se o código a ser escapado possui os elementos <<, como mostrado na figura 4, o XHP cria a entidade &lt; a partir dele, transformando-o em código não executável. Em PHP puro, você usaria a função htmlspecialchars() para isso, como no exemplo a seguir:

echo '<p>Your input: '.htmlspecialchars($_POST['input']).'</p>';

que é, de fato, o equivalente à linha 4 da listagem 4.

Figura 4: as tags HTML inseridas aqui acabam como texto na saída (veja Listagem 4).
Figura 4: as tags HTML digitadas serão convertidas em texto puro (veja Listagem 4).

Listagem 4: exemplo de uso de caracteres de escape

01 <?php
02 require 'init.php';
03
04 if($_POST['input']) echo
<p>Your input: {
$_POST['input']}</p>;
05
06 echo <form method="post">
07  <input type="text" name="input" />
08  <input type="submit" />
09  </form>;

O método appendChild() cria mais elementos li (veja Listagem 5).
O método appendChild() cria mais elementos li (veja Listagem 5).

Anexado objetivamente

O XHP usa uma abordagem extremamente sofisticada sob o capô. Ele executa inicialmente não apenas todas as marcações básicas que iniciam pela função echo, como também cria um objeto PHP separado para cada elemento XML ou HTML. Por exemplo, a declaração:

$list = <ul />;

não é seguida pela <ul /> , mas sim por um objeto que representa a lista HTML. Todos os objetos criados dessa forma pelo XHP automaticamente têm um método appendChild() para tornar mais fácil adicionar mais elementos filhos. A listagem 5 mostra um exemplo:

Listagem 5: utilizando o appendchild()

01 $ingredient = range(1, 3);
02 $list = <ul />;
03
04 foreach ($count as $number) {
05 $list‐>appendChild(<li>{$number}</li>);
06 }
07
08 echo <div><p>My numbers:</p>{$list}</div>;

Primeiro, O XHP cria uma lista vazia, sem numeração <ul />.  Em seguida, repete através dos elementos da matriz $number e gera um novo valor para cada entrada da lista <li> … </ li>. O resultado é o mostrado na figura 5. O XHP ignora todos os espaços, pois estes são espaços entre os elementos individuais. Assim, o XHP converte isto:

<p><span>Hello</span> <span>World</span></p>

para Hello World.

Clássico

Além dos elementos HTML especificados, também é possível definir elementos por conta própria. O XHP economiza uma quantidade enorme de tempo, pois, se for necessário apenas adicionar uma tag como <ingredients /> no código, ela será trocada automaticamente pelo XHP para uma lista HTML com ingredientes. Dessa forma, é possível rapidamente montar um modelo de template. Para usar um elemento, é necessário criar apenas uma nova classe; no entanto, algumas condições se aplicam à classe: deve ser derivado do :x:element da classe pai, que – entre outras coisas – fornece a função appendChild() apresentada anteriormente. Além disso, o nome da classe deve usar o nome da tag e começar com dois pontos. Finalmente, é necessário implementar a função render(), que retorna objetos XHP.

A listagem 6 mostra um exemplo que cria um novo elemento chamado ingredients. O primeiro passo é definir uma classe de combinação :ingredients. Isso ensina o XHP a trabalhar com a tag <ingredients />.Uma vez que a tag aparecer em algum lugar do código PHP, o XHP criará o objeto (linha 7 da listagem 6). Uma vez que a saída echo inicia o elemento, o XHP chama automaticamente o método render(), que retorna uma lista pequena de dados. Nesse ponto, a listagem 6 usa um truque: os elementos <ul> … </ ul> referentes à lista de dados retornada por render() são automaticamente transformados pelo XHP em um objeto XHP. Em outras palavras, o render() retorna um objeto XHP, conforme solicitado. Retornar a string “um artigo”, no entanto, resultaria em um erro.

A listagem 6 declara a classe :ingredients (figura 6). Aparentemente, por conter a estrutura de um namespace XML, o XHP usa um prefixo separado por dois pontos do nome real do elemento. Aqui, é possível selecionar o prefixo admin para que toda a classe atenda pelo nome de admin:ingredients e a tag será apresentada como <admin:ingredients />.
No entanto, isso é apenas uma convenção; você pode deixar de fora o prefixo (ou namespace) em seus próprios projetos.

Listagem 6: Classe de exemplo

01 class :ingredients extends :x:element {
02  protected function render() {
03 return <ul><li>flour</li><li>sugar</li></ul>;
04  }
05 }
06
07 $list = <ingredients/>;
08 echo <div> {$list} </div>;

Métodos primitivos

O elemento ingredients automaticamente retorna uma lista (veja Listagem 6).
O elemento ingredients automaticamente retorna uma lista (veja Listagem 6).

O método render() deve sempre retornar objetos compatíveis com o XHP. Se queremos apenas retornar uma string, será necessário derivar seu elemento da classe pai x:primitive. No entanto, essa classe é expressamente voltada para “elementos de baixo nível”, tais como os elementos HTML ou uma tag <h1> que precisa de uma saída de marcação de elemento ou tags. Em contraste com o x:element, as classes derivadas de x:primitive são necessárias para implementar o método stringify(), que por sua vez retorna uma string que também acaba na saída posterior. A listagem 7 mostra um exemplo de um elemento chamado ingredient, no qual o método stringify() simplesmente exibe a string <div> flour </ div>.

A listagem 6 e a listagem 7 não se preocupam com os elementos filho. No caso a seguir:
echo <admin:ingredient> <div>sugar</div> </admin:ingredient>;

a saída ainda seria flour, como mostrado na figura 7.

Em vez de div, stringify() também poderia dar como saída tags definidas por usuário (veja Listagem 7).
Dentro da div, stringify() vai retornar tags definidas pelo usuário (veja Listagem 7).

A string <div> sugar </ div> é um elemento filho de admin:ingredient. Um loop será necessário para que o stringify() possa percorrer todos os elementos filhos e integrá-los na saída dos dados (figura 8).

Listagem 7: Derivativas de :x:primitive
01 class: admin:ingredient extends :x:primitive {
02  protected function stringify() {
03 return '<div>flour</div>';
04  }
05 }
06
07 $elem = <admin:ingredient />;
08 echo $elem;
Nesse caso, ingredient também gera como saída seu elemento filho (veja a Listagem 8), que é um elemento div.
Nesse caso, o elemento ingredient também retorna elementos filhos (veja a Listagem 8), que consistem de um elemento div.
A listagem 8 mostra o loop, que utiliza o método getChildren() pararetornar todos os elementos filhos do objeto. O código presente na listagem 8está disponível para todas as classes derivadas do elemento x:element.
Listagem 8: Evolução dos elementos filhos

01 class: admin:ingredient extends :x:primitive {
02  protected function stringify() {
03 foreach ($this‐>getChildren() as $child) {
04 $output .= :x:base::renderChild($child);
05  }
06  $output .= '<div> flour </div>';
07  return $output;
08  }
09 }
10 
11 $elem = <admin:ingredient> <div>sugar</div> </admin:ingredient>;
12 echo $elem;

***

Na segunda e última parte, veremos a continuação, que vai abordar características e regras.