TYPO3 CMS  TYPO3_6-2
PreparedStatement.php
Go to the documentation of this file.
1 <?php
3 
34 
40  const PARAM_NULL = 0;
46  const PARAM_INT = 1;
52  const PARAM_STR = 2;
58  const PARAM_BOOL = 3;
64  const PARAM_AUTOTYPE = 4;
73  const FETCH_ASSOC = 2;
80  const FETCH_NUM = 3;
86  protected $query;
87 
94 
100  protected $table;
101 
107  protected $parameters;
108 
114  protected $defaultFetchMode = self::FETCH_ASSOC;
115 
121  protected $statement;
122 
126  protected $fields;
127 
131  protected $buffer;
132 
140 
155  public function __construct($query, $table, array $precompiledQueryParts = array()) {
156  $this->query = $query;
157  $this->precompiledQueryParts = $precompiledQueryParts;
158  $this->table = $table;
159  $this->parameters = array();
160 
161  // Test if named placeholders are used
162  if ($this->hasNamedPlaceholders($query) || count($precompiledQueryParts) > 0) {
163  $this->statement = NULL;
164  } else {
165  // Only question mark placeholders are used
166  $this->statement = $GLOBALS['TYPO3_DB']->prepare_PREPAREDquery($this->query, $this->precompiledQueryParts);
167  }
168 
169  $this->parameterWrapToken = $this->generateParameterWrapToken();
170  }
171 
192  public function bindValues(array $values) {
193  foreach ($values as $parameter => $value) {
194  $key = is_int($parameter) ? $parameter + 1 : $parameter;
195  $this->bindValue($key, $value, self::PARAM_AUTOTYPE);
196  }
197  return $this;
198  }
199 
224  public function bindValue($parameter, $value, $data_type = self::PARAM_AUTOTYPE) {
225  switch ($data_type) {
226  case self::PARAM_INT:
227  if (!is_int($value)) {
228  throw new \InvalidArgumentException('$value is not an integer as expected: ' . $value, 1281868686);
229  }
230  break;
231  case self::PARAM_BOOL:
232  if (!is_bool($value)) {
233  throw new \InvalidArgumentException('$value is not a boolean as expected: ' . $value, 1281868687);
234  }
235  break;
236  case self::PARAM_NULL:
237  if (!is_null($value)) {
238  throw new \InvalidArgumentException('$value is not NULL as expected: ' . $value, 1282489834);
239  }
240  break;
241  }
242  if (!is_int($parameter) && !preg_match('/^:[\\w]+$/', $parameter)) {
243  throw new \InvalidArgumentException('Parameter names must start with ":" followed by an arbitrary number of alphanumerical characters.', 1395055513);
244  }
245  $key = is_int($parameter) ? $parameter - 1 : $parameter;
246  $this->parameters[$key] = array(
247  'value' => $value,
248  'type' => $data_type == self::PARAM_AUTOTYPE ? $this->guessValueType($value) : $data_type
249  );
250  return $this;
251  }
252 
282  public function execute(array $input_parameters = array()) {
283  $parameterValues = $this->parameters;
284  if (!empty($input_parameters)) {
285  $parameterValues = array();
286  foreach ($input_parameters as $key => $value) {
287  $parameterValues[$key] = array(
288  'value' => $value,
289  'type' => $this->guessValueType($value)
290  );
291  }
292  }
293 
294  if ($this->statement !== NULL) {
295  // The statement has already been executed, we try to reset it
296  // for current run but will set it to NULL if it fails for some
297  // reason, just as if it were the first run
298  if (!@$this->statement->reset()) {
299  $this->statement = NULL;
300  }
301  }
302  if ($this->statement === NULL) {
303  // The statement has never been executed so we prepare it and
304  // store it for further reuse
307 
309  if (count($precompiledQueryParts) > 0) {
310  $query = implode('', $precompiledQueryParts['queryParts']);
311  }
312  $this->statement = $GLOBALS['TYPO3_DB']->prepare_PREPAREDquery($query, $precompiledQueryParts);
313  if ($this->statement === NULL) {
314  return FALSE;
315  }
316  }
317 
318  $combinedTypes = '';
319  $values = array();
320  foreach ($parameterValues as $parameterValue) {
321  switch ($parameterValue['type']) {
322  case self::PARAM_NULL:
323  $type = 's';
324  $value = NULL;
325  break;
326  case self::PARAM_INT:
327  $type = 'i';
328  $value = (int)$parameterValue['value'];
329  break;
330  case self::PARAM_STR:
331  $type = 's';
332  $value = $parameterValue['value'];
333  break;
334  case self::PARAM_BOOL:
335  $type = 'i';
336  $value = $parameterValue['value'] ? 1 : 0;
337  break;
338  default:
339  throw new \InvalidArgumentException(sprintf('Unknown type %s used for parameter %s.', $parameterValue['type'], $key), 1281859196);
340  }
341 
342  $combinedTypes .= $type;
343  $values[] = $value;
344  }
345 
346  // ->bind_param requires second up to last arguments as references
347  if (!empty($combinedTypes)) {
348  $bindParamArguments = array();
349  $bindParamArguments[] = $combinedTypes;
350  $numberOfExtraParamArguments = count($values);
351  for ($i = 0; $i < $numberOfExtraParamArguments; $i++) {
352  $bindParamArguments[] = &$values[$i];
353  }
354 
355  call_user_func_array(array($this->statement, 'bind_param'), $bindParamArguments);
356  }
357 
358  $success = $this->statement->execute();
359 
360  // Store result
361  if (!$success || $this->statement->store_result() === FALSE) {
362  return FALSE;
363  }
364 
365  if (count($this->fields) === 0) {
366  // Store the list of fields
367  if ($this->statement instanceof \mysqli_stmt) {
368  $result = $this->statement->result_metadata();
369  if ($result instanceof \mysqli_result) {
370  $fields = $result->fetch_fields();
371  $result->close();
372  }
373  } else {
374  $fields = $this->statement->fetch_fields();
375  }
376  if (is_array($fields)) {
377  foreach ($fields as $field) {
378  $this->fields[] = $field->name;
379  }
380  }
381 
382  }
383 
384  // New result set available
385  $this->buffer = NULL;
386 
387  // Empty binding parameters
388  $this->parameters = array();
389 
390  // Return the success flag
391  return $success;
392  }
393 
401  public function fetch($fetch_style = 0) {
402  if ($fetch_style == 0) {
403  $fetch_style = $this->defaultFetchMode;
404  }
405 
406  if ($this->statement instanceof \mysqli_stmt) {
407  if ($this->buffer === NULL) {
408  $variables = array();
409  $this->buffer = array();
410  foreach ($this->fields as $field) {
411  $this->buffer[$field] = NULL;
412  $variables[] = &$this->buffer[$field];
413  }
414 
415  call_user_func_array(array($this->statement, 'bind_result'), $variables);
416  }
417  $success = $this->statement->fetch();
418  $columns = $this->buffer;
419  } else {
420  $columns = $this->statement->fetch();
421  $success = is_array($columns);
422  }
423 
424  if ($success) {
425  $row = array();
426  foreach ($columns as $key => $value) {
427  switch ($fetch_style) {
428  case self::FETCH_ASSOC:
429  $row[$key] = $value;
430  break;
431  case self::FETCH_NUM:
432  $row[] = $value;
433  break;
434  default:
435  throw new \InvalidArgumentException('$fetch_style must be either TYPO3\\CMS\\Core\\Database\\PreparedStatement::FETCH_ASSOC or TYPO3\\CMS\\Core\\Database\\PreparedStatement::FETCH_NUM', 1281646455);
436  }
437  }
438  } else {
439  $row = FALSE;
440  }
441 
442  return $row;
443  }
444 
452  public function seek($rowNumber) {
453  $success = $this->statement->data_seek((int)$rowNumber);
454  if ($this->statement instanceof \mysqli_stmt) {
455  // data_seek() does not return anything
456  $success = TRUE;
457  }
458  return $success;
459  }
460 
468  public function fetchAll($fetch_style = 0) {
469  $rows = array();
470  while (($row = $this->fetch($fetch_style)) !== FALSE) {
471  $rows[] = $row;
472  }
473  return $rows;
474  }
475 
483  public function free() {
484  $this->statement->close();
485  }
486 
493  public function rowCount() {
494  return $this->statement->num_rows;
495  }
496 
503  public function errorCode() {
504  return $this->statement->errno;
505  }
506 
517  public function errorInfo() {
518  return array(
519  $this->statement->errno,
520  $this->statement->error
521  );
522  }
523 
531  public function setFetchMode($mode) {
532  switch ($mode) {
533  case self::FETCH_ASSOC:
534 
535  case self::FETCH_NUM:
536  $this->defaultFetchMode = $mode;
537  break;
538  default:
539  throw new \InvalidArgumentException('$mode must be either TYPO3\\CMS\\Core\\Database\\PreparedStatement::FETCH_ASSOC or TYPO3\\CMS\\Core\\Database\\PreparedStatement::FETCH_NUM', 1281875340);
540  }
541  }
542 
549  protected function guessValueType($value) {
550  if (is_bool($value)) {
551  $type = self::PARAM_BOOL;
552  } elseif (is_int($value)) {
553  $type = self::PARAM_INT;
554  } elseif (is_null($value)) {
555  $type = self::PARAM_NULL;
556  } else {
557  $type = self::PARAM_STR;
558  }
559  return $type;
560  }
561 
568  protected function hasNamedPlaceholders($query) {
569  $matches = preg_match('/(?<![\\w:]):[\\w]+\\b/', $query);
570  return $matches > 0;
571  }
572 
581  protected function convertNamedPlaceholdersToQuestionMarks(&$query, array &$parameterValues, array &$precompiledQueryParts) {
582  $queryPartsCount = count($precompiledQueryParts['queryParts']);
583  $newParameterValues = array();
584  $hasNamedPlaceholders = FALSE;
585 
586  if ($queryPartsCount === 0) {
587  $hasNamedPlaceholders = $this->hasNamedPlaceholders($query);
588  if ($hasNamedPlaceholders) {
589  $query = $this->tokenizeQueryParameterMarkers($query, $parameterValues);
590  }
591  } elseif (count($parameterValues) > 0) {
592  $hasNamedPlaceholders = !is_int(key($parameterValues));
593  if ($hasNamedPlaceholders) {
594  for ($i = 1; $i < $queryPartsCount; $i += 2) {
595  $key = $precompiledQueryParts['queryParts'][$i];
596  $precompiledQueryParts['queryParts'][$i] = '?';
597  $newParameterValues[] = $parameterValues[$key];
598  }
599  }
600  }
601 
602  if ($hasNamedPlaceholders) {
603  if ($queryPartsCount === 0) {
604  // Convert named placeholders to standard question mark placeholders
605  $quotedParamWrapToken = preg_quote($this->parameterWrapToken, '/');
606  while (preg_match(
607  '/' . $quotedParamWrapToken . '(.*?)' . $quotedParamWrapToken . '/',
608  $query,
609  $matches
610  )) {
611  $key = $matches[1];
612 
613  $newParameterValues[] = $parameterValues[$key];
614  $query = preg_replace(
615  '/' . $quotedParamWrapToken . $key . $quotedParamWrapToken . '/',
616  '?',
617  $query,
618  1
619  );
620  }
621  }
622 
623  $parameterValues = $newParameterValues;
624  }
625  }
626 
635  protected function tokenizeQueryParameterMarkers($query, array $parameterValues) {
636  $unnamedParameterCount = 0;
637  foreach ($parameterValues as $key => $typeValue) {
638  if (!is_int($key)) {
639  if (!preg_match('/^:[\\w]+$/', $key)) {
640  throw new \InvalidArgumentException('Parameter names must start with ":" followed by an arbitrary number of alphanumerical characters.', 1282348825);
641  }
642  // Replace the marker (not preceeded by a word character or a ':' but
643  // followed by a word boundary)
644  $query = preg_replace('/(?<![\\w:])' . preg_quote($key, '/') . '\\b/', $this->parameterWrapToken . $key . $this->parameterWrapToken, $query);
645  } else {
646  $unnamedParameterCount++;
647  }
648  }
649  $parts = explode('?', $query, $unnamedParameterCount + 1);
650  $query = implode($this->parameterWrapToken . '?' . $this->parameterWrapToken, $parts);
651  return $query;
652  }
653 
659  protected function generateParameterWrapToken() {
661  }
662 
663 }
bindValue($parameter, $value, $data_type=self::PARAM_AUTOTYPE)
__construct($query, $table, array $precompiledQueryParts=array())
execute(array $input_parameters=array())
convertNamedPlaceholdersToQuestionMarks(&$query, array &$parameterValues, array &$precompiledQueryParts)
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.
if(!defined('TYPO3_MODE')) $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_userauth.php']['logoff_pre_processing'][]
tokenizeQueryParameterMarkers($query, array $parameterValues)