Pergunta

Visão geral

Por volta do final de 2009, escrevi um sistema de templates simples para PHP/HTML para ser usado internamente por nossos designers em sites do tipo brochura.O objetivo do sistema é permitir modelos em HTML puro por meio de tags personalizadas que são processadas por PHP.Por exemplo, uma página de modelo pode ter esta aparência:

<tt:Page template="templates/main.html">
  <tt:Content name="leftColumn">
    <p> blah blah </p>
    ...
  </tt:Content>
  <tt:Content name="rightColumn">
    <p> blah blah </p>
    ...
  </tt:Content>
</tt:Page>

O modelo em si pode ser parecido com isto:

<html>
  <head>...</head>
  <body>
    <div style="float:left; width:45%">
      <tt:Container name="leftColumn" />
    </div>
    <div style="width:45%">
      <tt:Container name="rightColumn" />
    </div>
  </body>
</html>

Além das tags Page e Content/Container, existem algumas outras tags incluídas no núcleo para coisas como controle de fluxo, iteração sobre uma coleção, saída de valores dinâmicos, etc.A estrutura foi projetada para que seja muito fácil adicionar seu próprio conjunto de tags registradas sob outro prefixo e namespace.

Tags personalizadas para PHP

Como analisamos essas tags personalizadas?Como não há garantia de que o arquivo HTML seja XML bem formado, soluções como XSLT/XPATH não serão confiáveis.Em vez disso, usamos uma regex para procurar tags com prefixos registrados e substituí-las por código PHP.O código PHP é um design baseado em pilha...ao encontrar uma tag de abertura, um objeto que representa a tag é criado, colocado na pilha e sua "função de inicialização" (se houver) é executada.Sempre que uma tag de fechamento registrada é encontrada, o objeto mais recente é retirado da pilha e sua "função de renderização" é executada.

Então, depois que o framework substituir as tags de modelo por PHP, nossa página de exemplo poderá ficar mais ou menos assim (na verdade, é um pouco mais feia):

<?php $tags->push('tt', 'Page', array('template'=>'templates/main.html')); ?>
  <?php $tags->push('tt', 'Content', array('name'=>'leftColumn')); ?>
    <p> blah blah </p>
    ...
  <?php $tags->pop(); ?>
  <?php $tags->push('tt', 'Content', array('name'=>'rightColumn')); ?>
    <p> blah blah </p>
    ...
  <?php $tags->pop(); ?>
<?php $tags->pop(); ?>

O bom, o ruim e eval

Agora, como executar nosso código PHP recém-gerado?Posso pensar em algumas opções aqui.O mais fácil é simplesmente eval a string, e isso funciona bem o suficiente.No entanto, qualquer programador lhe dirá "eval é mau, não o use..." então a questão é: existe algo mais apropriado do que eval que podemos usar aqui?

Eu considerei usar um arquivo temporário ou em cache, usando php:// fluxos de saída, etc, mas até onde posso ver, eles não oferecem nenhuma vantagem real sobre eval.O cache pode acelerar as coisas, mas na prática todos os sites que temos sobre esse assunto já são extremamente rápidos, então não vejo necessidade de fazer otimizações de velocidade neste momento.

Questões

Para cada uma das coisas nesta lista:é uma boa ideia?Você consegue pensar em uma alternativa melhor?

  • toda a ideia em geral (tags personalizadas para html/php)
  • convertendo tags em código php em vez de processar diretamente
  • a abordagem baseada em pilha
  • o uso de eval (ou similar)

Obrigado pela leitura e TIA por qualquer conselho.:)

Foi útil?

Solução

Deixe-me defender uma abordagem diferente.Em vez de gerar código PHP dinamicamente e depois tentar descobrir como executá-lo com segurança, execute-o diretamente ao encontrar as tags.Você pode processe todo o bloco de HTML de uma só vez e manipule cada tag conforme você a encontra imediatamente.

Escreva um loop que procure tags.Sua estrutura básica ficará assim:

  1. Procure uma tag personalizada, que você encontra na posição n.
  2. Tudo antes da posição n deve ser HTML simples, então salve-o para processamento ou imprima-o imediatamente (se você não tiver tags em seu $tags pilha, você provavelmente não precisará salvá-la em lugar nenhum).
  3. Execute o código apropriado para a tag.Em vez de gerar código que chama $tags->push, apenas ligue $tags->push diretamente.
  4. Volte para a etapa 1.

Com essa abordagem, você apenas chama funções PHP diretamente, nunca cria código PHP dinamicamente e o executa mais tarde.A necessidade de eval se foi.

Você basicamente terá dois casos para a etapa 3.Ao encontrar uma tag de abertura, você fará uma ação imediata push.Mais tarde, quando você clicar na tag de fechamento, poderá fazer um pop e manipule a tag da maneira apropriada, agora que você processou todo o conteúdo do elemento personalizado.

Também é mais eficiente processar o HTML desta forma.Fazer várias pesquisas e substituições em uma string HTML longa é ineficiente, pois cada pesquisa e cada substituição são O(n) no comprimento da string.Isso significa que você está varrendo a string repetidamente e cada vez que faz uma substituição, você precisa gerar novas strings de comprimento semelhante.Se você tiver 20 KB de HTML, cada substituição envolverá a pesquisa nesses 20 KB e a criação de uma nova string de 20 KB posteriormente.

Outras dicas

Postei outra resposta porque é radicalmente diferente da primeira, o que também pode ser valioso.

Essencialmente, esta questão pergunta como executar código PHP com regex.Pode não parecer tão óbvio, mas é isso que a avaliação pretende realizar.

Com isso dito, em vez de passar preg_replace e depois fazer uma avaliação, você poderia simplesmente usar a função preg_replace_callback do PHP para executar um trecho de código quando correspondido.

Veja aqui como funciona a função: http://us.php.net/manual/en/function.preg-replace-callback.php

Sim, em vez de eval, você pode fazer o que o zend e outros frameworks importantes fazem, usar buffer de saída:

ob_start();
include($template_file); //has some HTML and output generating PHP
$result = ob_get_contents();
ob_end_clean();
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top