function HookOrder::changePriority

Change the priority of a hook implementation.

Parameters

\Drupal\Core\DependencyInjection\ContainerBuilder $container: The container builder.

string $hook: The name of the hook.

string $class_and_method: Class and method separated by :: containing the hook implementation which should be changed.

bool $should_be_larger: TRUE for before/first, FALSE for after/last. Larger priority listeners fire first.

array|null $others: Other hook implementations to compare to, if any. The array is keyed by string containing a class and method separated by ::, the value is not used.

Return value

void

4 calls to HookOrder::changePriority()
HookOrder::after in core/lib/Drupal/Core/Hook/HookOrder.php
Set a hook implementation to fire after others.
HookOrder::before in core/lib/Drupal/Core/Hook/HookOrder.php
Set a hook implementation to fire before others.
HookOrder::first in core/lib/Drupal/Core/Hook/HookOrder.php
Set a hook implementation to be first.
HookOrder::last in core/lib/Drupal/Core/Hook/HookOrder.php
Set a hook implementation to be last.

File

core/lib/Drupal/Core/Hook/HookOrder.php, line 114

Class

HookOrder
Helper methods to set priorities of hook implementations.

Namespace

Drupal\Core\Hook

Code

protected static function changePriority(ContainerBuilder $container, string $hook, string $class_and_method, bool $should_be_larger, ?array $others = NULL) : void {
    $event = "drupal_hook.{$hook}";
    foreach ($container->findTaggedServiceIds('kernel.event_listener') as $id => $attributes) {
        foreach ($attributes as $key => $tag) {
            if ($tag['event'] === $event) {
                $index = "{$id}.{$key}";
                $priority = $tag['priority'];
                // Symfony documents event listener priorities to be integers,
                // HookCollectorPass sets them to be integers, ::setPriority() only
                // accepts integers.
                assert(is_int($priority));
                $priorities[$index] = $priority;
                $specifier = "{$id}::" . $tag['method'];
                if ($class_and_method === $specifier) {
                    $index_this = $index;
                }
                elseif (!isset($others) || isset($others[$specifier])) {
                    $priorities_other[] = $priority;
                }
            }
        }
    }
    if (!isset($index_this) || !isset($priorities) || !isset($priorities_other)) {
        return;
    }
    // The priority of the hook being changed.
    $priority_this = $priorities[$index_this];
    // The priority of the hook being compared to.
    $priority_other = $should_be_larger ? max($priorities_other) : min($priorities_other);
    // If the order is correct there is nothing to do. If the two priorities
    // are the same then the order is undefined and so it can't be correct.
    // If they are not the same and $priority_this is already larger exactly
    // when $should_be_larger says then it's the correct order.
    if ($priority_this !== $priority_other && $should_be_larger === $priority_this > $priority_other) {
        return;
    }
    $priority_new = $priority_other + ($should_be_larger ? 1 : -1);
    // For ::first() / ::last() this new priority is already larger/smaller
    // than all existing priorities but for ::before() / ::after() it might
    // belong to an already existing hook. In this case set the new priority
    // temporarily to be halfway between $priority_other and $priority_new
    // then give all hook implementations new, integer priorities keeping this
    // new order. This ensures the hook implementation being changed is in the
    // right order relative to both $priority_other and the hook whose
    // priority was $priority_new.
    if (in_array($priority_new, $priorities)) {
        $priorities[$index_this] = $priority_other + ($should_be_larger ? 0.5 : -0.5);
        asort($priorities);
        $changed_indexes = array_keys($priorities);
        $priorities = array_combine($changed_indexes, range(1, count($changed_indexes)));
    }
    else {
        $priorities[$index_this] = $priority_new;
        $changed_indexes = [
            $index_this,
        ];
    }
    foreach ($changed_indexes as $index) {
        [
            $id,
            $key,
        ] = explode('.', $index);
        self::setPriority($container, $id, (int) $key, $priorities[$index]);
    }
}

Buggy or inaccurate documentation? Please file an issue. Need support? Need help programming? Connect with the Drupal community.