diff --git a/image_replace.info.yml b/image_replace.info.yml
index 207819a..fea45b8 100644
--- a/image_replace.info.yml
+++ b/image_replace.info.yml
@@ -1,6 +1,6 @@
 name: Image Replace Effect
 type: module
 description: 'Provides an image style effect capable of replacing the whole image with another one.'
-core: 8.x
+core_version_requirement: ^8 || ^9
 dependencies:
   - image
diff --git a/image_replace.install b/image_replace.install
index 8754a4e..6cd3462 100644
--- a/image_replace.install
+++ b/image_replace.install
@@ -11,34 +11,33 @@ use Drupal\Core\Database\Database;
  * Implements hook_schema().
  */
 function image_replace_schema() {
-  $schema['image_replace'] = array(
+  $schema['image_replace'] = [
     'description' => 'Stores a map of image replacements for certain styles.',
-    'fields' => array(
-      'target_style' => array(
+    'fields' => [
+      'target_style' => [
         'description' => 'The style machine name.',
         'type' => 'varchar_ascii',
         'length' => 255,
         'not null' => TRUE,
-      ),
-      'target_uri' => array(
-        'description' => 'The original URI to access the file.',
+      ],
+      'target_uri_hash' => [
+        'description' => 'Hash of target uris. Used as part of primary key',
         'type' => 'varchar',
-        'length' => 255,
+        'length' => '64',
         'not null' => TRUE,
         'default' => '',
-        'binary' => TRUE,
-      ),
-      'replacement_uri' => array(
+      ],
+      'replacement_uri' => [
         'description' => 'The replacement URI to use instead.',
         'type' => 'varchar',
         'length' => 255,
         'not null' => TRUE,
         'default' => '',
         'binary' => TRUE,
-      ),
-    ),
-    'primary key' => array('target_style', array('target_uri', 191)),
-  );
+      ],
+    ],
+    'primary key' => ['target_style', 'target_uri_hash'],
+  ];
 
   return $schema;
 }
@@ -49,22 +48,62 @@ function image_replace_schema() {
 function image_replace_update_8101(&$sandbox) {
   $schema = Database::getConnection()->schema();
   $schema->dropPrimaryKey('image_replace');
-  $schema->changeField('image_replace', 'target_uri', 'target_uri', array(
+  $schema->changeField('image_replace', 'target_uri', 'target_uri', [
     'description' => 'The original URI to access the file.',
     'type' => 'varchar',
     'length' => 255,
     'not null' => TRUE,
     'default' => '',
     'binary' => TRUE,
-  ));
-  $schema->changeField('image_replace', 'replacement_uri', 'replacement_uri', array(
+  ]);
+  $schema->changeField('image_replace', 'replacement_uri', 'replacement_uri', [
     'description' => 'The replacement URI to use instead.',
     'type' => 'varchar',
     'length' => 255,
     'not null' => TRUE,
     'default' => '',
     'binary' => TRUE,
-  ));
+  ]);
   $schema->addPrimaryKey('image_replace',
-    array('target_style', array('target_uri', 191)));
+    ['target_style', ['target_uri', 191]]);
+}
+
+/**
+ * Switch to hash based key system
+ */
+function image_replace_update_8102(&$sandbox) {
+  $database = Database::getConnection();
+  $image_replace_storage = Drupal::service('image_replace.storage');
+
+  $image_replace_dataset = $database->select('image_replace')
+    ->fields('image_replace', [
+      'target_style',
+      'target_uri',
+      'replacement_uri',
+    ])
+    ->execute()
+    ->fetchAll();
+
+  $schema = $database->schema();
+  $schema->dropPrimaryKey('image_replace');
+  $schema->addField('image_replace', 'target_uri_hash', [
+    'type' => 'varchar',
+    'length' => '64',
+    'not null' => TRUE,
+    'default' => '',
+    'description' => 'Hash of target uris. Used as part of primary key',
+  ]);
+
+  //Convert existing entries over to hash
+  foreach ($image_replace_dataset as $key => $data) {
+    $database->delete('image_replace')
+      ->condition('target_style', $data->target_style)
+      ->condition('target_uri', $data->target_uri)
+      ->execute();
+
+    $image_replace_storage->add($data->target_style, $data->target_uri, $data->replacement_uri);
+  }
+
+  $schema->dropField('image_replace', 'target_uri');
+  $schema->addPrimaryKey('image_replace', ['target_style', 'target_uri_hash']);
 }
diff --git a/image_replace.module b/image_replace.module
index ff7fbde..bb523c0 100644
--- a/image_replace.module
+++ b/image_replace.module
@@ -5,69 +5,11 @@
  * Provides an image style effect replacing the whole image with another one.
  */
 
-use Drupal\Component\Utility\SafeMarkup;
 use Drupal\Core\Entity\ContentEntityInterface;
 use Drupal\Core\Entity\EntityInterface;
-use Drupal\field\Entity\FieldConfig;
 use Drupal\field\Entity\FieldStorageConfig;
 use Drupal\image\ImageStyleInterface;
 
-/**
- * Determine replacement image uri for the given original filename.
- *
- * @param string $target_style
- *   The target image style name.
- * @param string $target_uri
- *   The uri of the image for which to find a replacement.
- *
- * @return string|NULL
- *   The replacement uri when a mapping for the given uri/style combination
- *   exists.
- */
-function image_replace_get($target_style, $target_uri) {
-  return db_select('image_replace')
-    ->fields('image_replace', array('replacement_uri'))
-    ->condition('target_style', $target_style)
-    ->condition('target_uri', $target_uri)
-    ->execute()
-    ->fetchField();
-}
-
-/**
- * Add an image replacement mapping.
- *
- * @param string $target_style
- *   The target image style name.
- * @param string $target_uri
- *   The uri of the image for which to set a replacement.
- * @param string $replacement_uri
- *   The replacement uri to set for the given uri/style combination.
- */
-function image_replace_add($target_style, $target_uri, $replacement_uri) {
-  return db_insert('image_replace')
-    ->fields(array(
-      'target_style' => $target_style,
-      'target_uri' => $target_uri,
-      'replacement_uri' => $replacement_uri,
-    ))
-    ->execute();
-}
-
-/**
- * Remove the given image replacement mapping if it exists.
- *
- * @param string $target_style
- *   The target image style name.
- * @param string $target_uri
- *   The uri of the image for which to remove the replacement.
- */
-function image_replace_remove($target_style, $target_uri) {
-  return db_delete('image_replace')
-    ->condition('target_style', $target_style)
-    ->condition('target_uri', $target_uri)
-    ->execute();
-}
-
 /**
  * Implements hook_form_FORM_ID_alter() for 'field_config_edit_form'.
  *
@@ -76,7 +18,7 @@ function image_replace_remove($target_style, $target_uri) {
 function image_replace_form_field_config_edit_form_alter(&$form, &$form_state, $form_id) {
   $field = $form_state->getFormObject()->getEntity();
   if ($field->getType() == 'image') {
-    $form['third_party_settings']['image_replace'] = array(
+    $form['third_party_settings']['image_replace'] = [
       '#type' => 'fieldset',
       '#collapsible' => TRUE,
       '#collapsed' => TRUE,
@@ -84,25 +26,25 @@ function image_replace_form_field_config_edit_form_alter(&$form, &$form_state, $
       '#description' => t('Use another image when rendered with certain image styles.'),
       '#weight' => 99,
       '#tree' => TRUE,
-      '#element_validate' => array('image_replace_form_field_config_edit_form_element_validate'),
-    );
+      '#element_validate' => ['image_replace_form_field_config_edit_form_element_validate'],
+    ];
 
-    $image_field_options = array_map(function($replacement_field) {
+    $image_field_options = array_map(function ($replacement_field) {
       return $replacement_field->label();
-    }, _image_replace_image_fields($field->getTargetEntityTypeId(), $field->getTargetBundle()));
+    }, \Drupal::service('image_replace.helper')->getImageFields($field->getTargetEntityTypeId(), $field->getTargetBundle()));
     unset($image_field_options[$field->getName()]);
 
-    $image_style_map = $field->getThirdPartySetting('image_replace', 'image_style_map', array());
-    foreach (_image_replace_style_options() as $image_style => $label) {
+    $image_style_map = $field->getThirdPartySetting('image_replace', 'image_style_map', []);
+    foreach (\Drupal::service('image_replace.helper')->getStyleOptions() as $image_style => $label) {
       $default_value = isset($image_style_map[$image_style]['source_field']) ? $image_style_map[$image_style]['source_field'] : NULL;
-      $form['third_party_settings']['image_replace']['image_style_map'][$image_style]['source_field'] = array(
+      $form['third_party_settings']['image_replace']['image_style_map'][$image_style]['source_field'] = [
         '#type' => 'select',
         '#title' => $label,
-        '#description' => t('The image field to use as a source when rendered with the %style image style.', array('%style' => $label)),
+        '#description' => t('The image field to use as a source when rendered with the %style image style.', ['%style' => $label]),
         '#options' => $image_field_options,
         '#default_value' => $default_value,
         '#empty_value' => FALSE,
-      );
+      ];
 
       if ($default_value) {
         $form['third_party_settings']['image_replace']['#collapsed'] = FALSE;
@@ -123,18 +65,18 @@ function image_replace_form_field_config_edit_form_element_validate($element, &$
   if ($field_storage->hasData()) {
     $changed = FALSE;
 
-    foreach (array_keys(_image_replace_style_options()) as $image_style) {
+    foreach (array_keys(\Drupal::service('image_replace.helper')->getStyleOptions()) as $image_style) {
       $current_value = $element['image_style_map'][$image_style]['source_field']['#default_value'];
       $new_value = $element['image_style_map'][$image_style]['source_field']['#value'];
-      if(!(empty($current_value) && empty($new_value)) && $current_value != $new_value) {
+      if (!(empty($current_value) && empty($new_value)) && $current_value != $new_value) {
         $changed = TRUE;
         break;
       }
     }
 
     if ($changed) {
-      drupal_set_message(t('The image replacement settings have been modified. As a result, it is necessary to rebuild the image replacement mapping for existing content. Note: The replacement mapping is updated automatically when saving an entity. Content can be resaved in bulk using the <em>save content</em> of the <a href="@content_url">administrative interface</a>', array('@content_url' => '/admin/content')), 'warning');
-      drupal_set_message(t('Also note that images already might be cached in the browser or by any intermediate HTTP cache. On live sites the only way to force browsers to redownload a cached image is to reupload the image with a different name.'), 'warning');
+      \Drupal::messenger()->addWarning(t('The image replacement settings have been modified. As a result, it is necessary to rebuild the image replacement mapping for existing content. Note: The replacement mapping is updated automatically when saving an entity. Content can be resaved in bulk using the <em>save content</em> of the <a href="@content_url">administrative interface</a>', ['@content_url' => '/admin/content']));
+      \Drupal::messenger()->addWarning(t('Also note that images already might be cached in the browser or by any intermediate HTTP cache. On live sites the only way to force browsers to redownload a cached image is to reupload the image with a different name.'));
     }
   }
 }
@@ -151,11 +93,11 @@ function image_replace_entity_presave(EntityInterface $entity) {
 
   // Collect involved image fields and target mapping from image instance
   // settings.
-  $involved_fields = array();
-  $target_map = array();
+  $involved_fields = [];
+  $target_map = [];
 
-  foreach (_image_replace_image_fields($entity->getEntityTypeId(), $entity->bundle()) as $target_field => $field) {
-    if ($field && $source_map = $field->getThirdPartySetting('image_replace', 'image_style_map', array())) {
+  foreach (\Drupal::service('image_replace.helper')->getImageFields($entity->getEntityTypeId(), $entity->bundle()) as $target_field => $field) {
+    if ($field && $source_map = $field->getThirdPartySetting('image_replace', 'image_style_map', [])) {
       $involved_fields[$target_field] = $target_field;
       foreach ($source_map as $target_style => $record) {
         $source_field = $record['source_field'];
@@ -166,9 +108,9 @@ function image_replace_entity_presave(EntityInterface $entity) {
   }
 
   // Extract all uris from all involved image fields.
-  $uri_map = array();
+  $uri_map = [];
   foreach ($involved_fields as $field_name) {
-    $uri_map[$field_name] = array();
+    $uri_map[$field_name] = [];
     if (!empty($entity->{$field_name})) {
       foreach ($entity->{$field_name} as $image) {
         $uri_map[$field_name][] = $image->entity->getFileUri();
@@ -180,9 +122,9 @@ function image_replace_entity_presave(EntityInterface $entity) {
   foreach ($target_map as $target_field => $source_map) {
     foreach ($source_map as $target_style => $source_field) {
       foreach ($uri_map[$target_field] as $delta => $target_uri) {
-        image_replace_remove($target_style, $target_uri);
+        \Drupal::service('image_replace.storage')->remove($target_style, $target_uri);
         if (isset($uri_map[$source_field][$delta])) {
-          image_replace_add($target_style, $target_uri, $uri_map[$source_field][$delta]);
+          \Drupal::service('image_replace.storage')->add($target_style, $target_uri, $uri_map[$source_field][$delta]);
         }
       }
     }
@@ -211,58 +153,3 @@ function image_replace_image_style_presave(ImageStyleInterface $image_style) {
   }
   $image_style->set('effects', $effects);
 }
-
-/**
- * Implements hook_views_api().
- */
-function image_replace_views_api() {
-  return array(
-    'api' => 3,
-  );
-}
-
-/**
- * Collect info for all image field instances on a given entity_type/bundle.
- *
- * @param string $entity_type
- *   The entity type, e.g. node, for which the info shall be returned.
- * @param string $bundle
- *   The bundle name for which to return instances.
- *
- * @return array
- *   An associative array of instance arrays keyed by the field name.
- */
-function _image_replace_image_fields($entity_type, $bundle) {
-  $image_fields = array();
-
-  $field_definitions = \Drupal::service('entity_field.manager')
-    ->getFieldDefinitions($entity_type, $bundle);
-  foreach ($field_definitions as $field_name => $field_definition) {
-    if ($field_definition->getType() == 'image') {
-      $image_fields[$field_name] = FieldConfig::loadByName(
-        $entity_type, $bundle, $field_name);
-    }
-  }
-
-  return $image_fields;
-}
-
-/**
- * Return a list of image replace enabled style => label map.
- *
- * @return array
- *   A key-value list where keys are style names and values are style labels of
- *   image styles having a replace effect configured.
- */
-function _image_replace_style_options() {
-  $styles = entity_load_multiple('image_style');
-  $result = array();
-  foreach ($styles as $style_name => $style) {
-    foreach ($style->getEffects() as $effect) {
-      if ($effect->getPluginId() == 'image_replace') {
-        $result[$style_name] = SafeMarkup::checkPlain($style->get('label'));
-      }
-    }
-  }
-  return $result;
-}
diff --git a/image_replace.rules.inc b/image_replace.rules.inc
deleted file mode 100644
index 4b824b1..0000000
--- a/image_replace.rules.inc
+++ /dev/null
@@ -1,34 +0,0 @@
-<?php
-
-/**
- * @file
- * Rules integration for Image Replace module.
- */
-
-/**
- * Implements hook_rules_action_info().
- */
-function image_replace_rules_action_info() {
-  return array(
-    'image_replace_rebuild_mapping_action' => array(
-      'label' => t('Rebuild image replace mapping'),
-      'parameter' => array(
-        'entity' => array(
-          'type' => 'entity',
-          'label' => t('Entity'),
-        ),
-      ),
-      'group' => t('Image Replace'),
-    ),
-  );
-}
-
-/**
- * Rules action callback: Rebuild image replacement mapping for a given entity.
- *
- * @param EntityMetadataWrapper $wrapper
- *   The entity for which the mapping should be rebuilt.
- */
-function image_replace_rebuild_mapping_action(EntityMetadataWrapper $wrapper) {
-  image_replace_field_attach_presave($wrapper->type(), $wrapper->value());
-}
diff --git a/image_replace.rules_defaults.inc b/image_replace.rules_defaults.inc
deleted file mode 100644
index f3362d0..0000000
--- a/image_replace.rules_defaults.inc
+++ /dev/null
@@ -1,26 +0,0 @@
-<?php
-
-/**
- * @file
- * Default rules for Image Replace module.
- */
-
-/**
- * Implements hook_default_rules_configuration().
- */
-function image_replace_default_rules_configuration() {
-  $items = array();
-  $items['image_replace_rebuild_mapping'] = entity_import('rules_config', '{ "image_replace_rebuild_mapping" : {
-      "LABEL" : "Rebuild Image Replacement Mapping",
-      "PLUGIN" : "rule",
-      "OWNER" : "rules",
-      "REQUIRES" : [ "image_replace" ],
-      "USES VARIABLES" : { "entity" : { "label" : "Entity", "type" : "entity" } },
-      "DO" : [
-        { "image_replace_rebuild_mapping_action" : { "entity" : [ "entity" ] } }
-      ]
-    }
-  }');
-
-  return $items;
-}
diff --git a/image_replace.services.yml b/image_replace.services.yml
new file mode 100644
index 0000000..92d22f2
--- /dev/null
+++ b/image_replace.services.yml
@@ -0,0 +1,7 @@
+services:
+  image_replace.storage:
+    class: Drupal\image_replace\ImageReplaceStorage
+    arguments: ['@database']
+  image_replace.helper:
+    class: Drupal\image_replace\ImageReplaceHelper
+    arguments: ['@entity_field.manager']
diff --git a/image_replace.views_default.inc b/image_replace.views_default.inc
deleted file mode 100644
index dafe866..0000000
--- a/image_replace.views_default.inc
+++ /dev/null
@@ -1,88 +0,0 @@
-<?php
-
-/**
- * @file
- * Default views for Image Replace module.
- */
-
-/**
- * Implements hook_views_default_views().
- */
-function image_replace_views_default_views() {
-  $views = array();
-
-  $view = new view();
-  $view->name = 'image_replace_rebuild_mapping_node';
-  $view->description = '';
-  $view->tag = 'default';
-  $view->base_table = 'node';
-  $view->human_name = 'Rebuild image replacement mapping';
-  $view->core = 7;
-  $view->api_version = '3.0';
-  $view->disabled = FALSE; /* Edit this to true to make a default view disabled initially */
-
-  /* Display: Master */
-  $handler = $view->new_display('default', 'Master', 'default');
-  $handler->display->display_options['title'] = 'Rebuild image replacement mapping';
-  $handler->display->display_options['use_more_always'] = FALSE;
-  $handler->display->display_options['access']['type'] = 'perm';
-  $handler->display->display_options['access']['perm'] = 'administer site configuration';
-  $handler->display->display_options['cache']['type'] = 'none';
-  $handler->display->display_options['query']['type'] = 'views_query';
-  $handler->display->display_options['exposed_form']['type'] = 'basic';
-  $handler->display->display_options['pager']['type'] = 'full';
-  $handler->display->display_options['pager']['options']['items_per_page'] = '50';
-  $handler->display->display_options['style_plugin'] = 'table';
-  /* Field: Bulk operations: Content */
-  $handler->display->display_options['fields']['views_bulk_operations']['id'] = 'views_bulk_operations';
-  $handler->display->display_options['fields']['views_bulk_operations']['table'] = 'node';
-  $handler->display->display_options['fields']['views_bulk_operations']['field'] = 'views_bulk_operations';
-  $handler->display->display_options['fields']['views_bulk_operations']['vbo_settings']['display_type'] = '1';
-  $handler->display->display_options['fields']['views_bulk_operations']['vbo_settings']['enable_select_all_pages'] = 1;
-  $handler->display->display_options['fields']['views_bulk_operations']['vbo_settings']['row_clickable'] = 1;
-  $handler->display->display_options['fields']['views_bulk_operations']['vbo_settings']['force_single'] = 0;
-  $handler->display->display_options['fields']['views_bulk_operations']['vbo_settings']['entity_load_capacity'] = '10';
-  $handler->display->display_options['fields']['views_bulk_operations']['vbo_operations'] = array(
-    'rules_component::image_replace_rebuild_mapping' => array(
-      'selected' => 1,
-      'postpone_processing' => 0,
-      'skip_confirmation' => 0,
-      'override_label' => 0,
-      'label' => '',
-    ),
-  );
-  /* Field: Content: Title */
-  $handler->display->display_options['fields']['title']['id'] = 'title';
-  $handler->display->display_options['fields']['title']['table'] = 'node';
-  $handler->display->display_options['fields']['title']['field'] = 'title';
-  $handler->display->display_options['fields']['title']['alter']['word_boundary'] = FALSE;
-  $handler->display->display_options['fields']['title']['alter']['ellipsis'] = FALSE;
-  /* Filter criterion: Content: Type */
-  $handler->display->display_options['filters']['type']['id'] = 'type';
-  $handler->display->display_options['filters']['type']['table'] = 'node';
-  $handler->display->display_options['filters']['type']['field'] = 'type';
-  $handler->display->display_options['filters']['type']['value'] = array();
-  $handler->display->display_options['filters']['type']['exposed'] = TRUE;
-  $handler->display->display_options['filters']['type']['expose']['operator_id'] = 'type_op';
-  $handler->display->display_options['filters']['type']['expose']['label'] = 'Type';
-  $handler->display->display_options['filters']['type']['expose']['use_operator'] = TRUE;
-  $handler->display->display_options['filters']['type']['expose']['operator'] = 'type_op';
-  $handler->display->display_options['filters']['type']['expose']['identifier'] = 'type';
-  $handler->display->display_options['filters']['type']['expose']['multiple'] = TRUE;
-  $handler->display->display_options['filters']['type']['expose']['remember_roles'] = array();
-
-  /* Display: Page */
-  $handler = $view->new_display('page', 'Page', 'page');
-  $handler->display->display_options['path'] = 'admin/config/media/image-replace-rebuild-node';
-  $handler->display->display_options['menu']['type'] = 'normal';
-  $handler->display->display_options['menu']['title'] = 'Rebuild image replacement mapping (Content)';
-  $handler->display->display_options['menu']['description'] = 'Apply a new replacement mapping to existing content. Requires <a href="https://www.drupal.org/project/views_bulk_operations">Views Bulk Operations</a> and <a href="https://www.drupal.org/project/rules">Rules</a>.';
-  $handler->display->display_options['menu']['weight'] = '0';
-  $handler->display->display_options['menu']['name'] = 'management';
-  $handler->display->display_options['menu']['context'] = 0;
-  $handler->display->display_options['menu']['context_only_inline'] = 0;
-
-  $views[$view->name] = $view;
-
-  return $views;
-}
diff --git a/src/ImageReplaceHelper.php b/src/ImageReplaceHelper.php
new file mode 100644
index 0000000..040a3fb
--- /dev/null
+++ b/src/ImageReplaceHelper.php
@@ -0,0 +1,78 @@
+<?php
+
+namespace Drupal\image_replace;
+
+use Drupal\Component\Utility\Html;
+use Drupal\Core\Entity\EntityFieldManagerInterface;
+use Drupal\field\Entity\FieldConfig;
+use Drupal\field\Entity\FieldStorageConfig;
+use Drupal\image\Entity\ImageStyle;
+
+/**
+ * Defines a class for image_replace helper functions.
+ */
+class ImageReplaceHelper {
+
+  /**
+   * The entity field manager.
+   *
+   * @var \Drupal\Core\Entity\EntityFieldManagerInterface
+   */
+  protected $entityFieldManager;
+
+  /**
+   * Constructs a ImageReplaceStorage object.
+   *
+   * @param \Drupal\Core\Entity\EntityFieldManagerInterface $entity_field_manager
+   *   The database connection to be used.
+   */
+  public function __construct(EntityFieldManagerInterface $entity_field_manager) {
+    $this->entityFieldManager = $entity_field_manager;
+  }
+
+  /**
+   * Collect info for all image field instances on a given entity_type/bundle.
+   *
+   * @param string $entity_type
+   *   The entity type, e.g. node, for which the info shall be returned.
+   * @param string $bundle
+   *   The bundle name for which to return instances.
+   *
+   * @return array
+   *   An associative array of instance arrays keyed by the field name.
+   */
+  function getImageFields($entity_type, $bundle) {
+    $image_fields = [];
+
+    $field_definitions = $this->entityFieldManager->getFieldDefinitions($entity_type, $bundle);
+    foreach ($field_definitions as $field_name => $field_definition) {
+      if ($field_definition->getType() == 'image') {
+        $image_fields[$field_name] = FieldConfig::loadByName(
+          $entity_type, $bundle, $field_name);
+      }
+    }
+
+    return $image_fields;
+  }
+
+  /**
+   * Return a list of image replace enabled style => label map.
+   *
+   * @return array
+   *   A key-value list where keys are style names and values are style labels
+   *   of image styles having a replace effect configured.
+   */
+  function getStyleOptions() {
+    $styles = ImageStyle::loadMultiple();
+    $result = [];
+    foreach ($styles as $style_name => $style) {
+      foreach ($style->getEffects() as $effect) {
+        if ($effect->getPluginId() == 'image_replace') {
+          $result[$style_name] = Html::escape($style->get('label'));
+        }
+      }
+    }
+    return $result;
+  }
+
+}
diff --git a/src/ImageReplaceStorage.php b/src/ImageReplaceStorage.php
new file mode 100644
index 0000000..86278fc
--- /dev/null
+++ b/src/ImageReplaceStorage.php
@@ -0,0 +1,89 @@
+<?php
+
+namespace Drupal\image_replace;
+
+use Drupal\Core\Database\Connection;
+
+/**
+ * Defines a class for image_replace storage operations.
+ */
+class ImageReplaceStorage {
+
+  /**
+   * Active database connection.
+   *
+   * @var \Drupal\Core\Database\Connection
+   */
+  protected $database;
+
+  /**
+   * Constructs a ImageReplaceStorage object.
+   *
+   * @param \Drupal\Core\Database\Connection $database
+   *   The database connection to be used.
+   */
+  public function __construct(Connection $database) {
+    $this->database = $database;
+  }
+
+  /**
+   * Determine replacement image uri for the given original filename.
+   *
+   * @param string $target_style
+   *   The target image style name.
+   * @param string $target_uri
+   *   The uri of the image for which to find a replacement.
+   *
+   * @return string|NULL
+   *   The replacement uri when a mapping for the given uri/style combination
+   *   exists.
+   */
+  function get($target_style, $target_uri) {
+    $target_uri_hash = hash('sha256', $target_uri);
+    return $this->database->select('image_replace')
+      ->fields('image_replace', ['replacement_uri'])
+      ->condition('target_style', $target_style)
+      ->condition('target_uri_hash', $target_uri_hash)
+      ->execute()
+      ->fetchField();
+  }
+
+  /**
+   * Add an image replacement mapping.
+   *
+   * @param string $target_style
+   *   The target image style name.
+   * @param string $target_uri
+   *   The uri of the image for which to set a replacement.
+   * @param string $replacement_uri
+   *   The replacement uri to set for the given uri/style combination.
+   */
+  function add($target_style, $target_uri, $replacement_uri) {
+    $target_uri_hash = hash('sha256', $target_uri);
+    return $this->database->insert('image_replace')
+      ->fields([
+        'target_style' => $target_style,
+        'target_uri_hash' => $target_uri_hash,
+        'replacement_uri' => $replacement_uri,
+      ])
+      ->execute();
+  }
+
+  /**
+   * Remove the given image replacement mapping if it exists.
+   *
+   * @param string $target_style
+   *   The target image style name.
+   * @param string $target_uri
+   *   The uri of the image for which to remove the replacement.
+   */
+  function remove($target_style, $target_uri) {
+    $target_uri_hash = hash('sha256', $target_uri);
+    return $this->database->delete('image_replace')
+      ->condition('target_style', $target_style)
+      ->condition('target_uri_hash', $target_uri_hash)
+      ->execute();
+  }
+
+
+}
diff --git a/src/Plugin/ImageEffect/ImageReplaceEffect.php b/src/Plugin/ImageEffect/ImageReplaceEffect.php
index 66741c4..683d175 100644
--- a/src/Plugin/ImageEffect/ImageReplaceEffect.php
+++ b/src/Plugin/ImageEffect/ImageReplaceEffect.php
@@ -10,6 +10,7 @@ namespace Drupal\image_replace\Plugin\ImageEffect;
 use Drupal\Core\Image\ImageFactory;
 use Drupal\Core\Image\ImageInterface;
 use Drupal\image\ImageEffectBase;
+use Drupal\image_replace\ImageReplaceStorage;
 use Psr\Log\LoggerInterface;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 
@@ -31,14 +32,22 @@ class ImageReplaceEffect extends ImageEffectBase {
    */
   protected $imageFactory;
 
+  /**
+   * The image replace storage service.
+   *
+   * @var \Drupal\image_replace\ImageReplaceStorage
+   */
+  protected $imageReplaceStorage;
+
   /**
    * {@inheritdoc}
    */
-  public function __construct(array $configuration, $plugin_id, $plugin_definition, LoggerInterface $logger, ImageFactory $image_factory) {
+  public function __construct(array $configuration, $plugin_id, $plugin_definition, LoggerInterface $logger, ImageFactory $image_factory, ImageReplaceStorage $image_replace_storage) {
     parent::__construct($configuration, $plugin_id, $plugin_definition, $logger);
 
     $this->setConfiguration($configuration);
     $this->imageFactory = $image_factory;
+    $this->imageReplaceStorage = $image_replace_storage;
   }
 
   /**
@@ -50,7 +59,8 @@ class ImageReplaceEffect extends ImageEffectBase {
       $plugin_id,
       $plugin_definition,
       $container->get('logger.factory')->get('image'),
-      $container->get('image.factory')
+      $container->get('image.factory'),
+      $container->get('image_replace.storage')
     );
   }
 
@@ -59,12 +69,12 @@ class ImageReplaceEffect extends ImageEffectBase {
    */
   public function applyEffect(ImageInterface $image) {
     $configuration = $this->getConfiguration();
-    $replacement_file = image_replace_get($configuration['data']['image_style'], $image->getSource());
+    $replacement_file = $this->imageReplaceStorage->get($configuration['data']['image_style'], $image->getSource());
     if ($replacement_file) {
       $toolkit_id = $image->getToolkitId();
       $replacement_image = $this->imageFactory->get($replacement_file, $toolkit_id);
       if ($replacement_image) {
-        $image->apply('image_replace', array('replacement_image' => $replacement_image));
+        $image->apply('image_replace', ['replacement_image' => $replacement_image]);
       }
     }
   }
diff --git a/src/Plugin/ImageToolkit/Operation/gd/Replace.php b/src/Plugin/ImageToolkit/Operation/gd/Replace.php
index bae0991..5e8046d 100644
--- a/src/Plugin/ImageToolkit/Operation/gd/Replace.php
+++ b/src/Plugin/ImageToolkit/Operation/gd/Replace.php
@@ -28,11 +28,11 @@ class Replace extends GDImageToolkitOperationBase {
    * {@inheritdoc}
    */
   protected function arguments() {
-    return array(
-      'replacement_image' => array(
+    return [
+      'replacement_image' => [
         'description' => 'The replacement image',
-      ),
-    );
+      ],
+    ];
   }
 
   /**
@@ -48,20 +48,20 @@ class Replace extends GDImageToolkitOperationBase {
   /**
    * {@inheritdoc}
    */
-  protected function execute(array $arguments = array()) {
+  protected function execute(array $arguments = []) {
     // Create a new resource of the required dimensions, and replace the
     // original resource on it with resampling. Destroy the original resource
     // upon success.
     $replacement_toolkit = $this->getReplacementImageToolkit($arguments);
 
     $original_resource = $this->getToolkit()->getResource();
-    $data = array(
+    $data = [
       'width' => $replacement_toolkit->getWidth(),
       'height' => $replacement_toolkit->getHeight(),
       'extension' => image_type_to_extension($this->getToolkit()->getType(), FALSE),
       'transparent_color' => $replacement_toolkit->getTransparentColor(),
       'is_temp' => TRUE,
-    );
+    ];
     if ($this->getToolkit()->apply('create_new', $data)) {
       if (imagecopy($this->getToolkit()->getResource(), $replacement_toolkit->getResource(), 0, 0, 0, 0, $data['width'], $data['height'])) {
         imagedestroy($original_resource);
@@ -87,7 +87,7 @@ class Replace extends GDImageToolkitOperationBase {
    * @return \Drupal\system\Plugin\ImageToolkit\GDToolkit
    *   The correctly typed replacement image toolkit for GD operations.
    */
-  protected function getReplacementImageToolkit(array $arguments = array()) {
+  protected function getReplacementImageToolkit(array $arguments = []) {
     return $arguments['replacement_image']->getToolkit();
   }
 
diff --git a/src/Tests/AdminTest.php b/src/Tests/AdminTest.php
index b8d3700..1f324e0 100644
--- a/src/Tests/AdminTest.php
+++ b/src/Tests/AdminTest.php
@@ -6,7 +6,7 @@
 
 namespace Drupal\image_replace\Tests;
 
-use Drupal\Component\Utility\SafeMarkup;
+use Drupal\Component\Utility\Html;
 use Drupal\image\Entity\ImageStyle;
 use Drupal\node\Entity\Node;
 
@@ -17,7 +17,7 @@ use Drupal\node\Entity\Node;
  */
 class AdminTest extends ImageReplaceTestBase {
 
-  public static $modules = array('views', 'node', 'field_ui', 'image_replace');
+  public static $modules = ['views', 'node', 'field_ui', 'image_replace'];
 
   protected $styleName;
 
@@ -34,14 +34,14 @@ class AdminTest extends ImageReplaceTestBase {
     $this->createImageStyle($this->styleName);
 
     // Add the replacement image field to the article bundle.
-    $this->drupalCreateContentType(array('type' => 'article', 'name' => 'Article'));
+    $this->drupalCreateContentType(['type' => 'article', 'name' => 'Article']);
     $this->createImageField('image_replacement', 'article');
 
     // Add the original image field to the article bundle and specify
     // the replacement image as replacement.
     $this->createImageField('image_original', 'article');
 
-    $display = entity_get_display('node', 'article', 'teaser');
+    $display = \Drupal::service('entity_display.repository')->getViewDisplay('node', 'article', 'teaser');
     $display_options = $display->getComponent('image_original');
     $display_options['type'] = 'image';
     $display_options['settings']['image_style'] = $this->styleName;
@@ -49,7 +49,7 @@ class AdminTest extends ImageReplaceTestBase {
     $display->setComponent('image_original', $display_options);
     $display->save();
 
-    $display = entity_get_display('node', 'article', 'full');
+    $display = \Drupal::service('entity_display.repository')->getViewDisplay('node', 'article', 'full');
     $display_options = $display->getComponent('image_original');
     $display_options['type'] = 'image';
     $display_options['settings']['image_style'] = NULL;
@@ -57,7 +57,7 @@ class AdminTest extends ImageReplaceTestBase {
     $display->setComponent('image_original', $display_options);
     $display->save();
 
-    $this->adminUser = $this->drupalCreateUser(array(
+    $this->adminUser = $this->drupalCreateUser([
       'access content',
       'administer content types',
       'administer image styles',
@@ -66,7 +66,7 @@ class AdminTest extends ImageReplaceTestBase {
       'create article content',
       'delete any article content',
       'edit any article content',
-    ));
+    ]);
   }
 
   /**
@@ -86,57 +86,56 @@ class AdminTest extends ImageReplaceTestBase {
     $this->drupalLogin($this->adminUser);
 
     $this->drupalGet('admin/structure/types/manage/article/fields/node.article.image_original');
-    $this->assertResponse(200);
+    $this->assertSession()->statusCodeEquals(200);
 
     // Verify that a select field is present with a list of available source
     // fields for the generated image style.
     $field_name = 'third_party_settings[image_replace][image_style_map][' . $this->styleName . '][source_field]';
-    $this->assertFieldByName($field_name, "0", 'Image replace selector found for style containing the image replace effect.');
-    $result = $this->xpath($this->constructFieldXpath('name', $field_name));
-
-    $options = $this->getAllOptions($result[0]);
+    $this->assertSession()->fieldExists($field_name);
+    $items = $this->getSession()->getPage()->findField($field_name)->findAll('xpath', 'option');
     $contains_image_original = FALSE;
     $contains_image_replacement = FALSE;
-    foreach ($options as $option) {
-      $contains_image_original |= $option['value'] == 'image_original';
-      $contains_image_replacement |= $option['value'] == 'image_replacement';
+    foreach ($items as $item) {
+      $contains_image_original |= $item->getValue() == 'image_original';
+      $contains_image_replacement |= $item->getValue()== 'image_replacement';
     }
+
     $this->assertFalse($contains_image_original, 'Original image field is not in the list of options.');
     $this->assertTrue($contains_image_replacement, 'Replacement image field is in the list of options.');
 
     // Verify that no select field is present for an image style which does not
     // contain the replacement effect.
     $field_name = 'third_party_settings[image_replace][image_style_map][' . $unrelated_style_name . '][source_field]';
-    $this->assertNoFieldByName($field_name, NULL, 'Image replace settings not present for unrelated style.');
+    $this->assertSession()->fieldNotExists($field_name);
 
     // Choose the replacement image field as the replacement source.
     $field_name = 'third_party_settings[image_replace][image_style_map][' . $this->styleName . '][source_field]';
-    $edit = array(
+    $edit = [
       $field_name => 'image_replacement',
-    );
-    $this->drupalPostForm(NULL, $edit, t('Save settings'));
-    $this->assertResponse(200);
+    ];
+    $this->submitForm($edit, t('Save settings'));
+    $this->assertSession()->statusCodeEquals(200);
 
     // Verify that no message is displayed if the mapping changes when there is
     // no existing content.
-    $this->assertNoText('The image replacement settings have been modified. As a result, it is necessary to rebuild the image replacement mapping for existing content. Note: The replacement mapping is updated automatically when saving an entity.');
+    $this->assertSession()->pageTextNotContains('The image replacement settings have been modified. As a result, it is necessary to rebuild the image replacement mapping for existing content. Note: The replacement mapping is updated automatically when saving an entity.');
 
     // Post new content.
-    $edit = array(
+    $edit = [
       'title[0][value]' => $this->randomString(),
       'promote[value]' => 1,
-    );
-    $edit['files[image_original_0]'] = drupal_realpath($original_file->getFileUri());
-    $edit['files[image_replacement_0]'] = drupal_realpath($replacement_file->getFileUri());
-    $this->drupalPostForm('node/add/article', $edit, $this->getNodeSaveButtonText());
-    $this->assertResponse(200);
+    ];
+    $edit['files[image_original_0]'] = \Drupal::service('file_system')->realpath($original_file->getFileUri());
+    $edit['files[image_replacement_0]'] = \Drupal::service('file_system')->realpath($replacement_file->getFileUri());
+    $this->drupalPostForm('node/add/article', $edit, t('Save'));
+    $this->assertSession()->statusCodeEquals(200);
 
-    $edit = array(
+    $edit = [
       'image_original[0][alt]' => $this->randomString(),
       'image_replacement[0][alt]' => $this->randomString(),
-    );
-    $this->drupalPostForm(NULL, $edit, $this->getNodeSaveButtonText());
-    $this->assertResponse(200);
+    ];
+    $this->submitForm($edit, t('Save'));
+    $this->assertSession()->statusCodeEquals(200);
 
     preg_match('/node\/([0-9]+)/', $this->getUrl(), $matches);
     $node = Node::load($matches[1]);
@@ -144,54 +143,39 @@ class AdminTest extends ImageReplaceTestBase {
     // Verify that the original image is shown on the full node view.
     $generated_url = file_create_url($node->image_original->entity->getFileUri());
     $relative_url = file_url_transform_relative($generated_url);
-    $this->assertRaw(SafeMarkup::checkPlain($relative_url), 'Original image displayed');
+    $this->assertSession()->responseContains(Html::escape($relative_url));
 
     $generated_image_data = $this->drupalGet($generated_url);
-    $this->assertResponse(200);
+    $this->assertSession()->statusCodeEquals(200);
 
     // Assert that the result is the original image.
-    $generated_uri = file_unmanaged_save_data($generated_image_data);
+    $generated_uri = \Drupal::service('file_system')->saveData($generated_image_data);
     $this->assertTrue($this->imageIsOriginal($generated_uri), 'The generated file should be the same as the original file on full node view.');
 
     // Verify that the replacement image is shown on the teaser.
     $this->drupalGet('node');
-    $this->assertResponse(200);
+    $this->assertSession()->statusCodeEquals(200);
     $generated_url = ImageStyle::load($this->styleName)->buildUrl($node->image_original->entity->getFileUri());
     $relative_url = file_url_transform_relative($generated_url);
-    $this->assertRaw(SafeMarkup::checkPlain($relative_url), SafeMarkup::format('Image displayed using style @style.', array('@style' => $this->styleName)));
+    $this->assertSession()->responseContains(Html::escape($relative_url));
 
     $generated_image_data = $this->drupalGet($generated_url);
-    $this->assertResponse(200);
+    $this->assertSession()->statusCodeEquals(200);
 
     // Assert that the result is the replacement image.
-    $generated_uri = file_unmanaged_save_data($generated_image_data);
+    $generated_uri = \Drupal::service('file_system')->saveData($generated_image_data);
     $this->assertTrue($this->imageIsReplacement($generated_uri), 'The generated file should be the same as the replacement file on teaser.');
 
     // Go back to the field settings and reset the replacement mapping.
     $field_name = 'third_party_settings[image_replace][image_style_map][' . $this->styleName . '][source_field]';
-    $edit = array(
+    $edit = [
       $field_name => '0',
-    );
+    ];
     $this->drupalPostForm('admin/structure/types/manage/article/fields/node.article.image_original', $edit, t('Save settings'));
-    $this->assertResponse(200);
+    $this->assertSession()->statusCodeEquals(200);
 
     // Verify that a message is displayed if the mapping changes when there is
     // existing content.
-    $this->assertText('The image replacement settings have been modified. As a result, it is necessary to rebuild the image replacement mapping for existing content. Note: The replacement mapping is updated automatically when saving an entity.');
+    $this->assertSession()->pageTextContains('The image replacement settings have been modified. As a result, it is necessary to rebuild the image replacement mapping for existing content. Note: The replacement mapping is updated automatically when saving an entity.');
   }
-
-  /**
-   * Returns value string for node form submit button (changed in 8.4.x).
-   */
-  protected function getNodeSaveButtonText() {
-    list($core_major, $core_minor) = explode('.', \Drupal::VERSION, 2);
-
-    if ($core_major == 8 && $core_minor < 4) {
-      return t('Save and publish');
-    }
-    else {
-      return t('Save');
-    }
-  }
-
 }
diff --git a/src/Tests/EntityTest.php b/src/Tests/EntityTest.php
index d941150..7cbefc0 100644
--- a/src/Tests/EntityTest.php
+++ b/src/Tests/EntityTest.php
@@ -6,7 +6,7 @@
 
 namespace Drupal\image_replace\Tests;
 
-use Drupal\Component\Utility\SafeMarkup;
+use Drupal\Component\Utility\Html;
 use Drupal\image\Entity\ImageStyle;
 use Drupal\node\Entity\Node;
 
@@ -22,7 +22,7 @@ class EntityTest extends ImageReplaceTestBase {
    *
    * @var array
    */
-  public static $modules = array('views', 'node', 'image_replace');
+  public static $modules = ['views', 'node', 'image_replace'];
 
   protected $styleName;
 
@@ -37,20 +37,20 @@ class EntityTest extends ImageReplaceTestBase {
     $this->createImageStyle($this->styleName);
 
     // Add the replacement image field to the article bundle.
-    $this->drupalCreateContentType(array('type' => 'article', 'name' => 'Article'));
+    $this->drupalCreateContentType(['type' => 'article', 'name' => 'Article']);
     $this->createImageField('image_replacement', 'article');
 
     // Add the original image field to the article bundle and specify
     // the replacement image as replacement.
     $field = $this->createImageField('image_original', 'article');
-    $field->setThirdPartySetting('image_replace', 'image_style_map', array(
-      $this->styleName => array(
+    $field->setThirdPartySetting('image_replace', 'image_style_map', [
+      $this->styleName => [
         'source_field' => 'image_replacement',
-      ),
-    ));
+      ],
+    ]);
     $field->save();
 
-    $display = entity_get_display('node', 'article', 'teaser');
+    $display = \Drupal::service('entity_display.repository')->getViewDisplay('node', 'article', 'teaser');
     $display_options = $display->getComponent('image_original');
     $display_options['type'] = 'image';
     $display_options['settings']['image_style'] = $this->styleName;
@@ -58,7 +58,7 @@ class EntityTest extends ImageReplaceTestBase {
     $display->setComponent('image_original', $display_options);
     $display->save();
 
-    $display = entity_get_display('node', 'article', 'full');
+    $display = \Drupal::service('entity_display.repository')->getViewDisplay('node', 'article', 'full');
     $display_options = $display->getComponent('image_original');
     $display_options['type'] = 'image';
     $display_options['settings']['image_style'] = NULL;
@@ -89,30 +89,30 @@ class EntityTest extends ImageReplaceTestBase {
 
     // Check teaser.
     $this->drupalGet('node');
-    $this->assertResponse(200);
+    $this->assertSession()->statusCodeEquals(200);
     $generated_url = ImageStyle::load($this->styleName)->buildUrl($node->image_original->entity->getFileUri());
     $relative_url = file_url_transform_relative($generated_url);
-    $this->assertRaw(SafeMarkup::checkPlain($relative_url), SafeMarkup::format('Image displayed using style @style.', array('@style' => $this->styleName)));
+    $this->assertSession()->responseContains(Html::escape($relative_url));
 
     $generated_image_data = $this->drupalGet($generated_url);
-    $this->assertResponse(200);
+    $this->assertSession()->statusCodeEquals(200);
 
     // Assert that the result is the replacement image.
-    $generated_uri = file_unmanaged_save_data($generated_image_data);
+    $generated_uri = \Drupal::service('file_system')->saveData($generated_image_data);
     $this->assertTrue($this->imageIsReplacement($generated_uri), 'The generated file should be the same as the replacement file on teaser.');
 
     // Check full view.
     $this->drupalGet('node/' . $node->id());
-    $this->assertResponse(200);
+    $this->assertSession()->statusCodeEquals(200);
     $generated_url = file_create_url($node->image_original->entity->getFileUri());
     $relative_url = file_url_transform_relative($generated_url);
-    $this->assertRaw(SafeMarkup::checkPlain($relative_url), 'Original image displayed');
+    $this->assertSession()->responseContains(Html::escape($relative_url));
 
     $generated_image_data = $this->drupalGet($generated_url);
-    $this->assertResponse(200);
+    $this->assertSession()->statusCodeEquals(200);
 
     // Assert that the result is the original image.
-    $generated_uri = file_unmanaged_save_data($generated_image_data);
+    $generated_uri = \Drupal::service('file_system')->saveData($generated_image_data);
     $this->assertTrue($this->imageIsOriginal($generated_uri), 'The generated file should be the same as the original file on full node view.');
   }
 
diff --git a/src/Tests/ImageReplaceTestBase.php b/src/Tests/ImageReplaceTestBase.php
index b647d79..ef02e09 100644
--- a/src/Tests/ImageReplaceTestBase.php
+++ b/src/Tests/ImageReplaceTestBase.php
@@ -6,17 +6,18 @@
 
 namespace Drupal\image_replace\Tests;
 
-use Drupal\simpletest\WebTestBase;
-use Drupal\node\Entity\Node;
-use Drupal\field\Entity\FieldStorageConfig;
-use Drupal\image\Entity\ImageStyle;
+use Drupal\Core\File\FileSystemInterface;
 use Drupal\field\Entity\FieldConfig;
+use Drupal\field\Entity\FieldStorageConfig;
 use Drupal\file\Entity\File;
+use Drupal\image\Entity\ImageStyle;
+use Drupal\node\Entity\Node;
+use Drupal\Tests\BrowserTestBase;
 
 /**
  * Tests functionality of the replace image effect.
  */
-abstract class ImageReplaceTestBase extends WebTestBase {
+abstract class ImageReplaceTestBase extends BrowserTestBase {
 
   /**
    * Create a new image field.
@@ -32,13 +33,14 @@ abstract class ImageReplaceTestBase extends WebTestBase {
    * @param array $widget_settings
    *   A list of widget settings that will be added to the widget defaults.
    */
-  protected function createImageField($name, $type_name, $storage_settings = array(), $field_settings = array(), $widget_settings = array()) {
+  protected function createImageField($name, $type_name, $storage_settings = [], $field_settings = [], $widget_settings = []) {
     FieldStorageConfig::create([
       'field_name' => $name,
       'entity_type' => 'node',
       'type' => 'image',
       'settings' => $storage_settings,
-      'cardinality' => !empty($storage_settings['cardinality']) ? $storage_settings['cardinality'] : 1,
+      'cardinality' => !empty($storage_settings['cardinality']) ?
+        $storage_settings['cardinality'] : 1,
     ])->save();
 
     $field_config = FieldConfig::create([
@@ -51,14 +53,14 @@ abstract class ImageReplaceTestBase extends WebTestBase {
     ]);
     $field_config->save();
 
-    entity_get_form_display('node', $type_name, 'default')
-      ->setComponent($name, array(
+    \Drupal::service('entity_display.repository')->getFormDisplay('node', $type_name, 'default')
+      ->setComponent($name, [
         'type' => 'image_image',
         'settings' => $widget_settings,
-      ))
+      ])
       ->save();
 
-    entity_get_display('node', $type_name, 'default')
+    \Drupal::service('entity_display.repository')->getViewDisplay('node', $type_name, 'default')
       ->setComponent($name)
       ->save();
 
@@ -80,10 +82,10 @@ abstract class ImageReplaceTestBase extends WebTestBase {
       'name' => $name,
       'label' => $this->randomString(),
     ]);
-    $effect = array(
+    $effect = [
       'id' => 'image_replace',
-      'data' => array(),
-    );
+      'data' => [],
+    ];
     $style->addImageEffect($effect);
     $style->save();
     return $style;
@@ -97,47 +99,48 @@ abstract class ImageReplaceTestBase extends WebTestBase {
    */
   protected function createTestFiles() {
     // Generate test images.
-    $original_uri = file_unmanaged_copy(__DIR__ . '/fixtures/original.png', 'public://', FILE_EXISTS_RENAME);
+    $file_system = \Drupal::service('file_system');
+    $original_uri = $file_system->copy(__DIR__ . '/fixtures/original.png', 'public://', FileSystemInterface::EXISTS_RENAME);
     $this->assertTrue($this->imageIsOriginal($original_uri));
     $this->assertFalse($this->imageIsReplacement($original_uri));
     $original_file = File::create([
-      'filename' => drupal_basename($original_uri),
+      'filename' => $file_system->basename($original_uri),
       'uri' => $original_uri,
       'status' => FILE_STATUS_PERMANENT,
       'filemime' => \Drupal::service('file.mime_type.guesser')->guess($original_uri),
     ]);
     $original_file->save();
 
-    $replacement_uri = file_unmanaged_copy(__DIR__ . '/fixtures/replacement.png', 'public://', FILE_EXISTS_RENAME);
+    $replacement_uri = $file_system->copy(__DIR__ . '/fixtures/replacement.png', 'public://', FileSystemInterface::EXISTS_RENAME);
     $this->assertTrue($this->imageIsReplacement($replacement_uri));
     $this->assertFalse($this->imageIsOriginal($replacement_uri));
     $replacement_file = File::create([
-      'filename' => drupal_basename($replacement_uri),
+      'filename' => $file_system->basename($replacement_uri),
       'uri' => $replacement_uri,
       'status' => FILE_STATUS_PERMANENT,
       'filemime' => \Drupal::service('file.mime_type.guesser')->guess($replacement_uri),
     ]);
     $replacement_file->save();
 
-    return array($original_file, $replacement_file);
+    return [$original_file, $replacement_file];
   }
 
   /**
    * Returns TRUE if the image pointed at by the URI is the original image.
    */
   protected function imageIsOriginal($image_uri) {
-    $expected_info = array(
+    $expected_info = [
       'height' => 90,
       'mime_type' => 'image/png',
       'width' => 120,
-    );
+    ];
 
     $image = \Drupal::service('image.factory')->get($image_uri);
-    $image_info = array(
+    $image_info = [
       'height' => $image->getHeight(),
       'mime_type' => $image->getMimeType(),
       'width' => $image->getWidth(),
-    );
+    ];
 
     // FIXME: Assert that original image has a red pixel on x=40, y=30.
     return $expected_info === $image_info;
@@ -147,18 +150,18 @@ abstract class ImageReplaceTestBase extends WebTestBase {
    * Returns TRUE if the image pointed at by the URI is the replacement image.
    */
   protected function imageIsReplacement($image_uri) {
-    $expected_info = array(
+    $expected_info = [
       'height' => 60,
       'mime_type' => 'image/png',
       'width' => 80,
-    );
+    ];
 
     $image = \Drupal::service('image.factory')->get($image_uri);
-    $image_info = array(
+    $image_info = [
       'height' => $image->getHeight(),
       'mime_type' => $image->getMimeType(),
       'width' => $image->getWidth(),
-    );
+    ];
 
     // FIXME: Assert that replacement image has a green pixel on x=40, y=30.
     return $expected_info === $image_info;
diff --git a/src/Tests/ReplaceEffectTest.php b/src/Tests/ReplaceEffectTest.php
index 010d48c..ade2ad1 100644
--- a/src/Tests/ReplaceEffectTest.php
+++ b/src/Tests/ReplaceEffectTest.php
@@ -20,7 +20,7 @@ class ReplaceEffectTest extends ImageReplaceTestBase {
    *
    * @var array
    */
-  public static $modules = array('image_replace');
+  public static $modules = ['image_replace'];
 
   /**
    * Tests functionality of the replace image effect.
@@ -36,41 +36,41 @@ class ReplaceEffectTest extends ImageReplaceTestBase {
 
     // Create an image style containing the replace effect.
     $style_name = 'image_replace_test';
-    $style = $this->createImageStyle($style_name);
+    $this->createImageStyle($style_name);
 
     // Apply the image style to a test image.
     $generated_url = ImageStyle::load($style_name)->buildUrl($original_file->getFileUri());
     $generated_image_data = $this->drupalGet($generated_url);
-    $this->assertResponse(200);
+    $this->assertSession()->statusCodeEquals(200);
 
     // Assert that the result is the original image.
-    $generated_uri = file_unmanaged_save_data($generated_image_data);
+    $generated_uri = \Drupal::service('file_system')->saveData($generated_image_data);
     $this->assertTrue($this->imageIsOriginal($generated_uri), 'The generated file should be the same as the original file if there is no replacement mapping.');
 
     // Set up a replacement image.
-    image_replace_add($style_name, $original_file->getFileUri(), $replacement_file->getFileUri());
+    \Drupal::service('image_replace.storage')->add($style_name, $original_file->getFileUri(), $replacement_file->getFileUri());
     ImageStyle::load($style_name)->flush();
 
     // Apply the image style to the test imge.
     $generated_url = ImageStyle::load($style_name)->buildUrl($original_file->getFileUri());
     $generated_image_data = $this->drupalGet($generated_url);
-    $this->assertResponse(200);
+    $this->assertSession()->statusCodeEquals(200);
 
     // Assert that the result is the replacement image.
-    $generated_uri = file_unmanaged_save_data($generated_image_data);
+    $generated_uri = \Drupal::service('file_system')->saveData($generated_image_data);
     $this->assertTrue($this->imageIsReplacement($generated_uri), 'The generated file should be the same as the replacement file.');
 
     // Set up a replacement image.
-    image_replace_remove($style_name, $original_file->getFileUri(), $replacement_file->getFileUri());
+    \Drupal::service('image_replace.storage')->remove($style_name, $original_file->getFileUri(), $replacement_file->getFileUri());
     ImageStyle::load($style_name)->flush();
 
     // Apply the image style to a test image.
     $generated_url = ImageStyle::load($style_name)->buildUrl($original_file->getFileUri());
     $generated_image_data = $this->drupalGet($generated_url);
-    $this->assertResponse(200);
+    $this->assertSession()->statusCodeEquals(200);
 
     // Assert that the result is the original image.
-    $generated_uri = file_unmanaged_save_data($generated_image_data);
+    $generated_uri = \Drupal::service('file_system')->saveData($generated_image_data);
     $this->assertTrue($this->imageIsOriginal($generated_uri), 'The generated file should be the same as the original file if the replacement mapping was removed.');
   }
 
-- 
2.20.0.windows.1

