Skip to content

Convert Doctrine annotations from phpdoc-parser nodes, remove StaticDoctrineAnnotationParser - #8881

Merged
TomasVotruba merged 2 commits into
rectorphp:mainfrom
ondrejmirtes:doctrine-native-nodes
Oct 29, 2026
Merged

TomasVotruba merged 2 commits into
rectorphp:mainfrom
ondrejmirtes:doctrine-native-nodes

Conversation

@ondrejmirtes

@ondrejmirtes ondrejmirtes commented Oct 28, 2026 •

Copy link
Copy Markdown
Contributor

And now a dessert 😉

phpdoc-parser already parses Doctrine annotations (since 1.22.0) to DoctrineTagValueNode. Before, DoctrineAnnotationDecorator printed these nodes back to a string and parsed the string again with StaticDoctrineAnnotationParser, ArrayParser and PlainValueParser. Now the decorator converts the phpdoc-parser nodes directly to DoctrineAnnotationTagValueNode, CurlyListNode, ArrayItemNode and StringNode. The public API does not change.

Removed: StaticDoctrineAnnotationParser, ArrayParser, PlainValueParser, NextTokenType, and the @\FQN text and description splitting in DoctrineAnnotationDecorator.

Changes in behaviour. Rector now uses the phpdoc-parser and Doctrine semantics:

  • A multi-line string keeps the * line prefixes in the docblock node, as in doctrine/annotations. When Rector converts it to an attribute, it removes the prefixes.
  • A {} list in a {} list is a CurlyListNode. Before, it was a PHP array.
  • An integer key stays an integer: {1="Foo"}. ArrayItemNode now prints integer keys. Before, a changed annotation lost them.
  • A nested annotation that is a positional argument has the key null. Before, the key was 0.
  • Single-quoted strings and quoted argument keys are not Doctrine syntax: @Foo('bar'), @Foo("key"="value"). phpdoc-parser gives InvalidTagValueNode for them, and Rector does not change them. Fixtures are updated.

Needs rectorphp/rector-doctrine#543 to be merged first.

@ondrejmirtes
ondrejmirtes marked this pull request as draft October 28, 2026 19:17
@ondrejmirtes
ondrejmirtes force-pushed the doctrine-native-nodes branch 3 times, most recently from 1293dde to 54899d0 Compare October 28, 2026 20:10
@ondrejmirtes
ondrejmirtes marked this pull request as ready for review October 28, 2026 21:58
@ondrejmirtes
ondrejmirtes force-pushed the doctrine-native-nodes branch from 54899d0 to 1815799 Compare October 29, 2026 05:56
@ondrejmirtes

Copy link
Copy Markdown
Contributor Author

This is ready.

@TomasVotruba

Copy link
Copy Markdown
Member

Thanks the updates 👍 Great to have these glitches resolve out of the box. I think we can leave the invalid docblock in Doctrine tags behind.

Let's merge 🚀

@TomasVotruba
TomasVotruba merged commit 9a6fdb0 into rectorphp:main Oct 29, 2026
51 checks passed
@bbrala

bbrala commented Oct 30, 2026

Copy link
Copy Markdown
Contributor

MIght regres drupal rector a bit this, not 100% sure yet. We used part of this code to do annotation to attribute conversion for Drupal

@TomasVotruba

Copy link
Copy Markdown
Member

We still do annotation to attributes conversion as before.
Can you share broken example? https://getrector.com/demo uses latest dev-main Rector

@bbrala

bbrala commented Oct 30, 2026

Copy link
Copy Markdown
Contributor

Yeah will investigate and post back if there work in rector.

@bbrala

bbrala commented Oct 30, 2026 •

Copy link
Copy Markdown
Contributor

Well, seems this is not really an issue, but i did find a discrepancy between doctrines parser and rector.

https://getrector.com/demo/0ffc42df-7754-41b2-969e-b8e30dde6c1e

- doctrine/annotations (upstream, 2.0.x): the lexer skips whitespace and * between tokens; 
   in DocLexer::getNonCatchablePatterns() they are ['\s+', '\*+', '(.)']. The parser's MethodCall() 
   then checks whether the next token is (, so a newline plus * before the ( doesn't matter.
- Drupal's copy (Drupal\Component\Annotation\Doctrine\DocParser) uses the same lexer 
   patterns. I ran it on both forms from the demo and got the same values for each:
      next line: [{"id":"next_line","label":"Next line"}]
      same line: [{"id":"same_line","label":"Same line"}]

The cause is that PHPStan's phpdoc-parser, which Rector uses, only treats the tag as a Doctrine annotation when the ( comes straight after the tag name. Otherwise it gives the tag a plain text value. Rector's rule then turns that plain tag into an attribute with no arguments and drops the docblock text, so data is lost.

But it seems this might not be a rector issue but a phpstan issue ;x

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

4 participants