{"record":{"id":"b8a05398b6304c7b","repo":"symfony/finder","slug":"invalid-php-callback","errorCode":null,"errorMessage":"Invalid PHP callback.","messagePattern":"Invalid PHP callback\\.","errorType":"validation","errorClass":"InvalidArgumentException","httpStatus":null,"severity":"error","filePath":"Iterator/CustomFilterIterator.php","lineNumber":38,"sourceCode":" * @author Fabien Potencier <fabien@symfony.com>\n *\n * @extends \\FilterIterator<string, \\SplFileInfo>\n */\nclass CustomFilterIterator extends \\FilterIterator\n{\n    private array $filters = [];\n\n    /**\n     * @param \\Iterator<string, \\SplFileInfo> $iterator The Iterator to filter\n     * @param callable[]                      $filters  An array of PHP callbacks\n     *\n     * @throws \\InvalidArgumentException\n     */\n    public function __construct(\\Iterator $iterator, array $filters)\n    {\n        foreach ($filters as $filter) {\n            if (!\\is_callable($filter)) {\n                throw new \\InvalidArgumentException('Invalid PHP callback.');\n            }\n        }\n        $this->filters = $filters;\n\n        parent::__construct($iterator);\n    }\n\n    /**\n     * Filters the iterator values.\n     */\n    public function accept(): bool\n    {\n        $fileinfo = $this->current();\n\n        foreach ($this->filters as $filter) {\n            if (false === $filter($fileinfo)) {\n                return false;\n            }","sourceCodeStart":20,"sourceCodeEnd":56,"githubUrl":"https://github.com/symfony/finder/blob/4d6c057bfd67c5a93e8025c85ca9364cba7e260a/Iterator/CustomFilterIterator.php#L20-L56","documentation":"CustomFilterIterator requires every entry of the $filters array to be a PHP callable; if any element is not callable it throws InvalidArgumentException('Invalid PHP callback.') before filtering begins. The library needs real callbacks to apply the custom acceptance logic.","triggerScenarios":"new CustomFilterIterator($iterator, ['strlen', 42]) or Finder::filter([null]) — passing an array containing a non-callable, a method string for a nonexistent method used as a single string, or an object without __invoke.","commonSituations":"Config-driven filters read as strings/arrays that aren't callables, typos in 'Class::method' strings, passing [object, 'method'] arrays where the method doesn't exist so is_callable fails.","solutions":["Ensure each filter is callable: closure, 'function' name, ['Class','method'] / 'Class::method' existing, or invokable object","Validate with is_callable($filter) before constructing","If filters come from config, map/whitelist them to real PHP callables"],"exampleFix":"// before\n$finder->filter(['nonexistent_function']);\n// after\n$finder->filter([static fn (\\SplFileInfo $f) => $f->getExtension() === 'php']);","handlingStrategy":"type-guard","validationCode":"foreach ($filters as $f) { if (!is_callable($f)) { throw new \\InvalidArgumentException('Filter is not callable'); } }","typeGuard":"function allCallable(array $filters): bool { return array_all($filters, fn ($f) => is_callable($f)); }","tryCatchPattern":"try { $finder->filter($filters); } catch (\\InvalidArgumentException $e) { /* handle non-callable filter */ }","preventionTips":["Always wrap filters as closures when the source is config or user input","Verify 'Class::method' strings exist (method_exists) before passing","Keep exclusion/filter arrays flat and typed at the boundary"],"tags":["callbacks","invalid-argument","finder"],"backgroundTag":"invalid-argument","analyzedSha":"4d6c057bfd67c5a93e8025c85ca9364cba7e260a","analyzedAt":"2026-09-13T07:49:33.433Z","contentChangedAt":"2026-09-13T07:49:33.433Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}