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 "asPhp(); } foreach (Gtk_Dumper::getPhpGtkClassNames() as $className) { $class = new GtkReflectionClass($className); echo $class->asPhp(); } } main();