Passed
Pull Request — master (#50)
by Marco
02:36
created

ParameterTypeContravarianceChanged   A

Complexity

Total Complexity 8

Size/Duplication

Total Lines 59
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
dl 0
loc 59
rs 10
c 0
b 0
f 0
wmc 8

4 Methods

Rating   Name   Duplication   Size   Complexity  
A __invoke() 0 14 2
A typeToString() 0 8 3
A compareParameter() 0 18 2
A __construct() 0 4 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Roave\BackwardCompatibility\DetectChanges\BCBreak\FunctionBased;
6
7
use Roave\BackwardCompatibility\Change;
8
use Roave\BackwardCompatibility\Changes;
9
use Roave\BackwardCompatibility\DetectChanges\Variance\TypeIsContravariant;
10
use Roave\BackwardCompatibility\Formatter\ReflectionFunctionAbstractName;
11
use Roave\BetterReflection\Reflection\ReflectionFunctionAbstract;
12
use Roave\BetterReflection\Reflection\ReflectionParameter;
13
use Roave\BetterReflection\Reflection\ReflectionType;
14
use function array_intersect_key;
15
use function array_values;
16
use function sprintf;
17
18
/**
19
 * When a parameter type changes, the new type should be wider than the previous type, or else
20
 * the callers will be passing invalid data to the function.
21
 */
22
final class ParameterTypeContravarianceChanged implements FunctionBased
23
{
24
    /** @var TypeIsContravariant */
25
    private $typeIsContravariant;
26
27
    /** @var ReflectionFunctionAbstractName */
28
    private $formatFunction;
29
30
    public function __construct(TypeIsContravariant $typeIsContravariant)
31
    {
32
        $this->typeIsContravariant = $typeIsContravariant;
33
        $this->formatFunction      = new ReflectionFunctionAbstractName();
34
    }
35
36
    public function __invoke(ReflectionFunctionAbstract $fromFunction, ReflectionFunctionAbstract $toFunction) : Changes
37
    {
38
        /** @var ReflectionParameter[] $fromParameters */
39
        $fromParameters = array_values($fromFunction->getParameters());
40
        /** @var ReflectionParameter[] $toParameters */
41
        $toParameters = array_values($toFunction->getParameters());
42
43
        $changes = Changes::empty();
44
45
        foreach (array_intersect_key($fromParameters, $toParameters) as $parameterIndex => $commonParameter) {
46
            $changes = $changes->mergeWith($this->compareParameter($commonParameter, $toParameters[$parameterIndex]));
47
        }
48
49
        return $changes;
50
    }
51
52
    private function compareParameter(ReflectionParameter $fromParameter, ReflectionParameter $toParameter) : Changes
53
    {
54
        $fromType = $fromParameter->getType();
55
        $toType   = $toParameter->getType();
56
57
        if ($this->typeIsContravariant->__invoke($fromType, $toType)) {
58
            return Changes::empty();
59
        }
60
61
        return Changes::fromList(Change::changed(
62
            sprintf(
63
                'The parameter $%s of %s changed from %s to a non-contravariant %s',
64
                $fromParameter->getName(),
65
                $this->formatFunction->__invoke($fromParameter->getDeclaringFunction()),
66
                $this->typeToString($fromType),
67
                $this->typeToString($toType)
68
            ),
69
            true
70
        ));
71
    }
72
73
    private function typeToString(?ReflectionType $type) : string
74
    {
75
        if (! $type) {
76
            return 'no type';
77
        }
78
79
        return ($type->allowsNull() ? '?' : '')
80
            . $type->__toString();
81
    }
82
}
83