Use Symfony-compatible router

This commit is contained in:
Dmitriy Simushev 2014-11-11 10:34:04 +00:00
parent f8909d5e8c
commit f5470239e4
8 changed files with 192 additions and 343 deletions

View File

@ -697,7 +697,17 @@ users_update:
_controller: Mibew\Controller\UsersController::updateAction _controller: Mibew\Controller\UsersController::updateAction
_access_check: Mibew\AccessControl\Check\LoggedInCheck _access_check: Mibew\AccessControl\Check\LoggedInCheck
# Remove trailing slashe. This route is the last one because previous rotes can # System routes and includes
# The following route is a fake and is used only to load plugins routes. It can
# have any unique name.
_include_pluign_routes:
# Resource name has no meaning and can be any value.
resource: .
# The type actually indicates that plugins routes should be included.
type: plugin
# Remove trailing slashes. This route is the last one because previous rotes can
# (but definitely should not) have trailing slashes. # (but definitely should not) have trailing slashes.
remove_trailing_slash: remove_trailing_slash:
path: /{url} path: /{url}

View File

@ -1,3 +1,9 @@
# The following route is a fake and is used only to load routes of the core. It
# can have any unique name.
_include_core_routes:
resource: routing.yml
type: yaml
# Override the home route to allow users use <mibew root>/install.php path to # Override the home route to allow users use <mibew root>/install.php path to
# the installer. # the installer.
home: home:

View File

@ -22,18 +22,21 @@ require_once(dirname(__FILE__) . '/libs/init.php');
use Mibew\Application; use Mibew\Application;
use Mibew\Authentication\AuthenticationManager; use Mibew\Authentication\AuthenticationManager;
use Mibew\Routing\RouteCollectionLoader;
use Mibew\Routing\Router; use Mibew\Routing\Router;
use Symfony\Component\HttpFoundation\Request; use Mibew\Routing\Loader\PluginLoader;
use Symfony\Component\Config\FileLocator; use Symfony\Component\Config\FileLocator;
use Symfony\Component\Config\Loader\LoaderResolver;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Loader\YamlFileLoader;
// Prepare router // Prepare router
$file_locator = new FileLocator(array(MIBEW_FS_ROOT)); $file_locator = new FileLocator(array(MIBEW_FS_ROOT));
$router = new Router(new RouteCollectionLoader($file_locator)); $route_loader = new YamlFileLoader($file_locator);
$router->setOption( $loader_resolver = new LoaderResolver(array(
'route_collection', $route_loader,
RouteCollectionLoader::ROUTES_CORE | RouteCollectionLoader::ROUTES_PLUGINS new PluginLoader(),
); ));
$router = new Router($route_loader, 'configs/routing.yml');
// Prepare files cache // Prepare files cache
$cache_driver = new \Stash\Driver\FileSystem(); $cache_driver = new \Stash\Driver\FileSystem();

View File

@ -24,17 +24,21 @@ require_once(dirname(__FILE__) . '/libs/init.php');
use Mibew\Application; use Mibew\Application;
use Mibew\Authentication\DummyAuthenticationManager; use Mibew\Authentication\DummyAuthenticationManager;
use Mibew\Routing\RouteCollectionLoader; use Mibew\Routing\Loader\DummyPluginLoader;
use Mibew\Routing\Router; use Mibew\Routing\Router;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Config\FileLocator; use Symfony\Component\Config\FileLocator;
use Symfony\Component\Config\Loader\LoaderResolver;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Loader\YamlFileLoader;
// Prepare router
$file_locator = new FileLocator(array(MIBEW_FS_ROOT)); $file_locator = new FileLocator(array(MIBEW_FS_ROOT));
$router = new Router(new RouteCollectionLoader($file_locator)); $route_loader = new YamlFileLoader($file_locator);
$router->setOption( $loader_resolver = new LoaderResolver(array(
'route_collection', $route_loader,
RouteCollectionLoader::ROUTES_CORE | RouteCollectionLoader::ROUTES_INSTALLATION new DummyPluginLoader(),
); ));
$router = new Router($route_loader, 'configs/routing_install.yml');
$application = new Application($router, new DummyAuthenticationManager()); $application = new Application($router, new DummyAuthenticationManager());

View File

@ -0,0 +1,49 @@
<?php
/*
* This file is a part of Mibew Messenger.
*
* Copyright 2005-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace Mibew\Routing\Loader;
use Symfony\Component\Config\Loader\Loader;
use Symfony\Component\Routing\RouteCollection;
/**
* Pretends to load plugins routes.
*
* Actually it does nothing and can be used as a stub in cases when plugins
* routes should not be loaded.
*/
class DummyPluginLoader extends Loader
{
/**
* {@inheritdoc}
*/
public function load($resource, $type = null)
{
// There is no actual loading.
return (new RouteCollection());
}
/**
* {@inheritdoc}
*/
public function supports($resource, $type = null)
{
return $type === 'plugin';
}
}

View File

@ -0,0 +1,56 @@
<?php
/*
* This file is a part of Mibew Messenger.
*
* Copyright 2005-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace Mibew\Routing\Loader;
use Mibew\Plugin\Manager as PluginManager;
use Symfony\Component\Config\Loader\Loader;
use Symfony\Component\Routing\RouteCollection;
/**
* Loads plugins routes.
*/
class PluginLoader extends Loader
{
/**
* {@inheritdoc}
*/
public function load($resource, $type = null)
{
$collection = new RouteCollection();
foreach (PluginManager::getAllPlugins() as $plugin) {
$resource = $plugin->getFilesPath() . '/routing.yml';
if (!file_exists($resource)) {
// The plugin has no routing file.
continue;
}
$collection->addCollection($this->import($resource, 'yaml'));
}
return $collection;
}
/**
* {@inheritdoc}
*/
public function supports($resource, $type = null)
{
return $type === 'plugin';
}
}

View File

@ -1,140 +0,0 @@
<?php
/*
* This file is a part of Mibew Messenger.
*
* Copyright 2005-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
namespace Mibew\Routing;
use Mibew\Plugin\Manager as PluginManager;
use Mibew\EventDispatcher\EventDispatcher;
use Mibew\EventDispatcher\Events;
use Symfony\Component\Routing\Loader\YamlFileLoader;
use Symfony\Component\Routing\RouteCollection;
use Symfony\Component\Config\FileLocatorInterface;
/**
* Encapsulates routes loading logic.
*/
class RouteCollectionLoader
{
/**
* Indicates that only routes of the core should be loaded.
*/
const ROUTES_CORE = 1;
/**
* Indicates that only plugins' routes should be loaded.
*/
const ROUTES_PLUGINS = 2;
/**
* Indicates that only routes related with installation should be loaded.
*/
const ROUTES_INSTALLATION = 4;
/**
* Indicates that all available routes should be loaded.
*/
const ROUTES_ALL = 7;
/**
* @var YamlFileLoader|null
*/
protected $loader = null;
/**
* Class constructor.
*/
public function __construct(FileLocatorInterface $locator)
{
$this->loader = new YamlFileLoader($locator);
}
/**
* Load routes of specified type.
*
* @param int $type Type of routes to load. Can be one of
* RouteCollectionLoader::ROUTES_* constants.
* @return RouteCollection
*/
public function load($type = self::ROUTES_ALL)
{
$collection = new RouteCollection();
// Load core routes if needed
if ($type & self::ROUTES_CORE) {
$collection->addCollection($this->loadCoreRoutes());
}
// Load installation routes if needed
if ($type & self::ROUTES_INSTALLATION) {
$collection->addCollection($this->loadInstallationRoutes());
}
// Load plugins routes if needed
if ($type & self::ROUTES_PLUGINS) {
$collection->addCollection($this->loadPluginRoutes());
}
// Add an ability for plugins to alter routes list
$arguments = array('routes' => $collection);
EventDispatcher::getInstance()->triggerEvent(Events::ROUTES_ALTER, $arguments);
return $arguments['routes'];
}
/**
* Loads routes of the core.
*
* @return RouteCollection
* @throws \RuntimeException If core routing file is not found.
*/
protected function loadCoreRoutes()
{
return $this->loader->load('configs/routing.yml');
}
/**
* Loads routes related with installation process.
*
* @return RouteCollection
* @throws \RuntimeException If core installation routing file is not found.
*/
protected function loadInstallationRoutes()
{
return $this->loader->load('configs/routing_install.yml');
}
/**
* Loads plugins' routes.
*
* @return RouteCollection
*/
protected function loadPluginRoutes()
{
$collection = new RouteCollection();
foreach (PluginManager::getAllPlugins() as $plugin) {
$file = $plugin->getFilesPath() . '/routing.yml';
if (!file_exists($file)) {
continue;
}
$collection->addCollection($this->loader->load($file));
}
return $collection;
}
}

View File

@ -19,57 +19,53 @@
namespace Mibew\Routing; namespace Mibew\Routing;
use Symfony\Component\HttpFoundation\Request; use Mibew\EventDispatcher\EventDispatcher;
use Symfony\Component\Routing\RequestContext; use Mibew\EventDispatcher\Events;
use Symfony\Component\Routing\RouteCollection; use Symfony\Component\Routing\Router as BaseRouter;
use Symfony\Component\Routing\Matcher\RequestMatcherInterface;
use Symfony\Component\Routing\Matcher\UrlMatcher;
use Mibew\Routing\Generator\UrlGenerator;
use Mibew\Routing\RouteCollectionLoader;
class Router implements RouterInterface, RequestMatcherInterface class Router extends BaseRouter implements RouterInterface
{ {
/** /**
* @var UrlMatcher|null * {@inheritdoc}
*/
protected $matcher = null;
/**
* @var UrlGenerator|null
*/
protected $generator = null;
/**
* @var RequestContext
*/
protected $context;
/**
* @var RouteCollection|null
*/
protected $collection = null;
/**
* @var RouteCollectionLoader|null
*/
protected $loader = null;
/**
* @var array Router's options.
*/
protected $options = array();
/**
* Class constructor.
* *
* @param RouteLoader $loader An instance of route loader. * The only difference from
* @param RequestContext $context The context of the request. * {@link \Symfony\Component\Routing\Router::setOptions()} is in default
* values.
*/ */
public function __construct(RouteCollectionLoader $loader, RequestContext $context = null, $options = array()) public function setOptions(array $options)
{ {
$this->context = $context ? $context : new RequestContext(); $this->options = array(
$this->loader = $loader; 'cache_dir' => null,
$this->setOptions($options); 'debug' => false,
'generator_class' => 'Mibew\\Routing\\Generator\\UrlGenerator',
'generator_base_class' => 'Mibew\\Routing\\Generator\\UrlGenerator',
'generator_dumper_class' => 'Symfony\\Component\\Routing\\Generator\\Dumper\\PhpGeneratorDumper',
'generator_cache_class' => 'MibewUrlGenerator',
'matcher_class' => 'Symfony\\Component\\Routing\\Matcher\\UrlMatcher',
'matcher_base_class' => 'Symfony\\Component\\Routing\\Matcher\\UrlMatcher',
'matcher_dumper_class' => 'Symfony\\Component\\Routing\\Matcher\\Dumper\\PhpMatcherDumper',
'matcher_cache_class' => 'MibewUrlMatcher',
'resource_type' => null,
'strict_requirements' => true,
);
// Check option names and live merge, if errors are encountered
// Exception will be thrown
$invalid = array();
foreach ($options as $key => $value) {
if (array_key_exists($key, $this->options)) {
$this->options[$key] = $value;
} else {
$invalid[] = $key;
}
}
if ($invalid) {
throw new \InvalidArgumentException(sprintf(
'The Router does not support the following options: "%s".',
implode('", "', $invalid)
));
}
} }
/** /**
@ -77,21 +73,19 @@ class Router implements RouterInterface, RequestMatcherInterface
*/ */
public function getRouteCollection() public function getRouteCollection()
{ {
if (is_null($this->collection)) { if (null === $this->collection) {
$this->collection = $this->loader->load($this->getOption('route_collection')); $collection = $this->loader->load($this->resource, $this->options['resource_type']);
// Add an ability for plugins to alter routes list
$arguments = array('routes' => $collection);
EventDispatcher::getInstance()->triggerEvent(Events::ROUTES_ALTER, $arguments);
$this->collection = $arguments['routes'];
} }
return $this->collection; return $this->collection;
} }
/**
* {@inheritdoc}
*/
public function generate($name, $parameters = array(), $referenceType = self::ABSOLUTE_PATH)
{
return $this->getGenerator()->generate($name, $parameters, $referenceType);
}
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
@ -99,137 +93,4 @@ class Router implements RouterInterface, RequestMatcherInterface
{ {
return $this->getGenerator()->generateSecure($name, $parameters, $referenceType); return $this->getGenerator()->generateSecure($name, $parameters, $referenceType);
} }
/**
* {@inheritdoc}
*/
public function getContext()
{
return $this->context;
}
/**
* {@inheritdoc}
*/
public function setContext(RequestContext $context)
{
$this->context = $context;
// Update request context in URL matcher instance
if (!is_null($this->matcher)) {
$this->matcher->setContext($context);
}
// Update request context in URL generator instance
if (!is_null($this->generator)) {
$this->generator->setContext($context);
}
}
/**
* {@inheritdoc}
*/
public function matchRequest(Request $request)
{
return $this->getMatcher()->matchRequest($request);
}
/**
* {@inheritdoc}
*/
public function match($pathinfo)
{
return $this->getMatcher()->match($pathinfo);
}
/**
* Gets the UrlMatcher instance associated with this Router.
*
* @return UrlMatcher
*/
public function getMatcher()
{
if (is_null($this->matcher)) {
$this->matcher = new UrlMatcher($this->getRouteCollection(), $this->getContext());
}
return $this->matcher;
}
/**
* Gets the UrlGenerator instance associated with this Router.
*
* @return UrlGenerator
*/
public function getGenerator()
{
if (is_null($this->generator)) {
$this->generator = new UrlGenerator($this->getRouteCollection(), $this->getContext());
}
return $this->generator;
}
/**
* Sets all router's options.
*
* @param array $options List of options to set.
* @throws \InvalidArgumentException If not supported option is found.
*/
public function setOptions($options)
{
// Reset router's options to defaults.
$this->options = array(
'route_collection' => RouteCollectionLoader::ROUTES_ALL,
);
// Update options and find invalid ones.
$invalid = array();
foreach ($options as $name => $value) {
if (array_key_exists($name, $this->options)) {
$this->options[$name] = $value;
} else {
$invalid[] = $name;
}
}
if (!empty($invalid)) {
throw new \InvalidArgumentException(sprintf(
'The Router does not support the following options: "%s".',
implode('", "', $invalid)
));
}
}
/**
* Sets router's option to the specified value.
*
* @param string $name Option name.
* @param string $value Option value.
* @throws \InvalidArgumentException If the option is not supported.
*/
public function setOption($name, $value)
{
if (!array_key_exists($name, $this->options)) {
throw new \InvalidArgumentException(sprintf('The Router does not support "%s" option.', $name));
}
$this->options[$name] = $value;
}
/**
* Gets router's option by its name.
*
* @param string $name Option name.
* @return mixed Option value.
* @throws \InvalidArgumentException If the option is not supported.
*/
public function getOption($name)
{
if (!array_key_exists($name, $this->options)) {
throw new \InvalidArgumentException(sprintf('The Router does not support "%s" option.', $name));
}
return $this->options[$name];
}
} }