Changeset 6f52434 in git


Ignore:
Timestamp:
2015-07-17T17:36:20Z (21 months ago)
Author:
Xuefer <xuefer@…>
Branches:
master, trunk
Parents:
0feb144
git-author:
Xuefer <xuefer@…> (07/17/2015 07:03:05 AM)
git-committer:
Xuefer <xuefer@…> (07/17/2015 05:36:20 PM)
Message:

Decompiler: separate/rewrite output code

Files:
2 edited

Legend:

Unmodified
Added
Removed
  • devel/sample.cpp.php

    r8f8ebf1 r6f52434  
    254254        $a = 'a' . 'b';
    255255        $a = 'a' . 'abc';
     256        $a = "a$abc d"; // RESULT: 'a' . $abc . ' d';
    256257        $a = __FILE__;
    257258#if PHP_VERSION >= 530
     
    309310        $a = ($b ? $c : $d);
    310311        $a = (f1() ? f2() : f3());
     312        $a = ($b ? $c : ($a ? 1 : 2));
     313        $a = (f1() ? f2() : ($a ? 1 : 2));
    311314        ($a = $b) xor $c;
    312         ($a = $b) and $c;
    313         ($a = $b) or $c;
     315        ($a = $b) and $c; // RESULT: ($a = $b) && $c;
     316        ($a = $b) or $c; // RESULT: ($a = $b) || $c;
    314317        $a = $b && $c;
    315318        $a = $b || $c;
     
    360363    /** doc */
    361364    protected function protectedMethod(ClassName $a, $b = array(
    362             array('array')
    363             ))
     365        array('array')
     366        ))
    364367    {
    365368        $runtimeArray = array('1');
     
    618621    }
    619622
     623    switch (emptySwitch()) {
     624    }
     625
    620626    switch ('emptySwitch()') {
    621627    }
     
    655661#if PHP_VERSION >= 530
    656662    echo 'goto a';
    657 
    658663    goto a;
    659664    $i = 1;
  • lib/Decompiler.class.php

    r0feb144 r6f52434  
    1414}
    1515
     16class Decompiler_Output
     17{
     18    // {{{
     19    var $errorToOutput = true;
     20    var $target;
     21
     22    function Decompiler_Output($target)
     23    {
     24        $this->target = $target;
     25    }
     26    // }}}
     27
     28    var $indent = "";
     29    function indent() // {{{
     30    {
     31        $this->indent .= INDENT;
     32    }
     33    // }}}
     34    function outdent() // {{{
     35    {
     36        $this->indent = substr($this->indent, 0, -strlen(INDENT));
     37    }
     38    // }}}
     39
     40    function writeOne_($code) // {{{
     41    {
     42        if ($this->target == STDOUT) {
     43            echo $code;
     44        }
     45        else {
     46            fwrite($this->target, $code);
     47        }
     48    }
     49    // }}}
     50    var $writes = 0;
     51    function write() // {{{
     52    {
     53        ++$this->writes;
     54        foreach (func_get_args() as $code) {
     55            if (is_object($code)) {
     56                $s = $code;
     57                $code = $code->toCode($this->indent);
     58            }
     59            if (is_array($code)) {
     60                call_user_func_array(array(&$this, 'write'), $code);
     61            }
     62            else {
     63                $this->writeOne_((string) $code);
     64            }
     65        }
     66    }
     67    // }}}
     68    function writeln() // {{{
     69    {
     70        if ($this->codeTypeCurrent != $this->codeTypeNext) {
     71            if ($this->codeTypeCurrent && $this->codeTypeNext == 'line') {
     72                $this->writeOne_(PHP_EOL);
     73            }
     74            $this->codeTypeCurrent = $this->codeTypeNext;
     75        }
     76        $this->writeOne_($this->indent);
     77
     78        $args = func_get_args();
     79        call_user_func_array(array(&$this, 'write'), $args);
     80
     81        $this->writeOne_(PHP_EOL);
     82    }
     83    // }}}
     84    function printfError($format) // {{{
     85    {
     86        $args = func_get_args();
     87        unset($args[0]);
     88
     89        foreach ($args as $i => $arg) {
     90            unset($arg);
     91
     92            if (is_object($args[$i])) {
     93                $args[$i] = $args[$i]->toCode($this->indent);
     94            }
     95        }
     96
     97        $error = implode("", $args);
     98        trigger_error($error);
     99        if ($this->errorToOutput) {
     100            $this->beginComment();
     101            fwrite($this->target, $error);
     102            $this->endComment();
     103        }
     104    }
     105    // }}}
     106
     107    var $inComment = 0;
     108    function beginComment() // {{{
     109    {
     110        if (!$this->inComment++) {
     111            $this->writeln("/*");
     112        }
     113    }
     114    // }}}
     115    function endComment() // {{{
     116    {
     117        if (!--$this->inComment) {
     118            $this->writeln("*/");
     119        }
     120    }
     121    // }}}
     122
     123    var $scopeStack = array();
     124    var $codeTypeCurrent = null;
     125    var $codeTypeNext = 'line';
     126    /*
     127    line  <- line
     128
     129    something { <- complexblock
     130        line <- line
     131    } <- complexblock
     132
     133    sdf <- line
     134    */
     135    function beginBlock_($isComplex) // {{{
     136    {
     137        // array_push($this->scopeStack, array($this->codeTypeCurrent, $this->codeTypeNext));
     138        array_push($this->scopeStack, $this->codeTypeNext);
     139        $this->codeTypeCurrent = null;
     140        $this->codeTypeNext = $isComplex ? 'complexblock' : 'line';
     141    }
     142    // }}}
     143    function endBlock_() // {{{
     144    {
     145        // list($this->codeTypeCurrent, $this->codeTypeNext) = array_pop($this->scopeStack);
     146        $this->codeTypeNext = array_pop($this->scopeStack);
     147    }
     148    // }}}
     149    function beginScope() // {{{
     150    {
     151        $this->beginBlock_(false);
     152        $this->indent();
     153    }
     154    // }}}
     155    function endScope() // {{{
     156    {
     157        $this->outdent();
     158        $this->endBlock_();
     159    }
     160    // }}}
     161    function beginComplexBlock() // {{{
     162    {
     163        if (isset($this->codeTypeCurrent)) {
     164            $this->writeOne_(PHP_EOL);
     165        }
     166
     167        $this->beginBlock_(true);
     168    }
     169    // }}}
     170    function endComplexBlock() // {{{
     171    {
     172        $this->endBlock_();
     173    }
     174    // }}}
     175}
     176
     177class Decompiler_Backtrace // {{{
     178{
     179    function Decompiler_Backtrace()
     180    {
     181        $this->backtrace = debug_backtrace();
     182        array_shift($this->backtrace);
     183    }
     184
     185    function toCode($indent)
     186    {
     187        $code = array();
     188        foreach ($this->backtrace as $stack) {
     189            $args = array();
     190            foreach ($stack['args'] as $arg) {
     191                if (is_scalar($arg)) {
     192                    $args[] = var_export($arg, true);
     193                }
     194                else if (is_array($arg)) {
     195                    $array = array();
     196                    foreach ($arg as $key => $value) {
     197                        $array[] = var_export($key, true) . " => " . (is_scalar($value) ? var_export($value, true) : gettype($value));
     198                        if (count($array) >= 5) {
     199                            $array[] = '...';
     200                            break;
     201                        }
     202                    }
     203                    $args[] = "array(" . implode(', ', $array) . ')';
     204                }
     205                else {
     206                    $args[] = gettype($arg);
     207                }
     208            }
     209            $code[] = sprintf("%s%d: %s::%s(%s)" . PHP_EOL
     210                    , $indent
     211                    , $stack['line']
     212                    , isset($stack['class']) ? $stack['class'] : ''
     213                    , $stack['function']
     214                    , implode(', ', $args)
     215                    );
     216        }
     217        return implode("", $code);
     218    }
     219}
     220// }}}
    16221function printBacktrace() // {{{
    17222{
    18     if (!$GLOBALS['__xcache_decompiler']->inComment++) {
    19         echo '/*', PHP_EOL;
    20     }
    21     $backtrace = debug_backtrace();
    22     foreach ($backtrace as $stack) {
    23         $args = array();
    24         foreach ($stack['args'] as $arg) {
    25             if (is_scalar($arg)) {
    26                 $args[] = var_export($arg, true);
    27             }
    28             else if (is_array($arg)) {
    29                 $array = array();
    30                 foreach ($arg as $key => $value) {
    31                     $array[] = var_export($key, true) . " => " . (is_scalar($value) ? var_export($value, true) : gettype($value));
    32                     if (count($array) >= 5) {
    33                         $array[] = '...';
    34                         break;
    35                     }
    36                 }
    37                 $args[] = "array(" . implode(', ', $array) . ')';
    38             }
    39             else {
    40                 $args[] = gettype($arg);
    41             }
    42         }
    43         printf("%d: %s::%s(%s)" . PHP_EOL
    44                 , $stack['line']
    45                 , isset($stack['class']) ? $stack['class'] : ''
    46                 , $stack['function']
    47                 , implode(', ', $args)
    48                 );
    49     }
    50     if (!--$GLOBALS['__xcache_decompiler']->inComment) {
    51         echo '*/', PHP_EOL;
    52     }
     223    $decompiler = &$GLOBALS['__xcache_decompiler'];
     224    $decompiler->output->beginComment();
     225    $decompiler->output->write(new Decompiler_Backtrace());
     226    $decompiler->output->endComment();
    53227}
    54228// }}}
    55229
    56 function str($code, $indent = '') // {{{
    57 {
    58     if (is_array($code)) {
    59         $array = array();
    60         foreach ($code as $key => $value) {
    61             $array[$key] = str($value, $indent);
    62         }
    63         return $array;
    64     }
    65     if (is_object($code)) {
    66         $code = foldToCode($code, $indent);
    67         return $code->toCode($indent);
    68     }
    69 
    70     return (string) $code;
    71 }
    72 // }}}
    73 
    74 function foldToCode($src, $indent = '') // {{{ wrap or rewrap anything to Decompiler_Code
    75 {
    76     if (is_array($indent)) {
    77         $indent = $indent['indent'];
    78     }
    79 
    80     $decompiler = &$GLOBALS['__xcache_decompiler'];
    81     if (!is_object($src)) {
    82         return new Decompiler_Code($decompiler, $src);
    83     }
    84 
    85     if (!method_exists($src, 'toCode')) {
    86         var_dump($src);
    87         exit('no toCode');
    88     }
    89     if (get_class($src) != 'Decompiler_Code') {
    90         // rewrap it
    91         $src = new Decompiler_Code($decompiler, $src->toCode($indent));
    92     }
    93 
    94     return $src;
    95 }
    96 // }}}
    97230function decompileAst($ast) // {{{
    98231{
     
    132265
    133266    case ZEND_UNARY_PLUS:
    134         return new Decompiler_Code($decompiler, '+' . str(decompileAst($ast[0])));
     267        return new Decompiler_UnaryOp($decompiler, XC_ADD, decompileAst($ast[0]));
    135268
    136269    case ZEND_UNARY_MINUS:
    137         return new Decompiler_Code($decompiler, '-' . str(decompileAst($ast[0])));
     270        return new Decompiler_UnaryOp($decompiler, XC_SUB, decompileAst($ast[0]));
    138271
    139272    default:
     
    162295        if ((xcache_get_type($value) & IS_CONSTANT_TYPE_MASK) == IS_CONSTANT) {
    163296            // constant
    164             return $decompiler->stripNamespace($originalValue);
     297            return new Decompiler_Code($decompiler, $decompiler->stripNamespace($originalValue));
    165298        }
    166299
     
    187320function unquoteName_($str, $asVariableName, $indent = '') // {{{
    188321{
    189     $str = str($str, $indent);
     322    $str = is_object($str) ? $str->toCode($indent) : $str;
    190323    if (preg_match("!^'[\\w_][\\w\\d_\\\\]*'\$!", $str)) {
    191324        return str_replace('\\\\', '\\', substr($str, 1, -1));
     
    211344class Decompiler_Object // {{{
    212345{
     346    function toCode($indent)
     347    {
     348        return "";
     349    }
    213350}
    214351// }}}
     
    278415}
    279416// }}}
     417class Decompiler_Statements extends Decompiler_Code // {{{
     418{
     419    var $src;
     420
     421    function toCode($indent)
     422    {
     423        $code = array();
     424        foreach ($this->src as $i => $src) {
     425            if ($i) {
     426                $code[] = ', ';
     427            }
     428            $code[] = $src;
     429        }
     430        return $code;
     431    }
     432}
     433// }}}
    280434class Decompiler_UnaryOp extends Decompiler_Code // {{{
    281435{
     
    295449
    296450        if (is_a($this->op, 'Decompiler_TernaryOp') || is_a($this->op, 'Decompiler_BinaryOp') && $this->op->opc != $this->opc) {
    297             $op = "(" . str($this->op, $indent) . ")";
     451            $op = array("(", $this->op, ")");
    298452        }
    299453        else {
    300454            $op = $this->op;
    301455        }
    302         return $opstr . str($op, $indent);
     456        return array($opstr, $op);
    303457    }
    304458}
     
    323477
    324478        if (is_a($this->op1, 'Decompiler_TernaryOp') || is_a($this->op1, 'Decompiler_BinaryOp') && $this->op1->opc != $this->opc) {
    325             $op1 = "(" . str($this->op1, $indent) . ")";
     479            $op1 = array("(", $this->op1, ")");
    326480        }
    327481        else {
     
    330484
    331485        if (is_a($this->op2, 'Decompiler_TernaryOp') || is_a($this->op2, 'Decompiler_BinaryOp') && $this->op2->opc != $this->opc && substr($opstr, -1) != '=') {
    332             $op2 = "(" . str($this->op2, $indent) . ")";
     486            $op2 = array("(", $this->op2, ")");
    333487        }
    334488        else {
     
    336490        }
    337491
    338         if (str($op1) == '0' && ($this->opc == XC_ADD || $this->opc == XC_SUB)) {
    339             return $opstr . str($op2, $indent);
    340         }
    341 
    342         return str($op1, $indent) . ' ' . $opstr . ($this->opc == XC_ASSIGN_REF ? '' : ' ') . str($op2, $indent);
     492        if (is_a($op1, 'Decompiler_Value') && $op1->value == '0' && ($this->opc == XC_ADD || $this->opc == XC_SUB)) {
     493            $unaryOp = new Decompiler_UnaryOp($this->decompiler, $this->opc, $op2);
     494            return $unaryOp->toCode($indent);
     495        }
     496
     497        return array($op1, ' ', $opstr, ($this->opc == XC_ASSIGN_REF ? '' : ' '), $op2);
    343498    }
    344499}
     
    362517        $trueValue = $this->trueValue;
    363518        if (is_a($this->trueValue, 'Decompiler_TernaryOp')) {
    364             $trueValue = "(" . str($trueValue, $indent) . ")";
     519            $trueValue = array("(", $trueValue, ")");
    365520        }
    366521        $falseValue = $this->falseValue;
    367522        if (is_a($this->falseValue, 'Decompiler_TernaryOp')) {
    368             $falseValue = "(" . str($falseValue, $indent) . ")";
    369         }
    370 
    371         return str($this->condition) . ' ? ' . str($trueValue) . ' : ' . str($falseValue);
     523            $falseValue = array("(", $falseValue, ")");
     524        }
     525
     526        return array($this->condition, ' ? ', $trueValue, ' : ', $falseValue);
    372527    }
    373528}
     
    375530class Decompiler_Fetch extends Decompiler_Code // {{{
    376531{
    377     var $src;
    378532    var $fetchType;
    379 
    380     function Decompiler_Fetch(&$decompiler, $src, $type, $globalSrc)
     533    var $name;
     534    var $scope;
     535
     536    function Decompiler_Fetch(&$decompiler, $scope, $name, $type)
    381537    {
    382538        $this->decompiler = &$decompiler;
    383         $this->src = $src;
     539        $this->scope = $scope;
     540        $this->name = $name;
     541        unset($this->src);
    384542        $this->fetchType = $type;
    385         $this->globalSrc = $globalSrc;
    386543    }
    387544
     
    389546    {
    390547        switch ($this->fetchType) {
     548        case XC_FETCH_PROPERTY:
     549            return array($this->scope, '->', $this->name);
     550
     551        case ZEND_FETCH_STATIC_MEMBER:
     552            return array($this->scope, '::$', $this->name);
     553
    391554        case ZEND_FETCH_LOCAL:
    392             return '$' . $this->src;
     555            return array('$', $this->name);
     556
    393557        case ZEND_FETCH_STATIC:
    394558            if (ZEND_ENGINE_2_3) {
    395559                // closure local variable?
    396                 return 'STR' . str($this->src);
     560                return 'STR' . $this->name;
    397561            }
    398562            else {
    399                 return str(value($this->src));
     563                return value($this->name);
    400564            }
    401565            die('static fetch cant to string');
     566
    402567        case ZEND_FETCH_GLOBAL:
    403568        case ZEND_FETCH_GLOBAL_LOCK:
    404             return $this->globalSrc;
     569            return xcache_is_autoglobal($this->name) ? array('$', $this->name) : array("\$GLOBALS[", value($this->name), "]");
     570
    405571        default:
    406572            var_dump($this->fetchType);
     
    410576}
    411577// }}}
    412 class Decompiler_Box // {{{
     578class Decompiler_Box extends Decompiler_Object // {{{
    413579{
    414580    var $obj;
     
    436602    {
    437603        if (is_a($this->value, 'Decompiler_ListBox')) {
    438             $exp = str($this->value->obj->src, $indent);
     604            $exp = array($this->value->obj->src);
    439605        }
    440606        else {
    441             $exp = str($this->value, $indent);
     607            $exp = array($this->value);
    442608        }
    443609        $last = count($this->offsets) - 1;
    444610        foreach ($this->offsets as $i => $dim) {
    445611            if ($this->isObject && $i == $last) {
    446                 $exp .= '->' . unquoteVariableName($dim, $indent);
     612                $exp[] = '->';
     613                $exp[] = unquoteVariableName($dim, $indent);
    447614            }
    448615            else {
    449                 $exp .= '[' . str($dim, $indent) . ']';
     616                $exp[] = '[';
     617                $exp[] = $dim;
     618                $exp[] = ']';
    450619            }
    451620        }
     
    471640            $dim->value = $this->src;
    472641            if (!isset($dim->assign)) {
    473                 return str($dim, $indent);
    474             }
    475             return str($this->dims[0]->assign, $indent) . ' = ' . str($dim, $indent);
     642                return $dim;
     643            }
     644            return array($this->dims[0]->assign, ' = ', $dim);
    476645        }
    477646        /* flatten dims */
     
    482651                $assign = &$assign[$offset];
    483652            }
    484             $assign = foldToCode($dim->assign, $indent);
    485         }
    486         return str($this->toList($assigns)) . ' = ' . str($this->src, $indent);
     653            $assign = $dim->assign;
     654        }
     655        return array($this->toList($assigns), ' = ', $this->src);
    487656    }
    488657
     
    494663        }
    495664        $max = call_user_func_array('max', $keys);
    496         $list = "list(";
     665        $code = array("list(");
    497666        for ($i = 0; $i <= $max; $i++) {
    498667            if ($i) {
    499                 $list .= ', ';
     668                $code[] = ', ';
    500669            }
    501670            if (!isset($assigns[$i])) {
     
    503672            }
    504673            if (is_array($assigns[$i])) {
    505                 $list .= $this->toList($assigns[$i]);
     674                $code[] = $this->toList($assigns[$i]);
    506675            }
    507676            else {
    508                 $list .= $assigns[$i];
    509             }
    510         }
    511         return $list . ')';
     677                $code[] = $assigns[$i];
     678            }
     679        }
     680        $code[] = ')';
     681        return $code;
    512682    }
    513683}
     
    533703        $index = 0;
    534704        foreach ($this->value as $element) {
    535             list($key, $value, $ref) = $element;
    536             if (!isset($key)) {
    537                 $key = $index++;
    538             }
    539             $elementsCode[] = array(str($key, $subindent), str($value, $subindent), $key, $value, $ref);
    540         }
    541 
    542         $exp = "array(";
     705            $key = $element[0];
     706            if (isset($key)) {
     707                $key = value($key);
     708                $keyCode = $key->toCode($subindent);
     709            }
     710            else {
     711                $keyCode = $index++;
     712            }
     713            $element[0] = $keyCode;
     714            $elementsCode[] = $element;
     715        }
     716
     717        $code = array("array(");
    543718        $indent = $indent . INDENT;
    544719        $assocWidth = 0;
     
    546721        $i = 0;
    547722        foreach ($elementsCode as $element) {
    548             list($keyCode, $valueCode) = $element;
    549             if ((string) $i !== $keyCode) {
     723            $keyCode = $element[0];
     724            if (is_array($keyCode) || (string) $i !== (string) $keyCode) {
    550725                $assocWidth = 1;
    551726                break;
     
    554729        }
    555730        foreach ($elementsCode as $element) {
    556             list($keyCode, $valueCode, $key, $value) = $element;
    557             if ($assocWidth) {
     731            list($keyCode, $value) = $element;
     732            if ($assocWidth && !is_array($keyCode)) {
    558733                $len = strlen($keyCode);
    559734                if ($assocWidth < $len) {
     
    568743        $i = 0;
    569744        foreach ($elementsCode as $element) {
    570             list($keyCode, $value, , , $ref) = $element;
     745            list($keyCode, $value, $ref) = $element;
    571746            if ($multiline) {
    572747                if ($i) {
    573                     $exp .= ",";
    574                 }
    575                 $exp .= PHP_EOL;
    576                 $exp .= $indent;
     748                    $code[] = ",";
     749                }
     750                $code[] = PHP_EOL;
     751                $code[] = $indent;
    577752            }
    578753            else {
    579754                if ($i) {
    580                     $exp .= ", ";
     755                    $code[] = ", ";
    581756                }
    582757            }
    583758
    584759            if ($assocWidth) {
    585                 if ($multiline) {
    586                     $exp .= sprintf("%-{$assocWidth}s => ", $keyCode);
     760                if ($multiline && !is_array($keyCode)) {
     761                    $code[] = sprintf("%-{$assocWidth}s => ", $keyCode);
    587762                }
    588763                else {
    589                     $exp .= $keyCode . ' => ';
    590                 }
    591             }
    592 
    593             $exp .= $ref;
    594             $exp .= $value;
     764                    $code[] = $keyCode;
     765                    $code[] = ' => ';
     766                }
     767            }
     768
     769            $code[] = $ref;
     770            $value = value($value);
     771            $code[] = $value->toCode($subindent);
    595772
    596773            $i++;
    597774        }
    598775        if ($multiline) {
    599             $exp .= PHP_EOL . "$indent)";
     776            $code[] = PHP_EOL . "$indent)";
    600777        }
    601778        else {
    602             $exp .= ")";
    603         }
    604         return $exp;
     779            $code[] = ")";
     780        }
     781        return $code;
    605782    }
    606783}
     
    614791        foreach ($array as $key => $value) {
    615792            if ((xcache_get_type($value) & IS_CONSTANT_INDEX)) {
    616                 $keyCode = $GLOBALS['__xcache_decompiler']->stripNamespace(
     793                $keyCode = new Decompiler_Code($this, $GLOBALS['__xcache_decompiler']->stripNamespace(
    617794                        ZEND_ENGINE_2_3
    618795                        ? substr($key, 0, -2)
    619796                        : $key
    620                         );
     797                        ));
    621798            }
    622799            else {
    623                 $keyCode = value($key);
     800                $keyCode = $key;
    624801            }
    625802            $elements[] = array($keyCode, value($value), '');
     
    651828    var $outputPhp;
    652829    var $outputOpcode;
    653     var $inComment = 0;
    654830    var $value2constant = array();
     831    var $output;
    655832
    656833    function Decompiler($outputTypes)
     
    658835        $this->outputPhp = in_array('php', $outputTypes);
    659836        $this->outputOpcode = in_array('opcode', $outputTypes);
     837        $this->output = new Decompiler_Output(STDOUT);
    660838        $GLOBALS['__xcache_decompiler'] = $this;
    661839        // {{{ testing
     
    677855                XC_BW_NOT   => '~',
    678856                XC_BOOL_NOT => '!',
     857                XC_ADD      => "+",
     858                XC_SUB      => "-",
     859                XC_NEW      => "new ",
     860                XC_THROW    => "throw ",
     861                XC_CLONE    => "clone ",
    679862                );
    680863        $this->binaryOp = array(
     
    711894                XC_BOOL_XOR            => "xor",
    712895                XC_ASSIGN              => "=",
     896                XC_ASSIGN_DIM          => "=",
     897                XC_ASSIGN_OBJ          => "=",
    713898                XC_ASSIGN_REF          => "= &",
    714899                XC_JMP_SET             => "?:",
     
    716901                XC_JMPZ_EX             => "&&",
    717902                XC_JMPNZ_EX            => "||",
     903                XC_INSTANCEOF          => "instanceof",
    718904                );
    719905        if (defined('IS_CONSTANT_AST')) {
     
    744930
    745931            $this->namespace = $namespace;
    746             echo 'namespace ', $this->namespace, ";", PHP_EOL, PHP_EOL;
     932            $this->output->beginComplexBlock();
     933            $this->output->writeln('namespace ', $this->namespace, ";");
     934            $this->output->endComplexBlock();
    747935        }
    748936
     
    756944        }
    757945
    758         $name = str($name);
    759946        $len = strlen($this->namespace) + 1;
     947        if (!is_string($name)) {
     948            printBacktrace();
     949            exit;
     950        }
    760951        if (strncasecmp($name, $this->namespace . '\\', $len) == 0) {
    761952            return substr($name, $len);
     
    778969    function outputPhp($range) // {{{
    779970    {
    780         $needBlankline = isset($this->EX['lastBlock']);
    781         $indent = $this->EX['indent'];
    782971        $curticks = 0;
    783972        for ($i = $range[0]; $i <= $range[1]; $i++) {
    784973            $op = $this->EX['opcodes'][$i];
    785974            if (isset($op['gofrom'])) {
    786                 if ($needBlankline) {
    787                     $needBlankline = false;
    788                     echo PHP_EOL;
    789                 }
     975                $this->output->beginComplexBlock();
    790976                echo 'label' . $i, ":", PHP_EOL;
     977                $this->output->endComplexBlock();
    791978            }
    792979            if (isset($op['php'])) {
    793                 $toticks = isset($op['ticks']) ? (int) str($op['ticks']) : 0;
     980                $toticks = isset($op['ticks']) ? (int) $op['ticks'] : 0;
    794981                if ($curticks != $toticks) {
    795                     $oldticks = $curticks;
     982                    if ($curticks) {
     983                        $this->output->endScope();
     984                        $this->output->writeln("}");
     985                        $this->output->endComplexBlock();
     986                    }
    796987                    $curticks = $toticks;
    797                     if (!$curticks) {
    798                         echo $this->EX['indent'], "}", PHP_EOL, PHP_EOL;
    799                         $indent = $this->EX['indent'];
    800                     }
    801                     else {
    802                         if ($oldticks) {
    803                             echo $this->EX['indent'], "}", PHP_EOL, PHP_EOL;
    804                         }
    805                         else if (!$oldticks) {
    806                             $indent .= INDENT;
    807                         }
    808                         if ($needBlankline) {
    809                             $needBlankline = false;
    810                             echo PHP_EOL;
    811                         }
    812                         echo $this->EX['indent'], "declare (ticks=$curticks) {", PHP_EOL;
    813                     }
    814                 }
    815                 if ($needBlankline) {
    816                     $needBlankline = false;
    817                     echo PHP_EOL;
    818                 }
    819                 echo $indent, str($op['php'], $indent), ";", PHP_EOL;
     988                    if ($curticks) {
     989                        $this->output->beginComplexBlock();
     990                        $this->output->writeln("declare (ticks=$curticks) {");
     991                        $this->output->beginScope();
     992                    }
     993                }
     994                $this->output->writeln($op['php'], ';');
    820995                unset($op['php']);
    821                 $this->EX['lastBlock'] = 'basic';
    822996            }
    823997        }
    824998        if ($curticks) {
    825             echo $this->EX['indent'], "}", PHP_EOL;
    826         }
    827     }
    828     // }}}
    829     function getOpVal($op, $free = false) // {{{
     999            $this->output->endScope();
     1000            $this->output->writeln("}");
     1001            $this->output->endComplexBlock();
     1002        }
     1003    }
     1004    // }}}
     1005    function getOpVal($op, $free = false, $namespaced = false) // {{{
    8301006    {
    8311007        switch ($op['op_type']) {
    8321008        case XC_IS_CONST:
     1009            if ($namespaced && isset($op['constant'])) {
     1010                return $this->stripNamespace($op['constant']);
     1011            }
    8331012            return value($op['constant']);
    8341013
     
    8511030            $vars = &$this->EX['op_array']['vars'];
    8521031            $var = $op['var'];
    853             return isset($vars[$var]) ? '$' . $vars[$var]['name'] : '$?';
     1032            return new Decompiler_Fetch($this, null, isset($vars[$var]) ? $vars[$var]['name'] : '?', ZEND_FETCH_LOCAL);
    8541033
    8551034        case XC_IS_UNUSED:
     
    9561135        // $opcodes[$line]['opcode'] = XC_NOP;
    9571136        unset($opcodes[$line]['jmptos']);
    958     }
    959     // }}}
    960     function beginScope($doIndent = true) // {{{
    961     {
    962         array_push($this->EX['scopeStack'], array($this->EX['lastBlock'], $this->EX['indent']));
    963         if ($doIndent) {
    964             $this->EX['indent'] .= INDENT;
    965         }
    966         $this->EX['lastBlock'] = null;
    967     }
    968     // }}}
    969     function endScope() // {{{
    970     {
    971         list($this->EX['lastBlock'], $this->EX['indent']) = array_pop($this->EX['scopeStack']);
    972     }
    973     // }}}
    974     function beginComplexBlock() // {{{
    975     {
    976         if (isset($this->EX['lastBlock'])) {
    977             echo PHP_EOL;
    978             $this->EX['lastBlock'] = null;
    979         }
    980     }
    981     // }}}
    982     function endComplexBlock() // {{{
    983     {
    984         $this->EX['lastBlock'] = 'complex';
    9851137    }
    9861138    // }}}
     
    11221274            $this->removeJmpInfo($bodyRange[1]);
    11231275
     1276            $this->output->beginComplexBlock();
    11241277            $initial = '';
    1125             $this->beginScope();
     1278            $this->output->beginScope();
    11261279            $this->dasmBasicBlock($conditionRange);
    11271280            $conditionCodes = array();
    11281281            for ($i = $conditionRange[0]; $i <= $conditionRange[1]; ++$i) {
    11291282                if (isset($opcodes[$i]['php'])) {
    1130                     $conditionCodes[] = str($opcodes[$i]['php'], $this->EX);
    1131                 }
    1132             }
    1133             $conditionCodes[] = str($this->getOpVal($opcodes[$conditionRange[1]]['op1']), $this->EX);
    1134             if (implode(',', $conditionCodes) == 'true') {
     1283                    $conditionCodes[] = $opcodes[$i]['php'];
     1284                }
     1285            }
     1286            $conditionCodes[] = $this->getOpVal($opcodes[$conditionRange[1]]['op1']);
     1287            if (count($conditionCodes) == 1 && $conditionCodes[0] == 'true') {
    11351288                $conditionCodes = array();
    11361289            }
    1137             $this->endScope();
    1138 
    1139             $this->beginScope();
     1290            $this->output->endScope();
     1291
     1292            $this->output->beginScope();
    11401293            $this->dasmBasicBlock($nextRange);
    11411294            $nextCodes = array();
    11421295            for ($i = $nextRange[0]; $i <= $nextRange[1]; ++$i) {
    11431296                if (isset($opcodes[$i]['php'])) {
    1144                     $nextCodes[] = str($opcodes[$i]['php'], $this->EX);
    1145                 }
    1146             }
    1147             $this->endScope();
    1148 
    1149             $this->beginComplexBlock();
    1150             echo $this->EX['indent'], 'for (', str($initial, $this->EX), '; ', implode(', ', $conditionCodes), '; ', implode(', ', $nextCodes), ') ', '{', PHP_EOL;
     1297                    $nextCodes[] = $opcodes[$i]['php'];
     1298                }
     1299            }
     1300            $this->output->endScope();
     1301
     1302            $this->output->writeln('for (', $initial, '; ', new Decompiler_Statements($this, $conditionCodes), '; ', new Decompiler_Statements($this, $nextCodes), ') ', '{');
    11511303            $this->clearJmpInfo_brk_cont($bodyRange);
    1152             $this->beginScope();
     1304            $this->output->beginScope();
    11531305            $this->recognizeAndDecompileClosedBlocks($bodyRange);
    1154             $this->endScope();
    1155             echo $this->EX['indent'], '}', PHP_EOL;
    1156             $this->endComplexBlock();
     1306            $this->output->endScope();
     1307            $this->output->writeln('}');
     1308            $this->output->endComplexBlock();
    11571309            return true;
    11581310        }
     
    11621314    {
    11631315        if ($this->isIfCondition($range)) {
    1164             $this->beginComplexBlock();
     1316            $this->output->beginComplexBlock();
    11651317            $isElseIf = false;
    11661318            do {
     
    11701322                $condition = $this->getOpVal($opcodes[$ifRange[0]]['op1']);
    11711323
    1172                 echo $this->EX['indent'], $isElseIf ? 'else if' : 'if', ' (', str($condition, $this->EX), ') ', '{', PHP_EOL;
    1173                 $this->beginScope();
     1324                $this->output->writeln($isElseIf ? 'else if' : 'if', ' (', $condition, ') ', '{');
     1325                $this->output->beginScope();
    11741326                $this->recognizeAndDecompileClosedBlocks($ifRange);
    1175                 $this->endScope();
    1176                 $this->EX['lastBlock'] = null;
    1177                 echo $this->EX['indent'], '}', PHP_EOL;
     1327                $this->output->endScope();
     1328                $this->output->writeln("}");
    11781329
    11791330                $isElseIf = true;
     
    11931344            if ($ifRange[1] <= $range[1]) {
    11941345                $elseRange = array($ifRange[1], $range[1]);
    1195                 echo $this->EX['indent'], 'else ', '{', PHP_EOL;
    1196                 $this->beginScope();
     1346                $this->output->writeln('else ', '{');
     1347                $this->output->beginScope();
    11971348                $this->recognizeAndDecompileClosedBlocks($elseRange);
    1198                 $this->endScope();
    1199                 $this->EX['lastBlock'] = null;
    1200                 echo $this->EX['indent'], '}', PHP_EOL;
    1201             }
    1202             $this->endComplexBlock();
     1349                $this->output->endScope();
     1350                $this->output->writeln("}");
     1351            }
     1352            $this->output->endComplexBlock();
    12031353            return true;
    12041354        }
     
    12071357         && $firstOp['jmptos'][0] - 1 == $range[1]
    12081358         && ($opcodes[$firstOp['jmptos'][0] - 1]['opcode'] == XC_RETURN || $opcodes[$firstOp['jmptos'][0] - 1]['opcode'] == XC_GENERATOR_RETURN)) {
    1209             $this->beginComplexBlock();
     1359            $this->output->beginComplexBlock();
    12101360            $this->removeJmpInfo($range[0]);
    12111361            $condition = $this->getOpVal($opcodes[$range[0]]['op1']);
    12121362
    1213             echo $this->EX['indent'], 'if (', str($condition, $this->EX), ') ', '{', PHP_EOL;
    1214             $this->beginScope();
     1363            $this->output->writeln('if (', $condition, ') ', '{');
     1364            $this->output->beginScope();
    12151365            $this->recognizeAndDecompileClosedBlocks($range);
    1216             $this->endScope();
    1217             echo $this->EX['indent'], '}', PHP_EOL;
    1218             $this->endComplexBlock();
     1366            $this->output->endScope();
     1367            $this->output->writeln('}');
     1368            $this->output->endComplexBlock();
    12191369            return true;
    12201370        }
     
    12541404            }
    12551405
    1256             $this->beginComplexBlock();
    1257             echo $this->EX['indent'], "try {", PHP_EOL;
    1258             $this->beginScope();
     1406            $this->output->beginComplexBlock();
     1407            $this->output->writeln("try {");
     1408            $this->output->beginScope();
    12591409            $this->recognizeAndDecompileClosedBlocks($tryRange);
    1260             $this->endScope();
    1261             echo $this->EX['indent'], '}', PHP_EOL;
     1410            $this->output->endScope();
     1411            $this->output->writeln("}");
    12621412            if (!$catchBlocks) {
    12631413                printBacktrace();
     
    12691419                $this->dasmBasicBlock(array($catchFirst, $catchOpLine));
    12701420                $catchOp = &$opcodes[$catchOpLine];
    1271                 echo $this->EX['indent'], "catch ("
    1272                         , $this->stripNamespace(isset($catchOp['op1']['constant']) ? $catchOp['op1']['constant'] : str($this->getOpVal($catchOp['op1'])))
     1421                $this->output->writeln("catch ("
     1422                        , $this->stripNamespace(isset($catchOp['op1']['constant']) ? $catchOp['op1']['constant'] : $this->getOpVal($catchOp['op1']))
    12731423                        , ' '
    1274                         , isset($catchOp['op2']['constant']) ? '$' . $catchOp['op2']['constant'] : str($this->getOpVal($catchOp['op2']))
    1275                         , ") {", PHP_EOL;
     1424                        , isset($catchOp['op2']['constant']) ? '$' . $catchOp['op2']['constant'] : $this->getOpVal($catchOp['op2'])
     1425                        , ") {"
     1426                        );
    12761427                unset($catchOp);
    12771428
    1278                 $this->EX['lastBlock'] = null;
    1279                 $this->beginScope();
     1429                $this->output->beginScope();
    12801430                $this->recognizeAndDecompileClosedBlocks(array($catchBodyFirst, $catchBodyLast));
    1281                 $this->endScope();
    1282                 echo $this->EX['indent'], '}', PHP_EOL;
    1283             }
    1284             $this->endComplexBlock();
     1431                $this->output->endScope();
     1432                $this->output->writeln("}");
     1433            }
     1434            $this->output->endComplexBlock();
    12851435            return true;
    12861436        }
     
    13201470            }
    13211471
    1322             $this->beginComplexBlock();
    1323 
    1324             echo $this->EX['indent'], 'switch (', str($this->getOpVal($caseOp['op1'], true), $this->EX), ") {", PHP_EOL;
     1472            $this->output->beginComplexBlock();
     1473
     1474            $this->output->writeln('switch (', $this->getOpVal($caseOp['op1'], true), ") {");
    13251475            $caseIsOut = false;
    13261476            $caseExpressionBegin = $range[0];
     
    13371487                $caseOp = $opcodes[$caseFirst];
    13381488
    1339                 echo $this->EX['indent'];
    13401489                if ($caseOp['opcode'] == XC_CASE) {
    1341                     echo 'case ';
    1342                     echo str($this->getOpVal($caseOp['op2']), $this->EX);
    1343                     echo ':', PHP_EOL;
     1490                    $this->output->writeln('case ', $this->getOpVal($caseOp['op2']), ':');
    13441491
    13451492                    $this->removeJmpInfo($caseFirst);
     
    13511498                }
    13521499                else {
    1353                     echo 'default';
    1354                     echo ':', PHP_EOL;
     1500                    $this->output->writeln('default:');
    13551501
    13561502                    assert('$opcodes[$caseFirst]["opcode"] == XC_JMP');
     
    13731519                }
    13741520
    1375                 $this->beginScope();
     1521                $this->output->beginScope();
    13761522                $this->recognizeAndDecompileClosedBlocks(array($caseFirst, $caseLast));
    1377                 $this->endScope();
     1523                $this->output->endScope();
    13781524                $caseIsOut = true;
    13791525            }
    1380             echo $this->EX['indent'], '}', PHP_EOL;
    1381 
    1382             $this->endComplexBlock();
     1526            $this->output->writeln('}');
     1527
     1528            $this->output->endComplexBlock();
    13831529            return true;
    13841530        }
     
    13911537            $this->removeJmpInfo($range[1]);
    13921538            $this->clearJmpInfo_brk_cont($range);
    1393             $this->beginComplexBlock();
    1394 
    1395             echo $this->EX['indent'], "do {", PHP_EOL;
    1396             $this->beginScope();
     1539            $this->output->beginComplexBlock();
     1540
     1541            $this->output->writeln("do {");
     1542            $this->output->beginScope();
    13971543            $this->recognizeAndDecompileClosedBlocks($range);
    1398             $this->endScope();
    1399             echo $this->EX['indent'], "} while (", str($this->getOpVal($lastOp['op1']), $this->EX), ');', PHP_EOL;
    1400 
    1401             $this->endComplexBlock();
     1544            $this->output->endScope();
     1545            $this->output->writeln("} while (", $this->getOpVal($lastOp['op1']), ');');
     1546
     1547            $this->output->endComplexBlock();
    14021548            return true;
    14031549        }
     
    14121558            $this->removeJmpInfo($firstJmpOp['line']);
    14131559            $this->removeJmpInfo($range[1]);
    1414             $this->beginComplexBlock();
     1560            $this->output->beginComplexBlock();
    14151561
    14161562            ob_start();
    1417             $this->beginScope();
     1563            $this->output->beginScope();
    14181564            $this->recognizeAndDecompileClosedBlocks($range);
    1419             $this->endScope();
     1565            $this->output->endScope();
    14201566            $body = ob_get_clean();
    14211567
    1422             echo $this->EX['indent'], "while (", str($this->getOpVal($firstJmpOp['op1'])), ") {", PHP_EOL;
     1568            $this->output->writeln("while (", $this->getOpVal($firstJmpOp['op1']), ") {");
    14231569            echo $body;
    1424             echo $this->EX['indent'], '}', PHP_EOL;
    1425 
    1426             $this->endComplexBlock();
     1570            $this->output->writeln('}');
     1571
     1572            $this->output->endComplexBlock();
    14271573            return true;
    14281574        }
     
    14391585            $this->removeJmpInfo($lastJmpOp['line']);
    14401586            $this->clearJmpInfo_brk_cont($range);
    1441             $this->beginComplexBlock();
     1587            $this->output->beginComplexBlock();
    14421588
    14431589            ob_start();
    1444             $this->beginScope();
     1590            $this->output->beginScope();
    14451591            $this->recognizeAndDecompileClosedBlocks($range);
    1446             $this->endScope();
     1592            $this->output->endScope();
    14471593            $body = ob_get_clean();
    14481594
    1449             $as = str(foldToCode($firstJmpOp['fe_as'], $this->EX), $this->EX);
     1595            $as = $firstJmpOp['fe_as'];
    14501596            if (isset($firstJmpOp['fe_key'])) {
    1451                 $as = str($firstJmpOp['fe_key'], $this->EX) . ' => ' . $as;
    1452             }
    1453 
    1454             echo $this->EX['indent'], "foreach (", str($firstJmpOp['fe_src'], $this->EX), " as $as) {", PHP_EOL;
     1597                $as = array($firstJmpOp['fe_key'], ' => ', $as);
     1598            }
     1599
     1600            $this->output->writeln("foreach (", $firstJmpOp['fe_src'], " as ", $as, ") {");
    14551601            echo $body;
    1456             echo $this->EX['indent'], '}', PHP_EOL;
    1457 
    1458             $this->endComplexBlock();
     1602            $this->output->writeln('}');
     1603
     1604            $this->output->endComplexBlock();
    14591605            return true;
    14601606        }
     
    15291675                        $this->decompileBasicBlock(array($starti, $blockFirst - 1));
    15301676                    }
    1531                     if ($this->decompileComplexBlock(array($blockFirst, $blockLast)) === false) {
    1532                         if ($this->EX['lastBlock'] == 'complex') {
    1533                             echo PHP_EOL;
    1534                         }
    1535                         $this->EX['lastBlock'] = null;
    1536                     }
     1677                    $this->decompileComplexBlock(array($blockFirst, $blockLast));
    15371678                    $starti = $blockLast + 1;
    15381679                    $i = $starti;
     
    17121853    }
    17131854    // }}}
    1714     function &dop_array($op_array, $indent = '') // {{{
    1715     {
    1716         $this->fixOpCode($op_array['opcodes'], true, $indent == '' ? 1 : null);
     1855    function &dop_array($op_array, $isFunction = false) // {{{
     1856    {
     1857        $this->fixOpCode($op_array['opcodes'], true, $isFunction ? null : 1);
    17171858
    17181859        $opcodes = &$op_array['opcodes'];
     
    17211862        $this->EX = &$EX;
    17221863        $EX['Ts'] = $this->outputPhp ? array() : null;
    1723         $EX['indent'] = $indent;
    17241864        $EX['op_array'] = &$op_array;
    17251865        $EX['opcodes'] = &$opcodes;
     
    17301870        $EX['argstack'] = array();
    17311871        $EX['arg_types_stack'] = array();
    1732         $EX['scopeStack'] = array();
    17331872        $EX['silence'] = 0;
    17341873        $EX['recvs'] = array();
    17351874        $EX['uses'] = array();
    1736         $EX['lastBlock'] = null;
    17371875        $EX['value2constant'] = array();
    17381876        if (isset($this->activeMethod)) {
     
    18111949                $this->EX['object'] = $istmpres ? (int) $res['var'] : null;
    18121950                $this->EX['called_scope'] = null;
    1813                 $this->EX['fbc'] = 'new ' . $this->stripNamespace(isset($op1['constant']) ? $op1['constant'] : $this->getOpVal($op1));
    1814                 break;
    1815                 // }}}
    1816             case XC_THROW: // {{{
    1817                 $resvar = 'throw ' . str($this->getOpVal($op1), $this->EX);
    1818                 break;
    1819                 // }}}
    1820             case XC_CLONE: // {{{
    1821                 $resvar = 'clone ' . str($this->getOpVal($op1), $this->EX);
     1951                $this->EX['fbc'] = new Decompiler_UnaryOp($this, $opc, $this->getOpVal($op1, false, true));
    18221952                break;
    18231953                // }}}
     
    18261956                // }}}
    18271957            case XC_INSTANCEOF: // {{{
    1828                 $resvar = str($this->getOpVal($op1), $this->EX) . ' instanceof ' . $this->stripNamespace($this->getOpVal($op2));
     1958                $resvar = new Decompiler_BinaryOp($this, $this->getOpVal($op1), $opc, $this->stripNamespace($this->getOpVal($op2)));
    18291959                break;
    18301960                // }}}
     
    18451975                }
    18461976                else {
    1847                     $class = isset($op2['constant']) ? $op2['constant'] : $this->getOpVal($op2);
     1977                    $class = $this->getOpVal($op2, true, true);
    18481978                }
    18491979                $resvar = $class;
     
    18671997                }
    18681998
    1869                 $resvar = str($resvar) . '::' . unquoteName($this->getOpVal($op2));
     1999                $resvar = new Decompiler_Code($this, array($resvar, '::', unquoteName($this->getOpVal($op2))));
    18702000                break;
    18712001                // }}}
     
    18782008            case XC_FETCH_IS:
    18792009                $fetchType = defined('ZEND_FETCH_TYPE_MASK') ? ($ext & ZEND_FETCH_TYPE_MASK) : $op2[!ZEND_ENGINE_2 ? 'fetch_type' : 'EA.type'];
    1880                 $name = isset($op1['constant']) ? $op1['constant'] : unquoteName($this->getOpVal($op1));
     2010                $name = isset($op1['constant']) ? $op1['constant'] : $this->getOpVal($op1);
    18812011                if ($fetchType == ZEND_FETCH_STATIC_MEMBER) {
    1882                     $class = isset($op2['constant']) ? $op2['constant'] : $this->getOpVal($op2);
    1883                     $rvalue = $this->stripNamespace($class) . '::$' . $name;
     2012                    $class = $this->getOpVal($op2, false, true);
    18842013                }
    18852014                else {
    1886                     $rvalue = isset($op1['constant']) ? $op1['constant'] : $this->getOpVal($op1);
    1887                     $globalName = xcache_is_autoglobal($name) ? "\$$name" : "\$GLOBALS[" . str($this->getOpVal($op1), $this->EX) . "]";
    1888                     $rvalue = new Decompiler_Fetch($this, $rvalue, $fetchType, $globalName);
    1889                 }
     2015                    $class = null;
     2016                }
     2017                $rvalue = new Decompiler_Fetch($this, $class, $name, $fetchType);
    18902018
    18912019                if ($res['op_type'] != XC_IS_UNUSED) {
     
    19042032                }
    19052033
    1906                 $op['php'] = "unset(" . str($rvalue, $this->EX) . ")";
     2034                $op['php'] = array("unset(", $rvalue, ")");
    19072035                $lastphpop = &$op;
    19082036                break;
     
    19792107                    ++ $i;
    19802108                    $rvalue = $this->getOpVal($opcodes[$i]['op1']);
    1981                     $resvar = str($lvalue, $this->EX) . ' = ' . str($rvalue);
     2109                    $resvar = new Decompiler_BinaryOp($this, $lvalue, $opc, $rvalue);
    19822110                }
    19832111                else if ($opc == XC_UNSET_DIM || $opc == XC_UNSET_OBJ || $opc == XC_UNSET_DIM_OBJ) {
    1984                     $op['php'] = "unset(" . str($rvalue, $this->EX) . ")";
     2112                    $op['php'] = array("unset(", $rvalue, ")");
    19852113                    $lastphpop = &$op;
    19862114                }
     
    20032131                    $dim->assign = $lvalue;
    20042132                    if ($dim->isLast) {
    2005                         $resvar = foldToCode($dim->value, $this->EX);
     2133                        $resvar = $dim->value;
    20062134                    }
    20072135                    unset($dim);
    20082136                    break;
    20092137                }
    2010                 if (is_a($rvalue, 'Decompiler_Fetch')) {
    2011                     $src = str($rvalue->src, $this->EX);
    2012                     $name = unquoteName($src);
    2013                     if ('$' . $name == $lvalue) {
     2138                if (is_a($lvalue, 'Decompiler_Fetch') && is_a($rvalue, 'Decompiler_Fetch')) {
     2139                    if ($lvalue->name == $rvalue->name) {
    20142140                        switch ($rvalue->fetchType) {
    20152141                        case ZEND_FETCH_STATIC:
    20162142                            $statics = &$this->EX['op_array']['static_variables'];
    2017                             if ((xcache_get_type($statics[$name]) & IS_LEXICAL_VAR)) {
    2018                                 $this->EX['uses'][] = str($lvalue);
     2143                            if ((xcache_get_type($statics[$rvalue->name]) & IS_LEXICAL_VAR)) {
     2144                                $this->EX['uses'][] = $lvalue;
    20192145                                unset($statics);
    20202146                                break 2;
     
    20302156                $lvalue = $this->getOpVal($op1);
    20312157                $rvalue = $this->getOpVal($op2);
    2032                 if (is_a($rvalue, 'Decompiler_Fetch')) {
    2033                     $src = str($rvalue->src, $this->EX);
    2034                     if ('$' . unquoteName($src) == $lvalue) {
     2158                if (is_a($lvalue, 'Decompiler_Fetch') && is_a($rvalue, 'Decompiler_Fetch')) {
     2159                    if ($lvalue->name == $rvalue->name) {
    20352160                        switch ($rvalue->fetchType) {
    20362161                        case ZEND_FETCH_GLOBAL:
    20372162                        case ZEND_FETCH_GLOBAL_LOCK:
    2038                             $resvar = 'global ' . $lvalue;
     2163                            $resvar = new Decompiler_Code($this, array('global ', $lvalue));
    20392164                            break 2;
    20402165                        case ZEND_FETCH_STATIC:
    20412166                            $statics = &$this->EX['op_array']['static_variables'];
    2042                             $name = unquoteName($src);
    2043                             if ((xcache_get_type($statics[$name]) & IS_LEXICAL_REF)) {
    2044                                 $this->EX['uses'][] = '&' . str($lvalue);
     2167                            if ((xcache_get_type($statics[$rvalue->name]) & IS_LEXICAL_REF)) {
     2168                                $this->EX['uses'][] = array('&', $lvalue);
    20452169                                unset($statics);
    20462170                                break 2;
    20472171                            }
    20482172
    2049                             $resvar = 'static ' . $lvalue;
    2050                             if (isset($statics[$name])) {
    2051                                 $var = $statics[$name];
    2052                                 $resvar .= ' = ';
    2053                                 $resvar .= str(value($var), $this->EX);
     2173                            $resvar = array();
     2174                            $resvar[] = 'static ';
     2175                            $resvar[] = $lvalue;
     2176                            if (isset($statics[$rvalue->name])) {
     2177                                $var = $statics[$rvalue->name];
     2178                                $resvar[] = ' = ';
     2179                                $resvar[] = value($var);
    20542180                            }
     2181                            $resvar = new Decompiler_Code($this, $resvar);
    20552182                            unset($statics);
    20562183                            break 2;
     
    20752202                    $obj = '$this';
    20762203                }
    2077                 $rvalue = str($obj) . "->" . unquoteVariableName($this->getOpVal($op2));
     2204                $name = isset($op2['constant']) ? new Decompiler_Value($this, $op2['constant']) : $this->getOpVal($op2);
    20782205                if ($res['op_type'] != XC_IS_UNUSED) {
    2079                     $resvar = $rvalue;
     2206                    $resvar = new Decompiler_Fetch($this, $obj, $name->value, XC_FETCH_PROPERTY);
    20802207                }
    20812208                if ($opc == XC_ASSIGN_OBJ) {
     
    20832210                    $lvalue = $rvalue;
    20842211                    $rvalue = $this->getOpVal($opcodes[$i]['op1']);
    2085                     $resvar = "$lvalue = " . str($rvalue);
     2212                    $resvar = new Decompiler_BinaryOp($this, $lvalue, $opc, $rvalue);
    20862213                }
    20872214                break;
     
    21132240                            $container = '$this';
    21142241                        }
    2115                         $rvalue = str($container, $this->EX) . "->" . unquoteVariableName($dim);
     2242                        $rvalue = array($container, "->", unquoteVariableName($dim));
    21162243                    }
    21172244                    else {
    2118                         $rvalue = str($container, $this->EX) . '[' . str($dim) .']';
     2245                        $rvalue = array($container, '[', $dim, ']');
    21192246                    }
    21202247                }
     
    21222249                switch (((!ZEND_ENGINE_2 ? $op['op2']['var'] /* constant */ : $ext) & ZEND_ISSET_ISEMPTY_MASK)) {
    21232250                case ZEND_ISSET:
    2124                     $rvalue = "isset(" . str($rvalue) . ")";
     2251                    $rvalue = array("isset(", $rvalue, ")");
    21252252                    break;
    21262253                case ZEND_ISEMPTY:
    2127                     $rvalue = "empty(" . str($rvalue) . ")";
     2254                    $rvalue = array("empty(", $rvalue, ")");
    21282255                    break;
    21292256                }
    2130                 $resvar = $rvalue;
     2257                $resvar = new Decompiler_Code($this, $rvalue);
    21312258                break;
    21322259                // }}}
     
    21362263            case XC_SEND_VAR: // {{{
    21372264                $ref = (!ZEND_ENGINE_2_4 && $opc == XC_SEND_REF ? '&' : '');
    2138                 $this->EX['argstack'][] = $ref . str($this->getOpVal($op1));
     2265                $this->EX['argstack'][] = array($ref, $this->getOpVal($op1));
    21392266                break;
    21402267                // }}}
     
    21442271                if ($opc == XC_INIT_STATIC_METHOD_CALL) {
    21452272                    $this->EX['object'] = null;
    2146                     $this->EX['called_scope'] = $this->stripNamespace(isset($op1['constant']) ? $op1['constant'] : $this->getOpVal($op1));
     2273                    $this->EX['called_scope'] = $this->getOpVal($op1, false, true);
    21472274                }
    21482275                else {
     
    21842311                    $this->EX['called_scope'] = null;
    21852312                }
    2186                 $this->EX['fbc'] = isset($op2['constant']) ? $op2['constant'] : $this->getOpVal($op2);
     2313                $this->EX['fbc'] = $this->getOpVal($op2, true, true);
    21872314                break;
    21882315                // }}}
     
    21982325                $fname = $this->EX['op_array']['funcs'][$which]['name'];
    21992326                $args = $this->popargs($ext);
    2200                 $resvar = $fname . "($args)";
     2327                $resvar = new Decompiler_Code($this, array($fname, "(", $args, ")"));
    22012328                break;
    22022329            case XC_DO_FCALL:
    22032330                $fname = unquoteName($this->getOpVal($op1), $this->EX);
    22042331                $args = $this->popargs($ext);
    2205                 $resvar = $fname . "($args)";
     2332                $resvar = new Decompiler_Code($this, array($fname, "(", $args, ")"));
    22062333                break;
    22072334            case XC_DO_FCALL_BY_NAME: // {{{
     
    22122339                }
    22132340
    2214                 $args = $this->popargs($ext);
    2215 
    2216                 $prefix = (isset($object) ? str($object) . '->' : '' )
    2217                     . (isset($this->EX['called_scope']) ? str($this->EX['called_scope']) . '::' : '');
    2218                 $resvar = $prefix
    2219                     . (!$prefix ? $this->stripNamespace($this->EX['fbc']) : str($this->EX['fbc']))
    2220                     . "($args)";
    2221                 unset($args);
     2341                $code = array();
     2342                if (isset($object)) {
     2343                    $code[] = $object;
     2344                    $code[] = '->';
     2345                }
     2346                if (isset($this->EX['called_scope'])) {
     2347                    $code[] = $this->EX['called_scope'];
     2348                    $code[] = '::';
     2349                }
     2350                if (isset($this->EX['fbc'])) {
     2351                    $code[] = $this->EX['fbc'];
     2352                }
     2353                $code[] = '(';
     2354                $code[] = $this->popargs($ext);
     2355                $code[] = ')';
     2356                $resvar = new Decompiler_Code($this, $code);
     2357                unset($code);
    22222358
    22232359                if (is_int($this->EX['object'])) {
     
    22882424                    $i += 2;
    22892425                }
    2290                 if ($this->EX['lastBlock'] == 'complex') {
    2291                     echo PHP_EOL;
    2292                 }
    2293                 $this->EX['lastBlock'] = null;
    22942426                $this->activeClass = $class['name'];
    22952427
    2296                 $indent = $this->EX['indent'];
    22972428                $oldEX = &$this->EX;
    22982429                unset($this->EX);
    2299                 $this->dclass($class, $indent);
     2430                $this->dclass($class);
    23002431                $this->EX = &$oldEX;
    23012432                unset($oldEX);
    23022433
    23032434                $this->activeClass = null;
    2304                 echo PHP_EOL;
    23052435                unset($class);
    23062436                break;
     
    23172447                switch ($opc) {
    23182448                case XC_ADD_CHAR:
    2319                     $op2val = value(chr(str($op2val)));
     2449                    $op2val = value(chr($op2val->value));
    23202450                    break;
    23212451                case XC_ADD_STRING:
     
    23242454                    break;
    23252455                }
    2326                 if (str($op1val) == "''") {
     2456                if (!isset($op1val) == "''") {
    23272457                    $rvalue = $op2val;
    23282458                }
    2329                 else if (str($op2val) == "''") {
     2459                else if (!isset($op2val) == "''") {
    23302460                    $rvalue = $op1val;
    23312461                }
    23322462                else {
    2333                     $rvalue = str($op1val) . ' . ' . str($op2val);
     2463                    $rvalue = new Decompiler_BinaryOp($this, $op1val, XC_CONCAT, $op2val);
    23342464                }
    23352465                $resvar = $rvalue;
     
    23382468            case XC_PRINT: // {{{
    23392469                $op1val = $this->getOpVal($op1);
    2340                 $resvar = "print(" . str($op1val) . ")";
     2470                $resvar = new Decompiler_Code($this, array("print(", $op1val, ")"));
    23412471                break;
    23422472                // }}}
    23432473            case XC_ECHO: // {{{
    23442474                $op1val = $this->getOpVal($op1);
    2345                 $resvar = "echo " . str($op1val);
     2475                $resvar = new Decompiler_Code($this, array("echo ", $op1val));
    23462476                break;
    23472477                // }}}
    23482478            case XC_EXIT: // {{{
    23492479                $op1val = $this->getOpVal($op1);
    2350                 $resvar = "exit(" . str($op1val) . ")";
     2480                $resvar = new Decompiler_Code($this, array("exit(", $op1val, ")"));
    23512481                break;
    23522482                // }}}
     
    23872517            case XC_RETURN_BY_REF:
    23882518            case XC_RETURN: // {{{
    2389                 $resvar = "return " . str($this->getOpVal($op1, $this->EX));
     2519                $resvar = new Decompiler_Code($this, array("return ", $this->getOpVal($op1)));
    23902520                break;
    23912521                // }}}
     
    23932523                $type = ZEND_ENGINE_2_4 ? $ext : $op2['var']; // hack
    23942524                $keyword = $this->includeTypes[$type];
    2395                 $rvalue = str($this->getOpVal($op1));
     2525                $rvalue = $this->getOpVal($op1);
    23962526                if ($type == ZEND_EVAL) {
    2397                     $resvar = "$keyword($rvalue)";
     2527                    $resvar = new Decompiler_Code($this, array($keyword, "(", $rvalue, ")"));
    23982528                }
    23992529                else {
    2400                     $resvar = "$keyword $rvalue";
     2530                    $resvar = new Decompiler_Code($this, array($keyword, " ", $rvalue));
    24012531                }
    24022532                break;
     
    24282558                // }}}
    24292559            case XC_YIELD: // {{{
    2430                 $resvar = 'yield ' . str($this->getOpVal($op1));
     2560                $resvar = new Decompiler_Code($this, array('yield ', $this->getOpVal($op1)));
    24312561                break;
    24322562                // }}}
    24332563            case XC_SWITCH_FREE: // {{{
    24342564                if (isset($T[$op1['var']])) {
    2435                     $this->beginComplexBlock();
    2436                     echo $this->EX['indent'], 'switch (', str($this->getOpVal($op1)), ") {", PHP_EOL;
    2437                     echo $this->EX['indent'], '}', PHP_EOL;
    2438                     $this->endComplexBlock();
     2565                    $this->output->beginComplexBlock();
     2566                    $this->output->writeln('switch (', $this->getOpVal($op1), ") {");
     2567                    $this->output->writeln('}');
     2568                    $this->output->endComplexBlock();
    24392569                }
    24402570                break;
     
    24432573                $free = $T[$op1['var']];
    24442574                if (!is_a($free, 'Decompiler_Box')) {
    2445                     $op['php'] = is_object($free) ? $free : $this->unquote($free, '(', ')');
     2575                    $op['php'] = is_object($free) || is_array($free) ? $free : $this->unquote($free, '(', ')');
    24462576                    $lastphpop = &$op;
    24472577                }
     
    24642594            case XC_BRK:
    24652595                $resvar = $opc == XC_CONT ? 'continue' : 'break';
    2466                 $count = str($this->getOpVal($op2));
    2467                 if ($count != '1') {
    2468                     $resvar .= ' ' . $count;
     2596                $count = $this->getOpVal($op2);
     2597                if ($count->value != 1) {
     2598                    $resvar .= ' ' . $count->value;
    24692599                }
    24702600                break;
     
    25042634                $flags = array_flip(explode('_', $opname));
    25052635                if (isset($flags['OBJ'])) {
    2506                     $resvar = str($this->getOpVal($op1)) . '->' . $op2['constant'];
     2636                    $code = array($this->getOpVal($op1), '->', $op2['constant']);
    25072637                }
    25082638                else {
    2509                     $resvar = str($this->getOpVal($op1));
     2639                    $code = array($this->getOpVal($op1));
    25102640                }
    25112641                $opstr = isset($flags['DEC']) ? '--' : '++';
    25122642                if (isset($flags['POST'])) {
    2513                     $resvar .= $opstr;
     2643                    $code[] = $opstr;
    25142644                }
    25152645                else {
    2516                     $resvar = "$opstr$resvar";
    2517                 }
     2646                    array_unshift($code, $opstr);
     2647                }
     2648                $resvar = new Decompiler_Code($this, $code);
    25182649                break;
    25192650                // }}}
     
    25252656            case XC_END_SILENCE: // {{{
    25262657                $this->EX['silence']--;
    2527                 $lastresvar = '@' . str($lastresvar, $this->EX);
     2658                $lastresvar = new Decompiler_Code($this, array('@', $lastresvar));
    25282659                break;
    25292660                // }}}
     
    25412672                assert(isset($type2cast[$type]));
    25422673                $cast = $type2cast[$type];
    2543                 $resvar = $cast . ' ' . str($this->getOpVal($op1));
     2674                $resvar = new Decompiler_Code($this, array($cast, ' ', $this->getOpVal($op1)));
    25442675                break;
    25452676                // }}}
     
    25552686                $key = substr($key . ".", 0, strlen($key));
    25562687
    2557                 $indent = $this->EX['indent'];
    25582688                $oldEX = &$this->EX;
    25592689                unset($this->EX);
    2560                 $this->dfunction($this->dc['function_table'][$key], $indent);
     2690                $this->dfunction($this->dc['function_table'][$key]);
    25612691                $this->EX = $oldEX;
    25622692
     
    25692699                $key = substr($key . ".", 0, strlen($key));
    25702700
    2571                 $indent = $this->EX['indent'];
    25722701                $oldEX = &$this->EX;
    25732702                unset($this->EX);
    2574                 $this->dfunction($this->dc['function_table'][$key], $indent);
     2703                $this->dfunction($this->dc['function_table'][$key]);
    25752704                $this->EX = &$oldEX;
    25762705                unset($oldEX);
     
    25822711            case XC_DECLARE_CONST:
    25832712                $name = $this->stripNamespace(unquoteName($this->getOpVal($op1), $this->EX));
    2584                 $value = str($this->getOpVal($op2));
    2585                 $resvar = 'const ' . $name . ' = ' . $value;
     2713                $value = $this->getOpVal($op2);
     2714                $resvar = new Decompiler_Code($this, array('const ', $name, ' = ', $value));
    25862715                break;
    25872716            case XC_DECLARE_FUNCTION_OR_CLASS:
     
    25892718                break;
    25902719            case XC_TICKS:
    2591                 $lastphpop['ticks'] = ZEND_ENGINE_2_4 ? $ext : $this->getOpVal($op1);
     2720                $lastphpop['ticks'] = ZEND_ENGINE_2_4 ? $ext : ($op1 = $this->getOpVal($op1) ? $op1->value : 0);
    25922721                // $this->EX['tickschanged'] = true;
    25932722                break;
     
    26632792        for ($i = 0; $i < $n; $i++) {
    26642793            $a = array_pop($this->EX['argstack']);
    2665             if (is_array($a)) {
    2666                 array_unshift($args, foldToCode($a, $this->EX));
    2667             }
    2668             else {
    2669                 array_unshift($args, $a);
    2670             }
    2671         }
    2672         return implode(', ', $args);
     2794            array_unshift($args, $a);
     2795        }
     2796        return new Decompiler_Statements($this, $args);
     2797    }
     2798    // }}}
     2799    function opValToString__($code) // {{{
     2800    {
     2801        while (is_object($code)) {
     2802            $code = $code->toCode('');
     2803        }
     2804
     2805        if (is_array($code)) {
     2806            foreach ($code as $c) {
     2807                $this->opValToString__($c);
     2808            }
     2809        }
     2810        else {
     2811            echo $code;
     2812        }
     2813    }
     2814    // }}}
     2815    function opValToString_($op) // {{{
     2816    {
     2817        ob_start();
     2818        $this->opValToString__($this->getOpVal($op, null));
     2819        return ob_get_clean();
    26732820    }
    26742821    // }}}
     
    26822829            $s = '$' . $op['var'];
    26832830            if ($which != 'result' && isset($this->EX['Ts'])) {
    2684                 $s .= ':' . str($this->getOpVal($op, null));
     2831                $s .= ':' . $this->opValToString_($op);
    26852832            }
    26862833            return $s;
     
    26892836            $s = '#' . $op['var'];
    26902837            if ($which != 'result' && isset($this->EX['Ts'])) {
    2691                 $s .= ':' . str($this->getOpVal($op, null));
     2838                $s .= ':' . $this->opValToString_($op);
    26922839            }
    26932840            return $s;
    26942841
    26952842        case XC_IS_CONST:
    2696             return isset($this->EX['Ts']) ? str($this->getOpVal($op, null)) : $op['var'] . ':' . var_export($op['constant'], true);
     2843            return isset($this->EX['Ts']) ? $this->opValToString_($op) : $op['var'] . ':' . var_export($op['constant'], true);
    26972844
    26982845        default:
    2699             return isset($this->EX['Ts']) ? str($this->getOpVal($op, null)) : $op['op_type'] . '?' . $op['var'];
     2846            return isset($this->EX['Ts']) ? $this->opValToString_($op) : $op['op_type'] . '?' . $op['var'];
    27002847        }
    27012848    }
     
    27042851    {
    27052852        assert('isset($op)');
    2706         echo str_pad($op['line'], $padding);
    2707         echo str_pad($op['lineno'], $padding);
     2853        $this->output->write(str_pad($op['line'], $padding));
     2854        $this->output->write(str_pad($op['lineno'], $padding));
    27082855
    27092856        if (isset($op['oldopcode'])) {
     
    27172864            $name = substr($name, 5);
    27182865        }
    2719         echo ' ', str_pad($name, 25);
     2866        $this->output->write(' ', str_pad($name, 25));
    27202867
    27212868        $types = array('result' => 9, 'op1' => 20, 'op2' => 20);
     
    27232870        $resUsed = ((ZEND_ENGINE_2_4 ? ($res['op_type'] & EXT_TYPE_UNUSED) : ($res['EA.type'] & EXT_TYPE_UNUSED)) || $res['op_type'] == XC_IS_UNUSED) ? '' : '=';
    27242871        foreach ($types as $which => $len) {
    2725             echo ' ', str_pad($this->opToString($op[$which], $which) . ($which == 'result' ? $resUsed : ''), $len);
    2726         }
    2727         echo "\t;", $op['extended_value'];
     2872            $this->output->write(' ', str_pad($this->opToString($op[$which], $which) . ($which == 'result' ? $resUsed : ''), $len));
     2873        }
     2874        $this->output->write("\t;", $op['extended_value']);
    27282875        if (isset($op['isCatchBegin'])) {
    2729             echo ' CB';
     2876            $this->output->write(' CB');
    27302877        }
    27312878        if (!empty($op['jmptos'])) {
    2732             echo "\t>>", implode(',', $op['jmptos']);
     2879            $this->output->write("\t>>", implode(',', $op['jmptos']));
    27332880        }
    27342881        if (!empty($op['jmpfroms'])) {
    2735             echo "\t<<", implode(',', $op['jmpfroms']);
    2736         }
    2737 
    2738         echo PHP_EOL;
     2882            $this->output->write("\t<<", implode(',', $op['jmpfroms']));
     2883        }
     2884
     2885        $this->output->write(PHP_EOL);
    27392886    }
    27402887    // }}}
    27412888    function dumpRange($range, $ts = true) // {{{
    27422889    {
    2743         if (!$this->inComment++) {
    2744             echo $this->EX['indent'], "/*", PHP_EOL;
    2745         }
     2890        $this->output->beginComment();
    27462891        if (!$ts) {
    27472892            $Ts = $this->EX['Ts'];
     
    27502895        $padding = max(strlen($range[1]), strlen($this->EX['opcodes'][$range[1]]['lineno'])) + 1;
    27512896        for ($i = $range[0]; $i <= $range[1]; ++$i) {
    2752             echo $this->EX['indent'];
     2897            $this->output->write($this->output->indent);
    27532898            $this->dumpOp($this->EX['opcodes'][$i], $padding);
    27542899        }
     
    27562901            $this->EX['Ts'] = $Ts;
    27572902        }
    2758         if (!--$this->inComment) {
    2759             echo $this->EX['indent'], "*/", PHP_EOL;
    2760         }
     2903        $this->output->endComment();
    27612904    }
    27622905    // }}}
     
    27772920
    27782921        $refrest = false;
     2922        $args = array();
    27792923        for ($i = 0; $i < $c; $i++) {
    2780             if ($i) {
    2781                 echo ', ';
    2782             }
     2924            $arg = array();
    27832925            $recv = isset($this->EX['recvs'][$i + 1]) ? $this->EX['recvs'][$i + 1] : null;
    27842926            if (isset($op_array['arg_info'])) {
    27852927                $ai = $op_array['arg_info'][$i];
    27862928                if (isset($ai['type_hint']) ? ($ai['type_hint'] == IS_CALLABLE || $ai['type_hint'] == IS_OBJECT) : !empty($ai['class_name'])) {
    2787                     echo $this->stripNamespace($ai['class_name']), ' ';
     2929                    $arg[] = $this->stripNamespace($ai['class_name']);
     2930                    $arg[] = ' ';
    27882931                    if (!ZEND_ENGINE_2_2 && $ai['allow_null']) {
    2789                         echo 'or NULL ';
     2932                        $arg[] = 'or NULL ';
    27902933                    }
    27912934                }
    27922935                else if (isset($ai['type_hint']) ? $ai['type_hint'] == IS_ARRAY : !empty($ai['array_type_hint'])) {
    2793                     echo 'array ';
     2936                    $arg[] = 'array ';
    27942937                    if (!ZEND_ENGINE_2_2 && $ai['allow_null']) {
    2795                         echo 'or NULL ';
     2938                        $arg[] = 'or NULL ';
    27962939                    }
    27972940                }
    27982941                if ($ai['pass_by_reference']) {
    2799                     echo '&';
    2800                 }
    2801                 printf("\$%s", $ai['name']);
     2942                    $arg[] = '&';
     2943                }
     2944                $arg[] = '$';
     2945                $arg[] = $ai['name'];
    28022946            }
    28032947            else {
    28042948                if ($refrest) {
    2805                     echo '&';
     2949                    $arg[] = '&';
    28062950                }
    28072951                else if (!empty($op_array['arg_types']) && isset($op_array['arg_types'][$i])) {
     
    28112955                        /* fall */
    28122956                    case BYREF_FORCE:
    2813                         echo '&';
     2957                        $arg[] = '&';
    28142958                        break;
    28152959
     
    28212965                    }
    28222966                }
    2823                 echo str($recv[0], $this->EX);
     2967                $arg[] = $recv[0];
    28242968            }
    28252969            if (isset($recv) && isset($recv[1])) {
    2826                 echo ' = ', str($recv[1], $this->EX);
    2827             }
    2828         }
     2970                $arg[] = ' = ';
     2971                $arg[] = $recv[1];
     2972            }
     2973            $args[] = $arg;
     2974        }
     2975        return new Decompiler_Statements($this, $args);
    28292976    }
    28302977    // }}}
    28312978    function duses() // {{{
    28322979    {
     2980        $code = array();
    28332981        if ($this->EX['uses']) {
    2834             echo " use(", implode(', ', $this->EX['uses']), ')';
    2835         }
    2836     }
    2837     // }}}
    2838     function dfunction($func, $indent = '', $decorations = array(), $nobody = false) // {{{
     2982            $code[] = " use(";
     2983            $code[] = new Decompiler_Statements($this, $this->EX['uses']);
     2984            $code[] = ')';
     2985        }
     2986        return $code;
     2987    }
     2988    // }}}
     2989    function dfunction($func, $decorations = array(), $nobody = false) // {{{
    28392990    {
    28402991        static $opcode_count = 0;
    28412992        $opcode_count += count($func['op_array']['opcodes']);
    28422993
    2843         $this->detectNamespace($func['op_array']['function_name']);
     2994        $functionName = $this->stripNamespace($func['op_array']['function_name']);
     2995        $this->detectNamespace($functionName);
     2996
     2997        $isExpression = false;
     2998        if ($functionName == '{closure}') {
     2999            $functionName = '';
     3000            $isExpression = true;
     3001        }
     3002
     3003        if (!$nobody && !$isExpression) {
     3004            $this->output->beginComplexBlock();
     3005        }
    28443006
    28453007        $returnByRef = '';
     
    28523014        else {
    28533015            ob_start();
    2854             $EX = &$this->dop_array($func['op_array'], $indent . INDENT);
     3016            $this->output->beginScope();
     3017            $EX = &$this->dop_array($func['op_array'], true);
     3018            $this->output->endScope();
    28553019            $body = ob_get_clean();
    28563020            $hasReturn = false;
     
    28683032            }
    28693033            if ($hasReturn && $hasReturnByRef) {
    2870                 echo $indent, "// WARN: both return and return-by-ref present", PHP_EOL;
     3034                $this->output->printfError("WARN: both return and return-by-ref present" . PHP_EOL);
    28713035            }
    28723036            if ($hasReturnByRef) {
     
    28753039        }
    28763040
    2877         $functionName = $this->stripNamespace($func['op_array']['function_name']);
    2878         $isExpression = false;
    2879         if ($functionName == '{closure}') {
    2880             $functionName = '';
    2881             $isExpression = true;
    2882         }
    2883         echo $isExpression ? '' : $indent;
     3041        if (!empty($func['op_array']['doc_comment'])) {
     3042            $this->output->writeln($func['op_array']['doc_comment']);
     3043        }
     3044
     3045        $functionDeclare = array();
    28843046        if ($decorations) {
    2885             echo implode(' ', $decorations), ' ';
     3047            $functionDeclare[] = implode(' ', $decorations);
     3048            $functionDeclare[] = ' ';
    28863049        }
    28873050        $this->EX = &$EX;
    28883051        unset($EX);
    2889         echo 'function', $functionName ? ' ' . $returnByRef . $functionName : '', '(';
    2890         $this->dargs();
    2891         echo ")";
    2892         $this->duses();
     3052        $functionDeclare[] = 'function';
     3053        $functionDeclare[] = $functionName ? ' ' . $returnByRef . $functionName : '';
     3054        $functionDeclare[] = '(';
     3055        $functionDeclare[] = $this->dargs();
     3056        $functionDeclare[] = ")";
     3057        $functionDeclare[] = $this->duses();
    28933058        unset($this->EX);
    28943059        if ($nobody) {
    2895             echo ";", PHP_EOL;
     3060            $functionDeclare[] = ";";
     3061            $this->output->writeln($functionDeclare);
    28963062        }
    28973063        else {
    28983064            if (!$isExpression) {
    2899                 echo PHP_EOL;
    2900                 echo $indent, "{", PHP_EOL;
     3065                $this->output->writeln($functionDeclare);
     3066                $this->output->writeln("{");
    29013067            }
    29023068            else {
    2903                 echo " {", PHP_EOL;
    2904             }
    2905 
     3069                $this->output->write($functionDeclare);
     3070                $this->output->write(" {");
     3071                $this->output->write(PHP_EOL);
     3072            }
    29063073            echo $body;
    2907             echo "$indent}";
    29083074            if (!$isExpression) {
    2909                 echo PHP_EOL;
     3075                $this->output->writeln("}");
     3076            }
     3077            else {
     3078                $this->output->write($this->output->indent);
     3079                $this->output->write("}");
     3080            }
     3081            if (!$isExpression) {
     3082                $this->output->endComplexBlock();
    29103083            }
    29113084        }
     
    29193092    }
    29203093    // }}}
    2921     function dclass($class, $indent = '') // {{{
     3094    function dclass($class) // {{{
    29223095    {
    29233096        $this->value2constant[$this->activeClass] = '__CLASS__';
     
    29253098
    29263099        // {{{ class decl
    2927         if (!empty($class['doc_comment'])) {
    2928             echo $indent;
    2929             echo $class['doc_comment'];
    2930             echo PHP_EOL;
    2931         }
    29323100        $isInterface = false;
    29333101        $decorations = array();
     
    29463114        }
    29473115
    2948         echo $indent;
     3116        $this->output->beginComplexBlock();
     3117        if (!empty($class['doc_comment'])) {
     3118            $this->output->writeln($class['doc_comment']);
     3119        }
     3120        $classDeclare = array();
    29493121        if ($decorations) {
    2950             echo implode(' ', $decorations), ' ';
    2951         }
    2952         echo $isInterface ? 'interface ' : 'class ', $this->stripNamespace($class['name']);
     3122            $classDeclare[] = implode(' ', $decorations) . ' ';
     3123        }
     3124        $classDeclare[] = $isInterface ? 'interface ' : 'class ';
     3125        $classDeclare[] = $this->stripNamespace($class['name']);
    29533126        if ($class['parent']) {
    2954             echo ' extends ', $this->stripNamespace($class['parent']);
     3127            $classDeclare[] = ' extends ';
     3128            $classDeclare[] = $this->stripNamespace($class['parent']);
    29553129        }
    29563130        /* TODO */
    29573131        if (!empty($class['interfaces'])) {
    2958             echo ' implements ';
    2959             echo implode(', ', $class['interfaces']);
    2960         }
    2961         echo PHP_EOL;
    2962         echo $indent, "{";
     3132            $classDeclare[] = ' implements ';
     3133            $classDeclare[] = implode(', ', $class['interfaces']);
     3134        }
     3135        $this->output->writeln($classDeclare);
     3136        $this->output->writeln("{");
     3137        $this->output->beginScope();
    29633138        // }}}
    2964         $newindent = INDENT . $indent;
    29653139        // {{{ const
    29663140        if (!empty($class['constants_table'])) {
    2967             echo PHP_EOL;
     3141            $this->output->beginComplexBlock();
    29683142            foreach ($class['constants_table'] as $name => $v) {
    2969                 echo $newindent;
    2970                 echo 'const ', $name, ' = ';
    2971                 echo str(value($v), $newindent);
    2972                 echo ";", PHP_EOL;
    2973             }
     3143                $this->output->writeln('const ', $name, ' = ', value($v), ";");
     3144            }
     3145            $this->output->endComplexBlock();
    29743146        }
    29753147        // }}}
     
    29803152        $member_variables = $class[ZEND_ENGINE_2 ? 'properties_info' : 'default_properties'];
    29813153        if ($member_variables) {
    2982             echo PHP_EOL;
     3154            $this->output->beginComplexBlock();
    29833155            foreach ($member_variables as $name => $dummy) {
    29843156                $info = isset($class['properties_info']) ? $class['properties_info'][$name] : null;
    29853157                if (isset($info) && !empty($info['doc_comment'])) {
    2986                     echo $newindent;
    2987                     echo $info['doc_comment'];
    2988                     echo PHP_EOL;
    2989                 }
    2990 
    2991                 echo $newindent;
     3158                    $this->output->writeln($info['doc_comment']);
     3159                }
     3160
     3161                $variableDeclare = array();
    29923162                if (ZEND_ENGINE_2) {
    29933163                    $static = ($info['flags'] & ZEND_ACC_STATIC);
    29943164
    29953165                    if ($static) {
    2996                         echo "static ";
     3166                        $variableDeclare[] = "static ";
    29973167                    }
    29983168                }
     
    30003170                $mangleSuffix = '';
    30013171                if (!ZEND_ENGINE_2) {
    3002                     echo 'var ';
     3172                    $variableDeclare[] = 'var ';
    30033173                }
    30043174                else if (!isset($info)) {
    3005                     echo 'public ';
     3175                    $variableDeclare[] = 'public ';
    30063176                }
    30073177                else {
     
    30113181                    switch ($info['flags'] & ZEND_ACC_PPP_MASK) {
    30123182                    case ZEND_ACC_PUBLIC:
    3013                         echo "public ";
     3183                        $variableDeclare[] = "public ";
    30143184                        break;
    30153185                    case ZEND_ACC_PRIVATE:
    3016                         echo "private ";
     3186                        $variableDeclare[] = "private ";
    30173187                        $mangleSuffix = "\000";
    30183188                        break;
    30193189                    case ZEND_ACC_PROTECTED:
    3020                         echo "protected ";
     3190                        $variableDeclare[] = "protected ";
    30213191                        $mangleSuffix = "\000";
    30223192                        break;
     
    30243194                }
    30253195
    3026                 echo '$', $name;
     3196                $variableDeclare[] = '$';
     3197                $variableDeclare[] = $name;
    30273198
    30283199                if (ZEND_ENGINE_2_4) {
     
    30413212                    }
    30423213                }
    3043                 if (isset($value)) {
    3044                     echo ' = ';
    3045                     echo str(value($value), $newindent);
    3046                 }
    3047                 echo ";", PHP_EOL;
    3048             }
     3214                $value = value($value);
     3215                if (is_a($value, 'Decompiler_Value') && !isset($value->value)) {
     3216                    // skip value;
     3217                }
     3218                else {
     3219                    $variableDeclare[] = ' = ';
     3220                    $variableDeclare[] = $value;
     3221                }
     3222                $variableDeclare[] = ";";
     3223                $this->output->writeln($variableDeclare);
     3224            }
     3225            $this->output->endComplexBlock();
    30493226        }
    30503227        // }}}
     
    30543231                if (!isset($func['scope']) || $func['scope'] == $class['name']) {
    30553232                    // TODO: skip shadow here
    3056                     echo PHP_EOL;
    3057                     $opa = $func['op_array'];
    3058                     if (!empty($opa['doc_comment'])) {
    3059                         echo $newindent;
    3060                         echo $opa['doc_comment'];
    3061                         echo PHP_EOL;
    3062                     }
     3233                    $opa = &$func['op_array'];
    30633234                    $isAbstractMethod = false;
    30643235                    $decorations = array();
     
    30923263                    $this->activeMethod = $this->activeClass . '::' . $opa['function_name'];
    30933264                    $this->activeFunction = $opa['function_name'];
    3094                     $this->dfunction($func, $newindent, $decorations, $isInterface || $isAbstractMethod);
     3265                    $this->dfunction($func, $decorations, $isInterface || $isAbstractMethod);
    30953266                    $this->activeFunction = null;
    30963267                    $this->activeMethod = null;
     
    31023273        }
    31033274        // }}}
    3104         echo $indent, "}", PHP_EOL;
     3275        $this->output->endScope();
     3276        $this->output->writeln("}");
     3277        $this->output->endComplexBlock();
    31053278        unset($this->value2constant[$this->activeClass]);
    31063279    }
     
    31443317    function output() // {{{
    31453318    {
    3146         echo "<?". "php";
    3147         echo PHP_EOL, PHP_EOL;
     3319        $this->output->beginComplexBlock();
     3320        $this->output->writeln("<" . "?php");
     3321        $this->output->endComplexBlock();
     3322
    31483323        foreach ($this->dc['class_table'] as $key => $class) {
    31493324            if ($key{0} != "\0") {
     
    31513326                $this->dclass($class);
    31523327                $this->activeClass = null;
    3153                 echo PHP_EOL;
    31543328            }
    31553329        }
     
    31603334                $this->dfunction($func);
    31613335                $this->activeFunction = null;
    3162                 echo PHP_EOL;
    31633336            }
    31643337        }
    31653338
    31663339        $this->dop_array($this->dc['op_array']);
    3167         echo PHP_EOL, "?" . ">", PHP_EOL;
     3340        $this->output->beginComplexBlock();
     3341        $this->output->writeln("?" . ">");
     3342        $this->output->endComplexBlock();
    31683343
    31693344        if (!empty($this->test)) {
     
    32493424    define('ZEND_FETCH_ADD_LOCK',         1);
    32503425}
     3426define('XC_FETCH_PROPERTY', 10);
    32513427
    32523428if (ZEND_ENGINE_2_4) {
Note: See TracChangeset for help on using the changeset viewer.