alexpts/php-psr15-routing

View on GitHub
src/PTS/PSR15Routing/Router.php

Summary

Maintainability
A
0 mins
Test Coverage
<?php
declare(strict_types=1);

namespace PTS\PSR15Routing;

use Psr\Http\Message\ServerRequestInterface;
use PTS\Tools\Collection;
use PTS\Tools\DuplicateKeyException;

class Router extends Collection
{
    /**
     * @param string $name
     * @param Route $route
     * @param int $priority
     *
     * @return Router
     * @throws DuplicateKeyException
     */
    public function add(string $name, Route $route, int $priority = 50): self
    {
        return $this->addItem($name, $route, $priority);
    }

    public function remove(string $name): self
    {
        return $this->removeItemWithoutPriority($name);
    }

    public function getRoutes(bool $sortByPriority = true): array
    {
        return $this->getFlatItems($sortByPriority);
    }

    /**
     * @param ServerRequestInterface $request
     *
     * @return Route
     *
     * @throws NotFoundRouteException
     */
    public function match(ServerRequestInterface $request): Route
    {
        $uri = $request->getUri()->getPath();
        $method = $request->getMethod();

        foreach ($this->getRoutes() as $route) {
            $activeRoute = $this->matchRoute($route, $uri, $method);
            if ($activeRoute !== null) {
                return $activeRoute;
            }
        }

        throw new NotFoundRouteException('Route not found');
    }

    protected function isAllowMethod(Route $route, string $method): bool
    {
        $allows = $route->getMethods();
        return \count($allows) ? \in_array($method, $allows, true) : true;
    }

    protected function matchRoute(Route $route, string $pathUrl, string $method): ?Route
    {
        if (!$this->isAllowMethod($route, $method)) {
            return null;
        }

        $regexp = $this->makeRegExp($route);

        if (preg_match('~^'.$regexp.'$~Uiu', $pathUrl, $values)) {
            $filterValues = array_filter(array_keys($values), '\is_string');
            $matches = array_intersect_key($values, array_flip($filterValues));
            return $route->setMatches($matches);
        }

        return null;
    }

    public function makeRegExp(Route $route): string
    {
        $regexp = $route->getPath();
        $restrictions = $route->getRestrictions();
        $placeholders = [];

        if (preg_match_all('~{(.*)}~Uu', $regexp, $placeholders)) {
            foreach ($placeholders[0] as $index => $match) {
                $name = $placeholders[1][$index];
                $replace = array_key_exists($name, $restrictions) ? $restrictions[$name] : '[^\/]+';
                $replace = '(?<'.$name.'>'.$replace.')';
                $regexp = str_replace($match, $replace, $regexp);
            }
        }

        return $regexp;
    }
}