Plugins


1Presentation

It is possible to request the execution of plugins, before and/or after the execution of the application controllers. Technically, plugins are controllers with special methods, which can modify the request before passing it on to the next plugin or to the designated controller. You can chain plugins before the controller, and other plugins after.

The source files for plugins are stored in the controllers directory of the application.

The same object can have both plugin and controller roles.

Plugins provided by Temma

Temma provides several plugins, which documentation is available in the "Helpers" section:

  • Api: To manage API access
  • Cache: To cache the generated HTML pages
  • Language: Multilingual localization management

2Configuration

The configuration of pre-plugins and post-plugins is done using the etc/temma.php file (see the configuration documentation). It is possible to configure global plugins, which will be executed for all requests:

[
    'plugins => [
        // list of pre-plugins
        '_pre' => [
            'AuthenticationPlugin',
            'CmsController',
        ],
        // list of post-plugins
        '_post' => [
            'CleanSessionPlugin'
        ]
    ]
]

Plugins are executed following the order in which they are declared.

It is possible to configure plugins that will only run for certain controllers:

[
    'plugins' => [
        // plugins systematically executed for all controllers
        '_pre'  => [ 'AuthenticationPlugin', 'CmsController' ],
        '_post' => [ 'CleanSessionPlugin' ],

        // plugins called only for the homepage controller
        'Homepage' => [
            // plugins executed before the controller
            '_pre'  => [ 'SomePlugin', 'AnotherPlugin' ],
            // plugins executed after the controller
            '_post' => [ 'LastPlugin' ]
        ],

        // plugins called only for the display controller
        'Display' => [
            '_post' => [ 'CheckOutputPlugin' ]
        ]
    ]
]

It is even possible to define plugins that will only be called for certain actions:

[
    'plugins' => [
        // plugins called only for the homepage controller
        'Homepage' => [
            // plugins executed before the controller
            '_pre'  => [ 'SomePlugin', 'AnotherPlugin' ],
            // plugins executed after the controller
            '_post' => [ 'LastPlugin' ],

            // plugins for the show action
            'show' => [
                // plugins executed before the action
                '_pre'  => [ 'SpecialPlugin' ],
                // plugins executed after the action
                '_post' => [ 'FooPlugin' ]
            ],

            // plugins for the remove action
            'remove' => [
                // plugins executed before the action
                '_pre' => [ 'BarPlugin' ]
            ]
        ]
    ]
]

It is also possible to define plugins that will be called for all controllers except one. In this case, simply place an hyphen ("-") in front of the controller name:

[
    'plugins' => [
        // plugins called for all controllers except the authentication controller
        '-Auth' => [
            // plugins always executed before the controller
            '_pre'  => [ 'AuthPlugin' ],
            // plugins always executed after the controller
            '_post' => [ 'LastPlugin' ]
        ]
    ]
]

Finally, you can define plugins that will be called for all actions except one. Here again, simply place a hyphen ("-") in front of the action name:

[
    'plugins' => [
        // plugins called only for the homepage controller
        'Homepage' => [
            // plugins for the show action
            'show' => [
                // plugins executed before the action
                '_pre' => [ 'SpecialPlugin' ],
            ],

            // plugins for all actions except the remove action
            '-remove' => [
                // plugins executed before the action
                '_pre' => [ 'BarPlugin' ]
            ]
        ],

        // plugins called for all controllers except the authentication controller
        '-Auth' => [
            // plugins executed before any action
            '_pre'   => [ 'FooPlugin' ],
            // plugin executed before any action, except the login action
            '-login' => [ 'AnotherPlugin' ]
        ]
    ]
]

3Write your own plugins

3.1Principles

Plugins make it possible to modularize the development of web applications. They make it possible in particular to factorize processing which must be done systematically at the start or at the end of all accesses.

This can be, for example, a plugin which checks that certain URLs are only accessible to authenticated users, and which will be executed before the processes reach the controllers; if the user is not authenticated, the plugin will redirect him to the authentication page, and the controller will not even be executed.
This can be a plugin which is executed at the end of the processing, to set template variables which are necessary for all the pages.
The possibilities are limitless…

Plugins can even manipulate framework data, which gives them the ability to change the controller or the action that will be executed, as well as the parameters that will be passed to it.


3.2Development

Here is an example of a plugin:

class CheckPlugin extends \Temma\Web\Plugin {
    public function plugin() {
        // we set a template variable
        $this['checked'] = true;
    }
}

As can be seen in this example, plugins inherit from the \Temma\Web\Plugin parent class, which itself derives from the \Temma\Web\Controller class. Plugins are therefore controllers with just an additional capacity.
A plugin can therefore also be used as a controller, and a controller can also be used as a plugin.

In this example, we have defined a plugin() method, which will be executed when the plugin is called. It is therefore the configuration that will determine whether the object is a pre-plugin or a post-plugin.

To transmit data between plugins, or between plugins and the controller (and vice versa), template variables must be used. If a plugin sets a template variable, it will be accessible by all the plugins/controllers that follow in the execution chain.

So that the same object (whether it is only a plugin, or controller+plugin) can be both a pre-plugin and a post-plugin, by executing different code depending on the moment of its execution, we can define preplugin() and postplugin() methods.

  • When a plugin is executed in the PRE action chain, Temma first looks to see if it has a method named preplugin(). If so, it is executed; otherwise, we execute the plugin() method.
  • When a plugin is executed in the POST chain of actions, Temma first looks to see if it has a method named postplugin(). If so, it is executed; otherwise we execute the plugin () method.

Here is an example of a controller that is able to act as a pre-plugin and as a post-plugin, in addition to having several actions:

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

    // URL verification pre-plugin
    public function preplugin() {
        if (!$this->_checkUrl()) {
            return $this->httpError(404);
        }
        $this['checked'] = true;
    }

    // post-plugin for reprocessing variables before sending to the template
    public function postplugin() {
        $dirtyData = $this['dirtyData'];
        $cleanData = htmlspecialchars($dirtyData);
        $this['cleanData'] = $cleanData;
    }

    // root action
    public function index() {
        $this['pages'] = $this->_dao->search();
    }

    // additional action
    public function show($id) {
        $this['page'] = $this->_dao->get($id);
    }

    // private method
    private function _checkUrl() {
        // ...
        return (true);
    }
}

3.3Handling plugins and controller

The plugins and the controller can dynamically modify the information that the framework uses to determine which plugins/controller to run.

Modifying plugins

In a plugin method or a controller method (initialization, action or finalization), it is possible to retrieve the complete list of plugins, and to update it.

This is done through the $this->_loader->config object, which offers the plugins attribute, readable and writable. This attribute directly contains the array of plugins as defined in the etc/temma.php file (see the configuration documentation).

If you change the plugins configuration, you may need to return an EXEC_RESTART or EXEC_REBOOT status, to force Temma to rerun the plugins chain.

Here is an example of a pre-plugin that reverses the order of the post-plugins.

class ReverseExamplePlugin extends \Temma\Web\Plugin {
    public function preplugin() {
        // retrieve the list of plugins
        $plugins = $this->_loader->config->plugins;

        // inversion of post-plugins
        $plugins['_post'] = array_reverse($plugins['_post']);

        // update the list of plugins
        $this->_loader->config->plugins = $plugins;
    }
}
Controller modification

In a plugin method or a controller method (initialization, action or finalization), it is possible to access the following information, and to modify it:

  • The name of the controller to run.
  • The name of the action to perform.
  • The parameters supplied to the action.

All this information can be accessed by the $this->_loader->request object, which offers the following methods:

  • getController(): Returns the name of the controller that will be executed. Identical to the $this['CONTROLLER'] template variable.
  • getAction(): Returns the name of the action that will be executed. Identical to the $this['ACTION'] template variable.
  • getNbrParams(): Returns the number of parameters.
  • getParams(): Returns the list of parameters.
  • getParam(int $index, [$default]): Returns the parameter whose index is given as a parameter. A default value can be passed as the second parameter; it will be returned if the requested parameter is not defined.
  • setController(string $name): Sets the name of the controller to run.
  • setAction(string $name): Defines the name of the action to run.
  • setParams(array $params): Defines the list of parameters.
  • setParam(int $index, string $value): Sets the value of a parameter whose index is provided.

Warning: If you modify the controller and/or the action with the setController() and setAction() methods, remember to update the corresponding template variables $this['CONTROLLER'] and $this['ACTION'], as well as $this['URL'].


3.4Plugin example

Here is an example of a simplistic pre-plugin that extracts the language at the beginning of the URL.
For example, for a URL /en/article/show/123/my-article, the plugin will put "en" in the template variable $this['lang'], then do what it takes for Temma to do as if the received URL had been /article/show/123/my-article.

class LangExamplePlugin extends \Temma\Web\Plugin {
    public function preplugin() {
        // retrieve the language
        $lang = $this['CONTROLLER'];
        // retrieve the controller
        $newController = $this['ACTION'];
        // retrieve the parameters
        $params = $this->_loader->request->getParams();
        // extract the action and shift the parameters
        $newAction = array_shift($params);

        // update the data in Temma
        $this->_loader->request->setController($newController);
        $this->_loader->request->setAction($newAction);
        $this->_loader->request->setParams($params);
        // update the template variables
        $this['lang'] = $lang;
        $this['CONTROLLER'] = $newController;
        $this['ACTION'] = $newAction;
        $this['URL'] = substr($this['URL'], strlen($lang));
    }
}