? ? ? Pending

User tests: Successful: Unsuccessful:

avatar mbabker
mbabker
17 Mar 2019

Summary of Changes

To register commands to the console application, the best practice is to have a console plugin catching the Joomla\Application\ApplicationEvents::BEFORE_EXECUTE event and adding the command via $event->getApplication()->addCommand(). However, this comes with a performance penalty of having instantiated a Joomla\Console\Command\AbstractCommand class object. This issue is in part caused by the design of Joomla\Console\Loader\LoaderInterface which in essence specifies a read-only API once the loader is instantiated, which is suitable for the environments that Symfony applications are built for (and where the joomla/console package design came from, in addition to internally using it to the extent practical) or less dynamic Joomla! Framework applications. This read-only API isn't a great fit for the CMS environment.

To bypass this performance penalty, a new Joomla\CMS\Console\Loader\WritableLoaderInterface is introduced with a Joomla\CMS\Console\Loader\WritableContainerLoader implementation of it, and the console application adjusted to use this writable loader. This means plugins would now be able to add the command to the lazy loader and have it instantiated as late as possible as a container service instead of requiring the command class always be instantiated (and yes, even though this is a CLI thing, you still don't want a performance trade-off of potentially instantiating 50 unused classes).

Testing Instructions

Running php cli/joomla.php with this patch applied still works. Unit tests for the new class pass. If creating a console plugin to add a command, this snippet is the gist of what you'd do to register a command.

use Joomla\Application\Event\ApplicationEvent;
use Joomla\CMS\Console\Loader\WritableLoaderInterface;
use Joomla\CMS\Factory;
use Joomla\DI\Container;

public function onBeforeExecute(ApplicationEvent $event): void
{
    $serviceId = 'my.command';

    Factory::getContainer()->share(
        $serviceId,
        function (Container $container) {
            // do stuff to create command class and return it
        },
        true
    );

    Factory::getContainer()->get(WritableLoaderInterface::class)->add('my:command', $serviceId);
}
avatar mbabker mbabker - open - 17 Mar 2019
avatar mbabker mbabker - change - 17 Mar 2019
Status New Pending
avatar joomla-cms-bot joomla-cms-bot - change - 17 Mar 2019
Category Libraries Unit Tests
avatar wilsonge wilsonge - change - 17 Mar 2019
Labels Added: ? ?
avatar wilsonge wilsonge - change - 17 Mar 2019
Status Pending Fixed in Code Base
Closed_Date 0000-00-00 00:00:00 2019-03-17 23:29:46
Closed_By wilsonge
Labels Added: ?
avatar wilsonge wilsonge - close - 17 Mar 2019
avatar wilsonge wilsonge - merge - 17 Mar 2019
avatar wilsonge
wilsonge - comment - 17 Mar 2019

Thanks - looks good to me

avatar alikon
alikon - comment - 20 Mar 2019

@bosunski can you please double check if we need to change something in #21452

avatar bosunski
bosunski - comment - 27 Mar 2019

Alright, @alikon.

I have updated the PR.

avatar svenbluege
svenbluege - comment - 7 Oct 2020

Joomla\Application\ApplicationEvents::BEFORE_EXECUTE is application.before_execute and it looks like only events like this get executed while launching cli/joomla.php. The method onBeforeExecute gets not executed. Adding something like Factory::getApplication()->getDispatcher()->addListener(ApplicationEvents::BEFORE_EXECUTE, array($this, 'onBeforeExecute')); in the constructor of a system plugin helps, but I guess this is not what you had in mind, right? Do you have any hints on how to integrate a new command via a plugin?


This comment was created with the J!Tracker Application at issues.joomla.org/tracker/joomla-cms/24223.

avatar svenbluege
svenbluege - comment - 10 Oct 2020

Never mind. Of course this is unnecessary. I updated the documentation here: https://docs.joomla.org/J4.x:Writing_A_CLI_Application

avatar wilsonge
wilsonge - comment - 10 Oct 2020

Sorry just came to look into this. Thankyou for updating the docs!

Add a Comment

Login with GitHub to post a comment