Framework Documentation - Application Package
CLI applications
AbstractApplication has no HTTP dependency. It is a perfectly good base for cron jobs, workers
and one-off scripts, and it is what joomla/console builds on.
A plain CLI application
<?php
use Joomla\Application\AbstractApplication;
use Joomla\Registry\Registry;
require __DIR__ . '/vendor/autoload.php';
final class ImportApplication extends AbstractApplication
{
protected function doExecute()
{
$file = $this->get('import.file');
if (!is_readable($file)) {
$this->getLogger()->error('Import file is not readable', ['file' => $file]);
$this->close(1);
}
foreach (new SplFileObject($file) as $line) {
// …
}
$this->getLogger()->info('Import finished');
}
}
$app = new ImportApplication(new Registry(['import.file' => $argv[1] ?? '']));
$app->execute();You still get the configuration registry, the PSR-3 logger, the event dispatcher and the
before_execute/after_execute/error events.
Note that AbstractApplication::execute() swallows the throwable — it turns it into an
application.error event and returns normally, with exit code 0. For a CLI process that is usually
wrong. Add a listener that sets the exit status:
$dispatcher->addListener(
ApplicationEvents::ERROR,
static function (ApplicationErrorEvent $event): void {
fwrite(STDERR, $event->getError()->getMessage() . PHP_EOL);
$event->getApplication()->close(1);
}
);Console applications
For anything with commands, arguments and options, use joomla/console instead of rolling your
own. Joomla\Console\Application extends AbstractApplication, so the configuration, logger and
event machinery documented here apply unchanged:
use Joomla\Console\Application;
use Joomla\Console\Command\AbstractCommand;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
final class ImportCommand extends AbstractCommand
{
protected static $defaultName = 'app:import';
protected function configure(): void
{
$this->setDescription('Import records from a CSV file');
$this->addArgument('file', InputArgument::REQUIRED, 'Path to the CSV file');
}
protected function doExecute(InputInterface $input, OutputInterface $output): int
{
$file = $input->getArgument('file');
// …
return 0;
}
}
$console = new Application();
$console->addCommand(new ImportCommand());
$console->execute();
joomla/consoleuses the$defaultNamestatic property, which Symfony removed in 7.0 in favour
of the#[AsCommand]attribute. Use$defaultNamehere — the attribute is not read.
Sharing code between web and CLI
Put the shared wiring in a container and let each entry point build the application it needs:
// bootstrap.php
return static function (): Joomla\DI\Container {
$container = new Joomla\DI\Container();
$container->registerServiceProvider(new ConfigProvider());
$container->registerServiceProvider(new DatabaseProvider());
$container->registerServiceProvider(new EventProvider());
return $container;
};// public/index.php
$container = (require __DIR__ . '/../bootstrap.php')();
$container->get(Joomla\Application\WebApplication::class)->execute();// bin/console
$container = (require __DIR__ . '/../bootstrap.php')();
$container->get(Joomla\Console\Application::class)->execute();The tutorial builds exactly this structure — see
Building a complete application.