├── examples ├── includes.php ├── check_eval.php ├── disassemble_simple.php ├── disassemble_labels.php ├── scan_eval.php ├── php2dot_simple.php ├── check_include.php └── php2dot_loops.php ├── config.m4 ├── config.h.in ├── LICENSE ├── php_bytekit.h ├── bytekit.c └── run-tests.php /examples/includes.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /config.m4: -------------------------------------------------------------------------------- 1 | dnl $Id$ 2 | dnl config.m4 for extension bytekit 3 | 4 | PHP_ARG_ENABLE(bytekit, whether to enable ByteKit support, 5 | [ --enable-bytekit Enable ByteKit support]) 6 | 7 | if test "$PHP_BYTEKIT" != "no"; then 8 | AC_DEFINE(HAVE_BYTEKIT, 1, [ ByteKit ]) 9 | PHP_NEW_EXTENSION(bytekit, bytekit.c, $ext_shared) 10 | fi 11 | -------------------------------------------------------------------------------- /config.h.in: -------------------------------------------------------------------------------- 1 | /* config.h.in. Generated automatically from configure.in by autoheader 2.13. */ 2 | 3 | /* Define if your C compiler doesn't accept -c and -o together. */ 4 | #undef NO_MINUS_C_MINUS_O 5 | 6 | /* Define if you have the header file. */ 7 | #undef HAVE_DLFCN_H 8 | 9 | /* ByteKit */ 10 | #undef HAVE_BYTEKIT 11 | 12 | /* Whether to build bytekit as dynamic module */ 13 | #undef COMPILE_DL_BYTEKIT 14 | 15 | -------------------------------------------------------------------------------- /examples/check_eval.php: -------------------------------------------------------------------------------- 1 | &$func) { 7 | $c = &$func['code']; 8 | for ($i=0; $i -------------------------------------------------------------------------------- /examples/disassemble_simple.php: -------------------------------------------------------------------------------- 1 | &$function) { 11 | 12 | echo $functionname, ":\n"; 13 | for ($i=0; $i 0 ? ", " : "") . $c['operands'][$j]['string']; 19 | } 20 | 21 | echo " ", str_pad($c['mnemonic'], 32, " ", STR_PAD_RIGHT), $operands; 22 | echo "\n"; 23 | } 24 | echo "\n"; 25 | } 26 | ?> -------------------------------------------------------------------------------- /examples/disassemble_labels.php: -------------------------------------------------------------------------------- 1 | &$function) { 11 | $jumptargets = array(); 12 | for ($i=0; $i 0 ? ", " : "") . $c['operands'][$j]['string']; 33 | } 34 | 35 | echo " ", str_pad($c['mnemonic'], 32, " ", STR_PAD_RIGHT), $operands; 36 | echo "\n"; 37 | } 38 | echo "\n"; 39 | } 40 | ?> -------------------------------------------------------------------------------- /examples/scan_eval.php: -------------------------------------------------------------------------------- 1 | isFile()) { 14 | $relName = $file->getPathName(); 15 | if (preg_match('#^.*\.php$#', $relName, $res)) { 16 | 17 | $found = array(); 18 | 19 | $res = bytekit_disassemble_file($relName); 20 | foreach ($res['functions'] as $key => &$func) { 21 | $c = &$func['code']; 22 | for ($i=0; $i -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------- 2 | The Bytekit License, version 1.0 3 | (Based on "The PHP License", version 3.0) 4 | Copyright (c) 2008-2009 SektionEins GmbH. All rights reserved. 5 | -------------------------------------------------------------------- 6 | 7 | Redistribution and use in source and binary forms, with or without 8 | modification, is permitted provided that the following conditions 9 | are met: 10 | 11 | 1. Redistributions of source code must retain the above copyright 12 | notice, this list of conditions and the following disclaimer. 13 | 14 | 2. Redistributions in binary form must reproduce the above copyright 15 | notice, this list of conditions and the following disclaimer in 16 | the documentation and/or other materials provided with the 17 | distribution. 18 | 19 | 3. The name "Bytekit" must not be used to endorse or promote products 20 | derived from this software without prior written permission. For 21 | written permission, please contact license@bytekit.org. 22 | 23 | 4. Products derived from this software may not be called "Bytekit", nor 24 | may "Bytekit" appear in their name, without prior written permission 25 | from license@bytekit.org. 26 | 27 | 5. SektionEins GmbH may publish revised and/or new versions of the 28 | license from time to time. Each version will be given a 29 | distinguishing version number. Once covered code has been 30 | published under a particular version of the license, you may 31 | always continue to use it under the terms of that version. You 32 | may also choose to use such covered code under the terms of any 33 | subsequent version of the license published by SektionEins GmbH. 34 | No one other than SektionEins GmbH has the right to modify the 35 | terms applicable to covered code created under this License. 36 | 37 | 6. Redistributions of any form whatsoever must retain the following 38 | acknowledgment: "This product includes Bytekit, freely available 39 | from ". 40 | 41 | THIS SOFTWARE IS PROVIDED BY SEKTIONEINS GMBH ``AS IS'' AND ANY 42 | EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 43 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 44 | PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE PHP DEVELOPMENT TEAM OR 45 | ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 46 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 47 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 48 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 49 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 50 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 51 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 52 | -------------------------------------------------------------------- 53 | -------------------------------------------------------------------------------- /examples/php2dot_simple.php: -------------------------------------------------------------------------------- 1 | $function) { 21 | $f_i++; 22 | 23 | $nodes = array(); 24 | 25 | $filefname = str_replace("/", "_", $functionname); 26 | 27 | $fout = fopen($outname."/".$filefname.".dot", "w+"); 28 | if (!$fout) die(); 29 | fprintf($fout, "digraph flowgraph { 30 | node [ 31 | fontname=\"Courier\" 32 | fontsize=\"12\" 33 | shape=\"plaintext\" 34 | ]; 35 | graph [ 36 | rankdir=\"HR\" 37 | bgcolor=\"#f7f7f7\" 38 | label=\"Control Flow Graph for $functionname()\" 39 | labeljust=\"c\" 40 | labelloc=\"t\" 41 | fontname=\"Courier\" 42 | fontsize=\"16\" 43 | ]; 44 | mindist = 0.4; 45 | overlap = false; 46 | "); 47 | 48 | $code = &$function['code']; 49 | $bb = &$function['bb']; 50 | $bbid = -1; 51 | for ($i=0; $i $value) { 75 | $bgcolor = "#FF8888"; 76 | 77 | fprintf($fout, '"bb_%d" [', $key); 78 | fprintf($fout, "\n label =<\n"); 79 | fprintf($fout, "\n", sprintf("%08x",$nodes[$key]['address'])); 81 | foreach($nodes[$key]['instructions'] as $instr) { 82 | fprintf($fout, "\n", 83 | htmlentities(sprintf("%08x",$instr['address'])), 84 | htmlentities($instr['mnemonic']), 85 | htmlentities($instr['operands'])); 86 | } 87 | fprintf($fout, "
%s:
%s%s%s
>\n"); 88 | fprintf($fout, "];\n"); 89 | } 90 | 91 | $cfg = &$function['cfg']; 92 | 93 | for ($i=1; $i<=count($cfg); $i++) { 94 | if (count($cfg[$i])==0) continue; 95 | foreach ($cfg[$i] as $key => $value) { 96 | if ($nodes[$i] == false) continue; 97 | 98 | switch ($value) { 99 | case BYTEKIT_EDGE_TRUE: $style = 'color="#00ff00"'; break; 100 | case BYTEKIT_EDGE_FALSE: $style = 'color="#ff0000"'; break; 101 | case BYTEKIT_EDGE_NORMAL: $style = 'color="#000000"'; break; 102 | case BYTEKIT_EDGE_EXCEPTION: $style = 'style=dotted, penwidth=3.0, color="#0000ff"';break; 103 | default: $style = 'color="#0000ff"'; 104 | } 105 | fprintf($fout, '"bb_%d" -> "bb_%d" [%s];'."\n", $i, $key, $style); 106 | } 107 | } 108 | 109 | fprintf($fout, "}\n"); 110 | fclose($fout); 111 | system("dot -Tsvg -o $outname/$filefname.svg $outname/$filefname.dot"); 112 | } -------------------------------------------------------------------------------- /examples/check_include.php: -------------------------------------------------------------------------------- 1 | &$func) { 13 | $r = &$func['raw']; 14 | $c = &$func['code']; 15 | $o = &$r['opcodes']; 16 | 17 | /* Find all INCLUDE_OR_EVAL opcodes */ 18 | for ($i=0; $i 0) { 25 | if ($start_bb != bytekit_opline_to_bb($func, $end)) break; 26 | $end--; 27 | } 28 | 29 | $is_safe = bytekit_trace_backward($func, $start-1, $end, $o[$i]['op1']); 30 | 31 | if ($is_safe == BYTEKIT_IS_UNSAFE) { 32 | $found[] = $o[$i]['lineno']; 33 | } 34 | } 35 | } 36 | } 37 | 38 | if (count($found) == 0) die(); 39 | 40 | sort($found); 41 | 42 | $fileData = file($fname); 43 | 44 | for ($i=0; $i= $end) 76 | { 77 | $ol = &$o[$i]; 78 | $opcode = $ol['opcode']; 79 | 80 | switch ($opcode) { 81 | case BYTEKIT_INCLUDE_OR_EVAL: 82 | if (bytekit_equal_node($s, $ol['result'])) { 83 | return BYTEKIT_IS_UNSAFE; 84 | } 85 | break; 86 | case BYTEKIT_FETCH_CONSTANT: 87 | if (bytekit_equal_node($s, $ol['result'])) { 88 | return BYTEKIT_IS_SAFE; 89 | } 90 | break; 91 | case BYTEKIT_ASSIGN: 92 | if (bytekit_equal_node($s, $ol['op1'])) { 93 | return bytekit_trace_backward($func, $i-1, $end, $ol['op2']); 94 | } 95 | break; 96 | case BYTEKIT_ASSIGN_CONCAT: 97 | if (bytekit_equal_node($s, $ol['op1'])) { 98 | $res1 = bytekit_trace_backward($func, $i-1, $end, $ol['op1']) == BYTEKIT_IS_SAFE; 99 | $res2 = bytekit_trace_backward($func, $i-1, $end, $ol['op2']) == BYTEKIT_IS_SAFE; 100 | return ($res1 && $res2) ? BYTEKIT_IS_SAFE : BYTEKIT_IS_UNSAFE; 101 | } 102 | break; 103 | case BYTEKIT_CONCAT: 104 | if (bytekit_equal_node($s, $ol['result'])) { 105 | $res1 = bytekit_trace_backward($func, $i-1, $end, $ol['op1']) == BYTEKIT_IS_SAFE; 106 | $res2 = bytekit_trace_backward($func, $i-1, $end, $ol['op2']) == BYTEKIT_IS_SAFE; 107 | return ($res1 && $res2) ? BYTEKIT_IS_SAFE : BYTEKIT_IS_UNSAFE; 108 | } 109 | break; 110 | default: return BYTEKIT_IS_UNSAFE; 111 | } 112 | $i--; 113 | } 114 | 115 | return BYTEKIT_IS_UNSAFE; 116 | } 117 | ?> -------------------------------------------------------------------------------- /examples/php2dot_loops.php: -------------------------------------------------------------------------------- 1 | $function) { 21 | $f_i++; 22 | 23 | $dc = eliminateDeadCode($function); 24 | 25 | $nodes = array(); 26 | 27 | $filefname = str_replace("/", "_", $functionname); 28 | 29 | $fout = fopen($outname."/".$filefname.".dot", "w+"); 30 | if (!$fout) die(); 31 | fprintf($fout, "digraph flowgraph { 32 | node [ 33 | fontname=\"Courier\" 34 | fontsize=\"12\" 35 | shape=\"plaintext\" 36 | ]; 37 | graph [ 38 | rankdir=\"HR\" 39 | bgcolor=\"#f7f7f7\" 40 | label=\"Control Flow Graph for $functionname()\" 41 | labeljust=\"c\" 42 | labelloc=\"t\" 43 | fontname=\"Courier\" 44 | fontsize=\"16\" 45 | ]; 46 | mindist = 0.4; 47 | overlap = false; 48 | "); 49 | 50 | $code = &$function['code']; 51 | $bb = &$function['bb']; 52 | $bbid = -1; 53 | for ($i=0; $i $value) { 79 | $bgcolor = "#FF8888"; 80 | 81 | fprintf($fout, '"bb_%d" [', $key); 82 | fprintf($fout, "\n label =<\n"); 83 | fprintf($fout, "\n", sprintf("%08x",$nodes[$key]['address'])); 85 | foreach($nodes[$key]['instructions'] as $instr) { 86 | fprintf($fout, "\n", 87 | htmlentities(sprintf("%08x",$instr['address'])), 88 | htmlentities($instr['mnemonic']), 89 | htmlentities($instr['operands'])); 90 | } 91 | fprintf($fout, "
%s:
%s%s%s
>\n"); 92 | fprintf($fout, "];\n"); 93 | } 94 | 95 | $cfg = &$function['cfg']; 96 | 97 | $doms = createDominatorTree($cfg); 98 | 99 | $loopEdge = array(); 100 | $dfsOrder = array(); 101 | $dfsId = 1; 102 | $depths = array(); 103 | detectLoops($cfg, 1, $dummy=array(), 1); 104 | 105 | for ($i=1; $i<=count($cfg); $i++) { 106 | if (count($cfg[$i])==0) continue; 107 | foreach ($cfg[$i] as $key => $value) { 108 | if ($nodes[$i] == false) continue; 109 | 110 | switch ($value) { 111 | case BYTEKIT_EDGE_TRUE: $style = 'color="#00ff00"'; break; 112 | case BYTEKIT_EDGE_FALSE: $style = 'color="#ff0000"'; break; 113 | case BYTEKIT_EDGE_NORMAL: $style = 'color="#000000"'; break; 114 | case BYTEKIT_EDGE_EXCEPTION: $style = 'style=dotted, penwidth=3.0, color="#0000ff"';break; 115 | default: $style = 'color="#0000ff"'; 116 | } 117 | if (isBackEdge($cfg, $doms, $i, $key)) { 118 | $style = 'style=dashed, penwidth=3.0, ' . $style; 119 | } 120 | fprintf($fout, '"bb_%d" -> "bb_%d" [%s];'."\n", $i, $key, $style); 121 | } 122 | } 123 | 124 | fprintf($fout, "}\n"); 125 | fclose($fout); 126 | system("dot -Tsvg -o $outname/$filefname.svg $outname/$filefname.dot"); 127 | } 128 | 129 | function eliminateDeadCode(&$f) 130 | { 131 | $cfg = &$f['cfg']; 132 | // var_dump($cfg); 133 | $dcfound = false; 134 | $dc = array(); 135 | do { 136 | $in = array(); 137 | for ($i=1; $i<=count($cfg); $i++) { 138 | $in[$i] = 0; 139 | } 140 | for ($i=1; $i<=count($cfg); $i++) { 141 | foreach ($cfg[$i] as $child => $value) { 142 | $in[$child]++; 143 | } 144 | } 145 | $dcfound = false; 146 | for ($i=2; $i<=count($cfg); $i++) { 147 | if ($in[$i] == 0 && !in_array($i, $dc)) { 148 | $cfg[$i] = array(); 149 | $dcfound = true; 150 | $dc[] = $i; 151 | } 152 | } 153 | } while ($dcfound == true); 154 | //var_dump($dc); 155 | return $dc; 156 | } 157 | 158 | function isBackEdge(&$cfg, &$doms, $n1, $n2) 159 | { 160 | $postorderMap = createPostorderMapping($cfg); 161 | 162 | if ($n2 == 1) return true; 163 | if ($n1 == $n2) return true; 164 | $n1 = $postorderMap[$n1]; 165 | $n2 = $postorderMap[$n2]; 166 | 167 | while ($doms[$n1] != $n1) { 168 | if ($doms[$n1] == $n2) return true; 169 | $n1 = $doms[$n1]; 170 | } 171 | return false; 172 | } 173 | 174 | function detectLoops(&$cfg, $node, &$visited, $depth) 175 | { 176 | global $dfsOrder, $dfsId, $visitedBy, $depths; 177 | 178 | $visited[$node] = 1; 179 | $dfsOrder[$node] = $dfsId++; 180 | $depths[$node] = $depth; 181 | 182 | $childs = $cfg[$node]; 183 | if (count($childs)==0) return; 184 | foreach ($childs as $cnode => $dummy) { 185 | if (isset($visited[$cnode])) { 186 | if ($depths[$cnode] < $depths[$node]) { 187 | $GLOBALS['loopEdge'][$node][$cnode] = 1; 188 | } 189 | } else { 190 | $visitedBy[$cnode] = $dfsOrder[$node]; 191 | detectLoops($cfg, $cnode, $visited, $depth+1); 192 | } 193 | } 194 | } 195 | 196 | function createPredecessorArray(&$cfg) 197 | { 198 | $predecessors = array(); 199 | foreach ($cfg as $key => $value) { 200 | $predecessors[$key] = array(); 201 | } 202 | /* build up predecessors */ 203 | foreach ($cfg as $key => $node) { 204 | foreach ($node as $cnode => $dummy) { 205 | $predecessors[$cnode][] = $key; 206 | } 207 | } 208 | return $predecessors; 209 | } 210 | 211 | function do_postorder(&$cfg, &$visited, &$postorder, $node) 212 | { 213 | $succ = $cfg[$node]; 214 | $visited[$node] = true; 215 | 216 | foreach ($succ as $cnode => $dummy) { 217 | if (!isset($visited[$cnode])) { 218 | do_postorder($cfg, $visited, $postorder, $cnode); 219 | } 220 | } 221 | $postorder[] = $node; 222 | } 223 | 224 | function createReversePostorder(&$cfg) 225 | { 226 | $postorder = array(); 227 | $visited = array(); 228 | 229 | do_postorder($cfg, $visited, $postorder, 1); 230 | 231 | return array_reverse($postorder); 232 | } 233 | 234 | function createPostorderMapping(&$cfg) 235 | { 236 | $postorder = array(); 237 | $visited = array(); 238 | $mapping = array(); 239 | 240 | do_postorder($cfg, $visited, $postorder, 1); 241 | 242 | for ($i=0; $i $value) { 256 | if (isset($postorderMap[$key])) $doms[$postorderMap[$key]] = null; 257 | } 258 | 259 | /* Initialize algorithm */ 260 | $start_node = $postorderMap[1]; 261 | $doms[$start_node] = $start_node; 262 | $changed = true; 263 | $predecessors = createPredecessorArray($cfg); 264 | $reversePostorder = createReversePostorder($cfg); 265 | 266 | while ($changed) { 267 | $changed = false; 268 | foreach ($reversePostorder as $b) { 269 | if ($b == 1) continue; 270 | //echo "now: $b\n"; 271 | $new_idom = null; 272 | //var_dump($predecessors[$b]); 273 | for ($i=0; $i | 16 | +----------------------------------------------------------------------+ 17 | */ 18 | 19 | /* $Id$ */ 20 | 21 | #ifndef PHP_BYTEKIT_H 22 | #define PHP_BYTEKIT_H 23 | 24 | #define BYTEKIT_VERSION "0.1.1" 25 | 26 | #ifdef ZTS 27 | #include "TSRM.h" 28 | #endif 29 | 30 | extern zend_module_entry bytekit_module_entry; 31 | #define phpext_bytekit_ptr &bytekit_module_entry 32 | 33 | PHP_MINIT_FUNCTION(bytekit); 34 | PHP_MSHUTDOWN_FUNCTION(bytekit); 35 | PHP_MINFO_FUNCTION(bytekit); 36 | 37 | PHP_FUNCTION(bytekit_disassemble_file); 38 | 39 | ZEND_BEGIN_MODULE_GLOBALS(bytekit) 40 | zval *compile_errors; 41 | zend_bool in_error_cb; 42 | 43 | zend_op *op_array_opcodes; 44 | char *opcodes_base; 45 | ZEND_END_MODULE_GLOBALS(bytekit) 46 | 47 | #ifdef ZTS 48 | #define BYTEKIT_G(v) TSRMG(bytekit_globals_id, zend_bytekit_globals *, v) 49 | #else 50 | #define BYTEKIT_G(v) (bytekit_globals.v) 51 | #endif 52 | 53 | /* Compatibility */ 54 | #ifndef ZEND_ISSET_ISEMPTY_MASK 55 | #define ZEND_ISSET_ISEMPTY_MASK (ZEND_ISSET | ZEND_ISEMPTY) 56 | #endif 57 | 58 | typedef struct _bytekit_define_list { 59 | long val; 60 | char *str; 61 | long flags; /* flags for first opline */ 62 | long flags2; /* flags for second opline */ 63 | } bytekit_define_list; 64 | 65 | 66 | #define BYTEKIT_UNKNOWN "UNKNOWN" 67 | 68 | /* Operand Flags */ 69 | #define BYTEKIT_SRC_MASK 15 70 | #define BYTEKIT_SRC_RES1 1 71 | #define BYTEKIT_SRC_RES2 2 72 | #define BYTEKIT_SRC_OP1 3 73 | #define BYTEKIT_SRC_OP2 4 74 | #define BYTEKIT_SRC_OP3 5 75 | #define BYTEKIT_SRC_OP4 6 76 | #define BYTEKIT_SRC_EXT1 7 77 | #define BYTEKIT_SRC_EXT2 8 78 | 79 | #define BYTEKIT_OPERAND_WRITTEN (1 << 4) 80 | 81 | /* Operand Types */ 82 | #define BYTEKIT_TYPE_VALUE 1 83 | #define BYTEKIT_TYPE_CV 2 84 | #define BYTEKIT_TYPE_VAR 3 85 | #define BYTEKIT_TYPE_TMP_VAR 4 86 | #define BYTEKIT_TYPE_SYMBOL 5 87 | 88 | /* Edge Types */ 89 | #define BYTEKIT_EDGE_TRUE 0 90 | #define BYTEKIT_EDGE_FALSE 1 91 | #define BYTEKIT_EDGE_NORMAL 2 92 | #define BYTEKIT_EDGE_EXCEPTION 3 93 | 94 | /* Fake Opcodes */ 95 | 96 | #if PHP_VERSION_ID < 50300 97 | #define ZEND_GOTO 222 98 | #define ZEND_INIT_NS_FCALL_BY_NAME 222 99 | #define ZEND_DECLARE_CONST 222 100 | #define ZEND_DECLARE_INHERITED_CLASS_DELAYED 222 101 | #define ZEND_JMP_SET 222 102 | #define ZEND_DECLARE_LAMBDA_FUNCTION 222 103 | #endif 104 | 105 | /* Disassemble File Flags */ 106 | 107 | #define BYTEKIT_SHOW_SPECIALS 1 108 | 109 | /* Opcode Flags */ 110 | 111 | #define BYTEKIT_OP_TYPE_MASK 7 112 | #define BYTEKIT_OP_WRITE 8 113 | #define BYTEKIT_OP_HIDE 16 114 | #define BYTEKIT_OP_USED (BYTEKIT_OP_TYPE_MASK | BYTEKIT_OP_WRITE) 115 | 116 | #define BYTEKIT_OP_CVAR 1 117 | #define BYTEKIT_OP_TVAR 2 118 | #define BYTEKIT_OP_NUM 3 119 | #define BYTEKIT_OP_OPNUM 4 120 | #define BYTEKIT_OP_JMPADDR 5 121 | 122 | #define BYTEKIT_RES_SHIFT 0 123 | #define BYTEKIT_OP1_SHIFT 5 124 | #define BYTEKIT_OP2_SHIFT 10 125 | #define BYTEKIT_EXT_SHIFT 15 126 | #define BYTEKIT_TYPE_SHIFT 20 127 | 128 | #define BYTEKIT_OP_STOP ( 1 << BYTEKIT_TYPE_SHIFT ) 129 | 130 | #define BYTEKIT_RES_TYPE_MASK ( BYTEKIT_OP_TYPE_MASK << BYTEKIT_RES_SHIFT ) 131 | #define BYTEKIT_RES_WRITE ( BYTEKIT_OP_WRITE << BYTEKIT_RES_SHIFT ) 132 | #define BYTEKIT_RES_HIDE ( BYTEKIT_OP_HIDE << BYTEKIT_RES_SHIFT ) 133 | #define BYTEKIT_RES_USED ( BYTEKIT_OP_USED << BYTEKIT_RES_SHIFT ) 134 | #define BYTEKIT_RES_CVAR ( BYTEKIT_OP_CVAR << BYTEKIT_RES_SHIFT ) 135 | #define BYTEKIT_RES_TVAR ( BYTEKIT_OP_TVAR << BYTEKIT_RES_SHIFT ) 136 | #define BYTEKIT_RES_NUM ( BYTEKIT_OP_NUM << BYTEKIT_RES_SHIFT ) 137 | #define BYTEKIT_RES_OPNUM ( BYTEKIT_OP_OPNUM << BYTEKIT_RES_SHIFT ) 138 | #define BYTEKIT_RES_JMPADDR ( BYTEKIT_OP_JMPADDR << BYTEKIT_RES_SHIFT ) 139 | 140 | #define BYTEKIT_RES_FORCE 0x80000000 141 | 142 | #define BYTEKIT_OP1_TYPE_MASK ( BYTEKIT_OP_TYPE_MASK << BYTEKIT_OP1_SHIFT ) 143 | #define BYTEKIT_OP1_WRITE ( BYTEKIT_OP_WRITE << BYTEKIT_OP1_SHIFT ) 144 | #define BYTEKIT_OP1_HIDE ( BYTEKIT_OP_HIDE << BYTEKIT_OP1_SHIFT ) 145 | #define BYTEKIT_OP1_USED ( BYTEKIT_OP_USED << BYTEKIT_OP1_SHIFT ) 146 | #define BYTEKIT_OP1_CVAR ( BYTEKIT_OP_CVAR << BYTEKIT_OP1_SHIFT ) 147 | #define BYTEKIT_OP1_TVAR ( BYTEKIT_OP_TVAR << BYTEKIT_OP1_SHIFT ) 148 | #define BYTEKIT_OP1_NUM ( BYTEKIT_OP_NUM << BYTEKIT_OP1_SHIFT ) 149 | #define BYTEKIT_OP1_OPNUM ( BYTEKIT_OP_OPNUM << BYTEKIT_OP1_SHIFT ) 150 | #define BYTEKIT_OP1_JMPADDR ( BYTEKIT_OP_JMPADDR << BYTEKIT_OP1_SHIFT ) 151 | 152 | #define BYTEKIT_OP2_TYPE_MASK ( BYTEKIT_OP_TYPE_MASK << BYTEKIT_OP2_SHIFT ) 153 | #define BYTEKIT_OP2_WRITE ( BYTEKIT_OP_WRITE << BYTEKIT_OP2_SHIFT ) 154 | #define BYTEKIT_OP2_HIDE ( BYTEKIT_OP_HIDE << BYTEKIT_OP2_SHIFT ) 155 | #define BYTEKIT_OP2_USED ( BYTEKIT_OP_USED << BYTEKIT_OP2_SHIFT ) 156 | #define BYTEKIT_OP2_CVAR ( BYTEKIT_OP_CVAR << BYTEKIT_OP2_SHIFT ) 157 | #define BYTEKIT_OP2_TVAR ( BYTEKIT_OP_TVAR << BYTEKIT_OP2_SHIFT ) 158 | #define BYTEKIT_OP2_NUM ( BYTEKIT_OP_NUM << BYTEKIT_OP2_SHIFT ) 159 | #define BYTEKIT_OP2_OPNUM ( BYTEKIT_OP_OPNUM << BYTEKIT_OP2_SHIFT ) 160 | #define BYTEKIT_OP2_JMPADDR ( BYTEKIT_OP_JMPADDR << BYTEKIT_OP2_SHIFT ) 161 | 162 | #define BYTEKIT_EXT_SPECIAL ( 1 << BYTEKIT_EXT_SHIFT ) 163 | #define BYTEKIT_EXT_NUM ( 2 << BYTEKIT_EXT_SHIFT ) 164 | #define BYTEKIT_EXT_OPNUM ( 3 << BYTEKIT_EXT_SHIFT ) 165 | #define BYTEKIT_EXT_FETCH ( 4 << BYTEKIT_EXT_SHIFT ) 166 | #define BYTEKIT_EXT_TVAR ( 5 << BYTEKIT_EXT_SHIFT ) 167 | 168 | #define BYTEKIT_EXT_USED ( 15 << BYTEKIT_EXT_SHIFT ) 169 | #define BYTEKIT_EXT_TYPE_MASK ( 15 << BYTEKIT_EXT_SHIFT ) 170 | #define BYTEKIT_EXT_HIDE ( 16 << BYTEKIT_EXT_SHIFT ) 171 | 172 | static bytekit_define_list bytekit_constant_names[] = { 173 | { BYTEKIT_OP_TYPE_MASK, "OP_TYPE_MASK" }, 174 | { BYTEKIT_OP_WRITE, "OP_WRITE" }, 175 | { BYTEKIT_OP_HIDE, "OP_HIDE" }, 176 | { BYTEKIT_OP_USED, "OP_USED" }, 177 | 178 | { BYTEKIT_OP_CVAR, "OP_CVAR" }, 179 | { BYTEKIT_OP_TVAR, "OP_TVAR" }, 180 | { BYTEKIT_OP_NUM, "OP_NUM" }, 181 | { BYTEKIT_OP_OPNUM, "OP_OPNUM" }, 182 | { BYTEKIT_OP_JMPADDR, "OP_JMPADDR" }, 183 | 184 | { BYTEKIT_RES_SHIFT, "RES_SHIFT" }, 185 | { BYTEKIT_OP1_SHIFT, "OP1_SHIFT" }, 186 | { BYTEKIT_OP2_SHIFT, "OP2_SHIFT" }, 187 | { BYTEKIT_EXT_SHIFT, "EXT_SHIFT" }, 188 | { BYTEKIT_TYPE_SHIFT, "TYPE_SHIFT" }, 189 | 190 | { BYTEKIT_OP_STOP, "OP_STOP" }, 191 | 192 | { BYTEKIT_RES_TYPE_MASK, "RES_TYPE_MASK" }, 193 | { BYTEKIT_RES_WRITE, "RES_WRITE" }, 194 | { BYTEKIT_RES_HIDE, "RES_HIDE" }, 195 | { BYTEKIT_RES_USED, "RES_USED" }, 196 | { BYTEKIT_RES_CVAR, "RES_CVAR" }, 197 | { BYTEKIT_RES_TVAR, "RES_TVAR" }, 198 | { BYTEKIT_RES_NUM, "RES_NUM" }, 199 | { BYTEKIT_RES_OPNUM, "RES_OPNUM" }, 200 | { BYTEKIT_RES_JMPADDR, "RES_JMPADDR" }, 201 | 202 | { BYTEKIT_RES_FORCE, "RES_FORCE" }, 203 | 204 | { BYTEKIT_OP1_TYPE_MASK, "OP1_TYPE_MASK" }, 205 | { BYTEKIT_OP1_WRITE, "OP1_WRITE" }, 206 | { BYTEKIT_OP1_HIDE, "OP1_HIDE" }, 207 | { BYTEKIT_OP1_USED, "OP1_USED" }, 208 | { BYTEKIT_OP1_CVAR, "OP1_CVAR" }, 209 | { BYTEKIT_OP1_TVAR, "OP1_TVAR" }, 210 | { BYTEKIT_OP1_NUM, "OP1_NUM" }, 211 | { BYTEKIT_OP1_OPNUM, "OP1_OPNUM" }, 212 | { BYTEKIT_OP1_JMPADDR, "OP1_JMPADDR" }, 213 | 214 | { BYTEKIT_OP2_TYPE_MASK, "OP2_TYPE_MASK" }, 215 | { BYTEKIT_OP2_WRITE, "OP2_WRITE" }, 216 | { BYTEKIT_OP2_HIDE, "OP2_HIDE" }, 217 | { BYTEKIT_OP2_USED, "OP2_USED" }, 218 | { BYTEKIT_OP2_CVAR, "OP2_CVAR" }, 219 | { BYTEKIT_OP2_TVAR, "OP2_TVAR" }, 220 | { BYTEKIT_OP2_NUM, "OP2_NUM" }, 221 | { BYTEKIT_OP2_OPNUM, "OP2_OPNUM" }, 222 | { BYTEKIT_OP2_JMPADDR, "OP2_JMPADDR" }, 223 | 224 | { BYTEKIT_EXT_SPECIAL, "EXT_SPECIAL" }, 225 | { BYTEKIT_EXT_NUM, "EXT_NUM" }, 226 | { BYTEKIT_EXT_OPNUM, "EXT_OPNUM" }, 227 | { BYTEKIT_EXT_FETCH, "EXT_FETCH" }, 228 | { BYTEKIT_EXT_TVAR, "EXT_TVAR" }, 229 | 230 | { BYTEKIT_EXT_USED, "EXT_USED" }, 231 | { BYTEKIT_EXT_HIDE, "EXT_HIDE" }, 232 | 233 | { BYTEKIT_SHOW_SPECIALS, "SHOW_SPECIALS" }, 234 | 235 | { BYTEKIT_SRC_MASK, "SRC_MASK" }, 236 | { BYTEKIT_SRC_RES1, "SRC_RES1" }, 237 | { BYTEKIT_SRC_RES2, "SRC_RES2" }, 238 | { BYTEKIT_SRC_OP1, "SRC_OP1" }, 239 | { BYTEKIT_SRC_OP2, "SRC_OP2" }, 240 | { BYTEKIT_SRC_OP3, "SRC_OP3" }, 241 | { BYTEKIT_SRC_OP4, "SRC_OP4" }, 242 | { BYTEKIT_SRC_EXT1, "SRC_EXT1" }, 243 | { BYTEKIT_SRC_EXT2, "SRC_EXT2" }, 244 | 245 | { BYTEKIT_OPERAND_WRITTEN, "OPERAND_WRITTEN" }, 246 | 247 | { BYTEKIT_TYPE_VALUE, "TYPE_VALUE" }, 248 | { BYTEKIT_TYPE_CV, "TYPE_CV" }, 249 | { BYTEKIT_TYPE_VAR, "TYPE_VAR" }, 250 | { BYTEKIT_TYPE_TMP_VAR, "TYPE_TMP_VAR" }, 251 | { BYTEKIT_TYPE_SYMBOL, "TYPE_SYMBOL" }, 252 | 253 | { BYTEKIT_EDGE_TRUE, "EDGE_TRUE" }, 254 | { BYTEKIT_EDGE_FALSE, "EDGE_FALSE" }, 255 | { BYTEKIT_EDGE_NORMAL, "EDGE_NORMAL" }, 256 | { BYTEKIT_EDGE_EXCEPTION, "EDGE_EXCEPTION" }, 257 | 258 | { 0, NULL } 259 | }; 260 | 261 | static bytekit_define_list bytekit_zend_constant_names[] = { 262 | { IS_CONST, "IS_CONST" }, 263 | { IS_TMP_VAR, "IS_TMP_VAR" }, 264 | { IS_VAR, "IS_VAR" }, 265 | { IS_UNUSED, "IS_UNUSED" }, 266 | { IS_CV, "IS_CV" }, 267 | { EXT_TYPE_UNUSED, "EXT_TYPE_UNUSED" }, 268 | { ZEND_FETCH_CLASS_SELF, "FETCH_CLASS_SELF" }, 269 | { ZEND_FETCH_CLASS_PARENT, "FETCH_CLASS_PARENT" }, 270 | { IS_NULL, "IS_NULL" }, 271 | { IS_LONG, "IS_LONG" }, 272 | { IS_DOUBLE, "IS_DOUBLE" }, 273 | { IS_BOOL, "IS_BOOL" }, 274 | { IS_ARRAY, "IS_ARRAY" }, 275 | { IS_OBJECT, "IS_OBJECT" }, 276 | { IS_STRING, "IS_STRING" }, 277 | { IS_RESOURCE, "IS_RESOURCE" }, 278 | { IS_CONSTANT, "IS_CONSTANT" }, 279 | { IS_CONSTANT_ARRAY, "IS_CONSTANT_ARRAY" }, 280 | { ZEND_ISSET, "ISSET" }, 281 | { ZEND_ISEMPTY, "ISEMPTY" }, 282 | { ZEND_FETCH_STATIC_MEMBER, "FETCH_STATIC_MEMBER"}, 283 | { ZEND_FETCH_LOCAL, "FETCH_LOCAL"}, 284 | { ZEND_FETCH_GLOBAL, "FETCH_GLOBAL"}, 285 | { ZEND_FETCH_GLOBAL_LOCK, "FETCH_GLOBAL_LOCK"}, 286 | { ZEND_FE_FETCH_WITH_KEY, "FE_FETCH_WITH_KEY"}, 287 | { ZEND_ACC_PUBLIC, "ACC_PUBLIC" }, 288 | { ZEND_ACC_PRIVATE, "ACC_PRIVATE" }, 289 | { ZEND_ACC_PROTECTED, "ACC_PROTECTED" }, 290 | { ZEND_ACC_PPP_MASK, "ACC_PPP_MASK" }, 291 | { ZEND_ACC_INTERFACE, "ACC_INTERFACE" }, 292 | { ZEND_ACC_FINAL, "ACC_FINAL" }, 293 | { ZEND_ACC_ABSTRACT, "ACC_ABSTRACT" }, 294 | { ZEND_ACC_IMPLEMENTED_ABSTRACT, "ACC_IMPLEMENTED_ABSTRACT" }, 295 | { ZEND_ACC_STATIC, "ACC_STATIC" }, 296 | { ZEND_ACC_SHADOW, "ACC_SHADOW" }, 297 | { ZEND_ACC_FINAL_CLASS, "ACC_FINAL_CLASS" }, 298 | { ZEND_ACC_IMPLICIT_ABSTRACT_CLASS, "ACC_IMPLICIT_ABSTRACT_CLASS" }, 299 | { ZEND_ACC_EXPLICIT_ABSTRACT_CLASS, "ACC_EXPLICIT_ABSTRACT_CLASS" }, 300 | { 0, NULL } 301 | }; 302 | 303 | static bytekit_define_list bytekit_opcode_names[] = { 304 | { ZEND_NOP, "NOP", 0, 0 }, 305 | { ZEND_ADD, "ADD", BYTEKIT_RES_WRITE | BYTEKIT_RES_CVAR | BYTEKIT_OP1_CVAR | BYTEKIT_OP2_CVAR, 0 }, 306 | { ZEND_SUB, "SUB", BYTEKIT_RES_WRITE | BYTEKIT_RES_CVAR | BYTEKIT_OP1_CVAR | BYTEKIT_OP2_CVAR, 0 }, 307 | { ZEND_MUL, "MUL", BYTEKIT_RES_WRITE | BYTEKIT_RES_CVAR | BYTEKIT_OP1_CVAR | BYTEKIT_OP2_CVAR, 0 }, 308 | { ZEND_DIV, "DIV", BYTEKIT_RES_WRITE | BYTEKIT_RES_CVAR | BYTEKIT_OP1_CVAR | BYTEKIT_OP2_CVAR, 0 }, 309 | { ZEND_MOD, "MOD", BYTEKIT_RES_WRITE | BYTEKIT_RES_CVAR | BYTEKIT_OP1_CVAR | BYTEKIT_OP2_CVAR, 0 }, 310 | { ZEND_SL, "SL", BYTEKIT_RES_WRITE | BYTEKIT_RES_CVAR | BYTEKIT_OP1_CVAR | BYTEKIT_OP2_CVAR, 0 }, 311 | { ZEND_SR, "SR", BYTEKIT_RES_WRITE | BYTEKIT_RES_CVAR | BYTEKIT_OP1_CVAR | BYTEKIT_OP2_CVAR, 0 }, 312 | { ZEND_CONCAT, "CONCAT", BYTEKIT_RES_WRITE | BYTEKIT_RES_CVAR | BYTEKIT_OP1_CVAR | BYTEKIT_OP2_CVAR, 0 }, 313 | { ZEND_BW_OR, "BW_OR", BYTEKIT_RES_WRITE | BYTEKIT_RES_CVAR | BYTEKIT_OP1_CVAR | BYTEKIT_OP2_CVAR, 0 }, 314 | { ZEND_BW_AND, "BW_AND", BYTEKIT_RES_WRITE | BYTEKIT_RES_CVAR | BYTEKIT_OP1_CVAR | BYTEKIT_OP2_CVAR, 0 }, 315 | { ZEND_BW_XOR, "BW_XOR", BYTEKIT_RES_WRITE | BYTEKIT_RES_CVAR | BYTEKIT_OP1_CVAR | BYTEKIT_OP2_CVAR, 0 }, 316 | { ZEND_BW_NOT, "BW_NOT", BYTEKIT_RES_WRITE | BYTEKIT_RES_CVAR | BYTEKIT_OP1_CVAR, 0 }, 317 | { ZEND_BOOL_NOT, "BOOL_NOT", BYTEKIT_RES_WRITE | BYTEKIT_RES_CVAR | BYTEKIT_OP1_CVAR, 0 }, 318 | { ZEND_BOOL_XOR, "BOOL_XOR", BYTEKIT_RES_WRITE | BYTEKIT_RES_CVAR | BYTEKIT_OP1_CVAR | BYTEKIT_OP2_CVAR, 0 }, 319 | { ZEND_IS_IDENTICAL, "IS_IDENTICAL", BYTEKIT_RES_WRITE | BYTEKIT_RES_CVAR | BYTEKIT_OP1_CVAR | BYTEKIT_OP2_CVAR, 0 }, 320 | { ZEND_IS_NOT_IDENTICAL, "IS_NOT_IDENTICAL", BYTEKIT_RES_WRITE | BYTEKIT_RES_CVAR | BYTEKIT_OP1_CVAR | BYTEKIT_OP2_CVAR, 0 }, 321 | { ZEND_IS_EQUAL, "IS_EQUAL", BYTEKIT_RES_WRITE | BYTEKIT_RES_CVAR | BYTEKIT_OP1_CVAR | BYTEKIT_OP2_CVAR, 0 }, 322 | { ZEND_IS_NOT_EQUAL, "IS_NOT_EQUAL", BYTEKIT_RES_WRITE | BYTEKIT_RES_CVAR | BYTEKIT_OP1_CVAR | BYTEKIT_OP2_CVAR, 0 }, 323 | { ZEND_IS_SMALLER, "IS_SMALLER", BYTEKIT_RES_WRITE | BYTEKIT_RES_CVAR | BYTEKIT_OP1_CVAR | BYTEKIT_OP2_CVAR, 0 }, 324 | { ZEND_IS_SMALLER_OR_EQUAL, "IS_SMALLER_OR_EQUAL", BYTEKIT_RES_WRITE | BYTEKIT_RES_CVAR | BYTEKIT_OP1_CVAR | BYTEKIT_OP2_CVAR, 0 }, 325 | { ZEND_CAST, "CAST", BYTEKIT_RES_WRITE | BYTEKIT_RES_CVAR | BYTEKIT_OP1_CVAR | BYTEKIT_EXT_SPECIAL, 0 }, 326 | { ZEND_QM_ASSIGN, "QM_ASSIGN", BYTEKIT_RES_WRITE | BYTEKIT_RES_CVAR | BYTEKIT_OP1_CVAR, 0 }, 327 | { ZEND_ASSIGN_ADD, "ASSIGN_ADD", BYTEKIT_RES_WRITE | BYTEKIT_OP1_WRITE | BYTEKIT_RES_CVAR | BYTEKIT_OP1_CVAR | BYTEKIT_OP2_CVAR | BYTEKIT_EXT_SPECIAL, 0 }, 328 | { ZEND_ASSIGN_SUB, "ASSIGN_SUB", BYTEKIT_RES_WRITE | BYTEKIT_OP1_WRITE | BYTEKIT_RES_CVAR | BYTEKIT_OP1_CVAR | BYTEKIT_OP2_CVAR | BYTEKIT_EXT_SPECIAL, 0 }, 329 | { ZEND_ASSIGN_MUL, "ASSIGN_MUL", BYTEKIT_RES_WRITE | BYTEKIT_OP1_WRITE | BYTEKIT_RES_CVAR | BYTEKIT_OP1_CVAR | BYTEKIT_OP2_CVAR | BYTEKIT_EXT_SPECIAL, 0 }, 330 | { ZEND_ASSIGN_DIV, "ASSIGN_DIV", BYTEKIT_RES_WRITE | BYTEKIT_OP1_WRITE | BYTEKIT_RES_CVAR | BYTEKIT_OP1_CVAR | BYTEKIT_OP2_CVAR | BYTEKIT_EXT_SPECIAL, 0 }, 331 | { ZEND_ASSIGN_MOD, "ASSIGN_MOD", BYTEKIT_RES_WRITE | BYTEKIT_OP1_WRITE | BYTEKIT_RES_CVAR | BYTEKIT_OP1_CVAR | BYTEKIT_OP2_CVAR | BYTEKIT_EXT_SPECIAL, 0 }, 332 | { ZEND_ASSIGN_SL, "ASSIGN_SL", BYTEKIT_RES_WRITE | BYTEKIT_OP1_WRITE | BYTEKIT_RES_CVAR | BYTEKIT_OP1_CVAR | BYTEKIT_OP2_CVAR | BYTEKIT_EXT_SPECIAL, 0 }, 333 | { ZEND_ASSIGN_SR, "ASSIGN_SR", BYTEKIT_RES_WRITE | BYTEKIT_OP1_WRITE | BYTEKIT_RES_CVAR | BYTEKIT_OP1_CVAR | BYTEKIT_OP2_CVAR | BYTEKIT_EXT_SPECIAL, 0 }, 334 | { ZEND_ASSIGN_CONCAT, "ASSIGN_CONCAT", BYTEKIT_RES_WRITE | BYTEKIT_OP1_WRITE | BYTEKIT_RES_CVAR | BYTEKIT_OP1_CVAR | BYTEKIT_OP2_CVAR | BYTEKIT_EXT_SPECIAL, 0 }, 335 | { ZEND_ASSIGN_BW_OR, "ASSIGN_BW_OR", BYTEKIT_RES_WRITE | BYTEKIT_OP1_WRITE | BYTEKIT_RES_CVAR | BYTEKIT_OP1_CVAR | BYTEKIT_OP2_CVAR | BYTEKIT_EXT_SPECIAL, 0 }, 336 | { ZEND_ASSIGN_BW_AND, "ASSIGN_BW_AND", BYTEKIT_RES_WRITE | BYTEKIT_OP1_WRITE | BYTEKIT_RES_CVAR | BYTEKIT_OP1_CVAR | BYTEKIT_OP2_CVAR | BYTEKIT_EXT_SPECIAL, 0 }, 337 | { ZEND_ASSIGN_BW_XOR, "ASSIGN_BW_XOR", BYTEKIT_RES_WRITE | BYTEKIT_OP1_WRITE | BYTEKIT_RES_CVAR | BYTEKIT_OP1_CVAR | BYTEKIT_OP2_CVAR | BYTEKIT_EXT_SPECIAL, 0 }, 338 | { ZEND_PRE_INC, "PRE_INC", BYTEKIT_RES_WRITE | BYTEKIT_OP1_WRITE | BYTEKIT_RES_CVAR | BYTEKIT_OP1_CVAR, 0 }, 339 | { ZEND_PRE_DEC, "PRE_DEC", BYTEKIT_RES_WRITE | BYTEKIT_OP1_WRITE | BYTEKIT_RES_CVAR | BYTEKIT_OP1_CVAR, 0 }, 340 | { ZEND_POST_INC, "POST_INC", BYTEKIT_RES_WRITE | BYTEKIT_OP1_WRITE | BYTEKIT_RES_CVAR | BYTEKIT_OP1_CVAR, 0 }, 341 | { ZEND_POST_DEC, "POST_DEC", BYTEKIT_RES_WRITE | BYTEKIT_OP1_WRITE | BYTEKIT_RES_CVAR | BYTEKIT_OP1_CVAR, 0 }, 342 | { ZEND_ASSIGN, "ASSIGN", BYTEKIT_RES_WRITE | BYTEKIT_OP1_WRITE | BYTEKIT_RES_CVAR | BYTEKIT_OP1_CVAR | BYTEKIT_OP2_CVAR, 0 }, 343 | { ZEND_ASSIGN_REF, "ASSIGN_REF", BYTEKIT_RES_WRITE | BYTEKIT_OP1_WRITE | BYTEKIT_RES_CVAR | BYTEKIT_OP1_CVAR | BYTEKIT_OP2_CVAR | BYTEKIT_EXT_NUM, 0 }, 344 | { ZEND_ECHO, "ECHO", BYTEKIT_OP1_CVAR, 0 }, 345 | { ZEND_PRINT, "PRINT", BYTEKIT_RES_WRITE | BYTEKIT_RES_CVAR | BYTEKIT_OP1_CVAR, 0 }, 346 | { ZEND_JMP, "JMP", BYTEKIT_OP1_JMPADDR, 0 }, 347 | { ZEND_JMPZ, "JMPZ", BYTEKIT_OP1_CVAR | BYTEKIT_OP2_JMPADDR, 0 }, 348 | { ZEND_JMPNZ, "JMPNZ", BYTEKIT_OP1_CVAR | BYTEKIT_OP2_JMPADDR, 0 }, 349 | { ZEND_JMPZNZ, "JMPZNZ", BYTEKIT_OP1_CVAR | BYTEKIT_OP2_OPNUM | BYTEKIT_EXT_OPNUM, 0 }, 350 | { ZEND_JMPZ_EX, "JMPZ_EX", BYTEKIT_RES_WRITE | BYTEKIT_RES_CVAR | BYTEKIT_OP1_CVAR | BYTEKIT_OP2_JMPADDR, 0 }, 351 | { ZEND_JMPNZ_EX, "JMPNZ_EX", BYTEKIT_RES_WRITE | BYTEKIT_RES_CVAR | BYTEKIT_OP1_CVAR | BYTEKIT_OP2_JMPADDR, 0 }, 352 | { ZEND_CASE, "CASE", BYTEKIT_RES_WRITE | BYTEKIT_RES_CVAR | BYTEKIT_OP1_CVAR | BYTEKIT_OP2_CVAR, 0 }, 353 | { ZEND_SWITCH_FREE, "SWITCH_FREE", BYTEKIT_OP1_WRITE | BYTEKIT_OP1_CVAR | BYTEKIT_EXT_SPECIAL, 0 }, 354 | { ZEND_BRK, "BRK", BYTEKIT_OP1_OPNUM | BYTEKIT_OP1_HIDE | BYTEKIT_OP2_CVAR, 0 }, 355 | { ZEND_CONT, "CONT", BYTEKIT_OP1_OPNUM | BYTEKIT_OP1_HIDE |BYTEKIT_OP2_CVAR, 0 }, 356 | { ZEND_BOOL, "BOOL", BYTEKIT_RES_WRITE | BYTEKIT_RES_CVAR | BYTEKIT_OP1_CVAR, 0 }, 357 | { ZEND_INIT_STRING, "INIT_STRING", BYTEKIT_RES_WRITE | BYTEKIT_RES_CVAR, 0 }, 358 | { ZEND_ADD_CHAR, "ADD_CHAR", BYTEKIT_RES_WRITE | BYTEKIT_RES_CVAR | BYTEKIT_OP1_CVAR | BYTEKIT_OP2_CVAR, 0 }, 359 | { ZEND_ADD_STRING, "ADD_STRING", BYTEKIT_RES_WRITE | BYTEKIT_RES_CVAR | BYTEKIT_OP1_CVAR | BYTEKIT_OP2_CVAR, 0 }, 360 | { ZEND_ADD_VAR, "ADD_VAR", BYTEKIT_RES_WRITE | BYTEKIT_RES_CVAR | BYTEKIT_OP1_CVAR | BYTEKIT_OP2_CVAR, 0 }, 361 | { ZEND_BEGIN_SILENCE, "BEGIN_SILENCE", BYTEKIT_RES_WRITE | BYTEKIT_RES_CVAR, 0 }, 362 | { ZEND_END_SILENCE, "END_SILENCE", BYTEKIT_OP1_CVAR, 0 }, 363 | { ZEND_INIT_FCALL_BY_NAME, "INIT_FCALL_BY_NAME", BYTEKIT_OP1_CVAR | BYTEKIT_OP2_CVAR | BYTEKIT_EXT_SPECIAL, 0 }, 364 | { ZEND_DO_FCALL, "DO_FCALL", BYTEKIT_RES_WRITE | BYTEKIT_RES_CVAR | BYTEKIT_OP1_CVAR | BYTEKIT_OP2_CVAR | BYTEKIT_EXT_SPECIAL, 0 }, 365 | { ZEND_DO_FCALL_BY_NAME, "DO_FCALL_BY_NAME", BYTEKIT_RES_WRITE | BYTEKIT_RES_CVAR | BYTEKIT_OP1_CVAR | BYTEKIT_EXT_SPECIAL, 0 }, 366 | { ZEND_RETURN, "RETURN", BYTEKIT_OP1_CVAR | BYTEKIT_EXT_SPECIAL | BYTEKIT_OP_STOP, 0 }, 367 | { ZEND_RECV, "RECV", BYTEKIT_RES_WRITE | BYTEKIT_RES_CVAR | BYTEKIT_RES_FORCE | BYTEKIT_OP1_CVAR | BYTEKIT_EXT_FETCH, 0 }, 368 | { ZEND_RECV_INIT, "RECV_INIT", BYTEKIT_RES_WRITE | BYTEKIT_RES_CVAR | BYTEKIT_RES_FORCE | BYTEKIT_OP1_CVAR | BYTEKIT_OP2_CVAR | BYTEKIT_EXT_FETCH, 0 }, 369 | { ZEND_SEND_VAL, "SEND_VAL", BYTEKIT_OP1_CVAR | BYTEKIT_OP2_NUM | BYTEKIT_EXT_SPECIAL, 0 }, 370 | { ZEND_SEND_VAR, "SEND_VAR", BYTEKIT_OP1_CVAR | BYTEKIT_OP2_NUM | BYTEKIT_EXT_SPECIAL, 0 }, 371 | { ZEND_SEND_REF, "SEND_REF", BYTEKIT_OP1_CVAR, 0 }, 372 | { ZEND_NEW, "NEW", BYTEKIT_RES_WRITE | BYTEKIT_RES_CVAR | BYTEKIT_OP1_TVAR | BYTEKIT_OP2_OPNUM, 0 }, 373 | { ZEND_INIT_NS_FCALL_BY_NAME, "INIT_NS_FCALL_BY_NAME", BYTEKIT_OP1_CVAR | BYTEKIT_OP2_CVAR | BYTEKIT_EXT_SPECIAL, BYTEKIT_OP1_CVAR }, 374 | { ZEND_FREE, "FREE", BYTEKIT_OP1_WRITE | BYTEKIT_OP1_CVAR, 0 }, 375 | { ZEND_INIT_ARRAY, "INIT_ARRAY", BYTEKIT_RES_WRITE | BYTEKIT_OP1_WRITE | BYTEKIT_RES_CVAR | BYTEKIT_OP1_CVAR | BYTEKIT_OP2_CVAR | BYTEKIT_EXT_NUM, 0 }, 376 | { ZEND_ADD_ARRAY_ELEMENT, "ADD_ARRAY_ELEMENT", BYTEKIT_RES_WRITE | BYTEKIT_OP1_WRITE | BYTEKIT_RES_CVAR | BYTEKIT_OP1_CVAR | BYTEKIT_OP2_CVAR | BYTEKIT_EXT_NUM, 0 }, 377 | { ZEND_INCLUDE_OR_EVAL, "INCLUDE_OR_EVAL", BYTEKIT_RES_WRITE | BYTEKIT_RES_CVAR | BYTEKIT_OP1_CVAR | BYTEKIT_OP2_CVAR, 0 }, 378 | { ZEND_UNSET_VAR, "UNSET_VAR", BYTEKIT_OP1_CVAR | BYTEKIT_OP2_CVAR, 0 }, 379 | { ZEND_UNSET_DIM, "UNSET_DIM", BYTEKIT_OP1_CVAR | BYTEKIT_OP2_CVAR, 0 }, 380 | { ZEND_UNSET_OBJ, "UNSET_OBJ", BYTEKIT_OP1_CVAR | BYTEKIT_OP2_CVAR, 0 }, 381 | { ZEND_FE_RESET, "FE_RESET", BYTEKIT_RES_WRITE | BYTEKIT_RES_CVAR | BYTEKIT_OP1_CVAR | BYTEKIT_OP2_OPNUM, 0 }, 382 | { ZEND_FE_FETCH, "FE_FETCH", BYTEKIT_RES_WRITE | BYTEKIT_RES_CVAR | BYTEKIT_OP1_CVAR | BYTEKIT_OP2_OPNUM | BYTEKIT_EXT_SPECIAL, 0 }, 383 | { ZEND_EXIT, "EXIT", BYTEKIT_OP1_CVAR | BYTEKIT_OP_STOP, 0 }, 384 | { ZEND_FETCH_R, "FETCH_R", BYTEKIT_RES_WRITE | BYTEKIT_RES_CVAR | BYTEKIT_OP1_CVAR | BYTEKIT_OP2_CVAR, 0 }, 385 | { ZEND_FETCH_DIM_R, "FETCH_DIM_R", BYTEKIT_RES_WRITE | BYTEKIT_RES_CVAR | BYTEKIT_OP1_CVAR | BYTEKIT_OP2_CVAR | BYTEKIT_EXT_SPECIAL, 0 }, 386 | { ZEND_FETCH_OBJ_R, "FETCH_OBJ_R", BYTEKIT_RES_WRITE | BYTEKIT_RES_CVAR | BYTEKIT_OP1_CVAR | BYTEKIT_OP2_CVAR, 0 }, 387 | { ZEND_FETCH_W, "FETCH_W", BYTEKIT_RES_WRITE | BYTEKIT_RES_CVAR | BYTEKIT_OP1_CVAR | BYTEKIT_OP2_CVAR, 0 }, 388 | { ZEND_FETCH_DIM_W, "FETCH_DIM_W", BYTEKIT_RES_WRITE | BYTEKIT_RES_CVAR | BYTEKIT_OP1_CVAR | BYTEKIT_OP2_CVAR | BYTEKIT_EXT_SPECIAL, 0 }, 389 | { ZEND_FETCH_OBJ_W, "FETCH_OBJ_W", BYTEKIT_RES_WRITE | BYTEKIT_RES_CVAR | BYTEKIT_OP2_CVAR | BYTEKIT_EXT_SPECIAL, 0 }, 390 | { ZEND_FETCH_RW, "FETCH_RW", BYTEKIT_RES_WRITE | BYTEKIT_RES_CVAR | BYTEKIT_OP1_CVAR | BYTEKIT_OP2_CVAR, 0 }, 391 | { ZEND_FETCH_DIM_RW, "FETCH_DIM_RW", BYTEKIT_RES_WRITE | BYTEKIT_RES_CVAR | BYTEKIT_OP1_CVAR | BYTEKIT_OP2_CVAR}, 392 | { ZEND_FETCH_OBJ_RW, "FETCH_OBJ_RW", BYTEKIT_RES_WRITE | BYTEKIT_RES_CVAR | BYTEKIT_OP2_CVAR, 0 }, 393 | { ZEND_FETCH_IS, "FETCH_IS", BYTEKIT_RES_WRITE | BYTEKIT_RES_CVAR | BYTEKIT_OP1_CVAR | BYTEKIT_OP2_CVAR, 0 }, 394 | { ZEND_FETCH_DIM_IS, "FETCH_DIM_IS", BYTEKIT_RES_WRITE | BYTEKIT_RES_CVAR | BYTEKIT_OP1_CVAR | BYTEKIT_OP2_CVAR, 0 }, 395 | { ZEND_FETCH_OBJ_IS, "FETCH_OBJ_IS", BYTEKIT_RES_WRITE | BYTEKIT_RES_CVAR | BYTEKIT_OP1_CVAR | BYTEKIT_OP2_CVAR, 0 }, 396 | { ZEND_FETCH_FUNC_ARG, "FETCH_FUNC_ARG", BYTEKIT_RES_WRITE | BYTEKIT_RES_CVAR | BYTEKIT_OP1_CVAR | BYTEKIT_OP2_CVAR | BYTEKIT_EXT_SPECIAL, 0 }, 397 | { ZEND_FETCH_DIM_FUNC_ARG, "FETCH_DIM_FUNC_ARG", BYTEKIT_RES_WRITE | BYTEKIT_RES_CVAR | BYTEKIT_OP1_CVAR | BYTEKIT_OP2_CVAR | BYTEKIT_EXT_SPECIAL, 0 }, 398 | { ZEND_FETCH_OBJ_FUNC_ARG, "FETCH_OBJ_FUNC_ARG", BYTEKIT_RES_WRITE | BYTEKIT_RES_CVAR | BYTEKIT_OP1_CVAR | BYTEKIT_OP2_CVAR | BYTEKIT_EXT_SPECIAL, 0 }, 399 | { ZEND_FETCH_UNSET, "FETCH_UNSET", BYTEKIT_RES_WRITE | BYTEKIT_RES_CVAR | BYTEKIT_OP1_CVAR | BYTEKIT_OP2_CVAR, 0 }, 400 | { ZEND_FETCH_DIM_UNSET, "FETCH_DIM_UNSET", BYTEKIT_RES_WRITE | BYTEKIT_RES_CVAR | BYTEKIT_OP1_CVAR | BYTEKIT_OP2_CVAR, 0 }, 401 | { ZEND_FETCH_OBJ_UNSET, "FETCH_OBJ_UNSET", BYTEKIT_RES_WRITE | BYTEKIT_RES_CVAR | BYTEKIT_OP1_CVAR | BYTEKIT_OP2_CVAR, 0 }, 402 | { ZEND_FETCH_DIM_TMP_VAR, "FETCH_DIM_TMP_VAR", BYTEKIT_RES_WRITE | BYTEKIT_RES_CVAR | BYTEKIT_OP1_CVAR | BYTEKIT_OP2_CVAR, 0 }, 403 | { ZEND_FETCH_CONSTANT, "FETCH_CONSTANT", BYTEKIT_RES_WRITE | BYTEKIT_RES_CVAR | BYTEKIT_OP1_CVAR | BYTEKIT_OP2_CVAR | BYTEKIT_EXT_FETCH, 0 }, 404 | { ZEND_GOTO, "GOTO", BYTEKIT_OP1_JMPADDR | BYTEKIT_OP2_CVAR | BYTEKIT_EXT_OPNUM, 0 }, 405 | { ZEND_EXT_STMT, "EXT_STMT", 0, 0 }, 406 | { ZEND_EXT_FCALL_BEGIN, "EXT_FCALL_BEGIN", 0, 0 }, 407 | { ZEND_EXT_FCALL_END, "EXT_FCALL_END", 0, 0 }, 408 | { ZEND_EXT_NOP, "EXT_NOP", 0, 0 }, 409 | { ZEND_TICKS, "TICKS", BYTEKIT_OP1_CVAR, 0 }, 410 | { ZEND_SEND_VAR_NO_REF, "SEND_VAR_NO_REF", BYTEKIT_OP1_CVAR | BYTEKIT_OP2_NUM | BYTEKIT_EXT_SPECIAL, 0 }, 411 | { ZEND_CATCH, "CATCH", BYTEKIT_OP1_TVAR | BYTEKIT_OP2_CVAR | BYTEKIT_EXT_OPNUM, 0 }, 412 | { ZEND_THROW, "THROW", BYTEKIT_OP1_CVAR, 0 }, 413 | { ZEND_FETCH_CLASS, "FETCH_CLASS", BYTEKIT_RES_WRITE | BYTEKIT_RES_TVAR | BYTEKIT_OP2_CVAR | BYTEKIT_EXT_FETCH, 0 }, 414 | { ZEND_CLONE, "CLONE", BYTEKIT_RES_WRITE | BYTEKIT_RES_CVAR | BYTEKIT_OP1_CVAR, 0 }, 415 | { ZEND_INIT_METHOD_CALL, "INIT_METHOD_CALL", BYTEKIT_OP1_CVAR | BYTEKIT_OP2_CVAR, 0 }, 416 | { ZEND_INIT_STATIC_METHOD_CALL, "INIT_STATIC_METHOD_CALL", BYTEKIT_OP1_CVAR | BYTEKIT_OP2_CVAR, 0 }, 417 | { ZEND_ISSET_ISEMPTY_VAR, "ISSET_ISEMPTY_VAR", BYTEKIT_RES_WRITE | BYTEKIT_RES_CVAR | BYTEKIT_OP1_CVAR | BYTEKIT_OP2_CVAR | BYTEKIT_EXT_SPECIAL, 0 }, 418 | { ZEND_ISSET_ISEMPTY_DIM_OBJ, "ISSET_ISEMPTY_DIM_OBJ", BYTEKIT_RES_WRITE | BYTEKIT_RES_CVAR | BYTEKIT_OP1_CVAR | BYTEKIT_OP2_CVAR | BYTEKIT_EXT_SPECIAL, 0 }, 419 | { ZEND_PRE_INC_OBJ, "PRE_INC_OBJ", BYTEKIT_RES_WRITE | BYTEKIT_OP1_WRITE | BYTEKIT_RES_CVAR | BYTEKIT_OP1_CVAR | BYTEKIT_OP2_CVAR, 0 }, 420 | { ZEND_PRE_DEC_OBJ, "PRE_DEC_OBJ", BYTEKIT_RES_WRITE | BYTEKIT_OP1_WRITE | BYTEKIT_RES_CVAR | BYTEKIT_OP1_CVAR | BYTEKIT_OP2_CVAR, 0 }, 421 | { ZEND_POST_INC_OBJ, "POST_INC_OBJ", BYTEKIT_RES_WRITE | BYTEKIT_OP1_WRITE | BYTEKIT_RES_CVAR | BYTEKIT_OP1_CVAR | BYTEKIT_OP2_CVAR, 0 }, 422 | { ZEND_POST_DEC_OBJ, "POST_DEC_OBJ", BYTEKIT_RES_WRITE | BYTEKIT_OP1_WRITE | BYTEKIT_RES_CVAR | BYTEKIT_OP1_CVAR | BYTEKIT_OP2_CVAR, 0 }, 423 | { ZEND_ASSIGN_OBJ, "ASSIGN_OBJ", BYTEKIT_RES_WRITE | BYTEKIT_OP1_WRITE | BYTEKIT_RES_CVAR | BYTEKIT_OP1_CVAR | BYTEKIT_OP2_CVAR, BYTEKIT_OP1_CVAR }, 424 | { ZEND_OP_DATA, "OP_DATA", 0, 0 }, 425 | { ZEND_INSTANCEOF, "INSTANCEOF", BYTEKIT_RES_WRITE | BYTEKIT_RES_CVAR | BYTEKIT_OP1_CVAR | BYTEKIT_OP2_CVAR, 0 }, 426 | { ZEND_DECLARE_CLASS, "DECLARE_CLASS", BYTEKIT_RES_WRITE | BYTEKIT_RES_CVAR | BYTEKIT_OP1_CVAR | BYTEKIT_OP2_CVAR, 0 }, 427 | { ZEND_DECLARE_INHERITED_CLASS, "DECLARE_INHERITED_CLASS", BYTEKIT_RES_WRITE | BYTEKIT_RES_CVAR | BYTEKIT_OP1_CVAR | BYTEKIT_OP2_CVAR | BYTEKIT_EXT_TVAR, 0 }, 428 | { ZEND_DECLARE_FUNCTION, "DECLARE_FUNCTION", BYTEKIT_OP1_CVAR | BYTEKIT_OP2_CVAR, 0 }, 429 | { ZEND_RAISE_ABSTRACT_ERROR, "RAISE_ABSTRACT_ERROR", BYTEKIT_OP_STOP, 0 }, 430 | { ZEND_DECLARE_CONST, "DECLARE_CONST", BYTEKIT_OP1_CVAR | BYTEKIT_OP2_CVAR, 0 }, 431 | { ZEND_ADD_INTERFACE, "ADD_INTERFACE", BYTEKIT_OP1_TVAR | BYTEKIT_OP2_CVAR | BYTEKIT_EXT_FETCH, 0 }, 432 | { ZEND_DECLARE_INHERITED_CLASS_DELAYED, "DECLARE_INHERITED_CLASS_DELAYED", BYTEKIT_RES_CVAR | BYTEKIT_OP1_CVAR | BYTEKIT_OP2_CVAR | BYTEKIT_EXT_TVAR, 0 }, 433 | { ZEND_VERIFY_ABSTRACT_CLASS, "VERIFY_ABSTRACT_CLASS", BYTEKIT_OP1_TVAR, 0 }, 434 | { ZEND_ASSIGN_DIM, "ASSIGN_DIM", BYTEKIT_RES_WRITE | BYTEKIT_OP1_WRITE | BYTEKIT_RES_CVAR | BYTEKIT_OP1_CVAR | BYTEKIT_OP2_CVAR, BYTEKIT_OP1_CVAR | BYTEKIT_OP2_CVAR }, 435 | { ZEND_ISSET_ISEMPTY_PROP_OBJ, "ISSET_ISEMPTY_PROP_OBJ", BYTEKIT_RES_WRITE | BYTEKIT_RES_CVAR | BYTEKIT_OP1_CVAR | BYTEKIT_OP2_CVAR | BYTEKIT_EXT_SPECIAL, 0 }, 436 | { ZEND_HANDLE_EXCEPTION, "HANDLE_EXCEPTION", 0, 0 }, 437 | { ZEND_USER_OPCODE, "USER_OPCODE", 0, 0 }, 438 | { ZEND_JMP_SET, "JMP_SET", BYTEKIT_OP1_CVAR | BYTEKIT_OP2_JMPADDR, 0 }, 439 | { ZEND_DECLARE_LAMBDA_FUNCTION, "DECLARE_LAMBDA_FUNCTION", BYTEKIT_RES_WRITE | BYTEKIT_RES_CVAR | BYTEKIT_OP1_CVAR | BYTEKIT_OP2_CVAR, 0 }, 440 | { 0, NULL } 441 | }; 442 | 443 | #endif /* PHP_BYTEKIT_H */ 444 | 445 | /* 446 | * Local variables: 447 | * tab-width: 4 448 | * c-basic-offset: 4 449 | * End: 450 | * vim600: noet sw=4 ts=4 fdm=marker 451 | * vim<600: noet sw=4 ts=4 452 | */ 453 | 454 | -------------------------------------------------------------------------------- /bytekit.c: -------------------------------------------------------------------------------- 1 | /* 2 | +----------------------------------------------------------------------+ 3 | | Bytekit | 4 | +----------------------------------------------------------------------+ 5 | | Copyright (c) 2008-2009 SektionEins GmbH | 6 | +----------------------------------------------------------------------+ 7 | | This source file is subject to version 1.0 of the Bytekit license, | 8 | | that is bundled with this package in the file LICENSE, and is | 9 | | available at through the world-wide-web at | 10 | | http://www.bytekit.org/license.txt | 11 | | If you did not receive a copy of the Bytekit license and are unable | 12 | | to obtain it through the world-wide-web, please send a note to | 13 | | license@bytekit.org so we can mail you a copy immediately. | 14 | +----------------------------------------------------------------------+ 15 | | Author: Stefan Esser | 16 | +----------------------------------------------------------------------+ 17 | */ 18 | 19 | /*************************************************************************/ 20 | /* This code combines features of vld, parsekit with additional features */ 21 | /* therefore some parts of thr code are based on these extensions */ 22 | /*************************************************************************/ 23 | 24 | /* $Id$ */ 25 | 26 | #ifdef HAVE_CONFIG_H 27 | #include "config.h" 28 | #endif 29 | 30 | #include "php.h" 31 | #include "php_ini.h" 32 | #include "ext/standard/info.h" 33 | #include "ext/standard/php_string.h" 34 | #include "php_bytekit.h" 35 | 36 | /* This code does only work on PHP >= 5.2.0 - PHP 6 not yet supported */ 37 | #if (!defined(PHP_VERSION_ID) || (PHP_VERSION_ID < 50200) || (PHP_VERSION_ID >= 60000)) 38 | # error "Bytekit can only be compiled for PHP >= 5.2.0 and not yet for PHP 6" 39 | #endif 40 | 41 | ZEND_DECLARE_MODULE_GLOBALS(bytekit) 42 | 43 | /* original error callback handler */ 44 | void (*bytekit_original_error_cb)(int type, const char *error_filename, const uint error_lineno, const char *format, va_list args); 45 | 46 | /* {{{ bytekit_error_cb 47 | ignore, catch and return non core errors */ 48 | static void bytekit_error_cb(int type, const char *error_filename, const uint error_lineno, const char *format, va_list args) 49 | { 50 | char *buffer; 51 | int buffer_len; 52 | zval *tmpzval; 53 | TSRMLS_FETCH(); 54 | 55 | /* Do not touch core errors and avoid loops */ 56 | if (type == E_CORE_ERROR || BYTEKIT_G(in_error_cb)) { 57 | bytekit_original_error_cb(type, (char *)error_filename, error_lineno, format, args); 58 | return; 59 | } 60 | 61 | /* Ignore errors if selected */ 62 | if (!BYTEKIT_G(compile_errors)) { 63 | return; 64 | } 65 | 66 | /* We are processing an error */ 67 | BYTEKIT_G(in_error_cb) = 1; 68 | 69 | MAKE_STD_ZVAL(tmpzval); 70 | array_init(tmpzval); 71 | add_assoc_long(tmpzval, "errno", type); 72 | add_assoc_string(tmpzval, "filename", (char *)error_filename, 1); 73 | add_assoc_long(tmpzval, "lineno", error_lineno); 74 | buffer_len = vspprintf(&buffer, PG(log_errors_max_len), format, args); 75 | add_assoc_stringl(tmpzval, "errstr", buffer, buffer_len, 1); 76 | 77 | if (Z_TYPE_P(BYTEKIT_G(compile_errors)) == IS_NULL) { 78 | array_init(BYTEKIT_G(compile_errors)); 79 | } 80 | add_next_index_zval(BYTEKIT_G(compile_errors), tmpzval); 81 | 82 | /* We are no longer processing the error */ 83 | BYTEKIT_G(in_error_cb) = 0; 84 | } 85 | /* }}} */ 86 | 87 | /* {{{ bytekit_get_opcode_info */ 88 | static char* bytekit_get_opcode_info(long val, long *pflags, long *pflags2) 89 | { 90 | bytekit_define_list *names; 91 | 92 | for (names = bytekit_opcode_names; names->str; names++) { 93 | if (names->val == val) { 94 | if (pflags) { 95 | *pflags = names->flags; 96 | } 97 | if (pflags2) { 98 | *pflags2 = names->flags2; 99 | } 100 | return names->str; 101 | } 102 | } 103 | 104 | return BYTEKIT_UNKNOWN; 105 | } 106 | /* }}} */ 107 | 108 | /* {{{ bytekit_get_opcode_mnemonic */ 109 | static char* bytekit_get_opcode_mnemonic(zend_op_array *zop, zend_op *op TSRMLS_DC) 110 | { 111 | bytekit_define_list *names; 112 | char *postfix = "", *n, *r; 113 | int i; 114 | 115 | switch (op->opcode) { 116 | case ZEND_INCLUDE_OR_EVAL: 117 | switch (Z_LVAL(op->op2.u.constant)) { 118 | case ZEND_INCLUDE: 119 | return estrndup("INCLUDE", sizeof("INCLUDE")-1); 120 | case ZEND_REQUIRE: 121 | return estrndup("REQUIRE", sizeof("REQUIRE")-1); 122 | case ZEND_INCLUDE_ONCE: 123 | return estrndup("INCLUDE_ONCE", sizeof("INCLUDE_ONCE")-1); 124 | case ZEND_REQUIRE_ONCE: 125 | return estrndup("REQUIRE_ONCE", sizeof("REQUIRE_ONCE")-1); 126 | case ZEND_EVAL: 127 | return estrndup("EVAL", sizeof("EVAL")-1); 128 | default: 129 | /* UNKNOWN decode as INCLUDE_OR_EVAL */ 130 | break; 131 | } 132 | break; 133 | case ZEND_ISSET_ISEMPTY_VAR: 134 | { 135 | int ev = op->extended_value & ZEND_ISSET_ISEMPTY_MASK; 136 | if (ev == ZEND_ISSET) return estrndup("ISSET_VAR", sizeof("ISSET_VAR")-1); 137 | if (ev == ZEND_ISEMPTY) return estrndup("ISEMPTY_VAR", sizeof("ISEMPTY_VAR")-1); 138 | } 139 | break; 140 | case ZEND_ISSET_ISEMPTY_DIM_OBJ: 141 | { 142 | int ev = op->extended_value & ZEND_ISSET_ISEMPTY_MASK; 143 | if (ev == ZEND_ISSET) return estrndup("ISSET_DIM_OBJ", sizeof("ISSET_DIM_OBJ")-1); 144 | if (ev == ZEND_ISEMPTY) return estrndup("ISEMPTY_DIM_OBJ", sizeof("ISEMPTY_DIM_OBJ")-1); 145 | } 146 | break; 147 | case ZEND_ISSET_ISEMPTY_PROP_OBJ: 148 | { 149 | int ev = op->extended_value & ZEND_ISSET_ISEMPTY_MASK; 150 | if (ev == ZEND_ISSET) return estrndup("ISSET_PROP_OBJ", sizeof("ISSET_PROP_OBJ")-1); 151 | if (ev == ZEND_ISEMPTY) return estrndup("ISEMPTY_PROP_OBJ", sizeof("ISEMPTY_PROP_OBJ")-1); 152 | } 153 | break; 154 | case ZEND_ASSIGN_ADD: 155 | case ZEND_ASSIGN_SUB: 156 | case ZEND_ASSIGN_MUL: 157 | case ZEND_ASSIGN_DIV: 158 | case ZEND_ASSIGN_MOD: 159 | case ZEND_ASSIGN_SL: 160 | case ZEND_ASSIGN_SR: 161 | case ZEND_ASSIGN_CONCAT: 162 | case ZEND_ASSIGN_BW_OR: 163 | case ZEND_ASSIGN_BW_AND: 164 | case ZEND_ASSIGN_BW_XOR: 165 | if (op->extended_value == ZEND_ASSIGN_OBJ) { 166 | postfix = "_OBJ"; 167 | } else if (op->extended_value == ZEND_ASSIGN_DIM) { 168 | postfix = "_DIM"; 169 | } 170 | n = bytekit_get_opcode_info(op->opcode, NULL, NULL); 171 | if (n == BYTEKIT_UNKNOWN) { 172 | /* should not happen */ 173 | n = "UNKNOWN"; 174 | } 175 | i = strlen(n); 176 | r = emalloc(i + strlen(postfix) + 1); 177 | memcpy(r, n, i); 178 | memcpy(r + i, postfix, strlen(postfix)); 179 | r[i+strlen(postfix)] = 0; 180 | return r; 181 | case ZEND_CAST: 182 | switch (op->extended_value) { 183 | case IS_NULL: 184 | return estrndup("CAST_NULL", sizeof("CAST_NULL")-1); 185 | case IS_BOOL: 186 | return estrndup("CAST_BOOL", sizeof("CAST_BOOL")-1); 187 | case IS_LONG: 188 | return estrndup("CAST_LONG", sizeof("CAST_LONG")-1); 189 | case IS_DOUBLE: 190 | return estrndup("CAST_DOUBLE", sizeof("CAST_DOUBLE")-1); 191 | case IS_STRING: 192 | return estrndup("CAST_STRING", sizeof("CAST_STRING")-1); 193 | case IS_ARRAY: 194 | return estrndup("CAST_ARRAY", sizeof("CAST_ARRAY")-1); 195 | case IS_OBJECT: 196 | return estrndup("CAST_OBJECT", sizeof("CAST_OBJECT")-1); 197 | default: 198 | /* UNKNOWN decode as CAST */ 199 | break; 200 | } 201 | break; 202 | default: 203 | break; 204 | } 205 | 206 | return estrdup(bytekit_get_opcode_info(op->opcode, NULL, NULL)); 207 | } 208 | /* }}} */ 209 | 210 | /* {{{ bytekit_get_opcode_flags */ 211 | static int bytekit_get_opcode_flags(zend_op_array *zop, zend_op *op, long *flags, long *flags2 TSRMLS_DC) 212 | { 213 | bytekit_define_list *names; 214 | 215 | switch (op->opcode) { 216 | case ZEND_INCLUDE_OR_EVAL: 217 | switch (Z_LVAL(op->op2.u.constant)) { 218 | case ZEND_INCLUDE: 219 | case ZEND_REQUIRE: 220 | case ZEND_INCLUDE_ONCE: 221 | case ZEND_REQUIRE_ONCE: 222 | case ZEND_EVAL: 223 | break; 224 | default: 225 | /* UNKNOWN decode as INCLUDE_OR_EVAL */ 226 | break; 227 | } 228 | break; 229 | case ZEND_CAST: 230 | switch (op->extended_value) { 231 | case IS_NULL: 232 | case IS_BOOL: 233 | case IS_LONG: 234 | case IS_DOUBLE: 235 | case IS_STRING: 236 | case IS_ARRAY: 237 | case IS_OBJECT: 238 | break; 239 | default: 240 | /* UNKNOWN decode as CAST */ 241 | break; 242 | } 243 | break; 244 | case ZEND_ASSIGN_ADD: 245 | case ZEND_ASSIGN_SUB: 246 | case ZEND_ASSIGN_MUL: 247 | case ZEND_ASSIGN_DIV: 248 | case ZEND_ASSIGN_MOD: 249 | case ZEND_ASSIGN_SL: 250 | case ZEND_ASSIGN_SR: 251 | case ZEND_ASSIGN_CONCAT: 252 | case ZEND_ASSIGN_BW_OR: 253 | case ZEND_ASSIGN_BW_AND: 254 | case ZEND_ASSIGN_BW_XOR: 255 | bytekit_get_opcode_info(op->opcode, flags, flags2); 256 | if (op->extended_value == ZEND_ASSIGN_OBJ || op->extended_value == ZEND_ASSIGN_DIM) { 257 | *flags2 = BYTEKIT_OP1_CVAR | BYTEKIT_OP2_CVAR; 258 | } 259 | return SUCCESS; 260 | case ZEND_FE_FETCH: 261 | if (op->extended_value & ZEND_FE_FETCH_WITH_KEY) { 262 | *flags = BYTEKIT_RES_WRITE | BYTEKIT_RES_CVAR | BYTEKIT_OP1_CVAR | BYTEKIT_OP2_OPNUM | BYTEKIT_EXT_SPECIAL; 263 | *flags2 = BYTEKIT_RES_WRITE | BYTEKIT_RES_CVAR; 264 | return SUCCESS; 265 | } 266 | *flags = BYTEKIT_RES_WRITE | BYTEKIT_RES_CVAR | BYTEKIT_OP1_CVAR | BYTEKIT_OP2_OPNUM | BYTEKIT_EXT_SPECIAL; 267 | *flags2 = 0; 268 | return SUCCESS; 269 | default: 270 | break; 271 | } 272 | 273 | *flags = *flags2 = 0; 274 | 275 | if (bytekit_get_opcode_info(op->opcode, flags, flags2) == BYTEKIT_UNKNOWN) { 276 | return SUCCESS; 277 | } 278 | return FAILURE; 279 | } 280 | /* }}} */ 281 | 282 | /* {{{ bytekit_get_opcode_oplines */ 283 | static int bytekit_get_opcode_oplines(zend_op_array *zop, zend_op *op TSRMLS_DC) 284 | { 285 | int length = 1; 286 | 287 | switch (op->opcode) { 288 | case ZEND_ASSIGN_OBJ: 289 | case ZEND_ASSIGN_DIM: 290 | length = 2; 291 | break; 292 | case ZEND_INIT_NS_FCALL_BY_NAME: 293 | length = 2; 294 | break; 295 | case ZEND_FE_FETCH: 296 | length = 2; 297 | break; 298 | 299 | case ZEND_ASSIGN_ADD: 300 | case ZEND_ASSIGN_SUB: 301 | case ZEND_ASSIGN_MUL: 302 | case ZEND_ASSIGN_DIV: 303 | case ZEND_ASSIGN_MOD: 304 | case ZEND_ASSIGN_SL: 305 | case ZEND_ASSIGN_SR: 306 | case ZEND_ASSIGN_CONCAT: 307 | case ZEND_ASSIGN_BW_OR: 308 | case ZEND_ASSIGN_BW_AND: 309 | case ZEND_ASSIGN_BW_XOR: 310 | if (op->extended_value == ZEND_ASSIGN_OBJ || op->extended_value == ZEND_ASSIGN_DIM) { 311 | length = 2; 312 | } 313 | } 314 | 315 | if (length > 1) { 316 | if ((op+1)->opcode != ZEND_OP_DATA) { 317 | php_error_docref(NULL TSRMLS_CC, E_WARNING, "bytekit_get_opcode_oplines: ZEND_OP_DATA expected but %u found.", (op+1)->opcode); 318 | } 319 | } 320 | 321 | return length; 322 | } 323 | /* }}} */ 324 | 325 | /* {{{ bytekit_destroy_function */ 326 | static int bytekit_destroy_function(HashTable *function_table, zend_function *function, char *function_name, int function_name_len TSRMLS_DC) 327 | { 328 | /* TODO: properly destroy the function */ 329 | if (zend_hash_del(function_table, function_name, function_name_len) == FAILURE) { 330 | return FAILURE; 331 | } 332 | 333 | return SUCCESS; 334 | } 335 | /* }}} */ 336 | 337 | 338 | /* {{{ bytekit_cleanup_functions */ 339 | static int bytekit_cleanup_functions(HashTable *function_table, int clean_count TSRMLS_DC) 340 | { 341 | HashPosition pos; 342 | 343 | zend_hash_internal_pointer_end_ex(function_table, &pos); 344 | while (clean_count < zend_hash_num_elements(function_table)) { 345 | long func_index; 346 | unsigned int func_name_len; 347 | char *func_name; 348 | zend_function *function; 349 | 350 | if (zend_hash_get_current_data_ex(function_table, (void **)&function, &pos) == FAILURE) { 351 | php_error_docref(NULL TSRMLS_CC, E_ERROR, "bytekit_cleanup_functions: error traversing function table."); 352 | return FAILURE; 353 | } 354 | 355 | if (function->type == ZEND_INTERNAL_FUNCTION) { 356 | /* Inherited internal method */ 357 | zend_hash_move_backwards_ex(function_table, &pos); 358 | clean_count++; 359 | continue; 360 | } else if (function->type != ZEND_USER_FUNCTION) { 361 | php_error_docref(NULL TSRMLS_CC, E_ERROR, "bytekit_cleanup_functions: illegal function entry found."); 362 | return FAILURE; 363 | } 364 | 365 | if (zend_hash_get_current_key_ex(function_table, &func_name, &func_name_len, &func_index, 0, &pos) == HASH_KEY_IS_STRING) { 366 | zend_hash_move_backwards_ex(function_table, &pos); 367 | 368 | if (bytekit_destroy_function(function_table, function, func_name, func_name_len TSRMLS_CC) == FAILURE) { 369 | php_error_docref(NULL TSRMLS_CC, E_ERROR, "bytekit_cleanup_functions: unable to destroy function."); 370 | return FAILURE; 371 | } 372 | } else { 373 | php_error_docref(NULL TSRMLS_CC, E_ERROR, "bytekit_cleanup_functions: illegal function table key found."); 374 | return FAILURE; 375 | } 376 | } 377 | return SUCCESS; 378 | } 379 | /* }}} */ 380 | 381 | 382 | /* {{{ bytekit_destroy_class */ 383 | static int bytekit_destroy_class(HashTable *class_table, zend_class_entry *class_entry, char *class_name, int class_name_len TSRMLS_DC) 384 | { 385 | int rval = SUCCESS; 386 | 387 | /* TODO: properly destroy the class */ 388 | if (zend_hash_num_elements(&(class_entry->function_table)) > 0) { 389 | rval = bytekit_cleanup_functions(&(class_entry->function_table), 0 TSRMLS_CC); 390 | } 391 | 392 | if (zend_hash_del(class_table, class_name, class_name_len) == FAILURE) { 393 | rval = FAILURE; 394 | } 395 | 396 | return rval; 397 | } 398 | /* }}} */ 399 | 400 | 401 | /* {{{ bytekit_cleanup_classes */ 402 | static int bytekit_cleanup_classes(HashTable *class_table, int clean_count TSRMLS_DC) 403 | { 404 | while (clean_count < zend_hash_num_elements(class_table)) { 405 | long class_index; 406 | unsigned int class_name_len; 407 | char *class_name; 408 | zend_class_entry *class_entry, **pce; 409 | 410 | zend_hash_internal_pointer_end(class_table); 411 | if (zend_hash_get_current_data(class_table, (void **)&pce) == FAILURE || !pce || !(*pce)) { 412 | php_error_docref(NULL TSRMLS_CC, E_ERROR, "bytekit_cleanup_classes: unable to traverse class table."); 413 | return FAILURE; 414 | } 415 | class_entry = *pce; 416 | 417 | if (class_entry->type != ZEND_USER_CLASS) { 418 | php_error_docref(NULL TSRMLS_CC, E_ERROR, "bytekit_cleanup_classes: illegal class found in class table."); 419 | return FAILURE; 420 | } 421 | 422 | if (zend_hash_get_current_key_ex(class_table, &class_name, &class_name_len, &class_index, 0, NULL) == HASH_KEY_IS_STRING) { 423 | 424 | if (bytekit_destroy_class(class_table, class_entry, class_name, class_name_len TSRMLS_CC) == FAILURE) { 425 | php_error_docref(NULL TSRMLS_CC, E_ERROR, "bytekit_cleanup_classes: unable to destroy class."); 426 | return FAILURE; 427 | } 428 | } else { 429 | php_error_docref(NULL TSRMLS_CC, E_ERROR, "bytekit_cleanup_classes: illegal class table key found."); 430 | } 431 | } 432 | return SUCCESS; 433 | } 434 | /* }}} */ 435 | 436 | /* {{{ bytekit_cleanup_functions_and_classes */ 437 | static void bytekit_cleanup_functions_and_classes(int original_num_functions, int original_num_classes TSRMLS_DC) 438 | { 439 | 440 | if (original_num_functions < zend_hash_num_elements(EG(function_table))) { 441 | bytekit_cleanup_functions(EG(function_table), original_num_functions TSRMLS_CC); 442 | } 443 | 444 | if (original_num_classes < zend_hash_num_elements(EG(class_table))) { 445 | bytekit_cleanup_classes(EG(class_table), original_num_classes TSRMLS_CC); 446 | } 447 | 448 | } 449 | /* }}} */ 450 | 451 | inline int bytekit_disassemble_zval_null(zval *return_value, zvalue_value value TSRMLS_DC) 452 | { 453 | char *tmpstr; 454 | add_assoc_string(return_value, "string", "null", 1); 455 | return SUCCESS; 456 | } 457 | 458 | inline int bytekit_disassemble_zval_long(zval *return_value, zvalue_value value TSRMLS_DC) 459 | { 460 | char *tmpstr; 461 | spprintf(&tmpstr, 0, "%ld", value.lval); 462 | add_assoc_string(return_value, "string", tmpstr, 0); 463 | return SUCCESS; 464 | } 465 | 466 | inline int bytekit_disassemble_zval_double(zval *return_value, zvalue_value value TSRMLS_DC) 467 | { 468 | char *tmpstr; 469 | spprintf(&tmpstr, 0, "%g", value.dval); 470 | add_assoc_string(return_value, "string", tmpstr, 0); 471 | return SUCCESS; 472 | } 473 | 474 | inline int bytekit_disassemble_zval_string(zval *return_value, zvalue_value value TSRMLS_DC) 475 | { 476 | char *tmpstr; 477 | char *newstr; 478 | if (value.str.len == 0) { 479 | newstr = estrdup(""); 480 | } else { 481 | newstr = php_addcslashes(value.str.val, value.str.len, NULL, 0, "\n\r\t\a\b\f\v\0", 9 TSRMLS_CC); 482 | } 483 | spprintf(&tmpstr, 0, "'%s'", newstr); 484 | efree(newstr); 485 | add_assoc_string(return_value, "string", tmpstr, 0); 486 | return SUCCESS; 487 | } 488 | 489 | inline int bytekit_disassemble_zval_array(zval *return_value, zvalue_value value TSRMLS_DC) 490 | { 491 | char *tmpstr; 492 | spprintf(&tmpstr, 0, ""); 493 | add_assoc_string(return_value, "string", tmpstr, 0); 494 | return SUCCESS; 495 | } 496 | 497 | inline int bytekit_disassemble_zval_object(zval *return_value, zvalue_value value TSRMLS_DC) 498 | { 499 | char *tmpstr; 500 | spprintf(&tmpstr, 0, ""); 501 | add_assoc_string(return_value, "string", tmpstr, 0); 502 | return SUCCESS; 503 | } 504 | 505 | inline int bytekit_disassemble_zval_bool(zval *return_value, zvalue_value value TSRMLS_DC) 506 | { 507 | char *tmpstr; 508 | spprintf(&tmpstr, 0, value.lval ? "true" : "false"); 509 | add_assoc_string(return_value, "string", tmpstr, 0); 510 | return SUCCESS; 511 | } 512 | 513 | inline int bytekit_disassemble_zval_resource(zval *return_value, zvalue_value value TSRMLS_DC) 514 | { 515 | char *tmpstr; 516 | spprintf(&tmpstr, 0, ""); 517 | add_assoc_string(return_value, "string", tmpstr, 0); 518 | return SUCCESS; 519 | } 520 | 521 | inline int bytekit_disassemble_zval_constant(zval *return_value, zvalue_value value TSRMLS_DC) 522 | { 523 | char *tmpstr; 524 | spprintf(&tmpstr, 0, ""); 525 | add_assoc_string(return_value, "string", tmpstr, 0); 526 | return SUCCESS; 527 | } 528 | 529 | inline int bytekit_disassemble_zval_constant_array(zval *return_value, zvalue_value value TSRMLS_DC) 530 | { 531 | char *tmpstr; 532 | spprintf(&tmpstr, 0, ""); 533 | add_assoc_string(return_value, "string", tmpstr, 0); 534 | return SUCCESS; 535 | } 536 | 537 | 538 | int bytekit_disassemble_zval (zval *return_value, zval *val TSRMLS_DC) 539 | { 540 | char *tmpstr; 541 | zval *cval; 542 | 543 | add_assoc_long(return_value, "type", BYTEKIT_TYPE_VALUE); 544 | 545 | MAKE_STD_ZVAL(cval); 546 | *cval = *val; 547 | zval_copy_ctor(cval); 548 | #if PHP_VERSION_ID >= 50300 549 | Z_SET_REFCOUNT_P(cval, 1); 550 | Z_UNSET_ISREF_P(cval); 551 | #else 552 | cval->refcount = 1; 553 | cval->is_ref = 0; 554 | #endif 555 | add_assoc_zval(return_value, "value", cval); 556 | 557 | switch (val->type) { 558 | case IS_NULL: return bytekit_disassemble_zval_null (return_value, val->value TSRMLS_CC); 559 | case IS_LONG: return bytekit_disassemble_zval_long (return_value, val->value TSRMLS_CC); 560 | case IS_DOUBLE: return bytekit_disassemble_zval_double (return_value, val->value TSRMLS_CC); 561 | case IS_STRING: return bytekit_disassemble_zval_string (return_value, val->value TSRMLS_CC); 562 | case IS_ARRAY: return bytekit_disassemble_zval_array (return_value, val->value TSRMLS_CC); 563 | case IS_OBJECT: return bytekit_disassemble_zval_object (return_value, val->value TSRMLS_CC); 564 | case IS_BOOL: return bytekit_disassemble_zval_bool (return_value, val->value TSRMLS_CC); 565 | case IS_RESOURCE: return bytekit_disassemble_zval_resource (return_value, val->value TSRMLS_CC); 566 | case IS_CONSTANT: return bytekit_disassemble_zval_constant (return_value, val->value TSRMLS_CC); 567 | case IS_CONSTANT_ARRAY: return bytekit_disassemble_zval_constant_array (return_value, val->value TSRMLS_CC); 568 | } 569 | spprintf(&tmpstr, 0, ""); 570 | add_assoc_string(return_value, "string", tmpstr, 0); 571 | return SUCCESS; 572 | } 573 | 574 | /* {{{ bytekit_address2string */ 575 | static char *bytekit_address2string(long long address, int cnt TSRMLS_DC) 576 | { 577 | char *res = (char *)emalloc(64); 578 | 579 | if (cnt == 0) { 580 | sprintf(res, "%lld", address); 581 | } else { 582 | sprintf(res, "%0*lld", cnt, address); 583 | } 584 | return res; 585 | } 586 | /* }}} */ 587 | 588 | /* {{{ bytekit_address2hexstring */ 589 | static char *bytekit_address2hexstring(long long address, int cnt TSRMLS_DC) 590 | { 591 | char *res = (char *)emalloc(64); 592 | 593 | if (cnt == 0) { 594 | sprintf(res, "%llx", address); 595 | } else { 596 | sprintf(res, "%0*llx", cnt, address); 597 | } 598 | return res; 599 | } 600 | /* }}} */ 601 | 602 | /* {{{ bytekit_disassemble_node */ 603 | static int bytekit_disassemble_node(zval *return_value, zend_op_array *op_array, znode *node, int i, long long address, long opcode_flags, long options TSRMLS_DC) 604 | { 605 | char *tmpstr; 606 | char *base_address = BYTEKIT_G(opcodes_base) ? BYTEKIT_G(opcodes_base) : op_array->opcodes; 607 | long long dst; 608 | char *dststr; 609 | 610 | array_init(return_value); 611 | 612 | switch (opcode_flags) { 613 | case BYTEKIT_OP_OPNUM: 614 | dst = address - i + node->u.opline_num; 615 | dststr = bytekit_address2hexstring(dst, 0 TSRMLS_CC); 616 | spprintf(&tmpstr, 0, "loc_%s", dststr); 617 | efree(dststr); 618 | add_assoc_string(return_value, "string", tmpstr, 0); 619 | add_assoc_long(return_value, "type", BYTEKIT_TYPE_SYMBOL); 620 | add_assoc_long(return_value, "value", dst); 621 | return SUCCESS; 622 | case BYTEKIT_OP_NUM: 623 | spprintf(&tmpstr, 0, "%d", node->u.opline_num); 624 | add_assoc_string(return_value, "string", tmpstr, 0); 625 | add_assoc_long(return_value, "type", BYTEKIT_TYPE_VALUE); 626 | add_assoc_long(return_value, "value", node->u.opline_num ); 627 | return SUCCESS; 628 | case BYTEKIT_OP_JMPADDR: 629 | dst = address - i + ((char *)node->u.jmp_addr - base_address) / sizeof(zend_op); 630 | dststr = bytekit_address2hexstring(dst, 0 TSRMLS_CC); 631 | spprintf(&tmpstr, 0, "loc_%s", dststr); 632 | efree(dststr); 633 | add_assoc_string(return_value, "string", tmpstr, 0); 634 | add_assoc_long(return_value, "type", BYTEKIT_TYPE_SYMBOL); 635 | add_assoc_long(return_value, "value", dst); 636 | return SUCCESS; 637 | case BYTEKIT_OP_TVAR: 638 | spprintf(&tmpstr, 0, "~%d", node->u.var / sizeof(temp_variable)); 639 | add_assoc_string(return_value, "string", tmpstr, 0); 640 | add_assoc_long(return_value, "type", BYTEKIT_TYPE_TMP_VAR); 641 | add_assoc_long(return_value, "value", node->u.var / sizeof(temp_variable)); 642 | return SUCCESS; 643 | } 644 | 645 | switch (node->op_type) { 646 | case IS_CONST: /* 1 */ 647 | bytekit_disassemble_zval (return_value, &node->u.constant TSRMLS_CC); 648 | return SUCCESS; 649 | case IS_CV: /* 16 */ 650 | spprintf(&tmpstr, 0, "!%d", node->u.var); 651 | add_assoc_string(return_value, "string", tmpstr, 0); 652 | add_assoc_long(return_value, "type", BYTEKIT_TYPE_CV); 653 | add_assoc_long(return_value, "value", node->u.var ); 654 | return SUCCESS; 655 | case IS_TMP_VAR: /* 2 */ 656 | spprintf(&tmpstr, 0, "~%d", node->u.var / sizeof(temp_variable)); 657 | add_assoc_string(return_value, "string", tmpstr, 0); 658 | add_assoc_long(return_value, "type", BYTEKIT_TYPE_TMP_VAR); 659 | add_assoc_long(return_value, "value", node->u.var / sizeof(temp_variable)); 660 | return SUCCESS; 661 | case IS_VAR: /* 4 */ 662 | spprintf(&tmpstr, 0, "$%d", node->u.var / sizeof(temp_variable)); 663 | add_assoc_string(return_value, "string", tmpstr, 0); 664 | add_assoc_long(return_value, "type", BYTEKIT_TYPE_VAR); 665 | add_assoc_long(return_value, "value", node->u.var / sizeof(temp_variable)); 666 | return SUCCESS; 667 | case IS_UNUSED: 668 | return FAILURE; 669 | default: 670 | return SUCCESS; 671 | } 672 | } 673 | /* }}} */ 674 | 675 | 676 | /* {{{ bytekit_disassemble_ext */ 677 | static int bytekit_disassemble_ext(zval *return_value, zend_op_array *op_array, ulong ext, int i, long long address, long opcode_flags, long options TSRMLS_DC) 678 | { 679 | char *tmpstr; 680 | char *base_address = BYTEKIT_G(opcodes_base) ? BYTEKIT_G(opcodes_base) : op_array->opcodes; 681 | long long dst; 682 | char *dststr; 683 | 684 | array_init(return_value); 685 | 686 | switch (opcode_flags) { 687 | case BYTEKIT_EXT_SPECIAL: 688 | if ((options & BYTEKIT_SHOW_SPECIALS) == 0) { 689 | return FAILURE; 690 | } 691 | spprintf(&tmpstr, 0, "s(%d)", ext); 692 | add_assoc_string(return_value, "string", tmpstr, 0); 693 | add_assoc_long(return_value, "type", BYTEKIT_TYPE_VALUE); 694 | add_assoc_long(return_value, "value", ext); 695 | return SUCCESS; 696 | case BYTEKIT_EXT_NUM: 697 | spprintf(&tmpstr, 0, "%d", ext); 698 | add_assoc_string(return_value, "string", tmpstr, 0); 699 | add_assoc_long(return_value, "type", BYTEKIT_TYPE_VALUE); 700 | add_assoc_long(return_value, "value", ext); 701 | return SUCCESS; 702 | case BYTEKIT_EXT_FETCH: 703 | spprintf(&tmpstr, 0, "f(%d)", ext); 704 | add_assoc_string(return_value, "string", tmpstr, 0); 705 | add_assoc_long(return_value, "type", BYTEKIT_TYPE_VALUE); 706 | add_assoc_long(return_value, "value", ext); 707 | return SUCCESS; 708 | case BYTEKIT_EXT_OPNUM: 709 | dst = address - i + ext; 710 | dststr = bytekit_address2hexstring(dst, 0 TSRMLS_CC); 711 | spprintf(&tmpstr, 0, "loc_%s", dststr); 712 | efree(dststr); 713 | add_assoc_string(return_value, "string", tmpstr, 0); 714 | add_assoc_long(return_value, "type", BYTEKIT_TYPE_SYMBOL); 715 | add_assoc_long(return_value, "value", dst); 716 | return SUCCESS; 717 | case BYTEKIT_EXT_TVAR: 718 | spprintf(&tmpstr, 0, "~%d", ext / sizeof(temp_variable)); 719 | add_assoc_string(return_value, "string", tmpstr, 0); 720 | add_assoc_long(return_value, "type", BYTEKIT_TYPE_TMP_VAR); 721 | add_assoc_long(return_value, "value", ext / sizeof(temp_variable)); 722 | return SUCCESS; 723 | } 724 | 725 | return FAILURE; 726 | } 727 | /* }}} */ 728 | 729 | 730 | /* {{{ bytekit_disassemble_op */ 731 | static void bytekit_disassemble_op(zval *return_value, zend_op_array *op_array, zend_op *op, int i, long long address, long options, int *oplines TSRMLS_DC) 732 | { 733 | zval *result, *op1, *op2, *operands, *ext; 734 | long opcode_flags = 0, opcode_flags2 = 0; 735 | int opcode; 736 | char *mnemonic; 737 | 738 | array_init(return_value); 739 | 740 | opcode = op->opcode; 741 | 742 | add_assoc_string(return_value, "address", bytekit_address2string(address, 0 TSRMLS_CC), 0); 743 | add_assoc_long(return_value, "opline", i); 744 | 745 | /* determine mnemonic (including our aliases) and further opcode specific infos */ 746 | mnemonic = bytekit_get_opcode_mnemonic(op_array, op TSRMLS_CC); 747 | add_assoc_string(return_value, "mnemonic", mnemonic, 0); 748 | bytekit_get_opcode_flags(op_array, op, &opcode_flags, &opcode_flags2 TSRMLS_CC); 749 | if (oplines) { 750 | *oplines = bytekit_get_opcode_oplines(op_array, op TSRMLS_CC); 751 | } 752 | 753 | /* initialize operands */ 754 | MAKE_STD_ZVAL(operands); 755 | array_init(operands); 756 | 757 | /* args: result, op1, op2 */ 758 | if ((opcode_flags & BYTEKIT_RES_USED) && ((opcode_flags & BYTEKIT_RES_HIDE)==0) && (!(op->result.u.EA.type & EXT_TYPE_UNUSED) || (opcode_flags & BYTEKIT_RES_FORCE))) { 759 | MAKE_STD_ZVAL(result); 760 | if (bytekit_disassemble_node(result, op_array, &(op->result), i, address, (opcode_flags & BYTEKIT_RES_TYPE_MASK) >> BYTEKIT_RES_SHIFT, options TSRMLS_CC) == SUCCESS) { 761 | add_assoc_long(result, "flags", BYTEKIT_SRC_RES1 | ((opcode_flags & BYTEKIT_RES_WRITE) ? BYTEKIT_OPERAND_WRITTEN : 0)); 762 | add_next_index_zval(operands, result); 763 | } else { 764 | zval_dtor(result); 765 | FREE_ZVAL(result); 766 | } 767 | } 768 | 769 | if ((opcode_flags2 & BYTEKIT_RES_USED) && ((opcode_flags2 & BYTEKIT_RES_HIDE)==0) && !((op+1)->result.u.EA.type & EXT_TYPE_UNUSED)) { 770 | MAKE_STD_ZVAL(result); 771 | if (bytekit_disassemble_node(result, op_array, &((op+1)->result), i, address, (opcode_flags2 & BYTEKIT_RES_TYPE_MASK) >> BYTEKIT_RES_SHIFT, options TSRMLS_CC) == SUCCESS) { 772 | add_assoc_long(result, "flags", BYTEKIT_SRC_RES2 | ((opcode_flags2 & BYTEKIT_RES_WRITE) ? BYTEKIT_OPERAND_WRITTEN : 0)); 773 | add_next_index_zval(operands, result); 774 | } else { 775 | zval_dtor(result); 776 | FREE_ZVAL(result); 777 | } 778 | } 779 | 780 | if ((opcode_flags & BYTEKIT_OP1_USED) && ((opcode_flags & BYTEKIT_OP1_HIDE)==0)) { 781 | MAKE_STD_ZVAL(op1); 782 | if (bytekit_disassemble_node(op1, op_array, &(op->op1), i, address, (opcode_flags & BYTEKIT_OP1_TYPE_MASK) >> BYTEKIT_OP1_SHIFT, options TSRMLS_CC) == SUCCESS) { 783 | add_assoc_long(op1, "flags", BYTEKIT_SRC_OP1 | ((opcode_flags & BYTEKIT_OP1_WRITE) ? BYTEKIT_OPERAND_WRITTEN : 0)); 784 | add_next_index_zval(operands, op1); 785 | } else { 786 | zval_dtor(op1); 787 | FREE_ZVAL(op1); 788 | } 789 | } 790 | 791 | if ((opcode_flags & BYTEKIT_OP2_USED) && ((opcode_flags & BYTEKIT_OP2_HIDE)==0)) { 792 | MAKE_STD_ZVAL(op2); 793 | if (bytekit_disassemble_node(op2, op_array, &(op->op2), i, address, (opcode_flags & BYTEKIT_OP2_TYPE_MASK) >> BYTEKIT_OP2_SHIFT, options TSRMLS_CC) == SUCCESS) { 794 | add_assoc_long(op2, "flags", BYTEKIT_SRC_OP2 | ((opcode_flags & BYTEKIT_OP2_WRITE) ? BYTEKIT_OPERAND_WRITTEN : 0)); 795 | add_next_index_zval(operands, op2); 796 | } else { 797 | zval_dtor(op2); 798 | FREE_ZVAL(op2); 799 | } 800 | } 801 | 802 | if ((opcode_flags & BYTEKIT_EXT_USED) && ((opcode_flags & BYTEKIT_EXT_HIDE)==0)) { 803 | MAKE_STD_ZVAL(ext); 804 | if (bytekit_disassemble_ext(ext, op_array, op->extended_value, i, address, opcode_flags & BYTEKIT_EXT_TYPE_MASK, options TSRMLS_CC) == SUCCESS) { 805 | add_assoc_long(ext, "flags", BYTEKIT_SRC_EXT1); 806 | add_next_index_zval(operands, ext); 807 | } else { 808 | zval_dtor(ext); 809 | FREE_ZVAL(ext); 810 | } 811 | } 812 | 813 | if ((opcode_flags2 & BYTEKIT_OP1_USED) && ((opcode_flags2 & BYTEKIT_OP1_HIDE)==0)) { 814 | MAKE_STD_ZVAL(op1); 815 | if (bytekit_disassemble_node(op1, op_array, &((op+1)->op1), i, address, (opcode_flags2 & BYTEKIT_OP1_TYPE_MASK) >> BYTEKIT_OP1_SHIFT, options TSRMLS_CC) == SUCCESS) { 816 | add_assoc_long(op1, "flags", BYTEKIT_SRC_OP3 | ((opcode_flags2 & BYTEKIT_OP1_WRITE) ? BYTEKIT_OPERAND_WRITTEN : 0)); 817 | add_next_index_zval(operands, op1); 818 | } else { 819 | zval_dtor(op1); 820 | FREE_ZVAL(op1); 821 | } 822 | } 823 | 824 | if ((opcode_flags2 & BYTEKIT_OP2_USED) && ((opcode_flags2 & BYTEKIT_OP2_HIDE)==0)) { 825 | MAKE_STD_ZVAL(op2); 826 | if (bytekit_disassemble_node(op2, op_array, &((op+1)->op2), i, address, (opcode_flags2 & BYTEKIT_OP2_TYPE_MASK) >> BYTEKIT_OP2_SHIFT, options TSRMLS_CC) == SUCCESS) { 827 | add_assoc_long(op2, "flags", BYTEKIT_SRC_OP4 | ((opcode_flags & BYTEKIT_OP2_WRITE) ? BYTEKIT_OPERAND_WRITTEN : 0)); 828 | add_next_index_zval(operands, op2); 829 | } else { 830 | zval_dtor(op2); 831 | FREE_ZVAL(op2); 832 | } 833 | } 834 | 835 | if ((opcode_flags2 & BYTEKIT_EXT_USED) && ((opcode_flags2 & BYTEKIT_EXT_HIDE)==0)) { 836 | MAKE_STD_ZVAL(ext); 837 | if (bytekit_disassemble_ext(ext, op_array, (op+1)->extended_value, i, address, opcode_flags2 & BYTEKIT_EXT_TYPE_MASK, options TSRMLS_CC) == SUCCESS) { 838 | add_assoc_long(ext, "flags", BYTEKIT_SRC_EXT2); 839 | add_next_index_zval(operands, ext); 840 | } else { 841 | zval_dtor(ext); 842 | FREE_ZVAL(ext); 843 | } 844 | } 845 | 846 | add_assoc_zval(return_value, "operands", operands); 847 | } 848 | /* }}} */ 849 | 850 | 851 | 852 | 853 | static void bytekit_get_next_oplines(zend_op_array *ops, zend_op *op, int i, int len, int *next_t, int *next_f, int *exception TSRMLS_DC) 854 | { 855 | int tc; 856 | char *opcodes_base = BYTEKIT_G(opcodes_base) ? BYTEKIT_G(opcodes_base) : ops->opcodes; 857 | 858 | /* first assume we end */ 859 | *next_t = -1; 860 | *next_f = -1; 861 | *exception = -1; 862 | 863 | /* next line(s) depend(s) on opcodes */ 864 | switch (op->opcode) { 865 | default: 866 | *next_t = i + len; 867 | break; 868 | case ZEND_GOTO: 869 | case ZEND_JMP: 870 | *next_t = (op->op1.u.jmp_addr - (zend_op *)opcodes_base); 871 | break; 872 | 873 | /* case ZEND_FE_RESET: <- handling FE_RESET correctly makes CFG unnecessary complex */ 874 | case ZEND_FE_FETCH: 875 | *next_t = i + len; 876 | *next_f = op->op2.u.opline_num; 877 | break; 878 | 879 | case ZEND_JMPZ: 880 | case ZEND_JMPNZ: 881 | case ZEND_JMPZ_EX: 882 | case ZEND_JMPNZ_EX: 883 | *next_f = i + len; 884 | *next_t = (op->op2.u.jmp_addr - (zend_op *)opcodes_base); 885 | break; 886 | 887 | case ZEND_JMPZNZ: 888 | *next_t = op->extended_value; 889 | *next_f = op->op2.u.opline_num; 890 | break; 891 | 892 | case ZEND_BRK: 893 | case ZEND_CONT: 894 | { 895 | zend_brk_cont_element *jmp_to; 896 | int array_offset = op->op1.u.opline_num; 897 | int nest_level = 1; 898 | do { 899 | if (array_offset < 0 || array_offset >= ops->last_brk_cont) { 900 | break; 901 | } 902 | jmp_to = &ops->brk_cont_array[array_offset]; 903 | array_offset = jmp_to->parent; 904 | } while (--nest_level > 0); 905 | if (nest_level == 1) { 906 | break; 907 | } 908 | if (op->opcode == ZEND_BRK) { 909 | *next_t = jmp_to->brk; 910 | } else { 911 | *next_t = jmp_to->cont; 912 | } 913 | } 914 | break; 915 | 916 | case ZEND_EXIT: 917 | case ZEND_RETURN: 918 | break; 919 | 920 | case ZEND_HANDLE_EXCEPTION: 921 | break; 922 | 923 | case ZEND_THROW: 924 | /* search the try/catch array for the correct catch block */ 925 | for (tc=0; tclast_try_catch; tc++) { 926 | if (ops->try_catch_array[tc].try_op > i) { 927 | /* ignore all further blocks */ 928 | break; 929 | } 930 | if (i >= ops->try_catch_array[tc].try_op && i < ops->try_catch_array[tc].catch_op) { 931 | *exception = ops->try_catch_array[tc].catch_op; 932 | } else { 933 | php_error_docref(NULL TSRMLS_CC, E_WARNING, "bytekit_get_next_oplines: found throw outside of try/catch"); 934 | } 935 | } 936 | break; 937 | } 938 | 939 | /* special handling of last opcode before catch branch */ 940 | for (tc=0; tclast_try_catch; tc++) { 941 | if (ops->try_catch_array[tc].try_op > i) { 942 | /* ignore all further blocks */ 943 | break; 944 | } 945 | if (ops->try_catch_array[tc].catch_op == i+1) { 946 | *exception = ops->try_catch_array[tc].catch_op; 947 | } 948 | } 949 | } 950 | 951 | /* {{{ bytekit_build_basic_blocks */ 952 | static void bytekit_build_basic_blocks(zval *return_value, zend_op_array *ops, long flags TSRMLS_DC) 953 | { 954 | zend_op *op; 955 | int i = 0; 956 | int *entries, *exits, *bb, *next_f, *next_t, *exception; 957 | zval *a_entries, *a_exits, *a_bb, *a_next_f, *a_next_t, *a_exception, *a_cfg, **a_cfg_elements; 958 | unsigned int numopcodes; 959 | int oplines, bbid; 960 | 961 | /* Allocate slots for all opcodes */ 962 | numopcodes = ops->last; 963 | entries = (int *) ecalloc(numopcodes, sizeof(int)); 964 | exits = (int *) ecalloc(numopcodes, sizeof(int)); 965 | bb = (int *) ecalloc(numopcodes, sizeof(int)); 966 | next_t = (int *) ecalloc(numopcodes, sizeof(int)); 967 | next_f = (int *) ecalloc(numopcodes, sizeof(int)); 968 | exception = (int *) ecalloc(numopcodes, sizeof(int)); 969 | 970 | /* determine the number of entries and exits */ 971 | /* opcodes with zero or more than 1 entries */ 972 | /* start a new basic block, opcodes with zero */ 973 | /* or more than 1 exits end a basic block */ 974 | for (op = ops->opcodes, i = 0; op && i < ops->last; op++, i++) { 975 | oplines = bytekit_get_opcode_oplines(ops, op TSRMLS_CC); 976 | 977 | bytekit_get_next_oplines(ops, op, i, oplines, &next_t[i], &next_f[i], &exception[i] TSRMLS_CC); 978 | 979 | if (next_t[i] != -1) { 980 | exits[i]++; 981 | entries[next_t[i]]++; 982 | } 983 | 984 | if (next_f[i] != -1) { 985 | exits[i]++; 986 | entries[next_f[i]]++; 987 | } 988 | 989 | if (exception[i] != -1) { 990 | exits[i]++; 991 | entries[exception[i]]++; 992 | } 993 | 994 | while (oplines > 1) { 995 | i++; 996 | entries[i] = -1; 997 | exits[i] = -1; 998 | op++; 999 | oplines--; 1000 | } 1001 | } 1002 | 1003 | /* fill in the basic block number */ 1004 | for (op = ops->opcodes, i = 0, bbid = 1; op && i < ops->last; op++, i++) { 1005 | if (i > 0 && exits[i-1] == 0) { 1006 | bbid++; 1007 | } else if (i > 0 && exits[i-1] > 1) { 1008 | bbid++; 1009 | } else if (i > 0 && entries[i] == 1) { 1010 | int z = i-1; 1011 | while (z > 0 && entries[z] == -1) z--; 1012 | if (next_t[z] != i && next_f[z] != i) { 1013 | bbid++; 1014 | } 1015 | } else if (i > 0 && entries[i] == 0) { 1016 | bbid++; 1017 | } else if (i > 0 && entries[i] > 1) { 1018 | bbid++; 1019 | } 1020 | bb[i] = bbid; 1021 | } 1022 | 1023 | /* create a simple CFG as nested array */ 1024 | MAKE_STD_ZVAL(a_cfg); 1025 | array_init(a_cfg); 1026 | a_cfg_elements = (zval **) ecalloc(bbid, sizeof(zval *)); 1027 | for (i=0; iopcodes, i=0; op && i < ops->last; op++, i++) { 1032 | int type = (exits[i]-(exception[i]!=-1?1:0)) == 2 ? BYTEKIT_EDGE_TRUE : BYTEKIT_EDGE_NORMAL; 1033 | if (entries[i] == -1 || exits[i] < 1) { 1034 | continue; 1035 | } 1036 | 1037 | if (next_t[i] != -1) { 1038 | /* jump to another basic block? */ 1039 | if ((bb[i] != bb[next_t[i]]) || (bb[i]==bb[next_t[i]] && (i > next_t[i]))) { 1040 | add_index_long(a_cfg_elements[bb[i]-1],bb[next_t[i]], type); 1041 | } 1042 | } 1043 | if (next_f[i] != -1) { 1044 | /* jump to another basic block? */ 1045 | if ((bb[i] != bb[next_f[i]]) || (bb[i]==bb[next_f[i]] && (i > next_f[i]))) { 1046 | add_index_long(a_cfg_elements[bb[i]-1],bb[next_f[i]], BYTEKIT_EDGE_FALSE); 1047 | } 1048 | } 1049 | if (exception[i] != -1) { 1050 | /* jump to another basic block? */ 1051 | if ((bb[i] != bb[exception[i]]) || (bb[i]==bb[exception[i]] && (i > exception[i]))) { 1052 | add_index_long(a_cfg_elements[bb[i]-1],bb[exception[i]], BYTEKIT_EDGE_EXCEPTION); 1053 | } 1054 | } 1055 | } 1056 | for (i=0; iopcodes, i = 0, bbid = 0; op && i < ops->last; op++, i++) { 1077 | if (entries[i] != -1) { 1078 | add_next_index_long(a_entries, entries[i]); 1079 | add_next_index_long(a_exits, exits[i]); 1080 | add_next_index_long(a_bb, bb[i]); 1081 | add_next_index_long(a_next_t, next_t[i]); 1082 | add_next_index_long(a_next_f, next_f[i]); 1083 | add_next_index_long(a_exception, exception[i]); 1084 | } 1085 | } 1086 | add_assoc_zval(return_value, "entries", a_entries); 1087 | add_assoc_zval(return_value, "exits", a_exits); 1088 | add_assoc_zval(return_value, "bb", a_bb); 1089 | add_assoc_zval(return_value, "next_t", a_next_t); 1090 | add_assoc_zval(return_value, "next_f", a_next_f); 1091 | add_assoc_zval(return_value, "exception", a_exception); 1092 | 1093 | /* free variables */ 1094 | efree(entries); 1095 | efree(exits); 1096 | efree(bb); 1097 | efree(next_t); 1098 | efree(next_f); 1099 | efree(exception); 1100 | } 1101 | /* }}} */ 1102 | 1103 | long long base_address = 1024; 1104 | 1105 | /* {{{ bytekit_raw_arginfo */ 1106 | static void bytekit_raw_arginfo(zval *return_value, zend_uint num_args, zend_arg_info *arginfo, long flags TSRMLS_DC) 1107 | { 1108 | zend_uint i; 1109 | 1110 | array_init(return_value); 1111 | 1112 | for (i = 0; i < num_args; i++) { 1113 | zval *tmpzval; 1114 | 1115 | MAKE_STD_ZVAL(tmpzval); 1116 | array_init(tmpzval); 1117 | add_assoc_stringl(tmpzval, "name", arginfo[i].name, arginfo[i].name_len, 1); 1118 | if (arginfo[i].class_name_len) { 1119 | add_assoc_stringl(tmpzval, "class_name", arginfo[i].class_name, arginfo[i].class_name_len, 1); 1120 | } else { 1121 | add_assoc_null(tmpzval, "class_name"); 1122 | } 1123 | add_assoc_bool(tmpzval, "allow_null", arginfo[i].allow_null); 1124 | add_assoc_bool(tmpzval, "pass_by_reference", arginfo[i].pass_by_reference); 1125 | add_assoc_bool(tmpzval, "array_type_hint", arginfo[i].array_type_hint); 1126 | 1127 | add_next_index_zval(return_value, tmpzval); 1128 | } 1129 | } 1130 | /* }}} */ 1131 | 1132 | /* {{{ bytekit_raw_node */ 1133 | static void bytekit_raw_node(zval *return_value, zend_op_array *op_array, znode *node, long opcode_flags, long options TSRMLS_DC) 1134 | { 1135 | char *opcodes_base = BYTEKIT_G(opcodes_base) ? BYTEKIT_G(opcodes_base) : op_array->opcodes; 1136 | 1137 | array_init(return_value); 1138 | add_assoc_long(return_value, "type", node->op_type); 1139 | 1140 | add_assoc_long(return_value, "EA.var", node->u.EA.var); 1141 | add_assoc_long(return_value, "EA.type", node->u.EA.type); 1142 | 1143 | if (node->op_type == IS_CONST) { 1144 | zval *tmpzval; 1145 | MAKE_STD_ZVAL(tmpzval); 1146 | *tmpzval = node->u.constant; 1147 | zval_copy_ctor(tmpzval); 1148 | #if PHP_VERSION_ID >= 50300 1149 | tmpzval->refcount__gc = 1; 1150 | #else 1151 | tmpzval->refcount = 1; 1152 | #endif 1153 | if (tmpzval->type == IS_CONSTANT_ARRAY) tmpzval->type = IS_ARRAY; 1154 | if (tmpzval->type == IS_CONSTANT) tmpzval->type = IS_STRING; 1155 | add_assoc_zval(return_value, "constant", tmpzval); 1156 | } else if (node->op_type == IS_CV) { 1157 | add_assoc_long(return_value, "var", node->u.var); 1158 | } else { 1159 | 1160 | add_assoc_long(return_value, "var", node->u.var / sizeof(temp_variable)); 1161 | 1162 | switch (opcode_flags) { 1163 | case BYTEKIT_OP_OPNUM: 1164 | add_assoc_long(return_value, "opline", node->u.opline_num); 1165 | break; 1166 | case BYTEKIT_OP_NUM: 1167 | add_assoc_long(return_value, "number", node->u.opline_num); 1168 | break; 1169 | case BYTEKIT_OP_JMPADDR: 1170 | add_assoc_long(return_value, "opline", ((char *)node->u.jmp_addr - opcodes_base) / sizeof(zend_op)); 1171 | break; 1172 | case BYTEKIT_OP_TVAR: 1173 | break; 1174 | } 1175 | add_assoc_long(return_value, "constant", Z_LVAL(node->u.constant)); 1176 | } 1177 | } 1178 | /* }}} */ 1179 | 1180 | 1181 | /* {{{ bytekit_raw_op */ 1182 | static void bytekit_raw_op(zval *return_value, zend_op_array *op_array, zend_op *op, long options TSRMLS_DC) 1183 | { 1184 | zval *result, *op1, *op2; 1185 | long opcode_flags = 0, opcode_flags2 = 0; 1186 | int i, opcode; 1187 | 1188 | array_init(return_value); 1189 | 1190 | opcode = op->opcode; 1191 | add_assoc_long(return_value, "opcode", opcode); 1192 | 1193 | bytekit_get_opcode_flags(op_array, op, &opcode_flags, &opcode_flags2 TSRMLS_CC); 1194 | add_assoc_long(return_value, "flags", opcode_flags); 1195 | 1196 | if (opcode_flags & BYTEKIT_RES_USED) { 1197 | MAKE_STD_ZVAL(result); 1198 | bytekit_raw_node(result, op_array, &(op->result), (opcode_flags & BYTEKIT_RES_USED) >> BYTEKIT_RES_SHIFT, options TSRMLS_CC); 1199 | add_assoc_zval(return_value, "result", result); 1200 | } else { 1201 | add_assoc_null(return_value, "result"); 1202 | } 1203 | 1204 | if (opcode_flags & BYTEKIT_OP1_USED) { 1205 | MAKE_STD_ZVAL(op1); 1206 | bytekit_raw_node(op1, op_array, &(op->op1), (opcode_flags & BYTEKIT_OP1_USED) >> BYTEKIT_OP1_SHIFT, options TSRMLS_CC); 1207 | add_assoc_zval(return_value, "op1", op1); 1208 | } else { 1209 | add_assoc_null(return_value, "op1"); 1210 | } 1211 | 1212 | if (opcode_flags & BYTEKIT_OP2_USED) { 1213 | MAKE_STD_ZVAL(op2); 1214 | bytekit_raw_node(op2, op_array, &(op->op2), (opcode_flags & BYTEKIT_OP2_USED) >> BYTEKIT_OP2_SHIFT, options TSRMLS_CC); 1215 | add_assoc_zval(return_value, "op2", op2); 1216 | } else { 1217 | add_assoc_null(return_value, "op2"); 1218 | } 1219 | 1220 | add_assoc_long(return_value, "extended_value", op->extended_value); 1221 | add_assoc_long(return_value, "lineno", op->lineno); 1222 | } 1223 | /* }}} */ 1224 | 1225 | /* {{{ bytekit_disassemble_op_array */ 1226 | static void bytekit_disassemble_op_array(zval *return_value, zend_op_array *ops, long flags TSRMLS_DC) 1227 | { 1228 | zend_op *op; 1229 | zval *tmpzval, *raw; 1230 | int i = 0; 1231 | long long address = 0; 1232 | 1233 | array_init(return_value); 1234 | 1235 | MAKE_STD_ZVAL(raw); 1236 | array_init(raw); 1237 | 1238 | /* determine address from zend_op_array information */ 1239 | /* for now just increase */ 1240 | address = base_address; 1241 | base_address += ops->last; 1242 | base_address += (ops->last % 1024)==0 ? 0 : 1024 - (ops->last % 1024); 1243 | 1244 | MAKE_STD_ZVAL(tmpzval); 1245 | array_init(tmpzval); 1246 | for (op = ops->opcodes, i = 0; op && i < ops->last; op++, i++) { 1247 | zval *zop; 1248 | int oplines; 1249 | 1250 | MAKE_STD_ZVAL(zop); 1251 | bytekit_disassemble_op(zop, ops, op, i, address+i, flags, &oplines TSRMLS_CC); 1252 | add_next_index_zval(tmpzval, zop); 1253 | 1254 | /* handle multi opline opcodes correctly */ 1255 | i += oplines - 1; 1256 | while (oplines > 1) { 1257 | op++; oplines--; 1258 | } 1259 | } 1260 | add_assoc_zval(return_value, "code", tmpzval); 1261 | bytekit_build_basic_blocks(return_value, ops, flags TSRMLS_CC); 1262 | 1263 | 1264 | 1265 | /********************************************************************************/ 1266 | /* Now fill in raw information about the op_array similar to what parsekit does */ 1267 | /********************************************************************************/ 1268 | 1269 | add_assoc_long(raw, "type", (long)(ops->type)); 1270 | if (ops->function_name) { 1271 | add_assoc_string(raw, "function_name", ops->function_name, 1); 1272 | } else { 1273 | add_assoc_null(raw, "function_name"); 1274 | } 1275 | 1276 | if (ops->scope && ops->scope->name) { 1277 | add_assoc_stringl(raw, "scope", ops->scope->name, ops->scope->name_length, 1); 1278 | } else { 1279 | add_assoc_null(raw, "scope"); 1280 | } 1281 | add_assoc_long(raw, "fn_flags", ops->fn_flags); 1282 | 1283 | add_assoc_long(raw, "num_args", ops->num_args); 1284 | add_assoc_long(raw, "required_num_args", ops->required_num_args); 1285 | add_assoc_bool(raw, "pass_rest_by_reference", ops->pass_rest_by_reference); 1286 | 1287 | 1288 | if (ops->last_try_catch > 0) { 1289 | MAKE_STD_ZVAL(tmpzval); 1290 | array_init(tmpzval); 1291 | for (i = 0; i < ops->last_try_catch; i++) { 1292 | zval *tmp_zval; 1293 | 1294 | MAKE_STD_ZVAL(tmp_zval); 1295 | array_init(tmp_zval); 1296 | add_assoc_long(tmp_zval, "try_op", ops->try_catch_array[i].try_op); 1297 | add_assoc_long(tmp_zval, "catch_op", ops->try_catch_array[i].catch_op); 1298 | add_index_zval(tmpzval, i, tmp_zval); 1299 | } 1300 | add_assoc_zval(raw, "try_catch_array", tmpzval); 1301 | } else { 1302 | add_assoc_null(raw, "try_catch_array"); 1303 | } 1304 | 1305 | /* TODO: not supported by PHP 5.3 */ 1306 | /* add_assoc_bool(raw, "uses_this", ops->uses_this); */ 1307 | add_assoc_long(raw, "line_start", ops->line_start); 1308 | add_assoc_long(raw, "line_end", ops->line_end); 1309 | 1310 | if (ops->doc_comment && ops->doc_comment_len) { 1311 | add_assoc_stringl(raw, "doc_comment", ops->doc_comment, ops->doc_comment_len, 1); 1312 | } else { 1313 | add_assoc_null(raw, "doc_comment"); 1314 | } 1315 | 1316 | add_assoc_bool(raw, "return_reference", ops->return_reference); 1317 | add_assoc_long(raw, "refcount", *(ops->refcount)); 1318 | add_assoc_long(raw, "last", ops->last); 1319 | add_assoc_long(raw, "size", ops->size); 1320 | add_assoc_long(raw, "T", ops->T); 1321 | add_assoc_long(raw, "last_brk_cont", ops->last_brk_cont); 1322 | add_assoc_long(raw, "current_brk_cont", ops->current_brk_cont); 1323 | add_assoc_long(raw, "backpatch_count", ops->backpatch_count); 1324 | add_assoc_bool(raw, "done_pass_two", ops->done_pass_two); 1325 | 1326 | if (ops->last_brk_cont > 0) { 1327 | MAKE_STD_ZVAL(tmpzval); 1328 | array_init(tmpzval); 1329 | for (i = 0; i < ops->last_brk_cont; i++) { 1330 | zval *tmp_zval; 1331 | 1332 | MAKE_STD_ZVAL(tmp_zval); 1333 | array_init(tmp_zval); 1334 | add_assoc_long(tmp_zval, "start", ops->brk_cont_array[i].start); 1335 | add_assoc_long(tmp_zval, "cont", ops->brk_cont_array[i].cont); 1336 | add_assoc_long(tmp_zval, "brk", ops->brk_cont_array[i].brk); 1337 | add_assoc_long(tmp_zval, "parent", ops->brk_cont_array[i].parent); 1338 | add_index_zval(tmpzval, i, tmp_zval); 1339 | } 1340 | add_assoc_zval(raw, "brk_cont_array", tmpzval); 1341 | } else { 1342 | add_assoc_null(raw, "brk_cont_array"); 1343 | } 1344 | 1345 | if (ops->vars) { 1346 | MAKE_STD_ZVAL(tmpzval); 1347 | array_init(tmpzval); 1348 | for (i = 0; i < ops->last_var; i++) { 1349 | add_index_stringl(tmpzval, i, ops->vars[i].name, ops->vars[i].name_len, 1); 1350 | } 1351 | add_assoc_zval(raw, "cv", tmpzval); 1352 | } else { 1353 | add_assoc_null(raw, "cv"); 1354 | } 1355 | 1356 | if (ops->static_variables) { 1357 | zval *tmp_zval; 1358 | 1359 | MAKE_STD_ZVAL(tmpzval); 1360 | array_init(tmpzval); 1361 | zend_hash_copy(HASH_OF(tmpzval), ops->static_variables, (copy_ctor_func_t) zval_add_ref, (void *) &tmp_zval, sizeof(zval *)); 1362 | add_assoc_zval(raw, "static_variables", tmpzval); 1363 | } else { 1364 | add_assoc_null(raw, "static_variables"); 1365 | } 1366 | 1367 | if (ops->start_op) { 1368 | char sop[(sizeof(void *) * 2) + 1]; 1369 | 1370 | snprintf(sop, sizeof(sop), "%X", (unsigned int)ops->start_op); 1371 | add_assoc_string(raw, "start_op", sop, 1); 1372 | } else { 1373 | add_assoc_null(raw, "start_op"); 1374 | } 1375 | 1376 | if (ops->filename) { 1377 | add_assoc_string(raw, "filename", ops->filename, 1); 1378 | } else { 1379 | add_assoc_null(raw, "filename"); 1380 | } 1381 | 1382 | MAKE_STD_ZVAL(tmpzval); 1383 | array_init(tmpzval); 1384 | for (op = ops->opcodes, i = 0; op && i < ops->last; op++, i++) { 1385 | zval *zop; 1386 | 1387 | MAKE_STD_ZVAL(zop); 1388 | bytekit_raw_op(zop, ops, op, flags TSRMLS_CC); 1389 | add_next_index_zval(tmpzval, zop); 1390 | } 1391 | add_assoc_zval(raw, "opcodes", tmpzval); 1392 | 1393 | add_assoc_zval(return_value, "raw", raw); 1394 | } 1395 | /* }}} */ 1396 | 1397 | /* {{{ bytekit disassemble_functions */ 1398 | static int bytekit_disassemble_functions(zval *return_value, char *scope, zval *methods, HashTable *function_table, int count, long flags TSRMLS_DC) 1399 | { 1400 | HashPosition pos; 1401 | 1402 | zval **functions, **raw; 1403 | HashTable *ht = HASH_OF(return_value); 1404 | 1405 | if (ht && zend_hash_find(ht, "functions", sizeof("functions"), (void **)&functions) == FAILURE) { 1406 | 1407 | } 1408 | 1409 | zend_hash_internal_pointer_end_ex(function_table, &pos); 1410 | while (count < zend_hash_num_elements(function_table)) { 1411 | zend_function *function; 1412 | zval *function_ops; 1413 | char *name; 1414 | 1415 | if (zend_hash_get_current_data_ex(function_table, (void **)&function, &pos) == FAILURE) { 1416 | php_error_docref(NULL TSRMLS_CC, E_ERROR, "bytekit_disassemble_functions: unable to traverse function table."); 1417 | return FAILURE; 1418 | } 1419 | zend_hash_move_backwards_ex(function_table, &pos); 1420 | count++; 1421 | 1422 | if (function->type == ZEND_INTERNAL_FUNCTION) { 1423 | continue; 1424 | } else if (function->type != ZEND_USER_FUNCTION) { 1425 | php_error_docref(NULL TSRMLS_CC, E_ERROR, "bytekit_disassemble_functions: illegal function entry found"); 1426 | return FAILURE; 1427 | } 1428 | if (methods != NULL) { 1429 | add_next_index_string(methods, function->common.function_name, 1); 1430 | } 1431 | 1432 | MAKE_STD_ZVAL(function_ops); 1433 | bytekit_disassemble_op_array(function_ops, &(function->op_array), flags TSRMLS_CC); 1434 | if (scope == NULL) { 1435 | name = estrdup(function->common.function_name); 1436 | } else { 1437 | spprintf(&name, 0, "%s::%s", scope, function->common.function_name); 1438 | } 1439 | 1440 | /* NEW HANDLING OF ARG_INFO */ 1441 | 1442 | if (zend_hash_find(HASH_OF(function_ops), "raw", sizeof("raw"), (void **)&raw) == SUCCESS) { 1443 | zval *tmpzval; 1444 | if (function->common.num_args && function->common.arg_info) { 1445 | MAKE_STD_ZVAL(tmpzval); 1446 | bytekit_raw_arginfo(tmpzval, function->common.num_args, function->common.arg_info, flags TSRMLS_CC); 1447 | add_assoc_zval(*raw, "arg_info", tmpzval); 1448 | } else { 1449 | add_assoc_null(*raw, "arg_info"); 1450 | } 1451 | } 1452 | 1453 | add_assoc_zval(*functions, name, function_ops); 1454 | efree(name); 1455 | } 1456 | 1457 | return SUCCESS; 1458 | } 1459 | /* }}} */ 1460 | 1461 | /* {{{ bytekit_disassemble_class_entry */ 1462 | static int bytekit_disassemble_class_entry(zval *return_value, zval *raw_class, zend_class_entry *ce, long flags TSRMLS_DC) 1463 | { 1464 | zval *tmpzval; 1465 | int i; 1466 | char *classname = estrndup(ce->name, ce->name_length); 1467 | 1468 | add_assoc_long(raw_class, "type", ce->type); 1469 | add_assoc_stringl(raw_class, "name", ce->name, ce->name_length, 1); 1470 | if (ce->parent) { 1471 | add_assoc_stringl(raw_class, "parent", ce->parent->name, ce->parent->name_length, 1); 1472 | } else { 1473 | add_assoc_null(raw_class, "parent"); 1474 | } 1475 | add_assoc_bool(raw_class, "constants_updated", ce->constants_updated); 1476 | 1477 | add_assoc_long(raw_class, "ce_flags", ce->ce_flags); 1478 | 1479 | if (ce->constructor) { 1480 | add_assoc_string(raw_class, "constructor", ce->constructor->common.function_name, 1); 1481 | } else { 1482 | add_assoc_null(raw_class, "constructor"); 1483 | } 1484 | 1485 | if (ce->clone) { 1486 | add_assoc_string(raw_class, "clone", ce->clone->common.function_name, 1); 1487 | } else { 1488 | add_assoc_null(raw_class, "clone"); 1489 | } 1490 | 1491 | if (ce->__get) { 1492 | add_assoc_string(raw_class, "__get", ce->__get->common.function_name, 1); 1493 | } else { 1494 | add_assoc_null(raw_class, "__get"); 1495 | } 1496 | 1497 | if (ce->__set) { 1498 | add_assoc_string(raw_class, "__set", ce->__set->common.function_name, 1); 1499 | } else { 1500 | add_assoc_null(raw_class, "__set"); 1501 | } 1502 | 1503 | if (ce->__call) { 1504 | add_assoc_string(raw_class, "__call", ce->__call->common.function_name, 1); 1505 | } else { 1506 | add_assoc_null(raw_class, "__call"); 1507 | } 1508 | 1509 | if (zend_hash_num_elements(&(ce->properties_info)) > 0) { 1510 | zend_property_info *property_info; 1511 | 1512 | MAKE_STD_ZVAL(tmpzval); 1513 | array_init(tmpzval); 1514 | for (zend_hash_internal_pointer_reset(&(ce->properties_info)); 1515 | zend_hash_get_current_data(&(ce->properties_info), (void **)&property_info) == SUCCESS; 1516 | zend_hash_move_forward(&(ce->properties_info))) { 1517 | zval *tmp_zval; 1518 | 1519 | MAKE_STD_ZVAL(tmp_zval); 1520 | array_init(tmp_zval); 1521 | add_assoc_long(tmp_zval, "flags", property_info->flags); 1522 | add_assoc_stringl(tmp_zval, "name", property_info->name, property_info->name_length, 1); 1523 | add_next_index_zval(tmpzval, tmp_zval); 1524 | } 1525 | add_assoc_zval(raw_class, "properties_info", tmpzval); 1526 | } else { 1527 | add_assoc_null(raw_class, "properties_info"); 1528 | } 1529 | 1530 | if (ce->static_members && zend_hash_num_elements(ce->static_members) > 0) { 1531 | zval *tmp_zval; 1532 | 1533 | MAKE_STD_ZVAL(tmpzval); 1534 | array_init(tmpzval); 1535 | zend_hash_copy(HASH_OF(tmpzval), ce->static_members, (copy_ctor_func_t) zval_add_ref, (void *) &tmp_zval, sizeof(zval *)); 1536 | add_assoc_zval(raw_class, "static_members", tmpzval); 1537 | } else { 1538 | add_assoc_null(raw_class, "static_members"); 1539 | } 1540 | 1541 | if (zend_hash_num_elements(&(ce->constants_table)) > 0) { 1542 | zval *tmp_zval; 1543 | 1544 | MAKE_STD_ZVAL(tmpzval); 1545 | array_init(tmpzval); 1546 | zend_hash_copy(HASH_OF(tmpzval), &(ce->constants_table), (copy_ctor_func_t) zval_add_ref, (void *) &tmp_zval, sizeof(zval *)); 1547 | add_assoc_zval(raw_class, "constants_table", tmpzval); 1548 | } else { 1549 | add_assoc_null(raw_class, "constants_table"); 1550 | } 1551 | 1552 | if (ce->num_interfaces > 0) { 1553 | MAKE_STD_ZVAL(tmpzval); 1554 | array_init(tmpzval); 1555 | for (i = 0; i < ce->num_interfaces; i++) { 1556 | add_next_index_stringl(tmpzval, ce->interfaces[i]->name, ce->interfaces[i]->name_length, 1); 1557 | } 1558 | add_assoc_zval(raw_class, "interfaces", tmpzval); 1559 | } else { 1560 | add_assoc_null(raw_class, "interfaces"); 1561 | } 1562 | 1563 | add_assoc_string(raw_class, "filename", ce->filename, 1); 1564 | add_assoc_long(raw_class, "line_start", ce->line_start); 1565 | add_assoc_long(raw_class, "line_end", ce->line_end); 1566 | if (ce->doc_comment) { 1567 | add_assoc_stringl(raw_class, "doc_comment", ce->doc_comment, ce->doc_comment_len, 1); 1568 | } else { 1569 | add_assoc_null(raw_class, "doc_comment"); 1570 | } 1571 | 1572 | add_assoc_long(raw_class, "refcount", ce->refcount); 1573 | 1574 | if (zend_hash_num_elements(&(ce->function_table)) > 0) { 1575 | zval *methods; 1576 | MAKE_STD_ZVAL(methods); 1577 | array_init(methods); 1578 | if (bytekit_disassemble_functions(return_value, classname, methods, &(ce->function_table), 0, flags TSRMLS_CC) == FAILURE) { 1579 | efree(classname); 1580 | return FAILURE; 1581 | } 1582 | add_assoc_zval(raw_class, "methods", methods); 1583 | } else { 1584 | add_assoc_null(raw_class, "methods"); 1585 | } 1586 | efree(classname); 1587 | 1588 | if (zend_hash_num_elements(&(ce->default_properties)) > 0) { 1589 | zval *tmp_zval; 1590 | 1591 | MAKE_STD_ZVAL(tmpzval); 1592 | array_init(tmpzval); 1593 | zend_hash_copy(HASH_OF(tmpzval), &(ce->default_properties), (copy_ctor_func_t) zval_add_ref, (void *) &tmp_zval, sizeof(zval *)); 1594 | add_assoc_zval(raw_class, "default_properties", tmpzval); 1595 | } else { 1596 | add_assoc_null(raw_class, "default_properties"); 1597 | } 1598 | 1599 | return SUCCESS; 1600 | } 1601 | /* }}} */ 1602 | 1603 | 1604 | /* {{{ bytekit_disassemble_classes */ 1605 | static int bytekit_disassemble_classes(zval *return_value, HashTable *class_table, int count, long flags TSRMLS_DC) 1606 | { 1607 | zval *classes_raw; 1608 | 1609 | MAKE_STD_ZVAL(classes_raw); 1610 | array_init(classes_raw); 1611 | 1612 | zend_hash_internal_pointer_end(class_table); 1613 | while (count < zend_hash_num_elements(class_table)) { 1614 | zend_class_entry *class_entry, **pce; 1615 | zval *class_data; 1616 | 1617 | if (zend_hash_get_current_data(class_table, (void **)&pce) == FAILURE || !pce || !(*pce)) { 1618 | php_error_docref(NULL TSRMLS_CC, E_ERROR, "bytekit_disassemble_classes: unable to traverse class table."); 1619 | return FAILURE; 1620 | } 1621 | count++; 1622 | zend_hash_move_backwards(class_table); 1623 | 1624 | class_entry = *pce; 1625 | 1626 | if (class_entry->type != ZEND_USER_CLASS) { 1627 | php_error_docref(NULL TSRMLS_CC, E_ERROR, "bytekit_disassemble_classes: illegal class entry found."); 1628 | return FAILURE; 1629 | } 1630 | MAKE_STD_ZVAL(class_data); 1631 | array_init(class_data); 1632 | if (bytekit_disassemble_class_entry(return_value, class_data, class_entry, flags TSRMLS_CC) == FAILURE) { 1633 | return FAILURE; 1634 | } 1635 | 1636 | add_assoc_zval_ex(classes_raw, class_entry->name, class_entry->name_length + 1, class_data); 1637 | } 1638 | 1639 | add_assoc_zval(return_value, "classes", classes_raw); 1640 | return SUCCESS; 1641 | } 1642 | /* }}} */ 1643 | 1644 | /* {{{ bytekit_cleanup_functions_and_classes */ 1645 | static void bytekit_disassemble_functions_and_classes(zval *return_value, int num_functions, int num_classes, long flags TSRMLS_DC) 1646 | { 1647 | if (num_functions < zend_hash_num_elements(EG(function_table))) { 1648 | bytekit_disassemble_functions(return_value, NULL, NULL, EG(function_table), num_functions, flags TSRMLS_CC); 1649 | } 1650 | 1651 | if (num_classes < zend_hash_num_elements(EG(class_table))) { 1652 | bytekit_disassemble_classes(return_value, EG(class_table), num_classes, flags TSRMLS_CC); 1653 | } 1654 | } 1655 | /* }}} */ 1656 | 1657 | /* {{{ proto array bytekit_disassemble_file(string filename[, array &errors[, mixed options]]) 1658 | Return array of disassembled opcodes compiled from phpfile */ 1659 | PHP_FUNCTION(bytekit_disassemble_file) 1660 | { 1661 | int original_num_functions = zend_hash_num_elements(EG(function_table)); 1662 | int original_num_classes = zend_hash_num_elements(EG(class_table)); 1663 | int dependencies_num_functions; 1664 | int dependencies_num_classes; 1665 | zend_uchar original_handle_op_arrays; 1666 | zend_op_array *ops = NULL; 1667 | zval *zfilename, *zerrors = NULL, *zoptions = NULL; 1668 | HashTable *dependencies = NULL; 1669 | long flags = 0; 1670 | zend_execute_data *orig_execute_data; 1671 | 1672 | if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z|zz", &zfilename, &zerrors, &zoptions) == FAILURE) { 1673 | RETURN_FALSE; 1674 | } 1675 | 1676 | /* handle the error array */ 1677 | if (zerrors) { 1678 | zval_dtor(zerrors); 1679 | ZVAL_NULL(zerrors); 1680 | BYTEKIT_G(compile_errors) = zerrors; 1681 | } 1682 | 1683 | /* handle the options */ 1684 | if (zoptions) { 1685 | 1686 | if (Z_TYPE_P(zoptions) == IS_LONG) { 1687 | flags = Z_LVAL_P(zoptions); 1688 | } else if (Z_TYPE_P(zoptions) == IS_ARRAY) { 1689 | zval **option; 1690 | HashTable *ht = HASH_OF(zoptions); 1691 | 1692 | if (ht && zend_hash_find(ht, "dependencies", sizeof("dependencies"), (void **)&option) == SUCCESS && Z_TYPE_PP(option) == IS_ARRAY) { 1693 | dependencies = HASH_OF(*option); 1694 | } else if (ht && zend_hash_find(ht, "flags", sizeof("flags"), (void **)&option) == SUCCESS && Z_TYPE_PP(option) == IS_LONG) { 1695 | flags = Z_LVAL_PP(option); 1696 | } 1697 | } /* ignore everything else */ 1698 | } 1699 | 1700 | convert_to_string(zfilename); 1701 | #ifdef ZEND_COMPILE_HANDLE_OP_ARRAY 1702 | original_handle_op_arrays = CG(compiler_options) & ZEND_COMPILE_HANDLE_OP_ARRAY; 1703 | /* CG(compiler_options) &= ~ZEND_COMPILE_HANDLE_OP_ARRAY; */ 1704 | #else 1705 | original_handle_op_arrays = CG(handle_op_arrays); 1706 | /* CG(handle_op_arrays) = 0;~*/ 1707 | #endif 1708 | /* this is not 100% thread-safe but without 1709 | a lock it is not possible to get it better */ 1710 | while (zend_error_cb == bytekit_error_cb) {} 1711 | bytekit_original_error_cb = zend_error_cb; 1712 | zend_error_cb = bytekit_error_cb; 1713 | 1714 | /* Handle all dependencies */ 1715 | if (dependencies) { 1716 | 1717 | zval **zdependency; 1718 | 1719 | for (zend_hash_internal_pointer_reset(dependencies); 1720 | zend_hash_get_current_data(dependencies, (void **)&zdependency) == SUCCESS; 1721 | zend_hash_move_forward(dependencies)) { 1722 | 1723 | orig_execute_data = EG(current_execute_data); 1724 | 1725 | zend_try { 1726 | ops = compile_filename(ZEND_INCLUDE, *zdependency TSRMLS_CC); 1727 | } zend_catch { 1728 | ops = NULL; 1729 | } zend_end_try(); 1730 | 1731 | EG(current_execute_data) = orig_execute_data; 1732 | 1733 | /* in case of an error clean up and exit */ 1734 | if (ops == NULL || Z_TYPE_P(BYTEKIT_G(compile_errors)) != IS_NULL) { 1735 | 1736 | bytekit_cleanup_functions_and_classes(original_num_functions, original_num_classes TSRMLS_CC); 1737 | 1738 | /* do not forget to restore the error cb */ 1739 | if (zend_error_cb == bytekit_error_cb) { 1740 | zend_error_cb = bytekit_original_error_cb; 1741 | } 1742 | RETURN_FALSE; 1743 | } else if (ops != NULL) { 1744 | /* get rid of op_array */ 1745 | destroy_op_array(ops TSRMLS_CC); 1746 | efree(ops); 1747 | } 1748 | } 1749 | } 1750 | 1751 | dependencies_num_functions = zend_hash_num_elements(EG(function_table)); 1752 | dependencies_num_classes = zend_hash_num_elements(EG(class_table)); 1753 | 1754 | orig_execute_data = EG(current_execute_data); 1755 | 1756 | zend_try { 1757 | 1758 | zend_file_handle file_handle; 1759 | 1760 | file_handle.filename = zfilename->value.str.val; 1761 | file_handle.free_filename = 0; 1762 | file_handle.type = ZEND_HANDLE_FILENAME; 1763 | file_handle.opened_path = NULL; 1764 | file_handle.handle.fp = NULL; 1765 | 1766 | ops = zend_compile_file(&file_handle, ZEND_INCLUDE TSRMLS_CC); 1767 | 1768 | zend_destroy_file_handle(&file_handle TSRMLS_CC); 1769 | 1770 | } zend_catch { 1771 | ops = NULL; 1772 | } zend_end_try(); 1773 | 1774 | EG(current_execute_data) = orig_execute_data; 1775 | 1776 | 1777 | /* this is again not 100% thread-safe but better than nothing */ 1778 | if (zend_error_cb == bytekit_error_cb) { 1779 | zend_error_cb = bytekit_original_error_cb; 1780 | } 1781 | 1782 | BYTEKIT_G(compile_errors) = NULL; 1783 | #ifdef ZEND_COMPILE_HANDLE_OP_ARRAY 1784 | CG(compiler_options) |= original_handle_op_arrays; 1785 | #else 1786 | CG(handle_op_arrays) = original_handle_op_arrays; 1787 | #endif 1788 | if (ops) { 1789 | zval *main, *functions; 1790 | array_init(return_value); 1791 | MAKE_STD_ZVAL(main); 1792 | MAKE_STD_ZVAL(functions); 1793 | array_init(functions); 1794 | 1795 | bytekit_disassemble_op_array(main, ops, flags TSRMLS_CC); 1796 | destroy_op_array(ops TSRMLS_CC); 1797 | efree(ops); 1798 | add_assoc_zval(functions, "main", main); 1799 | add_assoc_zval(return_value, "functions", functions); 1800 | bytekit_disassemble_functions_and_classes(return_value, dependencies_num_functions, dependencies_num_classes, flags TSRMLS_CC); 1801 | add_assoc_long(return_value, "base", base_address); 1802 | } else { 1803 | RETVAL_FALSE; 1804 | } 1805 | 1806 | /* do not forget to cleanup our mess */ 1807 | bytekit_cleanup_functions_and_classes(original_num_functions, original_num_classes TSRMLS_CC); 1808 | } 1809 | /* }}} */ 1810 | 1811 | /* {{{ proto long bytekit_get_baseaddress() 1812 | Return the current baseaddress */ 1813 | PHP_FUNCTION(bytekit_get_baseaddress) 1814 | { 1815 | RETURN_LONG(base_address); 1816 | } 1817 | /* }}} */ 1818 | 1819 | /* {{{ proto bytekit_set_baseaddress(long baseaddress) 1820 | Set the current baseaddress */ 1821 | PHP_FUNCTION(bytekit_set_baseaddress) 1822 | { 1823 | zval **value; 1824 | 1825 | if (ZEND_NUM_ARGS() != 1 || zend_get_parameters_ex(1, &value) == FAILURE) { 1826 | WRONG_PARAM_COUNT; 1827 | } 1828 | 1829 | convert_scalar_to_number_ex(value); 1830 | 1831 | if (Z_TYPE_PP(value) == IS_DOUBLE) { 1832 | base_address = fabs(Z_DVAL_PP(value)); 1833 | } else if (Z_TYPE_PP(value) == IS_LONG) { 1834 | base_address = Z_LVAL_PP(value); 1835 | } 1836 | RETURN_LONG(base_address); 1837 | } 1838 | /* }}} */ 1839 | 1840 | #if PHP_VERSION_ID < 50300 1841 | static 1842 | #endif 1843 | ZEND_BEGIN_ARG_INFO(bytekit_second_arg_force_ref, 0) 1844 | ZEND_ARG_PASS_INFO(0) 1845 | ZEND_ARG_PASS_INFO(1) 1846 | ZEND_END_ARG_INFO() 1847 | 1848 | #if PHP_VERSION_ID < 50300 1849 | static 1850 | #endif 1851 | ZEND_BEGIN_ARG_INFO(bytekit_arginfo_get_baseaddress, 0) 1852 | ZEND_END_ARG_INFO() 1853 | 1854 | #if PHP_VERSION_ID < 50300 1855 | static 1856 | #endif 1857 | ZEND_BEGIN_ARG_INFO(bytekit_arginfo_set_baseaddress, 0) 1858 | ZEND_ARG_INFO(0, baseaddress) 1859 | ZEND_END_ARG_INFO() 1860 | 1861 | /* {{{ function_entry 1862 | */ 1863 | function_entry bytekit_functions[] = { 1864 | PHP_FE(bytekit_disassemble_file, bytekit_second_arg_force_ref) 1865 | PHP_FE(bytekit_get_baseaddress, bytekit_arginfo_get_baseaddress) 1866 | PHP_FE(bytekit_set_baseaddress, bytekit_arginfo_set_baseaddress) 1867 | {NULL, NULL, NULL} 1868 | }; 1869 | /* }}} */ 1870 | 1871 | /* {{{ bytekit_module_entry 1872 | */ 1873 | zend_module_entry bytekit_module_entry = { 1874 | STANDARD_MODULE_HEADER, 1875 | "bytekit", 1876 | bytekit_functions, 1877 | PHP_MINIT(bytekit), 1878 | PHP_MSHUTDOWN(bytekit), 1879 | NULL, /* RINIT */ 1880 | NULL, /* RSHUTDOWN */ 1881 | PHP_MINFO(bytekit), 1882 | BYTEKIT_VERSION, 1883 | STANDARD_MODULE_PROPERTIES 1884 | }; 1885 | /* }}} */ 1886 | 1887 | #ifdef COMPILE_DL_BYTEKIT 1888 | ZEND_GET_MODULE(bytekit) 1889 | #endif 1890 | 1891 | #define REGISTER_BYTEKIT_CONSTANTS(define_list) \ 1892 | { \ 1893 | char const_name[96]; \ 1894 | int const_name_len; \ 1895 | bytekit_define_list *defines = (define_list); \ 1896 | while (defines->str) { \ 1897 | /* the macros don't like variable constant names */ \ 1898 | const_name_len = snprintf(const_name, sizeof(const_name), "BYTEKIT_%s", defines->str); \ 1899 | zend_register_long_constant(const_name, const_name_len+1, defines->val, CONST_CS | CONST_PERSISTENT, module_number TSRMLS_CC); \ 1900 | defines++; \ 1901 | } \ 1902 | } 1903 | 1904 | /* {{{ bytekit_init_globals 1905 | */ 1906 | static void bytekit_init_globals(zend_bytekit_globals *bytekit_globals) 1907 | { 1908 | bytekit_globals->compile_errors = NULL; 1909 | bytekit_globals->in_error_cb = 0; 1910 | bytekit_globals->op_array_opcodes = NULL; 1911 | bytekit_globals->opcodes_base = NULL; 1912 | } 1913 | /* }}} */ 1914 | 1915 | /* {{{ PHP_MINIT_FUNCTION 1916 | */ 1917 | PHP_MINIT_FUNCTION(bytekit) 1918 | { 1919 | REGISTER_BYTEKIT_CONSTANTS(bytekit_constant_names); 1920 | REGISTER_BYTEKIT_CONSTANTS(bytekit_opcode_names); 1921 | REGISTER_BYTEKIT_CONSTANTS(bytekit_zend_constant_names); 1922 | 1923 | ZEND_INIT_MODULE_GLOBALS(bytekit, bytekit_init_globals, NULL); 1924 | 1925 | return SUCCESS; 1926 | } 1927 | /* }}} */ 1928 | 1929 | /* {{{ PHP_MSHUTDOWN_FUNCTION 1930 | */ 1931 | PHP_MSHUTDOWN_FUNCTION(bytekit) 1932 | { 1933 | return SUCCESS; 1934 | } 1935 | /* }}} */ 1936 | 1937 | /* {{{ PHP_MINFO_FUNCTION 1938 | */ 1939 | PHP_MINFO_FUNCTION(bytekit) 1940 | { 1941 | php_info_print_table_start(); 1942 | php_info_print_table_header(2, "Bytekit support", "enabled"); 1943 | php_info_print_table_end(); 1944 | } 1945 | /* }}} */ 1946 | 1947 | 1948 | /* 1949 | * Local variables: 1950 | * tab-width: 4 1951 | * c-basic-offset: 4 1952 | * End: 1953 | * vim600: noet sw=4 ts=4 fdm=marker 1954 | * vim<600: noet sw=4 ts=4 1955 | */ 1956 | -------------------------------------------------------------------------------- /run-tests.php: -------------------------------------------------------------------------------- 1 | #!/usr/bin/php 2 | | 18 | | Preston L. Bannister | 19 | | Marcus Boerger | 20 | | Derick Rethans | 21 | | Sander Roobol | 22 | | (based on version by: Stig Bakken ) | 23 | | (based on the PHP 3 test framework by Rasmus Lerdorf) | 24 | +----------------------------------------------------------------------+ 25 | */ 26 | 27 | /* $Id: run-tests.php 308726 2011-02-27 17:55:39Z felipe $ */ 28 | 29 | /* Sanity check to ensure that pcre extension needed by this script is available. 30 | * In the event it is not, print a nice error message indicating that this script will 31 | * not run without it. 32 | */ 33 | 34 | if (!extension_loaded('pcre')) { 35 | echo <<'; 261 | save_text($info_file, $php_info); 262 | $info_params = array(); 263 | settings2array($ini_overwrites, $info_params); 264 | settings2params($info_params); 265 | $php_info = `$php $pass_options $info_params "$info_file"`; 266 | define('TESTED_PHP_VERSION', `$php -n -r "echo PHP_VERSION;"`); 267 | 268 | if ($php_cgi && $php != $php_cgi) { 269 | $php_info_cgi = `$php_cgi $pass_options $info_params -q "$info_file"`; 270 | $php_info_sep = "\n---------------------------------------------------------------------"; 271 | $php_cgi_info = "$php_info_sep\nPHP : $php_cgi $php_info_cgi$php_info_sep"; 272 | } else { 273 | $php_cgi_info = ''; 274 | } 275 | 276 | @unlink($info_file); 277 | 278 | // load list of enabled extensions 279 | save_text($info_file, ''); 280 | $exts_to_test = explode(',',`$php $pass_options $info_params "$info_file"`); 281 | // check for extensions that need special handling and regenerate 282 | $info_params_ex = array( 283 | 'session' => array('session.auto_start=0'), 284 | 'tidy' => array('tidy.clean_output=0'), 285 | 'zlib' => array('zlib.output_compression=Off'), 286 | 'xdebug' => array('xdebug.default_enable=0'), 287 | 'mbstring' => array('mbstring.func_overload=0'), 288 | ); 289 | 290 | foreach($info_params_ex as $ext => $ini_overwrites_ex) { 291 | if (in_array($ext, $exts_to_test)) { 292 | $ini_overwrites = array_merge($ini_overwrites, $ini_overwrites_ex); 293 | } 294 | } 295 | 296 | @unlink($info_file); 297 | 298 | // Write test context information. 299 | echo " 300 | ===================================================================== 301 | PHP : $php $php_info $php_cgi_info 302 | CWD : $cwd 303 | Extra dirs : "; 304 | foreach ($user_tests as $test_dir) { 305 | echo "{$test_dir}\n "; 306 | } 307 | echo " 308 | VALGRIND : " . ($leak_check ? $valgrind_header : 'Not used') . " 309 | ===================================================================== 310 | "; 311 | } 312 | 313 | define('PHP_QA_EMAIL', 'qa-reports@lists.php.net'); 314 | define('QA_SUBMISSION_PAGE', 'http://qa.php.net/buildtest-process.php'); 315 | 316 | function save_or_mail_results() 317 | { 318 | global $sum_results, $just_save_results, $failed_test_summary, 319 | $PHP_FAILED_TESTS, $CUR_DIR, $php, $output_file, $compression; 320 | 321 | /* We got failed Tests, offer the user to send an e-mail to QA team, unless NO_INTERACTION is set */ 322 | if (!getenv('NO_INTERACTION')) { 323 | $fp = fopen("php://stdin", "r+"); 324 | if ($sum_results['FAILED'] || $sum_results['BORKED'] || $sum_results['WARNED'] || $sum_results['LEAKED'] || $sum_results['XFAILED']) { 325 | echo "\nYou may have found a problem in PHP."; 326 | } 327 | echo "\nWe would like to send this report automatically to the\n"; 328 | echo "PHP QA team, to give us a better understanding of how\nthe test cases are doing. If you don't want to send it\n"; 329 | echo "immediately, you can choose \"s\" to save the report to\na file that you can send us later.\n"; 330 | echo "Do you want to send this report now? [Yns]: "; 331 | flush(); 332 | 333 | $user_input = fgets($fp, 10); 334 | $just_save_results = (strtolower($user_input[0]) == 's'); 335 | } 336 | 337 | if ($just_save_results || !getenv('NO_INTERACTION')) { 338 | if ($just_save_results || strlen(trim($user_input)) == 0 || strtolower($user_input[0]) == 'y') { 339 | /* 340 | * Collect information about the host system for our report 341 | * Fetch phpinfo() output so that we can see the PHP enviroment 342 | * Make an archive of all the failed tests 343 | * Send an email 344 | */ 345 | if ($just_save_results) { 346 | $user_input = 's'; 347 | } 348 | 349 | /* Ask the user to provide an email address, so that QA team can contact the user */ 350 | if (!strncasecmp($user_input, 'y', 1) || strlen(trim($user_input)) == 0) { 351 | echo "\nPlease enter your email address.\n(Your address will be mangled so that it will not go out on any\nmailinglist in plain text): "; 352 | flush(); 353 | $user_email = trim(fgets($fp, 1024)); 354 | $user_email = str_replace("@", " at ", str_replace(".", " dot ", $user_email)); 355 | } 356 | 357 | $failed_tests_data = ''; 358 | $sep = "\n" . str_repeat('=', 80) . "\n"; 359 | $failed_tests_data .= $failed_test_summary . "\n"; 360 | $failed_tests_data .= get_summary(true, false) . "\n"; 361 | 362 | if ($sum_results['FAILED']) { 363 | foreach ($PHP_FAILED_TESTS['FAILED'] as $test_info) { 364 | $failed_tests_data .= $sep . $test_info['name'] . $test_info['info']; 365 | $failed_tests_data .= $sep . file_get_contents(realpath($test_info['output']), FILE_BINARY); 366 | $failed_tests_data .= $sep . file_get_contents(realpath($test_info['diff']), FILE_BINARY); 367 | $failed_tests_data .= $sep . "\n\n"; 368 | } 369 | $status = "failed"; 370 | } else { 371 | $status = "success"; 372 | } 373 | 374 | $failed_tests_data .= "\n" . $sep . 'BUILD ENVIRONMENT' . $sep; 375 | $failed_tests_data .= "OS:\n" . PHP_OS . " - " . php_uname() . "\n\n"; 376 | $ldd = $autoconf = $sys_libtool = $libtool = $compiler = 'N/A'; 377 | 378 | if (substr(PHP_OS, 0, 3) != "WIN") { 379 | /* If PHP_AUTOCONF is set, use it; otherwise, use 'autoconf'. */ 380 | if (getenv('PHP_AUTOCONF')) { 381 | $autoconf = shell_exec(getenv('PHP_AUTOCONF') . ' --version'); 382 | } else { 383 | $autoconf = shell_exec('autoconf --version'); 384 | } 385 | 386 | /* Always use the generated libtool - Mac OSX uses 'glibtool' */ 387 | $libtool = shell_exec($CUR_DIR . '/libtool --version'); 388 | 389 | /* Use shtool to find out if there is glibtool present (MacOSX) */ 390 | $sys_libtool_path = shell_exec(__DIR__ . '/build/shtool path glibtool libtool'); 391 | 392 | if ($sys_libtool_path) { 393 | $sys_libtool = shell_exec(str_replace("\n", "", $sys_libtool_path) . ' --version'); 394 | } 395 | 396 | /* Try the most common flags for 'version' */ 397 | $flags = array('-v', '-V', '--version'); 398 | $cc_status = 0; 399 | 400 | foreach($flags AS $flag) { 401 | system(getenv('CC') . " $flag >/dev/null 2>&1", $cc_status); 402 | if ($cc_status == 0) { 403 | $compiler = shell_exec(getenv('CC') . " $flag 2>&1"); 404 | break; 405 | } 406 | } 407 | 408 | $ldd = shell_exec("ldd $php 2>/dev/null"); 409 | } 410 | 411 | $failed_tests_data .= "Autoconf:\n$autoconf\n"; 412 | $failed_tests_data .= "Bundled Libtool:\n$libtool\n"; 413 | $failed_tests_data .= "System Libtool:\n$sys_libtool\n"; 414 | $failed_tests_data .= "Compiler:\n$compiler\n"; 415 | $failed_tests_data .= "Bison:\n". shell_exec('bison --version 2>/dev/null') . "\n"; 416 | $failed_tests_data .= "Libraries:\n$ldd\n"; 417 | $failed_tests_data .= "\n"; 418 | 419 | if (isset($user_email)) { 420 | $failed_tests_data .= "User's E-mail: " . $user_email . "\n\n"; 421 | } 422 | 423 | $failed_tests_data .= $sep . "PHPINFO" . $sep; 424 | $failed_tests_data .= shell_exec($php . ' -ddisplay_errors=stderr -dhtml_errors=0 -i 2> /dev/null'); 425 | 426 | if ($just_save_results || !mail_qa_team($failed_tests_data, $compression, $status)) { 427 | file_put_contents($output_file, $failed_tests_data); 428 | 429 | if (!$just_save_results) { 430 | echo "\nThe test script was unable to automatically send the report to PHP's QA Team\n"; 431 | } 432 | 433 | echo "Please send " . $output_file . " to " . PHP_QA_EMAIL . " manually, thank you.\n"; 434 | } else { 435 | fwrite($fp, "\nThank you for helping to make PHP better.\n"); 436 | fclose($fp); 437 | } 438 | } 439 | } 440 | } 441 | 442 | // Determine the tests to be run. 443 | 444 | $test_files = array(); 445 | $redir_tests = array(); 446 | $test_results = array(); 447 | $PHP_FAILED_TESTS = array('BORKED' => array(), 'FAILED' => array(), 'WARNED' => array(), 'LEAKED' => array(), 'XFAILED' => array()); 448 | 449 | // If parameters given assume they represent selected tests to run. 450 | $failed_tests_file= false; 451 | $pass_option_n = false; 452 | $pass_options = ''; 453 | 454 | $compression = 0; 455 | $output_file = $CUR_DIR . '/php_test_results_' . date('Ymd_Hi') . '.txt'; 456 | 457 | if ($compression) { 458 | $output_file = 'compress.zlib://' . $output_file . '.gz'; 459 | } 460 | 461 | $just_save_results = false; 462 | $leak_check = false; 463 | $html_output = false; 464 | $html_file = null; 465 | $temp_source = null; 466 | $temp_target = null; 467 | $temp_urlbase = null; 468 | $conf_passed = null; 469 | $no_clean = false; 470 | 471 | $cfgtypes = array('show', 'keep'); 472 | $cfgfiles = array('skip', 'php', 'clean', 'out', 'diff', 'exp'); 473 | $cfg = array(); 474 | 475 | foreach($cfgtypes as $type) { 476 | $cfg[$type] = array(); 477 | 478 | foreach($cfgfiles as $file) { 479 | $cfg[$type][$file] = false; 480 | } 481 | } 482 | 483 | if (getenv('TEST_PHP_ARGS')) { 484 | 485 | if (!isset($argc) || !$argc || !isset($argv)) { 486 | $argv = array(__FILE__); 487 | } 488 | 489 | $argv = array_merge($argv, split(' ', getenv('TEST_PHP_ARGS'))); 490 | $argc = count($argv); 491 | } 492 | 493 | if (isset($argc) && $argc > 1) { 494 | 495 | for ($i=1; $i<$argc; $i++) { 496 | $is_switch = false; 497 | $switch = substr($argv[$i],1,1); 498 | $repeat = substr($argv[$i],0,1) == '-'; 499 | 500 | while ($repeat) { 501 | 502 | if (!$is_switch) { 503 | $switch = substr($argv[$i],1,1); 504 | } 505 | 506 | $is_switch = true; 507 | 508 | if ($repeat) { 509 | foreach($cfgtypes as $type) { 510 | if (strpos($switch, '--' . $type) === 0) { 511 | foreach($cfgfiles as $file) { 512 | if ($switch == '--' . $type . '-' . $file) { 513 | $cfg[$type][$file] = true; 514 | $is_switch = false; 515 | break; 516 | } 517 | } 518 | } 519 | } 520 | } 521 | 522 | if (!$is_switch) { 523 | $is_switch = true; 524 | break; 525 | } 526 | 527 | $repeat = false; 528 | 529 | switch($switch) { 530 | case 'r': 531 | case 'l': 532 | $test_list = file($argv[++$i]); 533 | if ($test_list) { 534 | foreach($test_list as $test) { 535 | $matches = array(); 536 | if (preg_match('/^#.*\[(.*)\]\:\s+(.*)$/', $test, $matches)) { 537 | $redir_tests[] = array($matches[1], $matches[2]); 538 | } else if (strlen($test)) { 539 | $test_files[] = trim($test); 540 | } 541 | } 542 | } 543 | if ($switch != 'l') { 544 | break; 545 | } 546 | $i--; 547 | // break left intentionally 548 | case 'w': 549 | $failed_tests_file = fopen($argv[++$i], 'w+t'); 550 | break; 551 | case 'a': 552 | $failed_tests_file = fopen($argv[++$i], 'a+t'); 553 | break; 554 | case 'c': 555 | $conf_passed = $argv[++$i]; 556 | break; 557 | case 'd': 558 | $ini_overwrites[] = $argv[++$i]; 559 | break; 560 | //case 'h' 561 | case '--keep-all': 562 | foreach($cfgfiles as $file) { 563 | $cfg['keep'][$file] = true; 564 | } 565 | break; 566 | //case 'l' 567 | case 'm': 568 | $leak_check = true; 569 | $valgrind_cmd = "valgrind --version"; 570 | $valgrind_header = system_with_timeout($valgrind_cmd, $environment); 571 | $replace_count = 0; 572 | if (!$valgrind_header) { 573 | error("Valgrind returned no version info, cannot proceed.\nPlease check if Valgrind is installed."); 574 | } else { 575 | $valgrind_version = preg_replace("/valgrind-([0-9])\.([0-9])\.([0-9]+)([.-\w]+)?(\s+)/", '$1$2$3', $valgrind_header, 1, $replace_count); 576 | if ($replace_count != 1 || !is_numeric($valgrind_version)) { 577 | error("Valgrind returned invalid version info (\"$valgrind_header\"), cannot proceed."); 578 | } 579 | $valgrind_header = trim($valgrind_header); 580 | } 581 | break; 582 | case 'n': 583 | if (!$pass_option_n) { 584 | $pass_options .= ' -n'; 585 | } 586 | $pass_option_n = true; 587 | break; 588 | case '--no-clean': 589 | $no_clean = true; 590 | break; 591 | case 'p': 592 | $php = $argv[++$i]; 593 | putenv("TEST_PHP_EXECUTABLE=$php"); 594 | $environment['TEST_PHP_EXECUTABLE'] = $php; 595 | break; 596 | case 'q': 597 | putenv('NO_INTERACTION=1'); 598 | break; 599 | //case 'r' 600 | case 's': 601 | $output_file = $argv[++$i]; 602 | $just_save_results = true; 603 | break; 604 | case '--set-timeout': 605 | $environment['TEST_TIMEOUT'] = $argv[++$i]; 606 | break; 607 | case '--show-all': 608 | foreach($cfgfiles as $file) { 609 | $cfg['show'][$file] = true; 610 | } 611 | break; 612 | case '--temp-source': 613 | $temp_source = $argv[++$i]; 614 | break; 615 | case '--temp-target': 616 | $temp_target = $argv[++$i]; 617 | if ($temp_urlbase) { 618 | $temp_urlbase = $temp_target; 619 | } 620 | break; 621 | case '--temp-urlbase': 622 | $temp_urlbase = $argv[++$i]; 623 | break; 624 | case 'v': 625 | case '--verbose': 626 | $DETAILED = true; 627 | break; 628 | case 'x': 629 | $environment['SKIP_SLOW_TESTS'] = 1; 630 | break; 631 | //case 'w' 632 | case '-': 633 | // repeat check with full switch 634 | $switch = $argv[$i]; 635 | if ($switch != '-') { 636 | $repeat = true; 637 | } 638 | break; 639 | case '--html': 640 | $html_file = fopen($argv[++$i], 'wt'); 641 | $html_output = is_resource($html_file); 642 | break; 643 | case '--version': 644 | echo '$Revision: 308726 $' . "\n"; 645 | exit(1); 646 | 647 | default: 648 | echo "Illegal switch '$switch' specified!\n"; 649 | case 'h': 650 | case '-help': 651 | case '--help': 652 | echo << Read the testfiles to be executed from . After the test 658 | has finished all failed tests are written to the same . 659 | If the list is empty and no further test is specified then 660 | all tests are executed (same as: -r -w ). 661 | 662 | -r Read the testfiles to be executed from . 663 | 664 | -w Write a list of all failed tests to . 665 | 666 | -a Same as -w but append rather then truncating . 667 | 668 | -c Look for php.ini in directory or use as ini. 669 | 670 | -n Pass -n option to the php binary (Do not use a php.ini). 671 | 672 | -d foo=bar Pass -d option to the php binary (Define INI entry foo 673 | with value 'bar'). 674 | 675 | -m Test for memory leaks with Valgrind. 676 | 677 | -p Specify PHP executable to run. 678 | 679 | -q Quiet, no user interaction (same as environment NO_INTERACTION). 680 | 681 | -s Write output to . 682 | 683 | -x Sets 'SKIP_SLOW_TESTS' environmental variable. 684 | 685 | --verbose 686 | -v Verbose mode. 687 | 688 | --help 689 | -h This Help. 690 | 691 | --html Generate HTML output. 692 | 693 | --temp-source --temp-target [--temp-urlbase ] 694 | Write temporary files to by replacing from the 695 | filenames to generate with . If --html is being used and 696 | given then the generated links are relative and prefixed 697 | with the given url. In general you want to make the path 698 | to your source files and some pach in your web page 699 | hierarchy with pointing to . 700 | 701 | --keep-[all|php|skip|clean] 702 | Do not delete 'all' files, 'php' test file, 'skip' or 'clean' 703 | file. 704 | 705 | --set-timeout [n] 706 | Set timeout for individual tests, where [n] is the number of 707 | seconds. The default value is 60 seconds, or 300 seconds when 708 | testing for memory leaks. 709 | 710 | --show-[all|php|skip|clean|exp|diff|out] 711 | Show 'all' files, 'php' test file, 'skip' or 'clean' file. You 712 | can also use this to show the output 'out', the expected result 713 | 'exp' or the difference between them 'diff'. The result types 714 | get written independent of the log format, however 'diff' only 715 | exists when a test fails. 716 | 717 | --no-clean Do not execute clean section if any. 718 | 719 | HELP; 720 | exit(1); 721 | } 722 | } 723 | 724 | if (!$is_switch) { 725 | $testfile = realpath($argv[$i]); 726 | 727 | if (!$testfile && strpos($argv[$i], '*') !== false && function_exists('glob')) { 728 | 729 | if (preg_match("/\.phpt$/", $argv[$i])) { 730 | $pattern_match = glob($argv[$i]); 731 | } else if (preg_match("/\*$/", $argv[$i])) { 732 | $pattern_match = glob($argv[$i] . '.phpt'); 733 | } else { 734 | die("bogus test name " . $argv[$i] . "\n"); 735 | } 736 | 737 | if (is_array($pattern_match)) { 738 | $test_files = array_merge($test_files, $pattern_match); 739 | } 740 | 741 | } else if (is_dir($testfile)) { 742 | find_files($testfile); 743 | } else if (preg_match("/\.phpt$/", $testfile)) { 744 | $test_files[] = $testfile; 745 | } else { 746 | die("bogus test name " . $argv[$i] . "\n"); 747 | } 748 | } 749 | } 750 | 751 | if (strlen($conf_passed)) { 752 | if (substr(PHP_OS, 0, 3) == "WIN") { 753 | $pass_options .= " -c " . escapeshellarg($conf_passed); 754 | } else { 755 | $pass_options .= " -c '$conf_passed'"; 756 | } 757 | } 758 | 759 | $test_files = array_unique($test_files); 760 | $test_files = array_merge($test_files, $redir_tests); 761 | 762 | // Run selected tests. 763 | $test_cnt = count($test_files); 764 | 765 | if ($test_cnt) { 766 | putenv('NO_INTERACTION=1'); 767 | verify_config(); 768 | write_information($html_output); 769 | usort($test_files, "test_sort"); 770 | $start_time = time(); 771 | 772 | if (!$html_output) { 773 | echo "Running selected tests.\n"; 774 | } else { 775 | show_start($start_time); 776 | } 777 | 778 | $test_idx = 0; 779 | run_all_tests($test_files, $environment); 780 | $end_time = time(); 781 | 782 | if ($html_output) { 783 | show_end($end_time); 784 | } 785 | 786 | if ($failed_tests_file) { 787 | fclose($failed_tests_file); 788 | } 789 | 790 | if (count($test_files) || count($test_results)) { 791 | compute_summary(); 792 | if ($html_output) { 793 | fwrite($html_file, "
\n" . get_summary(false, true)); 794 | } 795 | echo "====================================================================="; 796 | echo get_summary(false, false); 797 | } 798 | 799 | if ($html_output) { 800 | fclose($html_file); 801 | } 802 | 803 | if ($output_file != '' && $just_save_results) { 804 | save_or_mail_results(); 805 | } 806 | 807 | if (getenv('REPORT_EXIT_STATUS') == 1 and preg_match('/FAILED(?: |$)/', implode(' ', $test_results))) { 808 | exit(1); 809 | } 810 | 811 | exit(0); 812 | } 813 | } 814 | 815 | verify_config(); 816 | write_information($html_output); 817 | 818 | // Compile a list of all test files (*.phpt). 819 | $test_files = array(); 820 | $exts_tested = count($exts_to_test); 821 | $exts_skipped = 0; 822 | $ignored_by_ext = 0; 823 | sort($exts_to_test); 824 | $test_dirs = array(); 825 | $optionals = array('tests', 'ext', 'Zend', 'ZendEngine2', 'sapi/cli', 'sapi/cgi'); 826 | 827 | foreach($optionals as $dir) { 828 | if (@filetype($dir) == 'dir') { 829 | $test_dirs[] = $dir; 830 | } 831 | } 832 | 833 | // Convert extension names to lowercase 834 | foreach ($exts_to_test as $key => $val) { 835 | $exts_to_test[$key] = strtolower($val); 836 | } 837 | 838 | foreach ($test_dirs as $dir) { 839 | find_files("{$cwd}/{$dir}", ($dir == 'ext')); 840 | } 841 | 842 | foreach ($user_tests as $dir) { 843 | find_files($dir, ($dir == 'ext')); 844 | } 845 | 846 | function find_files($dir, $is_ext_dir = false, $ignore = false) 847 | { 848 | global $test_files, $exts_to_test, $ignored_by_ext, $exts_skipped, $exts_tested; 849 | 850 | $o = opendir($dir) or error("cannot open directory: $dir"); 851 | 852 | while (($name = readdir($o)) !== false) { 853 | 854 | if (is_dir("{$dir}/{$name}") && !in_array($name, array('.', '..', 'CVS'))) { 855 | $skip_ext = ($is_ext_dir && !in_array(strtolower($name), $exts_to_test)); 856 | if ($skip_ext) { 857 | $exts_skipped++; 858 | } 859 | find_files("{$dir}/{$name}", false, $ignore || $skip_ext); 860 | } 861 | 862 | // Cleanup any left-over tmp files from last run. 863 | if (substr($name, -4) == '.tmp') { 864 | @unlink("$dir/$name"); 865 | continue; 866 | } 867 | 868 | // Otherwise we're only interested in *.phpt files. 869 | if (substr($name, -5) == '.phpt') { 870 | if ($ignore) { 871 | $ignored_by_ext++; 872 | } else { 873 | $testfile = realpath("{$dir}/{$name}"); 874 | $test_files[] = $testfile; 875 | } 876 | } 877 | } 878 | 879 | closedir($o); 880 | } 881 | 882 | function test_name($name) 883 | { 884 | if (is_array($name)) { 885 | return $name[0] . ':' . $name[1]; 886 | } else { 887 | return $name; 888 | } 889 | } 890 | 891 | function test_sort($a, $b) 892 | { 893 | global $cwd; 894 | 895 | $a = test_name($a); 896 | $b = test_name($b); 897 | 898 | $ta = strpos($a, "{$cwd}/tests") === 0 ? 1 + (strpos($a, "{$cwd}/tests/run-test") === 0 ? 1 : 0) : 0; 899 | $tb = strpos($b, "{$cwd}/tests") === 0 ? 1 + (strpos($b, "{$cwd}/tests/run-test") === 0 ? 1 : 0) : 0; 900 | 901 | if ($ta == $tb) { 902 | return strcmp($a, $b); 903 | } else { 904 | return $tb - $ta; 905 | } 906 | } 907 | 908 | $test_files = array_unique($test_files); 909 | usort($test_files, "test_sort"); 910 | 911 | $start_time = time(); 912 | show_start($start_time); 913 | 914 | $test_cnt = count($test_files); 915 | $test_idx = 0; 916 | run_all_tests($test_files, $environment); 917 | $end_time = time(); 918 | 919 | if ($failed_tests_file) { 920 | fclose($failed_tests_file); 921 | } 922 | 923 | // Summarize results 924 | 925 | if (0 == count($test_results)) { 926 | echo "No tests were run.\n"; 927 | return; 928 | } 929 | 930 | compute_summary(); 931 | 932 | show_end($end_time); 933 | show_summary(); 934 | 935 | if ($html_output) { 936 | fclose($html_file); 937 | } 938 | 939 | save_or_mail_results(); 940 | 941 | if (getenv('REPORT_EXIT_STATUS') == 1 and $sum_results['FAILED']) { 942 | exit(1); 943 | } 944 | exit(0); 945 | 946 | // 947 | // Send Email to QA Team 948 | // 949 | 950 | function mail_qa_team($data, $compression, $status = false) 951 | { 952 | $url_bits = parse_url(QA_SUBMISSION_PAGE); 953 | 954 | if (empty($url_bits['port'])) { 955 | $url_bits['port'] = 80; 956 | } 957 | 958 | $data = "php_test_data=" . urlencode(base64_encode(str_replace("\00", '[0x0]', $data))); 959 | $data_length = strlen($data); 960 | 961 | $fs = fsockopen($url_bits['host'], $url_bits['port'], $errno, $errstr, 10); 962 | 963 | if (!$fs) { 964 | return false; 965 | } 966 | 967 | $php_version = urlencode(TESTED_PHP_VERSION); 968 | 969 | echo "\nPosting to {$url_bits['host']} {$url_bits['path']}\n"; 970 | fwrite($fs, "POST " . $url_bits['path'] . "?status=$status&version=$php_version HTTP/1.1\r\n"); 971 | fwrite($fs, "Host: " . $url_bits['host'] . "\r\n"); 972 | fwrite($fs, "User-Agent: QA Browser 0.1\r\n"); 973 | fwrite($fs, "Content-Type: application/x-www-form-urlencoded\r\n"); 974 | fwrite($fs, "Content-Length: " . $data_length . "\r\n\r\n"); 975 | fwrite($fs, $data); 976 | fwrite($fs, "\r\n\r\n"); 977 | fclose($fs); 978 | 979 | return 1; 980 | } 981 | 982 | 983 | // 984 | // Write the given text to a temporary file, and return the filename. 985 | // 986 | 987 | function save_text($filename, $text, $filename_copy = null) 988 | { 989 | global $DETAILED; 990 | 991 | if ($filename_copy && $filename_copy != $filename) { 992 | if (file_put_contents($filename_copy, (binary) $text, FILE_BINARY) === false) { 993 | error("Cannot open file '" . $filename_copy . "' (save_text)"); 994 | } 995 | } 996 | 997 | if (file_put_contents($filename, (binary) $text, FILE_BINARY) === false) { 998 | error("Cannot open file '" . $filename . "' (save_text)"); 999 | } 1000 | 1001 | if (1 < $DETAILED) echo " 1002 | FILE $filename {{{ 1003 | $text 1004 | }}} 1005 | "; 1006 | } 1007 | 1008 | // 1009 | // Write an error in a format recognizable to Emacs or MSVC. 1010 | // 1011 | 1012 | function error_report($testname, $logname, $tested) 1013 | { 1014 | $testname = realpath($testname); 1015 | $logname = realpath($logname); 1016 | 1017 | switch (strtoupper(getenv('TEST_PHP_ERROR_STYLE'))) { 1018 | case 'MSVC': 1019 | echo $testname . "(1) : $tested\n"; 1020 | echo $logname . "(1) : $tested\n"; 1021 | break; 1022 | case 'EMACS': 1023 | echo $testname . ":1: $tested\n"; 1024 | echo $logname . ":1: $tested\n"; 1025 | break; 1026 | } 1027 | } 1028 | 1029 | function system_with_timeout($commandline, $env = null, $stdin = null) 1030 | { 1031 | global $leak_check, $cwd; 1032 | 1033 | $data = b''; 1034 | 1035 | $bin_env = array(); 1036 | foreach((array)$env as $key => $value) { 1037 | $bin_env[(binary)$key] = (binary)$value; 1038 | } 1039 | 1040 | $proc = proc_open($commandline, array( 1041 | 0 => array('pipe', 'r'), 1042 | 1 => array('pipe', 'w'), 1043 | 2 => array('pipe', 'w') 1044 | ), $pipes, $cwd, $bin_env, array('suppress_errors' => true, 'binary_pipes' => true)); 1045 | 1046 | if (!$proc) { 1047 | return false; 1048 | } 1049 | 1050 | if (!is_null($stdin)) { 1051 | fwrite($pipes[0], (binary) $stdin); 1052 | } 1053 | fclose($pipes[0]); 1054 | 1055 | $timeout = $leak_check ? 300 : (isset($env['TEST_TIMEOUT']) ? $env['TEST_TIMEOUT'] : 60); 1056 | 1057 | while (true) { 1058 | /* hide errors from interrupted syscalls */ 1059 | $r = $pipes; 1060 | $w = null; 1061 | $e = null; 1062 | 1063 | $n = @stream_select($r, $w, $e, $timeout); 1064 | 1065 | if ($n === false) { 1066 | break; 1067 | } else if ($n === 0) { 1068 | /* timed out */ 1069 | $data .= b"\n ** ERROR: process timed out **\n"; 1070 | proc_terminate($proc); 1071 | return $data; 1072 | } else if ($n > 0) { 1073 | $line = (binary) fread($pipes[1], 8192); 1074 | if (strlen($line) == 0) { 1075 | /* EOF */ 1076 | break; 1077 | } 1078 | $data .= $line; 1079 | } 1080 | } 1081 | 1082 | $stat = proc_get_status($proc); 1083 | 1084 | if ($stat['signaled']) { 1085 | $data .= b"\nTermsig=" . $stat['stopsig']; 1086 | } 1087 | 1088 | $code = proc_close($proc); 1089 | return $data; 1090 | } 1091 | 1092 | function run_all_tests($test_files, $env, $redir_tested = null) 1093 | { 1094 | global $test_results, $failed_tests_file, $php, $test_cnt, $test_idx; 1095 | 1096 | foreach($test_files as $name) { 1097 | 1098 | if (is_array($name)) { 1099 | $index = "# $name[1]: $name[0]"; 1100 | 1101 | if ($redir_tested) { 1102 | $name = $name[0]; 1103 | } 1104 | } else if ($redir_tested) { 1105 | $index = "# $redir_tested: $name"; 1106 | } else { 1107 | $index = $name; 1108 | } 1109 | $test_idx++; 1110 | $result = run_test($php, $name, $env); 1111 | 1112 | if (!is_array($name) && $result != 'REDIR') { 1113 | $test_results[$index] = $result; 1114 | if ($failed_tests_file && ($result == 'XFAILED' || $result == 'FAILED' || $result == 'WARNED' || $result == 'LEAKED')) { 1115 | fwrite($failed_tests_file, "$index\n"); 1116 | } 1117 | } 1118 | } 1119 | } 1120 | 1121 | // 1122 | // Show file or result block 1123 | // 1124 | function show_file_block($file, $block, $section = null) 1125 | { 1126 | global $cfg; 1127 | 1128 | if ($cfg['show'][$file]) { 1129 | 1130 | if (is_null($section)) { 1131 | $section = strtoupper($file); 1132 | } 1133 | 1134 | echo "\n========" . $section . "========\n"; 1135 | echo rtrim($block); 1136 | echo "\n========DONE========\n"; 1137 | } 1138 | } 1139 | 1140 | function binary_section($section) 1141 | { 1142 | return PHP_MAJOR_VERSION < 6 || 1143 | ( 1144 | $section == 'FILE' || 1145 | $section == 'FILEEOF' || 1146 | $section == 'EXPECT' || 1147 | $section == 'EXPECTF' || 1148 | $section == 'EXPECTREGEX' || 1149 | $section == 'EXPECTHEADERS' || 1150 | $section == 'SKIPIF' || 1151 | $section == 'CLEAN' 1152 | ); 1153 | } 1154 | 1155 | // 1156 | // Run an individual test case. 1157 | // 1158 | function run_test($php, $file, $env) 1159 | { 1160 | global $log_format, $info_params, $ini_overwrites, $cwd, $PHP_FAILED_TESTS; 1161 | global $pass_options, $DETAILED, $IN_REDIRECT, $test_cnt, $test_idx; 1162 | global $leak_check, $temp_source, $temp_target, $cfg, $environment; 1163 | global $no_clean; 1164 | global $valgrind_version; 1165 | $temp_filenames = null; 1166 | $org_file = $file; 1167 | 1168 | if (isset($env['TEST_PHP_CGI_EXECUTABLE'])) { 1169 | $php_cgi = $env['TEST_PHP_CGI_EXECUTABLE']; 1170 | } 1171 | 1172 | if (is_array($file)) { 1173 | $file = $file[0]; 1174 | } 1175 | 1176 | if ($DETAILED) echo " 1177 | ================= 1178 | TEST $file 1179 | "; 1180 | 1181 | // Load the sections of the test file. 1182 | $section_text = array('TEST' => ''); 1183 | 1184 | $fp = fopen($file, "rb") or error("Cannot open test file: $file"); 1185 | 1186 | $borked = false; 1187 | $bork_info = ''; 1188 | 1189 | if (!feof($fp)) { 1190 | $line = fgets($fp); 1191 | 1192 | if ($line === false) { 1193 | $bork_info = "cannot read test"; 1194 | $borked = true; 1195 | } 1196 | } else { 1197 | $bork_info = "empty test [$file]"; 1198 | $borked = true; 1199 | } 1200 | if (!$borked && strncmp('--TEST--', $line, 8)) { 1201 | $bork_info = "tests must start with --TEST-- [$file]"; 1202 | $borked = true; 1203 | } 1204 | 1205 | $section = 'TEST'; 1206 | $secfile = false; 1207 | $secdone = false; 1208 | 1209 | while (!feof($fp)) { 1210 | $line = fgets($fp); 1211 | 1212 | if ($line === false) { 1213 | break; 1214 | } 1215 | 1216 | // Match the beginning of a section. 1217 | if (preg_match(b'/^--([_A-Z]+)--/', $line, $r)) { 1218 | $section = $r[1]; 1219 | settype($section, STRING_TYPE); 1220 | 1221 | if (isset($section_text[$section])) { 1222 | $bork_info = "duplicated $section section"; 1223 | $borked = true; 1224 | } 1225 | 1226 | $section_text[$section] = binary_section($section) ? b'' : ''; 1227 | $secfile = $section == 'FILE' || $section == 'FILEEOF' || $section == 'FILE_EXTERNAL'; 1228 | $secdone = false; 1229 | continue; 1230 | } 1231 | 1232 | if (!binary_section($section)) { 1233 | $line = unicode_decode($line, "utf-8"); 1234 | if ($line == false) { 1235 | $bork_info = "cannot read test"; 1236 | $borked = true; 1237 | break; 1238 | } 1239 | } 1240 | 1241 | // Add to the section text. 1242 | if (!$secdone) { 1243 | $section_text[$section] .= $line; 1244 | } 1245 | 1246 | // End of actual test? 1247 | if ($secfile && preg_match(b'/^===DONE===\s*$/', $line)) { 1248 | $secdone = true; 1249 | } 1250 | } 1251 | 1252 | // the redirect section allows a set of tests to be reused outside of 1253 | // a given test dir 1254 | if (!$borked) { 1255 | if (@count($section_text['REDIRECTTEST']) == 1) { 1256 | 1257 | if ($IN_REDIRECT) { 1258 | $borked = true; 1259 | $bork_info = "Can't redirect a test from within a redirected test"; 1260 | } else { 1261 | $borked = false; 1262 | } 1263 | 1264 | } else { 1265 | 1266 | if (@count($section_text['FILE']) + @count($section_text['FILEEOF']) + @count($section_text['FILE_EXTERNAL']) != 1) { 1267 | $bork_info = "missing section --FILE--"; 1268 | $borked = true; 1269 | } 1270 | 1271 | if (@count($section_text['FILEEOF']) == 1) { 1272 | $section_text['FILE'] = preg_replace(b"/[\r\n]+$/", b'', $section_text['FILEEOF']); 1273 | unset($section_text['FILEEOF']); 1274 | } 1275 | 1276 | if (@count($section_text['FILE_EXTERNAL']) == 1) { 1277 | // don't allow tests to retrieve files from anywhere but this subdirectory 1278 | $section_text['FILE_EXTERNAL'] = dirname($file) . '/' . trim(str_replace('..', '', $section_text['FILE_EXTERNAL'])); 1279 | 1280 | if (file_exists($section_text['FILE_EXTERNAL'])) { 1281 | $section_text['FILE'] = file_get_contents($section_text['FILE_EXTERNAL'], FILE_BINARY); 1282 | unset($section_text['FILE_EXTERNAL']); 1283 | } else { 1284 | $bork_info = "could not load --FILE_EXTERNAL-- " . dirname($file) . '/' . trim($section_text['FILE_EXTERNAL']); 1285 | $borked = true; 1286 | } 1287 | } 1288 | 1289 | if ((@count($section_text['EXPECT']) + @count($section_text['EXPECTF']) + @count($section_text['EXPECTREGEX'])) != 1) { 1290 | $bork_info = "missing section --EXPECT--, --EXPECTF-- or --EXPECTREGEX--"; 1291 | $borked = true; 1292 | } 1293 | } 1294 | } 1295 | fclose($fp); 1296 | 1297 | $shortname = str_replace($cwd . '/', '', $file); 1298 | $tested_file = $shortname; 1299 | 1300 | if ($borked) { 1301 | show_result("BORK", $bork_info, $tested_file); 1302 | $PHP_FAILED_TESTS['BORKED'][] = array ( 1303 | 'name' => $file, 1304 | 'test_name' => '', 1305 | 'output' => '', 1306 | 'diff' => '', 1307 | 'info' => "$bork_info [$file]", 1308 | ); 1309 | return 'BORKED'; 1310 | } 1311 | 1312 | $tested = trim($section_text['TEST']); 1313 | 1314 | /* For GET/POST tests, check if cgi sapi is available and if it is, use it. */ 1315 | if (!empty($section_text['GET']) || !empty($section_text['POST']) || !empty($section_text['POST_RAW']) || !empty($section_text['COOKIE']) || !empty($section_text['EXPECTHEADERS'])) { 1316 | if (isset($php_cgi)) { 1317 | $old_php = $php; 1318 | $php = $php_cgi . ' -C '; 1319 | } else if (!strncasecmp(PHP_OS, "win", 3) && file_exists(dirname($php) . "/php-cgi.exe")) { 1320 | $old_php = $php; 1321 | $php = realpath(dirname($php) . "/php-cgi.exe") . ' -C '; 1322 | } else { 1323 | if (file_exists(dirname($php) . "/../../sapi/cgi/php-cgi")) { 1324 | $old_php = $php; 1325 | $php = realpath(dirname($php) . "/../../sapi/cgi/php-cgi") . ' -C '; 1326 | } else if (file_exists("./sapi/cgi/php-cgi")) { 1327 | $old_php = $php; 1328 | $php = realpath("./sapi/cgi/php-cgi") . ' -C '; 1329 | } else { 1330 | show_result('SKIP', $tested, $tested_file, "reason: CGI not available"); 1331 | return 'SKIPPED'; 1332 | } 1333 | } 1334 | } 1335 | 1336 | show_test($test_idx, $shortname); 1337 | 1338 | if (is_array($IN_REDIRECT)) { 1339 | $temp_dir = $test_dir = $IN_REDIRECT['dir']; 1340 | } else { 1341 | $temp_dir = $test_dir = realpath(dirname($file)); 1342 | } 1343 | 1344 | if ($temp_source && $temp_target) { 1345 | $temp_dir = str_replace($temp_source, $temp_target, $temp_dir); 1346 | } 1347 | 1348 | $main_file_name = basename($file,'phpt'); 1349 | 1350 | $diff_filename = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name . 'diff'; 1351 | $log_filename = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name . 'log'; 1352 | $exp_filename = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name . 'exp'; 1353 | $output_filename = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name . 'out'; 1354 | $memcheck_filename = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name . 'mem'; 1355 | $sh_filename = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name . 'sh'; 1356 | $temp_file = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name . 'php'; 1357 | $test_file = $test_dir . DIRECTORY_SEPARATOR . $main_file_name . 'php'; 1358 | $temp_skipif = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name . 'skip.php'; 1359 | $test_skipif = $test_dir . DIRECTORY_SEPARATOR . $main_file_name . 'skip.php'; 1360 | $temp_clean = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name . 'clean.php'; 1361 | $test_clean = $test_dir . DIRECTORY_SEPARATOR . $main_file_name . 'clean.php'; 1362 | $tmp_post = $temp_dir . DIRECTORY_SEPARATOR . uniqid('/phpt.'); 1363 | $tmp_relative_file = str_replace(__DIR__ . DIRECTORY_SEPARATOR, '', $test_file) . 't'; 1364 | 1365 | if ($temp_source && $temp_target) { 1366 | $temp_skipif .= 's'; 1367 | $temp_file .= 's'; 1368 | $temp_clean .= 's'; 1369 | $copy_file = $temp_dir . DIRECTORY_SEPARATOR . basename(is_array($file) ? $file[1] : $file) . '.phps'; 1370 | 1371 | if (!is_dir(dirname($copy_file))) { 1372 | mkdir(dirname($copy_file), 0777, true) or error("Cannot create output directory - " . dirname($copy_file)); 1373 | } 1374 | 1375 | if (isset($section_text['FILE'])) { 1376 | save_text($copy_file, $section_text['FILE']); 1377 | } 1378 | 1379 | $temp_filenames = array( 1380 | 'file' => $copy_file, 1381 | 'diff' => $diff_filename, 1382 | 'log' => $log_filename, 1383 | 'exp' => $exp_filename, 1384 | 'out' => $output_filename, 1385 | 'mem' => $memcheck_filename, 1386 | 'sh' => $sh_filename, 1387 | 'php' => $temp_file, 1388 | 'skip' => $temp_skipif, 1389 | 'clean'=> $temp_clean); 1390 | } 1391 | 1392 | if (is_array($IN_REDIRECT)) { 1393 | $tested = $IN_REDIRECT['prefix'] . ' ' . trim($section_text['TEST']); 1394 | $tested_file = $tmp_relative_file; 1395 | } 1396 | 1397 | // unlink old test results 1398 | @unlink($diff_filename); 1399 | @unlink($log_filename); 1400 | @unlink($exp_filename); 1401 | @unlink($output_filename); 1402 | @unlink($memcheck_filename); 1403 | @unlink($sh_filename); 1404 | @unlink($temp_file); 1405 | @unlink($test_file); 1406 | @unlink($temp_skipif); 1407 | @unlink($test_skipif); 1408 | @unlink($tmp_post); 1409 | @unlink($temp_clean); 1410 | @unlink($test_clean); 1411 | 1412 | // Reset environment from any previous test. 1413 | $env['REDIRECT_STATUS'] = ''; 1414 | $env['QUERY_STRING'] = ''; 1415 | $env['PATH_TRANSLATED'] = ''; 1416 | $env['SCRIPT_FILENAME'] = ''; 1417 | $env['REQUEST_METHOD'] = ''; 1418 | $env['CONTENT_TYPE'] = ''; 1419 | $env['CONTENT_LENGTH'] = ''; 1420 | $env['TZ'] = ''; 1421 | 1422 | if (!empty($section_text['ENV'])) { 1423 | 1424 | foreach(explode("\n", trim($section_text['ENV'])) as $e) { 1425 | $e = explode('=', trim($e), 2); 1426 | 1427 | if (!empty($e[0]) && isset($e[1])) { 1428 | $env[$e[0]] = $e[1]; 1429 | } 1430 | } 1431 | } 1432 | 1433 | // Default ini settings 1434 | $ini_settings = array(); 1435 | // additional ini overwrites 1436 | //$ini_overwrites[] = 'setting=value'; 1437 | settings2array($ini_overwrites, $ini_settings); 1438 | 1439 | // Any special ini settings 1440 | // these may overwrite the test defaults... 1441 | if (array_key_exists('INI', $section_text)) { 1442 | if (strpos($section_text['INI'], '{PWD}') !== false) { 1443 | $section_text['INI'] = str_replace('{PWD}', dirname($file), $section_text['INI']); 1444 | } 1445 | settings2array(preg_split( "/[\n\r]+/", $section_text['INI']), $ini_settings); 1446 | } 1447 | 1448 | settings2params($ini_settings); 1449 | 1450 | // Check if test should be skipped. 1451 | $info = ''; 1452 | $warn = false; 1453 | 1454 | if (array_key_exists('SKIPIF', $section_text)) { 1455 | 1456 | if (trim($section_text['SKIPIF'])) { 1457 | show_file_block('skip', $section_text['SKIPIF']); 1458 | save_text($test_skipif, $section_text['SKIPIF'], $temp_skipif); 1459 | $extra = substr(PHP_OS, 0, 3) !== "WIN" ? 1460 | "unset REQUEST_METHOD; unset QUERY_STRING; unset PATH_TRANSLATED; unset SCRIPT_FILENAME; unset REQUEST_METHOD;": ""; 1461 | 1462 | if ($leak_check) { 1463 | $env['USE_ZEND_ALLOC'] = '0'; 1464 | } else { 1465 | $env['USE_ZEND_ALLOC'] = '1'; 1466 | } 1467 | 1468 | $output = system_with_timeout("$extra $php $pass_options -q $ini_settings -d display_errors=0 $test_skipif", $env); 1469 | 1470 | if (!$cfg['keep']['skip']) { 1471 | @unlink($test_skipif); 1472 | } 1473 | 1474 | if (!strncasecmp('skip', ltrim($output), 4)) { 1475 | 1476 | if (preg_match('/^\s*skip\s*(.+)\s*/i', $output, $m)) { 1477 | show_result('SKIP', $tested, $tested_file, "reason: $m[1]", $temp_filenames); 1478 | } else { 1479 | show_result('SKIP', $tested, $tested_file, '', $temp_filenames); 1480 | } 1481 | 1482 | if (isset($old_php)) { 1483 | $php = $old_php; 1484 | } 1485 | 1486 | if (!$cfg['keep']['skip']) { 1487 | @unlink($test_skipif); 1488 | } 1489 | 1490 | return 'SKIPPED'; 1491 | } 1492 | 1493 | if (!strncasecmp('info', ltrim($output), 4)) { 1494 | if (preg_match('/^\s*info\s*(.+)\s*/i', $output, $m)) { 1495 | $info = " (info: $m[1])"; 1496 | } 1497 | } 1498 | 1499 | if (!strncasecmp('warn', ltrim($output), 4)) { 1500 | if (preg_match('/^\s*warn\s*(.+)\s*/i', $output, $m)) { 1501 | $warn = true; /* only if there is a reason */ 1502 | $info = " (warn: $m[1])"; 1503 | } 1504 | } 1505 | } 1506 | } 1507 | 1508 | if (@count($section_text['REDIRECTTEST']) == 1) { 1509 | $test_files = array(); 1510 | 1511 | $IN_REDIRECT = eval($section_text['REDIRECTTEST']); 1512 | $IN_REDIRECT['via'] = "via [$shortname]\n\t"; 1513 | $IN_REDIRECT['dir'] = realpath(dirname($file)); 1514 | $IN_REDIRECT['prefix'] = trim($section_text['TEST']); 1515 | 1516 | if (count($IN_REDIRECT['TESTS']) == 1) { 1517 | 1518 | if (is_array($org_file)) { 1519 | $test_files[] = $org_file[1]; 1520 | } else { 1521 | $GLOBALS['test_files'] = $test_files; 1522 | find_files($IN_REDIRECT['TESTS']); 1523 | 1524 | foreach($GLOBALS['test_files'] as $f) { 1525 | $test_files[] = array($f, $file); 1526 | } 1527 | } 1528 | $test_cnt += @count($test_files) - 1; 1529 | $test_idx--; 1530 | 1531 | show_redirect_start($IN_REDIRECT['TESTS'], $tested, $tested_file); 1532 | 1533 | // set up environment 1534 | $redirenv = array_merge($environment, $IN_REDIRECT['ENV']); 1535 | $redirenv['REDIR_TEST_DIR'] = realpath($IN_REDIRECT['TESTS']) . DIRECTORY_SEPARATOR; 1536 | 1537 | usort($test_files, "test_sort"); 1538 | run_all_tests($test_files, $redirenv, $tested); 1539 | 1540 | show_redirect_ends($IN_REDIRECT['TESTS'], $tested, $tested_file); 1541 | 1542 | // a redirected test never fails 1543 | $IN_REDIRECT = false; 1544 | return 'REDIR'; 1545 | 1546 | } else { 1547 | 1548 | $bork_info = "Redirect info must contain exactly one TEST string to be used as redirect directory."; 1549 | show_result("BORK", $bork_info, '', $temp_filenames); 1550 | $PHP_FAILED_TESTS['BORKED'][] = array ( 1551 | 'name' => $file, 1552 | 'test_name' => '', 1553 | 'output' => '', 1554 | 'diff' => '', 1555 | 'info' => "$bork_info [$file]", 1556 | ); 1557 | } 1558 | } 1559 | 1560 | if (is_array($org_file) || @count($section_text['REDIRECTTEST']) == 1) { 1561 | 1562 | if (is_array($org_file)) { 1563 | $file = $org_file[0]; 1564 | } 1565 | 1566 | $bork_info = "Redirected test did not contain redirection info"; 1567 | show_result("BORK", $bork_info, '', $temp_filenames); 1568 | $PHP_FAILED_TESTS['BORKED'][] = array ( 1569 | 'name' => $file, 1570 | 'test_name' => '', 1571 | 'output' => '', 1572 | 'diff' => '', 1573 | 'info' => "$bork_info [$file]", 1574 | ); 1575 | return 'BORKED'; 1576 | } 1577 | 1578 | // We've satisfied the preconditions - run the test! 1579 | show_file_block('php', $section_text['FILE'], 'TEST'); 1580 | save_text($test_file, $section_text['FILE'], $temp_file); 1581 | 1582 | if (array_key_exists('GET', $section_text)) { 1583 | $query_string = trim($section_text['GET']); 1584 | } else { 1585 | $query_string = ''; 1586 | } 1587 | 1588 | $env['REDIRECT_STATUS'] = '1'; 1589 | $env['QUERY_STRING'] = $query_string; 1590 | $env['PATH_TRANSLATED'] = $test_file; 1591 | $env['SCRIPT_FILENAME'] = $test_file; 1592 | 1593 | if (array_key_exists('COOKIE', $section_text)) { 1594 | $env['HTTP_COOKIE'] = trim($section_text['COOKIE']); 1595 | } else { 1596 | $env['HTTP_COOKIE'] = ''; 1597 | } 1598 | 1599 | $args = isset($section_text['ARGS']) ? ' -- ' . $section_text['ARGS'] : ''; 1600 | 1601 | if (array_key_exists('POST_RAW', $section_text) && !empty($section_text['POST_RAW'])) { 1602 | 1603 | $post = trim($section_text['POST_RAW']); 1604 | $raw_lines = explode("\n", $post); 1605 | 1606 | $request = ''; 1607 | $started = false; 1608 | 1609 | foreach ($raw_lines as $line) { 1610 | 1611 | if (empty($env['CONTENT_TYPE']) && preg_match('/^Content-Type:(.*)/i', $line, $res)) { 1612 | $env['CONTENT_TYPE'] = trim(str_replace("\r", '', $res[1])); 1613 | continue; 1614 | } 1615 | 1616 | if ($started) { 1617 | $request .= "\n"; 1618 | } 1619 | 1620 | $started = true; 1621 | $request .= $line; 1622 | } 1623 | 1624 | $env['CONTENT_LENGTH'] = strlen($request); 1625 | $env['REQUEST_METHOD'] = 'POST'; 1626 | 1627 | if (empty($request)) { 1628 | return 'BORKED'; 1629 | } 1630 | 1631 | save_text($tmp_post, $request); 1632 | $cmd = "$php $pass_options $ini_settings -f \"$test_file\" 2>&1 < $tmp_post"; 1633 | 1634 | } else if (array_key_exists('POST', $section_text) && !empty($section_text['POST'])) { 1635 | 1636 | $post = trim($section_text['POST']); 1637 | 1638 | if (array_key_exists('GZIP_POST', $section_text) && function_exists('gzencode')) { 1639 | $post = gzencode($post, 9, FORCE_GZIP); 1640 | $env['HTTP_CONTENT_ENCODING'] = 'gzip'; 1641 | } else if (array_key_exists('DEFLATE_POST', $section_text) && function_exists('gzcompress')) { 1642 | $post = gzcompress($post, 9); 1643 | $env['HTTP_CONTENT_ENCODING'] = 'deflate'; 1644 | } 1645 | 1646 | save_text($tmp_post, $post); 1647 | $content_length = strlen($post); 1648 | 1649 | $env['REQUEST_METHOD'] = 'POST'; 1650 | $env['CONTENT_TYPE'] = 'application/x-www-form-urlencoded'; 1651 | $env['CONTENT_LENGTH'] = $content_length; 1652 | 1653 | $cmd = "$php $pass_options $ini_settings -f \"$test_file\" 2>&1 < $tmp_post"; 1654 | 1655 | } else { 1656 | 1657 | $env['REQUEST_METHOD'] = 'GET'; 1658 | $env['CONTENT_TYPE'] = ''; 1659 | $env['CONTENT_LENGTH'] = ''; 1660 | 1661 | $cmd = "$php $pass_options $ini_settings -f \"$test_file\" $args 2>&1"; 1662 | } 1663 | 1664 | if ($leak_check) { 1665 | $env['USE_ZEND_ALLOC'] = '0'; 1666 | 1667 | if ($valgrind_version >= 330) { 1668 | /* valgrind 3.3.0+ doesn't have --log-file-exactly option */ 1669 | $cmd = "valgrind -q --tool=memcheck --trace-children=yes --log-file=$memcheck_filename $cmd"; 1670 | } else { 1671 | $cmd = "valgrind -q --tool=memcheck --trace-children=yes --log-file-exactly=$memcheck_filename $cmd"; 1672 | } 1673 | 1674 | } else { 1675 | $env['USE_ZEND_ALLOC'] = '1'; 1676 | } 1677 | 1678 | if ($DETAILED) echo " 1679 | CONTENT_LENGTH = " . $env['CONTENT_LENGTH'] . " 1680 | CONTENT_TYPE = " . $env['CONTENT_TYPE'] . " 1681 | PATH_TRANSLATED = " . $env['PATH_TRANSLATED'] . " 1682 | QUERY_STRING = " . $env['QUERY_STRING'] . " 1683 | REDIRECT_STATUS = " . $env['REDIRECT_STATUS'] . " 1684 | REQUEST_METHOD = " . $env['REQUEST_METHOD'] . " 1685 | SCRIPT_FILENAME = " . $env['SCRIPT_FILENAME'] . " 1686 | HTTP_COOKIE = " . $env['HTTP_COOKIE'] . " 1687 | COMMAND $cmd 1688 | "; 1689 | 1690 | $out = (binary) system_with_timeout($cmd, $env, isset($section_text['STDIN']) ? $section_text['STDIN'] : null); 1691 | 1692 | if (array_key_exists('CLEAN', $section_text) && (!$no_clean || $cfg['keep']['clean'])) { 1693 | 1694 | if (trim($section_text['CLEAN'])) { 1695 | show_file_block('clean', $section_text['CLEAN']); 1696 | save_text($test_clean, trim($section_text['CLEAN']), $temp_clean); 1697 | 1698 | if (!$no_clean) { 1699 | $clean_params = array(); 1700 | settings2array($ini_overwrites, $clean_params); 1701 | settings2params($clean_params); 1702 | $extra = substr(PHP_OS, 0, 3) !== "WIN" ? 1703 | "unset REQUEST_METHOD; unset QUERY_STRING; unset PATH_TRANSLATED; unset SCRIPT_FILENAME; unset REQUEST_METHOD;": ""; 1704 | system_with_timeout("$extra $php $pass_options -q $clean_params $test_clean", $env); 1705 | } 1706 | 1707 | if (!$cfg['keep']['clean']) { 1708 | @unlink($test_clean); 1709 | } 1710 | } 1711 | } 1712 | 1713 | @unlink($tmp_post); 1714 | 1715 | $leaked = false; 1716 | $passed = false; 1717 | 1718 | if ($leak_check) { // leak check 1719 | $leaked = filesize($memcheck_filename) > 0; 1720 | 1721 | if (!$leaked) { 1722 | @unlink($memcheck_filename); 1723 | } 1724 | } 1725 | 1726 | // Does the output match what is expected? 1727 | $output = preg_replace(b"/\r\n/", b"\n", trim($out)); 1728 | 1729 | /* when using CGI, strip the headers from the output */ 1730 | $headers = b""; 1731 | 1732 | if (isset($old_php) && preg_match(b"/^(.*?)\r?\n\r?\n(.*)/s", $out, $match)) { 1733 | $output = trim($match[2]); 1734 | $rh = preg_split(b"/[\n\r]+/", $match[1]); 1735 | $headers = array(); 1736 | 1737 | foreach ($rh as $line) { 1738 | if (strpos($line, b':') !== false) { 1739 | $line = explode(b':', $line, 2); 1740 | $headers[trim($line[0])] = trim($line[1]); 1741 | } 1742 | } 1743 | } 1744 | 1745 | $failed_headers = false; 1746 | 1747 | if (isset($section_text['EXPECTHEADERS'])) { 1748 | $want = array(); 1749 | $wanted_headers = array(); 1750 | $lines = preg_split(b"/[\n\r]+/", (binary) $section_text['EXPECTHEADERS']); 1751 | 1752 | foreach($lines as $line) { 1753 | if (strpos($line, b':') !== false) { 1754 | $line = explode(b':', $line, 2); 1755 | $want[trim($line[0])] = trim($line[1]); 1756 | $wanted_headers[] = trim($line[0]) . b': ' . trim($line[1]); 1757 | } 1758 | } 1759 | 1760 | $org_headers = $headers; 1761 | $headers = array(); 1762 | $output_headers = array(); 1763 | 1764 | foreach($want as $k => $v) { 1765 | 1766 | if (isset($org_headers[$k])) { 1767 | $headers = $org_headers[$k]; 1768 | $output_headers[] = $k . b': ' . $org_headers[$k]; 1769 | } 1770 | 1771 | if (!isset($org_headers[$k]) || $org_headers[$k] != $v) { 1772 | $failed_headers = true; 1773 | } 1774 | } 1775 | 1776 | ksort($wanted_headers); 1777 | $wanted_headers = join(b"\n", $wanted_headers); 1778 | ksort($output_headers); 1779 | $output_headers = join(b"\n", $output_headers); 1780 | } 1781 | 1782 | show_file_block('out', $output); 1783 | 1784 | if (isset($section_text['EXPECTF']) || isset($section_text['EXPECTREGEX'])) { 1785 | 1786 | if (isset($section_text['EXPECTF'])) { 1787 | $wanted = trim($section_text['EXPECTF']); 1788 | } else { 1789 | $wanted = trim($section_text['EXPECTREGEX']); 1790 | } 1791 | 1792 | show_file_block('exp', $wanted); 1793 | $wanted_re = preg_replace(b'/\r\n/', b"\n", $wanted); 1794 | 1795 | if (isset($section_text['EXPECTF'])) { 1796 | 1797 | // do preg_quote, but miss out any %r delimited sections 1798 | $temp = b""; 1799 | $r = b"%r"; 1800 | $startOffset = 0; 1801 | $length = strlen($wanted_re); 1802 | while($startOffset < $length) { 1803 | $start = strpos($wanted_re, $r, $startOffset); 1804 | if ($start !== false) { 1805 | // we have found a start tag 1806 | $end = strpos($wanted_re, $r, $start+2); 1807 | if ($end === false) { 1808 | // unbalanced tag, ignore it. 1809 | $end = $start = $length; 1810 | } 1811 | } else { 1812 | // no more %r sections 1813 | $start = $end = $length; 1814 | } 1815 | // quote a non re portion of the string 1816 | $temp = $temp . preg_quote(substr($wanted_re, $startOffset, ($start - $startOffset)), b'/'); 1817 | // add the re unquoted. 1818 | if ($end > $start) { 1819 | $temp = $temp . b'(' . substr($wanted_re, $start+2, ($end - $start-2)). b')'; 1820 | } 1821 | $startOffset = $end + 2; 1822 | } 1823 | $wanted_re = $temp; 1824 | 1825 | $wanted_re = str_replace( 1826 | array(b'%binary_string_optional%'), 1827 | version_compare(PHP_VERSION, '6.0.0-dev') == -1 ? b'string' : b'binary string', 1828 | $wanted_re 1829 | ); 1830 | $wanted_re = str_replace( 1831 | array(b'%unicode_string_optional%'), 1832 | version_compare(PHP_VERSION, '6.0.0-dev') == -1 ? b'string' : b'Unicode string', 1833 | $wanted_re 1834 | ); 1835 | $wanted_re = str_replace( 1836 | array(b'%unicode\|string%', b'%string\|unicode%'), 1837 | version_compare(PHP_VERSION, '6.0.0-dev') == -1 ? b'string' : b'unicode', 1838 | $wanted_re 1839 | ); 1840 | $wanted_re = str_replace( 1841 | array(b'%u\|b%', b'%b\|u%'), 1842 | version_compare(PHP_VERSION, '6.0.0-dev') == -1 ? b'' : b'u', 1843 | $wanted_re 1844 | ); 1845 | // Stick to basics 1846 | $wanted_re = str_replace(b'%e', b'\\' . DIRECTORY_SEPARATOR, $wanted_re); 1847 | $wanted_re = str_replace(b'%s', b'[^\r\n]+', $wanted_re); 1848 | $wanted_re = str_replace(b'%S', b'[^\r\n]*', $wanted_re); 1849 | $wanted_re = str_replace(b'%a', b'.+', $wanted_re); 1850 | $wanted_re = str_replace(b'%A', b'.*', $wanted_re); 1851 | $wanted_re = str_replace(b'%w', b'\s*', $wanted_re); 1852 | $wanted_re = str_replace(b'%i', b'[+-]?\d+', $wanted_re); 1853 | $wanted_re = str_replace(b'%d', b'\d+', $wanted_re); 1854 | $wanted_re = str_replace(b'%x', b'[0-9a-fA-F]+', $wanted_re); 1855 | $wanted_re = str_replace(b'%f', b'[+-]?\.?\d+\.?\d*(?:[Ee][+-]?\d+)?', $wanted_re); 1856 | $wanted_re = str_replace(b'%c', b'.', $wanted_re); 1857 | // %f allows two points "-.0.0" but that is the best *simple* expression 1858 | } 1859 | /* DEBUG YOUR REGEX HERE 1860 | var_dump($wanted_re); 1861 | print(str_repeat('=', 80) . "\n"); 1862 | var_dump($output); 1863 | */ 1864 | if (preg_match(b"/^$wanted_re\$/s", $output)) { 1865 | $passed = true; 1866 | if (!$cfg['keep']['php']) { 1867 | @unlink($test_file); 1868 | } 1869 | if (isset($old_php)) { 1870 | $php = $old_php; 1871 | } 1872 | 1873 | if (!$leaked && !$failed_headers) { 1874 | if (isset($section_text['XFAIL'] )) { 1875 | $warn = true; 1876 | $info = " (warn: XFAIL section but test passes)"; 1877 | }else { 1878 | show_result("PASS", $tested, $tested_file, '', $temp_filenames); 1879 | return 'PASSED'; 1880 | } 1881 | } 1882 | } 1883 | 1884 | } else { 1885 | 1886 | $wanted = (binary) trim($section_text['EXPECT']); 1887 | $wanted = preg_replace(b'/\r\n/',b"\n", $wanted); 1888 | show_file_block('exp', $wanted); 1889 | 1890 | // compare and leave on success 1891 | if (!strcmp($output, $wanted)) { 1892 | $passed = true; 1893 | 1894 | if (!$cfg['keep']['php']) { 1895 | @unlink($test_file); 1896 | } 1897 | 1898 | if (isset($old_php)) { 1899 | $php = $old_php; 1900 | } 1901 | 1902 | if (!$leaked && !$failed_headers) { 1903 | if (isset($section_text['XFAIL'] )) { 1904 | $warn = true; 1905 | $info = " (warn: XFAIL section but test passes)"; 1906 | }else { 1907 | show_result("PASS", $tested, $tested_file, '', $temp_filenames); 1908 | return 'PASSED'; 1909 | } 1910 | } 1911 | } 1912 | 1913 | $wanted_re = null; 1914 | } 1915 | 1916 | // Test failed so we need to report details. 1917 | if ($failed_headers) { 1918 | $passed = false; 1919 | $wanted = (binary) $wanted_headers . b"\n--HEADERS--\n" . (binary) $wanted; 1920 | $output = (binary) $output_headers . b"\n--HEADERS--\n" . (binary) $output; 1921 | 1922 | if (isset($wanted_re)) { 1923 | $wanted_re = preg_quote($wanted_headers . "\n--HEADERS--\n", '/') . $wanted_re; 1924 | } 1925 | } 1926 | 1927 | if ($leaked) { 1928 | $restype[] = 'LEAK'; 1929 | } 1930 | 1931 | if ($warn) { 1932 | $restype[] = 'WARN'; 1933 | } 1934 | 1935 | if (!$passed) { 1936 | if (isset($section_text['XFAIL'])) { 1937 | $restype[] = 'XFAIL'; 1938 | $info = ' XFAIL REASON: ' . $section_text['XFAIL']; 1939 | } else { 1940 | $restype[] = 'FAIL'; 1941 | } 1942 | } 1943 | 1944 | if (!$passed) { 1945 | 1946 | // write .exp 1947 | if (strpos($log_format, 'E') !== false && file_put_contents($exp_filename, (binary) $wanted, FILE_BINARY) === false) { 1948 | error("Cannot create expected test output - $exp_filename"); 1949 | } 1950 | 1951 | // write .out 1952 | if (strpos($log_format, 'O') !== false && file_put_contents($output_filename, (binary) $output, FILE_BINARY) === false) { 1953 | error("Cannot create test output - $output_filename"); 1954 | } 1955 | 1956 | // write .diff 1957 | $diff = generate_diff($wanted, $wanted_re, $output); 1958 | if (is_array($IN_REDIRECT)) { 1959 | $diff = "# original source file: $shortname\n" . $diff; 1960 | } 1961 | show_file_block('diff', $diff); 1962 | if (strpos($log_format, 'D') !== false && file_put_contents($diff_filename, (binary) $diff, FILE_BINARY) === false) { 1963 | error("Cannot create test diff - $diff_filename"); 1964 | } 1965 | 1966 | // write .sh 1967 | if (strpos($log_format, 'S') !== false && file_put_contents($sh_filename, b"#!/bin/sh 1968 | 1969 | {$cmd} 1970 | ", FILE_BINARY) === false) { 1971 | error("Cannot create test shell script - $sh_filename"); 1972 | } 1973 | chmod($sh_filename, 0755); 1974 | 1975 | // write .log 1976 | if (strpos($log_format, 'L') !== false && file_put_contents($log_filename, b" 1977 | ---- EXPECTED OUTPUT 1978 | $wanted 1979 | ---- ACTUAL OUTPUT 1980 | $output 1981 | ---- FAILED 1982 | ", FILE_BINARY) === false) { 1983 | error("Cannot create test log - $log_filename"); 1984 | error_report($file, $log_filename, $tested); 1985 | } 1986 | } 1987 | 1988 | show_result(implode('&', $restype), $tested, $tested_file, $info, $temp_filenames); 1989 | 1990 | foreach ($restype as $type) { 1991 | $PHP_FAILED_TESTS[$type.'ED'][] = array ( 1992 | 'name' => $file, 1993 | 'test_name' => (is_array($IN_REDIRECT) ? $IN_REDIRECT['via'] : '') . $tested . " [$tested_file]", 1994 | 'output' => $output_filename, 1995 | 'diff' => $diff_filename, 1996 | 'info' => $info, 1997 | ); 1998 | } 1999 | 2000 | if (isset($old_php)) { 2001 | $php = $old_php; 2002 | } 2003 | 2004 | return $restype[0] . 'ED'; 2005 | } 2006 | 2007 | function comp_line($l1, $l2, $is_reg) 2008 | { 2009 | if ($is_reg) { 2010 | return preg_match(b'/^'. (binary) $l1 . b'$/s', (binary) $l2); 2011 | } else { 2012 | return !strcmp((binary) $l1, (binary) $l2); 2013 | } 2014 | } 2015 | 2016 | function count_array_diff($ar1, $ar2, $is_reg, $w, $idx1, $idx2, $cnt1, $cnt2, $steps) 2017 | { 2018 | $equal = 0; 2019 | 2020 | while ($idx1 < $cnt1 && $idx2 < $cnt2 && comp_line($ar1[$idx1], $ar2[$idx2], $is_reg)) { 2021 | $idx1++; 2022 | $idx2++; 2023 | $equal++; 2024 | $steps--; 2025 | } 2026 | if (--$steps > 0) { 2027 | $eq1 = 0; 2028 | $st = $steps / 2; 2029 | 2030 | for ($ofs1 = $idx1 + 1; $ofs1 < $cnt1 && $st-- > 0; $ofs1++) { 2031 | $eq = @count_array_diff($ar1, $ar2, $is_reg, $w, $ofs1, $idx2, $cnt1, $cnt2, $st); 2032 | 2033 | if ($eq > $eq1) { 2034 | $eq1 = $eq; 2035 | } 2036 | } 2037 | 2038 | $eq2 = 0; 2039 | $st = $steps; 2040 | 2041 | for ($ofs2 = $idx2 + 1; $ofs2 < $cnt2 && $st-- > 0; $ofs2++) { 2042 | $eq = @count_array_diff($ar1, $ar2, $is_reg, $w, $idx1, $ofs2, $cnt1, $cnt2, $st); 2043 | if ($eq > $eq2) { 2044 | $eq2 = $eq; 2045 | } 2046 | } 2047 | 2048 | if ($eq1 > $eq2) { 2049 | $equal += $eq1; 2050 | } else if ($eq2 > 0) { 2051 | $equal += $eq2; 2052 | } 2053 | } 2054 | 2055 | return $equal; 2056 | } 2057 | 2058 | function generate_array_diff($ar1, $ar2, $is_reg, $w) 2059 | { 2060 | $idx1 = 0; $ofs1 = 0; $cnt1 = @count($ar1); 2061 | $idx2 = 0; $ofs2 = 0; $cnt2 = @count($ar2); 2062 | $diff = array(); 2063 | $old1 = array(); 2064 | $old2 = array(); 2065 | 2066 | while ($idx1 < $cnt1 && $idx2 < $cnt2) { 2067 | 2068 | if (comp_line($ar1[$idx1], $ar2[$idx2], $is_reg)) { 2069 | $idx1++; 2070 | $idx2++; 2071 | continue; 2072 | } else { 2073 | 2074 | $c1 = @count_array_diff($ar1, $ar2, $is_reg, $w, $idx1+1, $idx2, $cnt1, $cnt2, 10); 2075 | $c2 = @count_array_diff($ar1, $ar2, $is_reg, $w, $idx1, $idx2+1, $cnt1, $cnt2, 10); 2076 | 2077 | if ($c1 > $c2) { 2078 | $old1[$idx1] = (binary) sprintf("%03d- ", $idx1+1) . $w[$idx1++]; 2079 | $last = 1; 2080 | } else if ($c2 > 0) { 2081 | $old2[$idx2] = (binary) sprintf("%03d+ ", $idx2+1) . $ar2[$idx2++]; 2082 | $last = 2; 2083 | } else { 2084 | $old1[$idx1] = (binary) sprintf("%03d- ", $idx1+1) . $w[$idx1++]; 2085 | $old2[$idx2] = (binary) sprintf("%03d+ ", $idx2+1) . $ar2[$idx2++]; 2086 | } 2087 | } 2088 | } 2089 | 2090 | reset($old1); $k1 = key($old1); $l1 = -2; 2091 | reset($old2); $k2 = key($old2); $l2 = -2; 2092 | 2093 | while ($k1 !== null || $k2 !== null) { 2094 | 2095 | if ($k1 == $l1 + 1 || $k2 === null) { 2096 | $l1 = $k1; 2097 | $diff[] = current($old1); 2098 | $k1 = next($old1) ? key($old1) : null; 2099 | } else if ($k2 == $l2 + 1 || $k1 === null) { 2100 | $l2 = $k2; 2101 | $diff[] = current($old2); 2102 | $k2 = next($old2) ? key($old2) : null; 2103 | } else if ($k1 < $k2) { 2104 | $l1 = $k1; 2105 | $diff[] = current($old1); 2106 | $k1 = next($old1) ? key($old1) : null; 2107 | } else { 2108 | $l2 = $k2; 2109 | $diff[] = current($old2); 2110 | $k2 = next($old2) ? key($old2) : null; 2111 | } 2112 | } 2113 | 2114 | while ($idx1 < $cnt1) { 2115 | $diff[] = (binary) sprintf("%03d- ", $idx1 + 1) . $w[$idx1++]; 2116 | } 2117 | 2118 | while ($idx2 < $cnt2) { 2119 | $diff[] = (binary) sprintf("%03d+ ", $idx2 + 1) . $ar2[$idx2++]; 2120 | } 2121 | 2122 | return $diff; 2123 | } 2124 | 2125 | function generate_diff($wanted, $wanted_re, $output) 2126 | { 2127 | $w = explode(b"\n", $wanted); 2128 | $o = explode(b"\n", $output); 2129 | $r = is_null($wanted_re) ? $w : explode(b"\n", $wanted_re); 2130 | $diff = generate_array_diff($r, $o, !is_null($wanted_re), $w); 2131 | 2132 | return implode(b"\r\n", $diff); 2133 | } 2134 | 2135 | function error($message) 2136 | { 2137 | echo "ERROR: {$message}\n"; 2138 | exit(1); 2139 | } 2140 | 2141 | function settings2array($settings, &$ini_settings) 2142 | { 2143 | foreach($settings as $setting) { 2144 | 2145 | if (strpos($setting, '=') !== false) { 2146 | $setting = explode("=", $setting, 2); 2147 | $name = trim($setting[0]); 2148 | $value = trim($setting[1]); 2149 | 2150 | if ($name == 'extension') { 2151 | 2152 | if (!isset($ini_settings[$name])) { 2153 | $ini_settings[$name] = array(); 2154 | } 2155 | 2156 | $ini_settings[$name][] = $value; 2157 | 2158 | } else { 2159 | $ini_settings[$name] = $value; 2160 | } 2161 | } 2162 | } 2163 | } 2164 | 2165 | function settings2params(&$ini_settings) 2166 | { 2167 | $settings = ''; 2168 | 2169 | foreach($ini_settings as $name => $value) { 2170 | 2171 | if (is_array($value)) { 2172 | foreach($value as $val) { 2173 | $val = addslashes($val); 2174 | $settings .= " -d \"$name=$val\""; 2175 | } 2176 | } else { 2177 | if (substr(PHP_OS, 0, 3) == "WIN" && !empty($value) && $value{0} == '"') { 2178 | $len = strlen($value); 2179 | 2180 | if ($value{$len - 1} == '"') { 2181 | $value{0} = "'"; 2182 | $value{$len - 1} = "'"; 2183 | } 2184 | } else { 2185 | $value = addslashes($value); 2186 | } 2187 | 2188 | $settings .= " -d \"$name=$value\""; 2189 | } 2190 | } 2191 | 2192 | $ini_settings = $settings; 2193 | } 2194 | 2195 | function compute_summary() 2196 | { 2197 | global $n_total, $test_results, $ignored_by_ext, $sum_results, $percent_results; 2198 | 2199 | $n_total = count($test_results); 2200 | $n_total += $ignored_by_ext; 2201 | $sum_results = array( 2202 | 'PASSED' => 0, 2203 | 'WARNED' => 0, 2204 | 'SKIPPED' => 0, 2205 | 'FAILED' => 0, 2206 | 'BORKED' => 0, 2207 | 'LEAKED' => 0, 2208 | 'XFAILED' => 0 2209 | ); 2210 | 2211 | foreach ($test_results as $v) { 2212 | $sum_results[$v]++; 2213 | } 2214 | 2215 | $sum_results['SKIPPED'] += $ignored_by_ext; 2216 | $percent_results = array(); 2217 | 2218 | while (list($v, $n) = each($sum_results)) { 2219 | $percent_results[$v] = (100.0 * $n) / $n_total; 2220 | } 2221 | } 2222 | 2223 | function get_summary($show_ext_summary, $show_html) 2224 | { 2225 | global $exts_skipped, $exts_tested, $n_total, $sum_results, $percent_results, $end_time, $start_time, $failed_test_summary, $PHP_FAILED_TESTS, $leak_check; 2226 | 2227 | $x_total = $n_total - $sum_results['SKIPPED'] - $sum_results['BORKED']; 2228 | 2229 | if ($x_total) { 2230 | $x_warned = (100.0 * $sum_results['WARNED']) / $x_total; 2231 | $x_failed = (100.0 * $sum_results['FAILED']) / $x_total; 2232 | $x_xfailed = (100.0 * $sum_results['XFAILED']) / $x_total; 2233 | $x_leaked = (100.0 * $sum_results['LEAKED']) / $x_total; 2234 | $x_passed = (100.0 * $sum_results['PASSED']) / $x_total; 2235 | } else { 2236 | $x_warned = $x_failed = $x_passed = $x_leaked = $x_xfailed = 0; 2237 | } 2238 | 2239 | $summary = ''; 2240 | 2241 | if ($show_html) { 2242 | $summary .= "
\n";
2243 | 	}
2244 | 
2245 | 	if ($show_ext_summary) {
2246 | 		$summary .= '
2247 | =====================================================================
2248 | TEST RESULT SUMMARY
2249 | ---------------------------------------------------------------------
2250 | Exts skipped    : ' . sprintf('%4d', $exts_skipped) . '
2251 | Exts tested     : ' . sprintf('%4d', $exts_tested) . '
2252 | ---------------------------------------------------------------------
2253 | ';
2254 | 	}
2255 | 
2256 | 	$summary .= '
2257 | Number of tests : ' . sprintf('%4d', $n_total) . '          ' . sprintf('%8d', $x_total);
2258 | 
2259 | 	if ($sum_results['BORKED']) {
2260 | 		$summary .= '
2261 | Tests borked    : ' . sprintf('%4d (%5.1f%%)', $sum_results['BORKED'], $percent_results['BORKED']) . ' --------';
2262 | 	}
2263 | 
2264 | 	$summary .= '
2265 | Tests skipped   : ' . sprintf('%4d (%5.1f%%)', $sum_results['SKIPPED'], $percent_results['SKIPPED']) . ' --------
2266 | Tests warned    : ' . sprintf('%4d (%5.1f%%)', $sum_results['WARNED'], $percent_results['WARNED']) . ' ' . sprintf('(%5.1f%%)', $x_warned) . '
2267 | Tests failed    : ' . sprintf('%4d (%5.1f%%)', $sum_results['FAILED'], $percent_results['FAILED']) . ' ' . sprintf('(%5.1f%%)', $x_failed) . '
2268 | Expected fail   : ' . sprintf('%4d (%5.1f%%)', $sum_results['XFAILED'], $percent_results['XFAILED']) . ' ' . sprintf('(%5.1f%%)', $x_xfailed);
2269 | 
2270 | 	if ($leak_check) {
2271 | 		$summary .= '
2272 | Tests leaked    : ' . sprintf('%4d (%5.1f%%)', $sum_results['LEAKED'], $percent_results['LEAKED']) . ' ' . sprintf('(%5.1f%%)', $x_leaked);
2273 | 	}
2274 | 
2275 | 	$summary .= '
2276 | Tests passed    : ' . sprintf('%4d (%5.1f%%)', $sum_results['PASSED'], $percent_results['PASSED']) . ' ' . sprintf('(%5.1f%%)', $x_passed) . '
2277 | ---------------------------------------------------------------------
2278 | Time taken      : ' . sprintf('%4d seconds', $end_time - $start_time) . '
2279 | =====================================================================
2280 | ';
2281 | 	$failed_test_summary = '';
2282 | 
2283 | 	if (count($PHP_FAILED_TESTS['BORKED'])) {
2284 | 		$failed_test_summary .= '
2285 | =====================================================================
2286 | BORKED TEST SUMMARY
2287 | ---------------------------------------------------------------------
2288 | ';
2289 | 		foreach ($PHP_FAILED_TESTS['BORKED'] as $failed_test_data) {
2290 | 			$failed_test_summary .= $failed_test_data['info'] . "\n";
2291 | 		}
2292 | 
2293 | 		$failed_test_summary .=  "=====================================================================\n";
2294 | 	}
2295 | 
2296 | 	if (count($PHP_FAILED_TESTS['FAILED'])) {
2297 | 		$failed_test_summary .= '
2298 | =====================================================================
2299 | FAILED TEST SUMMARY
2300 | ---------------------------------------------------------------------
2301 | ';
2302 | 		foreach ($PHP_FAILED_TESTS['FAILED'] as $failed_test_data) {
2303 | 			$failed_test_summary .= $failed_test_data['test_name'] . $failed_test_data['info'] . "\n";
2304 | 		}
2305 | 		$failed_test_summary .=  "=====================================================================\n";
2306 | 	}
2307 | 	if (count($PHP_FAILED_TESTS['XFAILED'])) {
2308 | 		$failed_test_summary .= '
2309 | =====================================================================
2310 | EXPECTED FAILED TEST SUMMARY
2311 | ---------------------------------------------------------------------
2312 | ';
2313 | 		foreach ($PHP_FAILED_TESTS['XFAILED'] as $failed_test_data) {
2314 | 			$failed_test_summary .= $failed_test_data['test_name'] . $failed_test_data['info'] . "\n";
2315 | 		}
2316 | 		$failed_test_summary .=  "=====================================================================\n";
2317 | 	}
2318 | 
2319 | 	if (count($PHP_FAILED_TESTS['WARNED'])) {
2320 | 		$failed_test_summary .= '
2321 | =====================================================================
2322 | WARNED TEST SUMMARY
2323 | ---------------------------------------------------------------------
2324 | ';
2325 | 		foreach ($PHP_FAILED_TESTS['WARNED'] as $failed_test_data) {
2326 | 			$failed_test_summary .= $failed_test_data['test_name'] . $failed_test_data['info'] . "\n";
2327 | 		}
2328 | 
2329 | 		$failed_test_summary .=  "=====================================================================\n";
2330 | 	}
2331 | 
2332 | 	if (count($PHP_FAILED_TESTS['LEAKED'])) {
2333 | 		$failed_test_summary .= '
2334 | =====================================================================
2335 | LEAKED TEST SUMMARY
2336 | ---------------------------------------------------------------------
2337 | ';
2338 | 		foreach ($PHP_FAILED_TESTS['LEAKED'] as $failed_test_data) {
2339 | 			$failed_test_summary .= $failed_test_data['test_name'] . $failed_test_data['info'] . "\n";
2340 | 		}
2341 | 
2342 | 		$failed_test_summary .=  "=====================================================================\n";
2343 | 	}
2344 | 
2345 | 	if ($failed_test_summary && !getenv('NO_PHPTEST_SUMMARY')) {
2346 | 		$summary .= $failed_test_summary;
2347 | 	}
2348 | 
2349 | 	if ($show_html) {
2350 | 		$summary .= "
"; 2351 | } 2352 | 2353 | return $summary; 2354 | } 2355 | 2356 | function show_start($start_time) 2357 | { 2358 | global $html_output, $html_file; 2359 | 2360 | if ($html_output) { 2361 | fwrite($html_file, "

Time Start: " . date('Y-m-d H:i:s', $start_time) . "

\n"); 2362 | fwrite($html_file, "\n"); 2363 | } 2364 | 2365 | echo "TIME START " . date('Y-m-d H:i:s', $start_time) . "\n=====================================================================\n"; 2366 | } 2367 | 2368 | function show_end($end_time) 2369 | { 2370 | global $html_output, $html_file; 2371 | 2372 | if ($html_output) { 2373 | fwrite($html_file, "
\n"); 2374 | fwrite($html_file, "

Time End: " . date('Y-m-d H:i:s', $end_time) . "

\n"); 2375 | } 2376 | 2377 | echo "=====================================================================\nTIME END " . date('Y-m-d H:i:s', $end_time) . "\n"; 2378 | } 2379 | 2380 | function show_summary() 2381 | { 2382 | global $html_output, $html_file; 2383 | 2384 | if ($html_output) { 2385 | fwrite($html_file, "
\n" . get_summary(true, true)); 2386 | } 2387 | 2388 | echo get_summary(true, false); 2389 | } 2390 | 2391 | function show_redirect_start($tests, $tested, $tested_file) 2392 | { 2393 | global $html_output, $html_file; 2394 | 2395 | if ($html_output) { 2396 | fwrite($html_file, "---> $tests ($tested [$tested_file]) begin\n"); 2397 | } 2398 | 2399 | echo "---> $tests ($tested [$tested_file]) begin\n"; 2400 | } 2401 | 2402 | function show_redirect_ends($tests, $tested, $tested_file) 2403 | { 2404 | global $html_output, $html_file; 2405 | 2406 | if ($html_output) { 2407 | fwrite($html_file, "---> $tests ($tested [$tested_file]) done\n"); 2408 | } 2409 | 2410 | echo "---> $tests ($tested [$tested_file]) done\n"; 2411 | } 2412 | 2413 | function show_test($test_idx, $shortname) 2414 | { 2415 | global $test_cnt; 2416 | 2417 | echo "TEST $test_idx/$test_cnt [$shortname]\r"; 2418 | flush(); 2419 | } 2420 | 2421 | function show_result($result, $tested, $tested_file, $extra = '', $temp_filenames = null) 2422 | { 2423 | global $html_output, $html_file, $temp_target, $temp_urlbase; 2424 | 2425 | echo "$result $tested [$tested_file] $extra\n"; 2426 | 2427 | if ($html_output) { 2428 | 2429 | if (isset($temp_filenames['file']) && file_exists($temp_filenames['file'])) { 2430 | $url = str_replace($temp_target, $temp_urlbase, $temp_filenames['file']); 2431 | $tested = "$tested"; 2432 | } 2433 | 2434 | if (isset($temp_filenames['skip']) && file_exists($temp_filenames['skip'])) { 2435 | 2436 | if (empty($extra)) { 2437 | $extra = "skipif"; 2438 | } 2439 | 2440 | $url = str_replace($temp_target, $temp_urlbase, $temp_filenames['skip']); 2441 | $extra = "$extra"; 2442 | 2443 | } else if (empty($extra)) { 2444 | $extra = " "; 2445 | } 2446 | 2447 | if (isset($temp_filenames['diff']) && file_exists($temp_filenames['diff'])) { 2448 | $url = str_replace($temp_target, $temp_urlbase, $temp_filenames['diff']); 2449 | $diff = "diff"; 2450 | } else { 2451 | $diff = " "; 2452 | } 2453 | 2454 | if (isset($temp_filenames['mem']) && file_exists($temp_filenames['mem'])) { 2455 | $url = str_replace($temp_target, $temp_urlbase, $temp_filenames['mem']); 2456 | $mem = "leaks"; 2457 | } else { 2458 | $mem = " "; 2459 | } 2460 | 2461 | fwrite($html_file, 2462 | "" . 2463 | "$result" . 2464 | "$tested" . 2465 | "$extra" . 2466 | "$diff" . 2467 | "$mem" . 2468 | "\n"); 2469 | } 2470 | } 2471 | 2472 | /* 2473 | * Local variables: 2474 | * tab-width: 4 2475 | * c-basic-offset: 4 2476 | * End: 2477 | * vim: noet sw=4 ts=4 2478 | */ 2479 | ?> 2480 | --------------------------------------------------------------------------------