This is what I use:
<?php
##############################################################################
#
# Mocking class that provides fine-grained control over the behavior of
# built-in PHP functions.
#
# From the unit test, the tester can control the behavior of mocked
# built-in functions. For instance, if we want file_exists to return
# 'false' for a particular test, we can do the following:
#
# $PHP = new MockPHPWrapper();
# $PHP->defaultRetvals['file_exists'] = false;
# $classUnderTest = new SomeClass($PHP);
#
#
#
# In this example, "SomeClass" is a class that has been modified to make
# all calls to PHP built-in functions using the real PHPWrapper.
# For tests, we supply a MockPHPWrapper instance, run a test, then validate
# the results in the MockPHPWrapper instance.
#
##############################################################################
class MockPHPWrapper {
public $defaultRetvals;
public $call_count_array;
public $call_param_array;
public $retval_overrides_array;
public function __construct() {
$this->call_count_array = array();
$this->call_param_array = array();
$this->retval_overrides_array = array();
$this->setDefaultRetvals();
}
##########################################################################
#
# Sets the return value to be used for a single instance of an invocation
# of a mocked method.
#
# $funcname - name of the modcked method
# $invocationIndex - specifies the zero-based index of the invocation -
# If $funcname is 'xyz', $invocationIndex
# is 2 and $retVal is 42, then the 3rd
# invocaetion of 'xyz' with return 42.
##########################################################################
public function overrideReturnValue($funcname, $invocationIndex, $retVal, $isBool = false) {
if ($isBool && $retVal === false) {
$retVal = new FalsePlaceholder();
}
if ( !array_key_exists($funcname, $this->retval_overrides_array)) {
$this->retval_overrides_array[$funcname] = array();
}
$this->retval_overrides_array[$funcname][$invocationIndex] = $retVal;
}
private function setDefaultRetvals() {
$this->defaultRetvals = array(
'is_readable'=>true,
'file_exists'=>true,
'is_executable'=>true,
'unlink'=>true,
'exec'=>true,
'is_dir'=>true,
'dirname'=>'/some/dir',
'mkdir'=>true,
'curl_init'=>true,
'curl_setopt'=>true,
'curl_exec'=>true,
'curl_getinfo'=>'infostring',
'curl_close'=>null,
'fopen'=>1,
'filesize'=>2,
'file_put_contents'=>3);
// for each key in the defaultRetvals array, we will
// add a 0 entry for the call_count_array
foreach ($this->defaultRetvals as $key=>$value) {
if (!array_key_exists($key, $this->call_count_array)) {
$this->call_count_array[$key] = 0;
// now initialize the param array element
$this->call_param_array[$key] = array();
}
}
}
public function getLastParameter($funcname) {
return end($this->call_param_array[$funcname]);
}
public function getParametersByIndex($funcname, $n) {
return $this->call_param_array[$funcname][$n];
}
protected function returnValue($funcname) {
$currentCallCount = $this->call_count_array[$funcname] - 1;
if (array_key_exists($funcname, $this->retval_overrides_array) &&
array_key_exists($currentCallCount, $this->retval_overrides_array[$funcname])) {
$retval = $this->retval_overrides_array[$funcname][$currentCallCount];
if (is_object($retval) && get_class($retval) === 'FalsePlaceholder') {
$retval = false;
return $retval;
}
return $retval;
} else {
$defaultRetval = $this->defaultRetvals[$funcname];
return $defaultRetval;
}
}
public function dumpCountResults() {
return json_encode($this->call_count_array);
}
public function dumpDefaultRetvals() {
return json_encode($this->defaultRetvals);
}
public function dumpOverrideRetvals() {
return json_encode($this->retval_overrides_array);
}
# Mocks
public function is_readable($in){
$this->call_count_array[__FUNCTION__]++;
array_push($this->call_param_array[__FUNCTION__], $in);
return $this->returnValue(__FUNCTION__);
}
public function file_exists($in) {
$this->call_count_array[__FUNCTION__]++;
array_push($this->call_param_array[__FUNCTION__], $in);
return $this->returnValue(__FUNCTION__);
}
public function is_executable($in) {
$this->call_count_array[__FUNCTION__]++;
array_push($this->call_param_array[__FUNCTION__], $in);
return $this->returnValue(__FUNCTION__);
}
public function unlink($in) {
$this->call_count_array[__FUNCTION__]++;
array_push($this->call_param_array[__FUNCTION__], $in);
return $this->returnValue(__FUNCTION__);
}
public function exec($in) {
$this->call_count_array[__FUNCTION__]++;
array_push($this->call_param_array[__FUNCTION__], $in);
return $this->returnValue(__FUNCTION__);
}
public function is_dir($in) {
$this->call_count_array[__FUNCTION__]++;
array_push($this->call_param_array[__FUNCTION__], $in);
return $this->returnValue(__FUNCTION__);
}
public function dirname($in) {
$this->call_count_array[__FUNCTION__]++;
array_push($this->call_param_array[__FUNCTION__], $in);
return $this->returnValue(__FUNCTION__);
}
public function mkdir($in1, $in2, $in3) {
$this->call_count_array[__FUNCTION__]++;
array_push($this->call_param_array[__FUNCTION__], array($in1, $in2, $in3));
return $this->returnValue(__FUNCTION__);
}
public function curl_init($in) {
$this->call_count_array[__FUNCTION__]++;
$this->call_param_array[__FUNCTION__] = array($in);
return $this->returnValue(__FUNCTION__);
}
public function curl_setopt($in1, $in2, $in3) {
$this->call_count_array[__FUNCTION__]++;
array_push($this->call_param_array[__FUNCTION__], array($in1, $in2, $in3));
return $this->returnValue(__FUNCTION__);
}
public function curl_exec($in) {
$this->call_count_array[__FUNCTION__]++;
$this->call_param_array[__FUNCTION__] = array($in);
return $this->returnValue(__FUNCTION__);
}
public function curl_getinfo($in1, $in2) {
$this->call_count_array[__FUNCTION__]++;
array_push($this->call_param_array[__FUNCTION__], array($in1, $in2));
return $this->returnValue(__FUNCTION__);
}
public function curl_close($in) {
$this->call_count_array[__FUNCTION__]++;
$this->call_param_array[__FUNCTION__] = array($in);
return null;
}
public function fopen($in1, $in2) {
$this->call_count_array[__FUNCTION__]++;
array_push($this->call_param_array[__FUNCTION__], array($in1, $in2));
return $this->returnValue(__FUNCTION__);
}
public function filesize($in) {
$this->call_count_array[__FUNCTION__]++;
array_push($this->call_param_array[__FUNCTION__], $in);
return $this->returnValue(__FUNCTION__);
}
public function file_put_contents($in1, $in2) {
$this->call_count_array[__FUNCTION__]++;
array_push($this->call_param_array[__FUNCTION__], array($in1, $in2));
return $this->returnValue(__FUNCTION__);
}
}
class FalsePlaceholder {}
?>
The mock wrapper above is used for tests, and live code just delegates to actual built-ins through this simple wrapper:
<?php
/*
* PHP wrapper used by some scripts
*
*/
##############################################################################
#
# Utility wrapper for built-in PHP functions
# (very helpful for making code unit-testable)
#
##############################################################################
class PHPWrapper {
public function is_readable($in) {
return is_readable($in);
}
public function file_exists($file) {
return file_exists($file);
}
public function is_executable($in) {
return is_executable($in);
}
public function unlink($in) {
return unlink($in);
}
public function exec($in) {
return exec($in);
}
public function is_dir($in) {
return is_dir($in);
}
public function dirname($in) {
return dirname($in);
}
public function mkdir($name, $perms, $recur) {
return mkdir($name, $perms, $recur);
}
public function curl_init($url) {
return curl_init($url);
}
public function curl_setopt($handle, $option, $value) {
return curl_setopt($handle, $option, $value);
}
public function curl_exec($handle) {
return curl_exec($handle);
}
public function curl_getinfo($handle, $option = null) {
return curl_getinfo($handle, $option);
}
public function curl_close($handle) {
return curl_close($handle);
}
public function fopen($filename, $mode) {
return fopen($filename, $mode);
}
public function filesize($filename) {
return filesize($filename);
}
public function file_put_contents($filename, $contents) {
return file_put_contents($filename, $contents);
}
}
?>```