RoutingUrlGeneratorTest.php 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822
  1. <?php
  2. namespace Illuminate\Tests\Routing;
  3. use Illuminate\Contracts\Routing\UrlRoutable;
  4. use Illuminate\Database\Eloquent\Model;
  5. use Illuminate\Http\Request;
  6. use Illuminate\Routing\Exceptions\UrlGenerationException;
  7. use Illuminate\Routing\Route;
  8. use Illuminate\Routing\RouteCollection;
  9. use Illuminate\Routing\UrlGenerator;
  10. use InvalidArgumentException;
  11. use PHPUnit\Framework\TestCase;
  12. use Symfony\Component\HttpFoundation\Request as SymfonyRequest;
  13. use Symfony\Component\Routing\Exception\RouteNotFoundException;
  14. class RoutingUrlGeneratorTest extends TestCase
  15. {
  16. public function testBasicGeneration()
  17. {
  18. $url = new UrlGenerator(
  19. new RouteCollection,
  20. Request::create('http://www.foo.com/')
  21. );
  22. $this->assertSame('http://www.foo.com/foo/bar', $url->to('foo/bar'));
  23. $this->assertSame('https://www.foo.com/foo/bar', $url->to('foo/bar', [], true));
  24. $this->assertSame('https://www.foo.com/foo/bar/baz/boom', $url->to('foo/bar', ['baz', 'boom'], true));
  25. $this->assertSame('https://www.foo.com/foo/bar/baz?foo=bar', $url->to('foo/bar?foo=bar', ['baz'], true));
  26. /*
  27. * Test HTTPS request URL generation...
  28. */
  29. $url = new UrlGenerator(
  30. new RouteCollection,
  31. Request::create('https://www.foo.com/')
  32. );
  33. $this->assertSame('https://www.foo.com/foo/bar', $url->to('foo/bar'));
  34. /*
  35. * Test asset URL generation...
  36. */
  37. $url = new UrlGenerator(
  38. new RouteCollection,
  39. Request::create('http://www.foo.com/index.php/')
  40. );
  41. $this->assertSame('http://www.foo.com/foo/bar', $url->asset('foo/bar'));
  42. $this->assertSame('https://www.foo.com/foo/bar', $url->asset('foo/bar', true));
  43. }
  44. public function testBasicGenerationWithHostFormatting()
  45. {
  46. $url = new UrlGenerator(
  47. $routes = new RouteCollection,
  48. Request::create('http://www.foo.com/')
  49. );
  50. $route = new Route(['GET'], '/named-route', ['as' => 'plain']);
  51. $routes->add($route);
  52. $url->formatHostUsing(function ($host) {
  53. return str_replace('foo.com', 'foo.org', $host);
  54. });
  55. $this->assertSame('http://www.foo.org/foo/bar', $url->to('foo/bar'));
  56. $this->assertSame('/named-route', $url->route('plain', [], false));
  57. }
  58. public function testBasicGenerationWithRequestBaseUrlWithSubfolder()
  59. {
  60. $request = Request::create('http://www.foo.com/subfolder/foo/bar/subfolder/');
  61. $request->server->set('SCRIPT_FILENAME', '/var/www/laravel-project/public/subfolder/index.php');
  62. $request->server->set('PHP_SELF', '/subfolder/index.php');
  63. $url = new UrlGenerator(
  64. $routes = new RouteCollection,
  65. $request
  66. );
  67. $route = new Route(['GET'], 'foo/bar/subfolder', ['as' => 'foobar']);
  68. $routes->add($route);
  69. $this->assertSame('/subfolder', $request->getBaseUrl());
  70. $this->assertSame('/foo/bar/subfolder', $url->route('foobar', [], false));
  71. }
  72. public function testBasicGenerationWithRequestBaseUrlWithSubfolderAndFileSuffix()
  73. {
  74. $request = Request::create('http://www.foo.com/subfolder/index.php');
  75. $request->server->set('SCRIPT_FILENAME', '/var/www/laravel-project/public/subfolder/index.php');
  76. $request->server->set('PHP_SELF', '/subfolder/index.php');
  77. $url = new UrlGenerator(
  78. $routes = new RouteCollection,
  79. $request
  80. );
  81. $route = new Route(['GET'], 'foo/bar/subfolder', ['as' => 'foobar']);
  82. $routes->add($route);
  83. $this->assertSame('/subfolder', $request->getBasePath());
  84. $this->assertSame('/subfolder/index.php', $request->getBaseUrl());
  85. $this->assertSame('/foo/bar/subfolder', $url->route('foobar', [], false));
  86. }
  87. public function testBasicGenerationWithRequestBaseUrlWithFileSuffix()
  88. {
  89. $request = Request::create('http://www.foo.com/other.php');
  90. $request->server->set('SCRIPT_FILENAME', '/var/www/laravel-project/public/other.php');
  91. $request->server->set('PHP_SELF', '/other.php');
  92. $url = new UrlGenerator(
  93. $routes = new RouteCollection,
  94. $request
  95. );
  96. $route = new Route(['GET'], 'foo/bar/subfolder', ['as' => 'foobar']);
  97. $routes->add($route);
  98. $this->assertSame('', $request->getBasePath());
  99. $this->assertSame('/other.php', $request->getBaseUrl());
  100. $this->assertSame('/foo/bar/subfolder', $url->route('foobar', [], false));
  101. }
  102. public function testBasicGenerationWithPathFormatting()
  103. {
  104. $url = new UrlGenerator(
  105. $routes = new RouteCollection,
  106. Request::create('http://www.foo.com/')
  107. );
  108. $route = new Route(['GET'], '/named-route', ['as' => 'plain']);
  109. $routes->add($route);
  110. $url->formatPathUsing(function ($path) {
  111. return '/something'.$path;
  112. });
  113. $this->assertSame('http://www.foo.com/something/foo/bar', $url->to('foo/bar'));
  114. $this->assertSame('/something/named-route', $url->route('plain', [], false));
  115. }
  116. public function testUrlFormattersShouldReceiveTargetRoute()
  117. {
  118. $url = new UrlGenerator(
  119. $routes = new RouteCollection,
  120. Request::create('http://abc.com/')
  121. );
  122. $namedRoute = new Route(['GET'], '/bar', ['as' => 'plain', 'root' => 'bar.com', 'path' => 'foo']);
  123. $routes->add($namedRoute);
  124. $url->formatHostUsing(function ($root, $route) {
  125. return $route ? 'http://'.$route->getAction('root') : $root;
  126. });
  127. $url->formatPathUsing(function ($path, $route) {
  128. return $route ? '/'.$route->getAction('path') : $path;
  129. });
  130. $this->assertSame('http://abc.com/foo/bar', $url->to('foo/bar'));
  131. $this->assertSame('http://bar.com/foo', $url->route('plain'));
  132. }
  133. public function testBasicRouteGeneration()
  134. {
  135. $url = new UrlGenerator(
  136. $routes = new RouteCollection,
  137. Request::create('http://www.foo.com/')
  138. );
  139. /*
  140. * Empty Named Route
  141. */
  142. $route = new Route(['GET'], '/', ['as' => 'plain']);
  143. $routes->add($route);
  144. /*
  145. * Named Routes
  146. */
  147. $route = new Route(['GET'], 'foo/bar', ['as' => 'foo']);
  148. $routes->add($route);
  149. /*
  150. * Parameters...
  151. */
  152. $route = new Route(['GET'], 'foo/bar/{baz}/breeze/{boom}', ['as' => 'bar']);
  153. $routes->add($route);
  154. /*
  155. * Single Parameter...
  156. */
  157. $route = new Route(['GET'], 'foo/bar/{baz}', ['as' => 'foobar']);
  158. $routes->add($route);
  159. /*
  160. * Optional parameter
  161. */
  162. $route = new Route(['GET'], 'foo/bar/{baz?}', ['as' => 'optional']);
  163. $routes->add($route);
  164. /*
  165. * HTTPS...
  166. */
  167. $route = new Route(['GET'], 'foo/baz', ['as' => 'baz', 'https']);
  168. $routes->add($route);
  169. /*
  170. * Controller Route Route
  171. */
  172. $route = new Route(['GET'], 'foo/bam', ['controller' => 'foo@bar']);
  173. $routes->add($route);
  174. /*
  175. * Non ASCII routes
  176. */
  177. $route = new Route(['GET'], 'foo/bar/åαф/{baz}', ['as' => 'foobarbaz']);
  178. $routes->add($route);
  179. /*
  180. * Fragments
  181. */
  182. $route = new Route(['GET'], 'foo/bar#derp', ['as' => 'fragment']);
  183. $routes->add($route);
  184. /*
  185. * Invoke action
  186. */
  187. $route = new Route(['GET'], 'foo/invoke', ['controller' => 'InvokableActionStub']);
  188. $routes->add($route);
  189. /*
  190. * With Default Parameter
  191. */
  192. $url->defaults(['locale' => 'en']);
  193. $route = new Route(['GET'], 'foo', ['as' => 'defaults', 'domain' => '{locale}.example.com', function () {
  194. //
  195. }]);
  196. $routes->add($route);
  197. $this->assertSame('/', $url->route('plain', [], false));
  198. $this->assertSame('/?foo=bar', $url->route('plain', ['foo' => 'bar'], false));
  199. $this->assertSame('http://www.foo.com/foo/bar', $url->route('foo'));
  200. $this->assertSame('/foo/bar', $url->route('foo', [], false));
  201. $this->assertSame('/foo/bar?foo=bar', $url->route('foo', ['foo' => 'bar'], false));
  202. $this->assertSame('http://www.foo.com/foo/bar/taylor/breeze/otwell?fly=wall', $url->route('bar', ['taylor', 'otwell', 'fly' => 'wall']));
  203. $this->assertSame('http://www.foo.com/foo/bar/otwell/breeze/taylor?fly=wall', $url->route('bar', ['boom' => 'taylor', 'baz' => 'otwell', 'fly' => 'wall']));
  204. $this->assertSame('http://www.foo.com/foo/bar/0', $url->route('foobar', 0));
  205. $this->assertSame('http://www.foo.com/foo/bar/2', $url->route('foobar', 2));
  206. $this->assertSame('http://www.foo.com/foo/bar/taylor', $url->route('foobar', 'taylor'));
  207. $this->assertSame('/foo/bar/taylor/breeze/otwell?fly=wall', $url->route('bar', ['taylor', 'otwell', 'fly' => 'wall'], false));
  208. $this->assertSame('https://www.foo.com/foo/baz', $url->route('baz'));
  209. $this->assertSame('http://www.foo.com/foo/bam', $url->action('foo@bar'));
  210. $this->assertSame('http://www.foo.com/foo/bam', $url->action(['foo', 'bar']));
  211. $this->assertSame('http://www.foo.com/foo/invoke', $url->action('InvokableActionStub'));
  212. $this->assertSame('http://www.foo.com/foo/bar/taylor/breeze/otwell?wall&woz', $url->route('bar', ['wall', 'woz', 'boom' => 'otwell', 'baz' => 'taylor']));
  213. $this->assertSame('http://www.foo.com/foo/bar/taylor/breeze/otwell?wall&woz', $url->route('bar', ['taylor', 'otwell', 'wall', 'woz']));
  214. $this->assertSame('http://www.foo.com/foo/bar', $url->route('optional'));
  215. $this->assertSame('http://www.foo.com/foo/bar', $url->route('optional', ['baz' => null]));
  216. $this->assertSame('http://www.foo.com/foo/bar', $url->route('optional', ['baz' => '']));
  217. $this->assertSame('http://www.foo.com/foo/bar/0', $url->route('optional', ['baz' => 0]));
  218. $this->assertSame('http://www.foo.com/foo/bar/taylor', $url->route('optional', 'taylor'));
  219. $this->assertSame('http://www.foo.com/foo/bar/taylor', $url->route('optional', ['taylor']));
  220. $this->assertSame('http://www.foo.com/foo/bar/taylor?breeze', $url->route('optional', ['taylor', 'breeze']));
  221. $this->assertSame('http://www.foo.com/foo/bar/taylor?wall=woz', $url->route('optional', ['wall' => 'woz', 'taylor']));
  222. $this->assertSame('http://www.foo.com/foo/bar/taylor?wall=woz&breeze', $url->route('optional', ['wall' => 'woz', 'breeze', 'baz' => 'taylor']));
  223. $this->assertSame('http://www.foo.com/foo/bar?wall=woz', $url->route('optional', ['wall' => 'woz']));
  224. $this->assertSame('http://www.foo.com/foo/bar/%C3%A5%CE%B1%D1%84/%C3%A5%CE%B1%D1%84', $url->route('foobarbaz', ['baz' => 'åαф']));
  225. $this->assertSame('/foo/bar#derp', $url->route('fragment', [], false));
  226. $this->assertSame('/foo/bar?foo=bar#derp', $url->route('fragment', ['foo' => 'bar'], false));
  227. $this->assertSame('/foo/bar?baz=%C3%A5%CE%B1%D1%84#derp', $url->route('fragment', ['baz' => 'åαф'], false));
  228. $this->assertSame('http://en.example.com/foo', $url->route('defaults'));
  229. }
  230. public function testFluentRouteNameDefinitions()
  231. {
  232. $url = new UrlGenerator(
  233. $routes = new RouteCollection,
  234. Request::create('http://www.foo.com/')
  235. );
  236. /*
  237. * Named Routes
  238. */
  239. $route = new Route(['GET'], 'foo/bar', []);
  240. $route->name('foo');
  241. $routes->add($route);
  242. $routes->refreshNameLookups();
  243. $this->assertSame('http://www.foo.com/foo/bar', $url->route('foo'));
  244. }
  245. public function testControllerRoutesWithADefaultNamespace()
  246. {
  247. $url = new UrlGenerator(
  248. $routes = new RouteCollection,
  249. Request::create('http://www.foo.com/')
  250. );
  251. $url->setRootControllerNamespace('namespace');
  252. /*
  253. * Controller Route Route
  254. */
  255. $route = new Route(['GET'], 'foo/bar', ['controller' => 'namespace\foo@bar']);
  256. $routes->add($route);
  257. $route = new Route(['GET'], 'something/else', ['controller' => 'something\foo@bar']);
  258. $routes->add($route);
  259. $route = new Route(['GET'], 'foo/invoke', ['controller' => 'namespace\InvokableActionStub']);
  260. $routes->add($route);
  261. $this->assertSame('http://www.foo.com/foo/bar', $url->action('foo@bar'));
  262. $this->assertSame('http://www.foo.com/something/else', $url->action('\something\foo@bar'));
  263. $this->assertSame('http://www.foo.com/foo/invoke', $url->action('InvokableActionStub'));
  264. }
  265. public function testControllerRoutesOutsideOfDefaultNamespace()
  266. {
  267. $url = new UrlGenerator(
  268. $routes = new RouteCollection,
  269. Request::create('http://www.foo.com/')
  270. );
  271. $url->setRootControllerNamespace('namespace');
  272. $route = new Route(['GET'], 'root/namespace', ['controller' => '\root\namespace@foo']);
  273. $routes->add($route);
  274. $route = new Route(['GET'], 'invokable/namespace', ['controller' => '\root\namespace\InvokableActionStub']);
  275. $routes->add($route);
  276. $this->assertSame('http://www.foo.com/root/namespace', $url->action('\root\namespace@foo'));
  277. $this->assertSame('http://www.foo.com/invokable/namespace', $url->action('\root\namespace\InvokableActionStub'));
  278. }
  279. public function testRoutableInterfaceRouting()
  280. {
  281. $url = new UrlGenerator(
  282. $routes = new RouteCollection,
  283. Request::create('http://www.foo.com/')
  284. );
  285. $route = new Route(['GET'], 'foo/{bar}', ['as' => 'routable']);
  286. $routes->add($route);
  287. $model = new RoutableInterfaceStub;
  288. $model->key = 'routable';
  289. $this->assertSame('/foo/routable', $url->route('routable', [$model], false));
  290. }
  291. public function testRoutableInterfaceRoutingWithCustomBindingField()
  292. {
  293. $url = new UrlGenerator(
  294. $routes = new RouteCollection,
  295. Request::create('http://www.foo.com/')
  296. );
  297. $route = new Route(['GET'], 'foo/{bar:slug}', ['as' => 'routable']);
  298. $routes->add($route);
  299. $model = new RoutableInterfaceStub;
  300. $model->key = 'routable';
  301. $this->assertSame('/foo/test-slug', $url->route('routable', ['bar' => $model], false));
  302. $this->assertSame('/foo/test-slug', $url->route('routable', [$model], false));
  303. }
  304. public function testRoutableInterfaceRoutingWithSingleParameter()
  305. {
  306. $url = new UrlGenerator(
  307. $routes = new RouteCollection,
  308. Request::create('http://www.foo.com/')
  309. );
  310. $route = new Route(['GET'], 'foo/{bar}', ['as' => 'routable']);
  311. $routes->add($route);
  312. $model = new RoutableInterfaceStub;
  313. $model->key = 'routable';
  314. $this->assertSame('/foo/routable', $url->route('routable', $model, false));
  315. }
  316. public function testRoutesMaintainRequestScheme()
  317. {
  318. $url = new UrlGenerator(
  319. $routes = new RouteCollection,
  320. Request::create('https://www.foo.com/')
  321. );
  322. /*
  323. * Named Routes
  324. */
  325. $route = new Route(['GET'], 'foo/bar', ['as' => 'foo']);
  326. $routes->add($route);
  327. $this->assertSame('https://www.foo.com/foo/bar', $url->route('foo'));
  328. }
  329. public function testHttpOnlyRoutes()
  330. {
  331. $url = new UrlGenerator(
  332. $routes = new RouteCollection,
  333. Request::create('https://www.foo.com/')
  334. );
  335. /*
  336. * Named Routes
  337. */
  338. $route = new Route(['GET'], 'foo/bar', ['as' => 'foo', 'http']);
  339. $routes->add($route);
  340. $this->assertSame('http://www.foo.com/foo/bar', $url->route('foo'));
  341. }
  342. public function testRoutesWithDomains()
  343. {
  344. $url = new UrlGenerator(
  345. $routes = new RouteCollection,
  346. Request::create('http://www.foo.com/')
  347. );
  348. $route = new Route(['GET'], 'foo/bar', ['as' => 'foo', 'domain' => 'sub.foo.com']);
  349. $routes->add($route);
  350. /*
  351. * Wildcards & Domains...
  352. */
  353. $route = new Route(['GET'], 'foo/bar/{baz}', ['as' => 'bar', 'domain' => 'sub.{foo}.com']);
  354. $routes->add($route);
  355. $this->assertSame('http://sub.foo.com/foo/bar', $url->route('foo'));
  356. $this->assertSame('http://sub.taylor.com/foo/bar/otwell', $url->route('bar', ['taylor', 'otwell']));
  357. $this->assertSame('/foo/bar/otwell', $url->route('bar', ['taylor', 'otwell'], false));
  358. }
  359. public function testRoutesWithDomainsAndPorts()
  360. {
  361. $url = new UrlGenerator(
  362. $routes = new RouteCollection,
  363. Request::create('http://www.foo.com:8080/')
  364. );
  365. $route = new Route(['GET'], 'foo/bar', ['as' => 'foo', 'domain' => 'sub.foo.com']);
  366. $routes->add($route);
  367. /*
  368. * Wildcards & Domains...
  369. */
  370. $route = new Route(['GET'], 'foo/bar/{baz}', ['as' => 'bar', 'domain' => 'sub.{foo}.com']);
  371. $routes->add($route);
  372. $this->assertSame('http://sub.foo.com:8080/foo/bar', $url->route('foo'));
  373. $this->assertSame('http://sub.taylor.com:8080/foo/bar/otwell', $url->route('bar', ['taylor', 'otwell']));
  374. }
  375. public function testRoutesWithDomainsStripsProtocols()
  376. {
  377. /*
  378. * http:// Route
  379. */
  380. $url = new UrlGenerator(
  381. $routes = new RouteCollection,
  382. Request::create('http://www.foo.com/')
  383. );
  384. $route = new Route(['GET'], 'foo/bar', ['as' => 'foo', 'domain' => 'http://sub.foo.com']);
  385. $routes->add($route);
  386. $this->assertSame('http://sub.foo.com/foo/bar', $url->route('foo'));
  387. /*
  388. * https:// Route
  389. */
  390. $url = new UrlGenerator(
  391. $routes = new RouteCollection,
  392. Request::create('https://www.foo.com/')
  393. );
  394. $route = new Route(['GET'], 'foo/bar', ['as' => 'foo', 'domain' => 'https://sub.foo.com']);
  395. $routes->add($route);
  396. $this->assertSame('https://sub.foo.com/foo/bar', $url->route('foo'));
  397. }
  398. public function testHttpsRoutesWithDomains()
  399. {
  400. $url = new UrlGenerator(
  401. $routes = new RouteCollection,
  402. Request::create('https://foo.com/')
  403. );
  404. /*
  405. * When on HTTPS, no need to specify 443
  406. */
  407. $route = new Route(['GET'], 'foo/bar', ['as' => 'baz', 'domain' => 'sub.foo.com']);
  408. $routes->add($route);
  409. $this->assertSame('https://sub.foo.com/foo/bar', $url->route('baz'));
  410. }
  411. public function testRoutesWithDomainsThroughProxy()
  412. {
  413. Request::setTrustedProxies(['10.0.0.1'], SymfonyRequest::HEADER_X_FORWARDED_ALL);
  414. $url = new UrlGenerator(
  415. $routes = new RouteCollection,
  416. Request::create('http://www.foo.com/', 'GET', [], [], [], ['REMOTE_ADDR' => '10.0.0.1', 'HTTP_X_FORWARDED_PORT' => '80'])
  417. );
  418. $route = new Route(['GET'], 'foo/bar', ['as' => 'foo', 'domain' => 'sub.foo.com']);
  419. $routes->add($route);
  420. $this->assertSame('http://sub.foo.com/foo/bar', $url->route('foo'));
  421. }
  422. public function providerRouteParameters()
  423. {
  424. return [
  425. [['test' => 123]],
  426. [['one' => null, 'test' => 123]],
  427. [['one' => '', 'test' => 123]],
  428. ];
  429. }
  430. /**
  431. * @dataProvider providerRouteParameters
  432. */
  433. public function testUrlGenerationForControllersRequiresPassingOfRequiredParameters($parameters)
  434. {
  435. $this->expectException(UrlGenerationException::class);
  436. $url = new UrlGenerator(
  437. $routes = new RouteCollection,
  438. Request::create('http://www.foo.com:8080/')
  439. );
  440. $route = new Route(['GET'], 'foo/{one}/{two?}/{three?}', ['as' => 'foo', function () {
  441. //
  442. }]);
  443. $routes->add($route);
  444. $this->assertSame('http://www.foo.com:8080/foo?test=123', $url->route('foo', $parameters));
  445. }
  446. public function provideParametersAndExpectedMeaningfulExceptionMessages()
  447. {
  448. return [
  449. 'Missing parameters "one", "two" and "three"' => [
  450. [],
  451. 'Missing required parameters for [Route: foo] [URI: foo/{one}/{two}/{three}/{four?}] [Missing parameters: one, two, three].',
  452. ],
  453. 'Missing parameters "two" and "three"' => [
  454. ['one' => '123'],
  455. 'Missing required parameters for [Route: foo] [URI: foo/{one}/{two}/{three}/{four?}] [Missing parameters: two, three].',
  456. ],
  457. 'Missing parameters "one" and "three"' => [
  458. ['two' => '123'],
  459. 'Missing required parameters for [Route: foo] [URI: foo/{one}/{two}/{three}/{four?}] [Missing parameters: one, three].',
  460. ],
  461. 'Missing parameters "one" and "two"' => [
  462. ['three' => '123'],
  463. 'Missing required parameters for [Route: foo] [URI: foo/{one}/{two}/{three}/{four?}] [Missing parameters: one, two].',
  464. ],
  465. 'Missing parameter "three"' => [
  466. ['one' => '123', 'two' => '123'],
  467. 'Missing required parameter for [Route: foo] [URI: foo/{one}/{two}/{three}/{four?}] [Missing parameter: three].',
  468. ],
  469. 'Missing parameter "two"' => [
  470. ['one' => '123', 'three' => '123'],
  471. 'Missing required parameter for [Route: foo] [URI: foo/{one}/{two}/{three}/{four?}] [Missing parameter: two].',
  472. ],
  473. 'Missing parameter "one"' => [
  474. ['two' => '123', 'three' => '123'],
  475. 'Missing required parameter for [Route: foo] [URI: foo/{one}/{two}/{three}/{four?}] [Missing parameter: one].',
  476. ],
  477. ];
  478. }
  479. /**
  480. * @dataProvider provideParametersAndExpectedMeaningfulExceptionMessages
  481. */
  482. public function testUrlGenerationThrowsExceptionForMissingParametersWithMeaningfulMessage($parameters, $expectedMeaningfulExceptionMessage)
  483. {
  484. $this->expectException(UrlGenerationException::class);
  485. $this->expectExceptionMessage($expectedMeaningfulExceptionMessage);
  486. $url = new UrlGenerator(
  487. $routes = new RouteCollection,
  488. Request::create('http://www.foo.com:8080/')
  489. );
  490. $route = new Route(['GET'], 'foo/{one}/{two}/{three}/{four?}', ['as' => 'foo', function () {
  491. //
  492. }]);
  493. $routes->add($route);
  494. $url->route('foo', $parameters);
  495. }
  496. public function testForceRootUrl()
  497. {
  498. $url = new UrlGenerator(
  499. $routes = new RouteCollection,
  500. Request::create('http://www.foo.com/')
  501. );
  502. $url->forceRootUrl('https://www.bar.com');
  503. $this->assertSame('http://www.bar.com/foo/bar', $url->to('foo/bar'));
  504. // Ensure trailing slash is trimmed from root URL as UrlGenerator already handles this
  505. $url->forceRootUrl('http://www.foo.com/');
  506. $this->assertSame('http://www.foo.com/bar', $url->to('/bar'));
  507. /*
  508. * Route Based...
  509. */
  510. $url = new UrlGenerator(
  511. $routes = new RouteCollection,
  512. Request::create('http://www.foo.com/')
  513. );
  514. $url->forceScheme('https');
  515. $route = new Route(['GET'], '/foo', ['as' => 'plain']);
  516. $routes->add($route);
  517. $this->assertSame('https://www.foo.com/foo', $url->route('plain'));
  518. $url->forceRootUrl('https://www.bar.com');
  519. $this->assertSame('https://www.bar.com/foo', $url->route('plain'));
  520. }
  521. public function testPrevious()
  522. {
  523. $url = new UrlGenerator(
  524. new RouteCollection,
  525. Request::create('http://www.foo.com/')
  526. );
  527. $url->getRequest()->headers->set('referer', 'http://www.bar.com/');
  528. $this->assertSame('http://www.bar.com/', $url->previous());
  529. $url->getRequest()->headers->remove('referer');
  530. $this->assertEquals($url->to('/'), $url->previous());
  531. $this->assertEquals($url->to('/foo'), $url->previous('/foo'));
  532. }
  533. public function testRouteNotDefinedException()
  534. {
  535. $this->expectException(RouteNotFoundException::class);
  536. $this->expectExceptionMessage('Route [not_exists_route] not defined.');
  537. $url = new UrlGenerator(
  538. new RouteCollection,
  539. Request::create('http://www.foo.com/')
  540. );
  541. $url->route('not_exists_route');
  542. }
  543. public function testSignedUrl()
  544. {
  545. $url = new UrlGenerator(
  546. $routes = new RouteCollection,
  547. $request = Request::create('http://www.foo.com/')
  548. );
  549. $url->setKeyResolver(function () {
  550. return 'secret';
  551. });
  552. $route = new Route(['GET'], 'foo', ['as' => 'foo', function () {
  553. //
  554. }]);
  555. $routes->add($route);
  556. $request = Request::create($url->signedRoute('foo'));
  557. $this->assertTrue($url->hasValidSignature($request));
  558. $request = Request::create($url->signedRoute('foo').'?tempered=true');
  559. $this->assertFalse($url->hasValidSignature($request));
  560. }
  561. public function testSignedUrlImplicitModelBinding()
  562. {
  563. $url = new UrlGenerator(
  564. $routes = new RouteCollection,
  565. $request = Request::create('http://www.foo.com/')
  566. );
  567. $url->setKeyResolver(function () {
  568. return 'secret';
  569. });
  570. $route = new Route(['GET'], 'foo/{user:uuid}', ['as' => 'foo', function () {
  571. //
  572. }]);
  573. $routes->add($route);
  574. $user = new RoutingUrlGeneratorTestUser(['uuid' => '0231d4ac-e9e3-4452-a89a-4427cfb23c3e']);
  575. $request = Request::create($url->signedRoute('foo', $user));
  576. $this->assertTrue($url->hasValidSignature($request));
  577. }
  578. public function testSignedRelativeUrl()
  579. {
  580. $url = new UrlGenerator(
  581. $routes = new RouteCollection,
  582. $request = Request::create('http://www.foo.com/')
  583. );
  584. $url->setKeyResolver(function () {
  585. return 'secret';
  586. });
  587. $route = new Route(['GET'], 'foo', ['as' => 'foo', function () {
  588. //
  589. }]);
  590. $routes->add($route);
  591. $result = $url->signedRoute('foo', [], null, false);
  592. $request = Request::create($result);
  593. $this->assertTrue($url->hasValidSignature($request, false));
  594. $request = Request::create($url->signedRoute('foo', [], null, false).'?tempered=true');
  595. $this->assertFalse($url->hasValidSignature($request, false));
  596. }
  597. public function testSignedUrlParameterCannotBeNamedSignature()
  598. {
  599. $url = new UrlGenerator(
  600. $routes = new RouteCollection,
  601. $request = Request::create('http://www.foo.com/')
  602. );
  603. $url->setKeyResolver(function () {
  604. return 'secret';
  605. });
  606. $route = new Route(['GET'], 'foo/{signature}', ['as' => 'foo', function () {
  607. //
  608. }]);
  609. $routes->add($route);
  610. $this->expectException(InvalidArgumentException::class);
  611. $this->expectExceptionMessage('reserved');
  612. Request::create($url->signedRoute('foo', ['signature' => 'bar']));
  613. }
  614. public function testSignedUrlParameterCannotBeNamedExpires()
  615. {
  616. $url = new UrlGenerator(
  617. $routes = new RouteCollection,
  618. $request = Request::create('http://www.foo.com/')
  619. );
  620. $url->setKeyResolver(function () {
  621. return 'secret';
  622. });
  623. $route = new Route(['GET'], 'foo/{expires}', ['as' => 'foo', function () {
  624. //
  625. }]);
  626. $routes->add($route);
  627. $this->expectException(InvalidArgumentException::class);
  628. $this->expectExceptionMessage('reserved');
  629. Request::create($url->signedRoute('foo', ['expires' => 253402300799]));
  630. }
  631. }
  632. class RoutableInterfaceStub implements UrlRoutable
  633. {
  634. public $key;
  635. public $slug = 'test-slug';
  636. public function getRouteKey()
  637. {
  638. return $this->{$this->getRouteKeyName()};
  639. }
  640. public function getRouteKeyName()
  641. {
  642. return 'key';
  643. }
  644. public function resolveRouteBinding($routeKey, $field = null)
  645. {
  646. //
  647. }
  648. public function resolveChildRouteBinding($childType, $routeKey, $field = null)
  649. {
  650. //
  651. }
  652. }
  653. class InvokableActionStub
  654. {
  655. public function __invoke()
  656. {
  657. return 'hello';
  658. }
  659. }
  660. class RoutingUrlGeneratorTestUser extends Model
  661. {
  662. protected $fillable = ['uuid'];
  663. }