Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
"nikic/php-parser": "^5.0",
"symfony/finder": "^5.4 || ^6.4 || ^7.0",
"twig/twig": "^2.0 || ^3.0",
"doctrine/annotations": "^1.7 || ^2.0"
"phpstan/phpdoc-parser": "^2.3"
},
"require-dev": {
"symfony/phpunit-bridge": "^5.4 || ^6.4 || ^7.0",
Expand Down
2 changes: 1 addition & 1 deletion phpunit.xml.dist
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
convertWarningsToExceptions="true"
processIsolation="false"
stopOnFailure="false"
bootstrap="./tests/bootstrap.php"
bootstrap="./vendor/autoload.php"
>

<php>
Expand Down
2 changes: 2 additions & 0 deletions src/Annotation/Desc.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
namespace Translation\Extractor\Annotation;

/**
* @deprecated since 2.3, this class is not used anymore. @Desc is now considered as a PHPDoc tag.
*
* @Annotation
*/
final class Desc
Expand Down
2 changes: 2 additions & 0 deletions src/Annotation/Ignore.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
namespace Translation\Extractor\Annotation;

/**
* @deprecated since 2.3, this class is not used anymore. @Ignore is now considered as a PHPDoc tag.
*
* @Annotation
*/
final class Ignore
Expand Down
2 changes: 2 additions & 0 deletions src/Annotation/Translate.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
namespace Translation\Extractor\Annotation;

/**
* @deprecated since 2.3, this class is not used anymore. @Translate is now considered as a PHPDoc tag.
*
* @Annotation
*/
class Translate
Expand Down
58 changes: 34 additions & 24 deletions src/Visitor/BaseVisitor.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,16 @@

namespace Translation\Extractor\Visitor;

use Doctrine\Common\Annotations\DocParser;
use PhpParser\Node;
use PHPStan\PhpDocParser\Ast\ConstExpr\DoctrineConstExprStringNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\Doctrine\DoctrineTagValueNode;
use PHPStan\PhpDocParser\Lexer\Lexer;
use PHPStan\PhpDocParser\Parser\ConstExprParser;
use PHPStan\PhpDocParser\Parser\PhpDocParser;
use PHPStan\PhpDocParser\Parser\TokenIterator;
use PHPStan\PhpDocParser\Parser\TypeParser;
use PHPStan\PhpDocParser\ParserConfig;
use Symfony\Component\Finder\SplFileInfo;
use Translation\Extractor\Annotation\Desc;
use Translation\Extractor\Annotation\Ignore;
use Translation\Extractor\Model\Error;
use Translation\Extractor\Model\SourceCollection;
use Translation\Extractor\Model\SourceLocation;
Expand All @@ -27,7 +32,8 @@
*/
abstract class BaseVisitor implements Visitor
{
private ?DocParser $docParser = null;
protected ?Lexer $lexer = null;
protected ?PhpDocParser $phpDocParser = null;

protected ?SourceCollection $collection = null;
protected SplFileInfo $file;
Expand All @@ -54,9 +60,11 @@ protected function addError(Node $node, string $errorMessage): void
$line = $node->getAttribute('startLine');
}
if (null !== $docComment) {
$context = 'file '.$file.' near line '.$line;
foreach ($this->getDocParser()->parse($docComment->getText(), $context) as $annotation) {
if ($annotation instanceof Ignore) {
$phpDocNode = $this->getPhpDocParser()->parse(
new TokenIterator($this->lexer->tokenize($docComment->getText()))
);
foreach ($phpDocNode->getTags() as $tag) {
if ('@Ignore' === $tag->name) {
return;
}
}
Expand All @@ -78,36 +86,38 @@ protected function getLocation(string $text, int $line, ?Node $node = null, arra
{
$file = $this->getAbsoluteFilePath();
if (null !== $node && null !== $docComment = $node->getDocComment()) {
$parserContext = 'file '.$file.' near line '.$line;
foreach ($this->getDocParser()->parse($docComment->getText(), $parserContext) as $annotation) {
if ($annotation instanceof Ignore) {
$phpDocNode = $this->getPhpDocParser()->parse(
new TokenIterator($this->lexer->tokenize($docComment->getText()))
);
foreach ($phpDocNode->getTags() as $tag) {
if ('@Ignore' === $tag->name) {
return null;
} elseif ($annotation instanceof Desc) {
$context['desc'] = $annotation->text;
} elseif ('@Desc' === $tag->name && $tag->value instanceof DoctrineTagValueNode) {
if ([] !== $tag->value->annotation->arguments) {
$context['desc'] = DoctrineConstExprStringNode::unescape($tag->value->annotation->arguments[0]->value);
}
}
}
}

return new SourceLocation($text, $file, $line, $context);
}

private function getDocParser(): DocParser
protected function getPhpDocParser(): PhpDocParser
{
if (null === $this->docParser) {
$this->docParser = new DocParser();

$this->docParser->setImports([
'ignore' => Ignore::class,
'desc' => Desc::class,
]);
$this->docParser->setIgnoreNotImportedAnnotations(true);
if (null === $this->phpDocParser) {
$config = new ParserConfig(usedAttributes: []);
$this->lexer = new Lexer($config);
$constExprParser = new ConstExprParser($config);
$typeParser = new TypeParser($config, $constExprParser);
$this->phpDocParser = new PhpDocParser($config, $typeParser, $constExprParser);
}

return $this->docParser;
return $this->phpDocParser;
}

public function setDocParser(DocParser $docParser): void
public function setPhpDocParser(PhpDocParser $phpDocParser): void
{
$this->docParser = $docParser;
$this->phpDocParser = $phpDocParser;
}
}
24 changes: 9 additions & 15 deletions src/Visitor/Php/Symfony/FormTypeChoices.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,9 @@

namespace Translation\Extractor\Visitor\Php\Symfony;

use Doctrine\Common\Annotations\DocParser;
use PhpParser\Node;
use PhpParser\NodeVisitor;
use Translation\Extractor\Annotation\Ignore;
use PHPStan\PhpDocParser\Parser\TokenIterator;
use Translation\Extractor\Model\SourceLocation;

/**
Expand Down Expand Up @@ -134,20 +133,15 @@ public function enterNode(Node $node): ?Node

protected function isIgnored(Node $node): bool
{
// because of getDocParser method is private, we have to create a new custom instance
$docParser = new DocParser();
$docParser->setImports([
'ignore' => Ignore::class,
]);
$docParser->setIgnoreNotImportedAnnotations(true);
if (null !== $docComment = $node->getDocComment()) {
foreach ($docParser->parse($docComment->getText()) as $annotation) {
if ($annotation instanceof Ignore) {
return true;
}
}
if (null === $node->getDocComment()) {
return false;
}

return false;
$phpDocNode = $this->getPhpDocParser()->parse(
new TokenIterator($this->lexer->tokenize($node->getDocComment()->getText()))
);
$ignoreTags = $phpDocNode->getTagsByName('@Ignore');

return \count($ignoreTags) > 0;
}
}
11 changes: 2 additions & 9 deletions src/Visitor/Php/Symfony/ValidationAnnotation.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@

namespace Translation\Extractor\Visitor\Php\Symfony;

use Doctrine\Common\Annotations\AnnotationException;
use PhpParser\Node;
use PhpParser\NodeVisitor;
use Symfony\Component\Validator\Mapping\ClassMetadata;
Expand Down Expand Up @@ -60,14 +59,8 @@ public function enterNode(Node $node): ?Node
return null;
}

try {
/** @var ClassMetadata $metadata */
$metadata = $this->metadataFactory->getMetadataFor($name);
} catch (AnnotationException $e) {
$this->addError($node, \sprintf('Could not parse class "%s" for annotations. %s', $this->namespace, $e->getMessage()));

return null;
}
/** @var ClassMetadata $metadata */
$metadata = $this->metadataFactory->getMetadataFor($name);

if (!$metadata->hasConstraints() && !\count($metadata->getConstrainedProperties())) {
return null;
Expand Down
39 changes: 18 additions & 21 deletions src/Visitor/Php/TranslateAnnotationVisitor.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,12 @@

namespace Translation\Extractor\Visitor\Php;

use Doctrine\Common\Annotations\DocParser;
use PhpParser\Comment;
use PhpParser\Node;
use PhpParser\NodeVisitor;
use PHPStan\PhpDocParser\Ast\ConstExpr\DoctrineConstExprStringNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\Doctrine\DoctrineTagValueNode;
use PHPStan\PhpDocParser\Parser\TokenIterator;
use Translation\Extractor\Annotation\Translate;

/**
Expand All @@ -24,22 +26,6 @@
*/
class TranslateAnnotationVisitor extends BasePHPVisitor implements NodeVisitor
{
protected ?DocParser $translateDocParser = null;

private function getTranslateDocParser(): DocParser
{
if (null === $this->translateDocParser) {
$this->translateDocParser = new DocParser();

$this->translateDocParser->setImports([
'translate' => Translate::class,
]);
$this->translateDocParser->setIgnoreNotImportedAnnotations(true);
}

return $this->translateDocParser;
}

public function enterNode(Node $node): ?Node
{
// look for strings
Expand All @@ -58,10 +44,21 @@ public function enterNode(Node $node): ?Node
return null;
}

foreach ($this->getTranslateDocParser()->parse($comment->getText()) as $annotation) {
// add phrase to dictionary
$this->addLocation($node->value, $node->getAttribute('startLine'), $node, ['domain' => $annotation->getDomain()]);

$phpDocNode = $this->getPhpDocParser()->parse(
new TokenIterator($this->lexer->tokenize($comment->getText()))
);

$translateTags = $phpDocNode->getTagsByName('@Translate');
if ([] !== $translateTags) {
$domain = 'messages';
if ($translateTags[0]->value instanceof DoctrineTagValueNode) {
foreach ($translateTags[0]->value->annotation->arguments as $argument) {
if ('domain' === $argument->key->name) {
$domain = DoctrineConstExprStringNode::unescape($argument->value);
}
}
}
$this->addLocation($node->value, $node->getAttribute('startLine'), $node, ['domain' => $domain]);
break;
}
}
Expand Down
22 changes: 0 additions & 22 deletions tests/bootstrap.php

This file was deleted.

Loading