PageRenderTime 35ms CodeModel.GetById 23ms app.highlight 8ms RepoModel.GetById 2ms app.codeStats 0ms

/framework/vendor/zend/Zend/Rest/Route.php

http://zoop.googlecode.com/
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}