<commit_before><?php

declare(strict_types=1);

namespace Brick\App\Controller\Annotation;

/**
 * Defines a route on a controller.
 *
 * When used on a class, it defines a prefix for the routes of all class methods, and named parameters will be provided
 * as constructor parameters.
 *
 * When used on a function, named parameters will be provided as function parameters.
 *
 * @Annotation
 * @Target({"CLASS", "METHOD"})
 */
class Route
{
    /**
     * @var string
     */
    private $regexp;

    /**
     * The list of parameter names.
     *
     * @var string[]
     */
    private $parameterNames = [];

    /**
     * The list of HTTP methods (e.g. GET or POST) this route is valid for.
     *
     * If this list is empty, all methods are allowed.
     * Note that this property is only valid on controller methods, and ignored on controller classes.
     *
     * @var string[]
     */
    private $methods = [];

    /**
     * @param array $params
     */
    public function __construct(array $params)
    {
        if (! isset($params['value'])) {
            throw new \LogicException('@Route requires a path.');
        }

        $path = $params['value'];

        if (! is_string($path)) {
            throw new \LogicException('@Route path must be a string.');
        }

        if (isset($params['methods'])) {
            if (! is_array($params['methods'])) {
                throw new \LogicException('@Route.methods must be an array of strings.');
            }

            foreach ($params['methods'] as $method) {
                if (! is_string($method)) {
                    throw new \LogicException('@Route.methods must be an array of strings.');
                }
            }

            $this->methods = $params['methods'];
        }

        $this->regexp = preg_replace_callback('/\{([^\}]+)\}|(.+?)/', function(array $matches) : string {
            if (isset($matches[2])) {
                return preg_quote($matches[2], '/');
            }

            $this->parameterNames[] = $matches[1];

            return '([^\/]+)';
        }, $path);
    }

    /**
     * @return string
     */
    public function getRegexp() : string
    {<commit_msg>Support for custom regexp patterns in @Route annotations<commit_after><?php

declare(strict_types=1);

namespace Brick\App\Controller\Annotation;

/**
 * Defines a route on a controller.
 *
 * When used on a class, it defines a prefix for the routes of all class methods, and named parameters will be provided
 * as constructor parameters.
 *
 * When used on a function, named parameters will be provided as function parameters.
 *
 * @Annotation
 * @Target({"CLASS", "METHOD"})
 */
class Route
{
    /**
     * @var string
     */
    private $regexp;

    /**
     * The list of parameter names.
     *
     * @var string[]
     */
    private $parameterNames = [];

    /**
     * A map of parameter name to regexp patterns.
     *
     * The pattern defaults to [^\/]+, but can be overridden here.
     * NO CAPTURING PARENTHESES MUST BE USED INSIDE THESE PATTERNS.
     *
     * @var string[]
     */
    private $patterns = [];

    /**
     * The list of HTTP methods (e.g. GET or POST) this route is valid for.
     *
     * If this list is empty, all methods are allowed.
     * Note that this property is only valid on controller methods, and ignored on controller classes.
     *
     * @var string[]
     */
    private $methods = [];

    /**
     * @param array $params
     */
    public function __construct(array $params)
    {
        if (! isset($params['value'])) {
            throw new \LogicException('@Route requires a path.');
        }

        $path = $params['value'];

        if (! is_string($path)) {
            throw new \LogicException('@Route path must be a string.');
        }

        if (isset($params['methods'])) {
            if (! is_array($params['methods'])) {
                throw new \LogicException('@Route.methods must be an array of strings.');
            }

            foreach ($params['methods'] as $method) {
                if (! is_string($method)) {
                    throw new \LogicException('@Route.methods must only contain strings.');
                }
            }

            $this->methods = $params['methods'];
        }

        if (isset($params['patterns'])) {
            if (! is_array($params['patterns'])) {
                throw new \LogicException('@Route.patterns must be an array of strings.');
            }

            foreach ($params['patterns'] as $pattern) {
                if (! is_string($pattern)) {
                    throw new \LogicException('@Route.patterns must only contain strings.');
                }
            }

            $this->patterns = $params['patterns'];
        }

        $this->regexp = preg_replace_callback('/\{([^\}]+)\}|(.+?)/', function(array $matches) : string {
            if (isset($matches[2])) {
                return preg_quote($matches[2], '/');
            }

            $parameterName = $matches[1];
            $this->parameterNames[] = $parameterName;

            if (isset($this->patterns[$parameterName])) {
                $pattern = $this->patterns[$parameterName];
            } else {
                $pattern = '[^\/]+';
            }

            return '(' . $pattern. ')';
        }, $path);

        foreach ($this->patterns as $parameterName => $pattern) {
            if (! in_array($parameterName, $this->parameterNames, true)) {
                throw new \LogicException(sprintf('Pattern does not match any parameter: "%s".', $parameterName));
            }
        }
    }

    /**
     * @return string
     */
    public function getRegexp() : string
    {
{'fraction_non_alphanumeric': 0.14075372480280457, 'fraction_numerical': 0.0014022787028921998, 'mean_word_length': 1.8861911987860394, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 15, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4314687', 'n_tokens_mistral': 1572, 'n_tokens_neox': 1524, 'n_words': 459}