Skip to content

Upgrade from 1.x

Hooks 2.x replaces version 1’s registration and output model with explicit actions, filters, collectors, processors, and renderers. This guide shows how to move the common patterns; it is not a copy of the version-1 manual.

The mappings below follow the core and Laravel package upgrade guides. The 2.x packages are beta releases, so test your migration against the versions your application supports.

Install the beta packages explicitly in your application:

Terminal window
composer require magdicom/hooks:"^2.0@beta"

For Laravel, install both packages:

Terminal window
composer require magdicom/laravel-hooks:"^2.0@beta" magdicom/hooks:"^2.0@beta"

Do not change global Composer minimum-stability settings. See installation for PHP and Laravel requirements.

Version 1’s registration method covered several jobs. In 2.x, choose the method that matches what the caller needs:

Version 1 intent Version 2 registration and invocation Result
register() for side effects addAction() + doAction() Synchronous void execution; callback returns are ignored
register() for transformations addFilter() + applyFilters() Sequentially transformed value
register()/all() for result gathering addCollector() + collect() One raw result per listener

There is no single replacement for every old register() call. Look at what each registration does, then choose actions, filters, or collectors.

<?php
declare(strict_types=1);
use Magdicom\Hooks;
$hooks = new Hooks();
$hooks->addAction('invoice.paid', static function (int $invoiceId): void {
// Send a synchronous notification or write an audit entry.
});
$hooks->doAction('invoice.paid', 42);

doAction() returns void. If callers used the old callback’s return value, migrate that code to a filter or collector instead.

<?php
declare(strict_types=1);
use Magdicom\Hooks;
$hooks = new Hooks();
$hooks->addFilter('profile.label', static function (string $label): string {
return strtoupper($label);
});
$label = $hooks->applyFilters('profile.label', 'administrator');

The current value is the first callback argument, and each callback’s return value becomes the next value. With no listeners, applyFilters() returns the original value.

Result gathering: register()/all() to collectors

Section titled “Result gathering: register()/all() to collectors”
<?php
declare(strict_types=1);
use Magdicom\Hooks;
$hooks = new Hooks();
$hooks->addCollector('dashboard.widgets', static fn (): array => ['owner']);
$hooks->addCollector('dashboard.widgets', static fn (): array => ['activity']);
$cards = $hooks->collect('dashboard.widgets');
// [['owner'], ['activity']]

collect() returns one raw entry per listener. It does not recreate implicit version-1 aggregation, flattening, or output state.

For result lists, the migration is:

// Version 2
$results = $hooks->collect('dashboard.widgets');

This replaces both old all() result gathering and all()->toArray(). If you need a merged or flattened shape, configure a collector processor such as MergeProcessor or FlattenProcessor, or transform the raw array yourself. collect() never merges entries automatically.

When the old code selected one collector result, configure the intended reduction explicitly:

<?php
declare(strict_types=1);
use Magdicom\Hooks;
use Magdicom\Processors\FirstProcessor;
use Magdicom\Processors\LastProcessor;
$hooks = new Hooks();
$hooks->addCollector('checkout.banner', static fn (): string => 'Primary');
$hooks->addCollector('checkout.banner', static fn (): string => 'Fallback');
$hooks->setProcessor('checkout.banner', new FirstProcessor());
$first = $hooks->process('checkout.banner');
$hooks->setProcessor('checkout.banner', new LastProcessor());
$last = $hooks->process('checkout.banner');

FirstProcessor and LastProcessor return null for an empty result list. Selection is a collector decision; actions and filters do not have first() or last() methods.

Parameters: global arrays to explicit arguments

Section titled “Parameters: global arrays to explicit arguments”

If version 1 stored parameters globally, pass the values at invocation time:

<?php
declare(strict_types=1);
use Magdicom\Hooks;
$hooks = new Hooks();
$hooks->addFilter('invoice.total', static function (int $price, string $currency): int {
return $currency === 'USD' ? $price : $price + 1;
});
$price = $hooks->applyFilters('invoice.total', 100, 'USD');

For a larger shared input, pass one typed context object as an explicit argument:

<?php
declare(strict_types=1);
use Magdicom\Hooks;
final readonly class RenderContext
{
public function __construct(
public string $locale,
public string $userId,
) {
}
}
$hooks = new Hooks();
$context = new RenderContext('en', 'user-42');
$hooks->doAction('profile.rendered', $context);

The Hooks core does not maintain a hidden global parameter array. Processors and renderers can use ProcessingContext to read the hook point and original invocation arguments.

When the old code assembled collector output as a string, use a renderer with an explicit separator:

<?php
declare(strict_types=1);
use Magdicom\Hooks;
use Magdicom\Processors\ConcatenateRenderer;
$hooks = new Hooks();
$hooks->addCollector('navigation.labels', static fn (): string => 'Hooks');
$hooks->addCollector('navigation.labels', static fn (): string => 'Beta');
$hooks->setRenderer('navigation.labels', new ConcatenateRenderer(' · '));
$output = $hooks->render('navigation.labels');
// 'Hooks · Beta'

ConcatenateRenderer accepts supported scalar, string, null, and Stringable values. It returns an empty string for an empty result list; unsupported values throw UnexpectedValueException.

These version-1 APIs are removed in 2.x:

register(), all(), first(), last(), toArray(), toString(), __toString(), setParameter(), setParam(), setParameters(), and setParams().

Rewrite chained version-1 calls as explicit steps:

  1. register a named action, filter, or collector;
  2. invoke it with explicit arguments;
  3. collect raw results when needed;
  4. process or render those results explicitly.

There is no shared legacy output or chaining state after dispatch. Decide explicitly where aggregation, selection, string conversion, and parameter ownership belong in the new code.

This guide does not recreate the complete version-1 manual. Use the historical Hooks releases and source when you need the old package documentation, then use this page to plan the move to 2.x Beta.