CompareClasses::__invoke()   A
last analyzed

Complexity

Conditions 2
Paths 1

Size

Total Lines 21
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 11
c 0
b 0
f 0
nc 1
nop 3
dl 0
loc 21
rs 9.9
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Roave\BackwardCompatibility;
6
7
use Generator;
8
use Roave\BackwardCompatibility\DetectChanges\BCBreak\ClassBased\ClassBased;
9
use Roave\BackwardCompatibility\DetectChanges\BCBreak\InterfaceBased\InterfaceBased;
10
use Roave\BackwardCompatibility\DetectChanges\BCBreak\TraitBased\TraitBased;
11
use Roave\BetterReflection\Reflection\ReflectionClass;
12
use Roave\BetterReflection\Reflector\ClassReflector;
13
use Roave\BetterReflection\Reflector\Exception\IdentifierNotFound;
14
use function array_filter;
15
use function array_map;
16
use function Safe\preg_match;
17
use function Safe\sprintf;
18
19
final class CompareClasses implements CompareApi
20
{
21
    /** @var ClassBased */
22
    private $classBasedComparisons;
23
24
    /** @var InterfaceBased */
25
    private $interfaceBasedComparisons;
26
27
    /** @var TraitBased */
28
    private $traitBasedComparisons;
29
30
    public function __construct(
31
        ClassBased $classBasedComparisons,
32
        InterfaceBased $interfaceBasedComparisons,
33
        TraitBased $traitBasedComparisons
34
    ) {
35
        $this->classBasedComparisons     = $classBasedComparisons;
36
        $this->interfaceBasedComparisons = $interfaceBasedComparisons;
37
        $this->traitBasedComparisons     = $traitBasedComparisons;
38
    }
39
40
    public function __invoke(
41
        ClassReflector $definedSymbols,
42
        ClassReflector $pastSourcesWithDependencies,
43
        ClassReflector $newSourcesWithDependencies
44
    ) : Changes {
45
        $definedApiClassNames = array_map(
46
            static function (ReflectionClass $class) : string {
47
                return $class->getName();
48
            },
49
            array_filter(
50
                $definedSymbols->getAllClasses(),
51
                function (ReflectionClass $class) : bool {
52
                    return ! ($class->isAnonymous() || $this->isInternalDocComment($class->getDocComment()));
53
                }
54
            )
55
        );
56
57
        return Changes::fromIterator($this->makeSymbolsIterator(
58
            $definedApiClassNames,
59
            $pastSourcesWithDependencies,
60
            $newSourcesWithDependencies
61
        ));
62
    }
63
64
    /**
65
     * @param string[] $definedApiClassNames
66
     *
67
     * @return iterable|Change[]
68
     */
69
    private function makeSymbolsIterator(
70
        array $definedApiClassNames,
71
        ClassReflector $pastSourcesWithDependencies,
72
        ClassReflector $newSourcesWithDependencies
73
    ) : iterable {
74
        foreach ($definedApiClassNames as $apiClassName) {
75
            $oldSymbol = $pastSourcesWithDependencies->reflect($apiClassName);
76
77
            yield from $this->examineSymbol($oldSymbol, $newSourcesWithDependencies);
0 ignored issues
show
Bug Best Practice introduced by
The expression YieldFromNode returns the type Generator which is incompatible with the documented return type Roave\BackwardCompatibility\Change[]|iterable.
Loading history...
78
        }
79
    }
80
81
    private function examineSymbol(
82
        ReflectionClass $oldSymbol,
83
        ClassReflector $newSourcesWithDependencies
84
    ) : Generator {
85
        try {
86
            $newClass = $newSourcesWithDependencies->reflect($oldSymbol->getName());
87
        } catch (IdentifierNotFound $exception) {
88
            yield Change::removed(sprintf('Class %s has been deleted', $oldSymbol->getName()), true);
89
90
            return;
91
        }
92
93
        if ($oldSymbol->isInterface()) {
94
            yield from $this->interfaceBasedComparisons->__invoke($oldSymbol, $newClass);
95
96
            return;
97
        }
98
99
        if ($oldSymbol->isTrait()) {
100
            yield from $this->traitBasedComparisons->__invoke($oldSymbol, $newClass);
101
102
            return;
103
        }
104
105
        yield from $this->classBasedComparisons->__invoke($oldSymbol, $newClass);
106
    }
107
108
    private function isInternalDocComment(string $comment) : bool
109
    {
110
        return preg_match('/\s+@internal\s+/', $comment) === 1;
111
    }
112
}
113