src/Route.php
public function resolve(array $parts, string $ext)
    {
        $part = array_shift($parts);

        $sub_part = $part;
        if (!empty($ext) && substr($part, -strlen($ext)) === $ext)
            $sub_part = substr($part, 0, -strlen($ext));

        // Ask child routes to resolve the route
        if (isset($this->children[$sub_part]))
        {
            // When a sub route exists, there's not falling back to this level anymore.
            return $this->children[$sub_part]->resolve($parts, $ext);
            //if (!empty($route))
            //    return $route;
        }

        // No sub-route, so put the part back in place
        if (!empty($part) && $sub_part !== "index")
            array_unshift($parts, $part);
        $route = null;

        // Check if a ext-specific app is available
        if (!empty($ext) && isset($this->apps[$ext]))
            $route = $this->apps[$ext];

        // Check if a generic app is available
        if (empty($route) && isset($this->apps['_']))
            $route = $this->apps['_'];

        if (empty($route) && empty($ext) && !empty($this->apps))
        {
            $route = reset($this->apps);
            $route['ext'] = null;
        }

        if (!empty($route) && !empty($ext))
            $route['ext'] = $ext;

        if (!empty($route) && !isset($route['remainder']))
            $route['remainder'] = $parts;

        // Return the route
        return $route;
    }
public function resolve(array $parts, string $ext)
    {
        $part = array_shift($parts);

        $sub_part = $part;
        if (!empty($ext) && substr($part, -strlen($ext)) === $ext)
            $sub_part = substr($part, 0, -strlen($ext));

        // Ask child routes to resolve the route
        if (isset($this->children[$sub_part]))
        {
            // When a sub route exists, there's not falling back to this level anymore.
            return $this->children[$sub_part]->resolve($parts, $ext);
            //if (!empty($route))
            //    return $route;
        }

        // No sub-route, so put the part back in place
        if (!empty($part) && $sub_part !== "index")
            array_unshift($parts, $part);
        $route = null;

        // Check if a ext-specific app is available
        if (!empty($ext) && isset($this->apps[$ext]))
            $route = $this->apps[$ext];

        // Check if a generic app is available
        if (empty($route) && isset($this->apps['_']))
            $route = $this->apps['_'];

        if (empty($route) && empty($ext) && !empty($this->apps))
        {
            $route = reset($this->apps);
            $route['ext'] = null;
        }

        if (!empty($route) && !empty($ext))
            $route['ext'] = $ext;

        if (!empty($route) && !isset($route['remainder']))
            $route['remainder'] = $parts;

        // Return the route
        return $route;
    }
Find a app for the specified route (or part of it). This method
recursively delegates to the child nodes, until an app is found.

index.php will match all other route endings. If a child node for a
route part exists, this child becomes responsible, and if it does not
resolve, null is returned, which should be interpreted as a 404 - Not
Found.

@param array $parts The route parts to resolve
@param string $ext The file extension that may be removed from the route
@return array The resolved route, or null if none was found.