diff --git a/.travis.yml b/.travis.yml index ad64353..56fb1ec 100644 --- a/.travis.yml +++ b/.travis.yml @@ -22,7 +22,7 @@ services: sudo: false install: - - composer install --no-interaction + - travis_retry composer install --no-interaction before_script: - mysql -e 'CREATE DATABASE IF NOT EXISTS test;' diff --git a/src/Command.php b/src/Command.php index 81d9eea..23f5fe3 100644 --- a/src/Command.php +++ b/src/Command.php @@ -126,6 +126,9 @@ abstract class Command extends EventEmitter implements CommandInterface */ const INIT_AUTHENTICATE = 0xf1; + /** + * @var Connection + */ protected $connection; private $states = []; @@ -133,10 +136,9 @@ abstract class Command extends EventEmitter implements CommandInterface private $error; /** - * Construtor. + * Constructor. * - * @param integer $cmd - * @param string $q + * @param Connection $connection */ public function __construct(Connection $connection) { diff --git a/src/Connection.php b/src/Connection.php index 7978cd6..1a65e88 100644 --- a/src/Connection.php +++ b/src/Connection.php @@ -75,12 +75,15 @@ class Connection extends EventEmitter implements ConnectionInterface public function __construct(LoopInterface $loop, array $connectOptions = array(), ConnectorInterface $connector = null) { $this->loop = $loop; + if (!$connector) { $connector = new Connector($loop); } $this->connector = $connector; + $this->executor = new Executor($this); - $this->options = $connectOptions + $this->options; + + $this->options = array_replace($this->options, $connectOptions); } /** @@ -262,7 +265,7 @@ public function getServerOptions() } /** - * @param Exception $err Error from socket. + * @param \Exception $err Error from socket. * * @return void * @internal @@ -293,12 +296,14 @@ public function handleConnectionClosed() */ protected function _doCommand(Command $command) { - if ($command->equals(Command::INIT_AUTHENTICATE)) { - return $this->executor->undequeue($command); - } elseif ($this->state >= self::STATE_CONNECTING && $this->state <= self::STATE_AUTHENTICATED) { - return $this->executor->enqueue($command); - } else { - throw new Exception("Can't send command"); - } + if ($command->equals(Command::INIT_AUTHENTICATE)) { + return $this->executor->undequeue($command); + } + + if ($this->state >= self::STATE_CONNECTING && $this->state <= self::STATE_AUTHENTICATED) { + return $this->executor->enqueue($command); + } + + throw new Exception("Can't send command"); } } diff --git a/src/Protocal/Binary.php b/src/Protocal/Binary.php index 1aa732e..1b826bc 100644 --- a/src/Protocal/Binary.php +++ b/src/Protocal/Binary.php @@ -6,14 +6,16 @@ class Binary { /** * Build structure of labels - * @param string Dot-separated labels list - * @return \PHPDaemon\Utils\binary + * + * @param string $q Dot-separated labels list + * + * @return string */ public static function labels($q) { $e = explode('.', $q); $r = ''; - for ($i = 0, $s = sizeof($e); $i < $s; ++$i) { + for ($i = 0, $s = count($e); $i < $s; ++$i) { $r .= chr(strlen($e[$i])) . $e[$i]; } if (binarySubstr($r, -1) !== "\x00") { @@ -25,7 +27,10 @@ public static function labels($q) /** * Parse structure of labels - * @param binary + * + * @param string $data + * @param string $orig + * * @return string Dot-separated labels list */ public static function parseLabels(&$data, $orig = null) @@ -35,11 +40,11 @@ public static function parseLabels(&$data, $orig = null) $l = ord($data[0]); if ($l >= 192) { - $pos = Binary::bytes2int(chr($l - 192) . binarySubstr($data, 1, 1)); + $pos = self::bytes2int(chr($l - 192) . binarySubstr($data, 1, 1)); $data = binarySubstr($data, 2); $ref = binarySubstr($orig, $pos); - return $str . Binary::parseLabels($ref); + return $str . self::parseLabels($ref); } $p = substr($data, 1, $l); @@ -55,11 +60,14 @@ public static function parseLabels(&$data, $orig = null) /** * Build length-value binary snippet - * @param string Data - * @param [string Number of bytes to encode length. Default is 1 - * @return \PHPDaemon\Utils\binary + * + * @param string $str Data + * @param int $len Number of bytes to encode length. Default is 1 + * @param bool $lrev + * + * @return string */ - public static function LV($str, $len = 1, $lrev = FALSE) + public static function LV($str, $len = 1, $lrev = false) { $l = static::i2b($len, strlen($str)); if ($lrev) { @@ -71,8 +79,10 @@ public static function LV($str, $len = 1, $lrev = FALSE) /** * Build nul-terminated string, with 2-byte of length - * @param string Data - * @return \PHPDaemon\Utils\binary + * + * @param string $str Data + * + * @return string */ public static function LVnull($str) { @@ -81,8 +91,10 @@ public static function LVnull($str) /** * Build byte - * @param integer Byte number - * @return \PHPDaemon\Utils\binary + * + * @param int $int Byte number + * + * @return string */ public static function byte($int) { @@ -91,8 +103,10 @@ public static function byte($int) /** * Build word (2 bytes) big-endian - * @param integer Integer - * @return \PHPDaemon\Utils\binary + * + * @param int $int Integer + * + * @return string */ public static function word($int) { @@ -101,8 +115,10 @@ public static function word($int) /** * Build word (2 bytes) little-endian - * @param integer Integer - * @return \PHPDaemon\Utils\binary + * + * @param int $int Integer + * + * @return string */ public static function wordl($int) { @@ -111,8 +127,10 @@ public static function wordl($int) /** * Build double word (4 bytes) big-endian - * @param integer Integer - * @return \PHPDaemon\Utils\binary + * + * @param int $int Integer + * + * @return string */ public static function dword($int) { @@ -121,8 +139,10 @@ public static function dword($int) /** * Build double word (4 bytes) little endian - * @param integer Integer - * @return \PHPDaemon\Utils\binary + * + * @param int $int Integer + * + * @return string */ public static function dwordl($int) { @@ -131,8 +151,10 @@ public static function dwordl($int) /** * Build quadro word (8 bytes) big endian - * @param integer Integer - * @return \PHPDaemon\Utils\binary + * + * @param int $int Integer + * + * @return string */ public static function qword($int) { @@ -141,9 +163,11 @@ public static function qword($int) /** * Build quadro word (8 bytes) little endian - * @param integer Integer - * @return \PHPDaemon\Utils\binary - */ + * + * @param int $int Integer + * + * @return string + */ public static function qwordl($int) { return strrev(static::qword($int)); @@ -151,8 +175,10 @@ public static function qwordl($int) /** * Parse byte, and remove it - * @param &string Data - * @return integer + * + * @param string $p &Data + * + * @return int */ public static function getByte(&$p) { @@ -164,7 +190,9 @@ public static function getByte(&$p) /** * Get single-byte character - * @param &string Data + * + * @param string $p &Data + * * @return string */ public static function getChar(&$p) @@ -177,23 +205,27 @@ public static function getChar(&$p) /** * Parse word (2 bytes) - * @param &string Data - * @param boolean Little endian? - * @return integer + * + * @param string $p &Data + * @param bool $l Little endian? + * + * @return int */ public static function getWord(&$p, $l = false) { - $r = static::bytes2int(binarySubstr($p, 0, 2), !!$l); + $r = static::bytes2int(binarySubstr($p, 0, 2), (bool)$l); $p = binarySubstr($p, 2); - return intval($r); + return (int)$r; } /** * Get word (2 bytes) - * @param &string Data - * @param boolean Little endian? - * @return \PHPDaemon\Utils\binary + * + * @param string $p &data + * @param bool $l Little endian? + * + * @return bool|string */ public static function getStrWord(&$p, $l = false) { @@ -208,37 +240,43 @@ public static function getStrWord(&$p, $l = false) /** * Get double word (4 bytes) - * @param &string Data - * @param boolean Little endian? - * @return integer + * + * @param string $p &Data + * @param bool $l Little endian? + * + * @return int */ public static function getDWord(&$p, $l = false) { - $r = static::bytes2int(binarySubstr($p, 0, 4), !!$l); + $r = static::bytes2int(binarySubstr($p, 0, 4), (bool)$l); $p = binarySubstr($p, 4); - return intval($r); + return (int)$r; } /** * Parse quadro word (8 bytes) - * @param &string Data - * @param boolean Little endian? - * @return integer + * + * @param string $p &Data + * @param bool $l Little endian? + * + * @return int */ public static function getQword(&$p, $l = false) { - $r = static::bytes2int(binarySubstr($p, 0, 8), !!$l); + $r = static::bytes2int(binarySubstr($p, 0, 8), (bool)$l); $p = binarySubstr($p, 8); - return intval($r); + return (int)$r; } /** * Get quadro word (8 bytes) - * @param &string Data - * @param boolean Little endian? - * @return \PHPDaemon\Utils\binary + * + * @param string $p &data + * @param bool $l Little endian? + * + * @return bool|string */ public static function getStrQWord(&$p, $l = false) { @@ -252,9 +290,11 @@ public static function getStrQWord(&$p, $l = false) } /** - * Parse nul-terminated string - * @param &string Data - * @return \PHPDaemon\Utils\binary + * Parse null-terminated string + * + * @param string $str &Data + * + * @return bool|string */ public static function getString(&$str) { @@ -270,24 +310,28 @@ public static function getString(&$str) /** * Parse length-value structure - * @param &string Data - * @param number Number of length bytes - * @param boolean Nul-terminated? Default is false - * @param boolean Length is little endian? + * + * @param string $p &Data + * @param int $l Number of length bytes + * @param bool $nul Nul-terminated? Default is false + * @param bool $lrev Length is little endian? + * * @return string */ public static function getLV(&$p, $l = 1, $nul = false, $lrev = false) { - $s = static::b2i(binarySubstr($p, 0, $l), !!$lrev); + $s = static::b2i(binarySubstr($p, 0, $l), (bool)$lrev); $p = binarySubstr($p, $l); if ($s == 0) { return ''; } $r = ''; if (strlen($p) < $s) { + // TODO? -> "Debug" is not defined echo("getLV error: buf length (" . strlen($p) . "): " . Debug::exportBytes($p) . ", must be >= string length (" . $s . ")\n"); } elseif ($nul) { if ($p{$s - 1} != "\x00") { + // TODO? -> "Debug" is not defined echo("getLV error: Wrong end of NUL-string (" . Debug::exportBytes($p{$s - 1}) . "), len " . $s . "\n"); } else { $d = $s - 1; @@ -307,16 +351,18 @@ public static function getLV(&$p, $l = 1, $nul = false, $lrev = false) /** * Converts integer to binary string - * @param integer Length - * @param integer Integer - * @param boolean Optional. Little endian. Default value - false. + * + * @param int $len Length + * @param int $int Integer + * @param bool $l Optional. Little endian. Default value - false. + * * @return string Resulting binary string */ public static function int2bytes($len, $int = 0, $l = false) { $hexstr = dechex($int); - if ($len === NULL) { + if ($len === null) { if (strlen($hexstr) % 2) { $hexstr = "0" . $hexstr; } @@ -336,8 +382,10 @@ public static function int2bytes($len, $int = 0, $l = false) /** * Convert array of flags into bit array - * @param array Flags - * @param integer Length. Default is 4 + * + * @param array $flags Flags + * @param int $len Length. Default is 4 + * * @return string */ public static function flags2bitarray($flags, $len = 4) @@ -360,9 +408,11 @@ public static function i2b($bytes, $int = 0, $l = false) /** * Convert bytes into integer - * @param string Bytes - * @param boolean Little endian? Default is false - * @return integer + * + * @param string $str Bytes + * @param bool $l Little endian? Default is false + * + * @return int */ public static function bytes2int($str, $l = false) { @@ -388,9 +438,11 @@ public static function b2i($hex = 0, $l = false) /** * Convert bitmap into bytes - * @param string Bitmap - * @param boolean Check length? - * @return \PHPDaemon\Utils\binary + * + * @param string $bitmap Bitmap + * @param int $check_len Check length? + * + * @return string */ public static function bitmap2bytes($bitmap, $check_len = 0) { @@ -408,7 +460,9 @@ public static function bitmap2bytes($bitmap, $check_len = 0) /** * Get bitmap - * @param byte + * + * @param $byte + * * @return string */ public static function getbitmap($byte) @@ -417,15 +471,22 @@ public static function getbitmap($byte) } } -function binarySubstr($s, $p, $l = NULL) +/** + * @param string $s input string + * @param int $p start + * @param null|int $l length + * + * @return bool|string + */ +function binarySubstr($s, $p, $l = null) { - if ($l === NULL) { + if ($l === null) { $ret = substr($s, $p); } else { $ret = substr($s, $p, $l); } - if ($ret === FALSE) { + if ($ret === false) { $ret = ''; } diff --git a/src/Protocal/Parser.php b/src/Protocal/Parser.php index 0284400..b4e0b8c 100644 --- a/src/Protocal/Parser.php +++ b/src/Protocal/Parser.php @@ -5,6 +5,7 @@ use Evenement\EventEmitter; use React\MySQL\Exception; use React\MySQL\Command; +use React\MySQL\Executor; class Parser extends EventEmitter { @@ -72,21 +73,27 @@ class Parser extends EventEmitter protected $connectOptions; /** - * @var \React\Stream\Stream + * @var \React\Stream\DuplexStreamInterface + * + * TODO? -> is this really the "DuplexStreamInterface"? */ protected $stream; + /** * @var \React\MySQL\Executor */ protected $executor; + /** + * @var \SplQueue + */ protected $queue; - public function __construct($stream, $executor) + public function __construct($stream, Executor $executor) { $this->stream = $stream; $this->executor = $executor; - $this->queue = new \SplQueue($this); + $this->queue = new \SplQueue(); $executor->on('new', array($this, 'handleNewCommand')); } @@ -116,7 +123,7 @@ public function setOptions($options) { foreach ($options as $option => $value) { if (property_exists($this, $option)) { - $this->$option = $value; + $this->{$option} = $value; } } } @@ -270,7 +277,7 @@ public function parse($data) } elseif ($this->rsState === self::RS_STATE_ROW) { $this->debug('Row packet of Data packet'); $row = []; - for ($i = 0, $nf = sizeof($this->resultFields); $i < $nf; ++$i) { + for ($i = 0, $nf = count($this->resultFields); $i < $nf; ++$i) { $row[$this->resultFields[$i]['name']] = $this->parseEncodedString(); } $this->resultRows[] = $row; @@ -353,6 +360,7 @@ public function read($len, $skiplen = 0) if (strlen($this->buffer) - $this->bufferPos - $len - $skiplen < 0) { throw new \LogicException('Logic Error'); } + $buffer = substr($this->buffer, $this->bufferPos, $len); $this->bufferPos += $len; if ($skiplen) { diff --git a/src/Query.php b/src/Query.php index 9baad24..da8fa40 100644 --- a/src/Query.php +++ b/src/Query.php @@ -4,83 +4,121 @@ class Query { + /** + * @var string + */ private $sql; + /** + * helper, to check if the sql-query is build + * + * @var null|string + */ private $builtSql; + /** + * @var array + */ private $params = []; - private $escapeChars = array( - "\x00" => "\\0", - "\r" => "\\r", - "\n" => "\\n", - "\t" => "\\t", - //"\b" => "\\b", - //"\x1a" => "\\Z", - "'" => "\'", - '"' => '\"', - "\\" => "\\\\", - //"%" => "\\%", - //"_" => "\\_", - ); + /** + * @var array + */ + private $paramsByNameTmp = []; + + private $escapeChars = [ + "\x00" => "\\0", + "\r" => "\\r", + "\n" => "\\n", + "\t" => "\\t", + //"\b" => "\\b", + //"\x1a" => "\\Z", + "'" => "\'", + '"' => '\"', + "\\" => "\\\\", + //"%" => "\\%", + //"_" => "\\_", + ]; + /** + * Query constructor. + * + * @param string $sql + */ public function __construct($sql) { - $this->sql = $this->builtSql = $sql; + $this->sql = $sql; + $this->builtSql = $sql; } /** - * Binding params for the query, mutiple arguments support. + * Binding params for the query, multiple arguments support. * - * @param mixed $param - * @return \React\MySQL\Query + * @param mixed $param + * + * @return $this */ public function bindParams() { $this->builtSql = null; - $this->params = func_get_args(); + $this->params = \func_get_args(); return $this; } + /** + * Binding params for the query, via array as input. + * + * @param array $params + * + * @return $this + */ public function bindParamsFromArray(array $params) { $this->builtSql = null; - $this->params = $params; + $this->params = $params; return $this; } /** - * Binding params for the query, mutiple arguments support. + * Binding params for the query, multiple arguments support. + * + * @param mixed $param * - * @param mixed $param - * @return \React\MySQL\Query - * @deprecated + * @return $this + * + * @deprecated

use "bindParams()" or "bindParamsFromArray() instead"

*/ public function params() { - $this->params = func_get_args(); $this->builtSql = null; + $this->params = \func_get_args(); return $this; } + /** + * @param string $str + * + * @return string + */ public function escape($str) { - return strtr($str, $this->escapeChars); + return \strtr($str, $this->escapeChars); } /** - * @param mixed $value - * @return string + * @param mixed $value + * + * @return mixed */ protected function resolveValueForSql($value) { - $type = gettype($value); + $type = \gettype($value); switch ($type) { case 'boolean': - $value = (int) $value; + $value = (int)$value; break; case 'double': case 'integer': @@ -93,14 +131,13 @@ protected function resolveValueForSql($value) foreach ($value as $v) { $nvalue[] = $this->resolveValueForSql($v); } - $value = implode(',', $nvalue); + $value = \implode(',', $nvalue); break; case 'NULL': $value = 'NULL'; break; default: - throw new \InvalidArgumentException(sprintf('Not supportted value type of %s.', $type)); - break; + throw new \InvalidArgumentException(\sprintf('Not supported value type of %s.', $type)); } return $value; @@ -110,73 +147,137 @@ protected function buildSql() { $sql = $this->sql; - $offset = strpos($sql, '?'); - foreach ($this->params as $param) { - $replacement = $this->resolveValueForSql($param); - $sql = substr_replace($sql, $replacement, $offset, 1); - $offset = strpos($sql, '?', $offset + strlen($replacement)); - } - if ($offset !== false) { - throw new \LogicException('Params not enouth to build sql'); + if (\count($this->params) > 0) { + $sql = $this->parseQueryParams($sql); + $sql = $this->parseQueryParamsByName($sql); } return $sql; - /* - $names = array(); - $inName = false; - $currName = ''; - $currIdx = 0; - $sql = $this->sql; - $len = strlen($sql); - $i = 0; - do { - $c = $sql[$i]; - if ($c === '?') { - $names[$i] = $c; - } elseif ($c === ':') { - $currName .= $c; - $currIdx = $i; - $inName = true; - } elseif ($c === ' ') { - $inName = false; - if ($currName) { - $names[$currIdx] = $currName; - $currName = ''; - } - } else { - if ($inName) { - $currName .= $c; + } + + /** + * @param string $sql + * + * @return string + */ + private function parseQueryParams($sql) + { + // reset the internal params helper + $this->paramsByNameTmp = $this->params; + + $params = $this->params; + $offset = \strpos($sql, '?'); + + // is there anything to parse? + if ( + $offset === false + || + \count($params) === 0 + ) { + return $sql; + } + + foreach ($params as $key => $param) { + + if ($offset === false) { + continue; + } + + // use this only for not named parameters + if (!\is_int($key)) { + continue; + } + if (\is_array($param) && \count($param) > 0) { + foreach ($param as $paramInnerKey => $paramInnerValue) { + if (!\is_int($paramInnerKey)) { + continue 2; + } } } - } while (++ $i < $len); - if ($inName) { - $names[$currIdx] = $currName; + $replacement = $this->resolveValueForSql($param); + unset($params[$key]); + $sql = \substr_replace($sql, $replacement, $offset, 1); + $offset = \strpos($sql, '?', $offset + \strlen((string)$replacement)); } - $namedMarks = $unnamedMarks = array(); - foreach ($this->params as $arg) { - if (is_array($arg)) { - $namedMarks += $arg; - } else { - $unnamedMarks[] = $arg; - } + $this->paramsByNameTmp = $params; + + return $sql; + } + + /** + * Returns the SQL by replacing :placeholders with SQL-escaped values. + * + * @param mixed $sql

The SQL string.

+ * + * @return string + */ + private function parseQueryParamsByName($sql) + { + $params = $this->paramsByNameTmp; + + // is there anything to parse? + if ( + \strpos($sql, ':') === false + || + \count($params) === 0 + ) { + return $sql; } - $offset = 0; - foreach ($names as $idx => $value) { - if ($value === '?') { - $replacement = array_shift($unnamedMarks); - } else { - $replacement = $namedMarks[$value]; + foreach ($params as $paramsKey => $paramsInner) { + + // use the key from a named parameter + if (!\is_int($paramsKey)) { + $paramsInner = [$paramsKey => $paramsInner]; + } + + $paramsInner = (array)$paramsInner; + + // reset + $offset = null; + $replacement = null; + $name = null; + + foreach ($paramsInner as $name => $param) { + // use this only for named parameters + if (\is_int($name)) { + continue; + } + + // add ":" if needed + if (\strpos($name, ':') !== 0) { + $nameTmp = ':' . $name; + } else { + $nameTmp = $name; + } + + if ($offset === null) { + $offset = \strpos($sql, $nameTmp); + } else { + $offset = \strpos($sql, $nameTmp, $offset + \strlen((string)$replacement)); + } + + if ($offset === false) { + continue; + } + + $replacement = $this->resolveValueForSql($param); + + $sql = \substr_replace($sql, $replacement, $offset, \strlen($nameTmp)); + } + + if ( + $offset === false + && + $name !== null + ) { + unset($params[$name]); } - list($arg, $len) = $this->getEscapedStringAndLen($replacement); - $sql = substr_replace($sql, $arg, $idx + $offset, strlen($value)); - $offset += $len - strlen($value); } return $sql; - */ } /** diff --git a/tests/QueryTest.php b/tests/QueryTest.php index 1d974ec..b20963e 100644 --- a/tests/QueryTest.php +++ b/tests/QueryTest.php @@ -9,55 +9,105 @@ class QueryTest extends \PHPUnit_Framework_TestCase public function testBindParams() { $query = new Query('select * from test where id = ? and name = ?'); - $sql = $query->bindParams(100, 'test')->getSql(); + $sql = $query->bindParams(100, 'test')->getSql(); $this->assertEquals("select * from test where id = 100 and name = 'test'", $sql); $query = new Query('select * from test where id in (?) and name = ?'); - $sql = $query->bindParams([1, 2], 'test')->getSql(); + $sql = $query->bindParams([1, 2], 'test')->getSql(); $this->assertEquals("select * from test where id in (1,2) and name = 'test'", $sql); - /* + + $query = new Query('select * from test where id in (?) and name = ?'); + $sql = $query->bindParams([1, 2], 'Iñtërnâtiônàlizætiøn')->getSql(); + $this->assertEquals("select * from test where id in (1,2) and name = 'Iñtërnâtiônàlizætiøn'", $sql); + $query = new Query('select * from test where id = :id and name = :name'); - $sql = $query->params(array(':id' => 100, ':name' => 'test'))->getSql(); + $sql = $query->bindParams([':id' => 100, ':name' => 'test'])->getSql(); $this->assertEquals("select * from test where id = 100 and name = 'test'", $sql); $query = new Query('select * from test where id = :id and name = ?'); - $sql = $query->params('test', array(':id' => 100))->getSql(); + $sql = $query->bindParams('test', [':id' => 100])->getSql(); $this->assertEquals("select * from test where id = 100 and name = 'test'", $sql); - */ + + $query = new Query('select * from test where name = ? or id = :id'); + $sql = $query->bindParams('test', [':id' => 100])->getSql(); + $this->assertEquals("select * from test where name = 'test' or id = 100", $sql); + + $query = new Query('select * from test where name = ? or id = :id'); + $sql = $query->bindParams('test', ['id' => 100])->getSql(); + $this->assertEquals("select * from test where name = 'test' or id = 100", $sql); + + $query = new Query('select * from test where name = :name or id IN (?)'); + $sql = $query->bindParams([':name' => 'Iñtërnâtiônàlizætiøn'], [1, 2])->getSql(); + $this->assertEquals("select * from test where name = 'Iñtërnâtiônàlizætiøn' or id IN (1,2)", $sql); + } + + public function testBindParamsFromArray() + { + $query = new Query('select * from test where name = :name or id = ?'); + $sql = $query->bindParamsFromArray([':name' => 'Iñtërnâtiônàlizætiøn', 2])->getSql(); + $this->assertEquals("select * from test where name = 'Iñtërnâtiônàlizætiøn' or id = 2", $sql); + + $query = new Query('select * from test where name = ? or id = :id'); + $sql = $query->bindParamsFromArray(['Iñtërnâtiônàlizætiøn', ':id' => 2])->getSql(); + $this->assertEquals("select * from test where name = 'Iñtërnâtiônàlizætiøn' or id = 2", $sql); + + $query = new Query('select * from test where name = ? or id = :id'); + $sql = $query->bindParamsFromArray([':id' => 2])->getSql(); + $this->assertEquals("select * from test where name = ? or id = 2", $sql); + + $query = new Query('select * from test where name = :name or id = :id'); + $sql = $query->bindParamsFromArray([':name' => 'Iñtërnâtiônàlizætiøn', ':id' => 2])->getSql(); + $this->assertEquals("select * from test where name = 'Iñtërnâtiônàlizætiøn' or id = 2", $sql); + + $query = new Query('select * from test where name = :name'); + $sql = $query->bindParamsFromArray([':name' => 'Iñtërnâtiônàlizætiøn', ':id' => 2])->getSql(); + $this->assertEquals("select * from test where name = 'Iñtërnâtiônàlizætiøn'", $sql); + + $query = new Query('select * from test where id = :id'); + $sql = $query->bindParamsFromArray([':name' => 'Iñtërnâtiônàlizætiøn', ':id' => 2])->getSql(); + $this->assertEquals("select * from test where id = 2", $sql); + + $query = new Query('select * from test where name = :name'); + $sql = $query->bindParamsFromArray([':name_non' => 'Iñtërnâtiônàlizætiøn', ':id' => 2])->getSql(); + $this->assertEquals("select * from test where name = :name", $sql); + + $query = new Query('select * from test where id = :id'); + $sql = $query->bindParamsFromArray([':name' => 'Iñtërnâtiônàlizætiøn', ':id_non' => 2])->getSql(); + $this->assertEquals("select * from test where id = :id", $sql); } public function testGetSqlReturnsQuestionMarkReplacedWhenBound() { $query = new Query('select ?'); - $sql = $query->bindParams('hello')->getSql(); + $sql = $query->bindParams('hello')->getSql(); $this->assertEquals("select 'hello'", $sql); } public function testGetSqlReturnsQuestionMarkReplacedWhenBoundFromLastCall() { $query = new Query('select ?'); - $sql = $query->bindParams('foo')->bindParams('bar')->getSql(); + $sql = $query->bindParams('foo')->bindParams('bar')->getSql(); $this->assertEquals("select 'bar'", $sql); } public function testGetSqlReturnsQuestionMarkReplacedWithNullValueWhenBound() { $query = new Query('select ?'); - $sql = $query->bindParams(null)->getSql(); + $sql = $query->bindParams(null)->getSql(); $this->assertEquals("select NULL", $sql); } public function testGetSqlReturnsQuestionMarkReplacedFromBoundWhenBound() { $query = new Query('select CONCAT(?, ?)'); - $sql = $query->bindParams('hello??', 'world??')->getSql(); + $sql = $query->bindParams('hello??', 'world??')->getSql(); $this->assertEquals("select CONCAT('hello??', 'world??')", $sql); } public function testGetSqlReturnsQuestionMarksAsIsWhenNotBound() { $query = new Query('select "hello?"'); - $sql = $query->getSql(); + $sql = $query->getSql(); $this->assertEquals("select \"hello?\"", $sql); }