Skip to content

Commit ab28a9c

Browse files
committed
Implement query named parameters.
1 parent 89067b2 commit ab28a9c

File tree

2 files changed

+71
-4
lines changed

2 files changed

+71
-4
lines changed

src/Io/Query.php

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -125,16 +125,34 @@ protected function resolveValueForSql($value)
125125

126126
protected function buildSql()
127127
{
128+
$tooManyParametersError = 'Params not enough to build sql';
128129
$sql = $this->sql;
129130

130131
$offset = strpos($sql, '?');
131-
foreach ($this->params as $param) {
132+
$offsetNamed = 0;
133+
134+
foreach ($this->params as $key => $param) {
132135
$replacement = $this->resolveValueForSql($param);
133-
$sql = substr_replace($sql, $replacement, $offset, 1);
134-
$offset = strpos($sql, '?', $offset + strlen($replacement));
136+
137+
if (is_string($key)) {
138+
$prefix = $key[0] === ':' ? '' : ':';
139+
$offsetNamed = strpos($sql, $prefix . $key, $offsetNamed);
140+
141+
if ($offsetNamed === false) {
142+
throw new \LogicException($tooManyParametersError);
143+
}
144+
145+
$sql = substr_replace($sql, $replacement, $offsetNamed, strlen($prefix) + strlen($key));
146+
$offset = strpos($sql, '?', strlen($replacement));
147+
$offsetNamed += strlen($replacement);
148+
} else {
149+
$sql = substr_replace($sql, $replacement, $offset, 1);
150+
$offset = strpos($sql, '?', $offset + strlen($replacement));
151+
}
135152
}
153+
136154
if ($offset !== false) {
137-
throw new \LogicException('Params not enough to build sql');
155+
throw new \LogicException($tooManyParametersError);
138156
}
139157

140158
return $sql;

tests/Io/QueryTest.php

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,45 @@ public function testBindParams()
2727
*/
2828
}
2929

30+
public function testNamedParams()
31+
{
32+
$query = new Query('select * from test where id = :id and name = :name');
33+
$sql = $query->bindParamsFromArray([
34+
'id' => 100,
35+
'name' => 'test'
36+
])->getSql();
37+
$this->assertEquals("select * from test where id = 100 and name = 'test'", $sql);
38+
39+
$query = new Query('select * from test where id = :id and name = :name');
40+
$sql = $query->bindParamsFromArray([
41+
':id' => 100,
42+
':name' => 'test'
43+
])->getSql();
44+
$this->assertEquals("select * from test where id = 100 and name = 'test'", $sql);
45+
46+
$query = new Query('select * from test where id in (:in) and name = :name');
47+
$sql = $query->bindParamsFromArray([
48+
'in' => [1, 2],
49+
'name' => 'test'
50+
])->getSql();
51+
$this->assertEquals("select * from test where id in (1,2) and name = 'test'", $sql);
52+
53+
// mixed named & ?
54+
$query = new Query('select * from test where id in (?) and name = :name');
55+
$sql = $query->bindParamsFromArray([
56+
[1, 2],
57+
'name' => 'test'
58+
])->getSql();
59+
$this->assertEquals("select * from test where id in (1,2) and name = 'test'", $sql);
60+
61+
$query = new Query('select * from test where id in (:in) and name = ?');
62+
$sql = $query->bindParamsFromArray([
63+
'in' => [1, 2],
64+
'test'
65+
])->getSql();
66+
$this->assertEquals("select * from test where id in (1,2) and name = 'test'", $sql);
67+
}
68+
3069
public function testGetSqlReturnsQuestionMarkReplacedWhenBound()
3170
{
3271
$query = new Query('select ?');
@@ -55,6 +94,16 @@ public function testGetSqlReturnsQuestionMarkReplacedFromBoundWhenBound()
5594
$this->assertEquals("select CONCAT('hello??', 'world??')", $sql);
5695
}
5796

97+
public function testGetSqlReturnsNamedParamsReplacedFromBoundWhenBound()
98+
{
99+
$query = new Query('select CONCAT(:param1, :param2)');
100+
$sql = $query->bindParamsFromArray([
101+
'param1' => ':param2',
102+
'param2' => 'world'
103+
])->getSql();
104+
$this->assertEquals("select CONCAT(':param2', 'world')", $sql);
105+
}
106+
58107
public function testGetSqlReturnsQuestionMarksAsIsWhenNotBound()
59108
{
60109
$query = new Query('select "hello?"');

0 commit comments

Comments
 (0)