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

Fix token parsing on latest PHP 8 version, fixes #339 #344

Closed
wants to merge 1 commit into from
Closed

Fix token parsing on latest PHP 8 version, fixes #339 #344

wants to merge 1 commit into from

Conversation

shyim
Copy link
Contributor

@shyim shyim commented Jul 31, 2020

This fixes the failing tests from TokenParser on PHP 8.

There are 10 left Failing tests. But it should be fixed by an PHPUnit Update

PHPUnit 7.5.20 by Sebastian Bergmann and contributors.

.........................................................E....E  63 / 456 ( 13%)
EEEEEEE........................................................ 126 / 456 ( 27%)
............................................................... 189 / 456 ( 41%)
............................................................... 252 / 456 ( 55%)
............................................................... 315 / 456 ( 69%)
......................................S........................ 378 / 456 ( 82%)
..................E.....................S...................... 441 / 456 ( 96%)
...............                                                 456 / 456 (100%)

Time: 812 ms, Memory: 10.00 MB

There were 10 errors:

1) Doctrine\Tests\Common\Annotations\AnnotationRegistryTest::testRegisterLoaderIfNotExistsOnlyRegisteresSameLoaderOnce
ParseError: syntax error, unexpected token "match", expecting variable

/var/www/html/annotations/tests/Doctrine/Tests/Common/Annotations/AnnotationRegistryTest.php:183

2) Doctrine\Tests\Common\Annotations\CachedReaderTest::testIgnoresStaleCache
Method ReflectionParameter::getClass() is deprecated

/var/www/html/annotations/tests/Doctrine/Tests/Common/Annotations/CachedReaderTest.php:228
/var/www/html/annotations/tests/Doctrine/Tests/Common/Annotations/CachedReaderTest.php:22

3) Doctrine\Tests\Common\Annotations\CachedReaderTest::testIgnoresStaleCacheWithParentClass
Method ReflectionParameter::getClass() is deprecated

/var/www/html/annotations/tests/Doctrine/Tests/Common/Annotations/CachedReaderTest.php:228
/var/www/html/annotations/tests/Doctrine/Tests/Common/Annotations/CachedReaderTest.php:34

4) Doctrine\Tests\Common\Annotations\CachedReaderTest::testIgnoresStaleCacheWithTraits
Method ReflectionParameter::getClass() is deprecated

/var/www/html/annotations/tests/Doctrine/Tests/Common/Annotations/CachedReaderTest.php:228
/var/www/html/annotations/tests/Doctrine/Tests/Common/Annotations/CachedReaderTest.php:46

5) Doctrine\Tests\Common\Annotations\CachedReaderTest::testIgnoresStaleCacheWithTraitsThatUseOtherTraits
Method ReflectionParameter::getClass() is deprecated

/var/www/html/annotations/tests/Doctrine/Tests/Common/Annotations/CachedReaderTest.php:228
/var/www/html/annotations/tests/Doctrine/Tests/Common/Annotations/CachedReaderTest.php:60

6) Doctrine\Tests\Common\Annotations\CachedReaderTest::testIgnoresStaleCacheWithInterfacesThatExtendOtherInterfaces
Method ReflectionParameter::getClass() is deprecated

/var/www/html/annotations/tests/Doctrine/Tests/Common/Annotations/CachedReaderTest.php:228
/var/www/html/annotations/tests/Doctrine/Tests/Common/Annotations/CachedReaderTest.php:76

7) Doctrine\Tests\Common\Annotations\CachedReaderTest::testUsesFreshCacheWithTraitsThatUseOtherTraits
Method ReflectionParameter::getClass() is deprecated

/var/www/html/annotations/tests/Doctrine/Tests/Common/Annotations/CachedReaderTest.php:268
/var/www/html/annotations/tests/Doctrine/Tests/Common/Annotations/CachedReaderTest.php:93

8) Doctrine\Tests\Common\Annotations\CachedReaderTest::testPurgeLoadedAnnotations
Method ReflectionParameter::getClass() is deprecated

/var/www/html/annotations/tests/Doctrine/Tests/Common/Annotations/CachedReaderTest.php:228
/var/www/html/annotations/tests/Doctrine/Tests/Common/Annotations/CachedReaderTest.php:109

9) Doctrine\Tests\Common\Annotations\CachedReaderTest::testAvoidCallingFilemtimeTooMuch
Method ReflectionParameter::getClass() is deprecated

/var/www/html/annotations/tests/Doctrine/Tests/Common/Annotations/CachedReaderTest.php:157

10) Doctrine\Tests\Common\Annotations\PhpParserTest::testClassFileDoesNotExist
Error: Call to undefined method ReflectionUnionType::getName()

/var/www/html/annotations/tests/Doctrine/Tests/Common/Annotations/PhpParserTest.php:63

ERRORS!
Tests: 456, Assertions: 1049, Errors: 10, Skipped: 2.

fixes #339

@greg0ire

This comment has been minimized.

@greg0ire greg0ire changed the base branch from master to 1.10.x July 31, 2020 18:52
Copy link
Member

@greg0ire greg0ire left a comment

Choose a reason for hiding this comment

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

Your commit message should mention https://wiki.php.net/rfc/namespaced_names_as_token , otherwise it's hard to understand why we need this. Also, please try to reduce the amount of copy/paste, it makes it hard to see what changes between the 2 methods, so it does not help understanding the commit.

Good job though, looks like it fixes the errors 👍

@jkufner
Copy link
Contributor

jkufner commented Aug 1, 2020

More importantly, link that RFC from the code, so that when we return it a few years later, we know what happened.

@greg0ire
Copy link
Member

greg0ire commented Aug 1, 2020

I'm attempting to address the at() warning issue at #345

@@ -172,7 +222,7 @@ public function parseUseStatements($namespaceName)
public function parseNamespace()
{
$name = '';
while (($token = $this->next()) && ($token[0] === T_STRING || $token[0] === T_NS_SEPARATOR)) {
while (($token = $this->next()) && ($token[0] === T_STRING || $token[0] === T_NS_SEPARATOR || (\PHP_VERSION_ID >=80000 && $token[0] === T_NAME_QUALIFIED))) {
Copy link
Member

Choose a reason for hiding this comment

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

isn't T_NAME_FULLY_QUALIFIED also allowed in a namespace declaration ?

@stof
Copy link
Member

stof commented Aug 5, 2020

@greg0ire I don't think we can easily avoid the copy-paste. The common code is the handling of group use statements (cases involving {, , and }) while the handling of other tokens (for the actual segments of a use statement) changed to the different structure of tokens in PHP 8. And we cannot extract the handling of group use statements to a separate method as these cases of the switch are altering the local state (it would of course be possible to extract things, but the result would probably be even harder to read)

@stof
Copy link
Member

stof commented Aug 5, 2020

For reference, here is the diff between both methods:

--- a/v7.php
+++ b/v8.php
@@ -3,7 +3,7 @@ class TokenParser
 {
     // Other code here. this is a file extract
 
-    private function parseUseStatementV7(): array
+    private function parseUseStatementV8(): array
     {
         $groupRoot = '';
         $class = '';
@@ -12,14 +12,22 @@ class TokenParser
         $explicitAlias = false;
         while (($token = $this->next())) {
             $isNameToken = $token[0] === T_STRING || $token[0] === T_NS_SEPARATOR;
-            if (!$explicitAlias && $isNameToken) {
+
+            if ($token[0] === T_NAME_QUALIFIED || $token[0] === T_NAME_FULLY_QUALIFIED) {
                 $class .= $token[1];
-                $alias = $token[1];
-            } else if ($explicitAlias && $isNameToken) {
-                $alias .= $token[1];
+
+                $classSplit = explode('\\', $token[1]);
+                $alias = $classSplit[count($classSplit) - 1];
+            } else if($token[0] === T_NS_SEPARATOR) {
+                $class .= '\\';
             } else if ($token[0] === T_AS) {
                 $explicitAlias = true;
                 $alias = '';
+            } else if ($isNameToken && !$explicitAlias) {
+                $class = $token[1];
+                $alias = $token[1];
+            } else if ($isNameToken && $explicitAlias) {
+                $alias = $token[1];
             } else if ($token === ',') {
                 $statements[strtolower($alias)] = $groupRoot . $class;
                 $class = '';

} else if ($token[0] === T_AS) {
$explicitAlias = true;
$alias = '';
} else if ($isNameToken && !$explicitAlias) {
Copy link
Member

Choose a reason for hiding this comment

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

this should use $token[0] === T_STRING rather than $isNameToken, as the case of $token[0] === T_NS_SEPARATOR would never reach that case anyway.
Same for the next condition

@stof
Copy link
Member

stof commented Aug 5, 2020

btw, I think the case of $explicitAlias could be cleaned for the code for PHP 7 too: T_NS_SEPARATOR is not allowed in the alias.

@jkufner
Copy link
Contributor

jkufner commented Aug 5, 2020

I believe that this is one of the few rare cases when copy&paste is the right thing to do. However, it would be nice to add a few comments to mark the differences (and group them together). Also, the conditions should be as similar to the original as possible, e.g., keeping the order of $isNameToken and $explicitAlias. It should be easy to do the diff just by looking at the code. The guy reading it when PHP9 appears will thank you.

@stof
Copy link
Member

stof commented Aug 5, 2020

The guy reading it when PHP9 appears will thank you.

At that time, I'm almost sure that the maintained version of doctrine/annotation will not support PHP 7 anyway so the duplicate code would be gone.
Thus, I doubt PHP 9 would change the token structure of use statements again...

@greg0ire
Copy link
Member

greg0ire commented Aug 5, 2020

You're right, I didn't realize the diff was so important. A good commit message, or, as @jkufner pointed out, comments in the code would help.

@stof
Copy link
Member

stof commented Aug 5, 2020

@greg0ire as I said, cleaning the PHP 7 code could allow reducing it (for instance, we cannot have T_NS_SEPARATOR inside a explicit alias and we can only have one T_STRING so the branch of $explicitAlias === true should look the same in both versions if implemented cleanly in both)

fabpot added a commit to symfony/symfony that referenced this pull request Aug 10, 2020
…errabus)

This PR was merged into the 3.4 branch.

Discussion
----------

[ClassLoader][Routing] Fix namespace parsing on php 8

| Q             | A
| ------------- | ---
| Branch?       | 3.4
| Bug fix?      | yes
| New feature?  | no
| Deprecations? | no
| Tickets       | N/A
| License       | MIT
| Doc PR        | N/A

The way namespace declarations are parsed has changed in php 8 (see php/php-src#5827). This PR should fix the corresponding issues in the ClassLoader and Routing components.

Note that Doctrine Annotations suffers from the same issue (doctrine/annotations#339). I had to run the Routing tests locally with doctrine/annotations#344 applied.

Commits
-------

3d293b2 [ClassLoader][Routing] Fix namespace parsing on php 8.
@stof
Copy link
Member

stof commented Aug 10, 2020

I created a separate PR in #347 to keep a single method for both PHP 7 and PHP8.

@stof
Copy link
Member

stof commented Aug 10, 2020

I'm closing this PR in favor of #347

@stof stof closed this Aug 10, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Aliasing breaks with PHP8
4 participants