Passed
Pull Request — master (#19)
by Dominik
03:39
created

ConstraintViolationDetector::exceptAllowed()   A

Complexity

Conditions 5
Paths 5

Size

Total Lines 19
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 5.025

Importance

Changes 0
Metric Value
eloc 9
c 0
b 0
f 0
dl 0
loc 19
ccs 9
cts 10
cp 0.9
rs 9.6111
cc 5
nc 5
nop 1
crap 5.025
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Dominikb\ComposerLicenseChecker;
6
7
use Dominikb\ComposerLicenseChecker\Contracts\LicenseConstraintHandler;
8
use Dominikb\ComposerLicenseChecker\Exceptions\LogicException;
9
10
class ConstraintViolationDetector implements LicenseConstraintHandler
11
{
12
    /** @var string[] */
13
    protected $blocklist = [];
14
15
    /** @var string[] */
16
    protected $allowlist = [];
17
18
    /** @var Dependency[] */
19
    private $alwaysAllowed = [];
20
21 5
    public function setBlocklist(array $licenses): void
22
    {
23 5
        $this->blocklist = $licenses;
24 5
    }
25
26 4
    public function setAllowlist(array $licenses): void
27
    {
28 4
        $this->allowlist = $licenses;
29 4
    }
30
31 2
    public function allow($dependencies): void
32
    {
33 2
        if (! is_array($dependencies)) {
34 2
            $dependencies = [$dependencies];
35
        }
36
37 2
        foreach ($dependencies as $allowed) {
38 2
            $this->alwaysAllowed[] = $allowed;
39
        }
40 2
    }
41
42
    /**
43
     * @param Dependency[] $dependencies
44
     *
45
     * @return ConstraintViolation[]
46
     * @throws LogicException
47
     */
48 7
    public function detectViolations(array $dependencies): array
49
    {
50 7
        $this->ensureConfigurationIsValid();
51
52 6
        $possibleViolators = $this->exceptAllowed($dependencies);
53
54
        return [
55 6
            $this->detectBlocklistViolation($possibleViolators),
56 6
            $this->detectAllowlistViolation($possibleViolators),
57
        ];
58
    }
59
60
    /**
61
     * @throws LogicException
62
     */
63 7
    public function ensureConfigurationIsValid(): void
64
    {
65 7
        $overlap = array_intersect($this->blocklist, $this->allowlist);
66
67 7
        if (count($overlap) > 0) {
68 1
            $invalidLicenseConditionals = sprintf('"%s"', implode('", "', $overlap));
69 1
            throw new LogicException("Licenses must not be on the block- and allowlist at the same time: ${invalidLicenseConditionals}");
70
        }
71 6
    }
72
73
    /**
74
     * @param Dependency[] $dependencies
75
     */
76 6
    private function detectBlocklistViolation(array $dependencies): ConstraintViolation
77
    {
78 6
        $violation = new ConstraintViolation('Blocked license found!');
79
80 6
        if (! empty($this->blocklist)) {
81 4
            foreach ($dependencies as $dependency) {
82 2
                if ($this->allLicensesOnList($dependency->getLicenses(), $this->blocklist)) {
83 1
                    $violation->add($dependency);
84
                }
85
            }
86
        }
87
88 6
        return $violation;
89
    }
90
91
    /**
92
     * @param Dependency[] $dependencies
93
     */
94 6
    private function detectAllowlistViolation(array $dependencies): ConstraintViolation
95
    {
96 6
        $violation = new ConstraintViolation('Unallowed license found!');
97
98 6
        if (! empty($this->allowlist)) {
99 3
            foreach ($dependencies as $dependency) {
100 2
                if (! $this->anyLicenseOnList($dependency->getLicenses(), $this->allowlist)) {
101 1
                    $violation->add($dependency);
102
                }
103
            }
104
        }
105
106 6
        return $violation;
107
    }
108
109 2
    private function allLicensesOnList(array $licenses, array $list): bool
110
    {
111 2
        return count(array_intersect($licenses, $list)) === count($licenses);
112
    }
113
114 2
    private function anyLicenseOnList(array $licenses, array $list): bool
115
    {
116 2
        return count(array_intersect($licenses, $list)) > 0;
117
    }
118
119
    /**
120
     * @param Dependency[] $dependencies
121
     *
122
     * @return Dependency[]
123
     */
124 6
    private function exceptAllowed(array $dependencies): array
125
    {
126 6
        $possiblyViolating = [];
127
128 6
        if (empty($this->alwaysAllowed)) {
129 4
            return $dependencies;
130
        }
131
132 2
        foreach ($dependencies as $dependency) {
133 2
            foreach ($this->alwaysAllowed as $allowedDependency) {
134 2
                if ($this->matches($allowedDependency, $dependency)) {
135 2
                    continue 2; // Outer for: test next dependency
136
                }
137
            }
138
139
            $possiblyViolating[] = $dependency;
140
        }
141
142 2
        return $possiblyViolating;
143
    }
144
145
    /**
146
     * Determine if the $original author and package name match for $tryMatch.
147
     * An empty string for either the author or package gets interpreted as a wilcard.
148
     *
149
     * @param Dependency $original
150
     * @param Dependency $tryMatch
151
     *
152
     * @return bool
153
     */
154 2
    private function matches(Dependency $original, Dependency $tryMatch): bool
155
    {
156 2
        if ($original->getAuthorName()) {
157 2
            if (! ($original->getAuthorName() === $tryMatch->getAuthorName())) {
158
                return false;
159
            }
160
        }
161
162 2
        if ($original->getPackageName()) {
163 1
            if (! ($original->getPackageName() === $tryMatch->getPackageName())) {
164
                return false;
165
            }
166
        }
167
168 2
        return true;
169
    }
170
}
171