From d8499a24d0dc24a2098e9c18a3ae96acde70fa87 Mon Sep 17 00:00:00 2001 From: Ingo Schommer Date: Fri, 4 Feb 2022 10:07:27 +1300 Subject: [PATCH] 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 Co-authored-by: Maxime Rainville --- cli-script.php | 14 +- src/Core/BaseKernel.php | 507 +++++++++++++++++++++ src/Core/CoreKernel.php | 510 +--------------------- src/Core/DatabaselessKernel.php | 63 +++ src/ORM/Connect/NullDatabase.php | 336 ++++++++++++++ src/ORM/Connect/NullDatabaseException.php | 12 + 6 files changed, 949 insertions(+), 493 deletions(-) create mode 100644 src/Core/BaseKernel.php create mode 100644 src/Core/DatabaselessKernel.php create mode 100644 src/ORM/Connect/NullDatabase.php create mode 100644 src/ORM/Connect/NullDatabaseException.php diff --git a/cli-script.php b/cli-script.php index cb88be8e5..879b2de65 100755 --- a/cli-script.php +++ b/cli-script.php @@ -4,6 +4,9 @@ use SilverStripe\Control\CLIRequestBuilder; use SilverStripe\Control\HTTPApplication; use SilverStripe\Core\CoreKernel; +use SilverStripe\ORM\DB; +use SilverStripe\ORM\Connect\NullDatabase; +use SilverStripe\Core\DatabaselessKernel; require __DIR__ . '/src/includes/autoload.php'; @@ -16,8 +19,17 @@ if (!in_array(PHP_SAPI, ["cli", "cgi", "cgi-fcgi"])) { // Build request and detect flush $request = CLIRequestBuilder::createFromEnvironment(); + +$skipDatabase = in_array('--no-database', $argv); +if ($skipDatabase) { + DB::set_conn(new NullDatabase()); +} // Default application -$kernel = new CoreKernel(BASE_PATH); +$kernel = $skipDatabase + ? new DatabaselessKernel(BASE_PATH) + : new CoreKernel(BASE_PATH); + $app = new HTTPApplication($kernel); $response = $app->handle($request); + $response->output(); diff --git a/src/Core/BaseKernel.php b/src/Core/BaseKernel.php new file mode 100644 index 000000000..2265cc1b0 --- /dev/null +++ b/src/Core/BaseKernel.php @@ -0,0 +1,507 @@ +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.

' + . 'See the ' + . 'Environment Management 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; + } +} diff --git a/src/Core/CoreKernel.php b/src/Core/CoreKernel.php index 89bbac014..baa420f92 100644 --- a/src/Core/CoreKernel.php +++ b/src/Core/CoreKernel.php @@ -2,190 +2,30 @@ 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\Install\DatabaseAdapterRegistry; -use SilverStripe\Logging\ErrorHandler; use SilverStripe\ORM\DB; -use SilverStripe\View\PublicThemes; -use SilverStripe\View\SSViewer; -use SilverStripe\View\ThemeManifest; -use SilverStripe\View\ThemeResourceLoader; -use SilverStripe\Dev\Deprecation; +use Exception; /** * 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 - * Uninitialized before boot + * Uninitialised before boot * * @var bool */ private $flush; /** - * Create a new kernel for this application - * - * @param string $basePath Path to base dir for this application + * @param false $flush + * @throws HTTPResponse_Exception + * @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) { $this->flush = $flush; @@ -198,22 +38,23 @@ class CoreKernel implements Kernel $this->bootDatabaseGlobals(); $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; - $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); + $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); } } @@ -260,80 +101,6 @@ class CoreKernel implements Kernel 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.

' - . 'See the ' - . 'Environment Management 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 * @@ -451,247 +218,6 @@ class CoreKernel implements Kernel 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 * diff --git a/src/Core/DatabaselessKernel.php b/src/Core/DatabaselessKernel.php new file mode 100644 index 000000000..d5c981ba4 --- /dev/null +++ b/src/Core/DatabaselessKernel.php @@ -0,0 +1,63 @@ +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; + } +} diff --git a/src/ORM/Connect/NullDatabase.php b/src/ORM/Connect/NullDatabase.php new file mode 100644 index 000000000..73d2ab893 --- /dev/null +++ b/src/ORM/Connect/NullDatabase.php @@ -0,0 +1,336 @@ +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 ''; + } +} diff --git a/src/ORM/Connect/NullDatabaseException.php b/src/ORM/Connect/NullDatabaseException.php new file mode 100644 index 000000000..68a7e6b62 --- /dev/null +++ b/src/ORM/Connect/NullDatabaseException.php @@ -0,0 +1,12 @@ +