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

Reply via email to