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); } } } }
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) } }
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 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 \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); } }
/** * @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 addMethod(ClassMethodNode $node) { $method = new ReflectionMethod($node->name); $method->setStartLine((int) $node->getAttribute('startLine')); $method->setEndLine((int) $node->getAttribute('endLine')); $method->setDocComment((string) $node->getDocComment()); $method->setReturnsByRef((bool) $node->returnsByRef()); $method->setFinal((bool) $node->isFinal()); $method->setStatic((bool) $node->isStatic()); $this->addFunctionLikeParameters($method, $node->params); // All functions in an interface are implicitly abstract. There is no need to use the abstract keyword when declaring the function. if ($this->context->getReflection() instanceof ReflectionInterface) { $method->setAbstract(true); } else { $method->setAbstract((bool) $node->isAbstract()); } if ($node->isPrivate()) { $method->setVisibility(ReflectionMethod::IS_PRIVATE); } elseif ($node->isProtected()) { $method->setVisibility(ReflectionMethod::IS_PROTECTED); } else { $method->setVisibility(ReflectionMethod::IS_PUBLIC); } $this->context->getReflection()->addMethod($method); $this->context->enterFunctionLike($method); }
public function rewriteStmt_ClassMethod(\PhpParser\Node\Stmt\ClassMethod $Node) { if ($this->in_interface) { return null; } // if (SoftMocks::isMocked(self::class, static::class, __FUNCTION__)) { // $params = [/* variables with references to them */]; // $mm_func_args = func_get_args(); // return eval(SoftMocks::getMockCode(self::class, static::class, __FUNCTION__)); // } $static = new \PhpParser\Node\Arg(new \PhpParser\Node\Expr\ClassConstFetch(new \PhpParser\Node\Name("static"), "class")); $function = new \PhpParser\Node\Expr\ConstFetch(new \PhpParser\Node\Name("__FUNCTION__")); $params_arr = []; foreach ($Node->params as $Par) { $params_arr[] = new \PhpParser\Node\Expr\ArrayItem(new \PhpParser\Node\Expr\Variable($Par->name), null, $Par->byRef); } $body_stmts = [new \PhpParser\Node\Expr\Assign(new \PhpParser\Node\Expr\Variable("mm_func_args"), new \PhpParser\Node\Expr\FuncCall(new \PhpParser\Node\Name("func_get_args"))), new \PhpParser\Node\Expr\Assign(new \PhpParser\Node\Expr\Variable("params"), new \PhpParser\Node\Expr\Array_($params_arr))]; // generators cannot return values, // we need special code handling them because yield cannot be used inside eval // we get something like the following: // // $mm_callback = SoftMocks::getMockForGenerator(); // foreach ($mm_callback(...) as $mm_val) { yield $mm_val; } if ($this->has_yield) { $args = [$static, $function]; $body_stmts[] = new \PhpParser\Node\Expr\Assign(new \PhpParser\Node\Expr\Variable("mm_callback"), new \PhpParser\Node\Expr\StaticCall(new \PhpParser\Node\Name("\\" . SoftMocks::class), "getMockForGenerator", $args)); $func_call_args = []; foreach ($Node->params as $Par) { $func_call_args[] = new \PhpParser\Node\Arg(new \PhpParser\Node\Expr\Variable($Par->name)); } $val = new \PhpParser\Node\Expr\Variable("mm_val"); $body_stmts[] = new \PhpParser\Node\Stmt\Foreach_(new \PhpParser\Node\Expr\FuncCall(new \PhpParser\Node\Expr\Variable("mm_callback"), $func_call_args), $val, ['byRef' => $Node->byRef, 'stmts' => [new \PhpParser\Node\Expr\Yield_($val)]]); $body_stmts[] = new \PhpParser\Node\Stmt\Return_(); } else { $args = [new \PhpParser\Node\Arg(new \PhpParser\Node\Expr\ClassConstFetch(new \PhpParser\Node\Name($this->cur_class), "class")), $static, $function]; $body_stmts[] = new \PhpParser\Node\Stmt\Return_(new \PhpParser\Node\Expr\Eval_(new \PhpParser\Node\Expr\StaticCall(new \PhpParser\Node\Name("\\" . SoftMocks::class), "getMockCode", $args))); } $MockCheck = new \PhpParser\Node\Stmt\If_(new \PhpParser\Node\Expr\StaticCall(new \PhpParser\Node\Name("\\" . SoftMocks::class), $this->has_yield ? "isGeneratorMocked" : "isMocked", $args), ['stmts' => $body_stmts]); if (is_array($Node->stmts)) { array_unshift($Node->stmts, $MockCheck, new \PhpParser\Node\Name("/** @codeCoverageIgnore */")); } else { if (!$Node->isAbstract()) { $Node->stmts = [$MockCheck, new \PhpParser\Node\Name("/** @codeCoverageIgnore */")]; } } }