/** * Set imports for annotations * * @param array $imports */ public function setImports(array $imports) { $convertedImports = array(); foreach ($imports as $aliasName => $fullName) { $convertedImports[strtolower($aliasName)] = $fullName; } $this->parser->setImports($convertedImports); }
/** * {@inheritdoc} */ public function process($context, Result $result, Collection $collection) { $this->result = $result; $this->collection = $collection; $this->docParser = new DocParser(); $this->docParser->setIgnoreNotImportedAnnotations(true); $phpdoc = $namespace = false; $tokenParser = new TokenParser($context->getFileContent()); while ($token = $tokenParser->next(false)) { if (false === is_array($token)) { continue; } switch ($token[0]) { case T_DOC_COMMENT: if ($phpdoc !== false) { $this->processDocComment($phpdoc); $phpdoc = false; } $phpdoc = $token[1]; break; case T_NAMESPACE: $namespace = $tokenParser->parseNamespace(); break; case T_USE: // add imports to doc parser to be able to use annotation without FQCN. foreach ($tokenParser->parseUseStatement() as $alias => $target) { if ($target[0] === '\\') { $target = substr($target, 1); } $imports[$alias] = $target; } $this->docParser->setImports($imports); break; case T_CLASS: $token = $tokenParser->next(false); $class = $namespace ? $namespace . '\\' . $token[1] : $token[1]; $this->dispatchEvent(Events::TOKEN_PHP_CLASS, new Context\PHP\PHPClass($class, $phpdoc), $phpdoc); break; case T_VARIABLE: $this->dispatchEvent(Events::TOKEN_PHP_VARIABLE, new Context\PHP\PHPVariable(substr($token[1], 1), isset($class) ? $class : null, $phpdoc), $phpdoc); break; case T_FUNCTION: $token = $tokenParser->next(false); if (!is_array($token)) { continue; } $this->dispatchEvent(Events::TOKEN_PHP_FUNCTION, new Context\PHP\PHPFunction($token[1], isset($class) ? $class : null, $phpdoc), $phpdoc); break; } } }
protected final function getDocParser() { $docParser = new DocParser(); $docParser->setImports(array('desc' => 'JMS\\TranslationBundle\\Annotation\\Desc', 'meaning' => 'JMS\\TranslationBundle\\Annotation\\Meaning', 'ignore' => 'JMS\\TranslationBundle\\Annotation\\Ignore')); $docParser->setIgnoreNotImportedAnnotations(true); return $docParser; }
/** * {@inheritDoc} */ public function getMethodAnnotations(ReflectionMethod $method) { $class = $method->getDeclaringClass(); $context = 'method ' . $class->getName() . '::' . $method->getName() . '()'; $this->parser->setTarget(Target::TARGET_METHOD); $this->parser->setImports($this->getMethodImports($method)); $this->parser->setIgnoredAnnotationNames($this->getIgnoredAnnotationNames($class)); return $this->parser->parse($method->getDocComment(), $context); }
public function testIssueGlobalNamespace() { $docblock = "@Entity"; $parser = new DocParser(); $parser->setImports(array("__NAMESPACE__" => "Doctrine\\Tests\\Common\\Annotations\\Ticket\\Doctrine\\ORM\\Mapping")); $annots = $parser->parse($docblock); $this->assertEquals(1, count($annots)); $this->assertInstanceOf("Doctrine\\Tests\\Common\\Annotations\\Ticket\\Doctrine\\ORM\\Mapping\\Entity", $annots[0]); }
public function __construct($docParser = null) { if ($docParser === null) { $docParser = new DocParser(); $docParser->setIgnoreNotImportedAnnotations(true); $docParser->setImports(['swg' => 'Swagger\\Annotations']); // Use @SWG\* for swagger annotations. } $this->docParser = $docParser; }
private function extract($directory) { $twig = new \Twig_Environment(); $twig->addExtension(new SymfonyTranslationExtension($translator = new IdentityTranslator(new MessageSelector()))); $twig->addExtension(new TranslationExtension($translator)); $docParser = new DocParser(); $docParser->setImports(array('desc' => 'JMS\\TranslationBundle\\Annotation\\Desc', 'meaning' => 'JMS\\TranslationBundle\\Annotation\\Meaning', 'ignore' => 'JMS\\TranslationBundle\\Annotation\\Ignore')); $docParser->setIgnoreNotImportedAnnotations(true); $factory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader())); $extractor = new FileExtractor($twig, new NullLogger(), array(new DefaultPhpFileExtractor($docParser), new TranslationContainerExtractor(), new TwigFileExtractor($twig), new ValidationExtractor($factory), new FormExtractor($docParser))); $extractor->setDirectory($directory); return $extractor->extract(); }
public function scanForAnnotations(string $docBlock, $context, array $imports) : Annotations { // Hack to ensure an attempt to autoload an annotation class is made AnnotationRegistry::registerLoader(function ($class) { return (bool) class_exists($class); }); $imports = array_combine(array_map(function ($string) { return strtolower($string); }, array_keys($imports)), array_values($imports)); $parser = new DocParser(); $parser->setIgnoreNotImportedAnnotations(true); $parser->setImports($imports); return new Annotations($parser->parse($docBlock, $context)); }
/** * @group performance */ public function testDocParsePerformance() { $imports = array('ignorephpdoc' => 'Annotations\\Annotation\\IgnorePhpDoc', 'ignoreannotation' => 'Annotations\\Annotation\\IgnoreAnnotation', 'route' => 'Doctrine\\Tests\\Common\\Annotations\\Fixtures\\Annotation\\Route', 'template' => 'Doctrine\\Tests\\Common\\Annotations\\Fixtures\\Annotation\\Template', '__NAMESPACE__' => 'Doctrine\\Tests\\Common\\Annotations\\Fixtures'); $ignored = array('access', 'author', 'copyright', 'deprecated', 'example', 'ignore', 'internal', 'link', 'see', 'since', 'tutorial', 'version', 'package', 'subpackage', 'name', 'global', 'param', 'return', 'staticvar', 'static', 'var', 'throws', 'inheritdoc'); $method = $this->getMethod(); $methodComment = $method->getDocComment(); $classComment = $method->getDeclaringClass()->getDocComment(); $time = microtime(true); for ($i = 0, $c = 200; $i < $c; $i++) { $parser = new DocParser(); $parser->setImports($imports); $parser->setIgnoredAnnotationNames($ignored); $parser->setIgnoreNotImportedAnnotations(true); $parser->parse($methodComment); $parser->parse($classComment); } $time = microtime(true) - $time; $this->printResults('doc-parser', $time, $c); }
private function extract($file, FormExtractor $extractor = null) { if (!is_file($file = __DIR__ . '/Fixture/' . $file)) { throw new RuntimeException(sprintf('The file "%s" does not exist.', $file)); } $file = new \SplFileInfo($file); if (null === $extractor) { $docParser = new DocParser(); $docParser->setImports(array('desc' => 'JMS\\TranslationBundle\\Annotation\\Desc', 'meaning' => 'JMS\\TranslationBundle\\Annotation\\Meaning', 'ignore' => 'JMS\\TranslationBundle\\Annotation\\Ignore')); $docParser->setIgnoreNotImportedAnnotations(true); $extractor = new FormExtractor($docParser); } $lexer = new \PHPParser_Lexer(file_get_contents($file)); $parser = new \PHPParser_Parser(); $ast = $parser->parse($lexer); $catalogue = new MessageCatalogue(); $extractor->visitPhpFile($file, $catalogue, $ast); return $catalogue; }
private function extract($directory) { $twig = new \Twig_Environment(); $twig->addExtension(new SymfonyTranslationExtension($translator = new IdentityTranslator(new MessageSelector()))); $twig->addExtension(new TranslationExtension($translator)); $loader = new \Twig_Loader_Filesystem(realpath(__DIR__ . "/Fixture/SimpleTest/Resources/views/")); $twig->setLoader($loader); $docParser = new DocParser(); $docParser->setImports(array('desc' => 'JMS\\TranslationBundle\\Annotation\\Desc', 'meaning' => 'JMS\\TranslationBundle\\Annotation\\Meaning', 'ignore' => 'JMS\\TranslationBundle\\Annotation\\Ignore')); $docParser->setIgnoreNotImportedAnnotations(true); //use correct factory class depending on whether using Symfony 2 or 3 if (class_exists('Symfony\\Component\\Validator\\Mapping\\Factory\\LazyLoadingMetadataFactory')) { $metadataFactoryClass = 'Symfony\\Component\\Validator\\Mapping\\Factory\\LazyLoadingMetadataFactory'; } else { $metadataFactoryClass = 'Symfony\\Component\\Validator\\Mapping\\ClassMetadataFactory'; } $factory = new $metadataFactoryClass(new AnnotationLoader(new AnnotationReader())); $extractor = new FileExtractor($twig, new NullLogger(), array(new DefaultPhpFileExtractor($docParser), new TranslationContainerExtractor(), new TwigFileExtractor($twig), new ValidationExtractor($factory), new FormExtractor($docParser))); $extractor->setDirectory($directory); return $extractor->extract(); }
protected function setUp() { $docParser = new DocParser(); $docParser->setImports(array('desc' => 'JMS\\TranslationBundle\\Annotation\\Desc', 'meaning' => 'JMS\\TranslationBundle\\Annotation\\Meaning', 'ignore' => 'JMS\\TranslationBundle\\Annotation\\Ignore')); $docParser->setIgnoreNotImportedAnnotations(true); $this->extractor = new FormExtractor($docParser); }
/** * Collects parsing metadata for a given annotation class * * @param string $name The annotation name */ private function collectAnnotationMetadata($name) { if (self::$metadataParser == null) { self::$metadataParser = new self(); self::$metadataParser->setTarget(Target::TARGET_CLASS); self::$metadataParser->setIgnoreNotImportedAnnotations(true); self::$metadataParser->setImports(array('target' => 'Doctrine\\Common\\Annotations\\Annotation\\Target', 'attribute' => 'Doctrine\\Common\\Annotations\\Annotation\\Attribute', 'attributes' => 'Doctrine\\Common\\Annotations\\Annotation\\Attributes')); AnnotationRegistry::registerFile(__DIR__ . '/Annotation/Target.php'); AnnotationRegistry::registerFile(__DIR__ . '/Annotation/Attribute.php'); AnnotationRegistry::registerFile(__DIR__ . '/Annotation/Attributes.php'); } $class = new \ReflectionClass($name); $docComment = $class->getDocComment(); // Sets default values for annotation metadata $metadata = array('default_property' => null, 'has_constructor' => null !== ($constructor = $class->getConstructor()) && $constructor->getNumberOfParameters() > 0, 'properties' => array(), 'property_types' => array(), 'attribute_types' => array(), 'targets_literal' => null, 'targets' => Target::TARGET_ALL, 'is_annotation' => false !== strpos($docComment, '@Annotation')); // verify that the class is really meant to be an annotation if ($metadata['is_annotation']) { foreach (self::$metadataParser->parse($docComment, 'class @' . $name) as $annotation) { if ($annotation instanceof Target) { $metadata['targets'] = $annotation->targets; $metadata['targets_literal'] = $annotation->literal; } elseif ($annotation instanceof Attributes) { foreach ($annotation->value as $attrib) { // handle internal type declaration $type = isset(self::$typeMap[$attrib->type]) ? self::$typeMap[$attrib->type] : $attrib->type; // handle the case if the property type is mixed if ('mixed' !== $type) { // Checks if the property has array<type> if (false !== ($pos = strpos($type, '<'))) { $arrayType = substr($type, $pos + 1, -1); $type = 'array'; if (isset(self::$typeMap[$arrayType])) { $arrayType = self::$typeMap[$arrayType]; } $metadata['attribute_types'][$attrib->name]['array_type'] = $arrayType; } $metadata['attribute_types'][$attrib->name]['type'] = $type; $metadata['attribute_types'][$attrib->name]['value'] = $attrib->type; $metadata['attribute_types'][$attrib->name]['required'] = $attrib->required; } } } } // if not has a constructor will inject values into public properties if (false === $metadata['has_constructor']) { // collect all public properties foreach ($class->getProperties(\ReflectionProperty::IS_PUBLIC) as $property) { $metadata['properties'][$property->name] = $property->name; // checks if the property has @var annotation if (false !== ($propertyComment = $property->getDocComment()) && false !== strpos($propertyComment, '@var') && preg_match('/@var\\s+([^\\s]+)/', $propertyComment, $matches)) { // literal type declaration $value = $matches[1]; // handle internal type declaration $type = isset(self::$typeMap[$value]) ? self::$typeMap[$value] : $value; // handle the case if the property type is mixed if ('mixed' !== $type) { // Checks if the property has @var array<type> annotation if (false !== ($pos = strpos($type, '<'))) { $arrayType = substr($type, $pos + 1, -1); $type = 'array'; if (isset(self::$typeMap[$arrayType])) { $arrayType = self::$typeMap[$arrayType]; } $metadata['attribute_types'][$property->name]['array_type'] = $arrayType; } $metadata['attribute_types'][$property->name]['type'] = $type; $metadata['attribute_types'][$property->name]['value'] = $value; $metadata['attribute_types'][$property->name]['required'] = false !== strpos($propertyComment, '@Required'); } } } // choose the first property as default property $metadata['default_property'] = reset($metadata['properties']); } } self::$annotationMetadata[$name] = $metadata; }
public function createTestParser() { $parser = new DocParser(); $parser->setIgnoreNotImportedAnnotations(true); $parser->setImports(array('name' => 'Doctrine\\Tests\\Common\\Annotations\\Name', '__NAMESPACE__' => 'Doctrine\\Tests\\Common\\Annotations')); return $parser; }
/** * Collects parsing metadata for a given annotation class * * @param string $name The annotation name * * @return void */ private function collectAnnotationMetadata($name) { if (self::$metadataParser === null) { self::$metadataParser = new self(); self::$metadataParser->setIgnoreNotImportedAnnotations(true); self::$metadataParser->setIgnoredAnnotationNames($this->ignoredAnnotationNames); self::$metadataParser->setImports(array('enum' => 'Doctrine\\Common\\Annotations\\Annotation\\Enum', 'target' => 'Doctrine\\Common\\Annotations\\Annotation\\Target', 'attribute' => 'Doctrine\\Common\\Annotations\\Annotation\\Attribute', 'attributes' => 'Doctrine\\Common\\Annotations\\Annotation\\Attributes')); AnnotationRegistry::registerFile(__DIR__ . '/Annotation/Enum.php'); AnnotationRegistry::registerFile(__DIR__ . '/Annotation/Target.php'); AnnotationRegistry::registerFile(__DIR__ . '/Annotation/Attribute.php'); AnnotationRegistry::registerFile(__DIR__ . '/Annotation/Attributes.php'); } $class = new \ReflectionClass($name); $docComment = $class->getDocComment(); // Sets default values for annotation metadata $metadata = array('default_property' => null, 'has_constructor' => null !== ($constructor = $class->getConstructor()) && $constructor->getNumberOfParameters() > 0, 'properties' => array(), 'property_types' => array(), 'attribute_types' => array(), 'targets_literal' => null, 'targets' => Target::TARGET_ALL, 'is_annotation' => false !== strpos($docComment, '@Annotation')); // verify that the class is really meant to be an annotation if ($metadata['is_annotation']) { self::$metadataParser->setTarget(Target::TARGET_CLASS); foreach (self::$metadataParser->parse($docComment, 'class @' . $name) as $annotation) { if ($annotation instanceof Target) { $metadata['targets'] = $annotation->targets; $metadata['targets_literal'] = $annotation->literal; continue; } if ($annotation instanceof Attributes) { foreach ($annotation->value as $attribute) { $this->collectAttributeTypeMetadata($metadata, $attribute); } } } // if not has a constructor will inject values into public properties if (false === $metadata['has_constructor']) { // collect all public properties foreach ($class->getProperties(\ReflectionProperty::IS_PUBLIC) as $property) { $metadata['properties'][$property->name] = $property->name; if (false === ($propertyComment = $property->getDocComment())) { continue; } $attribute = new Attribute(); $attribute->required = false !== strpos($propertyComment, '@Required'); $attribute->name = $property->name; $attribute->type = false !== strpos($propertyComment, '@var') && preg_match('/@var\\s+([^\\s]+)/', $propertyComment, $matches) ? $matches[1] : 'mixed'; $this->collectAttributeTypeMetadata($metadata, $attribute); // checks if the property has @Enum if (false !== strpos($propertyComment, '@Enum')) { $context = 'property ' . $class->name . "::\$" . $property->name; self::$metadataParser->setTarget(Target::TARGET_PROPERTY); foreach (self::$metadataParser->parse($propertyComment, $context) as $annotation) { if (!$annotation instanceof Enum) { continue; } $metadata['enum'][$property->name]['value'] = $annotation->value; $metadata['enum'][$property->name]['literal'] = !empty($annotation->literal) ? $annotation->literal : $annotation->value; } } } // choose the first property as default property $metadata['default_property'] = reset($metadata['properties']); } } self::$annotationMetadata[$name] = $metadata; }
/** * Shared implementation for parseFile() & parseContents(). * @param array $tokens The result of a token_get_all() */ protected function parseTokens($tokens, $parseContext) { $this->docParser = new DocParser(); $this->docParser->setIgnoreNotImportedAnnotations(true); reset($tokens); $token = ''; $namespace = ''; $imports = array('swg' => 'Swagger\\Annotations'); $this->docParser->setImports($imports); $uses = array(); $classContext = $parseContext; // Use the parseContext until a classContext is created. $comment = false; $line = 0; while ($token !== false) { $previousToken = $token; $token = $this->nextToken($tokens, $parseContext); if (is_array($token) === false) { // Ignore tokens like "{", "}", etc continue; } if ($token[0] === T_DOC_COMMENT) { if ($comment) { // 2 Doc-comments in succession? $this->parseContext(new Context(array('comment' => $comment, 'line' => $line), $classContext)); } $comment = $token[1]; $line = $token[2]; continue; } if ($token[0] === T_ABSTRACT) { $token = $this->nextToken($tokens, $parseContext); // Skip "abstract" keyword } if ($token[0] === T_CLASS) { // Doc-comment before a class? if (is_array($previousToken) && $previousToken[0] === T_DOUBLE_COLON) { //php 5.5 class name resolution (i.e. ClassName::class) continue; } $token = $this->nextToken($tokens, $parseContext); $classContext = new Context(array('class' => $namespace ? $namespace . '\\' . $token[1] : $token[1], 'line' => $token[2]), $parseContext); // @todo detect end-of-class and reset $class $extends = null; $token = $this->nextToken($tokens, $parseContext); if ($token[0] === T_EXTENDS) { $classContext->extends = $this->prefixNamespace($namespace, $this->parseNamespace($tokens, $token, $parseContext), $uses); } if ($comment) { $classContext->comment = $comment; $classContext->line = $line; $this->parseContext($classContext); $comment = false; continue; } } if ($comment) { if ($token[0] == T_STATIC) { $token = $this->nextToken($tokens, $parseContext); if ($token[0] === T_VARIABLE) { // static property $this->parseContext(new Context(array('property' => substr($token[1], 1), 'static' => true, 'comment' => $comment, 'line' => $line), $classContext)); $comment = false; continue; } } if (in_array($token[0], array(T_PRIVATE, T_PROTECTED, T_PUBLIC, T_VAR))) { // Scope $token = $this->nextToken($tokens, $parseContext); if ($token[0] == T_STATIC) { $token = $this->nextToken($tokens, $parseContext); } if ($token[0] === T_VARIABLE) { // instance property $this->parseContext(new Context(array('property' => substr($token[1], 1), 'comment' => $comment, 'line' => $line), $classContext)); $comment = false; } elseif ($token[0] === T_FUNCTION) { $token = $this->nextToken($tokens, $parseContext); if ($token[0] === T_STRING) { $this->parseContext(new Context(array('method' => $token[1], 'comment' => $comment, 'line' => $line), $classContext)); $comment = false; } } continue; } elseif ($token[0] === T_FUNCTION) { $token = $this->nextToken($tokens, $parseContext); if ($token[0] === T_STRING) { $this->parseContext(new Context(array('method' => $token[1], 'comment' => $comment, 'line' => $line), $classContext)); $comment = false; } } if (in_array($token[0], array(T_NAMESPACE, T_USE)) === false) { // Skip "use" & "namespace" to prevent "never imported" warnings) // Not a doc-comment for a class, property or method? $this->parseContext(new Context(array('comment' => $comment, 'line' => $line), $classContext)); $comment = false; } } if ($token[0] === T_NAMESPACE) { $namespace = $this->parseNamespace($tokens, $token, $parseContext); continue; } if ($token[0] === T_USE) { $statements = $this->parseUseStatement($tokens, $token, $parseContext); foreach ($statements as $alias => $target) { if ($target[0] === '\\') { $target = substr($target, 1); } $imports[$alias] = $target; } $this->docParser->setImports($imports); continue; } } if ($comment) { // File ends with a T_DOC_COMMENT $this->parseContext(new Context(array('comment' => $comment, 'line' => $line), $classContext)); } $rootContext = $parseContext->getRootContext(); if ($rootContext->info) { $this->info = $rootContext->info; } if ($rootContext->authorizations) { $this->authorizations = $rootContext->authorizations; } }