diff --git a/core/lib/Drupal/Component/Annotation/Plugin/Discovery/AnnotatedClassDiscovery.php b/core/lib/Drupal/Component/Annotation/Plugin/Discovery/AnnotatedClassDiscovery.php
index b04e39d..e726ed6 100644
--- a/core/lib/Drupal/Component/Annotation/Plugin/Discovery/AnnotatedClassDiscovery.php
+++ b/core/lib/Drupal/Component/Annotation/Plugin/Discovery/AnnotatedClassDiscovery.php
@@ -8,6 +8,7 @@
 namespace Drupal\Component\Annotation\Plugin\Discovery;
 
 use Drupal\Component\Annotation\AnnotationInterface;
+use Drupal\Component\Plugin\Discovery\DiscoveryBase;
 use Drupal\Component\Plugin\Discovery\DiscoveryInterface;
 use Drupal\Component\Annotation\Reflection\MockFileFinder;
 use Doctrine\Common\Annotations\SimpleAnnotationReader;
@@ -17,7 +18,7 @@
 /**
  * Defines a discovery mechanism to find annotated plugins in PSR-0 namespaces.
  */
-class AnnotatedClassDiscovery implements DiscoveryInterface {
+class AnnotatedClassDiscovery extends DiscoveryBase implements DiscoveryInterface {
 
   /**
    * The namespaces within which to find plugin classes.
@@ -76,14 +77,6 @@ protected function getAnnotationReader() {
   }
 
   /**
-   * Implements Drupal\Component\Plugin\Discovery\DiscoveryInterface::getDefinition().
-   */
-  public function getDefinition($plugin_id) {
-    $plugins = $this->getDefinitions();
-    return isset($plugins[$plugin_id]) ? $plugins[$plugin_id] : NULL;
-  }
-
-  /**
    * Implements Drupal\Component\Plugin\Discovery\DiscoveryInterface::getDefinitions().
    */
   public function getDefinitions() {
diff --git a/core/lib/Drupal/Component/Plugin/Discovery/DerivativeDiscoveryDecorator.php b/core/lib/Drupal/Component/Plugin/Discovery/DerivativeDiscoveryDecorator.php
index 16281df..e84521c 100644
--- a/core/lib/Drupal/Component/Plugin/Discovery/DerivativeDiscoveryDecorator.php
+++ b/core/lib/Drupal/Component/Plugin/Discovery/DerivativeDiscoveryDecorator.php
@@ -39,10 +39,10 @@ public function __construct(DiscoveryInterface $decorated) {
    *   Thrown if the 'derivative' class specified in the plugin definition does
    *   not implement \Drupal\Component\Plugin\Derivative\DerivativeInterface.
    */
-  public function getDefinition($plugin_id) {
+  public function getDefinition($plugin_id, $exception_on_invalid = TRUE) {
     list($base_plugin_id, $derivative_id) = $this->decodePluginId($plugin_id);
 
-    $plugin_definition = $this->decorated->getDefinition($base_plugin_id);
+    $plugin_definition = $this->decorated->getDefinition($base_plugin_id, $exception_on_invalid);
     if (isset($plugin_definition)) {
       $derivative_fetcher = $this->getDerivativeFetcher($base_plugin_id, $plugin_definition);
       if ($derivative_fetcher) {
diff --git a/core/lib/Drupal/Component/Plugin/Discovery/DiscoveryBase.php b/core/lib/Drupal/Component/Plugin/Discovery/DiscoveryBase.php
new file mode 100644
index 0000000..42907c3
--- /dev/null
+++ b/core/lib/Drupal/Component/Plugin/Discovery/DiscoveryBase.php
@@ -0,0 +1,57 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Component\Plugin\Discovery\DiscoveryBase.
+ */
+
+namespace Drupal\Component\Plugin\Discovery;
+
+use Drupal\Component\Plugin\Exception\UnknownPluginException;
+
+/**
+ * Contains a base class for discovery.
+ *
+ * @todo Replace with a trait.
+ */
+abstract class DiscoveryBase implements DiscoveryInterface {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getDefinition($plugin_id, $exception_on_invalid = TRUE) {
+    $definitions = $this->getDefinitions();
+    return $this->doGetDefinition($definitions, $plugin_id, $exception_on_invalid);
+  }
+
+  /**
+   * Gets a specific plugin definition.
+   *
+   * @param array $definitions
+   *   An array of the available plugin definitions.
+   * @param string $plugin_id
+   *   A plugin id.
+   * @param bool $exception_on_invalid
+   *   (optional) If TRUE, an invalid plugin ID will throw an exception.
+   *   Defaults to FALSE.
+   *
+   * @return array|null
+   *   A plugin definition, or NULL if the plugin ID is invalid and
+   *   $exception_on_invalid is TRUE.
+   *
+   * @throws \Drupal\Component\Plugin\Exception\UnknownPluginException
+   *   Thrown if $plugin_id is invalid and $exception_on_invalid is TRUE.
+   */
+  protected function doGetDefinition(array $definitions, $plugin_id, $exception_on_invalid) {
+    // Avoid using a ternary that would create a copy of the array.
+    if (isset($definitions[$plugin_id])) {
+      return $definitions[$plugin_id];
+    }
+    elseif (!$exception_on_invalid) {
+      return NULL;
+    }
+
+    throw new UnknownPluginException($plugin_id, sprintf('The "%s" plugin does not exist.', $plugin_id));
+  }
+
+}
diff --git a/core/lib/Drupal/Component/Plugin/Discovery/DiscoveryCachedBase.php b/core/lib/Drupal/Component/Plugin/Discovery/DiscoveryCachedBase.php
new file mode 100644
index 0000000..be151d5
--- /dev/null
+++ b/core/lib/Drupal/Component/Plugin/Discovery/DiscoveryCachedBase.php
@@ -0,0 +1,36 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Component\Plugin\Discovery\DiscoveryCachedBase.
+ */
+
+namespace Drupal\Component\Plugin\Discovery;
+
+/**
+ * Contains a base class for statically cached discovery.
+ *
+ * @todo Replace with a trait.
+ */
+abstract class DiscoveryCachedBase extends DiscoveryBase {
+
+  /**
+   * Cached definitions array.
+   *
+   * @var array
+   */
+  protected $definitions;
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getDefinition($plugin_id, $exception_on_invalid = TRUE) {
+    // Fetch definitions if they're not loaded yet.
+    if (!isset($this->definitions)) {
+      $this->getDefinitions();
+    }
+
+    return $this->doGetDefinition($this->definitions, $plugin_id, $exception_on_invalid);
+  }
+
+}
diff --git a/core/lib/Drupal/Component/Plugin/Discovery/DiscoveryInterface.php b/core/lib/Drupal/Component/Plugin/Discovery/DiscoveryInterface.php
index aaf4743..dc4adef 100644
--- a/core/lib/Drupal/Component/Plugin/Discovery/DiscoveryInterface.php
+++ b/core/lib/Drupal/Component/Plugin/Discovery/DiscoveryInterface.php
@@ -18,11 +18,18 @@
    *
    * @param string $plugin_id
    *   A plugin id.
+   * @param bool $exception_on_invalid
+   *   (optional) If TRUE, an invalid plugin ID will throw an exception.
+   *   Defaults to FALSE.
    *
    * @return array|null
-   *   A plugin definition, or NULL if no definition was found for $plugin_id.
+   *   A plugin definition, or NULL if the plugin ID is invalid and
+   *   $exception_on_invalid is TRUE.
+   *
+   * @throws \InvalidArgumentException
+   *   Thrown if $plugin_id is invalid and $exception_on_invalid is TRUE.
    */
-  public function getDefinition($plugin_id);
+  public function getDefinition($plugin_id, $exception_on_invalid = TRUE);
 
   /**
    * Gets the definition of all plugins for this type.
diff --git a/core/lib/Drupal/Component/Plugin/Discovery/ProcessDecorator.php b/core/lib/Drupal/Component/Plugin/Discovery/ProcessDecorator.php
index fee6518..a262fa2 100644
--- a/core/lib/Drupal/Component/Plugin/Discovery/ProcessDecorator.php
+++ b/core/lib/Drupal/Component/Plugin/Discovery/ProcessDecorator.php
@@ -13,7 +13,7 @@
  * Example use cases include adding in default values for a definition, or
  * providing a backwards compatibility layer for renamed definition properties.
  */
-class ProcessDecorator implements DiscoveryInterface {
+class ProcessDecorator extends DiscoveryBase implements DiscoveryInterface {
 
   /**
    * The Discovery object being decorated.
@@ -47,16 +47,6 @@ public function __construct(DiscoveryInterface $decorated, $process_callback) {
   }
 
   /**
-   * Implements \Drupal\Component\Plugin\Discovery\DicoveryInterface::getDefinition().
-   */
-  public function getDefinition($plugin_id) {
-    $definitions = $this->getDefinitions();
-    if (isset($definitions[$plugin_id])) {
-      return $definitions[$plugin_id];
-    }
-  }
-
-  /**
    * Implements \Drupal\Component\Plugin\Discovery\DicoveryInterface::getDefinitions().
    */
   public function getDefinitions() {
diff --git a/core/lib/Drupal/Component/Plugin/Discovery/StaticDiscovery.php b/core/lib/Drupal/Component/Plugin/Discovery/StaticDiscovery.php
index de74fb5..2eac43e 100644
--- a/core/lib/Drupal/Component/Plugin/Discovery/StaticDiscovery.php
+++ b/core/lib/Drupal/Component/Plugin/Discovery/StaticDiscovery.php
@@ -11,7 +11,7 @@
  * A discovery mechanism that allows plugin definitions to be manually
  * registered rather than actively discovered.
  */
-class StaticDiscovery implements DiscoveryInterface {
+class StaticDiscovery extends DiscoveryCachedBase implements DiscoveryInterface {
 
   /**
    * The array of plugin definitions, keyed by plugin id.
@@ -21,13 +21,6 @@ class StaticDiscovery implements DiscoveryInterface {
   protected $definitions = array();
 
   /**
-   * Implements Drupal\Component\Plugin\Discovery\DiscoveryInterface::getDefinition().
-   */
-  public function getDefinition($base_plugin_id) {
-    return isset($this->definitions[$base_plugin_id]) ? $this->definitions[$base_plugin_id] : NULL;
-  }
-
-  /**
    * Implements Drupal\Component\Plugin\Discovery\DiscoveryInterface::getDefinitions().
    */
   public function getDefinitions() {
diff --git a/core/lib/Drupal/Component/Plugin/Discovery/StaticDiscoveryDecorator.php b/core/lib/Drupal/Component/Plugin/Discovery/StaticDiscoveryDecorator.php
index c92ae8d..fbad2dd 100644
--- a/core/lib/Drupal/Component/Plugin/Discovery/StaticDiscoveryDecorator.php
+++ b/core/lib/Drupal/Component/Plugin/Discovery/StaticDiscoveryDecorator.php
@@ -41,14 +41,14 @@ public function __construct(DiscoveryInterface $decorated, $registerDefinitions
   }
 
   /**
-   * Implements Drupal\Component\Plugin\Discovery\DiscoveryInterface::getDefinition().
+   * {@inheritdoc}
    */
-  public function getDefinition($base_plugin_id) {
+  public function getDefinition($base_plugin_id, $exception_on_invalid = TRUE) {
     if (isset($this->registerDefinitions)) {
       call_user_func($this->registerDefinitions);
     }
     $this->definitions += $this->decorated->getDefinitions();
-    return parent::getDefinition($base_plugin_id);
+    return parent::getDefinition($base_plugin_id, $exception_on_invalid);
   }
 
   /**
diff --git a/core/lib/Drupal/Component/Plugin/PluginManagerBase.php b/core/lib/Drupal/Component/Plugin/PluginManagerBase.php
index 34a5592..7cca440 100644
--- a/core/lib/Drupal/Component/Plugin/PluginManagerBase.php
+++ b/core/lib/Drupal/Component/Plugin/PluginManagerBase.php
@@ -48,8 +48,8 @@
   /**
    * {@inheritdoc}
    */
-  public function getDefinition($plugin_id) {
-    return $this->discovery->getDefinition($plugin_id);
+  public function getDefinition($plugin_id, $exception_on_invalid = TRUE) {
+    return $this->discovery->getDefinition($plugin_id, $exception_on_invalid);
   }
 
   /**
diff --git a/core/lib/Drupal/Core/Config/TypedConfigManager.php b/core/lib/Drupal/Core/Config/TypedConfigManager.php
index 09ec0c5..e8a3e90 100644
--- a/core/lib/Drupal/Core/Config/TypedConfigManager.php
+++ b/core/lib/Drupal/Core/Config/TypedConfigManager.php
@@ -145,9 +145,9 @@ public function createInstance($plugin_id, array $configuration, $name = NULL, $
   }
 
   /**
-   * Implements Drupal\Component\Plugin\Discovery\DiscoveryInterface::getDefinition().
+   * {@inheritdoc}
    */
-  public function getDefinition($base_plugin_id) {
+  public function getDefinition($base_plugin_id, $exception_on_invalid = TRUE) {
     $definitions = $this->getDefinitions();
     if (isset($definitions[$base_plugin_id])) {
       $type = $base_plugin_id;
@@ -164,7 +164,7 @@ public function getDefinition($base_plugin_id) {
     $definition = $definitions[$type];
     // Check whether this type is an extension of another one and compile it.
     if (isset($definition['type'])) {
-      $merge = $this->getDefinition($definition['type']);
+      $merge = $this->getDefinition($definition['type'], $exception_on_invalid);
       $definition = NestedArray::mergeDeep($merge, $definition);
       // Unset type so we try the merge only once per type.
       unset($definition['type']);
diff --git a/core/lib/Drupal/Core/Entity/EntityManager.php b/core/lib/Drupal/Core/Entity/EntityManager.php
index 6221633..d8d3948 100644
--- a/core/lib/Drupal/Core/Entity/EntityManager.php
+++ b/core/lib/Drupal/Core/Entity/EntityManager.php
@@ -161,8 +161,8 @@ public function clearCachedDefinitions() {
   /**
    * {@inheritdoc}
    */
-  public function getDefinition($entity_type_id, $exception_on_invalid = FALSE) {
-    if (($entity_type = parent::getDefinition($entity_type_id)) && class_exists($entity_type->getClass())) {
+  public function getDefinition($entity_type_id, $exception_on_invalid = TRUE) {
+    if (($entity_type = parent::getDefinition($entity_type_id, FALSE)) && class_exists($entity_type->getClass())) {
       return $entity_type;
     }
     elseif (!$exception_on_invalid) {
@@ -176,7 +176,7 @@ public function getDefinition($entity_type_id, $exception_on_invalid = FALSE) {
    * {@inheritdoc}
    */
   public function hasController($entity_type, $controller_type) {
-    if ($definition = $this->getDefinition($entity_type)) {
+    if ($definition = $this->getDefinition($entity_type, FALSE)) {
       return $definition->hasControllerClass($controller_type);
     }
     return FALSE;
@@ -201,7 +201,7 @@ public function getListController($entity_type) {
    */
   public function getFormController($entity_type, $operation) {
     if (!isset($this->controllers['form'][$operation][$entity_type])) {
-      if (!$class = $this->getDefinition($entity_type, TRUE)->getFormClass($operation)) {
+      if (!$class = $this->getDefinition($entity_type)->getFormClass($operation)) {
         throw new InvalidPluginDefinitionException($entity_type, sprintf('The "%s" entity type did not specify a "%s" form class.', $entity_type, $operation));
       }
       if (in_array('Drupal\Core\DependencyInjection\ContainerInjectionInterface', class_implements($class))) {
@@ -251,7 +251,7 @@ public function getAccessController($entity_type) {
    */
   public function getController($entity_type, $controller_type, $controller_class_getter = NULL) {
     if (!isset($this->controllers[$controller_type][$entity_type])) {
-      $definition = $this->getDefinition($entity_type, TRUE);
+      $definition = $this->getDefinition($entity_type);
       if ($controller_class_getter) {
         $class = $definition->{$controller_class_getter}();
       }
@@ -298,7 +298,7 @@ public function getForm(EntityInterface $entity, $operation = 'default', array $
    * {@inheritdoc}
    */
   public function getAdminRouteInfo($entity_type, $bundle) {
-    if (($entity_info = $this->getDefinition($entity_type)) && $admin_form = $entity_info->getLinkTemplate('admin-form')) {
+    if (($entity_info = $this->getDefinition($entity_type, FALSE)) && $admin_form = $entity_info->getLinkTemplate('admin-form')) {
       return array(
         'route_name' => $admin_form,
         'route_parameters' => array(
@@ -321,7 +321,7 @@ public function getFieldDefinitions($entity_type, $bundle = NULL) {
       else {
         // @todo: Refactor to allow for per-bundle overrides.
         // See https://drupal.org/node/2114707.
-        $entity_info = $this->getDefinition($entity_type);
+        $entity_info = $this->getDefinition($entity_type, TRUE);
         $class = $entity_info->getClass();
 
         $this->entityFieldInfo[$entity_type] = array(
diff --git a/core/lib/Drupal/Core/Entity/EntityManagerInterface.php b/core/lib/Drupal/Core/Entity/EntityManagerInterface.php
index 35c7280..06c5718 100644
--- a/core/lib/Drupal/Core/Entity/EntityManagerInterface.php
+++ b/core/lib/Drupal/Core/Entity/EntityManagerInterface.php
@@ -244,28 +244,16 @@ public function getBundleInfo($entity_type);
   public function getTranslationFromContext(EntityInterface $entity, $langcode = NULL, $context = array());
 
   /**
-   * Returns the entity type info for a specific entity type.
-   *
-   * @param string $entity_type_id
-   *   The ID of the entity type.
-   * @param bool $exception_on_invalid
-   *   (optional) If TRUE, an invalid entity type ID will throw an exception.
-   *   Defaults to FALSE.
+   * {@inheritdoc}
    *
    * @return \Drupal\Core\Entity\EntityTypeInterface|null
-   *   Returns the entity type object, or NULL if the entity type ID is invalid
-   *   and $exception_on_invalid is TRUE.
-   *
-   * @throws \InvalidArgumentException
-   *   Thrown if $entity_type_id is invalid and $exception_on_invalid is TRUE.
    */
-  public function getDefinition($entity_type_id, $exception_on_invalid = FALSE);
+  public function getDefinition($entity_type_id, $exception_on_invalid = TRUE);
 
   /**
-   * Returns an array of entity type info, keyed by entity type name.
+   * {@inheritdoc}
    *
    * @return \Drupal\Core\Entity\EntityTypeInterface[]
-   *   An array of entity type objects.
    */
   public function getDefinitions();
 
diff --git a/core/lib/Drupal/Core/Plugin/DefaultPluginManager.php b/core/lib/Drupal/Core/Plugin/DefaultPluginManager.php
index 3264735..bde7bed 100644
--- a/core/lib/Drupal/Core/Plugin/DefaultPluginManager.php
+++ b/core/lib/Drupal/Core/Plugin/DefaultPluginManager.php
@@ -9,6 +9,7 @@
 
 use Drupal\Component\Plugin\Discovery\CachedDiscoveryInterface;
 use Drupal\Core\Cache\Cache;
+use Drupal\Component\Plugin\Exception\UnknownPluginException;
 use Drupal\Core\Plugin\Discovery\ContainerDerivativeDiscoveryDecorator;
 use Drupal\Component\Plugin\PluginManagerBase;
 use Drupal\Component\Plugin\PluginManagerInterface;
@@ -154,7 +155,9 @@ protected function alterInfo(ModuleHandlerInterface $module_handler, $alter_hook
   /**
    * {@inheritdoc}
    */
-  public function getDefinition($plugin_id) {
+  public function getDefinition($plugin_id, $exception_on_invalid = TRUE) {
+    // @todo When \Drupal\Component\Plugin\Discovery\DiscoveryCachedBase is
+    //   converted to a trait, use that and remove this implementation.
     // Fetch definitions if they're not loaded yet.
     if (!isset($this->definitions)) {
       $this->getDefinitions();
@@ -163,7 +166,11 @@ public function getDefinition($plugin_id) {
     if (isset($this->definitions[$plugin_id])) {
       return $this->definitions[$plugin_id];
     }
-    return array();
+    elseif (!$exception_on_invalid) {
+      return NULL;
+    }
+
+    throw new UnknownPluginException($plugin_id, sprintf('The "%s" plugin does not exist.', $plugin_id));
   }
 
   /**
diff --git a/core/lib/Drupal/Core/Plugin/Discovery/AlterDecorator.php b/core/lib/Drupal/Core/Plugin/Discovery/AlterDecorator.php
index c81894e..58b273f 100644
--- a/core/lib/Drupal/Core/Plugin/Discovery/AlterDecorator.php
+++ b/core/lib/Drupal/Core/Plugin/Discovery/AlterDecorator.php
@@ -7,12 +7,13 @@
 
 namespace Drupal\Core\Plugin\Discovery;
 
+use Drupal\Component\Plugin\Discovery\DiscoveryBase;
 use Drupal\Component\Plugin\Discovery\DiscoveryInterface;
 
 /**
  * Enables altering of discovered plugin definitions.
  */
-class AlterDecorator implements DiscoveryInterface {
+class AlterDecorator extends DiscoveryBase implements DiscoveryInterface {
   /**
    * The name of the alter hook that will be implemented by this discovery instance.
    *
@@ -43,15 +44,6 @@ public function __construct(DiscoveryInterface $decorated, $hook) {
   }
 
   /**
-   * Implements Drupal\Component\Plugin\Discovery\DiscoveryInterface::getDefinition().
-   */
-  public function getDefinition($plugin_id) {
-    $definitions = $this->getDefinitions();
-    return isset($definitions[$plugin_id]) ? $definitions[$plugin_id] : NULL;
-  }
-
-
-  /**
    * Implements Drupal\Component\Plugin\Discovery\DiscoveryInterface::getDefinitions().
    */
   public function getDefinitions() {
diff --git a/core/lib/Drupal/Core/Plugin/Discovery/CacheDecorator.php b/core/lib/Drupal/Core/Plugin/Discovery/CacheDecorator.php
index f5d9a84..be73a34 100644
--- a/core/lib/Drupal/Core/Plugin/Discovery/CacheDecorator.php
+++ b/core/lib/Drupal/Core/Plugin/Discovery/CacheDecorator.php
@@ -8,6 +8,7 @@
 namespace Drupal\Core\Plugin\Discovery;
 
 use Drupal\Component\Plugin\Discovery\CachedDiscoveryInterface;
+use Drupal\Component\Plugin\Discovery\DiscoveryCachedBase;
 use Drupal\Component\Plugin\Discovery\DiscoveryInterface;
 use Drupal\Core\Cache\Cache;
 use Drupal\Core\Cache\CacheBackendInterface;
@@ -15,7 +16,7 @@
 /**
  * Enables static and persistent caching of discovered plugin definitions.
  */
-class CacheDecorator implements CachedDiscoveryInterface {
+class CacheDecorator extends DiscoveryCachedBase implements CachedDiscoveryInterface {
 
   /**
    * The cache key used to store the definition list.
@@ -85,28 +86,6 @@ public function __construct(DiscoveryInterface $decorated, $cache_key, $cache_bi
   }
 
   /**
-   * Implements Drupal\Component\Plugin\Discovery\DicoveryInterface::getDefinition().
-   */
-  public function getDefinition($plugin_id) {
-    // Optimize for fast access to definitions if they are already in memory.
-    if (isset($this->definitions)) {
-      // Avoid using a ternary that would create a copy of the array.
-      if (isset($this->definitions[$plugin_id])) {
-        return $this->definitions[$plugin_id];
-      }
-      else {
-        return;
-      }
-    }
-
-    $definitions = $this->getDefinitions();
-    // Avoid using a ternary that would create a copy of the array.
-    if (isset($definitions[$plugin_id])) {
-      return $definitions[$plugin_id];
-    }
-  }
-
-  /**
    * Implements Drupal\Component\Plugin\Discovery\DicoveryInterface::getDefinitions().
    */
   public function getDefinitions() {
diff --git a/core/lib/Drupal/Core/Plugin/Discovery/HookDiscovery.php b/core/lib/Drupal/Core/Plugin/Discovery/HookDiscovery.php
index 035d402..3b93078 100644
--- a/core/lib/Drupal/Core/Plugin/Discovery/HookDiscovery.php
+++ b/core/lib/Drupal/Core/Plugin/Discovery/HookDiscovery.php
@@ -7,13 +7,14 @@
 
 namespace Drupal\Core\Plugin\Discovery;
 
+use Drupal\Component\Plugin\Discovery\DiscoveryBase;
 use Drupal\Component\Plugin\Discovery\DiscoveryInterface;
 use Drupal\Core\Extension\ModuleHandlerInterface;
 
 /**
  * Provides a hook-based plugin discovery class.
  */
-class HookDiscovery implements DiscoveryInterface {
+class HookDiscovery extends DiscoveryBase implements DiscoveryInterface {
 
   /**
    * The name of the hook that will be implemented by this discovery instance.
@@ -44,14 +45,6 @@ function __construct(ModuleHandlerInterface $module_handler, $hook) {
   }
 
   /**
-   * Implements Drupal\Component\Plugin\Discovery\DicoveryInterface::getDefinition().
-   */
-  public function getDefinition($plugin_id) {
-    $plugins = $this->getDefinitions();
-    return isset($plugins[$plugin_id]) ? $plugins[$plugin_id] : NULL;
-  }
-
-  /**
    * Implements Drupal\Component\Plugin\Discovery\DicoveryInterface::getDefinitions().
    */
   public function getDefinitions() {
diff --git a/core/lib/Drupal/Core/Plugin/Discovery/InfoHookDecorator.php b/core/lib/Drupal/Core/Plugin/Discovery/InfoHookDecorator.php
index 4733c8c..8899c40 100644
--- a/core/lib/Drupal/Core/Plugin/Discovery/InfoHookDecorator.php
+++ b/core/lib/Drupal/Core/Plugin/Discovery/InfoHookDecorator.php
@@ -7,12 +7,13 @@
 
 namespace Drupal\Core\Plugin\Discovery;
 
+use Drupal\Component\Plugin\Discovery\DiscoveryBase;
 use Drupal\Component\Plugin\Discovery\DiscoveryInterface;
 
 /**
  * Allows info hook implementations to enhance discovered plugin definitions.
  */
-class InfoHookDecorator implements DiscoveryInterface {
+class InfoHookDecorator extends DiscoveryBase implements DiscoveryInterface {
 
   /**
    * The Discovery object being decorated.
@@ -42,14 +43,6 @@ public function __construct(DiscoveryInterface $decorated, $hook) {
   }
 
   /**
-   * Implements Drupal\Component\Plugin\Discovery\DiscoveryInterface::getDefinition().
-   */
-  public function getDefinition($plugin_id) {
-    $definitions = $this->getDefinitions();
-    return isset($definitions[$plugin_id]) ? $definitions[$plugin_id] : NULL;
-  }
-
-  /**
    * Implements Drupal\Component\Plugin\Discovery\DiscoveryInterface::getDefinitions().
    */
   public function getDefinitions() {
diff --git a/core/lib/Drupal/Core/Plugin/Discovery/YamlDiscovery.php b/core/lib/Drupal/Core/Plugin/Discovery/YamlDiscovery.php
index 516704b..6f32328 100644
--- a/core/lib/Drupal/Core/Plugin/Discovery/YamlDiscovery.php
+++ b/core/lib/Drupal/Core/Plugin/Discovery/YamlDiscovery.php
@@ -7,13 +7,14 @@
 
 namespace Drupal\Core\Plugin\Discovery;
 
+use Drupal\Component\Plugin\Discovery\DiscoveryBase;
 use Drupal\Component\Plugin\Discovery\DiscoveryInterface;
 use Drupal\Component\Discovery\YamlDiscovery as ComponentYamlDiscovery;
 
 /**
  * Allows YAML files to define plugin definitions.
  */
-class YamlDiscovery implements DiscoveryInterface {
+class YamlDiscovery extends DiscoveryBase implements DiscoveryInterface {
 
   /**
    * YAML file discovery and parsing handler.
@@ -38,14 +39,6 @@ function __construct($name, array $directories) {
   /**
    * {@inheritdoc}
    */
-  public function getDefinition($plugin_id) {
-    $definitions = $this->getDefinitions();
-    return isset($definitions[$plugin_id]) ? $definitions[$plugin_id] : NULL;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
   public function getDefinitions() {
     $plugins = $this->discovery->findAll();
 
diff --git a/core/modules/datetime/lib/Drupal/datetime/Plugin/Field/FieldType/DateTimeItem.php b/core/modules/datetime/lib/Drupal/datetime/Plugin/Field/FieldType/DateTimeItem.php
index c5a9cf7..28ea82c 100644
--- a/core/modules/datetime/lib/Drupal/datetime/Plugin/Field/FieldType/DateTimeItem.php
+++ b/core/modules/datetime/lib/Drupal/datetime/Plugin/Field/FieldType/DateTimeItem.php
@@ -54,7 +54,7 @@ public function getPropertyDefinitions() {
       static::$propertyDefinitions['value'] = DataDefinition::create('datetime_iso8601')
         ->setLabel(t('Date value'));
 
-      static::$propertyDefinitions['date'] = DataDefinition::create('datetime_computed')
+      static::$propertyDefinitions['date'] = DataDefinition::create('any')
         ->setLabel(t('Computed date'))
         ->setDescription(t('The computed DateTime object.'))
         ->setComputed(TRUE)
diff --git a/core/modules/field/tests/modules/field_test/field_test.info.yml b/core/modules/field/tests/modules/field_test/field_test.info.yml
index b05dab5..b43a394 100644
--- a/core/modules/field/tests/modules/field_test/field_test.info.yml
+++ b/core/modules/field/tests/modules/field_test/field_test.info.yml
@@ -5,3 +5,5 @@ core: 8.x
 package: Testing
 version: VERSION
 hidden: true
+dependencies:
+  - entity_test
diff --git a/core/modules/filter/lib/Drupal/filter/FilterPluginManager.php b/core/modules/filter/lib/Drupal/filter/FilterPluginManager.php
index 1a1fcc0..7362b9a 100644
--- a/core/modules/filter/lib/Drupal/filter/FilterPluginManager.php
+++ b/core/modules/filter/lib/Drupal/filter/FilterPluginManager.php
@@ -47,10 +47,14 @@ public function __construct(\Traversable $namespaces, CacheBackendInterface $cac
   /**
    * {@inheritdoc}
    */
-  public function getDefinition($plugin_id) {
-    $plugins = $this->getDefinitions();
+  public function getDefinition($plugin_id, $exception_on_invalid = TRUE) {
+    $definitions = $this->getDefinitions();
+    // Avoid using a ternary that would create a copy of the array.
+    if (isset($definitions[$plugin_id])) {
+      return $definitions[$plugin_id];
+    }
     // If the requested filter is missing, use the null filter.
-    return isset($plugins[$plugin_id]) ? $plugins[$plugin_id] : $plugins['filter_null'];
+    return $definitions['filter_null'];
   }
 
 }
diff --git a/core/modules/forum/forum.install b/core/modules/forum/forum.install
index 55539ae..b5d1382 100644
--- a/core/modules/forum/forum.install
+++ b/core/modules/forum/forum.install
@@ -92,7 +92,7 @@ function forum_install() {
  * Implements hook_module_preinstall().
  */
 function forum_module_preinstall($module) {
-  $list_boolean = \Drupal::service('plugin.manager.field.field_type')->getDefinition('list_boolean');
+  $list_boolean = \Drupal::service('plugin.manager.field.field_type')->getDefinition('list_boolean', FALSE);
   if (empty($list_boolean) && $module == 'forum') {
     // Make sure that the list_boolean field type is available before our
     // default config is installed.
diff --git a/core/modules/system/tests/modules/entity_test/entity_test.install b/core/modules/system/tests/modules/entity_test/entity_test.install
index a03acf0..f480676 100644
--- a/core/modules/system/tests/modules/entity_test/entity_test.install
+++ b/core/modules/system/tests/modules/entity_test/entity_test.install
@@ -32,7 +32,7 @@ function entity_test_install() {
     ))->save();
 
     entity_get_form_display($entity_type, $entity_type, 'default')
-      ->setComponent('field_test_text', array('type' => 'text_text'))
+      ->setComponent('field_test_text', array('type' => 'text_textfield'))
       ->save();
   }
 }
diff --git a/core/modules/views_ui/tests/Drupal/views_ui/Tests/ViewListControllerTest.php b/core/modules/views_ui/tests/Drupal/views_ui/Tests/ViewListControllerTest.php
index 534f824..bd4dc60 100644
--- a/core/modules/views_ui/tests/Drupal/views_ui/Tests/ViewListControllerTest.php
+++ b/core/modules/views_ui/tests/Drupal/views_ui/Tests/ViewListControllerTest.php
@@ -42,6 +42,7 @@ public function testBuildRowEntityList() {
       ->will($this->returnValueMap(array(
         array(
           'default',
+          TRUE,
           array(
             'id' => 'default',
             'title' => 'Master',
@@ -51,6 +52,7 @@ public function testBuildRowEntityList() {
         ),
         array(
           'page',
+          TRUE,
           array(
             'id' => 'page',
             'title' => 'Page',
@@ -63,6 +65,7 @@ public function testBuildRowEntityList() {
         ),
         array(
           'embed',
+          TRUE,
           array(
             'id' => 'embed',
             'title' => 'embed',
diff --git a/core/tests/Drupal/Tests/Core/Entity/EntityManagerTest.php b/core/tests/Drupal/Tests/Core/Entity/EntityManagerTest.php
index 81dc34d..e1477a1 100644
--- a/core/tests/Drupal/Tests/Core/Entity/EntityManagerTest.php
+++ b/core/tests/Drupal/Tests/Core/Entity/EntityManagerTest.php
@@ -8,6 +8,7 @@
 namespace Drupal\Tests\Core\Entity;
 
 use Drupal\Component\Plugin\Discovery\DiscoveryInterface;
+use Drupal\Component\Plugin\Exception\UnknownPluginException;
 use Drupal\Core\DependencyInjection\ContainerBuilder;
 use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
 use Drupal\Core\Entity\ContentEntityInterface;
@@ -149,16 +150,22 @@ protected function tearDown() {
    */
   protected function setUpEntityManager($definitions = array()) {
     $class = $this->getMockClass('Drupal\Core\Entity\EntityInterface');
-    $definitions_map = array();
     foreach ($definitions as $entity_type_id => $entity_type) {
       $entity_type->expects($this->any())
         ->method('getClass')
         ->will($this->returnValue($class));
-      $definitions_map[] = array($entity_type_id, $entity_type);
     }
     $this->discovery->expects($this->any())
       ->method('getDefinition')
-      ->will($this->returnValueMap($definitions_map));
+      ->will($this->returnCallback(function ($entity_type_id, $exception_on_invalid = FALSE) use ($definitions) {
+        if (isset($definitions[$entity_type_id])) {
+          return $definitions[$entity_type_id];
+        }
+        elseif (!$exception_on_invalid) {
+          return NULL;
+        }
+        else throw new UnknownPluginException($entity_type_id);
+      }));
     $this->discovery->expects($this->any())
       ->method('getDefinitions')
       ->will($this->returnValue($definitions));
@@ -195,7 +202,7 @@ public function testGetDefinition($entity_type_id, $expected) {
       'banana' => $entity,
     ));
 
-    $entity_type = $this->entityManager->getDefinition($entity_type_id);
+    $entity_type = $this->entityManager->getDefinition($entity_type_id, FALSE);
     if ($expected) {
       $this->assertInstanceOf('Drupal\Core\Entity\EntityTypeInterface', $entity_type);
     }
diff --git a/core/tests/Drupal/Tests/Core/Plugin/Discovery/HookDiscoveryTest.php b/core/tests/Drupal/Tests/Core/Plugin/Discovery/HookDiscoveryTest.php
index 3a120c8..1bc7e4c 100644
--- a/core/tests/Drupal/Tests/Core/Plugin/Discovery/HookDiscoveryTest.php
+++ b/core/tests/Drupal/Tests/Core/Plugin/Discovery/HookDiscoveryTest.php
@@ -116,7 +116,7 @@ public function testGetDefinition() {
         )
       ));
 
-    $this->assertNull($this->hookDiscovery->getDefinition('test_non_existant'));
+    $this->assertNull($this->hookDiscovery->getDefinition('test_non_existant', FALSE));
 
     $plugin_definition = $this->hookDiscovery->getDefinition('test_id_1');
     $this->assertEquals($plugin_definition['class'], 'Drupal\plugin_test\Plugin\plugin_test\fruit\Apple');
@@ -131,6 +131,21 @@ public function testGetDefinition() {
     $this->assertEquals($plugin_definition['module'], 'hook_discovery_test2');
   }
 
+  /**
+   * Tests the getDefinition method with an unknown plugin ID.
+   *
+   * @see \Drupal\Core\Plugin\Discovery::getDefinition()
+   *
+   * @expectedException \Drupal\Component\Plugin\Exception\UnknownPluginException
+   */
+  public function testGetDefinitionWithUnknownID() {
+    $this->moduleHandler->expects($this->once())
+      ->method('getImplementations')
+      ->will($this->returnValue(array()));
+
+    $this->hookDiscovery->getDefinition('test_non_existant', TRUE);
+  }
+
 }
 
 }
