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 
33  protected $events;
34 
38  protected $routes;
39 
43  protected $matcher;
44 
48  protected $handlers;
49 
53  protected $response;
54 
58  protected $plugins;
59 
70  public function __construct(
71  EventsService $events,
72  RouteCollection $routes,
73  UrlMatcher $matcher,
74  HandlersService $handlers,
77  ) {
78  $this->events = $events;
79  $this->routes = $routes;
80  $this->matcher = $matcher;
81  $this->handlers = $handlers;
82  $this->response = $response;
83  $this->plugins = $plugins;
84  }
85 
96  public function route(HttpRequest $request) {
97  $this->beginTimer(['build page']);
98 
99  $request->validate();
100 
101  $response = $this->getResponse($request);
102 
103  if ($this->response->getSentResponse()) {
104  return true;
105  }
106 
107  $this->response->respond($response);
108 
109  return headers_sent();
110  }
111 
120  public function getResponse(HttpRequest $request) {
121  $response = $this->prepareResponse($request);
122 
123  if (!$response instanceof ResponseBuilder) {
124  throw new PageNotFoundException();
125  }
126 
127  if ($request->getFirstUrlSegment() == 'action') {
128  if ($response->getForwardURL() === null) {
129  $response->setForwardURL(REFERRER);
130  }
131  }
132 
133  return $response;
134  }
135 
145  protected function prepareResponse(HttpRequest $request) {
146 
147  $segments = $request->getUrlSegments();
148  $path = '/' . implode('/', $segments);
149 
150  try {
151  $parameters = $this->matcher->match($path);
152 
153  $resource = (string) elgg_extract('_resource', $parameters);
154  unset($parameters['_resource']);
155 
156  $handler = elgg_extract('_handler', $parameters);
157  unset($parameters['_handler']);
158 
159  $controller = elgg_extract('_controller', $parameters);
160  unset($parameters['_controller']);
161 
162  $file = elgg_extract('_file', $parameters);
163  unset($parameters['_file']);
164 
165  $deprecated = elgg_extract('_deprecated', $parameters, '');
166  unset($parameters['_deprecated']);
167 
168  $middleware = elgg_extract('_middleware', $parameters, []);
169  unset($parameters['_middleware']);
170 
171  $required_plugins = (array) elgg_extract('_required_plugins', $parameters, []);
172  unset($parameters['_required_plugins']);
173 
174  unset($parameters['_detect_page_owner']);
175 
176  foreach ($required_plugins as $plugin_id) {
177  if (!$this->plugins->isActive($plugin_id)) {
178  throw new PageNotFoundException();
179  }
180  }
181 
182  $route = $this->routes->get($parameters['_route']);
183  $route->setMatchedParameters($parameters);
184  $request->setRoute($route);
185 
186  $envelope = new \Elgg\Request(elgg(), $request);
187  $parameters['request'] = $envelope;
188 
189  if (!empty($deprecated)) {
190  elgg_deprecated_notice("The route \"{$route->getName()}\" has been deprecated.", $deprecated);
191  }
192 
193  foreach ($middleware as $callable) {
194  $result = $this->handlers->call($callable, $envelope, null);
195  if ($result[1] instanceof ResponseBuilder) {
196  return $result[1];
197  }
198  }
199 
200  if ($handler) {
201  return $this->getResponseFromHandler($handler, $envelope);
202  } elseif ($controller) {
203  $result = $this->handlers->call($controller, $envelope, null);
204  if ($result[1] instanceof ResponseBuilder) {
205  return $result[1];
206  }
207  } elseif ($file) {
208  return $this->getResponseFromFile($file, $envelope);
209  }
210 
211  $output = elgg_view_resource($resource, $parameters);
212  return elgg_ok_response($output);
213  } catch (ResourceNotFoundException $ex) {
214  throw new PageNotFoundException();
215  } catch (MethodNotAllowedException $ex) {
216  throw new BadRequestException();
217  }
218  }
219 
229  if (!is_callable($handler)) {
230  return null;
231  }
232 
233  $path = trim($request->getPath(), '/');
234  $segments = explode('/', $path);
235  $identifier = array_shift($segments) ? : '';
236 
237  ob_start();
238  try {
239  $response = call_user_func($handler, $segments, $identifier, $request);
240  } catch (\Exception $ex) {
241  ob_end_clean();
242  throw $ex;
243  }
244 
245  $output = ob_get_clean();
246 
247  if ($response instanceof ResponseBuilder) {
248  return $response;
249  } else if ($response === false) {
250  return null;
251  }
252 
253  return elgg_ok_response($output);
254  }
255 
265  protected function getResponseFromFile(string $file, \Elgg\Request $request) {
266  if (!is_file($file)) {
267  throw new PageNotFoundException(elgg_echo('actionnotfound', [$request->getPath()]), ELGG_HTTP_NOT_IMPLEMENTED);
268  }
269 
270  ob_start();
271 
272  try {
273  $response = include $file;
274  } catch (\Exception $ex) {
275  ob_get_clean();
276  throw $ex;
277  }
278 
279  $output = ob_get_clean();
280 
281  if ($response instanceof ResponseBuilder) {
282  return $response;
283  }
284 
285  return elgg_ok_response($output);
286  }
287 
296  public function allowRewrite(HttpRequest $request) {
297  $segments = $request->getUrlSegments();
298  if (!empty($segments)) {
299  $identifier = array_shift($segments);
300  } else {
301  $identifier = '';
302  }
303 
304  $old = [
305  'identifier' => $identifier,
306  'segments' => $segments,
307  ];
308  $new = $this->events->triggerResults('route:rewrite', $identifier, $old, $old);
309  if ($new === $old) {
310  return $request;
311  }
312 
313  if (
314  !isset($new['identifier']) ||
315  !isset($new['segments']) ||
316  !is_string($new['identifier']) ||
317  !is_array($new['segments'])
318  ) {
319  throw new RuntimeException('rewrite_path handler returned invalid route data.');
320  }
321 
322  // rewrite request
323  $segments = $new['segments'];
324  array_unshift($segments, $new['identifier']);
325 
326  return $request->setUrlSegments($segments);
327  }
328 }
trait Profilable
Make an object accept a timer.
Definition: Profilable.php:12
HTTP response service.
HTTP response builder interface.
route(HttpRequest $request)
Routes the request to a registered page handler.
Definition: Router.php:96
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:97
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:145
$response
Definition: content.php:10
$request
Definition: livesearch.php:11
elgg_echo(string $message_key, array $args=[], string $language= '')
Elgg language module Functions to manage language and translations.
Definition: languages.php:17
Events service.
$plugins
Definition: categories.php:3
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:327
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
__construct(EventsService $events, RouteCollection $routes, UrlMatcher $matcher, HandlersService $handlers, ResponseFactory $response, Plugins $plugins)
Constructor.
Definition: Router.php:70
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:256
$path
Definition: details.php:68
getResponseFromHandler($handler,\Elgg\Request $request)
Get response from handler function.
Definition: Router.php:228
Thrown when page is not accessible.
const REFERRER
Definition: constants.php:42
allowRewrite(HttpRequest $request)
Filter a request through the &#39;route:rewrite&#39; event.
Definition: Router.php:296
RouteCollection Wrapper.
Thrown when request is malformatted.
beginTimer(array $keys)
Start the timer (when enabled)
Definition: Profilable.php:46
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:29
getResponse(HttpRequest $request)
Build a response.
Definition: Router.php:120
$output
Definition: download.php:9
getResponseFromFile(string $file,\Elgg\Request $request)
Get response from file.
Definition: Router.php:265
var elgg
Definition: elgglib.js:4