Skip to content

Commit a1e3bd9

Browse files
committed
Merge pull request #13 from lisachenko/simplify-code-after-changes
Simplify code after PR
2 parents 56c4bea + e1abbae commit a1e3bd9

13 files changed

+285
-469
lines changed

src/Aspect/AbstractContractAspect.php

+97
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
<?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+
11+
namespace PhpDeal\Aspect;
12+
13+
use Doctrine\Common\Annotations\Annotation;
14+
use Doctrine\Common\Annotations\Reader;
15+
use DomainException;
16+
use Go\Aop\Intercept\MethodInvocation;
17+
use PhpDeal\Exception\ContractViolation;
18+
19+
abstract class AbstractContractAspect
20+
{
21+
/**
22+
* @var Reader
23+
*/
24+
protected $reader;
25+
26+
/**
27+
* @param Reader $reader Annotation reader
28+
*/
29+
public function __construct(Reader $reader)
30+
{
31+
$this->reader = $reader;
32+
}
33+
34+
/**
35+
* Returns an associative list of arguments for the method invocation
36+
*
37+
* @param MethodInvocation $invocation
38+
* @return array
39+
*/
40+
protected function fetchMethodArguments(MethodInvocation $invocation)
41+
{
42+
$parameters = $invocation->getMethod()->getParameters();
43+
$argumentNames = array_map(function (\ReflectionParameter $parameter) {
44+
return $parameter->name;
45+
}, $parameters);
46+
$parameters = array_combine($argumentNames, $invocation->getArguments());
47+
48+
return $parameters;
49+
}
50+
51+
/**
52+
* Performs verification of contracts for given invocation
53+
*
54+
* @param MethodInvocation $invocation Current invocation
55+
* @param array|Annotation[] $contracts Contract annotation
56+
* @param object|string $instance Invocation instance or string for static class
57+
* @param string $scope Scope of method
58+
* @param array $args List of arguments for the method
59+
*
60+
* @throws DomainException
61+
*/
62+
protected function ensureContracts(MethodInvocation $invocation, array $contracts, $instance, $scope, array $args)
63+
{
64+
static $invoker = null;
65+
if (!$invoker) {
66+
$invoker = function () {
67+
extract(func_get_arg(0));
68+
69+
return eval('return ' . func_get_arg(1) . '; ?>');
70+
};
71+
}
72+
73+
$instance = is_object($instance) ? $instance : null;
74+
$boundInvoker = $invoker->bindTo($instance, $scope);
75+
76+
foreach ($contracts as $contract) {
77+
$contractExpression = $contract->value;
78+
try {
79+
$invocationResult = $boundInvoker->__invoke($args, $contractExpression);
80+
81+
// we accept as a result only true or null
82+
// null may be a result of assertions from beberlei/assert which passed
83+
if ($invocationResult !== null && $invocationResult !== true) {
84+
$errorMessage = 'Invalid return value received from the assertion body,'
85+
. ' only boolean or void can be returned';
86+
throw new DomainException($errorMessage);
87+
}
88+
89+
} catch (\Error $internalError) {
90+
// PHP-7 friendly interceptor for fatal errors
91+
throw new ContractViolation($invocation, $contractExpression, $internalError);
92+
} catch (\Exception $internalException) {
93+
throw new ContractViolation($invocation, $contractExpression, $internalException);
94+
}
95+
}
96+
}
97+
}

src/Aspect/InvariantCheckerAspect.php

+38-6
Original file line numberDiff line numberDiff line change
@@ -13,20 +13,23 @@
1313
use Doctrine\Common\Annotations\Reader;
1414
use Go\Aop\Aspect;
1515
use Go\Aop\Intercept\MethodInvocation;
16-
use PhpDeal\Contract\InvariantContract;
16+
use PhpDeal\Annotation\Invariant;
17+
use PhpDeal\Contract\Fetcher\ParentClass\InvariantFetcher;
1718
use PhpDeal\Exception\ContractViolation;
1819
use Go\Lang\Annotation\Around;
20+
use ReflectionClass;
1921

20-
class InvariantCheckerAspect implements Aspect
22+
class InvariantCheckerAspect extends AbstractContractAspect implements Aspect
2123
{
2224
/**
23-
* @var InvariantContract
25+
* @var InvariantFetcher
2426
*/
25-
private $contractChecker;
27+
private $invariantFetcher;
2628

2729
public function __construct(Reader $reader)
2830
{
29-
$this->contractChecker = new InvariantContract($reader);
31+
parent::__construct($reader);
32+
$this->invariantFetcher = new InvariantFetcher(Invariant::class, $reader);
3033
}
3134

3235
/**
@@ -40,6 +43,35 @@ public function __construct(Reader $reader)
4043
*/
4144
public function invariantContract(MethodInvocation $invocation)
4245
{
43-
return $this->contractChecker->check($invocation);
46+
$object = $invocation->getThis();
47+
$args = $this->fetchMethodArguments($invocation);
48+
$class = $invocation->getMethod()->getDeclaringClass();
49+
if ($class->isCloneable()) {
50+
$args['__old'] = clone $object;
51+
}
52+
53+
$result = $invocation->proceed();
54+
$args['__result'] = $result;
55+
56+
$allContracts = $this->fetchAllContracts($class);
57+
$this->ensureContracts($invocation, $allContracts, $object, $class->name, $args);
58+
59+
return $result;
60+
}
61+
62+
/**
63+
* @param ReflectionClass $class
64+
* @return array
65+
*/
66+
private function fetchAllContracts(ReflectionClass $class)
67+
{
68+
$allContracts = $this->invariantFetcher->getConditions($class);
69+
foreach ($this->reader->getClassAnnotations($class) as $annotation) {
70+
if ($annotation instanceof Invariant) {
71+
$allContracts[] = $annotation;
72+
}
73+
}
74+
75+
return array_unique($allContracts);
4476
}
4577
}

src/Aspect/PostconditionCheckerAspect.php

+49-6
Original file line numberDiff line numberDiff line change
@@ -13,20 +13,22 @@
1313
use Doctrine\Common\Annotations\Reader;
1414
use Go\Aop\Aspect;
1515
use Go\Aop\Intercept\MethodInvocation;
16-
use PhpDeal\Contract\PostconditionContract;
16+
use PhpDeal\Annotation\Ensure;
17+
use PhpDeal\Contract\Fetcher\ParentClass\MethodConditionFetcher;
1718
use PhpDeal\Exception\ContractViolation;
1819
use Go\Lang\Annotation\Around;
1920

20-
class PostconditionCheckerAspect implements Aspect
21+
class PostconditionCheckerAspect extends AbstractContractAspect implements Aspect
2122
{
2223
/**
23-
* @var PostconditionContract
24+
* @var MethodConditionFetcher
2425
*/
25-
private $contractChecker;
26+
private $methodConditionFetcher;
2627

2728
public function __construct(Reader $reader)
2829
{
29-
$this->contractChecker = new PostconditionContract($reader);
30+
parent::__construct($reader);
31+
$this->methodConditionFetcher = new MethodConditionFetcher(Ensure::class, $reader);
3032
}
3133

3234
/**
@@ -40,6 +42,47 @@ public function __construct(Reader $reader)
4042
*/
4143
public function postConditionContract(MethodInvocation $invocation)
4244
{
43-
return $this->contractChecker->check($invocation);
45+
$object = $invocation->getThis();
46+
$args = $this->fetchMethodArguments($invocation);
47+
$class = $invocation->getMethod()->getDeclaringClass();
48+
if ($class->isCloneable()) {
49+
$args['__old'] = clone $object;
50+
}
51+
52+
$result = $invocation->proceed();
53+
$args['__result'] = $result;
54+
$allContracts = $this->fetchAllContracts($invocation);
55+
56+
$this->ensureContracts($invocation, $allContracts, $object, $class->name, $args);
57+
58+
return $result;
59+
}
60+
61+
/**
62+
* @param MethodInvocation $invocation
63+
* @return array
64+
*/
65+
private function fetchAllContracts(MethodInvocation $invocation)
66+
{
67+
$allContracts = $this->fetchParentsContracts($invocation);
68+
foreach ($invocation->getMethod()->getAnnotations() as $annotation) {
69+
if ($annotation instanceof Ensure) {
70+
$allContracts[] = $annotation;
71+
}
72+
}
73+
74+
return array_unique($allContracts);
75+
}
76+
77+
/**
78+
* @param MethodInvocation $invocation
79+
* @return array
80+
*/
81+
private function fetchParentsContracts(MethodInvocation $invocation)
82+
{
83+
return $this->methodConditionFetcher->getConditions(
84+
$invocation->getMethod()->getDeclaringClass(),
85+
$invocation->getMethod()->name
86+
);
4487
}
4588
}

src/Aspect/PreconditionCheckerAspect.php

+42-6
Original file line numberDiff line numberDiff line change
@@ -13,20 +13,22 @@
1313
use Doctrine\Common\Annotations\Reader;
1414
use Go\Aop\Aspect;
1515
use Go\Aop\Intercept\MethodInvocation;
16-
use PhpDeal\Contract\PreconditionContract;
16+
use PhpDeal\Annotation\Verify;
17+
use PhpDeal\Contract\Fetcher\ParentClass\MethodConditionWithInheritDocFetcher;
1718
use PhpDeal\Exception\ContractViolation;
1819
use Go\Lang\Annotation\Before;
1920

20-
class PreconditionCheckerAspect implements Aspect
21+
class PreconditionCheckerAspect extends AbstractContractAspect implements Aspect
2122
{
2223
/**
23-
* @var PreconditionContract
24+
* @var MethodConditionWithInheritDocFetcher
2425
*/
25-
private $contractChecker;
26+
private $methodConditionFetcher;
2627

2728
public function __construct(Reader $reader)
2829
{
29-
$this->contractChecker = new PreconditionContract($reader);
30+
parent::__construct($reader);
31+
$this->methodConditionFetcher = new MethodConditionWithInheritDocFetcher(Verify::class, $reader);
3032
}
3133

3234
/**
@@ -39,6 +41,40 @@ public function __construct(Reader $reader)
3941
*/
4042
public function preConditionContract(MethodInvocation $invocation)
4143
{
42-
$this->contractChecker->check($invocation);
44+
$object = $invocation->getThis();
45+
$args = $this->fetchMethodArguments($invocation);
46+
$scope = $invocation->getMethod()->getDeclaringClass()->name;
47+
48+
$allContracts = $this->fetchAllContracts($invocation);
49+
$this->ensureContracts($invocation, $allContracts, $object, $scope, $args);
50+
}
51+
52+
/**
53+
* @param MethodInvocation $invocation
54+
* @return array
55+
*/
56+
private function fetchAllContracts(MethodInvocation $invocation)
57+
{
58+
$allContracts = $this->fetchParentsContracts($invocation);
59+
60+
foreach ($invocation->getMethod()->getAnnotations() as $annotation) {
61+
if ($annotation instanceof Verify) {
62+
$allContracts[] = $annotation;
63+
}
64+
}
65+
66+
return array_unique($allContracts);
67+
}
68+
69+
/**
70+
* @param MethodInvocation $invocation
71+
* @return array
72+
*/
73+
private function fetchParentsContracts(MethodInvocation $invocation)
74+
{
75+
return $this->methodConditionFetcher->getConditions(
76+
$invocation->getMethod()->getDeclaringClass(),
77+
$invocation->getMethod()->name
78+
);
4379
}
4480
}

0 commit comments

Comments
 (0)