Getting Started
The 0.1.1 release is documentation-only: it clarifies how application-level state isolation works and how the Scope-based fixes are intended to be applied. There are no API changes, deprecations, or new runtime features in this release, so an existing integration does not need code changes.
Start with the package documentation’s state-isolation section rather than the source. The key concept to internalise is that this package is about keeping long-lived Laravel application state from leaking between requests/jobs in workers such as Octane, queue workers, and other resident processes. The Scope-based fixes are the mechanism: stateful bindings should be resolved per lifecycle rather than treated as process-wide singletons.
Minimal first steps:
- Install the package with Composer and confirm the service provider is discovered (or register it manually if the package documents that requirement).
- Publish any provided config if the package exposes one, and read the comments around isolation boundaries.
- Identify one stateful service in your app — tenant context, request metadata, locale, an auth/actor context object, or a request-scoped repository — and bind it through the package’s scoped mechanism instead of
singleton().
- Verify the behaviour in the same runtime you deploy: if you use Octane, test under Octane, not only under
php artisan serve.
First use case: wrap a request/tenant context object so it is reset between requests. Resolve it in middleware or a controller, assert it is fresh on the next request, and confirm that no static or singleton-held reference survives.
Implementation Patterns
Treat the package as the lifecycle boundary for stateful services. The most reliable pattern is:
- Bind stateful collaborators as scoped instances, not singletons.
- Populate them at the start of a request/job (middleware, listener, or job handler).
- Let the package/container clear them at the end of the lifecycle.
- Resolve them lazily where possible, so short-lived paths do not pay for state setup they do not use.
Common day-to-day patterns:
- Request context object. A single context class holds tenant ID, request ID, actor, locale, and feature flags. Bind it as scoped, set it in middleware, and inject it into services. This avoids sprinkling
request() and Auth::user() calls through domain code.
- Tenant-aware repositories. If a repository caches tenant-specific query state, make the repository scoped. A singleton repository that memoises a tenant connection is the classic leak this package is designed to prevent.
- Queue jobs. Job workers are long-lived. If a job sets context, ensure it is flushed in
finally or via the package’s lifecycle hooks. Do not assume the next job starts from a clean container unless you have verified it.
- Testing. In feature tests, resolve the scoped service, mutate it, then simulate a second lifecycle and assert isolation. If the package exposes a flush/reset helper, call it in test teardown to avoid cross-test contamination.
- Integration with Octane. If you use Octane listeners or
Octane::tick(), keep state mutation inside the request lifecycle. Octane flushes scoped instances between requests, but it does not know about your own static caches.
- Extension points. If the package exposes a scope registry or lifecycle hook, prefer registering your stateful services there rather than manually calling a reset method in many places. Centralised registration is easier to audit.
For existing code, migrate incrementally: start with services that hold request data, then move on to caches and connection-bound objects. Do not convert everything at once; singletons that are genuinely stateless can stay singletons.
Gotchas and Tips
- Docs-only release does not mean nothing changed. The 0.1.1 clarification is a signal that application-level isolation was ambiguous. Re-read the Scope section and audit your bindings against it; a misunderstanding here produces intermittent, hard-to-reproduce bugs rather than immediate failures.
- Singletons still leak. The package cannot isolate what you bind as a singleton. Search for
singleton(, $this->app->singleton, and bind(..., true) in providers and package code.
- Static state is invisible to the container. Static properties, static caches, and global variables are not scoped. Reset them explicitly or refactor them into scoped services.
- Config caching. If the package reads config at registration time and you run
config:cache, verify behaviour after caching. Stale config is a common cause of “works locally, fails in production”.
- Queue workers need explicit attention.
queue:restart after deployment, and confirm whether the package hooks into job lifecycle events. A worker that processes many jobs amplifies any leak.
- Tests can hide leaks. A fresh application per test masks state leakage. Add at least one test that exercises two consecutive lifecycles in the same application instance.
- Debugging approach. When state appears to bleed across requests, instrument the scoped service’s constructor and destructor, log object IDs, and confirm a new instance is created per lifecycle. Check
app()->getBindings() to see whether a binding is scoped or shared.
- Extension points. Prefer the package’s documented scope registration over ad-hoc reset calls. If you must reset manually, put it in one place — middleware, a job base class, or a listener — rather than scattering it.
- Version pinning. This is an early
0.x package. Pin the version in composer.json and review release notes before upgrading; documentation clarifications in 0.x often precede behavioural fixes in later patches.