Skip to content

Commit 49607e8

Browse files
committed
Support PHP 8.4
Also switched from Psalm to PHPStan.
1 parent 1614e43 commit 49607e8

13 files changed

Lines changed: 70 additions & 72 deletions

File tree

.github/workflows/php.yml

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ jobs:
77

88
strategy:
99
matrix:
10-
php: [ '8.1', '8.2', '8.3' ]
10+
php: [ '8.1', '8.2', '8.3', '8.4' ]
1111

1212
services:
1313
mysql:
@@ -38,18 +38,19 @@ jobs:
3838
uses: shivammathur/setup-php@v2
3939
with:
4040
php-version: ${{ matrix.php }}
41-
tools: psalm
42-
extensions: sqlsrv-5.10.1
4341

4442
- name: Setup problem matchers for PHPUnit
4543
run: echo "::add-matcher::${{ runner.tool_cache }}/phpunit.json"
4644

45+
- name: Validate composer.json and composer.lock
46+
run: composer validate --strict
47+
4748
- name: Install Composer dependencies
4849
run: composer install --no-progress
4950

50-
- name: Run Psalm
51-
run: psalm --output-format=github
52-
if: ${{ matrix.php == '8.3' }}
51+
- name: Perform static analysis
52+
run: composer analyze -- --error-format=github
53+
if: ${{ matrix.php == '8.4' }}
5354

5455
- name: Run PHPUnit
5556
run: composer test-without-mssql

composer.json

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,9 @@
2121
},
2222
"require-dev": {
2323
"friendsofphp/php-cs-fixer": "^3.64",
24+
"phpstan/phpstan": "^2.1",
2425
"phpunit/phpunit": "^10.5",
25-
"psalm/plugin-phpunit": "^0.19",
26-
"ramsey/uuid": "^4.2.3",
27-
"vimeo/psalm": "^5.26"
26+
"ramsey/uuid": "^4.2.3"
2827
},
2928
"autoload": {
3029
"psr-4": {
@@ -40,7 +39,7 @@
4039
"sort-packages": true
4140
},
4241
"scripts": {
43-
"analyze": "psalm",
42+
"analyze": "phpstan analyze --level 10 src test",
4443
"cs-fix": "php-cs-fixer fix -v",
4544
"test": "phpunit",
4645
"test-mssql": "phpunit --exclude-group mysql,pgsql",

psalm.xml

Lines changed: 0 additions & 22 deletions
This file was deleted.

src/Options.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,10 @@ public function __construct(
5050
} elseif ($this->driver === 'pgsql') {
5151
$this->binarySelectedAsStream = true;
5252
$this->nativeBoolColumns = true;
53-
$this->floatSelectedAsString = true;
53+
54+
if (PHP_VERSION_ID < 80_400) {
55+
$this->floatSelectedAsString = true;
56+
}
5457
}
5558
}
5659
}

src/PeachySql.php

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ public function __construct(PDO $connection, ?Options $options = null)
3636
public function begin(): void
3737
{
3838
if (!$this->conn->beginTransaction()) {
39+
/** @phpstan-ignore argument.type */
3940
throw $this->getError('Failed to begin transaction', $this->conn->errorInfo());
4041
}
4142
}
@@ -47,6 +48,7 @@ public function begin(): void
4748
public function commit(): void
4849
{
4950
if (!$this->conn->commit()) {
51+
/** @phpstan-ignore argument.type */
5052
throw $this->getError('Failed to commit transaction', $this->conn->errorInfo());
5153
}
5254
}
@@ -58,6 +60,7 @@ public function commit(): void
5860
public function rollback(): void
5961
{
6062
if (!$this->conn->rollback()) {
63+
/** @phpstan-ignore argument.type */
6164
throw $this->getError('Failed to roll back transaction', $this->conn->errorInfo());
6265
}
6366
}
@@ -72,10 +75,12 @@ final public function makeBinaryParam(?string $binaryStr): array
7275
return [$binaryStr, PDO::PARAM_LOB, 0, $driverOptions];
7376
}
7477

75-
/** @internal */
78+
/**
79+
* @param array{0: string, 1: int|null, 2: string|null} $error
80+
* @internal
81+
*/
7682
public static function getError(string $message, array $error): SqlException
7783
{
78-
/** @var array{0: string, 1: int|null, 2: string|null} $error */
7984
$code = $error[1] ?? 0;
8085
$details = $error[2] ?? '';
8186
$sqlState = $error[0];
@@ -84,18 +89,19 @@ public static function getError(string $message, array $error): SqlException
8489
}
8590

8691
/**
87-
* Returns a prepared statement which can be executed multiple times
92+
* Returns a prepared statement which can be executed multiple times.
93+
* @param list<mixed> $params
8894
* @throws SqlException if an error occurs
8995
*/
9096
public function prepare(string $sql, array $params = []): Statement
9197
{
9298
try {
9399
if (!$stmt = $this->conn->prepare($sql)) {
100+
/** @phpstan-ignore argument.type */
94101
throw $this->getError('Failed to prepare statement', $this->conn->errorInfo());
95102
}
96103

97104
$i = 0;
98-
/** @psalm-suppress MixedAssignment */
99105
foreach ($params as &$param) {
100106
$i++;
101107

@@ -111,14 +117,16 @@ public function prepare(string $sql, array $params = []): Statement
111117
}
112118
}
113119
} catch (\PDOException $e) {
120+
/** @phpstan-ignore argument.type */
114121
throw $this->getError('Failed to prepare statement', $this->conn->errorInfo());
115122
}
116123

117124
return new Statement($stmt, $this->usedPrepare, $this->options);
118125
}
119126

120127
/**
121-
* Prepares and executes a single query with bound parameters
128+
* Prepares and executes a single query with bound parameters.
129+
* @param list<mixed> $params
122130
*/
123131
public function query(string $sql, array $params = []): Statement
124132
{

src/QueryBuilder/Insert.php

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ public static function batchRows(array $colVals, int $maxBoundParams, int $maxRo
2929
$maxRowsPerQuery = $maxRows;
3030
}
3131

32+
/** @phpstan-ignore argument.type */
3233
return array_chunk($colVals, $maxRowsPerQuery);
3334
}
3435

@@ -38,25 +39,18 @@ public static function batchRows(array $colVals, int $maxBoundParams, int $maxRo
3839
*/
3940
public function buildQuery(string $table, array $colVals): SqlParams
4041
{
41-
self::validateColValsStructure($colVals);
42+
if (!$colVals || empty($colVals[0])) {
43+
throw new \Exception('A valid array of columns/values to insert must be specified');
44+
}
4245

4346
$columns = $this->escapeColumns(array_keys($colVals[0]));
4447
$insert = "INSERT INTO {$table} (" . implode(', ', $columns) . ')';
4548

4649
$valSetStr = ' (' . str_repeat('?,', count($columns) - 1) . '?),';
4750
$valStr = ' VALUES' . substr_replace(str_repeat($valSetStr, count($colVals)), '', -1); // remove trailing comma
48-
$params = array_merge(...array_map('array_values', $colVals));
51+
/** @phpstan-ignore argument.type */
52+
$params = array_merge(...array_map(array_values(...), $colVals));
4953

5054
return new SqlParams($insert . $valStr, $params);
5155
}
52-
53-
/**
54-
* @throws \Exception if the column/values array does not have a valid structure
55-
*/
56-
private static function validateColValsStructure(array $colVals): void
57-
{
58-
if (empty($colVals[0]) || !is_array($colVals[0])) {
59-
throw new \Exception('A valid array of columns/values to insert must be specified');
60-
}
61-
}
6256
}

src/QueryBuilder/Select.php

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
class Select extends Query
99
{
1010
/**
11+
* @param string[] $orderBy
1112
* @throws \Exception if there is an invalid sort direction
1213
*/
1314
public function buildOrderByClause(array $orderBy): string
@@ -19,12 +20,10 @@ public function buildOrderByClause(array $orderBy): string
1920
$sql = ' ORDER BY ';
2021

2122
// [column1, column2, ...]
22-
if (isset($orderBy[0])) {
23-
/** @var array<int, string> $orderBy */
23+
if (array_is_list($orderBy)) {
2424
return $sql . implode(', ', $this->escapeColumns($orderBy));
2525
}
2626

27-
/** @var array<string, string> $orderBy */
2827
// [column1 => direction, column2 => direction, ...]
2928
foreach ($orderBy as $column => $direction) {
3029
$column = $this->escapeIdentifier($column);

src/QueryBuilder/Selector.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ class Selector
1111
{
1212
/** @var WhereClause */
1313
private array $where = [];
14+
/**
15+
* @var string[]
16+
*/
1417
private array $orderBy = [];
1518
private ?int $limit = null;
1619
private ?int $offset = null;
@@ -35,6 +38,7 @@ public function where(array $filter): static
3538
}
3639

3740
/**
41+
* @param string[] $sort
3842
* @throws \Exception if called more than once
3943
*/
4044
public function orderBy(array $sort): static

src/QueryBuilder/Update.php

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ public function buildQuery(string $table, array $set, array $where): SqlParams
2424
$params = [];
2525
$sql = "UPDATE {$table} SET ";
2626

27-
/** @psalm-suppress MixedAssignment */
2827
foreach ($set as $column => $value) {
2928
$sql .= $this->escapeIdentifier($column) . ' = ?, ';
3029
$params[] = $value;

src/Statement.php

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,11 @@ public function execute(): void
3636

3737
try {
3838
if (!$this->stmt->execute()) {
39+
/** @phpstan-ignore argument.type */
3940
throw PeachySql::getError('Failed to execute prepared statement', $this->stmt->errorInfo());
4041
}
4142
} catch (PDOException $e) {
43+
/** @phpstan-ignore argument.type */
4244
throw PeachySql::getError('Failed to execute prepared statement', $this->stmt->errorInfo());
4345
}
4446

@@ -61,15 +63,13 @@ public function execute(): void
6163

6264
/**
6365
* Returns an iterator which can be used to loop through each row in the result
64-
* @return \Generator<int, array>
66+
* @return \Generator<int, mixed[]>
6567
*/
6668
public function getIterator(): \Generator
6769
{
6870
if ($this->stmt !== null) {
69-
while (
70-
/** @var array|false $row */
71-
$row = $this->stmt->fetch(PDO::FETCH_ASSOC)
72-
) {
71+
while ($row = $this->stmt->fetch(PDO::FETCH_ASSOC)) {
72+
/** @phpstan-ignore generator.valueType */
7373
yield $row;
7474
}
7575

@@ -94,20 +94,23 @@ public function close(): void
9494
}
9595

9696
/**
97-
* Returns all rows selected by the query
97+
* Returns all rows selected by the query.
98+
* @return mixed[]
9899
*/
99100
public function getAll(): array
100101
{
101102
return iterator_to_array($this->getIterator());
102103
}
103104

104105
/**
105-
* Returns the first selected row, or null if zero rows were returned
106+
* Returns the first selected row, or null if zero rows were returned.
107+
* @return mixed[]|null
106108
*/
107109
public function getFirst(): ?array
108110
{
109111
$row = $this->getIterator()->current();
110112

113+
/** @phpstan-ignore notIdentical.alwaysTrue */
111114
if ($row !== null) {
112115
$this->close(); // don't leave the SQL statement open
113116
}

0 commit comments

Comments
 (0)