TYPO3 CMS  TYPO3_8-7
DatabaseConnectionTest.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 
20 
24 class DatabaseConnectionTest extends \TYPO3\TestingFramework\Core\Unit\UnitTestCase
25 {
29  protected $subject;
33  protected $testTable = 'test_database_connection';
34 
38  protected $testField = 'test_field';
39 
43  protected $anotherTestField = 'another_test_field';
44 
48  protected function setUp()
49  {
50  parent::setUp();
51  $this->subject = $this->getAccessibleMock(DatabaseConnection::class, ['dummy'], [], '', false);
52  $this->subject->_set('databaseName', 'typo3_test');
53  }
54 
56  // Write/Read tests for charsets and binaries
58 
62  public function storedFullAsciiRangeCallsLinkObjectWithGivenData()
63  {
64  $binaryString = '';
65  for ($i = 0; $i < 256; $i++) {
66  $binaryString .= chr($i);
67  }
68 
70  $subject = $this->getAccessibleMock(DatabaseConnection::class, ['fullQuoteStr'], [], '', false);
71  $subject->_set('isConnected', true);
72  $subject
73  ->expects($this->any())
74  ->method('fullQuoteStr')
75  ->will($this->returnCallback(function ($data) {
76  return $data;
77  }));
78  $mysqliProphecy = $this->prophesize(\mysqli::class);
79  $mysqliProphecy->query("INSERT INTO {$this->testTable} ({$this->testField}) VALUES ({$binaryString})")
80  ->shouldBeCalled();
81  $subject->_set('link', $mysqliProphecy->reveal());
82 
83  $subject->exec_INSERTquery($this->testTable, [$this->testField => $binaryString]);
84  }
85 
90  public function storedGzipCompressedDataReturnsSameData()
91  {
92  $testStringWithBinary = gzcompress('sdfkljer4587');
93 
95  $subject = $this->getAccessibleMock(DatabaseConnection::class, ['fullQuoteStr'], [], '', false);
96  $subject->_set('isConnected', true);
97  $subject
98  ->expects($this->any())
99  ->method('fullQuoteStr')
100  ->will($this->returnCallback(function ($data) {
101  return $data;
102  }));
103  $mysqliProphecy = $this->prophesize(\mysqli::class);
104  $mysqliProphecy->query("INSERT INTO {$this->testTable} ({$this->testField}) VALUES ({$testStringWithBinary})")
105  ->shouldBeCalled();
106  $subject->_set('link', $mysqliProphecy->reveal());
107 
108  $subject->exec_INSERTquery($this->testTable, [$this->testField => $testStringWithBinary]);
109  }
110 
112  // Tests concerning listQuery
114 
119  public function listQueryWithIntegerCommaAsValue()
120  {
122  $subject = $this->getAccessibleMock(DatabaseConnection::class, ['quoteStr'], [], '', false);
123  $subject->_set('isConnected', true);
124  $subject
125  ->expects($this->any())
126  ->method('quoteStr')
127  ->will($this->returnCallback(function ($data) {
128  return $data;
129  }));
130  // Note: 44 = ord(',')
131  $this->assertEquals($subject->listQuery('dummy', 44, 'table'), $subject->listQuery('dummy', '44', 'table'));
132  }
133 
137  public function listQueryThrowsExceptionIfValueContainsComma()
138  {
139  $this->expectException(\InvalidArgumentException::class);
140  $this->expectExceptionCode(1294585862);
141 
143  $subject = $this->getAccessibleMock(DatabaseConnection::class, ['quoteStr'], [], '', false);
144  $subject->_set('isConnected', true);
145  $subject->listQuery('aField', 'foo,bar', 'aTable');
146  }
147 
149  // Tests concerning searchQuery
151 
157  public function searchQueryDataProvider()
158  {
159  return [
160  'One search word in one field' => [
161  '(pages.title LIKE \'%TYPO3%\')',
162  ['TYPO3'],
163  ['title'],
164  'pages',
165  'AND',
166  ],
167  'One search word with special chars (for like)' => [
168  '(pages.title LIKE \'%TYPO3\\_100\\%%\')',
169  ['TYPO3_100%'],
170  ['title'],
171  'pages',
172  'AND',
173  ],
174  'One search word in multiple fields' => [
175  "(pages.title LIKE '%TYPO3%' OR pages.keyword LIKE '%TYPO3%' OR pages.description LIKE '%TYPO3%')",
176  ['TYPO3'],
177  ['title', 'keyword', 'description'],
178  'pages',
179  'AND',
180  ],
181  'Multiple search words in one field with AND constraint' => [
182  "(pages.title LIKE '%TYPO3%') AND (pages.title LIKE '%is%') AND (pages.title LIKE '%great%')",
183  ['TYPO3', 'is', 'great'],
184  ['title'],
185  'pages',
186  'AND',
187  ],
188  'Multiple search words in one field with OR constraint' => [
189  "(pages.title LIKE '%TYPO3%') OR (pages.title LIKE '%is%') OR (pages.title LIKE '%great%')",
190  ['TYPO3', 'is', 'great'],
191  ['title'],
192  'pages',
193  'OR',
194  ],
195  'Multiple search words in multiple fields with AND constraint' => [
196  "(pages.title LIKE '%TYPO3%' OR pages.keywords LIKE '%TYPO3%' OR pages.description LIKE '%TYPO3%') " .
197  "AND (pages.title LIKE '%is%' OR pages.keywords LIKE '%is%' OR pages.description LIKE '%is%') " .
198  "AND (pages.title LIKE '%great%' OR pages.keywords LIKE '%great%' OR pages.description LIKE '%great%')",
199  ['TYPO3', 'is', 'great'],
200  ['title', 'keywords', 'description'],
201  'pages',
202  'AND',
203  ],
204  'Multiple search words in multiple fields with OR constraint' => [
205  "(pages.title LIKE '%TYPO3%' OR pages.keywords LIKE '%TYPO3%' OR pages.description LIKE '%TYPO3%') " .
206  "OR (pages.title LIKE '%is%' OR pages.keywords LIKE '%is%' OR pages.description LIKE '%is%') " .
207  "OR (pages.title LIKE '%great%' OR pages.keywords LIKE '%great%' OR pages.description LIKE '%great%')",
208  ['TYPO3', 'is', 'great'],
209  ['title', 'keywords', 'description'],
210  'pages',
211  'OR',
212  ],
213  ];
214  }
215 
225  public function searchQueryCreatesQuery($expectedResult, array $searchWords, array $fields, $table, $constraint)
226  {
228  $subject = $this->getMockBuilder(DatabaseConnection::class)
229  ->setMethods(['quoteStr'])
230  ->disableOriginalConstructor()
231  ->getMock();
232  $subject
233  ->expects($this->any())
234  ->method('quoteStr')
235  ->will($this->returnCallback(function ($data) {
236  return $data;
237  }));
238 
239  $this->assertSame($expectedResult, $subject->searchQuery($searchWords, $fields, $table, $constraint));
240  }
241 
243  // Tests concerning escapeStringForLikeComparison
245 
249  public function escapeStringForLikeComparison()
250  {
252  $subject = $this->getMockBuilder(DatabaseConnection::class)
253  ->setMethods(['dummy'])
254  ->disableOriginalConstructor()
255  ->getMock();
256  $this->assertEquals('foo\\_bar\\%', $subject->escapeStrForLike('foo_bar%', 'table'));
257  }
258 
260  // Tests concerning stripOrderByForOrderByKeyword
262 
270  {
271  return [
272  'single ORDER BY' => [
273  'ORDER BY name, tstamp',
274  'name, tstamp'
275  ],
276  'single ORDER BY in lower case' => [
277  'order by name, tstamp',
278  'name, tstamp'
279  ],
280  'ORDER BY with additional space behind' => [
281  'ORDER BY name, tstamp',
282  'name, tstamp'
283  ],
284  'ORDER BY without space between the words' => [
285  'ORDERBY name, tstamp',
286  'name, tstamp'
287  ],
288  'ORDER BY added twice' => [
289  'ORDER BY ORDER BY name, tstamp',
290  'name, tstamp'
291  ],
292  'ORDER BY added twice without spaces in the first occurrence' => [
293  'ORDERBY ORDER BY name, tstamp',
294  'name, tstamp',
295  ],
296  'ORDER BY added twice without spaces in the second occurrence' => [
297  'ORDER BYORDERBY name, tstamp',
298  'name, tstamp',
299  ],
300  'ORDER BY added twice without spaces' => [
301  'ORDERBYORDERBY name, tstamp',
302  'name, tstamp'
303  ],
304  'ORDER BY added twice without spaces afterwards' => [
305  'ORDERBYORDERBYname, tstamp',
306  'name, tstamp'
307  ],
308  ];
309  }
310 
317  public function stripOrderByForOrderByKeyword($orderByClause, $expectedResult)
318  {
320  $subject = $this->getMockBuilder(DatabaseConnection::class)
321  ->setMethods(['dummy'])
322  ->disableOriginalConstructor()
323  ->getMock();
324  $strippedQuery = $subject->stripOrderBy($orderByClause);
325  $this->assertEquals($expectedResult, $strippedQuery);
326  }
327 
329  // Tests concerning stripGroupByForGroupByKeyword
331 
339  {
340  return [
341  'single GROUP BY' => [
342  'GROUP BY name, tstamp',
343  'name, tstamp'
344  ],
345  'single GROUP BY in lower case' => [
346  'group by name, tstamp',
347  'name, tstamp'
348  ],
349  'GROUP BY with additional space behind' => [
350  'GROUP BY name, tstamp',
351  'name, tstamp'
352  ],
353  'GROUP BY without space between the words' => [
354  'GROUPBY name, tstamp',
355  'name, tstamp'
356  ],
357  'GROUP BY added twice' => [
358  'GROUP BY GROUP BY name, tstamp',
359  'name, tstamp'
360  ],
361  'GROUP BY added twice without spaces in the first occurrence' => [
362  'GROUPBY GROUP BY name, tstamp',
363  'name, tstamp',
364  ],
365  'GROUP BY added twice without spaces in the second occurrence' => [
366  'GROUP BYGROUPBY name, tstamp',
367  'name, tstamp',
368  ],
369  'GROUP BY added twice without spaces' => [
370  'GROUPBYGROUPBY name, tstamp',
371  'name, tstamp'
372  ],
373  'GROUP BY added twice without spaces afterwards' => [
374  'GROUPBYGROUPBYname, tstamp',
375  'name, tstamp'
376  ],
377  ];
378  }
379 
386  public function stripGroupByForGroupByKeyword($groupByClause, $expectedResult)
387  {
389  $subject = $this->getMockBuilder(DatabaseConnection::class)
390  ->setMethods(['dummy'])
391  ->disableOriginalConstructor()
392  ->getMock();
393  $strippedQuery = $subject->stripGroupBy($groupByClause);
394  $this->assertEquals($expectedResult, $strippedQuery);
395  }
396 
398  // Tests concerning stripOrderByForOrderByKeyword
400 
407  public function cleanIntArrayDataProvider()
408  {
409  return [
410  'simple array' => [
411  [1, 2, 3],
412  [1, 2, 3],
413  ],
414  'string array' => [
415  ['2', '4', '8'],
416  [2, 4, 8],
417  ],
418  'string array with letters #1' => [
419  ['3', '6letters', '12'],
420  [3, 6, 12],
421  ],
422  'string array with letters #2' => [
423  ['3', 'letters6', '12'],
424  [3, 0, 12],
425  ],
426  'string array with letters #3' => [
427  ['3', '6letters4', '12'],
428  [3, 6, 12],
429  ],
430  'associative array' => [
431  ['apples' => 3, 'bananas' => 4, 'kiwis' => 9],
432  ['apples' => 3, 'bananas' => 4, 'kiwis' => 9],
433  ],
434  'associative string array' => [
435  ['apples' => '1', 'bananas' => '5', 'kiwis' => '7'],
436  ['apples' => 1, 'bananas' => 5, 'kiwis' => 7],
437  ],
438  'associative string array with letters #1' => [
439  ['apples' => '1', 'bananas' => 'no5', 'kiwis' => '7'],
440  ['apples' => 1, 'bananas' => 0, 'kiwis' => 7],
441  ],
442  'associative string array with letters #2' => [
443  ['apples' => '1', 'bananas' => '5yes', 'kiwis' => '7'],
444  ['apples' => 1, 'bananas' => 5, 'kiwis' => 7],
445  ],
446  'associative string array with letters #3' => [
447  ['apples' => '1', 'bananas' => '5yes9', 'kiwis' => '7'],
448  ['apples' => 1, 'bananas' => 5, 'kiwis' => 7],
449  ],
450  'multidimensional associative array' => [
451  ['apples' => '1', 'bananas' => [3, 4], 'kiwis' => '7'],
452  // intval(array(...)) is 1
453  // But by specification "cleanIntArray" should only get used on one-dimensional arrays
454  ['apples' => 1, 'bananas' => 1, 'kiwis' => 7],
455  ],
456  ];
457  }
458 
465  public function cleanIntArray($exampleData, $expectedResult)
466  {
467  $sanitizedArray = $this->subject->cleanIntArray($exampleData);
468  $this->assertEquals($expectedResult, $sanitizedArray);
469  }
470 
475  {
476  $str = '234,-434,4.3,0, 1';
477  $result = $this->subject->cleanIntList($str);
478  $this->assertSame('234,-434,4,0,1', $result);
479  }
480 
484  public function sqlForSelectMmQuery()
485  {
486  $result = $this->subject->SELECT_mm_query(
487  '*',
488  'sys_category',
489  'sys_category_record_mm',
490  'tt_content',
491  'AND sys_category.uid = 1',
492  '',
493  'sys_category.title DESC'
494  );
495  $expected = 'SELECT * FROM sys_category,sys_category_record_mm,tt_content ' .
496  'WHERE sys_category.uid=sys_category_record_mm.uid_local ' .
497  'AND tt_content.uid=sys_category_record_mm.uid_foreign ' .
498  'AND sys_category.uid = 1 ORDER BY sys_category.title DESC';
499  $this->assertEquals($expected, $result);
500  }
501 
508  {
509  return [
510  'noQuote boolean false' => [
511  ['aField' => 'aValue', 'anotherField' => 'anotherValue'],
512  ['aField' => "'aValue'", 'anotherField' => "'anotherValue'"],
513  false,
514  ],
515  'noQuote boolean true' => [
516  ['aField' => 'aValue', 'anotherField' => 'anotherValue'],
517  ['aField' => 'aValue', 'anotherField' => 'anotherValue'],
518  true,
519  ],
520  'noQuote list of fields' => [
521  ['aField' => 'aValue', 'anotherField' => 'anotherValue'],
522  ['aField' => "'aValue'", 'anotherField' => 'anotherValue'],
523  'anotherField',
524  ],
525  'noQuote array of fields' => [
526  ['aField' => 'aValue', 'anotherField' => 'anotherValue'],
527  ['aField' => 'aValue', 'anotherField' => "'anotherValue'"],
528  ['aField'],
529  ],
530  ];
531  }
532 
540  public function noQuoteForFullQuoteArray(array $input, array $expected, $noQuote)
541  {
543  $subject = $this->getMockBuilder(DatabaseConnection::class)
544  ->setMethods(['fullQuoteStr'])
545  ->disableOriginalConstructor()
546  ->getMock();
547 
548  $subject
549  ->expects($this->any())
550  ->method('fullQuoteStr')
551  ->will($this->returnCallback(function ($data) {
552  return '\'' . (string)$data . '\'';
553  }));
554  $this->assertSame($expected, $subject->fullQuoteArray($input, 'aTable', $noQuote));
555  }
556 
560  public function sqlSelectDbReturnsTrue()
561  {
563  $subject = $this->getAccessibleMock(DatabaseConnection::class, ['dummy'], [], '', false);
564  $subject->_set('isConnected', true);
565  $subject->_set('databaseName', $this->testTable);
566 
567  $mysqliProphecy = $this->prophesize(\mysqli::class);
568  $mysqliProphecy->select_db($this->testTable)->shouldBeCalled()->willReturn(true);
569  $subject->_set('link', $mysqliProphecy->reveal());
570 
571  $this->assertTrue($subject->sql_select_db());
572  }
573 
577  public function sqlSelectDbReturnsFalse()
578  {
579  $GLOBALS['TYPO3_CONF_VARS']['SYS']['systemLogLevel'] = GeneralUtility::SYSLOG_SEVERITY_WARNING;
580 
582  $subject = $this->getAccessibleMock(DatabaseConnection::class, ['sql_error'], [], '', false);
583  $subject->_set('isConnected', true);
584  $subject->_set('databaseName', $this->testTable);
585  $subject->expects($this->any())->method('sql_error')->will($this->returnValue(''));
586 
587  $mysqliProphecy = $this->prophesize(\mysqli::class);
588  $mysqliProphecy->select_db($this->testTable)->shouldBeCalled()->willReturn(false);
589  $subject->_set('link', $mysqliProphecy->reveal());
590 
591  $this->assertFalse($subject->sql_select_db());
592  }
593 
597  public function insertQueryCreateValidQuery()
598  {
599  $this->subject = $this->getAccessibleMock(DatabaseConnection::class, ['fullQuoteStr'], [], '', false);
600  $this->subject->expects($this->any())
601  ->method('fullQuoteStr')
602  ->will($this->returnCallback(function ($data) {
603  return '\'' . (string)$data . '\'';
604  }));
605 
606  $fieldValues = [$this->testField => 'Foo'];
607  $queryExpected = "INSERT INTO {$this->testTable} ({$this->testField}) VALUES ('Foo')";
608  $queryGenerated = $this->subject->INSERTquery($this->testTable, $fieldValues);
609  $this->assertSame($queryExpected, $queryGenerated);
610  }
611 
616  {
617  $this->subject = $this->getAccessibleMock(DatabaseConnection::class, ['fullQuoteStr'], [], '', false);
618  $this->subject->expects($this->any())
619  ->method('fullQuoteStr')
620  ->will($this->returnCallback(function ($data) {
621  return '\'' . (string)$data . '\'';
622  }));
623  $fieldValues = [
624  $this->testField => 'Foo',
625  $this->anotherTestField => 'Bar',
626  ];
627  $queryExpected = "INSERT INTO {$this->testTable} ({$this->testField},{$this->anotherTestField}) " .
628  "VALUES ('Foo','Bar')";
629  $queryGenerated = $this->subject->INSERTquery($this->testTable, $fieldValues);
630  $this->assertSame($queryExpected, $queryGenerated);
631  }
632 
637  {
638  $this->subject = $this->getAccessibleMock(DatabaseConnection::class, ['fullQuoteStr'], [], '', false);
639  $this->subject->expects($this->any())
640  ->method('fullQuoteStr')
641  ->will($this->returnCallback(function ($data) {
642  return '\'' . (string)$data . '\'';
643  }));
645  $values = [
646  ['Foo', 100],
647  ['Bar', 200],
648  ['Baz', 300],
649  ];
650  $queryExpected = "INSERT INTO {$this->testTable} ({$this->testField}, {$this->anotherTestField}) " .
651  "VALUES ('Foo', '100'), ('Bar', '200'), ('Baz', '300')";
652  $queryGenerated = $this->subject->INSERTmultipleRows($this->testTable, $fields, $values);
653  $this->assertSame($queryExpected, $queryGenerated);
654  }
655 
659  public function updateQueryCreateValidQuery()
660  {
661  $this->subject = $this->getAccessibleMock(DatabaseConnection::class, ['fullQuoteStr'], [], '', false);
662  $this->subject->expects($this->any())
663  ->method('fullQuoteStr')
664  ->will($this->returnCallback(function ($data) {
665  return '\'' . (string)$data . '\'';
666  }));
667 
668  $fieldsValues = [$this->testField => 'aTestValue'];
669  $queryExpected = "UPDATE {$this->testTable} SET {$this->testField}='aTestValue' WHERE id=1";
670  $queryGenerated = $this->subject->UPDATEquery($this->testTable, 'id=1', $fieldsValues);
671  $this->assertSame($queryExpected, $queryGenerated);
672  }
673 
677  public function deleteQueryCreateValidQuery()
678  {
679  $queryExpected = "DELETE FROM {$this->testTable} WHERE id=1";
680  $queryGenerated = $this->subject->DELETEquery($this->testTable, 'id=1');
681  $this->assertSame($queryExpected, $queryGenerated);
682  }
683 
687  public function selectQueryCreateValidQuery()
688  {
689  $queryExpected = "SELECT {$this->testField} FROM {$this->testTable} WHERE id=1";
690  $queryGenerated = $this->subject->SELECTquery($this->testField, $this->testTable, 'id=1');
691  $this->assertSame($queryExpected, $queryGenerated);
692  }
693 
698  {
699  $queryExpected = "SELECT {$this->testField} FROM {$this->testTable}";
700  $queryGenerated = $this->subject->SELECTquery($this->testField, $this->testTable, '');
701  $this->assertSame($queryExpected, $queryGenerated);
702  }
703 
708  {
709  $queryExpected = "SELECT {$this->testField} FROM {$this->testTable} WHERE id=1 GROUP BY id";
710  $queryGenerated = $this->subject->SELECTquery($this->testField, $this->testTable, 'id=1', 'id');
711  $this->assertSame($queryExpected, $queryGenerated);
712  }
713 
718  {
719  $queryExpected = "SELECT {$this->testField} FROM {$this->testTable} WHERE id=1 ORDER BY id";
720  $queryGenerated = $this->subject->SELECTquery($this->testField, $this->testTable, 'id=1', '', 'id');
721  $this->assertSame($queryExpected, $queryGenerated);
722  }
723 
728  {
729  $queryGenerated = $this->subject->SELECTquery($this->testField, $this->testTable, 'id=1', '', '', '1,2');
730  $queryExpected = "SELECT {$this->testField} FROM {$this->testTable} WHERE id=1 LIMIT 1,2";
731  $this->assertSame($queryExpected, $queryGenerated);
732  }
733 
738  {
739  $queryExpected = "SELECT {$this->testField} FROM {$this->testTable} WHERE id=1";
740  $queryGenerated = $this->subject->SELECTsubquery($this->testField, $this->testTable, 'id=1');
741  $this->assertSame($queryExpected, $queryGenerated);
742  }
743 
748  {
749  $queryExpected = "TRUNCATE TABLE {$this->testTable}";
750  $queryGenerated = $this->subject->TRUNCATEquery($this->testTable);
751  $this->assertSame($queryExpected, $queryGenerated);
752  }
753 }
$fields
Definition: pages.php:4
if(TYPO3_MODE==='BE') $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tsfebeuserauth.php']['frontendEditingController']['default']