PHP 5.6 introduces phpdbg, which is a new SAPI—Server API—for debugging PHP code.
Unlike existing debugging solutions, such as xdebug, and Zend Debugger, which are extensions and run within your standard SAPI—be that a web server like Nginx, or Apache, or on the CLI, phpdbg is completely standalone as it’s own fully-contained SAPI.
phpdbg is similar to the traditional C-debugger, gdb
with support for step-through debugging, code disassembly, and remote debugging.
To install phpdbg, simply specify --enable-phpdbg
at compile time. To further enhance phpdbg, you should also specify --with-readline[=DIR]
.
For Mac OS X, which ships with the alternative
libedit
, you can install readline with homebrew (see: http://brew.sh) and configure with--with-readline=/usr/local/Cellar/readline/<version>
—making sure that you replace<version>
with your installed version.
Once installed, you will find the phpdbg
binary alongside your regular CLI php
binary.
The simplest way to use phpdbg, is simple to run it from the terminal, phpdbg
.
Doing so will bring you to the phpdbg prompt, that will look something like this:
$ phpdbg
[Welcome to phpdbg, the interactive PHP debugger, v0.4.0]
To get help using phpdbg type "help" and press enter
[Please report bugs to <http://github.com/krakjoe/phpdbg/issues>]
phpdbg>
At the prompt, you can then load your script using the exec
or e
command:
phpdbg> exec <script>
Alternatively, you can simply specify the script on the command line:
$ phpdbg <script>
Either way, you are ready to debug.
To further enhance your session, you can specify a .phpdbginit
file, or use the default one that ships with PHP (look in sapi/phpdbg/
). This file will automatically run if it’s found in the CWD (current working directory), or you can specify it on the command line using the -i
flag.
This file can contain both phpdbg commands (e.g. exec <script>
) or embedded PHP code.
<:
and :>
instead of standard PHP tags (<?php
and ?>
)You can use this file to automatically create a debugging environment for your application, that you can commit to version control and others can easily use.
For example, you might choose to place it in the root of your project, and have it pull in a bootstrap file, and then execute your index file.
The default .phpdbginit
also includes the following to allow auto-completion (on <tab>
):
<:
if (function_exists('readline_completion_function')) {
readline_completion_function(function(){
return array_merge(
get_defined_functions()['user'],
array_keys(get_defined_constants())
);
});
}
:>
Once you have phpdbg running, you can run your script by calling run
; but doing this immediately will simply run the script—this is only useful if you want to see PHP display it’s regular errors.
To pause execution at any point, we use the break
command, so if we want to break on the first line of our script, we can simply use break 1
. However, it should be noted that you cannot break on PHP tags—<?php
, <?
or ?>
—though you can on echo tags (<?=
), or on blank lines.
Additionally, you can set a watch, which is a breakpoint that observes changes to a given variable, using the watch $var
command for any variable currently in scope.
Now if you call run
it will execute until it reaches your breakpoint and then pause. At this point you can start to inspect and walk through your code.
You can use ev <code>
to run any code in the current scope (effectively making changes at runtime), or you can use ev $var
to show the contents of a variable in the current scope.
If you want to step through your code, use step
, which will step through one line at a time.
To go from breakpoint to breakpoint, you can use continue
, which will continue execution until the next breakpoint, watch, or the end of the script is reached.
You can use list #
to show the given number of lines from the current breakpoint for context.
phpdbg ships with a bootstrap file example for setting up the debug session to emulate a webserver environment. The file is PHP code that will run in the context of the debugging session.
You can use your .phpdbginit
to automatically setup your bootstrap:
ev include("web-bootstrap.php");
exec <file>
Where the web-bootstrap.php
looks something like this:
<?php
if (!defined('PHPDBG_BOOTSTRAPPED')) {
// Define the Document Root
define("PHPDBG_BOOTPATH", "/path/to/project/public");
// Set to the file you're going to debug
define("PHPDBG_BOOTSTRAP", "index.php");
define(
"PHPDBG_BOOTSTRAPPED",
sprintf("/%s", PHPDBG_BOOTSTRAP)
);
}
// Switch to the Document Root
chdir(PHPDBG_BOOTPATH);
// Define GPCS, REQUEST and FILES super-globals
$_GET = array();
$_POST = array();
$_COOKIE = array();
$_REQUEST = array();
$_FILES = array();
// Define a reasonable $_SERVER
$_SERVER = array
(
'HTTP_HOST' => 'localhost',
'HTTP_CONNECTION' => 'keep-alive',
'HTTP_ACCEPT' => 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'HTTP_USER_AGENT' => 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1547.65 Safari/537.36',
'HTTP_ACCEPT_ENCODING' => 'gzip,deflate,sdch',
'HTTP_ACCEPT_LANGUAGE' => 'en-US,en;q=0.8',
'HTTP_COOKIE' => 'tz=Europe%2FLondon; __utma=1.347100075.1384196523.1384196523.1384196523.1; __utmc=1; __utmz=1.1384196523.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none)',
'PATH' => '/usr/local/bin:/usr/bin:/bin',
'SERVER_SIGNATURE' => '<address>Apache/2.4.6 (Ubuntu) Server at phpdbg.com Port 80</address>',
'SERVER_SOFTWARE' => 'Apache/2.4.6 (Ubuntu)',
'SERVER_NAME' => 'localhost',
'SERVER_ADDR' => '127.0.0.1',
'SERVER_PORT' => '80',
'REMOTE_ADDR' => '127.0.0.1',
'DOCUMENT_ROOT' => PHPDBG_BOOTPATH,
'REQUEST_SCHEME' => 'http',
'CONTEXT_PREFIX' => '',
'CONTEXT_DOCUMENT_ROOT' => PHPDBG_BOOTPATH,
'SERVER_ADMIN' => '[no address given]',
'SCRIPT_FILENAME' => sprintf(
'%s/%s', PHPDBG_BOOTPATH, PHPDBG_BOOTSTRAP
),
'REMOTE_PORT' => '47931',
'GATEWAY_INTERFACE' => 'CGI/1.1',
'SERVER_PROTOCOL' => 'HTTP/1.1',
'REQUEST_METHOD' => 'GET',
'QUERY_STRING' => '',
'REQUEST_URI' => PHPDBG_BOOTSTRAPPED,
'SCRIPT_NAME' => PHPDBG_BOOTSTRAPPED,
'PHP_SELF' => PHPDBG_BOOTSTRAPPED,
'REQUEST_TIME' => time(),
);
phpdbg also allows you to do remote debugging, with a Java based client that can connect an allow you to run commands on the remote machine. You can grab the client from http://phpdbg.org.
To start remote debugging, use the -l
(lower-case L) flag. Remote debugging uses two ports, one for input, and a second for output. By default, if you specify one port, it will use that for input, and double it for output.
$ phpdbg -l4000
This will start the remote debugger, with input on port 4000, and output on port 8000. You can explicitly set the ports using:
$ phpdbg -l4000/8000
Next, start the client by using:
$ java -jar /path/to/phpdbg-ui.jar
Note: in Mac OS X, you should simply click to open the file in Finder, trying to start it from the command line will not work.
Once the GUI loads, you can set the remote host, and input/output ports at the bottom, then click the “Connect” button.
You can then simply write commands in the Command input, and hit Enter to run them. The debugger works identically to as if you were on the remote machine.
phpdbg has three types of commands:
phpdbginit
scripts, evaluate code, run shell commands, and quit phpdbg.list
l
(lower-case L)View source code:
list <#>
— show # lines of the current file, or from the current breakpointlist [func|f] <functionName>
— show the source for the specified functionlist [func|f] .<methodName>
— show the source of the specified method in the current class scope (during execution)list [m] <ClassName::methodName>
— show the source of the method in the specified classlist c ClassName
— show the source for specified classinfo
i
View information about the current environment:
info break|b
— Show current breakpointsinfo files|F
— Show included filesinfo classes|c
— Show loaded classesinfo funcs|f
— Show loaded classesinfo error|e
— Show last errorinfo vars |v
— Show active variablesinfo litera|l
— Show active literal constantsinfo memory|m
— Show memory manager statsprint
p
View opcodes and opcode information:
print
— Show information about the current contextprint exec|e
— Show opcodes for the current execution context (as loaded by exec
)opline|o
— Show opcodes in the current oplineclass|c
— Show opcodes in the specified classmethod|m
— Show opcodes in the specified methodfunc|f
— Show opcodes in the specified functionstack|s
— Show opcodes in the current stackback
t
Show the current backtrace, optionally limited to a number of frames.
back
— Show the complete backtraceback <#>
— Show <#>
number of frames from the backtraceframe
f
Switch to a different frame (an entry in the stack) so that you can run other commands in that context (e.g. if you are current within a method, you can change to the calling method and inspect it’s variables). You can get information on the current stack using the back
command.
frame <#>
— Switch to frame with number <#>
. The currently executing frame is always 0
with it’s called being 1
and so-on up the stack.help
h
Help is the command you will likely use the most, showing complete documentation on each and every command.
help
— Show the help overview, including a list of commandshelp <command>
— Show details help on the specified commandexec
e
Set the execution context, which is a fancy way of saying: the file to debug. This can be specifed using this command, or on the command line using -e
or as the last anonymous argument.
exec <file>
— Set the given file as the execution contextrun
r
Run the current file in the execution scope, optionally with arguments:
run
— Run the current filerun <arg1> <arg2>
— Run the current file and populate $argv[]
with the specified argumentsstep
s
Continue execution when the debugger is paused, until the next line or opcode is reached and then break again (even if no breakpoint exists). This is configurable by set stepping line
or set stepping opcode
.
step
— Continue execution until the next line is reached and break againcontinue
c
Continue execution after hitting a break or watchpoint.
continue
— Continue executing until the next break or watchpoint, or the end of the script is reacheduntil
u
Continue execution, skipping any breakpoints on the current line
until
finish
F
Continue execution, skipping any breakpoints in the current stack, only breaking at the next breakpoint in subsequent stacks.
finish
— Skip to the breakpoint not in the current stackleave
L
Similar to finish
, except that a temporary breakpoint is insert at the end of the current stack and execution will continue until that point.
leave
— Skip to the end of the current stack and pausebreak
b
Show the current breakpoint, or set a new breakpoint.
break
— Show the current breakpointbreak <on>
— Set a new breakpoint on something (see below)break at|a|@ <on> if <condition>
— Set a new breakpoint on something, if a given condition is metbreak del|d|~ <#>
— Remove the given breakpoint (See info break
for breakpoint numbers)It is possible to set break points on:
break <file>:<#>
break <#>
break
amespacefunction
break
amespaceClassName::methodName
break 0x7ff68f570e08
break
amespacefunction#<#>
break
amespaceClassName::methodName#<#>
break <file>#<#>
break if <condition>
break at <where> if <condition>
ZEND_ADD
) occurs: break <opcode>
Note: conditional breakpoints add a lot of overhead and can significantly slow down execution. Use with caution.
watch
w
Allows you to track when a variable is modified, pausing execution so you can inspect the variable using ev $var
. The variable must already be defined (in the current scope) before you can set a watch on it.
watch
— List all current watcheswatch $var
— Observe changes to $var
watch array|a $var[]|$var->
— Observe when entries are added/removedwatch recursive|r $var
clear
C
Clear all breakpoints
clear
— Clear all breakpointsclean
X
Clean the execution environment, effectively un-registering all classes, functions, and variables, so that you can re-run the script from scratch.
clean
— Clean the environmentset
S
Configure how phpdbg looks and behaves.
set prompt|p <string>
— Set the prompt to <string>
set color|c <element> <color>
— Set <element>
, one of “prompt”, “notice”, or “error”, to <color>
set colors|C on|off
— Set display of colors on, or offset oplog|O <file>
— Log the executed opcodes to <file>
set break|b # on|off
— Temporarily disable, or re-enable breakpoint the given breakpoint (See info break
for breakpoint numbers)set breaks|B
— Temporarily disable, or re-enable all breakpointsset quiet|q on|off
— Set quiet mode on or offset stepping|s line|opcode
— Set whether step
should move forward by line, or by opcodeset refcount|r on|off
— Enable or disable refcount display when hitting watchpoints.Valid colors for set color
are none, white, red, green, yellow, blue, purple, cyan and black. All colours except none can be followed by an optional -bold or -underline qualifier.
source
<
Execute a .phpdbginit
formatted file, allowing you to create multiple files to assist in common tasks.
source <file>
— Execute <file>
register
R
Register a function as a top-level phpdbg command. You can then run the function quickly and easily in the current scope. Functions are then called like: functionName arg1 arg2
.
register <function>
— Register a function as a top-level phdbg commandsh
Execute a shell command without having to leave phpdbg
sh <cmd>
— Execute the given command, as if you were in a shell.ev
Execute PHP code in the current scope, and show the result, or print_r()
a variable. ev
is possible the most powerful command in phpdbg, as it allows you to actively modify the code, changing variables, running functions, etc. For example, if you wanted to see how your script coped with the loss of a file handle, or database connection, you could close it, and watch what happens.
ev $var
— print_r()
the given variableev <code>
— Evaluate the given code.quit
q
Quit phpdbg
quit
— Quit phpdbg52.15.57.3