Important Annotation:
Don't jump to conclusions. PHPinChains is not "meta-data hell". You can use pre-configured configuration files shipping with the framework to write complete application without configuring anything other than then the database. For big applications a small amount of configuration will be necessary. PHPinChains lets you decide how many configuration has to be done and how many things just follow conventions. But it lets you the power to configure your project in many ways if you want to.
If you just want to use a preconfigured filter chain go here and skip this page.
PHPinChains provides an implementation of the Intercepting Filter Pattern (Core J2EE Patterns, Microsoft). All services provided with the framework are filters or part of a filter. The filters are organized in a chain which is configured via an XML file. The main chain can be divided into sub chains.
Each filter has a match which is simply a regular expression. If the regular expression matches, than the filter is invoked by the chain. The filter itself invokes the chain after pre-processing while the chain can invoke the next filter and so on. Each filter can stop the chain by not giving the control back to the chain (call the doFilter method of the provided filter chain reference). After the last filter has done its processing, the control falls back to the last filter which was invoked, where it can do some post-processing. The control falls back until the first invoked filter has finished its post-processing.
Implementing a filter:
To implement a filter create a new file with the name of the new filter e.g. MyNewFilter.php. Write an new class MyNewFilter which must extend Filter (phpinchains/Filter.php; you do not need to include Filter.php). Now overrride the two methods init (& $config) (& not needed in php5) and doFilter (& $env, & $chain).
In init you can do some initialization, in $config the parameters are provided which can be specified in the XML file to configure a filter.
In the doFilter method the processing code of the filter is located. Place here the code for pre- and post-processing and between the two code parts the call of the filter chain: $chain->doFilter($env);.
function doFilter (& $env, & $chain)
{
// pre-processing code
$chain->doFilter($env);
// post-processing code
}
After implementing your filter, place it in the filterpath you can specify in the XML configuration file or place it in phpinchains/filters. The $env variable contains a reference to something like a context object. It contains methods for accessing the request params (param($name): access post and get parameters, getGet($name): access only get parameters, getPost($name): access only post parameters, getFile($name): access uploaded files) as well as methods to set and get attributes which can be accessed in the following filters or filter parts (setAttribute($name, $value), getAttribute($name), setZombie($name, $value), getZombie($name)). We will discuss the special behavior of the attributes in the part about the ActionDispatcherFilter as well as we will learn what a zombie is.
For including additional php files you can use the function m2import, which is an alternative for include_once.
Configuring chains:
The configuration file is build by XML tags in name space conf. The root tag is conf:app. It does not have any attributes. We now will see a sample configuration file and than discuss what it means:
<conf:app>In conf:params you can place some configuration parameters for the whole application. They are accessible through $env->getConfig($name) in the PHP programmed parts and with configScope->name in the EL in your PSPs (PHP Server Pages). PHP Server Pages are much like Java Server Pages, except that the scriptlets are written in PHP, the EL is more PHP like and a bit more powerful (because it can handle arrays and action form object structures in the same way) and the custom tags are programmed in an other, more straight forward way and must not be imported in a name space (the tags just have a name space which is mapped to a directory name) in order to use them.
<conf:params>
<conf:param name="lang" value="de_DE"/>
</conf:params>
<conf:filters filterPath="filter">
<conf:subChain match="/test/*">
<conf:filter class="PathInfoActionParamFilter" match="*">
<conf:filterParam name="module" value="/test"/>
</conf:filter>
<conf:filter class="ActionDispatcherFilter" match="*"
capture="false" captureAttribute="testCapture">
<conf:filterParam name="basedir"
value="/documentroot/modules/test"/>
<conf:filterParam name="class-extension" value=".php"/>
<conf:filterParam name="view-by-methodname" value="true"/>
</conf:filter>
</conf:subChain>
<conf:filter class="RedirectFilter" match="*" keep="red">
</conf:filter>
<conf:filter restore="${red}">
<conf:filterParam name="target" value="/test"/>
</conf:filter>
</conf:filters>
</conf:app>
Within conf:filters the filter chain is defined. The attribute filterpath can be used to specify an additional custom path for your filters. The filter chain will load look for a filter in your own filter path first and only if the filter was not found there it will be loaded from phpinchains/filters.
conf:subChain allows us to divide the chain in subchains and can make the search for a filter to load an invoke much faster, by reducing the regular expressions to match against. The matches of filters within a conf:subChain will only be processed if the match attribute of the subchain matches. For that reason a subchain with match * does not make any sense, because it will always be processed. The subchain in this example is only processed if the main file of the chains application is access like:
http://somehost/index.php/test/some/more/slashesWith the conf:filter tag a filter can be declared. The filters are processed in the order they are declared in the XML configuration file. The two attributes class and match are required. class specifies the class name of the filter (file is named after class as mentioned above) and match specifies the already explained regular expression to find out if a filter should be invoked.
There are four optional attributes for the conf:filter tag: capture, captureAttribute, keep and restore.
The attributes capture and captureAttribute must be used together. If capture has value is 'true' the output of the filter will be captured and stored in the attribute which name is specified by captureAttribute. You can get the captured output by calling $env->getAttributes(
The attributes keep and restore are used to reduce redundant information. If you use the attribute keep the configuration of the filter is stored in a EL variable with the name specified by keep. To define a new filter which differs only a little bit from one kept e.g. with keep="test", you can define a new filter with conf:filter and use of attribute restore="${test}" to configure the new filter with the same values as the old and specify only the differing attributes (${test} is a EL expression which returns the variable test). The params specified by conf:filterParam are also stored in the keep variable.
There are some special tags, you can insert programm logic in the configuration file. Thus it is possible to reduce the size of a configuration file and to eliminate more redundancy. The tags are conf:if, conf:set and conf:foreach, and they are identical with the ones in the namespace c. They are explained on the page about PSPs and EL.
With conf:filterParams you can configure a filter through parameters the filter understands. You can declare the name and the value with constant values and by using EL in the XML attribute value (also in the other tags).
Example:
<conf:filterParam name="testname" value="testvalue"/>Additionally you can initialize the filter params with values derived from the match regular expression (attribute match of tag conf:filter). The match regex is evaluated when the chain is processed. After that saved parts can be inserted by using {1} for example to insert the first saved part in the value of the filter param.
is equivalent to
<conf:set var="var1" value="tna"/>
<conf:set var="var2" value="stv"/>
<conf:filterParam name="tes${var1}me" value="te${var2}alue"/>
Example:
<conf:filter class="MyFilter" match="/peng/*">After calling the index file via
<conf:filterParam name="part" value="part: {1}"/>
</conf:filter>
http://somehost/index.php/peng/some/more/chars
With some knowledge of pcre (* will be translated to (.*) and *? will be translated to (.*?)) you can do things like:
<conf:filter class="RedirectFilter" match="/admin/*.(gif|jpg|png)">Every part of the regular expression surrounded by parenthesis will be saved. Thus this filter only redirects to http://somehost/imgres/... if the path ends with gif, jpeg or png.
<conf:filterParam name="target" value="http://somehost/imgres/{1}.{2}"/>
<conf:filterParam name="add-slash" value="false"/>
</conf:filter>
Starting a chain:
Once you have defined a chain you need to load and start it in your programm. This task is quite simple and is done by the following lines of php code which should be placed in the index file of you application. In this example the XML configuration file is named app.xml and is located in the same directory as the index file of the application. PHPinChains is located in a directory called lib which was created in the same directory as the index file of the application.
include_once('lib/phpinchains/core.php');
PHPinChains::prepareAndStartChain('app.xml');
By defining the constant MODEL2_INCLUDE_PATH you can declare paths separated by ':' you like it would be possible with ini_set for setting the include paths.The configuration will be compiled to php source after you accessed the index file the first time and later on each time you modify app.xml. After the compilation was done and the app.xml.php file exists, it is possible to remove it (e.g. on the deployment sytem). Then the modification checking and the compilation part will be skipped.


