maxim Tue Feb 4 15:49:23 2003 EDT Added files: /phpdoc/scripts bughunter.php Log: Initial checkin... Bug Hunter script is an attempt to "mechanically" scan and compare the documentation and C sources in order to hunt down the documentation imperfections.
Index: phpdoc/scripts/bughunter.php +++ phpdoc/scripts/bughunter.php <?php /* +----------------------------------------------------------------------+ | PHP Version 4 | +----------------------------------------------------------------------+ | Copyright (c) 1997-2003 The PHP Group | +----------------------------------------------------------------------+ | This source file is subject to version 2.02 of the PHP licience, | | that is bundled with this package in the file LICENCE and is | | avalible through the world wide web at | | http://www.php.net/license/2_02.txt. | | If uou did not receive a copy of the PHP license and are unable to | | obtain it through the world wide web, please send a note to | | [EMAIL PROTECTED] so we can mail you a copy immediately | +----------------------------------------------------------------------+ | Authors: Maxim Maletsky <[EMAIL PROTECTED]> | +----------------------------------------------------------------------+ $Id: bughunter.php,v 1.1 2003/02/04 20:49:23 maxim Exp $ */
/* REQUIRES: PHP 4.3.1-dev or higher Bug Hunter script is an attempt to "mechanically" scan and compare the documentation and C sources in order to hunt down the documentation imperfections. This program parses the XML documentation and compares it to the C source code. Its goal is to hunt down inconsistencies between the current documentation and the actual source code in the CVS. BUGHUNTER v1.0 : Hunts down the inconsistencies between the docs and the source code (req PHP 4.3.1-dev+) Usage: php bughunter.php [opts] <extension name> <files to parse> Ex: php bughunter.php mcve /php4/ext/mcve/*.c If running from browser, create an intermediate page with this: $dir = 'E:/CVS/PHP'; // Top level directory of your PHP repository echo "<pre>"; chdir("$dir/phpdoc/scripts/"); include('bughunter.php'); Access it with ?a=* to get the full set of data Array Structure: extension >>> function >>> source >> proto ) NOTE: display_disabled_function which is located in Zend/Zend_API.c is ignored from this test due its illogical location */ /* CHANGELOG: 02/04/03 v1.0 - Original Release */ /* TODO: * Optimize error tracking for scanning the code * Parse the docs structure * Parse the C code to get the return types where possible * Make a better dysplay. (Should it write its output to a file?) * Better document above */ error_reporting(E_ALL); set_time_limit(180); Class BugHunter { var $argc = Array(); var $argv = Array(); var $script_version = '1.0'; var $php_min_version = '4.3.1-dev'; var $parse_all = ''; var $parse_ext = ''; var $module = 'php5'; var $root = '../../'; var $php_types = Array( 'int' ,'string' ,'array' ,'object' ,'mixed' ); var $rex_php_types = ''; var $index = Array(); function BugHunter() { // Check for PHP compatibility if (!version_compare($this->php_min_version, PHP_VERSION, '<=')) { echo "You need {$this->php_min_version} or higher!\n" . "YOU HAVE: " . PHP_VERSION . "\n"; Return Exit; } // Prepare the params $this->argc = $_SERVER['argc']; $this->argv = $_SERVER['argv']; $this->rex_php_types = implode('|', $this->php_types); if ($this->argc < 2 or !$this->parse_argv()) { $this->usage(); Return Exit; } $skip = False; foreach(explode("\r\n", $this->read_file("{$this->root}phpdoc/funclist.txt")) as $line) { flush(); if(preg_match_all("/^# ([[:alnum:]_\/]+\/([[:alnum:]_]+)\/[[:alnum:]_]+\.c)$/", $line, $file, PREG_SET_ORDER)) { $ext = strtolower($file[0][2]); if(!file_exists($this->root . $file[0][1])) { $skip = True; $cache = $file; echo "\nExtension <b>{$file[0][2]}</b> does not exist in <b>{$file[0][1]}</b>!"; echo " Ommitting ...\n"; Continue; } $skip = False; $this->{$skip? 'skipped' : 'index'}[$ext]['location'] = $file[0][1]; } else { if($skip) echo "\t$line()\n"; $this->{$skip? 'skipped' : 'index'}[$ext]['function'][] = strtolower($line); } } if($this->parse_all) { flush(); foreach($this->index as $ext => $data) { $this->cur_ext = $ext; $this->result[$ext] = $this->parse_source($this->read_file($this->root . $data['location'])); } } else if($this->parse_ext) { if(!isset($this->index[$this->parse_ext])) { echo "\n\n"; echo isset($this->skipped[$this->parse_ext])? "Nothing to do for <b>{$this->parse_ext}</b>" : "Unknown Extension <b>{$this->parse_ext}</b>"; echo "\n"; Return Exit; } $this->result[$this->parse_ext] = $this->parse_source($this->read_file("{$this->root}{$this->index[$this->parse_ext]['location']}")); } Return True; } function parse_argv() { if($this->argv[1] == '*') $this->parse_all = True; else $this->parse_ext = $this->argv[1]; Return True; } function usage() { echo "\n BUGHUNTER v{$this->script_version}" . "\n Hunt down the inconsistencies between the docs and the source code (req PHP 4.3.0-dev+)" . "\n" . "\n Usage: {$this->argv[0]} [opts] <extension name>" . "\n Ex: {$this->argv[0]} oci8" . "\n" . "\n Authors: Maxim Maletsky <[EMAIL PROTECTED]>" . "\n" ; Return Exit; } function read_file($filename) { $fp = fopen($filename, 'rb'); if (!$fp) Return ''; $buffer = fread($fp, filesize($filename)); fclose($fp); Return $buffer; } function parse_source($buffer) { $proto = $function = $synopsis = $result = array(); $rex_proto = "/[[:space:]]*\/\*[[:space:]]*\{\{\{[[:space:]]*proto[[:space:]]*(.+)[[:space:]]*\*\//msU"; $rex_proto_synopsis = "/^[[:space:]]*(.+)[[:space:]]+([[:alnum:]_]+)[[:space:]]*\((.*)\)[[:space:]]*((.*)[[:space:]]*)*$/"; // Break source code into functions preg_match_all($rex_proto, $buffer, $proto); // for each proto for($i=0; $i<sizeof($proto[1]); $i++) { #echo "<i>" . $proto[1][$i] . "</i> : "; // Break protos into tiny pieces preg_match_all($rex_proto_synopsis, $proto[1][$i], $detail, PREG_SET_ORDER); #echo "<b>" . $detail[0][3] . "</b> : "; #echo "<b><i>" . $detail[0][4] . "</i></b>\n\n"; if(!isset($detail[0][3])) { echo "\n<b>Failed parsing proto:</b> <i>{$proto[1][$i]}</i> in <b>{$this->index[$this->cur_ext]['location']}</b>\n"; Return Array(); } // Retrieve the original string (might not be needed) list($proto_str, ) = explode("\n", $proto[1][$i]); // Parse parameters into a structured array and debug the errors. list($params, $errors) = $this->parse_params(strtolower(trim($detail[0][3]))); // Compose the resulting array $result[strtolower($detail[0][2])]['source']['proto'] = Array( 'original' => trim($proto_str) // Full function proto ,'desc' => trim($detail[0][4]) // Function description ,'return' => strtolower(trim($detail[0][1])) // Return Type ,'params' => $params // Parameters ,'errors' => $errors // Parameter Errors ); } // As in every good C function, lets return something Return $result; } function parse_params($param_str='') { $p = $params = $error = $struct = Array(); // if no proto then there's nothing for us to do if(!strlen($param_str)) Return Array($params, $error); $p = explode(',', $param_str); $optional = False; for($i=0; $i<sizeof($p); $i++) { // required or optional? if($optional) $params[$i+1]['optional'] = $optional; else { if(substr($p[$i], 0, 1) == '[') $params[$i+1]['optional'] = $optional = True; else if(substr($p[$i], -1) == '[') $optional = True; } $p[$i] = trim($p[$i], '[] '); // if plain text mandatory parameter if(preg_match_all("/^({$this->rex_php_types}) +(\&?)([a-z]+[[:alnum:]_\|]*)$/", $p[$i], $matches, PREG_SET_ORDER)) { $params[$i+1]['type'] = $matches[0][1]; $params[$i+1]['name'] = $matches[0][3]; if(strlen($matches[0][2])) $params[$i+1]['referenced'] = True; } // Debug, calculate error else { // If the type passed but there is no variable name if(preg_match("/^({$this->rex_php_types})$/", $p[$i])) $error[$i+1]['no_var'] = True; // If the variable has no type specified else if(preg_match("/^(\&?)([a-z]+[[:alnum:]_\|]*)$/", $p[$i])) $error[$i+1]['no_type'] = True; // If the variable has an unknown type associated else if(preg_match_all("/^(.+) +(\&?)([a-z]+[[:alnum:]_\|]*)$/", $p[$i], $matches, PREG_SET_ORDER)) $error[$i+1]['wrong_type'] = $matches[0][1]; // If there is no variable and no type whatsoever else if(!strlen($p[$i])) $error[$i+1]['no_data'] = True; } } Return Array($params, $error); } } // Debugging: when runs as module // Just add assign any key to the valuein the order if($_SERVER['REQUEST_METHOD'] == 'GET') { $_SERVER['argv'][0] = basename(__FILE__); foreach($_GET as $v) $_SERVER['argv'][] = $v; $_SERVER['argc'] = sizeof($_SERVER['argv']); } // debugging function getmicrotime(){ list($usec, $sec) = explode(" ",microtime()); return ((float)$usec + (float)$sec); } $t_start = getmicrotime(); // and Voila! $hunter = new BugHunter(); echo "\n<hr>Process took : " . (round(getmicrotime() - $t_start, 3)*1000) . " ms\n<hr>\n"; #print_r((array) $hunter); #phpinfo(); $tot = 0; foreach($hunter->result as $ext => $function) { flush(); echo "\n\n<b>$ext</b>\n<hr>\n"; $err = 0; foreach($function as $name=>$data) { if(!empty($data['source']['proto']['errors'])) { echo "\n\t" . str_pad($name, 50) . " : "; foreach($data['source']['proto']['errors'] as $param=>$error) { echo "\t\tp: $param : "; foreach($error as $err_type=>$err_val) { echo "<b>" . str_pad($err_type, 10) . "</b>" . " - " .str_pad($err_val , 10) ; $err++; } } } } echo "\n\nTotal bugs: $err"; $tot += $err; } echo "\n\nTotal proto inconsistencies: $tot"; ?>
-- PHP Documentation Mailing List (http://www.php.net/) To unsubscribe, visit: http://www.php.net/unsub.php