Construct the internal pattern to test the urls against

@param string $path
@param string $routePattern
@return boolean
@throws \Exception
11138
private function constructInternalRegularExpression($path, $routePattern)
    {
        if (empty($routePattern)) return false;

        $fullRoute = $path;

        if (substr($fullRoute, -1) == '/') {
            $fullRoute = substr($fullRoute, 0, -1);
        }

        $fullRoute .= $routePattern;

        $patternSegments = $this->urlSegmentsFromPath($fullRoute);

        $variableNumber = 0;
        $hasOptional = false;
        $lastSegmentRequired = true;

        foreach ($patternSegments as $pattern) {
            $variableNumber++;

            $isOptionalPattern = $this->stringIsWrapped($pattern, '[', ']');
            $isDynamicPattern = $this->stringIsWrapped($pattern, '{', '}');

            if (!$isDynamicPattern) {
                $validatorClass = new \Slab\Router\Validators\Value(); //@todo findClass

                $validatorClass->setChallenge($pattern);

                if ($isOptionalPattern)
                {
                    // Can't have optional value validators
                    //$validatorClass->setOptional(true);
                }
                else
                {
                    $this->requiredSegments++;
                }

                $this->validations[] = $validatorClass;

                continue;
            }

            if (!$isOptionalPattern && $hasOptional)
            {
                throw new \Exception("Route " . $path . " has required fields after optional fields.");
            }

            list($validatorClass, $variableName) = explode(':', $pattern);

            if ($validatorClass[0] != '\\')
            {
                $validatorClass = ucfirst($validatorClass);

                if (strcasecmp($validatorClass, 'String') == 0)
                {
                    $validatorClass = 'Text';
                }

                $validatorClassName = '\Slab\Router\Validators\\' . $validatorClass;
            }
            else
            {
                $validatorClassName = $validatorClass;
            }

            if (!class_exists($validatorClassName))
            {
                throw new \Exception("Route " . $path . " specified invalid validator class " . $validatorClass);
            }


            $validatorClass = new $validatorClassName;

            if (!$validatorClass instanceof \Slab\Router\Validators\ValidatorInterface)
            {
                throw new \Exception("Validator " . $validatorClass . " does not explicitly implement interface \Slab\Router\Validators\ValidatorInterface");
            }

            $this->validations[] = $validatorClass;

            $this->variableMapping[$variableNumber] = $variableName;

            if (!$isOptionalPattern)
            {
                ++$this->requiredSegments;
            }
        }

        return true;
    }
823978
protected function buildRoute(Route $route)
    {
        if ($route->getBlock()) {
            return $route;
        }

        list($pattern, $params) = $this->parsePlaceholders($route->getPattern());
        return $route->setPatternWithoutReset($pattern)->setParams($params)->setBlock(true);
    }
1158437
public static function compareSlashes($routePattern, $path)
    {
        $patternSeparator = count(explode('/', $routePattern));
        if (preg_match('/\/$/', $routePattern)) {
            $patternSeparator--;
        }
        $routePattern = preg_replace('/\/\{.*\}$/', '', $routePattern);
        $cleanPatternSeparator = count(explode('/', $routePattern));
        if (preg_match('/\/$/', $routePattern)) {
            $cleanPatternSeparator--;
        }
        $path_sep = count(explode('/', $path));
        if (preg_match('/\/$/', $path)) {
            $path_sep--;
        }
        return abs($patternSeparator - $path_sep) < 1 || abs($cleanPatternSeparator - $path_sep) < 1;
    }
1005348
public function findMatch(array $path, $method)
    {
        usort($this->patterns, ['Jivoo\Utilities', 'prioritySorter']);
        foreach ($this->patterns as $pattern) {
            if ($pattern['method'] != 'ANY' and $pattern['method'] != $method) {
                continue;
            }
            $parameters = $this->applyPattern($pattern['pattern'], $path);
            if (isset($parameters)) {
                return $pattern['route']->withParameters($parameters);
            }
        }
        return null;
    }
676963
public function setRoute($route)
        {
            if (null !== $route && (!is_string($route) || strlen($route) < 1)) {
                throw new Exception(
                    'Invalid argument: $route must be a non-empty string or null'
                );
            }

            $this->_route     = $route;
            $this->_hrefCache = null;

            return $this;
        }
136593
public function add($pattern, $paths = null, $method = null)
    {
        return $this->_addRoute($pattern, $paths, $method);
    }
800181
protected function _addRoute($pattern, $paths = null, $method = null)
    {
        $route = new Route($pattern, $paths, $method);
        if ($method !== 'REST' && strpos($pattern, '{') === false) {
            $this->_simple_routes[$method][$pattern] = $route;
        } else {
            $this->_regex_routes[] = $route;
        }

        return $route;
    }
444604
public function route(RouterInterface $router, string $path, string $filter, array &$vars) : bool
    {
        // dispatch if no paths remain in buffer(means last node)
        if (empty($path)){
            if (empty($filter)){
                $event = $this->filters['*'] ?? '';
            }
            else{
                $event = $this->filters[$filter] ?? $this->filters['*'] ?? '';
                if(!$event){
                    foreach($this->filters as $filter => $e){
                        if ($filter === self::FILTER_KEY_DEFAULT){
                            $event = $e;
                        }
                    }
                }
            }
            if (!empty($event)){
                $router->dispatch($vars, $event);
                return true;
            }
        }
        return parent::route($router, $path, $filter, $vars);
    }
54984
protected function filterPath($path, $pattern, array $extensions=[])
    {
        $usePattern = $this->isFilterPattern($pattern);

        if (!$usePattern && !$extensions) {
            return true;
        }

        if ($usePattern && !fnmatch($pattern, $this->basename($path))) {
            return false;
        }

        if ($extensions && !in_array(strtolower($this->extension($path)), $extensions)) {
            return false;
        }

        return true;
    }
1044418
public function route($path)
    {
        $route = $this->configureSlice()->atData($path);

        if (empty($route))
        {
            throw new Exceptions\NotFound('Route `' . $path . '` not found');
        }

        return $route;
    }
1147824
protected function doMatch($pattern, $path, $fullMatch)
    {

        if (($pattern[0] == '/') != ($path[0] == '/')) {
            return false;
        }
        if ('/' == substr($pattern, '-1')) {
            $pattern .= '**';
        }

        $pattDirs = $this->tokenizeToStringArray($pattern);
        $pathDirs = $this->tokenizeToStringArray($path);

        $pattIdxStart = 0;
        $pattIdxEnd = count($pattDirs) - 1;
        $pathIdxStart = 0;
        $pathIdxEnd = count($pathDirs) - 1;

        // Match all elements up to the first **
        while ($pattIdxStart <= $pattIdxEnd and $pathIdxStart <= $pathIdxEnd) {
            $patDir = $pattDirs[$pattIdxStart];
            if ('**' == $patDir) {
                break;
            }
            if (!$this->matchStrings($patDir, $pathDirs[$pathIdxStart])) {
                return false;
            }
            $pattIdxStart++;
            $pathIdxStart++;
        }

        if ($pathIdxStart > $pathIdxEnd) {
            // Path is exhausted, only match if rest of pattern is * or **'s
            if ($pattIdxStart > $pattIdxEnd) {
                return ('/' == substr($pattern, '-1') ? '/' == substr($path, '-1') :
                        !('/' == substr($path, '-1')));
            }
            if (!$fullMatch) {
                return true;
            }
            if ($pattIdxStart == $pattIdxEnd and '*' == $pattDirs[$pattIdxStart] and '/' == substr($path, -1)) {
                return true;
            }
            for ($i = $pattIdxStart; $i <= $pattIdxEnd; $i++) {
                if (!'**' == $pattDirs[$i]) {
                    return false;
                }
            }

            return false;
        } elseif ($pattIdxStart > $pattIdxEnd) {
            // String not exhausted, but pattern is. Failure.
            return false;
        } elseif (!$fullMatch and "**" == $pattDirs[$pattIdxStart]) {
            // Path start definitely matches due to "**" part in pattern.
            return true;
        }

        // up to last '**'
        while ($pattIdxStart <= $pattIdxEnd and $pathIdxStart <= $pathIdxEnd) {
            $patDir = $pattDirs[$pattIdxEnd];
            if ('**' == $patDir) {
                break;
            }
            if (!$this->matchStrings($patDir, $pathDirs[$pathIdxEnd])) {
                return false;
            }
            $pattIdxEnd--;
            $pathIdxEnd--;
        }
        if ($pathIdxStart > $pathIdxEnd) {
            // String is exhausted
            for ($i = $pattIdxStart; $i <= $pattIdxEnd; $i++) {
                if (!('**' == $pattDirs[$i])) {
                    return false;
                }
            }

            return true;
        }

        while ($pattIdxStart != $pattIdxEnd and $pathIdxStart <= $pathIdxEnd) {
            $patIdxTmp = -1;
            for ($i = $pattIdxStart + 1; $i <= $pattIdxEnd; $i++) {
                if ('**' == $pattDirs[$i]) {
                    $patIdxTmp = $i;
                    break;
                }
            }
            if ($patIdxTmp == $pattIdxStart + 1) {
                // '**/**' situation, so skip one
                $pattIdxStart++;
                continue;
            }

            // Find the pattern between padIdxStart & padIdxTmp in str between
            // strIdxStart & strIdxEnd
            $patLength = ($patIdxTmp - $pattIdxStart - 1);
            $strLength = ($pathIdxEnd - $pathIdxStart + 1);
            $foundIdx = -1;

            for ($i = 0; $i <= $strLength - $patLength; $i++) {
                for ($j = 0; $j < $patLength; $j++) {
                    $subPat = $pattDirs[$pattIdxStart + $j + 1];
                    $subStr = $pathDirs[$pathIdxStart + $i + $j];
                    if (!$this->matchStrings($subPat, $subStr)) {
                        continue 2;
                    }
                }
                $foundIdx = $pathIdxStart + $i;
                break;
            }

            if ($foundIdx == -1) {
                return false;
            }

            $pattIdxStart = $patIdxTmp;
            $pathIdxStart = $foundIdx + $patLength;

        }

        for ($i = $pattIdxStart; $i <= $pattIdxEnd; $i++) {
            if (!'**' == $pattDirs[$i]) {
                return false;
            }
        }

        return true;

    }
253943
protected function matchPath($path)
	{
		$continue = true;

		$routePath = $this->properties['path'];

		if($this->properties['requestable'] === false)
			return false;

		if($routePath === false)
			return false;

		if($routePath === '')
		{
			return array(
				'matched'=> ($path === '' ? true : false),
				'parameter'=> array(),
				'continue' => $continue
				);
		}

		// route check
		$segments = explode('/', $routePath);
		$paths = explode('/', $path);

		// initialize states
		$matched = true;
		$pathParams	= array();

		// route segment loop.
		$equal = null;

		$equalSegmentLength = count($segments) == count($paths);

		foreach($segments as $no => $segment)
		{
			// non-pattern based validation
//			if($segment == '' || ($segment[0] != '[' || $segment[strlen($segment) - 1] != ']'))
            if(strpos($segment, ':') === false)
			{
				$equal	= false;

				// need to move this logic outside perhaps.
				if(!$equalSegmentLength)
					$matched = false;

				if(isset($paths[$no]) && $paths[$no] != $segment)
				{
					$matched	= false;
					break;
				}
				else
				{
					$equal	= true;
				}

				continue;
			}

			// pattern based validation
			$pattern	= trim($segment, '[]');
			
			@list($pattern, $segmentParamName) = explode(':', $pattern);

			// no color was passed. thus, could't retrieve second value.
			if(!$segmentParamName)
			{
				$matched	= false;
				break;
			}

			// optional flag
			$isOptional			= $segmentParamName[strlen($segmentParamName)-1] == '?';

			$segmentParamName	= trim($segmentParamName, '?');

			// no data at current uri path segment.
			if(!isset($paths[$no]) || (isset($paths[$no]) && $paths[$no] === ''))
			{
				// but if optional, continue searching without breaking.
				if($isOptional)
				{
					$matched	= true;  
					continue;
				}

				$matched	= false;
				break;
			}

			if($paths[$no] === '' && !$isOptional)
			{
				$matched = false;
				break;
			}

			// pattern based matching
			switch($pattern)
			{
				// match all, so do nothing.
				case '':
					if($paths[$no] == '' && !$isOptional)
					{
						$matched = false;
						break 2;
					}
				break;
				// integer
				case 'i':
					// segment value isn't numeric. OR is cumpulsory.
					if(!is_numeric($paths[$no]) && !$isOptional)
					{
						$continue = false;
						$matched = false;
						break 2;
					}
				break;
				// segments remainder
				case '*':
					$path = explode('/', $path, $no+1);
					$pathParams[$segmentParamName] = array_pop($path);
					$matched = true;
					break 2;
				break;
				// segments remainder into array.
				// to be deprecated.
				case '**':
					// get all the rest of path for param, and explode it so it return as list of segment.
					$explodes = explode('/', $path, $no+1);
					$pathParams[$segmentParamName]	= explode('/', array_pop($explodes));
					$matched		= true;
					break 2; // break the param loop, and set matched directly to true.
				break;
				default:
					// split pattern with 
					$split = explode('|', $pattern);

					if(!in_array($paths[$no], $split))
					{
						$matched = false;
						break 2;
					}
				break;
			}

			if(count($segments) != count($paths))
				$matched = false;

			// set parameter of the current segment
			$pathParams[$segmentParamName]	= $paths[$no];
		
		} // segments loop end

		// build result.
		$result 	= array();

		$result['continue'] = $equal === false ? false : $continue;

		// pattern matched flag.
		$result['matched']	= $matched;

		// pass parameter.
		$result['parameter'] = $pathParams;

		// return matched, parameters founds, and continue flag to continue search
		return $result;
	}
709553
protected function callPatternFilters($route, $request)
	{
		foreach ($this->findPatternFilters($request) as $filter => $parameters)
		{
			$response = $this->callRouteFilter($filter, $parameters, $route, $request);

			if ( ! is_null($response)) return $response;
		}
	}
1149973
private function hasRoute($routePattern, $routeName)
    {
        if (!$routeName) {
            throw new RoutingRuntimeException('Route name is empty!');
        }

        $route = $this->routeManager->findByRoutePattern($routePattern);

        return ((null !== $route) && ($routeName !== $route->getName()));
    }
1298930
public function parse()
    {
        if ($this->isParsed || $this->uriPattern === null || $this->uriPattern === '') {
            return;
        }
        $this->routeParts = [];
        $currentRoutePartIsOptional = false;
        if (substr($this->uriPattern, -1) === '/') {
            throw new InvalidUriPatternException('The URI pattern "' . $this->uriPattern . '" of route "' . $this->getName() . '" ends with a slash, which is not allowed. You can put the trailing slash in brackets to make it optional.', 1234782997);
        }
        if ($this->uriPattern[0] === '/') {
            throw new InvalidUriPatternException('The URI pattern "' . $this->uriPattern . '" of route "' . $this->getName() . '" starts with a slash, which is not allowed.', 1234782983);
        }

        $matches = [];
        preg_match_all(self::PATTERN_EXTRACTROUTEPARTS, $this->uriPattern, $matches, PREG_SET_ORDER);

        /** @var $lastRoutePart RoutePartInterface */
        $lastRoutePart = null;
        foreach ($matches as $match) {
            $routePartType = empty($match['dynamic']) ? self::ROUTEPART_TYPE_STATIC : self::ROUTEPART_TYPE_DYNAMIC;
            $routePartName = $match['content'];
            if (!empty($match['optionalStart'])) {
                if ($lastRoutePart !== null && $lastRoutePart->isOptional()) {
                    throw new InvalidUriPatternException('the URI pattern "' . $this->uriPattern . '" of route "' . $this->getName() . '" contains successive optional Route sections, which is not allowed.', 1234562050);
                }
                $currentRoutePartIsOptional = true;
            }
            $routePart = null;
            switch ($routePartType) {
                case self::ROUTEPART_TYPE_DYNAMIC:
                    if ($lastRoutePart instanceof DynamicRoutePartInterface) {
                        throw new InvalidUriPatternException('the URI pattern "' . $this->uriPattern . '" of route "' . $this->getName() . '" contains successive Dynamic Route Parts, which is not allowed.', 1218446975);
                    }
                    if (isset($this->routePartsConfiguration[$routePartName]['handler'])) {
                        $routePart = $this->objectManager->get($this->routePartsConfiguration[$routePartName]['handler']);
                        if (!$routePart instanceof DynamicRoutePartInterface) {
                            throw new InvalidRoutePartHandlerException(sprintf('routePart handlers must implement "%s" in route "%s"', DynamicRoutePartInterface::class, $this->getName()), 1218480972);
                        }
                    } elseif (isset($this->routePartsConfiguration[$routePartName]['objectType'])) {
                        $routePart = new IdentityRoutePart();
                        $routePart->setObjectType($this->routePartsConfiguration[$routePartName]['objectType']);
                        if (isset($this->routePartsConfiguration[$routePartName]['uriPattern'])) {
                            $routePart->setUriPattern($this->routePartsConfiguration[$routePartName]['uriPattern']);
                        }
                    } else {
                        $routePart = new DynamicRoutePart();
                    }
                    $routePartDefaultValue = ObjectAccess::getPropertyPath($this->defaults, $routePartName);
                    if ($routePartDefaultValue !== null) {
                        $routePart->setDefaultValue($routePartDefaultValue);
                    }
                    break;
                case self::ROUTEPART_TYPE_STATIC:
                    $routePart = new StaticRoutePart();
                    if ($lastRoutePart !== null && $lastRoutePart instanceof DynamicRoutePartInterface) {
                        /** @var DynamicRoutePartInterface $lastRoutePart */
                        $lastRoutePart->setSplitString($routePartName);
                    }
            }
            $routePart->setName($routePartName);
            if ($currentRoutePartIsOptional) {
                $routePart->setOptional(true);
                if ($routePart instanceof DynamicRoutePartInterface && !$routePart->hasDefaultValue()) {
                    throw new InvalidRouteSetupException(sprintf('There is no default value specified for the optional route part "{%s}" of route "%s", but all dynamic optional route parts need a default.', $routePartName, $this->getName()), 1477140679);
                }
            }
            $routePart->setLowerCase($this->lowerCase);
            if (isset($this->routePartsConfiguration[$routePartName]['options'])) {
                $routePart->setOptions($this->routePartsConfiguration[$routePartName]['options']);
            }
            if (isset($this->routePartsConfiguration[$routePartName]['toLowerCase'])) {
                $routePart->setLowerCase($this->routePartsConfiguration[$routePartName]['toLowerCase']);
            }

            $this->routeParts[] = $routePart;
            if (!empty($match['optionalEnd'])) {
                if (!$currentRoutePartIsOptional) {
                    throw new InvalidUriPatternException('The URI pattern "' . $this->uriPattern . '" of route "' . $this->getName() . '" contains an unopened optional section.', 1234564495);
                }
                $currentRoutePartIsOptional = false;
            }
            $lastRoutePart = $routePart;
        }
        if ($currentRoutePartIsOptional) {
            throw new InvalidUriPatternException('The URI pattern "' . $this->uriPattern . '" of route "' . $this->getName() . '" contains an unterminated optional section.', 1234563922);
        }
        $this->isParsed = true;
    }
1966
public function setKeyPattern($keyPattern)
    {
        $keyPattern = (string)$keyPattern;
        if ($this->keyPattern !== $keyPattern) {
            // validate pattern
            if ($keyPattern !== '') {
                ErrorHandler::start(E_WARNING);
                $result = preg_match($keyPattern, '');
                $error  = ErrorHandler::stop();
                if ($result === false) {
                    throw new Exception\InvalidArgumentException(
                        sprintf(
                            'Invalid pattern "%s"%s',
                            $keyPattern,
                            ($error ? ': ' . $error->getMessage() : '')
                        ),
                        0,
                        $error
                    );
                }
            }

            $this->keyPattern = $keyPattern;
        }

        return $this;
    }
937613
private function matchSequentiallyForMethods(array $methods, string $path)
    {
        if ($result = $this->matchKeyValue($path, $methods)) {
            return $result;
        }

        foreach ($methods as $currentMethod) {
            foreach ($this->routes[$currentMethod] ?? [] as $handler => $handlerData) {
                foreach ($handlerData as list($patternRoute, $extraParams)) {
                    /** @var PatternRoute $patternRoute */
                    /** @var array $extraParams */
                    if ($patternRoute->match($path, $queryParams)) {
                        return new InternalRoute($handler, \array_replace($extraParams, $queryParams));
                    }
                }
            }
        }

        return null;
    }