cvs: phpdoc /scripts check-references.php

From: Date: Thu, 12 Aug 2004 18:10:52 +0000
Subject: cvs: phpdoc /scripts check-references.php
Groups: php.doc 
Request: Send a blank email to phpdoc+get-969363080@lists.php.net to get a copy of this message
vrana Thu Aug 12 14:10:52 2004 EDT Modified files: /phpdoc/scripts check-references.php Log: New version checking parameters (types, optional) in functions using zend_parse_parameters() http://cvs.php.net/diff.php/phpdoc/scripts/check-references.php?r1=1.2&r2=1.3&ty=u Index: phpdoc/scripts/check-references.php diff -u phpdoc/scripts/check-references.php:1.2 phpdoc/scripts/check-references.php:1.3 --- phpdoc/scripts/check-references.php:1.2 Wed Aug 4 03:46:36 2004 +++ phpdoc/scripts/check-references.php Thu Aug 12 14:10:51 2004 @@ -28,9 +28,11 @@ } if (!isset($_SERVER["argv"][1]) || !is_dir($phpdoc_dir)) { - echo "Purpose: Check parameters passed by reference from PHP sources.\n"; + echo "Purpose: Check parameters (types, optional, reference) from PHP sources.\n"; echo "Usage: check-references.php language\n"; - echo "Note: Functions not found in sources are not checked.\n"; + echo "Notes:\n"; + echo "- Functions not found in sources are checked as without references.\n"; + echo "- Types and optional params are checked only in some functions.\n"; exit(); } @@ -52,6 +54,7 @@ "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), @@ -65,11 +68,62 @@ "all_args_force_by_ref" => 1, ); +// convert source formatting to document types, built from ZendAPI/zend.arguments.retrieval and howto/chapter-conventions +function params_source_to_doc($type_spec) +{ + static $zend_params = array( + "l" => "int", + "d" => "float", + "s" => "string", + "b" => "bool", + "r" => "resource", + "a" => "array", + "o" => "object", + "O" => "object", + "z" => "mixed", + "Z" => "mixed", + "|" => "optional" + ); + $return = array(); + for ($i=0; $i < strlen($type_spec); $i++) { + $ch = $type_spec{$i}; + if ($ch != "/" && $ch != "!") { + if (!isset($zend_params[$ch])) { + echo "! Unknown formatting specifier '$ch' in '$type_spec'.\n"; + $zend_params[$ch] = "unknown"; + } + $return[] = $zend_params[$ch]; + } + } + return $return; +} + // some parameters should be passed only by reference but they are not forced to -$wrong_source = array("dbplus_info", "dbplus_next", "php_check_syntax", "xdiff_string_merge3", "xdiff_string_patch"); +$wrong_source = array("dbplus_info", "dbplus_next", "xdiff_string_merge3", "xdiff_string_patch"); +$difficult_params = array( + "ibase_blob_import", + "imagefilter", + "mt_rand", "rand", + "mcrypt_get_block_size", "mcrypt_get_key_size", "mcrypt_get_cipher_name", // inverse order + "mysql_ping", + "pdf_get_parameter", + "tidy_getopt", // uses zend_parse_method_parameters + // better to fix in sources: + "imagepstext", + "ncurses_keyok", "ncurses_use_env", "ncurses_use_extended_names", + "openssl_x509_export_to_file", "openssl_x509_export", + "snmp_set_quick_print", + "tcpwrap_check", + "get_headers", + "wddx_packet_end", + "pdf_add_bookmark", "pdf_findfont", "pdf_get_value", "pdf_open_file", "pdf_open_image_file", "pdf_setcolor", "pdf_show_boxed", "pdf_stringwidth", + "apd_echo", + "fdf_set_on_import_javascript", +); // read referenced parameters from sources -$source_refs = array(); +$source_refs = array(); // array("function_name" => number_ref, ...) +$source_types = array(); // array("function_name" => array("type_spec", filename, lineno), ...) foreach (array_merge(glob("$zend_dir/*.c*"), glob("$phpsrc_dir/ext/*/*.c*"), glob("$pecl_dir/*/*.c*")) as $filename) { $file = file_get_contents($filename); preg_match_all("~^[ \t]*(?:ZEND|PHP)_FE\\((\\w+)\\s*,\\s*(\\w+)\\s*[,)]~mS", $file, $matches, PREG_SET_ORDER); @@ -82,17 +136,31 @@ $source_refs[strtolower($val[1])] = $number_refs[$val[2]]; } } + // read parameters + preg_match_all('~^(?:ZEND|PHP)(?:_NAMED)?_FUNCTION\\(([^)]+)\\)(.*)^\\}~msSU', $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($val[1][0])); + if (!in_array($function_name, $difficult_params) + && strpos($val[2][0], 'zend_parse_parameters_ex') === false // indicate difficulty + && preg_match('~.*zend_parse_parameters\\([^,]*,\\s*"([^"]*)"~sS', $val[2][0], $matches2, PREG_OFFSET_CAPTURE) // .* to catch last occurence + // zend_parse_method_parameters is not yet supported + ) { + $source_types[$function_name] = array($matches2[1][0], $filename, substr_count(substr($file, 0, $val[2][1] + $matches2[1][1]), "\n") + 1); + } + } } +echo "Sources were read.\n"; // compare with documentation foreach (glob("$phpdoc_dir/reference/*/functions/*.xml") as $filename) { if (preg_match('~^(.*<methodsynopsis>.*)<methodname>([^<]+)<(.*)</methodsynopsis>~sSU', file_get_contents($filename), $matches)) { - $lineno = substr_count($matches[1], "\n"); - $function_name = strtolower($matches[2]); + $lineno = substr_count($matches[1], "\n") + 1; + $function_name = strtolower(trim($matches[2])); if (strpos($function_name, '-') || strpos($function_name, ':')) { continue; // methods are not supported } $methodsynopsis = $matches[3]; + $source_ref =& $source_refs[$function_name]; preg_match_all('~<parameter>(&amp;)?~S', $methodsynopsis, $matches); $byref = array(); @@ -101,13 +169,45 @@ $byref[] = $key + 1; } } - if (is_int($source_ref) && $byref[0] == $source_ref && count($byref) == count($matches[1]) - $source_ref + 1) { - $byref = $source_ref; + if (!in_array($function_name, $wrong_source) + && (is_int($source_ref) ? $byref[0] != $source_ref || count($byref) != count($matches[1]) - $source_ref + 1 : $byref != $source_ref) + ) { + 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"; } - if ($byref != $source_ref && !in_array($function_name, $wrong_source)) { - 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"; + + $source_type =& $source_types[$function_name]; + if (isset($source_type)) { + preg_match_all('~<methodparam(\\s+choice=[\'"]opt[\'"])?>\\s*<type>([^<]+)</type>~i', $methodsynopsis, $matches); // (PREG_OFFSET_CAPTURE can be used to get precise line numbers) + $optional_source = false; + $optional_doc = false; + $i = 0; + $error = ""; + foreach (params_source_to_doc($source_type[0]) as $param) { + if ($param == "optional") { + $optional_source = true; + continue; + } elseif (isset($matches[2][$i])) { // sufficient number of parameters in the documentation + if ($matches[2][$i] != $param && $param != "mixed") { + $error .= "Parameter #" . ($i+1) . " should be of type $param (is " . $matches[2][$i] . ") in $filename on line " . ($lineno + $i + 1) . ".\n"; + } + if (!empty($matches[1][$i])) { + $optional_doc = true; // all rest is optional to allow e.g. exif_thumbnail(filename [, width, height [, imagetype]]) + } + if ($optional_doc != $optional_source) { + $error .= "Parameter #" . ($i+1) . " should" . ($optional_source ? "" : " not") . " be optional in $filename on line " . ($lineno + $i + 1) . ".\n"; + } + } + $i++; + } + if ($i != count($matches[2])) { + $error = "Wrong number of parameters (" . count($matches[2]) . " instead of $i) in $filename on line $lineno.\n"; // other errors ignored + } + if ($error) { + echo "$error: source in $source_type[1] on line $source_type[2].\n"; + } } } } +echo "Done.\n"; ?>

« previous php.doc (#969363080) next »