Open Journal Systems  3.3.0
PKPRouter.inc.php
1 <?php
2 
57 class PKPRouter {
58  //
59  // Internal state cache variables
60  // NB: Please do not access directly but
61  // only via their respective getters/setters
62  //
64  var $_application;
66  var $_dispatcher;
68  var $_contextDepth;
70  var $_contextList;
74  var $_contextPaths = array();
76  var $_contexts = array();
78  var $_handler;
79 
83  function __construct() {
84  }
85 
90  function &getApplication() {
91  assert(is_a($this->_application, 'PKPApplication'));
93  }
94 
99  function setApplication($application) {
100  $this->_application = $application;
101 
102  // Retrieve context depth and list
103  $this->_contextDepth = $application->getContextDepth();
104  $this->_contextList = $application->getContextList();
105  $this->_flippedContextList = array_flip($this->_contextList);
106  }
107 
112  function &getDispatcher() {
113  assert(is_a($this->_dispatcher, 'Dispatcher'));
115  }
116 
121  function setDispatcher($dispatcher) {
122  $this->_dispatcher = $dispatcher;
123  }
124 
129  function setHandler($handler) {
130  $this->_handler = $handler;
131  }
132 
137  function getHandler() {
138  return $this->_handler;
139  }
140 
146  function supports($request) {
147  // Default implementation returns always true
148  return true;
149  }
150 
156  function isCacheable($request) {
157  // Default implementation returns always false
158  return false;
159  }
160 
167  function getRequestedContextPaths($request) {
168  // Handle context depth 0
169  if (!$this->_contextDepth) return array();
170 
171  // Validate context parameters
172  assert(isset($this->_contextDepth) && isset($this->_contextList));
173 
174  $isPathInfoEnabled = $request->isPathInfoEnabled();
175  $userVars = array();
176  $url = null;
177 
178  // Determine the context path
179  if (empty($this->_contextPaths)) {
180  if ($isPathInfoEnabled) {
181  // Retrieve url from the path info
182  if (isset($_SERVER['PATH_INFO'])) {
183  $url = $_SERVER['PATH_INFO'];
184  }
185  } else {
186  $url = $request->getCompleteUrl();
187  $userVars = $request->getUserVars();
188  }
189 
190  $this->_contextPaths = Core::getContextPaths($url, $isPathInfoEnabled,
191  $this->_contextList, $this->_contextDepth, $userVars);
192 
193  HookRegistry::call('Router::getRequestedContextPaths', array(&$this->_contextPaths));
194  }
195 
196  return $this->_contextPaths;
197  }
198 
205  function getRequestedContextPath($request, $requestedContextLevel = 1) {
206  // Handle context depth 0
207  if (!$this->_contextDepth) return null;
208 
209  // Validate the context level
210  assert(isset($this->_contextDepth) && isset($this->_contextList));
211  assert($requestedContextLevel > 0 && $requestedContextLevel <= $this->_contextDepth);
212 
213  // Return the full context, then retrieve the requested context path
214  $contextPaths = $this->getRequestedContextPaths($request);
215  assert(isset($this->_contextPaths[$requestedContextLevel - 1]));
216  return $this->_contextPaths[$requestedContextLevel - 1];
217  }
218 
226  function &getContext($request, $requestedContextLevel = 1, $forceReload = false) {
227  // Handle context depth 0
228  if (!$this->_contextDepth) {
229  $nullVar = null;
230  return $nullVar;
231  }
232 
233  if ($forceReload || !isset($this->_contexts[$requestedContextLevel])) {
234  // Retrieve the requested context path (this validates the context level and the path)
235  $path = $this->getRequestedContextPath($request, $requestedContextLevel);
236 
237  // Resolve the path to the context
238  if ($path == 'index') {
239  $this->_contexts[$requestedContextLevel] = null;
240  } else {
241  // Get the context name (this validates the context name)
242  $requestedContextName = $this->_contextLevelToContextName($requestedContextLevel);
243 
244  // Get the DAO for the requested context.
245  $contextClass = ucfirst($requestedContextName);
246  $daoName = $contextClass.'DAO';
247  $daoInstance = DAORegistry::getDAO($daoName);
248 
249  // Retrieve the context from the DAO (by path)
250  $daoMethod = 'getByPath';
251  assert(method_exists($daoInstance, $daoMethod));
252  $this->_contexts[$requestedContextLevel] = $daoInstance->$daoMethod($path);
253  }
254  }
255 
256  return $this->_contexts[$requestedContextLevel];
257  }
258 
265  function &getContextByName($request, $requestedContextName) {
266  // Handle context depth 0
267  if (!$this->_contextDepth) {
268  $nullVar = null;
269  return $nullVar;
270  }
271 
272  // Convert the context name to a context level (this validates the context name)
273  $requestedContextLevel = $this->_contextNameToContextLevel($requestedContextName);
274 
275  // Retrieve the requested context by level
276  $returner = $this->getContext($request, $requestedContextLevel);
277  return $returner;
278  }
279 
285  function getIndexUrl($request) {
286  if (!isset($this->_indexUrl)) {
287  if ($request->isRestfulUrlsEnabled()) {
288  $this->_indexUrl = $request->getBaseUrl();
289  } else {
290  $this->_indexUrl = $request->getBaseUrl() . '/index.php';
291  }
292  HookRegistry::call('Router::getIndexUrl', array(&$this->_indexUrl));
293  }
294 
295  return $this->_indexUrl;
296  }
297 
298 
299  //
300  // Protected template methods to be implemented by sub-classes.
301  //
307  function getCacheFilename($request) {
308  // must be implemented by sub-classes
309  assert(false);
310  }
311 
316  function route($request) {
317  // Must be implemented by sub-classes.
318  assert(false);
319  }
320 
333  function url($request, $newContext = null, $handler = null, $op = null, $path = null,
334  $params = null, $anchor = null, $escape = false) {
335  // Must be implemented by sub-classes.
336  assert(false);
337  }
338 
345  function handleAuthorizationFailure($request, $authorizationMessage) {
346  // Must be implemented by sub-classes.
347  assert(false);
348  }
349 
350 
351  //
352  // Private helper methods
353  //
369  function _authorizeInitializeAndCallRequest(&$serviceEndpoint, $request, &$args, $validate = true) {
370  $dispatcher = $this->getDispatcher();
371 
372  // It's conceivable that a call has gotten this far without
373  // actually being callable, e.g. a component has been named
374  // that does not exist and that no plugin has registered.
375  if (!is_callable($serviceEndpoint)) $dispatcher->handle404();
376 
377  // Pass the dispatcher to the handler.
378  $serviceEndpoint[0]->setDispatcher($dispatcher);
379 
380  // Authorize the request.
381  $roleAssignments = $serviceEndpoint[0]->getRoleAssignments();
382  assert(is_array($roleAssignments));
383  if ($serviceEndpoint[0]->authorize($request, $args, $roleAssignments)) {
384  // Execute class-wide data integrity checks.
385  if ($validate) $serviceEndpoint[0]->validate($request, $args);
386 
387  // Let the handler initialize itself.
388  $serviceEndpoint[0]->initialize($request, $args);
389 
390  // Call the service endpoint.
391  $result = call_user_func($serviceEndpoint, $args, $request);
392  } else {
393  // Authorization failed - try to retrieve a user
394  // message.
395  $authorizationMessage = $serviceEndpoint[0]->getLastAuthorizationMessage();
396 
397  // Set a generic authorization message if no
398  // specific authorization message was set.
399  if ($authorizationMessage == '') $authorizationMessage = 'user.authorization.accessDenied';
400 
401  // Handle the authorization failure.
402  $result = $this->handleAuthorizationFailure($request, $authorizationMessage);
403  }
404 
405  // Return the result of the operation to the client.
406  if (is_string($result)) echo $result;
407  elseif (is_a($result, 'JSONMessage')) {
408  header('Content-Type: application/json');
409  echo $result->getString();
410  }
411  }
412 
428  function _urlCanonicalizeNewContext($newContext) {
429  // Create an empty array in case no new context was given.
430  if (is_null($newContext)) $newContext = array();
431 
432  // If we got the new context as a scalar then transform
433  // it into an array.
434  if (is_scalar($newContext)) $newContext = array($newContext);
435 
436  // Check whether any new context has been provided.
437  // If not then return an empty array.
438  $newContextProvided = false;
439  foreach($newContext as $contextElement) {
440  if(isset($contextElement)) $newContextProvided = true;
441  }
442  if (!$newContextProvided) $newContext = array();
443 
444  return $newContext;
445  }
446 
463  function _urlGetBaseAndContext($request, $newContext = array()) {
464  $pathInfoEnabled = $request->isPathInfoEnabled();
465 
466  // Retrieve the context list.
467  $contextList = $this->_contextList;
468 
469  $baseUrlConfigSuffix = '';
470  $overriddenContextCount = 0;
471 
472  // Determine URL context
473  $context = array();
474  foreach ($contextList as $contextKey => $contextName) {
475  if ($pathInfoEnabled) {
476  $contextParameter = '';
477  } else {
478  $contextParameter = $contextName.'=';
479  }
480 
481  $newContextValue = array_shift($newContext);
482  if (isset($newContextValue)) {
483  // A new context has been set so use it.
484  $contextValue = rawurlencode($newContextValue);
485  } else {
486  // No new context has been set so determine
487  // the current request's context
488  $contextObject = $this->getContextByName($request, $contextName);
489  if ($contextObject) $contextValue = $contextObject->getPath();
490  else $contextValue = 'index';
491  }
492 
493  // Check whether the base URL is overridden.
494  $baseUrlConfigSuffix .= "[$contextValue]";
495  $newOverriddenBaseUrl = Config::getVar('general', 'base_url' . $baseUrlConfigSuffix);
496  if (!empty($newOverriddenBaseUrl)) {
497  $overriddenContextCount = $contextKey + 1;
498  $overriddenBaseUrl = $newOverriddenBaseUrl;
499  }
500 
501  $context[] = $contextParameter.$contextValue;
502  }
503 
504  // Generate the base url
505  if (!empty($overriddenBaseUrl)) {
506  $baseUrl = $overriddenBaseUrl;
507 
508  // Throw the overridden context(s) away
509  while ($overriddenContextCount>0) {
510  array_shift($context);
511  $overriddenContextCount--;
512  }
513  } else {
514  $baseUrl = $this->getIndexUrl($request);
515  }
516 
517  // Join base URL and context and return the result
518  $baseUrlAndContext = array_merge(array($baseUrl), $context);
519  return $baseUrlAndContext;
520  }
521 
531  function _urlGetAdditionalParameters($request, $params = null, $escape = true) {
532  $additionalParameters = array();
533  if (!empty($params)) {
534  assert(is_array($params));
535  foreach ($params as $key => $value) {
536  if (is_array($value)) {
537  foreach($value as $element) {
538  $additionalParameters[] = $key.($escape?'%5B%5D=':'[]=').rawurlencode($element);
539  }
540  } else {
541  $additionalParameters[] = $key.'='.rawurlencode($value);
542  }
543  }
544  }
545 
546  return $additionalParameters;
547  }
548 
558  function _urlFromParts($baseUrl, $pathInfoArray = array(), $queryParametersArray = array(), $anchor = '', $escape = false) {
559  // parse_url does not support protocol relative URLs;
560  // work around it here (https://bugs.php.net/bug.php?id=66274)
561  if (strpos($baseUrl,'//')===0) {
562  $baseUrl = 'http:' . $baseUrl;
563  $protocolRelativeWorkaround = true;
564  } else {
565  $protocolRelativeWorkaround = false;
566  }
567 
568  // Parse the base url
569  $baseUrlParts = parse_url($baseUrl);
570  assert(isset($baseUrlParts['scheme']) && isset($baseUrlParts['host']) && !isset($baseUrlParts['fragment']));
571 
572  // Reconstruct the base url without path and query
573  // (supporting the work-around for protocol relative URLs)
574  $baseUrl = $protocolRelativeWorkaround?'//':($baseUrlParts['scheme'].'://');
575  if (isset($baseUrlParts['user'])) {
576  $baseUrl .= $baseUrlParts['user'];
577  if (isset($baseUrlParts['pass'])) {
578  $baseUrl .= ':'.$baseUrlParts['pass'];
579  }
580  $baseUrl .= '@';
581  }
582  $baseUrl .= $baseUrlParts['host'];
583  if (isset($baseUrlParts['port'])) $baseUrl .= ':'.$baseUrlParts['port'];
584  $baseUrl .= '/';
585 
586  // Add path info from the base URL
587  // to the path info array (if any).
588  if (isset($baseUrlParts['path'])) {
589  $pathInfoArray = array_merge(explode('/', trim($baseUrlParts['path'], '/')), $pathInfoArray);
590  }
591 
592  // Add query parameters from the base URL
593  // to the query parameter array (if any).
594  if (isset($baseUrlParts['query'])) {
595  $queryParametersArray = array_merge(explode('&', $baseUrlParts['query']), $queryParametersArray);
596  }
597 
598  // Expand path info
599  $pathInfo = implode('/', $pathInfoArray);
600 
601  // Expand query parameters
602  $amp = ($escape ? '&amp;' : '&');
603  $queryParameters = implode($amp, $queryParametersArray);
604  $queryParameters = (empty($queryParameters) ? '' : '?'.$queryParameters);
605 
606  // Assemble and return the final URL
607  return $baseUrl.$pathInfo.$queryParameters.$anchor;
608  }
609 
615  function _contextLevelToContextName($contextLevel) {
616  assert(isset($this->_contextList[$contextLevel - 1]));
617  return $this->_contextList[$contextLevel - 1];
618  }
619 
625  function _contextNameToContextLevel($contextName) {
626  assert(isset($this->_flippedContextList[$contextName]));
627  return $this->_flippedContextList[$contextName] + 1;
628  }
629 }
PKPRouter\_authorizeInitializeAndCallRequest
_authorizeInitializeAndCallRequest(&$serviceEndpoint, $request, &$args, $validate=true)
Definition: PKPRouter.inc.php:393
$op
$op
Definition: lib/pkp/pages/help/index.php:18
PKPRouter\setHandler
setHandler($handler)
Definition: PKPRouter.inc.php:153
PKPRouter\$_handler
$_handler
Definition: PKPRouter.inc.php:102
PKPRouter\getHandler
getHandler()
Definition: PKPRouter.inc.php:161
PKPRouter\setDispatcher
setDispatcher($dispatcher)
Definition: PKPRouter.inc.php:145
$application
$application
Definition: index.php:65
DAORegistry\getDAO
static & getDAO($name, $dbconn=null)
Definition: DAORegistry.inc.php:57
PKPRouter\$_flippedContextList
$_flippedContextList
Definition: PKPRouter.inc.php:87
PKPRouter\$_application
$_application
Definition: PKPRouter.inc.php:67
PKPRouter\_contextNameToContextLevel
_contextNameToContextLevel($contextName)
Definition: PKPRouter.inc.php:649
PKPRouter\route
route($request)
Definition: PKPRouter.inc.php:340
PKPRouter\getContextByName
& getContextByName($request, $requestedContextName)
Definition: PKPRouter.inc.php:289
PKPRouter\url
url($request, $newContext=null, $handler=null, $op=null, $path=null, $params=null, $anchor=null, $escape=false)
Definition: PKPRouter.inc.php:357
PKPRouter\_urlCanonicalizeNewContext
_urlCanonicalizeNewContext($newContext)
Definition: PKPRouter.inc.php:452
PKPRouter\handleAuthorizationFailure
handleAuthorizationFailure($request, $authorizationMessage)
Definition: PKPRouter.inc.php:369
PKPRouter\_urlGetBaseAndContext
_urlGetBaseAndContext($request, $newContext=array())
Definition: PKPRouter.inc.php:487
Core\getContextPaths
static getContextPaths($urlInfo, $isPathInfo, $contextList=null, $contextDepth=null, $userVars=array())
Definition: Core.inc.php:137
PKPRouter\setApplication
setApplication($application)
Definition: PKPRouter.inc.php:123
PKPRouter\supports
supports($request)
Definition: PKPRouter.inc.php:170
PKPRouter\$_contexts
$_contexts
Definition: PKPRouter.inc.php:97
PKPRouter\$_contextPaths
$_contextPaths
Definition: PKPRouter.inc.php:92
PKPRouter\_contextLevelToContextName
_contextLevelToContextName($contextLevel)
Definition: PKPRouter.inc.php:639
Config\getVar
static getVar($section, $key, $default=null)
Definition: Config.inc.php:35
PKPRouter\getContext
& getContext($request, $requestedContextLevel=1, $forceReload=false)
Definition: PKPRouter.inc.php:250
PKPRouter\$_contextList
$_contextList
Definition: PKPRouter.inc.php:82
PKPRouter\getRequestedContextPaths
getRequestedContextPaths($request)
Definition: PKPRouter.inc.php:191
PKPRouter\getApplication
& getApplication()
Definition: PKPRouter.inc.php:114
PKPRouter\isCacheable
isCacheable($request)
Definition: PKPRouter.inc.php:180
PKPRouter
Basic router class that has functionality common to all routers.
Definition: PKPRouter.inc.php:57
PKPRouter\_urlFromParts
_urlFromParts($baseUrl, $pathInfoArray=array(), $queryParametersArray=array(), $anchor='', $escape=false)
Definition: PKPRouter.inc.php:582
PKPRouter\getCacheFilename
getCacheFilename($request)
Definition: PKPRouter.inc.php:331
PKPRouter\__construct
__construct()
Definition: PKPRouter.inc.php:107
PKPRouter\$_dispatcher
$_dispatcher
Definition: PKPRouter.inc.php:72
PKPRouter\getRequestedContextPath
getRequestedContextPath($request, $requestedContextLevel=1)
Definition: PKPRouter.inc.php:229
HookRegistry\call
static call($hookName, $args=null)
Definition: HookRegistry.inc.php:86
PKPRouter\_urlGetAdditionalParameters
_urlGetAdditionalParameters($request, $params=null, $escape=true)
Definition: PKPRouter.inc.php:555
PKPRouter\getIndexUrl
getIndexUrl($request)
Definition: PKPRouter.inc.php:309
PKPRouter\getDispatcher
& getDispatcher()
Definition: PKPRouter.inc.php:136
PKPRouter\$_contextDepth
$_contextDepth
Definition: PKPRouter.inc.php:77