Commit 77b8f3af authored by catch's avatar catch

Issue #2843828 by alexpott, timmillwood:...

Issue #2843828 by alexpott, timmillwood: \Drupal\Core\DrupalKernel::initializeSettings() can result in moving the autoloader position
parent be832f3b
...@@ -1031,15 +1031,17 @@ protected function initializeSettings(Request $request) { ...@@ -1031,15 +1031,17 @@ protected function initializeSettings(Request $request) {
$prefix = Settings::getApcuPrefix('class_loader', $this->root); $prefix = Settings::getApcuPrefix('class_loader', $this->root);
$apc_loader = new ApcClassLoader($prefix, $this->classLoader); $apc_loader = new ApcClassLoader($prefix, $this->classLoader);
$this->classLoader->unregister(); $this->classLoader->unregister();
$apc_loader->register();
$old_loader = $this->classLoader;
$this->classLoader = $apc_loader;
// The optimized classloader might be persistent and store cache misses. // The optimized classloader might be persistent and store cache misses.
// For example, once a cache miss is stored in APCu clearing it on a // For example, once a cache miss is stored in APCu clearing it on a
// specific web-head will not clear any other web-heads. Therefore // specific web-head will not clear any other web-heads. Therefore
// fallback to the composer class loader that only statically caches // fallback to the composer class loader that only statically caches
// misses. // misses.
$old_loader->register(); $old_loader = $this->classLoader;
$this->classLoader = $apc_loader;
// Our class loaders are preprended to ensure they come first like the
// class loader they are replacing.
$old_loader->register(TRUE);
$apc_loader->register(TRUE);
} }
} }
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
use Drupal\Core\DrupalKernel; use Drupal\Core\DrupalKernel;
use Drupal\Tests\UnitTestCase; use Drupal\Tests\UnitTestCase;
use org\bovigo\vfs\vfsStream; use org\bovigo\vfs\vfsStream;
use Symfony\Component\ClassLoader\ApcClassLoader;
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Request;
/** /**
...@@ -47,6 +48,63 @@ public function testTrustedHosts($host, $server_name, $message, $expected = FALS ...@@ -47,6 +48,63 @@ public function testTrustedHosts($host, $server_name, $message, $expected = FALS
Request::setFactory(NULL); Request::setFactory(NULL);
} }
/**
* Tests the reregistration of autoloaders if APCu available.
*
* This test runs in a separate process since it registers class loaders and
* results in statics being set.
*
* @runInSeparateProcess
* @preserveGlobalState disabled
* @requires function apcu_fetch
* @covers ::initializeSettings
*/
public function testInitializeSettings() {
$request = new Request();
$classloader = new fakeAutoloader();
// Create a kernel suitable for testing.
$kernel = $this->getMockBuilder(DrupalKernel::class)
->disableOriginalConstructor()
->setMethods(['do_not_mock_any_methods'])
->getMock();
$classloader_property = new \ReflectionProperty($kernel, 'classLoader');
$classloader_property->setAccessible(TRUE);
$classloader_property->setValue($kernel, $classloader);
$method = new \ReflectionMethod($kernel, 'initializeSettings');
$method->setAccessible(TRUE);
// Prepend another autoloader to simulate Drush's autoloader.
$fake_drush_autoloader = function () {
return NULL;
};
spl_autoload_register($fake_drush_autoloader, TRUE, TRUE);
// Before calling DrupalKernel::initializeSettings() the first autoloader
// is the fake Drush autoloader.
$this->assertSame($fake_drush_autoloader, spl_autoload_functions()[0]);
// Call DrupalKernel::initializeSettings() to simulate part of a Drupal
// bootstrap. During the include of autoload.php Composer would prepend
// Drupal's autoloader and then this method should not result in Drush's
// autoloader becoming the first autoloader even if it swaps out
// Composer's autoloader for an optimised one.
$method->invoke($kernel, $request);
$autoloaders = spl_autoload_functions();
// The first autoloader should be the APCu based autoloader.
$this->assertInstanceOf(ApcClassLoader::class, $autoloaders[0][0]);
// The second autoloader should be the original autoloader the kernel was
// constructed with.
$this->assertSame($classloader, $autoloaders[1][0]);
// The third autoloader should be Drush's autoloader.
$this->assertSame($fake_drush_autoloader, $autoloaders[2]);
// Reset the request factory because it is statically stored on the
// request.
Request::setFactory(NULL);
}
/** /**
* Provides test data for testTrustedHosts(). * Provides test data for testTrustedHosts().
*/ */
...@@ -136,6 +194,49 @@ public function testFindSitePath() { ...@@ -136,6 +194,49 @@ public function testFindSitePath() {
} }
/**
* A fake autoloader for testing
*/
class fakeAutoloader {
/**
* Registers this instance as an autoloader.
*
* @param bool $prepend
* Whether to prepend the autoloader or not
*/
public function register($prepend = FALSE) {
spl_autoload_register(array($this, 'loadClass'), TRUE, $prepend);
}
/**
* Unregisters this instance as an autoloader.
*/
public function unregister() {
spl_autoload_unregister(array($this, 'loadClass'));
}
/**
* Loads the given class or interface.
*
* @return null
* This class never loads.
*/
public function loadClass() {
return NULL;
}
/**
* Finds a file by class name while caching lookups to APC.
*
* @return null
* This class never finds.
*/
public function findFile() {
return NULL;
}
}
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment