/vendor/zendframework/zend-mvc/src/Router/Http/Hostname.php
PHP | 330 lines | 178 code | 54 blank | 98 comment | 29 complexity | 0a8f95e27decbb13bbb17b905fe65f37 MD5 | raw file
- <?php
- /**
- * Zend Framework (http://framework.zend.com/)
- *
- * @link http://github.com/zendframework/zf2 for the canonical source repository
- * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
- * @license http://framework.zend.com/license/new-bsd New BSD License
- */
- namespace Zend\Mvc\Router\Http;
- use Traversable;
- use Zend\Mvc\Router\Exception;
- use Zend\Stdlib\ArrayUtils;
- use Zend\Stdlib\RequestInterface as Request;
- /**
- * Hostname route.
- */
- class Hostname implements RouteInterface
- {
- /**
- * Parts of the route.
- *
- * @var array
- */
- protected $parts;
- /**
- * Regex used for matching the route.
- *
- * @var string
- */
- protected $regex;
- /**
- * Map from regex groups to parameter names.
- *
- * @var array
- */
- protected $paramMap = array();
- /**
- * Default values.
- *
- * @var array
- */
- protected $defaults;
- /**
- * List of assembled parameters.
- *
- * @var array
- */
- protected $assembledParams = array();
- /**
- * Create a new hostname route.
- *
- * @param string $route
- * @param array $constraints
- * @param array $defaults
- */
- public function __construct($route, array $constraints = array(), array $defaults = array())
- {
- $this->defaults = $defaults;
- $this->parts = $this->parseRouteDefinition($route);
- $this->regex = $this->buildRegex($this->parts, $constraints);
- }
- /**
- * factory(): defined by RouteInterface interface.
- *
- * @see \Zend\Mvc\Router\RouteInterface::factory()
- * @param array|Traversable $options
- * @return Hostname
- * @throws Exception\InvalidArgumentException
- */
- public static function factory($options = array())
- {
- if ($options instanceof Traversable) {
- $options = ArrayUtils::iteratorToArray($options);
- } elseif (!is_array($options)) {
- throw new Exception\InvalidArgumentException(__METHOD__ . ' expects an array or Traversable set of options');
- }
- if (!isset($options['route'])) {
- throw new Exception\InvalidArgumentException('Missing "route" in options array');
- }
- if (!isset($options['constraints'])) {
- $options['constraints'] = array();
- }
- if (!isset($options['defaults'])) {
- $options['defaults'] = array();
- }
- return new static($options['route'], $options['constraints'], $options['defaults']);
- }
- /**
- * Parse a route definition.
- *
- * @param string $def
- * @return array
- * @throws Exception\RuntimeException
- */
- protected function parseRouteDefinition($def)
- {
- $currentPos = 0;
- $length = strlen($def);
- $parts = array();
- $levelParts = array(&$parts);
- $level = 0;
- while ($currentPos < $length) {
- if (!preg_match('(\G(?P<literal>[a-z0-9-.]*)(?P<token>[:{\[\]]|$))', $def, $matches, 0, $currentPos)) {
- throw new Exception\RuntimeException('Matched hostname literal contains a disallowed character');
- }
- $currentPos += strlen($matches[0]);
- if (!empty($matches['literal'])) {
- $levelParts[$level][] = array('literal', $matches['literal']);
- }
- if ($matches['token'] === ':') {
- if (!preg_match('(\G(?P<name>[^:.{\[\]]+)(?:{(?P<delimiters>[^}]+)})?:?)', $def, $matches, 0, $currentPos)) {
- throw new Exception\RuntimeException('Found empty parameter name');
- }
- $levelParts[$level][] = array('parameter', $matches['name'], isset($matches['delimiters']) ? $matches['delimiters'] : null);
- $currentPos += strlen($matches[0]);
- } elseif ($matches['token'] === '[') {
- $levelParts[$level][] = array('optional', array());
- $levelParts[$level + 1] = &$levelParts[$level][count($levelParts[$level]) - 1][1];
- $level++;
- } elseif ($matches['token'] === ']') {
- unset($levelParts[$level]);
- $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 $parts;
- }
- /**
- * Build the matching regex from parsed parts.
- *
- * @param array $parts
- * @param array $constraints
- * @param int $groupIndex
- * @return string
- * @throws Exception\RuntimeException
- */
- protected function buildRegex(array $parts, array $constraints, &$groupIndex = 1)
- {
- $regex = '';
- foreach ($parts as $part) {
- switch ($part[0]) {
- case 'literal':
- $regex .= preg_quote($part[1]);
- break;
- case 'parameter':
- $groupName = '?P<param' . $groupIndex . '>';
- if (isset($constraints[$part[1]])) {
- $regex .= '(' . $groupName . $constraints[$part[1]] . ')';
- } elseif ($part[2] === null) {
- $regex .= '(' . $groupName . '[^.]+)';
- } else {
- $regex .= '(' . $groupName . '[^' . $part[2] . ']+)';
- }
- $this->paramMap['param' . $groupIndex++] = $part[1];
- break;
- case 'optional':
- $regex .= '(?:' . $this->buildRegex($part[1], $constraints, $groupIndex) . ')?';
- break;
- }
- }
- return $regex;
- }
- /**
- * Build host.
- *
- * @param array $parts
- * @param array $mergedParams
- * @param bool $isOptional
- * @return string
- * @throws Exception\RuntimeException
- * @throws Exception\InvalidArgumentException
- */
- protected function buildHost(array $parts, array $mergedParams, $isOptional)
- {
- $host = '';
- $skip = true;
- $skippable = false;
- foreach ($parts as $part) {
- switch ($part[0]) {
- case 'literal':
- $host .= $part[1];
- break;
- case 'parameter':
- $skippable = true;
- if (!isset($mergedParams[$part[1]])) {
- if (!$isOptional) {
- throw new Exception\InvalidArgumentException(sprintf('Missing parameter "%s"', $part[1]));
- }
- return '';
- } elseif (!$isOptional || !isset($this->defaults[$part[1]]) || $this->defaults[$part[1]] !== $mergedParams[$part[1]]) {
- $skip = false;
- }
- $host .= $mergedParams[$part[1]];
- $this->assembledParams[] = $part[1];
- break;
- case 'optional':
- $skippable = true;
- $optionalPart = $this->buildHost($part[1], $mergedParams, true);
- if ($optionalPart !== '') {
- $host .= $optionalPart;
- $skip = false;
- }
- break;
- }
- }
- if ($isOptional && $skippable && $skip) {
- return '';
- }
- return $host;
- }
- /**
- * match(): defined by RouteInterface interface.
- *
- * @see \Zend\Mvc\Router\RouteInterface::match()
- * @param Request $request
- * @return RouteMatch|null
- */
- public function match(Request $request)
- {
- if (!method_exists($request, 'getUri')) {
- return;
- }
- $uri = $request->getUri();
- $host = $uri->getHost();
- $result = preg_match('(^' . $this->regex . '$)', $host, $matches);
- if (!$result) {
- return;
- }
- $params = array();
- foreach ($this->paramMap as $index => $name) {
- if (isset($matches[$index]) && $matches[$index] !== '') {
- $params[$name] = $matches[$index];
- }
- }
- return new RouteMatch(array_merge($this->defaults, $params));
- }
- /**
- * assemble(): Defined by RouteInterface interface.
- *
- * @see \Zend\Mvc\Router\RouteInterface::assemble()
- * @param array $params
- * @param array $options
- * @return mixed
- */
- public function assemble(array $params = array(), array $options = array())
- {
- $this->assembledParams = array();
- if (isset($options['uri'])) {
- $host = $this->buildHost(
- $this->parts,
- array_merge($this->defaults, $params),
- false
- );
- $options['uri']->setHost($host);
- }
- // A hostname does not contribute to the path, thus nothing is returned.
- return '';
- }
- /**
- * getAssembledParams(): defined by RouteInterface interface.
- *
- * @see RouteInterface::getAssembledParams
- * @return array
- */
- public function getAssembledParams()
- {
- return $this->assembledParams;
- }
- }