Welcome! Log In Create A New Profile

Advanced

[PHP-DEV] Easy method of overriding built-in php functions.

Posted by Ryan Jentzsch 
Ryan Jentzsch
[PHP-DEV] Easy method of overriding built-in php functions.
August 16, 2017 10:10PM
I am aware of Advanced PHP debugger but what I need this type of
functionality for is mocking and testing.

As an example I want to use PHPUnit mocking feature to create a mock of a
class. However in the constructor of this class is the following line to
get the expected JSON string:

$result = file_get_contents('php://input');

I can not override the file_get_contents() function. The work around is to
monkey patch the class as a mock and override the constructor (copying ALL
logic from the constructor to the mock allowing for $result to be taken
from the constructor's argument list).

This is awkward and in my use case the constructor also makes calls to
static methods in other objects which the latest version of PHPUnit does
not allow you to mock static methods.

ADP is not acceptable to override functions in that it requires a binary to
be installed and php.ini to be configured as well as no support for this in
Windows.

Is there an RFC that I didn't find that allows for easily overriding
built-in functions? For example:

@function file_get_contents($string)
{
return 'mock string';
}

My C skills are quite rusty and understanding the Zend engine is a daunting
task or I'd create an RFC myself. The devil is in the details. The real
question I am asking is HOW DIFFICULT would something like this be to code
and include in the PHP core?
Christoph M. Becker
[PHP-DEV] Re: Easy method of overriding built-in php functions.
August 16, 2017 10:30PM
On 16.08.2017 at 22:02, Ryan Jentzsch wrote:

> I am aware of Advanced PHP debugger but what I need this type of
> functionality for is mocking and testing.
>
> As an example I want to use PHPUnit mocking feature to create a mock of a
> class. However in the constructor of this class is the following line to
> get the expected JSON string:
>
> $result = file_get_contents('php://input');
>
> I can not override the file_get_contents() function. The work around is to
> monkey patch the class as a mock and override the constructor (copying ALL
> logic from the constructor to the mock allowing for $result to be taken
> from the constructor's argument list).
>
> This is awkward and in my use case the constructor also makes calls to
> static methods in other objects which the latest version of PHPUnit does
> not allow you to mock static methods.
>
> ADP is not acceptable to override functions in that it requires a binary to
> be installed and php.ini to be configured as well as no support for this in
> Windows.
>
> Is there an RFC that I didn't find that allows for easily overriding
> built-in functions? For example:
>
> @function file_get_contents($string)
> {
> return 'mock string';
> }
>
> My C skills are quite rusty and understanding the Zend engine is a daunting
> task or I'd create an RFC myself. The devil is in the details. The real
> question I am asking is HOW DIFFICULT would something like this be to code
> and include in the PHP core?

There are already runkit and uopz which allow to override existing
functions:

* https://pecl.php.net/package/runkit
* https://pecl.php.net/package/uopz

--
Christoph M. Becker

--
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php
On Wed, Aug 16, 2017 at 4:02 PM, Ryan Jentzsch <[email protected]> wrote:
> As an example I want to use PHPUnit mocking feature to create a mock of a
> class. However in the constructor of this class is the following line to
> get the expected JSON string:
>
> $result = file_get_contents('php://input');
>
> I can not override the file_get_contents() function.
>
Short answer: No. There's no current, viable RFC for this purpose.
Personally, I don't expect one would pass.

-Sara

--
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php
On 8/16/17 3:02 PM, Ryan Jentzsch wrote:
> I am aware of Advanced PHP debugger but what I need this type of
> functionality for is mocking and testing.

If you're using namespaces you can try overriding the built-in functions
that way. I've done this successfully in my own library for mocking ldap
and imap function calls (https://git.io/v7ALm)

Original post about the idea.
http://www.manuel-strehl.de/dev/overwrite_PHP_built-in_functions.en.html


> As an example I want to use PHPUnit mocking feature to create a mock
> of a class. However in the constructor of this class is the following
> line to get the expected JSON string:

There is the php-mock library, which appears to have integration for
PHPUnit, Mockery, and Prophecy. I can't vouch for it as I've not used it
yet.

https://github.com/php-mock/php-mock

Dave
--
David Lundgren
dlundgren@syberisle.net
GPG: 0x26F54D7F

--
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php
On 16 August 2017 at 21:02, Ryan Jentzsch <[email protected]> wrote:
>
> Is there an RFC that I didn't find that allows for easily overriding
>built-in functions?

No, but there is an extension: https://pecl.php.net/package/uopz

However, I believe the maintainer of it recommends avoiding using it
if at all possible because:

> However in the constructor of this class is the following line to
> get the expected JSON string:
>
> $result = file_get_contents('php://input');
>
> I can not override the file_get_contents() function. The work around is to
> monkey patch the class as a mock and override the constructor

Your workaround is bad solution to the wrong problem.

Trying to unit test things that interact with the real world is
fundamentally broken thing to try to do.

A much better thing to do would be to isolate the internal layers of
your application from the 'horrible outside world', by extracting the
bit that interacts with the outside world with an interface to be
injected:

interface InputReader {
public function getString();
}

class PhpInputReader implements InputReader {
public function getString() {
return file_get_contents('php://input');
}
}

And then inject a mock version of InputReader when you are doing a
unit test for the class that now depends on an InputReader.

And just to be clear; it is fundamentally impossible to write a unit
test for the class PhpInputReader, as it interacts with the system and
so can't be unit tested. Trying to force a unit-test for something
that should have an integration test is just the wrong thing to do.

There's a talk by a guy called J B Rainsberger that helped me
understand the exact nature of unit tests and integrated tests:
https://www.youtube.com/watch?v=VDfX44fZoMc or
http://vimeo.com/80533536

I really strongly recommend watching it to anyone who is thinking that
replacing functions like file_get_contents() with test version is an
appropriate thing to do.

cheers
Dan
Ack

--
PHP Internals - PHP Runtime Development Mailing List
To unsubscribe, visit: http://www.php.net/unsub.php
Sorry, only registered users may post in this forum.

Click here to login