Skip to content

Instantly share code, notes, and snippets.

@benjamw
Last active April 25, 2026 23:24
Show Gist options
  • Select an option

  • Save benjamw/1690140 to your computer and use it in GitHub Desktop.

Select an option

Save benjamw/1690140 to your computer and use it in GitHub Desktop.
PHP array_filter_recursive function
<?php
/**
* Exactly the same as array_filter except this function
* filters within multidimensional arrays
*
* @param array $array
* @param callable|null $callback optional filter callback function
* @param bool $remove_empty_arrays optional flag removal of empty arrays after filtering
*
* @return array filtered array
*/
function array_filter_recursive(array $array, callable $callback = null, bool $remove_empty_arrays = false): array {
foreach ($array as $key => & $value) { // mind the reference
if (is_array($value)) {
$value = call_user_func_array(__FUNCTION__, array($value, $callback, $remove_empty_arrays));
if ($remove_empty_arrays && ! (bool) $value) {
unset($array[$key]);
}
}
else {
if ( ! is_null($callback) && ! $callback($value)) {
unset($array[$key]);
}
elseif ( ! (bool) $value) {
unset($array[$key]);
}
}
}
unset($value); // kill the reference
return $array;
}
@TMDevel89
Copy link
Copy Markdown

TMDevel89 commented Apr 25, 2026

Version from tiria-fred
Added $mode argument like originaly array_filter and added additional flag to include $array in callback argument list
Usage: array_filter modes and bitwise or ARRAY_FILTER_INCLUDE_ARRAY

/**
 * Recursively filters a multidimensional array.
 *
 * Works like `array_filter()`, but operates on all levels of a multidimensional array.
 * Elements are removed if the optional callback returns `false` or if no callback
 * is provided. Optionally empty sub-arrays can also be removed after filtering.
 *
 * The `$mode` parameter allows you to control what arguments are passed to the callback:
 * - `0` or empty (default): callback receives the value (`$value`) only.
 * - `ARRAY_FILTER_USE_KEY`: callback receives the key (`$key`) only.
 * - `ARRAY_FILTER_USE_BOTH`: callback receives both value and key (`$value, $key`).
 * You can also combine (bitwise OR) the custom flag `ARRAY_FILTER_INCLUDE_ARRAY` to
 * pass the entire array as an additional argument to the callback.
 *
 * @param array         $array               The array to filter recursively.
 * @param callable|null $callback            Optional. Callback function with signature
 *                                           `fn($value, $key, $array): bool` (depending on `$mode`).
 *                                           Keeps elements only if it returns `true`.
 * @param bool          $remove_empty_arrays Optional. Remove empty arrays after filtering.
 * @param int           $mode                Optional. Controls what arguments the callback receives.
 *
 * @return array The filtered array, preserving the original multidimensional structure.
 *
 * @source https://gist.github.com/benjamw/1690140
 */
if (!defined('ARRAY_FILTER_INCLUDE_ARRAY'))
	define('ARRAY_FILTER_INCLUDE_ARRAY', max(array_map(fn($v) => constant("ARRAY_FILTER_USE_$v"), ['KEY','BOTH']))**2);

function array_filter_recursive(array $array, callable $callback = null, bool $remove_empty_arrays = false, int $mode = 0): array {
	foreach ($array as $key => & $value) { // mind the reference
		if (is_array($value)) {
			$args = func_get_args(); // Store all original arguments
			$args[0] = $value; // Replace the first argument for recursion
			$value = (__FUNCTION__)(...$args); // Recursive call
			if ($remove_empty_arrays && !(bool)$value) unset($array[$key]);
		}
		else {
			if (!is_null($callback)) {
				// Map mode to callback argument sets
				$cbArgsMap = [
					ARRAY_FILTER_USE_KEY  => [$key],
					ARRAY_FILTER_USE_BOTH => [$value, $key],
				];

				$cb_args = $cbArgsMap[$mode & ~ARRAY_FILTER_INCLUDE_ARRAY] ?? [$value]; // Default to [$value] if mode not found
				if ((bool)($mode & ARRAY_FILTER_INCLUDE_ARRAY)) // Optionally add the full array
					$cb_args[] = '$array';

				if (!$callback(...$cb_args)) unset($array[$key]);
			}
			elseif (!(bool)$value) {
				unset($array[$key]);
			}
		}
	}
	unset($value); // kill the reference

	return $array;
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment