{"record":{"id":"ca9d6f4a30bd117f","repo":"phalcon/cphalcon","slug":"the-sort-callback-must-be-callable-or-null","errorCode":null,"errorMessage":"The sort callback must be callable or null","messagePattern":"The sort callback must be callable or null","errorType":"validation","errorClass":"InvalidArgumentException","httpStatus":null,"severity":"error","filePath":"phalcon/Support/Collection.zep","lineNumber":551,"sourceCode":"    /**\n     * Returns a new collection sorted by value. Keys are preserved. When a\n     * callback is supplied, `uasort` is used. Without a callback, the\n     * comparison direction is controlled by the `$order` argument\n     * (`SORT_ASC` or `SORT_DESC`).\n     *\n     * @phpstan-return static<T>\n     *\n     * @param callable|null $callback\n     */\n    public function sort(var callback = null, int order = 4) -> <static>\n    {\n        var result;\n\n        let result = this->data;\n\n        if (null !== callback) {\n            if unlikely true !== is_callable(callback) {\n                throw new InvalidArgumentException(\n                    \"The sort callback must be callable or null\"\n                );\n            }\n\n            uasort(result, callback);\n        } elseif (order === SORT_DESC) {\n            arsort(result);\n        } else {\n            asort(result);\n        }\n\n        return this->cloneEmpty(result);\n    }\n\n    /**\n     * Returns the object in an array format\n     *\n     * @phpstan-return array<array-key, T>","sourceCodeStart":533,"sourceCodeEnd":569,"githubUrl":"https://github.com/phalcon/cphalcon/blob/b7419de9cd0a8a3f48441ead84c9f8415d463e25/phalcon/Support/Collection.zep#L533-L569","documentation":"Support\\Collection::sort(var callback = null, int order = 4) accepts null (built-in asort/arsort by order) or a callable passed to uasort. A non-null value that fails is_callable() throws InvalidArgumentException('The sort callback must be callable or null') before any sorting happens.","triggerScenarios":"A string function name that does not exist (typo like 'strcomp'); wrong method-reference syntax such as $this->compare instead of [$this, 'compare']; 'self::compare' as a plain string where the class is namespaced without a leading backslash; a callback name from config/request input that was never defined; the comparison method was renamed or made private during refactor.","commonSituations":"Dynamic comparator names driven by configuration; refactors renaming compare methods; closures transported via serialization losing callability; first-class callable syntax confusion producing a string instead of a Closure.","solutions":["Use a closure or canonical array syntax: $collection->sort(fn($a, $b) => $a <=> $b) or $collection->sort([$this, 'compareByName'])","Validate dynamic callbacks before use: if ($cb !== null && !is_callable($cb)) { throw new InvalidArgumentException(...); }","After renames, grep for string callback references; ensure the method is accessible (public)"],"exampleFix":"// before\n$collection->sort([$this, 'compareByName']); // method renamed -> not callable\n\n// after\n$collection->sort(fn(array $a, array $b): int => $a['name'] <=> $b['name']);","handlingStrategy":"type-guard","validationCode":"if ($callback !== null && !is_callable($callback)) {\n    throw new InvalidArgumentException('Provided sort callback is not callable');\n}\n$sorted = $collection->sort($callback);","typeGuard":"function isSortCallback(mixed $callback): bool\n{\n    return $callback === null || is_callable($callback);\n}","tryCatchPattern":null,"preventionTips":["Prefer closures or [$object, 'method'] arrays over string callables","Validate callback names coming from config or request input with is_callable() before use","After renaming comparator methods, grep for string references and fix visibility"],"tags":["php","phalcon","collection","sorting","callback","validation"],"backgroundTag":"invalid-callback","analyzedSha":"b7419de9cd0a8a3f48441ead84c9f8415d463e25","analyzedAt":"2026-08-21T06:21:18.811Z","schemaVersion":2},"datasetVersion":"2026-08-21T11:28:35.574Z"}