In Lithium itself, when creating a method that can be filtered, a method is implemented as a closure and is passed to either Object::_filter() or StaticObject::_filter(). Each object internally maintains its own list of filters, which are applied in these methods and passed to Filters::run(). When implementing a custom filter system outside of Lithium, you can create your own list of filters, and pass it to $options['data'] in the run() method. When creating a filter to apply to a method, you need the name of the method you want to call, along with a **closure**, that defines what you want the filter to do. All filters take the same 3 parameters: $self,$params, and $chain. - $self: If the filter is applied on an object instance, then $self will be that instance. If applied to a static class, then $self will be a string containing the fully-namespaced class name. - $params: Contains an associative array of the parameters that are passed into the method. You can modify or inspect these parameters before allowing the method to continue. - $chain: Finally, $chain contains the list of filters in line to be executed (as an instance of the Filters class). At the bottom of $chain is the method itself. This is why most filters contain a line that looks like this: {{{return $chain->next($self, $params, $chain);}}} This passes control to the next filter in the chain, and finally, to the method itself. This allows you to interact with the return value as well as the parameters. Within the framework, you can call applyFilter() on any object (static or instantiated) and pass the name of the method you would like to filter, along with the filter itself. For example: {{{use \lithium\action\Dispatcher; Dispatcher::applyFilter('run', function($self, $params, $chain) { Custom pre-dispatch logic goes here $response = $chain->next($self, $params, $chain); $response now contains a Response object with the result of the dispatched request, and can be modified as appropriate ... return $response; });}}} The logic in the closure will now be executed on every call to Dispatcher::run(), and $response will always be modified by any custom logic present before being returned from run().
See also: lithium\util\collection\Filters::run()
See also: lithium\core\Object::_filter()
See also: lithium\core\StaticObject::_filter()
See also: lithium\core\Object::applyFilter()
See also: lithium\core\StaticObject::applyFilter()
Inheritance: extends lithium\util\Collection
 public function testRunWithoutChain()
 {
     $options = array('method' => __FUNCTION__, 'class' => __CLASS__, 'items' => array(function ($self, $params, $chain) {
         return $chain->next($self, $params, null);
     }, 'This is a filter chain that calls $chain->next() without the $chain argument.'));
     $result = Filters::run(__CLASS__, array(), $options);
     $expected = 'This is a filter chain that calls $chain->next() without the $chain argument.';
     $this->assertEqual($expected, $result);
 }
Exemple #2
0
 public function testLazyApply()
 {
     $class = 'lithium\\tests\\mocks\\util\\MockFilters';
     Filters::apply($class, 'filteredMethod', function ($self, $params, $chain) {
         return md5($chain->next($self, $params, $chain));
     });
     $expected = md5('Working?');
     $result = $class::filteredMethod();
     $this->assertEqual($expected, $result);
     Filters::apply($class, 'filteredMethod', function ($self, $params, $chain) {
         return sha1($chain->next($self, $params, $chain));
     });
     $expected = md5(sha1('Working?'));
     $result = $class::filteredMethod();
     $this->assertEqual($expected, $result);
 }
 public function testWriteFilter()
 {
     $base = Libraries::get(true, 'resources') . '/tmp/logs';
     $this->skipIf(!is_writable($base), "Path `{$base}` is not writable.");
     Filters::apply('lithium\\analysis\\Logger', 'write', function ($self, $params, $chain) {
         $params['message'] = 'Filtered Message';
         return $chain->next($self, $params, $chain);
     });
     $config = array('default' => array('adapter' => 'File', 'timestamp' => false, 'format' => "{:message}\n"));
     Logger::config($config);
     $result = Logger::write('info', 'Original Message');
     $this->assertTrue(file_exists($base . '/info.log'));
     $expected = "Filtered Message\n";
     $result = file_get_contents($base . '/info.log');
     $this->assertEqual($expected, $result);
     unlink($base . '/info.log');
 }
Exemple #4
0
<?php

use lithium\util\collection\Filters;
use li3_memoize\extensions\Memoize;
/**
 * Filters the creation of helpers/models
 */
Filters::apply('lithium\\core\\Libraries', 'instance', function ($self, $params, $chain) {
    // Prescan Model
    if (isset($params['options']['model'])) {
        Memoize::catchModel($self, $params, $chain);
    }
    $object = $chain->next($self, $params, $chain);
    // Postscan Helper
    if ($params['type'] == 'helper') {
        Memoize::catchHelper($object);
    }
    return $object;
});
Exemple #5
0
 /**
  * Executes a set of filters against a method by taking a method's main implementation as a
  * callback, and iteratively wrapping the filters around it.
  *
  * @see lithium\util\collection\Filters
  * @param string|array $method The name of the method being executed, or an array containing
  *        the name of the class that defined the method, and the method name.
  * @param array $params An associative array containing all the parameters passed into
  *        the method.
  * @param Closure $callback The method's implementation, wrapped in a closure.
  * @param array $filters Additional filters to apply to the method for this call only.
  * @return mixed
  */
 protected static function _filter($method, $params, $callback, $filters = array())
 {
     $class = get_called_class();
     $hasNoFilters = empty(static::$_methodFilters[$class][$method]);
     if ($hasNoFilters && !$filters && !Filters::hasApplied($class, $method)) {
         return $callback($class, $params, null);
     }
     if (!isset(static::$_methodFilters[$class][$method])) {
         static::$_methodFilters += array($class => array());
         static::$_methodFilters[$class][$method] = array();
     }
     $data = array_merge(static::$_methodFilters[$class][$method], $filters, array($callback));
     return Filters::run($class, $params, compact('data', 'class', 'method'));
 }
Exemple #6
0
 /**
  * Executes a set of filters against a method by taking a method's main implementation as a
  * callback, and iteratively wrapping the filters around it. This, along with the `Filters`
  * class, is the core of Lithium's filters system. This system allows you to "reach into" an
  * object's methods which are marked as _filterable_, and intercept calls to those methods,
  * optionally modifying parameters or return values.
  *
  * @see lithium\core\Object::applyFilter()
  * @see lithium\util\collection\Filters
  * @param string $method The name of the method being executed, usually the value of
  *               `__METHOD__`.
  * @param array $params An associative array containing all the parameters passed into
  *              the method.
  * @param Closure $callback The method's implementation, wrapped in a closure.
  * @param array $filters Additional filters to apply to the method for this call only.
  * @return mixed Returns the return value of `$callback`, modified by any filters passed in
  *         `$filters` or applied with `applyFilter()`.
  */
 protected function _filter($method, $params, $callback, $filters = array())
 {
     list($class, $method) = explode('::', $method);
     if (empty($this->_methodFilters[$method]) && empty($filters)) {
         return $callback($this, $params, null);
     }
     $f = isset($this->_methodFilters[$method]) ? $this->_methodFilters[$method] : array();
     $data = array_merge($f, $filters, array($callback));
     return Filters::run($this, $params, compact('data', 'class', 'method'));
 }
Exemple #7
0
 /**
  * Uses service location (i.e. `Libraries::locate()`) to look up a named class of a particular
  * type, and creates an instance of it, and passes an array of parameters to the constructor.
  *
  * If the given class can't be found, an exception is thrown.
  *
  * @param string $type The type of class as defined by `Libraries::$_paths`.
  * @param string $name The un-namespaced name of the class to instantiate.
  * @param array $options An array of constructor parameters to pass to the class.
  * @return object If the class is found, returns an instance of it, otherwise throws an
  *         exception.
  * @throws lithium\core\ClassNotFoundException Throws an exception if the class can't be found.
  * @filter
  */
 public static function instance($type, $name, array $options = array())
 {
     $params = compact('type', 'name', 'options');
     $_paths =& static::$_paths;
     $implementation = function ($self, $params) use(&$_paths) {
         $name = $params['name'];
         $type = $params['type'];
         if (!$name && !$type) {
             $message = "Invalid class lookup: `\$name` and `\$type` are empty.";
             throw new ClassNotFoundException($message);
         }
         if (!is_string($type) && $type !== null && !isset($_paths[$type])) {
             throw new ClassNotFoundException("Invalid class type `{$type}`.");
         }
         if (!($class = $self::locate($type, $name))) {
             throw new ClassNotFoundException("Class `{$name}` of type `{$type}` not found.");
         }
         if (is_object($class)) {
             return $class;
         }
         if (!(is_string($class) && class_exists($class))) {
             throw new ClassNotFoundException("Class `{$name}` of type `{$type}` not defined.");
         }
         return new $class($params['options']);
     };
     if (!isset(static::$_methodFilters[__FUNCTION__])) {
         return $implementation(get_called_class(), $params);
     }
     $class = get_called_class();
     $method = __FUNCTION__;
     $data = array_merge(static::$_methodFilters[__FUNCTION__], array($implementation));
     return Filters::run($class, $params, compact('data', 'class', 'method'));
 }
 /**
  * Executes a set of filters against a method by taking a method's main implementation as a
  * callback, and iteratively wrapping the filters around it.
  *
  * @param string|array $method The name of the method being executed, or an array containing
  *        the name of the class that defined the method, and the method name.
  * @param array $params An associative array containing all the parameters passed into
  *        the method.
  * @param Closure $callback The method's implementation, wrapped in a closure.
  * @param array $filters Additional filters to apply to the method for this call only.
  * @return mixed
  * @see lithium\util\collection\Filters
  */
 protected static function _filter($method, $params, $callback, $filters = array())
 {
     if (!strpos($method, '::')) {
         $class = get_called_class();
     } else {
         list($class, $method) = explode('::', $method);
     }
     if (empty(static::$_methodFilters[$class][$method]) && empty($filters)) {
         return $callback->__invoke($class, $params, null);
     }
     if (!isset(static::$_methodFilters[$class][$method])) {
         static::$_methodFilters += array($class => array());
         static::$_methodFilters[$class][$method] = array();
     }
     $items = array_merge(static::$_methodFilters[$class][$method], $filters, array($callback));
     return Filters::run($class, $params, compact('items', 'class', 'method'));
 }
Exemple #9
0
 /**
  * Collects a set of filters to iterate. Creates a filter chain for the given class/method,
  * executes it, and returns the value.
  *
  * @param mixed $class The class for which this filter chain is being created. If this is the
  *        result of a static method call, `$class` should be a string. Otherwise, it should
  *        be the instance of the object making the call.
  * @param array $params An associative array of the given method's parameters.
  * @param array $options The configuration options with which to create the filter chain.
  *        Mainly, these options allow the `Filters` object to be queried for details such as
  *        which class / method initiated it. Available keys:
  *        - `'class'`: The name of the class that initiated the filter chain.
  *        - `'method'`: The name of the method that initiated the filter chain.
  *        - `'data'` _array_: An array of callable objects (usually closures) to be iterated
  *          through. By default, execution will be nested such that the first item will be
  *          executed first, and will be the last to return.
  * @return Returns the value returned by the first closure in `$options['data`]`.
  */
 public static function run($class, $params, array $options = array())
 {
     $defaults = array('class' => null, 'method' => null, 'data' => array());
     $options += $defaults;
     $lazyFilterCheck = is_string($class) && $options['method'];
     if ($lazyFilterCheck && isset(static::$_lazyFilters[$class][$options['method']])) {
         $filters = static::$_lazyFilters[$class][$options['method']];
         unset(static::$_lazyFilters[$class][$options['method']]);
         $options['data'] = array_merge($filters, $options['data']);
         foreach ($filters as $filter) {
             $class::applyFilter($options['method'], $filter);
         }
     }
     $chain = new Filters($options);
     $next = $chain->rewind();
     return $next($class, $params, $chain);
 }
Exemple #10
0
<?php

// Define used classes.
use lithium\util\collection\Filters;
use lithium\storage\Session;
// Get to the `Session` instance by filtering the `Dispatcher`
Filters::apply('\\lithium\\action\\Dispatcher', '_callable', function ($self, $params, $chain) {
    $controller = $chain->next($self, $params, $chain);
    Session::applyFilter('read', function ($self, $params, $chain) {
        $profiler = \Profiler::start('storage\\Session::read');
        $result = $chain->next($self, $params, $chain);
        $profiler->end();
        return $result;
    });
    Session::applyFilter('write', function ($self, $params, $chain) {
        $profiler = \Profiler::start('storage\\Session::write');
        $result = $chain->next($self, $params, $chain);
        $profiler->end();
        return $result;
    });
    // Return the controller object.
    return $controller;
});
Exemple #11
0
<?php

// Define used classes.
use lithium\util\collection\Filters;
// Get to the `Controller` instance by filtering the `Dispatcher`
Filters::apply('\\lithium\\action\\Dispatcher', '_callable', function ($self, $params, $chain) {
    $controller = $chain->next($self, $params, $chain);
    if (is_a($controller, '\\lithium\\action\\Controller')) {
        /**
         * Apply the filter to our `Controller` instance. We can't apply the
         * filter statically.
         */
        $controller->applyFilter('__invoke', function ($self, $params, $chain) {
            $profiler = \Profiler::start('action\\Controller::__invoke');
            $response = $chain->next($self, $params, $chain);
            $profiler->end();
            // Return the response object.
            return $response;
        });
    }
    // Return the controller object.
    return $controller;
});
Exemple #12
0
// Define used classes.
use lithium\util\collection\Filters;
use lithium\data\Connections;
// Attach to the `Connections` adapters after dispatch.
Filters::apply('\\lithium\\action\\Dispatcher', '_callable', function ($self, $params, $chain) {
    /**
     * Loop over all defined `Connections` adapters and tack in our
     * filter on the `_execute` method.
     */
    foreach (Connections::get() as $connection) {
        $connection = Connections::get($connection);
        $connection->applyFilter('_execute', function ($self, $params, $chain) {
            $profiler = \Profiler::sqlStart($params['sql']);
            $response = $chain->next($self, $params, $chain);
            $profiler->end();
            return $response;
        });
        $connection->applyFilter('read', function ($self, $params, $chain) {
            $response = $chain->next($self, $params, $chain);
            \Profiler::start($response->model());
            $info = $response->result()->resource();
            $profiler = \Profiler::sqlStart(json_encode($info->info()));
            $profiler->end();
            \Profiler::end($response->model());
            return $response;
        });
    }
    // Return the controller.
    return $chain->next($self, $params, $chain);
});
Exemple #13
0
Filters::apply('lithium\\action\\Dispatcher', '_callable', function ($self, $params, $chain) {
    if (!Environment::is(Libraries::get('li3_profiler', 'environment'))) {
        // Enable the profiler.
        \Profiler::disable();
    } else {
        $controller = $chain->next($self, $params, $chain);
        if (Libraries::get('li3_profiler', 'inject')) {
            /**
             * If we have a `Controller` object we will filter it so that we can
             * inject our rendering HTML.
             */
            if (is_a($controller, '\\lithium\\action\\Controller')) {
                $controller->applyFilter('__invoke', function ($self, $params, $chain) {
                    $response = $chain->next($self, $params, $chain);
                    if ($response->type === 'text/html') {
                        /**
                         * Here we tack in our rendering if the `Response` object happens
                         * to be "text/html" and we are enabled.
                         */
                        ob_start();
                        \Profiler::render();
                        $response->body = str_replace('</body>', ob_get_clean() . '</body>', $response->body);
                    }
                    return $response;
                });
            }
        }
        return $controller;
    }
    return $chain->next($self, $params, $chain);
});
Exemple #14
0
<?php

// Define used classes.
use lithium\util\collection\Filters;
// Tack in our `Dispatcher` profiling.
Filters::apply('\\lithium\\action\\Dispatcher', '_callable', function ($self, $params, $chain) {
    $profiler = \Profiler::start('action\\Dispatcher::_callable');
    $controller = $chain->next($self, $params, $chain);
    $profiler->end();
    return $controller;
});
Exemple #15
0
 public static function initDispatcher()
 {
     // Request -> Response
     Filters::apply('lithium\\action\\Dispatcher', 'run', function ($self, $params, $chain) {
         $data = array();
         $data['start'] = microtime(true);
         $data['memory'] = memory_get_usage(true);
         $data['name'] = 'Dispatcher::run';
         $response = $chain->next($self, $params, $chain);
         $data['end'] = microtime(true);
         $data['memory'] = memory_get_usage(true) - $data['memory'];
         $data['time'] = $data['end'] - $data['start'];
         Debugger::push('events', $data);
         Debugger::inc('events.time', $data['time']);
         return $response;
     });
     // Router -> Controller
     Filters::apply('lithium\\action\\Dispatcher', '_callable', function ($self, $params, $chain) {
         $data = array();
         $data['start'] = microtime(true);
         $data['memory'] = memory_get_usage(true);
         $data['name'] = 'Dispatcher::_callable';
         $controller = $chain->next($self, $params, $chain);
         $data['end'] = microtime(true);
         $data['memory'] = memory_get_usage(true) - $data['memory'];
         $data['time'] = $data['end'] - $data['start'];
         Debugger::push('events', $data);
         Debugger::inc('events.time', $data['time']);
         if ($controller instanceof \lithium\action\Controller) {
             Debugger::setRequest($params['request']);
             Debugger::bindTo($controller);
         }
         return $controller;
     });
 }
Exemple #16
0
 public static function apply($object, array $conditions, $handler)
 {
     $conditions = $conditions ?: array('type' => 'Exception');
     list($class, $method) = is_string($object) ? explode('::', $object) : $object;
     $wrap = static::$_exceptionHandler;
     $_self = get_called_class();
     $filter = function ($self, $params, $chain) use($_self, $conditions, $handler, $wrap) {
         try {
             return $chain->next($self, $params, $chain);
         } catch (Exception $e) {
             if (!$_self::matches($e, $conditions)) {
                 throw $e;
             }
             return $handler($wrap($e, true), $params);
         }
     };
     if (is_string($class)) {
         Filters::apply($class, $method, $filter);
     } else {
         $class->applyFilter($method, $filter);
     }
 }
Exemple #17
0
<?php

// Define used classes.
use lithium\util\collection\Filters;
use lithium\security\Auth;
// Get to the `Auth` instance by filtering the `Dispatcher`
Filters::apply('\\lithium\\action\\Dispatcher', '_callable', function ($self, $params, $chain) {
    $controller = $chain->next($self, $params, $chain);
    Auth::applyFilter('check', function ($self, $params, $chain) {
        $profiler = \Profiler::start('security\\Auth::check');
        $result = $chain->next($self, $params, $chain);
        $profiler->end();
        return $result;
    });
    Auth::applyFilter('set', function ($self, $params, $chain) {
        $profiler = \Profiler::start('security\\Auth::set');
        $result = $chain->next($self, $params, $chain);
        $profiler->end();
        return $result;
    });
    Auth::applyFilter('clear', function ($self, $params, $chain) {
        $profiler = \Profiler::start('security\\Auth::clear');
        $result = $chain->next($self, $params, $chain);
        $profiler->end();
        return $result;
    });
    // Return the controller object.
    return $controller;
});
Exemple #18
0
<?php

// Define used classes.
use lithium\util\collection\Filters;
// Tack in our `Dispatcher` profiling.
Filters::apply('\\lithium\\net\\http\\Media', 'render', function ($self, $params, $chain) {
    $profiler = \Profiler::start('net\\http\\Media::render');
    $result = $chain->next($self, $params, $chain);
    $profiler->end();
    return $result;
});