/framework/vendor/zend/Zend/Rest/Route.php
PHP | 401 lines | 229 code | 44 blank | 128 comment | 60 complexity | e67ab8811433f7b8c592f963cbc4aa05 MD5 | raw file
1<?php 2/** 3 * Zend Framework 4 * 5 * LICENSE 6 * 7 * This source file is subject to the new BSD license that is bundled 8 * with this package in the file LICENSE.txt. 9 * It is also available through the world-wide-web at this URL: 10 * http://framework.zend.com/license/new-bsd 11 * If you did not receive a copy of the license and are unable to 12 * obtain it through the world-wide-web, please send an email 13 * to license@zend.com so we can send you a copy immediately. 14 * 15 * @category Zend 16 * @package Zend_Rest 17 * @copyright Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com) 18 * @license http://framework.zend.com/license/new-bsd New BSD License 19 * @version $Id: Route.php 20096 2010-01-06 02:05:09Z bkarwin $ 20 */ 21 22/** 23 * @see Zend_Controller_Router_Route_Interface 24 */ 25require_once 'Zend/Controller/Router/Route/Interface.php'; 26 27/** 28 * @see Zend_Controller_Router_Route_Module 29 */ 30require_once 'Zend/Controller/Router/Route/Module.php'; 31 32/** 33 * @see Zend_Controller_Dispatcher_Interface 34 */ 35require_once 'Zend/Controller/Dispatcher/Interface.php'; 36 37/** 38 * @see Zend_Controller_Request_Abstract 39 */ 40require_once 'Zend/Controller/Request/Abstract.php'; 41 42/** 43 * Rest Route 44 * 45 * Request-aware route for RESTful modular routing 46 * 47 * @category Zend 48 * @package Zend_Rest 49 * @copyright Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com) 50 * @license http://framework.zend.com/license/new-bsd New BSD License 51 */ 52class Zend_Rest_Route extends Zend_Controller_Router_Route_Module 53{ 54 /** 55 * Specific Modules to receive RESTful routes 56 * @var array 57 */ 58 protected $_restfulModules = null; 59 60 /** 61 * Specific Modules=>Controllers to receive RESTful routes 62 * @var array 63 */ 64 protected $_restfulControllers = null; 65 66 /** 67 * @var Zend_Controller_Front 68 */ 69 protected $_front; 70 71 /** 72 * Constructor 73 * 74 * @param Zend_Controller_Front $front Front Controller object 75 * @param array $defaults Defaults for map variables with keys as variable names 76 * @param array $responders Modules or controllers to receive RESTful routes 77 */ 78 public function __construct(Zend_Controller_Front $front, 79 array $defaults = array(), 80 array $responders = array() 81 ) { 82 $this->_defaults = $defaults; 83 84 if ($responders) { 85 $this->_parseResponders($responders); 86 } 87 88 $this->_front = $front; 89 $this->_dispatcher = $front->getDispatcher(); 90 } 91 92 /** 93 * Instantiates route based on passed Zend_Config structure 94 */ 95 public static function getInstance(Zend_Config $config) 96 { 97 $frontController = Zend_Controller_Front::getInstance(); 98 $defaultsArray = array(); 99 $restfulConfigArray = array(); 100 foreach ($config as $key => $values) { 101 if ($key == 'type') { 102 // do nothing 103 } elseif ($key == 'defaults') { 104 $defaultsArray = $values->toArray(); 105 } else { 106 $restfulConfigArray[$key] = explode(',', $values); 107 } 108 } 109 $instance = new self($frontController, $defaultsArray, $restfulConfigArray); 110 return $instance; 111 } 112 113 /** 114 * Matches a user submitted request. Assigns and returns an array of variables 115 * on a successful match. 116 * 117 * If a request object is registered, it uses its setModuleName(), 118 * setControllerName(), and setActionName() accessors to set those values. 119 * Always returns the values as an array. 120 * 121 * @param Zend_Controller_Request_Http $request Request used to match against this routing ruleset 122 * @return array An array of assigned values or a false on a mismatch 123 */ 124 public function match($request, $partial = false) 125 { 126 if (!$request instanceof Zend_Controller_Request_Http) { 127 $request = $this->_front->getRequest(); 128 } 129 $this->_request = $request; 130 $this->_setRequestKeys(); 131 132 $path = $request->getPathInfo(); 133 $params = $request->getParams(); 134 $values = array(); 135 $path = trim($path, self::URI_DELIMITER); 136 137 if ($path != '') { 138 139 $path = explode(self::URI_DELIMITER, $path); 140 // Determine Module 141 $moduleName = $this->_defaults[$this->_moduleKey]; 142 $dispatcher = $this->_front->getDispatcher(); 143 if ($dispatcher && $dispatcher->isValidModule($path[0])) { 144 $moduleName = $path[0]; 145 if ($this->_checkRestfulModule($moduleName)) { 146 $values[$this->_moduleKey] = array_shift($path); 147 $this->_moduleValid = true; 148 } 149 } 150 151 // Determine Controller 152 $controllerName = $this->_defaults[$this->_controllerKey]; 153 if (count($path) && !empty($path[0])) { 154 if ($this->_checkRestfulController($moduleName, $path[0])) { 155 $controllerName = $path[0]; 156 $values[$this->_controllerKey] = array_shift($path); 157 $values[$this->_actionKey] = 'get'; 158 } else { 159 // If Controller in URI is not found to be a RESTful 160 // Controller, return false to fall back to other routes 161 return false; 162 } 163 } elseif ($this->_checkRestfulController($moduleName, $controllerName)) { 164 $values[$this->_controllerKey] = $controllerName; 165 $values[$this->_actionKey] = 'get'; 166 } else { 167 return false; 168 } 169 170 //Store path count for method mapping 171 $pathElementCount = count($path); 172 173 // Check for "special get" URI's 174 $specialGetTarget = false; 175 if ($pathElementCount && array_search($path[0], array('index', 'new')) > -1) { 176 $specialGetTarget = array_shift($path); 177 } elseif ($pathElementCount && $path[$pathElementCount-1] == 'edit') { 178 $specialGetTarget = 'edit'; 179 $params['id'] = $path[$pathElementCount-2]; 180 } elseif ($pathElementCount == 1) { 181 $params['id'] = urldecode(array_shift($path)); 182 } elseif ($pathElementCount == 0 && !isset($params['id'])) { 183 $specialGetTarget = 'index'; 184 } 185 186 // Digest URI params 187 if ($numSegs = count($path)) { 188 for ($i = 0; $i < $numSegs; $i = $i + 2) { 189 $key = urldecode($path[$i]); 190 $val = isset($path[$i + 1]) ? urldecode($path[$i + 1]) : null; 191 $params[$key] = $val; 192 } 193 } 194 195 // Determine Action 196 $requestMethod = strtolower($request->getMethod()); 197 if ($requestMethod != 'get') { 198 if ($request->getParam('_method')) { 199 $values[$this->_actionKey] = strtolower($request->getParam('_method')); 200 } elseif ( $request->getHeader('X-HTTP-Method-Override') ) { 201 $values[$this->_actionKey] = strtolower($request->getHeader('X-HTTP-Method-Override')); 202 } else { 203 $values[$this->_actionKey] = $requestMethod; 204 } 205 206 // Map PUT and POST to actual create/update actions 207 // based on parameter count (posting to resource or collection) 208 switch( $values[$this->_actionKey] ){ 209 case 'post': 210 if ($pathElementCount > 0) { 211 $values[$this->_actionKey] = 'put'; 212 } else { 213 $values[$this->_actionKey] = 'post'; 214 } 215 break; 216 case 'put': 217 $values[$this->_actionKey] = 'put'; 218 break; 219 } 220 221 } elseif ($specialGetTarget) { 222 $values[$this->_actionKey] = $specialGetTarget; 223 } 224 225 } 226 $this->_values = $values + $params; 227 228 $result = $this->_values + $this->_defaults; 229 230 if ($partial && $result) 231 $this->setMatchedPath($request->getPathInfo()); 232 233 return $result; 234 } 235 236 /** 237 * Assembles user submitted parameters forming a URL path defined by this route 238 * 239 * @param array $data An array of variable and value pairs used as parameters 240 * @param bool $reset Weither to reset the current params 241 * @param bool $encode Weither to return urlencoded string 242 * @return string Route path with user submitted parameters 243 */ 244 public function assemble($data = array(), $reset = false, $encode = true) 245 { 246 if (!$this->_keysSet) { 247 if (null === $this->_request) { 248 $this->_request = $this->_front->getRequest(); 249 } 250 $this->_setRequestKeys(); 251 } 252 253 $params = (!$reset) ? $this->_values : array(); 254 255 foreach ($data as $key => $value) { 256 if ($value !== null) { 257 $params[$key] = $value; 258 } elseif (isset($params[$key])) { 259 unset($params[$key]); 260 } 261 } 262 263 $params += $this->_defaults; 264 265 $url = ''; 266 267 if ($this->_moduleValid || array_key_exists($this->_moduleKey, $data)) { 268 if ($params[$this->_moduleKey] != $this->_defaults[$this->_moduleKey]) { 269 $module = $params[$this->_moduleKey]; 270 } 271 } 272 unset($params[$this->_moduleKey]); 273 274 $controller = $params[$this->_controllerKey]; 275 unset($params[$this->_controllerKey]); 276 277 unset($params[$this->_actionKey]); 278 279 if (isset($params['index']) && $params['index']) { 280 unset($params['index']); 281 $url .= '/index'; 282 foreach ($params as $key => $value) { 283 if ($encode) $value = urlencode($value); 284 $url .= '/' . $key . '/' . $value; 285 } 286 } elseif (isset($params['id'])) { 287 $url .= '/' . $params['id']; 288 } 289 290 if (!empty($url) || $controller !== $this->_defaults[$this->_controllerKey]) { 291 $url = '/' . $controller . $url; 292 } 293 294 if (isset($module)) { 295 $url = '/' . $module . $url; 296 } 297 298 return ltrim($url, self::URI_DELIMITER); 299 } 300 301 /** 302 * Tells Rewrite Router which version this Route is 303 * 304 * @return int Route "version" 305 */ 306 public function getVersion() 307 { 308 return 2; 309 } 310 311 /** 312 * Parses the responders array sent to constructor to know 313 * which modules and/or controllers are RESTful 314 * 315 * @param array $responders 316 */ 317 protected function _parseResponders($responders) 318 { 319 $modulesOnly = true; 320 foreach ($responders as $responder) { 321 if(is_array($responder)) { 322 $modulesOnly = false; 323 break; 324 } 325 } 326 if ($modulesOnly) { 327 $this->_restfulModules = $responders; 328 } else { 329 $this->_restfulControllers = $responders; 330 } 331 } 332 333 /** 334 * Determine if a specified module supports RESTful routing 335 * 336 * @param string $moduleName 337 * @return bool 338 */ 339 protected function _checkRestfulModule($moduleName) 340 { 341 if ($this->_allRestful()) { 342 return true; 343 } 344 if ($this->_fullRestfulModule($moduleName)) { 345 return true; 346 } 347 if ($this->_restfulControllers && array_key_exists($moduleName, $this->_restfulControllers)) { 348 return true; 349 } 350 return false; 351 } 352 353 /** 354 * Determine if a specified module + controller combination supports 355 * RESTful routing 356 * 357 * @param string $moduleName 358 * @param string $controllerName 359 * @return bool 360 */ 361 protected function _checkRestfulController($moduleName, $controllerName) 362 { 363 if ($this->_allRestful()) { 364 return true; 365 } 366 if ($this->_fullRestfulModule($moduleName)) { 367 return true; 368 } 369 if ($this->_checkRestfulModule($moduleName) 370 && $this->_restfulControllers 371 && (false !== array_search($controllerName, $this->_restfulControllers[$moduleName])) 372 ) { 373 return true; 374 } 375 return false; 376 } 377 378 /** 379 * Determines if RESTful routing applies to the entire app 380 * 381 * @return bool 382 */ 383 protected function _allRestful() 384 { 385 return (!$this->_restfulModules && !$this->_restfulControllers); 386 } 387 388 /** 389 * Determines if RESTful routing applies to an entire module 390 * 391 * @param string $moduleName 392 * @return bool 393 */ 394 protected function _fullRestfulModule($moduleName) 395 { 396 return ( 397 $this->_restfulModules 398 && (false !==array_search($moduleName, $this->_restfulModules)) 399 ); 400 } 401}