<commit_before><?php

namespace Reroute;

use DomainException;
use ReflectionFunction;
use Psr\Http\Message\ServerRequestInterface;
use Zend\Diactoros\ServerRequest;
use Zend\Diactoros\ServerRequestFactory;
use Zend\Diactoros\Request;
use League\Pipeline\PipelineBuilder;
use League\Pipeline\Pipeline;
use League\Pipeline\StageInterface;

class Router implements StageInterface
{
    /**
     * @var array
     * Array storing defined routes.
     */
    protected $routes = [];

    /**
     * @var Reroute\State
     * Endstate for this route.
     */
    protected $state;

    /**
     * @var Zend\Diactoros\ServerRequest
     * Request object for the current request.
     */
    protected $request;

    /**
    * @var string
     * Host to use for every URL. Defaults to http://localhost
     * Note that this is fine if all URLs are on the same domain anyway, and
     * you're not passing the host name during resolve.
     */
    protected $host = 'http://localhost';

    /**
     * @var string
     * Name of the current endstate.
     */
    protected $name = null;

    /**
     * Constructor. In most cases you won't need to worry about the constructor
     * arguments, but optionally you can pass a path part all routes _must_
     * match (e.g. if Reroute only needs to catch parts of your project).
     *
     * @param string $url The path part _all_ URLs for this router must fall
     *  under in order to match.
     * @param League\Pipeline\Pipeline $pipe Optional pipeline to chain onto.
     * @return void
     */
    public function __construct($url = null, Pipeline $pipe = null)
    {
        $this->request = ServerRequestFactory::fromGlobals();
        $this->url = $this->normalize($url);
        $this->pipeline = new PipelineBuilder;
        if (isset($pipe)) {
            $this->pipeline->add($pipe);
        }
    }

    /**
     * Adds a callable to the pipeline.
     *
     * @param callable $stage Callable stage to add.
     */
    public function pipe(callable $stage)
    {
        $this->pipeline->add($stage);
        return $this;
    }

    /**
     * Setup (part of) a URL for catching. The chain is called on match and
     * before control is delegated.
     *
     * to (sub) routers or `then`
     * calls.
     *
     * @param string|null $url The URL(part) to match for this state. If null,
     *  something randomly invalid is used (useful for defining named states for
     *  error pages).
     * @param callable $callback Optional grouping callback.
     * @return Reroute\Router A new sub-router.
     */
    public function when($url, callable $callback = null)
    {
        if (is_null($url)) {
            $url = '!!!!'.rand(0, 999).microtime();
        } else {
            // Brace style to regex:
            $url = preg_replace('@{([a-z]\w*)}@', "(?'\\1'\w+)", $url);
            // Angular style to regex:
            $url = preg_replace('@:([a-z]\w*)@', "(?'\\1'\w+)", $url);
        }
        $parts = parse_url($this->url);
        $check = parse_url($url);
        if (!isset($check['host'])) {
            $url = $this->url.$url;
        }
        $url = $this->normalize(
            $url,
            isset($parts['scheme']) ? $parts['scheme'] : 'http',
            isset($parts['host']) ? $parts['host'] : 'localhost'
        );
        $url = preg_replace("@(?<!:)/{2,}@", '/', $url);
        if (!isset($this->routes[$url])) {
            $this->routes[$url] = new Router($url, $this->pipeline->build());
        }
        if (isset($callback)) {
            $callback($this->routes[$url]);
        }
        return $this->routes[$url];
    }

    /**
     * If the current request URI ends with this URL, yield the associated
     * state. A state can be anything but will get wrapped in a `State` object.
     *
     * @param string $name The (preferably) unique optional name of this state.
     * @param mixed $state A valid state for the matched URL.
     * @return self The current router, for chaining.
     * @see Reroute\Route::generate
     */
    public function then($name, $state = null)
    {
        if (!isset($state)) {
            $state = $name;
            $name = null;
        }
        $this->name = $name;
        $this->state = new State($name, $state);
        return $this;
    }

    public function process($payload)
    {
        return $this($payload);
    }

    /**
     * Attempt to resolve a Reroute\State associated with a request.
     *
     * @param Psr\Http\Message\ServerRequestInterface $request The request to
     *  handle. Defaults to the current request.
     * @return Reroute\State|null If succesful, the corresponding state is
     *  returned, otherwise null (the implementor should then show a 404 or
     *  something else notifying the user).
     */
    public function __invoke(ServerRequestInterface $request = null)
    {
        if (isset($request)) {
            $this->request = $request;
        }
        if (!isset($url)) {
            $url = $this->request->getUri().'';
        }
        $url = $this->normalize($url);
        $parts = parse_url($url);
        unset($parts['query'], $parts['fragment']);
        $parts += parse_url($this->host);
        $url = http_build_url('', $parts);
        foreach ($this->routes as $match => $router) {
            if (preg_match("@^$match(.*)$@", $url, $matches)) {
                $last = array_pop($matches);
                unset($matches[0]);
                if (!strlen($last)) {
                    $this->pipe(new Stage(
                        function ($request) use ($matches, $router) {
                            return $router->state->__invoke(
                                $matches,
                                $this->request
                            );
                        }
                    ));
                    return $this->pipeline
                        ->build()
                        ->process($this->request);
                } else {
                    return $router($this->request);
                }
            }
        }
        return null;
    }
<commit_msg>even better: check what the status is, if middleware already turned it into a response, just return that<commit_after><?php

namespace Reroute;

use DomainException;
use ReflectionFunction;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Message\ResponseInterface;
use Zend\Diactoros\ServerRequestFactory;
use League\Pipeline\PipelineBuilder;
use League\Pipeline\Pipeline;
use League\Pipeline\StageInterface;

class Router implements StageInterface
{
    /**
     * @var array
     * Array storing defined routes.
     */
    protected $routes = [];

    /**
     * @var Reroute\State
     * Endstate for this route.
     */
    protected $state;

    /**
     * @var Psr\Http\Message\ServerRequestInterface
     * Request object for the current request.
     */
    protected $request;

    /**
    * @var string
     * Host to use for every URL. Defaults to http://localhost
     * Note that this is fine if all URLs are on the same domain anyway, and
     * you're not passing the host name during resolve.
     */
    protected $host = 'http://localhost';

    /**
     * @var string
     * Name of the current endstate.
     */
    protected $name = null;

    /**
     * Constructor. In most cases you won't need to worry about the constructor
     * arguments, but optionally you can pass a path part all routes _must_
     * match (e.g. if Reroute only needs to catch parts of your project).
     *
     * @param string $url The path part _all_ URLs for this router must fall
     *  under in order to match.
     * @param League\Pipeline\Pipeline $pipe Optional pipeline to chain onto.
     * @return void
     */
    public function __construct($url = null, Pipeline $pipe = null)
    {
        $this->request = ServerRequestFactory::fromGlobals();
        $this->url = $this->normalize($url);
        $this->pipeline = new PipelineBuilder;
        if (isset($pipe)) {
            $this->pipeline->add($pipe);
        }
    }

    /**
     * Adds a callable to the pipeline.
     *
     * @param callable $stage Callable stage to add.
     */
    public function pipe(callable $stage)
    {
        $this->pipeline->add($stage);
        return $this;
    }

    /**
     * Setup (part of) a URL for catching. The chain is called on match and
     * before control is delegated.
     *
     * to (sub) routers or `then`
     * calls.
     *
     * @param string|null $url The URL(part) to match for this state. If null,
     *  something randomly invalid is used (useful for defining named states for
     *  error pages).
     * @param callable $callback Optional grouping callback.
     * @return Reroute\Router A new sub-router.
     */
    public function when($url, callable $callback = null)
    {
        if (is_null($url)) {
            $url = '!!!!'.rand(0, 999).microtime();
        } else {
            // Brace style to regex:
            $url = preg_replace('@{([a-z]\w*)}@', "(?'\\1'\w+)", $url);
            // Angular style to regex:
            $url = preg_replace('@:([a-z]\w*)@', "(?'\\1'\w+)", $url);
        }
        $parts = parse_url($this->url);
        $check = parse_url($url);
        if (!isset($check['host'])) {
            $url = $this->url.$url;
        }
        $url = $this->normalize(
            $url,
            isset($parts['scheme']) ? $parts['scheme'] : 'http',
            isset($parts['host']) ? $parts['host'] : 'localhost'
        );
        $url = preg_replace("@(?<!:)/{2,}@", '/', $url);
        if (!isset($this->routes[$url])) {
            $this->routes[$url] = new Router($url, $this->pipeline->build());
        }
        if (isset($callback)) {
            $callback($this->routes[$url]);
        }
        return $this->routes[$url];
    }

    /**
     * If the current request URI ends with this URL, yield the associated
     * state. A state can be anything but will get wrapped in a `State` object.
     *
     * @param string $name The (preferably) unique optional name of this state.
     * @param mixed $state A valid state for the matched URL.
     * @return self The current router, for chaining.
     * @see Reroute\Route::generate
     */
    public function then($name, $state = null)
    {
        if (!isset($state)) {
            $state = $name;
            $name = null;
        }
        $this->name = $name;
        $this->state = new State($name, $state);
        return $this;
    }

    public function process($payload)
    {
        return $this($payload);
    }

    /**
     * Attempt to resolve a Reroute\State associated with a request.
     *
     * @param Psr\Http\Message\ServerRequestInterface $request The request to
     *  handle. Defaults to the current request.
     * @return Reroute\State|null If succesful, the corresponding state is
     *  returned, otherwise null (the implementor should then show a 404 or
     *  something else notifying the user).
     */
    public function __invoke(ServerRequestInterface $request = null)
    {
        if (isset($request)) {
            $this->request = $request;
        }
        if (!isset($url)) {
            $url = $this->request->getUri().'';
        }
        $url = $this->normalize($url);
        $parts = parse_url($url);
        unset($parts['query'], $parts['fragment']);
        $parts += parse_url($this->host);
        $url = http_build_url('', $parts);
        foreach ($this->routes as $match => $router) {
            if (preg_match("@^$match(.*)$@", $url, $matches)) {
                $last = array_pop($matches);
                unset($matches[0]);
                if (!strlen($last)) {
                    $this->pipe(new Stage(
                        function ($request) use ($matches, $router) {
                            if ($request instanceof ServerRequestInterface) {
                                return $router->state->__invoke(
                                    $matches,
                                    $request
                                );
                            } elseif ($request instanceof ResponseInterface) {
                                return $request;
                            }
                            throw new DomainException(
                                "The pipeline must resolve either with a custom
                                 Psr\Http\Message\ResponseInterface, or with the
                                 original request."
                            );
                        }
                    ));
                    return $this->pipeline
                        ->build()
                        ->process($this->request);
                } else {
                    return $router($this->request);
                }
            }
        }
        return null;
    }

{'fraction_non_alphanumeric': 0.13011297234125438, 'fraction_numerical': 0.0017140631086871834, 'mean_word_length': 1.7777537329582342, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 72, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '863201', 'n_tokens_mistral': 3575, 'n_tokens_neox': 3498, 'n_words': 1044}