Pending

User tests: Successful: Unsuccessful:

avatar MacJoom
MacJoom
6 Sep 2026
  • I read the Generative AI policy and my contribution is either not created with the help of AI or is compatible with the policy and GNU/GPL 2 or later.

Summary of Changes

Ten call sites in core still handed triggerEvent() a positional array. None of those event names are
in CoreEventAware::$eventNameToConcreteClass, so they all fell back to the generic Joomla\Event\Event:
no named arguments, no validation and no documented shape for plugin developers. Three of them also
passed an argument by reference, which stops working in 7.0.

This adds a concrete event class for each of them, registers it in CoreEventAware and dispatches it
explicitly:

Event New class
onFinderIndexAfterIndex Joomla\CMS\Event\Finder\IndexAfterIndexEvent
onFinderIndexAfterDelete Joomla\CMS\Event\Finder\IndexAfterDeleteEvent
onFinderIndexAfterPurge Joomla\CMS\Event\Finder\IndexAfterPurgeEvent
onFinderSortOrderFields Joomla\CMS\Event\Finder\SortOrderFieldsEvent
onMailBeforeTagsRendering Joomla\CMS\Event\Mail\BeforeTagsRenderingEvent
onGetStats Joomla\CMS\Event\Module\GetStatsEvent
onBuildAdministratorLoginURL Joomla\CMS\Event\Application\BuildAdministratorLoginUrlEvent

Arguments are declared in the order the old positional arrays used, so plugins registered through
CMSPlugin::registerLegacyListener() still receive them in the same order.

The three by-reference sites now read the value back off the event instead. Listeners return a changed
value with updateSortOrderFields(), updateMail() or updateUri(), the same shape
Menu\AfterGetMenuTypeOptionsEvent::updateItems() already uses.

There are no positional triggerEvent() calls left in core after this.

Dispatching on the right dispatcher

PluginHelper::importPlugin() registers listeners on the application dispatcher by default, but callers
may pass their own and it tracks plugins per dispatcher via spl_object_hash(). IndexController::optimise()
and IndexerController both pass the controller's. Dispatching on any other object then silently finds no
listeners, so every importPlugin() / dispatch() pair in the changed code now uses the same object.

That exposed a related problem in PluginHelper::import(): the dispatcher was only injected into plugins
on the legacy branch, never into SubscriberInterface plugins, which go through addSubscriber(). Any
plugin that follows the CMSPlugin::getDispatcher() deprecation advice and declares
DispatcherAwareInterface itself was therefore left with an unset dispatcher. It is now injected for those
plugins.

The injection is deliberately gated on the plugin declaring the setter itself. CMSPlugin implements
DispatcherAwareInterface, so an ungated call would route all 157 core subscriber plugins through the
deprecated CMSPlugin::setDispatcher() and emit a deprecation for each of them on every request.

phpstan.neon

PHPStan inherits a parent's @deprecated onto an overriding method and offers no way to undo it, so it
reports Adapter::getDispatcher() as deprecated even though that method is declared on Adapter without
the tag. A file-scoped ignoreErrors rule covers the four affected files, with a @TODO to remove it once
CMSPlugin stops implementing DispatcherAwareInterface in 7.0.

B/C breaks

  • Listeners using $event->getArgument(0) on these seven events must switch to the named arguments.
    Legacy CMSPlugin listeners are unaffected.
  • onMailBeforeTagsRendering, onFinderSortOrderFields and onBuildAdministratorLoginURL no longer pass
    by reference.
  • onGetStats listeners must return an array; a non-array result now throws instead of being skipped.
  • onFinderIndexAfterIndex listeners receive a Result, now type-checked.

Testing Instructions

Install the test plugin below, publish it, then work through the five areas. Every step should log a line
to administrator/logs/ (or use error_log() / a var_dump() if you prefer).

Test plugin — plugins/system/eventprobe/

eventprobe.xml:

<?xml version="1.0" encoding="utf-8"?>
<extension type="plugin" group="system" method="upgrade">
    <name>plg_system_eventprobe</name>
    <version>1.0.0</version>
    <files>
        <filename plugin="eventprobe">eventprobe.php</filename>
    </files>
</extension>

eventprobe.php:

<?php
\defined('_JEXEC') or die;

use Joomla\CMS\Log\Log;
use Joomla\CMS\Plugin\CMSPlugin;
use Joomla\Event\SubscriberInterface;

class PlgSystemEventprobe extends CMSPlugin implements SubscriberInterface
{
    public static function getSubscribedEvents(): array
    {
        return [
            'onFinderIndexAfterIndex'      => 'probe',
            'onFinderIndexAfterDelete'     => 'probe',
            'onFinderIndexAfterPurge'      => 'probe',
            'onFinderSortOrderFields'      => 'probe',
            'onMailBeforeTagsRendering'    => 'probe',
            'onGetStats'                   => 'probe',
            'onBuildAdministratorLoginURL' => 'probe',
        ];
    }

    public function probe($event): void
    {
        Log::add(
            $event->getName() . ' -> ' . \get_class($event) . ' args: ' . implode(', ', array_keys($event->getArguments())),
            Log::INFO,
            'eventprobe'
        );

        // onGetStats expects an array result.
        if ($event->getName() === 'onGetStats') {
            $event->addResult([['title' => 'Event probe', 'data' => 'ok']]);
        }
    }
}

Register it in #__extensions via Discover, or install it as a zip.

1. Smart Search indexing
Components → Smart Search → Index. Press Index, let it finish, then press Purge, then Index again.
Delete a published article and confirm its entry disappears from the index.
The log should show onFinderIndexAfterIndex, onFinderIndexAfterDelete and onFinderIndexAfterPurge
with the concrete classes and named arguments (subject, linkId / id / no arguments).

2. Smart Search sort order (front end)
With Smart Search indexed, add a Search menu item and run a search. The "Sort by" dropdown must still list
all its options. onFinderSortOrderFields fires with a sortOrderFields argument, and a listener calling
$event->updateSortOrderFields([...]) must visibly change the dropdown.

3. Mail template tags
System → Templates → Mail Templates → edit any template. The clickable list of tags below the body field
must render exactly as before. onMailBeforeTagsRendering fires with templateId and subject.

4. Statistics modules
Enable Statistics (site, mod_stats) in a template position and view the front end, and check the
Popular Articles / statistics area on the admin home dashboard (mod_stats_admin). Existing rows must
render, plus the extra "Event probe → ok" row the test plugin adds via addResult().

5. Update notification task
System → Scheduled Tasks → create or run an Update notification task. It must run without error and send
its mail. onBuildAdministratorLoginURL fires with a subject holding the Uri, and a listener calling
$event->getUri()->setVar('secret', 'abc') must have that parameter appear in the link inside the mail.

Also worth checking: with the test plugin published, no new deprecation notices appear in the log on a
normal page load — the PluginHelper change must not start emitting CMSPlugin::setDispatcher()
deprecations for ordinary plugins.

Actual result BEFORE applying this Pull Request

All five areas work, but the seven events are dispatched as a generic Joomla\Event\Event with positional
arguments, so a listener can only reach them through $event->getArgument(0). Three of them rely on
by-reference arguments. A plugin declaring DispatcherAwareInterface itself gets an unset dispatcher from
PluginHelper::import(), and $this->getDispatcher() throws UnexpectedValueException.

Expected result AFTER applying this Pull Request

All five areas behave identically for a user. The seven events are dispatched as concrete classes with
named, validated arguments and documented getters. Listeners change values through update*() methods
instead of by-reference arguments. Plugins that declare DispatcherAwareInterface themselves receive the
dispatcher their listeners were registered on.

Link to documentations

Please select:

  • Documentation link for guide.joomla.org:

  • No documentation changes for guide.joomla.org needed

  • Pull Request link for manual.joomla.org: joomla/Manual#704

  • No documentation changes for manual.joomla.org needed

avatar MacJoom MacJoom - open - 6 Sep 2026
avatar MacJoom MacJoom - change - 6 Sep 2026
Status New Pending
avatar joomla-cms-bot joomla-cms-bot - change - 6 Sep 2026
Category Administration com_finder Modules Front End Libraries Plugins
avatar MacJoom MacJoom - change - 6 Sep 2026
The description was changed
avatar MacJoom MacJoom - edited - 6 Sep 2026

Add a Comment

Login with GitHub to post a comment