Debugging PHP applications with xdebug

January 7, 2008

Tutorials

By: Stefan Priebsch

Other Articles in the Series

Part One: Introducing xdebug
Part Two: Tracing PHP Applications with xdebug
Part Three: Profiling PHP Applications With xdebug
Part Four: Debugging PHP applications with xdebug
Part Five: Creating Code Coverage Statistics with xdebug

Welcome to the fourth installment in our five-part series of xdebug articles on the Zend DevZone.
This week, we will explore debugging PHP code with xdebug, the swiss army knife for PHP developers.
In this article, we assume that you have xdebug installed on your system. If you haven’t, the first article
of the series explains how to install and configure xdebug.

Debugging software is not exactly a fun job for developers.
The most widely used debugger for PHP still seems to be a
var_dump() statement, possibly in conjunction with die() to
halt program execution at a certain point.
While there is nothing wrong using var_dump() statements in itself, you
still need to change the program code to debug a PHP script. And worse, after you have finished debugging,
you must remove all var_dump() statements again (well you should, at least).
It may well be that a few days later you’ll find yourself adding the very same var_dump()
statements to your code again because you need to go hunting another bug.

Of course, you could just comment out the var_dump() statements, but that looks really ugly in the code.
Another option would be to wrap the var_dump() in conditional clauses, and only execute them when, say, a constant DEBUG
is defined.
This affects performance, because even if the var_dump() statements are not executed,
the conditional clause must be executed. And besides, it looks even uglier in the code.

As we have already learned in the second articles of this series, having xdebug create a trace log might be a better option in this case, because you do not have to change the program code.
But a trace log, even if only created for a part of the application, provides us with a lot of information that may not be necessary
for the debugging process, so using a debugger is a much better solution.
A debugger allows you to pause program execution at any time, inspect or modify
the current variable values, and then continue the program.
By executing a program step by step, you can closely watch the code doing its job, which
hopefully helps you to find out quickly where things go wrong.

Beyond var_dump, debugging in PHP has been problematic for a long time,
at least if you were not willing to spend money on a commercial IDE that supports debugging.
With xdebug, open source debugging of PHP code has – in theory – been
possible for quite a while now. I say theoretically because until recently no good and free
debug client for xdebug was available for both Windows and Unix.

This fall, the problem of having no release-quality cross-platform xdebug client has been solved with the release of Eclipse PDT.
Eclipse PDT is a free IDE for PHP supporting xdebug out of the box.
So, without further ado, let us install Eclipse PDT to get started with debugging.

Installing Eclipse PDT

Eclipse PDT (PDT is short for PHP Development Tools) is written in Java,
and thus works on most platforms where a Java Runtime Environment is present.
If you have no Java Runtime Environment installed on your computer, you can download your copy from
www.sun.com.

You can download a ready-to-go-package of Eclipse PDT from
http://www.eclipse.org/pdt. You should
choose the appropriate version for your platform, though all packages are basically
only compressed archives of the necessary files to run eclipse.
Still, the Windows version comes with an .exe file that starts Eclipse, which
is way more user-friendly than calling the Java classes directly.

As I write this article, the current Release Build version of Eclipse PDT
is R20070917. Choose the latest available version and have some coffee
ready, because the Eclipse PDT download is over 100 MB in size. But since
it is a Java application, having a nice cup of coffee while you wait for the
download to complete should be suitable.
Once the download has completed, unpack the downloaded archive
and you are ready to start Eclipse PDT.

How debugging works

Before we get into the configuration of xdebug and Eclipse PDT, let us have a look
at how PHP debugging with xdebug actually works. This will help you better understand
the configuration described below.

When debugging is enabled in php.ini, xdebug controls the program
execution in PHP, which basically means that xdebug can pause and resume program execution
at any time. When program execution is paused, xdebug can retrieve information about
the current program state, like reading variable values. It is even possible for xdebug to
change the value of a variable, then continue the script execution with a modified value.

The xdebug extension is a server, expecting client connections at a certain configurable port.
There are two protocols that can be used to communicate between the xdebug client and
the xdebug server, GDB and DBGp. GDB is an older protocol, which has been superceded by
DBGp. By sending commands to the xdebug server, the xdebug client acts as a sort of
remote control for PHP, telling PHP to pause execution, execute one step, or to continue the program execution.
The client is usually embedded into an editor or the IDE (in our
case, into Eclipse PDT), so you will not have to deal with the debug protocol itself.

The PHP server with xdebug can run on a another system than the one running the xdebug client.
That is why xdebug is called a remote debugger. For simplicity, we will set up the debugging server and client
on the same computer.

There are two different modes of starting a debug session with xdebug. They are controlled by the
php.ini setting xdebug.remote_mode. The default setting is req, which
makes xdebug always connect to the debug client when a script is started. If you want xdebug
to only connect to the debug client on a breakpoint or an error in the script, you can set
xdebug.remote_mode to jit. I would recommend keeping the default setting,
which you can achieve by putting no xdebug.remote_mode setting into php.ini.

To actually start a debug session, you must pass a parameter XDEBUG_SESSION_START
to the script by GET, POST, or cookie. The value of this parameter is the debug session name, which should
be unique at a given point in time, so that xdebug can distinguish different debug sessions running concurrently.
To end a debug session, you need to pass XDEBUG_SESSION_STOP to the script.

Instead of manually dealing with the starting and stopping of debug sessions, you can install
a firefox plugin
that allows you to conveniently start and stop a debug session with a mouse click.

Using Eclipse PDT, you will not even have to worry about the browser plugin, as the IDE
takes care of passing the appropriate parameters to the browser.
xdebug also requires setting of a IDE key, which you also do not have to worry about, because
Eclipse sets it for you.

Configuring xdebug

Now let us configure xdebug debugging. Add the following settings to php.ini:

xdebug.remote_enable=On
xdebug.remote_host="localhost"
xdebug.remote_port=9000
xdebug.remote_handler="dbgp"

Make sure you add these lines after the zend_extension_ts line that
loads the xdebug extension. The first entry enables the debugger. The
second entry defines that the debug client runs on localhost – this could
be any valid DNS name or IP address, if the client and the server were not on the same computer.

The third setting defines the port the xdebug server expects the client to listen on (which, strictly speaking,
makes the client a server, but let us not get confused about this).
By default, the port is 9000.
This port is configured in Eclipse by default, and if there is no compelling reason to do otherwise,
you should keep the default port. If you want to change the port, keep in mind that you have to configure the
same port number in Eclipse and php.ini.

Also, make sure that no firewall gets into your way. When you start Eclipse, you might see
a warning that Java is trying to set up a server, bind to a port, access the network, or perform some obscure
potentially dangerous action. Of course, it’s not dangerous, it’s just the xdebug client trying to listen at port 9000.
If you have problems with debugging, check if there are any firewalls between the debug server
and the debug client that might block port 9000.

Last but not least, we have to tell xdebug which protocol the client speaks. Eclipse PDT speaks DBGp, so you may
not change this setting.

Configuring Eclipse PDT

To configure Eclipse PDT, start Eclipse by double-clicking the Eclipse executable.
Create a new PHP project. Let us name the project debug_test.
Now, create a file debug.php in the project, add some code, then save the file.

Now let us configure Eclipse for debugging with xdebug. First of all,
we will configure Eclipse to launch projects in an external browser instead of its internal
browser. When an external browser is configured, all debugging sessions will be launched
in an external browser as well.
Using an external browser is not absolutely necessary, but I prefer to work with Firefox instead of
Eclipse’s internal browser.
Choose Window from the menu bar, then select Preferences (see the following screenshot).
Expand the General subtree,
and click Web Browser. Now, select the radio button Use external browser
and click Apply.

Eclipse PDT supports the Zend debugger and xdebug. By default, the Zend debugger
is activated. To change this setting to xdebug, expand the PHP subtree
and the Debug subtree of PHP. Then, change
PHP debugger to Xdebug and click Apply.

Now, choose Run from the menu bar and click the entry Open Debug Dialog.
Then, double click PHP Web Page to create a new debug configuration.

You will see a window with three tabs, Server, Advanced, and Common.
When not already selected, choose Xdebug as Server Debugger.
In the File / Project text field, you must enter the path to the script you want to debug.
This path must be relative to your workspace. On my system, this is /debug_test/debug.php.
Click Browse and select the file debug.php in the debug_test directory.

Eclipse needs to know the URL matching the script filename and path you just entered. This is
required to highlight the currently executed line in the source code.
The URL text field shows the URL that is mapped to the filename.
By default, the URL text field deactivated because the Auto Generate checkbox is activated.
If the displayed URL does not match the URL you would type into your browser to see the script you have specified
in File / Project, uncheck Auto Generate and enter the correct URL in the URL text field.
If thg script to debug requires any GET parameters, you can append them to the URL given here.

Do not forget to click Apply to save the changes.
The following screenshot shows how the debug configuration looks like on my system:

Change to the Advanced tab and make sure that Open in Browser and the radio button
Debug All Pages both are checked.
Now you can close the configuration window and start a debugging session.

Debugging a PHP script

To start debugging a PHP script, choose Run from the menu bar and select
Debug. You can shortcut this by pressing the F11 key. Eclipse will ask you wether you want to open
the debug view. You should tick the checkbox to remember this setting, otherwise you will be asked the same question
every time you start debugging.

The following screenshot shows Eclipse’s debug view of my debug.php script (which contains some pointless code):

Eclipse has opened your system’s default browser (or whatever browser you have configured it to open).
You will not see output in the browser window yet, because Eclipse
by default pauses script execution on the first line of the script, as if a breakpoint were set on this line.
If you want to disable this behaviour, uncheck
the Break at First Line checkbox in the Breakpoint section from the debug dialog configuration window.

As the screenshot shows, you can see the source code of the debugged file, with the currently executed line marked with
an arroq to the left of the line number.
In the above right area, you can choose between different tabs.
The Variables tab shows the current values
of all variables in the current scope. The superglobal variables are valid in every scope, so they are always displayed.
The Breakpoints tab lets you view and edit all breakpoints in your script. Eclipse will remember any breakpoints
you have set on the code, even if you close and restart Eclipse.

You can now continue program execution until the next breakpoint is reached, execute just one step, or step into
the next function, or out of the next function scope by clicking the appropriate icons in the Debug
top left area of the window.
Stepping is useful when you have located the problem area
in your code and want to watch closely what is happening. You will see the variable values change with each step.

Changing Variables at Runtime

You can even change variable values during script runtime. While this does not really fix a bug, it
might be useful to provoke certain errors without modifying the source code.
To change a variable, click the current value, modify it and press Enter.

Breakpoints

A breakpoint pauses script execution and allows you to inspect variable values, then continue the program.
The program execution is also paused if an exception occurs in your PHP code.
To set a breakpoint, right-click a line number in the source code, then choose Toggle Breakpoints from
the context menu. You can remove breakpoints in the same way, or remove them in the Breakpoints tab
by right-clicking a breakpoint and selecting Remove from the context menu.

You can even add a condition to a breakpoint. Conditional breakpoints will only pause the program execution when the
given condition is met. This is can be very useful when the same piece of code is executed multiple times with
different parametrization.
To add a condition to a breakpoint, right-click the breakpoint icon to the left of the line number in
the source code view. Choose Breakpoint Properties.
You can remove conditions in the same way or by right-clicking a breakpoint and
selecting Set Condition from the context menu in the Breakpoints tab.

Check Enable Set Condition and enter a condition in PHP code into the text field. In my debug.php,
the function test() is called on line 11 and a breakpoint is set on this line. By adding the condition
$a != '' xdebug will only pause program execution on this line when the local variable $a
is non-empty at the time the breakpoint is reached.

To end a debugging session, highlight Remote Launch in the top left pane, then click the Terminate icon which is
located between the Run and the various Step icons. If you had Eclipse run the script in an external
browser, you need to close the browser window manually.

Conclusion

Remote debugging is a great interactive and non-intrusive way of finding bugs in your PHP scripts. Instead of putting var_dump()
statements into your code, or working your way through a long trace log including parameter and return values, debugging
gives you a macrosopic view of critical areas in your code.

Next week, we will explore creating code coverage statistics with xdebug. To aggregate the statistics
and create a nicely formatted HTML report of the code coverage in our projects, we will also use phing and PHPUnit.
So, make sure you check back here next week for the fifth and final part in this series of xdebug articles.
Until then: Happy debugging – with xdebug, of course.

About the author:

Stefan Priebsch has been solving IT problems for over 25 years.
He is founder and CEO of e-novative GmbH, one of the first German IT
consultancies offering PHP-based solutions.
Stefan holds a university degree in computer science,
is an internationally recognized PHP expert, book author, trainer and consultant.
You can reach him at stefan.priebsch@e-novative.de.

About Cal Evans

Many moons ago, at the tender age of 14, Cal touched his first computer. (We're using the term "computer" loosely here, it was a TRS-80 Model 1) Since then his life has never been the same. He graduated from TRS-80s to Commodores and eventually to IBM PC's. For the past 10 years Cal has worked with PHP and MySQL on Linux OSX, and when necessary, Windows. He has built on a variety of projects ranging in size from simple web pages to multi-million dollar web applications. When not banging his head on his monitor, attempting a blood sacrifice to get a particular piece of code working, he enjoys building and managing development teams using his widely imitated but never patented management style of "management by wandering around". Cal is currently based in Nashville, TN and is gainfully unemployed as the Chief Marketing Officer of Blue Parabola, LLC. Cal is happily married to wife 1.28, the lovely and talented Kathy. Together they have 2 kids who were both bright enough not to pursue a career in IT. Cal blogs at http://blog.calevans.com and is the founder and host of Day Camp 4 Developers

View all posts by Cal Evans

16 Responses to “Debugging PHP applications with xdebug”

  1. binarios Says:

    Hi. I’m trying to install the Eclipse/PDT with XDebug.
    I’ve got it working so that I can see that XDebug is loaded (for example,
    in phpinfo()), but when I try to create a new Debug Launch Configuration,
    and click on "New_Configuration" under "PHP Web Script with XDebug", I get
    the following error: "An error has occurred. See error log for more
    details.
    org.eclipse.php.internal.core.util.FileUtils.fileExists(Ljava/lang/String;)Z
    ".
    Here is the first part of the eclipse error log:

    !MESSAGE Problems occurred when invoking code from plug-in:
    "org.eclipse.jface".
    !STACK 0
    java.lang.NoSuchMethodError:
    org.eclipse.php.internal.core.util.FileUtils.fileExists(Ljava/lang/String;)Z
    at
    org.eclipse.php.xdebug.ui.launching.XDebugPHPServerTab.isValid(XDebugPHPServerTab.java:508)
    at
    org.eclipse.php.xdebug.ui.launching.XDebugPHPServerTab.initializeFrom(XDebugPHPServerTab.java:406)
    at
    org.eclipse.debug.ui.AbstractLaunchConfigurationTabGroup.initializeFrom(AbstractLaunchConfigurationTabGroup.java:86)
    at
    org.eclipse.debug.internal.ui.launchConfigurations.LaunchConfigurationTabGroupWrapper.initializeFrom(LaunchConfigurationTabGroupWrapper.java:194)
    at
    org.eclipse.debug.internal.ui.launchConfigurations.LaunchConfigurationTabGroupViewer.displayInstanceTabs(LaunchConfigurationTabGroupViewer.java:751)
    at
    org.eclipse.debug.internal.ui.launchConfigurations.LaunchConfigurationTabGroupViewer$8.run(LaunchConfigurationTabGroupViewer.java:623)

  2. awongh Says:

    I found this on google, so maybe some others will fnd it helpful:

    I couldn’t find the firefox add-on that was linked to in the article, so I built my own. You can get it here:

    https://addons.mozilla.org/en-US/firefox/addon/181743/

  3. eburner Says:

    A year have passed with no reply to sethsyberg! May be I missed something, but I can’t solve the problem. So, how to initiate xdebug session with Zend Studio from browser? Just setting cookie XDEBUG_SESSION(_START)=ECLIPSE_DBGP has no effect. Btw, xdc (standalone xdebug win-client) responds right to such query by launching a debug session.

  4. francescospegni Says:

    hi peter,

    thanks for your very interesting series of articles about xdebug. i just would remark two things:

    1. the xdebug.remote_host parameter should "point" to the host that actually runs the client (the textual one or the Eclipse PDT machine that wants to debug the code)

    2. that xdebug.remote_host can contain only one IP address, not allowing several developers from different workstations to debug programs hosted on the same apache + PHP + xdebug server (at least using this basic settings, i don’t know using proxies…)

    i think this would help the reader (which is not so familiar with xdebug, as i’m not familiar with that tool) to better understand.

    i say you this because following your example i was continuously trying to set up xdebug on a remote machine but the setting xdebug.remote_host=localhost prevented me from debugging it from my laptop… i know i was trying to do a stupid thing ;) but at the beginning it was not clear to me the relation between the parameter and the location of the client

    hope this could help. thanks again :)

    francesco

  5. dmakovec Says:

    distreff – see dtaylor7′s post above yours for steps on how to get XDebug enabled again. Having a Mac, I started with the steps I’d worked out for PDT (http://www.makovec.net/software/39-software/55-eclipse-pdt-with-debug-on-leopard.html), then did the following from Terminal.app:

    cd /Applications/Zend/Zend\ Studio\ for\ Eclipse\ -\ 6.0.1/plugins
    mkdir disabled
    mv com.zend.php.debug.* disabled
    /Applications/Zend/Zend\ Studio\ for\ Eclipse\ -\ 6.0.1/ZendStudio.app/Contents/MacOS/ZendStudio -clean

    After you’ve done all that from the command line, your XDebug options will be back. You can then quit out and go back to loading up ZSE from the desktop.

  6. distreff Says:

    When giving up trying to use Zend debugger or Zend Core/Platform and wanted to try xdebug, I find suddenly that some of the latest updates from Zend did remove xdebug from the Preferences list in php -> Debug -> Installed Debugger.

    Is there a way to get this back again, or is the installation of Zend Studio in original Eclipse not compatible anymor ??

  7. dtaylor7 Says:

    I think Zend hates xdebug… But the ZSE contains a full PDT (it has xdebug support). So, remove (move) the plugins/com.zend.php.debug*, then start the studion with "ZendStudio -clean"

    Hmm, there will be xdebug support in the PHP Debugger select… I tested it, works.

    :D

  8. dmondark Says:

    Thanks Stefan for the great series and Cal for posting it :)
    Yossi, any news about the knowledge base article regarding ZSE support for xdebug?

  9. sethsyberg Says:

    Debugging scripts like this seems to only be so helpful… It seems 80% or so of my debugging requires some interaction with a web page (i.e. a http POST). Maybe I’m missing something, but is there a way to do this with Xdebug? I’m basically looking for the same functionality I’m used to with Zend Studio’s browser plug in, where I can specify debug "Next Page" or "All POSTS". Again, this pretty much makes up all of my debugging, it’s very rare that I’ll have need to debug a script without a post.

  10. opzend Says:

    I have a problem undestanding debugging with xDebug (using it with PDt). Here is my problem:
    xdebug adds an id at the end of the URLs to identify the current file handled. That way it is possible to trace, etc.
    So good so fine. But if the php file includes a link to another php file, then when you click on the link, the id is not added and you are not able to continue the tracing on that linked file… And this prevents basically all debugging of existing CMS, modules of CMS, etc.
    Am i missing something ? how can I trace linked php code?
    Thanks for your answer.

  11. lkjairath Says:

    What is the best practice using a debugger with PDT. Thanks in advance.

  12. janburda Says:

    Thank You very much for your article. I’ve started with Xdebug and Eclipse, everythink go well, script is started in external browser with SESSION name in GET but it doesn’t stop at breakpoints and doesn’t show variables. I don’t know why. Where can be fault? What is critically important settings for remote debugging with Xdebug and Eclipse? Thank You very much. Jan

  13. e-novative Says:

    I have used pdt-all-in-one-1.0-R20070917, which works fine for me. I haven’t tried out 1.0.2 yet, so I can’t tell wether there is a problem with xdebug support, but you might want to give 1.0-R20070917 a try for the time being.

  14. fmpwizard Says:

    Using the complete PDT download version 1.0.2 Stable Build: S20071213-M1 does not have the option to select xdebug as as debugger.
    I have installed the xdebug extension for php and it shows on on phpinfo() (installed using pecl install xdebug)

  15. yossi_zend1 Says:

    We are going to provide a knowledge base article in the support section on how to enable XDebug support in Zend Studio for Eclipse. Keep in mind that XDebug support is for debugging purposes only (and not profiling or code coverage).

    Stay tuned.

    Yossi Leon
    Product Manager, Zend Development Tools

  16. antych Says:

    I read that Zend will provide howto on xdebug support for ZSE, but I’d love to try it now. Could you provide instructions on adding and configuring Xdebug client in ZSE Beta2? Cheers.