MDL-9616 - Custom checks in environment.xml
[moodle.git] / lib / environmentlib.php
1 <?php  //$Id$
3 ///////////////////////////////////////////////////////////////////////////
4 //                                                                       //
5 // NOTICE OF COPYRIGHT                                                   //
6 //                                                                       //
7 // Moodle - Modular Object-Oriented Dynamic Learning Environment         //
8 //          http://moodle.com                                            //
9 //                                                                       //
10 // Copyright (C) 2001-3001 Martin Dougiamas        http://dougiamas.com  //
11 //           (C) 2001-3001 Eloy Lafuente (stronk7) http://contiento.com  //
12 //                                                                       //
13 // This program is free software; you can redistribute it and/or modify  //
14 // it under the terms of the GNU General Public License as published by  //
15 // the Free Software Foundation; either version 2 of the License, or     //
16 // (at your option) any later version.                                   //
17 //                                                                       //
18 // This program is distributed in the hope that it will be useful,       //
19 // but WITHOUT ANY WARRANTY; without even the implied warranty of        //
20 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         //
21 // GNU General Public License for more details:                          //
22 //                                                                       //
23 //          http://www.gnu.org/copyleft/gpl.html                         //
24 //                                                                       //
25 ///////////////////////////////////////////////////////////////////////////
27 // This library includes all the necessary stuff to execute some standard
28 // tests of required versions and libraries to run Moodle. It can be
29 // used from the admin interface, and both at install and upgrade.
30 //
31 // All the info is stored in the admin/environment.xml file,
32 // supporting to have an updated version in dataroot/environment
34 /// Add required files
35     require_once($CFG->libdir.'/xmlize.php');
37 /// Define a buch of XML processing errors
38     define('NO_ERROR',                           0);
39     define('NO_VERSION_DATA_FOUND',              1);
40     define('NO_DATABASE_SECTION_FOUND',          2);
41     define('NO_DATABASE_VENDORS_FOUND',          3);
42     define('NO_DATABASE_VENDOR_MYSQL_FOUND',     4);
43     define('NO_DATABASE_VENDOR_POSTGRES_FOUND',  5);
44     define('NO_PHP_SECTION_FOUND',               6);
45     define('NO_PHP_VERSION_FOUND',               7);
46     define('NO_PHP_EXTENSIONS_SECTION_FOUND',    8);
47     define('NO_PHP_EXTENSIONS_NAME_FOUND',       9);
48     define('NO_DATABASE_VENDOR_VERSION_FOUND',  10);
49     define('NO_UNICODE_SECTION_FOUND',          11);
50     define('NO_CUSTOM_CHECK_FOUND',             12);
51     define('CUSTOM_CHECK_FILE_MISSING',         13);
52     define('CUSTOM_CHECK_FUNCTION_MISSING',     14);
54 /**
55  * This function will perform the whole check, returning
56  * true or false as final result. Also, he full array of
57  * environment_result will be returned in the parameter list.
58  * The function looks for the best version to compare and
59  * everything. This is the only function that should be called
60  * ever from the rest of Moodle.
61  * @param string version version to check. 
62  * @param array results array of results checked.
63  * @return boolean true/false, depending of results
64  */
65 function check_moodle_environment($version, &$environment_results, $print_table=true) {
67     $status = true;
69 /// This are cached per request
70     static $result = true;
71     static $env_results;
72     static $cache_exists = false;
74 /// if we have results cached, use them
75     if ($cache_exists) {
76         $environment_results = $env_results;
77 /// No cache exists, calculate everything
78     } else {
79     /// Get the more recent version before the requested
80         if (!$version = get_latest_version_available($version)) {
81             $status = false;
82         }
84     /// Perform all the checks
85         if (!($environment_results = environment_check($version)) && $status) {
86             $status = false;
87         }
89     /// Iterate over all the results looking for some error in required items
90     /// or some error_code
91         if ($status) {
92             foreach ($environment_results as $environment_result) {
93                 if (!$environment_result->getStatus() && $environment_result->getLevel() == 'required'
94                   && !$environment_result->getBypassStr()) {
95                     $result = false; // required item that is not bypased
96                 } else if ($environment_result->getStatus() && $environment_result->getLevel() == 'required'
97                   && $environment_result->getRestrictStr()) {
98                     $result = false; // required item that is restricted
99                 } else if ($environment_result->getErrorCode()) {
100                     $result = false;
101                 }
102             }
103         }
104     /// Going to end, we store environment_results to cache
105         $env_results = $environment_results;
106         $cache_exists = true;
107     } ///End of cache block
109 /// If we have decided to print all the information, just do it
110     if ($print_table) {
111         print_moodle_environment($result && $status, $environment_results);
112     }
114     return ($result && $status);
117 /** 
118  * This function will print one beautiful table with all the environmental
119  * configuration and how it suits Moodle needs.
120  * @param boolean final result of the check (true/false)
121  * @param array environment_results array of results gathered
122  */
123 function print_moodle_environment($result, $environment_results) {
125 /// Get some strings
126     $strname = get_string('name');
127     $strinfo = get_string('info');
128     $strreport = get_string('report');
129     $strstatus = get_string('status');
130     $strok = get_string('ok');
131     $strerror = get_string('error');
132     $strcheck = get_string('check');
133     $strbypassed = get_string('bypassed');
134     $strrestricted = get_string('restricted');
135     $strenvironmenterrortodo = get_string('environmenterrortodo', 'admin');
137 /// Here we'll store all the feedback found
138     $feedbacktext = '';
140 /// Table header
141     $table = new stdClass;
142     $table->head  = array ($strname, $strinfo, $strreport, $strstatus);
143     $table->align = array ('center', 'center', 'left', 'center');
144     $table->wrap  = array ('nowrap', '', '', 'nowrap');
145     $table->size  = array ('10', 10, '100%', '10');
146     $table->width = '90%';
147     $table->class = 'environmenttable generaltable';
149 /// Iterate over each environment_result
150     $continue = true;
151     foreach ($environment_results as $environment_result) {
152         $errorline   = false;
153         $warningline = false;
154         if ($continue) {
155             $type = $environment_result->getPart();
156             $info = $environment_result->getInfo();
157             $status = $environment_result->getStatus();
158             $error_code = $environment_result->getErrorCode();
159         /// Process Report field
160             $rec = new stdClass();
161         /// Something has gone wrong at parsing time
162             if ($error_code) {
163                 $stringtouse = 'environmentxmlerror';
164                 $rec->error_code = $error_code;
165                 $status = $strerror;
166                 $errorline = true;
167                 $continue = false;
168             }
170             if ($continue) {
171             /// We are comparing versions
172                 if ($rec->needed = $environment_result->getNeededVersion()) {
173                     $rec->current = $environment_result->getCurrentVersion();
174                     if ($environment_result->getLevel() == 'required') {
175                         $stringtouse = 'environmentrequireversion';
176                     } else {
177                         $stringtouse = 'environmentrecommendversion';
178                     }
179             /// We are checking installed & enabled things
180                 } else {
181                     if ($environment_result->getLevel() == 'required') {
182                         $stringtouse = 'environmentrequireinstall';
183                     } else {
184                         $stringtouse = 'environmentrecommendinstall';
185                     }
186                 }
187             /// Calculate the status value
188                 if ($environment_result->getBypassStr() != '') {            //Handle bypassed result (warning)
189                     $status = $strbypassed;
190                     $warningline = true;
191                 } else if ($environment_result->getRestrictStr() != '') {   //Handle restricted result (error)
192                     $status = $strrestricted;
193                     $errorline = true;
194                 } else {
195                     if ($status) {                                          //Handle ok result (ok)
196                         $status = $strok;
197                     } else {
198                         if ($environment_result->getLevel() == 'optional') {//Handle check result (warning)
199                             $status = $strcheck;
200                             $warningline = true;
201                         } else {                                            //Handle error result (error)
202                             $status = $strcheck;                       
203                             $errorline = true;
204                         }
205                     }
206                 }
207             }
208     
209         /// Build the text
210             $report = get_string($stringtouse, 'admin', $rec);
211         /// Format error or warning line
212             if ($errorline || $warningline) {
213                 $styletoapply = $errorline? 'error':'warn';
214                 $type = '<span class="'.$styletoapply.'">'.$type.'</span>';
215                 $info = '<span class="'.$styletoapply.'">'.$info.'</span>';
216                 $report = '<span class="'.$styletoapply.'">'.$report.'</span>';
217                 $status = '<span class="'.$styletoapply.'">'.$status.'</span>';
218             }
219         /// Add the row to the table
220             $table->data[] = array ($type, $info, $report, $status);
221         ///Process the feedback if necessary
222             if ($feedbackstr = $environment_result->getFeedbackStr()) {
223                 $feedbacktext .= '<li class="environmenttable">'.get_string($feedbackstr, 'admin').'</li>';
224             }
225         ///Process the bypass if necessary
226             if ($bypassstr = $environment_result->getBypassStr()) {
227                 $feedbacktext .= '<li class="environmenttable">'.get_string($bypassstr, 'admin').'</li>';
228             }
229         ///Process the restrict if necessary
230             if ($restrictstr = $environment_result->getRestrictStr()) {
231                 $feedbacktext .= '<li class="environmenttable">'.get_string($restrictstr, 'admin').'</li>';
232             }
233         }
234     }
235     
236 /// Print table
237     print_table($table);
239 /// And feedback accumulated text
240     if ($feedbacktext) {
241         print_simple_box('<ul>'.$feedbacktext.'</ul>', 'center', '90%', '', '', 'environmentbox generalbox');
242     }
244 /// Finally, if any error has happened, print the summary box
245     if (!$result) {
246         print_simple_box($strenvironmenterrortodo, 'center', '', '', '', 'environmentbox errorbox');
247     }
251 /**
252  * This function will normalize any version to just a serie of numbers
253  * separated by dots. Everything else will be removed.
254  * @param string $version the original version
255  * @return string the normalized version
256  */
257 function normalize_version($version) {
258 /// Replace everything but numbers and dots by dots
259     $version = preg_replace('/[^\.\d]/', '.', $version);
260 /// Combine multiple dots in one
261     $version = preg_replace('/(\.{2,})/', '.', $version);
262 /// Trim possible leading and trailing dots
263     $version = trim($version, '.');
265     return $version;
269 /**
270  * This function will load the environment.xml file and xmlize it
271  * @return mixed the xmlized structure or false on error
272  */
273 function load_environment_xml() {
274     
275     global $CFG;
277     static $data; //Only load and xmlize once by request
279     if (!empty($data)) {
280         return $data;
281     }
283 /// First of all, take a look inside $CFG->dataroot/environment/environment.xml
284     $file = $CFG->dataroot.'/environment/environment.xml';
285     $internalfile = $CFG->dirroot.'/'.$CFG->admin.'/environment.xml';
286     if (!is_file($file) || !is_readable($file) || filemtime($file) < filemtime($internalfile) || 
287         !$contents = file_get_contents($file)) {
288     /// Fallback to fixed $CFG->admin/environment.xml
289         if (!is_file($internalfile) || !is_readable($internalfile) || !$contents = file_get_contents($internalfile)) {
290             return false;
291         }
292     }
293 /// XML the whole file
294     $data = xmlize($contents);
296     return $data;
300 /**
301  * This function will return the list of Moodle versions available
302  * @return mixed array of versions. False on error.
303  */
304 function get_list_of_environment_versions ($contents) {
306     static $versions = array();
308     if (!empty($versions)) {
309         return $versions;
310     }
312     if (isset($contents['COMPATIBILITY_MATRIX']['#']['MOODLE'])) {
313         foreach ($contents['COMPATIBILITY_MATRIX']['#']['MOODLE'] as $version) {
314             $versions[] = $version['@']['version'];
315         }
316     }
318     return $versions;
322 /**
323  * This function will return the most recent version in the environment.xml
324  * file previous or equal to the version requested
325  * @param string version top version from which we start to look backwards
326  * @return string more recent version or false if not found
327  */
328 function get_latest_version_available ($version) {
330 /// Normalize the version requested
331     $version = normalize_version($version);
333 /// Load xml file
334     if (!$contents = load_environment_xml()) {
335         return false;
336     }
338 /// Detect available versions
339     if (!$versions = get_list_of_environment_versions($contents)) {
340         return false;
341     }
342 /// First we look for exact version
343     if (in_array($version, $versions)) {
344         return $version;
345     } else {
346         $found_version = false;
347     /// Not exact match, so we are going to iterate over the list searching
348     /// for the latest version before the requested one
349         foreach ($versions as $arrversion) {
350             if (version_compare($arrversion, $version, '<')) {
351                 $found_version = $arrversion;
352             }
353         }
354     }
356     return $found_version;
360 /** 
361  * This function will return the xmlized data belonging to one Moodle version
362  * @return mixed the xmlized structure or false on error
363  */
364 function get_environment_for_version($version) {
365    
366 /// Normalize the version requested
367     $version = normalize_version($version);
369 /// Load xml file
370     if (!$contents = load_environment_xml()) {
371         return false;
372     }
374 /// Detect available versions
375     if (!$versions = get_list_of_environment_versions($contents)) {
376         return false;
377     }
379 /// If the version requested is available
380     if (!in_array($version, $versions)) {
381         return false;
382     }
384 /// We now we have it. Extract from full contents.
385     $fl_arr = array_flip($versions);
386     
387     return $contents['COMPATIBILITY_MATRIX']['#']['MOODLE'][$fl_arr[$version]];
391 /** 
392  * This function will check for everything (DB, PHP and PHP extensions for now)
393  * returning an array of environment_result objects.
394  * @param string $version xml version we are going to use to test this server
395  * @return array array of results encapsulated in one environment_result object
396  */
397 function environment_check($version) {
399 /// Normalize the version requested
400     $version = normalize_version($version);
402     $results = array(); //To store all the results
404     $results[] = environment_check_unicode($version);
405     $results[] = environment_check_database($version);
406     $results[] = environment_check_php($version);
408     $phpext_results = environment_check_php_extensions($version);
409     $results = array_merge($results, $phpext_results);
411     $custom_results = environment_custom_checks($version);
412     $results = array_merge($results, $custom_results);
414     return $results;
418 /**
419  * This function will check if php extensions requirements are satisfied
420  * @param string $version xml version we are going to use to test this server
421  * @return array array of results encapsulated in one environment_result object
422  */
423 function environment_check_php_extensions($version) {
425     $results = array();
427 /// Get the enviroment version we need
428     if (!$data = get_environment_for_version($version)) {
429     /// Error. No version data found
430         $result = new environment_results('php_extension');
431         $result->setStatus(false);
432         $result->setErrorCode(NO_VERSION_DATA_FOUND);
433         return $result;
434     }
436 /// Extract the php_extension part
437     if (!isset($data['#']['PHP_EXTENSIONS']['0']['#']['PHP_EXTENSION'])) {
438     /// Error. No PHP section found
439         $result = new environment_results('php_extension');
440         $result->setStatus(false);
441         $result->setErrorCode(NO_PHP_EXTENSIONS_SECTION_FOUND);
442         return $result;
443     }
444 /// Iterate over extensions checking them and creating the needed environment_results
445     foreach($data['#']['PHP_EXTENSIONS']['0']['#']['PHP_EXTENSION'] as $extension) {
446         $result = new environment_results('php_extension');
447     /// Check for level
448         $level = get_level($extension);
449     /// Check for extension name
450         if (!isset($extension['@']['name'])) {
451             $result->setStatus(false);
452             $result->setErrorCode(NO_PHP_EXTENSIONS_NAME_FOUND);
453         } else {
454             $extension_name = $extension['@']['name'];
455         /// The name exists. Just check if it's an installed extension
456             if (!extension_loaded($extension_name)) {
457                 $result->setStatus(false);
458             } else {
459                 $result->setStatus(true);
460             }
461             $result->setLevel($level);
462             $result->setInfo($extension_name);
463         }
465     /// Do any actions defined in the XML file.
466         process_environment_result($extension, $result);
468     /// Add the result to the array of results
469         $results[] = $result;
470     }
473     return $results;
476 /**
477  * This function will do the custom checks.
478  * @param string $version xml version we are going to use to test this server.
479  * @return array array of results encapsulated in environment_result objects.
480  */
481 function environment_custom_checks($version) {
482     global $CFG;
484     $results = array();
486 /// Get the enviroment version we need
487     if (!$data = get_environment_for_version($version)) {
488     /// Error. No version data found - but this will already have been reported.
489         return $results;
490     }
492 /// Extract the CUSTOM_CHECKS part
493     if (!isset($data['#']['CUSTOM_CHECKS']['0']['#']['CUSTOM_CHECK'])) {
494     /// No custom checks found - not a problem
495         return $results;
496     }
498 /// Iterate over extensions checking them and creating the needed environment_results
499     foreach($data['#']['CUSTOM_CHECKS']['0']['#']['CUSTOM_CHECK'] as $check) {
500         $result = new environment_results('custom_check');
502     /// Check for level
503         $level = get_level($check);
505     /// Check for extension name
506         if (isset($check['@']['file']) && isset($check['@']['function'])) {
507             $file = $CFG->dirroot . '/' . $check['@']['file'];
508             echo $file;
509             $function = $check['@']['function'];
510             if (is_readable($file)) {
511                 include_once($file);
512                 if (function_exists($function)) {
513                     $result->setLevel($level);
514                     $result->setInfo($function);
515                     $result = $function($result);
516                 } else {
517                     $result->setStatus(false);
518                     $result->setErrorCode(CUSTOM_CHECK_FUNCTION_MISSING);
519                 }
520             } else {
521                 $result->setStatus(false);
522                 $result->setErrorCode(CUSTOM_CHECK_FILE_MISSING);
523             }
524         } else {
525             $result->setStatus(false);
526             $result->setErrorCode(NO_CUSTOM_CHECK_FOUND);
527         }
529         if (!is_null($result)) {
530         /// Do any actions defined in the XML file.
531             process_environment_result($check, $result);
532     
533         /// Add the result to the array of results
534             $results[] = $result;
535         }
536     }
538     return $results;
541 /**
542  * This function will check if php requirements are satisfied
543  * @param string $version xml version we are going to use to test this server
544  * @return object results encapsulated in one environment_result object
545  */
546 function environment_check_php($version) {
548     $result = new environment_results('php');
550 /// Get the enviroment version we need
551     if (!$data = get_environment_for_version($version)) {
552     /// Error. No version data found
553         $result->setStatus(false);
554         $result->setErrorCode(NO_VERSION_DATA_FOUND);
555         return $result;
556     }
558 /// Extract the php part
559     if (!isset($data['#']['PHP'])) {
560     /// Error. No PHP section found
561         $result->setStatus(false);
562         $result->setErrorCode(NO_PHP_SECTION_FOUND);
563         return $result;
564     } else {
565     /// Extract level and version
566         $level = get_level($data['#']['PHP']['0']);
567         if (!isset($data['#']['PHP']['0']['@']['version'])) {
568             $result->setStatus(false);
569             $result->setErrorCode(NO_PHP_VERSION_FOUND);
570             return $result;
571         } else {
572             $needed_version = $data['#']['PHP']['0']['@']['version'];
573         }
574     }
576 /// Now search the version we are using
577     $current_version = normalize_version(phpversion());
579 /// And finally compare them, saving results
580     if (version_compare($current_version, $needed_version, '>=')) {
581         $result->setStatus(true);
582     } else {
583         $result->setStatus(false);
584     }
585     $result->setLevel($level);   
586     $result->setCurrentVersion($current_version);
587     $result->setNeededVersion($needed_version);
589 /// Do any actions defined in the XML file.
590     process_environment_result($data['#']['PHP'][0], $result);
592     return $result;
596 /**
597  * This function will check if unicode database requirements are satisfied
598  * @param string $version xml version we are going to use to test this server
599  * @return object results encapsulated in one environment_result object
600  */
601 function environment_check_unicode($version) {
602     global $db;
604     $result = new environment_results('unicode');
606     /// Get the enviroment version we need
607     if (!$data = get_environment_for_version($version)) {
608     /// Error. No version data found
609         $result->setStatus(false);
610         $result->setErrorCode(NO_VERSION_DATA_FOUND);
611         return $result;
612     }
614     /// Extract the unicode part
616     if (!isset($data['#']['UNICODE'])) {
617     /// Error. No DATABASE section found
618         $result->setStatus(false);
619         $result->setErrorCode(NO_UNICODE_SECTION_FOUND);
620         return $result;
621     } else {
622     /// Extract level
623         $level = get_level($data['#']['UNICODE']['0']);
624     }
626     if (!$unicodedb = setup_is_unicodedb()) {
627         $result->setStatus(false);
628     } else {
629         $result->setStatus(true);
630     }
632     $result->setLevel($level);
634 /// Do any actions defined in the XML file.
635     process_environment_result($data['#']['UNICODE'][0], $result);
637     return $result;
640 /**
641  * This function will check if database requirements are satisfied
642  * @param string $version xml version we are going to use to test this server
643  * @return object results encapsulated in one environment_result object
644  */
645 function environment_check_database($version) {
647     global $db;
649     $result = new environment_results('database');
651     $vendors = array();  //Array of vendors in version
653 /// Get the enviroment version we need
654     if (!$data = get_environment_for_version($version)) {
655     /// Error. No version data found
656         $result->setStatus(false);
657         $result->setErrorCode(NO_VERSION_DATA_FOUND);
658         return $result;
659     }
661 /// Extract the database part
662     if (!isset($data['#']['DATABASE'])) {
663     /// Error. No DATABASE section found
664         $result->setStatus(false);
665         $result->setErrorCode(NO_DATABASE_SECTION_FOUND);
666         return $result;
667     } else {
668     /// Extract level
669         $level = get_level($data['#']['DATABASE']['0']);
670     }
672 /// Extract DB vendors. At least 2 are mandatory (mysql & postgres)
673     if (!isset($data['#']['DATABASE']['0']['#']['VENDOR'])) {
674     /// Error. No VENDORS found
675         $result->setStatus(false);
676         $result->setErrorCode(NO_DATABASE_VENDORS_FOUND);
677         return $result;
678     } else {
679     /// Extract vendors
680         foreach ($data['#']['DATABASE']['0']['#']['VENDOR'] as $vendor) {
681             if (isset($vendor['@']['name']) && isset($vendor['@']['version'])) {
682                 $vendors[$vendor['@']['name']] = $vendor['@']['version'];
683                 $vendorsxml[$vendor['@']['name']] = $vendor;
684             }
685         }
686     }
687 /// Check we have the mysql vendor version
688     if (empty($vendors['mysql'])) {
689         $result->setStatus(false);
690         $result->setErrorCode(NO_DATABASE_VENDOR_MYSQL_FOUND);
691         return $result;
692     }
693 /// Check we have the postgres vendor version
694     if (empty($vendors['postgres'])) {
695         $result->setStatus(false);
696         $result->setErrorCode(NO_DATABASE_VENDOR_POSTGRES_FOUND);
697         return $result;
698     }
700 /// Now search the version we are using (depending of vendor)
701     $current_vendor = set_dbfamily();
703     $dbinfo = $db->ServerInfo();
704     $current_version = normalize_version($dbinfo['version']);
705     $needed_version = $vendors[$current_vendor];
707 /// Check we have a needed version
708     if (!$needed_version) {
709         $result->setStatus(false);
710         $result->setErrorCode(NO_DATABASE_VENDOR_VERSION_FOUND);
711         return $result;
712     }
714 /// And finally compare them, saving results
715     if (version_compare($current_version, $needed_version, '>=')) {
716         $result->setStatus(true);
717     } else {
718         $result->setStatus(false);
719     }
720     $result->setLevel($level);   
721     $result->setCurrentVersion($current_version);
722     $result->setNeededVersion($needed_version);
723     $result->setInfo($current_vendor);
725 /// Do any actions defined in the XML file.
726     process_environment_result($vendorsxml[$current_vendor], $result);
728     return $result;
732 /**
733  * This function will post-process the result record by executing the specified
734  * function, modifying it as necessary, also a custom message will be added
735  * to the result object to be printed by the display layer.
736  * Every bypass function must be defined in this file and it'll return
737  * true/false to decide if the original test is bypassed or no. Also
738  * such bypass functions are able to directly handling the result object
739  * although it should be only under exceptional conditions.
740  *
741  * @param string xmldata containing the bypass data
742  * @param object result object to be updated
743  */
744 function process_environment_bypass($xml, &$result) {
746 /// Only try to bypass if we were in error and it was required
747     if ($result->getStatus() || $result->getLevel() == 'optional') {
748         return;
749     }
751 /// It there is bypass info (function and message)
752     if (is_array($xml['#']) && isset($xml['#']['BYPASS'][0]['@']['function']) && isset($xml['#']['BYPASS'][0]['@']['message'])) {
753         $function = $xml['#']['BYPASS'][0]['@']['function'];
754         $message  = $xml['#']['BYPASS'][0]['@']['message'];
755     /// Look for the function
756         if (function_exists($function)) {
757         /// Call it, and if bypass = true is returned, apply meesage
758             if ($function($result)) {
759             /// We only set the bypass message if the function itself hasn't defined it before
760                 if (empty($result->getBypassStr)) {
761                     $result->setBypassStr($message);
762                 }
763             }
764         }
765     }
768 /**
769  * This function will post-process the result record by executing the specified
770  * function, modifying it as necessary, also a custom message will be added
771  * to the result object to be printed by the display layer.
772  * Every restrict function must be defined in this file and it'll return
773  * true/false to decide if the original test is restricted or no. Also
774  * such restrict functions are able to directly handling the result object
775  * although it should be only under exceptional conditions.
776  *
777  * @param string xmldata containing the restrict data
778  * @param object result object to be updated
779  */
780 function process_environment_restrict($xml, &$result) {
782 /// Only try to restrict if we were not in error and it was required
783     if (!$result->getStatus() || $result->getLevel() == 'optional') {
784         return;
785     }
786 /// It there is restrict info (function and message)
787     if (is_array($xml['#']) && isset($xml['#']['RESTRICT'][0]['@']['function']) && isset($xml['#']['RESTRICT'][0]['@']['message'])) {
788         $function = $xml['#']['RESTRICT'][0]['@']['function'];
789         $message  = $xml['#']['RESTRICT'][0]['@']['message'];
790     /// Look for the function
791         if (function_exists($function)) {
792         /// Call it, and if restrict = true is returned, apply meesage
793             if ($function($result)) {
794             /// We only set the restrict message if the function itself hasn't defined it before
795                 if (empty($result->getRestrictStr)) {
796                     $result->setRestrictStr($message);
797                 }
798             }
799         }
800     }
803 /**
804  * This function will detect if there is some message available to be added to the
805  * result in order to clarify enviromental details.
806  * @param string xmldata containing the feedback data
807  * @param object reult object to be updated
808  */
809 function process_environment_messages($xml, &$result) {
811 /// If there is feedback info
812     if (is_array($xml['#']) && isset($xml['#']['FEEDBACK'][0]['#'])) {
813         $feedbackxml = $xml['#']['FEEDBACK'][0]['#'];
815         if (!$result->status and $result->getLevel() == 'required') {
816             if (isset($feedbackxml['ON_ERROR'][0]['@']['message'])) {
817                 $result->setFeedbackStr($feedbackxml['ON_ERROR'][0]['@']['message']);
818             }
819         } else if (!$result->status and $result->getLevel() == 'optional') {
820             if (isset($feedbackxml['ON_CHECK'][0]['@']['message'])) {
821                 $result->setFeedbackStr($feedbackxml['ON_CHECK'][0]['@']['message']);
822             }
823         } else {
824             if (isset($feedbackxml['ON_OK'][0]['@']['message'])) {
825                 $result->setFeedbackStr($feedbackxml['ON_OK'][0]['@']['message']);
826             }
827         }
828     }
832 //--- Helper Class to return results to caller ---//
835 /** 
836  * This class is used to return the results of the environment
837  * main functions (environment_check_xxxx)
838  */
839 class environment_results {
841     var $part;            //which are we checking (database, php, php_extension)
842     var $status;          //true/false
843     var $error_code;      //integer. See constants at the beginning of the file
844     var $level;           //required/optional
845     var $current_version; //current version detected
846     var $needed_version;  //version needed
847     var $info;            //Aux. info (DB vendor, library...)
848     var $feedback_str;    //String to show on error|on check|on ok
849     var $bypass_str;      //String to show if some bypass has happened
850     var $restrict_str;    //String to show if some restrict has happened
852     /**
853      * Constructor of the environment_result class. Just set default values
854      */
855     function environment_results($part) {
856         $this->part=$part;
857         $this->status=false;
858         $this->error_code=NO_ERROR;
859         $this->level='required';
860         $this->current_version='';
861         $this->needed_version='';
862         $this->info='';
863         $this->feedback_str='';
864         $this->bypass_str='';
865         $this->restrict_str='';
866     }
868     /**
869      * Set the status
870      * @param boolean the status (true/false)
871      */
872     function setStatus($status) {
873         $this->status=$status;
874         if ($status) {
875             $this->setErrorCode(NO_ERROR);
876         }
877     }
879     /**
880      * Set the error_code
881      * @param integer the error code (see constants above)
882      */
883     function setErrorCode($error_code) {
884         $this->error_code=$error_code;
885     }
887     /**
888      * Set the level
889      * @param string the level (required, optional)
890      */
891     function setLevel($level) {
892         $this->level=$level;
893     }
895     /**
896      * Set the current version
897      * @param string the current version
898      */
899     function setCurrentVersion($current_version) {
900         $this->current_version=$current_version;
901     }
903     /**
904      * Set the needed version
905      * @param string the needed version
906      */
907     function setNeededVersion($needed_version) {
908         $this->needed_version=$needed_version;
909     }
911     /**
912      * Set the auxiliary info
913      * @param string the auxiliary info
914      */
915     function setInfo($info) {
916         $this->info=$info;
917     }
918     
919     /**
920      * Set the feedback string
921      * @param string the feedback string
922      */
923     function setFeedbackStr($str) {
924         $this->feedback_str=$str;
925     }
927     /**
928      * Set the bypass string
929      * @param string the bypass string
930      */
931     function setBypassStr($str) {
932         $this->bypass_str=$str;
933     }
935     /**
936      * Set the restrict string
937      * @param string the restrict string
938      */
939     function setRestrictStr($str) {
940         $this->restrict_str=$str;
941     }
943     /**
944      * Get the status
945      * @return boolean result
946      */
947     function getStatus() {
948         return $this->status;
949     }
951     /**
952      * Get the error code
953      * @return integer error code
954      */
955     function getErrorCode() {
956         return $this->error_code;
957     }
959     /**
960      * Get the level
961      * @return string level
962      */
963     function getLevel() {
964         return $this->level;
965     }
967     /**
968      * Get the current version 
969      * @return string current version
970      */
971     function getCurrentVersion() {
972         return $this->current_version;
973     }
975     /**
976      * Get the needed version
977      * @return string needed version
978      */
979     function getNeededVersion() {
980         return $this->needed_version;
981     }
983     /**
984      * Get the aux info
985      * @return string info
986      */
987     function getInfo() {
988         return $this->info;
989     }
991     /**
992      * Get the part this result belongs to
993      * @return string part
994      */
995     function getPart() {
996         return $this->part;
997     }
999     /**
1000      * Get the feedback string
1001      * @return string feedback string
1002      */
1003     function getFeedbackStr() {
1004         return $this->feedback_str;
1005     }
1007     /**
1008      * Get the bypass string
1009      * @return string bypass string
1010      */
1011     function getBypassStr() {
1012         return $this->bypass_str;
1013     }
1015     /**
1016      * Get the restrict string
1017      * @return string restrict string
1018      */
1019     function getRestrictStr() {
1020         return $this->restrict_str;
1021     }
1024 /// Here all the bypass functions are coded to be used by the environment
1025 /// checker. All those functions will receive the result object and will
1026 /// return it modified as needed (status and bypass string)
1028 /**
1029  * This function will bypass MySQL 4.1.16 reqs if:
1030  *   - We are using MySQL > 4.1.12, informing about problems with non latin chars in the future
1031  *
1032  * @param object result object to handle
1033  * @return boolean true/false to determinate if the bypass has to be performed (true) or no (false)
1034  */
1035 function bypass_mysql416_reqs ($result) {
1036 /// See if we are running MySQL >= 4.1.12
1037     if (version_compare($result->getCurrentVersion(), '4.1.12', '>=')) {
1038         return true;
1039     }
1041     return false;
1044 /// Here all the restrict functions are coded to be used by the environment
1045 /// checker. All those functions will receive the result object and will
1046 /// return it modified as needed (status and bypass string)
1048 /** 
1049  * This function will restrict PHP reqs if:
1050  *   - We are using PHP 5.0.x, informing about the buggy version
1051  *
1052  * @param object result object to handle
1053  * @return boolean true/false to determinate if the restrict has to be performed (true) or no (false)
1054  */
1055 function restrict_php50_version($result) {
1056     if (version_compare($result->getCurrentVersion(), '5.0.0', '>=')
1057       and version_compare($result->getCurrentVersion(), '5.0.99', '<')) {
1058         return true;
1059     }
1060     return false;
1063 /**
1064  * @param array $element the element from the environment.xml file that should have
1065  *      either a level="required" or level="optional" attribute.
1066  * @read string "required" or "optional".
1067  */
1068 function get_level($element) {
1069     $level = 'required';
1070     if (isset($element['@']['level'])) {
1071         $level = $element['@']['level'];
1072         if (!in_array($level, array('required', 'optional'))) {
1073             debugging('The level of a check in the environment.xml file must be "required" or level="optional".', DEBUG_DEVELOPER);
1074             $level = 'required';
1075         }
1076     } else {
1077         debugging('Checks in the environment.xml file must have a level="required" or level="optional" attribute.', DEBUG_DEVELOPER);
1078     }
1079     return $level;
1082 /**
1083  * Once the result has been determined, look in the XML for any
1084  * messages, or other things that should be done depending on the outcome.
1085  * @param array $element the element from the environment.xml file which
1086  *      may have children defining what should be done with the outcome.
1087  * @param object $result the result of the test, which may be modified by
1088  *      this function as specified in the XML.
1089  */
1090 function process_environment_result($element, &$result) {
1091 /// Process messages, modifying the $result if needed.
1092     process_environment_messages($element, $result);
1093 /// Process bypass, modifying $result if needed.
1094     process_environment_bypass($element, $result);
1095 /// Process restrict, modifying $result if needed.
1096     process_environment_restrict($element, $result);
1098 ?>