PageRenderTime 51ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

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

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