InheritCheckerAspect::inheritMethodContracts()   A
last analyzed

Complexity

Conditions 3
Paths 2

Size

Total Lines 17

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 3

Importance

Changes 0
Metric Value
dl 0
loc 17
ccs 11
cts 11
cp 1
rs 9.7
c 0
b 0
f 0
cc 3
nc 2
nop 1
crap 3
1
<?php
2
3
namespace PhpDeal\Aspect;
4
5
use Doctrine\Common\Annotations\Reader;
6
use Go\Aop\Aspect;
7
use Go\Aop\Intercept\MethodInvocation;
8
use Go\Aop\Support\AnnotatedReflectionMethod;
9
use Go\Lang\Annotation\Around;
10
use PhpDeal\Annotation\Ensure;
11
use PhpDeal\Annotation\Invariant;
12
use PhpDeal\Annotation\Verify;
13
use PhpDeal\Contract\Fetcher\Parent\InvariantFetcher;
14
use PhpDeal\Contract\Fetcher\Parent\MethodConditionFetcher;
15
use PhpDeal\Exception\ContractViolation;
16
use ReflectionClass;
17
use ReflectionMethod;
18
19
class InheritCheckerAspect extends AbstractContractAspect implements Aspect
20
{
21
    /**
22
     * @var MethodConditionFetcher
23
     */
24
    private $methodConditionFetcher;
25
26
    /** @var InvariantFetcher */
27
    private $invariantFetcher;
28
29
    public function __construct(Reader $reader)
30
    {
31
        parent::__construct($reader);
32
        $this->methodConditionFetcher = new MethodConditionFetcher([
33
            Ensure::class,
34
            Verify::class,
35
            Invariant::class
36
        ], $reader);
37
        $this->invariantFetcher = new InvariantFetcher([Invariant::class], $reader);
38
    }
39
40
    /**
41
     * Verifies inherit contracts for the method
42
     *
43
     * @Around("@execution(PhpDeal\Annotation\Inherit)")
44
     * @param MethodInvocation $invocation
45
     *
46
     * @throws ContractViolation
47
     * @throws \ReflectionException
48
     * @return mixed
49
     */
50 2
    public function inheritMethodContracts(MethodInvocation $invocation)
51
    {
52 2
        $object = $invocation->getThis();
53 2
        $args   = $this->fetchMethodArguments($invocation);
54 2
        $class  = $invocation->getMethod()->getDeclaringClass();
55 2
        if (\is_object($object) && $class->isCloneable()) {
56 2
            $args['__old'] = clone $object;
57
        }
58
59 2
        $result = $invocation->proceed();
60 2
        $args['__result'] = $result;
61 2
        $allContracts = $this->fetchMethodContracts($invocation);
62
63 2
        $this->ensureContracts($invocation, $allContracts, $object, $class->name, $args);
64
65 1
        return $result;
66
    }
67
68
    /**
69
     * @Around("@within(PhpDeal\Annotation\Inherit) && execution(public **->*(*))")
70
     * @param MethodInvocation $invocation
71
     * @return mixed
72
     * @throws \ReflectionException
73
     */
74
    public function inheritClassContracts(MethodInvocation $invocation)
75
    {
76
        $object = $invocation->getThis();
77
        $args   = $this->fetchMethodArguments($invocation);
78
        $class  = $invocation->getMethod()->getDeclaringClass();
79
        if (\is_object($object) && $class->isCloneable()) {
80
            $args['__old'] = clone $object;
81
        }
82
83
        $result = $invocation->proceed();
84
        $args['__result'] = $result;
85
86
        $allContracts = $this->fetchClassContracts($class);
87
        $this->ensureContracts($invocation, $allContracts, $object, $class->name, $args);
88
89
        return $result;
90
    }
91
92
    /**
93
     * @param MethodInvocation $invocation
94
     * @return array
95
     * @throws \ReflectionException
96
     */
97 2
    private function fetchMethodContracts(MethodInvocation $invocation): array
98
    {
99 2
        $allContracts = $this->fetchParentsMethodContracts($invocation);
100
        /** @var ReflectionMethod&AnnotatedReflectionMethod $reflectionMethod */
0 ignored issues
show
Documentation introduced by
The doc-type ReflectionMethod&AnnotatedReflectionMethod could not be parsed: Unknown type name "ReflectionMethod&AnnotatedReflectionMethod" at position 0. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
101 2
        $reflectionMethod = $invocation->getMethod();
102
103 2
        foreach ($reflectionMethod->getAnnotations() as $annotation) {
104 2
            $annotationClass = \get_class($annotation);
105
106 2
            if (\in_array($annotationClass, [Ensure::class, Verify::class, Invariant::class], true)) {
107
                $allContracts[] = $annotation;
108
            }
109
        }
110
111 2
        return \array_unique($allContracts);
112
    }
113
114
    /**
115
     * @param MethodInvocation $invocation
116
     * @return array
117
     * @throws \ReflectionException
118
     */
119 2
    private function fetchParentsMethodContracts(MethodInvocation $invocation): array
120
    {
121 2
        return $this->methodConditionFetcher->getConditions(
122 2
            $invocation->getMethod()->getDeclaringClass(),
123 2
            $invocation->getMethod()->name
124
        );
125
    }
126
127
    /**
128
     * @param ReflectionClass $class
129
     * @return array
130
     */
131
    private function fetchClassContracts(ReflectionClass $class): array
132
    {
133
        $allContracts = $this->invariantFetcher->getConditions($class);
134
        foreach ($this->reader->getClassAnnotations($class) as $annotation) {
135
            if ($annotation instanceof Invariant) {
136
                $allContracts[] = $annotation;
137
            }
138
        }
139
140
        return \array_unique($allContracts);
141
    }
142
}
143