* @copyright 2014 Barry vd. Heuvel / Fruitcake Studio (http://www.fruitcakestudio.nl) * @license http://www.opensource.org/licenses/mit-license.php MIT * @link https://github.com/barryvdh/laravel-ide-helper */ namespace Barryvdh\LaravelIdeHelper; use phpDocumentor\Reflection\DocBlock; use phpDocumentor\Reflection\DocBlock\Context; use phpDocumentor\Reflection\DocBlock\Tag; use phpDocumentor\Reflection\DocBlock\Tag\ReturnTag; use phpDocumentor\Reflection\DocBlock\Tag\ParamTag; use phpDocumentor\Reflection\DocBlock\Serializer as DocBlockSerializer; class Method { /** @var \phpDocumentor\Reflection\DocBlock */ protected $phpdoc; /** @var \ReflectionMethod */ protected $method; protected $output = ''; protected $name; protected $namespace; protected $params = array(); protected $params_with_default = array(); protected $interfaces = array(); protected $return = null; /** * @param \ReflectionMethod $method * @param string $alias * @param string $class * @param string|null $methodName * @param array $interfaces */ public function __construct(\ReflectionMethod $method, $alias, $class, $methodName = null, $interfaces = array()) { $this->method = $method; $this->interfaces = $interfaces; $this->name = $methodName ?: $method->name; $this->namespace = $method->getDeclaringClass()->getNamespaceName(); //Create a DocBlock and serializer instance $this->phpdoc = new DocBlock($method, new Context($this->namespace)); //Normalize the description and inherit the docs from parents/interfaces try { $this->normalizeDescription(); $this->normalizeParams(); $this->normalizeReturn(); } catch (\Exception $e) {} //Get the parameters, including formatted default values $this->getParameters($method); //Make the method static $this->phpdoc->appendTag(Tag::createInstance('@static', $this->phpdoc)); //Reference the 'real' function in the declaringclass $declaringClass = $method->getDeclaringClass(); $this->declaringClassName = '\\' . ltrim($declaringClass->name, '\\'); $this->root = '\\' . ltrim($class->getName(), '\\'); } /** * Get the class wherein the function resides * * @return string */ public function getDeclaringClass() { return $this->declaringClassName; } /** * Return the class from which this function would be called * * @return string */ public function getRoot() { return $this->root; } /** * Get the docblock for this method * * @param string $prefix * @return mixed */ public function getDocComment($prefix = "\t\t") { $serializer = new DocBlockSerializer(1, $prefix); return $serializer->getDocComment($this->phpdoc); } /** * Get the method name * * @return string */ public function getName() { return $this->name; } /** * Get the parameters for this method * * @param bool $implode Wether to implode the array or not * @return string */ public function getParams($implode = true) { return $implode ? implode(', ', $this->params) : $this->params; } /** * Get the parameters for this method including default values * * @param bool $implode Wether to implode the array or not * @return string */ public function getParamsWithDefault($implode = true) { return $implode ? implode(', ', $this->params_with_default) : $this->params_with_default; } /** * Get the description and get the inherited docs. * */ protected function normalizeDescription() { //Get the short + long description from the DocBlock $description = $this->phpdoc->getText(); //Loop through parents/interfaces, to fill in {@inheritdoc} if (strpos($description, '{@inheritdoc}') !== false) { $inheritdoc = $this->getInheritDoc($this->method); $inheritDescription = $inheritdoc->getText(); $description = str_replace('{@inheritdoc}', $inheritDescription, $description); $this->phpdoc->setText($description); //Add the tags that are inherited $inheritTags = $inheritdoc->getTags(); if ($inheritTags) { foreach ($inheritTags as $tag) { $tag->setDocBlock(); $this->phpdoc->appendTag($tag); } } } } /** * Normalize the parameters */ protected function normalizeParams() { //Get the return type and adjust them for beter autocomplete $paramTags = $this->phpdoc->getTagsByName('param'); if ($paramTags) { /** @var ParamTag $tag */ foreach($paramTags as $tag){ // Convert the keywords $content = $this->convertKeywords($tag->getContent()); $tag->setContent($content); // Get the expanded type and re-set the content $content = $tag->getType() . ' ' . $tag->getVariableName() . ' ' . $tag->getDescription(); $tag->setContent(trim($content)); } } } /** * Normalize the return tag (make full namespace, replace interfaces) */ protected function normalizeReturn() { //Get the return type and adjust them for beter autocomplete $returnTags = $this->phpdoc->getTagsByName('return'); if ($returnTags) { /** @var ReturnTag $tag */ $tag = reset($returnTags); // Get the expanded type $returnValue = $tag->getType(); // Replace the interfaces foreach($this->interfaces as $interface => $real){ $returnValue = str_replace($interface, $real, $returnValue); } // Set the changed content $tag->setContent($returnValue . ' ' . $tag->getDescription()); $this->return = $returnValue; }else{ $this->return = null; } } /** * Convert keywwords that are incorrect. * * @param string $string * @return string */ protected function convertKeywords($string) { $string = str_replace('\Closure', 'Closure', $string); $string = str_replace('Closure', '\Closure', $string); $string = str_replace('dynamic', 'mixed', $string); return $string; } /** * Should the function return a value? * * @return bool */ public function shouldReturn() { if($this->return !== "void" && $this->method->name !== "__construct"){ return true; } return false; } /** * Get the parameters and format them correctly * * @param $method * @return array */ public function getParameters($method) { //Loop through the default values for paremeters, and make the correct output string $params = array(); $paramsWithDefault = array(); foreach ($method->getParameters() as $param) { $paramStr = '$' . $param->getName(); $params[] = $paramStr; if ($param->isOptional()) { $default = $param->getDefaultValue(); if (is_bool($default)) { $default = $default ? 'true' : 'false'; } elseif (is_array($default)) { $default = 'array()'; } elseif (is_null($default)) { $default = 'null'; } elseif (is_int($default)) { //$default = $default; } else { $default = "'" . trim($default) . "'"; } $paramStr .= " = $default"; } $paramsWithDefault[] = $paramStr; } $this->params = $params; $this->params_with_default = $paramsWithDefault; } /** * @param \ReflectionMethod $reflectionMethod * @return string */ protected function getInheritDoc($reflectionMethod) { $parentClass = $reflectionMethod->getDeclaringClass()->getParentClass(); //Get either a parent or the interface if ($parentClass) { $method = $parentClass->getMethod($reflectionMethod->getName()); } else { $method = $reflectionMethod->getPrototype(); } if ($method) { $phpdoc = new DocBlock($method); if (strpos($phpdoc->getText(), '{@inheritdoc}') !== false) { //Not at the end yet, try another parent/interface.. return $this->getInheritDoc($method); } else { return $phpdoc; } } } }