Filter

1. Introduction

One main concept of the Adventure PHP Framework is the separation between url or url layout respectively and the functionality of the software in common. You can find many framework products that have a strong binding between the url and software components that leads to adaption of the software components in case the url changes. Further, direct adressing leads to less granularity concerning your software components or modules. This makes reusability a hard job to do.

A further aspect is the HMVC implementation of the APF (see Page controller) that enables you to build up your application or website out of any number of parts that are also runnable in standalone mode. Directly adressing the MVC document controllers (see (Document-)Controller) used within your application would be much overhead on the one hand and significently decreases readability and massively disturbes SEO optimization efforts.

For this reason, the APF uses decouples software components from the url based on the input and output filters. Input filters are designed to transform the incoming request to a generic, internal representation that is understood by any software component. After application execution Output filters then transform the internal representation into the outside form to let the user not know about the internals.

Beside the classic application case URL-Layout there are further possibilities to use input and output filters. One example is shrinking of HTML code, injection of content after the transformation phase, and securing user input.

2. Architecture

The following sequence diagram presents the timing model of the input and output filters:

Filter timing modell

Input and output filters are executed within a filter chain according to the order they are added. Within the FilterChain each filter can decide whether the next filter is executed and which input the next filter gets. All shipped default filters do not interrupt the chain.

The chains for input and output filtering are singleton objects (valid within one request) and can be accessed from all APF conponents. Normally, configuration is done within the bootstrap file (index.php).

The timing modell of the front controller describes that the FilterChain including the input filters us executed by the front controller at the beginning of the request processing. This ensures, that all information sent with the request has been transformed to an internal representation before the front controller starts to handle the request.

Moreover, the timing model describes that after front controller execution the generated HTML output is passed to the FilterChain including the output filters. This ensures that the content can be adapted to the outside representation.

2.1. FilterChain

Due to configuration reasons and to express the two different intents of the FilterChain for input as well as for output filters, two separate implementations are shipped with the APF: the InputFilterChain and OutputFilterChain.

Both classes are derived from the generic AbstractFilterChain that implements the FilterChain interface:

PHP code
interface FilterChain { public function filter($input); public function appendFilter(ChainedContentFilter $filter); public function prependFilter(ChainedContentFilter $filter); public function removeFilter($class); } abstract class AbstractFilterChain implements FilterChain { ... public function filter($input) { ... } public function &appendFilter(ChainedContentFilter $filter) { ... } public function &prependFilter(ChainedContentFilter $filter) { ... } public function &removeFilter($class) { ... } public function &reset() { ... } public function &clear() { ... } }

2.2. Filter

As you might have taken from the code stub of the previous chapter the filter chains are able to process filters that comply with the ChainedContentFilter interface. In contrast to the filter implementation before 1.14 the are passed the content to filter as well as the FilterChain for further proceeding:

PHP code
interface ChainedContentFilter { function filter(FilterChain &$chain, $input = null); }

Thus, each input and output filter is able to proceed with the chain or to interrupt it returning the filtered content. Beyond, each filter can inject or remove filters from the chain as desired.

One potential application case of adding filters within the chain is configuring filters according to the result of a previous one. For example, a "RouterFilter" may add a special filter concerning the desired url layout (e.g. /shop -> ShopUrlLayoutInputFilter, /page -> PagesUrlLayoutInputFilter).

Please note, that filters that have already been executed within the chain cannot be removed during execution of the FilterChain. Otherwise, the chain would become inconsistent.

The implementation of a filter can be derived from any base class (e.g. the functionality from APFObject) but must be compliant with the interface above.

The next code block contains a filter implementation that removes all unnecessary spaces and line breaks from the HTML code:

PHP code
class WhitespaceOutputFilter implements ChainedContentFilter { public function filter(FilterChain &$chain, $input = null) { // remove newline and blank characters $input = preg_replace('/\r|\n|\t|\s{3,}/', '',$input); // execute further filters on the chain return $chain->filter($input); } }

Starting with 1.15 you can add filters before and during execution of the FilterChain. For this purpose you can use appendFilter() to add a filter at the end of the chain and prependFilter() to add a filter to the start.

Please note that filters prepended to the chain during execution may not be executed.

3. Configuration

3.1. Default setup

Installing the APF from a fresh release package, the filer chains are already pre-configured with the default filter by the front controller. These are the ChainedGenericInputFilter as an input filter to resolve the url layout described in chapter 4 and the ChainedGenericOutputFilter as an output filter to rewrite urls to their external representation in cass url rewriting is activated.

3.2. Adapted setup

As already mentioned above, the configuration of the chains is normally done within the bootstrap file (index.php) but can be adapted within all software components on-demand.

Setting up the FilterChain is easy to do after including the page controller and front controller:

PHP code
include('./apps/core/pagecontroller/pagecontroller.php'); import('core::frontcontroller', 'Frontcontroller'); // add an additional custom input filter import('my::project::filters', 'AdditionalInputFilter'); InputFilterChain::getInstance()->appendFilter(new AdditionalInputFilter()); // add an additional custom output filter import('my::project::filters', 'AdditionalOutputFilter'); OutputFilterChain::getInstance()->appendFilter(new AdditionalOutputFilter()); // clear chain and add a custom input filter import('my::project::filters', 'CustomInputFilter'); InputFilterChain::getInstance()->clear()->appendFilter(new CustomInputFilter()); // clear chain and add a custom output filter import('my::project::filters', 'CustomOutputFilter'); OutputFilterChain::getInstance()->clear()->appendFilter(new CustomOutputFilter());

Due to the fact, that the chain is able to execute any number of filters each combination of the above samples can be used to create your project setup. In order to configure more custom input filters and one more custom output filter the following code is sufficient:

PHP code
include('./apps/core/pagecontroller/pagecontroller.php'); import('core::frontcontroller', 'Frontcontroller'); // cleas chain and add custom input filters import('my::project::filters', 'AdditionalInputFilterOne'); import('my::project::filters', 'AdditionalInputFilterTwo'); InputFilterChain::getInstance() ->clear() ->appendFilter(new AdditionalInputFilterOne()) ->appendFilter(new AdditionalInputFilterTwo()); // add another output filter import('my::project::filters', 'AdditionalOutputFilter'); OutputFilterChain::getInstance()->appendFilter(new AdditionalOutputFilter());

If you intend to change the order within the chain, please clear and re-setup the chain.

3.3. Interrupting the chain

As noted in the previous chapter the execution of the chain can be influenced by each filter. In case a filter implementation directly returns the output, the filter chain execution is interrupted. This means that filters that are registered to be invoked after this particular filter will not be executed:

PHP code
class IgnorantOutputFilter implements ChainedContentFilter { public function filter(FilterChain &$chain, $input = null) { return $input; } }

3.4. Deactivating filters

In order to deactivate the default filters completely, just add the following to your bootstrap file:

PHP code
include('./apps/core/pagecontroller/pagecontroller.php'); import('core::frontcontroller', 'Frontcontroller'); InputFilterChain::getInstance()->clear(); OutputFilterChain::getInstance()->clear();

3.5. Removing filters

The implementation of the APF filter chain provides the facility to remove single filters by their class name:

PHP code
include('./apps/core/pagecontroller/pagecontroller.php'); import('core::frontcontroller', 'Frontcontroller'); // remove the default output filter OutputFilterChain::getInstance()->removeFilter('ChainedGenericOutputFilter');

4. Url layout

The classic approach of input and output filters within the APF is resolving different url layouts and processing the information (e.g. front controller actions). Custom filters can be added during chain execution or replaced by other filters to control the input and output.

The url layout of the APF handles two cases: normal urls and so-called rewrite urls that are url paths that represent normal urls. Within the next chapters these two alternatives are discussed in detail:

4.1. Normal urls

Normal urls are urls that address one or more bootstrap files and contain dynamic parameters. Example:

Code
http://www.example.com/index.php?page=news&news-page=2

The request parameters are already interpreted by the webserver and passed to the PHP engine. Thus, no processing is needed for this case.

Nevertheless, normal urls can contain front controller action instructions that are encoded in a special way:

Code
http://www.example.com/index.php?modules_captcha_biz-action:showCaptcha=name:0123456789|mode:transparent

The above url addresses an action within the namespace modules::captcha::biz having the name showCaptcha. The action is passed the parameters name and mode. An action definition like this can be present several times within the url and can have any number of parameters. In addition, "normal" parameters can be mixed with front controller action instructions as desired.

The scheme of action url representations can be read about in the URL-Layout chapter of the front controller documentation.

In case of normal urls the duty of the input filter is to analyze the front controller instruction (namespace, action name, parameter) and to add the action to the front controller's action stack.

4.2. Rewrite urls

The difference between rewrite urls and normal urls is the separation of the parameters. Couples of parameters are not separated by "&" but by "/" and parameter names from their values by "/", too. In order to resolve this scheme a separator must be defined to distinguish between normal parameters and action instructions. Further, a separator between different action instructions must be defined to be able to add any number of action instructions to the url.

For this reason, the "/~/" separator has been introduced. This sign separates normal parameter strings from front controller actions that are treated differently. Simple parameter-value-couples are resolved to associative assignments and are added to the $_REQUEST array. Actions are resolved according to their url layout and passed to the front controller as described for "normal" urls.

The following code box presents the rewritten url for the first sample within the last chapter (simple parameters only):

Code
http://www.example.com/page/news/news-page/2

Having a look at the front controller action sample, the rewritten url is like this:

Code
http://www.example.com/~/modules_captcha_biz-action/showCaptcha/name/0123456789/mode/transparent

The necessary adaption to operate an application in rewrite url mode can be taken from the URL rewriting page.

Further notes on implementing custom url layouts can be found in the wiki article Implementierung von eigenen URL-Layouts (German).

4.3. Output formatting

A further part of the filters delivered with the APF is the output filter. This component is responsible to rewrite "normal" urls to slash-urls. The filter is executed after the page is transformed and all front controller actions are executed to adapt the generated content.

To support the output filter's tasks or to avoid url re-formatting you may implement and configure your own link scheme. Doing so, the urls will be formatted correctly during transformation time.

Comments

Do you want to add a comment to the article above, or do you want to post additional hints? So please click here. Comments already posted can be found below.
There are no comments belonging to this article.