64a3a4ffed9571e0925990d08849d61d806ea676
[moodle.git] / lib / environmentlib.php
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.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
18 /**
19  * This library includes all the necessary stuff to execute some standard
20  * tests of required versions and libraries to run Moodle. It can be
21  * used from the admin interface, and both at install and upgrade.
22  *
23  * All the info is stored in the admin/environment.xml file,
24  * supporting to have an updated version in dataroot/environment
25  *
26  * @copyright  (C) 2001-3001 Eloy Lafuente (stronk7) {@link http://contiento.com}
27  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
28  * @package    core
29  * @subpackage admin
30  */
32 defined('MOODLE_INTERNAL') || die();
34 /// Add required files
35 /**
36  * Include the necessary
37  */
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.
88  *
89  * @param string $version version to check.
90  * @param int $env_select one of ENV_SELECT_NEWER | ENV_SELECT_DATAROOT | ENV_SELECT_RELEASE decide xml to use. Default ENV_SELECT_NEWER (BC)
91  * @return array with two elements. The first element true/false, depending on
92  *      on whether the check passed. The second element is an array of environment_results
93  *      objects that has detailed information about the checks and which ones passed.
94  */
95 function check_moodle_environment($version, $env_select = ENV_SELECT_NEWER) {
96     if ($env_select != ENV_SELECT_NEWER and $env_select != ENV_SELECT_DATAROOT and $env_select != ENV_SELECT_RELEASE) {
97         throw new coding_exception('Incorrect value of $env_select parameter');
98     }
100 /// Get the more recent version before the requested
101     if (!$version = get_latest_version_available($version, $env_select)) {
102         return array(false, array());
103     }
105 /// Perform all the checks
106     if (!$environment_results = environment_check($version, $env_select)) {
107         return array(false, array());
108     }
110 /// Iterate over all the results looking for some error in required items
111 /// or some error_code
112     $result = true;
113     foreach ($environment_results as $environment_result) {
114         if (!$environment_result->getStatus() && $environment_result->getLevel() == 'required'
115           && !$environment_result->getBypassStr()) {
116             $result = false; // required item that is not bypased
117         } else if ($environment_result->getStatus() && $environment_result->getLevel() == 'required'
118           && $environment_result->getRestrictStr()) {
119             $result = false; // required item that is restricted
120         } else if ($environment_result->getErrorCode()) {
121             $result = false;
122         }
123     }
125     return array($result, $environment_results);
129 /**
130  * Returns array of critical errors in plain text format
131  * @param array $environment_results array of results gathered
132  * @return array errors
133  */
134 function environment_get_errors($environment_results) {
135     global $CFG;
136     $errors = array();
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;
151         }
153         /// Calculate the status value
154         if ($environment_result->getBypassStr() != '') {
155             // not interesting
156             continue;
157         } else if ($environment_result->getRestrictStr() != '') {
158             // error
159         } else {
160             if ($status) {
161                 // ok
162                 continue;
163             } else {
164                 if ($environment_result->getLevel() == 'optional') {
165                     // just a warning
166                     continue;
167                 } else {
168                     // error
169                 }
170             }
171         }
173         // We are comparing versions
174         $rec = new stdClass();
175         if ($rec->needed = $environment_result->getNeededVersion()) {
176             $rec->current = $environment_result->getCurrentVersion();
177             if ($environment_result->getLevel() == 'required') {
178                 $stringtouse = 'environmentrequireversion';
179             } else {
180                 $stringtouse = 'environmentrecommendversion';
181             }
182         // We are checking installed & enabled things
183         } else if ($environment_result->getPart() == 'custom_check') {
184             if ($environment_result->getLevel() == 'required') {
185                 $stringtouse = 'environmentrequirecustomcheck';
186             } else {
187                 $stringtouse = 'environmentrecommendcustomcheck';
188             }
189         } else if ($environment_result->getPart() == 'php_setting') {
190             if ($status) {
191                 $stringtouse = 'environmentsettingok';
192             } else if ($environment_result->getLevel() == 'required') {
193                 $stringtouse = 'environmentmustfixsetting';
194             } else {
195                 $stringtouse = 'environmentshouldfixsetting';
196             }
197         } else {
198             if ($environment_result->getLevel() == 'required') {
199                 $stringtouse = 'environmentrequireinstall';
200             } else {
201                 $stringtouse = 'environmentrecommendinstall';
202             }
203         }
204         $report = get_string($stringtouse, 'admin', $rec);
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;
217         }
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);
225         }
226     }
228     return $errors;
232 /**
233  * This function will normalize any version to just a serie of numbers
234  * separated by dots. Everything else will be removed.
235  *
236  * @param string $version the original version
237  * @return string the normalized version
238  */
239 function normalize_version($version) {
241 /// 1.9 Beta 2 should be read 1.9 on enviromental checks, not 1.9.2
242 /// we can discard everything after the first space
243     $version = trim($version);
244     $versionarr = explode(" ",$version);
245     if (!empty($versionarr)) {
246         $version = $versionarr[0];
247     }
248 /// Replace everything but numbers and dots by dots
249     $version = preg_replace('/[^\.\d]/', '.', $version);
250 /// Combine multiple dots in one
251     $version = preg_replace('/(\.{2,})/', '.', $version);
252 /// Trim possible leading and trailing dots
253     $version = trim($version, '.');
255     return $version;
259 /**
260  * This function will load the environment.xml file and xmlize it
261  *
262  * @staticvar array $data
263  * @uses ENV_SELECT_NEWER
264  * @uses ENV_SELECT_DATAROOT
265  * @uses ENV_SELECT_RELEASE
266  * @param int|string $env_select one of ENV_SELECT_NEWER | ENV_SELECT_DATAROOT | ENV_SELECT_RELEASE decide xml to use. String means plugin name.
267  * @return mixed the xmlized structure or false on error
268  */
269 function load_environment_xml($env_select=ENV_SELECT_NEWER) {
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];
277     }
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;
290                     }
291                 }
292                 break;
293             case ENV_SELECT_DATAROOT:
294                 if (!is_file($file) || !is_readable($file) || !$contents = file_get_contents($file)) {
295                     $contents = false;
296                 }
297                 break;
298             case ENV_SELECT_RELEASE:
299                 if (!is_file($internalfile) || !is_readable($internalfile) || !$contents = file_get_contents($internalfile)) {
300                     $contents = false;
301                 }
302                 break;
303         }
304     } else {
305         if ($plugindir = core_component::get_component_directory($env_select)) {
306             $pluginfile = "$plugindir/environment.xml";
307             if (!is_file($pluginfile) || !is_readable($pluginfile) || !$contents = file_get_contents($pluginfile)) {
308                 $contents = false;
309             }
310         }
311     }
312     // XML the whole file.
313     if ($contents !== false) {
314         $contents = xmlize($contents);
315     }
317     $data[$env_select] = $contents;
319     return $data[$env_select];
323 /**
324  * This function will return the list of Moodle versions available
325  *
326  * @return array of versions
327  */
328 function get_list_of_environment_versions($contents) {
329     $versions = array();
331     if (isset($contents['COMPATIBILITY_MATRIX']['#']['MOODLE'])) {
332         foreach ($contents['COMPATIBILITY_MATRIX']['#']['MOODLE'] as $version) {
333             $versions[] = $version['@']['version'];
334         }
335     }
337     if (isset($contents['COMPATIBILITY_MATRIX']['#']['PLUGIN'])) {
338         $versions[] = 'all';
339     }
341     return $versions;
345 /**
346  * This function will return the most recent version in the environment.xml
347  * file previous or equal to the version requested
348  *
349  * @param string $version top version from which we start to look backwards
350  * @param int $env_select one of ENV_SELECT_NEWER | ENV_SELECT_DATAROOT | ENV_SELECT_RELEASE decide xml to use.
351  * @return string|bool string more recent version or false if not found
352  */
353 function get_latest_version_available($version, $env_select) {
354     if ($env_select != ENV_SELECT_NEWER and $env_select != ENV_SELECT_DATAROOT and $env_select != ENV_SELECT_RELEASE) {
355         throw new coding_exception('Incorrect value of $env_select parameter');
356     }
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;
364     }
366 /// Detect available versions
367     if (!$versions = get_list_of_environment_versions($contents)) {
368         return false;
369     }
370 /// First we look for exact version
371     if (in_array($version, $versions)) {
372         return $version;
373     } else {
374         $found_version = false;
375     /// Not exact match, so we are going to iterate over the list searching
376     /// for the latest version before the requested one
377         foreach ($versions as $arrversion) {
378             if (version_compare($arrversion, $version, '<')) {
379                 $found_version = $arrversion;
380             }
381         }
382     }
384     return $found_version;
388 /**
389  * This function will return the xmlized data belonging to one Moodle version
390  *
391  * @param string $version top version from which we start to look backwards
392  * @param int|string $env_select one of ENV_SELECT_NEWER | ENV_SELECT_DATAROOT | ENV_SELECT_RELEASE decide xml to use. String means plugin name.
393  * @return mixed the xmlized structure or false on error
394  */
395 function get_environment_for_version($version, $env_select) {
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;
403     }
405 /// Detect available versions
406     if (!$versions = get_list_of_environment_versions($contents)) {
407         return false;
408     }
410     // If $env_select is not numeric then this is being called on a plugin, and not the core environment.xml
411     // If a version of 'all' is in the arry is also means that the new <PLUGIN> tag was found, this should
412     // be matched against any version of Moodle.
413     if (!is_numeric($env_select) && in_array('all', $versions)
414             && environment_verify_plugin($env_select, $contents['COMPATIBILITY_MATRIX']['#']['PLUGIN'][0])) {
415         return $contents['COMPATIBILITY_MATRIX']['#']['PLUGIN'][0];
416     }
418 /// If the version requested is available
419     if (!in_array($version, $versions)) {
420         return false;
421     }
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]];
429 /**
430  * Checks if a plugin tag has a name attribute and it matches the plugin being tested.
431  *
432  * @param string $plugin the name of the plugin.
433  * @param array $pluginxml the xmlised structure for the plugin tag being tested.
434  * @return boolean true if the name attribute exists and matches the plugin being tested.
435  */
436 function environment_verify_plugin($plugin, $pluginxml) {
437     if (!isset($pluginxml['@']['name']) || $pluginxml['@']['name'] != $plugin) {
438         return false;
439     }
440     return true;
443 /**
444  * This function will check for everything (DB, PHP and PHP extensions for now)
445  * returning an array of environment_result objects.
446  *
447  * @global object
448  * @param string $version xml version we are going to use to test this server
449  * @param int $env_select one of ENV_SELECT_NEWER | ENV_SELECT_DATAROOT | ENV_SELECT_RELEASE decide xml to use.
450  * @return environment_results[] array of results encapsulated in one environment_result object
451  */
452 function environment_check($version, $env_select) {
453     global $CFG;
455     if ($env_select != ENV_SELECT_NEWER and $env_select != ENV_SELECT_DATAROOT and $env_select != ENV_SELECT_RELEASE) {
456         throw new coding_exception('Incorrect value of $env_select parameter');
457     }
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);
467     }
468     $results[] = environment_check_unicode($version, $env_select);
469     $results[] = environment_check_database($version, $env_select);
470     $results[] = environment_check_php($version, $env_select);
472     if ($result = environment_check_pcre_unicode($version, $env_select)) {
473         $results[] = $result;
474     }
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;
498             }
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;
507             }
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;
516                 }
517             }
519             $pluginresults = environment_check_php_settings($version, $plugin);
520             foreach ($pluginresults as $result) {
521                 if ($result->error_code != NO_VERSION_DATA_FOUND) {
522                     $result->plugin = $plugin;
523                     $results[] = $result;
524                 }
525             }
527             $pluginresults = environment_custom_checks($version, $plugin);
528             foreach ($pluginresults as $result) {
529                 if ($result->error_code != NO_VERSION_DATA_FOUND) {
530                     $result->plugin = $plugin;
531                     $results[] = $result;
532                 }
533             }
534         }
535     }
537     return $results;
541 /**
542  * This function will check if php extensions requirements are satisfied
543  *
544  * @uses NO_VERSION_DATA_FOUND
545  * @uses NO_PHP_EXTENSIONS_SECTION_FOUND
546  * @uses NO_PHP_EXTENSIONS_NAME_FOUND
547  * @param string $version xml version we are going to use to test this server
548  * @param int|string $env_select one of ENV_SELECT_NEWER | ENV_SELECT_DATAROOT | ENV_SELECT_RELEASE decide xml to use. String means plugin name.
549  * @return array array of results encapsulated in one environment_result object
550  */
551 function environment_check_php_extensions($version, $env_select) {
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);
562     }
564 /// Extract the php_extension part
565     if (!isset($data['#']['PHP_EXTENSIONS']['0']['#']['PHP_EXTENSION'])) {
566     /// Error. No PHP section found
567         $result = new environment_results('php_extension');
568         $result->setStatus(false);
569         $result->setErrorCode(NO_PHP_EXTENSIONS_SECTION_FOUND);
570         return array($result);
571     }
572 /// Iterate over extensions checking them and creating the needed environment_results
573     foreach($data['#']['PHP_EXTENSIONS']['0']['#']['PHP_EXTENSION'] as $extension) {
574         $result = new environment_results('php_extension');
575     /// Check for level
576         $level = get_level($extension);
577     /// Check for extension name
578         if (!isset($extension['@']['name'])) {
579             $result->setStatus(false);
580             $result->setErrorCode(NO_PHP_EXTENSIONS_NAME_FOUND);
581         } else {
582             $extension_name = $extension['@']['name'];
583         /// The name exists. Just check if it's an installed extension
584             if (!extension_loaded($extension_name)) {
585                 $result->setStatus(false);
586             } else {
587                 $result->setStatus(true);
588             }
589             $result->setLevel($level);
590             $result->setInfo($extension_name);
591         }
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;
598     }
601     return $results;
604 /**
605  * This function will check if php extensions requirements are satisfied
606  *
607  * @uses NO_VERSION_DATA_FOUND
608  * @uses NO_PHP_SETTINGS_NAME_FOUND
609  * @param string $version xml version we are going to use to test this server
610  * @param int|string $env_select one of ENV_SELECT_NEWER | ENV_SELECT_DATAROOT | ENV_SELECT_RELEASE decide xml to use. String means plugin name.
611  * @return array array of results encapsulated in one environment_result object
612  */
613 function environment_check_php_settings($version, $env_select) {
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;
625     }
627 /// Extract the php_setting part
628     if (!isset($data['#']['PHP_SETTINGS']['0']['#']['PHP_SETTING'])) {
629     /// No PHP section found - ignore
630         return $results;
631     }
632 /// Iterate over settings checking them and creating the needed environment_results
633     foreach($data['#']['PHP_SETTINGS']['0']['#']['PHP_SETTING'] as $setting) {
634         $result = new environment_results('php_setting');
635     /// Check for level
636         $level = get_level($setting);
637         $result->setLevel($level);
638     /// Check for extension name
639         if (!isset($setting['@']['name'])) {
640             $result->setStatus(false);
641             $result->setErrorCode(NO_PHP_SETTINGS_NAME_FOUND);
642         } else {
643             $setting_name  = $setting['@']['name'];
644             $setting_value = $setting['@']['value'];
645             $result->setInfo($setting_name);
647             if ($setting_name == 'memory_limit') {
648                 $current = ini_get('memory_limit');
649                 if ($current == -1) {
650                     $result->setStatus(true);
651                 } else {
652                     $current  = get_real_size($current);
653                     $minlimit = get_real_size($setting_value);
654                     if ($current < $minlimit) {
655                         @ini_set('memory_limit', $setting_value);
656                         $current = ini_get('memory_limit');
657                         $current = get_real_size($current);
658                     }
659                     $result->setStatus($current >= $minlimit);
660                 }
662             } else {
663                 $current = ini_get_bool($setting_name);
664             /// The name exists. Just check if it's an installed extension
665                 if ($current == $setting_value) {
666                     $result->setStatus(true);
667                 } else {
668                     $result->setStatus(false);
669                 }
670             }
671         }
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;
678     }
681     return $results;
684 /**
685  * This function will do the custom checks.
686  *
687  * @uses CUSTOM_CHECK_FUNCTION_MISSING
688  * @uses CUSTOM_CHECK_FILE_MISSING
689  * @uses NO_CUSTOM_CHECK_FOUND
690  * @param string $version xml version we are going to use to test this server.
691  * @param int|string $env_select one of ENV_SELECT_NEWER | ENV_SELECT_DATAROOT | ENV_SELECT_RELEASE decide xml to use. String means plugin name.
692  * @return array array of results encapsulated in environment_result objects.
693  */
694 function environment_custom_checks($version, $env_select) {
695     global $CFG;
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;
707     }
709 /// Extract the CUSTOM_CHECKS part
710     if (!isset($data['#']['CUSTOM_CHECKS']['0']['#']['CUSTOM_CHECK'])) {
711     /// No custom checks found - not a problem
712         return $results;
713     }
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);
730                 }
731             }
733             if (is_callable($function)) {
734                 $result->setLevel($level);
735                 $result->setInfo($function);
736                 $result = call_user_func($function, $result);
737             } else if (!$file or is_readable($file)) {
738             /// Only show error for current version (where function MUST exist)
739             /// else, we are performing custom checks against future versiosn
740             /// and function MAY not exist, so it doesn't cause error, just skip
741             /// custom check by returning null. MDL-15939
742                 if (version_compare($current_version, $version, '>=')) {
743                     $result->setStatus(false);
744                     $result->setInfo($function);
745                     $result->setErrorCode(CUSTOM_CHECK_FUNCTION_MISSING);
746                 } else {
747                     $result = null;
748                 }
749             } else {
750             /// Only show error for current version (where function MUST exist)
751             /// else, we are performing custom checks against future versiosn
752             /// and function MAY not exist, so it doesn't cause error, just skip
753             /// custom check by returning null. MDL-15939
754                 if (version_compare($current_version, $version, '>=')) {
755                     $result->setStatus(false);
756                     $result->setInfo($function);
757                     $result->setErrorCode(CUSTOM_CHECK_FILE_MISSING);
758                 } else {
759                     $result = null;
760                 }
761             }
762         } else {
763             $result->setStatus(false);
764             $result->setErrorCode(NO_CUSTOM_CHECK_FOUND);
765         }
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;
773         }
774     }
776     return $results;
779 /**
780  * This function will check if Moodle requirements are satisfied
781  *
782  * @uses NO_VERSION_DATA_FOUND
783  * @param string $version xml version we are going to use to test this server
784  * @param int|string $env_select one of ENV_SELECT_NEWER | ENV_SELECT_DATAROOT | ENV_SELECT_RELEASE decide xml to use. String means plugin name.
785  * @return object results encapsulated in one environment_result object
786  */
787 function environment_check_moodle($version, $env_select) {
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;
797     }
799 /// Extract the moodle part
800     if (!isset($data['@']['requires'])) {
801         $needed_version = '1.0'; /// Default to 1.0 if no moodle requires is found
802     } else {
803     /// Extract required moodle version
804         $needed_version = $data['@']['requires'];
805     }
807 /// Now search the version we are using
808     $release = get_config('', 'release');
809     $current_version = normalize_version($release);
810     if (strpos($release, 'dev') !== false) {
811         // when final version is required, dev is NOT enough!
812         $current_version = $current_version - 0.1;
813     }
815 /// And finally compare them, saving results
816     if (version_compare($current_version, $needed_version, '>=')) {
817         $result->setStatus(true);
818     } else {
819         $result->setStatus(false);
820     }
821     $result->setLevel('required');
822     $result->setCurrentVersion($release);
823     $result->setNeededVersion($needed_version);
825     return $result;
828 /**
829  * This function will check if php requirements are satisfied
830  *
831  * @uses NO_VERSION_DATA_FOUND
832  * @uses NO_PHP_SECTION_FOUND
833  * @uses NO_PHP_VERSION_FOUND
834  * @param string $version xml version we are going to use to test this server
835  * @param int|string $env_select one of ENV_SELECT_NEWER | ENV_SELECT_DATAROOT | ENV_SELECT_RELEASE decide xml to use. String means plugin name.
836  * @return object results encapsulated in one environment_result object
837  */
838 function environment_check_php($version, $env_select) {
840     $result = new environment_results('php');
842 /// Get the enviroment version we need
843     if (!$data = get_environment_for_version($version, $env_select)) {
844     /// Error. No version data found
845         $result->setStatus(false);
846         $result->setErrorCode(NO_VERSION_DATA_FOUND);
847         return $result;
848     }
850 /// Extract the php part
851     if (!isset($data['#']['PHP'])) {
852     /// Error. No PHP section found
853         $result->setStatus(false);
854         $result->setErrorCode(NO_PHP_SECTION_FOUND);
855         return $result;
856     } else {
857     /// Extract level and version
858         $level = get_level($data['#']['PHP']['0']);
859         if (!isset($data['#']['PHP']['0']['@']['version'])) {
860             $result->setStatus(false);
861             $result->setErrorCode(NO_PHP_VERSION_FOUND);
862             return $result;
863         } else {
864             $needed_version = $data['#']['PHP']['0']['@']['version'];
865         }
866     }
868 /// Now search the version we are using
869     $current_version = normalize_version(phpversion());
871 /// And finally compare them, saving results
872     if (version_compare($current_version, $needed_version, '>=')) {
873         $result->setStatus(true);
874     } else {
875         $result->setStatus(false);
876     }
877     $result->setLevel($level);
878     $result->setCurrentVersion($current_version);
879     $result->setNeededVersion($needed_version);
881 /// Do any actions defined in the XML file.
882     process_environment_result($data['#']['PHP'][0], $result);
884     return $result;
887 /**
888  * Looks for buggy PCRE implementation, we need unicode support in Moodle...
889  * @param string $version xml version we are going to use to test this server
890  * @param int|string $env_select one of ENV_SELECT_NEWER | ENV_SELECT_DATAROOT | ENV_SELECT_RELEASE decide xml to use. String means plugin name.
891  * @return stdClass results encapsulated in one environment_result object, null if irrelevant
892  */
893 function environment_check_pcre_unicode($version, $env_select) {
894     $result = new environment_results('pcreunicode');
896     // Get the environment version we need
897     if (!$data = get_environment_for_version($version, $env_select)) {
898         // Error. No version data found!
899         $result->setStatus(false);
900         $result->setErrorCode(NO_VERSION_DATA_FOUND);
901         return $result;
902     }
904     if (!isset($data['#']['PCREUNICODE'])) {
905         return null;
906     }
908     $level = get_level($data['#']['PCREUNICODE']['0']);
909     $result->setLevel($level);
911     if (!function_exists('preg_match')) {
912         // The extension test fails instead.
913         return null;
915     } else if (@preg_match('/\pL/u', 'a') and @preg_match('/á/iu', 'Á')) {
916         $result->setStatus(true);
918     } else {
919         $result->setStatus(false);
920     }
922     // Do any actions defined in the XML file.
923     process_environment_result($data['#']['PCREUNICODE'][0], $result);
925     return $result;
928 /**
929  * This function will check if unicode database requirements are satisfied
930  *
931  * @uses NO_VERSION_DATA_FOUND
932  * @uses NO_UNICODE_SECTION_FOUND
933  * @param string $version xml version we are going to use to test this server
934  * @param int|string $env_select one of ENV_SELECT_NEWER | ENV_SELECT_DATAROOT | ENV_SELECT_RELEASE decide xml to use. String means plugin name.
935  * @return object results encapsulated in one environment_result object
936  */
937 function environment_check_unicode($version, $env_select) {
938     global $DB;
940     $result = new environment_results('unicode');
942     /// Get the enviroment version we need
943     if (!$data = get_environment_for_version($version, $env_select)) {
944     /// Error. No version data found
945         $result->setStatus(false);
946         $result->setErrorCode(NO_VERSION_DATA_FOUND);
947         return $result;
948     }
950     /// Extract the unicode part
952     if (!isset($data['#']['UNICODE'])) {
953     /// Error. No UNICODE section found
954         $result->setStatus(false);
955         $result->setErrorCode(NO_UNICODE_SECTION_FOUND);
956         return $result;
957     } else {
958     /// Extract level
959         $level = get_level($data['#']['UNICODE']['0']);
960     }
962     if (!$unicodedb = $DB->setup_is_unicodedb()) {
963         $result->setStatus(false);
964     } else {
965         $result->setStatus(true);
966     }
968     $result->setLevel($level);
970 /// Do any actions defined in the XML file.
971     process_environment_result($data['#']['UNICODE'][0], $result);
973     return $result;
976 /**
977  * This function will check if database requirements are satisfied
978  *
979  * @uses NO_VERSION_DATA_FOUND
980  * @uses NO_DATABASE_SECTION_FOUND
981  * @uses NO_DATABASE_VENDORS_FOUND
982  * @uses NO_DATABASE_VENDOR_MYSQL_FOUND
983  * @uses NO_DATABASE_VENDOR_POSTGRES_FOUND
984  * @uses NO_DATABASE_VENDOR_VERSION_FOUND
985  * @param string $version xml version we are going to use to test this server
986  * @param int|string $env_select one of ENV_SELECT_NEWER | ENV_SELECT_DATAROOT | ENV_SELECT_RELEASE decide xml to use. String means plugin name.
987  * @return object results encapsulated in one environment_result object
988  */
989 function environment_check_database($version, $env_select) {
991     global $DB;
993     $result = new environment_results('database');
995     $vendors = array();  //Array of vendors in version
997 /// Get the enviroment version we need
998     if (!$data = get_environment_for_version($version, $env_select)) {
999     /// Error. No version data found
1000         $result->setStatus(false);
1001         $result->setErrorCode(NO_VERSION_DATA_FOUND);
1002         return $result;
1003     }
1005 /// Extract the database part
1006     if (!isset($data['#']['DATABASE'])) {
1007     /// Error. No DATABASE section found
1008         $result->setStatus(false);
1009         $result->setErrorCode(NO_DATABASE_SECTION_FOUND);
1010         return $result;
1011     } else {
1012     /// Extract level
1013         $level = get_level($data['#']['DATABASE']['0']);
1014     }
1016 /// Extract DB vendors. At least 2 are mandatory (mysql & postgres)
1017     if (!isset($data['#']['DATABASE']['0']['#']['VENDOR'])) {
1018     /// Error. No VENDORS found
1019         $result->setStatus(false);
1020         $result->setErrorCode(NO_DATABASE_VENDORS_FOUND);
1021         return $result;
1022     } else {
1023     /// Extract vendors
1024         foreach ($data['#']['DATABASE']['0']['#']['VENDOR'] as $vendor) {
1025             if (isset($vendor['@']['name']) && isset($vendor['@']['version'])) {
1026                 $vendors[$vendor['@']['name']] = $vendor['@']['version'];
1027                 $vendorsxml[$vendor['@']['name']] = $vendor;
1028             }
1029         }
1030     }
1031 /// Check we have the mysql vendor version
1032     if (empty($vendors['mysql'])) {
1033         $result->setStatus(false);
1034         $result->setErrorCode(NO_DATABASE_VENDOR_MYSQL_FOUND);
1035         return $result;
1036     }
1037 /// Check we have the postgres vendor version
1038     if (empty($vendors['postgres'])) {
1039         $result->setStatus(false);
1040         $result->setErrorCode(NO_DATABASE_VENDOR_POSTGRES_FOUND);
1041         return $result;
1042     }
1044 /// Now search the version we are using (depending of vendor)
1045     $current_vendor = $DB->get_dbvendor();
1047     $dbinfo = $DB->get_server_info();
1048     $current_version = normalize_version($dbinfo['version']);
1049     $needed_version = $vendors[$current_vendor];
1051 /// Check we have a needed version
1052     if (!$needed_version) {
1053         $result->setStatus(false);
1054         $result->setErrorCode(NO_DATABASE_VENDOR_VERSION_FOUND);
1055         return $result;
1056     }
1058     // Check if the DB Vendor has been properly configured.
1059     // Hack: this is required when playing with MySQL and MariaDB since they share the same PHP module and base DB classes,
1060     // whilst they are slowly evolving using separate directions though MariaDB is still an "almost" drop-in replacement.
1061     $dbvendorismysql = ($current_vendor === 'mysql');
1062     $dbtypeismariadb = (stripos($dbinfo['description'], 'mariadb') !== false);
1063     if ($dbvendorismysql && $dbtypeismariadb) {
1064         $result->setStatus(false);
1065         $result->setLevel($level);
1066         $result->setInfo($current_vendor . ' (' . $dbinfo['description'] . ')');
1067         $result->setFeedbackStr('environmentmariadbwrongdbtype');
1068         return $result;
1069     }
1071 /// And finally compare them, saving results
1072     if (version_compare($current_version, $needed_version, '>=')) {
1073         $result->setStatus(true);
1074     } else {
1075         $result->setStatus(false);
1076     }
1077     $result->setLevel($level);
1078     $result->setCurrentVersion($current_version);
1079     $result->setNeededVersion($needed_version);
1080     $result->setInfo($current_vendor . ' (' . $dbinfo['description'] . ')');
1082 /// Do any actions defined in the XML file.
1083     process_environment_result($vendorsxml[$current_vendor], $result);
1085     return $result;
1089 /**
1090  * This function will post-process the result record by executing the specified
1091  * function, modifying it as necessary, also a custom message will be added
1092  * to the result object to be printed by the display layer.
1093  * Every bypass function must be defined in this file and it'll return
1094  * true/false to decide if the original test is bypassed or no. Also
1095  * such bypass functions are able to directly handling the result object
1096  * although it should be only under exceptional conditions.
1097  *
1098  * @param string xmldata containing the bypass data
1099  * @param object result object to be updated
1100  * @return void
1101  */
1102 function process_environment_bypass($xml, &$result) {
1104 /// Only try to bypass if we were in error and it was required
1105     if ($result->getStatus() || $result->getLevel() == 'optional') {
1106         return;
1107     }
1109 /// It there is bypass info (function and message)
1110     if (is_array($xml['#']) && isset($xml['#']['BYPASS'][0]['@']['function']) && isset($xml['#']['BYPASS'][0]['@']['message'])) {
1111         $function = $xml['#']['BYPASS'][0]['@']['function'];
1112         $message  = $xml['#']['BYPASS'][0]['@']['message'];
1113     /// Look for the function
1114         if (function_exists($function)) {
1115         /// Call it, and if bypass = true is returned, apply meesage
1116             if ($function($result)) {
1117             /// We only set the bypass message if the function itself hasn't defined it before
1118                 if (empty($result->getBypassStr)) {
1119                     if (isset($xml['#']['BYPASS'][0]['@']['plugin'])) {
1120                         $result->setBypassStr(array($message, $xml['#']['BYPASS'][0]['@']['plugin']));
1121                     } else {
1122                         $result->setBypassStr($message);
1123                     }
1124                 }
1125             }
1126         }
1127     }
1130 /**
1131  * This function will post-process the result record by executing the specified
1132  * function, modifying it as necessary, also a custom message will be added
1133  * to the result object to be printed by the display layer.
1134  * Every restrict function must be defined in this file and it'll return
1135  * true/false to decide if the original test is restricted or no. Also
1136  * such restrict functions are able to directly handling the result object
1137  * although it should be only under exceptional conditions.
1138  *
1139  * @param string xmldata containing the restrict data
1140  * @param object result object to be updated
1141  * @return void
1142  */
1143 function process_environment_restrict($xml, &$result) {
1145 /// Only try to restrict if we were not in error and it was required
1146     if (!$result->getStatus() || $result->getLevel() == 'optional') {
1147         return;
1148     }
1149 /// It there is restrict info (function and message)
1150     if (is_array($xml['#']) && isset($xml['#']['RESTRICT'][0]['@']['function']) && isset($xml['#']['RESTRICT'][0]['@']['message'])) {
1151         $function = $xml['#']['RESTRICT'][0]['@']['function'];
1152         $message  = $xml['#']['RESTRICT'][0]['@']['message'];
1153     /// Look for the function
1154         if (function_exists($function)) {
1155         /// Call it, and if restrict = true is returned, apply meesage
1156             if ($function($result)) {
1157             /// We only set the restrict message if the function itself hasn't defined it before
1158                 if (empty($result->getRestrictStr)) {
1159                     if (isset($xml['#']['RESTRICT'][0]['@']['plugin'])) {
1160                         $result->setRestrictStr(array($message, $xml['#']['RESTRICT'][0]['@']['plugin']));
1161                     } else {
1162                         $result->setRestrictStr($message);
1163                     }
1164                 }
1165             }
1166         }
1167     }
1170 /**
1171  * This function will detect if there is some message available to be added to the
1172  * result in order to clarify enviromental details.
1173  *
1174  * @uses INCORRECT_FEEDBACK_FOR_REQUIRED
1175  * @uses INCORRECT_FEEDBACK_FOR_OPTIONAL
1176  * @param string xmldata containing the feedback data
1177  * @param object reult object to be updated
1178  */
1179 function process_environment_messages($xml, &$result) {
1181 /// If there is feedback info
1182     if (is_array($xml['#']) && isset($xml['#']['FEEDBACK'][0]['#'])) {
1183         $feedbackxml = $xml['#']['FEEDBACK'][0]['#'];
1185         // Detect some incorrect feedback combinations.
1186         if ($result->getLevel() == 'required' and isset($feedbackxml['ON_CHECK'])) {
1187             $result->setStatus(false);
1188             $result->setErrorCode(INCORRECT_FEEDBACK_FOR_REQUIRED);
1189         } else if ($result->getLevel() == 'optional' and isset($feedbackxml['ON_ERROR'])) {
1190             $result->setStatus(false);
1191             $result->setErrorCode(INCORRECT_FEEDBACK_FOR_OPTIONAL);
1192         }
1194         if (!$result->status and $result->getLevel() == 'required') {
1195             if (isset($feedbackxml['ON_ERROR'][0]['@']['message'])) {
1196                 if (isset($feedbackxml['ON_ERROR'][0]['@']['plugin'])) {
1197                     $result->setFeedbackStr(array($feedbackxml['ON_ERROR'][0]['@']['message'], $feedbackxml['ON_ERROR'][0]['@']['plugin']));
1198                 } else {
1199                     $result->setFeedbackStr($feedbackxml['ON_ERROR'][0]['@']['message']);
1200                 }
1201             }
1202         } else if (!$result->status and $result->getLevel() == 'optional') {
1203             if (isset($feedbackxml['ON_CHECK'][0]['@']['message'])) {
1204                 if (isset($feedbackxml['ON_CHECK'][0]['@']['plugin'])) {
1205                     $result->setFeedbackStr(array($feedbackxml['ON_CHECK'][0]['@']['message'], $feedbackxml['ON_CHECK'][0]['@']['plugin']));
1206                 } else {
1207                     $result->setFeedbackStr($feedbackxml['ON_CHECK'][0]['@']['message']);
1208                 }
1209             }
1210         } else {
1211             if (isset($feedbackxml['ON_OK'][0]['@']['message'])) {
1212                 if (isset($feedbackxml['ON_OK'][0]['@']['plugin'])) {
1213                     $result->setFeedbackStr(array($feedbackxml['ON_OK'][0]['@']['message'], $feedbackxml['ON_OK'][0]['@']['plugin']));
1214                 } else {
1215                     $result->setFeedbackStr($feedbackxml['ON_OK'][0]['@']['message']);
1216                 }
1217             }
1218         }
1219     }
1223 //--- Helper Class to return results to caller ---//
1226 /**
1227  * Helper Class to return results to caller
1228  *
1229  * @copyright 1999 onwards Martin Dougiamas  {@link http://moodle.com}
1230  * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
1231  * @package moodlecore
1232  */
1233 class environment_results {
1234     /**
1235      * @var string Which are we checking (database, php, php_extension, php_extension)
1236      */
1237     var $part;
1238     /**
1239      * @var bool true means the test passed and all is OK. false means it failed.
1240      */
1241     var $status;
1242     /**
1243      * @var integer See constants at the beginning of the file
1244      */
1245     var $error_code;
1246     /**
1247      * @var string required/optional
1248      */
1249     var $level;
1250     /**
1251      * @var string current version detected
1252      */
1253     var $current_version;
1254     /**
1255      * @var string version needed
1256      */
1257     var $needed_version;
1258     /**
1259      * @var string Aux. info (DB vendor, library...)
1260      */
1261     var $info;
1262     /**
1263      * @var string String to show on error|on check|on ok
1264      */
1265     var $feedback_str;
1266     /**
1267      * @var string String to show if some bypass has happened
1268      */
1269     var $bypass_str;
1270     /**
1271      * @var string String to show if some restrict has happened
1272      */
1273     var $restrict_str;
1274     /**
1275      * @var string|null full plugin name or null if main environment
1276      */
1277     var $plugin = null;
1278     /**
1279      * Constructor of the environment_result class. Just set default values
1280      *
1281      * @param string $part
1282      */
1283     public function __construct($part) {
1284         $this->part=$part;
1285         $this->status=false;
1286         $this->error_code=NO_ERROR;
1287         $this->level='required';
1288         $this->current_version='';
1289         $this->needed_version='';
1290         $this->info='';
1291         $this->feedback_str='';
1292         $this->bypass_str='';
1293         $this->restrict_str='';
1294     }
1296     /**
1297      * Old syntax of class constructor. Deprecated in PHP7.
1298      *
1299      * @deprecated since Moodle 3.1
1300      */
1301     public function environment_results($part) {
1302         debugging('Use of class name as constructor is deprecated', DEBUG_DEVELOPER);
1303         self::__construct($part);
1304     }
1306     /**
1307      * Set the status
1308      *
1309      * @param bool $testpassed true means the test passed and all is OK. false means it failed.
1310      */
1311     function setStatus($testpassed) {
1312         $this->status = $testpassed;
1313         if ($testpassed) {
1314             $this->setErrorCode(NO_ERROR);
1315         }
1316     }
1318     /**
1319      * Set the error_code
1320      *
1321      * @param integer $error_code the error code (see constants above)
1322      */
1323     function setErrorCode($error_code) {
1324         $this->error_code=$error_code;
1325     }
1327     /**
1328      * Set the level
1329      *
1330      * @param string $level the level (required, optional)
1331      */
1332     function setLevel($level) {
1333         $this->level=$level;
1334     }
1336     /**
1337      * Set the current version
1338      *
1339      * @param string $current_version the current version
1340      */
1341     function setCurrentVersion($current_version) {
1342         $this->current_version=$current_version;
1343     }
1345     /**
1346      * Set the needed version
1347      *
1348      * @param string $needed_version the needed version
1349      */
1350     function setNeededVersion($needed_version) {
1351         $this->needed_version=$needed_version;
1352     }
1354     /**
1355      * Set the auxiliary info
1356      *
1357      * @param string $info the auxiliary info
1358      */
1359     function setInfo($info) {
1360         $this->info=$info;
1361     }
1363     /**
1364      * Set the feedback string
1365      *
1366      * @param mixed $str the feedback string that will be fetched from the admin lang file.
1367      *                  pass just the string or pass an array of params for get_string
1368      *                  You always should put your string in admin.php but a third param is useful
1369      *                  to pass an $a object / string to get_string
1370      */
1371     function setFeedbackStr($str) {
1372         $this->feedback_str=$str;
1373     }
1376     /**
1377      * Set the bypass string
1378      *
1379      * @param string $str the bypass string that will be fetched from the admin lang file.
1380      *                  pass just the string or pass an array of params for get_string
1381      *                  You always should put your string in admin.php but a third param is useful
1382      *                  to pass an $a object / string to get_string
1383      */
1384     function setBypassStr($str) {
1385         $this->bypass_str=$str;
1386     }
1388     /**
1389      * Set the restrict string
1390      *
1391      * @param string $str the restrict string that will be fetched from the admin lang file.
1392      *                  pass just the string or pass an array of params for get_string
1393      *                  You always should put your string in admin.php but a third param is useful
1394      *                  to pass an $a object / string to get_string
1395      */
1396     function setRestrictStr($str) {
1397         $this->restrict_str=$str;
1398     }
1400     /**
1401      * Get the status
1402      *
1403      * @return bool true means the test passed and all is OK. false means it failed.
1404      */
1405     function getStatus() {
1406         return $this->status;
1407     }
1409     /**
1410      * Get the error code
1411      *
1412      * @return integer error code
1413      */
1414     function getErrorCode() {
1415         return $this->error_code;
1416     }
1418     /**
1419      * Get the level
1420      *
1421      * @return string level
1422      */
1423     function getLevel() {
1424         return $this->level;
1425     }
1427     /**
1428      * Get the current version
1429      *
1430      * @return string current version
1431      */
1432     function getCurrentVersion() {
1433         return $this->current_version;
1434     }
1436     /**
1437      * Get the needed version
1438      *
1439      * @return string needed version
1440      */
1441     function getNeededVersion() {
1442         return $this->needed_version;
1443     }
1445     /**
1446      * Get the aux info
1447      *
1448      * @return string info
1449      */
1450     function getInfo() {
1451         return $this->info;
1452     }
1454     /**
1455      * Get the part this result belongs to
1456      *
1457      * @return string part
1458      */
1459     function getPart() {
1460         return $this->part;
1461     }
1463     /**
1464      * Get the feedback string
1465      *
1466      * @return mixed feedback string (can be an array of params for get_string or a single string to fetch from
1467      *                  admin.php lang file).
1468      */
1469     function getFeedbackStr() {
1470         return $this->feedback_str;
1471     }
1473     /**
1474      * Get the bypass string
1475      *
1476      * @return mixed bypass string (can be an array of params for get_string or a single string to fetch from
1477      *                  admin.php lang file).
1478      */
1479     function getBypassStr() {
1480         return $this->bypass_str;
1481     }
1483     /**
1484      * Get the restrict string
1485      *
1486      * @return mixed restrict string (can be an array of params for get_string or a single string to fetch from
1487      *                  admin.php lang file).
1488      */
1489     function getRestrictStr() {
1490         return $this->restrict_str;
1491     }
1493     /**
1494      * @todo Document this function
1495      *
1496      * @param mixed $string params for get_string, either a string to fetch from admin.php or an array of
1497      *                       params for get_string.
1498      * @param string $class css class(es) for message.
1499      * @return string feedback string fetched from lang file wrapped in p tag with class $class or returns
1500      *                              empty string if $string is empty.
1501      */
1502     function strToReport($string, $class){
1503         if (!empty($string)){
1504             if (is_array($string)){
1505                 $str = call_user_func_array('get_string', $string);
1506             } else {
1507                 $str = get_string($string, 'admin');
1508             }
1509             return '<p class="'.$class.'">'.$str.'</p>';
1510         } else {
1511             return '';
1512         }
1513     }
1515     /**
1516      * Get plugin name.
1517      *
1518      * @return string plugin name
1519      */
1520     function getPluginName() {
1521         if ($this->plugin) {
1522             $manager = core_plugin_manager::instance();
1523             list($plugintype, $pluginname) = core_component::normalize_component($this->plugin);
1524             return $manager->plugintype_name($plugintype) . ' / ' . $manager->plugin_name($this->plugin);
1525         } else {
1526             return '';
1527         }
1528     }
1531 /// Here all the restrict functions are coded to be used by the environment
1532 /// checker. All those functions will receive the result object and will
1533 /// return it modified as needed (status and bypass string)
1535 /**
1536  * @param array $element the element from the environment.xml file that should have
1537  *      either a level="required" or level="optional" attribute.
1538  * @return string "required" or "optional".
1539  */
1540 function get_level($element) {
1541     $level = 'required';
1542     if (isset($element['@']['level'])) {
1543         $level = $element['@']['level'];
1544         if (!in_array($level, array('required', 'optional'))) {
1545             debugging('The level of a check in the environment.xml file must be "required" or "optional".', DEBUG_DEVELOPER);
1546             $level = 'required';
1547         }
1548     } else {
1549         debugging('Checks in the environment.xml file must have a level="required" or level="optional" attribute.', DEBUG_DEVELOPER);
1550     }
1551     return $level;
1554 /**
1555  * Once the result has been determined, look in the XML for any
1556  * messages, or other things that should be done depending on the outcome.
1557  *
1558  * @param array $element the element from the environment.xml file which
1559  *      may have children defining what should be done with the outcome.
1560  * @param object $result the result of the test, which may be modified by
1561  *      this function as specified in the XML.
1562  */
1563 function process_environment_result($element, &$result) {
1564 /// Process messages, modifying the $result if needed.
1565     process_environment_messages($element, $result);
1566 /// Process bypass, modifying $result if needed.
1567     process_environment_bypass($element, $result);
1568 /// Process restrict, modifying $result if needed.
1569     process_environment_restrict($element, $result);
1572 /**
1573  * Check if the current PHP version is greater than or equal to
1574  * PHP version 7.
1575  *
1576  * @param object $result an environment_results instance
1577  * @return bool result of version check
1578  */
1579 function restrict_php_version_7(&$result) {
1580     return restrict_php_version($result, '7');
1583 /**
1584  * Check if the current PHP version is greater than or equal to an
1585  * unsupported version.
1586  *
1587  * @param object $result an environment_results instance
1588  * @param string $version the version of PHP that can't be used
1589  * @return bool result of version check
1590  */
1591 function restrict_php_version(&$result, $version) {
1593     // Get the current PHP version.
1594     $currentversion = normalize_version(phpversion());
1596     // Confirm we're using a supported PHP version.
1597     if (version_compare($currentversion, $version, '<')) {
1598         // Everything is ok, the restriction doesn't apply.
1599         return false;
1600     } else {
1601         // We're using an unsupported PHP version, apply restriction.
1602         return true;
1603     }
1606 /**
1607  * Check if the current PHP version is greater than or equal to
1608  * PHP version 7.1.
1609  *
1610  * @param object $result an environment_results instance
1611  * @return bool result of version check
1612  */
1613 function restrict_php_version_71(&$result) {
1614     return restrict_php_version($result, '7.1');
1617 /**
1618  * Check if the current PHP version is greater than or equal to
1619  * PHP version 7.2.
1620  *
1621  * @param object $result an environment_results instance
1622  * @return bool result of version check
1623  */
1624 function restrict_php_version_72(&$result) {
1625     return restrict_php_version($result, '7.2');
1628 /**
1629  * Check if the current PHP version is greater than or equal to
1630  * PHP version 7.3.
1631  *
1632  * @param object $result an environment_results instance
1633  * @return bool result of version check
1634  */
1635 function restrict_php_version_73(&$result) {
1636     return restrict_php_version($result, '7.3');
1639 /**
1640  * Check if the current PHP version is greater than or equal to
1641  * PHP version 7.4.
1642  *
1643  * @param object $result an environment_results instance
1644  * @return bool result of version check
1645  */
1646 function restrict_php_version_74(&$result) {
1647     return restrict_php_version($result, '7.4');