diff --git a/core/lib/Drupal/Component/PhpStorage/FileReadOnlyStorage.php b/core/lib/Drupal/Component/PhpStorage/FileReadOnlyStorage.php index 2a9bbb9..278a823 100644 --- a/core/lib/Drupal/Component/PhpStorage/FileReadOnlyStorage.php +++ b/core/lib/Drupal/Component/PhpStorage/FileReadOnlyStorage.php @@ -36,7 +36,7 @@ public function __construct(array $configuration) { /** * {@inheritdoc} */ - public function load($name, callable $success = NULL, $wait = 200000, $retry = 5) { + public function load($name, callable $success = NULL, $force = FALSE, $wait = 200000, $retry = 5) { // The FALSE returned on failure is enough for the caller to handle this, // we do not want a warning too. Also, no race conditions in read only // storage so ignore all those parameters. @@ -46,7 +46,7 @@ public function load($name, callable $success = NULL, $wait = 200000, $retry = 5 /** * {@inheritdoc} */ - public function loadClass($name, $class_name, $wait = 200000, $retry = 5) { + public function loadClass($name, $class_name, $force = FALSE, $wait = 200000, $retry = 5) { // No race conditions in read only storage, huzzah! return $this->load($name); } diff --git a/core/lib/Drupal/Component/PhpStorage/FileStorage.php b/core/lib/Drupal/Component/PhpStorage/FileStorage.php index 87f5c18..bef7a05 100644 --- a/core/lib/Drupal/Component/PhpStorage/FileStorage.php +++ b/core/lib/Drupal/Component/PhpStorage/FileStorage.php @@ -35,9 +35,14 @@ public function __construct(array $configuration) { /** * {@inheritdoc} */ - public function load($name, callable $success = NULL, $wait = 200000, $retry = 5) { + public function load($name, callable $success = NULL, $force = FALSE, $wait = 200000, $retry = 5) { if (!isset($success)) { - return $this->doIncludeOnce($name); + // Without a success callback, the only way to be safe is to include the + // file once. + return $force ? $this->doInclude($name) : $this->doIncludeOnce($name); + } + if (!$force && $success()) { + return TRUE; } // If the success callback fails after loading the file a race condition is // very likely so wait some random time allowing the racing process to @@ -53,7 +58,7 @@ public function load($name, callable $success = NULL, $wait = 200000, $retry = 5 /** * {@inheritdoc} */ - public function loadClass($name, $class_name = NULL, $wait = 200000, $retry = 5) { + public function loadClass($name, $class_name = NULL, $force = FALSE, $wait = 200000, $retry = 5) { // The FALSE returned on failure is enough for the caller to handle this, // we do not want a warning too. return $this->load($name, function () use($class_name) { return class_exists($class_name, FALSE); }, $wait, $retry); diff --git a/core/lib/Drupal/Component/PhpStorage/PhpStorageInterface.php b/core/lib/Drupal/Component/PhpStorage/PhpStorageInterface.php index fadc7ad..587c56d 100644 --- a/core/lib/Drupal/Component/PhpStorage/PhpStorageInterface.php +++ b/core/lib/Drupal/Component/PhpStorage/PhpStorageInterface.php @@ -24,12 +24,21 @@ * This function may be called for a file that doesn't exist, and that should * not result in errors. * + * It is safe to call this function multiple times: the relevant code will + * only be loaded once. + * * @param string $name * The virtual file name. Can be a relative path. * @param callable $success * A race condition might cause the file to exist but be empty. If this * parameter is specified the system will retry loading if the callback - * return FALSE to allow the racing process to finish. + * return FALSE to allow the racing process to finish. It is strongly + * recommended to supply this argument or use loadClass() instead to load + * a class. Otherwise, if the same file is written by anotehr process, + * a false positive might occur. + * @param bool $force + * Override the safety guarantee and force loading. For example, the caller + * might've already determined the code does not exist before calling. * @param int $wait * Wait a random amount of time between $wait and 2 * $wait microseconds * between retries. @@ -39,21 +48,26 @@ * @return * TRUE on success, FALSE otherwise. */ - public function load($name, callable $success = NULL, $wait = 200000, $retry = 5); + public function load($name, callable $success = NULL, $force = FALSE, $wait = 200000, $retry = 5); /** * Loads a class from storage. * - * Depending on storage implementation, exists() checks can be expensive, so - * this function may be called for a file that doesn't exist, and that should + * This function may be called for a file that doesn't exist, and that should * not result in errors. * + * It is safe to call this function multiple times: the class will only be + * loaded once. + * * @param string $name * The virtual file name. Can be a relative path. * @param string $class_name * A race condition might cause the file to exist but be empty. The system * will retry loading if the class does not exist to allow the racing * process to finish. + * @param bool $force + * Override the safety guarantee and force loading. For example, the caller + * might've already determined the class does not exist before calling. * @param int $wait * Wait a random amount of time between $wait and 2 * $wait microseconds * between retries. @@ -63,7 +77,7 @@ public function load($name, callable $success = NULL, $wait = 200000, $retry = 5 * @return * TRUE on success, FALSE otherwise. */ - public function loadClass($name, $class_name, $wait = 200000, $retry = 5); + public function loadClass($name, $class_name, $force = FALSE, $wait = 200000, $retry = 5); /** * Saves PHP code to storage. diff --git a/core/lib/Drupal/Core/DrupalKernel.php b/core/lib/Drupal/Core/DrupalKernel.php index 755950c..03c6a7f 100644 --- a/core/lib/Drupal/Core/DrupalKernel.php +++ b/core/lib/Drupal/Core/DrupalKernel.php @@ -746,9 +746,7 @@ protected function initializeContainer($rebuild = FALSE) { $fully_qualified_class_name = '\\' . $this->getClassNamespace() . '\\' . $class_name; // First, try to load from storage. - if (!class_exists($fully_qualified_class_name, FALSE)) { - $this->storage()->loadClass($class_name . '.php', $fully_qualified_class_name); - } + $this->storage()->loadClass($class_name . '.php', $fully_qualified_class_name); // If the load succeeded or the class already existed, use it. if (class_exists($fully_qualified_class_name, FALSE)) { $container = new $fully_qualified_class_name; diff --git a/core/lib/Drupal/Core/Template/TwigEnvironment.php b/core/lib/Drupal/Core/Template/TwigEnvironment.php index 3807888..c30ca2e 100644 --- a/core/lib/Drupal/Core/Template/TwigEnvironment.php +++ b/core/lib/Drupal/Core/Template/TwigEnvironment.php @@ -127,9 +127,9 @@ public function loadTemplate($name, $index = NULL) { $this->updateCompiledTemplate($cache_filename, $name); } - if (!$this->storage()->loadClass($cache_filename, $cls)) { + if (!$this->storage()->loadClass($cache_filename, $cls, TRUE)) { $this->updateCompiledTemplate($cache_filename, $name); - $this->storage()->loadClass($cache_filename, $cls); + $this->storage()->loadClass($cache_filename, $cls, TRUE); } } }