9a843a9da63b50f8a369e125218289718b064626
[Packages/TYPO3.CMS.git] / typo3 / sysext / core / Tests / Unit / Database / PreparedStatementTest.php
1 <?php
2 namespace TYPO3\CMS\Core\Tests\Unit\Database;
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
17 use TYPO3\CMS\Core\Database\DatabaseConnection;
18 use TYPO3\CMS\Core\Database\PreparedStatement;
19
20 /**
21 * Test case
22 */
23 class PreparedStatementTest extends \TYPO3\Components\TestingFramework\Core\UnitTestCase
24 {
25 /**
26 * @var \PHPUnit_Framework_MockObject_MockObject|DatabaseConnection
27 */
28 protected $databaseStub;
29
30 /**
31 * Create a new database mock object for every test
32 * and backup the original global database object.
33 *
34 * @return void
35 */
36 protected function setUp()
37 {
38 $this->databaseStub = $this->setUpAndReturnDatabaseStub();
39 }
40
41 //////////////////////
42 // Utility functions
43 //////////////////////
44 /**
45 * Set up the stub to be able to get the result of the prepared statement.
46 *
47 * @return \PHPUnit_Framework_MockObject_MockObject
48 */
49 private function setUpAndReturnDatabaseStub()
50 {
51 $GLOBALS['TYPO3_DB'] = $this->getAccessibleMock(
52 DatabaseConnection::class,
53 ['prepare_PREPAREDquery'],
54 [],
55 '',
56 false,
57 false
58 );
59
60 return $GLOBALS['TYPO3_DB'];
61 }
62
63 /**
64 * Create an object fo the subject to be tested.
65 *
66 * @param string $query
67 * @return PreparedStatement
68 */
69 private function createPreparedStatement($query)
70 {
71 return new PreparedStatement($query, 'pages');
72 }
73
74 ////////////////////////////////////
75 // Tests for the utility functions
76 ////////////////////////////////////
77
78 /**
79 * @test
80 * @return void
81 */
82 public function setUpAndReturnDatabaseStubReturnsMockObjectOfDatabaseConnection()
83 {
84 $this->assertTrue($this->setUpAndReturnDatabaseStub() instanceof DatabaseConnection);
85 }
86
87 /**
88 * @test
89 * @return void
90 */
91 public function createPreparedStatementReturnsInstanceOfPreparedStatementClass()
92 {
93 $this->assertTrue($this->createPreparedStatement('dummy') instanceof PreparedStatement);
94 }
95
96 ///////////////////////////////////////
97 // Tests for \TYPO3\CMS\Core\Database\PreparedStatement
98 ///////////////////////////////////////
99 /**
100 * Data Provider for two tests, providing sample queries, parameters and expected result queries.
101 *
102 * @see parametersAreReplacedInQueryByCallingExecute
103 * @see parametersAreReplacedInQueryWhenBoundWithBindValues
104 * @return array
105 */
106 public function parametersAndQueriesDataProvider()
107 {
108 return [
109 'one named integer parameter' => [
110 'SELECT * FROM pages WHERE pid=:pid',
111 [':pid' => 1],
112 'SELECT * FROM pages WHERE pid=?'
113 ],
114 'one unnamed integer parameter' => [
115 'SELECT * FROM pages WHERE pid=?',
116 [1],
117 'SELECT * FROM pages WHERE pid=?'
118 ],
119 'one named integer parameter is replaced multiple times' => [
120 'SELECT * FROM pages WHERE pid=:pid OR uid=:pid',
121 [':pid' => 1],
122 'SELECT * FROM pages WHERE pid=? OR uid=?'
123 ],
124 'two named integer parameters are replaced' => [
125 'SELECT * FROM pages WHERE pid=:pid OR uid=:uid',
126 [':pid' => 1, ':uid' => 10],
127 'SELECT * FROM pages WHERE pid=? OR uid=?'
128 ],
129 'two unnamed integer parameters are replaced' => [
130 'SELECT * FROM pages WHERE pid=? OR uid=?',
131 [1, 1],
132 'SELECT * FROM pages WHERE pid=? OR uid=?'
133 ],
134 ];
135 }
136
137 /**
138 * Checking if calling execute() with parameters, they are
139 * properly replaced in the query.
140 *
141 * @test
142 * @dataProvider parametersAndQueriesDataProvider
143 * @param string $query Query with unreplaced markers
144 * @param array $parameters Array of parameters to be replaced in the query
145 * @param string $expectedResult Query with all markers replaced
146 * @return void
147 */
148 public function parametersAreReplacedByQuestionMarkInQueryByCallingExecute($query, $parameters, $expectedResult)
149 {
150 $statement = $this->createPreparedStatement($query);
151 $this->databaseStub->expects($this->any())
152 ->method('prepare_PREPAREDquery')
153 ->with($this->equalTo($expectedResult));
154 $statement->execute($parameters);
155 }
156
157 /**
158 * Checking if parameters bound to the statement by bindValues()
159 * are properly replaced in the query.
160 *
161 * @test
162 * @dataProvider parametersAndQueriesDataProvider
163 * @param string $query Query with unreplaced markers
164 * @param array $parameters Array of parameters to be replaced in the query
165 * @param string $expectedResult Query with all markers replaced
166 * @return void
167 */
168 public function parametersAreReplacedInQueryWhenBoundWithBindValues($query, $parameters, $expectedResult)
169 {
170 $statement = $this->createPreparedStatement($query);
171 $this->databaseStub->expects($this->any())
172 ->method('prepare_PREPAREDquery')
173 ->with($this->equalTo($expectedResult));
174 $statement->bindValues($parameters);
175 $statement->execute();
176 }
177
178 /**
179 * Data Provider with invalid parameters.
180 *
181 * @see invalidParameterTypesPassedToBindValueThrowsException
182 * @return array
183 */
184 public function invalidParameterTypesPassedToBindValueThrowsExceptionDataProvider()
185 {
186 return [
187 'integer passed with param type NULL' => [
188 1,
189 PreparedStatement::PARAM_NULL,
190 1282489834
191 ],
192 'string passed with param type NULL' => [
193 '1',
194 PreparedStatement::PARAM_NULL,
195 1282489834
196 ],
197 'bool passed with param type NULL' => [
198 true,
199 PreparedStatement::PARAM_NULL,
200 1282489834
201 ],
202 'NULL passed with param type INT' => [
203 null,
204 PreparedStatement::PARAM_INT,
205 1281868686
206 ],
207 'string passed with param type INT' => [
208 '1',
209 PreparedStatement::PARAM_INT,
210 1281868686
211 ],
212 'bool passed with param type INT' => [
213 true,
214 PreparedStatement::PARAM_INT,
215 1281868686
216 ],
217 'NULL passed with param type BOOL' => [
218 null,
219 PreparedStatement::PARAM_BOOL,
220 1281868687
221 ],
222 'string passed with param type BOOL' => [
223 '1',
224 PreparedStatement::PARAM_BOOL,
225 1281868687
226 ],
227 'integer passed with param type BOOL' => [
228 1,
229 PreparedStatement::PARAM_BOOL,
230 1281868687
231 ]
232 ];
233 }
234
235 /**
236 * Checking if an exception is thrown if invalid parameters are
237 * provided vor bindValue().
238 *
239 * @test
240 * @dataProvider invalidParameterTypesPassedToBindValueThrowsExceptionDataProvider
241 * @param mixed $parameter Parameter to be replaced in the query
242 * @param int $type Type of the parameter value
243 * @param int $exceptionCode Expected exception code
244 * @return void
245 */
246 public function invalidParameterTypesPassedToBindValueThrowsException($parameter, $type, $exceptionCode)
247 {
248 $this->expectException(\InvalidArgumentException::class);
249 $this->expectExceptionCode($exceptionCode);
250
251 $statement = $this->createPreparedStatement('');
252 $statement->bindValue(1, $parameter, $type);
253 }
254
255 /**
256 * Data Provider for invalid marker names.
257 *
258 * @see passingInvalidMarkersThrowsExeption
259 * @return array
260 */
261 public function passingInvalidMarkersThrowsExceptionDataProvider()
262 {
263 return [
264 'using other prefix than colon' => [
265 /** @lang text */
266 'SELECT * FROM pages WHERE pid=#pid',
267 ['#pid' => 1]
268 ],
269 'using non alphanumerical character' => [
270 /** @lang text */
271 'SELECT * FROM pages WHERE title=:stra≠e',
272 [':stra≠e' => 1]
273 ],
274 'no colon used' => [
275 /** @lang text */
276 'SELECT * FROM pages WHERE pid=pid',
277 ['pid' => 1]
278 ],
279 'colon at the end' => [
280 /** @lang text */
281 'SELECT * FROM pages WHERE pid=pid:',
282 ['pid:' => 1]
283 ],
284 'colon without alphanumerical character' => [
285 /** @lang text */
286 'SELECT * FROM pages WHERE pid=:',
287 [':' => 1]
288 ]
289 ];
290 }
291
292 /**
293 * Checks if an exception is thrown, if parameter have invalid marker named.
294 *
295 * @test
296 * @dataProvider passingInvalidMarkersThrowsExceptionDataProvider
297 * @param string $query Query with unreplaced markers
298 * @param array $parameters Array of parameters to be replaced in the query
299 * @return void
300 */
301 public function passingInvalidMarkersThrowsException($query, $parameters)
302 {
303 $this->expectException(\InvalidArgumentException::class);
304 $this->expectExceptionCode(1395055513);
305
306 $statement = $this->createPreparedStatement($query);
307 $statement->bindValues($parameters);
308 }
309 }