Index: src/wp-includes/plugin.php
===================================================================
--- src/wp-includes/plugin.php	(revision 36219)
+++ src/wp-includes/plugin.php	(working copy)
@@ -20,7 +20,7 @@
  */
 
 // Initialize the filter globals.
-global $wp_filter, $wp_actions, $merged_filters, $wp_current_filter;
+global $wp_filter, $wp_actions, $merged_filters, $wp_current_filter, $wp_done_filters;
 
 if ( ! isset( $wp_filter ) )
 	$wp_filter = array();
@@ -34,6 +34,10 @@
 if ( ! isset( $wp_current_filter ) )
 	$wp_current_filter = array();
 
+if ( ! isset( $wp_done_filters ) ) {
+	$wp_done_filters = array();
+}
+
 /**
  * Hook a function or method to a specific filter action.
  *
@@ -190,6 +194,7 @@
  * @global array $wp_filter         Stores all of the filters.
  * @global array $merged_filters    Merges the filter hooks using this function.
  * @global array $wp_current_filter Stores the list of current filters with the current one last.
+ * @global array $wp_done_filters   Stores the number of times filters have been run.
  *
  * @param string $tag     The name of the filter hook.
  * @param mixed  $value   The value on which the filters hooked to `$tag` are applied on.
@@ -197,8 +202,14 @@
  * @return mixed The filtered value after all hooked functions are applied to it.
  */
 function apply_filters( $tag, $value ) {
-	global $wp_filter, $merged_filters, $wp_current_filter;
+	global $wp_filter, $merged_filters, $wp_current_filter, $wp_done_filters, $wp_actions;
 
+	if ( ! isset( $wp_actions[ $tag ] ) ) {
+		$wp_actions[$tag] = 1;
+	} else {
+		++$wp_actions[ $tag] ;
+	}
+
 	$args = array();
 
 	// Do 'all' actions first.
@@ -233,6 +244,11 @@
 			if ( !is_null($the_['function']) ){
 				$args[1] = $value;
 				$value = call_user_func_array($the_['function'], array_slice($args, 1, (int) $the_['accepted_args']));
+				if ( empty( $wp_done_filters[ $tag ] ) ){
+					$wp_done_filters[ $tag ] = 1;
+				} else {
+					$wp_done_filters[ $tag ] = $wp_done_filters[ $tag ] + 1;
+				}
 			}
 
 	} while ( next($wp_filter[$tag]) !== false );
@@ -252,7 +268,8 @@
  *
  * @global array $wp_filter         Stores all of the filters
  * @global array $merged_filters    Merges the filter hooks using this function.
- * @global array $wp_current_filter Stores the list of current filters with the current one last
+ * @global array $wp_current_filter Stores the list of current filters with the current one last.
+ * @global array $wp_done_filters   Stores the number of times filters have been run.
  *
  * @param string $tag  The name of the filter hook.
  * @param array  $args The arguments supplied to the functions hooked to $tag.
@@ -259,8 +276,14 @@
  * @return mixed The filtered value after all hooked functions are applied to it.
  */
 function apply_filters_ref_array($tag, $args) {
-	global $wp_filter, $merged_filters, $wp_current_filter;
+	global $wp_filter, $merged_filters, $wp_current_filter, $wp_done_filters, $wp_actions;
 
+	if ( ! isset( $wp_actions[ $tag ] ) ) {
+		$wp_actions[$tag] = 1;
+	} else {
+		++$wp_actions[ $tag] ;
+	}
+
 	// Do 'all' actions first
 	if ( isset($wp_filter['all']) ) {
 		$wp_current_filter[] = $tag;
@@ -287,8 +310,14 @@
 
 	do {
 		foreach ( (array) current($wp_filter[$tag]) as $the_ )
-			if ( !is_null($the_['function']) )
+			if ( !is_null($the_['function'] ) ) {
 				$args[0] = call_user_func_array($the_['function'], array_slice($args, 0, (int) $the_['accepted_args']));
+				if ( empty( $wp_done_filters[ $tag ] ) ){
+					$wp_done_filters[ $tag ] = 1;
+				} else {
+					$wp_done_filters[ $tag ] = $wp_done_filters[ $tag ] + 1;
+				}
+			}
 
 	} while ( next($wp_filter[$tag]) !== false );
 
@@ -530,6 +559,42 @@
 }
 
 /**
+ * Retrieve the number of functions run on a filter.
+ *
+ * @since 4.5.0
+ *
+ * @global array $wp_done_filters Increments the amount of times a function was run on a filter.
+ *
+ * @param string $tag The name of the filter hook.
+ * @return int The number of times filter hooked function is fired on filter $tag.
+ */
+function filter_functions_run($tag) {
+	global $wp_done_filters;
+
+	if ( ! isset( $wp_done_filters[ $tag ] ) ) {
+		return 0;
+	}
+
+	return $wp_done_filters[$tag];
+}
+
+/**
+ * Retrieve the number of times a filter is fired.
+ *
+ * An alias of did_action for filters.
+ *
+ * @since 4.5.0
+ *
+ * @uses did_action
+ *
+ * @param string $tag The name of the filter hook.
+ * @return int The number of times filter hook $tag is fired.
+ */
+function did_filter($tag) {
+	return did_action($tag);
+}
+
+/**
  * Retrieve the number of times an action is fired.
  *
  * @since 2.1.0
Index: tests/phpunit/tests/filters.php
===================================================================
--- tests/phpunit/tests/filters.php	(revision 36219)
+++ tests/phpunit/tests/filters.php	(working copy)
@@ -235,6 +235,56 @@
 	}
 
 	/**
+	 * @ticket 35357
+	 */
+	function test_did_filter() {
+		$obj = new stdClass();
+		$a = new MockAction();
+		$b = new MockAction();
+		$c = new MockAction();
+		$tag1 = rand_str();
+		$tag2 = rand_str();
+		$tag3 = rand_str();
+
+		add_action( $tag1, array( $a, 'filter_append' ), 10, 2 );
+		add_action( $tag2, array( $b, 'filter_append' ), 10, 2 );
+		add_action( $tag3, array( $c, 'filter_append' ), 10, 2 );
+
+		$result1 = apply_filters_ref_array( $tag2, array( 'string', &$obj ) );
+		$result2 = apply_filters_ref_array( $tag3, array( 'string', &$obj ) );
+		$result3 = apply_filters_ref_array( $tag3, array( 'string', &$obj ) );
+
+		$this->assertEquals( 0, did_filter( $tag1 ) );
+		$this->assertEquals( 1, did_filter( $tag2 ) );
+		$this->assertEquals( 2, did_filter( $tag3 ) );
+	}
+
+	/**
+	 * @ticket 35357
+	 */
+	function test_filter_functions_run() {
+		$obj = new stdClass();
+		$a = new MockAction();
+		$b = new MockAction();
+		$tag1 = rand_str();
+		$tag2 = rand_str();
+		$tag3 = rand_str();
+
+		add_action( $tag2, array( $a, 'filter_append' ), 10, 2 );
+
+		add_action( $tag3, array( $a, 'filter_append' ), 10, 2 );
+		add_action( $tag3, array( $b, 'filter_append' ), 10, 2 );
+
+		$result1 = apply_filters_ref_array( $tag1, array('string', &$obj ) );
+		$result2 = apply_filters_ref_array( $tag2, array('string', &$obj ) );
+		$result3 = apply_filters_ref_array( $tag3, array('string', &$obj ) );
+
+		$this->assertEquals( 0, filter_functions_run( $tag1 ) );
+		$this->assertEquals( 1, filter_functions_run( $tag2 ) );
+		$this->assertEquals( 2, filter_functions_run( $tag3 ) );
+	}
+
+	/**
 	 * @ticket 12723
 	 */
 	function test_filter_ref_array_result() {
