mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-10-22 14:05:37 +02:00
NEW NullDatabase (#10016)
* NEW DatabaselessKernel to support operation without DB This is required for GraphQL code generation in CI (without a working runtime database/webserver environment). Context: https://github.com/silverstripe/silverstripe-graphql/issues/388 * New --no-database option for sake * Refactor to abstract class * Apply feedback peer review Co-authored-by: Aaron Carlino <unclecheese@leftandmain.com> Co-authored-by: Maxime Rainville <maxime@silverstripe.com>
This commit is contained in:
parent
3b5c72f3fd
commit
d8499a24d0
@ -4,6 +4,9 @@
|
|||||||
use SilverStripe\Control\CLIRequestBuilder;
|
use SilverStripe\Control\CLIRequestBuilder;
|
||||||
use SilverStripe\Control\HTTPApplication;
|
use SilverStripe\Control\HTTPApplication;
|
||||||
use SilverStripe\Core\CoreKernel;
|
use SilverStripe\Core\CoreKernel;
|
||||||
|
use SilverStripe\ORM\DB;
|
||||||
|
use SilverStripe\ORM\Connect\NullDatabase;
|
||||||
|
use SilverStripe\Core\DatabaselessKernel;
|
||||||
|
|
||||||
require __DIR__ . '/src/includes/autoload.php';
|
require __DIR__ . '/src/includes/autoload.php';
|
||||||
|
|
||||||
@ -16,8 +19,17 @@ if (!in_array(PHP_SAPI, ["cli", "cgi", "cgi-fcgi"])) {
|
|||||||
// Build request and detect flush
|
// Build request and detect flush
|
||||||
$request = CLIRequestBuilder::createFromEnvironment();
|
$request = CLIRequestBuilder::createFromEnvironment();
|
||||||
|
|
||||||
|
|
||||||
|
$skipDatabase = in_array('--no-database', $argv);
|
||||||
|
if ($skipDatabase) {
|
||||||
|
DB::set_conn(new NullDatabase());
|
||||||
|
}
|
||||||
// Default application
|
// Default application
|
||||||
$kernel = new CoreKernel(BASE_PATH);
|
$kernel = $skipDatabase
|
||||||
|
? new DatabaselessKernel(BASE_PATH)
|
||||||
|
: new CoreKernel(BASE_PATH);
|
||||||
|
|
||||||
$app = new HTTPApplication($kernel);
|
$app = new HTTPApplication($kernel);
|
||||||
$response = $app->handle($request);
|
$response = $app->handle($request);
|
||||||
|
|
||||||
$response->output();
|
$response->output();
|
||||||
|
507
src/Core/BaseKernel.php
Normal file
507
src/Core/BaseKernel.php
Normal file
@ -0,0 +1,507 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace SilverStripe\Core;
|
||||||
|
|
||||||
|
use InvalidArgumentException;
|
||||||
|
use Monolog\Handler\StreamHandler;
|
||||||
|
use Monolog\Logger;
|
||||||
|
use Psr\Log\LoggerInterface;
|
||||||
|
use SilverStripe\Config\Collections\CachedConfigCollection;
|
||||||
|
use SilverStripe\Control\Director;
|
||||||
|
use SilverStripe\Control\HTTPResponse;
|
||||||
|
use SilverStripe\Control\HTTPResponse_Exception;
|
||||||
|
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;
|
||||||
|
use SilverStripe\Core\Manifest\ModuleLoader;
|
||||||
|
use SilverStripe\Core\Manifest\ModuleManifest;
|
||||||
|
use SilverStripe\Dev\DebugView;
|
||||||
|
use SilverStripe\Dev\Deprecation;
|
||||||
|
use SilverStripe\Logging\ErrorHandler;
|
||||||
|
use SilverStripe\View\PublicThemes;
|
||||||
|
use SilverStripe\View\SSViewer;
|
||||||
|
use SilverStripe\View\ThemeManifest;
|
||||||
|
use SilverStripe\View\ThemeResourceLoader;
|
||||||
|
use Exception;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simple Kernel container
|
||||||
|
*/
|
||||||
|
abstract class BaseKernel implements Kernel
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var Kernel
|
||||||
|
*/
|
||||||
|
protected $nestedFrom = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var Injector
|
||||||
|
*/
|
||||||
|
protected $container = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $enviroment = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var ClassLoader
|
||||||
|
*/
|
||||||
|
protected $classLoader = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var ModuleLoader
|
||||||
|
*/
|
||||||
|
protected $moduleLoader = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var ConfigLoader
|
||||||
|
*/
|
||||||
|
protected $configLoader = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var InjectorLoader
|
||||||
|
*/
|
||||||
|
protected $injectorLoader = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var ThemeResourceLoader
|
||||||
|
*/
|
||||||
|
protected $themeResourceLoader = null;
|
||||||
|
|
||||||
|
protected $basePath = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates whether the Kernel has been booted already
|
||||||
|
*
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
private $booted = false;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new kernel for this application
|
||||||
|
*
|
||||||
|
* @param string $basePath Path to base dir for this application
|
||||||
|
*/
|
||||||
|
public function __construct($basePath)
|
||||||
|
{
|
||||||
|
$this->basePath = $basePath;
|
||||||
|
|
||||||
|
// 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(['locator' => SilverStripeServiceConfigurationLocator::class]);
|
||||||
|
$injectorLoader->pushManifest($injector);
|
||||||
|
$this->setInjectorLoader($injectorLoader);
|
||||||
|
|
||||||
|
// Manifest cache factory
|
||||||
|
$manifestCacheFactory = $this->buildManifestCacheFactory();
|
||||||
|
|
||||||
|
// Class loader
|
||||||
|
$classLoader = ClassLoader::inst();
|
||||||
|
$classLoader->pushManifest(new ClassManifest($basePath, $manifestCacheFactory));
|
||||||
|
$this->setClassLoader($classLoader);
|
||||||
|
|
||||||
|
// Module loader
|
||||||
|
$moduleLoader = ModuleLoader::inst();
|
||||||
|
$moduleManifest = new ModuleManifest($basePath, $manifestCacheFactory);
|
||||||
|
$moduleLoader->pushManifest($moduleManifest);
|
||||||
|
$this->setModuleLoader($moduleLoader);
|
||||||
|
|
||||||
|
// Config loader
|
||||||
|
// @todo refactor CoreConfigFactory
|
||||||
|
$configFactory = new CoreConfigFactory($manifestCacheFactory);
|
||||||
|
$configManifest = $configFactory->createRoot();
|
||||||
|
$configLoader = ConfigLoader::inst();
|
||||||
|
$configLoader->pushManifest($configManifest);
|
||||||
|
$this->setConfigLoader($configLoader);
|
||||||
|
|
||||||
|
// Load template manifest
|
||||||
|
$themeResourceLoader = ThemeResourceLoader::inst();
|
||||||
|
$themeResourceLoader->addSet(SSViewer::PUBLIC_THEME, new PublicThemes());
|
||||||
|
$themeResourceLoader->addSet(SSViewer::DEFAULT_THEME, new ThemeManifest(
|
||||||
|
$basePath,
|
||||||
|
null, // project is defined in config, and this argument is deprecated
|
||||||
|
$manifestCacheFactory
|
||||||
|
));
|
||||||
|
$this->setThemeResourceLoader($themeResourceLoader);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialise PHP with default variables
|
||||||
|
*/
|
||||||
|
protected function bootPHP()
|
||||||
|
{
|
||||||
|
if ($this->getEnvironment() === self::LIVE) {
|
||||||
|
// limited to fatal errors and warnings in live mode
|
||||||
|
error_reporting(E_ALL & ~(E_DEPRECATED | E_STRICT | E_NOTICE));
|
||||||
|
} else {
|
||||||
|
// Report all errors in dev / test mode
|
||||||
|
error_reporting(E_ALL | E_STRICT);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ensure we have enough memory
|
||||||
|
*/
|
||||||
|
Environment::increaseMemoryLimitTo('64M');
|
||||||
|
|
||||||
|
// Ensure we don't run into xdebug's fairly conservative infinite recursion protection limit
|
||||||
|
if (function_exists('xdebug_enable')) {
|
||||||
|
$current = ini_get('xdebug.max_nesting_level');
|
||||||
|
if ((int)$current < 200) {
|
||||||
|
ini_set('xdebug.max_nesting_level', 200);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set default encoding
|
||||||
|
*/
|
||||||
|
mb_http_output('UTF-8');
|
||||||
|
mb_internal_encoding('UTF-8');
|
||||||
|
mb_regex_encoding('UTF-8');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enable better garbage collection
|
||||||
|
*/
|
||||||
|
gc_enable();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Boot all manifests
|
||||||
|
*
|
||||||
|
* @param bool $flush
|
||||||
|
*/
|
||||||
|
protected function bootManifests($flush)
|
||||||
|
{
|
||||||
|
// Setup autoloader
|
||||||
|
$this->getClassLoader()->init(
|
||||||
|
$this->getIncludeTests(),
|
||||||
|
$flush,
|
||||||
|
$this->getIgnoredCIConfigs()
|
||||||
|
);
|
||||||
|
|
||||||
|
// Find modules
|
||||||
|
$this->getModuleLoader()->init(
|
||||||
|
$this->getIncludeTests(),
|
||||||
|
$flush,
|
||||||
|
$this->getIgnoredCIConfigs()
|
||||||
|
);
|
||||||
|
|
||||||
|
// Flush config
|
||||||
|
if ($flush) {
|
||||||
|
$config = $this->getConfigLoader()->getManifest();
|
||||||
|
if ($config instanceof CachedConfigCollection) {
|
||||||
|
$config->setFlush(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// tell modules to sort, now that config is available
|
||||||
|
$this->getModuleLoader()->getManifest()->sort();
|
||||||
|
|
||||||
|
// Find default templates
|
||||||
|
$defaultSet = $this->getThemeResourceLoader()->getSet('$default');
|
||||||
|
if ($defaultSet instanceof ThemeManifest) {
|
||||||
|
$defaultSet->setProject(
|
||||||
|
ModuleManifest::config()->get('project')
|
||||||
|
);
|
||||||
|
$defaultSet->init(
|
||||||
|
$this->getIncludeTests(),
|
||||||
|
$flush,
|
||||||
|
$this->getIgnoredCIConfigs()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Include all _config.php files
|
||||||
|
*/
|
||||||
|
protected function bootConfigs()
|
||||||
|
{
|
||||||
|
global $project;
|
||||||
|
$projectBefore = $project;
|
||||||
|
$config = ModuleManifest::config();
|
||||||
|
// After loading all other app manifests, include _config.php files
|
||||||
|
$this->getModuleLoader()->getManifest()->activateConfig();
|
||||||
|
if ($project && $project !== $projectBefore) {
|
||||||
|
Deprecation::notice('5.0', '$project global is deprecated');
|
||||||
|
$config->set('project', $project);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Turn on error handling
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
protected function bootErrorHandling()
|
||||||
|
{
|
||||||
|
// Register error handler
|
||||||
|
$errorHandler = Injector::inst()->get(ErrorHandler::class);
|
||||||
|
$errorHandler->start();
|
||||||
|
|
||||||
|
// Register error log file
|
||||||
|
$errorLog = Environment::getEnv('SS_ERROR_LOG');
|
||||||
|
if ($errorLog) {
|
||||||
|
$logger = Injector::inst()->get(LoggerInterface::class);
|
||||||
|
if ($logger instanceof Logger) {
|
||||||
|
$logger->pushHandler(new StreamHandler($this->basePath . '/' . $errorLog, Logger::WARNING));
|
||||||
|
} else {
|
||||||
|
user_error("SS_ERROR_LOG setting only works with Monolog, you are using another logger", E_USER_WARNING);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the environment type
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*
|
||||||
|
* @deprecated 5.0 use Director::get_environment_type() instead. Since 5.0 it should return only if kernel overrides. No checking SESSION or Environment.
|
||||||
|
*/
|
||||||
|
public function getEnvironment()
|
||||||
|
{
|
||||||
|
// Check set
|
||||||
|
if ($this->enviroment) {
|
||||||
|
return $this->enviroment;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check saved session
|
||||||
|
$env = $this->sessionEnvironment();
|
||||||
|
if ($env) {
|
||||||
|
return $env;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check getenv
|
||||||
|
if ($env = Environment::getEnv('SS_ENVIRONMENT_TYPE')) {
|
||||||
|
return $env;
|
||||||
|
}
|
||||||
|
|
||||||
|
return self::LIVE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check or update any temporary environment specified in the session.
|
||||||
|
*
|
||||||
|
* @return null|string
|
||||||
|
*
|
||||||
|
* @deprecated 5.0 Use Director::get_session_environment_type() instead
|
||||||
|
*/
|
||||||
|
protected function sessionEnvironment()
|
||||||
|
{
|
||||||
|
if (!$this->booted) {
|
||||||
|
// session is not initialyzed yet, neither is manifest
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Director::get_session_environment_type();
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract public function boot($flush = false);
|
||||||
|
|
||||||
|
abstract public function isFlushed();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if there's a legacy _ss_environment.php file
|
||||||
|
*
|
||||||
|
* @throws HTTPResponse_Exception
|
||||||
|
*/
|
||||||
|
protected function detectLegacyEnvironment()
|
||||||
|
{
|
||||||
|
// Is there an _ss_environment.php file?
|
||||||
|
if (!file_exists($this->basePath . '/_ss_environment.php') &&
|
||||||
|
!file_exists(dirname($this->basePath) . '/_ss_environment.php')
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build error response
|
||||||
|
$dv = new DebugView();
|
||||||
|
$body = implode([
|
||||||
|
$dv->renderHeader(),
|
||||||
|
$dv->renderInfo(
|
||||||
|
"Configuration Error",
|
||||||
|
Director::absoluteBaseURL()
|
||||||
|
),
|
||||||
|
$dv->renderParagraph(
|
||||||
|
'You need to replace your _ss_environment.php file with a .env file, or with environment variables.<br><br>'
|
||||||
|
. 'See the <a href="https://docs.silverstripe.org/en/4/getting_started/environment_management/">'
|
||||||
|
. 'Environment Management</a> docs for more information.'
|
||||||
|
),
|
||||||
|
$dv->renderFooter()
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Raise error
|
||||||
|
$response = new HTTPResponse($body, 500);
|
||||||
|
throw new HTTPResponse_Exception($response);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If missing configuration, redirect to install.php if it exists.
|
||||||
|
* Otherwise show a server error to the user.
|
||||||
|
*
|
||||||
|
* @param string $msg Optional message to show to the user on an installed project (install.php missing).
|
||||||
|
*/
|
||||||
|
protected function redirectToInstaller($msg = '')
|
||||||
|
{
|
||||||
|
// Error if installer not available
|
||||||
|
if (!file_exists(Director::publicFolder() . '/install.php')) {
|
||||||
|
throw new HTTPResponse_Exception(
|
||||||
|
$msg,
|
||||||
|
500
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Redirect to installer
|
||||||
|
$response = new HTTPResponse();
|
||||||
|
$response->redirect(Director::absoluteURL('install.php'));
|
||||||
|
throw new HTTPResponse_Exception($response);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return ManifestCacheFactory
|
||||||
|
*/
|
||||||
|
protected function buildManifestCacheFactory()
|
||||||
|
{
|
||||||
|
return new ManifestCacheFactory([
|
||||||
|
'namespace' => 'manifestcache',
|
||||||
|
'directory' => TEMP_PATH,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When manifests are discovering files, tests files in modules using the following CI library type will be ignored.
|
||||||
|
*
|
||||||
|
* The purpose of this method is to avoid loading PHPUnit test files with incompatible definitions.
|
||||||
|
*
|
||||||
|
* @return string[] List of CI types to ignore as defined by `Module`.
|
||||||
|
*/
|
||||||
|
protected function getIgnoredCIConfigs(): array
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
protected function getIncludeTests()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param bool $bool
|
||||||
|
*/
|
||||||
|
protected function setBooted(bool $bool): void
|
||||||
|
{
|
||||||
|
$this->booted = $bool;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function shutdown()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public function nest()
|
||||||
|
{
|
||||||
|
// Clone this kernel, nesting config / injector manifest containers
|
||||||
|
$kernel = clone $this;
|
||||||
|
$kernel->setConfigLoader($this->configLoader->nest());
|
||||||
|
$kernel->setInjectorLoader($this->injectorLoader->nest());
|
||||||
|
$kernel->nestedFrom = $this;
|
||||||
|
return $kernel;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function activate()
|
||||||
|
{
|
||||||
|
$this->configLoader->activate();
|
||||||
|
$this->injectorLoader->activate();
|
||||||
|
|
||||||
|
// Self register
|
||||||
|
$this->getInjectorLoader()
|
||||||
|
->getManifest()
|
||||||
|
->registerService($this, Kernel::class);
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getNestedFrom()
|
||||||
|
{
|
||||||
|
return $this->nestedFrom;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getContainer()
|
||||||
|
{
|
||||||
|
return $this->getInjectorLoader()->getManifest();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setInjectorLoader(InjectorLoader $injectorLoader)
|
||||||
|
{
|
||||||
|
$this->injectorLoader = $injectorLoader;
|
||||||
|
$injectorLoader
|
||||||
|
->getManifest()
|
||||||
|
->registerService($this, Kernel::class);
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getInjectorLoader()
|
||||||
|
{
|
||||||
|
return $this->injectorLoader;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getClassLoader()
|
||||||
|
{
|
||||||
|
return $this->classLoader;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setClassLoader(ClassLoader $classLoader)
|
||||||
|
{
|
||||||
|
$this->classLoader = $classLoader;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getModuleLoader()
|
||||||
|
{
|
||||||
|
return $this->moduleLoader;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setModuleLoader(ModuleLoader $moduleLoader)
|
||||||
|
{
|
||||||
|
$this->moduleLoader = $moduleLoader;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setEnvironment($environment)
|
||||||
|
{
|
||||||
|
if (!in_array($environment, [self::DEV, self::TEST, self::LIVE, null])) {
|
||||||
|
throw new InvalidArgumentException(
|
||||||
|
"Director::set_environment_type passed '$environment'. It should be passed dev, test, or live"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
$this->enviroment = $environment;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getConfigLoader()
|
||||||
|
{
|
||||||
|
return $this->configLoader;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setConfigLoader($configLoader)
|
||||||
|
{
|
||||||
|
$this->configLoader = $configLoader;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getThemeResourceLoader()
|
||||||
|
{
|
||||||
|
return $this->themeResourceLoader;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setThemeResourceLoader($themeResourceLoader)
|
||||||
|
{
|
||||||
|
$this->themeResourceLoader = $themeResourceLoader;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
}
|
@ -2,190 +2,30 @@
|
|||||||
|
|
||||||
namespace SilverStripe\Core;
|
namespace SilverStripe\Core;
|
||||||
|
|
||||||
use InvalidArgumentException;
|
|
||||||
use Monolog\Handler\StreamHandler;
|
|
||||||
use Monolog\Logger;
|
|
||||||
use Psr\Log\LoggerInterface;
|
|
||||||
use SilverStripe\Config\Collections\CachedConfigCollection;
|
|
||||||
use SilverStripe\Control\Director;
|
|
||||||
use SilverStripe\Control\HTTPResponse;
|
|
||||||
use SilverStripe\Control\HTTPResponse_Exception;
|
use SilverStripe\Control\HTTPResponse_Exception;
|
||||||
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;
|
|
||||||
use SilverStripe\Core\Manifest\ModuleLoader;
|
|
||||||
use SilverStripe\Core\Manifest\ModuleManifest;
|
|
||||||
use SilverStripe\Dev\DebugView;
|
|
||||||
use SilverStripe\Dev\Install\DatabaseAdapterRegistry;
|
use SilverStripe\Dev\Install\DatabaseAdapterRegistry;
|
||||||
use SilverStripe\Logging\ErrorHandler;
|
|
||||||
use SilverStripe\ORM\DB;
|
use SilverStripe\ORM\DB;
|
||||||
use SilverStripe\View\PublicThemes;
|
use Exception;
|
||||||
use SilverStripe\View\SSViewer;
|
|
||||||
use SilverStripe\View\ThemeManifest;
|
|
||||||
use SilverStripe\View\ThemeResourceLoader;
|
|
||||||
use SilverStripe\Dev\Deprecation;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Simple Kernel container
|
* Simple Kernel container
|
||||||
*/
|
*/
|
||||||
class CoreKernel implements Kernel
|
class CoreKernel extends BaseKernel
|
||||||
{
|
{
|
||||||
/**
|
|
||||||
* @var Kernel
|
|
||||||
*/
|
|
||||||
protected $nestedFrom = null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var Injector
|
|
||||||
*/
|
|
||||||
protected $container = null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
protected $environment = null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var ClassLoader
|
|
||||||
*/
|
|
||||||
protected $classLoader = null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var ModuleLoader
|
|
||||||
*/
|
|
||||||
protected $moduleLoader = null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var ConfigLoader
|
|
||||||
*/
|
|
||||||
protected $configLoader = null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var InjectorLoader
|
|
||||||
*/
|
|
||||||
protected $injectorLoader = null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var ThemeResourceLoader
|
|
||||||
*/
|
|
||||||
protected $themeResourceLoader = null;
|
|
||||||
|
|
||||||
protected $basePath = null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Indicates whether the Kernel has been booted already
|
|
||||||
*
|
|
||||||
* @var bool
|
|
||||||
*/
|
|
||||||
private $booted = false;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Indicates whether the Kernel has been flushed on boot
|
* Indicates whether the Kernel has been flushed on boot
|
||||||
* Uninitialized before boot
|
* Uninitialised before boot
|
||||||
*
|
*
|
||||||
* @var bool
|
* @var bool
|
||||||
*/
|
*/
|
||||||
private $flush;
|
private $flush;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new kernel for this application
|
* @param false $flush
|
||||||
*
|
* @throws HTTPResponse_Exception
|
||||||
* @param string $basePath Path to base dir for this application
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
public function __construct($basePath)
|
|
||||||
{
|
|
||||||
$this->basePath = $basePath;
|
|
||||||
|
|
||||||
// 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(['locator' => SilverStripeServiceConfigurationLocator::class]);
|
|
||||||
$injectorLoader->pushManifest($injector);
|
|
||||||
$this->setInjectorLoader($injectorLoader);
|
|
||||||
|
|
||||||
// Manifest cache factory
|
|
||||||
$manifestCacheFactory = $this->buildManifestCacheFactory();
|
|
||||||
|
|
||||||
// Class loader
|
|
||||||
$classLoader = ClassLoader::inst();
|
|
||||||
$classLoader->pushManifest(new ClassManifest($basePath, $manifestCacheFactory));
|
|
||||||
$this->setClassLoader($classLoader);
|
|
||||||
|
|
||||||
// Module loader
|
|
||||||
$moduleLoader = ModuleLoader::inst();
|
|
||||||
$moduleManifest = new ModuleManifest($basePath, $manifestCacheFactory);
|
|
||||||
$moduleLoader->pushManifest($moduleManifest);
|
|
||||||
$this->setModuleLoader($moduleLoader);
|
|
||||||
|
|
||||||
// Config loader
|
|
||||||
// @todo refactor CoreConfigFactory
|
|
||||||
$configFactory = new CoreConfigFactory($manifestCacheFactory);
|
|
||||||
$configManifest = $configFactory->createRoot();
|
|
||||||
$configLoader = ConfigLoader::inst();
|
|
||||||
$configLoader->pushManifest($configManifest);
|
|
||||||
$this->setConfigLoader($configLoader);
|
|
||||||
|
|
||||||
// Load template manifest
|
|
||||||
$themeResourceLoader = ThemeResourceLoader::inst();
|
|
||||||
$themeResourceLoader->addSet(SSViewer::PUBLIC_THEME, new PublicThemes());
|
|
||||||
$themeResourceLoader->addSet(SSViewer::DEFAULT_THEME, new ThemeManifest(
|
|
||||||
$basePath,
|
|
||||||
null, // project is defined in config, and this argument is deprecated
|
|
||||||
$manifestCacheFactory
|
|
||||||
));
|
|
||||||
$this->setThemeResourceLoader($themeResourceLoader);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the environment type
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*
|
|
||||||
* @deprecated 5.0 use Director::get_environment_type() instead. Since 5.0 it should return only if kernel overrides. No checking SESSION or Environment.
|
|
||||||
*/
|
|
||||||
public function getEnvironment()
|
|
||||||
{
|
|
||||||
// Check set
|
|
||||||
if ($this->environment) {
|
|
||||||
return $this->environment;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check saved session
|
|
||||||
$env = $this->sessionEnvironment();
|
|
||||||
if ($env) {
|
|
||||||
return $env;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check getenv
|
|
||||||
if ($env = Environment::getEnv('SS_ENVIRONMENT_TYPE')) {
|
|
||||||
return $env;
|
|
||||||
}
|
|
||||||
|
|
||||||
return self::LIVE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check or update any temporary environment specified in the session.
|
|
||||||
*
|
|
||||||
* @return null|string
|
|
||||||
*
|
|
||||||
* @deprecated 5.0 Use Director::get_session_environment_type() instead
|
|
||||||
*/
|
|
||||||
protected function sessionEnvironment()
|
|
||||||
{
|
|
||||||
if (!$this->booted) {
|
|
||||||
// session is not initialized yet, neither is manifest
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Director::get_session_environment_type();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function boot($flush = false)
|
public function boot($flush = false)
|
||||||
{
|
{
|
||||||
$this->flush = $flush;
|
$this->flush = $flush;
|
||||||
@ -198,22 +38,23 @@ class CoreKernel implements Kernel
|
|||||||
$this->bootDatabaseGlobals();
|
$this->bootDatabaseGlobals();
|
||||||
$this->validateDatabase();
|
$this->validateDatabase();
|
||||||
|
|
||||||
$this->booted = true;
|
$this->setBooted(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Include all _config.php files
|
* Check that the database configuration is valid, throwing an HTTPResponse_Exception if it's not
|
||||||
|
*
|
||||||
|
* @throws HTTPResponse_Exception
|
||||||
*/
|
*/
|
||||||
protected function bootConfigs()
|
protected function validateDatabase()
|
||||||
{
|
{
|
||||||
global $project;
|
$databaseConfig = DB::getConfig();
|
||||||
$projectBefore = $project;
|
// Gracefully fail if no DB is configured
|
||||||
$config = ModuleManifest::config();
|
if (empty($databaseConfig['database'])) {
|
||||||
// After loading all other app manifests, include _config.php files
|
$msg = 'Silverstripe Framework requires a "database" key in DB::getConfig(). ' .
|
||||||
$this->getModuleLoader()->getManifest()->activateConfig();
|
'Did you forget to set SS_DATABASE_NAME or SS_DATABASE_CHOOSE_NAME in your environment?';
|
||||||
if ($project && $project !== $projectBefore) {
|
$this->detectLegacyEnvironment();
|
||||||
Deprecation::notice('5.0', '$project global is deprecated');
|
$this->redirectToInstaller($msg);
|
||||||
$config->set('project', $project);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -260,80 +101,6 @@ class CoreKernel implements Kernel
|
|||||||
DB::setConfig($databaseConfig);
|
DB::setConfig($databaseConfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Check that the database configuration is valid, throwing an HTTPResponse_Exception if it's not
|
|
||||||
*
|
|
||||||
* @throws HTTPResponse_Exception
|
|
||||||
*/
|
|
||||||
protected function validateDatabase()
|
|
||||||
{
|
|
||||||
$databaseConfig = DB::getConfig();
|
|
||||||
// Gracefully fail if no DB is configured
|
|
||||||
if (empty($databaseConfig['database'])) {
|
|
||||||
$msg = 'Silverstripe Framework requires a "database" key in DB::getConfig(). ' .
|
|
||||||
'Did you forget to set SS_DATABASE_NAME or SS_DATABASE_CHOOSE_NAME in your environment?';
|
|
||||||
$this->detectLegacyEnvironment();
|
|
||||||
$this->redirectToInstaller($msg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if there's a legacy _ss_environment.php file
|
|
||||||
*
|
|
||||||
* @throws HTTPResponse_Exception
|
|
||||||
*/
|
|
||||||
protected function detectLegacyEnvironment()
|
|
||||||
{
|
|
||||||
// Is there an _ss_environment.php file?
|
|
||||||
if (!file_exists($this->basePath . '/_ss_environment.php') &&
|
|
||||||
!file_exists(dirname($this->basePath) . '/_ss_environment.php')
|
|
||||||
) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Build error response
|
|
||||||
$dv = new DebugView();
|
|
||||||
$body = implode([
|
|
||||||
$dv->renderHeader(),
|
|
||||||
$dv->renderInfo(
|
|
||||||
"Configuration Error",
|
|
||||||
Director::absoluteBaseURL()
|
|
||||||
),
|
|
||||||
$dv->renderParagraph(
|
|
||||||
'You need to replace your _ss_environment.php file with a .env file, or with environment variables.<br><br>'
|
|
||||||
. 'See the <a href="https://docs.silverstripe.org/en/4/getting_started/environment_management/">'
|
|
||||||
. 'Environment Management</a> docs for more information.'
|
|
||||||
),
|
|
||||||
$dv->renderFooter()
|
|
||||||
]);
|
|
||||||
|
|
||||||
// Raise error
|
|
||||||
$response = new HTTPResponse($body, 500);
|
|
||||||
throw new HTTPResponse_Exception($response);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* If missing configuration, redirect to install.php if it exists.
|
|
||||||
* Otherwise show a server error to the user.
|
|
||||||
*
|
|
||||||
* @param string $msg Optional message to show to the user on an installed project (install.php missing).
|
|
||||||
*/
|
|
||||||
protected function redirectToInstaller($msg = '')
|
|
||||||
{
|
|
||||||
// Error if installer not available
|
|
||||||
if (!file_exists(Director::publicFolder() . '/install.php')) {
|
|
||||||
throw new HTTPResponse_Exception(
|
|
||||||
$msg,
|
|
||||||
500
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Redirect to installer
|
|
||||||
$response = new HTTPResponse();
|
|
||||||
$response->redirect(Director::absoluteURL('install.php'));
|
|
||||||
throw new HTTPResponse_Exception($response);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Load database config from environment
|
* Load database config from environment
|
||||||
*
|
*
|
||||||
@ -451,247 +218,6 @@ class CoreKernel implements Kernel
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Initialise PHP with default variables
|
|
||||||
*/
|
|
||||||
protected function bootPHP()
|
|
||||||
{
|
|
||||||
if ($this->getEnvironment() === self::LIVE) {
|
|
||||||
// limited to fatal errors and warnings in live mode
|
|
||||||
error_reporting(E_ALL & ~(E_DEPRECATED | E_STRICT | E_NOTICE));
|
|
||||||
} else {
|
|
||||||
// Report all errors in dev / test mode
|
|
||||||
error_reporting(E_ALL | E_STRICT);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Ensure we have enough memory
|
|
||||||
*/
|
|
||||||
Environment::increaseMemoryLimitTo('64M');
|
|
||||||
|
|
||||||
// Ensure we don't run into xdebug's fairly conservative infinite recursion protection limit
|
|
||||||
if (function_exists('xdebug_enable')) {
|
|
||||||
$current = ini_get('xdebug.max_nesting_level');
|
|
||||||
if ((int)$current < 200) {
|
|
||||||
ini_set('xdebug.max_nesting_level', 200);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set default encoding
|
|
||||||
*/
|
|
||||||
mb_http_output('UTF-8');
|
|
||||||
mb_internal_encoding('UTF-8');
|
|
||||||
mb_regex_encoding('UTF-8');
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Enable better garbage collection
|
|
||||||
*/
|
|
||||||
gc_enable();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return ManifestCacheFactory
|
|
||||||
*/
|
|
||||||
protected function buildManifestCacheFactory()
|
|
||||||
{
|
|
||||||
return new ManifestCacheFactory([
|
|
||||||
'namespace' => 'manifestcache',
|
|
||||||
'directory' => TEMP_PATH,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
protected function getIncludeTests()
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* When manifests are discovering files, tests files in modules using the following CI library type will be ignored.
|
|
||||||
*
|
|
||||||
* The purpose of this method is to avoid loading PHPUnit test files with incompatible definitions.
|
|
||||||
*
|
|
||||||
* @return string[] List of CI types to ignore as defined by `Module`.
|
|
||||||
*/
|
|
||||||
protected function getIgnoredCIConfigs(): array
|
|
||||||
{
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Boot all manifests
|
|
||||||
*
|
|
||||||
* @param bool $flush
|
|
||||||
*/
|
|
||||||
protected function bootManifests($flush)
|
|
||||||
{
|
|
||||||
// Setup autoloader
|
|
||||||
$this->getClassLoader()->init(
|
|
||||||
$this->getIncludeTests(),
|
|
||||||
$flush,
|
|
||||||
$this->getIgnoredCIConfigs()
|
|
||||||
);
|
|
||||||
|
|
||||||
// Find modules
|
|
||||||
$this->getModuleLoader()->init(
|
|
||||||
$this->getIncludeTests(),
|
|
||||||
$flush,
|
|
||||||
$this->getIgnoredCIConfigs()
|
|
||||||
);
|
|
||||||
|
|
||||||
// Flush config
|
|
||||||
if ($flush) {
|
|
||||||
$config = $this->getConfigLoader()->getManifest();
|
|
||||||
if ($config instanceof CachedConfigCollection) {
|
|
||||||
$config->setFlush(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// tell modules to sort, now that config is available
|
|
||||||
$this->getModuleLoader()->getManifest()->sort();
|
|
||||||
|
|
||||||
// Find default templates
|
|
||||||
$defaultSet = $this->getThemeResourceLoader()->getSet('$default');
|
|
||||||
if ($defaultSet instanceof ThemeManifest) {
|
|
||||||
$defaultSet->setProject(
|
|
||||||
ModuleManifest::config()->get('project')
|
|
||||||
);
|
|
||||||
$defaultSet->init(
|
|
||||||
$this->getIncludeTests(),
|
|
||||||
$flush,
|
|
||||||
$this->getIgnoredCIConfigs()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Turn on error handling
|
|
||||||
*/
|
|
||||||
protected function bootErrorHandling()
|
|
||||||
{
|
|
||||||
// Register error handler
|
|
||||||
$errorHandler = Injector::inst()->get(ErrorHandler::class);
|
|
||||||
$errorHandler->start();
|
|
||||||
|
|
||||||
// Register error log file
|
|
||||||
$errorLog = Environment::getEnv('SS_ERROR_LOG');
|
|
||||||
if ($errorLog) {
|
|
||||||
$logger = Injector::inst()->get(LoggerInterface::class);
|
|
||||||
if ($logger instanceof Logger) {
|
|
||||||
$logger->pushHandler(new StreamHandler($this->basePath . '/' . $errorLog, Logger::WARNING));
|
|
||||||
} else {
|
|
||||||
user_error("SS_ERROR_LOG setting only works with Monolog, you are using another logger", E_USER_WARNING);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function shutdown()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public function nest()
|
|
||||||
{
|
|
||||||
// Clone this kernel, nesting config / injector manifest containers
|
|
||||||
$kernel = clone $this;
|
|
||||||
$kernel->setConfigLoader($this->configLoader->nest());
|
|
||||||
$kernel->setInjectorLoader($this->injectorLoader->nest());
|
|
||||||
$kernel->nestedFrom = $this;
|
|
||||||
return $kernel;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function activate()
|
|
||||||
{
|
|
||||||
$this->configLoader->activate();
|
|
||||||
$this->injectorLoader->activate();
|
|
||||||
|
|
||||||
// Self register
|
|
||||||
$this->getInjectorLoader()
|
|
||||||
->getManifest()
|
|
||||||
->registerService($this, Kernel::class);
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getNestedFrom()
|
|
||||||
{
|
|
||||||
return $this->nestedFrom;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getContainer()
|
|
||||||
{
|
|
||||||
return $this->getInjectorLoader()->getManifest();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function setInjectorLoader(InjectorLoader $injectorLoader)
|
|
||||||
{
|
|
||||||
$this->injectorLoader = $injectorLoader;
|
|
||||||
$injectorLoader
|
|
||||||
->getManifest()
|
|
||||||
->registerService($this, Kernel::class);
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getInjectorLoader()
|
|
||||||
{
|
|
||||||
return $this->injectorLoader;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getClassLoader()
|
|
||||||
{
|
|
||||||
return $this->classLoader;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function setClassLoader(ClassLoader $classLoader)
|
|
||||||
{
|
|
||||||
$this->classLoader = $classLoader;
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getModuleLoader()
|
|
||||||
{
|
|
||||||
return $this->moduleLoader;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function setModuleLoader(ModuleLoader $moduleLoader)
|
|
||||||
{
|
|
||||||
$this->moduleLoader = $moduleLoader;
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function setEnvironment($environment)
|
|
||||||
{
|
|
||||||
if (!in_array($environment, [self::DEV, self::TEST, self::LIVE, null])) {
|
|
||||||
throw new InvalidArgumentException(
|
|
||||||
"Director::set_environment_type passed '$environment'. It should be passed dev, test, or live"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
$this->environment = $environment;
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getConfigLoader()
|
|
||||||
{
|
|
||||||
return $this->configLoader;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function setConfigLoader($configLoader)
|
|
||||||
{
|
|
||||||
$this->configLoader = $configLoader;
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getThemeResourceLoader()
|
|
||||||
{
|
|
||||||
return $this->themeResourceLoader;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function setThemeResourceLoader($themeResourceLoader)
|
|
||||||
{
|
|
||||||
$this->themeResourceLoader = $themeResourceLoader;
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns whether the Kernel has been flushed on boot
|
* Returns whether the Kernel has been flushed on boot
|
||||||
*
|
*
|
||||||
|
63
src/Core/DatabaselessKernel.php
Normal file
63
src/Core/DatabaselessKernel.php
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace SilverStripe\Core;
|
||||||
|
|
||||||
|
use Exception;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Boot a kernel without requiring a database connection.
|
||||||
|
* This is a workaround for the lack of composition in the boot stages
|
||||||
|
* of CoreKernel, as well as for the framework's misguided assumptions
|
||||||
|
* around the availability of a database for every execution path.
|
||||||
|
*
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
class DatabaselessKernel extends BaseKernel
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Indicates whether the Kernel has been flushed on boot
|
||||||
|
* Uninitialised before boot
|
||||||
|
*
|
||||||
|
* @var bool
|
||||||
|
*/
|
||||||
|
private $flush;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allows disabling of the configured error handling.
|
||||||
|
* This can be useful to ensure the execution context (e.g. composer)
|
||||||
|
* can consistently use its own error handling.
|
||||||
|
*
|
||||||
|
* @var boolean
|
||||||
|
*/
|
||||||
|
protected $bootErrorHandling = true;
|
||||||
|
|
||||||
|
public function setBootErrorHandling(bool $bool)
|
||||||
|
{
|
||||||
|
$this->bootErrorHandling = $bool;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param false $flush
|
||||||
|
* @throws Exception
|
||||||
|
*/
|
||||||
|
public function boot($flush = false)
|
||||||
|
{
|
||||||
|
$this->flush = $flush;
|
||||||
|
|
||||||
|
$this->bootPHP();
|
||||||
|
$this->bootManifests($flush);
|
||||||
|
$this->bootErrorHandling();
|
||||||
|
$this->bootConfigs();
|
||||||
|
|
||||||
|
$this->setBooted(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function isFlushed()
|
||||||
|
{
|
||||||
|
return $this->flush;
|
||||||
|
}
|
||||||
|
}
|
336
src/ORM/Connect/NullDatabase.php
Normal file
336
src/ORM/Connect/NullDatabase.php
Normal file
@ -0,0 +1,336 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace SilverStripe\ORM\Connect;
|
||||||
|
|
||||||
|
use BadMethodCallException;
|
||||||
|
use Exception;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility class required due to bad coupling in framework.
|
||||||
|
* Not every framework execution should require a working database connection.
|
||||||
|
* For example, when generating class and config manifests for deployment bundles,
|
||||||
|
* or when generating code in a silverstripe/graphql schema build.
|
||||||
|
*
|
||||||
|
* This class creates the required no-ops to fulfill the contract,
|
||||||
|
* and create exceptions as required.
|
||||||
|
*
|
||||||
|
* It also avoids introducing new third party core dependencies that
|
||||||
|
* would be required with https://github.com/tractorcow/silverstripe-proxy-db.
|
||||||
|
*
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
class NullDatabase extends Database
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $errorMessage = 'Using NullDatabase, cannot interact with database';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private $queryErrorMessage = 'Using NullDatabase, cannot execute query: %s';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $msg
|
||||||
|
*/
|
||||||
|
public function setErrorMessage(string $msg): self
|
||||||
|
{
|
||||||
|
$this->errorMessage = $msg;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $msg
|
||||||
|
*/
|
||||||
|
public function setQueryErrorMessage(string $msg): self
|
||||||
|
{
|
||||||
|
$this->queryErrorMessage = $msg;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws NullDatabaseException
|
||||||
|
*/
|
||||||
|
public function query($sql, $errorLevel = E_USER_ERROR)
|
||||||
|
{
|
||||||
|
throw new NullDatabaseException(sprintf($this->queryErrorMessage, $sql));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws NullDatabaseException
|
||||||
|
*/
|
||||||
|
public function preparedQuery($sql, $parameters, $errorLevel = E_USER_ERROR)
|
||||||
|
{
|
||||||
|
throw new NullDatabaseException(sprintf($this->queryErrorMessage, $sql));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws NullDatabaseException
|
||||||
|
*/
|
||||||
|
public function getConnector()
|
||||||
|
{
|
||||||
|
throw new NullDatabaseException($this->errorMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws NullDatabaseException
|
||||||
|
*/
|
||||||
|
public function getSchemaManager()
|
||||||
|
{
|
||||||
|
throw new NullDatabaseException($this->errorMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws NullDatabaseException
|
||||||
|
*/
|
||||||
|
public function getQueryBuilder()
|
||||||
|
{
|
||||||
|
throw new NullDatabaseException($this->errorMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function getGeneratedID($table)
|
||||||
|
{
|
||||||
|
// no-op
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isActive()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function escapeString($value)
|
||||||
|
{
|
||||||
|
return $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function quoteString($value)
|
||||||
|
{
|
||||||
|
return $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function escapeIdentifier($value, $separator = '.')
|
||||||
|
{
|
||||||
|
return $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function escapeColumnKeys($fieldValues)
|
||||||
|
{
|
||||||
|
return $fieldValues;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws NullDatabaseException
|
||||||
|
*/
|
||||||
|
public function manipulate($manipulation)
|
||||||
|
{
|
||||||
|
throw new NullDatabaseException($this->errorMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws NullDatabaseException
|
||||||
|
*/
|
||||||
|
public function clearAllData()
|
||||||
|
{
|
||||||
|
throw new NullDatabaseException($this->errorMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws NullDatabaseException
|
||||||
|
*/
|
||||||
|
public function clearTable($table)
|
||||||
|
{
|
||||||
|
throw new NullDatabaseException($this->errorMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function nullCheckClause($field, $isNull)
|
||||||
|
{
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function comparisonClause(
|
||||||
|
$field,
|
||||||
|
$value,
|
||||||
|
$exact = false,
|
||||||
|
$negate = false,
|
||||||
|
$caseSensitive = null,
|
||||||
|
$parameterised = false
|
||||||
|
) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function formattedDatetimeClause($date, $format)
|
||||||
|
{
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function datetimeIntervalClause($date, $interval)
|
||||||
|
{
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function datetimeDifferenceClause($date1, $date2)
|
||||||
|
{
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function concatOperator()
|
||||||
|
{
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function supportsCollations()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function supportsTimezoneOverride()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getVersion()
|
||||||
|
{
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDatabaseServer()
|
||||||
|
{
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function affectedRows()
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function searchEngine(
|
||||||
|
$classesToSearch,
|
||||||
|
$keywords,
|
||||||
|
$start,
|
||||||
|
$pageLength,
|
||||||
|
$sortBy = "Relevance DESC",
|
||||||
|
$extraFilter = "",
|
||||||
|
$booleanSearch = false,
|
||||||
|
$alternativeFileFilter = "",
|
||||||
|
$invertedMatch = false
|
||||||
|
) {
|
||||||
|
// no-op
|
||||||
|
}
|
||||||
|
|
||||||
|
public function supportsTransactions()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function supportsSavepoints()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function supportsTransactionMode(string $mode): bool
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function withTransaction(
|
||||||
|
$callback,
|
||||||
|
$errorCallback = null,
|
||||||
|
$transactionMode = false,
|
||||||
|
$errorIfTransactionsUnsupported = false
|
||||||
|
) {
|
||||||
|
// no-op
|
||||||
|
}
|
||||||
|
|
||||||
|
public function supportsExtensions($extensions)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function transactionStart($transactionMode = false, $sessionCharacteristics = false)
|
||||||
|
{
|
||||||
|
// no-op
|
||||||
|
}
|
||||||
|
|
||||||
|
public function transactionSavepoint($savepoint)
|
||||||
|
{
|
||||||
|
// no-op
|
||||||
|
}
|
||||||
|
|
||||||
|
public function transactionRollback($savepoint = false)
|
||||||
|
{
|
||||||
|
// no-op
|
||||||
|
}
|
||||||
|
|
||||||
|
public function transactionEnd($chain = false)
|
||||||
|
{
|
||||||
|
// no-op
|
||||||
|
}
|
||||||
|
|
||||||
|
public function transactionDepth()
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function supportsLocks()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function canLock($name)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getLock($name, $timeout = 5)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function releaseLock($name)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function connect($parameters)
|
||||||
|
{
|
||||||
|
// no-op
|
||||||
|
}
|
||||||
|
|
||||||
|
public function databaseExists($name)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function databaseList()
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function selectDatabase($name, $create = false, $errorLevel = E_USER_ERROR)
|
||||||
|
{
|
||||||
|
// no-op
|
||||||
|
}
|
||||||
|
|
||||||
|
public function dropSelectedDatabase()
|
||||||
|
{
|
||||||
|
// no-op
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getSelectedDatabase()
|
||||||
|
{
|
||||||
|
// no-op
|
||||||
|
}
|
||||||
|
|
||||||
|
public function now()
|
||||||
|
{
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function random()
|
||||||
|
{
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
}
|
12
src/ORM/Connect/NullDatabaseException.php
Normal file
12
src/ORM/Connect/NullDatabaseException.php
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<?php
|
||||||
|
namespace SilverStripe\ORM\Connect;
|
||||||
|
|
||||||
|
use LogicException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exception thrown when a database operation is attempted on a `NullDatabase`.
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
class NullDatabaseException extends LogicException
|
||||||
|
{
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user