3 namespace Friendica\App;
6 use FastRoute\DataGenerator\GroupCountBased;
7 use FastRoute\Dispatcher;
8 use FastRoute\RouteCollector;
9 use FastRoute\RouteParser\Std;
10 use Friendica\Core\Hook;
11 use Friendica\Network\HTTPException\InternalServerErrorException;
14 * Wrapper for FastRoute\Router
16 * This wrapper only makes use of a subset of the router features, mainly parses a route rule to return the relevant
19 * Actual routes are defined in App->collectRoutes.
21 * @package Friendica\App
28 const ALLOWED_METHODS = [
33 /** @var RouteCollector */
34 protected $routeCollector;
37 * @var string The HTTP method
42 * @param array $server The $_SERVER variable
43 * @param RouteCollector|null $routeCollector Optional the loaded Route collector
45 public function __construct(array $server, RouteCollector $routeCollector = null)
47 $httpMethod = $server['REQUEST_METHOD'] ?? self::GET;
48 $this->httpMethod = in_array($httpMethod, self::ALLOWED_METHODS) ? $httpMethod : self::GET;
50 $this->routeCollector = isset($routeCollector) ?
52 new RouteCollector(new Std(), new GroupCountBased());
56 * @param array $routes The routes to add to the Router
58 * @return self The router instance with the loaded routes
60 * @throws InternalServerErrorException In case of invalid configs
62 public function addRoutes(array $routes)
64 $routeCollector = (isset($this->routeCollector) ?
65 $this->routeCollector :
66 new RouteCollector(new Std(), new GroupCountBased()));
68 foreach ($routes as $route => $config) {
69 if ($this->isGroup($config)) {
70 $this->addGroup($route, $config, $routeCollector);
71 } elseif ($this->isRoute($config)) {
72 $routeCollector->addRoute($config[1], $route, $config[0]);
74 throw new InternalServerErrorException("Wrong route config for route '" . print_r($route, true) . "'");
78 $this->routeCollector = $routeCollector;
84 * Adds a group of routes to a given group
86 * @param string $groupRoute The route of the group
87 * @param array $routes The routes of the group
88 * @param RouteCollector $routeCollector The route collector to add this group
90 private function addGroup(string $groupRoute, array $routes, RouteCollector $routeCollector)
92 $routeCollector->addGroup($groupRoute, function (RouteCollector $routeCollector) use ($routes) {
93 foreach ($routes as $route => $config) {
94 if ($this->isGroup($config)) {
95 $this->addGroup($route, $config, $routeCollector);
96 } elseif ($this->isRoute($config)) {
97 $routeCollector->addRoute($config[1], $route, $config[0]);
99 throw new InternalServerErrorException("Wrong route config for route '" . print_r($route, true) . "'");
106 * Returns true in case the config is a group config
108 * @param array $config
112 private function isGroup(array $config)
116 is_string(array_keys($config)[0]) &&
117 // This entry should NOT be a BaseModule
118 (substr(array_keys($config)[0], 0, strlen('Friendica\Module')) !== 'Friendica\Module') &&
119 // The second argument is an array (another routes)
120 is_array(array_values($config)[0]);
124 * Returns true in case the config is a route config
126 * @param array $config
130 private function isRoute(array $config)
133 // The config array should at least have one entry
134 !empty($config[0]) &&
135 // This entry should be a BaseModule
136 (substr($config[0], 0, strlen('Friendica\Module')) === 'Friendica\Module') &&
137 // Either there is no other argument
138 (empty($config[1]) ||
139 // Or the second argument is an array (HTTP-Methods)
140 is_array($config[1]));
144 * The current route collector
146 * @return RouteCollector|null
148 public function getRouteCollector()
150 return $this->routeCollector;
154 * Returns the relevant module class name for the given page URI or NULL if no route rule matched.
156 * @param string $cmd The path component of the request URL without the query string
158 * @return string|null A Friendica\BaseModule-extending class name if a route rule matched
160 public function getModuleClass($cmd)
162 // Add routes from addons
163 Hook::callAll('route_collection', $this->routeCollector);
165 $cmd = '/' . ltrim($cmd, '/');
167 $dispatcher = new \FastRoute\Dispatcher\GroupCountBased($this->routeCollector->getData());
171 $routeInfo = $dispatcher->dispatch($this->httpMethod, $cmd);
172 if ($routeInfo[0] === Dispatcher::FOUND) {
173 $moduleClass = $routeInfo[1];