From 4fb0f9d8fd5269e96d04fe7650ee6e6ff1d6b585 Mon Sep 17 00:00:00 2001
From: webchick <drupal@webchick.net>
Date: Sat, 23 Aug 2014 13:19:53 -0700
Subject: [PATCH] Issue #2251113 by alexpott, damiankloip, Crell, Wim Leers,
 dawehner, sun: Use container parameters instead of settings.

---
 core/INSTALL.txt                              |  17 +-
 core/core.services.yml                        |  32 ++-
 core/includes/install.core.inc                | 188 +++++++++++-------
 core/lib/Drupal/Core/CoreServiceProvider.php  |  45 +----
 .../DependencyInjection/YamlFileLoader.php    |   8 +
 core/lib/Drupal/Core/DrupalKernel.php         |   3 +-
 .../Installer/InstallerServiceProvider.php    |   5 +
 .../Core/KeyValueStore/KeyValueFactory.php    |  28 +--
 .../Drupal/Core/Template/TwigEnvironment.php  |  12 +-
 .../modules/simpletest/src/KernelTestBase.php |   7 +-
 core/modules/simpletest/src/WebTestBase.php   |  23 +++
 .../DatabaseStorageExpirableTest.php          |   5 +-
 .../KeyValueStore/DatabaseStorageTest.php     |   5 +-
 .../Tests/KeyValueStore/MemoryStorageTest.php |   5 +-
 .../Tests/KeyValueStore/StorageTestBase.php   |   4 +-
 .../src/Tests/Theme/TwigDebugMarkupTest.php   |   8 +-
 .../src/Tests/Theme/TwigSettingsTest.php      |  24 ++-
 core/modules/system/system.install            |   8 +-
 sites/default/default.services.yml            |  53 +++++
 sites/example.settings.local.php              |  53 -----
 20 files changed, 317 insertions(+), 216 deletions(-)
 create mode 100644 sites/default/default.services.yml

diff --git a/core/INSTALL.txt b/core/INSTALL.txt
index 18804738344c..f985620ab1fa 100644
--- a/core/INSTALL.txt
+++ b/core/INSTALL.txt
@@ -147,26 +147,30 @@ INSTALLATION
 
    b. Missing settings file.
 
-      Drupal will try to automatically create a settings.php configuration file,
-      which is normally in the directory sites/default (to avoid problems when
-      upgrading, Drupal is not packaged with this file). If auto-creation fails,
-      you will need to create this file yourself, using the file
-      sites/default/default.settings.php as a template.
+      Drupal will try to automatically create settings.php and services.yml
+      files, which are normally in the directory sites/default (to avoid
+      problems when upgrading, Drupal is not packaged with this file). If
+      auto-creation of either file fails, you will need to create the file
+      yourself. Use the template sites/default/default.settings.php or
+      sites/default/default.services.yml respectively.
 
       For example, on a Unix/Linux command line, you can make a copy of the
-      default.settings.php file with the command:
+      default.settings.php and default.services.yml files with the commands:
 
         cp sites/default/default.settings.php sites/default/settings.php
+        cp sites/default/default.services.yml sites/default/services.yml
 
       Next, grant write privileges to the file to everyone (including the web
       server) with the command:
 
         chmod a+w sites/default/settings.php
+        chmod a+w sites/default/services.yml
 
       Be sure to set the permissions back after the installation is finished!
       Sample command:
 
         chmod go-w sites/default/settings.php
+        chmod go-w sites/default/services.yml
 
    c. Write permissions after install.
 
@@ -176,6 +180,7 @@ INSTALLATION
       from a Unix/Linux command line:
 
         chmod go-w sites/default/settings.php
+        chmod go-w sites/default/services.yml
         chmod go-w sites/default
 
 4. Verify that the site is working.
diff --git a/core/core.services.yml b/core/core.services.yml
index dec001f6ee96..c26834cc2315 100644
--- a/core/core.services.yml
+++ b/core/core.services.yml
@@ -1,3 +1,9 @@
+parameters:
+  twig.config: {}
+  factory.keyvalue:
+    default: keyvalue.database
+  factory.keyvalue.expirable:
+    default: keyvalue.expirable.database
 services:
   cache_factory:
     class: Drupal\Core\Cache\CacheFactory
@@ -156,13 +162,13 @@ services:
     arguments: ['@request_stack', '@url_generator']
   keyvalue:
     class: Drupal\Core\KeyValueStore\KeyValueFactory
-    arguments: ['@service_container', '@settings']
+    arguments: ['@service_container', '%factory.keyvalue%']
   keyvalue.database:
     class: Drupal\Core\KeyValueStore\KeyValueDatabaseFactory
     arguments: ['@serialization.phpserialize', '@database']
   keyvalue.expirable:
     class: Drupal\Core\KeyValueStore\KeyValueExpirableFactory
-    arguments: ['@service_container', '@settings']
+    arguments: ['@service_container', '%factory.keyvalue.expirable%']
   keyvalue.expirable.database:
     class: Drupal\Core\KeyValueStore\KeyValueDatabaseExpirableFactory
     arguments: ['@serialization.phpserialize', '@database']
@@ -922,6 +928,28 @@ services:
     arguments: ['@module_handler']
   info_parser:
     class: Drupal\Core\Extension\InfoParser
+  twig:
+    class: Drupal\Core\Template\TwigEnvironment
+    arguments: ['@twig.loader', '@module_handler', '@theme_handler', '%twig.config%']
+    tags:
+      - { name: service_collector, tag: 'twig.extension', call: addExtension }
+  twig.extension:
+    class: Drupal\Core\Template\TwigExtension
+    tags:
+      - { name: twig.extension, priority: 100 }
+    calls:
+      - [setGenerators, ['@url_generator']]
+  # @todo Figure out what to do about debugging functions.
+  # @see http://drupal.org/node/1804998
+  twig.extension.debug:
+    class: Twig_Extension_Debug
+    tags:
+      - { name: twig.extension }
+  twig.loader:
+    alias: twig.loader.filesystem
+  twig.loader.filesystem:
+    class: Twig_Loader_Filesystem
+    arguments: ['%app.root%']
   element_info:
     alias: plugin.manager.element_info
   file.mime_type.guesser:
diff --git a/core/includes/install.core.inc b/core/includes/install.core.inc
index 4cdc52945ee0..3a877a87c2b0 100644
--- a/core/includes/install.core.inc
+++ b/core/includes/install.core.inc
@@ -1886,67 +1886,88 @@ function install_check_requirements($install_state) {
   // Check the profile requirements.
   $requirements = drupal_check_profile($profile, $install_state);
 
-  // If Drupal is not set up already, we need to create a settings file.
-  if (!$install_state['settings_verified']) {
+  if ($install_state['settings_verified']) {
+    return $requirements;
+  }
+
+  // If Drupal is not set up already, we need to try to create the default
+  // settings and services files.
+  $default_files = array();
+  $default_files['settings.php'] = array(
+    'file' => 'settings.php',
+    'file_default' => 'default.settings.php',
+    'title_default' => t('Default settings file'),
+    'description_default' => t('The default settings file does not exist.'),
+    'title' => t('Settings file'),
+  );
+  $default_files['services.yml'] = array(
+    'file' => 'services.yml',
+    'file_default' => 'default.services.yml',
+    'title_default' => t('Default services file'),
+    'description_default' => t('The default services file does not exist.'),
+    'title' => t('Services file'),
+  );
+
+  foreach ($default_files as $default_file_info) {
     $readable = FALSE;
     $writable = FALSE;
     $conf_path = './' . conf_path(FALSE);
-    $settings_file = $conf_path . '/settings.php';
-    $default_settings_file = './sites/default/default.settings.php';
-    $file = $conf_path;
+    $file = $conf_path . "/{$default_file_info['file']}";
+    $default_file = "./sites/default/{$default_file_info['file_default']}";
     $exists = FALSE;
     // Verify that the directory exists.
     if (drupal_verify_install_file($conf_path, FILE_EXIST, 'dir')) {
-      // Check if a settings.php file already exists.
-      $file = $settings_file;
-      if (drupal_verify_install_file($settings_file, FILE_EXIST)) {
+      if (drupal_verify_install_file($file, FILE_EXIST)) {
         // If it does, make sure it is writable.
-        $readable = drupal_verify_install_file($settings_file, FILE_READABLE);
-        $writable = drupal_verify_install_file($settings_file, FILE_WRITABLE);
+        $readable = drupal_verify_install_file($file, FILE_READABLE);
+        $writable = drupal_verify_install_file($file, FILE_WRITABLE);
         $exists = TRUE;
       }
     }
 
-    // If default.settings.php does not exist, or is not readable, throw an
-    // error.
-    if (!drupal_verify_install_file($default_settings_file, FILE_EXIST|FILE_READABLE)) {
-      $requirements['default settings file exists'] = array(
-        'title'       => t('Default settings file'),
-        'value'       => t('The default settings file does not exist.'),
-        'severity'    => REQUIREMENT_ERROR,
-        'description' => t('The @drupal installer requires that the %default-file file not be modified in any way from the original download.', array('@drupal' => drupal_install_profile_distribution_name(), '%default-file' => $default_settings_file)),
+    // If the default $default_file does not exist, or is not readable,
+    // report an error.
+    if (!drupal_verify_install_file($default_file, FILE_EXIST | FILE_READABLE)) {
+      $requirements["default $file file exists"] = array(
+        'title' => $default_file_info['title_default'],
+        'value' => $default_file_info['description_default'],
+        'severity' => REQUIREMENT_ERROR,
+        'description' => t('The @drupal installer requires that the %default-file file not be modified in any way from the original download.', array(
+            '@drupal' => drupal_install_profile_distribution_name(),
+            '%default-file' => $default_file
+          )),
       );
     }
-    // Otherwise, if settings.php does not exist yet, we can try to copy
-    // default.settings.php to create it.
+    // Otherwise, if $file does not exist yet, we can try to copy
+    // $default_file to create it.
     elseif (!$exists) {
-      $copied = drupal_verify_install_file($conf_path, FILE_EXIST|FILE_WRITABLE, 'dir') && @copy($default_settings_file, $settings_file);
+      $copied = drupal_verify_install_file($conf_path, FILE_EXIST | FILE_WRITABLE, 'dir') && @copy($default_file, $file);
       if ($copied) {
-        // If the new settings file has the same owner as default.settings.php,
-        // this means default.settings.php is owned by the webserver user.
-        // This is an inherent security weakness because it allows a malicious
-        // webserver process to append arbitrary PHP code and then execute it.
-        // However, it is also a common configuration on shared hosting, and
-        // there is nothing Drupal can do to prevent it. In this situation,
-        // having settings.php also owned by the webserver does not introduce
-        // any additional security risk, so we keep the file in place.
-        if (fileowner($default_settings_file) === fileowner($settings_file)) {
-          $readable = drupal_verify_install_file($settings_file, FILE_READABLE);
-          $writable = drupal_verify_install_file($settings_file, FILE_WRITABLE);
+        // If the new $file file has the same owner as $default_file this means
+        // $default_file is owned by the webserver user. This is an inherent
+        // security weakness because it allows a malicious webserver process to
+        // append arbitrary PHP code and then execute it. However, it is also a
+        // common configuration on shared hosting, and there is nothing Drupal
+        // can do to prevent it. In this situation, having $file also owned by
+        // the webserver does not introduce any additional security risk, so we
+        // keep the file in place.
+        if (fileowner($default_file) === fileowner($file)) {
+          $readable = drupal_verify_install_file($file, FILE_READABLE);
+          $writable = drupal_verify_install_file($file, FILE_WRITABLE);
           $exists = TRUE;
         }
-        // If settings.php and default.settings.php have different owners, this
-        // probably means the server is set up "securely" (with the webserver
-        // running as its own user, distinct from the user who owns all the
-        // Drupal PHP files), although with either a group or world writable
-        // sites directory. Keeping settings.php owned by the webserver would
-        // therefore introduce a security risk. It would also cause a usability
-        // problem, since site owners who do not have root access to the file
-        // system would be unable to edit their settings file later on. We
-        // therefore must delete the file we just created and force the
-        // administrator to log on to the server and create it manually.
+        // If $file and $default_file have different owners, this probably means
+        // the server is set up "securely" (with the webserver running as its
+        // own user, distinct from the user who owns all the Drupal PHP files),
+        // although with either a group or world writable sites directory.
+        // Keeping $file owned by the webserver would therefore introduce a
+        // security risk. It would also cause a usability problem, since site
+        // owners who do not have root access to the file system would be unable
+        // to edit their settings file later on. We therefore must delete the
+        // file we just created and force the administrator to log on to the
+        // server and create it manually.
         else {
-          $deleted = @drupal_unlink($settings_file);
+          $deleted = @drupal_unlink($file);
           // We expect deleting the file to be successful (since we just
           // created it ourselves above), but if it fails somehow, we set a
           // variable so we can display a one-time error message to the
@@ -1956,57 +1977,76 @@ function install_check_requirements($install_state) {
           $exists = !$deleted;
           if ($exists) {
             $settings_file_ownership_error = TRUE;
-            $readable = drupal_verify_install_file($settings_file, FILE_READABLE);
-            $writable = drupal_verify_install_file($settings_file, FILE_WRITABLE);
+            $readable = drupal_verify_install_file($file, FILE_READABLE);
+            $writable = drupal_verify_install_file($file, FILE_WRITABLE);
           }
         }
       }
     }
 
-    // If settings.php does not exist, throw an error.
+    // If the $file does not exist, throw an error.
     if (!$exists) {
-      $requirements['settings file exists'] = array(
-        'title'       => t('Settings file'),
-        'value'       => t('The settings file does not exist.'),
-        'severity'    => REQUIREMENT_ERROR,
-        'description' => t('The @drupal installer requires that you create a settings file as part of the installation process. Copy the %default_file file to %file. More details about installing Drupal are available in <a href="@install_txt">INSTALL.txt</a>.', array('@drupal' => drupal_install_profile_distribution_name(), '%file' => $file, '%default_file' => $default_settings_file, '@install_txt' => base_path() . 'core/INSTALL.txt')),
+      $requirements["$file file exists"] = array(
+        'title' => $default_file_info['title'],
+        'value' => t('The %file does not exist.', array('%file' => $default_file_info['title'])),
+        'severity' => REQUIREMENT_ERROR,
+        'description' => t('The @drupal installer requires that you create a %file as part of the installation process. Copy the %default_file file to %file. More details about installing Drupal are available in <a href="@install_txt">INSTALL.txt</a>.', array(
+            '@drupal' => drupal_install_profile_distribution_name(),
+            '%file' => $file,
+            '%default_file' => $default_file,
+            '@install_txt' => base_path() . 'core/INSTALL.txt'
+          )),
       );
     }
     else {
-      $requirements['settings file exists'] = array(
-        'title'       => t('Settings file'),
-        'value'       => t('The %file file exists.', array('%file' => $file)),
+      $requirements["$file file exists"] = array(
+        'title' => $default_file_info['title'],
+        'value' => t('The %file exists.', array('%file' => $file)),
       );
-      // If settings.php is not readable, throw an error.
+      // If the $file is not readable, throw an error.
       if (!$readable) {
-        $requirements['settings file readable'] = array(
-          'title'       => t('Settings file'),
-          'value'       => t('The settings file is not readable.'),
-          'severity'    => REQUIREMENT_ERROR,
-          'description' => t('@drupal requires read permissions to %file at all times. If you are unsure how to grant file permissions, consult the <a href="@handbook_url">online handbook</a>.', array('@drupal' => drupal_install_profile_distribution_name(), '%file' => $file, '@handbook_url' => 'http://drupal.org/server-permissions')),
+        $requirements["$file file readable"] = array(
+          'title' => $default_file_info['title'],
+          'value' => t('The %file is not readable.', array('%file' => $default_file_info['title'])),
+          'severity' => REQUIREMENT_ERROR,
+          'description' => t('@drupal requires read permissions to %file at all times. If you are unsure how to grant file permissions, consult the <a href="@handbook_url">online handbook</a>.', array(
+              '@drupal' => drupal_install_profile_distribution_name(),
+              '%file' => $file,
+              '@handbook_url' => 'http://drupal.org/server-permissions'
+            )),
         );
       }
-      // If settings.php is not writable, throw an error.
+      // If the $file is not writable, throw an error.
       if (!$writable) {
-        $requirements['settings file writable'] = array(
-          'title'       => t('Settings file'),
-          'value'       => t('The settings file is not writable.'),
-          'severity'    => REQUIREMENT_ERROR,
-          'description' => t('The @drupal installer requires write permissions to %file during the installation process. If you are unsure how to grant file permissions, consult the <a href="@handbook_url">online handbook</a>.', array('@drupal' => drupal_install_profile_distribution_name(), '%file' => $file, '@handbook_url' => 'http://drupal.org/server-permissions')),
+        $requirements["$file file writeable"] = array(
+          'title' => $default_file_info['title'],
+          'value' => t('The %file is not writable.', array('%file' => $default_file_info['title'])),
+          'severity' => REQUIREMENT_ERROR,
+          'description' => t('The @drupal installer requires write permissions to %file during the installation process. If you are unsure how to grant file permissions, consult the <a href="@handbook_url">online handbook</a>.', array(
+              '@drupal' => drupal_install_profile_distribution_name(),
+              '%file' => $file,
+              '@handbook_url' => 'http://drupal.org/server-permissions'
+            )),
         );
       }
       else {
-        $requirements['settings file'] = array(
-          'title'       => t('Settings file'),
-          'value'       => t('The settings file is writable.'),
+        $requirements["$file file"] = array(
+          'title' => $default_file_info['title'],
+          'value' => t('The @file is writable.', array('@file' => $default_file_info['title'])),
         );
       }
       if (!empty($settings_file_ownership_error)) {
-        $requirements['settings file ownership'] = array(
-          'title'       => t('Settings file'),
-          'value'       => t('The settings file is owned by the web server.'),
-          'severity'    => REQUIREMENT_ERROR,
-          'description' => t('The @drupal installer failed to create a settings file with proper file ownership. Log on to your web server, remove the existing %file file, and create a new one by copying the %default_file file to %file. More details about installing Drupal are available in <a href="@install_txt">INSTALL.txt</a>. If you have problems with the file permissions on your server, consult the <a href="@handbook_url">online handbook</a>.', array('@drupal' => drupal_install_profile_distribution_name(), '%file' => $file, '%default_file' => $default_settings_file, '@install_txt' => base_path() . 'core/INSTALL.txt', '@handbook_url' => 'http://drupal.org/server-permissions')),
+        $requirements["$file file ownership"] = array(
+          'title' => $default_file_info['title'],
+          'value' => t('The @file is owned by the web server.', array('@file' => $default_file_info['title'])),
+          'severity' => REQUIREMENT_ERROR,
+          'description' => t('The @drupal installer failed to create a %file file with proper file ownership. Log on to your web server, remove the existing %file file, and create a new one by copying the %default_file file to %file. More details about installing Drupal are available in <a href="@install_txt">INSTALL.txt</a>. If you have problems with the file permissions on your server, consult the <a href="@handbook_url">online handbook</a>.', array(
+              '@drupal' => drupal_install_profile_distribution_name(),
+              '%file' => $file,
+              '%default_file' => $default_file,
+              '@install_txt' => base_path() . 'core/INSTALL.txt',
+              '@handbook_url' => 'http://drupal.org/server-permissions'
+            )),
         );
       }
     }
diff --git a/core/lib/Drupal/Core/CoreServiceProvider.php b/core/lib/Drupal/Core/CoreServiceProvider.php
index a006a9866b0c..8662d640e403 100644
--- a/core/lib/Drupal/Core/CoreServiceProvider.php
+++ b/core/lib/Drupal/Core/CoreServiceProvider.php
@@ -18,10 +18,6 @@
 use Drupal\Core\DependencyInjection\Compiler\RegisterAccessChecksPass;
 use Drupal\Core\DependencyInjection\Compiler\RegisterServicesForDestructionPass;
 use Drupal\Core\Plugin\PluginManagerPass;
-use Drupal\Core\Site\Settings;
-use Symfony\Component\DependencyInjection\ContainerInterface;
-use Symfony\Component\DependencyInjection\Reference;
-use Symfony\Component\DependencyInjection\Definition;
 use Symfony\Component\DependencyInjection\Compiler\PassConfig;
 
 /**
@@ -42,7 +38,8 @@ class CoreServiceProvider implements ServiceProviderInterface  {
    * {@inheritdoc}
    */
   public function register(ContainerBuilder $container) {
-    $this->registerTwig($container);
+    $container->setParameter('app.root', DRUPAL_ROOT);
+
     $this->registerUuid($container);
     $this->registerTest($container);
 
@@ -72,44 +69,6 @@ public function register(ContainerBuilder $container) {
     $container->addCompilerPass(new PluginManagerPass());
   }
 
-  /**
-   * Registers Twig services.
-   *
-   * This method is public and static so that it can be reused in the installer.
-   */
-  public static function registerTwig(ContainerBuilder $container) {
-    $container->register('twig.loader.filesystem', 'Twig_Loader_Filesystem')
-      ->addArgument(DRUPAL_ROOT);
-    $container->setAlias('twig.loader', 'twig.loader.filesystem');
-
-     $twig_extension = new Definition('Drupal\Core\Template\TwigExtension');
-     $twig_extension->addMethodCall('setGenerators', array(new Reference('url_generator')));
-
-    $container->register('twig', 'Drupal\Core\Template\TwigEnvironment')
-      ->addArgument(new Reference('twig.loader'))
-      ->addArgument(array(
-        // This is saved / loaded via drupal_php_storage().
-        // All files can be refreshed by clearing caches.
-        // @todo ensure garbage collection of expired files.
-        // When in the installer, twig_cache must be FALSE until we know the
-        // files folder is writable.
-        'cache' => drupal_installation_attempted() ? FALSE : Settings::get('twig_cache', TRUE),
-        'autoescape' => TRUE,
-        'debug' => Settings::get('twig_debug', FALSE),
-        'auto_reload' => Settings::get('twig_auto_reload', NULL),
-      ))
-      ->addArgument(new Reference('module_handler'))
-      ->addArgument(new Reference('theme_handler'))
-      ->addMethodCall('addExtension', array($twig_extension))
-      // @todo Figure out what to do about debugging functions.
-      // @see http://drupal.org/node/1804998
-      ->addMethodCall('addExtension', array(new Definition('Twig_Extension_Debug')))
-      ->addTag('service_collector', array(
-        'tag' => 'twig.extension',
-        'call' => 'addExtension',
-      ));
-  }
-
   /**
    * Determines and registers the UUID service.
    *
diff --git a/core/lib/Drupal/Core/DependencyInjection/YamlFileLoader.php b/core/lib/Drupal/Core/DependencyInjection/YamlFileLoader.php
index 75282d289d3a..d4ae53e2d0bf 100644
--- a/core/lib/Drupal/Core/DependencyInjection/YamlFileLoader.php
+++ b/core/lib/Drupal/Core/DependencyInjection/YamlFileLoader.php
@@ -52,6 +52,14 @@ public function __construct(ContainerBuilder $container)
       $this->container = $container;
     }
 
+    /**
+     * Resets the internal cache. This method is mostly useful for tests.
+     */
+    public static function reset()
+    {
+        static::$yaml = array();
+    }
+
     /**
      * Loads a Yaml file.
      *
diff --git a/core/lib/Drupal/Core/DrupalKernel.php b/core/lib/Drupal/Core/DrupalKernel.php
index 7b69341e63d2..da657c5f708d 100644
--- a/core/lib/Drupal/Core/DrupalKernel.php
+++ b/core/lib/Drupal/Core/DrupalKernel.php
@@ -529,7 +529,8 @@ public function discoverServiceProviders() {
     if ($container_yamls = Settings::get('container_yamls')) {
       $this->serviceYamls['site'] = $container_yamls;
     }
-    if (file_exists($site_services_yml = $this->getSitePath() . '/services.yml')) {
+    $site_services_yml = $this->getSitePath() . '/services.yml';
+    if (file_exists($site_services_yml) && is_readable($site_services_yml)) {
       $this->serviceYamls['site'][] = $site_services_yml;
     }
   }
diff --git a/core/lib/Drupal/Core/Installer/InstallerServiceProvider.php b/core/lib/Drupal/Core/Installer/InstallerServiceProvider.php
index 6c393b16c99b..d64bcbbb59f0 100644
--- a/core/lib/Drupal/Core/Installer/InstallerServiceProvider.php
+++ b/core/lib/Drupal/Core/Installer/InstallerServiceProvider.php
@@ -60,6 +60,11 @@ public function register(ContainerBuilder $container) {
    * {@inheritdoc}
    */
   public function alter(ContainerBuilder $container) {
+    // Disable Twig cache (php storage does not exist yet).
+    $twig_config = $container->getParameter('twig.config');
+    $twig_config['cache'] = FALSE;
+    $container->setParameter('twig.config', $twig_config);
+
     // Disable configuration overrides.
     // ConfigFactory would to try to load language overrides and InstallStorage
     // throws an exception upon trying to load a non-existing file.
diff --git a/core/lib/Drupal/Core/KeyValueStore/KeyValueFactory.php b/core/lib/Drupal/Core/KeyValueStore/KeyValueFactory.php
index 717ee04b5416..40851923440d 100644
--- a/core/lib/Drupal/Core/KeyValueStore/KeyValueFactory.php
+++ b/core/lib/Drupal/Core/KeyValueStore/KeyValueFactory.php
@@ -7,7 +7,6 @@
 
 namespace Drupal\Core\KeyValueStore;
 
-use Drupal\Core\Site\Settings;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 
 /**
@@ -29,7 +28,7 @@ class KeyValueFactory implements KeyValueFactoryInterface {
    * This is a setting name that will be used if the specific setting does not
    * exist. The setting value will be the id of a service.
    */
-  const DEFAULT_SETTING = 'keyvalue_default';
+  const DEFAULT_SETTING = 'default';
 
   /**
    * The default service id.
@@ -50,22 +49,15 @@ class KeyValueFactory implements KeyValueFactoryInterface {
    */
   protected $container;
 
-  /**
-   * The read-only settings container.
-   *
-   * @var \Drupal\Core\Site\Settings
-   */
-  protected $settings;
-
   /**
    * @param \Symfony\Component\DependencyInjection\ContainerInterface $container
    *   The service container.
-   * @param \Drupal\Core\Site\Settings $settings
-   *  The read-only settings container.
+   * @param array $options
+   *   (optional) Collection-specific storage override options.
    */
-  function __construct(ContainerInterface $container, Settings $settings) {
+  function __construct(ContainerInterface $container, array $options = array()) {
     $this->container = $container;
-    $this->settings = $settings;
+    $this->options = $options;
   }
 
   /**
@@ -73,14 +65,16 @@ function __construct(ContainerInterface $container, Settings $settings) {
    */
   public function get($collection) {
     if (!isset($this->stores[$collection])) {
-      if ($service_name = $this->settings->get(static::SPECIFIC_PREFIX . $collection)) {
+      if (isset($this->options[$collection])) {
+        $service_id = $this->options[$collection];
       }
-      elseif ($service_name = $this->settings->get(static::DEFAULT_SETTING)) {
+      elseif (isset($this->options[static::DEFAULT_SETTING])) {
+        $service_id = $this->options[static::DEFAULT_SETTING];
       }
       else {
-        $service_name = static::DEFAULT_SERVICE;
+        $service_id = static::DEFAULT_SERVICE;
       }
-      $this->stores[$collection] = $this->container->get($service_name)->get($collection);
+      $this->stores[$collection] = $this->container->get($service_id)->get($collection);
     }
     return $this->stores[$collection];
   }
diff --git a/core/lib/Drupal/Core/Template/TwigEnvironment.php b/core/lib/Drupal/Core/Template/TwigEnvironment.php
index d8da1ca33bde..fba0173613b0 100644
--- a/core/lib/Drupal/Core/Template/TwigEnvironment.php
+++ b/core/lib/Drupal/Core/Template/TwigEnvironment.php
@@ -41,7 +41,7 @@ class TwigEnvironment extends \Twig_Environment {
    * Constructs a TwigEnvironment object and stores cache and storage
    * internally.
    */
-  public function __construct(\Twig_LoaderInterface $loader = NULL, $options = array(), ModuleHandlerInterface $module_handler, ThemeHandlerInterface $theme_handler) {
+  public function __construct(\Twig_LoaderInterface $loader = NULL, ModuleHandlerInterface $module_handler, ThemeHandlerInterface $theme_handler, $options = array()) {
     // @todo Pass as arguments from the DIC.
     $this->cache_object = \Drupal::cache();
 
@@ -68,6 +68,16 @@ public function __construct(\Twig_LoaderInterface $loader = NULL, $options = arr
     $this->templateClasses = array();
     $this->stringLoader = new \Twig_Loader_String();
 
+    $options += array(
+      // @todo Ensure garbage collection of expired files.
+      'cache' => TRUE,
+      // @todo Remove this.
+      // @see http://drupal.org/node/1712444
+      'autoescape' => FALSE,
+      'debug' => FALSE,
+      'auto_reload' => NULL,
+    );
+
     parent::__construct($loader, $options);
   }
 
diff --git a/core/modules/simpletest/src/KernelTestBase.php b/core/modules/simpletest/src/KernelTestBase.php
index afea5908e5f6..949bd0459aea 100644
--- a/core/modules/simpletest/src/KernelTestBase.php
+++ b/core/modules/simpletest/src/KernelTestBase.php
@@ -15,6 +15,7 @@
 use Drupal\Core\Language\Language;
 use Drupal\Core\Site\Settings;
 use Drupal\Core\Entity\Schema\EntitySchemaProviderInterface;
+use Symfony\Component\DependencyInjection\Parameter;
 use Symfony\Component\DependencyInjection\Reference;
 use Symfony\Component\HttpFoundation\Request;
 
@@ -260,7 +261,9 @@ public function containerBuild(ContainerBuilder $container) {
       ->addArgument(Database::getConnection())
       ->addArgument('config');
 
-    $this->settingsSet('keyvalue_default', 'keyvalue.memory');
+    $keyvalue_options = $container->getParameter('factory.keyvalue') ?: array();
+    $keyvalue_options['default'] = 'keyvalue.memory';
+    $container->setParameter('factory.keyvalue', $keyvalue_options);
     $container->set('keyvalue.memory', $this->keyValueFactory);
     if (!$container->has('keyvalue')) {
       // TestBase::setUp puts a completely empty container in
@@ -280,7 +283,7 @@ public function containerBuild(ContainerBuilder $container) {
       $container
         ->register('keyvalue', 'Drupal\Core\KeyValueStore\KeyValueFactory')
         ->addArgument(new Reference('service_container'))
-        ->addArgument(new Reference('settings'));
+        ->addArgument(new Parameter('factory.keyvalue'));
 
       $container->register('state', 'Drupal\Core\State\State')
         ->addArgument(new Reference('keyvalue'));
diff --git a/core/modules/simpletest/src/WebTestBase.php b/core/modules/simpletest/src/WebTestBase.php
index a12c31020ecf..05467d2a19c6 100644
--- a/core/modules/simpletest/src/WebTestBase.php
+++ b/core/modules/simpletest/src/WebTestBase.php
@@ -8,9 +8,11 @@
 namespace Drupal\simpletest;
 
 use Drupal\Component\Serialization\Json;
+use Drupal\Component\Serialization\Yaml;
 use Drupal\Component\Utility\Crypt;
 use Drupal\Component\Utility\NestedArray;
 use Drupal\Component\Utility\String;
+use Drupal\Core\DependencyInjection\YamlFileLoader;
 use Drupal\Core\DrupalKernel;
 use Drupal\Core\Database\Database;
 use Drupal\Core\Database\ConnectionNotDefinedException;
@@ -812,6 +814,7 @@ protected function setUp() {
     // Not using File API; a potential error must trigger a PHP warning.
     $directory = DRUPAL_ROOT . '/' . $this->siteDirectory;
     copy(DRUPAL_ROOT . '/sites/default/default.settings.php', $directory . '/settings.php');
+    copy(DRUPAL_ROOT . '/sites/default/default.services.yml', $directory . '/services.yml');
 
     // All file system paths are created by System module during installation.
     // @see system_requirements()
@@ -1018,6 +1021,26 @@ protected function writeSettings(array $settings) {
     drupal_rewrite_settings($settings, $filename);
   }
 
+  /**
+   * Changes parameters in the services.yml file.
+   *
+   * @param $name
+   *   The name of the parameter.
+   * @param $value
+   *   The value of the parameter.
+   */
+  protected function setContainerParameter($name, $value) {
+    $filename = $this->siteDirectory . '/services.yml';
+    chmod($filename, 0666);
+
+    $services = Yaml::decode(file_get_contents($filename));
+    $services['parameters'][$name] = $value;
+    file_put_contents($filename, Yaml::encode($services));
+
+    // Clear the YML file cache.
+    YamlFileLoader::reset();
+  }
+
   /**
    * Queues custom translations to be written to settings.php.
    *
diff --git a/core/modules/system/src/Tests/KeyValueStore/DatabaseStorageExpirableTest.php b/core/modules/system/src/Tests/KeyValueStore/DatabaseStorageExpirableTest.php
index b18befa45ba4..f83584d6871a 100644
--- a/core/modules/system/src/Tests/KeyValueStore/DatabaseStorageExpirableTest.php
+++ b/core/modules/system/src/Tests/KeyValueStore/DatabaseStorageExpirableTest.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\system\Tests\KeyValueStore;
 
+use Drupal\Core\KeyValueStore\KeyValueFactory;
 use Symfony\Component\DependencyInjection\Reference;
 
 /**
@@ -33,7 +34,9 @@ protected function setUp() {
       ->addArgument(new Reference('database'));
     $this->container
       ->register('serialization.phpserialize', 'Drupal\Component\Serialization\PhpSerialize');
-    $this->settingsSet('keyvalue_expirable_default', 'keyvalue.expirable.database');
+    $parameter = array();
+    $parameter[KeyValueFactory::DEFAULT_SETTING] = 'keyvalue.expirable.database';
+    $this->container->setParameter('factory.keyvalue.expirable', $parameter);
   }
 
   protected function tearDown() {
diff --git a/core/modules/system/src/Tests/KeyValueStore/DatabaseStorageTest.php b/core/modules/system/src/Tests/KeyValueStore/DatabaseStorageTest.php
index f3f61be804d7..3180ac838602 100644
--- a/core/modules/system/src/Tests/KeyValueStore/DatabaseStorageTest.php
+++ b/core/modules/system/src/Tests/KeyValueStore/DatabaseStorageTest.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\system\Tests\KeyValueStore;
 
+use Drupal\Core\KeyValueStore\KeyValueFactory;
 use Symfony\Component\DependencyInjection\Reference;
 
 /**
@@ -32,7 +33,9 @@ protected function setUp() {
       ->addArgument(new Reference('database'));
     $this->container
       ->register('serialization.phpserialize', 'Drupal\Component\Serialization\PhpSerialize');
-    $this->settingsSet('keyvalue_default', 'keyvalue.database');
+    $parameter = array();
+    $parameter[KeyValueFactory::DEFAULT_SETTING] = 'keyvalue.database';
+    $this->container->setParameter('factory.keyvalue', $parameter);
   }
 
   protected function tearDown() {
diff --git a/core/modules/system/src/Tests/KeyValueStore/MemoryStorageTest.php b/core/modules/system/src/Tests/KeyValueStore/MemoryStorageTest.php
index 10b2a15601d2..770d758130a3 100644
--- a/core/modules/system/src/Tests/KeyValueStore/MemoryStorageTest.php
+++ b/core/modules/system/src/Tests/KeyValueStore/MemoryStorageTest.php
@@ -6,6 +6,7 @@
  */
 
 namespace Drupal\system\Tests\KeyValueStore;
+use Drupal\Core\KeyValueStore\KeyValueFactory;
 
 /**
  * Tests the key-value memory storage.
@@ -25,7 +26,9 @@ protected function setUp() {
     parent::setUp();
     $this->container
       ->register('keyvalue.memory', 'Drupal\Core\KeyValueStore\KeyValueMemoryFactory');
-    $this->settingsSet('keyvalue_default', 'keyvalue.memory');
+    $parameter = array();
+    $parameter[KeyValueFactory::DEFAULT_SETTING] = 'keyvalue.memory';
+    $this->container->setParameter('factory.keyvalue', $parameter);
   }
 
 }
diff --git a/core/modules/system/src/Tests/KeyValueStore/StorageTestBase.php b/core/modules/system/src/Tests/KeyValueStore/StorageTestBase.php
index 0190e6b494c2..2f70e0096643 100644
--- a/core/modules/system/src/Tests/KeyValueStore/StorageTestBase.php
+++ b/core/modules/system/src/Tests/KeyValueStore/StorageTestBase.php
@@ -58,11 +58,11 @@ protected function setUp() {
     $this->container
       ->register('keyvalue', 'Drupal\Core\KeyValueStore\KeyValueFactory')
       ->addArgument(new Reference('service_container'))
-      ->addArgument(new Reference('settings'));
+      ->addArgument('%factory.keyvalue%');
     $this->container
       ->register('keyvalue.expirable', 'Drupal\Core\KeyValueStore\KeyValueExpirableFactory')
       ->addArgument(new Reference('service_container'))
-      ->addArgument(new Reference('settings'));
+      ->addArgument('%factory.keyvalue.expirable%');
     // Define two data collections,
     $this->collections = array(0 => 'zero', 1 => 'one');
 
diff --git a/core/modules/system/src/Tests/Theme/TwigDebugMarkupTest.php b/core/modules/system/src/Tests/Theme/TwigDebugMarkupTest.php
index 2836c2ca266a..efb081803c78 100644
--- a/core/modules/system/src/Tests/Theme/TwigDebugMarkupTest.php
+++ b/core/modules/system/src/Tests/Theme/TwigDebugMarkupTest.php
@@ -31,7 +31,9 @@ function testTwigDebugMarkup() {
     theme_enable(array('test_theme'));
     \Drupal::config('system.theme')->set('default', 'test_theme')->save();
     // Enable debug, rebuild the service container, and clear all caches.
-    $this->settingsSet('twig_debug', TRUE);
+    $parameters = $this->container->getParameter('twig.config');
+    $parameters['debug'] = TRUE;
+    $this->setContainerParameter('twig.config', $parameters);
     $this->rebuildContainer();
     $this->resetAll();
 
@@ -67,7 +69,9 @@ function testTwigDebugMarkup() {
     $this->assertTrue(strpos($output, '* node--foo--bar' . $extension . PHP_EOL . '   * node--foo' . $extension . PHP_EOL . '   * node--3--full' . $extension . PHP_EOL . '   * node--3' . $extension . PHP_EOL . '   * node--page--full' . $extension . PHP_EOL . '   * node--page' . $extension . PHP_EOL . '   * node--full' . $extension . PHP_EOL . '   x node' . $extension) !== FALSE, 'Suggested template files found in order and base template shown as current template.');
 
     // Disable debug, rebuild the service container, and clear all caches.
-    $this->settingsSet('twig_debug', FALSE);
+    $parameters = $this->container->getParameter('twig.config');
+    $parameters['debug'] = FALSE;
+    $this->setContainerParameter('twig.config', $parameters);
     $this->rebuildContainer();
     $this->resetAll();
 
diff --git a/core/modules/system/src/Tests/Theme/TwigSettingsTest.php b/core/modules/system/src/Tests/Theme/TwigSettingsTest.php
index 277eef51a575..e57043234605 100644
--- a/core/modules/system/src/Tests/Theme/TwigSettingsTest.php
+++ b/core/modules/system/src/Tests/Theme/TwigSettingsTest.php
@@ -29,14 +29,18 @@ class TwigSettingsTest extends WebTestBase {
    */
   function testTwigAutoReloadOverride() {
     // Enable auto reload and rebuild the service container.
-    $this->settingsSet('twig_auto_reload', TRUE);
+    $parameters = $this->container->getParameter('twig.config');
+    $parameters['auto_reload'] = TRUE;
+    $this->setContainerParameter('twig.config', $parameters);
     $this->rebuildContainer();
 
     // Check isAutoReload() via the Twig service container.
     $this->assertTrue($this->container->get('twig')->isAutoReload(), 'Automatic reloading of Twig templates enabled.');
 
     // Disable auto reload and check the service container again.
-    $this->settingsSet('twig_auto_reload', FALSE);
+    $parameters = $this->container->getParameter('twig.config');
+    $parameters['auto_reload'] = FALSE;
+    $this->setContainerParameter('twig.config', $parameters);
     $this->rebuildContainer();
 
     $this->assertFalse($this->container->get('twig')->isAutoReload(), 'Automatic reloading of Twig templates disabled.');
@@ -47,7 +51,9 @@ function testTwigAutoReloadOverride() {
    */
   function testTwigDebugOverride() {
     // Enable debug and rebuild the service container.
-    $this->settingsSet('twig_debug', TRUE);
+    $parameters = $this->container->getParameter('twig.config');
+    $parameters['debug'] = TRUE;
+    $this->setContainerParameter('twig.config', $parameters);
     $this->rebuildContainer();
 
     // Check isDebug() via the Twig service container.
@@ -55,12 +61,16 @@ function testTwigDebugOverride() {
     $this->assertTrue($this->container->get('twig')->isAutoReload(), 'Twig automatic reloading is enabled when debug is enabled.');
 
     // Override auto reload when debug is enabled.
-    $this->settingsSet('twig_auto_reload', FALSE);
+    $parameters = $this->container->getParameter('twig.config');
+    $parameters['auto_reload'] = FALSE;
+    $this->setContainerParameter('twig.config', $parameters);
     $this->rebuildContainer();
     $this->assertFalse($this->container->get('twig')->isAutoReload(), 'Twig automatic reloading can be disabled when debug is enabled.');
 
     // Disable debug and check the service container again.
-    $this->settingsSet('twig_debug', FALSE);
+    $parameters = $this->container->getParameter('twig.config');
+    $parameters['debug'] = FALSE;
+    $this->setContainerParameter('twig.config', $parameters);
     $this->rebuildContainer();
 
     $this->assertFalse($this->container->get('twig')->isDebug(), 'Twig debug disabled.');
@@ -98,7 +108,9 @@ function testTwigCacheOverride() {
     $this->assertTrue(PhpStorageFactory::get('twig')->exists($cache_filename), 'Cached Twig template found.');
 
     // Disable the Twig cache and rebuild the service container.
-    $this->settingsSet('twig_cache', FALSE);
+    $parameters = $this->container->getParameter('twig.config');
+    $parameters['cache'] = FALSE;
+    $this->setContainerParameter('twig.config', $parameters);
     $this->rebuildContainer();
 
     // This should return false after rebuilding the service container.
diff --git a/core/modules/system/system.install b/core/modules/system/system.install
index 4c945167538e..8e7b534c95cb 100644
--- a/core/modules/system/system.install
+++ b/core/modules/system/system.install
@@ -219,7 +219,7 @@ function system_requirements($phase) {
     if (!drupal_verify_install_file($conf_path, FILE_NOT_WRITABLE, 'dir')) {
       $conf_errors[] = t("The directory %file is not protected from modifications and poses a security risk. You must change the directory's permissions to be non-writable.", array('%file' => $conf_path));
     }
-    foreach (array('settings.php', 'settings.local.php') as $conf_file) {
+    foreach (array('settings.php', 'settings.local.php', 'services.yml') as $conf_file) {
       $full_path = $conf_path . '/' . $conf_file;
       if (file_exists($full_path) && !drupal_verify_install_file($full_path, FILE_EXIST|FILE_READABLE|FILE_NOT_WRITABLE)) {
         $conf_errors[] = t("The file %file is not protected from modifications and poses a security risk. You must change the file's permissions to be non-writable.", array('%file' => $full_path));
@@ -236,18 +236,18 @@ function system_requirements($phase) {
         );
         $description = drupal_render($item_list);
       }
-      $requirements['settings.php'] = array(
+      $requirements['configuration_files'] = array(
         'value' => t('Not protected'),
         'severity' => REQUIREMENT_ERROR,
         'description' => $description,
       );
     }
     else {
-      $requirements['settings.php'] = array(
+      $requirements['configuration_files'] = array(
         'value' => t('Protected'),
       );
     }
-    $requirements['settings.php']['title'] = t('Configuration files');
+    $requirements['configuration_files']['title'] = t('Configuration files');
   }
 
   // Test the contents of the .htaccess files.
diff --git a/sites/default/default.services.yml b/sites/default/default.services.yml
new file mode 100644
index 000000000000..280eb43d9bca
--- /dev/null
+++ b/sites/default/default.services.yml
@@ -0,0 +1,53 @@
+parameters:
+  twig.config:
+    # Twig debugging:
+    #
+    # When debugging is enabled:
+    # - The markup of each Twig template is surrounded by HTML comments that
+    #   contain theming information, such as template file name suggestions.
+    # - Note that this debugging markup will cause automated tests that directly
+    #  check rendered HTML to fail. When running automated tests, 'twig_debug'
+    #   should be set to FALSE.
+    # - The dump() function can be used in Twig templates to output information
+    #   about template variables.
+    # - Twig templates are automatically recompiled whenever the source code
+    #  changes (see twig_auto_reload below).
+    #
+    # For more information about debugging Twig templates, see
+    # http://drupal.org/node/1906392.
+    #
+    # Not recommended in production environments
+    # @default false
+    debug: false
+    # Twig auto-reload:
+    #
+    # Automatically recompile Twig templates whenever the source code changes.
+    # If you don't provide a value for twig_auto_reload, it will be determined
+    # based on the value of twig_debug.
+    #
+    #  Not recommended in production environments
+    # @default null
+    auto_reload: null
+    # Twig cache:
+    #
+    # By default, Twig templates will be compiled and stored in the filesystem
+    # to increase performance. Disabling the Twig cache will recompile the
+    # templates from source each time they are used. In most cases the
+    # twig_auto_reload setting above should be enabled rather than disabling the
+    # Twig cache.
+    #
+    # Not recommended in production environments
+    # @default true
+    cache: true
+  factory.keyvalue:
+    {}
+    # Default key/value storage service to use.
+    # @default keyvalue.database
+    #default: keyvalue.database
+    # Collection-specific overrides.
+    #state: keyvalue.database
+  factory.keyvalue.expirable:
+    {}
+    # Default key/value expirable storage service to use.
+    # @default keyvalue.database.expirable
+    #default: keyvalue.database.expirable
diff --git a/sites/example.settings.local.php b/sites/example.settings.local.php
index 3fac75760922..6bf2fd705e44 100644
--- a/sites/example.settings.local.php
+++ b/sites/example.settings.local.php
@@ -46,56 +46,3 @@
  * using these parameters in a request to rebuild.php.
  */
 $settings['rebuild_access'] = TRUE;
-
-/**
- * Twig debugging:
- *
- * When debugging is enabled:
- * - The markup of each Twig template is surrounded by HTML comments that
- *   contain theming information, such as template file name suggestions.
- * - Note that this debugging markup will cause automated tests that directly
- *   check rendered HTML to fail. When running automated tests, 'twig_debug'
- *   should be set to FALSE.
- * - The dump() function can be used in Twig templates to output information
- *   about template variables.
- * - Twig templates are automatically recompiled whenever the source code
- *   changes (see twig_auto_reload below).
- *
- * Note: changes to this setting will only take effect once the cache is
- * cleared.
- *
- * For more information about debugging Twig templates, see
- * http://drupal.org/node/1906392.
- *
- * Not recommended in production environments (Default: FALSE).
- */
-# $settings['twig_debug'] = TRUE;
-
-/**
- * Twig auto-reload:
- *
- * Automatically recompile Twig templates whenever the source code changes. If
- * you don't provide a value for twig_auto_reload, it will be determined based
- * on the value of twig_debug.
- *
- * Note: changes to this setting will only take effect once the cache is
- * cleared.
- *
- * Not recommended in production environments (Default: NULL).
- */
-# $settings['twig_auto_reload'] = TRUE;
-
-/**
- * Twig cache:
- *
- * By default, Twig templates will be compiled and stored in the filesystem to
- * increase performance. Disabling the Twig cache will recompile the templates
- * from source each time they are used. In most cases the twig_auto_reload
- * setting above should be enabled rather than disabling the Twig cache.
- *
- * Note: changes to this setting will only take effect once the cache is
- * cleared.
- *
- * Not recommended in production environments (Default: TRUE).
- */
-# $settings['twig_cache'] = FALSE;
-- 
GitLab