Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 4.3.x will end 7 October 2024 (12 months).
  • Bug fixes for security issues in 4.3.x will end 21 April 2025 (18 months).
  • PHP version: minimum PHP 8.0.0 Note: minimum PHP version has increased since Moodle 4.1. PHP 8.2.x is supported too.

Differences Between: [Versions 310 and 403] [Versions 311 and 403] [Versions 39 and 403] [Versions 400 and 403] [Versions 401 and 403]

   1  <?php
   2  
   3  // This file is part of Moodle - http://moodle.org/
   4  //
   5  // Moodle is free software: you can redistribute it and/or modify
   6  // it under the terms of the GNU General Public License as published by
   7  // the Free Software Foundation, either version 3 of the License, or
   8  // (at your option) any later version.
   9  //
  10  // Moodle is distributed in the hope that it will be useful,
  11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
  12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13  // GNU General Public License for more details.
  14  //
  15  // You should have received a copy of the GNU General Public License
  16  // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
  17  
  18  /**
  19   * This script creates config.php file and prepares database.
  20   *
  21   * This script is not intended for beginners!
  22   * Potential problems:
  23   * - su to apache account or sudo before execution
  24   * - not compatible with Windows platform
  25   *
  26   * @package    core
  27   * @subpackage cli
  28   * @copyright  2009 Petr Skoda (http://skodak.org)
  29   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  30   */
  31  
  32  // Force OPcache reset if used, we do not want any stale caches
  33  // when detecting if upgrade necessary or when running upgrade.
  34  if (function_exists('opcache_reset') and !isset($_SERVER['REMOTE_ADDR'])) {
  35      opcache_reset();
  36  }
  37  
  38  define('CLI_SCRIPT', true);
  39  define('CACHE_DISABLE_ALL', true);
  40  
  41  require(__DIR__.'/../../config.php');
  42  require_once($CFG->libdir.'/adminlib.php');       // various admin-only functions
  43  require_once($CFG->libdir.'/upgradelib.php');     // general upgrade/install related functions
  44  require_once($CFG->libdir.'/clilib.php');         // cli only functions
  45  require_once($CFG->libdir.'/environmentlib.php');
  46  
  47  // now get cli options
  48  $lang = isset($SESSION->lang) ? $SESSION->lang : $CFG->lang;
  49  list($options, $unrecognized) = cli_get_params(
  50      array(
  51          'allow-unstable'            => false,
  52          'help'                      => false,
  53          'is-maintenance-required'   => false,
  54          'is-pending'                => false,
  55          'lang'                      => $lang,
  56          'maintenance'               => true,
  57          'non-interactive'           => false,
  58          'set-ui-upgrade-lock'       => false,
  59          'unset-ui-upgrade-lock'     => false,
  60          'verbose-settings'          => false,
  61      ),
  62      array(
  63          'h' => 'help'
  64      )
  65  );
  66  
  67  if ($options['lang']) {
  68      $SESSION->lang = $options['lang'];
  69  }
  70  
  71  $interactive = empty($options['non-interactive']);
  72  
  73  if ($unrecognized) {
  74      $unrecognized = implode("\n  ", $unrecognized);
  75      cli_error(get_string('cliunknowoption', 'admin', $unrecognized));
  76  }
  77  
  78  if ($options['help']) {
  79      $help =
  80  "Command line Moodle upgrade.
  81  Please note you must execute this script with the same uid as apache!
  82  
  83  Site defaults may be changed via local/defaults.php.
  84  
  85  Options:
  86  --allow-unstable            Upgrade even if the version is not marked as stable yet,
  87                              required in non-interactive mode.
  88  -h, --help                  Print out this help.
  89  --is-maintenance-required   Returns exit code 2 if the upgrade requires maintenance mode.
  90                              Returns exit code 3 if no maintenance is required for the upgrade.
  91  --is-pending                Exit with error code 2 if an upgrade is required.
  92  --lang=CODE                 Set preferred language for CLI output. Defaults to the
  93                              site language if not set. Defaults to 'en' if the lang
  94                              parameter is invalid or if the language pack is not
  95                              installed.
  96  --maintenance               Sets whether this upgrade will use maintenance mode.
  97                              If not possible, the upgrade will not happen and the script will exit.
  98                              WARNING: Caches (except theme) will be STALE and MUST be purged after upgrading.
  99                              DO NOT USE if the upgrade contains known breaking changes to the way data
 100                              and the database interact.
 101                              RECOMMENDED for lightweight deployments, to allow for a graceful purge and
 102                              rebuild of the cache.
 103  --non-interactive           No interactive questions or confirmations.
 104  --set-ui-upgrade-lock       Sets the upgrade to CLI only and unable to be triggered from the frontend.
 105                              If called with --maintenance=false, the lock WILL NOT be released when the
 106                              upgrade finishes, and MUST be manually removed.
 107                              If called with --is-maintenance-required before an upgrade,
 108                              The lock WILL be released when the upgrade finishes.
 109  --unset-ui-upgrade-lock     Removes the frontend upgrade lock, if the lock exists.
 110                              Useful when an error during the upgrade leaves the upgrade locked,
 111                              or there is need to control the time where the unlock happens.
 112  --verbose-settings          Show new settings values. By default only the name of
 113                              new core or plugin settings are displayed. This option
 114                              outputs the new values as well as the setting name.
 115  
 116  Example:
 117  \$sudo -u www-data /usr/bin/php admin/cli/upgrade.php
 118  "; //TODO: localize - to be translated later when everything is finished
 119  
 120      echo $help;
 121      die;
 122  }
 123  
 124  if (empty($CFG->version)) {
 125      cli_error(get_string('missingconfigversion', 'debug'));
 126  }
 127  
 128  require("$CFG->dirroot/version.php");       // defines $version, $release, $branch and $maturity
 129  $CFG->target_release = $release;            // used during installation and upgrades
 130  
 131  if ($version < $CFG->version) {
 132      cli_error(get_string('downgradedcore', 'error'));
 133  }
 134  
 135  $oldversion = "$CFG->release ($CFG->version)";
 136  $newversion = "$release ($version)";
 137  
 138  if ($options['unset-ui-upgrade-lock']) {
 139      // Unconditionally unset this config if requested.
 140      set_config('outagelessupgrade', false);
 141      cli_writeln(get_string('cliupgradeunsetlock', 'admin'));
 142  }
 143  
 144  $allhash = core_component::get_all_component_hash();
 145  
 146  // Initialise allcomponent hash if not set. It will be correctly set after upgrade.
 147  $CFG->allcomponenthash = $CFG->allcomponenthash ?? '';
 148  if (!$options['maintenance']) {
 149      if ($allhash !== $CFG->allcomponenthash) {
 150          // Throw an error here, we can't proceed, this needs to set maintenance.
 151          cli_error(get_string('cliupgrademaintenancerequired', 'core_admin'), 2);
 152      }
 153  
 154      // Set a constant to stop any upgrade var from being set during processing.
 155      // This protects against the upgrade setting timeouts and maintenance during the upgrade.
 156      define('CLI_UPGRADE_RUNNING', true);
 157  
 158      // This database control is the control to block the GUI from doing upgrade related actions.
 159      set_config('outagelessupgrade', true);
 160  }
 161  
 162  // We should ignore all upgrade locks here.
 163  if (!moodle_needs_upgrading(false)) {
 164      cli_error(get_string('cliupgradenoneed', 'core_admin', $newversion), 0);
 165  }
 166  
 167  // Handle exit based options for outputting upgrade state.
 168  if ($options['is-pending'] || $options['is-maintenance-required']) {
 169      // If we aren't doing a maintenance check, plain pending check.
 170      if (!$options['is-maintenance-required']) {
 171          cli_error(get_string('cliupgradepending', 'core_admin'), 2);
 172      }
 173  
 174      // Can we do this safely with no maintenance/outage? Detect if there is a schema or other application state change.
 175      if ($allhash !== $CFG->allcomponenthash) {
 176          // State change here, we need to do this in maintenance.
 177          cli_writeln(get_string('cliupgradepending', 'core_admin'));
 178          cli_error(get_string('cliupgrademaintenancerequired', 'core_admin'), 2);
 179      }
 180  
 181      // If requested, we should always set the upgrade lock here, so this cannot be run from frontend.
 182      if ($options['set-ui-upgrade-lock']) {
 183          set_config('outagelessupgrade', true);
 184          cli_writeln(get_string('cliupgradesetlock', 'admin'));
 185      }
 186  
 187      // We can do an upgrade without maintenance!
 188      cli_writeln(get_string('cliupgradepending', 'core_admin'));
 189      cli_error(get_string('cliupgrademaintenancenotrequired', 'core_admin'), 3);
 190  }
 191  
 192  // Test environment first.
 193  list($envstatus, $environment_results) = check_moodle_environment(normalize_version($release), ENV_SELECT_RELEASE);
 194  if (!$envstatus) {
 195      $errors = environment_get_errors($environment_results);
 196      cli_heading(get_string('environment', 'admin'));
 197      foreach ($errors as $error) {
 198          list($info, $report) = $error;
 199          echo "!! $info !!\n$report\n\n";
 200      }
 201      exit(1);
 202  }
 203  
 204  // Make sure there are no files left over from previous versions.
 205  if (upgrade_stale_php_files_present()) {
 206      cli_problem(get_string('upgradestalefiles', 'admin'));
 207  
 208      // Stale file info contains HTML elements which aren't suitable for CLI.
 209      $upgradestalefilesinfo = get_string('upgradestalefilesinfo', 'admin', get_docs_url('Upgrading'));
 210      cli_error(strip_tags($upgradestalefilesinfo));
 211  }
 212  
 213  // Test plugin dependencies.
 214  $failed = array();
 215  if (!core_plugin_manager::instance()->all_plugins_ok($version, $failed, $CFG->branch)) {
 216      cli_problem(get_string('pluginscheckfailed', 'admin', array('pluginslist' => implode(', ', array_unique($failed)))));
 217      cli_error(get_string('pluginschecktodo', 'admin'));
 218  }
 219  
 220  $a = new stdClass();
 221  $a->oldversion = $oldversion;
 222  $a->newversion = $newversion;
 223  
 224  if ($interactive) {
 225      echo cli_heading(get_string('databasechecking', '', $a)) . PHP_EOL;
 226  }
 227  
 228  // make sure we are upgrading to a stable release or display a warning
 229  if (isset($maturity)) {
 230      if (($maturity < MATURITY_STABLE) and !$options['allow-unstable']) {
 231          $maturitylevel = get_string('maturity'.$maturity, 'admin');
 232  
 233          if ($interactive) {
 234              cli_separator();
 235              cli_heading(get_string('notice'));
 236              echo get_string('maturitycorewarning', 'admin', $maturitylevel) . PHP_EOL;
 237              echo get_string('morehelp') . ': ' . get_docs_url('admin/versions') . PHP_EOL;
 238              cli_separator();
 239          } else {
 240              cli_problem(get_string('maturitycorewarning', 'admin', $maturitylevel));
 241              cli_error(get_string('maturityallowunstable', 'admin'));
 242          }
 243      }
 244  }
 245  
 246  if ($interactive) {
 247      echo html_to_text(get_string('upgradesure', 'admin', $newversion))."\n";
 248      $prompt = get_string('cliyesnoprompt', 'admin');
 249      $input = cli_input($prompt, '', array(get_string('clianswerno', 'admin'), get_string('cliansweryes', 'admin')));
 250      if ($input == get_string('clianswerno', 'admin')) {
 251          exit(1);
 252      }
 253  }
 254  
 255  if ($version > $CFG->version) {
 256  
 257      // Only purge caches if this is a plain upgrade.
 258      // In the case of a no-outage upgrade, we will gracefully roll caches after upgrade.
 259      if ($options['maintenance']) {
 260          // We purge all of MUC's caches here.
 261          // Caches are disabled for upgrade by CACHE_DISABLE_ALL so we must set the first arg to true.
 262          // This ensures a real config object is loaded and the stores will be purged.
 263          // This is the only way we can purge custom caches such as memcache or APC.
 264          // Note: all other calls to caches will still used the disabled API.
 265          cache_helper::purge_all(true);
 266      }
 267  
 268      upgrade_core($version, true);
 269  }
 270  set_config('release', $release);
 271  set_config('branch', $branch);
 272  
 273  // unconditionally upgrade
 274  upgrade_noncore(true);
 275  
 276  // log in as admin - we need doanything permission when applying defaults
 277  \core\session\manager::set_user(get_admin());
 278  
 279  // Apply default settings and output those that have changed.
 280  cli_heading(get_string('cliupgradedefaultheading', 'admin'));
 281  $settingsoutput = admin_apply_default_settings(null, false);
 282  
 283  foreach ($settingsoutput as $setting => $value) {
 284  
 285      if ($options['verbose-settings']) {
 286          $stringvlaues = array(
 287                  'name' => $setting,
 288                  'defaultsetting' => var_export($value, true) // Expand objects.
 289          );
 290          echo get_string('cliupgradedefaultverbose', 'admin', $stringvlaues) . PHP_EOL;
 291  
 292      } else {
 293          echo get_string('cliupgradedefault', 'admin', $setting) . PHP_EOL;
 294  
 295      }
 296  }
 297  
 298  // This needs to happen at the end to ensure it occurs after all caches
 299  // have been purged for the last time.
 300  // This will build a cached version of the current theme for the user
 301  // to immediately start browsing the site.
 302  upgrade_themes();
 303  
 304  echo get_string('cliupgradefinished', 'admin', $a)."\n";
 305  
 306  if (!$options['maintenance']) {
 307      cli_writeln(get_string('cliupgradecompletenomaintenanceupgrade', 'admin'));
 308  
 309      // Here we check if upgrade lock has not been specifically set during this upgrade run.
 310      // This supports wider server orchestration actions happening, which should call with no-maintenance AND set-ui-upgrade-lock,
 311      // such as a new docker container deployment, of which the moodle upgrade is only a component.
 312      if (!$options['set-ui-upgrade-lock']) {
 313          // In this case we should release the lock now, as the upgrade is finished.
 314          // We weren't told to keep the lock with set-ui-upgrade-lock, so release.
 315          set_config('outagelessupgrade', false);
 316      }
 317  }
 318  
 319  exit(0); // 0 means success