Filtering and validating user input
One of the most often required features in web environments is filtering and/or validating user input. It is as well one of the most important features, because not doing so can result in security problems of applications which may compromise the data of the application. In order to make such a often recurring task easier to handle Stubbles offers a unique interface to user input and for filtering and validating it.
The request layer
The request layer provides access to user input which includes:
- request parameters
- input from forms
- cookie values
- request headers
They are available via an instance of the stubRequest interface. A concrete implementation of this interface is the stubWebRequest class. To retrieve a request value from this class one has to uses validators and filters (see below).
Choose the source
Every time a request value should be accessed it has to be stated in which source it can be found: parameters, cookies or headers. Each method that operates on request values offers a parameter $source which can take three differant values: stubRequest::SOURCE_COOKIE, stubRequest::SOURCE_HEADER and stubRequest::SOURCE_PARAM. The default value is stubRequest::SOURCE_PARAM. If a wrong value for $source is supplied it falls back to stubRequest::SOURCE_PARAM.
Cancelling the request
In case something is very wrong with the request it can be cancelled via cancel(stubEventDispatcher $dispatcher = null). The method takes an instance of stubEventDispatcher which is used to trigger an event with the name onRequestCancelled. If no dispatcher is given the default global dispatcher is used to trigger the event.
Subsequently the method isCancelled() will always return true. Please be aware that there is no possibility to undo the cancellation!
Validators
How validators work
All validators provided by Stubbles can be found in net.stubbles.helper.validators. They implement the stubValidator interface. Every validator offers the validate($value) method. It takes the value to validate and returns true if the value fulfils the criteria of the validator and false if not. The other method available is getCriterias() which returns an associative array of the criteria used to validate the value.
Combining validators
It is possible to combine any validator to one big validator via the stubAndValidator, the stubOrValidator and the stubXorValidator. A validator can be added to them via the addValidator(stubValidator $validator) method.
With the stubAndValidator the value has to to validate in all of the added validators, within the stubOrValidator the value must only validate in one of the added validators, and within the stubXorValidator it must validate in exactly one of the added validators.
Applying a validator on user input
To apply a validator on user input one has to create an instance of the validator to use. The request instance offers two methods for applying a validator:
validateValue(stubValidator $validator, $valueName, $source = self::SOURCE_PARAM) and getValidatedValue(stubValidator $validator, $valueName, $source = self::SOURCE_PARAM)
The difference between both versions is that the first one will return the return value of the validator (true or false) while the latter one will return the value in case the validator returned true and null if the validator returned false. Please note that the first returns false as well if the value does not exist, while the second will return null in such cases. Effectively this means that a non-existing value and a value which does not validate properly are treated the same way by both methods.
Creating own validators
To create a validator it has to implement the stubValidator interface. Within the validate($value) method any check required can be performed. If all checks are satisfied with the content of $value it should return true, if not return false to indicate that something is wrong the value.
Filters
How filters work
Filters can be found in net::stubbles::ipo::request::filters, implementing the stubFilter interface. While a validator only checks if a value fulfils a given set of criteria, a filter checks against a set of criteria and is allowed to change the value, e.g. it can take an id of something, check if it is a valid id and if that is the case construct the something object and return this.
Filters throw a stubFilterException in case one of the criteria is not fulfilled.
Additionally, filters may return default values if the input value is null or empty. Mostly this depends on the filter or how the filter is configured.
Applying filters
To apply a filter on user input the request instance offers one method: getFilteredValue(stubFilter $filter, $valueName, $source = stubRequest::SOURCE_PARAM)
If the filter throws a stubFilterException this will be caught and added to the internal error list of the request instance while the method will return null. In any other case the filtered value will be returned.
See creating filters for more informations about how to create the filter for different requirements.
Error handling in filters
Some of the filters that are part of Stubbles take an instance of a stubRequestValueErrorFactory as argument for the constructor. This factory is able to create instances of stubRequestValueError which hold a list of error messages in different languages. If the filter detects that a value does not validate against a criterion it throws a stubFilteredException which in turn takes a stubRequestValueError as argument. If a filter is applied on a request value the exception is caught by the request class and the stubRequestValueError added to its internal list of errors.
To check if a value is really not set (because getFilteredValue() returned null) or if an error occurred while filtering one can use the hasValueError($valueName, $source = stubRequest::SOURCE_PARAM) method. It returns true if an error for this request value exists, false otherwise. To get the value error use getValueError($valueName, $source = stubRequest::SOURCE_PARAM). The getValueErrors($source = stubRequest::SOURCE_PARAM) method can be used to retrieve a list of all errors that occurred so far.
Mapping error values
Stubbles provides a list of default error values, and their ids are used to access them. Sometimes it is better to provide a specialised error message for a request parameter instead of the default more general message. To do this you can map error values by using the stubRequestValueErrorFactoryMappingDecorator. This decorates any other stubRequestValueErrorFactory and provides an additional method addMapping($oldErrorId, $newErrorId). If the mapping decorator instance is put into the filter instead the default factory the decorator will make sure that any requests to create an error value with the id $oldErrorId instead create an error value with the id $newErrorId.
Examples
Take the following request:
http://example.org/?foo=bar
In the examples $rveFactory is an instance of stubRequestValueErrorFactory.
Retrieve the value with a filter:
$fooFilter = new stubStringFilter()); $foo = $request->getFilteredValue($fooFilter, 'foo'); var_dump($foo);
Result: bar (length=3)
With a filter that turns the value into an object:
class Foo {}
$fooFilter = new FooFilter();
$foo = $request->getFilteredValue($fooFilter, 'foo');
var_dump($foo);
Result: object(Foo)[4]
Retrieve the value with a validator:
$foo = $request->getValidatedValue(new stubRegexValidator('/bar/'), 'foo');
var_dump($foo);
Result: bar (length=3)
Just use the validator to check:
$foo = $request->validateValue(new stubRegexValidator('/baz/'), 'foo');
var_dump($foo);
Result: bool(false)
Getting the value from a header:
$foo = $request->getValidatedValue(new stubRegexValidator('/bar/'), 'Foo', stubRequest::SOURCE_HEADER);
var_dump($foo);
Result: null
Getting the value from a cookie named foo with the value baz:
$foo = $request->getValidatedValue(new stubRegexValidator('/[bar|baz]/'), 'foo', stubRequest::SOURCE_COOKIE);
var_dump($foo);
Result: baz (length=3)
Retrieve the value with a filter, but an error occurs:
$fooFilter = new stubValidatorFilterDecorator(stubStringFilter(), $rveFactory, new stubRegexValidator('/baz/'));
$foo = $request->getFilteredValue($fooFilter, 'foo');
var_dump($foo);
var_dump($request->hasValueError('foo'));
var_dump($request->getValueError('foo'));
Result: null, bool(true), object(stubRequestValueError)[5] ... (snipped)
