User tests: Successful: Unsuccessful:
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).
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);
}
Status | New | ⇒ | Pending |
Category | ⇒ | Libraries Unit Tests |
Labels |
Added:
?
?
|
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:
?
|
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?
Never mind. Of course this is unnecessary. I updated the documentation here: https://docs.joomla.org/J4.x:Writing_A_CLI_Application
Sorry just came to look into this. Thankyou for updating the docs!
Thanks - looks good to me