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