* @package com.woltlab.wcf
* @subpackage system
* @category Community Framework
+ * @deprecated 2.2
*/
abstract class SingletonFactory {
/**
* Singletons do not support a public constructor. Override init() if
* your class needs to initialize components on creation.
*/
- public final function __construct() {
+ public function __construct() {
$this->init();
}
* @category Community Framework
*/
class ApplicationHandler extends SingletonFactory {
+ /**
+ * @var ApplicationCacheBuilder
+ */
+ protected $applicationCacheBuilder;
/**
* application cache
* @var array<array>
*/
protected $pageURLs = array();
+ /**
+ * ApplicationHandler constructor.
+ *
+ * @param ApplicationCacheBuilder $applicationCacheBuilder
+ */
+ public function __construct(ApplicationCacheBuilder $applicationCacheBuilder) {
+ $this->applicationCacheBuilder = $applicationCacheBuilder;
+
+ parent::__construct();
+ }
+
/**
* Initializes cache.
*/
protected function init() {
- $this->cache = ApplicationCacheBuilder::getInstance()->getData();
+ $this->cache = $this->applicationCacheBuilder->getData();
}
/**
* Returns the primary application.
*
- * @return \wcf\data\application\Application
+ * @return Application
*/
public function getPrimaryApplication() {
$packageID = ($this->cache['primary']) ?: PACKAGE_ID;
* Returns an application based upon it's abbreviation. Will return the
* primary application if $abbreviation equals to 'wcf'
*
- * @return \wcf\data\application\Application
+ * @param string $abbreviation package abbreviation, e.g. `wbb` for `com.woltlab.wbb`
+ * @return Application
*/
public function getApplication($abbreviation) {
if ($abbreviation == 'wcf') {
* Returns pseudo-application representing WCF used for special cases,
* e.g. cross-domain files requestable through the webserver.
*
- * @return \wcf\data\application\Application
+ * @return Application
*/
public function getWCF() {
return $this->cache['wcf'];
/**
* Returns the currently active application.
*
- * @return \wcf\data\application\Application
+ * @return Application
*/
public function getActiveApplication() {
// work-around during WCFSetup
/**
* Returns a list of dependent applications.
*
- * @return array<\wcf\data\application\Application>
+ * @return Application[]
*/
public function getDependentApplications() {
$applications = $this->getApplications();
/**
* Returns a list of all active applications.
*
- * @return array<\wcf\data\application\Application>
+ * @return Application[]
*/
public function getApplications() {
return $this->cache['application'];
/**
* Returns abbreviation for a given package id or null if application is unknown.
*
+ * @param int $packageID unique package id
* @return string
*/
public function getAbbreviation($packageID) {
*/
protected $actions = null;
+ /**
+ * @var EventListenerCacheBuilder
+ */
+ protected $eventListenerCacheBuilder;
+
/**
* registered inherit actions
* @var array
* instances of registerd actions
* @var array
*/
- protected $actionsObjects = array();
+ protected $actionsObjects = [];
/**
* instances of registered inherit actions
* @var array
*/
- protected $inheritedActionsObjects = array();
+ protected $inheritedActionsObjects = [];
/**
* instances of listener objects
- * @var array<\wcf\system\event\IEventListener>
+ * @var IEventListener[]
*/
- protected $listenerObjects = array();
+ protected $listenerObjects = [];
+
+ /**
+ * EventHandler constructor.
+ *
+ * @param EventListenerCacheBuilder $eventListenerCacheBuilder
+ */
+ public function __construct(EventListenerCacheBuilder $eventListenerCacheBuilder) {
+ $this->eventListenerCacheBuilder = $eventListenerCacheBuilder;
+
+ parent::__construct();
+ }
/**
* Loads all registered actions of the active package.
*/
protected function loadActions() {
$environment = ((class_exists('wcf\system\WCFACP', false) || class_exists('wcf\system\CLIWCF', false)) ? 'admin' : 'user');
- $cache = EventListenerCacheBuilder::getInstance()->getData();
+ $cache = $this->eventListenerCacheBuilder->getData();
if (isset($cache['actions'][$environment])) {
$this->actions = $cache['actions'][$environment];
unset($cache);
if (!is_array($this->actions)) {
- $this->actions = array();
+ $this->actions = [];
}
if (!is_array($this->inheritedActions)) {
- $this->inheritedActions = array();
+ $this->inheritedActions = [];
}
}
* @param string $className
* @param string $name
* @param array &$parameters
+ * @throws SystemException
*/
protected function executeInheritedActions($eventObj, $eventName, $className, $name, array &$parameters) {
// create objects of the actions
if (!isset($this->inheritedActionsObjects[$name]) || !is_array($this->inheritedActionsObjects[$name])) {
- $this->inheritedActionsObjects[$name] = array();
+ $this->inheritedActionsObjects[$name] = [];
// get parent classes
- $familyTree = array();
+ $familyTree = [];
$member = (is_object($eventObj) ? get_class($eventObj) : $eventObj);
while ($member != false) {
$familyTree[] = $member;
* @param mixed $eventObj
* @param string $eventName
* @param array &$parameters
+ * @throws SystemException
*/
public function fireAction($eventObj, $eventName, array &$parameters = array()) {
// get class name
if (!isset($this->actionsObjects[$name]) || !is_array($this->actionsObjects[$name])) {
if (!isset($this->actions[$name]) || !is_array($this->actions[$name])) {
// no action registered
- return false;
+ return;
}
- $this->actionsObjects[$name] = array();
+ $this->actionsObjects[$name] = [];
foreach ($this->actions[$name] as $eventListener) {
if ($eventListener->validateOptions() && $eventListener->validatePermissions()) {
if (isset($this->actionsObjects[$name][$eventListener->listenerClassName])) continue;
/**
* Generates an unique name for an action.
*
- * @param string $className
- * @param string $eventName
+ * @param string $className
+ * @param string $eventName
+ * @return string unique action name
*/
public static function generateKey($className, $eventName) {
return $eventName.'@'.$className;
* @param string $controller url controller
* @param boolean $isAcpRequest true if this is an ACP request
* @return array<string> className, controller and pageType
+ * @throws SystemException
*/
public function resolve($application, $controller, $isAcpRequest) {
// validate controller
* @package com.woltlab.wcf
* @subpackage system.request
* @category Community Framework
+ * @deprecated 2.2:2.3 Consider using \wcf\system\request\route\DynamicRequestRoute
*/
class FlexibleRoute implements IRoute {
/**
* @package com.woltlab.wcf
* @subpackage system.request
* @category Community Framework
+ * @deprecated 2.2:2.3 will be replaced with \wcf\system\request\route\IRequestRoute
*/
interface IRoute {
/**
class RequestHandler extends SingletonFactory {
/**
* active request object
- * @var \wcf\system\request\Request
+ * @var Request
*/
protected $activeRequest = null;
/**
- * @var \wcf\system\request\ControllerMap
+ * @var ApplicationHandler
*/
- protected $controllerMap = null;
+ protected $applicationHandler;
+
+ /**
+ * @var ControllerMap
+ */
+ protected $controllerMap;
/**
* true, if current domain mismatch any known domain
*/
protected $isACPRequest = false;
+ /**
+ * @var RouteHandler
+ */
+ protected $routeHandler;
+
+ /**
+ * RequestHandler constructor.
+ *
+ * @param ApplicationHandler $applicationHandler
+ * @param ControllerMap $controllerMap
+ * @param RouteHandler $routeHandler
+ */
+ public function __construct(ApplicationHandler $applicationHandler, ControllerMap $controllerMap, RouteHandler $routeHandler) {
+ $this->applicationHandler = $applicationHandler;
+ $this->controllerMap = $controllerMap;
+ $this->routeHandler = $routeHandler;
+
+ parent::__construct();
+ }
+
/**
* @see \wcf\system\SingletonFactory::init()
*/
protected function init() {
- $this->controllerMap = new ControllerMap();
-
if (isset($_SERVER['HTTP_HOST'])) {
- foreach (ApplicationHandler::getInstance()->getApplications() as $application) {
+ foreach ($this->applicationHandler->getApplications() as $application) {
if ($application->domainName == $_SERVER['HTTP_HOST']) {
$this->inRescueMode = false;
break;
// check if WCF is running as standalone
if ($this->inRescueMode() && PACKAGE_ID == 1) {
- if (ApplicationHandler::getInstance()->getWCF()->domainName == $_SERVER['HTTP_HOST']) {
+ if ($this->applicationHandler->getWCF()->domainName == $_SERVER['HTTP_HOST']) {
$this->inRescueMode = false;
}
}
/**
* Handles a http request.
- *
+ *
* @param string $application
* @param boolean $isACPRequest
+ * @throws AJAXException
+ * @throws IllegalLinkException
+ * @throws SystemException
*/
public function handle($application = 'wcf', $isACPRequest = false) {
$this->isACPRequest = $isACPRequest;
- if (!RouteHandler::getInstance()->matches()) {
+ // initialize route handler
+ $this->routeHandler->setRequestHandler($this);
+ $this->routeHandler->setDefaultRoutes();
+
+ if (!$this->routeHandler->matches()) {
if (ENABLE_DEBUG_MODE) {
throw new SystemException("Cannot handle request, no valid route provided.");
}
* Builds a new request.
*
* @param string $application
+ * @throws IllegalLinkException
*/
protected function buildRequest($application) {
try {
- $routeData = RouteHandler::getInstance()->getRouteData();
+ $routeData = $this->routeHandler->getRouteData();
// handle landing page for frontend requests
if (!$this->isACPRequest()) {
// check if accessing from the wrong domain (e.g. "www." omitted but domain was configured with)
if (!defined('WCF_RUN_MODE') || WCF_RUN_MODE != 'embedded') {
- $applicationObject = ApplicationHandler::getInstance()->getApplication($application);
+ $applicationObject = $this->applicationHandler->getApplication($application);
if ($applicationObject->domainName != $_SERVER['HTTP_HOST']) {
// build URL, e.g. http://example.net/forum/
$url = FileUtil::addTrailingSlash(RouteHandler::getProtocol() . $applicationObject->domainName . RouteHandler::getPath());
* @param array $routeData
*/
protected function handleDefaultController($application, array &$routeData) {
- if (!RouteHandler::getInstance()->isDefaultController()) {
+ if (!$this->routeHandler->isDefaultController()) {
return;
}
// resolve implicit application abbreviation for landing page controller
$landingPageApplication = $landingPage->getApplication();
- $primaryApplication = ApplicationHandler::getInstance()->getPrimaryApplication();
- $primaryApplicationAbbr = ApplicationHandler::getInstance()->getAbbreviation($primaryApplication->packageID);
+ $primaryApplication = $this->applicationHandler->getPrimaryApplication();
+ $primaryApplicationAbbr = $this->applicationHandler->getAbbreviation($primaryApplication->packageID);
if ($landingPageApplication == 'wcf') {
$landingPageApplication = $primaryApplicationAbbr;
}
}
// set default controller
- $applicationObj = WCF::getApplicationObject(ApplicationHandler::getInstance()->getApplication($application));
+ $applicationObj = WCF::getApplicationObject($this->applicationHandler->getApplication($application));
$routeData['controller'] = preg_replace('~^.*?\\\([^\\\]+)(?:Action|Form|Page)$~', '\\1', $applicationObj->getPrimaryController());
$routeData['controller'] = $this->getControllerMap()->lookup($routeData['controller']);
}
use wcf\system\application\ApplicationHandler;
use wcf\system\event\EventHandler;
use wcf\system\exception\SystemException;
+use wcf\system\request\route\DynamicRequestRoute;
use wcf\system\SingletonFactory;
use wcf\system\WCF;
use wcf\util\FileUtil;
*/
protected static $secure = null;
+ /**
+ * @var ApplicationHandler
+ */
+ protected $applicationHandler;
+
/**
* list of application abbreviation and default controller name
* @var array<string>
*/
protected $defaultControllers = null;
+ /**
+ * @var EventHandler
+ */
+ protected $eventHandler;
+
/**
* true, if default controller is used (support for custom landing page)
* @var boolean
*/
protected $isDefaultController = false;
+ /**
+ * @var RequestHandler
+ */
+ protected $requestHandler;
+
/**
* list of available routes
- * @var array<\wcf\system\request\IRoute>
+ * @var IRoute[]
*/
protected $routes = array();
protected $routeData = null;
/**
- * @see \wcf\system\SingletonFactory::init()
+ * RouteHandler constructor.
+ *
+ * @param ApplicationHandler $applicationHandler
+ * @param EventHandler $eventHandler
+ */
+ public function __construct(ApplicationHandler $applicationHandler, EventHandler $eventHandler) {
+ $this->applicationHandler = $applicationHandler;
+ $this->eventHandler = $eventHandler;
+
+ parent::__construct();
+ }
+
+ /**
+ * Sets default routes.
*/
- protected function init() {
- $this->addDefaultRoutes();
+ public function setDefaultRoutes() {
+ /** @var \wcf\system\request\route\DynamicRequestRoute $route */
+ $route = WCF::getDIContainer()->make(DynamicRequestRoute::class);
+ $route->setIsACP(true);
+ $this->addRoute($route);
+
+ $route = WCF::getDIContainer()->make(DynamicRequestRoute::class);
+ $route->setIsACP(false);
+ $this->addRoute($route);
// fire event
- EventHandler::getInstance()->fireAction($this, 'didInit');
+ $this->eventHandler->fireAction($this, 'didInit');
}
/**
- * Adds default routes.
+ * Sets the required request handler, setter function to avoid circular dependencies.
+ *
+ * @param RequestHandler $requestHandler
*/
- protected function addDefaultRoutes() {
- $acpRoute = new FlexibleRoute(true);
- $this->addRoute($acpRoute);
-
- if (URL_LEGACY_MODE) {
- $defaultRoute = new Route('default');
- $defaultRoute->setSchema('/{controller}/{id}');
- $defaultRoute->setParameterOption('controller', null, null, true);
- $defaultRoute->setParameterOption('id', null, '\d+', true);
- $this->addRoute($defaultRoute);
- }
- else {
- $defaultRoute = new FlexibleRoute(false);
- $this->addRoute($defaultRoute);
- }
+ public function setRequestHandler(RequestHandler $requestHandler) {
+ $this->requestHandler = $requestHandler;
}
/**
* Adds a new route to the beginning of all routes.
*
- * @param \wcf\system\request\IRoute $route
+ * @param IRoute $route
*/
public function addRoute(IRoute $route) {
array_unshift($this->routes, $route);
/**
* Returns all registered routes.
*
- * @return array<\wcf\system\request\IRoute>
+ * @return IRoute[]
**/
public function getRoutes() {
return $this->routes;
*/
public function matches() {
foreach ($this->routes as $route) {
- if (RequestHandler::getInstance()->isACPRequest() != $route->isACP()) {
+ if ($this->requestHandler->isACPRequest() != $route->isACP()) {
continue;
}
* @param array $components
* @param boolean $isACP
* @return string
+ * @throws SystemException
*/
public function buildRoute(array $components, $isACP = null) {
- if ($isACP === null) $isACP = RequestHandler::getInstance()->isACPRequest();
+ if ($isACP === null) $isACP = $this->requestHandler->isACPRequest();
foreach ($this->routes as $route) {
if ($isACP != $route->isACP()) {
public static function getPathInfo() {
if (self::$pathInfo === null) {
self::$pathInfo = '';
+ $requestHandler = WCF::getDIContainer()->get(RequestHandler::class);
- if (!URL_LEGACY_MODE || RequestHandler::getInstance()->isACPRequest()) {
+ if (!URL_LEGACY_MODE || $requestHandler->isACPRequest()) {
// WCF 2.1: ?Foo/Bar/
if (!empty($_SERVER['QUERY_STRING'])) {
// don't use parse_str as it replaces dots with underscores
}
// WCF 2.0: index.php/Foo/Bar/
- if ((URL_LEGACY_MODE && !RequestHandler::getInstance()->isACPRequest()) || (RequestHandler::getInstance()->isACPRequest() && empty(self::$pathInfo))) {
+ if ((URL_LEGACY_MODE && !$requestHandler->isACPRequest()) || ($requestHandler->isACPRequest() && empty(self::$pathInfo))) {
if (isset($_SERVER['PATH_INFO'])) {
self::$pathInfo = $_SERVER['PATH_INFO'];
}
if ($this->defaultControllers === null) {
$this->defaultControllers = array();
- foreach (ApplicationHandler::getInstance()->getApplications() as $application) {
+ foreach ($this->applicationHandler->getApplications() as $application) {
$app = WCF::getApplicationObject($application);
if (!$app) {
* @package com.woltlab.wcf
* @subpackage system.request
* @category Community Framework
+ * @deprecated 2.2:2.3 Consider using \wcf\system\request\route\StaticRequestRoute
*/
class StaticRoute extends FlexibleRoute {
/**
--- /dev/null
+<?php
+namespace wcf\system\request\route;
+
+use wcf\system\application\ApplicationHandler;
+use wcf\system\menu\page\PageMenu;
+use wcf\system\request\RequestHandler;
+use wcf\system\request\RouteHandler;
+
+class DynamicRequestRoute implements IRequestRoute {
+ /**
+ * @var \wcf\system\application\ApplicationHandler
+ */
+ protected $applicationHandler;
+
+ /**
+ * schema for outgoing links
+ * @var array<array>
+ */
+ protected $buildSchema = [];
+
+ /**
+ * route is restricted to ACP
+ * @var boolean
+ */
+ protected $isACP = false;
+
+ /**
+ * @var \wcf\system\menu\page\PageMenu;
+ */
+ protected $pageMenu;
+
+ /**
+ * pattern for incoming requests
+ * @var string
+ */
+ protected $pattern = '';
+
+ /**
+ * primary application's abbreviation (e.g. "wbb")
+ * @var string
+ */
+ protected $primaryApplication = '';
+
+ /**
+ * @var \wcf\system\request\RequestHandler
+ */
+ protected $requestHandler;
+
+ /**
+ * list of required components
+ * @var array<string>
+ */
+ protected $requireComponents = [];
+
+ /**
+ * parsed request data
+ * @var array<mixed>
+ */
+ protected $routeData = [];
+
+ /**
+ * @var \wcf\system\request\RouteHandler;
+ */
+ protected $routeHandler;
+
+ /**
+ * DynamicRequestRoute constructor.
+ *
+ * @param \wcf\system\application\ApplicationHandler $applicationHandler
+ * @param \wcf\system\menu\page\PageMenu $pageMenu
+ * @param \wcf\system\request\RequestHandler $requestHandler
+ * @param \wcf\system\request\RouteHandler $routeHandler
+ */
+ public function __construct(ApplicationHandler $applicationHandler, PageMenu $pageMenu, RequestHandler $requestHandler, RouteHandler $routeHandler) {
+ $this->applicationHandler = $applicationHandler;
+ $this->pageMenu = $pageMenu;
+ $this->requestHandler = $requestHandler;
+ $this->routeHandler = $routeHandler;
+
+ $this->init();
+ }
+
+ /**
+ * Sets default routing information.
+ */
+ protected function init() {
+ $this->setPattern('~
+ /?
+ (?:
+ (?P<controller>[A-Za-z0-9\-]+)
+ (?:
+ /
+ (?P<id>\d+)
+ (?:
+ -
+ (?P<title>[^/]+)
+ )?
+ )?
+ )?
+ ~x');
+ $this->setBuildSchema('/{controller}/{id}-{title}/');
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function setIsACP($isACP) {
+ $this->isACP = $isACP;
+ }
+
+ /**
+ * Sets the build schema used to build outgoing links.
+ *
+ * @param string $buildSchema
+ */
+ public function setBuildSchema($buildSchema) {
+ $this->buildSchema = [];
+
+ $buildSchema = ltrim($buildSchema, '/');
+ $components = preg_split('~({(?:[a-z]+)})~', $buildSchema, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
+ $delimiters = ['/', '-', '.', '_'];
+
+ foreach ($components as $component) {
+ $type = 'component';
+ if (preg_match('~{([a-z]+)}~', $component, $matches)) {
+ $component = $matches[1];
+ }
+ else {
+ $type = 'separator';
+ }
+
+ $this->buildSchema[] = [
+ 'type' => $type,
+ 'value' => $component
+ ];
+ }
+ }
+
+ /**
+ * Sets the route pattern used to evaluate an incoming request.
+ *
+ * @param string $pattern
+ */
+ public function setPattern($pattern) {
+ $this->pattern = $pattern;
+ }
+
+ /**
+ * Sets the list of required components.
+ *
+ * @param array<string> $requiredComponents
+ */
+ public function setRequiredComponents(array $requiredComponents) {
+ $this->requireComponents = $requiredComponents;
+ }
+
+ /**
+ * @see IRoute::buildLink()
+ */
+ public function buildLink(array $components) {
+ $application = (isset($components['application'])) ? $components['application'] : null;
+
+ // drop application component to avoid being appended as query string
+ unset($components['application']);
+
+ // handle default values for controller
+ $useBuildSchema = true;
+ if (count($components) == 1 && isset($components['controller'])) {
+ $ignoreController = false;
+
+ if (!RequestHandler::getInstance()->isACPRequest()) {
+ $landingPage = PageMenu::getInstance()->getLandingPage();
+ if ($this->primaryApplication === '') {
+ $primaryApplication = ApplicationHandler::getInstance()->getPrimaryApplication();
+ $this->primaryApplication = ApplicationHandler::getInstance()->getAbbreviation($primaryApplication->packageID);
+ }
+
+ // check if this is the default controller
+ if (strcasecmp(RouteHandler::getInstance()->getDefaultController($application), $components['controller']) === 0) {
+ // check if this matches the primary application
+ if ($this->primaryApplication === $application) {
+ if (strcasecmp($landingPage->getController(), $components['controller']) === 0) {
+ // skip controller if it matches the default controller
+ $ignoreController = true;
+ }
+ }
+ else {
+ // skip default controller
+ $ignoreController = true;
+ }
+ }
+ else if (strcasecmp($landingPage->getController(), $components['controller']) === 0) {
+ // landing page
+ $ignoreController = true;
+ }
+ }
+
+ // drops controller from route
+ if ($ignoreController) {
+ $useBuildSchema = false;
+
+ // unset the controller, since it would otherwise be added with http_build_query()
+ unset($components['controller']);
+ }
+ }
+
+ return $this->buildRoute($components, $application, $useBuildSchema);
+ }
+
+ /**
+ * Builds the actual link, the parameter $useBuildSchema can be set to false for
+ * empty routes, e.g. for the default page.
+ *
+ * @param array $components
+ * @param string $application
+ * @param boolean $useBuildSchema
+ * @return string
+ */
+ protected function buildRoute(array $components, $application, $useBuildSchema) {
+ $link = '';
+
+ if ($useBuildSchema) {
+ $lastSeparator = null;
+ $skipToLastSeparator = false;
+ foreach ($this->buildSchema as $component) {
+ $value = $component['value'];
+
+ if ($component['type'] === 'separator') {
+ $lastSeparator = $value;
+ }
+ else if ($skipToLastSeparator === false) {
+ // routes are build from left-to-right
+ if (empty($components[$value])) {
+ $skipToLastSeparator = true;
+
+ // drop empty components to avoid them being appended as query string argument
+ unset($components[$value]);
+
+ continue;
+ }
+
+ if ($lastSeparator !== null) {
+ $link .= $lastSeparator;
+ $lastSeparator = null;
+ }
+
+ // handle controller names
+ if ($value === 'controller') {
+ $components[$value] = $this->getControllerName($application, $components[$value]);
+ }
+
+ $link .= $components[$value];
+ unset($components[$value]);
+ }
+ }
+
+ if (!empty($link) && $lastSeparator !== null) {
+ $link .= $lastSeparator;
+ }
+ }
+
+ if ($this->isACP || !URL_OMIT_INDEX_PHP) {
+ if (!empty($link)) {
+ $link = 'index.php?' . $link;
+ }
+ }
+
+ if (!empty($components)) {
+ if (strpos($link, '?') === false) $link .= '?';
+ else $link .= '&';
+
+ $link .= http_build_query($components, '', '&');
+ }
+
+ return $link;
+ }
+
+ /**
+ * @see IRoute::canHandle()
+ */
+ public function canHandle(array $components) {
+ if (!empty($this->requireComponents)) {
+ foreach ($this->requireComponents as $component => $pattern) {
+ if (empty($components[$component])) {
+ return false;
+ }
+
+ if ($pattern && !preg_match($pattern, $components[$component])) {
+ return false;
+ }
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * @see IRoute::getRouteData()
+ */
+ public function getRouteData() {
+ return $this->routeData;
+ }
+
+ /**
+ * @see IRoute::isACP()
+ */
+ public function isACP() {
+ return $this->isACP;
+ }
+
+ /**
+ * @see IRoute::matches()
+ */
+ public function matches($requestURL) {
+ if (preg_match($this->pattern, $requestURL, $matches)) {
+ foreach ($matches as $key => $value) {
+ if (!is_numeric($key)) {
+ $this->routeData[$key] = $value;
+ }
+ }
+
+ $this->routeData['isDefaultController'] = (!isset($this->routeData['controller']));
+
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Returns the transformed controller name.
+ *
+ * @param string $application
+ * @param string $controller
+ * @return string
+ */
+ protected function getControllerName($application, $controller) {
+ return RequestHandler::getInstance()->getControllerMap()->lookup($controller);
+ }
+}
--- /dev/null
+<?php
+namespace wcf\system\request\route;
+use wcf\system\request\IRoute;
+
+/**
+ * Default interface for route implementations.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2015 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf
+ * @subpackage system.request
+ * @category Community Framework
+ */
+interface IRequestRoute extends IRoute {
+ /**
+ * Configures this route to handle either ACP or frontend requests.
+ *
+ * @param boolean $isACP true if route handles ACP requests
+ */
+ public function setIsACP($isACP);
+}
--- /dev/null
+<?php
+namespace wcf\system\request\route;
+use wcf\system\application\ApplicationHandler;
+use wcf\system\menu\page\PageMenu;
+use wcf\system\request\ControllerMap;
+use wcf\system\request\RequestHandler;
+use wcf\system\request\RouteHandler;
+
+/**
+ * Static route implementation to resolve HTTP requests, handling a single controller.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2015 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf
+ * @subpackage system.request
+ * @category Community Framework
+ */
+class StaticRequestRoute extends DynamicRequestRoute {
+ /**
+ * @var \wcf\system\request\ControllerMap
+ */
+ protected $controllerMap;
+
+ /**
+ * static application identifier
+ * @var string
+ */
+ protected $staticApplication = '';
+
+ /**
+ * static controller name, not the FQN
+ * @var string
+ */
+ protected $staticController = '';
+
+ /**
+ * StaticRequestRoute constructor.
+ *
+ * @param \wcf\system\application\ApplicationHandler $applicationHandler
+ * @param \wcf\system\request\ControllerMap $controllerMap
+ * @param \wcf\system\menu\page\PageMenu $pageMenu
+ * @param \wcf\system\request\RequestHandler $requestHandler
+ * @param \wcf\system\request\RouteHandler $routeHandler
+ */
+ public function __construct(ApplicationHandler $applicationHandler, ControllerMap $controllerMap, PageMenu $pageMenu, RequestHandler $requestHandler, RouteHandler $routeHandler) {
+ parent::__construct($applicationHandler, $pageMenu, $requestHandler, $routeHandler);
+
+ $this->controllerMap = $controllerMap;
+ }
+
+ /**
+ * @see \wcf\system\request\route\IRequestRoute::setIsACP()
+ */
+ public function setIsACP($isACP) {
+ // static routes are disallowed for ACP
+ parent::setIsACP(false);
+ }
+
+ /**
+ * Sets the static controller for this route.
+ *
+ * @param string $application
+ * @param string $controller
+ */
+ public function setStaticController($application, $controller) {
+ $this->staticApplication = $application;
+ $this->staticController = $controller;
+
+ $this->requireComponents['controller'] = '~^' . $this->staticController . '$~';
+ }
+
+ /**
+ * @see \wcf\system\request\IRoute::buildLink()
+ */
+ public function buildLink(array $components) {
+ // static routes don't have these components
+ unset($components['application']);
+ unset($components['controller']);
+
+ return $this->buildRoute($components, '', true);
+ }
+
+ /**
+ * @see \wcf\system\request\IRoute::canHandle()
+ */
+ public function canHandle(array $components) {
+ if (isset($components['application']) && $components['application'] == $this->staticApplication) {
+ if (isset($components['controller']) && $components['controller'] == $this->staticController) {
+ return parent::canHandle($components);
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * @see \wcf\system\request\IRoute::matches()
+ */
+ public function matches($requestURL) {
+ if (parent::matches($requestURL)) {
+ $this->routeData['application'] = $this->staticApplication;
+ $this->routeData['controller'] = $this->controllerMap->lookup($this->staticController);
+ $this->routeData['isDefaultController'] = false;
+
+ return true;
+ }
+
+ return false;
+ }
+}