1887 lines
44 KiB
PHP
1887 lines
44 KiB
PHP
![]() |
<?php
|
||
|
|
||
|
define('INDENT', "\t");
|
||
|
ini_set('error_reporting', E_ALL);
|
||
|
|
||
|
function color($str, $color = 33)
|
||
|
{
|
||
|
return "\x1B[{$color}m$str\x1B[0m";
|
||
|
}
|
||
|
|
||
|
function str($src, $indent = '') // {{{
|
||
|
{
|
||
|
if (is_array($indent)) {
|
||
|
$indent = $indent['indent'];
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
$e = xcache_get_special_value($src);
|
||
|
if (isset($e)) {
|
||
|
if (is_array($e)) {
|
||
|
$src = $e;
|
||
|
}
|
||
|
else {
|
||
|
return $e;
|
||
|
}
|
||
|
}
|
||
|
*/
|
||
|
|
||
|
if (is_array($src)) {
|
||
|
die_error('array str');
|
||
|
$src = new Decompiler_Array($src);
|
||
|
return $src->__toString($indent);
|
||
|
}
|
||
|
|
||
|
if (is_object($src)) {
|
||
|
if (!method_exists($src, '__toString')) {
|
||
|
var_dump($src);
|
||
|
die_error('no __toString');
|
||
|
}
|
||
|
return $src->__toString($indent);
|
||
|
}
|
||
|
|
||
|
return $src;
|
||
|
}
|
||
|
// }}}
|
||
|
function value($value) // {{{
|
||
|
{
|
||
|
$spec = xcache_get_special_value($value);
|
||
|
if (isset($spec)) {
|
||
|
$value = $spec;
|
||
|
if (!is_array($value)) {
|
||
|
// constant
|
||
|
return $value;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (is_array($value)) {
|
||
|
$value = new Decompiler_Array($value, true);
|
||
|
}
|
||
|
else {
|
||
|
$value = new Decompiler_Value($value, true);
|
||
|
}
|
||
|
return $value;
|
||
|
}
|
||
|
// }}}
|
||
|
class Decompiler_Object // {{{
|
||
|
{
|
||
|
}
|
||
|
// }}}
|
||
|
class Decompiler_Value extends Decompiler_Object // {{{
|
||
|
{
|
||
|
var $value;
|
||
|
|
||
|
function Decompiler_Value($value = null)
|
||
|
{
|
||
|
$this->value = $value;
|
||
|
}
|
||
|
|
||
|
function __toString()
|
||
|
{
|
||
|
return var_export($this->value, true);
|
||
|
}
|
||
|
}
|
||
|
// }}}
|
||
|
class Decompiler_Code extends Decompiler_Object // {{{
|
||
|
{
|
||
|
var $src;
|
||
|
|
||
|
function Decompiler_Code($src)
|
||
|
{
|
||
|
$this->src = $src;
|
||
|
}
|
||
|
|
||
|
function __toString()
|
||
|
{
|
||
|
return $this->src;
|
||
|
}
|
||
|
}
|
||
|
// }}}
|
||
|
class Decompiler_Binop extends Decompiler_Code // {{{
|
||
|
{
|
||
|
var $opc;
|
||
|
var $op1;
|
||
|
var $op2;
|
||
|
var $parent;
|
||
|
|
||
|
function Decompiler_Binop($parent, $op1, $opc, $op2)
|
||
|
{
|
||
|
$this->parent = &$parent;
|
||
|
$this->opc = $opc;
|
||
|
$this->op1 = $op1;
|
||
|
$this->op2 = $op2;
|
||
|
}
|
||
|
|
||
|
function __toString()
|
||
|
{
|
||
|
$op1 = str($this->op1);
|
||
|
if (is_a($this->op1, 'Decompiler_Binop') && $this->op1->opc != $this->opc) {
|
||
|
$op1 = "($op1)";
|
||
|
}
|
||
|
$opstr = $this->parent->binops[$this->opc];
|
||
|
if ($op1 == '0' && $this->opc == XC_SUB) {
|
||
|
return $opstr . str($this->op2);
|
||
|
}
|
||
|
return $op1 . ' ' . $opstr . ' ' . str($this->op2);
|
||
|
}
|
||
|
}
|
||
|
// }}}
|
||
|
class Decompiler_Fetch extends Decompiler_Code // {{{
|
||
|
{
|
||
|
var $src;
|
||
|
var $fetchType;
|
||
|
|
||
|
function Decompiler_Fetch($src, $type, $globalsrc)
|
||
|
{
|
||
|
$this->src = $src;
|
||
|
$this->fetchType = $type;
|
||
|
$this->globalsrc = $globalsrc;
|
||
|
}
|
||
|
|
||
|
function __toString()
|
||
|
{
|
||
|
switch ($this->fetchType) {
|
||
|
case ZEND_FETCH_LOCAL:
|
||
|
return '$' . substr($this->src, 1, -1);
|
||
|
case ZEND_FETCH_STATIC:
|
||
|
die('static fetch cant to string');
|
||
|
case ZEND_FETCH_GLOBAL:
|
||
|
case ZEND_FETCH_GLOBAL_LOCK:
|
||
|
return $this->globalsrc;
|
||
|
default:
|
||
|
var_dump($this->fetchType);
|
||
|
assert(0);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
// }}}
|
||
|
class Decompiler_Box // {{{
|
||
|
{
|
||
|
var $obj;
|
||
|
|
||
|
function Decompiler_Box(&$obj)
|
||
|
{
|
||
|
$this->obj = &$obj;
|
||
|
}
|
||
|
|
||
|
function __toString($indent)
|
||
|
{
|
||
|
return $this->obj->__toString($indent);
|
||
|
}
|
||
|
}
|
||
|
// }}}
|
||
|
class Decompiler_Dim extends Decompiler_Value // {{{
|
||
|
{
|
||
|
var $offsets = array();
|
||
|
var $isLast = false;
|
||
|
var $assign = null;
|
||
|
|
||
|
function __toString()
|
||
|
{
|
||
|
if (is_a($this->value, 'Decompiler_ListBox')) {
|
||
|
$exp = str($this->value->obj->src);
|
||
|
}
|
||
|
else {
|
||
|
$exp = str($this->value);
|
||
|
}
|
||
|
foreach ($this->offsets as $dim) {
|
||
|
$exp .= '[' . str($dim) . ']';
|
||
|
}
|
||
|
return $exp;
|
||
|
}
|
||
|
}
|
||
|
// }}}
|
||
|
class Decompiler_DimBox extends Decompiler_Box // {{{
|
||
|
{
|
||
|
}
|
||
|
// }}}
|
||
|
class Decompiler_List extends Decompiler_Code // {{{
|
||
|
{
|
||
|
var $src;
|
||
|
var $dims = array();
|
||
|
var $everLocked = false;
|
||
|
|
||
|
function __toString()
|
||
|
{
|
||
|
if (count($this->dims) == 1 && !$this->everLocked) {
|
||
|
$dim = $this->dims[0];
|
||
|
unset($dim->value);
|
||
|
$dim->value = $this->src;
|
||
|
if (!isset($dim->assign)) {
|
||
|
return str($dim);
|
||
|
}
|
||
|
return str($this->dims[0]->assign) . ' = ' . str($dim);
|
||
|
}
|
||
|
/* flatten dims */
|
||
|
$assigns = array();
|
||
|
foreach ($this->dims as $dim) {
|
||
|
$assign = &$assigns;
|
||
|
foreach ($dim->offsets as $offset) {
|
||
|
$assign = &$assign[$offset];
|
||
|
}
|
||
|
$assign = str($dim->assign);
|
||
|
}
|
||
|
return $this->toList($assigns) . ' = ' . str($this->src);
|
||
|
}
|
||
|
|
||
|
function toList($assigns)
|
||
|
{
|
||
|
$keys = array_keys($assigns);
|
||
|
if (count($keys) < 2) {
|
||
|
$keys[] = 0;
|
||
|
}
|
||
|
$max = call_user_func_array('max', $keys);
|
||
|
$list = 'list(';
|
||
|
for ($i = 0; $i <= $max; $i ++) {
|
||
|
if ($i) {
|
||
|
$list .= ', ';
|
||
|
}
|
||
|
if (!isset($assigns[$i])) {
|
||
|
continue;
|
||
|
}
|
||
|
if (is_array($assigns[$i])) {
|
||
|
$list .= $this->toList($assigns[$i]);
|
||
|
}
|
||
|
else {
|
||
|
$list .= $assigns[$i];
|
||
|
}
|
||
|
}
|
||
|
return $list . ')';
|
||
|
}
|
||
|
}
|
||
|
// }}}
|
||
|
class Decompiler_ListBox extends Decompiler_Box // {{{
|
||
|
{
|
||
|
}
|
||
|
// }}}
|
||
|
class Decompiler_Array extends Decompiler_Value // {{{
|
||
|
{
|
||
|
var $needExport = false;
|
||
|
|
||
|
function Decompiler_Array($value = array(), $needexport = false)
|
||
|
{
|
||
|
$this->value = $value;
|
||
|
$this->needExport = $needexport;
|
||
|
}
|
||
|
|
||
|
function __toString($indent)
|
||
|
{
|
||
|
$exp = "array(";
|
||
|
$indent .= INDENT;
|
||
|
$assoclen = 0;
|
||
|
$multiline = 0;
|
||
|
$i = 0;
|
||
|
foreach ($this->value as $k => $v) {
|
||
|
if ($i !== $k) {
|
||
|
$len = strlen($k);
|
||
|
if ($assoclen < $len) {
|
||
|
$assoclen = $len;
|
||
|
}
|
||
|
}
|
||
|
if (is_array($v)) {
|
||
|
$multiline ++;
|
||
|
}
|
||
|
++ $i;
|
||
|
}
|
||
|
if ($assoclen && $this->needExport) {
|
||
|
$assoclen += 2;
|
||
|
}
|
||
|
|
||
|
$i = 0;
|
||
|
$subindent = $indent . INDENT;
|
||
|
foreach ($this->value as $k => $v) {
|
||
|
if ($multiline) {
|
||
|
if ($i) {
|
||
|
$exp .= ",";
|
||
|
}
|
||
|
$exp .= "\n";
|
||
|
$exp .= $indent;
|
||
|
}
|
||
|
else {
|
||
|
if ($i) {
|
||
|
$exp .= ", ";
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ($this->needExport) {
|
||
|
$k = var_export($k, true);
|
||
|
}
|
||
|
if ($multiline) {
|
||
|
$exp .= sprintf("%{$assoclen}s => ", $k);
|
||
|
}
|
||
|
else if ($assoclen) {
|
||
|
$exp .= $k . ' => ';
|
||
|
}
|
||
|
|
||
|
if (is_array($v)) {
|
||
|
$v = new Decompiler_Array($v, $this->needExport);
|
||
|
}
|
||
|
$exp .= str($v, $subindent);
|
||
|
|
||
|
$i ++;
|
||
|
}
|
||
|
if ($multiline) {
|
||
|
$exp .= "$indent);";
|
||
|
}
|
||
|
else {
|
||
|
$exp .= ")";
|
||
|
}
|
||
|
return $exp;
|
||
|
}
|
||
|
}
|
||
|
// }}}
|
||
|
class Decompiler_ForeachBox extends Decompiler_Box // {{{
|
||
|
{
|
||
|
var $iskey;
|
||
|
|
||
|
function __toString($indent)
|
||
|
{
|
||
|
return 'foreach (' . '';
|
||
|
}
|
||
|
}
|
||
|
// }}}
|
||
|
|
||
|
class Decompiler
|
||
|
{
|
||
|
var $rName = '!^[\\w_][\\w\\d_]*$!';
|
||
|
var $rQuotedName = "!^'[\\w_][\\w\\d_]*'\$!";
|
||
|
|
||
|
function Decompiler()
|
||
|
{
|
||
|
// {{{ opinfo
|
||
|
$this->unaryops = array(
|
||
|
XC_BW_NOT => '~',
|
||
|
XC_BOOL_NOT => '!',
|
||
|
);
|
||
|
$this->binops = array(
|
||
|
XC_ADD => "+",
|
||
|
XC_ASSIGN_ADD => "+=",
|
||
|
XC_SUB => "-",
|
||
|
XC_ASSIGN_SUB => "-=",
|
||
|
XC_MUL => "*",
|
||
|
XC_ASSIGN_MUL => "*=",
|
||
|
XC_DIV => "/",
|
||
|
XC_ASSIGN_DIV => "/=",
|
||
|
XC_MOD => "%",
|
||
|
XC_ASSIGN_MOD => "%=",
|
||
|
XC_SL => "<<",
|
||
|
XC_ASSIGN_SL => "<<=",
|
||
|
XC_SR => ">>",
|
||
|
XC_ASSIGN_SR => ">>=",
|
||
|
XC_CONCAT => ".",
|
||
|
XC_ASSIGN_CONCAT => ".=",
|
||
|
XC_IS_IDENTICAL => "===",
|
||
|
XC_IS_NOT_IDENTICAL => "!==",
|
||
|
XC_IS_EQUAL => "==",
|
||
|
XC_IS_NOT_EQUAL => "!=",
|
||
|
XC_IS_SMALLER => "<",
|
||
|
XC_IS_SMALLER_OR_EQUAL => "<=",
|
||
|
XC_BW_OR => "|",
|
||
|
XC_ASSIGN_BW_OR => "|=",
|
||
|
XC_BW_AND => "&",
|
||
|
XC_ASSIGN_BW_AND => "&=",
|
||
|
XC_BW_XOR => "^",
|
||
|
XC_ASSIGN_BW_XOR => "^=",
|
||
|
XC_BOOL_XOR => "xor",
|
||
|
);
|
||
|
// }}}
|
||
|
$this->includeTypes = array( // {{{
|
||
|
ZEND_EVAL => 'eval',
|
||
|
ZEND_INCLUDE => 'include',
|
||
|
ZEND_INCLUDE_ONCE => 'include_once',
|
||
|
ZEND_REQUIRE => 'require',
|
||
|
ZEND_REQUIRE_ONCE => 'require_once',
|
||
|
);
|
||
|
// }}}
|
||
|
}
|
||
|
function outputPhp(&$opcodes, $opline, $last, $indent) // {{{
|
||
|
{
|
||
|
$origindent = $indent;
|
||
|
$curticks = 0;
|
||
|
for ($i = $opline; $i <= $last; $i ++) {
|
||
|
$op = $opcodes[$i];
|
||
|
if (isset($op['php'])) {
|
||
|
$toticks = isset($op['ticks']) ? $op['ticks'] : 0;
|
||
|
if ($curticks != $toticks) {
|
||
|
if (!$toticks) {
|
||
|
echo $origindent, "}\n";
|
||
|
$indent = $origindent;
|
||
|
}
|
||
|
else {
|
||
|
if ($curticks) {
|
||
|
echo $origindent, "}\n";
|
||
|
}
|
||
|
else if (!$curticks) {
|
||
|
$indent .= INDENT;
|
||
|
}
|
||
|
echo $origindent, "declare(ticks=$curticks) {\n";
|
||
|
}
|
||
|
$curticks = $toticks;
|
||
|
}
|
||
|
echo $indent, str($op['php']), ";\n";
|
||
|
}
|
||
|
}
|
||
|
if ($curticks) {
|
||
|
echo $origindent, "}\n";
|
||
|
}
|
||
|
}
|
||
|
// }}}
|
||
|
function getOpVal($op, &$EX, $tostr = true, $free = false) // {{{
|
||
|
{
|
||
|
switch ($op['op_type']) {
|
||
|
case XC_IS_CONST:
|
||
|
return str(value($op['u.constant']));
|
||
|
|
||
|
case XC_IS_VAR:
|
||
|
case XC_IS_TMP_VAR:
|
||
|
$T = &$EX['Ts'];
|
||
|
$ret = $T[$op['u.var']];
|
||
|
if ($tostr) {
|
||
|
$ret = str($ret, $EX);
|
||
|
}
|
||
|
if ($free) {
|
||
|
unset($T[$op['u.var']]);
|
||
|
}
|
||
|
return $ret;
|
||
|
|
||
|
case XC_IS_CV:
|
||
|
$var = $op['u.var'];
|
||
|
$var = $EX['op_array']['vars'][$var];
|
||
|
return '$' . $var['name'];
|
||
|
|
||
|
case XC_IS_UNUSED:
|
||
|
return null;
|
||
|
}
|
||
|
}
|
||
|
// }}}
|
||
|
function &dop_array($op_array, $indent = '') // {{{
|
||
|
{
|
||
|
$opcodes = &$op_array['opcodes'];
|
||
|
$last = count($opcodes) - 1;
|
||
|
if ($opcodes[$last]['opcode'] == XC_HANDLE_EXCEPTION) {
|
||
|
unset($opcodes[$last]);
|
||
|
}
|
||
|
$EX['indent'] = '';
|
||
|
//for ($i = 0, $cnt = count($opcodes); $i < $cnt; $i ++) {
|
||
|
// $opcodes[$i]['opcode'] = xcache_get_fixed_opcode($opcodes[$i]['opcode'], $i);
|
||
|
//}
|
||
|
// {{{ build jmp array
|
||
|
for ($i = 0, $cnt = count($opcodes); $i < $cnt; $i ++) {
|
||
|
$op = &$opcodes[$i];
|
||
|
/*
|
||
|
if ($op['opcode'] == XC_JMPZ) {
|
||
|
$this->dumpop($op, $EX);
|
||
|
var_dump($op);
|
||
|
}
|
||
|
continue;
|
||
|
*/
|
||
|
$op['line'] = $i;
|
||
|
switch ($op['opcode']) {
|
||
|
case XC_JMP:
|
||
|
$target = $op['op1']['u.var'];
|
||
|
$op['jmpouts'] = array($target);
|
||
|
$opcodes[$target]['jmpins'][] = $i;
|
||
|
break;
|
||
|
|
||
|
case XC_JMPZNZ:
|
||
|
$jmpz = $op['op2']['u.opline_num'];
|
||
|
$jmpnz = $op['extended_value'];
|
||
|
$op['jmpouts'] = array($jmpz, $jmpnz);
|
||
|
$opcodes[$jmpz]['jmpins'][] = $i;
|
||
|
$opcodes[$jmpnz]['jmpins'][] = $i;
|
||
|
break;
|
||
|
|
||
|
case XC_JMPZ:
|
||
|
case XC_JMPNZ:
|
||
|
case XC_JMPZ_EX:
|
||
|
case XC_JMPNZ_EX:
|
||
|
// case XC_FE_RESET:
|
||
|
case XC_FE_FETCH:
|
||
|
// case XC_JMP_NO_CTOR:
|
||
|
$target = $op['op2']['u.opline_num'];
|
||
|
//if (!isset($target)) {
|
||
|
// $this->dumpop($op, $EX);
|
||
|
// var_dump($op); exit;
|
||
|
//}
|
||
|
$op['jmpouts'] = array($target);
|
||
|
$opcodes[$target]['jmpins'][] = $i;
|
||
|
break;
|
||
|
|
||
|
/*
|
||
|
case XC_RETURN:
|
||
|
$op['jmpouts'] = array();
|
||
|
break;
|
||
|
*/
|
||
|
}
|
||
|
}
|
||
|
unset($op);
|
||
|
// }}}
|
||
|
// build semi-basic blocks
|
||
|
$nextbbs = array();
|
||
|
$starti = 0;
|
||
|
for ($i = 1, $cnt = count($opcodes); $i < $cnt; $i ++) {
|
||
|
if (isset($opcodes[$i]['jmpins'])
|
||
|
|| isset($opcodes[$i - 1]['jmpouts'])) {
|
||
|
$nextbbs[$starti] = $i;
|
||
|
$starti = $i;
|
||
|
}
|
||
|
}
|
||
|
$nextbbs[$starti] = $cnt;
|
||
|
|
||
|
$EX = array();
|
||
|
$EX['Ts'] = array();
|
||
|
$EX['indent'] = $indent;
|
||
|
$EX['nextbbs'] = $nextbbs;
|
||
|
$EX['op_array'] = &$op_array;
|
||
|
$EX['opcodes'] = &$opcodes;
|
||
|
// func call
|
||
|
$EX['object'] = null;
|
||
|
$EX['fbc'] = null;
|
||
|
$EX['argstack'] = array();
|
||
|
$EX['arg_types_stack'] = array();
|
||
|
$EX['last'] = count($opcodes) - 1;
|
||
|
$EX['silence'] = 0;
|
||
|
|
||
|
for ($next = 0, $last = $EX['last'];
|
||
|
$loop = $this->outputCode($EX, $next, $last, $indent, true);
|
||
|
list($next, $last) = $loop) {
|
||
|
// empty
|
||
|
}
|
||
|
return $EX;
|
||
|
}
|
||
|
// }}}
|
||
|
function outputCode(&$EX, $opline, $last, $indent, $loop = false) // {{{
|
||
|
{
|
||
|
$op = &$EX['opcodes'][$opline];
|
||
|
$next = $EX['nextbbs'][$opline];
|
||
|
|
||
|
$end = $next - 1;
|
||
|
if ($end > $last) {
|
||
|
$end = $last;
|
||
|
}
|
||
|
|
||
|
if (isset($op['jmpins'])) {
|
||
|
echo "\nline", $op['line'], ":\n";
|
||
|
}
|
||
|
else {
|
||
|
// echo ";;;\n";
|
||
|
}
|
||
|
$this->dasmBasicBlock($EX, $opline, $end);
|
||
|
$this->outputPhp($EX['opcodes'], $opline, $end, $indent);
|
||
|
// jmpout op
|
||
|
$op = &$EX['opcodes'][$end];
|
||
|
$op1 = $op['op1'];
|
||
|
$op2 = $op['op2'];
|
||
|
$ext = $op['extended_value'];
|
||
|
$line = $op['line'];
|
||
|
|
||
|
if (isset($EX['opcodes'][$next])) {
|
||
|
if (isset($last) && $next > $last) {
|
||
|
$next = null;
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
$next = null;
|
||
|
}
|
||
|
if ($op['opcode'] == XC_FE_FETCH) {
|
||
|
$opline = $next;
|
||
|
$next = $op['op2']['u.opline_num'];
|
||
|
$end = $next - 1;
|
||
|
|
||
|
ob_start();
|
||
|
$this->outputCode($EX, $opline, $end /* - 1 skip last jmp */, $indent . INDENT);
|
||
|
$body = ob_get_clean();
|
||
|
|
||
|
$as = str($op['fe_as']);
|
||
|
if (isset($op['fe_key'])) {
|
||
|
$as = str($op['fe_key']) . ' => ' . $as;
|
||
|
}
|
||
|
echo "{$indent}foreach (" . str($op['fe_src']) . " as $as) {\n";
|
||
|
echo $body;
|
||
|
echo "{$indent}}";
|
||
|
// $this->outputCode($EX, $next, $last, $indent);
|
||
|
// return;
|
||
|
}
|
||
|
/*
|
||
|
if ($op['opcode'] == XC_JMPZ) {
|
||
|
$target = $op2['u.opline_num'];
|
||
|
if ($line + 1) {
|
||
|
$nextblock = $EX['nextbbs'][$next];
|
||
|
$jmpop = end($nextblock);
|
||
|
if ($jmpop['opcode'] == XC_JMP) {
|
||
|
$ifendline = $op2['u.opline_num'];
|
||
|
if ($ifendline >= $line) {
|
||
|
$cond = $op['cond'];
|
||
|
echo "{$indent}if ($cond) {\n";
|
||
|
$this->outputCode($EX, $next, $last, INDENT . $indent);
|
||
|
echo "$indent}\n";
|
||
|
$this->outputCode($EX, $target, $last, $indent);
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
*/
|
||
|
if (!isset($next)) {
|
||
|
return;
|
||
|
}
|
||
|
if (!empty($op['jmpouts']) && isset($op['isjmp'])) {
|
||
|
if (isset($op['cond'])) {
|
||
|
echo "{$indent}check ($op[cond]) {\n";
|
||
|
echo INDENT;
|
||
|
}
|
||
|
echo $indent;
|
||
|
echo xcache_get_opcode($op['opcode']), ' line', $op['jmpouts'][0];
|
||
|
if (isset($op['jmpouts'][1])) {
|
||
|
echo ', line', $op['jmpouts'][1];
|
||
|
}
|
||
|
echo ";";
|
||
|
// echo ' // <- line', $op['line'];
|
||
|
echo "\n";
|
||
|
if (isset($op['cond'])) echo "$indent}\n";
|
||
|
}
|
||
|
|
||
|
// proces JMPZ_EX/JMPNZ_EX for AND,OR
|
||
|
$op = &$EX['opcodes'][$next];
|
||
|
/*
|
||
|
if (isset($op['jmpins'])) {
|
||
|
foreach (array_reverse($op['jmpins']) as $fromline) {
|
||
|
$fromop = $EX['opcodes'][$fromline];
|
||
|
switch ($fromop['opcode']) {
|
||
|
case XC_JMPZ_EX: $opstr = 'and'; break;
|
||
|
case XC_JMPNZ_EX: $opstr = 'or'; break;
|
||
|
case XC_JMPZNZ: var_dump($fromop); exit;
|
||
|
default: continue 2;
|
||
|
}
|
||
|
|
||
|
$var = $fromop['result']['u.var'];
|
||
|
var_dump($EX['Ts'][$var]);
|
||
|
$EX['Ts'][$var] = '(' . $fromop['and_or'] . " $opstr " . $EX['Ts'][$var] . ')';
|
||
|
}
|
||
|
#$this->outputCode($EX, $next, $last, $indent);
|
||
|
#return;
|
||
|
}
|
||
|
*/
|
||
|
if (isset($op['cond_false'])) {
|
||
|
// $this->dumpop($op, $EX);
|
||
|
// any true comes here, so it's a "or"
|
||
|
$cond = implode(' and ', $op['cond_false']);
|
||
|
// var_dump($op['cond'] = $cond);
|
||
|
/*
|
||
|
$rvalue = implode(' or ', $op['cond_true']) . ' or ' . $rvalue;
|
||
|
unset($op['cond_true']);
|
||
|
*/
|
||
|
}
|
||
|
|
||
|
if ($loop) {
|
||
|
return array($next, $last);
|
||
|
}
|
||
|
$this->outputCode($EX, $next, $last, $indent);
|
||
|
}
|
||
|
// }}}
|
||
|
function unquoteName($str) // {{{
|
||
|
{
|
||
|
if (preg_match($this->rQuotedName, $str)) {
|
||
|
$str = substr($str, 1, -1);
|
||
|
}
|
||
|
return $str;
|
||
|
}
|
||
|
// }}}
|
||
|
function dasmBasicBlock(&$EX, $opline, $last) // {{{
|
||
|
{
|
||
|
$T = &$EX['Ts'];
|
||
|
$opcodes = &$EX['opcodes'];
|
||
|
$lastphpop = null;
|
||
|
|
||
|
for ($i = $opline, $ic = $last + 1; $i < $ic; $i ++) {
|
||
|
// {{{ prepair
|
||
|
$op = &$opcodes[$i];
|
||
|
$opc = $op['opcode'];
|
||
|
if ($opc == XC_NOP) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
$op1 = $op['op1'];
|
||
|
$op2 = $op['op2'];
|
||
|
$res = $op['result'];
|
||
|
$ext = $op['extended_value'];
|
||
|
|
||
|
$opname = xcache_get_opcode($opc);
|
||
|
|
||
|
if ($opname == 'UNDEF' || !isset($opname)) {
|
||
|
echo 'UNDEF OP:';
|
||
|
$this->dumpop($op, $EX);
|
||
|
continue;
|
||
|
}
|
||
|
// $this->dumpop($op, $EX); //var_dump($op);
|
||
|
|
||
|
$resvar = null;
|
||
|
if (($res['u.EA.type'] & EXT_TYPE_UNUSED) || $res['op_type'] == XC_IS_UNUSED) {
|
||
|
$istmpres = false;
|
||
|
}
|
||
|
else {
|
||
|
$istmpres = true;
|
||
|
}
|
||
|
// }}}
|
||
|
// echo $opname, "\n";
|
||
|
|
||
|
$call = array(&$this, $opname);
|
||
|
if (is_callable($call)) {
|
||
|
$this->{$opname}($op, $EX);
|
||
|
}
|
||
|
else if (isset($this->binops[$opc])) { // {{{
|
||
|
$op1val = $this->getOpVal($op1, $EX, false);
|
||
|
$op2val = $this->getOpVal($op2, $EX, false);
|
||
|
$rvalue = new Decompiler_Binop($this, $op1val, $opc, $op2val);
|
||
|
$resvar = $rvalue;
|
||
|
// }}}
|
||
|
}
|
||
|
else if (isset($this->unaryops[$opc])) { // {{{
|
||
|
$op1val = $this->getOpVal($op1, $EX);
|
||
|
$myop = $this->unaryops[$opc];
|
||
|
$rvalue = "$myop$op1val";
|
||
|
$resvar = $rvalue;
|
||
|
// }}}
|
||
|
}
|
||
|
else {
|
||
|
switch ($opc) {
|
||
|
case XC_NEW: // {{{
|
||
|
array_push($EX['arg_types_stack'], array($EX['object'], $EX['fbc']));
|
||
|
$EX['object'] = (int) $res['u.var'];
|
||
|
$EX['fbc'] = 'new ' . $this->unquoteName($this->getOpVal($op1, $EX));
|
||
|
if (PHP_VERSION < 5) {
|
||
|
$resvar = '$new object$';
|
||
|
}
|
||
|
break;
|
||
|
// }}}
|
||
|
case XC_FETCH_CLASS: // {{{
|
||
|
if ($op2['op_type'] == XC_IS_UNUSED) {
|
||
|
switch ($ext) {
|
||
|
case ZEND_FETCH_CLASS_SELF:
|
||
|
$class = 'self';
|
||
|
break;
|
||
|
case ZEND_FETCH_CLASS_PARENT:
|
||
|
$class = 'parent';
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
$class = $op2['u.constant'];
|
||
|
if (is_object($class)) {
|
||
|
$class = get_class($class);
|
||
|
}
|
||
|
}
|
||
|
$resvar = $class;
|
||
|
break;
|
||
|
// }}}
|
||
|
case XC_FETCH_CONSTANT: // {{{
|
||
|
if ($op1['op_type'] == XC_IS_CONST) {
|
||
|
$resvar = $op1['u.constant'];
|
||
|
}
|
||
|
else if ($op1['op_type'] == XC_IS_UNUSED) {
|
||
|
$resvar = $op2['u.constant'];
|
||
|
}
|
||
|
else {
|
||
|
$class = $T[$op1['u.var']];
|
||
|
assert($class[0] == 'class');
|
||
|
$resvar = $class[1] . '::' . $op2['u.constant'];
|
||
|
}
|
||
|
break;
|
||
|
// }}}
|
||
|
// {{{ case XC_FETCH_*
|
||
|
case XC_FETCH_R:
|
||
|
case XC_FETCH_W:
|
||
|
case XC_FETCH_RW:
|
||
|
case XC_FETCH_FUNC_ARG:
|
||
|
case XC_FETCH_UNSET:
|
||
|
case XC_FETCH_IS:
|
||
|
case XC_UNSET_VAR:
|
||
|
$rvalue = $this->getOpVal($op1, $EX);
|
||
|
$fetchtype = $op2[PHP_VERSION < 5 ? 'u.fetch_type' : 'u.EA.type'];
|
||
|
switch ($fetchtype) {
|
||
|
case ZEND_FETCH_STATIC_MEMBER:
|
||
|
$class = $this->getOpVal($op2, $EX);
|
||
|
$rvalue = $class . '::$' . $this->unquoteName($rvalue);
|
||
|
break;
|
||
|
default:
|
||
|
$name = $this->unquoteName($rvalue);
|
||
|
$globalname = xcache_is_autoglobal($name) ? "\$$name" : "\$GLOBALS[$rvalue]";
|
||
|
$rvalue = new Decompiler_Fetch($rvalue, $fetchtype, $globalname);
|
||
|
break;
|
||
|
}
|
||
|
if ($opc == XC_UNSET_VAR) {
|
||
|
$op['php'] = "unset(" . str($rvalue) . ")";
|
||
|
$lastphpop = &$op;
|
||
|
}
|
||
|
else if ($res['op_type'] != XC_IS_UNUSED) {
|
||
|
$resvar = $rvalue;
|
||
|
}
|
||
|
break;
|
||
|
// }}}
|
||
|
// {{{ case XC_FETCH_DIM_*
|
||
|
case XC_FETCH_DIM_TMP_VAR:
|
||
|
case XC_FETCH_DIM_R:
|
||
|
case XC_FETCH_DIM_W:
|
||
|
case XC_FETCH_DIM_RW:
|
||
|
case XC_FETCH_DIM_FUNC_ARG:
|
||
|
case XC_FETCH_DIM_UNSET:
|
||
|
case XC_FETCH_DIM_IS:
|
||
|
case XC_ASSIGN_DIM:
|
||
|
case XC_UNSET_DIM:
|
||
|
case XC_UNSET_DIM_OBJ:
|
||
|
$src = $this->getOpVal($op1, $EX, false);
|
||
|
if (is_a($src, "Decompiler_ForeachBox")) {
|
||
|
$src->iskey = $this->getOpVal($op2, $EX);
|
||
|
$resvar = $src;
|
||
|
break;
|
||
|
}
|
||
|
else if (is_a($src, "Decompiler_DimBox")) {
|
||
|
$dimbox = $src;
|
||
|
}
|
||
|
else {
|
||
|
if (!is_a($src, "Decompiler_ListBox")) {
|
||
|
$list = new Decompiler_List($this->getOpVal($op1, $EX, false));
|
||
|
|
||
|
$src = new Decompiler_ListBox($list);
|
||
|
if (!isset($op1['u.var'])) {
|
||
|
$this->dumpop($op, $EX);
|
||
|
var_dump($op);
|
||
|
die('missing u.var');
|
||
|
}
|
||
|
$T[$op1['u.var']] = $src;
|
||
|
unset($list);
|
||
|
}
|
||
|
$dim = new Decompiler_Dim($src);
|
||
|
$src->obj->dims[] = &$dim;
|
||
|
|
||
|
$dimbox = new Decompiler_DimBox($dim);
|
||
|
}
|
||
|
$dim = &$dimbox->obj;
|
||
|
$dim->offsets[] = $this->getOpVal($op2, $EX);
|
||
|
if ($ext == ZEND_FETCH_ADD_LOCK) {
|
||
|
$src->obj->everLocked = true;
|
||
|
}
|
||
|
else if ($ext == ZEND_FETCH_STANDARD) {
|
||
|
$dim->isLast = true;
|
||
|
}
|
||
|
unset($dim);
|
||
|
$rvalue = $dimbox;
|
||
|
|
||
|
if ($opc == XC_ASSIGN_DIM) {
|
||
|
$lvalue = $rvalue;
|
||
|
++ $i;
|
||
|
$rvalue = $this->getOpVal($opcodes[$i]['op1'], $EX);
|
||
|
$resvar = str($lvalue) . ' = ' . $rvalue;
|
||
|
}
|
||
|
else if ($opc == XC_UNSET_DIM) {
|
||
|
$op['php'] = "unset(" . str($rvalue) . ")";
|
||
|
$lastphpop = &$op;
|
||
|
}
|
||
|
else if ($res['op_type'] != XC_IS_UNUSED) {
|
||
|
$resvar = $rvalue;
|
||
|
}
|
||
|
break;
|
||
|
// }}}
|
||
|
case XC_ASSIGN: // {{{
|
||
|
$lvalue = $this->getOpVal($op1, $EX);
|
||
|
$rvalue = $this->getOpVal($op2, $EX, false);
|
||
|
if (is_a($rvalue, 'Decompiler_ForeachBox')) {
|
||
|
$type = $rvalue->iskey ? 'fe_key' : 'fe_as';
|
||
|
$rvalue->obj[$type] = $lvalue;
|
||
|
unset($T[$op2['u.var']]);
|
||
|
break;
|
||
|
}
|
||
|
if (is_a($rvalue, "Decompiler_DimBox")) {
|
||
|
$dim = &$rvalue->obj;
|
||
|
$dim->assign = $lvalue;
|
||
|
if ($dim->isLast) {
|
||
|
$resvar = str($dim->value);
|
||
|
}
|
||
|
unset($dim);
|
||
|
break;
|
||
|
}
|
||
|
$resvar = "$lvalue = " . str($rvalue, $EX);
|
||
|
break;
|
||
|
// }}}
|
||
|
case XC_ASSIGN_REF: // {{{
|
||
|
$lvalue = $this->getOpVal($op1, $EX);
|
||
|
$rvalue = $this->getOpVal($op2, $EX, false);
|
||
|
if (is_a($rvalue, 'Decompiler_Fetch')) {
|
||
|
$src = str($rvalue->src);
|
||
|
if (substr($src, 1, -1) == substr($lvalue, 1)) {
|
||
|
switch ($rvalue->fetchType) {
|
||
|
case ZEND_FETCH_GLOBAL:
|
||
|
$resvar = 'global ' . $lvalue;
|
||
|
break 2;
|
||
|
case ZEND_FETCH_STATIC:
|
||
|
$statics = &$EX['op_array']['static_variables'];
|
||
|
$resvar = 'static ' . $lvalue;
|
||
|
$name = substr($src, 1, -1);
|
||
|
if (isset($statics[$name])) {
|
||
|
$var = $statics[$name];
|
||
|
$resvar .= ' = ';
|
||
|
$resvar .= str(value($var), $EX);
|
||
|
}
|
||
|
unset($statics);
|
||
|
break 2;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
$rvalue = str($rvalue);
|
||
|
$resvar = "$lvalue = &$rvalue";
|
||
|
break;
|
||
|
// }}}
|
||
|
// {{{ case XC_FETCH_OBJ_*
|
||
|
case XC_FETCH_OBJ_R:
|
||
|
case XC_FETCH_OBJ_W:
|
||
|
case XC_FETCH_OBJ_RW:
|
||
|
case XC_FETCH_OBJ_FUNC_ARG:
|
||
|
case XC_FETCH_OBJ_UNSET:
|
||
|
case XC_FETCH_OBJ_IS:
|
||
|
case XC_ASSIGN_OBJ:
|
||
|
$obj = $this->getOpVal($op1, $EX);
|
||
|
if (!isset($obj)) {
|
||
|
$obj = '$this';
|
||
|
}
|
||
|
$prop = $this->getOpVal($op2, $EX);
|
||
|
if (preg_match($this->rQuotedName, $prop)) {
|
||
|
$prop = substr($prop, 1, -1);;
|
||
|
$rvalue = "{$obj}->$prop";
|
||
|
}
|
||
|
else {
|
||
|
$rvalue = "{$obj}->{" . "$prop}";
|
||
|
}
|
||
|
if ($res['op_type'] != XC_IS_UNUSED) {
|
||
|
$resvar = $rvalue;
|
||
|
}
|
||
|
if ($opc == XC_ASSIGN_OBJ) {
|
||
|
++ $i;
|
||
|
$lvalue = $rvalue;
|
||
|
$rvalue = $this->getOpVal($opcodes[$i]['op1'], $EX);
|
||
|
$resvar = "$lvalue = $rvalue";
|
||
|
}
|
||
|
break;
|
||
|
// }}}
|
||
|
case XC_ISSET_ISEMPTY_DIM_OBJ:
|
||
|
case XC_ISSET_ISEMPTY_PROP_OBJ:
|
||
|
case XC_ISSET_ISEMPTY:
|
||
|
case XC_ISSET_ISEMPTY_VAR: // {{{
|
||
|
if ($opc == XC_ISSET_ISEMPTY_VAR) {
|
||
|
$rvalue = $this->getOpVal($op1, $EX);;
|
||
|
if (preg_match($this->rQuotedName, $rvalue)) {
|
||
|
$rvalue = '$' . substr($rvalue, 1, -1);
|
||
|
}
|
||
|
else {
|
||
|
$rvalue = "${" . $rvalue . "}";
|
||
|
}
|
||
|
if ($op2['u.EA.type'] == ZEND_FETCH_STATIC_MEMBER) {
|
||
|
$class = $this->getOpVal($op2, $EX);
|
||
|
$rvalue = $class . '::' . $rvalue;
|
||
|
}
|
||
|
}
|
||
|
else if ($opc == XC_ISSET_ISEMPTY) {
|
||
|
$rvalue = $this->getOpVal($op1, $EX);
|
||
|
}
|
||
|
else {
|
||
|
$container = $this->getOpVal($op1, $EX);
|
||
|
$dim = $this->getOpVal($op2, $EX);
|
||
|
$rvalue = $container . "[$dim]";
|
||
|
}
|
||
|
|
||
|
switch (PHP_VERSION < 5 ? $op['op2']['u.var'] /* u.constant */ : $ext) {
|
||
|
case ZEND_ISSET:
|
||
|
$rvalue = "isset($rvalue)";
|
||
|
break;
|
||
|
case ZEND_ISEMPTY:
|
||
|
$rvalue = "empty($rvalue)";
|
||
|
break;
|
||
|
default:
|
||
|
$this->dumpop($op, $EX);
|
||
|
die_error('1');
|
||
|
}
|
||
|
$resvar = $rvalue;
|
||
|
break;
|
||
|
// }}}
|
||
|
case XC_SEND_VAR_NO_REF:
|
||
|
case XC_SEND_VAL:
|
||
|
case XC_SEND_REF:
|
||
|
case XC_SEND_VAR: // {{{
|
||
|
$ref = ($opc == XC_SEND_REF ? '&' : '');
|
||
|
$EX['argstack'][] = $ref . $this->getOpVal($op1, $EX);
|
||
|
break;
|
||
|
// }}}
|
||
|
case XC_INIT_METHOD_CALL:
|
||
|
case XC_INIT_FCALL_BY_FUNC:
|
||
|
case XC_INIT_FCALL_BY_NAME: // {{{
|
||
|
if (($ext & ZEND_CTOR_CALL)) {
|
||
|
break;
|
||
|
}
|
||
|
array_push($EX['arg_types_stack'], array($EX['object'], $EX['fbc']));
|
||
|
if ($opc == XC_INIT_METHOD_CALL || $op1['op_type'] != XC_IS_UNUSED) {
|
||
|
$obj = $this->getOpVal($op1, $EX);
|
||
|
if (!isset($obj)) {
|
||
|
$obj = '$this';
|
||
|
}
|
||
|
$EX['object'] = $obj;
|
||
|
if ($res['op_type'] != XC_IS_UNUSED) {
|
||
|
$resvar = '$obj call$';
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
$EX['object'] = null;
|
||
|
}
|
||
|
|
||
|
if ($opc == XC_INIT_FCALL_BY_FUNC) {
|
||
|
$which = $op1['u.var'];
|
||
|
$EX['fbc'] = $EX['op_array']['funcs'][$which]['name'];
|
||
|
}
|
||
|
else {
|
||
|
$EX['fbc'] = $this->getOpVal($op2, $EX, false);
|
||
|
}
|
||
|
break;
|
||
|
// }}}
|
||
|
case XC_DO_FCALL_BY_FUNC:
|
||
|
$which = $op1['u.var'];
|
||
|
$fname = $EX['op_array']['funcs'][$which]['name'];
|
||
|
$args = $this->popargs($EX, $ext);
|
||
|
$resvar = $fname . "($args)";
|
||
|
break;
|
||
|
case XC_DO_FCALL:
|
||
|
$fname = $this->unquoteName($this->getOpVal($op1, $EX, false));
|
||
|
$args = $this->popargs($EX, $ext);
|
||
|
$resvar = $fname . "($args)";
|
||
|
break;
|
||
|
case XC_DO_FCALL_BY_NAME: // {{{
|
||
|
$object = null;
|
||
|
|
||
|
$fname = $this->unquoteName($EX['fbc']);
|
||
|
if (!is_int($EX['object'])) {
|
||
|
$object = $EX['object'];
|
||
|
}
|
||
|
|
||
|
$args = $this->popargs($EX, $ext);
|
||
|
|
||
|
$resvar =
|
||
|
(isset($object) ? $object . '->' : '' )
|
||
|
. $fname . "($args)";
|
||
|
unset($args);
|
||
|
|
||
|
if (is_int($EX['object'])) {
|
||
|
$T[$EX['object']] = $resvar;
|
||
|
$resvar = null;
|
||
|
}
|
||
|
list($EX['object'], $EX['fbc']) = array_pop($EX['arg_types_stack']);
|
||
|
break;
|
||
|
// }}}
|
||
|
case XC_VERIFY_ABSTRACT_CLASS: // {{{
|
||
|
//unset($T[$op1['u.var']]);
|
||
|
break;
|
||
|
// }}}
|
||
|
case XC_DECLARE_CLASS:
|
||
|
case XC_DECLARE_INHERITED_CLASS: // {{{
|
||
|
$key = $op1['u.constant'];
|
||
|
$class = &$this->dc['class_table'][$key];
|
||
|
if (!isset($class)) {
|
||
|
echo 'class not found: ' . $key;
|
||
|
exit;
|
||
|
}
|
||
|
$class['name'] = $this->unquoteName($this->getOpVal($op2, $EX));
|
||
|
if ($opc == XC_DECLARE_INHERITED_CLASS) {
|
||
|
$ext /= XC_SIZEOF_TEMP_VARIABLE;
|
||
|
$class['parent'] = $T[$ext];
|
||
|
unset($T[$ext]);
|
||
|
}
|
||
|
else {
|
||
|
$class['parent'] = null;
|
||
|
}
|
||
|
|
||
|
while ($i + 2 < $ic
|
||
|
&& $opcodes[$i + 2]['opcode'] == XC_ADD_INTERFACE
|
||
|
&& $opcodes[$i + 2]['op1']['u.var'] == $res['u.var']
|
||
|
&& $opcodes[$i + 1]['opcode'] == XC_FETCH_CLASS) {
|
||
|
$fetchop = &$opcodes[$i + 1];
|
||
|
$impl = $this->unquoteName($this->getOpVal($fetchop['op2'], $EX));
|
||
|
$addop = &$opcodes[$i + 2];
|
||
|
$class['interfaces'][$addop['extended_value']] = $impl;
|
||
|
unset($fetchop, $addop);
|
||
|
$i += 2;
|
||
|
}
|
||
|
$this->dclass($class);
|
||
|
unset($class);
|
||
|
break;
|
||
|
// }}}
|
||
|
case XC_INIT_STRING: // {{{
|
||
|
$resvar = "''";
|
||
|
break;
|
||
|
// }}}
|
||
|
case XC_ADD_CHAR:
|
||
|
case XC_ADD_STRING:
|
||
|
case XC_ADD_VAR: // {{{
|
||
|
$op1val = $this->getOpVal($op1, $EX);
|
||
|
$op2val = $this->getOpVal($op2, $EX);
|
||
|
switch ($opc) {
|
||
|
case XC_ADD_CHAR:
|
||
|
$op2val = str(chr($op2val), $EX);
|
||
|
break;
|
||
|
case XC_ADD_STRING:
|
||
|
$op2val = str($op2val, $EX);
|
||
|
break;
|
||
|
case XC_ADD_VAR:
|
||
|
break;
|
||
|
}
|
||
|
if ($op1val == "''") {
|
||
|
$rvalue = $op2val;
|
||
|
}
|
||
|
else if ($op2val == "''") {
|
||
|
$rvalue = $op1val;
|
||
|
}
|
||
|
else {
|
||
|
$rvalue = $op1val . ' . ' . $op2val;
|
||
|
}
|
||
|
$resvar = $rvalue;
|
||
|
// }}}
|
||
|
break;
|
||
|
case XC_PRINT: // {{{
|
||
|
$op1val = $this->getOpVal($op1, $EX);
|
||
|
$resvar = "print($op1val)";
|
||
|
break;
|
||
|
// }}}
|
||
|
case XC_ECHO: // {{{
|
||
|
$op1val = $this->getOpVal($op1, $EX);
|
||
|
$resvar = "echo $op1val";
|
||
|
break;
|
||
|
// }}}
|
||
|
case XC_EXIT: // {{{
|
||
|
$op1val = $this->getOpVal($op1, $EX);
|
||
|
$resvar = "exit($op1val)";
|
||
|
break;
|
||
|
// }}}
|
||
|
case XC_INIT_ARRAY:
|
||
|
case XC_ADD_ARRAY_ELEMENT: // {{{
|
||
|
$rvalue = $this->getOpVal($op1, $EX, false, true);
|
||
|
|
||
|
if ($opc == XC_ADD_ARRAY_ELEMENT) {
|
||
|
$offset = $this->getOpVal($op2, $EX);
|
||
|
if (isset($offset)) {
|
||
|
$T[$res['u.var']]->value[$offset] = $rvalue;
|
||
|
}
|
||
|
else {
|
||
|
$T[$res['u.var']]->value[] = $rvalue;
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
if ($opc == XC_INIT_ARRAY) {
|
||
|
$resvar = new Decompiler_Array();
|
||
|
if (!isset($rvalue)) {
|
||
|
continue;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
$offset = $this->getOpVal($op2, $EX);
|
||
|
if (isset($offset)) {
|
||
|
$resvar->value[$offset] = $rvalue;
|
||
|
}
|
||
|
else {
|
||
|
$resvar->value[] = $rvalue;
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
// }}}
|
||
|
case XC_QM_ASSIGN: // {{{
|
||
|
$resvar = $this->getOpVal($op1, $EX);
|
||
|
break;
|
||
|