vrana Fri Jun 24 05:14:10 2005 EDT
Modified files: /phpdoc/scripts check-references.php Log: Check return types http://cvs.php.net/diff.php/phpdoc/scripts/check-references.php?r1=1.17&r2=1.18&ty=u Index: phpdoc/scripts/check-references.php diff -u phpdoc/scripts/check-references.php:1.17 phpdoc/scripts/check-references.php:1.18 --- phpdoc/scripts/check-references.php:1.17 Wed Jun 22 06:38:05 2005 +++ phpdoc/scripts/check-references.php Fri Jun 24 05:14:10 2005 @@ -76,6 +76,8 @@ $valid_types = "int|float|string|bool|resource|array|object|mixed|number"; $invalid_types = "integer|long|double|boolean|class"; // objects are written as appropriate class name so there is no complete list of valid types +$retval_mapping = array("TRUE" => "bool", "BOOL" => "bool", "LONG" => "int", "DOUBLE" => "float", "STRING" => "string", "STRINGL" => "string", "ARRAY" => "array", "OBJECT" => "object", "RESOURCE" => "resource"); // FALSE and NULL omitted because they are used for errors +$retval_types = implode('|', array_keys($retval_mapping)); $operators = "!=|<=?|>=?|=="; $max_args = 12; // maximum number of regular function arguments @@ -118,6 +120,17 @@ "soapclient::__soapcall", ); +$difficult_retvals = array( + "set_error_handler", "set_exception_handler", "highlight_file", "highlight_string", "pg_cancel_query", "pg_connection_busy", "mysqli_query", + // better to fix in sources: + "debug_print_backtrace", // array instead of void + "dbmopen", // int instead of resource + "pg_lo_open", // int instead of resource + "ircg_pconnect", // int instead of resource + "notes_search", // RETURN_LONG commented out + "exif_tagname", // RETURN_BOOL(FALSE) instead of RETURN_FALSE +); + $difficult_params = array( "ibase_blob_import", "ibase_execute", "imagefilter", @@ -132,8 +145,9 @@ "snmp_set_quick_print", "apd_echo", "easter_date", - "mysql_pconnect", + "tidy_repair_string", ); + $difficult_arg_count = array( "getdate", "min", "max", "implode", "strtok", "sybase_fetch_object", "cpdf_text", "pdf_get_parameter", "pg_fetch_assoc", "odbc_exec", "odbc_result_all", "yaz_wait", @@ -147,7 +161,9 @@ // read referenced parameters from sources $source_refs = array(); // array("function_name" => number_ref, ...) $source_types = array(); // array("function_name" => array("type_spec", filename, lineno), ...) +$return_types = array(); // array("function_name" => array("doc_type", filename, lineno), ...) $source_arg_counts = array(); // array("function_name" => array(disallowed_count => true, ...), ...) +//~ foreach (array("$phpsrc_dir/ext/dbx") as $dirname) { foreach (array_merge(array($zend_dir), glob("$phpsrc_dir/ext/*"), glob("$phpsrc_dir/sapi/*"), glob("$pecl_dir/*")) as $dirname) { $files = array(); $aliases = array(); // php_function => sources_function @@ -155,7 +171,7 @@ $files[$filename] = file_get_contents($filename); // named functions - preg_match_all('~PHP_NAMED_FE\\((\\w*)\\s*,\\s*(\\w*)~', $files[$filename], $matches, PREG_SET_ORDER); + preg_match_all('~(?:PHP|ZEND)_NAMED_FE\\((\\w*)\\s*,\\s*(\\w*)~', $files[$filename], $matches, PREG_SET_ORDER); foreach ($matches as $val) { $aliases[$val[2]] = $val[1]; } @@ -178,17 +194,45 @@ preg_match_all('~^(?:ZEND|PHP)(_NAMED)?_(?:FUNCTION|METHOD)\\(([^)]+)\\)(.*)^\\}~msU', $file, $matches, PREG_SET_ORDER | PREG_OFFSET_CAPTURE); // }}} is not in all sources so ^} is used instead foreach ($matches as $val) { $function_name = strtolower(trim(preg_replace('~\\s*,\\s*~', '::', ($val[1][0] ? $aliases[$val[2][0]] : $val[2][0])))); + $function_body = $val[3][0]; + $lineno = substr_count(substr($file, 0, $val[3][1]), "\n") + 1; + + // return type + if (!in_array($function_name, $difficult_retvals)) { + preg_match_all("~\\b(?:RETURN|RETVAL|(?:return_value->type|Z_TYPE_P\\(return_value\\))\\s*=\\s*IS)_($retval_types)|(?:ZVAL_|convert_to_)((?i)$retval_types)(?:_ex)?\\(return_value~", $function_body, $types, PREG_SET_ORDER); + if (preg_match_all('~()(array|object)(?:_and_properties)?_init\\(return_value~', $function_body, $matches, PREG_SET_ORDER)) { + $types = array_merge($types, $matches); + } + if (preg_match('~(?:ZEND_REGISTER_RESOURCE\\(|php_stream_to_zval.*)return_value~', $function_body)) { + $types[] = array("", "RESOURCE", ""); + } + if ($types) { + $type = $retval_mapping[$types[0][1] . strtoupper($types[0][2])]; + for ($i=1; $i < count($types); $i++) { + $type1 = $retval_mapping[$types[$i][1] . strtoupper($types[$i][2])]; + if ($type1 != $type) { + if (($type1 == "int" || $type1 == "float") && ($type == "int" || $type == "float" || $type == "number")) { + $type = "number"; + } else { + $type = "mixed"; + break; + } + } + } + $return_types[$function_name] = array($type, $filename, $lineno); + } + } + + // other function call if (preg_match('~(\\w+)\\(INTERNAL_FUNCTION_PARAM_PASSTHRU~', $val[3][0], $matches2) && !preg_match('~ZEND_NUM_ARGS\\(\\)~', $val[3][0]) && $matches2[1] != "php_exec_ex" && preg_match('~' . preg_quote($matches2[1], '~') . '\\(INTERNAL_FUNCTION_PARAMETERS(.*)^\\}~msU', $file, $matches2, PREG_OFFSET_CAPTURE) && !preg_match('~^.*\\b(?:expected_args|behavior|st)\\b~', $matches2[1][0]) ) { - $val[3] = $matches2[1]; + $function_body = $matches2[1][0]; + $lineno = substr_count(substr($file, 0, $matches2[1][1]), "\n") + 1; } - $lineno = substr_count(substr($file, 0, $val[3][1]), "\n") + 1; - $function_body = $val[3][0]; - // types and optional if (!in_array($function_name, $difficult_params) && strpos($function_body, 'zend_parse_parameters_ex') === false // indicate difficulty @@ -254,11 +298,10 @@ foreach ($matches2 as $val2) { $function_name = strtolower($val2[2]); $method_name = strtolower("$val[1]::$val2[1]"); - if (isset($source_types[$function_name])) { - $source_types[$method_name] = $source_types[$function_name]; - } - if ($source_arg_counts[$function_name]) { - $source_arg_counts[$method_name] = $source_arg_counts[$function_name]; + foreach (array("source_types", "source_arg_counts", "return_types") as $var) { + if (isset($GLOBALS[$var][$function_name])) { + $GLOBALS[$var][$method_name] = $GLOBALS[$var][$function_name]; + } } } } @@ -268,7 +311,7 @@ echo "Sources were read.\n"; // compare with documentation -$counts = array("refs" => 0, "types" => 0, "arg_counts" => 0); +$counts = array("refs" => 0, "types" => 0, "arg_counts" => 0, "return" => 0); foreach (glob("$phpdoc_dir/reference/*/functions/*.xml") as $filename) { if (preg_match('~^(.*(?:(\\w+)</classname></ooclass>\\s*)?<methodsynopsis>(.*))<methodname>([^<]+)<(.*)</methodsynopsis>~sU', file_get_contents($filename), $matches)) { $lineno = substr_count($matches[1], "\n") + 1; @@ -277,7 +320,14 @@ $methodsynopsis = $matches[5]; // return type - if (preg_match("~<type>(callback|$invalid_types)</type>~", $return_type)) { + if (isset($return_types[$function_name])) { + $counts["return"]++; + if (!preg_match("~<type>(" . $return_types[$function_name][0] . ")</type>~", $return_type) && ($return_types[$function_name][0] != "object" || preg_match("~<type>($valid_types|$invalid_types)</type>~", $return_type))) { + echo "Wrong return type in $filename on line $lineno.\n"; + echo ": (" . $return_types[$function_name][0] . ") in " . $return_types[$function_name][1] . " on line " . $return_types[$function_name][2] . ".\n"; + } + unset($return_types[$function_name]); + } elseif (preg_match("~<type>(callback|$invalid_types)</type>~", $return_type)) { echo "Wrong return type in $filename on line $lineno.\n"; } @@ -366,4 +416,4 @@ echo "$counts[refs]/". count($source_refs) ." references checked.\n"; echo "$counts[types]/". count($source_types) ." types checked.\n"; echo "$counts[arg_counts]/". count($source_arg_counts) ." arg counts checked.\n"; -?> +echo "$counts[return]/". count($return_types) ." return types checked.\n";