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 &
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('&', '&', $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