UrlGeneratorTest.php 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773
  1. <?php
  2. /*
  3. * This file is part of the Symfony package.
  4. *
  5. * (c) Fabien Potencier <fabien@symfony.com>
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. namespace Symfony\Component\Routing\Tests\Generator;
  11. use PHPUnit\Framework\TestCase;
  12. use Symfony\Component\Routing\Generator\UrlGenerator;
  13. use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
  14. use Symfony\Component\Routing\RequestContext;
  15. use Symfony\Component\Routing\Route;
  16. use Symfony\Component\Routing\RouteCollection;
  17. class UrlGeneratorTest extends TestCase
  18. {
  19. public function testAbsoluteUrlWithPort80()
  20. {
  21. $routes = $this->getRoutes('test', new Route('/testing'));
  22. $url = $this->getGenerator($routes)->generate('test', [], UrlGeneratorInterface::ABSOLUTE_URL);
  23. $this->assertEquals('http://localhost/app.php/testing', $url);
  24. }
  25. public function testAbsoluteSecureUrlWithPort443()
  26. {
  27. $routes = $this->getRoutes('test', new Route('/testing'));
  28. $url = $this->getGenerator($routes, ['scheme' => 'https'])->generate('test', [], UrlGeneratorInterface::ABSOLUTE_URL);
  29. $this->assertEquals('https://localhost/app.php/testing', $url);
  30. }
  31. public function testAbsoluteUrlWithNonStandardPort()
  32. {
  33. $routes = $this->getRoutes('test', new Route('/testing'));
  34. $url = $this->getGenerator($routes, ['httpPort' => 8080])->generate('test', [], UrlGeneratorInterface::ABSOLUTE_URL);
  35. $this->assertEquals('http://localhost:8080/app.php/testing', $url);
  36. }
  37. public function testAbsoluteSecureUrlWithNonStandardPort()
  38. {
  39. $routes = $this->getRoutes('test', new Route('/testing'));
  40. $url = $this->getGenerator($routes, ['httpsPort' => 8080, 'scheme' => 'https'])->generate('test', [], UrlGeneratorInterface::ABSOLUTE_URL);
  41. $this->assertEquals('https://localhost:8080/app.php/testing', $url);
  42. }
  43. public function testRelativeUrlWithoutParameters()
  44. {
  45. $routes = $this->getRoutes('test', new Route('/testing'));
  46. $url = $this->getGenerator($routes)->generate('test', [], UrlGeneratorInterface::ABSOLUTE_PATH);
  47. $this->assertEquals('/app.php/testing', $url);
  48. }
  49. public function testRelativeUrlWithParameter()
  50. {
  51. $routes = $this->getRoutes('test', new Route('/testing/{foo}'));
  52. $url = $this->getGenerator($routes)->generate('test', ['foo' => 'bar'], UrlGeneratorInterface::ABSOLUTE_PATH);
  53. $this->assertEquals('/app.php/testing/bar', $url);
  54. }
  55. public function testRelativeUrlWithNullParameter()
  56. {
  57. $routes = $this->getRoutes('test', new Route('/testing.{format}', ['format' => null]));
  58. $url = $this->getGenerator($routes)->generate('test', [], UrlGeneratorInterface::ABSOLUTE_PATH);
  59. $this->assertEquals('/app.php/testing', $url);
  60. }
  61. /**
  62. * @expectedException \Symfony\Component\Routing\Exception\InvalidParameterException
  63. */
  64. public function testRelativeUrlWithNullParameterButNotOptional()
  65. {
  66. $routes = $this->getRoutes('test', new Route('/testing/{foo}/bar', ['foo' => null]));
  67. // This must raise an exception because the default requirement for "foo" is "[^/]+" which is not met with these params.
  68. // Generating path "/testing//bar" would be wrong as matching this route would fail.
  69. $this->getGenerator($routes)->generate('test', [], UrlGeneratorInterface::ABSOLUTE_PATH);
  70. }
  71. public function testRelativeUrlWithOptionalZeroParameter()
  72. {
  73. $routes = $this->getRoutes('test', new Route('/testing/{page}'));
  74. $url = $this->getGenerator($routes)->generate('test', ['page' => 0], UrlGeneratorInterface::ABSOLUTE_PATH);
  75. $this->assertEquals('/app.php/testing/0', $url);
  76. }
  77. public function testNotPassedOptionalParameterInBetween()
  78. {
  79. $routes = $this->getRoutes('test', new Route('/{slug}/{page}', ['slug' => 'index', 'page' => 0]));
  80. $this->assertSame('/app.php/index/1', $this->getGenerator($routes)->generate('test', ['page' => 1]));
  81. $this->assertSame('/app.php/', $this->getGenerator($routes)->generate('test'));
  82. }
  83. public function testRelativeUrlWithExtraParameters()
  84. {
  85. $routes = $this->getRoutes('test', new Route('/testing'));
  86. $url = $this->getGenerator($routes)->generate('test', ['foo' => 'bar'], UrlGeneratorInterface::ABSOLUTE_PATH);
  87. $this->assertEquals('/app.php/testing?foo=bar', $url);
  88. }
  89. public function testAbsoluteUrlWithExtraParameters()
  90. {
  91. $routes = $this->getRoutes('test', new Route('/testing'));
  92. $url = $this->getGenerator($routes)->generate('test', ['foo' => 'bar'], UrlGeneratorInterface::ABSOLUTE_URL);
  93. $this->assertEquals('http://localhost/app.php/testing?foo=bar', $url);
  94. }
  95. public function testUrlWithNullExtraParameters()
  96. {
  97. $routes = $this->getRoutes('test', new Route('/testing'));
  98. $url = $this->getGenerator($routes)->generate('test', ['foo' => null], UrlGeneratorInterface::ABSOLUTE_URL);
  99. $this->assertEquals('http://localhost/app.php/testing', $url);
  100. }
  101. public function testUrlWithExtraParametersFromGlobals()
  102. {
  103. $routes = $this->getRoutes('test', new Route('/testing'));
  104. $generator = $this->getGenerator($routes);
  105. $context = new RequestContext('/app.php');
  106. $context->setParameter('bar', 'bar');
  107. $generator->setContext($context);
  108. $url = $generator->generate('test', ['foo' => 'bar']);
  109. $this->assertEquals('/app.php/testing?foo=bar', $url);
  110. }
  111. public function testUrlWithGlobalParameter()
  112. {
  113. $routes = $this->getRoutes('test', new Route('/testing/{foo}'));
  114. $generator = $this->getGenerator($routes);
  115. $context = new RequestContext('/app.php');
  116. $context->setParameter('foo', 'bar');
  117. $generator->setContext($context);
  118. $url = $generator->generate('test', []);
  119. $this->assertEquals('/app.php/testing/bar', $url);
  120. }
  121. public function testGlobalParameterHasHigherPriorityThanDefault()
  122. {
  123. $routes = $this->getRoutes('test', new Route('/{_locale}', ['_locale' => 'en']));
  124. $generator = $this->getGenerator($routes);
  125. $context = new RequestContext('/app.php');
  126. $context->setParameter('_locale', 'de');
  127. $generator->setContext($context);
  128. $url = $generator->generate('test', []);
  129. $this->assertSame('/app.php/de', $url);
  130. }
  131. /**
  132. * @expectedException \Symfony\Component\Routing\Exception\RouteNotFoundException
  133. */
  134. public function testGenerateWithoutRoutes()
  135. {
  136. $routes = $this->getRoutes('foo', new Route('/testing/{foo}'));
  137. $this->getGenerator($routes)->generate('test', [], UrlGeneratorInterface::ABSOLUTE_URL);
  138. }
  139. /**
  140. * @expectedException \Symfony\Component\Routing\Exception\MissingMandatoryParametersException
  141. */
  142. public function testGenerateForRouteWithoutMandatoryParameter()
  143. {
  144. $routes = $this->getRoutes('test', new Route('/testing/{foo}'));
  145. $this->getGenerator($routes)->generate('test', [], UrlGeneratorInterface::ABSOLUTE_URL);
  146. }
  147. /**
  148. * @expectedException \Symfony\Component\Routing\Exception\InvalidParameterException
  149. */
  150. public function testGenerateForRouteWithInvalidOptionalParameter()
  151. {
  152. $routes = $this->getRoutes('test', new Route('/testing/{foo}', ['foo' => '1'], ['foo' => 'd+']));
  153. $this->getGenerator($routes)->generate('test', ['foo' => 'bar'], UrlGeneratorInterface::ABSOLUTE_URL);
  154. }
  155. /**
  156. * @expectedException \Symfony\Component\Routing\Exception\InvalidParameterException
  157. */
  158. public function testGenerateForRouteWithInvalidParameter()
  159. {
  160. $routes = $this->getRoutes('test', new Route('/testing/{foo}', [], ['foo' => '1|2']));
  161. $this->getGenerator($routes)->generate('test', ['foo' => '0'], UrlGeneratorInterface::ABSOLUTE_URL);
  162. }
  163. public function testGenerateForRouteWithInvalidOptionalParameterNonStrict()
  164. {
  165. $routes = $this->getRoutes('test', new Route('/testing/{foo}', ['foo' => '1'], ['foo' => 'd+']));
  166. $generator = $this->getGenerator($routes);
  167. $generator->setStrictRequirements(false);
  168. $this->assertNull($generator->generate('test', ['foo' => 'bar'], UrlGeneratorInterface::ABSOLUTE_URL));
  169. }
  170. public function testGenerateForRouteWithInvalidOptionalParameterNonStrictWithLogger()
  171. {
  172. $routes = $this->getRoutes('test', new Route('/testing/{foo}', ['foo' => '1'], ['foo' => 'd+']));
  173. $logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock();
  174. $logger->expects($this->once())
  175. ->method('error');
  176. $generator = $this->getGenerator($routes, [], $logger);
  177. $generator->setStrictRequirements(false);
  178. $this->assertNull($generator->generate('test', ['foo' => 'bar'], UrlGeneratorInterface::ABSOLUTE_URL));
  179. }
  180. public function testGenerateForRouteWithInvalidParameterButDisabledRequirementsCheck()
  181. {
  182. $routes = $this->getRoutes('test', new Route('/testing/{foo}', ['foo' => '1'], ['foo' => 'd+']));
  183. $generator = $this->getGenerator($routes);
  184. $generator->setStrictRequirements(null);
  185. $this->assertSame('/app.php/testing/bar', $generator->generate('test', ['foo' => 'bar']));
  186. }
  187. /**
  188. * @expectedException \Symfony\Component\Routing\Exception\InvalidParameterException
  189. */
  190. public function testGenerateForRouteWithInvalidMandatoryParameter()
  191. {
  192. $routes = $this->getRoutes('test', new Route('/testing/{foo}', [], ['foo' => 'd+']));
  193. $this->getGenerator($routes)->generate('test', ['foo' => 'bar'], UrlGeneratorInterface::ABSOLUTE_URL);
  194. }
  195. /**
  196. * @expectedException \Symfony\Component\Routing\Exception\InvalidParameterException
  197. */
  198. public function testGenerateForRouteWithInvalidUtf8Parameter()
  199. {
  200. $routes = $this->getRoutes('test', new Route('/testing/{foo}', [], ['foo' => '\pL+'], ['utf8' => true]));
  201. $this->getGenerator($routes)->generate('test', ['foo' => 'abc123'], UrlGeneratorInterface::ABSOLUTE_URL);
  202. }
  203. /**
  204. * @expectedException \Symfony\Component\Routing\Exception\InvalidParameterException
  205. */
  206. public function testRequiredParamAndEmptyPassed()
  207. {
  208. $routes = $this->getRoutes('test', new Route('/{slug}', [], ['slug' => '.+']));
  209. $this->getGenerator($routes)->generate('test', ['slug' => '']);
  210. }
  211. public function testSchemeRequirementDoesNothingIfSameCurrentScheme()
  212. {
  213. $routes = $this->getRoutes('test', new Route('/', [], [], [], '', ['http']));
  214. $this->assertEquals('/app.php/', $this->getGenerator($routes)->generate('test'));
  215. $routes = $this->getRoutes('test', new Route('/', [], [], [], '', ['https']));
  216. $this->assertEquals('/app.php/', $this->getGenerator($routes, ['scheme' => 'https'])->generate('test'));
  217. }
  218. public function testSchemeRequirementForcesAbsoluteUrl()
  219. {
  220. $routes = $this->getRoutes('test', new Route('/', [], [], [], '', ['https']));
  221. $this->assertEquals('https://localhost/app.php/', $this->getGenerator($routes)->generate('test'));
  222. $routes = $this->getRoutes('test', new Route('/', [], [], [], '', ['http']));
  223. $this->assertEquals('http://localhost/app.php/', $this->getGenerator($routes, ['scheme' => 'https'])->generate('test'));
  224. }
  225. public function testSchemeRequirementCreatesUrlForFirstRequiredScheme()
  226. {
  227. $routes = $this->getRoutes('test', new Route('/', [], [], [], '', ['Ftp', 'https']));
  228. $this->assertEquals('ftp://localhost/app.php/', $this->getGenerator($routes)->generate('test'));
  229. }
  230. public function testPathWithTwoStartingSlashes()
  231. {
  232. $routes = $this->getRoutes('test', new Route('//path-and-not-domain'));
  233. // this must not generate '//path-and-not-domain' because that would be a network path
  234. $this->assertSame('/path-and-not-domain', $this->getGenerator($routes, ['BaseUrl' => ''])->generate('test'));
  235. }
  236. public function testNoTrailingSlashForMultipleOptionalParameters()
  237. {
  238. $routes = $this->getRoutes('test', new Route('/category/{slug1}/{slug2}/{slug3}', ['slug2' => null, 'slug3' => null]));
  239. $this->assertEquals('/app.php/category/foo', $this->getGenerator($routes)->generate('test', ['slug1' => 'foo']));
  240. }
  241. public function testWithAnIntegerAsADefaultValue()
  242. {
  243. $routes = $this->getRoutes('test', new Route('/{default}', ['default' => 0]));
  244. $this->assertEquals('/app.php/foo', $this->getGenerator($routes)->generate('test', ['default' => 'foo']));
  245. }
  246. public function testNullForOptionalParameterIsIgnored()
  247. {
  248. $routes = $this->getRoutes('test', new Route('/test/{default}', ['default' => 0]));
  249. $this->assertEquals('/app.php/test', $this->getGenerator($routes)->generate('test', ['default' => null]));
  250. }
  251. public function testQueryParamSameAsDefault()
  252. {
  253. $routes = $this->getRoutes('test', new Route('/test', ['page' => 1]));
  254. $this->assertSame('/app.php/test?page=2', $this->getGenerator($routes)->generate('test', ['page' => 2]));
  255. $this->assertSame('/app.php/test', $this->getGenerator($routes)->generate('test', ['page' => 1]));
  256. $this->assertSame('/app.php/test', $this->getGenerator($routes)->generate('test', ['page' => '1']));
  257. $this->assertSame('/app.php/test', $this->getGenerator($routes)->generate('test'));
  258. }
  259. public function testArrayQueryParamSameAsDefault()
  260. {
  261. $routes = $this->getRoutes('test', new Route('/test', ['array' => ['foo', 'bar']]));
  262. $this->assertSame('/app.php/test?array%5B0%5D=bar&array%5B1%5D=foo', $this->getGenerator($routes)->generate('test', ['array' => ['bar', 'foo']]));
  263. $this->assertSame('/app.php/test?array%5Ba%5D=foo&array%5Bb%5D=bar', $this->getGenerator($routes)->generate('test', ['array' => ['a' => 'foo', 'b' => 'bar']]));
  264. $this->assertSame('/app.php/test', $this->getGenerator($routes)->generate('test', ['array' => ['foo', 'bar']]));
  265. $this->assertSame('/app.php/test', $this->getGenerator($routes)->generate('test', ['array' => [1 => 'bar', 0 => 'foo']]));
  266. $this->assertSame('/app.php/test', $this->getGenerator($routes)->generate('test'));
  267. }
  268. public function testGenerateWithSpecialRouteName()
  269. {
  270. $routes = $this->getRoutes('$péß^a|', new Route('/bar'));
  271. $this->assertSame('/app.php/bar', $this->getGenerator($routes)->generate('$péß^a|'));
  272. }
  273. public function testUrlEncoding()
  274. {
  275. $expectedPath = '/app.php/@:%5B%5D/%28%29*%27%22%20+,;-._~%26%24%3C%3E|%7B%7D%25%5C%5E%60!%3Ffoo=bar%23id'
  276. .'/@:%5B%5D/%28%29*%27%22%20+,;-._~%26%24%3C%3E|%7B%7D%25%5C%5E%60!%3Ffoo=bar%23id'
  277. .'?query=%40%3A%5B%5D/%28%29%2A%27%22%20%2B%2C%3B-._~%26%24%3C%3E%7C%7B%7D%25%5C%5E%60%21%3Ffoo%3Dbar%23id';
  278. // This tests the encoding of reserved characters that are used for delimiting of URI components (defined in RFC 3986)
  279. // and other special ASCII chars. These chars are tested as static text path, variable path and query param.
  280. $chars = '@:[]/()*\'" +,;-._~&$<>|{}%\\^`!?foo=bar#id';
  281. $routes = $this->getRoutes('test', new Route("/$chars/{varpath}", [], ['varpath' => '.+']));
  282. $this->assertSame($expectedPath, $this->getGenerator($routes)->generate('test', [
  283. 'varpath' => $chars,
  284. 'query' => $chars,
  285. ]));
  286. }
  287. public function testEncodingOfRelativePathSegments()
  288. {
  289. $routes = $this->getRoutes('test', new Route('/dir/../dir/..'));
  290. $this->assertSame('/app.php/dir/%2E%2E/dir/%2E%2E', $this->getGenerator($routes)->generate('test'));
  291. $routes = $this->getRoutes('test', new Route('/dir/./dir/.'));
  292. $this->assertSame('/app.php/dir/%2E/dir/%2E', $this->getGenerator($routes)->generate('test'));
  293. $routes = $this->getRoutes('test', new Route('/a./.a/a../..a/...'));
  294. $this->assertSame('/app.php/a./.a/a../..a/...', $this->getGenerator($routes)->generate('test'));
  295. }
  296. public function testAdjacentVariables()
  297. {
  298. $routes = $this->getRoutes('test', new Route('/{x}{y}{z}.{_format}', ['z' => 'default-z', '_format' => 'html'], ['y' => '\d+']));
  299. $generator = $this->getGenerator($routes);
  300. $this->assertSame('/app.php/foo123', $generator->generate('test', ['x' => 'foo', 'y' => '123']));
  301. $this->assertSame('/app.php/foo123bar.xml', $generator->generate('test', ['x' => 'foo', 'y' => '123', 'z' => 'bar', '_format' => 'xml']));
  302. // The default requirement for 'x' should not allow the separator '.' in this case because it would otherwise match everything
  303. // and following optional variables like _format could never match.
  304. $this->{method_exists($this, $_ = 'expectException') ? $_ : 'setExpectedException'}('Symfony\Component\Routing\Exception\InvalidParameterException');
  305. $generator->generate('test', ['x' => 'do.t', 'y' => '123', 'z' => 'bar', '_format' => 'xml']);
  306. }
  307. public function testOptionalVariableWithNoRealSeparator()
  308. {
  309. $routes = $this->getRoutes('test', new Route('/get{what}', ['what' => 'All']));
  310. $generator = $this->getGenerator($routes);
  311. $this->assertSame('/app.php/get', $generator->generate('test'));
  312. $this->assertSame('/app.php/getSites', $generator->generate('test', ['what' => 'Sites']));
  313. }
  314. public function testRequiredVariableWithNoRealSeparator()
  315. {
  316. $routes = $this->getRoutes('test', new Route('/get{what}Suffix'));
  317. $generator = $this->getGenerator($routes);
  318. $this->assertSame('/app.php/getSitesSuffix', $generator->generate('test', ['what' => 'Sites']));
  319. }
  320. public function testDefaultRequirementOfVariable()
  321. {
  322. $routes = $this->getRoutes('test', new Route('/{page}.{_format}'));
  323. $generator = $this->getGenerator($routes);
  324. $this->assertSame('/app.php/index.mobile.html', $generator->generate('test', ['page' => 'index', '_format' => 'mobile.html']));
  325. }
  326. /**
  327. * @expectedException \Symfony\Component\Routing\Exception\InvalidParameterException
  328. */
  329. public function testDefaultRequirementOfVariableDisallowsSlash()
  330. {
  331. $routes = $this->getRoutes('test', new Route('/{page}.{_format}'));
  332. $this->getGenerator($routes)->generate('test', ['page' => 'index', '_format' => 'sl/ash']);
  333. }
  334. /**
  335. * @expectedException \Symfony\Component\Routing\Exception\InvalidParameterException
  336. */
  337. public function testDefaultRequirementOfVariableDisallowsNextSeparator()
  338. {
  339. $routes = $this->getRoutes('test', new Route('/{page}.{_format}'));
  340. $this->getGenerator($routes)->generate('test', ['page' => 'do.t', '_format' => 'html']);
  341. }
  342. public function testWithHostDifferentFromContext()
  343. {
  344. $routes = $this->getRoutes('test', new Route('/{name}', [], [], [], '{locale}.example.com'));
  345. $this->assertEquals('//fr.example.com/app.php/Fabien', $this->getGenerator($routes)->generate('test', ['name' => 'Fabien', 'locale' => 'fr']));
  346. }
  347. public function testWithHostSameAsContext()
  348. {
  349. $routes = $this->getRoutes('test', new Route('/{name}', [], [], [], '{locale}.example.com'));
  350. $this->assertEquals('/app.php/Fabien', $this->getGenerator($routes, ['host' => 'fr.example.com'])->generate('test', ['name' => 'Fabien', 'locale' => 'fr']));
  351. }
  352. public function testWithHostSameAsContextAndAbsolute()
  353. {
  354. $routes = $this->getRoutes('test', new Route('/{name}', [], [], [], '{locale}.example.com'));
  355. $this->assertEquals('http://fr.example.com/app.php/Fabien', $this->getGenerator($routes, ['host' => 'fr.example.com'])->generate('test', ['name' => 'Fabien', 'locale' => 'fr'], UrlGeneratorInterface::ABSOLUTE_URL));
  356. }
  357. /**
  358. * @expectedException \Symfony\Component\Routing\Exception\InvalidParameterException
  359. */
  360. public function testUrlWithInvalidParameterInHost()
  361. {
  362. $routes = $this->getRoutes('test', new Route('/', [], ['foo' => 'bar'], [], '{foo}.example.com'));
  363. $this->getGenerator($routes)->generate('test', ['foo' => 'baz'], UrlGeneratorInterface::ABSOLUTE_PATH);
  364. }
  365. /**
  366. * @expectedException \Symfony\Component\Routing\Exception\InvalidParameterException
  367. */
  368. public function testUrlWithInvalidParameterInHostWhenParamHasADefaultValue()
  369. {
  370. $routes = $this->getRoutes('test', new Route('/', ['foo' => 'bar'], ['foo' => 'bar'], [], '{foo}.example.com'));
  371. $this->getGenerator($routes)->generate('test', ['foo' => 'baz'], UrlGeneratorInterface::ABSOLUTE_PATH);
  372. }
  373. /**
  374. * @expectedException \Symfony\Component\Routing\Exception\InvalidParameterException
  375. */
  376. public function testUrlWithInvalidParameterEqualsDefaultValueInHost()
  377. {
  378. $routes = $this->getRoutes('test', new Route('/', ['foo' => 'baz'], ['foo' => 'bar'], [], '{foo}.example.com'));
  379. $this->getGenerator($routes)->generate('test', ['foo' => 'baz'], UrlGeneratorInterface::ABSOLUTE_PATH);
  380. }
  381. public function testUrlWithInvalidParameterInHostInNonStrictMode()
  382. {
  383. $routes = $this->getRoutes('test', new Route('/', [], ['foo' => 'bar'], [], '{foo}.example.com'));
  384. $generator = $this->getGenerator($routes);
  385. $generator->setStrictRequirements(false);
  386. $this->assertNull($generator->generate('test', ['foo' => 'baz'], UrlGeneratorInterface::ABSOLUTE_PATH));
  387. }
  388. public function testHostIsCaseInsensitive()
  389. {
  390. $routes = $this->getRoutes('test', new Route('/', [], ['locale' => 'en|de|fr'], [], '{locale}.FooBar.com'));
  391. $generator = $this->getGenerator($routes);
  392. $this->assertSame('//EN.FooBar.com/app.php/', $generator->generate('test', ['locale' => 'EN'], UrlGeneratorInterface::NETWORK_PATH));
  393. }
  394. public function testDefaultHostIsUsedWhenContextHostIsEmpty()
  395. {
  396. $routes = $this->getRoutes('test', new Route('/path', ['domain' => 'my.fallback.host'], ['domain' => '.+'], [], '{domain}'));
  397. $generator = $this->getGenerator($routes);
  398. $generator->getContext()->setHost('');
  399. $this->assertSame('http://my.fallback.host/app.php/path', $generator->generate('test', [], UrlGeneratorInterface::ABSOLUTE_URL));
  400. }
  401. public function testDefaultHostIsUsedWhenContextHostIsEmptyAndPathReferenceType()
  402. {
  403. $routes = $this->getRoutes('test', new Route('/path', ['domain' => 'my.fallback.host'], ['domain' => '.+'], [], '{domain}'));
  404. $generator = $this->getGenerator($routes);
  405. $generator->getContext()->setHost('');
  406. $this->assertSame('//my.fallback.host/app.php/path', $generator->generate('test', [], UrlGeneratorInterface::ABSOLUTE_PATH));
  407. }
  408. public function testAbsoluteUrlFallbackToPathIfHostIsEmptyAndSchemeIsHttp()
  409. {
  410. $routes = $this->getRoutes('test', new Route('/route'));
  411. $generator = $this->getGenerator($routes);
  412. $generator->getContext()->setHost('');
  413. $generator->getContext()->setScheme('https');
  414. $this->assertSame('/app.php/route', $generator->generate('test', [], UrlGeneratorInterface::ABSOLUTE_URL));
  415. }
  416. public function testAbsoluteUrlFallbackToNetworkIfSchemeIsEmptyAndHostIsNot()
  417. {
  418. $routes = $this->getRoutes('test', new Route('/path'));
  419. $generator = $this->getGenerator($routes);
  420. $generator->getContext()->setHost('example.com');
  421. $generator->getContext()->setScheme('');
  422. $this->assertSame('//example.com/app.php/path', $generator->generate('test', [], UrlGeneratorInterface::ABSOLUTE_URL));
  423. }
  424. public function testAbsoluteUrlFallbackToPathIfSchemeAndHostAreEmpty()
  425. {
  426. $routes = $this->getRoutes('test', new Route('/path'));
  427. $generator = $this->getGenerator($routes);
  428. $generator->getContext()->setHost('');
  429. $generator->getContext()->setScheme('');
  430. $this->assertSame('/app.php/path', $generator->generate('test', [], UrlGeneratorInterface::ABSOLUTE_URL));
  431. }
  432. public function testAbsoluteUrlWithNonHttpSchemeAndEmptyHost()
  433. {
  434. $routes = $this->getRoutes('test', new Route('/path', [], [], [], '', ['file']));
  435. $generator = $this->getGenerator($routes);
  436. $generator->getContext()->setBaseUrl('');
  437. $generator->getContext()->setHost('');
  438. $this->assertSame('file:///path', $generator->generate('test', [], UrlGeneratorInterface::ABSOLUTE_URL));
  439. }
  440. public function testGenerateNetworkPath()
  441. {
  442. $routes = $this->getRoutes('test', new Route('/{name}', [], [], [], '{locale}.example.com', ['http']));
  443. $this->assertSame('//fr.example.com/app.php/Fabien', $this->getGenerator($routes)->generate('test',
  444. ['name' => 'Fabien', 'locale' => 'fr'], UrlGeneratorInterface::NETWORK_PATH), 'network path with different host'
  445. );
  446. $this->assertSame('//fr.example.com/app.php/Fabien?query=string', $this->getGenerator($routes, ['host' => 'fr.example.com'])->generate('test',
  447. ['name' => 'Fabien', 'locale' => 'fr', 'query' => 'string'], UrlGeneratorInterface::NETWORK_PATH), 'network path although host same as context'
  448. );
  449. $this->assertSame('http://fr.example.com/app.php/Fabien', $this->getGenerator($routes, ['scheme' => 'https'])->generate('test',
  450. ['name' => 'Fabien', 'locale' => 'fr'], UrlGeneratorInterface::NETWORK_PATH), 'absolute URL because scheme requirement does not match context'
  451. );
  452. $this->assertSame('http://fr.example.com/app.php/Fabien', $this->getGenerator($routes)->generate('test',
  453. ['name' => 'Fabien', 'locale' => 'fr'], UrlGeneratorInterface::ABSOLUTE_URL), 'absolute URL with same scheme because it is requested'
  454. );
  455. }
  456. public function testGenerateRelativePath()
  457. {
  458. $routes = new RouteCollection();
  459. $routes->add('article', new Route('/{author}/{article}/'));
  460. $routes->add('comments', new Route('/{author}/{article}/comments'));
  461. $routes->add('host', new Route('/{article}', [], [], [], '{author}.example.com'));
  462. $routes->add('scheme', new Route('/{author}/blog', [], [], [], '', ['https']));
  463. $routes->add('unrelated', new Route('/about'));
  464. $generator = $this->getGenerator($routes, ['host' => 'example.com', 'pathInfo' => '/fabien/symfony-is-great/']);
  465. $this->assertSame('comments', $generator->generate('comments',
  466. ['author' => 'fabien', 'article' => 'symfony-is-great'], UrlGeneratorInterface::RELATIVE_PATH)
  467. );
  468. $this->assertSame('comments?page=2', $generator->generate('comments',
  469. ['author' => 'fabien', 'article' => 'symfony-is-great', 'page' => 2], UrlGeneratorInterface::RELATIVE_PATH)
  470. );
  471. $this->assertSame('../twig-is-great/', $generator->generate('article',
  472. ['author' => 'fabien', 'article' => 'twig-is-great'], UrlGeneratorInterface::RELATIVE_PATH)
  473. );
  474. $this->assertSame('../../bernhard/forms-are-great/', $generator->generate('article',
  475. ['author' => 'bernhard', 'article' => 'forms-are-great'], UrlGeneratorInterface::RELATIVE_PATH)
  476. );
  477. $this->assertSame('//bernhard.example.com/app.php/forms-are-great', $generator->generate('host',
  478. ['author' => 'bernhard', 'article' => 'forms-are-great'], UrlGeneratorInterface::RELATIVE_PATH)
  479. );
  480. $this->assertSame('https://example.com/app.php/bernhard/blog', $generator->generate('scheme',
  481. ['author' => 'bernhard'], UrlGeneratorInterface::RELATIVE_PATH)
  482. );
  483. $this->assertSame('../../about', $generator->generate('unrelated',
  484. [], UrlGeneratorInterface::RELATIVE_PATH)
  485. );
  486. }
  487. /**
  488. * @dataProvider provideRelativePaths
  489. */
  490. public function testGetRelativePath($sourcePath, $targetPath, $expectedPath)
  491. {
  492. $this->assertSame($expectedPath, UrlGenerator::getRelativePath($sourcePath, $targetPath));
  493. }
  494. public function provideRelativePaths()
  495. {
  496. return [
  497. [
  498. '/same/dir/',
  499. '/same/dir/',
  500. '',
  501. ],
  502. [
  503. '/same/file',
  504. '/same/file',
  505. '',
  506. ],
  507. [
  508. '/',
  509. '/file',
  510. 'file',
  511. ],
  512. [
  513. '/',
  514. '/dir/file',
  515. 'dir/file',
  516. ],
  517. [
  518. '/dir/file.html',
  519. '/dir/different-file.html',
  520. 'different-file.html',
  521. ],
  522. [
  523. '/same/dir/extra-file',
  524. '/same/dir/',
  525. './',
  526. ],
  527. [
  528. '/parent/dir/',
  529. '/parent/',
  530. '../',
  531. ],
  532. [
  533. '/parent/dir/extra-file',
  534. '/parent/',
  535. '../',
  536. ],
  537. [
  538. '/a/b/',
  539. '/x/y/z/',
  540. '../../x/y/z/',
  541. ],
  542. [
  543. '/a/b/c/d/e',
  544. '/a/c/d',
  545. '../../../c/d',
  546. ],
  547. [
  548. '/a/b/c//',
  549. '/a/b/c/',
  550. '../',
  551. ],
  552. [
  553. '/a/b/c/',
  554. '/a/b/c//',
  555. './/',
  556. ],
  557. [
  558. '/root/a/b/c/',
  559. '/root/x/b/c/',
  560. '../../../x/b/c/',
  561. ],
  562. [
  563. '/a/b/c/d/',
  564. '/a',
  565. '../../../../a',
  566. ],
  567. [
  568. '/special-chars/sp%20ce/1€/mäh/e=mc²',
  569. '/special-chars/sp%20ce/1€/<µ>/e=mc²',
  570. '../<µ>/e=mc²',
  571. ],
  572. [
  573. 'not-rooted',
  574. 'dir/file',
  575. 'dir/file',
  576. ],
  577. [
  578. '//dir/',
  579. '',
  580. '../../',
  581. ],
  582. [
  583. '/dir/',
  584. '/dir/file:with-colon',
  585. './file:with-colon',
  586. ],
  587. [
  588. '/dir/',
  589. '/dir/subdir/file:with-colon',
  590. 'subdir/file:with-colon',
  591. ],
  592. [
  593. '/dir/',
  594. '/dir/:subdir/',
  595. './:subdir/',
  596. ],
  597. ];
  598. }
  599. public function testFragmentsCanBeAppendedToUrls()
  600. {
  601. $routes = $this->getRoutes('test', new Route('/testing'));
  602. $url = $this->getGenerator($routes)->generate('test', ['_fragment' => 'frag ment'], UrlGeneratorInterface::ABSOLUTE_PATH);
  603. $this->assertEquals('/app.php/testing#frag%20ment', $url);
  604. $url = $this->getGenerator($routes)->generate('test', ['_fragment' => '0'], UrlGeneratorInterface::ABSOLUTE_PATH);
  605. $this->assertEquals('/app.php/testing#0', $url);
  606. }
  607. public function testFragmentsDoNotEscapeValidCharacters()
  608. {
  609. $routes = $this->getRoutes('test', new Route('/testing'));
  610. $url = $this->getGenerator($routes)->generate('test', ['_fragment' => '?/'], UrlGeneratorInterface::ABSOLUTE_PATH);
  611. $this->assertEquals('/app.php/testing#?/', $url);
  612. }
  613. public function testFragmentsCanBeDefinedAsDefaults()
  614. {
  615. $routes = $this->getRoutes('test', new Route('/testing', ['_fragment' => 'fragment']));
  616. $url = $this->getGenerator($routes)->generate('test', [], UrlGeneratorInterface::ABSOLUTE_PATH);
  617. $this->assertEquals('/app.php/testing#fragment', $url);
  618. }
  619. /**
  620. * @dataProvider provideLookAroundRequirementsInPath
  621. */
  622. public function testLookRoundRequirementsInPath($expected, $path, $requirement)
  623. {
  624. $routes = $this->getRoutes('test', new Route($path, [], ['foo' => $requirement, 'baz' => '.+?']));
  625. $this->assertSame($expected, $this->getGenerator($routes)->generate('test', ['foo' => 'a/b', 'baz' => 'c/d/e']));
  626. }
  627. public function provideLookAroundRequirementsInPath()
  628. {
  629. yield ['/app.php/a/b/b%28ar/c/d/e', '/{foo}/b(ar/{baz}', '.+(?=/b\\(ar/)'];
  630. yield ['/app.php/a/b/bar/c/d/e', '/{foo}/bar/{baz}', '.+(?!$)'];
  631. yield ['/app.php/bar/a/b/bam/c/d/e', '/bar/{foo}/bam/{baz}', '(?<=/bar/).+'];
  632. yield ['/app.php/bar/a/b/bam/c/d/e', '/bar/{foo}/bam/{baz}', '(?<!^).+'];
  633. }
  634. protected function getGenerator(RouteCollection $routes, array $parameters = [], $logger = null)
  635. {
  636. $context = new RequestContext('/app.php');
  637. foreach ($parameters as $key => $value) {
  638. $method = 'set'.$key;
  639. $context->$method($value);
  640. }
  641. return new UrlGenerator($routes, $context, $logger);
  642. }
  643. protected function getRoutes($name, Route $route)
  644. {
  645. $routes = new RouteCollection();
  646. $routes->add($name, $route);
  647. return $routes;
  648. }
  649. }