{"record":{"id":"9d853de3de098319","repo":"symfony/finder","slug":"invalid-php-callback-excludedirectoryfilteriterator","errorCode":null,"errorMessage":"Invalid PHP callback.","messagePattern":"Invalid PHP callback\\.","errorType":"validation","errorClass":"InvalidArgumentException","httpStatus":null,"severity":"error","filePath":"Iterator/ExcludeDirectoryFilterIterator.php","lineNumber":50,"sourceCode":"    private ?string $excludedPattern = null;\n    private ?string $excludedRootPattern = null;\n    /** @var list<callable(SplFileInfo):bool> */\n    private array $pruneFilters = [];\n\n    /**\n     * @param \\Iterator<string, SplFileInfo>          $iterator    The Iterator to filter\n     * @param list<string|callable(SplFileInfo):bool> $directories An array of directories to exclude\n     */\n    public function __construct(\\Iterator $iterator, array $directories)\n    {\n        $this->iterator = $iterator;\n        $this->isRecursive = $iterator instanceof \\RecursiveIterator;\n        $patterns = [];\n        $rootPatterns = [];\n        foreach ($directories as $directory) {\n            if (!\\is_string($directory)) {\n                if (!\\is_callable($directory)) {\n                    throw new \\InvalidArgumentException('Invalid PHP callback.');\n                }\n\n                $this->pruneFilters[] = $directory;\n\n                continue;\n            }\n\n            $directory = rtrim($directory, '/');\n            if (str_starts_with($directory, '/')) {\n                $rootPatterns[] = preg_quote(substr($directory, 1), '#');\n            } elseif (!$this->isRecursive || str_contains($directory, '/')) {\n                $patterns[] = preg_quote($directory, '#');\n            } else {\n                $this->excludedDirs[$directory] = true;\n            }\n        }\n        if ($patterns) {\n            $this->excludedPattern = '#(?:^|/)(?:'.implode('|', $patterns).')(?:/|$)#';","sourceCodeStart":32,"sourceCodeEnd":68,"githubUrl":"https://github.com/symfony/finder/blob/4d6c057bfd67c5a93e8025c85ca9364cba7e260a/Iterator/ExcludeDirectoryFilterIterator.php#L32-L68","documentation":"ExcludeDirectoryFilterIterator treats each entry of $directories as either a directory name string or a prune filter callable; when an entry is neither a string nor callable it throws InvalidArgumentException('Invalid PHP callback.'). This occurs while building the exclusion rules in the constructor.","triggerScenarios":"Finder::exclude([123, ['not-callable-array'], new \\stdClass()]) — passing integers, non-invokable objects, or arrays that aren't valid callables as exclusion entries.","commonSituations":"Config files listing exclusions with numeric or mixed values, dynamic exclusion lists built from unvalidated input, accidentally passing a nested array of names instead of flat strings.","solutions":["Pass only strings (directory names) or valid callables; flatten the array","Coerce non-string values to strings with (string) or validate each with is_string($d) || is_callable($d) beforehand","Fix config deserialization that injects numeric keys or objects into the exclusion list"],"exampleFix":"// before\n$finder->exclude([0 => 'vendor', 'cache' => true]); // 'true' boolean entry\n// after\n$finder->exclude(['vendor', 'cache']);","handlingStrategy":"type-guard","validationCode":"foreach ($dirs as $d) { if (!is_string($d) && !is_callable($d)) { throw new \\InvalidArgumentException('Exclusion must be a string or callable'); } }","typeGuard":"function isValidExclusion(mixed $d): bool { return is_string($d) || is_callable($d); }","tryCatchPattern":"try { $finder->exclude($dirs); } catch (\\InvalidArgumentException $e) { /* handle invalid exclusion entry */ }","preventionTips":["Pass only directory-name strings or callables to Finder::exclude()","Flatten nested arrays and cast scalars to strings before excluding","Validate config-derived exclusion lists with array_filter($dirs, 'is_string')"],"tags":["callbacks","finder","exclusion-filter"],"backgroundTag":"invalid-argument","analyzedSha":"4d6c057bfd67c5a93e8025c85ca9364cba7e260a","analyzedAt":"2026-09-13T07:49:33.433Z","contentChangedAt":"2026-09-13T07:49:33.433Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}