Debugging Zend Framework in Zend Studio

Yesterday I managed to discover a minor bug in the Zend_Controller_Router_Route class in the Zend Framework. In one of my view scripts I used the url helper to display the href part of a link doing something like this:

<a href="<?= $this->url(array(), 'newsCommentsRss') ?>">rss</a>

The newsCommentsRss route is an instance of the Zend_Controller_Router_Route class and is created like this:

$routes['newsCommentsRss'] = new Zend_Controller_Router_Route('/news/:id/comments/rss',
    array('module' => 'feed', 'controller' => 'rss', 'action' => 'newsComments', 'id' => null),
    array('id' => '\d+'));

The route consist of some static words along with a variable called :id that is supposed to be a number (see the regexp in the last argument to the constructor). It’s initially set to null, so if I somehow forget to set it using the Zend_View’s url helper, Zend Framework will promptly tell me to do stuff the right way! As you can see above I actually forgot to set the id when using the url helper (it’s supposed to be set in the first argument to the helper, but I specified an empty array). It was pure luck that I noticed it, but the weird part is that the :id in the route was set to the correct value even if I didn’t specify it. What gives?! Since I like my things to be logical I had to find out what was going on here.

I use Zend Studio these days and downloaded and installed the standalone Zend Debugger so I could do some debugging of ZF. At first I thought the bug was in the url helper so I added a break point at the line where I output the route and started the debugger. To set a break point in Zend Studio you just have to click on the line you want to break point to be set.

Setting a breakpoint in Zend Studio

When starting the debugger it stops at the first line asking you what to do next. Since we only want to see what happens at the break point I just hit run. The line with the break point itself is not too interesting. The interesting part is what happens inside the url helper class. When the debugger stops at the break point I “stepped inside” to see what was going on.

I kept on stepping until I reached the url method of the url helper. At some point in that method it fetches the route using the getRoute method of the router instance:

$route = $router->getRoute($name);

I stepped on until that line had been executed. When inspecting the $route variable I saw that the :id part of route had already been set.

The bug had to be somewhere else then!

Since I use ZF’s MVC components I also use the front dispatcher that matches the current request to all the routes to see which module/controller/action it is supposed to execute. I added a breakpoint in the code where the dispatcher kicks in to see what happened.

When stepping through the front controller I found the point where it tries to route the current request:

$router->route($this->_request);

I stepped into that call and found the piece of code that iterates through all routes to see if anyone matches:

foreach (array_reverse($this->_routes) as $name => $route) {
    if ($params = $route->match($pathInfo)) {
        $this->_setRequestParams($request, $params);
        $this->_currentRoute = $name;
        break;
    }
}

I stepped through some iterations until it tried to match the newsControllerRss route. I inspected the route object and saw that the values has not been set yet. I entered the match method of the Zend_Controller_Router_Route and started stepping through.

Now we are operating on the newsCommentsRss instance itself so I added a watch on $this and switched to the watches view in Zend Studio since I’m only interested in the route object.

The match method splits up the request it’s looking for into several parts. The request at this time was “news/11/Title” and is split up into an array with three elements. Some of you might see that the rss route (news/:id/comments/rss) is very similar to the current request. The difference is the static /rss at the end.

The match method loops through the parts of the request and doing matches against each part of the route. The first part of the request and the route is the same. Next iteration!

The next part of the request is “11” and the next part of the route is :id which must be a number. Since 11 is in fact a number this part also matches, but, since we have a variable in the route, the value is stored in the object itself:

if ($name !== null) {
    // It's a variable. Setting a value
    $this->_values[$name] = $pathPart;
    $unique[$name] = true;
} else {
    $pathStaticCount++;
}

At this moment $this had been changed (see the next two images).

Before second iteration:

After second iteration:

The third iteration will try to match the “Title” part from the request against the static “comments” part of the route. Since these two don’t match the match method returns false to let the outer loop have a go at the next route in the array. But hold on … what about the value that was set to 11? It’s not reset! And there you go! Bug found. :)

Some of you might notice that it’s not that easy to actually be affected by this bug. You need two similar routes like the two I have:

  1. /news/:id/:title
  2. /news/:id/comments/rss

and they have to appear in that order in the array of routes (the dispatcher searches routes in reverse order). You also have to actually have a request that matches the first one and on that same page output the second one. You might say that it’s stupid to have routes like this, but hey, everybody is allowed to be stupid once in a while!

When an error happens in something as big as the Zend Framework it can take quite some time to find out whats going on without a debugger. I would probably be still trying to figure out what was going on without the debug feature in Zend Studio. Since I’m such a nice guy (yeah right) I created an issue in ZF’s issue tracker. Just like all nice guys should! :)

EDIT: I just saw that the bug had been fixed (the last comment in the issue).

Advertisements
This entry was posted in PHP, Technology and tagged , , , . Bookmark the permalink.

4 Responses to Debugging Zend Framework in Zend Studio

  1. Pingback: Christer Goes Bug Hunting | Mats Lindh

  2. Toppy says:

    Thanks for the article Christer. I was able to save some myself some time and sanity with a google search. Found out it wasn’t my code that was problematic.

    I was having the same problem last night when I was building urls in succession with $this->view->url(array(‘controller’ => controller, ‘action’ => action, ‘id’ => id), null); and then a $this->view->url(array(‘controller’ => controller), null);

    Instead of just having a URL with just controller assigned the second call would add in an action and id.

    I did an svn update and still have the same problem. I need to explicitly assign null to action and id in the second call. Have you verified with your code that the fix worked?

  3. christer says:

    @Toppy: The fix solved my problem at least. What you are trying to do is somewhat different. I think my patch (with some minor modifications) made it to the 1.5.2 release.

  4. Okay, it looks like the normal Debugging of Zend Studio, so not making use of any features of Zend Framework debugging.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s