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