PageRenderTime 46ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

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

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