Move serialize functions to variable-serializer.cpp
[hiphop-php.git] / hphp / tools / dbgp-client.php
blobf1d0dc9777964fb5dcacb5fbc666a362b5834117
1 #!/usr/local/bin/php
3 <?php
5 /* Needed for pcntl_signal(). */
6 declare(ticks=1);
8 class Connection {
9 public $fd = null;
10 public $send_break = false;
11 public $expect_responses = 1;
12 public $port = 9000;
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);
28 global $conn;
30 // No connection yet.
31 if (!is_resource($conn->fd)) {
32 exit(1);
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);
49 assert($result);
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);
66 return $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) {
78 $bytes = 0;
79 $message = "";
81 do {
82 $buffer = "";
83 $result = @socket_recv($socket, $buffer, 1024, 0);
84 if ($result === false) {
85 return format_socket_error($socket, "Client socket error") . "\n";
87 $bytes += $result;
88 $message .= $buffer;
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");
99 echo "$error\n";
100 exit(1);
104 function parse_options() {
105 $opts = getopt("p::h");
107 global $conn;
109 if (isset($opts["p"])) {
110 $conn->port = $opts["p"];
113 if (isset($opts["h"])) {
114 print_usage();
115 exit(0);
119 function print_usage() {
120 echo "Usage:\n";
121 echo "dbgp-client.php [-pPORT]\n";
124 function main() {
125 assert(pcntl_signal(SIGINT, 'handle_sigint'));
127 parse_options();
129 echo "-- Simple DBGp Client --\n";
131 global $conn;
133 // Start the listening socket.
134 list($socket, $port) = start_client($conn->port);
136 echo "Listening on port $port\n";
138 // Accept a connection.
139 $fd = null;
140 while (true) {
141 $fd = @socket_accept($socket);
143 if ($fd !== false) {
144 echo "Connected to an XDebug server!\n";
145 break;
148 socket_close($socket);
150 $conn->fd = $fd;
152 while (true) {
153 // Wait for the expected number of responses. Normally we expect 1
154 // response, but with the break command, we expect 2.
155 $responses = "";
156 while ($conn->expect_responses > 0) {
157 $response = read_response($fd);
159 if (starts_with($response, "Client socket error")) {
160 break;
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;
178 continue;
181 // Echo back the response to the user if it isn't a stream.
182 if (!is_stream($responses)) {
183 echo "$responses\n";
186 // Received response saying we're stopping.
187 if (strpos($responses, "status=\"stopped\"") > 0) {
188 echo "-- Request ended, stopping --\n";
189 break;
192 // Get a command from the user and send it.
193 $line = readline("(dbgp) $ ");
194 $line = trim($line);
196 if ($line === "") {
197 continue;
200 if (starts_with("quit", $line)) {
201 echo "-- Quitting, request will continue running --\n";
202 break;
205 send_command($fd, $line);
208 socket_close($fd);
209 $conn->fd = null;
212 main();