/**
  * Returns a method for a class by a given name
  *
  * @param   text.doclet.ClassDoc self
  * @param   string name
  * @return  text.doclet.MethodDoc
  * @throws  lang.ElementNotFoundException
  */
 public static function methodNamed(ClassDoc $self, $name)
 {
     foreach ($self->methods as $method) {
         if ($name === $method->name()) {
             return $method;
         }
     }
     raise('lang.ElementNotFoundException', 'No such method ' . $name . ' in ' . $self->name());
 }
Example #2
0
 /**
  * Parses a class file and returns a classdoc element
  *
  * @param   string classname fully qualified class name
  * @return  text.doclet.ClassDoc
  * @throws  lang.IllegalArgumentException if class could not be found or parsed
  */
 public function classNamed($classname)
 {
     static $cache = array();
     static $map = array('uses' => self::T_USES, 'define' => self::T_DEFINE);
     // Check cache
     if (isset($cache[$classname])) {
         return $cache[$classname];
     }
     // Check for php namespace - in this case, we have a builtin class. These
     // classes will not be documented for the moment.
     if ('php.' == substr($classname, 0, 4)) {
         return null;
     }
     // Find class
     if (!($loader = $this->findClass($classname))) {
         throw new \lang\IllegalArgumentException(sprintf('Could not find %s in %s', \xp::stringOf($classname), \xp::stringOf($this->sourcepath)));
     }
     // Tokenize contents
     $tokens = @token_get_all($loader->loadClassBytes($classname));
     if (!$tokens || T_OPEN_TAG !== $tokens[0][0]) {
         throw new \lang\IllegalArgumentException(sprintf('Could not parse "%s" from %s, first token: %s', $classname, \xp::stringOf($loader), \xp::stringOf($tokens[0])));
     }
     with($doc = new ClassDoc(), $doc->setLoader($loader), $doc->setRoot($this));
     $annotations = $comment = $package = null;
     $modifiers = array();
     $state = self::ST_INITIAL;
     for ($i = 0, $s = sizeof($tokens); $i < $s; $i++) {
         $t = $tokens[$i];
         if (is_array($t) && isset($map[$t[1]])) {
             $t[0] = $map[$t[1]];
         }
         switch ($state . $t[0]) {
             case self::ST_INITIAL . T_DOC_COMMENT:
             case self::ST_CLASS_BODY . T_DOC_COMMENT:
                 $comment = $t[1];
                 break;
             case self::ST_INITIAL . T_COMMENT:
             case self::ST_CLASS_BODY . T_COMMENT:
                 if (strncmp('#[@', $t[1], 3) == 0) {
                     $annotations = substr($t[1], 2);
                 } else {
                     if (strncmp('#', $t[1], 1) == 0) {
                         $annotations .= substr($t[1], 1);
                     }
                 }
                 if (']' == substr(rtrim($t[1]), -1)) {
                     $annotations = '[' . trim($annotations);
                 }
                 break;
             case self::ST_INITIAL . T_VARIABLE:
                 if ('$package' === $t[1]) {
                     // RFC #0037: $package= 'lang.reflect';
                     while (T_CONSTANT_ENCAPSED_STRING !== $tokens[$i][0] && $i < $s) {
                         $i++;
                     }
                     $package = trim($tokens[$i][1], '\'"');
                 }
                 break;
             case self::ST_INITIAL . self::T_USES:
                 $state = self::ST_USES;
                 break;
             case self::ST_USES . T_CONSTANT_ENCAPSED_STRING:
                 $cn = trim($t[1], '"\'');
                 if (!$this->findClass($cn)) {
                     throw new \lang\IllegalStateException('Could not find used class "' . $cn . '" for class ' . $classname . ' in ' . \xp::stringOf($this->sourcepath));
                 }
                 $doc->usedClasses->classes[] = $cn;
                 break;
             case self::ST_USES . ')':
                 $state = self::ST_INITIAL;
                 break;
             case self::ST_INITIAL . self::T_DEFINE:
                 $state = self::ST_DEFINE;
                 break;
             case self::ST_INITIAL . T_NAMESPACE:
                 $package = '';
                 $i += 2;
                 // Eat "namespace"
                 while (';' !== $tokens[$i][0] && $i < $s) {
                     $package .= trim($tokens[$i++][1]);
                 }
                 $package = strtr($package, '\\', '.');
                 break;
             case self::ST_DEFINE . T_CONSTANT_ENCAPSED_STRING:
                 $state = self::ST_DEFINE_VALUE;
                 $define = trim($t[1], '"\'');
                 break;
             case self::ST_DEFINE_VALUE . T_CONSTANT_ENCAPSED_STRING:
             case self::ST_DEFINE_VALUE . T_LNUMBER:
             case self::ST_DEFINE_VALUE . T_DNUMBER:
             case self::ST_DEFINE_VALUE . T_STRING:
                 $doc->constants[$define] = $t[1];
                 break;
             case self::ST_DEFINE_VALUE . ')':
                 $state = self::ST_INITIAL;
                 break;
             case self::ST_INITIAL . T_INTERFACE:
                 $doc->type = INTERFACE_CLASS;
                 // Fall-through intended
             // Fall-through intended
             case self::ST_INITIAL . T_CLASS:
                 while (T_STRING !== $tokens[$i][0] && $i < $s) {
                     $i++;
                 }
                 $name = $tokens[$i][1];
                 if ($package && substr(strtr($name, 'ยท', '.'), 0, strlen($package)) == $package) {
                     $name = substr($name, strlen($package) + 1);
                 }
                 $doc->name = $name;
                 $doc->qualifiedName = $classname;
                 $doc->rawComment = $comment;
                 $doc->annotations = $annotations;
                 $doc->modifiers = $modifiers;
                 $comment = $annotations = null;
                 $modifiers = array();
                 $state = self::ST_CLASS;
                 break;
             case self::ST_CLASS . T_EXTENDS:
                 while (T_STRING !== $tokens[$i][0] && $i < $s) {
                     $i++;
                 }
                 $doc->superclass = $this->classNamed($this->qualifyName($doc, $tokens[$i][1]));
                 break;
             case self::ST_CLASS . T_IMPLEMENTS:
                 $state = self::ST_IMPLEMENTS;
                 break;
             case self::ST_IMPLEMENTS . T_STRING:
                 $doc->interfaces->classes[] = $this->qualifyName($doc, $t[1]);
                 break;
             case self::ST_CLASS . '{':
             case self::ST_IMPLEMENTS . '{':
                 $state = self::ST_CLASS_BODY;
                 break;
             case self::ST_CLASS_BODY . T_VARIABLE:
                 $state = self::ST_CLASS_VAR;
                 // Fall-through intended
             // Fall-through intended
             case self::ST_CLASS_VAR . T_VARIABLE:
                 unset($field);
                 $field = new FieldDoc();
                 $field->declaring = $doc;
                 $field->name = $t[1];
                 $field->modifiers = $modifiers;
                 break;
             case self::ST_CLASS_VAR . '=':
                 $state = self::ST_VARIABLE_VALUE;
                 break;
             case self::ST_CLASS_VAR . ',':
                 $doc->fields[] = $field;
                 break;
             case self::ST_CLASS_VAR . ';':
                 $doc->fields[] = $field;
                 $state = self::ST_CLASS_BODY;
                 $modifiers = array();
                 break;
             case self::ST_VARIABLE_VALUE . T_CONSTANT_ENCAPSED_STRING:
             case self::ST_VARIABLE_VALUE . T_LNUMBER:
             case self::ST_VARIABLE_VALUE . T_DNUMBER:
             case self::ST_VARIABLE_VALUE . T_STRING:
                 $field->constantValue = $t[1];
                 $state = self::ST_CLASS_VAR;
                 break;
             case self::ST_VARIABLE_VALUE . T_ARRAY:
                 $brackets = 0;
                 $src = '';
                 do {
                     $t = $tokens[$i];
                     $src .= is_array($t) ? $t[1] : $t;
                     if ('(' == $t[0]) {
                         $brackets++;
                     } else {
                         if (')' == $t[0] and --$brackets <= 0) {
                             break;
                         }
                     }
                 } while (++$i < $s);
                 $field->constantValue = $src;
                 $state = self::ST_CLASS_VAR;
                 break;
                 // Before member declaration (e.g. public static $..., protected function ...)
             // Before member declaration (e.g. public static $..., protected function ...)
             case self::ST_CLASS_BODY . T_PUBLIC:
             case self::ST_CLASS_BODY . T_PRIVATE:
             case self::ST_CLASS_BODY . T_PROTECTED:
             case self::ST_CLASS_BODY . T_STATIC:
             case self::ST_CLASS_BODY . T_FINAL:
             case self::ST_CLASS_BODY . T_ABSTRACT:
                 // Before class declaration (e.g. abstract class ...)
             // Before class declaration (e.g. abstract class ...)
             case self::ST_INITIAL . T_FINAL:
             case self::ST_INITIAL . T_ABSTRACT:
                 $modifiers[$t[1]] = true;
                 break;
             case self::ST_CLASS_BODY . T_FUNCTION:
                 while (T_STRING !== $tokens[$i][0] && $i < $s) {
                     $i++;
                 }
                 with($method = new MethodDoc(), $method->setRoot($this));
                 $method->name = $tokens[$i][1];
                 $method->rawComment = $comment;
                 $method->annotations = $annotations;
                 $method->modifiers = $modifiers;
                 $method->declaring = $doc;
                 // Omit static initializer, it's not a real function
                 if ('__static' != $method->name) {
                     $doc->methods[] = $method;
                 }
                 $comment = $annotations = null;
                 $modifiers = array();
                 $state = self::ST_FUNCTION;
                 break;
             case self::ST_FUNCTION . '(':
                 $state = self::ST_FUNCTION_ARGUMENTS;
                 $argument = null;
                 break;
             case self::ST_FUNCTION_ARGUMENTS . T_VARIABLE:
                 $argument = $t[1];
                 break;
             case self::ST_FUNCTION_ARGUMENTS . ',':
                 $method->arguments[$argument] = null;
                 break;
             case self::ST_FUNCTION_ARGUMENTS . '=':
                 $state = self::ST_ARGUMENT_VALUE;
                 break;
             case self::ST_ARGUMENT_VALUE . T_CONSTANT_ENCAPSED_STRING:
             case self::ST_ARGUMENT_VALUE . T_LNUMBER:
             case self::ST_ARGUMENT_VALUE . T_DNUMBER:
             case self::ST_ARGUMENT_VALUE . T_STRING:
                 $method->arguments[$argument] = $t[1];
                 break;
             case self::ST_ARGUMENT_VALUE . T_ARRAY:
                 $brackets = 0;
                 $src = '';
                 do {
                     $t = $tokens[$i];
                     $src .= is_array($t) ? $t[1] : $t;
                     if ('(' == $t[0]) {
                         $brackets++;
                     } else {
                         if (')' == $t[0] and --$brackets <= 0) {
                             break;
                         }
                     }
                 } while (++$i < $s);
                 $method->arguments[$argument] = $src;
                 break;
             case self::ST_ARGUMENT_VALUE . ',':
                 $state = self::ST_FUNCTION_ARGUMENTS;
                 break;
             case self::ST_ARGUMENT_VALUE . ')':
                 $state = self::ST_FUNCTION;
                 break;
             case self::ST_FUNCTION_ARGUMENTS . ')':
                 $argument && ($method->arguments[$argument] = null);
                 $state = self::ST_FUNCTION;
                 break;
             case self::ST_FUNCTION . ';':
                 // Interface and abstract methods have no body
                 $state = self::ST_CLASS_BODY;
                 break;
             case self::ST_FUNCTION . '{':
                 $brackets = 0;
                 do {
                     $c = $tokens[$i][0];
                     if ('{' == $c) {
                         $brackets++;
                     } else {
                         if ('}' == $c and --$brackets <= 0) {
                             break;
                         }
                     }
                 } while (++$i < $s);
                 $state = self::ST_CLASS_BODY;
                 break;
             case self::ST_CLASS_BODY . '}':
                 $state = self::ST_INITIAL;
                 break;
         }
     }
     return $cache[$classname] = $doc;
 }