Completed
Pull Request — master (#15)
by Kirill
04:58
created

AbstractContractAspect::ensureContracts()   D

Complexity

Conditions 9
Paths 24

Size

Total Lines 42
Code Lines 25

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 17
CRAP Score 11.0104

Importance

Changes 3
Bugs 0 Features 0
Metric Value
c 3
b 0
f 0
dl 0
loc 42
ccs 17
cts 24
cp 0.7083
rs 4.909
cc 9
eloc 25
nc 24
nop 5
crap 11.0104
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 34
    protected function fetchMethodArguments(MethodInvocation $invocation)
41
    {
42 34
        $parameters = $invocation->getMethod()->getParameters();
43
        $argumentNames = array_map(function (\ReflectionParameter $parameter) {
44 33
            return $parameter->name;
45 34
        }, $parameters);
46
        
47 34
        $parameters = [];
48 34
        $argumentValues = $invocation->getArguments();
49
50 34
        foreach ($argumentNames as $i => $name) {
51 33
            $parameters[$name] = isset($argumentValues[$i]) 
52 31
                ? $argumentValues[$i]
53 33
                : null;
54
        }
55
56 34
        return $parameters;
57
    }
58
59
    /**
60
     * Performs verification of contracts for given invocation
61
     *
62
     * @param MethodInvocation $invocation Current invocation
63
     * @param array|Annotation[] $contracts Contract annotation
64
     * @param object|string $instance Invocation instance or string for static class
65
     * @param string $scope Scope of method
66
     * @param array $args List of arguments for the method
67
     *
68
     * @throws DomainException
69
     */
70 34
    protected function ensureContracts(MethodInvocation $invocation, array $contracts, $instance, $scope, array $args)
71
    {
72 34
        static $invoker = null;
73 34
        if (!$invoker) {
74 34
            $invoker = function () {
75 34
                extract(func_get_arg(0));
0 ignored issues
show
Bug introduced by
func_get_arg(0) cannot be passed to extract() as the parameter $var_array expects a reference.
Loading history...
76
77
                try {
78 34
                    return eval('return ' . $contractExpression . '; ?>');
79 34
                } catch (\Throwable $e) {
0 ignored issues
show
Unused Code introduced by
catch (\Throwable $e) { ...$contractExpression); } does not seem to be reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
Bug introduced by
The class Throwable does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
80 34
                    throw new \InvalidArgumentException('PHP Syntax error in ' . $contractExpression);
81
                }
82 3
            };
83
        }
84
85 34
        $instance     = is_object($instance) ? $instance : null;
86 34
        $boundInvoker = $invoker->bindTo($instance, $scope);
87
88 34
        foreach ($contracts as $contract) {
89 34
            $contractExpression = $contract->value;
90
            try {
91 34
                $invocationResult = $boundInvoker->__invoke($args, $contractExpression);
92
93
                // we accept as a result only true or null
94
                // null may be a result of assertions from beberlei/assert which passed
95
                if ($invocationResult !== null && $invocationResult !== true) {
96
                    $errorMessage = 'Invalid return value received from the assertion body,'
97
                        . ' only boolean or void can be returned';
98
                    throw new DomainException($errorMessage);
99
                }
100
0 ignored issues
show
Coding Style introduced by
Blank line found at end of control structure
Loading history...
101 34
            } catch (\Error $internalError) {
0 ignored issues
show
Bug introduced by
The class Error does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
102
                // PHP-7 friendly interceptor for fatal errors
103
                throw new ContractViolation($invocation, $contractExpression, new \Exception(
104
                    $internalError->getMessage(),
105
                    $internalError->getCode()
106
                ));
107 34
            } catch (\Exception $internalException) {
108 34
                throw new ContractViolation($invocation, $contractExpression, $internalException);
109
            }
110
        }
111
    }
112
}
113