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

On the road to phpstan 2.0 compatibility #334

Open
wants to merge 8 commits into
base: master
Choose a base branch
from

Conversation

hostep
Copy link
Contributor

@hostep hostep commented Nov 12, 2024

I went over https://github.com/phpstan/phpstan/blob/2.0.x/UPGRADING.md#upgrading-guide-for-extension-developers

More specifically these 2 guides, which can be seen in 2 individual commits in this PR:

Even more in detail:

  1. Using RuleErrorBuilder instead of returning strings:
    • wrapped the previous string into a RuleError object
    • introduced an identifier using the format bitExpertMagento.{classname-without-Rule-suffix-and-lowercase-starting-letter} (the regex only allows letters and digits and dots, no dashes or underscores)
    • The guide recommended to remove the phpdoc on top of the processNode method, which I did but while running phpstan in this project, it complained that the if (!$node instanceof MethodCall) { check was no longer needed. So I removed it and then also removed the unit tests that checked this particular case as well.
  2. Replaced instanceof type checking:
    • I temporarily installed the phpstan/phpstan-deprecation-rules package as a dev requirement, which brought these necessary changes to light, I later on removed it again, because it complained about a bunch of other things as well, which are not in scope of this PR
    • I've used the recommened getConstantStrings method, this always returns an array which can be empty or contain one or multiple ConstantString objects
    • if I understand it correctly, this array is to deal with union types, so adjusted code for this
    • I didn't test these changes, just made sure unit tests were green, so this probably requires extra manual testing, since I feel like I don't quite understand these 2 classes that got modified in this PR

I think these are the only 2 major changes needed for PHPStan 2.0 compatibility. In theory, these should even be backwards compatible with PHPStan 1.11.x and higher.

I didn't change the composer.json requirements to 2.0 just yet, would first like to get some feedback on this, to see if we are moving in the right direction.

@hostep
Copy link
Contributor Author

hostep commented Nov 12, 2024

Just out of curiosity, I tried updating the phpstan packages to 2.0 compatible ones in composer, and a lot more work will be required, but that's all to satisfy the automated tests in this package, not to have compatibility with PHPStan 2.0 itself I think:

  • phpunit complains about no longer being able to create a mock for PHPStan\Reflection\ClassReflection as that class is now declared as final (is being used in 3 different test classes)
  • in TestFrameworkObjectManagerDynamicReturnTypeExtensionUnitTest::returnsErrorTypeForUnkownMethodCall the name on $methodCall needs to be defined, otherwise phpunit fails with Typed property PhpParser\Node\Expr\MethodCall::$name must not be accessed before initialization
  • Running clover coverage check requires the upgrade of nikic/php-parser from v4 to v5 for some reason
  • Running phpstan 2.0 on the module itself gives 47 errors, some of these are because the max level increased from 9 to 10 and we currently use max, so we run into a bunch of new errors. If I decrease the level back to 9, we still have 39 errors to be solved

So all in all, still quite a lot of work ahead for the switch to PHPStan 2.0 ...

@shochdoerfer
Copy link
Member

Thanks a lot for the overview of the current migration state. Very much appreciated. I feared it will be a lot of work ;(

phpunit complains about no longer being able to create a mock for PHPStan\Reflection\ClassReflection as that class is now declared as final (is being used in 3 different test classes)

We may be able to use the final class directly in the tests. We probably just need to configure it correctly (if possible).

in TestFrameworkObjectManagerDynamicReturnTypeExtensionUnitTest::returnsErrorTypeForUnkownMethodCall the name on $methodCall needs to be defined, otherwise phpunit fails with Typed property PhpParser\Node\Expr\MethodCall::$name must not be accessed before initialization

Sounds like a minor change.

Running clover coverage check requires the upgrade of nikic/php-parser from v4 to v5 for some reason

I am fine with upgrading the dependency. Also, I am fine with limiting the "compatible" Magento and/or PHP versions. Since we currently support > Magento 2.3.0 and PHP versions from 7.4 to 8.x it makes things not easy to maintain. Maybe it makes sense to use Magento 2.4 as a minimum or even 2.4.6 or something. Since the older versions of the extension still work, you can use them for older installations without any issues.

Running phpstan 2.0 on the module itself gives 47 errors, some of these are because the max level increased from 9 to 10 and we currently use max, so we run into a bunch of new errors. If I decrease the level back to 9, we still have 39 errors to be solved.

Damn ;(

@hostep
Copy link
Contributor Author

hostep commented Nov 13, 2024

Added bunch of extra commits. I tried to keep them small to allow for easier review.

The biggest problem is this one:

PHPUnit\Framework\MockObject\ClassIsFinalException: Class "PHPStan\Reflection\ClassReflection" is declared "final" and cannot be doubled

I can't find a way to solve it, but I didn't try very hard. So for now I marked the unit tests that have this issue to be skipped.
If we can fix this problem, we'll also probably fix like 90% of the phpstan failures.

There are some more phpstan errors I don't know how to properly solve, but it's in code that I don't really understand, so it's hard to figure this out.

The changes done in the following 2 files around those ConstantString I don't feel very secure about, so please make sure to check those in more detail:

  • Type/ObjectManagerDynamicReturnTypeExtension.php
  • Type/TestFrameworkObjectManagerDynamicReturnTypeExtension.php

Other feedback is also welcome in case you notice something that we should handle in a different way.

@@ -69,7 +69,7 @@ protected function getFileContents(string $class): string
$namespace = explode('\\', ltrim($class, '\\'));
/** @var string $factoryClassname */
$factoryClassname = array_pop($namespace);
$originalClassname = preg_replace('#Factory$#', '', $factoryClassname);
$originalClassname = preg_replace('#Factory$#', '', $factoryClassname) ?? $factoryClassname;
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I don't like this change, it's to keep phpstan stop from complaining about preg_replace to return null in case of a failure. Maybe we should mark to ignore this phpstan error?

$defaultValue = ' = ' . $parameter->getDefaultValue();
if (is_string($parameter->getDefaultValue())) {
$defaultValue = ' = ' . $parameter->getDefaultValue();
}
Copy link
Contributor Author

@hostep hostep Nov 13, 2024

Choose a reason for hiding this comment

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

Not sure if this is a good solution, $parameter->getDefaultValue() returns mixed, so phpstan complained about this concatenation not being with a string.

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.

2 participants