diff --git src/wp-includes/class-wp-hook.php src/wp-includes/class-wp-hook.php
index efb1360..ebdcecc 100644
--- src/wp-includes/class-wp-hook.php
+++ src/wp-includes/class-wp-hook.php
@@ -42,6 +42,23 @@ final class WP_Hook implements Iterator, ArrayAccess {
 	private $current_priority = array();
 
 	/**
+	 * The current callback that is running.
+	 *
+	 * @since 5.0
+	 * @var callable
+	 */
+	private $current_callback = null;
+	/**
+
+	/**
+	 * The previous callback that ran before the current one.
+	 *
+	 * @since 5.0
+	 * @var callable
+	 */
+	private $previous_callback = null;
+
+	/**
 	 * Number of levels this hook can be recursively called.
 	 *
 	 * @since 4.7.0
@@ -279,6 +296,8 @@ final class WP_Hook implements Iterator, ArrayAccess {
 					$args[0] = $value;
 				}
 
+				$this->current_callback = $the_;
+
 				// Avoid the array_slice if possible.
 				if ( $the_['accepted_args'] == 0 ) {
 					$value = call_user_func_array( $the_['function'], array() );
@@ -287,6 +306,9 @@ final class WP_Hook implements Iterator, ArrayAccess {
 				} else {
 					$value = call_user_func_array( $the_['function'], array_slice( $args, 0, (int) $the_['accepted_args'] ) );
 				}
+
+				$this->current_callback = null;
+				$this->previous_callback = $the_;
 			}
 		} while ( false !== next( $this->iterations[ $nesting_level ] ) );
 
@@ -353,6 +375,28 @@ final class WP_Hook implements Iterator, ArrayAccess {
 	}
 
 	/**
+	 * Return the current callback that is running.
+	 *
+	 * @since 5.0
+	 *
+	 * @return callable|null Callback that is running or null if no callback running.
+	 */
+	public function current_callback() {
+		return $this->current_callback;
+	}
+
+	/**
+	 * Return the callback that has just run, or ran before the current callback.
+	 *
+	 * @since 5.0
+	 *
+	 * @return callable|null Callback that ran previously or null if no callback ran previously.
+	 */
+	public function previous_callback() {
+		return $this->previous_callback;
+	}
+
+	/**
 	 * Normalizes filters set up before WordPress has initialized to WP_Hook objects.
 	 *
 	 * @since 4.7.0
diff --git src/wp-includes/plugin.php src/wp-includes/plugin.php
index 879bb12..5da1805 100644
--- src/wp-includes/plugin.php
+++ src/wp-includes/plugin.php
@@ -115,6 +115,70 @@ function add_filter( $tag, $function_to_add, $priority = 10, $accepted_args = 1
 }
 
 /**
+ * Hook a function or method to a specific filter callback that is only run once.
+ *
+ * The added $function_to_add will only be run once and removed from the filter.
+ *
+ * @since 5.0
+ * @see add_filter
+ */
+function add_filter_once( $tag, $function_to_add, $priority = 10, $accepted_args = 1 ) {
+	$result = add_filter( $tag, $function_to_add, $priority, $accepted_args );
+
+	global $wp_filter;
+
+	$idx = _wp_filter_build_unique_id( null, $function_to_add, null );
+	$wp_filter[ $tag ]->callbacks[ $priority ][ "_remove_filter_once_$idx" ] = array(
+		'function'      => '_remove_filter_once',
+		'accepted_args' => $accepted_args
+	);
+
+	// Add a remove action immediately after our $function_to_add has run.
+	add_filter( $tag, '_remove_filter_once', $priority );
+
+	return $result;
+}
+
+/**
+ * Callback that removes an ephemeral filter (via add_filter_once)
+ * immediately after its execution.
+ *
+ * This can be substituted for anoymous callbacks once WordPress
+ * reaches PHP 5.3 minimum maturity.
+ *
+ * @since 5.0
+ * @see add_filter_once
+ * @internal
+ */
+function _remove_filter_once() {
+	global $wp_filter;
+
+	$tag               = current_filter();
+	$current_filter    = $wp_filter[ $tag ];
+	$current_priority  = $current_filter->current_priority();
+	$previous_callback = $current_filter->previous_callback();
+
+	// Remove the callback that has to be run once.
+	remove_filter( $tag, $previous_callback['function'], $current_priority );
+
+	$idx = _wp_filter_build_unique_id( null, $previous_callback['function'], null );
+
+	/**
+	 * Remove the remove filter :) ... Filterception.
+	 *
+	 * `remove_filter` removes a callback by unique ID.
+	 * This means that we can precaluculate and pass the
+	 * unique ID as is.
+	 */
+	remove_filter( $tag, "_remove_filter_once_$idx", $current_priority );
+
+	if ( func_num_args() ) {
+		// If this is a filter return the result.
+		return func_get_arg( 0 );
+	}
+}
+
+/**
  * Check if any filter has been registered for a hook.
  *
  * @since 2.5.0
@@ -407,6 +471,18 @@ function add_action( $tag, $function_to_add, $priority = 10, $accepted_args = 1
 }
 
 /**
+ * Hook a function or method to a specific action callback that is only run once.
+ *
+ * The added $function_to_add will only be run once and removed from the action.
+ *
+ * @since 5.0
+ * @see add_action
+ */
+function add_action_once( $tag, $function_to_add, $priority = 10, $accepted_args = 1 ) {
+	return add_filter_once( $tag, $function_to_add, $priority, $accepted_args );
+}
+
+/**
  * Execute functions hooked on a specific action hook.
  *
  * This function invokes all functions attached to action hook `$tag`. It is
