From ad589b35b463f5b77a3237744f30b2e2c148b3dd Mon Sep 17 00:00:00 2001 From: Thomas de Graaff Date: Sat, 2 Sep 2017 20:10:28 +0000 Subject: [PATCH 1/2] Adds aegir support. --- src/Bootstrap/ConsoleSettings.php | 178 ++++++++++++++++++++++++++++++ src/Bootstrap/DrupalKernel.php | 56 +++++++++- 2 files changed, 233 insertions(+), 1 deletion(-) create mode 100644 src/Bootstrap/ConsoleSettings.php diff --git a/src/Bootstrap/ConsoleSettings.php b/src/Bootstrap/ConsoleSettings.php new file mode 100644 index 000000000..b521768aa --- /dev/null +++ b/src/Bootstrap/ConsoleSettings.php @@ -0,0 +1,178 @@ +storage = $settings; + self::$instance = $this; + } + + /** + * Returns the settings instance. + * + * A singleton is used because this class is used before the container is + * available. + * + * @return \Drupal\Console\Bootstrap\ConsoleSettings + * + * @throws \BadMethodCallException + * Thrown when the settings instance has not been initialized yet. + */ + public static function getInstance() { + if (self::$instance === NULL) { + throw new \BadMethodCallException('ConsoleSettings::$instance is not initialized yet. Whatever you are trying to do, it might be too early for that. You could call ConsoleSettings::initialize(), but it is probably better to wait until it is called in the regular way. Also check for recursions.'); + } + return self::$instance; + } + + /** + * Protects creating with clone. + */ + private function __clone() { + } + + /** + * Prevents settings from being serialized. + */ + public function __sleep() { + throw new \LogicException('ConsoleSettings can not be serialized. This probably means you are serializing an object that has an indirect reference to the ConsoleSettings object. Adjust your code so that is not necessary.'); + } + + /** + * Returns a setting. + * + * ConsoleSettings can be set in settings.php in the $settings array and requested + * by this function. ConsoleSettings should be used over configuration for read-only, + * possibly low bootstrap configuration that is environment specific. + * + * @param string $name + * The name of the setting to return. + * @param mixed $default + * (optional) The default value to use if this setting is not set. + * + * @return mixed + * The value of the setting, the provided default if not set. + */ + public static function get($name, $default = NULL) { + return isset(self::$instance->storage[$name]) ? self::$instance->storage[$name] : $default; + } + + /** + * Bootstraps settings.php and the ConsoleSettings singleton. + * + * @param string $app_root + * The app root. + * @param string $site_path + * The current site path. + * @param \Composer\Autoload\ClassLoader $class_loader + * The class loader that is used for this request. Passed by reference and + * exposed to the local scope of settings.php, so as to allow it to be + * decorated with Symfony's ApcClassLoader, for example. + * + * @see default.settings.php + */ + public static function initialize($app_root, $site_path, &$class_loader) { + // Export these settings.php variables to the global namespace. + global $config_directories, $config; + $settings = []; + $config = []; + $databases = []; + + if (is_readable($app_root . '/' . $site_path . '/settings.php')) { + require $app_root . '/' . $site_path . '/settings.php'; + } + + // If datases is not set, check if drushrc.php contains valid setting (Aegir). + $items = array( + 'driver' => 'db_type', + 'database' => 'db_name', + 'username'=> 'db_user', + 'password' => 'db_passwd', + 'host' => 'db_host', + 'port' => 'db_port', + ); + + $db_settings_fail = TRUE; + foreach ($items as $key => $item) { + if (!empty($databases['default']['default'][$key])) { + $db_settings_fail = FALSE; + break; + } + } + + if ($db_settings_fail) { + if (is_readable($app_root . '/' . $site_path . '/settings.php')) { + require $app_root . '/' . $site_path . '/drushrc.php'; + foreach ($items as $key => $item) { + if (!empty($options[$item])) { + $databases['default']['default'][$key] = $options[$item]; + } + } + } + } + + // Initialize Database. + Database::setMultipleConnectionInfo($databases); + + // Initialize ConsoleSettings. + new ConsoleSettings($settings); + } + + /** + * Generates a prefix for APCu user cache keys. + * + * A standardized prefix is useful to allow visual inspection of an APCu user + * cache. By default, this method will produce a unique prefix per site using + * the hash salt. If the setting 'apcu_ensure_unique_prefix' is set to FALSE + * then if the caller does not provide a $site_path only the Drupal root will + * be used. This allows WebTestBase to use the same prefix ensuring that the + * number of APCu items created during a full test run is kept to a minimum. + * Additionally, if a multi site implementation does not use site specific + * module directories setting apcu_ensure_unique_prefix would allow the sites + * to share APCu cache items. + * + * @param $identifier + * An identifier for the prefix. For example, 'class_loader' or + * 'cache_backend'. + * + * @return string + * The prefix for APCu user cache keys. + */ + public static function getApcuPrefix($identifier, $root, $site_path = '') { + if (static::get('apcu_ensure_unique_prefix', TRUE)) { + return 'drupal.' . $identifier . '.' . \Drupal::VERSION . '.' . static::get('deployment_identifier') . '.' . hash_hmac('sha256', $identifier, static::get('hash_salt') . '.' . $root . '/' . $site_path); + } + return 'drupal.' . $identifier . '.' . \Drupal::VERSION . '.' . static::get('deployment_identifier') . '.' . Crypt::hashBase64($root . '/' . $site_path); + } + +} diff --git a/src/Bootstrap/DrupalKernel.php b/src/Bootstrap/DrupalKernel.php index 0bf201230..12b645974 100644 --- a/src/Bootstrap/DrupalKernel.php +++ b/src/Bootstrap/DrupalKernel.php @@ -5,6 +5,7 @@ use Symfony\Component\HttpFoundation\Request; use Drupal\Core\DrupalKernel as DrupalKernelBase; use Drupal\Core\DependencyInjection\ServiceModifierInterface; +use Drupal\Console\Bootstrap\ConsoleSettings; /** * Class DrupalKernel @@ -25,11 +26,64 @@ public static function createFromRequest(Request $request, $class_loader, $envir { $kernel = new static($environment, $class_loader, $allow_dumping, $app_root); static::bootEnvironment($app_root); - $kernel->initializeSettings($request); + $kernel->consoleInitializeSettings($request); $kernel->handle($request); return $kernel; } + /** + * @inheritdoc + */ + protected function consoleInitializeSettings(Request $request) { + $site_path = static::findSitePath($request); + $this->setSitePath($site_path); + $class_loader_class = get_class($this->classLoader); + ConsoleSettings::initialize($this->root, $site_path, $this->classLoader); + + // Initialize our list of trusted HTTP Host headers to protect against + // header attacks. + $host_patterns = ConsoleSettings::get('trusted_host_patterns', []); + if (PHP_SAPI !== 'cli' && !empty($host_patterns)) { + if (static::setupTrustedHosts($request, $host_patterns) === FALSE) { + throw new BadRequestHttpException('The provided host name is not valid for this server.'); + } + } + + // If the class loader is still the same, possibly + // upgrade to an optimized class loader. + if ($class_loader_class == get_class($this->classLoader) + && ConsoleSettings::get('class_loader_auto_detect', TRUE)) { + $prefix = ConsoleSettings::getApcuPrefix('class_loader', $this->root); + $loader = NULL; + + // We autodetect one of the following three optimized classloaders, if + // their underlying extension exists. + if (function_exists('apcu_fetch')) { + $loader = new ApcClassLoader($prefix, $this->classLoader); + } + elseif (extension_loaded('wincache')) { + $loader = new WinCacheClassLoader($prefix, $this->classLoader); + } + elseif (extension_loaded('xcache')) { + $loader = new XcacheClassLoader($prefix, $this->classLoader); + } + if (!empty($loader)) { + $this->classLoader->unregister(); + // The optimized classloader might be persistent and store cache misses. + // For example, once a cache miss is stored in APCu clearing it on a + // specific web-head will not clear any other web-heads. Therefore + // fallback to the composer class loader that only statically caches + // misses. + $old_loader = $this->classLoader; + $this->classLoader = $loader; + // Our class loaders are preprended to ensure they come first like the + // class loader they are replacing. + $old_loader->register(TRUE); + $loader->register(TRUE); + } + } + } + /** * @param \Drupal\Core\DependencyInjection\ServiceModifierInterface $serviceModifier */ From 4d397f016babd698f89adcfaa3a6c519a585dfe5 Mon Sep 17 00:00:00 2001 From: Thomas de Graaff Date: Sun, 3 Sep 2017 08:19:10 +0000 Subject: [PATCH 2/2] Fixes copy paste neglicence bug. --- src/Bootstrap/ConsoleSettings.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Bootstrap/ConsoleSettings.php b/src/Bootstrap/ConsoleSettings.php index b521768aa..bca97e5e6 100644 --- a/src/Bootstrap/ConsoleSettings.php +++ b/src/Bootstrap/ConsoleSettings.php @@ -131,7 +131,7 @@ public static function initialize($app_root, $site_path, &$class_loader) { } if ($db_settings_fail) { - if (is_readable($app_root . '/' . $site_path . '/settings.php')) { + if (is_readable($app_root . '/' . $site_path . '/drushrc.php')) { require $app_root . '/' . $site_path . '/drushrc.php'; foreach ($items as $key => $item) { if (!empty($options[$item])) {