diff --git a/src/Statement.php b/src/Statement.php index 7affd1f..aac76ee 100644 --- a/src/Statement.php +++ b/src/Statement.php @@ -209,23 +209,27 @@ protected function bindParametersFromContainer(): void $args = []; foreach ($parameters as $name => &$value) { - if ($this->parameterContainer->offsetHasErrata($name)) { - switch ($this->parameterContainer->offsetGetErrata($name)) { - case ParameterContainer::TYPE_DOUBLE: - $type .= 'd'; - break; - case ParameterContainer::TYPE_NULL: - $value = null; // as per @see http://www.php.net/manual/en/mysqli-stmt.bind-param.php#96148 - case ParameterContainer::TYPE_INTEGER: - $type .= 'i'; - break; - case ParameterContainer::TYPE_STRING: - default: - $type .= 's'; - break; - } - } $args[] = &$value; + + if (! $this->parameterContainer->offsetHasErrata($name)) { + $type .= 's'; + continue; + } + + switch ($this->parameterContainer->offsetGetErrata($name)) { + case ParameterContainer::TYPE_DOUBLE: + $type .= 'd'; + break; + case ParameterContainer::TYPE_NULL: + $value = null; // as per @see http://www.php.net/manual/en/mysqli-stmt.bind-param.php#96148 + case ParameterContainer::TYPE_INTEGER: + $type .= 'i'; + break; + case ParameterContainer::TYPE_STRING: + default: + $type .= 's'; + break; + } } if ($args) { @@ -233,4 +237,11 @@ protected function bindParametersFromContainer(): void call_user_func_array([$this->resource, 'bind_param'], $args); } } + + /** Perform a deep clone */ + public function __clone(): void + { + $this->isPrepared = false; + $this->parameterContainer = clone $this->parameterContainer; + } } diff --git a/test/integration/Mysqli/StatementResultTest.php b/test/integration/Mysqli/StatementResultTest.php index 8447c2c..d2980c1 100644 --- a/test/integration/Mysqli/StatementResultTest.php +++ b/test/integration/Mysqli/StatementResultTest.php @@ -53,6 +53,36 @@ public function bindsDoubleAndNullParameterTypes(): void static::assertTrue($result->isQueryResult()); } + #[Test] + public function bindsMixedTypedAndUntypedParameters(): void + { + $container = new ParameterContainer(); + $container->offsetSet('id', 1, ParameterContainer::TYPE_INTEGER); + $container->offsetSet('value', 'bar'); + + $result = $this->createDriver(false) + ->createStatement('SELECT * FROM test WHERE id = ? AND value = ?') + ->execute($container); + + static::assertNotNull($result); + static::assertSame(['id' => 1, 'name' => 'foo', 'value' => 'bar'], $result->current()); + } + + #[Test] + public function bindsUntypedParametersAsStrings(): void + { + $container = new ParameterContainer(); + $container->offsetSet('id', '1'); + $container->offsetSet('value', 'bar'); + + $result = $this->createDriver(false) + ->createStatement('SELECT * FROM test WHERE id = ? AND value = ?') + ->execute($container); + + static::assertNotNull($result); + static::assertSame(['id' => 1, 'name' => 'foo', 'value' => 'bar'], $result->current()); + } + #[Test] public function bufferAfterIterationStartedThrows(): void { diff --git a/test/unit/StatementTest.php b/test/unit/StatementTest.php index 3eb5956..4482da9 100644 --- a/test/unit/StatementTest.php +++ b/test/unit/StatementTest.php @@ -4,19 +4,47 @@ namespace PhpDbTest\Mysql; +use mysqli_stmt; use PhpDb\Adapter\Driver\DriverInterface; use PhpDb\Adapter\Exception\InvalidArgumentException; +use PhpDb\Adapter\ParameterContainer; use PhpDb\Adapter\Profiler\ProfilerInterface; use PhpDb\Mysql\Statement; use PHPUnit\Framework\Attributes\CoversMethod; +use PHPUnit\Framework\Attributes\Group; use PHPUnit\Framework\Attributes\Test; use PHPUnit\Framework\TestCase; #[CoversMethod(Statement::class, 'getProfiler')] #[CoversMethod(Statement::class, 'setProfiler')] #[CoversMethod(Statement::class, 'setDriver')] +#[CoversMethod(Statement::class, '__clone')] +#[Group('unit')] final class StatementTest extends TestCase { + #[Test] + public function cloneCopiesTheParameterContainer(): void + { + $container = new ParameterContainer(['id' => 1]); + $statement = new Statement($container); + + $clone = clone $statement; + + static::assertNotSame($container, $clone->getParameterContainer()); + static::assertEquals($container, $clone->getParameterContainer()); + } + + #[Test] + public function cloneIsNotPrepared(): void + { + $statement = new Statement(); + $statement->setResource($this->createStub(mysqli_stmt::class)); + + $clone = clone $statement; + + static::assertFalse($clone->isPrepared()); + } + #[Test] public function profilerAccessors(): void {