/** * Test a function or method for a given class * * @dataProvider dataReflectionTestFunctions * * @param string|array $function The function name, or array of class name and method name. */ public function testFunction($function) { // We can't pass Reflector objects in here because they get printed out as the // data set when a test fails if (is_array($function)) { $ref = new \ReflectionMethod($function[0], $function[1]); $name = $function[0] . '::' . $function[1] . '()'; } else { $ref = new \ReflectionFunction($function); $name = $function . '()'; } $docblock = new \phpDocumentor\Reflection\DocBlock($ref); $doc_comment = $ref->getDocComment(); $method_params = $ref->getParameters(); $doc_params = $docblock->getTagsByName('param'); $this->assertNotFalse($doc_comment, sprintf('The docblock for `%s` should not be missing.', $name)); $this->assertNotEmpty($docblock->getShortDescription(), sprintf('The docblock description for `%s` should not be empty.', $name)); $this->assertSame(count($method_params), count($doc_params), sprintf('The number of @param docs for `%s` should match its number of parameters.', $name)); // @TODO check description ends in full stop foreach ($method_params as $i => $param) { $param_doc = $doc_params[$i]; $description = $param_doc->getDescription(); $content = $param_doc->getContent(); // @TODO decide how to handle variadic functions // ReflectionParameter::isVariadic — Checks if the parameter is variadic $is_hash = 0 === strpos($description, '{') && strlen($description) - 1 === strrpos($description, '}'); if ($is_hash) { $lines = explode("\n", $description); $description = $lines[1]; } $this->assertNotEmpty($description, sprintf('The @param description for the `%s` parameter of `%s` should not be empty.', $param_doc->getVariableName(), $name)); list($param_doc_type, $param_doc_name) = preg_split('#\\s+#', $param_doc->getContent()); $this->assertSame('$' . $param->getName(), $param_doc_name, sprintf('The @param name for the `%s` parameter of `%s` is incorrect.', '$' . $param->getName(), $name)); if ($param->isArray()) { $this->assertNotFalse(strpos($param_doc_type, 'array'), sprintf('The @param type hint for the `%s` parameter of `%s` should state that it accepts an array.', $param_doc->getVariableName(), $name)); } if (($param_class = $param->getClass()) && 'stdClass' !== $param_class->getName()) { $this->assertNotFalse(strpos($param_doc_type, $param_class->getName()), sprintf('The @param type hint for the `%s` parameter of `%s` should state that it accepts an object of type `%s`.', $param_doc->getVariableName(), $name, $param_class->getName())); } $this->assertFalse(strpos($param_doc_type, 'callback'), sprintf('`callback` is not a valid type. `callable` should be used in the @param type hint for the `%s` parameter of `%s` instead.', $param_doc->getVariableName(), $name)); if ($param->isCallable()) { $this->assertNotFalse(strpos($param_doc_type, 'callable'), sprintf('The @param type hint for the `%s` parameter of `%s` should state that it accepts a callable.', $param_doc->getVariableName(), $name)); } if ($param->isOptional()) { $this->assertNotFalse(strpos($description, 'Optional.'), sprintf('The @param description for the optional `%s` parameter of `%s` should state that it is optional.', $param_doc->getVariableName(), $name)); } else { $this->assertFalse(strpos($description, 'Optional.'), sprintf('The @param description for the required `%s` parameter of `%s` should not state that it is optional.', $param_doc->getVariableName(), $name)); } if ($param->isDefaultValueAvailable() && array() !== $param->getDefaultValue()) { $this->assertNotFalse(strpos($description, 'Default '), sprintf('The @param description for the `%s` parameter of `%s` should state its default value.', $param_doc->getVariableName(), $name)); } else { $this->assertFalse(strpos($description, 'Default '), sprintf('The @param description for the `%s` parameter of `%s` should not state a default value.', $param_doc->getVariableName(), $name)); } } }
/** * Scans this class for any method containing the @phpdoc-event tag and * connects it to the event dispatcher. * * The @phpdoc-event tag has as description the name of the event to * connect to. When encountered will that event be linked to the associated * method. * * It is thus important that such a method has a single argument $event of * type sfEvent. This contains the arguments that were dispatched. * * @return void */ protected function connectHooksToDispatcher() { $refl = new \ReflectionObject($this); // connect all events of the each method to the event_dispatcher /** @var \ReflectionMethod $method */ foreach ($refl->getMethods() as $method) { if (!$method->getDocComment()) { continue; } $docblock = new \phpDocumentor\Reflection\DocBlock($method->getDocComment()); /** @var \phpDocumentor\Reflection\DocBlock\Tag $event */ foreach ($docblock->getTagsByName('phpdoc-event') as $event) { $this->event_dispatcher->addListener($event->getDescription(), array($this, $method->getName())); } } }
/** * Factory depuis une reflection * * @param ReflectionProperty $reflection * @return static */ public static function fromReflection(ReflectionProperty $reflection) { // gestion du type $type = implode('|', $reflection->getDocBlockTypeStrings()); // cas de la valeur null $value = $reflection->getDefaultValue(); if (is_null($value)) { $value = Maker::NO_VALUE; $class = $reflection->getDeclaringClass(); foreach ($class->getAst()->stmts as $stmt) { // si pas un attribut, on zap if (!$stmt instanceof \PhpParser\Node\Stmt\Property) { continue; } foreach ($stmt->props as $prop) { if ($prop instanceof \PhpParser\Node\Stmt\PropertyProperty) { // lecture du fichier $file = file($class->getFileName()); if (!empty($line = $file[$prop->getLine() - 1])) { if (strpos($line, '=')) { $value = null; } } } } } } // construction $property = new static($reflection->getName(), $value, $type); // docblock $docblock = new \phpDocumentor\Reflection\DocBlock($reflection->getDocComment()); $property->setSummary($docblock->getShortDescription()); $property->setDescription($docblock->getLongDescription()); // gestion des modifiers $reflection->isPrivate() ? $property->enablePrivate() : $property->disablePrivate(); $reflection->isProtected() ? $property->enableProtected() : $property->disabledProtected(); $reflection->isPublic() ? $property->enablePublic() : $property->disablePublic(); $reflection->isStatic() ? $property->enableStatic() : $property->disableStatic(); return $property; }
protected function loadClassMethods(\ReflectionClass $reflectedObject) { $this->classMethods = array(); $classMethods = $reflectedObject->getMethods(); if (!empty($classMethods)) { foreach ($classMethods as $classMethod) { $reflectedMethod = $reflectedObject->getMethod($classMethod->name); $method = new Object\Method(); $method->setName($reflectedMethod->getName()); $method->setLevel($this->level + 1); // static if ($reflectedMethod->isStatic()) { $method->setStatic(); } // visibility if ($reflectedMethod->isPublic()) { $method->setVisibility(Object\VisibilityInterface::VISIBILITY_PUBLIC); $this->publicMethodsNumber++; } elseif ($reflectedMethod->isProtected()) { $method->setVisibility(Object\VisibilityInterface::VISIBILITY_PROTECTED); $this->protectedMethodsNumber++; } elseif ($reflectedMethod->isPrivate()) { $method->setVisibility(Object\VisibilityInterface::VISIBILITY_PRIVATE); $this->privateMethodsNumber++; } // phpdoc comment if (class_exists('\\phpDocumentor\\Reflection\\DocBlock')) { $phpdoc = new \phpDocumentor\Reflection\DocBlock($reflectedMethod->getDocComment()); $method->setShortDescription($phpdoc->getShortDescription()); $method->setLongDescription($phpdoc->getLongDescription()); } // parameters $methodParameters = $reflectedMethod->getParameters(); foreach ($methodParameters as $methodParameterReflected) { $methodParameter = new Object\MethodParameter(); $methodParameter->setName($methodParameterReflected->getName()); $class = $methodParameterReflected->getClass(); if ($class instanceof \ReflectionClass) { $methodParameter->setType($class->getName()); } elseif ($methodParameterReflected->isArray()) { $methodParameter->setType('array'); } if ($methodParameterReflected->isPassedByReference()) { $methodParameter->setReference(); } if ($methodParameterReflected->isDefaultValueAvailable()) { $default = $methodParameterReflected->getDefaultValue(); $defaultValueType = $this->factory->factory($default, $this->level + 1); $methodParameter->setDefaultValue($defaultValueType); } $method->addMethodParameter($methodParameter); } $this->classMethods[] = $method; } } // order methods usort($this->classMethods, function (Object\Method $methodA, Object\Method $methodB) { $orderValueA = $methodA->getVisibility() . $methodA->getName(); $orderValueB = $methodB->getVisibility() . $methodB->getName(); return strcasecmp($orderValueA, $orderValueB); }); }
/** * Analyse $class for extract self content * * @return $this */ protected function reflect() { $reflection = $this->getClass(); // docblock $docblock = new \phpDocumentor\Reflection\DocBlock($reflection->getDocComment()); $this->setSummary($docblock->getShortDescription()); $this->setDescription($docblock->getLongDescription()); // NAMESPACE $namespace = $reflection->getNamespaceName(); if (!empty($namespace)) { $this->setNamespace($namespace); } // ALIAS $file = $reflection->getFileName(); $this->setFilename($file); // lecture du fichier if (file_exists($file)) { $file = file($file); //Première lignes du fichier foreach (array_slice($file, 0, $reflection->getStartLine()) as $line) { // analyse de partern d'alias if (preg_match('#use (?<class>[^\\s^;]+)(\\s+as\\s+(?<alias>[^\\s^;]+))?\\s*;?#', $line, $match)) { $class = $match['class']; $alias = empty($match['alias']) ? collect(explode('\\', $class))->last() : $match['alias']; $this->addAlias($alias, $class); } } } // PARENT CLASS $parent = $reflection->getParentClass(); if (!empty($parent)) { $this->setParent($parent->getName()); } // CONSTANT $this->setConstants($reflection->getConstants()); // PROPERTIES foreach ($reflection->getProperties() as $property) { $this->addProperty(Property::fromReflection($property)); } // METHODS foreach ($reflection->getImmediateMethods() as $method) { $this->addMethod(Method::fromReflection($method)); } return $this; }
public function beforeTraverse(array $nodes) { $node = null; $key = 0; foreach ($nodes as $k => $n) { if (!$n instanceof \PHPParser_Node_Stmt_InlineHTML) { $node = $n; $key = $k; break; } } if ($node) { $comments = (array) $node->getAttribute('comments'); // remove non-DocBlock comments $comments = array_values(array_filter($comments, function ($comment) { return $comment instanceof \PHPParser_Comment_Doc; })); if (!empty($comments)) { $docblock = new \phpDocumentor\Reflection\DocBlock((string) $comments[0]); // the first DocBlock in a file documents the file if // * it precedes another DocBlock or // * it contains a @package tag and doesn't precede a class // declaration or // * it precedes a non-documentable element (thus no include, // require, class, function, define, const) if (count($comments) > 1 || !$node instanceof \PHPParser_Node_Stmt_Class && $docblock->hasTag('package') || !$this->isNodeDocumentable($node)) { $docblock->line_number = $comments[0]->getLine(); $this->doc_block = $docblock; // remove the file level DocBlock from the node's comments $comments = array_slice($comments, 1); } } // always update the comments attribute so that standard comments // do not stop DocBlock from being attached to an element $node->setAttribute('comments', $comments); $nodes[$key] = $node; } \phpDocumentor\Plugin\EventDispatcher::getInstance()->dispatch('reflection.docblock-extraction.post', \phpDocumentor\Reflection\Events\PostDocBlockExtractionEvent::createInstance($this)->setDocblock($this->doc_block)); return $nodes; }
/** * Displays available options and parameters this component offers. * * @apiMethod GET * @apiUri / * @return void */ public function indexTask() { // var to hold output $output = new stdClass(); $output->component = substr($this->_option, 4); $bits = explode('v', get_class($this)); $output->version = str_replace('_', '.', end($bits)); $output->tasks = array(); $output->errors = array(); // create reflection class of file $classReflector = new ReflectionClass($this); // loop through each method and process doc foreach ($classReflector->getMethods() as $method) { // create docblock object & make sure we have something $phpdoc = new \phpDocumentor\Reflection\DocBlock($method); // skip constructor if (substr($method->getName(), -4) != 'Task' || in_array($method->getName(), array('registerTask', 'unregisterTask'))) { continue; } // skip method in the parent class (already processed), /*if ($className != $method->getDeclaringClass()->getName()) { //continue; }*/ // skip if we dont have a short desc // but put in error if (!$phpdoc->getShortDescription()) { $output->errors[] = sprintf('Missing docblock for method "%s" in "%s"', $method->getName(), str_replace(PATH_ROOT, '', $classReflector->getFileName())); continue; } // create endpoint data array $endpoint = array('name' => substr($method->getName(), 0, -4), 'description' => preg_replace('/\\s+/', ' ', $phpdoc->getShortDescription()), 'method' => '', 'uri' => '', 'parameters' => array()); // loop through each tag foreach ($phpdoc->getTags() as $tag) { // get tag name and content $name = strtolower(str_replace('api', '', $tag->getName())); $content = $tag->getContent(); // handle parameters separately // json decode param input if ($name == 'parameter') { $parameter = json_decode($content); if (json_last_error() != JSON_ERROR_NONE) { $output->errors[] = sprintf('Unable to parse parameter info for method "%s" in "%s"', $method->getName(), str_replace(PATH_ROOT, '', $classReflector->getFileName())); continue; } $endpoint['parameters'][] = (array) $parameter; continue; } if ($name == 'uri' && $method->getName() == 'indexTask') { $content .= $output->component; } // add data to endpoint data $endpoint[$name] = $content; } // add endpoint to output $output->tasks[] = $endpoint; } if (count($output->errors) <= 0) { unset($output->errors); } $this->send($output); }
/** * @param $route * @return array */ private function addRouteDefinition(\DC\Router\IRoute $route) { $path = $this->simplifyPathForSwagger($route->getPath()); $method = strtolower($route->getMethod()); $callable = $route->getCallable(); $reflection = $this->reflector->getReflectionFunctionForCallable($callable); if ($reflection instanceof \ReflectionMethod) { if (!$reflection->getDeclaringClass()->implementsInterface('\\DC\\Router\\Swagger\\ISwaggerAPI')) { return; } } $phpdoc = new \phpDocumentor\Reflection\DocBlock($reflection); if (count($phpdoc->getTagsByName(SwaggerExcludeTag::$name)) > 0) { return; } $routeDef = ["produces" => ["application/json"], "responses" => []]; if ($method == "post" || $method == "put") { $routeDef["consumes"] = ["application/json"]; } if ($reflection instanceof \ReflectionMethod) { $reflectionClass = $reflection->getDeclaringClass(); $routeDef["tags"] = [$this->addTagForController($reflectionClass)]; } $parameters = $this->routeMatcher->getParameterInfo($route); $paramTags = $phpdoc->getTagsByName("param"); $reflectionParameters = $reflection->getParameters(); $reflectionParametersByName = []; foreach ($reflectionParameters as $parameter) { $reflectionParametersByName[$parameter->getName()] = $parameter; } foreach ($parameters as $parameter) { $matchingParamTags = array_filter($paramTags, function ($t) use($parameter) { return $t->getVariableName() == '$' . $parameter->getInternalName(); }); $paramTag = reset($matchingParamTags); $reflectionParameter = $reflectionParametersByName[$parameter->getInternalName()]; $required = !isset($reflectionParameter) || !$reflectionParameter->isOptional(); $paramDef = ["name" => $parameter->getQueryName(), "in" => $parameter->getPlacement(), "required" => $required]; if ($paramTag != null) { $paramDef["description"] = $paramTag->getDescription(); $paramDef += $this->getTypeDefinition($paramTag->getType()); } else { $paramDef["type"] = "string"; } $routeDef["parameters"][] = $paramDef; } /** * @var $returnTag \phpDocumentor\Reflection\DocBlock\Tag\ReturnTag[] */ $returnTag = $phpdoc->getTagsByName("return"); if (count($returnTag) > 0) { $returnTypes = $returnTag[0]->getTypes(); if (!isset($returnTypes)) { $returnTypes = [$returnTag[0]->getType()]; } foreach ($returnTypes as $type) { $defRef = $this->getTypeDefinitionOrReference($type); $routeDef["responses"][200] = ["schema" => $defRef, "description" => $returnTag[0]->getDescription()]; } } /** @var $throwsTags \phpDocumentor\Reflection\DocBlock\Tag\ThrowsTag[] */ $throwsTags = $phpdoc->getTagsByName("throws"); if (count($throwsTags) > 0) { foreach ($throwsTags as $throwsTag) { $type = $throwsTag->getType(); $defRef = $this->getTypeDefinitionOrReference($type); $routeDef["responses"][500] = ["schema" => $defRef, "description" => $throwsTag->getDescription()]; } } $routeDef["summary"] = $phpdoc->getShortDescription(); $description = $phpdoc->getLongDescription()->getContents(); if (strlen($description) > 0) { $routeDef["description"] = $description; } $this->def['paths'][$path][$method] = $routeDef; }
/** * * * @param ReflectionMethod $reflection * @return Method */ public static function fromReflection(ReflectionMethod $reflection) { // gestion du type // $type = implode('|', $reflection->getDocBlockTypeStrings()); // // construction $method = new static($reflection->getName(), [], $reflection->getBodyCode()); // docblock $docblock = new \phpDocumentor\Reflection\DocBlock($reflection->getDocComment()); $method->setSummary($docblock->getShortDescription()); $method->setDescription($docblock->getLongDescription()); // gestion des modifiers $reflection->isPrivate() ? $method->enablePrivate() : $method->disablePrivate(); $reflection->isProtected() ? $method->enableProtected() : $method->disabledProtected(); $reflection->isPublic() ? $method->enablePublic() : $method->disablePublic(); $reflection->isStatic() ? $method->enableStatic() : $method->disableStatic(); $reflection->isFinal() ? $method->enableFinal() : $method->disableFinal(); foreach ($reflection->getParameters() as $parameter) { $method->addParameter(Parameter::fromReflection($parameter)); } return $method; }
public static function getPropsAsStringByReflectionMethod(\ReflectionMethod $method, $withoutValues = false) { $props = array(); if ($method->getDocComment()) { $docComment = new \phpDocumentor\Reflection\DocBlock($method->getDocComment()); foreach ($docComment->getTags() as $tag) { /* @var $tag \phpDocumentor\Reflection\DocBlock\Tag\ReturnTag */ if ($tag->getName() == "param") { $props[] = self::getInstance()->getValueByType($tag->getType(), $withoutValues); } } } else { foreach ($method->getParameters() as $parameter) { // var_dump($parameter->getDefaultValue()); // var_dump($parameter->getDefaultValueConstantName()); $param = \ReflectionParameter::export(array($parameter->getDeclaringClass()->name, $parameter->getDeclaringFunction()->name), $parameter->name, true); preg_match('/(\\[ )(<)(.*)(>)( )([^\\]=]+)/', $param, $matches); if (count($matches) === 7) { $t = explode(" ", trim($matches[6])); if (count($t) === 2) { if (strtolower($t[0]) == "array") { return 'array()'; } if (strtolower($t[0]) == "closure") { return 'function(){}'; } $props[] = '$this->getMock(\'' . str_replace('\\', '\\\\', $t[0]) . '\')'; } else { if ($parameter->isDefaultValueAvailable()) { $props[] = $parameter->getDefaultValue(); } else { $props[] = 1; } } } } } $props2 = array(); foreach ($props as $prop2) { if ($prop2 != "") { $props2[] = $prop2; } } return implode(",", $props2); }