PageRenderTime 53ms CodeModel.GetById 26ms RepoModel.GetById 0ms app.codeStats 0ms

/Zend/Rest/Route.php

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