Criando seu próprio framework PHP
Templating
/**
* Este é o quinto de uma série de artigos traduzidos e adaptados
* a partir dos originais: "Create your own PHP Framework -
* http://symfony.com/doc/current/create_framework/index.html
* by Fabien Potencier.
*/
O leitor mais atento deve ter notado que nosso framework engessou a forma como o código dos templates é executado. Para páginas simples, como as que foram criadas até agora, isso não é um problema. Mas quando quisermos adicionar mais lógica, seremos forçados a colocar essa lógica diretamente nos templates, o que geralmente não é uma boa ideia. Ainda mais se você estiver seguindo o princípio Separation of Concerns.
Vamos separar o código do template da lógica de negócios adicionando uma nova camada: o Controller. A missão do controller é gerar uma resposta baseada nas informações contidas na requisição feita pelo cliente.
Altere a parte do nosso framework que trata da renderização dos templates para ficar da seguinte forma:
Como agora a renderização está sendo realizada por uma função externa render_template()
precisamos passar pra ela os atributos extraídos da URL. Nós poderíamos passar um argumento adicional, mas ao invés disso utilizaremos outra funcionalidade da classe Request chamada attributes: Request attributes é uma forma de adicionar ao objeto Request informações que não estão diretamente ligadas a requisição HTTP.
Com isso podemos criar a função render_template()
, que nada mais é do que um controller genérico que renderiza templates quando não há nenhuma lógica específica.
Para manter o mesmo template anterior, os atributos da requisição são extraídos antes do template ser renderizado:
function render_template($request)
{
extract($request->attributes->all(), EXTR_SKIP);
ob_start();
include sprintf(__DIR__.'/../src/pages/%s.php', $_route);
return new Response(ob_get_clean());
}
Como render_template
é utilizada através de um argumento passado para a função call_user_func()
, podemos substituí-la por qualquer callback PHP válido. Isso possibilita o uso:
- de uma função.
- uma função anônima.
- ou o método de uma classe como um controller.
… você escolhe.
Por convenção, para cada rota, o controller associado é configurado pelo atributo _controller
de um objeto Router:
$routes->add('hello', new Routing\Route('/hello/{name}', array(
'name' => 'World',
'_controller' => 'render_template',
)));
try {
$request->attributes->add($matcher->match($request->getPathInfo()));
$response = call_user_func($request->attributes->get('_controller'), $request);
} catch (Routing\Exception\ResourceNotFoundException $exception) {
$response = new Response('Not Found', 404);
} catch (Exception $exception) {
$response = new Response('An error occurred', 500);
}
Agora podemos associar uma rota a qualquer controller e é claro, dentro de um controller você ainda pode utilizar render_template()
para renderizar um template:
$routes->add('hello', new Routing\Route('/hello/{name}', array(
'name' => 'World',
'_controller' => function ($request) {
return render_template($request);
}
)));
Isso é bem flexível pois você pode alterar o objeto Response posteriormente e até mesmo passar argumentos adicionais para o templete:
$routes->add('hello', new Routing\Route('/hello/{name}', array(
'name' => 'World',
'_controller' => function ($request) {
// $foo will be available in the template
$request->attributes->set('foo', 'bar');
$response = render_template($request);
// change some header
$response->headers->set('Content-Type', 'text/plain');
return $response;
}
)));
Eis a versão atualizada e aprimorada do nosso framework:
Para celebrar o nascimento do nosso novo framework, vamos criar uma nova aplicação que necessita de uma lógica simples. Nossa aplicação possui uma página que diz se um ano é bissexto ou não. Ao acessar /is_leap_year
, você obtém a resposta para o ano corrente, mas você tembém pode especificar o ano desejado /is_leap_year/2009
. Sendo genérico, o framework não precisa ser modificado, basta criar um novo arquivo app.php
:
A função is_leap_year()
retorna true
quando o ano informado é bissexto, false
caso contrário. Se o $year
for null
, o ano corrente é testado. O controller é simples: ele obtém o ano dos atributos da requisição, passa para a função is_leap_year()
, de acordo com a resposta cria um novo objeto Response.
Como sempre, você pode parar por aqui e usar o framework como ele está. Provavelmente é só isso que você precisa para criar sites simples como esses e alguns outros.
Acompanhe o desenvolvimento através deste repositório no GitHub.
Quer ver os artigos anteriores desta série?
Quarto artigo:
Terceiro artigo:
Segundo artigo:
Primeiro artigo: