MapFilter Usage

MapFilter

MapFilter is a tool designed to create filters that process user queries to one or more results according to defined pattern.

  • A pattern is an arbitrary implementation of MapFilter_PatternInterface. The pattern is used during the parsing procedure to do all the work needed to obtain all kinds of results specified by implemented interfaces.
  • A query is a data structure (usually obtained from the user) to be filtered by the pattern. MapFilter itself does not constrain the type of user query. However, it might be constraint by the pattern.
  • Results are the outcomes of user query filtering. Every pattern should provide one or more results.

To use MapFilter there is need to define the pattern (and inject it using constructor or setPattern method) describing expected data. After setting the query (using constructor or setQuery method), MapFilter is ready to proceed with input filtering. Filtering results are obtained by fetchResult. The result is the value returned by the MapFilter_PatternInterface::parse() method or the pattern instance itself in case the method returned null.

Examples

MapFilter

There are three different ways how to create MapFilter instance:

    $pattern = new MapFilter_NullPattern ();

    $query = Array ( 'attr0' => 'value', 'attr1' => 'value' );

    // Configure filter using constructor.
    $filter0 = new MapFilter ( $pattern, $query );

    // Create empty filter and configure it using fluent interface.
    $filter1 = new MapFilter ();
    $filter1->setPattern ( $pattern )->setQuery ( $query );

    // Optional combination of both can be used as well.
    $filter2 = new MapFilter ( $pattern );
    $filter2->setQuery ( $query );

All three filters are identical.

User Patterns

A power of MapFilter is in declaration of user patterns.

Let's have an associative array (representing command line options or deserialized configuration file etc.) and we would like to find out whether it contains certain keys and what keys are redundant. Our pattern would have two outcomes:

  • associative array containing only valid keys and corresponding values
  • and array of keys that ware filtered out

We declare the class to represent this outcomes.

class ArrayKeyWhitelistResult {

    private $_filtered;
    private $_redundantKeys;

    /*
     * Initialize whitelist
     *
     * @param     Array   $whitelist      Array of allowed keys.
     */
    public function __construct(Array $filtered, Array $redundantKeys)
    {
        $this->_filtered = $filtered;
        $this->_redundantKeys = $redundantKeys;
    }

    /*
     * Get filtering results
     *
     * @return  Array   Array containing keys and values from
     *                  query that DID match the whitelist pattern.
     */
    public function getFiltered()
    {
        return $this->_filtered;
    }

    /*
     * Get filtered out
     *
     * @return  Array   Array containing keys from querty that DID NOT match
     *                  the whitelist pattern.
     */
    public function getRedundantKeys()
    {
        return $this->_redundantKeys;
    }
}

Next we create a pattern that performs filtering. It's MapFilter_PatternInterface::parse() is supposed to return our result type.

class ArrayKeyWhitelistPattern implements Mapfilter_PatternInterface {

    private $_whitelist;

    /*
     * Initialize whitelist
     *
     * @param     Array   $whitelist      Array of allowed keys.
     */
    public function __construct(Array $whitelist)
    {
        $this->_whitelist = array_unique($whitelist);
    }

    /*
     * Perform a filtering
     *
     * @param     Array   $query  A query to filter.
     * @return    ArrayKeyWhitelistResult
     */
    public function parse($query)
    {

        if ( !is_array($query) && !( $query instanceof ArrayAccess ) ) {

          throw new MapFilter_InvalidStructureException;
        }

        $filtered = $redundantKeys = Array();
        foreach ($query as $keyCandidate => $valueCandidate) {

            if(in_array($keyCandidate, $this->_whitelist)) {

                $filtered[ $keyCandidate ] = $valueCandidate;
            } else {

                $redundantKeys[] = $keyCandidate;
            }
        }

        return new ArrayKeyWhitelistResult($filtered, $redundantKeys);
    }
}

Using of the pattern is straightforward. Pattern is initialized with allowed keys, then injected to MapFilter along with the query. After that the filter is ready to fetch the results.

    // Allowed options
    $pattern = new ArrayKeyWhitelistPattern (
        Array ( '-v', '-h', '-o' )
    );

    $filter = new MapFilter ( $pattern, $query );

    $valid = $filter->fetchResult ()->getFiltered ();
    $redundant = $filter->fetchResult ()->getRedundantKeys ();

MapFilter will call the ArrayKeyWhitelistPattern::parse() method when we ask for the results for the first time. Even if we call MapFilter::fetchResult() several times (as we did in the last example), MapFilter_PatternInterface::parse() is guaranteed not to be called more than once.

Reuse of filter instance

Instance of MapFilter can be easily reused setting new pattern or query (or perhaps both) using setter methods. This could be useful in case we would like to filter the same query using different filters or more likely use the same filter with different user queries. In that case MapFilter calls MapFilter_PatternInterface::parse() for every unique configuration.

    // Initial pattern configuration
    $filter = new MapFilter ( $pattern, $query );

    // Parsing is done here
    $newResult = $filter->fetchResult ();

    // Here we parse again since we have reset the query
    $newQueryResult = $filter->setQuery ( $query )->fetchResult ();

    // Here we parse again since we have reset the pattern
    $newPatternResult = $filter->setPattern ( $pattern )->fetchResult ();

    // No needed to parse the same query using the same pattern
    // $newPatternResult === $sameResult
    $sameResult = $filter->fetchResult ();

See also:
MapFilter
MapFilter_PatternInterface