程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> 網頁編程 >> PHP編程 >> PHP綜合 >> Zend Framework教程之路由功能Zend_Controller_Router詳解

Zend Framework教程之路由功能Zend_Controller_Router詳解

編輯:PHP綜合

本文實例講述了Zend Framework教程之路由功能Zend_Controller_Router用法。分享給大家供大家參考,具體如下:

Zend Framework的路由提供了兩個主要功能路由和創建路由。

Zend_Controller_Router的Route類和相應Route目錄下的類定義常見的路由操作。

接口Zend_Controller_Router_Interface,類Zend_Controller_Router_Abstract和Zend_Controller_Router_Rewrite完成了基本的路由,創建路由,刪除路由的功能。

└── Router
    ├── Abstract.php
    ├── Exception.php
    ├── Interface.php
    ├── Rewrite.php
    ├── Route
    │   ├── Abstract.php
    │   ├── Chain.php
    │   ├── Hostname.php
    │   ├── Interface.php
    │   ├── Module.php
    │   ├── Regex.php
    │   └── Static.php
    └── Route.php

Zend_Controller_Router路由功能的實現

Zend_Controller_Router_Interface

<?php
interface Zend_Controller_Router_Interface
{
  /**
   * Processes a request and sets its controller and action. If
   * no route was possible, an exception is thrown.
   *
   * @param Zend_Controller_Request_Abstract
   * @throws Zend_Controller_Router_Exception
   * @return Zend_Controller_Request_Abstract|boolean
   */
  public function route(Zend_Controller_Request_Abstract $dispatcher);
  /**
   * Generates a URL path that can be used in URL creation, redirection, etc.
   *
   * May be passed user params to override ones from URI, Request or even defaults.
   * If passed parameter has a value of null, it's URL variable will be reset to
   * default.
   *
   * If null is passed as a route name assemble will use the current Route or 'default'
   * if current is not yet set.
   *
   * Reset is used to signal that all parameters should be reset to it's defaults.
   * Ignoring all URL specified values. User specified params still get precedence.
   *
   * Encode tells to url encode resulting path parts.
   *
   * @param array $userParams Options passed by a user used to override parameters
   * @param mixed $name The name of a Route to use
   * @param bool $reset Whether to reset to the route defaults ignoring URL params
   * @param bool $encode Tells to encode URL parts on output
   * @throws Zend_Controller_Router_Exception
   * @return string Resulting URL path
   */
  public function assemble($userParams, $name = null, $reset = false, $encode = true);
  /**
   * Retrieve Front Controller
   *
   * @return Zend_Controller_Front
   */
  public function getFrontController();
  /**
   * Set Front Controller
   *
   * @param Zend_Controller_Front $controller
   * @return Zend_Controller_Router_Interface
   */
  public function setFrontController(Zend_Controller_Front $controller);
  /**
   * Add or modify a parameter with which to instantiate any helper objects
   *
   * @param string $name
   * @param mixed $param
   * @return Zend_Controller_Router_Interface
   */
  public function setParam($name, $value);
  /**
   * Set an array of a parameters to pass to helper object constructors
   *
   * @param array $params
   * @return Zend_Controller_Router_Interface
   */
  public function setParams(array $params);
  /**
   * Retrieve a single parameter from the controller parameter stack
   *
   * @param string $name
   * @return mixed
   */
  public function getParam($name);
  /**
   * Retrieve the parameters to pass to helper object constructors
   *
   * @return array
   */
  public function getParams();
  /**
   * Clear the controller parameter stack
   *
   * By default, clears all parameters. If a parameter name is given, clears
   * only that parameter; if an array of parameter names is provided, clears
   * each.
   *
   * @param null|string|array single key or array of keys for params to clear
   * @return Zend_Controller_Router_Interface
   */
  public function clearParams($name = null);
}

Zend_Controller_Router_Abstract

<?php
/** Zend_Controller_Router_Interface */
require_once 'Zend/Controller/Router/Interface.php';
abstract class Zend_Controller_Router_Abstract implements Zend_Controller_Router_Interface
{
  /**
   * URI delimiter
   */
  const URI_DELIMITER = '/';
  /**
   * Front controller instance
   * @var Zend_Controller_Front
   */
  protected $_frontController;
  /**
   * Array of invocation parameters to use when instantiating action
   * controllers
   * @var array
   */
  protected $_invokeParams = array();
  /**
   * Constructor
   *
   * @param array $params
   * @return void
   */
  public function __construct(array $params = array())
  {
    $this->setParams($params);
  }
  /**
   * Add or modify a parameter to use when instantiating an action controller
   *
   * @param string $name
   * @param mixed $value
   * @return Zend_Controller_Router
   */
  public function setParam($name, $value)
  {
    $name = (string) $name;
    $this->_invokeParams[$name] = $value;
    return $this;
  }
  /**
   * Set parameters to pass to action controller constructors
   *
   * @param array $params
   * @return Zend_Controller_Router
   */
  public function setParams(array $params)
  {
    $this->_invokeParams = array_merge($this->_invokeParams, $params);
    return $this;
  }
  /**
   * Retrieve a single parameter from the controller parameter stack
   *
   * @param string $name
   * @return mixed
   */
  public function getParam($name)
  {
    if(isset($this->_invokeParams[$name])) {
      return $this->_invokeParams[$name];
    }
    return null;
  }
  /**
   * Retrieve action controller instantiation parameters
   *
   * @return array
   */
  public function getParams()
  {
    return $this->_invokeParams;
  }
  /**
   * Clear the controller parameter stack
   *
   * By default, clears all parameters. If a parameter name is given, clears
   * only that parameter; if an array of parameter names is provided, clears
   * each.
   *
   * @param null|string|array single key or array of keys for params to clear
   * @return Zend_Controller_Router
   */
  public function clearParams($name = null)
  {
    if (null === $name) {
      $this->_invokeParams = array();
    } elseif (is_string($name) && isset($this->_invokeParams[$name])) {
      unset($this->_invokeParams[$name]);
    } elseif (is_array($name)) {
      foreach ($name as $key) {
        if (is_string($key) && isset($this->_invokeParams[$key])) {
          unset($this->_invokeParams[$key]);
        }
      }
    }
    return $this;
  }
  /**
   * Retrieve Front Controller
   *
   * @return Zend_Controller_Front
   */
  public function getFrontController()
  {
    // Used cache version if found
    if (null !== $this->_frontController) {
      return $this->_frontController;
    }
    require_once 'Zend/Controller/Front.php';
    $this->_frontController = Zend_Controller_Front::getInstance();
    return $this->_frontController;
  }
  /**
   * Set Front Controller
   *
   * @param Zend_Controller_Front $controller
   * @return Zend_Controller_Router_Interface
   */
  public function setFrontController(Zend_Controller_Front $controller)
  {
    $this->_frontController = $controller;
    return $this;
  }
}

Zend_Controller_Router_Rewrite

<?php
/** Zend_Controller_Router_Abstract */
require_once 'Zend/Controller/Router/Abstract.php';
/** Zend_Controller_Router_Route */
require_once 'Zend/Controller/Router/Route.php';
class Zend_Controller_Router_Rewrite extends Zend_Controller_Router_Abstract
{
  /**
   * Whether or not to use default routes
   *
   * @var boolean
   */
  protected $_useDefaultRoutes = true;
  /**
   * Array of routes to match against
   *
   * @var array
   */
  protected $_routes = array();
  /**
   * Currently matched route
   *
   * @var Zend_Controller_Router_Route_Interface
   */
  protected $_currentRoute = null;
  /**
   * Global parameters given to all routes
   *
   * @var array
   */
  protected $_globalParams = array();
  /**
   * Separator to use with chain names
   *
   * @var string
   */
  protected $_chainNameSeparator = '-';
  /**
   * Determines if request parameters should be used as global parameters
   * inside this router.
   *
   * @var boolean
   */
  protected $_useCurrentParamsAsGlobal = false;
  /**
   * Add default routes which are used to mimic basic router behaviour
   *
   * @return Zend_Controller_Router_Rewrite
   */
  public function addDefaultRoutes()
  {
    if (!$this->hasRoute('default')) {
      $dispatcher = $this->getFrontController()->getDispatcher();
      $request = $this->getFrontController()->getRequest();
      require_once 'Zend/Controller/Router/Route/Module.php';
      $compat = new Zend_Controller_Router_Route_Module(array(), $dispatcher, $request);
      $this->_routes = array('default' => $compat) + $this->_routes;
    }
    return $this;
  }
  /**
   * Add route to the route chain
   *
   * If route contains method setRequest(), it is initialized with a request object
   *
   * @param string                 $name    Name of the route
   * @param Zend_Controller_Router_Route_Interface $route   Instance of the route
   * @return Zend_Controller_Router_Rewrite
   */
  public function addRoute($name, Zend_Controller_Router_Route_Interface $route)
  {
    if (method_exists($route, 'setRequest')) {
      $route->setRequest($this->getFrontController()->getRequest());
    }
    $this->_routes[$name] = $route;
    return $this;
  }
  /**
   * Add routes to the route chain
   *
   * @param array $routes Array of routes with names as keys and routes as values
   * @return Zend_Controller_Router_Rewrite
   */
  public function addRoutes($routes) {
    foreach ($routes as $name => $route) {
      $this->addRoute($name, $route);
    }
    return $this;
  }
  /**
   * Create routes out of Zend_Config configuration
   *
   * Example INI:
   * routes.archive.route = "archive/:year/*"
   * routes.archive.defaults.controller = archive
   * routes.archive.defaults.action = show
   * routes.archive.defaults.year = 2000
   * routes.archive.reqs.year = "\d+"
   *
   * routes.news.type = "Zend_Controller_Router_Route_Static"
   * routes.news.route = "news"
   * routes.news.defaults.controller = "news"
   * routes.news.defaults.action = "list"
   *
   * And finally after you have created a Zend_Config with above ini:
   * $router = new Zend_Controller_Router_Rewrite();
   * $router->addConfig($config, 'routes');
   *
   * @param Zend_Config $config Configuration object
   * @param string   $section Name of the config section containing route's definitions
   * @throws Zend_Controller_Router_Exception
   * @return Zend_Controller_Router_Rewrite
   */
  public function addConfig(Zend_Config $config, $section = null)
  {
    if ($section !== null) {
      if ($config->{$section} === null) {
        require_once 'Zend/Controller/Router/Exception.php';
        throw new Zend_Controller_Router_Exception("No route configuration in section '{$section}'");
      }
      $config = $config->{$section};
    }
    foreach ($config as $name => $info) {
      $route = $this->_getRouteFromConfig($info);
      if ($route instanceof Zend_Controller_Router_Route_Chain) {
        if (!isset($info->chain)) {
          require_once 'Zend/Controller/Router/Exception.php';
          throw new Zend_Controller_Router_Exception("No chain defined");
        }
        if ($info->chain instanceof Zend_Config) {
          $childRouteNames = $info->chain;
        } else {
          $childRouteNames = explode(',', $info->chain);
        }
        foreach ($childRouteNames as $childRouteName) {
          $childRoute = $this->getRoute(trim($childRouteName));
          $route->chain($childRoute);
        }
        $this->addRoute($name, $route);
      } elseif (isset($info->chains) && $info->chains instanceof Zend_Config) {
        $this->_addChainRoutesFromConfig($name, $route, $info->chains);
      } else {
        $this->addRoute($name, $route);
      }
    }
    return $this;
  }
  /**
   * Get a route frm a config instance
   *
   * @param Zend_Config $info
   * @return Zend_Controller_Router_Route_Interface
   */
  protected function _getRouteFromConfig(Zend_Config $info)
  {
    $class = (isset($info->type)) ? $info->type : 'Zend_Controller_Router_Route';
    if (!class_exists($class)) {
      require_once 'Zend/Loader.php';
      Zend_Loader::loadClass($class);
    }
    $route = call_user_func(array($class, 'getInstance'), $info);
    if (isset($info->abstract) && $info->abstract && method_exists($route, 'isAbstract')) {
      $route->isAbstract(true);
    }
    return $route;
  }
  /**
   * Add chain routes from a config route
   *
   * @param string                 $name
   * @param Zend_Controller_Router_Route_Interface $route
   * @param Zend_Config              $childRoutesInfo
   * @return void
   */
  protected function _addChainRoutesFromConfig($name,
                         Zend_Controller_Router_Route_Interface $route,
                         Zend_Config $childRoutesInfo)
  {
    foreach ($childRoutesInfo as $childRouteName => $childRouteInfo) {
      if (is_string($childRouteInfo)) {
        $childRouteName = $childRouteInfo;
        $childRoute   = $this->getRoute($childRouteName);
      } else {
        $childRoute = $this->_getRouteFromConfig($childRouteInfo);
      }
      if ($route instanceof Zend_Controller_Router_Route_Chain) {
        $chainRoute = clone $route;
        $chainRoute->chain($childRoute);
      } else {
        $chainRoute = $route->chain($childRoute);
      }
      $chainName = $name . $this->_chainNameSeparator . $childRouteName;
      if (isset($childRouteInfo->chains)) {
        $this->_addChainRoutesFromConfig($chainName, $chainRoute, $childRouteInfo->chains);
      } else {
        $this->addRoute($chainName, $chainRoute);
      }
    }
  }
  /**
   * Remove a route from the route chain
   *
   * @param string $name Name of the route
   * @throws Zend_Controller_Router_Exception
   * @return Zend_Controller_Router_Rewrite
   */
  public function removeRoute($name)
  {
    if (!isset($this->_routes[$name])) {
      require_once 'Zend/Controller/Router/Exception.php';
      throw new Zend_Controller_Router_Exception("Route $name is not defined");
    }
    unset($this->_routes[$name]);
    return $this;
  }
  /**
   * Remove all standard default routes
   *
   * @param Zend_Controller_Router_Route_Interface Route
   * @return Zend_Controller_Router_Rewrite
   */
  public function removeDefaultRoutes()
  {
    $this->_useDefaultRoutes = false;
    return $this;
  }
  /**
   * Check if named route exists
   *
   * @param string $name Name of the route
   * @return boolean
   */
  public function hasRoute($name)
  {
    return isset($this->_routes[$name]);
  }
  /**
   * Retrieve a named route
   *
   * @param string $name Name of the route
   * @throws Zend_Controller_Router_Exception
   * @return Zend_Controller_Router_Route_Interface Route object
   */
  public function getRoute($name)
  {
    if (!isset($this->_routes[$name])) {
      require_once 'Zend/Controller/Router/Exception.php';
      throw new Zend_Controller_Router_Exception("Route $name is not defined");
    }
    return $this->_routes[$name];
  }
  /**
   * Retrieve a currently matched route
   *
   * @throws Zend_Controller_Router_Exception
   * @return Zend_Controller_Router_Route_Interface Route object
   */
  public function getCurrentRoute()
  {
    if (!isset($this->_currentRoute)) {
      require_once 'Zend/Controller/Router/Exception.php';
      throw new Zend_Controller_Router_Exception("Current route is not defined");
    }
    return $this->getRoute($this->_currentRoute);
  }
  /**
   * Retrieve a name of currently matched route
   *
   * @throws Zend_Controller_Router_Exception
   * @return Zend_Controller_Router_Route_Interface Route object
   */
  public function getCurrentRouteName()
  {
    if (!isset($this->_currentRoute)) {
      require_once 'Zend/Controller/Router/Exception.php';
      throw new Zend_Controller_Router_Exception("Current route is not defined");
    }
    return $this->_currentRoute;
  }
  /**
   * Retrieve an array of routes added to the route chain
   *
   * @return array All of the defined routes
   */
  public function getRoutes()
  {
    return $this->_routes;
  }
  /**
   * Find a matching route to the current PATH_INFO and inject
   * returning values to the Request object.
   *
   * @throws Zend_Controller_Router_Exception
   * @return Zend_Controller_Request_Abstract Request object
   */
  public function route(Zend_Controller_Request_Abstract $request)
  {
    if (!$request instanceof Zend_Controller_Request_Http) {
      require_once 'Zend/Controller/Router/Exception.php';
      throw new Zend_Controller_Router_Exception('Zend_Controller_Router_Rewrite requires a Zend_Controller_Request_Http-based request object');
    }
    if ($this->_useDefaultRoutes) {
      $this->addDefaultRoutes();
    }
    // Find the matching route
    $routeMatched = false;
    foreach (array_reverse($this->_routes, true) as $name => $route) {
      // TODO: Should be an interface method. Hack for 1.0 BC
      if (method_exists($route, 'isAbstract') && $route->isAbstract()) {
        continue;
      }
      // TODO: Should be an interface method. Hack for 1.0 BC
      if (!method_exists($route, 'getVersion') || $route->getVersion() == 1) {
        $match = $request->getPathInfo();
      } else {
        $match = $request;
      }
      if ($params = $route->match($match)) {
        $this->_setRequestParams($request, $params);
        $this->_currentRoute = $name;
        $routeMatched    = true;
        break;
      }
    }
     if (!$routeMatched) {
       require_once 'Zend/Controller/Router/Exception.php';
       throw new Zend_Controller_Router_Exception('No route matched the request', 404);
     }
    if($this->_useCurrentParamsAsGlobal) {
      $params = $request->getParams();
      foreach($params as $param => $value) {
        $this->setGlobalParam($param, $value);
      }
    }
    return $request;
  }
  protected function _setRequestParams($request, $params)
  {
    foreach ($params as $param => $value) {
      $request->setParam($param, $value);
      if ($param === $request->getModuleKey()) {
        $request->setModuleName($value);
      }
      if ($param === $request->getControllerKey()) {
        $request->setControllerName($value);
      }
      if ($param === $request->getActionKey()) {
        $request->setActionName($value);
      }
    }
  }
  /**
   * Generates a URL path that can be used in URL creation, redirection, etc.
   *
   * @param array $userParams Options passed by a user used to override parameters
   * @param mixed $name The name of a Route to use
   * @param bool $reset Whether to reset to the route defaults ignoring URL params
   * @param bool $encode Tells to encode URL parts on output
   * @throws Zend_Controller_Router_Exception
   * @return string Resulting absolute URL path
   */
  public function assemble($userParams, $name = null, $reset = false, $encode = true)
  {
    if (!is_array($userParams)) {
      require_once 'Zend/Controller/Router/Exception.php';
      throw new Zend_Controller_Router_Exception('userParams must be an array');
    }
    if ($name == null) {
      try {
        $name = $this->getCurrentRouteName();
      } catch (Zend_Controller_Router_Exception $e) {
        $name = 'default';
      }
    }
    // Use UNION (+) in order to preserve numeric keys
    $params = $userParams + $this->_globalParams;
    $route = $this->getRoute($name);
    $url  = $route->assemble($params, $reset, $encode);
    if (!preg_match('|^[a-z]+://|', $url)) {
      $url = rtrim($this->getFrontController()->getBaseUrl(), self::URI_DELIMITER) . self::URI_DELIMITER . $url;
    }
    return $url;
  }
  /**
   * Set a global parameter
   *
   * @param string $name
   * @param mixed $value
   * @return Zend_Controller_Router_Rewrite
   */
  public function setGlobalParam($name, $value)
  {
    $this->_globalParams[$name] = $value;
    return $this;
  }
  /**
   * Set the separator to use with chain names
   *
   * @param string $separator The separator to use
   * @return Zend_Controller_Router_Rewrite
   */
  public function setChainNameSeparator($separator) {
    $this->_chainNameSeparator = $separator;
    return $this;
  }
  /**
   * Get the separator to use for chain names
   *
   * @return string
   */
  public function getChainNameSeparator() {
    return $this->_chainNameSeparator;
  }
  /**
   * Determines/returns whether to use the request parameters as global parameters.
   *
   * @param boolean|null $use
   *      Null/unset when you want to retrieve the current state.
   *      True when request parameters should be global, false otherwise
   * @return boolean|Zend_Controller_Router_Rewrite
   *       Returns a boolean if first param isn't set, returns an
   *       instance of Zend_Controller_Router_Rewrite otherwise.
   *
   */
  public function useRequestParametersAsGlobal($use = null) {
    if($use === null) {
      return $this->_useCurrentParamsAsGlobal;
    }
    $this->_useCurrentParamsAsGlobal = (bool) $use;
    return $this;
  }
}

添加路由的操作方法

public function addRoute($name, Zend_Controller_Router_Route_Interface $route)
public function addRoutes($routes)

$router = $ctrl->getRouter(); // returns a rewrite router by default
$router->addRoute('user',
         new Zend_Controller_Router_Route('user/:username'));

addRoute的第一個參數是路由名。第二個參數是路由自己。路由名最普通的用法是通過Zend_View_Url助手的方法:

"<?= $this->url(array('username' => 'martel'), 'user') ?>">Martel</a>

它將導致在 href: user/martel.

路由是一個簡單的過程,這個過程通過所有提供的路由和匹配它的當前請求的URI定義來迭代。當一個正匹配被發現,變量值從路由實例返回並注入到Zend_Controller_Request對象以備將來在派遣器和用戶創建的控制器中使用。如果是負匹配,在鏈中的下個路由被檢查。

Note: 倒序匹配

用倒序來匹配路由確保最通用的路由被首先定義。

Note: 返回的值

從路由返回的值來自於URL參數或用於定義的缺省值。這些變量以後可通過Zend_Controller_Request::getParam() 或 Zend_Controller_Action::_getParam() 方法來訪問。

有三個特殊的變量可用於你的路由-'module'、 'controller' 和 'action'。這些特殊的變量被Zend_Controller_Dispatcher用來找出控制器和動作然後派遣過去。

Note: 特殊變量

如果你選擇通過 setControllerKey 和 setActionKey方法的方式來改變缺省值,這些特殊變量的名字可能會不同。

缺省路由

Zend_Controller_Router_Rewrite 和缺省路由一起預先配置,它將以controller/action的形式匹配URIs。另外,模塊名可以被指定作為第一個路徑參數,允許這種module/controller/action形式的URIs。最後,它也將缺省地匹配任何另外的追加到URI的參數-controller/action/var1/value1/var2/value2。

一些路由如何匹配的例子:

// Assuming the following:
$ctrl->setControllerDirectory(
  array(
    'default' => '/path/to/default/controllers',
    'news'  => '/path/to/news/controllers',
    'blog'  => '/path/to/blog/controllers'
  )
);
Module only:
http://example/news
  module == news
Invalid module maps to controller name:
http://example/foo
  controller == foo
Module + controller:
http://example/blog/archive
  module   == blog
  controller == archive
Module + controller + action:
http://example/blog/archive/list
  module   == blog
  controller == archive
  action   == list
Module + controller + action + params:
http://example/blog/archive/list/sort/alpha/date/desc
  module   == blog
  controller == archive
  action   == list
  sort    == alpha
  date    == desc

缺省路由是存儲在RewriteRouter名(index)為'default'的簡單的Zend_Controller_Router_Route_Module對象。它被創建多多少少象下面這樣:

$compat = new Zend_Controller_Router_Route_Module(array(),
                         $dispatcher,
                         $request);
$this->addRoute('default', $compat);

如果你不想這個特別的缺省路由在你的路由計劃中,你可以重寫你自己的‘缺省'路由(例如,把它存儲在'default'名下)或用removeDefaultRoutes()完全清除它:

// Remove any default routes
$router->removeDefaultRoutes();

為了增加路由的靈活性,方便自定義新的路由類型,Zend_Controller_Router定義了Zend_Controller_Router_Route_Interface接口和類Zend_Controller_Router_Route_Abstract,實現相應的類方法即可定義路由類型,為開發提供了便利。

Zend_Controller_Router的路由類型

Zend_Controller_Router默認提供了以下路由類型,分別為:

Zend_Controller_Router_Route
Zend_Controller_Router_Route_Static
Zend_Controller_Router_Route_Regex
Zend_Controller_Router_Route_Hostname
Zend_Controller_Router_Route_Module
Zend_Controller_Router_Route_Chain
Zend_Controller_Router_Route

Zend_Controller_Router_Route是標准的框架路由。它結合了靈活路由定義的易用性。每個路由包含了基本的URL映射(靜態的和動態的部分(變量))並且可以被缺省地初始化,也可以根據不同的要求初始化。

讓我們想象一下我們假設的應用程序將需要一些廣域內容作者的信息頁面。我們想能夠把浏覽器指向http://domain.com/author/martel去看一個叫"martel"的信息。有這樣功能的路由看起來是這樣的:

$route = new Zend_Controller_Router_Route(
  'author/:username',
  array(
    'controller' => 'profile',
    'action'   => 'userinfo'
  )
);
$router->addRoute('user', $route);

在Zend_Controller_Router_Route裡構造函數的第一個參數是路由的定義,它將匹配一個URL。路由定義包含靜態的和動態部分,它們由正斜槓('/')符分開。靜態部分只是簡單的字符:author。動態部分,被叫做變量,用預設的冒號來標記變量名::username。

Note: 字符的的用法

當前的實現允許你使用任何字符(正斜槓除外)作為變量標識符,但強烈建議只使用PHP使用的變量標識符。將來的實現也許會改變這個行為,它可能會導致在你的代碼裡有隱藏的bugs。

當你把浏覽器指向http://domain.com/author/martel這個例子的路由應該被匹配,它所有的變量將被注入到Zend_Controller_Request對象並在ProfileController可訪問。由這個例子返回的變量可能會被表示為如下鍵和值配對的數組:

$values = array(
  'username'  => 'martel',
  'controller' => 'profile',
  'action'   => 'userinfo'
);

稍後,基於這些值,Zend_Controller_Dispatcher_Standard應該調用ProfileController類(在缺省模塊中)中的userinfoAction()方法。你將依靠Zend_Controller_Action::_getParam()或者Zend_Controller_Request::getParam()方法能夠訪問所有的變量:

public function userinfoAction()
{
  $request = $this->getRequest();
  $username = $request->getParam('username');
  $username = $this->_getParam('username');
}

路由定義可以包一個額外的特別字符-通配符-表示為'*'號。它被用來取得參數,和缺省模塊路由類似(在URI中定義的var=>value)。下面的路由多多少少地模仿了模塊路由的行為:

$route = new Zend_Controller_Router_Route(
  ':module/:controller/:action/*',
  array('module' => 'default')
);
$router->addRoute('default', $route);

變量缺省

在路由中每個變量可以有一個缺省值,這就是Zend_Controller_Router_Route中構造函數使用的第二個變量。這個參數是一個數組,在數組中鍵表示變量名,值就是期望的缺省值:

$route = new Zend_Controller_Router_Route(
  'archive/:year',
  array('year' => 2006)
);
$router->addRoute('archive', $route);

上述路由將匹配象http://domain.com/archive/2005和http://example.com/archive的URLs。對於後者變量year將有一個初始的缺省值為2006。

這個例子將導致注入一個year變量給請求對象。應為沒有路由信息出現(沒有控制器和動作參數被定義),應用程序將被派遣給缺省的控制器和動作方法(它們都在Zend_Controller_Dispatcher_Abstract被定義)。為使它更可用,你必須提供一個有效的控制器和動作作為路由的缺省值:

$route = new Zend_Controller_Router_Route(
  'archive/:year',
  array(
    'year'    => 2006,
    'controller' => 'archive',
    'action'   => 'show'
  )
);
$router->addRoute('archive', $route);

這個路由將導致派遣給ArchiveController類的showAction()方法。

變量請求

當變量請求被設定,第三個參數可以加給Zend_Controller_Router_Route的構造函數。這些被定義為正則表達式的一部分:

$route = new Zend_Controller_Router_Route(
  'archive/:year',
  array(
    'year'    => 2006,
    'controller' => 'archive',
    'action'   => 'show'
  ),
  array('year' => '\d+')
);
$router->addRoute('archive', $route);

用上述定義的路由,路由器僅當year變量包含數字數據將匹配它,例如http://domain.com/archive/2345。象http://example.com/archive/test這樣的URL將不被匹配並且控制將被傳遞給在鏈中的下一個路由。

主機名路由

你也可以使用主機名做路由匹配。對簡單的匹配使用靜態主機名選項:

$route = new Zend_Controller_Router_Route(
  array(
    'host' => 'blog.mysite.com',
    'path' => 'archive'
  ),
  array(
    'module'   => 'blog',
    'controller' => 'archive',
    'action'   => 'index'
  )
);
$router->addRoute('archive', $route);

如果你想匹配參數在主機名裡,使用 regex 選項。在下面例子中,子域為動作控制器被用作用戶名參數。 當組裝路由時,你可以給出用戶名為參數,就像你用其它路徑參數一樣:

$route = new Zend_Controller_Router_Route(
  array(
    'host' => array(
      'regex'  => '([a-z]+).mysite.com',
      'reverse' => '%s.mysite.com'
      'params' => array(
        1 => 'username'
      )
    ),
    'path' => ''
  ),
  array(
    'module'   => 'users',
    'controller' => 'profile',
    'action'   => 'index'
  )
);
$router->addRoute('profile', $route);

Zend_Controller_Router_Route_Static

設置固定不變的路由:

$route = new Zend_Controller_Router_Route_Static(
  'login',
  array('controller' => 'auth', 'action' => 'login')
);
$router->addRoute('login', $route);

上面的路由將匹配http://domain.com/login的URL,並分派到 AuthController::loginAction().

Zend_Controller_Router_Route_Regex

除了缺省的和靜態的路由類型外,正則表達式路由類型也可用。這個路由比其它路由更強更靈活,只是稍微有點復雜。同時,它應該比標准路由快。

象標准路由一樣,這個路由必須用路由定義和一些缺省條件來初始化。讓我們創建一個archive路由作為例子,和先前定義的類似,這次只是用了Regex:

$route = new Zend_Controller_Router_Route_Regex(
  'archive/(\d+)',
  array(
    'controller' => 'archive',
    'action'   => 'show'
  )
);
$router->addRoute('archive', $route);

每個定義的regex子模式將被注入到請求對象裡。同上述的例子,再成功匹配http://domain.com/archive/2006之後,結果值的數組看起來象這樣:

$values = array(
  1      => '2006',
  'controller' => 'archive',
  'action'   => 'show'
);

Note: 在匹配之前,開頭和結尾的斜槓從路由器裡的URL中去除掉了。結果,匹配http://domain.com/foo/bar/,需要foo/bar這樣的regex,而不是/foo/bar。

Note: 行開頭和行結尾符號(分別為'^' 和 '$')被自動預先追加到所有表達式。這樣,你不需要在你的正則表達式裡用它們,你應該匹配整個字符串。

Note: 這個路由類使用#符作為分隔符。這意味著你將需要避免哈希符('#')但不是正斜槓('/')在你的路由定義裡。因為'#'符(名稱為錨)很少被傳給webserver,你將幾乎不需要在你的regex裡使用它。

你可以用通常的辦法獲得已定義的子模式的內容:

public function showAction()
{
  $request = $this->getRequest();
  $year  = $request->getParam(1); // $year = '2006';
}

Note: 注意這個鍵是整數(1) 而不是字符串('1')。

因為'year'的缺省沒有設置,這個路由將和它的標准路由副本不是非常精確地相同。即使我們為'year'聲明一個缺省並使子模式可選,也不清楚是否會在拖尾斜槓(trailing slash)上還將有問題。方案是使整個'year'部分和斜槓一起可選但只抓取數字部分:(這段比較繞口,請校對者仔細看看,謝謝 Jason Qi)

$route = new Zend_Controller_Router_Route_Regex(
  'archive(?:/(\d+))?',
  array(
    1      => '2006',
    'controller' => 'archive',
    'action'   => 'show'
  )
);
$router->addRoute('archive', $route);

讓我們看看你可能注意到的問題。 給參數使用基於整數的鍵不是容易管理的辦法,今後可能會有問題。這就是為什麼有第三個參數。這是個聯合數組表示一個regex子模式到參數名鍵的映射。我們來看看一個簡單的例子:

$route = new Zend_Controller_Router_Route_Regex(
  'archive/(\d+)',
  array(
    'controller' => 'archive',
    'action' => 'show'
  ),
  array(
    1 => 'year'
  )
);
$router->addRoute('archive', $route);

這將導致下面的值被注入到請求:

$values = array(
  'year'    => '2006',
  'controller' => 'archive',
  'action'   => 'show'
);

這個映射被任何目錄來定義使它能工作於任何環境。鍵可以包含變量名或子模式索引:

$route = new Zend_Controller_Router_Route_Regex(
  'archive/(\d+)',
  array( ... ),
  array(1 => 'year')
);
// OR
$route = new Zend_Controller_Router_Route_Regex(
  'archive/(\d+)',
  array( ... ),
  array('year' => 1)
);

Note: 子模式鍵必須用整數表示。

注意在請求值中的數字索引不見了,代替的是一個名字變量。當然如果你願意可以把數字和名字變量混合使用:

$route = new Zend_Controller_Router_Route_Regex(
  'archive/(\d+)/page/(\d+)',
  array( ... ),
  array('year' => 1)
);

這將導致在請求中有混合的值可用。例如:URLhttp://domain.com/archive/2006/page/10將在下列結果中:

$values = array(
  'year'    => '2006',
  2      => 10,
  'controller' => 'archive',
  'action'   => 'show'
);

因為regex模型不容易顛倒,如果你想用URL助手或這個類中的 assemble方法,你需要准備一個顛倒的URL。這個顛倒的路徑用可由sprintf()解析的字符串來表示並定義為第四個構造參數:

$route = new Zend_Controller_Router_Route_Regex(
  'archive/(\d+)',
  array( ... ),
  array('year' => 1),
  'archive/%s'
);

所有這些都已經可能由標准路由對象完成,那麼使用Regex路由的好處在哪裡?首先,它允許你不受限制地描述任何類型的URL。想象一下你有一個博客並希望創建象http://domain.com/blog/archive/01-Using_the_Regex_Router.html這樣的URLs,還有把解析它路徑元素中的最後部分,01-Using_the_Regex_Router.html,到一個文章的ID和文章的標題/描述;這不可能由標准路由完成。用Regex路由,你可以做象下面的方案:

$route = new Zend_Controller_Router_Route_Regex(
  'blog/archive/(\d+)-(.+)\.html',
  array(
    'controller' => 'blog',
    'action'   => 'view'
  ),
  array(
    1 => 'id',
    2 => 'description'
  ),
  'blog/archive/%d-%s.html'
);
$router->addRoute('blogArchive', $route);

正如你所看到的,這個在標准路由上添加了巨大的靈活性。

通過配置文件定義路由規則

例如

[production]
routes.archive.route = "archive/:year/*"
routes.archive.defaults.controller = archive
routes.archive.defaults.action = show
routes.archive.defaults.year = 2000
routes.archive.reqs.year = "\d+"
routes.news.type = "Zend_Controller_Router_Route_Static"
routes.news.route = "news"
routes.news.defaults.controller = "news"
routes.news.defaults.action = "list"
routes.archive.type = "Zend_Controller_Router_Route_Regex"
routes.archive.route = "archive/(\d+)"
routes.archive.defaults.controller = "archive"
routes.archive.defaults.action = "show"
routes.archive.map.1 = "year"
; OR: routes.archive.map.year = 1

上述的INI文件可以被讀進Zend_Config對象:

$config = new Zend_Config_Ini('/path/to/config.ini', 'production');
$router = new Zend_Controller_Router_Rewrite();
$router->addConfig($config, 'routes');

在上面的例子中,我們告訴路由器去使用INI文件'routes'一節給它的路由。每個在這個節下的頂級鍵將用來定義路由名;上述例子定義了路由'archive'和'news'。每個路由接著要求,至少,一個'route'條目和一個或更多'defaults'條目;可選地,一個或更多'reqs'('required'的簡寫)可能要求提供。總之,這些相對應的三個參數提供給Zend_Controller_Router_Route_Interface對象。一個選項鍵,'type',可用來指定路由類的類型給特殊的路由;缺省地,它使用Zend_Controller_Router_Route。在上述例子中,'news'路由被定義來使用Zend_Controller_Router_Route_Static。

自定義路由類型

標准的rewrite路由器應當最大限度提供你所需的功能;大多時候,為了通過已知的路由提供新的或修改的功能,你將只需要創建一個新的路由類型

那就是說,你可能想要用不同的路由范例。接口Zend_Controller_Router_Interface提供了需要最少的信息來創建路由器,並包含一個單個的方法。

interface Zend_Controller_Router_Interface
{
 /**
  * @param Zend_Controller_Request_Abstract $request
  * @throws Zend_Controller_Router_Exception
  * @return Zend_Controller_Request_Abstract
  */
 public function route(Zend_Controller_Request_Abstract $request);
}

路由只發生一次:當請求第一次接收到系統。路由器的意圖是基於請求的環境決定控制器、動作和可選的參數,並把它們發給請求。請求對象接著傳遞給派遣器。如果不可能映射一個路由到一個派遣令牌,路由器對請求對象就什麼也不做。

更多關於zend相關內容感興趣的讀者可查看本站專題:《Zend FrameWork框架入門教程》、《php優秀開發框架總結》、《Yii框架入門及常用技巧總結》、《ThinkPHP入門教程》、《php面向對象程序設計入門教程》、《php+mysql數據庫操作入門教程》及《php常見數據庫操作技巧匯總》

希望本文所述對大家PHP程序設計有所幫助。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved