dispatcher = $dispatcher; $this->erase_response(); } /** * Resets the response of the controller * * @return void */ function erase_response() { $this->performed = FALSE; $this->response = new Trails_Response(); } /** * Return this controller's response * * @return mixed the controller's response */ function get_response() { return $this->response; } /** * This method extracts an action string and further arguments from it's * parameter. The action string is mapped to a method being called afterwards * using the said arguments. That method is called and a response object is * generated, populated and sent back to the dispatcher. * * @param type * * @return type */ function perform($unconsumed) { list($action, $args, $format) = $this->extract_action_and_args($unconsumed); $this->format = isset($format) ? $format : 'html'; $before_filter_result = $this->before_filter($action, $args); # send action to controller # TODO (mlunzena) shouldn't the after filter be triggered too? if (!(FALSE === $before_filter_result || $this->performed)) { $callable = $this->map_action($action); if (is_callable($callable)) { call_user_func_array($callable, $args); } else { $this->does_not_understand($action, $args); } if (!$this->performed) { $this->render_action($action); } $this->after_filter($action, $args); } return $this->response; } /** * Extracts action and args from a string. * * @param string the processed string * * @return array an array with two elements - a string containing the * action and an array of strings representing the args */ function extract_action_and_args($string) { if ('' === $string) { return $this->default_action_and_args(); } // find optional file extension $format = NULL; if (preg_match('/^(.*[^\/.])\.(\w+)$/', $string, $matches)) { list($_, $string, $format) = $matches; } // TODO this should possibly remove empty tokens $args = explode('/', $string); $action = array_shift($args); return array($action, $args, $format); } /** * Return the default action and arguments * * @return an array containing the action, an array of args and the format * */ function default_action_and_args() { return array('index', array(), NULL); } /** * Maps the action to an actual method name. * * @param string the action * * @return string the mapped method name */ function map_action($action) { return array(&$this, $action . '_action'); } /** * Callback function being called before an action is executed. If this * function does not return FALSE, the action will be called, otherwise * an error will be generated and processing will be aborted. If this function * already #rendered or #redirected, further processing of the action is * withheld. * * @param string Name of the action to perform. * @param array An array of arguments to the action. * * @return bool */ function before_filter(&$action, &$args) { } /** * Callback function being called after an action is executed. * * @param string Name of the action to perform. * @param array An array of arguments to the action. * * @return void */ function after_filter($action, $args) { } /** * * * @param type * @param type * * @return void */ function does_not_understand($action, $args) { throw new Trails_UnknownAction("No action responded to '$action'."); } /** * * * @param string * * @return void */ function redirect($to) { if ($this->performed) { throw new Trails_DoubleRenderError(); } $this->performed = TRUE; # get uri; keep absolute URIs $url = preg_match('#^(/|\w+://)#', $to) ? $to : $this->url_for($to); $this->response->add_header('Location', $url)->set_status(302); } /** * Renders the given text as the body of the response. * * @param string the text to be rendered * * @return void */ function render_text($text = ' ') { if ($this->performed) { throw new Trails_DoubleRenderError(); } $this->performed = TRUE; $this->response->set_body($text); } /** * Renders the empty string as the response's body. * * @return void */ function render_nothing() { $this->render_text(''); } /** * Renders the template of the given action as the response's body. * * @param string the action * * @return void */ function render_action($action) { $this->render_template($this->get_default_template($action), $this->layout); } function get_default_template($action) { $class = get_class($this); $controller_name = Trails_Inflector::underscore(substr($class, 0, -10)); return $controller_name.'/'.$action; } /** * Renders a template using an optional layout template. * * @param mixed a flexi template * @param mixes a flexi template which is used as layout * * @return void */ function render_template($template_name, $layout = NULL) { # open template $factory = $this->get_template_factory(); $template = $factory->open($template_name); $template->set_attributes($this->get_assigned_variables()); if (isset($layout)) { $template->set_layout($layout); } $this->render_text($template->render()); } /** * Create and return a template factory for this controller. * * @return Flexi\Factory */ function get_template_factory() { return new Flexi\Factory($this->dispatcher->trails_root . '/views/'); } /** * This method returns all the set instance variables to be used as attributes * for a template. This controller is returned too as value for * key 'controller'. * * @return array an associative array of variables for the template */ function get_assigned_variables() { $assigns = array(); $protected = get_class_vars(get_class($this)); foreach (get_object_vars($this) as $var => $value) { if (!array_key_exists($var, $protected)) { $assigns[$var] =& $this->$var; } } $assigns['controller'] = $this; return $assigns; } /** * Sets the layout to be used by this controller per default. * * @param mixed a flexi template to be used as layout * * @return void */ function set_layout($layout) { $this->layout = $layout; } /** * Returns a URL to a specified route to your Trails application. * * Example: * Your Trails application is located at 'http://example.com/dispatch.php'. * So your dispatcher's trails_uri is set to 'http://example.com/dispatch.php' * If you want the URL to your 'wiki' controller with action 'show' and * parameter 'page' you should send: * * $url = $controller->url_for('wiki/show', 'page'); * * $url should then contain 'http://example.com/dispatch.php/wiki/show/page'. * * The first parameter is a string containing the controller and optionally an * action: * * - "{controller}/{action}" * - "path/to/controller/action" * - "controller" * * This "controller/action" string is not url encoded. You may provide * additional parameter which will be urlencoded and concatenated with * slashes: * * $controller->url_for('wiki/show', 'page'); * -> 'wiki/show/page' * * $controller->url_for('wiki/show', 'page', 'one and a half'); * -> 'wiki/show/page/one+and+a+half' * * @param string a string containing a controller and optionally an action * @param strings optional arguments * * @return string a URL to this route */ function url_for($to/*, ...*/) { # urlencode all but the first argument $args = func_get_args(); $args = array_map('urlencode', $args); $args[0] = $to; return $this->dispatcher->trails_uri . '/' . join('/', $args); } /** * * * @param type * * @return type */ function set_status($status, $reason_phrase = NULL) { $this->response->set_status($status, $reason_phrase); } /** * Sets the content type of the controller's response. * * @param string the content type * * @return void */ function set_content_type($type) { $this->response->add_header('Content-Type', $type); } /** * Exception handler called when the performance of an action raises an * exception. * * @param object the thrown exception * * @return object a response object */ function rescue($exception) { return $this->dispatcher->trails_error($exception); } function respond_to($ext) { return $this->format === $ext; } }