Install the Package:
composer require hyperf/macroable
No additional configuration is required unless using container macros (see Implementation Patterns).
First Use Case: Extending a Collection
Hyperf’s Collection class (or a custom collection) can be extended with domain-specific macros. Start by creating a macro-enabled collection class:
use Hyperf\Collection\Collection;
use Hyperf\Macroable\Macroable;
class DomainCollection extends Collection
{
use Macroable;
}
Define a Macro: Add a reusable method to your collection:
DomainCollection::macro('groupByDate', function () {
return $this->groupBy(fn ($item) => $item['created_at']->format('Y-m-d'));
});
Use the Macro:
$collection = new DomainCollection([/* items */]);
$grouped = $collection->groupByDate();
Where to Look Next:
vendor/hyperf/collection for built-in methods to extend.src/Macroable.php for advanced usage (e.g., static macros, container macros).Extending Built-in Classes:
calculateRevenue() for e-commerce).
use Hyperf\Collection\Collection;
Collection::macro('calculateRevenue', function () {
return $this->sum('price');
});
slugify()).
use Hyperf\Support\Str;
Str::macro('slugify', function ($value) {
return Str::of($value)->slug();
});
Domain-Specific Macros:
use Hyperf\Database\Model\Builder;
Builder::macro('activeOnly', function (Builder $query) {
return $query->where('is_active', true);
});
use Hyperf\HttpMessage\Stream\SwooleStream;
SwooleStream::macro('sanitize', function (SwooleStream $stream) {
return Str::of($stream)->replace(['<script>', '</script>'], '');
});
Container Macros: Register macros globally via Hyperf’s container (for framework-wide reuse):
use Hyperf\Di\Container;
use Hyperf\Macroable\Macroable;
Container::get(Macroable::class)->extend('collection', function () {
return new class extends Collection {
use Macroable;
};
});
Service Providers: Centralize macro registrations in a Hyperf service provider:
use Hyperf\Framework\Contract\ConfigInterface;
use Hyperf\Di\Annotation\Inject;
class MacroServiceProvider extends ServiceProvider
{
public function register()
{
Collection::macro('customMethod', function () {
return 'Registered via provider';
});
}
}
Testing Macros: Use Hyperf’s testing tools to mock macros:
$this->mock(Collection::class)
->shouldReceive('customMethod')
->andReturn('Mocked result');
Macros in Middleware: Extend middleware with fluent macros for request/response transformations:
use Hyperf\HttpServer\Contract\RequestInterface;
RequestInterface::macro('getJsonApiPayload', function () {
return $this->json()->get('data', []);
});
Performance Considerations:
Hyperf\Cache).Container Macros and Singleton Scope:
Container::get(Macroable::class) may behave unexpectedly in multi-process Hyperf applications (e.g., workers). Ensure macros are stateless or use shared storage (e.g., Redis).Macro Overrides:
Collection::macro('pluck', function () { ... }); // Overrides Collection::pluck()
if (!method_exists(Collection::class, 'customMethod')) {
Collection::macro('customMethod', function () { ... });
}
Static Macros and Autoloading:
Str::macro()) may trigger autoloading delays if used in performance-critical paths. Pre-register macros in a service provider:
Str::macro('custom', function () { ... });
Hyperf’s DI Container Quirks:
Container do not persist across process restarts (e.g., in Hyperf’s worker model). Re-register macros in onWorkerStart:
$container->get(Macroable::class)->extend('collection', function () { ... });
Macros in Coroutines:
go() coroutines share the same context. Avoid mutable state:
// ❌ Risky: Shared state across coroutines
Collection::macro('incrementCounter', function () {
static $counter = 0;
return ++$counter;
});
Macro Not Found:
GroupByDate vs. groupByDate).Method Not Callable:
Macroable trait:
class MyClass {
use Macroable; // Required!
}
Container Macro Issues:
Container::get(Macroable::class)->hasMacro('method') to check registration:
if (!$container->get(Macroable::class)->hasMacro('customMethod')) {
throw new \RuntimeException('Macro not registered!');
}
Performance Bottlenecks:
hyperf\di\profiler to identify slow lookups.Namespacing Macros: Use namespaced macros to avoid collisions:
Collection::macro('ecommerce:calculateRevenue', function () { ... });
Conditional Macros: Dynamically register macros based on config:
if (config('app.feature_flags.enable_custom_macros')) {
Collection::macro('customMethod', function () { ... });
}
Macro Events (Laravel-Style):
Simulate Laravel’s Macroable::macro() event using Hyperf’s events:
event(new MacroRegistered('collection', 'customMethod'));
Extending Hyperf’s Core:
Override core classes (e.g., Hyperf\Collection\Collection) in a decorator or extension:
use Hyperf\Collection\Collection as BaseCollection;
class ExtendedCollection extends BaseCollection
{
use Macroable;
public static function boot()
{
static::macro('newMethod', function () { ... });
}
}
Macros in Tests: Reset macros between tests to avoid pollution:
use Hyperf\Macroable\Macroable;
beforeEach(function () {
Macroable::forgetAll();
});
Hyperf-Specific Extensions:
onWorkerStart for worker processes.Hyperf\AsyncQueue\Job to queue macro-heavy operations.Documentation: Add PHPDoc blocks to macros for IDE autocompletion:
/**
* Calculate revenue for a collection of orders.
*
* @return float
*/
Collection::macro('calculateRevenue', function () { ... });
Fallback for Laravel Macros: If migrating from Laravel, create a compatibility layer:
if (!class_exists('Str')) {
class_alias(\Hyperf\Support\Str::class, 'Str');
}
How can I help you explore Laravel packages today?