Ejemplo n.º 1
0
 /**
  * Annotation     ::= "@" AnnotationName ["(" [Values] ")"]
  * AnnotationName ::= QualifiedName | SimpleName
  * QualifiedName  ::= NameSpacePart "\" {NameSpacePart "\"}* SimpleName
  * NameSpacePart  ::= identifier | null | false | true
  * SimpleName     ::= identifier | null | false | true
  *
  * @throws AnnotationException
  * @return mixed False if it is not a valid annotation.
  */
 private function Annotation()
 {
     $this->match(DocLexer::T_AT);
     // check if we have an annotation
     $name = $this->Identifier();
     // only process names which are not fully qualified, yet
     // fully qualified names must start with a \
     $originalName = $name;
     if ('\\' !== $name[0]) {
         $alias = false === ($pos = strpos($name, '\\')) ? $name : substr($name, 0, $pos);
         $found = false;
         if ($this->namespaces) {
             foreach ($this->namespaces as $namespace) {
                 if ($this->classExists($namespace . '\\' . $name)) {
                     $name = $namespace . '\\' . $name;
                     $found = true;
                     break;
                 }
             }
         } elseif (isset($this->imports[$loweredAlias = strtolower($alias)])) {
             if (false !== $pos) {
                 $name = $this->imports[$loweredAlias] . substr($name, $pos);
             } else {
                 $name = $this->imports[$loweredAlias];
             }
             $found = true;
         } elseif (isset($this->imports['__NAMESPACE__']) && $this->classExists($this->imports['__NAMESPACE__'] . '\\' . $name)) {
             $name = $this->imports['__NAMESPACE__'] . '\\' . $name;
             $found = true;
         } elseif ($this->classExists($name)) {
             $found = true;
         }
         if (!$found) {
             if ($this->ignoreNotImportedAnnotations || isset($this->ignoredAnnotationNames[$name])) {
                 return false;
             }
             throw AnnotationException::semanticalError(sprintf('The annotation "@%s" in %s was never imported. Did you maybe forget to add a "use" statement for this annotation?', $name, $this->context));
         }
     }
     if (!$this->classExists($name)) {
         throw AnnotationException::semanticalError(sprintf('The annotation "@%s" in %s does not exist, or could not be auto-loaded.', $name, $this->context));
     }
     // at this point, $name contains the fully qualified class name of the
     // annotation, and it is also guaranteed that this class exists, and
     // that it is loaded
     // collects the metadata annotation only if there is not yet
     if (!isset(self::$annotationMetadata[$name])) {
         $this->collectAnnotationMetadata($name);
     }
     // verify that the class is really meant to be an annotation and not just any ordinary class
     if (self::$annotationMetadata[$name]['is_annotation'] === false) {
         if (isset($this->ignoredAnnotationNames[$originalName])) {
             return false;
         }
         throw AnnotationException::semanticalError(sprintf('The class "%s" is not annotated with @Annotation. Are you sure this class can be used as annotation? If so, then you need to add @Annotation to the _class_ doc comment of "%s". If it is indeed no annotation, then you need to add @IgnoreAnnotation("%s") to the _class_ doc comment of %s.', $name, $name, $originalName, $this->context));
     }
     //if target is nested annotation
     $target = $this->isNestedAnnotation ? Target::TARGET_ANNOTATION : $this->target;
     // Next will be nested
     $this->isNestedAnnotation = true;
     //if annotation does not support current target
     if (0 === (self::$annotationMetadata[$name]['targets'] & $target) && $target) {
         throw AnnotationException::semanticalError(sprintf('Annotation @%s is not allowed to be declared on %s. You may only use this annotation on these code elements: %s.', $originalName, $this->context, self::$annotationMetadata[$name]['targets_literal']));
     }
     $values = array();
     if ($this->lexer->isNextToken(DocLexer::T_OPEN_PARENTHESIS)) {
         $this->match(DocLexer::T_OPEN_PARENTHESIS);
         if (!$this->lexer->isNextToken(DocLexer::T_CLOSE_PARENTHESIS)) {
             $values = $this->Values();
         }
         $this->match(DocLexer::T_CLOSE_PARENTHESIS);
     }
     // checks all declared attributes
     foreach (self::$annotationMetadata[$name]['attribute_types'] as $property => $type) {
         if ($property === self::$annotationMetadata[$name]['default_property'] && !isset($values[$property]) && isset($values['value'])) {
             $property = 'value';
         }
         // handle a not given attribute or null value
         if (!isset($values[$property])) {
             if ($type['required']) {
                 throw AnnotationException::requiredError($property, $originalName, $this->context, 'a(n) ' . $type['value']);
             }
             continue;
         }
         if ($type['type'] === 'array') {
             // handle the case of a single value
             if (!is_array($values[$property])) {
                 $values[$property] = array($values[$property]);
             }
             // checks if the attribute has array type declaration, such as "array<string>"
             if (isset($type['array_type'])) {
                 foreach ($values[$property] as $item) {
                     if (gettype($item) !== $type['array_type'] && !$item instanceof $type['array_type']) {
                         throw AnnotationException::typeError($property, $originalName, $this->context, 'either a(n) ' . $type['array_type'] . ', or an array of ' . $type['array_type'] . 's', $item);
                     }
                 }
             }
         } elseif (gettype($values[$property]) !== $type['type'] && !$values[$property] instanceof $type['type']) {
             throw AnnotationException::typeError($property, $originalName, $this->context, 'a(n) ' . $type['value'], $values[$property]);
         }
     }
     // check if the annotation expects values via the constructor,
     // or directly injected into public properties
     if (self::$annotationMetadata[$name]['has_constructor'] === true) {
         return new $name($values);
     }
     $instance = new $name();
     foreach ($values as $property => $value) {
         if (!isset(self::$annotationMetadata[$name]['properties'][$property])) {
             if ('value' !== $property) {
                 throw AnnotationException::creationError(sprintf('The annotation @%s declared on %s does not have a property named "%s". Available properties: %s', $originalName, $this->context, $property, implode(', ', self::$annotationMetadata[$name]['properties'])));
             }
             // handle the case if the property has no annotations
             if (!($property = self::$annotationMetadata[$name]['default_property'])) {
                 throw AnnotationException::creationError(sprintf('The annotation @%s declared on %s does not accept any values, but got %s.', $originalName, $this->context, json_encode($values)));
             }
         }
         $instance->{$property} = $value;
     }
     return $instance;
 }