Index: /trunk/lib/Decompiler.class.php
===================================================================
--- /trunk/lib/Decompiler.class.php	(revision 982)
+++ /trunk/lib/Decompiler.class.php	(revision 982)
@@ -0,0 +1,2817 @@
+<?php
+
+define('INDENT', "\t");
+ini_set('error_reporting', E_ALL);
+
+function color($str, $color = 33)
+{
+	return "\x1B[{$color}m$str\x1B[0m";
+}
+
+function str($code, $indent = '') // {{{
+{
+	if (is_array($code)) {
+		$array = array();
+		foreach ($code as $key => $value) {
+			$array[$key] = str($value, $indent);
+		}
+		return $array;
+	}
+	if (is_object($code)) {
+		$code = foldToCode($code, $indent);
+		return $code->toCode($indent);
+	}
+
+	return (string) $code;
+}
+// }}}
+function foldToCode($src, $indent = '') // {{{ wrap or rewrap anything to Decompiler_Code
+{
+	if (is_array($indent)) {
+		$indent = $indent['indent'];
+	}
+
+	if (!is_object($src)) {
+		return new Decompiler_Code($src);
+	}
+
+	if (!method_exists($src, 'toCode')) {
+		var_dump($src);
+		exit('no toCode');
+	}
+	if (get_class($src) != 'Decompiler_Code') {
+		// rewrap it
+		$src = new Decompiler_Code($src->toCode($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_a($value, 'Decompiler_Object')) {
+		// use as is
+	}
+	else if (is_array($value)) {
+		$value = new Decompiler_ConstArray($value);
+	}
+	else {
+		$value = new Decompiler_Value($value);
+	}
+	return $value;
+}
+// }}}
+function unquoteName_($str, $asVariableName, $indent = '') // {{{
+{
+	$str = str($str, $indent);
+	if (preg_match("!^'[\\w_][\\w\\d_\\\\]*'\$!", $str)) {
+		return str_replace('\\\\', '\\', substr($str, 1, -1));
+	}
+	else if ($asVariableName) {
+		return "{" . $str . "}";
+	}
+	else {
+		return $str;
+	}
+}
+// }}}
+function unquoteVariableName($str, $indent = '') // {{{
+{
+	return unquoteName_($str, true, $indent);
+}
+// }}}
+function unquoteName($str, $indent = '') // {{{
+{
+	return unquoteName_($str, false, $indent);
+}
+// }}}
+class Decompiler_Object // {{{
+{
+}
+// }}}
+class Decompiler_Value extends Decompiler_Object // {{{
+{
+	var $value;
+
+	function Decompiler_Value($value = null)
+	{
+		$this->value = $value;
+	}
+
+	function toCode($indent)
+	{
+		$code = var_export($this->value, true);
+		if (gettype($this->value) == 'string') {
+			switch ($this->value) {
+			case "\r":
+				return '"\\r"';
+			case "\n":
+				return '"\\n"';
+			case "\r\n":
+				return '"\\r\\n"';
+			}
+			$code = str_replace("\r\n", '\' . "\\r\\n" . \'', $code);
+			$code = str_replace("\r", '\' . "\\r" . \'', $code);
+			$code = str_replace("\n", '\' . "\\n" . \'', $code);
+		}
+		return $code;
+	}
+}
+// }}}
+class Decompiler_Code extends Decompiler_Object // {{{
+{
+	var $src;
+
+	function Decompiler_Code($src)
+	{
+		assert('isset($src)');
+		$this->src = $src;
+	}
+
+	function toCode($indent)
+	{
+		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 toCode($indent)
+	{
+		$opstr = $this->parent->binops[$this->opc];
+
+		if (is_a($this->op1, 'Decompiler_TriOp') || is_a($this->op1, 'Decompiler_Binop') && $this->op1->opc != $this->opc) {
+			$op1 = "(" . str($this->op1, $indent) . ")";
+		}
+		else {
+			$op1 = $this->op1;
+		}
+
+		if (is_a($this->op2, 'Decompiler_TriOp') || is_a($this->op2, 'Decompiler_Binop') && $this->op2->opc != $this->opc && substr($opstr, -1) != '=') {
+			$op2 = "(" . str($this->op2, $indent) . ")";
+		}
+		else {
+			$op2 = $this->op2;
+		}
+
+		if (str($op1) == '0' && ($this->opc == XC_ADD || $this->opc == XC_SUB)) {
+			return $opstr . str($op2, $indent);
+		}
+
+		return str($op1, $indent) . ' ' . $opstr . ($this->opc == XC_ASSIGN_REF ? '' : ' ') . str($op2, $indent);
+	}
+}
+// }}}
+class Decompiler_TriOp extends Decompiler_Code // {{{
+{
+	var $condition;
+	var $trueValue;
+	var $falseValue;
+
+	function Decompiler_TriOp($condition, $trueValue, $falseValue)
+	{
+		$this->condition = $condition;
+		$this->trueValue = $trueValue;
+		$this->falseValue = $falseValue;
+	}
+
+	function toCode($indent)
+	{
+		$trueValue = $this->trueValue;
+		if (is_a($this->trueValue, 'Decompiler_TriOp')) {
+			$trueValue = "(" . str($trueValue, $indent) . ")";
+		}
+		$falseValue = $this->falseValue;
+		if (is_a($this->falseValue, 'Decompiler_TriOp')) {
+			$falseValue = "(" . str($falseValue, $indent) . ")";
+		}
+
+		return str($this->condition) . ' ? ' . str($trueValue) . ' : ' . str($falseValue);
+	}
+}
+// }}}
+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 toCode($indent)
+	{
+		switch ($this->fetchType) {
+		case ZEND_FETCH_LOCAL:
+			return '$' . substr($this->src, 1, -1);
+		case ZEND_FETCH_STATIC:
+			if (ZEND_ENGINE_2_3) {
+				// closure local variable?
+				return str($this->src);
+			}
+			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 toCode($indent)
+	{
+		return $this->obj->toCode($indent);
+	}
+}
+// }}}
+class Decompiler_Dim extends Decompiler_Value // {{{
+{
+	var $offsets = array();
+	var $isLast = false;
+	var $isObject = false;
+	var $assign = null;
+
+	function toCode($indent)
+	{
+		if (is_a($this->value, 'Decompiler_ListBox')) {
+			$exp = str($this->value->obj->src, $indent);
+		}
+		else {
+			$exp = str($this->value, $indent);
+		}
+		$last = count($this->offsets) - 1;
+		foreach ($this->offsets as $i => $dim) {
+			if ($this->isObject && $i == $last) {
+				$exp .= '->' . unquoteVariableName($dim, $indent);
+			}
+			else {
+				$exp .= '[' . str($dim, $indent) . ']';
+			}
+		}
+		return $exp;
+	}
+}
+// }}}
+class Decompiler_DimBox extends Decompiler_Box // {{{
+{
+}
+// }}}
+class Decompiler_List extends Decompiler_Code // {{{
+{
+	var $src;
+	var $dims = array();
+	var $everLocked = false;
+
+	function toCode($indent)
+	{
+		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, $indent);
+			}
+			return str($this->dims[0]->assign, $indent) . ' = ' . str($dim, $indent);
+		}
+		/* flatten dims */
+		$assigns = array();
+		foreach ($this->dims as $dim) {
+			$assign = &$assigns;
+			foreach ($dim->offsets as $offset) {
+				$assign = &$assign[$offset];
+			}
+			$assign = foldToCode($dim->assign, $indent);
+		}
+		return str($this->toList($assigns)) . ' = ' . str($this->src, $indent);
+	}
+
+	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 // {{{
+{
+	// emenets
+	function Decompiler_Array()
+	{
+		$this->value = array();
+	}
+
+	function toCode($indent)
+	{
+		$subindent = $indent . INDENT;
+
+		$elementsCode = array();
+		$index = 0;
+		foreach ($this->value as $element) {
+			list($key, $value) = $element;
+			if (!isset($key)) {
+				$key = $index++;
+			}
+			$elementsCode[] = array(str($key, $subindent), str($value, $subindent), $key, $value);
+		}
+
+		$exp = "array(";
+		$indent = $indent . INDENT;
+		$assocWidth = 0;
+		$multiline = 0;
+		$i = 0;
+		foreach ($elementsCode as $element) {
+			list($keyCode, $valueCode) = $element;
+			if ((string) $i !== $keyCode) {
+				$assocWidth = 1;
+				break;
+			}
+			++$i;
+		}
+		foreach ($elementsCode as $element) {
+			list($keyCode, $valueCode, $key, $value) = $element;
+			if ($assocWidth) {
+				$len = strlen($keyCode);
+				if ($assocWidth < $len) {
+					$assocWidth = $len;
+				}
+			}
+			if (is_array($value) || is_a($value, 'Decompiler_Array')) {
+				$multiline ++;
+			}
+		}
+
+		$i = 0;
+		foreach ($elementsCode as $element) {
+			list($keyCode, $value) = $element;
+			if ($multiline) {
+				if ($i) {
+					$exp .= ",";
+				}
+				$exp .= "\n";
+				$exp .= $indent;
+			}
+			else {
+				if ($i) {
+					$exp .= ", ";
+				}
+			}
+
+			if ($assocWidth) {
+				if ($multiline) {
+					$exp .= sprintf("%-{$assocWidth}s => ", $keyCode);
+				}
+				else {
+					$exp .= $keyCode . ' => ';
+				}
+			}
+
+			$exp .= $value;
+
+			$i ++;
+		}
+		if ($multiline) {
+			$exp .= "\n$indent)";
+		}
+		else {
+			$exp .= ")";
+		}
+		return $exp;
+	}
+}
+// }}}
+class Decompiler_ConstArray extends Decompiler_Array // {{{
+{
+	function Decompiler_ConstArray($array)
+	{
+		$elements = array();
+		foreach ($array as $key => $value) {
+			$elements[] = array(value($key), value($value));
+		}
+		$this->value = $elements;
+	}
+}
+// }}}
+class Decompiler_ForeachBox extends Decompiler_Box // {{{
+{
+	var $iskey;
+
+	function toCode($indent)
+	{
+		return 'foreach (' . '';
+	}
+}
+// }}}
+
+class Decompiler
+{
+	var $namespace;
+	var $namespaceDecided;
+
+	function Decompiler()
+	{
+		// {{{ testing
+		// XC_UNDEF XC_OP_DATA
+		$this->test = !empty($_ENV['XCACHE_DECOMPILER_TEST']);
+		$this->usedOps = array();
+
+		if ($this->test) {
+			$content = file_get_contents(__FILE__);
+			for ($i = 0; $opname = xcache_get_opcode($i); $i ++) {
+				if (!preg_match("/\\bXC_" . $opname . "\\b(?!')/", $content)) {
+					echo "not recognized opcode ", $opname, "\n";
+				}
+			}
+		}
+		// }}}
+		// {{{ 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",
+				XC_ASSIGN              => "=",
+				XC_ASSIGN_REF          => "= &",
+				XC_JMP_SET             => "?:",
+				XC_JMPZ_EX             => "&&",
+				XC_JMPNZ_EX            => "||",
+				);
+		// }}}
+		$this->includeTypes = array( // {{{
+				ZEND_EVAL         => 'eval',
+				ZEND_INCLUDE      => 'include',
+				ZEND_INCLUDE_ONCE => 'include_once',
+				ZEND_REQUIRE      => 'require',
+				ZEND_REQUIRE_ONCE => 'require_once',
+				);
+				// }}}
+	}
+	function detectNamespace($name) // {{{
+	{
+		if ($this->namespaceDecided) {
+			return;
+		}
+
+		if (strpos($name, '\\') !== false) {
+			$this->namespace = strtok($name, '\\');
+			echo 'namespace ', $this->namespace, ";\n\n";
+		}
+
+		$this->namespaceDecided = true;
+	}
+	// }}}
+	function stripNamespace($name) // {{{
+	{
+		$len = strlen($this->namespace) + 1;
+		if (substr($name, 0, $len) == $this->namespace . '\\') {
+			return substr($name, $len);
+		}
+		else {
+			return $name;
+		}
+	}
+	// }}}
+	function outputPhp(&$EX, $range) // {{{
+	{
+		$needBlankline = isset($EX['lastBlock']);
+		$indent = $EX['indent'];
+		$curticks = 0;
+		for ($i = $range[0]; $i <= $range[1]; $i ++) {
+			$op = $EX['opcodes'][$i];
+			if (isset($op['gofrom'])) {
+				if ($needBlankline) {
+					$needBlankline = false;
+					echo PHP_EOL;
+				}
+				echo 'label' . $i, ":\n";
+			}
+			if (isset($op['php'])) {
+				$toticks = isset($op['ticks']) ? (int) str($op['ticks']) : 0;
+				if ($curticks != $toticks) {
+					$oldticks = $curticks;
+					$curticks = $toticks;
+					if (!$curticks) {
+						echo $EX['indent'], "}\n\n";
+						$indent = $EX['indent'];
+					}
+					else {
+						if ($oldticks) {
+							echo $EX['indent'], "}\n\n";
+						}
+						else if (!$oldticks) {
+							$indent .= INDENT;
+						}
+						if ($needBlankline) {
+							$needBlankline = false;
+							echo PHP_EOL;
+						}
+						echo $EX['indent'], "declare (ticks=$curticks) {\n";
+					}
+				}
+				if ($needBlankline) {
+					$needBlankline = false;
+					echo PHP_EOL;
+				}
+				echo $indent, str($op['php'], $indent), ";\n";
+				$EX['lastBlock'] = 'basic';
+			}
+		}
+		if ($curticks) {
+			echo $EX['indent'], "}\n";
+		}
+	}
+	// }}}
+	function getOpVal($op, &$EX, $free = false) // {{{
+	{
+		switch ($op['op_type']) {
+		case XC_IS_CONST:
+			return value($op['constant']);
+
+		case XC_IS_VAR:
+		case XC_IS_TMP_VAR:
+			$T = &$EX['Ts'];
+			$ret = $T[$op['var']];
+			if ($free && empty($this->keepTs)) {
+				unset($T[$op['var']]);
+			}
+			return $ret;
+
+		case XC_IS_CV:
+			$var = $op['var'];
+			$var = $EX['op_array']['vars'][$var];
+			return '$' . $var['name'];
+
+		case XC_IS_UNUSED:
+			return null;
+		}
+	}
+	// }}}
+	function removeKeyPrefix($array, $prefix) // {{{
+	{
+		$prefixLen = strlen($prefix);
+		$ret = array();
+		foreach ($array as $key => $value) {
+			if (substr($key, 0, $prefixLen) == $prefix) {
+				$key = substr($key, $prefixLen);
+			}
+			$ret[$key] = $value;
+		}
+		return $ret;
+	}
+	// }}}
+	function &fixOpcode($opcodes, $removeTailing = false, $defaultReturnValue = null) // {{{
+	{
+		$last = count($opcodes) - 1;
+		for ($i = 0; $i <= $last; $i ++) {
+			if (function_exists('xcache_get_fixed_opcode')) {
+				$opcodes[$i]['opcode'] = xcache_get_fixed_opcode($opcodes[$i]['opcode'], $i);
+			}
+			if (isset($opcodes[$i]['op1'])) {
+				$opcodes[$i]['op1'] = $this->removeKeyPrefix($opcodes[$i]['op1'], 'u.');
+				$opcodes[$i]['op2'] = $this->removeKeyPrefix($opcodes[$i]['op2'], 'u.');
+				$opcodes[$i]['result'] = $this->removeKeyPrefix($opcodes[$i]['result'], 'u.');
+			}
+			else {
+				$op = array(
+					'op1' => array(),
+					'op2' => array(),
+					'op3' => array(),
+				);
+				foreach ($opcodes[$i] as $name => $value) {
+					if (preg_match('!^(op1|op2|result)\\.(.*)!', $name, $m)) {
+						list(, $which, $field) = $m;
+						$op[$which][$field] = $value;
+					}
+					else if (preg_match('!^(op1|op2|result)_type$!', $name, $m)) {
+						list(, $which) = $m;
+						$op[$which]['op_type'] = $value;
+					}
+					else {
+						$op[$name] = $value;
+					}
+				}
+				$opcodes[$i] = $op;
+			}
+		}
+
+		if ($removeTailing) {
+			$last = count($opcodes) - 1;
+			if ($opcodes[$last]['opcode'] == XC_HANDLE_EXCEPTION) {
+				$this->usedOps[XC_HANDLE_EXCEPTION] = true;
+				$opcodes[$last]['opcode'] = XC_NOP;
+				--$last;
+			}
+			if ($opcodes[$last]['opcode'] == XC_RETURN) {
+				$op1 = $opcodes[$last]['op1'];
+				if ($op1['op_type'] == XC_IS_CONST && array_key_exists('constant', $op1) && $op1['constant'] === $defaultReturnValue) {
+					$opcodes[$last]['opcode'] = XC_NOP;
+					--$last;
+				}
+			}
+		}
+		return $opcodes;
+	}
+	// }}}
+	function decompileBasicBlock(&$EX, $range, $unhandled = false) // {{{
+	{
+		$this->dasmBasicBlock($EX, $range);
+		if ($unhandled) {
+			$this->dumpRange($EX, $range);
+		}
+		$this->outputPhp($EX, $range);
+	}
+	// }}}
+	function isIfCondition(&$EX, $range) // {{{
+	{
+		$opcodes = &$EX['opcodes'];
+		$firstOp = &$opcodes[$range[0]];
+		return $firstOp['opcode'] == XC_JMPZ && !empty($firstOp['jmpouts']) && $opcodes[$firstOp['jmpouts'][0] - 1]['opcode'] == XC_JMP
+		 && !empty($opcodes[$firstOp['jmpouts'][0] - 1]['jmpouts'])
+		 && $opcodes[$firstOp['jmpouts'][0] - 1]['jmpouts'][0] == $range[1] + 1;
+	}
+	// }}}
+	function removeJmpInfo(&$EX, $line) // {{{
+	{
+		$opcodes = &$EX['opcodes'];
+		foreach ($opcodes[$line]['jmpouts'] as $jmpTo) {
+			$jmpins = &$opcodes[$jmpTo]['jmpins'];
+			$jmpins = array_flip($jmpins);
+			unset($jmpins[$line]);
+			$jmpins = array_keys($jmpins);
+		}
+		// $opcodes[$line]['opcode'] = XC_NOP;
+		unset($opcodes[$line]['jmpouts']);
+	}
+	// }}}
+	function beginScope(&$EX, $doIndent = true) // {{{
+	{
+		array_push($EX['scopeStack'], array($EX['lastBlock'], $EX['indent']));
+		if ($doIndent) {
+			$EX['indent'] .= INDENT;
+		}
+		$EX['lastBlock'] = null;
+	}
+	// }}}
+	function endScope(&$EX) // {{{
+	{
+		list($EX['lastBlock'], $EX['indent']) = array_pop($EX['scopeStack']);
+	}
+	// }}}
+	function beginComplexBlock(&$EX) // {{{
+	{
+		if (isset($EX['lastBlock'])) {
+			echo PHP_EOL;
+			$EX['lastBlock'] = null;
+		}
+	}
+	// }}}
+	function endComplexBlock(&$EX) // {{{
+	{
+		$EX['lastBlock'] = 'complex';
+	}
+	// }}}
+	function decompileComplexBlock(&$EX, $range) // {{{
+	{
+		$T = &$EX['Ts'];
+		$opcodes = &$EX['opcodes'];
+		$indent = $EX['indent'];
+
+		$firstOp = &$opcodes[$range[0]];
+		$lastOp = &$opcodes[$range[1]];
+
+		// {{{ && || and or
+		if (($firstOp['opcode'] == XC_JMPZ_EX || $firstOp['opcode'] == XC_JMPNZ_EX) && !empty($firstOp['jmpouts'])
+		 && $firstOp['jmpouts'][0] == $range[1] + 1
+		 && $lastOp['opcode'] == XC_BOOL
+		 && $firstOp['opcode']['result']['var'] == $lastOp['opcode']['result']['var']
+		) {
+			$this->removeJmpInfo($EX, $range[0]);
+
+			$this->recognizeAndDecompileClosedBlocks($EX, array($range[0], $range[0]));
+			$op1 = $this->getOpVal($firstOp['result'], $EX, true);
+
+			$this->recognizeAndDecompileClosedBlocks($EX, array($range[0] + 1, $range[1]));
+			$op2 = $this->getOpVal($lastOp['result'], $EX, true);
+
+			$T[$firstOp['result']['var']] = new Decompiler_Binop($this, $op1, $firstOp['opcode'], $op2);
+			return false;
+		}
+		// }}}
+		// {{{ ?: excluding JMP_SET
+		if ($firstOp['opcode'] == XC_JMPZ && !empty($firstOp['jmpouts'])
+		 && $range[1] >= $range[0] + 3
+		 && $opcodes[$firstOp['jmpouts'][0] - 2]['opcode'] == XC_QM_ASSIGN
+		 && $opcodes[$firstOp['jmpouts'][0] - 1]['opcode'] == XC_JMP && $opcodes[$firstOp['jmpouts'][0] - 1]['jmpouts'][0] == $range[1] + 1
+		 && $lastOp['opcode'] == XC_QM_ASSIGN
+		) {
+			$trueRange = array($range[0] + 1, $firstOp['jmpouts'][0] - 2);
+			$falseRange = array($firstOp['jmpouts'][0], $range[1]);
+			$this->removeJmpInfo($EX, $range[0]);
+
+			$condition = $this->getOpVal($firstOp['op1'], $EX);
+			$this->recognizeAndDecompileClosedBlocks($EX, $trueRange);
+			$trueValue = $this->getOpVal($opcodes[$trueRange[1]]['result'], $EX, true);
+			$this->recognizeAndDecompileClosedBlocks($EX, $falseRange);
+			$falseValue = $this->getOpVal($opcodes[$falseRange[1]]['result'], $EX, true);
+			$T[$opcodes[$trueRange[1]]['result']['var']] = new Decompiler_TriOp($condition, $trueValue, $falseValue);
+			return false;
+		}
+		// }}}
+		// {{{ goto
+		if ($firstOp['opcode'] == XC_JMP && !empty($firstOp['jmpouts']) && $firstOp['jmpouts'][0] == $range[1] + 1) {
+			$this->removeJmpInfo($EX, $range[0]);
+			$firstOp['opcode'] = XC_GOTO;
+			$target = $firstOp['op1']['var'];
+			$firstOp['goto'] = $target;
+			$opcodes[$target]['gofrom'][] = $range[0];
+
+			$this->recognizeAndDecompileClosedBlocks($EX, $range);
+			return false;
+		}
+		// }}}
+		// {{{ for
+		if (!empty($firstOp['jmpins']) && $opcodes[$firstOp['jmpins'][0]]['opcode'] == XC_JMP
+		 && $lastOp['opcode'] == XC_JMP && !empty($lastOp['jmpouts']) && $lastOp['jmpouts'][0] <= $firstOp['jmpins'][0]
+		 && !empty($opcodes[$range[1] + 1]['jmpins']) && $opcodes[$opcodes[$range[1] + 1]['jmpins'][0]]['opcode'] == XC_JMPZNZ
+		) {
+			$nextRange = array($lastOp['jmpouts'][0], $firstOp['jmpins'][0]);
+			$conditionRange = array($range[0], $nextRange[0] - 1);
+			$this->removeJmpInfo($EX, $conditionRange[1]);
+			$bodyRange = array($nextRange[1], $range[1]);
+			$this->removeJmpInfo($EX, $bodyRange[1]);
+
+			$initial = '';
+			$this->beginScope($EX);
+			$this->dasmBasicBlock($EX, $conditionRange);
+			$conditionCodes = array();
+			for ($i = $conditionRange[0]; $i <= $conditionRange[1]; ++$i) {
+				if (isset($opcodes[$i]['php'])) {
+					$conditionCodes[] = str($opcodes[$i]['php'], $EX);
+				}
+			}
+			$conditionCodes[] = str($this->getOpVal($opcodes[$conditionRange[1]]['op1'], $EX), $EX);
+			if (implode(',', $conditionCodes) == 'true') {
+				$conditionCodes = array();
+			}
+			$this->endScope($EX);
+
+			$this->beginScope($EX);
+			$this->dasmBasicBlock($EX, $nextRange);
+			$nextCodes = array();
+			for ($i = $nextRange[0]; $i <= $nextRange[1]; ++$i) {
+				if (isset($opcodes[$i]['php'])) {
+					$nextCodes[] = str($opcodes[$i]['php'], $EX);
+				}
+			}
+			$this->endScope($EX);
+
+			$this->beginComplexBlock($EX);
+			echo $indent, 'for (', str($initial, $EX), '; ', implode(', ', $conditionCodes), '; ', implode(', ', $nextCodes), ') ', '{', PHP_EOL;
+			$this->beginScope($EX);
+			$this->recognizeAndDecompileClosedBlocks($EX, $bodyRange);
+			$this->endScope($EX);
+			echo $indent, '}', PHP_EOL;
+			$this->endComplexBlock($EX);
+			return;
+		}
+		// }}}
+		// {{{ if/elseif/else
+		if ($this->isIfCondition($EX, $range)) {
+			$this->beginComplexBlock($EX);
+			$isElseIf = false;
+			do {
+				$ifRange = array($range[0], $opcodes[$range[0]]['jmpouts'][0] - 1);
+				$this->removeJmpInfo($EX, $ifRange[0]);
+				$this->removeJmpInfo($EX, $ifRange[1]);
+				$condition = $this->getOpVal($opcodes[$ifRange[0]]['op1'], $EX);
+
+				echo $indent, $isElseIf ? 'else if' : 'if', ' (', str($condition, $EX), ') ', '{', PHP_EOL;
+				$this->beginScope($EX);
+				$this->recognizeAndDecompileClosedBlocks($EX, $ifRange);
+				$this->endScope($EX);
+				$EX['lastBlock'] = null;
+				echo $indent, '}', PHP_EOL;
+
+				$isElseIf = true;
+				// search for else if
+				$range[0] = $ifRange[1] + 1;
+				for ($i = $ifRange[1] + 1; $i <= $range[1]; ++$i) {
+					// find first jmpout
+					if (!empty($opcodes[$i]['jmpouts'])) {
+						if ($this->isIfCondition($EX, array($i, $range[1]))) {
+							$this->dasmBasicBlock($EX, array($range[0], $i));
+							$range[0] = $i;
+						}
+						break;
+					}
+				}
+			} while ($this->isIfCondition($EX, $range));
+			if ($ifRange[1] < $range[1]) {
+				$elseRange = array($ifRange[1], $range[1]);
+				echo $indent, 'else ', '{', PHP_EOL;
+				$this->beginScope($EX);
+				$this->recognizeAndDecompileClosedBlocks($EX, $elseRange);
+				$this->endScope($EX);
+				$EX['lastBlock'] = null;
+				echo $indent, '}', PHP_EOL;
+			}
+			$this->endComplexBlock($EX);
+			return;
+		}
+		if ($firstOp['opcode'] == XC_JMPZ && !empty($firstOp['jmpouts'])
+		 && $firstOp['jmpouts'][0] - 1 == $range[1] && $opcodes[$firstOp['jmpouts'][0] - 1]['opcode'] == XC_RETURN) {
+			$this->beginComplexBlock($EX);
+			$this->removeJmpInfo($EX, $range[0]);
+			$condition = $this->getOpVal($opcodes[$range[0]]['op1'], $EX);
+
+			echo $indent, 'if (', str($condition, $EX), ') ', '{', PHP_EOL;
+			$this->beginScope($EX);
+			$this->recognizeAndDecompileClosedBlocks($EX, $range);
+			$this->endScope($EX);
+			echo $indent, '}', PHP_EOL;
+			$this->endComplexBlock($EX);
+			return;
+		}
+		// }}}
+		// {{{ try/catch
+		if (!empty($firstOp['jmpins']) && !empty($opcodes[$firstOp['jmpins'][0]]['isCatchBegin'])) {
+			$catchBlocks = array();
+			$catchFirst = $firstOp['jmpins'][0];
+
+			$tryRange = array($range[0], $catchFirst - 1);
+
+			// search for XC_CATCH
+			$this->removeJmpInfo($EX, $catchFirst);
+			for ($i = $catchFirst; $i <= $range[1]; ) {
+				if ($opcodes[$i]['opcode'] == XC_CATCH) {
+					$catchOpLine = $i;
+					$this->removeJmpInfo($EX, $catchOpLine);
+
+					$catchNext = $opcodes[$catchOpLine]['extended_value'];
+					$catchBodyLast = $catchNext - 1;
+					if ($opcodes[$catchBodyLast]['opcode'] == XC_JMP) {
+						--$catchBodyLast;
+					}
+
+					$catchBlocks[$catchFirst] = array($catchOpLine, $catchBodyLast);
+
+					$i = $catchFirst = $catchNext;
+				}
+				else {
+					++$i;
+				}
+			}
+
+			if ($opcodes[$tryRange[1]]['opcode'] == XC_JMP) {
+				--$tryRange[1];
+			}
+
+			$this->beginComplexBlock($EX);
+			echo $indent, "try {", PHP_EOL;
+			$this->beginScope($EX);
+			$this->recognizeAndDecompileClosedBlocks($EX, $tryRange);
+			$this->endScope($EX);
+			echo $indent, '}', PHP_EOL;
+			foreach ($catchBlocks as $catchFirst => $catchInfo) {
+				list($catchOpLine, $catchBodyLast) = $catchInfo;
+				$catchBodyFirst = $catchOpLine + 1;
+				$this->dasmBasicBlock($EX, array($catchFirst, $catchOpLine));
+				$catchOp = &$opcodes[$catchOpLine];
+				echo $indent, 'catch (', str($this->getOpVal($catchOp['op1'], $EX)), ' ', str($this->getOpVal($catchOp['op2'], $EX)), ") {", PHP_EOL;
+				unset($catchOp);
+
+				$EX['lastBlock'] = null;
+				$this->beginScope($EX);
+				$this->recognizeAndDecompileClosedBlocks($EX, array($catchBodyFirst, $catchBodyLast));
+				$this->endScope($EX);
+				echo $indent, '}', PHP_EOL;
+			}
+			$this->endComplexBlock($EX);
+			return;
+		}
+		// }}}
+		// {{{ switch/case
+		if ($firstOp['opcode'] == XC_SWITCH_FREE && isset($T[$firstOp['op1']['var']])) {
+			// TODO: merge this code to CASE code. use SWITCH_FREE to detect begin of switch by using $Ts if possible
+			$this->beginComplexBlock($EX);
+			echo $indent, 'switch (', str($this->getOpVal($firstOp['op1'], $EX)), ") {", PHP_EOL;
+			echo $indent, '}', PHP_EOL;
+			$this->endComplexBlock($EX);
+			return;
+		}
+
+		if (
+			($firstOp['opcode'] == XC_CASE
+			|| $firstOp['opcode'] == XC_JMP && !empty($firstOp['jmpouts']) && $opcodes[$firstOp['jmpouts'][0]]['opcode'] == XC_CASE
+			)
+		 	 && !empty($lastOp['jmpouts'])
+		) {
+			$cases = array();
+			$caseDefault = null;
+			$caseOp = null;
+			for ($i = $range[0]; $i <= $range[1]; ) {
+				$op = $opcodes[$i];
+				if ($op['opcode'] == XC_CASE) {
+					if (!isset($caseOp)) {
+						$caseOp = $op;
+					}
+					$jmpz = $opcodes[$i + 1];
+					assert('$jmpz["opcode"] == XC_JMPZ');
+					$caseNext = $jmpz['jmpouts'][0];
+					$cases[$i] = $caseNext - 1;
+					$i = $caseNext;
+				}
+				else if ($op['opcode'] == XC_JMP && $op['jmpouts'][0] >= $i) {
+					// default
+					$caseNext = $op['jmpouts'][0];
+					$caseDefault = $i;
+					$cases[$i] = $caseNext - 1;
+					$i = $caseNext;
+				}
+				else {
+					++$i;
+				}
+			}
+
+			$this->beginComplexBlock($EX);
+
+			echo $indent, 'switch (', str($this->getOpVal($caseOp['op1'], $EX), $EX), ") {", PHP_EOL;
+			$caseIsOut = false;
+			foreach ($cases as $caseFirst => $caseLast) {
+				if ($caseIsOut && empty($lastCaseFall)) {
+					echo PHP_EOL;
+				}
+
+				$caseOp = $opcodes[$caseFirst];
+
+				echo $indent;
+				if ($caseOp['opcode'] == XC_CASE) {
+					echo 'case ';
+					echo str($this->getOpVal($caseOp['op2'], $EX), $EX);
+					echo ':', PHP_EOL;
+
+					$this->removeJmpInfo($EX, $caseFirst);
+					++$caseFirst;
+
+					assert('$opcodes[$caseFirst]["opcode"] == XC_JMPZ');
+					$this->removeJmpInfo($EX, $caseFirst);
+					++$caseFirst;
+				}
+				else {
+					echo 'default';
+					echo ':', PHP_EOL;
+
+					assert('$opcodes[$caseFirst]["opcode"] == XC_JMP');
+					$this->removeJmpInfo($EX, $caseFirst);
+					++$caseFirst;
+				}
+
+				assert('$opcodes[$caseLast]["opcode"] == XC_JMP');
+				$this->removeJmpInfo($EX, $caseLast);
+				--$caseLast;
+				switch ($opcodes[$caseLast]['opcode']) {
+				case XC_BRK:
+				case XC_CONT:
+				case XC_GOTO:
+					$lastCaseFall = false;
+					break;
+
+				default:
+					$lastCaseFall = true;
+				}
+
+				$this->beginScope($EX);
+				$this->recognizeAndDecompileClosedBlocks($EX, array($caseFirst, $caseLast));
+				$this->endScope($EX);
+				$caseIsOut = true;
+			}
+			echo $indent, '}', PHP_EOL;
+
+			$this->endComplexBlock($EX);
+			return;
+		}
+		// }}}
+		// {{{ do/while
+		if ($lastOp['opcode'] == XC_JMPNZ && !empty($lastOp['jmpouts'])
+		 && $lastOp['jmpouts'][0] == $range[0]) {
+			$this->removeJmpInfo($EX, $range[1]);
+			$this->beginComplexBlock($EX);
+
+			echo $indent, "do {", PHP_EOL;
+			$this->beginScope($EX);
+			$this->recognizeAndDecompileClosedBlocks($EX, $range);
+			$this->endScope($EX);
+			echo $indent, "} while (", str($this->getOpVal($lastOp['op1'], $EX)), ');', PHP_EOL;
+
+			$this->endComplexBlock($EX);
+			return;
+		}
+		// }}}
+
+		// {{{ search firstJmpOp
+		$firstJmp = null;
+		$firstJmpOp = null;
+		for ($i = $range[0]; $i <= $range[1]; ++$i) {
+			if (!empty($opcodes[$i]['jmpouts'])) {
+				$firstJmp = $i;
+				$firstJmpOp = &$opcodes[$firstJmp];
+				break;
+			}
+		}
+		// }}}
+
+		// {{{ while
+		if (isset($firstJmpOp)
+		 && $firstJmpOp['opcode'] == XC_JMPZ
+		 && $firstJmpOp['jmpouts'][0] > $range[1]
+		 && $lastOp['opcode'] == XC_JMP && !empty($lastOp['jmpouts'])
+		 && $lastOp['jmpouts'][0] == $range[0]) {
+			$this->removeJmpInfo($EX, $firstJmp);
+			$this->removeJmpInfo($EX, $range[1]);
+			$this->beginComplexBlock($EX);
+
+			ob_start();
+			$this->beginScope($EX);
+			$this->recognizeAndDecompileClosedBlocks($EX, $range);
+			$this->endScope($EX);
+			$body = ob_get_clean();
+
+			echo $indent, 'while (', str($this->getOpVal($firstJmpOp['op1'], $EX)), ") {", PHP_EOL;
+			echo $body;
+			echo $indent, '}', PHP_EOL;
+
+			$this->endComplexBlock($EX);
+			return;
+		}
+		// }}}
+		// {{{ foreach
+		if (isset($firstJmpOp)
+		 && $firstJmpOp['opcode'] == XC_FE_FETCH
+		 && $firstJmpOp['jmpouts'][0] > $range[1]
+		 && $lastOp['opcode'] == XC_JMP && !empty($lastOp['jmpouts'])
+		 && $lastOp['jmpouts'][0] == $firstJmp) {
+			$this->removeJmpInfo($EX, $firstJmp);
+			$this->removeJmpInfo($EX, $range[1]);
+			$this->beginComplexBlock($EX);
+
+			ob_start();
+			$this->beginScope($EX);
+			$this->recognizeAndDecompileClosedBlocks($EX, $range);
+			$this->endScope($EX);
+			$body = ob_get_clean();
+
+			$as = foldToCode($firstJmpOp['fe_as'], $EX);
+			if (isset($firstJmpOp['fe_key'])) {
+				$as = str($firstJmpOp['fe_key'], $EX) . ' => ' . str($as);
+			}
+
+			echo $indent, 'foreach (', str($firstJmpOp['fe_src'], $EX), " as $as) {", PHP_EOL;
+			echo $body;
+			echo $indent, '}', PHP_EOL;
+
+			$this->endComplexBlock($EX);
+			if ($opcodes[$range[1] + 1]['opcode'] == XC_SWITCH_FREE) {
+				$this->removeJmpInfo($EX, $range[1] + 1);
+			}
+			return;
+		}
+		// }}}
+
+		$this->decompileBasicBlock($EX, $range, true);
+	}
+	// }}}
+	function recognizeAndDecompileClosedBlocks(&$EX, $range) // {{{ decompile in a tree way
+	{
+		$opcodes = &$EX['opcodes'];
+
+		$starti = $range[0];
+		for ($i = $starti; $i <= $range[1]; ) {
+			if (!empty($opcodes[$i]['jmpins']) || !empty($opcodes[$i]['jmpouts'])) {
+				$blockFirst = $i;
+				$blockLast = -1;
+				$j = $blockFirst;
+				do {
+					$op = $opcodes[$j];
+					if (!empty($op['jmpins'])) {
+						// care about jumping from blocks behind, not before
+						foreach ($op['jmpins'] as $oplineNumber) {
+							if ($oplineNumber <= $range[1] && $blockLast < $oplineNumber) {
+								$blockLast = $oplineNumber;
+							}
+						}
+					}
+					if (!empty($op['jmpouts'])) {
+						$blockLast = max($blockLast, max($op['jmpouts']) - 1);
+					}
+					++$j;
+				} while ($j <= $blockLast);
+				if (!assert('$blockLast <= $range[1]')) {
+					var_dump($blockLast, $range[1]);
+				}
+
+				if ($blockLast >= $blockFirst) {
+					if ($blockFirst > $starti) {
+						$this->decompileBasicBlock($EX, array($starti, $blockFirst - 1));
+					}
+					if ($this->decompileComplexBlock($EX, array($blockFirst, $blockLast)) === false) {
+						if ($EX['lastBlock'] == 'complex') {
+							echo PHP_EOL;
+						}
+						$EX['lastBlock'] = null;
+					}
+					$starti = $blockLast + 1;
+					$i = $starti;
+				}
+				else {
+					++$i;
+				}
+			}
+			else {
+				++$i;
+			}
+		}
+		if ($starti <= $range[1]) {
+			$this->decompileBasicBlock($EX, array($starti, $range[1]));
+		}
+	}
+	// }}}
+	function &dop_array($op_array, $indent = '') // {{{
+	{
+		$op_array['opcodes'] = $this->fixOpcode($op_array['opcodes'], true, $indent == '' ? 1 : null);
+		$opcodes = &$op_array['opcodes'];
+		$last = count($opcodes) - 1;
+		// {{{ build jmpins/jmpouts to op_array
+		for ($i = 0; $i <= $last; $i ++) {
+			$op = &$opcodes[$i];
+			$op['line'] = $i;
+			switch ($op['opcode']) {
+			case XC_CONT:
+			case XC_BRK:
+				$op['jmpouts'] = array();
+				break;
+
+			case XC_GOTO:
+				$target = $op['op1']['var'];
+				$op['goto'] = $target;
+				$opcodes[$target]['gofrom'][] = $i;
+				break;
+
+			case XC_JMP:
+				$target = $op['op1']['var'];
+				$op['jmpouts'] = array($target);
+				$opcodes[$target]['jmpins'][] = $i;
+				break;
+
+			case XC_JMPZNZ:
+				$jmpz = $op['op2']['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_JMP_SET:
+			// case XC_FE_RESET:
+			case XC_FE_FETCH:
+			// case XC_JMP_NO_CTOR:
+				$target = $op['op2']['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;
+			*/
+
+			case XC_SWITCH_FREE:
+				$op['jmpouts'] = array($i + 1);
+				$opcodes[$i + 1]['jmpins'][] = $i;
+				break;
+
+			case XC_CASE:
+				// just to link together
+				$op['jmpouts'] = array($i + 2);
+				$opcodes[$i + 2]['jmpins'][] = $i;
+				break;
+
+			case XC_CATCH:
+				$catchNext = $op['extended_value'];
+				$op['jmpouts'] = array($catchNext);
+				$opcodes[$catchNext]['jmpins'][] = $i;
+				break;
+			}
+			/*
+			if (!empty($op['jmpouts']) || !empty($op['jmpins'])) {
+				echo $i, "\t", xcache_get_opcode($op['opcode']), PHP_EOL;
+			}
+			// */
+		}
+		unset($op);
+		if ($op_array['try_catch_array']) {
+			foreach ($op_array['try_catch_array'] as $try_catch_element) {
+				$catch_op = $try_catch_element['catch_op'];
+				$try_op = $try_catch_element['try_op'];
+				$opcodes[$try_op]['jmpins'][] = $catch_op;
+				$opcodes[$catch_op]['jmpouts'][] = $try_op;
+				$opcodes[$catch_op]['isCatchBegin'] = true;
+			}
+		}
+		// }}}
+		// build semi-basic blocks
+		$nextbbs = array();
+		$starti = 0;
+		for ($i = 1; $i <= $last; $i ++) {
+			if (isset($opcodes[$i]['jmpins'])
+			 || isset($opcodes[$i - 1]['jmpouts'])) {
+				$nextbbs[$starti] = $i;
+				$starti = $i;
+			}
+		}
+		$nextbbs[$starti] = $last + 1;
+
+		$EX = array();
+		$EX['Ts'] = array();
+		$EX['indent'] = $indent;
+		$EX['nextbbs'] = $nextbbs;
+		$EX['op_array'] = &$op_array;
+		$EX['opcodes'] = &$opcodes;
+		$EX['range'] = array(0, count($opcodes) - 1);
+		// func call
+		$EX['object'] = null;
+		$EX['called_scope'] = null;
+		$EX['fbc'] = null;
+		$EX['argstack'] = array();
+		$EX['arg_types_stack'] = array();
+		$EX['scopeStack'] = array();
+		$EX['silence'] = 0;
+		$EX['recvs'] = array();
+		$EX['uses'] = array();
+		$EX['lastBlock'] = null;
+
+		/* dump whole array
+		$this->keepTs = true;
+		$this->dasmBasicBlock($EX, $range);
+		for ($i = $range[0]; $i <= $range[1]; ++$i) {
+			echo $i, "\t", $this->dumpop($opcodes[$i], $EX);
+		}
+		// */
+		// decompile in a tree way
+		$this->recognizeAndDecompileClosedBlocks($EX, $EX['range'], $EX['indent']);
+		return $EX;
+	}
+	// }}}
+	function dasmBasicBlock(&$EX, $range) // {{{
+	{
+		$T = &$EX['Ts'];
+		$opcodes = &$EX['opcodes'];
+		$lastphpop = null;
+
+		for ($i = $range[0]; $i <= $range[1]; $i ++) {
+			// {{{ prepair
+			$op = &$opcodes[$i];
+			$opc = $op['opcode'];
+			if ($opc == XC_NOP) {
+				$this->usedOps[$opc] = true;
+				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;
+			}
+			// echo $i, ' '; $this->dumpop($op, $EX); //var_dump($op);
+
+			$resvar = null;
+			unset($curResVar);
+			if (array_key_exists($res['var'], $T)) {
+				$curResVar = &$T[$res['var']];
+			}
+			if ((ZEND_ENGINE_2_4 ? ($res['op_type'] & EXT_TYPE_UNUSED) : ($res['EA.type'] & EXT_TYPE_UNUSED)) || $res['op_type'] == XC_IS_UNUSED) {
+				$istmpres = false;
+			}
+			else {
+				$istmpres = true;
+			}
+			// }}}
+			// echo $opname, "\n";
+
+			$notHandled = false;
+			switch ($opc) {
+			case XC_NEW: // {{{
+				array_push($EX['arg_types_stack'], array($EX['fbc'], $EX['object'], $EX['called_scope']));
+				$EX['object'] = (int) $res['var'];
+				$EX['called_scope'] = null;
+				$EX['fbc'] = 'new ' . unquoteName($this->getOpVal($op1, $EX), $EX);
+				if (!ZEND_ENGINE_2) {
+					$resvar = '$new object$';
+				}
+				break;
+				// }}}
+			case XC_THROW: // {{{
+				$resvar = 'throw ' . str($this->getOpVal($op1, $EX));
+				break;
+				// }}}
+			case XC_CLONE: // {{{
+				$resvar = 'clone ' . str($this->getOpVal($op1, $EX));
+				break;
+				// }}}
+			case XC_CATCH: // {{{
+				break;
+				// }}}
+			case XC_INSTANCEOF: // {{{
+				$resvar = str($this->getOpVal($op1, $EX)) . ' instanceof ' . str($this->getOpVal($op2, $EX));
+				break;
+				// }}}
+			case XC_FETCH_CLASS: // {{{
+				if ($op2['op_type'] == XC_IS_UNUSED) {
+					switch (($ext & (defined('ZEND_FETCH_CLASS_MASK') ? ZEND_FETCH_CLASS_MASK : 0xFF))) {
+					case ZEND_FETCH_CLASS_SELF:
+						$class = 'self';
+						break;
+					case ZEND_FETCH_CLASS_PARENT:
+						$class = 'parent';
+						break;
+					case ZEND_FETCH_CLASS_STATIC:
+						$class = 'static';
+						break;
+					}
+					$istmpres = true;
+				}
+				else {
+					$class = $this->getOpVal($op2, $EX);
+					if (isset($op2['constant'])) {
+						$class = $this->stripNamespace(unquoteName($class));
+					}
+				}
+				$resvar = $class;
+				break;
+				// }}}
+			case XC_FETCH_CONSTANT: // {{{
+				if ($op1['op_type'] == XC_IS_UNUSED) {
+					$resvar = $this->stripNamespace($op2['constant']);
+					break;
+				}
+
+				if ($op1['op_type'] == XC_IS_CONST) {
+					$resvar = $this->stripNamespace($op1['constant']);
+				}
+				else {
+					$resvar = $this->getOpVal($op1, $EX);
+				}
+
+				$resvar = str($resvar) . '::' . unquoteName($this->getOpVal($op2, $EX));
+				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);
+				if (defined('ZEND_FETCH_TYPE_MASK')) {
+					$fetchtype = ($ext & ZEND_FETCH_TYPE_MASK);
+				}
+				else {
+					$fetchtype = $op2[!ZEND_ENGINE_2 ? 'fetch_type' : 'EA.type'];
+				}
+				switch ($fetchtype) {
+				case ZEND_FETCH_STATIC_MEMBER:
+					$class = $this->getOpVal($op2, $EX);
+					$rvalue = str($class) . '::$' . unquoteName($rvalue, $EX);
+					break;
+				default:
+					$name = unquoteName($rvalue, $EX);
+					$globalname = xcache_is_autoglobal($name) ? "\$$name" : "\$GLOBALS[" . str($rvalue) . "]";
+					$rvalue = new Decompiler_Fetch($rvalue, $fetchtype, $globalname);
+					break;
+				}
+				if ($opc == XC_UNSET_VAR) {
+					$op['php'] = "unset(" . str($rvalue, $EX) . ")";
+					$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_OBJ: // PHP 4 only
+			case XC_UNSET_DIM:
+			case XC_UNSET_OBJ:
+				$src = $this->getOpVal($op1, $EX);
+				if (is_a($src, "Decompiler_ForeachBox")) {
+					$src->iskey = $this->getOpVal($op2, $EX);
+					$resvar = $src;
+					break;
+				}
+
+				if (is_a($src, "Decompiler_DimBox")) {
+					$dimbox = $src;
+				}
+				else {
+					if (!is_a($src, "Decompiler_ListBox")) {
+						$op1val = $this->getOpVal($op1, $EX);
+						$list = new Decompiler_List(isset($op1val) ? $op1val : '$this');
+
+						$src = new Decompiler_ListBox($list);
+						if (!isset($op1['var'])) {
+							$this->dumpop($op, $EX);
+							var_dump($op);
+							die('missing var');
+						}
+						$T[$op1['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;
+				}
+				if ($opc == XC_UNSET_OBJ) {
+					$dim->isObject = true;
+				}
+				unset($dim);
+				$rvalue = $dimbox;
+				unset($dimbox);
+
+				if ($opc == XC_ASSIGN_DIM) {
+					$lvalue = $rvalue;
+					++ $i;
+					$rvalue = $this->getOpVal($opcodes[$i]['op1'], $EX);
+					$resvar = str($lvalue, $EX) . ' = ' . str($rvalue);
+				}
+				else if ($opc == XC_UNSET_DIM || $opc == XC_UNSET_OBJ) {
+					$op['php'] = "unset(" . str($rvalue, $EX) . ")";
+					$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);
+				if (is_a($rvalue, 'Decompiler_ForeachBox')) {
+					$type = $rvalue->iskey ? 'fe_key' : 'fe_as';
+					$rvalue->obj[$type] = $lvalue;
+					unset($T[$op2['var']]);
+					break;
+				}
+				if (is_a($rvalue, "Decompiler_DimBox")) {
+					$dim = &$rvalue->obj;
+					$dim->assign = $lvalue;
+					if ($dim->isLast) {
+						$resvar = foldToCode($dim->value, $EX);
+					}
+					unset($dim);
+					break;
+				}
+				if (is_a($rvalue, 'Decompiler_Fetch')) {
+					$src = str($rvalue->src, $EX);
+					if ('$' . unquoteName($src) == $lvalue) {
+						switch ($rvalue->fetchType) {
+						case ZEND_FETCH_STATIC:
+							$statics = &$EX['op_array']['static_variables'];
+							if ((xcache_get_type($statics[$name]) & IS_LEXICAL_VAR)) {
+								$EX['uses'][] = str($lvalue);
+								unset($statics);
+								break 2;
+							}
+							unset($statics);
+						}
+					}
+				}
+				$resvar = new Decompiler_Binop($this, $lvalue, XC_ASSIGN, $rvalue);
+				break;
+				// }}}
+			case XC_ASSIGN_REF: // {{{
+				$lvalue = $this->getOpVal($op1, $EX);
+				$rvalue = $this->getOpVal($op2, $EX);
+				if (is_a($rvalue, 'Decompiler_Fetch')) {
+					$src = str($rvalue->src, $EX);
+					if ('$' . unquoteName($src) == $lvalue) {
+						switch ($rvalue->fetchType) {
+						case ZEND_FETCH_GLOBAL:
+						case ZEND_FETCH_GLOBAL_LOCK:
+							$resvar = 'global ' . $lvalue;
+							break 2;
+						case ZEND_FETCH_STATIC:
+							$statics = &$EX['op_array']['static_variables'];
+							if ((xcache_get_type($statics[$name]) & IS_LEXICAL_REF)) {
+								$EX['uses'][] = '&' . str($lvalue);
+								unset($statics);
+								break 2;
+							}
+
+							$resvar = 'static ' . $lvalue;
+							$name = unquoteName($src);
+							if (isset($statics[$name])) {
+								$var = $statics[$name];
+								$resvar .= ' = ';
+								$resvar .= str(value($var), $EX);
+							}
+							unset($statics);
+							break 2;
+						default:
+						}
+					}
+				}
+				// TODO: PHP_6 global
+				$resvar = new Decompiler_Binop($this, $lvalue, XC_ASSIGN_REF, $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';
+				}
+				$rvalue = str($obj) . "->" . unquoteVariableName($this->getOpVal($op2, $EX), $EX);
+				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 = " . str($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);
+					// for < PHP_5_3
+					if ($op1['op_type'] == XC_IS_CONST) {
+						$rvalue = '$' . unquoteVariableName($this->getOpVal($op1, $EX));
+					}
+					if ($op2['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);
+					if ($opc == XC_ISSET_ISEMPTY_PROP_OBJ) {
+						if (!isset($container)) {
+							$container = '$this';
+						}
+						$rvalue = $container . "->" . unquoteVariableName($dim);
+					}
+					else {
+						$rvalue = $container . '[' . str($dim) .']';
+					}
+				}
+
+				switch ((!ZEND_ENGINE_2 ? $op['op2']['var'] /* constant */ : $ext) & (ZEND_ISSET|ZEND_ISEMPTY)) {
+				case ZEND_ISSET:
+					$rvalue = "isset(" . str($rvalue) . ")";
+					break;
+				case ZEND_ISEMPTY:
+					$rvalue = "empty(" . str($rvalue) . ")";
+					break;
+				}
+				$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 . str($this->getOpVal($op1, $EX));
+				break;
+				// }}}
+			case XC_INIT_STATIC_METHOD_CALL:
+			case XC_INIT_METHOD_CALL: // {{{
+				array_push($EX['arg_types_stack'], array($EX['fbc'], $EX['object'], $EX['called_scope']));
+				if ($opc == XC_INIT_STATIC_METHOD_CALL || $opc == XC_INIT_METHOD_CALL || $op1['op_type'] != XC_IS_UNUSED) {
+					$obj = $this->getOpVal($op1, $EX);
+					if (!isset($obj)) {
+						$obj = '$this';
+					}
+					if ($opc == XC_INIT_STATIC_METHOD_CALL || /* PHP4 */ isset($op1['constant'])) {
+						$EX['object'] = null;
+						$EX['called_scope'] = $this->stripNamespace(unquoteName($obj, $EX));
+					}
+					else {
+						$EX['object'] = $obj;
+						$EX['called_scope'] = null;
+					}
+					if ($res['op_type'] != XC_IS_UNUSED) {
+						$resvar = '$obj call$';
+					}
+				}
+				else {
+					$EX['object'] = null;
+					$EX['called_scope'] = null;
+				}
+
+				$EX['fbc'] = $this->getOpVal($op2, $EX);
+				if (($opc == XC_INIT_STATIC_METHOD_CALL || $opc == XC_INIT_METHOD_CALL) && !isset($EX['fbc'])) {
+					$EX['fbc'] = '__construct';
+				}
+				break;
+				// }}}
+			case XC_INIT_NS_FCALL_BY_NAME:
+			case XC_INIT_FCALL_BY_NAME: // {{{
+				array_push($EX['arg_types_stack'], array($EX['fbc'], $EX['object'], $EX['called_scope']));
+				if (!ZEND_ENGINE_2 && ($ext & ZEND_CTOR_CALL)) {
+					break;
+				}
+				$EX['object'] = null;
+				$EX['called_scope'] = null;
+				$EX['fbc'] = $this->getOpVal($op2, $EX);
+				break;
+				// }}}
+			case XC_INIT_FCALL_BY_FUNC: // {{{ deprecated even in PHP 4?
+				$EX['object'] = null;
+				$EX['called_scope'] = null;
+				$which = $op1['var'];
+				$EX['fbc'] = $EX['op_array']['funcs'][$which]['name'];
+				break;
+				// }}}
+			case XC_DO_FCALL_BY_FUNC:
+				$which = $op1['var'];
+				$fname = $EX['op_array']['funcs'][$which]['name'];
+				$args = $this->popargs($EX, $ext);
+				$resvar = $fname . "($args)";
+				break;
+			case XC_DO_FCALL:
+				$fname = unquoteName($this->getOpVal($op1, $EX), $EX);
+				$args = $this->popargs($EX, $ext);
+				$resvar = $fname . "($args)";
+				break;
+			case XC_DO_FCALL_BY_NAME: // {{{
+				$object = null;
+
+				$fname = unquoteName($EX['fbc'], $EX);
+				if (!is_int($EX['object'])) {
+					$object = $EX['object'];
+				}
+
+				$args = $this->popargs($EX, $ext);
+
+				$prefix = (isset($object) ? $object . '->' : '' )
+					. (isset($EX['called_scope']) ? $EX['called_scope'] . '::' : '' );
+				$resvar = $prefix
+					. (!$prefix ? $this->stripNamespace($fname) : $fname)
+					. "($args)";
+				unset($args);
+
+				if (is_int($EX['object'])) {
+					$T[$EX['object']] = $resvar;
+					$resvar = null;
+				}
+				list($EX['fbc'], $EX['object'], $EX['called_scope']) = array_pop($EX['arg_types_stack']);
+				break;
+				// }}}
+			case XC_VERIFY_ABSTRACT_CLASS: // {{{
+				//unset($T[$op1['var']]);
+				break;
+				// }}}
+			case XC_DECLARE_CLASS: 
+			case XC_DECLARE_INHERITED_CLASS:
+			case XC_DECLARE_INHERITED_CLASS_DELAYED: // {{{
+				$key = $op1['constant'];
+				if (!isset($this->dc['class_table'][$key])) {
+					echo 'class not found: ', $key, 'existing classes are:', "\n";
+					var_dump(array_keys($this->dc['class_table']));
+					exit;
+				}
+				$class = &$this->dc['class_table'][$key];
+				if (!isset($class['name'])) {
+					$class['name'] = unquoteName($this->getOpVal($op2, $EX), $EX);
+				}
+				if ($opc == XC_DECLARE_INHERITED_CLASS || $opc == XC_DECLARE_INHERITED_CLASS_DELAYED) {
+					$ext /= XC_SIZEOF_TEMP_VARIABLE;
+					$class['parent'] = $T[$ext];
+					unset($T[$ext]);
+				}
+				else {
+					$class['parent'] = null;
+				}
+
+				for (;;) {
+					if ($i + 1 <= $range[1]
+					 && $opcodes[$i + 1]['opcode'] == XC_ADD_INTERFACE
+					 && $opcodes[$i + 1]['op1']['var'] == $res['var']) {
+						// continue
+					}
+					else if ($i + 2 <= $range[1]
+					 && $opcodes[$i + 2]['opcode'] == XC_ADD_INTERFACE
+					 && $opcodes[$i + 2]['op1']['var'] == $res['var']
+					 && $opcodes[$i + 1]['opcode'] == XC_FETCH_CLASS) {
+						// continue
+					}
+					else {
+						break;
+					}
+					$this->usedOps[XC_ADD_INTERFACE] = true;
+
+					$fetchop = &$opcodes[$i + 1];
+					$interface = $this->stripNamespace(unquoteName($this->getOpVal($fetchop['op2'], $EX), $EX));
+					$addop = &$opcodes[$i + 2];
+					$class['interfaces'][$addop['extended_value']] = $interface;
+					unset($fetchop, $addop);
+					$i += 2;
+				}
+				$this->dclass($class, $EX['indent']);
+				echo "\n";
+				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 = value(chr(str($op2val)));
+					break;
+				case XC_ADD_STRING:
+					break;
+				case XC_ADD_VAR:
+					break;
+				}
+				if (str($op1val) == "''") {
+					$rvalue = $op2val;
+				}
+				else if (str($op2val) == "''") {
+					$rvalue = $op1val;
+				}
+				else {
+					$rvalue = str($op1val) . ' . ' . str($op2val);
+				}
+				$resvar = $rvalue;
+				// }}}
+				break;
+			case XC_PRINT: // {{{
+				$op1val = $this->getOpVal($op1, $EX);
+				$resvar = "print(" . str($op1val) . ")";
+				break;
+				// }}}
+			case XC_ECHO: // {{{
+				$op1val = $this->getOpVal($op1, $EX);
+				$resvar = "echo " . str($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, true);
+
+				if ($opc == XC_ADD_ARRAY_ELEMENT) {
+					$assoc = $this->getOpVal($op2, $EX);
+					if (isset($assoc)) {
+						$curResVar->value[] = array($assoc, $rvalue);
+					}
+					else {
+						$curResVar->value[] = array(null, $rvalue);
+					}
+				}
+				else {
+					if ($opc == XC_INIT_ARRAY) {
+						$resvar = new Decompiler_Array();
+						if (!isset($rvalue)) {
+							continue;
+						}
+					}
+
+					$assoc = $this->getOpVal($op2, $EX);
+					if (isset($assoc)) {
+						$resvar->value[] = array($assoc, $rvalue);
+					}
+					else {
+						$resvar->value[] = array(null, $rvalue);
+					}
+				}
+				break;
+				// }}}
+			case XC_QM_ASSIGN: // {{{
+				if (isset($curResVar) && is_a($curResVar, 'Decompiler_Binop')) {
+					$curResVar->op2 = $this->getOpVal($op1, $EX);
+				}
+				else {
+					$resvar = $this->getOpVal($op1, $EX);
+				}
+				break;
+				// }}}
+			case XC_BOOL: // {{{
+				$resvar = /*'(bool) ' .*/ $this->getOpVal($op1, $EX);
+				break;
+				// }}}
+			case XC_RETURN: // {{{
+				$resvar = "return " . str($this->getOpVal($op1, $EX));
+				break;
+				// }}}
+			case XC_INCLUDE_OR_EVAL: // {{{
+				$type = $op2['var']; // hack
+				$keyword = $this->includeTypes[$type];
+				$resvar = "$keyword " . str($this->getOpVal($op1, $EX));
+				break;
+				// }}}
+			case XC_FE_RESET: // {{{
+				$resvar = $this->getOpVal($op1, $EX);
+				break;
+				// }}}
+			case XC_FE_FETCH: // {{{
+				$op['fe_src'] = $this->getOpVal($op1, $EX, true);
+				$fe = new Decompiler_ForeachBox($op);
+				$fe->iskey = false;
+				$T[$res['var']] = $fe;
+
+				++ $i;
+				if (($ext & ZEND_FE_FETCH_WITH_KEY)) {
+					$fe = new Decompiler_ForeachBox($op);
+					$fe->iskey = true;
+
+					$res = $opcodes[$i]['result'];
+					$T[$res['var']] = $fe;
+				}
+				break;
+				// }}}
+			case XC_SWITCH_FREE: // {{{
+				break;
+				// }}}
+			case XC_FREE: // {{{
+				$free = $T[$op1['var']];
+				if (!is_a($free, 'Decompiler_Array') && !is_a($free, 'Decompiler_Box')) {
+					$op['php'] = is_object($free) ? $free : $this->unquote($free, '(', ')');
+					$lastphpop = &$op;
+				}
+				unset($T[$op1['var']], $free);
+				break;
+				// }}}
+			case XC_JMP_NO_CTOR:
+				break;
+			case XC_JMP_SET: // ?:
+				$resvar = new Decompiler_Binop($this, $this->getOpVal($op1, $EX), XC_JMP_SET, null);
+				break;
+			case XC_JMPZ_EX: // and
+			case XC_JMPNZ_EX: // or
+				$resvar = $this->getOpVal($op1, $EX);
+				break;
+
+			case XC_JMPNZ: // while
+			case XC_JMPZNZ: // for
+			case XC_JMPZ: // {{{
+				break;
+				// }}}
+			case XC_CONT:
+			case XC_BRK:
+				$resvar = $opc == XC_CONT ? 'continue' : 'break';
+				$count = str($this->getOpVal($op2, $EX));
+				if ($count != '1') {
+					$resvar .= ' ' . $count;
+				}
+				break;
+			case XC_GOTO:
+				$resvar = 'goto label' . $op['op1']['var'];
+				$istmpres = false;
+				break;
+
+			case XC_JMP: // {{{
+				break;
+				// }}}
+			case XC_CASE:
+				// $switchValue = $this->getOpVal($op1, $EX);
+				$caseValue = $this->getOpVal($op2, $EX);
+				$resvar = $caseValue;
+				break;
+			case XC_RECV_INIT:
+			case XC_RECV:
+				$offset = $this->getOpVal($op1, $EX);
+				$lvalue = $this->getOpVal($op['result'], $EX);
+				if ($opc == XC_RECV_INIT) {
+					$default = value($op['op2']['constant']);
+				}
+				else {
+					$default = null;
+				}
+				$EX['recvs'][str($offset)] = array($lvalue, $default);
+				break;
+			case XC_POST_DEC:
+			case XC_POST_INC:
+			case XC_POST_DEC_OBJ:
+			case XC_POST_INC_OBJ:
+			case XC_PRE_DEC:
+			case XC_PRE_INC:
+			case XC_PRE_DEC_OBJ:
+			case XC_PRE_INC_OBJ: // {{{
+				$flags = array_flip(explode('_', $opname));
+				if (isset($flags['OBJ'])) {
+					$resvar = $this->getOpVal($op1, $EX) . '->' . unquoteVariableName($this->getOpVal($op2, $EX), $EX);
+				}
+				else {
+					$resvar = $this->getOpVal($op1, $EX);
+				}
+				$opstr = isset($flags['DEC']) ? '--' : '++';
+				if (isset($flags['POST'])) {
+					$resvar .= $opstr;
+				}
+				else {
+					$resvar = "$opstr$resvar";
+				}
+				break;
+				// }}}
+
+			case XC_BEGIN_SILENCE: // {{{
+				$EX['silence'] ++;
+				break;
+				// }}}
+			case XC_END_SILENCE: // {{{
+				$EX['silence'] --;
+				$lastresvar = '@' . str($lastresvar, $EX);
+				break;
+				// }}}
+			case XC_CAST: // {{{
+				$type = $ext;
+				static $type2cast = array(
+						IS_LONG   => '(int)',
+						IS_DOUBLE => '(double)',
+						IS_STRING => '(string)',
+						IS_ARRAY  => '(array)',
+						IS_OBJECT => '(object)',
+						IS_BOOL   => '(bool)',
+						IS_NULL   => '(unset)',
+						);
+				assert(isset($type2cast[$type]));
+				$cast = $type2cast[$type];
+				$resvar = $cast . ' ' . $this->getOpVal($op1, $EX);
+				break;
+				// }}}
+			case XC_EXT_STMT:
+			case XC_EXT_FCALL_BEGIN:
+			case XC_EXT_FCALL_END:
+			case XC_EXT_NOP:
+				break;
+			case XC_DECLARE_FUNCTION:
+				$this->dfunction($this->dc['function_table'][$op1['constant']], $EX['indent']);
+				break;
+			case XC_DECLARE_LAMBDA_FUNCTION: // {{{
+				ob_start();
+				$this->dfunction($this->dc['function_table'][$op1['constant']], $EX['indent']);
+				$resvar = ob_get_clean();
+				$istmpres = true;
+				break;
+				// }}}
+			case XC_DECLARE_CONST:
+				$name = $this->stripNamespace(unquoteName($this->getOpVal($op1, $EX), $EX));
+				$value = str($this->getOpVal($op2, $EX));
+				$resvar = 'const ' . $name . ' = ' . $value;
+				break;
+			case XC_DECLARE_FUNCTION_OR_CLASS:
+				/* always removed by compiler */
+				break;
+			case XC_TICKS:
+				$lastphpop['ticks'] = $this->getOpVal($op1, $EX);
+				// $EX['tickschanged'] = true;
+				break;
+			case XC_RAISE_ABSTRACT_ERROR:
+				// abstract function body is empty, don't need this code
+				break;
+			case XC_USER_OPCODE:
+				echo '// ZEND_USER_OPCODE, impossible to decompile';
+				break;
+			case XC_OP_DATA:
+				break;
+			default: // {{{
+				$call = array(&$this, $opname);
+				if (is_callable($call)) {
+					$this->usedOps[$opc] = true;
+					$this->{$opname}($op, $EX);
+				}
+				else if (isset($this->binops[$opc])) { // {{{
+					$this->usedOps[$opc] = true;
+					$op1val = $this->getOpVal($op1, $EX);
+					$op2val = $this->getOpVal($op2, $EX);
+					$rvalue = new Decompiler_Binop($this, $op1val, $opc, $op2val);
+					$resvar = $rvalue;
+					// }}}
+				}
+				else if (isset($this->unaryops[$opc])) { // {{{
+					$this->usedOps[$opc] = true;
+					$op1val = $this->getOpVal($op1, $EX);
+					$myop = $this->unaryops[$opc];
+					$rvalue = $myop . str($op1val);
+					$resvar = $rvalue;
+					// }}}
+				}
+				else {
+					$notHandled = true;
+				}
+				// }}}
+			}
+			if ($notHandled) {
+				echo "\x1B[31m * TODO ", $opname, "\x1B[0m\n";
+			}
+			else {
+				$this->usedOps[$opc] = true;
+			}
+
+			if (isset($resvar)) {
+				if ($istmpres) {
+					$T[$res['var']] = $resvar;
+					$lastresvar = &$T[$res['var']];
+				}
+				else {
+					$op['php'] = $resvar;
+					$lastphpop = &$op;
+					$lastresvar = &$op['php'];
+				}
+			}
+		}
+		return $T;
+	}
+	// }}}
+	function unquote($str, $st, $ed) // {{{
+	{
+		$l1 = strlen($st);
+		$l2 = strlen($ed);
+		if (substr($str, 0, $l1) === $st && substr($str, -$l2) === $ed) {
+			$str = substr($str, $l1, -$l2);
+		}
+		return $str;
+	}
+	// }}}
+	function popargs(&$EX, $n) // {{{
+	{
+		$args = array();
+		for ($i = 0; $i < $n; $i ++) {
+			$a = array_pop($EX['argstack']);
+			if (is_array($a)) {
+				array_unshift($args, foldToCode($a, $EX));
+			}
+			else {
+				array_unshift($args, $a);
+			}
+		}
+		return implode(', ', $args);
+	}
+	// }}}
+	function dumpop($op, &$EX) // {{{
+	{
+		assert('isset($op)');
+		$op1 = $op['op1'];
+		$op2 = $op['op2'];
+		$d = array(xcache_get_opcode($op['opcode']), $op['opcode']);
+
+		foreach (array('op1' => '1:', 'op2' => '2:', 'result' => '>') as $k => $kk) {
+			switch ($op[$k]['op_type']) {
+			case XC_IS_UNUSED:
+				$d[$kk] = 'U:' . $op[$k]['opline_num'];
+				break;
+
+			case XC_IS_VAR:
+				$d[$kk] = '$' . $op[$k]['var'];
+				if ($k != 'result') {
+					$d[$kk] .= ':' . str($this->getOpVal($op[$k], $EX));
+				}
+				break;
+
+			case XC_IS_TMP_VAR:
+				$d[$kk] = '#' . $op[$k]['var'];
+				if ($k != 'result') {
+					$d[$kk] .= ':' . str($this->getOpVal($op[$k], $EX));
+				}
+				break;
+
+			case XC_IS_CV:
+				$d[$kk] = $this->getOpVal($op[$k], $EX);
+				break;
+
+			default:
+				if ($k == 'result') {
+					var_dump($op);
+					assert(0);
+					exit;
+				}
+				else {
+					$d[$kk] = $this->getOpVal($op[$k], $EX);
+				}
+			}
+		}
+		$d[';'] = $op['extended_value'];
+		if (!empty($op['jmpouts'])) {
+			$d['>>'] = implode(',', $op['jmpouts']);
+		}
+		if (!empty($op['jmpins'])) {
+			$d['<<'] = implode(',', $op['jmpins']);
+		}
+
+		foreach ($d as $k => $v) {
+			echo is_int($k) ? '' : $k, str($v), "\t";
+		}
+		echo PHP_EOL;
+	}
+	// }}}
+	function dumpRange(&$EX, $range) // {{{
+	{
+		for ($i = $range[0]; $i <= $range[1]; ++$i) {
+			echo $EX['indent'], $i, "\t"; $this->dumpop($EX['opcodes'][$i], $EX);
+		}
+		echo $EX['indent'], "==", PHP_EOL;
+	}
+	// }}}
+	function dargs(&$EX) // {{{
+	{
+		$op_array = &$EX['op_array'];
+
+		if (isset($op_array['num_args'])) {
+			$c = $op_array['num_args'];
+		}
+		else if (!empty($op_array['arg_types'])) {
+			$c = count($op_array['arg_types']);
+		}
+		else {
+			// php4
+			$c = count($EX['recvs']);
+		}
+
+		$refrest = false;
+		for ($i = 0; $i < $c; $i ++) {
+			if ($i) {
+				echo ', ';
+			}
+			$arg = $EX['recvs'][$i + 1];
+			if (isset($op_array['arg_info'])) {
+				$ai = $op_array['arg_info'][$i];
+				if (!empty($ai['class_name'])) {
+					echo $this->stripNamespace($ai['class_name']), ' ';
+					if (!ZEND_ENGINE_2_2 && $ai['allow_null']) {
+						echo 'or NULL ';
+					}
+				}
+				else if (!empty($ai['array_type_hint'])) {
+					echo 'array ';
+					if (!ZEND_ENGINE_2_2 && $ai['allow_null']) {
+						echo 'or NULL ';
+					}
+				}
+				if ($ai['pass_by_reference']) {
+					echo '&';
+				}
+				printf("\$%s", $ai['name']);
+			}
+			else {
+				if ($refrest) {
+					echo '&';
+				}
+				else if (!empty($op_array['arg_types']) && isset($op_array['arg_types'][$i])) {
+					switch ($op_array['arg_types'][$i]) {
+					case BYREF_FORCE_REST:
+						$refrest = true;
+						/* fall */
+					case BYREF_FORCE:
+						echo '&';
+						break;
+
+					case BYREF_NONE:
+					case BYREF_ALLOW:
+						break;
+					default:
+						assert(0);
+					}
+				}
+				echo str($arg[0], $EX);
+			}
+			if (isset($arg[1])) {
+				echo ' = ', str($arg[1], $EX);
+			}
+		}
+	}
+	// }}}
+	function duses(&$EX) // {{{
+	{
+		if ($EX['uses']) {
+			echo ' use(', implode(', ', $EX['uses']), ')';
+		}
+	}
+	// }}}
+	function dfunction($func, $indent = '', $decorations = array(), $nobody = false) // {{{
+	{
+		$this->detectNamespace($func['op_array']['function_name']);
+
+		if ($nobody) {
+			$EX = array();
+			$EX['op_array'] = &$func['op_array'];
+			$EX['recvs'] = array();
+			$EX['uses'] = array();
+		}
+		else {
+			ob_start();
+			$EX = &$this->dop_array($func['op_array'], $indent . INDENT);
+			$body = ob_get_clean();
+		}
+
+		$functionName = $this->stripNamespace($func['op_array']['function_name']);
+		$isExpression = false;
+		if ($functionName == '{closure}') {
+			$functionName = '';
+			$isExpression = true;
+		}
+		echo $isExpression ? '' : $indent;
+		if ($decorations) {
+			echo implode(' ', $decorations), ' ';
+		}
+		echo 'function', $functionName ? ' ' . $functionName : '', '(';
+		$this->dargs($EX);
+		echo ")";
+		$this->duses($EX);
+		if ($nobody) {
+			echo ";\n";
+		}
+		else {
+			if (!$isExpression) {
+				echo "\n";
+				echo $indent, "{\n";
+			}
+			else {
+				echo " {\n";
+			}
+
+			echo $body;
+			echo "$indent}";
+			if (!$isExpression) {
+				echo "\n";
+			}
+		}
+	}
+	// }}}
+	function dclass($class, $indent = '') // {{{
+	{
+		$this->detectNamespace($class['name']);
+
+		// {{{ class decl
+		if (!empty($class['doc_comment'])) {
+			echo $indent;
+			echo $class['doc_comment'];
+			echo "\n";
+		}
+		$isInterface = false;
+		$decorations = array();
+		if (!empty($class['ce_flags'])) {
+			if ($class['ce_flags'] & ZEND_ACC_INTERFACE) {
+				$isInterface = true;
+			}
+			else {
+				if ($class['ce_flags'] & ZEND_ACC_IMPLICIT_ABSTRACT_CLASS) {
+					$decorations[] = "abstract";
+				}
+				if ($class['ce_flags'] & ZEND_ACC_FINAL_CLASS) {
+					$decorations[] = "final";
+				}
+			}
+		}
+
+		echo $indent;
+		if ($decorations) {
+			echo implode(' ', $decorations), ' ';
+		}
+		echo $isInterface ? 'interface ' : 'class ', $this->stripNamespace($class['name']);
+		if ($class['parent']) {
+			echo ' extends ', $class['parent'];
+		}
+		/* TODO */
+		if (!empty($class['interfaces'])) {
+			echo ' implements ';
+			echo implode(', ', $class['interfaces']);
+		}
+		echo "\n";
+		echo $indent, "{";
+		// }}}
+		$newindent = INDENT . $indent;
+		// {{{ const, static
+		foreach (array('constants_table' => 'const '
+					, 'static_members' => 'static $') as $type => $prefix) {
+			if (!empty($class[$type])) {
+				echo "\n";
+				// TODO: skip shadow?
+				foreach ($class[$type] as $name => $v) {
+					echo $newindent;
+					echo $prefix, $name, ' = ';
+					echo str(value($v), $newindent);
+					echo ";\n";
+				}
+			}
+		}
+		// }}}
+		// {{{ properties
+		$member_variables = isset($class['properties_info']) ? $class['properties_info'] : ($class['default_static_members'] + $class['default_properties']);
+		if ($member_variables) {
+			echo "\n";
+			$infos = !empty($class['properties_info']) ? $class['properties_info'] : null;
+			foreach ($member_variables as $name => $dummy) {
+				$info = (isset($infos) && isset($infos[$name])) ? $infos[$name] : null;
+				if (isset($info)) {
+					if (!empty($info['doc_comment'])) {
+						echo $newindent;
+						echo $info['doc_comment'];
+						echo "\n";
+					}
+				}
+
+				echo $newindent;
+				$static = false;
+				if (isset($info)) {
+					if ($info['flags'] & ZEND_ACC_STATIC) {
+						$static = true;
+					}
+				}
+				else if (isset($class['default_static_members'][$name])) {
+					$static = true;
+				}
+
+				if ($static) {
+					echo "static ";
+				}
+
+				$mangled = false;
+				if (!ZEND_ENGINE_2) {
+					echo 'var ';
+				}
+				else if (!isset($info)) {
+					echo 'public ';
+				}
+				else {
+					if ($info['flags'] & ZEND_ACC_SHADOW) {
+						continue;
+					}
+					switch ($info['flags'] & ZEND_ACC_PPP_MASK) {
+					case ZEND_ACC_PUBLIC:
+						echo "public ";
+						break;
+					case ZEND_ACC_PRIVATE:
+						echo "private ";
+						$mangled = true;
+						break;
+					case ZEND_ACC_PROTECTED:
+						echo "protected ";
+						$mangled = true;
+						break;
+					}
+				}
+
+				echo '$', $name;
+
+				if (isset($info['offset'])) {
+					$value = $class[$static ? 'default_static_members_table' : 'default_properties_table'][$info['offset']];
+				}
+				else {
+					$key = isset($info) ? $info['name'] . ($mangled ? "\000" : "") : $name;
+
+					$value = $class[$static ? 'default_static_members' : 'default_properties'][$key];
+				}
+				if (isset($value)) {
+					echo ' = ';
+					echo str(value($value), $newindent);
+				}
+				echo ";\n";
+			}
+		}
+		// }}}
+		// {{{ function_table
+		if (isset($class['function_table'])) {
+			foreach ($class['function_table'] as $func) {
+				if (!isset($func['scope']) || $func['scope'] == $class['name']) {
+					// TODO: skip shadow here
+					echo "\n";
+					$opa = $func['op_array'];
+					if (!empty($opa['doc_comment'])) {
+						echo $newindent;
+						echo $opa['doc_comment'];
+						echo "\n";
+					}
+					$isAbstractMethod = false;
+					$decorations = array();
+					if (isset($opa['fn_flags'])) {
+						if (($opa['fn_flags'] & ZEND_ACC_ABSTRACT) && !$isInterface) {
+							$decorations[] = "abstract";
+							$isAbstractMethod = true;
+						}
+						if ($opa['fn_flags'] & ZEND_ACC_FINAL) {
+							$decorations[] = "final";
+						}
+						if ($opa['fn_flags'] & ZEND_ACC_STATIC) {
+							$decorations[] = "static";
+						}
+
+						switch ($opa['fn_flags'] & ZEND_ACC_PPP_MASK) {
+							case ZEND_ACC_PUBLIC:
+								$decorations[] = "public";
+								break;
+							case ZEND_ACC_PRIVATE:
+								$decorations[] = "private";
+								break;
+							case ZEND_ACC_PROTECTED:
+								$decorations[] = "protected";
+								break;
+							default:
+								$decorations[] = "<visibility error>";
+								break;
+						}
+					}
+					$this->dfunction($func, $newindent, $decorations, $isInterface || $isAbstractMethod);
+					if ($opa['function_name'] == 'Decompiler') {
+						//exit;
+					}
+				}
+			}
+		}
+		// }}}
+		echo $indent, "}\n";
+	}
+	// }}}
+	function decompileString($string) // {{{
+	{
+		$this->dc = xcache_dasm_string($string);
+		if ($this->dc === false) {
+			echo "error compling string\n";
+			return false;
+		}
+	}
+	// }}}
+	function decompileFile($file) // {{{
+	{
+		$this->dc = xcache_dasm_file($file);
+		if ($this->dc === false) {
+			echo "error compling $file\n";
+			return false;
+		}
+	}
+	// }}}
+	function decompileDasm($content) // {{{
+	{
+		$this->dc = $content;
+	}
+	// }}}
+	function output() // {{{
+	{
+		echo "<?". "php\n\n";
+		foreach ($this->dc['class_table'] as $key => $class) {
+			if ($key{0} != "\0") {
+				$this->dclass($class);
+				echo "\n";
+			}
+		}
+
+		foreach ($this->dc['function_table'] as $key => $func) {
+			if ($key{0} != "\0") {
+				$this->dfunction($func);
+				echo "\n";
+			}
+		}
+
+		$this->dop_array($this->dc['op_array']);
+		echo "\n?" . ">\n";
+
+		if (!empty($this->test)) {
+			$this->outputUnusedOp();
+		}
+		return true;
+	}
+	// }}}
+	function outputUnusedOp() // {{{
+	{
+		for ($i = 0; $opname = xcache_get_opcode($i); $i ++) {
+			if ($opname == 'UNDEF')  {
+				continue;
+			}
+
+			if (!isset($this->usedOps[$i])) {
+				echo "not covered opcode ", $opname, "\n";
+			}
+		}
+	}
+	// }}}
+}
+
+// {{{ defines
+define('ZEND_ENGINE_2_4', PHP_VERSION >= "5.3.99");
+define('ZEND_ENGINE_2_3', ZEND_ENGINE_2_4 || PHP_VERSION >= "5.3.");
+define('ZEND_ENGINE_2_2', ZEND_ENGINE_2_3 || PHP_VERSION >= "5.2.");
+define('ZEND_ENGINE_2_1', ZEND_ENGINE_2_2 || PHP_VERSION >= "5.1.");
+define('ZEND_ENGINE_2',   ZEND_ENGINE_2_1 || PHP_VERSION >= "5.0.");
+
+define('ZEND_ACC_STATIC',         0x01);
+define('ZEND_ACC_ABSTRACT',       0x02);
+define('ZEND_ACC_FINAL',          0x04);
+define('ZEND_ACC_IMPLEMENTED_ABSTRACT',       0x08);
+
+define('ZEND_ACC_IMPLICIT_ABSTRACT_CLASS',    0x10);
+define('ZEND_ACC_EXPLICIT_ABSTRACT_CLASS',    0x20);
+define('ZEND_ACC_FINAL_CLASS',                0x40);
+define('ZEND_ACC_INTERFACE',                  0x80);
+if (ZEND_ENGINE_2_4) {
+	define('ZEND_ACC_TRAIT',                  0x120);
+}
+define('ZEND_ACC_PUBLIC',     0x100);
+define('ZEND_ACC_PROTECTED',  0x200);
+define('ZEND_ACC_PRIVATE',    0x400);
+define('ZEND_ACC_PPP_MASK',  (ZEND_ACC_PUBLIC | ZEND_ACC_PROTECTED | ZEND_ACC_PRIVATE));
+
+define('ZEND_ACC_CHANGED',    0x800);
+define('ZEND_ACC_IMPLICIT_PUBLIC',    0x1000);
+
+define('ZEND_ACC_CTOR',       0x2000);
+define('ZEND_ACC_DTOR',       0x4000);
+define('ZEND_ACC_CLONE',      0x8000);
+
+define('ZEND_ACC_ALLOW_STATIC',   0x10000);
+
+define('ZEND_ACC_SHADOW', 0x2000);
+
+if (ZEND_ENGINE_2_4) {
+	define('ZEND_FETCH_GLOBAL',           0x00000000);
+	define('ZEND_FETCH_LOCAL',            0x10000000);
+	define('ZEND_FETCH_STATIC',           0x20000000);
+	define('ZEND_FETCH_STATIC_MEMBER',    0x30000000);
+	define('ZEND_FETCH_GLOBAL_LOCK',      0x40000000);
+	define('ZEND_FETCH_LEXICAL',          0x50000000);
+
+	define('ZEND_FETCH_TYPE_MASK',        0x70000000);
+}
+else {
+	define('ZEND_FETCH_GLOBAL',           0);
+	define('ZEND_FETCH_LOCAL',            1);
+	define('ZEND_FETCH_STATIC',           2);
+	define('ZEND_FETCH_STATIC_MEMBER',    3);
+	define('ZEND_FETCH_GLOBAL_LOCK',      4);
+}
+
+define('ZEND_FETCH_CLASS_DEFAULT',    0);
+define('ZEND_FETCH_CLASS_SELF',       1);
+define('ZEND_FETCH_CLASS_PARENT',     2);
+define('ZEND_FETCH_CLASS_MAIN',       3);
+define('ZEND_FETCH_CLASS_GLOBAL',     4);
+define('ZEND_FETCH_CLASS_AUTO',       5);
+define('ZEND_FETCH_CLASS_INTERFACE',  6);
+define('ZEND_FETCH_CLASS_STATIC',     7);
+if (ZEND_ENGINE_2_4) {
+	define('ZEND_FETCH_CLASS_TRAIT',     14);
+}
+if (ZEND_ENGINE_2_3) {
+	define('ZEND_FETCH_CLASS_MASK',     0xF);
+}
+
+define('ZEND_EVAL',               (1<<0));
+define('ZEND_INCLUDE',            (1<<1));
+define('ZEND_INCLUDE_ONCE',       (1<<2));
+define('ZEND_REQUIRE',            (1<<3));
+define('ZEND_REQUIRE_ONCE',       (1<<4));
+
+define('ZEND_ISSET',              (1<<0));
+define('ZEND_ISEMPTY',            (1<<1));
+if (ZEND_ENGINE_2_4) {
+	define('EXT_TYPE_UNUSED',     (1<<5));
+}
+else {
+	define('EXT_TYPE_UNUSED',     (1<<0));
+}
+
+define('ZEND_FETCH_STANDARD',     0);
+define('ZEND_FETCH_ADD_LOCK',     1);
+
+define('ZEND_FE_FETCH_BYREF',     1);
+define('ZEND_FE_FETCH_WITH_KEY',  2);
+
+define('ZEND_MEMBER_FUNC_CALL',   1<<0);
+define('ZEND_CTOR_CALL',          1<<1);
+
+define('ZEND_ARG_SEND_BY_REF',        (1<<0));
+define('ZEND_ARG_COMPILE_TIME_BOUND', (1<<1));
+define('ZEND_ARG_SEND_FUNCTION',      (1<<2));
+
+define('BYREF_NONE',       0);
+define('BYREF_FORCE',      1);
+define('BYREF_ALLOW',      2);
+define('BYREF_FORCE_REST', 3);
+define('IS_NULL',     0);
+define('IS_LONG',     1);
+define('IS_DOUBLE',   2);
+define('IS_BOOL',     ZEND_ENGINE_2 ? 3 : 6);
+define('IS_ARRAY',    4);
+define('IS_OBJECT',   5);
+define('IS_STRING',   ZEND_ENGINE_2 ? 6 : 3);
+define('IS_RESOURCE', 7);
+define('IS_CONSTANT', 8);
+define('IS_CONSTANT_ARRAY',   9);
+/* Ugly hack to support constants as static array indices */
+define('IS_CONSTANT_TYPE_MASK',   0x0f);
+define('IS_CONSTANT_UNQUALIFIED', 0x10);
+define('IS_CONSTANT_INDEX',       0x80);
+define('IS_LEXICAL_VAR',          0x20);
+define('IS_LEXICAL_REF',          0x40);
+
+@define('XC_IS_CV', 16);
+
+/*
+if (preg_match_all('!XC_[A-Z_]+!', file_get_contents(__FILE__), $ms)) {
+	$verdiff = array();
+	foreach ($ms[0] as $k) {
+		if (!defined($k)) {
+			$verdiff[$k] = -1;
+			define($k, -1);
+		}
+	}
+	var_export($verdiff);
+}
+/*/
+foreach (array (
+	'XC_HANDLE_EXCEPTION' => -1,
+	'XC_FETCH_CLASS' => -1,
+	'XC_FETCH_' => -1,
+	'XC_FETCH_DIM_' => -1,
+	'XC_ASSIGN_DIM' => -1,
+	'XC_UNSET_DIM' => -1,
+	'XC_UNSET_OBJ' => -1,
+	'XC_ASSIGN_OBJ' => -1,
+	'XC_ISSET_ISEMPTY_DIM_OBJ' => -1,
+	'XC_ISSET_ISEMPTY_PROP_OBJ' => -1,
+	'XC_ISSET_ISEMPTY_VAR' => -1,
+	'XC_INIT_STATIC_METHOD_CALL' => -1,
+	'XC_INIT_METHOD_CALL' => -1,
+	'XC_VERIFY_ABSTRACT_CLASS' => -1,
+	'XC_DECLARE_CLASS' => -1,
+	'XC_DECLARE_INHERITED_CLASS' => -1,
+	'XC_DECLARE_INHERITED_CLASS_DELAYED' => -1,
+	'XC_ADD_INTERFACE' => -1,
+	'XC_POST_DEC_OBJ' => -1,
+	'XC_POST_INC_OBJ' => -1,
+	'XC_PRE_DEC_OBJ' => -1,
+	'XC_PRE_INC_OBJ' => -1,
+	'XC_UNSET_OBJ' => -1,
+	'XC_JMP_NO_CTOR' => -1,
+	'XC_FETCH_' => -1,
+	'XC_FETCH_DIM_' => -1,
+	'XC_UNSET_DIM_OBJ' => -1,
+	'XC_ISSET_ISEMPTY' => -1,
+	'XC_INIT_FCALL_BY_FUNC' => -1,
+	'XC_DO_FCALL_BY_FUNC' => -1,
+	'XC_DECLARE_FUNCTION_OR_CLASS' => -1,
+	'XC_INIT_NS_FCALL_BY_NAME' => -1,
+	'XC_GOTO' => -1,
+	'XC_CATCH' => -1,
+	'XC_THROW' => -1,
+	'XC_INSTANCEOF' => -1,
+	'XC_DECLARE_FUNCTION' => -1,
+	'XC_RAISE_ABSTRACT_ERROR' => -1,
+	'XC_DECLARE_CONST' => -1,
+	'XC_USER_OPCODE' => -1,
+	'XC_JMP_SET' => -1,
+	'XC_DECLARE_LAMBDA_FUNCTION' => -1,
+) as $k => $v) {
+	if (!defined($k)) {
+		define($k, $v);
+	}
+}
+// }}}
+
Index: /trunk/bin/phpdop.phpr
===================================================================
--- /trunk/bin/phpdop.phpr	(revision 982)
+++ /trunk/bin/phpdop.phpr	(revision 982)
@@ -0,0 +1,130 @@
+#! /usr/bin/php
+<?php
+
+$srcdir = dirname(__FILE__);
+require_once("$srcdir/../lib/Decompiler.class.php");
+if (file_exists("$srcdir/phpdc.debug.php")) {
+	include("$srcdir/phpdc.debug.php");
+}
+
+function get_op($op)
+{
+	switch ($op['op_type']) {
+	case 1: // CONST
+		return var_export($op['constant'], true);
+
+	case 2: // IS_TMP_VAR
+		return 't@' . $op['var'];
+
+	case 4:
+		return 'v$' . $op['var'];
+
+	case 8: // UNUSED
+		if (isset($op['opline_num'])) {
+			return 'l#' . $op['opline_num'];
+		}
+		else {
+			return '-';
+		}
+
+	default:
+		return $op['op_type'] . $op['var'];
+	}
+}
+
+function dump_opcodes($opcodes, $indent = '')
+{
+	global $decompiler;
+
+	$types = array('result' => 5, 'op1' => 20, 'op2' => 20);
+	foreach ($decompiler->fixOpcode($opcodes) as $line => $op) {
+		echo $indent;
+		echo sprintf("%3d ", $op['lineno']);
+		echo sprintf("%3x ", $line);
+		$name = xcache_get_opcode($op['opcode']);
+
+		if (substr($name, 0, 5) == 'ZEND_') {
+			$name = substr($name, 5);
+		}
+		echo str_pad($name, 25);
+
+		foreach ($types as $t => $len) {
+			echo str_pad(isset($op[$t]) ? get_op($op[$t]) : "", $len);
+		}
+		printf("%5s", isset($op['extended_value']) ?  $op['extended_value'] : "");
+
+		echo "\n";
+	}
+}
+
+function dump_function($name, $func, $indent = '')
+{
+	if (isset($func['op_array'])) {
+		$op_array = $func['op_array'];
+		unset($func['op_array']);
+	}
+	else {
+		$op_array = null;
+	}
+	var_dump($func);
+	echo $indent, 'function ', $name, "\n";
+	if (isset($op_array)) {
+		dump_opcodes($op_array['opcodes'], "  " . $indent);
+	}
+}
+
+function dump_class($name, $class, $indent = '')
+{
+	if (isset($class['function_table'])) {
+		$funcs = $class['function_table'];
+		unset($class['function_table']);
+	}
+	else {
+		$funcs = null;
+	}
+	echo $indent, 'class ', $name, "\n";
+	if (isset($funcs)) {
+		foreach ($funcs as $name => $func) {
+			dump_function($name, $func, "  " . $indent);
+		}
+	}
+}
+
+if (!isset($argv[1])) {
+	die("Usage: $argv[0] <file>\n");
+}
+$decompiler = new Decompiler();
+if (isset($argv[2])) {
+	eval('$pk = ' . file_get_contents($argv[2]) . ';');
+}
+else {
+	$pk = xcache_dasm_file($argv[1]);
+}
+$op_array = $funcs = $classes = null;
+if (isset($pk['op_array'])) {
+	$op_array = $pk['op_array'];
+	unset($pk['op_array']);
+}
+if (isset($pk['function_table'])) {
+	$funcs = $pk['function_table'];
+	unset($pk['function_table']);
+}
+if (isset($pk['class_table'])) {
+	$classes = $pk['class_table'];
+	unset($pk['class_table']);
+}
+var_dump($pk);
+if (isset($classes)) {
+	foreach ($classes as $name => $class) {
+		dump_class($name, $class);
+	}
+}
+if (isset($funcs)) {
+	foreach ($funcs as $name => $func) {
+		dump_function($name, $func);
+	}
+}
+if (isset($op_array)) {
+	dump_opcodes($op_array['opcodes']);
+}
+
Index: /trunk/bin/phpdc.phpr
===================================================================
--- /trunk/bin/phpdc.phpr	(revision 982)
+++ /trunk/bin/phpdc.phpr	(revision 982)
@@ -0,0 +1,33 @@
+#! /usr/bin/php -dopen_basedir=
+<?php
+
+$srcdir = dirname(__FILE__);
+require_once("$srcdir/../lib/Decompiler.class.php");
+if (file_exists("$srcdir/phpdc.debug.php")) {
+	include("$srcdir/phpdc.debug.php");
+}
+
+if (!isset($argv)) {
+	$argv = $_SERVER['argv'];
+	$argc = $_SERVER['argc'];
+}
+
+$dc = new Decompiler();
+if (isset($argv[2])) {
+	eval('$dc->dc = ' . file_get_contents($argv[2]) . ';');
+}
+else if (isset($argv[1])) {
+	$dc->decompileFile($argv[1]);
+}
+else {
+	$phpcode = '';
+	if (!defined('stdin')) {
+		define('stdin', fopen('php://stdin', 'rb'));
+	}
+	while (!feof(stdin)) {
+		$phpcode .= fgets(stdin);
+	}
+	$dc->decompileString($phpcode);
+}
+$dc->output();
+
Index: /trunk/htdocs/cacher/index.php
===================================================================
--- /trunk/htdocs/cacher/index.php	(revision 605)
+++ /trunk/htdocs/cacher/index.php	(revision 605)
@@ -0,0 +1,3 @@
+<?php
+
+include("xcache.php");
Index: /trunk/htdocs/cacher/common-en.lang.php
===================================================================
--- /trunk/htdocs/cacher/common-en.lang.php	(revision 1038)
+++ /trunk/htdocs/cacher/common-en.lang.php	(revision 1038)
@@ -0,0 +1,81 @@
+<?php
+
+$GLOBALS['config']['show_todo_strings'] = false;
+
+$strings = array(
+		'cache.cache'
+		=> 'Cache|',
+		'cache.slots'
+		=> 'Slots|Number of hash slots. the setting from your php.ini',
+		'cache.size'
+		=> 'Size|Cache Size, Size of the cache (or cache chunk), in bytes',
+		'cache.avail'
+		=> 'Avail|Available Memory, free memory in bytes of this cache',
+		'cache.used'
+		=> 'Used|Used Memory, used memory in bytes of this cache',
+		'cache.blocksgraph'
+		=> 'Percent Graph|Shows how much memory available in percent, and memory blocks status in graph',
+		'cache.operations'
+		=> 'Operations|Press the clear button to clean this cache',
+		'cache.compiling'
+		=> 'Comp.|Compiling flag, "yes" if the cache is busy compiling php script',
+		'cache.hits'
+		=> 'Hits|Cache Hits, hit=a var/php is loaded from this cache',
+		'cache.hits_avg_h'
+		=> 'Hits/H|Average Hits per Hour. Only last 24 hours is logged',
+		'cache.hits_graph'
+		=> 'Hits*24H|Hits graph of last 24 hours',
+		'cache.hits_avg_s'
+		=> 'Hits/S|Average Hits per Second. Only last 5 seconds is logged',
+		'cache.updates'
+		=> 'Updates|Cache Updates',
+		'cache.clogs'
+		=> 'Clogs|Compiling Clogs, clog=compiling is needed but avoided to wait(be blocked) when the cache is busy compiling already',
+		'cache.ooms'
+		=> 'OOMs|Out Of Memory, how many times a new item should be stored but there isn\'t enough memory in the cache, think of increasing the xcache.size or xcache.var_size',
+		'cache.errors'
+		=> 'Errs|Compiler errors, how many times your script is compiled but failed. You should really check what is happening if you see this value increase. (See Help for more information)',
+		'cache.readonly_protected'
+		=> 'Protected|Whether readonly_protection is available and enable on this cache (See help for more information)',
+		'cache.cached'
+		=> 'Cached|Number of entries stored in this cache',
+		'cache.deleted'
+		=> 'Deleted|Number of entries is pending in delete list (expired but referenced)',
+		'cache.gc_timer'
+		=> 'GC|Seconds count down of Garbage Collection',
+		'entry.id'
+		=> 'Id|',
+		'entry.name'
+		=> 'Entry name|The entry name or filename',
+		'entry.hits'
+		=> 'Hits|Times this entry is hit (loaded from this cache)',
+		'entry.size'
+		=> 'Size|Size in bytes of this entry in the cache',
+		'entry.refcount'
+		=> 'Refs|Reference count of this entry is holded by a php request',
+		'entry.phprefcount'
+		=> 'Shares|Count of entry sharing this php data',
+		'entry.file_size'
+		=> 'Src Size|Size of the source file',
+		'entry.file_mtime'
+		=> 'Modified|Last modified time of the source file',
+		'entry.file_device'
+		=> 'dev|device number of the source file',
+		'entry.file_inode'
+		=> 'ino|inode number of the source file',
+		'entry.class_cnt'
+		=> 'Cls.|Count of classes',
+		'entry.function_cnt'
+		=> 'Funcs|Count of functions',
+		'entry.hash'
+		=> 'Hash|Hash value of this entry',
+		'entry.atime'
+		=> 'Access|Last time when this entry is accessed',
+		'entry.ctime'
+		=> 'Create|The time when this entry is stored',
+		'entry.delete'
+		=> 'Delete|The time when this entry is deleted',
+		'entry.remove'
+		=> 'Remove|',
+);
+
Index: /trunk/htdocs/cacher/help-en.lang.php
===================================================================
--- /trunk/htdocs/cacher/help-en.lang.php	(revision 1038)
+++ /trunk/htdocs/cacher/help-en.lang.php	(revision 1038)
@@ -0,0 +1,4 @@
+<dl>
+<dt>Compiler errors: </dt> <dd><a href="http://www.php.net/manual/en/ref.errorfunc.php#ini.error-log">ini.error-log</a> and <a href="http://cn2.php.net/manual/en/ref.errorfunc.php#ini.display-errors">ini.display-errors</a></dd>
+<dt>Readonly protection: </dt> <dd><a href="http://xcache.lighttpd.net/wiki/ReadonlyProtection">ReadonlyProtection</a></dd>
+</dl>
Index: /trunk/htdocs/cacher/help-zh-simplified-utf-8.lang.php
===================================================================
--- /trunk/htdocs/cacher/help-zh-simplified-utf-8.lang.php	(revision 1038)
+++ /trunk/htdocs/cacher/help-zh-simplified-utf-8.lang.php	(revision 1038)
@@ -0,0 +1,4 @@
+<dl>
+<dt>编译错误: </dt> <dd><a href="http://www.php.net/manual/en/ref.errorfunc.php#ini.error-log">ini.error-log</a> and <a href="http://cn2.php.net/manual/en/ref.errorfunc.php#ini.display-errors">ini.display-errors</a></dd>
+<dt>只读保护: </dt> <dd><a href="http://xcache.lighttpd.net/wiki/ReadonlyProtection">ReadonlyProtection</a></dd>
+</dl>
Index: /trunk/htdocs/cacher/common-zh-simplified-utf-8.lang.php
===================================================================
--- /trunk/htdocs/cacher/common-zh-simplified-utf-8.lang.php	(revision 1042)
+++ /trunk/htdocs/cacher/common-zh-simplified-utf-8.lang.php	(revision 1042)
@@ -0,0 +1,131 @@
+<?php
+
+$strings = array(
+		'XCache Help'
+		=> 'XCache 帮助信息',
+		'Help'
+		=> '帮助',
+		'Clear'
+		=> '清除',
+		'Sure to clear?'
+		=> '确认要清除吗?',
+		'% Free'
+		=> '% 剩余',
+		'% Used'
+		=> '% 已用',
+		'Hits'
+		=> '命中',
+		'Modify'
+		=> '修改',
+		'See also'
+		=> '建议参考',
+		'Legends:'
+		=> '图例:',
+		'Used Blocks'
+		=> '已用块',
+		'Free Blocks'
+		=> '未用块',
+		'Total'
+		=> '总共',
+		'Caches'
+		=> '缓存区',
+		'php Cached'
+		=> '缓存的 php 脚本',
+		'php Deleted'
+		=> '待删 php 缓存',
+		'var Cached'
+		=> '缓存的变量',
+		'var Deleted'
+		=> '待删变量',
+		'Statistics'
+		=> '统计信息',
+		'List PHP'
+		=> '列出PHP',
+		'List Var Data'
+		=> '列变量数据',
+		'XCache %s Administration'
+		=> 'XCache %s 管理页面',
+		'Module Info'
+		=> '模块信息',
+		'Remove Selected'
+		=> '删除所选',
+		'Editing Variable %s'
+		=> '正在编辑变量 %s',
+		'Set %s in config to enable'
+		=> '请在配置文件中设置 %s 启用本功能',
+		'cache.cache'
+		=> '缓存|',
+		'cache.slots'
+		=> '槽|Hash 槽个数, 对应 php.ini 里的设置',
+		'cache.size'
+		=> '大小|共享内存区大小, 单位: 字节',
+		'cache.avail'
+		=> '剩余|可用内存, 对应共享内存区的剩余内存字节数',
+		'cache.used'
+		=> '已用|已用内存, 对应共享内存区的已用内存字节数',
+		'cache.blocksgraph'
+		=> '百分比图|条状显示可用内存的比例, 以及显示分配块状态',
+		'cache.operations'
+		=> '操作|点击按钮清除对应共享内存区的数据',
+		'cache.compiling'
+		=> '编译中|编译标记, 当共享内存区正在编译 php 脚本时标记为 "yes"',
+		'cache.hits'
+		=> '命中|共享内存命中次数, 命中=从该共享内存载入php或者变量',
+		'cache.hits_avg_h'
+		=> '命中/H|每小时命中次数. 只统计最后 24 小时',
+		'cache.hits_graph'
+		=> '24H 分布|24 小时命中分布图. 图表现是最后 24 小时的命中次数',
+		'cache.hits_avg_s'
+		=> '命中/S|每秒命中次数. 只统计最后 5 秒',
+		'cache.updates'
+		=> '更新|共享内存更新次数',
+		'cache.clogs'
+		=> '阻塞|编译阻塞跳过, 阻塞=当需该共享内存区负责编译时, 其他进程/现成无法访问此共享内存. 跳过=XCache 自动判断阻塞的共享内存区自动跳过阻塞等待, 直接使用非共享内存方式继续处理请求',
+		'cache.ooms'
+		=> '内存不足|内存不足次数, 显示需要存储新数据但是共享内存区内存不足的次数. 如果出现太频繁请考虑加大配置中的 xcache.size 或者 xcache.var_size',
+		'cache.errors'
+		=> '错误|编译错误, 显示您的脚本被编译时出错的次数. 如果您发现这个数字不断增长, 您应该检查什么脚本产生错误. 参考 帮助 获取更多信息',
+		'cache.readonly_protected'
+		=> '保护|显示该 Cache 是否支持并启用 readonly_protection. 参考 帮助 获取更多信息',
+		'cache.cached'
+		=> '缓存|共享内存于该共享内存区的项目条数',
+		'cache.deleted'
+		=> '待删|共享内存区内将要删除的项目 (已经删除但是还被某些进程占用)',
+		'cache.gc_timer'
+		=> 'GC|垃圾回收的倒计时',
+		'entry.id'
+		=> 'Id|',
+		'entry.name'
+		=> '项目名/文件名|项目名或者文件名',
+		'entry.hits'
+		=> '命中|该项目被命中的次数 (从共享内存区载入)',
+		'entry.size'
+		=> '大小|项目在共享内存里占用字节数',
+		'entry.refcount'
+		=> '引用数|项目依然被其他进程占据的引用次数',
+		'entry.phprefcount'
+		=> '共享数|与本项目相同 PHP 代码的个数',
+		'entry.file_size'
+		=> '源大小|源文件大小',
+		'entry.file_mtime'
+		=> '修改|源文件最后修改时间',
+		'entry.file_device'
+		=> 'dev|源文件所在设备ID',
+		'entry.file_inode'
+		=> 'ino|源文件的 inode',
+		'entry.class_cnt'
+		=> '类|类个数',
+		'entry.function_cnt'
+		=> '函数|函数个数',
+		'entry.hash'
+		=> '哈希|该项目的哈希值',
+		'entry.atime'
+		=> '访问|最后访问该项目的时间',
+		'entry.ctime'
+		=> '创建|该项目被创建于共享内的时间',
+		'entry.delete'
+		=> '删除|该项目被决定删除的时间',
+		'entry.remove'
+		=> '删除|',
+);
+
Index: /trunk/htdocs/cacher/edit.tpl.php
===================================================================
--- /trunk/htdocs/cacher/edit.tpl.php	(revision 1038)
+++ /trunk/htdocs/cacher/edit.tpl.php	(revision 1038)
@@ -0,0 +1,18 @@
+<?php include("header.tpl.php"); ?>
+<?php
+$h_name = htmlspecialchars($name);
+$h_value = htmlspecialchars($value);
+?>
+<form method="post" action="">
+	<fieldset>
+		<legend><?php echo sprintf(_("Editing Variable %s"), $h_name); ?></legend>
+		<textarea name="value" style="width: 100%; height: 200px; overflow-y: auto" <?php echo $editable ? "" : "disabled=disabled"; ?>><?php echo $h_value; ?></textarea><br>
+		<input type="submit" <?php echo $editable ? "" : "disabled=disabled"; ?>>
+		<?php
+		if (!$editable) {
+			echo sprintf(_("Set %s in config to enable"), "\$config['enable_eval'] = true");
+		}
+		?>
+	</fieldset>
+</form>
+<?php include("footer.tpl.php"); ?>
Index: /trunk/htdocs/cacher/tablesort.js
===================================================================
--- /trunk/htdocs/cacher/tablesort.js	(revision 1037)
+++ /trunk/htdocs/cacher/tablesort.js	(revision 1037)
@@ -0,0 +1,58 @@
+var sort_column;
+var prev_span = null;
+function get_inner_text(el) {
+ if((typeof el == 'string')||(typeof el == 'undefined'))
+  return el;
+ if(el.innerText)
+  return el.innerText;
+ else {
+  var str = "";
+  var cs = el.childNodes;
+  var l = cs.length;
+  for (var i=0;i<l;i++) {
+   if (cs[i].nodeType==1) str += get_inner_text(cs[i]);
+   else if (cs[i].nodeType==3) str += cs[i].nodeValue;
+  }
+ }
+ return str;
+}
+function sortfn(a,b) {
+ var i = a.cells[sort_column].getAttribute('int');
+ if (i != null) {
+  return parseInt(i)-parseInt(b.cells[sort_column].getAttribute('int'));
+ } else {
+  var at = get_inner_text(a.cells[sort_column]);
+  var bt = get_inner_text(b.cells[sort_column]);
+  aa = at.toLowerCase();
+  bb = bt.toLowerCase();
+  if (aa==bb) return 0;
+  else if (aa<bb) return -1;
+  else return 1;
+ }
+}
+function resort(lnk) {
+ var span = lnk.childNodes[1];
+ if (!span) {
+ 	 var span = document.createElement("span")
+ 	 span.className = "sortarrow";
+ 	 lnk.appendChild(span);
+ }
+ var table = lnk.parentNode.parentNode.parentNode.parentNode;
+ var rows = new Array();
+ for (j=1;j<table.rows.length;j++)
+  rows[j-1] = table.rows[j];
+ sort_column = lnk.parentNode.cellIndex;
+ rows.sort(sortfn);
+ if (prev_span != null) prev_span.innerHTML = '';
+ if (span.getAttribute('sortdir')=='down') {
+  span.innerHTML = '&uarr;';
+  span.setAttribute('sortdir','up');
+  rows.reverse();
+ } else {
+  span.innerHTML = '&darr;';
+  span.setAttribute('sortdir','down');
+ }
+ for (i=0;i<rows.length;i++)
+  table.tBodies[0].appendChild(rows[i]);
+ prev_span = span;
+}
Index: /trunk/htdocs/cacher/edit.php
===================================================================
--- /trunk/htdocs/cacher/edit.php	(revision 934)
+++ /trunk/htdocs/cacher/edit.php	(revision 934)
@@ -0,0 +1,45 @@
+<?php
+
+include("./common.php");
+
+if (!isset($_GET['name'])) {
+	die("missing name");
+}
+
+$name = $_GET['name'];
+// trigger auth
+$vcnt = xcache_count(XC_TYPE_VAR);
+
+if ($_SERVER['REQUEST_METHOD'] == 'POST') {
+	if (!empty($config['enable_eval'])) {
+		eval('$value = ' . $_POST['value']);
+	}
+	else {
+		$value = $_POST['value'];
+	}
+	xcache_set($name, $value);
+	header("Location: xcache.php?type=" . XC_TYPE_VAR);
+	exit;
+}
+$value = xcache_get($name);
+if (!empty($enable['enable_eval'])) {
+	$value = var_export($value, true);
+	$editable = true;
+}
+else {
+	if (is_string($value)) {
+		$editable = true;
+	}
+	else {
+		$editable = false;
+		$value = var_export($value, true);
+	}
+}
+
+$php_version = phpversion();
+$xcache_version = XCACHE_VERSION;
+$xcache_modules = XCACHE_MODULES;
+
+include("edit.tpl.php");
+
+?>
Index: /trunk/htdocs/cacher/header.tpl.php
===================================================================
--- /trunk/htdocs/cacher/header.tpl.php	(revision 1038)
+++ /trunk/htdocs/cacher/header.tpl.php	(revision 1038)
@@ -0,0 +1,17 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<?php
+echo <<<HEAD
+	<meta http-equiv="Content-Type" content="text/html; charset=$config[charset]" />
+	<meta http-equiv="Content-Language" content="$config[lang]" />
+	<script type="text/javascript" src="tablesort.js" charset="$config[charset]"></script>
+HEAD;
+?>
+
+	<link rel="stylesheet" type="text/css" href="xcache.css" />
+	<title><?php echo sprintf(_("XCache %s Administration"), $xcache_version); ?></title>
+</head>
+
+<body>
+<h1><?php echo sprintf(_("XCache %s Administration"), $xcache_version); ?></h1>
Index: /trunk/htdocs/cacher/common.php
===================================================================
--- /trunk/htdocs/cacher/common.php	(revision 1038)
+++ /trunk/htdocs/cacher/common.php	(revision 1038)
@@ -0,0 +1,152 @@
+<?php
+
+function xcache_validateFileName($name)
+{
+	return preg_match('!^[a-zA-Z0-9._-]+$!', $name);
+}
+
+function get_language_file_ex($name, $l, $s)
+{
+	static $lmap = array(
+			'zh'    => 'zh-simplified',
+			'zh-hk' => 'zh-traditional',
+			'zh-tw' => 'zh-traditional',
+			);
+	static $smap = array(
+			'gbk'     => 'gb2312',
+			'gb18030' => 'gb2312',
+			);
+
+	if (isset($lmap[$l])) {
+		$l = $lmap[$l];
+	}
+	$file = "$name-$l-$s.lang.php";
+	if (xcache_validateFileName($file) && file_exists($file)) {
+		return $file;
+	}
+	if (isset($smap[$s])) {
+		$s = $smap[$s];
+		$file = "$name-$l-$s.lang.php";
+		if (xcache_validateFileName($file) && file_exists($file)) {
+			return $file;
+		}
+	}
+	$file = "$name-$l.lang.php";
+	if (xcache_validateFileName($file) && file_exists($file)) {
+		return $file;
+	}
+	return null;
+}
+
+function get_language_file($name)
+{
+	global $config;
+	$s = strtolower($config['charset']);
+	if (!empty($config['lang'])) {
+		$l = strtolower($config['lang']);
+		$file = get_language_file_ex($name, $l, $s);
+		if (!isset($file)) {
+			$l = strtok($l, ':-');
+			$file = get_language_file_ex($name, $l, $s);
+		}
+	}
+	else if (!empty($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
+		foreach (explode(',', str_replace(' ', '', $_SERVER['HTTP_ACCEPT_LANGUAGE'])) as $l) {
+			$l = strtok($l, ':;');
+			$file = get_language_file_ex($name, $l, $s);
+			if (isset($file)) {
+				$config['lang'] = $l;
+				break;
+			}
+			if (strpos($l, '-') !== false) {
+				$ll = strtok($l, ':-');
+				$file = get_language_file_ex($name, $ll, $s);
+				if (isset($file)) {
+					$config['lang'] = $l;
+					break;
+				}
+			}
+		}
+	}
+	return isset($file) ? $file : "$name-en.lang.php";
+}
+
+function _($str)
+{
+	if (isset($GLOBALS['strings'][$str])) {
+		return $GLOBALS['strings'][$str];
+	}
+	if (!empty($GLOBALS['config']['show_todo_strings'])) {
+		return '<span style="color:red">' . htmlspecialchars($str) . '</span>|';
+	}
+	return $str;
+}
+
+function __($str)
+{
+	return _($str);
+}
+
+function N_($str)
+{
+	return $str;
+}
+
+function stripaddslashes_array($value, $mqs = false)
+{
+	if (is_array($value)) {
+		foreach($value as $k => $v) {
+			$value[$k] = stripaddslashes_array($v, $mqs);
+		}
+	}
+	else if(is_string($value)) {
+		$value = $mqs ? str_replace('\'\'', '\'', $value) : stripslashes($value);
+	}
+	return $value;
+}
+
+function ob_filter_path_nicer_default($list_html)
+{
+	$sep = DIRECTORY_SEPARATOR;
+	$docRoot = $_SERVER['DOCUMENT_ROOT'];
+	if ($sep != '/') {
+		$docRoot = str_replace('/', $sep, $docRoot);
+	}
+	$list_html = str_replace(">$docRoot",  ">{DOCROOT}" . (substr($docRoot, -1) == $sep ? $sep : ""), $list_html);
+	$xcachedir = realpath(dirname(__FILE__) . "$sep..$sep");
+	$list_html = str_replace(">$xcachedir$sep", ">{XCache}$sep", $list_html);
+	if ($sep == '/') {
+		$list_html = str_replace(">/home/", ">{H}/", $list_html);
+	}
+	return $list_html;
+}
+
+
+error_reporting(E_ALL);
+ini_set('display_errors', 'On');
+define('REQUEST_TIME', time());
+
+if (function_exists('get_magic_quotes_gpc') && @get_magic_quotes_gpc()) {
+	$mqs = (bool) ini_get('magic_quotes_sybase');
+	$_GET = stripaddslashes_array($_GET, $mqs);
+	$_POST = stripaddslashes_array($_POST, $mqs);
+	$_REQUEST = stripaddslashes_array($_REQUEST, $mqs);
+	unset($mqs);
+}
+ini_set('magic_quotes_runtime', '0');
+
+$config = array();
+include("./config.default.php");
+if (file_exists("./config.php")) {
+	include("./config.php");
+}
+
+include(get_language_file("common"));
+if (empty($config['lang'])) {
+	$config['lang'] = 'en-us';
+}
+
+header("Cache-Control: no-cache, must-revalidate");
+header("Expires: Sat, 26 Jul 1997 05:00:00 GMT");
+
+?>
Index: /trunk/htdocs/cacher/config.default.php
===================================================================
--- /trunk/htdocs/cacher/config.default.php	(revision 920)
+++ /trunk/htdocs/cacher/config.default.php	(revision 920)
@@ -0,0 +1,26 @@
+<?php
+
+// this is default config
+// DO NOT rename/delete/modify this file
+// see config.example.php
+
+// detected by browser
+// $config['lang'] = 'en-us';
+
+$config['charset'] = "UTF-8";
+
+// translators only
+$config['show_todo_strings'] = false;
+
+// width of graph for free or usage blocks
+$config['percent_graph_width'] = 120;
+$config['percent_graph_type'] = 'used'; // either 'used' or 'free'
+
+// only enable if you have password protection for admin page
+// enabling this option will cause user to eval() whatever code they want
+$config['enable_eval'] = false;
+
+// this ob filter is applied for the cache list, not the whole page
+$config['path_nicer'] = 'ob_filter_path_nicer_default';
+
+?>
Index: /trunk/htdocs/cacher/help.php
===================================================================
--- /trunk/htdocs/cacher/help.php	(revision 1038)
+++ /trunk/htdocs/cacher/help.php	(revision 1038)
@@ -0,0 +1,33 @@
+<?php
+include("./common.php");
+?>
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<?php
+echo <<<HEAD
+	<meta http-equiv="Content-Type" content="text/html; charset=$config[charset]" />
+	<meta http-equiv="Content-Language" content="$config[lang]" />
+	<script type="text/javascript" src="tablesort.js" charset="$config[charset]"></script>
+HEAD;
+?>
+
+	<link rel="stylesheet" type="text/css" href="xcache.css" />
+	<title><?php echo _('XCache Help'); ?></title>
+	<script>
+	function toggle(o)
+	{
+		o.style.display = o.style.display != 'block' ? 'block' : 'none';
+	}
+	</script>
+</head>
+
+<body>
+<h1><?php echo _('XCache Help'); ?></h1>
+<div id1="help">
+<?php include(get_language_file("help")); ?>
+</div>
+
+<?php echo _('See also'); ?>: <a href="http://xcache.lighttpd.net/wiki/PhpIni">Setting php.ini for XCache</a> @ <a href="http://xcache.lighttpd.net/">XCache wiki</a>
+</body>
+</html>
Index: /trunk/htdocs/cacher/xcache.tpl.php
===================================================================
--- /trunk/htdocs/cacher/xcache.tpl.php	(revision 1041)
+++ /trunk/htdocs/cacher/xcache.tpl.php	(revision 1041)
@@ -0,0 +1,301 @@
+<?php include("header.tpl.php"); ?>
+<div id="help">
+	<a href="help.php"><?php echo _("Help") ?> &raquo;</a>
+</div>
+<div class="switcher"><?php echo switcher("type", $types); ?></div>
+<?php
+$a = new Cycle('class="col1"', 'class="col2"');
+$b = new Cycle('class="col1"', 'class="col2"');
+?>
+<table cellspacing="0" cellpadding="4" class="cycles caches">
+	<caption><?php echo _('Caches'); ?></caption>
+	<col />
+	<col align="right" />
+	<col align="right" />
+	<col align="right" />
+	<col />
+	<col />
+	<col align="right" />
+	<col align="right" />
+	<col align="right" />
+	<col />
+	<col align="right" />
+	<col align="right" />
+	<col align="right" />
+	<col align="right" />
+	<col align="right" />
+	<col align="right" />
+	<col align="right" />
+	<col />
+	<tr <?php echo $a->next(); ?>>
+	<?php echo
+		th(N_("cache.cache"))
+		, th(N_("cache.size"))
+		, th(N_("cache.avail"))
+		, th(N_("cache.used"))
+		, th(N_("cache.blocksgraph"))
+		, th(N_("cache.operations"))
+		, th(N_("cache.compiling"))
+		, th(N_("cache.hits"))
+		, th(N_("cache.hits_graph"))
+		, th(N_("cache.hits_avg_h"))
+		, th(N_("cache.hits_avg_s"))
+		, th(N_("cache.updates"))
+		, th(N_("cache.clogs"))
+		, th(N_("cache.ooms"))
+		, th(N_("cache.errors"))
+		, th(N_("cache.readonly_protected"))
+		, th(N_("cache.cached"))
+		, th(N_("cache.deleted"))
+		, th(N_("cache.gc_timer"))
+		;
+	?>
+	</tr>
+	<?php
+	$numkeys = explode(',', 'slots,size,avail,hits,updates,clogs,ooms,errors,cached,deleted');
+	$l_clear = _('Clear');
+	$l_clear_confirm = _('Sure to clear?');
+	foreach ($cacheinfos as $i => $ci) {
+		echo "
+		<tr ", $a->next(), ">";
+		$pvalue = (int) ($ci['avail'] / $ci['size'] * 100);
+		$pempty = 100 - $pvalue;
+		if ($config['percent_graph_type'] == 'used') {
+			// swap
+			$tmp = $pvalue;
+			$pvalue = $pempty;
+			$pempty = $tmp;
+		}
+
+		$w = $config['percent_graph_width'];
+		if (empty($ci['istotal'])) {
+			$graph = freeblock_to_graph($ci['free_blocks'], $ci['size']);
+			$blocksgraph = "<div class=\"blocksgraph\" style=\"width: {$w}px\">{$graph}</div>";
+		}
+		else {
+			$blocksgraph = '';
+		}
+
+		$ci_slots = size($ci['slots']);
+		$ci_size  = size($ci['size']);
+		$ci_avail = size($ci['avail']);
+		$ci = number_formats($ci, $numkeys);
+
+		$hits_avg_h     = number_format(array_avg($ci['hits_by_hour']), 2);
+		$hits_avg_s     = number_format(array_avg($ci['hits_by_second']), 2);
+		$hits_graph_h   = hits_to_graph($ci['hits_by_hour']);
+		$hits_graph_h_w = count($ci['hits_by_hour']) * 2;
+
+		if (!empty($ci['istotal'])) {
+			$ci['compiling']    = '-';
+			$ci['can_readonly'] = '-';
+		}
+		else {
+			$ci['compiling']    = $ci['type'] == $type_php ? ($ci['compiling'] ? 'yes' : 'no') : '-';
+			$ci['can_readonly'] = $ci['can_readonly'] ? 'yes' : 'no';
+		}
+		echo <<<EOS
+		<th>{$ci['cache_name']}</th>
+		<td title="{$ci['slots']}">{$ci_slots}</td>
+		<td title="{$ci['size']}">{$ci_size}</td>
+		<td title="{$ci['avail']}">{$ci_avail}</td>
+		<td title="{$pvalue} %"
+			><div class="percent" style="width: {$w}px"
+				><div style="width: {$pvalue}%" class="pvalue"></div
+				><div style="width: {$pempty}%" class="pempty"></div
+			></div
+		>{$blocksgraph}</td>
+		<td
+			><form method="post" action=""
+				><div
+					><input type="hidden" name="type" value="{$ci['type']}"
+					/><input type="hidden" name="cacheid" value="{$ci['cacheid']}"
+					/><input type="submit" name="clearcache" value="{$l_clear}" class="submit" onclick="return confirm('{$l_clear_confirm}');"
+				/></div
+			></form
+		></td>
+		<td>{$ci['compiling']}</td>
+		<td>{$ci['hits']}</td>
+		<td><div class="hitsgraph" style="width: {$hits_graph_h_w}px">{$hits_graph_h}</div></td>
+		<td>{$hits_avg_h}</td>
+		<td>{$hits_avg_s}</td>
+		<td>{$ci['updates']}</td>
+		<td>{$ci['clogs']}</td>
+		<td>{$ci['ooms']}</td>
+		<td>{$ci['errors']}</td>
+		<td>{$ci['can_readonly']}</td>
+		<td>{$ci['cached']}</td>
+		<td>{$ci['deleted']}</td>
+		<td>{$ci['gc']}</td>
+EOS;
+
+			$b->reset();
+			?>
+	</tr>
+	<?php } ?>
+</table>
+<div class="blockarea legends">
+	<div class="legendtitle"><?php echo _('Legends:'); ?></div>
+	<div class="legend pvalue">&nbsp;&nbsp;</div>
+	<div class="legendtitle"><?php echo _($config['percent_graph_type'] == 'free' ? '% Free' : '% Used'); ?></div>
+	<div class="legend" style="background: rgb(0,0,255)">&nbsp;&nbsp;</div>
+	<div class="legendtitle"><?php echo _($config['percent_graph_type'] == 'free' ? 'Free Blocks' : 'Used Blocks'); ?></div>
+	<div class="legend" style="background: rgb(255,0,0)">&nbsp;&nbsp;</div>
+	<div class="legendtitle"><?php echo _('Hits'); ?></div>
+</div>
+<?php
+
+if ($cachelist) {
+	$isphp = $cachelist['type'] == $type_php;
+	ob_start($config['path_nicer']);
+	foreach (array('Cached' => $cachelist['cache_list'], 'Deleted' => $cachelist['deleted_list']) as $listname => $entries) {
+		$a->reset();
+		?>
+
+	<form action="" method="post">
+	<table cellspacing="0" cellpadding="4" class="cycles entries" width="100%">
+		<caption><?php echo _("{$cachelist['type_name']} $listname"); ?></caption>
+		<?php
+		echo "
+		<tr ", $a->next(), ">";
+		?>
+
+			<?php
+			if ($isphp) {
+				echo
+					th("entry.id")
+					;
+			}
+			else {
+				echo
+					th("entry.remove", 'width="20"')
+					;
+			}
+
+			echo
+				th(N_("entry.name"))
+				, th(N_("entry.hits"))
+				, th(N_("entry.size"))
+				;
+
+			if ($isphp) {
+				echo
+					th(N_("entry.refcount"))
+					, th(N_("entry.phprefcount"))
+					, th(N_("entry.class_cnt"))
+					, th(N_("entry.function_cnt"))
+					, th(N_("entry.file_size"))
+					, th(N_("entry.file_mtime"))
+					;
+				if ($haveinode) {
+					echo
+						th(N_("entry.file_device"))
+						, th(N_("entry.file_inode"))
+						;
+				}
+			}
+			echo
+				th(N_("entry.hash"))
+				, th(N_("entry.atime"))
+				, th(N_("entry.ctime"))
+				;
+
+			if ($listname == 'Deleted') {
+				echo
+					th(N_("entry.delete"))
+					;
+			}
+			?>
+		</tr>
+		<?php
+		foreach ($entries as $i => $entry) {
+			echo "
+			<tr ", $a->next(), ">";
+			$name     = htmlspecialchars($entry['name']);
+			$hits     = number_format($entry['hits']);
+			$size     = size($entry['size']);
+			$class_cnt = number_format($entry['class_cnt']);
+			$function_cnt = number_format($entry['function_cnt']);
+			if ($isphp) {
+				$phprefcount = number_format($entry['phprefcount']);
+				$file_size   = size($entry['file_size']);
+			}
+
+			if ($isphp) {
+				$file_mtime = age($entry['file_mtime']);
+			}
+			$ctime = age($entry['ctime']);
+			$atime = age($entry['atime']);
+			if ($listname == 'Deleted') {
+				$dtime = age($entry['dtime']);
+			}
+
+			if (!$isphp) {
+				echo <<<ENTRY
+					<td><input type="checkbox" name="remove[]" value="{$name}"/></td>
+ENTRY;
+				$uname = urlencode($entry['name']);
+				$namelink = "<a href=\"edit.php?name=$uname\">$name</a>";
+			}
+			else {
+				$namelink = $name;
+			}
+
+			echo <<<ENTRY
+			<td>{$entry['cache_name']} {$i}</td>
+			<td>{$namelink}</td>
+			<td align="right" int="{$entry['hits']}">{$entry['hits']}</td>
+			<td align="right" int="{$entry['size']}">{$size}</td>
+ENTRY;
+			if ($isphp) {
+				$refcount = number_format($entry['refcount']);
+				echo <<<ENTRY
+				<td align="right" int="{$entry['refcount']}">{$entry['refcount']}</td>
+				<td align="right" int="{$entry['phprefcount']}">{$phprefcount}</td>
+				<td align="right" int="{$entry['class_cnt']}">{$class_cnt}</td>
+				<td align="right" int="{$entry['function_cnt']}">{$function_cnt}</td>
+				<td align="right" int="{$entry['file_size']}">{$file_size}</td>
+				<td align="right" int="{$entry['file_mtime']}">{$file_mtime}</td>
+ENTRY;
+				if (isset($entry['file_inode'])) {
+					echo <<<ENTRY
+					<td align="right" int="{$entry['file_device']}">{$entry['file_device']}</td>
+					<td align="right" int="{$entry['file_inode']}">{$entry['file_inode']}</td>
+ENTRY;
+				}
+			}
+			echo <<<ENTRY
+			<td align="right" int="{$entry['hvalue']}">{$entry['hvalue']}</td>
+			<td align="right" int="{$entry['atime']}">{$atime}</td>
+			<td align="right" int="{$entry['ctime']}">{$ctime}</td>
+ENTRY;
+			if ($listname == 'Deleted') {
+			echo <<<ENTRY
+				<td align="right" int="{$entry['dtime']}">{$dtime}</td>
+ENTRY;
+			}
+
+			echo "
+		</tr>
+			";
+		}
+		?>
+
+	</table>
+	<?php if (!$isphp) { ?>
+	<input type="submit" value="<?php echo _("Remove Selected"); ?>">
+	<?php } ?>
+	</form>
+<?php
+	}
+	ob_end_flush();
+}
+if ($moduleinfo) {
+	$t_moduleinfo = _("Module Info");
+	echo <<<HTML
+<h2>$t_moduleinfo</h2>
+<div class="phpinfo">$moduleinfo</div>
+HTML;
+}
+?>
+<?php include("footer.tpl.php"); ?>
Index: /trunk/htdocs/cacher/mkpassword.php
===================================================================
--- /trunk/htdocs/cacher/mkpassword.php	(revision 546)
+++ /trunk/htdocs/cacher/mkpassword.php	(revision 546)
@@ -0,0 +1,24 @@
+<html>
+	<head>
+		<title>Simple MD5 password generator</title>
+	</head>
+	<body>
+		<h1>Simple MD5 password generator</h1>
+		<form method="post">
+			<fieldset>
+				md5: <input type="password" name="password"> <input type="submit"><br>
+				<div>
+				<?php
+				if ($_SERVER['REQUEST_METHOD'] == 'POST') {
+					$md5 = md5(@ $_POST['password']);
+					$offs = mt_rand(0 + 1, 31 - 1);
+					$md5_1 = substr($md5, 0, $offs);
+					$md5_2 = substr($md5, $offs);
+					echo "Result: <span>$md5_1</span><span>$md5_2</span>";
+				}
+				?>
+				</div>
+			</fieldset>
+		</form>
+	</body>
+</html>
Index: /trunk/htdocs/cacher/xcache.php
===================================================================
--- /trunk/htdocs/cacher/xcache.php	(revision 1041)
+++ /trunk/htdocs/cacher/xcache.php	(revision 1041)
@@ -0,0 +1,407 @@
+<?php
+
+include("./common.php");
+
+class Cycle
+{
+	var $values;
+	var $i;
+	var $count;
+
+	function Cycle($v)
+	{
+		$this->values = func_get_args();
+		$this->i = -1;
+		$this->count = count($this->values);
+	}
+
+	function next()
+	{
+		$this->i = ($this->i + 1) % $this->count;
+		return $this->values[$this->i];
+	}
+
+	function cur()
+	{
+		return $this->values[$this->i];
+	}
+
+	function reset()
+	{
+		$this->i = -1;
+	}
+}
+
+function number_formats($a, $keys)
+{
+	foreach ($keys as $k) {
+		$a[$k] = number_format($a[$k]);
+	}
+	return $a;
+}
+
+function size($size)
+{
+	$size = (int) $size;
+	if ($size < 1024)
+		return number_format($size, 2) . ' b';
+
+	if ($size < 1048576)
+		return number_format($size / 1024, 2) . ' K';
+
+	return number_format($size / 1048576, 2) . ' M';
+}
+
+function age($time)
+{
+	if (!$time) return '';
+	$delta = REQUEST_TIME - $time;
+
+	if ($delta < 0) {
+		$delta = -$delta;
+	}
+	
+  	static $seconds = array(1, 60, 3600, 86400, 604800, 2678400, 31536000);
+	static $name = array('s', 'm', 'h', 'd', 'w', 'M', 'Y');
+
+	for ($i = 6; $i >= 0; $i --) {
+		if ($delta >= $seconds[$i]) {
+			$ret = (int) ($delta / $seconds[$i]);
+			return $ret . ' ' . $name[$i];
+		}
+	}
+
+	return '0 s';
+}
+
+function freeblock_to_graph($freeblocks, $size)
+{
+	global $config;
+
+	// cached in static variable
+	static $graph_initial;
+	if (!isset($graph_initial)) {
+		$graph_initial = array_fill(0, $config['percent_graph_width'], 0);
+	}
+	$graph = $graph_initial;
+	foreach ($freeblocks as $b) {
+		$begin = $b['offset'] / $size * $config['percent_graph_width'];
+		$end = ($b['offset'] + $b['size']) / $size * $config['percent_graph_width'];
+
+		if ((int) $begin == (int) $end) {
+			$v = $end - $begin;
+			$graph[(int) $v] += $v - (int) $v;
+		}
+		else {
+			$graph[(int) $begin] += 1 - ($begin - (int) $begin);
+			$graph[(int) $end] += $end - (int) $end;
+			for ($i = (int) $begin + 1, $e = (int) $end; $i < $e; $i ++) {
+				$graph[$i] += 1;
+			}
+		}
+	}
+	$html = array();
+	$c = 255;
+	foreach ($graph as $k => $v) {
+		if ($config['percent_graph_type'] != 'free') {
+			$v = 1 - $v;
+		}
+		$v = (int) ($v * $c);
+		$r = $g = $c - $v;
+		$b = $c;
+		$html[] = '<div style="background: rgb(' . "$r,$g,$b" . ')"></div>';
+	}
+	return implode('', $html);
+}
+
+function calc_total(&$total, $data)
+{
+	foreach ($data as $k => $v) {
+		switch ($k) {
+		case 'type':
+		case 'cache_name':
+		case 'cacheid':
+		case 'free_blocks':
+			continue 2;
+		}
+		if (!isset($total[$k])) {
+			$total[$k] = $v;
+		}
+		else {
+			switch ($k) {
+			case 'hits_by_hour':
+			case 'hits_by_second':
+				foreach ($data[$k] as $kk => $vv) {
+					$total[$k][$kk] += $vv;
+				}
+				break;
+
+			default:
+				$total[$k] += $v;
+			}
+		}
+	}
+}
+
+function array_avg($a)
+{
+	if (count($a) == 0) {
+		return '';
+	}
+	return array_sum($a) / count($a);
+}
+
+function bar_hits_percent($v, $percent, $active)
+{
+	$r = 220 + (int) ($percent * 25);
+	$g = $b = 220 - (int) ($percent * 220);
+	$percent = (int) ($percent * 100);
+	$a = $active ? ' active' : '';
+	return '<div title="' . $v . '">'
+		. '<div class="barf' . $a . '" style="height: ' . (100 - $percent) . '%"></div>'
+		. '<div class="barv' . $a . '" style="background: rgb(' . "$r,$g,$b" . '); height: ' . $percent . '%"></div>'
+		. '</div>';
+}
+
+function hits_to_graph($hits)
+{
+	$max = 0;
+	foreach ($hits as $v) {
+		if ($max < $v) {
+			$max = $v;
+		}
+	}
+	if (!$max) {
+		return '';
+	}
+	$t = (time() / (60 * 60)) % 24;
+	$html = array();
+	foreach ($hits as $i => $v) {
+		$html[] = bar_hits_percent($v, $v / $max, $i == $t);
+	}
+	return implode('', $html);
+}
+
+function switcher($name, $options)
+{
+	$n = isset($_GET[$name]) ? $_GET[$name] : null;
+	$html = array();
+	foreach ($options as $k => $v) {
+		$html[] = sprintf('<a href="?%s=%s"%s>%s</a>', $name, $k, $k == $n ? ' class="active"' : '', $v);
+	}
+	return implode('', $html);
+}
+
+function th($name, $attrs = null)
+{
+	$translated = __($name);
+	if ($translated == $name) {
+		$translated = "$name|$name";
+	}
+	list($text, $title) = explode('|', $translated, 2);
+	return sprintf('%s<th%s id="%s" title="%s"><a href="javascript:" onclick="resort(this); return false">%s</a></th>%s'
+			, "\t"
+			, $attrs ? " $attrs" : ""
+			, $name, htmlspecialchars(trim($title)), trim($text)
+			, "\n");
+}
+
+$php_version = phpversion();
+$xcache_version = XCACHE_VERSION;
+$xcache_modules = XCACHE_MODULES;
+
+if (!extension_loaded('XCache')) {
+	include("header.tpl.php");
+	echo '<h1>XCache is not loaded</h1>';
+	ob_start();
+	phpinfo(INFO_GENERAL);
+	$info = ob_get_clean();
+	if (preg_match_all("!<tr>[^<]*<td[^>]*>[^<]*(?:Configuration|ini|Server API)[^<]*</td>[^<]*<td[^>]*>[^<]*</td>[^<]*</tr>!s", $info, $m)) {
+		echo '<div class="phpinfo">';
+		echo 'PHP Info';
+		echo '<table>';
+		echo implode('', $m[0]);
+		echo '</table>';
+		echo '</div>';
+	}
+	if (preg_match('!<td class="v">(.*?\\.ini)!', $info, $m)) {
+		echo "Please check $m[1]";
+	}
+	else if (preg_match('!Configuration File \\(php.ini\\) Path *</td><td class="v">([^<]+)!', $info, $m)) {
+		echo "Please put a php.ini in $m[1] and load XCache extension";
+	}
+	else {
+		echo "You don't even have a php.ini yet?";
+	}
+	echo "(See above)";
+	include("footer.tpl.php");
+	exit;
+}
+$pcnt = xcache_count(XC_TYPE_PHP);
+$vcnt = xcache_count(XC_TYPE_VAR);
+
+if ($_SERVER['REQUEST_METHOD'] == 'POST') {
+	$remove = @ $_POST['remove'];
+	if ($remove && is_array($remove)) {
+		foreach ($remove as $name) {
+			xcache_unset($name);
+		}
+	}
+}
+
+$moduleinfo = null;
+$type_none = -1;
+if (!isset($_GET['type'])) {
+	$_GET['type'] = $type_none;
+}
+$_GET['type'] = $type = (int) $_GET['type'];
+
+// {{{ process clear
+function processClear()
+{
+	$type = isset($_POST['type']) ? $_POST['type'] : null;
+	if ($type != XC_TYPE_PHP && $type != XC_TYPE_VAR) {
+		$type = null;
+	}
+	if (isset($type)) {
+		$cacheid = (int) (isset($_POST['cacheid']) ? $_POST['cacheid'] : 0);
+		if (isset($_POST['clearcache'])) {
+			$count = xcache_count($type);
+			if ($cacheid >= 0) {
+				for ($cacheid = 0; $cacheid < $count; $cacheid ++) {
+					xcache_clear_cache($type, $cacheid);
+				}
+			}
+			else {
+				xcache_clear_cache($type);
+			}
+		}
+	}
+}
+processClear();
+// }}}
+// {{{ load info/list
+$cacheinfos = array();
+$total = array();
+for ($i = 0; $i < $pcnt; $i ++) {
+	$data = xcache_info(XC_TYPE_PHP, $i);
+	if ($type === XC_TYPE_PHP) {
+		$data += xcache_list(XC_TYPE_PHP, $i);
+	}
+	$data['type'] = XC_TYPE_PHP;
+	$data['cache_name'] = "php#$i";
+	$data['cacheid'] = $i;
+	$cacheinfos[] = $data;
+	if ($pcnt >= 2) {
+		calc_total($total, $data);
+	}
+}
+
+if ($pcnt >= 2) {
+	$total['type'] = XC_TYPE_PHP;
+	$total['cache_name'] = _('Total');
+	$total['cacheid'] = -1;
+	$total['gc'] = null;
+	$total['istotal'] = true;
+	$cacheinfos[] = $total;
+}
+
+$total = array();
+for ($i = 0; $i < $vcnt; $i ++) {
+	$data = xcache_info(XC_TYPE_VAR, $i);
+	if ($type === XC_TYPE_VAR) {
+		$data += xcache_list(XC_TYPE_VAR, $i);
+	}
+	$data['type'] = XC_TYPE_VAR;
+	$data['cache_name'] = "var#$i";
+	$data['cacheid'] = $i;
+	$cacheinfos[] = $data;
+	if ($vcnt >= 2) {
+		calc_total($total, $data);
+	}
+}
+
+if ($vcnt >= 2) {
+	$total['type'] = XC_TYPE_VAR;
+	$total['cache_name'] = _('Total');
+	$total['cacheid'] = -1;
+	$total['gc'] = null;
+	$total['istotal'] = true;
+	$cacheinfos[] = $total;
+}
+// }}}
+// {{{ merge the list
+switch ($type) {
+case XC_TYPE_PHP:
+case XC_TYPE_VAR:
+	$cachelist = array('type' => $type, 'cache_list' => array(), 'deleted_list' => array());
+	if ($type == XC_TYPE_VAR) {
+		$cachelist['type_name'] = 'var';
+	}
+	else {
+		$cachelist['type_name'] = 'php';
+	}
+	foreach ($cacheinfos as $i => $c) {
+		if (!empty($c['istotal'])) {
+			continue;
+		}
+		if ($c['type'] == $type && isset($c['cache_list'])) {
+			foreach ($c['cache_list'] as $e) {
+				$e['cache_name'] = $c['cache_name'];
+				$cachelist['cache_list'][] = $e;
+			}
+			foreach ($c['deleted_list'] as $e) {
+				$e['cache_name'] = $c['cache_name'];
+				$cachelist['deleted_list'][] = $e;
+			}
+		}
+	}
+	if ($type == XC_TYPE_PHP) {
+		$inodes = array();
+		$haveinode = false;
+		foreach ($cachelist['cache_list'] as $e) {
+			if (isset($e['file_inode'])) {
+				$haveinode = true;
+				break;
+			}
+		}
+		if (!$haveinode) {
+			foreach ($cachelist['deleted_list'] as $e) {
+				if (isset($e['file_inode'])) {
+					$haveinode = true;
+					break;
+				}
+			}
+		}
+	}
+	unset($data);
+	break;
+
+default:
+	$_GET['type'] = $type_none;
+	$cachelist = array();
+	ob_start();
+	phpinfo(INFO_MODULES);
+	$moduleinfo = ob_get_clean();
+	if (preg_match_all('!(XCache[^<>]*)</a></h2>(.*?)<h2>!is', $moduleinfo, $m)) {
+		$moduleinfo = array();
+		foreach ($m[1] as $i => $dummy) {
+			$moduleinfo[] = '<h3>' . trim($m[1][$i]) . '</h3>';
+			$moduleinfo[] = str_replace('<br />', '', trim($m[2][$i]));
+		}
+		$moduleinfo = implode('', $moduleinfo);
+	}
+	else {
+		$moduleinfo = null;
+	}
+	break;
+}
+// }}}
+
+$type_php = XC_TYPE_PHP;
+$type_var = XC_TYPE_VAR;
+$types = array($type_none => _('Statistics'), $type_php => _('List PHP'), $type_var => _('List Var Data'));
+
+include("xcache.tpl.php");
+
+?>
Index: /trunk/htdocs/cacher/xcache.css
===================================================================
--- /trunk/htdocs/cacher/xcache.css	(revision 1041)
+++ /trunk/htdocs/cacher/xcache.css	(revision 1041)
@@ -0,0 +1,55 @@
+input, table { font-family: sans-serif; }
+input { font-size: 12px; }
+table { border-collapse: collapse; font-size: 11px; margin: 0; }
+table caption, h2, h3 { font-size: 16px; font-weight: bold; text-align: left; padding-top: 20px; margin-bottom: 2px; }
+h3 { font-size: 14px; padding-top: 2px; margin-bottom: 0px; }
+td, th { white-space: pre; }
+table.cycles { border: 1px solid black; margin-top: 5px; margin-bottom: 5px; }
+table.cycles .col1 { background-color: #f5f5f5; }
+table.cycles .col2 { background-color: #e0e0e0; }
+table.cycles th, table.cycles td { border: 1px solid black; font-family: monospace; }
+table.cycles th { background-color: #9999cc; color: black; font-weight: bold; height: 20px; line-height: 20px; font-family: serif; }
+th a { color: black; font-weight: bold; display: block; width: 100%; height: 100%; }
+th { font-size: 12px; }
+.phpinfo table { border: 1px solid black; margin-bottom: 1px; }
+.phpinfo table th, .phpinfo table td { border: 1px solid black; }
+.phpinfo table th { font-weight: bold; }
+.phpinfo .e {background-color: #ccccff; font-weight: bold; color: #000000;}
+.phpinfo .h {background-color: #9999cc; font-weight: bold; color: #000000;}
+.phpinfo .v {background-color: #cccccc; color: #000000;}
+.button { }
+span.sortarrow { color: white; text-decoration: none; }
+form {margin: 0; padding: 0}
+
+.percent { height: 3px; margin-bottom: 1px; border: 1px solid gray; }
+.percent div { float: left; height: 100%; }
+.pvalue { background: limegreen; }
+
+.blocksgraph { height: 16px; }
+.blocksgraph div { float: left; height: 3px; width: 4px; border: solid gray; border-width: 0 0px 1px 0; }
+.blocksgraph { border: 1px solid gray; border-bottom: 0px; }
+
+.hitsgraph { height: 20px; margin: auto; }
+.hitsgraph div { float: left; width: 2px; height: 100%; }
+.hitsgraph div:hover { background: gray; }
+.hitsgraph div div { float: none; width: 100%; }
+.hitsgraph div div.barf { border: 0px solid gray; border-width: 1px 0 0 0; }
+.hitsgraph div div.barv { border: 0px solid gray; border-width: 0 0 1px 0; }
+.hitsgraph div div.barf.active { border-color: yellow; }
+.hitsgraph div div.barv.active { border-color: yellow; }
+
+.switcher, h1 { text-align: center; display: block; }
+.switcher a { color: blue; padding: 1px 8px 1px 8px; border: 1px solid white; border-bottom-color: black; text-decoration: none; }
+.switcher a:hover.active
+, .switcher a:active.active
+, .switcher a.active { color: black; background: #99C; border-color: #99C; border-left-color: black; border-right-color: black; }
+.switcher a:hover { background: #f0f0f0; }
+.switcher a:active { background: #e0e0e0; }
+#help { display: block; float: right; }
+.footnote { text-align: right; font-size: 12px; }
+dl { overflow: hidden; }
+dt { font-weight: bold; clear: both; float: left; width: 100px; text-align: right; margin: 0; }
+dd { margin: 0; }
+.blockarea { overflow: hidden; _width: 1px; }
+div.legend { float: left; border: 1px solid gray; font: 12px/12px monospace; }
+div.legendtitle { float: left; padding: 2px; padding-right: 10px; font: 12px/12px monospace; }
Index: /trunk/htdocs/cacher/footer.tpl.php
===================================================================
--- /trunk/htdocs/cacher/footer.tpl.php	(revision 934)
+++ /trunk/htdocs/cacher/footer.tpl.php	(revision 934)
@@ -0,0 +1,9 @@
+<div class="footnote">
+<?php echo <<<EOS
+PHP {$php_version} Powered By: XCache {$xcache_version}, {$xcache_modules}
+EOS;
+?>
+</div>
+
+</body>
+</html>
Index: /trunk/htdocs/cacher/config.example.php
===================================================================
--- /trunk/htdocs/cacher/config.example.php	(revision 920)
+++ /trunk/htdocs/cacher/config.example.php	(revision 920)
@@ -0,0 +1,74 @@
+<?php
+
+// this is example config
+// DO NOT rename/delete/modify this file
+// if you want to customize config, copy this file and name it as config.php
+// upgrading your config.php when config.example.php were upgraded
+
+// leave this setting unset to auto detect using browser request header
+// $config['lang'] = 'en-us';
+
+$config['charset'] = "UTF-8";
+
+// enable this for translators only
+$config['show_todo_strings'] = false;
+
+// width of graph for free or usage blocks
+$config['percent_graph_width'] = 120;
+$config['percent_graph_type'] = 'used'; // either 'used' or 'free'
+
+// only enable if you have password protection for admin page
+// enabling this option will cause user to eval() whatever code they want
+$config['enable_eval'] = false;
+
+// this ob filter is applied for the cache list, not the whole page
+function custom_ob_filter_path_nicer($list_html)
+{
+	$list_html = ob_filter_path_nicer_default($list_html); // this function is from common.php
+	return $list_html;
+}
+$config['path_nicer'] = 'custom_ob_filter_path_nicer';
+
+// XCache builtin http auth is enforce for security reason
+// if http auth is disabled, any vhost user that can upload *.php, will see all variable data cached in XCache
+
+// but if you have your own login/permission system, you can use the following example
+// {{{ login example
+// this is an example only, it's won't work for you without your implemention.
+/*
+function check_admin_and_by_pass_xcache_http_auth()
+{
+	require("/path/to/user-login-and-permission-lib.php");
+	session_start();
+
+	if (user_logined()) {
+		user_load_permissions();
+		if (user_is_admin()) {
+			// user is trusted after permission checks above.
+			// tell XCache about it (the only secure way to by pass XCache http auth)
+			$_SERVER["PHP_AUTH_USER"] = "moo";
+			$_SERVER["PHP_AUTH_PW"] = "your-xcache-password-before-md5";
+		}
+		else {
+			die("Permission denied");
+		}
+	}
+	else {
+		if (!ask_the_user_to_login()) {
+			exit;
+		}
+	}
+
+	return true;
+}
+
+check_admin_and_by_pass_xcache_http_auth();
+*/
+// }}}
+
+/* by pass XCache http auth
+$_SERVER["PHP_AUTH_USER"] = "moo";
+$_SERVER["PHP_AUTH_PW"] = "your-xcache-password-before-md5";
+*/
+
+?>
Index: /trunk/htdocs/cacher/common-zh-traditional-utf-8.lang.php
===================================================================
--- /trunk/htdocs/cacher/common-zh-traditional-utf-8.lang.php	(revision 1042)
+++ /trunk/htdocs/cacher/common-zh-traditional-utf-8.lang.php	(revision 1042)
@@ -0,0 +1,131 @@
+<?php
+
+$strings = array(
+		'XCache Help'
+		=> 'XCache 說明訊息',
+		'Help'
+		=> '說明',
+		'Clear'
+		=> '清除',
+		'Sure to clear?'
+		=> '確認要清除嗎?',
+		'% Free'
+		=> '% 剩余',
+		'% Used'
+		=> '% 已用',
+		'Hits'
+		=> '命中',
+		'Modify'
+		=> '修改',
+		'See also'
+		=> '建議參考',
+		'Legends:'
+		=> '图例:',
+		'Used Blocks'
+		=> '已用块',
+		'Free Blocks'
+		=> '未用块',
+		'Total'
+		=> '总共',
+		'Caches'
+		=> '快取',
+		'php Cached'
+		=> '快取的 php 指令',
+		'php Deleted'
+		=> '待刪 php 指令',
+		'var Cached'
+		=> '快取的變數',
+		'var Deleted'
+		=> '待刪變數',
+		'Statistics'
+		=> '統計訊息',
+		'List PHP'
+		=> '列出PHP',
+		'List Var Data'
+		=> '列變數資料',
+		'XCache %s Administration'
+		=> 'XCache %s 管理頁面',
+		'Module Info'
+		=> '組元訊息',
+		'Remove Selected'
+		=> '移除所选',
+		'Editing Variable %s'
+		=> '正在编辑变量 %s',
+		'Set %s in config to enable'
+		=> '請在配置文件中設置 %s 啟用本功能',
+		'cache.cache'
+		=> '快取|',
+		'cache.slots'
+		=> '槽|Hash 槽個數，對應 php.ini 裡的設置',
+		'cache.size'
+		=> '大小|共享記憶體區大小，單位：位元',
+		'cache.avail'
+		=> '剩餘|可用記憶體，對應共享記憶體區的剩餘記憶體位元數',
+		'cache.used'
+		=> '已用|已用内存, 对应共享内存区的已用内存字节数',
+		'cache.blocksgraph'
+		=> '百分比图|條狀顯示可用記憶體的比例',
+		'cache.operations'
+		=> '操作按鈕|點擊按鈕清除對應共享記憶體區的資料',
+		'cache.compiling'
+		=> '編譯中|編譯標記，當共享記憶體區正在編譯 php 指令時標記為 "yes"',
+		'cache.hits'
+		=> '命中|共享記憶體命中次數，命中=從該共享記憶體載入php或者變數',
+		'cache.hits_avg_h'
+		=> '命中/H|每小时命中次数. 只统计最后 24 小时',
+		'cache.hits_graph'
+		=> '24H 分布|24 小时命中分布图. 图表现是最后 24 小时的命中次数',
+		'cache.hits_avg_s'
+		=> '命中/S|每秒命中次数. 只统计最后 5 秒',
+		'cache.updates'
+		=> '更新|共享記憶更新過次數',
+		'cache.clogs'
+		=> '阻塞|編譯阻塞跳過，阻塞=當需該共享記憶體區負責編譯時，其他程序/現成無法存取此共享記憶體. 跳過=XCache 自動判斷阻塞的共享記憶體區自動跳過阻塞等待，直接使用非共享記憶體方式繼續處理請求',
+		'cache.ooms'
+		=> '記憶體不足|記憶體不足次數，顯示需要儲存新資料但是共享記憶體區記憶體不足的次數. 如果出現太頻繁請考慮加大配置中的 xcache.size 或者 xcache.var_size',
+		'cache.errors'
+		=> '错误|编译错误, 显示您的脚本被编译时出错的次数. 如果您发现这个数字不断增长, 您应该检查什么脚本产生错误. 参考 說明',
+		'cache.readonly_protected'
+		=> 'Protected|顯示該 Cache 是否支援並啟用 readonly_protection. 参考 說明',
+		'cache.cached'
+		=> '快取|共享記憶體於該共享記憶體區的項目個數',
+		'cache.deleted'
+		=> '待刪|共享記憶體區內將要刪除的項目 (已經刪除但是還被某些程序佔用)',
+		'cache.gc_timer'
+		=> 'GC|垃圾回收的倒數計時',
+		'entry.id'
+		=> 'Id|',
+		'entry.name'
+		=> '項目名稱/檔案名稱|項目名稱或者檔案名稱',
+		'entry.hits'
+		=> '命中|該項目被命中的次數 (從共享記憶體區載入)',
+		'entry.size'
+		=> '大小|項目在共享記憶體裡佔用位元數',
+		'entry.refcount'
+		=> '引用數|項目依然被其他程序佔用的引用次數',
+		'entry.phprefcount'
+		=> '共享|與本項目相同 PHP 內容的个數',
+		'entry.file_size'
+		=> '源大小|原始檔案大小',
+		'entry.file_mtime'
+		=> '修改|原始檔案最後修改時間',
+		'entry.file_device'
+		=> 'dev|原始檔案所在設備ID',
+		'entry.file_inode'
+		=> 'ino|原始檔案的inode',
+		'entry.class_cnt'
+		=> '类|类个数',
+		'entry.function_cnt'
+		=> '函数|函数个数',
+		'entry.hash'
+		=> 'Hash|Hash',
+		'entry.atime'
+		=> '存取|最後存取該項目的時間',
+		'entry.ctime'
+		=> '建立|該項目被建立於共享內的時間',
+		'entry.delete'
+		=> '移除|該項目被移除於共享內的時間',
+		'entry.remove'
+		=> '移除|',
+);
+
Index: /trunk/htdocs/cacher/help-zh-traditional-utf-8.lang.php
===================================================================
--- /trunk/htdocs/cacher/help-zh-traditional-utf-8.lang.php	(revision 1038)
+++ /trunk/htdocs/cacher/help-zh-traditional-utf-8.lang.php	(revision 1038)
@@ -0,0 +1,4 @@
+<dl>
+<dt>编译错误: </dt> <dd><a href="http://www.php.net/manual/en/ref.errorfunc.php#ini.error-log">ini.error-log</a> and <a href="http://cn2.php.net/manual/en/ref.errorfunc.php#ini.display-errors">ini.display-errors</a></dd>
+<dt>只读保护: </dt> <dd><a href="http://xcache.lighttpd.net/wiki/ReadonlyProtection">ReadonlyProtection</a></dd>
+</dl>
Index: /trunk/htdocs/coverager/index.php
===================================================================
--- /trunk/htdocs/coverager/index.php	(revision 393)
+++ /trunk/htdocs/coverager/index.php	(revision 393)
@@ -0,0 +1,3 @@
+<?php
+
+include("coverager.php");
Index: /trunk/htdocs/coverager/common-en.lang.php
===================================================================
--- /trunk/htdocs/coverager/common-en.lang.php	(revision 393)
+++ /trunk/htdocs/coverager/common-en.lang.php	(revision 393)
@@ -0,0 +1,5 @@
+<?php
+
+$GLOBALS['show_todo_strings'] = false;
+
+?>
Index: /trunk/htdocs/coverager/common-zh-simplified-gb2312.lang.php
===================================================================
--- /trunk/htdocs/coverager/common-zh-simplified-gb2312.lang.php	(revision 393)
+++ /trunk/htdocs/coverager/common-zh-simplified-gb2312.lang.php	(revision 393)
@@ -0,0 +1,25 @@
+<?php
+
+$strings = array(
+		'root'
+		=> '��ʼ',
+		'Directory'
+		=> 'Ŀ¼',
+		'File'
+		=> '�ļ�',
+		'Percent'
+		=> '������',
+		'Hits'
+		=> '����',
+		'Lines'
+		=> '����',
+		'TODO'
+		=> '�����ļ�',
+		'XCache PHP Code Coverage Viewer'
+		=> 'XCache PHP ���븲�ǲ鿴��',
+		'module'
+		=> 'ģ��',
+		''
+		=> '',
+		);
+
Index: /trunk/htdocs/coverager/common.php
===================================================================
--- /trunk/htdocs/coverager/common.php	(revision 418)
+++ /trunk/htdocs/coverager/common.php	(revision 418)
@@ -0,0 +1,112 @@
+<?php
+
+function get_language_file_ex($name, $l, $s)
+{
+	static $lmap = array(
+			'zh'    => 'zh-simplified',
+			'zh-hk' => 'zh-traditional',
+			'zh-tw' => 'zh-traditional',
+			);
+	static $smap = array(
+			'gbk'     => 'gb2312',
+			'gb18030' => 'gb2312',
+			);
+
+	if (isset($lmap[$l])) {
+		$l = $lmap[$l];
+	}
+	if (file_exists($file = "$name-$l-$s.lang.php")) {
+		return $file;
+	}
+	if (isset($smap[$s])) {
+		$s = $smap[$s];
+		if (file_exists($file = "$name-$l-$s.lang.php")) {
+			return $file;
+		}
+	}
+	if (file_exists($file = "$name-$l.lang.php")) {
+		return $file;
+	}
+	return null;
+}
+
+function get_language_file($name)
+{
+	global $charset, $lang;
+	$s = strtolower($charset);
+	if (isset($lang)) {
+		$l = strtolower($lang);
+		$file = get_language_file_ex($name, $l, $s);
+		if (!isset($file)) {
+			$l = strtok($l, '-');
+			$file = get_language_file_ex($name, $l, $s);
+		}
+	}
+	else if (!empty($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
+		foreach (explode(',', str_replace(' ', '', $_SERVER['HTTP_ACCEPT_LANGUAGE'])) as $l) {
+			$l = strtok($l, ';');
+			$file = get_language_file_ex($name, $l, $s);
+			if (isset($file)) {
+				$lang = $l;
+				break;
+			}
+			if (strpos($l, '-') !== false) {
+				$ll = strtok($l, '-');
+				$file = get_language_file_ex($name, $ll, $s);
+				if (isset($file)) {
+					$lang = $l;
+					break;
+				}
+			}
+		}
+	}
+	return isset($file) ? $file : "$name-en.lang.php";
+}
+
+function _T($str)
+{
+	if (isset($GLOBALS['strings'][$str])) {
+		return $GLOBALS['strings'][$str];
+	}
+	if (!empty($GLOBALS['show_todo_strings'])) {
+		return '<span style="color:red">' . htmlspecialchars($str) . '</span>';
+	}
+	return $str;
+}
+
+function stripaddslashes_array($value, $mqs = false)
+{
+	if (is_array($value)) {
+		foreach($value as $k => $v) {
+			$value[$k] = stripaddslashes_array($v, $mqs);
+		}
+	}
+	else if(is_string($value)) {
+		$value = $mqs ? str_replace('\'\'', '\'', $value) : stripslashes($value);
+	}
+	return $value;
+}
+
+error_reporting(E_ALL);
+ini_set('display_errors', 'On');
+define('REQUEST_TIME', time());
+
+if (get_magic_quotes_gpc()) {
+	$mqs = (bool) ini_get('magic_quotes_sybase');
+	$_GET = stripaddslashes_array($_GET, $mqs);
+	$_POST = stripaddslashes_array($_POST, $mqs);
+	$_REQUEST = stripaddslashes_array($_REQUEST, $mqs);
+}
+ini_set('magic_quotes_runtime', '0');
+
+$charset = "UTF-8";
+if (file_exists("./config.php")) {
+	include("./config.php");
+}
+
+include(get_language_file("common"));
+if (!isset($lang)) {
+	$lang = 'en-us';
+}
+
+?>
Index: /trunk/htdocs/coverager/common-zh-simplified-utf-8.lang.php
===================================================================
--- /trunk/htdocs/coverager/common-zh-simplified-utf-8.lang.php	(revision 393)
+++ /trunk/htdocs/coverager/common-zh-simplified-utf-8.lang.php	(revision 393)
@@ -0,0 +1,25 @@
+<?php
+
+$strings = array(
+		'root'
+		=> '开始',
+		'Directory'
+		=> '目录',
+		'File'
+		=> '文件',
+		'Percent'
+		=> '覆盖率',
+		'Hits'
+		=> '命中',
+		'Lines'
+		=> '行数',
+		'TODO'
+		=> '闲置文件',
+		'XCache PHP Code Coverage Viewer'
+		=> 'XCache PHP 代码覆盖查看器',
+		'module'
+		=> '模块',
+		''
+		=> '',
+		);
+
Index: /trunk/htdocs/coverager/coverager.tpl.php
===================================================================
--- /trunk/htdocs/coverager/coverager.tpl.php	(revision 530)
+++ /trunk/htdocs/coverager/coverager.tpl.php	(revision 530)
@@ -0,0 +1,253 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<?php
+echo <<<HEAD
+	<meta http-equiv="Content-Type" content="text/html; charset=$this->charset" />
+	<meta http-equiv="Content-Language" content="{$this->lang}" />
+	<script type="text/javascript" src="tablesort.js" charset="$this->charset"></script>
+HEAD;
+?>
+
+	<link rel="stylesheet" type="text/css" href="coverager.css" />
+	<title><?php echo _T("XCache PHP Code Coverage Viewer"); ?></title>
+</head>
+<body>
+<h1><?php echo _T("XCache PHP Code Coverage Viewer"); ?></h1>
+
+<?php
+function calc_percent($info, &$percent, &$class)
+{
+	if (!$info['total']) {
+		$percent = 0;
+	}
+	else {
+		$percent = (int) ($info['hits'] / $info['total'] * 100);
+	}
+	if ($percent < 15) {
+		$class = "Lo";
+	}
+	else if ($percent < 50) {
+		$class = "Med";
+	}
+	else {
+		$class = "Hi";
+	}
+}
+
+function bar($percent, $class)
+{
+	return <<<EOS
+	<div class="coverBarOutline">
+		<div class="coverBar{$class}" style="width:{$percent}%"></div>
+		<div class="coverPer{$class}">{$percent}</div>
+	</div>
+EOS;
+}
+
+function dir_head()
+{
+	global $cycle;
+	$cycle = new Cycle('class="col1"', 'class="col2"');
+	$l_dir = _T("Directory");
+	$l_per = _T("Percent");
+	$l_hit = _T("Hits");
+	$l_lns = _T("Lines");
+	$l_tds = _T("TODO");
+	return <<<EOS
+<div class="table-center">
+	<table cellpadding="2" cellspacing="0" border="0" class="cycles center">
+	<tr>
+		<th>{$l_dir}</th><th>{$l_per}</th><th>{$l_hit}</th><th>{$l_lns}</th><th>{$l_tds}</th>
+	</tr>
+EOS;
+}
+
+function dir_row($info, $srcdir)
+{
+	global $cycle;
+	if ($info['files'] || $info['todos']) {
+		$srcdir .= DIRECTORY_SEPARATOR;
+		$c = $cycle->next();
+		$srcdir_html = htmlspecialchars($srcdir);
+		$todos = number_format($info['todos']);
+		if ($info['total']) {
+			$srcdir_url = urlencode($srcdir);
+			$hits  = number_format($info['hits']);
+			$total = number_format($info['total']);
+			calc_percent($info, $percent, $class);
+			$bar = bar($percent, $class);
+			return <<<EOS
+			<tr $c>
+				<td class="coverFile"><a href="?path={$srcdir_url}">{$srcdir_html}</a></td>
+				<td class="coverBar">$bar</td>
+				<td class="coverNum{$class}">{$hits}</td>
+				<td class="coverNum{$class}">{$total}</td>
+				<td class="coverNum{$class}">{$todos}</td>
+			</tr>
+EOS;
+		}
+		else {
+			return <<<EOS
+			<tr $c>
+				<td class="coverFile">{$srcdir_html}</td>
+				<td class="coverBar"></td>
+				<td class="coverNumLo"></td>
+				<td class="coverNumLo"></td>
+				<td class="coverNumLo">{$todos}</td>
+			</tr>
+EOS;
+		}
+	}
+}
+
+function dir_foot()
+{
+	return <<<EOS
+	</table>
+</div>
+EOS;
+}
+
+function file_head()
+{
+	global $cycle;
+	$cycle = new Cycle('class="col1"', 'class="col2"');
+	$l_fil = _T("File");
+	$l_per = _T("Percent");
+	$l_hit = _T("Hits");
+	$l_lns = _T("Lines");
+	return <<<EOS
+<div class="center-table">
+	<table cellpadding="2" cellspacing="0" border="0" class="cycles center">
+	<tr>
+		<th>{$l_fil}</th><th>{$l_per}</th><th>{$l_hit}</th><th>{$l_lns}</th>
+	</tr>
+EOS;
+}
+
+function file_row($info, $srcfile)
+{
+	global $cycle;
+
+	$c = $cycle->next();
+	$srcfile_html = htmlspecialchars($srcfile);
+	$total = number_format($info['total']);
+	if ($info['total']) {
+		$hits = number_format($info['hits']);
+		$srcfile_url = urlencode($srcfile);
+		calc_percent($info, $percent, $class);
+		$bar = bar($percent, $class);
+		return <<<EOS
+			<tr $c>
+					<td class="coverFile"><a href="?path={$srcfile_url}">{$srcfile_html}</a></td>
+					<td class="coverBar">$bar</td>
+					<td class="coverNum{$class}">{$hits}</td>
+					<td class="coverNum{$class}">{$total}</td>
+			</tr>
+EOS;
+	}
+	else {
+		return <<<EOS
+			<tr $c>
+					<td class="coverFile">{$srcfile_html}</a></td>
+					<td class="coverBar"></td>
+					<td class="coverNumLo"></td>
+					<td class="coverNumLo">{$total}</td>
+			</tr>
+EOS;
+	}
+}
+
+function file_foot()
+{
+	return <<<EOS
+    </table>
+</div>
+EOS;
+}
+
+$l_root = _T("root");
+if ($action == 'dir') {
+	if (function_exists('ob_filter_path_nicer')) {
+		ob_start('ob_filter_path_nicer');
+	}
+	$path_html = htmlspecialchars($path);
+	echo <<<EOS
+	<div>
+		<a href="?">$l_root</a> $path<br />
+	</div>
+EOS;
+	echo dir_head($dirinfo);
+	echo dir_row($dirinfo, $path);
+	echo dir_foot($dirinfo);
+	if ($dirinfo['subdirs']) {
+		echo dir_head();
+		foreach ($dirinfo['subdirs'] as $srcdir => $info) {
+			echo dir_row($info, $srcdir);
+		}
+		echo dir_foot();
+	}
+	if ($dirinfo['files']) {
+		echo file_head();
+		foreach ($dirinfo['files'] as $srcfile => $info) {
+			echo file_row($info, $srcfile);
+		}
+		echo file_foot();
+	}
+}
+else if ($action == 'file') {
+	if (function_exists('ob_filter_path_nicer')) {
+		ob_start('ob_filter_path_nicer');
+	}
+	$dir_url = urlencode($dir);
+	$dir_html = htmlspecialchars($dir);
+	echo <<<EOS
+	<div>
+		<a href="?">$l_root</a> <a href="?path={$dir_url}">{$dir_html}</a>/<strong>{$filename}</strong><br />
+	</div>
+EOS;
+
+	echo file_head();
+	echo file_row($fileinfo, $path);
+	echo file_foot();
+
+	if ($tplfile) {
+		$tplfile_html = htmlspecialchars($tplfile);
+		echo <<<EOS
+		<div>
+			<a href="#tpl">{$tplfile_html}</a><br />
+		</div>
+EOS;
+	}
+	if (function_exists('ob_filter_path_nicer')) {
+		ob_end_flush();
+	}
+	echo <<<EOS
+	<div class="code">
+		<ol>{$filecov}</ol>
+	</div>
+EOS;
+	if ($tplfile) {
+		echo <<<EOS
+	<a name="tpl">{$tplfile}</a>
+	<div class="code">
+		<ol>{$tplcov}</ol>
+	</div>
+EOS;
+	}
+}
+else {
+	$error_html = htmlspecialchars($error);
+	echo <<<EOS
+	<span class="error">{$error_html}</span>
+EOS;
+}
+?>
+
+<div class="footnote">
+Powered By: XCache <?php echo $xcache_version; ?> coverager <?php echo _T("module"); ?>
+</div>
+
+</body>
+</html>
Index: /trunk/htdocs/coverager/coverager.php
===================================================================
--- /trunk/htdocs/coverager/coverager.php	(revision 531)
+++ /trunk/htdocs/coverager/coverager.php	(revision 531)
@@ -0,0 +1,360 @@
+<?php
+
+include("./common.php");
+
+class Cycle
+{
+	var $values;
+	var $i;
+	var $count;
+
+	function Cycle($v)
+	{
+		$this->values = func_get_args();
+		$this->i = -1;
+		$this->count = count($this->values);
+	}
+
+	function next()
+	{
+		$this->i = ($this->i + 1) % $this->count;
+		return $this->values[$this->i];
+	}
+
+	function cur()
+	{
+		return $this->values[$this->i];
+	}
+
+	function reset()
+	{
+		$this->i = -1;
+	}
+}
+
+class XcacheCoverageViewer
+{
+	var $syntaxhiglight = true;
+	var $usecache = false;
+	var $include_paths = array();
+	var $exclude_paths = array();
+	var $charset = 'UTF-8';
+	var $lang = 'en-us';
+	var $datadir = null;
+	var $datadir_len = null;
+	var $path = null;
+	var $outpath = null;
+
+	function XcacheCoverageViewer()
+	{
+		$this->datadir = ini_get('xcache.coveragedump_directory');
+
+		// copy config
+		foreach (array('charset', 'include_paths', 'exclude_paths', 'syntaxhiglight', 'usecache', 'datadir', 'lang') as $k) {
+			if (isset($GLOBALS[$k])) {
+				$this->{$k} = $GLOBALS[$k];
+			}
+		}
+
+		$this->datadir = preg_replace('!/$!', '', $this->datadir);
+		$this->datadir_len = strlen($this->datadir);
+
+		$this->path = isset($_GET['path']) ? $_GET['path'] : '';
+		$this->path = preg_replace('!\.{2,}!', '.', $this->path);
+		$qsep = preg_quote(DIRECTORY_SEPARATOR, '!');
+		$this->path = preg_replace("![\\\\$qsep]{2,}!", DIRECTORY_SEPARATOR, $this->path);
+		$this->path = preg_replace("!$qsep$!", '', $this->path);
+		if ($this->path == '/') {
+			$this->path = '';
+		}
+		$this->outpath = $this->datadir . $this->path;
+	}
+
+	function main()
+	{
+		$path = $this->path;
+
+		if (is_dir($this->outpath)) {
+			$action = 'dir';
+			$prefix_len = strlen($path) + 1;
+			$dirinfo = $this->loadDir($this->outpath);
+			if (!$this->usecache) {
+				ksort($dirinfo['subdirs']);
+				ksort($dirinfo['files']);
+			}
+		}
+		else if (is_file($this->outpath . ".pcov")) {
+			$action = 'file';
+
+			$dir = dirname($path);
+			$filename = basename($path);
+
+			$fileinfo = $this->loadCov($this->outpath . ".pcov");
+
+			$lines = file($path);
+			// fix the tabs not in the middle
+			foreach ($lines as $l => $line) {
+				if (preg_match('!^(\\t*)([^\\t]+\\t.*)$!s', $line, $m)) {
+					$lines[$l] = $m[1];
+					$chunks = explode("\t", $m[2]);
+					for ($i = 0, $c = count($chunks) - 1; $i < $c; $i ++) {
+						$lines[$l] .= $chunks[$i] . str_repeat(" ", 4 - (strlen($chunks[$i]) % 4));
+					}
+					$lines[$l] .= $chunks[$c];
+				}
+			}
+			if ($this->syntaxhiglight) {
+				$source = implode('', $lines);
+				ob_start();
+				highlight_string($source);
+				$lines = str_replace("\n", "", ob_get_clean());
+				$lines = str_replace('<code>', '', $lines);
+				$lines = str_replace('</code>', '', $lines);
+				$lines = preg_replace('(^<span[^>]*>|</span>$)', '', $lines);
+				$lines = explode('<br />', $lines);
+				$last = array_pop($lines);
+				$lines[count($lines) - 1] .= $last;
+				$filecov = sprint_cov($fileinfo['cov'], $lines, false);
+				unset($source);
+			}
+			else {
+				$filecov = sprint_cov($fileinfo['cov'], $lines);
+			}
+
+			list($tplfile, $tpllines, $tplcov) = $this->loadTplCov($fileinfo['cov'], substr($this->outpath, $this->datadir_len));
+			if ($tplfile) {
+				$tplcov = sprint_cov($tplcov, $tpllines);
+				unset($tpllines);
+			}
+		}
+		else if (!$this->datadir) {
+			$action = 'error';
+			$error  = 'require `ini:xcache.coveragedump_directory` or `config:$datadir` to be set';
+		}
+		else {
+			$action = 'error';
+			$error  = "no data";
+		}
+
+		$xcache_version = defined('XCACHE_VERSION') ? XCACHE_VERSION : '';
+		include("coverager.tpl.php");
+	}
+
+	function loadDir($outdir, $addtodo = null)
+	{
+		if ($this->usecache) {
+			$cachefile = $outdir . "/.pcovcache";
+			if (file_exists($cachefile)) {
+				return unserialize(file_get_contents($cachefile));
+			}
+		}
+		$srcdir = substr($outdir, $this->datadir_len);
+
+		$total = $hits = $todos = 0;
+		$files = array();
+		$subdirs = array();
+		if (!isset($addtodo)) {
+			if ($this->include_paths) {
+				foreach ($this->include_paths as $p) {
+					if (strncmp($p, $srcdir, strlen($p)) == 0) {
+						$addtodo = true;
+						break;
+					}
+				}
+			}
+		}
+		if ($addtodo) {
+			if ($this->exclude_paths) {
+				foreach ($this->exclude_paths as $p) {
+					if (strncmp($p, $srcdir, strlen($p)) == 0) {
+						$addtodo = false;
+						break;
+					}
+				}
+			}
+		}
+		foreach (glob($outdir . "/*") as $outfile) {
+			if (is_dir($outfile)) {
+				$info = $this->loadDir($outfile, $addtodo);
+				$srcfile = substr($outfile, $this->datadir_len);
+				$subdirs += $info['subdirs'];
+				$total   += $info['total'];
+				$hits    += $info['hits'];
+				$todos   += $info['todos'];
+				unset($info['subdirs']);
+				$subdirs[$srcfile] = $info;
+			}
+			else if (substr($outfile, -5) == ".pcov") {
+				// pass
+				$info = $this->loadFile($outfile);
+				$total += $info['total'];
+				$hits  += $info['hits'];
+				$srcfile = substr($outfile, $this->datadir_len, -5);
+				$files[$srcfile] = $info;
+			}
+			else {
+				continue;
+			}
+		}
+		if ($addtodo === true) {
+			foreach (glob($srcdir . "/*") as $srcfile) {
+				if (!isset($files[$srcfile]) && is_file($srcfile)) {
+					$files[$srcfile] = array('total' => 0, 'hits' => 0);
+					$todos ++;
+				}
+				else if (!isset($subdirs[$srcfile]) && is_dir($srcfile)) {
+					$subdirs[$srcfile] = array('total' => 0, 'hits' => 0, 'todos' => 1, 'files' => 0, 'subdirs' => array());
+					$todos ++;
+				}
+			}
+		}
+
+		if ($this->usecache) {
+			ksort($subdirs);
+			ksort($files);
+		}
+
+		$info = array(
+				'total'   => $total,
+				'hits'    => $hits,
+				'todos'   => $todos,
+				'files'   => $files,
+				'subdirs' => $subdirs,
+				);
+
+		if ($this->usecache) {
+			$fp = fopen($cachefile, "wb");
+			fwrite($fp, serialize($info));
+			fclose($fp);
+		}
+		return $info;
+	}
+
+	function loadFile($file)
+	{
+		if ($this->usecache) {
+			$cachefile = $file . "cache";
+			if (file_exists($cachefile)) {
+				return unserialize(file_get_contents($cachefile));
+			}
+		}
+
+		$info = $this->loadCov($file); //, $lines);
+		unset($info['cov']);
+
+		if ($this->usecache) {
+			$fp = fopen($cachefile, "wb");
+			fwrite($fp, serialize($info));
+			fclose($fp);
+		}
+		return $info;
+	}
+
+	function loadCov($file)//, $lines)
+	{
+		$total = $hits = 0;
+
+		$cov = xcache_coverager_decode(file_get_contents($file));
+
+		return array('total' => count($cov) - 1, 'hits' => $cov[0], 'cov' => $cov);
+	}
+
+	function loadTplCov($cov, $ctpl)
+	{
+		$tplinfofile = $ctpl . '.phpinfo';
+
+		if (!file_exists($tplinfofile)) {
+			return;
+		}
+
+		$tplinfo = unserialize(file_get_contents($tplinfofile));
+
+		if (!isset($tplinfo['sourceFile'])) {
+			return;
+		}
+		$tplfile = $tplinfo['sourceFile'];
+		if (!isset($tplinfo['lineMap']) || !count($tplinfo['lineMap'])) {
+			return;
+		}
+
+		$tpllines = file($tplfile);
+
+		$dline = 0;
+		$sline = 0;
+		$tplcov = array();
+		foreach ($cov as $line => $times) {
+			// find nearest line
+			while ($dline < $line) {
+				if ((list($dline, $sline) = each($tplinfo['lineMap'])) === false) {
+					break 2;
+				}
+			}
+
+			$tplcov[$sline] = $times;
+		}
+		return array($tplfile, $tpllines, $tplcov);
+	}
+}
+
+function sprint_cov($cov, $lines, $encode = true)
+{
+	$lastattr = null;
+	foreach ($lines as $l => $line) {
+		$offs = $l + 1;
+		if ($encode) {
+			$line = str_replace("\n", "", htmlspecialchars($line));
+		}
+		else if ($line !== "") {
+			if (substr($line, 0, 7) == '</span>') {
+				$lastattr = null;
+				$line = substr($line, 7);
+			}
+			else if (isset($lastattr)) {
+				$line = $lastattr . $line;
+			}
+
+			if (preg_match('!(<span[^>]+>|</span>)[^<>]*$!', $line, $m)) {
+				if ($m[1] == '</span>') {
+					$lastattr = null;
+				}
+				else {
+					$line .= '</span>';
+					$lastattr = $m[1];
+				}
+			}
+		}
+		if (isset($cov[$offs])) {
+			$lines[$l] = sprintf("<li class=\"line%sCov\"><pre class=\"code\"> %s\t%s\n</pre></li>"
+					, $cov[$offs] ? '' : 'No'
+					, $cov[$offs]
+					, $line);
+		}
+		else {
+			$lines[$l] = "<li><pre class=\"code\">\t$line\n</pre></li>";
+		}
+	}
+	return implode('', $lines);
+}
+
+if (!function_exists('xcache_coverager_decode')) {
+	function xcache_coverager_decode($bytes)
+	{
+		$bytes = unpack('l*', $bytes);
+		$i = 1;
+		if ($bytes[$i ++] != 0x564f4350) {
+			return null;
+		}
+		$end = count($bytes);
+		$cov = array();
+		for (/* empty*/; $i <= $end; $i += 2) {
+			$hits = $bytes[$i + 1];
+			$cov[$bytes[$i]] = $hits <= 0 ? 0 : $hits;
+		}
+		return $cov;
+	}
+}
+
+$app = new XcacheCoverageViewer();
+$app->main();
+
+?>
Index: /trunk/htdocs/coverager/coverager.css
===================================================================
--- /trunk/htdocs/coverager/coverager.css	(revision 605)
+++ /trunk/htdocs/coverager/coverager.css	(revision 605)
@@ -0,0 +1,71 @@
+h1 { text-align: center; display: block; }
+input, table { font-family: sans-serif; font-size: 11px; }
+th { font-size: 12px; }
+table { border-collapse: collapse; }
+table.center { margin-left: auto; margin-right: auto; }
+table\-center { text-align: center; }
+table.cycles { border: 1px solid black; margin-top: 5px; margin-bottom: 5px; }
+table.cycles .col1 { background-color: #f5f5f5; }
+table.cycles .col2 { background-color: #e0e0e0; }
+table.cycles th, table.cycles td { border: 1px solid black; font-family: monospace; }
+table.cycles th { background-color: #9999cc; color: black; font-weight: bold; height: 20px; line-height: 20px; font-family: serif; }
+th a { color: black; font-weight: bold; display: block; width: 100%; height: 100%; }
+
+.coverFile {
+	text-align:       left;
+	color:            gray;
+	padding-left:     10px;
+	padding-right:    10px;
+}
+
+.coverBar {
+	padding-left:     10px;
+	padding-right:    10px;
+}
+
+.coverBarOutline {
+	border: 1px solid black;
+	background-color: #ffffff;
+	width: 100px;
+	font-size: 11px; line-height: 11px;
+	font-family: tahoma;
+	position: relative;
+}
+
+.coverPerHi, .coverPerMed, .coverPerLo {
+	text-align:       center;
+	font-weight:      bold;
+	left: 0px; top: 2px;
+	position: absolute;
+	width: 100%;
+}
+.coverBarHi, .coverBarMed, .coverBarLo { left: 1px; top: 1px; height: 14px; }
+.coverBarHi  { background-color: #A7FC9D; }
+.coverBarMed { background-color: #FFEA20; }
+.coverBarLo  { background-color: #FF0000; }
+
+
+.coverNumHi, .coverNumMed, .coverNumLo {
+	text-align:       right;
+	padding-left:     10px;
+	padding-right:    10px;
+}
+.coverNumHi  { /*background-color: #A7FC9D;*/ }
+.coverNumMed { /*background-color: #FFEA20;*/ }
+.coverNumLo  { background-color: #FF0000; }
+
+.lineCov   { background-color: #F0F0F0; }
+.lineNoCov { background-color: #ffe0e0; }
+div.code {
+	border: 1px solid gray;
+	font-size: 12px;
+}
+pre.code {
+	font-family: monospace;
+	font-size: 12px;
+	white-space: pre;
+	padding: 0; margin: 0;
+}
+
+.footnote { text-align: right; font-size: 12px; }
+.error { color: red; }
Index: /trunk/htdocs/coverager/config.php.example
===================================================================
--- /trunk/htdocs/coverager/config.php.example	(revision 605)
+++ /trunk/htdocs/coverager/config.php.example	(revision 605)
@@ -0,0 +1,59 @@
+<?php
+
+// this is an example only
+// write your own config and name it as config.php
+
+// detected by browser
+// $lang = 'en-us';
+
+$charset = "UTF-8";
+
+// developers only
+$show_todo_strings = false;
+
+// $include_paths = array("/www/my-php-project/");
+// $exclude_paths = array("/www/my-php-project/tmp/");
+$syntaxhiglight = true;
+$usecache = false;
+//// $datadir is default to ini_get("xcache.coveragedump_directory")
+// $datadir = '';
+
+function ob_filter_path_nicer($o)
+{
+	$sep = DIRECTORY_SEPARATOR;
+	$o = str_replace($_SERVER['DOCUMENT_ROOT'],  "{DOCROOT}" . (substr($d, -1) == $sep ? $sep : ""), $o);
+	$xcachedir = realpath(dirname(__FILE__) . "$sep..$sep");
+	$o = str_replace($xcachedir . $sep, "{XCache}$sep", $o);
+	if ($sep == '/') {
+		$o = str_replace("/home/", "{H}/", $o);
+	}
+	return $o;
+}
+
+// implement your auth here if needed
+// {{{ home made login example
+// this is an example only, it's won't work for you without your implemention.
+function check_admin_auth()
+{
+	require("/path/to/user-login-and-permission-lib.php");
+	session_start();
+
+	if (!user_logined()) {
+		if (!ask_the_user_to_login()) {
+			exit;
+		}
+	}
+
+	user_load_permissions();
+	if (!user_is_admin()) {
+		die("Permission denied");
+	}
+
+	return true;
+}
+
+// uncomment:
+// check_admin_auth()
+// }}}
+
+?>
