Passed
Pull Request — master (#38)
by Marco
03:01
created

ParameterDefaultValueChanged::compare()   B

Complexity

Conditions 3
Paths 3

Size

Total Lines 32
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 32
rs 8.8571
c 0
b 0
f 0
cc 3
eloc 19
nc 3
nop 2
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Roave\ApiCompare\Comparator\BackwardsCompatibility\FunctionBased;
6
7
use Roave\ApiCompare\Change;
8
use Roave\ApiCompare\Changes;
9
use Roave\BetterReflection\Reflection\ReflectionFunctionAbstract;
10
use Roave\BetterReflection\Reflection\ReflectionMethod;
11
use Roave\BetterReflection\Reflection\ReflectionParameter;
12
use function assert;
13
14
/**
15
 * A default value for a parameter should not change, as that can lead to change in expected execution
16
 * behavior.
17
 */
18
final class ParameterDefaultValueChanged implements FunctionBased
19
{
20
    public function compare(ReflectionFunctionAbstract $fromFunction, ReflectionFunctionAbstract $toFunction) : Changes
21
    {
22
        $fromParametersWithDefaults = $this->defaultParameterValues($fromFunction);
23
        $toParametersWithDefaults   = $this->defaultParameterValues($toFunction);
24
25
        $changes = Changes::new();
26
27
        foreach (array_intersect_key($fromParametersWithDefaults, $toParametersWithDefaults) as $parameterIndex => $parameter) {
28
            assert($parameter instanceof ReflectionParameter);
29
30
            $defaultValueFrom = $parameter->getDefaultValue();
31
            $defaultValueTo   = $toParametersWithDefaults[$parameterIndex]->getDefaultValue();
32
33
            if ($defaultValueFrom === $defaultValueTo) {
34
                continue;
35
            }
36
37
            $changes = $changes->mergeWith(Changes::fromArray([
38
                Change::changed(
39
                    sprintf(
40
                        'Default parameter value for for parameter $%s of %s() changed from %s to %s',
41
                        $parameter->getName(),
42
                        $this->functionOrMethodName($fromFunction),
43
                        var_export($defaultValueFrom, true),
44
                        var_export($defaultValueTo, true)
45
                    ),
46
                    true
47
                ),
48
            ]));
49
        }
50
51
        return $changes;
52
    }
53
54
    /** @return ReflectionParameter[] indexed by parameter index */
55
    private function defaultParameterValues(ReflectionFunctionAbstract $function) : array
56
    {
57
        $optionalParameters = array_values(array_filter(
58
            $function->getParameters(),
59
            function (ReflectionParameter $parameter) : bool {
60
                return $parameter->isDefaultValueAvailable();
61
            }
62
        ));
63
64
        return array_combine(
65
            array_map(function (ReflectionParameter $parameter) : int {
66
                return $parameter->getPosition();
67
            }, $optionalParameters),
68
            $optionalParameters
69
        );
70
    }
71
72
    private function functionOrMethodName(ReflectionFunctionAbstract $function) : string
73
    {
74
        if ($function instanceof ReflectionMethod) {
75
            return $function->getDeclaringClass()->getName()
76
                . ($function->isStatic() ? '::' : '#')
77
                . $function->getName();
78
        }
79
80
        return $function->getName();
81
    }
82
}
83