| Drupal | PHP Cross Reference | Content Management Systems |
1 <?php 2 3 /** 4 * @file 5 * Functions that need to be loaded on every Drupal request. 6 */ 7 8 /** 9 * The current system version. 10 */ 11 define('VERSION', '7.17'); 12 13 /** 14 * Core API compatibility. 15 */ 16 define('DRUPAL_CORE_COMPATIBILITY', '7.x'); 17 18 /** 19 * Minimum supported version of PHP. 20 */ 21 define('DRUPAL_MINIMUM_PHP', '5.2.4'); 22 23 /** 24 * Minimum recommended value of PHP memory_limit. 25 */ 26 define('DRUPAL_MINIMUM_PHP_MEMORY_LIMIT', '32M'); 27 28 /** 29 * Error reporting level: display no errors. 30 */ 31 define('ERROR_REPORTING_HIDE', 0); 32 33 /** 34 * Error reporting level: display errors and warnings. 35 */ 36 define('ERROR_REPORTING_DISPLAY_SOME', 1); 37 38 /** 39 * Error reporting level: display all messages. 40 */ 41 define('ERROR_REPORTING_DISPLAY_ALL', 2); 42 43 /** 44 * Indicates that the item should never be removed unless explicitly selected. 45 * 46 * The item may be removed using cache_clear_all() with a cache ID. 47 */ 48 define('CACHE_PERMANENT', 0); 49 50 /** 51 * Indicates that the item should be removed at the next general cache wipe. 52 */ 53 define('CACHE_TEMPORARY', -1); 54 55 /** 56 * @defgroup logging_severity_levels Logging severity levels 57 * @{ 58 * Logging severity levels as defined in RFC 3164. 59 * 60 * The WATCHDOG_* constant definitions correspond to the logging severity levels 61 * defined in RFC 3164, section 4.1.1. PHP supplies predefined LOG_* constants 62 * for use in the syslog() function, but their values on Windows builds do not 63 * correspond to RFC 3164. The associated PHP bug report was closed with the 64 * comment, "And it's also not a bug, as Windows just have less log levels," 65 * and "So the behavior you're seeing is perfectly normal." 66 * 67 * @see http://www.faqs.org/rfcs/rfc3164.html 68 * @see http://bugs.php.net/bug.php?id=18090 69 * @see http://php.net/manual/function.syslog.php 70 * @see http://php.net/manual/network.constants.php 71 * @see watchdog() 72 * @see watchdog_severity_levels() 73 */ 74 75 /** 76 * Log message severity -- Emergency: system is unusable. 77 */ 78 define('WATCHDOG_EMERGENCY', 0); 79 80 /** 81 * Log message severity -- Alert: action must be taken immediately. 82 */ 83 define('WATCHDOG_ALERT', 1); 84 85 /** 86 * Log message severity -- Critical conditions. 87 */ 88 define('WATCHDOG_CRITICAL', 2); 89 90 /** 91 * Log message severity -- Error conditions. 92 */ 93 define('WATCHDOG_ERROR', 3); 94 95 /** 96 * Log message severity -- Warning conditions. 97 */ 98 define('WATCHDOG_WARNING', 4); 99 100 /** 101 * Log message severity -- Normal but significant conditions. 102 */ 103 define('WATCHDOG_NOTICE', 5); 104 105 /** 106 * Log message severity -- Informational messages. 107 */ 108 define('WATCHDOG_INFO', 6); 109 110 /** 111 * Log message severity -- Debug-level messages. 112 */ 113 define('WATCHDOG_DEBUG', 7); 114 115 /** 116 * @} End of "defgroup logging_severity_levels". 117 */ 118 119 /** 120 * First bootstrap phase: initialize configuration. 121 */ 122 define('DRUPAL_BOOTSTRAP_CONFIGURATION', 0); 123 124 /** 125 * Second bootstrap phase: try to serve a cached page. 126 */ 127 define('DRUPAL_BOOTSTRAP_PAGE_CACHE', 1); 128 129 /** 130 * Third bootstrap phase: initialize database layer. 131 */ 132 define('DRUPAL_BOOTSTRAP_DATABASE', 2); 133 134 /** 135 * Fourth bootstrap phase: initialize the variable system. 136 */ 137 define('DRUPAL_BOOTSTRAP_VARIABLES', 3); 138 139 /** 140 * Fifth bootstrap phase: initialize session handling. 141 */ 142 define('DRUPAL_BOOTSTRAP_SESSION', 4); 143 144 /** 145 * Sixth bootstrap phase: set up the page header. 146 */ 147 define('DRUPAL_BOOTSTRAP_PAGE_HEADER', 5); 148 149 /** 150 * Seventh bootstrap phase: find out language of the page. 151 */ 152 define('DRUPAL_BOOTSTRAP_LANGUAGE', 6); 153 154 /** 155 * Final bootstrap phase: Drupal is fully loaded; validate and fix input data. 156 */ 157 define('DRUPAL_BOOTSTRAP_FULL', 7); 158 159 /** 160 * Role ID for anonymous users; should match what's in the "role" table. 161 */ 162 define('DRUPAL_ANONYMOUS_RID', 1); 163 164 /** 165 * Role ID for authenticated users; should match what's in the "role" table. 166 */ 167 define('DRUPAL_AUTHENTICATED_RID', 2); 168 169 /** 170 * The number of bytes in a kilobyte. 171 * 172 * For more information, visit http://en.wikipedia.org/wiki/Kilobyte. 173 */ 174 define('DRUPAL_KILOBYTE', 1024); 175 176 /** 177 * The language code used when no language is explicitly assigned. 178 * 179 * Defined by ISO639-2 for "Undetermined". 180 */ 181 define('LANGUAGE_NONE', 'und'); 182 183 /** 184 * The type of language used to define the content language. 185 */ 186 define('LANGUAGE_TYPE_CONTENT', 'language_content'); 187 188 /** 189 * The type of language used to select the user interface. 190 */ 191 define('LANGUAGE_TYPE_INTERFACE', 'language'); 192 193 /** 194 * The type of language used for URLs. 195 */ 196 define('LANGUAGE_TYPE_URL', 'language_url'); 197 198 /** 199 * Language written left to right. Possible value of $language->direction. 200 */ 201 define('LANGUAGE_LTR', 0); 202 203 /** 204 * Language written right to left. Possible value of $language->direction. 205 */ 206 define('LANGUAGE_RTL', 1); 207 208 /** 209 * Time of the current request in seconds elapsed since the Unix Epoch. 210 * 211 * This differs from $_SERVER['REQUEST_TIME'], which is stored as a float 212 * since PHP 5.4.0. Float timestamps confuse most PHP functions 213 * (including date_create()). 214 * 215 * @see http://php.net/manual/reserved.variables.server.php 216 * @see http://php.net/manual/function.time.php 217 */ 218 define('REQUEST_TIME', (int) $_SERVER['REQUEST_TIME']); 219 220 /** 221 * Flag for drupal_set_title(); text is not sanitized, so run check_plain(). 222 */ 223 define('CHECK_PLAIN', 0); 224 225 /** 226 * Flag for drupal_set_title(); text has already been sanitized. 227 */ 228 define('PASS_THROUGH', -1); 229 230 /** 231 * Signals that the registry lookup cache should be reset. 232 */ 233 define('REGISTRY_RESET_LOOKUP_CACHE', 1); 234 235 /** 236 * Signals that the registry lookup cache should be written to storage. 237 */ 238 define('REGISTRY_WRITE_LOOKUP_CACHE', 2); 239 240 /** 241 * Regular expression to match PHP function names. 242 * 243 * @see http://php.net/manual/en/language.functions.php 244 */ 245 define('DRUPAL_PHP_FUNCTION_PATTERN', '[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*'); 246 247 /** 248 * Provides a caching wrapper to be used in place of large array structures. 249 * 250 * This class should be extended by systems that need to cache large amounts 251 * of data and have it represented as an array to calling functions. These 252 * arrays can become very large, so ArrayAccess is used to allow different 253 * strategies to be used for caching internally (lazy loading, building caches 254 * over time etc.). This can dramatically reduce the amount of data that needs 255 * to be loaded from cache backends on each request, and memory usage from 256 * static caches of that same data. 257 * 258 * Note that array_* functions do not work with ArrayAccess. Systems using 259 * DrupalCacheArray should use this only internally. If providing API functions 260 * that return the full array, this can be cached separately or returned 261 * directly. However since DrupalCacheArray holds partial content by design, it 262 * should be a normal PHP array or otherwise contain the full structure. 263 * 264 * Note also that due to limitations in PHP prior to 5.3.4, it is impossible to 265 * write directly to the contents of nested arrays contained in this object. 266 * Only writes to the top-level array elements are possible. So if you 267 * previously had set $object['foo'] = array(1, 2, 'bar' => 'baz'), but later 268 * want to change the value of 'bar' from 'baz' to 'foobar', you cannot do so 269 * a targeted write like $object['foo']['bar'] = 'foobar'. Instead, you must 270 * overwrite the entire top-level 'foo' array with the entire set of new 271 * values: $object['foo'] = array(1, 2, 'bar' => 'foobar'). Due to this same 272 * limitation, attempts to create references to any contained data, nested or 273 * otherwise, will fail silently. So $var = &$object['foo'] will not throw an 274 * error, and $var will be populated with the contents of $object['foo'], but 275 * that data will be passed by value, not reference. For more information on 276 * the PHP limitation, see the note in the official PHP documentation at· 277 * http://php.net/manual/en/arrayaccess.offsetget.php on 278 * ArrayAccess::offsetGet(). 279 * 280 * By default, the class accounts for caches where calling functions might 281 * request keys in the array that won't exist even after a cache rebuild. This 282 * prevents situations where a cache rebuild would be triggered over and over 283 * due to a 'missing' item. These cases are stored internally as a value of 284 * NULL. This means that the offsetGet() and offsetExists() methods 285 * must be overridden if caching an array where the top level values can 286 * legitimately be NULL, and where $object->offsetExists() needs to correctly 287 * return (equivalent to array_key_exists() vs. isset()). This should not 288 * be necessary in the majority of cases. 289 * 290 * Classes extending this class must override at least the 291 * resolveCacheMiss() method to have a working implementation. 292 * 293 * offsetSet() is not overridden by this class by default. In practice this 294 * means that assigning an offset via arrayAccess will only apply while the 295 * object is in scope and will not be written back to the persistent cache. 296 * This follows a similar pattern to static vs. persistent caching in 297 * procedural code. Extending classes may wish to alter this behavior, for 298 * example by overriding offsetSet() and adding an automatic call to persist(). 299 * 300 * @see SchemaCache 301 */ 302 abstract class DrupalCacheArray implements ArrayAccess { 303 304 /** 305 * A cid to pass to cache_set() and cache_get(). 306 */ 307 protected $cid; 308 309 /** 310 * A bin to pass to cache_set() and cache_get(). 311 */ 312 protected $bin; 313 314 /** 315 * An array of keys to add to the cache at the end of the request. 316 */ 317 protected $keysToPersist = array(); 318 319 /** 320 * Storage for the data itself. 321 */ 322 protected $storage = array(); 323 324 /** 325 * Constructs a DrupalCacheArray object. 326 * 327 * @param $cid 328 * The cid for the array being cached. 329 * @param $bin 330 * The bin to cache the array. 331 */ 332 public function __construct($cid, $bin) { 333 $this->cid = $cid; 334 $this->bin = $bin; 335 336 if ($cached = cache_get($this->cid, $this->bin)) { 337 $this->storage = $cached->data; 338 } 339 } 340 341 /** 342 * Implements ArrayAccess::offsetExists(). 343 */ 344 public function offsetExists($offset) { 345 return $this->offsetGet($offset) !== NULL; 346 } 347 348 /** 349 * Implements ArrayAccess::offsetGet(). 350 */ 351 public function offsetGet($offset) { 352 if (isset($this->storage[$offset]) || array_key_exists($offset, $this->storage)) { 353 return $this->storage[$offset]; 354 } 355 else { 356 return $this->resolveCacheMiss($offset); 357 } 358 } 359 360 /** 361 * Implements ArrayAccess::offsetSet(). 362 */ 363 public function offsetSet($offset, $value) { 364 $this->storage[$offset] = $value; 365 } 366 367 /** 368 * Implements ArrayAccess::offsetUnset(). 369 */ 370 public function offsetUnset($offset) { 371 unset($this->storage[$offset]); 372 } 373 374 /** 375 * Flags an offset value to be written to the persistent cache. 376 * 377 * If a value is assigned to a cache object with offsetSet(), by default it 378 * will not be written to the persistent cache unless it is flagged with this 379 * method. This allows items to be cached for the duration of a request, 380 * without necessarily writing back to the persistent cache at the end. 381 * 382 * @param $offset 383 * The array offset that was request. 384 * @param $persist 385 * Optional boolean to specify whether the offset should be persisted or 386 * not, defaults to TRUE. When called with $persist = FALSE the offset will 387 * be unflagged so that it will not written at the end of the request. 388 */ 389 protected function persist($offset, $persist = TRUE) { 390 $this->keysToPersist[$offset] = $persist; 391 } 392 393 /** 394 * Resolves a cache miss. 395 * 396 * When an offset is not found in the object, this is treated as a cache 397 * miss. This method allows classes implementing the interface to look up 398 * the actual value and allow it to be cached. 399 * 400 * @param $offset 401 * The offset that was requested. 402 * 403 * @return 404 * The value of the offset, or NULL if no value was found. 405 */ 406 abstract protected function resolveCacheMiss($offset); 407 408 /** 409 * Writes a value to the persistent cache immediately. 410 * 411 * @param $data 412 * The data to write to the persistent cache. 413 * @param $lock 414 * Whether to acquire a lock before writing to cache. 415 */ 416 protected function set($data, $lock = TRUE) { 417 // Lock cache writes to help avoid stampedes. 418 // To implement locking for cache misses, override __construct(). 419 $lock_name = $this->cid . ':' . $this->bin; 420 if (!$lock || lock_acquire($lock_name)) { 421 if ($cached = cache_get($this->cid, $this->bin)) { 422 $data = $cached->data + $data; 423 } 424 cache_set($this->cid, $data, $this->bin); 425 if ($lock) { 426 lock_release($lock_name); 427 } 428 } 429 } 430 431 /** 432 * Destructs the DrupalCacheArray object. 433 */ 434 public function __destruct() { 435 $data = array(); 436 foreach ($this->keysToPersist as $offset => $persist) { 437 if ($persist) { 438 $data[$offset] = $this->storage[$offset]; 439 } 440 } 441 if (!empty($data)) { 442 $this->set($data); 443 } 444 } 445 } 446 447 /** 448 * Starts the timer with the specified name. 449 * 450 * If you start and stop the same timer multiple times, the measured intervals 451 * will be accumulated. 452 * 453 * @param $name 454 * The name of the timer. 455 */ 456 function timer_start($name) { 457 global $timers; 458 459 $timers[$name]['start'] = microtime(TRUE); 460 $timers[$name]['count'] = isset($timers[$name]['count']) ? ++$timers[$name]['count'] : 1; 461 } 462 463 /** 464 * Reads the current timer value without stopping the timer. 465 * 466 * @param $name 467 * The name of the timer. 468 * 469 * @return 470 * The current timer value in ms. 471 */ 472 function timer_read($name) { 473 global $timers; 474 475 if (isset($timers[$name]['start'])) { 476 $stop = microtime(TRUE); 477 $diff = round(($stop - $timers[$name]['start']) * 1000, 2); 478 479 if (isset($timers[$name]['time'])) { 480 $diff += $timers[$name]['time']; 481 } 482 return $diff; 483 } 484 return $timers[$name]['time']; 485 } 486 487 /** 488 * Stops the timer with the specified name. 489 * 490 * @param $name 491 * The name of the timer. 492 * 493 * @return 494 * A timer array. The array contains the number of times the timer has been 495 * started and stopped (count) and the accumulated timer value in ms (time). 496 */ 497 function timer_stop($name) { 498 global $timers; 499 500 if (isset($timers[$name]['start'])) { 501 $stop = microtime(TRUE); 502 $diff = round(($stop - $timers[$name]['start']) * 1000, 2); 503 if (isset($timers[$name]['time'])) { 504 $timers[$name]['time'] += $diff; 505 } 506 else { 507 $timers[$name]['time'] = $diff; 508 } 509 unset($timers[$name]['start']); 510 } 511 512 return $timers[$name]; 513 } 514 515 /** 516 * Returns the appropriate configuration directory. 517 * 518 * Returns the configuration path based on the site's hostname, port, and 519 * pathname. Uses find_conf_path() to find the current configuration directory. 520 * See default.settings.php for examples on how the URL is converted to a 521 * directory. 522 * 523 * @param bool $require_settings 524 * Only configuration directories with an existing settings.php file 525 * will be recognized. Defaults to TRUE. During initial installation, 526 * this is set to FALSE so that Drupal can detect a matching directory, 527 * then create a new settings.php file in it. 528 * @param bool $reset 529 * Force a full search for matching directories even if one had been 530 * found previously. Defaults to FALSE. 531 * 532 * @return 533 * The path of the matching directory. 534 * 535 * @see default.settings.php 536 */ 537 function conf_path($require_settings = TRUE, $reset = FALSE) { 538 $conf = &drupal_static(__FUNCTION__, ''); 539 540 if ($conf && !$reset) { 541 return $conf; 542 } 543 544 $confdir = 'sites'; 545 546 $sites = array(); 547 if (file_exists(DRUPAL_ROOT . '/' . $confdir . '/sites.php')) { 548 // This will overwrite $sites with the desired mappings. 549 include(DRUPAL_ROOT . '/' . $confdir . '/sites.php'); 550 } 551 552 $uri = explode('/', $_SERVER['SCRIPT_NAME'] ? $_SERVER['SCRIPT_NAME'] : $_SERVER['SCRIPT_FILENAME']); 553 $server = explode('.', implode('.', array_reverse(explode(':', rtrim($_SERVER['HTTP_HOST'], '.'))))); 554 for ($i = count($uri) - 1; $i > 0; $i--) { 555 for ($j = count($server); $j > 0; $j--) { 556 $dir = implode('.', array_slice($server, -$j)) . implode('.', array_slice($uri, 0, $i)); 557 if (isset($sites[$dir]) && file_exists(DRUPAL_ROOT . '/' . $confdir . '/' . $sites[$dir])) { 558 $dir = $sites[$dir]; 559 } 560 if (file_exists(DRUPAL_ROOT . '/' . $confdir . '/' . $dir . '/settings.php') || (!$require_settings && file_exists(DRUPAL_ROOT . '/' . $confdir . '/' . $dir))) { 561 $conf = "$confdir/$dir"; 562 return $conf; 563 } 564 } 565 } 566 $conf = "$confdir/default"; 567 return $conf; 568 } 569 570 /** 571 * Sets appropriate server variables needed for command line scripts to work. 572 * 573 * This function can be called by command line scripts before bootstrapping 574 * Drupal, to ensure that the page loads with the desired server parameters. 575 * This is because many parts of Drupal assume that they are running in a web 576 * browser and therefore use information from the global PHP $_SERVER variable 577 * that does not get set when Drupal is run from the command line. 578 * 579 * In many cases, the default way in which this function populates the $_SERVER 580 * variable is sufficient, and it can therefore be called without passing in 581 * any input. However, command line scripts running on a multisite installation 582 * (or on any installation that has settings.php stored somewhere other than 583 * the sites/default folder) need to pass in the URL of the site to allow 584 * Drupal to detect the correct location of the settings.php file. Passing in 585 * the 'url' parameter is also required for functions like request_uri() to 586 * return the expected values. 587 * 588 * Most other parameters do not need to be passed in, but may be necessary in 589 * some cases; for example, if Drupal's ip_address() function needs to return 590 * anything but the standard localhost value ('127.0.0.1'), the command line 591 * script should pass in the desired value via the 'REMOTE_ADDR' key. 592 * 593 * @param $variables 594 * (optional) An associative array of variables within $_SERVER that should 595 * be replaced. If the special element 'url' is provided in this array, it 596 * will be used to populate some of the server defaults; it should be set to 597 * the URL of the current page request, excluding any $_GET request but 598 * including the script name (e.g., http://www.example.com/mysite/index.php). 599 * 600 * @see conf_path() 601 * @see request_uri() 602 * @see ip_address() 603 */ 604 function drupal_override_server_variables($variables = array()) { 605 // Allow the provided URL to override any existing values in $_SERVER. 606 if (isset($variables['url'])) { 607 $url = parse_url($variables['url']); 608 if (isset($url['host'])) { 609 $_SERVER['HTTP_HOST'] = $url['host']; 610 } 611 if (isset($url['path'])) { 612 $_SERVER['SCRIPT_NAME'] = $url['path']; 613 } 614 unset($variables['url']); 615 } 616 // Define default values for $_SERVER keys. These will be used if $_SERVER 617 // does not already define them and no other values are passed in to this 618 // function. 619 $defaults = array( 620 'HTTP_HOST' => 'localhost', 621 'SCRIPT_NAME' => NULL, 622 'REMOTE_ADDR' => '127.0.0.1', 623 'REQUEST_METHOD' => 'GET', 624 'SERVER_NAME' => NULL, 625 'SERVER_SOFTWARE' => NULL, 626 'HTTP_USER_AGENT' => NULL, 627 ); 628 // Replace elements of the $_SERVER array, as appropriate. 629 $_SERVER = $variables + $_SERVER + $defaults; 630 } 631 632 /** 633 * Initializes the PHP environment. 634 */ 635 function drupal_environment_initialize() { 636 if (!isset($_SERVER['HTTP_REFERER'])) { 637 $_SERVER['HTTP_REFERER'] = ''; 638 } 639 if (!isset($_SERVER['SERVER_PROTOCOL']) || ($_SERVER['SERVER_PROTOCOL'] != 'HTTP/1.0' && $_SERVER['SERVER_PROTOCOL'] != 'HTTP/1.1')) { 640 $_SERVER['SERVER_PROTOCOL'] = 'HTTP/1.0'; 641 } 642 643 if (isset($_SERVER['HTTP_HOST'])) { 644 // As HTTP_HOST is user input, ensure it only contains characters allowed 645 // in hostnames. See RFC 952 (and RFC 2181). 646 // $_SERVER['HTTP_HOST'] is lowercased here per specifications. 647 $_SERVER['HTTP_HOST'] = strtolower($_SERVER['HTTP_HOST']); 648 if (!drupal_valid_http_host($_SERVER['HTTP_HOST'])) { 649 // HTTP_HOST is invalid, e.g. if containing slashes it may be an attack. 650 header($_SERVER['SERVER_PROTOCOL'] . ' 400 Bad Request'); 651 exit; 652 } 653 } 654 else { 655 // Some pre-HTTP/1.1 clients will not send a Host header. Ensure the key is 656 // defined for E_ALL compliance. 657 $_SERVER['HTTP_HOST'] = ''; 658 } 659 660 // When clean URLs are enabled, emulate ?q=foo/bar using REQUEST_URI. It is 661 // not possible to append the query string using mod_rewrite without the B 662 // flag (this was added in Apache 2.2.8), because mod_rewrite unescapes the 663 // path before passing it on to PHP. This is a problem when the path contains 664 // e.g. "&" or "%" that have special meanings in URLs and must be encoded. 665 $_GET['q'] = request_path(); 666 667 // Enforce E_ALL, but allow users to set levels not part of E_ALL. 668 error_reporting(E_ALL | error_reporting()); 669 670 // Override PHP settings required for Drupal to work properly. 671 // sites/default/default.settings.php contains more runtime settings. 672 // The .htaccess file contains settings that cannot be changed at runtime. 673 674 // Don't escape quotes when reading files from the database, disk, etc. 675 ini_set('magic_quotes_runtime', '0'); 676 // Use session cookies, not transparent sessions that puts the session id in 677 // the query string. 678 ini_set('session.use_cookies', '1'); 679 ini_set('session.use_only_cookies', '1'); 680 ini_set('session.use_trans_sid', '0'); 681 // Don't send HTTP headers using PHP's session handler. 682 ini_set('session.cache_limiter', 'none'); 683 // Use httponly session cookies. 684 ini_set('session.cookie_httponly', '1'); 685 686 // Set sane locale settings, to ensure consistent string, dates, times and 687 // numbers handling. 688 setlocale(LC_ALL, 'C'); 689 } 690 691 /** 692 * Validates that a hostname (for example $_SERVER['HTTP_HOST']) is safe. 693 * 694 * @return 695 * TRUE if only containing valid characters, or FALSE otherwise. 696 */ 697 function drupal_valid_http_host($host) { 698 return preg_match('/^\[?(?:[a-zA-Z0-9-:\]_]+\.?)+$/', $host); 699 } 700 701 /** 702 * Sets the base URL, cookie domain, and session name from configuration. 703 */ 704 function drupal_settings_initialize() { 705 global $base_url, $base_path, $base_root; 706 707 // Export these settings.php variables to the global namespace. 708 global $databases, $cookie_domain, $conf, $installed_profile, $update_free_access, $db_url, $db_prefix, $drupal_hash_salt, $is_https, $base_secure_url, $base_insecure_url; 709 $conf = array(); 710 711 if (file_exists(DRUPAL_ROOT . '/' . conf_path() . '/settings.php')) { 712 include_once DRUPAL_ROOT . '/' . conf_path() . '/settings.php'; 713 } 714 $is_https = isset($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) == 'on'; 715 716 if (isset($base_url)) { 717 // Parse fixed base URL from settings.php. 718 $parts = parse_url($base_url); 719 $http_protocol = $parts['scheme']; 720 if (!isset($parts['path'])) { 721 $parts['path'] = ''; 722 } 723 $base_path = $parts['path'] . '/'; 724 // Build $base_root (everything until first slash after "scheme://"). 725 $base_root = substr($base_url, 0, strlen($base_url) - strlen($parts['path'])); 726 } 727 else { 728 // Create base URL. 729 $http_protocol = $is_https ? 'https' : 'http'; 730 $base_root = $http_protocol . '://' . $_SERVER['HTTP_HOST']; 731 732 $base_url = $base_root; 733 734 // $_SERVER['SCRIPT_NAME'] can, in contrast to $_SERVER['PHP_SELF'], not 735 // be modified by a visitor. 736 if ($dir = rtrim(dirname($_SERVER['SCRIPT_NAME']), '\/')) { 737 $base_path = $dir; 738 $base_url .= $base_path; 739 $base_path .= '/'; 740 } 741 else { 742 $base_path = '/'; 743 } 744 } 745 $base_secure_url = str_replace('http://', 'https://', $base_url); 746 $base_insecure_url = str_replace('https://', 'http://', $base_url); 747 748 if ($cookie_domain) { 749 // If the user specifies the cookie domain, also use it for session name. 750 $session_name = $cookie_domain; 751 } 752 else { 753 // Otherwise use $base_url as session name, without the protocol 754 // to use the same session identifiers across HTTP and HTTPS. 755 list( , $session_name) = explode('://', $base_url, 2); 756 // HTTP_HOST can be modified by a visitor, but we already sanitized it 757 // in drupal_settings_initialize(). 758 if (!empty($_SERVER['HTTP_HOST'])) { 759 $cookie_domain = $_SERVER['HTTP_HOST']; 760 // Strip leading periods, www., and port numbers from cookie domain. 761 $cookie_domain = ltrim($cookie_domain, '.'); 762 if (strpos($cookie_domain, 'www.') === 0) { 763 $cookie_domain = substr($cookie_domain, 4); 764 } 765 $cookie_domain = explode(':', $cookie_domain); 766 $cookie_domain = '.' . $cookie_domain[0]; 767 } 768 } 769 // Per RFC 2109, cookie domains must contain at least one dot other than the 770 // first. For hosts such as 'localhost' or IP Addresses we don't set a cookie domain. 771 if (count(explode('.', $cookie_domain)) > 2 && !is_numeric(str_replace('.', '', $cookie_domain))) { 772 ini_set('session.cookie_domain', $cookie_domain); 773 } 774 // To prevent session cookies from being hijacked, a user can configure the 775 // SSL version of their website to only transfer session cookies via SSL by 776 // using PHP's session.cookie_secure setting. The browser will then use two 777 // separate session cookies for the HTTPS and HTTP versions of the site. So we 778 // must use different session identifiers for HTTPS and HTTP to prevent a 779 // cookie collision. 780 if ($is_https) { 781 ini_set('session.cookie_secure', TRUE); 782 } 783 $prefix = ini_get('session.cookie_secure') ? 'SSESS' : 'SESS'; 784 session_name($prefix . substr(hash('sha256', $session_name), 0, 32)); 785 } 786 787 /** 788 * Returns and optionally sets the filename for a system resource. 789 * 790 * The filename, whether provided, cached, or retrieved from the database, is 791 * only returned if the file exists. 792 * 793 * This function plays a key role in allowing Drupal's resources (modules 794 * and themes) to be located in different places depending on a site's 795 * configuration. For example, a module 'foo' may legally be be located 796 * in any of these three places: 797 * 798 * modules/foo/foo.module 799 * sites/all/modules/foo/foo.module 800 * sites/example.com/modules/foo/foo.module 801 * 802 * Calling drupal_get_filename('module', 'foo') will give you one of 803 * the above, depending on where the module is located. 804 * 805 * @param $type 806 * The type of the item (i.e. theme, theme_engine, module, profile). 807 * @param $name 808 * The name of the item for which the filename is requested. 809 * @param $filename 810 * The filename of the item if it is to be set explicitly rather 811 * than by consulting the database. 812 * 813 * @return 814 * The filename of the requested item. 815 */ 816 function drupal_get_filename($type, $name, $filename = NULL) { 817 // The location of files will not change during the request, so do not use 818 // drupal_static(). 819 static $files = array(), $dirs = array(); 820 821 // Profiles are a special case: they have a fixed location and naming. 822 if ($type == 'profile') { 823 $profile_filename = "profiles/$name/$name.profile"; 824 $files[$type][$name] = file_exists($profile_filename) ? $profile_filename : FALSE; 825 } 826 if (!isset($files[$type])) { 827 $files[$type] = array(); 828 } 829 830 if (!empty($filename) && file_exists($filename)) { 831 $files[$type][$name] = $filename; 832 } 833 elseif (isset($files[$type][$name])) { 834 // nothing 835 } 836 // Verify that we have an active database connection, before querying 837 // the database. This is required because this function is called both 838 // before we have a database connection (i.e. during installation) and 839 // when a database connection fails. 840 else { 841 try { 842 if (function_exists('db_query')) { 843 $file = db_query("SELECT filename FROM {system} WHERE name = :name AND type = :type", array(':name' => $name, ':type' => $type))->fetchField(); 844 if (file_exists(DRUPAL_ROOT . '/' . $file)) { 845 $files[$type][$name] = $file; 846 } 847 } 848 } 849 catch (Exception $e) { 850 // The database table may not exist because Drupal is not yet installed, 851 // or the database might be down. We have a fallback for this case so we 852 // hide the error completely. 853 } 854 // Fallback to searching the filesystem if the database could not find the 855 // file or the file returned by the database is not found. 856 if (!isset($files[$type][$name])) { 857 // We have a consistent directory naming: modules, themes... 858 $dir = $type . 's'; 859 if ($type == 'theme_engine') { 860 $dir = 'themes/engines'; 861 $extension = 'engine'; 862 } 863 elseif ($type == 'theme') { 864 $extension = 'info'; 865 } 866 else { 867 $extension = $type; 868 } 869 870 if (!isset($dirs[$dir][$extension])) { 871 $dirs[$dir][$extension] = TRUE; 872 if (!function_exists('drupal_system_listing')) { 873 require_once DRUPAL_ROOT . '/includes/common.inc'; 874 } 875 // Scan the appropriate directories for all files with the requested 876 // extension, not just the file we are currently looking for. This 877 // prevents unnecessary scans from being repeated when this function is 878 // called more than once in the same page request. 879 $matches = drupal_system_listing("/^" . DRUPAL_PHP_FUNCTION_PATTERN . "\.$extension$/", $dir, 'name', 0); 880 foreach ($matches as $matched_name => $file) { 881 $files[$type][$matched_name] = $file->uri; 882 } 883 } 884 } 885 } 886 887 if (isset($files[$type][$name])) { 888 return $files[$type][$name]; 889 } 890 } 891 892 /** 893 * Loads the persistent variable table. 894 * 895 * The variable table is composed of values that have been saved in the table 896 * with variable_set() as well as those explicitly specified in the 897 * configuration file. 898 */ 899 function variable_initialize($conf = array()) { 900 // NOTE: caching the variables improves performance by 20% when serving 901 // cached pages. 902 if ($cached = cache_get('variables', 'cache_bootstrap')) { 903 $variables = $cached->data; 904 } 905 else { 906 // Cache miss. Avoid a stampede. 907 $name = 'variable_init'; 908 if (!lock_acquire($name, 1)) { 909 // Another request is building the variable cache. 910 // Wait, then re-run this function. 911 lock_wait($name); 912 return variable_initialize($conf); 913 } 914 else { 915 // Proceed with variable rebuild. 916 $variables = array_map('unserialize', db_query('SELECT name, value FROM {variable}')->fetchAllKeyed()); 917 cache_set('variables', $variables, 'cache_bootstrap'); 918 lock_release($name); 919 } 920 } 921 922 foreach ($conf as $name => $value) { 923 $variables[$name] = $value; 924 } 925 926 return $variables; 927 } 928 929 /** 930 * Returns a persistent variable. 931 * 932 * Case-sensitivity of the variable_* functions depends on the database 933 * collation used. To avoid problems, always use lower case for persistent 934 * variable names. 935 * 936 * @param $name 937 * The name of the variable to return. 938 * @param $default 939 * The default value to use if this variable has never been set. 940 * 941 * @return 942 * The value of the variable. Unserialization is taken care of as necessary. 943 * 944 * @see variable_del() 945 * @see variable_set() 946 */ 947 function variable_get($name, $default = NULL) { 948 global $conf; 949 950 return isset($conf[$name]) ? $conf[$name] : $default; 951 } 952 953 /** 954 * Sets a persistent variable. 955 * 956 * Case-sensitivity of the variable_* functions depends on the database 957 * collation used. To avoid problems, always use lower case for persistent 958 * variable names. 959 * 960 * @param $name 961 * The name of the variable to set. 962 * @param $value 963 * The value to set. This can be any PHP data type; these functions take care 964 * of serialization as necessary. 965 * 966 * @see variable_del() 967 * @see variable_get() 968 */ 969 function variable_set($name, $value) { 970 global $conf; 971 972 db_merge('variable')->key(array('name' => $name))->fields(array('value' => serialize($value)))->execute(); 973 974 cache_clear_all('variables', 'cache_bootstrap'); 975 976 $conf[$name] = $value; 977 } 978 979 /** 980 * Unsets a persistent variable. 981 * 982 * Case-sensitivity of the variable_* functions depends on the database 983 * collation used. To avoid problems, always use lower case for persistent 984 * variable names. 985 * 986 * @param $name 987 * The name of the variable to undefine. 988 * 989 * @see variable_get() 990 * @see variable_set() 991 */ 992 function variable_del($name) { 993 global $conf; 994 995 db_delete('variable') 996 ->condition('name', $name) 997 ->execute(); 998 cache_clear_all('variables', 'cache_bootstrap'); 999 1000 unset($conf[$name]); 1001 } 1002 1003 /** 1004 * Retrieves the current page from the cache. 1005 * 1006 * Note: we do not serve cached pages to authenticated users, or to anonymous 1007 * users when $_SESSION is non-empty. $_SESSION may contain status messages 1008 * from a form submission, the contents of a shopping cart, or other user- 1009 * specific content that should not be cached and displayed to other users. 1010 * 1011 * @param $check_only 1012 * (optional) Set to TRUE to only return whether a previous call found a 1013 * cache entry. 1014 * 1015 * @return 1016 * The cache object, if the page was found in the cache, NULL otherwise. 1017 */ 1018 function drupal_page_get_cache($check_only = FALSE) { 1019 global $base_root; 1020 static $cache_hit = FALSE; 1021 1022 if ($check_only) { 1023 return $cache_hit; 1024 } 1025 1026 if (drupal_page_is_cacheable()) { 1027 $cache = cache_get($base_root . request_uri(), 'cache_page'); 1028 if ($cache !== FALSE) { 1029 $cache_hit = TRUE; 1030 } 1031 return $cache; 1032 } 1033 } 1034 1035 /** 1036 * Determines the cacheability of the current page. 1037 * 1038 * @param $allow_caching 1039 * Set to FALSE if you want to prevent this page to get cached. 1040 * 1041 * @return 1042 * TRUE if the current page can be cached, FALSE otherwise. 1043 */ 1044 function drupal_page_is_cacheable($allow_caching = NULL) { 1045 $allow_caching_static = &drupal_static(__FUNCTION__, TRUE); 1046 if (isset($allow_caching)) { 1047 $allow_caching_static = $allow_caching; 1048 } 1049 1050 return $allow_caching_static && ($_SERVER['REQUEST_METHOD'] == 'GET' || $_SERVER['REQUEST_METHOD'] == 'HEAD') 1051 && !drupal_is_cli(); 1052 } 1053 1054 /** 1055 * Invokes a bootstrap hook in all bootstrap modules that implement it. 1056 * 1057 * @param $hook 1058 * The name of the bootstrap hook to invoke. 1059 * 1060 * @see bootstrap_hooks() 1061 */ 1062 function bootstrap_invoke_all($hook) { 1063 // Bootstrap modules should have been loaded when this function is called, so 1064 // we don't need to tell module_list() to reset its internal list (and we 1065 // therefore leave the first parameter at its default value of FALSE). We 1066 // still pass in TRUE for the second parameter, though; in case this is the 1067 // first time during the bootstrap that module_list() is called, we want to 1068 // make sure that its internal cache is primed with the bootstrap modules 1069 // only. 1070 foreach (module_list(FALSE, TRUE) as $module) { 1071 drupal_load('module', $module); 1072 module_invoke($module, $hook); 1073 } 1074 } 1075 1076 /** 1077 * Includes a file with the provided type and name. 1078 * 1079 * This prevents including a theme, engine, module, etc., more than once. 1080 * 1081 * @param $type 1082 * The type of item to load (i.e. theme, theme_engine, module). 1083 * @param $name 1084 * The name of the item to load. 1085 * 1086 * @return 1087 * TRUE if the item is loaded or has already been loaded. 1088 */ 1089 function drupal_load($type, $name) { 1090 // Once a file is included this can't be reversed during a request so do not 1091 // use drupal_static() here. 1092 static $files = array(); 1093 1094 if (isset($files[$type][$name])) { 1095 return TRUE; 1096 } 1097 1098 $filename = drupal_get_filename($type, $name); 1099 1100 if ($filename) { 1101 include_once DRUPAL_ROOT . '/' . $filename; 1102 $files[$type][$name] = TRUE; 1103 1104 return TRUE; 1105 } 1106 1107 return FALSE; 1108 } 1109 1110 /** 1111 * Sets an HTTP response header for the current page. 1112 * 1113 * Note: When sending a Content-Type header, always include a 'charset' type, 1114 * too. This is necessary to avoid security bugs (e.g. UTF-7 XSS). 1115 * 1116 * @param $name 1117 * The HTTP header name, or the special 'Status' header name. 1118 * @param $value 1119 * The HTTP header value; if equal to FALSE, the specified header is unset. 1120 * If $name is 'Status', this is expected to be a status code followed by a 1121 * reason phrase, e.g. "404 Not Found". 1122 * @param $append 1123 * Whether to append the value to an existing header or to replace it. 1124 */ 1125 function drupal_add_http_header($name, $value, $append = FALSE) { 1126 // The headers as name/value pairs. 1127 $headers = &drupal_static('drupal_http_headers', array()); 1128 1129 $name_lower = strtolower($name); 1130 _drupal_set_preferred_header_name($name); 1131 1132 if ($value === FALSE) { 1133 $headers[$name_lower] = FALSE; 1134 } 1135 elseif (isset($headers[$name_lower]) && $append) { 1136 // Multiple headers with identical names may be combined using comma (RFC 1137 // 2616, section 4.2). 1138 $headers[$name_lower] .= ',' . $value; 1139 } 1140 else { 1141 $headers[$name_lower] = $value; 1142 } 1143 drupal_send_headers(array($name => $headers[$name_lower]), TRUE); 1144 } 1145 1146 /** 1147 * Gets the HTTP response headers for the current page. 1148 * 1149 * @param $name 1150 * An HTTP header name. If omitted, all headers are returned as name/value 1151 * pairs. If an array value is FALSE, the header has been unset. 1152 * 1153 * @return 1154 * A string containing the header value, or FALSE if the header has been set, 1155 * or NULL if the header has not been set. 1156 */ 1157 function drupal_get_http_header($name = NULL) { 1158 $headers = &drupal_static('drupal_http_headers', array()); 1159 if (isset($name)) { 1160 $name = strtolower($name); 1161 return isset($headers[$name]) ? $headers[$name] : NULL; 1162 } 1163 else { 1164 return $headers; 1165 } 1166 } 1167 1168 /** 1169 * Sets the preferred name for the HTTP header. 1170 * 1171 * Header names are case-insensitive, but for maximum compatibility they should 1172 * follow "common form" (see RFC 2617, section 4.2). 1173 */ 1174 function _drupal_set_preferred_header_name($name = NULL) { 1175 static $header_names = array(); 1176 1177 if (!isset($name)) { 1178 return $header_names; 1179 } 1180 $header_names[strtolower($name)] = $name; 1181 } 1182 1183 /** 1184 * Sends the HTTP response headers that were previously set, adding defaults. 1185 * 1186 * Headers are set in drupal_add_http_header(). Default headers are not set 1187 * if they have been replaced or unset using drupal_add_http_header(). 1188 * 1189 * @param $default_headers 1190 * An array of headers as name/value pairs. 1191 * @param $single 1192 * If TRUE and headers have already be sent, send only the specified header. 1193 */ 1194 function drupal_send_headers($default_headers = array(), $only_default = FALSE) { 1195 $headers_sent = &drupal_static(__FUNCTION__, FALSE); 1196 $headers = drupal_get_http_header(); 1197 if ($only_default && $headers_sent) { 1198 $headers = array(); 1199 } 1200 $headers_sent = TRUE; 1201 1202 $header_names = _drupal_set_preferred_header_name(); 1203 foreach ($default_headers as $name => $value) { 1204 $name_lower = strtolower($name); 1205 if (!isset($headers[$name_lower])) { 1206 $headers[$name_lower] = $value; 1207 $header_names[$name_lower] = $name; 1208 } 1209 } 1210 foreach ($headers as $name_lower => $value) { 1211 if ($name_lower == 'status') { 1212 header($_SERVER['SERVER_PROTOCOL'] . ' ' . $value); 1213 } 1214 // Skip headers that have been unset. 1215 elseif ($value) { 1216 header($header_names[$name_lower] . ': ' . $value); 1217 } 1218 } 1219 } 1220 1221 /** 1222 * Sets HTTP headers in preparation for a page response. 1223 * 1224 * Authenticated users are always given a 'no-cache' header, and will fetch a 1225 * fresh page on every request. This prevents authenticated users from seeing 1226 * locally cached pages. 1227 * 1228 * Also give each page a unique ETag. This will force clients to include both 1229 * an If-Modified-Since header and an If-None-Match header when doing 1230 * conditional requests for the page (required by RFC 2616, section 13.3.4), 1231 * making the validation more robust. This is a workaround for a bug in Mozilla 1232 * Firefox that is triggered when Drupal's caching is enabled and the user 1233 * accesses Drupal via an HTTP proxy (see 1234 * https://bugzilla.mozilla.org/show_bug.cgi?id=269303): When an authenticated 1235 * user requests a page, and then logs out and requests the same page again, 1236 * Firefox may send a conditional request based on the page that was cached 1237 * locally when the user was logged in. If this page did not have an ETag 1238 * header, the request only contains an If-Modified-Since header. The date will 1239 * be recent, because with authenticated users the Last-Modified header always 1240 * refers to the time of the request. If the user accesses Drupal via a proxy 1241 * server, and the proxy already has a cached copy of the anonymous page with an 1242 * older Last-Modified date, the proxy may respond with 304 Not Modified, making 1243 * the client think that the anonymous and authenticated pageviews are 1244 * identical. 1245 * 1246 * @see drupal_page_set_cache() 1247 */ 1248 function drupal_page_header() { 1249 $headers_sent = &drupal_static(__FUNCTION__, FALSE); 1250 if ($headers_sent) { 1251 return TRUE; 1252 } 1253 $headers_sent = TRUE; 1254 1255 $default_headers = array( 1256 'Expires' => 'Sun, 19 Nov 1978 05:00:00 GMT', 1257 'Last-Modified' => gmdate(DATE_RFC1123, REQUEST_TIME), 1258 'Cache-Control' => 'no-cache, must-revalidate, post-check=0, pre-check=0', 1259 'ETag' => '"' . REQUEST_TIME . '"', 1260 ); 1261 drupal_send_headers($default_headers); 1262 } 1263 1264 /** 1265 * Sets HTTP headers in preparation for a cached page response. 1266 * 1267 * The headers allow as much as possible in proxies and browsers without any 1268 * particular knowledge about the pages. Modules can override these headers 1269 * using drupal_add_http_header(). 1270 * 1271 * If the request is conditional (using If-Modified-Since and If-None-Match), 1272 * and the conditions match those currently in the cache, a 304 Not Modified 1273 * response is sent. 1274 */ 1275 function drupal_serve_page_from_cache(stdClass $cache) { 1276 // Negotiate whether to use compression. 1277 $page_compression = variable_get('page_compression', TRUE) && extension_loaded('zlib'); 1278 $return_compressed = $page_compression && isset($_SERVER['HTTP_ACCEPT_ENCODING']) && strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip') !== FALSE; 1279 1280 // Get headers set in hook_boot(). Keys are lower-case. 1281 $hook_boot_headers = drupal_get_http_header(); 1282 1283 // Headers generated in this function, that may be replaced or unset using 1284 // drupal_add_http_headers(). Keys are mixed-case. 1285 $default_headers = array(); 1286 1287 foreach ($cache->data['headers'] as $name => $value) { 1288 // In the case of a 304 response, certain headers must be sent, and the 1289 // remaining may not (see RFC 2616, section 10.3.5). Do not override 1290 // headers set in hook_boot(). 1291 $name_lower = strtolower($name); 1292 if (in_array($name_lower, array('content-location', 'expires', 'cache-control', 'vary')) && !isset($hook_boot_headers[$name_lower])) { 1293 drupal_add_http_header($name, $value); 1294 unset($cache->data['headers'][$name]); 1295 } 1296 } 1297 1298 // If the client sent a session cookie, a cached copy will only be served 1299 // to that one particular client due to Vary: Cookie. Thus, do not set 1300 // max-age > 0, allowing the page to be cached by external proxies, when a 1301 // session cookie is present unless the Vary header has been replaced or 1302 // unset in hook_boot(). 1303 $max_age = !isset($_COOKIE[session_name()]) || isset($hook_boot_headers['vary']) ? variable_get('page_cache_maximum_age', 0) : 0; 1304 $default_headers['Cache-Control'] = 'public, max-age=' . $max_age; 1305 1306 // Entity tag should change if the output changes. 1307 $etag = '"' . $cache->created . '-' . intval($return_compressed) . '"'; 1308 header('Etag: ' . $etag); 1309 1310 // See if the client has provided the required HTTP headers. 1311 $if_modified_since = isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) ? strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']) : FALSE; 1312 $if_none_match = isset($_SERVER['HTTP_IF_NONE_MATCH']) ? stripslashes($_SERVER['HTTP_IF_NONE_MATCH']) : FALSE; 1313 1314 if ($if_modified_since && $if_none_match 1315 && $if_none_match == $etag // etag must match 1316 && $if_modified_since == $cache->created) { // if-modified-since must match 1317 header($_SERVER['SERVER_PROTOCOL'] . ' 304 Not Modified'); 1318 drupal_send_headers($default_headers); 1319 return; 1320 } 1321 1322 // Send the remaining headers. 1323 foreach ($cache->data['headers'] as $name => $value) { 1324 drupal_add_http_header($name, $value); 1325 } 1326 1327 $default_headers['Last-Modified'] = gmdate(DATE_RFC1123, $cache->created); 1328 1329 // HTTP/1.0 proxies does not support the Vary header, so prevent any caching 1330 // by sending an Expires date in the past. HTTP/1.1 clients ignores the 1331 // Expires header if a Cache-Control: max-age= directive is specified (see RFC 1332 // 2616, section 14.9.3). 1333 $default_headers['Expires'] = 'Sun, 19 Nov 1978 05:00:00 GMT'; 1334 1335 drupal_send_headers($default_headers); 1336 1337 // Allow HTTP proxies to cache pages for anonymous users without a session 1338 // cookie. The Vary header is used to indicates the set of request-header 1339 // fields that fully determines whether a cache is permitted to use the 1340 // response to reply to a subsequent request for a given URL without 1341 // revalidation. If a Vary header has been set in hook_boot(), it is assumed 1342 // that the module knows how to cache the page. 1343 if (!isset($hook_boot_headers['vary']) && !variable_get('omit_vary_cookie')) { 1344 header('Vary: Cookie'); 1345 } 1346 1347 if ($page_compression) { 1348 header('Vary: Accept-Encoding', FALSE); 1349 // If page_compression is enabled, the cache contains gzipped data. 1350 if ($return_compressed) { 1351 // $cache->data['body'] is already gzip'ed, so make sure 1352 // zlib.output_compression does not compress it once more. 1353 ini_set('zlib.output_compression', '0'); 1354 header('Content-Encoding: gzip'); 1355 } 1356 else { 1357 // The client does not support compression, so unzip the data in the 1358 // cache. Strip the gzip header and run uncompress. 1359 $cache->data['body'] = gzinflate(substr(substr($cache->data['body'], 10), 0, -8)); 1360 } 1361 } 1362 1363 // Print the page. 1364 print $cache->data['body']; 1365 } 1366 1367 /** 1368 * Defines the critical hooks that force modules to always be loaded. 1369 */ 1370 function bootstrap_hooks() { 1371 return array('boot', 'exit', 'watchdog', 'language_init'); 1372 } 1373 1374 /** 1375 * Unserializes and appends elements from a serialized string. 1376 * 1377 * @param $obj 1378 * The object to which the elements are appended. 1379 * @param $field 1380 * The attribute of $obj whose value should be unserialized. 1381 */ 1382 function drupal_unpack($obj, $field = 'data') { 1383 if ($obj->$field && $data = unserialize($obj->$field)) { 1384 foreach ($data as $key => $value) { 1385 if (!empty($key) && !isset($obj->$key)) { 1386 $obj->$key = $value; 1387 } 1388 } 1389 } 1390 return $obj; 1391 } 1392 1393 /** 1394 * Translates a string to the current language or to a given language. 1395 * 1396 * The t() function serves two purposes. First, at run-time it translates 1397 * user-visible text into the appropriate language. Second, various mechanisms 1398 * that figure out what text needs to be translated work off t() -- the text 1399 * inside t() calls is added to the database of strings to be translated. 1400 * These strings are expected to be in English, so the first argument should 1401 * always be in English. To enable a fully-translatable site, it is important 1402 * that all human-readable text that will be displayed on the site or sent to 1403 * a user is passed through the t() function, or a related function. See the 1404 * @link http://drupal.org/node/322729 Localization API @endlink pages for 1405 * more information, including recommendations on how to break up or not 1406 * break up strings for translation. 1407 * 1408 * You should never use t() to translate variables, such as calling 1409 * @code t($text); @endcode, unless the text that the variable holds has been 1410 * passed through t() elsewhere (e.g., $text is one of several translated 1411 * literal strings in an array). It is especially important never to call 1412 * @code t($user_text); @endcode, where $user_text is some text that a user 1413 * entered - doing that can lead to cross-site scripting and other security 1414 * problems. However, you can use variable substitution in your string, to put 1415 * variable text such as user names or link URLs into translated text. Variable 1416 * substitution looks like this: 1417 * @code 1418 * $text = t("@name's blog", array('@name' => format_username($account))); 1419 * @endcode 1420 * Basically, you can put variables like @name into your string, and t() will 1421 * substitute their sanitized values at translation time. (See the 1422 * Localization API pages referenced above and the documentation of 1423 * format_string() for details.) Translators can then rearrange the string as 1424 * necessary for the language (e.g., in Spanish, it might be "blog de @name"). 1425 * 1426 * During the Drupal installation phase, some resources used by t() wil not be 1427 * available to code that needs localization. See st() and get_t() for 1428 * alternatives. 1429 * 1430 * @param $string 1431 * A string containing the English string to translate. 1432 * @param $args 1433 * An associative array of replacements to make after translation. Based 1434 * on the first character of the key, the value is escaped and/or themed. 1435 * See format_string() for details. 1436 * @param $options 1437 * An associative array of additional options, with the following elements: 1438 * - 'langcode' (defaults to the current language): The language code to 1439 * translate to a language other than what is used to display the page. 1440 * - 'context' (defaults to the empty context): The context the source string 1441 * belongs to. 1442 * 1443 * @return 1444 * The translated string. 1445 * 1446 * @see st() 1447 * @see get_t() 1448 * @see format_string() 1449 * @ingroup sanitization 1450 */ 1451 function t($string, array $args = array(), array $options = array()) { 1452 global $language; 1453 static $custom_strings; 1454 1455 // Merge in default. 1456 if (empty($options['langcode'])) { 1457 $options['langcode'] = isset($language->language) ? $language->language : 'en'; 1458 } 1459 if (empty($options['context'])) { 1460 $options['context'] = ''; 1461 } 1462 1463 // First, check for an array of customized strings. If present, use the array 1464 // *instead of* database lookups. This is a high performance way to provide a 1465 // handful of string replacements. See settings.php for examples. 1466 // Cache the $custom_strings variable to improve performance. 1467 if (!isset($custom_strings[$options['langcode']])) { 1468 $custom_strings[$options['langcode']] = variable_get('locale_custom_strings_' . $options['langcode'], array()); 1469 } 1470 // Custom strings work for English too, even if locale module is disabled. 1471 if (isset($custom_strings[$options['langcode']][$options['context']][$string])) { 1472 $string = $custom_strings[$options['langcode']][$options['context']][$string]; 1473 } 1474 // Translate with locale module if enabled. 1475 elseif ($options['langcode'] != 'en' && function_exists('locale')) { 1476 $string = locale($string, $options['context'], $options['langcode']); 1477 } 1478 if (empty($args)) { 1479 return $string; 1480 } 1481 else { 1482 return format_string($string, $args); 1483 } 1484 } 1485 1486 /** 1487 * Replaces placeholders with sanitized values in a string. 1488 * 1489 * @param $string 1490 * A string containing placeholders. 1491 * @param $args 1492 * An associative array of replacements to make. Occurrences in $string of 1493 * any key in $args are replaced with the corresponding value, after 1494 * sanitization. The sanitization function depends on the first character of 1495 * the key: 1496 * - !variable: Inserted as is. Use this for text that has already been 1497 * sanitized. 1498 * - @variable: Escaped to HTML using check_plain(). Use this for anything 1499 * displayed on a page on the site. 1500 * - %variable: Escaped as a placeholder for user-submitted content using 1501 * drupal_placeholder(), which shows up as <em>emphasized</em> text. 1502 * 1503 * @see t() 1504 * @ingroup sanitization 1505 */ 1506 function format_string($string, array $args = array()) { 1507 // Transform arguments before inserting them. 1508 foreach ($args as $key => $value) { 1509 switch ($key[0]) { 1510 case '@': 1511 // Escaped only. 1512 $args[$key] = check_plain($value); 1513 break; 1514 1515 case '%': 1516 default: 1517 // Escaped and placeholder. 1518 $args[$key] = drupal_placeholder($value); 1519 break; 1520 1521 case '!': 1522 // Pass-through. 1523 } 1524 } 1525 return strtr($string, $args); 1526 } 1527 1528 /** 1529 * Encodes special characters in a plain-text string for display as HTML. 1530 * 1531 * Also validates strings as UTF-8 to prevent cross site scripting attacks on 1532 * Internet Explorer 6. 1533 * 1534 * @param $text 1535 * The text to be checked or processed. 1536 * 1537 * @return 1538 * An HTML safe version of $text, or an empty string if $text is not 1539 * valid UTF-8. 1540 * 1541 * @see drupal_validate_utf8() 1542 * @ingroup sanitization 1543 */ 1544 function check_plain($text) { 1545 return htmlspecialchars($text, ENT_QUOTES, 'UTF-8'); 1546 } 1547 1548 /** 1549 * Checks whether a string is valid UTF-8. 1550 * 1551 * All functions designed to filter input should use drupal_validate_utf8 1552 * to ensure they operate on valid UTF-8 strings to prevent bypass of the 1553 * filter. 1554 * 1555 * When text containing an invalid UTF-8 lead byte (0xC0 - 0xFF) is presented 1556 * as UTF-8 to Internet Explorer 6, the program may misinterpret subsequent 1557 * bytes. When these subsequent bytes are HTML control characters such as 1558 * quotes or angle brackets, parts of the text that were deemed safe by filters 1559 * end up in locations that are potentially unsafe; An onerror attribute that 1560 * is outside of a tag, and thus deemed safe by a filter, can be interpreted 1561 * by the browser as if it were inside the tag. 1562 * 1563 * The function does not return FALSE for strings containing character codes 1564 * above U+10FFFF, even though these are prohibited by RFC 3629. 1565 * 1566 * @param $text 1567 * The text to check. 1568 * 1569 * @return 1570 * TRUE if the text is valid UTF-8, FALSE if not. 1571 */ 1572 function drupal_validate_utf8($text) { 1573 if (strlen($text) == 0) { 1574 return TRUE; 1575 } 1576 // With the PCRE_UTF8 modifier 'u', preg_match() fails silently on strings 1577 // containing invalid UTF-8 byte sequences. It does not reject character 1578 // codes above U+10FFFF (represented by 4 or more octets), though. 1579 return (preg_match('/^./us', $text) == 1); 1580 } 1581 1582 /** 1583 * Returns the equivalent of Apache's $_SERVER['REQUEST_URI'] variable. 1584 * 1585 * Because $_SERVER['REQUEST_URI'] is only available on Apache, we generate an 1586 * equivalent using other environment variables. 1587 */ 1588 function request_uri() { 1589 if (isset($_SERVER['REQUEST_URI'])) { 1590 $uri = $_SERVER['REQUEST_URI']; 1591 } 1592 else { 1593 if (isset($_SERVER['argv'])) { 1594 $uri = $_SERVER['SCRIPT_NAME'] . '?' . $_SERVER['argv'][0]; 1595 } 1596 elseif (isset($_SERVER['QUERY_STRING'])) { 1597 $uri = $_SERVER['SCRIPT_NAME'] . '?' . $_SERVER['QUERY_STRING']; 1598 } 1599 else { 1600 $uri = $_SERVER['SCRIPT_NAME']; 1601 } 1602 } 1603 // Prevent multiple slashes to avoid cross site requests via the Form API. 1604 $uri = '/' . ltrim($uri, '/'); 1605 1606 return $uri; 1607 } 1608 1609 /** 1610 * Logs an exception. 1611 * 1612 * This is a wrapper function for watchdog() which automatically decodes an 1613 * exception. 1614 * 1615 * @param $type 1616 * The category to which this message belongs. 1617 * @param $exception 1618 * The exception that is going to be logged. 1619 * @param $message 1620 * The message to store in the log. If empty, a text that contains all useful 1621 * information about the passed-in exception is used. 1622 * @param $variables 1623 * Array of variables to replace in the message on display. Defaults to the 1624 * return value of drupal_decode_exception(). 1625 * @param $severity 1626 * The severity of the message, as per RFC 3164. 1627 * @param $link 1628 * A link to associate with the message. 1629 * 1630 * @see watchdog() 1631 * @see drupal_decode_exception() 1632 */ 1633 function watchdog_exception($type, Exception $exception, $message = NULL, $variables = array(), $severity = WATCHDOG_ERROR, $link = NULL) { 1634 1635 // Use a default value if $message is not set. 1636 if (empty($message)) { 1637 // The exception message is run through check_plain() by _drupal_decode_exception(). 1638 $message = '%type: !message in %function (line %line of %file).'; 1639 } 1640 // $variables must be an array so that we can add the exception information. 1641 if (!is_array($variables)) { 1642 $variables = array(); 1643 } 1644 1645 require_once DRUPAL_ROOT . '/includes/errors.inc'; 1646 $variables += _drupal_decode_exception($exception); 1647 watchdog($type, $message, $variables, $severity, $link); 1648 } 1649 1650 /** 1651 * Logs a system message. 1652 * 1653 * @param $type 1654 * The category to which this message belongs. Can be any string, but the 1655 * general practice is to use the name of the module calling watchdog(). 1656 * @param $message 1657 * The message to store in the log. Keep $message translatable 1658 * by not concatenating dynamic values into it! Variables in the 1659 * message should be added by using placeholder strings alongside 1660 * the variables argument to declare the value of the placeholders. 1661 * See t() for documentation on how $message and $variables interact. 1662 * @param $variables 1663 * Array of variables to replace in the message on display or 1664 * NULL if message is already translated or not possible to 1665 * translate. 1666 * @param $severity 1667 * The severity of the message; one of the following values as defined in 1668 * @link http://www.faqs.org/rfcs/rfc3164.html RFC 3164: @endlink 1669 * - WATCHDOG_EMERGENCY: Emergency, system is unusable. 1670 * - WATCHDOG_ALERT: Alert, action must be taken immediately. 1671 * - WATCHDOG_CRITICAL: Critical conditions. 1672 * - WATCHDOG_ERROR: Error conditions. 1673 * - WATCHDOG_WARNING: Warning conditions. 1674 * - WATCHDOG_NOTICE: (default) Normal but significant conditions. 1675 * - WATCHDOG_INFO: Informational messages. 1676 * - WATCHDOG_DEBUG: Debug-level messages. 1677 * @param $link 1678 * A link to associate with the message. 1679 * 1680 * @see watchdog_severity_levels() 1681 * @see hook_watchdog() 1682 */ 1683 function watchdog($type, $message, $variables = array(), $severity = WATCHDOG_NOTICE, $link = NULL) { 1684 global $user, $base_root; 1685 1686 static $in_error_state = FALSE; 1687 1688 // It is possible that the error handling will itself trigger an error. In that case, we could 1689 // end up in an infinite loop. To avoid that, we implement a simple static semaphore. 1690 if (!$in_error_state && function_exists('module_implements')) { 1691 $in_error_state = TRUE; 1692 1693 // The user object may not exist in all conditions, so 0 is substituted if needed. 1694 $user_uid = isset($user->uid) ? $user->uid : 0; 1695 1696 // Prepare the fields to be logged 1697 $log_entry = array( 1698 'type' => $type, 1699 'message' => $message, 1700 'variables' => $variables, 1701 'severity' => $severity, 1702 'link' => $link, 1703 'user' => $user, 1704 'uid' => $user_uid, 1705 'request_uri' => $base_root . request_uri(), 1706 'referer' => isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : '', 1707 'ip' => ip_address(), 1708 // Request time isn't accurate for long processes, use time() instead. 1709 'timestamp' => time(), 1710 ); 1711 1712 // Call the logging hooks to log/process the message 1713 foreach (module_implements('watchdog') as $module) { 1714 module_invoke($module, 'watchdog', $log_entry); 1715 } 1716 1717 // It is critical that the semaphore is only cleared here, in the parent 1718 // watchdog() call (not outside the loop), to prevent recursive execution. 1719 $in_error_state = FALSE; 1720 } 1721 } 1722 1723 /** 1724 * Sets a message to display to the user. 1725 * 1726 * Messages are stored in a session variable and displayed in page.tpl.php via 1727 * the $messages theme variable. 1728 * 1729 * Example usage: 1730 * @code 1731 * drupal_set_message(t('An error occurred and processing did not complete.'), 'error'); 1732 * @endcode 1733 * 1734 * @param string $message 1735 * (optional) The translated message to be displayed to the user. For 1736 * consistency with other messages, it should begin with a capital letter and 1737 * end with a period. 1738 * @param string $type 1739 * (optional) The message's type. Defaults to 'status'. These values are 1740 * supported: 1741 * - 'status' 1742 * - 'warning' 1743 * - 'error' 1744 * @param bool $repeat 1745 * (optional) If this is FALSE and the message is already set, then the 1746 * message won't be repeated. Defaults to TRUE. 1747 * 1748 * @return array|null 1749 * A multidimensional array with keys corresponding to the set message types. 1750 * The indexed array values of each contain the set messages for that type. 1751 * Or, if there are no messages set, the function returns NULL. 1752 * 1753 * @see drupal_get_messages() 1754 * @see theme_status_messages() 1755 */ 1756 function drupal_set_message($message = NULL, $type = 'status', $repeat = TRUE) { 1757 if ($message) { 1758 if (!isset($_SESSION['messages'][$type])) { 1759 $_SESSION['messages'][$type] = array(); 1760 } 1761 1762 if ($repeat || !in_array($message, $_SESSION['messages'][$type])) { 1763 $_SESSION['messages'][$type][] = $message; 1764 } 1765 1766 // Mark this page as being uncacheable. 1767 drupal_page_is_cacheable(FALSE); 1768 } 1769 1770 // Messages not set when DB connection fails. 1771 return isset($_SESSION['messages']) ? $_SESSION['messages'] : NULL; 1772 } 1773 1774 /** 1775 * Returns all messages that have been set with drupal_set_message(). 1776 * 1777 * @param string $type 1778 * (optional) Limit the messages returned by type. Defaults to NULL, meaning 1779 * all types. These values are supported: 1780 * - NULL 1781 * - 'status' 1782 * - 'warning' 1783 * - 'error' 1784 * @param bool $clear_queue 1785 * (optional) If this is TRUE, the queue will be cleared of messages of the 1786 * type specified in the $type parameter. Otherwise the queue will be left 1787 * intact. Defaults to TRUE. 1788 * 1789 * @return array 1790 * A multidimensional array with keys corresponding to the set message types. 1791 * The indexed array values of each contain the set messages for that type. 1792 * The messages returned are limited to the type specified in the $type 1793 * parameter. If there are no messages of the specified type, an empty array 1794 * is returned. 1795 * 1796 * @see drupal_set_message() 1797 * @see theme_status_messages() 1798 */ 1799 function drupal_get_messages($type = NULL, $clear_queue = TRUE) { 1800 if ($messages = drupal_set_message()) { 1801 if ($type) { 1802 if ($clear_queue) { 1803 unset($_SESSION['messages'][$type]); 1804 } 1805 if (isset($messages[$type])) { 1806 return array($type => $messages[$type]); 1807 } 1808 } 1809 else { 1810 if ($clear_queue) { 1811 unset($_SESSION['messages']); 1812 } 1813 return $messages; 1814 } 1815 } 1816 return array(); 1817 } 1818 1819 /** 1820 * Gets the title of the current page. 1821 * 1822 * The title is displayed on the page and in the title bar. 1823 * 1824 * @return 1825 * The current page's title. 1826 */ 1827 function drupal_get_title() { 1828 $title = drupal_set_title(); 1829 1830 // During a bootstrap, menu.inc is not included and thus we cannot provide a title. 1831 if (!isset($title) && function_exists('menu_get_active_title')) { 1832 $title = check_plain(menu_get_active_title()); 1833 } 1834 1835 return $title; 1836 } 1837 1838 /** 1839 * Sets the title of the current page. 1840 * 1841 * The title is displayed on the page and in the title bar. 1842 * 1843 * @param $title 1844 * Optional string value to assign to the page title; or if set to NULL 1845 * (default), leaves the current title unchanged. 1846 * @param $output 1847 * Optional flag - normally should be left as CHECK_PLAIN. Only set to 1848 * PASS_THROUGH if you have already removed any possibly dangerous code 1849 * from $title using a function like check_plain() or filter_xss(). With this 1850 * flag the string will be passed through unchanged. 1851 * 1852 * @return 1853 * The updated title of the current page. 1854 */ 1855 function drupal_set_title($title = NULL, $output = CHECK_PLAIN) { 1856 $stored_title = &drupal_static(__FUNCTION__); 1857 1858 if (isset($title)) { 1859 $stored_title = ($output == PASS_THROUGH) ? $title : check_plain($title); 1860 } 1861 1862 return $stored_title; 1863 } 1864 1865 /** 1866 * Checks to see if an IP address has been blocked. 1867 * 1868 * Blocked IP addresses are stored in the database by default. However for 1869 * performance reasons we allow an override in settings.php. This allows us 1870 * to avoid querying the database at this critical stage of the bootstrap if 1871 * an administrative interface for IP address blocking is not required. 1872 * 1873 * @param $ip 1874 * IP address to check. 1875 * 1876 * @return bool 1877 * TRUE if access is denied, FALSE if access is allowed. 1878 */ 1879 function drupal_is_denied($ip) { 1880 // Because this function is called on every page request, we first check 1881 // for an array of IP addresses in settings.php before querying the 1882 // database. 1883 $blocked_ips = variable_get('blocked_ips'); 1884 $denied = FALSE; 1885 if (isset($blocked_ips) && is_array($blocked_ips)) { 1886 $denied = in_array($ip, $blocked_ips); 1887 } 1888 // Only check if database.inc is loaded already. If 1889 // $conf['page_cache_without_database'] = TRUE; is set in settings.php, 1890 // then the database won't be loaded here so the IPs in the database 1891 // won't be denied. However the user asked explicitly not to use the 1892 // database and also in this case it's quite likely that the user relies 1893 // on higher performance solutions like a firewall. 1894 elseif (class_exists('Database', FALSE)) { 1895 $denied = (bool)db_query("SELECT 1 FROM {blocked_ips} WHERE ip = :ip", array(':ip' => $ip))->fetchField(); 1896 } 1897 return $denied; 1898 } 1899 1900 /** 1901 * Handles denied users. 1902 * 1903 * @param $ip 1904 * IP address to check. Prints a message and exits if access is denied. 1905 */ 1906 function drupal_block_denied($ip) { 1907 // Deny access to blocked IP addresses - t() is not yet available. 1908 if (drupal_is_denied($ip)) { 1909 header($_SERVER['SERVER_PROTOCOL'] . ' 403 Forbidden'); 1910 print 'Sorry, ' . check_plain(ip_address()) . ' has been banned.'; 1911 exit(); 1912 } 1913 } 1914 1915 /** 1916 * Returns a string of highly randomized bytes (over the full 8-bit range). 1917 * 1918 * This function is better than simply calling mt_rand() or any other built-in 1919 * PHP function because it can return a long string of bytes (compared to < 4 1920 * bytes normally from mt_rand()) and uses the best available pseudo-random 1921 * source. 1922 * 1923 * @param $count 1924 * The number of characters (bytes) to return in the string. 1925 */ 1926 function drupal_random_bytes($count) { 1927 // $random_state does not use drupal_static as it stores random bytes. 1928 static $random_state, $bytes, $php_compatible; 1929 // Initialize on the first call. The contents of $_SERVER includes a mix of 1930 // user-specific and system information that varies a little with each page. 1931 if (!isset($random_state)) { 1932 $random_state = print_r($_SERVER, TRUE); 1933 if (function_exists('getmypid')) { 1934 // Further initialize with the somewhat random PHP process ID. 1935 $random_state .= getmypid(); 1936 } 1937 $bytes = ''; 1938 } 1939 if (strlen($bytes) < $count) { 1940 // PHP versions prior 5.3.4 experienced openssl_random_pseudo_bytes() 1941 // locking on Windows and rendered it unusable. 1942 if (!isset($php_compatible)) { 1943 $php_compatible = version_compare(PHP_VERSION, '5.3.4', '>='); 1944 } 1945 // /dev/urandom is available on many *nix systems and is considered the 1946 // best commonly available pseudo-random source. 1947 if ($fh = @fopen('/dev/urandom', 'rb')) { 1948 // PHP only performs buffered reads, so in reality it will always read 1949 // at least 4096 bytes. Thus, it costs nothing extra to read and store 1950 // that much so as to speed any additional invocations. 1951 $bytes .= fread($fh, max(4096, $count)); 1952 fclose($fh); 1953 } 1954 // openssl_random_pseudo_bytes() will find entropy in a system-dependent 1955 // way. 1956 elseif ($php_compatible && function_exists('openssl_random_pseudo_bytes')) { 1957 $bytes .= openssl_random_pseudo_bytes($count - strlen($bytes)); 1958 } 1959 // If /dev/urandom is not available or returns no bytes, this loop will 1960 // generate a good set of pseudo-random bytes on any system. 1961 // Note that it may be important that our $random_state is passed 1962 // through hash() prior to being rolled into $output, that the two hash() 1963 // invocations are different, and that the extra input into the first one - 1964 // the microtime() - is prepended rather than appended. This is to avoid 1965 // directly leaking $random_state via the $output stream, which could 1966 // allow for trivial prediction of further "random" numbers. 1967 while (strlen($bytes) < $count) { 1968 $random_state = hash('sha256', microtime() . mt_rand() . $random_state); 1969 $bytes .= hash('sha256', mt_rand() . $random_state, TRUE); 1970 } 1971 } 1972 $output = substr($bytes, 0, $count); 1973 $bytes = substr($bytes, $count); 1974 return $output; 1975 } 1976 1977 /** 1978 * Calculates a base-64 encoded, URL-safe sha-256 hmac. 1979 * 1980 * @param $data 1981 * String to be validated with the hmac. 1982 * @param $key 1983 * A secret string key. 1984 * 1985 * @return 1986 * A base-64 encoded sha-256 hmac, with + replaced with -, / with _ and 1987 * any = padding characters removed. 1988 */ 1989 function drupal_hmac_base64($data, $key) { 1990 $hmac = base64_encode(hash_hmac('sha256', $data, $key, TRUE)); 1991 // Modify the hmac so it's safe to use in URLs. 1992 return strtr($hmac, array('+' => '-', '/' => '_', '=' => '')); 1993 } 1994 1995 /** 1996 * Calculates a base-64 encoded, URL-safe sha-256 hash. 1997 * 1998 * @param $data 1999 * String to be hashed. 2000 * 2001 * @return 2002 * A base-64 encoded sha-256 hash, with + replaced with -, / with _ and 2003 * any = padding characters removed. 2004 */ 2005 function drupal_hash_base64($data) { 2006 $hash = base64_encode(hash('sha256', $data, TRUE)); 2007 // Modify the hash so it's safe to use in URLs. 2008 return strtr($hash, array('+' => '-', '/' => '_', '=' => '')); 2009 } 2010 2011 /** 2012 * Merges multiple arrays, recursively, and returns the merged array. 2013 * 2014 * This function is similar to PHP's array_merge_recursive() function, but it 2015 * handles non-array values differently. When merging values that are not both 2016 * arrays, the latter value replaces the former rather than merging with it. 2017 * 2018 * Example: 2019 * @code 2020 * $link_options_1 = array('fragment' => 'x', 'attributes' => array('title' => t('X'), 'class' => array('a', 'b'))); 2021 * $link_options_2 = array('fragment' => 'y', 'attributes' => array('title' => t('Y'), 'class' => array('c', 'd'))); 2022 * 2023 * // This results in array('fragment' => array('x', 'y'), 'attributes' => array('title' => array(t('X'), t('Y')), 'class' => array('a', 'b', 'c', 'd'))). 2024 * $incorrect = array_merge_recursive($link_options_1, $link_options_2); 2025 * 2026 * // This results in array('fragment' => 'y', 'attributes' => array('title' => t('Y'), 'class' => array('a', 'b', 'c', 'd'))). 2027 * $correct = drupal_array_merge_deep($link_options_1, $link_options_2); 2028 * @endcode 2029 * 2030 * @param ... 2031 * Arrays to merge. 2032 * 2033 * @return 2034 * The merged array. 2035 * 2036 * @see drupal_array_merge_deep_array() 2037 */ 2038 function drupal_array_merge_deep() { 2039 $args = func_get_args(); 2040 return drupal_array_merge_deep_array($args); 2041 } 2042 2043 /** 2044 * Merges multiple arrays, recursively, and returns the merged array. 2045 * 2046 * This function is equivalent to drupal_array_merge_deep(), except the 2047 * input arrays are passed as a single array parameter rather than a variable 2048 * parameter list. 2049 * 2050 * The following are equivalent: 2051 * - drupal_array_merge_deep($a, $b); 2052 * - drupal_array_merge_deep_array(array($a, $b)); 2053 * 2054 * The following are also equivalent: 2055 * - call_user_func_array('drupal_array_merge_deep', $arrays_to_merge); 2056 * - drupal_array_merge_deep_array($arrays_to_merge); 2057 * 2058 * @see drupal_array_merge_deep() 2059 */ 2060 function drupal_array_merge_deep_array($arrays) { 2061 $result = array(); 2062 2063 foreach ($arrays as $array) { 2064 foreach ($array as $key => $value) { 2065 // Renumber integer keys as array_merge_recursive() does. Note that PHP 2066 // automatically converts array keys that are integer strings (e.g., '1') 2067 // to integers. 2068 if (is_integer($key)) { 2069 $result[] = $value; 2070 } 2071 // Recurse when both values are arrays. 2072 elseif (isset($result[$key]) && is_array($result[$key]) && is_array($value)) { 2073 $result[$key] = drupal_array_merge_deep_array(array($result[$key], $value)); 2074 } 2075 // Otherwise, use the latter value, overriding any previous value. 2076 else { 2077 $result[$key] = $value; 2078 } 2079 } 2080 } 2081 2082 return $result; 2083 } 2084 2085 /** 2086 * Generates a default anonymous $user object. 2087 * 2088 * @return Object - the user object. 2089 */ 2090 function drupal_anonymous_user() { 2091 $user = new stdClass(); 2092 $user->uid = 0; 2093 $user->hostname = ip_address(); 2094 $user->roles = array(); 2095 $user->roles[DRUPAL_ANONYMOUS_RID] = 'anonymous user'; 2096 $user->cache = 0; 2097 return $user; 2098 } 2099 2100 /** 2101 * Ensures Drupal is bootstrapped to the specified phase. 2102 * 2103 * In order to bootstrap Drupal from another PHP script, you can use this code: 2104 * @code 2105 * define('DRUPAL_ROOT', '/path/to/drupal'); 2106 * require_once DRUPAL_ROOT . '/includes/bootstrap.inc'; 2107 * drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL); 2108 * @endcode 2109 * 2110 * @param $phase 2111 * A constant telling which phase to bootstrap to. When you bootstrap to a 2112 * particular phase, all earlier phases are run automatically. Possible 2113 * values: 2114 * - DRUPAL_BOOTSTRAP_CONFIGURATION: Initializes configuration. 2115 * - DRUPAL_BOOTSTRAP_PAGE_CACHE: Tries to serve a cached page. 2116 * - DRUPAL_BOOTSTRAP_DATABASE: Initializes the database layer. 2117 * - DRUPAL_BOOTSTRAP_VARIABLES: Initializes the variable system. 2118 * - DRUPAL_BOOTSTRAP_SESSION: Initializes session handling. 2119 * - DRUPAL_BOOTSTRAP_PAGE_HEADER: Sets up the page header. 2120 * - DRUPAL_BOOTSTRAP_LANGUAGE: Finds out the language of the page. 2121 * - DRUPAL_BOOTSTRAP_FULL: Fully loads Drupal. Validates and fixes input 2122 * data. 2123 * @param $new_phase 2124 * A boolean, set to FALSE if calling drupal_bootstrap from inside a 2125 * function called from drupal_bootstrap (recursion). 2126 * 2127 * @return 2128 * The most recently completed phase. 2129 */ 2130 function drupal_bootstrap($phase = NULL, $new_phase = TRUE) { 2131 // Not drupal_static(), because does not depend on any run-time information. 2132 static $phases = array( 2133 DRUPAL_BOOTSTRAP_CONFIGURATION, 2134 DRUPAL_BOOTSTRAP_PAGE_CACHE, 2135 DRUPAL_BOOTSTRAP_DATABASE, 2136 DRUPAL_BOOTSTRAP_VARIABLES, 2137 DRUPAL_BOOTSTRAP_SESSION, 2138 DRUPAL_BOOTSTRAP_PAGE_HEADER, 2139 DRUPAL_BOOTSTRAP_LANGUAGE, 2140 DRUPAL_BOOTSTRAP_FULL, 2141 ); 2142 // Not drupal_static(), because the only legitimate API to control this is to 2143 // call drupal_bootstrap() with a new phase parameter. 2144 static $final_phase; 2145 // Not drupal_static(), because it's impossible to roll back to an earlier 2146 // bootstrap state. 2147 static $stored_phase = -1; 2148 2149 // When not recursing, store the phase name so it's not forgotten while 2150 // recursing. 2151 if ($new_phase) { 2152 $final_phase = $phase; 2153 } 2154 if (isset($phase)) { 2155 // Call a phase if it has not been called before and is below the requested 2156 // phase. 2157 while ($phases && $phase > $stored_phase && $final_phase > $stored_phase) { 2158 $current_phase = array_shift($phases); 2159 2160 // This function is re-entrant. Only update the completed phase when the 2161 // current call actually resulted in a progress in the bootstrap process. 2162 if ($current_phase > $stored_phase) { 2163 $stored_phase = $current_phase; 2164 } 2165 2166 switch ($current_phase) { 2167 case DRUPAL_BOOTSTRAP_CONFIGURATION: 2168 _drupal_bootstrap_configuration(); 2169 break; 2170 2171 case DRUPAL_BOOTSTRAP_PAGE_CACHE: 2172 _drupal_bootstrap_page_cache(); 2173 break; 2174 2175 case DRUPAL_BOOTSTRAP_DATABASE: 2176 _drupal_bootstrap_database(); 2177 break; 2178 2179 case DRUPAL_BOOTSTRAP_VARIABLES: 2180 _drupal_bootstrap_variables(); 2181 break; 2182 2183 case DRUPAL_BOOTSTRAP_SESSION: 2184 require_once DRUPAL_ROOT . '/' . variable_get('session_inc', 'includes/session.inc'); 2185 drupal_session_initialize(); 2186 break; 2187 2188 case DRUPAL_BOOTSTRAP_PAGE_HEADER: 2189 _drupal_bootstrap_page_header(); 2190 break; 2191 2192 case DRUPAL_BOOTSTRAP_LANGUAGE: 2193 drupal_language_initialize(); 2194 break; 2195 2196 case DRUPAL_BOOTSTRAP_FULL: 2197 require_once DRUPAL_ROOT . '/includes/common.inc'; 2198 _drupal_bootstrap_full(); 2199 break; 2200 } 2201 } 2202 } 2203 return $stored_phase; 2204 } 2205 2206 /** 2207 * Returns the time zone of the current user. 2208 */ 2209 function drupal_get_user_timezone() { 2210 global $user; 2211 if (variable_get('configurable_timezones', 1) && $user->uid && $user->timezone) { 2212 return $user->timezone; 2213 } 2214 else { 2215 // Ignore PHP strict notice if time zone has not yet been set in the php.ini 2216 // configuration. 2217 return variable_get('date_default_timezone', @date_default_timezone_get()); 2218 } 2219 } 2220 2221 /** 2222 * Provides custom PHP error handling. 2223 * 2224 * @param $error_level 2225 * The level of the error raised. 2226 * @param $message 2227 * The error message. 2228 * @param $filename 2229 * The filename that the error was raised in. 2230 * @param $line 2231 * The line number the error was raised at. 2232 * @param $context 2233 * An array that points to the active symbol table at the point the error 2234 * occurred. 2235 */ 2236 function _drupal_error_handler($error_level, $message, $filename, $line, $context) { 2237 require_once DRUPAL_ROOT . '/includes/errors.inc'; 2238 _drupal_error_handler_real($error_level, $message, $filename, $line, $context); 2239 } 2240 2241 /** 2242 * Provides custom PHP exception handling. 2243 * 2244 * Uncaught exceptions are those not enclosed in a try/catch block. They are 2245 * always fatal: the execution of the script will stop as soon as the exception 2246 * handler exits. 2247 * 2248 * @param $exception 2249 * The exception object that was thrown. 2250 */ 2251 function _drupal_exception_handler($exception) { 2252 require_once DRUPAL_ROOT . '/includes/errors.inc'; 2253 2254 try { 2255 // Log the message to the watchdog and return an error page to the user. 2256 _drupal_log_error(_drupal_decode_exception($exception), TRUE); 2257 } 2258 catch (Exception $exception2) { 2259 // Another uncaught exception was thrown while handling the first one. 2260 // If we are displaying errors, then do so with no possibility of a further uncaught exception being thrown. 2261 if (error_displayable()) { 2262 print '<h1>Additional uncaught exception thrown while handling exception.</h1>'; 2263 print '<h2>Original</h2><p>' . _drupal_render_exception_safe($exception) . '</p>'; 2264 print '<h2>Additional</h2><p>' . _drupal_render_exception_safe($exception2) . '</p><hr />'; 2265 } 2266 } 2267 } 2268 2269 /** 2270 * Sets up the script environment and loads settings.php. 2271 */ 2272 function _drupal_bootstrap_configuration() { 2273 // Set the Drupal custom error handler. 2274 set_error_handler('_drupal_error_handler'); 2275 set_exception_handler('_drupal_exception_handler'); 2276 2277 drupal_environment_initialize(); 2278 // Start a page timer: 2279 timer_start('page'); 2280 // Initialize the configuration, including variables from settings.php. 2281 drupal_settings_initialize(); 2282 } 2283 2284 /** 2285 * Attempts to serve a page from the cache. 2286 */ 2287 function _drupal_bootstrap_page_cache() { 2288 global $user; 2289 2290 // Allow specifying special cache handlers in settings.php, like 2291 // using memcached or files for storing cache information. 2292 require_once DRUPAL_ROOT . '/includes/cache.inc'; 2293 foreach (variable_get('cache_backends', array()) as $include) { 2294 require_once DRUPAL_ROOT . '/' . $include; 2295 } 2296 // Check for a cache mode force from settings.php. 2297 if (variable_get('page_cache_without_database')) { 2298 $cache_enabled = TRUE; 2299 } 2300 else { 2301 drupal_bootstrap(DRUPAL_BOOTSTRAP_VARIABLES, FALSE); 2302 $cache_enabled = variable_get('cache'); 2303 } 2304 drupal_block_denied(ip_address()); 2305 // If there is no session cookie and cache is enabled (or forced), try 2306 // to serve a cached page. 2307 if (!isset($_COOKIE[session_name()]) && $cache_enabled) { 2308 // Make sure there is a user object because its timestamp will be 2309 // checked, hook_boot might check for anonymous user etc. 2310 $user = drupal_anonymous_user(); 2311 // Get the page from the cache. 2312 $cache = drupal_page_get_cache(); 2313 // If there is a cached page, display it. 2314 if (is_object($cache)) { 2315 header('X-Drupal-Cache: HIT'); 2316 // Restore the metadata cached with the page. 2317 $_GET['q'] = $cache->data['path']; 2318 drupal_set_title($cache->data['title'], PASS_THROUGH); 2319 date_default_timezone_set(drupal_get_user_timezone()); 2320 // If the skipping of the bootstrap hooks is not enforced, call 2321 // hook_boot. 2322 if (variable_get('page_cache_invoke_hooks', TRUE)) { 2323 bootstrap_invoke_all('boot'); 2324 } 2325 drupal_serve_page_from_cache($cache); 2326 // If the skipping of the bootstrap hooks is not enforced, call 2327 // hook_exit. 2328 if (variable_get('page_cache_invoke_hooks', TRUE)) { 2329 bootstrap_invoke_all('exit'); 2330 } 2331 // We are done. 2332 exit; 2333 } 2334 else { 2335 header('X-Drupal-Cache: MISS'); 2336 } 2337 } 2338 } 2339 2340 /** 2341 * Initializes the database system and registers autoload functions. 2342 */ 2343 function _drupal_bootstrap_database() { 2344 // Redirect the user to the installation script if Drupal has not been 2345 // installed yet (i.e., if no $databases array has been defined in the 2346 // settings.php file) and we are not already installing. 2347 if (empty($GLOBALS['databases']) && !drupal_installation_attempted()) { 2348 include_once DRUPAL_ROOT . '/includes/install.inc'; 2349 install_goto('install.php'); 2350 } 2351 2352 // The user agent header is used to pass a database prefix in the request when 2353 // running tests. However, for security reasons, it is imperative that we 2354 // validate we ourselves made the request. 2355 if ($test_prefix = drupal_valid_test_ua()) { 2356 // Set the test run id for use in other parts of Drupal. 2357 $test_info = &$GLOBALS['drupal_test_info']; 2358 $test_info['test_run_id'] = $test_prefix; 2359 $test_info['in_child_site'] = TRUE; 2360 2361 foreach ($GLOBALS['databases']['default'] as &$value) { 2362 // Extract the current default database prefix. 2363 if (!isset($value['prefix'])) { 2364 $current_prefix = ''; 2365 } 2366 elseif (is_array($value['prefix'])) { 2367 $current_prefix = $value['prefix']['default']; 2368 } 2369 else { 2370 $current_prefix = $value['prefix']; 2371 } 2372 2373 // Remove the current database prefix and replace it by our own. 2374 $value['prefix'] = array( 2375 'default' => $current_prefix . $test_prefix, 2376 ); 2377 } 2378 } 2379 2380 // Initialize the database system. Note that the connection 2381 // won't be initialized until it is actually requested. 2382 require_once DRUPAL_ROOT . '/includes/database/database.inc'; 2383 2384 // Register autoload functions so that we can access classes and interfaces. 2385 // The database autoload routine comes first so that we can load the database 2386 // system without hitting the database. That is especially important during 2387 // the install or upgrade process. 2388 spl_autoload_register('drupal_autoload_class'); 2389 spl_autoload_register('drupal_autoload_interface'); 2390 } 2391 2392 /** 2393 * Loads system variables and all enabled bootstrap modules. 2394 */ 2395 function _drupal_bootstrap_variables() { 2396 global $conf; 2397 2398 // Initialize the lock system. 2399 require_once DRUPAL_ROOT . '/' . variable_get('lock_inc', 'includes/lock.inc'); 2400 lock_initialize(); 2401 2402 // Load variables from the database, but do not overwrite variables set in settings.php. 2403 $conf = variable_initialize(isset($conf) ? $conf : array()); 2404 // Load bootstrap modules. 2405 require_once DRUPAL_ROOT . '/includes/module.inc'; 2406 module_load_all(TRUE); 2407 } 2408 2409 /** 2410 * Invokes hook_boot(), initializes locking system, and sends HTTP headers. 2411 */ 2412 function _drupal_bootstrap_page_header() { 2413 bootstrap_invoke_all('boot'); 2414 2415 if (!drupal_is_cli()) { 2416 ob_start(); 2417 drupal_page_header(); 2418 } 2419 } 2420 2421 /** 2422 * Returns the current bootstrap phase for this Drupal process. 2423 * 2424 * The current phase is the one most recently completed by drupal_bootstrap(). 2425 * 2426 * @see drupal_bootstrap() 2427 */ 2428 function drupal_get_bootstrap_phase() { 2429 return drupal_bootstrap(); 2430 } 2431 2432 /** 2433 * Returns the test prefix if this is an internal request from SimpleTest. 2434 * 2435 * @return 2436 * Either the simpletest prefix (the string "simpletest" followed by any 2437 * number of digits) or FALSE if the user agent does not contain a valid 2438 * HMAC and timestamp. 2439 */ 2440 function drupal_valid_test_ua() { 2441 global $drupal_hash_salt; 2442 // No reason to reset this. 2443 static $test_prefix; 2444 2445 if (isset($test_prefix)) { 2446 return $test_prefix; 2447 } 2448 2449 if (isset($_SERVER['HTTP_USER_AGENT']) && preg_match("/^(simpletest\d+);(.+);(.+);(.+)$/", $_SERVER['HTTP_USER_AGENT'], $matches)) { 2450 list(, $prefix, $time, $salt, $hmac) = $matches; 2451 $check_string = $prefix . ';' . $time . ';' . $salt; 2452 // We use the salt from settings.php to make the HMAC key, since 2453 // the database is not yet initialized and we can't access any Drupal variables. 2454 // The file properties add more entropy not easily accessible to others. 2455 $key = $drupal_hash_salt . filectime(__FILE__) . fileinode(__FILE__); 2456 $time_diff = REQUEST_TIME - $time; 2457 // Since we are making a local request a 5 second time window is allowed, 2458 // and the HMAC must match. 2459 if ($time_diff >= 0 && $time_diff <= 5 && $hmac == drupal_hmac_base64($check_string, $key)) { 2460 $test_prefix = $prefix; 2461 return $test_prefix; 2462 } 2463 } 2464 2465 $test_prefix = FALSE; 2466 return $test_prefix; 2467 } 2468 2469 /** 2470 * Generates a user agent string with a HMAC and timestamp for simpletest. 2471 */ 2472 function drupal_generate_test_ua($prefix) { 2473 global $drupal_hash_salt; 2474 static $key; 2475 2476 if (!isset($key)) { 2477 // We use the salt from settings.php to make the HMAC key, since 2478 // the database is not yet initialized and we can't access any Drupal variables. 2479 // The file properties add more entropy not easily accessible to others. 2480 $key = $drupal_hash_salt . filectime(__FILE__) . fileinode(__FILE__); 2481 } 2482 // Generate a moderately secure HMAC based on the database credentials. 2483 $salt = uniqid('', TRUE); 2484 $check_string = $prefix . ';' . time() . ';' . $salt; 2485 return $check_string . ';' . drupal_hmac_base64($check_string, $key); 2486 } 2487 2488 /** 2489 * Enables use of the theme system without requiring database access. 2490 * 2491 * Loads and initializes the theme system for site installs, updates and when 2492 * the site is in maintenance mode. This also applies when the database fails. 2493 * 2494 * @see _drupal_maintenance_theme() 2495 */ 2496 function drupal_maintenance_theme() { 2497 require_once DRUPAL_ROOT . '/includes/theme.maintenance.inc'; 2498 _drupal_maintenance_theme(); 2499 } 2500 2501 /** 2502 * Returns a simple 404 Not Found page. 2503 * 2504 * If fast 404 pages are enabled, and this is a matching page then print a 2505 * simple 404 page and exit. 2506 * 2507 * This function is called from drupal_deliver_html_page() at the time when a 2508 * a normal 404 page is generated, but it can also optionally be called directly 2509 * from settings.php to prevent a Drupal bootstrap on these pages. See 2510 * documentation in settings.php for the benefits and drawbacks of using this. 2511 * 2512 * Paths to dynamically-generated content, such as image styles, should also be 2513 * accounted for in this function. 2514 */ 2515 function drupal_fast_404() { 2516 $exclude_paths = variable_get('404_fast_paths_exclude', FALSE); 2517 if ($exclude_paths && !preg_match($exclude_paths, $_GET['q'])) { 2518 $fast_paths = variable_get('404_fast_paths', FALSE); 2519 if ($fast_paths && preg_match($fast_paths, $_GET['q'])) { 2520 drupal_add_http_header('Status', '404 Not Found'); 2521 $fast_404_html = variable_get('404_fast_html', '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML+RDFa 1.0//EN" "http://www.w3.org/MarkUp/DTD/xhtml-rdfa-1.dtd"><html xmlns="http://www.w3.org/1999/xhtml"><head><title>404 Not Found</title></head><body><h1>Not Found</h1><p>The requested URL "@path" was not found on this server.</p></body></html>'); 2522 // Replace @path in the variable with the page path. 2523 print strtr($fast_404_html, array('@path' => check_plain(request_uri()))); 2524 exit; 2525 } 2526 } 2527 } 2528 2529 /** 2530 * Returns TRUE if a Drupal installation is currently being attempted. 2531 */ 2532 function drupal_installation_attempted() { 2533 return defined('MAINTENANCE_MODE') && MAINTENANCE_MODE == 'install'; 2534 } 2535 2536 /** 2537 * Returns the name of the proper localization function. 2538 * 2539 * get_t() exists to support localization for code that might run during 2540 * the installation phase, when some elements of the system might not have 2541 * loaded. 2542 * 2543 * This would include implementations of hook_install(), which could run 2544 * during the Drupal installation phase, and might also be run during 2545 * non-installation time, such as while installing the module from the the 2546 * module administration page. 2547 * 2548 * Example usage: 2549 * @code 2550 * $t = get_t(); 2551 * $translated = $t('translate this'); 2552 * @endcode 2553 * 2554 * Use t() if your code will never run during the Drupal installation phase. 2555 * Use st() if your code will only run during installation and never any other 2556 * time. Use get_t() if your code could run in either circumstance. 2557 * 2558 * @see t() 2559 * @see st() 2560 * @ingroup sanitization 2561 */ 2562 function get_t() { 2563 static $t; 2564 // This is not converted to drupal_static because there is no point in 2565 // resetting this as it can not change in the course of a request. 2566 if (!isset($t)) { 2567 $t = drupal_installation_attempted() ? 'st' : 't'; 2568 } 2569 return $t; 2570 } 2571 2572 /** 2573 * Initializes all the defined language types. 2574 */ 2575 function drupal_language_initialize() { 2576 $types = language_types(); 2577 2578 // Ensure the language is correctly returned, even without multilanguage 2579 // support. Also make sure we have a $language fallback, in case a language 2580 // negotiation callback needs to do a full bootstrap. 2581 // Useful for eg. XML/HTML 'lang' attributes. 2582 $default = language_default(); 2583 foreach ($types as $type) { 2584 $GLOBALS[$type] = $default; 2585 } 2586 if (drupal_multilingual()) { 2587 include_once DRUPAL_ROOT . '/includes/language.inc'; 2588 foreach ($types as $type) { 2589 $GLOBALS[$type] = language_initialize($type); 2590 } 2591 // Allow modules to react on language system initialization in multilingual 2592 // environments. 2593 bootstrap_invoke_all('language_init'); 2594 } 2595 } 2596 2597 /** 2598 * Returns a list of the built-in language types. 2599 * 2600 * @return 2601 * An array of key-values pairs where the key is the language type and the 2602 * value is its configurability. 2603 */ 2604 function drupal_language_types() { 2605 return array( 2606 LANGUAGE_TYPE_INTERFACE => TRUE, 2607 LANGUAGE_TYPE_CONTENT => FALSE, 2608 LANGUAGE_TYPE_URL => FALSE, 2609 ); 2610 } 2611 2612 /** 2613 * Returns TRUE if there is more than one language enabled. 2614 * 2615 * @return 2616 * TRUE if more than one language is enabled. 2617 */ 2618 function drupal_multilingual() { 2619 // The "language_count" variable stores the number of enabled languages to 2620 // avoid unnecessarily querying the database when building the list of 2621 // enabled languages on monolingual sites. 2622 return variable_get('language_count', 1) > 1; 2623 } 2624 2625 /** 2626 * Returns an array of the available language types. 2627 * 2628 * @return 2629 * An array of all language types where the keys of each are the language type 2630 * name and its value is its configurability (TRUE/FALSE). 2631 */ 2632 function language_types() { 2633 return array_keys(variable_get('language_types', drupal_language_types())); 2634 } 2635 2636 /** 2637 * Returns a list of installed languages, indexed by the specified key. 2638 * 2639 * @param $field 2640 * (optional) The field to index the list with. 2641 * 2642 * @return 2643 * An associative array, keyed on the values of $field. 2644 * - If $field is 'weight' or 'enabled', the array is nested, with the outer 2645 * array's values each being associative arrays with language codes as 2646 * keys and language objects as values. 2647 * - For all other values of $field, the array is only one level deep, and 2648 * the array's values are language objects. 2649 */ 2650 function language_list($field = 'language') { 2651 $languages = &drupal_static(__FUNCTION__); 2652 // Init language list 2653 if (!isset($languages)) { 2654 if (drupal_multilingual() || module_exists('locale')) { 2655 $languages['language'] = db_query('SELECT * FROM {languages} ORDER BY weight ASC, name ASC')->fetchAllAssoc('language'); 2656 // Users cannot uninstall the native English language. However, we allow 2657 // it to be hidden from the installed languages. Therefore, at least one 2658 // other language must be enabled then. 2659 if (!$languages['language']['en']->enabled && !variable_get('language_native_enabled', TRUE)) { 2660 unset($languages['language']['en']); 2661 } 2662 } 2663 else { 2664 // No locale module, so use the default language only. 2665 $default = language_default(); 2666 $languages['language'][$default->language] = $default; 2667 } 2668 } 2669 2670 // Return the array indexed by the right field 2671 if (!isset($languages[$field])) { 2672 $languages[$field] = array(); 2673 foreach ($languages['language'] as $lang) { 2674 // Some values should be collected into an array 2675 if (in_array($field, array('enabled', 'weight'))) { 2676 $languages[$field][$lang->$field][$lang->language] = $lang; 2677 } 2678 else { 2679 $languages[$field][$lang->$field] = $lang; 2680 } 2681 } 2682 } 2683 return $languages[$field]; 2684 } 2685 2686 /** 2687 * Returns the default language used on the site 2688 * 2689 * @param $property 2690 * Optional property of the language object to return 2691 */ 2692 function language_default($property = NULL) { 2693 $language = variable_get('language_default', (object) array('language' => 'en', 'name' => 'English', 'native' => 'English', 'direction' => 0, 'enabled' => 1, 'plurals' => 0, 'formula' => '', 'domain' => '', 'prefix' => '', 'weight' => 0, 'javascript' => '')); 2694 return $property ? $language->$property : $language; 2695 } 2696 2697 /** 2698 * Returns the requested URL path of the page being viewed. 2699 * 2700 * Examples: 2701 * - http://example.com/node/306 returns "node/306". 2702 * - http://example.com/drupalfolder/node/306 returns "node/306" while 2703 * base_path() returns "/drupalfolder/". 2704 * - http://example.com/path/alias (which is a path alias for node/306) returns 2705 * "path/alias" as opposed to the internal path. 2706 * - http://example.com/index.php returns an empty string (meaning: front page). 2707 * - http://example.com/index.php?page=1 returns an empty string. 2708 * 2709 * @return 2710 * The requested Drupal URL path. 2711 * 2712 * @see current_path() 2713 */ 2714 function request_path() { 2715 static $path; 2716 2717 if (isset($path)) { 2718 return $path; 2719 } 2720 2721 if (isset($_GET['q']) && is_string($_GET['q'])) { 2722 // This is a request with a ?q=foo/bar query string. $_GET['q'] is 2723 // overwritten in drupal_path_initialize(), but request_path() is called 2724 // very early in the bootstrap process, so the original value is saved in 2725 // $path and returned in later calls. 2726 $path = $_GET['q']; 2727 } 2728 elseif (isset($_SERVER['REQUEST_URI'])) { 2729 // This request is either a clean URL, or 'index.php', or nonsense. 2730 // Extract the path from REQUEST_URI. 2731 $request_path = strtok($_SERVER['REQUEST_URI'], '?'); 2732 $base_path_len = strlen(rtrim(dirname($_SERVER['SCRIPT_NAME']), '\/')); 2733 // Unescape and strip $base_path prefix, leaving q without a leading slash. 2734 $path = substr(urldecode($request_path), $base_path_len + 1); 2735 // If the path equals the script filename, either because 'index.php' was 2736 // explicitly provided in the URL, or because the server added it to 2737 // $_SERVER['REQUEST_URI'] even when it wasn't provided in the URL (some 2738 // versions of Microsoft IIS do this), the front page should be served. 2739 if ($path == basename($_SERVER['PHP_SELF'])) { 2740 $path = ''; 2741 } 2742 } 2743 else { 2744 // This is the front page. 2745 $path = ''; 2746 } 2747 2748 // Under certain conditions Apache's RewriteRule directive prepends the value 2749 // assigned to $_GET['q'] with a slash. Moreover we can always have a trailing 2750 // slash in place, hence we need to normalize $_GET['q']. 2751 $path = trim($path, '/'); 2752 2753 return $path; 2754 } 2755 2756 /** 2757 * Returns a component of the current Drupal path. 2758 * 2759 * When viewing a page at the path "admin/structure/types", for example, arg(0) 2760 * returns "admin", arg(1) returns "structure", and arg(2) returns "types". 2761 * 2762 * Avoid use of this function where possible, as resulting code is hard to 2763 * read. In menu callback functions, attempt to use named arguments. See the 2764 * explanation in menu.inc for how to construct callbacks that take arguments. 2765 * When attempting to use this function to load an element from the current 2766 * path, e.g. loading the node on a node page, use menu_get_object() instead. 2767 * 2768 * @param $index 2769 * The index of the component, where each component is separated by a '/' 2770 * (forward-slash), and where the first component has an index of 0 (zero). 2771 * @param $path 2772 * A path to break into components. Defaults to the path of the current page. 2773 * 2774 * @return 2775 * The component specified by $index, or NULL if the specified component was 2776 * not found. If called without arguments, it returns an array containing all 2777 * the components of the current path. 2778 */ 2779 function arg($index = NULL, $path = NULL) { 2780 // Even though $arguments doesn't need to be resettable for any functional 2781 // reasons (the result of explode() does not depend on any run-time 2782 // information), it should be resettable anyway in case a module needs to 2783 // free up the memory used by it. 2784 // Use the advanced drupal_static() pattern, since this is called very often. 2785 static $drupal_static_fast; 2786 if (!isset($drupal_static_fast)) { 2787 $drupal_static_fast['arguments'] = &drupal_static(__FUNCTION__); 2788 } 2789 $arguments = &$drupal_static_fast['arguments']; 2790 2791 if (!isset($path)) { 2792 $path = $_GET['q']; 2793 } 2794 if (!isset($arguments[$path])) { 2795 $arguments[$path] = explode('/', $path); 2796 } 2797 if (!isset($index)) { 2798 return $arguments[$path]; 2799 } 2800 if (isset($arguments[$path][$index])) { 2801 return $arguments[$path][$index]; 2802 } 2803 } 2804 2805 /** 2806 * Returns the IP address of the client machine. 2807 * 2808 * If Drupal is behind a reverse proxy, we use the X-Forwarded-For header 2809 * instead of $_SERVER['REMOTE_ADDR'], which would be the IP address of 2810 * the proxy server, and not the client's. The actual header name can be 2811 * configured by the reverse_proxy_header variable. 2812 * 2813 * @return 2814 * IP address of client machine, adjusted for reverse proxy and/or cluster 2815 * environments. 2816 */ 2817 function ip_address() { 2818 $ip_address = &drupal_static(__FUNCTION__); 2819 2820 if (!isset($ip_address)) { 2821 $ip_address = $_SERVER['REMOTE_ADDR']; 2822 2823 if (variable_get('reverse_proxy', 0)) { 2824 $reverse_proxy_header = variable_get('reverse_proxy_header', 'HTTP_X_FORWARDED_FOR'); 2825 if (!empty($_SERVER[$reverse_proxy_header])) { 2826 // If an array of known reverse proxy IPs is provided, then trust 2827 // the XFF header if request really comes from one of them. 2828 $reverse_proxy_addresses = variable_get('reverse_proxy_addresses', array()); 2829 2830 // Turn XFF header into an array. 2831 $forwarded = explode(',', $_SERVER[$reverse_proxy_header]); 2832 2833 // Trim the forwarded IPs; they may have been delimited by commas and spaces. 2834 $forwarded = array_map('trim', $forwarded); 2835 2836 // Tack direct client IP onto end of forwarded array. 2837 $forwarded[] = $ip_address; 2838 2839 // Eliminate all trusted IPs. 2840 $untrusted = array_diff($forwarded, $reverse_proxy_addresses); 2841 2842 // The right-most IP is the most specific we can trust. 2843 $ip_address = array_pop($untrusted); 2844 } 2845 } 2846 } 2847 2848 return $ip_address; 2849 } 2850 2851 /** 2852 * @addtogroup schemaapi 2853 * @{ 2854 */ 2855 2856 /** 2857 * Gets the schema definition of a table, or the whole database schema. 2858 * 2859 * The returned schema will include any modifications made by any 2860 * module that implements hook_schema_alter(). 2861 * 2862 * @param $table 2863 * The name of the table. If not given, the schema of all tables is returned. 2864 * @param $rebuild 2865 * If true, the schema will be rebuilt instead of retrieved from the cache. 2866 */ 2867 function drupal_get_schema($table = NULL, $rebuild = FALSE) { 2868 static $schema; 2869 2870 if ($rebuild || !isset($table)) { 2871 $schema = drupal_get_complete_schema($rebuild); 2872 } 2873 elseif (!isset($schema)) { 2874 $schema = new SchemaCache(); 2875 } 2876 2877 if (!isset($table)) { 2878 return $schema; 2879 } 2880 if (isset($schema[$table])) { 2881 return $schema[$table]; 2882 } 2883 else { 2884 return FALSE; 2885 } 2886 } 2887 2888 /** 2889 * Extends DrupalCacheArray to allow for dynamic building of the schema cache. 2890 */ 2891 class SchemaCache extends DrupalCacheArray { 2892 2893 /** 2894 * Constructs a SchemaCache object. 2895 */ 2896 public function __construct() { 2897 // Cache by request method. 2898 parent::__construct('schema:runtime:' . ($_SERVER['REQUEST_METHOD'] == 'GET'), 'cache'); 2899 } 2900 2901 /** 2902 * Overrides DrupalCacheArray::resolveCacheMiss(). 2903 */ 2904 protected function resolveCacheMiss($offset) { 2905 $complete_schema = drupal_get_complete_schema(); 2906 $value = isset($complete_schema[$offset]) ? $complete_schema[$offset] : NULL; 2907 $this->storage[$offset] = $value; 2908 $this->persist($offset); 2909 return $value; 2910 } 2911 } 2912 2913 /** 2914 * Gets the whole database schema. 2915 * 2916 * The returned schema will include any modifications made by any 2917 * module that implements hook_schema_alter(). 2918 * 2919 * @param $rebuild 2920 * If true, the schema will be rebuilt instead of retrieved from the cache. 2921 */ 2922 function drupal_get_complete_schema($rebuild = FALSE) { 2923 static $schema = array(); 2924 2925 if (empty($schema) || $rebuild) { 2926 // Try to load the schema from cache. 2927 if (!$rebuild && $cached = cache_get('schema')) { 2928 $schema = $cached->data; 2929 } 2930 // Otherwise, rebuild the schema cache. 2931 else { 2932 $schema = array(); 2933 // Load the .install files to get hook_schema. 2934 // On some databases this function may be called before bootstrap has 2935 // been completed, so we force the functions we need to load just in case. 2936 if (function_exists('module_load_all_includes')) { 2937 // This function can be called very early in the bootstrap process, so 2938 // we force the module_list() cache to be refreshed to ensure that it 2939 // contains the complete list of modules before we go on to call 2940 // module_load_all_includes(). 2941 module_list(TRUE); 2942 module_load_all_includes('install'); 2943 } 2944 2945 require_once DRUPAL_ROOT . '/includes/common.inc'; 2946 // Invoke hook_schema for all modules. 2947 foreach (module_implements('schema') as $module) { 2948 // Cast the result of hook_schema() to an array, as a NULL return value 2949 // would cause array_merge() to set the $schema variable to NULL as well. 2950 // That would break modules which use $schema further down the line. 2951 $current = (array) module_invoke($module, 'schema'); 2952 // Set 'module' and 'name' keys for each table, and remove descriptions, 2953 // as they needlessly slow down cache_get() for every single request. 2954 _drupal_schema_initialize($current, $module); 2955 $schema = array_merge($schema, $current); 2956 } 2957 2958 drupal_alter('schema', $schema); 2959 // If the schema is empty, avoid saving it: some database engines require 2960 // the schema to perform queries, and this could lead to infinite loops. 2961 if (!empty($schema) && (drupal_get_bootstrap_phase() == DRUPAL_BOOTSTRAP_FULL)) { 2962 cache_set('schema', $schema); 2963 } 2964 if ($rebuild) { 2965 cache_clear_all('schema:', 'cache', TRUE); 2966 } 2967 } 2968 } 2969 2970 return $schema; 2971 } 2972 2973 /** 2974 * @} End of "addtogroup schemaapi". 2975 */ 2976 2977 2978 /** 2979 * @addtogroup registry 2980 * @{ 2981 */ 2982 2983 /** 2984 * Confirms that an interface is available. 2985 * 2986 * This function is rarely called directly. Instead, it is registered as an 2987 * spl_autoload() handler, and PHP calls it for us when necessary. 2988 * 2989 * @param $interface 2990 * The name of the interface to check or load. 2991 * 2992 * @return 2993 * TRUE if the interface is currently available, FALSE otherwise. 2994 */ 2995 function drupal_autoload_interface($interface) { 2996 return _registry_check_code('interface', $interface); 2997 } 2998 2999 /** 3000 * Confirms that a class is available. 3001 * 3002 * This function is rarely called directly. Instead, it is registered as an 3003 * spl_autoload() handler, and PHP calls it for us when necessary. 3004 * 3005 * @param $class 3006 * The name of the class to check or load. 3007 * 3008 * @return 3009 * TRUE if the class is currently available, FALSE otherwise. 3010 */ 3011 function drupal_autoload_class($class) { 3012 return _registry_check_code('class', $class); 3013 } 3014 3015 /** 3016 * Checks for a resource in the registry. 3017 * 3018 * @param $type 3019 * The type of resource we are looking up, or one of the constants 3020 * REGISTRY_RESET_LOOKUP_CACHE or REGISTRY_WRITE_LOOKUP_CACHE, which 3021 * signal that we should reset or write the cache, respectively. 3022 * @param $name 3023 * The name of the resource, or NULL if either of the REGISTRY_* constants 3024 * is passed in. 3025 * 3026 * @return 3027 * TRUE if the resource was found, FALSE if not. 3028 * NULL if either of the REGISTRY_* constants is passed in as $type. 3029 */ 3030 function _registry_check_code($type, $name = NULL) { 3031 static $lookup_cache, $cache_update_needed; 3032 3033 if ($type == 'class' && class_exists($name) || $type == 'interface' && interface_exists($name)) { 3034 return TRUE; 3035 } 3036 3037 if (!isset($lookup_cache)) { 3038 $lookup_cache = array(); 3039 if ($cache = cache_get('lookup_cache', 'cache_bootstrap')) { 3040 $lookup_cache = $cache->data; 3041 } 3042 } 3043 3044 // When we rebuild the registry, we need to reset this cache so 3045 // we don't keep lookups for resources that changed during the rebuild. 3046 if ($type == REGISTRY_RESET_LOOKUP_CACHE) { 3047 $cache_update_needed = TRUE; 3048 $lookup_cache = NULL; 3049 return; 3050 } 3051 3052 // Called from drupal_page_footer, we write to permanent storage if there 3053 // changes to the lookup cache for this request. 3054 if ($type == REGISTRY_WRITE_LOOKUP_CACHE) { 3055 if ($cache_update_needed) { 3056 cache_set('lookup_cache', $lookup_cache, 'cache_bootstrap'); 3057 } 3058 return; 3059 } 3060 3061 // $type is either 'interface' or 'class', so we only need the first letter to 3062 // keep the cache key unique. 3063 $cache_key = $type[0] . $name; 3064 if (isset($lookup_cache[$cache_key])) { 3065 if ($lookup_cache[$cache_key]) { 3066 require_once DRUPAL_ROOT . '/' . $lookup_cache[$cache_key]; 3067 } 3068 return (bool) $lookup_cache[$cache_key]; 3069 } 3070 3071 // This function may get called when the default database is not active, but 3072 // there is no reason we'd ever want to not use the default database for 3073 // this query. 3074 $file = Database::getConnection('default', 'default')->query("SELECT filename FROM {registry} WHERE name = :name AND type = :type", array( 3075 ':name' => $name, 3076 ':type' => $type, 3077 )) 3078 ->fetchField(); 3079 3080 // Flag that we've run a lookup query and need to update the cache. 3081 $cache_update_needed = TRUE; 3082 3083 // Misses are valuable information worth caching, so cache even if 3084 // $file is FALSE. 3085 $lookup_cache[$cache_key] = $file; 3086 3087 if ($file) { 3088 require_once DRUPAL_ROOT . '/' . $file; 3089 return TRUE; 3090 } 3091 else { 3092 return FALSE; 3093 } 3094 } 3095 3096 /** 3097 * Rescans all enabled modules and rebuilds the registry. 3098 * 3099 * Rescans all code in modules or includes directories, storing the location of 3100 * each interface or class in the database. 3101 */ 3102 function registry_rebuild() { 3103 system_rebuild_module_data(); 3104 registry_update(); 3105 } 3106 3107 /** 3108 * Updates the registry based on the latest files listed in the database. 3109 * 3110 * This function should be used when system_rebuild_module_data() does not need 3111 * to be called, because it is already known that the list of files in the 3112 * {system} table matches those in the file system. 3113 * 3114 * @return 3115 * TRUE if the registry was rebuilt, FALSE if another thread was rebuilding 3116 * in parallel and the current thread just waited for completion. 3117 * 3118 * @see registry_rebuild() 3119 */ 3120 function registry_update() { 3121 // install_system_module() calls module_enable() which calls into this 3122 // function during initial system installation, so the lock system is neither 3123 // loaded nor does its storage exist yet. 3124 $in_installer = drupal_installation_attempted(); 3125 if (!$in_installer && !lock_acquire(__FUNCTION__)) { 3126 // Another request got the lock, wait for it to finish. 3127 lock_wait(__FUNCTION__); 3128 return FALSE; 3129 } 3130 3131 require_once DRUPAL_ROOT . '/includes/registry.inc'; 3132 _registry_update(); 3133 3134 if (!$in_installer) { 3135 lock_release(__FUNCTION__); 3136 } 3137 return TRUE; 3138 } 3139 3140 /** 3141 * @} End of "addtogroup registry". 3142 */ 3143 3144 /** 3145 * Provides central static variable storage. 3146 * 3147 * All functions requiring a static variable to persist or cache data within 3148 * a single page request are encouraged to use this function unless it is 3149 * absolutely certain that the static variable will not need to be reset during 3150 * the page request. By centralizing static variable storage through this 3151 * function, other functions can rely on a consistent API for resetting any 3152 * other function's static variables. 3153 * 3154 * Example: 3155 * @code 3156 * function language_list($field = 'language') { 3157 * $languages = &drupal_static(__FUNCTION__); 3158 * if (!isset($languages)) { 3159 * // If this function is being called for the first time after a reset, 3160 * // query the database and execute any other code needed to retrieve 3161 * // information about the supported languages. 3162 * ... 3163 * } 3164 * if (!isset($languages[$field])) { 3165 * // If this function is being called for the first time for a particular 3166 * // index field, then execute code needed to index the information already 3167 * // available in $languages by the desired field. 3168 * ... 3169 * } 3170 * // Subsequent invocations of this function for a particular index field 3171 * // skip the above two code blocks and quickly return the already indexed 3172 * // information. 3173 * return $languages[$field]; 3174 * } 3175 * function locale_translate_overview_screen() { 3176 * // When building the content for the translations overview page, make 3177 * // sure to get completely fresh information about the supported languages. 3178 * drupal_static_reset('language_list'); 3179 * ... 3180 * } 3181 * @endcode 3182 * 3183 * In a few cases, a function can have certainty that there is no legitimate 3184 * use-case for resetting that function's static variable. This is rare, 3185 * because when writing a function, it's hard to forecast all the situations in 3186 * which it will be used. A guideline is that if a function's static variable 3187 * does not depend on any information outside of the function that might change 3188 * during a single page request, then it's ok to use the "static" keyword 3189 * instead of the drupal_static() function. 3190 * 3191 * Example: 3192 * @code 3193 * function actions_do(...) { 3194 * // $stack tracks the number of recursive calls. 3195 * static $stack; 3196 * $stack++; 3197 * if ($stack > variable_get('actions_max_stack', 35)) { 3198 * ... 3199 * return; 3200 * } 3201 * ... 3202 * $stack--; 3203 * } 3204 * @endcode 3205 * 3206 * In a few cases, a function needs a resettable static variable, but the 3207 * function is called many times (100+) during a single page request, so 3208 * every microsecond of execution time that can be removed from the function 3209 * counts. These functions can use a more cumbersome, but faster variant of 3210 * calling drupal_static(). It works by storing the reference returned by 3211 * drupal_static() in the calling function's own static variable, thereby 3212 * removing the need to call drupal_static() for each iteration of the function. 3213 * Conceptually, it replaces: 3214 * @code 3215 * $foo = &drupal_static(__FUNCTION__); 3216 * @endcode 3217 * with: 3218 * @code 3219 * // Unfortunately, this does not work. 3220 * static $foo = &drupal_static(__FUNCTION__); 3221 * @endcode 3222 * However, the above line of code does not work, because PHP only allows static 3223 * variables to be initializied by literal values, and does not allow static 3224 * variables to be assigned to references. 3225 * - http://php.net/manual/en/language.variables.scope.php#language.variables.scope.static 3226 * - http://php.net/manual/en/language.variables.scope.php#language.variables.scope.references 3227 * The example below shows the syntax needed to work around both limitations. 3228 * For benchmarks and more information, see http://drupal.org/node/619666. 3229 * 3230 * Example: 3231 * @code 3232 * function user_access($string, $account = NULL) { 3233 * // Use the advanced drupal_static() pattern, since this is called very often. 3234 * static $drupal_static_fast; 3235 * if (!isset($drupal_static_fast)) { 3236 * $drupal_static_fast['perm'] = &drupal_static(__FUNCTION__); 3237 * } 3238 * $perm = &$drupal_static_fast['perm']; 3239 * ... 3240 * } 3241 * @endcode 3242 * 3243 * @param $name 3244 * Globally unique name for the variable. For a function with only one static, 3245 * variable, the function name (e.g. via the PHP magic __FUNCTION__ constant) 3246 * is recommended. For a function with multiple static variables add a 3247 * distinguishing suffix to the function name for each one. 3248 * @param $default_value 3249 * Optional default value. 3250 * @param $reset 3251 * TRUE to reset a specific named variable, or all variables if $name is NULL. 3252 * Resetting every variable should only be used, for example, for running 3253 * unit tests with a clean environment. Should be used only though via 3254 * function drupal_static_reset() and the return value should not be used in 3255 * this case. 3256 * 3257 * @return 3258 * Returns a variable by reference. 3259 * 3260 * @see drupal_static_reset() 3261 */ 3262 function &drupal_static($name, $default_value = NULL, $reset = FALSE) { 3263 static $data = array(), $default = array(); 3264 // First check if dealing with a previously defined static variable. 3265 if (isset($data[$name]) || array_key_exists($name, $data)) { 3266 // Non-NULL $name and both $data[$name] and $default[$name] statics exist. 3267 if ($reset) { 3268 // Reset pre-existing static variable to its default value. 3269 $data[$name] = $default[$name]; 3270 } 3271 return $data[$name]; 3272 } 3273 // Neither $data[$name] nor $default[$name] static variables exist. 3274 if (isset($name)) { 3275 if ($reset) { 3276 // Reset was called before a default is set and yet a variable must be 3277 // returned. 3278 return $data; 3279 } 3280 // First call with new non-NULL $name. Initialize a new static variable. 3281 $default[$name] = $data[$name] = $default_value; 3282 return $data[$name]; 3283 } 3284 // Reset all: ($name == NULL). This needs to be done one at a time so that 3285 // references returned by earlier invocations of drupal_static() also get 3286 // reset. 3287 foreach ($default as $name => $value) { 3288 $data[$name] = $value; 3289 } 3290 // As the function returns a reference, the return should always be a 3291 // variable. 3292 return $data; 3293 } 3294 3295 /** 3296 * Resets one or all centrally stored static variable(s). 3297 * 3298 * @param $name 3299 * Name of the static variable to reset. Omit to reset all variables. 3300 */ 3301 function drupal_static_reset($name = NULL) { 3302 drupal_static($name, NULL, TRUE); 3303 } 3304 3305 /** 3306 * Detects whether the current script is running in a command-line environment. 3307 */ 3308 function drupal_is_cli() { 3309 return (!isset($_SERVER['SERVER_SOFTWARE']) && (php_sapi_name() == 'cli' || (is_numeric($_SERVER['argc']) && $_SERVER['argc'] > 0))); 3310 } 3311 3312 /** 3313 * Formats text for emphasized display in a placeholder inside a sentence. 3314 * 3315 * Used automatically by format_string(). 3316 * 3317 * @param $text 3318 * The text to format (plain-text). 3319 * 3320 * @return 3321 * The formatted text (html). 3322 */ 3323 function drupal_placeholder($text) { 3324 return '<em class="placeholder">' . check_plain($text) . '</em>'; 3325 } 3326 3327 /** 3328 * Registers a function for execution on shutdown. 3329 * 3330 * Wrapper for register_shutdown_function() that catches thrown exceptions to 3331 * avoid "Exception thrown without a stack frame in Unknown". 3332 * 3333 * @param $callback 3334 * The shutdown function to register. 3335 * @param ... 3336 * Additional arguments to pass to the shutdown function. 3337 * 3338 * @return 3339 * Array of shutdown functions to be executed. 3340 * 3341 * @see register_shutdown_function() 3342 * @ingroup php_wrappers 3343 */ 3344 function &drupal_register_shutdown_function($callback = NULL) { 3345 // We cannot use drupal_static() here because the static cache is reset during 3346 // batch processing, which breaks batch handling. 3347 static $callbacks = array(); 3348 3349 if (isset($callback)) { 3350 // Only register the internal shutdown function once. 3351 if (empty($callbacks)) { 3352 register_shutdown_function('_drupal_shutdown_function'); 3353 } 3354 $args = func_get_args(); 3355 array_shift($args); 3356 // Save callback and arguments 3357 $callbacks[] = array('callback' => $callback, 'arguments' => $args); 3358 } 3359 return $callbacks; 3360 } 3361 3362 /** 3363 * Executes registered shutdown functions. 3364 */ 3365 function _drupal_shutdown_function() { 3366 $callbacks = &drupal_register_shutdown_function(); 3367 3368 // Set the CWD to DRUPAL_ROOT as it is not guaranteed to be the same as it 3369 // was in the normal context of execution. 3370 chdir(DRUPAL_ROOT); 3371 3372 try { 3373 while (list($key, $callback) = each($callbacks)) { 3374 call_user_func_array($callback['callback'], $callback['arguments']); 3375 } 3376 } 3377 catch (Exception $exception) { 3378 // If we are displaying errors, then do so with no possibility of a further uncaught exception being thrown. 3379 require_once DRUPAL_ROOT . '/includes/errors.inc'; 3380 if (error_displayable()) { 3381 print '<h1>Uncaught exception thrown in shutdown function.</h1>'; 3382 print '<p>' . _drupal_render_exception_safe($exception) . '</p><hr />'; 3383 } 3384 } 3385 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
title