PHPackages                             suhock/disposable - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. suhock/disposable

ActiveLibrary

suhock/disposable
=================

A minimal disposable library for deterministic resource cleanup

1.0.0(1mo ago)044↓66.7%MITPHPPHP ^8.3CI passing

Since Jul 18Pushed 1mo agoCompare

[ Source](https://github.com/suhock/php-disposable)[ Packagist](https://packagist.org/packages/suhock/disposable)[ Docs](https://github.com/suhock/php-disposable)[ RSS](/packages/suhock-disposable/feed)WikiDiscussions main Synced 2w ago

READMEChangelog (1)Dependencies (5)Versions (2)Used By (0)

Disposable for PHP
==================

[](#disposable-for-php)

A minimal primitive for deterministic resource cleanup: the `DisposableInterface`contract, and a `using()` helper that runs a callback and disposes afterward.

Installation
------------

[](#installation)

```
composer require suhock/disposable
```

Usage
-----

[](#usage)

Implement `DisposableInterface` on anything that must release something when its lifetime ends, and wrap its use in `using()`:

```
use Suhock\Disposable\DisposableInterface;

use function Suhock\Disposable\using;

final class UnitOfWork implements DisposableInterface
{
    private bool $disposed = false;

    public function __construct(
        private readonly Connection $connection,
    ) {}

    public function dispose(): void
    {
        if ($this->disposed) {
            return;
        }

        $this->disposed = true;
        $this->connection->rollBackIfActive();
    }
}

// using() runs the callback, then disposes $work, whether run() returns or throws
$result = using(
    new UnitOfWork($connection),
    fn(UnitOfWork $work) => $work->run(),
);
```

If both the callback and `dispose()` throw, the **callback** exception takes precedence and propagates, while the disposal exception is discarded.

The disposal pattern
--------------------

[](#the-disposal-pattern)

Disposal exists to release resources deterministically without having to rely on the timing of the garbage collector to call `__destruct()`. This can be particularly useful in background daemons, event loops, or other long-running processes where resource contention is a risk.

Three rules make an implementation robust.

**1. Make `dispose()` idempotent.**Disposing an already disposed object must do nothing; guard it with a flag (as above) so the release happens exactly once.

**2. Separate the object's own release from cascading.**If an object owns other disposables, dispose them from the object's `dispose()`, as well as releasing whatever the object holds directly.

Implement `__destruct()` as a backstop for objects dropped without a `dispose()`, but limit it to the object's own release and never cascade from it. During cycle collection and shutdown PHP runs destructors in an undefined order, so a disposable you own may already have been destroyed by the time your `__destruct()` runs.

Anything that must be released at a definite point belongs in `dispose()`, not `__destruct()`. When an object owns several disposables, cascade them in reverse construction order: release dependents before the things they depend on.

**3. Guard against use after disposal.**Throw a `DisposedException` (or similar) to prevent accidental use of a disposed object.

Example
-------

[](#example)

```
use Suhock\Disposable\DisposableInterface;
use Suhock\Disposable\DisposedException;

// One iteration of a queue worker's loop. Both resources it holds are contended.
// Leaving them to the garbage collector would stall the other workers waiting on
// the pool and the lock.
final class Job implements DisposableInterface
{
    private bool $disposed = false;

    private readonly Connection $connection;
    private readonly Lock $lock;

    public function __construct(
        private readonly ConnectionPool $pool,
        LockManager $locks,
    ) {
        $this->connection = $pool->lease(); // must return to pool when done
        $this->lock = $locks->acquire('reindex'); // must dispose when done
    }

    public function run(): void
    {
        DisposedException::throwIf($this->disposed, $this);

        // ... perform some work ...
    }

    public function dispose(): void
    {
        $this->disposeSelf();   // return our connection to the pool
        $this->lock->dispose(); // then cascade to the lock we own
    }

    public function __destruct()
    {
        $this->disposeSelf(); // backstop: return the connection, no cascade
    }

    private function disposeSelf(): void
    {
        if ($this->disposed) {
            return;
        }

        $this->disposed = true;
        $this->pool->release($this->connection); // returned to the pool
    }
}
```

###  Health Score

41

—

FairBetter than 87% of packages

Maintenance90

Actively maintained with recent releases

Popularity11

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity48

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Unknown

Total

1

Last Release

46d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/ee4d184f9162e82a9b49cb781178c7173510b0189d011c79bbb6f12dd0b0d2ff?d=identicon)[suhock](/maintainers/suhock)

---

Top Contributors

[![suhock](https://avatars.githubusercontent.com/u/5964592?v=4)](https://github.com/suhock "suhock (1 commits)")

---

Tags

cleanupdisposableresourceusingresourcecleanupdisposableusing

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan

Code StylePHP CS Fixer

Type Coverage Yes

### Embed Badge

![Health badge](/badges/suhock-disposable/health.svg)

```
[![Health](https://phpackages.com/badges/suhock-disposable/health.svg)](https://phpackages.com/packages/suhock-disposable)
```

###  Alternatives

[league/oauth2-server

A lightweight and powerful OAuth 2.0 authorization and resource server library with support for all the core specification grants. This library will allow you to secure your API with OAuth and allow your applications users to approve apps that want to access their data from your API.

6.7k156.0M342](/packages/league-oauth2-server)[fgribreau/mailchecker

Temporary (disposable/throwaway) email detection library. Covers 1987 fake email providers.

1.9k732.4k4](/packages/fgribreau-mailchecker)[propaganistas/laravel-disposable-email

Disposable email validator

6093.4M10](/packages/propaganistas-laravel-disposable-email)[sylius/resource-bundle

Resource component for Sylius.

24011.3M240](/packages/sylius-resource-bundle)[kigkonsult/icalcreator

iCalcreator is the PHP implementation of rfc2445/rfc5545 and rfc updates, management of calendar information

2493.0M23](/packages/kigkonsult-icalcreator)[sylius/grid-bundle

Amazing grids with support of filters and custom fields integrated into Symfony.

1369.2M66](/packages/sylius-grid-bundle)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
