illuminate/macroable
The Illuminate Macroable trait lets you add custom methods to a class at runtime without modifying its source. Register macros once and call them like native methods, making Laravel components and your own classes easily extensible.
Architecture fit
illuminate/macroable is a single-trait subtree split of laravel/framework. It has no service provider, no config, no facade, and no runtime dependencies. It is a horizontal capability, not a bounded context — it does not change how the application is structured, it changes how classes can be extended at runtime.laravel/framework gets it transitively, and it is the mechanism behind Collection, Str, Stringable, Request, Response, Rule, and factory macros. There is no "adopt or not" decision here — only a "govern or not" decision.Collection, Str, Builder) at boot, keeping call sites fluent and readable ($users->activeThisQuarter() instead of UserHelper::activeThisQuarter($users)).Integration feasibility
composer require illuminate/macroable is only necessary for non-Laravel PHP projects. Inside a Laravel app it is already present. There is no migration, no data backfill, no cutover window.dependents: 0 is a metric artifact of subtree-split packages (consumers depend on laravel/framework, not on the split), and the reported release date tracks the framework's release train rather than a meaningful package-level cadence. Neither should be read as abandonment signals.Technical risk
macro() re-registration replaces without warning), and undefined-method failures at runtime rather than compile time.@method annotations or stubs. Debugging a macro failure means debug_backtrace(), not a stack trace that names the method.illuminate/macroable and laravel/framework is a self-inflicted risk.Key questions
laravel/framework (recommended), or does a service need it as a direct dependency for a non-Laravel context? If the latter, who owns the version pin?vendor:verbNoun prefixes)?@method blocks or a generated stub) as a merge gate for new macros?Model, Builder, auth primitives) to avoid behavior that surprises future maintainers?hasMacro() guards?Stack fit
composer require illuminate/macroable is justified.Migration path
Str::of(...)->pipe(...), Collection chains, custom array_* wrappers) and replace with a named macro. This is a readability and discoverability win, not a functional one — sequence it opportunistically, not as a project.Illuminate\Support\Traits\Macroable to the split namespace: the legacy trait remains available as a compatibility alias, so existing use Macroable; statements continue to work. Treat any namespace change as cosmetic and defer it; there is no product benefit to churning use statements.Macroable is dead weight with a real upgrade cost and no upside.Compatibility
^8.2) and let laravel/framework resolve the split package. Do not pin illuminate/macroable independently in a Laravel app.Collection, Str). Any naming convention must assume a namespace shared with code you don't control — treat collisions as a when, not an if.Sequencing
::macro( across app, packages, and tests. Produce a register: name, target class, owner, purpose.CollectionMacrosServiceProvider, StringMacrosServiceProvider) rather than a 400-line AppServiceProvider::boot(). Namespace every macro by domain.hasMacro() checks with loud failures in non-production, and a boot-time assertion test that fails CI on duplicate registration.@method annotations or stub generation so Larastan/IDE surfaces macros. This converts a runtime-only capability into a statically checkable one — the highest-leverage step in the whole plan.Maintenance
Support
Scaling
Collection, Str, Model) is a shared mutable namespace with no ownership boundary. Above roughly 5 contributing teams or 50+ macros, collisions and "why does this method exist?" become the dominant cost. Mitigate with domain prefixes and a reviewed registry.Failure modes (ranked by likelihood)
Collection::macro('active', ...); last boot wins; behavior changes with no diff. Highest-likelihood, hardest-to-diagnose failure. Mitigate with duplicate-detection assertions in CI.BadMethodCallException in production paths that tests never covered. Mitigate by registering in a provider that boots in all contexts, and testing in queue context.@method stubs.$this inside the macro is the called instance, not the registering class; a macro written assuming one type misbehaves when invoked on another (e.g., on Eloquent\Collection vs Support\Collection).__call/__callStatic rather than the macro, lengthening incident triage. Small per-incident cost, non-trivial aggregate.Ramp-up
macro, hasMacro, mixin); the getting-started material is accurate and self-contained.@method annotations and a test, and the do-not-touch list of protected classes. Keep it under three pages, or nobody reads it.How can I help you explore Laravel packages today?