‪TYPO3CMS  10.4
TypoScriptParserTest.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 Prophecy\Argument;
27 use TYPO3\TestingFramework\Core\AccessibleObjectInterface;
28 use TYPO3\TestingFramework\Core\Unit\UnitTestCase;
29 
33 class ‪TypoScriptParserTest extends UnitTestCase
34 {
38  protected ‪$typoScriptParser;
39 
43  protected function ‪setUp(): void
44  {
45  parent::setUp();
46  $this->typoScriptParser = $this->getAccessibleMock(TypoScriptParser::class, ['dummy']);
47  }
48 
52  protected function ‪tearDown(): void
53  {
54  GeneralUtility::purgeInstances();
55  parent::tearDown();
56  }
57 
63  public function ‪executeValueModifierDataProvider(): array
64  {
65  return [
66  'prependString with string' => [
67  'prependString',
68  'abc',
69  '!',
70  '!abc'
71  ],
72  'prependString with empty string' => [
73  'prependString',
74  'foo',
75  '',
76  'foo',
77  ],
78  'appendString with string' => [
79  'appendString',
80  'abc',
81  '!',
82  'abc!',
83  ],
84  'appendString with empty string' => [
85  'appendString',
86  'abc',
87  '',
88  'abc',
89  ],
90  'removeString removes simple string' => [
91  'removeString',
92  'abcdef',
93  'bc',
94  'adef',
95  ],
96  'removeString removes nothing if no match' => [
97  'removeString',
98  'abcdef',
99  'foo',
100  'abcdef',
101  ],
102  'removeString removes multiple matches' => [
103  'removeString',
104  'FooBarFoo',
105  'Foo',
106  'Bar',
107  ],
108  'replaceString replaces simple match' => [
109  'replaceString',
110  'abcdef',
111  'bc|123',
112  'a123def',
113  ],
114  'replaceString replaces simple match with nothing' => [
115  'replaceString',
116  'abcdef',
117  'bc',
118  'adef',
119  ],
120  'replaceString replaces multiple matches' => [
121  'replaceString',
122  'FooBarFoo',
123  'Foo|Bar',
124  'BarBarBar',
125  ],
126  'addToList adds at end of existing list' => [
127  'addToList',
128  '123,456',
129  '789',
130  '123,456,789',
131  ],
132  'addToList adds at end of existing list including white-spaces' => [
133  'addToList',
134  '123,456',
135  ' 789 , 32 , 12 ',
136  '123,456, 789 , 32 , 12 ',
137  ],
138  'addToList adds nothing' => [
139  'addToList',
140  '123,456',
141  '',
142  '123,456,', // This result is probably not what we want (appended comma) ... fix it?
143  ],
144  'addToList adds to empty list' => [
145  'addToList',
146  '',
147  'foo',
148  'foo',
149  ],
150  'removeFromList removes value from list' => [
151  'removeFromList',
152  '123,456,789,abc',
153  '456',
154  '123,789,abc',
155  ],
156  'removeFromList removes value at beginning of list' => [
157  'removeFromList',
158  '123,456,abc',
159  '123',
160  '456,abc',
161  ],
162  'removeFromList removes value at end of list' => [
163  'removeFromList',
164  '123,456,abc',
165  'abc',
166  '123,456',
167  ],
168  'removeFromList removes multiple values from list' => [
169  'removeFromList',
170  'foo,123,bar,123',
171  '123',
172  'foo,bar',
173  ],
174  'removeFromList removes empty values' => [
175  'removeFromList',
176  'foo,,bar',
177  '',
178  'foo,bar',
179  ],
180  'uniqueList removes duplicates' => [
181  'uniqueList',
182  '123,456,abc,456,456',
183  '',
184  '123,456,abc',
185  ],
186  'uniqueList removes duplicate empty list values' => [
187  'uniqueList',
188  '123,,456,,abc',
189  '',
190  '123,,456,abc',
191  ],
192  'reverseList returns list reversed' => [
193  'reverseList',
194  '123,456,abc,456',
195  '',
196  '456,abc,456,123',
197  ],
198  'reverseList keeps empty values' => [
199  'reverseList',
200  ',123,,456,abc,,456',
201  '',
202  '456,,abc,456,,123,',
203  ],
204  'reverseList does not change single element' => [
205  'reverseList',
206  '123',
207  '',
208  '123',
209  ],
210  'sortList sorts a list' => [
211  'sortList',
212  '10,100,0,20,abc',
213  '',
214  '0,10,20,100,abc',
215  ],
216  'sortList sorts a list numeric' => [
217  'sortList',
218  '10,0,100,-20',
219  'numeric',
220  '-20,0,10,100',
221  ],
222  'sortList sorts a list descending' => [
223  'sortList',
224  '10,100,0,20,abc,-20',
225  'descending',
226  'abc,100,20,10,0,-20',
227  ],
228  'sortList sorts a list numeric descending' => [
229  'sortList',
230  '10,100,0,20,-20',
231  'descending,numeric',
232  '100,20,10,0,-20',
233  ],
234  'sortList ignores invalid modifier arguments' => [
235  'sortList',
236  '10,100,20',
237  'foo,descending,bar',
238  '100,20,10',
239  ],
240  ];
241  }
242 
252  string $modifierName,
253  string $currentValue,
254  string $modifierArgument,
255  string $expected
256  ): void {
257  $actualValue = $this->typoScriptParser->_call(
258  'executeValueModifier',
259  $modifierName,
260  $modifierArgument,
261  $currentValue
262  );
263  self::assertEquals($expected, $actualValue);
264  }
265 
266  public function ‪executeGetEnvModifierDataProvider(): array
267  {
268  return [
269  'environment variable not set' => [
270  [],
271  'bar',
272  'FOO',
273  null,
274  ],
275  'empty environment variable' => [
276  ['FOO' => ''],
277  'bar',
278  'FOO',
279  '',
280  ],
281  'empty current value' => [
282  ['FOO' => 'baz'],
283  null,
284  'FOO',
285  'baz',
286  ],
287  'environment variable and current value set' => [
288  ['FOO' => 'baz'],
289  'bar',
290  'FOO',
291  'baz',
292  ],
293  'neither environment variable nor current value set' => [
294  [],
295  null,
296  'FOO',
297  null,
298  ],
299  'empty environment variable name' => [
300  ['FOO' => 'baz'],
301  'bar',
302  '',
303  null,
304  ],
305  ];
306  }
307 
317  array $environmentVariables,
318  ?string $currentValue,
319  string $modifierArgument,
320  ?string $expected
321  ): void {
322  foreach ($environmentVariables as $environmentVariable => $value) {
323  putenv($environmentVariable . '=' . $value);
324  }
325  $actualValue = $this->typoScriptParser->_call(
326  'executeValueModifier',
327  'getEnv',
328  $modifierArgument,
329  $currentValue
330  );
331  self::assertEquals($expected, $actualValue);
332  foreach ($environmentVariables as $environmentVariable => $_) {
333  putenv($environmentVariable);
334  }
335  }
336 
342  public function ‪executeValueModifierInvalidDataProvider(): array
343  {
344  return [
345  'sortList sorts a list numeric' => [
346  'sortList',
347  '10,0,100,-20,abc',
348  'numeric',
349  ],
350  'sortList sorts a list numeric descending' => [
351  'sortList',
352  '10,100,0,20,abc,-20',
353  'descending,numeric',
354  ],
355  ];
356  }
357 
366  string $modifierName,
367  string $currentValue,
368  string $modifierArgument
369  ): void {
370  $this->expectException(\InvalidArgumentException::class);
371  $this->expectExceptionCode(1438191758);
372  $this->typoScriptParser->_call('executeValueModifier', $modifierName, $modifierArgument, $currentValue);
373  }
374 
378  public function ‪invalidCharactersInObjectNamesAreReported(): void
379  {
380  $timeTrackerProphecy = $this->prophesize(TimeTracker::class);
381  GeneralUtility::setSingletonInstance(TimeTracker::class, $timeTrackerProphecy->reveal());
382 
383  $typoScript = '$.10 = invalid';
384  $this->typoScriptParser->parse($typoScript);
385  $expected = 'Line 0: Object Name String, "$.10" contains invalid character "$". Must be alphanumeric or one of: "_:-/\."';
386  self::assertEquals($expected, $this->typoScriptParser->errors[0][0]);
387  }
388 
389  public function ‪invalidConditionsDataProvider(): array
390  {
391  return [
392  '[1 == 1]a' => ['[1 == 1]a', false],
393  '[1 == 1] # a comment' => ['[1 == 1] # a comment', false],
394  '[1 == 1]' => ['[1 == 1]', true],
395  ];
396  }
397 
404  public function ‪invalidConditionsAreReported(string $condition, bool $isValid): void
405  {
406  $timeTrackerProphecy = $this->prophesize(TimeTracker::class);
407  GeneralUtility::setSingletonInstance(TimeTracker::class, $timeTrackerProphecy->reveal());
408 
409  $this->typoScriptParser->parse($condition);
410  if (!$isValid) {
411  $expected = 'Line 0: Invalid condition found, any condition must end with "]": ' . $condition;
412  self::assertEquals($expected, $this->typoScriptParser->errors[0][0]);
413  }
414  }
415 
419  public function ‪emptyConditionIsReported(): void
420  {
421  $timeTrackerProphecy = $this->prophesize(TimeTracker::class);
422  GeneralUtility::setSingletonInstance(TimeTracker::class, $timeTrackerProphecy->reveal());
423 
424  $typoScript = '[]';
425  $this->typoScriptParser->parse($typoScript);
426  $expected = 'Empty condition is always false, this does not make sense. At line 0';
427  self::assertEquals($expected, $this->typoScriptParser->errors[0][0]);
428  }
429 
433  public function ‪doubleSlashCommentsDataProvider(): array
434  {
435  return [
436  'valid, without spaces' => ['// valid, without spaces'],
437  'valid, with one space' => [' // valid, with one space'],
438  'valid, with multiple spaces' => [' // valid, with multiple spaces'],
439  ];
440  }
441 
447  public function ‪doubleSlashCommentsAreValid(string $typoScript): void
448  {
449  $this->typoScriptParser->parse($typoScript);
450  self::assertEmpty($this->typoScriptParser->errors);
451  }
452 
456  public function ‪includeFileDataProvider(): array
457  {
458  return [
459  'TS code before not matching include' => [
460  '
461  foo = bar
462  <INCLUDE_TYPOSCRIPT: source="FILE:dev.ts" condition="applicationContext matches \"/^NotMatched/\"">
463  '
464  ],
465  'TS code after not matching include' => [
466  '
467  <INCLUDE_TYPOSCRIPT: source="FILE:dev.ts" condition="applicationContext matches \"/^NotMatched/\"">
468  foo = bar
469  '
470  ],
471  ];
472  }
473 
479  public function ‪includeFilesWithConditions(string $typoScript): void
480  {
481  // This test triggers a BackendUtility::BEgetRootLine() down below, we need to suppress the cache call
482  $cacheManagerProphecy = $this->prophesize(CacheManager::class);
483  $cacheProphecy = $this->prophesize(FrontendInterface::class);
484  $cacheManagerProphecy->getCache('runtime')->willReturn($cacheProphecy->reveal());
485  $cacheProphecy->get(Argument::cetera())->willReturn(false);
486  $cacheProphecy->set(Argument::cetera())->willReturn(false);
487  GeneralUtility::setSingletonInstance(CacheManager::class, $cacheManagerProphecy->reveal());
488 
489  $p = $this->prophesize(ConditionMatcher::class);
490  $p->match(Argument::cetera())->willReturn(false);
491  GeneralUtility::addInstance(ConditionMatcher::class, $p->reveal());
492 
493  $resolvedIncludeLines = ‪TypoScriptParser::checkIncludeLines($typoScript);
494  self::assertStringContainsString('foo = bar', $resolvedIncludeLines);
495  self::assertStringNotContainsString('INCLUDE_TYPOSCRIPT', $resolvedIncludeLines);
496  }
497 
501  public function ‪importFilesDataProvider(): array
502  {
503  return [
504  'Found include file as single file is imported' => [
505  // Input TypoScript
506  '@import "EXT:core/Tests/Unit/TypoScript/Fixtures/ext_typoscript_setup.txt"'
507  ,
508  // Expected
509  '
510 ### @import \'EXT:core/Tests/Unit/TypoScript/Fixtures/ext_typoscript_setup.txt\' begin ###
511 test.Core.TypoScript = 1
512 ### @import \'EXT:core/Tests/Unit/TypoScript/Fixtures/ext_typoscript_setup.txt\' end ###
513 '
514  ],
515  'Found include file is imported' => [
516  // Input TypoScript
517  'bennilove = before
518 @import "EXT:core/Tests/Unit/TypoScript/Fixtures/ext_typoscript_setup.txt"
519 '
520  ,
521  // Expected
522  '
523 bennilove = before
524 
525 ### @import \'EXT:core/Tests/Unit/TypoScript/Fixtures/ext_typoscript_setup.txt\' begin ###
526 test.Core.TypoScript = 1
527 ### @import \'EXT:core/Tests/Unit/TypoScript/Fixtures/ext_typoscript_setup.txt\' end ###
528 '
529  ],
530  'Not found file is not imported' => [
531  // Input TypoScript
532  'bennilove = before
533 @import "EXT:core/Tests/Unit/TypoScript/Fixtures/notfoundfile.txt"
534 '
535  ,
536  // Expected
537  '
538 bennilove = before
539 
540 ###
541 ### ERROR: No file or folder found for importing TypoScript on "EXT:core/Tests/Unit/TypoScript/Fixtures/notfoundfile.txt".
542 ###
543 '
544  ],
545  'All files with glob are imported' => [
546  // Input TypoScript
547  'bennilove = before
548 @import "EXT:core/Tests/Unit/TypoScript/Fixtures/*.txt"
549 '
550  ,
551  // Expected
552  '
553 bennilove = before
554 
555 ### @import \'EXT:core/Tests/Unit/TypoScript/Fixtures/ext_typoscript_setup.txt\' begin ###
556 test.Core.TypoScript = 1
557 ### @import \'EXT:core/Tests/Unit/TypoScript/Fixtures/ext_typoscript_setup.txt\' end ###
558 '
559  ],
560  'Specific file with typoscript ending is imported' => [
561  // Input TypoScript
562  'bennilove = before
563 @import "EXT:core/Tests/Unit/TypoScript/Fixtures/setup.typoscript"
564 '
565  ,
566  // Expected
567  '
568 bennilove = before
569 
570 ### @import \'EXT:core/Tests/Unit/TypoScript/Fixtures/setup.typoscript\' begin ###
571 test.TYPO3Forever.TypoScript = 1
572 
573 ### @import \'EXT:core/Tests/Unit/TypoScript/Fixtures/setup.typoscript\' end ###
574 '
575  ],
576  'All files in folder are imported, sorted by name' => [
577  // Input TypoScript
578  'bennilove = before
579 @import "EXT:core/Tests/Unit/TypoScript/Fixtures/"
580 '
581  ,
582  // Expected
583  '
584 bennilove = before
585 
586 ### @import \'EXT:core/Tests/Unit/TypoScript/Fixtures/recursive_includes_setup.typoscript\' begin ###
587 
588 ### @import \'EXT:core/Tests/Unit/TypoScript/Fixtures/setup.typoscript\' begin ###
589 test.TYPO3Forever.TypoScript = 1
590 
591 ### @import \'EXT:core/Tests/Unit/TypoScript/Fixtures/setup.typoscript\' end ###
592 
593 ### @import \'EXT:core/Tests/Unit/TypoScript/Fixtures/recursive_includes_setup.typoscript\' end ###
594 
595 
596 ### @import \'EXT:core/Tests/Unit/TypoScript/Fixtures/setup.typoscript\' begin ###
597 test.TYPO3Forever.TypoScript = 1
598 
599 ### @import \'EXT:core/Tests/Unit/TypoScript/Fixtures/setup.typoscript\' end ###
600 '
601  ],
602  'All files ending with typoscript in folder are imported' => [
603  // Input TypoScript
604  'bennilove = before
605 @import "EXT:core/Tests/Unit/TypoScript/Fixtures/*typoscript"
606 '
607  ,
608  // Expected
609  '
610 bennilove = before
611 
612 ### @import \'EXT:core/Tests/Unit/TypoScript/Fixtures/recursive_includes_setup.typoscript\' begin ###
613 
614 ### @import \'EXT:core/Tests/Unit/TypoScript/Fixtures/setup.typoscript\' begin ###
615 test.TYPO3Forever.TypoScript = 1
616 
617 ### @import \'EXT:core/Tests/Unit/TypoScript/Fixtures/setup.typoscript\' end ###
618 
619 ### @import \'EXT:core/Tests/Unit/TypoScript/Fixtures/recursive_includes_setup.typoscript\' end ###
620 
621 
622 ### @import \'EXT:core/Tests/Unit/TypoScript/Fixtures/setup.typoscript\' begin ###
623 test.TYPO3Forever.TypoScript = 1
624 
625 ### @import \'EXT:core/Tests/Unit/TypoScript/Fixtures/setup.typoscript\' end ###
626 '
627  ],
628  'All typoscript files in folder are imported' => [
629  // Input TypoScript
630  'bennilove = before
631 @import "EXT:core/Tests/Unit/TypoScript/Fixtures/*.typoscript"
632 '
633  ,
634  // Expected
635  '
636 bennilove = before
637 
638 ### @import \'EXT:core/Tests/Unit/TypoScript/Fixtures/recursive_includes_setup.typoscript\' begin ###
639 
640 ### @import \'EXT:core/Tests/Unit/TypoScript/Fixtures/setup.typoscript\' begin ###
641 test.TYPO3Forever.TypoScript = 1
642 
643 ### @import \'EXT:core/Tests/Unit/TypoScript/Fixtures/setup.typoscript\' end ###
644 
645 ### @import \'EXT:core/Tests/Unit/TypoScript/Fixtures/recursive_includes_setup.typoscript\' end ###
646 
647 
648 ### @import \'EXT:core/Tests/Unit/TypoScript/Fixtures/setup.typoscript\' begin ###
649 test.TYPO3Forever.TypoScript = 1
650 
651 ### @import \'EXT:core/Tests/Unit/TypoScript/Fixtures/setup.typoscript\' end ###
652 '
653  ],
654  'All typoscript files in folder with glob are not imported due to recursion level=0' => [
655  // Input TypoScript
656  'bennilove = before
657 @import "EXT:core/Tests/Unit/**/*.typoscript"
658 '
659  ,
660  // Expected
661  '
662 bennilove = before
663 
664 ###
665 ### ERROR: No file or folder found for importing TypoScript on "EXT:core/Tests/Unit/**/*.typoscript".
666 ###
667 '
668  ],
669  'TypoScript file ending is automatically added' => [
670  // Input TypoScript
671  'bennilove = before
672 @import "EXT:core/Tests/Unit/TypoScript/Fixtures/setup"
673 '
674  ,
675  // Expected
676  '
677 bennilove = before
678 
679 ### @import \'EXT:core/Tests/Unit/TypoScript/Fixtures/setup.typoscript\' begin ###
680 test.TYPO3Forever.TypoScript = 1
681 
682 ### @import \'EXT:core/Tests/Unit/TypoScript/Fixtures/setup.typoscript\' end ###
683 '
684  ],
685  ];
686  }
687 
694  public function ‪importFiles(string $typoScript, string $expected): void
695  {
696  $resolvedIncludeLines = ‪TypoScriptParser::checkIncludeLines($typoScript);
697  self::assertEquals($expected, $resolvedIncludeLines);
698  }
699 
706  public function ‪typoScriptIsParsedToArray(string $typoScript, array $expected): void
707  {
708  $this->typoScriptParser->parse($typoScript);
709  self::assertEquals($expected, $this->typoScriptParser->setup);
710  }
711 
715  public function ‪typoScriptIsParsedToArrayDataProvider(): array
716  {
717  return [
718  'simple assignment' => [
719  'key = value',
720  [
721  'key' => 'value',
722  ]
723  ],
724  'simple assignment with slash in key' => [
725  'lib/key = value',
726  [
727  'lib/key' => 'value',
728  ],
729  ],
730  'simple assignment with escaped dot at the beginning' => [
731  '\\.key = value',
732  [
733  '.key' => 'value',
734  ]
735  ],
736  'simple assignment with protected escaped dot at the beginning' => [
737  '\\\\.key = value',
738  [
739  '\\.' => [
740  'key' => 'value',
741  ],
742  ]
743  ],
744  'nested assignment' => [
745  'lib.key = value',
746  [
747  'lib.' => [
748  'key' => 'value',
749  ],
750  ],
751  ],
752  'nested assignment with escaped key' => [
753  'lib\\.key = value',
754  [
755  'lib.key' => 'value',
756  ],
757  ],
758  'nested assignment with escaped key and escaped dot at the beginning' => [
759  '\\.lib\\.key = value',
760  [
761  '.lib.key' => 'value',
762  ],
763  ],
764  'nested assignment with protected escaped key' => [
765  'lib\\\\.key = value',
766  [
767  'lib\\.' => ['key' => 'value'],
768  ],
769  ],
770  'nested assignment with protected escaped key and protected escaped dot at the beginning' => [
771  '\\\\.lib\\\\.key = value',
772  [
773  '\\.' => [
774  'lib\\.' => ['key' => 'value'],
775  ],
776  ],
777  ],
778  'assignment with escaped an non escaped keys' => [
779  'firstkey.secondkey\\.thirdkey.setting = value',
780  [
781  'firstkey.' => [
782  'secondkey.thirdkey.' => [
783  'setting' => 'value'
784  ]
785  ]
786  ]
787  ],
788  'nested structured assignment' => [
789  'lib {' . LF .
790  'key = value' . LF .
791  '}',
792  [
793  'lib.' => [
794  'key' => 'value',
795  ],
796  ],
797  ],
798  'nested structured assignment with escaped key inside' => [
799  'lib {' . LF .
800  'key\\.nextkey = value' . LF .
801  '}',
802  [
803  'lib.' => [
804  'key.nextkey' => 'value',
805  ],
806  ],
807  ],
808  'nested structured assignment with escaped key inside and escaped dots at the beginning' => [
809  '\\.lib {' . LF .
810  '\\.key\\.nextkey = value' . LF .
811  '}',
812  [
813  '.lib.' => [
814  '.key.nextkey' => 'value',
815  ],
816  ],
817  ],
818  'nested structured assignment with protected escaped key inside' => [
819  'lib {' . LF .
820  'key\\\\.nextkey = value' . LF .
821  '}',
822  [
823  'lib.' => [
824  'key\\.' => ['nextkey' => 'value'],
825  ],
826  ],
827  ],
828  'nested structured assignment with protected escaped key inside and protected escaped dots at the beginning' => [
829  '\\\\.lib {' . LF .
830  '\\\\.key\\\\.nextkey = value' . LF .
831  '}',
832  [
833  '\\.' => [
834  'lib.' => [
835  '\\.' => [
836  'key\\.' => ['nextkey' => 'value'],
837  ],
838  ],
839  ],
840  ],
841  ],
842  'nested structured assignment with escaped key' => [
843  'lib\\.anotherkey {' . LF .
844  'key = value' . LF .
845  '}',
846  [
847  'lib.anotherkey.' => [
848  'key' => 'value',
849  ],
850  ],
851  ],
852  'nested structured assignment with protected escaped key' => [
853  'lib\\\\.anotherkey {' . LF .
854  'key = value' . LF .
855  '}',
856  [
857  'lib\\.' => [
858  'anotherkey.' => [
859  'key' => 'value',
860  ],
861  ],
862  ],
863  ],
864  'multiline assignment' => [
865  'key (' . LF .
866  'first' . LF .
867  'second' . LF .
868  ')',
869  [
870  'key' => 'first' . LF . 'second',
871  ],
872  ],
873  'multiline assignment with escaped key' => [
874  'key\\.nextkey (' . LF .
875  'first' . LF .
876  'second' . LF .
877  ')',
878  [
879  'key.nextkey' => 'first' . LF . 'second',
880  ],
881  ],
882  'multiline assignment with protected escaped key' => [
883  'key\\\\.nextkey (' . LF .
884  'first' . LF .
885  'second' . LF .
886  ')',
887  [
888  'key\\.' => ['nextkey' => 'first' . LF . 'second'],
889  ],
890  ],
891  'copying values' => [
892  'lib.default = value' . LF .
893  'lib.copy < lib.default',
894  [
895  'lib.' => [
896  'default' => 'value',
897  'copy' => 'value',
898  ],
899  ],
900  ],
901  'copying values with escaped key' => [
902  'lib\\.default = value' . LF .
903  'lib.copy < lib\\.default',
904  [
905  'lib.default' => 'value',
906  'lib.' => [
907  'copy' => 'value',
908  ],
909  ],
910  ],
911  'copying values with protected escaped key' => [
912  'lib\\\\.default = value' . LF .
913  'lib.copy < lib\\\\.default',
914  [
915  'lib\\.' => ['default' => 'value'],
916  'lib.' => [
917  'copy' => 'value',
918  ],
919  ],
920  ],
921  'one-line hash comment' => [
922  'first = 1' . LF .
923  '# ignore = me' . LF .
924  'second = 2',
925  [
926  'first' => '1',
927  'second' => '2',
928  ],
929  ],
930  'one-line slash comment' => [
931  'first = 1' . LF .
932  '// ignore = me' . LF .
933  'second = 2',
934  [
935  'first' => '1',
936  'second' => '2',
937  ],
938  ],
939  'multi-line slash comment' => [
940  'first = 1' . LF .
941  '/*' . LF .
942  'ignore = me' . LF .
943  '*/' . LF .
944  'second = 2',
945  [
946  'first' => '1',
947  'second' => '2',
948  ],
949  ],
950  'multi-line slash comment in one line' => [
951  'first = 1' . LF .
952  '/* ignore = me */' . LF .
953  '/**** ignore = me **/' . LF .
954  'second = 2',
955  [
956  'first' => '1',
957  'second' => '2',
958  ],
959  ],
960  'nested assignment repeated segment names' => [
961  'test.test.test = 1',
962  [
963  'test.' => [
964  'test.' => [
965  'test' => '1',
966  ],
967  ]
968  ],
969  ],
970  'simple assignment operator with tab character before "="' => [
971  'test = someValue',
972  [
973  'test' => 'someValue',
974  ],
975  ],
976  'simple assignment operator character as value "="' => [
977  'test ==TEST=',
978  [
979  'test' => '=TEST=',
980  ],
981  ],
982  'nested assignment operator character as value "="' => [
983  'test.test ==TEST=',
984  [
985  'test.' => [
986  'test' => '=TEST=',
987  ],
988  ],
989  ],
990  'simple assignment character as value "<"' => [
991  'test =<TEST>',
992  [
993  'test' => '<TEST>',
994  ],
995  ],
996  'nested assignment character as value "<"' => [
997  'test.test =<TEST>',
998  [
999  'test.' => [
1000  'test' => '<TEST>',
1001  ],
1002  ],
1003  ],
1004  'simple assignment character as value ">"' => [
1005  'test =>TEST<',
1006  [
1007  'test' => '>TEST<',
1008  ],
1009  ],
1010  'nested assignment character as value ">"' => [
1011  'test.test =>TEST<',
1012  [
1013  'test.' => [
1014  'test' => '>TEST<',
1015  ],
1016  ],
1017  ],
1018  'nested assignment repeated segment names with whitespaces' => [
1019  'test.test.test = 1' . " \t",
1020  [
1021  'test.' => [
1022  'test.' => [
1023  'test' => '1',
1024  ],
1025  ]
1026  ],
1027  ],
1028  'simple assignment operator character as value "=" with whitespaces' => [
1029  'test = =TEST=' . " \t",
1030  [
1031  'test' => '=TEST=',
1032  ],
1033  ],
1034  'nested assignment operator character as value "=" with whitespaces' => [
1035  'test.test = =TEST=' . " \t",
1036  [
1037  'test.' => [
1038  'test' => '=TEST=',
1039  ],
1040  ],
1041  ],
1042  'simple assignment character as value "<" with whitespaces' => [
1043  'test = <TEST>' . " \t",
1044  [
1045  'test' => '<TEST>',
1046  ],
1047  ],
1048  'nested assignment character as value "<" with whitespaces' => [
1049  'test.test = <TEST>' . " \t",
1050  [
1051  'test.' => [
1052  'test' => '<TEST>',
1053  ],
1054  ],
1055  ],
1056  'simple assignment character as value ">" with whitespaces' => [
1057  'test = >TEST<' . " \t",
1058  [
1059  'test' => '>TEST<',
1060  ],
1061  ],
1062  'nested assignment character as value ">" with whitespaces' => [
1063  'test.test = >TEST<',
1064  [
1065  'test.' => [
1066  'test' => '>TEST<',
1067  ],
1068  ],
1069  ],
1070  'CSC example #1' => [
1071  'linkParams.ATagParams.dataWrap = class="{$styles.content.imgtext.linkWrap.lightboxCssClass}" rel="{$styles.content.imgtext.linkWrap.lightboxRelAttribute}"',
1072  [
1073  'linkParams.' => [
1074  'ATagParams.' => [
1075  'dataWrap' => 'class="{$styles.content.imgtext.linkWrap.lightboxCssClass}" rel="{$styles.content.imgtext.linkWrap.lightboxRelAttribute}"',
1076  ],
1077  ],
1078  ],
1079  ],
1080  'CSC example #2' => [
1081  'linkParams.ATagParams {' . LF .
1082  'dataWrap = class="{$styles.content.imgtext.linkWrap.lightboxCssClass}" rel="{$styles.content.imgtext.linkWrap.lightboxRelAttribute}"' . LF .
1083  '}',
1084  [
1085  'linkParams.' => [
1086  'ATagParams.' => [
1087  'dataWrap' => 'class="{$styles.content.imgtext.linkWrap.lightboxCssClass}" rel="{$styles.content.imgtext.linkWrap.lightboxRelAttribute}"',
1088  ],
1089  ],
1090  ],
1091  ],
1092  'CSC example #3' => [
1093  'linkParams.ATagParams.dataWrap (' . LF .
1094  'class="{$styles.content.imgtext.linkWrap.lightboxCssClass}" rel="{$styles.content.imgtext.linkWrap.lightboxRelAttribute}"' . LF .
1095  ')',
1096  [
1097  'linkParams.' => [
1098  'ATagParams.' => [
1099  'dataWrap' => 'class="{$styles.content.imgtext.linkWrap.lightboxCssClass}" rel="{$styles.content.imgtext.linkWrap.lightboxRelAttribute}"',
1100  ],
1101  ],
1102  ],
1103  ],
1104  'key with colon' => [
1105  'some:key = is valid',
1106  [
1107  'some:key' => 'is valid'
1108  ]
1109  ],
1110  'special operator' => [
1111  'some := addToList(a)',
1112  [
1113  'some' => 'a'
1114  ]
1115  ],
1116  'special operator with white-spaces' => [
1117  'some := addToList (a)',
1118  [
1119  'some' => 'a'
1120  ]
1121  ],
1122  'special operator with tabs' => [
1123  'some := addToList (a)',
1124  [
1125  'some' => 'a'
1126  ]
1127  ],
1128  'special operator with white-spaces and tabs in value' => [
1129  'some := addToList( a, b, c )',
1130  [
1131  'some' => 'a, b, c'
1132  ]
1133  ],
1134  'special operator and colon, no spaces' => [
1135  'some:key:=addToList(a)',
1136  [
1137  'some:key' => 'a'
1138  ]
1139  ],
1140  'key with all special symbols' => [
1141  'someSpecial\\_:-\\.Chars = is valid',
1142  [
1143  'someSpecial\\_:-.Chars' => 'is valid'
1144  ]
1145  ],
1146  ];
1147  }
1148 
1152  public function ‪setValCanBeCalledWithArrayValueParameter(): void
1153  {
1154  $string = '';
1155  $setup = [];
1156  $value = [];
1157  ‪$typoScriptParser = new TypoScriptParser();
1158  $mock = \Closure::bind(
1159  static function (TypoScriptParser ‪$typoScriptParser) use ($string, &$setup, $value) {
1160  return ‪$typoScriptParser->‪setVal($string, $setup, $value);
1161  },
1162  null,
1163  TypoScriptParser::class
1164  );
1165  $mock(‪$typoScriptParser);
1166  }
1167 
1171  public function ‪setValCanBeCalledWithStringValueParameter(): void
1172  {
1173  $string = '';
1174  $setup = [];
1175  $value = '';
1176  ‪$typoScriptParser = new TypoScriptParser();
1177  $mock = \Closure::bind(
1178  static function (TypoScriptParser ‪$typoScriptParser) use ($string, &$setup, $value) {
1179  return ‪$typoScriptParser->‪setVal($string, $setup, $value);
1180  },
1181  null,
1182  TypoScriptParser::class
1183  );
1184  $mock(‪$typoScriptParser);
1185  }
1186 
1195  string $key,
1196  string $expectedKeySegment,
1197  string $expectedRemainingKey
1198  ): void {
1199  [$keySegment, $remainingKey] = $this->typoScriptParser->_call('parseNextKeySegment', $key);
1200  self::assertSame($expectedKeySegment, $keySegment);
1201  self::assertSame($expectedRemainingKey, $remainingKey);
1202  }
1203 
1208  {
1209  return [
1210  'key without separator' => [
1211  'testkey',
1212  'testkey',
1213  ''
1214  ],
1215  'key with normal separator' => [
1216  'test.key',
1217  'test',
1218  'key'
1219  ],
1220  'key with multiple normal separators' => [
1221  'test.key.subkey',
1222  'test',
1223  'key.subkey'
1224  ],
1225  'key with separator and escape character' => [
1226  'te\\st.test',
1227  'te\\st',
1228  'test'
1229  ],
1230  'key with escaped separators' => [
1231  'test\\.key\\.subkey',
1232  'test.key.subkey',
1233  ''
1234  ],
1235  'key with escaped and unescaped separator 1' => [
1236  'test.test\\.key',
1237  'test',
1238  'test\\.key'
1239  ],
1240  'key with escaped and unescaped separator 2' => [
1241  'test\\.test.key\\.key2',
1242  'test.test',
1243  'key\\.key2'
1244  ],
1245  'key with escaped escape character' => [
1246  'test\\\\.key',
1247  'test\\',
1248  'key'
1249  ],
1250  'key with escaped separator and additional escape character' => [
1251  'test\\\\\\.key',
1252  'test\\\\',
1253  'key'
1254  ],
1255 
1256  'multiple escape characters within the key are preserved' => [
1257  'te\\\\st\\\\.key',
1258  'te\\\\st\\',
1259  'key'
1260  ]
1261  ];
1262  }
1263 }
‪TYPO3\CMS\Core\Tests\Unit\TypoScript\Parser
Definition: ConstantConfigurationParserTest.php:18
‪TYPO3\CMS\Core\Tests\Unit\TypoScript\Parser\TypoScriptParserTest\setValCanBeCalledWithStringValueParameter
‪setValCanBeCalledWithStringValueParameter()
Definition: TypoScriptParserTest.php:1170
‪TYPO3\CMS\Core\Tests\Unit\TypoScript\Parser\TypoScriptParserTest\tearDown
‪tearDown()
Definition: TypoScriptParserTest.php:51
‪TYPO3\CMS\Core\TypoScript\Parser\TypoScriptParser
Definition: TypoScriptParser.php:37
‪TYPO3\CMS\Core\Tests\Unit\TypoScript\Parser\TypoScriptParserTest\typoScriptIsParsedToArray
‪typoScriptIsParsedToArray(string $typoScript, array $expected)
Definition: TypoScriptParserTest.php:705
‪TYPO3\CMS\Core\Tests\Unit\TypoScript\Parser\TypoScriptParserTest\importFiles
‪importFiles(string $typoScript, string $expected)
Definition: TypoScriptParserTest.php:693
‪TYPO3\CMS\Backend\Configuration\TypoScript\ConditionMatching\ConditionMatcher
Definition: ConditionMatcher.php:30
‪TYPO3\CMS\Core\Tests\Unit\TypoScript\Parser\TypoScriptParserTest\setUp
‪setUp()
Definition: TypoScriptParserTest.php:42
‪TYPO3\CMS\Core\Tests\Unit\TypoScript\Parser\TypoScriptParserTest\executeValueModifierDataProvider
‪array executeValueModifierDataProvider()
Definition: TypoScriptParserTest.php:62
‪TYPO3\CMS\Core\Tests\Unit\TypoScript\Parser\TypoScriptParserTest\importFilesDataProvider
‪array importFilesDataProvider()
Definition: TypoScriptParserTest.php:500
‪TYPO3\CMS\Core\Tests\Unit\TypoScript\Parser\TypoScriptParserTest\doubleSlashCommentsAreValid
‪doubleSlashCommentsAreValid(string $typoScript)
Definition: TypoScriptParserTest.php:446
‪TYPO3\CMS\Core\Tests\Unit\TypoScript\Parser\TypoScriptParserTest\emptyConditionIsReported
‪emptyConditionIsReported()
Definition: TypoScriptParserTest.php:418
‪TYPO3\CMS\Core\Tests\Unit\TypoScript\Parser\TypoScriptParserTest\includeFilesWithConditions
‪includeFilesWithConditions(string $typoScript)
Definition: TypoScriptParserTest.php:478
‪TYPO3\CMS\Core\Tests\Unit\TypoScript\Parser\TypoScriptParserTest\executeValueModifierReturnsModifiedResult
‪executeValueModifierReturnsModifiedResult(string $modifierName, string $currentValue, string $modifierArgument, string $expected)
Definition: TypoScriptParserTest.php:250
‪TYPO3\CMS\Core\Tests\Unit\TypoScript\Parser\TypoScriptParserTest\$typoScriptParser
‪TypoScriptParser AccessibleObjectInterface $typoScriptParser
Definition: TypoScriptParserTest.php:37
‪TYPO3\CMS\Core\TypoScript\Parser\TypoScriptParser\checkIncludeLines
‪static string array checkIncludeLines($string, $cycle_counter=1, $returnFiles=false, $parentFilenameOrPath='')
Definition: TypoScriptParser.php:792
‪TYPO3\CMS\Core\Cache\CacheManager
Definition: CacheManager.php:35
‪TYPO3\CMS\Core\Tests\Unit\TypoScript\Parser\TypoScriptParserTest\invalidCharactersInObjectNamesAreReported
‪invalidCharactersInObjectNamesAreReported()
Definition: TypoScriptParserTest.php:377
‪TYPO3\CMS\Core\Tests\Unit\TypoScript\Parser\TypoScriptParserTest\typoScriptIsParsedToArrayDataProvider
‪array typoScriptIsParsedToArrayDataProvider()
Definition: TypoScriptParserTest.php:714
‪TYPO3\CMS\Core\Tests\Unit\TypoScript\Parser\TypoScriptParserTest\executeGetEnvModifierReturnsModifiedResult
‪executeGetEnvModifierReturnsModifiedResult(array $environmentVariables, ?string $currentValue, string $modifierArgument, ?string $expected)
Definition: TypoScriptParserTest.php:315
‪TYPO3\CMS\Core\Cache\Frontend\FrontendInterface
Definition: FrontendInterface.php:22
‪TYPO3\CMS\Core\Tests\Unit\TypoScript\Parser\TypoScriptParserTest\executeGetEnvModifierDataProvider
‪executeGetEnvModifierDataProvider()
Definition: TypoScriptParserTest.php:265
‪TYPO3\CMS\Core\Tests\Unit\TypoScript\Parser\TypoScriptParserTest\parseNextKeySegmentReturnsCorrectNextKeySegmentDataProvider
‪array parseNextKeySegmentReturnsCorrectNextKeySegmentDataProvider()
Definition: TypoScriptParserTest.php:1206
‪TYPO3\CMS\Core\Tests\Unit\TypoScript\Parser\TypoScriptParserTest\executeValueModifierThrowsException
‪executeValueModifierThrowsException(string $modifierName, string $currentValue, string $modifierArgument)
Definition: TypoScriptParserTest.php:364
‪TYPO3\CMS\Core\Tests\Unit\TypoScript\Parser\TypoScriptParserTest\parseNextKeySegmentReturnsCorrectNextKeySegment
‪parseNextKeySegmentReturnsCorrectNextKeySegment(string $key, string $expectedKeySegment, string $expectedRemainingKey)
Definition: TypoScriptParserTest.php:1193
‪TYPO3\CMS\Core\Tests\Unit\TypoScript\Parser\TypoScriptParserTest\setValCanBeCalledWithArrayValueParameter
‪setValCanBeCalledWithArrayValueParameter()
Definition: TypoScriptParserTest.php:1151
‪TYPO3\CMS\Core\Tests\Unit\TypoScript\Parser\TypoScriptParserTest\doubleSlashCommentsDataProvider
‪array doubleSlashCommentsDataProvider()
Definition: TypoScriptParserTest.php:432
‪TYPO3\CMS\Core\Tests\Unit\TypoScript\Parser\TypoScriptParserTest\invalidConditionsAreReported
‪invalidConditionsAreReported(string $condition, bool $isValid)
Definition: TypoScriptParserTest.php:403
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:46
‪TYPO3\CMS\Core\Tests\Unit\TypoScript\Parser\TypoScriptParserTest\invalidConditionsDataProvider
‪invalidConditionsDataProvider()
Definition: TypoScriptParserTest.php:388
‪TYPO3\CMS\Core\TimeTracker\TimeTracker
Definition: TimeTracker.php:30
‪TYPO3\CMS\Core\TypoScript\Parser\TypoScriptParser\setVal
‪setVal($string, array &$setup, $value, $wipeOut=false)
Definition: TypoScriptParser.php:667
‪TYPO3\CMS\Core\Tests\Unit\TypoScript\Parser\TypoScriptParserTest
Definition: TypoScriptParserTest.php:34
‪TYPO3\CMS\Core\Tests\Unit\TypoScript\Parser\TypoScriptParserTest\includeFileDataProvider
‪array includeFileDataProvider()
Definition: TypoScriptParserTest.php:455
‪TYPO3\CMS\Core\Tests\Unit\TypoScript\Parser\TypoScriptParserTest\executeValueModifierInvalidDataProvider
‪array executeValueModifierInvalidDataProvider()
Definition: TypoScriptParserTest.php:341