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

Fixes the special characters in bindParam for PDO #224

Merged
merged 4 commits into from
Nov 29, 2017
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/Adapter/Driver/Pdo/Pdo.php
Original file line number Diff line number Diff line change
Expand Up @@ -298,7 +298,8 @@ public function getPrepareType()
public function formatParameterName($name, $type = null)
{
if ($type === null && !is_numeric($name) || $type == self::PARAMETERIZATION_NAMED) {
return ':' . $name;
// using MD5 because of the PDO restriction [A-Za-z0-9_] for bindParam name
return ':' . md5($name);
}

return '?';
Expand Down
2 changes: 1 addition & 1 deletion src/Adapter/Driver/Pdo/Statement.php
Original file line number Diff line number Diff line change
Expand Up @@ -289,7 +289,7 @@ protected function bindParametersFromContainer()
}

// parameter is named or positional, value is reference
$parameter = is_int($name) ? ($name + 1) : $name;
$parameter = is_int($name) ? ($name + 1) : $this->driver->formatParameterName($name);
$this->resource->bindParam($parameter, $value, $type);
}
}
Expand Down
25 changes: 25 additions & 0 deletions test/Adapter/Driver/Pdo/PdoTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,29 @@ public function testGetDatabasePlatformName()
$this->assertEquals('SqlServer', $this->pdo->getDatabasePlatformName());
$this->assertEquals('SQLServer', $this->pdo->getDatabasePlatformName(DriverInterface::NAME_FORMAT_NATURAL));
}

public function getParamsAndType()
{
return [
[ 'foo', null, ':' . md5('foo')],
[ 'foo-', null, ':' . md5('foo-')],
[ 'foo$', null, ':' . md5('foo$')],
[ 1, null, '?' ],
[ '1', null, '?'],
[ 'foo', Pdo::PARAMETERIZATION_NAMED, ':' . md5('foo')],
[ 'foo-', Pdo::PARAMETERIZATION_NAMED, ':' . md5('foo-')],
[ 'foo$', Pdo::PARAMETERIZATION_NAMED, ':' . md5('foo$')],
[ 1, Pdo::PARAMETERIZATION_NAMED, ':' . md5('1')],
[ '1', Pdo::PARAMETERIZATION_NAMED, ':' . md5('1')],
];
}

/**
* @dataProvider getParamsAndType
*/
public function testFormatParameterName($name, $type, $expected)
{
$result = $this->pdo->formatParameterName($name, $type);
$this->assertEquals($expected, $result);
}
}
4 changes: 2 additions & 2 deletions test/Adapter/Driver/Pdo/StatementIntegrationTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ protected function tearDown()
public function testStatementExecuteWillConvertPhpBoolToPdoBoolWhenBinding()
{
$this->pdoStatementMock->expects($this->any())->method('bindParam')->with(
$this->equalTo('foo'),
$this->equalTo(':' . md5('foo')),
$this->equalTo(false),
$this->equalTo(\PDO::PARAM_BOOL)
);
Expand All @@ -57,7 +57,7 @@ public function testStatementExecuteWillConvertPhpBoolToPdoBoolWhenBinding()
public function testStatementExecuteWillUsePdoStrByDefaultWhenBinding()
{
$this->pdoStatementMock->expects($this->any())->method('bindParam')->with(
$this->equalTo('foo'),
$this->equalTo(':' . md5('foo')),
$this->equalTo('bar'),
$this->equalTo(\PDO::PARAM_STR)
);
Expand Down
27 changes: 27 additions & 0 deletions test/Adapter/Driver/Pdo/StatementTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

use Zend\Db\Adapter\Driver\Pdo\Connection;
use Zend\Db\Adapter\Driver\Pdo\Statement;
use Zend\Db\Adapter\Driver\Pdo\Result;
use Zend\Db\Adapter\Driver\Pdo\Pdo;
use Zend\Db\Adapter\ParameterContainer;

Expand Down Expand Up @@ -126,4 +127,30 @@ public function testExecute()
$this->statement->prepare('SELECT 1');
$this->assertInstanceOf('Zend\Db\Adapter\Driver\Pdo\Result', $this->statement->execute());
}

/**
* @see https://github.com/zendframework/zend-db/pull/224
*/
public function testExecuteWithSpecialCharInBindParam()
{
$testSqlite = new TestAsset\SqliteMemoryPdo('CREATE TABLE test (text_ TEXT, text$ TEXT);');
$this->statement->setDriver(new Pdo(new Connection($testSqlite)));
$this->statement->initialize($testSqlite);

$this->statement->prepare(
'INSERT INTO test (text_, text$) VALUES (:' .
md5('text_') .
', :' .
md5('text$') .
')'
);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd use sprintf here to make it more readable:

$this->statement->prepare(sprintf(
    'INSERT INTO test (text_, text$) VALUES (:%s, :%s)',
    md5('text_'),
    md5('text$')
));

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed.

$result = $this->statement->execute([ 'text_' => 'foo', 'text$' => 'bar']);
$this->assertInstanceOf(Result::class, $result);
$this->assertTrue($result->valid());

$result = $testSqlite->query('SELECT * FROM test');
$values = $result->fetch();
$this->assertEquals('foo', $values['text_']);
$this->assertEquals('bar', $values['text$']);
}
}
13 changes: 12 additions & 1 deletion test/Adapter/Driver/Pdo/TestAsset/SqliteMemoryPdo.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,19 @@ class SqliteMemoryPdo extends \Pdo
{
protected $mockStatement;

public function __construct()
public function __construct($sql = null)
{
parent::__construct('sqlite::memory:');

// execute the sql statement if not empty
if (!empty($sql)) {
if (false === $this->exec($sql)) {
throw new \Exception(sprintf(
"Error: %s, %s",
$this->errorCode(),
implode(",", $this->errorInfo())
));
}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Return early if $sql is empty:

if (empty($sql)) {
    return;
}

if (false === $this->exec($sql)) {
    throw new \Exception(/* ... */);
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed

}
}