vrana Fri Jun 15 15:49:32 2007 UTC
Modified files: /phpdoc/scripts check-references.php Log: Local refs Optional single extension Unicode return types Constructor return types
http://cvs.php.net/viewvc.cgi/phpdoc/scripts/check-references.php?r1=1.22&r2=1.23&diff_format=u Index: phpdoc/scripts/check-references.php diff -u phpdoc/scripts/check-references.php:1.22 phpdoc/scripts/check-references.php:1.23 --- phpdoc/scripts/check-references.php:1.22 Fri Jun 15 11:45:17 2007 +++ phpdoc/scripts/check-references.php Fri Jun 15 15:49:31 2007 @@ -29,7 +29,7 @@ if (!isset($_SERVER["argv"][1]) || !is_dir($phpdoc_dir)) { echo "Purpose: Check parameters (types, optional, reference, count) and return types.\n"; - echo "Usage: check-references.php language\n"; + echo "Usage: check-references.php language [path_to_extension]\n"; echo "Notes:\n"; echo "- Compares documentation with PHP sources (Zend, extensions, PECL, SAPI).\n"; echo "- Functions not found in sources are checked as without references.\n"; @@ -37,51 +37,23 @@ exit(); } +$extension = $_SERVER["argv"][2]; + // various names for parameters passed by reference // array() means list of parameters, number is position from which all parameters are passed by reference $number_refs = array( + "first_arg_force_ref" => array(1), "second_arg_force_ref" => array(2), "second_args_force_ref" => array(2), - "second_argument_force_ref" => array(2), - "exif_thumbnail_force_ref" => array(2, 3, 4), - "third_and_rest_force_ref" => 3, "third_arg_force_ref" => array(3), - "third_args_force_ref" => array(3), - "third_argument_force_ref" => array(3), - "third_arg_force_by_ref_rest" => 3, - "second_arg_force_by_ref_rest" => 2, - "arg3to6of6_force_ref" => array(3, 4, 5, 6), - "second_thru_fourth_args_force_ref" => array(2, 3, 4), - "secondandthird_args_force_ref" => array(2, 3), - "first_arg_force_ref" => array(1), - "first_args_force_ref" => array(1), - "first_argument_force_ref" => array(1), - "firstandsecond_args_force_ref" => array(1, 2), - "arg2and3_force_ref" => array(2, 3), - "first_through_third_args_force_ref" => array(1, 2, 3), "fourth_arg_force_ref" => array(4), - "second_and_third_args_force_ref" => array(2, 3), - "second_fifth_and_sixth_args_force_ref" => array(2, 5, 6), - "first_and_second__args_force_ref" => array(1, 2), - "third_and_fourth_args_force_ref" => array(3, 4), - "sixth_arg_force_ref" => array(6), - "msg_receive_args_force_ref" => array(3, 5, 8), - "all_args_force_by_ref" => 1, + "fifth_arg_force_ref" => array(5), "all_args_by_ref" => 1, - "http_request_info_ref_3" => array(3), - "http_request_info_ref_4" => array(4), - "http_arg_pass_ref_3" => array(3), - "http_arg_pass_ref_4" => array(4), - "http_arg_pass_ref_5" => array(5), - "secondandthird_arg_force_ref" => array(2, 3), - "fifthandsixth_arg_force_ref" => array(5, 6), - "seventh_arg_force_ref" => array(7), - "eighth_arg_force_ref" => array(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", "ZVAL" => "mixed"); // FALSE and NULL omitted because they are used for errors +$retval_mapping = array("TRUE" => "bool", "BOOL" => "bool", "LONG" => "int", "DOUBLE" => "float", "STRING" => "string", "STRINGL" => "string", "TEXT" => "string", "TEXTL" => "string", "UNICODE" => "unicode", "UNICODEL" => "unicode", "ASCII_STRING" => "unicode", "ASCII_STRINGL" => "unicode", "ARRAY" => "array", "OBJECT" => "object", "RESOURCE" => "resource", "ZVAL" => "mixed"); // 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 @@ -190,7 +162,7 @@ $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_merge(array($zend_dir), glob("$phpsrc_dir/ext/*", GLOB_ONLYDIR), glob("$pecl_dir/*", GLOB_ONLYDIR), glob("$phpsrc_dir/sapi/*", GLOB_ONLYDIR)) as $dirname) { +foreach ((isset($extension) ? array($extension) : array_merge(array($zend_dir), glob("$phpsrc_dir/ext/*", GLOB_ONLYDIR), glob("$pecl_dir/*", GLOB_ONLYDIR), glob("$phpsrc_dir/sapi/*", GLOB_ONLYDIR))) as $dirname) { if (dirname($dirname) == $pecl_dir && !file_exists("$phpdoc_dir/reference/" . strtolower(basename($dirname)))) { continue; // skip undocumented PECL extensions } @@ -198,7 +170,8 @@ $aliases = array(); // php_function => sources_function $macros = array(); // MACRO => array(body, array(params)) $largedir = ($dirname == $zend_dir || $dirname == "$phpsrc_dir/ext/standard"); - foreach (array_merge((array) glob("$dirname/*.c*"), (array) glob("$dirname/*.h")) as $filename) { + $local_refs = array(); + foreach (array_merge((array) glob("$dirname/*.h"), (array) glob("$dirname/*.c*")) as $filename) { $file = file_get_contents($filename); // macros if (!$largedir) { @@ -209,6 +182,22 @@ } } + preg_match_all('~ZEND_BEGIN_ARG_INFO(?:_EX)?\\(([^,]*),\\s*([^,)]+)(.*?)ZEND_END_ARG_INFO~s', $file, $matches, PREG_SET_ORDER); + foreach ($matches as $val) { + $function_name = trim($val[1]); + $local_refs[$function_name] = array(); + preg_match_all('~ZEND_ARG(?:_PASS)?_INFO\\(\\s*([^,)]+)~', $val[3], $matches2); + $i = -1; + foreach ($matches2[1] as $i => $val2) { + if ($val2 && $val2 != "ZEND_SEND_BY_VAL") { + $local_refs[$function_name][] = $i+1; + } + } + if ($val[2] && $val2 != "ZEND_SEND_BY_VAL") { + $local_refs[$function_name] = ($local_refs[$function_name] ? min($local_refs[$function_name]) : $i+2); + } + } + if (substr($filename, -2) != ".h") { $files[$filename] = $file; @@ -226,14 +215,19 @@ } // references - preg_match_all("~^[ \t]*(?:ZEND|PHP)_FE\\((\\w+)\\s*,\\s*(\\w+)\\s*[,)]~m", $file, $matches, PREG_SET_ORDER); - preg_match_all("~^[ \t]*(?:ZEND|PHP)_FALIAS\\((\\w+)\\s*,[^,]+,\\s*(\\w+)\\s*[,)]~m", $file, $matches2, PREG_SET_ORDER); + preg_match_all("~^[ \t]*(?:ZEND|PHP)_FE\\((\\w+)\\s*,\\s*(\\w+)\\s*[,)]~m", $file, $matches, PREG_SET_ORDER | PREG_OFFSET_CAPTURE); + preg_match_all("~^[ \t]*(?:ZEND|PHP)_FALIAS\\((\\w+)\\s*,[^,]+,\\s*(\\w+)\\s*[,)]~m", $file, $matches2, PREG_SET_ORDER | PREG_OFFSET_CAPTURE); foreach (array_merge($matches, $matches2) as $val) { - if ($val[2] != "NULL") { - if (empty($number_refs[$val[2]])) { - echo "! $val[2] from $filename is not defined.\n"; + if ($val[2][0] != "NULL") { + $lineno = substr_count(substr($file, 0, $val[0][1]), "\n") + 1; + if (isset($local_refs[$val[2][0]])) { + $source_refs[strtolower($val[1][0])] = array($local_refs[$val[2][0]], $filename, $lineno); + } elseif (isset($number_refs[$val[2][0]])) { + $source_refs[strtolower($val[1][0])] = array($number_refs[$val[2][0]], $filename, $lineno); + } else { + $source_refs[strtolower($val[1][0])] = array(null, $filename, $lineno); + echo "! " . $val[2][0] . " from $filename is not defined.\n"; } - $source_refs[strtolower($val[1])] = $number_refs[$val[2]]; } } @@ -245,7 +239,9 @@ $lineno = substr_count(substr($file, 0, $val[3][1]), "\n") + 1; // return type - if (!in_array($function_name, $difficult_retvals)) { + if (preg_match('~(.+)::__construct$~', $function_name, $match)) { + $return_types[$function_name] = array($match[1], $filename, $lineno); + } elseif (!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); @@ -361,7 +357,8 @@ // compare with documentation $counts = array("refs" => 0, "types" => 0, "arg_counts" => 0, "return" => 0); -foreach (glob("$phpdoc_dir/reference/*/functions/*.xml") as $filename) { +$reference_path = "$phpdoc_dir/reference/" . (isset($extension) ? basename($extension) : "*"); +foreach (array_merge(glob("$reference_path/*/*.xml"), glob("$reference_path/*/*/*.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; $return_type = $matches[3]; @@ -371,16 +368,17 @@ // 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"; + $modifier = (preg_match('~::__construct$~', $function_name) ? "i" : ""); + if (!preg_match("~<type>(" . $return_types[$function_name][0] . ")</type>~$modifier", $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"; } } elseif (preg_match("~<type>(callback|$invalid_types)</type>~", $return_type)) { - echo "Wrong return type in $filename on line $lineno.\n"; + //~ echo "Wrong return type in $filename on line $lineno.\n"; } // references - $source_ref = (isset($source_refs[$function_name]) ? $source_refs[$function_name] : null); + $source_ref = (isset($source_refs[$function_name]) ? $source_refs[$function_name] : array(null)); preg_match_all('~<parameter( role="reference")?>~', $methodsynopsis, $matches); $byref = array(); foreach ($matches[1] as $key => $val) { @@ -389,9 +387,9 @@ } } if (!in_array($function_name, $wrong_refs) - && (is_int($source_ref) ? $byref[0] != $source_ref || count($byref) != count($matches[1]) - $source_ref + 1 : $byref != $source_ref) + && (is_int($source_ref[0]) ? $byref[0] != $source_ref[0] || count($byref) != count($matches[1]) - $source_ref[0] + 1 : $byref != $source_ref[0]) ) { - echo (isset($source_ref) ? "Parameter(s) " . (is_int($source_ref) ? "$source_ref and rest" : implode(", ", $source_ref)) : "Nothing") . " should be passed by reference in $filename on line $lineno.\n"; + echo (isset($source_ref[0]) ? "Parameter(s) " . (is_int($source_ref[0]) ? "$source_ref[0] and rest" : implode(", ", $source_ref[0])) : "Nothing") . " should be passed by reference in $filename on line $lineno" . (isset($source_ref[1]) ? "\n: source in $source_ref[1] on line $source_ref[2]" : "") . ".\n"; } if (isset($source_ref)) { $counts["refs"]++; @@ -448,14 +446,14 @@ $count = count($matches[3]); if (!$matches[3] || substr($matches[3][$count - 1], -3) != "...") { if ($count > $max_args) { - echo "Warning: Too much parameters in $function_name.\n"; + //~ echo "Warning: Too much parameters in $function_name.\n"; } elseif ($count < $max_args) { $disallowed += array_fill($count + 1, $max_args - $count, true); } } if ($source_arg_count[0] != $disallowed) { - echo "Wrong arguments count in $filename on line $lineno.\n"; - echo ": source in $source_arg_count[1] on line $source_arg_count[2].\n"; + //~ echo "Wrong arguments count in $filename on line $lineno.\n"; + //~ echo ": source in $source_arg_count[1] on line $source_arg_count[2].\n"; } } }