File indexing completed on 2024-12-22 05:36:58
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_Rest 0017 * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) 0018 * @license http://framework.zend.com/license/new-bsd New BSD License 0019 * @version $Id$ 0020 */ 0021 0022 /** 0023 * @see Zend_Controller_Router_Route_Interface 0024 */ 0025 // require_once 'Zend/Controller/Router/Route/Interface.php'; 0026 0027 /** 0028 * @see Zend_Controller_Router_Route_Module 0029 */ 0030 // require_once 'Zend/Controller/Router/Route/Module.php'; 0031 0032 /** 0033 * @see Zend_Controller_Dispatcher_Interface 0034 */ 0035 // require_once 'Zend/Controller/Dispatcher/Interface.php'; 0036 0037 /** 0038 * @see Zend_Controller_Request_Abstract 0039 */ 0040 // require_once 'Zend/Controller/Request/Abstract.php'; 0041 0042 /** 0043 * Rest Route 0044 * 0045 * Request-aware route for RESTful modular routing 0046 * 0047 * @category Zend 0048 * @package Zend_Rest 0049 * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com) 0050 * @license http://framework.zend.com/license/new-bsd New BSD License 0051 */ 0052 class Zend_Rest_Route extends Zend_Controller_Router_Route_Module 0053 { 0054 /** 0055 * Specific Modules to receive RESTful routes 0056 * @var array 0057 */ 0058 protected $_restfulModules = null; 0059 0060 /** 0061 * Specific Modules=>Controllers to receive RESTful routes 0062 * @var array 0063 */ 0064 protected $_restfulControllers = null; 0065 0066 /** 0067 * @var Zend_Controller_Front 0068 */ 0069 protected $_front; 0070 0071 /** 0072 * Constructor 0073 * 0074 * @param Zend_Controller_Front $front Front Controller object 0075 * @param array $defaults Defaults for map variables with keys as variable names 0076 * @param array $responders Modules or controllers to receive RESTful routes 0077 */ 0078 public function __construct(Zend_Controller_Front $front, 0079 array $defaults = array(), 0080 array $responders = array() 0081 ) { 0082 $this->_defaults = $defaults; 0083 0084 if ($responders) { 0085 $this->_parseResponders($responders); 0086 } 0087 0088 $this->_front = $front; 0089 $this->_dispatcher = $front->getDispatcher(); 0090 } 0091 0092 /** 0093 * Instantiates route based on passed Zend_Config structure 0094 */ 0095 public static function getInstance(Zend_Config $config) 0096 { 0097 $frontController = Zend_Controller_Front::getInstance(); 0098 $defaultsArray = array(); 0099 $restfulConfigArray = array(); 0100 foreach ($config as $key => $values) { 0101 if ($key == 'type') { 0102 // do nothing 0103 } elseif ($key == 'defaults') { 0104 $defaultsArray = $values->toArray(); 0105 } else { 0106 $restfulConfigArray[$key] = explode(',', $values); 0107 } 0108 } 0109 $instance = new self($frontController, $defaultsArray, $restfulConfigArray); 0110 return $instance; 0111 } 0112 0113 /** 0114 * Matches a user submitted request. Assigns and returns an array of variables 0115 * on a successful match. 0116 * 0117 * If a request object is registered, it uses its setModuleName(), 0118 * setControllerName(), and setActionName() accessors to set those values. 0119 * Always returns the values as an array. 0120 * 0121 * @param Zend_Controller_Request_Http $request Request used to match against this routing ruleset 0122 * @return array An array of assigned values or a false on a mismatch 0123 */ 0124 public function match($request, $partial = false) 0125 { 0126 if (!$request instanceof Zend_Controller_Request_Http) { 0127 $request = $this->_front->getRequest(); 0128 } 0129 $this->_request = $request; 0130 $this->_setRequestKeys(); 0131 0132 $path = $request->getPathInfo(); 0133 $params = $request->getParams(); 0134 $values = array(); 0135 $path = trim($path, self::URI_DELIMITER); 0136 0137 if ($path != '') { 0138 0139 $path = explode(self::URI_DELIMITER, $path); 0140 // Determine Module 0141 $moduleName = $this->_defaults[$this->_moduleKey]; 0142 $dispatcher = $this->_front->getDispatcher(); 0143 if ($dispatcher && $dispatcher->isValidModule($path[0])) { 0144 $moduleName = $path[0]; 0145 if ($this->_checkRestfulModule($moduleName)) { 0146 $values[$this->_moduleKey] = array_shift($path); 0147 $this->_moduleValid = true; 0148 } 0149 } 0150 0151 // Determine Controller 0152 $controllerName = $this->_defaults[$this->_controllerKey]; 0153 if (count($path) && !empty($path[0])) { 0154 if ($this->_checkRestfulController($moduleName, $path[0])) { 0155 $controllerName = $path[0]; 0156 $values[$this->_controllerKey] = array_shift($path); 0157 $values[$this->_actionKey] = 'get'; 0158 } else { 0159 // If Controller in URI is not found to be a RESTful 0160 // Controller, return false to fall back to other routes 0161 return false; 0162 } 0163 } elseif ($this->_checkRestfulController($moduleName, $controllerName)) { 0164 $values[$this->_controllerKey] = $controllerName; 0165 $values[$this->_actionKey] = 'get'; 0166 } else { 0167 return false; 0168 } 0169 0170 //Store path count for method mapping 0171 $pathElementCount = count($path); 0172 0173 // Check for "special get" URI's 0174 $specialGetTarget = false; 0175 if ($pathElementCount && array_search($path[0], array('index', 'new')) > -1) { 0176 $specialGetTarget = array_shift($path); 0177 } elseif ($pathElementCount && $path[$pathElementCount-1] == 'edit') { 0178 $specialGetTarget = 'edit'; 0179 $params['id'] = urldecode($path[$pathElementCount-2]); 0180 } elseif ($pathElementCount == 1) { 0181 $params['id'] = urldecode(array_shift($path)); 0182 } elseif ($pathElementCount == 0 && !isset($params['id'])) { 0183 $specialGetTarget = 'index'; 0184 } 0185 0186 // Digest URI params 0187 if ($numSegs = count($path)) { 0188 for ($i = 0; $i < $numSegs; $i = $i + 2) { 0189 $key = urldecode($path[$i]); 0190 $val = isset($path[$i + 1]) ? $path[$i + 1] : null; 0191 $params[$key] = urldecode($val); 0192 } 0193 } 0194 0195 // Determine Action 0196 $requestMethod = strtolower($request->getMethod()); 0197 if ($requestMethod != 'get') { 0198 if ($request->getParam('_method')) { 0199 $values[$this->_actionKey] = strtolower($request->getParam('_method')); 0200 } elseif ( $request->getHeader('X-HTTP-Method-Override') ) { 0201 $values[$this->_actionKey] = strtolower($request->getHeader('X-HTTP-Method-Override')); 0202 } else { 0203 $values[$this->_actionKey] = $requestMethod; 0204 } 0205 0206 // Map PUT and POST to actual create/update actions 0207 // based on parameter count (posting to resource or collection) 0208 switch( $values[$this->_actionKey] ){ 0209 case 'post': 0210 if ($pathElementCount > 0) { 0211 $values[$this->_actionKey] = 'put'; 0212 } else { 0213 $values[$this->_actionKey] = 'post'; 0214 } 0215 break; 0216 case 'put': 0217 $values[$this->_actionKey] = 'put'; 0218 break; 0219 } 0220 0221 } elseif ($specialGetTarget) { 0222 $values[$this->_actionKey] = $specialGetTarget; 0223 } 0224 0225 } 0226 $this->_values = $values + $params; 0227 0228 $result = $this->_values + $this->_defaults; 0229 0230 if ($partial && $result) 0231 $this->setMatchedPath($request->getPathInfo()); 0232 0233 return $result; 0234 } 0235 0236 /** 0237 * Assembles user submitted parameters forming a URL path defined by this route 0238 * 0239 * @param array $data An array of variable and value pairs used as parameters 0240 * @param bool $reset Weither to reset the current params 0241 * @param bool $encode Weither to return urlencoded string 0242 * @return string Route path with user submitted parameters 0243 */ 0244 public function assemble($data = array(), $reset = false, $encode = true) 0245 { 0246 if (!$this->_keysSet) { 0247 if (null === $this->_request) { 0248 $this->_request = $this->_front->getRequest(); 0249 } 0250 $this->_setRequestKeys(); 0251 } 0252 0253 $params = (!$reset) ? $this->_values : array(); 0254 0255 foreach ($data as $key => $value) { 0256 if ($value !== null) { 0257 $params[$key] = $value; 0258 } elseif (isset($params[$key])) { 0259 unset($params[$key]); 0260 } 0261 } 0262 0263 $params += $this->_defaults; 0264 0265 $url = ''; 0266 0267 if ($this->_moduleValid || array_key_exists($this->_moduleKey, $data)) { 0268 if ($params[$this->_moduleKey] != $this->_defaults[$this->_moduleKey]) { 0269 $module = $params[$this->_moduleKey]; 0270 } 0271 } 0272 unset($params[$this->_moduleKey]); 0273 0274 $controller = $params[$this->_controllerKey]; 0275 unset($params[$this->_controllerKey]); 0276 0277 // set $action if value given is 'new' or 'edit' 0278 if (in_array($params[$this->_actionKey], array('new', 'edit'))) { 0279 $action = $params[$this->_actionKey]; 0280 } 0281 unset($params[$this->_actionKey]); 0282 0283 if (isset($params['index']) && $params['index']) { 0284 unset($params['index']); 0285 $url .= '/index'; 0286 if (isset($params['id'])) { 0287 $url .= '/'.$params['id']; 0288 unset($params['id']); 0289 } 0290 foreach ($params as $key => $value) { 0291 if ($encode) $value = urlencode($value); 0292 $url .= '/' . $key . '/' . $value; 0293 } 0294 } elseif (! empty($action) && isset($params['id'])) { 0295 $url .= sprintf('/%s/%s', $params['id'], $action); 0296 } elseif (! empty($action)) { 0297 $url .= sprintf('/%s', $action); 0298 } elseif (isset($params['id'])) { 0299 $url .= '/' . $params['id']; 0300 } 0301 0302 if (!empty($url) || $controller !== $this->_defaults[$this->_controllerKey]) { 0303 $url = '/' . $controller . $url; 0304 } 0305 0306 if (isset($module)) { 0307 $url = '/' . $module . $url; 0308 } 0309 0310 return ltrim($url, self::URI_DELIMITER); 0311 } 0312 0313 /** 0314 * Tells Rewrite Router which version this Route is 0315 * 0316 * @return int Route "version" 0317 */ 0318 public function getVersion() 0319 { 0320 return 2; 0321 } 0322 0323 /** 0324 * Parses the responders array sent to constructor to know 0325 * which modules and/or controllers are RESTful 0326 * 0327 * @param array $responders 0328 */ 0329 protected function _parseResponders($responders) 0330 { 0331 $modulesOnly = true; 0332 foreach ($responders as $responder) { 0333 if(is_array($responder)) { 0334 $modulesOnly = false; 0335 break; 0336 } 0337 } 0338 if ($modulesOnly) { 0339 $this->_restfulModules = $responders; 0340 } else { 0341 $this->_restfulControllers = $responders; 0342 } 0343 } 0344 0345 /** 0346 * Determine if a specified module supports RESTful routing 0347 * 0348 * @param string $moduleName 0349 * @return bool 0350 */ 0351 protected function _checkRestfulModule($moduleName) 0352 { 0353 if ($this->_allRestful()) { 0354 return true; 0355 } 0356 if ($this->_fullRestfulModule($moduleName)) { 0357 return true; 0358 } 0359 if ($this->_restfulControllers && array_key_exists($moduleName, $this->_restfulControllers)) { 0360 return true; 0361 } 0362 return false; 0363 } 0364 0365 /** 0366 * Determine if a specified module + controller combination supports 0367 * RESTful routing 0368 * 0369 * @param string $moduleName 0370 * @param string $controllerName 0371 * @return bool 0372 */ 0373 protected function _checkRestfulController($moduleName, $controllerName) 0374 { 0375 if ($this->_allRestful()) { 0376 return true; 0377 } 0378 if ($this->_fullRestfulModule($moduleName)) { 0379 return true; 0380 } 0381 if ($this->_checkRestfulModule($moduleName) 0382 && $this->_restfulControllers 0383 && (false !== array_search($controllerName, $this->_restfulControllers[$moduleName])) 0384 ) { 0385 return true; 0386 } 0387 return false; 0388 } 0389 0390 /** 0391 * Determines if RESTful routing applies to the entire app 0392 * 0393 * @return bool 0394 */ 0395 protected function _allRestful() 0396 { 0397 return (!$this->_restfulModules && !$this->_restfulControllers); 0398 } 0399 0400 /** 0401 * Determines if RESTful routing applies to an entire module 0402 * 0403 * @param string $moduleName 0404 * @return bool 0405 */ 0406 protected function _fullRestfulModule($moduleName) 0407 { 0408 return ( 0409 $this->_restfulModules 0410 && (false !==array_search($moduleName, $this->_restfulModules)) 0411 ); 0412 } 0413 }