/** * @param Context $context * @return boolean|null */ public function compile(Context $context) { $this->compiled = true; $context->scopePointer = $this->getPointer(); if ($this->statement->getDocComment() === null) { $context->notice('missing-docblock', sprintf('Missing docblock for %s() method', $this->name), $this->statement); } /** * It's not needed to compile empty method via it's abstract */ if ($this->isAbstract()) { /** @var ClassDefinition $scope */ $scope = $context->scope; if (!$scope->isAbstract()) { $context->notice('not-abstract-class-with-abstract-method', 'Class must be an abstract', $this->statement); } return true; } if (count($this->statement->stmts) == 0) { return $context->notice('not-implemented-method', sprintf('Method %s() is not implemented', $this->name), $this->statement); } if (count($this->statement->params) > 0) { /** @var Node\Param $parameter */ foreach ($this->statement->params as $parameter) { $context->addSymbol($parameter->name); } } foreach ($this->statement->stmts as $st) { \PHPSA\nodeVisitorFactory($st, $context); } }
private function enterClassMethod(ClassMethod $node) { $methodName = $node->name; if ($this->isGetterOrSetter($methodName) !== true && $this->isNeitherConstructorNorDestructor($methodName)) { $this->methods[$methodName] = $node->isPublic(); } }
/** * Parses ClassMethod node to MethodData * * @return MethodData */ public function parse(ClassMethod $node) { $method = new MethodData($node->name); $method->startLine = $node->getAttribute("startLine"); $method->endLine = $node->getAttribute("endLine"); $method->setType($node->type); $comments = $node->getAttribute("comments"); if (is_array($comments)) { /** @var Comment */ $comment = $this->commentParser->parse($comments[count($comments) - 1]->getText()); if ($comment->isInheritDoc()) { $method->doc = Comment::INHERIT_MARK; } else { $method->doc = $comment->getDoc(); $method->return = $comment->getReturn(); foreach ($comment->getVars() as $var) { if ($var instanceof MethodParam) { $method->addParam($var); } } } } foreach ($node->params as $child) { if ($child instanceof Param) { $method->addParam($this->parseMethodArgument($child)); } } return $method; }
protected function enterPublicMethod($fqcn, Stmt\ClassMethod $node) { $methodName = $node->name; $index = "{$fqcn}::{$methodName}"; // create implemenation node // if not abstract we add the vertex for the implementation if (!$node->isAbstract()) { $this->pushImplementation($node, $index); } // push param for implementation, these parameters will be connected // to copy-pasted signature (see below) foreach ($node->params as $order => $aParam) { $this->pushParameter($index, $order); } // copy paste this signature in every class which use this current trait // Anyway we check if there is no other parent which declaring first this method $traitUser = $this->getReflectionContext()->getClassesUsingTraitForDeclaringMethod($fqcn, $methodName); foreach ($traitUser as $classname) { // we copy-paste the signature declaration in the class which using the current trait $index = $classname . '::' . $methodName; if (!$this->getGraphContext()->existsVertex('method', $index)) { $v = new MethodVertex($index); $this->getGraph()->addVertex($v); $this->getGraphContext()->indicesVertex('method', $index, $v); } // we do not copy-paste the parameters, there will be connected to original parameters from trait (see above) } }
/** * @param ClassMethod $node * * @return NULL|\phpDocumentor\Reflection\DocBlock */ private function nodeToDocBlock(ClassMethod $node) { $attribute = $node->getAttributes(); if (isset($attribute['comments']) === false || isset($attribute['comments'][0]) === false) { return; } $docBlock = $attribute['comments'][0]->getText(); return new DocBlock($docBlock); }
public function testItContainsAMessageWhenReturnTypeChanged() { $test = new ReturnTypeChanged(); $old = new ClassMethod('foo'); $old->returnType = 'void if seal is broken'; $class = new Class_('the_class'); $class->namespacedName = $class->name; $old->setAttribute('parent', $class); $test->handle($old, new ClassMethod('foo')); $this->assertContains('changed return type', $test->lastException->getMessage()); }
public function testItIsFalseWhenNothingChanged() { $test = new BodyChanged(); $func = new ClassMethod('some_method'); $class = new Class_('the_class'); $class->namespacedName = $class->name; $func->setAttribute('parent', $class); $old = $func; $new = $func; $this->assertFalse($test->handle($func, $old, $new)); }
/** * @param ClassMethod $node * @param Doc $docComment */ private function reapplyModifiedNode(ClassMethod $node, Doc $docComment) { $attributes = $node->getAttributes(); $comments = isset($attributes['comments']) ? $attributes['comments'] : []; $lastComment = count($comments) ? $comments[count($comments) - 1] : null; if (!$lastComment instanceof Doc) { $comments[] = $docComment; } else { $comments[count($comments) - 1] = $docComment; } $node->setAttribute('comments', $comments); }
public function testItReturnTrueWhenReturnTypeRemoved() { $test = new ReturnTypeRemoved(); $old = new ClassMethod('foo'); $old->returnType = 'void if seal is broken'; $class = new Class_('the_class'); $class->namespacedName = $class->name; $old->setAttribute('parent', $class); $new = new ClassMethod('foo'); $this->assertTrue('' == (string) $new->getReturnType()); $this->assertTrue($test->handle($old, $new)); }
public function visitMethod(ClassMethod $node) { $m = new PhpMethod($node->name); $m->setAbstract($node->isAbstract()); $m->setFinal($node->isFinal()); $m->setVisibility($this->getVisibility($node)); $m->setStatic($node->isStatic()); $m->setReferenceReturned($node->returnsByRef()); // docblock if (($doc = $node->getDocComment()) !== null) { $m->setDocblock($doc->getReformattedText()); $docblock = $m->getDocblock(); $m->setDescription($docblock->getShortDescription()); $m->setLongDescription($docblock->getLongDescription()); } // params $params = $m->getDocblock()->getTags('param'); foreach ($node->params as $param) { /* @var $param Param */ $p = new PhpParameter(); $p->setName($param->name); $p->setPassedByReference($param->byRef); if (is_string($param->type)) { $p->setType($param->type); } else { if ($param->type instanceof Name) { $p->setType(implode('\\', $param->type->parts)); } } $this->parseValue($p, $param); $tag = $params->find($p, function (ParamTag $t, $p) { return $t->getVariable() == '$' . $p->getName(); }); if ($tag !== null) { $p->setType($tag->getType(), $tag->getDescription()); } $m->addParameter($p); } // return type and description $returns = $m->getDocblock()->getTags('return'); if ($returns->size() > 0) { $return = $returns->get(0); $m->setType($return->getType(), $return->getDescription()); } // body $stmts = $node->getStmts(); if (is_array($stmts) && count($stmts)) { $prettyPrinter = new PrettyPrinter(); $m->setBody($prettyPrinter->prettyPrint($stmts)); } $this->struct->setMethod($m); }
private function enterClassMethod(ClassMethod $node) { if ($node->isPublic()) { $methodName = $node->name; if ($this->isGetterOrSetter($methodName)) { $correspondingAttribute = strtolower(substr($methodName, 3)); if (!isset($this->publicMethods[$correspondingAttribute])) { $this->publicMethods[$correspondingAttribute] = array(); } $this->publicMethods[$correspondingAttribute][] = $methodName; } } }
static function getMethodAccessLevel(ClassMethod $level) { if ($level->isPublic()) { return "public"; } if ($level->isPrivate()) { return "private"; } if ($level->isProtected()) { return "protected"; } trigger_error("Impossible"); }
/** * @param Node\Stmt\ClassMethod $node * @param string $text * * @return string */ public function addParams(Node\Stmt\ClassMethod $node, $text) { $docBlock = new Docblock($text); foreach ($node->getParams() as $param) { $type = $this->getFullyQualifiedParamType($param); if (null === $type) { continue; } $docBlock->appendTag(new ParamTag((string) $type . ' $' . $param->name)); } return str_replace("* \n", "*\n", $docBlock->toString()); //TODO remove once https://github.com/gossi/docblock/pull/2 is merged }
public function testItContainsErrorMessageWhenSubjectNotFound() { $test = new IsRemoved(); $func = new ClassMethod('some_method'); $class = new Class_('the_class'); $class->namespacedName = $class->name; $func->setAttribute('parent', $class); $old = $func; $new = null; $this->assertTrue($test->handle($old, $new)); $this->assertTrue($test->isTriggered()); $this->assertInstanceOf('PHPSemVer\\Constraints\\FailedConstraint', $test->lastException); $this->assertEquals('the_class::some_method() removed', $test->lastException->getMessage()); }
protected function enterPublicMethod(Stmt\ClassMethod $node) { // NS $methodName = $node->name; $currentFqcn = $this->getCurrentFqcn(); $declaringFqcn = $this->getReflectionContext()->getDeclaringClass($currentFqcn, $methodName); // Vertices $signatureIndex = $declaringFqcn . '::' . $methodName; $classVertex = $this->findVertex('class', $currentFqcn); $signatureVertex = $this->findVertex('method', $signatureIndex); $implVertex = $this->findVertex('impl', $currentFqcn . '::' . $methodName); // if current class == declaring class, we add the edge if ($declaringFqcn == $currentFqcn) { $this->getGraph()->addEdge($classVertex, $signatureVertex); // C -> M if (!$node->isAbstract()) { $this->getGraph()->addEdge($signatureVertex, $implVertex); // M -> S $this->getGraph()->addEdge($implVertex, $classVertex); // S -> C } } else { if (!$node->isAbstract()) { $this->getGraph()->addEdge($classVertex, $implVertex); // C -> S $this->getGraph()->addEdge($implVertex, $classVertex); // S -> C } } // in any case, we link the implementation to the params foreach ($node->params as $idx => $param) { // adding edge from signature to param : $paramVertex = $this->findVertex('param', $signatureIndex . '/' . $idx); // it is possible to not find the param because the signature // is external to the source code : if (!is_null($paramVertex)) { if (!$node->isAbstract()) { $this->getGraph()->addEdge($implVertex, $paramVertex); // S -> P } if ($currentFqcn === $declaringFqcn) { $this->getGraph()->addEdge($signatureVertex, $paramVertex); // M -> P // now the type of the param : $this->typeHintParam($param, $paramVertex); } } } }
/** * Fetch an array of all return statements found within this function. * * Note that return statements within smaller scopes contained (e.g. anonymous classes, closures) are not returned * here as they are not within the immediate scope. * * @return Node\Stmt\Return_[] */ public function getReturnStatementsAst() { $visitor = new ReturnNodeVisitor(); $traverser = new NodeTraverser(); $traverser->addVisitor($visitor); $traverser->traverse($this->node->getStmts()); return $visitor->getReturnNodes(); }
private function getMethodNodeSignature(Node\Stmt\ClassMethod $node) { if ($node->isPublic()) { $accessModifier = FunctionSignature::ACCESS_PUBLIC; } elseif ($node->isProtected()) { $accessModifier = FunctionSignature::ACCESS_PROTECTED; } else { $accessModifier = FunctionSignature::ACCESS_PRIVATE; } if ($node->isFinal()) { $polymorphModifier = FunctionSignature::POLYMORPH_FINAL; } elseif ($node->isAbstract()) { $polymorphModifier = FunctionSignature::POLYMORPH_ABSTRACT; } else { $polymorphModifier = null; } return FunctionSignature::method($node->byRef, $accessModifier, $polymorphModifier, $node->isStatic(), $node->name, $this->getParameterExpressions($node->params)); }
/** * @param Context $context * @return bool */ public function compile(Context $context) { if ($this->st->getDocComment() === null) { return $context->notice('missing-docblock', sprintf('Missing docblock for %s() method', $this->name), $this->st); } if (count($this->ast) == 0) { return $context->notice('not-implemented-method', sprintf('Method %s() is not implemented', $this->name), $this->st); } if (count($this->st->params) > 0) { /** @var Node\Param $parameter */ foreach ($this->st->params as $parameter) { $context->addSymbol($parameter->name); } } foreach ($this->ast as $st) { $result = \PHPSA\nodeVisitorFactory($st, $context); } }
/** * @param ClassMethod $methodStmt * @param Context $context * @return bool */ public function pass(ClassMethod $methodStmt, Context $context) { $functionName = $methodStmt->name; if (!$functionName) { return false; } if (substr($functionName, 0, 4) !== 'test') { return false; } if ($methodStmt->getDocComment()) { $phpdoc = $this->docBlockFactory->create($methodStmt->getDocComment()->getText()); if ($phpdoc->hasTag('test')) { $context->notice('test.annotation', 'Annotation @test is not needed when the method is prefixed with test.', $methodStmt); return true; } } return false; }
/** * @param Node\Stmt\ClassMethod $node * @param string $text * * @return string */ public function addParams(Node\Stmt\ClassMethod $node, $text) { $docBlock = new Docblock($text); foreach ($node->getParams() as $param) { if ($param->type && $param->type instanceof Node\Name) { $type = $this->resolver->resolve(implode('\\', $param->type->parts), $this->context); $type = str_replace('\\\\', '\\', $type); } elseif ($param->type && is_string($param->type)) { $type = $param->type; } if (!isset($type)) { continue; } $docBlock->appendTag(new ParamTag((string) $type . ' $' . $param->name)); } $string = $docBlock->toString(); return str_replace("* \n", "*\n", $string); //TODO remove once https://github.com/gossi/docblock/pull/2 is merged }
/** * @param \PhpParser\Node\Stmt\ClassMethod $node */ private function processMethod(\PhpParser\Node\Stmt\ClassMethod $node) { /** @var $method \TheSeer\phpDox\Collector\MethodObject */ $method = $this->unit->addMethod($node->name); $method->setStartLine($node->getAttribute('startLine')); $method->setEndLine($node->getAttribute('endLine')); $method->setAbstract($node->isAbstract()); $method->setFinal($node->isFinal()); $method->setStatic($node->isStatic()); $visibility = 'public'; if ($node->isPrivate()) { $visibility = 'private'; } elseif ($node->isProtected()) { $visibility = 'protected'; } $method->setVisibility($visibility); $docComment = $node->getDocComment(); if ($docComment !== NULL) { $block = $this->dockblocParser->parse($docComment, $this->aliasMap); $method->setDocBlock($block); } $this->processMethodParams($method, $node->params); if ($node->stmts) { $this->processInlineComments($method, $node->stmts); } }
protected function extractStmtClassMethod(Stmt\ClassMethod $node) { return ['Kind' => Grapher::KIND_METHOD, 'Name' => $node->name, 'Path' => $node->namespacedName->toString('/'), 'Exported' => $node->isPublic()]; }
/** * @param ClassMethod $node * * @return RefMethod */ private function createMethod(ClassMethod $node) { $ref = new RefMethod(); $ref->class = $this->class; $ref->name = $node->name; if ($node->isPrivate()) { $ref->visibility = Visibility::IS_PRIVATE; } elseif ($node->isProtected()) { $ref->visibility = Visibility::IS_PROTECTED; } elseif ($node->isPublic()) { $ref->visibility = Visibility::IS_PUBLIC; } $ref->isStatic = $node->isStatic(); $ref->isAbstract = $node->isAbstract(); $ref->isFinal = $node->isFinal(); $ref->startLine = $node->getLine(); $ref->docComment = $this->createDocComment($node); $ref->returnType = $this->getTypeName($node->getReturnType()); foreach ($node->getParams() as $param) { $param = $this->createParam($param, $ref->docComment); $ref->params[$param->name] = $param; } return $ref; }
public function pStmt_ClassMethod(Stmt\ClassMethod $node) { $firstToken = ''; $lastToken = ''; if (count($node->params) > 0) { if ($node->getAttribute('startLine') != reset($node->params)->getAttribute('startLine')) { $firstToken = LF . $this->indentToken; } // if the last parameters endline is 2 lines above the first statements // startLine, the closing bracket is in a new line (except if there is a comment) if ($this->getFirstLineOfMethodBody($node->stmts) - end($node->params)->getAttribute('endLine') > 1) { $lastToken = LF; } } return $this->pModifiers($node->type) . 'function ' . ($node->byRef ? '&' : '') . $node->name . '(' . $firstToken . $this->pParameterNodes($node->params) . $lastToken . ')' . (NULL !== $node->stmts ? ' {' . LF . $this->pStmts($node->stmts) . LF . '}' . LF : ';'); }
public function isPublic() : bool { return $this->classMethod->isPublic(); }
/** * @return int */ public function getLine() { return $this->classMethodAfter->getLine(); }
/** * Ищвлекает тело метода из текущего узла * * @param Node|ClassMethod $stmt Узел * @param Method[] $methods Список методов * * @return void */ private function extractMethod($stmt, array &$methods) { if (!$stmt instanceof ClassMethod) { return; } $skip = $this->isPublicOnly && $stmt->isPublic() === false; if (!$skip) { $methods[] = new Method(new MethodContext($this->context->namespace, $this->context->class), $stmt->name, $this->printer->prettyPrint([$stmt])); } }
/** * @param Node\Stmt\ClassMethod $node */ protected function parseClassMethodNode(Node\Stmt\ClassMethod $node) { $parameters = []; /** @var \PhpParser\Node\Param $param */ foreach ($node->params as $param) { $parameters[] = ['name' => $param->name, 'type' => (string) $param->type, 'isReference' => $param->byRef, 'isVariadic' => $param->variadic, 'isOptional' => $param->default ? true : false]; } $this->structuralElements[$this->currentStructuralElement->namespacedName->toString()]['methods'][] = ['name' => $node->name, 'startLine' => $node->getLine(), 'isPublic' => $node->isPublic(), 'isPrivate' => $node->isPrivate(), 'isProtected' => $node->isProtected(), 'isStatic' => $node->isStatic(), 'returnType' => $node->getReturnType(), 'parameters' => $parameters, 'docComment' => $node->getDocComment() ? $node->getDocComment()->getText() : null]; }
/** * Get the line number that this function ends on. * * @return int */ public function getEndLine() { return (int) $this->node->getAttribute('endLine', -1); }
/** * @return int */ public function getLine() { return $this->classMethodBefore->getLine(); }