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  * Regex 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  */
0034 class Zend_Controller_Router_Route_Regex extends Zend_Controller_Router_Route_Abstract
0035 {
0036 
0037     /**
0038      * Regex string
0039      *
0040      * @var string|null
0041      */
0042     protected $_regex = null;
0043 
0044     /**
0045      * Default values for the route (ie. module, controller, action, params)
0046      *
0047      * @var array
0048      */
0049     protected $_defaults = array();
0050 
0051     /**
0052      * Reverse
0053      *
0054      * @var string|null
0055      */
0056     protected $_reverse = null;
0057 
0058     /**
0059      * Map
0060      *
0061      * @var array
0062      */
0063     protected $_map = array();
0064 
0065     /**
0066      * Values
0067      *
0068      * @var array
0069      */
0070     protected $_values = array();
0071 
0072     /**
0073      * Instantiates route based on passed Zend_Config structure
0074      *
0075      * @param Zend_Config $config Configuration object
0076      * @return Zend_Controller_Router_Route_Regex
0077      */
0078     public static function getInstance(Zend_Config $config)
0079     {
0080         $defs    = ($config->defaults instanceof Zend_Config) ? $config->defaults->toArray() : array();
0081         $map     = ($config->map instanceof Zend_Config) ? $config->map->toArray() : array();
0082         $reverse = (isset($config->reverse)) ? $config->reverse : null;
0083 
0084         return new self($config->route, $defs, $map, $reverse);
0085     }
0086 
0087     /**
0088      * Constructor
0089      *
0090      * @param       $route
0091      * @param array $defaults
0092      * @param array $map
0093      * @param null  $reverse
0094      */
0095     public function __construct($route, $defaults = array(), $map = array(), $reverse = null)
0096     {
0097         $this->_regex    = $route;
0098         $this->_defaults = (array) $defaults;
0099         $this->_map      = (array) $map;
0100         $this->_reverse  = $reverse;
0101     }
0102 
0103     /**
0104      * Get the version of the route
0105      *
0106      * @return int
0107      */
0108     public function getVersion()
0109     {
0110         return 1;
0111     }
0112 
0113     /**
0114      * Matches a user submitted path with a previously defined route.
0115      * Assigns and returns an array of defaults on a successful match.
0116      *
0117      * @param  string $path Path used to match against this routing map
0118      * @return array|false  An array of assigned values or a false on a mismatch
0119      */
0120     public function match($path, $partial = false)
0121     {
0122         if (!$partial) {
0123             $path  = trim(urldecode($path), self::URI_DELIMITER);
0124             $regex = '#^' . $this->_regex . '$#i';
0125         } else {
0126             $regex = '#^' . $this->_regex . '#i';
0127         }
0128 
0129         $res = preg_match($regex, $path, $values);
0130 
0131         if ($res === 0) {
0132             return false;
0133         }
0134 
0135         if ($partial) {
0136             $this->setMatchedPath($values[0]);
0137         }
0138 
0139         // array_filter_key()? Why isn't this in a standard PHP function set yet? :)
0140         foreach ($values as $i => $value) {
0141             if (!is_int($i) || $i === 0) {
0142                 unset($values[$i]);
0143             }
0144         }
0145 
0146         $this->_values = $values;
0147 
0148         $values   = $this->_getMappedValues($values);
0149         $defaults = $this->_getMappedValues($this->_defaults, false, true);
0150         $return   = $values + $defaults;
0151 
0152         return $return;
0153     }
0154 
0155     /**
0156      * Maps numerically indexed array values to it's associative mapped counterpart.
0157      * Or vice versa. Uses user provided map array which consists of index => name
0158      * parameter mapping. If map is not found, it returns original array.
0159      *
0160      * Method strips destination type of keys form source array. Ie. if source array is
0161      * indexed numerically then every associative key will be stripped. Vice versa if reversed
0162      * is set to true.
0163      *
0164      * @param  array   $values   Indexed or associative array of values to map
0165      * @param  boolean $reversed False means translation of index to association. True means reverse.
0166      * @param  boolean $preserve Should wrong type of keys be preserved or stripped.
0167      * @return array   An array of mapped values
0168      */
0169     protected function _getMappedValues($values, $reversed = false, $preserve = false)
0170     {
0171         if (count($this->_map) == 0) {
0172             return $values;
0173         }
0174 
0175         $return = array();
0176 
0177         foreach ($values as $key => $value) {
0178             if (is_int($key) && !$reversed) {
0179                 if (array_key_exists($key, $this->_map)) {
0180                     $index = $this->_map[$key];
0181                 } elseif (false === ($index = array_search($key, $this->_map))) {
0182                     $index = $key;
0183                 }
0184                 $return[$index] = $values[$key];
0185             } elseif ($reversed) {
0186                 $index = $key;
0187                 if (!is_int($key)) {
0188                     if (array_key_exists($key, $this->_map)) {
0189                         $index = $this->_map[$key];
0190                     } else {
0191                         $index = array_search($key, $this->_map, true);
0192                     }
0193                 }
0194                 if (false !== $index) {
0195                     $return[$index] = $values[$key];
0196                 }
0197             } elseif ($preserve) {
0198                 $return[$key] = $value;
0199             }
0200         }
0201 
0202         return $return;
0203     }
0204 
0205     /**
0206      * Assembles a URL path defined by this route
0207      *
0208      * @param  array   $data An array of name (or index) and value pairs used as parameters
0209      * @param  boolean $reset
0210      * @param  boolean $encode
0211      * @param  boolean $partial
0212      * @throws Zend_Controller_Router_Exception
0213      * @return string Route path with user submitted parameters
0214      */
0215     public function assemble($data = array(), $reset = false, $encode = false, $partial = false)
0216     {
0217         if ($this->_reverse === null) {
0218             // require_once 'Zend/Controller/Router/Exception.php';
0219             throw new Zend_Controller_Router_Exception('Cannot assemble. Reversed route is not specified.');
0220         }
0221 
0222         $defaultValuesMapped = $this->_getMappedValues($this->_defaults, true, false);
0223         $matchedValuesMapped = $this->_getMappedValues($this->_values, true, false);
0224         $dataValuesMapped    = $this->_getMappedValues($data, true, false);
0225 
0226         // handle resets, if so requested (By null value) to do so
0227         if (($resetKeys = array_search(null, $dataValuesMapped, true)) !== false) {
0228             foreach ((array)$resetKeys as $resetKey) {
0229                 if (isset($matchedValuesMapped[$resetKey])) {
0230                     unset($matchedValuesMapped[$resetKey]);
0231                     unset($dataValuesMapped[$resetKey]);
0232                 }
0233             }
0234         }
0235 
0236         // merge all the data together, first defaults, then values matched, then supplied
0237         $mergedData = $defaultValuesMapped;
0238         $mergedData = $this->_arrayMergeNumericKeys($mergedData, $matchedValuesMapped);
0239         $mergedData = $this->_arrayMergeNumericKeys($mergedData, $dataValuesMapped);
0240 
0241         if ($encode) {
0242             foreach ($mergedData as $key => &$value) {
0243                 $value = urlencode($value);
0244             }
0245         }
0246 
0247         ksort($mergedData);
0248 
0249         $return = @vsprintf($this->_reverse, $mergedData);
0250 
0251         if ($return === false) {
0252             // require_once 'Zend/Controller/Router/Exception.php';
0253             throw new Zend_Controller_Router_Exception('Cannot assemble. Too few arguments?');
0254         }
0255 
0256         return $return;
0257     }
0258 
0259     /**
0260      * Return a single parameter of route's defaults
0261      *
0262      * @param string $name Array key of the parameter
0263      * @return string Previously set default
0264      */
0265     public function getDefault($name)
0266     {
0267         if (isset($this->_defaults[$name])) {
0268             return $this->_defaults[$name];
0269         }
0270     }
0271 
0272     /**
0273      * Return an array of defaults
0274      *
0275      * @return array Route defaults
0276      */
0277     public function getDefaults()
0278     {
0279         return $this->_defaults;
0280     }
0281 
0282     /**
0283      * Get all variables which are used by the route
0284      *
0285      * @return array
0286      */
0287     public function getVariables()
0288     {
0289         $variables = array();
0290 
0291         foreach ($this->_map as $key => $value) {
0292             if (is_numeric($key)) {
0293                 $variables[] = $value;
0294             } else {
0295                 $variables[] = $key;
0296             }
0297         }
0298 
0299         return $variables;
0300     }
0301 
0302     /**
0303      * _arrayMergeNumericKeys() - allows for a strict key (numeric's included) array_merge.
0304      * php's array_merge() lacks the ability to merge with numeric keys.
0305      *
0306      * @param array $array1
0307      * @param array $array2
0308      * @return array
0309      */
0310     protected function _arrayMergeNumericKeys(Array $array1, Array $array2)
0311     {
0312         $returnArray = $array1;
0313         foreach ($array2 as $array2Index => $array2Value) {
0314             $returnArray[$array2Index] = $array2Value;
0315         }
0316 
0317         return $returnArray;
0318     }
0319 }