summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--devel/sample.cpp.php15
-rw-r--r--lib/Decompiler.class.php1146
2 files changed, 671 insertions, 490 deletions
diff --git a/devel/sample.cpp.php b/devel/sample.cpp.php
index 0a08c81..b85229d 100644
--- a/devel/sample.cpp.php
+++ b/devel/sample.cpp.php
@@ -253,6 +253,7 @@ abstract class ClassName
$a ^= $b;
$a = 'a' . 'b';
$a = 'a' . 'abc';
+ $a = "a$abc d"; // RESULT: 'a' . $abc . ' d';
$a = __FILE__;
#if PHP_VERSION >= 530
$a = __DIR__;
@@ -308,9 +309,11 @@ abstract class ClassName
{
$a = ($b ? $c : $d);
$a = (f1() ? f2() : f3());
+ $a = ($b ? $c : ($a ? 1 : 2));
+ $a = (f1() ? f2() : ($a ? 1 : 2));
($a = $b) xor $c;
- ($a = $b) and $c;
- ($a = $b) or $c;
+ ($a = $b) and $c; // RESULT: ($a = $b) && $c;
+ ($a = $b) or $c; // RESULT: ($a = $b) || $c;
$a = $b && $c;
$a = $b || $c;
}
@@ -359,8 +362,8 @@ abstract class ClassName
/** doc */
protected function protectedMethod(ClassName $a, $b = array(
- array('array')
- ))
+ array('array')
+ ))
{
$runtimeArray = array('1');
$runtimeArray2 = array(
@@ -617,6 +620,9 @@ function codeSwitch()
break;
}
+ switch (emptySwitch()) {
+ }
+
switch ('emptySwitch()') {
}
@@ -654,7 +660,6 @@ function codeKeyword()
#endif
#if PHP_VERSION >= 530
echo 'goto a';
-
goto a;
$i = 1;
diff --git a/lib/Decompiler.class.php b/lib/Decompiler.class.php
index 7f5c759..18f6777 100644
--- a/lib/Decompiler.class.php
+++ b/lib/Decompiler.class.php
@@ -13,87 +13,220 @@ function color($str, $color = 33)
return "\x1B[{$color}m$str\x1B[0m";
}
-function printBacktrace() // {{{
+class Decompiler_Output
{
- if (!$GLOBALS['__xcache_decompiler']->inComment++) {
- echo '/*', PHP_EOL;
+ // {{{
+ var $errorToOutput = true;
+ var $target;
+
+ function Decompiler_Output($target)
+ {
+ $this->target = $target;
}
- $backtrace = debug_backtrace();
- foreach ($backtrace as $stack) {
- $args = array();
- foreach ($stack['args'] as $arg) {
- if (is_scalar($arg)) {
- $args[] = var_export($arg, true);
- }
- else if (is_array($arg)) {
- $array = array();
- foreach ($arg as $key => $value) {
- $array[] = var_export($key, true) . " => " . (is_scalar($value) ? var_export($value, true) : gettype($value));
- if (count($array) >= 5) {
- $array[] = '...';
- break;
- }
- }
- $args[] = "array(" . implode(', ', $array) . ')';
+ // }}}
+
+ var $indent = "";
+ function indent() // {{{
+ {
+ $this->indent .= INDENT;
+ }
+ // }}}
+ function outdent() // {{{
+ {
+ $this->indent = substr($this->indent, 0, -strlen(INDENT));
+ }
+ // }}}
+
+ function writeOne_($code) // {{{
+ {
+ if ($this->target == STDOUT) {
+ echo $code;
+ }
+ else {
+ fwrite($this->target, $code);
+ }
+ }
+ // }}}
+ var $writes = 0;
+ function write() // {{{
+ {
+ ++$this->writes;
+ foreach (func_get_args() as $code) {
+ if (is_object($code)) {
+ $s = $code;
+ $code = $code->toCode($this->indent);
+ }
+ if (is_array($code)) {
+ call_user_func_array(array(&$this, 'write'), $code);
}
else {
- $args[] = gettype($arg);
+ $this->writeOne_((string) $code);
}
}
- printf("%d: %s::%s(%s)" . PHP_EOL
- , $stack['line']
- , isset($stack['class']) ? $stack['class'] : ''
- , $stack['function']
- , implode(', ', $args)
- );
}
- if (!--$GLOBALS['__xcache_decompiler']->inComment) {
- echo '*/', PHP_EOL;
+ // }}}
+ function writeln() // {{{
+ {
+ if ($this->codeTypeCurrent != $this->codeTypeNext) {
+ if ($this->codeTypeCurrent && $this->codeTypeNext == 'line') {
+ $this->writeOne_(PHP_EOL);
+ }
+ $this->codeTypeCurrent = $this->codeTypeNext;
+ }
+ $this->writeOne_($this->indent);
+
+ $args = func_get_args();
+ call_user_func_array(array(&$this, 'write'), $args);
+
+ $this->writeOne_(PHP_EOL);
+ }
+ // }}}
+ function printfError($format) // {{{
+ {
+ $args = func_get_args();
+ unset($args[0]);
+
+ foreach ($args as $i => $arg) {
+ unset($arg);
+
+ if (is_object($args[$i])) {
+ $args[$i] = $args[$i]->toCode($this->indent);
+ }
+ }
+
+ $error = implode("", $args);
+ trigger_error($error);
+ if ($this->errorToOutput) {
+ $this->beginComment();
+ fwrite($this->target, $error);
+ $this->endComment();
+ }
}
-}
-// }}}
+ // }}}
-function str($code, $indent = '') // {{{
-{
- if (is_array($code)) {
- $array = array();
- foreach ($code as $key => $value) {
- $array[$key] = str($value, $indent);
+ var $inComment = 0;
+ function beginComment() // {{{
+ {
+ if (!$this->inComment++) {
+ $this->writeln("/*");
}
- return $array;
}
- if (is_object($code)) {
- $code = foldToCode($code, $indent);
- return $code->toCode($indent);
+ // }}}
+ function endComment() // {{{
+ {
+ if (!--$this->inComment) {
+ $this->writeln("*/");
+ }
}
+ // }}}
- return (string) $code;
-}
-// }}}
+ var $scopeStack = array();
+ var $codeTypeCurrent = null;
+ var $codeTypeNext = 'line';
+ /*
+ line <- line
-function foldToCode($src, $indent = '') // {{{ wrap or rewrap anything to Decompiler_Code
-{
- if (is_array($indent)) {
- $indent = $indent['indent'];
- }
+ something { <- complexblock
+ line <- line
+ } <- complexblock
- $decompiler = &$GLOBALS['__xcache_decompiler'];
- if (!is_object($src)) {
- return new Decompiler_Code($decompiler, $src);
+ sdf <- line
+ */
+ function beginBlock_($isComplex) // {{{
+ {
+ // array_push($this->scopeStack, array($this->codeTypeCurrent, $this->codeTypeNext));
+ array_push($this->scopeStack, $this->codeTypeNext);
+ $this->codeTypeCurrent = null;
+ $this->codeTypeNext = $isComplex ? 'complexblock' : 'line';
+ }
+ // }}}
+ function endBlock_() // {{{
+ {
+ // list($this->codeTypeCurrent, $this->codeTypeNext) = array_pop($this->scopeStack);
+ $this->codeTypeNext = array_pop($this->scopeStack);
}
+ // }}}
+ function beginScope() // {{{
+ {
+ $this->beginBlock_(false);
+ $this->indent();
+ }
+ // }}}
+ function endScope() // {{{
+ {
+ $this->outdent();
+ $this->endBlock_();
+ }
+ // }}}
+ function beginComplexBlock() // {{{
+ {
+ if (isset($this->codeTypeCurrent)) {
+ $this->writeOne_(PHP_EOL);
+ }
- if (!method_exists($src, 'toCode')) {
- var_dump($src);
- exit('no toCode');
+ $this->beginBlock_(true);
}
- if (get_class($src) != 'Decompiler_Code') {
- // rewrap it
- $src = new Decompiler_Code($decompiler, $src->toCode($indent));
+ // }}}
+ function endComplexBlock() // {{{
+ {
+ $this->endBlock_();
+ }
+ // }}}
+}
+
+class Decompiler_Backtrace // {{{
+{
+ function Decompiler_Backtrace()
+ {
+ $this->backtrace = debug_backtrace();
+ array_shift($this->backtrace);
}
- return $src;
+ function toCode($indent)
+ {
+ $code = array();
+ foreach ($this->backtrace as $stack) {
+ $args = array();
+ foreach ($stack['args'] as $arg) {
+ if (is_scalar($arg)) {
+ $args[] = var_export($arg, true);
+ }
+ else if (is_array($arg)) {
+ $array = array();
+ foreach ($arg as $key => $value) {
+ $array[] = var_export($key, true) . " => " . (is_scalar($value) ? var_export($value, true) : gettype($value));
+ if (count($array) >= 5) {
+ $array[] = '...';
+ break;
+ }
+ }
+ $args[] = "array(" . implode(', ', $array) . ')';
+ }
+ else {
+ $args[] = gettype($arg);
+ }
+ }
+ $code[] = sprintf("%s%d: %s::%s(%s)" . PHP_EOL
+ , $indent
+ , $stack['line']
+ , isset($stack['class']) ? $stack['class'] : ''
+ , $stack['function']
+ , implode(', ', $args)
+ );
+ }
+ return implode("", $code);
+ }
}
// }}}
+function printBacktrace() // {{{
+{
+ $decompiler = &$GLOBALS['__xcache_decompiler'];
+ $decompiler->output->beginComment();
+ $decompiler->output->write(new Decompiler_Backtrace());
+ $decompiler->output->endComment();
+}
+// }}}
+
function decompileAst($ast) // {{{
{
$kind = $ast['kind'];
@@ -131,10 +264,10 @@ function decompileAst($ast) // {{{
);
case ZEND_UNARY_PLUS:
- return new Decompiler_Code($decompiler, '+' . str(decompileAst($ast[0])));
+ return new Decompiler_UnaryOp($decompiler, XC_ADD, decompileAst($ast[0]));
case ZEND_UNARY_MINUS:
- return new Decompiler_Code($decompiler, '-' . str(decompileAst($ast[0])));
+ return new Decompiler_UnaryOp($decompiler, XC_SUB, decompileAst($ast[0]));
default:
if (isset($decompiler->binaryOp[$kind])) {
@@ -161,7 +294,7 @@ function value($value) // {{{
if (isset($originalValue)) {
if ((xcache_get_type($value) & IS_CONSTANT_TYPE_MASK) == IS_CONSTANT) {
// constant
- return $decompiler->stripNamespace($originalValue);
+ return new Decompiler_Code($decompiler, $decompiler->stripNamespace($originalValue));
}
$value = $originalValue;
@@ -186,7 +319,7 @@ function value($value) // {{{
// }}}
function unquoteName_($str, $asVariableName, $indent = '') // {{{
{
- $str = str($str, $indent);
+ $str = is_object($str) ? $str->toCode($indent) : $str;
if (preg_match("!^'[\\w_][\\w\\d_\\\\]*'\$!", $str)) {
return str_replace('\\\\', '\\', substr($str, 1, -1));
}
@@ -210,6 +343,10 @@ function unquoteName($str, $indent = '') // {{{
// }}}
class Decompiler_Object // {{{
{
+ function toCode($indent)
+ {
+ return "";
+ }
}
// }}}
class Decompiler_Value extends Decompiler_Object // {{{
@@ -277,6 +414,23 @@ class Decompiler_Code extends Decompiler_Object // {{{
}
}
// }}}
+class Decompiler_Statements extends Decompiler_Code // {{{
+{
+ var $src;
+
+ function toCode($indent)
+ {
+ $code = array();
+ foreach ($this->src as $i => $src) {
+ if ($i) {
+ $code[] = ', ';
+ }
+ $code[] = $src;
+ }
+ return $code;
+ }
+}
+// }}}
class Decompiler_UnaryOp extends Decompiler_Code // {{{
{
var $opc;
@@ -294,12 +448,12 @@ class Decompiler_UnaryOp extends Decompiler_Code // {{{
$opstr = $this->decompiler->unaryOp[$this->opc];
if (is_a($this->op, 'Decompiler_TernaryOp') || is_a($this->op, 'Decompiler_BinaryOp') && $this->op->opc != $this->opc) {
- $op = "(" . str($this->op, $indent) . ")";
+ $op = array("(", $this->op, ")");
}
else {
$op = $this->op;
}
- return $opstr . str($op, $indent);
+ return array($opstr, $op);
}
}
// }}}
@@ -322,24 +476,25 @@ class Decompiler_BinaryOp extends Decompiler_Code // {{{
$opstr = $this->decompiler->binaryOp[$this->opc];
if (is_a($this->op1, 'Decompiler_TernaryOp') || is_a($this->op1, 'Decompiler_BinaryOp') && $this->op1->opc != $this->opc) {
- $op1 = "(" . str($this->op1, $indent) . ")";
+ $op1 = array("(", $this->op1, ")");
}
else {
$op1 = $this->op1;
}
if (is_a($this->op2, 'Decompiler_TernaryOp') || is_a($this->op2, 'Decompiler_BinaryOp') && $this->op2->opc != $this->opc && substr($opstr, -1) != '=') {
- $op2 = "(" . str($this->op2, $indent) . ")";
+ $op2 = array("(", $this->op2, ")");
}
else {
$op2 = $this->op2;
}
- if (str($op1) == '0' && ($this->opc == XC_ADD || $this->opc == XC_SUB)) {
- return $opstr . str($op2, $indent);
+ if (is_a($op1, 'Decompiler_Value') && $op1->value == '0' && ($this->opc == XC_ADD || $this->opc == XC_SUB)) {
+ $unaryOp = new Decompiler_UnaryOp($this->decompiler, $this->opc, $op2);
+ return $unaryOp->toCode($indent);
}
- return str($op1, $indent) . ' ' . $opstr . ($this->opc == XC_ASSIGN_REF ? '' : ' ') . str($op2, $indent);
+ return array($op1, ' ', $opstr, ($this->opc == XC_ASSIGN_REF ? '' : ' '), $op2);
}
}
// }}}
@@ -361,47 +516,58 @@ class Decompiler_TernaryOp extends Decompiler_Code // {{{
{
$trueValue = $this->trueValue;
if (is_a($this->trueValue, 'Decompiler_TernaryOp')) {
- $trueValue = "(" . str($trueValue, $indent) . ")";
+ $trueValue = array("(", $trueValue, ")");
}
$falseValue = $this->falseValue;
if (is_a($this->falseValue, 'Decompiler_TernaryOp')) {
- $falseValue = "(" . str($falseValue, $indent) . ")";
+ $falseValue = array("(", $falseValue, ")");
}
- return str($this->condition) . ' ? ' . str($trueValue) . ' : ' . str($falseValue);
+ return array($this->condition, ' ? ', $trueValue, ' : ', $falseValue);
}
}
// }}}
class Decompiler_Fetch extends Decompiler_Code // {{{
{
- var $src;
var $fetchType;
+ var $name;
+ var $scope;
- function Decompiler_Fetch(&$decompiler, $src, $type, $globalSrc)
+ function Decompiler_Fetch(&$decompiler, $scope, $name, $type)
{
$this->decompiler = &$decompiler;
- $this->src = $src;
+ $this->scope = $scope;
+ $this->name = $name;
+ unset($this->src);
$this->fetchType = $type;
- $this->globalSrc = $globalSrc;
}
function toCode($indent)
{
switch ($this->fetchType) {
+ case XC_FETCH_PROPERTY:
+ return array($this->scope, '->', $this->name);
+
+ case ZEND_FETCH_STATIC_MEMBER:
+ return array($this->scope, '::$', $this->name);
+
case ZEND_FETCH_LOCAL:
- return '$' . $this->src;
+ return array('$', $this->name);
+
case ZEND_FETCH_STATIC:
if (ZEND_ENGINE_2_3) {
// closure local variable?
- return 'STR' . str($this->src);
+ return 'STR' . $this->name;
}
else {
- return str(value($this->src));
+ return value($this->name);
}
die('static fetch cant to string');
+
case ZEND_FETCH_GLOBAL:
case ZEND_FETCH_GLOBAL_LOCK:
- return $this->globalSrc;
+ return xcache_is_autoglobal($this->name) ? array('$', $this->name) : array("\$GLOBALS[", value($this->name), "]");
+
default:
var_dump($this->fetchType);
assert(0);
@@ -409,7 +575,7 @@ class Decompiler_Fetch extends Decompiler_Code // {{{
}
}
// }}}
-class Decompiler_Box // {{{
+class Decompiler_Box extends Decompiler_Object // {{{
{
var $obj;
@@ -435,18 +601,21 @@ class Decompiler_Dim extends Decompiler_Value // {{{
function toCode($indent)
{
if (is_a($this->value, 'Decompiler_ListBox')) {
- $exp = str($this->value->obj->src, $indent);
+ $exp = array($this->value->obj->src);
}
else {
- $exp = str($this->value, $indent);
+ $exp = array($this->value);
}
$last = count($this->offsets) - 1;
foreach ($this->offsets as $i => $dim) {
if ($this->isObject && $i == $last) {
- $exp .= '->' . unquoteVariableName($dim, $indent);
+ $exp[] = '->';
+ $exp[] = unquoteVariableName($dim, $indent);
}
else {
- $exp .= '[' . str($dim, $indent) . ']';
+ $exp[] = '[';
+ $exp[] = $dim;
+ $exp[] = ']';
}
}
return $exp;
@@ -470,9 +639,9 @@ class Decompiler_List extends Decompiler_Code // {{{
unset($dim->value);
$dim->value = $this->src;
if (!isset($dim->assign)) {
- return str($dim, $indent);
+ return $dim;
}
- return str($this->dims[0]->assign, $indent) . ' = ' . str($dim, $indent);
+ return array($this->dims[0]->assign, ' = ', $dim);
}
/* flatten dims */
$assigns = array();
@@ -481,9 +650,9 @@ class Decompiler_List extends Decompiler_Code // {{{
foreach ($dim->offsets as $offset) {
$assign = &$assign[$offset];
}
- $assign = foldToCode($dim->assign, $indent);
+ $assign = $dim->assign;
}
- return str($this->toList($assigns)) . ' = ' . str($this->src, $indent);
+ return array($this->toList($assigns), ' = ', $this->src);
}
function toList($assigns)
@@ -493,22 +662,23 @@ class Decompiler_List extends Decompiler_Code // {{{
$keys[] = 0;
}
$max = call_user_func_array('max', $keys);
- $list = "list(";
+ $code = array("list(");
for ($i = 0; $i <= $max; $i++) {
if ($i) {
- $list .= ', ';
+ $code[] = ', ';
}
if (!isset($assigns[$i])) {
continue;
}
if (is_array($assigns[$i])) {
- $list .= $this->toList($assigns[$i]);
+ $code[] = $this->toList($assigns[$i]);
}
else {
- $list .= $assigns[$i];
+ $code[] = $assigns[$i];
}
}
- return $list . ')';
+ $code[] = ')';
+ return $code;
}
}
// }}}
@@ -532,29 +702,34 @@ class Decompiler_Array extends Decompiler_Value // {{{
$elementsCode = array();
$index = 0;
foreach ($this->value as $element) {
- list($key, $value, $ref) = $element;
- if (!isset($key)) {
- $key = $index++;
+ $key = $element[0];
+ if (isset($key)) {
+ $key = value($key);
+ $keyCode = $key->toCode($subindent);
}
- $elementsCode[] = array(str($key, $subindent), str($value, $subindent), $key, $value, $ref);
+ else {
+ $keyCode = $index++;
+ }
+ $element[0] = $keyCode;
+ $elementsCode[] = $element;
}
- $exp = "array(";
+ $code = array("array(");
$indent = $indent . INDENT;
$assocWidth = 0;
$multiline = 0;
$i = 0;
foreach ($elementsCode as $element) {
- list($keyCode, $valueCode) = $element;
- if ((string) $i !== $keyCode) {
+ $keyCode = $element[0];
+ if (is_array($keyCode) || (string) $i !== (string) $keyCode) {
$assocWidth = 1;
break;
}
++$i;
}
foreach ($elementsCode as $element) {
- list($keyCode, $valueCode, $key, $value) = $element;
- if ($assocWidth) {
+ list($keyCode, $value) = $element;
+ if ($assocWidth && !is_array($keyCode)) {
$len = strlen($keyCode);
if ($assocWidth < $len) {
$assocWidth = $len;
@@ -567,41 +742,43 @@ class Decompiler_Array extends Decompiler_Value // {{{
$i = 0;
foreach ($elementsCode as $element) {
- list($keyCode, $value, , , $ref) = $element;
+ list($keyCode, $value, $ref) = $element;
if ($multiline) {
if ($i) {
- $exp .= ",";
+ $code[] = ",";
}
- $exp .= PHP_EOL;
- $exp .= $indent;
+ $code[] = PHP_EOL;
+ $code[] = $indent;
}
else {
if ($i) {
- $exp .= ", ";
+ $code[] = ", ";
}
}
if ($assocWidth) {
- if ($multiline) {
- $exp .= sprintf("%-{$assocWidth}s => ", $keyCode);
+ if ($multiline && !is_array($keyCode)) {
+ $code[] = sprintf("%-{$assocWidth}s => ", $keyCode);
}
else {
- $exp .= $keyCode . ' => ';
+ $code[] = $keyCode;
+ $code[] = ' => ';
}
}
- $exp .= $ref;
- $exp .= $value;
+ $code[] = $ref;
+ $value = value($value);
+ $code[] = $value->toCode($subindent);
$i++;
}
if ($multiline) {
- $exp .= PHP_EOL . "$indent)";
+ $code[] = PHP_EOL . "$indent)";
}
else {
- $exp .= ")";
+ $code[] = ")";
}
- return $exp;
+ return $code;
}
}
// }}}
@@ -613,14 +790,14 @@ class Decompiler_ConstArray extends Decompiler_Array // {{{
$elements = array();
foreach ($array as $key => $value) {
if ((xcache_get_type($value) & IS_CONSTANT_INDEX)) {
- $keyCode = $GLOBALS['__xcache_decompiler']->stripNamespace(
+ $keyCode = new Decompiler_Code($this, $GLOBALS['__xcache_decompiler']->stripNamespace(
ZEND_ENGINE_2_3
? substr($key, 0, -2)
: $key
- );
+ ));
}
else {
- $keyCode = value($key);
+ $keyCode = $key;
}
$elements[] = array($keyCode, value($value), '');
}
@@ -650,13 +827,14 @@ class Decompiler
var $activeFunction;
var $outputPhp;
var $outputOpcode;
- var $inComment = 0;
var $value2constant = array();
+ var $output;
function Decompiler($outputTypes)
{
$this->outputPhp = in_array('php', $outputTypes);
$this->outputOpcode = in_array('opcode', $outputTypes);
+ $this->output = new Decompiler_Output(STDOUT);
$GLOBALS['__xcache_decompiler'] = $this;
// {{{ testing
// XC_UNDEF XC_OP_DATA
@@ -676,6 +854,11 @@ class Decompiler
$this->unaryOp = array(
XC_BW_NOT => '~',
XC_BOOL_NOT => '!',
+ XC_ADD => "+",
+ XC_SUB => "-",
+ XC_NEW => "new ",
+ XC_THROW => "throw ",
+ XC_CLONE => "clone ",
);
$this->binaryOp = array(
XC_ADD => "+",
@@ -710,11 +893,14 @@ class Decompiler
XC_ASSIGN_BW_XOR => "^=",
XC_BOOL_XOR => "xor",
XC_ASSIGN => "=",
+ XC_ASSIGN_DIM => "=",
+ XC_ASSIGN_OBJ => "=",
XC_ASSIGN_REF => "= &",
XC_JMP_SET => "?:",
XC_JMP_SET_VAR => "?:",
XC_JMPZ_EX => "&&",
XC_JMPNZ_EX => "||",
+ XC_INSTANCEOF => "instanceof",
);
if (defined('IS_CONSTANT_AST')) {
$this->binaryOp[ZEND_BOOL_AND] = '&&';
@@ -743,7 +929,9 @@ class Decompiler
}
$this->namespace = $namespace;
- echo 'namespace ', $this->namespace, ";", PHP_EOL, PHP_EOL;
+ $this->output->beginComplexBlock();
+ $this->output->writeln('namespace ', $this->namespace, ";");
+ $this->output->endComplexBlock();
}
$this->namespaceDecided = true;
@@ -755,8 +943,11 @@ class Decompiler
return $name;
}
- $name = str($name);
$len = strlen($this->namespace) + 1;
+ if (!is_string($name)) {
+ printBacktrace();
+ exit;
+ }
if (strncasecmp($name, $this->namespace . '\\', $len) == 0) {
return substr($name, $len);
}
@@ -777,59 +968,47 @@ class Decompiler
// }}}
function outputPhp($range) // {{{
{
- $needBlankline = isset($this->EX['lastBlock']);
- $indent = $this->EX['indent'];
$curticks = 0;
for ($i = $range[0]; $i <= $range[1]; $i++) {
$op = $this->EX['opcodes'][$i];
if (isset($op['gofrom'])) {
- if ($needBlankline) {
- $needBlankline = false;
- echo PHP_EOL;
- }
+ $this->output->beginComplexBlock();
echo 'label' . $i, ":", PHP_EOL;
+ $this->output->endComplexBlock();
}
if (isset($op['php'])) {
- $toticks = isset($op['ticks']) ? (int) str($op['ticks']) : 0;
+ $toticks = isset($op['ticks']) ? (int) $op['ticks'] : 0;
if ($curticks != $toticks) {
- $oldticks = $curticks;
- $curticks = $toticks;
- if (!$curticks) {
- echo $this->EX['indent'], "}", PHP_EOL, PHP_EOL;
- $indent = $this->EX['indent'];
+ if ($curticks) {
+ $this->output->endScope();
+ $this->output->writeln("}");
+ $this->output->endComplexBlock();
}
- else {
- if ($oldticks) {
- echo $this->EX['indent'], "}", PHP_EOL, PHP_EOL;
- }
- else if (!$oldticks) {
- $indent .= INDENT;
- }
- if ($needBlankline) {
- $needBlankline = false;
- echo PHP_EOL;
- }
- echo $this->EX['indent'], "declare (ticks=$curticks) {", PHP_EOL;
+ $curticks = $toticks;
+ if ($curticks) {
+ $this->output->beginComplexBlock();
+ $this->output->writeln("declare (ticks=$curticks) {");
+ $this->output->beginScope();
}
}
- if ($needBlankline) {
- $needBlankline = false;
- echo PHP_EOL;
- }
- echo $indent, str($op['php'], $indent), ";", PHP_EOL;
+ $this->output->writeln($op['php'], ';');
unset($op['php']);
- $this->EX['lastBlock'] = 'basic';
}
}
if ($curticks) {
- echo $this->EX['indent'], "}", PHP_EOL;
+ $this->output->endScope();
+ $this->output->writeln("}");
+ $this->output->endComplexBlock();
}
}
// }}}
- function getOpVal($op, $free = false) // {{{
+ function getOpVal($op, $free = false, $namespaced = false) // {{{
{
switch ($op['op_type']) {
case XC_IS_CONST:
+ if ($namespaced && isset($op['constant'])) {
+ return $this->stripNamespace($op['constant']);
+ }
return value($op['constant']);
case XC_IS_VAR:
@@ -850,7 +1029,7 @@ class Decompiler
case XC_IS_CV:
$vars = &$this->EX['op_array']['vars'];
$var = $op['var'];
- return isset($vars[$var]) ? '$' . $vars[$var]['name'] : '$?';
+ return new Decompiler_Fetch($this, null, isset($vars[$var]) ? $vars[$var]['name'] : '?', ZEND_FETCH_LOCAL);
case XC_IS_UNUSED:
return null;
@@ -957,33 +1136,6 @@ class Decompiler
unset($opcodes[$line]['jmptos']);
}
// }}}
- function beginScope($doIndent = true) // {{{
- {
- array_push($this->EX['scopeStack'], array($this->EX['lastBlock'], $this->EX['indent']));
- if ($doIndent) {
- $this->EX['indent'] .= INDENT;
- }
- $this->EX['lastBlock'] = null;
- }
- // }}}
- function endScope() // {{{
- {
- list($this->EX['lastBlock'], $this->EX['indent']) = array_pop($this->EX['scopeStack']);
- }
- // }}}
- function beginComplexBlock() // {{{
- {
- if (isset($this->EX['lastBlock'])) {
- echo PHP_EOL;
- $this->EX['lastBlock'] = null;
- }
- }
- // }}}
- function endComplexBlock() // {{{
- {
- $this->EX['lastBlock'] = 'complex';
- }
- // }}}
function op($range, $offset) // {{{
{
$opcodes = &$this->EX['opcodes'];
@@ -1121,39 +1273,39 @@ class Decompiler
$bodyRange = array($nextRange[1], $range[1]);
$this->removeJmpInfo($bodyRange[1]);
+ $this->output->beginComplexBlock();
$initial = '';
- $this->beginScope();
+ $this->output->beginScope();
$this->dasmBasicBlock($conditionRange);
$conditionCodes = array();
for ($i = $conditionRange[0]; $i <= $conditionRange[1]; ++$i) {
if (isset($opcodes[$i]['php'])) {
- $conditionCodes[] = str($opcodes[$i]['php'], $this->EX);
+ $conditionCodes[] = $opcodes[$i]['php'];
}
}
- $conditionCodes[] = str($this->getOpVal($opcodes[$conditionRange[1]]['op1']), $this->EX);
- if (implode(',', $conditionCodes) == 'true') {
+ $conditionCodes[] = $this->getOpVal($opcodes[$conditionRange[1]]['op1']);
+ if (count($conditionCodes) == 1 && $conditionCodes[0] == 'true') {
$conditionCodes = array();
}
- $this->endScope();
+ $this->output->endScope();
- $this->beginScope();
+ $this->output->beginScope();
$this->dasmBasicBlock($nextRange);
$nextCodes = array();
for ($i = $nextRange[0]; $i <= $nextRange[1]; ++$i) {
if (isset($opcodes[$i]['php'])) {
- $nextCodes[] = str($opcodes[$i]['php'], $this->EX);
+ $nextCodes[] = $opcodes[$i]['php'];
}
}
- $this->endScope();
+ $this->output->endScope();
- $this->beginComplexBlock();
- echo $this->EX['indent'], 'for (', str($initial, $this->EX), '; ', implode(', ', $conditionCodes), '; ', implode(', ', $nextCodes), ') ', '{', PHP_EOL;
+ $this->output->writeln('for (', $initial, '; ', new Decompiler_Statements($this, $conditionCodes), '; ', new Decompiler_Statements($this, $nextCodes), ') ', '{');
$this->clearJmpInfo_brk_cont($bodyRange);
- $this->beginScope();
+ $this->output->beginScope();
$this->recognizeAndDecompileClosedBlocks($bodyRange);
- $this->endScope();
- echo $this->EX['indent'], '}', PHP_EOL;
- $this->endComplexBlock();
+ $this->output->endScope();
+ $this->output->writeln('}');
+ $this->output->endComplexBlock();
return true;
}
}
@@ -1161,7 +1313,7 @@ class Decompiler
function decompile_if($range, &$opcodes, &$firstOp, &$lastOp) // {{{
{
if ($this->isIfCondition($range)) {
- $this->beginComplexBlock();
+ $this->output->beginComplexBlock();
$isElseIf = false;
do {
$ifRange = array($range[0], $opcodes[$range[0]]['jmptos'][0] - 1);
@@ -1169,12 +1321,11 @@ class Decompiler
$this->removeJmpInfo($ifRange[1]);
$condition = $this->getOpVal($opcodes[$ifRange[0]]['op1']);
- echo $this->EX['indent'], $isElseIf ? 'else if' : 'if', ' (', str($condition, $this->EX), ') ', '{', PHP_EOL;
- $this->beginScope();
+ $this->output->writeln($isElseIf ? 'else if' : 'if', ' (', $condition, ') ', '{');
+ $this->output->beginScope();
$this->recognizeAndDecompileClosedBlocks($ifRange);
- $this->endScope();
- $this->EX['lastBlock'] = null;
- echo $this->EX['indent'], '}', PHP_EOL;
+ $this->output->endScope();
+ $this->output->writeln("}");
$isElseIf = true;
// search for else if
@@ -1192,30 +1343,29 @@ class Decompiler
} while ($this->isIfCondition($range));
if ($ifRange[1] <= $range[1]) {
$elseRange = array($ifRange[1], $range[1]);
- echo $this->EX['indent'], 'else ', '{', PHP_EOL;
- $this->beginScope();
+ $this->output->writeln('else ', '{');
+ $this->output->beginScope();
$this->recognizeAndDecompileClosedBlocks($elseRange);
- $this->endScope();
- $this->EX['lastBlock'] = null;
- echo $this->EX['indent'], '}', PHP_EOL;
+ $this->output->endScope();
+ $this->output->writeln("}");
}
- $this->endComplexBlock();
+ $this->output->endComplexBlock();
return true;
}
if ($firstOp['opcode'] == XC_JMPZ && !empty($firstOp['jmptos'])
&& $firstOp['jmptos'][0] - 1 == $range[1]
&& ($opcodes[$firstOp['jmptos'][0] - 1]['opcode'] == XC_RETURN || $opcodes[$firstOp['jmptos'][0] - 1]['opcode'] == XC_GENERATOR_RETURN)) {
- $this->beginComplexBlock();
+ $this->output->beginComplexBlock();
$this->removeJmpInfo($range[0]);
$condition = $this->getOpVal($opcodes[$range[0]]['op1']);
- echo $this->EX['indent'], 'if (', str($condition, $this->EX), ') ', '{', PHP_EOL;
- $this->beginScope();
+ $this->output->writeln('if (', $condition, ') ', '{');
+ $this->output->beginScope();
$this->recognizeAndDecompileClosedBlocks($range);
- $this->endScope();
- echo $this->EX['indent'], '}', PHP_EOL;
- $this->endComplexBlock();
+ $this->output->endScope();
+ $this->output->writeln('}');
+ $this->output->endComplexBlock();
return true;
}
}
@@ -1253,12 +1403,12 @@ class Decompiler
--$tryRange[1];
}
- $this->beginComplexBlock();
- echo $this->EX['indent'], "try {", PHP_EOL;
- $this->beginScope();
+ $this->output->beginComplexBlock();
+ $this->output->writeln("try {");
+ $this->output->beginScope();
$this->recognizeAndDecompileClosedBlocks($tryRange);
- $this->endScope();
- echo $this->EX['indent'], '}', PHP_EOL;
+ $this->output->endScope();
+ $this->output->writeln("}");
if (!$catchBlocks) {
printBacktrace();
assert($catchBlocks);
@@ -1268,20 +1418,20 @@ class Decompiler
$catchBodyFirst = $catchOpLine + 1;
$this->dasmBasicBlock(array($catchFirst, $catchOpLine));
$catchOp = &$opcodes[$catchOpLine];
- echo $this->EX['indent'], "catch ("
- , $this->stripNamespace(isset($catchOp['op1']['constant']) ? $catchOp['op1']['constant'] : str($this->getOpVal($catchOp['op1'])))
+ $this->output->writeln("catch ("
+ , $this->stripNamespace(isset($catchOp['op1']['constant']) ? $catchOp['op1']['constant'] : $this->getOpVal($catchOp['op1']))
, ' '
- , isset($catchOp['op2']['constant']) ? '$' . $catchOp['op2']['constant'] : str($this->getOpVal($catchOp['op2']))
- , ") {", PHP_EOL;
+ , isset($catchOp['op2']['constant']) ? '$' . $catchOp['op2']['constant'] : $this->getOpVal($catchOp['op2'])
+ , ") {"
+ );
unset($catchOp);
- $this->EX['lastBlock'] = null;
- $this->beginScope();
+ $this->output->beginScope();
$this->recognizeAndDecompileClosedBlocks(array($catchBodyFirst, $catchBodyLast));
- $this->endScope();
- echo $this->EX['indent'], '}', PHP_EOL;
+ $this->output->endScope();
+ $this->output->writeln("}");
}
- $this->endComplexBlock();
+ $this->output->endComplexBlock();
return true;
}
}
@@ -1319,9 +1469,9 @@ class Decompiler
}
}
- $this->beginComplexBlock();
+ $this->output->beginComplexBlock();
- echo $this->EX['indent'], 'switch (', str($this->getOpVal($caseOp['op1'], true), $this->EX), ") {", PHP_EOL;
+ $this->output->writeln('switch (', $this->getOpVal($caseOp['op1'], true), ") {");
$caseIsOut = false;
$caseExpressionBegin = $range[0];
foreach ($cases as $caseFirst => $caseLast) {
@@ -1336,11 +1486,8 @@ class Decompiler
$caseOp = $opcodes[$caseFirst];
- echo $this->EX['indent'];
if ($caseOp['opcode'] == XC_CASE) {
- echo 'case ';
- echo str($this->getOpVal($caseOp['op2']), $this->EX);
- echo ':', PHP_EOL;
+ $this->output->writeln('case ', $this->getOpVal($caseOp['op2']), ':');
$this->removeJmpInfo($caseFirst);
++$caseFirst;
@@ -1350,8 +1497,7 @@ class Decompiler
++$caseFirst;
}
else {
- echo 'default';
- echo ':', PHP_EOL;
+ $this->output->writeln('default:');
assert('$opcodes[$caseFirst]["opcode"] == XC_JMP');
$this->removeJmpInfo($caseFirst);
@@ -1372,14 +1518,14 @@ class Decompiler
$lastCaseFall = true;
}
- $this->beginScope();
+ $this->output->beginScope();
$this->recognizeAndDecompileClosedBlocks(array($caseFirst, $caseLast));
- $this->endScope();
+ $this->output->endScope();
$caseIsOut = true;
}
- echo $this->EX['indent'], '}', PHP_EOL;
+ $this->output->writeln('}');
- $this->endComplexBlock();
+ $this->output->endComplexBlock();
return true;
}
}
@@ -1390,15 +1536,15 @@ class Decompiler
&& $lastOp['jmptos'][0] == $range[0]) {
$this->removeJmpInfo($range[1]);
$this->clearJmpInfo_brk_cont($range);
- $this->beginComplexBlock();
+ $this->output->beginComplexBlock();
- echo $this->EX['indent'], "do {", PHP_EOL;
- $this->beginScope();
+ $this->output->writeln("do {");
+ $this->output->beginScope();
$this->recognizeAndDecompileClosedBlocks($range);
- $this->endScope();
- echo $this->EX['indent'], "} while (", str($this->getOpVal($lastOp['op1']), $this->EX), ');', PHP_EOL;
+ $this->output->endScope();
+ $this->output->writeln("} while (", $this->getOpVal($lastOp['op1']), ');');
- $this->endComplexBlock();
+ $this->output->endComplexBlock();
return true;
}
}
@@ -1411,19 +1557,19 @@ class Decompiler
&& !empty($lastOp['jmptos']) && $lastOp['jmptos'][0] == $range[0]) {
$this->removeJmpInfo($firstJmpOp['line']);
$this->removeJmpInfo($range[1]);
- $this->beginComplexBlock();
+ $this->output->beginComplexBlock();
ob_start();
- $this->beginScope();
+ $this->output->beginScope();
$this->recognizeAndDecompileClosedBlocks($range);
- $this->endScope();
+ $this->output->endScope();
$body = ob_get_clean();
- echo $this->EX['indent'], "while (", str($this->getOpVal($firstJmpOp['op1'])), ") {", PHP_EOL;
+ $this->output->writeln("while (", $this->getOpVal($firstJmpOp['op1']), ") {");
echo $body;
- echo $this->EX['indent'], '}', PHP_EOL;
+ $this->output->writeln('}');
- $this->endComplexBlock();
+ $this->output->endComplexBlock();
return true;
}
}
@@ -1438,24 +1584,24 @@ class Decompiler
$this->removeJmpInfo($firstJmpOp['line']);
$this->removeJmpInfo($lastJmpOp['line']);
$this->clearJmpInfo_brk_cont($range);
- $this->beginComplexBlock();
+ $this->output->beginComplexBlock();
ob_start();
- $this->beginScope();
+ $this->output->beginScope();
$this->recognizeAndDecompileClosedBlocks($range);
- $this->endScope();
+ $this->output->endScope();
$body = ob_get_clean();
- $as = str(foldToCode($firstJmpOp['fe_as'], $this->EX), $this->EX);
+ $as = $firstJmpOp['fe_as'];
if (isset($firstJmpOp['fe_key'])) {
- $as = str($firstJmpOp['fe_key'], $this->EX) . ' => ' . $as;
+ $as = array($firstJmpOp['fe_key'], ' => ', $as);
}
- echo $this->EX['indent'], "foreach (", str($firstJmpOp['fe_src'], $this->EX), " as $as) {", PHP_EOL;
+ $this->output->writeln("foreach (", $firstJmpOp['fe_src'], " as ", $as, ") {");
echo $body;
- echo $this->EX['indent'], '}', PHP_EOL;
+ $this->output->writeln('}');
- $this->endComplexBlock();
+ $this->output->endComplexBlock();
return true;
}
}
@@ -1528,12 +1674,7 @@ class Decompiler
if ($blockFirst > $starti) {
$this->decompileBasicBlock(array($starti, $blockFirst - 1));
}
- if ($this->decompileComplexBlock(array($blockFirst, $blockLast)) === false) {
- if ($this->EX['lastBlock'] == 'complex') {
- echo PHP_EOL;
- }
- $this->EX['lastBlock'] = null;
- }
+ $this->decompileComplexBlock(array($blockFirst, $blockLast));
$starti = $blockLast + 1;
$i = $starti;
}
@@ -1711,16 +1852,15 @@ class Decompiler
unset($op);
}
// }}}
- function &dop_array($op_array, $indent = '') // {{{
+ function &dop_array($op_array, $isFunction = false) // {{{
{
- $this->fixOpCode($op_array['opcodes'], true, $indent == '' ? 1 : null);
+ $this->fixOpCode($op_array['opcodes'], true, $isFunction ? null : 1);
$opcodes = &$op_array['opcodes'];
$EX = array();
$this->EX = &$EX;
$EX['Ts'] = $this->outputPhp ? array() : null;
- $EX['indent'] = $indent;
$EX['op_array'] = &$op_array;
$EX['opcodes'] = &$opcodes;
// func call
@@ -1729,11 +1869,9 @@ class Decompiler
$EX['fbc'] = null;
$EX['argstack'] = array();
$EX['arg_types_stack'] = array();
- $EX['scopeStack'] = array();
$EX['silence'] = 0;
$EX['recvs'] = array();
$EX['uses'] = array();
- $EX['lastBlock'] = null;
$EX['value2constant'] = array();
if (isset($this->activeMethod)) {
$EX['value2constant'][$this->activeMethod] = '__METHOD__';
@@ -1810,22 +1948,14 @@ class Decompiler
array_push($this->EX['arg_types_stack'], array($this->EX['fbc'], $this->EX['object'], $this->EX['called_scope']));
$this->EX['object'] = $istmpres ? (int) $res['var'] : null;
$this->EX['called_scope'] = null;
- $this->EX['fbc'] = 'new ' . $this->stripNamespace(isset($op1['constant']) ? $op1['constant'] : $this->getOpVal($op1));
- break;
- // }}}
- case XC_THROW: // {{{
- $resvar = 'throw ' . str($this->getOpVal($op1), $this->EX);
- break;
- // }}}
- case XC_CLONE: // {{{
- $resvar = 'clone ' . str($this->getOpVal($op1), $this->EX);
+ $this->EX['fbc'] = new Decompiler_UnaryOp($this, $opc, $this->getOpVal($op1, false, true));
break;
// }}}
case XC_CATCH: // {{{
break;
// }}}
case XC_INSTANCEOF: // {{{
- $resvar = str($this->getOpVal($op1), $this->EX) . ' instanceof ' . $this->stripNamespace($this->getOpVal($op2));
+ $resvar = new Decompiler_BinaryOp($this, $this->getOpVal($op1), $opc, $this->stripNamespace($this->getOpVal($op2)));
break;
// }}}
case XC_FETCH_CLASS: // {{{
@@ -1844,7 +1974,7 @@ class Decompiler
$istmpres = true;
}
else {
- $class = isset($op2['constant']) ? $op2['constant'] : $this->getOpVal($op2);
+ $class = $this->getOpVal($op2, true, true);
}
$resvar = $class;
break;
@@ -1866,7 +1996,7 @@ class Decompiler
$resvar = $this->getOpVal($op1);
}
- $resvar = str($resvar) . '::' . unquoteName($this->getOpVal($op2));
+ $resvar = new Decompiler_Code($this, array($resvar, '::', unquoteName($this->getOpVal($op2))));
break;
// }}}
// {{{ case FETCH_*
@@ -1877,16 +2007,14 @@ class Decompiler
case XC_FETCH_UNSET:
case XC_FETCH_IS:
$fetchType = defined('ZEND_FETCH_TYPE_MASK') ? ($ext & ZEND_FETCH_TYPE_MASK) : $op2[!ZEND_ENGINE_2 ? 'fetch_type' : 'EA.type'];
- $name = isset($op1['constant']) ? $op1['constant'] : unquoteName($this->getOpVal($op1));
+ $name = isset($op1['constant']) ? $op1['constant'] : $this->getOpVal($op1);
if ($fetchType == ZEND_FETCH_STATIC_MEMBER) {
- $class = isset($op2['constant']) ? $op2['constant'] : $this->getOpVal($op2);
- $rvalue = $this->stripNamespace($class) . '::$' . $name;
+ $class = $this->getOpVal($op2, false, true);
}
else {
- $rvalue = isset($op1['constant']) ? $op1['constant'] : $this->getOpVal($op1);
- $globalName = xcache_is_autoglobal($name) ? "\$$name" : "\$GLOBALS[" . str($this->getOpVal($op1), $this->EX) . "]";
- $rvalue = new Decompiler_Fetch($this, $rvalue, $fetchType, $globalName);
+ $class = null;
}
+ $rvalue = new Decompiler_Fetch($this, $class, $name, $fetchType);
if ($res['op_type'] != XC_IS_UNUSED) {
$resvar = $rvalue;
@@ -1903,7 +2031,7 @@ class Decompiler
$rvalue = isset($op1['constant']) ? '$' . $op1['constant'] /* PHP5.1- */ : $this->getOpVal($op1);
}
- $op['php'] = "unset(" . str($rvalue, $this->EX) . ")";
+ $op['php'] = array("unset(", $rvalue, ")");
$lastphpop = &$op;
break;
// }}}
@@ -1978,10 +2106,10 @@ class Decompiler
$lvalue = $rvalue;
++ $i;
$rvalue = $this->getOpVal($opcodes[$i]['op1']);
- $resvar = str($lvalue, $this->EX) . ' = ' . str($rvalue);
+ $resvar = new Decompiler_BinaryOp($this, $lvalue, $opc, $rvalue);
}
else if ($opc == XC_UNSET_DIM || $opc == XC_UNSET_OBJ || $opc == XC_UNSET_DIM_OBJ) {
- $op['php'] = "unset(" . str($rvalue, $this->EX) . ")";
+ $op['php'] = array("unset(", $rvalue, ")");
$lastphpop = &$op;
}
else if ($res['op_type'] != XC_IS_UNUSED) {
@@ -2002,20 +2130,18 @@ class Decompiler
$dim = &$rvalue->obj;
$dim->assign = $lvalue;
if ($dim->isLast) {
- $resvar = foldToCode($dim->value, $this->EX);
+ $resvar = $dim->value;
}
unset($dim);
break;
}
- if (is_a($rvalue, 'Decompiler_Fetch')) {
- $src = str($rvalue->src, $this->EX);
- $name = unquoteName($src);
- if ('$' . $name == $lvalue) {
+ if (is_a($lvalue, 'Decompiler_Fetch') && is_a($rvalue, 'Decompiler_Fetch')) {
+ if ($lvalue->name == $rvalue->name) {
switch ($rvalue->fetchType) {
case ZEND_FETCH_STATIC:
$statics = &$this->EX['op_array']['static_variables'];
- if ((xcache_get_type($statics[$name]) & IS_LEXICAL_VAR)) {
- $this->EX['uses'][] = str($lvalue);
+ if ((xcache_get_type($statics[$rvalue->name]) & IS_LEXICAL_VAR)) {
+ $this->EX['uses'][] = $lvalue;
unset($statics);
break 2;
}
@@ -2029,29 +2155,30 @@ class Decompiler
case XC_ASSIGN_REF: // {{{
$lvalue = $this->getOpVal($op1);
$rvalue = $this->getOpVal($op2);
- if (is_a($rvalue, 'Decompiler_Fetch')) {
- $src = str($rvalue->src, $this->EX);
- if ('$' . unquoteName($src) == $lvalue) {
+ if (is_a($lvalue, 'Decompiler_Fetch') && is_a($rvalue, 'Decompiler_Fetch')) {
+ if ($lvalue->name == $rvalue->name) {
switch ($rvalue->fetchType) {
case ZEND_FETCH_GLOBAL:
case ZEND_FETCH_GLOBAL_LOCK:
- $resvar = 'global ' . $lvalue;
+ $resvar = new Decompiler_Code($this, array('global ', $lvalue));
break 2;
case ZEND_FETCH_STATIC:
$statics = &$this->EX['op_array']['static_variables'];
- $name = unquoteName($src);
- if ((xcache_get_type($statics[$name]) & IS_LEXICAL_REF)) {
- $this->EX['uses'][] = '&' . str($lvalue);
+ if ((xcache_get_type($statics[$rvalue->name]) & IS_LEXICAL_REF)) {
+ $this->EX['uses'][] = array('&', $lvalue);
unset($statics);
break 2;
}
- $resvar = 'static ' . $lvalue;
- if (isset($statics[$name])) {
- $var = $statics[$name];
- $resvar .= ' = ';
- $resvar .= str(value($var), $this->EX);
+ $resvar = array();
+ $resvar[] = 'static ';
+ $resvar[] = $lvalue;
+ if (isset($statics[$rvalue->name])) {
+ $var = $statics[$rvalue->name];
+ $resvar[] = ' = ';
+ $resvar[] = value($var);
}
+ $resvar = new Decompiler_Code($this, $resvar);
unset($statics);
break 2;
default:
@@ -2074,15 +2201,15 @@ class Decompiler
if (!isset($obj)) {
$obj = '$this';
}
- $rvalue = str($obj) . "->" . unquoteVariableName($this->getOpVal($op2));
+ $name = isset($op2['constant']) ? new Decompiler_Value($this, $op2['constant']) : $this->getOpVal($op2);
if ($res['op_type'] != XC_IS_UNUSED) {
- $resvar = $rvalue;
+ $resvar = new Decompiler_Fetch($this, $obj, $name->value, XC_FETCH_PROPERTY);
}
if ($opc == XC_ASSIGN_OBJ) {
++ $i;
$lvalue = $rvalue;
$rvalue = $this->getOpVal($opcodes[$i]['op1']);
- $resvar = "$lvalue = " . str($rvalue);
+ $resvar = new Decompiler_BinaryOp($this, $lvalue, $opc, $rvalue);
}
break;
// }}}
@@ -2112,22 +2239,22 @@ class Decompiler
if (!isset($container)) {
$container = '$this';
}
- $rvalue = str($container, $this->EX) . "->" . unquoteVariableName($dim);
+ $rvalue = array($container, "->", unquoteVariableName($dim));
}
else {
- $rvalue = str($container, $this->EX) . '[' . str($dim) .']';
+ $rvalue = array($container, '[', $dim, ']');
}
}
switch (((!ZEND_ENGINE_2 ? $op['op2']['var'] /* constant */ : $ext) & ZEND_ISSET_ISEMPTY_MASK)) {
case ZEND_ISSET:
- $rvalue = "isset(" . str($rvalue) . ")";
+ $rvalue = array("isset(", $rvalue, ")");
break;
case ZEND_ISEMPTY:
- $rvalue = "empty(" . str($rvalue) . ")";
+ $rvalue = array("empty(", $rvalue, ")");
break;
}
- $resvar = $rvalue;
+ $resvar = new Decompiler_Code($this, $rvalue);
break;
// }}}
case XC_SEND_VAR_NO_REF:
@@ -2135,7 +2262,7 @@ class Decompiler
case XC_SEND_REF:
case XC_SEND_VAR: // {{{
$ref = (!ZEND_ENGINE_2_4 && $opc == XC_SEND_REF ? '&' : '');
- $this->EX['argstack'][] = $ref . str($this->getOpVal($op1));
+ $this->EX['argstack'][] = array($ref, $this->getOpVal($op1));
break;
// }}}
case XC_INIT_STATIC_METHOD_CALL:
@@ -2143,7 +2270,7 @@ class Decompiler
array_push($this->EX['arg_types_stack'], array($this->EX['fbc'], $this->EX['object'], $this->EX['called_scope']));
if ($opc == XC_INIT_STATIC_METHOD_CALL) {
$this->EX['object'] = null;
- $this->EX['called_scope'] = $this->stripNamespace(isset($op1['constant']) ? $op1['constant'] : $this->getOpVal($op1));
+ $this->EX['called_scope'] = $this->getOpVal($op1, false, true);
}
else {
$obj = $this->getOpVal($op1);
@@ -2183,7 +2310,7 @@ class Decompiler
$this->EX['object'] = null;
$this->EX['called_scope'] = null;
}
- $this->EX['fbc'] = isset($op2['constant']) ? $op2['constant'] : $this->getOpVal($op2);
+ $this->EX['fbc'] = $this->getOpVal($op2, true, true);
break;
// }}}
case XC_INIT_FCALL_BY_FUNC: // {{{ deprecated even in PHP 4?
@@ -2197,12 +2324,12 @@ class Decompiler
$which = $op1['var'];
$fname = $this->EX['op_array']['funcs'][$which]['name'];
$args = $this->popargs($ext);
- $resvar = $fname . "($args)";
+ $resvar = new Decompiler_Code($this, array($fname, "(", $args, ")"));
break;
case XC_DO_FCALL:
$fname = unquoteName($this->getOpVal($op1), $this->EX);
$args = $this->popargs($ext);
- $resvar = $fname . "($args)";
+ $resvar = new Decompiler_Code($this, array($fname, "(", $args, ")"));
break;
case XC_DO_FCALL_BY_NAME: // {{{
$object = null;
@@ -2211,14 +2338,23 @@ class Decompiler
$object = $this->EX['object'];
}
- $args = $this->popargs($ext);
-
- $prefix = (isset($object) ? str($object) . '->' : '' )
- . (isset($this->EX['called_scope']) ? str($this->EX['called_scope']) . '::' : '');
- $resvar = $prefix
- . (!$prefix ? $this->stripNamespace($this->EX['fbc']) : str($this->EX['fbc']))
- . "($args)";
- unset($args);
+ $code = array();
+ if (isset($object)) {
+ $code[] = $object;
+ $code[] = '->';
+ }
+ if (isset($this->EX['called_scope'])) {
+ $code[] = $this->EX['called_scope'];
+ $code[] = '::';
+ }
+ if (isset($this->EX['fbc'])) {
+ $code[] = $this->EX['fbc'];
+ }
+ $code[] = '(';
+ $code[] = $this->popargs($ext);
+ $code[] = ')';
+ $resvar = new Decompiler_Code($this, $code);
+ unset($code);
if (is_int($this->EX['object'])) {
$T[$this->EX['object']] = $resvar;
@@ -2287,21 +2423,15 @@ class Decompiler
unset($fetchop, $addop);
$i += 2;
}
- if ($this->EX['lastBlock'] == 'complex') {
- echo PHP_EOL;
- }
- $this->EX['lastBlock'] = null;
$this->activeClass = $class['name'];
- $indent = $this->EX['indent'];
$oldEX = &$this->EX;
unset($this->EX);
- $this->dclass($class, $indent);
+ $this->dclass($class);
$this->EX = &$oldEX;
unset($oldEX);
$this->activeClass = null;
- echo PHP_EOL;
unset($class);
break;
// }}}
@@ -2316,38 +2446,38 @@ class Decompiler
$op2val = $this->getOpVal($op2);
switch ($opc) {
case XC_ADD_CHAR:
- $op2val = value(chr(str($op2val)));
+ $op2val = value(chr($op2val->value));
break;
case XC_ADD_STRING:
break;
case XC_ADD_VAR:
break;
}
- if (str($op1val) == "''") {
+ if (!isset($op1val) == "''") {
$rvalue = $op2val;
}
- else if (str($op2val) == "''") {
+ else if (!isset($op2val) == "''") {
$rvalue = $op1val;
}
else {
- $rvalue = str($op1val) . ' . ' . str($op2val);
+ $rvalue = new Decompiler_BinaryOp($this, $op1val, XC_CONCAT, $op2val);
}
$resvar = $rvalue;
// }}}
break;
case XC_PRINT: // {{{
$op1val = $this->getOpVal($op1);
- $resvar = "print(" . str($op1val) . ")";
+ $resvar = new Decompiler_Code($this, array("print(", $op1val, ")"));
break;
// }}}
case XC_ECHO: // {{{
$op1val = $this->getOpVal($op1);
- $resvar = "echo " . str($op1val);
+ $resvar = new Decompiler_Code($this, array("echo ", $op1val));
break;
// }}}
case XC_EXIT: // {{{
$op1val = $this->getOpVal($op1);
- $resvar = "exit(" . str($op1val) . ")";
+ $resvar = new Decompiler_Code($this, array("exit(", $op1val, ")"));
break;
// }}}
case XC_INIT_ARRAY:
@@ -2386,18 +2516,18 @@ class Decompiler
case XC_GENERATOR_RETURN:
case XC_RETURN_BY_REF:
case XC_RETURN: // {{{
- $resvar = "return " . str($this->getOpVal($op1, $this->EX));
+ $resvar = new Decompiler_Code($this, array("return ", $this->getOpVal($op1)));
break;
// }}}
case XC_INCLUDE_OR_EVAL: // {{{
$type = ZEND_ENGINE_2_4 ? $ext : $op2['var']; // hack
$keyword = $this->includeTypes[$type];
- $rvalue = str($this->getOpVal($op1));
+ $rvalue = $this->getOpVal($op1);
if ($type == ZEND_EVAL) {
- $resvar = "$keyword($rvalue)";
+ $resvar = new Decompiler_Code($this, array($keyword, "(", $rvalue, ")"));
}
else {
- $resvar = "$keyword $rvalue";
+ $resvar = new Decompiler_Code($this, array($keyword, " ", $rvalue));
}
break;
// }}}
@@ -2427,22 +2557,22 @@ class Decompiler
break;
// }}}
case XC_YIELD: // {{{
- $resvar = 'yield ' . str($this->getOpVal($op1));
+ $resvar = new Decompiler_Code($this, array('yield ', $this->getOpVal($op1)));
break;
// }}}
case XC_SWITCH_FREE: // {{{
if (isset($T[$op1['var']])) {
- $this->beginComplexBlock();
- echo $this->EX['indent'], 'switch (', str($this->getOpVal($op1)), ") {", PHP_EOL;
- echo $this->EX['indent'], '}', PHP_EOL;
- $this->endComplexBlock();
+ $this->output->beginComplexBlock();
+ $this->output->writeln('switch (', $this->getOpVal($op1), ") {");
+ $this->output->writeln('}');
+ $this->output->endComplexBlock();
}
break;
// }}}
case XC_FREE: // {{{
$free = $T[$op1['var']];
if (!is_a($free, 'Decompiler_Box')) {
- $op['php'] = is_object($free) ? $free : $this->unquote($free, '(', ')');
+ $op['php'] = is_object($free) || is_array($free) ? $free : $this->unquote($free, '(', ')');
$lastphpop = &$op;
}
unset($T[$op1['var']], $free);
@@ -2463,9 +2593,9 @@ class Decompiler
case XC_CONT:
case XC_BRK:
$resvar = $opc == XC_CONT ? 'continue' : 'break';
- $count = str($this->getOpVal($op2));
- if ($count != '1') {
- $resvar .= ' ' . $count;
+ $count = $this->getOpVal($op2);
+ if ($count->value != 1) {
+ $resvar .= ' ' . $count->value;
}
break;
case XC_GOTO:
@@ -2503,18 +2633,19 @@ class Decompiler
case XC_PRE_INC_OBJ: // {{{
$flags = array_flip(explode('_', $opname));
if (isset($flags['OBJ'])) {
- $resvar = str($this->getOpVal($op1)) . '->' . $op2['constant'];
+ $code = array($this->getOpVal($op1), '->', $op2['constant']);
}
else {
- $resvar = str($this->getOpVal($op1));
+ $code = array($this->getOpVal($op1));
}
$opstr = isset($flags['DEC']) ? '--' : '++';
if (isset($flags['POST'])) {
- $resvar .= $opstr;
+ $code[] = $opstr;
}
else {
- $resvar = "$opstr$resvar";
+ array_unshift($code, $opstr);
}
+ $resvar = new Decompiler_Code($this, $code);
break;
// }}}
@@ -2524,7 +2655,7 @@ class Decompiler
// }}}
case XC_END_SILENCE: // {{{
$this->EX['silence']--;
- $lastresvar = '@' . str($lastresvar, $this->EX);
+ $lastresvar = new Decompiler_Code($this, array('@', $lastresvar));
break;
// }}}
case XC_CAST: // {{{
@@ -2540,7 +2671,7 @@ class Decompiler
);
assert(isset($type2cast[$type]));
$cast = $type2cast[$type];
- $resvar = $cast . ' ' . str($this->getOpVal($op1));
+ $resvar = new Decompiler_Code($this, array($cast, ' ', $this->getOpVal($op1)));
break;
// }}}
case XC_EXT_STMT:
@@ -2554,10 +2685,9 @@ class Decompiler
// possible missing tailing \0 (outside of the string)
$key = substr($key . ".", 0, strlen($key));
- $indent = $this->EX['indent'];
$oldEX = &$this->EX;
unset($this->EX);
- $this->dfunction($this->dc['function_table'][$key], $indent);
+ $this->dfunction($this->dc['function_table'][$key]);
$this->EX = $oldEX;
unset($oldEX);
@@ -2568,10 +2698,9 @@ class Decompiler
// possible missing tailing \0 (outside of the string)
$key = substr($key . ".", 0, strlen($key));
- $indent = $this->EX['indent'];
$oldEX = &$this->EX;
unset($this->EX);
- $this->dfunction($this->dc['function_table'][$key], $indent);
+ $this->dfunction($this->dc['function_table'][$key]);
$this->EX = &$oldEX;
unset($oldEX);
@@ -2581,14 +2710,14 @@ class Decompiler
// }}}
case XC_DECLARE_CONST:
$name = $this->stripNamespace(unquoteName($this->getOpVal($op1), $this->EX));
- $value = str($this->getOpVal($op2));
- $resvar = 'const ' . $name . ' = ' . $value;
+ $value = $this->getOpVal($op2);
+ $resvar = new Decompiler_Code($this, array('const ', $name, ' = ', $value));
break;
case XC_DECLARE_FUNCTION_OR_CLASS:
/* always removed by compiler */
break;
case XC_TICKS:
- $lastphpop['ticks'] = ZEND_ENGINE_2_4 ? $ext : $this->getOpVal($op1);
+ $lastphpop['ticks'] = ZEND_ENGINE_2_4 ? $ext : ($op1 = $this->getOpVal($op1) ? $op1->value : 0);
// $this->EX['tickschanged'] = true;
break;
case XC_RAISE_ABSTRACT_ERROR:
@@ -2662,14 +2791,32 @@ class Decompiler
$args = array();
for ($i = 0; $i < $n; $i++) {
$a = array_pop($this->EX['argstack']);
- if (is_array($a)) {
- array_unshift($args, foldToCode($a, $this->EX));
- }
- else {
- array_unshift($args, $a);
+ array_unshift($args, $a);
+ }
+ return new Decompiler_Statements($this, $args);
+ }
+ // }}}
+ function opValToString__($code) // {{{
+ {
+ while (is_object($code)) {
+ $code = $code->toCode('');
+ }
+
+ if (is_array($code)) {
+ foreach ($code as $c) {
+ $this->opValToString__($c);
}
}
- return implode(', ', $args);
+ else {
+ echo $code;
+ }
+ }
+ // }}}
+ function opValToString_($op) // {{{
+ {
+ ob_start();
+ $this->opValToString__($this->getOpVal($op, null));
+ return ob_get_clean();
}
// }}}
function opToString($op, $which) // {{{
@@ -2681,30 +2828,30 @@ class Decompiler
case XC_IS_VAR:
$s = '$' . $op['var'];
if ($which != 'result' && isset($this->EX['Ts'])) {
- $s .= ':' . str($this->getOpVal($op, null));
+ $s .= ':' . $this->opValToString_($op);
}
return $s;
case XC_IS_TMP_VAR:
$s = '#' . $op['var'];
if ($which != 'result' && isset($this->EX['Ts'])) {
- $s .= ':' . str($this->getOpVal($op, null));
+ $s .= ':' . $this->opValToString_($op);
}
return $s;
case XC_IS_CONST:
- return isset($this->EX['Ts']) ? str($this->getOpVal($op, null)) : $op['var'] . ':' . var_export($op['constant'], true);
+ return isset($this->EX['Ts']) ? $this->opValToString_($op) : $op['var'] . ':' . var_export($op['constant'], true);
default:
- return isset($this->EX['Ts']) ? str($this->getOpVal($op, null)) : $op['op_type'] . '?' . $op['var'];
+ return isset($this->EX['Ts']) ? $this->opValToString_($op) : $op['op_type'] . '?' . $op['var'];
}
}
// }}}
function dumpOp($op, $padding = 4) // {{{
{
assert('isset($op)');
- echo str_pad($op['line'], $padding);
- echo str_pad($op['lineno'], $padding);
+ $this->output->write(str_pad($op['line'], $padding));
+ $this->output->write(str_pad($op['lineno'], $padding));
if (isset($op['oldopcode'])) {
$name = '//' . xcache_get_opcode($op['oldopcode']);
@@ -2716,48 +2863,44 @@ class Decompiler
if (substr($name, 0, 5) == 'ZEND_') {
$name = substr($name, 5);
}
- echo ' ', str_pad($name, 25);
+ $this->output->write(' ', str_pad($name, 25));
$types = array('result' => 9, 'op1' => 20, 'op2' => 20);
$res = $op['result'];
$resUsed = ((ZEND_ENGINE_2_4 ? ($res['op_type'] & EXT_TYPE_UNUSED) : ($res['EA.type'] & EXT_TYPE_UNUSED)) || $res['op_type'] == XC_IS_UNUSED) ? '' : '=';
foreach ($types as $which => $len) {
- echo ' ', str_pad($this->opToString($op[$which], $which) . ($which == 'result' ? $resUsed : ''), $len);
+ $this->output->write(' ', str_pad($this->opToString($op[$which], $which) . ($which == 'result' ? $resUsed : ''), $len));
}
- echo "\t;", $op['extended_value'];
+ $this->output->write("\t;", $op['extended_value']);
if (isset($op['isCatchBegin'])) {
- echo ' CB';
+ $this->output->write(' CB');
}
if (!empty($op['jmptos'])) {
- echo "\t>>", implode(',', $op['jmptos']);
+ $this->output->write("\t>>", implode(',', $op['jmptos']));
}
if (!empty($op['jmpfroms'])) {
- echo "\t<<", implode(',', $op['jmpfroms']);
+ $this->output->write("\t<<", implode(',', $op['jmpfroms']));
}
- echo PHP_EOL;
+ $this->output->write(PHP_EOL);
}
// }}}
function dumpRange($range, $ts = true) // {{{
{
- if (!$this->inComment++) {
- echo $this->EX['indent'], "/*", PHP_EOL;
- }
+ $this->output->beginComment();
if (!$ts) {
$Ts = $this->EX['Ts'];
$this->EX['Ts'] = null;
}
$padding = max(strlen($range[1]), strlen($this->EX['opcodes'][$range[1]]['lineno'])) + 1;
for ($i = $range[0]; $i <= $range[1]; ++$i) {
- echo $this->EX['indent'];
+ $this->output->write($this->output->indent);
$this->dumpOp($this->EX['opcodes'][$i], $padding);
}
if (!$ts) {
$this->EX['Ts'] = $Ts;
}
- if (!--$this->inComment) {
- echo $this->EX['indent'], "*/", PHP_EOL;
- }
+ $this->output->endComment();
}
// }}}
function dargs() // {{{
@@ -2776,33 +2919,34 @@ class Decompiler
}
$refrest = false;
+ $args = array();
for ($i = 0; $i < $c; $i++) {
- if ($i) {
- echo ', ';
- }
+ $arg = array();
$recv = isset($this->EX['recvs'][$i + 1]) ? $this->EX['recvs'][$i + 1] : null;
if (isset($op_array['arg_info'])) {
$ai = $op_array['arg_info'][$i];
if (isset($ai['type_hint']) ? ($ai['type_hint'] == IS_CALLABLE || $ai['type_hint'] == IS_OBJECT) : !empty($ai['class_name'])) {
- echo $this->stripNamespace($ai['class_name']), ' ';
+ $arg[] = $this->stripNamespace($ai['class_name']);
+ $arg[] = ' ';
if (!ZEND_ENGINE_2_2 && $ai['allow_null']) {
- echo 'or NULL ';
+ $arg[] = 'or NULL ';
}
}
else if (isset($ai['type_hint']) ? $ai['type_hint'] == IS_ARRAY : !empty($ai['array_type_hint'])) {
- echo 'array ';
+ $arg[] = 'array ';
if (!ZEND_ENGINE_2_2 && $ai['allow_null']) {
- echo 'or NULL ';
+ $arg[] = 'or NULL ';
}
}
if ($ai['pass_by_reference']) {
- echo '&';
+ $arg[] = '&';
}
- printf("\$%s", $ai['name']);
+ $arg[] = '$';
+ $arg[] = $ai['name'];
}
else {
if ($refrest) {
- echo '&';
+ $arg[] = '&';
}
else if (!empty($op_array['arg_types']) && isset($op_array['arg_types'][$i])) {
switch ($op_array['arg_types'][$i]) {
@@ -2810,7 +2954,7 @@ class Decompiler
$refrest = true;
/* fall */
case BYREF_FORCE:
- echo '&';
+ $arg[] = '&';
break;
case BYREF_NONE:
@@ -2820,27 +2964,45 @@ class Decompiler
assert(0);
}
}
- echo str($recv[0], $this->EX);
+ $arg[] = $recv[0];
}
if (isset($recv) && isset($recv[1])) {
- echo ' = ', str($recv[1], $this->EX);
+ $arg[] = ' = ';
+ $arg[] = $recv[1];
}
+ $args[] = $arg;
}
+ return new Decompiler_Statements($this, $args);
}
// }}}
function duses() // {{{
{
+ $code = array();
if ($this->EX['uses']) {
- echo " use(", implode(', ', $this->EX['uses']), ')';
+ $code[] = " use(";
+ $code[] = new Decompiler_Statements($this, $this->EX['uses']);
+ $code[] = ')';
}
+ return $code;
}
// }}}
- function dfunction($func, $indent = '', $decorations = array(), $nobody = false) // {{{
+ function dfunction($func, $decorations = array(), $nobody = false) // {{{
{
static $opcode_count = 0;
$opcode_count += count($func['op_array']['opcodes']);
- $this->detectNamespace($func['op_array']['function_name']);
+ $functionName = $this->stripNamespace($func['op_array']['function_name']);
+ $this->detectNamespace($functionName);
+
+ $isExpression = false;
+ if ($functionName == '{closure}') {
+ $functionName = '';
+ $isExpression = true;
+ }
+
+ if (!$nobody && !$isExpression) {
+ $this->output->beginComplexBlock();
+ }
$returnByRef = '';
if ($nobody) {
@@ -2851,7 +3013,9 @@ class Decompiler
}
else {
ob_start();
- $EX = &$this->dop_array($func['op_array'], $indent . INDENT);
+ $this->output->beginScope();
+ $EX = &$this->dop_array($func['op_array'], true);
+ $this->output->endScope();
$body = ob_get_clean();
$hasReturn = false;
$hasReturnByRef = false;
@@ -2867,46 +3031,55 @@ class Decompiler
}
}
if ($hasReturn && $hasReturnByRef) {
- echo $indent, "// WARN: both return and return-by-ref present", PHP_EOL;
+ $this->output->printfError("WARN: both return and return-by-ref present" . PHP_EOL);
}
if ($hasReturnByRef) {
$returnByRef = '&';
}
}
- $functionName = $this->stripNamespace($func['op_array']['function_name']);
- $isExpression = false;
- if ($functionName == '{closure}') {
- $functionName = '';
- $isExpression = true;
+ if (!empty($func['op_array']['doc_comment'])) {
+ $this->output->writeln($func['op_array']['doc_comment']);
}
- echo $isExpression ? '' : $indent;
+
+ $functionDeclare = array();
if ($decorations) {
- echo implode(' ', $decorations), ' ';
+ $functionDeclare[] = implode(' ', $decorations);
+ $functionDeclare[] = ' ';
}
$this->EX = &$EX;
unset($EX);
- echo 'function', $functionName ? ' ' . $returnByRef . $functionName : '', '(';
- $this->dargs();
- echo ")";
- $this->duses();
+ $functionDeclare[] = 'function';
+ $functionDeclare[] = $functionName ? ' ' . $returnByRef . $functionName : '';
+ $functionDeclare[] = '(';
+ $functionDeclare[] = $this->dargs();
+ $functionDeclare[] = ")";
+ $functionDeclare[] = $this->duses();
unset($this->EX);
if ($nobody) {
- echo ";", PHP_EOL;
+ $functionDeclare[] = ";";
+ $this->output->writeln($functionDeclare);
}
else {
if (!$isExpression) {
- echo PHP_EOL;
- echo $indent, "{", PHP_EOL;
+ $this->output->writeln($functionDeclare);
+ $this->output->writeln("{");
}
else {
- echo " {", PHP_EOL;
+ $this->output->write($functionDeclare);
+ $this->output->write(" {");
+ $this->output->write(PHP_EOL);
}
-
echo $body;
- echo "$indent}";
if (!$isExpression) {
- echo PHP_EOL;
+ $this->output->writeln("}");
+ }
+ else {
+ $this->output->write($this->output->indent);
+ $this->output->write("}");
+ }
+ if (!$isExpression) {
+ $this->output->endComplexBlock();
}
}
@@ -2918,17 +3091,12 @@ class Decompiler
}
}
// }}}
- function dclass($class, $indent = '') // {{{
+ function dclass($class) // {{{
{
$this->value2constant[$this->activeClass] = '__CLASS__';
$this->detectNamespace($class['name']);
// {{{ class decl
- if (!empty($class['doc_comment'])) {
- echo $indent;
- echo $class['doc_comment'];
- echo PHP_EOL;
- }
$isInterface = false;
$decorations = array();
if (!empty($class['ce_flags'])) {
@@ -2945,32 +3113,36 @@ class Decompiler
}
}
- echo $indent;
+ $this->output->beginComplexBlock();
+ if (!empty($class['doc_comment'])) {
+ $this->output->writeln($class['doc_comment']);
+ }
+ $classDeclare = array();
if ($decorations) {
- echo implode(' ', $decorations), ' ';
+ $classDeclare[] = implode(' ', $decorations) . ' ';
}
- echo $isInterface ? 'interface ' : 'class ', $this->stripNamespace($class['name']);
+ $classDeclare[] = $isInterface ? 'interface ' : 'class ';
+ $classDeclare[] = $this->stripNamespace($class['name']);
if ($class['parent']) {
- echo ' extends ', $this->stripNamespace($class['parent']);
+ $classDeclare[] = ' extends ';
+ $classDeclare[] = $this->stripNamespace($class['parent']);
}
/* TODO */
if (!empty($class['interfaces'])) {
- echo ' implements ';
- echo implode(', ', $class['interfaces']);
+ $classDeclare[] = ' implements ';
+ $classDeclare[] = implode(', ', $class['interfaces']);
}
- echo PHP_EOL;
- echo $indent, "{";
+ $this->output->writeln($classDeclare);
+ $this->output->writeln("{");
+ $this->output->beginScope();
// }}}
- $newindent = INDENT . $indent;
// {{{ const
if (!empty($class['constants_table'])) {
- echo PHP_EOL;
+ $this->output->beginComplexBlock();
foreach ($class['constants_table'] as $name => $v) {
- echo $newindent;
- echo 'const ', $name, ' = ';
- echo str(value($v), $newindent);
- echo ";", PHP_EOL;
+ $this->output->writeln('const ', $name, ' = ', value($v), ";");
}
+ $this->output->endComplexBlock();
}
// }}}
// {{{ properties
@@ -2979,30 +3151,28 @@ class Decompiler
}
$member_variables = $class[ZEND_ENGINE_2 ? 'properties_info' : 'default_properties'];
if ($member_variables) {
- echo PHP_EOL;
+ $this->output->beginComplexBlock();
foreach ($member_variables as $name => $dummy) {
$info = isset($class['properties_info']) ? $class['properties_info'][$name] : null;
if (isset($info) && !empty($info['doc_comment'])) {
- echo $newindent;
- echo $info['doc_comment'];
- echo PHP_EOL;
+ $this->output->writeln($info['doc_comment']);
}
- echo $newindent;
+ $variableDeclare = array();
if (ZEND_ENGINE_2) {
$static = ($info['flags'] & ZEND_ACC_STATIC);
if ($static) {
- echo "static ";
+ $variableDeclare[] = "static ";
}
}
$mangleSuffix = '';
if (!ZEND_ENGINE_2) {
- echo 'var ';
+ $variableDeclare[] = 'var ';
}
else if (!isset($info)) {
- echo 'public ';
+ $variableDeclare[] = 'public ';
}
else {
if ($info['flags'] & ZEND_ACC_SHADOW) {
@@ -3010,20 +3180,21 @@ class Decompiler
}
switch ($info['flags'] & ZEND_ACC_PPP_MASK) {
case ZEND_ACC_PUBLIC:
- echo "public ";
+ $variableDeclare[] = "public ";
break;
case ZEND_ACC_PRIVATE:
- echo "private ";
+ $variableDeclare[] = "private ";
$mangleSuffix = "\000";
break;
case ZEND_ACC_PROTECTED:
- echo "protected ";
+ $variableDeclare[] = "protected ";
$mangleSuffix = "\000";
break;
}
}
- echo '$', $name;
+ $variableDeclare[] = '$';
+ $variableDeclare[] = $name;
if (ZEND_ENGINE_2_4) {
$value = $class[$static ? 'default_static_members_table' : 'default_properties_table'][$info['offset']];
@@ -3040,12 +3211,18 @@ class Decompiler
$value = $class['default_properties'][$key];
}
}
- if (isset($value)) {
- echo ' = ';
- echo str(value($value), $newindent);
+ $value = value($value);
+ if (is_a($value, 'Decompiler_Value') && !isset($value->value)) {
+ // skip value;
+ }
+ else {
+ $variableDeclare[] = ' = ';
+ $variableDeclare[] = $value;
}
- echo ";", PHP_EOL;
+ $variableDeclare[] = ";";
+ $this->output->writeln($variableDeclare);
}
+ $this->output->endComplexBlock();
}
// }}}
// {{{ function_table
@@ -3053,13 +3230,7 @@ class Decompiler
foreach ($class['function_table'] as $func) {
if (!isset($func['scope']) || $func['scope'] == $class['name']) {
// TODO: skip shadow here
- echo PHP_EOL;
- $opa = $func['op_array'];
- if (!empty($opa['doc_comment'])) {
- echo $newindent;
- echo $opa['doc_comment'];
- echo PHP_EOL;
- }
+ $opa = &$func['op_array'];
$isAbstractMethod = false;
$decorations = array();
if (isset($opa['fn_flags'])) {
@@ -3091,7 +3262,7 @@ class Decompiler
}
$this->activeMethod = $this->activeClass . '::' . $opa['function_name'];
$this->activeFunction = $opa['function_name'];
- $this->dfunction($func, $newindent, $decorations, $isInterface || $isAbstractMethod);
+ $this->dfunction($func, $decorations, $isInterface || $isAbstractMethod);
$this->activeFunction = null;
$this->activeMethod = null;
if ($opa['function_name'] == 'Decompiler') {
@@ -3101,7 +3272,9 @@ class Decompiler
}
}
// }}}
- echo $indent, "}", PHP_EOL;
+ $this->output->endScope();
+ $this->output->writeln("}");
+ $this->output->endComplexBlock();
unset($this->value2constant[$this->activeClass]);
}
// }}}
@@ -3143,14 +3316,15 @@ class Decompiler
// }}}
function output() // {{{
{
- echo "<?". "php";
- echo PHP_EOL, PHP_EOL;
+ $this->output->beginComplexBlock();
+ $this->output->writeln("<" . "?php");
+ $this->output->endComplexBlock();
+
foreach ($this->dc['class_table'] as $key => $class) {
if ($key{0} != "\0") {
$this->activeClass = $class['name'];
$this->dclass($class);
$this->activeClass = null;
- echo PHP_EOL;
}
}
@@ -3159,12 +3333,13 @@ class Decompiler
$this->activeFunction = $key;
$this->dfunction($func);
$this->activeFunction = null;
- echo PHP_EOL;
}
}
$this->dop_array($this->dc['op_array']);
- echo PHP_EOL, "?" . ">", PHP_EOL;
+ $this->output->beginComplexBlock();
+ $this->output->writeln("?" . ">");
+ $this->output->endComplexBlock();
if (!empty($this->test)) {
$this->outputUnusedOp();
@@ -3248,6 +3423,7 @@ else {
define('ZEND_FETCH_STANDARD', 0);
define('ZEND_FETCH_ADD_LOCK', 1);
}
+define('XC_FETCH_PROPERTY', 10);
if (ZEND_ENGINE_2_4) {
define('ZEND_ISSET', 0x02000000);