Revert "[BUGFIX] Database API: Cast bool values to integer for MySQL"
[Packages/TYPO3.CMS.git] / typo3 / sysext / core / Classes / Database / DatabaseConnection.php
1 <?php
2 namespace TYPO3\CMS\Core\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\Utility\GeneralUtility;
18
19 /**
20 * Contains the class "DatabaseConnection" containing functions for building SQL queries
21 * and mysqli wrappers, thus providing a foundational API to all database
22 * interaction.
23 * This class is instantiated globally as $TYPO3_DB in TYPO3 scripts.
24 *
25 * TYPO3 "database wrapper" class (new in 3.6.0)
26 * This class contains
27 * - abstraction functions for executing INSERT/UPDATE/DELETE/SELECT queries ("Query execution"; These are REQUIRED for all future connectivity to the database, thus ensuring DBAL compliance!)
28 * - functions for building SQL queries (INSERT/UPDATE/DELETE/SELECT) ("Query building"); These are transitional functions for building SQL queries in a more automated way. Use these to build queries instead of doing it manually in your code!
29 * - mysqli wrapper functions; These are transitional functions. By a simple search/replace you should be able to substitute all mysql*() calls with $GLOBALS['TYPO3_DB']->sql*() and your application will work out of the box. YOU CANNOT (legally) use any mysqli functions not found as wrapper functions in this class!
30 * See the Project Coding Guidelines (doc_core_cgl) for more instructions on best-practise
31 *
32 * This class is not in itself a complete database abstraction layer but can be extended to be a DBAL (by extensions, see "dbal" for example)
33 * ALL connectivity to the database in TYPO3 must be done through this class!
34 * The points of this class are:
35 * - To direct all database calls through this class so it becomes possible to implement DBAL with extensions.
36 * - To keep it very easy to use for developers used to MySQL in PHP - and preserve as much performance as possible when TYPO3 is used with MySQL directly...
37 * - To create an interface for DBAL implemented by extensions; (Eg. making possible escaping characters, clob/blob handling, reserved words handling)
38 * - Benchmarking the DB bottleneck queries will become much easier; Will make it easier to find optimization possibilities.
39 *
40 * USE:
41 * In all TYPO3 scripts the global variable $TYPO3_DB is an instance of this class. Use that.
42 * Eg. $GLOBALS['TYPO3_DB']->sql_fetch_assoc()
43 */
44 class DatabaseConnection {
45
46 /**
47 * The AND constraint in where clause
48 *
49 * @var string
50 */
51 const AND_Constraint = 'AND';
52
53 /**
54 * The OR constraint in where clause
55 *
56 * @var string
57 */
58 const OR_Constraint = 'OR';
59
60 /**
61 * Set "TRUE" or "1" if you want database errors outputted. Set to "2" if you also want successful database actions outputted.
62 *
63 * @var bool|int
64 */
65 public $debugOutput = FALSE;
66
67 /**
68 * Internally: Set to last built query (not necessarily executed...)
69 *
70 * @var string
71 */
72 public $debug_lastBuiltQuery = '';
73
74 /**
75 * Set "TRUE" if you want the last built query to be stored in $debug_lastBuiltQuery independent of $this->debugOutput
76 *
77 * @var bool
78 */
79 public $store_lastBuiltQuery = FALSE;
80
81 /**
82 * Set this to 1 to get queries explained (devIPmask must match). Set the value to 2 to the same but disregarding the devIPmask.
83 * There is an alternative option to enable explain output in the admin panel under "TypoScript", which will produce much nicer output, but only works in FE.
84 *
85 * @var bool
86 */
87 public $explainOutput = 0;
88
89 /**
90 * @var string Database host to connect to
91 */
92 protected $databaseHost = '';
93
94 /**
95 * @var int Database port to connect to
96 */
97 protected $databasePort = 3306;
98
99 /**
100 * @var string|NULL Database socket to connect to
101 */
102 protected $databaseSocket = NULL;
103
104 /**
105 * @var string Database name to connect to
106 */
107 protected $databaseName = '';
108
109 /**
110 * @var string Database user to connect with
111 */
112 protected $databaseUsername = '';
113
114 /**
115 * @var string Database password to connect with
116 */
117 protected $databaseUserPassword = '';
118
119 /**
120 * @var bool TRUE if database connection should be persistent
121 * @see http://php.net/manual/de/mysqli.persistconns.php
122 */
123 protected $persistentDatabaseConnection = FALSE;
124
125 /**
126 * @var bool TRUE if connection between client and sql server is compressed
127 */
128 protected $connectionCompression = FALSE;
129
130 /**
131 * The charset for the connection; will be passed on to
132 * mysqli_set_charset during connection initialization.
133 *
134 * @var string
135 */
136 protected $connectionCharset = 'utf8';
137
138 /**
139 * @var array List of commands executed after connection was established
140 */
141 protected $initializeCommandsAfterConnect = array();
142
143 /**
144 * @var bool TRUE if database connection is established
145 */
146 protected $isConnected = FALSE;
147
148 /**
149 * @var \mysqli $link Default database link object
150 */
151 protected $link = NULL;
152
153 /**
154 * Default character set, applies unless character set or collation are explicitly set
155 *
156 * @var string
157 */
158 public $default_charset = 'utf8';
159
160 /**
161 * @var array<PostProcessQueryHookInterface>
162 */
163 protected $preProcessHookObjects = array();
164
165 /**
166 * @var array<PreProcessQueryHookInterface>
167 */
168 protected $postProcessHookObjects = array();
169
170 /**
171 * the date and time formats compatible with the database in general
172 *
173 * @var array
174 */
175 static protected $dateTimeFormats = array(
176 'date' => array(
177 'empty' => '0000-00-00',
178 'format' => 'Y-m-d'
179 ),
180 'datetime' => array(
181 'empty' => '0000-00-00 00:00:00',
182 'format' => 'Y-m-d H:i:s'
183 )
184 );
185
186 /**
187 * Initialize the database connection
188 *
189 * @return void
190 */
191 public function initialize() {
192 // Intentionally blank as this will be overloaded by DBAL
193 }
194
195 /************************************
196 *
197 * Query execution
198 *
199 * These functions are the RECOMMENDED DBAL functions for use in your applications
200 * Using these functions will allow the DBAL to use alternative ways of accessing data (contrary to if a query is returned!)
201 * They compile a query AND execute it immediately and then return the result
202 * This principle heightens our ability to create various forms of DBAL of the functions.
203 * Generally: We want to return a result pointer/object, never queries.
204 * Also, having the table name together with the actual query execution allows us to direct the request to other databases.
205 *
206 **************************************/
207
208 /**
209 * Creates and executes an INSERT SQL-statement for $table from the array with field/value pairs $fields_values.
210 * Using this function specifically allows us to handle BLOB and CLOB fields depending on DB
211 *
212 * @param string $table Table name
213 * @param array $fields_values Field values as key=>value pairs. Values will be escaped internally. Typically you would fill an array like "$insertFields" with 'fieldname'=>'value' and pass it to this function as argument.
214 * @param bool|array|string $no_quote_fields See fullQuoteArray()
215 * @return bool|\mysqli_result|object MySQLi result object / DBAL object
216 */
217 public function exec_INSERTquery($table, $fields_values, $no_quote_fields = FALSE) {
218 $res = $this->query($this->INSERTquery($table, $fields_values, $no_quote_fields));
219 if ($this->debugOutput) {
220 $this->debug('exec_INSERTquery');
221 }
222 foreach ($this->postProcessHookObjects as $hookObject) {
223 /** @var $hookObject PostProcessQueryHookInterface */
224 $hookObject->exec_INSERTquery_postProcessAction($table, $fields_values, $no_quote_fields, $this);
225 }
226 return $res;
227 }
228
229 /**
230 * Creates and executes an INSERT SQL-statement for $table with multiple rows.
231 *
232 * @param string $table Table name
233 * @param array $fields Field names
234 * @param array $rows Table rows. Each row should be an array with field values mapping to $fields
235 * @param bool|array|string $no_quote_fields See fullQuoteArray()
236 * @return bool|\mysqli_result|object MySQLi result object / DBAL object
237 */
238 public function exec_INSERTmultipleRows($table, array $fields, array $rows, $no_quote_fields = FALSE) {
239 $res = $this->query($this->INSERTmultipleRows($table, $fields, $rows, $no_quote_fields));
240 if ($this->debugOutput) {
241 $this->debug('exec_INSERTmultipleRows');
242 }
243 foreach ($this->postProcessHookObjects as $hookObject) {
244 /** @var $hookObject PostProcessQueryHookInterface */
245 $hookObject->exec_INSERTmultipleRows_postProcessAction($table, $fields, $rows, $no_quote_fields, $this);
246 }
247 return $res;
248 }
249
250 /**
251 * Creates and executes an UPDATE SQL-statement for $table where $where-clause (typ. 'uid=...') from the array with field/value pairs $fields_values.
252 * Using this function specifically allow us to handle BLOB and CLOB fields depending on DB
253 *
254 * @param string $table Database tablename
255 * @param string $where WHERE clause, eg. "uid=1". NOTICE: You must escape values in this argument with $this->fullQuoteStr() yourself!
256 * @param array $fields_values Field values as key=>value pairs. Values will be escaped internally. Typically you would fill an array like "$updateFields" with 'fieldname'=>'value' and pass it to this function as argument.
257 * @param bool|array|string $no_quote_fields See fullQuoteArray()
258 * @return bool|\mysqli_result|object MySQLi result object / DBAL object
259 */
260 public function exec_UPDATEquery($table, $where, $fields_values, $no_quote_fields = FALSE) {
261 $res = $this->query($this->UPDATEquery($table, $where, $fields_values, $no_quote_fields));
262 if ($this->debugOutput) {
263 $this->debug('exec_UPDATEquery');
264 }
265 foreach ($this->postProcessHookObjects as $hookObject) {
266 /** @var $hookObject PostProcessQueryHookInterface */
267 $hookObject->exec_UPDATEquery_postProcessAction($table, $where, $fields_values, $no_quote_fields, $this);
268 }
269 return $res;
270 }
271
272 /**
273 * Creates and executes a DELETE SQL-statement for $table where $where-clause
274 *
275 * @param string $table Database tablename
276 * @param string $where WHERE clause, eg. "uid=1". NOTICE: You must escape values in this argument with $this->fullQuoteStr() yourself!
277 * @return bool|\mysqli_result|object MySQLi result object / DBAL object
278 */
279 public function exec_DELETEquery($table, $where) {
280 $res = $this->query($this->DELETEquery($table, $where));
281 if ($this->debugOutput) {
282 $this->debug('exec_DELETEquery');
283 }
284 foreach ($this->postProcessHookObjects as $hookObject) {
285 /** @var $hookObject PostProcessQueryHookInterface */
286 $hookObject->exec_DELETEquery_postProcessAction($table, $where, $this);
287 }
288 return $res;
289 }
290
291 /**
292 * Creates and executes a SELECT SQL-statement
293 * Using this function specifically allow us to handle the LIMIT feature independently of DB.
294 *
295 * @param string $select_fields List of fields to select from the table. This is what comes right after "SELECT ...". Required value.
296 * @param string $from_table Table(s) from which to select. This is what comes right after "FROM ...". Required value.
297 * @param string $where_clause Additional WHERE clauses put in the end of the query. NOTICE: You must escape values in this argument with $this->fullQuoteStr() yourself! DO NOT PUT IN GROUP BY, ORDER BY or LIMIT!
298 * @param string $groupBy Optional GROUP BY field(s), if none, supply blank string.
299 * @param string $orderBy Optional ORDER BY field(s), if none, supply blank string.
300 * @param string $limit Optional LIMIT value ([begin,]max), if none, supply blank string.
301 * @return bool|\mysqli_result|object MySQLi result object / DBAL object
302 */
303 public function exec_SELECTquery($select_fields, $from_table, $where_clause, $groupBy = '', $orderBy = '', $limit = '') {
304 $query = $this->SELECTquery($select_fields, $from_table, $where_clause, $groupBy, $orderBy, $limit);
305 $res = $this->query($query);
306 if ($this->debugOutput) {
307 $this->debug('exec_SELECTquery');
308 }
309 if ($this->explainOutput) {
310 $this->explain($query, $from_table, $res->num_rows);
311 }
312 foreach ($this->postProcessHookObjects as $hookObject) {
313 /** @var $hookObject PostProcessQueryHookInterface */
314 $hookObject->exec_SELECTquery_postProcessAction($select_fields, $from_table, $where_clause, $groupBy = '', $orderBy = '', $limit = '', $this);
315 }
316 return $res;
317 }
318
319 /**
320 * Creates and executes a SELECT query, selecting fields ($select) from two/three tables joined
321 * Use $mm_table together with $local_table or $foreign_table to select over two tables. Or use all three tables to select the full MM-relation.
322 * The JOIN is done with [$local_table].uid <--> [$mm_table].uid_local / [$mm_table].uid_foreign <--> [$foreign_table].uid
323 * The function is very useful for selecting MM-relations between tables adhering to the MM-format used by TCE (TYPO3 Core Engine). See the section on $GLOBALS['TCA'] in Inside TYPO3 for more details.
324 *
325 * @param string $select Field list for SELECT
326 * @param string $local_table Tablename, local table
327 * @param string $mm_table Tablename, relation table
328 * @param string $foreign_table Tablename, foreign table
329 * @param string $whereClause Optional additional WHERE clauses put in the end of the query. NOTICE: You must escape values in this argument with $this->fullQuoteStr() yourself! DO NOT PUT IN GROUP BY, ORDER BY or LIMIT! You have to prepend 'AND ' to this parameter yourself!
330 * @param string $groupBy Optional GROUP BY field(s), if none, supply blank string.
331 * @param string $orderBy Optional ORDER BY field(s), if none, supply blank string.
332 * @param string $limit Optional LIMIT value ([begin,]max), if none, supply blank string.
333 * @return bool|\mysqli_result|object MySQLi result object / DBAL object
334 * @see exec_SELECTquery()
335 */
336 public function exec_SELECT_mm_query($select, $local_table, $mm_table, $foreign_table, $whereClause = '', $groupBy = '', $orderBy = '', $limit = '') {
337 $foreign_table_as = $foreign_table == $local_table ? $foreign_table . str_replace('.', '', uniqid('_join', TRUE)) : '';
338 $mmWhere = $local_table ? $local_table . '.uid=' . $mm_table . '.uid_local' : '';
339 $mmWhere .= ($local_table and $foreign_table) ? ' AND ' : '';
340 $tables = ($local_table ? $local_table . ',' : '') . $mm_table;
341 if ($foreign_table) {
342 $mmWhere .= ($foreign_table_as ?: $foreign_table) . '.uid=' . $mm_table . '.uid_foreign';
343 $tables .= ',' . $foreign_table . ($foreign_table_as ? ' AS ' . $foreign_table_as : '');
344 }
345 return $this->exec_SELECTquery($select, $tables, $mmWhere . ' ' . $whereClause, $groupBy, $orderBy, $limit);
346 }
347
348 /**
349 * Executes a select based on input query parts array
350 *
351 * @param array $queryParts Query parts array
352 * @return bool|\mysqli_result|object MySQLi result object / DBAL object
353 * @see exec_SELECTquery()
354 */
355 public function exec_SELECT_queryArray($queryParts) {
356 return $this->exec_SELECTquery($queryParts['SELECT'], $queryParts['FROM'], $queryParts['WHERE'], $queryParts['GROUPBY'], $queryParts['ORDERBY'], $queryParts['LIMIT']);
357 }
358
359 /**
360 * Creates and executes a SELECT SQL-statement AND traverse result set and returns array with records in.
361 *
362 * @param string $select_fields List of fields to select from the table. This is what comes right after "SELECT ...". Required value.
363 * @param string $from_table Table(s) from which to select. This is what comes right after "FROM ...". Required value.
364 * @param string $where_clause Additional WHERE clauses put in the end of the query. NOTICE: You must escape values in this argument with $this->fullQuoteStr() yourself! DO NOT PUT IN GROUP BY, ORDER BY or LIMIT!
365 * @param string $groupBy Optional GROUP BY field(s), if none, supply blank string.
366 * @param string $orderBy Optional ORDER BY field(s), if none, supply blank string.
367 * @param string $limit Optional LIMIT value ([begin,]max), if none, supply blank string.
368 * @param string $uidIndexField If set, the result array will carry this field names value as index. Requires that field to be selected of course!
369 * @return array|NULL Array of rows, or NULL in case of SQL error
370 * @see exec_SELECTquery()
371 * @throws \InvalidArgumentException
372 */
373 public function exec_SELECTgetRows($select_fields, $from_table, $where_clause, $groupBy = '', $orderBy = '', $limit = '', $uidIndexField = '') {
374 $res = $this->exec_SELECTquery($select_fields, $from_table, $where_clause, $groupBy, $orderBy, $limit);
375 if ($this->sql_error()) {
376 $this->sql_free_result($res);
377 return NULL;
378 }
379 $output = array();
380 $firstRecord = TRUE;
381 while ($record = $this->sql_fetch_assoc($res)) {
382 if ($uidIndexField) {
383 if ($firstRecord) {
384 $firstRecord = FALSE;
385 if (!array_key_exists($uidIndexField, $record)) {
386 $this->sql_free_result($res);
387 throw new \InvalidArgumentException('The given $uidIndexField "' . $uidIndexField . '" is not available in the result.', 1432933855);
388 }
389 }
390 $output[$record[$uidIndexField]] = $record;
391 } else {
392 $output[] = $record;
393 }
394 }
395 $this->sql_free_result($res);
396 return $output;
397 }
398
399 /**
400 * Creates and executes a SELECT SQL-statement AND gets a result set and returns an array with a single record in.
401 * LIMIT is automatically set to 1 and can not be overridden.
402 *
403 * @param string $select_fields List of fields to select from the table.
404 * @param string $from_table Table(s) from which to select.
405 * @param string $where_clause Optional additional WHERE clauses put in the end of the query. NOTICE: You must escape values in this argument with $this->fullQuoteStr() yourself!
406 * @param string $groupBy Optional GROUP BY field(s), if none, supply blank string.
407 * @param string $orderBy Optional ORDER BY field(s), if none, supply blank string.
408 * @param bool $numIndex If set, the result will be fetched with sql_fetch_row, otherwise sql_fetch_assoc will be used.
409 * @return array|FALSE|NULL Single row, FALSE on empty result, NULL on error
410 */
411 public function exec_SELECTgetSingleRow($select_fields, $from_table, $where_clause, $groupBy = '', $orderBy = '', $numIndex = FALSE) {
412 $res = $this->exec_SELECTquery($select_fields, $from_table, $where_clause, $groupBy, $orderBy, '1');
413 $output = NULL;
414 if ($res !== FALSE) {
415 if ($numIndex) {
416 $output = $this->sql_fetch_row($res);
417 } else {
418 $output = $this->sql_fetch_assoc($res);
419 }
420 $this->sql_free_result($res);
421 }
422 return $output;
423 }
424
425 /**
426 * Counts the number of rows in a table.
427 *
428 * @param string $field Name of the field to use in the COUNT() expression (e.g. '*')
429 * @param string $table Name of the table to count rows for
430 * @param string $where (optional) WHERE statement of the query
431 * @return mixed Number of rows counter (int) or FALSE if something went wrong (bool)
432 */
433 public function exec_SELECTcountRows($field, $table, $where = '1=1') {
434 $count = FALSE;
435 $resultSet = $this->exec_SELECTquery('COUNT(' . $field . ')', $table, $where);
436 if ($resultSet !== FALSE) {
437 list($count) = $this->sql_fetch_row($resultSet);
438 $count = (int)$count;
439 $this->sql_free_result($resultSet);
440 }
441 return $count;
442 }
443
444 /**
445 * Truncates a table.
446 *
447 * @param string $table Database tablename
448 * @return mixed Result from handler
449 */
450 public function exec_TRUNCATEquery($table) {
451 $res = $this->query($this->TRUNCATEquery($table));
452 if ($this->debugOutput) {
453 $this->debug('exec_TRUNCATEquery');
454 }
455 foreach ($this->postProcessHookObjects as $hookObject) {
456 /** @var $hookObject PostProcessQueryHookInterface */
457 $hookObject->exec_TRUNCATEquery_postProcessAction($table, $this);
458 }
459 return $res;
460 }
461
462 /**
463 * Central query method. Also checks if there is a database connection.
464 * Use this to execute database queries instead of directly calling $this->link->query()
465 *
466 * @param string $query The query to send to the database
467 * @return bool|\mysqli_result
468 */
469 protected function query($query) {
470 if (!$this->isConnected) {
471 $this->connectDB();
472 }
473 return $this->link->query($query);
474 }
475
476 /**************************************
477 *
478 * Query building
479 *
480 **************************************/
481 /**
482 * Creates an INSERT SQL-statement for $table from the array with field/value pairs $fields_values.
483 *
484 * @param string $table See exec_INSERTquery()
485 * @param array $fields_values See exec_INSERTquery()
486 * @param bool|array|string $no_quote_fields See fullQuoteArray()
487 * @return string|NULL Full SQL query for INSERT, NULL if $fields_values is empty
488 */
489 public function INSERTquery($table, $fields_values, $no_quote_fields = FALSE) {
490 // Table and fieldnames should be "SQL-injection-safe" when supplied to this
491 // function (contrary to values in the arrays which may be insecure).
492 if (!is_array($fields_values) || empty($fields_values)) {
493 return NULL;
494 }
495 foreach ($this->preProcessHookObjects as $hookObject) {
496 $hookObject->INSERTquery_preProcessAction($table, $fields_values, $no_quote_fields, $this);
497 }
498 // Quote and escape values
499 $fields_values = $this->fullQuoteArray($fields_values, $table, $no_quote_fields, TRUE);
500 // Build query
501 $query = 'INSERT INTO ' . $table . ' (' . implode(',', array_keys($fields_values)) . ') VALUES ' . '(' . implode(',', $fields_values) . ')';
502 // Return query
503 if ($this->debugOutput || $this->store_lastBuiltQuery) {
504 $this->debug_lastBuiltQuery = $query;
505 }
506 return $query;
507 }
508
509 /**
510 * Creates an INSERT SQL-statement for $table with multiple rows.
511 *
512 * @param string $table Table name
513 * @param array $fields Field names
514 * @param array $rows Table rows. Each row should be an array with field values mapping to $fields
515 * @param bool|array|string $no_quote_fields See fullQuoteArray()
516 * @return string|NULL Full SQL query for INSERT, NULL if $rows is empty
517 */
518 public function INSERTmultipleRows($table, array $fields, array $rows, $no_quote_fields = FALSE) {
519 // Table and fieldnames should be "SQL-injection-safe" when supplied to this
520 // function (contrary to values in the arrays which may be insecure).
521 if (empty($rows)) {
522 return NULL;
523 }
524 foreach ($this->preProcessHookObjects as $hookObject) {
525 /** @var $hookObject PreProcessQueryHookInterface */
526 $hookObject->INSERTmultipleRows_preProcessAction($table, $fields, $rows, $no_quote_fields, $this);
527 }
528 // Build query
529 $query = 'INSERT INTO ' . $table . ' (' . implode(', ', $fields) . ') VALUES ';
530 $rowSQL = array();
531 foreach ($rows as $row) {
532 // Quote and escape values
533 $row = $this->fullQuoteArray($row, $table, $no_quote_fields);
534 $rowSQL[] = '(' . implode(', ', $row) . ')';
535 }
536 $query .= implode(', ', $rowSQL);
537 // Return query
538 if ($this->debugOutput || $this->store_lastBuiltQuery) {
539 $this->debug_lastBuiltQuery = $query;
540 }
541 return $query;
542 }
543
544 /**
545 * Creates an UPDATE SQL-statement for $table where $where-clause (typ. 'uid=...') from the array with field/value pairs $fields_values.
546 *
547 *
548 * @param string $table See exec_UPDATEquery()
549 * @param string $where See exec_UPDATEquery()
550 * @param array $fields_values See exec_UPDATEquery()
551 * @param bool|array|string $no_quote_fields See fullQuoteArray()
552 * @throws \InvalidArgumentException
553 * @return string Full SQL query for UPDATE
554 */
555 public function UPDATEquery($table, $where, $fields_values, $no_quote_fields = FALSE) {
556 // Table and fieldnames should be "SQL-injection-safe" when supplied to this
557 // function (contrary to values in the arrays which may be insecure).
558 if (is_string($where)) {
559 foreach ($this->preProcessHookObjects as $hookObject) {
560 /** @var $hookObject PreProcessQueryHookInterface */
561 $hookObject->UPDATEquery_preProcessAction($table, $where, $fields_values, $no_quote_fields, $this);
562 }
563 $fields = array();
564 if (is_array($fields_values) && !empty($fields_values)) {
565 // Quote and escape values
566 $nArr = $this->fullQuoteArray($fields_values, $table, $no_quote_fields, TRUE);
567 foreach ($nArr as $k => $v) {
568 $fields[] = $k . '=' . $v;
569 }
570 }
571 // Build query
572 $query = 'UPDATE ' . $table . ' SET ' . implode(',', $fields) . ((string)$where !== '' ? ' WHERE ' . $where : '');
573 if ($this->debugOutput || $this->store_lastBuiltQuery) {
574 $this->debug_lastBuiltQuery = $query;
575 }
576 return $query;
577 } else {
578 throw new \InvalidArgumentException('TYPO3 Fatal Error: "Where" clause argument for UPDATE query was not a string in $this->UPDATEquery() !', 1270853880);
579 }
580 }
581
582 /**
583 * Creates a DELETE SQL-statement for $table where $where-clause
584 *
585 * @param string $table See exec_DELETEquery()
586 * @param string $where See exec_DELETEquery()
587 * @return string Full SQL query for DELETE
588 * @throws \InvalidArgumentException
589 */
590 public function DELETEquery($table, $where) {
591 if (is_string($where)) {
592 foreach ($this->preProcessHookObjects as $hookObject) {
593 /** @var $hookObject PreProcessQueryHookInterface */
594 $hookObject->DELETEquery_preProcessAction($table, $where, $this);
595 }
596 // Table and fieldnames should be "SQL-injection-safe" when supplied to this function
597 $query = 'DELETE FROM ' . $table . ((string)$where !== '' ? ' WHERE ' . $where : '');
598 if ($this->debugOutput || $this->store_lastBuiltQuery) {
599 $this->debug_lastBuiltQuery = $query;
600 }
601 return $query;
602 } else {
603 throw new \InvalidArgumentException('TYPO3 Fatal Error: "Where" clause argument for DELETE query was not a string in $this->DELETEquery() !', 1270853881);
604 }
605 }
606
607 /**
608 * Creates a SELECT SQL-statement
609 *
610 * @param string $select_fields See exec_SELECTquery()
611 * @param string $from_table See exec_SELECTquery()
612 * @param string $where_clause See exec_SELECTquery()
613 * @param string $groupBy See exec_SELECTquery()
614 * @param string $orderBy See exec_SELECTquery()
615 * @param string $limit See exec_SELECTquery()
616 * @return string Full SQL query for SELECT
617 */
618 public function SELECTquery($select_fields, $from_table, $where_clause, $groupBy = '', $orderBy = '', $limit = '') {
619 foreach ($this->preProcessHookObjects as $hookObject) {
620 /** @var $hookObject PreProcessQueryHookInterface */
621 $hookObject->SELECTquery_preProcessAction($select_fields, $from_table, $where_clause, $groupBy, $orderBy, $limit, $this);
622 }
623 // Table and fieldnames should be "SQL-injection-safe" when supplied to this function
624 // Build basic query
625 $query = 'SELECT ' . $select_fields . ' FROM ' . $from_table . ((string)$where_clause !== '' ? ' WHERE ' . $where_clause : '');
626 // Group by
627 $query .= (string)$groupBy !== '' ? ' GROUP BY ' . $groupBy : '';
628 // Order by
629 $query .= (string)$orderBy !== '' ? ' ORDER BY ' . $orderBy : '';
630 // Group by
631 $query .= (string)$limit !== '' ? ' LIMIT ' . $limit : '';
632 // Return query
633 if ($this->debugOutput || $this->store_lastBuiltQuery) {
634 $this->debug_lastBuiltQuery = $query;
635 }
636 return $query;
637 }
638
639 /**
640 * Creates a SELECT SQL-statement to be used as subquery within another query.
641 * BEWARE: This method should not be overriden within DBAL to prevent quoting from happening.
642 *
643 * @param string $select_fields List of fields to select from the table.
644 * @param string $from_table Table from which to select.
645 * @param string $where_clause Conditional WHERE statement
646 * @return string Full SQL query for SELECT
647 */
648 public function SELECTsubquery($select_fields, $from_table, $where_clause) {
649 // Table and fieldnames should be "SQL-injection-safe" when supplied to this function
650 // Build basic query:
651 $query = 'SELECT ' . $select_fields . ' FROM ' . $from_table . ((string)$where_clause !== '' ? ' WHERE ' . $where_clause : '');
652 // Return query
653 if ($this->debugOutput || $this->store_lastBuiltQuery) {
654 $this->debug_lastBuiltQuery = $query;
655 }
656 return $query;
657 }
658
659 /**
660 * Creates a TRUNCATE TABLE SQL-statement
661 *
662 * @param string $table See exec_TRUNCATEquery()
663 * @return string Full SQL query for TRUNCATE TABLE
664 */
665 public function TRUNCATEquery($table) {
666 foreach ($this->preProcessHookObjects as $hookObject) {
667 /** @var $hookObject PreProcessQueryHookInterface */
668 $hookObject->TRUNCATEquery_preProcessAction($table, $this);
669 }
670 // Table should be "SQL-injection-safe" when supplied to this function
671 // Build basic query:
672 $query = 'TRUNCATE TABLE ' . $table;
673 // Return query:
674 if ($this->debugOutput || $this->store_lastBuiltQuery) {
675 $this->debug_lastBuiltQuery = $query;
676 }
677 return $query;
678 }
679
680 /**
681 * Returns a WHERE clause that can find a value ($value) in a list field ($field)
682 * For instance a record in the database might contain a list of numbers,
683 * "34,234,5" (with no spaces between). This query would be able to select that
684 * record based on the value "34", "234" or "5" regardless of their position in
685 * the list (left, middle or right).
686 * The value must not contain a comma (,)
687 * Is nice to look up list-relations to records or files in TYPO3 database tables.
688 *
689 * @param string $field Field name
690 * @param string $value Value to find in list
691 * @param string $table Table in which we are searching (for DBAL detection of quoteStr() method)
692 * @return string WHERE clause for a query
693 * @throws \InvalidArgumentException
694 */
695 public function listQuery($field, $value, $table) {
696 $value = (string)$value;
697 if (strpos($value, ',') !== FALSE) {
698 throw new \InvalidArgumentException('$value must not contain a comma (,) in $this->listQuery() !', 1294585862);
699 }
700 $pattern = $this->quoteStr($value, $table);
701 $where = 'FIND_IN_SET(\'' . $pattern . '\',' . $field . ')';
702 return $where;
703 }
704
705 /**
706 * Returns a WHERE clause which will make an AND or OR search for the words in the $searchWords array in any of the fields in array $fields.
707 *
708 * @param array $searchWords Array of search words
709 * @param array $fields Array of fields
710 * @param string $table Table in which we are searching (for DBAL detection of quoteStr() method)
711 * @param string $constraint How multiple search words have to match ('AND' or 'OR')
712 * @return string WHERE clause for search
713 */
714 public function searchQuery($searchWords, $fields, $table, $constraint = self::AND_Constraint) {
715 switch ($constraint) {
716 case self::OR_Constraint:
717 $constraint = 'OR';
718 break;
719 default:
720 $constraint = 'AND';
721 }
722
723 $queryParts = array();
724 foreach ($searchWords as $sw) {
725 $like = ' LIKE \'%' . $this->quoteStr($sw, $table) . '%\'';
726 $queryParts[] = $table . '.' . implode(($like . ' OR ' . $table . '.'), $fields) . $like;
727 }
728 $query = '(' . implode(') ' . $constraint . ' (', $queryParts) . ')';
729
730 return $query;
731 }
732
733 /**************************************
734 *
735 * Prepared Query Support
736 *
737 **************************************/
738 /**
739 * Creates a SELECT prepared SQL statement.
740 *
741 * @param string $select_fields See exec_SELECTquery()
742 * @param string $from_table See exec_SELECTquery()
743 * @param string $where_clause See exec_SELECTquery()
744 * @param string $groupBy See exec_SELECTquery()
745 * @param string $orderBy See exec_SELECTquery()
746 * @param string $limit See exec_SELECTquery()
747 * @param array $input_parameters An array of values with as many elements as there are bound parameters in the SQL statement being executed. All values are treated as \TYPO3\CMS\Core\Database\PreparedStatement::PARAM_AUTOTYPE.
748 * @return \TYPO3\CMS\Core\Database\PreparedStatement Prepared statement
749 */
750 public function prepare_SELECTquery($select_fields, $from_table, $where_clause, $groupBy = '', $orderBy = '', $limit = '', array $input_parameters = array()) {
751 $query = $this->SELECTquery($select_fields, $from_table, $where_clause, $groupBy, $orderBy, $limit);
752 /** @var $preparedStatement \TYPO3\CMS\Core\Database\PreparedStatement */
753 $preparedStatement = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Database\PreparedStatement::class, $query, $from_table, array());
754 // Bind values to parameters
755 foreach ($input_parameters as $key => $value) {
756 $preparedStatement->bindValue($key, $value, PreparedStatement::PARAM_AUTOTYPE);
757 }
758 // Return prepared statement
759 return $preparedStatement;
760 }
761
762 /**
763 * Creates a SELECT prepared SQL statement based on input query parts array
764 *
765 * @param array $queryParts Query parts array
766 * @param array $input_parameters An array of values with as many elements as there are bound parameters in the SQL statement being executed. All values are treated as \TYPO3\CMS\Core\Database\PreparedStatement::PARAM_AUTOTYPE.
767 * @return \TYPO3\CMS\Core\Database\PreparedStatement Prepared statement
768 */
769 public function prepare_SELECTqueryArray(array $queryParts, array $input_parameters = array()) {
770 return $this->prepare_SELECTquery($queryParts['SELECT'], $queryParts['FROM'], $queryParts['WHERE'], $queryParts['GROUPBY'], $queryParts['ORDERBY'], $queryParts['LIMIT'], $input_parameters);
771 }
772
773 /**
774 * Prepares a prepared query.
775 *
776 * @param string $query The query to execute
777 * @param array $queryComponents The components of the query to execute
778 * @return \mysqli_stmt|object MySQLi statement / DBAL object
779 * @internal This method may only be called by \TYPO3\CMS\Core\Database\PreparedStatement
780 */
781 public function prepare_PREPAREDquery($query, array $queryComponents) {
782 if (!$this->isConnected) {
783 $this->connectDB();
784 }
785 $stmt = $this->link->stmt_init();
786 $success = $stmt->prepare($query);
787 if ($this->debugOutput) {
788 $this->debug('stmt_execute', $query);
789 }
790 return $success ? $stmt : NULL;
791 }
792
793 /**************************************
794 *
795 * Various helper functions
796 *
797 * Functions recommended to be used for
798 * - escaping values,
799 * - cleaning lists of values,
800 * - stripping of excess ORDER BY/GROUP BY keywords
801 *
802 **************************************/
803 /**
804 * Escaping and quoting values for SQL statements.
805 *
806 * @param string $str Input string
807 * @param string $table Table name for which to quote string. Just enter the table that the field-value is selected from (and any DBAL will look up which handler to use and then how to quote the string!).
808 * @param bool $allowNull Whether to allow NULL values
809 * @return string Output string; Wrapped in single quotes and quotes in the string (" / ') and \ will be backslashed (or otherwise based on DBAL handler)
810 * @see quoteStr()
811 */
812 public function fullQuoteStr($str, $table, $allowNull = FALSE) {
813 if (!$this->isConnected) {
814 $this->connectDB();
815 }
816 if ($allowNull && $str === NULL) {
817 return 'NULL';
818 }
819
820 return '\'' . $this->link->real_escape_string($str) . '\'';
821 }
822
823 /**
824 * Will fullquote all values in the one-dimensional array so they are ready to "implode" for an sql query.
825 *
826 * @param array $arr Array with values (either associative or non-associative array)
827 * @param string $table Table name for which to quote
828 * @param bool|array $noQuote List/array of keys NOT to quote (eg. SQL functions) - ONLY for associative arrays
829 * @param bool $allowNull Whether to allow NULL values
830 * @return array The input array with the values quoted
831 * @see cleanIntArray()
832 */
833 public function fullQuoteArray($arr, $table, $noQuote = FALSE, $allowNull = FALSE) {
834 if (is_string($noQuote)) {
835 $noQuote = explode(',', $noQuote);
836 } elseif (!is_array($noQuote)) {
837 $noQuote = FALSE;
838 }
839 foreach ($arr as $k => $v) {
840 if ($noQuote === FALSE || !in_array($k, $noQuote)) {
841 $arr[$k] = $this->fullQuoteStr($v, $table, $allowNull);
842 }
843 }
844 return $arr;
845 }
846
847 /**
848 * Substitution for PHP function "addslashes()"
849 * Use this function instead of the PHP addslashes() function when you build queries - this will prepare your code for DBAL.
850 * NOTICE: You must wrap the output of this function in SINGLE QUOTES to be DBAL compatible. Unless you have to apply the single quotes yourself you should rather use ->fullQuoteStr()!
851 *
852 * @param string $str Input string
853 * @param string $table Table name for which to quote string. Just enter the table that the field-value is selected from (and any DBAL will look up which handler to use and then how to quote the string!).
854 * @return string Output string; Quotes (" / ') and \ will be backslashed (or otherwise based on DBAL handler)
855 * @see quoteStr()
856 */
857 public function quoteStr($str, $table) {
858 if (!$this->isConnected) {
859 $this->connectDB();
860 }
861 return $this->link->real_escape_string($str);
862 }
863
864 /**
865 * Escaping values for SQL LIKE statements.
866 *
867 * @param string $str Input string
868 * @param string $table Table name for which to escape string. Just enter the table that the field-value is selected from (and any DBAL will look up which handler to use and then how to quote the string!).
869 * @return string Output string; % and _ will be escaped with \ (or otherwise based on DBAL handler)
870 * @see quoteStr()
871 */
872 public function escapeStrForLike($str, $table) {
873 return addcslashes($str, '_%');
874 }
875
876 /**
877 * Will convert all values in the one-dimensional array to integers.
878 * Useful when you want to make sure an array contains only integers before imploding them in a select-list.
879 *
880 * @param array $arr Array with values
881 * @return array The input array with all values cast to (int)
882 * @see cleanIntList()
883 */
884 public function cleanIntArray($arr) {
885 return array_map('intval', $arr);
886 }
887
888 /**
889 * Will force all entries in the input comma list to integers
890 * Useful when you want to make sure a commalist of supposed integers really contain only integers; You want to know that when you don't trust content that could go into an SQL statement.
891 *
892 * @param string $list List of comma-separated values which should be integers
893 * @return string The input list but with every value cast to (int)
894 * @see cleanIntArray()
895 */
896 public function cleanIntList($list) {
897 return implode(',', GeneralUtility::intExplode(',', $list));
898 }
899
900 /**
901 * Removes the prefix "ORDER BY" from the input string.
902 * This function is used when you call the exec_SELECTquery() function and want to pass the ORDER BY parameter by can't guarantee that "ORDER BY" is not prefixed.
903 * Generally; This function provides a work-around to the situation where you cannot pass only the fields by which to order the result.
904 *
905 * @param string $str eg. "ORDER BY title, uid
906 * @return string eg. "title, uid
907 * @see exec_SELECTquery(), stripGroupBy()
908 */
909 public function stripOrderBy($str) {
910 return preg_replace('/^(?:ORDER[[:space:]]*BY[[:space:]]*)+/i', '', trim($str));
911 }
912
913 /**
914 * Removes the prefix "GROUP BY" from the input string.
915 * This function is used when you call the SELECTquery() function and want to pass the GROUP BY parameter by can't guarantee that "GROUP BY" is not prefixed.
916 * Generally; This function provides a work-around to the situation where you cannot pass only the fields by which to order the result.
917 *
918 * @param string $str eg. "GROUP BY title, uid
919 * @return string eg. "title, uid
920 * @see exec_SELECTquery(), stripOrderBy()
921 */
922 public function stripGroupBy($str) {
923 return preg_replace('/^(?:GROUP[[:space:]]*BY[[:space:]]*)+/i', '', trim($str));
924 }
925
926 /**
927 * Takes the last part of a query, eg. "... uid=123 GROUP BY title ORDER BY title LIMIT 5,2" and splits each part into a table (WHERE, GROUPBY, ORDERBY, LIMIT)
928 * Work-around function for use where you know some userdefined end to an SQL clause is supplied and you need to separate these factors.
929 *
930 * @param string $str Input string
931 * @return array
932 */
933 public function splitGroupOrderLimit($str) {
934 // Prepending a space to make sure "[[:space:]]+" will find a space there
935 // for the first element.
936 $str = ' ' . $str;
937 // Init output array:
938 $wgolParts = array(
939 'WHERE' => '',
940 'GROUPBY' => '',
941 'ORDERBY' => '',
942 'LIMIT' => ''
943 );
944 // Find LIMIT
945 $reg = array();
946 if (preg_match('/^(.*)[[:space:]]+LIMIT[[:space:]]+([[:alnum:][:space:],._]+)$/i', $str, $reg)) {
947 $wgolParts['LIMIT'] = trim($reg[2]);
948 $str = $reg[1];
949 }
950 // Find ORDER BY
951 $reg = array();
952 if (preg_match('/^(.*)[[:space:]]+ORDER[[:space:]]+BY[[:space:]]+([[:alnum:][:space:],._]+)$/i', $str, $reg)) {
953 $wgolParts['ORDERBY'] = trim($reg[2]);
954 $str = $reg[1];
955 }
956 // Find GROUP BY
957 $reg = array();
958 if (preg_match('/^(.*)[[:space:]]+GROUP[[:space:]]+BY[[:space:]]+([[:alnum:][:space:],._]+)$/i', $str, $reg)) {
959 $wgolParts['GROUPBY'] = trim($reg[2]);
960 $str = $reg[1];
961 }
962 // Rest is assumed to be "WHERE" clause
963 $wgolParts['WHERE'] = $str;
964 return $wgolParts;
965 }
966
967 /**
968 * Returns the date and time formats compatible with the given database table.
969 *
970 * @param string $table Table name for which to return an empty date. Just enter the table that the field-value is selected from (and any DBAL will look up which handler to use and then how date and time should be formatted).
971 * @return array
972 */
973 public function getDateTimeFormats($table) {
974 return self::$dateTimeFormats;
975 }
976
977 /**************************************
978 *
979 * MySQL(i) wrapper functions
980 * (For use in your applications)
981 *
982 **************************************/
983 /**
984 * Executes query
985 * MySQLi query() wrapper function
986 * Beware: Use of this method should be avoided as it is experimentally supported by DBAL. You should consider
987 * using exec_SELECTquery() and similar methods instead.
988 *
989 * @param string $query Query to execute
990 * @return bool|\mysqli_result|object MySQLi result object / DBAL object
991 */
992 public function sql_query($query) {
993 $res = $this->query($query);
994 if ($this->debugOutput) {
995 $this->debug('sql_query', $query);
996 }
997 return $res;
998 }
999
1000 /**
1001 * Returns the error status on the last query() execution
1002 *
1003 * @return string MySQLi error string.
1004 */
1005 public function sql_error() {
1006 return $this->link->error;
1007 }
1008
1009 /**
1010 * Returns the error number on the last query() execution
1011 *
1012 * @return int MySQLi error number
1013 */
1014 public function sql_errno() {
1015 return $this->link->errno;
1016 }
1017
1018 /**
1019 * Returns the number of selected rows.
1020 *
1021 * @param bool|\mysqli_result|object $res MySQLi result object / DBAL object
1022 * @return int Number of resulting rows
1023 */
1024 public function sql_num_rows($res) {
1025 if ($this->debug_check_recordset($res)) {
1026 return $res->num_rows;
1027 } else {
1028 return FALSE;
1029 }
1030 }
1031
1032 /**
1033 * Returns an associative array that corresponds to the fetched row, or FALSE if there are no more rows.
1034 * MySQLi fetch_assoc() wrapper function
1035 *
1036 * @param bool|\mysqli_result|object $res MySQLi result object / DBAL object
1037 * @return array|boolean Associative array of result row.
1038 */
1039 public function sql_fetch_assoc($res) {
1040 if ($this->debug_check_recordset($res)) {
1041 $result = $res->fetch_assoc();
1042 if ($result === NULL) {
1043 // Needed for compatibility
1044 $result = FALSE;
1045 }
1046 return $result;
1047 } else {
1048 return FALSE;
1049 }
1050 }
1051
1052 /**
1053 * Returns an array that corresponds to the fetched row, or FALSE if there are no more rows.
1054 * The array contains the values in numerical indices.
1055 * MySQLi fetch_row() wrapper function
1056 *
1057 * @param bool|\mysqli_result|object $res MySQLi result object / DBAL object
1058 * @return array|boolean Array with result rows.
1059 */
1060 public function sql_fetch_row($res) {
1061 if ($this->debug_check_recordset($res)) {
1062 $result = $res->fetch_row();
1063 if ($result === NULL) {
1064 // Needed for compatibility
1065 $result = FALSE;
1066 }
1067 return $result;
1068 } else {
1069 return FALSE;
1070 }
1071 }
1072
1073 /**
1074 * Free result memory
1075 * free_result() wrapper function
1076 *
1077 * @param bool|\mysqli_result|object $res MySQLi result object / DBAL object
1078 * @return bool Returns TRUE on success or FALSE on failure.
1079 */
1080 public function sql_free_result($res) {
1081 if ($this->debug_check_recordset($res) && is_object($res)) {
1082 $res->free();
1083 return TRUE;
1084 } else {
1085 return FALSE;
1086 }
1087 }
1088
1089 /**
1090 * Get the ID generated from the previous INSERT operation
1091 *
1092 * @return int The uid of the last inserted record.
1093 */
1094 public function sql_insert_id() {
1095 return $this->link->insert_id;
1096 }
1097
1098 /**
1099 * Returns the number of rows affected by the last INSERT, UPDATE or DELETE query
1100 *
1101 * @return int Number of rows affected by last query
1102 */
1103 public function sql_affected_rows() {
1104 return $this->link->affected_rows;
1105 }
1106
1107 /**
1108 * Move internal result pointer
1109 *
1110 * @param bool|\mysqli_result|object $res MySQLi result object / DBAL object
1111 * @param int $seek Seek result number.
1112 * @return bool Returns TRUE on success or FALSE on failure.
1113 */
1114 public function sql_data_seek($res, $seek) {
1115 if ($this->debug_check_recordset($res)) {
1116 return $res->data_seek($seek);
1117 } else {
1118 return FALSE;
1119 }
1120 }
1121
1122 /**
1123 * Get the type of the specified field in a result
1124 * mysql_field_type() wrapper function
1125 *
1126 * @param bool|\mysqli_result|object $res MySQLi result object / DBAL object
1127 * @param int $pointer Field index.
1128 * @return string Returns the name of the specified field index, or FALSE on error
1129 */
1130 public function sql_field_type($res, $pointer) {
1131 // mysql_field_type compatibility map
1132 // taken from: http://www.php.net/manual/en/mysqli-result.fetch-field-direct.php#89117
1133 // Constant numbers see http://php.net/manual/en/mysqli.constants.php
1134 $mysql_data_type_hash = array(
1135 1=>'tinyint',
1136 2=>'smallint',
1137 3=>'int',
1138 4=>'float',
1139 5=>'double',
1140 7=>'timestamp',
1141 8=>'bigint',
1142 9=>'mediumint',
1143 10=>'date',
1144 11=>'time',
1145 12=>'datetime',
1146 13=>'year',
1147 16=>'bit',
1148 //252 is currently mapped to all text and blob types (MySQL 5.0.51a)
1149 253=>'varchar',
1150 254=>'char',
1151 246=>'decimal'
1152 );
1153 if ($this->debug_check_recordset($res)) {
1154 $metaInfo = $res->fetch_field_direct($pointer);
1155 if ($metaInfo === FALSE) {
1156 return FALSE;
1157 }
1158 return $mysql_data_type_hash[$metaInfo->type];
1159 } else {
1160 return FALSE;
1161 }
1162 }
1163
1164 /**
1165 * Open a (persistent) connection to a MySQL server
1166 *
1167 * @return bool|void
1168 * @throws \RuntimeException
1169 */
1170 public function sql_pconnect() {
1171 if ($this->isConnected) {
1172 return $this->link;
1173 }
1174
1175 if (!extension_loaded('mysqli')) {
1176 throw new \RuntimeException(
1177 'Database Error: PHP mysqli extension not loaded. This is a must have for TYPO3 CMS!',
1178 1271492607
1179 );
1180 }
1181
1182 $host = $this->persistentDatabaseConnection
1183 ? 'p:' . $this->databaseHost
1184 : $this->databaseHost;
1185
1186 $this->link = mysqli_init();
1187 $connected = $this->link->real_connect(
1188 $host,
1189 $this->databaseUsername,
1190 $this->databaseUserPassword,
1191 NULL,
1192 (int)$this->databasePort,
1193 $this->databaseSocket,
1194 $this->connectionCompression ? MYSQLI_CLIENT_COMPRESS : 0
1195 );
1196
1197 if ($connected) {
1198 $this->isConnected = TRUE;
1199
1200 if ($this->link->set_charset($this->connectionCharset) === FALSE) {
1201 GeneralUtility::sysLog(
1202 'Error setting connection charset to "' . $this->connectionCharset . '"',
1203 'core',
1204 GeneralUtility::SYSLOG_SEVERITY_ERROR
1205 );
1206 }
1207
1208 foreach ($this->initializeCommandsAfterConnect as $command) {
1209 if ($this->query($command) === FALSE) {
1210 GeneralUtility::sysLog(
1211 'Could not initialize DB connection with query "' . $command . '": ' . $this->sql_error(),
1212 'core',
1213 GeneralUtility::SYSLOG_SEVERITY_ERROR
1214 );
1215 }
1216 }
1217 $this->setSqlMode();
1218 $this->checkConnectionCharset();
1219 } else {
1220 // @todo This should raise an exception. Would be useful especially to work during installation.
1221 $error_msg = $this->link->connect_error;
1222 $this->link = NULL;
1223 GeneralUtility::sysLog(
1224 'Could not connect to MySQL server ' . $host . ' with user ' . $this->databaseUsername . ': ' . $error_msg,
1225 'core',
1226 GeneralUtility::SYSLOG_SEVERITY_FATAL
1227 );
1228 }
1229 return $this->link;
1230 }
1231
1232 /**
1233 * Fixes the SQL mode by unsetting NO_BACKSLASH_ESCAPES if found.
1234 *
1235 * @return void
1236 */
1237 protected function setSqlMode() {
1238 $resource = $this->sql_query('SELECT @@SESSION.sql_mode;');
1239 if ($resource) {
1240 $result = $this->sql_fetch_row($resource);
1241 if (isset($result[0]) && $result[0] && strpos($result[0], 'NO_BACKSLASH_ESCAPES') !== FALSE) {
1242 $modes = array_diff(GeneralUtility::trimExplode(',', $result[0]), array('NO_BACKSLASH_ESCAPES'));
1243 $query = 'SET sql_mode=\'' . $this->link->real_escape_string(implode(',', $modes)) . '\';';
1244 $this->sql_query($query);
1245 GeneralUtility::sysLog(
1246 'NO_BACKSLASH_ESCAPES could not be removed from SQL mode: ' . $this->sql_error(),
1247 'core',
1248 GeneralUtility::SYSLOG_SEVERITY_ERROR
1249 );
1250 }
1251 }
1252 }
1253
1254 /**
1255 * Select a SQL database
1256 *
1257 * @return bool Returns TRUE on success or FALSE on failure.
1258 */
1259 public function sql_select_db() {
1260 if (!$this->isConnected) {
1261 $this->connectDB();
1262 }
1263
1264 $ret = $this->link->select_db($this->databaseName);
1265 if (!$ret) {
1266 GeneralUtility::sysLog(
1267 'Could not select MySQL database ' . $this->databaseName . ': ' . $this->sql_error(),
1268 'core',
1269 GeneralUtility::SYSLOG_SEVERITY_FATAL
1270 );
1271 }
1272 return $ret;
1273 }
1274
1275 /**************************************
1276 *
1277 * SQL admin functions
1278 * (For use in the Install Tool and Extension Manager)
1279 *
1280 **************************************/
1281 /**
1282 * Listing databases from current MySQL connection. NOTICE: It WILL try to select those databases and thus break selection of current database.
1283 * This is only used as a service function in the (1-2-3 process) of the Install Tool.
1284 * In any case a lookup should be done in the _DEFAULT handler DBMS then.
1285 * Use in Install Tool only!
1286 *
1287 * @return array Each entry represents a database name
1288 * @throws \RuntimeException
1289 */
1290 public function admin_get_dbs() {
1291 $dbArr = array();
1292 $db_list = $this->query("SELECT SCHEMA_NAME FROM information_schema.SCHEMATA");
1293 if ($db_list === FALSE) {
1294 throw new \RuntimeException(
1295 'MySQL Error: Cannot get tablenames: "' . $this->sql_error() . '"!',
1296 1378457171
1297 );
1298 } else {
1299 while ($row = $db_list->fetch_object()) {
1300 try {
1301 $this->setDatabaseName($row->SCHEMA_NAME);
1302 if ($this->sql_select_db()) {
1303 $dbArr[] = $row->SCHEMA_NAME;
1304 }
1305 } catch (\RuntimeException $exception) {
1306 // The exception happens if we cannot connect to the database
1307 // (usually due to missing permissions). This is ok here.
1308 // We catch the exception, skip the database and continue.
1309 }
1310 }
1311 }
1312 return $dbArr;
1313 }
1314
1315 /**
1316 * Returns the list of tables from the default database, TYPO3_db (quering the DBMS)
1317 * In a DBAL this method should 1) look up all tables from the DBMS of
1318 * the _DEFAULT handler and then 2) add all tables *configured* to be managed by other handlers
1319 *
1320 * @return array Array with tablenames as key and arrays with status information as value
1321 */
1322 public function admin_get_tables() {
1323 $whichTables = array();
1324 $tables_result = $this->query('SHOW TABLE STATUS FROM `' . $this->databaseName . '`');
1325 if ($tables_result !== FALSE) {
1326 while ($theTable = $tables_result->fetch_assoc()) {
1327 $whichTables[$theTable['Name']] = $theTable;
1328 }
1329 $tables_result->free();
1330 }
1331 return $whichTables;
1332 }
1333
1334 /**
1335 * Returns information about each field in the $table (quering the DBMS)
1336 * In a DBAL this should look up the right handler for the table and return compatible information
1337 * This function is important not only for the Install Tool but probably for
1338 * DBALs as well since they might need to look up table specific information
1339 * in order to construct correct queries. In such cases this information should
1340 * probably be cached for quick delivery.
1341 *
1342 * @param string $tableName Table name
1343 * @return array Field information in an associative array with fieldname => field row
1344 */
1345 public function admin_get_fields($tableName) {
1346 $output = array();
1347 $columns_res = $this->query('SHOW FULL COLUMNS FROM `' . $tableName . '`');
1348 if ($columns_res !== FALSE) {
1349 while ($fieldRow = $columns_res->fetch_assoc()) {
1350 $output[$fieldRow['Field']] = $fieldRow;
1351 }
1352 $columns_res->free();
1353 }
1354 return $output;
1355 }
1356
1357 /**
1358 * Returns information about each index key in the $table (quering the DBMS)
1359 * In a DBAL this should look up the right handler for the table and return compatible information
1360 *
1361 * @param string $tableName Table name
1362 * @return array Key information in a numeric array
1363 */
1364 public function admin_get_keys($tableName) {
1365 $output = array();
1366 $keyRes = $this->query('SHOW KEYS FROM `' . $tableName . '`');
1367 if ($keyRes !== FALSE) {
1368 while ($keyRow = $keyRes->fetch_assoc()) {
1369 $output[] = $keyRow;
1370 }
1371 $keyRes->free();
1372 }
1373 return $output;
1374 }
1375
1376 /**
1377 * Returns information about the character sets supported by the current DBM
1378 * This function is important not only for the Install Tool but probably for
1379 * DBALs as well since they might need to look up table specific information
1380 * in order to construct correct queries. In such cases this information should
1381 * probably be cached for quick delivery.
1382 *
1383 * This is used by the Install Tool to convert tables with non-UTF8 charsets
1384 * Use in Install Tool only!
1385 *
1386 * @return array Array with Charset as key and an array of "Charset", "Description", "Default collation", "Maxlen" as values
1387 */
1388 public function admin_get_charsets() {
1389 $output = array();
1390 $columns_res = $this->query('SHOW CHARACTER SET');
1391 if ($columns_res !== FALSE) {
1392 while ($row = $columns_res->fetch_assoc()) {
1393 $output[$row['Charset']] = $row;
1394 }
1395 $columns_res->free();
1396 }
1397 return $output;
1398 }
1399
1400 /**
1401 * mysqli() wrapper function, used by the Install Tool and EM for all queries regarding management of the database!
1402 *
1403 * @param string $query Query to execute
1404 * @return bool|\mysqli_result|object MySQLi result object / DBAL object
1405 */
1406 public function admin_query($query) {
1407 $res = $this->query($query);
1408 if ($this->debugOutput) {
1409 $this->debug('admin_query', $query);
1410 }
1411 return $res;
1412 }
1413
1414 /******************************
1415 *
1416 * Connect handling
1417 *
1418 ******************************/
1419
1420 /**
1421 * Set database host
1422 *
1423 * @param string $host
1424 */
1425 public function setDatabaseHost($host = 'localhost') {
1426 $this->disconnectIfConnected();
1427 $this->databaseHost = $host;
1428 }
1429
1430 /**
1431 * Set database port
1432 *
1433 * @param int $port
1434 */
1435 public function setDatabasePort($port = 3306) {
1436 $this->disconnectIfConnected();
1437 $this->databasePort = (int)$port;
1438 }
1439
1440 /**
1441 * Set database socket
1442 *
1443 * @param string|NULL $socket
1444 */
1445 public function setDatabaseSocket($socket = NULL) {
1446 $this->disconnectIfConnected();
1447 $this->databaseSocket = $socket;
1448 }
1449
1450 /**
1451 * Set database name
1452 *
1453 * @param string $name
1454 */
1455 public function setDatabaseName($name) {
1456 $this->disconnectIfConnected();
1457 $this->databaseName = $name;
1458 }
1459
1460 /**
1461 * Set database username
1462 *
1463 * @param string $username
1464 */
1465 public function setDatabaseUsername($username) {
1466 $this->disconnectIfConnected();
1467 $this->databaseUsername = $username;
1468 }
1469
1470 /**
1471 * Set database password
1472 *
1473 * @param string $password
1474 */
1475 public function setDatabasePassword($password) {
1476 $this->disconnectIfConnected();
1477 $this->databaseUserPassword = $password;
1478 }
1479
1480 /**
1481 * Set persistent database connection
1482 *
1483 * @param bool $persistentDatabaseConnection
1484 * @see http://php.net/manual/de/mysqli.persistconns.php
1485 */
1486 public function setPersistentDatabaseConnection($persistentDatabaseConnection) {
1487 $this->disconnectIfConnected();
1488 $this->persistentDatabaseConnection = (bool)$persistentDatabaseConnection;
1489 }
1490
1491 /**
1492 * Set connection compression. Might be an advantage, if SQL server is not on localhost
1493 *
1494 * @param bool $connectionCompression TRUE if connection should be compressed
1495 */
1496 public function setConnectionCompression($connectionCompression) {
1497 $this->disconnectIfConnected();
1498 $this->connectionCompression = (bool)$connectionCompression;
1499 }
1500
1501 /**
1502 * Set commands to be fired after connection was established
1503 *
1504 * @param array $commands List of SQL commands to be executed after connect
1505 */
1506 public function setInitializeCommandsAfterConnect(array $commands) {
1507 $this->disconnectIfConnected();
1508 $this->initializeCommandsAfterConnect = $commands;
1509 }
1510
1511 /**
1512 * Set the charset that should be used for the MySQL connection.
1513 * The given value will be passed on to mysqli_set_charset().
1514 *
1515 * The default value of this setting is utf8.
1516 *
1517 * @param string $connectionCharset The connection charset that will be passed on to mysqli_set_charset() when connecting the database. Default is utf8.
1518 * @return void
1519 */
1520 public function setConnectionCharset($connectionCharset = 'utf8') {
1521 $this->disconnectIfConnected();
1522 $this->connectionCharset = $connectionCharset;
1523 }
1524
1525 /**
1526 * Connects to database for TYPO3 sites:
1527 *
1528 * @throws \RuntimeException
1529 * @throws \UnexpectedValueException
1530 * @return void
1531 */
1532 public function connectDB() {
1533 // Early return if connected already
1534 if ($this->isConnected) {
1535 return;
1536 }
1537
1538 if (!$this->databaseName) {
1539 throw new \RuntimeException(
1540 'TYPO3 Fatal Error: No database selected!',
1541 1270853882
1542 );
1543 }
1544
1545 if ($this->sql_pconnect()) {
1546 if (!$this->sql_select_db()) {
1547 throw new \RuntimeException(
1548 'TYPO3 Fatal Error: Cannot connect to the current database, "' . $this->databaseName . '"!',
1549 1270853883
1550 );
1551 }
1552 } else {
1553 throw new \RuntimeException(
1554 'TYPO3 Fatal Error: The current username, password or host was not accepted when the connection to the database was attempted to be established!',
1555 1270853884
1556 );
1557 }
1558
1559 // Prepare user defined objects (if any) for hooks which extend query methods
1560 $this->preProcessHookObjects = array();
1561 $this->postProcessHookObjects = array();
1562 if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_db.php']['queryProcessors'])) {
1563 foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_db.php']['queryProcessors'] as $classRef) {
1564 $hookObject = GeneralUtility::getUserObj($classRef);
1565 if (!(
1566 $hookObject instanceof PreProcessQueryHookInterface
1567 || $hookObject instanceof PostProcessQueryHookInterface
1568 )) {
1569 throw new \UnexpectedValueException(
1570 '$hookObject must either implement interface TYPO3\\CMS\\Core\\Database\\PreProcessQueryHookInterface or interface TYPO3\\CMS\\Core\\Database\\PostProcessQueryHookInterface',
1571 1299158548
1572 );
1573 }
1574 if ($hookObject instanceof PreProcessQueryHookInterface) {
1575 $this->preProcessHookObjects[] = $hookObject;
1576 }
1577 if ($hookObject instanceof PostProcessQueryHookInterface) {
1578 $this->postProcessHookObjects[] = $hookObject;
1579 }
1580 }
1581 }
1582 }
1583
1584 /**
1585 * Checks if database is connected
1586 *
1587 * @return bool
1588 */
1589 public function isConnected() {
1590 // We think we're still connected
1591 if ($this->isConnected) {
1592 // Check if this is really the case or if the database server has gone away for some reason
1593 $this->isConnected = $this->link->ping();
1594 }
1595 return $this->isConnected;
1596 }
1597
1598 /**
1599 * Checks if the current connection character set has the same value
1600 * as the connectionCharset variable.
1601 *
1602 * To determine the character set these MySQL session variables are
1603 * checked: character_set_client, character_set_results and
1604 * character_set_connection.
1605 *
1606 * If the character set does not match or if the session variables
1607 * can not be read a RuntimeException is thrown.
1608 *
1609 * @return void
1610 * @throws \RuntimeException
1611 */
1612 protected function checkConnectionCharset() {
1613 $sessionResult = $this->sql_query('SHOW SESSION VARIABLES LIKE \'character_set%\'');
1614
1615 if ($sessionResult === FALSE) {
1616 GeneralUtility::sysLog(
1617 'Error while retrieving the current charset session variables from the database: ' . $this->sql_error(),
1618 'core',
1619 GeneralUtility::SYSLOG_SEVERITY_ERROR
1620 );
1621 throw new \RuntimeException(
1622 'TYPO3 Fatal Error: Could not determine the current charset of the database.',
1623 1381847136
1624 );
1625 }
1626
1627 $charsetVariables = array();
1628 while (($row = $this->sql_fetch_row($sessionResult)) !== FALSE) {
1629 $variableName = $row[0];
1630 $variableValue = $row[1];
1631 $charsetVariables[$variableName] = $variableValue;
1632 }
1633 $this->sql_free_result($sessionResult);
1634
1635 // These variables are set with the "Set names" command which was
1636 // used in the past. This is why we check them.
1637 $charsetRequiredVariables = array(
1638 'character_set_client',
1639 'character_set_results',
1640 'character_set_connection',
1641 );
1642
1643 $hasValidCharset = TRUE;
1644 foreach ($charsetRequiredVariables as $variableName) {
1645 if (empty($charsetVariables[$variableName])) {
1646 GeneralUtility::sysLog(
1647 'A required session variable is missing in the current MySQL connection: ' . $variableName,
1648 'core',
1649 GeneralUtility::SYSLOG_SEVERITY_ERROR
1650 );
1651 throw new \RuntimeException(
1652 'TYPO3 Fatal Error: Could not determine the value of the database session variable: ' . $variableName,
1653 1381847779
1654 );
1655 }
1656
1657 if ($charsetVariables[$variableName] !== $this->connectionCharset) {
1658 $hasValidCharset = FALSE;
1659 break;
1660 }
1661 }
1662
1663 if (!$hasValidCharset) {
1664 throw new \RuntimeException(
1665 'It looks like the character set ' . $this->connectionCharset . ' is not used for this connection even though it is configured as connection charset. ' .
1666 'This TYPO3 installation is using the $GLOBALS[\'TYPO3_CONF_VARS\'][\'SYS\'][\'setDBinit\'] property with the following value: "' .
1667 $GLOBALS['TYPO3_CONF_VARS']['SYS']['setDBinit'] . '". Please make sure that this command does not overwrite the configured charset. ' .
1668 'Please note that for the TYPO3 database everything other than utf8 is unsupported since version 4.7.',
1669 1389697515
1670 );
1671 }
1672 }
1673
1674 /**
1675 * Disconnect from database if connected
1676 *
1677 * @return void
1678 */
1679 protected function disconnectIfConnected() {
1680 if ($this->isConnected) {
1681 $this->link->close();
1682 $this->isConnected = FALSE;
1683 }
1684 }
1685
1686 /**
1687 * Returns current database handle
1688 *
1689 * @return \mysqli|NULL
1690 */
1691 public function getDatabaseHandle() {
1692 return $this->link;
1693 }
1694
1695 /**
1696 * Set current database handle, usually \mysqli
1697 *
1698 * @param \mysqli $handle
1699 */
1700 public function setDatabaseHandle($handle) {
1701 $this->link = $handle;
1702 }
1703
1704 /**
1705 * Get the MySQL server version
1706 *
1707 * @return string
1708 */
1709 public function getServerVersion() {
1710 return $this->link->server_info;
1711 }
1712
1713 /******************************
1714 *
1715 * Debugging
1716 *
1717 ******************************/
1718 /**
1719 * Debug function: Outputs error if any
1720 *
1721 * @param string $func Function calling debug()
1722 * @param string $query Last query if not last built query
1723 * @return void
1724 */
1725 public function debug($func, $query = '') {
1726 $error = $this->sql_error();
1727 if ($error || (int)$this->debugOutput === 2) {
1728 \TYPO3\CMS\Core\Utility\DebugUtility::debug(
1729 array(
1730 'caller' => \TYPO3\CMS\Core\Database\DatabaseConnection::class . '::' . $func,
1731 'ERROR' => $error,
1732 'lastBuiltQuery' => $query ? $query : $this->debug_lastBuiltQuery,
1733 'debug_backtrace' => \TYPO3\CMS\Core\Utility\DebugUtility::debugTrail()
1734 ),
1735 $func,
1736 is_object($GLOBALS['error']) && @is_callable(array($GLOBALS['error'], 'debug'))
1737 ? ''
1738 : 'DB Error'
1739 );
1740 }
1741 }
1742
1743 /**
1744 * Checks if record set is valid and writes debugging information into devLog if not.
1745 *
1746 * @param bool|\mysqli_result|object MySQLi result object / DBAL object
1747 * @return bool TRUE if the record set is valid, FALSE otherwise
1748 */
1749 public function debug_check_recordset($res) {
1750 if ($res !== FALSE) {
1751 return TRUE;
1752 }
1753 $msg = 'Invalid database result detected';
1754 $trace = debug_backtrace();
1755 array_shift($trace);
1756 $cnt = count($trace);
1757 for ($i = 0; $i < $cnt; $i++) {
1758 // Complete objects are too large for the log
1759 if (isset($trace['object'])) {
1760 unset($trace['object']);
1761 }
1762 }
1763 $msg .= ': function TYPO3\\CMS\\Core\\Database\\DatabaseConnection->' . $trace[0]['function'] . ' called from file ' . substr($trace[0]['file'], (strlen(PATH_site) + 2)) . ' in line ' . $trace[0]['line'];
1764 GeneralUtility::sysLog(
1765 $msg . '. Use a devLog extension to get more details.',
1766 'core',
1767 GeneralUtility::SYSLOG_SEVERITY_ERROR
1768 );
1769 // Send to devLog if enabled
1770 if (TYPO3_DLOG) {
1771 $debugLogData = array(
1772 'SQL Error' => $this->sql_error(),
1773 'Backtrace' => $trace
1774 );
1775 if ($this->debug_lastBuiltQuery) {
1776 $debugLogData = array('SQL Query' => $this->debug_lastBuiltQuery) + $debugLogData;
1777 }
1778 GeneralUtility::devLog($msg . '.', 'Core/t3lib_db', 3, $debugLogData);
1779 }
1780 return FALSE;
1781 }
1782
1783 /**
1784 * Explain select queries
1785 * If $this->explainOutput is set, SELECT queries will be explained here. Only queries with more than one possible result row will be displayed.
1786 * The output is either printed as raw HTML output or embedded into the TS admin panel (checkbox must be enabled!)
1787 *
1788 * @todo Feature is not DBAL-compliant
1789 *
1790 * @param string $query SQL query
1791 * @param string $from_table Table(s) from which to select. This is what comes right after "FROM ...". Required value.
1792 * @param int $row_count Number of resulting rows
1793 * @return bool TRUE if explain was run, FALSE otherwise
1794 */
1795 protected function explain($query, $from_table, $row_count) {
1796 $debugAllowedForIp = GeneralUtility::cmpIP(
1797 GeneralUtility::getIndpEnv('REMOTE_ADDR'),
1798 $GLOBALS['TYPO3_CONF_VARS']['SYS']['devIPmask']
1799 );
1800 if (
1801 (int)$this->explainOutput == 1
1802 || ((int)$this->explainOutput == 2 && $debugAllowedForIp)
1803 ) {
1804 // Raw HTML output
1805 $explainMode = 1;
1806 } elseif ((int)$this->explainOutput == 3 && is_object($GLOBALS['TT'])) {
1807 // Embed the output into the TS admin panel
1808 $explainMode = 2;
1809 } else {
1810 return FALSE;
1811 }
1812 $error = $this->sql_error();
1813 $trail = \TYPO3\CMS\Core\Utility\DebugUtility::debugTrail();
1814 $explain_tables = array();
1815 $explain_output = array();
1816 $res = $this->sql_query('EXPLAIN ' . $query, $this->link);
1817 if (is_a($res, '\\mysqli_result')) {
1818 while ($tempRow = $this->sql_fetch_assoc($res)) {
1819 $explain_output[] = $tempRow;
1820 $explain_tables[] = $tempRow['table'];
1821 }
1822 $this->sql_free_result($res);
1823 }
1824 $indices_output = array();
1825 // Notice: Rows are skipped if there is only one result, or if no conditions are set
1826 if (
1827 $explain_output[0]['rows'] > 1
1828 || GeneralUtility::inList('ALL', $explain_output[0]['type'])
1829 ) {
1830 // Only enable output if it's really useful
1831 $debug = TRUE;
1832 foreach ($explain_tables as $table) {
1833 $tableRes = $this->sql_query('SHOW TABLE STATUS LIKE \'' . $table . '\'');
1834 $isTable = $this->sql_num_rows($tableRes);
1835 if ($isTable) {
1836 $res = $this->sql_query('SHOW INDEX FROM ' . $table, $this->link);
1837 if (is_a($res, '\\mysqli_result')) {
1838 while ($tempRow = $this->sql_fetch_assoc($res)) {
1839 $indices_output[] = $tempRow;
1840 }
1841 $this->sql_free_result($res);
1842 }
1843 }
1844 $this->sql_free_result($tableRes);
1845 }
1846 } else {
1847 $debug = FALSE;
1848 }
1849 if ($debug) {
1850 if ($explainMode) {
1851 $data = array();
1852 $data['query'] = $query;
1853 $data['trail'] = $trail;
1854 $data['row_count'] = $row_count;
1855 if ($error) {
1856 $data['error'] = $error;
1857 }
1858 if (!empty($explain_output)) {
1859 $data['explain'] = $explain_output;
1860 }
1861 if (!empty($indices_output)) {
1862 $data['indices'] = $indices_output;
1863 }
1864 if ($explainMode == 1) {
1865 \TYPO3\CMS\Core\Utility\DebugUtility::debug($data, 'Tables: ' . $from_table, 'DB SQL EXPLAIN');
1866 } elseif ($explainMode == 2) {
1867 $GLOBALS['TT']->setTSselectQuery($data);
1868 }
1869 }
1870 return TRUE;
1871 }
1872 return FALSE;
1873 }
1874
1875 /**
1876 * Serialize destructs current connection
1877 *
1878 * @return array All protected properties that should be saved
1879 */
1880 public function __sleep() {
1881 $this->disconnectIfConnected();
1882 return array(
1883 'debugOutput',
1884 'explainOutput',
1885 'databaseHost',
1886 'databasePort',
1887 'databaseSocket',
1888 'databaseName',
1889 'databaseUsername',
1890 'databaseUserPassword',
1891 'persistentDatabaseConnection',
1892 'connectionCompression',
1893 'initializeCommandsAfterConnect',
1894 'default_charset',
1895 );
1896 }
1897
1898 }