Index: /trunk/README
===================================================================
--- /trunk/README	(revision 1002)
+++ /trunk/README	(revision 1003)
@@ -1,1 +1,5 @@
-Please check http://xcache.lighttpd.net/ and https://groups.google.com/forum/?fromgroups#!forum/xcache for help
+XCache is a fast, stable PHP opcode cacher that has been tested and is now running on production servers under high load.
+It is tested (on linux) and supported on all of the latest version of PHP popular release branches.
+ThreadSafe/Windows is also perfectly supported.
+
+Please check http://xcache.lighttpd.net/ and https://groups.google.com/group/xcache for help
Index: /trunk/config.m4
===================================================================
--- /trunk/config.m4	(revision 1002)
+++ /trunk/config.m4	(revision 1003)
@@ -5,5 +5,5 @@
   [  --enable-xcache-$2    XCache: $4], no, no)
   if test "$PHP_$3" != "no"; then
-    xcache_sources="$xcache_sources submodules/xc_$1.c"
+    xcache_sources="$xcache_sources mod_$1/xc_$1.c"
     XCACHE_MODULES="$XCACHE_MODULES $1"
     HAVE_$3=1
Index: /trunk/config.w32
===================================================================
--- /trunk/config.w32	(revision 1002)
+++ /trunk/config.w32	(revision 1003)
@@ -45,5 +45,5 @@
 		var withval = eval("PHP_XCACHE_" + uname);
 		if (withval != "no") {
-			xcache_sources += " submodules/xc_" + name + ".c";
+			xcache_sources += " mod_" + name + "/xc_" + name + ".c";
 			XCACHE_MODULES += " " + name;
 			STDOUT.WriteLine("Enabling XCache Module: " + name);
Index: /trunk/devel/run
===================================================================
--- /trunk/devel/run	(revision 1002)
+++ /trunk/devel/run	(revision 1003)
@@ -81,5 +81,11 @@
 	phpize --clean \
 	&& phpize \
-	&& CFLAGS="-Wall -Wno-unused -W -Wshadow -std=gnu99" ./configure --enable-xcache-disassembler --enable-xcache-test --enable-xcache-constant \
+	&& CFLAGS="-Wall -Wno-unused -W -Wshadow -std=gnu99" ./configure \
+		--enable-xcache-cacher \
+		--enable-xcache-optimizer \
+		--enable-xcache-encoder \
+		--enable-xcache-decoder \
+		--enable-xcache-disassembler \
+		--enable-xcache-test --enable-xcache-constant \
 	&& make clean all
 	exit
@@ -135,8 +141,8 @@
 	;;
 dc)
-	exec ./php-cli -c devel.ini ./bin/phpdc.phpr "${args[@]}" | tee decompiled.php
+	exec ./php-cli -c devel.ini ./mod_disassembler/bin/phpdc.phpr "${args[@]}" | tee decompiled.php
 	;;
 dop)
-	exec ./php-cli -c devel.ini ./bin/phpdop.phpr "${args[@]}"
+	exec ./php-cli -c devel.ini ./mod_disassembler/bin/phpdop.phpr "${args[@]}"
 	;;
 retest)
Index: /trunk/mod_cacher/htdocs/common-en.lang.php
===================================================================
--- /trunk/mod_cacher/htdocs/common-en.lang.php	(revision 1003)
+++ /trunk/mod_cacher/htdocs/common-en.lang.php	(revision 1003)
@@ -0,0 +1,5 @@
+<?php
+
+$GLOBALS['config']['show_todo_strings'] = false;
+
+?>
Index: /trunk/mod_cacher/htdocs/common-zh-simplified-utf-8.lang.php
===================================================================
--- /trunk/mod_cacher/htdocs/common-zh-simplified-utf-8.lang.php	(revision 1003)
+++ /trunk/mod_cacher/htdocs/common-zh-simplified-utf-8.lang.php	(revision 1003)
@@ -0,0 +1,126 @@
+<?php
+
+$strings = array(
+		'Cache Legends'
+		=> 'Cache 帮助',
+		'List Legends'
+		=> 'List 帮助',
+		'XCache Administration Help'
+		=> 'XCache 管理页面帮助信息',
+		'Help'
+		=> '帮助',
+		'Slots'
+		=> '槽',
+		'Size'
+		=> '大小',
+		'Avail'
+		=> '剩余',
+		'Used'
+		=> '已用',
+		'Clear'
+		=> '清除',
+		'Sure to clear?'
+		=> '确认要清除吗?',
+		'Compiling'
+		=> '编译中',
+		'% Free'
+		=> '% 剩余',
+		'% Used'
+		=> '% 已用',
+		'Hits'
+		=> '命中',
+		'Hits 24H'
+		=> '24H 分布',
+		'Hits/H'
+		=> '命中/H',
+		'Hits/S'
+		=> '命中/S',
+		'Updates'
+		=> '更新',
+		'Clogs'
+		=> '阻塞',
+		'OOMs'
+		=> '内存不足',
+		'Errors'
+		=> '错误',
+		'Protected'
+		=> '保护',
+		'Cached'
+		=> '缓存',
+		'Deleted'
+		=> '待删',
+		'Delete'
+		=> '删除',
+		'Free Blocks'
+		=> '空闲块',
+
+		'entry'
+		=> '项目',
+		'Refcount'
+		=> '引用数',
+		'PhpShared'
+		=> '共享',
+		'SrcSize'
+		=> '源大小',
+		'Modify'
+		=> '修改',
+		'device'
+		=> '设备',
+		'inode'
+		=> 'Inode',
+		'hash'
+		=> 'Hash',
+		'Access'
+		=> '访问',
+		'Create'
+		=> '创建',
+		'See also'
+		=> '建议参考',
+		'GC'
+		=> 'GC',
+		'Legends:'
+		=> '图例:',
+		'Used Blocks'
+		=> '已用块',
+		'Free Blocks'
+		=> '未用块',
+		'Total'
+		=> '总共',
+		'Cache'
+		=> '缓冲区',
+		'Caches'
+		=> '缓冲区',
+		'Cached'
+		=> '缓冲',
+		'php Cached'
+		=> '缓冲的 php 脚本',
+		'php Deleted'
+		=> '待删 php 脚本',
+		'var Cached'
+		=> '缓冲的变量',
+		'var Deleted'
+		=> '待删变量',
+		'Statistics'
+		=> '摘要统计',
+		'List PHP'
+		=> '列出PHP',
+		'List Var Data'
+		=> '列变量数据',
+		'XCache %s Administration'
+		=> 'XCache %s 管理页面',
+		'size'
+		=> '大小',
+		'offset'
+		=> '位置',
+		'Module Info'
+		=> '模块信息',
+		'Remove Selected'
+		=> '删除所选',
+		'Editing Variable %s'
+		=> '正在编辑变量 %s',
+		'Set %s in config to enable'
+		=> '请在配置文件中设置 %s 启用本功能',
+		''
+		=> '',
+		);
+
Index: /trunk/mod_cacher/htdocs/common-zh-traditional-utf-8.lang.php
===================================================================
--- /trunk/mod_cacher/htdocs/common-zh-traditional-utf-8.lang.php	(revision 1003)
+++ /trunk/mod_cacher/htdocs/common-zh-traditional-utf-8.lang.php	(revision 1003)
@@ -0,0 +1,126 @@
+<?php
+
+$strings = array(
+		'Cache Legends'
+		=> 'Cache 說明',
+		'List Legends'
+		=> 'List 說明',
+		'XCache Administration Help'
+		=> 'XCache 管理頁面說明訊息',
+		'Help'
+		=> '說明',
+		'Slots'
+		=> '槽',
+		'Size'
+		=> '大小',
+		'Avail'
+		=> '剩餘',
+		'Used'
+		=> '已用',
+		'Clear'
+		=> '清除',
+		'Sure to clear?'
+		=> '確認要清除嗎?',
+		'Compiling'
+		=> '編譯中',
+		'% Free'
+		=> '% 剩余',
+		'% Used'
+		=> '% 已用',
+		'Hits'
+		=> '命中',
+		'Hits 24H'
+		=> '24H 分布',
+		'Hits/H'
+		=> '命中/H',
+		'Hits/S'
+		=> '命中/S',
+		'Updates'
+		=> '更新',
+		'Clogs'
+		=> '阻塞',
+		'OOMs'
+		=> '記憶體不足',
+		'Errors'
+		=> '错误',
+		'Protected'
+		=> '保護',
+		'Cached'
+		=> '快取',
+		'Deleted'
+		=> '待刪',
+		'Delete'
+		=> '刪除',
+		'Free Blocks'
+		=> '空閒塊',
+
+		'entry'
+		=> '項目',
+		'Refcount'
+		=> '引用數',
+		'PhpShared'
+		=> '共享',
+		'SrcSize'
+		=> '原始檔案大小',
+		'Modify'
+		=> '修改',
+		'device'
+		=> '設備',
+		'inode'
+		=> 'Inode',
+		'hash'
+		=> 'Hash',
+		'Access'
+		=> '存取',
+		'Create'
+		=> '建立',
+		'See also'
+		=> '建議參考',
+		'GC'
+		=> 'GC',
+		'Legends:'
+		=> '图例:',
+		'Used Blocks'
+		=> '已用块',
+		'Free Blocks'
+		=> '未用块',
+		'Total'
+		=> '总共',
+		'Cache'
+		=> '快取',
+		'Caches'
+		=> '快取',
+		'Cached'
+		=> '快取',
+		'php Cached'
+		=> '快取的 php 指令',
+		'php Deleted'
+		=> '待刪 php 指令',
+		'var Cached'
+		=> '快取的變數',
+		'var Deleted'
+		=> '待刪變數',
+		'Statistics'
+		=> '摘要統計',
+		'List PHP'
+		=> '列出PHP',
+		'List Var Data'
+		=> '列變數資料',
+		'XCache %s Administration'
+		=> 'XCache %s 管理頁面',
+		'size'
+		=> '大小',
+		'offset'
+		=> '位置',
+		'Module Info'
+		=> '組元訊息',
+		'Remove Selected'
+		=> '移除所选',
+		'Editing Variable %s'
+		=> '正在编辑变量 %s',
+		'Set %s in config to enable'
+		=> '請在配置文件中設置 %s 啟用本功能',
+		''
+		=> '',
+		);
+
Index: /trunk/mod_cacher/htdocs/common.php
===================================================================
--- /trunk/mod_cacher/htdocs/common.php	(revision 1003)
+++ /trunk/mod_cacher/htdocs/common.php	(revision 1003)
@@ -0,0 +1,142 @@
+<?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 _T($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 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/mod_cacher/htdocs/config.default.php
===================================================================
--- /trunk/mod_cacher/htdocs/config.default.php	(revision 1003)
+++ /trunk/mod_cacher/htdocs/config.default.php	(revision 1003)
@@ -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/mod_cacher/htdocs/config.example.php
===================================================================
--- /trunk/mod_cacher/htdocs/config.example.php	(revision 1003)
+++ /trunk/mod_cacher/htdocs/config.example.php	(revision 1003)
@@ -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/mod_cacher/htdocs/edit.php
===================================================================
--- /trunk/mod_cacher/htdocs/edit.php	(revision 1003)
+++ /trunk/mod_cacher/htdocs/edit.php	(revision 1003)
@@ -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/mod_cacher/htdocs/edit.tpl.php
===================================================================
--- /trunk/mod_cacher/htdocs/edit.tpl.php	(revision 1003)
+++ /trunk/mod_cacher/htdocs/edit.tpl.php	(revision 1003)
@@ -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(_T("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(_T("Set %s in config to enable"), "\$config['enable_eval'] = true");
+		}
+		?>
+	</fieldset>
+</form>
+<?php include("footer.tpl.php"); ?>
Index: /trunk/mod_cacher/htdocs/footer.tpl.php
===================================================================
--- /trunk/mod_cacher/htdocs/footer.tpl.php	(revision 1003)
+++ /trunk/mod_cacher/htdocs/footer.tpl.php	(revision 1003)
@@ -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/mod_cacher/htdocs/header.tpl.php
===================================================================
--- /trunk/mod_cacher/htdocs/header.tpl.php	(revision 1003)
+++ /trunk/mod_cacher/htdocs/header.tpl.php	(revision 1003)
@@ -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(_T("XCache %s Administration"), $xcache_version); ?></title>
+</head>
+
+<body>
+<h1><?php echo sprintf(_T("XCache %s Administration"), $xcache_version); ?></h1>
Index: /trunk/mod_cacher/htdocs/help-en.lang.php
===================================================================
--- /trunk/mod_cacher/htdocs/help-en.lang.php	(revision 1003)
+++ /trunk/mod_cacher/htdocs/help-en.lang.php	(revision 1003)
@@ -0,0 +1,37 @@
+<h2><?php echo _T('Cache Legends'); ?></h2>
+<dl>
+<dt><?php echo _T('Slots'); ?>: </dt><dd>Number of hash slots. the setting from your php.ini</dd>
+<dt><?php echo _T('Size'); ?>: </dt><dd>Cache Size, Size of the cache (or cache chunk), in bytes</dd>
+<dt><?php echo _T('Avail'); ?>: </dt><dd>Available Memory, free memory in bytes of this cache</dd>
+<dt><?php echo _T('% Used'); ?>: </dt><dd>Percent, A bar shows how much memory available in percent, and memory blocks status</dd>
+<dt><?php echo _T('Clear'); ?>: </dt><dd>Clear Button, Press the button to clean this cache</dd>
+<dt><?php echo _T('Compiling'); ?>: </dt><dd>Compiling flag, "yes" if the cache is busy compiling php script</dd>
+<dt><?php echo _T('Hits'); ?>: </dt><dd>Cache Hits, hit=a var/php is loaded from this cache</dd>
+<dt><?php echo _T('Hits/H'); ?>: </dt><dd>Average Hits per Hour. Only last 24 hours is logged</dd>
+<dt><?php echo _T('Hits 24H'); ?>: </dt><dd>Hits 24 Hours. Hits graph of last 24 hours</dd>
+<dt><?php echo _T('Hits/S'); ?>: </dt><dd>Average Hits per Second. Only last 5 seconds is logged</dd>
+<dt><?php echo _T('Updates'); ?>: </dt><dd>Cache Updates</dd>
+<dt><?php echo _T('Clogs'); ?>: </dt><dd>Compiling Clogs, clog=compiling is needed but avoided to wait(be blocked) when the cache is busy compiling already</dd>
+<dt><?php echo _T('OOMs'); ?>: </dt><dd>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</dd>
+<dt><?php echo _T('Errors'); ?>: </dt><dd>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 <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><?php echo _T('Protected'); ?>: </dt><dd>Whether <a href="http://xcache.lighttpd.net/wiki/ReadonlyProtection">readonly_protection</a> is available and enable on this cache</dd>
+<dt><?php echo _T('Cached'); ?>: </dt><dd>Number of entries stored in this cache</dd>
+<dt><?php echo _T('Deleted'); ?>: </dt><dd>Number of entries is pending in delete list (expired but referenced)</dd>
+<dt><?php echo _T('GC'); ?>: </dt><dd>Seconds count down of Garbage Collection</dd>
+<dt><?php echo _T('Free Blocks'); ?>: </dt><dd>Free blocks list in the specified cache</dd>
+</dl>
+
+<h2><?php echo _T('List Legends'); ?></h2>
+<dl>
+<dt><?php echo _T('entry'); ?>: </dt><dd>The entry name or filename</dd>
+<dt><?php echo _T('Hits'); ?>: </dt><dd>Times this entry is hit (loaded from this cache)</dd>
+<dt><?php echo _T('Refcount'); ?>: </dt><dd>Reference count of this entry is holded by a php request</dd>
+<dt><?php echo _T('Size'); ?>: </dt><dd>Size in bytes of this entry in the cache</dd>
+<dt><?php echo _T('PhpShared'); ?>: </dt><dd>Count of entry sharing this php data</dd>
+<dt><?php echo _T('SrcSize'); ?>: </dt><dd>Size of the source file</dd>
+<dt><?php echo _T('Modify'); ?>: </dt><dd>Last modified time of the source file</dd>
+<dt><?php echo _T('device'); ?>: </dt><dd>device number of the source file</dd>
+<dt><?php echo _T('inode'); ?>: </dt><dd>inode number of the source file</dd>
+<dt><?php echo _T('Access'); ?>: </dt><dd>Last access time of the cached entry</dd>
+<dt><?php echo _T('Create'); ?>: </dt><dd>The time when this entry is stored</dd>
+</dl>
Index: /trunk/mod_cacher/htdocs/help-zh-simplified-utf-8.lang.php
===================================================================
--- /trunk/mod_cacher/htdocs/help-zh-simplified-utf-8.lang.php	(revision 1003)
+++ /trunk/mod_cacher/htdocs/help-zh-simplified-utf-8.lang.php	(revision 1003)
@@ -0,0 +1,37 @@
+<h2><?php echo _T('Cache Legends'); ?></h2>
+<dl>
+<dt><?php echo _T('Slots'); ?>: </dt><dd>Hash 槽个数, 对应 php.ini 里的设置</dd>
+<dt><?php echo _T('Size'); ?>: </dt><dd>共享内存区大小, 单位: 字节</dd>
+<dt><?php echo _T('Avail'); ?>: </dt><dd>可用内存, 对应共享内存区的剩余内存字节数</dd>
+<dt><?php echo _T('% Used'); ?>: </dt><dd>百分比, 条状显示可用内存的比例, 以及显示分配块状态</dd>
+<dt><?php echo _T('Clear'); ?>: </dt><dd>清除按钮, 点击按钮清除对应共享内存区的数据</dd>
+<dt><?php echo _T('Compiling'); ?>: </dt><dd>编译标记, 当共享内存区正在编译 php 脚本时标记为 "yes"</dd>
+<dt><?php echo _T('Hits'); ?>: </dt><dd>共享内存命中次数, 命中=从该共享内存载入php或者变量</dd>
+<dt><?php echo _T('Hits/H'); ?>: </dt><dd>每小时命中次数. 只统计最后 24 小时</dd>
+<dt><?php echo _T('Hits 24H'); ?>: </dt><dd>24 小时命中分布图. 图表现是最后 24 小时的命中次数</dd>
+<dt><?php echo _T('Hits/S'); ?>: </dt><dd>每秒命中次数. 只统计最后 5 秒</dd>
+<dt><?php echo _T('Updates'); ?>: </dt><dd>共享内存更新次数</dd>
+<dt><?php echo _T('Clogs'); ?>: </dt><dd>编译阻塞跳过, 阻塞=当需该共享内存区负责编译时, 其他进程/现成无法访问此共享内存. 跳过=XCache 自动判断阻塞的共享内存区自动跳过阻塞等待, 直接使用非共享内存方式继续处理请求</dd>
+<dt><?php echo _T('OOMs'); ?>: </dt><dd>内存不足次数, 显示需要存储新数据但是共享内存区内存不足的次数. 如果出现太频繁请考虑加大配置中的 xcache.size 或者 xcache.var_size</dd>
+<dt><?php echo _T('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><?php echo _T('Protected'); ?>: </dt><dd>显示该 Cache 是否支持并启用 <a href="http://xcache.lighttpd.net/xcache/wiki/ReadonlyProtection">readonly_protection</a></dd>
+<dt><?php echo _T('Cached'); ?>: </dt><dd>共享内存于该共享内存区的项目条数</dd>
+<dt><?php echo _T('Deleted'); ?>: </dt><dd>共享内存区内将要删除的项目 (已经删除但是还被某些进程占用)</dd>
+<dt><?php echo _T('GC'); ?>: </dt><dd>垃圾回收的倒计时</dd>
+<dt><?php echo _T('Free Blocks'); ?>: </dt><dd>共享内存区内的空闲内存块</dd>
+</dl>
+
+<h2><?php echo _T('List Legends'); ?></h2>
+<dl>
+<dt><?php echo _T('entry'); ?>: </dt><dd>项目名或者文件名</dd>
+<dt><?php echo _T('Hits'); ?>: </dt><dd>该项目被命中的次数 (从共享内存区载入)</dd>
+<dt><?php echo _T('Refcount'); ?>: </dt><dd>项目依然被其他进程占据的引用次数</dd>
+<dt><?php echo _T('Size'); ?>: </dt><dd>项目在共享内存里占用字节数</dd>
+<dt><?php echo _T('PhpShared'); ?>: </dt><dd>与本项目相同 PHP 代码的个数</dd>
+<dt><?php echo _T('SrcSize'); ?>: </dt><dd>源文件大小</dd>
+<dt><?php echo _T('Modify'); ?>: </dt><dd>源文件最后修改时间</dd>
+<dt><?php echo _T('device'); ?>: </dt><dd>源文件所在设备ID</dd>
+<dt><?php echo _T('inode'); ?>: </dt><dd>源文件的inode</dd>
+<dt><?php echo _T('Access'); ?>: </dt><dd>最后访问该项目的时间</dd>
+<dt><?php echo _T('Create'); ?>: </dt><dd>该项目被创建于共享内的时间</dd>
+</dl>
Index: /trunk/mod_cacher/htdocs/help-zh-traditional-utf-8.lang.php
===================================================================
--- /trunk/mod_cacher/htdocs/help-zh-traditional-utf-8.lang.php	(revision 1003)
+++ /trunk/mod_cacher/htdocs/help-zh-traditional-utf-8.lang.php	(revision 1003)
@@ -0,0 +1,34 @@
+<h2><?php echo _T('Cache Legends'); ?></h2>
+<dl>
+<dt><?php echo _T('Slots'); ?>: </dt><dd>Hash 槽個數，對應 php.ini 裡的設置</dd>
+<dt><?php echo _T('Size'); ?>: </dt><dd>共享記憶體區大小，單位：位元</dd>
+<dt><?php echo _T('Avail'); ?>: </dt><dd>可用記憶體，對應共享記憶體區的剩餘記憶體位元數</dd>
+<dt><?php echo _T('% Used'); ?>: </dt><dd>百分比，條狀顯示可用記憶體的比例</dd>
+<dt><?php echo _T('Clear'); ?>: </dt><dd>清除按鈕，點擊按鈕清除對應共享記憶體區的資料</dd>
+<dt><?php echo _T('Compiling'); ?>: </dt><dd>編譯標記，當共享記憶體區正在編譯 php 指令時標記為 "yes"</dd>
+<dt><?php echo _T('Hits'); ?>: </dt><dd>共享記憶體命中次數，命中=從該共享記憶體載入php或者變數</dd>
+<dt><?php echo _T('Updates'); ?>: </dt><dd>共享記憶更新過次數</dd>
+<dt><?php echo _T('Clogs'); ?>: </dt><dd>編譯阻塞跳過，阻塞=當需該共享記憶體區負責編譯時，其他程序/現成無法存取此共享記憶體. 跳過=XCache 自動判斷阻塞的共享記憶體區自動跳過阻塞等待，直接使用非共享記憶體方式繼續處理請求</dd>
+<dt><?php echo _T('OOMs'); ?>: </dt><dd>記憶體不足次數，顯示需要儲存新資料但是共享記憶體區記憶體不足的次數. 如果出現太頻繁請考慮加大配置中的 xcache.size 或者 xcache.var_size</dd>
+<dt><?php echo _T('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><?php echo _T('Protected'); ?>: </dt><dd>顯示該 Cache 是否支援並啟用 <a href="http://xcache.lighttpd.net/xcache/wiki/ReadonlyProtection">readonly_protection</a></dd>
+<dt><?php echo _T('Cached'); ?>: </dt><dd>共享記憶體於該共享記憶體區的項目個數</dd>
+<dt><?php echo _T('Deleted'); ?>: </dt><dd>共享記憶體區內將要刪除的項目 (已經刪除但是還被某些程序佔用)</dd>
+<dt><?php echo _T('GC'); ?>: </dt><dd>垃圾回收的倒數計時</dd>
+<dt><?php echo _T('Free Blocks'); ?>: </dt><dd>共享記憶體區內的空閒記憶體區塊</dd>
+</dl>
+
+<h2><?php echo _T('List Legends'); ?></h2>
+<dl>
+<dt><?php echo _T('entry'); ?>: </dt><dd>項目名稱或者檔案名稱</dd>
+<dt><?php echo _T('Hits'); ?>: </dt><dd>該項目被命中的次數 (從共享記憶體區載入)</dd>
+<dt><?php echo _T('Refcount'); ?>: </dt><dd>項目依然被其他程序佔用的引用次數</dd>
+<dt><?php echo _T('Size'); ?>: </dt><dd>項目在共享記憶體裡佔用位元數</dd>
+<dt><?php echo _T('PhpShared'); ?>: </dt><dd>與本項目相同 PHP 內容的个數</dd>
+<dt><?php echo _T('SrcSize'); ?>: </dt><dd>原始檔案大小</dd>
+<dt><?php echo _T('Modify'); ?>: </dt><dd>原始檔案最後修改時間</dd>
+<dt><?php echo _T('device'); ?>: </dt><dd>原始檔案所在設備ID</dd>
+<dt><?php echo _T('inode'); ?>: </dt><dd>原始檔案的inode</dd>
+<dt><?php echo _T('Access'); ?>: </dt><dd>最後存取該項目的時間</dd>
+<dt><?php echo _T('Create'); ?>: </dt><dd>該項目被建立於共享內的時間</dd>
+</dl>
Index: /trunk/mod_cacher/htdocs/help.php
===================================================================
--- /trunk/mod_cacher/htdocs/help.php	(revision 1003)
+++ /trunk/mod_cacher/htdocs/help.php	(revision 1003)
@@ -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 _T('XCache Administration Help'); ?></title>
+	<script>
+	function toggle(o)
+	{
+		o.style.display = o.style.display != 'block' ? 'block' : 'none';
+	}
+	</script>
+</head>
+
+<body>
+<h1><?php echo _T('XCache Administration Help'); ?></h1>
+<div id1="help">
+<?php include(get_language_file("help")); ?>
+</div>
+
+<?php echo _T('See also'); ?>: <a href="http://xcache.lighttpd.net/wiki/PhpIni">setting php.ini for XCache</a> in the <a href="http://xcache.lighttpd.net/">XCache wiki</a>
+</body>
+</html>
Index: /trunk/mod_cacher/htdocs/index.php
===================================================================
--- /trunk/mod_cacher/htdocs/index.php	(revision 1003)
+++ /trunk/mod_cacher/htdocs/index.php	(revision 1003)
@@ -0,0 +1,3 @@
+<?php
+
+include("xcache.php");
Index: /trunk/mod_cacher/htdocs/mkpassword.php
===================================================================
--- /trunk/mod_cacher/htdocs/mkpassword.php	(revision 1003)
+++ /trunk/mod_cacher/htdocs/mkpassword.php	(revision 1003)
@@ -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/mod_cacher/htdocs/tablesort.js
===================================================================
--- /trunk/mod_cacher/htdocs/tablesort.js	(revision 1003)
+++ /trunk/mod_cacher/htdocs/tablesort.js	(revision 1003)
@@ -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 (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/mod_cacher/htdocs/xcache.css
===================================================================
--- /trunk/mod_cacher/htdocs/xcache.css	(revision 1003)
+++ /trunk/mod_cacher/htdocs/xcache.css	(revision 1003)
@@ -0,0 +1,54 @@
+input, table { font-family: sans-serif; }
+input { font-size: 12px; }
+table { border-collapse: collapse; font-size: 11px; margin: 0; margin-bottom: 10px; }
+table caption, h2 { font-size: 16px; font-weight: bold; text-align: left; padding-top: 20px; margin-bottom: 2px; }
+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; }
+.moduleinfo table { border: 1px solid black; }
+.moduleinfo table th, .moduleinfo table td { border: 1px solid black; }
+.moduleinfo table th { font-weight: bold; }
+.moduleinfo .e {background-color: #ccccff; font-weight: bold; color: #000000;}
+.moduleinfo .h {background-color: #9999cc; font-weight: bold; color: #000000;}
+.moduleinfo .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: 0 8px 0 8px; border: 1px solid white; border-bottom-color: black; }
+.switcher a:hover.active
+, .switcher a:active.active
+, .switcher a.active { color: black; text-decoration: none; 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/mod_cacher/htdocs/xcache.php
===================================================================
--- /trunk/mod_cacher/htdocs/xcache.php	(revision 1003)
+++ /trunk/mod_cacher/htdocs/xcache.php	(revision 1003)
@@ -0,0 +1,376 @@
+<?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);
+}
+
+if (!extension_loaded('XCache')) {
+	echo '<h1>XCache is not loaded</h1>';
+	ob_start();
+	phpinfo();
+	$info = ob_get_clean();
+	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?";
+	}
+	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'] = _T('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'] = _T('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('!XCache</a></h2>(.*?)<h2>!is', $moduleinfo, $m)) {
+		$moduleinfo = $m[1];
+	}
+	else {
+		$moduleinfo = null;
+	}
+	break;
+}
+// }}}
+
+$type_php = XC_TYPE_PHP;
+$type_var = XC_TYPE_VAR;
+$types = array($type_none => _T('Statistics'), $type_php => _T('List PHP'), $type_var => _T('List Var Data'));
+$php_version = phpversion();
+$xcache_version = XCACHE_VERSION;
+$xcache_modules = XCACHE_MODULES;
+
+include("xcache.tpl.php");
+
+?>
Index: /trunk/mod_cacher/htdocs/xcache.tpl.php
===================================================================
--- /trunk/mod_cacher/htdocs/xcache.tpl.php	(revision 1003)
+++ /trunk/mod_cacher/htdocs/xcache.tpl.php	(revision 1003)
@@ -0,0 +1,271 @@
+<?php include("header.tpl.php"); ?>
+<div id="help">
+	<a href="help.php"><?php echo _T("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">
+	<caption><?php echo _T('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(); ?>>
+		<th>-</th>
+		<th><?php echo _T('Slots'); ?></th>
+		<th><?php echo _T('Size'); ?></th>
+		<th><?php echo _T('Avail'); ?></th>
+		<th><?php echo _T($config['percent_graph_type'] == 'free' ? '% Free' : '% Used'); ?></th>
+		<th><?php echo _T('Clear'); ?></th>
+		<th><?php echo _T('Compiling'); ?></th>
+		<th><?php echo _T('Hits'); ?></th>
+		<th><?php echo _T('Hits/H'); ?></th>
+		<th><?php echo _T('Hits 24H'); ?></th>
+		<th><?php echo _T('Hits/S'); ?></th>
+		<th><?php echo _T('Updates'); ?></th>
+		<th><?php echo _T('Clogs'); ?></th>
+		<th><?php echo _T('OOMs'); ?></th>
+		<th><?php echo _T('Errors'); ?></th>
+		<th><?php echo _T('Protected'); ?></th>
+		<th><?php echo _T('Cached'); ?></th>
+		<th><?php echo _T('Deleted'); ?></th>
+		<th><?php echo _T('GC'); ?></th>
+	</tr>
+	<?php
+	$numkeys = explode(',', 'slots,size,avail,hits,updates,clogs,ooms,errors,cached,deleted');
+	$l_clear = _T('Clear');
+	$l_clear_confirm = _T('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>{$hits_avg_h}</td>
+		<td><div class="hitsgraph" style="width: {$hits_graph_h_w}px">{$hits_graph_h}</div></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 _T('Legends:'); ?></div>
+	<div class="legend pvalue">&nbsp;&nbsp;</div>
+	<div class="legendtitle"><?php echo _T($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 _T($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 _T('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 _T("{$cachelist['type_name']} $listname"); ?></caption>
+		<?php
+		echo "
+		<tr ", $a->next(), ">";
+		?>
+
+			<?php if (!$isphp) { ?>
+			<th width="20">R</th>
+			<?php } ?>
+			<th><a href="javascript:" onclick="resort(this); return false"><?php echo _T('Cache'); ?></a></th>
+			<th><a href="javascript:" onclick="resort(this); return false"><?php echo _T('entry'); ?></a></th>
+			<th><a href="javascript:" onclick="resort(this); return false"><?php echo _T('Hits'); ?></a></th>
+			<th><a href="javascript:" onclick="resort(this); return false"><?php echo _T('Size'); ?></a></th>
+			<?php if ($isphp) { ?>
+			<th><a href="javascript:" onclick="resort(this); return false"><?php echo _T('Refcount'); ?></a></th>
+			<th><a href="javascript:" onclick="resort(this); return false"><?php echo _T('PhpShared'); ?></a></th>
+			<th><a href="javascript:" onclick="resort(this); return false"><?php echo _T('SrcSize'); ?></a></th>
+			<th><a href="javascript:" onclick="resort(this); return false"><?php echo _T('Modify'); ?></a></th>
+			<?php if ($haveinode) { ?>
+			<th><a href="javascript:" onclick="resort(this); return false"><?php echo _T('device'); ?></a></th>
+			<th><a href="javascript:" onclick="resort(this); return false"><?php echo _T('inode'); ?></a></th>
+			<?php } ?>
+			<?php } ?>
+			<th><a href="javascript:" onclick="resort(this); return false"><?php echo _T('hash'); ?></a></th>
+			<th><a href="javascript:" onclick="resort(this); return false"><?php echo _T('Access'); ?></a></th>
+			<th><a href="javascript:" onclick="resort(this); return false"><?php echo _T('Create'); ?></a></th>
+			<?php if ($listname == 'Deleted') { ?>
+			<th><a href="javascript:" onclick="resort(this); return false"><?php echo _T('Delete'); ?></a></th>
+			<?php } ?>
+		</tr>
+		<?php
+		foreach ($entries as $i => $entry) {
+			echo "
+			<tr ", $a->next(), ">";
+			$name     = htmlspecialchars($entry['name']);
+			$hits     = number_format($entry['hits']);
+			$size     = size($entry['size']);
+			if ($isphp) {
+				$file_size   = size($entry['file_size']);
+				$phprefcount = number_format($entry['phprefcount']);
+			}
+
+			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['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 _T("Remove Selected"); ?>">
+	<?php } ?>
+	</form>
+<?php
+	}
+	ob_end_flush();
+}
+if ($moduleinfo) {
+	$t_moduleinfo = _T("Module Info");
+	echo <<<HTML
+<h2>$t_moduleinfo</h2>
+<div class="moduleinfo">$moduleinfo</div>
+HTML;
+}
+?>
+<?php include("footer.tpl.php"); ?>
Index: /trunk/mod_coverager/htdocs/common-en.lang.php
===================================================================
--- /trunk/mod_coverager/htdocs/common-en.lang.php	(revision 1003)
+++ /trunk/mod_coverager/htdocs/common-en.lang.php	(revision 1003)
@@ -0,0 +1,5 @@
+<?php
+
+$GLOBALS['show_todo_strings'] = false;
+
+?>
Index: /trunk/mod_coverager/htdocs/common-zh-simplified-gb2312.lang.php
===================================================================
--- /trunk/mod_coverager/htdocs/common-zh-simplified-gb2312.lang.php	(revision 1003)
+++ /trunk/mod_coverager/htdocs/common-zh-simplified-gb2312.lang.php	(revision 1003)
@@ -0,0 +1,25 @@
+<?php
+
+$strings = array(
+		'root'
+		=> '¿ªÊ¼',
+		'Directory'
+		=> 'Ä¿Â¼',
+		'File'
+		=> 'ÎÄ¼þ',
+		'Percent'
+		=> '¸²¸ÇÂÊ',
+		'Hits'
+		=> 'ÃüÖÐ',
+		'Lines'
+		=> 'ÐÐÊý',
+		'TODO'
+		=> 'ÏÐÖÃÎÄ¼þ',
+		'XCache PHP Code Coverage Viewer'
+		=> 'XCache PHP ´úÂë¸²¸Ç²é¿´Æ÷',
+		'module'
+		=> 'Ä£¿é',
+		''
+		=> '',
+		);
+
Index: /trunk/mod_coverager/htdocs/common-zh-simplified-utf-8.lang.php
===================================================================
--- /trunk/mod_coverager/htdocs/common-zh-simplified-utf-8.lang.php	(revision 1003)
+++ /trunk/mod_coverager/htdocs/common-zh-simplified-utf-8.lang.php	(revision 1003)
@@ -0,0 +1,25 @@
+<?php
+
+$strings = array(
+		'root'
+		=> '开始',
+		'Directory'
+		=> '目录',
+		'File'
+		=> '文件',
+		'Percent'
+		=> '覆盖率',
+		'Hits'
+		=> '命中',
+		'Lines'
+		=> '行数',
+		'TODO'
+		=> '闲置文件',
+		'XCache PHP Code Coverage Viewer'
+		=> 'XCache PHP 代码覆盖查看器',
+		'module'
+		=> '模块',
+		''
+		=> '',
+		);
+
Index: /trunk/mod_coverager/htdocs/common.php
===================================================================
--- /trunk/mod_coverager/htdocs/common.php	(revision 1003)
+++ /trunk/mod_coverager/htdocs/common.php	(revision 1003)
@@ -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/mod_coverager/htdocs/config.php.example
===================================================================
--- /trunk/mod_coverager/htdocs/config.php.example	(revision 1003)
+++ /trunk/mod_coverager/htdocs/config.php.example	(revision 1003)
@@ -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()
+// }}}
+
+?>
Index: /trunk/mod_coverager/htdocs/coverager.css
===================================================================
--- /trunk/mod_coverager/htdocs/coverager.css	(revision 1003)
+++ /trunk/mod_coverager/htdocs/coverager.css	(revision 1003)
@@ -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/mod_coverager/htdocs/coverager.php
===================================================================
--- /trunk/mod_coverager/htdocs/coverager.php	(revision 1003)
+++ /trunk/mod_coverager/htdocs/coverager.php	(revision 1003)
@@ -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/mod_coverager/htdocs/coverager.tpl.php
===================================================================
--- /trunk/mod_coverager/htdocs/coverager.tpl.php	(revision 1003)
+++ /trunk/mod_coverager/htdocs/coverager.tpl.php	(revision 1003)
@@ -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/mod_coverager/htdocs/index.php
===================================================================
--- /trunk/mod_coverager/htdocs/index.php	(revision 1003)
+++ /trunk/mod_coverager/htdocs/index.php	(revision 1003)
@@ -0,0 +1,3 @@
+<?php
+
+include("coverager.php");
Index: /trunk/mod_coverager/xc_coverager.c
===================================================================
--- /trunk/mod_coverager/xc_coverager.c	(revision 1003)
+++ /trunk/mod_coverager/xc_coverager.c	(revision 1003)
@@ -0,0 +1,593 @@
+#if 0
+#define XCACHE_DEBUG
+#endif
+
+#include <stdio.h>
+#include "xcache.h"
+#include "ext/standard/flock_compat.h"
+#ifdef HAVE_SYS_FILE_H
+#	include <sys/file.h>
+#endif
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include "stack.h"
+#include "xcache_globals.h"
+#include "coverager.h"
+#include "utils.h"
+typedef HashTable *coverager_t;
+#define PCOV_HEADER_MAGIC 0x564f4350
+
+static char *xc_coveragedump_dir = NULL;
+static zend_compile_file_t *old_compile_file = NULL;
+
+/* dumper */
+static void xc_destroy_coverage(void *pDest) /* {{{ */
+{
+	coverager_t cov = *(coverager_t*) pDest;
+	TRACE("destroy %p", cov);
+	zend_hash_destroy(cov);
+	efree(cov);
+}
+/* }}} */
+void xcache_mkdirs_ex(char *root, int rootlen, char *path, int pathlen TSRMLS_DC) /* {{{ */
+{
+	char *fullpath;
+	struct stat st;
+	ALLOCA_FLAG(use_heap)
+
+	TRACE("mkdirs %s %d %s %d", root, rootlen, path, pathlen);
+	fullpath = my_do_alloca(rootlen + pathlen + 1, use_heap);
+	memcpy(fullpath, root, rootlen);
+	memcpy(fullpath + rootlen, path, pathlen);
+	fullpath[rootlen + pathlen] = '\0';
+
+	if (stat(fullpath, &st) != 0) {
+		char *chr;
+
+		chr = strrchr(path, PHP_DIR_SEPARATOR);
+		if (chr && chr != path) {
+			*chr = '\0';
+			xcache_mkdirs_ex(root, rootlen, path, chr - path TSRMLS_CC);
+			*chr = PHP_DIR_SEPARATOR;
+		}
+		TRACE("mkdir %s", fullpath);
+#if PHP_MAJOR_VERSION > 5
+		php_stream_mkdir(fullpath, 0700, REPORT_ERRORS, NULL);
+#else
+		mkdir(fullpath, 0700);
+#endif
+	}
+	my_free_alloca(fullpath, use_heap);
+}
+/* }}} */
+static void xc_coverager_save_cov(char *srcfile, char *outfilename, coverager_t cov TSRMLS_DC) /* {{{ */
+{
+	long *buf = NULL, *p;
+	long covlines, *phits;
+	int fd = -1;
+	int size;
+	int newfile;
+	struct stat srcstat, outstat;
+	HashPosition pos;
+	char *contents = NULL;
+	long len;
+
+	if (stat(srcfile, &srcstat) != 0) {
+		return;
+	}
+
+	newfile = 0;
+	if (stat(outfilename, &outstat) != 0) {
+		newfile = 1;
+	}
+	else {
+		if (srcstat.st_mtime > outstat.st_mtime) {
+			newfile = 1;
+		}
+	}
+
+	fd = open(outfilename, O_RDWR | O_CREAT, 0600);
+	if (fd < 0) {
+		char *chr;
+		chr = strrchr(srcfile, PHP_DIR_SEPARATOR);
+		if (chr) {
+			*chr = '\0';
+			xcache_mkdirs_ex(xc_coveragedump_dir, strlen(xc_coveragedump_dir), srcfile, chr - srcfile TSRMLS_CC);
+			*chr = PHP_DIR_SEPARATOR;
+		}
+		fd = open(outfilename, O_RDWR | O_CREAT, 0600);
+		if (fd < 0) {
+			goto bailout;
+		}
+	}
+	if (flock(fd, LOCK_EX) != SUCCESS) {
+		goto bailout;
+	}
+
+	if (newfile) {
+		TRACE("%s", "new file");
+	}
+	else if (outstat.st_size) {
+		len = outstat.st_size;
+		contents = emalloc(len);
+		if (read(fd, (void *) contents, len) != len) {
+			goto bailout;
+		}
+		TRACE("oldsize %d", (int) len);
+		do {
+			p = (long *) contents;
+			len -= sizeof(long);
+			if (len < 0) {
+				break;
+			}
+			if (*p++ != PCOV_HEADER_MAGIC) {
+				TRACE("wrong magic in file %s", outfilename);
+				break;
+			}
+
+			p += 2; /* skip covliens */
+			len -= sizeof(long) * 2;
+			if (len < 0) {
+				break;
+			}
+
+			for (; len >= sizeof(long) * 2; len -= sizeof(long) * 2, p += 2) {
+				if (zend_hash_index_find(cov, p[0], (void**)&phits) == SUCCESS) {
+					if (p[1] == -1) {
+						/* OPTIMIZE: already marked */
+						continue;
+					}
+					if (*phits != -1) {
+						p[1] += *phits;
+					}
+				}
+				zend_hash_index_update(cov, p[0], &p[1], sizeof(p[1]), NULL);
+			}
+		} while (0);
+		efree(contents);
+		contents = NULL;
+	}
+
+
+	/* serialize */
+	size = (zend_hash_num_elements(cov) + 1) * sizeof(long) * 2 + sizeof(long);
+	p = buf = emalloc(size);
+	*p++ = PCOV_HEADER_MAGIC;
+	p += 2; /* for covlines */
+	covlines = 0;
+
+	zend_hash_internal_pointer_reset_ex(cov, &pos);
+	while (zend_hash_get_current_data_ex(cov, (void**)&phits, &pos) == SUCCESS) {
+		*p++ = pos->h;
+		*p++ = *phits;
+		if (*phits > 0) {
+			covlines ++;
+		}
+		zend_hash_move_forward_ex(cov, &pos);
+	}
+	p = buf + 1;
+	p[0] = 0;
+	p[1] = covlines;
+
+	if (ftruncate(fd, 0) != 0) {
+		goto bailout;
+	}
+	lseek(fd, 0, SEEK_SET);
+	if (write(fd, (char *) buf, size) != size) {
+		goto bailout;
+	}
+
+bailout:
+	if (contents) efree(contents);
+	if (fd >= 0) close(fd);
+	if (buf) efree(buf);
+}
+/* }}} */
+
+static void xc_coverager_initenv(TSRMLS_D) /* {{{ */
+{
+	if (!XG(coverages)) {
+		XG(coverages) = emalloc(sizeof(HashTable));
+		zend_hash_init(XG(coverages), 0, NULL, xc_destroy_coverage, 0);
+	}
+}
+/* }}} */
+static void xc_coverager_clean(TSRMLS_D) /* {{{ */
+{
+	if (XG(coverages)) {
+		HashPosition pos;
+		coverager_t *pcov;
+
+		zend_hash_internal_pointer_reset_ex(XG(coverages), &pos);
+		while (zend_hash_get_current_data_ex(XG(coverages), (void **) &pcov, &pos) == SUCCESS) {
+			long *phits;
+			coverager_t cov;
+			HashPosition pos2;
+
+			cov = *pcov;
+
+			zend_hash_internal_pointer_reset_ex(cov, &pos2);
+			while (zend_hash_get_current_data_ex(cov, (void**)&phits, &pos2) == SUCCESS) {
+				long hits = *phits;
+
+				if (hits != -1) {
+					hits = -1;
+					zend_hash_index_update(cov, pos2->h, &hits, sizeof(hits), NULL);
+				}
+				zend_hash_move_forward_ex(cov, &pos2);
+			}
+
+			zend_hash_move_forward_ex(XG(coverages), &pos);
+		}
+	}
+}
+/* }}} */
+static void xc_coverager_cleanup(TSRMLS_D) /* {{{ */
+{
+	if (XG(coverages)) {
+		zend_hash_destroy(XG(coverages));
+		efree(XG(coverages));
+		XG(coverages) = NULL;
+	}
+}
+/* }}} */
+
+static void xc_coverager_enable(TSRMLS_D) /* {{{ */
+{
+	XG(coverage_enabled) = 1;
+}
+/* }}} */
+static void xc_coverager_disable(TSRMLS_D) /* {{{ */
+{
+	XG(coverage_enabled) = 0;
+}
+/* }}} */
+
+void xc_coverager_request_init(TSRMLS_D) /* {{{ */
+{
+	if (XG(coverager)) {
+		xc_coverager_enable(TSRMLS_C);
+#ifdef ZEND_COMPILE_EXTENDED_INFO
+		CG(compiler_options) |= ZEND_COMPILE_EXTENDED_INFO;
+#else
+		CG(extended_info) = 1;
+#endif
+	}
+	else {
+		XG(coverage_enabled) = 0;
+	}
+}
+/* }}} */
+static void xc_coverager_autodump(TSRMLS_D) /* {{{ */
+{
+	coverager_t *pcov;
+	zstr s;
+	char *outfilename;
+	int dumpdir_len, outfilelen, alloc_len = 0;
+	uint size;
+	HashPosition pos;
+
+	if (XG(coverages) && xc_coveragedump_dir) {	
+		dumpdir_len = strlen(xc_coveragedump_dir);
+		alloc_len = dumpdir_len + 1 + 128;
+		outfilename = emalloc(alloc_len);
+		strcpy(outfilename, xc_coveragedump_dir);
+
+		zend_hash_internal_pointer_reset_ex(XG(coverages), &pos);
+		while (zend_hash_get_current_data_ex(XG(coverages), (void **) &pcov, &pos) == SUCCESS) {
+			zend_hash_get_current_key_ex(XG(coverages), &s, &size, NULL, 0, &pos);
+			outfilelen = dumpdir_len + size + 5;
+			if (alloc_len < outfilelen) {
+				alloc_len = outfilelen + 128;
+				outfilename = erealloc(outfilename, alloc_len);
+			}
+			strcpy(outfilename + dumpdir_len, ZSTR_S(s));
+			strcpy(outfilename + dumpdir_len + size - 1, ".pcov");
+
+			TRACE("outfilename %s", outfilename);
+			xc_coverager_save_cov(ZSTR_S(s), outfilename, *pcov TSRMLS_CC);
+			zend_hash_move_forward_ex(XG(coverages), &pos);
+		}
+		efree(outfilename);
+	}
+}
+/* }}} */
+static void xc_coverager_dump(zval *return_value TSRMLS_DC) /* {{{ */
+{
+	coverager_t *pcov;
+	HashPosition pos;
+
+	if (XG(coverages)) {
+		array_init(return_value);
+
+		zend_hash_internal_pointer_reset_ex(XG(coverages), &pos);
+		while (zend_hash_get_current_data_ex(XG(coverages), (void **) &pcov, &pos) == SUCCESS) {
+			zval *lines;
+			long *phits;
+			coverager_t cov;
+			HashPosition pos2;
+			zstr filename;
+			uint size;
+
+			cov = *pcov;
+			zend_hash_get_current_key_ex(XG(coverages), &filename, &size, NULL, 0, &pos);
+
+			MAKE_STD_ZVAL(lines);
+			array_init(lines);
+			zend_hash_internal_pointer_reset_ex(cov, &pos2);
+			while (zend_hash_get_current_data_ex(cov, (void**)&phits, &pos2) == SUCCESS) {
+				long hits = *phits;
+				add_index_long(lines, pos2->h, hits >= 0 ? hits : 0);
+				zend_hash_move_forward_ex(cov, &pos2);
+			}
+			add_assoc_zval_ex(return_value, ZSTR_S(filename), strlen(ZSTR_S(filename)) + 1, lines);
+
+			zend_hash_move_forward_ex(XG(coverages), &pos);
+		}
+	}
+	else {
+		RETVAL_NULL();
+	}
+}
+/* }}} */
+void xc_coverager_request_shutdown(TSRMLS_D) /* {{{ */
+{
+	if (XG(coverager)) {
+		xc_coverager_autodump(TSRMLS_C);
+		xc_coverager_cleanup(TSRMLS_C);
+	}
+}
+/* }}} */
+
+/* helper func to store hits into coverages */
+static coverager_t xc_coverager_get(const char *filename TSRMLS_DC) /* {{{ */
+{
+	int len = strlen(filename) + 1;
+	coverager_t cov, *pcov;
+
+	if (zend_u_hash_find(XG(coverages), IS_STRING, filename, len, (void **) &pcov) == SUCCESS) {
+		TRACE("got coverage %s %p", filename, *pcov);
+		return *pcov;
+	}
+	else {
+		cov = emalloc(sizeof(HashTable));
+		zend_hash_init(cov, 0, NULL, NULL, 0);
+		zend_u_hash_add(XG(coverages), IS_STRING, filename, len, (void **) &cov, sizeof(cov), NULL);
+		TRACE("new coverage %s %p", filename, cov);
+		return cov;
+	}
+}
+/* }}} */
+static void xc_coverager_add_hits(HashTable *cov, long line, long hits TSRMLS_DC) /* {{{ */
+{
+	long *poldhits;
+
+	if (line == 0) {
+		return;
+	}
+	if (zend_hash_index_find(cov, line, (void**)&poldhits) == SUCCESS) {
+		if (hits == -1) {
+			/* OPTIMIZE: -1 == init-ing, but it's already initized */
+			return;
+		}
+		if (*poldhits != -1) {
+			hits += *poldhits;
+		}
+	}
+	zend_hash_index_update(cov, line, &hits, sizeof(hits), NULL);
+}
+/* }}} */
+
+static int xc_coverager_get_op_array_size_no_tail(zend_op_array *op_array) /* {{{ */
+{
+	zend_uint last = op_array->last;
+	do {
+next_op:
+		if (last == 0) {
+			break;
+		}
+		switch (op_array->opcodes[last - 1].opcode) {
+#ifdef ZEND_HANDLE_EXCEPTION
+			case ZEND_HANDLE_EXCEPTION:
+#endif
+			case ZEND_RETURN:
+			case ZEND_EXT_STMT:
+				--last;
+				goto next_op;
+		}
+	} while (0);
+	return last;
+}
+/* }}} */
+
+/* prefill */
+static int xc_coverager_init_op_array(zend_op_array *op_array TSRMLS_DC) /* {{{ */
+{
+	zend_uint size;
+	coverager_t cov;
+	zend_uint i;
+
+	if (op_array->type != ZEND_USER_FUNCTION) {
+		return 0;
+	}
+
+	size = xc_coverager_get_op_array_size_no_tail(op_array);
+	cov = xc_coverager_get(op_array->filename TSRMLS_CC);
+	for (i = 0; i < size; i ++) {
+		switch (op_array->opcodes[i].opcode) {
+			case ZEND_EXT_STMT:
+#if 0
+			case ZEND_EXT_FCALL_BEGIN:
+			case ZEND_EXT_FCALL_END:
+#endif
+				xc_coverager_add_hits(cov, op_array->opcodes[i].lineno, -1 TSRMLS_CC);
+				break;
+		}
+	}
+	return 0;
+}
+/* }}} */
+static void xc_coverager_init_compile_result(zend_op_array *op_array TSRMLS_DC) /* {{{ */
+{
+	xc_compile_result_t cr;
+
+	xc_compile_result_init_cur(&cr, op_array TSRMLS_CC);
+	xc_apply_op_array(&cr, (apply_func_t) xc_coverager_init_op_array TSRMLS_CC);
+	xc_compile_result_free(&cr);
+}
+/* }}} */
+static zend_op_array *xc_compile_file_for_coverage(zend_file_handle *h, int type TSRMLS_DC) /* {{{ */
+{
+	zend_op_array *op_array;
+
+	op_array = old_compile_file(h, type TSRMLS_CC);
+	if (op_array) {
+		if (XG(coverager)) {
+			xc_coverager_initenv(TSRMLS_C);
+			xc_coverager_init_compile_result(op_array TSRMLS_CC);
+		}
+	}
+	return op_array;
+}
+/* }}} */
+
+/* hits */
+void xc_coverager_handle_ext_stmt(zend_op_array *op_array, zend_uchar op) /* {{{ */
+{
+	TSRMLS_FETCH();
+
+	if (XG(coverages) && XG(coverage_enabled)) {
+		int size = xc_coverager_get_op_array_size_no_tail(op_array);
+		int oplineno = (*EG(opline_ptr)) - op_array->opcodes;
+		if (oplineno < size) {
+			xc_coverager_add_hits(xc_coverager_get(op_array->filename TSRMLS_CC), (*EG(opline_ptr))->lineno, 1 TSRMLS_CC);
+		}
+	}
+}
+/* }}} */
+
+/* MINIT/MSHUTDOWN */
+int xc_coverager_module_init(int module_number TSRMLS_DC) /* {{{ */
+{
+	old_compile_file = zend_compile_file;
+	zend_compile_file = xc_compile_file_for_coverage;
+
+	if (cfg_get_string("xcache.coveragedump_directory", &xc_coveragedump_dir) == SUCCESS && xc_coveragedump_dir) {
+		int len;
+		xc_coveragedump_dir = pestrdup(xc_coveragedump_dir, 1);
+		len = strlen(xc_coveragedump_dir);
+		if (len) {
+			if (xc_coveragedump_dir[len - 1] == '/') {
+				xc_coveragedump_dir[len - 1] = '\0';
+			}
+		}
+		if (!strlen(xc_coveragedump_dir)) {
+			pefree(xc_coveragedump_dir, 1);
+			xc_coveragedump_dir = NULL;
+		}
+	}
+
+	return SUCCESS;
+}
+/* }}} */
+void xc_coverager_module_shutdown() /* {{{ */
+{
+	if (old_compile_file == xc_compile_file_for_coverage) {
+		zend_compile_file = old_compile_file;
+	}
+	if (xc_coveragedump_dir) {
+		pefree(xc_coveragedump_dir, 1);
+		xc_coveragedump_dir = NULL;
+	}
+}
+/* }}} */
+
+/* user api */
+/* {{{ proto array xcache_coverager_decode(string data)
+ * decode specified data which is saved by auto dumper to array
+ */
+PHP_FUNCTION(xcache_coverager_decode)
+{
+	char *str;
+	int len;
+	long *p;
+
+	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &str, &len) == FAILURE) {
+		return;
+	}
+
+	array_init(return_value);
+
+	p = (long*) str;
+	len -= sizeof(long);
+	if (len < 0) {
+		return;
+	}
+	if (*p++ != PCOV_HEADER_MAGIC) {
+		TRACE("%s", "wrong magic in xcache_coverager_decode");
+		return;
+	}
+
+	for (; len >= sizeof(long) * 2; len -= sizeof(long) * 2, p += 2) {
+		add_index_long(return_value, p[0], p[1] < 0 ? 0 : p[1]);
+	}
+}
+/* }}} */
+/* {{{ proto void xcache_coverager_start([bool clean = true])
+ * starts coverager data collecting
+ */
+PHP_FUNCTION(xcache_coverager_start)
+{
+	zend_bool clean = 1;
+
+	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|b", &clean) == FAILURE) {
+		return;
+	}
+
+	if (clean) {
+		xc_coverager_clean(TSRMLS_C);
+	}
+
+	if (XG(coverager)) {
+		xc_coverager_enable(TSRMLS_C);
+	}
+	else {
+		php_error(E_WARNING, "You can only start coverager after you set 'xcache.coverager' to 'On' in ini");
+	}
+}
+/* }}} */
+/* {{{ proto void xcache_coverager_stop([bool clean = false])
+ * stop coverager data collecting
+ */
+PHP_FUNCTION(xcache_coverager_stop)
+{
+	zend_bool clean = 0;
+
+	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|b", &clean) == FAILURE) {
+		return;
+	}
+
+	xc_coverager_disable(TSRMLS_C);
+	if (clean) {
+		xc_coverager_clean(TSRMLS_C);
+	}
+}
+/* }}} */
+/* {{{ proto array xcache_coverager_get([bool clean = false])
+ * get coverager data collected
+ */
+PHP_FUNCTION(xcache_coverager_get)
+{
+	zend_bool clean = 0;
+	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|b", &clean) == FAILURE) {
+		return;
+	}
+
+	xc_coverager_dump(return_value TSRMLS_CC);
+	if (clean) {
+		xc_coverager_clean(TSRMLS_C);
+	}
+}
+/* }}} */
Index: /trunk/mod_coverager/xc_coverager.h
===================================================================
--- /trunk/mod_coverager/xc_coverager.h	(revision 1003)
+++ /trunk/mod_coverager/xc_coverager.h	(revision 1003)
@@ -0,0 +1,17 @@
+#include "php.h"
+#include "xcache.h"
+
+void xc_coverager_handle_ext_stmt(zend_op_array *op_array, zend_uchar op);
+int xc_coverager_module_init(int module_number TSRMLS_DC);
+void xc_coverager_module_shutdown();
+void xc_coverager_request_init(TSRMLS_D);
+void xc_coverager_request_shutdown(TSRMLS_D);
+PHP_FUNCTION(xcache_coverager_decode);
+PHP_FUNCTION(xcache_coverager_start);
+PHP_FUNCTION(xcache_coverager_stop);
+PHP_FUNCTION(xcache_coverager_get);
+#define XCACHE_COVERAGER_FUNCTIONS() \
+	PHP_FE(xcache_coverager_decode,  NULL) \
+	PHP_FE(xcache_coverager_start,   NULL) \
+	PHP_FE(xcache_coverager_stop,    NULL) \
+	PHP_FE(xcache_coverager_get,     NULL)
Index: /trunk/mod_disassembler/bin/phpdc.phpr
===================================================================
--- /trunk/mod_disassembler/bin/phpdc.phpr	(revision 1003)
+++ /trunk/mod_disassembler/bin/phpdc.phpr	(revision 1003)
@@ -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/mod_disassembler/bin/phpdop.phpr
===================================================================
--- /trunk/mod_disassembler/bin/phpdop.phpr	(revision 1003)
+++ /trunk/mod_disassembler/bin/phpdop.phpr	(revision 1003)
@@ -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/mod_disassembler/lib/Decompiler.class.php
===================================================================
--- /trunk/mod_disassembler/lib/Decompiler.class.php	(revision 1003)
+++ /trunk/mod_disassembler/lib/Decompiler.class.php	(revision 1003)
@@ -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/mod_disassembler/xc_disassembler.c
===================================================================
--- /trunk/mod_disassembler/xc_disassembler.c	(revision 1003)
+++ /trunk/mod_disassembler/xc_disassembler.c	(revision 1003)
@@ -0,0 +1,217 @@
+#include "xc_disassembler.h"
+#include "xcache.h"
+#include "xcache/xc_utils.h"
+#include "xcache/xc_sandbox.h"
+#include "xc_processor.h"
+
+static void xc_dasm(zval *output, zend_op_array *op_array TSRMLS_DC) /* {{{ */
+{
+	const Bucket *b;
+	zval *zv, *list;
+	xc_compile_result_t cr;
+	int bufsize = 2;
+	char *buf;
+	xc_dasm_t dasm;
+
+	xc_compile_result_init_cur(&cr, op_array TSRMLS_CC);
+
+	xc_apply_op_array(&cr, (apply_func_t) xc_undo_pass_two TSRMLS_CC);
+	xc_apply_op_array(&cr, (apply_func_t) xc_fix_opcode TSRMLS_CC);
+
+	/* go */
+	array_init(output);
+
+	ALLOC_INIT_ZVAL(zv);
+	array_init(zv);
+	xc_dasm_zend_op_array(&dasm, zv, op_array TSRMLS_CC);
+	add_assoc_zval_ex(output, ZEND_STRS("op_array"), zv);
+
+	buf = emalloc(bufsize);
+
+	ALLOC_INIT_ZVAL(list);
+	array_init(list);
+	for (b = xc_sandbox_user_function_begin(TSRMLS_C); b; b = b->pListNext) {
+		int keysize, keyLength;
+
+		ALLOC_INIT_ZVAL(zv);
+		array_init(zv);
+		xc_dasm_zend_function(&dasm, zv, b->pData TSRMLS_CC);
+
+		keysize = BUCKET_KEY_SIZE(b) + 2;
+		if (keysize > bufsize) {
+			do {
+				bufsize *= 2;
+			} while (keysize > bufsize);
+			buf = erealloc(buf, bufsize);
+		}
+		memcpy(buf, BUCKET_KEY_S(b), keysize);
+		buf[keysize - 2] = buf[keysize - 1] = ""[0];
+		keyLength = b->nKeyLength;
+#ifdef IS_UNICODE
+		if (BUCKET_KEY_TYPE(b) == IS_UNICODE) {
+			if (buf[0] == ""[0] && buf[1] == ""[0]) {
+				keyLength ++;
+			}
+		} else
+#endif
+		{
+			if (buf[0] == ""[0]) {
+				keyLength ++;
+			}
+		}
+
+		add_u_assoc_zval_ex(list, BUCKET_KEY_TYPE(b), ZSTR(buf), keyLength, zv);
+	}
+	add_assoc_zval_ex(output, ZEND_STRS("function_table"), list);
+	
+	ALLOC_INIT_ZVAL(list);
+	array_init(list);
+	for (b = xc_sandbox_user_class_begin(TSRMLS_C); b; b = b->pListNext) {
+		int keysize, keyLength;
+
+		ALLOC_INIT_ZVAL(zv);
+		array_init(zv);
+		xc_dasm_zend_class_entry(&dasm, zv, CestToCePtr(*(xc_cest_t *)b->pData) TSRMLS_CC);
+
+		keysize = BUCKET_KEY_SIZE(b) + 2;
+		if (keysize > bufsize) {
+			do {
+				bufsize *= 2;
+			} while (keysize > bufsize);
+			buf = erealloc(buf, bufsize);
+		}
+		memcpy(buf, BUCKET_KEY_S(b), keysize);
+		buf[keysize - 2] = buf[keysize - 1] = ""[0];
+		keyLength = b->nKeyLength;
+#ifdef IS_UNICODE
+		if (BUCKET_KEY_TYPE(b) == IS_UNICODE) {
+			if (buf[0] == ""[0] && buf[1] == ""[0]) {
+				keyLength ++;
+			}
+		} else
+#endif
+		{
+			if (buf[0] == ""[0]) {
+				keyLength ++;
+			}
+		}
+		add_u_assoc_zval_ex(list, BUCKET_KEY_TYPE(b), ZSTR(buf), keyLength, zv);
+	}
+	efree(buf);
+	add_assoc_zval_ex(output, ZEND_STRS("class_table"), list);
+
+	/*xc_apply_op_array(&cr, (apply_func_t) xc_redo_pass_two TSRMLS_CC);*/
+	xc_compile_result_free(&cr);
+}
+/* }}} */
+typedef struct xc_dasm_sandboxed_t { /* {{{ */
+	enum Type {
+		xc_dasm_file_t
+		, xc_dasm_string_t
+	} type;
+	union {
+		zval *zfilename;
+		struct {
+			zval *source;
+			char *eval_name;
+		} compile_string;
+	} input;
+
+	zval *output;
+} xc_dasm_sandboxed_t; /* }}} */
+zend_op_array *xc_dasm_sandboxed(void *data TSRMLS_DC) /* {{{ */
+{
+	zend_bool catched = 0;
+	zend_op_array *op_array = NULL;
+	xc_dasm_sandboxed_t *sandboxed_dasm = (xc_dasm_sandboxed_t *) data;
+
+	zend_try {
+		if (sandboxed_dasm->type == xc_dasm_file_t) {
+			op_array = compile_filename(ZEND_REQUIRE, sandboxed_dasm->input.zfilename TSRMLS_CC);
+		}
+		else {
+			op_array = compile_string(sandboxed_dasm->input.compile_string.source, sandboxed_dasm->input.compile_string.eval_name TSRMLS_CC);
+		}
+	} zend_catch {
+		catched = 1;
+	} zend_end_try();
+
+	if (catched || !op_array) {
+#define return_value sandboxed_dasm->output
+		RETVAL_FALSE;
+#undef return_value
+		return NULL;
+	}
+
+	xc_dasm(sandboxed_dasm->output, op_array TSRMLS_CC);
+
+	/* free */
+#ifdef ZEND_ENGINE_2
+	destroy_op_array(op_array TSRMLS_CC);
+#else
+	destroy_op_array(op_array);
+#endif
+	efree(op_array);
+
+	return NULL;
+} /* }}} */
+void xc_dasm_string(zval *output, zval *source TSRMLS_DC) /* {{{ */
+{
+	xc_dasm_sandboxed_t sandboxed_dasm;
+	char *eval_name = zend_make_compiled_string_description("runtime-created function" TSRMLS_CC);
+
+	sandboxed_dasm.output = output;
+	sandboxed_dasm.type = xc_dasm_string_t;
+	sandboxed_dasm.input.compile_string.source = source;
+	sandboxed_dasm.input.compile_string.eval_name = eval_name;
+	xc_sandbox(&xc_dasm_sandboxed, (void *) &sandboxed_dasm, eval_name TSRMLS_CC);
+	efree(eval_name);
+}
+/* }}} */
+void xc_dasm_file(zval *output, const char *filename TSRMLS_DC) /* {{{ */
+{
+	zval *zfilename;
+	xc_dasm_sandboxed_t sandboxed_dasm;
+
+	MAKE_STD_ZVAL(zfilename);
+	zfilename->value.str.val = estrdup(filename);
+	zfilename->value.str.len = strlen(filename);
+	zfilename->type = IS_STRING;
+
+	sandboxed_dasm.output = output;
+	sandboxed_dasm.type = xc_dasm_file_t;
+	sandboxed_dasm.input.zfilename = zfilename;
+	xc_sandbox(&xc_dasm_sandboxed, (void *) &sandboxed_dasm, zfilename->value.str.val TSRMLS_CC);
+
+	zval_dtor(zfilename);
+	FREE_ZVAL(zfilename);
+}
+/* }}} */
+
+/* {{{ proto array xcache_dasm_file(string filename)
+   Disassemble file into opcode array by filename */
+PHP_FUNCTION(xcache_dasm_file)
+{
+	char *filename;
+	int filename_len;
+
+	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &filename, &filename_len) == FAILURE) {
+		return;
+	}
+	if (!filename_len) RETURN_FALSE;
+
+	xc_dasm_file(return_value, filename TSRMLS_CC);
+}
+/* }}} */
+/* {{{ proto array xcache_dasm_string(string code)
+   Disassemble php code into opcode array */
+PHP_FUNCTION(xcache_dasm_string)
+{
+	zval *code;
+
+	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &code) == FAILURE) {
+		return;
+	}
+	xc_dasm_string(return_value, code TSRMLS_CC);
+}
+/* }}} */
Index: /trunk/mod_disassembler/xc_disassembler.h
===================================================================
--- /trunk/mod_disassembler/xc_disassembler.h	(revision 1003)
+++ /trunk/mod_disassembler/xc_disassembler.h	(revision 1003)
@@ -0,0 +1,7 @@
+#include "php.h"
+
+PHP_FUNCTION(xcache_dasm_file);
+PHP_FUNCTION(xcache_dasm_string);
+#define XCACHE_DISASSEMBLER_FUNCTIONS() \
+	PHP_FE(xcache_dasm_file,         NULL) \
+	PHP_FE(xcache_dasm_string,       NULL)
Index: /trunk/mod_optimizer/xc_optimizer.c
===================================================================
--- /trunk/mod_optimizer/xc_optimizer.c	(revision 1003)
+++ /trunk/mod_optimizer/xc_optimizer.c	(revision 1003)
@@ -0,0 +1,618 @@
+#if 0
+#	define XCACHE_DEBUG
+#endif
+
+#include "xcache/xc_utils.h"
+#include "xc_optimizer.h"
+/* the "vector" stack */
+#include "util/xc_stack.h"
+#include "util/xc_trace.h"
+#include "xcache_globals.h"
+
+#ifdef XCACHE_DEBUG
+#error 1
+#	include "xc_processor.h"
+#	include "xcache/xc_const_string.h"
+#	include "ext/standard/php_var.h"
+#endif
+
+#ifdef IS_CV
+#	define XCACHE_IS_CV IS_CV
+#else
+#	define XCACHE_IS_CV 16
+#endif
+
+#ifdef ZEND_ENGINE_2_4
+#	undef Z_OP_CONSTANT
+/* Z_OP_CONSTANT is used before pass_two is applied */
+#	define Z_OP_CONSTANT(op) op_array->literals[op.constant].constant
+#endif
+
+typedef int bbid_t;
+enum {
+	BBID_INVALID = -1
+};
+/* {{{ basic block */
+typedef struct _bb_t {
+	bbid_t     id;
+	zend_bool  used;
+
+	zend_bool  alloc;
+	zend_op   *opcodes;
+	int        count;
+	int        size;
+
+	bbid_t     fall;
+#ifdef ZEND_ENGINE_2
+	bbid_t     catch;
+#endif
+
+	int        opnum; /* opnum after joining basic block */
+} bb_t;
+/* }}} */
+
+/* basic blocks */
+typedef xc_stack_t bbs_t;
+
+/* op array helper functions */
+static int op_array_convert_switch(zend_op_array *op_array) /* {{{ */
+{
+	int i;
+
+	if (op_array->brk_cont_array == NULL) {
+		return SUCCESS;
+	}
+
+	for (i = 0; i < op_array->last; i ++) {
+		zend_op *opline = &op_array->opcodes[i];
+		zend_brk_cont_element *jmp_to;
+		zend_bool can_convert = 1;
+		int array_offset, nest_levels, original_nest_levels;
+
+		switch (opline->opcode) {
+		case ZEND_BRK:
+		case ZEND_CONT:
+			break;
+
+#ifdef ZEND_GOTO
+		case ZEND_GOTO:
+#endif
+			continue;
+
+		default:
+			continue;
+		}
+
+		if (Z_OP_TYPE(opline->op2) != IS_CONST
+		 || Z_OP_CONSTANT(opline->op2).type != IS_LONG) {
+			return FAILURE;
+		}
+
+		nest_levels = Z_OP_CONSTANT(opline->op2).value.lval;
+		original_nest_levels = nest_levels;
+
+		array_offset = Z_OP(opline->op1).opline_num;
+		do {
+			if (array_offset == -1) {
+				/* this is a runtime error in ZE
+				zend_error(E_ERROR, "Cannot break/continue %d level%s", original_nest_levels, (original_nest_levels == 1) ? "" : "s");
+				*/
+				return FAILURE;
+			}
+			jmp_to = &op_array->brk_cont_array[array_offset];
+			if (nest_levels > 1) {
+				zend_op *brk_opline = &op_array->opcodes[jmp_to->brk];
+
+				switch (brk_opline->opcode) {
+				case ZEND_SWITCH_FREE:
+				case ZEND_FREE:
+#ifdef EXT_TYPE_FREE_ON_RETURN
+					if (!(brk_opline->extended_value & EXT_TYPE_FREE_ON_RETURN))
+#endif
+					{
+						can_convert = 0;
+					}
+					break;
+				}
+			}
+			array_offset = jmp_to->parent;
+		} while (--nest_levels > 0);
+
+		if (can_convert) {
+			/* rewrite to jmp */
+			switch (opline->opcode) {
+			case ZEND_BRK:
+				Z_OP(opline->op1).opline_num = jmp_to->brk;
+				break;
+
+			case ZEND_CONT:
+				Z_OP(opline->op1).opline_num = jmp_to->cont;
+				break;
+			}
+			Z_OP_TYPE(opline->op2) = IS_UNUSED;
+			opline->opcode = ZEND_JMP;
+		}
+	}
+
+	return SUCCESS;
+}
+/* }}} */
+/* {{{ op_flowinfo helper func */
+enum {
+	XC_OPNUM_INVALID = -1,
+};
+typedef struct {
+	int       jmpout_op1;
+	int       jmpout_op2;
+	int       jmpout_ext;
+	zend_bool fall;
+} op_flowinfo_t;
+static void op_flowinfo_init(op_flowinfo_t *fi)
+{
+	fi->jmpout_op1 = fi->jmpout_op2 = fi->jmpout_ext = XC_OPNUM_INVALID;
+	fi->fall = 0;
+}
+/* }}} */
+static int op_get_flowinfo(op_flowinfo_t *fi, zend_op *opline) /* {{{ */
+{
+	op_flowinfo_init(fi);
+
+	/* break=will fall */
+	switch (opline->opcode) {
+#ifdef ZEND_HANDLE_EXCEPTION
+	case ZEND_HANDLE_EXCEPTION:
+#endif
+	case ZEND_RETURN:
+	case ZEND_EXIT:
+		return SUCCESS; /* no fall */
+
+	case ZEND_JMP:
+		fi->jmpout_op1 = Z_OP(opline->op1).opline_num;
+		return SUCCESS; /* no fall */
+
+	case ZEND_JMPZNZ:
+		fi->jmpout_op2 = Z_OP(opline->op2).opline_num;
+		fi->jmpout_ext = (int) opline->extended_value;
+		return SUCCESS; /* no fall */
+
+	case ZEND_JMPZ:
+	case ZEND_JMPNZ:
+	case ZEND_JMPZ_EX:
+	case ZEND_JMPNZ_EX:
+#ifdef ZEND_JMP_SET
+	case ZEND_JMP_SET:
+#endif
+#ifdef ZEND_JMP_NO_CTOR
+	case ZEND_JMP_NO_CTOR:
+#endif
+#ifdef ZEND_NEW
+	case ZEND_NEW:
+#endif
+#ifdef ZEND_FE_RESET
+	case ZEND_FE_RESET:
+#endif      
+	case ZEND_FE_FETCH:
+		fi->jmpout_op2 = Z_OP(opline->op2).opline_num;
+		break;
+
+#ifdef ZEND_CATCH
+	case ZEND_CATCH:
+		fi->jmpout_ext = (int) opline->extended_value;
+		break;
+#endif
+
+	default:
+		return FAILURE;
+	}
+
+	fi->fall = 1;
+	return SUCCESS;
+}
+/* }}} */
+#ifdef XCACHE_DEBUG
+static void op_snprint(char *buf, int size, zend_uchar op_type, znode_op *op) /* {{{ */
+{
+	switch (op_type) {
+	case IS_CONST:
+		{
+			zval result;
+			zval *zv = &Z_OP_CONSTANT(*op);
+			TSRMLS_FETCH();
+
+			/* TODO: update for PHP 6 */
+			php_start_ob_buffer(NULL, 0, 1 TSRMLS_CC);
+			php_var_export(&zv, 1 TSRMLS_CC);
+
+			php_ob_get_buffer(&result TSRMLS_CC); 
+			php_end_ob_buffer(0, 0 TSRMLS_CC);
+			snprintf(buf, size, Z_STRVAL(result));
+			zval_dtor(&result);
+		}
+		break;
+
+	case IS_TMP_VAR:
+		snprintf(buf, size, "t@%d", Z_OP(*op).var);
+		break;
+
+	case XCACHE_IS_CV:
+	case IS_VAR:
+		snprintf(buf, size, "v@%d", Z_OP(*op).var);
+		break;
+
+	case IS_UNUSED:
+		if (Z_OP(*op).opline_num) {
+			snprintf(buf, size, "u#%d", Z_OP(op).opline_num);
+		}
+		else {
+			snprintf(buf, size, "-");
+		}
+		break;
+
+	default:
+		snprintf(buf, size, "%d %d", op->op_type, Z_OP(op).var);
+	}
+}
+/* }}} */
+static void op_print(int line, zend_op *first, zend_op *end) /* {{{ */
+{
+	zend_op *opline;
+	for (opline = first; opline < end; opline ++) {
+		char buf_r[20];
+		char buf_1[20];
+		char buf_2[20];
+		op_snprint(buf_r, sizeof(buf_r), Z_OP_TYPE(opline->result), &opline->result);
+		op_snprint(buf_1, sizeof(buf_1), Z_OP_TYPE(opline->op1),    &opline->op1);
+		op_snprint(buf_2, sizeof(buf_2), Z_OP_TYPE(opline->op2),    &opline->op2);
+		fprintf(stderr,
+				"%3d %3d"
+				" %-25s%-5s%-20s%-20s%5lu\r\n"
+				, opline->lineno, opline - first + line
+				, xc_get_opcode(opline->opcode), buf_r, buf_1, buf_2, opline->extended_value);
+	}
+}
+/* }}} */
+#endif
+
+/*
+ * basic block functions
+ */
+
+static bb_t *bb_new_ex(zend_op *opcodes, int count) /* {{{ */
+{
+	bb_t *bb = (bb_t *) ecalloc(sizeof(bb_t), 1);
+
+	bb->fall       = BBID_INVALID;
+#ifdef ZEND_ENGINE_2
+	bb->catch      = BBID_INVALID;
+#endif
+
+	if (opcodes) {
+		bb->alloc   = 0;
+		bb->size    = bb->count = count;
+		bb->opcodes = opcodes;
+	}
+	else {
+		bb->alloc   = 1;
+		bb->size    = bb->count = 8;
+		bb->opcodes = ecalloc(sizeof(zend_op), bb->size);
+	}
+
+	return bb;
+}
+/* }}} */
+#define bb_new() bb_new_ex(NULL, 0)
+static void bb_destroy(bb_t *bb) /* {{{ */
+{
+	if (bb->alloc) {
+		efree(bb->opcodes);
+	}
+	efree(bb);
+}
+/* }}} */
+#ifdef XCACHE_DEBUG
+static void bb_print(bb_t *bb, zend_op *opcodes) /* {{{ */
+{
+	int line = bb->opcodes - opcodes;
+	op_flowinfo_t fi;
+	zend_op *last = bb->opcodes + bb->count - 1;
+	bbid_t catchbbid;
+#ifdef ZEND_ENGINE_2
+	catchbbid = BBID_INVALID;
+#else
+	catchbbid = bb->catch;
+#endif
+
+	op_get_flowinfo(&fi, last);
+
+	fprintf(stderr,
+			"\r\n==== #%-3d cnt:%-3d lno:%-3d"
+			" %c%c"
+			" op1:%-3d op2:%-3d ext:%-3d fal:%-3d cat:%-3d %s ====\r\n"
+			, bb->id, bb->count, bb->alloc ? -1 : line
+			, bb->used ? 'U' : ' ', bb->alloc ? 'A' : ' '
+			, fi.jmpout_op1, fi.jmpout_op2, fi.jmpout_ext, bb->fall, catchbbid, xc_get_opcode(last->opcode)
+			);
+	op_print(line, bb->opcodes, last + 1);
+}
+/* }}} */
+#endif
+
+static bb_t *bbs_get(bbs_t *bbs, int n) /* {{{ */
+{
+	return (bb_t *) xc_stack_get(bbs, n);
+}
+/* }}} */
+static int bbs_count(bbs_t *bbs) /* {{{ */
+{
+	return xc_stack_count(bbs);
+}
+/* }}} */
+static void bbs_destroy(bbs_t *bbs) /* {{{ */
+{
+	bb_t *bb;
+	while (bbs_count(bbs)) {
+		bb = (bb_t *) xc_stack_pop(bbs);
+		bb_destroy(bb);
+	}
+	xc_stack_destroy(bbs);
+}
+/* }}} */
+#ifdef XCACHE_DEBUG
+static void bbs_print(bbs_t *bbs, zend_op *opcodes) /* {{{ */
+{
+	int i;
+	for (i = 0; i < xc_stack_count(bbs); i ++) {
+		bb_print(bbs_get(bbs, i), opcodes);
+	}
+}
+/* }}} */
+#endif
+#define bbs_init(bbs) xc_stack_init_ex(bbs, 16)
+static bb_t *bbs_add_bb(bbs_t *bbs, bb_t *bb) /* {{{ */
+{
+	bb->id = (bbid_t) xc_stack_count(bbs);
+	xc_stack_push(bbs, (void *) bb);
+	return bb;
+}
+/* }}} */
+static bb_t *bbs_new_bb_ex(bbs_t *bbs, zend_op *opcodes, int count) /* {{{ */
+{
+	return bbs_add_bb(bbs, bb_new_ex(opcodes, count));
+}
+/* }}} */
+static int bbs_build_from(bbs_t *bbs, zend_op_array *op_array, int count) /* {{{ */
+{
+	int i, start;
+	bb_t *pbb;
+	bbid_t id;
+	op_flowinfo_t fi;
+	zend_op *opline;
+	ALLOCA_FLAG(use_heap_bbids)
+	ALLOCA_FLAG(use_heap_catchbbids)
+	ALLOCA_FLAG(use_heap_markbbhead)
+	bbid_t *bbids          = my_do_alloca(count * sizeof(bbid_t),    use_heap_bbids);
+#ifdef ZEND_ENGINE_2
+	bbid_t *catchbbids     = my_do_alloca(count * sizeof(bbid_t),    use_heap_catchbbids);
+#endif
+	zend_bool *markbbhead  = my_do_alloca(count * sizeof(zend_bool), use_heap_markbbhead);
+
+	/* {{{ mark jmpin/jumpout */
+	memset(markbbhead,  0, count * sizeof(zend_bool));
+
+	markbbhead[0] = 1;
+	for (i = 0; i < count; i ++) {
+		if (op_get_flowinfo(&fi, &op_array->opcodes[i]) == SUCCESS) {
+			if (fi.jmpout_op1 != XC_OPNUM_INVALID) {
+				markbbhead[fi.jmpout_op1] = 1;
+			}
+			if (fi.jmpout_op2 != XC_OPNUM_INVALID) {
+				markbbhead[fi.jmpout_op2] = 1;
+			}
+			if (fi.jmpout_ext != XC_OPNUM_INVALID) {
+				markbbhead[fi.jmpout_ext] = 1;
+			}
+			if (i + 1 < count) {
+				markbbhead[i + 1] = 1;
+			}
+		}
+	}
+#ifdef ZEND_ENGINE_2
+	/* mark try start */
+	for (i = 0; i < op_array->last_try_catch; i ++) {
+		markbbhead[op_array->try_catch_array[i].try_op] = 1;
+	}
+#endif
+	/* }}} */
+	/* {{{ fill op lines with newly allocated id */
+	for (i = 0; i < count; i ++) {
+		bbids[i] = BBID_INVALID;
+	}
+
+	id = -1;
+	for (i = 0; i < count; i ++) {
+		if (markbbhead[i]) {
+			id ++;
+		}
+		bbids[i] = id;
+		TRACE("bbids[%d] = %d", i, id);
+	}
+	/* }}} */
+#ifdef ZEND_ENGINE_2
+	/* {{{ fill op lines with catch id */
+	for (i = 0; i < count; i ++) {
+		catchbbids[i] = BBID_INVALID;
+	}
+
+	for (i = 0; i < op_array->last_try_catch; i ++) {
+		int j;
+		zend_try_catch_element *e = &op_array->try_catch_array[i];
+		for (j = e->try_op; j < e->catch_op; j ++) {
+			catchbbids[j] = bbids[e->catch_op];
+		}
+	}
+#ifdef XCACHE_DEBUG
+	for (i = 0; i < count; i ++) {
+		TRACE("catchbbids[%d] = %d", i, catchbbids[i]);
+	}
+#endif
+	/* }}} */
+#endif
+	/* {{{ create basic blocks */
+	start = 0;
+	id = 0;
+	/* loop over to deal with the last block */
+	for (i = 1; i <= count; i ++) {
+		if (i < count && id == bbids[i]) {
+			continue;
+		}
+
+		opline = op_array->opcodes + start;
+		pbb = bbs_new_bb_ex(bbs, opline, i - start);
+#ifdef ZEND_ENGINE_2
+		pbb->catch = catchbbids[start];
+#endif
+
+		/* last */
+		opline = pbb->opcodes + pbb->count - 1;
+		if (op_get_flowinfo(&fi, opline) == SUCCESS) {
+			if (fi.jmpout_op1 != XC_OPNUM_INVALID) {
+				Z_OP(opline->op1).opline_num = bbids[fi.jmpout_op1];
+				assert(Z_OP(opline->op1).opline_num != BBID_INVALID);
+			}
+			if (fi.jmpout_op2 != XC_OPNUM_INVALID) {
+				Z_OP(opline->op2).opline_num = bbids[fi.jmpout_op2];
+				assert(Z_OP(opline->op2).opline_num != BBID_INVALID);
+			}
+			if (fi.jmpout_ext != XC_OPNUM_INVALID) {
+				opline->extended_value = bbids[fi.jmpout_ext];
+				assert(opline->extended_value != BBID_INVALID);
+			}
+			if (fi.fall && i + 1 < count) {
+				pbb->fall = bbids[i + 1];
+				TRACE("fall %d", pbb->fall);
+				assert(pbb->fall != BBID_INVALID);
+			}
+		}
+		if (i >= count) {
+			break;
+		}
+		start = i;
+		id = bbids[i];
+	}
+	/* }}} */
+
+	my_free_alloca(markbbhead, use_heap_markbbhead);
+#ifdef ZEND_ENGINE_2
+	my_free_alloca(catchbbids, use_heap_catchbbids);
+#endif
+	my_free_alloca(bbids,      use_heap_bbids);
+	return SUCCESS;
+}
+/* }}} */
+static void bbs_restore_opnum(bbs_t *bbs, zend_op_array *op_array) /* {{{ */
+{
+	int i;
+#ifdef ZEND_ENGINE_2
+	bbid_t lasttrybbid;
+	bbid_t lastcatchbbid;
+#endif
+
+	for (i = 0; i < bbs_count(bbs); i ++) {
+		op_flowinfo_t fi;
+		bb_t *bb = bbs_get(bbs, i);
+		zend_op *last = bb->opcodes + bb->count - 1;
+
+		if (op_get_flowinfo(&fi, last) == SUCCESS) {
+			if (fi.jmpout_op1 != XC_OPNUM_INVALID) {
+				Z_OP(last->op1).opline_num = bbs_get(bbs, fi.jmpout_op1)->opnum;
+				assert(Z_OP(last->op1).opline_num != BBID_INVALID);
+			}
+			if (fi.jmpout_op2 != XC_OPNUM_INVALID) {
+				Z_OP(last->op2).opline_num = bbs_get(bbs, fi.jmpout_op2)->opnum;
+				assert(Z_OP(last->op2).opline_num != BBID_INVALID);
+			}
+			if (fi.jmpout_ext != XC_OPNUM_INVALID) {
+				last->extended_value = bbs_get(bbs, fi.jmpout_ext)->opnum;
+				assert(last->extended_value != BBID_INVALID);
+			}
+		}
+	}
+
+#ifdef ZEND_ENGINE_2
+	lasttrybbid   = BBID_INVALID;
+	lastcatchbbid = BBID_INVALID;
+	op_array->last_try_catch = 0;
+	for (i = 0; i < bbs_count(bbs); i ++) {
+		bb_t *bb = bbs_get(bbs, i);
+
+		if (lastcatchbbid != bb->catch) {
+			if (lasttrybbid != BBID_INVALID && lastcatchbbid != BBID_INVALID) {
+				int try_catch_offset = op_array->last_try_catch ++;
+
+				op_array->try_catch_array = erealloc(op_array->try_catch_array, sizeof(zend_try_catch_element) * op_array->last_try_catch);
+				op_array->try_catch_array[try_catch_offset].try_op = bbs_get(bbs, lasttrybbid)->opnum;
+				op_array->try_catch_array[try_catch_offset].catch_op = bbs_get(bbs, lastcatchbbid)->opnum;
+			}
+			lasttrybbid   = i;
+			lastcatchbbid = bb->catch;
+		}
+	}
+	/* it is impossible to have last bb catched */
+#endif
+}
+/* }}} */
+
+/*
+ * optimize
+ */
+static int xc_optimize_op_array(zend_op_array *op_array TSRMLS_DC) /* {{{ */
+{
+	bbs_t bbs;
+
+	if (op_array->type != ZEND_USER_FUNCTION) {
+		return 0;
+	}
+
+#ifdef XCACHE_DEBUG
+#	if 0
+	TRACE("optimize file: %s", op_array->filename);
+	xc_dprint_zend_op_array(op_array, 0 TSRMLS_CC);
+#	endif
+	op_print(0, op_array->opcodes, op_array->opcodes + op_array->last);
+#endif
+
+	if (op_array_convert_switch(op_array) == SUCCESS) {
+		bbs_init(&bbs);
+		if (bbs_build_from(&bbs, op_array, op_array->last) == SUCCESS) {
+			int i;
+#ifdef XCACHE_DEBUG
+			bbs_print(&bbs, op_array->opcodes);
+#endif
+			/* TODO: calc opnum after basic block move */
+			for (i = 0; i < bbs_count(&bbs); i ++) {
+				bb_t *bb = bbs_get(&bbs, i);
+				bb->opnum = bb->opcodes - op_array->opcodes;
+			}
+			bbs_restore_opnum(&bbs, op_array);
+		}
+		bbs_destroy(&bbs);
+	}
+
+#ifdef XCACHE_DEBUG
+#	if 0
+	TRACE("%s", "after compiles");
+	xc_dprint_zend_op_array(op_array, 0 TSRMLS_CC);
+#	endif
+	op_print(0, op_array->opcodes, op_array->opcodes + op_array->last);
+#endif
+	return 0;
+}
+/* }}} */
+void xc_optimizer_op_array_handler(zend_op_array *op_array) /* {{{ */
+{
+	TSRMLS_FETCH();
+	if (XG(optimizer)) {
+		xc_optimize_op_array(op_array TSRMLS_CC);
+	}
+}
+/* }}} */
Index: /trunk/mod_optimizer/xc_optimizer.h
===================================================================
--- /trunk/mod_optimizer/xc_optimizer.h	(revision 1003)
+++ /trunk/mod_optimizer/xc_optimizer.h	(revision 1003)
@@ -0,0 +1,5 @@
+#include "php.h"
+#include "xcache.h"
+
+void xc_optimizer_op_array_handler(zend_op_array *op_array);
+#define XCACHE_OPTIMIZER_FUNCTIONS()
Index: /trunk/xcache.c
===================================================================
--- /trunk/xcache.c	(revision 1002)
+++ /trunk/xcache.c	(revision 1003)
@@ -29,15 +29,15 @@
 
 #ifdef HAVE_XCACHE_OPTIMIZER
-#	include "submodules/xc_optimizer.h"
+#	include "mod_optimizer/xc_optimizer.h"
 #else
 #	define XCACHE_OPTIMIZER_FUNCTIONS()
 #endif
 #ifdef HAVE_XCACHE_COVERAGER
-#	include "submodules/xc_coverager.h"
+#	include "mod_coverager/xc_coverager.h"
 #else
 #	define XCACHE_COVERAGER_FUNCTIONS()
 #endif
 #ifdef HAVE_XCACHE_DISASSEMBLER
-#	include "submodules/xc_disassembler.h"
+#	include "mod_disassembler/xc_disassembler.h"
 #else
 #	define XCACHE_DISASSEMBLER_FUNCTIONS()
Index: /trunk/xcache/xc_processor.c
===================================================================
--- /trunk/xcache/xc_processor.c	(revision 1002)
+++ /trunk/xcache/xc_processor.c	(revision 1003)
@@ -1,1 +1,1 @@
-#include "../xc_processor.c.h"
+#include "xc_processor.c.h"
Index: /trunk/xcache/xc_utils.c
===================================================================
--- /trunk/xcache/xc_utils.c	(revision 1002)
+++ /trunk/xcache/xc_utils.c	(revision 1003)
@@ -7,5 +7,5 @@
 #endif
 #include "xc_opcode_spec.h"
-#include "../util/xc_trace.h"
+#include "util/xc_trace.h"
 
 #ifndef max
