Skip to content

Commit

Permalink
CLI application crashed instantiating MVCFactory (joomla#38524)
Browse files Browse the repository at this point in the history
* CLI application crashed instantiating MVCFactory

Closes joomlagh-38518

* Self-document the --live-site option

* Update libraries/src/Application/ConsoleApplication.php

Co-authored-by: Richard Fath <richard67@users.noreply.github.com>

* No CLI --live-site uses https://joomla.invalid/set/by/console/application

Per joomla#38524 (comment)

* Make the example URL HTTPS

Per joomla#38524 (review)

* Consistent code alignment

* Code callisthenics

* Code callisthenics, redux

* Remove the ternary

Co-authored-by: Richard Fath <richard67@users.noreply.github.com>
  • Loading branch information
nikosdion and richard67 authored Aug 20, 2022
1 parent 63f9f9c commit b775fdb
Showing 1 changed file with 109 additions and 0 deletions.
109 changes: 109 additions & 0 deletions libraries/src/Application/ConsoleApplication.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
use Joomla\CMS\Language\Language;
use Joomla\CMS\Plugin\PluginHelper;
use Joomla\CMS\Router\Router;
use Joomla\CMS\Uri\Uri;
use Joomla\CMS\Version;
use Joomla\Console\Application;
use Joomla\Database\DatabaseAwareTrait;
Expand All @@ -27,7 +28,10 @@
use Joomla\Input\Input;
use Joomla\Registry\Registry;
use Joomla\Session\SessionInterface;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputDefinition;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;

Expand Down Expand Up @@ -229,6 +233,17 @@ public function execute()
// Load extension namespaces
$this->createExtensionNamespaceMap();

/**
* Address issues with instantiating WebApplication descendants under CLI.
*
* IMPORTANT! This code must be always be executed **before** the first use of
* PluginHelper::importPlugin(). Some plugins will attempt to register an MVCFactory for a
* component in their service provider. This will in turn try to get the SiteRouter service
* for the component which tries to get an instance of SiteApplication which will fail with
* a RuntimeException if the populateHttpHost() method has not already executed.
*/
$this->populateHttpHost();

// Import CMS plugin groups to be able to subscribe to events
PluginHelper::importPlugin('system');
PluginHelper::importPlugin('console');
Expand Down Expand Up @@ -463,4 +478,98 @@ public static function getRouter($name = null, array $options = array())

return Router::getInstance($name, $options);
}

/**
* Populates the HTTP_HOST and REQUEST_URI from the URL provided in the --live-site parameter.
*
* If the URL provided is empty or invalid we will use the URL
* https://joomla.invalid/set/by/console/application just so that the CLI application doesn't
* crash when a WebApplication descendant is instantiated in it.
*
* This is a practical workaround for using any service depending on a WebApplication
* descendant under CLI.
*
* Practical example: using a component's MVCFactory which instantiates the SiteRouter
* service for that component which in turn relies on an instance of SiteApplication.
*
* @return void
* @since __DEPLOY_VERSION__
* @see https://github.com/joomla/joomla-cms/issues/38518
*/
protected function populateHttpHost()
{
// First check for the --live-site command line option.
$input = $this->getConsoleInput();
$liveSite = '';

if ($input->hasParameterOption(['--live-site', false])) {
$liveSite = $input->getParameterOption(['--live-site'], '');
}

// Fallback to the $live_site global configuration option in configuration.php
$liveSite = $liveSite ?: $this->get('live_site', 'https://joomla.invalid/set/by/console/application');

/**
* Try to use the live site URL we were given. If all else fails, fall back to
* https://joomla.invalid/set/by/console/application.
*/
try {
$uri = Uri::getInstance($liveSite);
} catch (\RuntimeException $e) {
$uri = Uri::getInstance('https://joomla.invalid/set/by/console/application');
}

/**
* Yes, this is icky but it is the only way to trick WebApplication into compliance.
*
* @see \Joomla\Application\AbstractWebApplication::detectRequestUri
*/
$_SERVER['HTTP_HOST'] = $uri->toString(['host', 'port']);
$_SERVER['REQUEST_URI'] = $uri->getPath();
$_SERVER['HTTPS'] = $uri->getScheme() === 'https' ? 'on' : 'off';
}

/**
* Builds the default input definition.
*
* @return InputDefinition
*
* @since __DEPLOY_VERSION__
*/
protected function getDefaultInputDefinition(): InputDefinition
{
return new InputDefinition(
[
new InputArgument('command', InputArgument::REQUIRED, 'The command to execute'),
new InputOption(
'--live-site',
null,
InputOption::VALUE_OPTIONAL,
'The URL to your site, e.g. https://www.example.com'
),
new InputOption('--help', '-h', InputOption::VALUE_NONE, 'Display the help information'),
new InputOption(
'--quiet',
'-q',
InputOption::VALUE_NONE,
'Flag indicating that all output should be silenced'
),
new InputOption(
'--verbose',
'-v|vv|vvv',
InputOption::VALUE_NONE,
'Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug'
),
new InputOption('--version', '-V', InputOption::VALUE_NONE, 'Displays the application version'),
new InputOption('--ansi', '', InputOption::VALUE_NONE, 'Force ANSI output'),
new InputOption('--no-ansi', '', InputOption::VALUE_NONE, 'Disable ANSI output'),
new InputOption(
'--no-interaction',
'-n',
InputOption::VALUE_NONE,
'Flag to disable interacting with the user'
),
]
);
}
}

0 comments on commit b775fdb

Please sign in to comment.