protected function processMethod($class, $method) { $doc = new MethodDoc(); $doc->name = $method->getName(); $doc->loadSource($method); $doc->definedBy = $method->getDeclaringClass()->getName(); $doc->isAbstract = $method->isAbstract(); $doc->isFinal = $method->isFinal(); $doc->isProtected = $method->isProtected(); $doc->isStatic = $method->isStatic(); $doc->isInherited = $doc->definedBy !== $class->getName(); $doc->input = array(); foreach ($method->getParameters() as $param) { $p = new ParamDoc(); $p->name = $param->getName(); $p->isOptional = $param->isOptional(); if ($param->isDefaultValueAvailable()) { $p->defaultValue = $param->getDefaultValue(); } $p->isPassedByReference = $param->isPassedByReference(); $doc->input[] = $p; } reset($doc->input); $this->processComment($doc, $method->getDocComment()); $params = array(); foreach ($doc->input as $param) { $type = empty($param->type) ? '' : $this->getTypeUrl($param->type) . ' '; if ($param->isOptional) { $params[] = $type . ($param->isPassedByReference ? '&' : '') . '$' . $param->name . '=' . str_replace("\r", '', var_export($param->defaultValue, true)); } else { $params[] = $type . ($param->isPassedByReference ? '&' : '') . '$' . $param->name; } } $doc->signature = '{{' . $class->name . '::' . $doc->name . '|<b>' . $doc->name . '</b>}}(' . implode(', ', $params) . ')'; if ($doc->output !== null) { $doc->signature = $this->getTypeUrl($doc->output->type) . ' ' . $doc->signature; } else { $doc->signature = 'void ' . $doc->signature; } if (($modifier = implode(' ', Reflection::getModifierNames($method->getModifiers()))) !== '') { $doc->signature = $modifier . ' ' . $doc->signature; } return $doc; }
/** Add a method to this class. * * @param MethodDoc method */ function addMethod(MethodDoc $method) { if (isset($this->_methods[$method->name()])) { $phpdoctor = $this->_root->phpdoctor(); echo "\n"; $phpdoctor->warning('Found method ' . $method->name() . ' again, overwriting previous version'); } $this->_methods[$method->name()] = $method; }
/** * 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 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 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 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; }
/** * Writes method signaturw * * @param text.doclet.MethodDoc method * @param string base * @param io.streams.OutputStream */ protected function writeSignature(MethodDoc $method, $base, OutputStream $stream) { $return = $method->tags('return'); $params = $method->tags('param'); $stream->write(($return ? $this->typeLink($return[0]->type, $base) : 'void') . ' '); $stream->write('<a href="#' . $method->name() . '">' . $method->name() . '</a>('); for ($i = 0, $s = sizeof($params); $i < $s; $i++) { $stream->write($this->typeLink($params[$i]->type, $base) . ' ' . $params[$i]->parameter); $i < $s - 1 && $stream->write(', '); } $stream->write(')'); }