Skip to content

Commit 83ac96f

Browse files
committed
Merge pull request #8 from pdaw/master
Integration with beberlei/assert library, resolves #3
2 parents 7c03232 + 48f613b commit 83ac96f

8 files changed

+198
-40
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
/vendor/
22
composer.lock
3+
/tests/cache/

README.md

+20
Original file line numberDiff line numberDiff line change
@@ -132,3 +132,23 @@ NOTE! The code in the invariant may not call any public non-static members of th
132132
indirectly. Doing so will result in a stack overflow, as the invariant will wind up being called in an
133133
infinitely recursive manner.
134134

135+
Integration with assertion library
136+
----------
137+
138+
To enhance capabilities of contracts, it's possible to use [assertion library](https://github.com/beberlei/assert).
139+
```php
140+
/**
141+
* Deposits fixed amount of money to the account
142+
*
143+
* @param float $amount
144+
*
145+
* @Contract\Ensure("Assert\Assertion::integer($this->balance)")
146+
*/
147+
public function deposit($amount)
148+
{
149+
$this->balance += $amount;
150+
}
151+
```
152+
153+
[More assertions](https://github.com/beberlei/assert#list-of-assertions)
154+

composer.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22
"name": "lisachenko/php-deal",
33
"description": "Design by Contract framework for PHP",
44
"require": {
5-
"goaop/framework": "~1.0|~2.0@dev"
5+
"goaop/framework": "~1.0|~2.0@dev",
6+
"beberlei/assert": "^2.4"
67
},
78
"require-dev": {
89
"symfony/console": "~2.7|~3.0"

src/Aspect/ContractCheckerAspect.php

+29-13
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
use Go\Lang\Annotation\Before;
1919
use PhpDeal\Annotation as Contract;
2020
use PhpDeal\Exception\ContractViolation;
21+
use DomainException;
2122

2223
/**
2324
*/
@@ -60,9 +61,11 @@ public function preConditionContract(MethodInvocation $invocation)
6061
continue;
6162
}
6263

63-
if (!$this->isContractSatisfied($object, $scope, $args, $annotation)) {
64-
throw new ContractViolation($invocation, $annotation->value);
65-
};
64+
try {
65+
$this->ensureContractSatisfied($object, $scope, $args, $annotation);
66+
} catch (DomainException $e) {
67+
throw new ContractViolation($invocation, $annotation->value, $e->getPrevious());
68+
}
6669
}
6770
}
6871

@@ -92,9 +95,11 @@ public function postConditionContract(MethodInvocation $invocation)
9295
continue;
9396
}
9497

95-
if (!$this->isContractSatisfied($object, $class->name, $args, $annotation)) {
96-
throw new ContractViolation($invocation, $annotation->value);
97-
};
98+
try {
99+
$this->ensureContractSatisfied($object, $class->name, $args, $annotation);
100+
} catch (DomainException $e) {
101+
throw new ContractViolation($invocation, $annotation->value, $e->getPrevious());
102+
}
98103
}
99104

100105
return $result;
@@ -126,9 +131,11 @@ public function invariantContract(MethodInvocation $invocation)
126131
continue;
127132
}
128133

129-
if (!$this->isContractSatisfied($object, $class->name, $args, $annotation)) {
130-
throw new ContractViolation($invocation, $annotation->value);
131-
};
134+
try {
135+
$this->ensureContractSatisfied($object, $class->name, $args, $annotation);
136+
} catch (DomainException $e) {
137+
throw new ContractViolation($invocation, $annotation->value, $e->getPrevious());
138+
}
132139
}
133140

134141
return $result;
@@ -141,10 +148,9 @@ public function invariantContract(MethodInvocation $invocation)
141148
* @param string $scope Scope of method
142149
* @param array $args List of arguments for the method
143150
* @param Annotation $annotation Contract annotation
144-
*
145-
* @return mixed
151+
* @throws DomainException
146152
*/
147-
private function isContractSatisfied($instance, $scope, array $args, $annotation)
153+
private function ensureContractSatisfied($instance, $scope, array $args, $annotation)
148154
{
149155
static $invoker = null;
150156
if (!$invoker) {
@@ -156,7 +162,17 @@ private function isContractSatisfied($instance, $scope, array $args, $annotation
156162
}
157163
$instance = is_object($instance) ? $instance : null;
158164

159-
return $invoker->bindTo($instance, $scope)->__invoke($args, $annotation->value);
165+
try {
166+
$invocationResult = $invoker->bindTo($instance, $scope)->__invoke($args, $annotation->value);
167+
} catch (\Exception $e) {
168+
throw new DomainException("", 0, $e);
169+
}
170+
171+
// we accept as a result only true or null
172+
// null may be a result of assertions from beberlei/assert which passed
173+
if ($invocationResult !== null && $invocationResult !== true) {
174+
throw new DomainException();
175+
}
160176
}
161177

162178
/**
+32-8
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,51 @@
11
<?php
2+
/**
3+
* PHP Deal framework
4+
*
5+
* @copyright Copyright 2014, Lisachenko Alexander <[email protected]>
6+
*
7+
* This source file is subject to the license that is bundled
8+
* with this source code in the file LICENSE.
9+
*/
10+
211
namespace PhpDeal\Functional;
312

4-
use PhpDeal\Exception\ContractViolation;
513
use PhpDeal\Stub\EnsureStub;
614

715
class EnsureContractTest extends \PHPUnit_Framework_TestCase
816
{
17+
/**
18+
* @var EnsureStub
19+
*/
20+
private $stub;
21+
22+
public function setUp()
23+
{
24+
parent::setUp();
25+
$this->stub = new EnsureStub();
26+
}
27+
28+
public function tearDown()
29+
{
30+
unset($this->stub);
31+
parent::tearDown();
32+
}
33+
934
public function testEnsureValid()
1035
{
11-
$ensureStub = new EnsureStub();
12-
$ensureStub->increment(50);
36+
$this->stub->increment(50);
1337
}
1438

39+
/**
40+
* @expectedException \PhpDeal\Exception\ContractViolation
41+
*/
1542
public function testEnsureInvalid()
1643
{
17-
$this->setExpectedException(ContractViolation::class);
18-
$ensureStub = new EnsureStub();
19-
$ensureStub->badIncrement(40);
44+
$this->stub->badIncrement(40);
2045
}
2146

2247
public function testEnsureCanHandleResult()
2348
{
24-
$ensureStub = new EnsureStub();
25-
$ensureStub->returnPrivateValue();
49+
$this->stub->returnPrivateValue();
2650
}
2751
}
+36-10
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,55 @@
11
<?php
2+
/**
3+
* PHP Deal framework
4+
*
5+
* @copyright Copyright 2014, Lisachenko Alexander <[email protected]>
6+
*
7+
* This source file is subject to the license that is bundled
8+
* with this source code in the file LICENSE.
9+
*/
10+
211
namespace PhpDeal\Functional;
312

4-
use PhpDeal\Exception\ContractViolation;
513
use PhpDeal\Stub\Speed;
614

715
class InvariantContractTest extends \PHPUnit_Framework_TestCase
816
{
17+
/**
18+
* @var Speed
19+
*/
20+
private $stub;
21+
22+
public function setUp()
23+
{
24+
parent::setUp();
25+
$this->stub = new Speed();
26+
}
27+
28+
public function tearDown()
29+
{
30+
unset($this->stub);
31+
parent::tearDown();
32+
}
33+
934
public function testInvariantValid()
1035
{
11-
$speed = new Speed();
12-
$speed->accelerate(10, 30); // let's have a speed 300m/s
36+
$this->stub->accelerate(10, 30); // let's have a speed 300m/s
1337
}
1438

39+
/**
40+
* @expectedException \PhpDeal\Exception\ContractViolation
41+
*/
1542
public function testInvariantViolated()
1643
{
17-
$this->setExpectedException(ContractViolation::class);
18-
$speed = new Speed();
19-
$speed->accelerate(10, 3e7); // let's have a speed 3*1e8 m/s, faster than light!
44+
$this->stub->accelerate(10, 3e7); // let's have a speed 3*1e8 m/s, faster than light!
2045
}
2146

47+
/**
48+
* @expectedException \PhpDeal\Exception\ContractViolation
49+
*/
2250
public function testInvariantViolatedAfterSeveralMethods()
2351
{
24-
$this->setExpectedException(ContractViolation::class);
25-
$speed = new Speed();
26-
$speed->accelerate(10, 30); // let's have a speed 300m/s
27-
$speed->decelerate(20, 20); // Negative speed?
52+
$this->stub->accelerate(10, 30); // let's have a speed 300m/s
53+
$this->stub->decelerate(20, 20); // Negative speed?
2854
}
2955
}
+65-8
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,84 @@
11
<?php
2+
/**
3+
* PHP Deal framework
4+
*
5+
* @copyright Copyright 2014, Lisachenko Alexander <[email protected]>
6+
*
7+
* This source file is subject to the license that is bundled
8+
* with this source code in the file LICENSE.
9+
*/
10+
211
namespace PhpDeal\Functional;
312

4-
use PhpDeal\Exception\ContractViolation;
513
use PhpDeal\Stub\VerifyStub;
614

715
class VerifyContractTest extends \PHPUnit_Framework_TestCase
816
{
17+
/**
18+
* @var VerifyStub
19+
*/
20+
private $stub;
21+
22+
public function setUp()
23+
{
24+
parent::setUp();
25+
$this->stub = new VerifyStub();
26+
}
27+
28+
public function tearDown()
29+
{
30+
unset($this->stub);
31+
parent::tearDown();
32+
}
33+
934
public function testVerifyValid()
1035
{
11-
$verifyStub = new VerifyStub();
12-
$verifyStub->testNumeric(-200);
36+
$this->stub->testNumeric(-200);
1337
}
1438

39+
/**
40+
* @expectedException \PhpDeal\Exception\ContractViolation
41+
*/
1542
public function testVerifyInvalid()
1643
{
17-
$this->setExpectedException(ContractViolation::class);
18-
$verifyStub = new VerifyStub();
19-
$verifyStub->testNumeric('message');
44+
$this->stub->testNumeric('message');
2045
}
2146

2247
public function testAccessToPrivateFields()
2348
{
24-
$verifyStub = new VerifyStub();
25-
$verifyStub->testAccessToPrivateField(50);
49+
$this->stub->testAccessToPrivateField(50);
50+
}
51+
52+
public function testVerifyWithAssertValid()
53+
{
54+
$this->stub->add(100);
55+
}
56+
57+
public function providerVerifyWithAssertInvalid()
58+
{
59+
return [
60+
[
61+
'value' => ""
62+
],
63+
[
64+
'value' => 5.5
65+
],
66+
[
67+
'value' => null
68+
],
69+
[
70+
'value' => []
71+
]
72+
];
73+
}
74+
75+
/**
76+
* @param mixed $value
77+
* @dataProvider providerVerifyWithAssertInvalid
78+
* @expectedException \PhpDeal\Exception\ContractViolation
79+
*/
80+
public function testVerifyWithAssertInvalid($value)
81+
{
82+
$this->stub->add($value);
2683
}
2784
}

tests/Stub/VerifyStub.php

+13
Original file line numberDiff line numberDiff line change
@@ -47,4 +47,17 @@ public function testAccessToPrivateField($variable)
4747
{
4848
return;
4949
}
50+
51+
/**
52+
* Method with contract integrated with beberlei/assert library
53+
*
54+
* @param int $value
55+
* @return bool
56+
*
57+
* @Contract\Verify("Assert\Assertion::integer($value)")
58+
*/
59+
public function add($value)
60+
{
61+
return true;
62+
}
5063
}

0 commit comments

Comments
 (0)