Interfaces and ReflectionMethod::isConstructor()

php.internals

Sebastian Bergmann

6 years ago
I recently received a bug report for PHPUnit [1] that with PHP 8 test doubles (test stubs or mock objects) can no longer be created for interfaces that declare a constructor. I was able to reproduce this [2] and learned that PHPUnit's canMockMethod() [3] returns a different result on PHP 8 compared to PHP 7. This is due to the return value of ReflectionMethod::isConstructor() when the ReflectionMethod object was created for an interface. Here is a minimal, self-contained, reproducing test case: <?php interface I { public function __construct(); } var_dump((new ReflectionMethod(I::class, '__construct'))->isConstructor()); The code shown above yields the following result: * bool(true) for PHP 5.0.0 and PHP 5.0.1 * bool(false) for PHP 5.0.2 through PHP 7.4.4 * bool(true) for PHP 8.0-dev I think that the behaviour in PHP 8 (and PHP 5.0.0 and PHP 5.0.1) is correct. But I also think that constructor declarations have no place in interfaces ... so I am confused. Which is why I did not open a ticket on bugs.php.net and bring this topic to this list instead. To the best of my knowledge, this change in behaviour of isConstructor() between PHP 7 and PHP 8 is not yet documented. If it is intentional and here to stay then it should be documented. I can deal with this in PHPUnit's code either way, but need to know what the plan here is. Thanks! Sebastian
-- [1] https://github.com/sebastianbergmann/phpunit/issues/4139 [2] https://github.com/sebastianbergmann/phpunit/issues/4139#issuecomment-605407253 [3] https://github.com/sebastianbergmann/phpunit/blob/9.0.1/src/Framework/MockObject/Generator.php#L861

Sebastian Bergmann

6 years ago
Am 28.03.2020 um 08:44 schrieb Sebastian Bergmann:
> <?php > interface I > { > public function __construct(); > } > > var_dump((new ReflectionMethod(I::class, '__construct'))->isConstructor());
Here is the version of the script for testing with dead versions of PHP: https://3v4l.org/fMCub

Christoph Becker

6 years ago
On 28.03.2020 at 08:44, Sebastian Bergmann wrote:
> I recently received a bug report for PHPUnit [1] that with PHP 8 test > doubles (test stubs or mock objects) can no longer be created for > interfaces that declare a constructor. > > I was able to reproduce this [2] and learned that PHPUnit's > canMockMethod() [3] returns a different result on PHP 8 compared to PHP > 7. This is due to the return value of ReflectionMethod::isConstructor() > when the ReflectionMethod object was created for an interface. > > Here is a minimal, self-contained, reproducing test case: > > <?php > interface I > { > public function __construct(); > } > > var_dump((new ReflectionMethod(I::class, '__construct'))->isConstructor()); > > The code shown above yields the following result: > > * bool(true) for PHP 5.0.0 and PHP 5.0.1 > * bool(false) for PHP 5.0.2 through PHP 7.4.4 > * bool(true) for PHP 8.0-dev > > I think that the behaviour in PHP 8 (and PHP 5.0.0 and PHP 5.0.1) is > correct. But I also think that constructor declarations have no place in > interfaces ... so I am confused. Which is why I did not open a ticket on > bugs.php.net and bring this topic to this list instead. > > To the best of my knowledge, this change in behaviour of isConstructor() > between PHP 7 and PHP 8 is not yet documented. If it is intentional and > here to stay then it should be documented. > > I can deal with this in PHPUnit's code either way, but need to know what > the plan here is.
The relevant change[1] is that now the class entry members serialize_func, unserialize_func, constructor, destructor and clone are set, if the respective methods are declared on an interface. [1] <https://github.com/php/php-src/pull/3846/files#diff-3a8139128d4026ce0cb0c86beba4e6b9L5549-R5605>
-- Christoph M. Becker

Sebastian Bergmann

6 years ago
Am 28.03.2020 um 08:44 schrieb Sebastian Bergmann:
> I can deal with this in PHPUnit's code either way, but need to know what > the plan here is.
I worked around the issue now [1].
-- [1] https://github.com/sebastianbergmann/phpunit/commit/477798f6b226e830d358bc102cd02bb2e52cfdb2

Marco Pivetta

6 years ago
Hey Sebastian, <http://ocramius.github.com/> On Mon, Mar 30, 2020 at 8:45 AM Sebastian Bergmann <sebastian@php.net> wrote:
> Am 28.03.2020 um 08:44 schrieb Sebastian Bergmann: > > I can deal with this in PHPUnit's code either way, but need to know what > > the plan here is. > > I worked around the issue now [1]. > > -- > [1] > > https://github.com/sebastianbergmann/phpunit/commit/477798f6b226e830d358bc102cd02bb2e52cfdb2 >
Patch makes a lot of sense, but is it done that way to still support 7.x? I'm not sure (don't have a local build of PHP 8.x-dev) if https://wiki.php.net/rfc/remove_php4_constructors is going to come in effect as "PHP4 constructors are finally gone". Marco Pivetta http://twitter.com/Ocramius http://ocramius.github.com/

Sebastian Bergmann

6 years ago
Am 30.03.2020 um 10:14 schrieb Marco Pivetta:
> Patch makes a lot of sense, but is it done that way to still support 7.x? > I'm not sure (don't have a local build of PHP 8.x-dev) if > https://wiki.php.net/rfc/remove_php4_constructors is going to come in > effect as "PHP4 constructors are finally gone".
https://github.com/sebastianbergmann/phpunit/commit/8d399fd561a9a227898fb734c9e89fa1b13d75c8 :-)