Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
138 changes: 36 additions & 102 deletions analyzer-baseline.toml
Original file line number Diff line number Diff line change
Expand Up @@ -42,12 +42,6 @@ code = "mixed-assignment"
message = "Assigning `mixed` type to a variable may lead to unexpected behavior."
count = 1

[[issues]]
file = "src/Adapter/Adapter.php"
code = "possibly-null-argument"
message = 'Argument #1 of method `PhpDb\Adapter\Driver\DriverInterface::createStatement` is possibly `null`, but parameter type `resource|string` does not accept it.'
count = 1

[[issues]]
file = "src/Adapter/AdapterAwareInterface.php"
code = "missing-api-or-internal"
Expand Down Expand Up @@ -336,12 +330,6 @@ code = "missing-api-or-internal"
message = 'Abstract class `PhpDb\Adapter\Driver\Pdo\AbstractPdo` is missing an `@api` or `@internal` annotation.'
count = 1

[[issues]]
file = "src/Adapter/Driver/Pdo/AbstractPdo.php"
code = "mixed-return-statement"
message = 'Could not infer a precise return type for function `PhpDb\Adapter\Driver\Pdo\AbstractPdo::getLastGeneratedValue`. Saw type `mixed`.'
count = 1

[[issues]]
file = "src/Adapter/Driver/Pdo/AbstractPdo.php"
code = "non-existent-method"
Expand Down Expand Up @@ -420,12 +408,6 @@ code = "possible-method-access-on-null"
message = "Attempting to call a method on `null`."
count = 7

[[issues]]
file = "src/Adapter/Driver/Pdo/AbstractPdoConnection.php"
code = "possibly-null-argument"
message = 'Argument #1 of method `PhpDb\Adapter\Driver\DriverInterface::createStatement` is possibly `null`, but parameter type `resource|string` does not accept it.'
count = 1

[[issues]]
file = "src/Adapter/Driver/Pdo/AbstractPdoConnection.php"
code = "too-many-arguments"
Expand Down Expand Up @@ -474,18 +456,6 @@ code = "incompatible-return-type"
message = 'Return type `mixed` of `PhpDb\Adapter\Driver\Pdo\Result::next()` is incompatible with parent return type `void` of `Iterator::next()`'
count = 1

[[issues]]
file = "src/Adapter/Driver/Pdo/Result.php"
code = "less-specific-nested-argument-type"
message = 'Argument type mismatch for argument #1 of `PhpDb\ResultSet\AbstractResultSet::initialize`: expected `iterable<array-key, mixed>`, but provided type `$this(PhpDb\Adapter\Driver\Pdo\Result)` is less specific.'
count = 1

[[issues]]
file = "src/Adapter/Driver/Pdo/Result.php"
code = "less-specific-nested-argument-type"
message = 'Argument type mismatch for argument #1 of `PhpDb\ResultSet\ResultSetInterface::initialize`: expected `iterable<array-key, mixed>`, but provided type `$this(PhpDb\Adapter\Driver\Pdo\Result)` is less specific.'
count = 1

[[issues]]
file = "src/Adapter/Driver/Pdo/Result.php"
code = "missing-constant-type"
Expand Down Expand Up @@ -612,12 +582,6 @@ code = "class-must-be-final"
message = 'Class `PhpDb\Adapter\Driver\Pdo\Statement` should be declared `final`.'
count = 1

[[issues]]
file = "src/Adapter/Driver/Pdo/Statement.php"
code = "docblock-type-mismatch"
message = "Docblock return type `false|null|resource` is incompatible with native return type `PDOStatement|false|null`."
count = 1

[[issues]]
file = "src/Adapter/Driver/Pdo/Statement.php"
code = "imprecise-type"
Expand All @@ -636,42 +600,18 @@ code = "invalid-argument"
message = 'Invalid argument type for argument #1 of `PhpDb\Adapter\Driver\DriverInterface::createResult`: expected `resource`, but found `PDOStatement|false|null`.'
count = 1

[[issues]]
file = "src/Adapter/Driver/Pdo/Statement.php"
code = "invalid-method-access"
message = "Attempting to access a method on a non-object type (`false`)."
count = 3

[[issues]]
file = "src/Adapter/Driver/Pdo/Statement.php"
code = "invalid-property-assignment-value"
message = "Invalid type for property `$sql`: expected `string`, but got `null|string`."
count = 1

[[issues]]
file = "src/Adapter/Driver/Pdo/Statement.php"
code = "invalid-return-statement"
message = 'Invalid return type for function `PhpDb\Adapter\Driver\Pdo\Statement::getResource`: expected `false|null|resource`, but found `PDOStatement|false|null`.'
count = 1

[[issues]]
file = "src/Adapter/Driver/Pdo/Statement.php"
code = "mixed-argument"
message = "Invalid argument type for argument #2 of `implode`: expected `array<array-key, Stringable|null|scalar>|null`, but found `mixed`."
count = 1

[[issues]]
file = "src/Adapter/Driver/Pdo/Statement.php"
code = "mixed-assignment"
message = "Assigning `mixed` type to a variable may lead to unexpected behavior."
count = 2

[[issues]]
file = "src/Adapter/Driver/Pdo/Statement.php"
code = "possible-method-access-on-null"
message = "Attempting to call a method on `null`."
count = 3

[[issues]]
file = "src/Adapter/Driver/Pdo/Statement.php"
code = "possibly-false-argument"
Expand Down Expand Up @@ -1410,12 +1350,6 @@ code = "mixed-assignment"
message = "Assigning `mixed` type to a variable may lead to unexpected behavior."
count = 2

[[issues]]
file = "src/ResultSet/AbstractResultSet.php"
code = "no-value"
message = 'Argument #1 passed to method `PhpDb\ResultSet\AbstractResultSet::initializeFromResult` has type `never`, meaning it cannot produce a value.'
count = 1

[[issues]]
file = "src/ResultSet/Exception/InvalidArgumentException.php"
code = "class-must-be-final"
Expand All @@ -1428,6 +1362,24 @@ code = "class-must-be-final"
message = 'Class `PhpDb\ResultSet\Exception\RuntimeException` should be declared `final`.'
count = 1

[[issues]]
file = "src/ResultSet/HydratingResultSet.php"
code = "redundant-docblock-type"
message = "Redundant docblock type for variable `$row`."
count = 1

[[issues]]
file = "src/ResultSet/ObjectResultSet.php"
code = "redundant-docblock-type"
message = "Redundant docblock type for variable `$row`."
count = 1

[[issues]]
file = "src/ResultSet/RowPrototypeResultSet.php"
code = "redundant-condition"
message = "Redundant ternary operator: condition is always truthy."
count = 1

[[issues]]
file = "src/RowGateway/AbstractRowGateway.php"
code = "imprecise-type"
Expand Down Expand Up @@ -1482,12 +1434,6 @@ code = "incompatible-parameter-type"
message = 'Parameter `$offset` of `PhpDb\RowGateway\AbstractRowGateway::offsetunset()` expects type `string` but parent `ArrayAccess::offsetunset()` expects type `mixed`'
count = 1

[[issues]]
file = "src/RowGateway/AbstractRowGateway.php"
code = "invalid-array-element-key"
message = "Invalid array key type."
count = 1

[[issues]]
file = "src/RowGateway/AbstractRowGateway.php"
code = "less-specific-nested-argument-type"
Expand Down Expand Up @@ -1560,6 +1506,24 @@ code = "mixed-argument"
message = 'Invalid argument type for argument #1 of `PhpDb\RowGateway\Exception\RuntimeException::forMissingPrimaryKeyData`: expected `string`, but found `mixed`.'
count = 1

[[issues]]
file = "src/RowGateway/AbstractRowGateway.php"
code = "mixed-array-index"
message = "Invalid index type `mixed` used in array assignment on `array<array-key, mixed>`."
count = 4

[[issues]]
file = "src/RowGateway/AbstractRowGateway.php"
code = "mixed-array-index"
message = "Invalid index type `mixed` used in array assignment on `array<array-key, nonnull>`."
count = 1

[[issues]]
file = "src/RowGateway/AbstractRowGateway.php"
code = "mixed-array-index"
message = "Possibly invalid array key type."
count = 1

[[issues]]
file = "src/RowGateway/AbstractRowGateway.php"
code = "mixed-assignment"
Expand Down Expand Up @@ -1728,30 +1692,6 @@ code = "missing-constructor"
message = 'Class `PhpDb\Sql\Platform\AbstractPlatform` has typed properties without default values but no constructor to initialize them.'
count = 1

[[issues]]
file = "src/Sql/Platform/AbstractPlatform.php"
code = "mixed-assignment"
message = "Assigning `mixed` type to a variable may lead to unexpected behavior."
count = 1

[[issues]]
file = "src/Sql/Platform/AbstractPlatform.php"
code = "mixed-method-access"
message = "Attempting to access a method on a non-object type (`mixed`)."
count = 1

[[issues]]
file = "src/Sql/Platform/AbstractPlatform.php"
code = "mixed-return-statement"
message = 'Could not infer a precise return type for function `PhpDb\Sql\Platform\AbstractPlatform::getSqlString`. Saw type `mixed`.'
count = 1

[[issues]]
file = "src/Sql/Platform/AbstractPlatform.php"
code = "mixed-return-statement"
message = 'Could not infer a precise return type for function `PhpDb\Sql\Platform\AbstractPlatform::getTypeDecorator`. Saw type `mixed`.'
count = 1

[[issues]]
file = "src/Sql/Platform/AbstractPlatform.php"
code = "non-existent-method"
Expand Down Expand Up @@ -1794,12 +1734,6 @@ code = "mixed-return-statement"
message = 'Could not infer a precise return type for function `PhpDb\Sql\Platform\Platform::getDecorators`. Saw type `mixed`.'
count = 1

[[issues]]
file = "src/Sql/Platform/Platform.php"
code = "mixed-return-statement"
message = 'Could not infer a precise return type for function `PhpDb\Sql\Platform\Platform::getSqlString`. Saw type `mixed`.'
count = 1

[[issues]]
file = "src/Sql/Platform/Platform.php"
code = "non-existent-method"
Expand Down
6 changes: 0 additions & 6 deletions lint-baseline.toml
Original file line number Diff line number Diff line change
Expand Up @@ -66,12 +66,6 @@ code = "too-many-methods"
message = "Class has too many methods."
count = 1

[[issues]]
file = "src/Adapter/Driver/Pdo/Statement.php"
code = "halstead"
message = "Method has a high halstead difficulty"
count = 1

[[issues]]
file = "src/Adapter/Driver/Pdo/Statement.php"
code = "literal-named-argument"
Expand Down
17 changes: 14 additions & 3 deletions src/Adapter/Driver/Pdo/Statement.php
Original file line number Diff line number Diff line change
Expand Up @@ -70,10 +70,12 @@ public function execute(array|ParameterContainer|null $parameters = null): ?Resu
}
/** END Standard ParameterContainer Merging Block */

$resource = $this->getPreparedResource();

$this->profiler?->profilerStart($this);

try {
$this->resource->execute();
$resource->execute();
} catch (PDOException $e) {
$this->profiler?->profilerFinish();

Expand All @@ -83,7 +85,7 @@ public function execute(array|ParameterContainer|null $parameters = null): ?Resu
}

throw Exception\InvalidQueryException::forFailedStatement(
implode(' - ', $this->resource->errorInfo()),
implode(' - ', $resource->errorInfo()),
$code,
$e,
);
Expand Down Expand Up @@ -195,6 +197,7 @@ public function setSql(?string $sql): StatementContainerInterface
/** Bind parameters from container */
protected function bindParametersFromContainer(): void
{
$resource = $this->getPreparedResource();
$parameters = $this->parameterContainer->getNamedArray();
$errata = $this->parameterContainer->getErrataIterator()->getArrayCopy();

Expand Down Expand Up @@ -224,12 +227,20 @@ protected function bindParametersFromContainer(): void
$parameter = ':' . ltrim($name, ':');
}

$this->resource->bindParam($parameter, $value, $type);
$resource->bindParam($parameter, $value, $type);
}

$this->parametersBound = true;
}

/** @throws Exception\RuntimeException */
private function getPreparedResource(): PDOStatement
{
return $this->resource instanceof PDOStatement
? $this->resource
: throw Exception\RuntimeException::forUnpreparedStatement();
}

/** Perform a deep clone */
public function __clone(): void
{
Expand Down
7 changes: 7 additions & 0 deletions src/Adapter/Exception/RuntimeException.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ class RuntimeException extends Exception\RuntimeException

final public const string UNCOMPOSABLE_TRAIT = '%s can only be composed into %s';

final public const string UNPREPARED_STATEMENT = 'This statement has not been prepared';

final public const string UNSTARTED_PROFILE = 'A profile must be started before %s can be called.';

final public const string VULNERABLE_PLATFORM_QUOTE =
Expand Down Expand Up @@ -107,6 +109,11 @@ public static function forUncomposableTrait(string $trait, string $interface): s
return new self(sprintf(self::UNCOMPOSABLE_TRAIT, $trait, $interface));
}

public static function forUnpreparedStatement(): self
{
return new self(self::UNPREPARED_STATEMENT);
}

public static function forUnstartedProfile(string $function): self
{
return new self(sprintf(self::UNSTARTED_PROFILE, $function));
Expand Down
1 change: 1 addition & 0 deletions src/Sql/Platform/AbstractPlatform.php
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ public function getSqlString(?PlatformInterface $adapterPlatform = null): string
public function getTypeDecorator(
PreparableSqlInterface|SqlInterface $subject,
): PlatformDecoratorInterface|PreparableSqlInterface|SqlInterface {
/** @var PlatformDecoratorInterface $decorator */
foreach ($this->decorators as $type => $decorator) {
/** @phpstan-ignore-next-line instanceof with string class name is valid */
if (! $subject instanceof $type) {
Expand Down
15 changes: 8 additions & 7 deletions src/Sql/Update.php
Original file line number Diff line number Diff line change
Expand Up @@ -217,13 +217,14 @@ protected function processSet(
continue;
}

$setSql[] = $prefix
. $this->resolveColumnValue(
$value,
$platform,
$driver,
$parameterContainer,
);
$setSql[] =
$prefix
. $this->resolveColumnValue(
$value,
$platform,
$driver,
$parameterContainer,
);
}

return $this->formatSpecification(
Expand Down
19 changes: 19 additions & 0 deletions test/unit/Adapter/Driver/Pdo/StatementTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,16 @@ public function bindParametersDetectsNullValueType(): void
static::assertInstanceOf(Result::class, $result);
}

#[Test]
public function bindParametersThrowsWhenMarkedPreparedWithoutResource(): void
{
$this->statement->setParameterContainer(new ParameterContainer(['val' => 1]));
(new ReflectionProperty($this->statement, 'isPrepared'))->setValue($this->statement, true);

$this->expectExceptionObject(RuntimeException::forUnpreparedStatement());
$this->statement->execute();
}

#[Test]
public function bindParametersWithErrataTypeDefaultsToString(): void
{
Expand Down Expand Up @@ -350,6 +360,15 @@ public function executeThrowsOnInvalidParameterName(string $name): void
$this->statement->execute([$name => 'value']);
}

#[Test]
public function executeThrowsWhenMarkedPreparedWithoutResource(): void
{
(new ReflectionProperty($this->statement, 'isPrepared'))->setValue($this->statement, true);

$this->expectExceptionObject(RuntimeException::forUnpreparedStatement());
$this->statement->execute();
}

#[Test]
public function executeWithArrayParametersMergesIntoContainer(): void
{
Expand Down
Loading
Loading