public static function isMatch($path, $pattern)
    {
        // validate path
        if (null !== $path && (!is_string($path) || 0 >= mb_strlen($path) || 0 !== mb_strpos($path, '/'))) {
            throw new InvalidArgumentException('invalid path');
        }

        // validate pattern
        if (!is_string($pattern) || 0 >= mb_strlen($pattern) || (0 !== mb_strpos($pattern, '/') && '*' !== $pattern)) {
            throw new InvalidArgumentException('invalid pattern');
        }

        // wildcard match, every path allowed
        if ('*' === $pattern) {
            return [];
        }

        // exact match
        if ($path === $pattern) {
            return [];
        }

        if (false === mb_strpos($pattern, ':')) {
            // no variables defined in pattern, so it is always false...
            return false;
        }

        // replace all occurences of :var with (?P<var>([^_][^/]*))
        $pattern = preg_replace('/:([\w]+)/i', '(?P<${1}>([^_][^/]*))', $pattern);
        if (null === $pattern) {
            throw new LogicException('regular expression for parameter replacement failed');
        }

        // match the path with this regexp
        $pm = preg_match(
            sprintf(
                '#^%s$#',
                $pattern
            ),
            $path,
            $parameters
        );

        if (false === $pm) {
            throw new LogicException('regular expression for path matching failed');
        }

        if (0 === $pm) {
            // request path does not match pattern
            return false;
        }

        $patternParameters = [];
        if (null !== $parameters) {
            foreach ($parameters as $k => $v) {
                // find the name of the parameter in the callback and set it to
                // the value
                if (is_string($k)) {
                    $patternParameters[$k] = $v;
                }
            }
        }

        return $patternParameters;
    }