1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88: 89: 90: 91: 92: 93: 94: 95: 96: 97: 98: 99: 100: 101: 102: 103: 104: 105: 106: 107: 108: 109: 110: 111: 112: 113: 114: 115: 116: 117: 118: 119: 120: 121: 122: 123: 124: 125: 126: 127: 128: 129: 130: 131: 132: 133: 134: 135: 136: 137: 138: 139: 140: 141: 142: 143: 144: 145: 146: 147: 148: 149: 150: 151: 152: 153: 154: 155: 156: 157: 158: 159: 160: 161: 162: 163: 164: 165: 166: 167: 168: 169: 170: 171: 172: 173: 174: 175: 176: 177: 178: 179: 180: 181: 182: 183: 184: 185: 186: 187: 188: 189: 190: 191:
<?php
namespace phpmock;
use phpmock\generator\MockFunctionGenerator;
/**
* Mocking framework for built-in PHP functions.
*
* Mocking a build-in PHP function is achieved by using
* PHP's namespace fallback policy. A mock will provide the namespaced function.
* I.e. only unqualified functions in a non-global namespace can be mocked.
*
* Example:
* <code>
* namespace foo;
*
* use phpmock\Mock;
*
* $time = new Mock(
* __NAMESPACE__,
* "time",
* function () {
* return 3;
* }
* );
* $time->enable();
* assert (3 == time());
*
* $time->disable();
* assert (3 != time());
* </code>
*
* @author Markus Malkusch <markus@malkusch.de>
* @link bitcoin:1335STSwu9hST4vcMRppEPgENMHD2r1REK Donations
* @license http://www.wtfpl.net/txt/copying/ WTFPL
* @see MockBuilder
*/
class Mock implements Deactivatable
{
/**
* @var string namespace for the mock function.
*/
private $namespace;
/**
* @var string function name of the mocked function.
*/
private $name;
/**
* @var callable The function mock.
*/
private $function;
/**
* Set the namespace, function name and the mock function.
*
* @param string $namespace The namespace for the mock function.
* @param string $name The function name of the mocked function.
* @param callable $function The mock function.
*/
public function __construct($namespace, $name, callable $function)
{
if (empty($namespace)) {
throw new \InvalidArgumentException("Namespace should not be empty");
}
if (empty($name)) {
throw new \InvalidArgumentException("Function name should not be empty");
}
$this->namespace = $namespace;
$this->name = $name;
$this->function = $function;
}
/**
* Enables this mock.
*
* @throws MockEnabledException If the function has already an enabled mock.
* @see Mock::disable()
* @see Mock::disableAll()
*
* @SuppressWarnings(PHPMD)
*/
public function enable()
{
$registry = MockRegistry::getInstance();
if ($registry->isRegistered($this)) {
throw new MockEnabledException(
"$this->name is already enabled."
. "Call disable() on the existing mock."
);
}
$this->define();
$registry->register($this);
}
/**
* Disable this mock.
*
* @see Mock::enable()
* @see Mock::disableAll()
*/
public function disable()
{
MockRegistry::getInstance()->unregister($this);
}
/**
* Disable all mocks.
*
* @see Mock::enable()
* @see Mock::disable()
*/
public static function disableAll()
{
MockRegistry::getInstance()->unregisterAll();
}
/**
* Calls the mocked function.
*
* This method is called from the namespaced function.
*
* @param array $arguments the call arguments.
* @return mixed
* @internal
*/
public function call(array $arguments)
{
return call_user_func_array($this->function, $arguments);
}
/**
* Returns the fully qualified function name.
*
* @return string The function name with its namespace.
* @internal
*/
public function getFQFN()
{
return strtolower("{$this->getNamespace()}\\$this->name");
}
/**
* Returns the namespace without enclosing slashes.
*
* @return string The namespace
*/
public function getNamespace()
{
return trim($this->namespace, "\\");
}
/**
* Returns the unqualified function name.
*
* @return string The name of the mocked function.
*/
public function getName()
{
return $this->name;
}
/**
* Defines the mocked function in the given namespace.
*
* In most cases you don't have to call this method. enable() is doing this
* for you. But if the mock is defined after the first call in the
* tested class, the tested class doesn't resolve to the mock. This is
* documented in Bug #68541. You therefore have to define the namespaced
* function before the first call. Defining the function has no side
* effects as you still have to enable the mock. If the function was
* already defined this method does nothing.
*
* @see enable()
* @link https://bugs.php.net/bug.php?id=68541 Bug #68541
*/
public function define()
{
$fqfn = $this->getFQFN();
if (function_exists($fqfn)) {
return;
}
$functionGenerator = new MockFunctionGenerator($this);
$functionGenerator->defineFunction();
}
}