Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Symfony 7 support continuation #175

Merged
merged 18 commits into from
Jun 11, 2024
Merged
Show file tree
Hide file tree
Changes from 4 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
12 changes: 6 additions & 6 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,17 @@
}
],
"require": {
"php": "^7.2 || ^8.0",
"php": "^8.1",
antiftw marked this conversation as resolved.
Show resolved Hide resolved
"nikic/php-parser": "^3.0 || ^4.0",
"symfony/finder": "^3.4 || ^4.4 || ^5.0 || ^6.0",
"symfony/finder": "^3.4 || ^4.4 || ^5.0 || ^6.0 || ^7.0",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But I also think we should drop support for Symfony 3.4 and 4.4 too. Those are officially unmaintained anyway, so it's time to drop it. And I think we can bump 5 & 6 to 5.4 & 6.4 - I hope it will help us to make tests green easier

So, the ideal Symfony support version constraints should be: ^5.4 || ^6.4 || ^7.0 here and in other places.

Wdyt?

Copy link
Contributor Author

@antiftw antiftw May 29, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I also thought something (see remark in PR message) similar, but I thought I'd await your opinion about this since you have more in depth knowledge of the repository.

I'll update the PR.

UPDATE: Done.

Copy link
Contributor Author

@antiftw antiftw May 29, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I managed to fix some of the tests, however the other ones I'm not really sure what to do with. For the the remaining errors and failures it's related to my remark in the PR message. I'll provide an example:

In some of the code, the used annotations are in the middle of variables

        $builder
            ->add('foobar', TextType::class, array(
                /** @Desc("Foobar:") */
                'label' => 'test_label',
            ));

But if you try to put an attribute there it will give syntax errors. Furthermore, attributes only seem to be allowed in front of class/function definitions and class properties.

I'm not aware of any workarounds for this, so I'm not really sure how one would want to support this kind of functionality using attributes. Any of you have an idea?

EDIT:

I noticed the PHP STAN and CSFixer tests were failing because of an older PHP version, so I added ^7.3 ("back") to composer.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see, that's a bummer :/

I'm not sure. Probably we can use class properties where we will use the attribute and then reuse the property in that code? I'm not sure if this will stop working because of this though.

Or I wonder if we can continue using PHP annotations in this specific spot maybe?

Copy link
Contributor Author

@antiftw antiftw May 29, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree, that felt like an obvious thing to try, but sadly that didn't work. About using 2 separate readers, I'm guessing that's probably possible.

Also did not think about the fact that adding 7.3 back does not allow typing of class properties, so I'm not sure what you would prefer? Want me to remove the typing or bump it to 8.*?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you! ❤️

Copy link
Contributor Author

@antiftw antiftw Jun 4, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I have some bad news. I think I found what you were talking about.

I was reverting the changes, and when I came across this file tests/Functional/Visitor/Php/Symfony/ValidationAnnotationTest.php I was unable to change it back to AnnotationLoader without PHPstorm complaining.

Some furthing digging dug up:

Reading annotations in PHP files is deprecated since Symfony 6.4. Also, the AnnotationLoader was deprecated in Symfony 6.4.

soooo.... what now? :)

EDIT:

Ok, so I've just put back the AttributeLoader again and for those cases I am able to use the attributes (It's not actually this packages custom attributes, but rather a translation inside a validator attribute):

<?php

namespace Translation\Extractor\Tests\Resources\Php\Symfony;

use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Component\Validator\Constraints\NotBlank;

class ValidatorAnnotation
{

    #[Assert\NotNull(message: "start.null")]
    private string $start;

    #[NotBlank(message: "end.blank")]
    private string $end;
}

The final error it's failing on now is \Translation\Extractor\Tests\Functional\Visitor\Php\Symfony\ValidationAnnotationTest::testExtractAnnotationError

Which seems weird, because you would expect this to error, but for some reason it does not:

<?php

namespace Translation\Extractor\Tests\Resources\Php\Symfony;

class ValidatorAnnotationError
{
    #[Foobar('this should be an error')]
    private string $foo;
}

(I just replaced the annotation with its attribute counterpart)

Maybe rewrite the test that it does not check for errors but just to check if it did not find any valid attributes? When I dump the collection variable inside the test it just has two empty arrays:

object(Translation\Extractor\Model\SourceCollection)#43047 (2) {
   ["sourceLocations":"Translation\Extractor\Model\SourceCollection":private]=>
      array(0) {
   }
   ["errors":"Translation\Extractor\Model\SourceCollection":private]=>
      array(0) {
   }
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, heck... that's exactly the problem I faced, now I remember it :/

So, I'm not sure about the best solution here, I'm open to ideas

Copy link
Contributor Author

@antiftw antiftw Jun 4, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:)

Perhaps a way to test this is to change the test from

 public function testExtractAnnotationError()
    {
        $factory = new LazyLoadingMetadataFactory(new AttributeLoader());
        $extractor = new ValidationAnnotation($factory);
        $collection = $this->getSourceLocations($extractor, Resources\Php\Symfony\ValidatorAnnotationError::class);

        $errors = $collection->getErrors();
        $this->assertCount(1, $errors);
    }

to

 public function testExtractAnnotationError()
    {
        $factory = new LazyLoadingMetadataFactory(new AttributeLoader());
        $extractor = new ValidationAnnotation($factory);
        $collection = $this->getSourceLocations($extractor, Resources\Php\Symfony\ValidatorAnnotationError::class);

        $this->assertCount(0, $collection);
    }

Since apparently the AttributeReader does not give in errors when putting in non-existing attributes to extract. It rather just results in no attributes.

I'd like to hear what you think about this change. I've committed and pushed it so you can look over the changes without me copy pasting just snippets. To be clear, this commit passes the local vendor/bin/simple-phpunit testsuite.

Copy link
Contributor Author

@antiftw antiftw Jun 7, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok so I've had some pressing other issues to take care of but this evening I had some spare time to look into this PR.

I figured out how to run the GitHub actions locally so I was able to fix most of the warnings and errors that the statistical tests threw. However I've got one left that I don't really understand, and I was hoping it makes more sense to you:

[Static analysis/PHPStan     ]   ❗  ::error file=src/Visitor/Php/Symfony/AbstractFormType.php,line=,col=0::Ignored error pattern #^Access to an undefined property PhpParser\\Node\:\:\$args\.$# in path /home/antiftw/repos/forks/php-translation-extractor/src/Visitor/Php/Symfony/AbstractFormType.php was not matched in reported errors.

I was able to fix a similar error by adding a null check in the code. I tried the same here, but no luck. However a notable difference is that the other error did indicate a line number where it occurred, and this one does not. Also this one includes a part ("was not matched in reported errors") that the other did not.

EDIT:

Minor change and it seems to be passing the last test too now.

EDIT2: The package I'm using it in required a bump of a dependency of this package (nikic/php-parser) so that it could also use 5.0.0. This caused some issues but they are fixed too. All tests pass locally, so fingers crossed and it should work now.

"twig/twig": "^2.0 || ^3.0",
"doctrine/annotations": "^1.7 || ^2.0"
},
"require-dev": {
"symfony/phpunit-bridge": "^5.0 || ^6.0",
"symfony/translation": "^3.4 || ^4.4 || ^5.0 || ^6.0",
"symfony/validator": "^3.4 || ^4.4 || ^5.0 || ^6.0",
"symfony/twig-bridge": "^3.4 || ^4.4 || ^5.0 || ^6.0",
"symfony/phpunit-bridge": "^5.0 || ^6.0 || ^7.0",
"symfony/translation": "^3.4 || ^4.4 || ^5.0 || ^6.0 || ^7.0",
"symfony/validator": "^3.4 || ^4.4 || ^5.0 || ^6.0 || ^7.0",
"symfony/twig-bridge": "^3.4 || ^4.4 || ^5.0 || ^6.0 || ^7.0",
"knplabs/knp-menu": "^3.1"
},
"autoload": {
Expand Down
3 changes: 3 additions & 0 deletions src/Annotation/Desc.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,12 @@

namespace Translation\Extractor\Annotation;

use JetBrains\PhpStorm\Deprecated;
antiftw marked this conversation as resolved.
Show resolved Hide resolved

/**
* @Annotation
*/
#[Deprecated]
final class Desc
{
/**
Expand Down
3 changes: 3 additions & 0 deletions src/Annotation/Ignore.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,12 @@

namespace Translation\Extractor\Annotation;

use JetBrains\PhpStorm\Deprecated;

/**
* @Annotation
*/
#[Deprecated]
final class Ignore
{
}
3 changes: 3 additions & 0 deletions src/Annotation/Translate.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,12 @@

namespace Translation\Extractor\Annotation;

use JetBrains\PhpStorm\Deprecated;

/**
* @Annotation
*/
#[Deprecated]
class Translate
{
/**
Expand Down
18 changes: 18 additions & 0 deletions src/Attribute/Desc.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php

/*
* This file is part of the PHP Translation package.
*
* (c) PHP Translation team <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Translation\Extractor\Attribute;

#[\Attribute]
final class Desc
{
public string $text;
}
17 changes: 17 additions & 0 deletions src/Attribute/Ignore.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php

/*
* This file is part of the PHP Translation package.
*
* (c) PHP Translation team <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Translation\Extractor\Attribute;

#[\Attribute]
final class Ignore
{
}
33 changes: 33 additions & 0 deletions src/Attribute/Translate.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?php

/*
* This file is part of the PHP Translation package.
*
* (c) PHP Translation team <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Translation\Extractor\Attribute;

#[\Attribute]
class Translate
{
private string $domain = '';

/**
* Translate constructor.
*/
public function __construct(array $values = ['domain' => 'messages'])
{
if (isset($values['domain'])) {
$this->domain = $values['domain'];
}
}

public function getDomain(): string
{
return $this->domain;
}
}
2 changes: 1 addition & 1 deletion src/Extractor.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ final class Extractor
/**
* @var FileExtractor[]
*/
private $fileExtractors = [];
private array $fileExtractors = [];

public function extract(Finder $finder): SourceCollection
{
Expand Down
2 changes: 1 addition & 1 deletion src/FileExtractor/PHPFileExtractor.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ final class PHPFileExtractor implements FileExtractor
/**
* @var Visitor[]|NodeVisitor[]
*/
private $visitors = [];
private array $visitors = [];

/**
* {@inheritdoc}
Expand Down
6 changes: 2 additions & 4 deletions src/FileExtractor/TwigFileExtractor.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,10 @@ final class TwigFileExtractor extends AbstractExtension implements FileExtractor
/**
* @var NodeVisitorInterface[]
*/
private $visitors = [];
private $twig;
private array $visitors = [];

public function __construct(Environment $twig)
public function __construct(private readonly Environment $twig)
{
$this->twig = $twig;
$twig->addExtension($this);
}

Expand Down
15 changes: 5 additions & 10 deletions src/Model/Error.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,11 @@
*/
final class Error
{
private $message;
private $path;
private $line;

public function __construct(string $message, string $path, int $line)
{
$this->message = $message;
$this->path = (string) $path;
$this->line = $line;
}
public function __construct(
private readonly string $message,
private readonly string $path,
private readonly int $line
){}

public function getMessage(): string
{
Expand Down
4 changes: 2 additions & 2 deletions src/Model/SourceCollection.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,12 @@ final class SourceCollection implements \Countable, \IteratorAggregate
/**
* @var SourceLocation[]
*/
private $sourceLocations = [];
private array $sourceLocations = [];

/**
* @var Error[]
*/
private $errors = [];
private array $errors = [];

/**
* {@inheritdoc}
Expand Down
21 changes: 6 additions & 15 deletions src/Model/SourceLocation.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,21 +16,12 @@
*/
final class SourceLocation
{
/**
* Translation key.
*/
private $message;
private $path;
private $line;
private $context;

public function __construct(string $message, string $path, int $line, array $context = [])
{
$this->message = $message;
$this->path = (string) $path;
$this->line = $line;
$this->context = $context;
}
public function __construct(
private readonly string $message, /** Translation key. */
private readonly string $path,
private readonly int $line,
private readonly array $context = []
) {}

/**
* Create a source location from your current location.
Expand Down
16 changes: 5 additions & 11 deletions src/Visitor/BaseVisitor.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@
use Doctrine\Common\Annotations\DocParser;
use PhpParser\Node;
use Symfony\Component\Finder\SplFileInfo;
use Translation\Extractor\Annotation\Desc;
use Translation\Extractor\Annotation\Ignore;
use Translation\Extractor\Attribute\Desc;
use Translation\Extractor\Attribute\Ignore;
use Translation\Extractor\Model\Error;
use Translation\Extractor\Model\SourceCollection;
use Translation\Extractor\Model\SourceLocation;
Expand All @@ -27,17 +27,11 @@
*/
abstract class BaseVisitor implements Visitor
{
protected $collection;
protected $file;
private ?DocParser $docParser = null;

/**
* @var DocParser
*/
private $docParser;
protected SourceCollection $collection;
protected SplFileInfo $file;

/**
* {@inheritdoc}
*/
public function init(SourceCollection $collection, SplFileInfo $file): void
{
$this->collection = $collection;
Expand Down
13 changes: 3 additions & 10 deletions src/Visitor/Php/Knp/Menu/AbstractKnpMenuVisitor.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,20 +21,14 @@
*/
abstract class AbstractKnpMenuVisitor extends BasePHPVisitor implements NodeVisitor
{
/**
* @var bool
*/
private $isKnpMenuBuildingMethod = false;
private bool $isKnpMenuBuildingMethod = false;

/**
* @var string|bool
*/
private $domain;
private string|bool|null $domain;

/**
* @var SourceLocation[]
*/
private $sourceLocations = [];
private array $sourceLocations = [];

public function beforeTraverse(array $nodes): ?Node
{
Expand Down Expand Up @@ -126,7 +120,6 @@ public function afterTraverse(array $nodes): ?Node
return null;
}

/** @var SourceLocation $location */
foreach ($this->sourceLocations as $location) {
if (null !== $this->domain) {
$context = $location->getContext();
Expand Down
19 changes: 6 additions & 13 deletions src/Visitor/Php/SourceLocationContainerVisitor.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,8 @@
*/
final class SourceLocationContainerVisitor extends BasePHPVisitor implements NodeVisitor
{
/**
* @var string
*/
private $namespace = '';

/**
* @var array
*/
private $useStatements = [];
private string $namespace = '';
private array $useStatements = [];

/**
* {@inheritdoc}
Expand All @@ -49,16 +42,16 @@ public function enterNode(Node $node): ?Node
if ($node instanceof Node\Stmt\Namespace_) {
if (isset($node->name)) {
// Save namespace of this class for later.
$this->namespace = implode('\\', $node->name->parts);
$this->namespace = implode('\\', $node->name->getParts());
}
$this->useStatements = [];

return null;
}

if ($node instanceof Node\Stmt\UseUse) {
$key = isset($node->alias) ? $node->alias : $node->name->parts[\count($node->name->parts) - 1];
$this->useStatements[(string) $key] = implode('\\', $node->name->parts);
$key = $node->alias ?? $node->name->getParts()[\count($node->name->getParts()) - 1];
$this->useStatements[(string) $key] = implode('\\', $node->name->getParts());

return null;
}
Expand All @@ -69,7 +62,7 @@ public function enterNode(Node $node): ?Node

$isContainer = false;
foreach ($node->implements as $interface) {
$name = implode('\\', $interface->parts);
$name = implode('\\', $interface->getParts());
if (isset($this->useStatements[$name])) {
$name = $this->useStatements[$name];
}
Expand Down
9 changes: 2 additions & 7 deletions src/Visitor/Php/Symfony/AbstractFormType.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,9 @@ abstract class AbstractFormType extends BasePHPVisitor implements NodeVisitor
/**
* @var SourceLocation[]
*/
private $sourceLocations = [];
private array $sourceLocations = [];

/**
* @var string|null
*/
private $defaultDomain;
private string|null $defaultDomain;

/**
* {@inheritdoc}
Expand Down Expand Up @@ -121,7 +118,6 @@ private function parseDefaultsCall(Node $node): void
}

// check if a translation_domain is set as a default option
$domain = null;
foreach ($node->args[0]->value->items as $item) {
if (!$item->key instanceof Node\Scalar\String_) {
continue;
Expand All @@ -142,7 +138,6 @@ private function parseDefaultsCall(Node $node): void
*/
public function afterTraverse(array $nodes): ?Node
{
/** @var SourceLocation $location */
foreach ($this->sourceLocations as $location) {
if (null !== $this->defaultDomain) {
$context = $location->getContext();
Expand Down
4 changes: 2 additions & 2 deletions src/Visitor/Php/Symfony/Constraint.php
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ public function enterNode(Node $node): ?Node
return null;
}

$parts = $className->parts;
$parts = $className->getParts();
$isConstraintClass = false;

// we need to check every part since `Assert\NotBlank` would be split in 2 different pieces
Expand Down Expand Up @@ -151,7 +151,7 @@ public function enterNode(Node $node): ?Node
continue;
}

// there could be false positives but it should catch most of the useful properties
// there could be false positives, but it should catch most of the useful properties
// (e.g. `message`, `minMessage`)
if (false === stripos($item->key->value, 'message')) {
continue;
Expand Down
Loading
Loading