1<?php
2
3/*
4 * This file is part of the Symfony package.
5 *
6 * (c) Fabien Potencier <fabien@symfony.com>
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12namespace Symfony\Component\Routing\Tests\Generator\Dumper;
13
14use PHPUnit\Framework\TestCase;
15use Symfony\Component\Routing\Generator\Dumper\PhpGeneratorDumper;
16use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
17use Symfony\Component\Routing\RequestContext;
18use Symfony\Component\Routing\Route;
19use Symfony\Component\Routing\RouteCollection;
20
21/**
22 * @group legacy
23 */
24class PhpGeneratorDumperTest extends TestCase
25{
26    /**
27     * @var RouteCollection
28     */
29    private $routeCollection;
30
31    /**
32     * @var PhpGeneratorDumper
33     */
34    private $generatorDumper;
35
36    /**
37     * @var string
38     */
39    private $testTmpFilepath;
40
41    /**
42     * @var string
43     */
44    private $largeTestTmpFilepath;
45
46    protected function setUp(): void
47    {
48        parent::setUp();
49
50        $this->routeCollection = new RouteCollection();
51        $this->generatorDumper = new PhpGeneratorDumper($this->routeCollection);
52        $this->testTmpFilepath = sys_get_temp_dir().\DIRECTORY_SEPARATOR.'php_generator.'.$this->getName().'.php';
53        $this->largeTestTmpFilepath = sys_get_temp_dir().\DIRECTORY_SEPARATOR.'php_generator.'.$this->getName().'.large.php';
54        @unlink($this->testTmpFilepath);
55        @unlink($this->largeTestTmpFilepath);
56    }
57
58    protected function tearDown(): void
59    {
60        parent::tearDown();
61
62        @unlink($this->testTmpFilepath);
63
64        $this->routeCollection = null;
65        $this->generatorDumper = null;
66        $this->testTmpFilepath = null;
67    }
68
69    public function testDumpWithRoutes()
70    {
71        $this->routeCollection->add('Test', new Route('/testing/{foo}'));
72        $this->routeCollection->add('Test2', new Route('/testing2'));
73
74        file_put_contents($this->testTmpFilepath, $this->generatorDumper->dump());
75        include $this->testTmpFilepath;
76
77        $projectUrlGenerator = new \ProjectUrlGenerator(new RequestContext('/app.php'));
78
79        $absoluteUrlWithParameter = $projectUrlGenerator->generate('Test', ['foo' => 'bar'], UrlGeneratorInterface::ABSOLUTE_URL);
80        $absoluteUrlWithoutParameter = $projectUrlGenerator->generate('Test2', [], UrlGeneratorInterface::ABSOLUTE_URL);
81        $relativeUrlWithParameter = $projectUrlGenerator->generate('Test', ['foo' => 'bar'], UrlGeneratorInterface::ABSOLUTE_PATH);
82        $relativeUrlWithoutParameter = $projectUrlGenerator->generate('Test2', [], UrlGeneratorInterface::ABSOLUTE_PATH);
83
84        $this->assertEquals('http://localhost/app.php/testing/bar', $absoluteUrlWithParameter);
85        $this->assertEquals('http://localhost/app.php/testing2', $absoluteUrlWithoutParameter);
86        $this->assertEquals('/app.php/testing/bar', $relativeUrlWithParameter);
87        $this->assertEquals('/app.php/testing2', $relativeUrlWithoutParameter);
88    }
89
90    public function testDumpWithSimpleLocalizedRoutes()
91    {
92        $this->routeCollection->add('test', (new Route('/foo')));
93        $this->routeCollection->add('test.en', (new Route('/testing/is/fun'))->setDefault('_locale', 'en')->setDefault('_canonical_route', 'test')->setRequirement('_locale', 'en'));
94        $this->routeCollection->add('test.nl', (new Route('/testen/is/leuk'))->setDefault('_locale', 'nl')->setDefault('_canonical_route', 'test')->setRequirement('_locale', 'nl'));
95
96        $code = $this->generatorDumper->dump([
97            'class' => 'SimpleLocalizedProjectUrlGenerator',
98        ]);
99        file_put_contents($this->testTmpFilepath, $code);
100        include $this->testTmpFilepath;
101
102        $context = new RequestContext('/app.php');
103        $projectUrlGenerator = new \SimpleLocalizedProjectUrlGenerator($context, null, 'en');
104
105        $urlWithDefaultLocale = $projectUrlGenerator->generate('test');
106        $urlWithSpecifiedLocale = $projectUrlGenerator->generate('test', ['_locale' => 'nl']);
107        $context->setParameter('_locale', 'en');
108        $urlWithEnglishContext = $projectUrlGenerator->generate('test');
109        $context->setParameter('_locale', 'nl');
110        $urlWithDutchContext = $projectUrlGenerator->generate('test');
111
112        $this->assertEquals('/app.php/testing/is/fun', $urlWithDefaultLocale);
113        $this->assertEquals('/app.php/testen/is/leuk', $urlWithSpecifiedLocale);
114        $this->assertEquals('/app.php/testing/is/fun', $urlWithEnglishContext);
115        $this->assertEquals('/app.php/testen/is/leuk', $urlWithDutchContext);
116
117        // test with full route name
118        $this->assertEquals('/app.php/testing/is/fun', $projectUrlGenerator->generate('test.en'));
119
120        $context->setParameter('_locale', 'de_DE');
121        // test that it fall backs to another route when there is no matching localized route
122        $this->assertEquals('/app.php/foo', $projectUrlGenerator->generate('test'));
123    }
124
125    public function testDumpWithRouteNotFoundLocalizedRoutes()
126    {
127        $this->expectException('Symfony\Component\Routing\Exception\RouteNotFoundException');
128        $this->expectExceptionMessage('Unable to generate a URL for the named route "test" as such route does not exist.');
129        $this->routeCollection->add('test.en', (new Route('/testing/is/fun'))->setDefault('_locale', 'en')->setDefault('_canonical_route', 'test')->setRequirement('_locale', 'en'));
130
131        $code = $this->generatorDumper->dump([
132            'class' => 'RouteNotFoundLocalizedProjectUrlGenerator',
133        ]);
134        file_put_contents($this->testTmpFilepath, $code);
135        include $this->testTmpFilepath;
136
137        $projectUrlGenerator = new \RouteNotFoundLocalizedProjectUrlGenerator(new RequestContext('/app.php'), null, 'pl_PL');
138        $projectUrlGenerator->generate('test');
139    }
140
141    public function testDumpWithFallbackLocaleLocalizedRoutes()
142    {
143        $this->routeCollection->add('test.en', (new Route('/testing/is/fun'))->setDefault('_locale', 'en')->setDefault('_canonical_route', 'test')->setRequirement('_locale', 'en'));
144        $this->routeCollection->add('test.nl', (new Route('/testen/is/leuk'))->setDefault('_locale', 'nl')->setDefault('_canonical_route', 'test')->setRequirement('_locale', 'nl'));
145        $this->routeCollection->add('test.fr', (new Route('/tester/est/amusant'))->setDefault('_locale', 'fr')->setDefault('_canonical_route', 'test')->setRequirement('_locale', 'fr'));
146
147        $code = $this->generatorDumper->dump([
148            'class' => 'FallbackLocaleLocalizedProjectUrlGenerator',
149        ]);
150        file_put_contents($this->testTmpFilepath, $code);
151        include $this->testTmpFilepath;
152
153        $context = new RequestContext('/app.php');
154        $context->setParameter('_locale', 'en_GB');
155        $projectUrlGenerator = new \FallbackLocaleLocalizedProjectUrlGenerator($context, null, null);
156
157        // test with context _locale
158        $this->assertEquals('/app.php/testing/is/fun', $projectUrlGenerator->generate('test'));
159        // test with parameters _locale
160        $this->assertEquals('/app.php/testen/is/leuk', $projectUrlGenerator->generate('test', ['_locale' => 'nl_BE']));
161
162        $projectUrlGenerator = new \FallbackLocaleLocalizedProjectUrlGenerator(new RequestContext('/app.php'), null, 'fr_CA');
163        // test with default locale
164        $this->assertEquals('/app.php/tester/est/amusant', $projectUrlGenerator->generate('test'));
165    }
166
167    public function testDumpWithTooManyRoutes()
168    {
169        $this->routeCollection->add('Test', new Route('/testing/{foo}'));
170        for ($i = 0; $i < 32769; ++$i) {
171            $this->routeCollection->add('route_'.$i, new Route('/route_'.$i));
172        }
173        $this->routeCollection->add('Test2', new Route('/testing2'));
174
175        file_put_contents($this->largeTestTmpFilepath, $this->generatorDumper->dump([
176            'class' => 'ProjectLargeUrlGenerator',
177        ]));
178        $this->routeCollection = $this->generatorDumper = null;
179        include $this->largeTestTmpFilepath;
180
181        $projectUrlGenerator = new \ProjectLargeUrlGenerator(new RequestContext('/app.php'));
182
183        $absoluteUrlWithParameter = $projectUrlGenerator->generate('Test', ['foo' => 'bar'], UrlGeneratorInterface::ABSOLUTE_URL);
184        $absoluteUrlWithoutParameter = $projectUrlGenerator->generate('Test2', [], UrlGeneratorInterface::ABSOLUTE_URL);
185        $relativeUrlWithParameter = $projectUrlGenerator->generate('Test', ['foo' => 'bar'], UrlGeneratorInterface::ABSOLUTE_PATH);
186        $relativeUrlWithoutParameter = $projectUrlGenerator->generate('Test2', [], UrlGeneratorInterface::ABSOLUTE_PATH);
187
188        $this->assertEquals('http://localhost/app.php/testing/bar', $absoluteUrlWithParameter);
189        $this->assertEquals('http://localhost/app.php/testing2', $absoluteUrlWithoutParameter);
190        $this->assertEquals('/app.php/testing/bar', $relativeUrlWithParameter);
191        $this->assertEquals('/app.php/testing2', $relativeUrlWithoutParameter);
192    }
193
194    public function testDumpWithoutRoutes()
195    {
196        $this->expectException('InvalidArgumentException');
197        file_put_contents($this->testTmpFilepath, $this->generatorDumper->dump(['class' => 'WithoutRoutesUrlGenerator']));
198        include $this->testTmpFilepath;
199
200        $projectUrlGenerator = new \WithoutRoutesUrlGenerator(new RequestContext('/app.php'));
201
202        $projectUrlGenerator->generate('Test', []);
203    }
204
205    public function testGenerateNonExistingRoute()
206    {
207        $this->expectException('Symfony\Component\Routing\Exception\RouteNotFoundException');
208        $this->routeCollection->add('Test', new Route('/test'));
209
210        file_put_contents($this->testTmpFilepath, $this->generatorDumper->dump(['class' => 'NonExistingRoutesUrlGenerator']));
211        include $this->testTmpFilepath;
212
213        $projectUrlGenerator = new \NonExistingRoutesUrlGenerator(new RequestContext());
214        $projectUrlGenerator->generate('NonExisting', []);
215    }
216
217    public function testDumpForRouteWithDefaults()
218    {
219        $this->routeCollection->add('Test', new Route('/testing/{foo}', ['foo' => 'bar']));
220
221        file_put_contents($this->testTmpFilepath, $this->generatorDumper->dump(['class' => 'DefaultRoutesUrlGenerator']));
222        include $this->testTmpFilepath;
223
224        $projectUrlGenerator = new \DefaultRoutesUrlGenerator(new RequestContext());
225        $url = $projectUrlGenerator->generate('Test', []);
226
227        $this->assertEquals('/testing', $url);
228    }
229
230    public function testDumpWithSchemeRequirement()
231    {
232        $this->routeCollection->add('Test1', new Route('/testing', [], [], [], '', ['ftp', 'https']));
233
234        file_put_contents($this->testTmpFilepath, $this->generatorDumper->dump(['class' => 'SchemeUrlGenerator']));
235        include $this->testTmpFilepath;
236
237        $projectUrlGenerator = new \SchemeUrlGenerator(new RequestContext('/app.php'));
238
239        $absoluteUrl = $projectUrlGenerator->generate('Test1', [], UrlGeneratorInterface::ABSOLUTE_URL);
240        $relativeUrl = $projectUrlGenerator->generate('Test1', [], UrlGeneratorInterface::ABSOLUTE_PATH);
241
242        $this->assertEquals('ftp://localhost/app.php/testing', $absoluteUrl);
243        $this->assertEquals('ftp://localhost/app.php/testing', $relativeUrl);
244
245        $projectUrlGenerator = new \SchemeUrlGenerator(new RequestContext('/app.php', 'GET', 'localhost', 'https'));
246
247        $absoluteUrl = $projectUrlGenerator->generate('Test1', [], UrlGeneratorInterface::ABSOLUTE_URL);
248        $relativeUrl = $projectUrlGenerator->generate('Test1', [], UrlGeneratorInterface::ABSOLUTE_PATH);
249
250        $this->assertEquals('https://localhost/app.php/testing', $absoluteUrl);
251        $this->assertEquals('/app.php/testing', $relativeUrl);
252    }
253
254    public function testDumpWithLocalizedRoutesPreserveTheGoodLocaleInTheUrl()
255    {
256        $this->routeCollection->add('foo.en', (new Route('/{_locale}/fork'))->setDefault('_locale', 'en')->setDefault('_canonical_route', 'foo')->setRequirement('_locale', 'en'));
257        $this->routeCollection->add('foo.fr', (new Route('/{_locale}/fourchette'))->setDefault('_locale', 'fr')->setDefault('_canonical_route', 'foo')->setRequirement('_locale', 'fr'));
258        $this->routeCollection->add('fun.en', (new Route('/fun'))->setDefault('_locale', 'en')->setDefault('_canonical_route', 'fun')->setRequirement('_locale', 'en'));
259        $this->routeCollection->add('fun.fr', (new Route('/amusant'))->setDefault('_locale', 'fr')->setDefault('_canonical_route', 'fun')->setRequirement('_locale', 'fr'));
260
261        file_put_contents($this->testTmpFilepath, $this->generatorDumper->dump([
262            'class' => 'PreserveTheGoodLocaleInTheUrlGenerator',
263        ]));
264        include $this->testTmpFilepath;
265
266        $requestContext = new RequestContext();
267        $requestContext->setParameter('_locale', 'fr');
268
269        $phpGenerator = new \PreserveTheGoodLocaleInTheUrlGenerator($requestContext);
270
271        $this->assertSame('/fr/fourchette', $phpGenerator->generate('foo'));
272        $this->assertSame('/en/fork', $phpGenerator->generate('foo.en'));
273        $this->assertSame('/en/fork', $phpGenerator->generate('foo', ['_locale' => 'en']));
274        $this->assertSame('/fr/fourchette', $phpGenerator->generate('foo.fr', ['_locale' => 'en']));
275
276        $this->assertSame('/amusant', $phpGenerator->generate('fun'));
277        $this->assertSame('/fun', $phpGenerator->generate('fun.en'));
278        $this->assertSame('/fun', $phpGenerator->generate('fun', ['_locale' => 'en']));
279        $this->assertSame('/amusant', $phpGenerator->generate('fun.fr', ['_locale' => 'en']));
280    }
281}
282