web/lib/Zend/Rest/Route.php
changeset 64 162c1de6545a
parent 19 1c2f13fd785c
child 68 ecaf28ffe26e
equal deleted inserted replaced
63:5b37998e522e 64:162c1de6545a
       
     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 /**
       
    23  * @see Zend_Controller_Router_Route_Interface
       
    24  */
       
    25 require_once 'Zend/Controller/Router/Route/Interface.php';
       
    26 
       
    27 /**
       
    28  * @see Zend_Controller_Router_Route_Module
       
    29  */
       
    30 require_once 'Zend/Controller/Router/Route/Module.php';
       
    31 
       
    32 /**
       
    33  * @see Zend_Controller_Dispatcher_Interface
       
    34  */
       
    35 require_once 'Zend/Controller/Dispatcher/Interface.php';
       
    36 
       
    37 /**
       
    38  * @see Zend_Controller_Request_Abstract
       
    39  */
       
    40 require_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  */
       
    52 class 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         // set $action if value given is 'new' or 'edit'
       
   278         if (in_array($params[$this->_actionKey], array('new', 'edit'))) {
       
   279             $action = $params[$this->_actionKey];
       
   280         }
       
   281         unset($params[$this->_actionKey]);
       
   282 
       
   283         if (isset($params['index']) && $params['index']) {
       
   284             unset($params['index']);
       
   285             $url .= '/index';
       
   286             if (isset($params['id'])) {
       
   287                 $url .= '/'.$params['id'];
       
   288                 unset($params['id']);
       
   289             }
       
   290             foreach ($params as $key => $value) {
       
   291                 if ($encode) $value = urlencode($value);
       
   292                 $url .= '/' . $key . '/' . $value;
       
   293             }
       
   294         } elseif (! empty($action) && isset($params['id'])) {
       
   295             $url .= sprintf('/%s/%s', $params['id'], $action);
       
   296         } elseif (! empty($action)) {
       
   297             $url .= sprintf('/%s', $action);
       
   298         } elseif (isset($params['id'])) {
       
   299             $url .= '/' . $params['id'];
       
   300         }
       
   301 
       
   302         if (!empty($url) || $controller !== $this->_defaults[$this->_controllerKey]) {
       
   303             $url = '/' . $controller . $url;
       
   304         }
       
   305 
       
   306         if (isset($module)) {
       
   307             $url = '/' . $module . $url;
       
   308         }
       
   309 
       
   310         return ltrim($url, self::URI_DELIMITER);
       
   311     }
       
   312 
       
   313     /**
       
   314      * Tells Rewrite Router which version this Route is
       
   315      *
       
   316      * @return int Route "version"
       
   317      */
       
   318     public function getVersion()
       
   319     {
       
   320         return 2;
       
   321     }
       
   322 
       
   323     /**
       
   324      * Parses the responders array sent to constructor to know
       
   325      * which modules and/or controllers are RESTful
       
   326      *
       
   327      * @param array $responders
       
   328      */
       
   329     protected function _parseResponders($responders)
       
   330     {
       
   331         $modulesOnly = true;
       
   332         foreach ($responders as $responder) {
       
   333             if(is_array($responder)) {
       
   334                 $modulesOnly = false;
       
   335                 break;
       
   336             }
       
   337         }
       
   338         if ($modulesOnly) {
       
   339             $this->_restfulModules = $responders;
       
   340         } else {
       
   341             $this->_restfulControllers = $responders;
       
   342         }
       
   343     }
       
   344 
       
   345     /**
       
   346      * Determine if a specified module supports RESTful routing
       
   347      *
       
   348      * @param string $moduleName
       
   349      * @return bool
       
   350      */
       
   351     protected function _checkRestfulModule($moduleName)
       
   352     {
       
   353         if ($this->_allRestful()) {
       
   354             return true;
       
   355         }
       
   356         if ($this->_fullRestfulModule($moduleName)) {
       
   357             return true;
       
   358         }
       
   359         if ($this->_restfulControllers && array_key_exists($moduleName, $this->_restfulControllers)) {
       
   360             return true;
       
   361         }
       
   362         return false;
       
   363     }
       
   364 
       
   365     /**
       
   366      * Determine if a specified module + controller combination supports
       
   367      * RESTful routing
       
   368      *
       
   369      * @param string $moduleName
       
   370      * @param string $controllerName
       
   371      * @return bool
       
   372      */
       
   373     protected function _checkRestfulController($moduleName, $controllerName)
       
   374     {
       
   375         if ($this->_allRestful()) {
       
   376             return true;
       
   377         }
       
   378         if ($this->_fullRestfulModule($moduleName)) {
       
   379             return true;
       
   380         }
       
   381         if ($this->_checkRestfulModule($moduleName)
       
   382             && $this->_restfulControllers
       
   383             && (false !== array_search($controllerName, $this->_restfulControllers[$moduleName]))
       
   384         ) {
       
   385             return true;
       
   386         }
       
   387         return false;
       
   388     }
       
   389 
       
   390     /**
       
   391      * Determines if RESTful routing applies to the entire app
       
   392      *
       
   393      * @return bool
       
   394      */
       
   395     protected function _allRestful()
       
   396     {
       
   397         return (!$this->_restfulModules && !$this->_restfulControllers);
       
   398     }
       
   399 
       
   400     /**
       
   401      * Determines if RESTful routing applies to an entire module
       
   402      *
       
   403      * @param string $moduleName
       
   404      * @return bool
       
   405      */
       
   406     protected function _fullRestfulModule($moduleName)
       
   407     {
       
   408         return (
       
   409             $this->_restfulModules
       
   410             && (false !==array_search($moduleName, $this->_restfulModules))
       
   411         );
       
   412     }
       
   413 }