Changeset 174 for trunk/trax/vendor/trax/action_controller.php
- Timestamp:
- 03/13/06 21:10:15 (6 years ago)
- Files:
-
- 1 modified
-
trunk/trax/vendor/trax/action_controller.php (modified) (35 diffs)
Legend:
- Unmodified
- Added
- Removed
-
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) {
