Skip to content

Commit b4c584f

Browse files
committed
Bring the code into shape
1 parent c254ed1 commit b4c584f

File tree

6 files changed

+191
-40
lines changed

6 files changed

+191
-40
lines changed

README.md

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,41 @@ composer require php-mock/php-mock-prophecy
1313

1414
# Usage
1515

16-
TODO
16+
Build a new [`PHPProphet`](http://php-mock.github.io/php-mock-prophecy/api/class-phpmock.prophecy.PHPProphet.html)
17+
and create function prophecies for a given namespace
18+
with [`PHPProphet::prophesize()`](http://php-mock.github.io/php-mock-prophecy/api/class-phpmock.prophecy.PHPProphet.html#_prophesize):
19+
20+
```php
21+
<?php
22+
23+
namespace foo;
24+
25+
use phpmock\prophecy\PHPProphet;
26+
27+
$prophet = new PHPProphet();
28+
29+
$prophecy = $prophet->prophesize(__NAMESPACE__);
30+
$prophecy->time()->willReturn(123);
31+
$prophecy->reveal();
32+
33+
assert(123 == time());
34+
$prophet->checkPredictions();
35+
```
36+
37+
## Restrictions
38+
39+
This library comes with the same restrictions as the underlying
40+
[`php-mock`](https://github.com/php-mock/php-mock#requirements-and-restrictions):
41+
42+
* Only *unqualified* function calls in a namespace context can be prophesized.
43+
E.g. a call for `time()` in the namespace `foo` is prophesizable,
44+
a call for `\time()` is not.
45+
46+
* The mock has to be defined before the first call to the unqualified function
47+
in the tested class. This is documented in [Bug #68541](https://bugs.php.net/bug.php?id=68541).
48+
In most cases you can ignore this restriction. But if you happen to run into
49+
this issue you can call [`PHPProphet::define()`](http://php-mock.github.io/php-mock-prophecy/api/class-phpmock.prophecy.PHPProphet.html#_define)
50+
before that first call. This would define a side effectless namespaced function.
1751

1852
# License and authors
1953

@@ -26,4 +60,3 @@ If you like this project and feel generous donate a few Bitcoins here:
2660
[1335STSwu9hST4vcMRppEPgENMHD2r1REK](bitcoin:1335STSwu9hST4vcMRppEPgENMHD2r1REK)
2761

2862
[![Build Status](https://travis-ci.org/php-mock/php-mock-prophecy.svg?branch=master)](https://travis-ci.org/php-mock/php-mock-prophecy)
29-

classes/FunctionProphecy.php

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,18 @@
88
use Prophecy\Prophecy\MethodProphecy;
99

1010
/**
11-
* Function prophecy.
11+
* A function prophecy.
1212
*
1313
* @author Markus Malkusch <[email protected]>
1414
* @link bitcoin:1335STSwu9hST4vcMRppEPgENMHD2r1REK Donations
1515
* @license http://www.wtfpl.net/txt/copying/ WTFPL
16+
* @internal
1617
*/
17-
class FunctionProphecy implements ProphecyInterface
18+
final class FunctionProphecy implements ProphecyInterface
1819
{
1920

2021
/**
21-
* @var Prophet The prophet
22+
* @var Prophet The prophet.
2223
*/
2324
private $prophet;
2425

@@ -35,9 +36,8 @@ class FunctionProphecy implements ProphecyInterface
3536
/**
3637
* Sets the prophet.
3738
*
38-
* @param string $namespace The namespace.
39-
* @param Prophet $prophet The prophet.
40-
* @internal
39+
* @param string $namespace function namespace
40+
* @param Prophet $prophet prophet
4141
*/
4242
public function __construct($namespace, Prophet $prophet)
4343
{
@@ -46,12 +46,13 @@ public function __construct($namespace, Prophet $prophet)
4646
}
4747

4848
/**
49-
* Creates a new function prophecy using specified function name and arguments.
49+
* Creates a new function prophecy using the specified function name
50+
* and arguments.
5051
*
51-
* @param string $functionName The function name.
52-
* @param array $arguments The arguments.
52+
* @param string $functionName function name
53+
* @param array $arguments arguments
5354
*
54-
* @return MethodProphecy The function prophecy.
55+
* @return MethodProphecy function prophecy
5556
*/
5657
public function __call($functionName, array $arguments)
5758
{
@@ -64,7 +65,7 @@ public function __call($functionName, array $arguments)
6465

6566
/**
6667
* Reveals the function prophecies.
67-
*
68+
*
6869
* I.e. the prophesized functions will become effective.
6970
*/
7071
public function reveal()

classes/PHPProphet.php

Lines changed: 77 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,31 @@
33
namespace phpmock\prophecy;
44

55
use phpmock\MockRegistry;
6+
use phpmock\MockBuilder;
67
use Prophecy\Prophet;
8+
use Prophecy\Prophecy\ProphecyInterface;
79
use Prophecy\Exception\Prediction\AggregateException;
810

911
/**
10-
* PHPProphet creates prophecies for functions.
12+
* A Prophet for built-in PHP functions.
13+
*
14+
* Example:
15+
* <code>
16+
* $prophet = new PHPProphet();
17+
*
18+
* $prophecy = $prophet->prophesize(__NAMESPACE__);
19+
* $prophecy->time()->willReturn(123);
20+
* $prophecy->reveal();
21+
*
22+
* assert(123 == time());
23+
* $prophet->checkPredictions();
24+
* <code>
1125
*
1226
* @author Markus Malkusch <[email protected]>
1327
* @link bitcoin:1335STSwu9hST4vcMRppEPgENMHD2r1REK Donations
1428
* @license http://www.wtfpl.net/txt/copying/ WTFPL
1529
*/
16-
class PHPProphet
30+
final class PHPProphet
1731
{
1832

1933
/**
@@ -22,21 +36,27 @@ class PHPProphet
2236
private $prophet;
2337

2438
/**
25-
* Sets an optional prophet.
26-
*
27-
* @param Prophet $prophet The prophet.
39+
* Builds the prophet.
40+
*
41+
* @param Prophet|null $prophet optional proxied prophet
2842
*/
2943
public function __construct(Prophet $prophet = null)
3044
{
31-
$this->prophet = is_null($prophet) ? new Prophet() : $prophet;
45+
if (is_null($prophet)) {
46+
$prophet = new Prophet();
47+
}
48+
49+
$revealer = new ReferencePreservingRevealer(self::getProperty($prophet, "revealer"));
50+
$util = self::getProperty($prophet, "util");
51+
$this->prophet = new Prophet($prophet->getDoubler(), $revealer, $util);
3252
}
3353

3454
/**
35-
* Creates a new function prophecy.
36-
*
37-
* @param string $namespace The function namespace.
38-
*
39-
* @return FunctionProphecy The function prophecy.
55+
* Creates a new function prophecy for a given namespace.
56+
*
57+
* @param string $namespace function namespace
58+
*
59+
* @return ProphecyInterface function prophecy
4060
*/
4161
public function prophesize($namespace)
4262
{
@@ -47,12 +67,56 @@ public function prophesize($namespace)
4767
* Checks all predictions defined by prophecies of this Prophet.
4868
*
4969
* It will also disable all previously revealed function prophecies.
50-
*
51-
* @throws AggregateException If any prediction fails
70+
*
71+
* @throws AggregateException If any prediction fails.
5272
*/
5373
public function checkPredictions()
5474
{
5575
MockRegistry::getInstance()->unregisterAll();
5676
$this->prophet->checkPredictions();
5777
}
78+
79+
/**
80+
* Defines the function prophecy in the given namespace.
81+
*
82+
* In most cases you don't have to call this method. {@link prophesize()}
83+
* is doing this for you. But if the prophecy is defined after the first
84+
* call in the tested class, the tested class doesn't resolve to the prophecy.
85+
* This is documented in Bug #68541. You therefore have to define
86+
* the namespaced function before the first call.
87+
*
88+
* Defining the function has no side effects. If the function was
89+
* already defined this method does nothing.
90+
*
91+
* @param string $namespace function namespace
92+
* @param string $name function name
93+
*
94+
* @see prophesize()
95+
* @link https://bugs.php.net/bug.php?id=68541 Bug #68541
96+
*/
97+
public static function define($namespace, $name)
98+
{
99+
$builder = new MockBuilder();
100+
$builder->setNamespace($namespace)
101+
->setName($name)
102+
->setFunction(function () {
103+
})
104+
->build()
105+
->define();
106+
}
107+
108+
/**
109+
* Returns a private property of a prophet.
110+
*
111+
* @param Prophet $prophet prophet
112+
* @param string $property property name
113+
*
114+
* @return mixed property value of that prophet
115+
*/
116+
private static function getProperty(Prophet $prophet, $property)
117+
{
118+
$reflection = new \ReflectionProperty($prophet, $property);
119+
$reflection->setAccessible(true);
120+
return $reflection->getValue($prophet);
121+
}
58122
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
<?php
2+
3+
namespace phpmock\prophecy;
4+
5+
use Prophecy\Prophecy\RevealerInterface;
6+
7+
/**
8+
* Revealer proxy which keeps references.
9+
*
10+
* @author Markus Malkusch <[email protected]>
11+
* @link bitcoin:1335STSwu9hST4vcMRppEPgENMHD2r1REK Donations
12+
* @license http://www.wtfpl.net/txt/copying/ WTFPL
13+
* @internal
14+
*/
15+
final class ReferencePreservingRevealer implements RevealerInterface
16+
{
17+
18+
/**
19+
* @var RevealerInterface The subject.
20+
*/
21+
private $revealer;
22+
23+
/**
24+
* Sets the subject.
25+
*
26+
* @param RevealerInterface $revealer proxied revealer
27+
*/
28+
public function __construct(RevealerInterface $revealer)
29+
{
30+
$this->revealer = $revealer;
31+
}
32+
33+
/**
34+
* @SuppressWarnings(PHPMD)
35+
*/
36+
public function reveal($value)
37+
{
38+
if (is_array($value)) {
39+
foreach ($value as &$item) {
40+
$item = $this->revealer->reveal($item);
41+
}
42+
return $value;
43+
44+
} else {
45+
return $this->revealer->reveal($value);
46+
}
47+
}
48+
}

classes/Revelation.php

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,14 @@
77
use Prophecy\Prophecy\ProphecyInterface;
88

99
/**
10-
* The single function revelation.
10+
* A function revelation.
1111
*
1212
* @author Markus Malkusch <[email protected]>
1313
* @link bitcoin:1335STSwu9hST4vcMRppEPgENMHD2r1REK Donations
1414
* @license http://www.wtfpl.net/txt/copying/ WTFPL
1515
* @internal
1616
*/
17-
class Revelation implements ProphecyInterface
17+
final class Revelation implements ProphecyInterface
1818
{
1919

2020
/**
@@ -28,16 +28,16 @@ class Revelation implements ProphecyInterface
2828
private $functionName;
2929

3030
/**
31-
* @var ProphecyInterface The prophecy
31+
* @var ProphecyInterface The prophecy.
3232
*/
3333
private $prophecy;
3434

3535
/**
36-
* Setup the revelation.
37-
*
38-
* @param String $namespace The namespace.
39-
* @param String $functionName The function name.
40-
* @param ProphecyInterface $prophecy The prophecy.
36+
* Builds the revelation.
37+
*
38+
* @param String $namespace function namespace
39+
* @param String $functionName function name
40+
* @param ProphecyInterface $prophecy prophecy
4141
*/
4242
public function __construct($namespace, $functionName, ProphecyInterface $prophecy)
4343
{
@@ -48,10 +48,10 @@ public function __construct($namespace, $functionName, ProphecyInterface $prophe
4848

4949
/**
5050
* Reveals the function prophecy.
51-
*
51+
*
5252
* I.e. the prophesized function will become effective.
53-
*
54-
* @return Mock The enabled function mock.
53+
*
54+
* @return Mock enabled function mock
5555
*/
5656
public function reveal()
5757
{

tests/PHPProphetTest.php

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
use Prophecy\Argument;
77

88
/**
9-
* Tests PHPProphet.
9+
* A tests for PHPProphet.
1010
*
1111
* @author Markus Malkusch <[email protected]>
1212
* @link bitcoin:1335STSwu9hST4vcMRppEPgENMHD2r1REK Donations
@@ -17,7 +17,7 @@ class PHPProphetTest extends AbstractMockTest
1717
{
1818

1919
/**
20-
* @var PHPProphet The SUT.
20+
* @var PHPProphet The SUT.
2121
*/
2222
private $prophet;
2323

@@ -36,9 +36,14 @@ protected function disableMocks()
3636
protected function mockFunction($namespace, $functionName, callable $function)
3737
{
3838
$prophecy = $this->prophet->prophesize($namespace);
39-
$prophecy->$functionName(Argument::cetera())->will(function(array $parameters) use ($function) {
39+
$prophecy->$functionName(Argument::cetera())->will(function (array $parameters) use ($function) {
4040
return call_user_func_array($function, $parameters);
4141
});
4242
$prophecy->reveal();
4343
}
44+
45+
protected function defineFunction($namespace, $functionName)
46+
{
47+
PHPProphet::define($namespace, $functionName);
48+
}
4449
}

0 commit comments

Comments
 (0)