%PDF- %PDF-
Direktori : /var/www/projetos/suporte.iigd.com.br.old/src/Console/Database/ |
Current File : //var/www/projetos/suporte.iigd.com.br.old/src/Console/Database/AbstractConfigureCommand.php |
<?php /** * --------------------------------------------------------------------- * * GLPI - Gestionnaire Libre de Parc Informatique * * http://glpi-project.org * * @copyright 2015-2022 Teclib' and contributors. * @copyright 2003-2014 by the INDEPNET Development Team. * @licence https://www.gnu.org/licenses/gpl-3.0.html * * --------------------------------------------------------------------- * * LICENSE * * This file is part of GLPI. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <https://www.gnu.org/licenses/>. * * --------------------------------------------------------------------- */ namespace Glpi\Console\Database; use Config; use DBConnection; use DBmysql; use Glpi\Console\AbstractCommand; use Glpi\Console\Command\ForceNoPluginsOptionCommandInterface; use Glpi\System\Requirement\DbTimezones; use mysqli; use Symfony\Component\Console\Exception\InvalidArgumentException; use Symfony\Component\Console\Helper\Table; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Question\Question; abstract class AbstractConfigureCommand extends AbstractCommand implements ForceNoPluginsOptionCommandInterface { /** * Error code returned if DB configuration succeed. * * @var integer */ const SUCCESS = 0; /** * Error code returned if DB connection initialization fails. * * @var integer */ const ERROR_DB_CONNECTION_FAILED = 1; /** * Error code returned if DB engine is unsupported. * * @var integer */ const ERROR_DB_ENGINE_UNSUPPORTED = 2; /** * Error code returned when trying to configure and having a DB config already set. * * @var integer */ const ERROR_DB_CONFIG_ALREADY_SET = 3; /** * Error code returned when failing to save database configuration file. * * @var integer */ const ERROR_DB_CONFIG_FILE_NOT_SAVED = 4; protected $requires_db_up_to_date = false; protected function configure() { parent::configure(); $this->setName('glpi:database:install'); $this->setAliases(['db:install']); $this->setDescription('Install database schema'); $this->addOption( 'db-host', 'H', InputOption::VALUE_OPTIONAL, __('Database host'), 'localhost' ); $this->addOption( 'db-name', 'd', InputOption::VALUE_REQUIRED, __('Database name') ); $this->addOption( 'db-password', 'p', InputOption::VALUE_OPTIONAL, __('Database password (will be prompted for value if option passed without value)'), '' // Empty string by default (enable detection of null if passed without value) ); $this->addOption( 'db-port', 'P', InputOption::VALUE_OPTIONAL, __('Database port') ); $this->addOption( 'db-user', 'u', InputOption::VALUE_REQUIRED, __('Database user') ); $this->addOption( 'reconfigure', 'r', InputOption::VALUE_NONE, __('Reconfigure database, override configuration file if it already exists') ); $this->addOption( 'strict-configuration', null, InputOption::VALUE_NONE, __('Use strict configuration, to enforce warnings triggering on deprecated or discouraged usages') ); } protected function interact(InputInterface $input, OutputInterface $output) { $questions = [ 'db-name' => new Question(__('Database name:'), ''), // Required 'db-user' => new Question(__('Database user:'), ''), // Required 'db-password' => new Question(__('Database password:'), ''), // Prompt if null (passed without value) ]; $questions['db-password']->setHidden(true); // Make password input hidden foreach ($questions as $name => $question) { if (null === $input->getOption($name)) { /** @var \Symfony\Component\Console\Helper\QuestionHelper $question_helper */ $question_helper = $this->getHelper('question'); $value = $question_helper->ask($input, $output, $question); $input->setOption($name, $value); } } } protected function initDbConnection() { return; // Prevent DB connection } /** * Save database configuration file. * * @param InputInterface $input * @param OutputInterface $output * @param bool $compute_flags_from_db * * @throws InvalidArgumentException * * @return void */ protected function configureDatabase( InputInterface $input, OutputInterface $output, bool $compute_flags_from_db = true ) { $db_pass = $input->getOption('db-password'); $db_host = $input->getOption('db-host'); $db_name = $input->getOption('db-name'); $db_port = $input->getOption('db-port'); $db_user = $input->getOption('db-user'); $db_hostport = $db_host . (!empty($db_port) ? ':' . $db_port : ''); $reconfigure = $input->getOption('reconfigure'); $strict_configuration = $input->getOption('strict-configuration'); if (file_exists(GLPI_CONFIG_DIR . '/config_db.php') && !$reconfigure) { // Prevent overriding of existing DB throw new \Glpi\Console\Exception\EarlyExitException( '<error>' . __('Database configuration already exists. Use --reconfigure option to override existing configuration.') . '</error>', self::ERROR_DB_CONFIG_ALREADY_SET ); } $this->validateConfigInput($input); $this->askForDbConfigConfirmation( $input, $output, $db_hostport, $db_name, $db_user ); mysqli_report(MYSQLI_REPORT_OFF); $mysqli = new mysqli(); if (intval($db_port) > 0) { // Network port @$mysqli->connect($db_host, $db_user, $db_pass, null, $db_port); } else { // Unix Domain Socket @$mysqli->connect($db_host, $db_user, $db_pass, null, 0, $db_port); } if (0 !== $mysqli->connect_errno) { $message = sprintf( __('Database connection failed with message "(%s) %s".'), $mysqli->connect_errno, $mysqli->connect_error ); throw new \Glpi\Console\Exception\EarlyExitException( '<error>' . $message . '</error>', self::ERROR_DB_CONNECTION_FAILED ); } $db_exists = @$mysqli->select_db($db_name); ob_start(); $db_version_data = $mysqli->query('SELECT version()')->fetch_array(); $checkdb = Config::displayCheckDbEngine(false, $db_version_data[0]); $message = ob_get_clean(); if ($checkdb > 0) { throw new \Glpi\Console\Exception\EarlyExitException( '<error>' . $message . '</error>', self::ERROR_DB_ENGINE_UNSUPPORTED ); } if (!$db_exists || $strict_configuration || !$compute_flags_from_db) { // Force strict configuration $use_utf8mb4 = true; $allow_myisam = false; $allow_datetime = false; $allow_signed_keys = false; } else { // Instanciate DB to be able to compute boolean properties flags. $db = new class ($db_hostport, $db_user, $db_pass, $db_name) extends DBmysql { public function __construct($dbhost, $dbuser, $dbpassword, $dbdefault) { $this->dbhost = $dbhost; $this->dbuser = $dbuser; $this->dbpassword = $dbpassword; $this->dbdefault = $dbdefault; parent::__construct(); } }; $config_flags = $db->getComputedConfigBooleanFlags(); $use_utf8mb4 = $config_flags[DBConnection::PROPERTY_USE_UTF8MB4] ?? false; $allow_myisam = $config_flags[DBConnection::PROPERTY_ALLOW_MYISAM] ?? true; $allow_datetime = $config_flags[DBConnection::PROPERTY_ALLOW_DATETIME] ?? true; $allow_signed_keys = $config_flags[DBConnection::PROPERTY_ALLOW_SIGNED_KEYS] ?? true; } $log_deprecation_warnings = $strict_configuration; DBConnection::setConnectionCharset($mysqli, $use_utf8mb4); $are_timezones_available = $this->checkTimezonesAvailability($mysqli); $use_timezones = !$allow_datetime && $are_timezones_available; $db_name = $mysqli->real_escape_string($db_name); $output->writeln( '<comment>' . __('Saving configuration file...') . '</comment>', OutputInterface::VERBOSITY_VERBOSE ); $result = DBConnection::createMainConfig( $db_hostport, $db_user, $db_pass, $db_name, $use_timezones, $log_deprecation_warnings, $use_utf8mb4, $allow_myisam, $allow_datetime, $allow_signed_keys ); if (!$result) { $message = sprintf( __('Cannot write configuration file "%s".'), GLPI_CONFIG_DIR . DIRECTORY_SEPARATOR . 'config_db.php' ); throw new \Glpi\Console\Exception\EarlyExitException( '<error>' . $message . '</error>', self::ERROR_DB_CONFIG_FILE_NOT_SAVED ); } // Set $db instance to use new connection properties $this->db = new class ( $db_hostport, $db_user, $db_pass, $db_name, $use_timezones, $log_deprecation_warnings, $use_utf8mb4, $allow_myisam, $allow_datetime, $allow_signed_keys ) extends DBmysql { public function __construct( $dbhost, $dbuser, $dbpassword, $dbdefault, $use_timezones, $log_deprecation_warnings, $use_utf8mb4, $allow_myisam, $allow_datetime, $allow_signed_keys ) { $this->dbhost = $dbhost; $this->dbuser = $dbuser; $this->dbpassword = $dbpassword; $this->dbdefault = $dbdefault; $this->use_timezones = $use_timezones; $this->use_utf8mb4 = $use_utf8mb4; $this->allow_myisam = $allow_myisam; $this->allow_datetime = $allow_datetime; $this->allow_signed_keys = $allow_signed_keys; $this->log_deprecation_warnings = $log_deprecation_warnings; $this->clearSchemaCache(); } }; } public function getNoPluginsOptionValue() { return true; } /** * Check if DB is already configured. * * @return boolean */ protected function isDbAlreadyConfigured() { return file_exists(GLPI_CONFIG_DIR . '/config_db.php'); } /** * Validate configuration variables from input. * * @param InputInterface $input * * @throws InvalidArgumentException */ protected function validateConfigInput(InputInterface $input) { $db_name = $input->getOption('db-name'); $db_user = $input->getOption('db-user'); $db_pass = $input->getOption('db-password'); if (empty($db_name)) { throw new \Symfony\Component\Console\Exception\InvalidArgumentException( __('Database name defined by --db-name option cannot be empty.') ); } if (empty($db_user)) { throw new \Symfony\Component\Console\Exception\InvalidArgumentException( __('Database user defined by --db-user option cannot be empty.') ); } if (null === $db_pass) { // Will be null if option used without value and without interaction throw new \Symfony\Component\Console\Exception\InvalidArgumentException( __('--db-password option value cannot be null.') ); } } /** * Ask user to confirm DB configuration. * * @param InputInterface $input * @param OutputInterface $output * @param string $db_hostport DB host and port * @param string $db_name DB name * @param string $db_user DB username * * @return void */ protected function askForDbConfigConfirmation( InputInterface $input, OutputInterface $output, $db_hostport, $db_name, $db_user ) { $informations = new Table($output); $informations->addRow([__('Database host'), $db_hostport]); $informations->addRow([__('Database name'), $db_name]); $informations->addRow([__('Database user'), $db_user]); $informations->render(); $this->askForConfirmation(); } /** * Check timezones availability and return availability state. * * @param mysqli $mysqli * * @return bool */ private function checkTimezonesAvailability(mysqli $mysqli): bool { $db = new class ($mysqli) extends DBmysql { public function __construct($dbh) { $this->dbh = $dbh; } }; $timezones_requirement = new DbTimezones($db); if (!$timezones_requirement->isValidated()) { $message = __('Timezones usage cannot be activated due to following errors:'); foreach ($timezones_requirement->getValidationMessages() as $validation_message) { $message .= "\n - " . $validation_message; } $this->output->writeln( '<comment>' . $message . '</comment>', OutputInterface::VERBOSITY_QUIET ); if ($this->input->getOption('no-interaction')) { $message = sprintf( __('Fix them and run the "php bin/console %1$s" command to enable timezones.'), 'glpi:database:enable_timezones' ); $this->output->writeln('<comment>' . $message . '</comment>', OutputInterface::VERBOSITY_QUIET); } else { $this->askForConfirmation(); } } return $timezones_requirement->isValidated(); } }