Changeset 174
- Timestamp:
- 03/13/06 21:10:15 (6 years ago)
- Location:
- trunk/trax
- Files:
-
- 1 added
- 6 modified
-
app/views/layouts/application.phtml (added)
-
test/ActionControllerTest.php (modified) (5 diffs)
-
tutorials/PHPonTrax/ActionController.cls (modified) (7 diffs)
-
tutorials/PHPonTrax/PHPonTrax.pkg (modified) (4 diffs)
-
vendor/trax/action_controller.php (modified) (35 diffs)
-
vendor/trax/router.php (modified) (6 diffs)
-
vendor/trax/session.php (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
-
trunk/trax/test/ActionControllerTest.php
r163 r174 100 100 $ac = new ActionController; 101 101 // this URL doesn't match any route 102 $_SERVER['REDIRECT_URL'] = ' ~haas/foo/bar';102 $_SERVER['REDIRECT_URL'] = '/~haas/foo/bar'; 103 103 $this->assertFalse($ac->recognize_route()); 104 104 // this URL matches but the controller doesn't exist 105 $_SERVER['REDIRECT_URL'] = ' ~haas/nocontroller/foo/bar';105 $_SERVER['REDIRECT_URL'] = '/~haas/nocontroller/foo/bar'; 106 106 $this->assertFalse($ac->recognize_route()); 107 107 // this URL matches and the controller is where it should be 108 $_SERVER['REDIRECT_URL'] = ' ~haas/products/bar';108 $_SERVER['REDIRECT_URL'] = '/~haas/products/bar'; 109 109 $this->assertTrue($ac->recognize_route()); 110 110 } … … 155 155 $ac = new ActionController; 156 156 // this URL doesn't match any route 157 $_SERVER['REDIRECT_URL'] = ' ~haas/foo/bar';157 $_SERVER['REDIRECT_URL'] = '/~haas/foo/bar'; 158 158 try { 159 159 $ac->process_route(); … … 180 180 $ac = new ActionController; 181 181 // this URL matches default route 182 $_SERVER['REDIRECT_URL'] = ' ~haas/nocontroller/foo/bar';182 $_SERVER['REDIRECT_URL'] = '/~haas/nocontroller/foo/bar'; 183 183 try { 184 184 $ac->process_route(); … … 207 207 // this URL matches default route, but the controller 208 208 // file doesn't have a Noclass class 209 $_SERVER['REDIRECT_URL'] = ' ~haas/noclass/foo/bar';209 $_SERVER['REDIRECT_URL'] = '/~haas/noclass/foo/bar'; 210 210 try { 211 211 $ac->process_route(); … … 234 234 // $ac = new ActionController; 235 235 // // should invoke CatalogController 236 // $_SERVER['REDIRECT_URL'] = ' ~haas/products/bar';236 // $_SERVER['REDIRECT_URL'] = '/~haas/products/bar'; 237 237 // $ac->process_route(); 238 238 // // Remove the following line when you implement this test. -
trunk/trax/tutorials/PHPonTrax/ActionController.cls
r159 r174 15 15 <refsect1 id="{@id intro}"> 16 16 <title>Introduction</title> 17 <para>The {@link ActionController} base class does the 18 following:</para> 19 <orderedlist> 20 <listitem>Accepts a URL as input</listitem> 21 <listitem>Translates the URL into a controller and action</listitem> 22 <listitem>Creates the indicated controller object (which is a subclass 23 of ActionController) and calls its action method</listitem> 24 <listitem>Redirects to another URL or renders the output of the 25 action method</listitem> 26 </orderedlist> 17 27 </refsect1> 18 <refsect1 id="{@id filters}"> 19 <title>Filters</title> 28 <refsect1 is="{@id url}"> 29 <title>URL Processing</title> 30 31 <para> 32 When Apache receives an HTTP request addressed to a Trax 33 application, 34 {@link http://httpd.apache.org/docs/2.0/mod/mod_rewrite.html Apache mod_rewrite} 35 is invoked and rewrites the request to invoke Trax file 36 <literal>dispatch.php</literal>. At this time the URL which was 37 input to the rewrite rules is in 38 {@link http://www.php.net/manual/en/reserved.variables.php#reserved.variables.server $_SERVER}['REDIRECT_URL']. 39 <literal>dispatch.php</literal> creates a new {@link Dispatcher} 40 object and calls its 41 {@link Dispatcher::dispatch() dispatch()} method. dispatch() 42 restores the state of the session identified by a cookie in the 43 request, or creates a session if none exists. Then it creates a 44 new ActionController object and calls its 45 {@link ActionController::process_route() <literal>process_route()</literal>} 46 method. 47 </para> 48 49 <para> 50 The word "route" is used in Trax to describe a rule which 51 translates some URL into a particular controller object and method. 52 When <literal>process_route()</literal> receives control, it calls 53 {@link ActionController::recognize_route() recognize_route()} to 54 parse the URL into controller, action and id 55 components. recognize_route() calls 56 {@link ActionController::load_router() load_router()} to load the 57 "routing table", which is a list of one or more 58 <literal>$router->connect()</literal> calls, from 59 {@link routes.php <literal>config/routes.php</literal>}. 60 This list of calls define the rules for translating a URL into a 61 controller and action. 62 </para> 63 64 <para> 65 The translation rules work as follows: Starting with the first 66 rule in <literal>routes.php</literal>, each rule is tested against 67 the URL to see 68 whether the rule matches. If a rule does not match, then the next 69 rule in the table is tested in turn, until a rule matches or the 70 table is exhausted. If no matching rule is found, 71 recognize_route() tests the last route in the table to see whether 72 it is the default route <literal>:controller/:action/:id</literal> 73 . If the last route is the default route, then 74 <literal>recognize_route()</literal> 75 returns it as a match, even if it does not in fact match. But if 76 there is no matching route and the last route in the table is not 77 the default route, then recognize_route() returns 'failure' which is 78 equivalent to HTTP code '404 Not found'. 79 </para> 80 81 <para> 82 Each entry in the route table contains two parts: 83 <orderedlist> 84 <listitem> 85 A <important>path</important>, which is a character string to 86 test against the URL. 87 </listitem> 88 <listitem> 89 <important>Parameters</important>, which are not tested against 90 the URL and aren't involved unless the 91 <important>path</important> part of the entry matches the URL. 92 <important>Parameters</important> are optional (and frequently 93 omitted). 94 </listitem> 95 </orderedlist> 96 A <important>path</important> is a series of substrings separated 97 by '/' (forward slash) characters. Each of these substrings can 98 contain any character except '/'. The <important>path</important> 99 does not begin or end with '/'. A substring may not be the null 100 (no characters) string, but it is legal for the entire 101 path to be the null string. Each substring is one of the following: 102 <itemizedlist> 103 <listitem><literal>:controller</literal></listitem> 104 <listitem><literal>:action</literal></listitem> 105 <listitem><literal>:id</literal></listitem> 106 <listitem>A 107 {@link http://www.php.net/manual/en/ref.pcre.php Perl regular expression} 108 that does not begin with ':' (colon)</listitem> 109 </itemizedlist> 110 The following are legal <important>path</important> values: 111 <itemizedlist> 112 <listitem><literal>:controller/:action/:id</literal> 113 This is the default <important>path</important>. It matches URLs 114 like <literal>word1/word2/word3</literal></listitem> 115 <listitem><literal>catalog/product/:action/:id</literal> 116 Remember that <literal>catalog</literal> is a Perl regular 117 expression that matches <literal>catalog</literal>, and 118 <literal>product</literal> is a Perl regular expression that 119 matches <literal>product</literal>, so this 120 <important>path</important> matches URLs like 121 <literal>catalog/product/word1/word2</literal></listitem> 122 <listitem><literal>''</literal> matches '' (the empty string as a 123 <important>path</important> value matches the empty string as a 124 URL).</listitem> 125 <listitem><literal>member/name=.*</literal> matches URLs like 126 <literal>member/name=</literal> or 127 <literal>member/name=Tom.Jones</literal> or 128 <literal>member/name=Smith,J/since=1987/type=full</literal> etc. 129 </listitem> 130 </itemizedlist> 131 <literal>:controller</literal>, <literal>:action</literal> and 132 <literal>:id</literal> may each appear at most once in a 133 <important>path</important>. 134 </para> 135 136 <para> 137 After the URL has been matched to a <important>path</important>, 138 the next step is to extract the name of the controller and action 139 to be invoked on this URL. These must be valid names in the PHP 140 language consisting only of lower-case alphameric characters and 141 '_' (underscore), because the controller name will translate 142 directly into a file name and a class name, and the action name 143 will be used as the name of a method in that class. The controller 144 and action names come from the route that matches the URL. 145 </para> 146 147 <para> 148 There are two places that a route can specify a controller or 149 action name: as part of the <important>path</important>, or in the 150 <important>parameters</important>. The 151 <important>parameters</important> are the optional second part of a 152 route. The value of <important>parameters</important> is an array 153 with key values that may be <literal>:controller</literal> or 154 <literal>:action</literal>. The following are legal 155 <important>parameters</important> values: 156 <itemizedlist> 157 <listitem><literal>array(':controller' => 158 'new_product')</literal></listitem> 159 <listitem><literal>array(':action' => 'enter')</literal></listitem> 160 <listitem><literal>array(':controller' => 'membership', ':action 161 => 'new')</literal></listitem> 162 </itemizedlist> 163 </para> 164 165 <para> 166 When a URL matches a route, the controller name is extracted as 167 follows: First, if the <important>parameters</important> array 168 exists and has an element whose key is 169 <literal>:controller</literal>, then the value of that element is 170 used as the controller name. If no <literal>:controller</literal> 171 is specified by the <important>parameters</important>, then the 172 <important>path</important> is tested for a substring whose value 173 is <literal>:controller</literal>. If found, then the part of the 174 URL which matched that substring is used as the controller value. 175 A controller value must be specified by either the 176 <important>parameters</important> or the 177 <important>path</important>. The action name is extracted by the 178 same process, substituting <literal>:action</literal> for 179 <literal>:controller</literal>. If the 180 <important>path</important> has a substring <literal>:id</literal>, 181 then the part of the URL which matched that substring is forced to 182 lower case and the result assigned to 183 <literal>$_REQUEST['id']</literal>. 184 </para> 185 186 <para> 187 If <literal>routes.php</literal> contains the following: 188 <example> 189 router->connect('',array(':controller' => 'home')); 190 router->connect('product\?.*', 191 array(':controller' => 'catalog', ':action' => 'find')); 192 router->connect(':controller/:action/:id'); 193 </example> 194 Then URLs will match routes as follows: 195 <itemizedlist> 196 <listitem>URL <literal>''</literal> (no characters) will select 197 controller <literal>home</literal>, action not specified. 198 </listitem> 199 <listitem>URL <literal>product?item=4317</literal> will select 200 controller <literal>catalog</literal>, action 201 <literal>find</literal> 202 </listitem> 203 <listitem>URL <literal>cart/add/4317</literal> will select 204 controller <literal>cart</literal>, action <literal>add</literal> 205 </listitem> 206 </itemizedlist> 207 </para> 208 209 </refsect1> 210 <refsect1 is="{@id action}"> 211 <title>Action Call</title> 212 <para>When the names of the controller and action have been 213 successfully determined from the URL, the associated filesystem 214 paths are constructed and relevant files are loaded. 215 First file <literal>app/controllers/application.php</literal> is 216 loaded if it exists. This file contains the definition of the 217 {@link ApplicationController} class, which extends 218 <literal>ActionController</literal>. 219 <literal>ApplicationController</literal> contains properties and 220 methods used by all the controller classes, which should extend 221 <literal>ApplicationController</literal> . 222 Then the controller name is used to find the file and class 223 containing the selected controller. By Trax naming conventions, 224 if the controller name is 225 <arg choice="tute-comment">controller name</arg> 226 then the controller file name is 227 <arg choice="tute-comment">controller_name</arg><literal>_controller.php</literal> 228 and the controller class name is 229 <arg choice="tute-comment">ControllerName</arg> . So for a 230 "catalog item" controller, the controller file name is 231 <literal>catalog_item_controller</literal> and the controller class 232 name is <literal>CatalogItem</literal>. 233 The controller file is loaded and a new object of the controller 234 class is created. 235 </para> 236 237 <para> 238 Next any needed helper files are loaded. Helper files contain PHP 239 code which helps prepare the output of an action method for 240 viewing. If file 241 <literal>application_helper.php</literal> exists, it is loaded. 242 <literal>application_helper.php</literal> contains 243 helpers that apply to every controller in the application. 244 Then the controller-specific helper file 245 <arg choice="tute-comment">controller_name</arg><literal>_helper.php</literal> 246 is loaded if it exists. Finally any extra helper files, as 247 specified by calls to {@link ActionController::add_helper()}, are 248 loaded. 249 </para> 250 251 <para> 252 When controller and helper files have been loaded, the before 253 filters are executed (<important>FIXME:</important> We should check 254 return but don't). Next the controller object is tested for the 255 presence of a method with the name of the action as determined from 256 the URL. If such a method exists, it is called; if 257 no such method exists, then the controller object is tested 258 for the presence of a method named <literal>index()</literal>. 259 If such a method exists it is called, otherwise the request fails 260 with 404 Unknown action. If an action method was found 261 and called, the after filters are executed. 262 </para> 263 264 <refsect2 id="{@id helpers}"> 265 <title>Helper Loading</title> 266 <para>Helpers are classes that provide view logic. They exist to 267 hold view logic that would otherwise need to be added to a 268 template or controller. Helper services that are applicable to 269 the entire application go into 270 <literal>application_helper.php</literal>, while 271 controller-specific helper functions go into a helper file named 272 after the controller, as 273 <arg choice="tute-comment">controller_name</arg><literal>_helper.php</literal> 274 . Helper classes are written as subclasses of class {@link Helpers}, 275 which has a number of methods widely used by helper 276 subclasses. You can add a helper to an 277 <literal>ActionController</literal> object by calling its 278 {@link ActionController::add_helper() add_helper()} 279 method, passing the name of the helper as an argument. 280 </para> 281 282 <para> 283 A number of predefined helper classes are distributed with Trax: 284 <itemizedlist> 285 <listitem>{@link ActiveRecordHelper}</listitem> 286 <listitem>{@link AssetTagHelper}</listitem> 287 <listitem>{@link DateHelper}</listitem> 288 <listitem>{@link FormHelper}</listitem> 289 <listitem>{@link FormTagHelper}</listitem> 290 <listitem>{@link JavaScriptHelper}</listitem> 291 <listitem>{@link UrlHelper}</listitem> 292 </itemizedlist> 293 These classes are <important>not</important> automatically loaded, 294 you have to load them explicitly. 295 </para> 296 <para></para> 297 </refsect2> 298 299 <refsect2 id="{@id filters}"> 300 <title>Filters</title> 20 301 21 302 <para>Filters enable controllers to run shared pre and post … … 30 311 for a pre-processing <samp>before_filter</samp> to halt the processing 31 312 before the intended action is processed by returning false or 32 performing a redirect or render. This is especially useful for 313 performing a redirect or render. (FIXME: we don't implement this) 314 This is especially useful for 33 315 filters like authentication where you're not interested in 34 316 allowing the action to be performed if the proper credentials are 35 317 not in order.</para> 36 318 37 <refsect 2id="{@id filter_inherit}">319 <refsect3 id="{@id filter_inherit}"> 38 320 <title>Filter inheritance</title> 39 321 … … 66 348 audit method is called, then the verify_credentials method. If the 67 349 audit method returns false, then verify_credentials and the 68 intended action are never called.</para> 69 </refsect2> 70 71 <refsect2 id="{@id filter_types}"> 350 intended action are never called. <important>FIXME: 351 This is currently broken.</important></para> 352 </refsect3> 353 354 <refsect3 id="{@id filter_types}"> 72 355 <title>Filter types</title> 73 356 … … 120 403 have to be a block; any object that responds to call and returns 1 121 404 or -1 on arity will do (such as a Proc or an Method object).</para> 122 </refsect 2>123 124 <refsect 2id="{@id filter_skip}">405 </refsect3> 406 407 <refsect3 id="{@id filter_skip}"> 125 408 <title>Filter chain skipping</title> 126 409 … … 142 425 } 143 426 </example> 144 </refsect 2>145 146 <refsect 2id="{@id filter_conditions}">427 </refsect3> 428 429 <refsect3 id="{@id filter_conditions}"> 147 430 <title>Filter conditions</title> 148 431 … … 169 452 condition must come first and be placed in parentheses.</para> 170 453 171 <example>454 <example> 172 455 class UserPreferences extends ActionController 173 456 { … … 175 458 ... 176 459 } 177 </example> 460 </example> 461 </refsect3> 178 462 </refsect2> 463 </refsect1> 464 <refsect1 id="{@id render}"> 465 <title>Redirect Browser or Render Output</title> 466 467 <para>After the controller object's action method has returned to 468 <literal>ActionController::process_route()</literal> and the after 469 filters have been executed, the controller object is examined for 470 a property named <literal>redirect_to</literal>. If this 471 property exists and has a value, it means that the action method 472 has decided to redirect the user's browser to a different URL. The 473 value of the <literal>redirect_to</literal> property is passed to 474 {@link ActionController::redirect_to() <literal>redirect_to()</literal>} 475 which outputs a header redirecting the browser, then calls 476 {@link http://www.php.net/manual/en/function.exit.php exit} .</para> 477 478 <para> 479 If the action didn't redirect the browser, it should have provided 480 output to send to the browser. This is in the form of explicit 481 output produced by calls to 482 {@link http://www.php.net/manual/en/function.echo.php echo}, 483 {@link http://www.php.net/manual/en/function.print.php print} or 484 {@link http://www.php.net/manual/en/function.printf.php printf} , 485 plus any properties of the controller object that are referenced in 486 the layout. <literal>ActionController::process_route()</literal> 487 collects all output produced by the controller's action method 488 in the output buffer, for presentation within a layout. 489 </para> 490 491 <para> 492 If the controller object has a property 493 <literal>render_text</literal> which contains a string, then this 494 string is sent directly to the browser and all output and view 495 files are ignored. 496 </para> 497 498 <para> 499 If <literal>render_text</literal> is undefined or empty, then the 500 saved output of the controller's action method is to be rendered. 501 A <important>view file</important> determined by the action is 502 found and included. The view file for an action is 503 <literal>app/views/</literal><arg choice="tute-comment">controller_name/action_name</arg><literal>.phtml</literal> . 504 This file contains HTML, which goes to the output buffer after the 505 action method's output. The output buffer is now assigned to 506 $content_for_layout. Finally the layout file is loaded. The 507 view file and layout file both contain HTML with 508 {@link http://www.php.net/manual/en/language.basic-syntax.php embedded PHP} 509 expressions to present action method output to the user. 510 </para> 179 511 </refsect1> 180 512 <!-- -
trunk/trax/tutorials/PHPonTrax/PHPonTrax.pkg
r165 r174 269 269 To: 270 270 <example> 271 RewriteRule ^(.*)$ ~haas/dispatch.php?$1 [QSA,L]271 RewriteRule ^(.*)$ /~haas/dispatch.php?$1 [QSA,L] 272 272 </example> 273 273 </listitem> … … 303 303 <listitem> 304 304 With your favorite editor, open file 305 ~/trax/config/environment.php and change the line that defines306 TRAX_URL_PREFIXto:305 <literal>~/trax/config/environment.php</literal> and change the 306 line that defines <literal>TRAX_URL_PREFIX</literal> to: 307 307 <example> 308 308 define("TRAX_URL_PREFIX", "~haas"); 309 309 </example> 310 </listitem> 311 312 <listitem> 313 Change file permissions on the log files in ~/trax/log so that 314 Apache can write to them: 310 Also edit the line that defines <literal>TRAX_PUBLIC</literal> to 311 change <literal>public</literal> to <literal>public_html</literal>. 312 </listitem> 313 314 <listitem> 315 Change file permissions on the log files in 316 <literal>~/trax/log</literal> so that Apache can write to them: 315 317 <example> 316 318 $ <important>su -</important> … … 393 395 <refsect2 id="{@id ref_defines}"> 394 396 <title>Defines</title> 397 All the definitions below are set in file 398 {@link environment.php config/environment.php} 395 399 <itemizedlist> 400 401 <listitem><command>DEBUG</command> 402 <cmdsynopsis>boolean set to true if TRAX_MODE is 'development', 403 false otherwise. Determines whether PHP error messages should be 404 sent to the browser; true means show error messages on the 405 browser as well as the error log, false means send error messages 406 to only the error log. Implemented by calling 407 {@link http://www.php.net/ini_set ini_set()} 408 at the time DEBUG is defined. 409 </cmdsynopsis> 410 </listitem> 411 412 <listitem><command>PHP_LIB_ROOT</command> 413 <cmdsynopsis>Directory containing PHP libraries. This directory 414 will be added to the PHP 415 {@link http://www.php.net/manual/en/ini.core.php#ini.include-path include path}. 416 </cmdsynopsis> 417 </listitem> 418 419 <listitem><command>TRAX_LIB_ROOT</command> 420 <cmdsynopsis>Directory containing Trax libraries. Set to a 421 subdirectory of TRAX_ROOT or PHP_LIB_ROOT and added to the PHP 422 {@link http://www.php.net/manual/en/ini.core.php#ini.include-path include path}. 423 </cmdsynopsis> 424 </listitem> 396 425 397 426 <listitem><command>TRAX_MODE</command> … … 401 430 </listitem> 402 431 432 <listitem><command>TRAX_PATH_SEPERATOR</command> 433 <cmdsynopsis>Character to use as the separator when defining the 434 PHP include path. ';' for Windows, otherwise ':'. 435 </cmdsynopsis> 436 </listitem> 437 438 <listitem><command>TRAX_PUBLIC</command> 439 <cmdsynopsis>Subdirectory of the user's home directory referenced 440 by the Apache configuration variable <literal>UserDir</literal>. 441 </cmdsynopsis> 442 </listitem> 443 444 <listitem><command>TRAX_ROOT</command> 445 <cmdsynopsis>Filesystem path to the top of the Trax file tree. 446 </cmdsynopsis> 447 </listitem> 448 449 <listitem><command>TRAX_URL_PREFIX</command> 450 <cmdsynopsis>That part of a URL which refers to a Trax 451 application that comes after the domain name and before the 452 controller. Usually empty. In the case of a Trax application in 453 the home directory of user <literal>username</literal>, 454 <command>TRAX_URL_PREFIX</command> would be set to 455 '<literal>~username</literal>'. 456 </cmdsynopsis> 457 </listitem> 458 403 459 <listitem><command>TRAX_VIEWS_EXTENTION</command> 404 <cmdsynopsis>file extension for views</cmdsynopsis> 405 </listitem> 406 407 <listitem><command></command> 408 <cmdsynopsis> 409 </cmdsynopsis> 410 </listitem> 411 412 <listitem><command></command> 413 <cmdsynopsis> 414 </cmdsynopsis> 415 </listitem> 416 417 <listitem><command></command> 418 <cmdsynopsis> 419 </cmdsynopsis> 420 </listitem> 421 422 <listitem><command></command> 423 <cmdsynopsis> 424 </cmdsynopsis> 425 </listitem> 426 427 <listitem><command></command> 428 <cmdsynopsis> 429 </cmdsynopsis> 460 <cmdsynopsis>File extension for views, default 461 'phtml'.</cmdsynopsis> 430 462 </listitem> 431 463 -
trunk/trax/vendor/trax/action_controller.php
r167 r174 32 32 * Action controller 33 33 * 34 * <p><b>Filters</b></p> 34 * <p>The ActionController base class operates as follows:</p> 35 * <ol> 36 * <li>Accept a URL as input</li> 37 * <li>Translate the URL into a controller and action</li> 38 * <li>Create the indicated controller object (which is a subclass 39 * of ActionController) and call its action method</li> 40 * <li>Render the output of the action method</li> 41 * <li>Redirect to the next URL</li> 42 * </ol> 35 43 * 36 * <p>Filters enable controllers to run shared pre and post 37 * processing code for its actions. These filters can be used to do 38 * authentication, caching, or auditing before the intended action is 39 * performed. Or to do localization or output compression after the 40 * action has been performed.</p> 41 * 42 * <p>Filters have access to the request, response, and all the 43 * instance variables set by other filters in the chain or by the 44 * action (in the case of after filters). Additionally, it's possible 45 * for a pre-processing <samp>before_filter</samp> to halt the processing 46 * before the intended action is processed by returning false or 47 * performing a redirect or render. This is especially useful for 48 * filters like authentication where you're not interested in 49 * allowing the action to be performed if the proper credentials are 50 * not in order.</p> 51 * 52 * <p><b>Filter inheritance</b></p> 53 * 54 * <p>Controller inheritance hierarchies share filters downwards, but 55 * subclasses can also add new filters without affecting the 56 * superclass. For example:</p> 57 * 58 * <pre> 59 * class BankController extends ActionController 60 * { 61 * $this->before_filter = audit(); 62 * 63 * private function audit() { 64 * <i>record the action and parameters in an audit log</i> 65 * } 66 * } 67 * 68 * class VaultController extends BankController 69 * { 70 * $this->before_filter = verify_credentials(); 71 * 72 * private function verify_credentials() { 73 * <i>make sure the user is allowed into the vault</i> 74 * } 75 * } 76 * </pre> 77 * 78 * <p>Now any actions performed on the BankController will have the 79 * audit method called before. On the VaultController, first the 80 * audit method is called, then the verify_credentials method. If the 81 * audit method returns false, then verify_credentials and the 82 * intended action are never called.</p> 83 * 84 * <p><b>Filter types</b></p> 85 * 86 * <p>A filter can take one of three forms: method reference 87 * (symbol), external class, or inline method (proc). The first is the 88 * most common and works by referencing a protected or private method 89 * somewhere in the inheritance hierarchy of the controller by use of 90 * a symbol. In the bank example above, both BankController and 91 * VaultController use this form.</p> 92 * 93 * <p>Using an external class makes for more easily reused generic 94 * filters, such as output compression. External filter classes are 95 * implemented by having a static +filter+ method on any class and 96 * then passing this class to the filter method. Example:</p> 97 * 98 * <pre> 99 * class OutputCompressionFilter 100 * { 101 * static functionfilter(controller) { 102 * controller.response.body = compress(controller.response.body) 103 * } 104 * } 105 * 106 * class NewspaperController extends ActionController 107 * { 108 * $this->after_filter = OutputCompressionFilter; 109 * } 110 * </pre> 111 * 112 * <p>The filter method is passed the controller instance and is 113 * hence granted access to all aspects of the controller and can 114 * manipulate them as it sees fit.</p> 115 * 116 * <p>The inline method (using a proc) can be used to quickly do 117 * something small that doesn't require a lot of explanation. Or 118 * just as a quick test. It works like this:</p> 119 * 120 * <pre> 121 * class WeblogController extends ActionController 122 * { 123 * before_filter { |controller| false if controller.params["stop_action"] } 124 * } 125 * </pre> 126 * 127 * <p>As you can see, the block expects to be passed the controller 128 * after it has assigned the request to the internal variables. This 129 * means that the block has access to both the request and response 130 * objects complete with convenience methods for params, session, 131 * template, and assigns. Note: The inline method doesn't strictly 132 * have to be a block; any object that responds to call and returns 1 133 * or -1 on arity will do (such as a Proc or an Method object).</p> 134 * 135 * <p><b>Filter chain skipping</b></p> 136 * 137 * <p>Some times its convenient to specify a filter chain in a superclass 138 * that'll hold true for the majority of the subclasses, but not necessarily 139 * all of them. The subclasses that behave in exception can then specify 140 * which filters they would like to be relieved of. Examples</p> 141 * 142 * <pre> 143 * class ApplicationController extends ActionController 144 * { 145 * $this->before_filter = authenticate(); 146 * } 147 * 148 * class WeblogController extends ApplicationController 149 * { 150 * // will run the authenticate() filter 151 * } 152 * </pre> 153 * 154 * <p><b>Filter conditions</b></p> 155 * 156 * <p>Filters can be limited to run for only specific actions. This 157 * can be expressed either by listing the actions to exclude or 158 * the actions to include when executing the filter. Available 159 * conditions are +:only+ or +:except+, both of which accept an 160 * arbitrary number of method references. For example:</p> 161 * 162 * <pre> 163 * class Journal extends ActionController 164 * { 165 * // only require authentication if the current action is edit or delete 166 * before_filter :authorize, :only => [ :edit, :delete ] 167 * 168 * private function authorize() { 169 * // redirect to login unless authenticated 170 * } 171 * } 172 * </pre> 173 * 174 * <p>When setting conditions on inline method (proc) filters the 175 * condition must come first and be placed in parentheses.</p> 176 * 177 * <pre> 178 * class UserPreferences extends ActionController 179 * { 180 * before_filter(:except => :new) { ? some proc ... } 181 * * ... 182 * } 183 * </pre> 44 * For details see the 45 * {@tutorial PHPonTrax/ActionController.cls class tutorial} 184 46 */ 185 47 class ActionController { … … 202 64 203 65 /** 204 * @todo Document this attribute 66 * Value of :id parsed from URL then forced to lower case 67 * 68 * Set by {@link recognize_route()} 69 * @var string 205 70 */ 206 71 private $id; … … 223 88 224 89 /** 90 * Filesystem path to ../app/helpers/<i>extras</i> directory 91 * 92 * Set by {@link recognize_route()}, {@link set_paths()} 93 * @var string 94 */ 95 private $helpers_path; 96 97 /** 225 98 * Filesystem path to ../app/helpers/ directory 226 99 * … … 228 101 * @var string 229 102 */ 230 private $helpers_path;231 232 /**233 * @todo Document this attribute234 */235 103 private $helpers_base_path; 236 104 237 105 /** 238 * Filesystem path to ../app/layouts/ directory 239 * 240 * Set by {@link recognize_route()} 241 * @todo <b>FIXME:</> declare $layouts_base_path 106 * Filesystem path to ../app/views/layouts/<i>extras</i> directory 107 * 108 * Set by {@link recognize_route()}, {@link set_paths()} 242 109 * @var string 243 110 */ 244 111 private $layouts_path; 112 113 /** 114 * Filesystem path to ../app/views/layouts/ directory 115 * 116 * Set by {@link recognize_route()} 117 * @var string 118 */ 119 private $layouts_base_path; 245 120 246 121 /** … … 263 138 264 139 /** 265 * @todo Document this attribute 140 * Filesystem path to the controllername_helper.php file 141 * 142 * Set by {@link recognize_route()} 143 * @var string 266 144 */ 267 145 private $helper_file; … … 306 184 307 185 /** 186 * List of additional helper files for this controller object 187 * 188 * Set by {@link add_helper()} 189 * @var string[] 190 */ 191 private $helpers = array(); 192 193 /** 194 * List of filters to execute before calling action method 195 * 196 * Set by {@link add_before_filters() 197 * @var string[] 198 */ 199 private $before_filters = array(); 200 201 /** 202 * List of filters to execute after calling action method 203 * 204 * Set by {@link add_after_filters() 205 * @var string[] 206 */ 207 private $after_filters = array(); 208 209 /** 308 210 * @todo Document this attribute 309 211 */ 310 pr ivate $helpers = array();212 protected $before_filter = null; 311 213 312 214 /** 313 215 * @todo Document this attribute 314 216 */ 315 private $before_filters = array();316 317 /**318 * @todo Document this attribute319 */320 private $after_filters = array();321 322 /**323 * @todo Document this attribute324 */325 protected $before_filter = null;326 327 /**328 * @todo Document this attribute329 */330 217 protected $after_filter = null; 331 218 … … 340 227 341 228 /** 342 * @todo Document this attribute 229 * Filesystem path to the view file selected for this action 230 * 231 * Set by {@link process_route() 232 * @var string 343 233 */ 344 234 public $view_file; … … 371 261 /** 372 262 * @todo Document this attribute 263 * @todo <b>FIXME:</b> Not referenced in this class - is it used 264 * by subclasses? If so, for what? 373 265 */ 374 266 public $asset_host = null; 375 267 376 268 /** 377 * @todo Document this attribute 269 * File extension appended to view files 270 * 271 * Set from a define in {@link environment.php}. Usually phtml 272 * @var string 378 273 */ 379 274 public $views_file_extention = TRAX_VIEWS_EXTENTION; … … 462 357 * Convert URL to controller, action and id 463 358 * 464 * Parse the URL in $_SERVER['REDIRECT_URL'] into elements. 359 * Parse the URL in 360 * {@link 361 * http://www.php.net/manual/en/reserved.variables.php#reserved.variables.server $_SERVER}['REDIRECT_URL'] 362 * into elements. 465 363 * Compute filesystem paths to the various components used by the 466 364 * URL and store the paths in object private variables. … … 499 397 # current url 500 398 $browser_url = $_SERVER['REDIRECT_URL']; 399 //error_log('browser url='.$browser_url); 501 400 # strip off url prefix, if any 502 401 if(!is_null(TRAX_URL_PREFIX)) { 503 $browser_url = str_replace(TRAX_URL_PREFIX,"",$browser_url); 504 } 402 // FIXME: Do we know for sure that the 403 // initial '/' will be there? 404 $browser_url = str_replace('/'.TRAX_URL_PREFIX,"",$browser_url); 405 } 406 //error_log('browser url='.$browser_url); 505 407 506 408 # strip leading slash 409 // FIXME: Do we know for sure that the 410 // initial '/' will be there? 507 411 $browser_url = substr($browser_url,1); 412 //error_log('browser url='.$browser_url); 508 413 509 414 # strip trailing slash (if any) … … 511 416 $browser_url = substr($browser_url, 0, -1); 512 417 } 418 //error_log('browser url='.$browser_url); 513 419 514 420 if($browser_url) { … … 528 434 529 435 $route = $this->router->find_route($browser_url); 436 437 // find_route() returns an array if it finds a path that 438 // matches the URL, null if no match found 530 439 if(is_array($route)) { 531 440 $this->set_paths(); … … 538 447 $this->controller = strtolower($this->url_path[@array_search(":controller", $route_path)]); 539 448 } 540 449 //error_log('controller='.$this->controller); 541 450 if(@array_key_exists(":action",$route_params)) { 542 451 $this->action = $route_params[':action']; … … 546 455 $this->action = strtolower($this->url_path[@array_search(":action", $route_path)]); 547 456 } 548 457 //error_log('action='.$this->action); 458 // FIXME: RoR uses :name as a keyword parameter, id 459 // is not treated as a special case. 460 // Do we want to do the same? 549 461 if(@in_array(":id",$route_path) 550 462 && array_key_exists(@array_search(":id", $route_path), … … 555 467 } 556 468 } 557 469 //error_log('id='.$this->id); 558 470 $this->views_path .= "/" . $this->controller; 559 471 $this->controller_file = $this->controllers_path . "/" . $this->controller . "_controller.php"; … … 573 485 574 486 /** 575 * @todo Document this method 487 * Parse URL, extract controller and action and execute them 488 * 576 489 * @uses $action 577 490 * @uses $application_controller_file … … 591 504 * @uses raise() 592 505 * @uses ScaffoldController 593 * @uses Session::unset ()506 * @uses Session::unset_var() 594 507 * @uses $view_file 595 508 * @uses $views_file_extention … … 599 512 function process_route() { 600 513 514 $render_layout = true; 601 515 # First try to load the routes and setup the paths to everything 602 516 if(!$this->loaded) { … … 616 530 } 617 531 618 error_log('process_route() controller="'.$this->controller619 .'" action="'.$this->action.'"');532 //error_log('process_route() controller="'.$this->controller 533 // .'" action="'.$this->action.'"'); 620 534 # Include the controller file and execute action 621 535 // FIXME: redundant, recognize_route() already test for file exists 622 if (file_exists($this->controller_file)) {536 if (file_exists($this->controller_file)) { 623 537 include_once($this->controller_file); 624 538 if(class_exists($this->controller_class,false)) { … … 633 547 $GLOBALS['current_action_name'] = $this->action; 634 548 $GLOBALS['current_controller_object'] =& $this->controller_object; 635 error_log('$GLOBALS[\'current_action_name\']='636 .$GLOBALS['current_action_name']);637 error_log('$GLOBALS[\'current_controller_name\']='638 .$GLOBALS['current_controller_name']);639 error_log('$GLOBALS[\'current_controller_path\']='640 .$GLOBALS['current_controller_path']);549 // error_log('$GLOBALS[\'current_action_name\']=' 550 // .$GLOBALS['current_action_name']); 551 // error_log('$GLOBALS[\'current_controller_name\']=' 552 // .$GLOBALS['current_controller_name']); 553 // error_log('$GLOBALS[\'current_controller_path\']=' 554 // .$GLOBALS['current_controller_path']); 641 555 } 642 556 643 557 # Which layout should we use? 644 558 $layout_file = $this->determine_layout(); 645 error_log('layout_file="'.$layout_file.'"');646 # Check if there is any defined scaffolding to load559 // error_log('layout_file="'.$layout_file.'"'); 560 // # Check if there is any defined scaffolding to load 647 561 if(isset($this->controller_object->scaffold)) { 648 562 $scaffold = $this->controller_object->scaffold; … … 694 608 # Call the controller method based on the URL 695 609 $this->execute_before_filters(); 610 // FIXME: shouldn't we check return here? 696 611 if(method_exists($this->controller_object, $this->action)) { 697 error_log('controller has method "'.$this->action.'"');612 //error_log('controller has method "'.$this->action.'"'); 698 613 $action = $this->action; 699 614 $this->controller_object->$action(); 700 615 } elseif(file_exists($this->views_path . "/" . $this->action . "." . $this->views_file_extention)) { 701 error_log('views file "'.$this->action.'"');616 // error_log('views file "'.$this->action.'"'); 702 617 $action = $this->action; 703 618 } elseif(method_exists($this->controller_object, "index")) { 704 error_log('calling index()');619 //error_log('calling index()'); 705 620 $this->controller_object->index(); 706 621 } else { 707 error_log('no action');622 //error_log('no action'); 708 623 $this->raise("No action responded to ".$this->action, "Unknown action", "404"); 709 624 } … … 714 629 && $this->controller_object->redirect_to != '') { 715 630 $this->redirect_to($this->controller_object->redirect_to); 631 // redirect_to() exits instead of returning 716 632 } else { 717 633 # Pull all the class vars out and turn them from $this->var to $var … … 725 641 # If this isn't a scaffolding then get the view file to include 726 642 if(!isset($scaffold)) { 643 // error_log('not scaffolding, looking for view file'); 727 644 # Normal processing of the view 728 645 if(isset($this->controller_object->render_action) … … 735 652 $this->view_file = $this->views_path . "/" . "index" . "." . $this->views_file_extention; 736 653 } 654 // error_log('view file='.$this->view_file); 737 655 } 738 656 … … 748 666 ob_end_clean(); 749 667 668 //error_log('layout file='.$layout_file 669 // .' render_layout=' 670 // .var_export($render_layout,true)); 750 671 if(file_exists($layout_file) && $render_layout !== false) { 751 672 # render the layout … … 765 686 } 766 687 688 // error_log('keep flash='.var_export($this->keep_flash,true)); 767 689 if(!$this->keep_flash) { 768 690 # Nuke the flash … … 774 696 775 697 /** 776 * @todo Document this method 698 * Extend the search path for components 699 * 700 * On entry, $url_path is set according to the browser's URL and 701 * $controllers_path has been set according to the configuration 702 * in {@link environment.php config/environment.php} . Examine 703 * the $controllers_path directory for files or directories that 704 * match any component of the URL. If one is found, add that 705 * component to all paths. Replace the contents of $url_path 706 * with the list of URL components that did NOT match any files 707 * or directories. 777 708 * @uses $added_path 778 709 * @uses $controllers_path 779 710 * @uses $helpers_path 780 * @uses $layou s_path711 * @uses $layouts_path 781 712 * @uses $views_path 782 713 * @uses $url_path 714 * @todo <b>FIXME:</b> Creating a file or directory in 715 * app/controllers with the same name as a controller, action or 716 * other URL element will hijack the browser! 783 717 */ 784 718 function set_paths() { … … 808 742 /** 809 743 * Execute the before filters 744 * @uses $before_filters 810 745 */ 811 746 function execute_before_filters() { … … 825 760 * one filter function, or array of strings with the names of 826 761 * several filter functions. 762 * @uses $before_filters 827 763 */ 828 764 function add_before_filter($filter_function_name) { … … 840 776 } 841 777 778 /** 779 * Execute the after filters 780 * @uses $after_filters 781 */ 842 782 function execute_after_filters() { 843 783 if(count($this->controller_object->after_filters) > 0) { … … 857 797 * one filter function, or array of strings with the names of 858 798 * several filter functions. 799 * @uses $after_filters 859 800 */ 860 801 function add_after_filter($filter_function_name) { … … 872 813 } 873 814 815 /** 816 * Add a helper to the list of helpers used by a controller 817 * object 818 * 819 * @param $helper_name string Name of a helper to add to the list 820 * @uses $helpers 821 * @uses $controller_object 822 */ 874 823 function add_helper($helper_name) { 875 824 if(!in_array($helper_name, $this->controller_object->helpers)) { … … 969 918 970 919 /** 971 * @todo Document this method 920 * Select a layout file based on the controller object 921 * 972 922 * @uses $controller_object 973 923 * @uses $layouts_base_path … … 979 929 # I guess you don't want any layout 980 930 if($this->controller_object->layout == "null") { 931 //error_log('controller->layout absent'); 981 932 return null; 982 933 } … … 1015 966 1016 967 /** 1017 * Redirects the browser to the target specified in options. This parameter can take one of three forms: 1018 * 1019 * * Array: The URL will be generated by calling url_for() with the options. 1020 * * String starting with protocol:// (like http://): Is passed straight through as the target for redirection. 1021 * * String not containing a protocol: The current protocol and host is prepended to the string. 1022 * * back: Back to the page that issued the request. Useful for forms that are triggered from multiple places. Short-hand for redirect_to(request.env["HTTP_REFERER"]) 1023 * 1024 * Examples: 1025 * 1026 * redirect_to(array(":action" => "show", ":id" => 5)) 1027 * redirect_to("http://www.rubyonrails.org") 1028 * redirect_to("/images/screenshot.jpg") 1029 * redirect_to("back") 1030 * 1031 * @param mixed $options array or string url 968 * Redirect the browser to a specified target 969 * 970 * Redirect the browser to the target specified in $options. This 971 * parameter can take one of three forms: 972 * <ul> 973 * <li>Array: The URL will be generated by calling 974 * {@link url_for()} with the options.</li> 975 * <li>String starting with a protocol:// (like http://): Is 976 * passed straight through as the target for redirection.</li> 977 * <li>String not containing a protocol: The current protocol 978 * and host is prepended to the string.</li> 979 * <li>back: Back to the page that issued the request. Useful 980 * for forms that are triggered from multiple 981 * places. Short-hand for redirect_to(request.env["HTTP_REFERER"]) 982 * </ul> 983 * 984 * Examples: 985 * <ul> 986 * <li>redirect_to(array(":action" => "show", ":id" => 5))</li> 987 * <li>redirect_to("http://www.rubyonrails.org")</li> 988 * <li>redirect_to("/images/screenshot.jpg")</li> 989 * <li>redirect_to("back")</li> 990 * </ul> 991 * 992 * @param mixed $options array or string url 993 * @todo <b>FIXME:</b> Make header configurable 1032 994 */ 1033 995 function redirect_to($options = null) { -
trunk/trax/vendor/trax/router.php
r162 r174 106 106 * empty string, it matches a path that is an empty string. 107 107 * Otherwise, try to match $url to the path part of the table 108 * entry according to {@link http://www.php.net/manual/en/ref.pcre.php Perl regular expression} 109 * rules. Return the first matching route to the caller, and 110 * also save a copy in {@link $selected_route}. 108 * entry according to 109 * {@link http://www.php.net/manual/en/ref.pcre.php Perl regular expression} 110 * rules. If a matching route is found, return it any to the caller, and 111 * also save a copy in {@link $selected_route}; if no matching 112 * route is found return null. 111 113 * @param string $url 112 114 * @uses build_route_regexp() … … 115 117 * @uses $routes_count 116 118 * @uses $selected_route 117 * @return string[] Selected route.Path is in return['path'],119 * @return mixed Matching route or null. Path is in return['path'], 118 120 * params in return['params'], 119 121 */ 120 122 function find_route($url) { 123 //error_log('url='.$url); 121 124 // ensure at least one route (the default route) exists 122 125 if($this->routes_count == 0) { … … 130 133 unset($reg_exp); 131 134 $route_regexp = $this->build_route_regexp($route['path']); 135 //error_log("route regexp=/$route_regexp/"); 132 136 if($url == "" && $route_regexp == "") { 137 //error_log('selected'); 133 138 $this->selected_route = $route; 134 139 break; 135 140 } elseif(preg_match("/$route_regexp/",$url) && $route_regexp != "") { 141 //error_log('selected'); 136 142 $this->selected_route = $route; 137 143 break; 138 144 } elseif($route['path'] == $this->default_route_path) { 145 //error_log('defaulted'); 139 146 $this->selected_route = $route; 140 147 break; 141 148 } 142 149 } 143 150 //error_log('selected route='.var_export($this->selected_route,true)); 144 151 return $this->selected_route; 145 152 } // function find_route($url) … … 148 155 * Build a regular expression that matches a route 149 156 * 150 * <b>FIXME:</b> Should this method be private? 157 * @todo <b>FIXME:</b> Should this method be private? 158 * @todo <b>FIXME:</b> Shouldn't the regexp match be the same as 159 * for a PHP variable name? '[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*' 151 160 * @param string $route_path A route path. 152 161 * @return string Regular expression that matches the route in … … 161 170 $route_path = explode("/",$route_path); 162 171 } 163 // echo "route path:\n"; 164 // var_dump($route_path); 172 //error_log("route path:\n".var_export($route_path,true)); 165 173 if(count($route_path) > 0) { 166 174 foreach($route_path as $path_element) { … … 175 183 } 176 184 } 177 178 185 return $route_regexp; 179 186 } -
trunk/trax/vendor/trax/session.php
r159 r174 172 172 $key .= $_SERVER['REMOTE_ADDR']; 173 173 } 174 // error_log('get_hash() returns '.md5($key)); 174 175 return md5($key); 175 176 } … … 242 243 */ 243 244 function unset_var($key) { 244 if(self::is_valid_host()) { 245 // error_log('Session::unset_var("'.$key.'")'); 246 if(self::is_valid_host()) { 247 // error_log('before unsetting SESSION='.var_export($_SESSION,true)); 245 248 unset($_SESSION[self::get_hash()][$key]); 249 // error_log('after unsetting SESSION='.var_export($_SESSION,true)); 246 250 } 247 251 }
