5 /* Needed for pcntl_signal(). */
10 public $send_break = false;
11 public $expect_responses = 1;
15 $conn = new Connection();
17 ////////////////////////////////////////////////////////////////////////////////
19 function starts_with($big, $small) {
20 $slen = strlen($small);
21 return $slen === 0 ||
strncmp($big, $small, $slen) === 0;
24 /* SIGINT handler that sends a dbgp break command. */
25 function handle_sigint($sig) {
26 assert($sig === SIGINT
);
31 if (!is_resource($conn->fd
)) {
35 // If we have a connection going, then send it a break command.
36 $conn->send_break
= true;
39 function format_socket_error($fd, $prefix) {
40 $error = socket_last_error($fd);
41 return $prefix . ": " . socket_strerror($error);
44 /* Starts a client. Returns the socket and port used. */
45 function start_client($port) {
46 $socket = socket_create(AF_INET
, SOCK_STREAM
, 0);
47 @socket_bind
($socket, 'localhost', $port);
48 $result = socket_listen($socket);
50 return array($socket, $port);
53 /* Formats the given dbgp response for output. */
54 function format_response($m) {
55 // Remove # of bytes + null characters.
56 $m = str_replace("\0", "", $m);
57 $m = preg_replace("/^[0-9]+?(?=<)/", "", $m);
59 // Remove strings that could change between runs.
60 $m = preg_replace('/appid="[0-9]+"/', 'appid=""', $m);
61 $m = preg_replace('/engine version=".*?"/', 'engine version=""', $m);
62 $m = preg_replace('/protocol_version=".*?"/', 'protocol_version=""', $m);
63 $m = preg_replace('/ idekey=".*?"/', '', $m);
64 $m = preg_replace('/address="[0-9]+"/', 'address=""', $m);
69 /* Returns true iff the given message is a stream. */
70 function is_stream($msg) {
71 // This is hacky, but it works in all cases and doesn't require parsing xml.
72 $prefix = "<?xml version=\"1.0\" encoding=\"iso-8859-1\"?>\n<stream";
73 return starts_with($msg, $prefix);
76 /* Reads a dbgp message from the socket. */
77 function read_response($socket) {
83 $result = @socket_recv
($socket, $buffer, 1024, 0);
84 if ($result === false) {
85 return format_socket_error($socket, "Client socket error") . "\n";
89 } while ($message !== "" && $message[$bytes - 1] !== "\0");
91 return format_response($message);
94 /* Sends a command to the xdebug server. Exits process on failure. */
95 function send_command($fd, $cmd) {
96 $result = @socket_write
($fd, "$cmd\0");
97 if ($result === false) {
98 $error = format_socket_error($fd, "Client socket error");
104 function parse_options() {
105 $opts = getopt("p::h");
109 if (isset($opts["p"])) {
110 $conn->port
= $opts["p"];
113 if (isset($opts["h"])) {
119 function print_usage() {
121 echo "dbgp-client.php [-pPORT]\n";
125 assert(pcntl_signal(SIGINT
, 'handle_sigint'));
129 echo "-- Simple DBGp Client --\n";
133 // Start the listening socket.
134 list($socket, $port) = start_client($conn->port
);
136 echo "Listening on port $port\n";
138 // Accept a connection.
141 $fd = @socket_accept
($socket);
144 echo "Connected to an XDebug server!\n";
148 socket_close($socket);
153 // Wait for the expected number of responses. Normally we expect 1
154 // response, but with the break command, we expect 2.
156 while ($conn->expect_responses
> 0) {
157 $response = read_response($fd);
159 if (starts_with($response, "Client socket error")) {
163 // Init packet doesn't end in </response>.
164 $conn->expect_responses
-= substr_count($response, "</response>");
165 $conn->expect_responses
-= substr_count($response, "</init>");
166 $responses .= $response;
168 $conn->expect_responses
= 1;
170 // Might have been sent a Ctrl-c while waiting for the response.
171 if ($conn->send_break
) {
172 send_command($fd, "break -i SIGINT\0");
173 $conn->send_break
= false;
175 // We're expecting a response for the break command, and the command
176 // before the break command.
177 $conn->expect_responses
= 2;
181 // Echo back the response to the user if it isn't a stream.
182 if (!is_stream($responses)) {
186 // Received response saying we're stopping.
187 if (strpos($responses, "status=\"stopped\"") > 0) {
188 echo "-- Request ended, stopping --\n";
192 // Get a command from the user and send it.
193 $line = readline("(dbgp) $ ");
200 if (starts_with("quit", $line)) {
201 echo "-- Quitting, request will continue running --\n";
205 send_command($fd, $line);