bootstrap.inc 41 KB

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