Zend_Controller-Router.xml 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <!-- Reviewed: no -->
  3. <sect1 id="zend.controller.router" xmlns:xi="http://www.w3.org/2001/XInclude">
  4. <title>The Standard Router</title>
  5. <sect2 id="zend.controller.router.introduction">
  6. <title>Introduction</title>
  7. <para>
  8. <classname>Zend_Controller_Router_Rewrite</classname> is the standard
  9. framework router. Routing is the process of taking a <acronym>URI</acronym> endpoint
  10. (that part of the <acronym>URI</acronym> which comes after the base
  11. <acronym>URL</acronym>) and decomposing it into parameters to determine which module,
  12. controller, and action of that controller should receive the
  13. request. This values of the module, controller, action and other
  14. parameters are packaged into a
  15. <classname>Zend_Controller_Request_Http</classname> object which is then
  16. processed by <classname>Zend_Controller_Dispatcher_Standard</classname>.
  17. Routing occurs only once: when the request is initially received and
  18. before the first controller is dispatched.
  19. </para>
  20. <para>
  21. <classname>Zend_Controller_Router_Rewrite</classname> is designed to allow for
  22. mod_rewrite-like functionality using pure <acronym>PHP</acronym> structures. It is very
  23. loosely based on Ruby on Rails routing and does not require any
  24. prior knowledge of webserver <acronym>URL</acronym> rewriting. It is designed to work
  25. with a single Apache mod_rewrite rule (one of):
  26. </para>
  27. <programlisting language="php"><![CDATA[
  28. RewriteEngine on
  29. RewriteRule !\.(js|ico|gif|jpg|png|css|html)$ index.php
  30. ]]></programlisting>
  31. <para>
  32. or (preferred):
  33. </para>
  34. <programlisting language="php"><![CDATA[
  35. RewriteEngine On
  36. RewriteCond %{REQUEST_FILENAME} -s [OR]
  37. RewriteCond %{REQUEST_FILENAME} -l [OR]
  38. RewriteCond %{REQUEST_FILENAME} -d
  39. RewriteRule ^.*$ - [NC,L]
  40. RewriteRule ^.*$ index.php [NC,L]
  41. ]]></programlisting>
  42. <para>
  43. The rewrite router can also be used with the <acronym>IIS</acronym> webserver (versions
  44. &lt;= 7.0) if <ulink url="http://www.isapirewrite.com">Isapi_Rewrite</ulink> has been
  45. installed as an Isapi extension with the following rewrite rule:
  46. </para>
  47. <programlisting language="php"><![CDATA[
  48. RewriteRule ^[\w/\%]*(?:\.(?!(?:js|ico|gif|jpg|png|css|html)$)[\w\%]*$)? /index.php [I]
  49. ]]></programlisting>
  50. <note>
  51. <title>IIS Isapi_Rewrite</title>
  52. <para>
  53. When using <acronym>IIS</acronym>, <varname>$_SERVER['REQUEST_URI']</varname> will
  54. either not exist, or be set as an empty string. In this case,
  55. <classname>Zend_Controller_Request_Http</classname> will attempt to use
  56. the <varname>$_SERVER['HTTP_X_REWRITE_URL']</varname> value set by the
  57. <classname>Isapi_Rewrite</classname> extension.
  58. </para>
  59. </note>
  60. <para>
  61. <acronym>IIS</acronym> 7.0 introduces a native <acronym>URL</acronym> rewriting module,
  62. and it can be configured as follows:
  63. </para>
  64. <programlisting language="xml"><![CDATA[
  65. <?xml version="1.0" encoding="UTF-8"?>
  66. <configuration>
  67. <system.webServer>
  68. <rewrite>
  69. <rules>
  70. <rule name="Imported Rule 1" stopProcessing="true">
  71. <match url="^.*$" />
  72. <conditions logicalGrouping="MatchAny">
  73. <add input="{REQUEST_FILENAME}"
  74. matchType="IsFile" pattern=""
  75. ignoreCase="false" />
  76. <add input="{REQUEST_FILENAME}"
  77. matchType="IsDirectory"
  78. pattern="" ignoreCase="false" />
  79. </conditions>
  80. <action type="None" />
  81. </rule>
  82. <rule name="Imported Rule 2" stopProcessing="true">
  83. <match url="^.*$" />
  84. <action type="Rewrite" url="index.php" />
  85. </rule>
  86. </rules>
  87. </rewrite>
  88. </system.webServer>
  89. </configuration>
  90. ]]></programlisting>
  91. <para>
  92. If using Lighttpd, the following rewrite rule is valid:
  93. </para>
  94. <programlisting language="lighttpd"><![CDATA[
  95. url.rewrite-once = (
  96. ".*\?(.*)$" => "/index.php?$1",
  97. ".*\.(js|ico|gif|jpg|png|css|html)$" => "$0",
  98. "" => "/index.php"
  99. )
  100. ]]></programlisting>
  101. </sect2>
  102. <sect2 id="zend.controller.router.usage">
  103. <title>Using a Router</title>
  104. <para>
  105. To properly use the rewrite router you have to instantiate it, add
  106. some user defined routes and inject it into the controller. The
  107. following code illustrates the procedure:
  108. </para>
  109. <programlisting language="php"><![CDATA[
  110. // Create a router
  111. $router = $ctrl->getRouter(); // returns a rewrite router by default
  112. $router->addRoute(
  113. 'user',
  114. new Zend_Controller_Router_Route('user/:username',
  115. array('controller' => 'user',
  116. 'action' => 'info'))
  117. );
  118. ]]></programlisting>
  119. </sect2>
  120. <sect2 id="zend.controller.router.basic">
  121. <title>Basic Rewrite Router Operation</title>
  122. <para>
  123. The heart of the RewriteRouter is the definition of user defined
  124. routes. Routes are added by calling the addRoute method of
  125. RewriteRouter and passing in a new instance of a class implementing
  126. <classname>Zend_Controller_Router_Route_Interface</classname>. Eg.:
  127. </para>
  128. <programlisting language="php"><![CDATA[
  129. $router->addRoute('user',
  130. new Zend_Controller_Router_Route('user/:username'));
  131. ]]></programlisting>
  132. <para>
  133. Rewrite Router comes with six basic types of routes (one of which
  134. is special):
  135. </para>
  136. <itemizedlist mark="opencircle">
  137. <listitem>
  138. <para>
  139. <link
  140. linkend="zend.controller.router.routes.standard">Zend_Controller_Router_Route</link>
  141. </para>
  142. </listitem>
  143. <listitem>
  144. <para>
  145. <link
  146. linkend="zend.controller.router.routes.static">Zend_Controller_Router_Route_Static</link>
  147. </para>
  148. </listitem>
  149. <listitem>
  150. <para>
  151. <link
  152. linkend="zend.controller.router.routes.regex">Zend_Controller_Router_Route_Regex</link>
  153. </para>
  154. </listitem>
  155. <listitem>
  156. <para>
  157. <link
  158. linkend="zend.controller.router.routes.hostname">Zend_Controller_Router_Route_Hostname</link>
  159. </para>
  160. </listitem>
  161. <listitem>
  162. <para>
  163. <link
  164. linkend="zend.controller.router.routes.chain">Zend_Controller_Router_Route_Chain</link>
  165. </para>
  166. </listitem>
  167. <listitem>
  168. <para>
  169. <link
  170. linkend="zend.controller.router.default-routes">Zend_Controller_Router_Rewrite</link>
  171. *
  172. </para>
  173. </listitem>
  174. </itemizedlist>
  175. <para>
  176. Routes may be used numerous times to create a chain or user defined
  177. application routing schema. You may use any number of routes in any
  178. configuration, with the exception of the Module route, which should
  179. rather be used once and probably as the most generic route (i.e., as a
  180. default). Each route will be described in greater detail later on.
  181. </para>
  182. <para>
  183. The first parameter to addRoute is the name of the route. It is used
  184. as a handle for getting the routes out of the router (e.g., for <acronym>URL</acronym>
  185. generation purposes). The second parameter being the route itself.
  186. </para>
  187. <note>
  188. <para>
  189. The most common use of the route name is through the means of
  190. <classname>Zend_View</classname> url helper:
  191. </para>
  192. <programlisting language="php"><![CDATA[
  193. <a href=
  194. "<?php echo $this->url(array('username' => 'martel'), 'user') ?>">Martel</a>
  195. ]]></programlisting>
  196. <para>
  197. Which would result in the href: <filename>user/martel</filename>.
  198. </para>
  199. </note>
  200. <para>
  201. Routing is a simple process of iterating through all provided routes
  202. and matching its definitions to current request <acronym>URI</acronym>. When a positive
  203. match is found, variable values are returned from the Route instance
  204. and are injected into the <classname>Zend_Controller_Request</classname>
  205. object for later use in the dispatcher as well as in user created
  206. controllers. On a negative match result, the next route in the chain
  207. is checked.
  208. </para>
  209. <para>
  210. If you need to determine which route was matched, you can use the
  211. <methodname>getCurrentRouteName()</methodname> method, which will return the
  212. identifier used when registering the route with the router. If you
  213. want the actual route object, you can use
  214. <methodname>getCurrentRoute()</methodname>.
  215. </para>
  216. <note>
  217. <title>Reverse Matching</title>
  218. <para>
  219. Routes are matched in reverse order so make sure your most
  220. generic routes are defined first.
  221. </para>
  222. </note>
  223. <note>
  224. <title>Returned Values</title>
  225. <para>
  226. Values returned from routing come from <acronym>URL</acronym> parameters or user
  227. defined route defaults. These variables are later accessible
  228. through the <methodname>Zend_Controller_Request::getParam()</methodname> or
  229. <methodname>Zend_Controller_Action::_getParam()</methodname> methods.
  230. </para>
  231. </note>
  232. <para>
  233. There are three special variables which can be used in your routes
  234. - 'module', 'controller' and 'action'. These special variables are
  235. used by <classname>Zend_Controller_Dispatcher</classname> to find a controller and
  236. action to dispatch to.
  237. </para>
  238. <note>
  239. <title>Special Variables</title>
  240. <para>
  241. The names of these special variables may be different if you
  242. choose to alter the defaults in
  243. <classname>Zend_Controller_Request_Http</classname> by means of the
  244. <methodname>setControllerKey()</methodname> and
  245. <methodname>setActionKey()</methodname> methods.
  246. </para>
  247. </note>
  248. </sect2>
  249. <sect2 id="zend.controller.router.default-routes">
  250. <title>Default Routes</title>
  251. <para>
  252. <classname>Zend_Controller_Router_Rewrite</classname> comes preconfigured with a default
  253. route, which will match <acronym>URI</acronym>s in the shape of
  254. <filename>controller/action</filename>. Additionally, a module name may be
  255. specified as the first path element, allowing <acronym>URI</acronym>s of the form
  256. <filename>module/controller/action</filename>. Finally, it will also match
  257. any additional parameters appended to the <acronym>URI</acronym> by default -
  258. <filename>controller/action/var1/value1/var2/value2</filename>.
  259. </para>
  260. <para>
  261. Some examples of how such routes are matched:
  262. </para>
  263. <programlisting language="php"><![CDATA[
  264. // Assuming the following:
  265. $ctrl->setControllerDirectory(
  266. array(
  267. 'default' => '/path/to/default/controllers',
  268. 'news' => '/path/to/news/controllers',
  269. 'blog' => '/path/to/blog/controllers'
  270. )
  271. );
  272. Module only:
  273. http://example/news
  274. module == news
  275. Invalid module maps to controller name:
  276. http://example/foo
  277. controller == foo
  278. Module + controller:
  279. http://example/blog/archive
  280. module == blog
  281. controller == archive
  282. Module + controller + action:
  283. http://example/blog/archive/list
  284. module == blog
  285. controller == archive
  286. action == list
  287. Module + controller + action + params:
  288. http://example/blog/archive/list/sort/alpha/date/desc
  289. module == blog
  290. controller == archive
  291. action == list
  292. sort == alpha
  293. date == desc
  294. ]]></programlisting>
  295. <para>
  296. The default route is simply a
  297. <classname>Zend_Controller_Router_Route_Module</classname> object stored under
  298. the name (index) of 'default' in RewriteRouter. It's created
  299. more-or-less like below:
  300. </para>
  301. <programlisting language="php"><![CDATA[
  302. $compat = new Zend_Controller_Router_Route_Module(array(),
  303. $dispatcher,
  304. $request);
  305. $this->addRoute('default', $compat);
  306. ]]></programlisting>
  307. <para>
  308. If you do not want this particular default route in your routing
  309. schema, you may override it by creating your own 'default' route
  310. (i.e., storing it under the name of 'default') or removing it
  311. altogether by using <methodname>removeDefaultRoutes()</methodname>:
  312. </para>
  313. <programlisting language="php"><![CDATA[
  314. // Remove any default routes
  315. $router->removeDefaultRoutes();
  316. ]]></programlisting>
  317. </sect2>
  318. <sect2 id="zend.controller.router.rewritebase">
  319. <title>Base URL and Subdirectories</title>
  320. <para>
  321. The rewrite router can be used in subdirectories (e.g.,
  322. <filename>http://domain.com/user/application-root/</filename>) in which
  323. case the base <acronym>URL</acronym> of the application
  324. (<filename>/user/application-root</filename>) should be automatically
  325. detected by <classname>Zend_Controller_Request_Http</classname> and used
  326. accordingly.
  327. </para>
  328. <para>
  329. Should the base <acronym>URL</acronym> be detected incorrectly you can override it with
  330. your own base path by using
  331. <classname>Zend_Controller_Request_Http</classname> and calling the
  332. <methodname>setBaseUrl()</methodname> method (see <link
  333. linkend="zend.controller.request.http.baseurl">Base URL and Subdirectories</link>):
  334. </para>
  335. <programlisting language="php"><![CDATA[
  336. $request->setBaseUrl('/~user/application-root/');
  337. ]]></programlisting>
  338. </sect2>
  339. <sect2 id="zend.controller.router.global.parameters">
  340. <title>Global Parameters</title>
  341. <para>
  342. You can set global parameters in a router which are automatically
  343. supplied to a route when assembling through
  344. <methodname>setGlobalParam()</methodname>. If a global parameter is set
  345. but also given to the assemble method directly, the user parameter
  346. overrides the global parameter. You can set a global parameter this
  347. way:
  348. </para>
  349. <programlisting language="php"><![CDATA[
  350. $router->setGlobalParam('lang', 'en');
  351. ]]></programlisting>
  352. </sect2>
  353. <sect2 id="zend.controller.router.routes">
  354. <title>Route Types</title>
  355. <xi:include href="Zend_Controller-Router-Route.xml" />
  356. <xi:include href="Zend_Controller-Router-Route-Static.xml" />
  357. <xi:include href="Zend_Controller-Router-Route-Regex.xml" />
  358. <xi:include href="Zend_Controller-Router-Route-Hostname.xml" />
  359. <xi:include href="Zend_Controller-Router-Route-Chain.xml" />
  360. <xi:include href="Zend_Controller-Router-Route-Rest.xml" />
  361. </sect2>
  362. <sect2 id="zend.controller.router.add-config">
  363. <title>Using Zend_Config with the RewriteRouter</title>
  364. <para>
  365. Sometimes it is more convenient to update a configuration file with
  366. new routes than to change the code. This is possible via the
  367. <methodname>addConfig()</methodname> method. Basically, you create a
  368. <classname>Zend_Config</classname>-compatible configuration, and in your code read it in
  369. and pass it to the RewriteRouter.
  370. </para>
  371. <para>
  372. As an example, consider the following <acronym>INI</acronym> file:
  373. </para>
  374. <programlisting language="php"><![CDATA[
  375. [production]
  376. routes.archive.route = "archive/:year/*"
  377. routes.archive.defaults.controller = archive
  378. routes.archive.defaults.action = show
  379. routes.archive.defaults.year = 2000
  380. routes.archive.reqs.year = "\d+"
  381. routes.news.type = "Zend_Controller_Router_Route_Static"
  382. routes.news.route = "news"
  383. routes.news.defaults.controller = "news"
  384. routes.news.defaults.action = "list"
  385. routes.archive.type = "Zend_Controller_Router_Route_Regex"
  386. routes.archive.route = "archive/(\d+)"
  387. routes.archive.defaults.controller = "archive"
  388. routes.archive.defaults.action = "show"
  389. routes.archive.map.1 = "year"
  390. ; OR: routes.archive.map.year = 1
  391. ]]></programlisting>
  392. <para>
  393. The above <acronym>INI</acronym> file can then be read into a
  394. <classname>Zend_Config</classname> object as follows:
  395. </para>
  396. <programlisting language="php"><![CDATA[
  397. $config = new Zend_Config_Ini('/path/to/config.ini', 'production');
  398. $router = new Zend_Controller_Router_Rewrite();
  399. $router->addConfig($config, 'routes');
  400. ]]></programlisting>
  401. <para>
  402. In the above example, we tell the router to use the 'routes' section
  403. of the <acronym>INI</acronym> file to use for its routes. Each first-level key under
  404. that section will be used to define a route name; the above example
  405. defines the routes 'archive' and 'news'. Each route then requires,
  406. at minimum, a 'route' entry and one or more 'defaults' entries;
  407. optionally one or more 'reqs' (short for 'required') may be
  408. provided. All told, these correspond to the three arguments provided
  409. to a <classname>Zend_Controller_Router_Route_Interface</classname> object. An
  410. option key, 'type', can be used to specify the route class type to
  411. use for that particular route; by default, it uses
  412. <classname>Zend_Controller_Router_Route</classname>. In the example above, the
  413. 'news' route is defined to use
  414. <classname>Zend_Controller_Router_Route_Static</classname>.
  415. </para>
  416. </sect2>
  417. <sect2 id="zend.controller.router.subclassing">
  418. <title>Subclassing the Router</title>
  419. <para>
  420. The standard rewrite router should provide most functionality you
  421. may need; most often, you will only need to create a new route type
  422. in order to provide new or modified functionality over the provided
  423. routes.
  424. </para>
  425. <para>
  426. That said, you may at some point find yourself wanting to use a
  427. different routing paradigm. The interface
  428. <classname>Zend_Controller_Router_Interface</classname> provides the minimal
  429. information required to create a router, and consists of a single
  430. method.
  431. </para>
  432. <programlisting language="php"><![CDATA[
  433. interface Zend_Controller_Router_Interface
  434. {
  435. /**
  436. * @param Zend_Controller_Request_Abstract $request
  437. * @throws Zend_Controller_Router_Exception
  438. * @return Zend_Controller_Request_Abstract
  439. */
  440. public function route(Zend_Controller_Request_Abstract $request);
  441. }
  442. ]]></programlisting>
  443. <para>
  444. Routing only occurs once: when the request is first received into
  445. the system. The purpose of the router is to determine the
  446. controller, action, and optional parameters based on the request
  447. environment, and then set them in the request. The request object
  448. is then passed to the dispatcher. If it is not possible to map a
  449. route to a dispatch token, the router should do nothing to the
  450. request object.
  451. </para>
  452. </sect2>
  453. </sect1>
  454. <!--
  455. vim:se ts=4 sw=4 et:
  456. -->