phossa2/route

View on GitHub
src/Route/Collector/Collector.php

Summary

Maintainability
A
0 mins
Test Coverage
<?php
/**
 * Phossa Project
 *
 * PHP version 5.4
 *
 * @category  Library
 * @package   Phossa2\Route
 * @copyright Copyright (c) 2016 phossa.com
 * @license   http://mit-license.org/ MIT License
 * @link      http://www.phossa.com/
 */
/*# declare(strict_types=1); */

namespace Phossa2\Route\Collector;

use Phossa2\Route\Status;
use Phossa2\Route\Message\Message;
use Phossa2\Route\Parser\ParserGcb;
use Phossa2\Route\Exception\LogicException;
use Phossa2\Route\Interfaces\RouteInterface;
use Phossa2\Route\Interfaces\ParserInterface;
use Phossa2\Route\Interfaces\ResultInterface;

/**
 * Collector
 *
 * Regular Expression Routing (RER)
 *
 * @package Phossa2\Route
 * @author  Hong Zhang <phossa@126.com>
 * @see     CollectorAbstract
 * @version 2.0.0
 * @since   2.0.0 added
 */
class Collector extends CollectorAbstract
{
    /**
     * pattern parser
     *
     * @var    ParserInterface
     * @access protected
     */
    protected $parser;

    /**
     * routes
     *
     * @var    array
     * @access protected
     */
    protected $routes = [];

    /**
     * Constructor
     *
     * @param  ParserInterface $parser
     * @param  array $properties
     * @access public
     */
    public function __construct(
        ParserInterface $parser = null,
        array $properties = []
    ) {
        $this->parser = $parser ?: new ParserGcb();
        parent::__construct($properties);

        if ($this->isDebugging()) {
            $this->debug(Message::get(
                Message::RTE_PARSER_ADD,
                get_class($this),
                get_class($this->parser)
            ));
            $this->delegateDebugger($this->parser);
        }
    }

    /**
     * {@inheritDoc}
     */
    public function addRoute(RouteInterface $route)
    {
        // process pattern
        $routeKey = $this->getRouteKey($route);
        if (!isset($this->routes[$routeKey])) {
            $this->routes[$routeKey] = [];
            $this->parser->processRoute($routeKey, $route->getPattern());
        }

        $methods = $route->getMethods();
        foreach ($methods as $method) {
            $this->checkDuplication($route, $routeKey, $method);
            $this->routes[$routeKey][$method] = $route;
        }

        $this->debug(Message::get(
            Message::RTE_ROUTE_ADDED,
            $route->getPattern(),
            join('|', $methods)
        ));
        return $this;
    }

    /**
     * Same route pattern and method ?
     *
     * @param  RouteInterface $route
     * @param  string $routeKey
     * @param  string $method
     * @throws LogicException if duplication found
     * @access protected
     */
    protected function checkDuplication(
        RouteInterface $route,
        /*# string */ $routeKey,
        /*# string */ $method
    ) {
        if (isset($this->routes[$routeKey][$method])) {
            throw new LogicException(
                Message::get(
                    Message::RTE_ROUTE_DUPLICATED,
                    $route->getPattern(),
                    $method
                ),
                Message::RTE_ROUTE_DUPLICATED
            );
        }
    }

    /**
     * {@inheritDoc}
     */
    protected function match(ResultInterface $result)/*# : bool */
    {
        $res = $this->parser->matchPath($result->getPath());
        if ($res) {
            list($routeKey, $params) = $res;
            return $this->getRoute($result, $routeKey, $params);
        }
        return false;
    }

    /**
     * Calculate route's unique key
     *
     * @param  RouteInterface $route
     * @return string
     * @access protected
     */
    protected function getRouteKey(RouteInterface $route)/*# : string */
    {
        return 'x' . substr(md5($route->getPattern()), -7);
    }

    /**
     * Get matched route
     *
     * @param  ResultInterface $result
     * @param  string $routeKey unique route key
     * @param  array $matches matched parameters
     * @return bool
     * @access protected
     */
    protected function getRoute(
        ResultInterface $result,
        /*# string */ $routeKey,
        array $matches
    )/*# : bool */ {
        $method = $result->getMethod();
        if (!isset($this->routes[$routeKey][$method])) {
            $result->setStatus(Status::METHOD_NOT_ALLOWED);
            return false;
        }
        $route = $this->routes[$routeKey][$method];
        $result->setStatus(Status::OK)->setRoute($route)
            ->setParameters(array_replace($route->getDefault(), $matches))
            ->setHandler($route->getHandler(Status::OK));

        $this->debug(Message::get(
            Message::RTE_ROUTE_MATCHED,
            $result->getPath(),
            $route->getPattern()
        ));
        return true;
    }
}