TYPO3 CMS  TYPO3_8-7
QueryHelperTest.php
Go to the documentation of this file.
1 <?php
2 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 
21 
25 class QueryHelperTest extends \TYPO3\TestingFramework\Core\Unit\UnitTestCase
26 {
32  public function stripLogicalOperatorPrefixDataProvider(): array
33  {
34  return [
35  'unprefixed input' => ['1=1', '1=1'],
36  'leading/trailing whitespace is removed' => [' 1=1 ', '1=1'],
37  'AND' => ['AND 1=1', '1=1'],
38  'AND with leading space' => [' AND 1=1', '1=1'],
39  'AND with mixed whitespace' => [' AND 1<>1', '1<>1'],
40  'AND with opening bracket' => ['AND (1=1)', '(1=1)'],
41  'AND without whitespace before bracket' => ['AND(1=1)', '(1=1)'],
42  'AND within input' => ['1=1 AND 2=2', '1=1 AND 2=2'],
43  'OR' => ['OR 1=1', '1=1'],
44  'OR with leading space' => [' OR 1=1', '1=1'],
45  'OR with mixed whitespace' => [' OR 1<>1', '1<>1'],
46  'OR with opening bracket' => ['OR (1=1)', '(1=1)'],
47  'OR without whitespace before bracket' => ['OR(1=1)', '(1=1)'],
48  'OR within input' => ['1=1 OR 2=2', '1=1 OR 2=2'],
49  ];
50  }
51 
58  public function stripLogicalOperatorPrefixRemovesConstraintPrefixes(string $input, string $expectedSql)
59  {
60  $this->assertSame($expectedSql, QueryHelper::stripLogicalOperatorPrefix($input));
61  }
62 
68  public function parseOrderByDataProvider(): array
69  {
70  return [
71  'empty string' => [
72  '',
73  [],
74  ],
75  'single field' => [
76  'aField',
77  [
78  ['aField', null],
79  ],
80  ],
81  'single field with leading whitespace' => [
82  ' aField',
83  [
84  ['aField', null],
85  ],
86  ],
87  'prefixed single field' => [
88  'ORDER BY aField',
89  [
90  ['aField', null],
91  ],
92  ],
93  'prefixed single field with leading whitespace' => [
94  ' ORDER BY aField',
95  [
96  ['aField', null],
97  ],
98  ],
99  'single field with direction' => [
100  'aField DESC',
101  [
102  ['aField', 'DESC'],
103  ],
104  ],
105  'multiple fields' => [
106  'aField,anotherField, aThirdField',
107  [
108  ['aField', null],
109  ['anotherField', null],
110  ['aThirdField', null]
111  ],
112  ],
113  'multiple fields with direction' => [
114  'aField ASC,anotherField, aThirdField DESC',
115  [
116  ['aField', 'ASC'],
117  ['anotherField', null],
118  ['aThirdField', 'DESC']
119  ],
120  ],
121  'prefixed multiple fields with direction' => [
122  'ORDER BY aField ASC,anotherField, aThirdField DESC',
123  [
124  ['aField', 'ASC'],
125  ['anotherField', null],
126  ['aThirdField', 'DESC']
127  ],
128  ],
129  'with table prefix' => [
130  'ORDER BY be_groups.title',
131  [
132  ['be_groups.title', null]
133  ]
134  ],
135  ];
136  }
137 
144  public function parseOrderByTest(string $input, array $expectedResult)
145  {
146  $this->assertSame($expectedResult, QueryHelper::parseOrderBy($input));
147  }
148 
154  public function parseTableListDataProvider(): array
155  {
156  return [
157  'single table' => [
158  'aTable',
159  [
160  ['aTable', null],
161  ],
162  ],
163  'single table with leading whitespace' => [
164  ' aTable',
165  [
166  ['aTable', null],
167  ],
168  ],
169  'prefixed single table' => [
170  'FROM aTable',
171  [
172  ['aTable', null],
173  ],
174  ],
175  'prefixed single table with leading whitespace' => [
176  ' FROM aTable',
177  [
178  ['aTable', null],
179  ],
180  ],
181  'single table with alias' => [
182  'aTable a',
183  [
184  ['aTable', 'a'],
185  ],
186  ],
187  'multiple tables' => [
188  'aTable,anotherTable, aThirdTable',
189  [
190  ['aTable', null],
191  ['anotherTable', null],
192  ['aThirdTable', null]
193  ],
194  ],
195  'multiple tables with aliases' => [
196  'aTable a,anotherTable, aThirdTable AS c',
197  [
198  ['aTable', 'a'],
199  ['anotherTable', null],
200  ['aThirdTable', 'c']
201  ],
202  ],
203  'prefixed multiple tables with aliases' => [
204  'FROM aTable a,anotherTable, aThirdTable AS c',
205  [
206  ['aTable', 'a'],
207  ['anotherTable', null],
208  ['aThirdTable', 'c']
209  ],
210  ]
211  ];
212  }
213 
220  public function parseTableListTest(string $input, array $expectedResult)
221  {
222  $this->assertSame($expectedResult, QueryHelper::parseTableList($input));
223  }
224 
230  public function parseGroupByDataProvider(): array
231  {
232  return [
233  'single field' => [
234  'aField',
235  ['aField'],
236  ],
237  'single field with leading whitespace' => [
238  ' aField',
239  ['aField'],
240  ],
241  'prefixed single field' => [
242  'GROUP BY aField',
243  ['aField'],
244  ],
245  'prefixed single field with leading whitespace' => [
246  ' GROUP BY aField',
247  ['aField'],
248  ],
249  'multiple fields' => [
250  'aField,anotherField, aThirdField',
251  ['aField', 'anotherField', 'aThirdField']
252  ],
253  'prefixed multiple fields' => [
254  'GROUP BY aField,anotherField, aThirdField',
255  ['aField', 'anotherField', 'aThirdField']
256  ],
257  'with table prefix' => [
258  'GROUP BY be_groups.title',
259  ['be_groups.title']
260  ],
261  ];
262  }
263 
270  public function parseGroupByTest(string $input, array $expectedResult)
271  {
272  $this->assertSame($expectedResult, QueryHelper::parseGroupBy($input));
273  }
274 
280  public function parseJoinDataProvider(): array
281  {
282  return [
283  'unquoted tableName' => [
284  'aTable ON aTable.uid = anotherTable.uid_foreign',
285  [
286  'tableName' => 'aTable',
287  'tableAlias' => 'aTable',
288  'joinCondition' => 'aTable.uid = anotherTable.uid_foreign'
289  ],
290  ],
291  'quoted tableName' => [
292  '`aTable` ON aTable.uid = anotherTable.uid_foreign',
293  [
294  'tableName' => 'aTable',
295  'tableAlias' => 'aTable',
296  'joinCondition' => 'aTable.uid = anotherTable.uid_foreign'
297  ],
298  ],
299  'quoted tableName with alias' => [
300  '`aTable` a ON a.uid = anotherTable.uid_foreign',
301  [
302  'tableName' => 'aTable',
303  'tableAlias' => 'a',
304  'joinCondition' => 'a.uid = anotherTable.uid_foreign'
305  ],
306  ],
307  'quoted tableName with quoted alias' => [
308  '`aTable` `a` ON a.uid = anotherTable.uid_foreign',
309  [
310  'tableName' => 'aTable',
311  'tableAlias' => 'a',
312  'joinCondition' => 'a.uid = anotherTable.uid_foreign'
313  ],
314  ],
315  'quoted tableName with AS alias' => [
316  '`aTable` AS anAlias ON anAlias.uid = anotherTable.uid_foreign',
317  [
318  'tableName' => 'aTable',
319  'tableAlias' => 'anAlias',
320  'joinCondition' => 'anAlias.uid = anotherTable.uid_foreign'
321  ],
322  ],
323  'quoted tableName with AS quoted alias' => [
324  '`aTable` AS `anAlias` ON anAlias.uid = anotherTable.uid_foreign',
325  [
326  'tableName' => 'aTable',
327  'tableAlias' => 'anAlias',
328  'joinCondition' => 'anAlias.uid = anotherTable.uid_foreign'
329  ],
330  ],
331  'unquoted tableName with AS quoted alias' => [
332  'aTable AS `anAlias` ON anAlias.uid = anotherTable.uid_foreign',
333  [
334  'tableName' => 'aTable',
335  'tableAlias' => 'anAlias',
336  'joinCondition' => 'anAlias.uid = anotherTable.uid_foreign'
337  ],
338  ],
339  ];
340  }
341 
348  public function parseJoinSplitsStatement(string $input, array $expected)
349  {
350  $this->assertSame($expected, QueryHelper::parseJoin($input));
351  }
352 
358  public function quoteDatabaseIdentifierDataProvider(): array
359  {
360  return [
361  'no marked identifiers' => [
362  'colPos=0',
363  'colPos=0',
364  ],
365  'single fieldname' => [
366  '{#colPos}=0',
367  '"colPos"=0',
368  ],
369  'tablename and fieldname' => [
370  '{#tt_content.colPos}=0',
371  '"tt_content"."colPos"=0',
372  ],
373  'multiple fieldnames' => [
374  '{#colPos}={#aField}',
375  '"colPos"="aField"',
376  ],
377  ];
378  }
379 
386  public function quoteDatabaseIdentifiers(string $input, string $expected)
387  {
388  $connectionProphet = $this->prophesize(Connection::class);
389  $connectionProphet->quoteIdentifier(Argument::cetera())->will(function ($args) {
390  $parts = array_map(
391  function ($identifier) {
392  return '"' . $identifier . '"';
393  },
394  explode('.', $args[0])
395  );
396 
397  return implode('.', $parts);
398  });
399 
400  $this->assertSame($expected, QueryHelper::quoteDatabaseIdentifiers($connectionProphet->reveal(), $input));
401  }
402 }
parseGroupByTest(string $input, array $expectedResult)
parseOrderByTest(string $input, array $expectedResult)
stripLogicalOperatorPrefixRemovesConstraintPrefixes(string $input, string $expectedSql)
parseTableListTest(string $input, array $expectedResult)
static stripLogicalOperatorPrefix(string $constraint)
static quoteDatabaseIdentifiers(Connection $connection, string $sql)