/** * Shared implementation for parseFile() & parseContents(). * * @param array $tokens The result of a token_get_all() * @param Context $parseContext * @return Analysis */ protected function fromTokens($tokens, $parseContext) { $analyser = new Analyser(); $analysis = new Analysis(); reset($tokens); $token = ''; $imports = ['swg' => 'Swagger\\Annotations']; // Use @SWG\* for swagger annotations (unless overwritten by a use statement) $parseContext->uses = []; $classContext = $parseContext; // Use the parseContext until a classContext is created. $classDefinition = false; $comment = false; $line = 0; $lineOffset = $parseContext->line ?: 0; while ($token !== false) { $previousToken = $token; $token = $this->nextToken($tokens, $parseContext); if (is_array($token) === false) { // Ignore tokens like "{", "}", etc continue; } if ($token[0] === T_DOC_COMMENT) { if ($comment) { // 2 Doc-comments in succession? $analysis->addAnnotations($analyser->fromComment($comment, new Context(['line' => $line], $classContext))); } $comment = $token[1]; $line = $token[2] + $lineOffset; continue; } if ($token[0] === T_ABSTRACT) { $token = $this->nextToken($tokens, $parseContext); // Skip "abstract" keyword } if ($token[0] === T_CLASS) { // Doc-comment before a class? if (is_array($previousToken) && $previousToken[0] === T_DOUBLE_COLON) { //php 5.5 class name resolution (i.e. ClassName::class) continue; } $token = $this->nextToken($tokens, $parseContext); $classContext = new Context(['class' => $token[1], 'line' => $token[2]], $parseContext); if ($classDefinition) { $analysis->addClassDefinition($classDefinition); } $classDefinition = ['class' => $token[1], 'extends' => null, 'properties' => [], 'methods' => [], 'context' => $classContext]; // @todo detect end-of-class and reset $classContext $token = $this->nextToken($tokens, $parseContext); if ($token[0] === T_EXTENDS) { $classContext->extends = $this->parseNamespace($tokens, $token, $parseContext); $classDefinition['extends'] = $classContext->fullyQualifiedName($classContext->extends); } if ($comment) { $classContext->line = $line; $analysis->addAnnotations($analyser->fromComment($comment, $classContext)); $comment = false; continue; } } if ($token[0] == T_STATIC) { $token = $this->nextToken($tokens, $parseContext); if ($token[0] === T_VARIABLE) { // static property $propertyContext = new Context(['property' => substr($token[1], 1), 'static' => true, 'line' => $line], $classContext); if ($classDefinition) { $classDefinition['properties'][$propertyContext->property] = $propertyContext; } if ($comment) { $analysis->addAnnotations($analyser->fromComment($comment, $propertyContext)); $comment = false; } continue; } } if (in_array($token[0], [T_PRIVATE, T_PROTECTED, T_PUBLIC, T_VAR])) { // Scope $token = $this->nextToken($tokens, $parseContext); if ($token[0] == T_STATIC) { $token = $this->nextToken($tokens, $parseContext); } if ($token[0] === T_VARIABLE) { // instance property $propertyContext = new Context(['property' => substr($token[1], 1), 'line' => $line], $classContext); if ($classDefinition) { $classDefinition['properties'][$propertyContext->property] = $propertyContext; } if ($comment) { $analysis->addAnnotations($analyser->fromComment($comment, $propertyContext)); $comment = false; } } elseif ($token[0] === T_FUNCTION) { $token = $this->nextToken($tokens, $parseContext); if ($token[0] === T_STRING) { $methodContext = new Context(['method' => $token[1], 'line' => $line], $classContext); if ($classDefinition) { $classDefinition['methods'][$token[1]] = $methodContext; } if ($comment) { $analysis->addAnnotations($analyser->fromComment($comment, $methodContext)); $comment = false; } } } continue; } elseif ($token[0] === T_FUNCTION) { $token = $this->nextToken($tokens, $parseContext); if ($token[0] === T_STRING) { $methodContext = new Context(['method' => $token[1], 'line' => $line], $classContext); if ($classDefinition) { $classDefinition['methods'][$token[1]] = $methodContext; } if ($comment) { $analysis->addAnnotations($analyser->fromComment($comment, $methodContext)); $comment = false; } } } if (in_array($token[0], [T_NAMESPACE, T_USE]) === false) { // Skip "use" & "namespace" to prevent "never imported" warnings) // Not a doc-comment for a class, property or method? if ($comment) { $analysis->addAnnotations($analyser->fromComment($comment, new Context(['line' => $line], $classContext))); $comment = false; } } if ($token[0] === T_NAMESPACE) { $parseContext->namespace = $this->parseNamespace($tokens, $token, $parseContext); continue; } if ($token[0] === T_USE) { $statements = $this->parseUseStatement($tokens, $token, $parseContext); foreach ($statements as $alias => $target) { if ($target[0] === '\\') { $target = substr($target, 1); } $parseContext->uses[$alias] = $target; foreach (Analyser::$whitelist as $namespace) { if (strcasecmp(substr($target, 0, strlen($namespace)), $namespace) === 0) { $imports[strtolower($alias)] = $target; break; } } } $analyser->docParser->setImports($imports); continue; } } if ($comment) { // File ends with a T_DOC_COMMENT $analysis->addAnnotations($analyser->fromComment($comment, new Context(['line' => $line], $classContext))); } if ($classDefinition) { $analysis->addClassDefinition($classDefinition); } return $analysis; }
/** * * @param Analysis $analysis * @param Analyser $analyser * @param string $comment * @param Context $context */ private function analyseComment($analysis, $analyser, $comment, $context) { $analysis->addAnnotations($analyser->fromComment($comment, $context), $context); }