Elgg  Version master
Router.php
Go to the documentation of this file.
1 <?php
2 
3 namespace Elgg;
4 
17 
26 class Router {
27 
28  use Profilable;
29 
40  public function __construct(
41  protected EventsService $events,
42  protected RouteCollection $routes,
43  protected UrlMatcher $matcher,
44  protected HandlersService $handlers,
45  protected ResponseFactory $response,
46  protected Plugins $plugins
47  ) {
48  }
49 
60  public function route(HttpRequest $request): bool {
61  $this->beginTimer(['build page']);
62 
63  $request->validate();
64 
65  $response = $this->getResponse($request);
66 
67  if ($this->response->getSentResponse()) {
68  return true;
69  }
70 
71  $this->response->respond($response);
72 
73  return headers_sent();
74  }
75 
84  public function getResponse(HttpRequest $request) {
85  $response = $this->prepareResponse($request);
86 
87  if (!$response instanceof ResponseBuilder) {
88  throw new PageNotFoundException();
89  }
90 
91  if ($request->getFirstUrlSegment() == 'action') {
92  if ($response->getForwardURL() === null) {
93  $response->setForwardURL(REFERRER);
94  }
95  }
96 
97  return $response;
98  }
99 
109  protected function prepareResponse(HttpRequest $request) {
110 
111  $segments = $request->getUrlSegments();
112  $path = '/' . implode('/', $segments);
113 
114  try {
115  $parameters = $this->matcher->match($path);
116 
117  $resource = (string) elgg_extract('_resource', $parameters);
118  unset($parameters['_resource']);
119 
120  $handler = elgg_extract('_handler', $parameters);
121  unset($parameters['_handler']);
122 
123  $controller = elgg_extract('_controller', $parameters);
124  unset($parameters['_controller']);
125 
126  $file = elgg_extract('_file', $parameters);
127  unset($parameters['_file']);
128 
129  $deprecated = elgg_extract('_deprecated', $parameters, '');
130  unset($parameters['_deprecated']);
131 
132  $middleware = elgg_extract('_middleware', $parameters, []);
133  unset($parameters['_middleware']);
134 
135  $required_plugins = (array) elgg_extract('_required_plugins', $parameters, []);
136  unset($parameters['_required_plugins']);
137 
138  unset($parameters['_detect_page_owner']);
139 
140  foreach ($required_plugins as $plugin_id) {
141  if (!$this->plugins->isActive($plugin_id)) {
142  throw new PageNotFoundException();
143  }
144  }
145 
146  $route = $this->routes->get($parameters['_route']);
147  $route->setMatchedParameters($parameters);
148  $request->setRoute($route);
149 
150  $envelope = new \Elgg\Request(elgg(), $request);
151  $parameters['request'] = $envelope;
152 
153  if (!empty($deprecated)) {
154  elgg_deprecated_notice("The route \"{$route->getName()}\" has been deprecated.", $deprecated);
155  }
156 
157  foreach ($middleware as $callable) {
158  $result = $this->handlers->call($callable, $envelope, null);
159  if ($result[1] instanceof ResponseBuilder) {
160  return $result[1];
161  }
162  }
163 
164  if ($handler) {
165  return $this->getResponseFromHandler($handler, $envelope);
166  } elseif ($controller) {
167  $result = $this->handlers->call($controller, $envelope, null);
168  if ($result[1] instanceof ResponseBuilder) {
169  return $result[1];
170  }
171  } elseif ($file) {
172  return $this->getResponseFromFile($file, $envelope);
173  }
174 
175  $output = elgg_view_resource($resource, $parameters);
176  return elgg_ok_response($output);
177  } catch (ResourceNotFoundException $ex) {
178  throw new PageNotFoundException();
179  } catch (MethodNotAllowedException $ex) {
180  throw new BadRequestException();
181  }
182  }
183 
193  if (!is_callable($handler)) {
194  return null;
195  }
196 
197  $path = trim($request->getPath(), '/');
198  $segments = explode('/', $path);
199  $identifier = array_shift($segments) ?: '';
200 
201  ob_start();
202  try {
203  $response = call_user_func($handler, $segments, $identifier, $request);
204  } catch (\Exception $ex) {
205  ob_end_clean();
206  throw $ex;
207  }
208 
209  $output = ob_get_clean();
210 
211  if ($response instanceof ResponseBuilder) {
212  return $response;
213  } else if ($response === false) {
214  return null;
215  }
216 
217  return elgg_ok_response($output);
218  }
219 
229  protected function getResponseFromFile(string $file, \Elgg\Request $request) {
230  if (!is_file($file)) {
231  throw new PageNotFoundException(elgg_echo('actionnotfound', [$request->getPath()]), ELGG_HTTP_NOT_IMPLEMENTED);
232  }
233 
234  ob_start();
235 
236  try {
237  $response = include $file;
238  } catch (\Exception $ex) {
239  ob_get_clean();
240  throw $ex;
241  }
242 
243  $output = ob_get_clean();
244 
245  if ($response instanceof ResponseBuilder) {
246  return $response;
247  }
248 
249  return elgg_ok_response($output);
250  }
251 
260  public function allowRewrite(HttpRequest $request) {
261  $segments = $request->getUrlSegments();
262  if (!empty($segments)) {
263  $identifier = array_shift($segments);
264  } else {
265  $identifier = '';
266  }
267 
268  $old = [
269  'identifier' => $identifier,
270  'segments' => $segments,
271  ];
272  $new = $this->events->triggerResults('route:rewrite', $identifier, $old, $old);
273  if ($new === $old) {
274  return $request;
275  }
276 
277  if (!isset($new['identifier']) ||
278  !isset($new['segments']) ||
279  !is_string($new['identifier']) ||
280  !is_array($new['segments'])
281  ) {
282  throw new RuntimeException('rewrite_path handler returned invalid route data.');
283  }
284 
285  // rewrite request
286  $segments = $new['segments'];
287  array_unshift($segments, $new['identifier']);
288 
289  return $request->setUrlSegments($segments);
290  }
291 }
trait Profilable
Make an object accept a timer.
Definition: Profilable.php:12
elgg
Definition: install.js:27
HTTP response service.
HTTP response builder interface.
route(HttpRequest $request)
Routes the request to a registered page handler.
Definition: Router.php:60
Exception thrown if an error which can only be found on runtime occurs.
elgg_deprecated_notice(string $msg, string $dep_version)
Log a notice about deprecated use of a function, view, etc.
Definition: elgglib.php:115
const ELGG_HTTP_NOT_IMPLEMENTED
Definition: constants.php:92
Helpers for providing callable-based APIs.
UrlMatcher Wrapper.
Definition: UrlMatcher.php:8
elgg_ok_response($content= '', string|array $message= '', string $forward_url=null, int $status_code=ELGG_HTTP_OK)
Prepares a successful response to be returned by a page or an action handler.
prepareResponse(HttpRequest $request)
Prepare response.
Definition: Router.php:109
$response
Definition: content.php:10
$request
Definition: livesearch.php:12
elgg_echo(string $message_key, array $args=[], string $language= '')
Elgg language module Functions to manage language and translations.
Definition: languages.php:17
Events service.
Delegates requests to controllers based on the registered configuration.
Definition: Router.php:26
$resource
elgg_view_resource(string $name, array $vars=[])
Render a resource view.
Definition: views.php:307
if($item instanceof\ElggEntity) elseif($item instanceof\ElggRiverItem) elseif($item instanceof\ElggRelationship) elseif(is_callable([$item, 'getType']))
Definition: item.php:48
$plugin_id
Remove all user and plugin settings from the give plugin ID.
Definition: remove.php:8
elgg_extract($key, $array, $default=null, bool $strict=true)
Checks for $array[$key] and returns its value if it exists, else returns $default.
Definition: elgglib.php:254
$path
Definition: details.php:70
getResponseFromHandler($handler,\Elgg\Request $request)
Get response from handler function.
Definition: Router.php:192
Thrown when page is not accessible.
const REFERRER
Used in calls to forward() to specify the browser should be redirected to the referring page...
Definition: constants.php:37
allowRewrite(HttpRequest $request)
Filter a request through the &#39;route:rewrite&#39; event.
Definition: Router.php:260
RouteCollection Wrapper.
Thrown when request is malformatted.
beginTimer(array $keys)
Start the timer (when enabled)
Definition: Profilable.php:43
Request container.
Definition: Request.php:12
$segments
Definition: admin.php:13
$handler
Definition: add.php:7
Persistent, installation-wide key-value storage.
Definition: Plugins.php:28
getResponse(HttpRequest $request)
Build a response.
Definition: Router.php:84
__construct(protected EventsService $events, protected RouteCollection $routes, protected UrlMatcher $matcher, protected HandlersService $handlers, protected ResponseFactory $response, protected Plugins $plugins)
Constructor.
Definition: Router.php:40
$output
Definition: download.php:9
getResponseFromFile(string $file,\Elgg\Request $request)
Get response from file.
Definition: Router.php:229