Simplifying Controller logic with Exceptions
In the ongoing struggle against code bloat and creative ways to do things, I’ve been playing with throwing exceptions from model methods. While not earth shattering stuff, I’ve found it to have a few advantages over returning false
. First, you can end up with less if
and else
statements. Secondly, I find putting error messages where the errors occur helps keep my errors close to their source, and removes duplicate error messages when methods are used more than once.
- public function downloadResource($url, $userId, $type = 'image') {
- throw new OutOfBoundsException(__('Invalid media type', true));
- }
- $this->_loadSocket($url);
- $resource = $this->Socket->get($url);
- throw new OutOfBoundsException(__('Submitted url has no Mime-Type', true));
- }
- $allowedContentTypes = $this->_fetchMimes[$type];
- throw new OutOfBoundsException(__('Submitted url has an invalid Mime-Type', true));
- }
- 'file' => $this->_saveFetchedFile($resource, $url, $this->Socket->response['header']['Content-Type']),
- 'user_id' => $userId,
- 'title' => $url,
- )
- );
- $this->create($newMedia);
- if ($this->save()) {
- return true;
- }
- return false;
- }
This method downloads a resource from a remote address and saves it to the local filesystem and database. As you can see this method throws OutOfBoundsExceptions
when something outside its boundaries is done. The SPL library built into recent versions of PHP provides a number of very useful exception classes to use, and of course you can always make your own exception types. But most of the times I find the built in Exceptions expressive enough.
When using this method we slim down our controller method considerably as well. We no longer have to perform several if
checks for various things going wrong. We can simply write the ‘good’ code path and the error handling code path.
- try {
- $this->File->downloadResource($this->data['File']['url'], $this->Auth->user('id'), 'image');
- //do some additional file handling and data processing.
- $this->Session->setFlash(__('File uploaded successfully', true));
- } catch(OutOfBoundsException $e) {
- $this->Session->setFlash($e->getMessage());
- }
Testing the failures of methods with exceptions is easier in some ways as well. Instead of having to assert the false return you can directly send pass()
and fail()
messages to the test case.
- try {
- $this->File->downloadResource('http:/bogus.com/', 1, 'image');
- $this->fail('No exception thrown with bogus arguments');
- } catch (Exception $e) {
- $this->pass('Exception thrown');
- }
While exceptions are very useful at times, I don’t think they should universally replace all regular return false situations. For example throwing exceptions from Helpers is just going to cause more pain than is really necessary. But like any tool used correctly and you can goto
nice maintainable code.
I totally agree. Granted, I used to be a Java developer I was a PHP, and I’ve been a C++ developer for over 17 years, but exceptions tend to be under-utilized in PHP.
Lately all my CakePHP apps are bloated with exceptions and interfaces. They really help out organize your code, and even better ease your debugging (just do a try { … } catch(Exception $e) { var_dump($e); } and you get a nice trackback)
Mariano Iglesias on 6/12/09
I saw you’ve started using method visibility keywords in code in you posts. :)
Maybe is offtopic for this post, but is there any plans to make Cake PHP5 and using interfaces and all of that PHP5 stuff?
Rafal Grzegorek on 6/12/09
Great! Models are a perfect spot for using exceptions. Any other location you’ve found particularly useful?
I have found exceptions to be useful in components as well.
Marc Grabanski on 6/13/09
@Rafal: yes, PHP5 only support is planned for CakePHP v2.0
Torsten on 6/15/09
ah ! exceptions are really cool … the basic idea is to use em whenever you need to halt execution and preferrably get a back trace or at least some info about the situation to zero in.
but BEWARE ! Dont let em suck you in … the first time I got “trigger happy” with these and ended making my app’s code base really rigid
SayB on 6/17/09
One nice thing about exceptions is that they will bubble up the call stack so you can handle them at the point you want to which may not be in the calling function. If an exception bubbles all the way up the stack you know something seriously has gone wrong.
We use the php handler as a line underneath our AppController to catch any exceptions and bubble up to the top so that we can give a nice clean error page to users for uncaught exceptions.
/** * This is a php standard trap for any exceptions. This will catch all thrown exceptions (not application * exceptions) that are thrown by the app but not caught in catch() blocks */
set_exception_handler(array(‘AppController’, ‘handleException’));
David Wu on 7/23/09