-
-
Notifications
You must be signed in to change notification settings - Fork 291
ProxyGenerator: Added support for PHP 7.1 features #734
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
Conversation
6a77931
to
8080153
Compare
8080153
to
c58633f
Compare
Added cd7fbb4 to show the inconsistent signature change for parameter without nullable type, but with null as default value. How should we handle this? I consider it a bug since the signature is different, but there is no easy way to support it properly (allowsNull returns true when default value is NULL). |
Note: I stopped working on this unit 7.1 is in RC phase, since the On 1 Sep 2016 00:50, "Michael Moravec" [email protected] wrote:
|
Finished working on zendframework/zend-code#87 - should be stable enough to be usable :-) |
Need checking of:
|
Although 7.1 is RC2 already, Reflection is still a mess. Let's just wait for the resolution of php/php-src#2136 and see what happens. It may probably also fix the nonsense with broken allowsNull. 🙏 |
cd7fbb4
to
7818108
Compare
7818108
to
8e3b4d4
Compare
PR updated, now also includes support for void return type and iterable pseudotype. Tested against PHP 7.1 RC3. |
@Majkl578 amazing! Reviewing now. |
} | ||
|
||
if (method_exists($parameter, 'hasType') && $parameter->hasType() && $parameter->getType()->isBuiltin()) { | ||
return (string) $parameter->getType(); | ||
if (method_exists($parameter, 'hasType')) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This block is to be moved to a private method
@@ -935,25 +939,49 @@ private function buildParametersString(ClassMetadata $class, \ReflectionMethod $ | |||
*/ | |||
private function getParameterType(ClassMetadata $class, \ReflectionMethod $method, \ReflectionParameter $parameter) | |||
{ | |||
$typeDeclaration = ''; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This assignment and subsequent block is to be moved to a private method
@@ -1018,20 +1046,43 @@ private function getMethodReturnType(\ReflectionMethod $method) | |||
|
|||
$returnType = $method->getReturnType(); | |||
|
|||
$returnDeclaration = ': '; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To be moved to a private method (probably same method as block above)
|
||
$classCode = file_get_contents(__DIR__ . '/generated/__CG__DoctrineTestsCommonProxyVoidReturnTypeClass.php'); | ||
|
||
$this->assertEquals(1, substr_count($classCode, 'function returnsVoid(): void')); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should also verify that calling the method produces no return value. Without actually evaluating the code, we can't be sure that we support void
correctly. Not sure if we should put it here or in https://github.com/doctrine/common/blob/670d814b6c8b5e8f39a2764d090463d39b3502c9/tests/Doctrine/Tests/Common/Proxy/ProxyLogicTest.php
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What exactly do you mean? Since we can't put any return value into the method that is to be proxied (otherwise it's a fatal error), just testing that such method returns NULL, i.e. assertNull($proxy->returnsVoid())
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Something like $proxy->increment(10);
and then self::assertSame(10, $proxy->counter)
. Simply verifies that we're passing parameters.
3032832
to
fbf9791
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated, refactored most of the code to separate private method and added few more tests.
{ | ||
} | ||
|
||
public function nullableTypeHintWithDefaultNull(?int $param = null) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is still no way to detect this correctly at runtime (makes one test fail).
$this->assertEquals(1, substr_count($classCode, 'function nullableTypeHintObject(?\stdClass $param)')); | ||
$this->assertEquals(1, substr_count($classCode, 'function nullableTypeHintSelf(?\\' . $className . ' $param)')); | ||
$this->assertEquals(1, substr_count($classCode, 'function nullableTypeHintWithDefault(?int $param = 123)')); | ||
$this->assertEquals(1, substr_count($classCode, 'function nullableTypeHintWithDefaultNull(?int $param = NULL)')); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This fails in 7.1 because Reflection API still doesn't provide proper way to detect this case (see method declaration above) and I don't see any straightforward workaround that wouldn't break generated code in 7.0 (allowsNull exists there too so method_exists doesn't help).
Any advice how to handle this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ReflectionParameter::isOptional()
and ReflectionParameter::getDefaultValue()
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@trowski Thanks, unfortunately that's not enough either, I used isDefaultValueAvailable which should be the same. See https://github.com/doctrine/common/pull/734/files#diff-ce03ab9b396edcbb313c54234c20e0deR1081
The following:
public function foo(int $param = null)
would still generate:
public function foo(?int $param = null)
which is not correct (at least in <7.1). And since we don't want any version checks, it's tricky...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it needed for 7.1 to generate 7.0 compatible code? If so, you could special case nullable parameters with a null default value to always omit the preceding ?
. It may not be exactly as written, but it will be compatible.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@trowski That's a good question, probably not, but then someone could use 7.1 on CI server to warm-up cache (and generate proxies) and then 7.0 in production...
I already added special case for null default value, but then it has opposite effect.
I actually thought that this code would throw an error due to incompatible signatures:
class Foo {
function x(?int $x = NULL) {}
}
class Bar extends Foo {
function x(int $x = NULL) {}
}
But surprisingly it doesn't, even though subclass removes nullability (question mark) from the type, something I'd not really expect. This is super-confusing, really something not well-thought-out in my opinion... 😳
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Bar::x(int $x = null)
is not is not removing nullability from $x
, but rather just using older syntax to declare nullability. ?int $x = null
is semantically identical to int $x = null
, so the definitions are compatible.
4e46fbb
to
464507f
Compare
…, iterable pseudotype
464507f
to
87dc24e
Compare
Updated:
|
@@ -22,4 +22,12 @@ public function combinationOfTypeHintsAndNormal(\stdClass $a, $b, int $c) | |||
public function typeHintsWithVariadic(int ...$foo) | |||
{ | |||
} | |||
|
|||
public function withDefaultValue(int $foo = 123) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oooh, shiny! I'll have to test this on my libs too.
@Majkl578 excellent work! The only blocker for getting this merged is having |
Actually, never mind, tested locally, and everything works! Merging, thanks! |
…pehints Fix parameter/return type validation for interfaces (introduced in #734)
This was supposed to work without any change in the generator, but it's unfortunately not the case. There are multiple problems:
Forwarded upstream for more discussion to the PR where the changes were originally discussed and where you can also find more discussion with some other concerns: php/php-src#2068 (comment)
In case of no activity there I'll forward it to the bugs tracker soon or eventually php.internals.
(Travis still has old PHP 7.1 version without the changes, they'll appear in beta3.)