| |
PHP ${pdb_version} too old.
\nPlease set the path to a PHP >= 5.3 executable, see php_exec in the WEB-INF/web.xml", E_USER_ERROR); /** * a
simple logger * @access private */ class pdb_Logger { const FATAL = 1; const INFO = 2; const VERBOSE = 3; const
DEBUG = 4; private static $logLevel = 0; private static $logFileName; private static function println($msg, $level)
{ if (!self::$logLevel) self::$logLevel=PDB_DEBUG?self::DEBUG:self::INFO; if ($level <= self::$logLevel) {
static $file = null; if(!isset(self::$logFileName)) { self::$logFileName =
$_SERVER['DOCUMENT_ROOT'].DIRECTORY_SEPARATOR."pdb_PHPDebugger.log"; } if (!$file) $file =
fopen(self::$logFileName, "ab") or die("fopen"); fwrite($file, time().": "); fwrite($file, $msg."\n");
fflush($file); } } public static function logFatal($msg) { self::println($msg, self::FATAL); } public static
function logInfo($msg) { self::println($msg, self::INFO); } public static function logMessage($msg) {
self::println($msg, self::VERBOSE); } public static function logDebug($msg) { self::println($msg, self::DEBUG); }
public static function debug($msg) { self::logDebug($msg); } public static function log($msg) {
self::logMessage($msg); } public static function setLogLevel($level) { self::$logLevel=$level; } public static
function setLogFileName($name) { self::$logFileName = $name; } } /** * @access private */ interface pdb_Queue { /**
* Read a string from the queue * @return string a json encoded string of values * @access private */ public
function read(); /** * Write a string to the queue * @param string a json encoded string of values or TRUE *
@access private */ public function write($val); /** * Set the script output before server shutdown * @access
private */ public function setOutput($output); /** * Get the script output * @access private */ public function
getOutput(); /** * Mark the channel as dead. If marked, read will return boolean * TRUE, write will do nothing. *
@access private */ public function shutdown(); } /** * This class represents the debugger back end connection. It *
communicates with the debugger front end using a shared-memory queue. * It is slow, but it does not require any
special library. * @access private */ class pdb_PollingServerConnection implements pdb_Queue { protected $id;
protected $role, $to; protected $chanTrm; // "back end terminated" flag protected $output; const TIMER_DURATION =
200000; // every 200ms /** * Create a new communication using a unique id * @access private */ public function
pdb_PollingServerConnection($id) { $this->id = $id; $this->chanTrm = "pdb_trmserverthis->id}";
$this->output = ""; $this->prepareCookies(); $this->init(); } protected function checkTrm() { return
false!==$_SESSION[$this->chanTrm]; } protected function prepareCookies() { ini_set("session.use_cookies", true);
session_start(); session_write_close(); /* avoid PHP bug, which repeats set-cookie header for each iteration of
session_start/session_write_close */ ini_set("session.use_cookies", false); } protected function init() {
session_start(); $this->role = "client"; $this->to = "server"; $chanCtr =
"pdb_ctr{%this->role}{%this->id}"; $chan = "pdb_{%this->role}{%this->id}"; unset ($_SESSION[$chan]);
unset ($_SESSION[$chanCtr]); $this->role = "server"; $this->to = "client"; $chanCtr =
"pdb_ctr{%this->role}{%this->id}"; $chan = "pdb_{%this->role}{%this->id}"; unset ($_SESSION[$chan]);
unset ($_SESSION[$chanCtr]); if (isset($_SESSION[$this->chanTrm]) && !$this->checkTrm()) {
$_SESSION[$this->chanTrm] = true; session_write_close(); sleep(1); session_start(); }
$_SESSION[$this->chanTrm] = false; session_write_close(); } protected function poll() { $val = ""; $chanCtr =
"pdb_ctr{%this->role}{%this->id}"; $chan = "pdb_{%this->role}{%this->id}"; session_start(); if (!($val
= $this->checkTrm())) { if(!isset($_SESSION[$chanCtr])) { $_SESSION[$chan] = array(); $_SESSION[$chanCtr]=0; }
$seq = $_SESSION[$chanCtr]; $seqNext = count($_SESSION[$chan]); if (PDB_DEBUG)
pdb_Logger::debug("...{%this->role}, {%this->id} poll next # ${seqNext} (${seq}) ..."); if ($seqNext >
$seq) { $val = json_decode($_SESSION[$chan][$seq]); $_SESSION[$chan][$seq]=null; $_SESSION[$chanCtr]++; if
(PDB_DEBUG) pdb_Logger::debug("...{%this->role}, {%this->id} polled next # ${seqNext} (${seq}), got:
{%val->seq}"); } } session_write_close(); return $val; } protected function send($val) { $seq = $val->seq;
$chan = "pdb_{%this->to}{%this->id}"; session_start(); if (!$this->checkTrm())
$_SESSION[$chan][$seq]=json_encode($val); if (PDB_DEBUG) pdb_Logger::debug("...{%this->role}, {%this->id}
send: ${seq} ..."); session_write_close(); } /** * read a new value from the read queue * @access private */ public
function read() { $val = null; $cntr = 0; while(!($val=$this->poll())) { if ($cntr<=20) { $cntr++;
usleep(self::TIMER_DURATION); } else { usleep(self::TIMER_DURATION*5); } } return $val === true ? null : $val; }
/** * write a new value to the write queue * @access private */ public function write($val) {
$this->send((object)$val); } /** * Set the script output * @access private */ public function setOutput($output)
{ $this->output = $output; } /** * Get the script output * @access private */ public function getOutput() {
return $_SESSION[$this->chanTrm]; } /** * shut down the communication channel */ public function shutdown() { if
(PDB_DEBUG) pdb_Logger::debug("session terminated: {%this->chanTrm}"); session_start();
$_SESSION[$this->chanTrm] = $this->output; session_write_close(); } } /** * This class represents the
debugger front end connection. It * communicates with the debugger back end using a shared-memory queue. * It is
slow, but it does not require any special library. * @access private */ class pdb_PollingClientConnection extends
pdb_PollingServerConnection { private $seq; protected function init() { $this->role = "client"; $this->to =
"server"; } protected function poll() { $chan = "pdb_{%this->role}{%this->id}"; session_start(); if (!($val =
$this->checkTrm())) { if (PDB_DEBUG) pdb_Logger::debug("...{%this->role}, {%this->id} poll for
{%this->seq} ..."); if (isset($_SESSION[$chan][$this->seq])) { $val =
json_decode($_SESSION[$chan][$this->seq]); if (PDB_DEBUG) pdb_Logger::debug("...{%this->role}, {%this->id}
polled for {%this->seq}, got: {%val->seq}"); unset($_SESSION[$chan][$this->seq]); } }
session_write_close(); return $val; } /** * write a new value to the write queue * @access private */ public
function write($val) { $this->seq = $val->seq; parent::write($val); } /** * shut down the communication
channel * @access private */ public function shutdown() {} } if (!class_exists("pdb_Parser")) { /** * The PHP
parser * @access private */ class pdb_Parser { const BLOCK = 1; const STATEMENT = 2; const EXPRESSION = 3; const
FUNCTION_BLOCK = 4; // BLOCK w/ STEP() as last statement private $scriptName, $content; private $code; private
$output; private $line, $currentLine; private $beginStatement, $inPhp, $inDQuote; /** * Create a new PHP parser *
@param string the script name * @param string the script content * @access private */ public function
pdb_Parser($scriptName, $content) { $this->scriptName = $scriptName; $this->content = $content;
$this->code = token_get_all($content); $this->output = ""; $this->line = $this->currentLine = 0;
$this->beginStatement = $this->inPhp = $this->inDQuote = false; } private function toggleDQuote($chr) { if
($chr == '"') $this->inDQuote = !$this->inDQuote; } private function each() { $next = each ($this->code);
if ($next) { $cur = current($this->code); if (is_array($cur)) { $this->currentLine = $cur[2] + ($cur[1][0] ==
"\n" ? substr_count($cur[1], "\n") : 0); if ($this->isWhitespace($cur)) { $this->write($cur[1]); return
$this->each(); } } else $this->toggleDQuote($cur); } return $next; } private function write($code) { //echo
"write:::".$code."\n"; $this->output.=$code; } private function writeInclude($once) { $name = ""; while(1) { if
(!$this->each()) die("parse error"); $val = current($this->code); if (is_array($val)) { $name.=$val[1]; }
else { if ($val==';') break; $name.=$val; } } if (PDB_DEBUG == 2) $this->write("EVAL($name);"); else
$this->write("eval('?>'.pdb_startInclude($name, $once)); pdb_endInclude();"); } private function writeCall()
{ while(1) { if (!$this->each()) die("parse error"); $val = current($this->code); if (is_array($val)) {
$this->write($val[1]); } else { $this->write($val); if ($val=='{') break; } } $scriptName =
addslashes($this->scriptName); $this->write("\$__pdb_CurrentFrame=pdb_startCall(\"$scriptName\",
{%this->currentLine});"); } private function writeStep($pLevel) { $token = current($this->code); if
($this->inPhp && !$pLevel && !$this->inDQuote && $this->beginStatement &&
!$this->isWhitespace($token) && ($this->line != $this->currentLine)) { $line = $this->line =
$this->currentLine; $scriptName = addslashes($this->scriptName); if (PDB_DEBUG == 2)
$this->write(";STEP($line);"); else $this->write(";pdb_step(\"$scriptName\", $line,
pdb_getDefinedVars(get_defined_vars(), (isset(\$this) ? \$this : NULL)));"); } } private function writeNext() {
$this->next(); $token = current($this->code); if (is_array($token)) $token = $token[1];
$this->write($token); } private function nextIs($chr) { $i = 0; while(each($this->code)) { $cur =
current($this->code); $i++; if (is_array($cur)) { switch ($cur[0]) { case T_COMMENT: case T_DOC_COMMENT: case
T_WHITESPACE: break; /* skip */ default: while($i--) prev($this->code); return false; /* not found */ } } else {
while($i--) prev($this->code); return $cur == $chr; /* found */ } } while($i--) prev($this->code); return
false; /* not found */ } private function nextTokenIs($ar) { $i = 0; while(each($this->code)) { $cur =
current($this->code); $i++; if (is_array($cur)) { switch ($cur[0]) { case T_COMMENT: case T_DOC_COMMENT: case
T_WHITESPACE: break; /* skip */ default: while($i--) prev($this->code); return (in_array($cur[0], $ar)); } }
else { break; /* not found */ } } while($i--) prev($this->code); return false; /* not found */ } private
function isWhitespace($token) { $isWhitespace = false; switch($token[0]) { case T_COMMENT: case T_DOC_COMMENT: case
T_WHITESPACE: $isWhitespace = true; break; } return $isWhitespace; } private function next() { if
(!$this->each()) trigger_error("parse error", E_USER_ERROR); } private function parseBlock () {
$this->parse(self::BLOCK); } private function parseFunction () { $this->parse(self::FUNCTION_BLOCK); }
private function parseStatement () { $this->parse(self::STATEMENT); } private function parseExpression () {
$this->parse(self::EXPRESSION); } private function parse ($type) { if (PDB_DEBUG)
pdb_Logger::debug("parse:::$type"); $this->beginStatement = true; $pLevel = 0; do { $token =
current($this->code); if (!is_array($token)) { if (PDB_DEBUG) pdb_Logger::debug(":::".$token); if (!$pLevel
&& $type==self::FUNCTION_BLOCK && $token=='}') $this->writeStep($pLevel);
$this->write($token); if ($this->inPhp && !$this->inDQuote) { $this->beginStatement = false;
switch($token) { case '(': $pLevel++; break; case ')': if (!--$pLevel && $type==self::EXPRESSION) return;
break; case '{': $this->next(); $this->parseBlock(); break; case '}': if (!$pLevel) return; break; case ';':
if (!$pLevel) { if ($type==self::STATEMENT) return; $this->beginStatement = true; } break; } } } else { if
(PDB_DEBUG) pdb_Logger::debug(":::".$token[1].":(".token_name($token[0]).')'); if ($this->inDQuote) {
$this->write($token[1]); continue; } switch($token[0]) { case T_OPEN_TAG: case T_START_HEREDOC: case
T_OPEN_TAG_WITH_ECHO: $this->beginStatement = $this->inPhp = true; $this->write($token[1]); break; case
T_END_HEREDOC: case T_CLOSE_TAG: $this->writeStep($pLevel); $this->write($token[1]); $this->beginStatement
= $this->inPhp = false; break; case T_FUNCTION: $this->write($token[1]); $this->writeCall();
$this->next(); $this->parseFunction(); $this->beginStatement = true; break; case T_ELSE:
$this->write($token[1]); if ($this->nextIs('{')) { $this->writeNext(); $this->next();
$this->parseBlock(); } else { $this->next(); /* create an artificial block */ $this->write('{');
$this->beginStatement = true; $this->writeStep($pLevel); $this->parseStatement(); $this->write('}'); }
if ($type==self::STATEMENT) return; $this->beginStatement = true; break; case T_DO:
$this->writeStep($pLevel); $this->write($token[1]); if ($this->nextIs('{')) { $this->writeNext();
$this->next(); $this->parseBlock(); $this->next(); } else { $this->next(); /* create an artificial
block */ $this->write('{'); $this->beginStatement = true; $this->writeStep($pLevel);
$this->parseStatement(); $this->next(); $this->write('}'); } $token = current($this->code);
$this->write($token[1]); if ($token[0]!=T_WHILE) trigger_error("parse error", E_USER_ERROR); $this->next();
$this->parseExpression(); if ($type==self::STATEMENT) return; $this->beginStatement = true; break; case
T_CATCH: case T_IF: case T_ELSEIF: case T_FOR: case T_FOREACH: case T_WHILE: $this->writeStep($pLevel);
$this->write($token[1]); $this->next(); $this->parseExpression(); if ($this->nextIs('{')) {
$this->writeNext(); $this->next(); $this->parseBlock(); } else { $this->next(); /* create an artificial
block */ $this->write('{'); $this->beginStatement = true; $this->writeStep($pLevel);
$this->parseStatement(); $this->write('}'); } if ($this->nextTokenIs(array(T_ELSE, T_ELSEIF, T_CATCH))) {
$this->beginStatement = false; } else { if ($type==self::STATEMENT) return; $this->beginStatement = true; }
break; case T_REQUIRE_ONCE: case T_INCLUDE_ONCE: case T_INCLUDE: case T_REQUIRE: $this->writeStep($pLevel);
$this->writeInclude((($token[0]==T_REQUIRE_ONCE) || ($token[0]==T_INCLUDE_ONCE)) ? 1 : 0); if
($type==self::STATEMENT) return; $this->beginStatement = true; break; case T_CLASS: $this->write($token[1]);
$this->writeNext(); if ($this->nextIs('{')) { $this->writeNext(); $this->next();
$this->parseBlock(); $this->beginStatement = true; } else { $this->writeNext(); $this->beginStatement =
false; } break; case T_CASE: case T_DEFAULT: case T_PUBLIC: case T_PRIVATE: case T_PROTECTED: case T_STATIC: case
T_CONST: case T_GLOBAL: case T_ABSTRACT: $this->write($token[1]); $this->beginStatement = false; break;
default: $this->writeStep($pLevel); $this->write($token[1]); $this->beginStatement = false; break; } } }
while($this->each()); } /** * parse the given PHP script * @return the parsed PHP script * @access private */
public function parseScript() { do { $this->parseBlock(); } while($this->each()); return $this->output; }
} } /** * This structure represents the debugger front-end. It is used by the * JavaScript code to communicate with
the debugger back end. * @access private */ class pdb_JSDebuggerClient { private static function
getDebuggerFilename() { $script = __FILE__; $scriptName = basename($script); return realpath($scriptName ==
"PHPDebugger.php" ? $script :"java/PHPDebugger.php"); } private static function getCurrentRootDir() { $scriptName =
$_SERVER['SCRIPT_NAME']; $scriptFilename = $_SERVER['SCRIPT_FILENAME']; $scriptDirName = dirname($scriptName);
$scriptDir = dirname($scriptFilename); if ((strlen($scriptDirName)>1) && ($scriptDirName[1]=='~')) {
$scriptDirName = ltrim($scriptDirName, "/"); $idx = strpos($scriptDirName, '/'); $scriptDirName = $idx===false ? ''
: substr($scriptDirName, $idx); } elseif ((strlen($scriptDirName)==1) && (($scriptDirName[0]=='/') ||
($scriptDirName[0]=='\\'))) { $scriptDirName = ''; } if (PDB_DEBUG) pdb_Logger::debug("scriptDir: $scriptDir,
scriptDirName: $scriptDirName"); if ((strlen($scriptDir) < strlen($scriptDirName)) || ($scriptDirName &&
(substr($scriptDir, -strlen($scriptDirName)) != $scriptDirName))) return null; else return substr($scriptDir, 0,
strlen($scriptDir)-strlen($scriptDirName)); } /* * Return the script name * Example:
%2Fopt%2Fappserv%2Fapache-tomcat-6.0.14%2Fwebapps%2FJavaBridge%2Ftest.php" * * @return An urlencoded file name. */
public static function getDebugScriptName() { return urlencode($_SERVER['SCRIPT_FILENAME']); } /** * Return the
debugger URL. * Example: "/JavaBridge/java/PHPDebugger.php?source=settings.php" * * @return The debugger URL. *
@access private */ public static function getDebuggerURL() { $path = self::getDebuggerFilename(); if (!$path)
trigger_error("java/PHPDebugger.php not found in document root", E_USER_ERROR); $root = self::getCurrentRootDir();
$scriptName = $_SERVER['SCRIPT_NAME']; $scriptDirName = dirname($scriptName); $prefix = ''; if
((strlen($scriptDirName)>1) && ($scriptDirName[1]=='~')) { $scriptDirName = ltrim($scriptDirName, "/");
$idx = strpos($scriptDirName, '/'); $prefix = '/' . ($idx ? substr($scriptDirName, 0, $idx): $scriptDirName); } if
(PDB_DEBUG) pdb_Logger::debug("serverRoot: $root - path: $path"); if ($root && (strlen($root) <
strlen($path)) && (!strncmp($path, $root, strlen($root)))) $path = "${prefix}" . str_replace('\\', '/',
substr($path, strlen($root))); else // could not calculate debugger path $path = dirname($_SERVER['SCRIPT_NAME']) .
"/java/PHPDebugger.php"; $pathInfo = isset($_SERVER['PATH_INFO']) ? $_SERVER['PATH_INFO'] : ""; $query =
isset($_SERVER['QUERY_STRING']) ? $_SERVER['QUERY_STRING'] : ""; $url = "${path}${pathInfo}"; if ($query) $url .=
"?${query}"; return $url; } public static function getPostData() { $str = ''; foreach ($_POST as $key => $value)
{ if ($str) $str .= '&'; $str .= $key . '=' . urlencode($value); } return $str; } /** * Get the server's uniqe
session ID * @return a uniqe session ID * @access private */ public static function getServerID() { // TODO: allow
more than one debug session return 1; } private static function getConnection($id) { return new
pdb_PollingClientConnection($id); } private static function stripslashes($value) { $value = is_array($value) ?
array_map("self::stripslashes", $value) : stripslashes($value); return $value; } /** * Pass the command and
arguments to the debug back end and * output the response to JavaScript. * * @arg array The command arguments *
@access private */ public static function handleRequest($vars) { if (get_magic_quotes_gpc()) $vars =
self::stripslashes($vars); $msg = (object)$vars; if ($msg->cmd == "begin") sleep(1); // wait for the server to
settle if (PDB_DEBUG) pdb_Logger::debug("beginHandleRequest: ".$msg->cmd); $conn =
self::getConnection($msg->serverID); $conn->write($msg); if (!($response = $conn->read())) $output =
json_encode(array("cmd"=>"term", "output"=>$conn->getOutput())); else $output = json_encode($response);
echo "($output)"; if (PDB_DEBUG) pdb_Logger::debug("endHandleRequest"); } } /** * The current view frame. Contains
the current script name. May be * selected by clicking on a include() hyperlink Allows users to set * breakpoints
for this file. * * View will be discarded when go, step, end is invoked. * @access private */ class pdb_View { /**
The current script name */ public $scriptName; /** Back-link to the parent or null */ public $parent; protected
$bpCounter, $lineCounter, $code; /** * Create a new view frame * @param object the parent frame * @param string the
script name */ public function pdb_View($parent, $scriptName) { $this->parent = $parent; $this->scriptName =
$scriptName; $this->bpCounter = $this->lineCounter = 1; $this->code = null; } private function lineCB
($val) { return $val.(string)$this->lineCounter++; } private function breakpointCB ($val) { return
$val.'id="bp_'.(string)$this->bpCounter++.'"'; } function replaceCallback($code, $split, $cb) { $ar =
explode($split, $code); $last = array_pop($ar); $ar = array_map($cb, $ar); array_push($ar, $last); return
implode($ar); } /** * Return a HTML representation of the current script * @return string the clickable HTML
representation of the current script */ public function getHtmlScriptSource() { if (!$this->code) { $c=
''. ''. 'line#'. ''.
'
'; $code=show_source($this->scriptName, true); // br => span id=pb_ ... $code = str_replace('
', $c, $code); // handle incomplete last line, identical to preg: '|(?)\n\n$|' $code =
ereg_replace('"(([^>])|([^/]>)|([^ ]/>)|([^r] />)|([^b]r />)|([^<]br />))(\n\n)"', '\1 $c \8',
$code); $code = $this->replaceCallback($code, 'id="bp_"', array($this, "breakpointCB")); $code =
$this->replaceCallback($code, 'line#', array($this, "lineCB")); $this->code = $code; } return $this->code;
} } /** * The current view. Used to show the contents of a variable * @access private */ class pdb_VariableView
extends pdb_View { /** * Create a new variable view * @param object the parent frame * @param string the variable
name * @param string the variable value */ public function pdb_VariableView($parent, $name, $value) {
parent::pdb_View($parent, $name); $this->value = $value; } /** * {@inheritDoc} */ public function
getHtmlScriptSource() { return (highlight_string(print_r($this->value, true), true)); } } /** * The current
execution frame. Contains the current run-time script * name along with its state * @access private */ class
pdb_Environment extends pdb_View { /** bool true if a dynamic breakpoint should be inserted at the next line, false
otherwise */ public $stepNext; /** The execution vars */ public $vars; /** The current line */ public $line; /** *
Create a new execution frame * @param string the script name * @param bool true if a dynamic breakpoint should be
inserted at the next line, false otherwise */ public function pdb_Environment($parent, $scriptName, $stepNext) {
parent::pdb_View($parent, $scriptName); $this->stepNext = $stepNext; $this->line = -1; } /** * Update the
execution frame with the current state * @param string the current script name * @param int the current execution
line * @param mixed the current variables */ public function update ($line, &$vars) { $this->line = $line;
$this->vars = $vars; } public function __toString() { return "pdb_Environment: {%this->scriptName},
{%this->line}"; } } /** * Represents a breakpoint * @access private */ class pdb_Breakpoint { /** The script
name */ public $scriptName; /** The current line */ public $line; /** The breakpointName as seen by JavaScript */
public $breakpoint; /* The breakpoint type (not used yet) */ public $type; /** * Create a new breakpoint * @param
string the breakpoint name * @param string the script name * @param int the line */ public function
pdb_Breakpoint($breakpointName, $scriptName, $line) { $this->breakpoint = $breakpointName; $this->scriptName
= $scriptName; $this->line = $line; $this->type = 1; } /** * @return the string representation of the
breakpoint */ public function __toString() { return "{%this->line}@{%this->scriptName}, js name:
({%this->breakpoint}, type: {%this->type})"; } } /** * The current debug session. Contains the current
environment stack, * script output and all breakpoints set by the client. An optional * view is set by the
switchView command. * @access private */ final class pdb_Session { /** The collection of breakpoints */ public
$breakpoints; /** List of all frames */ public $allFrames; /** The current top level frame */ public
$currentTopLevelFrame; /** The current execution frame */ public $currentFrame; /** The current view */ public
$currentView; /** The script output */ public $output; /** * Create a new debug session for a given script * @param
string the script name */ public function pdb_Session($scriptName) { $this->breakpoints = $this->lines =
array(); $this->currentTopLevelFrame = $this->currentFrame = new pdb_Environment(null, $scriptName, true);
$this->allFrames[] = $this->currentFrame; $this->currentView = null; } /** * Return the clickable HTML
script source, either from the cusom view or from the current frame * @return string the HTML script source */
public function getCurrentViewHtmlScriptSource () { return $this->currentView ?
$this->currentView->getHtmlScriptSource() : $this->currentFrame->getHtmlScriptSource(); } /** * Return
the current frame script name * @return string the script name of the current frame */ public function
getScriptName () { return $this->currentFrame->scriptName; } /** * Return the current script name, either
from the view or from the current frame * @return string the current script name */ public function
getCurrentViewScriptName () { return $this->currentView ? $this->currentView->scriptName :
$this->getScriptName(); } /** * Return the breakpoints for the current script * @return object the breakpoints
*/ public function getBreakpoints () { $bp = array(); foreach ($this->breakpoints as $breakpoint) { if
($this->getCurrentViewScriptName() != $breakpoint->scriptName) continue; array_push($bp,
$breakpoint->breakpoint); } return $bp; } /** * toggle and write breakpoint reply * @param object the current
comm. channel * @param object the breakpoint */ public function toggleBreakpoint($breakpoint) { $id =
$breakpoint."@".$this->getCurrentViewScriptName(); if (!isset($this->breakpoints[$id])) {
$this->breakpoints[$id] = new pdb_Breakpoint($breakpoint, $this->getCurrentViewScriptName(),
substr($breakpoint, 3)); return false; } else { $bp = $this->breakpoints[$id]; unset
($this->breakpoints[$id]); return $bp; } } /** * check if there's a breakpoint * @param string the script name *
@param int the line within the script * @return true if a breakpoint exists at line, false otherwise */ public
function hasBreakpoint($scriptName, $line) { if ($this->currentFrame->stepNext) return true; foreach
($this->breakpoints as $breakpoint) { if (PDB_DEBUG) pdb_Logger::debug("process breakpoint::: $scriptName,
$line:: $breakpoint"); if($breakpoint->type==1) { if
($breakpoint->scriptName==$scriptName&&$breakpoint->line==$line) return true; } } return false; } /**
* parse code * @param string the script name * @param string the content * @return the parsed script */ public
function parseCode($scriptName, $content) { $parser = new pdb_Parser($scriptName, $content); return
$parser->parseScript(); } private static function doEval($__pdb_Code) { return eval ("?>".$__pdb_Code); } /**
* parse and execute script * @return the script output */ public function evalScript() { $code =
$this->parseCode($this->getScriptName(), file_get_contents($this->getScriptName())); if (PDB_DEBUG)
pdb_Logger::debug("eval:::$code,".$this->getScriptName()."\n"); ob_start(); self::doEval ($code);
$this->output = ob_get_contents(); ob_end_clean(); return $this->output; } } /** * The java script debugger
server daemon. Contains a debug session * and handles debug requests from the client. * @access private */ class
pdb_JSDebugger { /** The pdb_Session */ public $session; private $id; public $end; private $includedScripts;
private $conn; private $ignoreInterrupt; const STEP_INTO = 1; const STEP_OVER = 2; const STEP_OUT = 3; const GO =
4; private function getConnection($id) { return new pdb_PollingServerConnection($id); } /** * Create new PHP
debugger using a given comm. ID * @param int the communication address */ public function pdb_JSDebugger($id) {
$this->id = $id; $this->conn = $this->getConnection($id); $this->end = false; $this->session = null;
$this->includedScripts = array(); $this->ignoreInterrupt = false; set_error_handler("pdb_error_handler");
register_shutdown_function("pdb_shutdown"); } public function setError($errno, $errfile, $errline, $errstr) {
highlight_string("PHP error $errno: $errstr in $errfile line $errline"); } /** * Return the current comm. ID *
@return int the communication address */ public function getServerID() { return $this->id; } /** * Read data
from the front end * @return object the data */ public function read() { return $this->conn->read(); } /** *
Write data to the front end * @param object the data */ public function write($data) { $data["serverID"] =
$this->getServerID(); if (PDB_DEBUG) pdb_Logger::debug("->".print_r($data, true)); return
$this->conn->write($data); } private function ack() {
$this->write(array("cmd"=>$this->packet->cmd, "seq"=>$this->packet->seq)); } private function
getOutput() { if (!$this->session) return ""; if (!$this->end) $output = $this->session->output =
ob_get_contents(); return $this->session->output; } /** * Handle requests from the front end */ public
function handleRequests() { $this->ignoreInterrupt = false; while(!$this->end) { if (PDB_DEBUG)
pdb_Logger::debug("handleRequests: accept"); if (!($this->packet = $this->read())) break; // ignore
__destructors after shutdown if (PDB_DEBUG) pdb_Logger::debug("handleRequests: done accept
".$this->packet->cmd); switch($this->packet->cmd) { case "status":
$this->write(array("cmd"=>$this->packet->cmd, "seq"=>$this->packet->seq,
"line"=>$this->session->currentFrame->line,
"scriptName"=>$this->session->getCurrentViewScriptName(),
"breakpoints"=>$this->session->getBreakpoints())); break; case "extendedStatus":
$this->write(array("cmd"=>$this->packet->cmd, "seq"=>$this->packet->seq,
"line"=>$this->session->currentFrame->line,
"scriptName"=>$this->session->getCurrentViewScriptName(),
"script"=>$this->session->getCurrentViewHtmlScriptSource(),
"breakpoints"=>$this->session->getBreakpoints())); break; case "begin": chdir
(urldecode($this->packet->cwd)); $this->session = new
pdb_Session(urldecode($this->packet->scriptName)); $this->write(array("cmd"=>$this->packet->cmd,
"seq"=>$this->packet->seq, "scriptName"=>$this->packet->scriptName,
"script"=>$this->session->getCurrentViewHtmlScriptSource())); $this->session->evalScript();
$this->end = true; break; case "stepNext": if ($this->end) break; $this->session->currentView = null;
$this->ack(); return self::STEP_INTO; case "stepOver": if ($this->end) break;
$this->session->currentView = null; $this->ack(); return self::STEP_OVER; case "go": if ($this->end)
break; $this->session->currentView = null; $this->ack(); return self::GO; case "stepOut": if
($this->end) break; $this->session->currentView = null; $this->ack(); return self::STEP_OUT; case
"toggleBreakpoint": $bp = $this->session->toggleBreakpoint($this->packet->breakpoint);
$this->write($bp ? (array("cmd"=>"unsetBreakpoint", "seq"=>$this->packet->seq,
"scriptName"=>$bp->scriptName, "breakpoint"=>$bp->breakpoint)) : (array("cmd"=>"setBreakpoint",
"seq"=>$this->packet->seq, "scriptName"=>$this->session->getCurrentViewScriptName(),
"breakpoint"=>$this->packet->breakpoint))); break; case "toolTip": $name =
urldecode($this->packet->item); $value = ""; if ($name[0]=='$') { $idx = substr($name, 1); $env = (object)
$this->session->currentFrame->vars; $code = "return \$env->$idx;"; $value = eval($code); if
(is_object($value)) { $value = get_class($value) . " object"; } elseif (is_array($value)) { $value =
"array[".count($value)."]"; } elseif (!isset($value)) { $value = ""; } else { $value = print_r($value, true); } }
else { $value = $this->packet->item; } $this->write(array("cmd"=>$this->packet->cmd,
"seq"=>$this->packet->seq, "item"=>$this->packet->item, "value"=>$value)); break; case
"switchView": if (PDB_DEBUG) pdb_Logger::debug("switchView here"); $name =
urldecode($this->packet->scriptName); if (PDB_DEBUG) pdb_Logger::debug("switchView $name"); if
($name[0]=='$') { $idx = substr($name, 1); $env = (object) $this->session->currentFrame->vars; $code =
"return \$env->$idx;"; $pdb_dbg->end = true; $value = eval($code); $pdb_dbg->end = false;
$this->session->currentView = new pdb_VariableView($this->session->currentView, $name, $value); } else
{ $this->end = true; $value = self::resolveIncludePath(eval("return ${name};")); $this->end = false;
$this->session->currentView = new pdb_View($this->session->currentView, realpath($value)); }
$this->ack(); break; case "backView": if ($this->session->currentView) $this->session->currentView =
$this->session->currentView->parent; $this->ack(); break; case "output": if ($this->session) {
$this->write(array("cmd"=>$this->packet->cmd, "seq"=>$this->packet->seq,
"output"=>$this->getOutput())); } else { $this->ack(); } break; case "end": $this->end(); break;
default: if (PDB_DEBUG) pdb_Logger::debug("illegal packet: " . print_r($this->packet, true)); exit(1); } }
return self::GO; } public function end() { $this->session->currentView = null;
$this->write(array("cmd"=>"end", "seq"=>$this->packet->seq, "output"=>$this->getOutput()));
$this->end = true; } /** * shut down the current comm. channel */ public function shutdown() {
$this->conn->setOutput($this->getOutput()); $this->conn->shutdown(); } /** * called at run-time for
each frame * @return the current frame */ public function startCall($scriptName) { /* check for stepOver and
stepOut */ $stepNext = $this->session->currentFrame->stepNext == pdb_JSDebugger::STEP_INTO ?
pdb_JSDebugger::STEP_INTO : false; if (PDB_DEBUG) pdb_Logger::debug("startCall::$scriptName, $stepNext"); $env =
new pdb_Environment($this->session->currentFrame, $scriptName, $stepNext); $this->session->allFrames[]
= $env; return $env; } /** * @access private */ protected function resolveIncludePath($scriptName) { if
(file_exists($scriptName)) return realpath($scriptName); $paths = explode(PATH_SEPARATOR, get_include_path());
$name = $scriptName; foreach ($paths as $path) { $x = substr($path, -1); if ($x != "/" && $x !=
DIRECTORY_SEPARATOR) $path.=DIRECTORY_SEPARATOR; $scriptName = realpath("${path}${name}"); if ($scriptName) return
$scriptName; } trigger_error("file $scriptName not found", E_USER_ERROR); } /** * called at run-time for each
included file * @param string the script name * @return string the code */ public function
startInclude($scriptName, $once) { $isDebugger = (basename($scriptName) == "PHPDebugger.php"); if (!$isDebugger)
$scriptName = $this->resolveIncludePath($scriptName); if (PDB_DEBUG) pdb_Logger::debug("scriptName::$scriptName,
$isDebugger"); if ($once && isset($this->includedScripts[$scriptName])) $isDebugger = true; // include
only from a top-level environment // initial line# and vars may be wrong due to a side-effect in step
$this->session->currentFrame = $this->session->currentTopLevelFrame; $stepNext =
$this->session->currentFrame->stepNext == pdb_JSDebugger::STEP_INTO ? pdb_JSDebugger::STEP_INTO : false;
$this->session->currentFrame = new pdb_Environment($this->session->currentFrame, $scriptName,
$stepNext); $this->session->allFrames[] = $this->session->currentFrame; if ($isDebugger) // do not
debug self $code = ""; else $code = $this->session->parseCode(realpath($scriptName),
file_get_contents($scriptName)); $this->session->currentTopLevelFrame = $this->session->currentFrame;
if (PDB_DEBUG) pdb_Logger::debug("startInclude:::".$this->session->currentTopLevelFrame . " parent: " .
$this->session->currentTopLevelFrame->parent . " code: ".$code); if ($once)
$this->includedScripts[$scriptName] = true; return $code; } /** * called at run-time after the script has been
included */ public function endInclude() { if (PDB_DEBUG)
pdb_Logger::debug("endInclude:::".$this->session->currentTopLevelFrame . "parent:
".$this->session->currentTopLevelFrame->parent); $this->session->currentFrame =
$this->session->currentTopLevelFrame = $this->session->currentTopLevelFrame->parent; } /** * called
at run-time for each line * @param string the script name * @param int the current line * @param mixed the
execution variables */ public function step($scriptName, $line, $vars) { if ($this->ignoreInterrupt) return; //
avoid spurious step calls from __destruct() method $this->ignoreInterrupt = true; if (PDB_DEBUG)
pdb_Logger::logDebug("step: $scriptName @ $line"); // pull the current frame from the stack or the top-level
environment $this->session->currentFrame = (isset($vars['__pdb_CurrentFrame'])) ? $vars['__pdb_CurrentFrame']
: $this->session->currentTopLevelFrame; unset($vars['__pdb_CurrentFrame']);
$this->session->currentFrame->update($line, $vars); if ($this->session->hasBreakpoint($scriptName,
$line)) { $stepNext = $this->handleRequests(); if (PDB_DEBUG) pdb_Logger::logDebug("continue"); /* clear all
dynamic breakpoints */ foreach ($this->session->allFrames as $currentFrame) $currentFrame->stepNext =
false; /* set new dynamic breakpoint */ if ($stepNext != pdb_JSDebugger::GO) { $currentFrame =
$this->session->currentFrame; /* break in current frame or frame below */ if ($stepNext !=
pdb_JSDebugger::STEP_OUT) $currentFrame->stepNext = $stepNext; /* or break in any parent */ while ($currentFrame
= $currentFrame->parent) { $currentFrame->stepNext = $stepNext; } } } $this->ignoreInterrupt = false; if
(PDB_DEBUG) pdb_Logger::logDebug("endStep: $scriptName @ $line"); } } /** * Convenience function called by the
executor * @access private */ function pdb_getDefinedVars($vars1, $vars2) { if(isset($vars2)) $vars1['pbd_This'] =
$vars2; unset($vars1['__pdb_Code']); // see pdb_Message::doEval() return $vars1; } /** * Convenience function
called by the executor * @access private */ function pdb_startCall($scriptName, $line) { global $pdb_dbg; if
(isset($pdb_dbg)) return $pdb_dbg->startCall($scriptName); } /** * Convenience function called by the executor *
@access private */ function pdb_startInclude($scriptName, $once) { global $pdb_dbg; if (isset($pdb_dbg)) return
$pdb_dbg->startInclude($scriptName, $once); else return ""; } /** * Convenience function called by the executor
* @access private */ function pdb_endInclude() { global $pdb_dbg; if (isset($pdb_dbg)) $pdb_dbg->endInclude(); }
/** * Convenience function called by the executor * @access private */ function pdb_step($scriptName, $line, $vars)
{ global $pdb_dbg; if (isset($pdb_dbg)) $pdb_dbg->step($scriptName, $line, $vars); } /** * @access private */
function pdb_error_handler($errno, $errstr, $errfile, $errline) { global $pdb_dbg; if (PDB_DEBUG)
pdb_Logger::debug("PHP error $errno: $errstr in $errfile line $errline"); if ($pdb_dbg->end) return true; if
(strncmp(basename($errfile),"PHPDebugger", 11)) $pdb_dbg->setError($errno, $errfile, $errline, $errstr); return
true; } /** * @access private */ function pdb_shutdown() { global $pdb_dbg; if (PDB_DEBUG) pdb_Logger::debug("PHP
error: ".print_r(error_get_last(), true)); if ($pdb_dbg->end) return; $error = error_get_last(); if ($error) {
$pdb_dbg->setError($error['type'], $error['file'], $error['line'], $error['message']); $pdb_dbg->end();
$pdb_dbg->shutdown(); } } if (PDB_DEBUG==2) { $parser = new pdb_Parser($argv[1], file_get_contents($argv[1]));
echo $parser->parseScript(); exit (2); } /* * The JavaScript part, invoked after the debugger has been
included() * */ if (!isset($_SERVER['HTTP_XPDB_DEBUGGER'])) { session_start(); header("Expires: Sat, 1 Jan 2005
00:00:00 GMT"); header("Last-Modified: ".gmdate( "D, d M Y H:i:s")." GMT"); header("Cache-Control: no-store,
no-cache, must-revalidate, post-check=0, pre-check=0"); header("Pragma: no-cache"); header("Content-Type:
text/html"); ?>
|
|
|