Zend PHP 5 Certification Study Guide (2014)

Appendix C: phpdbg

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.

Installation

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.

Using phpdbg

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.

·        phpdbg commands must be specified one per line

·        PHP code must use <: 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())

        );

    });

}

:>

Debugging

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.

Emulating the Web Environment

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(),

);

Remote Debugging

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.

Commands

phpdbg has three types of commands:

1.    Informational: Show you information about the source code, the current session, and run-time data.

2.    Execution: Used to set the execution context, start and stop execution, set breakpoints, and watches

3.    Miscellaneous: These commands let you configure phpdbg, execute additional phpdbginit scripts, evaluate code, run shell commands, and quit phpdbg.

List

·        Command: list

·        Alias: l (lower-case L)

·        Type: informational

View source code:

·        list <#> — show # lines of the current file, or from the current breakpoint

·        list [func|f] <functionName> — show the source for the specified function

·        list [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 class

·        list c ClassName — show the source for specified class

Info

·        Command: info

·        Alias: i

·        Type: informational

View information about the current environment:

·        info break|b — Show current breakpoints

·        info files|F — Show included files

·        info classes|c — Show loaded classes

·        info funcs|f — Show loaded classes

·        info error|e — Show last error

·        info vars |v — Show active variables

·        info litera|l — Show active literal constants

·        info memory|m — Show memory manager stats

Print

·        Command: print

·        Alias: p

·        Type: informational

View opcodes and opcode information:

·        print — Show information about the current context

·        print exec|e — Show opcodes for the current execution context (as loaded by exec)

·        opline|o — Show opcodes in the current opline

·        class|c — Show opcodes in the specified class

·        method|m — Show opcodes in the specified method

·        func|f — Show opcodes in the specified function

·        stack|s — Show opcodes in the current stack

Back

·        Command: back

·        Alias: t

·        Type: informational

Show the current backtrace, optionally limited to a number of frames.

·        back — Show the complete backtrace

·        back <#> — Show <#> number of frames from the backtrace

Frame

·        Command: frame

·        Alias: f

·        Type: informational

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

·        Command: help

·        Alias: h

·        Type: informational

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 commands

·        help <command> — Show details help on the specified command

Exec

·        Command: exec

·        Alias: e

·        Type: Execution

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 context

Run

·        Command: run

·        Alias: r

·        Type: Execution

Run the current file in the execution scope, optionally with arguments:

·        run — Run the current file

·        run <arg1> <arg2> — Run the current file and populate $argv[] with the specified arguments

Step

·        Command: step

·        Alias: s

·        Type: Execution

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 again

Continue

·        Command: continue

·        Alias: c

·        Type: Execution

Continue execution after hitting a break or watchpoint.

·        continue — Continue executing until the next break or watchpoint, or the end of the script is reached

Until

·        Command: until

·        Alias: u

·        Type: Execution

Continue execution, skipping any breakpoints on the current line

·        until

Finish

·        Command: finish

·        Alias: F

·        Type: Execution

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 stack

Leave

·        Command: leave

·        Alias: L

·        Type: Execution

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 pause

Breakin cre

·        Command: break

·        Alias: b

·        Type: Execution

Show the current breakpoint, or set a new breakpoint.

·        break — Show the current breakpoint

·        break <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 met

·        break del|d|~ <#> — Remove the given breakpoint (See info break for breakpoint numbers)

It is possible to set break points on:

·        A given file/line: break <file>:<#>

·        A line in the current file: break <#>

·        Entry into a given function: break \namespace\function

·        Entry into a given method: break \namespace\ClassName::methodName

·        An opline address: break 0x7ff68f570e08

·        A given opline within a given function: break \namespace\function#<#>

·        A given opline within a given method: break \namespace\ClassName::methodName#<#>

·        A given opline in a given file: break <file>#<#>

·        When a given condition is met (anywhere): break if <condition>

·        When a given condition is met on any other breakpoint: break at <where> if <condition>

·        When a given opcode (e.g. ZEND_ADD) occurs: break <opcode>

Note: conditional breakpoints add a lot of overhead and can significantly slow down execution. Use with caution.

Watch

·        Command: watch

·        Alias: w

·        Type: Execution

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 watches

·        watch $var — Observe changes to $var

·        watch array|a $var[]|$var-> — Observe when entries are added/removed

·        watch recursive|r $var

Clear

·        Command: clear

·        Alias: C

·        Type: Execution

Clear all breakpoints

·        clear — Clear all breakpoints

Clean

·        Command: clean

·        Alias: X

·        Type: Execution

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 environment

Set

·        Command: set

·        Alias: S

·        Type: Miscellaneous

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 off

·        set 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 breakpoints

·        set quiet|q on|off — Set quiet mode on or off

·        set stepping|s line|opcode — Set whether step should move forward by line, or by opcode

·        set 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

·        Command: source

·        Alias: <

·        Type: Miscellaneous

Execute a .phpdbginit formatted file, allowing you to create multiple files to assist in common tasks.

·        source <file> — Execute <file>

Register

·        Command: register

·        Alias: R

·        Type: Miscellaneous

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 command

Sh

·        Command: sh

·        Alias: None

·        Type: Miscellaneous

Execute a shell command without having to leave phpdbg

·        sh <cmd> — Execute the given command, as if you were in a shell.

Ev

·        Command: ev

·        Alias: None

·        Type: Miscellaneous

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 variable

·        ev <code> — Evaluate the given code.

Quit

·        Command: quit

·        Alias: q

·        Type: Miscellaneous

Quit phpdbg

·        quit — Quit phpdbg