yiisoft/proxy
yiisoft/proxy provides lightweight proxy objects for PHP that defer instantiation until first use, enabling lazy service loading and cleaner dependency injection without changing your application code. Part of the Yii 3 ecosystem.
What we are evaluating. yiisoft/proxy is the Yii3 component for building runtime proxy objects — it generates a subclass of a target class whose methods delegate to a wrapped instance. It is a plumbing library, not an application feature: it exists so a service can be type-compatible with a concrete class or interface while its real construction is deferred or intercepted. It is framework-agnostic in design (PSR-friendly, no Yii runtime coupling implied) but it is maintained by the Yii core team for Yii's DI/definitions use cases.
Architecture fit. Moderate-to-weak for a Laravel codebase as a primary architectural tool, strong as a surgical utility.
ContextualAttribute, or a thin decorator. Case (b) is where a runtime proxy genuinely earns its keep.final blocks conventional mocking.Integration feasibility. Straightforward technically. composer require yiisoft/proxy, bind the proxy manager as a singleton in a service provider, point generated-code output at a writable path (storage/framework/proxies or a build-time directory). No Laravel version constraints are implied by a general-purpose proxy generator, so installation risk is low. The real integration work is policy work: deciding where proxies are permitted, because unconstrained use makes the object graph opaque.
Technical risk (ranked).
get_class(), appear oddly in stack traces, confuse dd()/var_dump, IDE navigation, and static analysis. Doctrine and Laravel internals occasionally make decisions on concrete class identity.static returns, by-reference parameters, and magic methods. Gaps here show up as subtle bugs, not loud failures.Key questions to close in the spike.
final methods, static returns, by-reference params, __call, __get, and readonly properties?composer dump-autoload --classmap-authoritative and OPcache preloading?Stack fit. Compatible in principle with any Laravel 10/11/12 application on a supported PHP 8.x. It brings no HTTP, container, or config opinions, so it will not collide with Laravel's kernel. Two friction points to design around: generated classes live outside Composer's autoload map (load them via an explicit require or a registered autoloader scoped to the proxy directory), and the proxy manager is stateful enough that it should be a singleton, not resolved per call.
Migration path. Treat this as an additive capability, not a migration. Nothing existing changes.
instanceof and interface type hints.ProxyServiceProvider plus an internal LazyProxy helper that centralizes the manager instance, the output directory, and a config flag. Application code must never touch yiisoft/proxy types directly; it should call our helper. This is the single most important decision — it makes the dependency replaceable.final classes), plus a code-review rule that proxies may not wrap domain entities, Eloquent models, or anything that is serialized.Compatibility checks before committing.
composer why-not against our pinned psr/* and PHP constraint to confirm no transitive conflict.composer install --classmap-authoritative in production does not shadow the proxy autoloader.Sequencing. Spike → wrapper + ADR → contract tests → one production call site → observe for a release cycle → only then consider a second use case. Do not adopt this library speculatively; it has no value until a specific class needs it.
Alternatives to weigh at the decision point. Laravel container closures / bind with a deferred resolver (zero dependency), a hand-written decorator implementing the same interface (zero dependency, more code), or friendsofphp/proxy-manager-lts (far larger installed base, more maturity, heavier footprint). If the spike shows the Yii package's API surface is thin enough to reimplement in ~100 lines, that may be the cheaper long-term answer than taking on a 22-star dependency for a plumbing concern.
Maintenance. Low ongoing code cost, non-trivial watcher cost. The library itself will not require frequent changes; what requires attention is PHP releases. Assign ownership of "PHP upgrade readiness" to the platform team and add the proxy generation step to the upgrade checklist. Pin the version explicitly (no floating constraint) and review the changelog on every bump. The BSD-3-Clause attribution must be carried into any distributed artifact.
Support. No commercial support, no Laravel community footprint, no Stack Overflow corpus. Escalation path is GitHub issues with Yii maintainers — a responsive but small team, and our use case (Laravel, not Yii DI) is not their design center. Expect to debug generation failures ourselves by reading generated output; that is a reasonable competency for a senior PHP engineer but should be named as an accepted cost, not discovered later.
Scaling characteristics.
Failure modes to plan for (with detection).
get_class, strict === comparisons, container re-resolution returning a proxy where a concrete instance was expected) → integration-level bugs. Mitigate with contract tests and a grep-able naming convention for proxied bindings.Ramp-up. A competent Laravel engineer is productive with the API in half a day; the real learning curve is debugging generated code and knowing when a proxy is the wrong tool — call it 2–3 days to fluency. Document the ADR, the helper, and one worked example in the team handbook. Onboarding cost is small but the concept is unusual enough that it will generate review questions on every PR; the policy document is what keeps that from becoming friction.
Bottom line. Technically sound, cheap to integrate, and genuinely useful for a narrow class of problems — but it is a low-adoption, single-vendor, code-generation dependency, and code generation is the single most PHP-version-fragile technique available. Approve it only behind an internal wrapper, only for named use cases, only with build-time generation and a PHP-version CI matrix. If the spike cannot produce a case that Laravel's container and a plain decorator both fail, decline and revisit when one appears.
How can I help you explore Laravel packages today?