Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
<?php
2
 
3
// This file is part of Moodle - http://moodle.org/
4
//
5
// Moodle is free software: you can redistribute it and/or modify
6
// it under the terms of the GNU General Public License as published by
7
// the Free Software Foundation, either version 3 of the License, or
8
// (at your option) any later version.
9
//
10
// Moodle is distributed in the hope that it will be useful,
11
// but WITHOUT ANY WARRANTY; without even the implied warranty of
12
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
// GNU General Public License for more details.
14
//
15
// You should have received a copy of the GNU General Public License
16
// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
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
 *
26
 * @copyright  (C) 2001-3001 Eloy Lafuente (stronk7) {@link http://contiento.com}
27
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
28
 * @package    core
29
 * @subpackage admin
30
 */
31
 
32
defined('MOODLE_INTERNAL') || die();
33
 
34
/// Add required files
35
/**
36
 * Include the necessary
37
 */
38
    require_once($CFG->libdir.'/xmlize.php');
39
 
40
/// Define a bunch of XML processing errors
41
    /** XML Processing Error */
42
    define('NO_ERROR',                           0);
43
    /** XML Processing Error */
44
    define('NO_VERSION_DATA_FOUND',              1);
45
    /** XML Processing Error */
46
    define('NO_DATABASE_SECTION_FOUND',          2);
47
    /** XML Processing Error */
48
    define('NO_DATABASE_VENDORS_FOUND',          3);
49
    /** XML Processing Error */
50
    define('NO_DATABASE_VENDOR_MYSQL_FOUND',     4);
51
    /** XML Processing Error */
52
    define('NO_DATABASE_VENDOR_POSTGRES_FOUND',  5);
53
    /** XML Processing Error */
54
    define('NO_PHP_SECTION_FOUND',               6);
55
    /** XML Processing Error */
56
    define('NO_PHP_VERSION_FOUND',               7);
57
    /** XML Processing Error */
58
    define('NO_PHP_EXTENSIONS_SECTION_FOUND',    8);
59
    /** XML Processing Error */
60
    define('NO_PHP_EXTENSIONS_NAME_FOUND',       9);
61
    /** XML Processing Error */
62
    define('NO_DATABASE_VENDOR_VERSION_FOUND',  10);
63
    /** XML Processing Error */
64
    define('NO_UNICODE_SECTION_FOUND',          11);
65
    /** XML Processing Error */
66
    define('NO_CUSTOM_CHECK_FOUND',             12);
67
    /** XML Processing Error */
68
    define('CUSTOM_CHECK_FILE_MISSING',         13);
69
    /** XML Processing Error */
70
    define('CUSTOM_CHECK_FUNCTION_MISSING',     14);
71
    /** XML Processing Error */
72
    define('NO_PHP_SETTINGS_NAME_FOUND',        15);
73
    /** XML Processing Error */
74
    define('INCORRECT_FEEDBACK_FOR_REQUIRED',   16);
75
    /** XML Processing Error */
76
    define('INCORRECT_FEEDBACK_FOR_OPTIONAL',   17);
77
 
78
/// Define algorithm used to select the xml file
79
    /** To select the newer file available to perform checks */
80
    define('ENV_SELECT_NEWER',                   0);
81
    /** To enforce the use of the file under dataroot */
82
    define('ENV_SELECT_DATAROOT',                1);
83
    /** To enforce the use of the file under admin (release) */
84
    define('ENV_SELECT_RELEASE',                 2);
85
 
86
/**
87
 * This function checks all the requirements defined in environment.xml.
88
 *
89
 * @param string $version version to check.
90
 * @param int $env_select one of ENV_SELECT_NEWER | ENV_SELECT_DATAROOT | ENV_SELECT_RELEASE decide xml to use. Default ENV_SELECT_NEWER (BC)
91
 * @return array with two elements. The first element true/false, depending on
92
 *      on whether the check passed. The second element is an array of environment_results
93
 *      objects that has detailed information about the checks and which ones passed.
94
 */
95
function check_moodle_environment($version, $env_select = ENV_SELECT_NEWER) {
96
    if ($env_select != ENV_SELECT_NEWER and $env_select != ENV_SELECT_DATAROOT and $env_select != ENV_SELECT_RELEASE) {
97
        throw new coding_exception('Incorrect value of $env_select parameter');
98
    }
99
 
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
    }
104
 
105
/// Perform all the checks
106
    if (!$environment_results = environment_check($version, $env_select)) {
107
        return array(false, array());
108
    }
109
 
110
/// Iterate over all the results looking for some error in required items
111
/// or some error_code
112
    $result = true;
113
    foreach ($environment_results as $environment_result) {
114
        if (!$environment_result->getStatus() && $environment_result->getLevel() == 'required'
115
          && !$environment_result->getBypassStr()) {
116
            $result = false; // required item that is not bypased
117
        } else if ($environment_result->getStatus() && $environment_result->getLevel() == 'required'
118
          && $environment_result->getRestrictStr()) {
119
            $result = false; // required item that is restricted
120
        } else if ($environment_result->getErrorCode()) {
121
            $result = false;
122
        }
123
    }
124
 
125
    return array($result, $environment_results);
126
}
127
 
128
 
129
/**
130
 * Returns array of critical errors in plain text format
131
 * @param array $environment_results array of results gathered
132
 * @return array errors
133
 */
134
function environment_get_errors($environment_results) {
135
    global $CFG;
136
    $errors = array();
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
        $plugin = $environment_result->getPluginName();
144
        $error_code = $environment_result->getErrorCode();
145
 
146
        $a = new stdClass();
147
        if ($error_code) {
148
            $a->error_code = $error_code;
149
            $errors[] = array($info, get_string('environmentxmlerror', 'admin', $a));
150
            return $errors;
151
        }
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
174
        $rec = new stdClass();
175
        if ($rec->needed = $environment_result->getNeededVersion()) {
176
            $rec->current = $environment_result->getCurrentVersion();
177
            if ($environment_result->getLevel() == 'required') {
178
                $stringtouse = 'environmentrequireversion';
179
            } else {
180
                $stringtouse = 'environmentrecommendversion';
181
            }
182
        // We are checking installed & enabled things
183
        } else if ($environment_result->getPart() == 'custom_check') {
184
            if ($environment_result->getLevel() == 'required') {
185
                $stringtouse = 'environmentrequirecustomcheck';
186
            } else {
187
                $stringtouse = 'environmentrecommendcustomcheck';
188
            }
189
        } else if ($environment_result->getPart() == 'php_setting') {
190
            if ($status) {
191
                $stringtouse = 'environmentsettingok';
192
            } else if ($environment_result->getLevel() == 'required') {
193
                $stringtouse = 'environmentmustfixsetting';
194
            } else {
195
                $stringtouse = 'environmentshouldfixsetting';
196
            }
197
        } else {
198
            if ($environment_result->getLevel() == 'required') {
199
                $stringtouse = 'environmentrequireinstall';
200
            } else {
201
                $stringtouse = 'environmentrecommendinstall';
202
            }
203
        }
204
        $report = get_string($stringtouse, 'admin', $rec);
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
 
213
        if ($plugin === '') {
214
            $report = '[' . get_string('coresystem') . '] ' . $report;
215
        } else {
216
            $report = '[' . $plugin . '] ' . $report;
217
        }
218
 
219
        $report .= ' - ' . html_to_text($feedbacktext);
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
 
232
/**
233
 * This function will normalize any version to just a serie of numbers
234
 * separated by dots. Everything else will be removed.
235
 *
236
 * @param string $version the original version
237
 * @return string the normalized version
238
 */
239
function normalize_version($version) {
240
 
241
/// 1.9 Beta 2 should be read 1.9 on enviromental checks, not 1.9.2
242
/// we can discard everything after the first space
243
    $version = trim($version);
244
    $versionarr = explode(" ",$version);
245
    if (!empty($versionarr)) {
246
        $version = $versionarr[0];
247
    }
248
/// Replace everything but numbers and dots by dots
249
    $version = preg_replace('/[^\.\d]/', '.', $version);
250
/// Combine multiple dots in one
251
    $version = preg_replace('/(\.{2,})/', '.', $version);
252
/// Trim possible leading and trailing dots
253
    $version = trim($version, '.');
254
 
255
    return $version;
256
}
257
 
258
 
259
/**
260
 * This function will load the environment.xml file and xmlize it
261
 *
262
 * @staticvar array $data
263
 * @uses ENV_SELECT_NEWER
264
 * @uses ENV_SELECT_DATAROOT
265
 * @uses ENV_SELECT_RELEASE
266
 * @param int|string $env_select one of ENV_SELECT_NEWER | ENV_SELECT_DATAROOT | ENV_SELECT_RELEASE decide xml to use. String means plugin name.
267
 * @return mixed the xmlized structure or false on error
268
 */
269
function load_environment_xml($env_select=ENV_SELECT_NEWER) {
270
 
271
    global $CFG;
272
 
273
    static $data = array(); // Only load and xmlize once by request.
274
 
275
    if (isset($data[$env_select])) {
276
        return $data[$env_select];
277
    }
278
    $contents = false;
279
 
280
    if (is_numeric($env_select)) {
281
        $file = $CFG->dataroot.'/environment/environment.xml';
282
        $internalfile = $CFG->dirroot.'/'.$CFG->admin.'/environment.xml';
283
        switch ($env_select) {
284
            case ENV_SELECT_NEWER:
285
                if (!is_file($file) || !is_readable($file) || filemtime($file) < filemtime($internalfile) ||
286
                    !$contents = file_get_contents($file)) {
287
                    /// Fallback to fixed $CFG->admin/environment.xml
288
                    if (!is_file($internalfile) || !is_readable($internalfile) || !$contents = file_get_contents($internalfile)) {
289
                        $contents = false;
290
                    }
291
                }
292
                break;
293
            case ENV_SELECT_DATAROOT:
294
                if (!is_file($file) || !is_readable($file) || !$contents = file_get_contents($file)) {
295
                    $contents = false;
296
                }
297
                break;
298
            case ENV_SELECT_RELEASE:
299
                if (!is_file($internalfile) || !is_readable($internalfile) || !$contents = file_get_contents($internalfile)) {
300
                    $contents = false;
301
                }
302
                break;
303
        }
304
    } else {
305
        if ($plugindir = core_component::get_component_directory($env_select)) {
306
            $pluginfile = "$plugindir/environment.xml";
307
            if (!is_file($pluginfile) || !is_readable($pluginfile) || !$contents = file_get_contents($pluginfile)) {
308
                $contents = false;
309
            }
310
        }
311
    }
312
    // XML the whole file.
313
    if ($contents !== false) {
314
        $contents = xmlize($contents);
315
    }
316
 
317
    $data[$env_select] = $contents;
318
 
319
    return $data[$env_select];
320
}
321
 
322
 
323
/**
324
 * This function will return the list of Moodle versions available
325
 *
326
 * @return array of versions
327
 */
328
function get_list_of_environment_versions($contents) {
329
    $versions = array();
330
 
331
    if (isset($contents['COMPATIBILITY_MATRIX']['#']['MOODLE'])) {
332
        foreach ($contents['COMPATIBILITY_MATRIX']['#']['MOODLE'] as $version) {
333
            $versions[] = $version['@']['version'];
334
        }
335
    }
336
 
337
    if (isset($contents['COMPATIBILITY_MATRIX']['#']['PLUGIN'])) {
338
        $versions[] = 'all';
339
    }
340
 
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
348
 *
349
 * @param string $version top version from which we start to look backwards
350
 * @param int $env_select one of ENV_SELECT_NEWER | ENV_SELECT_DATAROOT | ENV_SELECT_RELEASE decide xml to use.
351
 * @return string|bool string more recent version or false if not found
352
 */
353
function get_latest_version_available($version, $env_select) {
354
    if ($env_select != ENV_SELECT_NEWER and $env_select != ENV_SELECT_DATAROOT and $env_select != ENV_SELECT_RELEASE) {
355
        throw new coding_exception('Incorrect value of $env_select parameter');
356
    }
357
 
358
/// Normalize the version requested
359
    $version = normalize_version($version);
360
 
361
/// Load xml file
362
    if (!$contents = load_environment_xml($env_select)) {
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
371
    if (in_array($version, $versions, true)) {
372
        return $version;
373
    } else {
374
        $found_version = false;
375
    /// Not exact match, so we are going to iterate over the list searching
376
    /// for the latest version before the requested one
377
        foreach ($versions as $arrversion) {
378
            if (version_compare($arrversion, $version, '<')) {
379
                $found_version = $arrversion;
380
            }
381
        }
382
    }
383
 
384
    return $found_version;
385
}
386
 
387
 
388
/**
389
 * This function will return the xmlized data belonging to one Moodle version
390
 *
391
 * @param string $version top version from which we start to look backwards
392
 * @param int|string $env_select one of ENV_SELECT_NEWER | ENV_SELECT_DATAROOT | ENV_SELECT_RELEASE decide xml to use. String means plugin name.
393
 * @return mixed the xmlized structure or false on error
394
 */
395
function get_environment_for_version($version, $env_select) {
396
 
397
/// Normalize the version requested
398
    $version = normalize_version($version);
399
 
400
/// Load xml file
401
    if (!$contents = load_environment_xml($env_select)) {
402
        return false;
403
    }
404
 
405
/// Detect available versions
406
    if (!$versions = get_list_of_environment_versions($contents)) {
407
        return false;
408
    }
409
 
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
 
418
/// If the version requested is available
419
    if (!in_array($version, $versions, true)) {
420
        return false;
421
    }
422
 
423
/// We now we have it. Extract from full contents.
424
    $fl_arr = array_flip($versions);
425
 
426
    return $contents['COMPATIBILITY_MATRIX']['#']['MOODLE'][$fl_arr[$version]];
427
}
428
 
429
/**
430
 * Checks if a plugin tag has a name attribute and it matches the plugin being tested.
431
 *
432
 * @param string $plugin the name of the plugin.
433
 * @param array $pluginxml the xmlised structure for the plugin tag being tested.
434
 * @return boolean true if the name attribute exists and matches the plugin being tested.
435
 */
436
function environment_verify_plugin($plugin, $pluginxml) {
437
    if (!isset($pluginxml['@']['name']) || $pluginxml['@']['name'] != $plugin) {
438
        return false;
439
    }
440
    return true;
441
}
442
 
443
/**
444
 * This function will check for everything (DB, PHP and PHP extensions for now)
445
 * returning an array of environment_result objects.
446
 *
447
 * @global object
448
 * @param string $version xml version we are going to use to test this server
449
 * @param int $env_select one of ENV_SELECT_NEWER | ENV_SELECT_DATAROOT | ENV_SELECT_RELEASE decide xml to use.
450
 * @return environment_results[] array of results encapsulated in one environment_result object
451
 */
452
function environment_check($version, $env_select) {
453
    global $CFG;
454
 
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
 
459
/// Normalize the version requested
460
    $version = normalize_version($version);
461
 
462
    $results = array(); //To store all the results
463
 
464
/// Only run the moodle versions checker on upgrade, not on install
465
    if (!empty($CFG->version)) {
466
        $results[] = environment_check_moodle($version, $env_select);
467
    }
468
    $results[] = environment_check_unicode($version, $env_select);
469
    $results[] = environment_check_database($version, $env_select);
470
    $results[] = environment_check_php($version, $env_select);
471
 
472
    if ($result = environment_check_pcre_unicode($version, $env_select)) {
473
        $results[] = $result;
474
    }
475
 
476
    $phpext_results = environment_check_php_extensions($version, $env_select);
477
    $results = array_merge($results, $phpext_results);
478
 
479
    $phpsetting_results = environment_check_php_settings($version, $env_select);
480
    $results = array_merge($results, $phpsetting_results);
481
 
482
    $custom_results = environment_custom_checks($version, $env_select);
483
    $results = array_merge($results, $custom_results);
484
 
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
 
537
    return $results;
538
}
539
 
540
 
541
/**
542
 * This function will check if php extensions requirements are satisfied
543
 *
544
 * @uses NO_VERSION_DATA_FOUND
545
 * @uses NO_PHP_EXTENSIONS_SECTION_FOUND
546
 * @uses NO_PHP_EXTENSIONS_NAME_FOUND
547
 * @param string $version xml version we are going to use to test this server
548
 * @param int|string $env_select one of ENV_SELECT_NEWER | ENV_SELECT_DATAROOT | ENV_SELECT_RELEASE decide xml to use. String means plugin name.
549
 * @return array array of results encapsulated in one environment_result object
550
 */
551
function environment_check_php_extensions($version, $env_select) {
552
 
553
    $results = array();
554
 
555
/// Get the enviroment version we need
556
    if (!$data = get_environment_for_version($version, $env_select)) {
557
    /// Error. No version data found
558
        $result = new environment_results('php_extension');
559
        $result->setStatus(false);
560
        $result->setErrorCode(NO_VERSION_DATA_FOUND);
561
        return array($result);
562
    }
563
 
564
/// Extract the php_extension part
565
    if (!isset($data['#']['PHP_EXTENSIONS']['0']['#']['PHP_EXTENSION'])) {
566
    /// Error. No PHP section found
567
        $result = new environment_results('php_extension');
568
        $result->setStatus(false);
569
        $result->setErrorCode(NO_PHP_EXTENSIONS_SECTION_FOUND);
570
        return array($result);
571
    }
572
/// Iterate over extensions checking them and creating the needed environment_results
573
    foreach($data['#']['PHP_EXTENSIONS']['0']['#']['PHP_EXTENSION'] as $extension) {
574
        $result = new environment_results('php_extension');
575
    /// Check for level
576
        $level = get_level($extension);
577
    /// Check for extension name
578
        if (!isset($extension['@']['name'])) {
579
            $result->setStatus(false);
580
            $result->setErrorCode(NO_PHP_EXTENSIONS_NAME_FOUND);
581
        } else {
582
            $extension_name = $extension['@']['name'];
583
        /// The name exists. Just check if it's an installed extension
584
            if (!extension_loaded($extension_name)) {
585
                $result->setStatus(false);
586
            } else {
587
                $result->setStatus(true);
588
            }
589
            $result->setLevel($level);
590
            $result->setInfo($extension_name);
591
        }
592
 
593
    /// Do any actions defined in the XML file.
594
        process_environment_result($extension, $result);
595
 
596
    /// Add the result to the array of results
597
        $results[] = $result;
598
    }
599
 
600
 
601
    return $results;
602
}
603
 
604
/**
605
 * This function will check if php extensions requirements are satisfied
606
 *
607
 * @uses NO_VERSION_DATA_FOUND
608
 * @uses NO_PHP_SETTINGS_NAME_FOUND
609
 * @param string $version xml version we are going to use to test this server
610
 * @param int|string $env_select one of ENV_SELECT_NEWER | ENV_SELECT_DATAROOT | ENV_SELECT_RELEASE decide xml to use. String means plugin name.
611
 * @return array array of results encapsulated in one environment_result object
612
 */
613
function environment_check_php_settings($version, $env_select) {
614
 
615
    $results = array();
616
 
617
/// Get the enviroment version we need
618
    if (!$data = get_environment_for_version($version, $env_select)) {
619
    /// Error. No version data found
620
        $result = new environment_results('php_setting');
621
        $result->setStatus(false);
622
        $result->setErrorCode(NO_VERSION_DATA_FOUND);
623
        $results[] = $result;
624
        return $results;
625
    }
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
 
684
/**
685
 * This function will do the custom checks.
686
 *
687
 * @uses CUSTOM_CHECK_FUNCTION_MISSING
688
 * @uses CUSTOM_CHECK_FILE_MISSING
689
 * @uses NO_CUSTOM_CHECK_FOUND
690
 * @param string $version xml version we are going to use to test this server.
691
 * @param int|string $env_select one of ENV_SELECT_NEWER | ENV_SELECT_DATAROOT | ENV_SELECT_RELEASE decide xml to use. String means plugin name.
692
 * @return array array of results encapsulated in environment_result objects.
693
 */
694
function environment_custom_checks($version, $env_select) {
695
    global $CFG;
696
 
697
    $results = array();
698
 
699
/// Get current Moodle version (release) for later compare
700
    $release = isset($CFG->release) ? $CFG->release : $version; /// In case $CFG fails (at install) use $version
701
    $current_version = normalize_version($release);
702
 
703
/// Get the enviroment version we need
704
    if (!$data = get_environment_for_version($version, $env_select)) {
705
    /// Error. No version data found - but this will already have been reported.
706
        return $results;
707
    }
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
723
        if (isset($check['@']['function'])) {
724
            $function = $check['@']['function'];
725
            $file = null;
726
            if (isset($check['@']['file'])) {
727
                $file = $CFG->dirroot . '/' . $check['@']['file'];
728
                if (is_readable($file)) {
729
                    include_once($file);
730
                }
731
            }
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);
744
                    $result->setInfo($function);
745
                    $result->setErrorCode(CUSTOM_CHECK_FUNCTION_MISSING);
746
                } else {
747
                    $result = null;
748
                }
749
            } else {
750
            /// Only show error for current version (where function MUST exist)
751
            /// else, we are performing custom checks against future versiosn
752
            /// and function MAY not exist, so it doesn't cause error, just skip
753
            /// custom check by returning null. MDL-15939
754
                if (version_compare($current_version, $version, '>=')) {
755
                    $result->setStatus(false);
756
                    $result->setInfo($function);
757
                    $result->setErrorCode(CUSTOM_CHECK_FILE_MISSING);
758
                } else {
759
                    $result = null;
760
                }
761
            }
762
        } else {
763
            $result->setStatus(false);
764
            $result->setErrorCode(NO_CUSTOM_CHECK_FOUND);
765
        }
766
 
767
        if (!is_null($result)) {
768
        /// Do any actions defined in the XML file.
769
            process_environment_result($check, $result);
770
 
771
        /// Add the result to the array of results
772
            $results[] = $result;
773
        }
774
    }
775
 
776
    return $results;
777
}
778
 
779
/**
780
 * This function will check if Moodle requirements are satisfied
781
 *
782
 * @uses NO_VERSION_DATA_FOUND
783
 * @param string $version xml version we are going to use to test this server
784
 * @param int|string $env_select one of ENV_SELECT_NEWER | ENV_SELECT_DATAROOT | ENV_SELECT_RELEASE decide xml to use. String means plugin name.
785
 * @return object results encapsulated in one environment_result object
786
 */
787
function environment_check_moodle($version, $env_select) {
788
 
789
    $result = new environment_results('moodle');
790
 
791
/// Get the enviroment version we need
792
    if (!$data = get_environment_for_version($version, $env_select)) {
793
    /// Error. No version data found
794
        $result->setStatus(false);
795
        $result->setErrorCode(NO_VERSION_DATA_FOUND);
796
        return $result;
797
    }
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
808
    $release = get_config('', 'release');
809
    $current_version = normalize_version($release);
810
    if (strpos($release, 'dev') !== false) {
811
        // When final version is required, dev is NOT enough so, at all effects
812
        // it's like we are running the previous version.
813
        $versionarr = explode('.', $current_version);
814
        if (isset($versionarr[1]) and $versionarr[1] > 0) {
815
            $versionarr[1]--;
816
            $current_version = implode('.', $versionarr);
817
        } else {
818
            $current_version = $current_version - 0.1;
819
        }
820
    }
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');
829
    $result->setCurrentVersion($release);
830
    $result->setNeededVersion($needed_version);
831
 
832
    return $result;
833
}
834
 
835
/**
836
 * This function will check if php requirements are satisfied
837
 *
838
 * @uses NO_VERSION_DATA_FOUND
839
 * @uses NO_PHP_SECTION_FOUND
840
 * @uses NO_PHP_VERSION_FOUND
841
 * @param string $version xml version we are going to use to test this server
842
 * @param int|string $env_select one of ENV_SELECT_NEWER | ENV_SELECT_DATAROOT | ENV_SELECT_RELEASE decide xml to use. String means plugin name.
843
 * @return object results encapsulated in one environment_result object
844
 */
845
function environment_check_php($version, $env_select) {
846
 
847
    $result = new environment_results('php');
848
 
849
/// Get the enviroment version we need
850
    if (!$data = get_environment_for_version($version, $env_select)) {
851
    /// Error. No version data found
852
        $result->setStatus(false);
853
        $result->setErrorCode(NO_VERSION_DATA_FOUND);
854
        return $result;
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
865
        $level = get_level($data['#']['PHP']['0']);
866
        if (!isset($data['#']['PHP']['0']['@']['version'])) {
867
            $result->setStatus(false);
868
            $result->setErrorCode(NO_PHP_VERSION_FOUND);
869
            return $result;
870
        } else {
871
            $needed_version = $data['#']['PHP']['0']['@']['version'];
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);
883
    }
884
    $result->setLevel($level);
885
    $result->setCurrentVersion($current_version);
886
    $result->setNeededVersion($needed_version);
887
 
888
/// Do any actions defined in the XML file.
889
    process_environment_result($data['#']['PHP'][0], $result);
890
 
891
    return $result;
892
}
893
 
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
897
 * @param int|string $env_select one of ENV_SELECT_NEWER | ENV_SELECT_DATAROOT | ENV_SELECT_RELEASE decide xml to use. String means plugin name.
898
 * @return ?environment_results results encapsulated in one environment_result object, null if irrelevant
899
 */
900
function 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
}
934
 
935
/**
936
 * This function will check if unicode database requirements are satisfied
937
 *
938
 * @uses NO_VERSION_DATA_FOUND
939
 * @uses NO_UNICODE_SECTION_FOUND
940
 * @param string $version xml version we are going to use to test this server
941
 * @param int|string $env_select one of ENV_SELECT_NEWER | ENV_SELECT_DATAROOT | ENV_SELECT_RELEASE decide xml to use. String means plugin name.
942
 * @return object results encapsulated in one environment_result object
943
 */
944
function environment_check_unicode($version, $env_select) {
945
    global $DB;
946
 
947
    $result = new environment_results('unicode');
948
 
949
    /// Get the enviroment version we need
950
    if (!$data = get_environment_for_version($version, $env_select)) {
951
    /// Error. No version data found
952
        $result->setStatus(false);
953
        $result->setErrorCode(NO_VERSION_DATA_FOUND);
954
        return $result;
955
    }
956
 
957
    /// Extract the unicode part
958
 
959
    if (!isset($data['#']['UNICODE'])) {
960
    /// Error. No UNICODE section found
961
        $result->setStatus(false);
962
        $result->setErrorCode(NO_UNICODE_SECTION_FOUND);
963
        return $result;
964
    } else {
965
    /// Extract level
966
        $level = get_level($data['#']['UNICODE']['0']);
967
    }
968
 
969
    if (!$unicodedb = $DB->setup_is_unicodedb()) {
970
        $result->setStatus(false);
971
    } else {
972
        $result->setStatus(true);
973
    }
974
 
975
    $result->setLevel($level);
976
 
977
/// Do any actions defined in the XML file.
978
    process_environment_result($data['#']['UNICODE'][0], $result);
979
 
980
    return $result;
981
}
982
 
983
/**
984
 * This function will check if database requirements are satisfied
985
 *
986
 * @uses NO_VERSION_DATA_FOUND
987
 * @uses NO_DATABASE_SECTION_FOUND
988
 * @uses NO_DATABASE_VENDORS_FOUND
989
 * @uses NO_DATABASE_VENDOR_MYSQL_FOUND
990
 * @uses NO_DATABASE_VENDOR_POSTGRES_FOUND
991
 * @uses NO_DATABASE_VENDOR_VERSION_FOUND
992
 * @param string $version xml version we are going to use to test this server
993
 * @param int|string $env_select one of ENV_SELECT_NEWER | ENV_SELECT_DATAROOT | ENV_SELECT_RELEASE decide xml to use. String means plugin name.
994
 * @return object results encapsulated in one environment_result object
995
 */
996
function environment_check_database($version, $env_select) {
997
 
998
    global $DB;
999
 
1000
    $result = new environment_results('database');
1001
 
1002
    $vendors = array();  //Array of vendors in version
1003
 
1004
/// Get the enviroment version we need
1005
    if (!$data = get_environment_for_version($version, $env_select)) {
1006
    /// Error. No version data found
1007
        $result->setStatus(false);
1008
        $result->setErrorCode(NO_VERSION_DATA_FOUND);
1009
        return $result;
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
1020
        $level = get_level($data['#']['DATABASE']['0']);
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'];
1034
                $vendorsxml[$vendor['@']['name']] = $vendor;
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)
1052
    $current_vendor = $DB->get_dbvendor();
1053
 
1054
    $dbinfo = $DB->get_server_info();
1055
    $current_version = normalize_version($dbinfo['version']);
1056
    $needed_version = $vendors[$current_vendor];
1057
 
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
 
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
 
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);
1083
    }
1084
    $result->setLevel($level);
1085
    $result->setCurrentVersion($current_version);
1086
    $result->setNeededVersion($needed_version);
1087
    $result->setInfo($current_vendor . ' (' . $dbinfo['description'] . ')');
1088
 
1089
/// Do any actions defined in the XML file.
1090
    process_environment_result($vendorsxml[$current_vendor], $result);
1091
 
1092
    return $result;
1093
 
1094
}
1095
 
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 array $xml xml containing the bypass data
1106
 * @param environment_results $result object to be updated
1107
 * @return void
1108
 */
1109
function process_environment_bypass($xml, &$result) {
1110
 
1111
/// Only try to bypass if we were in error and it was required
1112
    if ($result->getStatus() || $result->getLevel() == 'optional') {
1113
        return;
1114
    }
1115
 
1116
/// It there is bypass info (function and message)
1117
    if (is_array($xml['#']) && isset($xml['#']['BYPASS'][0]['@']['function']) && isset($xml['#']['BYPASS'][0]['@']['message'])) {
1118
        $function = $xml['#']['BYPASS'][0]['@']['function'];
1119
        $message  = $xml['#']['BYPASS'][0]['@']['message'];
1120
    /// Look for the function
1121
        if (function_exists($function)) {
1122
        /// Call it, and if bypass = true is returned, apply meesage
1123
            if ($function($result)) {
1124
            /// We only set the bypass message if the function itself hasn't defined it before
1125
                if (empty($result->getBypassStr)) {
1126
                    if (isset($xml['#']['BYPASS'][0]['@']['plugin'])) {
1127
                        $result->setBypassStr(array($message, $xml['#']['BYPASS'][0]['@']['plugin']));
1128
                    } else {
1129
                        $result->setBypassStr($message);
1130
                    }
1131
                }
1132
            }
1133
        }
1134
    }
1135
}
1136
 
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 array $xml xmldata containing the restrict data
1147
 * @param environment_results $result object to be updated
1148
 * @return void
1149
 */
1150
function 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)) {
1166
                    if (isset($xml['#']['RESTRICT'][0]['@']['plugin'])) {
1167
                        $result->setRestrictStr(array($message, $xml['#']['RESTRICT'][0]['@']['plugin']));
1168
                    } else {
1169
                        $result->setRestrictStr($message);
1170
                    }
1171
                }
1172
            }
1173
        }
1174
    }
1175
}
1176
 
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.
1180
 *
1181
 * @uses INCORRECT_FEEDBACK_FOR_REQUIRED
1182
 * @uses INCORRECT_FEEDBACK_FOR_OPTIONAL
1183
 * @param array $xml xmldata containing the feedback data
1184
 * @param environment_results $result object to be updated
1185
 */
1186
function process_environment_messages($xml, &$result) {
1187
 
1188
/// If there is feedback info
1189
    if (is_array($xml['#']) && isset($xml['#']['FEEDBACK'][0]['#'])) {
1190
        $feedbackxml = $xml['#']['FEEDBACK'][0]['#'];
1191
 
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
 
1201
        if (!$result->status and $result->getLevel() == 'required') {
1202
            if (isset($feedbackxml['ON_ERROR'][0]['@']['message'])) {
1203
                if (isset($feedbackxml['ON_ERROR'][0]['@']['plugin'])) {
1204
                    $result->setFeedbackStr(array($feedbackxml['ON_ERROR'][0]['@']['message'], $feedbackxml['ON_ERROR'][0]['@']['plugin']));
1205
                } else {
1206
                    $result->setFeedbackStr($feedbackxml['ON_ERROR'][0]['@']['message']);
1207
                }
1208
            }
1209
        } else if (!$result->status and $result->getLevel() == 'optional') {
1210
            if (isset($feedbackxml['ON_CHECK'][0]['@']['message'])) {
1211
                if (isset($feedbackxml['ON_CHECK'][0]['@']['plugin'])) {
1212
                    $result->setFeedbackStr(array($feedbackxml['ON_CHECK'][0]['@']['message'], $feedbackxml['ON_CHECK'][0]['@']['plugin']));
1213
                } else {
1214
                    $result->setFeedbackStr($feedbackxml['ON_CHECK'][0]['@']['message']);
1215
                }
1216
            }
1217
        } else {
1218
            if (isset($feedbackxml['ON_OK'][0]['@']['message'])) {
1219
                if (isset($feedbackxml['ON_OK'][0]['@']['plugin'])) {
1220
                    $result->setFeedbackStr(array($feedbackxml['ON_OK'][0]['@']['message'], $feedbackxml['ON_OK'][0]['@']['plugin']));
1221
                } else {
1222
                    $result->setFeedbackStr($feedbackxml['ON_OK'][0]['@']['message']);
1223
                }
1224
            }
1225
        }
1226
    }
1227
}
1228
 
1229
 
1230
//--- Helper Class to return results to caller ---//
1231
 
1232
 
1233
/**
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
1239
 */
1240
class environment_results {
1241
    /**
1242
     * @var string Which are we checking (database, php, php_extension, php_extension)
1243
     */
1244
    var $part;
1245
    /**
1246
     * @var bool true means the test passed and all is OK. false means it failed.
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/recommended.
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;
1281
    /**
1282
     * @var string|null full plugin name or null if main environment
1283
     */
1284
    var $plugin = null;
1285
    /**
1286
     * Constructor of the environment_result class. Just set default values
1287
     *
1288
     * @param string $part
1289
     */
1290
    public function __construct($part) {
1291
        $this->part=$part;
1292
        $this->status=false;
1293
        $this->error_code=NO_ERROR;
1294
        $this->level='required';
1295
        $this->current_version='';
1296
        $this->needed_version='';
1297
        $this->info='';
1298
        $this->feedback_str='';
1299
        $this->bypass_str='';
1300
        $this->restrict_str='';
1301
    }
1302
 
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
 
1313
    /**
1314
     * Set the status
1315
     *
1316
     * @param bool $testpassed true means the test passed and all is OK. false means it failed.
1317
     */
1318
    function setStatus($testpassed) {
1319
        $this->status = $testpassed;
1320
        if ($testpassed) {
1321
            $this->setErrorCode(NO_ERROR);
1322
        }
1323
    }
1324
 
1325
    /**
1326
     * Set the error_code
1327
     *
1328
     * @param integer $error_code the error code (see constants above)
1329
     */
1330
    function setErrorCode($error_code) {
1331
        $this->error_code=$error_code;
1332
    }
1333
 
1334
    /**
1335
     * Set the level
1336
     *
1337
     * @param string $level the level (required, optional)
1338
     */
1339
    function setLevel($level) {
1340
        $this->level=$level;
1341
    }
1342
 
1343
    /**
1344
     * Set the current version
1345
     *
1346
     * @param string $current_version the current version
1347
     */
1348
    function setCurrentVersion($current_version) {
1349
        $this->current_version=$current_version;
1350
    }
1351
 
1352
    /**
1353
     * Set the needed version
1354
     *
1355
     * @param string $needed_version the needed version
1356
     */
1357
    function setNeededVersion($needed_version) {
1358
        $this->needed_version=$needed_version;
1359
    }
1360
 
1361
    /**
1362
     * Set the auxiliary info
1363
     *
1364
     * @param string $info the auxiliary info
1365
     */
1366
    function setInfo($info) {
1367
        $this->info=$info;
1368
    }
1369
 
1370
    /**
1371
     * Set the feedback string
1372
     *
1373
     * @param mixed $str the feedback string that will be fetched from the admin lang file.
1374
     *                  pass just the string or pass an array of params for get_string
1375
     *                  You always should put your string in admin.php but a third param is useful
1376
     *                  to pass an $a object / string to get_string
1377
     */
1378
    function setFeedbackStr($str) {
1379
        $this->feedback_str=$str;
1380
    }
1381
 
1382
 
1383
    /**
1384
     * Set the bypass string
1385
     *
1386
     * @param string $str the bypass string that will be fetched from the admin lang file.
1387
     *                  pass just the string or pass an array of params for get_string
1388
     *                  You always should put your string in admin.php but a third param is useful
1389
     *                  to pass an $a object / string to get_string
1390
     */
1391
    function setBypassStr($str) {
1392
        $this->bypass_str=$str;
1393
    }
1394
 
1395
    /**
1396
     * Set the restrict string
1397
     *
1398
     * @param string $str the restrict string that will be fetched from the admin lang file.
1399
     *                  pass just the string or pass an array of params for get_string
1400
     *                  You always should put your string in admin.php but a third param is useful
1401
     *                  to pass an $a object / string to get_string
1402
     */
1403
    function setRestrictStr($str) {
1404
        $this->restrict_str=$str;
1405
    }
1406
 
1407
    /**
1408
     * Get the status
1409
     *
1410
     * @return bool true means the test passed and all is OK. false means it failed.
1411
     */
1412
    function getStatus() {
1413
        return $this->status;
1414
    }
1415
 
1416
    /**
1417
     * Get the error code
1418
     *
1419
     * @return integer error code
1420
     */
1421
    function getErrorCode() {
1422
        return $this->error_code;
1423
    }
1424
 
1425
    /**
1426
     * Get the level
1427
     *
1428
     * @return string level
1429
     */
1430
    function getLevel() {
1431
        return $this->level;
1432
    }
1433
 
1434
    /**
1435
     * Get the current version
1436
     *
1437
     * @return string current version
1438
     */
1439
    function getCurrentVersion() {
1440
        return $this->current_version;
1441
    }
1442
 
1443
    /**
1444
     * Get the needed version
1445
     *
1446
     * @return string needed version
1447
     */
1448
    function getNeededVersion() {
1449
        return $this->needed_version;
1450
    }
1451
 
1452
    /**
1453
     * Get the aux info
1454
     *
1455
     * @return string info
1456
     */
1457
    function getInfo() {
1458
        return $this->info;
1459
    }
1460
 
1461
    /**
1462
     * Get the part this result belongs to
1463
     *
1464
     * @return string part
1465
     */
1466
    function getPart() {
1467
        return $this->part;
1468
    }
1469
 
1470
    /**
1471
     * Get the feedback string
1472
     *
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).
1475
     */
1476
    function getFeedbackStr() {
1477
        return $this->feedback_str;
1478
    }
1479
 
1480
    /**
1481
     * Get the bypass string
1482
     *
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).
1485
     */
1486
    function getBypassStr() {
1487
        return $this->bypass_str;
1488
    }
1489
 
1490
    /**
1491
     * Get the restrict string
1492
     *
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).
1495
     */
1496
    function getRestrictStr() {
1497
        return $this->restrict_str;
1498
    }
1499
 
1500
    /**
1501
     * @todo Document this function
1502
     *
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
    }
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
    }
1536
}
1537
 
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
 
1542
/**
1543
 * @param array $element the element from the environment.xml file that should have
1544
 *      either a level="required" or level="optional" attribute.
1545
 * @return string "required" or "optional".
1546
 */
1547
function get_level($element) {
1548
    $level = 'required';
1549
    if (isset($element['@']['level'])) {
1550
        $level = $element['@']['level'];
1551
        if (!in_array($level, ['required', 'optional', 'recommended'])) {
1552
            debugging('The level of a check in the environment.xml file must be "required", "optional" or "recommended".',
1553
                    DEBUG_DEVELOPER);
1554
            $level = 'required';
1555
        }
1556
    } else {
1557
        debugging('Checks in the environment.xml file must have a level="required" or level="optional" attribute.', DEBUG_DEVELOPER);
1558
    }
1559
    return $level;
1560
}
1561
 
1562
/**
1563
 * Once the result has been determined, look in the XML for any
1564
 * messages, or other things that should be done depending on the outcome.
1565
 *
1566
 * @param array $element the element from the environment.xml file which
1567
 *      may have children defining what should be done with the outcome.
1568
 * @param object $result the result of the test, which may be modified by
1569
 *      this function as specified in the XML.
1570
 */
1571
function process_environment_result($element, &$result) {
1572
/// Process messages, modifying the $result if needed.
1573
    process_environment_messages($element, $result);
1574
/// Process bypass, modifying $result if needed.
1575
    process_environment_bypass($element, $result);
1576
/// Process restrict, modifying $result if needed.
1577
    process_environment_restrict($element, $result);
1578
}
1579
 
1580
/**
1581
 * Check if the current PHP version is greater than or equal to
1582
 * PHP version 7.
1583
 *
1584
 * @param object $result an environment_results instance
1585
 * @return bool result of version check
1586
 */
1587
function restrict_php_version_7(&$result) {
1588
    return restrict_php_version($result, '7');
1589
}
1590
 
1591
/**
1592
 * Check if the current PHP version is greater than or equal to an
1593
 * unsupported version.
1594
 *
1595
 * @param object $result an environment_results instance
1596
 * @param string $version the version of PHP that can't be used
1597
 * @return bool result of version check
1598
 */
1599
function restrict_php_version(&$result, $version) {
1600
 
1601
    // Get the current PHP version.
1602
    $currentversion = normalize_version(phpversion());
1603
 
1604
    // Confirm we're using a supported PHP version.
1605
    if (version_compare($currentversion, $version, '<')) {
1606
        // Everything is ok, the restriction doesn't apply.
1607
        return false;
1608
    } else {
1609
        // We're using an unsupported PHP version, apply restriction.
1610
        return true;
1611
    }
1612
}
1613
 
1614
/**
1615
 * Check if the current PHP version is greater than or equal to
1616
 * PHP version 7.1.
1617
 *
1618
 * @param object $result an environment_results instance
1619
 * @return bool result of version check
1620
 */
1621
function restrict_php_version_71(&$result) {
1622
    return restrict_php_version($result, '7.1');
1623
}
1624
 
1625
/**
1626
 * Check if the current PHP version is greater than or equal to
1627
 * PHP version 7.2.
1628
 *
1629
 * @param object $result an environment_results instance
1630
 * @return bool result of version check
1631
 */
1632
function restrict_php_version_72(&$result) {
1633
    return restrict_php_version($result, '7.2');
1634
}
1635
 
1636
/**
1637
 * Check if the current PHP version is greater than or equal to
1638
 * PHP version 7.3.
1639
 *
1640
 * @param object $result an environment_results instance
1641
 * @return bool result of version check
1642
 */
1643
function restrict_php_version_73(&$result) {
1644
    return restrict_php_version($result, '7.3');
1645
}
1646
 
1647
/**
1648
 * Check if the current PHP version is greater than or equal to
1649
 * PHP version 7.4.
1650
 *
1651
 * @param object $result an environment_results instance
1652
 * @return bool result of version check
1653
 */
1654
function restrict_php_version_74(&$result) {
1655
    return restrict_php_version($result, '7.4');
1656
}
1657
 
1658
/**
1659
 * Check if the current PHP version is greater than or equal to
1660
 * PHP version 8.0
1661
 *
1662
 * @param object $result an environment_results instance
1663
 * @return bool result of version check
1664
 */
1665
function restrict_php_version_80($result) {
1666
    return restrict_php_version($result, '8.0');
1667
}
1668
 
1669
/**
1670
 * Check if the current PHP version is greater than or equal to
1671
 * PHP version 8.1
1672
 *
1673
 * @param object $result an environment_results instance
1674
 * @return bool result of version check
1675
 */
1676
function restrict_php_version_81($result) {
1677
    return restrict_php_version($result, '8.1');
1678
}
1679
 
1680
/**
1681
 * Check if the current PHP version is greater than or equal to
1682
 * PHP version 8.2
1683
 *
1684
 * @param object $result an environment_results instance
1685
 * @return bool result of version check
1686
 */
1687
function restrict_php_version_82($result) {
1688
    return restrict_php_version($result, '8.2');
1689
}
1690
 
1691
/**
1692
 * Check if the current PHP version is greater than or equal to
1693
 * PHP version 8.3
1694
 *
1695
 * @param object $result an environment_results instance
1696
 * @return bool result of version check
1697
 */
1698
function restrict_php_version_83($result) {
1699
    return restrict_php_version($result, '8.3');
1700
}