Skip to content

Commit

Permalink
[+]: add named parameter v2
Browse files Browse the repository at this point in the history
  • Loading branch information
voku committed Apr 28, 2018
1 parent c57bb1f commit d8f6f08
Show file tree
Hide file tree
Showing 3 changed files with 138 additions and 42 deletions.
9 changes: 6 additions & 3 deletions src/Protocal/Parser.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@
use React\MySQL\Exception;
use React\MySQL\Command;
use React\MySQL\Executor;
use React\Stream\DuplexStreamInterface;
use React\Stream\WritableResourceStream;

class Parser extends EventEmitter
{
Expand Down Expand Up @@ -76,6 +74,8 @@ class Parser extends EventEmitter

/**
* @var \React\Stream\DuplexStreamInterface
*
* TODO? -> is this really the "DuplexStreamInterface"?
*/
protected $stream;

Expand All @@ -84,9 +84,12 @@ class Parser extends EventEmitter
*/
protected $executor;

/**
* @var \SplQueue
*/
protected $queue;

public function __construct(DuplexStreamInterface $stream, Executor $executor)
public function __construct($stream, Executor $executor)
{
$this->stream = $stream;
$this->executor = $executor;
Expand Down
136 changes: 97 additions & 39 deletions src/Query.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,29 @@

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(
/**
* @var array
*/
private $paramsByNameTmp = [];

private $escapeChars = [
"\x00" => "\\0",
"\r" => "\\r",
"\n" => "\\n",
Expand All @@ -22,27 +38,41 @@ class Query
"\\" => "\\\\",
//"%" => "\\%",
//"_" => "\\_",
);
];

/**
* 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, 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;
Expand All @@ -54,30 +84,38 @@ public function bindParamsFromArray(array $params)
/**
* Binding params for the query, multiple arguments support.
*
* @param mixed $param
* @return \React\MySQL\Query
* @deprecated
* @param mixed $param
*
* @return $this
*
* @deprecated <p>use "bindParams()" or "bindParamsFromArray() instead"</p>
*/
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;
Expand All @@ -93,13 +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 supported value type of %s.', $type));
throw new \InvalidArgumentException(\sprintf('Not supported value type of %s.', $type));
}

return $value;
Expand All @@ -110,22 +148,24 @@ protected function buildSql()
$sql = $this->sql;

if (\count($this->params) > 0) {
$parseQueryParams = $this->_parseQueryParams($sql, $this->params);
$parseQueryParamsByName = $this->_parseQueryParamsByName($parseQueryParams['sql'], $parseQueryParams['params']);
$sql = $parseQueryParamsByName['sql'];
$sql = $this->parseQueryParams($sql);
$sql = $this->parseQueryParamsByName($sql);
}

return $sql;
}

/**
* @param string $sql
* @param array $params
*
* @return array <p>with the keys -> 'sql', 'params'</p>
* @return string
*/
private function _parseQueryParams($sql, array $params = [])
private function parseQueryParams($sql)
{
// reset the internal params helper
$this->paramsByNameTmp = $this->params;

$params = $this->params;
$offset = \strpos($sql, '?');

// is there anything to parse?
Expand All @@ -134,61 +174,75 @@ private function _parseQueryParams($sql, array $params = [])
||
\count($params) === 0
) {
return ['sql' => $sql, 'params' => $params];
return $sql;
}

foreach ($params as $key => $param) {

if ($offset === false) {
continue;
}

// use this only for not named parameters
if (!is_int($key)) {
if (!\is_int($key)) {
continue;
}
if (is_array($param) && count($param) > 0) {
if (\is_array($param) && \count($param) > 0) {
foreach ($param as $paramInnerKey => $paramInnerValue) {
if (!is_int($paramInnerKey)) {
if (!\is_int($paramInnerKey)) {
continue 2;
}
}
}

if ($offset === false) {
continue;
}

$replacement = $this->resolveValueForSql($param);
unset($params[$key]);
$sql = \substr_replace($sql, $replacement, $offset, 1);
$offset = \strpos($sql, '?', $offset + \strlen((string)$replacement));
}

return ['sql' => $sql, 'params' => $params];
$this->paramsByNameTmp = $params;

return $sql;
}

/**
* Returns the SQL by replacing :placeholders with SQL-escaped values.
*
* @param mixed $sql <p>The SQL string.</p>
* @param array $params <p>An array of key-value bindings.</p>
*
* @return array <p>with the keys -> 'sql', 'params'</p>
* @return string
*/
public function _parseQueryParamsByName($sql, array $params = [])
private function parseQueryParamsByName($sql)
{
$params = $this->paramsByNameTmp;

// is there anything to parse?
if (
\strpos($sql, ':') === false
||
\count($params) === 0
) {
return ['sql' => $sql, 'params' => $params];
return $sql;
}

foreach ($params as $paramsInner) {
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)) {
if (\is_int($name)) {
continue;
}

Expand All @@ -214,12 +268,16 @@ public function _parseQueryParamsByName($sql, array $params = [])
$sql = \substr_replace($sql, $replacement, $offset, \strlen($nameTmp));
}

if ($offset === false) {
if (
$offset === false
&&
$name !== null
) {
unset($params[$name]);
}
}

return ['sql' => $sql, 'params' => $params];
return $sql;
}

/**
Expand Down
35 changes: 35 additions & 0 deletions tests/QueryTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,41 @@ public function testBindParams()
$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 ?');
Expand Down

0 comments on commit d8f6f08

Please sign in to comment.