Recipe for debugging with XDebug, Netbeans and CakePHP

It took me a while to get this working so I didn’t spend all my time debugging the debugging. I’ll be brief and hope that you don’t run into any problems following this : )

Ingredients:

  • XDebug latest (new version out I notice, must try that soon)
  • PHP 5.2 or 5.3
  • Netbeans 7.1
  • Local install of Apache HTTPD server
  • CakePHP 1.3
  • Windows 7

Cooking time:

5 – 15 mins according to taste.

Preparation:

  1. Add XDebug extension to your php.ini file. Note with PHP 5.2 you must useĀ zend_extension_ts and with PHP 5.3 you must useĀ zend_extension. Restart Apache.
  2. Set the run configuration for your Netbeans project so that index file is webroot/index.php (or webroot/test.php works, I think webroot/ is the key). Open the Advanced dialog and tell it not to open the web browser, “Do Not Open Web Browser”!
  3. Open Netbeans / Options / PHP / General / Debugging and check “Stop at first line”. Haven’t had much success otherwise. Check “Watches and Balloon Evaluations” if you want but this sometimes causes problems.
  4. Select Debug / Debug Project. Netbeans will start listening for the debugger.
  5. Use Firefox. Install easy XDebug plugin.
  6. Click the green bug icon at the bottom-right of Firefox. XDebug will turn on.
  7. Refresh the page on your site and you should be able to step through your scripts and set breakpoints as necessary.

You might find in some situations Netbeans loses the connection to XDebug. Closing and reopening Netbeans is usually necessary to get it working again, but nevertheless, this is the best experience I’ve had debugging PHP so far. Comments and suggestions welcome.

CakePHP: throw Exceptions with gay abandon

Throwing exceptions when appropriate is a much cleaner way to handle errors in your PHP applications than the usual return false; and the inevitable if statement to check whether your method call worked. Not only does it make your code cleaner, you actually get some information about the error.

In a CakePHP MVC structure it’s fun to throw Exceptions from your models, but including a try/catch in every single controller method starts to get unwieldy. Instead you can create an exception handler in your AppController, something like this:

    public function beforeFilter()
    {
        set_exception_handler(array($this, 'handleException'));
        parent::beforeFilter();
    }

    public function handleException(Exception $exception)
    {
        try
        {
            throw $exception;
        }
        catch (ResourceNotFoundException $ex)
        {
            $this->cakeError('error404', array('message' => $ex->getMessage()));
        }
        catch (Exception $ex)
        {
            $this->cakeError('error500', array('message' => $ex->getMessage()));
        }
    }

Notice that we can rethrow the exception in the handler to avoid using an if or switch construct. Now in our controller method we can write code like this:

         $bagpuss = $this->Bagpuss->read(null, $id);
         if (!$bagpuss)
         {
             throw new ResourceNotFoundException('Could not find bagpuss');
         }

We could even go as far as to override read() in AppModel to throw a ResourceNotFoundException to simplify our controllers further, though I worry this will (a) break existing tools (change of contract), and (b) create too much dependency between model and controller.

Comments or suggestions welcome.

CakePHP: Testing static database tables

Took a while to work out how to do this, hope it helps someone. If you are unit testing models in CakePHP against large database tables that don’t ever change (in my case millions of rows of location data), you don’t really want to bother copying everything to the test database, or worse have the data re-imported before each individual test.

Instead you can set up your fixture like this:

class MyBigStaticTableFixture extends CakeTestFixture {
    var $name = 'MyBigStaticTable';
    var $table = 'my_big_static_tables';
    var $import = array('model' => 'MyBigStaticTable', 'records' => false);
}

// and your model test case as follows:

class MyBigStaticTableTestCase {
    ...
    function startTest() {
        ClassRegistry::config(array('ds' => 'default'));
        $this->MyBigStaticTable => ClassRegistry::init(array(
            'class' => 'MyBigStaticTable', 'ds' => 'default'));
    }
    ...
}

Not currently sure how this will affect related models. This is with CakePHP version 1.2 but I think should also work with 1.3.

Happy testing.