diff --git a/core/includes/common.inc b/core/includes/common.inc
index d0e9a2f..9ddbfbe 100644
--- a/core/includes/common.inc
+++ b/core/includes/common.inc
@@ -1160,84 +1160,6 @@ function valid_number_step($value, $step, $offset = 0.0) {
  */
 
 /**
- * Registers an event for the current visitor to the flood control mechanism.
- *
- * @param $name
- *   The name of an event.
- * @param $window
- *   Optional number of seconds before this event expires. Defaults to 3600 (1
- *   hour). Typically uses the same value as the flood_is_allowed() $window
- *   parameter. Expired events are purged on cron run to prevent the flood table
- *   from growing indefinitely.
- * @param $identifier
- *   Optional identifier (defaults to the current user's IP address).
- */
-function flood_register_event($name, $window = 3600, $identifier = NULL) {
-  if (!isset($identifier)) {
-    $identifier = ip_address();
-  }
-  db_insert('flood')
-    ->fields(array(
-      'event' => $name,
-      'identifier' => $identifier,
-      'timestamp' => REQUEST_TIME,
-      'expiration' => REQUEST_TIME + $window,
-    ))
-    ->execute();
-}
-
-/**
- * Makes the flood control mechanism forget an event for the current visitor.
- *
- * @param $name
- *   The name of an event.
- * @param $identifier
- *   Optional identifier (defaults to the current user's IP address).
- */
-function flood_clear_event($name, $identifier = NULL) {
-  if (!isset($identifier)) {
-    $identifier = ip_address();
-  }
-  db_delete('flood')
-    ->condition('event', $name)
-    ->condition('identifier', $identifier)
-    ->execute();
-}
-
-/**
- * Checks whether a user is allowed to proceed with the specified event.
- *
- * Events can have thresholds saying that each user can only do that event
- * a certain number of times in a time window. This function verifies that the
- * current user has not exceeded this threshold.
- *
- * @param $name
- *   The unique name of the event.
- * @param $threshold
- *   The maximum number of times each user can do this event per time window.
- * @param $window
- *   Number of seconds in the time window for this event (default is 3600
- *   seconds, or 1 hour).
- * @param $identifier
- *   Unique identifier of the current user. Defaults to their IP address.
- *
- * @return
- *   TRUE if the user is allowed to proceed. FALSE if they have exceeded the
- *   threshold and should not be allowed to proceed.
- */
-function flood_is_allowed($name, $threshold, $window = 3600, $identifier = NULL) {
-  if (!isset($identifier)) {
-    $identifier = ip_address();
-  }
-  $number = db_query("SELECT COUNT(*) FROM {flood} WHERE event = :event AND identifier = :identifier AND timestamp > :timestamp", array(
-    ':event' => $name,
-    ':identifier' => $identifier,
-    ':timestamp' => REQUEST_TIME - $window))
-    ->fetchField();
-  return ($number < $threshold);
-}
-
-/**
  * @defgroup sanitization Sanitization functions
  * @{
  * Functions to sanitize values.
diff --git a/core/lib/Drupal/Core/Utility/Flood.php b/core/lib/Drupal/Core/Utility/Flood.php
new file mode 100644
index 0000000..ce05418
--- /dev/null
+++ b/core/lib/Drupal/Core/Utility/Flood.php
@@ -0,0 +1,92 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\Core\Utility\Flood.
+ */
+
+namespace Drupal\Core\Utility;
+
+/**
+ * Provides flood control mechanisms.
+ */
+class Flood {
+
+  /**
+   * Registers an event for the current visitor to the flood control mechanism.
+   *
+   * @param $name
+   *   The name of an event.
+   * @param $window
+   *   Optional number of seconds before this event expires. Defaults to 3600 (1
+   *   hour). Typically uses the same value as the flood_is_allowed() $window
+   *   parameter. Expired events are purged on cron run to prevent the flood table
+   *   from growing indefinitely.
+   * @param $identifier
+   *   Optional identifier (defaults to the current user's IP address).
+   */
+  public static function registerEvent($name, $window = 3600, $identifier = NULL) {
+    if (!isset($identifier)) {
+      $identifier = ip_address();
+    }
+    db_insert('flood')
+      ->fields(array(
+        'event' => $name,
+        'identifier' => $identifier,
+        'timestamp' => REQUEST_TIME,
+        'expiration' => REQUEST_TIME + $window,
+      ))
+      ->execute();
+  }
+
+  /**
+   * Makes the flood control mechanism forget an event for the current visitor.
+   *
+   * @param $name
+   *   The name of an event.
+   * @param $identifier
+   *   Optional identifier (defaults to the current user's IP address).
+   */
+  public static function clearEvent($name, $identifier = NULL) {
+    if (!isset($identifier)) {
+      $identifier = ip_address();
+    }
+    db_delete('flood')
+      ->condition('event', $name)
+      ->condition('identifier', $identifier)
+      ->execute();
+  }
+
+  /**
+   * Checks whether a user is allowed to proceed with the specified event.
+   *
+   * Events can have thresholds saying that each user can only do that event
+   * a certain number of times in a time window. This function verifies that the
+   * current user has not exceeded this threshold.
+   *
+   * @param $name
+   *   The unique name of the event.
+   * @param $threshold
+   *   The maximum number of times each user can do this event per time window.
+   * @param $window
+   *   Number of seconds in the time window for this event (default is 3600
+   *   seconds, or 1 hour).
+   * @param $identifier
+   *   Unique identifier of the current user. Defaults to their IP address.
+   *
+   * @return
+   *   TRUE if the user is allowed to proceed. FALSE if they have exceeded the
+   *   threshold and should not be allowed to proceed.
+   */
+  public static function isAllowed($name, $threshold, $window = 3600, $identifier = NULL) {
+    if (!isset($identifier)) {
+      $identifier = ip_address();
+    }
+    $number = db_query("SELECT COUNT(*) FROM {flood} WHERE event = :event AND identifier = :identifier AND timestamp > :timestamp", array(
+      ':event' => $name,
+      ':identifier' => $identifier,
+      ':timestamp' => REQUEST_TIME - $window))
+      ->fetchField();
+    return ($number < $threshold);
+  }
+}
diff --git a/core/modules/contact/contact.pages.inc b/core/modules/contact/contact.pages.inc
index 61cc5d1..7d58e3c 100644
--- a/core/modules/contact/contact.pages.inc
+++ b/core/modules/contact/contact.pages.inc
@@ -5,6 +5,7 @@
  * Page callbacks for the Contact module.
  */
 
+use Drupal\Core\Utility\Flood;
 use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
 use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
 
@@ -23,7 +24,7 @@ function contact_site_form($form, &$form_state) {
   $config = config('contact.settings');
   $limit = $config->get('flood.limit');
   $interval = $config->get('flood.interval');
-  if (!flood_is_allowed('contact', $limit, $interval) && !user_access('administer contact forms')) {
+  if (!Flood::isAllowed('contact', $limit, $interval) && !user_access('administer contact forms')) {
     drupal_set_message(t("You cannot send more than %limit messages in @interval. Try again later.", array('%limit' => $limit, '@interval' => format_interval($interval))), 'error');
     throw new AccessDeniedHttpException();
   }
@@ -180,7 +181,7 @@ function contact_site_form_submit($form, &$form_state) {
     drupal_mail('contact', 'page_autoreply', $from, $language_interface->langcode, $values, $to);
   }
 
-  flood_register_event('contact', config('contact.settings')->get('flood.interval'));
+  Flood::registerEvent('contact', config('contact.settings')->get('flood.interval'));
   watchdog('mail', '%sender-name (@sender-from) sent an e-mail regarding %category.', array('%sender-name' => $values['name'], '@sender-from' => $from, '%category' => $values['category']['category']));
 
   // Jump to home page rather than back to contact page to avoid
@@ -204,7 +205,7 @@ function contact_personal_form($form, &$form_state, $recipient) {
   $config = config('contact.settings');
   $limit = $config->get('flood.limit');
   $interval = $config->get('flood.interval');
-  if (!flood_is_allowed('contact', $limit, $interval) && !user_access('administer contact forms') && !user_access('administer users')) {
+  if (!Flood::isAllowed('contact', $limit, $interval) && !user_access('administer contact forms') && !user_access('administer users')) {
     drupal_set_message(t("You cannot send more than %limit messages in @interval. Try again later.", array('%limit' => $limit, '@interval' => format_interval($interval))), 'error');
     throw new AccessDeniedHttpException();
   }
@@ -315,7 +316,7 @@ function contact_personal_form_submit($form, &$form_state) {
     drupal_mail('contact', 'user_copy', $from, $language_interface->langcode, $values, $from);
   }
 
-  flood_register_event('contact', config('contact.settings')->get('flood.interval'));
+  Flood::registerEvent('contact', config('contact.settings')->get('flood.interval'));
   watchdog('mail', '%sender-name (@sender-from) sent %recipient-name an e-mail.', array('%sender-name' => $values['name'], '@sender-from' => $from, '%recipient-name' => $values['recipient']->name));
 
   // Jump to the contacted user's profile page.
diff --git a/core/modules/system/lib/Drupal/system/Tests/System/FloodTest.php b/core/modules/system/lib/Drupal/system/Tests/System/FloodTest.php
index e120128..41db458 100644
--- a/core/modules/system/lib/Drupal/system/Tests/System/FloodTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/System/FloodTest.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\system\Tests\System;
 
+use Drupal\Core\Utility\Flood;
 use Drupal\simpletest\WebTestBase;
 
 /**
@@ -30,19 +31,19 @@ class FloodTest extends WebTestBase {
     $name = 'flood_test_cleanup';
 
     // Register expired event.
-    flood_register_event($name, $window_expired);
+    Flood::registerEvent($name, $window_expired);
     // Verify event is not allowed.
-    $this->assertFalse(flood_is_allowed($name, $threshold));
+    $this->assertFalse(Flood::isAllowed($name, $threshold));
     // Run cron and verify event is now allowed.
     $this->cronRun();
-    $this->assertTrue(flood_is_allowed($name, $threshold));
+    $this->assertTrue(Flood::isAllowed($name, $threshold));
 
     // Register unexpired event.
-    flood_register_event($name);
+    Flood::registerEvent($name);
     // Verify event is not allowed.
-    $this->assertFalse(flood_is_allowed($name, $threshold));
+    $this->assertFalse(Flood::isAllowed($name, $threshold));
     // Run cron and verify event is still not allowed.
     $this->cronRun();
-    $this->assertFalse(flood_is_allowed($name, $threshold));
+    $this->assertFalse(Flood::isAllowed($name, $threshold));
   }
 }
diff --git a/core/modules/user/user.module b/core/modules/user/user.module
index c57fca2..6ef7b17 100644
--- a/core/modules/user/user.module
+++ b/core/modules/user/user.module
@@ -4,6 +4,7 @@ use Drupal\Core\Database\Query\SelectInterface;
 use Drupal\Core\Entity\EntityInterface;
 use Drupal\file\File;
 use Drupal\Core\Template\Attribute;
+use Drupal\Core\Utility\Flood;
 use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
 
 /**
@@ -1628,7 +1629,7 @@ function user_login_authenticate_validate($form, &$form_state) {
     // independent of the per-user limit to catch attempts from one IP to log
     // in to many different user accounts.  We have a reasonably high limit
     // since there may be only one apparent IP for all users at an institution.
-    if (!flood_is_allowed('failed_login_attempt_ip', $flood_config->get('ip_limit'), $flood_config->get('ip_window'))) {
+    if (!Flood::isAllowed('failed_login_attempt_ip', $flood_config->get('ip_limit'), $flood_config->get('ip_window'))) {
       $form_state['flood_control_triggered'] = 'ip';
       return;
     }
@@ -1649,7 +1650,7 @@ function user_login_authenticate_validate($form, &$form_state) {
 
       // Don't allow login if the limit for this user has been reached.
       // Default is to allow 5 failed attempts every 6 hours.
-      if (!flood_is_allowed('failed_login_attempt_user', $flood_config->get('user_limit'), $flood_config->get('user_window'), $identifier)) {
+      if (!Flood::isAllowed('failed_login_attempt_user', $flood_config->get('user_limit'), $flood_config->get('user_window'), $identifier)) {
         $form_state['flood_control_triggered'] = 'user';
         return;
       }
@@ -1671,10 +1672,10 @@ function user_login_final_validate($form, &$form_state) {
   $flood_config = config('user.flood');
   if (empty($form_state['uid'])) {
     // Always register an IP-based failed login event.
-    flood_register_event('failed_login_attempt_ip', $flood_config->get('ip_window'));
+    Flood::registerEvent('failed_login_attempt_ip', $flood_config->get('ip_window'));
     // Register a per-user failed login event.
     if (isset($form_state['flood_control_user_identifier'])) {
-      flood_register_event('failed_login_attempt_user', $flood_config->get('user_window'), $form_state['flood_control_user_identifier']);
+      Flood::registerEvent('failed_login_attempt_user', $flood_config->get('user_window'), $form_state['flood_control_user_identifier']);
     }
 
     if (isset($form_state['flood_control_triggered'])) {
@@ -1694,7 +1695,7 @@ function user_login_final_validate($form, &$form_state) {
   elseif (isset($form_state['flood_control_user_identifier'])) {
     // Clear past failures for this user so as not to block a user who might
     // log in and out more than once in an hour.
-    flood_clear_event('failed_login_attempt_user', $form_state['flood_control_user_identifier']);
+    Flood::clearEvent('failed_login_attempt_user', $form_state['flood_control_user_identifier']);
   }
 }
 
