Elgg  Version 6.3
Router.php
Go to the documentation of this file.
1 <?php
2 
3 namespace Elgg;
4 
8 use Elgg\Http\Request as HttpRequest;
14 use Symfony\Component\Routing\Exception\MethodNotAllowedException;
15 use Symfony\Component\Routing\Exception\ResourceNotFoundException;
16 
25 class Router {
26 
27  use Profilable;
28 
38  public function __construct(
39  protected EventsService $events,
40  protected RouteCollection $routes,
41  protected UrlMatcher $matcher,
42  protected HandlersService $handlers,
43  protected ResponseFactory $response
44  ) {
45  }
46 
57  public function route(HttpRequest $request): bool {
58  $this->beginTimer(['build page']);
59 
60  $request->validate();
61 
62  $response = $this->getResponse($request);
63 
64  if ($this->response->getSentResponse()) {
65  return true;
66  }
67 
68  $this->response->respond($response);
69 
70  return headers_sent();
71  }
72 
81  public function getResponse(HttpRequest $request) {
82  $response = $this->prepareResponse($request);
83 
84  if (!$response instanceof ResponseBuilder) {
85  throw new PageNotFoundException();
86  }
87 
88  if ($request->getFirstUrlSegment() == 'action') {
89  if ($response->getForwardURL() === null) {
90  $response->setForwardURL(REFERRER);
91  }
92  }
93 
94  return $response;
95  }
96 
106  protected function prepareResponse(HttpRequest $request) {
107 
108  $segments = $request->getUrlSegments();
109  $path = '/' . implode('/', $segments);
110 
111  try {
112  $parameters = $this->matcher->match($path);
113 
114  $resource = (string) elgg_extract('_resource', $parameters);
115  unset($parameters['_resource']);
116 
117  $handler = elgg_extract('_handler', $parameters);
118  unset($parameters['_handler']);
119 
120  $controller = elgg_extract('_controller', $parameters);
121  unset($parameters['_controller']);
122 
123  $file = elgg_extract('_file', $parameters);
124  unset($parameters['_file']);
125 
126  $deprecated = elgg_extract('_deprecated', $parameters, '');
127  unset($parameters['_deprecated']);
128 
129  $middleware = elgg_extract('_middleware', $parameters, []);
130  unset($parameters['_middleware']);
131 
132  unset($parameters['_detect_page_owner']);
133  unset($parameters['_use_logged_in']);
134 
135  $route = $this->routes->get($parameters['_route']);
136  $route->setMatchedParameters($parameters);
137  $request->setRoute($route);
138 
139  $envelope = new \Elgg\Request(elgg(), $request);
140  $parameters['request'] = $envelope;
141 
142  if (!empty($deprecated)) {
143  elgg_deprecated_notice("The route \"{$route->getName()}\" has been deprecated.", $deprecated);
144  }
145 
146  foreach ($middleware as $callable) {
147  $result = $this->handlers->call($callable, $envelope, null);
148  if ($result[1] instanceof ResponseBuilder) {
149  return $result[1];
150  }
151  }
152 
153  if ($handler) {
154  return $this->getResponseFromHandler($handler, $envelope);
155  } elseif ($controller) {
156  $result = $this->handlers->call($controller, $envelope, null);
157  if ($result[1] instanceof ResponseBuilder) {
158  return $result[1];
159  }
160  } elseif ($file) {
161  return $this->getResponseFromFile($file, $envelope);
162  }
163 
164  $output = elgg_view_resource($resource, $parameters);
165  return elgg_ok_response($output);
166  } catch (ResourceNotFoundException $ex) {
167  throw new PageNotFoundException();
168  } catch (MethodNotAllowedException $ex) {
169  throw new BadRequestException();
170  }
171  }
172 
182  if (!is_callable($handler)) {
183  return null;
184  }
185 
186  $path = trim($request->getPath(), '/');
187  $segments = explode('/', $path);
188  $identifier = array_shift($segments) ?: '';
189 
190  ob_start();
191  try {
192  $response = call_user_func($handler, $segments, $identifier, $request);
193  } catch (\Exception $ex) {
194  ob_end_clean();
195  throw $ex;
196  }
197 
198  $output = ob_get_clean();
199 
200  if ($response instanceof ResponseBuilder) {
201  return $response;
202  } else if ($response === false) {
203  return null;
204  }
205 
206  return elgg_ok_response($output);
207  }
208 
218  protected function getResponseFromFile(string $file, \Elgg\Request $request) {
219  if (!is_file($file)) {
220  throw new PageNotFoundException(elgg_echo('actionnotfound', [$request->getPath()]), ELGG_HTTP_NOT_IMPLEMENTED);
221  }
222 
223  ob_start();
224 
225  try {
226  $response = include $file;
227  } catch (\Exception $ex) {
228  ob_get_clean();
229  throw $ex;
230  }
231 
232  $output = ob_get_clean();
233 
234  if ($response instanceof ResponseBuilder) {
235  return $response;
236  }
237 
238  return elgg_ok_response($output);
239  }
240 
249  public function allowRewrite(HttpRequest $request) {
250  $segments = $request->getUrlSegments();
251  if (!empty($segments)) {
252  $identifier = array_shift($segments);
253  } else {
254  $identifier = '';
255  }
256 
257  $old = [
258  'identifier' => $identifier,
259  'segments' => $segments,
260  ];
261  $new = $this->events->triggerResults('route:rewrite', $identifier, $old, $old);
262  if ($new === $old) {
263  return $request;
264  }
265 
266  if (!isset($new['identifier']) ||
267  !isset($new['segments']) ||
268  !is_string($new['identifier']) ||
269  !is_array($new['segments'])
270  ) {
271  throw new RuntimeException('rewrite_path handler returned invalid route data.');
272  }
273 
274  // rewrite request
275  $segments = $new['segments'];
276  array_unshift($segments, $new['identifier']);
277 
278  return $request->setUrlSegments($segments);
279  }
280 }
$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
RouteCollection Wrapper.
UrlMatcher Wrapper.
Definition: UrlMatcher.php:8
Delegates requests to controllers based on the registered configuration.
Definition: Router.php:25
prepareResponse(HttpRequest $request)
Prepare response.
Definition: Router.php:106
getResponseFromHandler($handler, \Elgg\Request $request)
Get response from handler function.
Definition: Router.php:181
getResponseFromFile(string $file, \Elgg\Request $request)
Get response from file.
Definition: Router.php:218
route(HttpRequest $request)
Routes the request to a registered page handler.
Definition: Router.php:57
getResponse(HttpRequest $request)
Build a response.
Definition: Router.php:81
__construct(protected EventsService $events, protected RouteCollection $routes, protected UrlMatcher $matcher, protected HandlersService $handlers, protected ResponseFactory $response)
Constructor.
Definition: Router.php:38
allowRewrite(HttpRequest $request)
Filter a request through the 'route:rewrite' event.
Definition: Router.php:249
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
$output
Definition: download.php:9
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:101
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:240
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