File indexing completed on 2024-06-23 05:55:09

0001 <?php
0002 /**
0003  * Zend Framework
0004  *
0005  * LICENSE
0006  *
0007  * This source file is subject to the new BSD license that is bundled
0008  * with this package in the file LICENSE.txt.
0009  * It is also available through the world-wide-web at this URL:
0010  * http://framework.zend.com/license/new-bsd
0011  * If you did not receive a copy of the license and are unable to
0012  * obtain it through the world-wide-web, please send an email
0013  * to license@zend.com so we can send you a copy immediately.
0014  *
0015  * @category   Zend
0016  * @package    Zend_Controller
0017  * @subpackage Router
0018  * @copyright  Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
0019  * @version    $Id$
0020  * @license    http://framework.zend.com/license/new-bsd     New BSD License
0021  */
0022 
0023 /** Zend_Controller_Router_Route_Abstract */
0024 // require_once 'Zend/Controller/Router/Route/Abstract.php';
0025 
0026 /**
0027  * Hostname Route
0028  *
0029  * @package    Zend_Controller
0030  * @subpackage Router
0031  * @copyright  Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
0032  * @license    http://framework.zend.com/license/new-bsd     New BSD License
0033  * @see        http://manuals.rubyonrails.com/read/chapter/65
0034  */
0035 class Zend_Controller_Router_Route_Hostname extends Zend_Controller_Router_Route_Abstract
0036 {
0037 
0038     /**
0039      * Host variable
0040      *
0041      * @var string
0042      */
0043     protected $_hostVariable = ':';
0044 
0045     /**
0046      * Regex delimiter
0047      *
0048      * @var string
0049      */
0050     protected $_regexDelimiter = '#';
0051 
0052     /**
0053      * Default regex string
0054      *
0055      * @var string|null
0056      */
0057     protected $_defaultRegex = null;
0058 
0059     /**
0060      * Holds names of all route's pattern variable names. Array index holds a position in host.
0061      *
0062      * @var array
0063      */
0064     protected $_variables = array();
0065 
0066     /**
0067      * Holds Route patterns for all host parts. In case of a variable it stores it's regex
0068      * requirement or null. In case of a static part, it holds only it's direct value.
0069      *
0070      * @var array
0071      */
0072     protected $_parts = array();
0073 
0074     /**
0075      * Holds user submitted default values for route's variables. Name and value pairs.
0076      *
0077      * @var array
0078      */
0079     protected $_defaults = array();
0080 
0081     /**
0082      * Holds user submitted regular expression patterns for route's variables' values.
0083      * Name and value pairs.
0084      *
0085      * @var array
0086      */
0087     protected $_requirements = array();
0088 
0089     /**
0090      * Default scheme
0091      *
0092      * @var string
0093      */
0094     protected $_scheme = null;
0095 
0096     /**
0097      * Associative array filled on match() that holds matched path values
0098      * for given variable names.
0099      *
0100      * @var array
0101      */
0102     protected $_values = array();
0103 
0104     /**
0105      * Current request object
0106      *
0107      * @var Zend_Controller_Request_Abstract
0108      */
0109     protected $_request;
0110 
0111     /**
0112      * Helper var that holds a count of route pattern's static parts
0113      * for validation
0114      *
0115      * @var int
0116      */
0117     private $_staticCount = 0;
0118 
0119     /**
0120      * Set the request object
0121      *
0122      * @param  Zend_Controller_Request_Abstract|null $request
0123      */
0124     public function setRequest(Zend_Controller_Request_Abstract $request = null)
0125     {
0126         $this->_request = $request;
0127     }
0128 
0129     /**
0130      * Get the request object
0131      *
0132      * @return Zend_Controller_Request_Abstract $request
0133      */
0134     public function getRequest()
0135     {
0136         if ($this->_request === null) {
0137             // require_once 'Zend/Controller/Front.php';
0138             $this->_request = Zend_Controller_Front::getInstance()->getRequest();
0139         }
0140 
0141         return $this->_request;
0142     }
0143 
0144     /**
0145      * Instantiates route based on passed Zend_Config structure
0146      *
0147      * @param Zend_Config $config Configuration object
0148      * @return Zend_Controller_Router_Route_Hostname
0149      */
0150     public static function getInstance(Zend_Config $config)
0151     {
0152         $reqs   = ($config->reqs instanceof Zend_Config) ? $config->reqs->toArray() : array();
0153         $defs   = ($config->defaults instanceof Zend_Config) ? $config->defaults->toArray() : array();
0154         $scheme = (isset($config->scheme)) ? $config->scheme : null;
0155 
0156         return new self($config->route, $defs, $reqs, $scheme);
0157     }
0158 
0159     /**
0160      * Prepares the route for mapping by splitting (exploding) it
0161      * to a corresponding atomic parts. These parts are assigned
0162      * a position which is later used for matching and preparing values.
0163      *
0164      * @param string $route    Map used to match with later submitted hostname
0165      * @param array  $defaults Defaults for map variables with keys as variable names
0166      * @param array  $reqs     Regular expression requirements for variables (keys as variable names)
0167      * @param string $scheme
0168      */
0169     public function __construct($route, $defaults = array(), $reqs = array(), $scheme = null)
0170     {
0171         $route               = trim($route, '.');
0172         $this->_defaults     = (array) $defaults;
0173         $this->_requirements = (array) $reqs;
0174         $this->_scheme       = $scheme;
0175 
0176         if ($route != '') {
0177             foreach (explode('.', $route) as $pos => $part) {
0178                 if (substr($part, 0, 1) == $this->_hostVariable) {
0179                     $name                   = substr($part, 1);
0180                     $this->_parts[$pos]     = (isset($reqs[$name]) ? $reqs[$name] : $this->_defaultRegex);
0181                     $this->_variables[$pos] = $name;
0182                 } else {
0183                     $this->_parts[$pos] = $part;
0184                     $this->_staticCount++;
0185                 }
0186             }
0187         }
0188     }
0189 
0190     /**
0191      * Matches a user submitted path with parts defined by a map. Assigns and
0192      * returns an array of variables on a successful match.
0193      *
0194      * @param Zend_Controller_Request_Http $request Request to get the host from
0195      * @return array|false An array of assigned values or a false on a mismatch
0196      */
0197     public function match($request)
0198     {
0199         // Check the scheme if required
0200         if ($this->_scheme !== null) {
0201             $scheme = $request->getScheme();
0202 
0203             if ($scheme !== $this->_scheme) {
0204                 return false;
0205             }
0206         }
0207 
0208         // Get the host and remove unnecessary port information
0209         $host = $request->getHttpHost();
0210         if (preg_match('#:\d+$#', $host, $result) === 1) {
0211             $host = substr($host, 0, -strlen($result[0]));
0212         }
0213 
0214         $hostStaticCount = 0;
0215         $values          = array();
0216 
0217         $host = trim($host, '.');
0218 
0219         if ($host != '') {
0220             $host = explode('.', $host);
0221 
0222             foreach ($host as $pos => $hostPart) {
0223                 // Host is longer than a route, it's not a match
0224                 if (!array_key_exists($pos, $this->_parts)) {
0225                     return false;
0226                 }
0227 
0228                 $name     = isset($this->_variables[$pos]) ? $this->_variables[$pos] : null;
0229                 $hostPart = urldecode($hostPart);
0230 
0231                 // If it's a static part, match directly
0232                 if ($name === null && $this->_parts[$pos] != $hostPart) {
0233                     return false;
0234                 }
0235 
0236                 // If it's a variable with requirement, match a regex. If not - everything matches
0237                 if ($this->_parts[$pos] !== null
0238                     && !preg_match(
0239                         $this->_regexDelimiter . '^' . $this->_parts[$pos] . '$' . $this->_regexDelimiter . 'iu',
0240                         $hostPart
0241                     )
0242                 ) {
0243                     return false;
0244                 }
0245 
0246                 // If it's a variable store it's value for later
0247                 if ($name !== null) {
0248                     $values[$name] = $hostPart;
0249                 } else {
0250                     $hostStaticCount++;
0251                 }
0252             }
0253         }
0254 
0255         // Check if all static mappings have been matched
0256         if ($this->_staticCount != $hostStaticCount) {
0257             return false;
0258         }
0259 
0260         $return = $values + $this->_defaults;
0261 
0262         // Check if all map variables have been initialized
0263         foreach ($this->_variables as $var) {
0264             if (!array_key_exists($var, $return)) {
0265                 return false;
0266             }
0267         }
0268 
0269         $this->_values = $values;
0270 
0271         return $return;
0272     }
0273 
0274     /**
0275      * Assembles user submitted parameters forming a hostname defined by this route
0276      *
0277      * @param  array   $data  An array of variable and value pairs used as parameters
0278      * @param  boolean $reset Whether or not to set route defaults with those provided in $data
0279      * @param  boolean $encode
0280      * @param  boolean $partial
0281      * @throws Zend_Controller_Router_Exception
0282      * @return string Route path with user submitted parameters
0283      */
0284     public function assemble($data = array(), $reset = false, $encode = false, $partial = false)
0285     {
0286         $host = array();
0287         $flag = false;
0288 
0289         foreach ($this->_parts as $key => $part) {
0290             $name = isset($this->_variables[$key]) ? $this->_variables[$key] : null;
0291 
0292             $useDefault = false;
0293             if (isset($name) && array_key_exists($name, $data) && $data[$name] === null) {
0294                 $useDefault = true;
0295             }
0296 
0297             if (isset($name)) {
0298                 if (isset($data[$name]) && !$useDefault) {
0299                     $host[$key] = $data[$name];
0300                     unset($data[$name]);
0301                 } elseif (!$reset && !$useDefault && isset($this->_values[$name])) {
0302                     $host[$key] = $this->_values[$name];
0303                 } elseif (isset($this->_defaults[$name])) {
0304                     $host[$key] = $this->_defaults[$name];
0305                 } else {
0306                     // require_once 'Zend/Controller/Router/Exception.php';
0307                     throw new Zend_Controller_Router_Exception($name . ' is not specified');
0308                 }
0309             } else {
0310                 $host[$key] = $part;
0311             }
0312         }
0313 
0314         $return = '';
0315 
0316         foreach (array_reverse($host, true) as $key => $value) {
0317             if ($flag || !isset($this->_variables[$key]) || $value !== $this->getDefault($this->_variables[$key])
0318                 || $partial
0319             ) {
0320                 if ($encode) {
0321                     $value = urlencode($value);
0322                 }
0323                 $return = '.' . $value . $return;
0324                 $flag   = true;
0325             }
0326         }
0327 
0328         $url = trim($return, '.');
0329 
0330         if ($this->_scheme !== null) {
0331             $scheme = $this->_scheme;
0332         } else {
0333             $request = $this->getRequest();
0334             if ($request instanceof Zend_Controller_Request_Http) {
0335                 $scheme = $request->getScheme();
0336             } else {
0337                 $scheme = 'http';
0338             }
0339         }
0340 
0341         $url = $scheme . '://' . $url;
0342 
0343         return $url;
0344     }
0345 
0346     /**
0347      * Return a single parameter of route's defaults
0348      *
0349      * @param string $name Array key of the parameter
0350      * @return string Previously set default
0351      */
0352     public function getDefault($name)
0353     {
0354         if (isset($this->_defaults[$name])) {
0355             return $this->_defaults[$name];
0356         }
0357 
0358         return null;
0359     }
0360 
0361     /**
0362      * Return an array of defaults
0363      *
0364      * @return array Route defaults
0365      */
0366     public function getDefaults()
0367     {
0368         return $this->_defaults;
0369     }
0370 
0371     /**
0372      * Get all variables which are used by the route
0373      *
0374      * @return array
0375      */
0376     public function getVariables()
0377     {
0378         return $this->_variables;
0379     }
0380 }