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