TYPO3 CMS  TYPO3_6-2
DatabaseConnection.php
Go to the documentation of this file.
1 <?php
3 
18 
47 
53  const AND_Constraint = 'AND';
54 
60  const OR_Constraint = 'OR';
61 
62  // Set "TRUE" or "1" if you want database errors outputted. Set to "2" if you also want successful database actions outputted.
66  public $debugOutput = FALSE;
67 
68  // Internally: Set to last built query (not necessarily executed...)
72  public $debug_lastBuiltQuery = '';
73 
74  // Set "TRUE" if you want the last built query to be stored in $debug_lastBuiltQuery independent of $this->debugOutput
78  public $store_lastBuiltQuery = FALSE;
79 
80  // Set this to 1 to get queries explained (devIPmask must match). Set the value to 2 to the same but disregarding the devIPmask.
81  // 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.
85  public $explainOutput = 0;
86 
90  protected $databaseHost = '';
91 
95  protected $databasePort = 3306;
96 
100  protected $databaseSocket = NULL;
101 
105  protected $databaseName = '';
106 
110  protected $databaseUsername = '';
111 
115  protected $databaseUserPassword = '';
116 
121  protected $persistentDatabaseConnection = FALSE;
122 
126  protected $connectionCompression = FALSE;
127 
134  protected $connectionCharset = 'utf8';
135 
139  protected $initializeCommandsAfterConnect = array();
140 
144  protected $isConnected = FALSE;
145 
149  protected $link = NULL;
150 
151  // Default character set, applies unless character set or collation are explicitly set
155  public $default_charset = 'utf8';
156 
160  protected $preProcessHookObjects = array();
161 
165  protected $postProcessHookObjects = array();
166 
167 
173  static protected $dateTimeFormats = array(
174  'date' => array(
175  'empty' => '0000-00-00',
176  'format' => 'Y-m-d'
177  ),
178  'datetime' => array(
179  'empty' => '0000-00-00 00:00:00',
180  'format' => 'Y-m-d H:i:s'
181  )
182  );
183 
189  public function initialize() {
190  // Intentionally blank as this will be overloaded by DBAL
191  }
192 
193  /************************************
194  *
195  * Query execution
196  *
197  * These functions are the RECOMMENDED DBAL functions for use in your applications
198  * Using these functions will allow the DBAL to use alternative ways of accessing data (contrary to if a query is returned!)
199  * They compile a query AND execute it immediately and then return the result
200  * This principle heightens our ability to create various forms of DBAL of the functions.
201  * Generally: We want to return a result pointer/object, never queries.
202  * Also, having the table name together with the actual query execution allows us to direct the request to other databases.
203  *
204  **************************************/
205 
215  public function exec_INSERTquery($table, $fields_values, $no_quote_fields = FALSE) {
216  $res = $this->query($this->INSERTquery($table, $fields_values, $no_quote_fields));
217  if ($this->debugOutput) {
218  $this->debug('exec_INSERTquery');
219  }
220  foreach ($this->postProcessHookObjects as $hookObject) {
222  $hookObject->exec_INSERTquery_postProcessAction($table, $fields_values, $no_quote_fields, $this);
223  }
224  return $res;
225  }
226 
236  public function exec_INSERTmultipleRows($table, array $fields, array $rows, $no_quote_fields = FALSE) {
237  $res = $this->query($this->INSERTmultipleRows($table, $fields, $rows, $no_quote_fields));
238  if ($this->debugOutput) {
239  $this->debug('exec_INSERTmultipleRows');
240  }
241  foreach ($this->postProcessHookObjects as $hookObject) {
243  $hookObject->exec_INSERTmultipleRows_postProcessAction($table, $fields, $rows, $no_quote_fields, $this);
244  }
245  return $res;
246  }
247 
258  public function exec_UPDATEquery($table, $where, $fields_values, $no_quote_fields = FALSE) {
259  $res = $this->query($this->UPDATEquery($table, $where, $fields_values, $no_quote_fields));
260  if ($this->debugOutput) {
261  $this->debug('exec_UPDATEquery');
262  }
263  foreach ($this->postProcessHookObjects as $hookObject) {
265  $hookObject->exec_UPDATEquery_postProcessAction($table, $where, $fields_values, $no_quote_fields, $this);
266  }
267  return $res;
268  }
269 
277  public function exec_DELETEquery($table, $where) {
278  $res = $this->query($this->DELETEquery($table, $where));
279  if ($this->debugOutput) {
280  $this->debug('exec_DELETEquery');
281  }
282  foreach ($this->postProcessHookObjects as $hookObject) {
284  $hookObject->exec_DELETEquery_postProcessAction($table, $where, $this);
285  }
286  return $res;
287  }
288 
301  public function exec_SELECTquery($select_fields, $from_table, $where_clause, $groupBy = '', $orderBy = '', $limit = '') {
302  $query = $this->SELECTquery($select_fields, $from_table, $where_clause, $groupBy, $orderBy, $limit);
303  $res = $this->query($query);
304  if ($this->debugOutput) {
305  $this->debug('exec_SELECTquery');
306  }
307  if ($this->explainOutput) {
308  $this->explain($query, $from_table, $res->num_rows);
309  }
310  foreach ($this->postProcessHookObjects as $hookObject) {
312  $hookObject->exec_SELECTquery_postProcessAction($select_fields, $from_table, $where_clause, $groupBy = '', $orderBy = '', $limit = '', $this);
313  }
314  return $res;
315  }
316 
334  public function exec_SELECT_mm_query($select, $local_table, $mm_table, $foreign_table, $whereClause = '', $groupBy = '', $orderBy = '', $limit = '') {
335  $foreign_table_as = $foreign_table == $local_table ? $foreign_table . str_replace('.', '', uniqid('_join', TRUE)) : '';
336  $mmWhere = $local_table ? $local_table . '.uid=' . $mm_table . '.uid_local' : '';
337  $mmWhere .= ($local_table and $foreign_table) ? ' AND ' : '';
338  $tables = ($local_table ? $local_table . ',' : '') . $mm_table;
339  if ($foreign_table) {
340  $mmWhere .= ($foreign_table_as ?: $foreign_table) . '.uid=' . $mm_table . '.uid_foreign';
341  $tables .= ',' . $foreign_table . ($foreign_table_as ? ' AS ' . $foreign_table_as : '');
342  }
343  return $this->exec_SELECTquery($select, $tables, $mmWhere . ' ' . $whereClause, $groupBy, $orderBy, $limit);
344  }
345 
353  public function exec_SELECT_queryArray($queryParts) {
354  return $this->exec_SELECTquery($queryParts['SELECT'], $queryParts['FROM'], $queryParts['WHERE'], $queryParts['GROUPBY'], $queryParts['ORDERBY'], $queryParts['LIMIT']);
355  }
356 
369  public function exec_SELECTgetRows($select_fields, $from_table, $where_clause, $groupBy = '', $orderBy = '', $limit = '', $uidIndexField = '') {
370  $res = $this->exec_SELECTquery($select_fields, $from_table, $where_clause, $groupBy, $orderBy, $limit);
371  if ($this->debugOutput) {
372  $this->debug('exec_SELECTquery');
373  }
374  if (!$this->sql_error()) {
375  $output = array();
376  if ($uidIndexField) {
377  while ($tempRow = $this->sql_fetch_assoc($res)) {
378  $output[$tempRow[$uidIndexField]] = $tempRow;
379  }
380  } else {
381  while ($output[] = $this->sql_fetch_assoc($res)) {
382 
383  }
384  array_pop($output);
385  }
386  $this->sql_free_result($res);
387  } else {
388  $output = NULL;
389  }
390  return $output;
391  }
392 
405  public function exec_SELECTgetSingleRow($select_fields, $from_table, $where_clause, $groupBy = '', $orderBy = '', $numIndex = FALSE) {
406  $res = $this->exec_SELECTquery($select_fields, $from_table, $where_clause, $groupBy, $orderBy, '1');
407  if ($this->debugOutput) {
408  $this->debug('exec_SELECTquery');
409  }
410  $output = NULL;
411  if ($res !== FALSE) {
412  if ($numIndex) {
413  $output = $this->sql_fetch_row($res);
414  } else {
415  $output = $this->sql_fetch_assoc($res);
416  }
417  $this->sql_free_result($res);
418  }
419  return $output;
420  }
421 
430  public function exec_SELECTcountRows($field, $table, $where = '1=1') {
431  $count = FALSE;
432  $resultSet = $this->exec_SELECTquery('COUNT(' . $field . ')', $table, $where);
433  if ($resultSet !== FALSE) {
434  list($count) = $this->sql_fetch_row($resultSet);
435  $count = (int)$count;
436  $this->sql_free_result($resultSet);
437  }
438  return $count;
439  }
440 
447  public function exec_TRUNCATEquery($table) {
448  $res = $this->query($this->TRUNCATEquery($table));
449  if ($this->debugOutput) {
450  $this->debug('exec_TRUNCATEquery');
451  }
452  foreach ($this->postProcessHookObjects as $hookObject) {
454  $hookObject->exec_TRUNCATEquery_postProcessAction($table, $this);
455  }
456  return $res;
457  }
458 
466  protected function query($query) {
467  if (!$this->isConnected) {
468  $this->connectDB();
469  }
470  return $this->link->query($query);
471  }
472 
473  /**************************************
474  *
475  * Query building
476  *
477  **************************************/
486  public function INSERTquery($table, $fields_values, $no_quote_fields = FALSE) {
487  // Table and fieldnames should be "SQL-injection-safe" when supplied to this
488  // function (contrary to values in the arrays which may be insecure).
489  if (!is_array($fields_values) || count($fields_values) === 0) {
490  return NULL;
491  }
492  foreach ($this->preProcessHookObjects as $hookObject) {
493  $hookObject->INSERTquery_preProcessAction($table, $fields_values, $no_quote_fields, $this);
494  }
495  // Quote and escape values
496  $fields_values = $this->fullQuoteArray($fields_values, $table, $no_quote_fields, TRUE);
497  // Build query
498  $query = 'INSERT INTO ' . $table . ' (' . implode(',', array_keys($fields_values)) . ') VALUES ' . '(' . implode(',', $fields_values) . ')';
499  // Return query
500  if ($this->debugOutput || $this->store_lastBuiltQuery) {
501  $this->debug_lastBuiltQuery = $query;
502  }
503  return $query;
504  }
505 
515  public function INSERTmultipleRows($table, array $fields, array $rows, $no_quote_fields = FALSE) {
516  // Table and fieldnames should be "SQL-injection-safe" when supplied to this
517  // function (contrary to values in the arrays which may be insecure).
518  if (count($rows) === 0) {
519  return NULL;
520  }
521  foreach ($this->preProcessHookObjects as $hookObject) {
523  $hookObject->INSERTmultipleRows_preProcessAction($table, $fields, $rows, $no_quote_fields, $this);
524  }
525  // Build query
526  $query = 'INSERT INTO ' . $table . ' (' . implode(', ', $fields) . ') VALUES ';
527  $rowSQL = array();
528  foreach ($rows as $row) {
529  // Quote and escape values
530  $row = $this->fullQuoteArray($row, $table, $no_quote_fields);
531  $rowSQL[] = '(' . implode(', ', $row) . ')';
532  }
533  $query .= implode(', ', $rowSQL);
534  // Return query
535  if ($this->debugOutput || $this->store_lastBuiltQuery) {
536  $this->debug_lastBuiltQuery = $query;
537  }
538  return $query;
539  }
540 
552  public function UPDATEquery($table, $where, $fields_values, $no_quote_fields = FALSE) {
553  // Table and fieldnames should be "SQL-injection-safe" when supplied to this
554  // function (contrary to values in the arrays which may be insecure).
555  if (is_string($where)) {
556  foreach ($this->preProcessHookObjects as $hookObject) {
558  $hookObject->UPDATEquery_preProcessAction($table, $where, $fields_values, $no_quote_fields, $this);
559  }
560  $fields = array();
561  if (is_array($fields_values) && count($fields_values)) {
562  // Quote and escape values
563  $nArr = $this->fullQuoteArray($fields_values, $table, $no_quote_fields, TRUE);
564  foreach ($nArr as $k => $v) {
565  $fields[] = $k . '=' . $v;
566  }
567  }
568  // Build query
569  $query = 'UPDATE ' . $table . ' SET ' . implode(',', $fields) . ((string)$where !== '' ? ' WHERE ' . $where : '');
570  if ($this->debugOutput || $this->store_lastBuiltQuery) {
571  $this->debug_lastBuiltQuery = $query;
572  }
573  return $query;
574  } else {
575  throw new \InvalidArgumentException('TYPO3 Fatal Error: "Where" clause argument for UPDATE query was not a string in $this->UPDATEquery() !', 1270853880);
576  }
577  }
578 
587  public function DELETEquery($table, $where) {
588  if (is_string($where)) {
589  foreach ($this->preProcessHookObjects as $hookObject) {
591  $hookObject->DELETEquery_preProcessAction($table, $where, $this);
592  }
593  // Table and fieldnames should be "SQL-injection-safe" when supplied to this function
594  $query = 'DELETE FROM ' . $table . ((string)$where !== '' ? ' WHERE ' . $where : '');
595  if ($this->debugOutput || $this->store_lastBuiltQuery) {
596  $this->debug_lastBuiltQuery = $query;
597  }
598  return $query;
599  } else {
600  throw new \InvalidArgumentException('TYPO3 Fatal Error: "Where" clause argument for DELETE query was not a string in $this->DELETEquery() !', 1270853881);
601  }
602  }
603 
615  public function SELECTquery($select_fields, $from_table, $where_clause, $groupBy = '', $orderBy = '', $limit = '') {
616  foreach ($this->preProcessHookObjects as $hookObject) {
618  $hookObject->SELECTquery_preProcessAction($select_fields, $from_table, $where_clause, $groupBy, $orderBy, $limit, $this);
619  }
620  // Table and fieldnames should be "SQL-injection-safe" when supplied to this function
621  // Build basic query
622  $query = 'SELECT ' . $select_fields . ' FROM ' . $from_table . ((string)$where_clause !== '' ? ' WHERE ' . $where_clause : '');
623  // Group by
624  $query .= (string)$groupBy !== '' ? ' GROUP BY ' . $groupBy : '';
625  // Order by
626  $query .= (string)$orderBy !== '' ? ' ORDER BY ' . $orderBy : '';
627  // Group by
628  $query .= (string)$limit !== '' ? ' LIMIT ' . $limit : '';
629  // Return query
630  if ($this->debugOutput || $this->store_lastBuiltQuery) {
631  $this->debug_lastBuiltQuery = $query;
632  }
633  return $query;
634  }
635 
645  public function SELECTsubquery($select_fields, $from_table, $where_clause) {
646  // Table and fieldnames should be "SQL-injection-safe" when supplied to this function
647  // Build basic query:
648  $query = 'SELECT ' . $select_fields . ' FROM ' . $from_table . ((string)$where_clause !== '' ? ' WHERE ' . $where_clause : '');
649  // Return query
650  if ($this->debugOutput || $this->store_lastBuiltQuery) {
651  $this->debug_lastBuiltQuery = $query;
652  }
653  return $query;
654  }
655 
662  public function TRUNCATEquery($table) {
663  foreach ($this->preProcessHookObjects as $hookObject) {
665  $hookObject->TRUNCATEquery_preProcessAction($table, $this);
666  }
667  // Table should be "SQL-injection-safe" when supplied to this function
668  // Build basic query:
669  $query = 'TRUNCATE TABLE ' . $table;
670  // Return query:
671  if ($this->debugOutput || $this->store_lastBuiltQuery) {
672  $this->debug_lastBuiltQuery = $query;
673  }
674  return $query;
675  }
676 
692  public function listQuery($field, $value, $table) {
693  $value = (string)$value;
694  if (strpos($value, ',') !== FALSE) {
695  throw new \InvalidArgumentException('$value must not contain a comma (,) in $this->listQuery() !', 1294585862);
696  }
697  $pattern = $this->quoteStr($value, $table);
698  $where = 'FIND_IN_SET(\'' . $pattern . '\',' . $field . ')';
699  return $where;
700  }
701 
711  public function searchQuery($searchWords, $fields, $table, $constraint = self::AND_Constraint) {
712  switch ($constraint) {
713  case self::OR_Constraint:
714  $constraint = 'OR';
715  break;
716  default:
717  $constraint = 'AND';
718  }
719 
720  $queryParts = array();
721  foreach ($searchWords as $sw) {
722  $like = ' LIKE \'%' . $this->quoteStr($sw, $table) . '%\'';
723  $queryParts[] = $table . '.' . implode(($like . ' OR ' . $table . '.'), $fields) . $like;
724  }
725  $query = '(' . implode(') ' . $constraint . ' (', $queryParts) . ')';
726 
727  return $query;
728  }
729 
730  /**************************************
731  *
732  * Prepared Query Support
733  *
734  **************************************/
747  public function prepare_SELECTquery($select_fields, $from_table, $where_clause, $groupBy = '', $orderBy = '', $limit = '', array $input_parameters = array()) {
748  $query = $this->SELECTquery($select_fields, $from_table, $where_clause, $groupBy, $orderBy, $limit);
750  $preparedStatement = GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Database\\PreparedStatement', $query, $from_table, array());
751  // Bind values to parameters
752  foreach ($input_parameters as $key => $value) {
753  $preparedStatement->bindValue($key, $value, PreparedStatement::PARAM_AUTOTYPE);
754  }
755  // Return prepared statement
756  return $preparedStatement;
757  }
758 
766  public function prepare_SELECTqueryArray(array $queryParts, array $input_parameters = array()) {
767  return $this->prepare_SELECTquery($queryParts['SELECT'], $queryParts['FROM'], $queryParts['WHERE'], $queryParts['GROUPBY'], $queryParts['ORDERBY'], $queryParts['LIMIT'], $input_parameters);
768  }
769 
778  public function prepare_PREPAREDquery($query, array $queryComponents) {
779  if (!$this->isConnected) {
780  $this->connectDB();
781  }
782  $stmt = $this->link->stmt_init();
783  $success = $stmt->prepare($query);
784  if ($this->debugOutput) {
785  $this->debug('stmt_execute', $query);
786  }
787  return $success ? $stmt : NULL;
788  }
789 
790  /**************************************
791  *
792  * Various helper functions
793  *
794  * Functions recommended to be used for
795  * - escaping values,
796  * - cleaning lists of values,
797  * - stripping of excess ORDER BY/GROUP BY keywords
798  *
799  **************************************/
809  public function fullQuoteStr($str, $table, $allowNull = FALSE) {
810  if (!$this->isConnected) {
811  $this->connectDB();
812  }
813  if ($allowNull && $str === NULL) {
814  return 'NULL';
815  }
816 
817  return '\'' . $this->link->real_escape_string($str) . '\'';
818  }
819 
830  public function fullQuoteArray($arr, $table, $noQuote = FALSE, $allowNull = FALSE) {
831  if (is_string($noQuote)) {
832  $noQuote = explode(',', $noQuote);
833  } elseif (!is_array($noQuote)) {
834  $noQuote = FALSE;
835  }
836  foreach ($arr as $k => $v) {
837  if ($noQuote === FALSE || !in_array($k, $noQuote)) {
838  $arr[$k] = $this->fullQuoteStr($v, $table, $allowNull);
839  }
840  }
841  return $arr;
842  }
843 
854  public function quoteStr($str, $table) {
855  if (!$this->isConnected) {
856  $this->connectDB();
857  }
858  return $this->link->real_escape_string($str);
859  }
860 
869  public function escapeStrForLike($str, $table) {
870  return addcslashes($str, '_%');
871  }
872 
881  public function cleanIntArray($arr) {
882  return array_map('intval', $arr);
883  }
884 
893  public function cleanIntList($list) {
894  return implode(',', GeneralUtility::intExplode(',', $list));
895  }
896 
906  public function stripOrderBy($str) {
907  return preg_replace('/^(?:ORDER[[:space:]]*BY[[:space:]]*)+/i', '', trim($str));
908  }
909 
919  public function stripGroupBy($str) {
920  return preg_replace('/^(?:GROUP[[:space:]]*BY[[:space:]]*)+/i', '', trim($str));
921  }
922 
930  public function splitGroupOrderLimit($str) {
931  // Prepending a space to make sure "[[:space:]]+" will find a space there
932  // for the first element.
933  $str = ' ' . $str;
934  // Init output array:
935  $wgolParts = array(
936  'WHERE' => '',
937  'GROUPBY' => '',
938  'ORDERBY' => '',
939  'LIMIT' => ''
940  );
941  // Find LIMIT
942  $reg = array();
943  if (preg_match('/^(.*)[[:space:]]+LIMIT[[:space:]]+([[:alnum:][:space:],._]+)$/i', $str, $reg)) {
944  $wgolParts['LIMIT'] = trim($reg[2]);
945  $str = $reg[1];
946  }
947  // Find ORDER BY
948  $reg = array();
949  if (preg_match('/^(.*)[[:space:]]+ORDER[[:space:]]+BY[[:space:]]+([[:alnum:][:space:],._]+)$/i', $str, $reg)) {
950  $wgolParts['ORDERBY'] = trim($reg[2]);
951  $str = $reg[1];
952  }
953  // Find GROUP BY
954  $reg = array();
955  if (preg_match('/^(.*)[[:space:]]+GROUP[[:space:]]+BY[[:space:]]+([[:alnum:][:space:],._]+)$/i', $str, $reg)) {
956  $wgolParts['GROUPBY'] = trim($reg[2]);
957  $str = $reg[1];
958  }
959  // Rest is assumed to be "WHERE" clause
960  $wgolParts['WHERE'] = $str;
961  return $wgolParts;
962  }
963 
970  public function getDateTimeFormats($table) {
971  return self::$dateTimeFormats;
972  }
973 
974  /**************************************
975  *
976  * MySQL(i) wrapper functions
977  * (For use in your applications)
978  *
979  **************************************/
989  public function sql_query($query) {
990  $res = $this->query($query);
991  if ($this->debugOutput) {
992  $this->debug('sql_query', $query);
993  }
994  return $res;
995  }
996 
1002  public function sql_error() {
1003  return $this->link->error;
1004  }
1005 
1011  public function sql_errno() {
1012  return $this->link->errno;
1013  }
1014 
1021  public function sql_num_rows($res) {
1022  if ($this->debug_check_recordset($res)) {
1023  return $res->num_rows;
1024  } else {
1025  return FALSE;
1026  }
1027  }
1028 
1036  public function sql_fetch_assoc($res) {
1037  if ($this->debug_check_recordset($res)) {
1038  $result = $res->fetch_assoc();
1039  if ($result === NULL) {
1040  // Needed for compatibility
1041  $result = FALSE;
1042  }
1043  return $result;
1044  } else {
1045  return FALSE;
1046  }
1047  }
1048 
1057  public function sql_fetch_row($res) {
1058  if ($this->debug_check_recordset($res)) {
1059  $result = $res->fetch_row();
1060  if ($result === NULL) {
1061  // Needed for compatibility
1062  $result = FALSE;
1063  }
1064  return $result;
1065  } else {
1066  return FALSE;
1067  }
1068  }
1069 
1077  public function sql_free_result($res) {
1078  if ($this->debug_check_recordset($res) && is_object($res)) {
1079  $res->free();
1080  return TRUE;
1081  } else {
1082  return FALSE;
1083  }
1084  }
1085 
1091  public function sql_insert_id() {
1092  return $this->link->insert_id;
1093  }
1094 
1100  public function sql_affected_rows() {
1101  return $this->link->affected_rows;
1102  }
1103 
1111  public function sql_data_seek($res, $seek) {
1112  if ($this->debug_check_recordset($res)) {
1113  return $res->data_seek($seek);
1114  } else {
1115  return FALSE;
1116  }
1117  }
1118 
1127  public function sql_field_type($res, $pointer) {
1128  // mysql_field_type compatibility map
1129  // taken from: http://www.php.net/manual/en/mysqli-result.fetch-field-direct.php#89117
1130  // Constant numbers see http://php.net/manual/en/mysqli.constants.php
1131  $mysql_data_type_hash = array(
1132  1=>'tinyint',
1133  2=>'smallint',
1134  3=>'int',
1135  4=>'float',
1136  5=>'double',
1137  7=>'timestamp',
1138  8=>'bigint',
1139  9=>'mediumint',
1140  10=>'date',
1141  11=>'time',
1142  12=>'datetime',
1143  13=>'year',
1144  16=>'bit',
1145  //252 is currently mapped to all text and blob types (MySQL 5.0.51a)
1146  253=>'varchar',
1147  254=>'char',
1148  246=>'decimal'
1149  );
1150  if ($this->debug_check_recordset($res)) {
1151  $metaInfo = $res->fetch_field_direct($pointer);
1152  if ($metaInfo === FALSE) {
1153  return FALSE;
1154  }
1155  return $mysql_data_type_hash[$metaInfo->type];
1156  } else {
1157  return FALSE;
1158  }
1159  }
1160 
1170  public function sql_pconnect($host = NULL, $username = NULL, $password = NULL) {
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  if ($host || $username || $password) {
1183  $this->handleDeprecatedConnectArguments($host, $username, $password);
1184  }
1185 
1186  $host = $this->persistentDatabaseConnection
1187  ? 'p:' . $this->databaseHost
1189 
1190  $this->link = mysqli_init();
1191  $connected = $this->link->real_connect(
1192  $host,
1193  $this->databaseUsername,
1194  $this->databaseUserPassword,
1195  NULL,
1196  (int)$this->databasePort,
1197  $this->databaseSocket,
1198  $this->connectionCompression ? MYSQLI_CLIENT_COMPRESS : 0
1199  );
1200 
1201  if ($connected) {
1202  $this->isConnected = TRUE;
1203 
1204  if ($this->link->set_charset($this->connectionCharset) === FALSE) {
1205  GeneralUtility::sysLog(
1206  'Error setting connection charset to "' . $this->connectionCharset . '"',
1207  'Core',
1209  );
1210  }
1211 
1212  foreach ($this->initializeCommandsAfterConnect as $command) {
1213  if ($this->query($command) === FALSE) {
1214  GeneralUtility::sysLog(
1215  'Could not initialize DB connection with query "' . $command . '": ' . $this->sql_error(),
1216  'Core',
1218  );
1219  }
1220  }
1221  $this->setSqlMode();
1222  $this->checkConnectionCharset();
1223  } else {
1224  // @TODO: This should raise an exception. Would be useful especially to work during installation.
1225  $error_msg = $this->link->connect_error;
1226  $this->link = NULL;
1227  GeneralUtility::sysLog(
1228  'Could not connect to MySQL server ' . $host . ' with user ' . $username . ': ' . $error_msg,
1229  'Core',
1231  );
1232  }
1233  return $this->link;
1234  }
1235 
1241  protected function setSqlMode() {
1242  $resource = $this->sql_query('SELECT @@SESSION.sql_mode;');
1243  if ($resource) {
1244  $result = $this->sql_fetch_row($resource);
1245  if (isset($result[0]) && $result[0] && strpos($result[0], 'NO_BACKSLASH_ESCAPES') !== FALSE) {
1246  $modes = array_diff(GeneralUtility::trimExplode(',', $result[0]), array('NO_BACKSLASH_ESCAPES'));
1247  $query = 'SET sql_mode=\'' . $this->link->real_escape_string(implode(',', $modes)) . '\';';
1248  $this->sql_query($query);
1249  GeneralUtility::sysLog(
1250  'NO_BACKSLASH_ESCAPES could not be removed from SQL mode: ' . $this->sql_error(),
1251  'Core',
1252  GeneralUtility::SYSLOG_SEVERITY_ERROR
1253  );
1254  }
1255  }
1256  }
1257 
1264  public function sql_select_db($TYPO3_db = NULL) {
1265  if (!$this->isConnected) {
1266  $this->connectDB();
1267  }
1268 
1269  if ($TYPO3_db) {
1270  GeneralUtility::deprecationLog(
1271  'DatabaseConnection->sql_select_db() should be called without arguments.' .
1272  ' Use the setDatabaseName() before. Will be removed two versions after 6.1.'
1273  );
1274  } else {
1275  $TYPO3_db = $this->databaseName;
1276  }
1277 
1278  $ret = $this->link->select_db($TYPO3_db);
1279  if (!$ret) {
1280  GeneralUtility::sysLog(
1281  'Could not select MySQL database ' . $TYPO3_db . ': ' . $this->sql_error(),
1282  'Core',
1284  );
1285  }
1286  return $ret;
1287  }
1288 
1289  /**************************************
1290  *
1291  * SQL admin functions
1292  * (For use in the Install Tool and Extension Manager)
1293  *
1294  **************************************/
1304  public function admin_get_dbs() {
1305  $dbArr = array();
1306  $db_list = $this->query("SELECT SCHEMA_NAME FROM information_schema.SCHEMATA");
1307  if ($db_list === FALSE) {
1308  throw new \RuntimeException(
1309  'MySQL Error: Cannot get tablenames: "' . $this->sql_error() . '"!',
1310  1378457171
1311  );
1312  } else {
1313  while ($row = $db_list->fetch_object()) {
1314  try {
1315  $this->setDatabaseName($row->SCHEMA_NAME);
1316  if ($this->sql_select_db()) {
1317  $dbArr[] = $row->SCHEMA_NAME;
1318  }
1319  } catch (\RuntimeException $exception) {
1320  // The exception happens if we cannot connect to the database
1321  // (usually due to missing permissions). This is ok here.
1322  // We catch the exception, skip the database and continue.
1323  }
1324  }
1325  }
1326  return $dbArr;
1327  }
1328 
1336  public function admin_get_tables() {
1337  $whichTables = array();
1338  $tables_result = $this->query('SHOW TABLE STATUS FROM `' . $this->databaseName . '`');
1339  if ($tables_result !== FALSE) {
1340  while ($theTable = $tables_result->fetch_assoc()) {
1341  $whichTables[$theTable['Name']] = $theTable;
1342  }
1343  $tables_result->free();
1344  }
1345  return $whichTables;
1346  }
1347 
1359  public function admin_get_fields($tableName) {
1360  $output = array();
1361  $columns_res = $this->query('SHOW COLUMNS FROM `' . $tableName . '`');
1362  if ($columns_res !== FALSE) {
1363  while ($fieldRow = $columns_res->fetch_assoc()) {
1364  $output[$fieldRow['Field']] = $fieldRow;
1365  }
1366  $columns_res->free();
1367  }
1368  return $output;
1369  }
1370 
1378  public function admin_get_keys($tableName) {
1379  $output = array();
1380  $keyRes = $this->query('SHOW KEYS FROM `' . $tableName . '`');
1381  if ($keyRes !== FALSE) {
1382  while ($keyRow = $keyRes->fetch_assoc()) {
1383  $output[] = $keyRow;
1384  }
1385  $keyRes->free();
1386  }
1387  return $output;
1388  }
1389 
1402  public function admin_get_charsets() {
1403  $output = array();
1404  $columns_res = $this->query('SHOW CHARACTER SET');
1405  if ($columns_res !== FALSE) {
1406  while ($row = $columns_res->fetch_assoc()) {
1407  $output[$row['Charset']] = $row;
1408  }
1409  $columns_res->free();
1410  }
1411  return $output;
1412  }
1413 
1420  public function admin_query($query) {
1421  $res = $this->query($query);
1422  if ($this->debugOutput) {
1423  $this->debug('admin_query', $query);
1424  }
1425  return $res;
1426  }
1427 
1428  /******************************
1429  *
1430  * Connect handling
1431  *
1432  ******************************/
1433 
1439  public function setDatabaseHost($host = 'localhost') {
1440  $this->disconnectIfConnected();
1441  $this->databaseHost = $host;
1442  }
1443 
1449  public function setDatabasePort($port = 3306) {
1450  $this->disconnectIfConnected();
1451  $this->databasePort = (int)$port;
1452  }
1453 
1459  public function setDatabaseSocket($socket = NULL) {
1460  $this->disconnectIfConnected();
1461  $this->databaseSocket = $socket;
1462  }
1463 
1469  public function setDatabaseName($name) {
1470  $this->disconnectIfConnected();
1471  $this->databaseName = $name;
1472  }
1473 
1479  public function setDatabaseUsername($username) {
1480  $this->disconnectIfConnected();
1481  $this->databaseUsername = $username;
1482  }
1483 
1489  public function setDatabasePassword($password) {
1490  $this->disconnectIfConnected();
1491  $this->databaseUserPassword = $password;
1492  }
1493 
1501  $this->disconnectIfConnected();
1502  $this->persistentDatabaseConnection = (bool)$persistentDatabaseConnection;
1503  }
1504 
1511  $this->disconnectIfConnected();
1512  $this->connectionCompression = (bool)$connectionCompression;
1513  }
1514 
1520  public function setInitializeCommandsAfterConnect(array $commands) {
1521  $this->disconnectIfConnected();
1522  $this->initializeCommandsAfterConnect = $commands;
1523  }
1524 
1534  public function setConnectionCharset($connectionCharset = 'utf8') {
1535  $this->disconnectIfConnected();
1536  $this->connectionCharset = $connectionCharset;
1537  }
1538 
1550  public function connectDB($host = NULL, $username = NULL, $password = NULL, $db = NULL) {
1551  // Early return if connected already
1552  if ($this->isConnected) {
1553  return;
1554  }
1555 
1556  if (!$this->databaseName && !$db) {
1557  throw new \RuntimeException(
1558  'TYPO3 Fatal Error: No database selected!',
1559  1270853882
1560  );
1561  }
1562 
1563  if ($host || $username || $password || $db) {
1564  $this->handleDeprecatedConnectArguments($host, $username, $password, $db);
1565  }
1566 
1567  if ($this->sql_pconnect()) {
1568  if (!$this->sql_select_db()) {
1569  throw new \RuntimeException(
1570  'TYPO3 Fatal Error: Cannot connect to the current database, "' . $this->databaseName . '"!',
1571  1270853883
1572  );
1573  }
1574  } else {
1575  throw new \RuntimeException(
1576  'TYPO3 Fatal Error: The current username, password or host was not accepted when the connection to the database was attempted to be established!',
1577  1270853884
1578  );
1579  }
1580 
1581  // Prepare user defined objects (if any) for hooks which extend query methods
1582  $this->preProcessHookObjects = array();
1583  $this->postProcessHookObjects = array();
1584  if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_db.php']['queryProcessors'])) {
1585  foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_db.php']['queryProcessors'] as $classRef) {
1586  $hookObject = GeneralUtility::getUserObj($classRef);
1587  if (!(
1588  $hookObject instanceof PreProcessQueryHookInterface
1589  || $hookObject instanceof PostProcessQueryHookInterface
1590  )) {
1591  throw new \UnexpectedValueException(
1592  '$hookObject must either implement interface TYPO3\\CMS\\Core\\Database\\PreProcessQueryHookInterface or interface TYPO3\\CMS\\Core\\Database\\PostProcessQueryHookInterface',
1593  1299158548
1594  );
1595  }
1596  if ($hookObject instanceof PreProcessQueryHookInterface) {
1597  $this->preProcessHookObjects[] = $hookObject;
1598  }
1599  if ($hookObject instanceof PostProcessQueryHookInterface) {
1600  $this->postProcessHookObjects[] = $hookObject;
1601  }
1602  }
1603  }
1604  }
1605 
1611  public function isConnected() {
1612  // We think we're still connected
1613  if ($this->isConnected) {
1614  // Check if this is really the case or if the database server has gone away for some reason
1615  // Using mysqlnd ping() does not reconnect (which we would not want anyway since charset etc would not be reinitialized that way)
1616  $this->isConnected = $this->link->ping();
1617  }
1618  return $this->isConnected;
1619  }
1620 
1635  protected function checkConnectionCharset() {
1636  $sessionResult = $this->sql_query('SHOW SESSION VARIABLES LIKE \'character_set%\'');
1637 
1638  if ($sessionResult === FALSE) {
1639  GeneralUtility::sysLog(
1640  'Error while retrieving the current charset session variables from the database: ' . $this->sql_error(),
1641  'Core',
1643  );
1644  throw new \RuntimeException(
1645  'TYPO3 Fatal Error: Could not determine the current charset of the database.',
1646  1381847136
1647  );
1648  }
1649 
1650  $charsetVariables = array();
1651  while (($row = $this->sql_fetch_row($sessionResult)) !== FALSE) {
1652  $variableName = $row[0];
1653  $variableValue = $row[1];
1654  $charsetVariables[$variableName] = $variableValue;
1655  }
1656  $this->sql_free_result($sessionResult);
1657 
1658  // These variables are set with the "Set names" command which was
1659  // used in the past. This is why we check them.
1660  $charsetRequiredVariables = array(
1661  'character_set_client',
1662  'character_set_results',
1663  'character_set_connection',
1664  );
1665 
1666  $hasValidCharset = TRUE;
1667  foreach ($charsetRequiredVariables as $variableName) {
1668  if (empty($charsetVariables[$variableName])) {
1669  GeneralUtility::sysLog(
1670  'A required session variable is missing in the current MySQL connection: ' . $variableName,
1671  'Core',
1673  );
1674  throw new \RuntimeException(
1675  'TYPO3 Fatal Error: Could not determine the value of the database session variable: ' . $variableName,
1676  1381847779
1677  );
1678  }
1679 
1680  if ($charsetVariables[$variableName] !== $this->connectionCharset) {
1681  $hasValidCharset = FALSE;
1682  break;
1683  }
1684  }
1685 
1686  if (!$hasValidCharset) {
1687  throw new \RuntimeException(
1688  'It looks like the character set ' . $this->connectionCharset . ' is not used for this connection even though it is configured as connection charset. ' .
1689  'This TYPO3 installation is using the $GLOBALS[\'TYPO3_CONF_VARS\'][\'SYS\'][\'setDBinit\'] property with the following value: "' .
1690  $GLOBALS['TYPO3_CONF_VARS']['SYS']['setDBinit'] . '". Please make sure that this command does not overwrite the configured charset. ' .
1691  'Please note that for the TYPO3 database everything other than utf8 is unsupported since version 4.7.',
1692  1389697515
1693  );
1694  }
1695  }
1696 
1702  protected function disconnectIfConnected() {
1703  if ($this->isConnected) {
1704  $this->link->close();
1705  $this->isConnected = FALSE;
1706  }
1707  }
1708 
1714  public function getDatabaseHandle() {
1715  return $this->link;
1716  }
1717 
1723  public function setDatabaseHandle($handle) {
1724  $this->link = $handle;
1725  }
1726 
1735  protected function handleDeprecatedConnectArguments($host = NULL, $username = NULL, $password = NULL, $db = NULL) {
1737  'DatabaseConnection->sql_pconnect() and DatabaseConnection->connectDB() should be ' .
1738  'called without arguments. Use the setters instead.'
1739  );
1740  if ($host) {
1741  if (strpos($host, ':') > 0) {
1742  list($databaseHost, $databasePort) = explode(':', $host);
1745  } else {
1746  $this->setDatabaseHost($host);
1747  }
1748  }
1749  if ($username) {
1750  $this->setDatabaseUsername($username);
1751  }
1752  if ($password) {
1753  $this->setDatabasePassword($password);
1754  }
1755  if ($db) {
1756  $this->setDatabaseName($db);
1757  }
1758  }
1759 
1760  /******************************
1761  *
1762  * Debugging
1763  *
1764  ******************************/
1773  public function debug($func, $query = '') {
1774  $error = $this->sql_error();
1775  if ($error || (int)$this->debugOutput === 2) {
1777  array(
1778  'caller' => 'TYPO3\\CMS\\Core\\Database\\DatabaseConnection::' . $func,
1779  'ERROR' => $error,
1780  'lastBuiltQuery' => $query ? $query : $this->debug_lastBuiltQuery,
1781  'debug_backtrace' => \TYPO3\CMS\Core\Utility\DebugUtility::debugTrail()
1782  ),
1783  $func,
1784  is_object($GLOBALS['error']) && @is_callable(array($GLOBALS['error'], 'debug'))
1785  ? ''
1786  : 'DB Error'
1787  );
1788  }
1789  }
1790 
1798  public function debug_check_recordset($res) {
1799  if ($res !== FALSE && $res !== NULL) {
1800  return TRUE;
1801  }
1802  $msg = 'Invalid database result detected';
1803  $trace = debug_backtrace();
1804  array_shift($trace);
1805  $cnt = count($trace);
1806  for ($i = 0; $i < $cnt; $i++) {
1807  // Complete objects are too large for the log
1808  if (isset($trace['object'])) {
1809  unset($trace['object']);
1810  }
1811  }
1812  $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'];
1813  GeneralUtility::sysLog(
1814  $msg . '. Use a devLog extension to get more details.',
1815  'Core/t3lib_db',
1817  );
1818  // Send to devLog if enabled
1819  if (TYPO3_DLOG) {
1820  $debugLogData = array(
1821  'SQL Error' => $this->sql_error(),
1822  'Backtrace' => $trace
1823  );
1824  if ($this->debug_lastBuiltQuery) {
1825  $debugLogData = array('SQL Query' => $this->debug_lastBuiltQuery) + $debugLogData;
1826  }
1827  GeneralUtility::devLog($msg . '.', 'Core/t3lib_db', 3, $debugLogData);
1828  }
1829  return FALSE;
1830  }
1831 
1844  protected function explain($query, $from_table, $row_count) {
1845  $debugAllowedForIp = GeneralUtility::cmpIP(
1846  GeneralUtility::getIndpEnv('REMOTE_ADDR'),
1847  $GLOBALS['TYPO3_CONF_VARS']['SYS']['devIPmask']
1848  );
1849  if (
1850  (int)$this->explainOutput == 1
1851  || ((int)$this->explainOutput == 2 && $debugAllowedForIp)
1852  ) {
1853  // Raw HTML output
1854  $explainMode = 1;
1855  } elseif ((int)$this->explainOutput == 3 && is_object($GLOBALS['TT'])) {
1856  // Embed the output into the TS admin panel
1857  $explainMode = 2;
1858  } else {
1859  return FALSE;
1860  }
1861  $error = $this->sql_error();
1863  $explain_tables = array();
1864  $explain_output = array();
1865  $res = $this->sql_query('EXPLAIN ' . $query, $this->link);
1866  if (is_a($res, '\\mysqli_result')) {
1867  while ($tempRow = $this->sql_fetch_assoc($res)) {
1868  $explain_output[] = $tempRow;
1869  $explain_tables[] = $tempRow['table'];
1870  }
1871  $this->sql_free_result($res);
1872  }
1873  $indices_output = array();
1874  // Notice: Rows are skipped if there is only one result, or if no conditions are set
1875  if (
1876  $explain_output[0]['rows'] > 1
1877  || GeneralUtility::inList('ALL', $explain_output[0]['type'])
1878  ) {
1879  // Only enable output if it's really useful
1880  $debug = TRUE;
1881  foreach ($explain_tables as $table) {
1882  $tableRes = $this->sql_query('SHOW TABLE STATUS LIKE \'' . $table . '\'');
1883  $isTable = $this->sql_num_rows($tableRes);
1884  if ($isTable) {
1885  $res = $this->sql_query('SHOW INDEX FROM ' . $table, $this->link);
1886  if (is_a($res, '\\mysqli_result')) {
1887  while ($tempRow = $this->sql_fetch_assoc($res)) {
1888  $indices_output[] = $tempRow;
1889  }
1890  $this->sql_free_result($res);
1891  }
1892  }
1893  $this->sql_free_result($tableRes);
1894  }
1895  } else {
1896  $debug = FALSE;
1897  }
1898  if ($debug) {
1899  if ($explainMode) {
1900  $data = array();
1901  $data['query'] = $query;
1902  $data['trail'] = $trail;
1903  $data['row_count'] = $row_count;
1904  if ($error) {
1905  $data['error'] = $error;
1906  }
1907  if (count($explain_output)) {
1908  $data['explain'] = $explain_output;
1909  }
1910  if (count($indices_output)) {
1911  $data['indices'] = $indices_output;
1912  }
1913  if ($explainMode == 1) {
1914  \TYPO3\CMS\Core\Utility\DebugUtility::debug($data, 'Tables: ' . $from_table, 'DB SQL EXPLAIN');
1915  } elseif ($explainMode == 2) {
1916  $GLOBALS['TT']->setTSselectQuery($data);
1917  }
1918  }
1919  return TRUE;
1920  }
1921  return FALSE;
1922  }
1923 
1929  public function __sleep() {
1930  $this->disconnectIfConnected();
1931  return array(
1932  'debugOutput',
1933  'explainOutput',
1934  'databaseHost',
1935  'databasePort',
1936  'databaseSocket',
1937  'databaseName',
1938  'databaseUsername',
1939  'databaseUserPassword',
1940  'persistentDatabaseConnection',
1941  'connectionCompression',
1942  'initializeCommandsAfterConnect',
1943  'default_charset',
1944  );
1945  }
1946 }
exec_SELECTcountRows($field, $table, $where='1=1')
prepare_PREPAREDquery($query, array $queryComponents)
static debug($var='', $header='', $group='Debug')
static devLog($msg, $extKey, $severity=0, $dataVar=FALSE)
exec_SELECT_mm_query($select, $local_table, $mm_table, $foreign_table, $whereClause='', $groupBy='', $orderBy='', $limit='')
fullQuoteArray($arr, $table, $noQuote=FALSE, $allowNull=FALSE)
static intExplode($delimiter, $string, $removeEmptyValues=FALSE, $limit=0)
exec_SELECTgetRows($select_fields, $from_table, $where_clause, $groupBy='', $orderBy='', $limit='', $uidIndexField='')
setPersistentDatabaseConnection($persistentDatabaseConnection)
static getUserObj($classRef, $checkPrefix='', $silent=FALSE)
exec_SELECTgetSingleRow($select_fields, $from_table, $where_clause, $groupBy='', $orderBy='', $numIndex=FALSE)
prepare_SELECTqueryArray(array $queryParts, array $input_parameters=array())
static trimExplode($delim, $string, $removeEmptyValues=FALSE, $limit=0)
if(empty($ajaxID)) elseif(empty($ajaxScript)) else
Definition: ajax.php:71
if($list_of_literals) if(!empty($literals)) if(!empty($literals)) $result
Analyse literals to prepend the N char to them if their contents aren&#39;t numeric.
handleDeprecatedConnectArguments($host=NULL, $username=NULL, $password=NULL, $db=NULL)
$host
Definition: server.php:35
explain($query, $from_table, $row_count)
SELECTsubquery($select_fields, $from_table, $where_clause)
connectDB($host=NULL, $username=NULL, $password=NULL, $db=NULL)
fullQuoteStr($str, $table, $allowNull=FALSE)
if(!defined('TYPO3_MODE')) $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_userauth.php']['logoff_pre_processing'][]
INSERTquery($table, $fields_values, $no_quote_fields=FALSE)
sql_pconnect($host=NULL, $username=NULL, $password=NULL)