PageRenderTime 40ms CodeModel.GetById 10ms RepoModel.GetById 0ms app.codeStats 0ms

/vendor/laravel/framework/src/Illuminate/Routing/UrlGenerator.php

https://gitlab.com/Laolballs/evotting
PHP | 681 lines | 293 code | 82 blank | 306 comment | 15 complexity | 61bfb714eb197c5e02ff6472279eae1c MD5 | raw file
  1. <?php namespace Illuminate\Routing;
  2. use Illuminate\Http\Request;
  3. use InvalidArgumentException;
  4. use Illuminate\Contracts\Routing\UrlRoutable;
  5. use Illuminate\Contracts\Routing\UrlGenerator as UrlGeneratorContract;
  6. class UrlGenerator implements UrlGeneratorContract {
  7. /**
  8. * The route collection.
  9. *
  10. * @var \Illuminate\Routing\RouteCollection
  11. */
  12. protected $routes;
  13. /**
  14. * The request instance.
  15. *
  16. * @var \Illuminate\Http\Request
  17. */
  18. protected $request;
  19. /**
  20. * The force URL root.
  21. *
  22. * @var string
  23. */
  24. protected $forcedRoot;
  25. /**
  26. * The forced schema for URLs.
  27. *
  28. * @var string
  29. */
  30. protected $forceSchema;
  31. /**
  32. * The root namespace being applied to controller actions.
  33. *
  34. * @var string
  35. */
  36. protected $rootNamespace;
  37. /**
  38. * The session resolver callable.
  39. *
  40. * @var callable
  41. */
  42. protected $sessionResolver;
  43. /**
  44. * Characters that should not be URL encoded.
  45. *
  46. * @var array
  47. */
  48. protected $dontEncode = array(
  49. '%2F' => '/',
  50. '%40' => '@',
  51. '%3A' => ':',
  52. '%3B' => ';',
  53. '%2C' => ',',
  54. '%3D' => '=',
  55. '%2B' => '+',
  56. '%21' => '!',
  57. '%2A' => '*',
  58. '%7C' => '|',
  59. '%3F' => '?',
  60. '%26' => '&',
  61. '%23' => '#',
  62. '%25' => '%',
  63. );
  64. /**
  65. * Create a new URL Generator instance.
  66. *
  67. * @param \Illuminate\Routing\RouteCollection $routes
  68. * @param \Illuminate\Http\Request $request
  69. * @return void
  70. */
  71. public function __construct(RouteCollection $routes, Request $request)
  72. {
  73. $this->routes = $routes;
  74. $this->setRequest($request);
  75. }
  76. /**
  77. * Get the full URL for the current request.
  78. *
  79. * @return string
  80. */
  81. public function full()
  82. {
  83. return $this->request->fullUrl();
  84. }
  85. /**
  86. * Get the current URL for the request.
  87. *
  88. * @return string
  89. */
  90. public function current()
  91. {
  92. return $this->to($this->request->getPathInfo());
  93. }
  94. /**
  95. * Get the URL for the previous request.
  96. *
  97. * @return string
  98. */
  99. public function previous()
  100. {
  101. $referrer = $this->request->headers->get('referer');
  102. $url = $referrer ? $this->to($referrer) : $this->getPreviousUrlFromSession();
  103. return $url ?: $this->to('/');
  104. }
  105. /**
  106. * Generate a absolute URL to the given path.
  107. *
  108. * @param string $path
  109. * @param mixed $extra
  110. * @param bool|null $secure
  111. * @return string
  112. */
  113. public function to($path, $extra = array(), $secure = null)
  114. {
  115. // First we will check if the URL is already a valid URL. If it is we will not
  116. // try to generate a new one but will simply return the URL as is, which is
  117. // convenient since developers do not always have to check if it's valid.
  118. if ($this->isValidUrl($path)) return $path;
  119. $scheme = $this->getScheme($secure);
  120. $extra = $this->formatParameters($extra);
  121. $tail = implode('/', array_map(
  122. 'rawurlencode', (array) $extra)
  123. );
  124. // Once we have the scheme we will compile the "tail" by collapsing the values
  125. // into a single string delimited by slashes. This just makes it convenient
  126. // for passing the array of parameters to this URL as a list of segments.
  127. $root = $this->getRootUrl($scheme);
  128. return $this->trimUrl($root, $path, $tail);
  129. }
  130. /**
  131. * Generate a secure, absolute URL to the given path.
  132. *
  133. * @param string $path
  134. * @param array $parameters
  135. * @return string
  136. */
  137. public function secure($path, $parameters = array())
  138. {
  139. return $this->to($path, $parameters, true);
  140. }
  141. /**
  142. * Generate a URL to an application asset.
  143. *
  144. * @param string $path
  145. * @param bool|null $secure
  146. * @return string
  147. */
  148. public function asset($path, $secure = null)
  149. {
  150. if ($this->isValidUrl($path)) return $path;
  151. // Once we get the root URL, we will check to see if it contains an index.php
  152. // file in the paths. If it does, we will remove it since it is not needed
  153. // for asset paths, but only for routes to endpoints in the application.
  154. $root = $this->getRootUrl($this->getScheme($secure));
  155. return $this->removeIndex($root).'/'.trim($path, '/');
  156. }
  157. /**
  158. * Remove the index.php file from a path.
  159. *
  160. * @param string $root
  161. * @return string
  162. */
  163. protected function removeIndex($root)
  164. {
  165. $i = 'index.php';
  166. return str_contains($root, $i) ? str_replace('/'.$i, '', $root) : $root;
  167. }
  168. /**
  169. * Generate a URL to a secure asset.
  170. *
  171. * @param string $path
  172. * @return string
  173. */
  174. public function secureAsset($path)
  175. {
  176. return $this->asset($path, true);
  177. }
  178. /**
  179. * Get the scheme for a raw URL.
  180. *
  181. * @param bool|null $secure
  182. * @return string
  183. */
  184. protected function getScheme($secure)
  185. {
  186. if (is_null($secure))
  187. {
  188. return $this->forceSchema ?: $this->request->getScheme().'://';
  189. }
  190. return $secure ? 'https://' : 'http://';
  191. }
  192. /**
  193. * Force the schema for URLs.
  194. *
  195. * @param string $schema
  196. * @return void
  197. */
  198. public function forceSchema($schema)
  199. {
  200. $this->forceSchema = $schema.'://';
  201. }
  202. /**
  203. * Get the URL to a named route.
  204. *
  205. * @param string $name
  206. * @param mixed $parameters
  207. * @param bool $absolute
  208. * @return string
  209. *
  210. * @throws \InvalidArgumentException
  211. */
  212. public function route($name, $parameters = array(), $absolute = true)
  213. {
  214. if ( ! is_null($route = $this->routes->getByName($name)))
  215. {
  216. return $this->toRoute($route, $parameters, $absolute);
  217. }
  218. throw new InvalidArgumentException("Route [{$name}] not defined.");
  219. }
  220. /**
  221. * Get the URL for a given route instance.
  222. *
  223. * @param \Illuminate\Routing\Route $route
  224. * @param mixed $parameters
  225. * @param bool $absolute
  226. * @return string
  227. */
  228. protected function toRoute($route, $parameters, $absolute)
  229. {
  230. $parameters = $this->formatParameters($parameters);
  231. $domain = $this->getRouteDomain($route, $parameters);
  232. $uri = strtr(rawurlencode($this->addQueryString($this->trimUrl(
  233. $root = $this->replaceRoot($route, $domain, $parameters),
  234. $this->replaceRouteParameters($route->uri(), $parameters)
  235. ), $parameters)), $this->dontEncode);
  236. return $absolute ? $uri : '/'.ltrim(str_replace($root, '', $uri), '/');
  237. }
  238. /**
  239. * Replace the parameters on the root path.
  240. *
  241. * @param \Illuminate\Routing\Route $route
  242. * @param string $domain
  243. * @param array $parameters
  244. * @return string
  245. */
  246. protected function replaceRoot($route, $domain, &$parameters)
  247. {
  248. return $this->replaceRouteParameters($this->getRouteRoot($route, $domain), $parameters);
  249. }
  250. /**
  251. * Replace all of the wildcard parameters for a route path.
  252. *
  253. * @param string $path
  254. * @param array $parameters
  255. * @return string
  256. */
  257. protected function replaceRouteParameters($path, array &$parameters)
  258. {
  259. if (count($parameters))
  260. {
  261. $path = preg_replace_sub(
  262. '/\{.*?\}/', $parameters, $this->replaceNamedParameters($path, $parameters)
  263. );
  264. }
  265. return trim(preg_replace('/\{.*?\?\}/', '', $path), '/');
  266. }
  267. /**
  268. * Replace all of the named parameters in the path.
  269. *
  270. * @param string $path
  271. * @param array $parameters
  272. * @return string
  273. */
  274. protected function replaceNamedParameters($path, &$parameters)
  275. {
  276. return preg_replace_callback('/\{(.*?)\??\}/', function($m) use (&$parameters)
  277. {
  278. return isset($parameters[$m[1]]) ? array_pull($parameters, $m[1]) : $m[0];
  279. }, $path);
  280. }
  281. /**
  282. * Add a query string to the URI.
  283. *
  284. * @param string $uri
  285. * @param array $parameters
  286. * @return mixed|string
  287. */
  288. protected function addQueryString($uri, array $parameters)
  289. {
  290. // If the URI has a fragment, we will move it to the end of the URI since it will
  291. // need to come after any query string that may be added to the URL else it is
  292. // not going to be available. We will remove it then append it back on here.
  293. if ( ! is_null($fragment = parse_url($uri, PHP_URL_FRAGMENT)))
  294. {
  295. $uri = preg_replace('/#.*/', '', $uri);
  296. }
  297. $uri .= $this->getRouteQueryString($parameters);
  298. return is_null($fragment) ? $uri : $uri."#{$fragment}";
  299. }
  300. /**
  301. * Format the array of URL parameters.
  302. *
  303. * @param mixed|array $parameters
  304. * @return array
  305. */
  306. protected function formatParameters($parameters)
  307. {
  308. return $this->replaceRoutableParameters($parameters);
  309. }
  310. /**
  311. * Replace UrlRoutable parameters with their route parameter.
  312. *
  313. * @param array $parameters
  314. * @return array
  315. */
  316. protected function replaceRoutableParameters($parameters = array())
  317. {
  318. $parameters = is_array($parameters) ? $parameters : array($parameters);
  319. foreach ($parameters as $key => $parameter)
  320. {
  321. if ($parameter instanceof UrlRoutable)
  322. {
  323. $parameters[$key] = $parameter->getRouteKey();
  324. }
  325. }
  326. return $parameters;
  327. }
  328. /**
  329. * Get the query string for a given route.
  330. *
  331. * @param array $parameters
  332. * @return string
  333. */
  334. protected function getRouteQueryString(array $parameters)
  335. {
  336. // First we will get all of the string parameters that are remaining after we
  337. // have replaced the route wildcards. We'll then build a query string from
  338. // these string parameters then use it as a starting point for the rest.
  339. if (count($parameters) == 0) return '';
  340. $query = http_build_query(
  341. $keyed = $this->getStringParameters($parameters)
  342. );
  343. // Lastly, if there are still parameters remaining, we will fetch the numeric
  344. // parameters that are in the array and add them to the query string or we
  345. // will make the initial query string if it wasn't started with strings.
  346. if (count($keyed) < count($parameters))
  347. {
  348. $query .= '&'.implode(
  349. '&', $this->getNumericParameters($parameters)
  350. );
  351. }
  352. return '?'.trim($query, '&');
  353. }
  354. /**
  355. * Get the string parameters from a given list.
  356. *
  357. * @param array $parameters
  358. * @return array
  359. */
  360. protected function getStringParameters(array $parameters)
  361. {
  362. return array_where($parameters, function($k, $v) { return is_string($k); });
  363. }
  364. /**
  365. * Get the numeric parameters from a given list.
  366. *
  367. * @param array $parameters
  368. * @return array
  369. */
  370. protected function getNumericParameters(array $parameters)
  371. {
  372. return array_where($parameters, function($k, $v) { return is_numeric($k); });
  373. }
  374. /**
  375. * Get the formatted domain for a given route.
  376. *
  377. * @param \Illuminate\Routing\Route $route
  378. * @param array $parameters
  379. * @return string
  380. */
  381. protected function getRouteDomain($route, &$parameters)
  382. {
  383. return $route->domain() ? $this->formatDomain($route, $parameters) : null;
  384. }
  385. /**
  386. * Format the domain and port for the route and request.
  387. *
  388. * @param \Illuminate\Routing\Route $route
  389. * @param array $parameters
  390. * @return string
  391. */
  392. protected function formatDomain($route, &$parameters)
  393. {
  394. return $this->addPortToDomain($this->getDomainAndScheme($route));
  395. }
  396. /**
  397. * Get the domain and scheme for the route.
  398. *
  399. * @param \Illuminate\Routing\Route $route
  400. * @return string
  401. */
  402. protected function getDomainAndScheme($route)
  403. {
  404. return $this->getRouteScheme($route).$route->domain();
  405. }
  406. /**
  407. * Add the port to the domain if necessary.
  408. *
  409. * @param string $domain
  410. * @return string
  411. */
  412. protected function addPortToDomain($domain)
  413. {
  414. if (in_array($this->request->getPort(), array('80', '443')))
  415. {
  416. return $domain;
  417. }
  418. return $domain.':'.$this->request->getPort();
  419. }
  420. /**
  421. * Get the root of the route URL.
  422. *
  423. * @param \Illuminate\Routing\Route $route
  424. * @param string $domain
  425. * @return string
  426. */
  427. protected function getRouteRoot($route, $domain)
  428. {
  429. return $this->getRootUrl($this->getRouteScheme($route), $domain);
  430. }
  431. /**
  432. * Get the scheme for the given route.
  433. *
  434. * @param \Illuminate\Routing\Route $route
  435. * @return string
  436. */
  437. protected function getRouteScheme($route)
  438. {
  439. if ($route->httpOnly())
  440. {
  441. return $this->getScheme(false);
  442. }
  443. elseif ($route->httpsOnly())
  444. {
  445. return $this->getScheme(true);
  446. }
  447. return $this->getScheme(null);
  448. }
  449. /**
  450. * Get the URL to a controller action.
  451. *
  452. * @param string $action
  453. * @param mixed $parameters
  454. * @param bool $absolute
  455. * @return string
  456. *
  457. * @throws \InvalidArgumentException
  458. */
  459. public function action($action, $parameters = array(), $absolute = true)
  460. {
  461. if ($this->rootNamespace && ! (strpos($action, '\\') === 0))
  462. {
  463. $action = $this->rootNamespace.'\\'.$action;
  464. }
  465. else
  466. {
  467. $action = trim($action, '\\');
  468. }
  469. if ( ! is_null($route = $this->routes->getByAction($action)))
  470. {
  471. return $this->toRoute($route, $parameters, $absolute);
  472. }
  473. throw new InvalidArgumentException("Action {$action} not defined.");
  474. }
  475. /**
  476. * Get the base URL for the request.
  477. *
  478. * @param string $scheme
  479. * @param string $root
  480. * @return string
  481. */
  482. protected function getRootUrl($scheme, $root = null)
  483. {
  484. if (is_null($root))
  485. {
  486. $root = $this->forcedRoot ?: $this->request->root();
  487. }
  488. $start = starts_with($root, 'http://') ? 'http://' : 'https://';
  489. return preg_replace('~'.$start.'~', $scheme, $root, 1);
  490. }
  491. /**
  492. * Set the forced root URL.
  493. *
  494. * @param string $root
  495. * @return void
  496. */
  497. public function forceRootUrl($root)
  498. {
  499. $this->forcedRoot = rtrim($root, '/');
  500. }
  501. /**
  502. * Determine if the given path is a valid URL.
  503. *
  504. * @param string $path
  505. * @return bool
  506. */
  507. public function isValidUrl($path)
  508. {
  509. if (starts_with($path, ['#', '//', 'mailto:', 'tel:', 'http://', 'https://'])) return true;
  510. return filter_var($path, FILTER_VALIDATE_URL) !== false;
  511. }
  512. /**
  513. * Format the given URL segments into a single URL.
  514. *
  515. * @param string $root
  516. * @param string $path
  517. * @param string $tail
  518. * @return string
  519. */
  520. protected function trimUrl($root, $path, $tail = '')
  521. {
  522. return trim($root.'/'.trim($path.'/'.$tail, '/'), '/');
  523. }
  524. /**
  525. * Get the request instance.
  526. *
  527. * @return \Symfony\Component\HttpFoundation\Request
  528. */
  529. public function getRequest()
  530. {
  531. return $this->request;
  532. }
  533. /**
  534. * Set the current request instance.
  535. *
  536. * @param \Illuminate\Http\Request $request
  537. * @return void
  538. */
  539. public function setRequest(Request $request)
  540. {
  541. $this->request = $request;
  542. }
  543. /**
  544. * Set the route collection.
  545. *
  546. * @param \Illuminate\Routing\RouteCollection $routes
  547. * @return $this
  548. */
  549. public function setRoutes(RouteCollection $routes)
  550. {
  551. $this->routes = $routes;
  552. return $this;
  553. }
  554. /**
  555. * Get the previous URL from the session if possible.
  556. *
  557. * @return string|null
  558. */
  559. protected function getPreviousUrlFromSession()
  560. {
  561. $session = $this->getSession();
  562. return $session ? $session->previousUrl() : null;
  563. }
  564. /**
  565. * Get the session implementation from the resolver.
  566. *
  567. * @return \Illuminate\Session\Store
  568. */
  569. protected function getSession()
  570. {
  571. return call_user_func($this->sessionResolver ?: function() {});
  572. }
  573. /**
  574. * Set the session resolver for the generator.
  575. *
  576. * @param callable $sessionResolver
  577. * @return $this
  578. */
  579. public function setSessionResolver(callable $sessionResolver)
  580. {
  581. $this->sessionResolver = $sessionResolver;
  582. return $this;
  583. }
  584. /**
  585. * Set the root controller namespace.
  586. *
  587. * @param string $rootNamespace
  588. * @return $this
  589. */
  590. public function setRootControllerNamespace($rootNamespace)
  591. {
  592. $this->rootNamespace = $rootNamespace;
  593. return $this;
  594. }
  595. }