Yielding Control to a Different Controller
Recently I've implemented a very nice feature in PINQ: the ability to yield control to a different controller. The way it works is by throwing a specific exception and the catch for that exception is within a do-while loop. Inside that loop is the code that parses a route, instantiate a controller and dispatch to an action. The catch changes the route to parse and tells the loop to iterate one more time, thus allowing the programmer to change the controller/action being executed.
This feature's main use case is error handling. With a proper error controller set up, such as the one below:
<?php
class ErrorController extends PinqController {
public $layout_file = 'error';
public function ANY_403() { set_http_status(403); }
public function ANY_404() { set_http_status(404); }
public function ANY_405() { set_http_status(405); }
public function ANY_500() { set_http_status(500); }
}
I can then do: yield('/error/404') for example, or more easily do yield(ERROR_404);. This feature allows me to move the presentation logic for errors into controllers that are under the control of the programmer (ie: in the application directory) and also not make common errors not something that needs to be handled separately of the rest of the program.
Another use case is extending the class that causes this change in control flow. For example, my DatabaseException class now causes an error 500 when thrown as usual. Here is the code to do that:
<?php
class DatabaseException extends YieldControlException {
public function __construct($message) {
parent::__construct(ERROR_500);
$this->message = $message;
}
}
I think this is an extremely sweet solution to the problem of gracefully handling certain kinds of errors.
Comments
-
Hoi Peter! =o) If you're wondering if anybody's reading your very interesting posts - I do. Have a nice day, Tom
posted by Thasmo on Jun 28, 2008 at 3:55pm -
I think a better solution would be to throw exceptions and catch them at lower levels for error handling and nicer display. throw new Error_404("User X not found"); Any other generic uncaught exceptions would produce a 500 Internal Server Error.
posted by Vance Lucas on Nov 19, 2008 at 12:26pm -
The main idea of the yield control except (since renamed to MetaResponse) is to change the resource entirely--as though a new response is being made. With that in mind, it made sense to let it bubble up entirely in order to properly escape the current response and then change the application to a new one.
posted by Peter Goodman on Nov 19, 2008 at 12:40pm
To that end, a 404 or 500 is really just a different response sent to the server. 404 and other http-related errors don't make sense as normal exception, i.e. there is no way to 'handle' a 404 if it is caught other than to display something. With this in mind, the meta response for errors is really just the second step, e.g:
either an error occured and an exception was caught, then we throw a 500 meta response if we can't handle it, or if we know there's nothing we can do to recover from the error we can throw a 500 meta response right away. It's purpose isn't to solve any problems, merely change the response.
Comment
