2008-07-17 03:51:29 +00:00
|
|
|
<?php
|
|
|
|
|
|
|
|
/**
|
|
|
|
* dibi - tiny'n'smart database abstraction layer
|
|
|
|
* ----------------------------------------------
|
|
|
|
*
|
2008-12-31 00:13:40 +00:00
|
|
|
* Copyright (c) 2005, 2009 David Grudl (http://davidgrudl.com)
|
2008-07-17 03:51:29 +00:00
|
|
|
*
|
|
|
|
* This source file is subject to the "dibi license" that is bundled
|
|
|
|
* with this package in the file license.txt.
|
|
|
|
*
|
|
|
|
* For more information please see http://dibiphp.com
|
|
|
|
*
|
2008-12-31 00:13:40 +00:00
|
|
|
* @copyright Copyright (c) 2005, 2009 David Grudl
|
2008-07-17 03:51:29 +00:00
|
|
|
* @license http://dibiphp.com/license dibi license
|
|
|
|
* @link http://dibiphp.com
|
|
|
|
* @package dibi
|
|
|
|
* @version $Id$
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* dibi SQL translator.
|
|
|
|
*
|
|
|
|
* @author David Grudl
|
2008-12-31 00:13:40 +00:00
|
|
|
* @copyright Copyright (c) 2005, 2009 David Grudl
|
2008-07-17 03:51:29 +00:00
|
|
|
* @package dibi
|
|
|
|
*/
|
2008-09-05 05:35:15 +00:00
|
|
|
final class DibiTranslator extends DibiObject
|
2008-07-17 03:51:29 +00:00
|
|
|
{
|
|
|
|
/** @var IDibiDriver */
|
|
|
|
private $driver;
|
|
|
|
|
|
|
|
/** @var int */
|
|
|
|
private $cursor;
|
|
|
|
|
|
|
|
/** @var array */
|
|
|
|
private $args;
|
|
|
|
|
|
|
|
/** @var bool */
|
|
|
|
private $hasError;
|
|
|
|
|
|
|
|
/** @var bool */
|
|
|
|
private $comment;
|
|
|
|
|
|
|
|
/** @var int */
|
|
|
|
private $ifLevel;
|
|
|
|
|
|
|
|
/** @var int */
|
|
|
|
private $ifLevelStart;
|
|
|
|
|
|
|
|
/** @var int */
|
|
|
|
private $limit;
|
|
|
|
|
|
|
|
/** @var int */
|
|
|
|
private $offset;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public function __construct(IDibiDriver $driver)
|
|
|
|
{
|
|
|
|
$this->driver = $driver;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* return IDibiDriver.
|
|
|
|
*/
|
|
|
|
public function getDriver()
|
|
|
|
{
|
|
|
|
return $this->driver;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Generates SQL.
|
|
|
|
* @param array
|
2009-02-05 21:08:00 +00:00
|
|
|
* @return string
|
|
|
|
* @throws DibiException
|
2008-07-17 03:51:29 +00:00
|
|
|
*/
|
|
|
|
public function translate(array $args)
|
|
|
|
{
|
|
|
|
$this->limit = -1;
|
|
|
|
$this->offset = 0;
|
|
|
|
$this->hasError = FALSE;
|
|
|
|
$commandIns = NULL;
|
|
|
|
$lastArr = NULL;
|
|
|
|
// shortcuts
|
|
|
|
$cursor = & $this->cursor;
|
|
|
|
$cursor = 0;
|
|
|
|
$this->args = array_values($args);
|
|
|
|
$args = & $this->args;
|
|
|
|
|
|
|
|
// conditional sql
|
|
|
|
$this->ifLevel = $this->ifLevelStart = 0;
|
|
|
|
$comment = & $this->comment;
|
|
|
|
$comment = FALSE;
|
|
|
|
|
|
|
|
// iterate
|
|
|
|
$sql = array();
|
|
|
|
while ($cursor < count($args))
|
|
|
|
{
|
|
|
|
$arg = $args[$cursor];
|
|
|
|
$cursor++;
|
|
|
|
|
|
|
|
// simple string means SQL
|
|
|
|
if (is_string($arg)) {
|
|
|
|
// speed-up - is regexp required?
|
2009-02-22 18:58:29 +00:00
|
|
|
$toSkip = strcspn($arg, '`[\'":%');
|
2008-07-17 03:51:29 +00:00
|
|
|
|
|
|
|
if (strlen($arg) === $toSkip) { // needn't be translated
|
|
|
|
$sql[] = $arg;
|
|
|
|
} else {
|
|
|
|
$sql[] = substr($arg, 0, $toSkip)
|
|
|
|
/*
|
|
|
|
preg_replace_callback('/
|
2009-05-11 18:43:30 +00:00
|
|
|
(?=[`[\'":%?]) ## speed-up
|
2008-07-17 03:51:29 +00:00
|
|
|
(?:
|
|
|
|
`(.+?)`| ## 1) `identifier`
|
|
|
|
\[(.+?)\]| ## 2) [identifier]
|
2009-03-08 23:27:31 +00:00
|
|
|
(\')((?:\'\'|[^\'])*)\'| ## 3,4) 'string'
|
2008-07-17 03:51:29 +00:00
|
|
|
(")((?:""|[^"])*)"| ## 5,6) "string"
|
2009-02-22 18:35:18 +00:00
|
|
|
(\'|")| ## 7) lone quote
|
2009-03-08 23:27:31 +00:00
|
|
|
:(\S*?:)([a-zA-Z0-9._]?)| ## 8,9) substitution
|
|
|
|
%([a-zA-Z]{1,4})(?![a-zA-Z]) ## 10) modifier
|
2009-05-11 18:43:30 +00:00
|
|
|
(\?) ## 11) placeholder
|
2008-07-17 03:51:29 +00:00
|
|
|
)/xs',
|
|
|
|
*/ // note: this can change $this->args & $this->cursor & ...
|
2009-05-11 18:43:30 +00:00
|
|
|
. preg_replace_callback('/(?=[`[\'":%?])(?:`(.+?)`|\[(.+?)\]|(\')((?:\'\'|[^\'])*)\'|(")((?:""|[^"])*)"|(\'|")|:(\S*?:)([a-zA-Z0-9._]?)|%([a-zA-Z]{1,4})(?![a-zA-Z])|(\?))/s',
|
2008-07-17 03:51:29 +00:00
|
|
|
array($this, 'cb'),
|
|
|
|
substr($arg, $toSkip)
|
|
|
|
);
|
|
|
|
|
|
|
|
}
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($comment) {
|
|
|
|
$sql[] = '...';
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2009-02-25 20:10:17 +00:00
|
|
|
if ($arg instanceof ArrayObject) {
|
|
|
|
$arg = (array) $arg;
|
|
|
|
}
|
|
|
|
|
2008-07-17 03:51:29 +00:00
|
|
|
if (is_array($arg)) {
|
|
|
|
if (is_string(key($arg))) {
|
|
|
|
// associative array -> autoselect between SET or VALUES & LIST
|
|
|
|
if ($commandIns === NULL) {
|
|
|
|
$commandIns = strtoupper(substr(ltrim($args[0]), 0, 6));
|
|
|
|
$commandIns = $commandIns === 'INSERT' || $commandIns === 'REPLAC';
|
|
|
|
$sql[] = $this->formatValue($arg, $commandIns ? 'v' : 'a');
|
|
|
|
} else {
|
|
|
|
if ($lastArr === $cursor - 1) $sql[] = ',';
|
|
|
|
$sql[] = $this->formatValue($arg, $commandIns ? 'l' : 'a');
|
|
|
|
}
|
|
|
|
$lastArr = $cursor;
|
|
|
|
continue;
|
|
|
|
|
|
|
|
} elseif ($cursor === 1) {
|
|
|
|
// implicit array expansion
|
|
|
|
$cursor = 0;
|
|
|
|
array_splice($args, 0, 1, $arg);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// default processing
|
|
|
|
$sql[] = $this->formatValue($arg, FALSE);
|
|
|
|
} // while
|
|
|
|
|
|
|
|
|
|
|
|
if ($comment) $sql[] = "*/";
|
|
|
|
|
|
|
|
$sql = implode(' ', $sql);
|
|
|
|
|
2009-02-05 21:08:00 +00:00
|
|
|
if ($this->hasError) {
|
|
|
|
throw new DibiException('SQL translate error', 0, $sql);
|
|
|
|
}
|
|
|
|
|
2008-07-17 03:51:29 +00:00
|
|
|
// apply limit
|
|
|
|
if ($this->limit > -1 || $this->offset > 0) {
|
|
|
|
$this->driver->applyLimit($sql, $this->limit, $this->offset);
|
|
|
|
}
|
|
|
|
|
2009-02-05 21:08:00 +00:00
|
|
|
return $sql;
|
2008-07-17 03:51:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Apply modifier to single value.
|
|
|
|
* @param mixed
|
|
|
|
* @param string
|
|
|
|
* @return string
|
|
|
|
*/
|
|
|
|
public function formatValue($value, $modifier)
|
|
|
|
{
|
|
|
|
// array processing (with or without modifier)
|
2009-02-25 20:10:17 +00:00
|
|
|
if ($value instanceof ArrayObject) {
|
|
|
|
$value = (array) $value;
|
|
|
|
}
|
2008-07-17 03:51:29 +00:00
|
|
|
|
2009-02-25 20:10:17 +00:00
|
|
|
if (is_array($value)) {
|
2008-07-17 03:51:29 +00:00
|
|
|
$vx = $kx = array();
|
|
|
|
switch ($modifier) {
|
|
|
|
case 'and':
|
2008-09-05 05:35:15 +00:00
|
|
|
case 'or': // key=val AND key IS NULL AND ...
|
|
|
|
if (empty($value)) {
|
2009-04-26 15:35:39 +00:00
|
|
|
return '1=1';
|
2009-02-05 21:08:00 +00:00
|
|
|
}
|
2008-09-05 05:35:15 +00:00
|
|
|
|
2009-02-05 21:08:00 +00:00
|
|
|
foreach ($value as $k => $v) {
|
2009-01-07 13:38:29 +00:00
|
|
|
if (is_string($k)) {
|
2008-09-05 05:35:15 +00:00
|
|
|
$pair = explode('%', $k, 2); // split into identifier & modifier
|
2009-01-07 17:30:01 +00:00
|
|
|
$k = $this->delimite($pair[0]) . ' ';
|
|
|
|
if (!isset($pair[1])) {
|
|
|
|
$v = $this->formatValue($v, FALSE);
|
|
|
|
$vx[] = $k . ($v === 'NULL' ? 'IS ' : '= ') . $v;
|
|
|
|
|
2009-02-05 21:10:50 +00:00
|
|
|
} elseif ($pair[1] === 'ex') { // TODO: this will be removed
|
|
|
|
$vx[] = $k . $this->formatValue($v, 'ex');
|
2009-01-07 17:30:01 +00:00
|
|
|
|
|
|
|
} else {
|
|
|
|
$v = $this->formatValue($v, $pair[1]);
|
|
|
|
$vx[] = $k . ($pair[1] === 'l' ? 'IN ' : ($v === 'NULL' ? 'IS ' : '= ')) . $v;
|
|
|
|
}
|
2009-01-07 13:38:29 +00:00
|
|
|
|
|
|
|
} else {
|
2009-02-05 21:10:50 +00:00
|
|
|
$vx[] = $this->formatValue($v, 'ex');
|
2008-09-05 05:35:15 +00:00
|
|
|
}
|
2008-07-17 03:51:29 +00:00
|
|
|
}
|
2009-06-19 11:03:24 +00:00
|
|
|
return '(' . implode(') ' . strtoupper($modifier) . ' (', $vx) . ')';
|
2008-09-05 05:35:15 +00:00
|
|
|
|
2009-08-13 12:20:03 +00:00
|
|
|
case 'n': // key, key, ... identifier names
|
2009-02-02 17:50:02 +00:00
|
|
|
foreach ($value as $k => $v) {
|
2009-08-13 12:20:03 +00:00
|
|
|
$pair = explode('%', $k, 2); // split into identifier & modifier
|
2009-02-02 17:50:02 +00:00
|
|
|
if (is_string($k)) {
|
2009-08-13 12:20:03 +00:00
|
|
|
$vx[] = $this->delimite($pair[0]) . (empty($v) ? '' : ' AS ' . $v);
|
2009-02-02 17:50:02 +00:00
|
|
|
} else {
|
2009-08-13 12:20:03 +00:00
|
|
|
$vx[] = $this->delimite($pair[0]);
|
2009-02-02 17:50:02 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return implode(', ', $vx);
|
|
|
|
|
|
|
|
|
2008-09-05 05:35:15 +00:00
|
|
|
case 'a': // key=val, key=val, ...
|
2008-07-17 03:51:29 +00:00
|
|
|
foreach ($value as $k => $v) {
|
|
|
|
$pair = explode('%', $k, 2); // split into identifier & modifier
|
|
|
|
$vx[] = $this->delimite($pair[0]) . '='
|
2009-06-02 09:05:13 +00:00
|
|
|
. $this->formatValue($v, isset($pair[1]) ? $pair[1] : (is_array($v) ? 'ex' : FALSE));
|
2008-07-17 03:51:29 +00:00
|
|
|
}
|
2009-02-05 21:08:00 +00:00
|
|
|
return implode(', ', $vx);
|
2008-07-17 03:51:29 +00:00
|
|
|
|
|
|
|
|
2008-09-05 05:35:15 +00:00
|
|
|
case 'l': // (val, val, ...)
|
2008-07-17 03:51:29 +00:00
|
|
|
foreach ($value as $k => $v) {
|
|
|
|
$pair = explode('%', $k, 2); // split into identifier & modifier
|
2009-06-02 09:05:13 +00:00
|
|
|
$vx[] = $this->formatValue($v, isset($pair[1]) ? $pair[1] : (is_array($v) ? 'ex' : FALSE));
|
2008-07-17 03:51:29 +00:00
|
|
|
}
|
2009-04-16 02:15:20 +00:00
|
|
|
return '(' . ($vx ? implode(', ', $vx) : 'NULL') . ')';
|
2008-07-17 03:51:29 +00:00
|
|
|
|
|
|
|
|
|
|
|
case 'v': // (key, key, ...) VALUES (val, val, ...)
|
|
|
|
foreach ($value as $k => $v) {
|
|
|
|
$pair = explode('%', $k, 2); // split into identifier & modifier
|
|
|
|
$kx[] = $this->delimite($pair[0]);
|
2009-06-02 09:05:13 +00:00
|
|
|
$vx[] = $this->formatValue($v, isset($pair[1]) ? $pair[1] : (is_array($v) ? 'ex' : FALSE));
|
2008-07-17 03:51:29 +00:00
|
|
|
}
|
|
|
|
return '(' . implode(', ', $kx) . ') VALUES (' . implode(', ', $vx) . ')';
|
|
|
|
|
2009-07-08 12:32:01 +00:00
|
|
|
case 'm': // (key, key, ...) VALUES (val, val, ...), (val, val, ...), ...
|
|
|
|
foreach ($value as $k => $v) {
|
2009-07-08 12:42:05 +00:00
|
|
|
if (is_array($v)) {
|
|
|
|
if (isset($proto)) {
|
|
|
|
if ($proto !== array_keys($v)) {
|
|
|
|
$this->hasError = TRUE;
|
|
|
|
return '**Multi-insert array "' . $k . '" is different.**';
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
$proto = array_keys($v);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
$this->hasError = TRUE;
|
|
|
|
return '**Unexpected type ' . gettype($v) . '**';
|
|
|
|
}
|
|
|
|
|
2009-07-08 12:32:01 +00:00
|
|
|
$pair = explode('%', $k, 2); // split into identifier & modifier
|
|
|
|
$kx[] = $this->delimite($pair[0]);
|
|
|
|
foreach ($v as $k2 => $v2) {
|
|
|
|
$vx[$k2][] = $this->formatValue($v2, isset($pair[1]) ? $pair[1] : (is_array($v2) ? 'ex' : FALSE));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
foreach ($vx as $k => $v) {
|
|
|
|
$vx[$k] = '(' . ($v ? implode(', ', $v) : 'NULL') . ')';
|
|
|
|
}
|
|
|
|
return '(' . implode(', ', $kx) . ') VALUES ' . implode(', ', $vx);
|
|
|
|
|
2008-10-01 16:04:16 +00:00
|
|
|
case 'by': // key ASC, key DESC
|
|
|
|
foreach ($value as $k => $v) {
|
2008-10-01 16:23:44 +00:00
|
|
|
if (is_string($k)) {
|
2008-10-28 01:03:50 +00:00
|
|
|
$v = (is_string($v) && strncasecmp($v, 'd', 1)) || $v > 0 ? 'ASC' : 'DESC';
|
2008-10-01 16:23:44 +00:00
|
|
|
$vx[] = $this->delimite($k) . ' ' . $v;
|
|
|
|
} else {
|
|
|
|
$vx[] = $this->delimite($v);
|
|
|
|
}
|
2008-10-01 16:04:16 +00:00
|
|
|
}
|
|
|
|
return implode(', ', $vx);
|
|
|
|
|
2009-02-05 21:10:50 +00:00
|
|
|
case 'ex':
|
2009-01-07 13:38:29 +00:00
|
|
|
case 'sql':
|
|
|
|
$translator = new self($this->driver);
|
2009-02-05 21:08:00 +00:00
|
|
|
return $translator->translate($value);
|
2009-01-07 13:38:29 +00:00
|
|
|
|
2008-09-05 05:35:15 +00:00
|
|
|
default: // value, value, value - all with the same modifier
|
2008-07-17 03:51:29 +00:00
|
|
|
foreach ($value as $v) {
|
|
|
|
$vx[] = $this->formatValue($v, $modifier);
|
|
|
|
}
|
2009-04-16 02:15:20 +00:00
|
|
|
return $vx ? implode(', ', $vx) : 'NULL';
|
2008-07-17 03:51:29 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// with modifier procession
|
|
|
|
if ($modifier) {
|
|
|
|
if ($value instanceof IDibiVariable) {
|
|
|
|
return $value->toSql($this, $modifier);
|
|
|
|
|
2009-02-05 01:26:08 +00:00
|
|
|
} elseif ($value instanceof DateTime) {
|
|
|
|
$value = $value->format('U');
|
|
|
|
|
2009-03-08 17:36:11 +00:00
|
|
|
} elseif ($value !== NULL && !is_scalar($value)) { // array is already processed
|
2008-07-17 03:51:29 +00:00
|
|
|
$this->hasError = TRUE;
|
|
|
|
return '**Unexpected type ' . gettype($value) . '**';
|
|
|
|
}
|
|
|
|
|
|
|
|
switch ($modifier) {
|
|
|
|
case 's': // string
|
|
|
|
case 'bin':// binary
|
|
|
|
case 'b': // boolean
|
2009-03-08 17:36:11 +00:00
|
|
|
return $value === NULL ? 'NULL' : $this->driver->escape($value, $modifier);
|
2008-07-17 03:51:29 +00:00
|
|
|
|
|
|
|
case 'sn': // string or NULL
|
2009-03-16 06:48:27 +00:00
|
|
|
return $value == '' ? 'NULL' : $this->driver->escape($value, dibi::TEXT); // notice two equal signs
|
2008-07-17 03:51:29 +00:00
|
|
|
|
2009-06-03 13:42:02 +00:00
|
|
|
case 'in': // signed int or NULL
|
|
|
|
if ($value == '') $value = NULL;
|
|
|
|
// intentionally break omitted
|
|
|
|
|
2008-07-17 03:51:29 +00:00
|
|
|
case 'i': // signed int
|
|
|
|
case 'u': // unsigned int, ignored
|
2008-10-02 09:01:38 +00:00
|
|
|
// support for long numbers - keep them unchanged
|
2008-07-17 03:51:29 +00:00
|
|
|
if (is_string($value) && preg_match('#[+-]?\d+(e\d+)?$#A', $value)) {
|
|
|
|
return $value;
|
2009-03-08 17:36:11 +00:00
|
|
|
} else {
|
|
|
|
return $value === NULL ? 'NULL' : (string) (int) ($value + 0);
|
2008-07-17 03:51:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
case 'f': // float
|
2008-10-02 09:01:38 +00:00
|
|
|
// support for extreme numbers - keep them unchanged
|
|
|
|
if (is_string($value) && is_numeric($value) && strpos($value, 'x') === FALSE) {
|
|
|
|
return $value; // something like -9E-005 is accepted by SQL, HEX values are not
|
2009-03-08 17:36:11 +00:00
|
|
|
} else {
|
|
|
|
return $value === NULL ? 'NULL' : rtrim(rtrim(number_format($value, 5, '.', ''), '0'), '.');
|
2008-07-17 03:51:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
case 'd': // date
|
|
|
|
case 't': // datetime
|
2009-03-08 17:36:11 +00:00
|
|
|
if ($value === NULL) {
|
|
|
|
return 'NULL';
|
|
|
|
} else {
|
|
|
|
return $this->driver->escape(is_numeric($value) ? (int) $value : strtotime($value), $modifier);
|
|
|
|
}
|
2008-07-17 03:51:29 +00:00
|
|
|
|
2008-10-01 16:23:44 +00:00
|
|
|
case 'by':
|
2008-07-17 03:51:29 +00:00
|
|
|
case 'n': // identifier name
|
|
|
|
return $this->delimite($value);
|
|
|
|
|
2009-02-05 21:10:50 +00:00
|
|
|
case 'ex':
|
|
|
|
case 'sql': // preserve as dibi-SQL (TODO: leave only %ex)
|
2008-07-17 03:51:29 +00:00
|
|
|
$value = (string) $value;
|
|
|
|
// speed-up - is regexp required?
|
2009-02-22 18:58:29 +00:00
|
|
|
$toSkip = strcspn($value, '`[\'":');
|
2008-07-17 03:51:29 +00:00
|
|
|
if (strlen($value) === $toSkip) { // needn't be translated
|
|
|
|
return $value;
|
|
|
|
} else {
|
|
|
|
return substr($value, 0, $toSkip)
|
2009-02-22 18:58:29 +00:00
|
|
|
. preg_replace_callback(
|
2009-03-08 23:27:31 +00:00
|
|
|
'/(?=[`[\'":])(?:`(.+?)`|\[(.+?)\]|(\')((?:\'\'|[^\'])*)\'|(")((?:""|[^"])*)"|(\'|")|:(\S*?:)([a-zA-Z0-9._]?))/s',
|
2009-02-22 18:58:29 +00:00
|
|
|
array($this, 'cb'),
|
|
|
|
substr($value, $toSkip)
|
2008-07-17 03:51:29 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2009-02-05 21:10:50 +00:00
|
|
|
case 'SQL': // preserve as real SQL (TODO: rename to %sql)
|
|
|
|
return (string) $value;
|
|
|
|
|
2008-07-17 03:51:29 +00:00
|
|
|
case 'and':
|
|
|
|
case 'or':
|
|
|
|
case 'a':
|
|
|
|
case 'l':
|
|
|
|
case 'v':
|
|
|
|
$this->hasError = TRUE;
|
|
|
|
return '**Unexpected type ' . gettype($value) . '**';
|
|
|
|
|
|
|
|
default:
|
|
|
|
$this->hasError = TRUE;
|
|
|
|
return "**Unknown or invalid modifier %$modifier**";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// without modifier procession
|
|
|
|
if (is_string($value))
|
2009-03-16 06:48:27 +00:00
|
|
|
return $this->driver->escape($value, dibi::TEXT);
|
2008-07-17 03:51:29 +00:00
|
|
|
|
|
|
|
if (is_int($value) || is_float($value))
|
2008-10-02 09:01:38 +00:00
|
|
|
return rtrim(rtrim(number_format($value, 5, '.', ''), '0'), '.');
|
2008-07-17 03:51:29 +00:00
|
|
|
|
|
|
|
if (is_bool($value))
|
2009-03-16 06:48:27 +00:00
|
|
|
return $this->driver->escape($value, dibi::BOOL);
|
2008-07-17 03:51:29 +00:00
|
|
|
|
|
|
|
if ($value === NULL)
|
|
|
|
return 'NULL';
|
|
|
|
|
|
|
|
if ($value instanceof IDibiVariable)
|
|
|
|
return $value->toSql($this, NULL);
|
|
|
|
|
2009-05-11 18:43:30 +00:00
|
|
|
if ($value instanceof DateTime)
|
|
|
|
return $value = $value->format('U');
|
|
|
|
|
2008-07-17 03:51:29 +00:00
|
|
|
$this->hasError = TRUE;
|
|
|
|
return '**Unexpected ' . gettype($value) . '**';
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* PREG callback from translate() or formatValue().
|
|
|
|
* @param array
|
|
|
|
* @return string
|
|
|
|
*/
|
|
|
|
private function cb($matches)
|
|
|
|
{
|
|
|
|
// [1] => `ident`
|
|
|
|
// [2] => [ident]
|
|
|
|
// [3] => '
|
|
|
|
// [4] => string
|
|
|
|
// [5] => "
|
|
|
|
// [6] => string
|
|
|
|
// [7] => lone-quote
|
2009-02-22 18:58:29 +00:00
|
|
|
// [8] => substitution
|
2009-03-08 23:27:31 +00:00
|
|
|
// [9] => substitution flag
|
|
|
|
// [10] => modifier (when called from self::translate())
|
2009-05-11 18:43:30 +00:00
|
|
|
// [11] => placeholder (when called from self::translate())
|
|
|
|
|
|
|
|
|
|
|
|
if (!empty($matches[11])) { // placeholder
|
|
|
|
$cursor = & $this->cursor;
|
|
|
|
|
|
|
|
if ($cursor >= count($this->args)) {
|
|
|
|
$this->hasError = TRUE;
|
|
|
|
return "**Extra placeholder**";
|
|
|
|
}
|
|
|
|
|
|
|
|
$cursor++;
|
|
|
|
return $this->formatValue($this->args[$cursor - 1], FALSE);
|
|
|
|
}
|
2008-07-17 03:51:29 +00:00
|
|
|
|
2009-03-08 23:27:31 +00:00
|
|
|
if (!empty($matches[10])) { // modifier
|
|
|
|
$mod = $matches[10];
|
2008-07-17 03:51:29 +00:00
|
|
|
$cursor = & $this->cursor;
|
|
|
|
|
|
|
|
if ($cursor >= count($this->args) && $mod !== 'else' && $mod !== 'end') {
|
|
|
|
$this->hasError = TRUE;
|
|
|
|
return "**Extra modifier %$mod**";
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($mod === 'if') {
|
|
|
|
$this->ifLevel++;
|
|
|
|
$cursor++;
|
|
|
|
if (!$this->comment && !$this->args[$cursor - 1]) {
|
|
|
|
// open comment
|
|
|
|
$this->ifLevelStart = $this->ifLevel;
|
|
|
|
$this->comment = TRUE;
|
|
|
|
return "/*";
|
|
|
|
}
|
|
|
|
return '';
|
|
|
|
|
|
|
|
} elseif ($mod === 'else') {
|
|
|
|
if ($this->ifLevelStart === $this->ifLevel) {
|
|
|
|
$this->ifLevelStart = 0;
|
|
|
|
$this->comment = FALSE;
|
|
|
|
return "*/";
|
|
|
|
} elseif (!$this->comment) {
|
|
|
|
$this->ifLevelStart = $this->ifLevel;
|
|
|
|
$this->comment = TRUE;
|
|
|
|
return "/*";
|
|
|
|
}
|
|
|
|
|
|
|
|
} elseif ($mod === 'end') {
|
|
|
|
$this->ifLevel--;
|
|
|
|
if ($this->ifLevelStart === $this->ifLevel + 1) {
|
|
|
|
// close comment
|
|
|
|
$this->ifLevelStart = 0;
|
|
|
|
$this->comment = FALSE;
|
|
|
|
return "*/";
|
|
|
|
}
|
|
|
|
return '';
|
|
|
|
|
|
|
|
} elseif ($mod === 'ex') { // array expansion
|
|
|
|
array_splice($this->args, $cursor, 1, $this->args[$cursor]);
|
|
|
|
return '';
|
|
|
|
|
|
|
|
} elseif ($mod === 'lmt') { // apply limit
|
|
|
|
if ($this->args[$cursor] !== NULL) $this->limit = (int) $this->args[$cursor];
|
|
|
|
$cursor++;
|
|
|
|
return '';
|
|
|
|
|
|
|
|
} elseif ($mod === 'ofs') { // apply offset
|
|
|
|
if ($this->args[$cursor] !== NULL) $this->offset = (int) $this->args[$cursor];
|
|
|
|
$cursor++;
|
|
|
|
return '';
|
|
|
|
|
|
|
|
} else { // default processing
|
|
|
|
$cursor++;
|
|
|
|
return $this->formatValue($this->args[$cursor - 1], $mod);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($this->comment) return '...';
|
|
|
|
|
|
|
|
if ($matches[1]) // SQL identifiers: `ident`
|
|
|
|
return $this->delimite($matches[1]);
|
|
|
|
|
|
|
|
if ($matches[2]) // SQL identifiers: [ident]
|
|
|
|
return $this->delimite($matches[2]);
|
|
|
|
|
|
|
|
if ($matches[3]) // SQL strings: '...'
|
2009-03-16 06:48:27 +00:00
|
|
|
return $this->driver->escape( str_replace("''", "'", $matches[4]), dibi::TEXT);
|
2008-07-17 03:51:29 +00:00
|
|
|
|
|
|
|
if ($matches[5]) // SQL strings: "..."
|
2009-03-16 06:48:27 +00:00
|
|
|
return $this->driver->escape( str_replace('""', '"', $matches[6]), dibi::TEXT);
|
2008-07-17 03:51:29 +00:00
|
|
|
|
|
|
|
if ($matches[7]) { // string quote
|
|
|
|
$this->hasError = TRUE;
|
|
|
|
return '**Alone quote**';
|
|
|
|
}
|
|
|
|
|
2009-02-22 18:58:29 +00:00
|
|
|
if ($matches[8]) { // SQL identifier substitution
|
2009-03-08 23:27:31 +00:00
|
|
|
$m = substr($matches[8], 0, -1);
|
|
|
|
$m = isset(dibi::$substs[$m]) ? dibi::$substs[$m] : call_user_func(dibi::$substFallBack, $m);
|
|
|
|
return $matches[9] == '' ? $this->formatValue($m, FALSE) : $m . $matches[9]; // value or identifier
|
2009-02-22 18:58:29 +00:00
|
|
|
}
|
|
|
|
|
2008-07-17 03:51:29 +00:00
|
|
|
die('this should be never executed');
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Apply substitutions to indentifier and delimites it.
|
|
|
|
* @param string indentifier
|
|
|
|
* @return string
|
|
|
|
*/
|
|
|
|
private function delimite($value)
|
|
|
|
{
|
2009-02-22 18:35:18 +00:00
|
|
|
if ($value === '*') {
|
|
|
|
return '*';
|
|
|
|
|
|
|
|
} elseif (strpos($value, ':') !== FALSE) { // provide substitution
|
|
|
|
$value = preg_replace_callback('#:(.*):#U', array(__CLASS__, 'subCb'), $value);
|
|
|
|
}
|
|
|
|
|
|
|
|
return $this->driver->escape($value, dibi::IDENTIFIER);
|
2008-07-17 03:51:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-02-22 18:35:18 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Substitution callback.
|
|
|
|
* @param array
|
|
|
|
* @return string
|
|
|
|
*/
|
|
|
|
private static function subCb($m)
|
|
|
|
{
|
|
|
|
$m = $m[1];
|
2009-03-08 23:27:31 +00:00
|
|
|
return isset(dibi::$substs[$m]) ? dibi::$substs[$m] : call_user_func(dibi::$substFallBack, $m);
|
2009-02-22 18:35:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|