phalcon/cphalcon · error · Phalcon\Cli\Router\Exceptions\BeforeMatchNotCallable

Before-Match callback is not callable in matched route '{pat

Error message

Before-Match callback is not callable in matched route '{pattern}'

What it means

Defensive recheck at match time: once a route matches and has a beforeMatch callback, the router runs is_callable() on it before invoking it with (arguments, route, router). Route::beforeMatch() already rejects non-callables at registration, so this variant fires when the callback becomes non-callable after the route was built.

Source

Thrown at phalcon/Cli/Router.zep:265

                if memstr(pattern, "^") {
                    let routeFound = preg_match(pattern, arguments, matches);
                } else {
                    let routeFound = pattern == arguments;
                }

                /**
                 * Check for beforeMatch conditions
                 */
                if routeFound {
                    let beforeMatch = route->getBeforeMatch();

                    if beforeMatch !== null {
                        /**
                         * Check first if the callback is callable
                         */
                        if unlikely !is_callable(beforeMatch) {
                            throw new BeforeMatchNotCallable(route->getPattern());
                        }

                        /**
                         * Check first if the callback is callable
                         */
                        let routeFound = call_user_func_array(
                            beforeMatch,
                            [
                                arguments,
                                route,
                                this
                            ]
                        );
                    }
                }

                if routeFound {
                    /**

View on GitHub (pinned to b7419de9cd)

Solutions

  1. Rebuild routes on each run instead of serializing them with beforeMatch closures
  2. Ensure the callback target (function name or [object, method] pair) still exists at match time
  3. If subclassing Route, keep getBeforeMatch() returning a valid callable or null

Example fix

// before (cached/serialized routes)
$routes = unserialize($cache->get('cli-routes'));
$router = new Router();
// beforeMatch closures were lost on serialize -> match-time throw

// after
$router = new Router();
$router->add('/backup', ['task' => 'backup'])
       ->beforeMatch(function ($args, $route, $router) {
           return php_sapi_name() === 'cli';
       });
Defensive patterns

Strategy: validation

Validate before calling

// Before matching, assert every route's beforeMatch survived registration/cache
foreach ($router->getRoutes() as $route) {
    $beforeMatch = $route->getBeforeMatch();
    if ($beforeMatch !== null && !is_callable($beforeMatch)) {
        throw new RuntimeException(
            'Route ' . $route->getPattern() . ' has a non-callable beforeMatch'
        );
    }
}
$router->handle($_SERVER['argv'] ?? null);

Try / catch

try {
    $router->handle($arguments);
} catch (\Phalcon\Cli\Router\Exception\BeforeMatchNotCallable $e) {
    // Callback degraded after registration (cache/serialization/subclass).
    // Rebuild the route table without the cached beforeMatch and retry.
    rebuildRoutes($router);
    $router->handle($arguments);
}

Prevention

When it happens

Trigger: A custom Route subclass that sets the beforeMatch property directly or overrides getBeforeMatch(); routes restored from cache/serialization where a closure was lost to a string or the referenced method no longer exists.

Common situations: Caching or serializing route objects with closures (closures do not survive serialization); renaming/removing the referenced function or method after routes were registered; relying on __call magic for which is_callable returns false.

Related errors


AI-assisted analysis of phalcon/cphalcon@b7419de9cd (2026-08-21). Data as JSON: /api/errors/66368589bba4f79a. Report an issue: GitHub.