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