diff --git a/core/modules/block/block.install b/core/modules/block/block.install
index a5bd3c4..33a0a5b 100644
--- a/core/modules/block/block.install
+++ b/core/modules/block/block.install
@@ -163,7 +163,7 @@ function block_schema() {
         'type' => 'varchar',
         'length' => 255,
         'not null' => FALSE,
-        'description' => 'The {filter_format}.format of the block body.',
+        'description' => 'The format id of the block body.',
       ),
     ),
     'unique keys' => array(
diff --git a/core/modules/block/lib/Drupal/block/Tests/BlockTest.php b/core/modules/block/lib/Drupal/block/Tests/BlockTest.php
index 7446052..8920061 100644
--- a/core/modules/block/lib/Drupal/block/Tests/BlockTest.php
+++ b/core/modules/block/lib/Drupal/block/Tests/BlockTest.php
@@ -36,12 +36,12 @@ function setUp() {
     config('system.site')->set('page.front', 'test-page')->save();
 
     // Create Full HTML text format.
-    $full_html_format = array(
+    $full_html_format_config = array(
       'format' => 'full_html',
       'name' => 'Full HTML',
     );
-    $full_html_format = (object) $full_html_format;
-    filter_format_save($full_html_format);
+    $full_html_format = entity_create('filter_format', $full_html_format_config);
+    $full_html_format->save();
     $this->checkPermissions(array(), TRUE);
 
     // Create and log in an administrative user having access to the Full HTML
diff --git a/core/modules/filter/config/filter.format.plain_text.yml b/core/modules/filter/config/filter.format.plain_text.yml
new file mode 100644
index 0000000..5967ffe
--- /dev/null
+++ b/core/modules/filter/config/filter.format.plain_text.yml
@@ -0,0 +1,45 @@
+format: plain_text
+name: 'Plain text'
+cache: '1'
+status: '1'
+weight: '10'
+roles:
+  anonymous: anonymous
+  authenticated: authenticated
+  administrator: administrator
+filters:
+  filter_html_escape:
+    module: filter
+    settings: {  }
+    status: '1'
+    weight: '0'
+  filter_url:
+    module: filter
+    settings:
+      filter_url_length: '72'
+    status: '1'
+    weight: '1'
+  filter_autop:
+    module: filter
+    settings: {  }
+    status: '1'
+    weight: '2'
+  filter_html:
+    module: filter
+    settings:
+      allowed_html: ''
+      filter_html_help: '0'
+      filter_html_nofollow: '0'
+    status: '0'
+    weight: '0'
+  filter_html_image_secure:
+    module: filter
+    settings: {  }
+    status: '0'
+    weight: '0'
+  filter_htmlcorrector:
+    module: filter
+    settings: {  }
+    status: '0'
+    weight: '0'
+langcode: und
diff --git a/core/modules/filter/filter.admin.inc b/core/modules/filter/filter.admin.inc
index c01fb93..b81e4cd 100644
--- a/core/modules/filter/filter.admin.inc
+++ b/core/modules/filter/filter.admin.inc
@@ -61,13 +61,12 @@ function filter_admin_overview($form) {
  * Form submission handler for filter_admin_overview().
  */
 function filter_admin_overview_submit($form, &$form_state) {
+  $filter_formats = filter_formats();
   foreach ($form_state['values']['formats'] as $id => $data) {
     if (is_array($data) && isset($data['weight'])) {
       // Only update if this is a form element with weight.
-      db_update('filter_format')
-        ->fields(array('weight' => $data['weight']))
-        ->condition('format', $id)
-        ->execute();
+      $filter_formats[$id]->weight = $data['weight'];
+      $filter_formats[$id]->save();
     }
   }
   filter_formats_reset();
@@ -133,10 +132,8 @@ function theme_filter_admin_overview($variables) {
 function filter_admin_format_page($format = NULL) {
   if (!isset($format->name)) {
     drupal_set_title(t('Add text format'));
-    $format = (object) array(
-      'format' => NULL,
-      'name' => '',
-    );
+
+    $format = entity_create('filter_format', array());
   }
   return drupal_get_form('filter_admin_format_form', $format);
 }
@@ -276,11 +273,11 @@ function filter_admin_format_form($form, &$form_state, $format) {
   );
 
   foreach ($filter_info as $name => $filter) {
-    if (isset($filter['settings callback'])) {
-      $function = $filter['settings callback'];
+    if (isset($filter['settings_callback'])) {
+      $function = $filter['settings_callback'];
       // Pass along stored filter settings and default settings, but also the
       // format object and all filters to allow for complex implementations.
-      $defaults = (isset($filter['default settings']) ? $filter['default settings'] : array());
+      $defaults = (isset($filter['default_settings']) ? $filter['default_settings'] : array());
       $settings_form = $function($form, $form_state, $filters[$name], $format, $defaults, $filters);
       if (!empty($settings_form)) {
         $form['filters']['settings'][$name] = array(
@@ -345,9 +342,12 @@ function filter_admin_format_form_validate($form, &$form_state) {
   form_set_value($form['format'], $format_format, $form_state);
   form_set_value($form['name'], $format_name, $form_state);
 
-  $result = db_query("SELECT format FROM {filter_format} WHERE name = :name AND format <> :format", array(':name' => $format_name, ':format' => $format_format))->fetchField();
-  if ($result) {
-    form_set_error('name', t('Text format names must be unique. A format named %name already exists.', array('%name' => $format_name)));
+  $filter_formats = entity_load_multiple('filter_format');
+  foreach ($filter_formats as $format) {
+    if ($format->name == $format_name && $format->format != $format_format) {
+      form_set_error('name', t('Text format names must be unique. A format named %name already exists.', array('%name' => $format_name)));
+      break;
+    }
   }
 }
 
@@ -365,14 +365,7 @@ function filter_admin_format_form_submit($form, &$form_state) {
   foreach ($form_state['values'] as $key => $value) {
     $format->$key = $value;
   }
-  $status = filter_format_save($format);
-
-  // Save user permissions.
-  if ($permission = filter_permission_name($format)) {
-    foreach ($format->roles as $rid => $enabled) {
-      user_role_change_permissions($rid, array($permission => $enabled));
-    }
-  }
+  $status = $format->save();
 
   switch ($status) {
     case SAVED_NEW:
diff --git a/core/modules/filter/filter.api.php b/core/modules/filter/filter.api.php
index f11a528..2331cea 100644
--- a/core/modules/filter/filter.api.php
+++ b/core/modules/filter/filter.api.php
@@ -11,109 +11,18 @@
  */
 
 /**
- * Define content filters.
- *
- * User submitted content is passed through a group of filters before it is
- * output in HTML, in order to remove insecure or unwanted parts, correct or
- * enhance the formatting, transform special keywords, etc. A group of filters
- * is referred to as a "text format". Administrators can create as many text
- * formats as needed. Individual filters can be enabled and configured
- * differently for each text format.
- *
- * This hook is invoked by filter_get_filters() and allows modules to register
- * input filters they provide.
- *
- * Filtering is a two-step process. First, the content is 'prepared' by calling
- * the 'prepare callback' function for every filter. The purpose of the
- * 'prepare callback' is to escape HTML-like structures. For example, imagine a
- * filter which allows the user to paste entire chunks of programming code
- * without requiring manual escaping of special HTML characters like < or &. If
- * the programming code were left untouched, then other filters could think it
- * was HTML and change it. For many filters, the prepare step is not necessary.
- *
- * The second step is the actual processing step. The result from passing the
- * text through all the filters' prepare steps gets passed to all the filters
- * again, this time with the 'process callback' function. The process callbacks
- * should then actually change the content: transform URLs into hyperlinks,
- * convert smileys into images, etc.
- *
- * For performance reasons content is only filtered once; the result is stored
- * in the cache table and retrieved from the cache the next time the same piece
- * of content is displayed. If a filter's output is dynamic, it can override
- * the cache mechanism, but obviously this should be used with caution: having
- * one filter that does not support caching in a particular text format
- * disables caching for the entire format, not just for one filter.
- *
- * Beware of the filter cache when developing your module: it is advised to set
- * your filter to 'cache' => FALSE while developing, but be sure to remove that
- * setting if it's not needed, when you are no longer in development mode.
- *
- * @return
- *   An associative array of filters, whose keys are internal filter names,
- *   which should be unique and therefore prefixed with the name of the module.
- *   Each value is an associative array describing the filter, with the
- *   following elements (all are optional except as noted):
- *   - title: (required) An administrative summary of what the filter does.
- *   - description: Additional administrative information about the filter's
- *     behavior, if needed for clarification.
- *   - settings callback: The name of a function that returns configuration
- *     form elements for the filter. See hook_filter_FILTER_settings() for
- *     details.
- *   - default settings: An associative array containing default settings for
- *     the filter, to be applied when the filter has not been configured yet.
- *   - prepare callback: The name of a function that escapes the content before
- *     the actual filtering happens. See hook_filter_FILTER_prepare() for
- *     details.
- *   - process callback: (required) The name the function that performs the
- *     actual filtering. See hook_filter_FILTER_process() for details.
- *   - cache (default TRUE): Specifies whether the filtered text can be cached.
- *     Note that setting this to FALSE makes the entire text format not
- *     cacheable, which may have an impact on the site's overall performance.
- *     See filter_format_allowcache() for details.
- *   - tips callback: The name of a function that returns end-user-facing
- *     filter usage guidelines for the filter. See hook_filter_FILTER_tips()
- *     for details.
- *   - weight: A default weight for the filter in new text formats.
- *
- * @see filter_example.module
- * @see hook_filter_info_alter()
- */
-function hook_filter_info() {
-  $filters['filter_html'] = array(
-    'title' => t('Limit allowed HTML tags'),
-    'description' => t('Allows you to restrict the HTML tags the user can use. It will also remove harmful content such as JavaScript events, JavaScript URLs and CSS styles from those tags that are not removed.'),
-    'process callback' => '_filter_html',
-    'settings callback' => '_filter_html_settings',
-    'default settings' => array(
-      'allowed_html' => '<a> <em> <strong> <cite> <blockquote> <code> <ul> <ol> <li> <dl> <dt> <dd>',
-      'filter_html_help' => 1,
-      'filter_html_nofollow' => 0,
-    ),
-    'tips callback' => '_filter_html_tips',
-  );
-  $filters['filter_autop'] = array(
-    'title' => t('Convert line breaks'),
-    'description' => t('Converts line breaks into HTML (i.e. &lt;br&gt; and &lt;p&gt;) tags.'),
-    'process callback' => '_filter_autop',
-    'tips callback' => '_filter_autop_tips',
-  );
-  return $filters;
-}
-
-/**
  * Perform alterations on filter definitions.
  *
  * @param $info
- *   Array of information on filters exposed by hook_filter_info()
- *   implementations.
+ *   Array of information on filters exposed by filter plugins.
  */
 function hook_filter_info_alter(&$info) {
   // Replace the PHP evaluator process callback with an improved
   // PHP evaluator provided by a module.
-  $info['php_code']['process callback'] = 'my_module_php_evaluator';
+  $info['php_code']['process_callback'] = 'my_module_php_evaluator';
 
   // Alter the default settings of the URL filter provided by core.
-  $info['filter_url']['default settings'] = array(
+  $info['filter_url']['default_settings'] = array(
     'filter_url_length' => 100,
   );
 }
@@ -126,7 +35,7 @@ function hook_filter_info_alter(&$info) {
  * Settings callback for hook_filter_info().
  *
  * Note: This is not really a hook. The function name is manually specified via
- * 'settings callback' in hook_filter_info(), with this recommended callback
+ * 'settings_callback' in hook_filter_info(), with this recommended callback
  * name pattern. It is called from filter_admin_format_form().
  *
  * This callback function is used to provide a settings form for filter
@@ -150,7 +59,7 @@ function hook_filter_info_alter(&$info) {
  * @param $format
  *   The format object being configured.
  * @param $defaults
- *   The default settings for the filter, as defined in 'default settings' in
+ *   The default settings for the filter, as defined in 'default_settings' in
  *   hook_filter_info(). These should be combined with $filter->settings to
  *   define the form element defaults.
  * @param $filters
@@ -176,11 +85,11 @@ function hook_filter_FILTER_settings($form, &$form_state, $filter, $format, $def
  * Prepare callback for hook_filter_info().
  *
  * Note: This is not really a hook. The function name is manually specified via
- * 'prepare callback' in hook_filter_info(), with this recommended callback
+ * 'prepare_callback' in hook_filter_info(), with this recommended callback
  * name pattern. It is called from check_markup().
  *
  * See hook_filter_info() for a description of the filtering process. Filters
- * should not use the 'prepare callback' step for anything other than escaping,
+ * should not use the 'prepare_callback' step for anything other than escaping,
  * because that would short-circuit the control the user has over the order in
  * which filters are applied.
  *
@@ -211,7 +120,7 @@ function hook_filter_FILTER_prepare($text, $filter, $format, $langcode, $cache,
  * Process callback for hook_filter_info().
  *
  * Note: This is not really a hook. The function name is manually specified via
- * 'process callback' in hook_filter_info(), with this recommended callback
+ * 'process_callback' in hook_filter_info(), with this recommended callback
  * name pattern. It is called from check_markup().
  *
  * See hook_filter_info() for a description of the filtering process. This step
@@ -241,37 +150,6 @@ function hook_filter_FILTER_process($text, $filter, $format, $langcode, $cache,
 }
 
 /**
- * Tips callback for hook_filter_info().
- *
- * Note: This is not really a hook. The function name is manually specified via
- * 'tips callback' in hook_filter_info(), with this recommended callback
- * name pattern. It is called from _filter_tips().
- *
- * A filter's tips should be informative and to the point. Short tips are
- * preferably one-liners.
- *
- * @param $filter
- *   An object representing the filter.
- * @param $format
- *   An object representing the text format the filter is contained in.
- * @param $long
- *   Whether this callback should return a short tip to display in a form
- *   (FALSE), or whether a more elaborate filter tips should be returned for
- *   theme_filter_tips() (TRUE).
- *
- * @return
- *   Translated text to display as a tip.
- */
-function hook_filter_FILTER_tips($filter, $format, $long) {
- if ($long) {
-    return t('Lines and paragraphs are automatically recognized. The &lt;br /&gt; line break, &lt;p&gt; paragraph and &lt;/p&gt; close paragraph tags are inserted automatically. If paragraphs are not recognized simply add a couple blank lines.');
-  }
-  else {
-    return t('Lines and paragraphs break automatically.');
-  }
-}
-
-/**
  * @addtogroup hooks
  * @{
  */
diff --git a/core/modules/filter/filter.install b/core/modules/filter/filter.install
index f3c29a5..4470e19 100644
--- a/core/modules/filter/filter.install
+++ b/core/modules/filter/filter.install
@@ -9,101 +9,6 @@
  * Implements hook_schema().
  */
 function filter_schema() {
-  $schema['filter'] = array(
-    'description' => 'Table that maps filters (HTML corrector) to text formats (Filtered HTML).',
-    'fields' => array(
-      'format' => array(
-        'type' => 'varchar',
-        'length' => 255,
-        'not null' => TRUE,
-        'description' => 'Foreign key: The {filter_format}.format to which this filter is assigned.',
-      ),
-      'module' => array(
-        'type' => 'varchar',
-        'length' => 64,
-        'not null' => TRUE,
-        'default' => '',
-        'description' => 'The origin module of the filter.',
-      ),
-      'name' => array(
-        'type' => 'varchar',
-        'length' => 32,
-        'not null' => TRUE,
-        'default' => '',
-        'description' => 'Name of the filter being referenced.',
-      ),
-      'weight' => array(
-        'type' => 'int',
-        'not null' => TRUE,
-        'default' => 0,
-        'description' => 'Weight of filter within format.',
-      ),
-      'status' => array(
-        'type' => 'int',
-        'not null' => TRUE,
-        'default' => 0,
-        'description' => 'Filter enabled status. (1 = enabled, 0 = disabled)',
-      ),
-      'settings' => array(
-        'type' => 'blob',
-        'not null' => FALSE,
-        'size' => 'big',
-        'serialize' => TRUE,
-        'description' => 'A serialized array of name value pairs that store the filter settings for the specific format.',
-      ),
-    ),
-    'primary key' => array('format', 'name'),
-    'indexes' => array(
-      'list' => array('weight', 'module', 'name'),
-    ),
-  );
-  $schema['filter_format'] = array(
-    'description' => 'Stores text formats: custom groupings of filters, such as Filtered HTML.',
-    'fields' => array(
-      'format' => array(
-        'type' => 'varchar',
-        'length' => 255,
-        'not null' => TRUE,
-        'description' => 'Primary Key: Unique machine name of the format.',
-      ),
-      'name' => array(
-        'type' => 'varchar',
-        'length' => 255,
-        'not null' => TRUE,
-        'default' => '',
-        'description' => 'Name of the text format (Filtered HTML).',
-        'translatable' => TRUE,
-      ),
-      'cache' => array(
-        'type' => 'int',
-        'not null' => TRUE,
-        'default' => 0,
-        'size' => 'tiny',
-        'description' => 'Flag to indicate whether format is cacheable. (1 = cacheable, 0 = not cacheable)',
-      ),
-      'status' => array(
-        'type' => 'int',
-        'unsigned' => TRUE,
-        'not null' => TRUE,
-        'default' => 1,
-        'size' => 'tiny',
-        'description' => 'The status of the text format. (1 = enabled, 0 = disabled)',
-      ),
-      'weight' => array(
-        'type' => 'int',
-        'not null' => TRUE,
-        'default' => 0,
-        'description' => 'Weight of text format to use when listing.',
-      ),
-    ),
-    'primary key' => array('format'),
-    'unique keys' => array(
-      'name' => array('name'),
-    ),
-    'indexes' => array(
-      'status_weight' => array('status', 'weight'),
-    ),
-  );
 
   $schema['cache_filter'] = drupal_get_schema_unprocessed('system', 'cache');
   $schema['cache_filter']['description'] = 'Cache table for the Filter module to store already filtered pieces of text, identified by text format and hash of the text.';
@@ -119,30 +24,7 @@ function filter_install() {
   // users have access to, so add it here. We initialize it as a simple, safe
   // plain text format with very basic formatting, but it can be modified by
   // installation profiles to have other properties.
-  $plain_text_format = array(
-    'format' => 'plain_text',
-    'name' => 'Plain text',
-    'weight' => 10,
-    'filters' => array(
-      // Escape all HTML.
-      'filter_html_escape' => array(
-        'weight' => 0,
-        'status' => 1,
-      ),
-      // URL filter.
-      'filter_url' => array(
-        'weight' => 1,
-        'status' => 1,
-      ),
-      // Line break filter.
-      'filter_autop' => array(
-        'weight' => 2,
-        'status' => 1,
-      ),
-    ),
-  );
-  $plain_text_format = (object) $plain_text_format;
-  filter_format_save($plain_text_format);
+  // See core/modules/filter/config/filter.format.plain_text.yml.
 }
 
 /**
@@ -162,6 +44,47 @@ function filter_update_8000() {
 }
 
 /**
+ * Migrate filter formats into configuration.
+ *
+ * @ingroup config_upgrade
+ */
+function filter_update_8001() {
+  $result = db_query('SELECT * FROM {filter_format}');
+  foreach ($result as $filter_format) {
+    // Find the settings for this format.
+    $filters = array();
+    $settings = db_query('SELECT * FROM {filter} WHERE format = :format', array(':format' => $filter_format->format));
+    foreach($settings as $setting) {
+      $filters[$setting->name] = array(
+        'weight' => $setting->weight,
+        'settings' => unserialize($setting->settings),
+        'status' => $setting->status,
+      );
+    }
+
+    // Save the config object.
+    $config = array(
+      'format' => $filter_format->format,
+      'name' => $filter_format->name,
+      'roles' => array_values(user_roles(FALSE, 'use text format ' . $filter_format->format)),
+      'status' => $filter_format->status,
+      'weight' => $filter_format->weight,
+      'filters' => $filters
+    );
+    $format = entity_create('filter_format', $config);
+    $format->save();
+  }
+}
+
+/**
+ * Drop the {filter} and {filter_format} tables.
+ */
+function filter_update_8002() {
+  db_drop_table('filter');
+  db_drop_table('filter_format');
+}
+
+/**
  * @} End of "defgroup updates-7.x-to-8.x".
  * The next series of updates should start at 9000.
  */
diff --git a/core/modules/filter/filter.module b/core/modules/filter/filter.module
index 1aff070..2ec4366 100644
--- a/core/modules/filter/filter.module
+++ b/core/modules/filter/filter.module
@@ -7,6 +7,7 @@
 
 use Drupal\Core\Cache\CacheBackendInterface;
 use Drupal\Core\Template\Attribute;
+use Drupal\filter\Plugin\Core\Entity\FilterFormat;
 
 /**
  * Non-HTML markup language filters that generate HTML.
@@ -174,6 +175,56 @@ function filter_menu() {
 }
 
 /**
+ * Implements MODULE_config_import_create().
+ */
+function filter_config_import_create($name, $new_config, $old_config) {
+  if (strpos($name, 'filter.format.') !== 0) {
+    return FALSE;
+  }
+
+  $filter_format = entity_create('filter_format', $new_config->get());
+  $filter_format->save();
+  return TRUE;
+}
+
+/**
+ * Implements MODULE_config_import_change().
+ */
+function filter_config_import_change($name, $new_config, $old_config) {
+  if (strpos($name, 'filter.format.') !== 0) {
+    return FALSE;
+  }
+
+  list(, , $id) = explode('.', $name);
+  $filter_format = entity_load('filter_format', $id);
+
+  $filter_format->original = clone $filter_format;
+  foreach ($old_config->get() as $property => $value) {
+    $filter_format->original->$property = $value;
+  }
+
+  foreach ($new_config->get() as $property => $value) {
+    $filter_format->$property = $value;
+  }
+
+  $filter_format->save();
+  return TRUE;
+}
+
+/**
+ * Implements MODULE_config_import_delete().
+ */
+function filter_config_import_delete($name, $new_config, $old_config) {
+  if (strpos($name, 'filter.format.') !== 0) {
+    return FALSE;
+  }
+
+  list(, , $id) = explode('.', $name);
+  entity_delete_multiple('filter_format', array($id));
+  return TRUE;
+}
+
+/**
  * Access callback: Checks access for disabling text formats.
  *
  * @param $format
@@ -209,116 +260,6 @@ function filter_format_load($format_id) {
 }
 
 /**
- * Saves a text format object to the database.
- *
- * @param $format
- *   A format object having the properties:
- *   - format: A machine-readable name representing the ID of the text format
- *     to save. If this corresponds to an existing text format, that format
- *     will be updated; otherwise, a new format will be created.
- *   - name: The title of the text format.
- *   - status: (optional) An integer indicating whether the text format is
- *     enabled (1) or not (0). Defaults to 1.
- *   - weight: (optional) The weight of the text format, which controls its
- *     placement in text format lists. If omitted, the weight is set to 0.
- *   - filters: (optional) An associative, multi-dimensional array of filters
- *     assigned to the text format, keyed by the name of each filter and using
- *     the properties:
- *     - weight: (optional) The weight of the filter in the text format. If
- *       omitted, either the currently stored weight is retained (if there is
- *       one), or the filter is assigned a weight of 10, which will usually
- *       put it at the bottom of the list.
- *     - status: (optional) A Boolean indicating whether the filter is
- *       enabled in the text format. If omitted, the filter will be disabled.
- *     - settings: (optional) An array of configured settings for the filter.
- *       See hook_filter_info() for details.
- *
- * @return
- *   SAVED_NEW or SAVED_UPDATED.
- */
-function filter_format_save($format) {
-  $format->name = trim($format->name);
-  $format->cache = _filter_format_is_cacheable($format);
-  if (!isset($format->status)) {
-    $format->status = 1;
-  }
-  if (!isset($format->weight)) {
-    $format->weight = 0;
-  }
-
-  // Insert or update the text format.
-  $return = db_merge('filter_format')
-    ->key(array('format' => $format->format))
-    ->fields(array(
-      'name' => $format->name,
-      'cache' => (int) $format->cache,
-      'status' => (int) $format->status,
-      'weight' => (int) $format->weight,
-    ))
-    ->execute();
-
-  // Programmatic saves may not contain any filters.
-  if (!isset($format->filters)) {
-    $format->filters = array();
-  }
-  $filter_info = filter_get_filters();
-  foreach ($filter_info as $name => $filter) {
-    // If the format does not specify an explicit weight for a filter, assign
-    // a default weight, either defined in hook_filter_info(), or the default of
-    // 0 by filter_get_filters().
-    if (!isset($format->filters[$name]['weight'])) {
-      $format->filters[$name]['weight'] = $filter['weight'];
-    }
-    $format->filters[$name]['status'] = isset($format->filters[$name]['status']) ? $format->filters[$name]['status'] : 0;
-    $format->filters[$name]['module'] = $filter['module'];
-
-    // If settings were passed, only ensure default settings.
-    if (isset($format->filters[$name]['settings'])) {
-      if (isset($filter['default settings'])) {
-        $format->filters[$name]['settings'] = array_merge($filter['default settings'], $format->filters[$name]['settings']);
-      }
-    }
-    // Otherwise, use default settings or fall back to an empty array.
-    else {
-      $format->filters[$name]['settings'] = isset($filter['default settings']) ? $filter['default settings'] : array();
-    }
-
-    $fields = array();
-    $fields['weight'] = $format->filters[$name]['weight'];
-    $fields['status'] = $format->filters[$name]['status'];
-    $fields['module'] = $format->filters[$name]['module'];
-    $fields['settings'] = serialize($format->filters[$name]['settings']);
-
-    db_merge('filter')
-      ->key(array(
-        'format' => $format->format,
-        'name' => $name,
-      ))
-      ->fields($fields)
-      ->execute();
-  }
-
-  if ($return == SAVED_NEW) {
-    module_invoke_all('filter_format_insert', $format);
-  }
-  else {
-    module_invoke_all('filter_format_update', $format);
-    // Explicitly indicate that the format was updated. We need to do this
-    // since if the filters were updated but the format object itself was not,
-    // the merge query above would not return an indication that anything had
-    // changed.
-    $return = SAVED_UPDATED;
-
-    // Clear the filter cache whenever a text format is updated.
-    cache('filter')->deleteTags(array('filter_format' => $format->format));
-  }
-
-  filter_formats_reset();
-
-  return $return;
-}
-
-/**
  * Disables a text format.
  *
  * There is no core facility to re-enable a disabled format. It is not deleted
@@ -330,10 +271,8 @@ function filter_format_save($format) {
  *   The text format object to be disabled.
  */
 function filter_format_disable($format) {
-  db_update('filter_format')
-    ->fields(array('status' => 0))
-    ->condition('format', $format->format)
-    ->execute();
+  $format->status = 0;
+  $format->save();
 
   // Allow modules to react on text format deletion.
   module_invoke_all('filter_format_disable', $format);
@@ -357,7 +296,8 @@ function filter_format_disable($format) {
  * @see filter_format_load()
  */
 function filter_format_exists($format_id) {
-  return (bool) db_query_range('SELECT 1 FROM {filter_format} WHERE format = :format', 0, 1, array(':format' => $format_id))->fetchField();
+  $formats = entity_load_multiple('filter_format');
+  return !empty($formats[$format_id]);
 }
 
 /**
@@ -456,13 +396,14 @@ function filter_formats($account = NULL) {
       $formats['all'] = $cache->data;
     }
     else {
-      $formats['all'] = db_select('filter_format', 'ff')
-        ->addTag('translatable')
-        ->fields('ff')
-        ->condition('status', 1)
-        ->orderBy('weight')
-        ->execute()
-        ->fetchAllAssoc('format');
+      $filter_formats = entity_load_multiple('filter_format');
+      $formats['all'] = array();
+      foreach ($filter_formats as $format_name => $filter_format) {
+        if (!empty($filter_format->status)) {
+          $formats['all'][$format_name] = $filter_format;
+        }
+      }
+      @uasort($formats['all'], 'Drupal\Core\Config\Entity\ConfigEntityBase::sort');
 
       cache()->set("filter_formats:{$language_interface->langcode}", $formats['all'], CacheBackendInterface::CACHE_PERMANENT, array('filter_formats' => TRUE));
     }
@@ -657,37 +598,29 @@ function filter_get_filters() {
   $filters = &drupal_static(__FUNCTION__, array());
 
   if (empty($filters)) {
-    foreach (module_implements('filter_info') as $module) {
-      $info = module_invoke($module, 'filter_info');
-      if (isset($info) && is_array($info)) {
-        // Assign the name of the module implementing the filters and ensure
-        // default values.
-        foreach (array_keys($info) as $name) {
-          $info[$name]['module'] = $module;
-          $info[$name] += array(
-            'description' => '',
-            'weight' => 0,
-          );
-        }
-        $filters = array_merge($filters, $info);
-      }
-    }
-    // Allow modules to alter filter definitions.
-    drupal_alter('filter_info', $filters);
-
-    uasort($filters, '_filter_list_cmp');
+    $filters = drupal_container()->get('plugin.manager.filter')->getDefinitions();
   }
 
   return $filters;
 }
 
 /**
- * Sorts an array of filters by filter name.
+ * Sorts an array of filters by filter status, weight, module, name.
  *
- * Callback for uasort() within filter_get_filters().
+ * @see filter_list_format()
+ * @see Drupal\filter\Plugin\Core\Entity\FilterFormat::save()
  */
-function _filter_list_cmp($a, $b) {
-  return strcmp($a['title'], $b['title']);
+function _filter_format_filter_cmp($a, $b) {
+  if ($a['status'] != $b['status']) {
+    return !empty($a['status']) ? -1 : 1;
+  }
+  if ($a['weight'] != $b['weight']) {
+    return ($a['weight'] < $b['weight']) ? -1 : 1;
+  }
+  elseif ($a['module'] != $b['module']) {
+    return strcmp($a['module'], $b['module']);
+  }
+  return strcmp($a['name'], $b['name']);
 }
 
 /**
@@ -721,7 +654,7 @@ function filter_format_allowcache($format_id) {
  *   TRUE if all the filters enabled in the given text format allow caching,
  *   FALSE otherwise.
  *
- * @see filter_format_save()
+ * @see Drupal\filter\Plugin\Core\Entity\FilterFormat::save()
  */
 function _filter_format_is_cacheable($format) {
   if (empty($format->filters)) {
@@ -761,9 +694,19 @@ function filter_list_format($format_id) {
       $filters['all'] = $cache->data;
     }
     else {
-      $result = db_query('SELECT * FROM {filter} ORDER BY weight, module, name');
-      foreach ($result as $record) {
-        $filters['all'][$record->format][$record->name] = $record;
+      $filter_formats = filter_formats();
+      foreach ($filter_formats as $filter_format) {
+        foreach ($filter_format->filters as $filter_name => $filter) {
+          $filter['name'] = $filter_name;
+          $filters['all'][$filter_format->format][$filter_name] = $filter;
+        }
+        @uasort($filters['all'][$filter_format->format], '_filter_format_filter_cmp');
+        foreach ($filters['all'][$filter_format->format] as $filter_name => $filter) {
+          // Before Conversion to CMI, filter were objects, now they are arrays.
+          // Convert filters back to objects to reduce the impact of changes.
+          // @todo Follow-up: filters should be arrays instead of objects.
+          $filters['all'][$filter_format->format][$filter_name] = (object)$filter;
+        }
       }
       cache()->set('filter_list_format', $filters['all']);
     }
@@ -775,11 +718,12 @@ function filter_list_format($format_id) {
     foreach ($filter_map as $name => $filter) {
       if (isset($filter_info[$name])) {
         $filter->title = $filter_info[$name]['title'];
-        // Unpack stored filter settings.
-        $filter->settings = (isset($filter->settings) ? unserialize($filter->settings) : array());
+
+        $filter->settings = isset($filter->settings) ? $filter->settings : array();
+
         // Merge in default settings.
-        if (isset($filter_info[$name]['default settings'])) {
-          $filter->settings += $filter_info[$name]['default settings'];
+        if (isset($filter_info[$name]['default_settings'])) {
+          $filter->settings += $filter_info[$name]['default_settings'];
         }
 
         $format_filters[$name] = $filter;
@@ -868,8 +812,8 @@ function check_markup($text, $format_id = NULL, $langcode = '', $cache = FALSE,
     if (in_array($filter_info[$name]['type'], $filter_types_to_skip)) {
       continue;
     }
-    if ($filter->status && isset($filter_info[$name]['prepare callback'])) {
-      $function = $filter_info[$name]['prepare callback'];
+    if ($filter->status && isset($filter_info[$name]['prepare_callback'])) {
+      $function = $filter_info[$name]['prepare_callback'];
       $text = $function($text, $filter, $format, $langcode, $cache, $cache_id);
     }
   }
@@ -880,8 +824,8 @@ function check_markup($text, $format_id = NULL, $langcode = '', $cache = FALSE,
     if (in_array($filter_info[$name]['type'], $filter_types_to_skip)) {
       continue;
     }
-    if ($filter->status && isset($filter_info[$name]['process callback'])) {
-      $function = $filter_info[$name]['process callback'];
+    if ($filter->status && isset($filter_info[$name]['process_callback'])) {
+      $function = $filter_info[$name]['process_callback'];
       $text = $function($text, $filter, $format, $langcode, $cache, $cache_id);
     }
   }
@@ -889,7 +833,7 @@ function check_markup($text, $format_id = NULL, $langcode = '', $cache = FALSE,
   // Cache the filtered text. This cache is infinitely valid. It becomes
   // obsolete when $text changes (which leads to a new $cache_id). It is
   // automatically flushed when the text format is updated.
-  // @see filter_format_save()
+  // @see Drupal\filter\Plugin\Core\Entity\FilterFormat::save()
   if ($cache) {
     cache('filter')->set($cache_id, $text, CacheBackendInterface::CACHE_PERMANENT, array('filter_format' => $format->format));
   }
@@ -1142,6 +1086,7 @@ function _filter_tips($format_id, $long = FALSE) {
 
   $formats = filter_formats($user);
   $filter_info = filter_get_filters();
+  $filter_manager = drupal_container()->get('plugin.manager.filter');
 
   $tips = array();
 
@@ -1152,10 +1097,9 @@ function _filter_tips($format_id, $long = FALSE) {
 
   foreach ($formats as $format) {
     $filters = filter_list_format($format->format);
-    $tips[$format->name] = array();
     foreach ($filters as $name => $filter) {
-      if ($filter->status && isset($filter_info[$name]['tips callback'])) {
-        $tip = $filter_info[$name]['tips callback']($filter, $format, $long);
+      if ($filter->status) {
+        $tip = $filter_manager->createInstance($name)->tips($filter, $format, $long);
         if (isset($tip)) {
           $tips[$format->name][$name] = array('tip' => $tip, 'id' => $name);
         }
@@ -1297,64 +1241,6 @@ function theme_filter_guidelines($variables) {
  */
 
 /**
- * Implements hook_filter_info().
- */
-function filter_filter_info() {
-  $filters['filter_html'] = array(
-    'title' => t('Limit allowed HTML tags'),
-    'type' => FILTER_TYPE_HTML_RESTRICTOR,
-    'process callback' => '_filter_html',
-    'settings callback' => '_filter_html_settings',
-    'default settings' => array(
-      'allowed_html' => '<a> <em> <strong> <cite> <blockquote> <code> <ul> <ol> <li> <dl> <dt> <dd> <h4> <h5> <h6>',
-      'filter_html_help' => 1,
-      'filter_html_nofollow' => 0,
-    ),
-    'tips callback' => '_filter_html_tips',
-    'weight' => -10,
-  );
-  $filters['filter_autop'] = array(
-    'title' => t('Convert line breaks into HTML (i.e. <code>&lt;br&gt;</code> and <code>&lt;p&gt;</code>)'),
-    'type' => FILTER_TYPE_MARKUP_LANGUAGE,
-    'process callback' => '_filter_autop',
-    'tips callback' => '_filter_autop_tips',
-  );
-  $filters['filter_url'] = array(
-    'title' => t('Convert URLs into links'),
-    'type' => FILTER_TYPE_MARKUP_LANGUAGE,
-    'process callback' => '_filter_url',
-    'settings callback' => '_filter_url_settings',
-    'default settings' => array(
-      'filter_url_length' => 72,
-    ),
-    'tips callback' => '_filter_url_tips',
-  );
-  $filters['filter_html_image_secure'] = array(
-    'title' => t('Restrict images to this site'),
-    'type' => FILTER_TYPE_HTML_RESTRICTOR,
-    'description' => t('Disallows usage of &lt;img&gt; tag sources that are not hosted on this site by replacing them with a placeholder image.'),
-    'process callback' => '_filter_html_image_secure_process',
-    'tips callback' => '_filter_html_image_secure_tips',
-    // Supposed to run after other filters and before HTML corrector by default.
-    'weight' => 9,
-  );
-  $filters['filter_htmlcorrector'] = array(
-    'title' =>  t('Correct faulty and chopped off HTML'),
-    'type' => FILTER_TYPE_HTML_RESTRICTOR,
-    'process callback' => '_filter_htmlcorrector',
-    'weight' => 10,
-  );
-  $filters['filter_html_escape'] = array(
-    'title' => t('Display any HTML as plain text'),
-    'type' => FILTER_TYPE_HTML_RESTRICTOR,
-    'process callback' => '_filter_html_escape',
-    'tips callback' => '_filter_html_escape_tips',
-    'weight' => -10,
-  );
-  return $filters;
-}
-
-/**
  * Filter settings callback for the HTML content filter.
  *
  * See hook_filter_FILTER_settings() for documentation of parameters and return
@@ -1403,106 +1289,6 @@ function _filter_html($text, $filter) {
 }
 
 /**
- * Filter tips callback: Provides help for the HTML filter.
- *
- * @see filter_filter_info()
- */
-function _filter_html_tips($filter, $format, $long = FALSE) {
-  global $base_url;
-
-  if (!($allowed_html = $filter->settings['allowed_html'])) {
-    return;
-  }
-  $output = t('Allowed HTML tags: @tags', array('@tags' => $allowed_html));
-  if (!$long) {
-    return $output;
-  }
-
-  $output = '<p>' . $output . '</p>';
-  if (!$filter->settings['filter_html_help']) {
-    return $output;
-  }
-
-  $output .= '<p>' . t('This site allows HTML content. While learning all of HTML may feel intimidating, learning how to use a very small number of the most basic HTML "tags" is very easy. This table provides examples for each tag that is enabled on this site.') . '</p>';
-  $output .= '<p>' . t('For more information see W3C\'s <a href="@html-specifications">HTML Specifications</a> or use your favorite search engine to find other sites that explain HTML.', array('@html-specifications' => 'http://www.w3.org/TR/html/')) . '</p>';
-  $tips = array(
-    'a' => array(t('Anchors are used to make links to other pages.'), '<a href="' . $base_url . '">' . check_plain(config('system.site')->get('name')) . '</a>'),
-    'br' => array(t('By default line break tags are automatically added, so use this tag to add additional ones. Use of this tag is different because it is not used with an open/close pair like all the others. Use the extra " /" inside the tag to maintain XHTML 1.0 compatibility'), t('Text with <br />line break')),
-    'p' => array(t('By default paragraph tags are automatically added, so use this tag to add additional ones.'), '<p>' . t('Paragraph one.') . '</p> <p>' . t('Paragraph two.') . '</p>'),
-    'strong' => array(t('Strong', array(), array('context' => 'Font weight')), '<strong>' . t('Strong', array(), array('context' => 'Font weight')) . '</strong>'),
-    'em' => array(t('Emphasized'), '<em>' . t('Emphasized') . '</em>'),
-    'cite' => array(t('Cited'), '<cite>' . t('Cited') . '</cite>'),
-    'code' => array(t('Coded text used to show programming source code'), '<code>' . t('Coded') . '</code>'),
-    'b' => array(t('Bolded'), '<b>' . t('Bolded') . '</b>'),
-    'u' => array(t('Underlined'), '<u>' . t('Underlined') . '</u>'),
-    'i' => array(t('Italicized'), '<i>' . t('Italicized') . '</i>'),
-    'sup' => array(t('Superscripted'), t('<sup>Super</sup>scripted')),
-    'sub' => array(t('Subscripted'), t('<sub>Sub</sub>scripted')),
-    'pre' => array(t('Preformatted'), '<pre>' . t('Preformatted') . '</pre>'),
-    'abbr' => array(t('Abbreviation'), t('<abbr title="Abbreviation">Abbrev.</abbr>')),
-    'acronym' => array(t('Acronym'), t('<acronym title="Three-Letter Acronym">TLA</acronym>')),
-    'blockquote' => array(t('Block quoted'), '<blockquote>' . t('Block quoted') . '</blockquote>'),
-    'q' => array(t('Quoted inline'), '<q>' . t('Quoted inline') . '</q>'),
-    // Assumes and describes tr, td, th.
-    'table' => array(t('Table'), '<table> <tr><th>' . t('Table header') . '</th></tr> <tr><td>' . t('Table cell') . '</td></tr> </table>'),
-    'tr' => NULL, 'td' => NULL, 'th' => NULL,
-    'del' => array(t('Deleted'), '<del>' . t('Deleted') . '</del>'),
-    'ins' => array(t('Inserted'), '<ins>' . t('Inserted') . '</ins>'),
-     // Assumes and describes li.
-    'ol' => array(t('Ordered list - use the &lt;li&gt; to begin each list item'), '<ol> <li>' . t('First item') . '</li> <li>' . t('Second item') . '</li> </ol>'),
-    'ul' => array(t('Unordered list - use the &lt;li&gt; to begin each list item'), '<ul> <li>' . t('First item') . '</li> <li>' . t('Second item') . '</li> </ul>'),
-    'li' => NULL,
-    // Assumes and describes dt and dd.
-    'dl' => array(t('Definition lists are similar to other HTML lists. &lt;dl&gt; begins the definition list, &lt;dt&gt; begins the definition term and &lt;dd&gt; begins the definition description.'), '<dl> <dt>' . t('First term') . '</dt> <dd>' . t('First definition') . '</dd> <dt>' . t('Second term') . '</dt> <dd>' . t('Second definition') . '</dd> </dl>'),
-    'dt' => NULL, 'dd' => NULL,
-    'h1' => array(t('Heading'), '<h1>' . t('Title') . '</h1>'),
-    'h2' => array(t('Heading'), '<h2>' . t('Subtitle') . '</h2>'),
-    'h3' => array(t('Heading'), '<h3>' . t('Subtitle three') . '</h3>'),
-    'h4' => array(t('Heading'), '<h4>' . t('Subtitle four') . '</h4>'),
-    'h5' => array(t('Heading'), '<h5>' . t('Subtitle five') . '</h5>'),
-    'h6' => array(t('Heading'), '<h6>' . t('Subtitle six') . '</h6>')
-  );
-  $header = array(t('Tag Description'), t('You Type'), t('You Get'));
-  preg_match_all('/<([a-z0-9]+)[^a-z0-9]/i', $allowed_html, $out);
-  foreach ($out[1] as $tag) {
-    if (!empty($tips[$tag])) {
-      $rows[] = array(
-        array('data' => $tips[$tag][0], 'class' => array('description')),
-        array('data' => '<code>' . check_plain($tips[$tag][1]) . '</code>', 'class' => array('type')),
-        array('data' => $tips[$tag][1], 'class' => array('get'))
-      );
-    }
-    else {
-      $rows[] = array(
-        array('data' => t('No help provided for tag %tag.', array('%tag' => $tag)), 'class' => array('description'), 'colspan' => 3),
-      );
-    }
-  }
-  $output .= theme('table', array('header' => $header, 'rows' => $rows));
-
-  $output .= '<p>' . t('Most unusual characters can be directly entered without any problems.') . '</p>';
-  $output .= '<p>' . t('If you do encounter problems, try using HTML character entities. A common example looks like &amp;amp; for an ampersand &amp; character. For a full list of entities see HTML\'s <a href="@html-entities">entities</a> page. Some of the available characters include:', array('@html-entities' => 'http://www.w3.org/TR/html4/sgml/entities.html')) . '</p>';
-
-  $entities = array(
-    array(t('Ampersand'), '&amp;'),
-    array(t('Greater than'), '&gt;'),
-    array(t('Less than'), '&lt;'),
-    array(t('Quotation mark'), '&quot;'),
-  );
-  $header = array(t('Character Description'), t('You Type'), t('You Get'));
-  unset($rows);
-  foreach ($entities as $entity) {
-    $rows[] = array(
-      array('data' => $entity[0], 'class' => array('description')),
-      array('data' => '<code>' . check_plain($entity[1]) . '</code>', 'class' => array('type')),
-      array('data' => $entity[1], 'class' => array('get'))
-    );
-  }
-  $output .= theme('table', array('header' => $header, 'rows' => $rows));
-  return $output;
-}
-
-/**
  * Filter URL settings callback: Provides settings for the URL filter.
  *
  * @see filter_filter_info()
@@ -1747,15 +1533,6 @@ function _filter_url_trim($text, $length = NULL) {
 }
 
 /**
- * Filter tips callback: Provides help for the URL filter.
- *
- * @see filter_filter_info()
- */
-function _filter_url_tips($filter, $format, $long = FALSE) {
-  return t('Web page addresses and e-mail addresses turn into links automatically.');
-}
-
-/**
  * Scans the input and makes sure that HTML tags are properly closed.
  */
 function _filter_htmlcorrector($text) {
@@ -1830,20 +1607,6 @@ function _filter_autop($text) {
 }
 
 /**
- * Filter tips callback: Provides help for the auto-paragraph filter.
- *
- * @see filter_filter_info()
- */
-function _filter_autop_tips($filter, $format, $long = FALSE) {
-  if ($long) {
-    return t('Lines and paragraphs are automatically recognized. The &lt;br /&gt; line break, &lt;p&gt; paragraph and &lt;/p&gt; close paragraph tags are inserted automatically. If paragraphs are not recognized simply add a couple blank lines.');
-  }
-  else {
-    return t('Lines and paragraphs break automatically.');
-  }
-}
-
-/**
  * Escapes all HTML tags, so they will be visible instead of being effective.
  */
 function _filter_html_escape($text) {
@@ -1851,15 +1614,6 @@ function _filter_html_escape($text) {
 }
 
 /**
- * Filter tips callback: Provides help for the HTML escaping filter.
- *
- * @see filter_filter_info()
- */
-function _filter_html_escape_tips($filter, $format, $long = FALSE) {
-  return t('No HTML tags allowed.');
-}
-
-/**
  * Process callback for local image filter.
  */
 function _filter_html_image_secure_process($text) {
@@ -1925,13 +1679,6 @@ function theme_filter_html_image_secure_image(&$variables) {
 }
 
 /**
- * Filter tips callback for secure HTML image filter.
- */
-function _filter_html_image_secure_tips($filter, $format, $long = FALSE) {
-  return t('Only images hosted on this site may be used in &lt;img&gt; tags.');
-}
-
-/**
  * @} End of "defgroup standard_filters".
  */
 
diff --git a/core/modules/filter/lib/Drupal/filter/FilterBundle.php b/core/modules/filter/lib/Drupal/filter/FilterBundle.php
new file mode 100644
index 0000000..a882ad4
--- /dev/null
+++ b/core/modules/filter/lib/Drupal/filter/FilterBundle.php
@@ -0,0 +1,25 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\filter\FilterBundle.
+ */
+
+namespace Drupal\filter;
+
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\HttpKernel\Bundle\Bundle;
+
+/**
+ * Filter dependency injection container.
+ */
+class FilterBundle extends Bundle {
+
+  /**
+   * Overrides \Symfony\Component\HttpKernel\Bundle\Bundle::build().
+   */
+  public function build(ContainerBuilder $container) {
+    $container->register('plugin.manager.filter', 'Drupal\filter\FilterManager');
+  }
+
+}
diff --git a/core/modules/filter/lib/Drupal/filter/FilterManager.php b/core/modules/filter/lib/Drupal/filter/FilterManager.php
new file mode 100644
index 0000000..a4ef4c5
--- /dev/null
+++ b/core/modules/filter/lib/Drupal/filter/FilterManager.php
@@ -0,0 +1,110 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\filter\FilterManager.
+ */
+
+namespace Drupal\filter;
+
+use Drupal\Component\Plugin\PluginManagerBase;
+use Drupal\Component\Plugin\Factory\DefaultFactory;
+use Drupal\Component\Plugin\Discovery\ProcessDecorator;
+use Drupal\Core\Plugin\Discovery\AlterDecorator;
+use Drupal\Core\Plugin\Discovery\AnnotatedClassDiscovery;
+
+/**
+ * Manages content filters.
+ *
+ * User submitted content is passed through a group of filters before it is
+ * output in HTML, in order to remove insecure or unwanted parts, correct or
+ * enhance the formatting, transform special keywords, etc. A group of filters
+ * is referred to as a "text format". Administrators can create as many text
+ * formats as needed. Individual filters can be enabled and configured
+ * differently for each text format.
+ *
+ * This hook is invoked by filter_get_filters() and allows modules to register
+ * input filters they provide.
+ *
+ * Filtering is a two-step process. First, the content is 'prepared' by calling
+ * the 'prepare_callback' function for every filter. The purpose of the
+ * 'prepare_callback' is to escape HTML-like structures. For example, imagine a
+ * filter which allows the user to paste entire chunks of programming code
+ * without requiring manual escaping of special HTML characters like < or &. If
+ * the programming code were left untouched, then other filters could think it
+ * was HTML and change it. For many filters, the prepare step is not necessary.
+ *
+ * The second step is the actual processing step. The result from passing the
+ * text through all the filters' prepare steps gets passed to all the filters
+ * again, this time with the 'process_callback' function. The process callbacks
+ * should then actually change the content: transform URLs into hyperlinks,
+ * convert smileys into images, etc.
+ *
+ * For performance reasons content is only filtered once; the result is stored
+ * in the cache table and retrieved from the cache the next time the same piece
+ * of content is displayed. If a filter's output is dynamic, it can override
+ * the cache mechanism, but obviously this should be used with caution: having
+ * one filter that does not support caching in a particular text format
+ * disables caching for the entire format, not just for one filter.
+ *
+ * Beware of the filter cache when developing your module: it is advised to set
+ * your filter to 'cache' => FALSE while developing, but be sure to remove that
+ * setting if it's not needed, when you are no longer in development mode.
+ *
+ * @return
+ *   An associative array of filters, whose keys are internal filter names,
+ *   which should be unique and therefore prefixed with the name of the module.
+ *   Each value is an associative array describing the filter, with the
+ *   following elements (all are optional except as noted):
+ *   - title: (required) An administrative summary of what the filter does.
+ *   - description: Additional administrative information about the filter's
+ *     behavior, if needed for clarification.
+ *   - settings callback: The name of a function that returns configuration
+ *     form elements for the filter. See hook_filter_FILTER_settings() for
+ *     details.
+ *   - default settings: An associative array containing default settings for
+ *     the filter, to be applied when the filter has not been configured yet.
+ *   - prepare callback: The name of a function that escapes the content before
+ *     the actual filtering happens. See hook_filter_FILTER_prepare() for
+ *     details.
+ *   - process callback: (required) The name the function that performs the
+ *     actual filtering. See hook_filter_FILTER_process() for details.
+ *   - cache (default TRUE): Specifies whether the filtered text can be cached.
+ *     Note that setting this to FALSE makes the entire text format not
+ *     cacheable, which may have an impact on the site's overall performance.
+ *     See filter_format_allowcache() for details.
+ *   - weight: A default weight for the filter in new text formats.
+ *
+ * @see filter_example.module
+ * @see hook_filter_info_alter()
+ */
+class FilterManager extends PluginManagerBase {
+
+  /**
+   * Constructs a FilterManager object.
+   */
+  public function __construct() {
+    $this->discovery = new AnnotatedClassDiscovery('filter', 'filter');
+    $this->discovery = new AlterDecorator($this->discovery, 'filter_info');
+    $this->discovery = new ProcessDecorator($this->discovery, array($this, 'processDefinition'));
+
+    $this->factory = new DefaultFactory($this->discovery);
+
+    $this->defaults += array(
+      'description' => '',
+      'weight' => 0,
+    );
+  }
+
+  /**
+   * Overrides \Drupal\Component\Plugin\PluginManagerBase::getDefinitions().
+   */
+  public function getDefinitions() {
+    $definitions = parent::getDefinitions();
+    uasort($definitions, function($a, $b) {
+      return strcmp($a['title'], $b['title']);
+    });
+    return $definitions;
+  }
+
+}
diff --git a/core/modules/filter/lib/Drupal/filter/Plugin/Core/Entity/FilterFormat.php b/core/modules/filter/lib/Drupal/filter/Plugin/Core/Entity/FilterFormat.php
new file mode 100644
index 0000000..dd8a7ec
--- /dev/null
+++ b/core/modules/filter/lib/Drupal/filter/Plugin/Core/Entity/FilterFormat.php
@@ -0,0 +1,160 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\filter\Plugin\Core\Entity\FilterFormat.
+ */
+
+namespace Drupal\filter\Plugin\Core\Entity;
+
+use Drupal\Core\Config\Entity\ConfigEntityBase;
+use Drupal\Core\Annotation\Plugin;
+use Drupal\Core\Annotation\Translation;
+
+/**
+ * Defines the Filter Format entity.
+ *
+ * @Plugin(
+ *   id = "filter_format",
+ *   label = @Translation("Filter Format"),
+ *   module = "filter",
+ *   controller_class = "Drupal\Core\Config\Entity\ConfigStorageController",
+ *   config_prefix = "filter.format",
+ *   entity_keys = {
+ *     "id" = "format",
+ *     "label" = "name",
+ *     "uuid" = "uuid"
+ *   }
+ * )
+ */
+class FilterFormat extends ConfigEntityBase {
+
+  /**
+   * Unique machine name of the format.
+   *
+   * @var string
+   */
+  public $format;
+
+  /**
+   * Name of the text format (Filtered HTML).
+   *
+   * @var string
+   */
+  public $name;
+
+  /**
+   * Flag to indicate whether format is cacheable. (1 = cacheable, 0 = not
+   * cacheable).
+   *
+   * @var int
+   */
+  public $cache = 0;
+
+  /**
+   * The status of the text format. (1 = enabled, 0 = disabled)
+   *
+   * @var int
+   */
+  public $status = 1;
+
+  /**
+   * Weight of text format to use when listing.
+   *
+   * @var int
+   */
+  public $weight = 0;
+
+  /**
+   * An array of name value pairs of the roles that can use this format.
+   *
+   * @var array
+   */
+  public $roles = array();
+
+  /**
+   * An array of name value pairs of the enabled filters for this text format.
+   *
+   * Each element of this array must contain at least the following values:
+   *   - weight: Weight of filter within format.
+   *   - settings: An array of name value pairs that store the filter settings
+   *     for the specific format.
+   *
+   * @var array
+   */
+  public $filters = array();
+
+  /**
+   * Implements Drupal\Core\Entity\EntityInterface::id().
+   */
+  public function id() {
+    return $this->format;
+  }
+
+  /**
+   * Implements Drupal\Core\Entity\EntityInterface::save().
+   */
+  public function save() {
+    $this->name = trim($this->name);
+    $this->cache = _filter_format_is_cacheable($this);
+    if (!isset($this->status)) {
+      $this->status = 1;
+    }
+    if (!isset($this->weight)) {
+      $this->weight = 0;
+    }
+
+    // Programmatic saves may not contain any filters.
+    if (!isset($this->filters)) {
+      $this->filters = array();
+    }
+    $filter_info = filter_get_filters();
+    foreach ($filter_info as $name => $filter) {
+      // If the format does not specify an explicit weight for a filter, assign
+      // a default weight, either defined in hook_filter_info(), or the default of
+      // 0 by filter_get_filters().
+      if (!isset($this->filters[$name]['weight'])) {
+        $this->filters[$name]['weight'] = $filter['weight'];
+      }
+      $this->filters[$name]['status'] = isset($this->filters[$name]['status']) ? $this->filters[$name]['status'] : 0;
+      $this->filters[$name]['module'] = $filter['module'];
+
+      // If settings were passed, only ensure default settings.
+      if (isset($this->filters[$name]['settings'])) {
+        if (isset($filter['default_settings'])) {
+          $this->filters[$name]['settings'] = array_merge($filter['default_settings'], $this->filters[$name]['settings']);
+        }
+      }
+      // Otherwise, use default settings or fall back to an empty array.
+      else {
+        $this->filters[$name]['settings'] = isset($filter['default_settings']) ? $filter['default_settings'] : array();
+      }
+
+      // Sort filters properties by key, to minimize diff issues.
+      ksort($this->filters[$name]);
+    }
+
+    // Sort filters by enabled/disabled first then by weight
+    @uasort($this->filters, '_filter_format_filter_cmp');
+
+    $return = parent::save();
+
+    if ($return == SAVED_UPDATED) {
+      // Clear the filter cache whenever a text format is updated.
+      cache('filter')->deleteTags(array('filter_format' => $this->format));
+    }
+
+    filter_formats_reset();
+
+    if (!empty($this->status)) {
+      // Save user permissions.
+      if ($permission = filter_permission_name($this)) {
+        foreach ($this->roles as $rid => $enabled) {
+          user_role_change_permissions($rid, array($permission => $enabled));
+        }
+      }
+    }
+
+    return $return;
+  }
+}
diff --git a/core/modules/filter/lib/Drupal/filter/Plugin/filter/filter/FilterAutoP.php b/core/modules/filter/lib/Drupal/filter/Plugin/filter/filter/FilterAutoP.php
new file mode 100644
index 0000000..9c41a4c
--- /dev/null
+++ b/core/modules/filter/lib/Drupal/filter/Plugin/filter/filter/FilterAutoP.php
@@ -0,0 +1,40 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\filter\Plugin\filter\filter\FilterAutoP.
+ */
+
+namespace Drupal\filter\Plugin\filter\filter;
+
+use Drupal\Core\Annotation\Plugin;
+use Drupal\Core\Annotation\Translation;
+use Drupal\filter\Plugin\filter\filter\FilterBase;
+use Drupal\filter\Plugin\Core\Entity\FilterFormat;
+
+/**
+ * @todo.
+ *
+ * @Plugin(
+ *   id = "filter_autop",
+ *   module = "filter",
+ *   title = @Translation("Convert line breaks into HTML (i.e. <code>&lt;br&gt;</code> and <code>&lt;p&gt;</code>)"),
+ *   type = FILTER_TYPE_MARKUP_LANGUAGE,
+ *   process_callback = "_filter_autop"
+ * )
+ */
+class FilterAutoP extends FilterBase {
+
+  /**
+   * Overrides \Drupal\filter\Plugin\filter\filter\Plugin\filter\filter\FilterBase::tips().
+   */
+  public function tips($filter, FilterFormat $format, $long = FALSE) {
+    if ($long) {
+      return t('Lines and paragraphs are automatically recognized. The &lt;br /&gt; line break, &lt;p&gt; paragraph and &lt;/p&gt; close paragraph tags are inserted automatically. If paragraphs are not recognized simply add a couple blank lines.');
+    }
+    else {
+      return t('Lines and paragraphs break automatically.');
+    }
+  }
+
+}
diff --git a/core/modules/filter/lib/Drupal/filter/Plugin/filter/filter/FilterBase.php b/core/modules/filter/lib/Drupal/filter/Plugin/filter/filter/FilterBase.php
new file mode 100644
index 0000000..6ec9d0e
--- /dev/null
+++ b/core/modules/filter/lib/Drupal/filter/Plugin/filter/filter/FilterBase.php
@@ -0,0 +1,24 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\filter\Plugin\filter\filter\FilterBase.
+ */
+
+namespace Drupal\filter\Plugin\filter\filter;
+
+use Drupal\Component\Plugin\PluginBase;
+use Drupal\filter\Plugin\Core\Entity\FilterFormat;
+
+/**
+ * @todo.
+ */
+abstract class FilterBase extends PluginBase implements FilterInterface {
+
+  /**
+   * Implements \Drupal\filter\Plugin\filter\filter\Plugin\filter\filter\FilterInterface::tips().
+   */
+  public function tips($filter, FilterFormat $format, $long = FALSE) {
+  }
+
+}
diff --git a/core/modules/filter/lib/Drupal/filter/Plugin/filter/filter/FilterHtml.php b/core/modules/filter/lib/Drupal/filter/Plugin/filter/filter/FilterHtml.php
new file mode 100644
index 0000000..94d5b34
--- /dev/null
+++ b/core/modules/filter/lib/Drupal/filter/Plugin/filter/filter/FilterHtml.php
@@ -0,0 +1,133 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\filter\Plugin\filter\filter\FilterHtml.
+ */
+
+namespace Drupal\filter\Plugin\filter\filter;
+
+use Drupal\Core\Annotation\Plugin;
+use Drupal\Core\Annotation\Translation;
+use Drupal\filter\Plugin\filter\filter\FilterBase;
+use Drupal\filter\Plugin\Core\Entity\FilterFormat;
+
+/**
+ * @todo.
+ *
+ * @Plugin(
+ *   id = "filter_html",
+ *   module = "filter",
+ *   title = @Translation("Limit allowed HTML tags"),
+ *   type = FILTER_TYPE_HTML_RESTRICTOR,
+ *   process_callback = "_filter_html",
+ *   settings_callback = "_filter_html_settings",
+ *   default_settings = {
+ *     "allowed_html" = "<a> <em> <strong> <cite> <blockquote> <code> <ul> <ol> <li> <dl> <dt> <dd> <h4> <h5> <h6>",
+ *     "filter_html_help" = 1,
+ *     "filter_html_nofollow" = 0
+ *   },
+ *   weight = -10
+ * )
+ */
+class FilterHtml extends FilterBase {
+
+  /**
+   * Overrides \Drupal\filter\Plugin\filter\filter\Plugin\filter\filter\FilterBase::tips().
+   */
+  public function tips($filter, FilterFormat $format, $long = FALSE) {
+    global $base_url;
+
+    if (!($allowed_html = $filter->settings['allowed_html'])) {
+      return;
+    }
+    $output = t('Allowed HTML tags: @tags', array('@tags' => $allowed_html));
+    if (!$long) {
+      return $output;
+    }
+
+    $output = '<p>' . $output . '</p>';
+    if (!$filter->settings['filter_html_help']) {
+      return $output;
+    }
+
+    $output .= '<p>' . t('This site allows HTML content. While learning all of HTML may feel intimidating, learning how to use a very small number of the most basic HTML "tags" is very easy. This table provides examples for each tag that is enabled on this site.') . '</p>';
+    $output .= '<p>' . t('For more information see W3C\'s <a href="@html-specifications">HTML Specifications</a> or use your favorite search engine to find other sites that explain HTML.', array('@html-specifications' => 'http://www.w3.org/TR/html/')) . '</p>';
+    $tips = array(
+      'a' => array(t('Anchors are used to make links to other pages.'), '<a href="' . $base_url . '">' . check_plain(config('system.site')->get('name')) . '</a>'),
+      'br' => array(t('By default line break tags are automatically added, so use this tag to add additional ones. Use of this tag is different because it is not used with an open/close pair like all the others. Use the extra " /" inside the tag to maintain XHTML 1.0 compatibility'), t('Text with <br />line break')),
+      'p' => array(t('By default paragraph tags are automatically added, so use this tag to add additional ones.'), '<p>' . t('Paragraph one.') . '</p> <p>' . t('Paragraph two.') . '</p>'),
+      'strong' => array(t('Strong', array(), array('context' => 'Font weight')), '<strong>' . t('Strong', array(), array('context' => 'Font weight')) . '</strong>'),
+      'em' => array(t('Emphasized'), '<em>' . t('Emphasized') . '</em>'),
+      'cite' => array(t('Cited'), '<cite>' . t('Cited') . '</cite>'),
+      'code' => array(t('Coded text used to show programming source code'), '<code>' . t('Coded') . '</code>'),
+      'b' => array(t('Bolded'), '<b>' . t('Bolded') . '</b>'),
+      'u' => array(t('Underlined'), '<u>' . t('Underlined') . '</u>'),
+      'i' => array(t('Italicized'), '<i>' . t('Italicized') . '</i>'),
+      'sup' => array(t('Superscripted'), t('<sup>Super</sup>scripted')),
+      'sub' => array(t('Subscripted'), t('<sub>Sub</sub>scripted')),
+      'pre' => array(t('Preformatted'), '<pre>' . t('Preformatted') . '</pre>'),
+      'abbr' => array(t('Abbreviation'), t('<abbr title="Abbreviation">Abbrev.</abbr>')),
+      'acronym' => array(t('Acronym'), t('<acronym title="Three-Letter Acronym">TLA</acronym>')),
+      'blockquote' => array(t('Block quoted'), '<blockquote>' . t('Block quoted') . '</blockquote>'),
+      'q' => array(t('Quoted inline'), '<q>' . t('Quoted inline') . '</q>'),
+      // Assumes and describes tr, td, th.
+      'table' => array(t('Table'), '<table> <tr><th>' . t('Table header') . '</th></tr> <tr><td>' . t('Table cell') . '</td></tr> </table>'),
+      'tr' => NULL, 'td' => NULL, 'th' => NULL,
+      'del' => array(t('Deleted'), '<del>' . t('Deleted') . '</del>'),
+      'ins' => array(t('Inserted'), '<ins>' . t('Inserted') . '</ins>'),
+       // Assumes and describes li.
+      'ol' => array(t('Ordered list - use the &lt;li&gt; to begin each list item'), '<ol> <li>' . t('First item') . '</li> <li>' . t('Second item') . '</li> </ol>'),
+      'ul' => array(t('Unordered list - use the &lt;li&gt; to begin each list item'), '<ul> <li>' . t('First item') . '</li> <li>' . t('Second item') . '</li> </ul>'),
+      'li' => NULL,
+      // Assumes and describes dt and dd.
+      'dl' => array(t('Definition lists are similar to other HTML lists. &lt;dl&gt; begins the definition list, &lt;dt&gt; begins the definition term and &lt;dd&gt; begins the definition description.'), '<dl> <dt>' . t('First term') . '</dt> <dd>' . t('First definition') . '</dd> <dt>' . t('Second term') . '</dt> <dd>' . t('Second definition') . '</dd> </dl>'),
+      'dt' => NULL, 'dd' => NULL,
+      'h1' => array(t('Heading'), '<h1>' . t('Title') . '</h1>'),
+      'h2' => array(t('Heading'), '<h2>' . t('Subtitle') . '</h2>'),
+      'h3' => array(t('Heading'), '<h3>' . t('Subtitle three') . '</h3>'),
+      'h4' => array(t('Heading'), '<h4>' . t('Subtitle four') . '</h4>'),
+      'h5' => array(t('Heading'), '<h5>' . t('Subtitle five') . '</h5>'),
+      'h6' => array(t('Heading'), '<h6>' . t('Subtitle six') . '</h6>')
+    );
+    $header = array(t('Tag Description'), t('You Type'), t('You Get'));
+    preg_match_all('/<([a-z0-9]+)[^a-z0-9]/i', $allowed_html, $out);
+    foreach ($out[1] as $tag) {
+      if (!empty($tips[$tag])) {
+        $rows[] = array(
+          array('data' => $tips[$tag][0], 'class' => array('description')),
+          array('data' => '<code>' . check_plain($tips[$tag][1]) . '</code>', 'class' => array('type')),
+          array('data' => $tips[$tag][1], 'class' => array('get'))
+        );
+      }
+      else {
+        $rows[] = array(
+          array('data' => t('No help provided for tag %tag.', array('%tag' => $tag)), 'class' => array('description'), 'colspan' => 3),
+        );
+      }
+    }
+    $output .= theme('table', array('header' => $header, 'rows' => $rows));
+
+    $output .= '<p>' . t('Most unusual characters can be directly entered without any problems.') . '</p>';
+    $output .= '<p>' . t('If you do encounter problems, try using HTML character entities. A common example looks like &amp;amp; for an ampersand &amp; character. For a full list of entities see HTML\'s <a href="@html-entities">entities</a> page. Some of the available characters include:', array('@html-entities' => 'http://www.w3.org/TR/html4/sgml/entities.html')) . '</p>';
+
+    $entities = array(
+      array(t('Ampersand'), '&amp;'),
+      array(t('Greater than'), '&gt;'),
+      array(t('Less than'), '&lt;'),
+      array(t('Quotation mark'), '&quot;'),
+    );
+    $header = array(t('Character Description'), t('You Type'), t('You Get'));
+    unset($rows);
+    foreach ($entities as $entity) {
+      $rows[] = array(
+        array('data' => $entity[0], 'class' => array('description')),
+        array('data' => '<code>' . check_plain($entity[1]) . '</code>', 'class' => array('type')),
+        array('data' => $entity[1], 'class' => array('get'))
+      );
+    }
+    $output .= theme('table', array('header' => $header, 'rows' => $rows));
+    return $output;
+  }
+
+}
diff --git a/core/modules/filter/lib/Drupal/filter/Plugin/filter/filter/FilterHtmlCorrector.php b/core/modules/filter/lib/Drupal/filter/Plugin/filter/filter/FilterHtmlCorrector.php
new file mode 100644
index 0000000..421eb76
--- /dev/null
+++ b/core/modules/filter/lib/Drupal/filter/Plugin/filter/filter/FilterHtmlCorrector.php
@@ -0,0 +1,28 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\filter\Plugin\filter\filter\FilterHtmlCorrector.
+ */
+
+namespace Drupal\filter\Plugin\filter\filter;
+
+use Drupal\Core\Annotation\Plugin;
+use Drupal\Core\Annotation\Translation;
+use Drupal\filter\Plugin\filter\filter\FilterBase;
+
+/**
+ * @todo.
+ *
+ * @Plugin(
+ *   id = "filter_htmlcorrector",
+ *   module = "filter",
+ *   title = @Translation("Correct faulty and chopped off HTML"),
+ *   type = FILTER_TYPE_HTML_RESTRICTOR,
+ *   process_callback = "_filter_htmlcorrector",
+ *   weight = 10
+ * )
+ */
+class FilterHtmlCorrector extends FilterBase {
+
+}
diff --git a/core/modules/filter/lib/Drupal/filter/Plugin/filter/filter/FilterHtmlEscape.php b/core/modules/filter/lib/Drupal/filter/Plugin/filter/filter/FilterHtmlEscape.php
new file mode 100644
index 0000000..951bba2
--- /dev/null
+++ b/core/modules/filter/lib/Drupal/filter/Plugin/filter/filter/FilterHtmlEscape.php
@@ -0,0 +1,36 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\filter\Plugin\filter\filter\FilterHtmlEscape.
+ */
+
+namespace Drupal\filter\Plugin\filter\filter;
+
+use Drupal\Core\Annotation\Plugin;
+use Drupal\Core\Annotation\Translation;
+use Drupal\filter\Plugin\filter\filter\FilterBase;
+use Drupal\filter\Plugin\Core\Entity\FilterFormat;
+
+/**
+ * @todo.
+ *
+ * @Plugin(
+ *   id = "filter_html_escape",
+ *   module = "filter",
+ *   title = @Translation("Display any HTML as plain text"),
+ *   type = FILTER_TYPE_HTML_RESTRICTOR,
+ *   process_callback = "_filter_html_escape",
+ *   weight = -10
+ * )
+ */
+class FilterHtmlEscape extends FilterBase {
+
+  /**
+   * Overrides \Drupal\filter\Plugin\filter\filter\Plugin\filter\filter\FilterBase::tips().
+   */
+  public function tips($filter, FilterFormat $format, $long = FALSE) {
+    return t('No HTML tags allowed.');
+  }
+
+}
diff --git a/core/modules/filter/lib/Drupal/filter/Plugin/filter/filter/FilterHtmlImageSecure.php b/core/modules/filter/lib/Drupal/filter/Plugin/filter/filter/FilterHtmlImageSecure.php
new file mode 100644
index 0000000..87aa157
--- /dev/null
+++ b/core/modules/filter/lib/Drupal/filter/Plugin/filter/filter/FilterHtmlImageSecure.php
@@ -0,0 +1,37 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\filter\Plugin\filter\filter\FilterHtmlImageSecure.
+ */
+
+namespace Drupal\filter\Plugin\filter\filter;
+
+use Drupal\Core\Annotation\Plugin;
+use Drupal\Core\Annotation\Translation;
+use Drupal\filter\Plugin\filter\filter\FilterBase;
+use Drupal\filter\Plugin\Core\Entity\FilterFormat;
+
+/**
+ * @todo.
+ *
+ * @Plugin(
+ *   id = "filter_html_image_secure",
+ *   module = "filter",
+ *   title = @Translation("Restrict images to this site"),
+ *   description = @Translation("Disallows usage of &lt;img&gt; tag sources that are not hosted on this site by replacing them with a placeholder image."),
+ *   type = FILTER_TYPE_HTML_RESTRICTOR,
+ *   process_callback = "_filter_html_image_secure_process",
+ *   weight = 9
+ * )
+ */
+class FilterHtmlImageSecure extends FilterBase {
+
+  /**
+   * Overrides \Drupal\filter\Plugin\filter\filter\Plugin\filter\filter\FilterBase::tips().
+   */
+  public function tips($filter, FilterFormat $format, $long = FALSE) {
+  return t('Only images hosted on this site may be used in &lt;img&gt; tags.');
+  }
+
+}
diff --git a/core/modules/filter/lib/Drupal/filter/Plugin/filter/filter/FilterInterface.php b/core/modules/filter/lib/Drupal/filter/Plugin/filter/filter/FilterInterface.php
new file mode 100644
index 0000000..250f91b
--- /dev/null
+++ b/core/modules/filter/lib/Drupal/filter/Plugin/filter/filter/FilterInterface.php
@@ -0,0 +1,37 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\filter\Plugin\filter\filter\Plugin\filter\filter\FilterBase.
+ */
+
+namespace Drupal\filter\Plugin\filter\filter;
+
+use Drupal\filter\Plugin\Core\Entity\FilterFormat;
+
+/**
+ * @todo.
+ */
+interface FilterInterface {
+
+  /**
+   * Generates a filter's tip.
+   *
+   * A filter's tips should be informative and to the point. Short tips are
+   * preferably one-liners.
+   *
+   * @param \stdClass $filter
+   *   An object representing the filter.
+   * @param \Drupal\filter\Plugin\Core\Entity\FilterFormat $format
+   *   An object representing the text format the filter is contained in.
+   * @param bool $long
+   *   Whether this callback should return a short tip to display in a form
+   *   (FALSE), or whether a more elaborate filter tips should be returned for
+   *   theme_filter_tips() (TRUE).
+   *
+   * @return string|null
+   *   Translated text to display as a tip, or NULL if this filter has no tip.
+   */
+  public function tips($filter, FilterFormat $format, $long = FALSE);
+
+}
diff --git a/core/modules/filter/lib/Drupal/filter/Plugin/filter/filter/FilterUrl.php b/core/modules/filter/lib/Drupal/filter/Plugin/filter/filter/FilterUrl.php
new file mode 100644
index 0000000..888300c
--- /dev/null
+++ b/core/modules/filter/lib/Drupal/filter/Plugin/filter/filter/FilterUrl.php
@@ -0,0 +1,39 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\filter\Plugin\filter\filter\FilterUrl.
+ */
+
+namespace Drupal\filter\Plugin\filter\filter;
+
+use Drupal\Core\Annotation\Plugin;
+use Drupal\Core\Annotation\Translation;
+use Drupal\filter\Plugin\filter\filter\FilterBase;
+use Drupal\filter\Plugin\Core\Entity\FilterFormat;
+
+/**
+ * @todo.
+ *
+ * @Plugin(
+ *   id = "filter_url",
+ *   module = "filter",
+ *   title = @Translation("Convert URLs into links"),
+ *   type = FILTER_TYPE_MARKUP_LANGUAGE,
+ *   process_callback = "_filter_url",
+ *   settings_callback = "_filter_url_settings",
+ *   default_settings = {
+ *     "filter_url_length" = 72
+ *   }
+ * )
+ */
+class FilterUrl extends FilterBase {
+
+  /**
+   * Overrides \Drupal\filter\Plugin\filter\filter\Plugin\filter\filter\FilterBase::tips().
+   */
+  public function tips($filter, FilterFormat $format, $long = FALSE) {
+    return t('Web page addresses and e-mail addresses turn into links automatically.');
+  }
+
+}
diff --git a/core/modules/filter/lib/Drupal/filter/Tests/FilterAPITest.php b/core/modules/filter/lib/Drupal/filter/Tests/FilterAPITest.php
index e355f2d..754cd1c 100644
--- a/core/modules/filter/lib/Drupal/filter/Tests/FilterAPITest.php
+++ b/core/modules/filter/lib/Drupal/filter/Tests/FilterAPITest.php
@@ -26,7 +26,7 @@ function setUp() {
     parent::setUp();
 
     // Create Filtered HTML format.
-    $filtered_html_format = array(
+    $filtered_html_format_config = array(
       'format' => 'filtered_html',
       'name' => 'Filtered HTML',
       'filters' => array(
@@ -41,11 +41,11 @@ function setUp() {
         ),
       )
     );
-    $filtered_html_format = (object) $filtered_html_format;
-    filter_format_save($filtered_html_format);
+    $filtered_html_format = entity_create('filter_format', $filtered_html_format_config);
+    $filtered_html_format->save();
 
     // Create Full HTML format.
-    $full_html_format = array(
+    $full_html_format_config = array(
       'format' => 'full_html',
       'name' => 'Full HTML',
       'weight' => 1,
@@ -56,8 +56,8 @@ function setUp() {
         ),
       ),
     );
-    $full_html_format = (object) $full_html_format;
-    filter_format_save($full_html_format);
+    $full_html_format = entity_create('filter_format', $full_html_format_config);
+    $full_html_format->save();
   }
 
   /**
diff --git a/core/modules/filter/lib/Drupal/filter/Tests/FilterAdminTest.php b/core/modules/filter/lib/Drupal/filter/Tests/FilterAdminTest.php
index 9a09a76..0b9d30f 100644
--- a/core/modules/filter/lib/Drupal/filter/Tests/FilterAdminTest.php
+++ b/core/modules/filter/lib/Drupal/filter/Tests/FilterAdminTest.php
@@ -152,14 +152,13 @@ function testFilterAdmin() {
     ));
     $this->assertTrue(!empty($elements), 'Reorder confirmed in admin interface.');
 
-    $result = db_query('SELECT * FROM {filter} WHERE format = :format ORDER BY weight ASC', array(':format' => $filtered));
-    $filters = array();
-    foreach ($result as $filter) {
-      if ($filter->name == $second_filter || $filter->name == $first_filter) {
-        $filters[] = $filter;
+    $filter_format = entity_load('filter_format', $filtered);
+    foreach ($filter_format->filters as $filter_name => $filter) {
+      if ($filter_name == $second_filter || $filter_name == $first_filter) {
+        $filters[] = $filter_name;
       }
     }
-    $this->assertTrue(($filters[0]->name == $second_filter && $filters[1]->name == $first_filter), 'Order confirmed in database.');
+    $this->assertTrue(($filters[0] == $second_filter && $filters[1] == $first_filter), t('Order confirmed in database.'));
 
     // Add format.
     $edit = array();
diff --git a/core/modules/filter/lib/Drupal/filter/Tests/FilterCrudTest.php b/core/modules/filter/lib/Drupal/filter/Tests/FilterCrudTest.php
index 766736f..2fa330b 100644
--- a/core/modules/filter/lib/Drupal/filter/Tests/FilterCrudTest.php
+++ b/core/modules/filter/lib/Drupal/filter/Tests/FilterCrudTest.php
@@ -35,15 +35,15 @@ public static function getInfo() {
    */
   function testTextFormatCrud() {
     // Add a text format with minimum data only.
-    $format = new stdClass();
+    $format = entity_create('filter_format', array());
     $format->format = 'empty_format';
     $format->name = 'Empty format';
-    filter_format_save($format);
+    $format->save();
     $this->verifyTextFormat($format);
     $this->verifyFilters($format);
 
     // Add another text format specifying all possible properties.
-    $format = new stdClass();
+    $format = entity_create('filter_format', array());
     $format->format = 'custom_format';
     $format->name = 'Custom format';
     $format->filters = array(
@@ -54,7 +54,7 @@ function testTextFormatCrud() {
         ),
       ),
     );
-    filter_format_save($format);
+    $format->save();
     $this->verifyTextFormat($format);
     $this->verifyFilters($format);
 
@@ -62,21 +62,19 @@ function testTextFormatCrud() {
     $format->name = 'Altered format';
     $format->filters['filter_url']['status'] = 0;
     $format->filters['filter_autop']['status'] = 1;
-    filter_format_save($format);
+    $format->save();
     $this->verifyTextFormat($format);
     $this->verifyFilters($format);
 
     // Add a uncacheable filter and save again.
     $format->filters['filter_test_uncacheable']['status'] = 1;
-    filter_format_save($format);
+    $format->save();
     $this->verifyTextFormat($format);
     $this->verifyFilters($format);
 
     // Disable the text format.
     filter_format_disable($format);
 
-    $db_format = db_query("SELECT * FROM {filter_format} WHERE format = :format", array(':format' => $format->format))->fetchObject();
-    $this->assertFalse($db_format->status, 'Database: Disabled text format is marked as disabled.');
     $formats = filter_formats();
     $this->assertTrue(!isset($formats[$format->format]), 'filter_formats: Disabled text format no longer exists.');
   }
@@ -86,16 +84,6 @@ function testTextFormatCrud() {
    */
   function verifyTextFormat($format) {
     $t_args = array('%format' => $format->name);
-    // Verify text format database record.
-    $db_format = db_select('filter_format', 'ff')
-      ->fields('ff')
-      ->condition('format', $format->format)
-      ->execute()
-      ->fetchObject();
-    $this->assertEqual($db_format->format, $format->format, format_string('Database: Proper format id for text format %format.', $t_args));
-    $this->assertEqual($db_format->name, $format->name, format_string('Database: Proper title for text format %format.', $t_args));
-    $this->assertEqual($db_format->cache, $format->cache, format_string('Database: Proper cache indicator for text format %format.', $t_args));
-    $this->assertEqual($db_format->weight, $format->weight, format_string('Database: Proper weight for text format %format.', $t_args));
 
     // Verify filter_format_load().
     $filter_format = filter_format_load($format->format);
@@ -123,27 +111,7 @@ function verifyTextFormat($format) {
    * Verify that filters are properly stored for a text format.
    */
   function verifyFilters($format) {
-    // Verify filter database records.
-    $filters = db_query("SELECT * FROM {filter} WHERE format = :format", array(':format' => $format->format))->fetchAllAssoc('name');
     $format_filters = $format->filters;
-    foreach ($filters as $name => $filter) {
-      $t_args = array('%format' => $format->name, '%filter' => $name);
-
-      // Verify that filter status is properly stored.
-      $this->assertEqual($filter->status, $format_filters[$name]['status'], format_string('Database: Proper status for %filter in text format %format.', $t_args));
-
-      // Verify that filter settings were properly stored.
-      $this->assertEqual(unserialize($filter->settings), isset($format_filters[$name]['settings']) ? $format_filters[$name]['settings'] : array(), format_string('Database: Proper filter settings for %filter in text format %format.', $t_args));
-
-      // Verify that each filter has a module name assigned.
-      $this->assertTrue(!empty($filter->module), format_string('Database: Proper module name for %filter in text format %format.', $t_args));
-
-      // Remove the filter from the copy of saved $format to check whether all
-      // filters have been processed later.
-      unset($format_filters[$name]);
-    }
-    // Verify that all filters have been processed.
-    $this->assertTrue(empty($format_filters), 'Database contains values for all filters in the saved format.');
 
     // Verify filter_list_format().
     $filters = filter_list_format($format->format);
diff --git a/core/modules/filter/lib/Drupal/filter/Tests/FilterDefaultFormatTest.php b/core/modules/filter/lib/Drupal/filter/Tests/FilterDefaultFormatTest.php
index 04fa520..02031e3 100644
--- a/core/modules/filter/lib/Drupal/filter/Tests/FilterDefaultFormatTest.php
+++ b/core/modules/filter/lib/Drupal/filter/Tests/FilterDefaultFormatTest.php
@@ -42,7 +42,17 @@ function testDefaultTextFormats() {
 
     // Adjust the weights so that the first and second formats (in that order)
     // are the two lowest weighted formats available to any user.
-    $minimum_weight = db_query("SELECT MIN(weight) FROM {filter_format}")->fetchField();
+    $minimum_weight = NULL;
+    foreach (entity_load_multiple('filter_format') as $format) {
+      if (is_null($minimum_weight)) {
+        $minimum_weight = $format->weight;
+      }
+      else {
+        if ($minimum_weight > $format->weight) {
+          $minimum_weight = $format->weight;
+        }
+      }
+    }
     $edit = array();
     $edit['formats[' . $first_format->format . '][weight]'] = $minimum_weight - 2;
     $edit['formats[' . $second_format->format . '][weight]'] = $minimum_weight - 1;
diff --git a/core/modules/filter/lib/Drupal/filter/Tests/FilterHtmlImageSecureTest.php b/core/modules/filter/lib/Drupal/filter/Tests/FilterHtmlImageSecureTest.php
index a7e38ac..4d664a8 100644
--- a/core/modules/filter/lib/Drupal/filter/Tests/FilterHtmlImageSecureTest.php
+++ b/core/modules/filter/lib/Drupal/filter/Tests/FilterHtmlImageSecureTest.php
@@ -33,7 +33,7 @@ function setUp() {
     parent::setUp();
 
     // Setup Filtered HTML text format.
-    $filtered_html_format = array(
+    $filtered_html_format_config = array(
       'format' => 'filtered_html',
       'name' => 'Filtered HTML',
       'filters' => array(
@@ -51,8 +51,8 @@ function setUp() {
         ),
       ),
     );
-    $filtered_html_format = (object) $filtered_html_format;
-    filter_format_save($filtered_html_format);
+    $filtered_html_format = entity_create('filter_format', $filtered_html_format_config);
+    $filtered_html_format->save();
 
     // Setup users.
     $this->checkPermissions(array(), TRUE);
diff --git a/core/modules/filter/lib/Drupal/filter/Tests/FilterSecurityTest.php b/core/modules/filter/lib/Drupal/filter/Tests/FilterSecurityTest.php
index 813d717..c99a1ce 100644
--- a/core/modules/filter/lib/Drupal/filter/Tests/FilterSecurityTest.php
+++ b/core/modules/filter/lib/Drupal/filter/Tests/FilterSecurityTest.php
@@ -36,7 +36,7 @@ function setUp() {
     $this->drupalCreateContentType(array('type' => 'page', 'name' => 'Basic page'));
 
     // Create Filtered HTML format.
-    $filtered_html_format = array(
+    $filtered_html_format_config = array(
       'format' => 'filtered_html',
       'name' => 'Filtered HTML',
       'filters' => array(
@@ -46,8 +46,8 @@ function setUp() {
         ),
       )
     );
-    $filtered_html_format = (object) $filtered_html_format;
-    filter_format_save($filtered_html_format);
+    $filtered_html_format = entity_create('filter_format', $filtered_html_format_config);
+    $filtered_html_format->save();
 
     $filtered_html_permission = filter_permission_name($filtered_html_format);
     user_role_grant_permissions(DRUPAL_ANONYMOUS_RID, array($filtered_html_permission));
diff --git a/core/modules/filter/lib/Drupal/filter/Tests/FilterSettingsTest.php b/core/modules/filter/lib/Drupal/filter/Tests/FilterSettingsTest.php
index 0a6c00c..1aa7909 100644
--- a/core/modules/filter/lib/Drupal/filter/Tests/FilterSettingsTest.php
+++ b/core/modules/filter/lib/Drupal/filter/Tests/FilterSettingsTest.php
@@ -31,12 +31,13 @@ function testFilterDefaults() {
     $filters = array_fill_keys(array_keys($filter_info), array());
 
     // Create text format using filter default settings.
-    $filter_defaults_format = (object) array(
+    $filter_defaults_format_config = array(
       'format' => 'filter_defaults',
       'name' => 'Filter defaults',
       'filters' => $filters,
     );
-    filter_format_save($filter_defaults_format);
+    $filter_defaults_format = entity_create('filter_format', $filter_defaults_format_config);
+    $filter_defaults_format->save();
 
     // Verify that default weights defined in hook_filter_info() were applied.
     $saved_settings = array();
@@ -51,7 +52,7 @@ function testFilterDefaults() {
     }
 
     // Re-save the text format.
-    filter_format_save($filter_defaults_format);
+    $filter_defaults_format->save();
     // Reload it from scratch.
     filter_formats_reset();
     $filter_defaults_format = filter_format_load($filter_defaults_format->format);
diff --git a/core/modules/php/lib/Drupal/php/Plugin/filter/filter/Php.php b/core/modules/php/lib/Drupal/php/Plugin/filter/filter/Php.php
new file mode 100644
index 0000000..fd97767
--- /dev/null
+++ b/core/modules/php/lib/Drupal/php/Plugin/filter/filter/Php.php
@@ -0,0 +1,71 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\php\Plugin\filter\filter\Php.
+ */
+
+namespace Drupal\php\Plugin\filter\filter;
+
+use Drupal\Core\Annotation\Plugin;
+use Drupal\Core\Annotation\Translation;
+use Drupal\filter\Plugin\filter\filter\FilterBase;
+use Drupal\filter\Plugin\Core\Entity\FilterFormat;
+
+/**
+ * Provides PHP code filter. Use with care.
+ *
+ * @Plugin(
+ *   id = "php_code",
+ *   module = "php",
+ *   title = @Translation("PHP evaluator"),
+ *   description = @Translation("Executes a piece of PHP code. The usage of this filter should be restricted to administrators only!"),
+ *   type = FILTER_TYPE_MARKUP_LANGUAGE,
+ *   process_callback = "php_eval",
+ *   cache = FALSE
+ * )
+ */
+class Php extends FilterBase {
+
+  /**
+   * Overrides \Drupal\filter\Plugin\filter\filter\Plugin\filter\filter\FilterBase::tips().
+   */
+  public function tips($filter, FilterFormat $format, $long = FALSE) {
+    global $base_url;
+    if ($long) {
+      $output = '<h4>' . t('Using custom PHP code') . '</h4>';
+      $output .= '<p>' . t('Custom PHP code may be embedded in some types of site content, including posts and blocks. While embedding PHP code inside a post or block is a powerful and flexible feature when used by a trusted user with PHP experience, it is a significant and dangerous security risk when used improperly. Even a small mistake when posting PHP code may accidentally compromise your site.') . '</p>';
+      $output .= '<p>' . t('If you are unfamiliar with PHP, SQL, or Drupal, avoid using custom PHP code within posts. Experimenting with PHP may corrupt your database, render your site inoperable, or significantly compromise security.') . '</p>';
+      $output .= '<p>' . t('Notes:') . '</p>';
+      $output .= '<ul><li>' . t('Remember to double-check each line for syntax and logic errors <strong>before</strong> saving.') . '</li>';
+      $output .= '<li>' . t('Statements must be correctly terminated with semicolons.') . '</li>';
+      $output .= '<li>' . t('Global variables used within your PHP code retain their values after your script executes.') . '</li>';
+      $output .= '<li>' . t('<code>register_globals</code> is <strong>turned off</strong>. If you need to use forms, understand and use the functions in <a href="@formapi">the Drupal Form API</a>.', array('@formapi' => url('http://api.drupal.org/api/group/form_api/8'))) . '</li>';
+      $output .= '<li>' . t('Use a <code>print</code> or <code>return</code> statement in your code to output content.') . '</li>';
+      $output .= '<li>' . t('Develop and test your PHP code using a separate test script and sample database before deploying on a production site.') . '</li>';
+      $output .= '<li>' . t('Consider including your custom PHP code within a site-specific module or <code>template.php</code> file rather than embedding it directly into a post or block.') . '</li>';
+      $output .= '<li>' . t('Be aware that the ability to embed PHP code within content is provided by the PHP Filter module. If this module is disabled or deleted, then blocks and posts with embedded PHP may display, rather than execute, the PHP code.') . '</li></ul>';
+      $output .= '<p>' . t('A basic example: <em>Creating a "Welcome" block that greets visitors with a simple message.</em>') . '</p>';
+      $output .= '<ul><li>' . t('<p>Add a custom block to your site, named "Welcome" . With its text format set to "PHP code" (or another format supporting PHP input), add the following in the Block body:</p>
+  <pre>
+  print t(\'Welcome visitor! Thank you for visiting.\');
+  </pre>') . '</li>';
+      $output .= '<li>' . t('<p>To display the name of a registered user, use this instead:</p>
+  <pre>
+  global $user;
+  if ($user->uid) {
+    print t(\'Welcome @name! Thank you for visiting.\', array(\'@name\' => user_format_name($user)));
+  }
+  else {
+    print t(\'Welcome visitor! Thank you for visiting.\');
+  }
+  </pre>') . '</li></ul>';
+      $output .= '<p>' . t('<a href="@drupal">Drupal.org</a> offers <a href="@php-snippets">some example PHP snippets</a>, or you can create your own with some PHP experience and knowledge of the Drupal system.', array('@drupal' => url('http://drupal.org'), '@php-snippets' => url('http://drupal.org/documentation/customization/php-snippets'))) . '</p>';
+      return $output;
+    }
+    else {
+      return t('You may post PHP code. You should include &lt;?php ?&gt; tags.');
+    }
+  }
+
+}
diff --git a/core/modules/php/php.install b/core/modules/php/php.install
index 12944dd..f2281f2 100644
--- a/core/modules/php/php.install
+++ b/core/modules/php/php.install
@@ -9,13 +9,20 @@
  * Implements hook_enable().
  */
 function php_enable() {
-  $format_exists = (bool) db_query_range('SELECT 1 FROM {filter_format} WHERE name = :name', 0, 1, array(':name' => 'PHP code'))->fetchField();
   // Add a PHP code text format, if it does not exist. Do this only for the
   // first install (or if the format has been manually deleted) as there is no
   // reliable method to identify the format in an uninstall hook or in
   // subsequent clean installs.
+  $format_exists = FALSE;
+  $filter_formats = entity_load_multiple('filter_format');
+  foreach ($filter_formats as $format) {
+    if ($format->name == 'PHP code') {
+      $format_exists = TRUE;
+      break;
+    }
+  }
   if (!$format_exists) {
-    $php_format = array(
+    $php_format_config = array(
       'format' => 'php_code',
       'name' => 'PHP code',
       // 'Plain text' format is installed with a weight of 10 by default. Use a
@@ -30,8 +37,8 @@ function php_enable() {
         ),
       ),
     );
-    $php_format = (object) $php_format;
-    filter_format_save($php_format);
+    $php_format = entity_create('filter_format', $php_format_config);
+    $php_format->save();
 
     drupal_set_message(t('A <a href="@php-code">PHP code</a> text format has been created.', array('@php-code' => url('admin/config/content/formats/' . $php_format->format))));
   }
diff --git a/core/modules/php/php.module b/core/modules/php/php.module
index 8e885e4..2932bfa 100644
--- a/core/modules/php/php.module
+++ b/core/modules/php/php.module
@@ -86,63 +86,3 @@ function php_eval($code) {
 
   return $output;
 }
-
-/**
- * Implements hook_filter_FILTER_tips().
- *
- * @see php_filter_info()
- */
-function _php_filter_tips($filter, $format, $long = FALSE) {
-  global $base_url;
-  if ($long) {
-    $output = '<h4>' . t('Using custom PHP code') . '</h4>';
-    $output .= '<p>' . t('Custom PHP code may be embedded in some types of site content, including posts and blocks. While embedding PHP code inside a post or block is a powerful and flexible feature when used by a trusted user with PHP experience, it is a significant and dangerous security risk when used improperly. Even a small mistake when posting PHP code may accidentally compromise your site.') . '</p>';
-    $output .= '<p>' . t('If you are unfamiliar with PHP, SQL, or Drupal, avoid using custom PHP code within posts. Experimenting with PHP may corrupt your database, render your site inoperable, or significantly compromise security.') . '</p>';
-    $output .= '<p>' . t('Notes:') . '</p>';
-    $output .= '<ul><li>' . t('Remember to double-check each line for syntax and logic errors <strong>before</strong> saving.') . '</li>';
-    $output .= '<li>' . t('Statements must be correctly terminated with semicolons.') . '</li>';
-    $output .= '<li>' . t('Global variables used within your PHP code retain their values after your script executes.') . '</li>';
-    $output .= '<li>' . t('<code>register_globals</code> is <strong>turned off</strong>. If you need to use forms, understand and use the functions in <a href="@formapi">the Drupal Form API</a>.', array('@formapi' => url('http://api.drupal.org/api/group/form_api/8'))) . '</li>';
-    $output .= '<li>' . t('Use a <code>print</code> or <code>return</code> statement in your code to output content.') . '</li>';
-    $output .= '<li>' . t('Develop and test your PHP code using a separate test script and sample database before deploying on a production site.') . '</li>';
-    $output .= '<li>' . t('Consider including your custom PHP code within a site-specific module or <code>template.php</code> file rather than embedding it directly into a post or block.') . '</li>';
-    $output .= '<li>' . t('Be aware that the ability to embed PHP code within content is provided by the PHP Filter module. If this module is disabled or deleted, then blocks and posts with embedded PHP may display, rather than execute, the PHP code.') . '</li></ul>';
-    $output .= '<p>' . t('A basic example: <em>Creating a "Welcome" block that greets visitors with a simple message.</em>') . '</p>';
-    $output .= '<ul><li>' . t('<p>Add a custom block to your site, named "Welcome" . With its text format set to "PHP code" (or another format supporting PHP input), add the following in the Block body:</p>
-<pre>
-print t(\'Welcome visitor! Thank you for visiting.\');
-</pre>') . '</li>';
-    $output .= '<li>' . t('<p>To display the name of a registered user, use this instead:</p>
-<pre>
-global $user;
-if ($user->uid) {
-  print t(\'Welcome @name! Thank you for visiting.\', array(\'@name\' => user_format_name($user)));
-}
-else {
-  print t(\'Welcome visitor! Thank you for visiting.\');
-}
-</pre>') . '</li></ul>';
-    $output .= '<p>' . t('<a href="@drupal">Drupal.org</a> offers <a href="@php-snippets">some example PHP snippets</a>, or you can create your own with some PHP experience and knowledge of the Drupal system.', array('@drupal' => url('http://drupal.org'), '@php-snippets' => url('http://drupal.org/documentation/customization/php-snippets'))) . '</p>';
-    return $output;
-  }
-  else {
-    return t('You may post PHP code. You should include &lt;?php ?&gt; tags.');
-  }
-}
-
-/**
- * Implements hook_filter_info().
- *
- * Provide PHP code filter. Use with care.
- */
-function php_filter_info() {
-  $filters['php_code'] = array(
-    'title' => t('PHP evaluator'),
-    'type' => FILTER_TYPE_MARKUP_LANGUAGE,
-    'description' => t('Executes a piece of PHP code. The usage of this filter should be restricted to administrators only!'),
-    'process callback' => 'php_eval',
-    'tips callback' => '_php_filter_tips',
-    'cache' => FALSE,
-  );
-  return $filters;
-}
diff --git a/core/modules/search/lib/Drupal/search/Tests/SearchRankingTest.php b/core/modules/search/lib/Drupal/search/Tests/SearchRankingTest.php
index a57e483..72c2431 100644
--- a/core/modules/search/lib/Drupal/search/Tests/SearchRankingTest.php
+++ b/core/modules/search/lib/Drupal/search/Tests/SearchRankingTest.php
@@ -106,12 +106,12 @@ function testRankings() {
    * Test rankings of HTML tags.
    */
   function testHTMLRankings() {
-    $full_html_format = array(
+    $full_html_format_config = array(
       'format' => 'full_html',
       'name' => 'Full HTML',
     );
-    $full_html_format = (object) $full_html_format;
-    filter_format_save($full_html_format);
+    $full_html_format = entity_create('filter_format', $full_html_format_config);
+    $full_html_format->save();
 
     // Login with sufficient privileges.
     $this->drupalLogin($this->drupalCreateUser(array('create page content')));
diff --git a/core/modules/simpletest/lib/Drupal/simpletest/Tests/DrupalUnitTestBaseTest.php b/core/modules/simpletest/lib/Drupal/simpletest/Tests/DrupalUnitTestBaseTest.php
index 5813ffd..c80495a 100644
--- a/core/modules/simpletest/lib/Drupal/simpletest/Tests/DrupalUnitTestBaseTest.php
+++ b/core/modules/simpletest/lib/Drupal/simpletest/Tests/DrupalUnitTestBaseTest.php
@@ -75,7 +75,6 @@ function testEnableModulesLoad() {
    */
   function testEnableModulesInstall() {
     $module = 'filter';
-    $table = 'filter';
 
     // @todo Remove after configuration system conversion.
     $this->enableModules(array('system'), FALSE);
@@ -88,10 +87,6 @@ function testEnableModulesInstall() {
     $list = module_list('permission');
     $this->assertFalse(in_array($module, $list), "{$module}_permission() in module_implements() not found.");
 
-    $this->assertFalse(db_table_exists($table), "'$table' database table not found.");
-    $schema = drupal_get_schema($table);
-    $this->assertFalse($schema, "'$table' table schema not found.");
-
     // Enable the module.
     $this->enableModules(array($module));
 
@@ -101,10 +96,6 @@ function testEnableModulesInstall() {
     $this->assertTrue(in_array($module, $list), "$module module in module_list() found.");
     $list = module_list('permission');
     $this->assertTrue(in_array($module, $list), "{$module}_permission() in module_implements() found.");
-
-    $this->assertTrue(db_table_exists($table), "'$table' database table found.");
-    $schema = drupal_get_schema($table);
-    $this->assertTrue($schema, "'$table' table schema found.");
   }
 
   /**
diff --git a/core/modules/system/lib/Drupal/system/Tests/Form/FormTest.php b/core/modules/system/lib/Drupal/system/Tests/Form/FormTest.php
index 9efe0c3..e1e0315 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Form/FormTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Form/FormTest.php
@@ -29,12 +29,12 @@ public static function getInfo() {
   function setUp() {
     parent::setUp();
 
-    $filtered_html_format = array(
+    $filtered_html_format_config = array(
       'format' => 'filtered_html',
       'name' => 'Filtered HTML',
     );
-    $filtered_html_format = (object) $filtered_html_format;
-    filter_format_save($filtered_html_format);
+    $filtered_html_format = entity_create('filter_format', $filtered_html_format_config);
+    $filtered_html_format->save();
 
     $filtered_html_permission = filter_permission_name($filtered_html_format);
     user_role_grant_permissions(DRUPAL_ANONYMOUS_RID, array($filtered_html_permission));
diff --git a/core/modules/system/lib/Drupal/system/Tests/Menu/BreadcrumbTest.php b/core/modules/system/lib/Drupal/system/Tests/Menu/BreadcrumbTest.php
index a77218b..143b6e9 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Menu/BreadcrumbTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Menu/BreadcrumbTest.php
@@ -152,7 +152,8 @@ function testBreadCrumbs() {
     $this->assertBreadcrumb("admin/structure/types/manage/$type/fields/body/widget-type", $trail);
 
     // Verify Filter text format administration breadcrumbs.
-    $format = db_query_range("SELECT format, name FROM {filter_format}", 1, 1)->fetch();
+    $filter_formats = filter_formats();
+    $format = reset($filter_formats);
     $format_id = $format->format;
     $trail = $config + array(
       'admin/config/content' => t('Content authoring'),
diff --git a/core/modules/system/lib/Drupal/system/Tests/Upgrade/FilterFormatUpgradePathTest.php b/core/modules/system/lib/Drupal/system/Tests/Upgrade/FilterFormatUpgradePathTest.php
new file mode 100644
index 0000000..1a42c7f
--- /dev/null
+++ b/core/modules/system/lib/Drupal/system/Tests/Upgrade/FilterFormatUpgradePathTest.php
@@ -0,0 +1,78 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\system\Tests\Upgrade\FilterFormatUpgradePathTest.
+ */
+
+namespace Drupal\system\Tests\Upgrade;
+
+/**
+ * Tests upgrading a bare database with user filter format data.
+ *
+ * Loads a bare installation of Drupal 7 with filter format data and runs the
+ * upgrade process on it. Tests for the conversion filter formats into
+ * configurables.
+ */
+class FilterFormatUpgradePathTest extends UpgradePathTestBase {
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Filter Formats upgrade test',
+      'description' => 'Upgrade tests with filter formats data.',
+      'group' => 'Upgrade path',
+    );
+  }
+
+  public function setUp() {
+    $path = drupal_get_path('module', 'system');
+    $this->databaseDumpFiles = array(
+      $path . '/tests/upgrade/drupal-7.bare.standard_all.database.php.gz',
+      $path . '/tests/upgrade/drupal-7.roles.database.php',
+      $path . '/tests/upgrade/drupal-7.filter_formats.database.php',
+    );
+    parent::setUp();
+  }
+
+  /**
+   * Tests expected filter formats entities after a successful upgrade.
+   */
+  public function testFilterFormatUpgrade() {
+    $this->assertTrue($this->performUpgrade(), 'The upgrade was completed successfully.');
+
+    // Checks that all the formats were upgraded
+    $one = filter_format_load('format_one');
+    $this->assertTrue(!empty($one), 'Filter Format one was successfully upgraded');
+    $two = filter_format_load('format_two');
+    $this->assertTrue(!empty($two), 'Filter Format two was successfully upgraded');
+
+    // Filter format 'Three' is disabled, and filter_format_load should return
+    // FALSE. However the entity should be accessible using entity_load.
+    $three_disabled = filter_format_load('format_three');
+    $three_entity = entity_load('filter_format', 'format_three');
+    $this->assertTrue(empty($three_disabled) && !empty($three_entity), 'Filter Format three was successfully upgraded and it is disabled');
+
+    // Check the access to the text formats.
+
+    // Check that the anonymous user role ID has been converted from "1" to
+    // "anonymous" and text formats permissions were updated.
+    $this->drupalGet('admin/people/permissions');
+    $this->assertFieldChecked('edit-anonymous-use-text-format-format-one', 'Use text format format_one permission for "anonymous" is set correctly.');
+    $this->assertNoFieldChecked('edit-anonymous-use-text-format-format-two', 'Use text format format_two permission for "anonymous" is set correctly.');
+
+    // Check that the anonymous user role ID has been converted from "2" to
+    // "authenticated" and text formats permissions were updated.
+    $this->assertNoFieldChecked('edit-authenticated-use-text-format-format-one', 'Use text format format_one permission for "authenticated" is set correctly.');
+    $this->assertFieldChecked('edit-authenticated-use-text-format-format-two', 'Use text format format_two permission for "authenticated" is set correctly.');
+
+    // Check that the permission for "gärtner" still exists and text formats
+    // permissions were updated.
+    $this->assertFieldChecked('edit-4-use-text-format-format-one', 'Use text format format_one permission for role is set correctly.');
+    $this->assertNoFieldChecked('edit-4-use-text-format-format-two', 'Use text format format_two permission for role is set correctly.');
+
+    // Check that role 5 cannot access to the defined text formats
+    $this->assertNoFieldChecked('edit-5-use-text-format-format-one', 'Use text format format_one permission for role is set correctly.');
+    $this->assertNoFieldChecked('edit-5-use-text-format-format-two', 'Use text format format_two permission for role is set correctly.');
+  }
+
+}
diff --git a/core/modules/system/tests/modules/filter_test/filter_test.module b/core/modules/system/tests/modules/filter_test/filter_test.module
index a61941a..71d3a23 100644
--- a/core/modules/system/tests/modules/filter_test/filter_test.module
+++ b/core/modules/system/tests/modules/filter_test/filter_test.module
@@ -27,25 +27,6 @@ function filter_test_filter_format_disable($format) {
 }
 
 /**
- * Implements hook_filter_info().
- */
-function filter_test_filter_info() {
-  $filters['filter_test_uncacheable'] = array(
-    'title' => 'Uncacheable filter',
-    'type' => FILTER_TYPE_TRANSFORM_IRREVERSIBLE,
-    'description' => 'Does nothing, but makes a text format uncacheable.',
-    'cache' => FALSE,
-  );
-  $filters['filter_test_replace'] = array(
-    'title' => 'Testing filter',
-    'type' => FILTER_TYPE_TRANSFORM_IRREVERSIBLE,
-    'description' => 'Replaces all content with filter and text format information.',
-    'process callback' => 'filter_test_replace',
-  );
-  return $filters;
-}
-
-/**
  * Process handler for filter_test_replace filter.
  *
  * Replaces all text with filter and text format information.
diff --git a/core/modules/system/tests/modules/filter_test/lib/Drupal/filter_test/Plugin/filter/filter/FilterTestReplace.php b/core/modules/system/tests/modules/filter_test/lib/Drupal/filter_test/Plugin/filter/filter/FilterTestReplace.php
new file mode 100644
index 0000000..179a7d4
--- /dev/null
+++ b/core/modules/system/tests/modules/filter_test/lib/Drupal/filter_test/Plugin/filter/filter/FilterTestReplace.php
@@ -0,0 +1,28 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\filter_test\Plugin\filter\filter\FilterTestReplace.
+ */
+
+namespace Drupal\filter_test\Plugin\filter\filter;
+
+use Drupal\Core\Annotation\Plugin;
+use Drupal\Core\Annotation\Translation;
+use Drupal\filter\Plugin\filter\filter\FilterBase;
+
+/**
+ * @todo.
+ *
+ * @Plugin(
+ *   id = "filter_test_replace",
+ *   module = "filter_test",
+ *   title = @Translation("Testing filter"),
+ *   description = @Translation("Replaces all content with filter and text format information."),
+ *   type = FILTER_TYPE_TRANSFORM_IRREVERSIBLE,
+ *   process_callback = "filter_test_replace"
+ * )
+ */
+class FilterTestReplace extends FilterBase {
+
+}
diff --git a/core/modules/system/tests/modules/filter_test/lib/Drupal/filter_test/Plugin/filter/filter/FilterTestUncacheable.php b/core/modules/system/tests/modules/filter_test/lib/Drupal/filter_test/Plugin/filter/filter/FilterTestUncacheable.php
new file mode 100644
index 0000000..8ce166b
--- /dev/null
+++ b/core/modules/system/tests/modules/filter_test/lib/Drupal/filter_test/Plugin/filter/filter/FilterTestUncacheable.php
@@ -0,0 +1,28 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\filter_test\Plugin\filter\filter\FilterTestUncacheable.
+ */
+
+namespace Drupal\filter_test\Plugin\filter\filter;
+
+use Drupal\Core\Annotation\Plugin;
+use Drupal\Core\Annotation\Translation;
+use Drupal\filter\Plugin\filter\filter\FilterBase;
+
+/**
+ * @todo.
+ *
+ * @Plugin(
+ *   id = "filter_test_uncacheable",
+ *   module = "filter_test",
+ *   title = @Translation("Uncacheable filter"),
+ *   description = @Translation("Does nothing, but makes a text format uncacheable"),
+ *   type = FILTER_TYPE_TRANSFORM_IRREVERSIBLE,
+ *   cache = FALSE
+ * )
+ */
+class FilterTestUncacheable extends FilterBase {
+
+}
diff --git a/core/modules/system/tests/upgrade/drupal-7.filter_formats.database.php b/core/modules/system/tests/upgrade/drupal-7.filter_formats.database.php
new file mode 100644
index 0000000..402670c
--- /dev/null
+++ b/core/modules/system/tests/upgrade/drupal-7.filter_formats.database.php
@@ -0,0 +1,174 @@
+<?php
+
+/**
+ * @file
+ * Database additions filter format tests. Used in upgrade.filter_formats.test.
+ *
+ * This dump only contains data and schema components relevant for role
+ * functionality. The drupal-7.bare.database.php file is imported before
+ * this dump, so the two form the database structure expected in tests
+ * altogether.
+ */
+
+db_insert('filter_format')->fields(array(
+  'format',
+  'name',
+  'cache',
+  'status',
+  'weight',
+))
+// Adds some filters formats
+->values(array(
+  'format' => 'format_one',
+  'name' => 'Format One',
+  'cache' => '1',
+  'weight' => '1',
+  'status' => '1'
+))
+->values(array(
+  'format' => 'format_two',
+  'name' => 'Format Two',
+  'cache' => '1',
+  'weight' => '2',
+  'status' => '1'
+))
+// Add a disabled filter format
+->values(array(
+  'format' => 'format_three',
+  'name' => 'Format Three',
+  'cache' => '1',
+  'weight' => '3',
+  'status' => '0'
+))
+->execute();
+
+// Adds filters to the crated filter formats
+db_insert('filter')->fields(array(
+  'format',
+  'module',
+  'name',
+  'weight',
+  'status',
+  'settings',
+))
+// Filters for: Format One
+->values(array(
+  'format' => 'format_one',
+  'module' => 'filter',
+  'name' => 'filter_autop',
+  'weight' => '2',
+  'status' => '1',
+  'settings' => 'a:0:{}',
+))
+->values(array(
+  'format' => 'format_one',
+  'module' => 'filter',
+  'name' => 'filter_html',
+  'weight' => '-10',
+  'status' => '0',
+  'settings' => 'a:3:{s:12:"allowed_html";s:74:"<a> <em> <strong> <cite> <blockquote> <code> <ul> <ol> <li> <dl> <dt> <dd>";s:16:"filter_html_help";i:1;s:20:"filter_html_nofollow";i:0;}',
+))
+->values(array(
+  'format' => 'format_one',
+  'module' => 'filter',
+  'name' => 'filter_htmlcorrector',
+  'weight' => '10',
+  'status' => '0',
+  'settings' => 'a:0:{}',
+))
+->values(array(
+  'format' => 'format_one',
+  'module' => 'filter',
+  'name' => 'filter_html_escape',
+  'weight' => '0',
+  'status' => '1',
+  'settings' => 'a:0:{}',
+))
+->values(array(
+  'format' => 'format_two',
+  'module' => 'filter',
+  'name' => 'filter_url',
+  'weight' => '1',
+  'status' => '1',
+  'settings' => 'a:1:{s:17:"filter_url_length";i:72;}',
+))
+->values(array(
+  'format' => 'format_two',
+  'module' => 'filter',
+  'name' => 'filter_autop',
+  'weight' => '0',
+  'status' => '0',
+  'settings' => 'a:0:{}',
+))
+->values(array(
+  'format' => 'format_three',
+  'module' => 'filter',
+  'name' => 'filter_html',
+  'weight' => '-10',
+  'status' => '1',
+  'settings' => 'a:3:{s:12:"allowed_html";s:9:"<a> <em> ";s:16:"filter_html_help";i:1;s:20:"filter_html_nofollow";i:0;}',
+))
+->values(array(
+  'format' => 'format_three',
+  'module' => 'filter',
+  'name' => 'filter_htmlcorrector',
+  'weight' => '10',
+  'status' => '0',
+  'settings' => 'a:0:{}',
+))
+->values(array(
+  'format' => 'format_three',
+  'module' => 'filter',
+  'name' => 'filter_html_escape',
+  'weight' => '-10',
+  'status' => '1',
+  'settings' => 'a:0:{}',
+))
+->values(array(
+  'format' => 'format_three',
+  'module' => 'filter',
+  'name' => 'filter_url',
+  'weight' => '0',
+  'status' => '1',
+  'settings' => 'a:1:{s:17:"filter_url_length";s:2:"72";}',
+))
+->execute();
+
+// Define which roles can use the text formats.
+db_insert('role_permission')->fields(array(
+  'rid',
+  'permission',
+  'module',
+))
+// Adds some filters formats
+->values(array(
+  'rid' => 1,
+  'permission' => 'use text format format_one',
+  'module' => 'filter',
+))
+->values(array(
+  'rid' => 4,
+  'permission' => 'use text format format_one',
+  'module' => 'filter',
+))
+->values(array(
+  'rid' => 2,
+  'permission' => 'use text format format_two',
+  'module' => 'filter',
+))
+->values(array(
+  'rid' => 4,
+  'permission' => 'use text format format_three',
+  'module' => 'filter',
+))
+->execute();
+
+db_insert('variable')->fields(array(
+  'name',
+  'value',
+))
+->values(array(
+  'name' => 'format_fallback_format',
+  'value' => 's:10:"plain_text";',
+))
+->execute();
diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TaxonomyTestBase.php b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TaxonomyTestBase.php
index 6f4dcb2..aebbb61 100644
--- a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TaxonomyTestBase.php
+++ b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TaxonomyTestBase.php
@@ -52,11 +52,13 @@ function createVocabulary() {
    * Returns a new term with random properties in vocabulary $vid.
    */
   function createTerm($vocabulary) {
+    $filter_formats = filter_formats();
+    $format = array_pop($filter_formats);
     $term = entity_create('taxonomy_term', array(
       'name' => $this->randomName(),
       'description' => $this->randomName(),
       // Use the first available text format.
-      'format' => db_query_range('SELECT format FROM {filter_format}', 0, 1)->fetchField(),
+      'format' => $format->format,
       'vid' => $vocabulary->vid,
       'langcode' => LANGUAGE_NOT_SPECIFIED,
     ));
diff --git a/core/modules/taxonomy/taxonomy.install b/core/modules/taxonomy/taxonomy.install
index f959d65..a438112 100644
--- a/core/modules/taxonomy/taxonomy.install
+++ b/core/modules/taxonomy/taxonomy.install
@@ -130,7 +130,7 @@ function taxonomy_schema() {
         'type' => 'varchar',
         'length' => 255,
         'not null' => FALSE,
-        'description' => 'The {filter_format}.format of the description.',
+        'description' => 'The Filter Format id of the description.',
       ),
       'weight' => array(
         'type' => 'int',
diff --git a/core/modules/user/lib/Drupal/user/Tests/UserSignatureTest.php b/core/modules/user/lib/Drupal/user/Tests/UserSignatureTest.php
index 9ed88ca..3712eba 100644
--- a/core/modules/user/lib/Drupal/user/Tests/UserSignatureTest.php
+++ b/core/modules/user/lib/Drupal/user/Tests/UserSignatureTest.php
@@ -41,19 +41,19 @@ function setUp() {
     // Prefetch and create text formats.
     $this->plain_text_format = filter_format_load('plain_text');
 
-    $filtered_html_format = array(
+    $filtered_html_format_config = array(
       'format' => 'filtered_html',
       'name' => 'Filtered HTML',
     );
-    $this->filtered_html_format = (object) $filtered_html_format;
-    filter_format_save($this->filtered_html_format);
+    $this->filtered_html_format = entity_create('filter_format', $filtered_html_format_config);
+    $this->filtered_html_format->save();
 
-    $full_html_format = array(
+    $full_html_format_config = array(
       'format' => 'full_html',
       'name' => 'Full HTML',
     );
-    $this->full_html_format = (object) $full_html_format;
-    filter_format_save($this->full_html_format);
+    $this->full_html_format = entity_create('filter_format', $full_html_format_config);
+    $this->full_html_format->save();
 
     user_role_grant_permissions(DRUPAL_AUTHENTICATED_RID, array(filter_permission_name($this->filtered_html_format)));
     $this->checkPermissions(array(), TRUE);
diff --git a/core/modules/user/user.install b/core/modules/user/user.install
index 705a725..85ff87e 100644
--- a/core/modules/user/user.install
+++ b/core/modules/user/user.install
@@ -75,7 +75,7 @@ function user_schema() {
         'type' => 'varchar',
         'length' => 255,
         'not null' => FALSE,
-        'description' => 'The {filter_format}.format of the signature.',
+        'description' => 'The Filter Format id of the signature.',
       ),
       'created' => array(
         'type' => 'int',
diff --git a/core/modules/views/lib/Drupal/views/Tests/DefaultViewsTest.php b/core/modules/views/lib/Drupal/views/Tests/DefaultViewsTest.php
index 7e37452..4129332 100644
--- a/core/modules/views/lib/Drupal/views/Tests/DefaultViewsTest.php
+++ b/core/modules/views/lib/Drupal/views/Tests/DefaultViewsTest.php
@@ -149,11 +149,13 @@ public function testDefaultViews() {
    * Returns a new term with random properties in vocabulary $vid.
    */
   function createTerm($vocabulary) {
+    $filter_formats = filter_formats();
+    $format = array_pop($filter_formats);
     $term = entity_create('taxonomy_term', array(
       'name' => $this->randomName(),
       'description' => $this->randomName(),
       // Use the first available text format.
-      'format' => db_query_range('SELECT format FROM {filter_format}', 0, 1)->fetchField(),
+      'format' => $format->format,
       'vid' => $vocabulary->vid,
       'langcode' => LANGUAGE_NOT_SPECIFIED,
     ));
diff --git a/core/profiles/standard/config/filter.format.filtered_html.yml b/core/profiles/standard/config/filter.format.filtered_html.yml
new file mode 100644
index 0000000..963132f
--- /dev/null
+++ b/core/profiles/standard/config/filter.format.filtered_html.yml
@@ -0,0 +1,45 @@
+format: filtered_html
+name: 'Filtered HTML'
+cache: '1'
+status: '1'
+weight: '0'
+roles:
+  anonymous: anonymous
+  authenticated: authenticated
+  administrator: administrator
+filters:
+  filter_url:
+    module: filter
+    settings:
+      filter_url_length: '72'
+    status: '1'
+    weight: '0'
+  filter_html:
+    module: filter
+    settings:
+      allowed_html: '<a> <em> <strong> <cite> <blockquote> <code> <ul> <ol> <li> <dl> <dt> <dd>'
+      filter_html_help: '1'
+      filter_html_nofollow: '0'
+    status: '1'
+    weight: '1'
+  filter_autop:
+    module: filter
+    settings: {  }
+    status: '1'
+    weight: '2'
+  filter_htmlcorrector:
+    module: filter
+    settings: {  }
+    status: '1'
+    weight: '10'
+  filter_html_escape:
+    module: filter
+    settings: {  }
+    status: '0'
+    weight: '-10'
+  filter_html_image_secure:
+    module: filter
+    settings: {  }
+    status: '0'
+    weight: '9'
+langcode: und
diff --git a/core/profiles/standard/config/filter.format.full_html.yml b/core/profiles/standard/config/filter.format.full_html.yml
new file mode 100644
index 0000000..463a6d3
--- /dev/null
+++ b/core/profiles/standard/config/filter.format.full_html.yml
@@ -0,0 +1,45 @@
+format: full_html
+name: 'Full HTML'
+cache: '1'
+status: '1'
+weight: '1'
+roles:
+  administrator: administrator
+  anonymous: '0'
+  authenticated: '0'
+filters:
+  filter_url:
+    module: filter
+    settings:
+      filter_url_length: '72'
+    status: '1'
+    weight: '0'
+  filter_autop:
+    module: filter
+    settings: {  }
+    status: '1'
+    weight: '1'
+  filter_htmlcorrector:
+    module: filter
+    settings: {  }
+    status: '1'
+    weight: '10'
+  filter_html:
+    module: filter
+    settings:
+      allowed_html: '<a> <em> <strong> <cite> <blockquote> <code> <ul> <ol> <li> <dl> <dt> <dd>'
+      filter_html_help: '1'
+      filter_html_nofollow: '0'
+    status: '0'
+    weight: '-10'
+  filter_html_escape:
+    module: filter
+    settings: {  }
+    status: '0'
+    weight: '-10'
+  filter_html_image_secure:
+    module: filter
+    settings: {  }
+    status: '0'
+    weight: '9'
+langcode: und
diff --git a/core/profiles/standard/standard.install b/core/profiles/standard/standard.install
index 725874c..5657ef6 100644
--- a/core/profiles/standard/standard.install
+++ b/core/profiles/standard/standard.install
@@ -12,62 +12,6 @@
  * @see system_install()
  */
 function standard_install() {
-  // Add text formats.
-  $filtered_html_format = array(
-    'format' => 'filtered_html',
-    'name' => 'Filtered HTML',
-    'weight' => 0,
-    'filters' => array(
-      // URL filter.
-      'filter_url' => array(
-        'weight' => 0,
-        'status' => 1,
-      ),
-      // HTML filter.
-      'filter_html' => array(
-        'weight' => 1,
-        'status' => 1,
-      ),
-      // Line break filter.
-      'filter_autop' => array(
-        'weight' => 2,
-        'status' => 1,
-      ),
-      // HTML corrector filter.
-      'filter_htmlcorrector' => array(
-        'weight' => 10,
-        'status' => 1,
-      ),
-    ),
-  );
-  $filtered_html_format = (object) $filtered_html_format;
-  filter_format_save($filtered_html_format);
-
-  $full_html_format = array(
-    'format' => 'full_html',
-    'name' => 'Full HTML',
-    'weight' => 1,
-    'filters' => array(
-      // URL filter.
-      'filter_url' => array(
-        'weight' => 0,
-        'status' => 1,
-      ),
-      // Line break filter.
-      'filter_autop' => array(
-        'weight' => 1,
-        'status' => 1,
-      ),
-      // HTML corrector filter.
-      'filter_htmlcorrector' => array(
-        'weight' => 10,
-        'status' => 1,
-      ),
-    ),
-  );
-  $full_html_format = (object) $full_html_format;
-  filter_format_save($full_html_format);
-
   // Enable Bartik theme and set it as default theme instead of Stark.
   // @see system_install()
   $default_theme = 'bartik';
@@ -382,6 +326,7 @@ function standard_install() {
   user_install_picture_field();
 
   // Enable default permissions for system roles.
+  $filtered_html_format = filter_format_load('filtered_html');
   $filtered_html_permission = filter_permission_name($filtered_html_format);
   user_role_grant_permissions(DRUPAL_ANONYMOUS_RID, array('access content', 'access comments', $filtered_html_permission));
   user_role_grant_permissions(DRUPAL_AUTHENTICATED_RID, array('access content', 'access comments', 'post comments', 'skip comment approval', $filtered_html_permission));
