Ir a la última revisión | Autoría | Comparar con el anterior | Ultima modificación | Ver Log |
<?php// This file is part of Moodle - http://moodle.org///// Moodle is free software: you can redistribute it and/or modify// it under the terms of the GNU General Public License as published by// the Free Software Foundation, either version 3 of the License, or// (at your option) any later version.//// Moodle is distributed in the hope that it will be useful,// but WITHOUT ANY WARRANTY; without even the implied warranty of// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the// GNU General Public License for more details.//// You should have received a copy of the GNU General Public License// along with Moodle. If not, see <http://www.gnu.org/licenses/>./*** This library includes all the necessary stuff to execute some standard* tests of required versions and libraries to run Moodle. It can be* used from the admin interface, and both at install and upgrade.** All the info is stored in the admin/environment.xml file,* supporting to have an updated version in dataroot/environment** @copyright (C) 2001-3001 Eloy Lafuente (stronk7) {@link http://contiento.com}* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later* @package core* @subpackage admin*/defined('MOODLE_INTERNAL') || die();/// Add required files/*** Include the necessary*/require_once($CFG->libdir.'/xmlize.php');/// Define a bunch of XML processing errors/** XML Processing Error */define('NO_ERROR', 0);/** XML Processing Error */define('NO_VERSION_DATA_FOUND', 1);/** XML Processing Error */define('NO_DATABASE_SECTION_FOUND', 2);/** XML Processing Error */define('NO_DATABASE_VENDORS_FOUND', 3);/** XML Processing Error */define('NO_DATABASE_VENDOR_MYSQL_FOUND', 4);/** XML Processing Error */define('NO_DATABASE_VENDOR_POSTGRES_FOUND', 5);/** XML Processing Error */define('NO_PHP_SECTION_FOUND', 6);/** XML Processing Error */define('NO_PHP_VERSION_FOUND', 7);/** XML Processing Error */define('NO_PHP_EXTENSIONS_SECTION_FOUND', 8);/** XML Processing Error */define('NO_PHP_EXTENSIONS_NAME_FOUND', 9);/** XML Processing Error */define('NO_DATABASE_VENDOR_VERSION_FOUND', 10);/** XML Processing Error */define('NO_UNICODE_SECTION_FOUND', 11);/** XML Processing Error */define('NO_CUSTOM_CHECK_FOUND', 12);/** XML Processing Error */define('CUSTOM_CHECK_FILE_MISSING', 13);/** XML Processing Error */define('CUSTOM_CHECK_FUNCTION_MISSING', 14);/** XML Processing Error */define('NO_PHP_SETTINGS_NAME_FOUND', 15);/** XML Processing Error */define('INCORRECT_FEEDBACK_FOR_REQUIRED', 16);/** XML Processing Error */define('INCORRECT_FEEDBACK_FOR_OPTIONAL', 17);/// Define algorithm used to select the xml file/** To select the newer file available to perform checks */define('ENV_SELECT_NEWER', 0);/** To enforce the use of the file under dataroot */define('ENV_SELECT_DATAROOT', 1);/** To enforce the use of the file under admin (release) */define('ENV_SELECT_RELEASE', 2);/*** This function checks all the requirements defined in environment.xml.** @param string $version version to check.* @param int $env_select one of ENV_SELECT_NEWER | ENV_SELECT_DATAROOT | ENV_SELECT_RELEASE decide xml to use. Default ENV_SELECT_NEWER (BC)* @return array with two elements. The first element true/false, depending on* on whether the check passed. The second element is an array of environment_results* objects that has detailed information about the checks and which ones passed.*/function check_moodle_environment($version, $env_select = ENV_SELECT_NEWER) {if ($env_select != ENV_SELECT_NEWER and $env_select != ENV_SELECT_DATAROOT and $env_select != ENV_SELECT_RELEASE) {throw new coding_exception('Incorrect value of $env_select parameter');}/// Get the more recent version before the requestedif (!$version = get_latest_version_available($version, $env_select)) {return array(false, array());}/// Perform all the checksif (!$environment_results = environment_check($version, $env_select)) {return array(false, array());}/// Iterate over all the results looking for some error in required items/// or some error_code$result = true;foreach ($environment_results as $environment_result) {if (!$environment_result->getStatus() && $environment_result->getLevel() == 'required'&& !$environment_result->getBypassStr()) {$result = false; // required item that is not bypased} else if ($environment_result->getStatus() && $environment_result->getLevel() == 'required'&& $environment_result->getRestrictStr()) {$result = false; // required item that is restricted} else if ($environment_result->getErrorCode()) {$result = false;}}return array($result, $environment_results);}/*** Returns array of critical errors in plain text format* @param array $environment_results array of results gathered* @return array errors*/function environment_get_errors($environment_results) {global $CFG;$errors = array();// Iterate over each environment_resultforeach ($environment_results as $environment_result) {$type = $environment_result->getPart();$info = $environment_result->getInfo();$status = $environment_result->getStatus();$plugin = $environment_result->getPluginName();$error_code = $environment_result->getErrorCode();$a = new stdClass();if ($error_code) {$a->error_code = $error_code;$errors[] = array($info, get_string('environmentxmlerror', 'admin', $a));return $errors;}/// Calculate the status valueif ($environment_result->getBypassStr() != '') {// not interestingcontinue;} else if ($environment_result->getRestrictStr() != '') {// error} else {if ($status) {// okcontinue;} else {if ($environment_result->getLevel() == 'optional') {// just a warningcontinue;} else {// error}}}// We are comparing versions$rec = new stdClass();if ($rec->needed = $environment_result->getNeededVersion()) {$rec->current = $environment_result->getCurrentVersion();if ($environment_result->getLevel() == 'required') {$stringtouse = 'environmentrequireversion';} else {$stringtouse = 'environmentrecommendversion';}// We are checking installed & enabled things} else if ($environment_result->getPart() == 'custom_check') {if ($environment_result->getLevel() == 'required') {$stringtouse = 'environmentrequirecustomcheck';} else {$stringtouse = 'environmentrecommendcustomcheck';}} else if ($environment_result->getPart() == 'php_setting') {if ($status) {$stringtouse = 'environmentsettingok';} else if ($environment_result->getLevel() == 'required') {$stringtouse = 'environmentmustfixsetting';} else {$stringtouse = 'environmentshouldfixsetting';}} else {if ($environment_result->getLevel() == 'required') {$stringtouse = 'environmentrequireinstall';} else {$stringtouse = 'environmentrecommendinstall';}}$report = get_string($stringtouse, 'admin', $rec);// Here we'll store all the feedback found$feedbacktext = '';// Append the feedback if there is some$feedbacktext .= $environment_result->strToReport($environment_result->getFeedbackStr(), 'error');// Append the restrict if there is some$feedbacktext .= $environment_result->strToReport($environment_result->getRestrictStr(), 'error');if ($plugin === '') {$report = '[' . get_string('coresystem') . '] ' . $report;} else {$report = '[' . $plugin . '] ' . $report;}$report .= ' - ' . html_to_text($feedbacktext);if ($environment_result->getPart() == 'custom_check'){$errors[] = array($info, $report);} else {$errors[] = array(($info !== '' ? "$type $info" : $type), $report);}}return $errors;}/*** This function will normalize any version to just a serie of numbers* separated by dots. Everything else will be removed.** @param string $version the original version* @return string the normalized version*/function normalize_version($version) {/// 1.9 Beta 2 should be read 1.9 on enviromental checks, not 1.9.2/// we can discard everything after the first space$version = trim($version);$versionarr = explode(" ",$version);if (!empty($versionarr)) {$version = $versionarr[0];}/// Replace everything but numbers and dots by dots$version = preg_replace('/[^\.\d]/', '.', $version);/// Combine multiple dots in one$version = preg_replace('/(\.{2,})/', '.', $version);/// Trim possible leading and trailing dots$version = trim($version, '.');return $version;}/*** This function will load the environment.xml file and xmlize it** @staticvar array $data* @uses ENV_SELECT_NEWER* @uses ENV_SELECT_DATAROOT* @uses ENV_SELECT_RELEASE* @param int|string $env_select one of ENV_SELECT_NEWER | ENV_SELECT_DATAROOT | ENV_SELECT_RELEASE decide xml to use. String means plugin name.* @return mixed the xmlized structure or false on error*/function load_environment_xml($env_select=ENV_SELECT_NEWER) {global $CFG;static $data = array(); // Only load and xmlize once by request.if (isset($data[$env_select])) {return $data[$env_select];}$contents = false;if (is_numeric($env_select)) {$file = $CFG->dataroot.'/environment/environment.xml';$internalfile = $CFG->dirroot.'/'.$CFG->admin.'/environment.xml';switch ($env_select) {case ENV_SELECT_NEWER:if (!is_file($file) || !is_readable($file) || filemtime($file) < filemtime($internalfile) ||!$contents = file_get_contents($file)) {/// Fallback to fixed $CFG->admin/environment.xmlif (!is_file($internalfile) || !is_readable($internalfile) || !$contents = file_get_contents($internalfile)) {$contents = false;}}break;case ENV_SELECT_DATAROOT:if (!is_file($file) || !is_readable($file) || !$contents = file_get_contents($file)) {$contents = false;}break;case ENV_SELECT_RELEASE:if (!is_file($internalfile) || !is_readable($internalfile) || !$contents = file_get_contents($internalfile)) {$contents = false;}break;}} else {if ($plugindir = core_component::get_component_directory($env_select)) {$pluginfile = "$plugindir/environment.xml";if (!is_file($pluginfile) || !is_readable($pluginfile) || !$contents = file_get_contents($pluginfile)) {$contents = false;}}}// XML the whole file.if ($contents !== false) {$contents = xmlize($contents);}$data[$env_select] = $contents;return $data[$env_select];}/*** This function will return the list of Moodle versions available** @return array of versions*/function get_list_of_environment_versions($contents) {$versions = array();if (isset($contents['COMPATIBILITY_MATRIX']['#']['MOODLE'])) {foreach ($contents['COMPATIBILITY_MATRIX']['#']['MOODLE'] as $version) {$versions[] = $version['@']['version'];}}if (isset($contents['COMPATIBILITY_MATRIX']['#']['PLUGIN'])) {$versions[] = 'all';}return $versions;}/*** This function will return the most recent version in the environment.xml* file previous or equal to the version requested** @param string $version top version from which we start to look backwards* @param int $env_select one of ENV_SELECT_NEWER | ENV_SELECT_DATAROOT | ENV_SELECT_RELEASE decide xml to use.* @return string|bool string more recent version or false if not found*/function get_latest_version_available($version, $env_select) {if ($env_select != ENV_SELECT_NEWER and $env_select != ENV_SELECT_DATAROOT and $env_select != ENV_SELECT_RELEASE) {throw new coding_exception('Incorrect value of $env_select parameter');}/// Normalize the version requested$version = normalize_version($version);/// Load xml fileif (!$contents = load_environment_xml($env_select)) {return false;}/// Detect available versionsif (!$versions = get_list_of_environment_versions($contents)) {return false;}/// First we look for exact versionif (in_array($version, $versions, true)) {return $version;} else {$found_version = false;/// Not exact match, so we are going to iterate over the list searching/// for the latest version before the requested oneforeach ($versions as $arrversion) {if (version_compare($arrversion, $version, '<')) {$found_version = $arrversion;}}}return $found_version;}/*** This function will return the xmlized data belonging to one Moodle version** @param string $version top version from which we start to look backwards* @param int|string $env_select one of ENV_SELECT_NEWER | ENV_SELECT_DATAROOT | ENV_SELECT_RELEASE decide xml to use. String means plugin name.* @return mixed the xmlized structure or false on error*/function get_environment_for_version($version, $env_select) {/// Normalize the version requested$version = normalize_version($version);/// Load xml fileif (!$contents = load_environment_xml($env_select)) {return false;}/// Detect available versionsif (!$versions = get_list_of_environment_versions($contents)) {return false;}// If $env_select is not numeric then this is being called on a plugin, and not the core environment.xml// If a version of 'all' is in the arry is also means that the new <PLUGIN> tag was found, this should// be matched against any version of Moodle.if (!is_numeric($env_select) && in_array('all', $versions)&& environment_verify_plugin($env_select, $contents['COMPATIBILITY_MATRIX']['#']['PLUGIN'][0])) {return $contents['COMPATIBILITY_MATRIX']['#']['PLUGIN'][0];}/// If the version requested is availableif (!in_array($version, $versions, true)) {return false;}/// We now we have it. Extract from full contents.$fl_arr = array_flip($versions);return $contents['COMPATIBILITY_MATRIX']['#']['MOODLE'][$fl_arr[$version]];}/*** Checks if a plugin tag has a name attribute and it matches the plugin being tested.** @param string $plugin the name of the plugin.* @param array $pluginxml the xmlised structure for the plugin tag being tested.* @return boolean true if the name attribute exists and matches the plugin being tested.*/function environment_verify_plugin($plugin, $pluginxml) {if (!isset($pluginxml['@']['name']) || $pluginxml['@']['name'] != $plugin) {return false;}return true;}/*** This function will check for everything (DB, PHP and PHP extensions for now)* returning an array of environment_result objects.** @global object* @param string $version xml version we are going to use to test this server* @param int $env_select one of ENV_SELECT_NEWER | ENV_SELECT_DATAROOT | ENV_SELECT_RELEASE decide xml to use.* @return environment_results[] array of results encapsulated in one environment_result object*/function environment_check($version, $env_select) {global $CFG;if ($env_select != ENV_SELECT_NEWER and $env_select != ENV_SELECT_DATAROOT and $env_select != ENV_SELECT_RELEASE) {throw new coding_exception('Incorrect value of $env_select parameter');}/// Normalize the version requested$version = normalize_version($version);$results = array(); //To store all the results/// Only run the moodle versions checker on upgrade, not on installif (!empty($CFG->version)) {$results[] = environment_check_moodle($version, $env_select);}$results[] = environment_check_unicode($version, $env_select);$results[] = environment_check_database($version, $env_select);$results[] = environment_check_php($version, $env_select);if ($result = environment_check_pcre_unicode($version, $env_select)) {$results[] = $result;}$phpext_results = environment_check_php_extensions($version, $env_select);$results = array_merge($results, $phpext_results);$phpsetting_results = environment_check_php_settings($version, $env_select);$results = array_merge($results, $phpsetting_results);$custom_results = environment_custom_checks($version, $env_select);$results = array_merge($results, $custom_results);// Always use the plugin directory version of environment.xml,// add-on developers need to keep those up-to-date with future info.foreach (core_component::get_plugin_types() as $plugintype => $unused) {foreach (core_component::get_plugin_list_with_file($plugintype, 'environment.xml') as $pluginname => $unused) {$plugin = $plugintype . '_' . $pluginname;$result = environment_check_database($version, $plugin);if ($result->error_code != NO_VERSION_DATA_FOUNDand $result->error_code != NO_DATABASE_SECTION_FOUNDand $result->error_code != NO_DATABASE_VENDORS_FOUND) {$result->plugin = $plugin;$results[] = $result;}$result = environment_check_php($version, $plugin);if ($result->error_code != NO_VERSION_DATA_FOUNDand $result->error_code != NO_PHP_SECTION_FOUNDand $result->error_code != NO_PHP_VERSION_FOUND) {$result->plugin = $plugin;$results[] = $result;}$pluginresults = environment_check_php_extensions($version, $plugin);foreach ($pluginresults as $result) {if ($result->error_code != NO_VERSION_DATA_FOUNDand $result->error_code != NO_PHP_EXTENSIONS_SECTION_FOUND) {$result->plugin = $plugin;$results[] = $result;}}$pluginresults = environment_check_php_settings($version, $plugin);foreach ($pluginresults as $result) {if ($result->error_code != NO_VERSION_DATA_FOUND) {$result->plugin = $plugin;$results[] = $result;}}$pluginresults = environment_custom_checks($version, $plugin);foreach ($pluginresults as $result) {if ($result->error_code != NO_VERSION_DATA_FOUND) {$result->plugin = $plugin;$results[] = $result;}}}}return $results;}/*** This function will check if php extensions requirements are satisfied** @uses NO_VERSION_DATA_FOUND* @uses NO_PHP_EXTENSIONS_SECTION_FOUND* @uses NO_PHP_EXTENSIONS_NAME_FOUND* @param string $version xml version we are going to use to test this server* @param int|string $env_select one of ENV_SELECT_NEWER | ENV_SELECT_DATAROOT | ENV_SELECT_RELEASE decide xml to use. String means plugin name.* @return array array of results encapsulated in one environment_result object*/function environment_check_php_extensions($version, $env_select) {$results = array();/// Get the enviroment version we needif (!$data = get_environment_for_version($version, $env_select)) {/// Error. No version data found$result = new environment_results('php_extension');$result->setStatus(false);$result->setErrorCode(NO_VERSION_DATA_FOUND);return array($result);}/// Extract the php_extension partif (!isset($data['#']['PHP_EXTENSIONS']['0']['#']['PHP_EXTENSION'])) {/// Error. No PHP section found$result = new environment_results('php_extension');$result->setStatus(false);$result->setErrorCode(NO_PHP_EXTENSIONS_SECTION_FOUND);return array($result);}/// Iterate over extensions checking them and creating the needed environment_resultsforeach($data['#']['PHP_EXTENSIONS']['0']['#']['PHP_EXTENSION'] as $extension) {$result = new environment_results('php_extension');/// Check for level$level = get_level($extension);/// Check for extension nameif (!isset($extension['@']['name'])) {$result->setStatus(false);$result->setErrorCode(NO_PHP_EXTENSIONS_NAME_FOUND);} else {$extension_name = $extension['@']['name'];/// The name exists. Just check if it's an installed extensionif (!extension_loaded($extension_name)) {$result->setStatus(false);} else {$result->setStatus(true);}$result->setLevel($level);$result->setInfo($extension_name);}/// Do any actions defined in the XML file.process_environment_result($extension, $result);/// Add the result to the array of results$results[] = $result;}return $results;}/*** This function will check if php extensions requirements are satisfied** @uses NO_VERSION_DATA_FOUND* @uses NO_PHP_SETTINGS_NAME_FOUND* @param string $version xml version we are going to use to test this server* @param int|string $env_select one of ENV_SELECT_NEWER | ENV_SELECT_DATAROOT | ENV_SELECT_RELEASE decide xml to use. String means plugin name.* @return array array of results encapsulated in one environment_result object*/function environment_check_php_settings($version, $env_select) {$results = array();/// Get the enviroment version we needif (!$data = get_environment_for_version($version, $env_select)) {/// Error. No version data found$result = new environment_results('php_setting');$result->setStatus(false);$result->setErrorCode(NO_VERSION_DATA_FOUND);$results[] = $result;return $results;}/// Extract the php_setting partif (!isset($data['#']['PHP_SETTINGS']['0']['#']['PHP_SETTING'])) {/// No PHP section found - ignorereturn $results;}/// Iterate over settings checking them and creating the needed environment_resultsforeach($data['#']['PHP_SETTINGS']['0']['#']['PHP_SETTING'] as $setting) {$result = new environment_results('php_setting');/// Check for level$level = get_level($setting);$result->setLevel($level);/// Check for extension nameif (!isset($setting['@']['name'])) {$result->setStatus(false);$result->setErrorCode(NO_PHP_SETTINGS_NAME_FOUND);} else {$setting_name = $setting['@']['name'];$setting_value = $setting['@']['value'];$result->setInfo($setting_name);if ($setting_name == 'memory_limit') {$current = ini_get('memory_limit');if ($current == -1) {$result->setStatus(true);} else {$current = get_real_size($current);$minlimit = get_real_size($setting_value);if ($current < $minlimit) {@ini_set('memory_limit', $setting_value);$current = ini_get('memory_limit');$current = get_real_size($current);}$result->setStatus($current >= $minlimit);}} else {$current = ini_get_bool($setting_name);/// The name exists. Just check if it's an installed extensionif ($current == $setting_value) {$result->setStatus(true);} else {$result->setStatus(false);}}}/// Do any actions defined in the XML file.process_environment_result($setting, $result);/// Add the result to the array of results$results[] = $result;}return $results;}/*** This function will do the custom checks.** @uses CUSTOM_CHECK_FUNCTION_MISSING* @uses CUSTOM_CHECK_FILE_MISSING* @uses NO_CUSTOM_CHECK_FOUND* @param string $version xml version we are going to use to test this server.* @param int|string $env_select one of ENV_SELECT_NEWER | ENV_SELECT_DATAROOT | ENV_SELECT_RELEASE decide xml to use. String means plugin name.* @return array array of results encapsulated in environment_result objects.*/function environment_custom_checks($version, $env_select) {global $CFG;$results = array();/// Get current Moodle version (release) for later compare$release = isset($CFG->release) ? $CFG->release : $version; /// In case $CFG fails (at install) use $version$current_version = normalize_version($release);/// Get the enviroment version we needif (!$data = get_environment_for_version($version, $env_select)) {/// Error. No version data found - but this will already have been reported.return $results;}/// Extract the CUSTOM_CHECKS partif (!isset($data['#']['CUSTOM_CHECKS']['0']['#']['CUSTOM_CHECK'])) {/// No custom checks found - not a problemreturn $results;}/// Iterate over extensions checking them and creating the needed environment_resultsforeach($data['#']['CUSTOM_CHECKS']['0']['#']['CUSTOM_CHECK'] as $check) {$result = new environment_results('custom_check');/// Check for level$level = get_level($check);/// Check for extension nameif (isset($check['@']['function'])) {$function = $check['@']['function'];$file = null;if (isset($check['@']['file'])) {$file = $CFG->dirroot . '/' . $check['@']['file'];if (is_readable($file)) {include_once($file);}}if (is_callable($function)) {$result->setLevel($level);$result->setInfo($function);$result = call_user_func($function, $result);} else if (!$file or is_readable($file)) {/// Only show error for current version (where function MUST exist)/// else, we are performing custom checks against future versiosn/// and function MAY not exist, so it doesn't cause error, just skip/// custom check by returning null. MDL-15939if (version_compare($current_version, $version, '>=')) {$result->setStatus(false);$result->setInfo($function);$result->setErrorCode(CUSTOM_CHECK_FUNCTION_MISSING);} else {$result = null;}} else {/// Only show error for current version (where function MUST exist)/// else, we are performing custom checks against future versiosn/// and function MAY not exist, so it doesn't cause error, just skip/// custom check by returning null. MDL-15939if (version_compare($current_version, $version, '>=')) {$result->setStatus(false);$result->setInfo($function);$result->setErrorCode(CUSTOM_CHECK_FILE_MISSING);} else {$result = null;}}} else {$result->setStatus(false);$result->setErrorCode(NO_CUSTOM_CHECK_FOUND);}if (!is_null($result)) {/// Do any actions defined in the XML file.process_environment_result($check, $result);/// Add the result to the array of results$results[] = $result;}}return $results;}/*** This function will check if Moodle requirements are satisfied** @uses NO_VERSION_DATA_FOUND* @param string $version xml version we are going to use to test this server* @param int|string $env_select one of ENV_SELECT_NEWER | ENV_SELECT_DATAROOT | ENV_SELECT_RELEASE decide xml to use. String means plugin name.* @return object results encapsulated in one environment_result object*/function environment_check_moodle($version, $env_select) {$result = new environment_results('moodle');/// Get the enviroment version we needif (!$data = get_environment_for_version($version, $env_select)) {/// Error. No version data found$result->setStatus(false);$result->setErrorCode(NO_VERSION_DATA_FOUND);return $result;}/// Extract the moodle partif (!isset($data['@']['requires'])) {$needed_version = '1.0'; /// Default to 1.0 if no moodle requires is found} else {/// Extract required moodle version$needed_version = $data['@']['requires'];}/// Now search the version we are using$release = get_config('', 'release');$current_version = normalize_version($release);if (strpos($release, 'dev') !== false) {// When final version is required, dev is NOT enough so, at all effects// it's like we are running the previous version.$versionarr = explode('.', $current_version);if (isset($versionarr[1]) and $versionarr[1] > 0) {$versionarr[1]--;$current_version = implode('.', $versionarr);} else {$current_version = $current_version - 0.1;}}/// And finally compare them, saving resultsif (version_compare($current_version, $needed_version, '>=')) {$result->setStatus(true);} else {$result->setStatus(false);}$result->setLevel('required');$result->setCurrentVersion($release);$result->setNeededVersion($needed_version);return $result;}/*** This function will check if php requirements are satisfied** @uses NO_VERSION_DATA_FOUND* @uses NO_PHP_SECTION_FOUND* @uses NO_PHP_VERSION_FOUND* @param string $version xml version we are going to use to test this server* @param int|string $env_select one of ENV_SELECT_NEWER | ENV_SELECT_DATAROOT | ENV_SELECT_RELEASE decide xml to use. String means plugin name.* @return object results encapsulated in one environment_result object*/function environment_check_php($version, $env_select) {$result = new environment_results('php');/// Get the enviroment version we needif (!$data = get_environment_for_version($version, $env_select)) {/// Error. No version data found$result->setStatus(false);$result->setErrorCode(NO_VERSION_DATA_FOUND);return $result;}/// Extract the php partif (!isset($data['#']['PHP'])) {/// Error. No PHP section found$result->setStatus(false);$result->setErrorCode(NO_PHP_SECTION_FOUND);return $result;} else {/// Extract level and version$level = get_level($data['#']['PHP']['0']);if (!isset($data['#']['PHP']['0']['@']['version'])) {$result->setStatus(false);$result->setErrorCode(NO_PHP_VERSION_FOUND);return $result;} else {$needed_version = $data['#']['PHP']['0']['@']['version'];}}/// Now search the version we are using$current_version = normalize_version(phpversion());/// And finally compare them, saving resultsif (version_compare($current_version, $needed_version, '>=')) {$result->setStatus(true);} else {$result->setStatus(false);}$result->setLevel($level);$result->setCurrentVersion($current_version);$result->setNeededVersion($needed_version);/// Do any actions defined in the XML file.process_environment_result($data['#']['PHP'][0], $result);return $result;}/*** Looks for buggy PCRE implementation, we need unicode support in Moodle...* @param string $version xml version we are going to use to test this server* @param int|string $env_select one of ENV_SELECT_NEWER | ENV_SELECT_DATAROOT | ENV_SELECT_RELEASE decide xml to use. String means plugin name.* @return ?environment_results results encapsulated in one environment_result object, null if irrelevant*/function environment_check_pcre_unicode($version, $env_select) {$result = new environment_results('pcreunicode');// Get the environment version we needif (!$data = get_environment_for_version($version, $env_select)) {// Error. No version data found!$result->setStatus(false);$result->setErrorCode(NO_VERSION_DATA_FOUND);return $result;}if (!isset($data['#']['PCREUNICODE'])) {return null;}$level = get_level($data['#']['PCREUNICODE']['0']);$result->setLevel($level);if (!function_exists('preg_match')) {// The extension test fails instead.return null;} else if (@preg_match('/\pL/u', 'a') and @preg_match('/á/iu', 'Á')) {$result->setStatus(true);} else {$result->setStatus(false);}// Do any actions defined in the XML file.process_environment_result($data['#']['PCREUNICODE'][0], $result);return $result;}/*** This function will check if unicode database requirements are satisfied** @uses NO_VERSION_DATA_FOUND* @uses NO_UNICODE_SECTION_FOUND* @param string $version xml version we are going to use to test this server* @param int|string $env_select one of ENV_SELECT_NEWER | ENV_SELECT_DATAROOT | ENV_SELECT_RELEASE decide xml to use. String means plugin name.* @return object results encapsulated in one environment_result object*/function environment_check_unicode($version, $env_select) {global $DB;$result = new environment_results('unicode');/// Get the enviroment version we needif (!$data = get_environment_for_version($version, $env_select)) {/// Error. No version data found$result->setStatus(false);$result->setErrorCode(NO_VERSION_DATA_FOUND);return $result;}/// Extract the unicode partif (!isset($data['#']['UNICODE'])) {/// Error. No UNICODE section found$result->setStatus(false);$result->setErrorCode(NO_UNICODE_SECTION_FOUND);return $result;} else {/// Extract level$level = get_level($data['#']['UNICODE']['0']);}if (!$unicodedb = $DB->setup_is_unicodedb()) {$result->setStatus(false);} else {$result->setStatus(true);}$result->setLevel($level);/// Do any actions defined in the XML file.process_environment_result($data['#']['UNICODE'][0], $result);return $result;}/*** This function will check if database requirements are satisfied** @uses NO_VERSION_DATA_FOUND* @uses NO_DATABASE_SECTION_FOUND* @uses NO_DATABASE_VENDORS_FOUND* @uses NO_DATABASE_VENDOR_MYSQL_FOUND* @uses NO_DATABASE_VENDOR_POSTGRES_FOUND* @uses NO_DATABASE_VENDOR_VERSION_FOUND* @param string $version xml version we are going to use to test this server* @param int|string $env_select one of ENV_SELECT_NEWER | ENV_SELECT_DATAROOT | ENV_SELECT_RELEASE decide xml to use. String means plugin name.* @return object results encapsulated in one environment_result object*/function environment_check_database($version, $env_select) {global $DB;$result = new environment_results('database');$vendors = array(); //Array of vendors in version/// Get the enviroment version we needif (!$data = get_environment_for_version($version, $env_select)) {/// Error. No version data found$result->setStatus(false);$result->setErrorCode(NO_VERSION_DATA_FOUND);return $result;}/// Extract the database partif (!isset($data['#']['DATABASE'])) {/// Error. No DATABASE section found$result->setStatus(false);$result->setErrorCode(NO_DATABASE_SECTION_FOUND);return $result;} else {/// Extract level$level = get_level($data['#']['DATABASE']['0']);}/// Extract DB vendors. At least 2 are mandatory (mysql & postgres)if (!isset($data['#']['DATABASE']['0']['#']['VENDOR'])) {/// Error. No VENDORS found$result->setStatus(false);$result->setErrorCode(NO_DATABASE_VENDORS_FOUND);return $result;} else {/// Extract vendorsforeach ($data['#']['DATABASE']['0']['#']['VENDOR'] as $vendor) {if (isset($vendor['@']['name']) && isset($vendor['@']['version'])) {$vendors[$vendor['@']['name']] = $vendor['@']['version'];$vendorsxml[$vendor['@']['name']] = $vendor;}}}/// Check we have the mysql vendor versionif (empty($vendors['mysql'])) {$result->setStatus(false);$result->setErrorCode(NO_DATABASE_VENDOR_MYSQL_FOUND);return $result;}/// Check we have the postgres vendor versionif (empty($vendors['postgres'])) {$result->setStatus(false);$result->setErrorCode(NO_DATABASE_VENDOR_POSTGRES_FOUND);return $result;}/// Now search the version we are using (depending of vendor)$current_vendor = $DB->get_dbvendor();$dbinfo = $DB->get_server_info();$current_version = normalize_version($dbinfo['version']);$needed_version = $vendors[$current_vendor];/// Check we have a needed versionif (!$needed_version) {$result->setStatus(false);$result->setErrorCode(NO_DATABASE_VENDOR_VERSION_FOUND);return $result;}// Check if the DB Vendor has been properly configured.// Hack: this is required when playing with MySQL and MariaDB since they share the same PHP module and base DB classes,// whilst they are slowly evolving using separate directions though MariaDB is still an "almost" drop-in replacement.$dbvendorismysql = ($current_vendor === 'mysql');$dbtypeismariadb = (stripos($dbinfo['description'], 'mariadb') !== false);if ($dbvendorismysql && $dbtypeismariadb) {$result->setStatus(false);$result->setLevel($level);$result->setInfo($current_vendor . ' (' . $dbinfo['description'] . ')');$result->setFeedbackStr('environmentmariadbwrongdbtype');return $result;}/// And finally compare them, saving resultsif (version_compare($current_version, $needed_version, '>=')) {$result->setStatus(true);} else {$result->setStatus(false);}$result->setLevel($level);$result->setCurrentVersion($current_version);$result->setNeededVersion($needed_version);$result->setInfo($current_vendor . ' (' . $dbinfo['description'] . ')');/// Do any actions defined in the XML file.process_environment_result($vendorsxml[$current_vendor], $result);return $result;}/*** This function will post-process the result record by executing the specified* function, modifying it as necessary, also a custom message will be added* to the result object to be printed by the display layer.* Every bypass function must be defined in this file and it'll return* true/false to decide if the original test is bypassed or no. Also* such bypass functions are able to directly handling the result object* although it should be only under exceptional conditions.** @param array $xml xml containing the bypass data* @param environment_results $result object to be updated* @return void*/function process_environment_bypass($xml, &$result) {/// Only try to bypass if we were in error and it was requiredif ($result->getStatus() || $result->getLevel() == 'optional') {return;}/// It there is bypass info (function and message)if (is_array($xml['#']) && isset($xml['#']['BYPASS'][0]['@']['function']) && isset($xml['#']['BYPASS'][0]['@']['message'])) {$function = $xml['#']['BYPASS'][0]['@']['function'];$message = $xml['#']['BYPASS'][0]['@']['message'];/// Look for the functionif (function_exists($function)) {/// Call it, and if bypass = true is returned, apply meesageif ($function($result)) {/// We only set the bypass message if the function itself hasn't defined it beforeif (empty($result->getBypassStr)) {if (isset($xml['#']['BYPASS'][0]['@']['plugin'])) {$result->setBypassStr(array($message, $xml['#']['BYPASS'][0]['@']['plugin']));} else {$result->setBypassStr($message);}}}}}}/*** This function will post-process the result record by executing the specified* function, modifying it as necessary, also a custom message will be added* to the result object to be printed by the display layer.* Every restrict function must be defined in this file and it'll return* true/false to decide if the original test is restricted or no. Also* such restrict functions are able to directly handling the result object* although it should be only under exceptional conditions.** @param array $xml xmldata containing the restrict data* @param environment_results $result object to be updated* @return void*/function process_environment_restrict($xml, &$result) {/// Only try to restrict if we were not in error and it was requiredif (!$result->getStatus() || $result->getLevel() == 'optional') {return;}/// It there is restrict info (function and message)if (is_array($xml['#']) && isset($xml['#']['RESTRICT'][0]['@']['function']) && isset($xml['#']['RESTRICT'][0]['@']['message'])) {$function = $xml['#']['RESTRICT'][0]['@']['function'];$message = $xml['#']['RESTRICT'][0]['@']['message'];/// Look for the functionif (function_exists($function)) {/// Call it, and if restrict = true is returned, apply meesageif ($function($result)) {/// We only set the restrict message if the function itself hasn't defined it beforeif (empty($result->getRestrictStr)) {if (isset($xml['#']['RESTRICT'][0]['@']['plugin'])) {$result->setRestrictStr(array($message, $xml['#']['RESTRICT'][0]['@']['plugin']));} else {$result->setRestrictStr($message);}}}}}}/*** This function will detect if there is some message available to be added to the* result in order to clarify enviromental details.** @uses INCORRECT_FEEDBACK_FOR_REQUIRED* @uses INCORRECT_FEEDBACK_FOR_OPTIONAL* @param array $xml xmldata containing the feedback data* @param environment_results $result object to be updated*/function process_environment_messages($xml, &$result) {/// If there is feedback infoif (is_array($xml['#']) && isset($xml['#']['FEEDBACK'][0]['#'])) {$feedbackxml = $xml['#']['FEEDBACK'][0]['#'];// Detect some incorrect feedback combinations.if ($result->getLevel() == 'required' and isset($feedbackxml['ON_CHECK'])) {$result->setStatus(false);$result->setErrorCode(INCORRECT_FEEDBACK_FOR_REQUIRED);} else if ($result->getLevel() == 'optional' and isset($feedbackxml['ON_ERROR'])) {$result->setStatus(false);$result->setErrorCode(INCORRECT_FEEDBACK_FOR_OPTIONAL);}if (!$result->status and $result->getLevel() == 'required') {if (isset($feedbackxml['ON_ERROR'][0]['@']['message'])) {if (isset($feedbackxml['ON_ERROR'][0]['@']['plugin'])) {$result->setFeedbackStr(array($feedbackxml['ON_ERROR'][0]['@']['message'], $feedbackxml['ON_ERROR'][0]['@']['plugin']));} else {$result->setFeedbackStr($feedbackxml['ON_ERROR'][0]['@']['message']);}}} else if (!$result->status and $result->getLevel() == 'optional') {if (isset($feedbackxml['ON_CHECK'][0]['@']['message'])) {if (isset($feedbackxml['ON_CHECK'][0]['@']['plugin'])) {$result->setFeedbackStr(array($feedbackxml['ON_CHECK'][0]['@']['message'], $feedbackxml['ON_CHECK'][0]['@']['plugin']));} else {$result->setFeedbackStr($feedbackxml['ON_CHECK'][0]['@']['message']);}}} else {if (isset($feedbackxml['ON_OK'][0]['@']['message'])) {if (isset($feedbackxml['ON_OK'][0]['@']['plugin'])) {$result->setFeedbackStr(array($feedbackxml['ON_OK'][0]['@']['message'], $feedbackxml['ON_OK'][0]['@']['plugin']));} else {$result->setFeedbackStr($feedbackxml['ON_OK'][0]['@']['message']);}}}}}//--- Helper Class to return results to caller ---///*** Helper Class to return results to caller** @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com}* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later* @package moodlecore*/class environment_results {/*** @var string Which are we checking (database, php, php_extension, php_extension)*/var $part;/*** @var bool true means the test passed and all is OK. false means it failed.*/var $status;/*** @var integer See constants at the beginning of the file*/var $error_code;/*** @var string required/optional/recommended.*/var $level;/*** @var string current version detected*/var $current_version;/*** @var string version needed*/var $needed_version;/*** @var string Aux. info (DB vendor, library...)*/var $info;/*** @var string String to show on error|on check|on ok*/var $feedback_str;/*** @var string String to show if some bypass has happened*/var $bypass_str;/*** @var string String to show if some restrict has happened*/var $restrict_str;/*** @var string|null full plugin name or null if main environment*/var $plugin = null;/*** Constructor of the environment_result class. Just set default values** @param string $part*/public function __construct($part) {$this->part=$part;$this->status=false;$this->error_code=NO_ERROR;$this->level='required';$this->current_version='';$this->needed_version='';$this->info='';$this->feedback_str='';$this->bypass_str='';$this->restrict_str='';}/*** Old syntax of class constructor. Deprecated in PHP7.** @deprecated since Moodle 3.1*/public function environment_results($part) {debugging('Use of class name as constructor is deprecated', DEBUG_DEVELOPER);self::__construct($part);}/*** Set the status** @param bool $testpassed true means the test passed and all is OK. false means it failed.*/function setStatus($testpassed) {$this->status = $testpassed;if ($testpassed) {$this->setErrorCode(NO_ERROR);}}/*** Set the error_code** @param integer $error_code the error code (see constants above)*/function setErrorCode($error_code) {$this->error_code=$error_code;}/*** Set the level** @param string $level the level (required, optional)*/function setLevel($level) {$this->level=$level;}/*** Set the current version** @param string $current_version the current version*/function setCurrentVersion($current_version) {$this->current_version=$current_version;}/*** Set the needed version** @param string $needed_version the needed version*/function setNeededVersion($needed_version) {$this->needed_version=$needed_version;}/*** Set the auxiliary info** @param string $info the auxiliary info*/function setInfo($info) {$this->info=$info;}/*** Set the feedback string** @param mixed $str the feedback string that will be fetched from the admin lang file.* pass just the string or pass an array of params for get_string* You always should put your string in admin.php but a third param is useful* to pass an $a object / string to get_string*/function setFeedbackStr($str) {$this->feedback_str=$str;}/*** Set the bypass string** @param string $str the bypass string that will be fetched from the admin lang file.* pass just the string or pass an array of params for get_string* You always should put your string in admin.php but a third param is useful* to pass an $a object / string to get_string*/function setBypassStr($str) {$this->bypass_str=$str;}/*** Set the restrict string** @param string $str the restrict string that will be fetched from the admin lang file.* pass just the string or pass an array of params for get_string* You always should put your string in admin.php but a third param is useful* to pass an $a object / string to get_string*/function setRestrictStr($str) {$this->restrict_str=$str;}/*** Get the status** @return bool true means the test passed and all is OK. false means it failed.*/function getStatus() {return $this->status;}/*** Get the error code** @return integer error code*/function getErrorCode() {return $this->error_code;}/*** Get the level** @return string level*/function getLevel() {return $this->level;}/*** Get the current version** @return string current version*/function getCurrentVersion() {return $this->current_version;}/*** Get the needed version** @return string needed version*/function getNeededVersion() {return $this->needed_version;}/*** Get the aux info** @return string info*/function getInfo() {return $this->info;}/*** Get the part this result belongs to** @return string part*/function getPart() {return $this->part;}/*** Get the feedback string** @return mixed feedback string (can be an array of params for get_string or a single string to fetch from* admin.php lang file).*/function getFeedbackStr() {return $this->feedback_str;}/*** Get the bypass string** @return mixed bypass string (can be an array of params for get_string or a single string to fetch from* admin.php lang file).*/function getBypassStr() {return $this->bypass_str;}/*** Get the restrict string** @return mixed restrict string (can be an array of params for get_string or a single string to fetch from* admin.php lang file).*/function getRestrictStr() {return $this->restrict_str;}/*** @todo Document this function** @param mixed $string params for get_string, either a string to fetch from admin.php or an array of* params for get_string.* @param string $class css class(es) for message.* @return string feedback string fetched from lang file wrapped in p tag with class $class or returns* empty string if $string is empty.*/function strToReport($string, $class){if (!empty($string)){if (is_array($string)){$str = call_user_func_array('get_string', $string);} else {$str = get_string($string, 'admin');}return '<p class="'.$class.'">'.$str.'</p>';} else {return '';}}/*** Get plugin name.** @return string plugin name*/function getPluginName() {if ($this->plugin) {$manager = core_plugin_manager::instance();list($plugintype, $pluginname) = core_component::normalize_component($this->plugin);return $manager->plugintype_name($plugintype) . ' / ' . $manager->plugin_name($this->plugin);} else {return '';}}}/// Here all the restrict functions are coded to be used by the environment/// checker. All those functions will receive the result object and will/// return it modified as needed (status and bypass string)/*** @param array $element the element from the environment.xml file that should have* either a level="required" or level="optional" attribute.* @return string "required" or "optional".*/function get_level($element) {$level = 'required';if (isset($element['@']['level'])) {$level = $element['@']['level'];if (!in_array($level, ['required', 'optional', 'recommended'])) {debugging('The level of a check in the environment.xml file must be "required", "optional" or "recommended".',DEBUG_DEVELOPER);$level = 'required';}} else {debugging('Checks in the environment.xml file must have a level="required" or level="optional" attribute.', DEBUG_DEVELOPER);}return $level;}/*** Once the result has been determined, look in the XML for any* messages, or other things that should be done depending on the outcome.** @param array $element the element from the environment.xml file which* may have children defining what should be done with the outcome.* @param object $result the result of the test, which may be modified by* this function as specified in the XML.*/function process_environment_result($element, &$result) {/// Process messages, modifying the $result if needed.process_environment_messages($element, $result);/// Process bypass, modifying $result if needed.process_environment_bypass($element, $result);/// Process restrict, modifying $result if needed.process_environment_restrict($element, $result);}/*** Check if the current PHP version is greater than or equal to* PHP version 7.** @param object $result an environment_results instance* @return bool result of version check*/function restrict_php_version_7(&$result) {return restrict_php_version($result, '7');}/*** Check if the current PHP version is greater than or equal to an* unsupported version.** @param object $result an environment_results instance* @param string $version the version of PHP that can't be used* @return bool result of version check*/function restrict_php_version(&$result, $version) {// Get the current PHP version.$currentversion = normalize_version(phpversion());// Confirm we're using a supported PHP version.if (version_compare($currentversion, $version, '<')) {// Everything is ok, the restriction doesn't apply.return false;} else {// We're using an unsupported PHP version, apply restriction.return true;}}/*** Check if the current PHP version is greater than or equal to* PHP version 7.1.** @param object $result an environment_results instance* @return bool result of version check*/function restrict_php_version_71(&$result) {return restrict_php_version($result, '7.1');}/*** Check if the current PHP version is greater than or equal to* PHP version 7.2.** @param object $result an environment_results instance* @return bool result of version check*/function restrict_php_version_72(&$result) {return restrict_php_version($result, '7.2');}/*** Check if the current PHP version is greater than or equal to* PHP version 7.3.** @param object $result an environment_results instance* @return bool result of version check*/function restrict_php_version_73(&$result) {return restrict_php_version($result, '7.3');}/*** Check if the current PHP version is greater than or equal to* PHP version 7.4.** @param object $result an environment_results instance* @return bool result of version check*/function restrict_php_version_74(&$result) {return restrict_php_version($result, '7.4');}/*** Check if the current PHP version is greater than or equal to* PHP version 8.0** @param object $result an environment_results instance* @return bool result of version check*/function restrict_php_version_80($result) {return restrict_php_version($result, '8.0');}/*** Check if the current PHP version is greater than or equal to* PHP version 8.1** @param object $result an environment_results instance* @return bool result of version check*/function restrict_php_version_81($result) {return restrict_php_version($result, '8.1');}/*** Check if the current PHP version is greater than or equal to* PHP version 8.2** @param object $result an environment_results instance* @return bool result of version check*/function restrict_php_version_82($result) {return restrict_php_version($result, '8.2');}/*** Check if the current PHP version is greater than or equal to* PHP version 8.3** @param object $result an environment_results instance* @return bool result of version check*/function restrict_php_version_83($result) {return restrict_php_version($result, '8.3');}