File indexing completed on 2025-01-26 05:24:55
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 * 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 extends Zend_Controller_Router_Route_Abstract 0036 { 0037 0038 /** 0039 * Default translator 0040 * 0041 * @var Zend_Translate 0042 */ 0043 protected static $_defaultTranslator; 0044 0045 /** 0046 * Translator 0047 * 0048 * @var Zend_Translate 0049 */ 0050 protected $_translator; 0051 0052 /** 0053 * Default locale 0054 * 0055 * @var mixed 0056 */ 0057 protected static $_defaultLocale; 0058 0059 /** 0060 * Locale 0061 * 0062 * @var mixed 0063 */ 0064 protected $_locale; 0065 0066 /** 0067 * Wether this is a translated route or not 0068 * 0069 * @var boolean 0070 */ 0071 protected $_isTranslated = false; 0072 0073 /** 0074 * Translatable variables 0075 * 0076 * @var array 0077 */ 0078 protected $_translatable = array(); 0079 0080 protected $_urlVariable = ':'; 0081 0082 protected $_urlDelimiter = self::URI_DELIMITER; 0083 0084 protected $_regexDelimiter = '#'; 0085 0086 protected $_defaultRegex = null; 0087 0088 /** 0089 * Holds names of all route's pattern variable names. Array index holds a position in URL. 0090 * 0091 * @var array 0092 */ 0093 protected $_variables = array(); 0094 0095 /** 0096 * Holds Route patterns for all URL parts. In case of a variable it stores it's regex 0097 * requirement or null. In case of a static part, it holds only it's direct value. 0098 * In case of a wildcard, it stores an asterisk (*) 0099 * 0100 * @var array 0101 */ 0102 protected $_parts = array(); 0103 0104 /** 0105 * Holds user submitted default values for route's variables. Name and value pairs. 0106 * 0107 * @var array 0108 */ 0109 protected $_defaults = array(); 0110 0111 /** 0112 * Holds user submitted regular expression patterns for route's variables' values. 0113 * Name and value pairs. 0114 * 0115 * @var array 0116 */ 0117 protected $_requirements = array(); 0118 0119 /** 0120 * Associative array filled on match() that holds matched path values 0121 * for given variable names. 0122 * 0123 * @var array 0124 */ 0125 protected $_values = array(); 0126 0127 /** 0128 * Associative array filled on match() that holds wildcard variable 0129 * names and values. 0130 * 0131 * @var array 0132 */ 0133 protected $_wildcardData = array(); 0134 0135 /** 0136 * Helper var that holds a count of route pattern's static parts 0137 * for validation 0138 * 0139 * @var int 0140 */ 0141 protected $_staticCount = 0; 0142 0143 public function getVersion() 0144 { 0145 return 1; 0146 } 0147 0148 /** 0149 * Instantiates route based on passed Zend_Config structure 0150 * 0151 * @param Zend_Config $config Configuration object 0152 * @return Zend_Controller_Router_Route 0153 */ 0154 public static function getInstance(Zend_Config $config) 0155 { 0156 $reqs = ($config->reqs instanceof Zend_Config) ? $config->reqs->toArray() : array(); 0157 $defs = ($config->defaults instanceof Zend_Config) ? $config->defaults->toArray() : array(); 0158 0159 return new self($config->route, $defs, $reqs); 0160 } 0161 0162 /** 0163 * Prepares the route for mapping by splitting (exploding) it 0164 * to a corresponding atomic parts. These parts are assigned 0165 * a position which is later used for matching and preparing values. 0166 * 0167 * @param string $route Map used to match with later submitted URL path 0168 * @param array $defaults Defaults for map variables with keys as variable names 0169 * @param array $reqs Regular expression requirements for variables (keys as variable names) 0170 * @param Zend_Translate $translator Translator to use for this instance 0171 * @param mixed|null $locale 0172 */ 0173 public function __construct( 0174 $route, $defaults = array(), $reqs = array(), Zend_Translate $translator = null, $locale = null 0175 ) 0176 { 0177 $route = trim($route, $this->_urlDelimiter); 0178 $this->_defaults = (array)$defaults; 0179 $this->_requirements = (array)$reqs; 0180 $this->_translator = $translator; 0181 $this->_locale = $locale; 0182 0183 if ($route !== '') { 0184 foreach (explode($this->_urlDelimiter, $route) as $pos => $part) { 0185 if (substr($part, 0, 1) == $this->_urlVariable && substr($part, 1, 1) != $this->_urlVariable) { 0186 $name = substr($part, 1); 0187 0188 if (substr($name, 0, 1) === '@' && substr($name, 1, 1) !== '@') { 0189 $name = substr($name, 1); 0190 $this->_translatable[] = $name; 0191 $this->_isTranslated = true; 0192 } 0193 0194 $this->_parts[$pos] = (isset($reqs[$name]) ? $reqs[$name] : $this->_defaultRegex); 0195 $this->_variables[$pos] = $name; 0196 } else { 0197 if (substr($part, 0, 1) == $this->_urlVariable) { 0198 $part = substr($part, 1); 0199 } 0200 0201 if (substr($part, 0, 1) === '@' && substr($part, 1, 1) !== '@') { 0202 $this->_isTranslated = true; 0203 } 0204 0205 $this->_parts[$pos] = $part; 0206 0207 if ($part !== '*') { 0208 $this->_staticCount++; 0209 } 0210 } 0211 } 0212 } 0213 } 0214 0215 /** 0216 * Matches a user submitted path with parts defined by a map. Assigns and 0217 * returns an array of variables on a successful match. 0218 * 0219 * @param string $path Path used to match against this routing map 0220 * @param boolean $partial 0221 * @throws Zend_Controller_Router_Exception 0222 * @return array|false An array of assigned values or a false on a mismatch 0223 */ 0224 public function match($path, $partial = false) 0225 { 0226 if ($this->_isTranslated) { 0227 $translateMessages = $this->getTranslator()->getMessages(); 0228 } 0229 0230 $pathStaticCount = 0; 0231 $values = array(); 0232 $matchedPath = ''; 0233 0234 if (!$partial) { 0235 $path = trim($path, $this->_urlDelimiter); 0236 } 0237 0238 if ($path !== '') { 0239 $path = explode($this->_urlDelimiter, $path); 0240 0241 foreach ($path as $pos => $pathPart) { 0242 // Path is longer than a route, it's not a match 0243 if (!array_key_exists($pos, $this->_parts)) { 0244 if ($partial) { 0245 break; 0246 } else { 0247 return false; 0248 } 0249 } 0250 0251 $matchedPath .= $pathPart . $this->_urlDelimiter; 0252 0253 // If it's a wildcard, get the rest of URL as wildcard data and stop matching 0254 if ($this->_parts[$pos] == '*') { 0255 $count = count($path); 0256 for ($i = $pos; $i < $count; $i += 2) { 0257 $var = urldecode($path[$i]); 0258 if (!isset($this->_wildcardData[$var]) && !isset($this->_defaults[$var]) 0259 && !isset($values[$var]) 0260 ) { 0261 $this->_wildcardData[$var] = (isset($path[$i + 1])) ? urldecode($path[$i + 1]) : null; 0262 } 0263 } 0264 0265 $matchedPath = implode($this->_urlDelimiter, $path); 0266 break; 0267 } 0268 0269 $name = isset($this->_variables[$pos]) ? $this->_variables[$pos] : null; 0270 $pathPart = urldecode($pathPart); 0271 0272 // Translate value if required 0273 $part = $this->_parts[$pos]; 0274 if ($this->_isTranslated 0275 && (substr($part, 0, 1) === '@' && substr($part, 1, 1) !== '@' 0276 && $name === null) 0277 || $name !== null && in_array($name, $this->_translatable) 0278 ) { 0279 if (substr($part, 0, 1) === '@') { 0280 $part = substr($part, 1); 0281 } 0282 0283 if (($originalPathPart = array_search($pathPart, $translateMessages)) !== false) { 0284 $pathPart = $originalPathPart; 0285 } 0286 } 0287 0288 if (substr($part, 0, 2) === '@@') { 0289 $part = substr($part, 1); 0290 } 0291 0292 // If it's a static part, match directly 0293 if ($name === null && $part != $pathPart) { 0294 return false; 0295 } 0296 0297 // If it's a variable with requirement, match a regex. If not - everything matches 0298 if ($part !== null 0299 && !preg_match( 0300 $this->_regexDelimiter . '^' . $part . '$' . $this->_regexDelimiter . 'iu', $pathPart 0301 ) 0302 ) { 0303 return false; 0304 } 0305 0306 // If it's a variable store it's value for later 0307 if ($name !== null) { 0308 $values[$name] = $pathPart; 0309 } else { 0310 $pathStaticCount++; 0311 } 0312 } 0313 } 0314 0315 // Check if all static mappings have been matched 0316 if ($this->_staticCount != $pathStaticCount) { 0317 return false; 0318 } 0319 0320 $return = $values + $this->_wildcardData + $this->_defaults; 0321 0322 // Check if all map variables have been initialized 0323 foreach ($this->_variables as $var) { 0324 if (!array_key_exists($var, $return)) { 0325 return false; 0326 } elseif ($return[$var] == '' || $return[$var] === null) { 0327 // Empty variable? Replace with the default value. 0328 $return[$var] = $this->_defaults[$var]; 0329 } 0330 } 0331 0332 $this->setMatchedPath(rtrim($matchedPath, $this->_urlDelimiter)); 0333 0334 $this->_values = $values; 0335 0336 return $return; 0337 } 0338 0339 /** 0340 * Assembles user submitted parameters forming a URL path defined by this route 0341 * 0342 * @param array $data An array of variable and value pairs used as parameters 0343 * @param boolean $reset Whether or not to set route defaults with those provided in $data 0344 * @param boolean $encode 0345 * @param boolean $partial 0346 * @throws Zend_Controller_Router_Exception 0347 * @return string Route path with user submitted parameters 0348 */ 0349 public function assemble($data = array(), $reset = false, $encode = false, $partial = false) 0350 { 0351 if ($this->_isTranslated) { 0352 $translator = $this->getTranslator(); 0353 0354 if (isset($data['@locale'])) { 0355 $locale = $data['@locale']; 0356 unset($data['@locale']); 0357 } else { 0358 $locale = $this->getLocale(); 0359 } 0360 } 0361 0362 $url = array(); 0363 $flag = false; 0364 0365 foreach ($this->_parts as $key => $part) { 0366 $name = isset($this->_variables[$key]) ? $this->_variables[$key] : null; 0367 0368 $useDefault = false; 0369 if (isset($name) && array_key_exists($name, $data) && $data[$name] === null) { 0370 $useDefault = true; 0371 } 0372 0373 if (isset($name)) { 0374 if (isset($data[$name]) && !$useDefault) { 0375 $value = $data[$name]; 0376 unset($data[$name]); 0377 } elseif (!$reset && !$useDefault && isset($this->_values[$name])) { 0378 $value = $this->_values[$name]; 0379 } elseif (!$reset && !$useDefault && isset($this->_wildcardData[$name])) { 0380 $value = $this->_wildcardData[$name]; 0381 } elseif (array_key_exists($name, $this->_defaults)) { 0382 $value = $this->_defaults[$name]; 0383 } else { 0384 // require_once 'Zend/Controller/Router/Exception.php'; 0385 throw new Zend_Controller_Router_Exception($name . ' is not specified'); 0386 } 0387 0388 if ($this->_isTranslated && in_array($name, $this->_translatable)) { 0389 $url[$key] = $translator->translate($value, $locale); 0390 } else { 0391 $url[$key] = $value; 0392 } 0393 } elseif ($part != '*') { 0394 if ($this->_isTranslated && substr($part, 0, 1) === '@') { 0395 if (substr($part, 1, 1) !== '@') { 0396 $url[$key] = $translator->translate(substr($part, 1), $locale); 0397 } else { 0398 $url[$key] = substr($part, 1); 0399 } 0400 } else { 0401 if (substr($part, 0, 2) === '@@') { 0402 $part = substr($part, 1); 0403 } 0404 0405 $url[$key] = $part; 0406 } 0407 } else { 0408 if (!$reset) { 0409 $data += $this->_wildcardData; 0410 } 0411 $defaults = $this->getDefaults(); 0412 foreach ($data as $var => $value) { 0413 if ($value !== null && (!isset($defaults[$var]) || $value != $defaults[$var])) { 0414 $url[$key++] = $var; 0415 $url[$key++] = $value; 0416 $flag = true; 0417 } 0418 } 0419 } 0420 } 0421 0422 $return = ''; 0423 0424 foreach (array_reverse($url, true) as $key => $value) { 0425 $defaultValue = null; 0426 0427 if (isset($this->_variables[$key])) { 0428 $defaultValue = $this->getDefault($this->_variables[$key]); 0429 0430 if ($this->_isTranslated && $defaultValue !== null 0431 && isset($this->_translatable[$this->_variables[$key]]) 0432 ) { 0433 $defaultValue = $translator->translate($defaultValue, $locale); 0434 } 0435 } 0436 0437 if ($flag || $value !== $defaultValue || $partial) { 0438 if ($encode) { 0439 $value = urlencode($value); 0440 } 0441 $return = $this->_urlDelimiter . $value . $return; 0442 $flag = true; 0443 } 0444 } 0445 0446 return trim($return, $this->_urlDelimiter); 0447 } 0448 0449 /** 0450 * Return a single parameter of route's defaults 0451 * 0452 * @param string $name Array key of the parameter 0453 * @return string Previously set default 0454 */ 0455 public function getDefault($name) 0456 { 0457 if (isset($this->_defaults[$name])) { 0458 return $this->_defaults[$name]; 0459 } 0460 0461 return null; 0462 } 0463 0464 /** 0465 * Return an array of defaults 0466 * 0467 * @return array Route defaults 0468 */ 0469 public function getDefaults() 0470 { 0471 return $this->_defaults; 0472 } 0473 0474 /** 0475 * Get all variables which are used by the route 0476 * 0477 * @return array 0478 */ 0479 public function getVariables() 0480 { 0481 return $this->_variables; 0482 } 0483 0484 /** 0485 * Set a default translator 0486 * 0487 * @param Zend_Translate $translator 0488 * @return void 0489 */ 0490 public static function setDefaultTranslator(Zend_Translate $translator = null) 0491 { 0492 self::$_defaultTranslator = $translator; 0493 } 0494 0495 /** 0496 * Get the default translator 0497 * 0498 * @return Zend_Translate 0499 */ 0500 public static function getDefaultTranslator() 0501 { 0502 return self::$_defaultTranslator; 0503 } 0504 0505 /** 0506 * Set a translator 0507 * 0508 * @param Zend_Translate $translator 0509 * @return void 0510 */ 0511 public function setTranslator(Zend_Translate $translator) 0512 { 0513 $this->_translator = $translator; 0514 } 0515 0516 /** 0517 * Get the translator 0518 * 0519 * @throws Zend_Controller_Router_Exception When no translator can be found 0520 * @return Zend_Translate 0521 */ 0522 public function getTranslator() 0523 { 0524 if ($this->_translator !== null) { 0525 return $this->_translator; 0526 } else { 0527 if (($translator = self::getDefaultTranslator()) !== null) { 0528 return $translator; 0529 } else { 0530 try { 0531 $translator = Zend_Registry::get('Zend_Translate'); 0532 } catch (Zend_Exception $e) { 0533 $translator = null; 0534 } 0535 0536 if ($translator instanceof Zend_Translate) { 0537 return $translator; 0538 } 0539 } 0540 } 0541 0542 // require_once 'Zend/Controller/Router/Exception.php'; 0543 throw new Zend_Controller_Router_Exception('Could not find a translator'); 0544 } 0545 0546 /** 0547 * Set a default locale 0548 * 0549 * @param mixed $locale 0550 * @return void 0551 */ 0552 public static function setDefaultLocale($locale = null) 0553 { 0554 self::$_defaultLocale = $locale; 0555 } 0556 0557 /** 0558 * Get the default locale 0559 * 0560 * @return mixed 0561 */ 0562 public static function getDefaultLocale() 0563 { 0564 return self::$_defaultLocale; 0565 } 0566 0567 /** 0568 * Set a locale 0569 * 0570 * @param mixed $locale 0571 * @return void 0572 */ 0573 public function setLocale($locale) 0574 { 0575 $this->_locale = $locale; 0576 } 0577 0578 /** 0579 * Get the locale 0580 * 0581 * @return mixed 0582 */ 0583 public function getLocale() 0584 { 0585 if ($this->_locale !== null) { 0586 return $this->_locale; 0587 } else { 0588 if (($locale = self::getDefaultLocale()) !== null) { 0589 return $locale; 0590 } else { 0591 try { 0592 $locale = Zend_Registry::get('Zend_Locale'); 0593 } catch (Zend_Exception $e) { 0594 $locale = null; 0595 } 0596 0597 if ($locale !== null) { 0598 return $locale; 0599 } 0600 } 0601 } 0602 0603 return null; 0604 } 0605 }