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
|
<?php
namespace phpmock;
use InvalidArgumentException;
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();
}
}
|