Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

  • Bug fixes for general core bugs in 3.9.x will end* 10 May 2021 (12 months).
  • Bug fixes for security issues in 3.9.x will end* 8 May 2023 (36 months).
  • PHP version: minimum PHP 7.2.0 Note: minimum PHP version has increased since Moodle 3.8. PHP 7.3.x and 7.4.x are supported too.

Differences Between: [Versions 39 and 310] [Versions 39 and 311] [Versions 39 and 400] [Versions 39 and 401] [Versions 39 and 402] [Versions 39 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 library includes all the necessary stuff to execute some standard
  20   * tests of required versions and libraries to run Moodle. It can be
  21   * used from the admin interface, and both at install and upgrade.
  22   *
  23   * All the info is stored in the admin/environment.xml file,
  24   * supporting to have an updated version in dataroot/environment
  25   *
  26   * @copyright  (C) 2001-3001 Eloy Lafuente (stronk7) {@link http://contiento.com}
  27   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  28   * @package    core
  29   * @subpackage admin
  30   */
  31  
  32  defined('MOODLE_INTERNAL') || die();
  33  
  34  /// Add required files
  35  /**
  36   * Include the necessary
  37   */
  38      require_once($CFG->libdir.'/xmlize.php');
  39  
  40  /// Define a bunch of XML processing errors
  41      /** XML Processing Error */
  42      define('NO_ERROR',                           0);
  43      /** XML Processing Error */
  44      define('NO_VERSION_DATA_FOUND',              1);
  45      /** XML Processing Error */
  46      define('NO_DATABASE_SECTION_FOUND',          2);
  47      /** XML Processing Error */
  48      define('NO_DATABASE_VENDORS_FOUND',          3);
  49      /** XML Processing Error */
  50      define('NO_DATABASE_VENDOR_MYSQL_FOUND',     4);
  51      /** XML Processing Error */
  52      define('NO_DATABASE_VENDOR_POSTGRES_FOUND',  5);
  53      /** XML Processing Error */
  54      define('NO_PHP_SECTION_FOUND',               6);
  55      /** XML Processing Error */
  56      define('NO_PHP_VERSION_FOUND',               7);
  57      /** XML Processing Error */
  58      define('NO_PHP_EXTENSIONS_SECTION_FOUND',    8);
  59      /** XML Processing Error */
  60      define('NO_PHP_EXTENSIONS_NAME_FOUND',       9);
  61      /** XML Processing Error */
  62      define('NO_DATABASE_VENDOR_VERSION_FOUND',  10);
  63      /** XML Processing Error */
  64      define('NO_UNICODE_SECTION_FOUND',          11);
  65      /** XML Processing Error */
  66      define('NO_CUSTOM_CHECK_FOUND',             12);
  67      /** XML Processing Error */
  68      define('CUSTOM_CHECK_FILE_MISSING',         13);
  69      /** XML Processing Error */
  70      define('CUSTOM_CHECK_FUNCTION_MISSING',     14);
  71      /** XML Processing Error */
  72      define('NO_PHP_SETTINGS_NAME_FOUND',        15);
  73      /** XML Processing Error */
  74      define('INCORRECT_FEEDBACK_FOR_REQUIRED',   16);
  75      /** XML Processing Error */
  76      define('INCORRECT_FEEDBACK_FOR_OPTIONAL',   17);
  77  
  78  /// Define algorithm used to select the xml file
  79      /** To select the newer file available to perform checks */
  80      define('ENV_SELECT_NEWER',                   0);
  81      /** To enforce the use of the file under dataroot */
  82      define('ENV_SELECT_DATAROOT',                1);
  83      /** To enforce the use of the file under admin (release) */
  84      define('ENV_SELECT_RELEASE',                 2);
  85  
  86  /**
  87   * This function checks all the requirements defined in environment.xml.
  88   *
  89   * @param string $version version to check.
  90   * @param int $env_select one of ENV_SELECT_NEWER | ENV_SELECT_DATAROOT | ENV_SELECT_RELEASE decide xml to use. Default ENV_SELECT_NEWER (BC)
  91   * @return array with two elements. The first element true/false, depending on
  92   *      on whether the check passed. The second element is an array of environment_results
  93   *      objects that has detailed information about the checks and which ones passed.
  94   */
  95  function check_moodle_environment($version, $env_select = ENV_SELECT_NEWER) {
  96      if ($env_select != ENV_SELECT_NEWER and $env_select != ENV_SELECT_DATAROOT and $env_select != ENV_SELECT_RELEASE) {
  97          throw new coding_exception('Incorrect value of $env_select parameter');
  98      }
  99  
 100  /// Get the more recent version before the requested
 101      if (!$version = get_latest_version_available($version, $env_select)) {
 102          return array(false, array());
 103      }
 104  
 105  /// Perform all the checks
 106      if (!$environment_results = environment_check($version, $env_select)) {
 107          return array(false, array());
 108      }
 109  
 110  /// Iterate over all the results looking for some error in required items
 111  /// or some error_code
 112      $result = true;
 113      foreach ($environment_results as $environment_result) {
 114          if (!$environment_result->getStatus() && $environment_result->getLevel() == 'required'
 115            && !$environment_result->getBypassStr()) {
 116              $result = false; // required item that is not bypased
 117          } else if ($environment_result->getStatus() && $environment_result->getLevel() == 'required'
 118            && $environment_result->getRestrictStr()) {
 119              $result = false; // required item that is restricted
 120          } else if ($environment_result->getErrorCode()) {
 121              $result = false;
 122          }
 123      }
 124  
 125      return array($result, $environment_results);
 126  }
 127  
 128  
 129  /**
 130   * Returns array of critical errors in plain text format
 131   * @param array $environment_results array of results gathered
 132   * @return array errors
 133   */
 134  function environment_get_errors($environment_results) {
 135      global $CFG;
 136      $errors = array();
 137  
 138      // Iterate over each environment_result
 139      foreach ($environment_results as $environment_result) {
 140          $type = $environment_result->getPart();
 141          $info = $environment_result->getInfo();
 142          $status = $environment_result->getStatus();
 143          $plugin = $environment_result->getPluginName();
 144          $error_code = $environment_result->getErrorCode();
 145  
 146          $a = new stdClass();
 147          if ($error_code) {
 148              $a->error_code = $error_code;
 149              $errors[] = array($info, get_string('environmentxmlerror', 'admin', $a));
 150              return $errors;
 151          }
 152  
 153          /// Calculate the status value
 154          if ($environment_result->getBypassStr() != '') {
 155              // not interesting
 156              continue;
 157          } else if ($environment_result->getRestrictStr() != '') {
 158              // error
 159          } else {
 160              if ($status) {
 161                  // ok
 162                  continue;
 163              } else {
 164                  if ($environment_result->getLevel() == 'optional') {
 165                      // just a warning
 166                      continue;
 167                  } else {
 168                      // error
 169                  }
 170              }
 171          }
 172  
 173          // We are comparing versions
 174          $rec = new stdClass();
 175          if ($rec->needed = $environment_result->getNeededVersion()) {
 176              $rec->current = $environment_result->getCurrentVersion();
 177              if ($environment_result->getLevel() == 'required') {
 178                  $stringtouse = 'environmentrequireversion';
 179              } else {
 180                  $stringtouse = 'environmentrecommendversion';
 181              }
 182          // We are checking installed & enabled things
 183          } else if ($environment_result->getPart() == 'custom_check') {
 184              if ($environment_result->getLevel() == 'required') {
 185                  $stringtouse = 'environmentrequirecustomcheck';
 186              } else {
 187                  $stringtouse = 'environmentrecommendcustomcheck';
 188              }
 189          } else if ($environment_result->getPart() == 'php_setting') {
 190              if ($status) {
 191                  $stringtouse = 'environmentsettingok';
 192              } else if ($environment_result->getLevel() == 'required') {
 193                  $stringtouse = 'environmentmustfixsetting';
 194              } else {
 195                  $stringtouse = 'environmentshouldfixsetting';
 196              }
 197          } else {
 198              if ($environment_result->getLevel() == 'required') {
 199                  $stringtouse = 'environmentrequireinstall';
 200              } else {
 201                  $stringtouse = 'environmentrecommendinstall';
 202              }
 203          }
 204          $report = get_string($stringtouse, 'admin', $rec);
 205  
 206          // Here we'll store all the feedback found
 207          $feedbacktext = '';
 208          // Append  the feedback if there is some
 209          $feedbacktext .= $environment_result->strToReport($environment_result->getFeedbackStr(), 'error');
 210          // Append the restrict if there is some
 211          $feedbacktext .= $environment_result->strToReport($environment_result->getRestrictStr(), 'error');
 212  
 213          if ($plugin === '') {
 214              $report = '[' . get_string('coresystem') . '] ' . $report;
 215          } else {
 216              $report = '[' . $plugin . '] ' . $report;
 217          }
 218  
 219          $report .= ' - ' . html_to_text($feedbacktext);
 220  
 221          if ($environment_result->getPart() == 'custom_check'){
 222              $errors[] = array($info, $report);
 223          } else {
 224              $errors[] = array(($info !== '' ? "$type $info" : $type), $report);
 225          }
 226      }
 227  
 228      return $errors;
 229  }
 230  
 231  
 232  /**
 233   * This function will normalize any version to just a serie of numbers
 234   * separated by dots. Everything else will be removed.
 235   *
 236   * @param string $version the original version
 237   * @return string the normalized version
 238   */
 239  function normalize_version($version) {
 240  
 241  /// 1.9 Beta 2 should be read 1.9 on enviromental checks, not 1.9.2
 242  /// we can discard everything after the first space
 243      $version = trim($version);
 244      $versionarr = explode(" ",$version);
 245      if (!empty($versionarr)) {
 246          $version = $versionarr[0];
 247      }
 248  /// Replace everything but numbers and dots by dots
 249      $version = preg_replace('/[^\.\d]/', '.', $version);
 250  /// Combine multiple dots in one
 251      $version = preg_replace('/(\.{2,})/', '.', $version);
 252  /// Trim possible leading and trailing dots
 253      $version = trim($version, '.');
 254  
 255      return $version;
 256  }
 257  
 258  
 259  /**
 260   * This function will load the environment.xml file and xmlize it
 261   *
 262   * @staticvar array $data
 263   * @uses ENV_SELECT_NEWER
 264   * @uses ENV_SELECT_DATAROOT
 265   * @uses ENV_SELECT_RELEASE
 266   * @param int|string $env_select one of ENV_SELECT_NEWER | ENV_SELECT_DATAROOT | ENV_SELECT_RELEASE decide xml to use. String means plugin name.
 267   * @return mixed the xmlized structure or false on error
 268   */
 269  function load_environment_xml($env_select=ENV_SELECT_NEWER) {
 270  
 271      global $CFG;
 272  
 273      static $data = array(); // Only load and xmlize once by request.
 274  
 275      if (isset($data[$env_select])) {
 276          return $data[$env_select];
 277      }
 278      $contents = false;
 279  
 280      if (is_numeric($env_select)) {
 281          $file = $CFG->dataroot.'/environment/environment.xml';
 282          $internalfile = $CFG->dirroot.'/'.$CFG->admin.'/environment.xml';
 283          switch ($env_select) {
 284              case ENV_SELECT_NEWER:
 285                  if (!is_file($file) || !is_readable($file) || filemtime($file) < filemtime($internalfile) ||
 286                      !$contents = file_get_contents($file)) {
 287                      /// Fallback to fixed $CFG->admin/environment.xml
 288                      if (!is_file($internalfile) || !is_readable($internalfile) || !$contents = file_get_contents($internalfile)) {
 289                          $contents = false;
 290                      }
 291                  }
 292                  break;
 293              case ENV_SELECT_DATAROOT:
 294                  if (!is_file($file) || !is_readable($file) || !$contents = file_get_contents($file)) {
 295                      $contents = false;
 296                  }
 297                  break;
 298              case ENV_SELECT_RELEASE:
 299                  if (!is_file($internalfile) || !is_readable($internalfile) || !$contents = file_get_contents($internalfile)) {
 300                      $contents = false;
 301                  }
 302                  break;
 303          }
 304      } else {
 305          if ($plugindir = core_component::get_component_directory($env_select)) {
 306              $pluginfile = "$plugindir/environment.xml";
 307              if (!is_file($pluginfile) || !is_readable($pluginfile) || !$contents = file_get_contents($pluginfile)) {
 308                  $contents = false;
 309              }
 310          }
 311      }
 312      // XML the whole file.
 313      if ($contents !== false) {
 314          $contents = xmlize($contents);
 315      }
 316  
 317      $data[$env_select] = $contents;
 318  
 319      return $data[$env_select];
 320  }
 321  
 322  
 323  /**
 324   * This function will return the list of Moodle versions available
 325   *
 326   * @return array of versions
 327   */
 328  function get_list_of_environment_versions($contents) {
 329      $versions = array();
 330  
 331      if (isset($contents['COMPATIBILITY_MATRIX']['#']['MOODLE'])) {
 332          foreach ($contents['COMPATIBILITY_MATRIX']['#']['MOODLE'] as $version) {
 333              $versions[] = $version['@']['version'];
 334          }
 335      }
 336  
 337      if (isset($contents['COMPATIBILITY_MATRIX']['#']['PLUGIN'])) {
 338          $versions[] = 'all';
 339      }
 340  
 341      return $versions;
 342  }
 343  
 344  
 345  /**
 346   * This function will return the most recent version in the environment.xml
 347   * file previous or equal to the version requested
 348   *
 349   * @param string $version top version from which we start to look backwards
 350   * @param int $env_select one of ENV_SELECT_NEWER | ENV_SELECT_DATAROOT | ENV_SELECT_RELEASE decide xml to use.
 351   * @return string|bool string more recent version or false if not found
 352   */
 353  function get_latest_version_available($version, $env_select) {
 354      if ($env_select != ENV_SELECT_NEWER and $env_select != ENV_SELECT_DATAROOT and $env_select != ENV_SELECT_RELEASE) {
 355          throw new coding_exception('Incorrect value of $env_select parameter');
 356      }
 357  
 358  /// Normalize the version requested
 359      $version = normalize_version($version);
 360  
 361  /// Load xml file
 362      if (!$contents = load_environment_xml($env_select)) {
 363          return false;
 364      }
 365  
 366  /// Detect available versions
 367      if (!$versions = get_list_of_environment_versions($contents)) {
 368          return false;
 369      }
 370  /// First we look for exact version
 371      if (in_array($version, $versions)) {
 372          return $version;
 373      } else {
 374          $found_version = false;
 375      /// Not exact match, so we are going to iterate over the list searching
 376      /// for the latest version before the requested one
 377          foreach ($versions as $arrversion) {
 378              if (version_compare($arrversion, $version, '<')) {
 379                  $found_version = $arrversion;
 380              }
 381          }
 382      }
 383  
 384      return $found_version;
 385  }
 386  
 387  
 388  /**
 389   * This function will return the xmlized data belonging to one Moodle version
 390   *
 391   * @param string $version top version from which we start to look backwards
 392   * @param int|string $env_select one of ENV_SELECT_NEWER | ENV_SELECT_DATAROOT | ENV_SELECT_RELEASE decide xml to use. String means plugin name.
 393   * @return mixed the xmlized structure or false on error
 394   */
 395  function get_environment_for_version($version, $env_select) {
 396  
 397  /// Normalize the version requested
 398      $version = normalize_version($version);
 399  
 400  /// Load xml file
 401      if (!$contents = load_environment_xml($env_select)) {
 402          return false;
 403      }
 404  
 405  /// Detect available versions
 406      if (!$versions = get_list_of_environment_versions($contents)) {
 407          return false;
 408      }
 409  
 410      // If $env_select is not numeric then this is being called on a plugin, and not the core environment.xml
 411      // If a version of 'all' is in the arry is also means that the new <PLUGIN> tag was found, this should
 412      // be matched against any version of Moodle.
 413      if (!is_numeric($env_select) && in_array('all', $versions)
 414              && environment_verify_plugin($env_select, $contents['COMPATIBILITY_MATRIX']['#']['PLUGIN'][0])) {
 415          return $contents['COMPATIBILITY_MATRIX']['#']['PLUGIN'][0];
 416      }
 417  
 418  /// If the version requested is available
 419      if (!in_array($version, $versions)) {
 420          return false;
 421      }
 422  
 423  /// We now we have it. Extract from full contents.
 424      $fl_arr = array_flip($versions);
 425  
 426      return $contents['COMPATIBILITY_MATRIX']['#']['MOODLE'][$fl_arr[$version]];
 427  }
 428  
 429  /**
 430   * Checks if a plugin tag has a name attribute and it matches the plugin being tested.
 431   *
 432   * @param string $plugin the name of the plugin.
 433   * @param array $pluginxml the xmlised structure for the plugin tag being tested.
 434   * @return boolean true if the name attribute exists and matches the plugin being tested.
 435   */
 436  function environment_verify_plugin($plugin, $pluginxml) {
 437      if (!isset($pluginxml['@']['name']) || $pluginxml['@']['name'] != $plugin) {
 438          return false;
 439      }
 440      return true;
 441  }
 442  
 443  /**
 444   * This function will check for everything (DB, PHP and PHP extensions for now)
 445   * returning an array of environment_result objects.
 446   *
 447   * @global object
 448   * @param string $version xml version we are going to use to test this server
 449   * @param int $env_select one of ENV_SELECT_NEWER | ENV_SELECT_DATAROOT | ENV_SELECT_RELEASE decide xml to use.
 450   * @return environment_results[] array of results encapsulated in one environment_result object
 451   */
 452  function environment_check($version, $env_select) {
 453      global $CFG;
 454  
 455      if ($env_select != ENV_SELECT_NEWER and $env_select != ENV_SELECT_DATAROOT and $env_select != ENV_SELECT_RELEASE) {
 456          throw new coding_exception('Incorrect value of $env_select parameter');
 457      }
 458  
 459  /// Normalize the version requested
 460      $version = normalize_version($version);
 461  
 462      $results = array(); //To store all the results
 463  
 464  /// Only run the moodle versions checker on upgrade, not on install
 465      if (!empty($CFG->version)) {
 466          $results[] = environment_check_moodle($version, $env_select);
 467      }
 468      $results[] = environment_check_unicode($version, $env_select);
 469      $results[] = environment_check_database($version, $env_select);
 470      $results[] = environment_check_php($version, $env_select);
 471  
 472      if ($result = environment_check_pcre_unicode($version, $env_select)) {
 473          $results[] = $result;
 474      }
 475  
 476      $phpext_results = environment_check_php_extensions($version, $env_select);
 477      $results = array_merge($results, $phpext_results);
 478  
 479      $phpsetting_results = environment_check_php_settings($version, $env_select);
 480      $results = array_merge($results, $phpsetting_results);
 481  
 482      $custom_results = environment_custom_checks($version, $env_select);
 483      $results = array_merge($results, $custom_results);
 484  
 485      // Always use the plugin directory version of environment.xml,
 486      // add-on developers need to keep those up-to-date with future info.
 487      foreach (core_component::get_plugin_types() as $plugintype => $unused) {
 488          foreach (core_component::get_plugin_list_with_file($plugintype, 'environment.xml') as $pluginname => $unused) {
 489              $plugin = $plugintype . '_' . $pluginname;
 490  
 491              $result = environment_check_database($version, $plugin);
 492              if ($result->error_code != NO_VERSION_DATA_FOUND
 493                  and $result->error_code != NO_DATABASE_SECTION_FOUND
 494                  and $result->error_code != NO_DATABASE_VENDORS_FOUND) {
 495  
 496                  $result->plugin = $plugin;
 497                  $results[] = $result;
 498              }
 499  
 500              $result = environment_check_php($version, $plugin);
 501              if ($result->error_code != NO_VERSION_DATA_FOUND
 502                  and $result->error_code != NO_PHP_SECTION_FOUND
 503                  and $result->error_code != NO_PHP_VERSION_FOUND) {
 504  
 505                  $result->plugin = $plugin;
 506                  $results[] = $result;
 507              }
 508  
 509              $pluginresults = environment_check_php_extensions($version, $plugin);
 510              foreach ($pluginresults as $result) {
 511                  if ($result->error_code != NO_VERSION_DATA_FOUND
 512                      and $result->error_code != NO_PHP_EXTENSIONS_SECTION_FOUND) {
 513  
 514                      $result->plugin = $plugin;
 515                      $results[] = $result;
 516                  }
 517              }
 518  
 519              $pluginresults = environment_check_php_settings($version, $plugin);
 520              foreach ($pluginresults as $result) {
 521                  if ($result->error_code != NO_VERSION_DATA_FOUND) {
 522                      $result->plugin = $plugin;
 523                      $results[] = $result;
 524                  }
 525              }
 526  
 527              $pluginresults = environment_custom_checks($version, $plugin);
 528              foreach ($pluginresults as $result) {
 529                  if ($result->error_code != NO_VERSION_DATA_FOUND) {
 530                      $result->plugin = $plugin;
 531                      $results[] = $result;
 532                  }
 533              }
 534          }
 535      }
 536  
 537      return $results;
 538  }
 539  
 540  
 541  /**
 542   * This function will check if php extensions requirements are satisfied
 543   *
 544   * @uses NO_VERSION_DATA_FOUND
 545   * @uses NO_PHP_EXTENSIONS_SECTION_FOUND
 546   * @uses NO_PHP_EXTENSIONS_NAME_FOUND
 547   * @param string $version xml version we are going to use to test this server
 548   * @param int|string $env_select one of ENV_SELECT_NEWER | ENV_SELECT_DATAROOT | ENV_SELECT_RELEASE decide xml to use. String means plugin name.
 549   * @return array array of results encapsulated in one environment_result object
 550   */
 551  function environment_check_php_extensions($version, $env_select) {
 552  
 553      $results = array();
 554  
 555  /// Get the enviroment version we need
 556      if (!$data = get_environment_for_version($version, $env_select)) {
 557      /// Error. No version data found
 558          $result = new environment_results('php_extension');
 559          $result->setStatus(false);
 560          $result->setErrorCode(NO_VERSION_DATA_FOUND);
 561          return array($result);
 562      }
 563  
 564  /// Extract the php_extension part
 565      if (!isset($data['#']['PHP_EXTENSIONS']['0']['#']['PHP_EXTENSION'])) {
 566      /// Error. No PHP section found
 567          $result = new environment_results('php_extension');
 568          $result->setStatus(false);
 569          $result->setErrorCode(NO_PHP_EXTENSIONS_SECTION_FOUND);
 570          return array($result);
 571      }
 572  /// Iterate over extensions checking them and creating the needed environment_results
 573      foreach($data['#']['PHP_EXTENSIONS']['0']['#']['PHP_EXTENSION'] as $extension) {
 574          $result = new environment_results('php_extension');
 575      /// Check for level
 576          $level = get_level($extension);
 577      /// Check for extension name
 578          if (!isset($extension['@']['name'])) {
 579              $result->setStatus(false);
 580              $result->setErrorCode(NO_PHP_EXTENSIONS_NAME_FOUND);
 581          } else {
 582              $extension_name = $extension['@']['name'];
 583          /// The name exists. Just check if it's an installed extension
 584              if (!extension_loaded($extension_name)) {
 585                  $result->setStatus(false);
 586              } else {
 587                  $result->setStatus(true);
 588              }
 589              $result->setLevel($level);
 590              $result->setInfo($extension_name);
 591          }
 592  
 593      /// Do any actions defined in the XML file.
 594          process_environment_result($extension, $result);
 595  
 596      /// Add the result to the array of results
 597          $results[] = $result;
 598      }
 599  
 600  
 601      return $results;
 602  }
 603  
 604  /**
 605   * This function will check if php extensions requirements are satisfied
 606   *
 607   * @uses NO_VERSION_DATA_FOUND
 608   * @uses NO_PHP_SETTINGS_NAME_FOUND
 609   * @param string $version xml version we are going to use to test this server
 610   * @param int|string $env_select one of ENV_SELECT_NEWER | ENV_SELECT_DATAROOT | ENV_SELECT_RELEASE decide xml to use. String means plugin name.
 611   * @return array array of results encapsulated in one environment_result object
 612   */
 613  function environment_check_php_settings($version, $env_select) {
 614  
 615      $results = array();
 616  
 617  /// Get the enviroment version we need
 618      if (!$data = get_environment_for_version($version, $env_select)) {
 619      /// Error. No version data found
 620          $result = new environment_results('php_setting');
 621          $result->setStatus(false);
 622          $result->setErrorCode(NO_VERSION_DATA_FOUND);
 623          $results[] = $result;
 624          return $results;
 625      }
 626  
 627  /// Extract the php_setting part
 628      if (!isset($data['#']['PHP_SETTINGS']['0']['#']['PHP_SETTING'])) {
 629      /// No PHP section found - ignore
 630          return $results;
 631      }
 632  /// Iterate over settings checking them and creating the needed environment_results
 633      foreach($data['#']['PHP_SETTINGS']['0']['#']['PHP_SETTING'] as $setting) {
 634          $result = new environment_results('php_setting');
 635      /// Check for level
 636          $level = get_level($setting);
 637          $result->setLevel($level);
 638      /// Check for extension name
 639          if (!isset($setting['@']['name'])) {
 640              $result->setStatus(false);
 641              $result->setErrorCode(NO_PHP_SETTINGS_NAME_FOUND);
 642          } else {
 643              $setting_name  = $setting['@']['name'];
 644              $setting_value = $setting['@']['value'];
 645              $result->setInfo($setting_name);
 646  
 647              if ($setting_name == 'memory_limit') {
 648                  $current = ini_get('memory_limit');
 649                  if ($current == -1) {
 650                      $result->setStatus(true);
 651                  } else {
 652                      $current  = get_real_size($current);
 653                      $minlimit = get_real_size($setting_value);
 654                      if ($current < $minlimit) {
 655                          @ini_set('memory_limit', $setting_value);
 656                          $current = ini_get('memory_limit');
 657                          $current = get_real_size($current);
 658                      }
 659                      $result->setStatus($current >= $minlimit);
 660                  }
 661  
 662              } else {
 663                  $current = ini_get_bool($setting_name);
 664              /// The name exists. Just check if it's an installed extension
 665                  if ($current == $setting_value) {
 666                      $result->setStatus(true);
 667                  } else {
 668                      $result->setStatus(false);
 669                  }
 670              }
 671          }
 672  
 673      /// Do any actions defined in the XML file.
 674          process_environment_result($setting, $result);
 675  
 676      /// Add the result to the array of results
 677          $results[] = $result;
 678      }
 679  
 680  
 681      return $results;
 682  }
 683  
 684  /**
 685   * This function will do the custom checks.
 686   *
 687   * @uses CUSTOM_CHECK_FUNCTION_MISSING
 688   * @uses CUSTOM_CHECK_FILE_MISSING
 689   * @uses NO_CUSTOM_CHECK_FOUND
 690   * @param string $version xml version we are going to use to test this server.
 691   * @param int|string $env_select one of ENV_SELECT_NEWER | ENV_SELECT_DATAROOT | ENV_SELECT_RELEASE decide xml to use. String means plugin name.
 692   * @return array array of results encapsulated in environment_result objects.
 693   */
 694  function environment_custom_checks($version, $env_select) {
 695      global $CFG;
 696  
 697      $results = array();
 698  
 699  /// Get current Moodle version (release) for later compare
 700      $release = isset($CFG->release) ? $CFG->release : $version; /// In case $CFG fails (at install) use $version
 701      $current_version = normalize_version($release);
 702  
 703  /// Get the enviroment version we need
 704      if (!$data = get_environment_for_version($version, $env_select)) {
 705      /// Error. No version data found - but this will already have been reported.
 706          return $results;
 707      }
 708  
 709  /// Extract the CUSTOM_CHECKS part
 710      if (!isset($data['#']['CUSTOM_CHECKS']['0']['#']['CUSTOM_CHECK'])) {
 711      /// No custom checks found - not a problem
 712          return $results;
 713      }
 714  
 715  /// Iterate over extensions checking them and creating the needed environment_results
 716      foreach($data['#']['CUSTOM_CHECKS']['0']['#']['CUSTOM_CHECK'] as $check) {
 717          $result = new environment_results('custom_check');
 718  
 719      /// Check for level
 720          $level = get_level($check);
 721  
 722      /// Check for extension name
 723          if (isset($check['@']['function'])) {
 724              $function = $check['@']['function'];
 725              $file = null;
 726              if (isset($check['@']['file'])) {
 727                  $file = $CFG->dirroot . '/' . $check['@']['file'];
 728                  if (is_readable($file)) {
 729                      include_once($file);
 730                  }
 731              }
 732  
 733              if (is_callable($function)) {
 734                  $result->setLevel($level);
 735                  $result->setInfo($function);
 736                  $result = call_user_func($function, $result);
 737              } else if (!$file or is_readable($file)) {
 738              /// Only show error for current version (where function MUST exist)
 739              /// else, we are performing custom checks against future versiosn
 740              /// and function MAY not exist, so it doesn't cause error, just skip
 741              /// custom check by returning null. MDL-15939
 742                  if (version_compare($current_version, $version, '>=')) {
 743                      $result->setStatus(false);
 744                      $result->setInfo($function);
 745                      $result->setErrorCode(CUSTOM_CHECK_FUNCTION_MISSING);
 746                  } else {
 747                      $result = null;
 748                  }
 749              } else {
 750              /// Only show error for current version (where function MUST exist)
 751              /// else, we are performing custom checks against future versiosn
 752              /// and function MAY not exist, so it doesn't cause error, just skip
 753              /// custom check by returning null. MDL-15939
 754                  if (version_compare($current_version, $version, '>=')) {
 755                      $result->setStatus(false);
 756                      $result->setInfo($function);
 757                      $result->setErrorCode(CUSTOM_CHECK_FILE_MISSING);
 758                  } else {
 759                      $result = null;
 760                  }
 761              }
 762          } else {
 763              $result->setStatus(false);
 764              $result->setErrorCode(NO_CUSTOM_CHECK_FOUND);
 765          }
 766  
 767          if (!is_null($result)) {
 768          /// Do any actions defined in the XML file.
 769              process_environment_result($check, $result);
 770  
 771          /// Add the result to the array of results
 772              $results[] = $result;
 773          }
 774      }
 775  
 776      return $results;
 777  }
 778  
 779  /**
 780   * This function will check if Moodle requirements are satisfied
 781   *
 782   * @uses NO_VERSION_DATA_FOUND
 783   * @param string $version xml version we are going to use to test this server
 784   * @param int|string $env_select one of ENV_SELECT_NEWER | ENV_SELECT_DATAROOT | ENV_SELECT_RELEASE decide xml to use. String means plugin name.
 785   * @return object results encapsulated in one environment_result object
 786   */
 787  function environment_check_moodle($version, $env_select) {
 788  
 789      $result = new environment_results('moodle');
 790  
 791  /// Get the enviroment version we need
 792      if (!$data = get_environment_for_version($version, $env_select)) {
 793      /// Error. No version data found
 794          $result->setStatus(false);
 795          $result->setErrorCode(NO_VERSION_DATA_FOUND);
 796          return $result;
 797      }
 798  
 799  /// Extract the moodle part
 800      if (!isset($data['@']['requires'])) {
 801          $needed_version = '1.0'; /// Default to 1.0 if no moodle requires is found
 802      } else {
 803      /// Extract required moodle version
 804          $needed_version = $data['@']['requires'];
 805      }
 806  
 807  /// Now search the version we are using
 808      $release = get_config('', 'release');
 809      $current_version = normalize_version($release);
 810      if (strpos($release, 'dev') !== false) {
 811          // when final version is required, dev is NOT enough!
 812          $current_version = $current_version - 0.1;
 813      }
 814  
 815  /// And finally compare them, saving results
 816      if (version_compare($current_version, $needed_version, '>=')) {
 817          $result->setStatus(true);
 818      } else {
 819          $result->setStatus(false);
 820      }
 821      $result->setLevel('required');
 822      $result->setCurrentVersion($release);
 823      $result->setNeededVersion($needed_version);
 824  
 825      return $result;
 826  }
 827  
 828  /**
 829   * This function will check if php requirements are satisfied
 830   *
 831   * @uses NO_VERSION_DATA_FOUND
 832   * @uses NO_PHP_SECTION_FOUND
 833   * @uses NO_PHP_VERSION_FOUND
 834   * @param string $version xml version we are going to use to test this server
 835   * @param int|string $env_select one of ENV_SELECT_NEWER | ENV_SELECT_DATAROOT | ENV_SELECT_RELEASE decide xml to use. String means plugin name.
 836   * @return object results encapsulated in one environment_result object
 837   */
 838  function environment_check_php($version, $env_select) {
 839  
 840      $result = new environment_results('php');
 841  
 842  /// Get the enviroment version we need
 843      if (!$data = get_environment_for_version($version, $env_select)) {
 844      /// Error. No version data found
 845          $result->setStatus(false);
 846          $result->setErrorCode(NO_VERSION_DATA_FOUND);
 847          return $result;
 848      }
 849  
 850  /// Extract the php part
 851      if (!isset($data['#']['PHP'])) {
 852      /// Error. No PHP section found
 853          $result->setStatus(false);
 854          $result->setErrorCode(NO_PHP_SECTION_FOUND);
 855          return $result;
 856      } else {
 857      /// Extract level and version
 858          $level = get_level($data['#']['PHP']['0']);
 859          if (!isset($data['#']['PHP']['0']['@']['version'])) {
 860              $result->setStatus(false);
 861              $result->setErrorCode(NO_PHP_VERSION_FOUND);
 862              return $result;
 863          } else {
 864              $needed_version = $data['#']['PHP']['0']['@']['version'];
 865          }
 866      }
 867  
 868  /// Now search the version we are using
 869      $current_version = normalize_version(phpversion());
 870  
 871  /// And finally compare them, saving results
 872      if (version_compare($current_version, $needed_version, '>=')) {
 873          $result->setStatus(true);
 874      } else {
 875          $result->setStatus(false);
 876      }
 877      $result->setLevel($level);
 878      $result->setCurrentVersion($current_version);
 879      $result->setNeededVersion($needed_version);
 880  
 881  /// Do any actions defined in the XML file.
 882      process_environment_result($data['#']['PHP'][0], $result);
 883  
 884      return $result;
 885  }
 886  
 887  /**
 888   * Looks for buggy PCRE implementation, we need unicode support in Moodle...
 889   * @param string $version xml version we are going to use to test this server
 890   * @param int|string $env_select one of ENV_SELECT_NEWER | ENV_SELECT_DATAROOT | ENV_SELECT_RELEASE decide xml to use. String means plugin name.
 891   * @return stdClass results encapsulated in one environment_result object, null if irrelevant
 892   */
 893  function environment_check_pcre_unicode($version, $env_select) {
 894      $result = new environment_results('pcreunicode');
 895  
 896      // Get the environment version we need
 897      if (!$data = get_environment_for_version($version, $env_select)) {
 898          // Error. No version data found!
 899          $result->setStatus(false);
 900          $result->setErrorCode(NO_VERSION_DATA_FOUND);
 901          return $result;
 902      }
 903  
 904      if (!isset($data['#']['PCREUNICODE'])) {
 905          return null;
 906      }
 907  
 908      $level = get_level($data['#']['PCREUNICODE']['0']);
 909      $result->setLevel($level);
 910  
 911      if (!function_exists('preg_match')) {
 912          // The extension test fails instead.
 913          return null;
 914  
 915      } else if (@preg_match('/\pL/u', 'a') and @preg_match('/á/iu', 'Á')) {
 916          $result->setStatus(true);
 917  
 918      } else {
 919          $result->setStatus(false);
 920      }
 921  
 922      // Do any actions defined in the XML file.
 923      process_environment_result($data['#']['PCREUNICODE'][0], $result);
 924  
 925      return $result;
 926  }
 927  
 928  /**
 929   * This function will check if unicode database requirements are satisfied
 930   *
 931   * @uses NO_VERSION_DATA_FOUND
 932   * @uses NO_UNICODE_SECTION_FOUND
 933   * @param string $version xml version we are going to use to test this server
 934   * @param int|string $env_select one of ENV_SELECT_NEWER | ENV_SELECT_DATAROOT | ENV_SELECT_RELEASE decide xml to use. String means plugin name.
 935   * @return object results encapsulated in one environment_result object
 936   */
 937  function environment_check_unicode($version, $env_select) {
 938      global $DB;
 939  
 940      $result = new environment_results('unicode');
 941  
 942      /// Get the enviroment version we need
 943      if (!$data = get_environment_for_version($version, $env_select)) {
 944      /// Error. No version data found
 945          $result->setStatus(false);
 946          $result->setErrorCode(NO_VERSION_DATA_FOUND);
 947          return $result;
 948      }
 949  
 950      /// Extract the unicode part
 951  
 952      if (!isset($data['#']['UNICODE'])) {
 953      /// Error. No UNICODE section found
 954          $result->setStatus(false);
 955          $result->setErrorCode(NO_UNICODE_SECTION_FOUND);
 956          return $result;
 957      } else {
 958      /// Extract level
 959          $level = get_level($data['#']['UNICODE']['0']);
 960      }
 961  
 962      if (!$unicodedb = $DB->setup_is_unicodedb()) {
 963          $result->setStatus(false);
 964      } else {
 965          $result->setStatus(true);
 966      }
 967  
 968      $result->setLevel($level);
 969  
 970  /// Do any actions defined in the XML file.
 971      process_environment_result($data['#']['UNICODE'][0], $result);
 972  
 973      return $result;
 974  }
 975  
 976  /**
 977   * This function will check if database requirements are satisfied
 978   *
 979   * @uses NO_VERSION_DATA_FOUND
 980   * @uses NO_DATABASE_SECTION_FOUND
 981   * @uses NO_DATABASE_VENDORS_FOUND
 982   * @uses NO_DATABASE_VENDOR_MYSQL_FOUND
 983   * @uses NO_DATABASE_VENDOR_POSTGRES_FOUND
 984   * @uses NO_DATABASE_VENDOR_VERSION_FOUND
 985   * @param string $version xml version we are going to use to test this server
 986   * @param int|string $env_select one of ENV_SELECT_NEWER | ENV_SELECT_DATAROOT | ENV_SELECT_RELEASE decide xml to use. String means plugin name.
 987   * @return object results encapsulated in one environment_result object
 988   */
 989  function environment_check_database($version, $env_select) {
 990  
 991      global $DB;
 992  
 993      $result = new environment_results('database');
 994  
 995      $vendors = array();  //Array of vendors in version
 996  
 997  /// Get the enviroment version we need
 998      if (!$data = get_environment_for_version($version, $env_select)) {
 999      /// Error. No version data found
1000          $result->setStatus(false);
1001          $result->setErrorCode(NO_VERSION_DATA_FOUND);
1002          return $result;
1003      }
1004  
1005  /// Extract the database part
1006      if (!isset($data['#']['DATABASE'])) {
1007      /// Error. No DATABASE section found
1008          $result->setStatus(false);
1009          $result->setErrorCode(NO_DATABASE_SECTION_FOUND);
1010          return $result;
1011      } else {
1012      /// Extract level
1013          $level = get_level($data['#']['DATABASE']['0']);
1014      }
1015  
1016  /// Extract DB vendors. At least 2 are mandatory (mysql & postgres)
1017      if (!isset($data['#']['DATABASE']['0']['#']['VENDOR'])) {
1018      /// Error. No VENDORS found
1019          $result->setStatus(false);
1020          $result->setErrorCode(NO_DATABASE_VENDORS_FOUND);
1021          return $result;
1022      } else {
1023      /// Extract vendors
1024          foreach ($data['#']['DATABASE']['0']['#']['VENDOR'] as $vendor) {
1025              if (isset($vendor['@']['name']) && isset($vendor['@']['version'])) {
1026                  $vendors[$vendor['@']['name']] = $vendor['@']['version'];
1027                  $vendorsxml[$vendor['@']['name']] = $vendor;
1028              }
1029          }
1030      }
1031  /// Check we have the mysql vendor version
1032      if (empty($vendors['mysql'])) {
1033          $result->setStatus(false);
1034          $result->setErrorCode(NO_DATABASE_VENDOR_MYSQL_FOUND);
1035          return $result;
1036      }
1037  /// Check we have the postgres vendor version
1038      if (empty($vendors['postgres'])) {
1039          $result->setStatus(false);
1040          $result->setErrorCode(NO_DATABASE_VENDOR_POSTGRES_FOUND);
1041          return $result;
1042      }
1043  
1044  /// Now search the version we are using (depending of vendor)
1045      $current_vendor = $DB->get_dbvendor();
1046  
1047      $dbinfo = $DB->get_server_info();
1048      $current_version = normalize_version($dbinfo['version']);
1049      $needed_version = $vendors[$current_vendor];
1050  
1051  /// Check we have a needed version
1052      if (!$needed_version) {
1053          $result->setStatus(false);
1054          $result->setErrorCode(NO_DATABASE_VENDOR_VERSION_FOUND);
1055          return $result;
1056      }
1057  
1058      // Check if the DB Vendor has been properly configured.
1059      // Hack: this is required when playing with MySQL and MariaDB since they share the same PHP module and base DB classes,
1060      // whilst they are slowly evolving using separate directions though MariaDB is still an "almost" drop-in replacement.
1061      $dbvendorismysql = ($current_vendor === 'mysql');
1062      $dbtypeismariadb = (stripos($dbinfo['description'], 'mariadb') !== false);
1063      if ($dbvendorismysql && $dbtypeismariadb) {
1064          $result->setStatus(false);
1065          $result->setLevel($level);
1066          $result->setInfo($current_vendor . ' (' . $dbinfo['description'] . ')');
1067          $result->setFeedbackStr('environmentmariadbwrongdbtype');
1068          return $result;
1069      }
1070  
1071  /// And finally compare them, saving results
1072      if (version_compare($current_version, $needed_version, '>=')) {
1073          $result->setStatus(true);
1074      } else {
1075          $result->setStatus(false);
1076      }
1077      $result->setLevel($level);
1078      $result->setCurrentVersion($current_version);
1079      $result->setNeededVersion($needed_version);
1080      $result->setInfo($current_vendor . ' (' . $dbinfo['description'] . ')');
1081  
1082  /// Do any actions defined in the XML file.
1083      process_environment_result($vendorsxml[$current_vendor], $result);
1084  
1085      return $result;
1086  
1087  }
1088  
1089  /**
1090   * This function will post-process the result record by executing the specified
1091   * function, modifying it as necessary, also a custom message will be added
1092   * to the result object to be printed by the display layer.
1093   * Every bypass function must be defined in this file and it'll return
1094   * true/false to decide if the original test is bypassed or no. Also
1095   * such bypass functions are able to directly handling the result object
1096   * although it should be only under exceptional conditions.
1097   *
1098   * @param string xmldata containing the bypass data
1099   * @param object result object to be updated
1100   * @return void
1101   */
1102  function process_environment_bypass($xml, &$result) {
1103  
1104  /// Only try to bypass if we were in error and it was required
1105      if ($result->getStatus() || $result->getLevel() == 'optional') {
1106          return;
1107      }
1108  
1109  /// It there is bypass info (function and message)
1110      if (is_array($xml['#']) && isset($xml['#']['BYPASS'][0]['@']['function']) && isset($xml['#']['BYPASS'][0]['@']['message'])) {
1111          $function = $xml['#']['BYPASS'][0]['@']['function'];
1112          $message  = $xml['#']['BYPASS'][0]['@']['message'];
1113      /// Look for the function
1114          if (function_exists($function)) {
1115          /// Call it, and if bypass = true is returned, apply meesage
1116              if ($function($result)) {
1117              /// We only set the bypass message if the function itself hasn't defined it before
1118                  if (empty($result->getBypassStr)) {
1119                      if (isset($xml['#']['BYPASS'][0]['@']['plugin'])) {
1120                          $result->setBypassStr(array($message, $xml['#']['BYPASS'][0]['@']['plugin']));
1121                      } else {
1122                          $result->setBypassStr($message);
1123                      }
1124                  }
1125              }
1126          }
1127      }
1128  }
1129  
1130  /**
1131   * This function will post-process the result record by executing the specified
1132   * function, modifying it as necessary, also a custom message will be added
1133   * to the result object to be printed by the display layer.
1134   * Every restrict function must be defined in this file and it'll return
1135   * true/false to decide if the original test is restricted or no. Also
1136   * such restrict functions are able to directly handling the result object
1137   * although it should be only under exceptional conditions.
1138   *
1139   * @param string xmldata containing the restrict data
1140   * @param object result object to be updated
1141   * @return void
1142   */
1143  function process_environment_restrict($xml, &$result) {
1144  
1145  /// Only try to restrict if we were not in error and it was required
1146      if (!$result->getStatus() || $result->getLevel() == 'optional') {
1147          return;
1148      }
1149  /// It there is restrict info (function and message)
1150      if (is_array($xml['#']) && isset($xml['#']['RESTRICT'][0]['@']['function']) && isset($xml['#']['RESTRICT'][0]['@']['message'])) {
1151          $function = $xml['#']['RESTRICT'][0]['@']['function'];
1152          $message  = $xml['#']['RESTRICT'][0]['@']['message'];
1153      /// Look for the function
1154          if (function_exists($function)) {
1155          /// Call it, and if restrict = true is returned, apply meesage
1156              if ($function($result)) {
1157              /// We only set the restrict message if the function itself hasn't defined it before
1158                  if (empty($result->getRestrictStr)) {
1159                      if (isset($xml['#']['RESTRICT'][0]['@']['plugin'])) {
1160                          $result->setRestrictStr(array($message, $xml['#']['RESTRICT'][0]['@']['plugin']));
1161                      } else {
1162                          $result->setRestrictStr($message);
1163                      }
1164                  }
1165              }
1166          }
1167      }
1168  }
1169  
1170  /**
1171   * This function will detect if there is some message available to be added to the
1172   * result in order to clarify enviromental details.
1173   *
1174   * @uses INCORRECT_FEEDBACK_FOR_REQUIRED
1175   * @uses INCORRECT_FEEDBACK_FOR_OPTIONAL
1176   * @param string xmldata containing the feedback data
1177   * @param object reult object to be updated
1178   */
1179  function process_environment_messages($xml, &$result) {
1180  
1181  /// If there is feedback info
1182      if (is_array($xml['#']) && isset($xml['#']['FEEDBACK'][0]['#'])) {
1183          $feedbackxml = $xml['#']['FEEDBACK'][0]['#'];
1184  
1185          // Detect some incorrect feedback combinations.
1186          if ($result->getLevel() == 'required' and isset($feedbackxml['ON_CHECK'])) {
1187              $result->setStatus(false);
1188              $result->setErrorCode(INCORRECT_FEEDBACK_FOR_REQUIRED);
1189          } else if ($result->getLevel() == 'optional' and isset($feedbackxml['ON_ERROR'])) {
1190              $result->setStatus(false);
1191              $result->setErrorCode(INCORRECT_FEEDBACK_FOR_OPTIONAL);
1192          }
1193  
1194          if (!$result->status and $result->getLevel() == 'required') {
1195              if (isset($feedbackxml['ON_ERROR'][0]['@']['message'])) {
1196                  if (isset($feedbackxml['ON_ERROR'][0]['@']['plugin'])) {
1197                      $result->setFeedbackStr(array($feedbackxml['ON_ERROR'][0]['@']['message'], $feedbackxml['ON_ERROR'][0]['@']['plugin']));
1198                  } else {
1199                      $result->setFeedbackStr($feedbackxml['ON_ERROR'][0]['@']['message']);
1200                  }
1201              }
1202          } else if (!$result->status and $result->getLevel() == 'optional') {
1203              if (isset($feedbackxml['ON_CHECK'][0]['@']['message'])) {
1204                  if (isset($feedbackxml['ON_CHECK'][0]['@']['plugin'])) {
1205                      $result->setFeedbackStr(array($feedbackxml['ON_CHECK'][0]['@']['message'], $feedbackxml['ON_CHECK'][0]['@']['plugin']));
1206                  } else {
1207                      $result->setFeedbackStr($feedbackxml['ON_CHECK'][0]['@']['message']);
1208                  }
1209              }
1210          } else {
1211              if (isset($feedbackxml['ON_OK'][0]['@']['message'])) {
1212                  if (isset($feedbackxml['ON_OK'][0]['@']['plugin'])) {
1213                      $result->setFeedbackStr(array($feedbackxml['ON_OK'][0]['@']['message'], $feedbackxml['ON_OK'][0]['@']['plugin']));
1214                  } else {
1215                      $result->setFeedbackStr($feedbackxml['ON_OK'][0]['@']['message']);
1216                  }
1217              }
1218          }
1219      }
1220  }
1221  
1222  
1223  //--- Helper Class to return results to caller ---//
1224  
1225  
1226  /**
1227   * Helper Class to return results to caller
1228   *
1229   * @copyright 1999 onwards Martin Dougiamas  {@link http://moodle.com}
1230   * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
1231   * @package moodlecore
1232   */
1233  class environment_results {
1234      /**
1235       * @var string Which are we checking (database, php, php_extension, php_extension)
1236       */
1237      var $part;
1238      /**
1239       * @var bool true means the test passed and all is OK. false means it failed.
1240       */
1241      var $status;
1242      /**
1243       * @var integer See constants at the beginning of the file
1244       */
1245      var $error_code;
1246      /**
1247       * @var string required/optional
1248       */
1249      var $level;
1250      /**
1251       * @var string current version detected
1252       */
1253      var $current_version;
1254      /**
1255       * @var string version needed
1256       */
1257      var $needed_version;
1258      /**
1259       * @var string Aux. info (DB vendor, library...)
1260       */
1261      var $info;
1262      /**
1263       * @var string String to show on error|on check|on ok
1264       */
1265      var $feedback_str;
1266      /**
1267       * @var string String to show if some bypass has happened
1268       */
1269      var $bypass_str;
1270      /**
1271       * @var string String to show if some restrict has happened
1272       */
1273      var $restrict_str;
1274      /**
1275       * @var string|null full plugin name or null if main environment
1276       */
1277      var $plugin = null;
1278      /**
1279       * Constructor of the environment_result class. Just set default values
1280       *
1281       * @param string $part
1282       */
1283      public function __construct($part) {
1284          $this->part=$part;
1285          $this->status=false;
1286          $this->error_code=NO_ERROR;
1287          $this->level='required';
1288          $this->current_version='';
1289          $this->needed_version='';
1290          $this->info='';
1291          $this->feedback_str='';
1292          $this->bypass_str='';
1293          $this->restrict_str='';
1294      }
1295  
1296      /**
1297       * Old syntax of class constructor. Deprecated in PHP7.
1298       *
1299       * @deprecated since Moodle 3.1
1300       */
1301      public function environment_results($part) {
1302          debugging('Use of class name as constructor is deprecated', DEBUG_DEVELOPER);
1303          self::__construct($part);
1304      }
1305  
1306      /**
1307       * Set the status
1308       *
1309       * @param bool $testpassed true means the test passed and all is OK. false means it failed.
1310       */
1311      function setStatus($testpassed) {
1312          $this->status = $testpassed;
1313          if ($testpassed) {
1314              $this->setErrorCode(NO_ERROR);
1315          }
1316      }
1317  
1318      /**
1319       * Set the error_code
1320       *
1321       * @param integer $error_code the error code (see constants above)
1322       */
1323      function setErrorCode($error_code) {
1324          $this->error_code=$error_code;
1325      }
1326  
1327      /**
1328       * Set the level
1329       *
1330       * @param string $level the level (required, optional)
1331       */
1332      function setLevel($level) {
1333          $this->level=$level;
1334      }
1335  
1336      /**
1337       * Set the current version
1338       *
1339       * @param string $current_version the current version
1340       */
1341      function setCurrentVersion($current_version) {
1342          $this->current_version=$current_version;
1343      }
1344  
1345      /**
1346       * Set the needed version
1347       *
1348       * @param string $needed_version the needed version
1349       */
1350      function setNeededVersion($needed_version) {
1351          $this->needed_version=$needed_version;
1352      }
1353  
1354      /**
1355       * Set the auxiliary info
1356       *
1357       * @param string $info the auxiliary info
1358       */
1359      function setInfo($info) {
1360          $this->info=$info;
1361      }
1362  
1363      /**
1364       * Set the feedback string
1365       *
1366       * @param mixed $str the feedback string that will be fetched from the admin lang file.
1367       *                  pass just the string or pass an array of params for get_string
1368       *                  You always should put your string in admin.php but a third param is useful
1369       *                  to pass an $a object / string to get_string
1370       */
1371      function setFeedbackStr($str) {
1372          $this->feedback_str=$str;
1373      }
1374  
1375  
1376      /**
1377       * Set the bypass string
1378       *
1379       * @param string $str the bypass string that will be fetched from the admin lang file.
1380       *                  pass just the string or pass an array of params for get_string
1381       *                  You always should put your string in admin.php but a third param is useful
1382       *                  to pass an $a object / string to get_string
1383       */
1384      function setBypassStr($str) {
1385          $this->bypass_str=$str;
1386      }
1387  
1388      /**
1389       * Set the restrict string
1390       *
1391       * @param string $str the restrict string that will be fetched from the admin lang file.
1392       *                  pass just the string or pass an array of params for get_string
1393       *                  You always should put your string in admin.php but a third param is useful
1394       *                  to pass an $a object / string to get_string
1395       */
1396      function setRestrictStr($str) {
1397          $this->restrict_str=$str;
1398      }
1399  
1400      /**
1401       * Get the status
1402       *
1403       * @return bool true means the test passed and all is OK. false means it failed.
1404       */
1405      function getStatus() {
1406          return $this->status;
1407      }
1408  
1409      /**
1410       * Get the error code
1411       *
1412       * @return integer error code
1413       */
1414      function getErrorCode() {
1415          return $this->error_code;
1416      }
1417  
1418      /**
1419       * Get the level
1420       *
1421       * @return string level
1422       */
1423      function getLevel() {
1424          return $this->level;
1425      }
1426  
1427      /**
1428       * Get the current version
1429       *
1430       * @return string current version
1431       */
1432      function getCurrentVersion() {
1433          return $this->current_version;
1434      }
1435  
1436      /**
1437       * Get the needed version
1438       *
1439       * @return string needed version
1440       */
1441      function getNeededVersion() {
1442          return $this->needed_version;
1443      }
1444  
1445      /**
1446       * Get the aux info
1447       *
1448       * @return string info
1449       */
1450      function getInfo() {
1451          return $this->info;
1452      }
1453  
1454      /**
1455       * Get the part this result belongs to
1456       *
1457       * @return string part
1458       */
1459      function getPart() {
1460          return $this->part;
1461      }
1462  
1463      /**
1464       * Get the feedback string
1465       *
1466       * @return mixed feedback string (can be an array of params for get_string or a single string to fetch from
1467       *                  admin.php lang file).
1468       */
1469      function getFeedbackStr() {
1470          return $this->feedback_str;
1471      }
1472  
1473      /**
1474       * Get the bypass string
1475       *
1476       * @return mixed bypass string (can be an array of params for get_string or a single string to fetch from
1477       *                  admin.php lang file).
1478       */
1479      function getBypassStr() {
1480          return $this->bypass_str;
1481      }
1482  
1483      /**
1484       * Get the restrict string
1485       *
1486       * @return mixed restrict string (can be an array of params for get_string or a single string to fetch from
1487       *                  admin.php lang file).
1488       */
1489      function getRestrictStr() {
1490          return $this->restrict_str;
1491      }
1492  
1493      /**
1494       * @todo Document this function
1495       *
1496       * @param mixed $string params for get_string, either a string to fetch from admin.php or an array of
1497       *                       params for get_string.
1498       * @param string $class css class(es) for message.
1499       * @return string feedback string fetched from lang file wrapped in p tag with class $class or returns
1500       *                              empty string if $string is empty.
1501       */
1502      function strToReport($string, $class){
1503          if (!empty($string)){
1504              if (is_array($string)){
1505                  $str = call_user_func_array('get_string', $string);
1506              } else {
1507                  $str = get_string($string, 'admin');
1508              }
1509              return '<p class="'.$class.'">'.$str.'</p>';
1510          } else {
1511              return '';
1512          }
1513      }
1514  
1515      /**
1516       * Get plugin name.
1517       *
1518       * @return string plugin name
1519       */
1520      function getPluginName() {
1521          if ($this->plugin) {
1522              $manager = core_plugin_manager::instance();
1523              list($plugintype, $pluginname) = core_component::normalize_component($this->plugin);
1524              return $manager->plugintype_name($plugintype) . ' / ' . $manager->plugin_name($this->plugin);
1525          } else {
1526              return '';
1527          }
1528      }
1529  }
1530  
1531  /// Here all the restrict functions are coded to be used by the environment
1532  /// checker. All those functions will receive the result object and will
1533  /// return it modified as needed (status and bypass string)
1534  
1535  /**
1536   * @param array $element the element from the environment.xml file that should have
1537   *      either a level="required" or level="optional" attribute.
1538   * @return string "required" or "optional".
1539   */
1540  function get_level($element) {
1541      $level = 'required';
1542      if (isset($element['@']['level'])) {
1543          $level = $element['@']['level'];
1544          if (!in_array($level, array('required', 'optional'))) {
1545              debugging('The level of a check in the environment.xml file must be "required" or "optional".', DEBUG_DEVELOPER);
1546              $level = 'required';
1547          }
1548      } else {
1549          debugging('Checks in the environment.xml file must have a level="required" or level="optional" attribute.', DEBUG_DEVELOPER);
1550      }
1551      return $level;
1552  }
1553  
1554  /**
1555   * Once the result has been determined, look in the XML for any
1556   * messages, or other things that should be done depending on the outcome.
1557   *
1558   * @param array $element the element from the environment.xml file which
1559   *      may have children defining what should be done with the outcome.
1560   * @param object $result the result of the test, which may be modified by
1561   *      this function as specified in the XML.
1562   */
1563  function process_environment_result($element, &$result) {
1564  /// Process messages, modifying the $result if needed.
1565      process_environment_messages($element, $result);
1566  /// Process bypass, modifying $result if needed.
1567      process_environment_bypass($element, $result);
1568  /// Process restrict, modifying $result if needed.
1569      process_environment_restrict($element, $result);
1570  }
1571  
1572  /**
1573   * Check if the current PHP version is greater than or equal to
1574   * PHP version 7.
1575   *
1576   * @param object $result an environment_results instance
1577   * @return bool result of version check
1578   */
1579  function restrict_php_version_7(&$result) {
1580      return restrict_php_version($result, '7');
1581  }
1582  
1583  /**
1584   * Check if the current PHP version is greater than or equal to an
1585   * unsupported version.
1586   *
1587   * @param object $result an environment_results instance
1588   * @param string $version the version of PHP that can't be used
1589   * @return bool result of version check
1590   */
1591  function restrict_php_version(&$result, $version) {
1592  
1593      // Get the current PHP version.
1594      $currentversion = normalize_version(phpversion());
1595  
1596      // Confirm we're using a supported PHP version.
1597      if (version_compare($currentversion, $version, '<')) {
1598          // Everything is ok, the restriction doesn't apply.
1599          return false;
1600      } else {
1601          // We're using an unsupported PHP version, apply restriction.
1602          return true;
1603      }
1604  }
1605  
1606  /**
1607   * Check if the current PHP version is greater than or equal to
1608   * PHP version 7.1.
1609   *
1610   * @param object $result an environment_results instance
1611   * @return bool result of version check
1612   */
1613  function restrict_php_version_71(&$result) {
1614      return restrict_php_version($result, '7.1');
1615  }
1616  
1617  /**
1618   * Check if the current PHP version is greater than or equal to
1619   * PHP version 7.2.
1620   *
1621   * @param object $result an environment_results instance
1622   * @return bool result of version check
1623   */
1624  function restrict_php_version_72(&$result) {
1625      return restrict_php_version($result, '7.2');
1626  }
1627  
1628  /**
1629   * Check if the current PHP version is greater than or equal to
1630   * PHP version 7.3.
1631   *
1632   * @param object $result an environment_results instance
1633   * @return bool result of version check
1634   */
1635  function restrict_php_version_73(&$result) {
1636      return restrict_php_version($result, '7.3');
1637  }
1638  
1639  /**
1640   * Check if the current PHP version is greater than or equal to
1641   * PHP version 7.4.
1642   *
1643   * @param object $result an environment_results instance
1644   * @return bool result of version check
1645   */
1646  function restrict_php_version_74(&$result) {
1647      return restrict_php_version($result, '7.4');
1648  }
1649  
1650  /**
1651   * Check if the current PHP version is greater than or equal to
1652   * PHP version 8.0
1653   *
1654   * @param object $result an environment_results instance
1655   * @return bool result of version check
1656   */
1657  function restrict_php_version_80($result) {
1658      return restrict_php_version($result, '8.0');
1659  }
1660  
1661  /**
1662   * Check if the current PHP version is greater than or equal to
1663   * PHP version 8.1
1664   *
1665   * @param object $result an environment_results instance
1666   * @return bool result of version check
1667   */
1668  function restrict_php_version_81($result) {
1669      return restrict_php_version($result, '8.1');
1670  }
1671  
1672  /**
1673   * Check if the current PHP version is greater than or equal to
1674   * PHP version 8.2
1675   *
1676   * @param object $result an environment_results instance
1677   * @return bool result of version check
1678   */
1679  function restrict_php_version_82($result) {
1680      return restrict_php_version($result, '8.2');
1681  }