]> git.mxchange.org Git - friendica.git/blob - src/App/Router.php
1bdfdcab1d0fac735d338c8fba7baa0e7def8dd6
[friendica.git] / src / App / Router.php
1 <?php
2
3 namespace Friendica\App;
4
5
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;
12
13 /**
14  * Wrapper for FastRoute\Router
15  *
16  * This wrapper only makes use of a subset of the router features, mainly parses a route rule to return the relevant
17  * module class.
18  *
19  * Actual routes are defined in App->collectRoutes.
20  *
21  * @package Friendica\App
22  */
23 class Router
24 {
25         const POST = 'POST';
26         const GET  = 'GET';
27
28         const ALLOWED_METHODS = [
29                 self::POST,
30                 self::GET,
31         ];
32
33         /** @var RouteCollector */
34         protected $routeCollector;
35
36         /**
37          * @var string The HTTP method
38          */
39         private $httpMethod;
40
41         /**
42          * @param array $server The $_SERVER variable
43          * @param RouteCollector|null $routeCollector Optional the loaded Route collector
44          */
45         public function __construct(array $server, RouteCollector $routeCollector = null)
46         {
47                 $httpMethod = $server['REQUEST_METHOD'] ?? self::GET;
48                 $this->httpMethod = in_array($httpMethod, self::ALLOWED_METHODS) ? $httpMethod : self::GET;
49
50                 $this->routeCollector = isset($routeCollector) ?
51                         $routeCollector :
52                         new RouteCollector(new Std(), new GroupCountBased());
53         }
54
55         /**
56          * @param array $routes The routes to add to the Router
57          *
58          * @return self The router instance with the loaded routes
59          *
60          * @throws InternalServerErrorException In case of invalid configs
61          */
62         public function addRoutes(array $routes)
63         {
64                 $routeCollector = (isset($this->routeCollector) ?
65                         $this->routeCollector :
66                         new RouteCollector(new Std(), new GroupCountBased()));
67
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]);
73                         } else {
74                                 throw new InternalServerErrorException("Wrong route config for route '" . print_r($route, true) . "'");
75                         }
76                 }
77
78                 $this->routeCollector = $routeCollector;
79
80                 return $this;
81         }
82
83         /**
84          * Adds a group of routes to a given group
85          *
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
89          */
90         private function addGroup(string $groupRoute, array $routes, RouteCollector $routeCollector)
91         {
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]);
98                                 }else {
99                                         throw new InternalServerErrorException("Wrong route config for route '" . print_r($route, true) . "'");
100                                 }
101                         }
102                 });
103         }
104
105         /**
106          * Returns true in case the config is a group config
107          *
108          * @param array $config
109          *
110          * @return bool
111          */
112         private function isGroup(array $config)
113         {
114                 return
115                         is_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]);
121         }
122
123         /**
124          * Returns true in case the config is a route config
125          *
126          * @param array $config
127          *
128          * @return bool
129          */
130         private function isRoute(array $config)
131         {
132                 return
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]));
141         }
142
143         /**
144          * The current route collector
145          *
146          * @return RouteCollector|null
147          */
148         public function getRouteCollector()
149         {
150                 return $this->routeCollector;
151         }
152
153         /**
154          * Returns the relevant module class name for the given page URI or NULL if no route rule matched.
155          *
156          * @param string $cmd The path component of the request URL without the query string
157          *
158          * @return string|null A Friendica\BaseModule-extending class name if a route rule matched
159          */
160         public function getModuleClass($cmd)
161         {
162                 // Add routes from addons
163                 Hook::callAll('route_collection', $this->routeCollector);
164
165                 $cmd = '/' . ltrim($cmd, '/');
166
167                 $dispatcher = new \FastRoute\Dispatcher\GroupCountBased($this->routeCollector->getData());
168
169                 $moduleClass = null;
170
171                 $routeInfo  = $dispatcher->dispatch($this->httpMethod, $cmd);
172                 if ($routeInfo[0] === Dispatcher::FOUND) {
173                         $moduleClass = $routeInfo[1];
174                 }
175
176                 return $moduleClass;
177         }
178 }