mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-10-22 14:05:37 +02:00
API Implement kernel nesting
This commit is contained in:
parent
fc7188da7d
commit
2f6336a15b
@ -188,7 +188,6 @@ class Director implements TemplateGlobalProvider
|
||||
$cookies = array(),
|
||||
&$request = null
|
||||
) {
|
||||
|
||||
Config::nest();
|
||||
Injector::nest();
|
||||
|
||||
|
@ -13,6 +13,7 @@ use SilverStripe\Core\Cache\ManifestCacheFactory;
|
||||
use SilverStripe\Core\Config\ConfigLoader;
|
||||
use SilverStripe\Core\Config\CoreConfigFactory;
|
||||
use SilverStripe\Core\Injector\Injector;
|
||||
use SilverStripe\Core\Injector\InjectorLoader;
|
||||
use SilverStripe\Core\Injector\SilverStripeServiceConfigurationLocator;
|
||||
use SilverStripe\Core\Manifest\ClassLoader;
|
||||
use SilverStripe\Core\Manifest\ClassManifest;
|
||||
@ -38,9 +39,10 @@ class AppKernel extends CoreKernel
|
||||
|
||||
// Initialise the dependency injector as soon as possible, as it is
|
||||
// subsequently used by some of the following code
|
||||
$injectorLoader = InjectorLoader::inst();
|
||||
$injector = new Injector(array('locator' => SilverStripeServiceConfigurationLocator::class));
|
||||
$this->setContainer($injector);
|
||||
Injector::set_inst($injector);
|
||||
$injectorLoader->pushManifest($injector);
|
||||
$this->setInjectorLoader($injectorLoader);
|
||||
|
||||
// Manifest cache factory
|
||||
$manifestCacheFactory = $this->buildManifestCacheFactory();
|
||||
|
@ -87,7 +87,7 @@ abstract class Config
|
||||
{
|
||||
// Unnest unless we would be left at 0 manifests
|
||||
$loader = ConfigLoader::inst();
|
||||
if ($loader->countManifests() < 2) {
|
||||
if ($loader->countManifests() <= 1) {
|
||||
user_error(
|
||||
"Unable to unnest root Config, please make sure you don't have mis-matched nest/unnest",
|
||||
E_USER_WARNING
|
||||
|
@ -82,14 +82,31 @@ class ConfigLoader
|
||||
}
|
||||
|
||||
/**
|
||||
* Nest the current manifest
|
||||
* Nest the config loader and activates it
|
||||
*
|
||||
* @return ConfigCollectionInterface
|
||||
* @return static
|
||||
*/
|
||||
public function nest()
|
||||
{
|
||||
$manifest = $this->getManifest()->nest();
|
||||
$this->pushManifest($manifest);
|
||||
return $manifest;
|
||||
// Nest config
|
||||
$manifest = clone $this->getManifest();
|
||||
|
||||
// Create new blank loader with new stack (top level nesting)
|
||||
$newLoader = new self;
|
||||
$newLoader->pushManifest($manifest);
|
||||
|
||||
// Activate new loader
|
||||
return $newLoader->activate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark this instance as the current instance
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function activate()
|
||||
{
|
||||
static::$instance = $this;
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
@ -5,6 +5,7 @@ namespace SilverStripe\Core;
|
||||
use InvalidArgumentException;
|
||||
use SilverStripe\Core\Config\ConfigLoader;
|
||||
use SilverStripe\Core\Injector\Injector;
|
||||
use SilverStripe\Core\Injector\InjectorLoader;
|
||||
use SilverStripe\Core\Manifest\ClassLoader;
|
||||
use SilverStripe\Core\Manifest\ModuleLoader;
|
||||
use SilverStripe\View\ThemeResourceLoader;
|
||||
@ -14,6 +15,11 @@ use SilverStripe\View\ThemeResourceLoader;
|
||||
*/
|
||||
class CoreKernel implements Kernel
|
||||
{
|
||||
/**
|
||||
* @var Kernel
|
||||
*/
|
||||
protected $nestedFrom = null;
|
||||
|
||||
/**
|
||||
* @var Injector
|
||||
*/
|
||||
@ -39,6 +45,11 @@ class CoreKernel implements Kernel
|
||||
*/
|
||||
protected $configLoader = null;
|
||||
|
||||
/**
|
||||
* @var InjectorLoader
|
||||
*/
|
||||
protected $injectorLoader = null;
|
||||
|
||||
/**
|
||||
* @var ThemeResourceLoader
|
||||
*/
|
||||
@ -54,21 +65,44 @@ class CoreKernel implements Kernel
|
||||
|
||||
public function nest()
|
||||
{
|
||||
// TODO: Implement nest() method.
|
||||
// Clone this kernel, nesting config / injector manifest containers
|
||||
$kernel = clone $this;
|
||||
$kernel->setConfigLoader($this->configLoader->nest());
|
||||
$kernel->setInjectorLoader($this->injectorLoader->nest());
|
||||
return $kernel;
|
||||
}
|
||||
|
||||
public function activate()
|
||||
{
|
||||
$this->configLoader->activate();
|
||||
$this->injectorLoader->activate();
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getNestedFrom()
|
||||
{
|
||||
return $this->nestedFrom;
|
||||
}
|
||||
|
||||
public function getContainer()
|
||||
{
|
||||
return $this->container;
|
||||
return $this->getInjectorLoader()->getManifest();
|
||||
}
|
||||
|
||||
public function setContainer(Injector $container)
|
||||
public function setInjectorLoader(InjectorLoader $injectorLoader)
|
||||
{
|
||||
$this->container = $container;
|
||||
$container->registerService($this, Kernel::class);
|
||||
$this->injectorLoader = $injectorLoader;
|
||||
$injectorLoader
|
||||
->getManifest()
|
||||
->registerService($this, Kernel::class);
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getInjectorLoader()
|
||||
{
|
||||
return $this->injectorLoader;
|
||||
}
|
||||
|
||||
public function getClassLoader()
|
||||
{
|
||||
return $this->classLoader;
|
||||
|
@ -106,10 +106,14 @@ class HTTPApplication implements Application
|
||||
*/
|
||||
public function execute(callable $callback)
|
||||
{
|
||||
return $this->callMiddleware(function () use ($callback) {
|
||||
// Pre-request boot
|
||||
$this->getKernel()->boot();
|
||||
return call_user_func($callback);
|
||||
});
|
||||
try {
|
||||
return $this->callMiddleware(function () use ($callback) {
|
||||
// Pre-request boot
|
||||
$this->getKernel()->boot();
|
||||
return call_user_func($callback);
|
||||
});
|
||||
} finally {
|
||||
$this->getKernel()->shutdown();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -236,18 +236,7 @@ class Injector implements ContainerInterface
|
||||
*/
|
||||
public static function inst()
|
||||
{
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the default global injector instance.
|
||||
*
|
||||
* @param Injector $instance
|
||||
* @return Injector Reference to new active Injector instance
|
||||
*/
|
||||
public static function set_inst(Injector $instance)
|
||||
{
|
||||
return self::$instance = $instance;
|
||||
return InjectorLoader::inst()->getManifest();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -262,11 +251,10 @@ class Injector implements ContainerInterface
|
||||
*/
|
||||
public static function nest()
|
||||
{
|
||||
$current = self::$instance;
|
||||
|
||||
$new = clone $current;
|
||||
$new->nestedFrom = $current;
|
||||
return self::set_inst($new);
|
||||
// Clone current injector and nest
|
||||
$new = clone self::inst();
|
||||
InjectorLoader::inst()->pushManifest($new);
|
||||
return $new;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -277,15 +265,17 @@ class Injector implements ContainerInterface
|
||||
*/
|
||||
public static function unnest()
|
||||
{
|
||||
if (self::inst()->nestedFrom) {
|
||||
self::set_inst(self::inst()->nestedFrom);
|
||||
} else {
|
||||
// Unnest unless we would be left at 0 manifests
|
||||
$loader = InjectorLoader::inst();
|
||||
if ($loader->countManifests() <= 1) {
|
||||
user_error(
|
||||
"Unable to unnest root Injector, please make sure you don't have mis-matched nest/unnest",
|
||||
E_USER_WARNING
|
||||
);
|
||||
} else {
|
||||
$loader->popManifest();
|
||||
}
|
||||
return self::inst();
|
||||
return static::inst();
|
||||
}
|
||||
|
||||
/**
|
||||
|
109
src/Core/Injector/InjectorLoader.php
Normal file
109
src/Core/Injector/InjectorLoader.php
Normal file
@ -0,0 +1,109 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Core\Injector;
|
||||
|
||||
use BadMethodCallException;
|
||||
|
||||
/**
|
||||
* Registers chained injectors
|
||||
*/
|
||||
class InjectorLoader
|
||||
{
|
||||
/**
|
||||
* @internal
|
||||
* @var self
|
||||
*/
|
||||
private static $instance;
|
||||
|
||||
/**
|
||||
* @var Injector[] map of injector instances
|
||||
*/
|
||||
protected $manifests = array();
|
||||
|
||||
/**
|
||||
* @return self
|
||||
*/
|
||||
public static function inst()
|
||||
{
|
||||
return self::$instance ? self::$instance : self::$instance = new self();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the currently active class manifest instance that is used for
|
||||
* loading classes.
|
||||
*
|
||||
* @return Injector
|
||||
*/
|
||||
public function getManifest()
|
||||
{
|
||||
if (empty($this->manifests)) {
|
||||
throw new BadMethodCallException("No injector manifests available");
|
||||
}
|
||||
return $this->manifests[count($this->manifests) - 1];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this class loader has a manifest.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasManifest()
|
||||
{
|
||||
return (bool)$this->manifests;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pushes a class manifest instance onto the top of the stack.
|
||||
*
|
||||
* @param Injector $manifest
|
||||
*/
|
||||
public function pushManifest(Injector $manifest)
|
||||
{
|
||||
$this->manifests[] = $manifest;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Injector
|
||||
*/
|
||||
public function popManifest()
|
||||
{
|
||||
return array_pop($this->manifests);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check number of manifests
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function countManifests()
|
||||
{
|
||||
return count($this->manifests);
|
||||
}
|
||||
|
||||
/**
|
||||
* Nest the config loader
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public function nest()
|
||||
{
|
||||
// Nest config
|
||||
$manifest = $this->getManifest()->nest();
|
||||
|
||||
// Create new blank loader with new stack (top level nesting)
|
||||
$newLoader = new self;
|
||||
$newLoader->pushManifest($manifest);
|
||||
|
||||
// Activate new loader
|
||||
$newLoader->activate();
|
||||
return $newLoader;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark this instance as the current instance
|
||||
*/
|
||||
public function activate()
|
||||
{
|
||||
static::$instance = $this;
|
||||
}
|
||||
}
|
@ -4,6 +4,7 @@ namespace SilverStripe\Core;
|
||||
|
||||
use SilverStripe\Core\Config\ConfigLoader;
|
||||
use SilverStripe\Core\Injector\Injector;
|
||||
use SilverStripe\Core\Injector\InjectorLoader;
|
||||
use SilverStripe\Core\Manifest\ClassLoader;
|
||||
use SilverStripe\Core\Manifest\ModuleLoader;
|
||||
use SilverStripe\View\ThemeResourceLoader;
|
||||
@ -42,23 +43,22 @@ interface Kernel
|
||||
/**
|
||||
* Nests this kernel, all components, and returns the nested value.
|
||||
*
|
||||
* @return static
|
||||
* @return Kernel
|
||||
*/
|
||||
public function nest();
|
||||
|
||||
/**
|
||||
* Ensures this kernel is the active kernel after or during nesting
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function activate();
|
||||
|
||||
/**
|
||||
* @return Injector
|
||||
*/
|
||||
public function getContainer();
|
||||
|
||||
/**
|
||||
* Sets injector
|
||||
*
|
||||
* @param Injector $container
|
||||
* @return $this
|
||||
*/
|
||||
public function setContainer(Injector $container);
|
||||
|
||||
/**
|
||||
* @return ClassLoader
|
||||
*/
|
||||
@ -70,6 +70,19 @@ interface Kernel
|
||||
*/
|
||||
public function setClassLoader(ClassLoader $classLoader);
|
||||
|
||||
/**
|
||||
* Get loader for injector instance
|
||||
*
|
||||
* @return InjectorLoader
|
||||
*/
|
||||
public function getInjectorLoader();
|
||||
|
||||
/**
|
||||
* @param InjectorLoader $injectorLoader
|
||||
* @return $this
|
||||
*/
|
||||
public function setInjectorLoader(InjectorLoader $injectorLoader);
|
||||
|
||||
/**
|
||||
* @return ModuleLoader
|
||||
*/
|
||||
|
@ -35,4 +35,10 @@ class TestKernel extends AppKernel
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function bootErrorHandling()
|
||||
{
|
||||
// Leave phpunit to capture errors
|
||||
restore_error_handler();
|
||||
}
|
||||
}
|
||||
|
@ -37,7 +37,9 @@ class ExtensionTestState implements TestState
|
||||
|
||||
public function setUpOnce($class)
|
||||
{
|
||||
$isAltered = false;
|
||||
// May be altered by another class
|
||||
$isAltered = $this->extensionsToReapply || $this->extensionsToRemove;
|
||||
|
||||
/** @var string|SapphireTest $class */
|
||||
/** @var string|DataObject $dataClass */
|
||||
// Remove any illegal extensions that are present
|
||||
@ -84,18 +86,19 @@ class ExtensionTestState implements TestState
|
||||
}
|
||||
}
|
||||
|
||||
// If we have made changes to the extensions present, then migrate the database schema.
|
||||
if ($isAltered || $this->extensionsToReapply || $this->extensionsToRemove || $class::getExtraDataObjects()) {
|
||||
// clear singletons, they're caching old extension info
|
||||
// which is used in DatabaseAdmin->doBuild()
|
||||
Injector::inst()->unregisterObjects(DataObject::class);
|
||||
|
||||
// If we have altered the schema, but SapphireTest::setUpBeforeClass() would not otherwise
|
||||
// reset the schema (if there were extra objects) then force a reset
|
||||
if ($isAltered && empty($class::getExtraDataObjects())) {
|
||||
DataObject::reset();
|
||||
if (!SapphireTest::using_temp_db()) {
|
||||
SapphireTest::create_temp_db();
|
||||
}
|
||||
SapphireTest::resetDBSchema(true);
|
||||
$class::resetDBSchema(true);
|
||||
}
|
||||
|
||||
// clear singletons, they're caching old extension info
|
||||
// which is used in DatabaseAdmin->doBuild()
|
||||
Injector::inst()->unregisterObjects(DataObject::class);
|
||||
}
|
||||
|
||||
public function tearDownOnce($class)
|
||||
|
@ -203,11 +203,11 @@ class SapphireTest extends PHPUnit_Framework_TestCase
|
||||
*/
|
||||
protected function setUp()
|
||||
{
|
||||
// Reset state
|
||||
self::$kernel->reset();
|
||||
|
||||
//nest config and injector for each test so they are effectively sandboxed per test
|
||||
Config::nest();
|
||||
Injector::nest();
|
||||
// Nest
|
||||
self::$kernel->nest();
|
||||
|
||||
// Call state helpers
|
||||
static::$state->setUp($this);
|
||||
@ -299,12 +299,20 @@ class SapphireTest extends PHPUnit_Framework_TestCase
|
||||
// Reset kernel
|
||||
static::$kernel->reset();
|
||||
|
||||
//nest config and injector for each suite so they are effectively sandboxed
|
||||
Config::nest();
|
||||
Injector::nest();
|
||||
// Nest kernel
|
||||
static::$kernel->nest();
|
||||
|
||||
// Call state helpers
|
||||
static::$state->setUpOnce(static::class);
|
||||
|
||||
// Build DB if we have objects
|
||||
if (static::getExtraDataObjects()) {
|
||||
DataObject::reset();
|
||||
if (!self::using_temp_db()) {
|
||||
self::create_temp_db();
|
||||
}
|
||||
static::resetDBSchema(true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -322,14 +330,14 @@ class SapphireTest extends PHPUnit_Framework_TestCase
|
||||
// Call state helpers
|
||||
static::$state->tearDownOnce(static::class);
|
||||
|
||||
//unnest injector / config now that the test suite is over
|
||||
// this will reset all the extensions on the object too (see setUpBeforeClass)
|
||||
Injector::unnest();
|
||||
Config::unnest();
|
||||
|
||||
static::resetDBSchema();
|
||||
// Unnest
|
||||
static::$kernel->activate();
|
||||
|
||||
// Reset PHP state
|
||||
static::$kernel->reset();
|
||||
|
||||
// Reset DB schema
|
||||
static::resetDBSchema();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -485,11 +493,10 @@ class SapphireTest extends PHPUnit_Framework_TestCase
|
||||
}
|
||||
|
||||
// Call state helpers
|
||||
static::$state->setUp($this);
|
||||
static::$state->tearDown($this);
|
||||
|
||||
//unnest injector / config now that tests are over
|
||||
Injector::unnest();
|
||||
Config::unnest();
|
||||
// Unnest
|
||||
self::$kernel->activate();
|
||||
|
||||
// Reset state
|
||||
self::$kernel->reset();
|
||||
@ -992,6 +999,11 @@ class SapphireTest extends PHPUnit_Framework_TestCase
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create temp DB without creating extra objects
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function create_temp_db()
|
||||
{
|
||||
// Disable PHPUnit error handling
|
||||
|
@ -53,10 +53,10 @@ class TestSession
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->session = Injector::inst()->create('SilverStripe\\Control\\Session', array());
|
||||
$this->cookies = Injector::inst()->create('SilverStripe\\Control\\Cookie_Backend');
|
||||
$this->session = Injector::inst()->create(Session::class, array());
|
||||
$this->cookies = Injector::inst()->create(Cookie_Backend::class);
|
||||
$this->controller = new Controller();
|
||||
$this->controller->setSession($this->session);
|
||||
// @todo - Ensure $this->session is set on all requests
|
||||
$this->controller->pushCurrent();
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user