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