Elgg  Version 3.0
Router.php
Go to the documentation of this file.
1 <?php
2 
3 namespace Elgg;
4 
10 use Exception;
15 
26 class Router {
27 
28  use Profilable;
29 
33  protected $hooks;
34 
38  protected $routes;
39 
43  protected $matcher;
44 
48  protected $handlers;
49 
53  protected $response;
54 
64  public function __construct(
65  PluginHooksService $hooks,
66  RouteCollection $routes,
67  UrlMatcher $matcher,
68  HandlersService $handlers,
69  ResponseFactory $response
70  ) {
71  $this->hooks = $hooks;
72  $this->routes = $routes;
73  $this->matcher = $matcher;
74  $this->handlers = $handlers;
75  $this->response = $response;
76  }
77 
90  public function route(HttpRequest $request) {
91  if ($this->timer) {
92  $this->timer->begin(['build page']);
93  }
94 
95  $request->validate();
96 
97  $response = $this->getResponse($request);
98 
99  if ($this->response->getSentResponse()) {
100  return true;
101  }
102 
103  $this->response->respond($response);
104 
105  return headers_sent();
106  }
107 
117  public function getResponse(HttpRequest $request) {
118  $response = $this->prepareLegacyResponse($request);
119 
120  if (!$response instanceof ResponseBuilder) {
121  $response = $this->prepareResponse($request);
122  }
123 
124  if (!$response instanceof ResponseBuilder) {
125  throw new PageNotFoundException();
126  }
127 
128  if ($request->getFirstUrlSegment() == 'action') {
129  if ($response->getForwardURL() === null) {
130  $response->setForwardURL(REFERRER);
131  }
132  }
133 
134  return $response;
135  }
136 
146 
147  $segments = $request->getUrlSegments();
148  if (!empty($segments)) {
149  $identifier = array_shift($segments);
150  } else {
151  $identifier = '';
152  $segments = [];
153  }
154 
155  $old = [
156  'identifier' => $identifier,
157  'handler' => $identifier, // backward compatibility
158  'segments' => $segments,
159  ];
160 
161  if (!$this->hooks->hasHandler('route', $identifier) && !$this->hooks->hasHandler('route', 'all')) {
162  return null;
163  }
164 
165  elgg_deprecated_notice('"route" hook has been deprecated. Use named routes instead', '3.0');
166 
167  try {
168  ob_start();
169 
170  $result = $this->hooks->trigger('route', $identifier, $old, $old);
171 
172  $output = ob_get_clean();
173 
174  if ($result instanceof ResponseBuilder) {
175  return $result;
176  } else if ($result === false) {
177  return elgg_ok_response($output);
178  } else if ($result !== $old) {
179  elgg_log("'route' hook should not be used to rewrite routing path. Use 'route:rewrite' hook instead", 'ERROR');
180 
181  if ($identifier != $result['identifier']) {
182  $identifier = $result['identifier'];
183  } else if ($identifier != $result['handler']) {
184  $identifier = $result['handler'];
185  }
186 
187  $segments = elgg_extract('segments', $result, [], false);
188 
189  array_unshift($segments, $identifier);
190 
191  $forward_url = implode('/', $segments);
192 
194  }
195  } catch (Exception $ex) {
196  ob_end_clean();
197  throw $ex;
198  }
199  }
200 
210  protected function prepareResponse(HttpRequest $request) {
211 
212  $segments = $request->getUrlSegments();
213  $path = '/' . implode('/', $segments);
214 
215  try {
216  $parameters = $this->matcher->match($path);
217 
218  $resource = elgg_extract('_resource', $parameters);
219  unset($parameters['_resource']);
220 
221  $handler = elgg_extract('_handler', $parameters);
222  unset($parameters['_handler']);
223 
224  $controller = elgg_extract('_controller', $parameters);
225  unset($parameters['_controller']);
226 
227  $file = elgg_extract('_file', $parameters);
228  unset($parameters['_file']);
229 
230  $middleware = elgg_extract('_middleware', $parameters, []);
231  unset($parameters['_middleware']);
232 
233  $route = $this->routes->get($parameters['_route']);
234  $route->setMatchedParameters($parameters);
235  $request->setRoute($route);
236 
237  $envelope = new \Elgg\Request(elgg(), $request);
238  $parameters['request'] = $envelope;
239 
240  foreach ($middleware as $callable) {
241  $result = $this->handlers->call($callable, $envelope, null);
242  if ($result[1] instanceof ResponseBuilder) {
243  return $result[1];
244  }
245  }
246 
247  if ($handler) {
248  return $this->getResponseFromHandler($handler, $envelope);
249  } else if ($controller) {
250  $result = $this->handlers->call($controller, $envelope, null);
251  if ($result[1] instanceof ResponseBuilder) {
252  return $result[1];
253  }
254  } else if ($file) {
255  return $this->getResponseFromFile($file, $envelope);
256  } else {
257  $output = elgg_view_resource($resource, $parameters);
258  return elgg_ok_response($output);
259  }
260  } catch (ResourceNotFoundException $ex) {
261  throw new PageNotFoundException();
262  } catch (MethodNotAllowedException $ex) {
263  throw new BadRequestException();
264  }
265  }
266 
278  if (!is_callable($handler)) {
279  return null;
280  }
281 
282  $path = trim($request->getPath(), '/');
283  $segments = explode('/', $path);
284  $identifier = array_shift($segments) ? : '';
285 
286  ob_start();
287  try {
288  $response = call_user_func($handler, $segments, $identifier, $request);
289  } catch (Exception $ex) {
290  ob_end_clean();
291  throw $ex;
292  }
293 
294  $output = ob_get_clean();
295 
296  if ($response instanceof ResponseBuilder) {
297  return $response;
298  } else if ($response === false) {
299  return null;
300  }
301 
302  return elgg_ok_response($output);
303  }
304 
316  if (!is_file($file) || !is_readable($file)) {
317  $path = $request->getPath();
318  throw new PageNotFoundException(elgg_echo('actionnotfound', [$path]), ELGG_HTTP_NOT_IMPLEMENTED);
319  }
320 
321  ob_start();
322 
323  try {
324  $response = include $file;
325  } catch (\Exception $ex) {
326  ob_get_clean();
327  throw $ex;
328  }
329 
330  $output = ob_get_clean();
331 
332  if ($response instanceof ResponseBuilder) {
333  return $response;
334  }
335 
336  return elgg_ok_response($output);
337  }
338 
346  public function allowRewrite(HttpRequest $request) {
347  $segments = $request->getUrlSegments();
348  if (!empty($segments)) {
349  $identifier = array_shift($segments);
350  } else {
351  $identifier = '';
352  }
353 
354  $old = [
355  'identifier' => $identifier,
356  'segments' => $segments,
357  ];
358  $new = $this->hooks->trigger('route:rewrite', $identifier, $old, $old);
359  if ($new === $old) {
360  return $request;
361  }
362 
363  if (
364  !isset($new['identifier']) ||
365  !isset($new['segments']) ||
366  !is_string($new['identifier']) ||
367  !is_array($new['segments'])
368  ) {
369  throw new RuntimeException('rewrite_path handler returned invalid route data.');
370  }
371 
372  // rewrite request
373  $segments = $new['segments'];
374  array_unshift($segments, $new['identifier']);
375 
376  return $request->setUrlSegments($segments);
377  }
378 }
WARNING: API IN FLUX.
HTTP response builder interface.
if(!$entity->delete()) $forward_url
Definition: delete.php:30
if(!array_key_exists($filename, $text_files)) $file
route(HttpRequest $request)
Routes the request to a registered page handler.
Definition: Router.php:90
const ELGG_HTTP_NOT_IMPLEMENTED
Definition: constants.php:107
Helpers for providing callable-based APIs.
elgg_view_resource($name, array $vars=[])
Render a resource view.
Definition: views.php:423
UrlMatcher Wrapper.
Definition: UrlMatcher.php:8
prepareResponse(HttpRequest $request)
Prepare response.
Definition: Router.php:210
elgg_redirect_response($forward_url=REFERRER, $status_code=ELGG_HTTP_FOUND)
Prepare a silent redirect response to be returned by a page or an action handler. ...
Thrown when page is not accessible.
$request
Page handler for autocomplete endpoint.
Definition: livesearch.php:9
const ELGG_HTTP_PERMANENTLY_REDIRECT
Definition: constants.php:78
$path
Definition: details.php:89
$resource
catch(LoginException $e) if($request->isXhr()) $output
Definition: login.php:56
trait Profilable
Make an object accept a timer.
Definition: Profilable.php:10
elgg_echo($message_key, array $args=[], $language="")
Given a message key, returns an appropriately translated full-text string.
Definition: languages.php:21
Configuration exception.
getResponseFromHandler($handler,\Elgg\Request $request)
Get response from handler function.
Definition: Router.php:277
const REFERRER
Definition: constants.php:42
getResponseFromFile($file,\Elgg\Request $request)
Get response from file.
Definition: Router.php:315
elgg_log($message, $level=\Psr\Log\LogLevel::NOTICE)
Log a message.
Definition: elgglib.php:786
elgg_deprecated_notice($msg, $dep_version, $backtrace_level=1)
Log a notice about deprecated use of a function, view, etc.
Definition: elgglib.php:841
prepareLegacyResponse(HttpRequest $request)
Prepare legacy response by listening to "route" hook.
Definition: Router.php:145
__construct(PluginHooksService $hooks, RouteCollection $routes, UrlMatcher $matcher, HandlersService $handlers, ResponseFactory $response)
Constructor.
Definition: Router.php:64
elgg_extract($key, $array, $default=null, $strict=true)
Checks for $array[$key] and returns its value if it exists, else returns $default.
Definition: elgglib.php:1131
allowRewrite(HttpRequest $request)
Filter a request through the route:rewrite hook.
Definition: Router.php:346
elgg_ok_response($content= '', $message= '', $forward_url=null, $status_code=ELGG_HTTP_OK)
Prepares a successful response to be returned by a page or an action handler.
RouteCollection Wrapper.
Request container.
Definition: Request.php:13
$handler
Definition: add.php:7
Thrown when request is malformatted.
getResponse(HttpRequest $request)
Build a response.
Definition: Router.php:117
var elgg
Definition: elgglib.js:4