Plugins


1Apresentação

É possível solicitar a execução de plugins, antes e/ou depois da execução dos controladores da aplicação. Tecnicamente, os plugins são controladores com métodos especiais, que podem modificar a requisição antes de repassá-la ao próximo plugin ou ao controlador designado. Você pode encadear plugins antes do controlador, e outros plugins depois.

Os arquivos-fonte dos plugins são armazenados no diretório controllers da aplicação.

O mesmo objeto pode ter tanto o papel de plugin quanto o de controlador.

Plugins fornecidos pelo Temma

O Temma fornece vários plugins, cuja documentação está disponível na seção "Helpers":

  • Api: Para gerenciar o acesso à API
  • Cache: Para armazenar em cache as páginas HTML geradas
  • Debug: Para depurar facilmente projetos Temma
  • Language: Gerenciamento de localização multilíngue
  • KebabCaseUrl: Tratamento de URLs no formato “kebab-case”

2Configuração

A configuração dos pré-plugins e pós-plugins é feita usando o arquivo etc/temma.php (veja a documentação de configuração). É possível configurar plugins globais, que serão executados para todas as requisições:

<?php

return [
    'plugins' => [
        // lista de pré-plugins
        '_pre' => [
            'AuthenticationPlugin',
            'CmsController',
        ],
        // lista de pós-plugins
        '_post' => [
            'CleanSessionPlugin'
        ]
    ]
];

Os plugins são executados seguindo a ordem em que são declarados.

É possível configurar plugins que só serão executados para determinados controladores:

<?php

return [
    'plugins' => [
        // plugins executados sistematicamente para todos os controladores
        '_pre'  => [ 'AuthenticationPlugin', 'CmsController' ],
        '_post' => [ 'CleanSessionPlugin' ],

        // plugins chamados apenas para o controlador homepage
        'Homepage' => [
            // plugins executados antes do controlador
            '_pre'  => [ 'SomePlugin', 'AnotherPlugin' ],
            // plugins executados depois do controlador
            '_post' => [ 'LastPlugin' ]
        ],

        // plugins chamados apenas para o controlador display
        'Display' => [
            '_post' => [ 'CheckOutputPlugin' ]
        ]
    ]
];

É até possível definir plugins que só serão chamados para determinadas ações:

<?php

return [
    'plugins' => [
        // plugins chamados apenas para o controlador homepage
        'Homepage' => [
            // plugins executados antes do controlador
            '_pre'  => [ 'SomePlugin', 'AnotherPlugin' ],
            // plugins executados depois do controlador
            '_post' => [ 'LastPlugin' ],

            // plugins para a ação show
            'show' => [
                // plugins executados antes da ação
                '_pre'  => [ 'SpecialPlugin' ],
                // plugins executados depois da ação
                '_post' => [ 'FooPlugin' ]
            ],

            // plugins para a ação remove
            'remove' => [
                // plugins executados antes da ação
                '_pre' => [ 'BarPlugin' ]
            ]
        ]
    ]
];

Também é possível definir plugins que serão chamados para todos os controladores, exceto um. Nesse caso, basta colocar um hífen ("-") na frente do nome do controlador:

<?php

return [
    'plugins' => [
        // plugins chamados para todos os controladores, exceto o controlador de autenticação
        '-Auth' => [
            // plugins sempre executados antes do controlador
            '_pre'  => [ 'AuthPlugin' ],
            // plugins sempre executados depois do controlador
            '_post' => [ 'LastPlugin' ]
        ]
    ]
];

Por fim, você pode definir plugins que serão chamados para todas as ações, exceto uma. Aqui também, basta colocar um hífen ("-") na frente do nome da ação:

<?php

return [
    'plugins' => [
        // plugins chamados apenas para o controlador homepage
        'Homepage' => [
            // plugins para a ação show
            'show' => [
                // plugins executados antes da ação
                '_pre' => [ 'SpecialPlugin' ],
            ],

            // plugins para todas as ações, exceto a ação remove
            '-remove' => [
                // plugins executados antes da ação
                '_pre' => [ 'BarPlugin' ]
            ]
        ],

        // plugins chamados para todos os controladores, exceto o controlador de autenticação
        '-Auth' => [
            // plugins executados antes de qualquer ação
            '_pre'   => [ 'FooPlugin' ],
            // plugin executado antes de qualquer ação, exceto a ação login
            '-login' => [ 'AnotherPlugin' ]
        ]
    ]
];

3Escreva seus próprios plugins

3.1Princípios

Os plugins possibilitam modularizar o desenvolvimento de aplicações web. Eles permitem, em particular, fatorar processamentos que devem ser feitos sistematicamente no início ou no fim de todos os acessos.

Pode ser, por exemplo, um plugin que verifica se determinadas URLs só são acessíveis a usuários autenticados, e que será executado antes que os processos cheguem aos controladores; se o usuário não estiver autenticado, o plugin o redirecionará para a página de autenticação, e o controlador nem sequer será executado.
Pode ser um plugin executado no fim do processamento, para definir variáveis de template que são necessárias para todas as páginas.
As possibilidades são infinitas…

Os plugins podem até manipular dados do framework, o que lhes dá a capacidade de alterar o controlador ou a ação que será executada, bem como os parâmetros que serão passados a ela.


3.2Desenvolvimento

Aqui está um exemplo de plugin:

class CheckPlugin extends \Temma\Web\Plugin {
    public function plugin() {
        // definimos uma variável de template
        $this['checked'] = true;
    }
}

Como se pode ver neste exemplo, os plugins herdam da classe pai \Temma\Web\Plugin, que por sua vez deriva da classe \Temma\Web\Controller. Os plugins são, portanto, controladores com apenas uma capacidade adicional.
Um plugin pode, portanto, também ser usado como controlador, e um controlador também pode ser usado como plugin.

Neste exemplo, definimos um método plugin(), que será executado quando o plugin for chamado. É, portanto, a configuração que determinará se o objeto é um pré-plugin ou um pós-plugin.

Para transmitir dados entre plugins, ou entre plugins e o controlador (e vice-versa), é preciso usar variáveis de template. Se um plugin define uma variável de template, ela ficará acessível a todos os plugins/controladores seguintes na cadeia de execução.

Para que o mesmo objeto (seja apenas um plugin, ou controlador+plugin) possa ser tanto um pré-plugin quanto um pós-plugin, executando código diferente dependendo do momento de sua execução, podemos definir métodos preplugin() e postplugin().

  • Quando um plugin é executado na cadeia de ações PRE, o Temma primeiro verifica se ele possui um método chamado preplugin(). Se sim, ele é executado; caso contrário, executamos o método plugin().
  • Quando um plugin é executado na cadeia POST de ações, o Temma primeiro verifica se ele possui um método chamado postplugin(). Se sim, ele é executado; caso contrário, executamos o método plugin().

Aqui está um exemplo de controlador capaz de atuar como pré-plugin e como pós-plugin, além de ter várias ações:

class Page extends \Temma\Web\Plugin {
    protected $_temmaAutoDao = true;

    // pré-plugin de verificação de URL
    public function preplugin() {
        if (!$this->_checkUrl()) {
            return $this->_httpError(404);
        }
        $this['checked'] = true;
    }

    // pós-plugin para reprocessar variáveis antes de enviar ao template
    public function postplugin() {
        $dirtyData = $this['dirtyData'];
        $cleanData = htmlspecialchars($dirtyData);
        $this['cleanData'] = $cleanData;
    }

    // ação raiz
    public function index() {
        $this['pages'] = $this->_dao->search();
    }

    // ação adicional
    public function show($id) {
        $this['page'] = $this->_dao->get($id);
    }

    // método privado
    private function _checkUrl() {
        // ...
        return (true);
    }
}

3.3Manipulação de plugins e controlador

Os plugins e o controlador podem modificar dinamicamente as informações que o framework usa para determinar quais plugins/controlador executar.

Modificação de plugins

Em um método de plugin ou de controlador (inicialização, ação ou finalização), é possível recuperar a lista completa de plugins, e atualizá-la.

Isso é feito por meio do objeto $this->_loader->config, que oferece o atributo plugins, legível e gravável. Esse atributo contém diretamente o array de plugins conforme definido no arquivo etc/temma.php (veja a documentação de configuração).

Se você alterar a configuração dos plugins, pode ser necessário retornar um status EXEC_RESTART ou EXEC_REBOOT, para forçar o Temma a executar novamente a cadeia de plugins.

Aqui está um exemplo de pré-plugin que inverte a ordem dos pós-plugins.

class ReverseExamplePlugin extends \Temma\Web\Plugin {
    public function preplugin() {
        // recupera a lista de plugins
        $plugins = $this->_loader->config->plugins;

        // inversão dos pós-plugins
        $plugins['_post'] = array_reverse($plugins['_post']);

        // atualiza a lista de plugins
        $this->_loader->config->plugins = $plugins;
    }
}
Modificação do controlador

Em um método de plugin ou de controlador (inicialização, ação ou finalização), é possível acessar as seguintes informações, e modificá-las:

  • O nome do controlador a ser executado.
  • O nome da ação a ser executada.
  • Os parâmetros fornecidos à ação.

Todas essas informações podem ser acessadas pelo objeto $this->_loader->request, que oferece os seguintes métodos:

  • getController(): Retorna o nome do controlador que será executado. Idêntico à variável de template $this['CONTROLLER'].
  • getAction(): Retorna o nome da ação que será executada. Idêntico à variável de template $this['ACTION'].
  • getParamCount(): Retorna o número de parâmetros.
  • getParams(): Retorna a lista de parâmetros.
  • getParam(int $index, [$default]): Retorna o parâmetro cujo índice é fornecido como parâmetro. Um valor padrão pode ser passado como segundo parâmetro; ele será retornado se o parâmetro solicitado não estiver definido.
  • setController(string $name): Define o nome do controlador a ser executado.
  • setAction(string $name): Define o nome da ação a ser executada.
  • setParams(array $params): Define a lista de parâmetros.
  • setParam(int $index, string $value): Define o valor de um parâmetro cujo índice é fornecido.

Atenção: Se você modificar o controlador e/ou a ação com os métodos setController() e setAction(), lembre-se de atualizar as variáveis de template correspondentes $this['CONTROLLER'] e $this['ACTION'], bem como $this['URL'].


3.4Exemplo de plugin

Aqui está um exemplo de um pré-plugin simplista que extrai o idioma no início da URL.
Por exemplo, para uma URL /en/article/show/123/my-article, o plugin colocará "en" na variável de template $this['lang'], e então fará o necessário para que o Temma se comporte como se a URL recebida tivesse sido /article/show/123/my-article.

class LangExamplePlugin extends \Temma\Web\Plugin {
    public function preplugin() {
        // recupera o idioma
        $lang = $this['CONTROLLER'];
        // recupera o controlador
        $newController = $this['ACTION'];
        // recupera os parâmetros
        $params = $this->_loader->request->getParams();
        // extrai a ação e desloca os parâmetros
        $newAction = array_shift($params);

        // atualiza os dados no Temma
        $this->_loader->request->setController($newController);
        $this->_loader->request->setAction($newAction);
        $this->_loader->request->setParams($params);
        // atualiza as variáveis de template
        $this['lang'] = $lang;
        $this['CONTROLLER'] = $newController;
        $this['ACTION'] = $newAction;
        $this['URL'] = mb_substr($this['URL'], mb_strlen($lang) + 1);
    }
}