You are here

private function DocParser::Annotation in Drupal 8

Same name and namespace in other branches
  1. 9 core/lib/Drupal/Component/Annotation/Doctrine/DocParser.php \Drupal\Component\Annotation\Doctrine\DocParser::Annotation()

Annotation ::= "@" AnnotationName MethodCall AnnotationName ::= QualifiedName | SimpleName QualifiedName ::= NameSpacePart "\" {NameSpacePart "\"}* SimpleName NameSpacePart ::= identifier | null | false | true SimpleName ::= identifier | null | false | true

Return value

mixed False if it is not a valid annotation.

Throws

AnnotationException

2 calls to DocParser::Annotation()
DocParser::Annotations in core/lib/Drupal/Component/Annotation/Doctrine/DocParser.php
Annotations ::= Annotation {[ "*" ]* [Annotation]}*
DocParser::PlainValue in core/lib/Drupal/Component/Annotation/Doctrine/DocParser.php
PlainValue ::= integer | string | float | boolean | Array | Annotation

File

core/lib/Drupal/Component/Annotation/Doctrine/DocParser.php, line 659
This class is a near-copy of Doctrine\Common\Annotations\DocParser, which is part of the Doctrine project: <http://www.doctrine-project.org>. It was copied from version 1.2.7.

Class

DocParser
A parser for docblock annotations.

Namespace

Drupal\Component\Annotation\Doctrine

Code

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) {
      if (isset($this->ignoredAnnotationNames[$name])) {
        return false;
      }
      foreach ($this->namespaces as $namespace) {
        if ($this
          ->classExists($namespace . '\\' . $name)) {
          $name = $namespace . '\\' . $name;
          $found = true;
          break;
        }
      }
    }
    elseif (isset($this->imports[$loweredAlias = strtolower($alias)])) {
      $found = true;
      $name = false !== $pos ? $this->imports[$loweredAlias] . substr($name, $pos) : $this->imports[$loweredAlias];
    }
    elseif (!isset($this->ignoredAnnotationNames[$name]) && isset($this->imports['__NAMESPACE__']) && $this
      ->classExists($this->imports['__NAMESPACE__'] . '\\' . $name)) {
      $name = $this->imports['__NAMESPACE__'] . '\\' . $name;
      $found = true;
    }
    elseif (!isset($this->ignoredAnnotationNames[$name]) && $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 = $this
    ->MethodCall();
  if (isset(self::$annotationMetadata[$name]['enum'])) {

    // checks all declared attributes
    foreach (self::$annotationMetadata[$name]['enum'] as $property => $enum) {

      // checks if the attribute is a valid enumerator
      if (isset($values[$property]) && !in_array($values[$property], $enum['value'])) {
        throw AnnotationException::enumeratorError($property, $name, $this->context, $enum['literal'], $values[$property]);
      }
    }
  }

  // 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::attributeTypeError($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::attributeTypeError($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;
}