Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
0.00% covered (danger)
0.00%
0 / 1
18.75% covered (danger)
18.75%
3 / 16
CRAP
54.33% covered (warning)
54.33%
69 / 127
router
0.00% covered (danger)
0.00%
0 / 1
18.75% covered (danger)
18.75%
3 / 16
201.12
54.33% covered (warning)
54.33%
69 / 127
 __construct
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
7 / 7
 get_routes
0.00% covered (danger)
0.00%
0 / 1
4.01
92.31% covered (success)
92.31%
12 / 13
 getRouteCollection
0.00% covered (danger)
0.00%
0 / 1
2
0.00% covered (danger)
0.00%
0 / 1
 setContext
0.00% covered (danger)
0.00%
0 / 1
3.07
80.00% covered (warning)
80.00%
8 / 10
 getContext
0.00% covered (danger)
0.00%
0 / 1
2
0.00% covered (danger)
0.00%
0 / 1
 generate
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
1 / 1
 match
0.00% covered (danger)
0.00%
0 / 1
2
0.00% covered (danger)
0.00%
0 / 1
 get_matcher
0.00% covered (danger)
0.00%
0 / 1
6
0.00% covered (danger)
0.00%
0 / 5
 create_dumped_url_matcher
0.00% covered (danger)
0.00%
0 / 1
12
0.00% covered (danger)
0.00%
0 / 14
 create_new_url_matcher
0.00% covered (danger)
0.00%
0 / 1
2
0.00% covered (danger)
0.00%
0 / 2
 get_generator
0.00% covered (danger)
0.00%
0 / 1
2.26
60.00% covered (warning)
60.00%
3 / 5
 create_dumped_url_generator
0.00% covered (danger)
0.00%
0 / 1
12
0.00% covered (danger)
0.00%
0 / 14
 create_new_url_generator
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
2 / 2
 resolveParameters
0.00% covered (danger)
0.00%
0 / 1
6.06
88.00% covered (warning)
88.00%
22 / 25
 resolve
0.00% covered (danger)
0.00%
0 / 1
10.14
60.00% covered (warning)
60.00%
6 / 10
 anonymous function
0.00% covered (danger)
0.00%
0 / 1
6.43
46.67% covered (danger)
46.67%
7 / 15
<?php
/**
*
* This file is part of the phpBB Forum Software package.
*
* @copyright (c) phpBB Limited <https://www.phpbb.com>
* @license       GNU General Public License, version 2 (GPL-2.0)
*
* For full copyright and license information, please see
* the docs/CREDITS.txt file.
*
*/
namespace phpbb\routing;
use phpbb\routing\resources_locator\resources_locator_interface;
use Symfony\Component\Config\ConfigCache;
use Symfony\Component\Config\Loader\LoaderInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\DependencyInjection\Exception\ParameterNotFoundException;
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
use Symfony\Component\Filesystem\Exception\IOException;
use Symfony\Component\Routing\Generator\Dumper\PhpGeneratorDumper;
use Symfony\Component\Routing\Generator\UrlGenerator;
use Symfony\Component\Routing\Matcher\Dumper\PhpMatcherDumper;
use Symfony\Component\Routing\Matcher\UrlMatcher;
use Symfony\Component\Routing\RequestContext;
use Symfony\Component\Routing\RouteCollection;
use Symfony\Component\Routing\RouterInterface;
/**
 * Integration of all pieces of the routing system for easier use.
 */
class router implements RouterInterface
{
    /**
     * @var ContainerInterface
     */
    protected $container;
    /**
     * @var resources_locator_interface
     */
    protected $resources_locator;
    /**
     * @var LoaderInterface
     */
    protected $loader;
    /**
     * PHP file extensions
     *
     * @var string
     */
    protected $php_ext;
    /**
     * @var \Symfony\Component\Routing\Matcher\UrlMatcherInterface|null
     */
    protected $matcher;
    /**
     * @var \Symfony\Component\Routing\Generator\UrlGeneratorInterface|null
     */
    protected $generator;
    /**
     * @var RequestContext
     */
    protected $context;
    /**
     * @var RouteCollection
     */
    protected $route_collection;
    /**
     * @var string
     */
    protected $cache_dir;
    /**
     * Construct method
     *
     * @param ContainerInterface            $container            DI container
     * @param resources_locator_interface    $resources_locator    Resources locator
     * @param LoaderInterface                $loader                Resources loader
     * @param string                        $php_ext            PHP file extension
     * @param string                        $cache_dir            phpBB cache directory
     */
    public function __construct(ContainerInterface $container, resources_locator_interface $resources_locator, LoaderInterface $loader, $php_ext, $cache_dir)
    {
        $this->container            = $container;
        $this->resources_locator    = $resources_locator;
        $this->loader                = $loader;
        $this->php_ext                = $php_ext;
        $this->context                = new RequestContext();
        $this->cache_dir            = $cache_dir;
    }
    /**
     * Get the list of routes
     *
     * @return RouteCollection Get the route collection
     */
    public function get_routes()
    {
        if ($this->route_collection === null /*|| $this->route_collection->count() === 0*/)
        {
            $this->route_collection = new RouteCollection;
            foreach ($this->resources_locator->locate_resources() as $resource)
            {
                if (is_array($resource))
                {
                    $this->route_collection->addCollection($this->loader->load($resource[0], $resource[1]));
                }
                else
                {
                    $this->route_collection->addCollection($this->loader->load($resource));
                }
            }
            $this->resolveParameters($this->route_collection);
        }
        return $this->route_collection;
    }
    /**
     * {@inheritdoc}
     */
    public function getRouteCollection()
    {
        return $this->get_routes();
    }
    /**
     * {@inheritdoc}
     */
    public function setContext(RequestContext $context)
    {
        $this->context = $context;
        if ($this->matcher !== null)
        {
            $this->get_matcher()->setContext($context);
        }
        if ($this->generator !== null)
        {
            $this->get_generator()->setContext($context);
        }
    }
    /**
     * {@inheritdoc}
     */
    public function getContext()
    {
        return $this->context;
    }
    /**
     * {@inheritdoc}
     */
    public function generate($name, $parameters = array(), $referenceType = self::ABSOLUTE_PATH)
    {
        return $this->get_generator()->generate($name, $parameters, $referenceType);
    }
    /**
     * {@inheritdoc}
     */
    public function match($pathinfo)
    {
        return $this->get_matcher()->match($pathinfo);
    }
    /**
     * Gets the UrlMatcher instance associated with this Router.
     *
     * @return \Symfony\Component\Routing\Matcher\UrlMatcherInterface A UrlMatcherInterface instance
     */
    public function get_matcher()
    {
        if ($this->matcher !== null)
        {
            return $this->matcher;
        }
        $this->create_dumped_url_matcher();
        return $this->matcher;
    }
    /**
     * Creates a new dumped URL Matcher (dump it if necessary)
     */
    protected function create_dumped_url_matcher()
    {
        try
        {
            $cache = new ConfigCache("{$this->cache_dir}url_matcher.{$this->php_ext}", defined('DEBUG'));
            if (!$cache->isFresh())
            {
                $dumper = new PhpMatcherDumper($this->get_routes());
                $options = array(
                    'class'      => 'phpbb_url_matcher',
                    'base_class' => 'Symfony\\Component\\Routing\\Matcher\\UrlMatcher',
                );
                $cache->write($dumper->dump($options), $this->get_routes()->getResources());
            }
            require_once($cache->getPath());
            $this->matcher = new \phpbb_url_matcher($this->context);
        }
        catch (IOException $e)
        {
            $this->create_new_url_matcher();
        }
    }
    /**
     * Creates a new URL Matcher
     */
    protected function create_new_url_matcher()
    {
        $this->matcher = new UrlMatcher($this->get_routes(), $this->context);
    }
    /**
     * Gets the UrlGenerator instance associated with this Router.
     *
     * @return \Symfony\Component\Routing\Generator\UrlGeneratorInterface A UrlGeneratorInterface instance
     */
    public function get_generator()
    {
        if ($this->generator !== null)
        {
            return $this->generator;
        }
        $this->create_dumped_url_generator();
        return $this->generator;
    }
    /**
     * Creates a new dumped URL Generator (dump it if necessary)
     */
    protected function create_dumped_url_generator()
    {
        try
        {
            $cache = new ConfigCache("{$this->cache_dir}url_generator.{$this->php_ext}", defined('DEBUG'));
            if (!$cache->isFresh())
            {
                $dumper = new PhpGeneratorDumper($this->get_routes());
                $options = array(
                    'class'      => 'phpbb_url_generator',
                    'base_class' => 'Symfony\\Component\\Routing\\Generator\\UrlGenerator',
                );
                $cache->write($dumper->dump($options), $this->get_routes()->getResources());
            }
            require_once($cache->getPath());
            $this->generator = new \phpbb_url_generator($this->context);
        }
        catch (IOException $e)
        {
            $this->create_new_url_generator();
        }
    }
    /**
     * Creates a new URL Generator
     */
    protected function create_new_url_generator()
    {
        $this->generator = new UrlGenerator($this->get_routes(), $this->context);
    }
    /**
     * Replaces placeholders with service container parameter values in:
     * - the route defaults,
     * - the route requirements,
     * - the route path,
     * - the route host,
     * - the route schemes,
     * - the route methods.
     *
     * @param RouteCollection $collection
     */
    protected function resolveParameters(RouteCollection $collection)
    {
        /** @var \Symfony\Component\Routing\Route $route */
        foreach ($collection as $route)
        {
            foreach ($route->getDefaults() as $name => $value)
            {
                $route->setDefault($name, $this->resolve($value));
            }
            $requirements = $route->getRequirements();
            unset($requirements['_scheme']);
            unset($requirements['_method']);
            foreach ($requirements as $name => $value)
            {
                $route->setRequirement($name, $this->resolve($value));
            }
            $route->setPath($this->resolve($route->getPath()));
            $route->setHost($this->resolve($route->getHost()));
            $schemes = array();
            foreach ($route->getSchemes() as $scheme)
            {
                $schemes = array_merge($schemes, explode('|', $this->resolve($scheme)));
            }
            $route->setSchemes($schemes);
            $methods = array();
            foreach ($route->getMethods() as $method)
            {
                $methods = array_merge($methods, explode('|', $this->resolve($method)));
            }
            $route->setMethods($methods);
            $route->setCondition($this->resolve($route->getCondition()));
        }
    }
    /**
     * Recursively replaces placeholders with the service container parameters.
     *
     * @param mixed $value The source which might contain "%placeholders%"
     *
     * @return mixed The source with the placeholders replaced by the container
     *               parameters. Arrays are resolved recursively.
     *
     * @throws ParameterNotFoundException When a placeholder does not exist as a container parameter
     * @throws RuntimeException           When a container value is not a string or a numeric value
     */
    private function resolve($value)
    {
        if (is_array($value))
        {
            foreach ($value as $key => $val)
            {
                $value[$key] = $this->resolve($val);
            }
            return $value;
        }
        if (!is_string($value))
        {
            return $value;
        }
        $container = $this->container;
        $escapedValue = preg_replace_callback('/%%|%([^%\s]++)%/', function ($match) use ($container, $value)
        {
            // skip %%
            if (!isset($match[1]))
            {
                return '%%';
            }
            $resolved = $container->getParameter($match[1]);
            if (is_string($resolved) || is_numeric($resolved))
            {
                return (string) $resolved;
            }
            throw new RuntimeException(sprintf(
                    'The container parameter "%s", used in the route configuration value "%s", '.
                    'must be a string or numeric, but it is of type %s.',
                    $match[1],
                    $value,
                    gettype($resolved)
                )
            );
        }, $value);
        return str_replace('%%', '%', $escapedValue);
    }
}