/** * 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())); } } }
/** * @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; }