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