[ Index ]

PHP Cross Reference of Joomla 4.2.2 documentation

title

Body

[close]

/libraries/src/Console/ -> SetConfigurationCommand.php (source)

   1  <?php
   2  
   3  /**
   4   * Joomla! Content Management System
   5   *
   6   * @copyright  (C) 2020 Open Source Matters, Inc. <https://www.joomla.org>
   7   * @license    GNU General Public License version 2 or later; see LICENSE.txt
   8   */
   9  
  10  namespace Joomla\CMS\Console;
  11  
  12  use Joomla\CMS\Factory;
  13  use Joomla\CMS\Language\Text;
  14  use Joomla\Console\Command\AbstractCommand;
  15  use Joomla\Database\DatabaseDriver;
  16  use Joomla\Registry\Registry;
  17  use Symfony\Component\Console\Input\Input;
  18  use Symfony\Component\Console\Input\InputArgument;
  19  use Symfony\Component\Console\Input\InputInterface;
  20  use Symfony\Component\Console\Output\OutputInterface;
  21  use Symfony\Component\Console\Style\SymfonyStyle;
  22  
  23  // phpcs:disable PSR1.Files.SideEffects
  24  \defined('JPATH_PLATFORM') or die;
  25  // phpcs:enable PSR1.Files.SideEffects
  26  
  27  /**
  28   * Console command Setting Configuration options
  29   *
  30   * @since  4.0.0
  31   */
  32  class SetConfigurationCommand extends AbstractCommand
  33  {
  34      /**
  35       * The default command name
  36       *
  37       * @var    string
  38       * @since  4.0.0
  39       */
  40      protected static $defaultName = 'config:set';
  41  
  42      /**
  43       * Stores the Input Object
  44       * @var Input
  45       * @since 4.0.0
  46       */
  47      private $cliInput;
  48  
  49      /**
  50       * SymfonyStyle Object
  51       * @var SymfonyStyle
  52       * @since 4.0.0
  53       */
  54      private $ioStyle;
  55  
  56      /**
  57       * Options Array
  58       * @var array
  59       * @since 4.0.0
  60       */
  61      private $options;
  62  
  63  
  64      /**
  65       * Return code if configuration is set successfully
  66       * @since 4.0.0
  67       */
  68      public const CONFIG_SET_SUCCESSFUL = 0;
  69  
  70      /**
  71       * Return code if configuration set failed
  72       * @since 4.0.0
  73       */
  74      public const CONFIG_SET_FAILED = 1;
  75  
  76      /**
  77       * Return code if config validation failed
  78       * @since 4.0.0
  79       */
  80      public const CONFIG_VALIDATION_FAILED = 2;
  81  
  82      /**
  83       * Return code if options are wrong
  84       * @since 4.0.0
  85       */
  86      public const CONFIG_OPTIONS_WRONG = 3;
  87  
  88      /**
  89       * Return code if database validation failed
  90       * @since 4.0.0
  91       */
  92      public const DB_VALIDATION_FAILED = 4;
  93  
  94      /**
  95       * Configures the IO
  96       *
  97       * @param   InputInterface   $input   Console Input
  98       * @param   OutputInterface  $output  Console Output
  99       *
 100       * @return void
 101       *
 102       * @since 4.0.0
 103       *
 104       */
 105      private function configureIO(InputInterface $input, OutputInterface $output)
 106      {
 107          $language = Factory::getLanguage();
 108          $language->load('', JPATH_INSTALLATION, null, false, false) ||
 109          $language->load('', JPATH_INSTALLATION, null, true);
 110          $language->load('com_config', JPATH_ADMINISTRATOR, null, false, false) ||
 111          $language->load('com_config', JPATH_ADMINISTRATOR, null, true);
 112          $this->cliInput = $input;
 113          $this->ioStyle = new SymfonyStyle($input, $output);
 114      }
 115  
 116      /**
 117       * Collects options from user input
 118       *
 119       * @param   array  $options  Options input by users
 120       *
 121       * @return boolean
 122       *
 123       * @since 4.0.0
 124       */
 125      private function retrieveOptionsFromInput(array $options): bool
 126      {
 127          $collected = [];
 128  
 129          foreach ($options as $option) {
 130              if (strpos($option, '=') === false) {
 131                  $this->ioStyle->error('Options and values should be separated by "="');
 132  
 133                  return false;
 134              }
 135  
 136              list($option, $value) = explode('=', $option);
 137  
 138              $collected[$option] = $value;
 139          }
 140  
 141          $this->options = $collected;
 142  
 143          return true;
 144      }
 145  
 146      /**
 147       * Validates the options provided
 148       *
 149       * @return boolean
 150       *
 151       * @since 4.0.0
 152       */
 153      private function validateOptions(): bool
 154      {
 155          $config = $this->getInitialConfigurationOptions();
 156  
 157          $configs = $config->toArray();
 158  
 159          $valid = true;
 160          array_walk(
 161              $this->options,
 162              function ($value, $key) use ($configs, &$valid) {
 163                  if (!array_key_exists($key, $configs)) {
 164                      $this->ioStyle->error("Can't find option *$key* in configuration list");
 165                      $valid = false;
 166                  }
 167              }
 168          );
 169  
 170          return $valid;
 171      }
 172  
 173      /**
 174       * Sets the options array
 175       *
 176       * @param   string  $options  Options string
 177       *
 178       * @since 4.0.0
 179       *
 180       * @return void
 181       */
 182      public function setOptions($options)
 183      {
 184          $this->options = explode(' ', $options);
 185      }
 186  
 187      /**
 188       * Collects the options array
 189       *
 190       * @return array|mixed
 191       *
 192       * @since 4.0.0
 193       */
 194      public function getOptions()
 195      {
 196          return $this->cliInput->getArgument('options');
 197      }
 198  
 199      /**
 200       * Returns Default configuration Object
 201       *
 202       * @return Registry
 203       *
 204       * @since 4.0.0
 205       */
 206      public function getInitialConfigurationOptions(): Registry
 207      {
 208          return (new Registry(new \JConfig()));
 209      }
 210  
 211  
 212      /**
 213       * Save the configuration file
 214       *
 215       * @param   array  $options  Options array
 216       *
 217       * @return boolean
 218       *
 219       * @since 4.0.0
 220       */
 221      public function saveConfiguration($options): bool
 222      {
 223          $app = $this->getApplication();
 224  
 225          // Check db connection encryption properties
 226          $model = $app->bootComponent('com_config')->getMVCFactory($app)->createModel('Application', 'Administrator');
 227  
 228          if (!$model->save($options)) {
 229              $this->ioStyle->error(Text::_('Failed to save properties'));
 230  
 231              return false;
 232          }
 233  
 234          return true;
 235      }
 236  
 237      /**
 238       * Initialise the command.
 239       *
 240       * @return void
 241       *
 242       * @since 4.0.0
 243       */
 244      protected function configure(): void
 245      {
 246          $this->addArgument(
 247              'options',
 248              InputArgument::REQUIRED | InputArgument::IS_ARRAY,
 249              'All the options you want to set'
 250          );
 251  
 252          $help = "<info>%command.name%</info> sets the value for a configuration option
 253                  \nUsage: <info>php %command.full_name%</info> <option>=<value>";
 254  
 255          $this->setDescription('Set a value for a configuration option');
 256          $this->setHelp($help);
 257      }
 258  
 259      /**
 260       * Verifies database connection
 261       *
 262       * @param   array  $options  Options array
 263       *
 264       * @return boolean|\Joomla\Database\DatabaseInterface
 265       *
 266       * @since 4.0.0
 267       * @throws \Exception
 268       */
 269      public function checkDb($options): bool
 270      {
 271          // Ensure a database type was selected.
 272          if (empty($options['dbtype'])) {
 273              $this->ioStyle->error(Text::_('INSTL_DATABASE_INVALID_TYPE'));
 274  
 275              return false;
 276          }
 277  
 278          // Ensure that a hostname and user name were input.
 279          if (empty($options['host']) || empty($options['user'])) {
 280              $this->ioStyle->error(Text::_('INSTL_DATABASE_INVALID_DB_DETAILS'));
 281  
 282              return false;
 283          }
 284  
 285          // Validate database table prefix.
 286          if (isset($options['dbprefix']) && !preg_match('#^[a-zA-Z]+[a-zA-Z0-9_]*$#', $options['dbprefix'])) {
 287              $this->ioStyle->error(Text::_('INSTL_DATABASE_PREFIX_MSG'));
 288  
 289              return false;
 290          }
 291  
 292          // Validate length of database table prefix.
 293          if (isset($options['dbprefix']) && strlen($options['dbprefix']) > 15) {
 294              $this->ioStyle->error(Text::_('INSTL_DATABASE_FIX_TOO_LONG'), 'warning');
 295  
 296              return false;
 297          }
 298  
 299          // Validate length of database name.
 300          if (strlen($options['db']) > 64) {
 301              $this->ioStyle->error(Text::_('INSTL_DATABASE_NAME_TOO_LONG'));
 302  
 303              return false;
 304          }
 305  
 306          // Validate database name.
 307          if (in_array($options['dbtype'], ['pgsql', 'postgresql'], true) && !preg_match('#^[a-zA-Z_][0-9a-zA-Z_$]*$#', $options['db'])) {
 308              $this->ioStyle->error(Text::_('INSTL_DATABASE_NAME_MSG_POSTGRES'));
 309  
 310              return false;
 311          }
 312  
 313          if (in_array($options['dbtype'], ['mysql', 'mysqli']) && preg_match('#[\\\\\/]#', $options['db'])) {
 314              $this->ioStyle->error(Text::_('INSTL_DATABASE_NAME_MSG_MYSQL'));
 315  
 316              return false;
 317          }
 318  
 319          // Workaround for UPPERCASE table prefix for PostgreSQL
 320          if (in_array($options['dbtype'], ['pgsql', 'postgresql'])) {
 321              if (isset($options['dbprefix']) && strtolower($options['dbprefix']) !== $options['dbprefix']) {
 322                  $this->ioStyle->error(Text::_('INSTL_DATABASE_FIX_LOWERCASE'));
 323  
 324                  return false;
 325              }
 326          }
 327  
 328          $app = $this->getApplication();
 329  
 330          // Check db connection encryption properties
 331          $model = $app->bootComponent('com_config')->getMVCFactory($app)->createModel('Application', 'Administrator');
 332  
 333          if (!$model->validateDbConnection($options)) {
 334              $this->ioStyle->error(Text::_('Failed to validate the db connection encryption properties'));
 335  
 336              return false;
 337          }
 338  
 339          // Build the connection options array.
 340          $settings = [
 341              'driver'   => $options['dbtype'],
 342              'host'     => $options['host'],
 343              'user'     => $options['user'],
 344              'password' => $options['password'],
 345              'database' => $options['db'],
 346              'prefix'   => $options['dbprefix'],
 347          ];
 348  
 349          if ((int) $options['dbencryption'] !== 0) {
 350              $settings['ssl'] = [
 351                  'enable'             => true,
 352                  'verify_server_cert' => (bool) $options['dbsslverifyservercert'],
 353              ];
 354  
 355              foreach (['cipher', 'ca', 'key', 'cert'] as $value) {
 356                  $confVal = trim($options['dbssl' . $value]);
 357  
 358                  if ($confVal !== '') {
 359                      $settings['ssl'][$value] = $confVal;
 360                  }
 361              }
 362          }
 363  
 364          // Get a database object.
 365          try {
 366              $db = DatabaseDriver::getInstance($settings);
 367              $db->getVersion();
 368          } catch (\Exception $e) {
 369              $this->ioStyle->error(
 370                  Text::sprintf(
 371                      'Cannot connect to database, verify that you specified the correct database details %s',
 372                      $e->getMessage()
 373                  )
 374              );
 375  
 376              return false;
 377          }
 378  
 379          if ((int) $options['dbencryption'] !== 0 && empty($db->getConnectionEncryption())) {
 380              if ($db->isConnectionEncryptionSupported()) {
 381                  $this->ioStyle->error(Text::_('COM_CONFIG_ERROR_DATABASE_ENCRYPTION_CONN_NOT_ENCRYPT'));
 382              } else {
 383                  $this->ioStyle->error(Text::_('COM_CONFIG_ERROR_DATABASE_ENCRYPTION_SRV_NOT_SUPPORTS'));
 384              }
 385  
 386              return false;
 387          }
 388  
 389          return true;
 390      }
 391  
 392      /**
 393       * Internal function to execute the command.
 394       *
 395       * @param   InputInterface   $input   The input to inject into the command.
 396       * @param   OutputInterface  $output  The output to inject into the command.
 397       *
 398       * @return  integer  The command exit code
 399       *
 400       * @since   4.0.0
 401       * @throws \Exception
 402       */
 403      protected function doExecute(InputInterface $input, OutputInterface $output): int
 404      {
 405          $this->configureIO($input, $output);
 406  
 407          $options = $this->getOptions();
 408  
 409          if (!$this->retrieveOptionsFromInput($options)) {
 410              return self::CONFIG_OPTIONS_WRONG;
 411          }
 412  
 413          if (!$this->validateOptions()) {
 414              return self::CONFIG_VALIDATION_FAILED;
 415          }
 416  
 417          $initialOptions = $this->getInitialConfigurationOptions()->toArray();
 418  
 419          $combinedOptions = $this->sanitizeOptions(array_merge($initialOptions, $this->options));
 420  
 421          if (!$this->checkDb($combinedOptions)) {
 422              return self::DB_VALIDATION_FAILED;
 423          }
 424  
 425          if ($this->saveConfiguration($combinedOptions)) {
 426              $this->ioStyle->success('Configuration set');
 427  
 428              return self::CONFIG_SET_SUCCESSFUL;
 429          }
 430  
 431          return self::CONFIG_SET_FAILED;
 432      }
 433  
 434      /**
 435       * Sanitize the options array for boolean
 436       *
 437       * @param   array  $options  Options array
 438       *
 439       * @return array
 440       *
 441       * @since 4.0.0
 442       */
 443      public function sanitizeOptions(array $options): array
 444      {
 445          foreach ($options as $key => $value) {
 446              $value = $value === 'false' ? false : $value;
 447              $value = $value === 'true' ? true : $value;
 448  
 449              $options[$key] = $value;
 450          }
 451  
 452          return $options;
 453      }
 454  }


Generated: Wed Sep 7 05:41:13 2022 Chilli.vc Blog - For Webmaster,Blog-Writer,System Admin and Domainer