Mercurial > hg > cmmr2012-drupal-site
comparison core/includes/bootstrap.inc @ 0:c75dbcec494b
Initial commit from drush-created site
author | Chris Cannam |
---|---|
date | Thu, 05 Jul 2018 14:24:15 +0000 |
parents | |
children | a9cd425dd02b |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:c75dbcec494b |
---|---|
1 <?php | |
2 | |
3 /** | |
4 * @file | |
5 * Functions that need to be loaded on every Drupal request. | |
6 */ | |
7 | |
8 use Drupal\Component\Utility\Crypt; | |
9 use Drupal\Component\Utility\Html; | |
10 use Drupal\Component\Utility\SafeMarkup; | |
11 use Drupal\Component\Utility\Unicode; | |
12 use Drupal\Core\Config\BootstrapConfigStorageFactory; | |
13 use Drupal\Core\Logger\RfcLogLevel; | |
14 use Drupal\Core\Test\TestDatabase; | |
15 use Drupal\Core\Session\AccountInterface; | |
16 use Drupal\Core\Site\Settings; | |
17 use Drupal\Core\Utility\Error; | |
18 use Drupal\Core\StringTranslation\TranslatableMarkup; | |
19 | |
20 /** | |
21 * Minimum supported version of PHP. | |
22 * | |
23 * Drupal cannot be installed on versions of PHP older than this version. | |
24 * | |
25 * @todo Move this to an appropriate autoloadable class. See | |
26 * https://www.drupal.org/project/drupal/issues/2908079 | |
27 */ | |
28 const DRUPAL_MINIMUM_PHP = '5.5.9'; | |
29 | |
30 /** | |
31 * Minimum recommended version of PHP. | |
32 * | |
33 * Sites installing Drupal on PHP versions lower than this will see a warning | |
34 * message, but Drupal can still be installed. Used for (e.g.) PHP versions | |
35 * that have reached their EOL or will in the near future. | |
36 * | |
37 * @todo Move this to an appropriate autoloadable class. See | |
38 * https://www.drupal.org/project/drupal/issues/2908079 | |
39 */ | |
40 const DRUPAL_RECOMMENDED_PHP = '7.1'; | |
41 | |
42 /** | |
43 * Minimum recommended value of PHP memory_limit. | |
44 * | |
45 * 64M was chosen as a minimum requirement in order to allow for additional | |
46 * contributed modules to be installed prior to hitting the limit. However, | |
47 * 40M is the target for the Standard installation profile. | |
48 * | |
49 * @todo Move this to an appropriate autoloadable class. See | |
50 * https://www.drupal.org/project/drupal/issues/2908079 | |
51 */ | |
52 const DRUPAL_MINIMUM_PHP_MEMORY_LIMIT = '64M'; | |
53 | |
54 /** | |
55 * Error reporting level: display no errors. | |
56 */ | |
57 const ERROR_REPORTING_HIDE = 'hide'; | |
58 | |
59 /** | |
60 * Error reporting level: display errors and warnings. | |
61 */ | |
62 const ERROR_REPORTING_DISPLAY_SOME = 'some'; | |
63 | |
64 /** | |
65 * Error reporting level: display all messages. | |
66 */ | |
67 const ERROR_REPORTING_DISPLAY_ALL = 'all'; | |
68 | |
69 /** | |
70 * Error reporting level: display all messages, plus backtrace information. | |
71 */ | |
72 const ERROR_REPORTING_DISPLAY_VERBOSE = 'verbose'; | |
73 | |
74 /** | |
75 * Role ID for anonymous users; should match what's in the "role" table. | |
76 * | |
77 * @deprecated in Drupal 8.0.0, will be removed before Drupal 9.0.0. | |
78 * Use Drupal\Core\Session\AccountInterface::ANONYMOUS_ROLE or | |
79 * \Drupal\user\RoleInterface::ANONYMOUS_ID instead. | |
80 * | |
81 * @see https://www.drupal.org/node/1619504 | |
82 */ | |
83 const DRUPAL_ANONYMOUS_RID = AccountInterface::ANONYMOUS_ROLE; | |
84 | |
85 /** | |
86 * Role ID for authenticated users; should match what's in the "role" table. | |
87 * | |
88 * @deprecated in Drupal 8.0.0, will be removed before Drupal 9.0.0. | |
89 * Use Drupal\Core\Session\AccountInterface::AUTHENTICATED_ROLE or | |
90 * \Drupal\user\RoleInterface::AUTHENTICATED_ID instead. | |
91 * | |
92 * @see https://www.drupal.org/node/1619504 | |
93 */ | |
94 const DRUPAL_AUTHENTICATED_RID = AccountInterface::AUTHENTICATED_ROLE; | |
95 | |
96 /** | |
97 * The maximum number of characters in a module or theme name. | |
98 */ | |
99 const DRUPAL_EXTENSION_NAME_MAX_LENGTH = 50; | |
100 | |
101 /** | |
102 * Time of the current request in seconds elapsed since the Unix Epoch. | |
103 * | |
104 * This differs from $_SERVER['REQUEST_TIME'], which is stored as a float | |
105 * since PHP 5.4.0. Float timestamps confuse most PHP functions | |
106 * (including date_create()). | |
107 * | |
108 * @see http://php.net/manual/reserved.variables.server.php | |
109 * @see http://php.net/manual/function.time.php | |
110 * | |
111 * @deprecated in Drupal 8.3.0, will be removed before Drupal 9.0.0. | |
112 * Use \Drupal::time()->getRequestTime(); | |
113 * | |
114 * @see https://www.drupal.org/node/2785211 | |
115 */ | |
116 define('REQUEST_TIME', (int) $_SERVER['REQUEST_TIME']); | |
117 | |
118 /** | |
119 * Regular expression to match PHP function names. | |
120 * | |
121 * @see http://php.net/manual/language.functions.php | |
122 */ | |
123 const DRUPAL_PHP_FUNCTION_PATTERN = '[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*'; | |
124 | |
125 /** | |
126 * $config_directories key for active directory. | |
127 * | |
128 * @see config_get_config_directory() | |
129 * | |
130 * @deprecated in Drupal 8.0.x and will be removed before 9.0.0. Drupal core no | |
131 * longer creates an active directory. | |
132 * | |
133 * @see https://www.drupal.org/node/2501187 | |
134 */ | |
135 const CONFIG_ACTIVE_DIRECTORY = 'active'; | |
136 | |
137 /** | |
138 * $config_directories key for sync directory. | |
139 * | |
140 * @see config_get_config_directory() | |
141 */ | |
142 const CONFIG_SYNC_DIRECTORY = 'sync'; | |
143 | |
144 /** | |
145 * $config_directories key for staging directory. | |
146 * | |
147 * @see config_get_config_directory() | |
148 * @see CONFIG_SYNC_DIRECTORY | |
149 * | |
150 * @deprecated in Drupal 8.0.x and will be removed before 9.0.0. The staging | |
151 * directory was renamed to sync. | |
152 * | |
153 * @see https://www.drupal.org/node/2574957 | |
154 */ | |
155 const CONFIG_STAGING_DIRECTORY = 'staging'; | |
156 | |
157 /** | |
158 * Defines the root directory of the Drupal installation. | |
159 * | |
160 * This strips two levels of directories off the current directory. | |
161 */ | |
162 define('DRUPAL_ROOT', dirname(dirname(__DIR__))); | |
163 | |
164 /** | |
165 * Returns the path of a configuration directory. | |
166 * | |
167 * Configuration directories are configured using $config_directories in | |
168 * settings.php. | |
169 * | |
170 * @param string $type | |
171 * The type of config directory to return. Drupal core provides the | |
172 * CONFIG_SYNC_DIRECTORY constant to access the sync directory. | |
173 * | |
174 * @return string | |
175 * The configuration directory path. | |
176 * | |
177 * @throws \Exception | |
178 */ | |
179 function config_get_config_directory($type) { | |
180 global $config_directories; | |
181 | |
182 // @todo Remove fallback in Drupal 9. https://www.drupal.org/node/2574943 | |
183 if ($type == CONFIG_SYNC_DIRECTORY && !isset($config_directories[CONFIG_SYNC_DIRECTORY]) && isset($config_directories[CONFIG_STAGING_DIRECTORY])) { | |
184 $type = CONFIG_STAGING_DIRECTORY; | |
185 } | |
186 | |
187 if (!empty($config_directories[$type])) { | |
188 return $config_directories[$type]; | |
189 } | |
190 // @todo https://www.drupal.org/node/2696103 Throw a more specific exception. | |
191 throw new \Exception("The configuration directory type '$type' does not exist"); | |
192 } | |
193 | |
194 /** | |
195 * Returns and optionally sets the filename for a system resource. | |
196 * | |
197 * The filename, whether provided, cached, or retrieved from the database, is | |
198 * only returned if the file exists. | |
199 * | |
200 * This function plays a key role in allowing Drupal's resources (modules | |
201 * and themes) to be located in different places depending on a site's | |
202 * configuration. For example, a module 'foo' may legally be located | |
203 * in any of these three places: | |
204 * | |
205 * core/modules/foo/foo.info.yml | |
206 * modules/foo/foo.info.yml | |
207 * sites/example.com/modules/foo/foo.info.yml | |
208 * | |
209 * Calling drupal_get_filename('module', 'foo') will give you one of | |
210 * the above, depending on where the module is located. | |
211 * | |
212 * @param $type | |
213 * The type of the item; one of 'core', 'profile', 'module', 'theme', or | |
214 * 'theme_engine'. | |
215 * @param $name | |
216 * The name of the item for which the filename is requested. Ignored for | |
217 * $type 'core'. | |
218 * @param $filename | |
219 * The filename of the item if it is to be set explicitly rather | |
220 * than by consulting the database. | |
221 * | |
222 * @return string | |
223 * The filename of the requested item or NULL if the item is not found. | |
224 */ | |
225 function drupal_get_filename($type, $name, $filename = NULL) { | |
226 // The location of files will not change during the request, so do not use | |
227 // drupal_static(). | |
228 static $files = []; | |
229 | |
230 // Type 'core' only exists to simplify application-level logic; it always maps | |
231 // to the /core directory, whereas $name is ignored. It is only requested via | |
232 // drupal_get_path(). /core/core.info.yml does not exist, but is required | |
233 // since drupal_get_path() returns the dirname() of the returned pathname. | |
234 if ($type === 'core') { | |
235 return 'core/core.info.yml'; | |
236 } | |
237 | |
238 // Profiles are converted into modules in system_rebuild_module_data(). | |
239 // @todo Remove false-exposure of profiles as modules. | |
240 if ($type == 'profile') { | |
241 $type = 'module'; | |
242 } | |
243 if (!isset($files[$type])) { | |
244 $files[$type] = []; | |
245 } | |
246 | |
247 if (isset($filename)) { | |
248 $files[$type][$name] = $filename; | |
249 } | |
250 elseif (!isset($files[$type][$name])) { | |
251 // If the pathname of the requested extension is not known, try to retrieve | |
252 // the list of extension pathnames from various providers, checking faster | |
253 // providers first. | |
254 // Retrieve the current module list (derived from the service container). | |
255 if ($type == 'module' && \Drupal::hasService('module_handler')) { | |
256 foreach (\Drupal::moduleHandler()->getModuleList() as $module_name => $module) { | |
257 $files[$type][$module_name] = $module->getPathname(); | |
258 } | |
259 } | |
260 // If still unknown, retrieve the file list prepared in state by | |
261 // system_rebuild_module_data() and | |
262 // \Drupal\Core\Extension\ThemeHandlerInterface::rebuildThemeData(). | |
263 if (!isset($files[$type][$name]) && \Drupal::hasService('state')) { | |
264 $files[$type] += \Drupal::state()->get('system.' . $type . '.files', []); | |
265 } | |
266 // If still unknown, create a user-level error message. | |
267 if (!isset($files[$type][$name])) { | |
268 trigger_error(SafeMarkup::format('The following @type is missing from the file system: @name', ['@type' => $type, '@name' => $name]), E_USER_WARNING); | |
269 } | |
270 } | |
271 | |
272 if (isset($files[$type][$name])) { | |
273 return $files[$type][$name]; | |
274 } | |
275 } | |
276 | |
277 /** | |
278 * Returns the path to a system item (module, theme, etc.). | |
279 * | |
280 * @param $type | |
281 * The type of the item; one of 'core', 'profile', 'module', 'theme', or | |
282 * 'theme_engine'. | |
283 * @param $name | |
284 * The name of the item for which the path is requested. Ignored for | |
285 * $type 'core'. | |
286 * | |
287 * @return string | |
288 * The path to the requested item or an empty string if the item is not found. | |
289 */ | |
290 function drupal_get_path($type, $name) { | |
291 return dirname(drupal_get_filename($type, $name)); | |
292 } | |
293 | |
294 /** | |
295 * Translates a string to the current language or to a given language. | |
296 * | |
297 * In order for strings to be localized, make them available in one of the ways | |
298 * supported by the @link i18n Localization API. @endlink When possible, use | |
299 * the \Drupal\Core\StringTranslation\StringTranslationTrait $this->t(). | |
300 * Otherwise create a new \Drupal\Core\StringTranslation\TranslatableMarkup | |
301 * object directly. | |
302 * | |
303 * See \Drupal\Core\StringTranslation\TranslatableMarkup::__construct() for | |
304 * important security information and usage guidelines. | |
305 * | |
306 * @param string $string | |
307 * A string containing the English text to translate. | |
308 * @param array $args | |
309 * (optional) An associative array of replacements to make after translation. | |
310 * Based on the first character of the key, the value is escaped and/or | |
311 * themed. See | |
312 * \Drupal\Component\Render\FormattableMarkup::placeholderFormat() for | |
313 * details. | |
314 * @param array $options | |
315 * (optional) An associative array of additional options, with the following | |
316 * elements: | |
317 * - 'langcode' (defaults to the current language): A language code, to | |
318 * translate to a language other than what is used to display the page. | |
319 * - 'context' (defaults to the empty context): The context the source string | |
320 * belongs to. See the @link i18n Internationalization topic @endlink for | |
321 * more information about string contexts. | |
322 * | |
323 * @return \Drupal\Core\StringTranslation\TranslatableMarkup | |
324 * An object that, when cast to a string, returns the translated string. | |
325 * | |
326 * @see \Drupal\Component\Render\FormattableMarkup::placeholderFormat() | |
327 * @see \Drupal\Core\StringTranslation\StringTranslationTrait::t() | |
328 * @see \Drupal\Core\StringTranslation\TranslatableMarkup::__construct() | |
329 * | |
330 * @ingroup sanitization | |
331 */ | |
332 function t($string, array $args = [], array $options = []) { | |
333 return new TranslatableMarkup($string, $args, $options); | |
334 } | |
335 | |
336 /** | |
337 * Formats a string for HTML display by replacing variable placeholders. | |
338 * | |
339 * @see \Drupal\Component\Render\FormattableMarkup::placeholderFormat() | |
340 * @see \Drupal\Component\Render\FormattableMarkup | |
341 * @see t() | |
342 * @ingroup sanitization | |
343 * | |
344 * @deprecated in Drupal 8.0.0, will be removed before Drupal 9.0.0. | |
345 * Use \Drupal\Component\Render\FormattableMarkup. | |
346 * | |
347 * @see https://www.drupal.org/node/2302363 | |
348 */ | |
349 function format_string($string, array $args) { | |
350 return SafeMarkup::format($string, $args); | |
351 } | |
352 | |
353 /** | |
354 * Checks whether a string is valid UTF-8. | |
355 * | |
356 * All functions designed to filter input should use drupal_validate_utf8 | |
357 * to ensure they operate on valid UTF-8 strings to prevent bypass of the | |
358 * filter. | |
359 * | |
360 * When text containing an invalid UTF-8 lead byte (0xC0 - 0xFF) is presented | |
361 * as UTF-8 to Internet Explorer 6, the program may misinterpret subsequent | |
362 * bytes. When these subsequent bytes are HTML control characters such as | |
363 * quotes or angle brackets, parts of the text that were deemed safe by filters | |
364 * end up in locations that are potentially unsafe; An onerror attribute that | |
365 * is outside of a tag, and thus deemed safe by a filter, can be interpreted | |
366 * by the browser as if it were inside the tag. | |
367 * | |
368 * The function does not return FALSE for strings containing character codes | |
369 * above U+10FFFF, even though these are prohibited by RFC 3629. | |
370 * | |
371 * @param $text | |
372 * The text to check. | |
373 * | |
374 * @return bool | |
375 * TRUE if the text is valid UTF-8, FALSE if not. | |
376 * | |
377 * @see \Drupal\Component\Utility\Unicode::validateUtf8() | |
378 * | |
379 * @deprecated in Drupal 8.0.0, will be removed before Drupal 9.0.0. | |
380 * Use \Drupal\Component\Utility\Unicode::validateUtf8(). | |
381 * | |
382 * @see https://www.drupal.org/node/1992584 | |
383 */ | |
384 function drupal_validate_utf8($text) { | |
385 return Unicode::validateUtf8($text); | |
386 } | |
387 | |
388 /** | |
389 * Logs an exception. | |
390 * | |
391 * This is a wrapper logging function which automatically decodes an exception. | |
392 * | |
393 * @param $type | |
394 * The category to which this message belongs. | |
395 * @param $exception | |
396 * The exception that is going to be logged. | |
397 * @param $message | |
398 * The message to store in the log. If empty, a text that contains all useful | |
399 * information about the passed-in exception is used. | |
400 * @param $variables | |
401 * Array of variables to replace in the message on display or | |
402 * NULL if message is already translated or not possible to | |
403 * translate. | |
404 * @param $severity | |
405 * The severity of the message, as per RFC 3164. | |
406 * @param $link | |
407 * A link to associate with the message. | |
408 * | |
409 * @see \Drupal\Core\Utility\Error::decodeException() | |
410 */ | |
411 function watchdog_exception($type, Exception $exception, $message = NULL, $variables = [], $severity = RfcLogLevel::ERROR, $link = NULL) { | |
412 | |
413 // Use a default value if $message is not set. | |
414 if (empty($message)) { | |
415 $message = '%type: @message in %function (line %line of %file).'; | |
416 } | |
417 | |
418 if ($link) { | |
419 $variables['link'] = $link; | |
420 } | |
421 | |
422 $variables += Error::decodeException($exception); | |
423 | |
424 \Drupal::logger($type)->log($severity, $message, $variables); | |
425 } | |
426 | |
427 /** | |
428 * Sets a message to display to the user. | |
429 * | |
430 * Messages are stored in a session variable and displayed in the page template | |
431 * via the $messages theme variable. | |
432 * | |
433 * Example usage: | |
434 * @code | |
435 * drupal_set_message(t('An error occurred and processing did not complete.'), 'error'); | |
436 * @endcode | |
437 * | |
438 * @param string|\Drupal\Component\Render\MarkupInterface $message | |
439 * (optional) The translated message to be displayed to the user. For | |
440 * consistency with other messages, it should begin with a capital letter and | |
441 * end with a period. | |
442 * @param string $type | |
443 * (optional) The message's type. Defaults to 'status'. These values are | |
444 * supported: | |
445 * - 'status' | |
446 * - 'warning' | |
447 * - 'error' | |
448 * @param bool $repeat | |
449 * (optional) If this is FALSE and the message is already set, then the | |
450 * message won't be repeated. Defaults to FALSE. | |
451 * | |
452 * @return array|null | |
453 * A multidimensional array with keys corresponding to the set message types. | |
454 * The indexed array values of each contain the set messages for that type, | |
455 * and each message is an associative array with the following format: | |
456 * - safe: Boolean indicating whether the message string has been marked as | |
457 * safe. Non-safe strings will be escaped automatically. | |
458 * - message: The message string. | |
459 * So, the following is an example of the full return array structure: | |
460 * @code | |
461 * array( | |
462 * 'status' => array( | |
463 * array( | |
464 * 'safe' => TRUE, | |
465 * 'message' => 'A <em>safe</em> markup string.', | |
466 * ), | |
467 * array( | |
468 * 'safe' => FALSE, | |
469 * 'message' => "$arbitrary_user_input to escape.", | |
470 * ), | |
471 * ), | |
472 * ); | |
473 * @endcode | |
474 * If there are no messages set, the function returns NULL. | |
475 * | |
476 * @see drupal_get_messages() | |
477 * @see status-messages.html.twig | |
478 * @see https://www.drupal.org/node/2774931 | |
479 * | |
480 * @deprecated in Drupal 8.5.0 and will be removed before Drupal 9.0.0. | |
481 * Use \Drupal\Core\Messenger\MessengerInterface::addMessage() instead. | |
482 */ | |
483 function drupal_set_message($message = NULL, $type = 'status', $repeat = FALSE) { | |
484 @trigger_error('drupal_set_message() is deprecated in Drupal 8.5.0 and will be removed before Drupal 9.0.0. Use \Drupal\Core\Messenger\MessengerInterface::addMessage() instead. See https://www.drupal.org/node/2774931', E_USER_DEPRECATED); | |
485 $messenger = \Drupal::messenger(); | |
486 if (isset($message)) { | |
487 $messenger->addMessage($message, $type, $repeat); | |
488 } | |
489 return $messenger->all(); | |
490 } | |
491 | |
492 /** | |
493 * Returns all messages that have been set with drupal_set_message(). | |
494 * | |
495 * @param string $type | |
496 * (optional) Limit the messages returned by type. Defaults to NULL, meaning | |
497 * all types. These values are supported: | |
498 * - NULL | |
499 * - 'status' | |
500 * - 'warning' | |
501 * - 'error' | |
502 * @param bool $clear_queue | |
503 * (optional) If this is TRUE, the queue will be cleared of messages of the | |
504 * type specified in the $type parameter. Otherwise the queue will be left | |
505 * intact. Defaults to TRUE. | |
506 * | |
507 * @return array | |
508 * An associative, nested array of messages grouped by message type, with | |
509 * the top-level keys as the message type. The messages returned are | |
510 * limited to the type specified in the $type parameter, if any. If there | |
511 * are no messages of the specified type, an empty array is returned. See | |
512 * drupal_set_message() for the array structure of individual messages. | |
513 * | |
514 * @see drupal_set_message() | |
515 * @see status-messages.html.twig | |
516 * @see https://www.drupal.org/node/2774931 | |
517 * | |
518 * @deprecated in Drupal 8.5.0 and will be removed before Drupal 9.0.0. | |
519 * Use \Drupal\Core\Messenger\MessengerInterface::all() or | |
520 * \Drupal\Core\Messenger\MessengerInterface::messagesByType() instead. | |
521 */ | |
522 function drupal_get_messages($type = NULL, $clear_queue = TRUE) { | |
523 @trigger_error('drupal_get_message() is deprecated in Drupal 8.5.0 and will be removed before Drupal 9.0.0. Use \Drupal\Core\Messenger\MessengerInterface::all() or \Drupal\Core\Messenger\MessengerInterface::messagesByType() instead. See https://www.drupal.org/node/2774931', E_USER_DEPRECATED); | |
524 $messenger = \Drupal::messenger(); | |
525 if ($messages = $messenger->all()) { | |
526 if ($type) { | |
527 if ($clear_queue) { | |
528 $messenger->deleteByType($type); | |
529 } | |
530 if (isset($messages[$type])) { | |
531 return [$type => $messages[$type]]; | |
532 } | |
533 } | |
534 else { | |
535 if ($clear_queue) { | |
536 $messenger->deleteAll(); | |
537 } | |
538 return $messages; | |
539 } | |
540 } | |
541 return []; | |
542 } | |
543 | |
544 /** | |
545 * Returns the time zone of the current user. | |
546 * | |
547 * @return string | |
548 * The name of the current user's timezone or the name of the default timezone. | |
549 */ | |
550 function drupal_get_user_timezone() { | |
551 $user = \Drupal::currentUser(); | |
552 $config = \Drupal::config('system.date'); | |
553 | |
554 if ($user && $config->get('timezone.user.configurable') && $user->isAuthenticated() && $user->getTimezone()) { | |
555 return $user->getTimezone(); | |
556 } | |
557 else { | |
558 // Ignore PHP strict notice if time zone has not yet been set in the php.ini | |
559 // configuration. | |
560 $config_data_default_timezone = $config->get('timezone.default'); | |
561 return !empty($config_data_default_timezone) ? $config_data_default_timezone : @date_default_timezone_get(); | |
562 } | |
563 } | |
564 | |
565 /** | |
566 * Provides custom PHP error handling. | |
567 * | |
568 * @param $error_level | |
569 * The level of the error raised. | |
570 * @param $message | |
571 * The error message. | |
572 * @param $filename | |
573 * The filename that the error was raised in. | |
574 * @param $line | |
575 * The line number the error was raised at. | |
576 * @param $context | |
577 * An array that points to the active symbol table at the point the error | |
578 * occurred. | |
579 */ | |
580 function _drupal_error_handler($error_level, $message, $filename, $line, $context) { | |
581 require_once __DIR__ . '/errors.inc'; | |
582 _drupal_error_handler_real($error_level, $message, $filename, $line, $context); | |
583 } | |
584 | |
585 /** | |
586 * Provides custom PHP exception handling. | |
587 * | |
588 * Uncaught exceptions are those not enclosed in a try/catch block. They are | |
589 * always fatal: the execution of the script will stop as soon as the exception | |
590 * handler exits. | |
591 * | |
592 * @param \Exception|\Throwable $exception | |
593 * The exception object that was thrown. | |
594 */ | |
595 function _drupal_exception_handler($exception) { | |
596 require_once __DIR__ . '/errors.inc'; | |
597 | |
598 try { | |
599 // Log the message to the watchdog and return an error page to the user. | |
600 _drupal_log_error(Error::decodeException($exception), TRUE); | |
601 } | |
602 // PHP 7 introduces Throwable, which covers both Error and | |
603 // Exception throwables. | |
604 catch (\Throwable $error) { | |
605 _drupal_exception_handler_additional($exception, $error); | |
606 } | |
607 // In order to be compatible with PHP 5 we also catch regular Exceptions. | |
608 catch (\Exception $exception2) { | |
609 _drupal_exception_handler_additional($exception, $exception2); | |
610 } | |
611 } | |
612 | |
613 /** | |
614 * Displays any additional errors caught while handling an exception. | |
615 * | |
616 * @param \Exception|\Throwable $exception | |
617 * The first exception object that was thrown. | |
618 * @param \Exception|\Throwable $exception2 | |
619 * The second exception object that was thrown. | |
620 */ | |
621 function _drupal_exception_handler_additional($exception, $exception2) { | |
622 // Another uncaught exception was thrown while handling the first one. | |
623 // If we are displaying errors, then do so with no possibility of a further | |
624 // uncaught exception being thrown. | |
625 if (error_displayable()) { | |
626 print '<h1>Additional uncaught exception thrown while handling exception.</h1>'; | |
627 print '<h2>Original</h2><p>' . Error::renderExceptionSafe($exception) . '</p>'; | |
628 print '<h2>Additional</h2><p>' . Error::renderExceptionSafe($exception2) . '</p><hr />'; | |
629 } | |
630 } | |
631 | |
632 /** | |
633 * Returns the test prefix if this is an internal request from SimpleTest. | |
634 * | |
635 * @param string $new_prefix | |
636 * Internal use only. A new prefix to be stored. | |
637 * | |
638 * @return string|false | |
639 * Either the simpletest prefix (the string "simpletest" followed by any | |
640 * number of digits) or FALSE if the user agent does not contain a valid | |
641 * HMAC and timestamp. | |
642 */ | |
643 function drupal_valid_test_ua($new_prefix = NULL) { | |
644 static $test_prefix; | |
645 | |
646 if (isset($new_prefix)) { | |
647 $test_prefix = $new_prefix; | |
648 } | |
649 if (isset($test_prefix)) { | |
650 return $test_prefix; | |
651 } | |
652 // Unless the below User-Agent and HMAC validation succeeds, we are not in | |
653 // a test environment. | |
654 $test_prefix = FALSE; | |
655 | |
656 // A valid Simpletest request will contain a hashed and salted authentication | |
657 // code. Check if this code is present in a cookie or custom user agent | |
658 // string. | |
659 $http_user_agent = isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : NULL; | |
660 $user_agent = isset($_COOKIE['SIMPLETEST_USER_AGENT']) ? $_COOKIE['SIMPLETEST_USER_AGENT'] : $http_user_agent; | |
661 if (isset($user_agent) && preg_match("/^simple(\w+\d+):(.+):(.+):(.+)$/", $user_agent, $matches)) { | |
662 list(, $prefix, $time, $salt, $hmac) = $matches; | |
663 $check_string = $prefix . ':' . $time . ':' . $salt; | |
664 // Read the hash salt prepared by drupal_generate_test_ua(). | |
665 // This function is called before settings.php is read and Drupal's error | |
666 // handlers are set up. While Drupal's error handling may be properly | |
667 // configured on production sites, the server's PHP error_reporting may not. | |
668 // Ensure that no information leaks on production sites. | |
669 $test_db = new TestDatabase($prefix); | |
670 $key_file = DRUPAL_ROOT . '/' . $test_db->getTestSitePath() . '/.htkey'; | |
671 if (!is_readable($key_file)) { | |
672 header($_SERVER['SERVER_PROTOCOL'] . ' 403 Forbidden'); | |
673 exit; | |
674 } | |
675 $private_key = file_get_contents($key_file); | |
676 // The file properties add more entropy not easily accessible to others. | |
677 $key = $private_key . filectime(__FILE__) . fileinode(__FILE__); | |
678 $time_diff = REQUEST_TIME - $time; | |
679 $test_hmac = Crypt::hmacBase64($check_string, $key); | |
680 // Since we are making a local request a 600 second time window is allowed, | |
681 // and the HMAC must match. | |
682 if ($time_diff >= 0 && $time_diff <= 600 && $hmac === $test_hmac) { | |
683 $test_prefix = $prefix; | |
684 } | |
685 else { | |
686 header($_SERVER['SERVER_PROTOCOL'] . ' 403 Forbidden (SIMPLETEST_USER_AGENT invalid)'); | |
687 exit; | |
688 } | |
689 } | |
690 return $test_prefix; | |
691 } | |
692 | |
693 /** | |
694 * Generates a user agent string with a HMAC and timestamp for simpletest. | |
695 */ | |
696 function drupal_generate_test_ua($prefix) { | |
697 static $key, $last_prefix; | |
698 | |
699 if (!isset($key) || $last_prefix != $prefix) { | |
700 $last_prefix = $prefix; | |
701 $test_db = new TestDatabase($prefix); | |
702 $key_file = DRUPAL_ROOT . '/' . $test_db->getTestSitePath() . '/.htkey'; | |
703 // When issuing an outbound HTTP client request from within an inbound test | |
704 // request, then the outbound request has to use the same User-Agent header | |
705 // as the inbound request. A newly generated private key for the same test | |
706 // prefix would invalidate all subsequent inbound requests. | |
707 // @see \Drupal\Core\Http\Plugin\SimpletestHttpRequestSubscriber | |
708 if (DRUPAL_TEST_IN_CHILD_SITE && $parent_prefix = drupal_valid_test_ua()) { | |
709 if ($parent_prefix != $prefix) { | |
710 throw new \RuntimeException("Malformed User-Agent: Expected '$parent_prefix' but got '$prefix'."); | |
711 } | |
712 // If the file is not readable, a PHP warning is expected in this case. | |
713 $private_key = file_get_contents($key_file); | |
714 } | |
715 else { | |
716 // Generate and save a new hash salt for a test run. | |
717 // Consumed by drupal_valid_test_ua() before settings.php is loaded. | |
718 $private_key = Crypt::randomBytesBase64(55); | |
719 file_put_contents($key_file, $private_key); | |
720 } | |
721 // The file properties add more entropy not easily accessible to others. | |
722 $key = $private_key . filectime(__FILE__) . fileinode(__FILE__); | |
723 } | |
724 // Generate a moderately secure HMAC based on the database credentials. | |
725 $salt = uniqid('', TRUE); | |
726 $check_string = $prefix . ':' . time() . ':' . $salt; | |
727 return 'simple' . $check_string . ':' . Crypt::hmacBase64($check_string, $key); | |
728 } | |
729 | |
730 /** | |
731 * Enables use of the theme system without requiring database access. | |
732 * | |
733 * Loads and initializes the theme system for site installs, updates and when | |
734 * the site is in maintenance mode. This also applies when the database fails. | |
735 * | |
736 * @see _drupal_maintenance_theme() | |
737 */ | |
738 function drupal_maintenance_theme() { | |
739 require_once __DIR__ . '/theme.maintenance.inc'; | |
740 _drupal_maintenance_theme(); | |
741 } | |
742 | |
743 /** | |
744 * Returns TRUE if a Drupal installation is currently being attempted. | |
745 */ | |
746 function drupal_installation_attempted() { | |
747 // This cannot rely on the MAINTENANCE_MODE constant, since that would prevent | |
748 // tests from using the non-interactive installer, in which case Drupal | |
749 // only happens to be installed within the same request, but subsequently | |
750 // executed code does not involve the installer at all. | |
751 // @see install_drupal() | |
752 return isset($GLOBALS['install_state']) && empty($GLOBALS['install_state']['installation_finished']); | |
753 } | |
754 | |
755 /** | |
756 * Gets the name of the currently active installation profile. | |
757 * | |
758 * When this function is called during Drupal's initial installation process, | |
759 * the name of the profile that's about to be installed is stored in the global | |
760 * installation state. At all other times, the "install_profile" setting will be | |
761 * available in container as a parameter. | |
762 * | |
763 * @return string|null | |
764 * The name of the installation profile or NULL if no installation profile is | |
765 * currently active. This is the case for example during the first steps of | |
766 * the installer or during unit tests. | |
767 * | |
768 * @deprecated in Drupal 8.3.0, will be removed before Drupal 9.0.0. | |
769 * Use the install_profile container parameter or \Drupal::installProfile() | |
770 * instead. If you are accessing the value before it is written to | |
771 * configuration during the installer use the $install_state global. If you | |
772 * need to access the value before container is available you can use | |
773 * BootstrapConfigStorageFactory to load the value directly from | |
774 * configuration. | |
775 * | |
776 * @see https://www.drupal.org/node/2538996 | |
777 */ | |
778 function drupal_get_profile() { | |
779 global $install_state; | |
780 | |
781 if (drupal_installation_attempted()) { | |
782 // If the profile has been selected return it. | |
783 if (isset($install_state['parameters']['profile'])) { | |
784 $profile = $install_state['parameters']['profile']; | |
785 } | |
786 else { | |
787 $profile = NULL; | |
788 } | |
789 } | |
790 else { | |
791 if (\Drupal::hasContainer()) { | |
792 $profile = \Drupal::installProfile(); | |
793 } | |
794 else { | |
795 $profile = BootstrapConfigStorageFactory::getDatabaseStorage()->read('core.extension')['profile']; | |
796 } | |
797 | |
798 // A BC layer just in in case this only exists in Settings. Introduced in | |
799 // Drupal 8.3.x and will be removed before Drupal 9.0.0. | |
800 if (empty($profile)) { | |
801 $profile = Settings::get('install_profile'); | |
802 } | |
803 } | |
804 | |
805 return $profile; | |
806 } | |
807 | |
808 /** | |
809 * Registers an additional namespace. | |
810 * | |
811 * @param string $name | |
812 * The namespace component to register; e.g., 'node'. | |
813 * @param string $path | |
814 * The relative path to the Drupal component in the filesystem. | |
815 */ | |
816 function drupal_classloader_register($name, $path) { | |
817 $loader = \Drupal::service('class_loader'); | |
818 $loader->addPsr4('Drupal\\' . $name . '\\', \Drupal::root() . '/' . $path . '/src'); | |
819 } | |
820 | |
821 /** | |
822 * Provides central static variable storage. | |
823 * | |
824 * All functions requiring a static variable to persist or cache data within | |
825 * a single page request are encouraged to use this function unless it is | |
826 * absolutely certain that the static variable will not need to be reset during | |
827 * the page request. By centralizing static variable storage through this | |
828 * function, other functions can rely on a consistent API for resetting any | |
829 * other function's static variables. | |
830 * | |
831 * Example: | |
832 * @code | |
833 * function example_list($field = 'default') { | |
834 * $examples = &drupal_static(__FUNCTION__); | |
835 * if (!isset($examples)) { | |
836 * // If this function is being called for the first time after a reset, | |
837 * // query the database and execute any other code needed to retrieve | |
838 * // information. | |
839 * ... | |
840 * } | |
841 * if (!isset($examples[$field])) { | |
842 * // If this function is being called for the first time for a particular | |
843 * // index field, then execute code needed to index the information already | |
844 * // available in $examples by the desired field. | |
845 * ... | |
846 * } | |
847 * // Subsequent invocations of this function for a particular index field | |
848 * // skip the above two code blocks and quickly return the already indexed | |
849 * // information. | |
850 * return $examples[$field]; | |
851 * } | |
852 * function examples_admin_overview() { | |
853 * // When building the content for the overview page, make sure to get | |
854 * // completely fresh information. | |
855 * drupal_static_reset('example_list'); | |
856 * ... | |
857 * } | |
858 * @endcode | |
859 * | |
860 * In a few cases, a function can have certainty that there is no legitimate | |
861 * use-case for resetting that function's static variable. This is rare, | |
862 * because when writing a function, it's hard to forecast all the situations in | |
863 * which it will be used. A guideline is that if a function's static variable | |
864 * does not depend on any information outside of the function that might change | |
865 * during a single page request, then it's ok to use the "static" keyword | |
866 * instead of the drupal_static() function. | |
867 * | |
868 * Example: | |
869 * @code | |
870 * function mymodule_log_stream_handle($new_handle = NULL) { | |
871 * static $handle; | |
872 * if (isset($new_handle)) { | |
873 * $handle = $new_handle; | |
874 * } | |
875 * return $handle; | |
876 * } | |
877 * @endcode | |
878 * | |
879 * In a few cases, a function needs a resettable static variable, but the | |
880 * function is called many times (100+) during a single page request, so | |
881 * every microsecond of execution time that can be removed from the function | |
882 * counts. These functions can use a more cumbersome, but faster variant of | |
883 * calling drupal_static(). It works by storing the reference returned by | |
884 * drupal_static() in the calling function's own static variable, thereby | |
885 * removing the need to call drupal_static() for each iteration of the function. | |
886 * Conceptually, it replaces: | |
887 * @code | |
888 * $foo = &drupal_static(__FUNCTION__); | |
889 * @endcode | |
890 * with: | |
891 * @code | |
892 * // Unfortunately, this does not work. | |
893 * static $foo = &drupal_static(__FUNCTION__); | |
894 * @endcode | |
895 * However, the above line of code does not work, because PHP only allows static | |
896 * variables to be initialized by literal values, and does not allow static | |
897 * variables to be assigned to references. | |
898 * - http://php.net/manual/language.variables.scope.php#language.variables.scope.static | |
899 * - http://php.net/manual/language.variables.scope.php#language.variables.scope.references | |
900 * The example below shows the syntax needed to work around both limitations. | |
901 * For benchmarks and more information, see https://www.drupal.org/node/619666. | |
902 * | |
903 * Example: | |
904 * @code | |
905 * function example_default_format_type() { | |
906 * // Use the advanced drupal_static() pattern, since this is called very often. | |
907 * static $drupal_static_fast; | |
908 * if (!isset($drupal_static_fast)) { | |
909 * $drupal_static_fast['format_type'] = &drupal_static(__FUNCTION__); | |
910 * } | |
911 * $format_type = &$drupal_static_fast['format_type']; | |
912 * ... | |
913 * } | |
914 * @endcode | |
915 * | |
916 * @param $name | |
917 * Globally unique name for the variable. For a function with only one static, | |
918 * variable, the function name (e.g. via the PHP magic __FUNCTION__ constant) | |
919 * is recommended. For a function with multiple static variables add a | |
920 * distinguishing suffix to the function name for each one. | |
921 * @param $default_value | |
922 * Optional default value. | |
923 * @param $reset | |
924 * TRUE to reset one or all variables(s). This parameter is only used | |
925 * internally and should not be passed in; use drupal_static_reset() instead. | |
926 * (This function's return value should not be used when TRUE is passed in.) | |
927 * | |
928 * @return array | |
929 * Returns a variable by reference. | |
930 * | |
931 * @see drupal_static_reset() | |
932 */ | |
933 function &drupal_static($name, $default_value = NULL, $reset = FALSE) { | |
934 static $data = [], $default = []; | |
935 // First check if dealing with a previously defined static variable. | |
936 if (isset($data[$name]) || array_key_exists($name, $data)) { | |
937 // Non-NULL $name and both $data[$name] and $default[$name] statics exist. | |
938 if ($reset) { | |
939 // Reset pre-existing static variable to its default value. | |
940 $data[$name] = $default[$name]; | |
941 } | |
942 return $data[$name]; | |
943 } | |
944 // Neither $data[$name] nor $default[$name] static variables exist. | |
945 if (isset($name)) { | |
946 if ($reset) { | |
947 // Reset was called before a default is set and yet a variable must be | |
948 // returned. | |
949 return $data; | |
950 } | |
951 // First call with new non-NULL $name. Initialize a new static variable. | |
952 $default[$name] = $data[$name] = $default_value; | |
953 return $data[$name]; | |
954 } | |
955 // Reset all: ($name == NULL). This needs to be done one at a time so that | |
956 // references returned by earlier invocations of drupal_static() also get | |
957 // reset. | |
958 foreach ($default as $name => $value) { | |
959 $data[$name] = $value; | |
960 } | |
961 // As the function returns a reference, the return should always be a | |
962 // variable. | |
963 return $data; | |
964 } | |
965 | |
966 /** | |
967 * Resets one or all centrally stored static variable(s). | |
968 * | |
969 * @param $name | |
970 * Name of the static variable to reset. Omit to reset all variables. | |
971 * Resetting all variables should only be used, for example, for running | |
972 * unit tests with a clean environment. | |
973 */ | |
974 function drupal_static_reset($name = NULL) { | |
975 drupal_static($name, NULL, TRUE); | |
976 } | |
977 | |
978 /** | |
979 * Formats text for emphasized display in a placeholder inside a sentence. | |
980 * | |
981 * @deprecated in Drupal 8.0.0, will be removed before Drupal 9.0.0. Use | |
982 * \Drupal\Component\Utility\SafeMarkup::format() or Twig's "placeholder" | |
983 * filter instead. Note this method should not be used to simply emphasize a | |
984 * string and therefore has few valid use-cases. Note also, that this method | |
985 * does not mark the string as safe. | |
986 * | |
987 * @see https://www.drupal.org/node/2302363 | |
988 */ | |
989 function drupal_placeholder($text) { | |
990 return '<em class="placeholder">' . Html::escape($text) . '</em>'; | |
991 } | |
992 | |
993 /** | |
994 * Registers a function for execution on shutdown. | |
995 * | |
996 * Wrapper for register_shutdown_function() that catches thrown exceptions to | |
997 * avoid "Exception thrown without a stack frame in Unknown". | |
998 * | |
999 * @param callable $callback | |
1000 * The shutdown function to register. | |
1001 * @param ... | |
1002 * Additional arguments to pass to the shutdown function. | |
1003 * | |
1004 * @return array | |
1005 * Array of shutdown functions to be executed. | |
1006 * | |
1007 * @see register_shutdown_function() | |
1008 * @ingroup php_wrappers | |
1009 */ | |
1010 function &drupal_register_shutdown_function($callback = NULL) { | |
1011 // We cannot use drupal_static() here because the static cache is reset during | |
1012 // batch processing, which breaks batch handling. | |
1013 static $callbacks = []; | |
1014 | |
1015 if (isset($callback)) { | |
1016 // Only register the internal shutdown function once. | |
1017 if (empty($callbacks)) { | |
1018 register_shutdown_function('_drupal_shutdown_function'); | |
1019 } | |
1020 $args = func_get_args(); | |
1021 // Remove $callback from the arguments. | |
1022 unset($args[0]); | |
1023 // Save callback and arguments | |
1024 $callbacks[] = ['callback' => $callback, 'arguments' => $args]; | |
1025 } | |
1026 return $callbacks; | |
1027 } | |
1028 | |
1029 /** | |
1030 * Executes registered shutdown functions. | |
1031 */ | |
1032 function _drupal_shutdown_function() { | |
1033 $callbacks = &drupal_register_shutdown_function(); | |
1034 | |
1035 // Set the CWD to DRUPAL_ROOT as it is not guaranteed to be the same as it | |
1036 // was in the normal context of execution. | |
1037 chdir(DRUPAL_ROOT); | |
1038 | |
1039 try { | |
1040 foreach ($callbacks as &$callback) { | |
1041 call_user_func_array($callback['callback'], $callback['arguments']); | |
1042 } | |
1043 } | |
1044 // PHP 7 introduces Throwable, which covers both Error and | |
1045 // Exception throwables. | |
1046 catch (\Throwable $error) { | |
1047 _drupal_shutdown_function_handle_exception($error); | |
1048 } | |
1049 // In order to be compatible with PHP 5 we also catch regular Exceptions. | |
1050 catch (\Exception $exception) { | |
1051 _drupal_shutdown_function_handle_exception($exception); | |
1052 } | |
1053 } | |
1054 | |
1055 /** | |
1056 * Displays and logs any errors that may happen during shutdown. | |
1057 * | |
1058 * @param \Exception|\Throwable $exception | |
1059 * The exception object that was thrown. | |
1060 * | |
1061 * @see _drupal_shutdown_function() | |
1062 */ | |
1063 function _drupal_shutdown_function_handle_exception($exception) { | |
1064 // If using PHP-FPM then fastcgi_finish_request() will have been fired | |
1065 // preventing further output to the browser. | |
1066 if (!function_exists('fastcgi_finish_request')) { | |
1067 // If we are displaying errors, then do so with no possibility of a | |
1068 // further uncaught exception being thrown. | |
1069 require_once __DIR__ . '/errors.inc'; | |
1070 if (error_displayable()) { | |
1071 print '<h1>Uncaught exception thrown in shutdown function.</h1>'; | |
1072 print '<p>' . Error::renderExceptionSafe($exception) . '</p><hr />'; | |
1073 } | |
1074 } | |
1075 error_log($exception); | |
1076 } |