Commit | Line | Data |
---|---|---|
b37eac91 | 1 | <?php |
b37eac91 | 2 | // This file is part of Moodle - http://moodle.org/ |
3 | // | |
4 | // Moodle is free software: you can redistribute it and/or modify | |
5 | // it under the terms of the GNU General Public License as published by | |
6 | // the Free Software Foundation, either version 3 of the License, or | |
7 | // (at your option) any later version. | |
8 | // | |
9 | // Moodle is distributed in the hope that it will be useful, | |
10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 | // GNU General Public License for more details. | |
13 | // | |
14 | // You should have received a copy of the GNU General Public License | |
15 | // along with Moodle. If not, see <http://www.gnu.org/licenses/>. | |
16 | ||
b37eac91 | 17 | /** |
18 | * These functions are required very early in the Moodle | |
19 | * setup process, before any of the main libraries are | |
20 | * loaded. | |
30fa50d0 | 21 | * |
78bfb562 PS |
22 | * @package core |
23 | * @subpackage lib | |
24 | * @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com} | |
25 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later | |
b37eac91 | 26 | */ |
d3f9f1f8 | 27 | |
78bfb562 PS |
28 | defined('MOODLE_INTERNAL') || die(); |
29 | ||
e63f95d7 PS |
30 | // Debug levels - always keep the values in ascending order! |
31 | /** No warnings and errors at all */ | |
0f0f0768 | 32 | define('DEBUG_NONE', 0); |
e63f95d7 PS |
33 | /** Fatal errors only */ |
34 | define('DEBUG_MINIMAL', E_ERROR | E_PARSE); | |
35 | /** Errors, warnings and notices */ | |
36 | define('DEBUG_NORMAL', E_ERROR | E_PARSE | E_WARNING | E_NOTICE); | |
37 | /** All problems except strict PHP warnings */ | |
38 | define('DEBUG_ALL', E_ALL & ~E_STRICT); | |
39 | /** DEBUG_ALL with all debug messages and strict warnings */ | |
40 | define('DEBUG_DEVELOPER', E_ALL | E_STRICT); | |
0f0f0768 PS |
41 | |
42 | /** Remove any memory limits */ | |
43 | define('MEMORY_UNLIMITED', -1); | |
44 | /** Standard memory limit for given platform */ | |
45 | define('MEMORY_STANDARD', -2); | |
46 | /** | |
47 | * Large memory limit for given platform - used in cron, upgrade, and other places that need a lot of memory. | |
48 | * Can be overridden with $CFG->extramemorylimit setting. | |
49 | */ | |
50 | define('MEMORY_EXTRA', -3); | |
51 | /** Extremely large memory limit - not recommended for standard scripts */ | |
52 | define('MEMORY_HUGE', -4); | |
c84a2dbe | 53 | |
3d673fc4 DM |
54 | /** |
55 | * Software maturity levels used by the core and plugins | |
56 | */ | |
57 | define('MATURITY_ALPHA', 50); // internals can be tested using white box techniques | |
58 | define('MATURITY_BETA', 100); // feature complete, ready for preview and testing | |
59 | define('MATURITY_RC', 150); // tested, will be released unless there are fatal bugs | |
60 | define('MATURITY_STABLE', 200); // ready for production deployment | |
61 | ||
0242bdc7 TH |
62 | /** |
63 | * Special value that can be used in $plugin->dependencies in version.php files. | |
64 | */ | |
65 | define('ANY_VERSION', 'any'); | |
66 | ||
67 | ||
6a525ce2 | 68 | /** |
b08b3569 | 69 | * Simple class. It is usually used instead of stdClass because it looks |
1387fcdd | 70 | * more familiar to Java developers ;-) Do not use for type checking of |
4f92410f | 71 | * function parameters. Please use stdClass instead. |
b37eac91 | 72 | * |
47d2216e PS |
73 | * @package core |
74 | * @subpackage lib | |
75 | * @copyright 2009 Petr Skoda {@link http://skodak.org} | |
76 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later | |
4f92410f | 77 | * @deprecated since 2.0 |
6a525ce2 | 78 | */ |
b08b3569 | 79 | class object extends stdClass {}; |
6a525ce2 | 80 | |
251387d0 | 81 | /** |
82 | * Base Moodle Exception class | |
b37eac91 | 83 | * |
c84a2dbe | 84 | * Although this class is defined here, you cannot throw a moodle_exception until |
85 | * after moodlelib.php has been included (which will happen very soon). | |
86 | * | |
47d2216e PS |
87 | * @package core |
88 | * @subpackage lib | |
89 | * @copyright 2008 Petr Skoda {@link http://skodak.org} | |
90 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later | |
251387d0 | 91 | */ |
92 | class moodle_exception extends Exception { | |
34223e03 SH |
93 | |
94 | /** | |
95 | * @var string The name of the string from error.php to print | |
96 | */ | |
251387d0 | 97 | public $errorcode; |
34223e03 SH |
98 | |
99 | /** | |
100 | * @var string The name of module | |
101 | */ | |
251387d0 | 102 | public $module; |
34223e03 SH |
103 | |
104 | /** | |
105 | * @var mixed Extra words and phrases that might be required in the error string | |
106 | */ | |
251387d0 | 107 | public $a; |
34223e03 SH |
108 | |
109 | /** | |
110 | * @var string The url where the user will be prompted to continue. If no url is provided the user will be directed to the site index page. | |
111 | */ | |
251387d0 | 112 | public $link; |
34223e03 SH |
113 | |
114 | /** | |
115 | * @var string Optional information to aid the debugging process | |
116 | */ | |
eee5d9bb | 117 | public $debuginfo; |
251387d0 | 118 | |
119 | /** | |
120 | * Constructor | |
121 | * @param string $errorcode The name of the string from error.php to print | |
122 | * @param string $module name of module | |
123 | * @param string $link The url where the user will be prompted to continue. If no url is provided the user will be directed to the site index page. | |
34223e03 | 124 | * @param mixed $a Extra words and phrases that might be required in the error string |
eee5d9bb | 125 | * @param string $debuginfo optional debugging information |
251387d0 | 126 | */ |
5ca18631 | 127 | function __construct($errorcode, $module='', $link='', $a=NULL, $debuginfo=null) { |
128 | if (empty($module) || $module == 'moodle' || $module == 'core') { | |
251387d0 | 129 | $module = 'error'; |
130 | } | |
131 | ||
5ca18631 | 132 | $this->errorcode = $errorcode; |
133 | $this->module = $module; | |
134 | $this->link = $link; | |
135 | $this->a = $a; | |
98984c09 | 136 | $this->debuginfo = is_null($debuginfo) ? null : (string)$debuginfo; |
251387d0 | 137 | |
4f6be42c DM |
138 | if (get_string_manager()->string_exists($errorcode, $module)) { |
139 | $message = get_string($errorcode, $module, $a); | |
8d37c9b2 | 140 | $haserrorstring = true; |
4f6be42c DM |
141 | } else { |
142 | $message = $module . '/' . $errorcode; | |
8d37c9b2 | 143 | $haserrorstring = false; |
4f6be42c | 144 | } |
251387d0 | 145 | |
94a994b4 | 146 | if (defined('PHPUNIT_TEST') and PHPUNIT_TEST and $debuginfo) { |
a3d5830a PS |
147 | $message = "$message ($debuginfo)"; |
148 | } | |
149 | ||
8d37c9b2 DM |
150 | if (!$haserrorstring and defined('PHPUNIT_TEST') and PHPUNIT_TEST) { |
151 | // Append the contents of $a to $debuginfo so helpful information isn't lost. | |
152 | // This emulates what {@link get_exception_info()} does. Unfortunately that | |
153 | // function is not used by phpunit. | |
154 | $message .= PHP_EOL.'$a contents: '.print_r($a, true); | |
155 | } | |
156 | ||
251387d0 | 157 | parent::__construct($message, 0); |
158 | } | |
159 | } | |
160 | ||
df997f84 PS |
161 | /** |
162 | * Course/activity access exception. | |
163 | * | |
164 | * This exception is thrown from require_login() | |
47d2216e | 165 | * |
f76249cc | 166 | * @package core_access |
47d2216e PS |
167 | * @copyright 2010 Petr Skoda {@link http://skodak.org} |
168 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later | |
df997f84 PS |
169 | */ |
170 | class require_login_exception extends moodle_exception { | |
34223e03 SH |
171 | /** |
172 | * Constructor | |
173 | * @param string $debuginfo Information to aid the debugging process | |
174 | */ | |
df997f84 PS |
175 | function __construct($debuginfo) { |
176 | parent::__construct('requireloginerror', 'error', '', NULL, $debuginfo); | |
177 | } | |
178 | } | |
179 | ||
559a5dbd | 180 | /** |
181 | * Web service parameter exception class | |
92fe97f9 | 182 | * @deprecated since Moodle 2.2 - use moodle exception instead |
559a5dbd | 183 | * This exception must be thrown to the web service client when a web service parameter is invalid |
184 | * The error string is gotten from webservice.php | |
185 | */ | |
186 | class webservice_parameter_exception extends moodle_exception { | |
187 | /** | |
188 | * Constructor | |
189 | * @param string $errorcode The name of the string from webservice.php to print | |
190 | * @param string $a The name of the parameter | |
34223e03 | 191 | * @param string $debuginfo Optional information to aid debugging |
559a5dbd | 192 | */ |
93602569 JM |
193 | function __construct($errorcode=null, $a = '', $debuginfo = null) { |
194 | parent::__construct($errorcode, 'webservice', '', $a, $debuginfo); | |
559a5dbd | 195 | } |
196 | } | |
197 | ||
9a0df45a | 198 | /** |
199 | * Exceptions indicating user does not have permissions to do something | |
200 | * and the execution can not continue. | |
47d2216e | 201 | * |
f76249cc | 202 | * @package core_access |
47d2216e PS |
203 | * @copyright 2009 Petr Skoda {@link http://skodak.org} |
204 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later | |
9a0df45a | 205 | */ |
206 | class required_capability_exception extends moodle_exception { | |
34223e03 SH |
207 | /** |
208 | * Constructor | |
209 | * @param context $context The context used for the capability check | |
210 | * @param string $capability The required capability | |
211 | * @param string $errormessage The error message to show the user | |
212 | * @param string $stringfile | |
213 | */ | |
9a0df45a | 214 | function __construct($context, $capability, $errormessage, $stringfile) { |
215 | $capabilityname = get_capability_string($capability); | |
848fe203 PS |
216 | if ($context->contextlevel == CONTEXT_MODULE and preg_match('/:view$/', $capability)) { |
217 | // we can not go to mod/xx/view.php because we most probably do not have cap to view it, let's go to course instead | |
d197ea43 | 218 | $paranetcontext = context::instance_by_id(get_parent_contextid($context)); |
848fe203 PS |
219 | $link = get_context_url($paranetcontext); |
220 | } else { | |
221 | $link = get_context_url($context); | |
222 | } | |
223 | parent::__construct($errormessage, $stringfile, $link, $capabilityname); | |
9a0df45a | 224 | } |
225 | } | |
226 | ||
655bbf51 | 227 | /** |
cce1b0b9 | 228 | * Exception indicating programming error, must be fixed by a programer. For example |
229 | * a core API might throw this type of exception if a plugin calls it incorrectly. | |
b37eac91 | 230 | * |
47d2216e PS |
231 | * @package core |
232 | * @subpackage lib | |
233 | * @copyright 2008 Petr Skoda {@link http://skodak.org} | |
234 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later | |
655bbf51 | 235 | */ |
236 | class coding_exception extends moodle_exception { | |
655bbf51 | 237 | /** |
238 | * Constructor | |
239 | * @param string $hint short description of problem | |
240 | * @param string $debuginfo detailed information how to fix problem | |
241 | */ | |
242 | function __construct($hint, $debuginfo=null) { | |
243 | parent::__construct('codingerror', 'debug', '', $hint, $debuginfo); | |
a3f7cbf6 | 244 | } |
245 | } | |
246 | ||
247 | /** | |
248 | * Exception indicating malformed parameter problem. | |
249 | * This exception is not supposed to be thrown when processing | |
250 | * user submitted data in forms. It is more suitable | |
251 | * for WS and other low level stuff. | |
47d2216e PS |
252 | * |
253 | * @package core | |
254 | * @subpackage lib | |
255 | * @copyright 2009 Petr Skoda {@link http://skodak.org} | |
256 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later | |
a3f7cbf6 | 257 | */ |
258 | class invalid_parameter_exception extends moodle_exception { | |
259 | /** | |
260 | * Constructor | |
261 | * @param string $debuginfo some detailed information | |
262 | */ | |
263 | function __construct($debuginfo=null) { | |
264 | parent::__construct('invalidparameter', 'debug', '', null, $debuginfo); | |
655bbf51 | 265 | } |
266 | } | |
267 | ||
d07ff72d | 268 | /** |
269 | * Exception indicating malformed response problem. | |
270 | * This exception is not supposed to be thrown when processing | |
271 | * user submitted data in forms. It is more suitable | |
272 | * for WS and other low level stuff. | |
273 | */ | |
274 | class invalid_response_exception extends moodle_exception { | |
275 | /** | |
276 | * Constructor | |
277 | * @param string $debuginfo some detailed information | |
278 | */ | |
279 | function __construct($debuginfo=null) { | |
280 | parent::__construct('invalidresponse', 'debug', '', null, $debuginfo); | |
281 | } | |
282 | } | |
283 | ||
cce1b0b9 | 284 | /** |
1387fcdd | 285 | * An exception that indicates something really weird happened. For example, |
cce1b0b9 | 286 | * if you do switch ($context->contextlevel), and have one case for each |
287 | * CONTEXT_... constant. You might throw an invalid_state_exception in the | |
f630a546 | 288 | * default case, to just in case something really weird is going on, and |
8dc0ae8f | 289 | * $context->contextlevel is invalid - rather than ignoring this possibility. |
b37eac91 | 290 | * |
47d2216e PS |
291 | * @package core |
292 | * @subpackage lib | |
293 | * @copyright 2009 onwards Martin Dougiamas {@link http://moodle.com} | |
294 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later | |
cce1b0b9 | 295 | */ |
296 | class invalid_state_exception extends moodle_exception { | |
297 | /** | |
298 | * Constructor | |
299 | * @param string $hint short description of problem | |
300 | * @param string $debuginfo optional more detailed information | |
301 | */ | |
302 | function __construct($hint, $debuginfo=null) { | |
303 | parent::__construct('invalidstatedetected', 'debug', '', $hint, $debuginfo); | |
304 | } | |
305 | } | |
306 | ||
4031f6a2 PS |
307 | /** |
308 | * An exception that indicates incorrect permissions in $CFG->dataroot | |
309 | * | |
310 | * @package core | |
311 | * @subpackage lib | |
312 | * @copyright 2010 Petr Skoda {@link http://skodak.org} | |
313 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later | |
314 | */ | |
315 | class invalid_dataroot_permissions extends moodle_exception { | |
316 | /** | |
317 | * Constructor | |
318 | * @param string $debuginfo optional more detailed information | |
319 | */ | |
320 | function __construct($debuginfo = NULL) { | |
321 | parent::__construct('invaliddatarootpermissions', 'error', '', NULL, $debuginfo); | |
322 | } | |
323 | } | |
324 | ||
cbad562e PS |
325 | /** |
326 | * An exception that indicates that file can not be served | |
327 | * | |
328 | * @package core | |
329 | * @subpackage lib | |
330 | * @copyright 2010 Petr Skoda {@link http://skodak.org} | |
331 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later | |
332 | */ | |
333 | class file_serving_exception extends moodle_exception { | |
334 | /** | |
335 | * Constructor | |
336 | * @param string $debuginfo optional more detailed information | |
337 | */ | |
338 | function __construct($debuginfo = NULL) { | |
339 | parent::__construct('cannotservefile', 'error', '', NULL, $debuginfo); | |
340 | } | |
341 | } | |
342 | ||
251387d0 | 343 | /** |
1387fcdd | 344 | * Default exception handler, uncaught exceptions are equivalent to error() in 1.9 and earlier |
fd1a792e | 345 | * |
30fa50d0 | 346 | * @param Exception $ex |
c19bc39c | 347 | * @return void -does not return. Terminates execution! |
251387d0 | 348 | */ |
c19bc39c | 349 | function default_exception_handler($ex) { |
f0202ae9 | 350 | global $CFG, $DB, $OUTPUT, $USER, $FULLME, $SESSION, $PAGE; |
1fbdf76d | 351 | |
352 | // detect active db transactions, rollback and log as error | |
695c5ec4 | 353 | abort_all_db_transactions(); |
1fe1d104 | 354 | |
d9e07264 | 355 | if (($ex instanceof required_capability_exception) && !CLI_SCRIPT && !AJAX_SCRIPT && !empty($CFG->autologinguests) && !empty($USER->autologinguest)) { |
f0202ae9 | 356 | $SESSION->wantsurl = qualified_me(); |
d9e07264 SH |
357 | redirect(get_login_url()); |
358 | } | |
359 | ||
c19bc39c | 360 | $info = get_exception_info($ex); |
34a2777c | 361 | |
c19bc39c | 362 | if (debugging('', DEBUG_MINIMAL)) { |
2e9b772f | 363 | $logerrmsg = "Default exception handler: ".$info->message.' Debug: '.$info->debuginfo."\n".format_backtrace($info->backtrace, true); |
695c5ec4 | 364 | error_log($logerrmsg); |
fd1a792e | 365 | } |
366 | ||
c19bc39c | 367 | if (is_early_init($info->backtrace)) { |
e9e567f3 | 368 | echo bootstrap_renderer::early_error($info->message, $info->moreinfourl, $info->link, $info->backtrace, $info->debuginfo, $info->errorcode); |
b7009474 | 369 | } else { |
3c1ea58b | 370 | try { |
a56c457e PS |
371 | if ($DB) { |
372 | // If you enable db debugging and exception is thrown, the print footer prints a lot of rubbish | |
373 | $DB->set_debug(0); | |
374 | } | |
3c1ea58b PS |
375 | echo $OUTPUT->fatal_error($info->message, $info->moreinfourl, $info->link, $info->backtrace, $info->debuginfo); |
376 | } catch (Exception $out_ex) { | |
377 | // default exception handler MUST not throw any exceptions!! | |
2e9b772f | 378 | // the problem here is we do not know if page already started or not, we only know that somebody messed up in outputlib or theme |
3c1ea58b | 379 | // so we just print at least something instead of "Exception thrown without a stack frame in Unknown on line 0":-( |
1adaa404 PS |
380 | if (CLI_SCRIPT or AJAX_SCRIPT) { |
381 | // just ignore the error and send something back using the safest method | |
e9e567f3 | 382 | echo bootstrap_renderer::early_error($info->message, $info->moreinfourl, $info->link, $info->backtrace, $info->debuginfo, $info->errorcode); |
1adaa404 PS |
383 | } else { |
384 | echo bootstrap_renderer::early_error_content($info->message, $info->moreinfourl, $info->link, $info->backtrace, $info->debuginfo); | |
385 | $outinfo = get_exception_info($out_ex); | |
386 | echo bootstrap_renderer::early_error_content($outinfo->message, $outinfo->moreinfourl, $outinfo->link, $outinfo->backtrace, $outinfo->debuginfo); | |
387 | } | |
3c1ea58b | 388 | } |
7544d13c | 389 | } |
390 | ||
34a2777c | 391 | exit(1); // General error code |
392 | } | |
393 | ||
85ba1e78 PS |
394 | /** |
395 | * Default error handler, prevents some white screens. | |
396 | * @param int $errno | |
397 | * @param string $errstr | |
398 | * @param string $errfile | |
399 | * @param int $errline | |
400 | * @param array $errcontext | |
401 | * @return bool false means use default error handler | |
402 | */ | |
403 | function default_error_handler($errno, $errstr, $errfile, $errline, $errcontext) { | |
404 | if ($errno == 4096) { | |
405 | //fatal catchable error | |
406 | throw new coding_exception('PHP catchable fatal error', $errstr); | |
407 | } | |
408 | return false; | |
409 | } | |
410 | ||
695c5ec4 PS |
411 | /** |
412 | * Unconditionally abort all database transactions, this function | |
413 | * should be called from exception handlers only. | |
414 | * @return void | |
415 | */ | |
416 | function abort_all_db_transactions() { | |
417 | global $CFG, $DB, $SCRIPT; | |
418 | ||
d5a8d9aa | 419 | // default exception handler MUST not throw any exceptions!! |
03221650 | 420 | |
695c5ec4 PS |
421 | if ($DB && $DB->is_transaction_started()) { |
422 | error_log('Database transaction aborted automatically in ' . $CFG->dirroot . $SCRIPT); | |
d5a8d9aa PS |
423 | // note: transaction blocks should never change current $_SESSION |
424 | $DB->force_transaction_rollback(); | |
695c5ec4 PS |
425 | } |
426 | } | |
427 | ||
b7009474 | 428 | /** |
50764d37 PS |
429 | * This function encapsulates the tests for whether an exception was thrown in |
430 | * early init -- either during setup.php or during init of $OUTPUT. | |
b7009474 | 431 | * |
432 | * If another exception is thrown then, and if we do not take special measures, | |
433 | * we would just get a very cryptic message "Exception thrown without a stack | |
434 | * frame in Unknown on line 0". That makes debugging very hard, so we do take | |
435 | * special measures in default_exception_handler, with the help of this function. | |
436 | * | |
437 | * @param array $backtrace the stack trace to analyse. | |
438 | * @return boolean whether the stack trace is somewhere in output initialisation. | |
439 | */ | |
50764d37 | 440 | function is_early_init($backtrace) { |
b7009474 | 441 | $dangerouscode = array( |
442 | array('function' => 'header', 'type' => '->'), | |
443 | array('class' => 'bootstrap_renderer'), | |
50764d37 | 444 | array('file' => dirname(__FILE__).'/setup.php'), |
b7009474 | 445 | ); |
446 | foreach ($backtrace as $stackframe) { | |
447 | foreach ($dangerouscode as $pattern) { | |
448 | $matches = true; | |
449 | foreach ($pattern as $property => $value) { | |
450 | if (!isset($stackframe[$property]) || $stackframe[$property] != $value) { | |
451 | $matches = false; | |
452 | } | |
453 | } | |
454 | if ($matches) { | |
455 | return true; | |
456 | } | |
457 | } | |
458 | } | |
459 | return false; | |
460 | } | |
461 | ||
34a2777c | 462 | /** |
cbf05caa PS |
463 | * Abort execution by throwing of a general exception, |
464 | * default exception handler displays the error message in most cases. | |
34a2777c | 465 | * |
466 | * @param string $errorcode The name of the language string containing the error message. | |
467 | * Normally this should be in the error.php lang file. | |
468 | * @param string $module The language file to get the error message from. | |
469 | * @param string $link The url where the user will be prompted to continue. | |
470 | * If no url is provided the user will be directed to the site index page. | |
471 | * @param object $a Extra words and phrases that might be required in the error string | |
cbf05caa PS |
472 | * @param string $debuginfo optional debugging information |
473 | * @return void, always throws exception! | |
34a2777c | 474 | */ |
cbf05caa PS |
475 | function print_error($errorcode, $module = 'error', $link = '', $a = null, $debuginfo = null) { |
476 | throw new moodle_exception($errorcode, $module, $link, $a, $debuginfo); | |
34a2777c | 477 | } |
478 | ||
479 | /** | |
c19bc39c PS |
480 | * Returns detailed information about specified exception. |
481 | * @param exception $ex | |
482 | * @return object | |
34a2777c | 483 | */ |
c19bc39c | 484 | function get_exception_info($ex) { |
34a2777c | 485 | global $CFG, $DB, $SESSION; |
486 | ||
c19bc39c PS |
487 | if ($ex instanceof moodle_exception) { |
488 | $errorcode = $ex->errorcode; | |
489 | $module = $ex->module; | |
490 | $a = $ex->a; | |
491 | $link = $ex->link; | |
492 | $debuginfo = $ex->debuginfo; | |
493 | } else { | |
494 | $errorcode = 'generalexceptionmessage'; | |
495 | $module = 'error'; | |
496 | $a = $ex->getMessage(); | |
497 | $link = ''; | |
98984c09 | 498 | $debuginfo = ''; |
34a2777c | 499 | } |
500 | ||
fb71453d AD |
501 | // Append the error code to the debug info to make grepping and googling easier |
502 | $debuginfo .= PHP_EOL."Error code: $errorcode"; | |
503 | ||
c19bc39c PS |
504 | $backtrace = $ex->getTrace(); |
505 | $place = array('file'=>$ex->getFile(), 'line'=>$ex->getLine(), 'exception'=>get_class($ex)); | |
506 | array_unshift($backtrace, $place); | |
507 | ||
c84a2dbe | 508 | // Be careful, no guarantee moodlelib.php is loaded. |
34a2777c | 509 | if (empty($module) || $module == 'moodle' || $module == 'core') { |
510 | $module = 'error'; | |
511 | } | |
fb71453d AD |
512 | // Search for the $errorcode's associated string |
513 | // If not found, append the contents of $a to $debuginfo so helpful information isn't lost | |
4f6be42c DM |
514 | if (function_exists('get_string_manager')) { |
515 | if (get_string_manager()->string_exists($errorcode, $module)) { | |
516 | $message = get_string($errorcode, $module, $a); | |
517 | } elseif ($module == 'error' && get_string_manager()->string_exists($errorcode, 'moodle')) { | |
c84a2dbe | 518 | // Search in moodle file if error specified - needed for backwards compatibility |
519 | $message = get_string($errorcode, 'moodle', $a); | |
4f6be42c DM |
520 | } else { |
521 | $message = $module . '/' . $errorcode; | |
fb71453d | 522 | $debuginfo .= PHP_EOL.'$a contents: '.print_r($a, true); |
c84a2dbe | 523 | } |
524 | } else { | |
525 | $message = $module . '/' . $errorcode; | |
fb71453d | 526 | $debuginfo .= PHP_EOL.'$a contents: '.print_r($a, true); |
c84a2dbe | 527 | } |
528 | ||
529 | // Be careful, no guarantee weblib.php is loaded. | |
530 | if (function_exists('clean_text')) { | |
531 | $message = clean_text($message); | |
532 | } else { | |
533 | $message = htmlspecialchars($message); | |
34a2777c | 534 | } |
34a2777c | 535 | |
536 | if (!empty($CFG->errordocroot)) { | |
eea3341d | 537 | $errordoclink = $CFG->errordocroot . '/en/'; |
34a2777c | 538 | } else { |
eea3341d | 539 | $errordoclink = get_docs_url(); |
34a2777c | 540 | } |
eea3341d | 541 | |
34a2777c | 542 | if ($module === 'error') { |
543 | $modulelink = 'moodle'; | |
0ae8f5fc | 544 | } else { |
34a2777c | 545 | $modulelink = $module; |
251387d0 | 546 | } |
eea3341d | 547 | $moreinfourl = $errordoclink . 'error/' . $modulelink . '/' . $errorcode; |
34a2777c | 548 | |
d4a03c00 | 549 | if (empty($link)) { |
34a2777c | 550 | if (!empty($SESSION->fromurl)) { |
551 | $link = $SESSION->fromurl; | |
552 | unset($SESSION->fromurl); | |
553 | } else { | |
554 | $link = $CFG->wwwroot .'/'; | |
555 | } | |
556 | } | |
557 | ||
c8d3345c PS |
558 | // when printing an error the continue button should never link offsite |
559 | if (stripos($link, $CFG->wwwroot) === false && | |
560 | stripos($link, $CFG->httpswwwroot) === false) { | |
561 | $link = $CFG->wwwroot.'/'; | |
562 | } | |
563 | ||
365a5941 | 564 | $info = new stdClass(); |
c19bc39c PS |
565 | $info->message = $message; |
566 | $info->errorcode = $errorcode; | |
567 | $info->backtrace = $backtrace; | |
568 | $info->link = $link; | |
569 | $info->moreinfourl = $moreinfourl; | |
570 | $info->a = $a; | |
571 | $info->debuginfo = $debuginfo; | |
30fa50d0 | 572 | |
c19bc39c | 573 | return $info; |
34a2777c | 574 | } |
575 | ||
eea3341d | 576 | /** |
32c842e2 | 577 | * Returns the Moodle Docs URL in the users language for a given 'More help' link. |
eea3341d | 578 | * |
32c842e2 TH |
579 | * There are three cases: |
580 | * | |
581 | * 1. In the normal case, $path will be a short relative path 'component/thing', | |
582 | * like 'mod/folder/view' 'group/import'. This gets turned into an link to | |
583 | * MoodleDocs in the user's language, and for the appropriate Moodle version. | |
584 | * E.g. 'group/import' may become 'http://docs.moodle.org/2x/en/group/import'. | |
585 | * The 'http://docs.moodle.org' bit comes from $CFG->docroot. | |
586 | * | |
587 | * This is the only option that should be used in standard Moodle code. The other | |
588 | * two options have been implemented because they are useful for third-party plugins. | |
589 | * | |
e2554394 | 590 | * 2. $path may be an absolute URL, starting http:// or https://. In this case, |
32c842e2 TH |
591 | * the link is used as is. |
592 | * | |
593 | * 3. $path may start %%WWWROOT%%, in which case that is replaced by | |
594 | * $CFG->wwwroot to make the link. | |
595 | * | |
596 | * @param string $path the place to link to. See above for details. | |
597 | * @return string The MoodleDocs URL in the user's language. for example @link http://docs.moodle.org/2x/en/$path} | |
eea3341d | 598 | */ |
32c842e2 | 599 | function get_docs_url($path = null) { |
eea3341d | 600 | global $CFG; |
32c842e2 TH |
601 | |
602 | // Absolute URLs are used unmodified. | |
603 | if (substr($path, 0, 7) === 'http://' || substr($path, 0, 8) === 'https://') { | |
604 | return $path; | |
605 | } | |
606 | ||
607 | // Paths starting %%WWWROOT%% have that replaced by $CFG->wwwroot. | |
608 | if (substr($path, 0, 11) === '%%WWWROOT%%') { | |
609 | return $CFG->wwwroot . substr($path, 11); | |
610 | } | |
611 | ||
612 | // Otherwise we do the normal case, and construct a MoodleDocs URL relative to $CFG->docroot. | |
613 | ||
ed01233a AB |
614 | // Check that $CFG->branch has been set up, during installation it won't be. |
615 | if (empty($CFG->branch)) { | |
32c842e2 | 616 | // It's not there yet so look at version.php. |
eea3341d AB |
617 | include($CFG->dirroot.'/version.php'); |
618 | } else { | |
32c842e2 | 619 | // We can use $CFG->branch and avoid having to include version.php. |
ed01233a | 620 | $branch = $CFG->branch; |
eea3341d | 621 | } |
ed01233a AB |
622 | // ensure branch is valid. |
623 | if (!$branch) { | |
eea3341d AB |
624 | // We should never get here but in case we do lets set $branch to . |
625 | // the smart one's will know that this is the current directory | |
626 | // and the smarter ones will know that there is some smart matching | |
627 | // that will ensure people end up at the latest version of the docs. | |
628 | $branch = '.'; | |
629 | } | |
630 | if (!empty($CFG->docroot)) { | |
631 | return $CFG->docroot . '/' . $branch . '/' . current_language() . '/' . $path; | |
632 | } else { | |
32c842e2 | 633 | return 'http://docs.moodle.org/'. $branch . '/' . current_language() . '/' . $path; |
eea3341d AB |
634 | } |
635 | } | |
636 | ||
34a2777c | 637 | /** |
638 | * Formats a backtrace ready for output. | |
639 | * | |
640 | * @param array $callers backtrace array, as returned by debug_backtrace(). | |
641 | * @param boolean $plaintext if false, generates HTML, if true generates plain text. | |
642 | * @return string formatted backtrace, ready for output. | |
643 | */ | |
644 | function format_backtrace($callers, $plaintext = false) { | |
a0394be4 | 645 | // do not use $CFG->dirroot because it might not be available in destructors |
34a2777c | 646 | $dirroot = dirname(dirname(__FILE__)); |
30fa50d0 | 647 | |
34a2777c | 648 | if (empty($callers)) { |
649 | return ''; | |
650 | } | |
651 | ||
652 | $from = $plaintext ? '' : '<ul style="text-align: left">'; | |
653 | foreach ($callers as $caller) { | |
654 | if (!isset($caller['line'])) { | |
655 | $caller['line'] = '?'; // probably call_user_func() | |
656 | } | |
657 | if (!isset($caller['file'])) { | |
658 | $caller['file'] = 'unknownfile'; // probably call_user_func() | |
659 | } | |
660 | $from .= $plaintext ? '* ' : '<li>'; | |
661 | $from .= 'line ' . $caller['line'] . ' of ' . str_replace($dirroot, '', $caller['file']); | |
662 | if (isset($caller['function'])) { | |
663 | $from .= ': call to '; | |
664 | if (isset($caller['class'])) { | |
665 | $from .= $caller['class'] . $caller['type']; | |
666 | } | |
667 | $from .= $caller['function'] . '()'; | |
668 | } else if (isset($caller['exception'])) { | |
669 | $from .= ': '.$caller['exception'].' thrown'; | |
670 | } | |
671 | $from .= $plaintext ? "\n" : '</li>'; | |
672 | } | |
673 | $from .= $plaintext ? '' : '</ul>'; | |
674 | ||
675 | return $from; | |
251387d0 | 676 | } |
6a525ce2 | 677 | |
cbad562e PS |
678 | /** |
679 | * This function makes the return value of ini_get consistent if you are | |
680 | * setting server directives through the .htaccess file in apache. | |
681 | * | |
682 | * Current behavior for value set from php.ini On = 1, Off = [blank] | |
683 | * Current behavior for value set from .htaccess On = On, Off = Off | |
684 | * Contributed by jdell @ unr.edu | |
685 | * | |
686 | * @param string $ini_get_arg The argument to get | |
687 | * @return bool True for on false for not | |
688 | */ | |
689 | function ini_get_bool($ini_get_arg) { | |
690 | $temp = ini_get($ini_get_arg); | |
691 | ||
692 | if ($temp == '1' or strtolower($temp) == 'on') { | |
693 | return true; | |
694 | } | |
695 | return false; | |
696 | } | |
697 | ||
fbf2c91e | 698 | /** |
699 | * This function verifies the sanity of PHP configuration | |
700 | * and stops execution if anything critical found. | |
701 | */ | |
702 | function setup_validate_php_configuration() { | |
703 | // this must be very fast - no slow checks here!!! | |
704 | ||
705 | if (ini_get_bool('register_globals')) { | |
706 | print_error('globalswarning', 'admin'); | |
707 | } | |
708 | if (ini_get_bool('session.auto_start')) { | |
709 | print_error('sessionautostartwarning', 'admin'); | |
710 | } | |
711 | if (ini_get_bool('magic_quotes_runtime')) { | |
712 | print_error('fatalmagicquotesruntime', 'admin'); | |
713 | } | |
714 | } | |
715 | ||
12bb0c3e PS |
716 | /** |
717 | * Initialise global $CFG variable | |
718 | * @return void | |
719 | */ | |
720 | function initialise_cfg() { | |
721 | global $CFG, $DB; | |
8b8aa060 | 722 | |
12bb0c3e PS |
723 | try { |
724 | if ($DB) { | |
007bfe8b SH |
725 | $localcfg = get_config('core'); |
726 | foreach ($localcfg as $name => $value) { | |
12bb0c3e PS |
727 | if (property_exists($CFG, $name)) { |
728 | // config.php settings always take precedence | |
729 | continue; | |
730 | } | |
731 | $CFG->{$name} = $value; | |
732 | } | |
733 | } | |
69ac5d47 | 734 | } catch (dml_exception $e) { |
12bb0c3e PS |
735 | // most probably empty db, going to install soon |
736 | } | |
737 | } | |
738 | ||
11e7b506 | 739 | /** |
75781f87 | 740 | * Initialises $FULLME and friends. Private function. Should only be called from |
741 | * setup.php. | |
11e7b506 | 742 | */ |
743 | function initialise_fullme() { | |
f0f8f9a7 | 744 | global $CFG, $FULLME, $ME, $SCRIPT, $FULLSCRIPT; |
11e7b506 | 745 | |
75781f87 | 746 | // Detect common config error. |
84b88cfd | 747 | if (substr($CFG->wwwroot, -1) == '/') { |
748 | print_error('wwwrootslash', 'error'); | |
749 | } | |
750 | ||
75781f87 | 751 | if (CLI_SCRIPT) { |
752 | initialise_fullme_cli(); | |
753 | return; | |
37ccf1fe | 754 | } |
11e7b506 | 755 | |
75781f87 | 756 | $rurl = setup_get_remote_url(); |
f0f8f9a7 PS |
757 | $wwwroot = parse_url($CFG->wwwroot.'/'); |
758 | ||
759 | if (empty($rurl['host'])) { | |
760 | // missing host in request header, probably not a real browser, let's ignore them | |
761 | ||
762 | } else if (!empty($CFG->reverseproxy)) { | |
763 | // $CFG->reverseproxy specifies if reverse proxy server used | |
764 | // Used in load balancing scenarios. | |
765 | // Do not abuse this to try to solve lan/wan access problems!!!!! | |
766 | ||
767 | } else { | |
768 | if (($rurl['host'] !== $wwwroot['host']) or (!empty($wwwroot['port']) and $rurl['port'] != $wwwroot['port'])) { | |
769 | // Explain the problem and redirect them to the right URL | |
770 | if (!defined('NO_MOODLE_COOKIES')) { | |
771 | define('NO_MOODLE_COOKIES', true); | |
772 | } | |
e9e567f3 | 773 | // The login/token.php script should call the correct url/port. |
31d5b2dd | 774 | if (defined('REQUIRE_CORRECT_ACCESS') && REQUIRE_CORRECT_ACCESS) { |
e9e567f3 JM |
775 | $wwwrootport = empty($wwwroot['port'])?'':$wwwroot['port']; |
776 | $calledurl = $rurl['host']; | |
777 | if (!empty($rurl['port'])) { | |
778 | $calledurl .= ':'. $rurl['port']; | |
779 | } | |
780 | $correcturl = $wwwroot['host']; | |
781 | if (!empty($wwwrootport)) { | |
782 | $correcturl .= ':'. $wwwrootport; | |
783 | } | |
784 | throw new moodle_exception('requirecorrectaccess', 'error', '', null, | |
785 | 'You called ' . $calledurl .', you should have called ' . $correcturl); | |
786 | } | |
f0f8f9a7 PS |
787 | redirect($CFG->wwwroot, get_string('wwwrootmismatch', 'error', $CFG->wwwroot), 3); |
788 | } | |
789 | } | |
11e7b506 | 790 | |
75781f87 | 791 | // Check that URL is under $CFG->wwwroot. |
792 | if (strpos($rurl['path'], $wwwroot['path']) === 0) { | |
793 | $SCRIPT = substr($rurl['path'], strlen($wwwroot['path'])-1); | |
794 | } else { | |
795 | // Probably some weird external script | |
796 | $SCRIPT = $FULLSCRIPT = $FULLME = $ME = null; | |
11e7b506 | 797 | return; |
798 | } | |
799 | ||
75781f87 | 800 | // $CFG->sslproxy specifies if external SSL appliance is used |
801 | // (That is, the Moodle server uses http, with an external box translating everything to https). | |
802 | if (empty($CFG->sslproxy)) { | |
56048f83 | 803 | if ($rurl['scheme'] === 'http' and $wwwroot['scheme'] === 'https') { |
75781f87 | 804 | print_error('sslonlyaccess', 'error'); |
805 | } | |
56048f83 PS |
806 | } else { |
807 | if ($wwwroot['scheme'] !== 'https') { | |
808 | throw new coding_exception('Must use https address in wwwroot when ssl proxy enabled!'); | |
809 | } | |
74162134 | 810 | $rurl['scheme'] = 'https'; // make moodle believe it runs on https, squid or something else it doing it |
75781f87 | 811 | } |
812 | ||
75781f87 | 813 | // hopefully this will stop all those "clever" admins trying to set up moodle |
814 | // with two different addresses in intranet and Internet | |
e0f2718f | 815 | if (!empty($CFG->reverseproxy) && $rurl['host'] === $wwwroot['host']) { |
75781f87 | 816 | print_error('reverseproxyabused', 'error'); |
817 | } | |
818 | ||
819 | $hostandport = $rurl['scheme'] . '://' . $wwwroot['host']; | |
820 | if (!empty($wwwroot['port'])) { | |
821 | $hostandport .= ':'.$wwwroot['port']; | |
822 | } | |
823 | ||
824 | $FULLSCRIPT = $hostandport . $rurl['path']; | |
825 | $FULLME = $hostandport . $rurl['fullpath']; | |
826 | $ME = $rurl['fullpath']; | |
75781f87 | 827 | } |
828 | ||
829 | /** | |
830 | * Initialises $FULLME and friends for command line scripts. | |
831 | * This is a private method for use by initialise_fullme. | |
832 | */ | |
833 | function initialise_fullme_cli() { | |
80e30f25 | 834 | global $CFG, $FULLME, $ME, $SCRIPT, $FULLSCRIPT; |
835 | ||
75781f87 | 836 | // Urls do not make much sense in CLI scripts |
837 | $backtrace = debug_backtrace(); | |
838 | $topfile = array_pop($backtrace); | |
839 | $topfile = realpath($topfile['file']); | |
840 | $dirroot = realpath($CFG->dirroot); | |
841 | ||
842 | if (strpos($topfile, $dirroot) !== 0) { | |
843 | // Probably some weird external script | |
844 | $SCRIPT = $FULLSCRIPT = $FULLME = $ME = null; | |
845 | } else { | |
846 | $relativefile = substr($topfile, strlen($dirroot)); | |
847 | $relativefile = str_replace('\\', '/', $relativefile); // Win fix | |
848 | $SCRIPT = $FULLSCRIPT = $relativefile; | |
849 | $FULLME = $ME = null; | |
850 | } | |
851 | } | |
852 | ||
853 | /** | |
854 | * Get the URL that PHP/the web server thinks it is serving. Private function | |
855 | * used by initialise_fullme. In your code, use $PAGE->url, $SCRIPT, etc. | |
856 | * @return array in the same format that parse_url returns, with the addition of | |
857 | * a 'fullpath' element, which includes any slasharguments path. | |
858 | */ | |
859 | function setup_get_remote_url() { | |
11e7b506 | 860 | $rurl = array(); |
e0f2718f PS |
861 | if (isset($_SERVER['HTTP_HOST'])) { |
862 | list($rurl['host']) = explode(':', $_SERVER['HTTP_HOST']); | |
863 | } else { | |
864 | $rurl['host'] = null; | |
865 | } | |
11e7b506 | 866 | $rurl['port'] = $_SERVER['SERVER_PORT']; |
75781f87 | 867 | $rurl['path'] = $_SERVER['SCRIPT_NAME']; // Script path without slash arguments |
2a9c0d05 | 868 | $rurl['scheme'] = (empty($_SERVER['HTTPS']) or $_SERVER['HTTPS'] === 'off' or $_SERVER['HTTPS'] === 'Off' or $_SERVER['HTTPS'] === 'OFF') ? 'http' : 'https'; |
11e7b506 | 869 | |
870 | if (stripos($_SERVER['SERVER_SOFTWARE'], 'apache') !== false) { | |
871 | //Apache server | |
f49b359b | 872 | $rurl['fullpath'] = $_SERVER['REQUEST_URI']; |
7e13a265 | 873 | |
11e7b506 | 874 | } else if (stripos($_SERVER['SERVER_SOFTWARE'], 'iis') !== false) { |
199c1981 | 875 | //IIS - needs a lot of tweaking to make it work |
11e7b506 | 876 | $rurl['fullpath'] = $_SERVER['SCRIPT_NAME']; |
877 | ||
878 | // NOTE: ignore PATH_INFO because it is incorrectly encoded using 8bit filesystem legacy encoding in IIS | |
879 | // since 2.0 we rely on iis rewrite extenssion like Helicon ISAPI_rewrite | |
21517960 | 880 | // example rule: RewriteRule ^([^\?]+?\.php)(\/.+)$ $1\?file=$2 [QSA] |
11e7b506 | 881 | |
882 | if ($_SERVER['QUERY_STRING'] != '') { | |
991ec2ee | 883 | $rurl['fullpath'] .= '?'.$_SERVER['QUERY_STRING']; |
11e7b506 | 884 | } |
885 | $_SERVER['REQUEST_URI'] = $rurl['fullpath']; // extra IIS compatibility | |
886 | ||
f49b359b PS |
887 | /* NOTE: following servers are not fully tested! */ |
888 | ||
889 | } else if (stripos($_SERVER['SERVER_SOFTWARE'], 'lighttpd') !== false) { | |
890 | //lighttpd - not officially supported | |
f49b359b PS |
891 | $rurl['fullpath'] = $_SERVER['REQUEST_URI']; // TODO: verify this is always properly encoded |
892 | ||
893 | } else if (stripos($_SERVER['SERVER_SOFTWARE'], 'nginx') !== false) { | |
894 | //nginx - not officially supported | |
28ec73dc PS |
895 | if (!isset($_SERVER['SCRIPT_NAME'])) { |
896 | die('Invalid server configuration detected, please try to add "fastcgi_param SCRIPT_NAME $fastcgi_script_name;" to the nginx server configuration.'); | |
897 | } | |
f49b359b PS |
898 | $rurl['fullpath'] = $_SERVER['REQUEST_URI']; // TODO: verify this is always properly encoded |
899 | ||
199c1981 PS |
900 | } else if (stripos($_SERVER['SERVER_SOFTWARE'], 'cherokee') !== false) { |
901 | //cherokee - not officially supported | |
199c1981 PS |
902 | $rurl['fullpath'] = $_SERVER['REQUEST_URI']; // TODO: verify this is always properly encoded |
903 | ||
8b8aa060 PS |
904 | } else if (stripos($_SERVER['SERVER_SOFTWARE'], 'zeus') !== false) { |
905 | //zeus - not officially supported | |
8b8aa060 PS |
906 | $rurl['fullpath'] = $_SERVER['REQUEST_URI']; // TODO: verify this is always properly encoded |
907 | ||
f49b359b PS |
908 | } else if (stripos($_SERVER['SERVER_SOFTWARE'], 'LiteSpeed') !== false) { |
909 | //LiteSpeed - not officially supported | |
f49b359b PS |
910 | $rurl['fullpath'] = $_SERVER['REQUEST_URI']; // TODO: verify this is always properly encoded |
911 | ||
4e3e8464 PS |
912 | } else if ($_SERVER['SERVER_SOFTWARE'] === 'HTTPD') { |
913 | //obscure name found on some servers - this is definitely not supported | |
914 | $rurl['fullpath'] = $_SERVER['REQUEST_URI']; // TODO: verify this is always properly encoded | |
915 | ||
d860f1ad PS |
916 | } else if (strpos($_SERVER['SERVER_SOFTWARE'], 'PHP') === 0) { |
917 | // built-in PHP Development Server | |
918 | $rurl['fullpath'] = $_SERVER['REQUEST_URI']; | |
919 | ||
920 | } else { | |
75781f87 | 921 | throw new moodle_exception('unsupportedwebserver', 'error', '', $_SERVER['SERVER_SOFTWARE']); |
11e7b506 | 922 | } |
0ae36f66 PS |
923 | |
924 | // sanitize the url a bit more, the encoding style may be different in vars above | |
925 | $rurl['fullpath'] = str_replace('"', '%22', $rurl['fullpath']); | |
926 | $rurl['fullpath'] = str_replace('\'', '%27', $rurl['fullpath']); | |
927 | ||
75781f87 | 928 | return $rurl; |
11e7b506 | 929 | } |
930 | ||
d3f9f1f8 | 931 | /** |
932 | * Initializes our performance info early. | |
933 | * | |
934 | * Pairs up with get_performance_info() which is actually | |
251387d0 | 935 | * in moodlelib.php. This function is here so that we can |
936 | * call it before all the libs are pulled in. | |
d3f9f1f8 | 937 | * |
938 | * @uses $PERF | |
939 | */ | |
940 | function init_performance_info() { | |
941 | ||
6fc4ad72 | 942 | global $PERF, $CFG, $USER; |
251387d0 | 943 | |
365a5941 | 944 | $PERF = new stdClass(); |
d3f9f1f8 | 945 | $PERF->logwrites = 0; |
946 | if (function_exists('microtime')) { | |
947 | $PERF->starttime = microtime(); | |
c84a2dbe | 948 | } |
d3f9f1f8 | 949 | if (function_exists('memory_get_usage')) { |
950 | $PERF->startmemory = memory_get_usage(); | |
951 | } | |
952 | if (function_exists('posix_times')) { | |
251387d0 | 953 | $PERF->startposixtimes = posix_times(); |
d3f9f1f8 | 954 | } |
955 | } | |
956 | ||
31a99877 | 957 | /** |
958 | * Indicates whether we are in the middle of the initial Moodle install. | |
959 | * | |
960 | * Very occasionally it is necessary avoid running certain bits of code before the | |
961 | * Moodle installation has completed. The installed flag is set in admin/index.php | |
962 | * after Moodle core and all the plugins have been installed, but just before | |
963 | * the person doing the initial install is asked to choose the admin password. | |
964 | * | |
965 | * @return boolean true if the initial install is not complete. | |
966 | */ | |
967 | function during_initial_install() { | |
968 | global $CFG; | |
969 | return empty($CFG->rolesactive); | |
970 | } | |
971 | ||
76f3815b | 972 | /** |
973 | * Function to raise the memory limit to a new value. | |
974 | * Will respect the memory limit if it is higher, thus allowing | |
975 | * settings in php.ini, apache conf or command line switches | |
0f0f0768 | 976 | * to override it. |
76f3815b | 977 | * |
0f0f0768 PS |
978 | * The memory limit should be expressed with a constant |
979 | * MEMORY_STANDARD, MEMORY_EXTRA or MEMORY_HUGE. | |
980 | * It is possible to use strings or integers too (eg:'128M'). | |
76f3815b | 981 | * |
0f0f0768 PS |
982 | * @param mixed $newlimit the new memory limit |
983 | * @return bool success | |
76f3815b | 984 | */ |
11e7b506 | 985 | function raise_memory_limit($newlimit) { |
0f0f0768 | 986 | global $CFG; |
76f3815b | 987 | |
0f0f0768 PS |
988 | if ($newlimit == MEMORY_UNLIMITED) { |
989 | ini_set('memory_limit', -1); | |
990 | return true; | |
991 | ||
992 | } else if ($newlimit == MEMORY_STANDARD) { | |
cd3059b1 PS |
993 | if (PHP_INT_SIZE > 4) { |
994 | $newlimit = get_real_size('128M'); // 64bit needs more memory | |
995 | } else { | |
996 | $newlimit = get_real_size('96M'); | |
997 | } | |
0f0f0768 PS |
998 | |
999 | } else if ($newlimit == MEMORY_EXTRA) { | |
cd3059b1 PS |
1000 | if (PHP_INT_SIZE > 4) { |
1001 | $newlimit = get_real_size('384M'); // 64bit needs more memory | |
1002 | } else { | |
1003 | $newlimit = get_real_size('256M'); | |
1004 | } | |
d827f7b8 | 1005 | if (!empty($CFG->extramemorylimit)) { |
0f0f0768 PS |
1006 | $extra = get_real_size($CFG->extramemorylimit); |
1007 | if ($extra > $newlimit) { | |
1008 | $newlimit = $extra; | |
1009 | } | |
1010 | } | |
1011 | ||
1012 | } else if ($newlimit == MEMORY_HUGE) { | |
1013 | $newlimit = get_real_size('2G'); | |
1014 | ||
1015 | } else { | |
1016 | $newlimit = get_real_size($newlimit); | |
1017 | } | |
1018 | ||
1019 | if ($newlimit <= 0) { | |
1020 | debugging('Invalid memory limit specified.'); | |
76f3815b | 1021 | return false; |
1022 | } | |
1023 | ||
cbad562e | 1024 | $cur = ini_get('memory_limit'); |
76f3815b | 1025 | if (empty($cur)) { |
1026 | // if php is compiled without --enable-memory-limits | |
1027 | // apparently memory_limit is set to '' | |
0f0f0768 | 1028 | $cur = 0; |
76f3815b | 1029 | } else { |
1030 | if ($cur == -1){ | |
1031 | return true; // unlimited mem! | |
1032 | } | |
0f0f0768 | 1033 | $cur = get_real_size($cur); |
76f3815b | 1034 | } |
1035 | ||
0f0f0768 | 1036 | if ($newlimit > $cur) { |
76f3815b | 1037 | ini_set('memory_limit', $newlimit); |
7022dd39 | 1038 | return true; |
1039 | } | |
1040 | return false; | |
1041 | } | |
1042 | ||
1043 | /** | |
1044 | * Function to reduce the memory limit to a new value. | |
1045 | * Will respect the memory limit if it is lower, thus allowing | |
1046 | * settings in php.ini, apache conf or command line switches | |
1047 | * to override it | |
1048 | * | |
1049 | * The memory limit should be expressed with a string (eg:'64M') | |
1050 | * | |
1051 | * @param string $newlimit the new memory limit | |
1052 | * @return bool | |
1053 | */ | |
f630a546 | 1054 | function reduce_memory_limit($newlimit) { |
7022dd39 | 1055 | if (empty($newlimit)) { |
1056 | return false; | |
1057 | } | |
cbad562e | 1058 | $cur = ini_get('memory_limit'); |
7022dd39 | 1059 | if (empty($cur)) { |
1060 | // if php is compiled without --enable-memory-limits | |
1061 | // apparently memory_limit is set to '' | |
0f0f0768 | 1062 | $cur = 0; |
7022dd39 | 1063 | } else { |
1064 | if ($cur == -1){ | |
1065 | return true; // unlimited mem! | |
1066 | } | |
1067 | $cur = get_real_size($cur); | |
1068 | } | |
1069 | ||
1070 | $new = get_real_size($newlimit); | |
1071 | // -1 is smaller, but it means unlimited | |
1072 | if ($new < $cur && $new != -1) { | |
1073 | ini_set('memory_limit', $newlimit); | |
76f3815b | 1074 | return true; |
1075 | } | |
1076 | return false; | |
1077 | } | |
1078 | ||
1079 | /** | |
1080 | * Converts numbers like 10M into bytes. | |
1081 | * | |
0f0f0768 PS |
1082 | * @param string $size The size to be converted |
1083 | * @return int | |
76f3815b | 1084 | */ |
0f0f0768 | 1085 | function get_real_size($size = 0) { |
76f3815b | 1086 | if (!$size) { |
1087 | return 0; | |
1088 | } | |
11e7b506 | 1089 | $scan = array(); |
0f0f0768 PS |
1090 | $scan['GB'] = 1073741824; |
1091 | $scan['Gb'] = 1073741824; | |
1092 | $scan['G'] = 1073741824; | |
76f3815b | 1093 | $scan['MB'] = 1048576; |
1094 | $scan['Mb'] = 1048576; | |
1095 | $scan['M'] = 1048576; | |
1096 | $scan['m'] = 1048576; | |
1097 | $scan['KB'] = 1024; | |
1098 | $scan['Kb'] = 1024; | |
1099 | $scan['K'] = 1024; | |
1100 | $scan['k'] = 1024; | |
1101 | ||
1102 | while (list($key) = each($scan)) { | |
1103 | if ((strlen($size)>strlen($key))&&(substr($size, strlen($size) - strlen($key))==$key)) { | |
1104 | $size = substr($size, 0, strlen($size) - strlen($key)) * $scan[$key]; | |
1105 | break; | |
1106 | } | |
1107 | } | |
1108 | return $size; | |
1109 | } | |
1110 | ||
cbad562e | 1111 | /** |
871ed458 PS |
1112 | * Try to disable all output buffering and purge |
1113 | * all headers. | |
1114 | * | |
34223e03 | 1115 | * @access private to be called only from lib/setup.php ! |
cbad562e PS |
1116 | * @return void |
1117 | */ | |
1118 | function disable_output_buffering() { | |
1119 | $olddebug = error_reporting(0); | |
1120 | ||
1121 | // disable compression, it would prevent closing of buffers | |
1122 | if (ini_get_bool('zlib.output_compression')) { | |
1123 | ini_set('zlib.output_compression', 'Off'); | |
1124 | } | |
1125 | ||
1126 | // try to flush everything all the time | |
1127 | ob_implicit_flush(true); | |
1128 | ||
1129 | // close all buffers if possible and discard any existing output | |
1130 | // this can actually work around some whitespace problems in config.php | |
1131 | while(ob_get_level()) { | |
1132 | if (!ob_end_clean()) { | |
1133 | // prevent infinite loop when buffer can not be closed | |
1134 | break; | |
1135 | } | |
1136 | } | |
1137 | ||
99302c5e PS |
1138 | // disable any other output handlers |
1139 | ini_set('output_handler', ''); | |
1140 | ||
cbad562e PS |
1141 | error_reporting($olddebug); |
1142 | } | |
1143 | ||
5e39d7aa | 1144 | /** |
1145 | * Check whether a major upgrade is needed. That is defined as an upgrade that | |
1146 | * changes something really fundamental in the database, so nothing can possibly | |
1147 | * work until the database has been updated, and that is defined by the hard-coded | |
1148 | * version number in this function. | |
1149 | */ | |
1150 | function redirect_if_major_upgrade_required() { | |
1151 | global $CFG; | |
1e7db9fe DW |
1152 | $lastmajordbchanges = 2013021100.01; |
1153 | if (empty($CFG->version) or (float)$CFG->version < $lastmajordbchanges or | |
5e39d7aa | 1154 | during_initial_install() or !empty($CFG->adminsetuppending)) { |
1155 | try { | |
1156 | @session_get_instance()->terminate_current(); | |
1157 | } catch (Exception $e) { | |
1158 | // Ignore any errors, redirect to upgrade anyway. | |
1159 | } | |
a38c7a10 | 1160 | $url = $CFG->wwwroot . '/' . $CFG->admin . '/index.php'; |
5e39d7aa | 1161 | @header($_SERVER['SERVER_PROTOCOL'] . ' 303 See Other'); |
a38c7a10 PS |
1162 | @header('Location: ' . $url); |
1163 | echo bootstrap_renderer::plain_redirect_message(htmlspecialchars($url)); | |
5e39d7aa | 1164 | exit; |
1165 | } | |
1166 | } | |
1167 | ||
d3f9f1f8 | 1168 | /** |
4031f6a2 PS |
1169 | * Function to check if a directory exists and by default create it if not exists. |
1170 | * | |
1171 | * Previously this was accepting paths only from dataroot, but we now allow | |
1172 | * files outside of dataroot if you supply custom paths for some settings in config.php. | |
1173 | * This function does not verify that the directory is writable. | |
d3f9f1f8 | 1174 | * |
0b9f2a02 PS |
1175 | * NOTE: this function uses current file stat cache, |
1176 | * please use clearstatcache() before this if you expect that the | |
1177 | * directories may have been removed recently from a different request. | |
1178 | * | |
4031f6a2 PS |
1179 | * @param string $dir absolute directory path |
1180 | * @param boolean $create directory if does not exist | |
1181 | * @param boolean $recursive create directory recursively | |
1182 | * @return boolean true if directory exists or created, false otherwise | |
d3f9f1f8 | 1183 | */ |
4031f6a2 | 1184 | function check_dir_exists($dir, $create = true, $recursive = true) { |
d3f9f1f8 | 1185 | global $CFG; |
1186 | ||
4031f6a2 | 1187 | umask(0000); // just in case some evil code changed it |
d3f9f1f8 | 1188 | |
4031f6a2 PS |
1189 | if (is_dir($dir)) { |
1190 | return true; | |
1191 | } | |
d3f9f1f8 | 1192 | |
4031f6a2 PS |
1193 | if (!$create) { |
1194 | return false; | |
d100b8a0 | 1195 | } |
1196 | ||
4031f6a2 PS |
1197 | return mkdir($dir, $CFG->directorypermissions, $recursive); |
1198 | } | |
1199 | ||
1200 | /** | |
e6958907 | 1201 | * Create a directory and make sure it is writable. |
4031f6a2 | 1202 | * |
32db0f0d | 1203 | * @private |
e6958907 | 1204 | * @param string $dir the full path of the directory to be created |
4031f6a2 PS |
1205 | * @param bool $exceptiononerror throw exception if error encountered |
1206 | * @return string|false Returns full path to directory if successful, false if not; may throw exception | |
1207 | */ | |
e6958907 | 1208 | function make_writable_directory($dir, $exceptiononerror = true) { |
4031f6a2 PS |
1209 | global $CFG; |
1210 | ||
4031f6a2 PS |
1211 | if (file_exists($dir) and !is_dir($dir)) { |
1212 | if ($exceptiononerror) { | |
1213 | throw new coding_exception($dir.' directory can not be created, file with the same name already exists.'); | |
1214 | } else { | |
1215 | return false; | |
1216 | } | |
1217 | } | |
1218 | ||
1219 | umask(0000); // just in case some evil code changed it | |
1220 | ||
1221 | if (!file_exists($dir)) { | |
1222 | if (!mkdir($dir, $CFG->directorypermissions, true)) { | |
1223 | if ($exceptiononerror) { | |
1224 | throw new invalid_dataroot_permissions($dir.' can not be created, check permissions.'); | |
1225 | } else { | |
d3f9f1f8 | 1226 | return false; |
1227 | } | |
d3f9f1f8 | 1228 | } |
1229 | } | |
1230 | ||
4031f6a2 PS |
1231 | if (!is_writable($dir)) { |
1232 | if ($exceptiononerror) { | |
1233 | throw new invalid_dataroot_permissions($dir.' is not writable, check permissions.'); | |
1234 | } else { | |
1235 | return false; | |
1236 | } | |
1237 | } | |
1238 | ||
1239 | return $dir; | |
d3f9f1f8 | 1240 | } |
1241 | ||
e6958907 TL |
1242 | /** |
1243 | * Protect a directory from web access. | |
1244 | * Could be extended in the future to support other mechanisms (e.g. other webservers). | |
1245 | * | |
32db0f0d | 1246 | * @private |
e6958907 TL |
1247 | * @param string $dir the full path of the directory to be protected |
1248 | */ | |
1249 | function protect_directory($dir) { | |
1250 | // Make sure a .htaccess file is here, JUST IN CASE the files area is in the open and .htaccess is supported | |
1251 | if (!file_exists("$dir/.htaccess")) { | |
1252 | if ($handle = fopen("$dir/.htaccess", 'w')) { // For safety | |
1253 | @fwrite($handle, "deny from all\r\nAllowOverride None\r\nNote: this file is broken intentionally, we do not want anybody to undo it in subdirectory!\r\n"); | |
1254 | @fclose($handle); | |
1255 | } | |
1256 | } | |
1257 | } | |
1258 | ||
1259 | /** | |
1260 | * Create a directory under dataroot and make sure it is writable. | |
32db0f0d | 1261 | * Do not use for temporary and cache files - see make_temp_directory() and make_cache_directory(). |
e6958907 TL |
1262 | * |
1263 | * @param string $directory the full path of the directory to be created under $CFG->dataroot | |
1264 | * @param bool $exceptiononerror throw exception if error encountered | |
1265 | * @return string|false Returns full path to directory if successful, false if not; may throw exception | |
1266 | */ | |
1267 | function make_upload_directory($directory, $exceptiononerror = true) { | |
1268 | global $CFG; | |
32db0f0d PS |
1269 | |
1270 | if (strpos($directory, 'temp/') === 0 or $directory === 'temp') { | |
1271 | debugging('Use make_temp_directory() for creation of temporary directory and $CFG->tempdir to get the location.'); | |
1272 | ||
1273 | } else if (strpos($directory, 'cache/') === 0 or $directory === 'cache') { | |
1274 | debugging('Use make_cache_directory() for creation of chache directory and $CFG->cachedir to get the location.'); | |
1275 | } | |
1276 | ||
e6958907 TL |
1277 | protect_directory($CFG->dataroot); |
1278 | return make_writable_directory("$CFG->dataroot/$directory", $exceptiononerror); | |
1279 | } | |
1280 | ||
1281 | /** | |
1282 | * Create a directory under tempdir and make sure it is writable. | |
32db0f0d | 1283 | * Temporary files should be used during the current request only! |
e6958907 TL |
1284 | * |
1285 | * @param string $directory the full path of the directory to be created under $CFG->tempdir | |
1286 | * @param bool $exceptiononerror throw exception if error encountered | |
1287 | * @return string|false Returns full path to directory if successful, false if not; may throw exception | |
1288 | */ | |
1289 | function make_temp_directory($directory, $exceptiononerror = true) { | |
1290 | global $CFG; | |
adf0fc0e PS |
1291 | if ($CFG->tempdir !== "$CFG->dataroot/temp") { |
1292 | check_dir_exists($CFG->tempdir, true, true); | |
1293 | protect_directory($CFG->tempdir); | |
7a7edd21 PS |
1294 | } else { |
1295 | protect_directory($CFG->dataroot); | |
adf0fc0e | 1296 | } |
e6958907 TL |
1297 | return make_writable_directory("$CFG->tempdir/$directory", $exceptiononerror); |
1298 | } | |
1299 | ||
1300 | /** | |
1301 | * Create a directory under cachedir and make sure it is writable. | |
1302 | * | |
1303 | * @param string $directory the full path of the directory to be created under $CFG->cachedir | |
1304 | * @param bool $exceptiononerror throw exception if error encountered | |
1305 | * @return string|false Returns full path to directory if successful, false if not; may throw exception | |
1306 | */ | |
1307 | function make_cache_directory($directory, $exceptiononerror = true) { | |
1308 | global $CFG; | |
adf0fc0e PS |
1309 | if ($CFG->cachedir !== "$CFG->dataroot/cache") { |
1310 | check_dir_exists($CFG->cachedir, true, true); | |
1311 | protect_directory($CFG->cachedir); | |
7a7edd21 PS |
1312 | } else { |
1313 | protect_directory($CFG->dataroot); | |
adf0fc0e | 1314 | } |
e6958907 TL |
1315 | return make_writable_directory("$CFG->cachedir/$directory", $exceptiononerror); |
1316 | } | |
1317 | ||
81b58cc2 PS |
1318 | /** |
1319 | * Checks if current user is a web crawler. | |
1320 | * | |
1321 | * This list can not be made complete, this is not a security | |
1322 | * restriction, we make the list only to help these sites | |
1323 | * especially when automatic guest login is disabled. | |
1324 | * | |
1325 | * If admin needs security they should enable forcelogin | |
1326 | * and disable guest access!! | |
1327 | * | |
1328 | * @return bool | |
1329 | */ | |
1330 | function is_web_crawler() { | |
1331 | if (!empty($_SERVER['HTTP_USER_AGENT'])) { | |
1332 | if (strpos($_SERVER['HTTP_USER_AGENT'], 'Googlebot') !== false ) { | |
1333 | return true; | |
1334 | } else if (strpos($_SERVER['HTTP_USER_AGENT'], 'google.com') !== false ) { // Google | |
1335 | return true; | |
1336 | } else if (strpos($_SERVER['HTTP_USER_AGENT'], 'Yahoo! Slurp') !== false ) { // Yahoo | |
1337 | return true; | |
1338 | } else if (strpos($_SERVER['HTTP_USER_AGENT'], '[ZSEBOT]') !== false ) { // Zoomspider | |
1339 | return true; | |
f28af1dd PS |
1340 | } else if (stripos($_SERVER['HTTP_USER_AGENT'], 'msnbot') !== false ) { // MSN Search |
1341 | return true; | |
1342 | } else if (strpos($_SERVER['HTTP_USER_AGENT'], 'bingbot') !== false ) { // Bing | |
81b58cc2 PS |
1343 | return true; |
1344 | } else if (strpos($_SERVER['HTTP_USER_AGENT'], 'Yandex') !== false ) { | |
1345 | return true; | |
1346 | } else if (strpos($_SERVER['HTTP_USER_AGENT'], 'AltaVista') !== false ) { | |
1347 | return true; | |
f28af1dd PS |
1348 | } else if (stripos($_SERVER['HTTP_USER_AGENT'], 'baiduspider') !== false ) { // Baidu |
1349 | return true; | |
1350 | } else if (strpos($_SERVER['HTTP_USER_AGENT'], 'Teoma') !== false ) { // Ask.com | |
1351 | return true; | |
81b58cc2 PS |
1352 | } |
1353 | } | |
1354 | return false; | |
1355 | } | |
2142d492 | 1356 | |
c84a2dbe | 1357 | /** |
1358 | * This class solves the problem of how to initialise $OUTPUT. | |
1359 | * | |
1360 | * The problem is caused be two factors | |
1361 | * <ol> | |
1362 | * <li>On the one hand, we cannot be sure when output will start. In particular, | |
30fa50d0 | 1363 | * an error, which needs to be displayed, could be thrown at any time.</li> |
c84a2dbe | 1364 | * <li>On the other hand, we cannot be sure when we will have all the information |
1365 | * necessary to correctly initialise $OUTPUT. $OUTPUT depends on the theme, which | |
1366 | * (potentially) depends on the current course, course categories, and logged in user. | |
1367 | * It also depends on whether the current page requires HTTPS.</li> | |
1368 | * </ol> | |
1369 | * | |
1370 | * So, it is hard to find a single natural place during Moodle script execution, | |
1371 | * which we can guarantee is the right time to initialise $OUTPUT. Instead we | |
1372 | * adopt the following strategy | |
1373 | * <ol> | |
1374 | * <li>We will initialise $OUTPUT the first time it is used.</li> | |
1375 | * <li>If, after $OUTPUT has been initialised, the script tries to change something | |
7e0d6675 | 1376 | * that $OUTPUT depends on, we throw an exception making it clear that the script |
c84a2dbe | 1377 | * did something wrong. |
1378 | * </ol> | |
1379 | * | |
1380 | * The only problem with that is, how do we initialise $OUTPUT on first use if, | |
1381 | * it is going to be used like $OUTPUT->somthing(...)? Well that is where this | |
1382 | * class comes in. Initially, we set up $OUTPUT = new bootstrap_renderer(). Then, | |
1383 | * when any method is called on that object, we initialise $OUTPUT, and pass the call on. | |
1384 | * | |
1385 | * Note that this class is used before lib/outputlib.php has been loaded, so we | |
1387fcdd | 1386 | * must be careful referring to classes/functions from there, they may not be |
c84a2dbe | 1387 | * defined yet, and we must avoid fatal errors. |
1388 | * | |
1389 | * @copyright 2009 Tim Hunt | |
1390 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later | |
1391 | * @since Moodle 2.0 | |
1392 | */ | |
1393 | class bootstrap_renderer { | |
1394 | /** | |
1395 | * Handles re-entrancy. Without this, errors or debugging output that occur | |
1396 | * during the initialisation of $OUTPUT, cause infinite recursion. | |
1397 | * @var boolean | |
1398 | */ | |
1399 | protected $initialising = false; | |
1400 | ||
b7009474 | 1401 | /** |
1402 | * Have we started output yet? | |
1403 | * @return boolean true if the header has been printed. | |
1404 | */ | |
1405 | public function has_started() { | |
1406 | return false; | |
1407 | } | |
1408 | ||
41701ffd PS |
1409 | /** |
1410 | * Constructor - to be used by core code only. | |
34223e03 SH |
1411 | * @param string $method The method to call |
1412 | * @param array $arguments Arguments to pass to the method being called | |
41701ffd PS |
1413 | * @return string |
1414 | */ | |
c84a2dbe | 1415 | public function __call($method, $arguments) { |
b7009474 | 1416 | global $OUTPUT, $PAGE; |
c84a2dbe | 1417 | |
87b6851c | 1418 | $recursing = false; |
1419 | if ($method == 'notification') { | |
1387fcdd | 1420 | // Catch infinite recursion caused by debugging output during print_header. |
87b6851c | 1421 | $backtrace = debug_backtrace(); |
1422 | array_shift($backtrace); | |
1423 | array_shift($backtrace); | |
50764d37 | 1424 | $recursing = is_early_init($backtrace); |
87b6851c | 1425 | } |
1426 | ||
eb5bdb35 PS |
1427 | $earlymethods = array( |
1428 | 'fatal_error' => 'early_error', | |
1429 | 'notification' => 'early_notification', | |
1430 | ); | |
1431 | ||
c84a2dbe | 1432 | // If lib/outputlib.php has been loaded, call it. |
87b6851c | 1433 | if (!empty($PAGE) && !$recursing) { |
eb5bdb35 PS |
1434 | if (array_key_exists($method, $earlymethods)) { |
1435 | //prevent PAGE->context warnings - exceptions might appear before we set any context | |
1436 | $PAGE->set_context(null); | |
1437 | } | |
b7009474 | 1438 | $PAGE->initialise_theme_and_output(); |
1439 | return call_user_func_array(array($OUTPUT, $method), $arguments); | |
c84a2dbe | 1440 | } |
2142d492 | 1441 | |
c84a2dbe | 1442 | $this->initialising = true; |
eb5bdb35 | 1443 | |
c84a2dbe | 1444 | // Too soon to initialise $OUTPUT, provide a couple of key methods. |
c84a2dbe | 1445 | if (array_key_exists($method, $earlymethods)) { |
1446 | return call_user_func_array(array('bootstrap_renderer', $earlymethods[$method]), $arguments); | |
1447 | } | |
1448 | ||
1449 | throw new coding_exception('Attempt to start output before enough information is known to initialise the theme.'); | |
1450 | } | |
1451 | ||
1452 | /** | |
1387fcdd | 1453 | * Returns nicely formatted error message in a div box. |
f0f8f9a7 PS |
1454 | * @static |
1455 | * @param string $message error message | |
41701ffd PS |
1456 | * @param string $moreinfourl (ignored in early errors) |
1457 | * @param string $link (ignored in early errors) | |
f0f8f9a7 PS |
1458 | * @param array $backtrace |
1459 | * @param string $debuginfo | |
30fa50d0 | 1460 | * @return string |
c84a2dbe | 1461 | */ |
3c1ea58b | 1462 | public static function early_error_content($message, $moreinfourl, $link, $backtrace, $debuginfo = null) { |
b7009474 | 1463 | global $CFG; |
1464 | ||
3c1ea58b PS |
1465 | $content = '<div style="margin-top: 6em; margin-left:auto; margin-right:auto; color:#990000; text-align:center; font-size:large; border-width:1px; |
1466 | border-color:black; background-color:#ffffee; border-style:solid; border-radius: 20px; border-collapse: collapse; | |
1467 | width: 80%; -moz-border-radius: 20px; padding: 15px"> | |
1468 | ' . $message . ' | |
1469 | </div>'; | |
7383a7e2 SH |
1470 | // Check whether debug is set. |
1471 | $debug = (!empty($CFG->debug) && $CFG->debug >= DEBUG_DEVELOPER); | |
1472 | // Also check we have it set in the config file. This occurs if the method to read the config table from the | |
1473 | // database fails, reading from the config table is the first database interaction we have. | |
1474 | $debug = $debug || (!empty($CFG->config_php_settings['debug']) && $CFG->config_php_settings['debug'] >= DEBUG_DEVELOPER ); | |
1475 | if ($debug) { | |
3c1ea58b | 1476 | if (!empty($debuginfo)) { |
c5d18164 PS |
1477 | $debuginfo = s($debuginfo); // removes all nasty JS |
1478 | $debuginfo = str_replace("\n", '<br />', $debuginfo); // keep newlines | |
1479 | $content .= '<div class="notifytiny">Debug info: ' . $debuginfo . '</div>'; | |
3c1ea58b PS |
1480 | } |
1481 | if (!empty($backtrace)) { | |
1482 | $content .= '<div class="notifytiny">Stack trace: ' . format_backtrace($backtrace, false) . '</div>'; | |
1483 | } | |
1484 | } | |
1485 | ||
1486 | return $content; | |
1487 | } | |
1488 | ||
1489 | /** | |
1490 | * This function should only be called by this class, or from exception handlers | |
f0f8f9a7 PS |
1491 | * @static |
1492 | * @param string $message error message | |
41701ffd PS |
1493 | * @param string $moreinfourl (ignored in early errors) |
1494 | * @param string $link (ignored in early errors) | |
f0f8f9a7 | 1495 | * @param array $backtrace |
41701ffd | 1496 | * @param string $debuginfo extra information for developers |
3c1ea58b PS |
1497 | * @return string |
1498 | */ | |
e9e567f3 | 1499 | public static function early_error($message, $moreinfourl, $link, $backtrace, $debuginfo = null, $errorcode = null) { |
1adaa404 PS |
1500 | global $CFG; |
1501 | ||
1502 | if (CLI_SCRIPT) { | |
1503 | echo "!!! $message !!!\n"; | |
1504 | if (!empty($CFG->debug) and $CFG->debug >= DEBUG_DEVELOPER) { | |
1505 | if (!empty($debuginfo)) { | |
1506 | echo "\nDebug info: $debuginfo"; | |
1507 | } | |
1508 | if (!empty($backtrace)) { | |
1509 | echo "\nStack trace: " . format_backtrace($backtrace, true); | |
1510 | } | |
1511 | } | |
1512 | return; | |
1513 | ||
1514 | } else if (AJAX_SCRIPT) { | |
1515 | $e = new stdClass(); | |
1516 | $e->error = $message; | |
1517 | $e->stacktrace = NULL; | |
1518 | $e->debuginfo = NULL; | |
1519 | if (!empty($CFG->debug) and $CFG->debug >= DEBUG_DEVELOPER) { | |
1520 | if (!empty($debuginfo)) { | |
1521 | $e->debuginfo = $debuginfo; | |
1522 | } | |
1523 | if (!empty($backtrace)) { | |
1524 | $e->stacktrace = format_backtrace($backtrace, true); | |
1525 | } | |
1526 | } | |
e9e567f3 | 1527 | $e->errorcode = $errorcode; |
8a7703ce | 1528 | @header('Content-Type: application/json; charset=utf-8'); |
1adaa404 PS |
1529 | echo json_encode($e); |
1530 | return; | |
1531 | } | |
1532 | ||
c84a2dbe | 1533 | // In the name of protocol correctness, monitoring and performance |
1387fcdd | 1534 | // profiling, set the appropriate error headers for machine consumption |
c84a2dbe | 1535 | if (isset($_SERVER['SERVER_PROTOCOL'])) { |
1536 | // Avoid it with cron.php. Note that we assume it's HTTP/1.x | |
1387fcdd | 1537 | // The 503 ode here means our Moodle does not work at all, the error happened too early |
c84a2dbe | 1538 | @header($_SERVER['SERVER_PROTOCOL'] . ' 503 Service Unavailable'); |
1539 | } | |
1540 | ||
1541 | // better disable any caching | |
1542 | @header('Content-Type: text/html; charset=utf-8'); | |
398862b9 | 1543 | @header('X-UA-Compatible: IE=edge'); |
c84a2dbe | 1544 | @header('Cache-Control: no-store, no-cache, must-revalidate'); |
1545 | @header('Cache-Control: post-check=0, pre-check=0', false); | |
1546 | @header('Pragma: no-cache'); | |
1547 | @header('Expires: Mon, 20 Aug 1969 09:23:00 GMT'); | |
1548 | @header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT'); | |
1549 | ||
5e39d7aa | 1550 | if (function_exists('get_string')) { |
c84a2dbe | 1551 | $strerror = get_string('error'); |
1552 | } else { | |
c84a2dbe | 1553 | $strerror = 'Error'; |
1554 | } | |
1555 | ||
3c1ea58b | 1556 | $content = self::early_error_content($message, $moreinfourl, $link, $backtrace, $debuginfo); |
5e39d7aa | 1557 | |
1558 | return self::plain_page($strerror, $content); | |
c84a2dbe | 1559 | } |
1560 | ||
f0f8f9a7 PS |
1561 | /** |
1562 | * Early notification message | |
1563 | * @static | |
34223e03 | 1564 | * @param string $message |
41701ffd | 1565 | * @param string $classes usually notifyproblem or notifysuccess |
f0f8f9a7 PS |
1566 | * @return string |
1567 | */ | |
c84a2dbe | 1568 | public static function early_notification($message, $classes = 'notifyproblem') { |
1569 | return '<div class="' . $classes . '">' . $message . '</div>'; | |
1570 | } | |
5e39d7aa | 1571 | |
f0f8f9a7 PS |
1572 | /** |
1573 | * Page should redirect message. | |
1574 | * @static | |
34223e03 | 1575 | * @param string $encodedurl redirect url |
f0f8f9a7 PS |
1576 | * @return string |
1577 | */ | |
5e39d7aa | 1578 | public static function plain_redirect_message($encodedurl) { |
41701ffd | 1579 | $message = '<div style="margin-top: 3em; margin-left:auto; margin-right:auto; text-align:center;">' . get_string('pageshouldredirect') . '<br /><a href="'. |
f0f8f9a7 | 1580 | $encodedurl .'">'. get_string('continue') .'</a></div>'; |
a0a268d5 | 1581 | return self::plain_page(get_string('redirect'), $message); |
5e39d7aa | 1582 | } |
1583 | ||
f0f8f9a7 PS |
1584 | /** |
1585 | * Early redirection page, used before full init of $PAGE global | |
1586 | * @static | |
34223e03 SH |
1587 | * @param string $encodedurl redirect url |
1588 | * @param string $message redirect message | |
1589 | * @param int $delay time in seconds | |
41701ffd | 1590 | * @return string redirect page |
f0f8f9a7 PS |
1591 | */ |
1592 | public static function early_redirect_message($encodedurl, $message, $delay) { | |
1593 | $meta = '<meta http-equiv="refresh" content="'. $delay .'; url='. $encodedurl .'" />'; | |
1594 | $content = self::early_error_content($message, null, null, null); | |
1595 | $content .= self::plain_redirect_message($encodedurl); | |
1596 | ||
1597 | return self::plain_page(get_string('redirect'), $content, $meta); | |
1598 | } | |
1599 | ||
1600 | /** | |
1601 | * Output basic html page. | |
1602 | * @static | |
34223e03 SH |
1603 | * @param string $title page title |
1604 | * @param string $content page content | |
41701ffd PS |
1605 | * @param string $meta meta tag |
1606 | * @return string html page | |
f0f8f9a7 | 1607 | */ |
48e114a5 | 1608 | public static function plain_page($title, $content, $meta = '') { |
5e39d7aa | 1609 | if (function_exists('get_string') && function_exists('get_html_lang')) { |
1610 | $htmllang = get_html_lang(); | |
1611 | } else { | |
1612 | $htmllang = ''; | |
1613 | } | |
1614 | ||
48e114a5 PS |
1615 | return '<!DOCTYPE html> |
1616 | <html ' . $htmllang . '> | |
5e39d7aa | 1617 | <head> |
1618 | <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> | |
f0f8f9a7 | 1619 | '.$meta.' |
5e39d7aa | 1620 | <title>' . $title . '</title> |
1621 | </head><body>' . $content . '</body></html>'; | |
1622 | } | |
c84a2dbe | 1623 | } |