How to mock php socket built-in functions?

I am working on some code that is read from a socket, and it is mistaken when it receives certain large input. I went to add a unit test for this before fixing it, but got stuck because I cannot mock fread (and other PHP built-in functions that I use, for example, fsockopen , feof , etc.).

In simple words, my problem is that this code does not work with "Fatal error: cannot override fgets () ...":

 function fgets($fp){ return "xxx"; } 

I understand that I can create a socket wrapper class that uses my real code, and then I could create a layout for this wrapper class. But this is The Tail Wagging The Dog, and I can think of reasons why this is a bad idea, other than the principle of things. (For example, to make the code more complex, efficiency, the presence of a refactoring code has not been tested yet.)

So my question is how to replace the built-in fgets() implementation with my own, as part of the unit test? (Or, if you want to think outside the box, the question can be formulated as follows: how can I control the string returned by fgets($socket) when $socket is the return value from fsockopen ?)


ASIDE

Installing apd as the correct answer requires is hard work; it was last released in 2004 and does not support php 5.3 out of the box. There is no Ubuntu package for it, as well as pecl install apd . So, here are the procedures for installing it (this is for ubuntu 10.04) (everything is done as root):

 pecl download apd tar xzf apd-1.0.1.tgz cd apd-1.0.1 phpize ./configure # Do edits (see below) make install 

See https://bugs.php.net/bug.php?id=58798 for the fix you need to do. NB. there is only one line that you really have to change, so you can do it manually, as follows: open php_apd.c in a text editor, go to line 967 and replace the line CG(extended_info) = 1 with this:

 CG(compiler_options) |= ZEND_COMPILE_EXTENDED_INFO; 

Finally, you need to add a php.ini entry to activate it. See http://php.net/manual/en/apd.installation.php

+1
source share
4 answers

If you donโ€™t have access to APD or Runkit, but are using namespaces, try the following: https://stackoverflow.com/a/212618/212 (the answer in the link refers to time (), but it doesn't matter)

+3
source

Change fsockopen to fopen to make mock, and do not change any other functions.

 $fp = fsockopen("www.example.com", 80, $errno, $errstr, 30); 

to

 $fp = fopen("/path/to/your/dummy_data_file"); 
+1
source

I blog about my experience , which contains a complete working code showing how to use override_function to achieve the desired goal; both reading files and reading sockets. I will not repeat this whole article here, but simply indicate how you should use the functions:

  • Use rename_function to give the name of the old, original function, so we can find it later.
  • Use override_function to define new behavior
  • Use rename_function to give a dummy name __overridden__

Step 1 is critical if you want to restore the original behavior afterwards.

In the โ€œFood for Thoughtโ€ section at the end, I show an alternative approach, which, in my opinion, is more reliable, and therefore I think it is safer to replace file functions when using phpUnit. It creates a constant replacement of functions, the default behavior of which is to switch to a built-in function. Then it checks the parameters (the $handle resource in this case) to decide if it will be called on a thread that requires different behavior. (I think you could call this an example of the Chain Of Responsibility template.)

0
source

All Articles