Download the PHP package phpdot/pool without Composer
On this page you can find all versions of the php package phpdot/pool. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.
Informations about the package pool
phpdot/pool
Generic, coroutine-safe connection pool for Swoole. Holds objects of any type behind a
Swoole\Coroutine\Channel, so borrowing and releasing are lock-free at the C level. Creates
connections up to a cap, reaps idle ones, optionally heartbeats them, validates on borrow and
return, and prevents leaks and cross-coroutine sharing — created in onWorkerStart, closed in
onWorkerStop.
Table of Contents
- Requirements
- Installation
- Usage
- Define a Connector
- Create and Initialize
- Borrow and Release
- Discard
- Configuration
- Idle Cleanup
- Heartbeat
- Validate on Borrow and Return
- Stats
- Shutdown and Draining
- Framework Wiring
- Architecture
- Testing
- License
Requirements
| Requirement | Constraint |
|---|---|
| PHP | >= 8.5 |
ext-swoole |
>= 6.2 |
phpdot/config |
^0.4 |
phpdot/contracts |
^0.4 |
psr/container |
^2.0 |
Installation
Usage
Define a Connector
The pool does not know what it pools. A connector — implementing
PHPdot\Contracts\Pool\ConnectorInterface (shipped by phpdot/contracts) — tells it how to
create, health-check, and close the underlying object.
isAlive() should be a single cheap round-trip (e.g. PING, SELECT 1); only a server-side
check catches connections killed by idle timeouts, firewall drops, or restarts.
Create and Initialize
The first borrow() initialises the pool — minConnections are created and the maintenance
timers start, inside the coroutine the borrow runs in. init() remains for explicit
pre-warming and is idempotent: a second call, explicit or self-triggered, is a no-op.
Borrow and Release
Borrow a connection, use it, then return it. On exhaustion borrow() waits up to
borrowTimeout, growing the pool on demand up to maxConnections.
borrow(): object— throwsPHPdot\Pool\Exception\BorrowTimeoutExceptionwhen none becomes available withinborrowTimeout, orPHPdot\Pool\Exception\PoolClosedExceptionafterclose().release(object $connection): void— returns the connection to the pool; releasing an unknown or already-released connection is silently ignored.
Discard
Permanently close a connection that must not be reused (a broken one), freeing its slot.
Configuration
PoolConfig is an immutable value object (also discoverable as #[Config('pool')]).
Total connections to the backing service = workers x maxConnections (e.g. 4 x 10 = 40).
Idle Cleanup
When maxIdleTime > 0.0, a timer every idleCheckInterval seconds closes connections idle
longer than maxIdleTime, never dropping below minConnections. Connections in use are untouched.
Heartbeat
When heartbeatInterval > 0.0, a separate timer calls isAlive() on idle connections and closes
dead ones, refilling toward minConnections. Off by default — enable it for backends that drop
idle connections aggressively.
Validate on Borrow and Return
- On borrow — when
validateOnBorrowAfterIdle >= 0.0and a popped connection has been idle at least that many seconds,isAlive()is called before hand-off; a dead one is closed and the borrow loop tries again.0.0validates every borrow; a negative value disables it. - On return — when
validateOnReturnistrue(default),release()callsisAlive()and discards (rather than re-pools) dead connections, so a connection poisoned mid-use cannot be handed straight back out.
Stats
stats() returns an immutable PoolStats snapshot for monitoring and health checks.
Shutdown and Draining
close(): void— full synchronous shutdown: stop timers, drain and close idle connections; borrowed connections close on their later release.isClosed(): boolreports the state.suspendTimers(): void— stop the idle/heartbeat timers without closing the pool, so in-flightborrow()calls still complete against live connections. Use it ononWorkerExitduring a graceful drain; the OS closes pooled connections when the worker exits.
Framework Wiring — the DI way
Applications do not touch Pool at all. pooled() binds a connection class to a named pool
as a scoped definition: the first resolution inside a coroutine borrows from the pool,
every later resolution in the same coroutine returns the same connection, and coroutine end
releases it — the container's own scoping does the lifecycle. Outside a coroutine (CLI, boot,
migrations) the same definition hands a dedicated, unpooled connection.
Each pool reads its own client's config block: the pool key sizes the pool, everything
else in the block hydrates the connector's configuration — when that parameter is a concrete
class (RedisConnector(RedisConfig)), automatically; when it is an interface
(DatabaseConnector(ConnectionConfig)), bind the interface and the registry resolves it
from the container. Dotted names address a multi-pool client's pools sub-block.
Sizing model: a scoped connection is held for the whole request, so a pool's max must
cover that worker's concurrent requests — a database pool of 100 is that model, and a
non-hookable client like MongoDB ('pool' => ['max' => 1]) serialises through a single
connection. Long-lived coroutines (SSE, WebSocket streams) must not hold a scoped connection
for the stream's lifetime: resolve them through PoolRegistry::connection() and release
when the work, not the stream, ends.
The one lifecycle an application still owns is the worker-exit drain — stop the maintenance timers so an exiting worker's event loop can close, never closing the pool itself:
For direct, manual wiring the low-level hooks remain: construct the pool after the worker
fork, suspendTimers() on worker exit, close() on worker stop.
Architecture
Pool is built on Swoole\Coroutine\Channel, a coroutine-safe bounded FIFO: pop() suspends
only the calling coroutine (never the worker process), push() wakes the next waiter, and the
lock is at the C level. Growth reserves the slot (currentCount++) before the yielding
connect(), so concurrent coroutines cannot overshoot maxConnections. The connection type is
supplied entirely through ConnectorInterface, which lives in phpdot/contracts — this package
depends on the contract, never on a concrete driver.
Testing
The package is standalone-testable:
License
MIT.
This repository is a read-only mirror, generated by CI from phpdot/monorepo. Pull requests and issues belong in the monorepo.
All versions of pool with dependencies
php Version >=8.5
phpdot/config Version ^0.5
phpdot/contracts Version ^0.5
psr/container Version ^2.0