. * --------------------------------------------------------------------- */ namespace Glpi\Console; if (!defined('GLPI_ROOT')) { die("Sorry. You can't access this file directly"); } use Config; use DB; use GLPI; use Glpi\Application\ErrorHandler; use Glpi\Console\Command\ForceNoPluginsOptionCommandInterface; use Glpi\Console\Command\GlpiCommandInterface; use Glpi\System\RequirementsManager; use Plugin; use Session; use Toolbox; use Symfony\Component\Console\Application as BaseApplication; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Helper\Helper; use Symfony\Component\Console\Input\ArgvInput; 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\Exception\CommandNotFoundException; use Symfony\Component\Console\Exception\RuntimeException; use Symfony\Component\Console\Output\OutputInterface; class Application extends BaseApplication { /** * Error code returned when system requirements are missing. * * @var integer */ const ERROR_MISSING_REQUIREMENTS = 128; // start application codes at 128 be sure to be different from commands codes /** * Pointer to $CFG_GLPI. * @var array */ private $config; /** * @var ErrorHandler */ private $error_handler; /** * @var DB */ private $db; /** * @var OutputInterface */ private $output; public function __construct() { parent::__construct('GLPI CLI', GLPI_VERSION); $this->initApplication(); $this->initDb(); $this->initSession(); $this->initCache(); $this->initConfig(); $this->computeAndLoadOutputLang(); // Load core commands only to check if called command prevent or not usage of plugins // Plugin commands will be loaded later $loader = new CommandLoader(false); $this->setCommandLoader($loader); $use_plugins = $this->usePlugins(); if ($use_plugins) { $this->loadActivePlugins(); $loader->registerPluginsCommands(); } } protected function getDefaultInputDefinition() { $definition = new InputDefinition( [ new InputArgument( 'command', InputArgument::REQUIRED, __('The command to execute') ), new InputOption( '--help', '-h', InputOption::VALUE_NONE, __('Display this help message') ), new InputOption( '--quiet', '-q', InputOption::VALUE_NONE, __('Do not output any message') ), 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, __('Display this application version') ), new InputOption( '--ansi', null, InputOption::VALUE_NONE, __('Force ANSI output') ), new InputOption( '--no-ansi', null, InputOption::VALUE_NONE, __('Disable ANSI output') ), new InputOption( '--no-interaction', '-n', InputOption::VALUE_NONE, __('Do not ask any interactive question') ), new InputOption( '--config-dir', null, InputOption::VALUE_OPTIONAL, __('Configuration directory to use') ), new InputOption( '--no-plugins', null, InputOption::VALUE_NONE, __('Disable GLPI plugins (unless commands forces plugins loading)') ), new InputOption( '--lang', null, InputOption::VALUE_OPTIONAL, __('Output language (default value is existing GLPI "language" configuration or "en_GB")') ) ] ); return $definition; } protected function configureIO(InputInterface $input, OutputInterface $output) { global $CFG_GLPI; $this->output = $output; $this->error_handler->setOutputHandler($output); parent::configureIO($input, $output); // Trigger error on invalid lang. This is not done before as error handler would not be set. $lang = $input->getParameterOption('--lang', null, true); if (null !== $lang && !array_key_exists($lang, $CFG_GLPI['languages'])) { throw new RuntimeException( sprintf(__('Invalid "--lang" option value "%s".'), $lang) ); } if ($output->getVerbosity() === OutputInterface::VERBOSITY_DEBUG) { Toolbox::setDebugMode(Session::DEBUG_MODE, 0, 0, 1); } } /** * Returns output handler. * * @return OutputInterface */ public function getOutput() { return $this->output; } protected function doRunCommand(Command $command, InputInterface $input, OutputInterface $output) { $begin_time = microtime(true); if ($command instanceof GlpiCommandInterface && $command->mustCheckMandatoryRequirements() && !$this->checkCoreMandatoryRequirements()) { return self::ERROR_MISSING_REQUIREMENTS; } $result = parent::doRunCommand($command, $input, $output); if ($output->getVerbosity() >= OutputInterface::VERBOSITY_VERY_VERBOSE) { $output->writeln( sprintf( __('Time elapsed: %s.'), Helper::formatTime(microtime(true) - $begin_time) ) ); $output->writeln( sprintf( __('Memory usage: %s.'), Helper::formatMemory(memory_get_peak_usage(true)) ) ); } return $result; } /** * Initalize GLPI. * * @global array $CFG_GLPI * @global GLPI $GLPI * * @return void */ private function initApplication() { // Disable debug at bootstrap (will be re-enabled later if requested by verbosity level). global $CFG_GLPI; $CFG_GLPI = array_merge( $CFG_GLPI, [ 'debug_sql' => 0, 'debug_vars' => 0, ] ); global $GLPI; $GLPI = new GLPI(); $GLPI->initLogger(); $this->error_handler = $GLPI->initErrorHandler(); Config::detectRootDoc(); } /** * Initialize database connection. * * @global DB $DB * * @return void * * @throws RuntimeException */ private function initDb() { if (!class_exists('DB', false) || !class_exists('mysqli', false)) { return; } global $DB; $DB = new DB(); $this->db = $DB; if (!$this->db->connected) { return; } ob_start(); $checkdb = Config::displayCheckDbEngine(); $message = ob_get_clean(); if ($checkdb > 0) { throw new RuntimeException($message); } } /** * Initialize GLPI session. * This is mandatory to init cache and load languages. * * @TODO Do not use session for console. * * @return void */ private function initSession() { if (!is_writable(GLPI_SESSION_DIR)) { throw new RuntimeException( sprintf(__('Cannot write in "%s" directory.'), GLPI_SESSION_DIR) ); } Session::setPath(); Session::start(); // Default value for use mode $_SESSION['glpi_use_mode'] = Session::NORMAL_MODE; } /** * Initialize GLPI cache. * * @global Laminas\Cache\Storage\StorageInterface $GLPI_CACHE * * @return void */ private function initCache() { global $GLPI_CACHE; $GLPI_CACHE = Config::getCache('cache_db'); } /** * Initialize GLPI configuration. * * @global array $CFG_GLPI * * @return void */ private function initConfig() { global $CFG_GLPI; $this->config = &$CFG_GLPI; if (!($this->db instanceof DB) || !$this->db->connected) { return; } Config::loadLegacyConfiguration(false); } /** * Compute and load output language. * * @return void * * @throws RuntimeException */ private function computeAndLoadOutputLang() { // 1. Check in command line arguments $input = new ArgvInput(); $lang = $input->getParameterOption('--lang', null, true); if (null !== $lang && !$this->isLanguageValid($lang)) { // Unset requested lang if invalid $lang = null; } // 2. Check in GLPI configuration if (null === $lang && array_key_exists('language', $this->config) && $this->isLanguageValid($this->config['language'])) { $lang = $this->config['language']; } // 3. Use default value if (null === $lang) { $lang = 'en_GB'; } $_SESSION['glpilanguage'] = $lang; Session::loadLanguage('', $this->usePlugins()); } /** * Check if a language is valid. * * @param string $language * * @return boolean */ private function isLanguageValid($language) { return is_array($this->config) && array_key_exists('languages', $this->config) && array_key_exists($language, $this->config['languages']); } /** * Load active plugins. * * @return void */ private function loadActivePlugins() { if (!($this->db instanceof DB) || !$this->db->connected) { return; } $plugin = new Plugin(); $plugin->init(true); } /** * Whether or not plugins have to be used. * * @return boolean */ private function usePlugins() { $input = new ArgvInput(); try { $command = $this->find($this->getCommandName($input)); if ($command instanceof ForceNoPluginsOptionCommandInterface) { return !$command->getNoPluginsOptionValue(); } } catch (CommandNotFoundException $e) { // Command will not be found at this point if it is a plugin command $command = null; // Say hello to CS checker } return !$input->hasParameterOption('--no-plugins', true); } /** * Check if core mandatory requirements are OK. * * @return boolean true if requirements are OK, false otherwise */ private function checkCoreMandatoryRequirements(): bool { $db = property_exists($this, 'db') ? $this->db : null; $requirements_manager = new RequirementsManager(); $core_requirements = $requirements_manager->getCoreRequirementList( $db instanceof \DBmysql && $db->connected ? $db : null ); if ($core_requirements->hasMissingMandatoryRequirements()) { $message = __('Some mandatory system requirements are missing.') . ' ' . __('Run "php bin/console glpi:system:check_requirements" for more details.'); $this->output->writeln( '' . $message . '', OutputInterface::VERBOSITY_QUIET ); return false; } return true; } }