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";
 
 ?>

Reply via email to