router.php 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270
  1. <?php
  2. /**
  3. * Copyright (c) 2012 Bart Visscher <bartv@thisnet.nl>
  4. * This file is licensed under the Affero General Public License version 3 or
  5. * later.
  6. * See the COPYING-README file.
  7. */
  8. namespace OC\Route;
  9. use OCP\Route\IRouter;
  10. use Symfony\Component\Routing\Matcher\UrlMatcher;
  11. use Symfony\Component\Routing\Generator\UrlGenerator;
  12. use Symfony\Component\Routing\RequestContext;
  13. use Symfony\Component\Routing\RouteCollection;
  14. class Router implements IRouter {
  15. /**
  16. * @var \Symfony\Component\Routing\RouteCollection[]
  17. */
  18. protected $collections = array();
  19. /**
  20. * @var \Symfony\Component\Routing\RouteCollection
  21. */
  22. protected $collection = null;
  23. /**
  24. * @var string
  25. */
  26. protected $collectionName = null;
  27. /**
  28. * @var \Symfony\Component\Routing\RouteCollection
  29. */
  30. protected $root = null;
  31. /**
  32. * @var \Symfony\Component\Routing\Generator\UrlGenerator
  33. */
  34. protected $generator = null;
  35. /**
  36. * @var string[]
  37. */
  38. protected $routingFiles;
  39. /**
  40. * @var string
  41. */
  42. protected $cacheKey;
  43. protected $loaded = false;
  44. protected $loadedApps = array();
  45. public function __construct() {
  46. $baseUrl = \OC_Helper::linkTo('', 'index.php');
  47. if (!\OC::$CLI) {
  48. $method = $_SERVER['REQUEST_METHOD'];
  49. } else {
  50. $method = 'GET';
  51. }
  52. $host = \OC_Request::serverHost();
  53. $schema = \OC_Request::serverProtocol();
  54. $this->context = new RequestContext($baseUrl, $method, $host, $schema);
  55. // TODO cache
  56. $this->root = $this->getCollection('root');
  57. }
  58. /**
  59. * Get the files to load the routes from
  60. *
  61. * @return string[]
  62. */
  63. public function getRoutingFiles() {
  64. if (!isset($this->routingFiles)) {
  65. $this->routingFiles = array();
  66. foreach (\OC_APP::getEnabledApps() as $app) {
  67. $file = \OC_App::getAppPath($app) . '/appinfo/routes.php';
  68. if (file_exists($file)) {
  69. $this->routingFiles[$app] = $file;
  70. }
  71. }
  72. }
  73. return $this->routingFiles;
  74. }
  75. /**
  76. * @return string
  77. */
  78. public function getCacheKey() {
  79. if (!isset($this->cacheKey)) {
  80. $files = $this->getRoutingFiles();
  81. $files[] = 'settings/routes.php';
  82. $files[] = 'core/routes.php';
  83. $files[] = 'ocs/routes.php';
  84. $this->cacheKey = \OC\Cache::generateCacheKeyFromFiles($files);
  85. }
  86. return $this->cacheKey;
  87. }
  88. /**
  89. * loads the api routes
  90. * @return void
  91. */
  92. public function loadRoutes($app = null) {
  93. if ($this->loaded) {
  94. return;
  95. }
  96. if (is_null($app)) {
  97. $this->loaded = true;
  98. $routingFiles = $this->getRoutingFiles();
  99. } else {
  100. if (isset($this->loadedApps[$app])) {
  101. return;
  102. }
  103. $file = \OC_App::getAppPath($app) . '/appinfo/routes.php';
  104. if (file_exists($file)) {
  105. $routingFiles = array($app => $file);
  106. } else {
  107. $routingFiles = array();
  108. }
  109. }
  110. foreach ($routingFiles as $app => $file) {
  111. if (!isset($this->loadedApps[$app])) {
  112. $this->loadedApps[$app] = true;
  113. $this->useCollection($app);
  114. $this->requireRouteFile($file);
  115. $collection = $this->getCollection($app);
  116. $collection->addPrefix('/apps/' . $app);
  117. $this->root->addCollection($collection);
  118. }
  119. }
  120. if (!isset($this->loadedApps['core'])) {
  121. $this->loadedApps['core'] = true;
  122. $this->useCollection('root');
  123. require_once 'settings/routes.php';
  124. require_once 'core/routes.php';
  125. // include ocs routes
  126. require_once 'ocs/routes.php';
  127. $collection = $this->getCollection('ocs');
  128. $collection->addPrefix('/ocs');
  129. $this->root->addCollection($collection);
  130. }
  131. }
  132. /**
  133. * @param string $name
  134. * @return \Symfony\Component\Routing\RouteCollection
  135. */
  136. protected function getCollection($name) {
  137. if (!isset($this->collections[$name])) {
  138. $this->collections[$name] = new RouteCollection();
  139. }
  140. return $this->collections[$name];
  141. }
  142. /**
  143. * Sets the collection to use for adding routes
  144. *
  145. * @param string $name Name of the collection to use.
  146. * @return void
  147. */
  148. public function useCollection($name) {
  149. $this->collection = $this->getCollection($name);
  150. $this->collectionName = $name;
  151. }
  152. /**
  153. * returns the current collection name in use for adding routes
  154. *
  155. * @return string the collection name
  156. */
  157. public function getCurrentCollection() {
  158. return $this->collectionName;
  159. }
  160. /**
  161. * Create a \OC\Route\Route.
  162. *
  163. * @param string $name Name of the route to create.
  164. * @param string $pattern The pattern to match
  165. * @param array $defaults An array of default parameter values
  166. * @param array $requirements An array of requirements for parameters (regexes)
  167. * @return \OC\Route\Route
  168. */
  169. public function create($name, $pattern, array $defaults = array(), array $requirements = array()) {
  170. $route = new Route($pattern, $defaults, $requirements);
  171. $this->collection->add($name, $route);
  172. return $route;
  173. }
  174. /**
  175. * Find the route matching $url
  176. *
  177. * @param string $url The url to find
  178. * @throws \Exception
  179. * @return void
  180. */
  181. public function match($url) {
  182. if (substr($url, 0, 6) === '/apps/') {
  183. // empty string / 'apps' / $app / rest of the route
  184. list(, , $app,) = explode('/', $url, 4);
  185. \OC::$REQUESTEDAPP = $app;
  186. $this->loadRoutes($app);
  187. } else if (substr($url, 0, 6) === '/core/' or substr($url, 0, 10) === '/settings/') {
  188. \OC::$REQUESTEDAPP = $url;
  189. if (!\OC_Config::getValue('maintenance', false) && !\OCP\Util::needUpgrade()) {
  190. \OC_App::loadApps();
  191. }
  192. $this->loadRoutes('core');
  193. } else {
  194. $this->loadRoutes();
  195. }
  196. $matcher = new UrlMatcher($this->root, $this->context);
  197. $parameters = $matcher->match($url);
  198. if (isset($parameters['action'])) {
  199. $action = $parameters['action'];
  200. if (!is_callable($action)) {
  201. var_dump($action);
  202. throw new \Exception('not a callable action');
  203. }
  204. unset($parameters['action']);
  205. call_user_func($action, $parameters);
  206. } elseif (isset($parameters['file'])) {
  207. include $parameters['file'];
  208. } else {
  209. throw new \Exception('no action available');
  210. }
  211. }
  212. /**
  213. * Get the url generator
  214. * @return \Symfony\Component\Routing\Generator\UrlGenerator
  215. *
  216. */
  217. public function getGenerator() {
  218. if (null !== $this->generator) {
  219. return $this->generator;
  220. }
  221. return $this->generator = new UrlGenerator($this->root, $this->context);
  222. }
  223. /**
  224. * Generate url based on $name and $parameters
  225. *
  226. * @param string $name Name of the route to use.
  227. * @param array $parameters Parameters for the route
  228. * @param bool $absolute
  229. * @return string
  230. */
  231. public function generate($name, $parameters = array(), $absolute = false) {
  232. $this->loadRoutes();
  233. return $this->getGenerator()->generate($name, $parameters, $absolute);
  234. }
  235. /**
  236. * To isolate the variable scope used inside the $file it is required in it's own method
  237. * @param string $file
  238. */
  239. private function requireRouteFile($file) {
  240. require_once $file;
  241. }
  242. }