Documentação do Symfony2
Renderizada do repositório symfony-docs-pt-BR no Github

A Arquitetura

Você é meu herói! Quem imaginaria que você ainda estaria aqui após as três primeiras partes? Seus esforços serão bem recompensados ​​em breve. As três primeiras partes não contemplaram profundamente a arquitetura do framework. Porque ela faz o Symfony2 destacar-se na multidão de frameworks, vamos mergulhar na arquitetura agora.

Compreendendo a estrutura de diretório

A estrutura de diretório de uma aplicação do Symfony2 é bastante flexível, mas a estrutura do diretório da distribuição Standard Edition reflete a estrutura típica e recomendada de uma aplicação Symfony2:

  • app/: A configuração da aplicação;
  • src/: O código PHP do projeto;
  • vendor/: As dependências de terceiros;
  • web/: O diretório raiz web.

O Diretório web/

O diretório raiz web é o local de todos os arquivos públicos e estáticos, como imagens, folhas de estilo e arquivos JavaScript. É também o local onde cada front controller reside:

// web/app.php
require_once __DIR__.'/../app/bootstrap.php.cache';
require_once __DIR__.'/../app/AppKernel.php';

use Symfony\Component\HttpFoundation\Request;

$kernel = new AppKernel('prod', false);
$kernel->loadClassCache();
$kernel->handle(Request::createFromGlobals())->send();

O kernel primeiro solicita o arquivo bootstrap.php.cache, que inicializa a estrutura e regista o autoloader (veja abaixo).

Como qualquer front controller, o app.php usa uma classe Kernel, AppKernel, para a inicialização da aplicação.

O Diretório app/

A classe AppKernel é o principal ponto de entrada da configuração da aplicação e, como tal, ele é armazenado no diretório app/.

Essa classe deve implementar dois métodos:

  • registerBundles() que deve retornar um array de todos os bundles necessários para executar a aplicação.
  • registerContainerConfiguration() que carrega a configuração da aplicação (veremos mais sobre isso depois).

O autoloading do PHP pode ser configurado via app/autoload.php:

// app/autoload.php
use Symfony\Component\ClassLoader\UniversalClassLoader;

$loader = new UniversalClassLoader();
$loader->registerNamespaces(array(
    'Symfony'          => array(__DIR__.'/../vendor/symfony/symfony/src', __DIR__.'/../vendor/bundles'),
    'Sensio'           => __DIR__.'/../vendor/bundles',
    'JMS'              => __DIR__.'/../vendor/jms/',
    'Doctrine\\Common' => __DIR__.'/../vendor/doctrine/common/lib',
    'Doctrine\\DBAL'   => __DIR__.'/../vendor/doctrine/dbal/lib',
    'Doctrine'         => __DIR__.'/../vendor/doctrine/orm/lib',
    'Monolog'          => __DIR__.'/../vendor/monolog/monolog/src',
    'Assetic'          => __DIR__.'/../vendor/kriswallsmith/assetic/src',
    'Metadata'         => __DIR__.'/../vendor/jms/metadata/src',
));
$loader->registerPrefixes(array(
    'Twig_Extensions_' => __DIR__.'/../vendor/twig/extensions/lib',
    'Twig_'            => __DIR__.'/../vendor/twig/twig/lib',
));

// ...

$loader->registerNamespaceFallbacks(array(
    __DIR__.'/../src',
));
$loader->register();

O Symfony\Component\ClassLoader\UniversalClassLoader é usado para fazer o autoload dos arquivos que respeitam as normas técnicas de interoperabilidade para namespaces do PHP 5.3 ou a convenção de nomenclatura para classes do PEAR. Como você pode ver aqui, todas as dependências são armazenadas sob o diretório vendor/, mas isso é apenas uma convenção. Você pode armazená-las onde quiser, globalmente em seu servidor ou localmente em seus projetos.

Note

Se você quiser saber mais sobre a flexibilidade do autoloader do Symfony2, leia o capítulo “O Componente ClassLoader”.

Compreendendo o Sistema dos Bundles

Esta seção apresenta um dos maiores e mais poderosos recursos do Symfony2, o sistema de bundle.

Um bundle é como um plugin em outro software. Então por que ele é chamado de bundle de não de plugin? Porque tudo é um bundle no Symfony2, desde as funcionalidades do núcleo do framework até o código que você escreve para a sua aplicação. Os bundles são cidadãos de primeira classe no Symfony2. Isso lhe fornece a flexibilidade de usar funcionalidades pré-construídas que vêm em bundles de terceiros ou distribuir os seus próprios bundles. Isso torna mais fácil a tarefa de escolher quais recursos que serão habilitados na sua aplicação e otimizá-los da maneira que desejar. E, no final do dia, o código da sua aplicação é tão importante quanto o próprio framework.

Registrando um Bundle

Uma aplicação é composta de bundles, que foram definidos no método registerBundles() da classe AppKernel. Cada bundle é um diretório que contém uma única classe Bundle que descreve ele:

// app/AppKernel.php
public function registerBundles()
{
    $bundles = array(
        new Symfony\Bundle\FrameworkBundle\FrameworkBundle(),
        new Symfony\Bundle\SecurityBundle\SecurityBundle(),
        new Symfony\Bundle\TwigBundle\TwigBundle(),
        new Symfony\Bundle\MonologBundle\MonologBundle(),
        new Symfony\Bundle\SwiftmailerBundle\SwiftmailerBundle(),
        new Symfony\Bundle\DoctrineBundle\DoctrineBundle(),
        new Symfony\Bundle\AsseticBundle\AsseticBundle(),
        new Sensio\Bundle\FrameworkExtraBundle\SensioFrameworkExtraBundle(),
        new JMS\SecurityExtraBundle\JMSSecurityExtraBundle(),
    );

    if (in_array($this->getEnvironment(), array('dev', 'test'))) {
        $bundles[] = new Acme\DemoBundle\AcmeDemoBundle();
        $bundles[] = new Symfony\Bundle\WebProfilerBundle\WebProfilerBundle();
        $bundles[] = new Sensio\Bundle\DistributionBundle\SensioDistributionBundle();
        $bundles[] = new Sensio\Bundle\GeneratorBundle\SensioGeneratorBundle();
    }

    return $bundles;
}

Além do AcmeDemoBundle que nós já falamos, observe que o kernel também habilita outros bundles, como o FrameworkBundle, DoctrineBundle, SwiftmailerBundle e o AsseticBundle. Todos eles fazem parte do framework.

Configurando um Bundle

Cada bundle pode ser personalizado através dos arquivos de configuração escritos em YAML, XML ou PHP. Esta é a configuração padrão:

# app/config/config.yml
imports:
    - { resource: parameters.yml }
    - { resource: security.yml }

framework:
    #esi:             ~
    #translator:      { fallback: "%locale%" }
    secret:          "%secret%"
    router:          { resource: "%kernel.root_dir%/config/routing.yml" }
    form:            true
    csrf_protection: true
    validation:      { enable_annotations: true }
    templating:      { engines: ['twig'] } #assets_version: SomeVersionScheme
    default_locale:  "%locale%"
    session:
        auto_start:     true

# Twig Configuration
twig:
    debug:            "%kernel.debug%"
    strict_variables: "%kernel.debug%"

# Assetic Configuration
assetic:
    debug:          "%kernel.debug%"
    use_controller: false
    bundles:        [ ]
    # java: /usr/bin/java
    filters:
        cssrewrite: ~
        # closure:
        #     jar: "%kernel.root_dir%/java/compiler.jar"
        # yui_css:
        #     jar: "%kernel.root_dir%/java/yuicompressor-2.4.2.jar"

# Doctrine Configuration
doctrine:
    dbal:
        driver:   "%database_driver%"
        host:     "%database_host%"
        port:     "%database_port%"
        dbname:   "%database_name%"
        user:     "%database_user%"
        password: "%database_password%"
        charset:  UTF8

    orm:
        auto_generate_proxy_classes: "%kernel.debug%"
        auto_mapping: true

# Swiftmailer Configuration
swiftmailer:
    transport: "%mailer_transport%"
    host:      "%mailer_host%"
    username:  "%mailer_user%"
    password:  "%mailer_password%"

jms_security_extra:
    secure_controllers:  true
    secure_all_services: false

Cada entrada como framework define a configuração para um bundle específico. Por exemplo, framework configura o FrameworkBundle enquanto swiftmailer configura o SwiftmailerBundle.

Cada ambiente pode substituir a configuração padrão, ao fornecer um arquivo de configuração específico. Por exemplo, o ambiente dev carrega o arquivo config_dev.yml, que carrega a configuração principal (ou seja, config.yml) e, então, modifica ela para adicionar algumas ferramentas de depuração:

# app/config/config_dev.yml
imports:
    - { resource: config.yml }

framework:
    router:   { resource: "%kernel.root_dir%/config/routing_dev.yml" }
    profiler: { only_exceptions: false }

web_profiler:
    toolbar: true
    intercept_redirects: false

monolog:
    handlers:
        main:
            type:  stream
            path:  "%kernel.logs_dir%/%kernel.environment%.log"
            level: debug
        firephp:
            type:  firephp
            level: info

assetic:
    use_controller: true

Estendendo um Bundle

Além de ser uma boa forma de organizar e configurar seu código, um bundle pode estender um outro bundle. A herança do bundle permite substituir qualquer bundle existente a fim de personalizar seus controladores, templates ou qualquer um de seus arquivos. Aqui é o onde os nomes lógicos (por exemplo, @AcmeDemoBundle/Controller/SecuredController.php) são úteis: eles abstraem onde o recurso é realmente armazenado.

Nomes Lógicos de Arquivos

Quando você quer fazer referência à um arquivo de um bundle, use esta notação: @BUNDLE_NAME/path/to/file; o Symfony2 irá resolver @BUNDLE_NAME para o caminho real do bundle. Por exemplo, o caminho lógico @AcmeDemoBundle/Controller/DemoController.php seria convertido para src/Acme/DemoBundle/Controller/DemoController.php, pois o Symfony conhece a localização do AcmeDemoBundle.

Nomes Lógicos de Controladores

Para os controladores, você precisa referenciar os nomes de métodos usando o formato BUNDLE_NAME:CONTROLLER_NAME:ACTION_NAME. Por exemplo, AcmeDemoBundle:Welcome:index mapeia para o método indexAction da classe Acme\DemoBundle\Controller\WelcomeController.

Nomes Lógicos de Templates

Para os templates, o nome lógico AcmeDemoBundle:Welcome:index.html.twig é convertido para o caminho de arquivo src/Acme/DemoBundle/Resources/views/Welcome/index.html.twig. Os templates tornam-se ainda mais interessantes quando você percebe que eles não precisam ser armazenados no sistema de arquivos. Você pode facilmente armazená-los em uma tabela do banco de dados, por exemplo.

Estendendo Bundles

Se você seguir estas convenções, então você pode usar bundle inheritance para “sobrescrever” os arquivos, controladores ou templates. Por exemplo, você pode criar um bundle - AcmeNewBundle - e especificar que ele sobrescreve o AcmeDemoBundle. Quando o Symfony carregar o controlador AcmeDemoBundle:Welcome:index, ele irá primeiro verificar a classe WelcomeController em AcmeNewBundle e, se ela não existir, então irá verificar o AcmeDemoBundle. Isto significa que um bundle pode sobrescrever quase qualquer parte de outro bundle!

Você entende agora porque o Symfony2 é tão flexível? Compartilhe os seus bundles entre aplicações, armazene-os localmente ou globalmente, a escolha é sua.

Usando os Vendors

São grandes as probabilidades de que a sua aplicação dependerá de bibliotecas de terceiros. Estas devem ser armazenadas no diretório vendor/. Este diretório já contém as bibliotecas do Symfony2, a biblioteca do SwiftMailer, o ORM Doctrine, o sistema de template Twig e algumas outras bibliotecas e bundles de terceiros.

Entendendo o Cache e Logs

O Symfony2 é provavelmente um dos mais rápidos frameworks full-stack atualmente. Mas como pode ser tão rápido se ele analisa e interpreta dezenas de arquivos YAML e XML para cada pedido? A velocidade é, em parte, devido ao seu sistema de cache. A configuração da aplicação é analisada somente no primeiro pedido e depois compilada em código PHP comum, que é armazenado no diretório app/cache/. No ambiente de desenvolvimento, o Symfony2 é inteligente o suficiente para liberar o cache quando você altera um arquivo. Mas, no ambiente de produção, é sua a responsabilidade de limpar o cache quando você atualizar o seu código ou alterar sua configuração.

Ao desenvolver uma aplicação web, as coisas podem dar errado em muitos aspectos. Os arquivos de log no diretório app/logs/ dizem tudo sobre os pedidos e ajudam a resolver os problemas rapidamente.

Utilizando a Interface da Linha de Comando

Cada aplicação vem com uma ferramenta de interface de linha de comando (app/console) que ajuda na manutenção da sua aplicação. Ela fornece comandos que aumentam a sua produtividade ao automatizar tarefas tediosas e repetitivas.

Execute-a sem argumentos para saber mais sobre suas capacidades:

$ php app/console

A opção --help ajuda a descobrir o uso de um comando:

$ php app/console router:debug --help

Considerações finais

Me chame de louco, mas, depois de ler esta parte, você deve estar confortável em mover as coisas e fazer o Symfony2 trabalhar para você. Tudo no Symfony2 é projetado para sair do seu caminho. Portanto, sinta-se livre para renomear e mover os diretórios como você desejar.

E isso é tudo para o início rápido. Desde testes até o envio de e-mails, você ainda precisa aprender muito para se tornar um mestre no Symfony2. Pronto para aprofundar nestes tópicos agora? Não procure mais - vá para o Livro oficial e escolha qualquer tema que você desejar.