MDL-76441 env: Moodle 4.2.x and 4.3.x do not support PHP 8.3
[moodle.git] / lib / environmentlib.php
blob4d82c2ca49ad99faa133e70a07950c00083ec5a7
1 <?php
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.
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/>.
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.
23 * All the info is stored in the admin/environment.xml file,
24 * supporting to have an updated version in dataroot/environment
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
32 defined('MOODLE_INTERNAL') || die();
34 /// Add required files
35 /**
36 * Include the necessary
38 require_once($CFG->libdir.'/xmlize.php');
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);
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);
86 /**
87 * This function checks all the requirements defined in environment.xml.
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.
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');
100 /// Get the more recent version before the requested
101 if (!$version = get_latest_version_available($version, $env_select)) {
102 return array(false, array());
105 /// Perform all the checks
106 if (!$environment_results = environment_check($version, $env_select)) {
107 return array(false, array());
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;
125 return array($result, $environment_results);
130 * Returns array of critical errors in plain text format
131 * @param array $environment_results array of results gathered
132 * @return array errors
134 function environment_get_errors($environment_results) {
135 global $CFG;
136 $errors = array();
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();
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;
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
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';
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';
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';
197 } else {
198 if ($environment_result->getLevel() == 'required') {
199 $stringtouse = 'environmentrequireinstall';
200 } else {
201 $stringtouse = 'environmentrecommendinstall';
204 $report = get_string($stringtouse, 'admin', $rec);
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');
213 if ($plugin === '') {
214 $report = '[' . get_string('coresystem') . '] ' . $report;
215 } else {
216 $report = '[' . $plugin . '] ' . $report;
219 $report .= ' - ' . html_to_text($feedbacktext);
221 if ($environment_result->getPart() == 'custom_check'){
222 $errors[] = array($info, $report);
223 } else {
224 $errors[] = array(($info !== '' ? "$type $info" : $type), $report);
228 return $errors;
233 * This function will normalize any version to just a serie of numbers
234 * separated by dots. Everything else will be removed.
236 * @param string $version the original version
237 * @return string the normalized version
239 function normalize_version($version) {
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];
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, '.');
255 return $version;
260 * This function will load the environment.xml file and xmlize it
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
269 function load_environment_xml($env_select=ENV_SELECT_NEWER) {
271 global $CFG;
273 static $data = array(); // Only load and xmlize once by request.
275 if (isset($data[$env_select])) {
276 return $data[$env_select];
278 $contents = false;
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;
292 break;
293 case ENV_SELECT_DATAROOT:
294 if (!is_file($file) || !is_readable($file) || !$contents = file_get_contents($file)) {
295 $contents = false;
297 break;
298 case ENV_SELECT_RELEASE:
299 if (!is_file($internalfile) || !is_readable($internalfile) || !$contents = file_get_contents($internalfile)) {
300 $contents = false;
302 break;
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;
312 // XML the whole file.
313 if ($contents !== false) {
314 $contents = xmlize($contents);
317 $data[$env_select] = $contents;
319 return $data[$env_select];
324 * This function will return the list of Moodle versions available
326 * @return array of versions
328 function get_list_of_environment_versions($contents) {
329 $versions = array();
331 if (isset($contents['COMPATIBILITY_MATRIX']['#']['MOODLE'])) {
332 foreach ($contents['COMPATIBILITY_MATRIX']['#']['MOODLE'] as $version) {
333 $versions[] = $version['@']['version'];
337 if (isset($contents['COMPATIBILITY_MATRIX']['#']['PLUGIN'])) {
338 $versions[] = 'all';
341 return $versions;
346 * This function will return the most recent version in the environment.xml
347 * file previous or equal to the version requested
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
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');
358 /// Normalize the version requested
359 $version = normalize_version($version);
361 /// Load xml file
362 if (!$contents = load_environment_xml($env_select)) {
363 return false;
366 /// Detect available versions
367 if (!$versions = get_list_of_environment_versions($contents)) {
368 return false;
370 /// First we look for exact version
371 if (in_array($version, $versions, true)) {
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;
384 return $found_version;
389 * This function will return the xmlized data belonging to one Moodle version
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
395 function get_environment_for_version($version, $env_select) {
397 /// Normalize the version requested
398 $version = normalize_version($version);
400 /// Load xml file
401 if (!$contents = load_environment_xml($env_select)) {
402 return false;
405 /// Detect available versions
406 if (!$versions = get_list_of_environment_versions($contents)) {
407 return false;
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];
418 /// If the version requested is available
419 if (!in_array($version, $versions, true)) {
420 return false;
423 /// We now we have it. Extract from full contents.
424 $fl_arr = array_flip($versions);
426 return $contents['COMPATIBILITY_MATRIX']['#']['MOODLE'][$fl_arr[$version]];
430 * Checks if a plugin tag has a name attribute and it matches the plugin being tested.
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.
436 function environment_verify_plugin($plugin, $pluginxml) {
437 if (!isset($pluginxml['@']['name']) || $pluginxml['@']['name'] != $plugin) {
438 return false;
440 return true;
444 * This function will check for everything (DB, PHP and PHP extensions for now)
445 * returning an array of environment_result objects.
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
452 function environment_check($version, $env_select) {
453 global $CFG;
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');
459 /// Normalize the version requested
460 $version = normalize_version($version);
462 $results = array(); //To store all the results
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);
468 $results[] = environment_check_unicode($version, $env_select);
469 $results[] = environment_check_database($version, $env_select);
470 $results[] = environment_check_php($version, $env_select);
472 if ($result = environment_check_pcre_unicode($version, $env_select)) {
473 $results[] = $result;
476 $phpext_results = environment_check_php_extensions($version, $env_select);
477 $results = array_merge($results, $phpext_results);
479 $phpsetting_results = environment_check_php_settings($version, $env_select);
480 $results = array_merge($results, $phpsetting_results);
482 $custom_results = environment_custom_checks($version, $env_select);
483 $results = array_merge($results, $custom_results);
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;
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) {
496 $result->plugin = $plugin;
497 $results[] = $result;
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) {
505 $result->plugin = $plugin;
506 $results[] = $result;
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) {
514 $result->plugin = $plugin;
515 $results[] = $result;
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;
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;
537 return $results;
542 * This function will check if php extensions requirements are satisfied
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
551 function environment_check_php_extensions($version, $env_select) {
553 $results = array();
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);
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);
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);
589 $result->setLevel($level);
590 $result->setInfo($extension_name);
593 /// Do any actions defined in the XML file.
594 process_environment_result($extension, $result);
596 /// Add the result to the array of results
597 $results[] = $result;
601 return $results;
605 * This function will check if php extensions requirements are satisfied
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
613 function environment_check_php_settings($version, $env_select) {
615 $results = array();
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;
627 /// Extract the php_setting part
628 if (!isset($data['#']['PHP_SETTINGS']['0']['#']['PHP_SETTING'])) {
629 /// No PHP section found - ignore
630 return $results;
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);
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);
659 $result->setStatus($current >= $minlimit);
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);
673 /// Do any actions defined in the XML file.
674 process_environment_result($setting, $result);
676 /// Add the result to the array of results
677 $results[] = $result;
681 return $results;
685 * This function will do the custom checks.
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.
694 function environment_custom_checks($version, $env_select) {
695 global $CFG;
697 $results = array();
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);
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;
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;
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');
719 /// Check for level
720 $level = get_level($check);
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);
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;
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;
762 } else {
763 $result->setStatus(false);
764 $result->setErrorCode(NO_CUSTOM_CHECK_FOUND);
767 if (!is_null($result)) {
768 /// Do any actions defined in the XML file.
769 process_environment_result($check, $result);
771 /// Add the result to the array of results
772 $results[] = $result;
776 return $results;
780 * This function will check if Moodle requirements are satisfied
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
787 function environment_check_moodle($version, $env_select) {
789 $result = new environment_results('moodle');
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;
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'];
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 so, at all effects
812 // it's like we are running the previous version.
813 $versionarr = explode('.', $current_version);
814 if (isset($versionarr[1]) and $versionarr[1] > 0) {
815 $versionarr[1]--;
816 $current_version = implode('.', $versionarr);
817 } else {
818 $current_version = $current_version - 0.1;
822 /// And finally compare them, saving results
823 if (version_compare($current_version, $needed_version, '>=')) {
824 $result->setStatus(true);
825 } else {
826 $result->setStatus(false);
828 $result->setLevel('required');
829 $result->setCurrentVersion($release);
830 $result->setNeededVersion($needed_version);
832 return $result;
836 * This function will check if php requirements are satisfied
838 * @uses NO_VERSION_DATA_FOUND
839 * @uses NO_PHP_SECTION_FOUND
840 * @uses NO_PHP_VERSION_FOUND
841 * @param string $version xml version we are going to use to test this server
842 * @param int|string $env_select one of ENV_SELECT_NEWER | ENV_SELECT_DATAROOT | ENV_SELECT_RELEASE decide xml to use. String means plugin name.
843 * @return object results encapsulated in one environment_result object
845 function environment_check_php($version, $env_select) {
847 $result = new environment_results('php');
849 /// Get the enviroment version we need
850 if (!$data = get_environment_for_version($version, $env_select)) {
851 /// Error. No version data found
852 $result->setStatus(false);
853 $result->setErrorCode(NO_VERSION_DATA_FOUND);
854 return $result;
857 /// Extract the php part
858 if (!isset($data['#']['PHP'])) {
859 /// Error. No PHP section found
860 $result->setStatus(false);
861 $result->setErrorCode(NO_PHP_SECTION_FOUND);
862 return $result;
863 } else {
864 /// Extract level and version
865 $level = get_level($data['#']['PHP']['0']);
866 if (!isset($data['#']['PHP']['0']['@']['version'])) {
867 $result->setStatus(false);
868 $result->setErrorCode(NO_PHP_VERSION_FOUND);
869 return $result;
870 } else {
871 $needed_version = $data['#']['PHP']['0']['@']['version'];
875 /// Now search the version we are using
876 $current_version = normalize_version(phpversion());
878 /// And finally compare them, saving results
879 if (version_compare($current_version, $needed_version, '>=')) {
880 $result->setStatus(true);
881 } else {
882 $result->setStatus(false);
884 $result->setLevel($level);
885 $result->setCurrentVersion($current_version);
886 $result->setNeededVersion($needed_version);
888 /// Do any actions defined in the XML file.
889 process_environment_result($data['#']['PHP'][0], $result);
891 return $result;
895 * Looks for buggy PCRE implementation, we need unicode support in Moodle...
896 * @param string $version xml version we are going to use to test this server
897 * @param int|string $env_select one of ENV_SELECT_NEWER | ENV_SELECT_DATAROOT | ENV_SELECT_RELEASE decide xml to use. String means plugin name.
898 * @return stdClass results encapsulated in one environment_result object, null if irrelevant
900 function environment_check_pcre_unicode($version, $env_select) {
901 $result = new environment_results('pcreunicode');
903 // Get the environment version we need
904 if (!$data = get_environment_for_version($version, $env_select)) {
905 // Error. No version data found!
906 $result->setStatus(false);
907 $result->setErrorCode(NO_VERSION_DATA_FOUND);
908 return $result;
911 if (!isset($data['#']['PCREUNICODE'])) {
912 return null;
915 $level = get_level($data['#']['PCREUNICODE']['0']);
916 $result->setLevel($level);
918 if (!function_exists('preg_match')) {
919 // The extension test fails instead.
920 return null;
922 } else if (@preg_match('/\pL/u', 'a') and @preg_match('/á/iu', 'Á')) {
923 $result->setStatus(true);
925 } else {
926 $result->setStatus(false);
929 // Do any actions defined in the XML file.
930 process_environment_result($data['#']['PCREUNICODE'][0], $result);
932 return $result;
936 * This function will check if unicode database requirements are satisfied
938 * @uses NO_VERSION_DATA_FOUND
939 * @uses NO_UNICODE_SECTION_FOUND
940 * @param string $version xml version we are going to use to test this server
941 * @param int|string $env_select one of ENV_SELECT_NEWER | ENV_SELECT_DATAROOT | ENV_SELECT_RELEASE decide xml to use. String means plugin name.
942 * @return object results encapsulated in one environment_result object
944 function environment_check_unicode($version, $env_select) {
945 global $DB;
947 $result = new environment_results('unicode');
949 /// Get the enviroment version we need
950 if (!$data = get_environment_for_version($version, $env_select)) {
951 /// Error. No version data found
952 $result->setStatus(false);
953 $result->setErrorCode(NO_VERSION_DATA_FOUND);
954 return $result;
957 /// Extract the unicode part
959 if (!isset($data['#']['UNICODE'])) {
960 /// Error. No UNICODE section found
961 $result->setStatus(false);
962 $result->setErrorCode(NO_UNICODE_SECTION_FOUND);
963 return $result;
964 } else {
965 /// Extract level
966 $level = get_level($data['#']['UNICODE']['0']);
969 if (!$unicodedb = $DB->setup_is_unicodedb()) {
970 $result->setStatus(false);
971 } else {
972 $result->setStatus(true);
975 $result->setLevel($level);
977 /// Do any actions defined in the XML file.
978 process_environment_result($data['#']['UNICODE'][0], $result);
980 return $result;
984 * This function will check if database requirements are satisfied
986 * @uses NO_VERSION_DATA_FOUND
987 * @uses NO_DATABASE_SECTION_FOUND
988 * @uses NO_DATABASE_VENDORS_FOUND
989 * @uses NO_DATABASE_VENDOR_MYSQL_FOUND
990 * @uses NO_DATABASE_VENDOR_POSTGRES_FOUND
991 * @uses NO_DATABASE_VENDOR_VERSION_FOUND
992 * @param string $version xml version we are going to use to test this server
993 * @param int|string $env_select one of ENV_SELECT_NEWER | ENV_SELECT_DATAROOT | ENV_SELECT_RELEASE decide xml to use. String means plugin name.
994 * @return object results encapsulated in one environment_result object
996 function environment_check_database($version, $env_select) {
998 global $DB;
1000 $result = new environment_results('database');
1002 $vendors = array(); //Array of vendors in version
1004 /// Get the enviroment version we need
1005 if (!$data = get_environment_for_version($version, $env_select)) {
1006 /// Error. No version data found
1007 $result->setStatus(false);
1008 $result->setErrorCode(NO_VERSION_DATA_FOUND);
1009 return $result;
1012 /// Extract the database part
1013 if (!isset($data['#']['DATABASE'])) {
1014 /// Error. No DATABASE section found
1015 $result->setStatus(false);
1016 $result->setErrorCode(NO_DATABASE_SECTION_FOUND);
1017 return $result;
1018 } else {
1019 /// Extract level
1020 $level = get_level($data['#']['DATABASE']['0']);
1023 /// Extract DB vendors. At least 2 are mandatory (mysql & postgres)
1024 if (!isset($data['#']['DATABASE']['0']['#']['VENDOR'])) {
1025 /// Error. No VENDORS found
1026 $result->setStatus(false);
1027 $result->setErrorCode(NO_DATABASE_VENDORS_FOUND);
1028 return $result;
1029 } else {
1030 /// Extract vendors
1031 foreach ($data['#']['DATABASE']['0']['#']['VENDOR'] as $vendor) {
1032 if (isset($vendor['@']['name']) && isset($vendor['@']['version'])) {
1033 $vendors[$vendor['@']['name']] = $vendor['@']['version'];
1034 $vendorsxml[$vendor['@']['name']] = $vendor;
1038 /// Check we have the mysql vendor version
1039 if (empty($vendors['mysql'])) {
1040 $result->setStatus(false);
1041 $result->setErrorCode(NO_DATABASE_VENDOR_MYSQL_FOUND);
1042 return $result;
1044 /// Check we have the postgres vendor version
1045 if (empty($vendors['postgres'])) {
1046 $result->setStatus(false);
1047 $result->setErrorCode(NO_DATABASE_VENDOR_POSTGRES_FOUND);
1048 return $result;
1051 /// Now search the version we are using (depending of vendor)
1052 $current_vendor = $DB->get_dbvendor();
1054 $dbinfo = $DB->get_server_info();
1055 $current_version = normalize_version($dbinfo['version']);
1056 $needed_version = $vendors[$current_vendor];
1058 /// Check we have a needed version
1059 if (!$needed_version) {
1060 $result->setStatus(false);
1061 $result->setErrorCode(NO_DATABASE_VENDOR_VERSION_FOUND);
1062 return $result;
1065 // Check if the DB Vendor has been properly configured.
1066 // Hack: this is required when playing with MySQL and MariaDB since they share the same PHP module and base DB classes,
1067 // whilst they are slowly evolving using separate directions though MariaDB is still an "almost" drop-in replacement.
1068 $dbvendorismysql = ($current_vendor === 'mysql');
1069 $dbtypeismariadb = (stripos($dbinfo['description'], 'mariadb') !== false);
1070 if ($dbvendorismysql && $dbtypeismariadb) {
1071 $result->setStatus(false);
1072 $result->setLevel($level);
1073 $result->setInfo($current_vendor . ' (' . $dbinfo['description'] . ')');
1074 $result->setFeedbackStr('environmentmariadbwrongdbtype');
1075 return $result;
1078 /// And finally compare them, saving results
1079 if (version_compare($current_version, $needed_version, '>=')) {
1080 $result->setStatus(true);
1081 } else {
1082 $result->setStatus(false);
1084 $result->setLevel($level);
1085 $result->setCurrentVersion($current_version);
1086 $result->setNeededVersion($needed_version);
1087 $result->setInfo($current_vendor . ' (' . $dbinfo['description'] . ')');
1089 /// Do any actions defined in the XML file.
1090 process_environment_result($vendorsxml[$current_vendor], $result);
1092 return $result;
1097 * This function will post-process the result record by executing the specified
1098 * function, modifying it as necessary, also a custom message will be added
1099 * to the result object to be printed by the display layer.
1100 * Every bypass function must be defined in this file and it'll return
1101 * true/false to decide if the original test is bypassed or no. Also
1102 * such bypass functions are able to directly handling the result object
1103 * although it should be only under exceptional conditions.
1105 * @param string xmldata containing the bypass data
1106 * @param object result object to be updated
1107 * @return void
1109 function process_environment_bypass($xml, &$result) {
1111 /// Only try to bypass if we were in error and it was required
1112 if ($result->getStatus() || $result->getLevel() == 'optional') {
1113 return;
1116 /// It there is bypass info (function and message)
1117 if (is_array($xml['#']) && isset($xml['#']['BYPASS'][0]['@']['function']) && isset($xml['#']['BYPASS'][0]['@']['message'])) {
1118 $function = $xml['#']['BYPASS'][0]['@']['function'];
1119 $message = $xml['#']['BYPASS'][0]['@']['message'];
1120 /// Look for the function
1121 if (function_exists($function)) {
1122 /// Call it, and if bypass = true is returned, apply meesage
1123 if ($function($result)) {
1124 /// We only set the bypass message if the function itself hasn't defined it before
1125 if (empty($result->getBypassStr)) {
1126 if (isset($xml['#']['BYPASS'][0]['@']['plugin'])) {
1127 $result->setBypassStr(array($message, $xml['#']['BYPASS'][0]['@']['plugin']));
1128 } else {
1129 $result->setBypassStr($message);
1138 * This function will post-process the result record by executing the specified
1139 * function, modifying it as necessary, also a custom message will be added
1140 * to the result object to be printed by the display layer.
1141 * Every restrict function must be defined in this file and it'll return
1142 * true/false to decide if the original test is restricted or no. Also
1143 * such restrict functions are able to directly handling the result object
1144 * although it should be only under exceptional conditions.
1146 * @param string xmldata containing the restrict data
1147 * @param object result object to be updated
1148 * @return void
1150 function process_environment_restrict($xml, &$result) {
1152 /// Only try to restrict if we were not in error and it was required
1153 if (!$result->getStatus() || $result->getLevel() == 'optional') {
1154 return;
1156 /// It there is restrict info (function and message)
1157 if (is_array($xml['#']) && isset($xml['#']['RESTRICT'][0]['@']['function']) && isset($xml['#']['RESTRICT'][0]['@']['message'])) {
1158 $function = $xml['#']['RESTRICT'][0]['@']['function'];
1159 $message = $xml['#']['RESTRICT'][0]['@']['message'];
1160 /// Look for the function
1161 if (function_exists($function)) {
1162 /// Call it, and if restrict = true is returned, apply meesage
1163 if ($function($result)) {
1164 /// We only set the restrict message if the function itself hasn't defined it before
1165 if (empty($result->getRestrictStr)) {
1166 if (isset($xml['#']['RESTRICT'][0]['@']['plugin'])) {
1167 $result->setRestrictStr(array($message, $xml['#']['RESTRICT'][0]['@']['plugin']));
1168 } else {
1169 $result->setRestrictStr($message);
1178 * This function will detect if there is some message available to be added to the
1179 * result in order to clarify enviromental details.
1181 * @uses INCORRECT_FEEDBACK_FOR_REQUIRED
1182 * @uses INCORRECT_FEEDBACK_FOR_OPTIONAL
1183 * @param string xmldata containing the feedback data
1184 * @param object reult object to be updated
1186 function process_environment_messages($xml, &$result) {
1188 /// If there is feedback info
1189 if (is_array($xml['#']) && isset($xml['#']['FEEDBACK'][0]['#'])) {
1190 $feedbackxml = $xml['#']['FEEDBACK'][0]['#'];
1192 // Detect some incorrect feedback combinations.
1193 if ($result->getLevel() == 'required' and isset($feedbackxml['ON_CHECK'])) {
1194 $result->setStatus(false);
1195 $result->setErrorCode(INCORRECT_FEEDBACK_FOR_REQUIRED);
1196 } else if ($result->getLevel() == 'optional' and isset($feedbackxml['ON_ERROR'])) {
1197 $result->setStatus(false);
1198 $result->setErrorCode(INCORRECT_FEEDBACK_FOR_OPTIONAL);
1201 if (!$result->status and $result->getLevel() == 'required') {
1202 if (isset($feedbackxml['ON_ERROR'][0]['@']['message'])) {
1203 if (isset($feedbackxml['ON_ERROR'][0]['@']['plugin'])) {
1204 $result->setFeedbackStr(array($feedbackxml['ON_ERROR'][0]['@']['message'], $feedbackxml['ON_ERROR'][0]['@']['plugin']));
1205 } else {
1206 $result->setFeedbackStr($feedbackxml['ON_ERROR'][0]['@']['message']);
1209 } else if (!$result->status and $result->getLevel() == 'optional') {
1210 if (isset($feedbackxml['ON_CHECK'][0]['@']['message'])) {
1211 if (isset($feedbackxml['ON_CHECK'][0]['@']['plugin'])) {
1212 $result->setFeedbackStr(array($feedbackxml['ON_CHECK'][0]['@']['message'], $feedbackxml['ON_CHECK'][0]['@']['plugin']));
1213 } else {
1214 $result->setFeedbackStr($feedbackxml['ON_CHECK'][0]['@']['message']);
1217 } else {
1218 if (isset($feedbackxml['ON_OK'][0]['@']['message'])) {
1219 if (isset($feedbackxml['ON_OK'][0]['@']['plugin'])) {
1220 $result->setFeedbackStr(array($feedbackxml['ON_OK'][0]['@']['message'], $feedbackxml['ON_OK'][0]['@']['plugin']));
1221 } else {
1222 $result->setFeedbackStr($feedbackxml['ON_OK'][0]['@']['message']);
1230 //--- Helper Class to return results to caller ---//
1234 * Helper Class to return results to caller
1236 * @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com}
1237 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
1238 * @package moodlecore
1240 class environment_results {
1242 * @var string Which are we checking (database, php, php_extension, php_extension)
1244 var $part;
1246 * @var bool true means the test passed and all is OK. false means it failed.
1248 var $status;
1250 * @var integer See constants at the beginning of the file
1252 var $error_code;
1254 * @var string required/optional
1256 var $level;
1258 * @var string current version detected
1260 var $current_version;
1262 * @var string version needed
1264 var $needed_version;
1266 * @var string Aux. info (DB vendor, library...)
1268 var $info;
1270 * @var string String to show on error|on check|on ok
1272 var $feedback_str;
1274 * @var string String to show if some bypass has happened
1276 var $bypass_str;
1278 * @var string String to show if some restrict has happened
1280 var $restrict_str;
1282 * @var string|null full plugin name or null if main environment
1284 var $plugin = null;
1286 * Constructor of the environment_result class. Just set default values
1288 * @param string $part
1290 public function __construct($part) {
1291 $this->part=$part;
1292 $this->status=false;
1293 $this->error_code=NO_ERROR;
1294 $this->level='required';
1295 $this->current_version='';
1296 $this->needed_version='';
1297 $this->info='';
1298 $this->feedback_str='';
1299 $this->bypass_str='';
1300 $this->restrict_str='';
1304 * Old syntax of class constructor. Deprecated in PHP7.
1306 * @deprecated since Moodle 3.1
1308 public function environment_results($part) {
1309 debugging('Use of class name as constructor is deprecated', DEBUG_DEVELOPER);
1310 self::__construct($part);
1314 * Set the status
1316 * @param bool $testpassed true means the test passed and all is OK. false means it failed.
1318 function setStatus($testpassed) {
1319 $this->status = $testpassed;
1320 if ($testpassed) {
1321 $this->setErrorCode(NO_ERROR);
1326 * Set the error_code
1328 * @param integer $error_code the error code (see constants above)
1330 function setErrorCode($error_code) {
1331 $this->error_code=$error_code;
1335 * Set the level
1337 * @param string $level the level (required, optional)
1339 function setLevel($level) {
1340 $this->level=$level;
1344 * Set the current version
1346 * @param string $current_version the current version
1348 function setCurrentVersion($current_version) {
1349 $this->current_version=$current_version;
1353 * Set the needed version
1355 * @param string $needed_version the needed version
1357 function setNeededVersion($needed_version) {
1358 $this->needed_version=$needed_version;
1362 * Set the auxiliary info
1364 * @param string $info the auxiliary info
1366 function setInfo($info) {
1367 $this->info=$info;
1371 * Set the feedback string
1373 * @param mixed $str the feedback string that will be fetched from the admin lang file.
1374 * pass just the string or pass an array of params for get_string
1375 * You always should put your string in admin.php but a third param is useful
1376 * to pass an $a object / string to get_string
1378 function setFeedbackStr($str) {
1379 $this->feedback_str=$str;
1384 * Set the bypass string
1386 * @param string $str the bypass string that will be fetched from the admin lang file.
1387 * pass just the string or pass an array of params for get_string
1388 * You always should put your string in admin.php but a third param is useful
1389 * to pass an $a object / string to get_string
1391 function setBypassStr($str) {
1392 $this->bypass_str=$str;
1396 * Set the restrict string
1398 * @param string $str the restrict string that will be fetched from the admin lang file.
1399 * pass just the string or pass an array of params for get_string
1400 * You always should put your string in admin.php but a third param is useful
1401 * to pass an $a object / string to get_string
1403 function setRestrictStr($str) {
1404 $this->restrict_str=$str;
1408 * Get the status
1410 * @return bool true means the test passed and all is OK. false means it failed.
1412 function getStatus() {
1413 return $this->status;
1417 * Get the error code
1419 * @return integer error code
1421 function getErrorCode() {
1422 return $this->error_code;
1426 * Get the level
1428 * @return string level
1430 function getLevel() {
1431 return $this->level;
1435 * Get the current version
1437 * @return string current version
1439 function getCurrentVersion() {
1440 return $this->current_version;
1444 * Get the needed version
1446 * @return string needed version
1448 function getNeededVersion() {
1449 return $this->needed_version;
1453 * Get the aux info
1455 * @return string info
1457 function getInfo() {
1458 return $this->info;
1462 * Get the part this result belongs to
1464 * @return string part
1466 function getPart() {
1467 return $this->part;
1471 * Get the feedback string
1473 * @return mixed feedback string (can be an array of params for get_string or a single string to fetch from
1474 * admin.php lang file).
1476 function getFeedbackStr() {
1477 return $this->feedback_str;
1481 * Get the bypass string
1483 * @return mixed bypass string (can be an array of params for get_string or a single string to fetch from
1484 * admin.php lang file).
1486 function getBypassStr() {
1487 return $this->bypass_str;
1491 * Get the restrict string
1493 * @return mixed restrict string (can be an array of params for get_string or a single string to fetch from
1494 * admin.php lang file).
1496 function getRestrictStr() {
1497 return $this->restrict_str;
1501 * @todo Document this function
1503 * @param mixed $string params for get_string, either a string to fetch from admin.php or an array of
1504 * params for get_string.
1505 * @param string $class css class(es) for message.
1506 * @return string feedback string fetched from lang file wrapped in p tag with class $class or returns
1507 * empty string if $string is empty.
1509 function strToReport($string, $class){
1510 if (!empty($string)){
1511 if (is_array($string)){
1512 $str = call_user_func_array('get_string', $string);
1513 } else {
1514 $str = get_string($string, 'admin');
1516 return '<p class="'.$class.'">'.$str.'</p>';
1517 } else {
1518 return '';
1523 * Get plugin name.
1525 * @return string plugin name
1527 function getPluginName() {
1528 if ($this->plugin) {
1529 $manager = core_plugin_manager::instance();
1530 list($plugintype, $pluginname) = core_component::normalize_component($this->plugin);
1531 return $manager->plugintype_name($plugintype) . ' / ' . $manager->plugin_name($this->plugin);
1532 } else {
1533 return '';
1538 /// Here all the restrict functions are coded to be used by the environment
1539 /// checker. All those functions will receive the result object and will
1540 /// return it modified as needed (status and bypass string)
1543 * @param array $element the element from the environment.xml file that should have
1544 * either a level="required" or level="optional" attribute.
1545 * @return string "required" or "optional".
1547 function get_level($element) {
1548 $level = 'required';
1549 if (isset($element['@']['level'])) {
1550 $level = $element['@']['level'];
1551 if (!in_array($level, array('required', 'optional'))) {
1552 debugging('The level of a check in the environment.xml file must be "required" or "optional".', DEBUG_DEVELOPER);
1553 $level = 'required';
1555 } else {
1556 debugging('Checks in the environment.xml file must have a level="required" or level="optional" attribute.', DEBUG_DEVELOPER);
1558 return $level;
1562 * Once the result has been determined, look in the XML for any
1563 * messages, or other things that should be done depending on the outcome.
1565 * @param array $element the element from the environment.xml file which
1566 * may have children defining what should be done with the outcome.
1567 * @param object $result the result of the test, which may be modified by
1568 * this function as specified in the XML.
1570 function process_environment_result($element, &$result) {
1571 /// Process messages, modifying the $result if needed.
1572 process_environment_messages($element, $result);
1573 /// Process bypass, modifying $result if needed.
1574 process_environment_bypass($element, $result);
1575 /// Process restrict, modifying $result if needed.
1576 process_environment_restrict($element, $result);
1580 * Check if the current PHP version is greater than or equal to
1581 * PHP version 7.
1583 * @param object $result an environment_results instance
1584 * @return bool result of version check
1586 function restrict_php_version_7(&$result) {
1587 return restrict_php_version($result, '7');
1591 * Check if the current PHP version is greater than or equal to an
1592 * unsupported version.
1594 * @param object $result an environment_results instance
1595 * @param string $version the version of PHP that can't be used
1596 * @return bool result of version check
1598 function restrict_php_version(&$result, $version) {
1600 // Get the current PHP version.
1601 $currentversion = normalize_version(phpversion());
1603 // Confirm we're using a supported PHP version.
1604 if (version_compare($currentversion, $version, '<')) {
1605 // Everything is ok, the restriction doesn't apply.
1606 return false;
1607 } else {
1608 // We're using an unsupported PHP version, apply restriction.
1609 return true;
1614 * Check if the current PHP version is greater than or equal to
1615 * PHP version 7.1.
1617 * @param object $result an environment_results instance
1618 * @return bool result of version check
1620 function restrict_php_version_71(&$result) {
1621 return restrict_php_version($result, '7.1');
1625 * Check if the current PHP version is greater than or equal to
1626 * PHP version 7.2.
1628 * @param object $result an environment_results instance
1629 * @return bool result of version check
1631 function restrict_php_version_72(&$result) {
1632 return restrict_php_version($result, '7.2');
1636 * Check if the current PHP version is greater than or equal to
1637 * PHP version 7.3.
1639 * @param object $result an environment_results instance
1640 * @return bool result of version check
1642 function restrict_php_version_73(&$result) {
1643 return restrict_php_version($result, '7.3');
1647 * Check if the current PHP version is greater than or equal to
1648 * PHP version 7.4.
1650 * @param object $result an environment_results instance
1651 * @return bool result of version check
1653 function restrict_php_version_74(&$result) {
1654 return restrict_php_version($result, '7.4');
1658 * Check if the current PHP version is greater than or equal to
1659 * PHP version 8.0
1661 * @param object $result an environment_results instance
1662 * @return bool result of version check
1664 function restrict_php_version_80($result) {
1665 return restrict_php_version($result, '8.0');
1669 * Check if the current PHP version is greater than or equal to
1670 * PHP version 8.1
1672 * @param object $result an environment_results instance
1673 * @return bool result of version check
1675 function restrict_php_version_81($result) {
1676 return restrict_php_version($result, '8.1');
1680 * Check if the current PHP version is greater than or equal to
1681 * PHP version 8.2
1683 * @param object $result an environment_results instance
1684 * @return bool result of version check
1686 function restrict_php_version_82($result) {
1687 return restrict_php_version($result, '8.2');
1691 * Check if the current PHP version is greater than or equal to
1692 * PHP version 8.3
1694 * @param object $result an environment_results instance
1695 * @return bool result of version check
1697 function restrict_php_version_83($result) {
1698 return restrict_php_version($result, '8.3');