Skip to content

Commit 71b7d61

Browse files
committed
Add comprehensive type annotations to AsyncPDO class
- Add generic type parameters and return type annotations for all public methods - Specify array shapes using PHPDoc syntax for better type safety - Add template types for callback parameters and return values - Replace loose array types with specific array<key, value> annotations - Improve null safety checks and comparisons - Remove unused PromiseCollectionHandler import and use global functions
1 parent a7a36c3 commit 71b7d61

File tree

1 file changed

+67
-68
lines changed

1 file changed

+67
-68
lines changed

src/Api/AsyncPDO.php

Lines changed: 67 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
namespace Rcalicdan\FiberAsync\Api;
44

55
use PDO;
6-
use Rcalicdan\FiberAsync\Async\Handlers\PromiseCollectionHandler;
76
use Rcalicdan\FiberAsync\PDO\AsyncPdoPool;
87
use Rcalicdan\FiberAsync\Promise\CancellablePromise;
98
use Rcalicdan\FiberAsync\Promise\Interfaces\PromiseInterface;
@@ -30,7 +29,7 @@ final class AsyncPDO
3029
* This is the single point of configuration and must be called before
3130
* using any other AsyncPDO methods. Multiple calls are ignored.
3231
*
33-
* @param array $dbConfig Database configuration array containing:
32+
* @param array<string, mixed> $dbConfig Database configuration array containing:
3433
* - dsn: Database connection string
3534
* - username: Database username
3635
* - password: Database password
@@ -55,7 +54,7 @@ public static function init(array $dbConfig, int $poolSize = 10): void
5554
*/
5655
public static function reset(): void
5756
{
58-
if (self::$pool) {
57+
if (self::$pool !== null) {
5958
self::$pool->close();
6059
}
6160
self::$pool = null;
@@ -68,23 +67,23 @@ public static function reset(): void
6867
* Automatically handles connection acquisition and release. The callback
6968
* receives a PDO instance and can perform any database operations.
7069
*
71-
* @param callable $callback Function that receives PDO instance
72-
* Signature: function(PDO $pdo): mixed
73-
* @return PromiseInterface Promise resolving to callback's return value
70+
* @template TResult
71+
* @param callable(PDO): TResult $callback Function that receives PDO instance
72+
* @return PromiseInterface<TResult> Promise resolving to callback's return value
7473
*
7574
* @throws \RuntimeException If AsyncPDO is not initialized
7675
*/
7776
public static function run(callable $callback): PromiseInterface
7877
{
79-
return Async::async(function () use ($callback) {
78+
return Async::async(function () use ($callback): mixed {
8079
$pdo = null;
8180

8281
try {
8382
$pdo = await(self::getPool()->get());
8483

8584
return $callback($pdo);
8685
} finally {
87-
if ($pdo) {
86+
if ($pdo !== null) {
8887
self::getPool()->release($pdo);
8988
}
9089
}
@@ -95,15 +94,15 @@ public static function run(callable $callback): PromiseInterface
9594
* Executes a SELECT query and returns all matching rows.
9695
*
9796
* @param string $sql SQL query with optional parameter placeholders
98-
* @param array $params Parameter values for prepared statement
99-
* @return PromiseInterface Promise resolving to array of associative arrays
97+
* @param array<string|int, mixed> $params Parameter values for prepared statement
98+
* @return PromiseInterface<array<int, array<string, mixed>>> Promise resolving to array of associative arrays
10099
*
101100
* @throws \RuntimeException If AsyncPDO is not initialized
102101
* @throws \PDOException If query execution fails
103102
*/
104103
public static function query(string $sql, array $params = []): PromiseInterface
105104
{
106-
return self::run(function (PDO $pdo) use ($sql, $params) {
105+
return self::run(function (PDO $pdo) use ($sql, $params): array {
107106
$stmt = $pdo->prepare($sql);
108107
$stmt->execute($params);
109108

@@ -115,15 +114,15 @@ public static function query(string $sql, array $params = []): PromiseInterface
115114
* Executes a SELECT query and returns the first matching row.
116115
*
117116
* @param string $sql SQL query with optional parameter placeholders
118-
* @param array $params Parameter values for prepared statement
119-
* @return PromiseInterface Promise resolving to associative array or false if no rows
117+
* @param array<string|int, mixed> $params Parameter values for prepared statement
118+
* @return PromiseInterface<array<string, mixed>|false> Promise resolving to associative array or false if no rows
120119
*
121120
* @throws \RuntimeException If AsyncPDO is not initialized
122121
* @throws \PDOException If query execution fails
123122
*/
124123
public static function fetchOne(string $sql, array $params = []): PromiseInterface
125124
{
126-
return self::run(function (PDO $pdo) use ($sql, $params) {
125+
return self::run(function (PDO $pdo) use ($sql, $params): array|false {
127126
$stmt = $pdo->prepare($sql);
128127
$stmt->execute($params);
129128

@@ -135,31 +134,52 @@ public static function fetchOne(string $sql, array $params = []): PromiseInterfa
135134
* Executes an INSERT, UPDATE, or DELETE statement and returns affected row count.
136135
*
137136
* @param string $sql SQL statement with optional parameter placeholders
138-
* @param array $params Parameter values for prepared statement
139-
* @return PromiseInterface Promise resolving to number of affected rows
137+
* @param array<string|int, mixed> $params Parameter values for prepared statement
138+
* @return PromiseInterface<int> Promise resolving to number of affected rows
140139
*
141140
* @throws \RuntimeException If AsyncPDO is not initialized
142141
* @throws \PDOException If statement execution fails
143142
*/
144143
public static function execute(string $sql, array $params = []): PromiseInterface
145144
{
146-
return self::run(function (PDO $pdo) use ($sql, $params) {
145+
return self::run(function (PDO $pdo) use ($sql, $params): int {
147146
$stmt = $pdo->prepare($sql);
148147
$stmt->execute($params);
149148

150149
return $stmt->rowCount();
151150
});
152151
}
153152

153+
/**
154+
* Executes a query and returns a single column value from the first row.
155+
*
156+
* Useful for queries that return a single scalar value like COUNT, MAX, etc.
157+
*
158+
* @param string $sql SQL query with optional parameter placeholders
159+
* @param array<string|int, mixed> $params Parameter values for prepared statement
160+
* @return PromiseInterface<mixed> Promise resolving to scalar value or false if no rows
161+
*
162+
* @throws \RuntimeException If AsyncPDO is not initialized
163+
* @throws \PDOException If query execution fails
164+
*/
165+
public static function fetchValue(string $sql, array $params = []): PromiseInterface
166+
{
167+
return self::run(function (PDO $pdo) use ($sql, $params): mixed {
168+
$stmt = $pdo->prepare($sql);
169+
$stmt->execute($params);
170+
171+
return $stmt->fetch(PDO::FETCH_COLUMN);
172+
});
173+
}
174+
154175
/**
155176
* Executes multiple operations within a database transaction.
156177
*
157178
* Automatically handles transaction begin/commit/rollback. If the callback
158179
* throws an exception, the transaction is rolled back automatically.
159180
*
160-
* @param callable $callback Transaction callback receiving PDO instance
161-
* Signature: function(PDO $pdo): mixed
162-
* @return PromiseInterface Promise resolving to callback's return value
181+
* @param callable(PDO): mixed $callback Transaction callback receiving PDO instance
182+
* @return PromiseInterface<mixed> Promise resolving to callback's return value
163183
*
164184
* @throws \RuntimeException If AsyncPDO is not initialized
165185
* @throws \PDOException If transaction operations fail
@@ -183,47 +203,27 @@ public static function transaction(callable $callback): PromiseInterface
183203
});
184204
}
185205

186-
/**
187-
* Executes a query and returns a single column value from the first row.
188-
*
189-
* Useful for queries that return a single scalar value like COUNT, MAX, etc.
190-
*
191-
* @param string $sql SQL query with optional parameter placeholders
192-
* @param array $params Parameter values for prepared statement
193-
* @return PromiseInterface Promise resolving to scalar value or false if no rows
194-
*
195-
* @throws \RuntimeException If AsyncPDO is not initialized
196-
* @throws \PDOException If query execution fails
197-
*/
198-
public static function fetchValue(string $sql, array $params = []): PromiseInterface
199-
{
200-
return self::run(function (PDO $pdo) use ($sql, $params) {
201-
$stmt = $pdo->prepare($sql);
202-
$stmt->execute($params);
203-
204-
return $stmt->fetch(PDO::FETCH_COLUMN);
205-
});
206-
}
207-
208206
/**
209207
* Race multiple transactions and commit only the winner, rolling back all others.
210208
*
211209
* Executes multiple transactions concurrently and commits the first one to complete
212210
* successfully while cancelling and rolling back all others. Useful for scenarios
213211
* like inventory reservation where only one transaction should succeed.
214212
*
215-
* @param array $transactions Array of transaction callbacks
216-
* Each callback signature: function(PDO $pdo): mixed
217-
* @return PromiseInterface Promise that resolves with the winner's result
213+
* @param array<int, callable(PDO): mixed> $transactions Array of transaction callbacks
214+
* @return PromiseInterface<mixed> Promise that resolves with the winner's result
218215
*
219216
* @throws \RuntimeException If AsyncPDO is not initialized
220217
* @throws Throwable If all transactions fail or system error occurs
221218
*/
222219
public static function raceTransactions(array $transactions): PromiseInterface
223220
{
224-
return Async::async(function () use ($transactions) {
221+
return Async::async(function () use ($transactions): mixed {
222+
/** @var array<int, CancellablePromise<array{result: mixed, winner_index: int, success: bool}>> $transactionPromises */
225223
$transactionPromises = [];
224+
/** @var array<int, PDO> $pdoConnections */
226225
$pdoConnections = [];
226+
/** @var array<int, CancellablePromise<array{result: mixed, winner_index: int, success: bool}>> $cancellablePromises */
227227
$cancellablePromises = [];
228228

229229
foreach ($transactions as $index => $transactionCallback) {
@@ -232,10 +232,9 @@ public static function raceTransactions(array $transactions): PromiseInterface
232232
$cancellablePromises[$index] = $cancellablePromise;
233233
}
234234

235-
$collectionHandler = new PromiseCollectionHandler;
236-
237235
try {
238-
$winnerResult = await($collectionHandler->race($transactionPromises));
236+
/** @var array{result: mixed, winner_index: int, success: bool} $winnerResult */
237+
$winnerResult = await(race($transactionPromises));
239238

240239
self::cancelLosingTransactions($cancellablePromises, $winnerResult['winner_index']);
241240

@@ -257,16 +256,16 @@ public static function raceTransactions(array $transactions): PromiseInterface
257256
* Creates a cancellable promise that executes a transaction callback and
258257
* stores the PDO connection for later cleanup operations.
259258
*
260-
* @param callable $transactionCallback Transaction function to execute
259+
* @param callable(PDO): mixed $transactionCallback Transaction function to execute
261260
* @param int $index Transaction index for identification
262-
* @param array &$pdoConnections Reference to array storing PDO connections
263-
* @return CancellablePromise Promise that can be cancelled mid-execution
261+
* @param array<int, PDO> $pdoConnections Reference to array storing PDO connections
262+
* @return CancellablePromise<array{result: mixed, winner_index: int, success: bool}> Promise that can be cancelled mid-execution
264263
*
265264
* @internal This method is for internal use by raceTransactions()
266265
*/
267266
private static function startCancellableRacingTransaction(callable $transactionCallback, int $index, array &$pdoConnections): CancellablePromise
268267
{
269-
$cancellablePromise = new CancellablePromise(function ($resolve, $reject) use ($transactionCallback, $index, &$pdoConnections) {
268+
$cancellablePromise = new CancellablePromise(function (callable $resolve, callable $reject) use ($transactionCallback, $index, &$pdoConnections) {
270269
$pdo = await(self::getPool()->get());
271270
$pdoConnections[$index] = $pdo;
272271

@@ -295,7 +294,7 @@ private static function startCancellableRacingTransaction(callable $transactionC
295294
}
296295
self::getPool()->release($pdo);
297296
} catch (Throwable $e) {
298-
error_log("Failed to cancel transaction {$index}: ".$e->getMessage());
297+
error_log("Failed to cancel transaction {$index}: " . $e->getMessage());
299298
self::getPool()->release($pdo);
300299
}
301300
}
@@ -310,7 +309,7 @@ private static function startCancellableRacingTransaction(callable $transactionC
310309
* Iterates through all racing transactions and cancels those that didn't win,
311310
* triggering their rollback handlers.
312311
*
313-
* @param array $cancellablePromises Array of CancellablePromise instances
312+
* @param array<int, CancellablePromise<array{result: mixed, winner_index: int, success: bool}>> $cancellablePromises Array of CancellablePromise instances
314313
* @param int $winnerIndex Index of the winning transaction to preserve
315314
*
316315
* @internal This method is for internal use by raceTransactions()
@@ -329,7 +328,7 @@ private static function cancelLosingTransactions(array $cancellablePromises, int
329328
*
330329
* Emergency cancellation of all racing transactions when a system error occurs.
331330
*
332-
* @param array $cancellablePromises Array of CancellablePromise instances
331+
* @param array<int, CancellablePromise<array{result: mixed, winner_index: int, success: bool}>> $cancellablePromises Array of CancellablePromise instances
333332
*
334333
* @internal This method is for internal use by raceTransactions()
335334
*/
@@ -348,17 +347,17 @@ private static function cancelAllTransactions(array $cancellablePromises): void
348347
* Commits the winning transaction and releases its connection back to the pool.
349348
* Losing transactions should already be cancelled by this point.
350349
*
351-
* @param array $pdoConnections Array of PDO connections indexed by transaction
350+
* @param array<int, PDO> $pdoConnections Array of PDO connections indexed by transaction
352351
* @param int $winnerIndex Index of the winning transaction
353-
* @return PromiseInterface Promise that resolves when finalization is complete
352+
* @return PromiseInterface<void> Promise that resolves when finalization is complete
354353
*
355354
* @throws Throwable If commit fails
356355
*
357356
* @internal This method is for internal use by raceTransactions()
358357
*/
359358
private static function finalizeRacingTransactions(array $pdoConnections, int $winnerIndex): PromiseInterface
360359
{
361-
return Async::async(function () use ($pdoConnections, $winnerIndex) {
360+
return Async::async(function () use ($pdoConnections, $winnerIndex): void {
362361
if (isset($pdoConnections[$winnerIndex])) {
363362
$pdo = $pdoConnections[$winnerIndex];
364363

@@ -369,7 +368,7 @@ private static function finalizeRacingTransactions(array $pdoConnections, int $w
369368
}
370369
self::getPool()->release($pdo);
371370
} catch (Throwable $e) {
372-
error_log("Failed to commit winner transaction {$winnerIndex}: ".$e->getMessage());
371+
error_log("Failed to commit winner transaction {$winnerIndex}: " . $e->getMessage());
373372
$pdo->rollBack();
374373
self::getPool()->release($pdo);
375374

@@ -385,32 +384,32 @@ private static function finalizeRacingTransactions(array $pdoConnections, int $w
385384
* Emergency cleanup that rolls back all racing transactions when a system
386385
* error occurs before a winner can be determined.
387386
*
388-
* @param array $pdoConnections Array of PDO connections to rollback
389-
* @return PromiseInterface Promise that resolves when all rollbacks complete
387+
* @param array<int, PDO> $pdoConnections Array of PDO connections to rollback
388+
* @return PromiseInterface<void> Promise that resolves when all rollbacks complete
390389
*
391390
* @internal This method is for internal use by raceTransactions()
392391
*/
393392
private static function rollbackAllTransactions(array $pdoConnections): PromiseInterface
394393
{
395-
return Async::async(function () use ($pdoConnections) {
394+
return Async::async(function () use ($pdoConnections): void {
395+
/** @var array<int, PromiseInterface<void>> $rollbackPromises */
396396
$rollbackPromises = [];
397397

398398
foreach ($pdoConnections as $index => $pdo) {
399-
$rollbackPromises[] = Async::async(function () use ($pdo, $index) {
399+
$rollbackPromises[] = Async::async(function () use ($pdo, $index): void {
400400
try {
401401
if ($pdo->inTransaction()) {
402402
$pdo->rollBack();
403403
}
404404
self::getPool()->release($pdo);
405405
} catch (Throwable $e) {
406-
error_log("Failed to rollback transaction {$index}: ".$e->getMessage());
406+
error_log("Failed to rollback transaction {$index}: " . $e->getMessage());
407407
self::getPool()->release($pdo);
408408
}
409409
})();
410410
}
411411

412-
$collectionHandler = new PromiseCollectionHandler;
413-
await($collectionHandler->all($rollbackPromises));
412+
await(all($rollbackPromises));
414413
})();
415414
}
416415

@@ -425,7 +424,7 @@ private static function rollbackAllTransactions(array $pdoConnections): PromiseI
425424
*/
426425
private static function getPool(): AsyncPdoPool
427426
{
428-
if (! self::$isInitialized) {
427+
if (! self::$isInitialized || self::$pool === null) {
429428
throw new \RuntimeException(
430429
'AsyncPDO has not been initialized. Please call AsyncPDO::init() at application startup.'
431430
);

0 commit comments

Comments
 (0)