Index: lang/reflect/Routine.class.php =================================================================== --- lang/reflect/Routine.class.php (revision 5626) +++ lang/reflect/Routine.class.php (working copy) @@ -34,7 +34,7 @@ */ function __construct(&$ref, $name) { $this->_ref= is_object($ref) ? get_class($ref) : $ref; - $this->name= $name; + $this->name= strtolower($name); } /** @@ -127,8 +127,19 @@ */ function getReturnType() { if (!($details= XPClass::detailsForMethod($this->_ref, $this->name))) return NULL; - return $details[DETAIL_RETURNS]; + return ltrim($details[DETAIL_RETURNS], '&'); } + + /** + * Retrieve whether this method returns a reference + * + * @access public + * @return string + */ + function returnsReference() { + if (!($details= XPClass::detailsForMethod($this->_ref, $this->name))) return NULL; + return '&' == $details[DETAIL_RETURNS]{0}; + } /** * Retrieve exception names @@ -165,7 +176,7 @@ function &getDeclaringClass() { $class= $this->_ref; while ($details= XPClass::detailsForClass(xp::nameOf($class))) { - if (isset($details[$this->name])) return new XPClass($class); + if (isset($details[1][$this->name])) return new XPClass($class); $class= get_parent_class($class); } return xp::null(); Index: lang/reflect/Argument.class.php =================================================================== --- lang/reflect/Argument.class.php (revision 5626) +++ lang/reflect/Argument.class.php (working copy) @@ -50,10 +50,20 @@ * @return string */ function getType() { - return $this->type; + return ltrim($this->type, '&'); } /** + * Returns whether this argument is passed by reference + * + * @access public + * @return string + */ + function isPassedByReference() { + return '&' == $this->type{0}; + } + + /** * Retrieve whether this argument is optional * * @access public Index: lang/reflect/Field.class.php =================================================================== --- lang/reflect/Field.class.php (revision 0) +++ lang/reflect/Field.class.php (revision 0) @@ -0,0 +1,72 @@ +_ref= is_object($ref) ? get_class($ref) : $ref; + $this->name= $name; + $this->value= $value; + } + + /** + * Get field's name. + * + * @access public + * @return string + */ + function getName() { + return $this->name; + } + + /** + * Gets field type + * + * @access public + * @return string + */ + function getType() { + if ($details= XPClass::detailsForField($this->_ref, $this->name)) { + if (isset($details[DETAIL_ANNOTATIONS]['type'])) return $details[DETAIL_ANNOTATIONS]['type']; + } + return gettype($this->value); + } + + /** + * Returns the XPClass object representing the class or interface + * that declares the field represented by this Field object. + * + * @access public + * @return &lang.XPClass + */ + function &getDeclaringClass() { + $class= $this->_ref; + while ($details= XPClass::detailsForClass(xp::nameOf($class))) { + if (isset($details[0][$this->name])) return new XPClass($class); + $class= get_parent_class($class); + } + return xp::null(); + } + + } +?> Index: lang/XPClass.class.php =================================================================== --- lang/XPClass.class.php (revision 5626) +++ lang/XPClass.class.php (working copy) @@ -6,6 +6,7 @@ uses( 'lang.reflect.Method', + 'lang.reflect.Field', 'lang.reflect.Constructor' ); @@ -215,19 +216,52 @@ } /** - * Retrieve a list of all declared member variables + * Retrieve a list of all member variables * * @access public - * @return string[] member names + * @return lang.reflect.Field[] array of field objects */ function getFields() { - return (is_object($this->_objref) + $f= array(); + foreach ((is_object($this->_objref) ? get_object_vars($this->_objref) : get_class_vars($this->_objref) - ); + ) as $field => $value) { + if ('__id' == $field) continue; + $f[]= &new Field($this->_objref, $field, $value); + } + return $f; } /** + * Retrieve a field by a specified name. Returns NULL if the specified + * field does not exist + * + * @access public + * @param string name + * @return &lang.reflect.Field + */ + function &getField($name) { + if (!$this->hasField($name)) return NULL; + + return new Field($this->_objref, $name, $this->{$name}); + } + + /** + * Checks whether this class has a field named "$field" or not. + * + * @access public + * @param string field the fields's name + * @return bool TRUE if field exists + */ + function hasField($field) { + return '__id' == $field ? FALSE : array_key_exists($field, is_object($this->_objref) + ? get_object_vars($this->_objref) + : get_class_vars($this->_objref) + ); + } + + /** * Retrieve the parent class's class object. Returns NULL if there * is no parent class. * @@ -382,7 +416,7 @@ if (!$class) return NULL; // Border case if (isset($details[$class])) return $details[$class]; - $details[$class]= array(); + $details[$class]= array(array(), array()); $name= strtr($class, '.', DIRECTORY_SEPARATOR); $l= strlen($name); @@ -391,7 +425,8 @@ // Found the class, now get API documentation $annotations= array(); - $comment= NULL; + $comment= NULL; + $members= TRUE; $tokens= token_get_all(file_get_contents($file)); for ($i= 0, $s= sizeof($tokens); $i < $s; $i++) { switch ($tokens[$i][0]) { @@ -426,12 +461,24 @@ ); $annotations= array(); $comment= NULL; - break; - + break; + + case T_VARIABLE: + if (!$members) break; + + // Have a member variable + $name= substr($tokens[$i][1], 1); + $details[$class][0][$name]= array( + DETAIL_ANNOTATIONS => $annotations + ); + $annotations= array(); + break; + case T_FUNCTION: + $members= FALSE; while (T_STRING !== $tokens[$i][0]) $i++; $m= strtolower($tokens[$i][1]); - $details[$class][$m]= array( + $details[$class][1][$m]= array( DETAIL_MODIFIERS => 0, DETAIL_ARGUMENTS => array(), DETAIL_RETURNS => 'void', @@ -457,11 +504,11 @@ switch ($match[1]) { case 'access': case 'model': - $details[$class][$m][DETAIL_MODIFIERS] |= constant('MODIFIER_'.strtoupper($match[2])); + $details[$class][1][$m][DETAIL_MODIFIERS] |= constant('MODIFIER_'.strtoupper($match[2])); break; case 'param': - $details[$class][$m][DETAIL_ARGUMENTS][]= &new Argument( + $details[$class][1][$m][DETAIL_ARGUMENTS][]= &new Argument( isset($match[3]) ? $match[3] : 'param', $match[2], isset($match[4]), @@ -470,11 +517,11 @@ break; case 'return': - $details[$class][$m][DETAIL_RETURNS]= $match[2]; + $details[$class][1][$m][DETAIL_RETURNS]= $match[2]; break; case 'throws': - $details[$class][$m][DETAIL_THROWS][]= $match[2]; + $details[$class][1][$m][DETAIL_THROWS][]= $match[2]; break; } } @@ -494,7 +541,7 @@ } /** - * Retrieve details for a specified class and methid. Note: Results + * Retrieve details for a specified class and method. Note: Results * from this method are cached! * * @model static @@ -506,11 +553,30 @@ function detailsForMethod($class, $method) { $method= strtolower($method); while ($details= XPClass::detailsForClass(xp::nameOf($class))) { - if (isset($details[$method])) return $details[$method]; + if (isset($details[1][$method])) return $details[1][$method]; $class= get_parent_class($class); } return NULL; } + + /** + * Retrieve details for a specified class and field. Note: Results + * from this method are cached! + * + * @model static + * @access public + * @param string class unqualified class name + * @param string method + * @return array + */ + function detailsForField($class, $field) { + $field= strtolower($field); + while ($details= XPClass::detailsForClass(xp::nameOf($class))) { + if (isset($details[0][$field])) return $details[0][$field]; + $class= get_parent_class($class); + } + return NULL; + } /** * Returns the XPClass object associated with the class with the given