diff --git a/core/lib/Drupal.php b/core/lib/Drupal.php
index 363cf12..1dadf42 100644
--- a/core/lib/Drupal.php
+++ b/core/lib/Drupal.php
@@ -5,10 +5,32 @@
  * Contains Drupal.
  */
 
+use Drupal\Core\DependencyInjection\PlaceholderContainer;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 use Drupal\Core\Url;
 
 /**
+ * Initialize \Drupal::$container with a placeholder object.
+ * See https://www.drupal.org/node/2363341
+ *
+ * This technique is the most reliable way to initialize static properties with
+ * non-trivial expressions. It should NOT be used for anything else. Also, the
+ * code being called MUST NOT have any side effects other than initializing the
+ * static properties.
+ *
+ * In general (e.g. in PSR-1), a PHP file should either declare symbols OR have
+ * side-effects, but not both. This specific case is ok only because the side
+ * effect applies to nothing else but the class declared in the same file, and
+ * it happens immediately after the class is being declared. A version of the
+ * class without this initialization applied is never available to the outside
+ * world.
+ *
+ * Note: PHP does not care whether this is called before or after the class
+ * declaration. It is called before only for better visibility.
+ */
+\Drupal::initStaticProperties(NULL);
+
+/**
  * Static Service Container wrapper.
  *
  * Generally, code in Drupal should accept its dependencies via either
@@ -93,7 +115,7 @@ class Drupal {
   const CORE_MINIMUM_SCHEMA_VERSION = 8000;
 
   /**
-   * The currently active container object.
+   * The currently active container object, or a placeholder container.
    *
    * @var \Symfony\Component\DependencyInjection\ContainerInterface
    */
@@ -108,18 +130,46 @@ class Drupal {
    *   environment does not leak into a test.
    */
   public static function setContainer(ContainerInterface $container = NULL) {
+    if (!isset($container)) {
+      // @todo Remove the NULL case, and use unsetContainer() instead.
+      $container = new PlaceholderContainer('\Drupal::$container was unset with setContainer(NULL).');
+    }
     static::$container = $container;
   }
 
   /**
+   * @param string|null $message
+   */
+  public static function unsetContainer($message = NULL) {
+    if (!isset($message)) {
+      $message = '\Drupal::$container was unset with \Drupal::unsetContainer().';
+    }
+    static::setContainer(new PlaceholderContainer($message));
+  }
+
+  /**
+   * Initializes the static properties. Called from within the class file.
+   */
+  public static function initStaticProperties() {
+    $message = '\Drupal::$container is not initialized yet. \Drupal::setContainer() must be called with a real container.';
+    static::setContainer(new PlaceholderContainer($message));
+  }
+
+  /**
    * Returns the currently active global container.
    *
    * @deprecated This method is only useful for the testing environment. It
    * should not be used otherwise.
    *
-   * @return \Symfony\Component\DependencyInjection\ContainerInterface
+   * @return \Symfony\Component\DependencyInjection\ContainerInterface|null
    */
   public static function getContainer() {
+    if (static::$container instanceof PlaceholderContainer) {
+      // Currently, some components depend on this method returning NULL if not
+      // initialized.
+      // @todo Throw an exception instead, and change all code that expects NULL.
+      return NULL;
+    }
     return static::$container;
   }
 
@@ -149,7 +199,7 @@ public static function service($id) {
    *   TRUE if the specified service exists, FALSE otherwise.
    */
   public static function hasService($id) {
-    return static::$container && static::$container->has($id);
+    return static::$container->has($id);
   }
 
   /**
@@ -159,7 +209,8 @@ public static function hasService($id) {
    *   TRUE if there is a currently active request object, FALSE otherwise.
    */
   public static function hasRequest() {
-    return static::$container && static::$container->has('request_stack') && static::$container->get('request_stack')->getCurrentRequest() !== NULL;
+    return static::$container->has('request_stack')
+      && static::$container->get('request_stack')->getCurrentRequest() !== NULL;
   }
 
   /**
@@ -469,6 +520,17 @@ public static function urlGenerator() {
    * the base path (like robots.txt) use Url::fromUri()->toString() with the
    * base:// scheme.
    *
+   * @param string $route_name
+   *   The name of the route
+   * @param array $route_parameters
+   *   An associative array of parameter names and values.
+   * @param array $options
+   *   (optional) An associative array of additional options,
+   *
+   * @return string
+   *   The generated URL for the given route.
+   *
+   * @see \Drupal\Core\Routing\UrlGeneratorInterface::generateFromRoute()
    * @see \Drupal\Core\Url
    * @see \Drupal\Core\Url::fromRoute()
    * @see \Drupal\Core\Url::fromUri()
@@ -493,6 +555,12 @@ public static function linkGenerator() {
    * generate() method. For detailed documentation, see
    * \Drupal\Core\Routing\LinkGeneratorInterface::generate().
    *
+   * @param string $text
+   * @param \Drupal\Core\Url $url
+   *
+   * @return string
+   *   An HTML string containing a link to the given route and parameters.
+   *
    * @see \Drupal\Core\Utility\LinkGeneratorInterface::generate()
    * @see \Drupal\Core\Url
    */
diff --git a/core/lib/Drupal/Core/DependencyInjection/ContainerNotInitializedException.php b/core/lib/Drupal/Core/DependencyInjection/ContainerNotInitializedException.php
new file mode 100644
index 0000000..6de8116
--- /dev/null
+++ b/core/lib/Drupal/Core/DependencyInjection/ContainerNotInitializedException.php
@@ -0,0 +1,15 @@
+<?php
+
+
+namespace Drupal\Core\DependencyInjection;
+
+/**
+ * Exception thrown when a method is called that requires a container, but the
+ * container is not initialized yet.
+ *
+ * @see \Drupal
+ * @see \Drupal\Core\DependencyInjection\PlaceholderContainer
+ */
+class ContainerNotInitializedException extends \Exception {
+
+}
diff --git a/core/lib/Drupal/Core/DependencyInjection/PlaceholderContainer.php b/core/lib/Drupal/Core/DependencyInjection/PlaceholderContainer.php
new file mode 100644
index 0000000..cdbf21c
--- /dev/null
+++ b/core/lib/Drupal/Core/DependencyInjection/PlaceholderContainer.php
@@ -0,0 +1,113 @@
+<?php
+
+
+namespace Drupal\Core\DependencyInjection;
+
+
+use Symfony\Component\DependencyInjection\ContainerInterface;
+use Symfony\Component\DependencyInjection\ScopeInterface;
+
+/**
+ * A placeholder container that throws an exception whenever it does anything.
+ *
+ * Used in
+ * @see \Drupal
+ */
+class PlaceholderContainer implements ContainerInterface {
+
+  /**
+   * Message for ContainerNotInitializedException.
+   * 
+   * @var string
+   */
+  protected $message;
+
+  /**
+   * Constructs a PlaceholderContainer object.
+   * 
+   * @param string $exception_message
+   *   Message for ContainerNotInitializedException.
+   */
+  public function __construct($exception_message = NULL) {
+    $this->message = isset($exception_message)
+      ? $exception_message
+      : 'Container not initialized.';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function set($id, $service, $scope = self::SCOPE_CONTAINER) {
+    throw new ContainerNotInitializedException($this->message);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function get($id, $invalidBehavior = self::EXCEPTION_ON_INVALID_REFERENCE) {
+    throw new ContainerNotInitializedException($this->message);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function has($id) {
+    return FALSE;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getParameter($name) {
+    throw new ContainerNotInitializedException($this->message);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function hasParameter($name) {
+    throw new ContainerNotInitializedException($this->message);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setParameter($name, $value) {
+    throw new ContainerNotInitializedException($this->message);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function enterScope($name) {
+    throw new ContainerNotInitializedException($this->message);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function leaveScope($name) {
+    throw new ContainerNotInitializedException($this->message);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function addScope(ScopeInterface $scope) {
+    throw new ContainerNotInitializedException($this->message);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function hasScope($name) {
+    throw new ContainerNotInitializedException($this->message);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function isScopeActive($name) {
+    throw new ContainerNotInitializedException($this->message);
+  }
+}
diff --git a/core/tests/Drupal/Tests/Core/DrupalContainerNotInitializedTest.php b/core/tests/Drupal/Tests/Core/DrupalContainerNotInitializedTest.php
new file mode 100644
index 0000000..60d84fc
--- /dev/null
+++ b/core/tests/Drupal/Tests/Core/DrupalContainerNotInitializedTest.php
@@ -0,0 +1,166 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Tests\Core\DrupalTest.
+ */
+
+namespace Drupal\Tests\Core;
+
+use Drupal\Core\DependencyInjection\ContainerNotInitializedException;
+use Drupal\Core\Url;
+
+/**
+ * Tests the case where Drupal::$container is not initialized.
+ *
+ * @group DrupalTest
+ */
+class DrupalContainerNotInitializedTest extends \PHPUnit_Framework_TestCase {
+
+  /**
+   * Tests the case where \Drupal::$container is not initialized.
+   *
+   * @dataProvider methodsWithReturnProvider
+   *
+   * @param string $method
+   *   The static method to call on \Drupal::
+   * @param mixed $expected
+   *   Expected return value.
+   * @param array $args
+   *   Arguments to pass into \Drupal::$method(..)
+   */
+  public function testContainerNotInitializedReturn($method, $expected, $args = array()) {
+    $result = call_user_func_array(['Drupal', $method], $args);
+    $this->assertEquals($expected, $result);
+  }
+
+  /**
+   * Tests the case where \Drupal::$container is not initialized.
+   *
+   * @dataProvider methodsWithExceptionProvider
+   *
+   * @param string $method
+   *   The static method to call on \Drupal::
+   * @param array $args
+   *   Arguments to pass into \Drupal::$method(..)
+   */
+  public function testContainerNotInitializedException($method, $args = array()) {
+    try {
+      call_user_func_array(['Drupal', $method], $args);
+    }
+    catch (ContainerNotInitializedException $e) {
+      $this->assertEquals(
+        '\Drupal::$container is not initialized yet. \Drupal::setContainer() must be called with a real container.',
+        $e->getMessage());
+      return;
+    }
+    $this->fail('\Drupal::service() should trigger an exception, if no container is available.');
+  }
+
+  /**
+   * Tests the case where \Drupal::$container was unset.
+   *
+   * @dataProvider methodsWithReturnProvider
+   *
+   * @param string $method
+   *   The static method to call on \Drupal::
+   * @param mixed $expected
+   *   Expected return value.
+   * @param array $args
+   *   Arguments to pass into \Drupal::$method(..)
+   */
+  public function testUnsetContainerReturn($method, $expected, $args = array()) {
+    \Drupal::unsetContainer(__METHOD__);
+    $result = call_user_func_array(['Drupal', $method], $args);
+    $this->assertEquals($expected, $result);
+  }
+
+  /**
+   * Tests the case where \Drupal::$container was unset.
+   *
+   * @dataProvider methodsWithExceptionProvider
+   *
+   * @param string $method
+   *   The static method to call on \Drupal::
+   * @param array $args
+   *   Arguments to pass into \Drupal::$method(..)
+   */
+  public function testUnsetContainerException($method, $args = array()) {
+    try {
+      \Drupal::unsetContainer(__METHOD__);
+      call_user_func_array(['Drupal', $method], $args);
+    }
+    catch (ContainerNotInitializedException $e) {
+      $this->assertEquals(__METHOD__, $e->getMessage());
+      return;
+    }
+    $this->fail('\Drupal::service() should trigger an exception, if no container is available.');
+  }
+
+  /**
+   * Data provider for two methods, see "@see" below.
+   *
+   * @return array[]
+   *
+   * @see testContainerNotInitializedReturn()
+   * @see testUnsetContainerReturn()
+   */
+  public function methodsWithReturnProvider() {
+    return array(
+      ['getContainer', NULL],
+      ['hasService', FALSE, ['test_service']],
+      ['hasRequest', FALSE],
+    );
+  }
+
+  /**
+   * Data provider for two methods, see "@see" below.
+   *
+   * @return array[]
+   *
+   * @see testContainerNotInitializedException()
+   * @see testUnsetContainerException()
+   */
+  public function methodsWithExceptionProvider() {
+    return array(
+      ['service', ['test_service']],
+      ['request'],
+      ['requestStack'],
+      ['routeMatch'],
+      ['currentUser'],
+      ['entityManager'],
+      ['database'],
+      ['cache', ['test']],
+      ['keyValueExpirable', ['test_collection']],
+      ['lock'],
+      ['config', ['test_config']],
+      ['configFactory'],
+      ['queue', ['test_queue', TRUE]],
+      ['keyValue', ['test_collection']],
+      ['state'],
+      ['httpClient'],
+      ['entityQuery', ['OR']],
+      ['entityQueryAggregate', ['test_entity', 'OR']],
+      ['flood', ['test_service']],
+      ['moduleHandler', ['test_service']],
+      ['typedDataManager', ['test_service']],
+      ['token'],
+      ['urlGenerator'],
+      ['url', ['test_route']],
+      ['linkGenerator'],
+      ['l', ['Test title', new Url('test_route')]],
+      ['translation'],
+      ['languageManager'],
+      ['csrfToken'],
+      ['transliteration'],
+      ['formBuilder'],
+      ['theme'],
+      ['isConfigSyncing'],
+      ['logger', ['test_channel']],
+      ['menuTree'],
+      ['pathValidator'],
+      ['accessManager'],
+    );
+  }
+
+}
