ocramius/proxy-manager
ProxyManager generates and manages PHP proxy classes (virtual proxies, lazy-loading value holders, etc.) to implement the Proxy Pattern. Useful for lazy-loading, interceptors, and advanced DI/ORM scenarios. Install via Composer and use factory helpers to create proxies.
Install via Composer:
composer require ocramius/proxy-manager
Start with the LazyLoadingValueHolderFactory for lazy-initializing heavy or expensive objects — ideal for service dependencies loaded from containers or databases. The minimal example:
use ProxyManager\Factory\LazyLoadingValueHolderFactory;
$factory = new LazyLoadingValueHolderFactory();
$proxy = $factory->createProxy(
HeavyService::class,
static function (&$wrappedObject, $proxy, $method, $params, &$initializer) {
$wrappedObject = new HeavyService(); // your instantiation logic
$initializer = null; // one-time init
return true;
}
);
// First call triggers lazy init; subsequent calls use the real object
$result = $proxy->doWork();
First stop: docs/lazy-loading-value-holder.md for core usage, initializer signatures, and lifecycle behavior.
Dependency Injection Containers (e.g., Symfony, Laravel): Use LazyLoadingValueHolder to defer instantiation of large services (e.g., ORM managers, SDK clients, PDF generators) until actually needed — reduces request warm-up time and memory footprint.
Repository Layer with DB Connections: Wrap repository dependencies (like Doctrine EntityManager) in lazy proxies to avoid connecting until query execution. Example for Laravel:
$factory = new LazyLoadingValueHolderFactory();
$proxy = $factory->createProxy(
UserRepository::class,
static function (&$wrapped) {
$wrapped = new UserRepository(app('db'));
$initializer = null;
return true;
}
);
Access Interceptor Proxies for Cross-Cutting Concerns: Use AccessInterceptorValueHolderFactory for AOP-style logic: logging, rate limiting, caching, and observability hooks without modifying business logic.
$factory = new AccessInterceptorValueHolderFactory();
$proxy = $factory->createProxy(
new ApiClient(),
// prefix (pre-method)
['fetchData' => fn() => logger()->info('API call starting')],
// suffix (post-method)
['fetchData' => fn() => logger()->info('API call finished')]
);
Testing & Stubbing: Generate mocks lazily or with intercepted behavior to avoid initializing complex dependencies during unit tests. LazyLoadingValueHolder proxies behave identically to the wrapped object — ideal for test doubles with deterministic setup.
Caching Layer Integration: Combine with PSR-6/16 caches: delay expensive object hydration until first method access, then cache the hydrated instance via the initializer.
Initializers Must Be Disabled After Use: Always set $initializer = null (by reference) after first setup — otherwise, the initializer will re-run on every property/method access. Silent performance regressions are common if omitted.
Interface Proxies Are Shallow: Proxying an interface (SomeInterface::class) restricts access only to interface methods, even if the wrapped class has more. Use this to reduce proxy size and prevent accidental coupling to implementation details.
Avoid func_get_args(): Methods relying on func_get_args(), func_get_arg(), or func_num_args() won’t receive parameters beyond the declared signature — migrate to variadic ...$args instead.
No Deep Property Copying: The proxy wraps the object — modifying public properties on the proxy does affect the wrapped object. Not suitable for immutability enforcement.
Production Optimization: Use the Config::setGhostObjectLoader() and setProxiesManager() APIs for autoloading generated proxies (avoid runtime reflection overhead). In Laravel, consider caching proxy classes via a custom ServiceProvider or build step.
Debugging: Inspect proxies via instanceof LazyLoadingInterface or method_exists($proxy, 'setProxyInitializer') to conditionally log or skip proxy logic in development.
Deprecation Warning: The package is unmaintained (last release 2022-03-05). For new projects, consider ocramius/package-versions successor efforts or modern alternatives like Symfony’s ProxyManagerBridge, Doctrine’s Common proxies (which use this package internally), or PHP 8.1+ __callStatic-based lazy patterns. Still safe for existing codebases.
Memory Stability: Lazy proxies do not reduce memory until initialized — if the initializer captures large closures or resources, memory may spike at first use. Profile with memory_get_usage() to validate gains.
How can I help you explore Laravel packages today?