Skip to content

Bump to PHP 7.4+ and upgrade to PHPStan v2#639

Merged
MarcelloDuarte merged 5 commits intophpspec:masterfrom
Jean85:phpstan-v2
Apr 29, 2025
Merged

Bump to PHP 7.4+ and upgrade to PHPStan v2#639
MarcelloDuarte merged 5 commits intophpspec:masterfrom
Jean85:phpstan-v2

Conversation

@Jean85
Copy link
Copy Markdown
Contributor

@Jean85 Jean85 commented Apr 29, 2025

@MarcelloDuarte this is the PR that I promised. I'll self-review to add more context to each change.

Comment on lines +4 to +5
message: '#^Parameter \#1 \$value of function strval expects bool\|float\|int\|resource\|string\|null, mixed given\.$#'
identifier: argument.type
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Multiple lines here are changed just because PHPStan v2 does no longer double-escape, and adds the identifier of the error. I'll comment only on new ignored issues.

Comment on lines +15 to +17
-
message: '#^Method Prophecy\\Doubler\\CachedDoubler\:\:createDoubleClass\(\) should return class\-string\<Prophecy\\Doubler\\DoubleInterface&T of object\> but returns class\-string\.$#'
identifier: return.type
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This is a method extending its parent which is generic. It seems that the nested-double generic isn't working, we could report this upstream to PHPStan

Comment on lines +28 to +37
message: '#^Call to function method_exists\(\) with ''ReflectionClass'' and ''isReadOnly'' will always evaluate to true\.$#'
identifier: function.alreadyNarrowedType
count: 1
path: src/Prophecy/Doubler/Generator/ClassMirror.php

-
message: '#^Call to function method_exists\(\) with ReflectionMethod and ''hasTentativeReturnT…'' will always evaluate to true\.$#'
identifier: function.alreadyNarrowedType
count: 1
path: src/Prophecy/Doubler/Generator/ClassMirror.php
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

These two are due to the fact that those reflection methods are present only from PHP 8.1. These workaround go away as soon as we bump the minimum PHP version.

Comment on lines +46 to +61
message: '#^PHPDoc tag @var with type static\(Prophecy\\Doubler\\LazyDouble\<U of object\>\) is not subtype of native type \$this\(Prophecy\\Doubler\\LazyDouble\<T of object\>\)\.$#'
identifier: varTag.nativeType
count: 1
path: src/Prophecy/Doubler/LazyDouble.php

-
message: '#^Property Prophecy\\Doubler\\LazyDouble\:\:\$class \(ReflectionClass\<T of object\>\|null\) does not accept ReflectionClass\<U of object\>\.$#'
identifier: assign.propertyType
count: 1
path: src/Prophecy/Doubler/LazyDouble.php

-
message: '#^Property Prophecy\\Doubler\\LazyDouble\:\:\$class \(ReflectionClass\<T of object\>\|null\) is never assigned ReflectionClass\<T of object\> so it can be removed from the property type\.$#'
identifier: property.unusedType
count: 1
path: src/Prophecy/Doubler/LazyDouble.php
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

These 3 are all from the same issue, the setParentClass method is very hard to map against PHPStan because it changes the type of the generic of the instance itself.

Comment on lines +63 to +67
-
message: '#^Call to function method_exists\(\) with ReflectionMethod and ''hasTentativeReturnT…'' will always evaluate to true\.$#'
identifier: function.alreadyNarrowedType
count: 1
path: src/Prophecy/Prophecy/MethodProphecy.php
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Again, 8.1+ reflection feature...

* @param MethodProphecy $methodProphecy
* @param int $count
* @param list<Call> $calls
* @param array<Call> $calls
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Inputing any array, casting to list below

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

do we need this, or should we use a more precise type in our CallCenter (which is probably already using lists) ?

Comment on lines +26 to +32
* @param array<Call> $calls
*/
public function __construct($message, MethodProphecy $methodProphecy, array $calls)
{
parent::__construct($message, $methodProphecy);

$this->calls = $calls;
$this->calls = array_values($calls);
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Same here, casting to list (previous was extending class)

$callback = $this->callback;

if ($callback instanceof Closure && method_exists('Closure', 'bind') && (new ReflectionFunction($callback))->getClosureThis() !== null) {
if ($callback instanceof Closure && (new ReflectionFunction($callback))->getClosureThis() !== null) {
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

bind is present since PHP 5.4, we can ditch this check.

$callback = $this->callback;

if ($callback instanceof Closure && method_exists('Closure', 'bind') && (new ReflectionFunction($callback))->getClosureThis() !== null) {
if ($callback instanceof Closure && (new ReflectionFunction($callback))->getClosureThis() !== null) {
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

bind is present since PHP 5.4, we can ditch this check.


$values = "\n".$values.$whitespace;
}
\assert(\is_object($value));
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This is just a change in how PHPStan detect types; better but not completely.

@Jean85
Copy link
Copy Markdown
Contributor Author

Jean85 commented Apr 29, 2025

@MarcelloDuarte I've bumped to PHP 7.4 as discussed.

I've also fixed the PHPStan analysis, but I saw that the output depends on the PHP version it runs on. It seems acceptable for now.

I also added a changelog entry, so that a 1.22 should be released with this bump. Let me know if this is ok or if it needs more work.

@Jean85 Jean85 changed the title PHPStan v2 Bump to PHP 7.4+ and upgrade to PHPStan v2 Apr 29, 2025
}

return $types;
return array_values($types);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

this is actually an issue in phpstan IMO. It should know that ReflectionUnionType::getTypes returns a list (so that doing array_map on it still returns a list)

return 'Array &'.$key;
}

\assert(\is_array($value));
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

isn't this removal causing the issues reported just below ?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

@Jean85 I see you already reverted the above. @stof are you happy for us to merge and release 1.22 to unblock phpspec?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

how is this PR unblocking phpspec ? Removing support for PHP versions in a dependency cannot be a blocker (phpspec requires that prophecy supports at least all the PHP versions it needs, but does not care if it supports more).

Copy link
Copy Markdown
Member

@MarcelloDuarte MarcelloDuarte Apr 29, 2025

Choose a reason for hiding this comment

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

It's some sort of circular dependency around stan. That wasn't my question though... Is this mergeable? Do we need to keep supporting PHP versions in EOL state to keep the next tag within minor release?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Do we need to keep supporting PHP versions in EOL state to keep the next tag within minor release?

As long as we update our php requirement properly in the composer.json, dropping support for some PHP versions is not a BC break (as composer will still respect that requirement when resolving semver ranges). That's still something that deserves a minor version IMO (it is not a bugfix), but it does not require a major version bump as far as Composer is concerned (Symfony itself has a policy to be stricter for the fullstack framework, but that's for other reasons)

@MarcelloDuarte MarcelloDuarte merged commit 35f1adb into phpspec:master Apr 29, 2025
10 checks passed
@Jean85 Jean85 deleted the phpstan-v2 branch April 30, 2025 11:13
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.

3 participants