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

[SOLID] Rector\SOLID\Rector\Class_\FinalizeClassesWithoutChildrenRector: should not change Doctrine Entities #1639

Closed
gnutix opened this issue Jun 21, 2019 · 18 comments
Assignees
Labels

Comments

@gnutix
Copy link
Contributor

gnutix commented Jun 21, 2019

When using the level solid, it will turn every non-final class to final, including Doctrine entities.
But as stated in Doctrine's documentation :

An entity is a lightweight, persistent domain object. An entity can be any regular PHP class observing the following restrictions: - An entity class must not be final or contain final methods.

So it would be nice if there was a way for Rector to detect @ORM\Entity annotation and not process these classes.

@gnutix gnutix changed the title [SOLID] Rector\SOLID\Rector\Class_\FinalizeClassesWithoutChildrenRector: should not apply to Doctrine entities [SOLID] Rector\SOLID\Rector\Class_\FinalizeClassesWithoutChildrenRector: does too much Jun 21, 2019
@gnutix gnutix changed the title [SOLID] Rector\SOLID\Rector\Class_\FinalizeClassesWithoutChildrenRector: does too much [SOLID] Rector\SOLID\Rector\Class_\FinalizeClassesWithoutChildrenRector: should not change Doctrine Entities Jun 21, 2019
@gnutix
Copy link
Contributor Author

gnutix commented Jun 21, 2019

Sorry, mistake from my side... It's handled correctly already.

@gnutix gnutix closed this as completed Jun 21, 2019
@gnutix gnutix reopened this Dec 24, 2019
@gnutix
Copy link
Contributor Author

gnutix commented Dec 24, 2019

I'm encountering this issue again :

docker run --rm -v $(pwd):/app rector_phar process --ansi --autoload-file /app/vendor/autoload.php --config /app/rector.yaml /app/src /app/tests --dry-run
Rector 0.6.x-dev@9a330e2
Config file: app/rector.yaml

 1062/1062 [▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓] 100%

1 file with changes
===================

1) app/src/SomeNamespace/ExpenseTag.php

    ---------- begin diff ----------
--- Original
+++ New
@@ -12,7 +12,7 @@
  * @ORM\Table(name="expense_tag")
  * @ORM\Entity()
  */
-class ExpenseTag
+final class ExpenseTag
 {
     /**
      * @var Expense
    ----------- end diff -----------

Applied rules:

 * Rector\SOLID\Rector\Class_\FinalizeClassesWithoutChildrenRector

Here's the file :

<?php

declare(strict_types=1);

namespace App\SomeNamespace;

use Doctrine\ORM\Mapping as ORM;
use SomeVendor\Snowflake\Snowflake;

/**
 * @ORM\HasLifecycleCallbacks
 * @ORM\Table(name="expense_tag")
 * @ORM\Entity()
 */
class ExpenseTag
{
    /**
     * @var Expense
     *
     * @ORM\Id
     * @ORM\ManyToOne(targetEntity="Expense", inversedBy="expenseTags")
     */
    private $expense;

    /**
     * @var Snowflake
     *
     * @ORM\Id
     * @ORM\Column(name="tag_id", type="snowflake")
     */
    private $tagId;

    public function __construct(Expense $expense, Snowflake $tagId)
    {
        $this->expense = $expense;
        $this->tagId = $tagId;
    }

    public function tagId(): Snowflake
    {
        return $this->tagId;
    }
}

I've tried adding this file "as is" in packages/SOLID/tests/Rector/Class_/FinalizeClassesWithoutChildrenRector/Fixture/custom_entity.php.inc and ran the tests with phpunit packages/SOLID/tests/Rector/Class_/FinalizeClassesWithoutChildrenRector/FinalizeClassesWithoutChildrenRectorTest.php, but they passed... :/ so I don't really understand why Rector tries to change it in my code base. Any thoughts @TomasVotruba ?

Could it be because it's running within Docker or with the phar ? (and Doctrine's annotation is not properly recognized ?)

@TomasVotruba
Copy link
Member

TomasVotruba commented Dec 25, 2019

Could it be because it's running within Docker or with the phar ? (and Doctrine's annotation is not properly recognized ?)

It probably is.

It would be great if you'd add this test case to https://github.com/rectorphp/rector-prefixed e2e test. I think it will be related to prefixed version

If you run it with --debug, what files are autoloaded?

@gnutix
Copy link
Contributor Author

gnutix commented Dec 30, 2019

I fixed it by running Rector from the root of the project (/app) instead of the root of the Docker container (/), see the 2nd point of my comment here

@gnutix gnutix closed this as completed Dec 30, 2019
@TomasVotruba
Copy link
Member

Could you update README for Docker to prevent that?

@gnutix
Copy link
Contributor Author

gnutix commented Dec 31, 2019

Not sure it applies to the rector/rector container (there seem to be some "COPY" and "WORKDIR" things in there already).

I'm not using the standard Rector container because I need to freeze Rector to a specific commit - so I'm using the PhpStan container in which I require rector instead, like this :

# Dockerfile
FROM phpstan/phpstan:0.12
RUN apk --update --progress --no-cache --repository http://dl-cdn.alpinelinux.org/alpine/v3.9/community add \
    php7-dom \
    php7-xmlwriter \
    && composer global require \
        rector/rector:"dev-master#2c3724b" #rector from December 30th 2019
WORKDIR /app
ENTRYPOINT ["rector"]

The WORKDIR /app is the part that made the difference for me. And I mount the volume with the project's root as /app (though that's not a "generic" solution code-wise, it's quite fragile...).

@TomasVotruba
Copy link
Member

Ok 👍

@gnutix gnutix reopened this Dec 31, 2019
@gnutix
Copy link
Contributor Author

gnutix commented Dec 31, 2019

False hope... the issue is not resolved. Now that I managed to run Rector without issues with our custom phpstan.neon (by renaming it), I can get rid of Docker and run rector.phar directly (which is much more robust / fast - at least on my machine). But the issue is back.

I managed to simplify it that much :

$ php rector.phar process --rule "Rector\SOLID\Rector\Class_\FinalizeClassesWithoutChildrenRector" --dry-run src/Domain/Expenses/ExpenseTag.php 
Rector 0.6.x-dev@f4bfaeb
Config file: rector.yaml

 3/3 [▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓] 100%

1 file with changes
===================

1) src/Domain/Expenses/ExpenseTag.php

    ---------- begin diff ----------
--- Original
+++ New
@@ -12,7 +12,7 @@
  * @ORM\Table(name="expense_tag")
  * @ORM\Entity()
  */
-class ExpenseTag
+final class ExpenseTag
 {
     /**
      * @var Expense
    ----------- end diff -----------

Applied rules:

 * Rector\SOLID\Rector\Class_\FinalizeClassesWithoutChildrenRector

I've tried to set the same thing in the playground repo, but it passed there :

Rector dev-master@d83ea25
Config file: rector.yaml

[parsing] src/Domain/Expenses/ExpenseTag.php
[refactoring] src/Domain/Expenses/ExpenseTag.php
[printing] src/Domain/Expenses/ExpenseTag.php

 [OK] Rector is done! 0 changed files                                                             

Any ideas ? :(

@TomasVotruba
Copy link
Member

TomasVotruba commented Dec 31, 2019

I'd debug FinalizeClassesWithoutChildrenRector

This method should return true:

This is the method:

public function isDoctrineEntityClassWithIdProperty(Class_ $class): bool
{
if (! $this->isDoctrineEntityClass($class)) {
return false;
}
foreach ($class->stmts as $classStmt) {
if (! $classStmt instanceof Property) {
continue;
}
if ($this->hasPropertyDoctrineIdTag($classStmt)) {
return true;
}
}
return false;
}

@gnutix
Copy link
Contributor Author

gnutix commented Jan 9, 2020

@TomasVotruba I took some time to investigate. Minimal code is :

# src/Whatever/Task.php
<?php

namespace Rector\Whatever;

use Doctrine\ORM\Mapping as ORM;

/**
 * @ORM\Entity
 */
class Task
{
}

Then I added dump($phpDocNode); after line $phpDocNode = $this->phpDocParser->parse($tokenIterator);, which gave :

cd bin && php rector process --dry-run --set solid ../src/Whatever/Task.php

  -phpDocNode: Rector\BetterPhpDocParser\Attributes\Ast\PhpDoc\AttributeAwarePhpDocNode^ {#6325
    +children: array:1 [
      0 => Rector\BetterPhpDocParser\Attributes\Ast\PhpDoc\AttributeAwarePhpDocTagNode^ {#6324
        +name: "@ORM\Entity"
        +value: Rector\BetterPhpDocParser\PhpDocNode\Doctrine\Class_\EntityTagValueNode^ {#6315
          -repositoryClass: null
          -readOnly: false
          #orderedVisibleItems: []
          #hasNewlineBeforeClosing: false
          #hasNewlineAfterOpening: false
          -attributes: []
        }
Rector 0.6.x-dev@9ae9807

 3/3 [▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓] 100%
                                                                                                                     
 [OK] Rector is done! 0 changed files                                                                                   

cd tmp && php rector.phar process --dry-run --set solid ../src/Whatever/Task.php

  -phpDocNode: Rector\BetterPhpDocParser\Attributes\Ast\PhpDoc\AttributeAwarePhpDocNode^ {#7834
    +children: array:1 [
      0 => Rector\BetterPhpDocParser\Attributes\Ast\PhpDoc\AttributeAwarePhpDocTagNode^ {#7833
        +name: "@ORM"
        +value: Rector\BetterPhpDocParser\Attributes\Ast\PhpDoc\AttributeAwareGenericTagValueNode^ {#7832
          +value: "\Entity"
          -attributes: []
        }
Rector 0.6.x-dev@9ae9807

 3/3 [▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓] 100%

1 file with changes
===================

1) ../src/Whatever/Task.php

    ---------- begin diff ----------
--- Original
+++ New
@@ -7,6 +7,6 @@
 /**
  * @ORM\Entity
  */
-class Task
+final class Task
 {
 }
    ----------- end diff -----------

Applied rules:
 * Rector\SOLID\Rector\Class_\FinalizeClassesWithoutChildrenRector
                                                                                                                        
 [OK] Rector is done! 1 changed files                                                                                   

So \PHPStan\PhpDocParser\Parser\PhpDocParser::parse() returns a different result, which then breaks your code.

What should I do ? Open a new issue on PHPStan's repo ? Invite ondrejmirtes to discuss it here ? Do you have any clue why this might happen ?

@TomasVotruba
Copy link
Member

TomasVotruba commented Jan 11, 2020

I have a theory... It might related to removed stubs, where are annotations located.

Without those, Rector doesn't know about these classes and cannot parse it as an annotation.

@gnutix
Copy link
Contributor Author

gnutix commented Jan 11, 2020

I don't think it could, as I reopened this issue 11 days ago and you merged the removal of the stubs/ folder from rector.phar two days ago.

Or do you mean the removal of specific stub classes, like the annotations ones ?

@gnutix
Copy link
Contributor Author

gnutix commented Jan 25, 2020

Now we know that the same issue happens with ECS's PHAR version and PhpCsFixer\Fixer\ClassNotation\FinalClassFixer.

@TomasVotruba
Copy link
Member

TomasVotruba commented Feb 23, 2020

Any update on this? Just fixed partially-reated embeddables here: #2921

If still on, we'll need failing Github Action from you to confirm. Without that, we'll have to close it as wontfix

@TomasVotruba
Copy link
Member

Demo link is needed: https://getrector.org/demo

@gnutix
Copy link
Contributor Author

gnutix commented Feb 25, 2020

I've tried running the current master version, and still get these :

10) src/Domain/Expenses/ExpenseTag.php

    ---------- begin diff ----------
--- Original
+++ New
@@ -12,7 +12,7 @@
  * @ORM\Table()
  * @ORM\Entity
  */
-class ExpenseTag
+final class ExpenseTag
 {
     /**
      * @var Expense
    ----------- end diff -----------

Applied rules:

 * Rector\SOLID\Rector\Class_\FinalizeClassesWithoutChildrenRector

And the demo does the same! 👍
https://getrector.org/demo/ebc84c7a-c901-46bf-9857-14b226cedd75

@TomasVotruba
Copy link
Member

When you remove the HasLifecycle annotatoin (fixed in master), the CI works correctly: https://getrector.org/demo/bf923428-7655-4f3b-8678-1aa5155fbd05#result

@gnutix
Copy link
Contributor Author

gnutix commented Feb 28, 2020

@TomasVotruba I upgraded to v0.7.6 and this issue still persists using rector.phar. I won't reopen, as I can't produce a demo nor a failing test, however I thought you should know. I've ignored this Rector in my code base, as ECS is already catching them too.

TomasVotruba added a commit that referenced this issue Jan 5, 2022
rectorphp/rector-src@2eb244c Add failing test fixture for AddArrayReturnDocTypeRector rectorphp/rector-src@9102b49 add dump_with_depth helper function rectorphp/rector-src@679be38 add strict return type based return doc rectorphp/rector-src@8fc1759 check for iterable type before adding return type rectorphp/rector-src@d8e1531 remove non-array test fixtures rectorphp/rector-src@ff5f80c keep nullable array type rectorphp/rector-src@b45ff7a remove unused test rectorphp/rector-src@49f1596 Merge pull request #1639 from rectorphp/tv-make-array-return-doc-work-only-with-arrays
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants