Browse Source

Version 1.1 is compatible with PHP-GTK 2.0 and 2.0.1

Frederic G. Marand 16 years ago
commit
60f6ec4cdb
1 changed files with 897 additions and 0 deletions
  1. 897 0
      Gtk_Dumper.php

+ 897 - 0
Gtk_Dumper.php

@@ -0,0 +1,897 @@
+<?php
+/**
+ * PHP-GTK Stub generator
+ *
+ * This program can be run on any system with PHP-GTK installed.
+ * It generates a skeleton file containing all classes present
+ * in the currently active PHP-GTK extension, with their
+ * constants, methods, and signatures thereof.
+ *
+ * This file can be installed on various IDEs to enable code
+ * completion when the IDE does not use the PHP-GTK interpreter
+ * itself.
+ *
+ * Installation on Eclipse PDT
+ * To enable code completion for PHP-GTK on Eclipse PDT:
+ * 1. create a "PHP-GTK Lookup" PHP project
+ * 2. run this file and save its output as a PHP file, like "php-reference.php"
+ * 3. place the generated file in a directory of its own, within the "PHP Lookup" project
+ * 4. When creating a new PHP Project, add the "PHP Lookup" project in the list of includes
+ *
+ * Similar steps should work in other IDEs
+ *
+ * Do NOT include/require it in your actual PHP source code:
+ * it would clash with the actual PHP-GTK symbols.
+ *
+ * This version has been tested on the following standard PGP-GTK2 builds:
+ * - 2.0.0-dev "leap day special"
+ * - 2.0.1 "you knew this was coming"
+ *
+ * @copyright Frederic G. MARAND
+ * @license CeCILL Version 2.0
+ * @link http://www.cecill.info/licences/Licence_CeCILL_V2-en.html
+ * @version $Id: Gtk_Dumper.php,v 1.1 2008-05-24 06:40:51 cvs Exp $
+ */
+
+error_reporting(E_ALL | E_STRICT);
+
+define('GTK_DUMPER_VERSION', '$Id: Gtk_Dumper.php,v 1.1 2008-05-24 06:40:51 cvs Exp $');
+
+/**
+ * Obtain the reflection info about the PHP-GTK extension
+ * @return string
+ */
+function getExtensionInfo()
+  {
+  $arExtensions = get_loaded_extensions();
+  if (!in_array('php-gtk', $arExtensions))
+    {
+    die('PHP-GTK not loaded');
+    }
+  $ret = "/**\n"
+   . " * PHP-GTK skeletons, generated from the extension using: \n"
+   . " * " . GTK_DUMPER_VERSION . PHP_EOL
+   . " * \n";
+
+  $ext = new ReflectionExtension('php-gtk');
+  $version = $ext->getVersion()
+    ? $ext->getVersion()
+    : 'No version info';
+  $ret .= sprintf
+    (
+    " * Name        : %s\n" .
+    " * Version     : %s\n" .
+    " * INI entries : %d\n",
+    $ext->getName(),
+    $version,
+    count($ext->getINIEntries())
+    );
+  foreach ($ext->getINIEntries() as $entry => $value)
+    {
+    $ret .= ' * - ' . $entry . ' = ' . $value . PHP_EOL;
+    }
+  $ret .= " */\n";
+  return $ret;
+  }
+
+/**
+ * internal use class to return the origin of a class, interface, method, object, or function
+ */
+class GtkOriginator
+  {
+  /**
+   * @param ReflectionFunctionAbstract $x
+   */
+  public function __construct($x)
+    {
+    $arMethodNames = get_class_methods($x);
+    if (!in_array('isInternal', $arMethodNames) or !in_array('isUserDefined', $arMethodNames))
+      {
+      throw new ReflectionException('GtkOriginator() takes classes implementing isInternal()/isUserDefined().  '
+        . get_class($x)
+        . print_r($arMethodNames, true));
+      }
+    $this->x = $x;
+    }
+
+  /**
+   * A consistency-checked readable form of the origin modifier
+   *
+   * @return string
+   */
+  public function getOrigin()
+    {
+    $mask = $this->x->isInternal() << 1 | $this->x->isUserDefined();
+    switch ($mask)
+      {
+      case 0: $ret = 'unknown_origin'; break;
+      case 1: $ret = 'user-defined'  ; break;
+      case 2: $ret = 'internal'      ; break;
+      case 3:
+        throw new ReflectionException("Inconsistent origin for $this->x->name().");
+        break;
+      // no possible other case
+      }
+
+    return $ret;
+    }
+  }
+
+/**
+ * The PHP-GTK 2.0 extension has some specific quirks:
+ * - only a limited subset of parameter attributes is used
+ * - optional parameters are visible, but not their default values
+ * - some parameters have invalid names: the first parameter in
+ *   GtkColorMap::alloc_color is "$color OR red"
+ * - some parameters are named as $... to denote extended parameter lists
+ *
+ * Number of parameters with given attribute combinations in version 2.0.0.0 of php_gtk.dll:
+ * - none: 21098
+ * - allowsNull: 2 (GtkIconView::set_drag_dest_item($pos) and Gdk::event_request_motions($event))
+ * - isOptional: 2412
+ * - valid class name: 132
+ * - valid class name + allowsNull: 5888
+ * - valid class name + isOptional: 1 (GtkIconView::__construct(GtkTreeModel $model))
+ * - valid class name + isOptional + allowsNull: 76
+ */
+class GtkReflectionParameter extends ReflectionParameter
+  {
+  /**
+   * Format the parameter as a valid PHP declaration.
+   * The means the information is not necessarily complete
+   * in comparison with the one available from __toString()
+   *
+   * The return value is an assoc array with two keys:
+   * - valid: if true, this is a valid PHP parameter declaration; otherwise it is a PHP comment containing a declaration
+   * - string: the actual result
+   *
+   * "Invalid" results typically happen when a parameter is optional, but
+   * without a default value. In that case, the comment contains the
+   * parameter declaration within a comment, meaning it cannot be inserted
+   * as such in a PHP function declaration.
+   *
+   * @return array
+   */
+  public function asPhp()
+    {
+    $ret = '';
+
+    $bitDA = $this->isDefaultValueAvailable() ? 1 : 0;
+    $bitAN = $this->allowsNull()              ? 1 : 0;
+    $bitOP = $this->isOptional()              ? 1 : 0;
+    /**
+     * An exception can occur on getClass():
+     * GtkIconView::set_drag_dest_item($pos)
+     * getting its class throws a ReflectionException:
+     * "Class GtkIconViewDropPosition does not exist"
+     * so we ignore it.
+     */
+    try
+      {
+      $typeName = $this->getClass();
+      }
+    catch (ReflectionException $e)
+      {
+      $typeName = NULL;
+      }
+    $bitCN  = empty($typeName)  ? 0 : 1;
+    $bitAR  = $this->isArray()  ? 1 : 0;
+    $bitDN  = $bitDA
+      ? (($this->getDefaultValue() === NULL) ? 1: 0)
+      : 0;
+    $mask= $bitDA + ($bitAN << 1) + ($bitOP << 2) + ($bitCN << 3) + ($bitAR << 4) + ($bitDN << 5);
+
+    /**
+     * @todo sanitize name
+     */
+    $name = $this->name;
+    $name = str_replace(' ', '_', $name);
+    // echo "// $name\n";
+
+    $ret = array('valid' => TRUE);
+    switch ($mask)
+      {
+      case 0: // plain
+        $ret['string'] = '$' . $name;
+        break;
+      case 2: // isDefaultAvailable
+        try
+          {
+          $def = var_export($this->getDefaultValue(), TRUE);
+          }
+        catch (ReflectionException $e)
+          {
+          $def = 'NULL /* Exception: ' . $e->getMessage() . ' */';
+          }
+        $ret['string'] = "\$$name = " . $def;
+        break;
+      case 4: // isOptional
+        $ret['string'] = "/* \$$name */";
+        $ret['valid'] = FALSE;
+        break;
+      case 8: // valid class name
+        $ret['string'] = $this->getClass()->name . ' $' . $name;
+        break;
+      case 10: // valid class name | allowsNull
+        $ret['string'] = $this->getClass()->name . ' $' . $name . " /* or NULL */";
+        break;
+      case 12: // valid class name | isOptional
+        $ret['string'] = '/* ' . $this->getClass()->name . ' $' . $name . ' */';
+        $ret['valid'] = FALSE;
+      case 14: // valid class name | isOptional | allowsNull
+        $ret['string'] = $this->getClass()->name . ' $' . $name . ' = NULL';
+        break;
+      case 16: // array
+        $ret['string'] = 'array $' . $name;
+        break;
+      default:
+        throw new ReflectionException("Unhandled combination of attributes " . sprintf('0x%02X', $mask) . " for parameter $name.");
+        break;
+      }
+    return $ret;
+    }
+  }
+
+/**
+ * Extends the ReflectionMethod to enable generation of PHP source code
+ * instead of pseudo-readable code as in __toString
+ *
+ */
+class GtkReflectionMethod extends ReflectionMethod
+  {
+  /**
+   * PHP-GTK2 includes some problematic method names, which clash
+   * with reserver words in PHP (foreach, unset)
+   *
+   * @param string $name
+   * @return string
+   */
+  protected function fixName($name)
+    {
+    /**
+     * Fix PHP-GTK 2.0.0.0 special names
+     */
+    switch ($name)
+      {
+      case 'foreach':
+        $ret = 'foreach_method';
+        break;
+      case 'unset':
+        $ret = 'unset_method';
+        break;
+      default:
+        $ret = $name;
+        break;
+      }
+    return $ret;
+    }
+
+  /**
+   * Some stats from PHP-GTK 2.0.0.0:
+   *
+   * - 0x0100 plain: 29443
+   * - 0x0101 static: 2539
+   * - 0x0104 public final: 24
+   * - 0x0108 public | undocumented 0x0008: 479
+   * - 0x2100 public | undocumented 0x2000: 243 (all of them constructors)
+   * - 0x2400 private | undocumented 0x2000: 2 (both of them constructors)
+   * - 0x8404 private final | undocumented 0x8000: 4 (PhpGtk*Exception::__clone())
+   *
+   * @return string
+   */
+  public function asPhp()
+    {
+    $ret = '  ';
+
+    /**
+     * built a more complete attribute list tant getModifiers()
+     */
+    $bitST = $this->isStatic()       ? ReflectionMethod::IS_STATIC   : 0; // 0x0001
+    $bitAB = $this->isAbstract()     ? ReflectionMethod::IS_ABSTRACT : 0; // 0x0002
+    $bitFI = $this->isFinal()        ? ReflectionMethod::IS_FINAL    : 0; // 0x0004
+    $bitPU = $this->isPublic()       ? ReflectionMethod::IS_PUBLIC   : 0; // 0x0100
+    $bitPO = $this->isProtected()    ? ReflectionMethod::IS_PROTECTED: 0; // 0x0200
+    $bitPV = $this->isPrivate()      ? ReflectionMethod::IS_PRIVATE  : 0; // 0x0400
+    $bitCO = $this->isConstructor()  ? 0x00004000                    : 0; // no visible conflict
+    $bitDE = $this->isDeprecated()   ? ReflectionFunction::IS_DEPRECATED: 0; // 0x00040000
+    $bitDS = $this->isDestructor()   ? 0x10000000                    : 0; // no visible conflict
+    /**
+     * PHP-GTK2 does not fill in these elements, so we do not use them:
+     *
+     'file          ' . $this->getFileName()
+     'startLine     ' . $this->getStartLine()
+     'endLine       ' . $this->getEndLine()
+     'docComment    ' . $this->getDocComment()
+     'static Vars   ' . implode(', ', $this->getStaticVariables())
+     */
+
+    $mask = $bitST | $bitAB | $bitFI | $bitPU | $bitPO | $bitPV | $bitCO | $bitDE | $bitDS
+      | $this->getModifiers();
+
+    if ($bitCO and $this->name != '__construct')
+      {
+      throw ReflectionException("Incorrectly named constructor $this->name.");
+      }
+    if ($bitDS and $this->name != '__destruct')
+      {
+      throw ReflectionException("Incorrectly named destructor $this->name.");
+      }
+
+    $name = $this->fixName($this->name);
+    $isInterface = $this->getDeclaringClass()->isInterface();
+    if ($isInterface)
+      {
+      if (!($mask & self::IS_ABSTRACT))
+        {
+        throw("Non-abstract method $name in interface declaration.");
+        }
+      $mask &= !(self::IS_ABSTRACT // implicit in interfaces
+        | self::IS_PUBLIC          // implicit in interfaces
+        | self::IS_PROTECTED       // forbidden in interfaces
+        | self::IS_PRIVATE         // forbidden in interfaces
+        );
+      }
+    $origin = new GtkOriginator($this);
+    $origin = $origin->getOrigin();
+
+    switch ($mask)
+      {
+      case 0x0000:
+        $ret .= "/* $origin */ function ";
+        break;
+      case 0x0100:
+        $ret .= "public /* $origin */ function ";
+        break;
+      case 0x0101:
+        $ret .= "static public /* $origin */ function ";
+        break;
+      case 0x0102:
+        $ret .= "abstract public /* $origin */ function ";
+        break;
+      case 0x0103:
+        $ret .= "static abstract public /* $origin */ function ";
+        break;
+      case 0x0104:
+        $ret .= "final public /* $origin */ function ";
+        break;
+      case 0x0108:
+        $ret .= "public /* 0x0008 $origin */ function ";
+        break;
+      case 0x2102:
+        $ret .= "public /* 0x2000 $origin */ function ";
+        break;
+      case 0x6100:
+        $ret .= "public /* 0x6000 $origin */ function ";
+        break;
+      case 0x6400:
+        $ret .= "private /* 0x6000 $origin */ function ";
+        break;
+        case 0x8404:
+        $ret .= "final private /* 0x8000 $origin */ function ";
+        break;
+      default:
+        throw new ReflectionException("Unhandled method attribute set " . sprintf('0x%08x', $mask)
+          . " for " . $this->getDeclaringClass()->name . '::' . $this->name . '().');
+        break;
+      }
+    /**
+     * Not used in PHP-GTK 2.0.0.0
+     */
+    if ($this->returnsReference())
+      {
+      $ret .= '&';
+      }
+
+    $ret .= $name . " // "
+      . $this->getNumberOfParameters()         . ' parameters, '
+      . $this->getNumberOfRequiredParameters() . " required."
+      . "\n    (\n    ";
+
+    $arParamStrings = array();
+    $arParamComments = array();
+    foreach ($this->getParameters() as $param)
+      {
+      $paramVector = $param->asPhp();
+      if ($paramVector['valid'])
+        {
+        $arParamStrings[] = $paramVector['string'];
+        }
+      else
+        {
+        $arParamComments[] = $paramVector['string'];
+        }
+      }
+
+    // @todo tidy formatting: on methods without parameters, one gets a useless empty line
+    $paramString = implode(",\n    ", $arParamStrings);
+    if (count($arParamComments))
+      {
+      $paramString .= "\n    " . implode("\n    ", $arParamComments);
+      }
+    $ret .= $paramString
+      . "\n    )";
+
+    $ret .= $isInterface ? ";\n\n" : " {}\n\n";
+    return $ret;
+    }
+
+  /**
+   * Convert the ReflectionParameter array to a GtkReflectionParameter array
+   *
+   * @return GtkReflectionParameter[]
+   */
+  function getParameters()
+    {
+    $arParams1 = parent::getParameters();
+    $arParams2 = array();
+    foreach($arParams1 as $param)
+      {
+      $pos = $param->getPosition();
+      $arParams2[] = new GtkReflectionParameter(array($this->class, $this->name), $pos);
+      }
+    unset($arParams1);
+    return $arParams2;
+    }
+
+  }
+
+/**
+ * An extension to the ReflectionClass, able to output
+ * reflection information about an interface either as
+ * structured content or PHP source code
+ */
+class GtkReflectionInterface extends GtkReflectionClass
+  {
+  /**
+   * Make sure the class/interface information matches
+   * the actual class
+   * @throws ReflectionException
+   * @return void
+   */
+  protected function getHeader()
+    {
+    /**
+     * Consistency check
+     */
+    if (!$this->isInterface())
+      {
+      throw new ReflectionException("Interface $this->name is listed as being a plain class.");
+      }
+
+    $ret = $this->getModifiersString()
+      . " interface $this->name";
+    return $ret;
+    }
+  }
+
+/**
+ * Extend the ReflectionClass to support PHP rendering
+ * There are no doccomments in PHP-GTK classes, so no
+ * method around $this->getDocComment()
+ * Same for interface $this->getProperties()
+ */
+class GtkReflectionClass extends ReflectionClass
+  {
+  /**
+   * @return string
+   */
+  protected function getModifiersString()
+    {
+    $ret = array();
+    $mask = $this->getModifiers();
+    if ($mask & self::IS_EXPLICIT_ABSTRACT) { $ret[] = 'abstract';           }
+    if ($mask & self::IS_FINAL)             { $ret[] = 'final';              }
+    if ($mask & self::IS_IMPLICIT_ABSTRACT) { $ret[] = '/* abstract */';     }
+    if ($this->isInstantiable())            { $ret[] = '/* instantiable */'; }
+    if ($this->isIterateable())             { $ret[] = '/* iterateable */';  }
+
+    $origin = new GtkOriginator($this);
+    $origin = $origin->getOrigin();
+    $ret[] = "/* $origin */";
+
+    $ret = implode(' ', $ret);
+    $ret = str_replace(' */ /* ', ' ', $ret);
+    return $ret;
+    }
+
+  /**
+   * Make sure the class/interface information matches
+   * the actual class
+   * @throws ReflectionException
+   * @return void
+   */
+  protected function getHeader()
+    {
+    /**
+     * Consistency check
+     */
+    if ($this->isInterface())
+      {
+      throw new ReflectionException("Class $this->name is listed as being an interface.");
+      }
+
+    $ret = $this->getModifiersString()
+      . " class $this->name";
+    return $ret;
+    }
+
+  /**
+   * return the inherited constants
+   */
+  protected function getInheritedConstants()
+    {
+    $ret = array();
+    $arAncestry = Gtk_Dumper::getClassAncestry($this->name);
+    array_pop($arAncestry); // remove current class
+    foreach($arAncestry as $ancestorName)
+      {
+      $rAncestor = new ReflectionClass($ancestorName);
+      $arAncestorConstants = $rAncestor->getConstants();
+      $ret = array_merge($ret,  $arAncestorConstants);
+      unset($rAncestor);
+      }
+    return $ret;
+    }
+
+  /**
+   * return the "constant" clauses
+   * @return string
+   */
+  protected function getConstantsString()
+    {
+    $arConstants = $this->getConstants();
+    $ret = '';
+
+    $arInheritedConstants = $this->getInheritedConstants();
+    foreach ($arConstants as $name => $value)
+      {
+      if (array_key_exists($name, $arInheritedConstants) and ($arInheritedConstants[$name] == $value))
+        {
+        continue; // skip constant: it has not changed from the parent class
+        }
+      /**
+       * These reserved words are used for Gdk blit modes
+       */
+      if (in_array($name, array('XOR', 'OR', 'AND')))
+        {
+        $comment = "// Actual name is $name, which is reserved in PHP";
+        $name = "${name}_BLIT"; //
+        }
+      else
+        {
+        $comment = NULL;
+        }
+
+      if (is_string($value))
+        {
+        $value = "'$value'";
+        }
+      $ret .= "  const $name = $value"
+        . (empty($comment) ? ";\n" : "; $comment\n");
+      }
+    $ret = (empty($ret) and !$this->isInterface()) // Interfaces don't have constants anyway
+      ? "  // No constants\n"
+      : $ret;
+    return $ret;
+    }
+
+
+  /**
+   * Format the class as a valid PHP declaration.
+   * The means the information is not necessarily complete
+   * in comparison with the one available from __toString()
+   *
+   * @todo tidy up vertical spacing
+   * @return string
+   */
+  public function asPhp()
+    {
+    // 1. the "class" line
+    $ret = $this->getHeader() ;
+
+    // 2. the parenting
+    $parent = $this->getParentClass();
+    if ($parent)
+      {
+      $ret .= " extends " . $parent->name;
+      }
+
+    // 3. the interfaces
+    $arInterfaceNames1 = $this->getInterfaceNames();
+    $arInterfaceNames2 = array();
+    foreach ($arInterfaceNames1 as $interface)
+      {
+      // @link http://www.php.net/~helly/php/ext/spl/interfaceTraversable.html
+      if ($interface == 'Traversable')
+        {
+        $ret .= "  // Traversable cannot be implement in userland PHP\n"
+             .  "  // see http://www.php.net/~helly/php/ext/spl/interfaceTraversable.html\n";
+        continue;
+        }
+      $arInterfaceNames2[] = $interface;
+      }
+
+    $ret .= empty($arInterfaceNames2)
+      ? NULL
+      : "\n  implements "
+        . implode(', ', $arInterfaceNames2);
+    $ret .= "\n  {\n";
+
+    // 4. the constants
+    $constantStrings = $this->getConstantsString();
+    if (!empty($constantStrings))
+      {
+      $ret .= $constantStrings . PHP_EOL;
+      }
+
+    // 5. the methods
+    foreach ($this->getMethods() as $method)
+      {
+      if ($method->getDeclaringClass()->name == $this->name)
+        {
+        $ret .= $method->asPhp();
+        }
+      }
+    $ret .= "  }\n";
+
+    return $ret;
+    }
+
+  /**
+   * Convert the ReflectionMethod array to a GtkReflectionMethod array
+   *
+   * @return GtkReflectionMethod[]
+   */
+  function getMethods($filter = NULL)
+    {
+    $arMethods1 = parent::getMethods();
+    $arMethods2 = array();
+    foreach($arMethods1 as $method)
+      {
+      $arMethods2[] = new GtkReflectionMethod($this->name, $method->name);
+      }
+    unset($arMethods1);
+    return $arMethods2;
+    }
+  }
+
+/**
+ * Static methods allow lists of classes and interfaces
+ * to be obtained, from which an exhaustive dump of the
+ * classes and interfaces in an extension can be obtained
+ * using the normal methods of the class.
+ */
+class Gtk_Dumper
+  {
+   /**
+   * The Reflection class for the class under examination
+   * @var ReflectionClass
+   */
+  protected $class;
+
+  /**
+   * The name of the class under examination
+   * This is a shortcut to avoid constant reuse
+   * of ReflectionClass::getName()
+   * @var string
+   */
+  protected $name;
+
+  /**
+   * @param string $name
+   */
+  public function __construct($name)
+    {
+    $this->name = $name;
+    $this->class = new ReflectionClass($name);
+    }
+
+  /**
+   * Return the hierarchy of parents to a class as an array starting
+   * at the root class
+   *
+   * @param string $className
+   * @return array
+   */
+  static public function getClassAncestry($className)
+    {
+    $ret = array();
+    $class = new ReflectionClass($className);
+    do
+      {
+      array_unshift($ret, $class->getName());
+      $class = $class->getParentClass();
+      } while($class);
+    return $ret;
+    }
+
+    /**
+   * List the classes in PHP-GTK
+   *
+   * Regrettably, the Reflection information on PHP-GTK2 classes
+   * is incomplete: it does not return the extension information on
+   * ReflectionClass::getExtension() and ReflectionClass::getExtensionName()
+   * so we have to use hard-coded information
+   *
+   * @return array
+   */
+  static public function getPhpGtkClassNames()
+    {
+    static $phpGtkRoots = array
+      (
+      /**
+       * These are the PHP-GTK 2 root classes
+       */
+      'Atk',
+      'GBoxed',
+      'Gdk',
+      'GdkAtom',
+      'Glade',
+      'GObject',
+      'GParamSpec',
+      'GPointer',
+      'GType',
+      'Gtk',
+      'GtkAccessible',
+      'GtkAtom',
+      'GtkTreeModelRow',
+      'GtkTreeModelRowIterator',
+      'Pango',
+
+      /**
+       * This one has an ancestor outside the extension
+       */
+      'PhpGtkException',
+      );
+
+    $arClassNames = get_declared_classes();
+    $ret = array();
+    foreach ($arClassNames as $className)
+      {
+      $rclass = new ReflectionClass($className);
+      $extName = $rclass->getExtensionName();
+      /**
+       * The PHP-GTK extension does not define a value for getExtensionName()
+       * so we can save time, ignore classes known to come from other extensions
+       */
+      if (!empty($extName))
+        {
+        continue;
+        }
+
+      /**
+       * we only want non-interface classes in this list
+       */
+      if ($rclass->isInterface())
+        {
+        continue;
+        }
+
+      /**
+       * We can't just use ancestry roots, because PhpGtkException is not a root,
+       * and other similar cases might arise in later versions.
+       */
+      if (count(array_intersect(self::getClassAncestry($className), $phpGtkRoots)))
+        {
+        $ret[] = $className;
+        }
+      }
+
+    return $ret;
+    }
+
+  /**
+   * List the interfaces in PHP-GTK
+   *
+   * Regrettably, the Reflection information on PHP-GTK2 classes
+   * is incomplete: it does not return the extension information on
+   * ReflectionClass::getExtension() and ReflectionClass::getExtensionName()
+   * so we have to use hard-coded information
+   *
+   * @return array
+   */
+  static public function getPhpGtkInterfaceNames()
+    {
+    $arInterfaceNames = get_declared_interfaces();
+    $ret = array();
+    foreach($arInterfaceNames as $name)
+      {
+      if (strpos($name, 'Gtk') === 0) // we only want interfaces with a name starting by "Gtk"
+        {
+        $ret[] = $name;
+        }
+      }
+    return $ret;
+    }
+
+  /**
+   * return the methods clause
+   *
+   * if name is not set, return all the methods, otherwise
+   * just return the info for the designated method
+   *
+   * @param string $name
+   * @return string
+   */
+  function getClassMethods($name = NULL)
+    {
+    $ret = '';
+    $arMethods = empty($name)
+      ? $this->class->getMethods()
+      : array('name' => $name);
+
+    foreach ($arMethods as $oMethod)
+      {
+      $method = $this->class->getMethod($oMethod->name);
+      $modifiers = Reflection::getModifierNames($method->getModifiers());
+      $dc = $method->getDeclaringClass();
+      if ($dc->name != $this->name)
+        {
+        continue; // skip inherited classes
+        }
+      $modifiers = implode (' ', $modifiers) . ' ';
+
+      $arParams = $method->getParameters();
+      $arParamStrings = array();
+      foreach ($arParams as $oParam)
+        {
+        if ($oParam->isOptional())
+          continue;
+        // print_r($oParam);
+        $name = str_replace(' ', '_', $oParam->name);
+
+        $isArray    = $oParam->isArray();
+        $allowsNull = $oParam->allowsNull();
+        $isRef      = $oParam->isPassedByReference();
+        $isOptional = $oParam->isOptional();
+        $hasDefault = $oParam->isDefaultValueAvailable();
+        $default    = $hasDefault ? $oParam->getDefaultValue() : NULL;
+        $s  = $isArray ? 'array ' : NULL ;
+        $s .= $isRef ? '&' : '';
+        $s .= "$$name";
+        $s .= $isOptional ? '/* optional */' : '';
+        $s .= $hasDefault ? ' = ' . $default : NULL;
+        $arParamStrings[] = $s;
+        // echo $s . PHP_EOL;
+        }
+
+      switch ($oMethod->name)
+        {
+        case 'foreach':
+          $methodName = 'foreach_method';
+          break;
+        case 'unset':
+          $methodName = 'unset_method';
+          break;
+        default:
+          $methodName = $oMethod->name;
+        }
+      $params = '(' . (count($arParamStrings) ? implode(', ', $arParamStrings) : NULL) . ')';
+      $ret .= '  ' . $modifiers . 'function ' . $methodName . $params . " {}\n";
+      }
+    return $ret;
+    }
+  }
+
+/**
+ * Global code
+ */
+function main()
+  {
+  echo "<?php\n";
+  echo getExtensionInfo();
+
+  foreach (Gtk_Dumper::getPhpGtkInterfaceNames() as $interfaceName)
+    {
+    $interface = new GtkReflectionInterface($interfaceName);
+    echo $interface->asPhp();
+    }
+
+  foreach (Gtk_Dumper::getPhpGtkClassNames() as $className)
+    {
+    $class = new GtkReflectionClass($className);
+    echo $class->asPhp();
+    }
+  }
+
+main();