Skip to content

Commit

Permalink
Use PHPStan 2.0
Browse files Browse the repository at this point in the history
  • Loading branch information
olvlvl committed Nov 17, 2024
1 parent 687ae1c commit ded95ea
Show file tree
Hide file tree
Showing 13 changed files with 96 additions and 83 deletions.
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
},
"require-dev": {
"ext-pdo_mysql": "*",
"phpstan/phpstan": "^1.12",
"phpstan/phpstan": "^2.0",
"phpunit/phpunit": "^11.4"
},
"autoload": {
Expand Down
3 changes: 3 additions & 0 deletions lib/ActiveRecord.php
Original file line number Diff line number Diff line change
Expand Up @@ -92,9 +92,11 @@ protected function get_primary_key_value(): mixed
$actual[] = $this->$property;
}

// @phpstan-ignore-next-line
return $actual;
}

// @phpstan-ignore-next-line
return $this->$primary;
}

Expand Down Expand Up @@ -295,6 +297,7 @@ public function delete(): void
$key = $this->$primary
?? throw new LogicException("Unable to delete record, the primary key is not defined");

// @phpstan-ignore-next-line
$model->delete($key);
}
}
2 changes: 1 addition & 1 deletion lib/ActiveRecord/BelongsToRelation.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ public function __invoke(ActiveRecord $record): ?ActiveRecord
}

/** @var ActiveRecord|null */
return $this->resolve_related_model()->find($id);
return $this->resolve_related_model()->find($id); // @phpstan-ignore argument.type
}

/**
Expand Down
3 changes: 1 addition & 2 deletions lib/ActiveRecord/Connection.php
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ public function query(string $statement, array $args = []): Statement

$this->telemetry->record_execute_duration(
$statement,
static fn() => $statement->execute($args)
static fn() => $statement->execute($args) // @phpstan-ignore argument.type
);

return $statement;
Expand All @@ -223,7 +223,6 @@ public function exec(string $statement): bool|int
$statement = $this->resolve_statement($statement);

try {
// @phpstan-ignore-next-line
return $this->telemetry->record_execute_duration(
$statement,
fn() => $this->pdo->exec($statement)
Expand Down
2 changes: 1 addition & 1 deletion lib/ActiveRecord/Driver/BasicDriver.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ abstract class BasicDriver implements Driver
use AccessorTrait;

/**
* @var callable
* @var callable():Connection
*/
private $connection_provider;

Expand Down
3 changes: 3 additions & 0 deletions lib/ActiveRecord/ModelProviderWithClosure.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@
*/
final readonly class ModelProviderWithClosure implements ModelProvider
{
/**
* @param Closure(string):Model $closure
*/
public function __construct(
private Closure $closure
) {
Expand Down
4 changes: 2 additions & 2 deletions lib/ActiveRecord/Property/DateTimePropertySupport.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,11 @@ public static function get(&$property): DateTime
public static function ensureNotEmpty(&$property, $datetime = 'now'): DateTime
{
if (!self::get($property)->is_empty) {
return $property;
return $property; // @phpstan-ignore return.type
}

self::set($property, $datetime);

return $property;
return $property; // @phpstan-ignore return.type
}
}
101 changes: 56 additions & 45 deletions lib/ActiveRecord/Query.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,34 +36,45 @@
* methods of the {@see Model} class create a {@see Query} object returned for
* further specification, such as filters or limits.
*
* @see self::get_all()
* @property-read array $all An array with all the records matching the query.
* @see self::get_one()
* @property-read TRecord|array<string>|false $one The first record matching the query.
* @see self::get_pairs()
* @property-read array $pairs An array of key/value pairs.
* @see self::get_rc()
* @property-read int|string|false|null $rc The first column of the first row matching the query.
* @see self::get_count()
* @property-read int $count The number of records matching the query.
* @see self::get_exists()
* @property-read bool|array $exists `true` if a record matching the query exists, `false`
* otherwise. If there are multiple records, the property is an array of booleans.
*
* @see self::get_joins()
* @property-read non-empty-string[] $joins The join collection from {@see join()}.
* @see self::get_joins_args()
* @property-read mixed[] $joins_args The arguments to the joins.
* @see self::get_conditions()
* @property-read non-empty-string[] $conditions The collected conditions.
* @see self::get_conditions_args()
* @property-read mixed[] $conditions_args The arguments to the conditions.
* @see self::get_having_args()
* @property-read mixed[] $having_args The arguments to the `HAVING` clause.
* @see self::get_args()
* @property-read mixed[] $args Returns the arguments to the query.
* @see self::get_prepared()
* @property-read Query<TRecord> $prepared Return a prepared query.
* @property-read array<mixed> $all
* An array with all the records matching the query.
* {@see self::get_all()}
* @property-read TRecord|array<string>|false $one
* The first record matching the query.
* {@see self::get_one()}
* @property-read array<string, scalar|null> $pairs
* An array of key/value pairs.
* {@see self::get_pairs()}
* @property-read int|string|false|null $rc
* The value of the first column of the first row.
* {@see self::get_rc()}
* @property-read int $count
* The number of records matching the query.
* {@see self::get_count()}
* @property-read bool $exists
* Whether the query has a match.
* {@see self::get_exists()}
* @property-read non-empty-string[] $joins
* The join collection from {@see join()}.
* {@see self::get_joins()}
* @property-read mixed[] $joins_args
* The arguments to the joins.
* {@see self::get_joins_args()}
* @property-read non-empty-string[] $conditions
* The collected conditions.
* {@see self::get_conditions()}
* @property-read mixed[] $conditions_args
* The arguments to the conditions.
* {@see self::get_conditions_args()}
* @property-read mixed[] $having_args
* The arguments to the `HAVING` clause.
* {@see self::get_having_args()}
* @property-read mixed[] $args
* Returns the arguments to the query.
* {@see self::get_args()}
* @property-read Query<TRecord> $prepared
* Return a prepared query.
* {@see self::get_prepared()}
*/
class Query implements IteratorAggregate
{
Expand Down Expand Up @@ -204,7 +215,7 @@ private function get_args(): array
* @param Model<TRecord> $model The model to query.
*/
public function __construct(
public readonly Model $model
public readonly Model $model // @phpstan-ignore generics.variance
) {
}

Expand Down Expand Up @@ -327,9 +338,8 @@ private function render_order(array $order): string
$field = array_shift($order);
assert(is_string($field));
$field_values = is_array($order[0]) ? $order[0] : $order;
$field_values = array_map(function ($v) use ($connection) {
return $connection->quote($v);
}, $field_values);
// @phpstan-ignore-next-line
$field_values = array_map(fn($v) => $connection->quote($v), $field_values);

return "ORDER BY FIELD($field, " . implode(', ', $field_values) . ")";
}
Expand Down Expand Up @@ -357,8 +367,7 @@ private function render_skip_and_take(?int $skip, ?int $take): string
/**
* Resolve the placeholders of a statement.
*
* Note: Currently, the method simply forwards the statement to the model's
* resolve_statement() method.
* Note: Currently, the method forwards the statement to the model's resolve_statement() method.
*/
private function resolve_statement(string $statement): string
{
Expand Down Expand Up @@ -546,12 +555,12 @@ private function render_join_on(string $column, string $as, Query $query): strin

$target = $this->model;

while ($target) {
while ($target instanceof Model) {
if ($target->schema->has_column($column)) {
break;
}

$target = $target->parent_model;
$target = $target->parent;
}

if (!$target) {
Expand Down Expand Up @@ -587,6 +596,7 @@ private function deferred_parse_conditions(mixed ...$conditions_and_args): array

if (is_array($arg)) {
foreach ($arg as $value) {
// @phpstan-ignore-next-line argument.type
$joined .= ',' . (is_numeric($value) ? $value : $this->model->connection->quote($value));
}

Expand All @@ -608,16 +618,17 @@ private function deferred_parse_conditions(mixed ...$conditions_and_args): array

$conditions = substr($c, 5);
} else {
assert(is_null($conditions) || is_string($conditions));

$conditions_args = [];

if ($args) {
if (is_array($args[0])) {
$conditions_args = $args[0];
} else {
#
# We dereference values otherwise the caller would get a corrupted array.
# We dereference values, otherwise the caller would get a corrupted array.
#

foreach ($args as $key => $value) {
$conditions_args[$key] = $value;
}
Expand Down Expand Up @@ -665,7 +676,7 @@ private function deferred_parse_conditions(mixed ...$conditions_and_args): array
*
* `$model->where([ 'order_id' => [ 123, 456, 789 ] ]);`
*
* This will return the orders with the `order_id` 123, 456 or 789.
* This will return the orders with the `order_id` 123, 456, or 789.
*
* 3. Modifiers
*
Expand All @@ -674,7 +685,7 @@ private function deferred_parse_conditions(mixed ...$conditions_and_args): array
*
* `$model->where([ '!order_id' => [ 123, 456, 789 ]]);`
*
* This will return the orders with the `order_id` different from 123, 456 and 789.
* This will return the orders with the `order_id` different from 123, 456, and 789.
*
* `$model->where([ '!order_count' => 2 ]);`
*
Expand Down Expand Up @@ -821,6 +832,7 @@ protected function get_prepared(): Statement
private function execute(): Statement
{
$statement = $this->prepare();
// @phpstan-ignore-next-line
$statement->execute($this->args);

return $statement;
Expand Down Expand Up @@ -932,11 +944,9 @@ protected function get_rc(): int|string|false|null
* $model->exists(1, 2);
* $model->exists([ 1, 2 ]);
*
* @param mixed $key
*
* @return bool|array
*/
public function exists($key = null) // @phpstan-ignore-line
public function exists(mixed $key = null) // @phpstan-ignore-line
{
if ($key !== null && func_num_args() > 1) {
$key = func_get_args();
Expand Down Expand Up @@ -987,9 +997,9 @@ public function exists($key = null) // @phpstan-ignore-line
return !empty($rc);
}

protected function get_exists(): bool
private function get_exists(): bool
{
// @phpstan-ignore-next-line
/** @var bool */
return $this->exists();
}

Expand Down Expand Up @@ -1021,6 +1031,7 @@ private function compute(string $method, ?string $column = null): string|array
$statement = ($this->model)($query, $this->args);

if ($method == 'COUNT' && $column) {
// @phpstan-ignore-next-line
return $statement->pairs;
}

Expand Down
4 changes: 4 additions & 0 deletions lib/ActiveRecord/Schema.php
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ public function __construct(
public array $indexes = []
) {
foreach ($columns as $name => $column) {
// (double-checking)
// @phpstan-ignore-next-line
$column instanceof Column
or throw new InvalidArgumentException(
sprintf(
Expand All @@ -70,6 +72,8 @@ public function __construct(
}

foreach ($indexes as $index) {
// (double-checking)
// @phpstan-ignore-next-line
$index instanceof Index
or throw new InvalidArgumentException(
sprintf(
Expand Down
32 changes: 12 additions & 20 deletions lib/ActiveRecord/Statement.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,33 +13,25 @@
/**
* A database statement.
*
* @see self::get_as_assoc()
* @property-read $this $as_assoc
* Equivalent to `mode(PDO::FETCH_ASSOC)`.
*
* @see self::get_all()
* @property-read array $all
* {@see self::get_as_assoc()}
* @property-read array<scalar|null> $all
* An array with the matching records.
* @see self::get_pairs()
* @property-read array $pairs
* {@see self::get_all()}
* @property-read array<scalar|null> $pairs
* An array of key/value pairs, where _key_ is the value of the first column and _value_ the value of the second
* column.
* @see self::get_one()
* {@see self::get_pairs()}
* @property-read mixed $one
* The first row of the result set (the cursor is closed).
* @see self::get_rc()
* {@see self::get_one()}
* @property-read int|string|false|null $rc
* The value of the first column of the first row.
* {@see self::get_rc()}
*/
final class Statement
{
/**
* @uses get_as_assoc
* @uses get_one
* @uses get_rc
* @uses get_all
* @uses get_pairs
*/
use AccessorTrait;

public function __construct(
Expand All @@ -64,7 +56,7 @@ public function __invoke(mixed ...$args): self
$args = $args[0];
}

$this->execute($args);
$this->execute($args); // @phpstan-ignore argument.type

return $this;
}
Expand All @@ -82,7 +74,7 @@ public function __toString(): string
*
* The connection queries count is incremented.
*
* @param mixed[] $params
* @param array<scalar|null> $params
*
* @throws StatementNotValid when the execution of the statement fails.
*/
Expand All @@ -103,7 +95,7 @@ public function execute(array $params = []): void
*
* @return $this
*
* @throws UnableToSetFetchMode if the mode cannot be set.
* @throws UnableToSetFetchMode if the mode can't be set.
*
* @see PDOStatement::setFetchMode()
*/
Expand Down Expand Up @@ -185,13 +177,13 @@ private function get_all(): array
}

/**
* @return array<string, string>
* @return array<string, scalar>
* Where _key_ is the value of the first column and _value_ the value of the second column.
*
* @see $pairs
*/
private function get_pairs(): array
{
return $this->pdo_statement->fetchAll(PDO::FETCH_KEY_PAIR);
return $this->pdo_statement->fetchAll(PDO::FETCH_KEY_PAIR); // @phpstan-ignore return.type
}
}
Loading

0 comments on commit ded95ea

Please sign in to comment.