Builds the url pattern using the `rest.url` definition from method phpDoc.

@param string $pattern    Defined `rest.url` pattern.
@param array  $parameters List of method parameters.

@return string
@throws RestException
519990
private function buildUrlPatternFromPattern($pattern, array $parameters)
    {
        foreach ($parameters as $p) {
            $pattern = str_replace('{' . $p->name . '}', $p->matchPattern, $pattern, $rcount);
            if ($rcount < 1) {
                throw new RestException(sprintf('Missing parameter "%s" for "%s" method in the rest.url definition.',
                    $p->name, $this->method->getName()));
            }
        }

        return rtrim($pattern, '/') . '/';
    }
856730
private function buildUrl($method, $params)
    {
        $url = $this->address.$method;

        return $url."?".http_build_query($params);
    }
1255882
protected function parsePattern($pattern)
    {
        $currentPos      = 0;
        $length          = strlen($pattern);
        $tokens          = [];
        $level           = 0;
        $quotedDelimiter = preg_quote($this->delimiter);

        while ($currentPos < $length) {
            preg_match('(\G(?P<literal>[^:{\[\]]*)(?P<token>[:\[\]]|$))', $pattern, $matches, 0, $currentPos);

            $currentPos += strlen($matches[0]);

            if (!empty($matches['literal'])) {
                $tokens[] = ['literal', $matches['literal']];
            }

            if ($matches['token'] === ':') {
                if (!preg_match(
                    '(\G(?P<name>[^:' . $quotedDelimiter . '{\[\]]+)(?:{(?P<delimiters>[^}]+)})?:?)',
                    $pattern,
                    $matches,
                    0,
                    $currentPos
                )) {
                    throw new Exception\RuntimeException('Found empty parameter name');
                }

                $tokens[] = [
                    'parameter',
                    $matches['name'],
                    isset($matches['delimiters']) ? $matches['delimiters'] : null
                ];

                $currentPos += strlen($matches[0]);
            } elseif ($matches['token'] === '[') {
                $tokens[] = ['optional-start'];
                $level++;
            } elseif ($matches['token'] === ']') {
                $tokens[] = ['optional-end'];
                $level--;

                if ($level < 0) {
                    throw new Exception\RuntimeException('Found closing bracket without matching opening bracket');
                }
            } else {
                break;
            }
        }

        if ($level > 0) {
            throw new Exception\RuntimeException('Found unbalanced brackets');
        }

        return $tokens;
    }
321581
public static function url(string $name, ?array $parameters = [])
  {
    $url = null;

    foreach (Swish::$routes as $route) {
      if ($route['name'] == $name) {
        $url = $route['pattern'];

        if ($route['required'] > 0 && ($route['required'] == count($parameters))) {
          foreach ($parameters as $name => $parameter) {
            $url = str_replace('{' . $name . '}', $parameter, $url);
          }
        }
      }
    }

    if (str_contains($url, ['{', '}'])) $url = null;

    return $url;
  }
520047
static function compilePattern($pattern, array $options = array())
    {

        $len = strlen($pattern);
        /**
         * contains:
         *
         *   array( 'text', $text ),
         *   array( 'variable', $match[0][0][0], $regexp, $var);
         *
         */
        $tokens = array();
        $variables = array();
        $pos = 0;

        /**
         *  the path like:
         *
         *      /blog/to/:year/:month
         *
         *  will be separated like:
         *      
         *      [
         *          '/blog/to',  (text token)
         *          '/:year',    (reg exp token)
         *          '/:month',   (reg exp token)
         *      ]
         */
        $matches = self::splitTokens($pattern);


        // build tokens
        foreach ($matches as $matchIdx => $match) {
            // match[0][1] // matched position for pattern.

            /*
             * Split tokens from abstract pattern
             * to rebuild regexp pattern.
             */
            if ($text = substr($pattern, $pos, $match[0][1] - $pos)) {
                $tokens[] = array( self::TOKEN_TYPE_TEXT, $text);
            }

            // the first char from pattern (which is the seperater)
            // and the first char from the next match pattern
            $seps = array($pattern[$pos]);


            // collect the separator tokens from the next matched pattern
            if (isset($matches[$matchIdx + 1])) {
                $nextMatch = $matches[$matchIdx + 1];

                // check if the next pattern is an optional pattern
                if ($nextMatch[0][0][0] === '(') {

                    $r = self::compilePattern($nextMatch[2][0] ,array());
                    if (isset($r['tokens'][0][1])) {
                        $seps[] = $r['tokens'][0][1][0];
                    }

                } else if ($nextMatch[0][0][0] === ':') { // variable token

                } else {
                    $seps[] = $nextMatch[0][0][0];
                }
            }


            $pos = $match[0][1] + strlen($match[0][0]);


            // generate optional pattern recursively
            if ($match[0][0][0] == '(' ){
                $optional = $match[2][0];
                $subroute = self::compilePattern($optional ,array(
                    'default'   => isset($options['default']) ? $options['default'] : null,
                    'require'   => isset($options['require']) ? $options['require'] : null,
                    'variables' => isset($options['variables']) ? $options['variables'] : null,
                ));

                $tokens[] = array( 
                    self::TOKEN_TYPE_OPTIONAL,
                    $optional[0],
                    $subroute['regex'],
                );
                foreach( $subroute['variables'] as $var ) {
                    $variables[] = $var;
                }
            } else {
                // generate a variable token 
                $varName = $match[1][0];

                // if we defined a pattern for this variable, we should use the given pattern..
                if ( isset( $options['require'][$varName] ) && $req = $options['require'][$varName]) {
                    $regexp = $req;
                } else {
                    if ($pos !== $len) {
                        $seps[] = $pattern[$pos];
                    }
                    // use the default pattern (which is based on the separater charactors we got)
                    $regexp = sprintf('[^%s]+', preg_quote(implode('', array_unique($seps)), '#'));
                }

                // append token item
                $tokens[] = array(self::TOKEN_TYPE_VARIABLE, 
                    $match[0][0][0], 
                    $regexp, 
                    $varName);

                // append variable name
                $variables[] = $varName;
            }
        }

        if ($pos < $len) {
            $tokens[] = array(self::TOKEN_TYPE_TEXT, substr($pattern, $pos));
        }

        // find the first optional token
        $firstOptional = INF;
        for ($i = count($tokens) - 1; $i >= 0; $i--) {
            if ( self::TOKEN_TYPE_VARIABLE === $tokens[$i][0] 
                && isset($options['default'][ $tokens[$i][3] ]) )
            {
                $firstOptional = $i;
            } 
            else 
            {
                break;
            }
        }

        // compute the matching regexp
        $regex = '';

        // indentation level
        $indent = 1;


        // token item structure:
        //   [0] => token type,
        //   [1] => separator
        //   [2] => pattern
        //   [3] => name, 

        // first optional token and only one token.
        if (1 === count($tokens) && 0 === $firstOptional) {
            $token = $tokens[0];
            ++$indent;

            // output regexp with separator and
            $regex .= str_repeat(' ', $indent * 4) . sprintf("%s(?:\n", preg_quote($token[1], '#'));

            // regular expression with place holder name. (?P<name>pattern)
            $regex .= str_repeat(' ', $indent * 4) . sprintf("(?P<%s>%s)\n", $token[3], $token[2]);

        } else {
            foreach ($tokens as $i => $token) {

                switch ( $token[0] ) {
                case self::TOKEN_TYPE_TEXT:
                    $regex .= str_repeat(' ', $indent * 4) . preg_quote($token[1], '#')."\n";
                    break;
                case self::TOKEN_TYPE_OPTIONAL:
                    // the question mark is for optional, the optional item may contains multiple tokens and patterns
                    $regex .= str_repeat(' ', $indent * 4) . "(?:\n" . $token[2] . str_repeat(' ', $indent * 4) . ")?\n";
                    break;
                default:
                    // append new pattern group for the optional pattern
                    if ($i >= $firstOptional) {
                        $regex .= str_repeat(' ', $indent * 4) . "(?:\n";
                        ++$indent;
                    }
                    $regex .= str_repeat(' ', $indent * 4). sprintf("%s(?P<%s>%s)\n", preg_quote($token[1], '#'), $token[3], $token[2]);
                    break;
                }
            }
        }

        // close groups
        while (--$indent) {
            $regex .= str_repeat(' ', $indent * 4).")?\n";
        }

        // save variables
        $options['variables'] = $variables;
        $options['regex'] = $regex;
        $options['tokens'] = $tokens;
        return $options;
    }
514504
private function addToUrl($parameters)
    {
        /** @var AddToUrlEvent $urlAfter */
        $urlAfter = $this->eventDispatcher->dispatch(ContaoEvents::BACKEND_ADD_TO_URL, new AddToUrlEvent($parameters));

        return $urlAfter->getUrl();
    }
821092
public function actionIndex(string $pattern): Response
    {
        /** @var Response $response */
        $response = Craft::$app->getResponse();
        $jsonOptions = null;
        $pretty = false;
        $cache = false;
        $statusCode = 200;
        $statusText = null;

        try {
            $plugin = Plugin::getInstance();
            $config = $plugin->getEndpoint($pattern);
            $request = Craft::$app->getRequest();
            $siteId = Craft::$app->getSites()->getCurrentSite()->id;

            if (is_callable($config)) {
                $params = Craft::$app->getUrlManager()->getRouteParams();
                $config = $this->_callWithParams($config, $params);
            }

            if (is_array($config)) {
                // Merge in the defaults
                $config = array_merge($plugin->getDefaultResourceAdapterConfig(), $config);
            }

            // Before anything else, check the cache
            $cache = ArrayHelper::remove($config, 'cache', false);

            if ($cache) {
                $cacheKey = 'elementapi:'.$siteId.':'.$request->getPathInfo().':'.$request->getQueryStringWithoutPath();
                $cacheService = Craft::$app->getCache();

                if (($cachedContent = $cacheService->get($cacheKey)) !== false) {
                    // Set the JSON headers
                    (new JsonResponseFormatter())->format($response);

                    // Set the cached JSON on the response and return
                    $response->format = Response::FORMAT_RAW;
                    $response->content = $cachedContent;
                    return $response;
                }
            }

            // Extract config settings that aren't meant for createResource()
            $serializer = ArrayHelper::remove($config, 'serializer');
            $jsonOptions = ArrayHelper::remove($config, 'jsonOptions', JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
            $pretty = ArrayHelper::remove($config, 'pretty', false);
            $includes = ArrayHelper::remove($config, 'includes', []);
            $excludes = ArrayHelper::remove($config, 'excludes', []);

            // Generate all transforms immediately
            Craft::$app->getConfig()->getGeneral()->generateTransformsBeforePageLoad = true;

            // Get the data resource
            try {
                $resource = $plugin->createResource($config);
            } catch (\Throwable $e) {
                throw new NotFoundHttpException($e->getMessage() ?: Craft::t('element-api', 'Resource not found'), 0, $e);
            }

            // Load Fractal
            $fractal = new Manager();

            // Serialize the data
            if (!$serializer instanceof SerializerAbstract) {
                switch ($serializer) {
                    case 'dataArray':
                        $serializer = new DataArraySerializer();
                        break;
                    case 'jsonApi':
                        $serializer = new JsonApiSerializer();
                        break;
                    case 'jsonFeed':
                        $serializer = new JsonFeedV1Serializer();
                        break;
                    default:
                        $serializer = new ArraySerializer();
                }
            }

            $fractal->setSerializer($serializer);

            // Parse includes/excludes
            $fractal->parseIncludes($includes);
            $fractal->parseExcludes($excludes);

            $data = $fractal->createData($resource);

            // Fire a 'beforeSendData' event
            if ($this->hasEventHandlers(self::EVENT_BEFORE_SEND_DATA)) {
                $this->trigger(self::EVENT_BEFORE_SEND_DATA, new DataEvent([
                    'data' => $data,
                ]));
            }

            $data = $data->toArray();
        } catch (\Throwable $e) {
            $data = [
                'error' => [
                    'code' => $e instanceof HttpException ? $e->statusCode : $e->getCode(),
                    'message' => $e->getMessage(),
                ]
            ];
            $statusCode = $e instanceof HttpException ? $e->statusCode : 500;
            $statusText = $e->getMessage();
        }

        // Create a JSON response formatter with custom options
        $formatter = new JsonResponseFormatter([
            'encodeOptions' => $jsonOptions,
            'prettyPrint' => $pretty,
        ]);

        // Manually format the response ahead of time, so we can access and cache the JSON
        $response->data = $data;
        $formatter->format($response);
        $response->data = null;
        $response->format = Response::FORMAT_RAW;

        // Cache it?
        if ($cache) {
            if ($cache !== true) {
                $expire = ConfigHelper::durationInSeconds($cache);
            } else {
                $expire = null;
            }
            /** @noinspection PhpUndefinedVariableInspection */
            $cacheService->set($cacheKey, $response->content, $expire);
        }

        // Don't double-encode the data
        $response->format = Response::FORMAT_RAW;
        $response->setStatusCode($statusCode, $statusText);
        return $response;
    }
351064
public function get(string $pattern, callable $callback)
    {
        if ($this->requestMethod === "GET") {
            $this->mapRoute($pattern, $callback);
        }
    }
1063185
protected function build_rest_url($url, $params = array()) {
        $base = '';
        if (substr($this->_url, 0, 4) !== 'http') {
            $base .= 'http://';
        }

        $base .= $this->_url;
        if ($this->_port != 80) {
            $base .= ':' . $this->_port;
        }

        $rurl = new \Rapid\URL($base . '/api/' . $url, $params);
        return $rurl->out();
    }
500021
protected function splitRoutePattern($pattern)
    {
        if (strpos($pattern, ' ') !== false && preg_match_all('/\s+\+(\w+)\b|\s+\-(\w+)\b/', $pattern, $matches)) {
            list($path) = preg_split('/\s+/', $pattern, 2);
            $inc = isset($matches[1]) ? array_filter($matches[1]) : [];
            $excl = isset($matches[2]) ? array_filter($matches[2]) : [];
        } else {
            $path = $pattern;
            $inc = [];
            $excl = [];
        }
        
        return [$path, $inc, $excl];
    }
375024
protected function assertPattern($pattern)
    {
        if (strlen($pattern) == 0) return null;

        $pattern = '/^'. str_replace('/', '\\/', $pattern). '$/';
        if (@preg_match($pattern, null) === false) {
            throw new DefinitionException("invalid regular expression pattern");
        }

        return $pattern;
    }
827184
final public function buildUrl(string $url, array $parameters = []) : string
    {
        if (! $this->builder) {
            throw new \RuntimeException('No URL builder has been registered');
        }

        return $this->builder->buildUrl($url, $parameters);
    }
615852
protected function getRegExpForPattern(string $pattern) : string
    {
        $patternReplaced = str_replace(['*', '?'], ["\nA\n", "\nQ\n"], $pattern);
        $patternReplaced = preg_quote($patternReplaced, '/');
        $patternReplaced = str_replace(["\nA\n", "\nQ\n"], ['.*', '.'], $patternReplaced);

        return '/^' . $patternReplaced . '$/';
    }
1003768
protected function createRoute($methods, $pattern, $callable)
	{
		$route = new Route($methods, $pattern, $callable, $this->routeGroups, $this->routeCounter);
		
		return $route;
	}
1248908
public function get(string $pattern, array $param): void
    {
        $this->call('get', $pattern, $param[0], $param[1], $param);
    }
880069
private function buildUrlByMethodName($method, array $parameters)
    {
        $url = $this
            ->apiUrls
            ->$method();

        return $this->host
            ? $this->buildUrlWithHost(
                $url,
                $parameters
            )
            : $this->buildUrlWithoutHost(
                $url,
                $parameters
            );
    }
1116981
public function resource($rule, $route = '', array $option = [], array $pattern = [])
    {
        return (new Resource($this, $this->group, $rule, $route, $option, $pattern, $this->rest))
            ->lazy($this->lazy);
    }