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():
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):
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
RuntimeResourceinterface, its unique ID is retrieved usingresourceId(). - 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:
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:
$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:
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:
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']);
}