diff --git a/core/includes/utility.inc b/core/includes/utility.inc index 290821c..ff65dc8 100644 --- a/core/includes/utility.inc +++ b/core/includes/utility.inc @@ -15,14 +15,14 @@ /** * Rebuilds all caches even when Drupal itself does not work. * - * @param \Composer\Autoload\ClassLoader $classloader - * The classloader. + * @param \Composer\Autoload\ClassLoader $class_loader + * The class loader. * @param \Symfony\Component\HttpFoundation\Request $request * The current request. * * @see rebuild.php */ -function drupal_rebuild(ClassLoader $classloader, Request $request) { +function drupal_rebuild(ClassLoader $class_loader, Request $request) { // Remove Drupal's error and exception handlers; they rely on a working // service container and other subsystems and will only cause a fatal error // that hides the actual error. @@ -34,7 +34,7 @@ function drupal_rebuild(ClassLoader $classloader, Request $request) { PhpStorageFactory::get('twig')->deleteAll(); // Bootstrap up to where caches exist and clear them. - $kernel = new DrupalKernel('prod', $classloader); + $kernel = new DrupalKernel('prod', $class_loader); $kernel->setSitePath(DrupalKernel::findSitePath($request)); $kernel->prepareLegacyRequest($request); diff --git a/core/lib/Drupal/Core/Cache/ApcuBackend.php b/core/lib/Drupal/Core/Cache/ApcuBackend.php index 9bac79e..acaa1bb 100644 --- a/core/lib/Drupal/Core/Cache/ApcuBackend.php +++ b/core/lib/Drupal/Core/Cache/ApcuBackend.php @@ -8,7 +8,7 @@ namespace Drupal\Core\Cache; /** - * Stores cache items in the Alternative PHP Cache User Cache (APCu). + * Stores cache items in the Alternative PHP Cache User Cache (APC). */ class ApcuBackend implements CacheBackendInterface { @@ -66,7 +66,7 @@ public function __construct($bin, $site_prefix, CacheTagsChecksumInterface $chec * The cache item ID to prefix. * * @return string - * The APCu key for the cache item ID. + * The APC key for the cache item ID. */ protected function getApcuKey($cid) { return $this->binPrefix . $cid; @@ -84,7 +84,7 @@ public function get($cid, $allow_invalid = FALSE) { * {@inheritdoc} */ public function getMultiple(&$cids, $allow_invalid = FALSE) { - // Translate the requested cache item IDs to APCu keys. + // Translate the requested cache item IDs to APC keys. $map = array(); foreach ($cids as $cid) { $map[$this->getApcuKey($cid)] = $cid; @@ -109,7 +109,7 @@ public function getMultiple(&$cids, $allow_invalid = FALSE) { /** * Returns all cached items, optionally limited by a cache ID prefix. * - * APCu is a memory cache, shared across all server processes. To prevent + * APC is a memory cache, shared across all server processes. To prevent * cache item clashes with other applications/installations, every cache item * is prefixed with a unique string for this site. Therefore, functions like * apc_clear_cache() cannot be used, and instead, a list of all cache items diff --git a/core/lib/Drupal/Core/Cache/ChainedFastBackend.php b/core/lib/Drupal/Core/Cache/ChainedFastBackend.php index ce5ddd2..1ff5c2c 100644 --- a/core/lib/Drupal/Core/Cache/ChainedFastBackend.php +++ b/core/lib/Drupal/Core/Cache/ChainedFastBackend.php @@ -12,7 +12,7 @@ * * In order to mitigate a network roundtrip for each cache get operation, this * cache allows a fast backend to be put in front of a slow(er) backend. - * Typically the fast backend will be something like APCu, and be bound to a + * Typically the fast backend will be something like APC, and be bound to a * single web node, and will not require a network round trip to fetch a cache * item. The fast backend will also typically be inconsistent (will only see * changes from one web node). The slower backend will be something like Mysql, @@ -21,7 +21,7 @@ * * In addition to being useful for sites running on multiple web nodes, this * backend can also be useful for sites running on a single web node where the - * fast backend (e.g., APCu) isn't shareable between the web and CLI processes. + * fast backend (e.g., APC) isn't shareable between the web and CLI processes. * Single-node configurations that don't have that limitation can just use the * fast cache backend directly. * diff --git a/core/lib/Drupal/Core/Cache/ChainedFastBackendFactory.php b/core/lib/Drupal/Core/Cache/ChainedFastBackendFactory.php index bba9d94..28c08dd 100644 --- a/core/lib/Drupal/Core/Cache/ChainedFastBackendFactory.php +++ b/core/lib/Drupal/Core/Cache/ChainedFastBackendFactory.php @@ -42,8 +42,8 @@ class ChainedFastBackendFactory implements CacheFactoryInterface { * - 'cache.backend.database' (if the above isn't specified) * @param string|NULL $fast_service_name * (optional) The service name of the fast backend factory. Defaults to: - * - 'cache.backend.apcu' (if the PHP process has APCu enabled) - * - NULL (if the PHP process doesn't have APCu enabled) + * - 'cache.backend.apcu' (if the PHP process has APC enabled) + * - NULL (if the PHP process doesn't have APC enabled) */ public function __construct(Settings $settings = NULL, $consistent_service_name = NULL, $fast_service_name = NULL) { // Default the consistent backend to the site's default backend. @@ -52,7 +52,7 @@ public function __construct(Settings $settings = NULL, $consistent_service_name $consistent_service_name = isset($cache_settings['default']) ? $cache_settings['default'] : 'cache.backend.database'; } - // Default the fast backend to APCu if it's available. + // Default the fast backend to APC if it's available. if (!isset($fast_service_name) && function_exists('apc_fetch')) { $fast_service_name = 'cache.backend.apcu'; } diff --git a/core/lib/Drupal/Core/DrupalKernel.php b/core/lib/Drupal/Core/DrupalKernel.php index be9821d..8592402 100644 --- a/core/lib/Drupal/Core/DrupalKernel.php +++ b/core/lib/Drupal/Core/DrupalKernel.php @@ -108,7 +108,7 @@ class DrupalKernel implements DrupalKernelInterface, TerminableInterface { protected $storage; /** - * The classloader object. + * The class loader object. * * @var \Composer\Autoload\ClassLoader */ @@ -217,6 +217,7 @@ public static function createFromRequest(Request $request, $class_loader, $envir // Include our bootstrap file. $core_root = dirname(dirname(dirname(__DIR__))); require_once $core_root . '/includes/bootstrap.inc'; + $class_loader_class = get_class($class_loader); $kernel = new static($environment, $class_loader, $allow_dumping); @@ -245,6 +246,19 @@ public static function createFromRequest(Request $request, $class_loader, $envir $response->prepare($request)->send(); } + // If the class loader is still the same, possibly upgrade to the APC class + // loader. + if ($class_loader_class == get_class($class_loader) + && Settings::get('class_loader_auto_detect', TRUE) + && Settings::get('hash_salt', FALSE) + && function_exists('apc_fetch')) { + $prefix = 'drupal.' . hash('sha256', 'drupal.' . Settings::getHashSalt()); + $apc_loader = new \Symfony\Component\ClassLoader\ApcClassLoader($prefix, $class_loader); + $class_loader->unregister(); + $apc_loader->register(); + $class_loader = $apc_loader; + } + return $kernel; } diff --git a/core/modules/system/src/Tests/Cache/ApcuBackendUnitTest.php b/core/modules/system/src/Tests/Cache/ApcuBackendUnitTest.php index 20fb3a4..4edc25c 100644 --- a/core/modules/system/src/Tests/Cache/ApcuBackendUnitTest.php +++ b/core/modules/system/src/Tests/Cache/ApcuBackendUnitTest.php @@ -10,7 +10,7 @@ use Drupal\Core\Cache\ApcuBackend; /** - * Tests the APCu cache backend. + * Tests the APC cache backend. * * @group Cache * @requires extension apc diff --git a/core/modules/system/src/Tests/DrupalKernel/DrupalKernelTest.php b/core/modules/system/src/Tests/DrupalKernel/DrupalKernelTest.php index 03e56fc..070e990 100644 --- a/core/modules/system/src/Tests/DrupalKernel/DrupalKernelTest.php +++ b/core/modules/system/src/Tests/DrupalKernel/DrupalKernelTest.php @@ -122,9 +122,9 @@ public function testCompileDIC() { $this->assertEqual(array_values($modules_enabled), $module_list); // Test that our synthetic services are there. - $classloader = $container->get('class_loader'); - $refClass = new \ReflectionClass($classloader); - $this->assertTrue($refClass->hasMethod('loadClass'), 'Container has a classloader'); + $class_loader = $container->get('class_loader'); + $refClass = new \ReflectionClass($class_loader); + $this->assertTrue($refClass->hasMethod('loadClass'), 'Container has a class loader'); // We make this assertion here purely to show that the new container below // is functioning correctly, i.e. we get a brand new ContainerBuilder @@ -150,9 +150,9 @@ public function testCompileDIC() { $this->assertTrue($container->has('service_provider_test_class'), 'Container has test service'); // Test that our synthetic services are there. - $classloader = $container->get('class_loader'); - $refClass = new \ReflectionClass($classloader); - $this->assertTrue($refClass->hasMethod('loadClass'), 'Container has a classloader'); + $class_loader = $container->get('class_loader'); + $refClass = new \ReflectionClass($class_loader); + $this->assertTrue($refClass->hasMethod('loadClass'), 'Container has a class loader'); // Check that the location of the new module is registered. $modules = $container->getParameter('container.modules'); diff --git a/core/rebuild.php b/core/rebuild.php index 24474d0..598a408 100644 --- a/core/rebuild.php +++ b/core/rebuild.php @@ -20,7 +20,12 @@ // Change the directory to the Drupal root. chdir('..'); -$autoloader = require_once 'autoload.php'; +// Clear the APC cache to ensure APC class loader is reset. +if (function_exists('apc_fetch')) { + apc_clear_cache('user'); +} + +$autoloader = require_once __DIR__ . '/vendor/autoload.php'; require_once __DIR__ . '/includes/utility.inc'; $request = Request::createFromGlobals(); diff --git a/sites/default/default.settings.php b/sites/default/default.settings.php index d4bb0de..3783d0a 100644 --- a/sites/default/default.settings.php +++ b/sites/default/default.settings.php @@ -383,18 +383,29 @@ /** * Class Loader. * - * By default, Composer's ClassLoader is used, which is best for development, as - * it does not break when code is moved in the file system. You can decorate the - * class loader with a cached solution for better performance, which is - * recommended for production sites. - * - * To do so, you may decorate and replace the local $class_loader variable. - * - * For example, to use Symfony's APC class loader, uncomment the code below. + * If the APC extension is detected, the Symfony APC class loader is used for + * performance reasons. Detection can be prevented by setting + * class_loader_auto_detect to false, as in the example below. + */ +# $settings['class_loader_auto_detect'] = FALSE; + +/* + * If the APC extension is not detected, either because APC is missing or + * because auto-detection has been disabled, auto-loading falls back to + * Composer's ClassLoader, which is good for development as it does not break + * when code is moved in the file system. You can also decorate the base class + * loader with another cached solution than the Symfony APC class loader, as + * all production sites should have a cached class loader of some sort enabled. + * + * To do so, you may decorate and replace the local $class_loader variable. For + * example, to use Symfony's APC class loader without automatic detection, + * uncomment the code below. */ /* if ($settings['hash_salt']) { - $apc_loader = new \Symfony\Component\ClassLoader\ApcClassLoader('drupal.' . $settings['hash_salt'], $class_loader); + $prefix = 'drupal.' . hash('sha256', 'drupal.' . $settings['hash_salt']); + $apc_loader = new \Symfony\Component\ClassLoader\ApcClassLoader($prefix, $class_loader); + unset($prefix); $class_loader->unregister(); $apc_loader->register(); $class_loader = $apc_loader;