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

PHP 8 support #477

Merged
merged 6 commits into from Jul 7, 2020
Merged

PHP 8 support #477

merged 6 commits into from Jul 7, 2020

Conversation

Ayesh
Copy link
Contributor

@Ayesh Ayesh commented Jul 4, 2020

Work in progress to bring PHP 8 support for prophecy.

Majority of the errors were deprecation notices. I fixed the optional-before-required errors and deprecation of ReflectionParameter methods getClass, isCallable, and isArray with branching code that checks getType is available, and then uses it instead of the shorthand now-deprecated functions.

I'm not completely sure about the fixes related to the new mixed type.

After these changes, there are only 4 errors/failures remaining from PHPUnit:

1) Tests\Prophecy\Doubler\ClassPatch\MagicCallPatchTest::it_supports_classes_with_invalid_tags
Method ReflectionParameter::getClass() is deprecated

prophecy/tests/Doubler/ClassPatch/MagicCallPatchTest.php:23

2) Tests\Prophecy\Doubler\Generator\ClassMirrorTest::it_marks_required_args_without_types_as_not_optional
Required parameter $arg_2 follows optional parameter $arg_1

prophecy\fixtures\WithArguments.php:7
prophecy/tests/Doubler/Generator/ClassMirrorTest.php:67

3) Tests\Prophecy\Doubler\Generator\ClassMirrorTest::it_doesnt_use_scalar_typehints
Error: Call to a member function getArguments() on null

prophecy/tests/Doubler/Generator/ClassMirrorTest.php:386

4) Tests\Prophecy\Doubler\Generator\ClassMirrorTest::it_changes_argument_names_if_they_are_varying
Error: Call to undefined method ReflectionUnionType::isBuiltin()

prophecy/tests/Doubler/Generator/ClassMirrorTest.php:480

I cannot get xdebug to work with PHP nightly builds, so these errors do not contain any backtraces. Any insights would be appreciated.
I tried to skip some of the tests (such as #2 above) with @require PHP <= 8 annotations for the test because the deprecated behavior is exactly what's this test is asserting.

ReflectionParameter::export method is deprecated, and removed in PHP 8, which causes #3 above.

@Ayesh Ayesh mentioned this pull request Jul 4, 2020
31 tasks
@Ayesh
Copy link
Contributor Author

Ayesh commented Jul 4, 2020

Related: phpspec/phpspec#1314

@Ayesh Ayesh marked this pull request as draft July 4, 2020 22:01
@ciaranmcnulty
Copy link
Member

I’d like to get #476 merged which would then simplify this a lot 👍

@Ayesh
Copy link
Contributor Author

Ayesh commented Jul 5, 2020

That would he lovely! We can remove a lot of void workarounds and always assume getType is available. I'll wait for those PRs, and then rebase this PR after that.

Thank you.

@ciaranmcnulty
Copy link
Member

Ok I merged it. I actually started a branch like this one but the complexity got too much for me so I did that other change :)

If you can rebase that looks good

For debugging in php8 I had similar issues and resorted to running in 7.4 for debug just to figure out where calls were coming from

@ciaranmcnulty
Copy link
Member

Maybe split out the changes that are to get the existing suite paying, from changes to support new features (eg mixed hint)

The latter would need additional tests etc

@Ayesh
Copy link
Contributor Author

Ayesh commented Jul 5, 2020

Thanks a lot @ciaranmcnulty . I sent PR #478 to enable builds. We can either remove workarounds for PHP <7.2 first, and then work on PHP 8 fixes, or the other way around.

@@ -45,7 +45,7 @@ class ClassMirror
*
* @throws \Prophecy\Exception\InvalidArgumentException
*/
public function reflect(ReflectionClass $class = null, array $interfaces)
public function reflect(ReflectionClass $class = null, array $interfaces = array())
Copy link
Member

Choose a reason for hiding this comment

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

I would rather change that to ?ReflectionClass $class, array $interfaces, to avoid changing the signature

@@ -250,7 +250,19 @@ private function isNullable(ReflectionParameter $parameter)
private function getParameterClassName(ReflectionParameter $parameter)
{
try {
return $parameter->getClass() ? $parameter->getClass()->getName() : null;
if (!method_exists($parameter, 'getType')) {
Copy link
Member

Choose a reason for hiding this comment

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

The min version is now PHP 7.2, so this is never true.

if (!$type || $type->isBuiltin()) {
return null;
}
if (!method_exists($type, 'getName')) {
Copy link
Member

Choose a reason for hiding this comment

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

I'd rather add a check for instanceof ReflectionNamedType, which will be needed anyway when adding the compatibility with union types.

@@ -222,11 +222,11 @@ private function getTypeHint(ReflectionParameter $parameter)
return $className;
}

if (true === $parameter->isArray()) {
if (version_compare(PHP_VERSION, '8', '<') && true === $parameter->isArray()) {
Copy link
Member

Choose a reason for hiding this comment

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

I suggest using \PHP_VERSION_ID < 80000 instead, which can be resolved at compile time.

Copy link
Member

Choose a reason for hiding this comment

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

Aha so that's the reason

Copy link
Member

Choose a reason for hiding this comment

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

yeah (not that qualifying the call is important for that. Relying on constant fallback will delay it until runtime)

return 'array';
}

if (version_compare(PHP_VERSION, '5.4', '>=') && true === $parameter->isCallable()) {
if (true === $parameter->isCallable()) {
Copy link
Member

Choose a reason for hiding this comment

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

shouldn't we have a version check here too according to the commit message ?

@stof
Copy link
Member

stof commented Jul 6, 2020

please also rebase this PR now that code for PHP < 7.2 has been removed.

@Ayesh
Copy link
Contributor Author

Ayesh commented Jul 6, 2020

Thanks a lot for the great feedback. I will rebase/update code with new minimum PHP version in mind and other changes.

I used version_compare() because it was already being used in other areas, but I think breaking that convention is worth the dead code elimination improvements.

Ayesh added 5 commits July 6, 2020 18:45
This pattern [emits a deprecation notice in PHP 8](https://php.watch/versions/8.0/deprecate-required-param-after-optional).
First parameter to `\Prophecy\Doubler\Generator\ClassMirror::reflect()` is now marked nullable because PHP 7.2 is the new minimum PHP version.
…ay` deprecations

`ReflectionParameter::isCallable()` and `ReflectionParameter::isArray()` are [deprecated in PHP 8](https://php.watch/versions/8.0/deprecated-reflectionparameter-methods).
They are now replaced with `ReflectionParameter::getType()` calls.
[`ReflectionParameter::getClass()` is deprecated in PHP 8](https://php.watch/versions/8.0/deprecated-reflectionparameter-methods)
It is now replaced with a combination of `ReflectionParameter::getType()` and `ReflectionParameter::getClass`.
…w nullability on it

PHP 8's new [`mixed` type](https://php.watch/versions/8.0/mixed-type#nullable) cannot be used as a nullable type, and needs special handling.
@Ayesh
Copy link
Contributor Author

Ayesh commented Jul 6, 2020

@stof I rebased and made some changes in the code, and with PHP 7.2 as the minimum, I could remove a lot of code. It would be great if you could take a look.

*/
public function reflect(ReflectionClass $class = null, array $interfaces)
public function reflect(?ReflectionClass $class, array $interfaces)
Copy link
Contributor

Choose a reason for hiding this comment

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

This is a major breaking change because the class is non-final. Please revert.

Copy link
Member

Choose a reason for hiding this comment

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

This weirdly does not seem to cause any issue https://3v4l.org/roQB8 #lolphp

Copy link
Contributor

Choose a reason for hiding this comment

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

Oh, it's because the 2nd param is required, so PHP treats them all as required, and null is specially implemented.

Copy link
Member

Choose a reason for hiding this comment

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

@GrahamCampbell did you delete a comment? It's not an issue for callers because they have no way of taking advantage of the optionality

Copy link
Contributor

Choose a reason for hiding this comment

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

Yeh, I replaced with with the comment that the 1st param was not really optional anyway

return $type->getName();
}

return null;
} catch (\ReflectionException $e) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Don't need this.

Copy link
Member

Choose a reason for hiding this comment

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

That's true @Ayesh the try/catch is because getClass can throw, getName does not

@GrahamCampbell
Copy link
Contributor

I this this PR is ready for merge now. It gets the ball rolling at least, even if it is not complete (just like the phpspec PR).

@ciaranmcnulty ciaranmcnulty marked this pull request as ready for review July 7, 2020 13:48
@ciaranmcnulty ciaranmcnulty merged commit ad49d54 into phpspec:master Jul 7, 2020
@ciaranmcnulty
Copy link
Member

Thanks, @Ayesh!

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.

None yet

4 participants