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,7 +202,7 @@
  * @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;
 
 	$args = array();
 
@@ -233,6 +238,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 +262,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,7 +270,7 @@
  * @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;
 
 	// Do 'all' actions first
 	if ( isset($wp_filter['all']) ) {
@@ -287,8 +298,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 +547,26 @@
 }
 
 /**
+ * Retrieve the number of times a filter is fired.
+ *
+ * @since 4.5.0
+ *
+ * @global array $wp_done_filters Increments the amount of times filter was triggered.
+ *
+ * @param string $tag The name of the filter hook.
+ * @return int The number of times filter hook $tag is fired.
+ */
+function did_filter($tag) {
+	global $wp_done_filters;
+
+	if ( ! isset( $wp_done_filters[ $tag ] ) ) {
+		return 0;
+	}
+
+	return $wp_done_filters[$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,31 @@
 	}
 
 	/**
+	 * @ticket 35357
+	 */
+	function test_did_filter() {
+		$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( did_filter( $tag1 ) , 0 );
+		$this->assertEquals( did_filter( $tag2 ) , 1 );
+		$this->assertEquals( did_filter( $tag3 ) , 2 );
+	}
+
+	/**
 	 * @ticket 12723
 	 */
 	function test_filter_ref_array_result() {
@@ -299,20 +324,20 @@
 	 * @ticket 29070
 	 */
 	 function test_has_filter_doesnt_reset_wp_filter() {
-	 	add_action( 'action_test_has_filter_doesnt_reset_wp_filter', '__return_null', 1 );
-	 	add_action( 'action_test_has_filter_doesnt_reset_wp_filter', '__return_null', 2 );
-	 	add_action( 'action_test_has_filter_doesnt_reset_wp_filter', '__return_null', 3 );
-	 	add_action( 'action_test_has_filter_doesnt_reset_wp_filter', array( $this, '_action_test_has_filter_doesnt_reset_wp_filter' ), 4 );
+		add_action( 'action_test_has_filter_doesnt_reset_wp_filter', '__return_null', 1 );
+		add_action( 'action_test_has_filter_doesnt_reset_wp_filter', '__return_null', 2 );
+		add_action( 'action_test_has_filter_doesnt_reset_wp_filter', '__return_null', 3 );
+		add_action( 'action_test_has_filter_doesnt_reset_wp_filter', array( $this, '_action_test_has_filter_doesnt_reset_wp_filter' ), 4 );
 
-	 	do_action( 'action_test_has_filter_doesnt_reset_wp_filter' );
+		do_action( 'action_test_has_filter_doesnt_reset_wp_filter' );
 	 }
 	 function _action_test_has_filter_doesnt_reset_wp_filter() {
-	 	global $wp_filter;
+		global $wp_filter;
 
-	 	has_action( 'action_test_has_filter_doesnt_reset_wp_filter', '_function_that_doesnt_exist' );
+		has_action( 'action_test_has_filter_doesnt_reset_wp_filter', '_function_that_doesnt_exist' );
 
 		$filters = current( $wp_filter['action_test_has_filter_doesnt_reset_wp_filter'] );
-	 	$the_ = current( $filters );
-	 	$this->assertEquals( $the_['function'], array( $this, '_action_test_has_filter_doesnt_reset_wp_filter' ) );
+		$the_ = current( $filters );
+		$this->assertEquals( $the_['function'], array( $this, '_action_test_has_filter_doesnt_reset_wp_filter' ) );
 	 }
 }
