Completed
Push — master ( 852076...2b713c )
by Alex
23s queued 14s
created

Asserts::assertSignatureMatches()   C

Complexity

Conditions 12
Paths 91

Size

Total Lines 78
Code Lines 56

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 1 Features 1
Metric Value
cc 12
eloc 56
c 2
b 1
f 1
nc 91
nop 3
dl 0
loc 78
rs 6.5333

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
declare(strict_types=1);
4
5
6
namespace AlgoWeb\ODataMetadata;
7
8
use AlgoWeb\ODataMetadata\Exception\ArgumentException;
9
use ReflectionException;
10
use ReflectionFunction;
11
use ReflectionFunctionAbstract;
12
use ReflectionMethod;
13
use ReflectionNamedType;
14
15
abstract class Asserts
16
{
17
    private static function performAsserts(): bool
18
    {
19
        return assert_options(ASSERT_ACTIVE) === 1;
20
    }
21
22
    public static function assertSignatureMatches(callable $expected, callable $actual, string $callablePropName): bool
23
    {
24
        if (!self::performAsserts()) {
25
            return true;
26
        }
27
        $expectedReflection = self::getReflection($expected);
28
        $actualReflection   = self::getReflection($actual);
29
        $messageBuilder     = function (string $messagePrefix = '') use ($callablePropName, $expectedReflection, $actualReflection): string {
30
            return sprintf(
31
                '%s%s should be a callable with signature %s, however callable with signature %s provided',
32
                empty($messagePrefix) ? $messagePrefix : trim($messagePrefix) . ' ',
33
                $callablePropName,
34
                self::getSignatureFromReflection($expectedReflection),
35
                self::getSignatureFromReflection($actualReflection)
36
            );
37
        };
38
        assert(
39
            $expectedReflection->getNumberOfRequiredParameters() ===  $actualReflection->getNumberOfRequiredParameters(),
40
            $messageBuilder('Incorrect Parameter Count')
41
        );
42
        if ($expectedReflection->hasReturnType()) {
43
            assert(
44
                $actualReflection->hasReturnType(),
45
                $messageBuilder('Missing Return Type')
46
            );
47
            //TODO: improve this to check that the actual type does not return a childType;
48
            $expectedReturnType = $expectedReflection->getReturnType();
49
            $actualReturnType   = $actualReflection->getReturnType();
50
            $name               = $expectedReturnType instanceof ReflectionNamedType ?
51
                $expectedReturnType->getName() :
52
                strval($expectedReturnType);
53
            $actName = $actualReturnType instanceof ReflectionNamedType ?
54
                $actualReturnType->getName() :
55
                strval($actualReturnType);
56
57
            assert(
58
                $name === $actName,
59
                $messageBuilder('IncorrectOrInvalid ReturnType')
60
            );
61
            if (!$expectedReturnType->allowsNull()) {
62
                assert(
63
                    !$actualReturnType->allowsNull(),
64
                    $messageBuilder('Nullable ReturnType Not allowed')
65
                );
66
            }
67
        }
68
69
        for ($i = 0; $i < $expectedReflection->getNumberOfParameters(); $i++) {
70
            $expectedParm = $expectedReflection->getParameters()[$i];
71
            if ($expectedParm->hasType()) {
72
                $actualParm = $actualReflection->getParameters()[$i];
73
                assert(
74
                    $actualParm->hasType(),
75
                    $messageBuilder(sprintf('Parameter %s Is missing TypeHint', $i))
76
                );
77
                $expectedParmType = $expectedParm->getType();
78
                $actualParmType   = $actualParm->getType();
79
                $name             = $expectedParmType instanceof ReflectionNamedType ?
80
                    $expectedParmType->getName() :
81
                    strval($expectedParmType);
82
                $actName = $actualParmType instanceof ReflectionNamedType ?
83
                    $actualParmType->getName() :
84
                    strval($actualParmType);
85
86
                //TODO: improve this to check that the actual type does not return a childType;
87
                assert(
88
                    $name === $actName,
89
                    $messageBuilder(sprintf('Parameter %s has Incorrect Type', $i))
90
                );
91
                if (!$expectedParm->allowsNull()) {
92
                    assert(
93
                        !$actualParm->allowsNull(),
94
                        $messageBuilder(sprintf('Parameter %s should disallow Nulls', $i))
95
                    );
96
                }
97
            }
98
        }
99
        return true;
100
    }
101
102
    private static function getSignatureFromReflection(ReflectionFunctionAbstract $reflection): string
103
    {
104
        $parameters = [];
105
        foreach ($reflection->getParameters() as $parameter) {
106
            $parameterString = '';
107
            if ($parameter->hasType()) {
108
                $parmType        = $parameter->getType();
109
                $parmName        = $parmType instanceof ReflectionNamedType ?
110
                                    $parmType->getName() :
111
                                    strval($parmType);
112
                $parameterString .= $parmType->allowsNull() ? '?' : '';
113
                $parameterString .= $parmName . ' ';
114
            }
115
            $parameterString .= $parameter->isVariadic() ? '...$' : '$';
116
            $parameterString .= $parameter->getName();
117
            if ($parameter->isOptional()) {
118
                try {
119
                    $parameterString .= ' = ' . strval($parameter->getDefaultValue());
120
                } catch (ReflectionException $e) {
121
                    // Keep on trucking
122
                }
123
            }
124
            $parameters[] = $parameterString;
125
        }
126
        $return = '';
127
        if ($reflection->hasReturnType()) {
128
            $returnType = $reflection->getReturnType();
129
            $name       = $returnType instanceof ReflectionNamedType ?
130
                $returnType->getName() :
131
                strval($returnType);
132
            $return .= ': ' . $name;
133
        }
134
        return sprintf('function(%s)%s', implode(',', $parameters), $return);
135
    }
136
137
    private static function getReflection(callable $method): ReflectionFunctionAbstract
138
    {
139
        try {
140
            return is_array($method) ? new ReflectionMethod(...$method) : new ReflectionFunction($method);
141
        } catch (ReflectionException $e) {
142
            throw new ArgumentException($e->getMessage());
143
        }
144
    }
145
}
146