diff --git a/core/lib/Drupal/Core/Action/ActionManager.php b/core/lib/Drupal/Core/Action/ActionManager.php
index d3a4c0a..2b4002d 100644
--- a/core/lib/Drupal/Core/Action/ActionManager.php
+++ b/core/lib/Drupal/Core/Action/ActionManager.php
@@ -22,6 +22,11 @@
 class ActionManager extends DefaultPluginManager {
 
   /**
+   * {@inheritdoc}
+   */
+  protected $interface = '\Drupal\Core\Action\ActionInterface';
+
+  /**
    * Constructs a new class instance.
    *
    * @param \Traversable $namespaces
diff --git a/core/lib/Drupal/Core/Archiver/ArchiverManager.php b/core/lib/Drupal/Core/Archiver/ArchiverManager.php
index b161bd9..ac11937 100644
--- a/core/lib/Drupal/Core/Archiver/ArchiverManager.php
+++ b/core/lib/Drupal/Core/Archiver/ArchiverManager.php
@@ -1,6 +1,7 @@
 <?php
 
 /**
+ * @file
  * Contains \Drupal\Core\Archiver\ArchiverManager.
  */
 
@@ -21,6 +22,11 @@
 class ArchiverManager extends DefaultPluginManager {
 
   /**
+   * {@inheritdoc}
+   */
+  protected $interface = '\Drupal\Core\Archiver\ArchiverInterface';
+
+  /**
    * Constructs a ArchiverManager object.
    *
    * @param \Traversable $namespaces
diff --git a/core/lib/Drupal/Core/Condition/ConditionManager.php b/core/lib/Drupal/Core/Condition/ConditionManager.php
index db0fa43..d7f717e 100644
--- a/core/lib/Drupal/Core/Condition/ConditionManager.php
+++ b/core/lib/Drupal/Core/Condition/ConditionManager.php
@@ -28,6 +28,11 @@ class ConditionManager extends DefaultPluginManager implements ExecutableManager
   use ContextAwarePluginManagerTrait;
 
   /**
+   * {@inheritdoc}
+   */
+  protected $interface = '\Drupal\Core\Condition\ConditionInterface';
+
+  /**
    * Constructs a ConditionManager object.
    *
    * @param \Traversable $namespaces
diff --git a/core/lib/Drupal/Core/Display/VariantManager.php b/core/lib/Drupal/Core/Display/VariantManager.php
index 7f18a29..384c484 100755
--- a/core/lib/Drupal/Core/Display/VariantManager.php
+++ b/core/lib/Drupal/Core/Display/VariantManager.php
@@ -20,6 +20,11 @@
 class VariantManager extends DefaultPluginManager {
 
   /**
+   * {@inheritdoc}
+   */
+  protected $interface = '\Drupal\Core\Display\VariantInterface';
+
+  /**
    * Constructs a new VariantManager.
    *
    * @param \Traversable $namespaces
diff --git a/core/lib/Drupal/Core/Entity/EntityManager.php b/core/lib/Drupal/Core/Entity/EntityManager.php
index 2f16516..4c0ad20 100644
--- a/core/lib/Drupal/Core/Entity/EntityManager.php
+++ b/core/lib/Drupal/Core/Entity/EntityManager.php
@@ -47,6 +47,11 @@ class EntityManager extends DefaultPluginManager implements EntityManagerInterfa
   use StringTranslationTrait;
 
   /**
+   * {@inheritdoc}
+   */
+  protected $interface = '\Drupal\Core\Entity\EntityInterface';
+
+  /**
    * Extra fields by bundle.
    *
    * @var array
diff --git a/core/lib/Drupal/Core/Field/FieldTypePluginManager.php b/core/lib/Drupal/Core/Field/FieldTypePluginManager.php
index 6a5f39a..3a7febd 100644
--- a/core/lib/Drupal/Core/Field/FieldTypePluginManager.php
+++ b/core/lib/Drupal/Core/Field/FieldTypePluginManager.php
@@ -20,6 +20,11 @@
 class FieldTypePluginManager extends DefaultPluginManager implements FieldTypePluginManagerInterface {
 
   /**
+   * {@inheritdoc}
+   */
+  protected $interface = '\Drupal\Core\Field\FieldItemInterface';
+
+  /**
    * Constructs the FieldTypePluginManager object
    *
    * @param \Traversable $namespaces
diff --git a/core/lib/Drupal/Core/Field/FormatterPluginManager.php b/core/lib/Drupal/Core/Field/FormatterPluginManager.php
index 5d5cea2..fc658ff 100644
--- a/core/lib/Drupal/Core/Field/FormatterPluginManager.php
+++ b/core/lib/Drupal/Core/Field/FormatterPluginManager.php
@@ -20,6 +20,11 @@
 class FormatterPluginManager extends DefaultPluginManager {
 
   /**
+   * {@inheritdoc}
+   */
+  protected $interface = '\Drupal\Core\Field\FormatterInterface';
+
+  /**
    * An array of formatter options for each field type.
    *
    * @var array
diff --git a/core/lib/Drupal/Core/Field/WidgetPluginManager.php b/core/lib/Drupal/Core/Field/WidgetPluginManager.php
index 207b5e3..8d525b1 100644
--- a/core/lib/Drupal/Core/Field/WidgetPluginManager.php
+++ b/core/lib/Drupal/Core/Field/WidgetPluginManager.php
@@ -20,6 +20,11 @@
 class WidgetPluginManager extends DefaultPluginManager {
 
   /**
+   * {@inheritdoc}
+   */
+  protected $interface = '\Drupal\Core\Field\WidgetInterface';
+
+  /**
    * The field type manager to define field.
    *
    * @var \Drupal\Core\Field\FieldTypePluginManagerInterface
diff --git a/core/lib/Drupal/Core/ImageToolkit/ImageToolkitManager.php b/core/lib/Drupal/Core/ImageToolkit/ImageToolkitManager.php
index 69e2f1c..eaa31f0 100644
--- a/core/lib/Drupal/Core/ImageToolkit/ImageToolkitManager.php
+++ b/core/lib/Drupal/Core/ImageToolkit/ImageToolkitManager.php
@@ -25,6 +25,11 @@
 class ImageToolkitManager extends DefaultPluginManager {
 
   /**
+   * {@inheritdoc}
+   */
+  protected $interface = '\Drupal\Core\ImageToolkit\ImageToolkitInterface';
+
+  /**
    * The config factory.
    *
    * @var \Drupal\Core\Config\ConfigFactoryInterface
diff --git a/core/lib/Drupal/Core/ImageToolkit/ImageToolkitOperationManager.php b/core/lib/Drupal/Core/ImageToolkit/ImageToolkitOperationManager.php
index ae7d0dd..8a65376 100644
--- a/core/lib/Drupal/Core/ImageToolkit/ImageToolkitOperationManager.php
+++ b/core/lib/Drupal/Core/ImageToolkit/ImageToolkitOperationManager.php
@@ -26,6 +26,11 @@
 class ImageToolkitOperationManager extends DefaultPluginManager implements ImageToolkitOperationManagerInterface {
 
   /**
+   * {@inheritdoc}
+   */
+  protected $interface = '\Drupal\Core\ImageToolkit\ImageToolkitOperationInterface';
+
+  /**
    * A logger instance.
    *
    * @var \Psr\Log\LoggerInterface
diff --git a/core/lib/Drupal/Core/Mail/MailManager.php b/core/lib/Drupal/Core/Mail/MailManager.php
index 48e53cf..102a012 100644
--- a/core/lib/Drupal/Core/Mail/MailManager.php
+++ b/core/lib/Drupal/Core/Mail/MailManager.php
@@ -29,6 +29,11 @@ class MailManager extends DefaultPluginManager implements MailManagerInterface {
   use StringTranslationTrait;
 
   /**
+   * {@inheritdoc}
+   */
+  protected $interface = '\Drupal\Core\Mail\MailInterface';
+
+  /**
    * The config factory.
    *
    * @var \Drupal\Core\Config\ConfigFactoryInterface
@@ -149,16 +154,7 @@ public function getInstance(array $options) {
     }
 
     if (empty($this->instances[$plugin_id])) {
-      $plugin = $this->createInstance($plugin_id);
-      if (is_subclass_of($plugin, '\Drupal\Core\Mail\MailInterface')) {
-        $this->instances[$plugin_id] = $plugin;
-      }
-      else {
-        throw new InvalidPluginDefinitionException($plugin_id, String::format('Class %class does not implement interface %interface', array(
-          '%class' => get_class($plugin),
-          '%interface' => 'Drupal\Core\Mail\MailInterface',
-        )));
-      }
+      $this->instances[$plugin_id] = $this->createInstance($plugin_id);
     }
     return $this->instances[$plugin_id];
   }
diff --git a/core/lib/Drupal/Core/Menu/ContextualLinkManager.php b/core/lib/Drupal/Core/Menu/ContextualLinkManager.php
index cbbf4e4..b4bd175 100644
--- a/core/lib/Drupal/Core/Menu/ContextualLinkManager.php
+++ b/core/lib/Drupal/Core/Menu/ContextualLinkManager.php
@@ -28,6 +28,11 @@
 class ContextualLinkManager extends DefaultPluginManager implements ContextualLinkManagerInterface {
 
   /**
+   * {@inheritdoc}
+   */
+  protected $interface = '\Drupal\Core\Menu\ContextualLinkInterface';
+
+  /**
    * Provides default values for a contextual link definition.
    *
    * @var array
diff --git a/core/lib/Drupal/Core/Menu/LocalActionManager.php b/core/lib/Drupal/Core/Menu/LocalActionManager.php
index 1167357..e881aa5 100644
--- a/core/lib/Drupal/Core/Menu/LocalActionManager.php
+++ b/core/lib/Drupal/Core/Menu/LocalActionManager.php
@@ -31,6 +31,11 @@
 class LocalActionManager extends DefaultPluginManager {
 
   /**
+   * {@inheritdoc}
+   */
+  protected $interface = '\Drupal\Core\Menu\LocalActionInterface';
+
+  /**
    * Provides some default values for all local action plugins.
    *
    * @var array
diff --git a/core/lib/Drupal/Core/Menu/LocalTaskManager.php b/core/lib/Drupal/Core/Menu/LocalTaskManager.php
index 839ef32..ef0a652 100644
--- a/core/lib/Drupal/Core/Menu/LocalTaskManager.php
+++ b/core/lib/Drupal/Core/Menu/LocalTaskManager.php
@@ -35,6 +35,11 @@ class LocalTaskManager extends DefaultPluginManager {
   /**
    * {@inheritdoc}
    */
+  protected $interface = '\Drupal\Core\Menu\LocalTaskInterface';
+
+  /**
+   * {@inheritdoc}
+   */
   protected $defaults = array(
     // (required) The name of the route this task links to.
     'route_name' => '',
diff --git a/core/lib/Drupal/Core/Plugin/DefaultPluginManager.php b/core/lib/Drupal/Core/Plugin/DefaultPluginManager.php
index e1d0731..53a9b5b 100644
--- a/core/lib/Drupal/Core/Plugin/DefaultPluginManager.php
+++ b/core/lib/Drupal/Core/Plugin/DefaultPluginManager.php
@@ -9,7 +9,9 @@
 
 use Drupal\Component\Plugin\Discovery\CachedDiscoveryInterface;
 use Drupal\Component\Plugin\Discovery\DiscoveryCachedTrait;
+use Drupal\Component\Plugin\Exception\PluginException;
 use Drupal\Component\Plugin\Exception\PluginNotFoundException;
+use Drupal\Component\Utility\String;
 use Drupal\Core\Plugin\Discovery\ContainerDerivativeDiscoveryDecorator;
 use Drupal\Component\Plugin\PluginManagerBase;
 use Drupal\Component\Plugin\PluginManagerInterface;
@@ -83,6 +85,13 @@ class DefaultPluginManager extends PluginManagerBase implements PluginManagerInt
   protected $defaults = array();
 
   /**
+   * Defines an interface each plugin should implement.
+   *
+   * @var string
+   */
+  protected $interface;
+
+  /**
    * Creates the discovery object.
    *
    * @param string|bool $subdir
@@ -237,8 +246,37 @@ protected function findDefinitions() {
       if (isset($plugin_definition['provider']) && !in_array($plugin_definition['provider'], array('core', 'component')) && !$this->moduleHandler->moduleExists($plugin_definition['provider'])) {
         unset($definitions[$plugin_id]);
       }
+      $this->enforcePluginInterface($plugin_id, $plugin_definition);
     }
     return $definitions;
   }
 
+  /**
+   * Enforces that a plugin implements the required interface.
+   *
+   * @param string $plugin_id
+   * @param array $plugin_definition
+   *
+   * @throws \Drupal\Component\Plugin\Exception\PluginException
+   */
+  protected function enforcePluginInterface($plugin_id, array $plugin_definition) {
+    // Make it possible to not specify an interface.
+    if (!isset($this->interface)) {
+      return;
+    }
+    // If there are multiple valid interfaces, check for at least one.
+    if (is_array($this->interface) && array_intersect(class_implements($plugin_definition['class']), $this->interface)) {
+      return;
+    }
+    elseif (is_string($this->interface) && is_subclass_of($plugin_definition['class'], $this->interface)) {
+      return;
+    }
+    throw new PluginException(String::format('Plugin "@plugin_id" (@class) in @provider should implement interface @interface', array(
+      '@plugin_id' => $plugin_id,
+      '@class' => $plugin_definition['class'],
+      '@provider' => $plugin_definition['provider'],
+      '@interface' => is_array($this->interface) ? implode(', ', $this->interface) : $this->interface,
+    )));
+  }
+
 }
diff --git a/core/lib/Drupal/Core/Render/ElementInfoManager.php b/core/lib/Drupal/Core/Render/ElementInfoManager.php
index 1dd2be4..a0264d2 100644
--- a/core/lib/Drupal/Core/Render/ElementInfoManager.php
+++ b/core/lib/Drupal/Core/Render/ElementInfoManager.php
@@ -26,6 +26,11 @@
 class ElementInfoManager extends DefaultPluginManager implements ElementInfoManagerInterface {
 
   /**
+   * {@inheritdoc}
+   */
+  protected $interface = '\Drupal\Core\Render\Element\ElementInterface';
+
+  /**
    * Stores the available element information.
    *
    * @var array
diff --git a/core/modules/aggregator/src/Plugin/AggregatorPluginManager.php b/core/modules/aggregator/src/Plugin/AggregatorPluginManager.php
index 53c3acd..1e5d4b6 100644
--- a/core/modules/aggregator/src/Plugin/AggregatorPluginManager.php
+++ b/core/modules/aggregator/src/Plugin/AggregatorPluginManager.php
@@ -26,6 +26,15 @@
 class AggregatorPluginManager extends DefaultPluginManager {
 
   /**
+   * {@inheritdoc}
+   */
+  protected $interface = array(
+    'Drupal\aggregator\Plugin\FetcherInterface',
+    'Drupal\aggregator\Plugin\ParserInterface',
+    'Drupal\aggregator\Plugin\ProcessorInterface',
+  );
+
+  /**
    * Constructs a AggregatorPluginManager object.
    *
    * @param string $type
diff --git a/core/modules/block/src/BlockManager.php b/core/modules/block/src/BlockManager.php
index 706af51..d86cfe9 100644
--- a/core/modules/block/src/BlockManager.php
+++ b/core/modules/block/src/BlockManager.php
@@ -27,6 +27,11 @@ class BlockManager extends DefaultPluginManager implements BlockManagerInterface
   use ContextAwarePluginManagerTrait;
 
   /**
+   * {@inheritdoc}
+   */
+  protected $interface = '\Drupal\block\BlockPluginInterface';
+
+  /**
    * An array of all available modules and their data.
    *
    * @var array
diff --git a/core/modules/ckeditor/src/CKEditorPluginManager.php b/core/modules/ckeditor/src/CKEditorPluginManager.php
index 289c906..c79c580 100644
--- a/core/modules/ckeditor/src/CKEditorPluginManager.php
+++ b/core/modules/ckeditor/src/CKEditorPluginManager.php
@@ -28,6 +28,11 @@
 class CKEditorPluginManager extends DefaultPluginManager {
 
   /**
+   * {@inheritdoc}
+   */
+  protected $interface = '\Drupal\ckeditor\CKEditorPluginInterface';
+
+  /**
    * Constructs a CKEditorPluginManager object.
    *
    * @param \Traversable $namespaces
diff --git a/core/modules/config_translation/src/ConfigMapperManager.php b/core/modules/config_translation/src/ConfigMapperManager.php
index 8eb3735..9dee11a 100644
--- a/core/modules/config_translation/src/ConfigMapperManager.php
+++ b/core/modules/config_translation/src/ConfigMapperManager.php
@@ -29,6 +29,11 @@
 class ConfigMapperManager extends DefaultPluginManager implements ConfigMapperManagerInterface {
 
   /**
+   * {@inheritdoc}
+   */
+  protected $interface = '\Drupal\config_translation\ConfigMapperInterface';
+
+  /**
    * The typed config manager.
    *
    * @var \Drupal\Core\Config\TypedConfigManagerInterface
diff --git a/core/modules/editor/src/Plugin/EditorManager.php b/core/modules/editor/src/Plugin/EditorManager.php
index 1dd6df2..332e4dc 100644
--- a/core/modules/editor/src/Plugin/EditorManager.php
+++ b/core/modules/editor/src/Plugin/EditorManager.php
@@ -22,6 +22,11 @@
 class EditorManager extends DefaultPluginManager {
 
   /**
+   * {@inheritdoc}
+   */
+  protected $interface = '\Drupal\editor\Plugin\EditorPluginInterface';
+
+  /**
    * Constructs an EditorManager object.
    *
    * @param \Traversable $namespaces
diff --git a/core/modules/entity_reference/src/Plugin/Type/SelectionPluginManager.php b/core/modules/entity_reference/src/Plugin/Type/SelectionPluginManager.php
index ce5ff83..c40dde0 100644
--- a/core/modules/entity_reference/src/Plugin/Type/SelectionPluginManager.php
+++ b/core/modules/entity_reference/src/Plugin/Type/SelectionPluginManager.php
@@ -31,6 +31,11 @@ class SelectionPluginManager extends DefaultPluginManager {
   /**
    * {@inheritdoc}
    */
+  protected $interface = '\Drupal\entity_reference\Plugin\Type\Selection\SelectionInterface';
+
+  /**
+   * {@inheritdoc}
+   */
   public function __construct(\Traversable $namespaces, CacheBackendInterface $cache_backend, ModuleHandlerInterface $module_handler) {
     $this->discovery = new AnnotatedClassDiscovery('Plugin/entity_reference/selection', $namespaces, 'Drupal\entity_reference\Annotation\EntityReferenceSelection');
 
diff --git a/core/modules/filter/src/FilterPluginManager.php b/core/modules/filter/src/FilterPluginManager.php
index 77a3c71..650d99a 100644
--- a/core/modules/filter/src/FilterPluginManager.php
+++ b/core/modules/filter/src/FilterPluginManager.php
@@ -23,6 +23,11 @@
 class FilterPluginManager extends DefaultPluginManager {
 
   /**
+   * {@inheritdoc}
+   */
+  protected $interface = '\Drupal\filter\Plugin\FilterInterface';
+
+  /**
    * Constructs a FilterPluginManager object.
    *
    * @param \Traversable $namespaces
diff --git a/core/modules/image/src/ImageEffectManager.php b/core/modules/image/src/ImageEffectManager.php
index 762f947..694bc3e 100644
--- a/core/modules/image/src/ImageEffectManager.php
+++ b/core/modules/image/src/ImageEffectManager.php
@@ -25,6 +25,11 @@
 class ImageEffectManager extends DefaultPluginManager {
 
   /**
+   * {@inheritdoc}
+   */
+  protected $interface = '\Drupal\image\ImageEffectInterface';
+
+  /**
    * Constructs a new ImageEffectManager.
    *
    * @param \Traversable $namespaces
diff --git a/core/modules/language/src/LanguageNegotiationMethodManager.php b/core/modules/language/src/LanguageNegotiationMethodManager.php
index 681c67c..14201b7 100644
--- a/core/modules/language/src/LanguageNegotiationMethodManager.php
+++ b/core/modules/language/src/LanguageNegotiationMethodManager.php
@@ -17,6 +17,11 @@
 class LanguageNegotiationMethodManager extends DefaultPluginManager {
 
   /**
+   * {@inheritdoc}
+   */
+  protected $interface = '\Drupal\language\LanguageNegotiationMethodInterface';
+
+  /**
    * Constructs a new LanguageNegotiationMethodManager object.
    *
    * @param \Traversable $namespaces
diff --git a/core/modules/migrate/src/Plugin/MigrateEntityDestinationFieldInterface.php b/core/modules/migrate/src/Plugin/MigrateEntityDestinationFieldInterface.php
index 4cefa2f..7d2696f 100644
--- a/core/modules/migrate/src/Plugin/MigrateEntityDestinationFieldInterface.php
+++ b/core/modules/migrate/src/Plugin/MigrateEntityDestinationFieldInterface.php
@@ -6,12 +6,13 @@
 
 namespace Drupal\migrate\Plugin;
 
+use Drupal\Component\Plugin\PluginInspectionInterface;
 use Drupal\Core\Field\FieldDefinitionInterface;
 
 /**
  * Handle the importing of a specific configurable field type.
  */
-interface MigrateEntityDestinationFieldInterface {
+interface MigrateEntityDestinationFieldInterface extends PluginInspectionInterface {
 
   /**
    * Convert an array of values into an array structure fit for entity_create.
diff --git a/core/modules/migrate/src/Plugin/MigratePluginManager.php b/core/modules/migrate/src/Plugin/MigratePluginManager.php
index 0ae77be..7cfe943 100644
--- a/core/modules/migrate/src/Plugin/MigratePluginManager.php
+++ b/core/modules/migrate/src/Plugin/MigratePluginManager.php
@@ -2,7 +2,7 @@
 
 /**
  * @file
- * Contains \Drupal\migrate\MigraterPluginManager.
+ * Contains \Drupal\migrate\Plugin\MigratePluginManager.
  */
 
 namespace Drupal\migrate\Plugin;
@@ -30,6 +30,17 @@
 class MigratePluginManager extends DefaultPluginManager {
 
   /**
+   * {@inheritdoc}
+   */
+  protected $interface = array(
+    'Drupal\migrate\Plugin\MigrateDestinationInterface',
+    'Drupal\migrate\Plugin\MigrateProcessInterface',
+    'Drupal\migrate\Plugin\MigrateSourceInterface',
+    'Drupal\migrate\Plugin\MigrateIdMapInterface',
+    'Drupal\migrate\Plugin\MigrateEntityDestinationFieldInterface',
+  );
+
+  /**
    * Constructs a MigratePluginManager object.
    *
    * @param string $type
diff --git a/core/modules/migrate_drupal/migrate_drupal.services.yml b/core/modules/migrate_drupal/migrate_drupal.services.yml
index 6dd03c0..2c8f00c 100644
--- a/core/modules/migrate_drupal/migrate_drupal.services.yml
+++ b/core/modules/migrate_drupal/migrate_drupal.services.yml
@@ -1,4 +1,4 @@
 services:
   plugin.manager.migrate.load:
-    class: Drupal\migrate\Plugin\MigratePluginManager
+    class: Drupal\migrate_drupal\Plugin\MigratePluginManager
     arguments: [load, '@container.namespaces', '@cache.discovery', '@module_handler']
diff --git a/core/modules/migrate_drupal/src/Plugin/MigrateLoadInterface.php b/core/modules/migrate_drupal/src/Plugin/MigrateLoadInterface.php
index 34dd07b..ce3ed83 100644
--- a/core/modules/migrate_drupal/src/Plugin/MigrateLoadInterface.php
+++ b/core/modules/migrate_drupal/src/Plugin/MigrateLoadInterface.php
@@ -2,11 +2,12 @@
 
 /**
  * @file
- * Contains Drupal\migrate\Plugin\MigrateLoadInterface
+ * Contains Drupal\migrate_drupal\Plugin\MigrateLoadInterface
  */
 
 namespace Drupal\migrate_drupal\Plugin;
 
+use Drupal\Component\Plugin\PluginInspectionInterface;
 use Drupal\Core\Entity\EntityStorageInterface;
 
 /**
@@ -16,7 +17,7 @@
  *
  * @ingroup migration
  */
-interface MigrateLoadInterface {
+interface MigrateLoadInterface extends PluginInspectionInterface {
 
   /**
    * Load an additional migration.
diff --git a/core/modules/migrate_drupal/src/Plugin/MigratePluginManager.php b/core/modules/migrate_drupal/src/Plugin/MigratePluginManager.php
new file mode 100644
index 0000000..1a1f881
--- /dev/null
+++ b/core/modules/migrate_drupal/src/Plugin/MigratePluginManager.php
@@ -0,0 +1,26 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\migrate\Plugin\MigratePluginManager.
+ */
+
+namespace Drupal\migrate_drupal\Plugin;
+
+use Drupal\migrate\Plugin\MigratePluginManager as BaseMigratePluginManager;
+
+/**
+ * Manages migrate_drupal plugins.
+ *
+ * @see plugin_api
+ *
+ * @ingroup migration
+ */
+class MigratePluginManager extends BaseMigratePluginManager {
+
+  /**
+   * {@inheritdoc}
+   */
+  protected $interface = 'Drupal\migrate_drupal\Plugin\MigrateLoadInterface';
+
+}
diff --git a/core/modules/quickedit/src/Plugin/InPlaceEditorManager.php b/core/modules/quickedit/src/Plugin/InPlaceEditorManager.php
index 876da2d..c6c937f 100644
--- a/core/modules/quickedit/src/Plugin/InPlaceEditorManager.php
+++ b/core/modules/quickedit/src/Plugin/InPlaceEditorManager.php
@@ -24,6 +24,11 @@
 class InPlaceEditorManager extends DefaultPluginManager {
 
   /**
+   * {@inheritdoc}
+   */
+  protected $interface = '\Drupal\quickedit\Plugin\InPlaceEditorInterface';
+
+  /**
    * Constructs a InPlaceEditorManager object.
    *
    * @param \Traversable $namespaces
diff --git a/core/modules/rest/src/Plugin/Type/ResourcePluginManager.php b/core/modules/rest/src/Plugin/Type/ResourcePluginManager.php
index ec9cb9f..4569df8 100644
--- a/core/modules/rest/src/Plugin/Type/ResourcePluginManager.php
+++ b/core/modules/rest/src/Plugin/Type/ResourcePluginManager.php
@@ -22,6 +22,11 @@
 class ResourcePluginManager extends DefaultPluginManager {
 
   /**
+   * {@inheritdoc}
+   */
+  protected $interface = '\Drupal\rest\Plugin\ResourceInterface';
+
+  /**
    * Constructs a new \Drupal\rest\Plugin\Type\ResourcePluginManager object.
    *
    * @param \Traversable $namespaces
diff --git a/core/modules/search/src/SearchPluginManager.php b/core/modules/search/src/SearchPluginManager.php
index 5670e71..c1cc34c 100644
--- a/core/modules/search/src/SearchPluginManager.php
+++ b/core/modules/search/src/SearchPluginManager.php
@@ -17,6 +17,11 @@
 class SearchPluginManager extends DefaultPluginManager {
 
   /**
+   * {@inheritdoc}
+   */
+  protected $interface = '\Drupal\search\Plugin\SearchInterface';
+
+  /**
    * Constructs SearchPluginManager
    *
    * @param \Traversable $namespaces
diff --git a/core/modules/system/src/Tests/Plugin/Discovery/AnnotatedClassDiscoveryTest.php b/core/modules/system/src/Tests/Plugin/Discovery/AnnotatedClassDiscoveryTest.php
index 941a93a..101dee6 100644
--- a/core/modules/system/src/Tests/Plugin/Discovery/AnnotatedClassDiscoveryTest.php
+++ b/core/modules/system/src/Tests/Plugin/Discovery/AnnotatedClassDiscoveryTest.php
@@ -43,6 +43,13 @@ protected function setUp() {
         'class' => 'Drupal\plugin_test\Plugin\plugin_test\fruit\Cherry',
         'provider' => 'plugin_test',
       ),
+      'kale' => array(
+        'id' => 'kale',
+        'label' => 'Kale',
+        'color' => 'green',
+        'class' => 'Drupal\plugin_test\Plugin\plugin_test\fruit\Kale',
+        'provider' => 'plugin_test',
+      ),
       'orange' => array(
         'id' => 'orange',
         'label' => 'Orange',
diff --git a/core/modules/system/src/Tests/Plugin/Discovery/CustomDirectoryAnnotatedClassDiscoveryTest.php b/core/modules/system/src/Tests/Plugin/Discovery/CustomDirectoryAnnotatedClassDiscoveryTest.php
index aa29a09..92a73f3 100644
--- a/core/modules/system/src/Tests/Plugin/Discovery/CustomDirectoryAnnotatedClassDiscoveryTest.php
+++ b/core/modules/system/src/Tests/Plugin/Discovery/CustomDirectoryAnnotatedClassDiscoveryTest.php
@@ -57,6 +57,13 @@ protected function setUp() {
         'class' => 'Drupal\plugin_test\Plugin\plugin_test\fruit\Cherry',
         'provider' => 'plugin_test',
       ),
+      'kale' => array(
+        'id' => 'kale',
+        'label' => 'Kale',
+        'color' => 'green',
+        'class' => 'Drupal\plugin_test\Plugin\plugin_test\fruit\Kale',
+        'provider' => 'plugin_test',
+      ),
       'orange' => array(
         'id' => 'orange',
         'label' => 'Orange',
diff --git a/core/modules/system/tests/modules/plugin_test/src/Plugin/DefaultsTestPluginManager.php b/core/modules/system/tests/modules/plugin_test/src/Plugin/DefaultsTestPluginManager.php
index e81c39e..68026e1 100644
--- a/core/modules/system/tests/modules/plugin_test/src/Plugin/DefaultsTestPluginManager.php
+++ b/core/modules/system/tests/modules/plugin_test/src/Plugin/DefaultsTestPluginManager.php
@@ -17,6 +17,11 @@
  */
 class DefaultsTestPluginManager extends DefaultPluginManager {
 
+  /**
+   * {@inheritdoc}
+   */
+  protected $interface = '\Drupal\Component\Plugin\PluginInspectionInterface';
+
  /**
   * Constructs a new DefaultsTestPluginManager instance.
   *
diff --git a/core/modules/system/tests/modules/plugin_test/src/Plugin/plugin_test/fruit/Apple.php b/core/modules/system/tests/modules/plugin_test/src/Plugin/plugin_test/fruit/Apple.php
index 9aabeca..d81b6ba 100644
--- a/core/modules/system/tests/modules/plugin_test/src/Plugin/plugin_test/fruit/Apple.php
+++ b/core/modules/system/tests/modules/plugin_test/src/Plugin/plugin_test/fruit/Apple.php
@@ -14,4 +14,4 @@
  *   color = "green"
  * )
  */
-class Apple {}
+class Apple implements FruitInterface {}
diff --git a/core/modules/system/tests/modules/plugin_test/src/Plugin/plugin_test/fruit/Banana.php b/core/modules/system/tests/modules/plugin_test/src/Plugin/plugin_test/fruit/Banana.php
index 4801da5..5e4d180 100644
--- a/core/modules/system/tests/modules/plugin_test/src/Plugin/plugin_test/fruit/Banana.php
+++ b/core/modules/system/tests/modules/plugin_test/src/Plugin/plugin_test/fruit/Banana.php
@@ -17,6 +17,6 @@
  *   }
  * )
  */
-class Banana {
+class Banana implements FruitInterface {
 
 }
diff --git a/core/modules/system/tests/modules/plugin_test/src/Plugin/plugin_test/fruit/Cherry.php b/core/modules/system/tests/modules/plugin_test/src/Plugin/plugin_test/fruit/Cherry.php
index d22b90a..30db552 100644
--- a/core/modules/system/tests/modules/plugin_test/src/Plugin/plugin_test/fruit/Cherry.php
+++ b/core/modules/system/tests/modules/plugin_test/src/Plugin/plugin_test/fruit/Cherry.php
@@ -14,4 +14,4 @@
  *   color = "red"
  * )
  */
-class Cherry {}
+class Cherry implements FruitInterface {}
diff --git a/core/modules/system/tests/modules/plugin_test/src/Plugin/plugin_test/fruit/FruitInterface.php b/core/modules/system/tests/modules/plugin_test/src/Plugin/plugin_test/fruit/FruitInterface.php
new file mode 100644
index 0000000..4c26071
--- /dev/null
+++ b/core/modules/system/tests/modules/plugin_test/src/Plugin/plugin_test/fruit/FruitInterface.php
@@ -0,0 +1,14 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\plugin_test\Plugin\plugin_test\fruit\FruitInterface.
+ */
+
+namespace Drupal\plugin_test\Plugin\plugin_test\fruit;
+
+/**
+ * Provides an interface for test plugins.
+ */
+interface FruitInterface { }
+
diff --git a/core/modules/system/tests/modules/plugin_test/src/Plugin/plugin_test/fruit/Apple.php b/core/modules/system/tests/modules/plugin_test/src/Plugin/plugin_test/fruit/Kale.php
similarity index 50%
copy from core/modules/system/tests/modules/plugin_test/src/Plugin/plugin_test/fruit/Apple.php
copy to core/modules/system/tests/modules/plugin_test/src/Plugin/plugin_test/fruit/Kale.php
index 9aabeca..9c69b15 100644
--- a/core/modules/system/tests/modules/plugin_test/src/Plugin/plugin_test/fruit/Apple.php
+++ b/core/modules/system/tests/modules/plugin_test/src/Plugin/plugin_test/fruit/Kale.php
@@ -2,16 +2,16 @@
 
 /**
  * @file
- * Definition of Drupal\plugin_test\Plugin\plugin_test\fruit\Apple.
+ * Contains \Drupal\plugin_test\Plugin\plugin_test\fruit\Kale.
  */
 
 namespace Drupal\plugin_test\Plugin\plugin_test\fruit;
 
 /**
  * @Plugin(
- *   id = "apple",
- *   label = "Apple",
+ *   id = "kale",
+ *   label = "Kale",
  *   color = "green"
  * )
  */
-class Apple {}
+class Kale {}
diff --git a/core/modules/system/tests/modules/plugin_test/src/Plugin/plugin_test/fruit/Orange.php b/core/modules/system/tests/modules/plugin_test/src/Plugin/plugin_test/fruit/Orange.php
index 27d7d00..10ba4ea 100644
--- a/core/modules/system/tests/modules/plugin_test/src/Plugin/plugin_test/fruit/Orange.php
+++ b/core/modules/system/tests/modules/plugin_test/src/Plugin/plugin_test/fruit/Orange.php
@@ -14,4 +14,4 @@
  *   color = "orange"
  * )
  */
-class Orange {}
+class Orange implements FruitInterface {}
diff --git a/core/modules/taxonomy/taxonomy.module b/core/modules/taxonomy/taxonomy.module
index 3058dc3..3a6d674 100644
--- a/core/modules/taxonomy/taxonomy.module
+++ b/core/modules/taxonomy/taxonomy.module
@@ -689,8 +689,21 @@ function taxonomy_implode_tags($tags, $vid = NULL) {
  * Implements hook_field_widget_info_alter().
  */
 function taxonomy_field_widget_info_alter(&$info) {
-  $info['options_select']['field_types'][] = 'taxonomy_term_reference';
-  $info['options_buttons']['field_types'][] = 'taxonomy_term_reference';
+  if (isset($info['options_select'])) {
+    $info['options_select']['field_types'][] = 'taxonomy_term_reference';
+  }
+  if (isset($info['options_buttons'])) {
+    $info['options_buttons']['field_types'][] = 'taxonomy_term_reference';
+  }
+}
+
+/**
+ * Implements hook_field_formatter_info_alter().
+ */
+function taxonomy_field_formatter_info_alter(array &$info) {
+  if (!\Drupal::moduleHandler()->moduleExists('entity_reference')) {
+    unset($info['entity_reference_rss_category']);
+  }
 }
 
 /**
diff --git a/core/modules/tour/src/TipPluginManager.php b/core/modules/tour/src/TipPluginManager.php
index 6f98f62..1a66bbb 100644
--- a/core/modules/tour/src/TipPluginManager.php
+++ b/core/modules/tour/src/TipPluginManager.php
@@ -22,6 +22,11 @@
 class TipPluginManager extends DefaultPluginManager {
 
   /**
+   * {@inheritdoc}
+   */
+  protected $interface = '\Drupal\tour\TipPluginInterface';
+
+  /**
    * Constructs a new TipPluginManager.
    *
    * @param \Traversable $namespaces
diff --git a/core/modules/update/tests/modules/update_test/src/Plugin/Archiver/UpdateTestArchiver.php b/core/modules/update/tests/modules/update_test/src/Plugin/Archiver/UpdateTestArchiver.php
new file mode 100644
index 0000000..330d8ab
--- /dev/null
+++ b/core/modules/update/tests/modules/update_test/src/Plugin/Archiver/UpdateTestArchiver.php
@@ -0,0 +1,51 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\update_test\Plugin\Archiver\UpdateTestArchiver.
+ */
+
+namespace Drupal\update_test\Plugin\Archiver;
+
+use Drupal\Core\Archiver\ArchiverInterface;
+
+/**
+ * Defines a test archiver implementation.
+ *
+ * @Archiver(
+ *   id = "update_test_archiver",
+ *   title = @Translation("Update Test Archiver"),
+ *   extensions = {"update-test-extension"}
+ * )
+ */
+class UpdateTestArchiver implements ArchiverInterface {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function add($file_path) {
+    return $this;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function remove($path) {
+    return $this;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function extract($path, array $files = array()) {
+    return $this;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function listContents() {
+    return array();
+  }
+
+}
diff --git a/core/modules/update/tests/modules/update_test/update_test.module b/core/modules/update/tests/modules/update_test/update_test.module
index 536d330..93c97f4 100644
--- a/core/modules/update/tests/modules/update_test/update_test.module
+++ b/core/modules/update/tests/modules/update_test/update_test.module
@@ -108,19 +108,6 @@ function update_test_mock_page($project_name) {
 }
 
 /**
- * Implements hook_archiver_info_alter().
- */
-function update_test_archiver_info_alter(&$definitions) {
-  // We only care about the extensions for now and the only way to add a new
-  // extension without a class is via alter. This definition is bogus, real
-  // archivers have a class and the definition goes in the annotation of the
-  // class.
-  $definitions['update_test_archiver'] = array(
-    'extensions' => array('update-test-extension'),
-  );
-}
-
-/**
  * Implements hook_filetransfer_info().
  */
 function update_test_filetransfer_info() {
diff --git a/core/modules/views/src/Form/ViewsExposedForm.php b/core/modules/views/src/Form/ViewsExposedForm.php
index 4bc76fd..fc01380 100644
--- a/core/modules/views/src/Form/ViewsExposedForm.php
+++ b/core/modules/views/src/Form/ViewsExposedForm.php
@@ -81,7 +81,7 @@ public function buildForm(array $form, FormStateInterface $form_state) {
 
     // Go through each handler and let it generate its exposed widget.
     foreach ($view->display_handler->handlers as $type => $value) {
-      /** @var \Drupal\views\Plugin\views\HandlerBase $handler */
+      /** @var \Drupal\views\Plugin\views\ViewsHandlerInterface $handler */
       foreach ($view->$type as $id => $handler) {
         if ($handler->canExpose() && $handler->isExposed()) {
           // Grouped exposed filters have their own forms.
@@ -134,7 +134,7 @@ public function buildForm(array $form, FormStateInterface $form_state) {
    */
   public function validateForm(array &$form, FormStateInterface $form_state) {
     foreach (array('field', 'filter') as $type) {
-      /** @var \Drupal\views\Plugin\views\HandlerBase[] $handlers */
+      /** @var \Drupal\views\Plugin\views\ViewsHandlerInterface[] $handlers */
       $handlers = &$form_state['view']->$type;
       foreach ($handlers as $key => $handler) {
         $handlers[$key]->validateExposed($form, $form_state);
@@ -150,7 +150,7 @@ public function validateForm(array &$form, FormStateInterface $form_state) {
    */
   public function submitForm(array &$form, FormStateInterface $form_state) {
     foreach (array('field', 'filter') as $type) {
-      /** @var \Drupal\views\Plugin\views\HandlerBase[] $handlers */
+      /** @var \Drupal\views\Plugin\views\ViewsHandlerInterface[] $handlers */
       $handlers = &$form_state['view']->$type;
       foreach ($handlers as $key => $info) {
         $handlers[$key]->submitExposed($form, $form_state);
diff --git a/core/modules/views/src/Plugin/ViewsHandlerManager.php b/core/modules/views/src/Plugin/ViewsHandlerManager.php
index 7b7b76e..0024a30 100644
--- a/core/modules/views/src/Plugin/ViewsHandlerManager.php
+++ b/core/modules/views/src/Plugin/ViewsHandlerManager.php
@@ -20,6 +20,11 @@
 class ViewsHandlerManager extends DefaultPluginManager {
 
   /**
+   * {@inheritdoc}
+   */
+  protected $interface = '\Drupal\views\Plugin\views\ViewsHandlerInterface';
+
+  /**
    * The views data cache.
    *
    * @var \Drupal\views\ViewsData
@@ -74,7 +79,7 @@ public function __construct($handler_type, \Traversable $namespaces, ViewsData $
    *   (optional) Override the actual handler object with this plugin ID. Used for
    *   aggregation when the handler is redirected to the aggregation handler.
    *
-   * @return \Drupal\views\Plugin\views\HandlerBase
+   * @return \Drupal\views\Plugin\views\ViewsHandlerInterface
    *   An instance of a handler object. May be a broken handler instance.
    */
   public function getHandler($item, $override = NULL) {
diff --git a/core/modules/views/src/Plugin/ViewsJoinManager.php b/core/modules/views/src/Plugin/ViewsJoinManager.php
new file mode 100644
index 0000000..66411a1
--- /dev/null
+++ b/core/modules/views/src/Plugin/ViewsJoinManager.php
@@ -0,0 +1,20 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\views\Plugin\ViewsJoinManager.
+ */
+
+namespace Drupal\views\Plugin;
+
+/**
+ * Plugin type manager for all views handlers.
+ */
+class ViewsJoinManager extends ViewsHandlerManager {
+
+  /**
+   * {@inheritdoc}
+   */
+  protected $interface = '\Drupal\views\Plugin\views\join\JoinPluginInterface';
+
+}
diff --git a/core/modules/views/src/Plugin/ViewsPluginManager.php b/core/modules/views/src/Plugin/ViewsPluginManager.php
index ca98555..ce70426 100644
--- a/core/modules/views/src/Plugin/ViewsPluginManager.php
+++ b/core/modules/views/src/Plugin/ViewsPluginManager.php
@@ -20,6 +20,11 @@
 class ViewsPluginManager extends DefaultPluginManager {
 
   /**
+   * {@inheritdoc}
+   */
+  protected $interface = '\Drupal\views\Plugin\views\ViewsPluginInterface';
+
+  /**
    * Constructs a ViewsPluginManager object.
    *
    * @param string $type
diff --git a/core/modules/views/src/Plugin/views/HandlerBase.php b/core/modules/views/src/Plugin/views/HandlerBase.php
index ce31f43..27d24d7 100644
--- a/core/modules/views/src/Plugin/views/HandlerBase.php
+++ b/core/modules/views/src/Plugin/views/HandlerBase.php
@@ -16,7 +16,6 @@
 use Drupal\Core\Render\Element;
 use Drupal\Core\Session\AccountInterface;
 use Drupal\views\Plugin\views\display\DisplayPluginBase;
-use Drupal\views\Plugin\views\PluginBase;
 use Drupal\views\ViewExecutable;
 use Drupal\Core\Database\Database;
 use Drupal\views\Views;
@@ -27,7 +26,7 @@
  *
  * @ingroup views_plugins
  */
-abstract class HandlerBase extends PluginBase {
+abstract class HandlerBase extends PluginBase implements ViewsHandlerInterface {
 
   /**
    * Where the $query object will reside:
@@ -109,7 +108,7 @@ public function __construct(array $configuration, $plugin_id, $plugin_definition
   }
 
   /**
-   * Overrides \Drupal\views\Plugin\views\PluginBase::init().
+   * {@inheritdoc}
    */
   public function init(ViewExecutable $view, DisplayPluginBase $display, array &$options = NULL) {
     parent::init($view, $display, $options);
@@ -172,7 +171,7 @@ protected function defineOptions() {
   }
 
   /**
-   * Return a string representing this handler's name in the UI.
+   * {@inheritdoc}
    */
   public function adminLabel($short = FALSE) {
     if (!empty($this->options['admin_label'])) {
@@ -184,11 +183,7 @@ public function adminLabel($short = FALSE) {
   }
 
   /**
-   * Shortcut to get a handler's raw field value.
-   *
-   * This should be overridden for handlers with formulae or other
-   * non-standard fields. Because this takes an argument, fields
-   * overriding this can just call return parent::getField($formula)
+   * {@inheritdoc}
    */
   public function getField($field = NULL) {
     if (!isset($field)) {
@@ -218,15 +213,7 @@ public function getField($field = NULL) {
   }
 
   /**
-   * Sanitize the value for output.
-   *
-   * @param $value
-   *   The value being rendered.
-   * @param $type
-   *   The type of sanitization needed. If not provided, String::checkPlain() is used.
-   *
-   * @return string
-   *   Returns the safe value.
+   * {@inheritdoc}
    */
   public function sanitizeValue($value, $type = NULL) {
     switch ($type) {
@@ -277,7 +264,7 @@ protected function caseTransform($string, $option) {
   }
 
   /**
-   * Build the options form.
+   * {@inheritdoc}
    */
   public function buildOptionsForm(&$form, FormStateInterface $form_state) {
     // Some form elements belong in a fieldset for presentation, but can't
@@ -483,10 +470,7 @@ public function showExposeForm(&$form, FormStateInterface $form_state) {
   }
 
   /**
-   * Check whether current user has access to this handler.
-   *
-   * @param AccountInterface $account
-   * @return boolean
+   * {@inheritdoc}
    */
   public function access(AccountInterface $account) {
     if (isset($this->definition['access callback']) && function_exists($this->definition['access callback'])) {
@@ -500,26 +484,19 @@ public function access(AccountInterface $account) {
   }
 
   /**
-   * Run before the view is built.
-   *
-   * This gives all the handlers some time to set up before any handler has
-   * been fully run.
+   * {@inheritdoc}
    */
   public function preQuery() {
   }
 
   /**
-   * Don't run a query by default.
+   * {@inheritdoc}
    */
   public function query() {
   }
 
   /**
-   * Run after the view is executed, before the result is cached.
-   *
-   * This gives all the handlers some time to modify values. This is primarily
-   * used so that handlers that pull up secondary data can put it in the
-   * $values so that the raw data can be utilized externally.
+   * {@inheritdoc}
    */
   public function postExecute(&$values) { }
 
@@ -534,8 +511,7 @@ protected function placeholder() {
   }
 
   /**
-   * Called just prior to query(), this lets a handler set up any relationship
-   * it needs.
+   * {@inheritdoc}
    */
   public function setRelationship() {
     // Ensure this gets set to something.
@@ -564,8 +540,7 @@ public function setRelationship() {
   }
 
   /**
-   * Ensure the main table for this handler is in the query. This is used
-   * a lot.
+   * {@inheritdoc}
    */
   public function ensureMyTable() {
     if (!isset($this->tableAlias)) {
@@ -575,7 +550,7 @@ public function ensureMyTable() {
   }
 
   /**
-   * Provide text for the administrative summary.
+   * {@inheritdoc}
    */
   public function adminSummary() { }
 
@@ -612,11 +587,7 @@ public function acceptExposedInput($input) { return TRUE; }
   public function storeExposedInput($input, $status) { return TRUE; }
 
   /**
-   * Get the join object that should be used for this handler.
-   *
-   * This method isn't used a great deal, but it's very handy for easily
-   * getting the join if it is necessary to make some changes to it, such
-   * as adding an 'extra'.
+   * {@inheritdoc}
    */
   public function getJoin() {
     // get the join from this table that links back to the base table.
@@ -635,21 +606,12 @@ public function getJoin() {
   }
 
   /**
-   * Validates the handler against the complete View.
-   *
-   * This is called when the complete View is being validated. For validating
-   * the handler options form use validateOptionsForm().
-   *
-   * @see views_handler::validateOptionsForm()
-   *
-   * @return
-   *   Empty array if the handler is valid; an array of error strings if it is not.
+   * {@inheritdoc}
    */
   public function validate() { return array(); }
 
   /**
-   * Determines if the handler is considered 'broken', meaning it's a
-   * a placeholder used when a handler can't be found.
+   * {@inheritdoc}
    */
   public function broken() {
     return FALSE;
@@ -692,24 +654,14 @@ protected function getViewsData() {
   }
 
   /**
-   * Sets the views data service.
-   *
-   * @param \Drupal\views\ViewsData $views_data
-   *   The views data.
+   * {@inheritdoc}
    */
   public function setViewsData(ViewsData $views_data) {
     $this->viewsData = $views_data;
   }
 
   /**
-   * Fetches a handler to join one table to a primary table from the data cache.
-   *
-   * @param string $table
-   *   The table to join from.
-   * @param string $base_table
-   *   The table to join to.
-   *
-   * @return \Drupal\views\Plugin\views\join\JoinPluginBase
+   * {@inheritdoc}
    */
   public static function getTableJoin($table, $base_table) {
     $data = Views::viewsData()->get($table);
@@ -745,13 +697,7 @@ public static function getTableJoin($table, $base_table) {
   }
 
   /**
-   * Determines the entity type used by this handler.
-   *
-   * If this handler uses a relationship, the base class of the relationship is
-   * taken into account.
-   *
-   * @return string
-   *   The machine name of the entity type.
+   * {@inheritdoc}
    */
   public function getEntityType() {
     // If the user has configured a relationship on the handler take that into
@@ -772,15 +718,7 @@ public function getEntityType() {
   }
 
   /**
-   * Breaks x,y,z and x+y+z into an array.
-   *
-   * @param string $str
-   *   The string to split.
-   * @param bool $force_int
-   *   Enforce a numeric check.
-   *
-   * @return \stdClass
-   *   A stdClass object containing value and operator properties.
+   * {@inheritdoc}
    */
   public static function breakString($str, $force_int = FALSE) {
     $operator = NULL;
diff --git a/core/modules/views/src/Plugin/views/PluginBase.php b/core/modules/views/src/Plugin/views/PluginBase.php
index 132ceec..ffc8b23 100644
--- a/core/modules/views/src/Plugin/views/PluginBase.php
+++ b/core/modules/views/src/Plugin/views/PluginBase.php
@@ -41,7 +41,7 @@
  *
  * @ingroup views_plugins
  */
-abstract class PluginBase extends ComponentPluginBase implements ContainerFactoryPluginInterface {
+abstract class PluginBase extends ComponentPluginBase implements ContainerFactoryPluginInterface, ViewsPluginInterface {
 
   /**
    * Options for this plugin will be held here.
@@ -100,14 +100,7 @@ public static function create(ContainerInterface $container, array $configuratio
   }
 
   /**
-   * Initialize the plugin.
-   *
-   * @param \Drupal\views\ViewExecutable $view
-   *   The view object.
-   * @param \Drupal\views\Plugin\views\display\DisplayPluginBase $display
-   *   The display handler.
-   * @param array $options
-   *   The options configured for this plugin.
+   * {@inheritdoc}
    */
   public function init(ViewExecutable $view, DisplayPluginBase $display, array &$options = NULL) {
     $this->view = $view;
@@ -163,8 +156,7 @@ protected function setOptionDefaults(array &$storage, array $options) {
   }
 
   /**
-   * Unpack options over our existing defaults, drilling down into arrays
-   * so that defaults don't get totally blown away.
+   * {@inheritdoc}
    */
   public function unpackOptions(&$storage, $options, $definition = NULL, $all = TRUE, $check = TRUE) {
     if ($check && !is_array($options)) {
@@ -203,19 +195,14 @@ public function unpackOptions(&$storage, $options, $definition = NULL, $all = TR
   }
 
   /**
-   * Clears a plugin.
+   * {@inheritdoc}
    */
   public function destroy() {
     unset($this->view, $this->display, $this->query);
   }
 
   /**
-   * Init will be called after construct, when the plugin is attached to a
-   * view and a display.
-   */
-
-  /**
-   * Provide a form to edit options for this plugin.
+   * {@inheritdoc}
    */
   public function buildOptionsForm(&$form, FormStateInterface $form_state) {
     // Some form elements belong in a fieldset for presentation, but can't
@@ -226,47 +213,41 @@ public function buildOptionsForm(&$form, FormStateInterface $form_state) {
   }
 
   /**
-   * Validate the options form.
+   * {@inheritdoc}
    */
   public function validateOptionsForm(&$form, FormStateInterface $form_state) { }
 
   /**
-   * Handle any special handling on the validate form.
+   * {@inheritdoc}
    */
   public function submitOptionsForm(&$form, FormStateInterface $form_state) { }
 
   /**
-   * Add anything to the query that we might need to.
+   * {@inheritdoc}
    */
   public function query() { }
 
   /**
-   * Provide a full list of possible theme templates used by this style.
+   * {@inheritdoc}
    */
   public function themeFunctions() {
     return $this->view->buildThemeFunctions($this->definition['theme']);
   }
 
   /**
-   * Validate that the plugin is correct and can be saved.
-   *
-   * @return
-   *   An array of error strings to tell the user what is wrong with this
-   *   plugin.
+   * {@inheritdoc}
    */
   public function validate() { return array(); }
 
   /**
-   * Returns the summary of the settings in the display.
+   * {@inheritdoc}
    */
   public function summaryTitle() {
     return t('Settings');
   }
 
   /**
-   * Return the human readable name of the display.
-   *
-   * This appears on the ui beside each plugin and beside the settings link.
+   * {@inheritdoc}
    */
   public function pluginTitle() {
     // Short_title is optional so its defaults to an empty string.
@@ -277,39 +258,21 @@ public function pluginTitle() {
   }
 
   /**
-   * Returns the usesOptions property.
+   * {@inheritdoc}
    */
   public function usesOptions() {
     return $this->usesOptions;
   }
 
   /**
-   * Returns a string with any core tokens replaced.
-   *
-   * @param string $string
-   *   The string to preform the token replacement on.
-   * @param array $options
-   *   An array of options, as passed to \Drupal\Core\Utility\Token::replace().
-   *
-   * @return string
-   *   The tokenized string.
+   * {@inheritdoc}
    */
   public function globalTokenReplace($string = '', array $options = array()) {
     return \Drupal::token()->replace($string, array('view' => $this->view), $options);
   }
 
   /**
-   * Returns an array of available token replacements.
-   *
-   * @param bool $prepared
-   *   Whether to return the raw token info for each token or an array of
-   *   prepared tokens for each type. E.g. "[view:name]".
-   * @param array $types
-   *   An array of additional token types to return, defaults to 'site' and
-   *   'view'.
-   *
-   * @return array
-   *   An array of available token replacement info or tokens, grouped by type.
+   * {@inheritdoc}
    */
   public function getAvailableGlobalTokens($prepared = FALSE, array $types = array()) {
     $info = \Drupal::token()->getInfo();
@@ -333,12 +296,7 @@ public function getAvailableGlobalTokens($prepared = FALSE, array $types = array
   }
 
   /**
-   * Adds elements for available core tokens to a form.
-   *
-   * @param array $form
-   *   The form array to alter, passed by reference.
-   * @param \Drupal\Core\Form\FormStateInterface $form_state
-   *   The current state of the form.
+   * {@inheritdoc}
    */
   public function globalTokenForm(&$form, FormStateInterface $form_state) {
     $token_items = array();
@@ -369,19 +327,7 @@ public function globalTokenForm(&$form, FormStateInterface $form_state) {
   }
 
   /**
-   * Moves form elements into fieldsets for presentation purposes.
-   *
-   * Many views forms use #tree = TRUE to keep their values in a hierarchy for
-   * easier storage. Moving the form elements into fieldsets during form
-   * building would break up that hierarchy. Therefore, we wait until the
-   * pre_render stage, where any changes we make affect presentation only and
-   * aren't reflected in $form_state->getValues().
-   *
-   * @param array $form
-   *   The form build array to alter.
-   *
-   * @return array
-   *   The form build array.
+   * {@inheritdoc}
    */
   public static function preRenderAddFieldsetMarkup(array $form) {
     foreach (Element::children($form) as $key) {
@@ -400,17 +346,7 @@ public static function preRenderAddFieldsetMarkup(array $form) {
   }
 
   /**
-   * Flattens the structure of form elements.
-   *
-   * If a form element has #flatten = TRUE, then all of it's children get moved
-   * to the same level as the element itself. So $form['to_be_flattened'][$key]
-   * becomes $form[$key], and $form['to_be_flattened'] gets unset.
-   *
-   * @param array $form
-   *   The form build array to alter.
-   *
-   * @return array
-   *   The form build array.
+   * {@inheritdoc}
    */
   public static function preRenderFlattenData($form) {
     foreach (Element::children($form) as $key) {
@@ -428,12 +364,7 @@ public static function preRenderFlattenData($form) {
   }
 
   /**
-   * Returns an array of module dependencies for this plugin.
-   *
-   * Dependencies are a list of module names, which might depend on the
-   * configuration.
-   *
-   * @return array
+   * {@inheritdoc}
    */
   public function getDependencies() {
     return array();
diff --git a/core/modules/views/src/Plugin/views/ViewsHandlerInterface.php b/core/modules/views/src/Plugin/views/ViewsHandlerInterface.php
new file mode 100644
index 0000000..4a48372
--- /dev/null
+++ b/core/modules/views/src/Plugin/views/ViewsHandlerInterface.php
@@ -0,0 +1,144 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\views\Plugin\views\ViewsHandlerInterface.
+ */
+
+namespace Drupal\views\Plugin\views;
+
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Session\AccountInterface;
+
+/**
+ * Provides an interface for all views handlers.
+ */
+interface ViewsHandlerInterface extends ViewsPluginInterface {
+
+  /**
+   * Run before the view is built.
+   *
+   * This gives all the handlers some time to set up before any handler has
+   * been fully run.
+   */
+  public function preQuery();
+
+  /**
+   * Determines the entity type used by this handler.
+   *
+   * If this handler uses a relationship, the base class of the relationship is
+   * taken into account.
+   *
+   * @return string
+   *   The machine name of the entity type.
+   */
+  public function getEntityType();
+
+  /**
+   * Determines if the handler is considered 'broken', meaning it's a
+   * a placeholder used when a handler can't be found.
+   */
+  public function broken();
+
+  /**
+   * Ensure the main table for this handler is in the query. This is used
+   * a lot.
+   */
+  public function ensureMyTable();
+
+  /**
+   * Check whether current user has access to this handler.
+   *
+   * @param AccountInterface $account
+   *
+   * @return boolean
+   */
+  public function access(AccountInterface $account);
+
+  /**
+   * Get the join object that should be used for this handler.
+   *
+   * This method isn't used a great deal, but it's very handy for easily
+   * getting the join if it is necessary to make some changes to it, such
+   * as adding an 'extra'.
+   */
+  public function getJoin();
+
+  /**
+   * Sanitize the value for output.
+   *
+   * @param $value
+   *   The value being rendered.
+   * @param $type
+   *   The type of sanitization needed. If not provided, String::checkPlain() is used.
+   *
+   * @return string
+   *   Returns the safe value.
+   */
+  public function sanitizeValue($value, $type = NULL);
+
+  /**
+   * Fetches a handler to join one table to a primary table from the data cache.
+   *
+   * @param string $table
+   *   The table to join from.
+   * @param string $base_table
+   *   The table to join to.
+   *
+   * @return \Drupal\views\Plugin\views\join\JoinPluginBase
+   */
+  public static function getTableJoin($table, $base_table);
+
+  /**
+   * Shortcut to get a handler's raw field value.
+   *
+   * This should be overridden for handlers with formulae or other
+   * non-standard fields. Because this takes an argument, fields
+   * overriding this can just call return parent::getField($formula)
+   */
+  public function getField($field = NULL);
+
+  /**
+   * Run after the view is executed, before the result is cached.
+   *
+   * This gives all the handlers some time to modify values. This is primarily
+   * used so that handlers that pull up secondary data can put it in the
+   * $values so that the raw data can be utilized externally.
+   */
+  public function postExecute(&$values);
+
+  /**
+   * Shortcut to display the exposed options form.
+   */
+  public function showExposeForm(&$form, FormStateInterface $form_state);
+
+  /**
+   * Called just prior to query(), this lets a handler set up any relationship
+   * it needs.
+   */
+  public function setRelationship();
+
+  /**
+   * Return a string representing this handler's name in the UI.
+   */
+  public function adminLabel($short = FALSE);
+
+  /**
+   * Breaks x,y,z and x+y+z into an array.
+   *
+   * @param string $str
+   *   The string to split.
+   * @param bool $force_int
+   *   Enforce a numeric check.
+   *
+   * @return \stdClass
+   *   A stdClass object containing value and operator properties.
+   */
+  public static function breakString($str, $force_int = FALSE);
+
+  /**
+   * Provide text for the administrative summary.
+   */
+  public function adminSummary();
+
+}
diff --git a/core/modules/views/src/Plugin/views/ViewsPluginInterface.php b/core/modules/views/src/Plugin/views/ViewsPluginInterface.php
new file mode 100644
index 0000000..df5de7a
--- /dev/null
+++ b/core/modules/views/src/Plugin/views/ViewsPluginInterface.php
@@ -0,0 +1,181 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\views\Plugin\views\ViewsPluginInterface.
+ */
+
+namespace Drupal\views\Plugin\views;
+
+use Drupal\Component\Plugin\DerivativeInspectionInterface;
+use Drupal\Component\Plugin\PluginInspectionInterface;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\views\Plugin\views\display\DisplayPluginBase;
+use Drupal\views\ViewExecutable;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Provides an interface for all views plugins.
+ */
+interface ViewsPluginInterface extends PluginInspectionInterface, DerivativeInspectionInterface {
+
+  /**
+   * Returns an array of module dependencies for this plugin.
+   *
+   * Dependencies are a list of module names, which might depend on the
+   * configuration.
+   *
+   * @return array
+   */
+  public function getDependencies();
+
+  /**
+   * Return the human readable name of the display.
+   *
+   * This appears on the ui beside each plugin and beside the settings link.
+   */
+  public function pluginTitle();
+
+  /**
+   * Returns the usesOptions property.
+   */
+  public function usesOptions();
+
+  /**
+   * Validate the options form.
+   */
+  public function validateOptionsForm(&$form, FormStateInterface $form_state);
+
+  /**
+   * Returns the summary of the settings in the display.
+   */
+  public function summaryTitle();
+
+  /**
+   * Moves form elements into fieldsets for presentation purposes.
+   *
+   * Many views forms use #tree = TRUE to keep their values in a hierarchy for
+   * easier storage. Moving the form elements into fieldsets during form
+   * building would break up that hierarchy. Therefore, we wait until the
+   * pre_render stage, where any changes we make affect presentation only and
+   * aren't reflected in $form_state->getValues().
+   *
+   * @param array $form
+   *   The form build array to alter.
+   *
+   * @return array
+   *   The form build array.
+   */
+  public static function preRenderAddFieldsetMarkup(array $form);
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition);
+
+  /**
+   * Initialize the plugin.
+   *
+   * @param \Drupal\views\ViewExecutable $view
+   *   The view object.
+   * @param \Drupal\views\Plugin\views\display\DisplayPluginBase $display
+   *   The display handler.
+   * @param array $options
+   *   The options configured for this plugin.
+   */
+  public function init(ViewExecutable $view, DisplayPluginBase $display, array &$options = NULL);
+
+  /**
+   * Handle any special handling on the validate form.
+   */
+  public function submitOptionsForm(&$form, FormStateInterface $form_state);
+
+  /**
+   * Adds elements for available core tokens to a form.
+   *
+   * @param array $form
+   *   The form array to alter, passed by reference.
+   * @param \Drupal\Core\Form\FormStateInterface $form_state
+   *   The current state of the form.
+   */
+  public function globalTokenForm(&$form, FormStateInterface $form_state);
+
+  /**
+   * Returns an array of available token replacements.
+   *
+   * @param bool $prepared
+   *   Whether to return the raw token info for each token or an array of
+   *   prepared tokens for each type. E.g. "[view:name]".
+   * @param array $types
+   *   An array of additional token types to return, defaults to 'site' and
+   *   'view'.
+   *
+   * @return array
+   *   An array of available token replacement info or tokens, grouped by type.
+   */
+  public function getAvailableGlobalTokens($prepared = FALSE, array $types = array());
+
+  /**
+   * Flattens the structure of form elements.
+   *
+   * If a form element has #flatten = TRUE, then all of it's children get moved
+   * to the same level as the element itself. So $form['to_be_flattened'][$key]
+   * becomes $form[$key], and $form['to_be_flattened'] gets unset.
+   *
+   * @param array $form
+   *   The form build array to alter.
+   *
+   * @return array
+   *   The form build array.
+   */
+  public static function preRenderFlattenData($form);
+
+  /**
+   * Returns a string with any core tokens replaced.
+   *
+   * @param string $string
+   *   The string to preform the token replacement on.
+   * @param array $options
+   *   An array of options, as passed to \Drupal\Core\Utility\Token::replace().
+   *
+   * @return string
+   *   The tokenized string.
+   */
+  public function globalTokenReplace($string = '', array $options = array());
+
+  /**
+   * Clears a plugin.
+   */
+  public function destroy();
+
+  /**
+   * Validate that the plugin is correct and can be saved.
+   *
+   * @return
+   *   An array of error strings to tell the user what is wrong with this
+   *   plugin.
+   */
+  public function validate();
+
+  /**
+   * Add anything to the query that we might need to.
+   */
+  public function query();
+
+  /**
+   * Unpack options over our existing defaults, drilling down into arrays
+   * so that defaults don't get totally blown away.
+   */
+  public function unpackOptions(&$storage, $options, $definition = NULL, $all = TRUE, $check = TRUE);
+
+  /**
+   * Provide a form to edit options for this plugin.
+   */
+  public function buildOptionsForm(&$form, FormStateInterface $form_state);
+
+  /**
+   * Provide a full list of possible theme templates used by this style.
+   */
+  public function themeFunctions();
+
+}
diff --git a/core/modules/views/src/Plugin/views/display/DisplayPluginBase.php b/core/modules/views/src/Plugin/views/display/DisplayPluginBase.php
index 31f30bc..9a3b7bf 100644
--- a/core/modules/views/src/Plugin/views/display/DisplayPluginBase.php
+++ b/core/modules/views/src/Plugin/views/display/DisplayPluginBase.php
@@ -837,7 +837,7 @@ public function usesFields() {
    * @param string $type
    *   The type of the plugin.
    *
-   * @return \Drupal\views\Plugin\views\PluginBase
+   * @return \Drupal\views\Plugin\views\ViewsPluginInterface
    */
   public function getPlugin($type) {
     // Look up the plugin name to use for this instance.
@@ -890,7 +890,7 @@ public function &getHandler($type, $id) {
   /**
    * Get a full array of handlers for $type. This caches them.
    *
-   * @return \Drupal\views\Plugin\views\HandlerBase[]
+   * @return \Drupal\views\Plugin\views\ViewsHandlerInterface[]
    */
   public function getHandlers($type) {
     if (!isset($this->handlers[$type])) {
diff --git a/core/modules/views/src/Plugin/views/join/JoinPluginBase.php b/core/modules/views/src/Plugin/views/join/JoinPluginBase.php
index cd1dda3..d282f39 100644
--- a/core/modules/views/src/Plugin/views/join/JoinPluginBase.php
+++ b/core/modules/views/src/Plugin/views/join/JoinPluginBase.php
@@ -58,7 +58,7 @@
  *
  * Extensions of this class can be used to create more interesting joins.
  */
-class JoinPluginBase extends PluginBase {
+class JoinPluginBase extends PluginBase implements JoinPluginInterface {
 
   /**
    * The table to join (right table).
@@ -181,16 +181,7 @@ public function __construct(array $configuration, $plugin_id, $plugin_definition
   }
 
   /**
-   * Builds the SQL for the join this object represents.
-   *
-   * When possible, try to use table alias instead of table names.
-   *
-   * @param $select_query
-   *   An select query object.
-   * @param $table
-   *   The base table to join.
-   * @param \Drupal\views\Plugin\views\query\QueryPluginBase $view_query
-   *   The source views query.
+   * {@inheritdoc}
    */
   public function buildJoin($select_query, $table, $view_query) {
     if (empty($this->configuration['table formula'])) {
diff --git a/core/modules/views/src/Plugin/views/join/JoinPluginInterface.php b/core/modules/views/src/Plugin/views/join/JoinPluginInterface.php
new file mode 100644
index 0000000..03fefb2
--- /dev/null
+++ b/core/modules/views/src/Plugin/views/join/JoinPluginInterface.php
@@ -0,0 +1,31 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\views\Plugin\views\join\JoinPluginInterface.
+ */
+
+namespace Drupal\views\Plugin\views\join;
+
+/**
+ * Represents a join and creates the SQL necessary to implement the join.
+ *
+ * Extensions of this class can be used to create more interesting joins.
+ */
+interface JoinPluginInterface {
+
+  /**
+   * Builds the SQL for the join this object represents.
+   *
+   * When possible, try to use table alias instead of table names.
+   *
+   * @param $select_query
+   *   An select query object.
+   * @param $table
+   *   The base table to join.
+   * @param \Drupal\views\Plugin\views\query\QueryPluginBase $view_query
+   *   The source views query.
+   */
+  public function buildJoin($select_query, $table, $view_query);
+
+}
diff --git a/core/modules/views/src/Tests/Handler/HandlerTest.php b/core/modules/views/src/Tests/Handler/HandlerTest.php
index 9c9e038..b182dee 100644
--- a/core/modules/views/src/Tests/Handler/HandlerTest.php
+++ b/core/modules/views/src/Tests/Handler/HandlerTest.php
@@ -204,16 +204,14 @@ public function testHandlerWeights() {
       $loaded_order = array_keys($view->display_handler->getOption($type));
       $this->assertIdentical($original_order[$type], $loaded_order);
     }
-
   }
 
-
   /**
    * Check to see if a value is the same as the value on a certain handler.
    *
    * @param $expected
    *   The expected value to check.
-   * @param \Drupal\views\Plugin\views\HandlerBase $handler
+   * @param \Drupal\views\Plugin\views\ViewsHandlerInterface $handler
    *   The handler that has the $handler->value property to compare with first.
    * @param string $message
    *   The message to display along with the assertion.
diff --git a/core/modules/views/views.services.yml b/core/modules/views/views.services.yml
index b66568f..1917f4a 100644
--- a/core/modules/views/views.services.yml
+++ b/core/modules/views/views.services.yml
@@ -33,7 +33,7 @@ services:
     class: Drupal\views\Plugin\ViewsHandlerManager
     arguments: [filter, '@container.namespaces', '@views.views_data', '@cache.discovery', '@module_handler']
   plugin.manager.views.join:
-    class: Drupal\views\Plugin\ViewsHandlerManager
+    class: Drupal\views\Plugin\ViewsJoinManager
     arguments: [join, '@container.namespaces', '@views.views_data', '@cache.discovery', '@module_handler']
   plugin.manager.views.pager:
     class: Drupal\views\Plugin\ViewsPluginManager
diff --git a/core/tests/Drupal/Tests/Core/Plugin/DefaultPluginManagerTest.php b/core/tests/Drupal/Tests/Core/Plugin/DefaultPluginManagerTest.php
index 7f7d3ac..4b3652c 100644
--- a/core/tests/Drupal/Tests/Core/Plugin/DefaultPluginManagerTest.php
+++ b/core/tests/Drupal/Tests/Core/Plugin/DefaultPluginManagerTest.php
@@ -13,6 +13,8 @@
  * Tests the DefaultPluginManager.
  *
  * @group Plugin
+ *
+ * @coversDefaultClass \Drupal\Core\Plugin\DefaultPluginManager
  */
 class DefaultPluginManagerTest extends UnitTestCase {
 
@@ -76,7 +78,7 @@ public function testDefaultPluginManagerWithDisabledModule() {
       ->with('disabled_module')
       ->will($this->returnValue(FALSE));
 
-    $plugin_manager = new TestPluginManager($this->namespaces, $definitions, $module_handler, 'test_alter_hook');
+    $plugin_manager = new TestPluginManager($this->namespaces, $definitions, $module_handler, 'test_alter_hook', '\Drupal\plugin_test\Plugin\plugin_test\fruit\FruitInterface');
 
     $this->assertEmpty($plugin_manager->getDefinition('cherry', FALSE), 'Plugin information of a disabled module is not available');
   }
@@ -101,7 +103,7 @@ public function testDefaultPluginManagerWithObjects() {
       ->with('disabled_module')
       ->will($this->returnValue(FALSE));
 
-    $plugin_manager = new TestPluginManager($this->namespaces, $definitions, $module_handler, 'test_alter_hook');
+    $plugin_manager = new TestPluginManager($this->namespaces, $definitions, $module_handler, 'test_alter_hook', '\Drupal\plugin_test\Plugin\plugin_test\fruit\FruitInterface');
 
     $this->assertEmpty($plugin_manager->getDefinition('cherry', FALSE), 'Plugin information is available');
   }
@@ -110,7 +112,7 @@ public function testDefaultPluginManagerWithObjects() {
    * Tests the plugin manager with no cache and altering.
    */
   public function testDefaultPluginManager() {
-    $plugin_manager = new TestPluginManager($this->namespaces, $this->expectedDefinitions);
+    $plugin_manager = new TestPluginManager($this->namespaces, $this->expectedDefinitions, NULL, NULL, '\Drupal\plugin_test\Plugin\plugin_test\fruit\FruitInterface');
     $this->assertEquals($this->expectedDefinitions, $plugin_manager->getDefinitions());
     $this->assertEquals($this->expectedDefinitions['banana'], $plugin_manager->getDefinition('banana'));
   }
@@ -129,7 +131,7 @@ public function testDefaultPluginManagerWithAlter() {
       ->method('alter')
       ->with($this->equalTo($alter_hook_name), $this->equalTo($this->expectedDefinitions));
 
-    $plugin_manager = new TestPluginManager($this->namespaces, $this->expectedDefinitions, $module_handler, $alter_hook_name);
+    $plugin_manager = new TestPluginManager($this->namespaces, $this->expectedDefinitions, $module_handler, $alter_hook_name, '\Drupal\plugin_test\Plugin\plugin_test\fruit\FruitInterface');
 
     $this->assertEquals($this->expectedDefinitions, $plugin_manager->getDefinitions());
     $this->assertEquals($this->expectedDefinitions['banana'], $plugin_manager->getDefinition('banana'));
@@ -153,7 +155,7 @@ public function testDefaultPluginManagerWithEmptyCache() {
       ->method('set')
       ->with($cid, $this->expectedDefinitions);
 
-    $plugin_manager = new TestPluginManager($this->namespaces, $this->expectedDefinitions);
+    $plugin_manager = new TestPluginManager($this->namespaces, $this->expectedDefinitions, NULL, NULL, '\Drupal\plugin_test\Plugin\plugin_test\fruit\FruitInterface');
     $plugin_manager->setCacheBackend($cache_backend, $cid);
 
     $this->assertEquals($this->expectedDefinitions, $plugin_manager->getDefinitions());
@@ -177,7 +179,7 @@ public function testDefaultPluginManagerWithFilledCache() {
       ->expects($this->never())
       ->method('set');
 
-    $plugin_manager = new TestPluginManager($this->namespaces, $this->expectedDefinitions);
+    $plugin_manager = new TestPluginManager($this->namespaces, $this->expectedDefinitions, NULL, NULL, '\Drupal\plugin_test\Plugin\plugin_test\fruit\FruitInterface');
     $plugin_manager->setCacheBackend($cache_backend, $cid);
 
     $this->assertEquals($this->expectedDefinitions, $plugin_manager->getDefinitions());
@@ -201,12 +203,85 @@ public function testCacheClearWithTags() {
 
     $this->getContainerWithCacheBins($cache_backend);
 
-    $plugin_manager = new TestPluginManager($this->namespaces, $this->expectedDefinitions);
+    $plugin_manager = new TestPluginManager($this->namespaces, $this->expectedDefinitions, NULL, NULL, '\Drupal\plugin_test\Plugin\plugin_test\fruit\FruitInterface');
     $plugin_manager->setCacheBackend($cache_backend, $cid, array('tag' => TRUE));
 
     $plugin_manager->clearCachedDefinitions();
   }
 
+  /**
+   * Tests plugins with the proper interface.
+   *
+   * @covers ::getDefinitions
+   * @covers ::enforcePluginInterface
+   */
+  public function testGetDefinitionsWithJustValidInterfaces() {
+    $plugin_manager = new TestPluginManager($this->namespaces, $this->expectedDefinitions, NULL, NULL, '\Drupal\plugin_test\Plugin\plugin_test\fruit\FruitInterface');
+
+    $definitions = $plugin_manager->getDefinitions();
+    $this->assertCount(2, $definitions);
+    $this->assertEquals($definitions, $this->expectedDefinitions);
+  }
+
+  /**
+   * Tests plugins without the proper interface.
+   *
+   * @covers ::getDefinitions
+   * @covers ::enforcePluginInterface
+   *
+   * @expectedException \Drupal\Component\Plugin\Exception\PluginException
+   * @expectedExceptionMessage Plugin "kale" (Drupal\plugin_test\Plugin\plugin_test\fruit\Kale) in plugin_test should implement interface \Drupal\plugin_test\Plugin\plugin_test\fruit\FruitInterface
+   */
+  public function testGetDefinitionsWithInvalidInterfaces() {
+    $module_handler = $this->getMock('Drupal\Core\Extension\ModuleHandlerInterface');
+
+    $module_handler->expects($this->any())
+      ->method('moduleExists')
+      ->with('plugin_test')
+      ->willReturn(FALSE);
+
+    $this->expectedDefinitions['kale'] = array(
+      'id' => 'kale',
+      'label' => 'Kale',
+      'color' => 'green',
+      'class' => 'Drupal\plugin_test\Plugin\plugin_test\fruit\Kale',
+      'provider' => 'plugin_test',
+    );
+    $this->expectedDefinitions['apple']['provider'] = 'plugin_test';
+    $this->expectedDefinitions['banana']['provider'] = 'plugin_test';
+
+    $plugin_manager = new TestPluginManager($this->namespaces, $this->expectedDefinitions, $module_handler, NULL, '\Drupal\plugin_test\Plugin\plugin_test\fruit\FruitInterface');
+    $plugin_manager->getDefinitions();
+  }
+
+  /**
+   * Tests plugins without a required interface.
+   *
+   * @covers ::getDefinitions
+   * @covers ::enforcePluginInterface
+   */
+  public function testGetDefinitionsWithoutRequiredInterface() {
+    $module_handler = $this->getMock('Drupal\Core\Extension\ModuleHandlerInterface');
+
+    $module_handler->expects($this->any())
+      ->method('moduleExists')
+      ->with('plugin_test')
+      ->willReturn(FALSE);
+
+    $this->expectedDefinitions['kale'] = array(
+      'id' => 'kale',
+      'label' => 'Kale',
+      'color' => 'green',
+      'class' => 'Drupal\plugin_test\Plugin\plugin_test\fruit\Kale',
+      'provider' => 'plugin_test',
+    );
+    $this->expectedDefinitions['apple']['provider'] = 'plugin_test';
+    $this->expectedDefinitions['banana']['provider'] = 'plugin_test';
+
+    $plugin_manager = new TestPluginManager($this->namespaces, $this->expectedDefinitions, $module_handler, NULL);
+    $plugin_manager->getDefinitions();
+  }
+
 }
 
 if (!defined('DRUPAL_ROOT')) {
diff --git a/core/tests/Drupal/Tests/Core/Plugin/TestPluginManager.php b/core/tests/Drupal/Tests/Core/Plugin/TestPluginManager.php
index a7ea79d..d6c6384 100644
--- a/core/tests/Drupal/Tests/Core/Plugin/TestPluginManager.php
+++ b/core/tests/Drupal/Tests/Core/Plugin/TestPluginManager.php
@@ -28,8 +28,10 @@ class TestPluginManager extends DefaultPluginManager {
    *   (optional) The module handler to invoke the alter hook with.
    * @param string $alter_hook
    *   (optional) Name of the alter hook.
+   * @param string $interface
+   *   (optional) The interface required for the plugins.
    */
-  public function __construct(\Traversable $namespaces, array $definitions, ModuleHandlerInterface $module_handler = NULL, $alter_hook = NULL) {
+  public function __construct(\Traversable $namespaces, array $definitions, ModuleHandlerInterface $module_handler = NULL, $alter_hook = NULL, $interface = NULL) {
     // Create the object that can be used to return definitions for all the
     // plugins available for this type. Most real plugin managers use a richer
     // discovery implementation, but StaticDiscovery lets us add some simple
@@ -46,6 +48,7 @@ public function __construct(\Traversable $namespaces, array $definitions, Module
     if ($alter_hook) {
       $this->alterInfo($alter_hook);
     }
+    $this->interface = $interface;
   }
 
 }
