Skip to content
This repository was archived by the owner on Jan 29, 2020. It is now read-only.

Commit ab816b7

Browse files
committed
Merge branch 'hotfix/224' into develop
Forward port #224
2 parents 507d4e2 + 85f741e commit ab816b7

File tree

7 files changed

+71
-6
lines changed

7 files changed

+71
-6
lines changed

CHANGELOG.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,10 @@ All notable changes to this project will be documented in this file, in reverse
5454

5555
### Fixed
5656

57-
- Nothing.
57+
- [#224](https://github.com/zendframework/zend-db/pull/224) fixes how parameters
58+
are bound to statements in the PDO adapter. PDO has a restriction on parameter
59+
names of `[0-9a-zA_Z_]`; as such, the driver now hashes the parameter names
60+
using `md5()` in order to ensure compatibility with other drivers.
5861

5962
## 2.8.2 - 2016-08-09
6063

src/Adapter/Driver/Pdo/Pdo.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -304,7 +304,8 @@ public function getPrepareType()
304304
public function formatParameterName($name, $type = null)
305305
{
306306
if ($type === null && ! is_numeric($name) || $type == self::PARAMETERIZATION_NAMED) {
307-
return ':' . $name;
307+
// using MD5 because of the PDO restriction [A-Za-z0-9_] for bindParam name
308+
return ':' . md5($name);
308309
}
309310

310311
return '?';

src/Adapter/Driver/Pdo/Statement.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -289,7 +289,7 @@ protected function bindParametersFromContainer()
289289
}
290290

291291
// parameter is named or positional, value is reference
292-
$parameter = is_int($name) ? ($name + 1) : $name;
292+
$parameter = is_int($name) ? ($name + 1) : $this->driver->formatParameterName($name);
293293
$this->resource->bindParam($parameter, $value, $type);
294294
}
295295
}

test/Adapter/Driver/Pdo/PdoTest.php

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,4 +39,29 @@ public function testGetDatabasePlatformName()
3939
self::assertEquals('SqlServer', $this->pdo->getDatabasePlatformName());
4040
self::assertEquals('SQLServer', $this->pdo->getDatabasePlatformName(DriverInterface::NAME_FORMAT_NATURAL));
4141
}
42+
43+
public function getParamsAndType()
44+
{
45+
return [
46+
[ 'foo', null, ':' . md5('foo')],
47+
[ 'foo-', null, ':' . md5('foo-')],
48+
[ 'foo$', null, ':' . md5('foo$')],
49+
[ 1, null, '?' ],
50+
[ '1', null, '?'],
51+
[ 'foo', Pdo::PARAMETERIZATION_NAMED, ':' . md5('foo')],
52+
[ 'foo-', Pdo::PARAMETERIZATION_NAMED, ':' . md5('foo-')],
53+
[ 'foo$', Pdo::PARAMETERIZATION_NAMED, ':' . md5('foo$')],
54+
[ 1, Pdo::PARAMETERIZATION_NAMED, ':' . md5('1')],
55+
[ '1', Pdo::PARAMETERIZATION_NAMED, ':' . md5('1')],
56+
];
57+
}
58+
59+
/**
60+
* @dataProvider getParamsAndType
61+
*/
62+
public function testFormatParameterName($name, $type, $expected)
63+
{
64+
$result = $this->pdo->formatParameterName($name, $type);
65+
$this->assertEquals($expected, $result);
66+
}
4267
}

test/Adapter/Driver/Pdo/StatementIntegrationTest.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ protected function tearDown()
5555
public function testStatementExecuteWillConvertPhpBoolToPdoBoolWhenBinding()
5656
{
5757
$this->pdoStatementMock->expects($this->any())->method('bindParam')->with(
58-
$this->equalTo('foo'),
58+
$this->equalTo(':' . md5('foo')),
5959
$this->equalTo(false),
6060
$this->equalTo(\PDO::PARAM_BOOL)
6161
);
@@ -65,7 +65,7 @@ public function testStatementExecuteWillConvertPhpBoolToPdoBoolWhenBinding()
6565
public function testStatementExecuteWillUsePdoStrByDefaultWhenBinding()
6666
{
6767
$this->pdoStatementMock->expects($this->any())->method('bindParam')->with(
68-
$this->equalTo('foo'),
68+
$this->equalTo(':' . md5('foo')),
6969
$this->equalTo('bar'),
7070
$this->equalTo(\PDO::PARAM_STR)
7171
);

test/Adapter/Driver/Pdo/StatementTest.php

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
use PHPUnit\Framework\TestCase;
1313
use Zend\Db\Adapter\Driver\Pdo\Connection;
1414
use Zend\Db\Adapter\Driver\Pdo\Pdo;
15+
use Zend\Db\Adapter\Driver\Pdo\Result;
1516
use Zend\Db\Adapter\Driver\Pdo\Statement;
1617
use Zend\Db\Adapter\ParameterContainer;
1718

@@ -127,4 +128,28 @@ public function testExecute()
127128
$this->statement->prepare('SELECT 1');
128129
self::assertInstanceOf('Zend\Db\Adapter\Driver\Pdo\Result', $this->statement->execute());
129130
}
131+
132+
/**
133+
* @see https://github.com/zendframework/zend-db/pull/224
134+
*/
135+
public function testExecuteWithSpecialCharInBindParam()
136+
{
137+
$testSqlite = new TestAsset\SqliteMemoryPdo('CREATE TABLE test (text_ TEXT, text$ TEXT);');
138+
$this->statement->setDriver(new Pdo(new Connection($testSqlite)));
139+
$this->statement->initialize($testSqlite);
140+
141+
$this->statement->prepare(sprintf(
142+
'INSERT INTO test (text_, text$) VALUES (:%s, :%s)',
143+
md5('text_'),
144+
md5('text$')
145+
));
146+
$result = $this->statement->execute([ 'text_' => 'foo', 'text$' => 'bar']);
147+
$this->assertInstanceOf(Result::class, $result);
148+
$this->assertTrue($result->valid());
149+
150+
$result = $testSqlite->query('SELECT * FROM test');
151+
$values = $result->fetch();
152+
$this->assertEquals('foo', $values['text_']);
153+
$this->assertEquals('bar', $values['text$']);
154+
}
130155
}

test/Adapter/Driver/Pdo/TestAsset/SqliteMemoryPdo.php

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,19 @@ class SqliteMemoryPdo extends \Pdo
1313
{
1414
protected $mockStatement;
1515

16-
public function __construct()
16+
public function __construct($sql = null)
1717
{
1818
parent::__construct('sqlite::memory:');
19+
20+
if (empty($sql)) {
21+
return;
22+
}
23+
if (false === $this->exec($sql)) {
24+
throw new \Exception(sprintf(
25+
"Error: %s, %s",
26+
$this->errorCode(),
27+
implode(",", $this->errorInfo())
28+
));
29+
}
1930
}
2031
}

0 commit comments

Comments
 (0)