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