Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Macroable Laravel Package

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.

View on GitHub
Deep Wiki
Context7

Technical Evaluation

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.
  • In practice it is already in the stack: any application on 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.
  • The genuine architectural value is avoiding inheritance and avoiding static helper sprawl: domain behavior can be attached to framework primitives (Collection, Str, Builder) at boot, keeping call sites fluent and readable ($users->activeThisQuarter() instead of UserHelper::activeThisQuarter($users)).
  • The genuine architectural cost is that macros are invisible extension points. They are not discoverable via reflection or IDE autocomplete, they are globally registered (not scoped to a module), and they can be silently overwritten. That is a governance problem more than a technology problem, and it is the one TPMs actually own.

Integration feasibility

  • Near-zero: 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.
  • Effort is not installation effort, it is convention effort: deciding where macros register, how they are named, and how they are tested.
  • Data caveat for the record: 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

  • Low on security and stability: MIT-licensed, maintained by the Laravel core team, battle-tested across the ecosystem. No additional CVE surface beyond the framework you already run.
  • Medium on maintainability: macro name collisions, silent overwrites (macro() re-registration replaces without warning), and undefined-method failures at runtime rather than compile time.
  • Medium on observability/tooling: macros don't appear in reflection or autocomplete, so static analysis (Larastan/PHPStan) and IDE support require explicit @method annotations or stubs. Debugging a macro failure means debug_backtrace(), not a stack trace that names the method.
  • Low-Medium on versioning: the split package's constraint is normally pinned in lockstep with the framework. Allowing independent version drift between illuminate/macroable and laravel/framework is a self-inflicted risk.
  • Low on performance: macro dispatch is a static array lookup; the only real cost is boot-time registration on every request (relevant under Octane, where it happens once per worker, and under cold-start/serverless, where it happens per invocation).

Key questions

  1. Are we consuming this transitively via 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?
  2. Do we already have undisciplined macros scattered across service providers? That inventory is the real scope of work.
  3. What is our naming convention to prevent collisions across teams and third-party packages (e.g., vendor:verbNoun prefixes)?
  4. Do we require static-analysis annotations (@method blocks or a generated stub) as a merge gate for new macros?
  5. Which classes are off-limits for macros (e.g., Model, Builder, auth primitives) to avoid behavior that surprises future maintainers?
  6. How do we detect and prevent silent macro overwrites in CI — a boot-time registry assertion, or hasMacro() guards?
  7. Does any of this need to behave identically in queue workers, Artisan, and HTTP contexts? (If registration lives in a provider, it usually does — but only if the provider is not HTTP-only.)

Integration Approach

Stack fit

  • Natural fit for Laravel 11+/PHP 8.2+ applications using Eloquent, Collections, or the String helpers. Also viable for standalone PHP libraries that want extensibility without an inheritance hierarchy — that is the one scenario where a direct composer require illuminate/macroable is justified.
  • Poor fit where the team's convention is explicit dependency injection and small, named value objects. Adding macros to an already-DI-heavy codebase creates two competing extension idioms.

Migration path

  • From inline/static helpers: identify repeated helper calls (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.
  • From 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.
  • Vendored/copied macro implementations: consolidate into the framework trait. A locally maintained copy of Macroable is dead weight with a real upgrade cost and no upside.

Compatibility

  • Constrain to the framework's own PHP floor (^8.2) and let laravel/framework resolve the split package. Do not pin illuminate/macroable independently in a Laravel app.
  • Third-party packages also register macros on shared classes (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

  1. Inventory (1–2 days): grep for ::macro( across app, packages, and tests. Produce a register: name, target class, owner, purpose.
  2. Consolidate (1–3 days): move registrations into dedicated, per-domain macro providers (CollectionMacrosServiceProvider, StringMacrosServiceProvider) rather than a 400-line AppServiceProvider::boot(). Namespace every macro by domain.
  3. Guard (0.5 day): add hasMacro() checks with loud failures in non-production, and a boot-time assertion test that fails CI on duplicate registration.
  4. Type (1 day): add @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.
  5. Govern (ongoing): publish a short internal standard: allowed target classes, naming convention, service-provider placement, test expectations, review checklist.
  6. Defer: the namespace migration and any standalone Composer adoption unless a non-Laravel consumer appears.

Operational Impact

Maintenance

  • Effectively zero marginal cost: no separate upgrade cadence, no migrations, no configuration drift. It rides the framework's release train, which the team already tracks.
  • The maintenance liability is entirely the macro catalog you build on top of it. A registry of 120 undocumented macros registered across a dozen providers is an operational debt that compounds quietly. Budget for periodic pruning, not for patching.

Support

  • Community and core-team maintained; no commercial SLA. Acceptable given the blast radius (a broken macro fails loudly and locally, not silently and broadly).
  • Internal support ownership should sit with the platform/architecture group, who own the macro standard and the provider layout — not with individual feature teams, who will otherwise fork conventions.

Scaling

  • Technically negligible: static array lookup per call, registration once per process. Under Octane the cost is per-worker boot; under serverless it is per-invocation, so keep the boot path lean and avoid conditional registration logic that does I/O.
  • Organizationally it does not scale by default. Cross-team macro registration on shared classes (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)

  1. Silent overwrite — two providers register Collection::macro('active', ...); last boot wins; behavior changes with no diff. Highest-likelihood, hardest-to-diagnose failure. Mitigate with duplicate-detection assertions in CI.
  2. Context divergence — macro registered in an HTTP-only provider is missing in queue workers or Artisan commands, producing BadMethodCallException in production paths that tests never covered. Mitigate by registering in a provider that boots in all contexts, and testing in queue context.
  3. Reflection blindness — a developer "cleans up" an allegedly unused method, or static analysis reports it as undefined and the fix is suppressed rather than annotated. Mitigate with @method stubs.
  4. Closure binding errors — $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).
  5. Third-party conflict — a package upgrade registers a macro name your app already uses, or vice versa. Rare, but the failure is a silent behavior change rather than an error.
  6. Debug opacity — stack traces point into __call/__callStatic rather than the macro, lengthening incident triage. Small per-incident cost, non-trivial aggregate.

Ramp-up

  • For an individual developer: ~half a day. The API is three methods (macro, hasMacro, mixin); the getting-started material is accurate and self-contained.
  • For a team: ~1–2 weeks to internalize the conventions (where to register, how to name, how to test, how to annotate) and to see the first PRs follow them without prompting. That is the real onboarding cost, and it is a documentation-and-review problem, not a training problem.
  • Onboarding material to produce: one page on macro registration placement, one worked example with @method annotations and a test, and the do-not-touch list of protected classes. Keep it under three pages, or nobody reads it.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
muka/shape-reader
tdt/json
refinery29/php-cs-fixer-config
hotmeteor/eco-env
mvaliolahi/hydrate
wearede/tbcpay-php
parsnick/steak
thibaud-dauce/mattermost-php
multilinguals/apollo-client
antaresbr/collection
kr0lik/phpstan-rules
loco/loco
starfederation/datastar-php
yupmin/modern-phystrix
stephenjude/extended-artisan-commands
lumphp/laravel-macroable
lumphp/laravel-collections
lumphp/laravel-contracts
sermepa/sermepa
arkecosystem/fortify