Entre as premissas do Zend Framework 2 (ZF2) está em resolver questões relativas à performance. Para resolver isso uma das estratégias foi redesenhar a forma como a antiga classe Zend_Loader_Autoloader (ZF1) trabalha, abusando, se é que se pode dizer isso, do pattern Lazy Loading, cujo objetivo é adiar tanto quando possível o carregamento de um objeto até que ele seja realmente necessário.
Neste post vou apresentar 4 formas diferentes de carregar suas classes, sendo 3 delas desacopladas da estrutura de aplicação do ZF2. Será apresentado também a forma de se “chamar” classes que utilizam namespaces e aquelas que utilizam prefixo.
Baixe o código-fonte deste artigo: autoloading
À moda antiga…
No Zend Framework 1 realizamos o autoloading de nossas classes mais ou menos assim:
define('DS',DIRECTORY_SEPARATOR);
define('PS',PATH_SEPARATOR);
set_include_path ( get_include_path() . PS . __DIR__ .
DS . 'library' . PS .
'/home/brainfork/zend-framework/zf1/library/');
require_once 'Zend'. DS .'Loader'. DS .'Autoloader.php';
$autoloader = Zend_Loader_Autoloader::getInstance();
//Classes com namespace
$autoloader->registerNamespace('ComNamespace');
//Classes sem namespace (com prefixo)
$autoloader->registerNamespace('ComPrefixo_');
$com_namespace = new ComNamespace\Foo();
$com_namespace->bar();
$com_prefixo = new ComPrefixo_Foo();
$com_prefixo->bar();
Note que na linha 01 inclui a pasta ‘library’ do meu diretório corrente com __DIR__ . DS . 'library'.
Ao fazer a chamada para ComNamespace\Foo ou ComPrefixo_Foo o ZF tem que realizar uma “tradução” nos nomes das classes convertendo barras ou sinais de underscore para separadores de diretório e então percorrer todos os caminhos de inclusão (include paths) definidos para encontrar o arquivo correspondente.
No ZF2 a estratégia de exigir, de alguma forma, que o desenvolvedor informe o caminho onde se encontram as classes que ele deseja carregar. Isso pode ser feito manualmente, informando os caminhos de acordo com o namespace ou prefixo ou por meio de um mapeamento completo feito com o auxílio do script classmap_generator.php, que veremos mais adiante.
Registrando meio meio de métodos
No exemplo abaixo, o mapeamento foi realizado utilizando-se os métodos registerNamespace e registerPrefix:
define('DS',DIRECTORY_SEPARATOR);
define('PS',PATH_SEPARATOR);
set_include_path ( get_include_path() . PS .
'/home/brainfork/zend-framework/zf2/library/' );
require_once 'Zend'. DS .'Loader'. DS .'StandardAutoloader.php';
$autoloader = new Zend\Loader\StandardAutoloader();
$autoloader->register();
//Classe com namespace
$autoloader->registerNamespace('ComNamespace', __DIR__ . DS . 'library' . DS . 'ComNamespace');
//Classes sem namespace (com prefixo)
$autoloader->registerPrefix('ComPrefixo_', __DIR__ . DS . 'library' . DS . 'ComPrefixo');
$com_namespace = new ComNamespace\Foo();
$com_namespace->bar();
$com_prefixo = new ComPrefixo_Foo();
$com_prefixo->bar();
Veja que na linha 04 eu já não incluo mais o diretório ‘library’ do meu projeto.
Registrando por Array
A segunda forma é chamar o passando para ele um array com o mapeamento das classes. Veja:
define('DS',DIRECTORY_SEPARATOR);
define('PS',PATH_SEPARATOR);
set_include_path ( get_include_path() . PS .
'/home/brainfork/zend-framework/zf2/library/' );
require_once 'Zend'. DS .'Loader'. DS .'AutoloaderFactory.php';
use Zend\Loader\AutoloaderFactory as Loader;
Loader::factory(
array(Loader::STANDARD_AUTOLOADER =>
array(
//Classe com namespace
'namespaces' => array(
'ComNamespace' => __DIR__ . DS . 'library' . DS . 'ComNamespace',
),
//Classes sem namespace (com prefixo)
'prefixes' => array(
'ComPrefixo_' => __DIR__ . DS . 'library' . DS . 'ComPrefixo',
),
)
)
);
$com_namespace = new ComNamespace\Foo();
$com_namespace->bar();
$com_prefixo = new ComPrefixo_Foo();
$com_prefixo->bar();
O Classmap Generator
A terceira forma de realizar o autoloading fora da estrutura de aplicação do ZF2 é utilizando a ferramenta classmap_generator.php
O que o classmap_generator faz é percorrer o diretório informado, varrendo em busca de classes para gerar um arquivo autoload_classmap.php, que nada mais é do que um array contendo um mapeamento explícito entre classes e seus respectivos caminhos. Veja no exemplo abaixo o código referente as duas classes que tenho no diretório ‘library’ do projeto:
// Generated by ZF2's ./bin/classmap_generator.php
return array(
'ComPrefixo_Foo' => __DIR__ . '/ComPrefixo/Foo.php',
'ComNamespace\Foo' => __DIR__ . '/ComNamespace/Foo.php',
);
Veja que neste caso não foram mapeados apenas os namespaces ou prefixos mas a classe diretamente, sendo a solução que apresenta melhor performance.
Para criar o mapeamento eu utilizo a ferramenta pelo console, dentro do diretório que eu desejo que seja mapeado, informando para o PHP o caminho completo par ao script. A saída é o arquivo autoload_classmap.php.
brainfork@bnix / $ cd /var/www/zf2labs/library brainfork@bnix /var/www/zf2labs/library $ brainfork@bnix /var/www/zf2labs/library $ php /home/brainfork/zend-framework/zf2/bin/classmap_generator.php -w Creating class file map for library in '/var/www/zf2labs/library'... Wrote classmap file to '/var/www/zf2labs/library/autoload_classmap.php'
A seguir você vê como o autoloader utiliza o arquivo autoload_classmap.php:
define('DS',DIRECTORY_SEPARATOR);
define('PS',PATH_SEPARATOR);
set_include_path ( get_include_path() . PS .
'/home/brainfork/zend-framework/zf2/library/' );
require_once 'Zend'. DS .'Loader'. DS .'ClassMapAutoloader.php';
$autoloader = new Zend\Loader\ClassMapAutoloader(
array(__DIR__ . DS . 'library'. DS .'autoload_classmap.php') );
$autoloader->register();
$com_namespace = new ComNamespace\Foo();
$com_namespace->bar();
$com_prefixo = new ComPrefixo_Foo();
$com_prefixo->bar();
Module Autoloader
Finalmente, a 4ª e última forma que irei apresentar. Desta vez demonstrarei como é feito o mapeamento de suas bibliotecas como módulos na estrutura de uma aplicação ZF2.
Parti da aplicação de exemplo Zend Skeleton Application disponível em https://github.com/zendframework/ZendSkeletonApplication/zipball/master
A abordagem de módulos no ZF2 foi remodelada para prover maior flexibilidade e reuso. A idéia é também permitir que a comunidade escreva e disponibilize seus módulos para serem utilizados por outras pessoas de maneira simples. Provavelmente os módulos serão disponibilizados em http://modules.zendframework.com/
A estrutura básica de uma aplicação do ZF2 é mostrada a seguir:
application_root/
config/
application.config.php
data/
module/
vendor/
public/
.htaccess
index.php
Já a estrutura de um módulo MVC você vê abaixo:
module_root/
Module.php
autoload_classmap.php
autoload_function.php
autoload_register.php
config/
module.config.php
public/
images/
css/
js/
src/
/
<code>
test/
phpunit.xml
bootstrap.php
/
view/
Fiz algumas modificações no Zend Skeleton Application, especialmente no layout, de modo a tornar a aplicação mais “limpa”.
Para incluir meu módulo fiz basicamente as seguintes alterações:
1º – Modifiquei o arquivo .htaccess, informando a localização do ZF2 na variável de ambiente ZF2_PATH.
SetEnv APPLICATION_ENV local
SetEnv ZF2_PATH "/home/brainfork/zend-framework/zf2/library/"
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} -s [OR]
RewriteCond %{REQUEST_FILENAME} -l [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^.*$ - [NC,L]
RewriteRule ^.*$ index.php [NC,L]
2º – Modifiquei o arquivo application.config.php, acrescentando meu módulo:
return array(
'modules' => array(
'Application',
'MinhaEmpresa',
),
'module_listener_options' => array(
'module_paths' => array(
'./module',
'./vendor',
),
),
);
3º – Criei a estrutura de arquivos e diretórios do meu módulo, copiando a estrutura do modulo Application que vem no Zend Skeleton Application:
Estou acrescentando um módulo que servirá apenas como uma biblioteca externa, não como um módulo MVC, por isso removi as pastas src, view e config.
vendor/
MinhaEmpresa/
library/
MeuPacote/
Foo.php
autoload_classmap.php
autoload_function.php
autoload_register.php
Module.php
4º – Modifiquei o arquivo Module.php especificando o namespace MinhaEmpresa:
namespace MinhaEmpresa;
use Zend\Module\Manager,
Zend\EventManager\StaticEventManager,
Zend\Module\Consumer\AutoloaderProvider;
class Module implements AutoloaderProvider
{
public function getAutoloaderConfig()
{
return array(
'Zend\Loader\ClassMapAutoloader' => array(
__DIR__ . '/autoload_classmap.php',
),
);
}
}
5º – Acrescentei a clase MeuPacote\Foo:
namespace MeuPacote;
class Foo
{
public function bar()
{
return get_class($this) . PHP_EOL;
}
}
6º – Gerei um o arquivo autoload_classmap.php com o Classmap Generator.
// Generated by ZF2's ./bin/classmap_generator.php
return array(
'MeuPacote\Foo' => __DIR__ . '/library/MeuPacote/Foo.php',
'MinhaEmpresa\Module' => __DIR__ . '/Module.php',
);
Sei que pode parecer confuso. Sugiro baixar o código-fonte disponibilizado no início deste artigo e alterá-lo um pouco, prestando atenção, principalmente, nos caminhos onde se encontram as libraries do ZF1 e ZF2.



Comentários Recentes