ParameterTypeContravarianceChanged   A
last analyzed

Complexity

Total Complexity 8

Size/Duplication

Total Lines 56
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 26
dl 0
loc 56
rs 10
c 0
b 0
f 0
wmc 8

4 Methods

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