/** run tests on a function. the code is passed in $txt */ function check_function($name, $txt, $offset) { global $API_params; if (preg_match_all('/zend_parse_parameters(?:_ex\\s*\\([^,]+,[^,]+|\\s*\\([^,]+),\\s*"([^"]*)"\\s*,\\s*([^{;]*)/S', $txt, $matches, PREG_SET_ORDER | PREG_OFFSET_CAPTURE)) { $GLOBALS['current_function'] = $name; foreach ($matches as $m) { $GLOBALS['error_few_vars_given'] = false; update_lineno($offset + $m[2][1]); $vars = get_vars(substr($txt, 0, $m[0][1])); // limit var search to current location $params = get_params($vars, $m[2][0]); $optional = $varargs = false; $last_last_char = $last_char = ''; $j = -1; $len = strlen($m[1][0]); for ($i = 0; $i < $len; ++$i) { switch ($char = $m[1][0][$i]) { // separator for optional parameters case '|': if ($optional) { error("more than one optional separator at char #{$i}"); } else { $optional = true; if ($i == $len - 1) { error("unnecessary optional separator"); } } break; // separate_zval_if_not_ref // separate_zval_if_not_ref case '/': if (!in_array($last_char, array('r', 'z'))) { error("the '/' specifier cannot be applied to '{$last_char}'"); } break; // nullable arguments // nullable arguments case '!': if (!in_array($last_char, array('a', 'C', 'f', 'h', 'o', 'O', 'r', 's', 't', 'z', 'Z'))) { error("the '!' specifier cannot be applied to '{$last_char}'"); } break; case '&': if (version_compare(VERSION, '6', 'ge')) { if ($last_char == 's' || $last_last_char == 's' && $last_char == '!') { check_param($params, ++$j, 'UConverter*', $optional); } else { error("the '&' specifier cannot be applied to '{$last_char}'"); } } else { error("unknown char ('&') at column {$i}"); } break; case '+': case '*': if (version_compare(VERSION, '6', 'ge')) { if ($varargs) { error("A varargs specifier can only be used once. repeated char at column {$i}"); } else { check_param($params, ++$j, 'zval****', $optional); check_param($params, ++$j, 'int*', $optional); $varargs = true; } } else { error("unknown char ('{$char}') at column {$i}"); } break; default: if (isset($API_params[$char])) { foreach ($API_params[$char] as $exp) { check_param($params, ++$j, $exp, $optional); } } else { error("unknown char ('{$char}') at column {$i}"); } } $last_last_char = $last_char; $last_char = $char; } } } }
/** run tests on a function. the code is passed in $txt */ function check_function($name, $txt, $offset) { global $API_params; $regex = '/ (?: zend_parse_parameters(?:_throw)? \\s*\\([^,]+ | zend_parse_(?:parameters_ex|method_parameters) \\s*\\([^,]+,[^,]+ | zend_parse_method_parameters_ex \\s*\\([^,]+,[^,]+,[^,+] ) ,\\s*"([^"]*)"\\s* ,\\s*([^{;]*) /Sx'; if (preg_match_all($regex, $txt, $matches, PREG_SET_ORDER | PREG_OFFSET_CAPTURE)) { $GLOBALS['current_function'] = $name; foreach ($matches as $m) { $GLOBALS['error_few_vars_given'] = false; update_lineno($offset + $m[2][1]); $vars = get_vars(substr($txt, 0, $m[0][1])); // limit var search to current location $params = get_params($vars, $m[2][0]); $optional = $varargs = false; $last_char = ''; $j = -1; $spec = $m[1][0]; $len = strlen($spec); for ($i = 0; $i < $len; ++$i) { $char = $spec[$i]; switch ($char = $spec[$i]) { // separator for optional parameters case '|': if ($optional) { error("more than one optional separator at char #{$i}"); } else { $optional = true; if ($i == $len - 1) { error("unnecessary optional separator"); } } break; // separate_zval_if_not_ref // separate_zval_if_not_ref case '/': if (in_array($last_char, array('l', 'L', 'd', 'b'))) { error("the '/' specifier should not be applied to '{$last_char}'"); } break; // nullable arguments // nullable arguments case '!': if (in_array($last_char, array('l', 'L', 'd', 'b'))) { check_param($params, ++$j, 'zend_bool*', $optional); } break; // variadic arguments // variadic arguments case '+': case '*': if ($varargs) { error("A varargs specifier can only be used once. repeated char at column {$i}"); } else { check_param($params, ++$j, 'zval**', $optional); check_param($params, ++$j, 'int*', $optional); $varargs = true; } break; case 's': case 'p': check_param($params, ++$j, 'char**', $optional, $allow_uninit = true); check_param($params, ++$j, 'size_t*', $optional, $allow_uninit = true); if ($optional && !$params[$j - 1][2] && !$params[$j][2] && $params[$j - 1][0] !== '**dummy**' && $params[$j][0] !== '**dummy**') { error("one of optional vars {$params[$j - 1][0]} or {$params[$j][0]} must be initialized", 1); } break; case 'C': // C must always be initialized, independently of whether it's optional check_param($params, ++$j, 'zend_class_entry**', false); break; default: if (!isset($API_params[$char])) { error("unknown char ('{$char}') at column {$i}"); } // If an is_null flag is in use, only that flag is required to be // initialized $allow_uninit = $i + 1 < $len && $spec[$i + 1] === '!' && in_array($char, array('l', 'L', 'd', 'b')); foreach ($API_params[$char] as $exp) { check_param($params, ++$j, $exp, $optional, $allow_uninit); } } $last_char = $char; } } } }