Skip to content

Resource Lifecycle ​

To prevent resource leaks in long-running processes (such as open file handles, database connections, or socket sockets), Nexphant uses a structured resource registry that binds the lifecycle of active resources to their respective execution owners.

The Resource Registry ​

The ResourceRegistry is a singleton that tracks all active resources. When an owner (e.g., request or fiber) closes, the registry automatically releases any resource tracked under that owner.

You can access the registry using Runtime::resources() or ResourceRegistry::instance():

php
use Nexphant\Runtime\Runtime;

$registry = Runtime::resources();

Tracking Resources ​

To track a resource, use the track() method. It requires the resource object, a string identifying the resource type, and the owner ID (usually the current owner):

php
use Nexphant\Runtime\Runtime;

$connection = new DatabaseConnection();

// Track the connection under the current owner
Runtime::resources()->track(
    $connection,
    'db_connection',
    Runtime::owner()
);

Resource Identification ​

  • If the resource implements the RuntimeResource interface, its unique ID is retrieved using resourceId().
  • Otherwise, the registry falls back to the object's spl_object_id().

The RuntimeResource Contract ​

For complex or custom resources, implement the RuntimeResource interface to support self-managed release logic:

php
namespace App\Resources;

use Nexphant\Core\Resource\RuntimeResource;

class SocketConnection implements RuntimeResource
{
    private bool $released = false;

    public function __construct(
        private string $id,
        private ?string $ownerId
    ) {}

    public function resourceId(): string
    {
        return $this->id;
    }

    public function resourceType(): string
    {
        return 'socket_connection';
    }

    public function ownerId(): ?string
    {
        return $this->ownerId;
    }

    public function release(): void
    {
        if (!$this->released) {
            // Close the socket connection
            fclose($this->socket);
            $this->released = true;
        }
    }

    public function isReleased(): bool
    {
        return $this->released;
    }
}

Releasing Resources ​

Resources can be released manually or automatically:

Automatic Scoped Release ​

When an owner is closed (e.g., when Runtime::withOwner() finishes, a request completes, or a background job returns), the OwnerRegistry calls:

php
$registry->releaseByOwner($ownerId, $reason);

This triggers the release() method on all tracked resources implementing RuntimeResource.

Manual Release ​

You can manually release a resource by its unique ID, which deletes it from the tracker:

php
use Nexphant\Runtime\Runtime;

$resourceId = (string)spl_object_id($connection);
Runtime::resources()->release($resourceId);

Leak Detection & Diagnostics ​

If a resource is not released after its owner finishes, it is counted as a leak. In debug environments, you can enable stack trace capture to trace exactly where leaked resources were created:

php
use Nexphant\Runtime\Runtime;

// Enable capturing backtrace of registered resources
Runtime::resources()->enableStackTrace(true);

// List all currently leaked (unreleased) resources
$leaks = Runtime::resources()->listLeaks();

foreach ($leaks as $leak) {
    echo "Leaked resource of type {$leak['type']} (Age: {$leak['age']}s)\n";
    // Show backtrace
    print_r($leak['stack_trace']);
}

Released under the MIT License.