Skip to content

Instantly share code, notes, and snippets.

@mrprompt
Created June 7, 2013 03:26
Show Gist options
  • Save mrprompt/5726881 to your computer and use it in GitHub Desktop.
Save mrprompt/5726881 to your computer and use it in GitHub Desktop.
Geração do arquivo para auto complete do PHP-GTK+
<?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-17 09:58:19 marand Exp $
*/
error_reporting(E_ALL | E_STRICT);
define('GTK_DUMPER_VERSION', '$Id: Gtk_Dumper.php,v 1.1 2008-05-17 09:58:19 marand 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();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment