astrotomic/php-conditional-proxy
A lightweight conditional proxy for PHP that lets you chain method calls on any object and execute them only when your conditions pass. Use when/unless-style checks to keep optional logic clean, fluent, and free of scattered if statements.
if and breaking a method chain. As a PM, the decision is not "is this valuable?" (it obviously is when you have chains) but "is this worth a dependency vs. 30 lines in-house?" — a low-stakes, reversible call.->when($featureEnabled)->withBetaFormatting() to a report/notification/export builder. This makes staged rollouts cheap to express and cheap to delete later.if noise in those builders improves readability of the code that defines customer-facing behavior, which shortens review cycles and lowers the chance of a conditional being silently dropped during refactor.null/"do nothing" branch is the common case.Adopt when:
__call-based magic: your team runs PHPStan/Larastan and an IDE that tolerates magic methods, and you're willing to add @method docblocks for completion.if-guard or tap()/optional() workaround.Look elsewhere when:
Conditionable trait (when() / unless()) already covers you. If the class in question can use Conditionable, that is the zero-dependency default — it takes a closure rather than returning a proxy, which is fine for most cases. Reach for this package only when the closure-per-call ergonomics genuinely hurt readability.For executives (30 seconds): "There's a small, MIT-licensed utility that lets our code apply optional configuration to a builder-style API without if statements — it makes conditional, flag-driven behavior cheaper to write and, more importantly, cheaper to remove when a rollout ends. Cost is a single dependency with no runtime footprint and a permissive license. Risk is that it's a low-traffic, single-maintainer package, so we'd use it only in leaf utilities and keep a vendor-it-ourselves escape hatch. Recommendation: adopt narrowly, don't standardize on it."
For engineering leads:
Problem it removes: repeated if ($cond) { $builder->method(); } blocks that interleave with fluent configuration and force temporary variables or duplicated chains.
Integration shape: a trait on your builder/service class, plus a when($condition) entry point that returns a proxy; subsequent calls either pass through or no-op. Roughly:
$report->when($user->isAdmin())->withInternalMetrics()
->when($tenant->hasFeature('beta'))->withBetaLayout()
->generate();
Semantics to agree on before merging: what the chain returns when the condition is false, and what happens to methods that return values. Document this in the PR — it is the only real footgun.
Dependency posture: MIT, tiny surface, no framework coupling. Vendor or reimplement if the maintainer goes quiet; the abstraction is small enough that a fork is a morning's work, not a migration.
Guardrails: allow it in builders, services, and test setup; disallow it in anything on the public SDK boundary or in code paths where the return value is consumed. Add @method annotations so static analysis stays green.
How can I help you explore Laravel packages today?