Ok, been working hard on this new version, but I think
everyone is going to like what's been done.

It can now scan  php4/ext/standard/*.c with only a couple
of small errors output (because of improper protos actually)

Though one drawback to having wildcard expansion is that
it now requires PHP-4.3.0-pre1 or higher for use of the
glob() function.


CHANGELOG:
10/26/02 v2.0 - Additional scanning for constants, and generation of
                constants.xml
              - Generation of references.xml template
              - functions with optional arguments would not parse
                properly, now it does
              - Wildcard scanning is now allowed
              - Requires PHP 4.3.0-pre1 or higher now
              - Usage is totally different

-Brad
<?php
/*
# +----------------------------------------------------------------------+
# | PHP Version 4                                                        |
# +----------------------------------------------------------------------+
# | Copyright (c) 1997-2002 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:   Brad House <[EMAIL PROTECTED]>                             |
# +----------------------------------------------------------------------+
#
# $Id: xml_proto.php,v 1.3 2002/10/24 21:48:43 philip Exp $
*/

/*
  REQUIRES: PHP 4.3.0-pre1 or higher

  This program generates XML documentation from the C source code
  of the extension.  For functions, the code _must_ have protos,
  for constants, the raw source code will be read and interpretted
  to generate the constants.xml file

  XML_PROTO v2.0 : Generate PHP documentation from proto defs (req PHP 4.3.0-pre1+)
  Usage: php xml_proto.php [opts] <extension name> <files to parse>
     Ex: php xml_proto.php mcve /php4/ext/mcve/*.c

  Options:
    -C : generate only constants.xml file (in current directory)
    -F : generate only fuction reference files (all will be placed in current 
directory)
    -A : (default) generate full documentation template
         This will generate <extension name>/ directory which reference.xml and 
contents.xml
         will be created in, and a subdirectory  <extension name>/functions/ which all
         function xml reference files will be generated in
*/

/* CHANGELOG:
   10/??/02 v1.0 - Original Release
   10/??/02 v1.1 - replace & with &amp;
   10/??/02 v1.2 - the Revision would be changed in static text meant for
                   insert into generated XML files (CVS did it)
   10/26/02 v2.0 - Additional scanning for constants, and generation of constants.xml
                 - Generation of references.xml template
                 - functions with optional arguments would not parse properly, now it 
does
                 - Wildcard scanning is now allowed
                 - Requires PHP 4.3.0-pre1 or higher now
                 - Usage is totally different
*/

$version="2.0";

$funclist=array();
$num_funcs=0;

$constlist=array();
$num_const=0;

$generate_constants=1;
$generate_functions=1;
$source_files=array();
$extension_name="";
$constant_dir="";
$function_dir="";

function new_function()
{
  global $funclist, $num_funcs;
  $funclist[$num_funcs]["args"]=array();
  $funclist[$num_funcs]["num_args"]=0;
  $num_funcs++;
  return($num_funcs-1);
}

function fix_name($name)
{
  $ret="";
  $len=strlen($name);

  for ($i=0; $i<$len; $i++) {
    $c=substr($name, $i, 1);
    if ($c == '_') {
      $ret .= '-';
    } else {
      $ret .= $c;
    }
  }
  return($ret);
}

function function_add_name($num, $name)
{
  global $funclist, $num_funcs;

  $funclist[$num]["function_name"]=$name;
  $funclist[$num]["function_name_fix"]=fix_name($name);
  return(1);
}

function function_add_type($num, $type)
{
  global $funclist, $num_funcs;
  $funclist[$num]["function_type"]=$type;
  return(1);
}

function function_add_purpose($num, $purpose)
{
  global $funclist, $num_funcs;
  $funclist[$num]["purpose"]=$purpose;
  return(1);
}

function function_add_arg($num, $type, $argname, $isopt)
{
  global $funclist, $num_funcs;

  $num_args=$funclist[$num]["num_args"];
  $funclist[$num]["args"][$num_args]["type"]=$type;
  $funclist[$num]["args"][$num_args]["variable"]=$argname;
  $funclist[$num]["args"][$num_args]["isopt"]=$isopt;
  $funclist[$num]["num_args"]++;

  return(1);
}

function write_reference_xml()
{
  global $extension_name, $constant_dir, $version;

  $filename= $constant_dir. "reference.xml";
  $fp=fopen($filename, "wb");
  if (!$fp) {
    echo "Failed writing: $filename\n";
    return(0);
  }

  fwrite($fp, "<?xml version='1.0' encoding='iso-8859-1'?>\n" .
       "<!-- $" . "Revision: 1.1 " . "$ -->\n" .
       "<!-- Generated by xml_proto.php v" . $version . ". Found in /scripts directory 
of phpdoc. -->\n" .
       " <reference id=\"ref." . $extension_name . "\">\n" .
       "  <title>$extension_name Functions</title>\n" .
       "  <titleabbrev>$extension_name</titleabbrev>\n" .
       "\n" .
       "  <partintro>\n" .
       "   <section id=\"" . $extension_name . ".intro\">\n" .
       "   &reftitle.intro;\n" .
       "    <para>\n" .
       "     This is the " . $extension_name . " extension.  It\n" .
       "     currently only lists the proto definitions.\n" .
       "    </para>\n" .
       "   </section>\n" .
       "   <section id=\"" . $extension_name . ".installation\">\n" .
       "   &reftitle.install;\n" .
       "    <para>\n" .
       "     To be written. Probably running configure with the\n" .
       "     <option role=\"configure\">--with-" . $extension_name . "</option>\n" .
       "     will properly enable this module.\n" .
       "    </para>\n" .
       "   </section>\n" .
       "  </partintro>\n" .
       " &reference.mcve.functions;\n" .
       " </reference>\n" .
       "<!-- Keep this comment at the end of the file\n" .
       "Local variables:\n" .
       "mode: sgml\n" .
       "sgml-omittag:t\n" .
       "sgml-shorttag:t\n" .
       "sgml-minimize-attributes:nil\n" .
       "sgml-always-quote-attributes:t\n" .
       "sgml-indent-step:1\n" .
       "sgml-indent-data:t\n" .
       "indent-tabs-mode:nil\n" .
       "sgml-parent-document:nil\n" .
       "sgml-default-dtd-file:\"../../../manual.ced\"\n" .
       "sgml-exposed-tags:nil\n" .
       "sgml-local-catalogs:nil\n" .
       "sgml-local-ecat-files:nil\n" .
       "End:\n" .
       "vim600: syn=xml fen fdm=syntax fdl=2 si\n" .
       "vim: et tw=78 syn=sgml\n" .
       "vi: ts=1 sw=1\n" .
       "-->\n");
  fclose($fp);
  
  echo "Wrote: $filename\n";
  return(1);
}

function write_functions_xml()
{
  global $funclist, $num_funcs;
  global $function_dir, $version;

  $filename="";
  $fp=0;

  for ($i=0; $i<$num_funcs; $i++) {
    $filename= $function_dir . $funclist[$i]["function_name_fix"] . ".xml";
    $fp=fopen($filename, "wb");
    if (!$fp) {
      echo "Failed writing: $filename\n";
      continue;
    }
    $fixname=$funclist[$i]["function_name_fix"];
    $funcname=$funclist[$i]["function_name"];
    $purpose=$funclist[$i]["purpose"];
    $functype=$funclist[$i]["function_type"];

    fwrite($fp, "<?xml version='1.0' encoding='iso-8859-1'?>\n" .
               "<!-- $" . "Revision: 1.1 $ -->\n" .
               "<!-- Generated by xml_proto.php v" . $version . ". Found in /scripts 
directory of phpdoc. -->\n" .
               "  <refentry id=\"function." . $fixname . "\">\n" .
               "   <refnamediv>\n" .
               "    <refname>$funcname</refname>\n" .
               "    <refpurpose>$purpose</refpurpose>\n" .
               "   </refnamediv>\n" .
               "   <refsect1>\n" .
               "    <title>Description</title>\n" .
               "    <methodsynopsis>\n" .
               "     <type>$functype</type><methodname>$funcname</methodname>\n");

    for ($j=0; $j<$funclist[$i]["num_args"]; $j++) {
      $argtype = $funclist[$i]["args"][$j]["type"];
      $argname = str_replace('&', '&amp;', $funclist[$i]["args"][$j]["variable"]);
      $isopt=$funclist[$i]["args"][$j]["isopt"];
      if (!$isopt) {
        fwrite($fp, "     
<methodparam><type>$argtype</type><parameter>$argname</parameter></methodparam>\n");
      } else {
        fwrite($fp, "     <methodparam 
choice=\"opt\"><type>$argtype</type><parameter>$argname</parameter></methodparam>\n");

      }
    }
    if ($funclist[$i]["num_args"] == 0){
      fwrite($fp, "     <void/>\n");
    }

    fwrite($fp, "    </methodsynopsis>\n" .
               "    <para>\n" .
               "     &warn.undocumented.func;\n" .
               "    </para>\n" .
               "   </refsect1>\n" .
               "  </refentry>\n" .
               "\n" .
               "<!-- Keep this comment at the end of the file\n" .
               "Local variables:\n" .
               "mode: sgml\n" .
               "sgml-omittag:t\n" .
               "sgml-shorttag:t\n" .
               "sgml-minimize-attributes:nil\n" .
               "sgml-always-quote-attributes:t\n" .
               "sgml-indent-step:1\n" .
               "sgml-indent-data:t\n" .
               "indent-tabs-mode:nil\n" .
               "sgml-parent-document:nil\n" .
               "sgml-default-dtd-file:\"../../../../manual.ced\"\n" .
               "sgml-exposed-tags:nil\n" .
               "sgml-local-catalogs:nil\n" .
               "sgml-local-ecat-files:nil\n" .
               "End:\n" .
               "vim600: syn=xml fen fdm=syntax fdl=2 si\n" .
               "vim: et tw=78 syn=sgml\n" .
               "vi: ts=1 sw=1\n" .
               "-->\n");
    fclose($fp);
    echo "Wrote: $filename\n";
  }
  return(1);
}

function read_file($filename)
{
  $fp = fopen($filename, "rb");
  if ($fp == 0) return("");
  $buffer=fread($fp, filesize($filename));
  fclose($fp);
  return($buffer);
}

function parse_desc($func_num, $data)
{
  $len=0;
  $i=0;
  $c=0;
  $temp="";
  $temp_len=0;
  $spaces=0;

  $len=strlen($data);
  for ($i=0; $i<$len; $i++) {
    $c=substr($data, $i, 1);
    switch ($c) {
      case '\r':
      case '\n':
      case ' ':
        if (!$spaces) {
          $spaces=1;
          $temp .= ' ';
          $temp_len++;
        }
      break;

      default:
        if ($c != '\r' && $c != '\n') {
          $spaces=0;
          $temp .= $c;
          $temp_len++;
        }
      break;
    }
  }
  function_add_purpose($func_num, $temp);
  return(1);
}

function parse_proto($proto)
{
  $len=0;
  $i=0;
  $c=0;
  $done=0;
  $start=0;
  $func_number=-1;
  $got_proto_def=0;
  $got_proto_type=0;
  $got_proto_name=0;
  $got_arg_type=0;
  $start_args=0;
  $temp="";
  $temp2="";
  $temp_len=0;
  $isopt=0;

  $len=strlen($proto);

  for ($i=0; $i<$len; $i++) {
    $c=substr($proto, $i, 1);
    switch ($c) {
      case '\r':
      case '\n':
      case ' ':
        if ($temp_len) {
          if (!$got_proto_def) {
            if (strcasecmp($temp, "proto") != 0) {
              // Possibly just a comment, don't output error info
              // echo "Not a proper proto definition: $proto\n";
              return(0);
            } else {
              $got_proto_def=1;
            }
          } else if (!$got_proto_type) {
            $func_number=new_function();
            function_add_type($func_number, $temp);
            $got_proto_type=1;
          } else if (!$got_proto_name) {
            function_add_name($func_number, $temp);
            $got_proto_name=1;
          } else if ($start_args && !$got_arg_type) {
            $got_arg_type=1;
            $temp2=$temp;
          } else if ($start_args && $got_arg_type) {
            $got_arg_type=0;
            function_add_arg($func_number, $temp2, $temp, $isopt);
            $temp2="";
          }
          $temp_len=0;
          $temp="";
        }
      break;
      
      case '[':
        if ($got_proto_name) {
          $isopt=1;
        } else {
          echo "Not a proper proto definition -5: $proto\n";
        }
      break;
      
      case ']':
        if ($got_proto_name && $isopt) {
        } else {
          echo "Not a proper proto definition -6: $proto\n";
        }
      break;

      case '(':
        if ($got_proto_type && $got_proto_def &&!$got_proto_name) {
          function_add_name($func_number, $temp);
          $temp="";
          $temp_len=0;
          $start_args=1;
          $got_proto_name=1;
        } else {
          echo "Not a proper proto definition -2: $proto\n";
          return(0);
        }

      break;

      case ')':
        if ($start_args) {
          if ($got_arg_type && $temp_len) {
            function_add_arg($func_number, $temp2, $temp, $isopt);
            $temp="";
            $temp_len=0;
          }
          $done=1;
        } else {
          echo "Not a proper proto definition -4: $proto\n";
          return(0);
        }
      break;

      case ',':
        if ($start_args && $got_arg_type) {
          $got_arg_type=0;
          function_add_arg($func_number, $temp2, $temp, $isopt);
          $temp2="";
          $temp="";
          $temp_len=0;
        } else if ($temp && !$temp2) {
          echo "Not a proper proto definition -3: $temp2 : $temp : $proto\n";
          return(0);
        }
      break;

      default:
        if ($c != '\r' && $c != '\n') {
          $temp .= $c;
          $temp_len++;
        }
      break;
    }
    if ($done) {
      $start=$i+1;
      break;
    }
  }
  parse_desc($func_number, substr($proto, $start));
  return(1);
}

function parse_file($buffer)
{
  global $funclist, $num_funcs;

  $temp1="";
  $temp2="";
  $ptr="";
  $args="";

  $ptr=$buffer;
  while (1) {
    $temp1=strstr($ptr, "{{{");
    if ($temp1 == false) break;
    $temp2=strstr($temp1, "*/");
    if ($temp2 == false) break;
    $args=substr($temp1, 3, strlen($temp1)-strlen($temp2)-3);
    parse_proto($args);
    $ptr=$temp2;
  }
  return(1);
}

function add_constant_to_list($name, $type)
{
  global $constlist;
  global $num_const;
  
  $constlist[$num_const]["name"]=$name;
  $constlist[$num_const]["type"]=$type;
  $num_const++;
  return(1);
}

function add_constant($varlist, $type)
{
  $on_name=0;
  $len=strlen($varlist);
  for ($i=0; $i<$len; $i++) {
    $c=substr($varlist, $i, 1);
    switch($c) {
      case '"';
        if (!$on_name) {
          $on_name=1;
          $name="";
        } else {
          $on_name=0;
          add_constant_to_list($name, $type);
          return(1);
        }
      break;

      case ',':
        return(0);
      break;

      default:
        if ($on_name) {
          $name .= $c;
        }
      break;

    }
  }
  return(0);
}

function scan_for_constants_byref($buffer, $string, $type)
{
  $ptr=$buffer;

  while (1) {
    $temp=stristr($ptr, $string);
    if (!$temp) { return(1); }
    $temp2=substr($temp, strlen($string), strlen($temp)-strlen($string));
    $temp3=stristr($temp2, "(");
    if (!$temp3) { return(1); }
    $temp4=substr($temp3, 1, strlen($temp3)-1);
    $temp5=stristr($temp4, ")");
    if (!$temp5) { return(1); }
    $varlist=substr($temp4, 0, strlen($temp4)-strlen($temp5));
    if (!add_constant($varlist, $type)) {
      echo "Invalid constant definition: ";
      $str=substr($temp, 0, strlen($temp)-strlen($temp5)+1);
      echo $str;
      echo "\n";
    }
    $ptr=$temp5;
  }
  return(0);
}

function scan_for_constants($buffer)
{
  scan_for_constants_byref($buffer, "REGISTER_LONG_CONSTANT", "long");
  scan_for_constants_byref($buffer, "REGISTER_DOUBLE_CONSTANT", "double");
  scan_for_constants_byref($buffer, "REGISTER_STRING_CONSTANT", "string");
}

function write_constants_xml()
{
  global $constant_dir;
  global $constlist;
  global $num_const;
  global $extension_name;
  global $version;

  if ($num_const == 0) {
    echo "No constants found, aborting write of constants.xml\n";
    return(1);
  }

  $filename = $constant_dir . "constants.xml";
  $fp=fopen($filename, "wb");
  if (!$fp) {
    echo "Failed writing: $filename\n";
    return(0);
  }

  fwrite($fp, "<?xml version='1.0' encoding='iso-8859-1'?>\n" .
              "<!-- $" . "Revision: 1.1 $ -->\n" .
               "<!-- Generated by xml_proto.php v" . $version . ". Found in /scripts 
directory of phpdoc. -->\n" .
               "<section id=\"" . $extension_name . ".constants\">\n" .
               " &reftitle.constants;\n" .
               " &extension.constants;\n" .
               " <variablelist>\n");
  for ($i=0; $i<$num_const; $i++) {
    $type=$constlist[$i]["type"];
    if (strcasecmp($type, "long") == 0) {
      $linkend="language.types.integer";
    } else if (strcasecmp($type, "double") == 0) {
      $linkend="language.types.double";
    } else if (strcasecmp($type, "string") == 0) {
      $linkend="language.types.string";
    }
    fwrite($fp, "  <varlistentry>\n" .
                 "   <term>\n" .
                 "    <constant>" . $constlist[$i]["name"] . "</constant>\n" .
                 "    (<link linkend=\"" . $linkend . "\">" . $type . "</link>)\n" .
                 "   </term>\n" .
                 "   <listitem>\n" .
                 "    <simpara>\n" .
                 "\n" .
                 "    </simpara>\n" .
                 "   </listitem>\n" .
                 "  </varlistentry>\n");
  }

  fwrite($fp, "\n" .
               "<!-- Keep this comment at the end of the file\n" .
               "Local variables:\n" .
               "mode: sgml\n" .
               "sgml-omittag:t\n" .
               "sgml-shorttag:t\n" .
               "sgml-minimize-attributes:nil\n" .
               "sgml-always-quote-attributes:t\n" .
               "sgml-indent-step:1\n" .
               "sgml-indent-data:t\n" .
               "indent-tabs-mode:nil\n" .
               "sgml-parent-document:nil\n" .
               "sgml-default-dtd-file:\"../../../../manual.ced\"\n" .
               "sgml-exposed-tags:nil\n" .
               "sgml-local-catalogs:nil\n" .
               "sgml-local-ecat-files:nil\n" .
               "End:\n" .
               "vim600: syn=xml fen fdm=syntax fdl=2 si\n" .
               "vim: et tw=78 syn=sgml\n" .
               "vi: ts=1 sw=1\n" .
               "-->\n");
  fclose($fp);

  echo "Wrote: $filename\n";
  return(1);
}

function create_xml_docs()
{
  global $source_files, $generate_constants, $generate_functions;
  global $funclist, $num_funcs;
  $num=count($source_files);

  for ($i=0; $i<$num; $i++) {
    echo "READING " . $source_files[$i] . "\n";
    $contents=read_file($source_files[$i]);
    if ($contents == false || $contents == "") {
      echo "Could not read $filename\n";
    }
    if ($generate_functions) {
      parse_file($contents);
    }
    if ($generate_constants) {
      echo "Scanning for constants\n";
      scan_for_constants($contents);
    }
  }


  if ($generate_functions) {
    echo "Writing function XML files\n";
    write_functions_xml();
  }

  if ($generate_constants) {
    echo "Writing constants XML file\n";
    write_constants_xml();
  }

  if ($generate_constants && $generate_functions) {
    echo "Writing reference XML file\n";
    write_reference_xml();
  }

  return(1);
}

function minimum_version($vercheck) {
  $minver = explode(".", $vercheck);
  $curver = explode(".", phpversion());
  if (($curver[0] < $minver[0]) || (($curver[0] = $minver[0]) && ($curver[1] < 
$minver[1])) ||
      (($curver[0] = $minver[0]) && ($curver[1] = $minver[1]) && ($curver[2][0] < 
$minver[2][0]))) {
    return false;
  } else {
    return true;
  }
}

function usage($progname)
{
  global $version;
  echo "XML_PROTO v$version : Generate PHP documentation from proto defs (req PHP 
4.3.0-pre1+)\n";
  echo "Usage: " . $progname . " [opts] <extension name> <files to parse>\n";
  echo "   Ex: " . $progname . " mcve /php4/ext/mcve/*.c\n\n";
  echo "Options:\n";
  echo "  -C : generate only constants.xml file (in current directory)\n";
  echo "  -F : generate only fuction reference files (all will be placed in current 
directory)\n";
  echo "  -A : (default) generate full documentation template\n";
  echo "       This will generate <extension name>/ directory which reference.xml and 
contents.xml\n";
  echo "       will be created in, and a subdirectory  <extension name>/functions/ 
which all\n";
  echo "       function xml reference files will be generated in\n\n";
  echo "Note: If you are documenting a new extension, you will need to add a new 
line\n";
  echo "      to the /manual.xml.in file under the <part id=\"funcref\"> section 
following\n";
  echo "      the format \"&reference.<extension name>.reference;\", but please try\n";
  echo "      to maintain the alphabetical order!\n\n";
}

function parse_cli($progargc, $progargv)
{
  global $generate_constants, $generate_functions, $extension_name;
  global $constant_dir, $function_dir, $source_files;

  $unknown_arg=0;

  for ($i=1; $i<$progargc; $i++) {
    if (strcasecmp($progargv[$i], "-C") == 0) {
      $generate_constants=1;
      $generate_functions=0;
    } else if (strcasecmp($progargv[$i], "-F") == 0) {
      $generate_functions=1;
      $generate_constants=0;
    } else if (strcasecmp($progargv[$i], "-A") == 0) {
      $generate_functions=1;
      $generate_constants=1;
    } else {
      if ($unknown_arg == 0) {
        $extension_name=$progargv[$i];
        if ($generate_functions && $generate_constants) {
          $constant_dir="./$extension_name/";
          $function_dir="./$extension_name/functions/";
        }
      } else {
        $temp_source_files=glob($progargv[$i]);
        $num=count($source_files);
        $new_num=count($temp_source_files);
        for ($j=0; $j<$new_num; $j++) {
          $source_files[$num+$j]=$temp_source_files[$j];
        }
        $total=count($source_files);
      }
      $unknown_arg++;
    }
  }

  return(1);
}

$myargc=$_SERVER["argc"];
$myargv=$_SERVER["argv"];

if (!minimum_version("4.3.0")) {
  echo "You need PHP 4.3.0-pre1 or higher!\n";
  $ver=phpversion();
  echo "YOU HAVE: $ver\n";
  exit();
}

if ($myargc < 3) {
  usage($myargv[0]);
  exit();
}

if (!parse_cli($myargc, $myargv)) {
  usage($myargv[0]);
  exit();
}

/* Generating it all, create directory structure */
if ($generate_constants && $generate_functions) {
  mkdir("./" . $extension_name);
  mkdir("./" . $extension_name . "/functions");
}

create_xml_docs();
  echo "\n";
  echo "Note: If you are documenting a new extension, you will need to add a new 
line\n";
  echo "      to the /manual.xml.in file under the <part id=\"funcref\"> section 
following\n";
  echo "      the format \"&reference.<extension name>.reference;\", but please try\n";
  echo "      to maintain the alphabetical order!\n\n";

?>


-- 
PHP Documentation Mailing List (http://www.php.net/)
To unsubscribe, visit: http://www.php.net/unsub.php

Reply via email to