UrlGeneratorTest.php 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724
  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('/route', ['domain' => 'my.fallback.host'], ['domain' => '.+'], [], '{domain}', ['http']));
  397. $generator = $this->getGenerator($routes);
  398. $generator->getContext()->setHost('');
  399. $this->assertSame('http://my.fallback.host/app.php/route', $generator->generate('test', [], UrlGeneratorInterface::ABSOLUTE_URL));
  400. }
  401. public function testDefaultHostIsUsedWhenContextHostIsEmptyAndSchemeIsNot()
  402. {
  403. $routes = $this->getRoutes('test', new Route('/route', ['domain' => 'my.fallback.host'], ['domain' => '.+'], [], '{domain}', ['http', 'https']));
  404. $generator = $this->getGenerator($routes);
  405. $generator->getContext()->setHost('');
  406. $generator->getContext()->setScheme('https');
  407. $this->assertSame('https://my.fallback.host/app.php/route', $generator->generate('test', [], UrlGeneratorInterface::ABSOLUTE_URL));
  408. }
  409. public function testAbsoluteUrlFallbackToRelativeIfHostIsEmptyAndSchemeIsNot()
  410. {
  411. $routes = $this->getRoutes('test', new Route('/route', [], [], [], '', ['http', 'https']));
  412. $generator = $this->getGenerator($routes);
  413. $generator->getContext()->setHost('');
  414. $generator->getContext()->setScheme('https');
  415. $this->assertSame('/app.php/route', $generator->generate('test', [], UrlGeneratorInterface::ABSOLUTE_URL));
  416. }
  417. public function testGenerateNetworkPath()
  418. {
  419. $routes = $this->getRoutes('test', new Route('/{name}', [], [], [], '{locale}.example.com', ['http']));
  420. $this->assertSame('//fr.example.com/app.php/Fabien', $this->getGenerator($routes)->generate('test',
  421. ['name' => 'Fabien', 'locale' => 'fr'], UrlGeneratorInterface::NETWORK_PATH), 'network path with different host'
  422. );
  423. $this->assertSame('//fr.example.com/app.php/Fabien?query=string', $this->getGenerator($routes, ['host' => 'fr.example.com'])->generate('test',
  424. ['name' => 'Fabien', 'locale' => 'fr', 'query' => 'string'], UrlGeneratorInterface::NETWORK_PATH), 'network path although host same as context'
  425. );
  426. $this->assertSame('http://fr.example.com/app.php/Fabien', $this->getGenerator($routes, ['scheme' => 'https'])->generate('test',
  427. ['name' => 'Fabien', 'locale' => 'fr'], UrlGeneratorInterface::NETWORK_PATH), 'absolute URL because scheme requirement does not match context'
  428. );
  429. $this->assertSame('http://fr.example.com/app.php/Fabien', $this->getGenerator($routes)->generate('test',
  430. ['name' => 'Fabien', 'locale' => 'fr'], UrlGeneratorInterface::ABSOLUTE_URL), 'absolute URL with same scheme because it is requested'
  431. );
  432. }
  433. public function testGenerateRelativePath()
  434. {
  435. $routes = new RouteCollection();
  436. $routes->add('article', new Route('/{author}/{article}/'));
  437. $routes->add('comments', new Route('/{author}/{article}/comments'));
  438. $routes->add('host', new Route('/{article}', [], [], [], '{author}.example.com'));
  439. $routes->add('scheme', new Route('/{author}/blog', [], [], [], '', ['https']));
  440. $routes->add('unrelated', new Route('/about'));
  441. $generator = $this->getGenerator($routes, ['host' => 'example.com', 'pathInfo' => '/fabien/symfony-is-great/']);
  442. $this->assertSame('comments', $generator->generate('comments',
  443. ['author' => 'fabien', 'article' => 'symfony-is-great'], UrlGeneratorInterface::RELATIVE_PATH)
  444. );
  445. $this->assertSame('comments?page=2', $generator->generate('comments',
  446. ['author' => 'fabien', 'article' => 'symfony-is-great', 'page' => 2], UrlGeneratorInterface::RELATIVE_PATH)
  447. );
  448. $this->assertSame('../twig-is-great/', $generator->generate('article',
  449. ['author' => 'fabien', 'article' => 'twig-is-great'], UrlGeneratorInterface::RELATIVE_PATH)
  450. );
  451. $this->assertSame('../../bernhard/forms-are-great/', $generator->generate('article',
  452. ['author' => 'bernhard', 'article' => 'forms-are-great'], UrlGeneratorInterface::RELATIVE_PATH)
  453. );
  454. $this->assertSame('//bernhard.example.com/app.php/forms-are-great', $generator->generate('host',
  455. ['author' => 'bernhard', 'article' => 'forms-are-great'], UrlGeneratorInterface::RELATIVE_PATH)
  456. );
  457. $this->assertSame('https://example.com/app.php/bernhard/blog', $generator->generate('scheme',
  458. ['author' => 'bernhard'], UrlGeneratorInterface::RELATIVE_PATH)
  459. );
  460. $this->assertSame('../../about', $generator->generate('unrelated',
  461. [], UrlGeneratorInterface::RELATIVE_PATH)
  462. );
  463. }
  464. /**
  465. * @dataProvider provideRelativePaths
  466. */
  467. public function testGetRelativePath($sourcePath, $targetPath, $expectedPath)
  468. {
  469. $this->assertSame($expectedPath, UrlGenerator::getRelativePath($sourcePath, $targetPath));
  470. }
  471. public function provideRelativePaths()
  472. {
  473. return [
  474. [
  475. '/same/dir/',
  476. '/same/dir/',
  477. '',
  478. ],
  479. [
  480. '/same/file',
  481. '/same/file',
  482. '',
  483. ],
  484. [
  485. '/',
  486. '/file',
  487. 'file',
  488. ],
  489. [
  490. '/',
  491. '/dir/file',
  492. 'dir/file',
  493. ],
  494. [
  495. '/dir/file.html',
  496. '/dir/different-file.html',
  497. 'different-file.html',
  498. ],
  499. [
  500. '/same/dir/extra-file',
  501. '/same/dir/',
  502. './',
  503. ],
  504. [
  505. '/parent/dir/',
  506. '/parent/',
  507. '../',
  508. ],
  509. [
  510. '/parent/dir/extra-file',
  511. '/parent/',
  512. '../',
  513. ],
  514. [
  515. '/a/b/',
  516. '/x/y/z/',
  517. '../../x/y/z/',
  518. ],
  519. [
  520. '/a/b/c/d/e',
  521. '/a/c/d',
  522. '../../../c/d',
  523. ],
  524. [
  525. '/a/b/c//',
  526. '/a/b/c/',
  527. '../',
  528. ],
  529. [
  530. '/a/b/c/',
  531. '/a/b/c//',
  532. './/',
  533. ],
  534. [
  535. '/root/a/b/c/',
  536. '/root/x/b/c/',
  537. '../../../x/b/c/',
  538. ],
  539. [
  540. '/a/b/c/d/',
  541. '/a',
  542. '../../../../a',
  543. ],
  544. [
  545. '/special-chars/sp%20ce/1€/mäh/e=mc²',
  546. '/special-chars/sp%20ce/1€/<µ>/e=mc²',
  547. '../<µ>/e=mc²',
  548. ],
  549. [
  550. 'not-rooted',
  551. 'dir/file',
  552. 'dir/file',
  553. ],
  554. [
  555. '//dir/',
  556. '',
  557. '../../',
  558. ],
  559. [
  560. '/dir/',
  561. '/dir/file:with-colon',
  562. './file:with-colon',
  563. ],
  564. [
  565. '/dir/',
  566. '/dir/subdir/file:with-colon',
  567. 'subdir/file:with-colon',
  568. ],
  569. [
  570. '/dir/',
  571. '/dir/:subdir/',
  572. './:subdir/',
  573. ],
  574. ];
  575. }
  576. public function testFragmentsCanBeAppendedToUrls()
  577. {
  578. $routes = $this->getRoutes('test', new Route('/testing'));
  579. $url = $this->getGenerator($routes)->generate('test', ['_fragment' => 'frag ment'], UrlGeneratorInterface::ABSOLUTE_PATH);
  580. $this->assertEquals('/app.php/testing#frag%20ment', $url);
  581. $url = $this->getGenerator($routes)->generate('test', ['_fragment' => '0'], UrlGeneratorInterface::ABSOLUTE_PATH);
  582. $this->assertEquals('/app.php/testing#0', $url);
  583. }
  584. public function testFragmentsDoNotEscapeValidCharacters()
  585. {
  586. $routes = $this->getRoutes('test', new Route('/testing'));
  587. $url = $this->getGenerator($routes)->generate('test', ['_fragment' => '?/'], UrlGeneratorInterface::ABSOLUTE_PATH);
  588. $this->assertEquals('/app.php/testing#?/', $url);
  589. }
  590. public function testFragmentsCanBeDefinedAsDefaults()
  591. {
  592. $routes = $this->getRoutes('test', new Route('/testing', ['_fragment' => 'fragment']));
  593. $url = $this->getGenerator($routes)->generate('test', [], UrlGeneratorInterface::ABSOLUTE_PATH);
  594. $this->assertEquals('/app.php/testing#fragment', $url);
  595. }
  596. protected function getGenerator(RouteCollection $routes, array $parameters = [], $logger = null)
  597. {
  598. $context = new RequestContext('/app.php');
  599. foreach ($parameters as $key => $value) {
  600. $method = 'set'.$key;
  601. $context->$method($value);
  602. }
  603. return new UrlGenerator($routes, $context, $logger);
  604. }
  605. protected function getRoutes($name, Route $route)
  606. {
  607. $routes = new RouteCollection();
  608. $routes->add($name, $route);
  609. return $routes;
  610. }
  611. }