]> granicus.if.org Git - php/commitdiff
next generation ext_skel script - initial checkin
authorHartmut Holzgraefe <hholzgra@php.net>
Wed, 19 Feb 2003 10:07:22 +0000 (10:07 +0000)
committerHartmut Holzgraefe <hholzgra@php.net>
Wed, 19 Feb 2003 10:07:22 +0000 (10:07 +0000)
scripts/ext_skel_ng/README [new file with mode: 0644]
scripts/ext_skel_ng/ext_skel_ng.php [new file with mode: 0644]
scripts/ext_skel_ng/extension.dtd [new file with mode: 0644]
scripts/ext_skel_ng/extension.xml [new file with mode: 0644]
scripts/ext_skel_ng/extension_parser.php [new file with mode: 0644]
scripts/ext_skel_ng/php_constant.php [new file with mode: 0644]
scripts/ext_skel_ng/php_element.php [new file with mode: 0644]
scripts/ext_skel_ng/php_function.php [new file with mode: 0644]
scripts/ext_skel_ng/xml_stream_callback_parser.php [new file with mode: 0644]
scripts/ext_skel_ng/xml_stream_parser.php [new file with mode: 0644]

diff --git a/scripts/ext_skel_ng/README b/scripts/ext_skel_ng/README
new file mode 100644 (file)
index 0000000..10d5cfd
--- /dev/null
@@ -0,0 +1,36 @@
+sorry, no real documentation yet ...
+just a short look at what is going on
+
+ext_skel_ng.php gets an extension description
+from an "extension.xml" file and generates working
+code and documentation stubs from that
+
+call "php ext_skel_ng.php" to see it at work,
+it will create a dummy extension including 
+
+- module globals and ini paramter setup
+- function registration and stubbs
+- documentation framework
+- config.m4 (only minimal for now)
+- ...
+
+almost every aspect of an extension may now be 
+configured using one xml description file instead
+of the old mixture of command line parameters
+and a proto file
+
+it is even possible to embed function code into
+the xml description right away, so it should be
+possible to create complete working extensions
+from just the xml description without further
+editing in a not to distant future
+
+for now almost all the 'helpfull comments' have 
+been removed from the generated code. some of
+them (like 'uncomment this if you have ini params)
+just don't make sense anymore, others will come
+back (configurable) at a later state 
+
+... have fun!
+
+Hartmut Holzgraefe <hholzgra@php.net>
diff --git a/scripts/ext_skel_ng/ext_skel_ng.php b/scripts/ext_skel_ng/ext_skel_ng.php
new file mode 100644 (file)
index 0000000..8cedd63
--- /dev/null
@@ -0,0 +1,13 @@
+<?php
+       require_once "extension_parser.php";
+
+       $ext = &new extension_parser(fopen("extension.xml", "r"));
+
+  system("rm -rf {$ext->name}");
+  mkdir($ext->name);
+
+       $ext->write_config_m4();
+       $ext->write_header_file();
+       $ext->write_code_file();
+       $ext->generate_documentation();
+?>
\ No newline at end of file
diff --git a/scripts/ext_skel_ng/extension.dtd b/scripts/ext_skel_ng/extension.dtd
new file mode 100644 (file)
index 0000000..533dac8
--- /dev/null
@@ -0,0 +1,80 @@
+<!--
+  -->
+
+<!ELEMENT extension (name|summary|description|license|maintainers|release|changelog|functions|constants|globals|deps)*>
+<!ATTLIST extension type    (source|binary|empty) "empty"
+                  version CDATA                 #REQUIRED>
+
+<!ELEMENT name (#PCDATA)>
+
+<!ELEMENT summary (#PCDATA)>
+
+<!ELEMENT description (#PCDATA)>
+
+<!ELEMENT maintainers (maintainer)+>
+
+<!ELEMENT maintainer (user|role|name|email)*>
+
+<!ELEMENT user (#PCDATA)>
+
+<!ELEMENT role (#PCDATA)>
+
+<!ELEMENT email (#PCDATA)>
+
+<!ELEMENT changelog (release)*>
+
+<!ELEMENT release (version|license|state|date|notes|filelist|deps)*>
+
+<!ELEMENT version (#PCDATA)>
+
+<!ELEMENT state (#PCDATA)>
+
+<!ELEMENT license (#PCDATA)>
+
+<!ELEMENT date (#PCDATA)>
+
+<!ELEMENT notes (#PCDATA)>
+
+<!ELEMENT functions (function)*>
+<!ELEMENT function (summary,proto,description*)>
+<!ATTLIST function
+  name  CDATA #REQUIRED
+>
+<!ELEMENT proto (#PCDATA)>  
+
+<!ELEMENT constants (constant)*>
+<!ELEMENT constant (#PCDATA)>
+<!ATTLIST constant
+  name  CDATA #REQUIRED
+  value CDATA #REQUIRED
+  type  (string|int|float) "string"
+>
+
+<!ELEMENT globals (phpini|global)*>
+<!ELEMENT phpini (#PCDATA)>
+<!ATTLIST phpini
+  name CDATA #REQUIRED
+  type CDATA #REQUIRED
+  value CDATA #REQUIRED
+  access (system|perdir|user|all) "all"
+  onupdate CDATA #IMPLIED
+>
+<!ELEMENT global (#PCDATA)>
+<!ATTLIST global
+  name CDATA #REQUIRED
+  type CDATA #REQUIRED
+>
+
+<!ELEMENT deps (with|lib|header|file|program)*>
+<!ELEMENT with (#PCDATA)>
+<!ATTLIST with
+  defaults CDATA #REQUIRED
+  testfile CDATA #REQUIRED
+  name     CDATA #IMPLIED
+>
+<!ELEMENT lib (#PCDATA)>
+<!ATTLIST lib
+  name       CDATA #REQUIRED
+  function   CDATA #REQUIRED
+  searchpath CDATA #IMPLIED
+>
\ No newline at end of file
diff --git a/scripts/ext_skel_ng/extension.xml b/scripts/ext_skel_ng/extension.xml
new file mode 100644 (file)
index 0000000..f9b485d
--- /dev/null
@@ -0,0 +1,82 @@
+<?xml version="1.0" encoding="ISO-8859-1" ?>
+<!DOCTYPE extension SYSTEM "extension.dtd">
+<extension version="0.1">
+       <name>dummy</name>
+  <summary>experimental dummy extension</summary>
+  <description>
+   this is used for testing of the extension generater only
+  </description>
+       
+  <maintainers>
+               <maintainer>            
+                       <user>hholzgra</user>
+                       <name>Hartmut Holzgraefe</name>
+                       <email>hholzgra@php.net</email>
+                       <role>lead</role>
+               </maintainer>
+               <maintainer>            
+                       <user>dummy</user>
+                       <name>Crashtest Dummy</name>
+                       <email>dummy@example.com</email>
+                       <role>dummy</role>
+               </maintainer>
+  </maintainers>
+               
+  <release>
+               <version>0.1</version>
+               <date>2002-02-16</date>
+               <state>alpha</state>
+    <license>other</license>
+    <notes>
+      - first experimental draft
+    </notes>
+  </release>
+
+  <changelog>
+  </changelog>
+       
+
+       <deps>
+         <!-- these are not yet used in any way :( -->
+    <with defaults='/usr:/usr/local' testfile='include/dummy.h'></with>
+    <lib name='dummy' function='dummy' searchpath='/usr/lib:/lib'></lib>
+  </deps>
+
+  <constants>
+               <constant name="DUMMY_OK"  type="int" value="1">dummy ok status</constant>
+               <constant name="DUMMY_ERR" type="int" value="0">dummy fault status</constant>
+  </constants>
+
+  <globals>
+               <global name="foobar" type="int"></global>
+               <phpini name="foo_int"    type="int"    value="42"     access="system"></phpini> 
+               <phpini name="foo_bool"    type="int"   value="on"     access="all"   onupdate="OnUpdateBool"></phpini> 
+               <phpini name="foo_string" type="string" value="foobar" access="all"   ></phpini> 
+  </globals>
+
+  <functions>
+
+   <function name='dummy_int'>
+    <summary>dummy integer conversion</summary>
+               <proto>int dummy_int(int bar)</proto>
+    <description>
+some funcy longer description
+
+foo
+bar
+               </description>
+   </function>
+
+   <function name='dummy_string'>
+    <summary>dummy string conversion</summary>
+               <proto>string dummy_string(string bar) foobar</proto>
+         <code>
+<![CDATA[
+  RETURN_STRINGL(bar, bar_len, 1);
+]]>
+    </code>
+   </function>
+
+  </functions>
+       
+</extension>
\ No newline at end of file
diff --git a/scripts/ext_skel_ng/extension_parser.php b/scripts/ext_skel_ng/extension_parser.php
new file mode 100644 (file)
index 0000000..81400f8
--- /dev/null
@@ -0,0 +1,789 @@
+<?php
+       // {{{ includes
+
+       require_once "php_element.php";
+       require_once "php_constant.php";
+       require_once "php_function.php";
+
+       require_once "xml_stream_parser.php";
+       require_once "xml_stream_callback_parser.php";
+
+       // }}} 
+
+       class extension_parser extends xml_stream_callback_parser {
+
+               // {{{ constructor
+
+       function extension_parser($stream) {
+               $this->template_dir = dirname(realpath(__FILE__))."/templates";
+               
+               $this->name = "foobar";
+
+               $this->license = "php";
+               
+               $this->constants = array();
+               $this->functions = array();
+               $this->globals   = array();
+               $this->phpini    = array();
+               $this->users     = array();
+               $this->dependson = array();
+               
+               parent::xml_stream_callback_parser($stream);
+       }
+
+       // }}} 
+
+               // {{{ parsing
+
+       // {{{   general infos
+
+               function handle_extension_name($attr) {
+                       $this->name = trim($this->cdata);
+               }
+
+               function handle_extension_summary($attr) {
+                       $this->summary = trim($this->cdata);
+               }
+
+               function handle_extension_description($attr) {
+                       $this->description = $this->cdata;
+               }
+
+               function handle_release_version($attr) {
+                       $this->version = trim($this->cdata);
+               }
+
+       function handle_maintainers_maintainer_user($attr) {
+               $this->user["user"] = trim($this->cdata);
+       }
+
+       function handle_maintainers_maintainer_name($attr) {
+               $this->user["name"] = trim($this->cdata);
+       }
+
+       function handle_maintainers_maintainer_email($attr) {
+               $this->user["email"] = trim($this->cdata);
+       }
+
+       function handle_maintainers_maintainer_role($attr) {
+               $this->user["role"] = trim($this->cdata);
+       }
+
+       function handle_maintainers_maintainer($attr) {
+               $this->users[$this->user["name"]] = $this->user;
+               unset($this->user);
+       }
+
+               // }}} 
+
+       // {{{   constants
+
+               function handle_constants_constant($attr) {
+                       $name = $attr["name"];
+                       $value= $attr["value"];
+                       $type = isset($attr["type"]) ? $attr["type"] : "string";
+
+                       switch($type) {
+                       case "int":
+                       case "integer":
+                               if (!is_numeric($value))   $this->error("invalid value for integer constant: '$value'"); 
+                               if ((int)$value != $value) $this->error("invalid value for integer constant: '$value'");
+                               $this->constants[] = &new php_constant($name, (int)$value, "integer", trim($this->cdata)); 
+                               break;
+                               
+                       case "float":
+                       case "double":
+                       case "real":
+                               if (!is_numeric($value))   $this->error("invalid value for integer constant: '$value'"); 
+                               $this->constants[] = &new php_constant($name, $value, "float", trim($this->cdata)); 
+                               break;
+
+                       case "string":
+                       default:
+                               $this->constants[] = &new php_constant($name, $value, "string", trim($this->cdata)); 
+                               break;
+                       }
+               }
+
+               // }}} 
+       
+       // {{{   functions
+
+               function handle_functions_function_summary($attr) {
+                       $this->func_summary = trim($this->cdata);
+               }
+
+               function handle_functions_function_proto($attr) {
+                       $this->func_proto = trim($this->cdata);
+               }
+
+               function handle_functions_function_description($attr) {
+                       $this->func_desc = trim($this->cdata);
+               }
+
+               function handle_functions_function_code($attr) {
+                       $this->func_code = $this->cdata;
+               }
+
+               function handle_functions_function($attr) {
+                       $this->functions[$attr['name']] = new php_function($attr['name'], $this->func_summary, $this->func_proto, @$this->func_desc, @$this->func_code);
+                       unset($this->func_summary);
+                       unset($this->func_proto);
+                       unset($this->func_desc);
+                       unset($this->func_code);
+               }
+
+               // }}} 
+       
+       // {{{   globals and php.ini
+
+               function handle_globals_global($attr) {
+                       if($attr["type"] == "string") $attr["type"] = "char*";
+                       $this->globals[$attr["name"]] = $attr;
+               }
+
+               function handle_globals_phpini($attr) {
+                       $ini = array("name" => $attr["name"],
+                                                                        "type" => $attr["type"],
+                                                                        "value"=> $attr["value"]
+                                                                        );
+                       switch($attr["access"]) {
+                       case "system":
+                               $ini["access"] = "PHP_INI_SYSTEM";
+                               break;
+                       case "perdir":
+                               $ini["access"] = "PHP_INI_PERDIR";
+                               break;
+                       case "user":
+                               $ini["access"] = "PHP_INI_USER";
+                               break;
+                       case "all":
+                       default:
+                               $ini["access"] = "PHP_INI_ALL";
+                               break;
+                       }
+                       if(isset($attr["onupdate"])) {
+                               $ini["onupdate"] = $attr["onupdate"];
+                       } else {
+                               switch($attr["type"]) {
+                               case "int":
+                               case "long":
+                                       $ini["onupdate"] = "OnUpdateInt";
+                                       break;
+                               case "float":
+                               case "double":
+                                       $ini["onupdate"] = "OnUpdateFloat";
+                                       break;
+                               case "string":
+                                       $ini["type"] = "char*";
+                                       // fallthru
+                               case "char*":
+                                       $ini["onupdate"] = "OnUpdateString";
+                                       break;
+                               }
+                       }
+                       $this->phpini[$attr["name"]] = $ini;
+                       $this->handle_globals_global($attr);
+               }
+
+       // }}} 
+
+       // }}} 
+
+               // {{{ output generation
+
+               // {{{   docbook documentation
+
+               function generate_documentation() {
+                       system("rm -rf {$this->name}/manual");
+                       mkdir("{$this->name}/manual");
+
+                       $docdir = "{$this->name}/manual/".$this->name;
+                       mkdir($docdir);
+
+                       $fp = fopen("$docdir/reference.xml", "w");
+                       fputs($fp,
+"<?xml version='1.0' encoding='iso-8859-1'?>
+<!-- \$Revision$ -->
+ <reference id='ref.{$this->name}'>
+  <title>{$this->summary}</title>
+  <titleabbrev>{$this->name}</titleabbrev>
+
+  <partintro>
+   <section id='{$this->name}.intro'>
+    &reftitle.intro;
+    <para>
+{$this->description}
+    </para>
+   </section>
+   
+   <section id='{$this->name}.requirements'>
+    &reftitle.required;
+    <para>
+    </para>
+   </section>
+
+   &reference.{$this->name}.configure;
+
+   <section id='{$this->name}.configuration'>
+    &reftitle.runtime;
+    &no.config;
+   </section>
+
+   <section id='{$this->name}.resources'>
+    &reftitle.resources;
+    &no.resource;
+   </section>
+
+   <section id='{$this->name}.constants'>
+    &reftitle.constants;
+    &no.constants;
+   </section>
+   
+  </partintro>
+
+&reference.{$this->name}.functions;
+
+ </reference>
+");
+      fputs($fp, php_element::docbook_editor_footer());
+
+                       fclose($fp);
+
+                       mkdir("$docdir/functions");
+                       foreach($this->functions as $name => $function) {
+                               $filename = $docdir . "/functions/" . strtolower(str_replace("_", "-", $name)) . ".xml";
+                               $funcfile = fopen($filename, "w");
+                               fputs($funcfile, $function->docbook_xml());
+                               fclose($funcfile);
+                       }
+               }
+
+               // }}} 
+
+         // {{{   extension entry
+
+               function generate_extension_entry() {
+                       return '
+/* {{{ '.$this->name.'_module_entry
+ */
+zend_module_entry '.$this->name.'_module_entry = {
+       STANDARD_MODULE_HEADER,
+       "'.$this->name.'",
+       '.$this->name.'_functions,
+       PHP_MINIT('.$this->name.'),     /* Replace with NULL if there is nothing to do at php startup   */ 
+       PHP_MSHUTDOWN('.$this->name.'), /* Replace with NULL if there is nothing to do at php shutdown  */
+       PHP_RINIT('.$this->name.'),               /* Replace with NULL if there is nothing to do at request start */
+       PHP_RSHUTDOWN('.$this->name.'), /* Replace with NULL if there is nothing to do at request end   */
+       PHP_MINFO('.$this->name.'),
+       "'.$this->version.'", 
+       STANDARD_MODULE_PROPERTIES
+};
+/* }}} */
+
+#ifdef COMPILE_DL_'.strtoupper($this->name).'
+ZEND_GET_MODULE('.$this->name.')
+#endif
+';
+               }
+
+       // }}} 
+
+         // {{{ globals and ini
+
+         function generate_globals_c() {
+                       if (empty($this->globals)) return "";
+                       
+                       $code = "ZEND_DECLARE_MODULE_GLOBALS({$this->name})\n\n";
+                       
+                       if (!empty($this->phpini)) {
+                               $code .= "PHP_INI_BEGIN()\n";
+                               foreach ($this->phpini as $name => $ini) {
+                                       $code .= "  STD_PHP_INI_ENTRY(\"{$this->name}.$name\", \"$ini[value]\", $ini[access], $ini[onupdate], $name, zend_{$this->name}_globals, {$this->name}_globals)\n";
+                               }
+                               $code .= "PHP_INI_END()\n\n";                   
+                               $code .= "static void php_{$this->name}_init_globals(zend_{$this->name}_globals *{$this->name}_globals)\n";
+                               $code .= "{\n";
+                               foreach ($this->globals as $name => $ini) {
+                                       $code .= "  {$this->name}_globals->$name = ";
+                                       if (strstr($ini["type"],"*")) {
+                                               $code .= "NULL;\n";
+                                       } else {
+                                               $code .= "0;\n";
+                                       }
+                               }
+                               $code .= "}\n\n";
+                               return $code;
+                       }
+               }
+               
+         function generate_globals_h() {
+                       if (empty($this->globals)) return "";
+                       
+                       $code = "ZEND_BEGIN_MODULE_GLOBALS({$this->name})\n";
+                       foreach($this->globals as $name => $global) {
+                               $code .= "  $global[type] $name;\n";
+                       }
+                       $code.= "ZEND_END_MODULE_GLOBALS({$this->name})\n";
+                       
+                       $upname = strtoupper($this->name);
+                       
+                       $code.= "
+
+#ifdef ZTS
+#define {$upname}_G(v) TSRMG({$this->name}_globals_id, zend_{$this->name}_globals *, v)
+#else
+#define {$upname}_G(v) ({$this->name}_globals.v)
+#endif
+
+";
+                       
+                       return $code;
+               }
+
+       // }}} 
+
+       // {{{ license and authoers
+
+         function get_license() {
+                       $code = "/*\n";
+                       switch($this->license) {
+                       case "php":
+                               $code.=
+'   +----------------------------------------------------------------------+
+   | PHP Version 4                                                        |
+   +----------------------------------------------------------------------+
+   | Copyright (c) 1997-2002 The PHP Group                                |
+   +----------------------------------------------------------------------+
+   | This source file is subject to version 2.02 of the PHP license,      |
+   | that is bundled with this package in the file LICENSE, and is        |
+   | available at through the world-wide-web at                           |
+   | http://www.php.net/license/2_02.txt.                                 |
+   | If you 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          |
+   | license@php.net so we can mail you a copy immediately.               |
+';
+                               break;
+                       
+                       default:
+                               $code.= 
+"   +----------------------------------------------------------------------+
+   | unkown license: '{$this->license}'                                                        |
+   +----------------------------------------------------------------------+
+";
+                               break;
+                       }
+
+                       $code.= "   +----------------------------------------------------------------------+\n";
+                       $prefix = "Authors: ";
+                       foreach($this->users as $name => $user) {
+                               $code .= sprintf("   | $prefix %-58s |\n", "$user[name] <$user[email]>");
+                               $prefix = str_repeat(" ",strlen($prefix));
+                       }
+                       $code.= "   +----------------------------------------------------------------------+\n";
+                       $code.= "*/\n\n";
+
+                       $code.= "/* $ Id: $ */ \n\n";
+
+                       return $code;
+         }
+
+       // }}} 
+
+       // {{{ editor config footer
+
+         function editor_config_c() {
+                       return '
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: noet sw=4 ts=4 fdm=marker
+ * vim<600: noet sw=4 ts=4
+ */
+';
+         }
+
+       // }}} 
+
+       // {{{ header file
+
+         function write_header_file() {
+                       $fp = fopen("{$this->name}/php_{$this->name}.h", "w");
+
+                       $upname = strtoupper($this->name);
+                       
+                       fputs($fp, $this->get_license());
+                       fputs($fp, "#ifndef PHP_{$upname}_H\n");
+                       fputs($fp, "#define PHP_{$upname}_H\n\n");
+
+                       fputs($fp, "#ifndef PHP_HAVE_{$upname}\n\n");
+
+                       fputs($fp, "
+extern zend_module_entry {$this->name}_module_entry;
+#define phpext_{$this->name}_ptr &{$this->name}_module_entry
+
+#ifdef PHP_WIN32
+#define PHP_{$upname}_API __declspec(dllexport)
+#else
+#define PHP_{$upname}_API
+#endif
+
+PHP_MINIT_FUNCTION({$this->name});
+PHP_MSHUTDOWN_FUNCTION({$this->name});
+PHP_RINIT_FUNCTION({$this->name});
+PHP_RSHUTDOWN_FUNCTION({$this->name});
+PHP_MINFO_FUNCTION({$this->name});
+
+#ifdef ZTS
+#include \"TSRM.h\"
+#endif
+
+");
+
+                       fputs($fp, $this->generate_globals_h());
+
+                       fputs($fp, "\n");
+
+                       foreach($this->functions as $name => $function) {
+                               fputs($fp, "PHP_FUNCTION($name);\n");
+                       }
+                       
+                       fputs($fp, "\n");
+
+                       fputs($fp, "#endif /* PHP_HAVE_{$upname} */\n\n");
+                       fputs($fp, "#endif /* PHP_{$upname}_H */\n\n");
+
+                       fputs($fp, $this->editor_config_c());
+
+                       fclose($fp);
+               }
+
+       // }}} 
+
+       // {{{ internal functions
+
+       function internal_functions_c() {
+               $code = "
+/* {{{ PHP_MINIT_FUNCTION */
+PHP_MINIT_FUNCTION({$this->name})
+{
+";
+
+               if(count($this->globals)) {
+                       $code .= "  ZEND_INIT_MODULE_GLOBALS({$this->name}, php_{$this->name}_init_globals, NULL)\n";
+               }
+
+               if(count($this->phpini)) {
+                       $code .= "  REGISTER_INI_ENTRIES();\n";
+               }
+
+               $code .="\n  /* add your stuff here */\n";
+
+               $code .= "
+  return SUCCESS;
+}
+/* }}} */
+
+";
+
+               $code .= "
+/* {{{ PHP_MSHUTDOWN_FUNCTION */
+PHP_MSHUTDOWN_FUNCTION({$this->name})
+{
+";
+
+               if(count($this->phpini)) {
+                       $code .= "  UNREGISTER_INI_ENTRIES();\n";
+               }
+
+               $code .="\n  /* add your stuff here */\n";
+
+               $code .= "
+  return SUCCESS;
+}
+/* }}} */
+
+";
+
+               $code .= "
+/* {{{ PHP_RINIT_FUNCTION */
+PHP_RINIT_FUNCTION({$this->name})
+{
+  /* add your stuff here */
+
+   return SUCCESS;
+}
+/* }}} */
+
+";
+
+               $code .= "
+/* {{{ PHP_RSHUTDOWN_FUNCTION */
+PHP_RSHUTDOWN_FUNCTION({$this->name})
+{
+  /* add your stuff here */
+
+  return SUCCESS;
+}
+/* }}} */
+
+";
+       
+  $code .= "
+/* {{{ PHP_MINFO_FUNCTION */
+PHP_MINFO_FUNCTION({$this->name})
+{
+  php_info_print_table_start();
+  php_info_print_table_header(2, \"{$this->name} support\", \"enabled\");
+  php_info_print_table_end();
+
+  /* add your stuff here */
+";
+
+if(count($this->phpini)) {
+       $code .= "\n  DISPLAY_INI_ENTRIES();";
+}
+$code .= "
+}
+/* }}} */
+
+";
+
+               return $code;
+  }
+
+       // }}} 
+
+
+       // {{{ public functions
+
+       function public_functions_c() {
+               $code = "";
+
+               foreach ($this->functions as $name => $func) {
+
+                       $code .= "\n/* {{{ func {$func->returns} {$func->name}(";
+                       if (isset($func->params)) {
+                               foreach ($func->params as $key => $param) {
+                                       if (!empty($param['optional'])) $code.=" [";
+                                       if ($key) $code.=", ";
+                                       $code .= $param['type']." ";
+                                       $code .= isset($param['name']) ? $param['name'] : "par_$key"; 
+                               }
+                       }
+                       for ($n=$func->optional; $n>0; $n--) {
+                               $code .= "]";
+                       }
+                       $code .= ")\n  ";
+                       if(!empty($func->summary)) $code .= $func->summary;
+                       $code .= " */\n";
+
+                       $code .= "PHP_FUNCTION({$func->name})\n";
+                       $code .= "{\n";
+                       $code .= "\tint argc = ZEND_NUM_ARGS();\n\n";
+                       
+                       if (isset($func->params)) {
+                               $arg_string="";
+                               $arg_pointers=array();
+                               $optional=false;
+                               $res_fetch="";
+                               
+                               foreach ($func->params as $key => $param) {
+                                       $name = isset($param['name']) ? $param['name'] : "par_$key"; 
+                                       $arg_pointers[]="&$name";
+                                       if(isset($param['optional'])&&!$optional) {
+                                               $optional=true;
+                                               $arg_string.="|";
+                                       }
+
+                                       switch($param['type']) {
+                                       case "void":
+                                               break;
+
+                                       case "bool":
+                                               $arg_string.="b";
+                                               $code .= "\tzend_bool $name = 0;\n";
+                                               break;
+
+                                       case "int":
+                                               $arg_string.="l";
+                                               $code .= "\tlong $name = 0;\n";
+                                               break;
+
+                                       case "float":
+                                               $arg_string.="d";
+                                               $code .= "\tdouble $name = 0.0;\n";
+                                               break;
+
+                                       case "string":
+                                               $arg_string.="s";
+                                               $code .= "\tchar * $name = NULL;\n";
+                                               $code .= "\tint {$name}_len = 0;\n";
+                                               $arg_pointers[]="&{$name}_len";
+                                               break;
+
+                                       case "array":
+                                               $arg_string.="a";
+                                               $code .= "\tzval * $name = NULL;\n";
+                                               break;
+
+                                       case "object": 
+                                               $arg_string.="o";
+                                               $code .= "\tzval * $name = NULL;\n";
+                                               break;
+
+                                       case "resource":
+                                               $arg_string.="r";
+                                               $code .= "\tzval * $name = NULL;\n";
+                                               $code .= "\tint * {$name}_id = -1;\n";
+                                               $arg_pointers[]="&{$name}_id";
+                                               $res_fetch.="\tif ($name) {\n"
+                                                       ."\t\tZEND_FETCH_RESOURCE(???, ???, $name, {$name}_id, \"???\", ???_rsrc_id);\n"
+                                                       ."\t}\n";
+                                               break;
+
+                                       case "mixed":
+                                               $arg_string.="z";
+                                               $code .= "\tzval * $name = NULL;\n";
+                                               break;
+                                       }
+                               }
+
+                               $code .= "\n\tif (zend_parse_parameters(argc TSRMLS_CC, \"$arg_string\", ".join(", ",$arg_pointers).") == FAILURE) return;\n";
+                               if($res_fetch) $code.="\n$res_fetch\n";
+                       } else {
+                               $code .= "\tif(argc>0) { WRONG_PARAM_COUNT; }\n";
+                       }
+
+      $code .= "\n";
+
+      if (!empty($func->code)) {
+        $code .= $func->code."\n";
+      } else {
+                         $code .= "\tphp_error(E_WARNING, \"{$func->name}: not yet implemented\");\n\n";
+                       
+                         switch($func->returns) {
+                         case "void":
+                                 break;
+                               
+                         case "bool":
+                                 $code .= "\tRETURN_FALSE;\n"; 
+                                 break;
+                               
+                         case "int":
+                               $code .= "\tRETURN_LONG(0);\n"; 
+                                 break;
+                               
+                         case "float":
+                                 $code .= "\tRETURN_DOUBLE(0.0);\n";
+                                 break;
+                               
+                         case "string":
+                                 $code .= "\tRETURN_STRINGL(\"\", 0, 1);\n";
+                                 break;
+
+                         case "array":
+                               $code .= "\tarray_init(return_value);\n";
+                                 break;
+                               
+                         case "object": 
+                                 $code .= "\tobject_init(return_value)\n";
+                                 break;
+                               
+                         case "resource":
+                                 $code .= "\t/* RETURN_RESOURCE(...); /*\n";
+                                 break;
+
+                         case "mixed":
+                                 $code .= "\t/* RETURN_...(...); /*\n";                                
+                                 break;
+                         }
+      }
+                       
+
+                       $code .= "}\n/* }}} */\n\n";
+               }
+
+               
+               return $code;
+       }
+
+       // }}} 
+
+
+  // {{{ code file
+
+       function write_code_file() {
+                       $fp = fopen("{$this->name}/{$this->name}.c", "w");
+
+                       $upname = strtoupper($this->name);
+                       
+                       fputs($fp, $this->get_license());
+
+                       fputs($fp, '
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <php.h>
+#include <php_ini.h>
+#include <ext/standard/info.h>
+
+');
+                       fputs($fp, "#include \"php_{$this->name}.h\"\n\n");
+                                               
+                       if (!empty($this->globals)) {
+                               fputs($fp, "ZEND_DECLARE_MODULE_GLOBALS({$this->name})\n\n");
+                       }
+
+                       fputs($fp, "/* {{{ {$this->name}_functions[] */\n");
+                       fputs($fp, "function_entry {$this->name}_functions[] = {\n");
+                       foreach($this->functions as $name => $function) {
+                               fputs($fp, sprintf("  PHP_FE(%-20s, NULL)\n",$name));
+                       }
+                       fputs($fp, "};\n/* }}} */\n\n");
+                       
+                       fputs($fp, $this->generate_extension_entry());
+
+                       fputs($fp, "\n/* {{{ globals and ini entries */\n");
+                       fputs($fp, $this->generate_globals_c());
+                       fputs($fp, "/* }}} */\n\n");
+
+                       fputs($fp, $this->internal_functions_c());
+
+                       fputs($fp, $this->public_functions_c());
+
+                       fputs($fp, $this->editor_config_c());
+       }
+
+       // }}} 
+
+
+       function write_config_m4() {
+
+               $upname = $this->name;
+               
+               $fp = fopen("{$this->name}/config.m4", "w");
+               fputs($fp, 
+"dnl
+dnl \$ Id: \$
+dnl
+
+PHP_ARG_ENABLE({$this->name} , whether to enable {$this->name} functions,
+[  --disable-{$this->name}         Disable {$this->name} functions], yes)
+
+if test \"\$PHP_$upname\" != \"no\"; then
+  AC_DEFINE(HAVE_$upname, 1, [ ])
+  PHP_NEW_EXTENSION({$this->name}, {$this->name}.c, \$ext_shared)
+fi
+");
+               fclose($fp);
+       }
+
+         // }}} 
+
+       }       
+
+?>
\ No newline at end of file
diff --git a/scripts/ext_skel_ng/php_constant.php b/scripts/ext_skel_ng/php_constant.php
new file mode 100644 (file)
index 0000000..fd1ac36
--- /dev/null
@@ -0,0 +1,38 @@
+<?php
+
+       class php_constant extends php_element {
+               function php_constant($name, $value, $type="string", $desc="") {
+                       $this->name = $name;
+                       $this->value= $value;
+                       $this->type = $type;
+                       $this->desc = $desc;
+               } 
+               
+               function c_code() {
+                       switch($this->type) {
+                       case "integer":
+                               return "REGISTER_LONG_CONSTANT(\"{$this->name}\", {$this->value}, 0);\n";
+                       
+                       case "float":
+                               return "REGISTER_DOUBLE_CONSTANT(\"{$this->name}\", {$this->value}, 0);\n";
+
+                       case "string":
+                               return "REGISTER_STRING_CONSTANT(\"{$this->name}\", \"$value\", ".strlen($this->value).", 0);\n";
+                       }
+               }
+
+               function docbook_xml() {
+                       return trim("
+<row>
+ <entry>
+  <constant id='constant".strtolower(str_replace("_","-",$this->name))."'>$name</constant>
+  (<link linkend='language.types.integer'>integer</link>)
+ </entry>
+ <entry>{$this->value}</entry>
+ <entry>{$this->desc}</entry>
+</row>
+")."\n";
+               }
+       }
+
+?>
\ No newline at end of file
diff --git a/scripts/ext_skel_ng/php_element.php b/scripts/ext_skel_ng/php_element.php
new file mode 100644 (file)
index 0000000..83de9dc
--- /dev/null
@@ -0,0 +1,74 @@
+<?php
+
+class php_element {
+       function is_type($name) {
+               $types = array("void"     => "void", 
+                                                                        "bool"     => "bool", 
+                                                                        "boolean"  => "bool", 
+                                                                        "int"      => "int", 
+                                                                        "integer"  => "int", 
+                                                                        "float"    => "float", 
+                                                                        "double"   => "float", 
+                                                                        "real"     => "float", 
+                                                                        "string"   => "string", 
+                                                                        "array"    => "array",
+                                                                        "object"   => "object",
+                                                                        "resource" => "resource",
+                                                                        "mixed"    => "mixes",
+                                                                        );
+               
+               if(isset($types[$name])) {
+                       return $types[$name];
+               } else {
+                       return false;
+               }
+       }
+
+       function is_name($name) {
+               if(ereg("[a-zA-Z0-9_]",$name)) {
+                       // TODO reserved words
+                       return true;
+               } 
+               return false;
+       }
+
+
+       function c_code() {
+               return "";
+       }
+
+       function h_code() {
+               return "";
+       }
+
+       function docbook_xml() {
+               return "";
+       }
+
+       function docbook_editor_footer($level=3) {
+               return '
+<!-- Keep this comment at the end of the file
+Local variables:
+mode: sgml
+sgml-omittag:t
+sgml-shorttag:t
+sgml-minimize-attributes:nil
+sgml-always-quote-attributes:t
+sgml-indent-step:1
+sgml-indent-data:t
+indent-tabs-mode:nil
+sgml-parent-document:nil
+sgml-default-dtd-file:"'.str_repeat("../",$level).'manual.ced"
+sgml-exposed-tags:nil
+sgml-local-catalogs:nil
+sgml-local-ecat-files:nil
+End:
+vim600: syn=xml fen fdm=syntax fdl=2 si
+vim: et tw=78 syn=sgml
+vi: ts=1 sw=1
+-->
+';
+       }
+}
+
+?>
\ No newline at end of file
diff --git a/scripts/ext_skel_ng/php_function.php b/scripts/ext_skel_ng/php_function.php
new file mode 100644 (file)
index 0000000..c9abe73
--- /dev/null
@@ -0,0 +1,210 @@
+<?php
+
+       class php_function extends php_element {
+         // all known php types
+               function php_function($name, $summary, $proto, $desc="", $code="") {
+                       $this->name = $name;
+                       $this->summary = $summary;
+                       $this->parse_proto($proto);
+                       $this->desc = empty($desc) ? "&warn.undocumented.func;" : $desc;
+      $this->code = $code;
+               } 
+               
+               function parse_proto($proto) {
+                       // 'tokenize' it
+                       $len=strlen($proto);
+                       $name="";
+                       $tokens=array();
+                       for($n=0;$n<$len;$n++) {
+                               $char = $proto{$n};
+                               if(ereg("[a-zA-Z0-9_]",$char)) {
+                                       $name.=$char;
+                               } else {
+                                       if($name) $tokens[]=$name;
+                                       $name="";
+                                       if(trim($char)) $tokens[]=$char;
+                               }
+                       }
+                       if($name) $tokens[]=$name;
+                       
+                       $n=0;
+                       $opts=0;
+                       $params=array();
+                       $return_type = ($this->is_type($tokens[$n])) ? $tokens[$n++] : "void";
+                       if(! $this->is_name($tokens[$n])) die("$tokens[$n] is not a valid function name");
+                       $function_name = $tokens[$n++];
+                       if($tokens[$n]!='(') die("'(' expected instead of '$tokens[$n]'");
+                       if($tokens[++$n]!=')') {                        
+                               for($param=0;$tokens[$n];$n++,$param++) {
+                                       if($tokens[$n]=='[') {
+                                               $params[$param]['optional']=true;
+                                               $opts++;
+                                               $n++;
+                                               if($param>0) { 
+                                                       if ($tokens[$n]!=',') die("',' expected after '[' instead of $token[$n]");
+                                                       $n++;
+                                               }
+                                       }
+                                       if(!$this->is_type($tokens[$n])) die("type name expected instead of $tokens[$n]");
+                                       $params[$param]['type']=$tokens[$n];
+                                       $n++;
+                                       if($this->is_name($tokens[$n])) {
+                                               $params[$param]['name']=$tokens[$n];
+                                               $n++;
+                                       }
+                                       if($tokens[$n]=='[') {
+                                               $n--;
+                                               continue;
+                                       }
+                                       if($tokens[$n]==',') continue;
+                                       if($tokens[$n]==']') break;
+                                       if($tokens[$n]==')') break;                     
+                               }
+                       }
+                       $numopts=$opts;
+                       while($tokens[$n]==']') {
+                               $n++;
+                               $opts--;
+                       }
+                       if($opts!=0) die ("'[' / ']' count mismatch");
+                       if($tokens[$n] != ')') die ("')' expected instead of $tokens[$n]");
+                       
+                       $this->name     = $function_name;
+                       $this->returns  = $return_type;
+                       $this->params   = $params;
+                       $this->optional = $numopts;
+               }
+
+               function c_code() {
+                       $code .= "\n/* {{{ proto {$this->returns} {$this->name}(";
+                       if(isset($this->params)) {
+                               foreach($this->params as $param) {
+                                       if(!empty($param['optional']))
+                                               $code.=" [";
+                                       if($key) 
+                                               $code.=", ";
+                                       $code .= $param['type']." ";
+                                       $code .= $param['name']; 
+                               }
+                       }
+                       for($n=$this->optional; $n>0; $n--) {
+                               $code .= "]";
+                       }
+                       $code .= ")\n  ";
+                       if(!empty($this->summary)) {
+                               $code .= $this->summary;
+                       }
+                       $code .= " */\n";
+                       $code .= "PHP_FUNCTION({$this->name})\n";
+                       $code .= "{\n";
+                       $code .= "\tint argc = ZEND_NUM_ARGS();\n\n";
+                       if(isset($this->params)) {
+                               $arg_string="";
+                               $arg_pointers=array();
+                               $optional=false;
+                               $res_fetch="";
+                               foreach($this->params as $param) {
+                                       $name = $param['name']; 
+                                       $arg_pointers[]="&$name";
+                                       if(isset($param['optional'])&&!$optional) {
+                                               $optional=true;
+                                               $arg_string.="|";
+                                       }
+                                       switch($param['type']) {
+                                               //case "void":
+                                       case "bool":
+                                               $arg_string.="b";
+                                               $code .= "\tzend_bool $name = 0;\n";
+                                               break;
+                                       case "int":
+                                               $arg_string.="l";
+                                               $code .= "\tlong $name = 0;\n";
+                                               break;
+                                       case "float":
+                                               $arg_string.="d";
+                                               $code .= "\tdouble $name = 0.0;\n";
+                                               break;
+                                       case "string":
+                                               $arg_string.="s";
+                                               $code .= "\tchar * $name = NULL;\n";
+                                               $code .= "\tint {$name}_len = 0;\n";
+                                               $arg_pointers[]="&{$name}_len";
+                                               break;
+                                       case "array":
+                                               $arg_string.="a";
+                                               $code .= "\tzval * $name = NULL;\n";
+                                               break;
+                                       case "object": 
+                                               $arg_string.="o";
+                                               $code .= "\tzval * $name = NULL;\n";
+                                               break;
+                                       case "resource":
+                                               $arg_string.="r";
+                                               $code .= "\tzval * $name = NULL;\n";
+                                               $code .= "\tint * {$name}_id = -1;\n";
+                                               $arg_pointers[]="&{$name}_id";
+                                               $res_fetch.="\tif ($name) {\n"
+                                                       ."\t\tZEND_FETCH_RESOURCE(???, ???, $name, {$name}_id, \"???\", ???_rsrc_id);\n"
+                                                       ."\t}\n";
+                                               break;
+                                       case "mixed":
+                                               $arg_string.="z";
+                                               $code .= "\tzval * $name = NULL;\n";
+                                               break;
+                                       }
+                               }
+                               $code .= "\n\tif (zend_parse_parameters(argc TSRMLS_CC, \"$arg_string\", ".join(", ",$arg_pointers).") == FAILURE) return;\n";
+                               if($res_fetch) $code.="\n$res_fetch\n";
+                       } else {
+                               $code .= "\tif(argc>0) { WRONG_PARAM_COUNT; }\n\n";
+                       }
+                       $code .= "\tphp_error(E_WARNING, \"{$this->name}: not yet implemented\");\n";
+                       $code .= "}\n/* }}} */\n\n";
+
+                       return $code;
+               }
+
+               function docbook_xml() {
+                       $xml = 
+'<?xml version="1.0" encoding="iso-8859-1"?>
+<!-- $Revision$ -->
+  <refentry id="function.'.strtolower(str_replace("_","-",$this->name)).'">
+   <refnamediv>
+    <refname>'.$this->name.'</refname>
+    <refpurpose>'.$this->summary.'</refpurpose>
+   </refnamediv>
+   <refsect1>
+    <title>Description</title>
+     <methodsynopsis>
+';
+
+                       $xml .= "      <type>{$this->returns}</type><methodname>{$this->name}</methodname>\n";
+                       if(empty($this->params)) {
+                               $xml .= "      <void/>\n";
+                       } else {
+                               foreach($this->params as $key => $param) {
+                                       if(isset($param['optional'])) {
+                                               $xml .= "      <methodparam choice='opt'>";
+                                       } else {
+                                               $xml .= "      <methodparam>";
+                                       }
+                                       $xml .= "<type>$param[type]</type><parameter>$param[name]</parameter>";
+                                       $xml .= "</methodparam>\n";
+                               }
+                       }
+
+$xml .= 
+'     </methodsynopsis>
+    <para>
+'.$this->desc.'
+    </para>
+   </refsect1>
+  </refentry>
+';
+      $xml .= $this->docbook_editor_footer(4);
+                       return $xml;
+               }
+       }
+
+?>
\ No newline at end of file
diff --git a/scripts/ext_skel_ng/xml_stream_callback_parser.php b/scripts/ext_skel_ng/xml_stream_callback_parser.php
new file mode 100644 (file)
index 0000000..50f1a1c
--- /dev/null
@@ -0,0 +1,35 @@
+<?php
+       class xml_stream_callback_parser extends xml_stream_parser {
+               function xml_stream_callback_parser ($stream) {
+                       $this->cdata = "";
+                       $this->tags = array();
+                       $this->attrs = array();
+
+                       parent::xml_stream_parser($stream);
+               }
+
+               function cdata($parser, $cdata) {
+                       $this->cdata .= $cdata;
+               }
+
+               function tag_open($parser, $tag, $attributes) {
+                       array_push($this->tags, $tag); 
+                       array_push($this->attrs, $attributes); 
+               }
+
+               function tag_close($parser, $tag) {
+                       $attributes = array_pop($this->attrs);
+
+                       for ($tags = $this->tags; count($tags); array_shift($tags)) {
+                               $method = "handle_".join("_", $tags);
+                               if(method_exists($this, $method)) {
+                                       $this->$method($attributes);
+                                       break;
+                               }
+                       }
+                
+                       $this->cdata = "";
+                       array_pop($this->tags); 
+               }
+       }
+?>
\ No newline at end of file
diff --git a/scripts/ext_skel_ng/xml_stream_parser.php b/scripts/ext_skel_ng/xml_stream_parser.php
new file mode 100644 (file)
index 0000000..626c50b
--- /dev/null
@@ -0,0 +1,42 @@
+<?php
+class xml_stream_parser  {
+    var $parser;
+
+    function xml_stream_parser($stream)
+    {
+                       if(!is_resource($stream)) die("not a stream");
+                       if(get_resource_type($stream) != "stream") die("not a stream");
+
+                       $this->parser = xml_parser_create();
+                       
+                       xml_set_object($this->parser, $this);
+                       xml_set_element_handler($this->parser, "tag_open", "tag_close");
+                       xml_set_character_data_handler($this->parser, "cdata");
+                       xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, false);
+               
+                       while(!feof($stream)) {
+                               xml_parse($this->parser, fgets($stream), feof($stream));
+                       }       
+                       xml_parser_free($this->parser);
+    }
+
+    function tag_open($parser, $tag, $attributes)
+    { 
+        var_dump($parser, $tag, $attributes); 
+    }
+
+    function cdata($parser, $cdata)
+    {
+        var_dump($parser, $cdata);
+    }
+
+    function tag_close($parser, $tag)
+    {
+        var_dump($parser, $tag);
+    }
+
+               function error($msg) {
+                       die("$msg in line ".xml_get_current_line_number($this->parser));
+               }
+} // end of class xml
+?>
\ No newline at end of file