TYPO3 CMS  TYPO3_7-6
TypoScriptParserTest.php
Go to the documentation of this file.
1 <?php
3 
4 /*
5  * This file is part of the TYPO3 CMS project.
6  *
7  * It is free software; you can redistribute it and/or modify it under
8  * the terms of the GNU General Public License, either version 2
9  * of the License, or any later version.
10  *
11  * For the full copyright and license information, please read the
12  * LICENSE.txt file that was distributed with this source code.
13  *
14  * The TYPO3 project - inspiring people to share!
15  */
16 
18 
23 {
27  protected $typoScriptParser = null;
28 
34  protected function setUp()
35  {
36  $accessibleClassName = $this->buildAccessibleProxy(\TYPO3\CMS\Core\TypoScript\Parser\TypoScriptParser::class);
37  $this->typoScriptParser = new $accessibleClassName();
38  }
39 
46  {
47  return [
48  'prependString with string' => [
49  'prependString',
50  'abc',
51  '!',
52  '!abc'
53  ],
54  'prependString with empty string' => [
55  'prependString',
56  'foo',
57  '',
58  'foo',
59  ],
60  'appendString with string' => [
61  'appendString',
62  'abc',
63  '!',
64  'abc!',
65  ],
66  'appendString with empty string' => [
67  'appendString',
68  'abc',
69  '',
70  'abc',
71  ],
72  'removeString removes simple string' => [
73  'removeString',
74  'abcdef',
75  'bc',
76  'adef',
77  ],
78  'removeString removes nothing if no match' => [
79  'removeString',
80  'abcdef',
81  'foo',
82  'abcdef',
83  ],
84  'removeString removes multiple matches' => [
85  'removeString',
86  'FooBarFoo',
87  'Foo',
88  'Bar',
89  ],
90  'replaceString replaces simple match' => [
91  'replaceString',
92  'abcdef',
93  'bc|123',
94  'a123def',
95  ],
96  'replaceString replaces simple match with nothing' => [
97  'replaceString',
98  'abcdef',
99  'bc',
100  'adef',
101  ],
102  'replaceString replaces multiple matches' => [
103  'replaceString',
104  'FooBarFoo',
105  'Foo|Bar',
106  'BarBarBar',
107  ],
108  'addToList adds at end of existing list' => [
109  'addToList',
110  '123,456',
111  '789',
112  '123,456,789',
113  ],
114  'addToList adds at end of existing list including white-spaces' => [
115  'addToList',
116  '123,456',
117  ' 789 , 32 , 12 ',
118  '123,456, 789 , 32 , 12 ',
119  ],
120  'addToList adds nothing' => [
121  'addToList',
122  '123,456',
123  '',
124  '123,456,', // This result is probably not what we want (appended comma) ... fix it?
125  ],
126  'addToList adds to empty list' => [
127  'addToList',
128  '',
129  'foo',
130  'foo',
131  ],
132  'removeFromList removes value from list' => [
133  'removeFromList',
134  '123,456,789,abc',
135  '456',
136  '123,789,abc',
137  ],
138  'removeFromList removes value at beginning of list' => [
139  'removeFromList',
140  '123,456,abc',
141  '123',
142  '456,abc',
143  ],
144  'removeFromList removes value at end of list' => [
145  'removeFromList',
146  '123,456,abc',
147  'abc',
148  '123,456',
149  ],
150  'removeFromList removes multiple values from list' => [
151  'removeFromList',
152  'foo,123,bar,123',
153  '123',
154  'foo,bar',
155  ],
156  'removeFromList removes empty values' => [
157  'removeFromList',
158  'foo,,bar',
159  '',
160  'foo,bar',
161  ],
162  'uniqueList removes duplicates' => [
163  'uniqueList',
164  '123,456,abc,456,456',
165  '',
166  '123,456,abc',
167  ],
168  'uniqueList removes duplicate empty list values' => [
169  'uniqueList',
170  '123,,456,,abc',
171  '',
172  '123,,456,abc',
173  ],
174  'reverseList returns list reversed' => [
175  'reverseList',
176  '123,456,abc,456',
177  '',
178  '456,abc,456,123',
179  ],
180  'reverseList keeps empty values' => [
181  'reverseList',
182  ',123,,456,abc,,456',
183  '',
184  '456,,abc,456,,123,',
185  ],
186  'reverseList does not change single element' => [
187  'reverseList',
188  '123',
189  '',
190  '123',
191  ],
192  'sortList sorts a list' => [
193  'sortList',
194  '10,100,0,20,abc',
195  '',
196  '0,10,20,100,abc',
197  ],
198  'sortList sorts a list numeric' => [
199  'sortList',
200  '10,0,100,-20',
201  'numeric',
202  '-20,0,10,100',
203  ],
204  'sortList sorts a list descending' => [
205  'sortList',
206  '10,100,0,20,abc,-20',
207  'descending',
208  'abc,100,20,10,0,-20',
209  ],
210  'sortList sorts a list numeric descending' => [
211  'sortList',
212  '10,100,0,20,-20',
213  'descending,numeric',
214  '100,20,10,0,-20',
215  ],
216  'sortList ignores invalid modifier arguments' => [
217  'sortList',
218  '10,100,20',
219  'foo,descending,bar',
220  '100,20,10',
221  ],
222  ];
223  }
224 
229  public function executeValueModifierReturnsModifiedResult($modifierName, $currentValue, $modifierArgument, $expected)
230  {
231  $actualValue = $this->typoScriptParser->_call('executeValueModifier', $modifierName, $modifierArgument, $currentValue);
232  $this->assertEquals($expected, $actualValue);
233  }
234 
241  {
242  return [
243  'sortList sorts a list numeric' => [
244  'sortList',
245  '10,0,100,-20,abc',
246  'numeric',
247  ],
248  'sortList sorts a list numeric descending' => [
249  'sortList',
250  '10,100,0,20,abc,-20',
251  'descending,numeric',
252  ],
253  ];
254  }
255 
260  public function executeValueModifierThrowsException($modifierName, $currentValue, $modifierArgument)
261  {
262  $this->setExpectedException('InvalidArgumentException', 'The list "' . $currentValue . '" should be sorted numerically but contains a non-numeric value');
263  $this->typoScriptParser->_call('executeValueModifier', $modifierName, $modifierArgument, $currentValue);
264  }
265 
269  public function includeFileDataProvider()
270  {
271  return [
272  'TS code before not matching include' => [
273  '
274  foo = bar
275  <INCLUDE_TYPOSCRIPT: source="FILE:dev.ts" condition="applicationContext = /^NotMatched/">
276  '
277  ],
278  'TS code after not matching include' => [
279  '
280  <INCLUDE_TYPOSCRIPT: source="FILE:dev.ts" condition="applicationContext = /^NotMatched/">
281  foo = bar
282  '
283  ],
284  ];
285  }
286 
291  public function includeFilesWithConditions($typoScript)
292  {
293  $resolvedIncludeLines = TypoScriptParser::checkIncludeLines($typoScript);
294  $this->assertContains('foo = bar', $resolvedIncludeLines);
295  $this->assertNotContains('INCLUDE_TYPOSCRIPT', $resolvedIncludeLines);
296  }
297 
304  public function typoScriptIsParsedToArray($typoScript, array $expected)
305  {
306  $this->typoScriptParser->parse($typoScript);
307  $this->assertEquals($expected, $this->typoScriptParser->setup);
308  }
309 
314  {
315  return [
316  'simple assignment' => [
317  'key = value',
318  [
319  'key' => 'value',
320  ]
321  ],
322  'simple assignment with escaped dot at the beginning' => [
323  '\\.key = value',
324  [
325  '.key' => 'value',
326  ]
327  ],
328  'simple assignment with protected escaped dot at the beginning' => [
329  '\\\\.key = value',
330  [
331  '\\.' => [
332  'key' => 'value',
333  ],
334  ]
335  ],
336  'nested assignment' => [
337  'lib.key = value',
338  [
339  'lib.' => [
340  'key' => 'value',
341  ],
342  ],
343  ],
344  'nested assignment with escaped key' => [
345  'lib\\.key = value',
346  [
347  'lib.key' => 'value',
348  ],
349  ],
350  'nested assignment with escaped key and escaped dot at the beginning' => [
351  '\\.lib\\.key = value',
352  [
353  '.lib.key' => 'value',
354  ],
355  ],
356  'nested assignment with protected escaped key' => [
357  'lib\\\\.key = value',
358  [
359  'lib\\.' => ['key' => 'value'],
360  ],
361  ],
362  'nested assignment with protected escaped key and protected escaped dot at the beginning' => [
363  '\\\\.lib\\\\.key = value',
364  [
365  '\\.' => [
366  'lib\\.' => ['key' => 'value'],
367  ],
368  ],
369  ],
370  'assignment with escaped an non escaped keys' => [
371  'firstkey.secondkey\\.thirdkey.setting = value',
372  [
373  'firstkey.' => [
374  'secondkey.thirdkey.' => [
375  'setting' => 'value'
376  ]
377  ]
378  ]
379  ],
380  'nested structured assignment' => [
381  'lib {' . LF .
382  'key = value' . LF .
383  '}',
384  [
385  'lib.' => [
386  'key' => 'value',
387  ],
388  ],
389  ],
390  'nested structured assignment with escaped key inside' => [
391  'lib {' . LF .
392  'key\\.nextkey = value' . LF .
393  '}',
394  [
395  'lib.' => [
396  'key.nextkey' => 'value',
397  ],
398  ],
399  ],
400  'nested structured assignment with escaped key inside and escaped dots at the beginning' => [
401  '\\.lib {' . LF .
402  '\\.key\\.nextkey = value' . LF .
403  '}',
404  [
405  '.lib.' => [
406  '.key.nextkey' => 'value',
407  ],
408  ],
409  ],
410  'nested structured assignment with protected escaped key inside' => [
411  'lib {' . LF .
412  'key\\\\.nextkey = value' . LF .
413  '}',
414  [
415  'lib.' => [
416  'key\\.' => ['nextkey' => 'value'],
417  ],
418  ],
419  ],
420  'nested structured assignment with protected escaped key inside and protected escaped dots at the beginning' => [
421  '\\\\.lib {' . LF .
422  '\\\\.key\\\\.nextkey = value' . LF .
423  '}',
424  [
425  '\\.' => [
426  'lib.' => [
427  '\\.' => [
428  'key\\.' => ['nextkey' => 'value'],
429  ],
430  ],
431  ],
432  ],
433  ],
434  'nested structured assignment with escaped key' => [
435  'lib\\.anotherkey {' . LF .
436  'key = value' . LF .
437  '}',
438  [
439  'lib.anotherkey.' => [
440  'key' => 'value',
441  ],
442  ],
443  ],
444  'nested structured assignment with protected escaped key' => [
445  'lib\\\\.anotherkey {' . LF .
446  'key = value' . LF .
447  '}',
448  [
449  'lib\\.' => [
450  'anotherkey.' => [
451  'key' => 'value',
452  ],
453  ],
454  ],
455  ],
456  'multiline assignment' => [
457  'key (' . LF .
458  'first' . LF .
459  'second' . LF .
460  ')',
461  [
462  'key' => 'first' . LF . 'second',
463  ],
464  ],
465  'multiline assignment with escaped key' => [
466  'key\\.nextkey (' . LF .
467  'first' . LF .
468  'second' . LF .
469  ')',
470  [
471  'key.nextkey' => 'first' . LF . 'second',
472  ],
473  ],
474  'multiline assignment with protected escaped key' => [
475  'key\\\\.nextkey (' . LF .
476  'first' . LF .
477  'second' . LF .
478  ')',
479  [
480  'key\\.' => ['nextkey' => 'first' . LF . 'second'],
481  ],
482  ],
483  'copying values' => [
484  'lib.default = value' . LF .
485  'lib.copy < lib.default',
486  [
487  'lib.' => [
488  'default' => 'value',
489  'copy' => 'value',
490  ],
491  ],
492  ],
493  'copying values with escaped key' => [
494  'lib\\.default = value' . LF .
495  'lib.copy < lib\\.default',
496  [
497  'lib.default' => 'value',
498  'lib.' => [
499  'copy' => 'value',
500  ],
501  ],
502  ],
503  'copying values with protected escaped key' => [
504  'lib\\\\.default = value' . LF .
505  'lib.copy < lib\\\\.default',
506  [
507  'lib\\.' => ['default' => 'value'],
508  'lib.' => [
509  'copy' => 'value',
510  ],
511  ],
512  ],
513  'one-line hash comment' => [
514  'first = 1' . LF .
515  '# ignore = me' . LF .
516  'second = 2',
517  [
518  'first' => '1',
519  'second' => '2',
520  ],
521  ],
522  'one-line slash comment' => [
523  'first = 1' . LF .
524  '// ignore = me' . LF .
525  'second = 2',
526  [
527  'first' => '1',
528  'second' => '2',
529  ],
530  ],
531  'multi-line slash comment' => [
532  'first = 1' . LF .
533  '/*' . LF .
534  'ignore = me' . LF .
535  '*/' . LF .
536  'second = 2',
537  [
538  'first' => '1',
539  'second' => '2',
540  ],
541  ],
542  'nested assignment repeated segment names' => [
543  'test.test.test = 1',
544  [
545  'test.' => [
546  'test.' => [
547  'test' => '1',
548  ],
549  ]
550  ],
551  ],
552  'simple assignment operator with tab character before "="' => [
553  'test = someValue',
554  [
555  'test' => 'someValue',
556  ],
557  ],
558  'simple assignment operator character as value "="' => [
559  'test ==TEST=',
560  [
561  'test' => '=TEST=',
562  ],
563  ],
564  'nested assignment operator character as value "="' => [
565  'test.test ==TEST=',
566  [
567  'test.' => [
568  'test' => '=TEST=',
569  ],
570  ],
571  ],
572  'simple assignment character as value "<"' => [
573  'test =<TEST>',
574  [
575  'test' => '<TEST>',
576  ],
577  ],
578  'nested assignment character as value "<"' => [
579  'test.test =<TEST>',
580  [
581  'test.' => [
582  'test' => '<TEST>',
583  ],
584  ],
585  ],
586  'simple assignment character as value ">"' => [
587  'test =>TEST<',
588  [
589  'test' => '>TEST<',
590  ],
591  ],
592  'nested assignment character as value ">"' => [
593  'test.test =>TEST<',
594  [
595  'test.' => [
596  'test' => '>TEST<',
597  ],
598  ],
599  ],
600  'nested assignment repeated segment names with whitespaces' => [
601  'test.test.test = 1' . " \t",
602  [
603  'test.' => [
604  'test.' => [
605  'test' => '1',
606  ],
607  ]
608  ],
609  ],
610  'simple assignment operator character as value "=" with whitespaces' => [
611  'test = =TEST=' . " \t",
612  [
613  'test' => '=TEST=',
614  ],
615  ],
616  'nested assignment operator character as value "=" with whitespaces' => [
617  'test.test = =TEST=' . " \t",
618  [
619  'test.' => [
620  'test' => '=TEST=',
621  ],
622  ],
623  ],
624  'simple assignment character as value "<" with whitespaces' => [
625  'test = <TEST>' . " \t",
626  [
627  'test' => '<TEST>',
628  ],
629  ],
630  'nested assignment character as value "<" with whitespaces' => [
631  'test.test = <TEST>' . " \t",
632  [
633  'test.' => [
634  'test' => '<TEST>',
635  ],
636  ],
637  ],
638  'simple assignment character as value ">" with whitespaces' => [
639  'test = >TEST<' . " \t",
640  [
641  'test' => '>TEST<',
642  ],
643  ],
644  'nested assignment character as value ">" with whitespaces' => [
645  'test.test = >TEST<',
646  [
647  'test.' => [
648  'test' => '>TEST<',
649  ],
650  ],
651  ],
652  'CSC example #1' => [
653  'linkParams.ATagParams.dataWrap = class="{$styles.content.imgtext.linkWrap.lightboxCssClass}" rel="{$styles.content.imgtext.linkWrap.lightboxRelAttribute}"',
654  [
655  'linkParams.' => [
656  'ATagParams.' => [
657  'dataWrap' => 'class="{$styles.content.imgtext.linkWrap.lightboxCssClass}" rel="{$styles.content.imgtext.linkWrap.lightboxRelAttribute}"',
658  ],
659  ],
660  ],
661  ],
662  'CSC example #2' => [
663  'linkParams.ATagParams {' . LF .
664  'dataWrap = class="{$styles.content.imgtext.linkWrap.lightboxCssClass}" rel="{$styles.content.imgtext.linkWrap.lightboxRelAttribute}"' . LF .
665  '}',
666  [
667  'linkParams.' => [
668  'ATagParams.' => [
669  'dataWrap' => 'class="{$styles.content.imgtext.linkWrap.lightboxCssClass}" rel="{$styles.content.imgtext.linkWrap.lightboxRelAttribute}"',
670  ],
671  ],
672  ],
673  ],
674  'CSC example #3' => [
675  'linkParams.ATagParams.dataWrap (' . LF .
676  'class="{$styles.content.imgtext.linkWrap.lightboxCssClass}" rel="{$styles.content.imgtext.linkWrap.lightboxRelAttribute}"' . LF .
677  ')',
678  [
679  'linkParams.' => [
680  'ATagParams.' => [
681  'dataWrap' => 'class="{$styles.content.imgtext.linkWrap.lightboxCssClass}" rel="{$styles.content.imgtext.linkWrap.lightboxRelAttribute}"',
682  ],
683  ],
684  ],
685  ],
686  'key with colon' => [
687  'some:key = is valid',
688  [
689  'some:key' => 'is valid'
690  ]
691  ],
692  'special operator' => [
693  'some := addToList(a)',
694  [
695  'some' => 'a'
696  ]
697  ],
698  'special operator with white-spaces' => [
699  'some := addToList (a)',
700  [
701  'some' => 'a'
702  ]
703  ],
704  'special operator with tabs' => [
705  'some := addToList (a)',
706  [
707  'some' => 'a'
708  ]
709  ],
710  'special operator with white-spaces and tabs in value' => [
711  'some := addToList( a, b, c )',
712  [
713  'some' => 'a, b, c'
714  ]
715  ],
716  'special operator and colon, no spaces' => [
717  'some:key:=addToList(a)',
718  [
719  'some:key' => 'a'
720  ]
721  ],
722  'key with all special symbols' => [
723  'someSpecial\\_:-\\.Chars = is valid',
724  [
725  'someSpecial\\_:-.Chars' => 'is valid'
726  ]
727  ],
728  ];
729  }
730 
735  {
736  $string = '';
737  $setup = [];
738  $value = [];
739  $this->typoScriptParser->setVal($string, $setup, $value);
740  }
741 
746  {
747  $string = '';
748  $setup = [];
749  $value = '';
750  $this->typoScriptParser->setVal($string, $setup, $value);
751  }
752 
757  public function parseNextKeySegmentReturnsCorrectNextKeySegment($key, $expectedKeySegment, $expectedRemainingKey)
758  {
759  list($keySegment, $remainingKey) = $this->typoScriptParser->_call('parseNextKeySegment', $key);
760  $this->assertSame($expectedKeySegment, $keySegment);
761  $this->assertSame($expectedRemainingKey, $remainingKey);
762  }
763 
768  {
769  return [
770  'key without separator' => [
771  'testkey',
772  'testkey',
773  ''
774  ],
775  'key with normal separator' => [
776  'test.key',
777  'test',
778  'key'
779  ],
780  'key with multiple normal separators' => [
781  'test.key.subkey',
782  'test',
783  'key.subkey'
784  ],
785  'key with separator and escape character' => [
786  'te\\st.test',
787  'te\\st',
788  'test'
789  ],
790  'key with escaped separators' => [
791  'test\\.key\\.subkey',
792  'test.key.subkey',
793  ''
794  ],
795  'key with escaped and unescaped separator 1' => [
796  'test.test\\.key',
797  'test',
798  'test\\.key'
799  ],
800  'key with escaped and unescaped separator 2' => [
801  'test\\.test.key\\.key2',
802  'test.test',
803  'key\\.key2'
804  ],
805  'key with escaped escape character' => [
806  'test\\\\.key',
807  'test\\',
808  'key'
809  ],
810  'key with escaped separator and additional escape character' => [
811  'test\\\\\\.key',
812  'test\\\\',
813  'key'
814  ],
815 
816  'multiple escape characters within the key are preserved' => [
817  'te\\\\st\\\\.key',
818  'te\\\\st\\',
819  'key'
820  ]
821  ];
822  }
823 }
executeValueModifierReturnsModifiedResult($modifierName, $currentValue, $modifierArgument, $expected)
executeValueModifierThrowsException($modifierName, $currentValue, $modifierArgument)
parseNextKeySegmentReturnsCorrectNextKeySegment($key, $expectedKeySegment, $expectedRemainingKey)