‪TYPO3CMS  ‪main
RedirectServiceTest.php
Go to the documentation of this file.
1 <?php
2 
3 declare(strict_types=1);
4 
5 /*
6  * This file is part of the TYPO3 CMS project.
7  *
8  * It is free software; you can redistribute it and/or modify it under
9  * the terms of the GNU General Public License, either version 2
10  * of the License, or any later version.
11  *
12  * For the full copyright and license information, please read the
13  * LICENSE.txt file that was distributed with this source code.
14  *
15  * The TYPO3 project - inspiring people to share!
16  */
17 
19 
20 use PHPUnit\Framework\Attributes\DataProvider;
21 use PHPUnit\Framework\Attributes\Test;
22 use PHPUnit\Framework\MockObject\MockObject;
23 use Psr\Container\ContainerInterface;
24 use Psr\Log\LoggerInterface;
25 use Psr\Log\NullLogger;
34 use TYPO3\CMS\Core\LinkHandling\TypoLinkCodecService;
36 use TYPO3\CMS\Core\Package\PackageManager;
44 use TYPO3\CMS\Core\TypoScript\FrontendTypoScriptFactory;
58 use TYPO3\TestingFramework\Core\Unit\UnitTestCase;
59 
60 final class ‪RedirectServiceTest extends UnitTestCase
61 {
62  protected bool ‪$resetSingletonInstances = true;
63 
65  protected MockObject&‪LinkService ‪$linkServiceMock;
66 
68 
69  protected MockObject&‪SiteFinder ‪$siteFinder;
71 
72  protected function ‪setUp(): void
73  {
74  parent::setUp();
75  $logger = new NullLogger();
76  $this->redirectCacheServiceMock = $this->getMockBuilder(RedirectCacheService::class)->disableOriginalConstructor()->getMock();
77  $this->linkServiceMock = $this->getMockBuilder(LinkService::class)->disableOriginalConstructor()->getMock();
78  $this->siteFinder = $this->getMockBuilder(SiteFinder::class)->disableOriginalConstructor()->getMock();
79  $this->redirectRepository = $this->getMockBuilder(RedirectRepository::class)->disableOriginalConstructor()->getMock();
80 
81  $this->redirectService = new ‪RedirectService(
82  $this->redirectCacheServiceMock,
83  $this->linkServiceMock,
84  $this->siteFinder,
87  new ‪Context(),
89  $this->createMock(Logger::class),
91  new ‪ErrorController(),
92  new ‪SysTemplateRepository(new ‪NoopEventDispatcher(), $this->createMock(ConnectionPool::class), new ‪Context()),
93  ),
94  new FrontendTypoScriptFactory(
95  $this->createMock(ContainerInterface::class),
98  $this->createMock(ConnectionPool::class),
99  $this->createMock(PackageManager::class),
100  new ‪Context(),
102  $this->createMock(SetRegistry::class),
103  ),
104  new ‪LossyTokenizer(),
107  ),
108  $this->createMock(PhpFrontend::class),
109  $this->createMock(LoggerInterface::class),
110  );
111 
112  ‪$GLOBALS['SIM_ACCESS_TIME'] = 42;
113  }
114 
115  #[Test]
117  {
118  $this->redirectCacheServiceMock->method('getRedirects')->willReturnMap([
119  ['example.com', []],
120  ['*', []],
121  ]);
122 
123  $result = $this->redirectService->matchRedirect('example.com', 'foo');
124 
125  self::assertNull($result);
126  }
127 
128  #[DataProvider('matchRedirectReturnsRedirectOnFlatMatchDataProvider')]
129  #[Test]
130  public function ‪matchRedirectReturnsRedirectOnFlatMatch(string $path = ''): void
131  {
132  $row = [
133  'target' => 'https://example.com',
134  'force_https' => '0',
135  'keep_query_parameters' => '0',
136  'target_statuscode' => '307',
137  'disabled' => '0',
138  'starttime' => '0',
139  'endtime' => '0',
140  ];
141  $this->redirectCacheServiceMock->method('getRedirects')->willReturnMap([
142  [
143  'example.com',
144  [
145  'flat' => [
146  $path . '/' => [
147  1 => $row,
148  ],
149  ],
150  ],
151  ],
152  [
153  '*',
154  [],
155  ],
156  ]);
157 
158  $result = $this->redirectService->matchRedirect('example.com', rawurlencode($path));
159 
160  self::assertSame($row, $result);
161  }
162 
164  {
165  return [
166  'default case' => [
167  'foo',
168  ],
169  'umlauts' => [
170  'äöü',
171  ],
172  'various special chars' => [
173  'special-chars-«-∑-€-®-†-Ω-¨-ø-π-å-‚-∂-ƒ-©-ª-º-∆-@-¥-≈-ç-√-∫-~-µ-∞-…-–',
174  ],
175  'chinese' => [
176  '应用',
177  ],
178  'hindi' => [
179  'कंपनी',
180  ],
181  'cyrilic' => [
182  'cyrilic-АВГДЄЅЗИѲІКЛМНѮѺПЧ',
183  ],
184  ];
185  }
186 
187  #[Test]
189  {
190  $row = [
191  'target' => 'https://example.com',
192  'force_https' => '0',
193  'keep_query_parameters' => '0',
194  'respect_query_parameters' => '1',
195  'target_statuscode' => '307',
196  'disabled' => '0',
197  'starttime' => '0',
198  'endtime' => '0',
199  ];
200  $this->redirectCacheServiceMock->method('getRedirects')->willReturnMap([
201  [
202  'example.com',
203  [
204  'respect_query_parameters' => [
205  'index.php?id=123' => [
206  1 => $row,
207  ],
208  ],
209  ],
210  ],
211  [
212  '*',
213  [],
214  ],
215  ]);
216 
217  $result = $this->redirectService->matchRedirect('example.com', 'index.php', 'id=123');
218 
219  self::assertSame($row, $result);
220  }
221 
222  #[Test]
224  {
225  $row = [
226  'target' => 'https://example.com',
227  'force_https' => '0',
228  'keep_query_parameters' => '0',
229  'respect_query_parameters' => '1',
230  'target_statuscode' => '307',
231  'disabled' => '0',
232  'starttime' => '0',
233  'endtime' => '0',
234  ];
235  $this->redirectCacheServiceMock->method('getRedirects')->willReturnMap([
236  [
237  'example.com',
238  [
239  'respect_query_parameters' => [
240  'index.php/?id=123' => [
241  1 => $row,
242  ],
243  ],
244  ],
245  ],
246  [
247  '*',
248  [],
249  ],
250  ]);
251 
252  $result = $this->redirectService->matchRedirect('example.com', 'index.php', 'id=123');
253 
254  self::assertSame($row, $result);
255  }
256 
257  #[Test]
259  {
260  $row = [
261  'target' => 'https://example.com/target',
262  'force_https' => '0',
263  'keep_query_parameters' => '0',
264  'respect_query_parameters' => '1',
265  'target_statuscode' => '307',
266  'disabled' => '0',
267  'starttime' => '0',
268  'endtime' => '0',
269  ];
270  $this->redirectCacheServiceMock->method('getRedirects')->willReturnMap([
271  [
272  'example.com',
273  [
274  'respect_query_parameters' => [
275  'index.php?id=123&a=b' => [
276  1 => $row,
277  ],
278  ],
279  ],
280  ],
281  [
282  '*',
283  [],
284  ],
285  ]);
286 
287  $result = $this->redirectService->matchRedirect('example.com', 'index.php', 'id=123&a=b');
288 
289  self::assertSame($row, $result);
290  }
291 
292  #[Test]
294  {
295  $row = [
296  'target' => 'https://example.com/target',
297  'force_https' => '0',
298  'keep_query_parameters' => '0',
299  'respect_query_parameters' => '1',
300  'target_statuscode' => '307',
301  'disabled' => '0',
302  'starttime' => '0',
303  'endtime' => '0',
304  ];
305  $this->redirectCacheServiceMock->method('getRedirects')->willReturnMap([
306  [
307  'example.com',
308  [
309  'respect_query_parameters' => [
310  'index.php?id=123&a=b' => [
311  1 => $row,
312  ],
313  ],
314  ],
315  ],
316  [
317  '*',
318  [],
319  ],
320  ]);
321 
322  $result = $this->redirectService->matchRedirect('example.com', 'index.php', 'id=123&a=a');
323 
324  self::assertNull($result);
325  }
326 
327  #[Test]
329  {
330  $row1 = [
331  'target' => 'https://example.com/no-promotion',
332  'force_https' => '0',
333  'keep_query_parameters' => '0',
334  'respect_query_parameters' => '0',
335  'target_statuscode' => '307',
336  'disabled' => '0',
337  'starttime' => '0',
338  'endtime' => '0',
339  ];
340  $row2 = [
341  'target' => 'https://example.com/promotion',
342  'force_https' => '0',
343  'keep_query_parameters' => '0',
344  'respect_query_parameters' => '1',
345  'target_statuscode' => '307',
346  'disabled' => '0',
347  'starttime' => '0',
348  'endtime' => '0',
349  ];
350  $this->redirectCacheServiceMock->method('getRedirects')->willReturnMap([
351  [
352  'example.com',
353  [
354  'flat' => [
355  'special/page/' =>
356  [
357  1 => $row1,
358  ],
359  ],
360  'respect_query_parameters' => [
361  'special/page?key=998877' => [
362  1 => $row2,
363  ],
364  ],
365  ],
366  ],
367  [
368  '*',
369  [],
370  ],
371  ]);
372 
373  $result = $this->redirectService->matchRedirect('example.com', 'special/page', 'key=998877');
374 
375  self::assertSame($row2, $result);
376  }
377 
378  #[Test]
380  {
381  $row1 = [
382  'target' => 'https://example.com',
383  'force_https' => '0',
384  'keep_query_parameters' => '0',
385  'target_statuscode' => '307',
386  'disabled' => '0',
387  'starttime' => '0',
388  'endtime' => '0',
389  ];
390  $row2 = [
391  'target' => 'https://example.net',
392  'force_https' => '0',
393  'keep_query_parameters' => '0',
394  'target_statuscode' => '307',
395  'disabled' => '0',
396  'starttime' => '0',
397  'endtime' => '0',
398  ];
399 
400  $this->redirectCacheServiceMock->method('getRedirects')->willReturnMap([
401  [
402  'example.com',
403  [
404  'flat' => [
405  'foo/' => [
406  1 => $row1,
407  ],
408  ],
409  ],
410  ],
411  [
412  '*',
413  [
414  'flat' => [
415  'foo/' => [
416  2 => $row2,
417  ],
418  ],
419  ],
420  ],
421  ]);
422 
423  $result = $this->redirectService->matchRedirect('example.com', 'foo');
424 
425  self::assertSame($row1, $result);
426  }
427 
428  #[Test]
430  {
431  $row = [
432  'target' => 'https://example.com',
433  'force_https' => '0',
434  'respect_query_parameters' => '0',
435  'keep_query_parameters' => '0',
436  'target_statuscode' => '307',
437  'disabled' => '0',
438  'starttime' => '0',
439  'endtime' => '0',
440  ];
441  $this->redirectCacheServiceMock->method('getRedirects')->willReturnMap([
442  [
443  'example.com',
444  [
445  'regexp_flat' => [
446  '/f.*?/' => [
447  1 => $row,
448  ],
449  ],
450  ],
451  ],
452  [
453  '*',
454  [],
455  ],
456  ]);
457 
458  $result = $this->redirectService->matchRedirect('example.com', 'foo');
459 
460  self::assertSame($row, $result);
461  }
462 
463  #[Test]
465  {
466  $row1 = [
467  'target' => 'https://example.com',
468  'force_https' => '0',
469  'keep_query_parameters' => '0',
470  'target_statuscode' => '307',
471  'starttime' => '0',
472  'endtime' => '0',
473  'disabled' => '1',
474  ];
475  $row2 = [
476  'target' => 'https://example.net',
477  'force_https' => '0',
478  'keep_query_parameters' => '0',
479  'target_statuscode' => '307',
480  'starttime' => '0',
481  'endtime' => '0',
482  'disabled' => '0',
483  ];
484  $this->redirectCacheServiceMock->method('getRedirects')->willReturnMap([
485  [
486  'example.com',
487  [
488  'flat' => [
489  'foo/' => [
490  1 => $row1,
491  2 => $row2,
492  ],
493  ],
494  ],
495  ],
496  [
497  '*',
498  [],
499  ],
500  ]);
501 
502  $result = $this->redirectService->matchRedirect('example.com', 'foo');
503 
504  self::assertSame($row2, $result);
505  }
506 
507  #[Test]
509  {
510  $this->linkServiceMock->method('resolve')->with(self::anything())->willThrowException(new ‪InvalidPathException('', 1516531195));
511  GeneralUtility::addInstance(TypoLinkCodecService::class, new TypoLinkCodecService(new ‪NoopEventDispatcher()));
512 
513  $result = $this->redirectService->getTargetUrl(['target' => 'invalid'], new ‪ServerRequest(new ‪Uri()));
514 
515  self::assertNull($result);
516  }
517 
518  #[Test]
519  public function ‪getTargetUrlReturnsUrlForTypeUrl(): void
520  {
521  $redirectTargetMatch = [
522  'target' => 'https://example.com',
523  'force_https' => '0',
524  'keep_query_parameters' => '0',
525  ];
526  $linkDetails = [
527  'type' => ‪LinkService::TYPE_URL,
528  'url' => 'https://example.com/',
529  ];
530  $this->linkServiceMock->method('resolve')->with($redirectTargetMatch['target'])->willReturn($linkDetails);
531  GeneralUtility::addInstance(TypoLinkCodecService::class, new TypoLinkCodecService(new ‪NoopEventDispatcher()));
532 
533  $source = new ‪Uri('https://example.com');
534  $request = new ‪ServerRequest($source);
535  $request = $request->withAttribute('site', new ‪Site('dummy', 13, []));
536  $result = $this->redirectService->getTargetUrl($redirectTargetMatch, $request);
537 
538  $uri = new ‪Uri('https://example.com/');
539  self::assertEquals($uri, $result);
540  }
541 
542  #[Test]
543  public function ‪getTargetUrlReturnsUrlForTypeFile(): void
544  {
545  $fileMock = $this->getMockBuilder(File::class)->disableOriginalConstructor()->getMock();
546  $fileMock->method('getPublicUrl')->willReturn('https://example.com/file.txt');
547  $redirectTargetMatch = [
548  'target' => 'https://example.com',
549  'force_https' => '0',
550  'keep_query_parameters' => '0',
551  ];
552  $linkDetails = [
553  'type' => ‪LinkService::TYPE_FILE,
554  'file' => $fileMock,
555  ];
556  $this->linkServiceMock->method('resolve')->with($redirectTargetMatch['target'])->willReturn($linkDetails);
557  GeneralUtility::addInstance(TypoLinkCodecService::class, new TypoLinkCodecService(new ‪NoopEventDispatcher()));
558 
559  $source = new ‪Uri('https://example.com');
560  $request = new ‪ServerRequest($source);
561  $request = $request->withAttribute('site', new ‪Site('dummy', 13, []));
562  $result = $this->redirectService->getTargetUrl($redirectTargetMatch, $request);
563 
564  $uri = new ‪Uri('https://example.com/file.txt');
565  self::assertEquals($uri, $result);
566  }
567 
568  #[Test]
570  {
571  $folderMock = $this->getMockBuilder(Folder::class)->disableOriginalConstructor()->getMock();
572  $folderMock->method('getPublicUrl')->willReturn('https://example.com/folder/');
573  $redirectTargetMatch = [
574  'target' => 'https://example.com',
575  'force_https' => '0',
576  'keep_query_parameters' => '0',
577  ];
578  $folder = $folderMock;
579  $linkDetails = [
580  'type' => ‪LinkService::TYPE_FOLDER,
581  'folder' => $folder,
582  ];
583  $this->linkServiceMock->method('resolve')->with($redirectTargetMatch['target'])->willReturn($linkDetails);
584  GeneralUtility::addInstance(TypoLinkCodecService::class, new TypoLinkCodecService(new ‪NoopEventDispatcher()));
585 
586  $source = new ‪Uri('https://example.com/');
587  $request = new ‪ServerRequest($source);
588  $request = $request->withAttribute('site', new ‪Site('dummy', 13, []));
589  $result = $this->redirectService->getTargetUrl($redirectTargetMatch, $request);
590 
591  $uri = new ‪Uri('https://example.com/folder/');
592  self::assertEquals($uri, $result);
593  }
594 
595  #[Test]
596  public function ‪getTargetUrlRespectsForceHttps(): void
597  {
598  $redirectTargetMatch = [
599  'target' => 'https://example.com',
600  'keep_query_parameters' => '0',
601  'force_https' => '1',
602  ];
603  $linkDetails = [
604  'type' => ‪LinkService::TYPE_URL,
605  'url' => 'http://example.com',
606  ];
607  $this->linkServiceMock->method('resolve')->with($redirectTargetMatch['target'])->willReturn($linkDetails);
608  GeneralUtility::addInstance(TypoLinkCodecService::class, new TypoLinkCodecService(new ‪NoopEventDispatcher()));
609 
610  $source = new ‪Uri('https://example.com');
611  $request = new ‪ServerRequest($source);
612  $request = $request->withAttribute('site', new ‪Site('dummy', 13, []));
613  $result = $this->redirectService->getTargetUrl($redirectTargetMatch, $request);
614 
615  $uri = new ‪Uri('https://example.com');
616  self::assertEquals($uri, $result);
617  }
618 
619  #[Test]
621  {
622  $redirectTargetMatch = [
623  'target' => 'https://example.com',
624  'force_https' => '0',
625  'keep_query_parameters' => '1',
626  ];
627  $linkDetails = [
628  'type' => ‪LinkService::TYPE_URL,
629  'url' => 'https://example.com/?foo=1&bar=2',
630  ];
631  $this->linkServiceMock->method('resolve')->with($redirectTargetMatch['target'])->willReturn($linkDetails);
632  GeneralUtility::addInstance(TypoLinkCodecService::class, new TypoLinkCodecService(new ‪NoopEventDispatcher()));
633 
634  $source = new ‪Uri('https://example.com/?bar=2&baz=4&foo=1');
635  $request = new ‪ServerRequest($source);
636  $request = $request->withQueryParams(['bar' => 3, 'baz' => 4]);
637  $request = $request->withAttribute('site', new ‪Site('dummy', 13, []));
638  $result = $this->redirectService->getTargetUrl($redirectTargetMatch, $request);
639 
640  $uri = new ‪Uri('https://example.com/?bar=2&baz=4&foo=1');
641  self::assertEquals($uri, $result);
642  }
643 
644  #[Test]
646  {
647  ‪$redirectService = $this->getAccessibleMock(
648  RedirectService::class,
649  ['getUriFromCustomLinkDetails'],
650  [
651  $this->redirectCacheServiceMock,
652  $this->linkServiceMock,
653  $this->siteFinder,
656  new ‪Context(),
658  $this->createMock(Logger::class),
660  new ‪ErrorController(),
661  new ‪SysTemplateRepository(new ‪NoopEventDispatcher(), $this->createMock(ConnectionPool::class), new ‪Context()),
662  ),
663  new FrontendTypoScriptFactory(
664  $this->createMock(ContainerInterface::class),
667  $this->createMock(ConnectionPool::class),
668  $this->createMock(PackageManager::class),
669  new ‪Context(),
671  $this->createMock(SetRegistry::class),
672  ),
673  new ‪LossyTokenizer(),
676  ),
677  $this->createMock(PhpFrontend::class),
678  $this->createMock(LoggerInterface::class),
679  ],
680  '',
681  );
682 
683  $pageRecord = 't3://page?uid=13';
684  $redirectTargetMatch = [
685  'target' => $pageRecord . ' - - - foo=bar',
686  'force_https' => 1,
687  'keep_query_parameters' => 1,
688  ];
689 
690  $linkDetails = [
691  'pageuid' => 13,
692  'type' => ‪LinkService::TYPE_PAGE,
693  'typoLinkParameter' => $pageRecord,
694  ];
695  $this->linkServiceMock->method('resolve')->with($pageRecord)->willReturn($linkDetails);
696  GeneralUtility::addInstance(TypoLinkCodecService::class, new TypoLinkCodecService(new ‪NoopEventDispatcher()));
697 
698  $queryParams = [];
699  $queryParams['foo'] = 'bar';
700  $uri = new ‪Uri('/page?foo=bar');
701 
702  $frontendUserAuthentication = new ‪FrontendUserAuthentication();
703  $site = new ‪Site('dummy', 13, []);
704  $request = new ‪ServerRequest($uri);
705  $request = $request->withQueryParams($queryParams);
706  $request = $request->withAttribute('site', $site);
707  $request = $request->withAttribute('frontend.user', $frontendUserAuthentication);
708  ‪$redirectService->method('getUriFromCustomLinkDetails')
709  ->with($redirectTargetMatch, $site, $linkDetails, $queryParams, $request)
710  ->willReturn($uri);
711  $result = ‪$redirectService->‪getTargetUrl($redirectTargetMatch, $request);
712 
713  self::assertEquals($uri, $result);
714  }
715 
716  #[Test]
718  {
719  $redirectTargetMatch = [
720  'source_path' => '#^/foo/(.*)#',
721  'target' => 'https://anotherdomain.com/$1',
722  'force_https' => '0',
723  'keep_query_parameters' => '1',
724  'is_regexp' => 1,
725  ];
726  $linkDetails = [
727  'type' => ‪LinkService::TYPE_URL,
728  'url' => 'https://anotherdomain.com/$1',
729  ];
730  $this->linkServiceMock->method('resolve')->with($redirectTargetMatch['target'])->willReturn($linkDetails);
731  GeneralUtility::addInstance(TypoLinkCodecService::class, new TypoLinkCodecService(new ‪NoopEventDispatcher()));
732 
733  $source = new ‪Uri('https://example.com/foo/bar');
734  $request = new ‪ServerRequest($source);
735  $request = $request->withAttribute('site', new ‪Site('dummy', 13, []));
736  $result = $this->redirectService->getTargetUrl($redirectTargetMatch, $request);
737 
738  $uri = new ‪Uri('https://anotherdomain.com/bar');
739  self::assertEquals($uri, $result);
740  }
741 
743  {
744  $cyrilicPlain = 'АВГДЄЅЗИѲІКЛМНѮѺПЧ';
745  return [
746  'index.php with query capture group - plain value' => [
747  '#^/index.php\?option=com_content&page=(.*)#',
748  'https://anotherdomain.com/$1',
749  'https://example.com/index.php?option=com_content&page=target',
750  'https://anotherdomain.com/target',
751  ],
752  'index.php with query capture group - cyrilic value' => [
753  '#^/index.php\?option=com_content&page=(.*)#',
754  'https://anotherdomain.com/$1',
755  sprintf('https://example.com/index.php?option=com_content&page=%s', $cyrilicPlain),
756  sprintf('https://anotherdomain.com/%s', $cyrilicPlain),
757  ],
758  'capture group in path and query capture group - cyrilic value' => [
759  '#^/index-(.*).php\?option=com_content&page=(.*)#',
760  'https://anotherdomain.com/$1/$2',
761  sprintf('https://example.com/index-%s.php?option=com_content&page=cyrilic-%s', $cyrilicPlain, $cyrilicPlain),
762  sprintf('https://anotherdomain.com/%s/cyrilic-%s', $cyrilicPlain, $cyrilicPlain),
763  ],
764  'cyrilic path with non-cyrilic capture group' => [
765  sprintf('#^/index-%s.php\?option=com_content&page=(.*)#', $cyrilicPlain),
766  'https://anotherdomain.com/$1',
767  sprintf('https://example.com/index-%s.php?option=com_content&page=cyrilic-%s', $cyrilicPlain, $cyrilicPlain),
768  sprintf('https://anotherdomain.com/cyrilic-%s', $cyrilicPlain),
769  ],
770  'cyrilic path with cyrilic capture group' => [
771  sprintf('#^/index-%s.php\?option=com_content&page=(.*)#', $cyrilicPlain),
772  'https://anotherdomain.com/$1',
773  sprintf('https://example.com/index-%s.php?option=com_content&page=cyrilic-%s', $cyrilicPlain, $cyrilicPlain),
774  sprintf('https://anotherdomain.com/cyrilic-%s', $cyrilicPlain),
775  ],
776  'cyrilic path with cyrilic capture group with cyrilic prefix' => [
777  sprintf('#^/index-%s.php\?option=com_content&page=%s(.*)#', $cyrilicPlain, $cyrilicPlain),
778  'https://anotherdomain.com/$1',
779  sprintf('https://example.com/index-%s.php?option=com_content&page=%scyrilic-%s', $cyrilicPlain, $cyrilicPlain, $cyrilicPlain),
780  sprintf('https://anotherdomain.com/cyrilic-%s', $cyrilicPlain),
781  ],
782  ];
783  }
784 
785  #[DataProvider('getTargetUrlWithQueryReplaceRegExpCaptureGroupDataProvider')]
786  #[Test]
788  string $redirectSourcePath,
789  string $redirectTarget,
790  string $requestUri,
791  string $expectedRedirectUri
792  ) {
793  $redirectTargetMatch = [
794  'uid' => 1,
795  'source_path' => $redirectSourcePath,
796  'target' => $redirectTarget,
797  'force_https' => '0',
798  'keep_query_parameters' => 0,
799  'is_regexp' => 1,
800  'respect_query_parameters' => 1,
801  ];
802  $linkDetails = [
803  'type' => ‪LinkService::TYPE_URL,
804  'url' => $redirectTarget,
805  'query' => '',
806  ];
807  $this->linkServiceMock->method('resolve')->with($redirectTargetMatch['target'])->willReturn($linkDetails);
808  GeneralUtility::addInstance(TypoLinkCodecService::class, new TypoLinkCodecService(new ‪NoopEventDispatcher()));
809 
810  $source = new ‪Uri($requestUri);
811  $queryParams = [];
812  parse_str($source->getQuery(), $queryParams);
813  $request = new ‪ServerRequest($source);
814  $request = $request->withQueryParams($queryParams);
815  $request = $request->withAttribute('site', new ‪Site('dummy', 13, []));
816  $result = $this->redirectService->getTargetUrl($redirectTargetMatch, $request);
817 
818  $uri = new ‪Uri($expectedRedirectUri);
819  self::assertEquals($uri, $result);
820  }
821 
823  {
824  $cyrilicPlain = 'АВГДЄЅЗИѲІКЛМНѮѺПЧ';
825  return [
826  'index with path slash with query capture group - plain value' => [
827  '#^/index/\?option=com_content&page=(.*)#',
828  'https://anotherdomain.com/$1',
829  'https://example.com/index/?option=com_content&page=target',
830  'https://anotherdomain.com/target?option=com_content&page=target',
831  ],
832  'index with query capture group - cyrilic value' => [
833  '#^/index/\?option=com_content&page=(.*)#',
834  'https://anotherdomain.com/$1',
835  sprintf('https://example.com/index/?option=com_content&page=%s', $cyrilicPlain),
836  sprintf('https://anotherdomain.com/%s?option=com_content&page=%s', $cyrilicPlain, $cyrilicPlain),
837  ],
838  'capture group in path and query capture group - cyrilic value' => [
839  '#^/index-(.*)/\?option=com_content&page=(.*)#',
840  'https://anotherdomain.com/$1/$2',
841  sprintf('https://example.com/index-%s/?option=com_content&page=cyrilic-%s', $cyrilicPlain, $cyrilicPlain),
842  sprintf('https://anotherdomain.com/%s/cyrilic-%s?option=com_content&page=cyrilic-%s', $cyrilicPlain, $cyrilicPlain, $cyrilicPlain),
843  ],
844  'cyrilic path with non-cyrilic capture group' => [
845  sprintf('#^/index-%s/\?option=com_content&page=(.*)#', $cyrilicPlain),
846  'https://anotherdomain.com/$1',
847  sprintf('https://example.com/index-%s/?option=com_content&page=cyrilic', $cyrilicPlain),
848  'https://anotherdomain.com/cyrilic?option=com_content&page=cyrilic',
849  ],
850  'cyrilic path with cyrilic capture group' => [
851  sprintf('#^/index-%s/\?option=com_content&page=(.*)#', $cyrilicPlain),
852  'https://anotherdomain.com/$1',
853  sprintf('https://example.com/index-%s/?option=com_content&page=cyrilic-%s', $cyrilicPlain, $cyrilicPlain),
854  sprintf('https://anotherdomain.com/cyrilic-%s?option=com_content&page=cyrilic-%s', $cyrilicPlain, $cyrilicPlain),
855  ],
856  'cyrilic path with cyrilic capture group with cyrilic prefix' => [
857  sprintf('#^/index-%s/\?option=com_content&page=%s(.*)#', $cyrilicPlain, $cyrilicPlain),
858  'https://anotherdomain.com/$1',
859  sprintf('https://example.com/index-%s/?option=com_content&page=%scyrilic-%s', $cyrilicPlain, $cyrilicPlain, $cyrilicPlain),
860  sprintf('https://anotherdomain.com/cyrilic-%s?option=com_content&page=%scyrilic-%s', $cyrilicPlain, $cyrilicPlain, $cyrilicPlain),
861  ],
862  ];
863  }
864 
865  #[DataProvider('getTargetUrlWithQueryAndSlashReplaceRegExpCaptureGroupDataProvider')]
866  #[Test]
868  string $redirectSourcePath,
869  string $redirectTarget,
870  string $requestUri,
871  string $expectedRedirectUri
872  ) {
873  $redirectTargetMatch = [
874  'uid' => 1,
875  'source_path' => $redirectSourcePath,
876  'target' => $redirectTarget,
877  'force_https' => '0',
878  'keep_query_parameters' => '1',
879  'is_regexp' => 1,
880  'respect_query_parameters' => 1,
881  ];
882  $linkDetails = [
883  'type' => ‪LinkService::TYPE_URL,
884  'url' => $redirectTarget,
885  ];
886  $this->linkServiceMock->method('resolve')->with($redirectTargetMatch['target'])->willReturn($linkDetails);
887  GeneralUtility::addInstance(TypoLinkCodecService::class, new TypoLinkCodecService(new ‪NoopEventDispatcher()));
888 
889  $source = new ‪Uri($requestUri);
890  $queryParams = [];
891  parse_str($source->getQuery(), $queryParams);
892  $request = new ‪ServerRequest($source);
893  $request = $request->withQueryParams($queryParams);
894  $request = $request->withAttribute('site', new ‪Site('dummy', 13, []));
895  $result = $this->redirectService->getTargetUrl($redirectTargetMatch, $request);
896 
897  $uri = new ‪Uri($expectedRedirectUri);
898  self::assertEquals($uri, $result);
899  }
900 }
‪TYPO3\CMS\Redirects\Tests\Unit\Service\RedirectServiceTest\getTargetUrlReplaceRegExpCaptureGroup
‪getTargetUrlReplaceRegExpCaptureGroup()
Definition: RedirectServiceTest.php:717
‪TYPO3\CMS\Core\TypoScript\IncludeTree\SysTemplateRepository
Definition: SysTemplateRepository.php:38
‪TYPO3\CMS\Redirects\Tests\Unit\Service\RedirectServiceTest\getTargetUrlWithQueryReplaceRegExpCaptureGroup
‪getTargetUrlWithQueryReplaceRegExpCaptureGroup(string $redirectSourcePath, string $redirectTarget, string $requestUri, string $expectedRedirectUri)
Definition: RedirectServiceTest.php:787
‪TYPO3\CMS\Redirects\Tests\Unit\Service\RedirectServiceTest\$redirectCacheServiceMock
‪MockObject &RedirectCacheService $redirectCacheServiceMock
Definition: RedirectServiceTest.php:64
‪TYPO3\CMS\Redirects\Tests\Unit\Service\RedirectServiceTest\getTargetUrlWithQueryAndSlashReplaceRegExpCaptureGroup
‪getTargetUrlWithQueryAndSlashReplaceRegExpCaptureGroup(string $redirectSourcePath, string $redirectTarget, string $requestUri, string $expectedRedirectUri)
Definition: RedirectServiceTest.php:867
‪TYPO3\CMS\Redirects\Tests\Unit\Service\RedirectServiceTest\getTargetUrlWithQueryAndSlashReplaceRegExpCaptureGroupDataProvider
‪static getTargetUrlWithQueryAndSlashReplaceRegExpCaptureGroupDataProvider()
Definition: RedirectServiceTest.php:822
‪TYPO3\CMS\Redirects\Tests\Unit\Service\RedirectServiceTest
Definition: RedirectServiceTest.php:61
‪TYPO3\CMS\Redirects\Tests\Unit\Service\RedirectServiceTest\$linkServiceMock
‪MockObject &LinkService $linkServiceMock
Definition: RedirectServiceTest.php:65
‪TYPO3\CMS\Core\Resource\Security\FileNameValidator
Definition: FileNameValidator.php:25
‪TYPO3\CMS\Redirects\Tests\Unit\Service\RedirectServiceTest\matchRedirectReturnsNullIfNoRedirectsExist
‪matchRedirectReturnsNullIfNoRedirectsExist()
Definition: RedirectServiceTest.php:116
‪TYPO3\CMS\Redirects\Tests\Unit\Service\RedirectServiceTest\getTargetUrlReturnsUrlForTypeUrl
‪getTargetUrlReturnsUrlForTypeUrl()
Definition: RedirectServiceTest.php:519
‪TYPO3\CMS\Redirects\Tests\Unit\Service\RedirectServiceTest\matchRedirectReturnsRedirectSpecificToDomainOnFlatMatchIfSpecificAndNonSpecificExist
‪matchRedirectReturnsRedirectSpecificToDomainOnFlatMatchIfSpecificAndNonSpecificExist()
Definition: RedirectServiceTest.php:379
‪TYPO3\CMS\Redirects\Tests\Unit\Service\RedirectServiceTest\getTargetUrlRespectsAdditionalParametersFromTypolink
‪getTargetUrlRespectsAdditionalParametersFromTypolink()
Definition: RedirectServiceTest.php:645
‪TYPO3\CMS\Redirects\Tests\Unit\Service\RedirectServiceTest\matchRedirectReturnsRedirectOnRespectQueryParametersMatchWithSlash
‪matchRedirectReturnsRedirectOnRespectQueryParametersMatchWithSlash()
Definition: RedirectServiceTest.php:223
‪TYPO3\CMS\Core\Cache\Frontend\PhpFrontend
Definition: PhpFrontend.php:25
‪TYPO3\CMS\Core\TypoScript\IncludeTree\Traverser\IncludeTreeTraverser
Definition: IncludeTreeTraverser.php:30
‪TYPO3\CMS\Redirects\Tests\Unit\Service\RedirectServiceTest\$redirectRepository
‪MockObject &RedirectRepository $redirectRepository
Definition: RedirectServiceTest.php:70
‪TYPO3\CMS\Redirects\Tests\Unit\Service\RedirectServiceTest\matchRedirectReturnsOnlyActiveRedirects
‪matchRedirectReturnsOnlyActiveRedirects()
Definition: RedirectServiceTest.php:464
‪TYPO3\CMS\Redirects\Service\RedirectService
Definition: RedirectService.php:54
‪TYPO3\CMS\Core\Site\SiteFinder
Definition: SiteFinder.php:31
‪TYPO3\CMS\Redirects\Tests\Unit\Service\RedirectServiceTest\matchRedirectReturnsRedirectOnRespectQueryParametersMatch
‪matchRedirectReturnsRedirectOnRespectQueryParametersMatch()
Definition: RedirectServiceTest.php:188
‪TYPO3\CMS\Core\TypoScript\IncludeTree\TreeFromLineStreamBuilder
Definition: TreeFromLineStreamBuilder.php:58
‪TYPO3\CMS\Frontend\Controller\ErrorController
Definition: ErrorController.php:38
‪TYPO3\CMS\Core\Context\Context
Definition: Context.php:54
‪TYPO3\CMS\Redirects\Tests\Unit\Service
Definition: RedirectServiceTest.php:18
‪TYPO3\CMS\Redirects\Tests\Unit\Service\RedirectServiceTest\matchRedirectReturnsRedirectOnFlatMatch
‪matchRedirectReturnsRedirectOnFlatMatch(string $path='')
Definition: RedirectServiceTest.php:130
‪TYPO3\CMS\Redirects\Service\RedirectService\getTargetUrl
‪getTargetUrl(array $matchedRedirect, ServerRequestInterface $request)
Definition: RedirectService.php:257
‪TYPO3\CMS\Redirects\Tests\Unit\Service\RedirectServiceTest\getTargetUrlReturnsNullIfUrlCouldNotBeResolved
‪getTargetUrlReturnsNullIfUrlCouldNotBeResolved()
Definition: RedirectServiceTest.php:508
‪TYPO3\CMS\Core\Http\Uri
Definition: Uri.php:30
‪TYPO3\CMS\Core\Site\Entity\Site
Definition: Site.php:42
‪TYPO3\CMS\Redirects\Tests\Unit\Service\RedirectServiceTest\setUp
‪setUp()
Definition: RedirectServiceTest.php:72
‪TYPO3\CMS\Core\Site\Set\SetRegistry
Definition: SetRegistry.php:30
‪TYPO3\CMS\Redirects\Service\RedirectCacheService
Definition: RedirectCacheService.php:34
‪TYPO3\CMS\Redirects\Tests\Unit\Service\RedirectServiceTest\matchRedirectReturnsMatchingRedirectWithMatchingQueryParametersOverMatchingPath
‪matchRedirectReturnsMatchingRedirectWithMatchingQueryParametersOverMatchingPath()
Definition: RedirectServiceTest.php:328
‪TYPO3\CMS\Redirects\Tests\Unit\Service\RedirectServiceTest\$resetSingletonInstances
‪bool $resetSingletonInstances
Definition: RedirectServiceTest.php:62
‪TYPO3\CMS\Redirects\Tests\Unit\Service\RedirectServiceTest\$redirectService
‪RedirectService $redirectService
Definition: RedirectServiceTest.php:67
‪TYPO3\CMS\Core\Resource\Folder
Definition: Folder.php:38
‪TYPO3\CMS\Core\Resource\File
Definition: File.php:26
‪TYPO3\CMS\Redirects\Tests\Unit\Service\RedirectServiceTest\getTargetUrlWithQueryReplaceRegExpCaptureGroupDataProvider
‪static getTargetUrlWithQueryReplaceRegExpCaptureGroupDataProvider()
Definition: RedirectServiceTest.php:742
‪TYPO3\CMS\Core\Http\ServerRequest
Definition: ServerRequest.php:39
‪TYPO3\CMS\Redirects\Tests\Unit\Service\RedirectServiceTest\matchRedirectReturnsRedirectOnFullRespectQueryParametersMatch
‪matchRedirectReturnsRedirectOnFullRespectQueryParametersMatch()
Definition: RedirectServiceTest.php:258
‪TYPO3\CMS\Redirects\Repository\RedirectRepository
Definition: RedirectRepository.php:31
‪TYPO3\CMS\Redirects\Tests\Unit\Service\RedirectServiceTest\getTargetUrlReturnsUrlForTypeFile
‪getTargetUrlReturnsUrlForTypeFile()
Definition: RedirectServiceTest.php:543
‪TYPO3\CMS\Core\EventDispatcher\NoopEventDispatcher
Definition: NoopEventDispatcher.php:29
‪TYPO3\CMS\Redirects\Tests\Unit\Service\RedirectServiceTest\getTargetUrlAddsExistingQueryParams
‪getTargetUrlAddsExistingQueryParams()
Definition: RedirectServiceTest.php:620
‪TYPO3\CMS\Redirects\Tests\Unit\Service\RedirectServiceTest\matchRedirectReturnsNullOnPartialRespectQueryParametersMatch
‪matchRedirectReturnsNullOnPartialRespectQueryParametersMatch()
Definition: RedirectServiceTest.php:293
‪$GLOBALS
‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['adminpanel']['modules']
Definition: ext_localconf.php:25
‪TYPO3\CMS\Core\Log\Logger
Definition: Logger.php:28
‪TYPO3\CMS\Core\TypoScript\IncludeTree\Traverser\ConditionVerdictAwareIncludeTreeTraverser
Definition: ConditionVerdictAwareIncludeTreeTraverser.php:38
‪TYPO3\CMS\Redirects\Tests\Unit\Service\RedirectServiceTest\matchRedirectReturnsRedirectOnRegexMatch
‪matchRedirectReturnsRedirectOnRegexMatch()
Definition: RedirectServiceTest.php:429
‪TYPO3\CMS\Redirects\Tests\Unit\Service\RedirectServiceTest\getTargetUrlReturnsUrlForTypeFolder
‪getTargetUrlReturnsUrlForTypeFolder()
Definition: RedirectServiceTest.php:569
‪TYPO3\CMS\Frontend\Authentication\FrontendUserAuthentication
Definition: FrontendUserAuthentication.php:33
‪TYPO3\CMS\Core\Database\ConnectionPool
Definition: ConnectionPool.php:46
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:52
‪TYPO3\CMS\Core\Resource\Exception\InvalidPathException
Definition: InvalidPathException.php:23
‪TYPO3\CMS\Core\TypoScript\Tokenizer\LossyTokenizer
Definition: LossyTokenizer.php:57
‪TYPO3\CMS\Core\TypoScript\IncludeTree\SysTemplateTreeBuilder
Definition: SysTemplateTreeBuilder.php:74
‪TYPO3\CMS\Core\Domain\Access\RecordAccessVoter
Definition: RecordAccessVoter.php:29
‪TYPO3\CMS\Redirects\Tests\Unit\Service\RedirectServiceTest\$siteFinder
‪MockObject &SiteFinder $siteFinder
Definition: RedirectServiceTest.php:69
‪TYPO3\CMS\Redirects\Tests\Unit\Service\RedirectServiceTest\matchRedirectReturnsRedirectOnFlatMatchDataProvider
‪static matchRedirectReturnsRedirectOnFlatMatchDataProvider()
Definition: RedirectServiceTest.php:163
‪TYPO3\CMS\Redirects\Tests\Unit\Service\RedirectServiceTest\getTargetUrlRespectsForceHttps
‪getTargetUrlRespectsForceHttps()
Definition: RedirectServiceTest.php:596
‪TYPO3\CMS\Frontend\Page\PageInformationFactory
Definition: PageInformationFactory.php:63