Passed
Pull Request — master (#22)
by Sergei
15:13
created

PathMatcher::matchExcept()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 13
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 6
nc 4
nop 1
dl 0
loc 13
rs 10
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Files\PathMatch;
6
7
use Yiisoft\Strings\StringHelper;
8
9
final class PathMatcher implements PathMatcherInterface
10
{
11
    /**
12
     * @var PathPattern[]|null
13
     */
14
    private ?array $only = null;
15
16
    /**
17
     * @var PathPattern[]|null
18
     */
19
    private ?array $except = null;
20
21
    /**
22
     * @var PathPattern[]|null
23
     */
24
    private ?array $callbacks = null;
25
26
    private bool $caseSensitive = false;
27
    private bool $matchFullPath = false;
28
    private bool $matchSlashesExactly = true;
29
30
    /**
31
     * Make string patterns case sensitive.
32
     * @return self
33
     */
34
    public function caseSensitive(): self
35
    {
36
        $new = clone $this;
37
        $new->caseSensitive = true;
38
        return $new;
39
    }
40
41
    /**
42
     * Match string patterns as full path, not just ending of path.
43
     * @return self
44
     */
45
    public function withFullPath(): self
46
    {
47
        $new = clone $this;
48
        $new->matchFullPath = true;
49
        return $new;
50
    }
51
52
    /**
53
     * Match `/` character with wildcards in string patterns.
54
     * @return self
55
     */
56
    public function withNotExactSlashes(): self
57
    {
58
        $new = clone $this;
59
        $new->matchSlashesExactly = false;
60
        return $new;
61
    }
62
63
    /**
64
     * Set list of patterns that the files or directories should match.
65
     * @param string|PathPattern ...$patterns
66
     * @return self
67
     */
68
    public function only(...$patterns): self
69
    {
70
        $new = clone $this;
71
        $new->only = $this->makePathPatterns($patterns);
72
        return $new;
73
    }
74
75
    /**
76
     * Set list of patterns that the files or directories should not match.
77
     * @param string|PathPattern ...$patterns
78
     * @return self
79
     */
80
    public function except(string ...$patterns): self
81
    {
82
        $new = clone $this;
83
        $new->except = $this->makePathPatterns($patterns);
84
        return $new;
85
    }
86
87
    /**
88
     * Set list of PHP callback that is called for each path.
89
     *
90
     * The signature of the callback should be: `function ($path)`, where `$path` refers the full path to be filtered.
91
     * The callback should return `true` if the passed path would match and `false` if it doesn't.
92
     *
93
     * @param callable ...$callbacks
94
     * @return self
95
     */
96
    public function callback(callable ...$callbacks): self
97
    {
98
        $new = clone $this;
99
        $new->callbacks = $callbacks;
0 ignored issues
show
Documentation Bug introduced by
It seems like $callbacks of type array<integer,callable> is incompatible with the declared type Yiisoft\Files\PathMatch\PathPattern[]|null of property $callbacks.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
100
        return $new;
101
    }
102
103
    /**
104
     * Checks if the passed path would match specified conditions.
105
     *
106
     * @param string $path The tested path.
107
     * @return bool|null Whether the path matches conditions or not.
108
     */
109
    public function match(string $path): ?bool
110
    {
111
        $path = str_replace('\\', '/', $path);
112
113
        if (!$this->matchOnly($path)) {
114
            return false;
115
        }
116
117
        if ($this->matchExcept($path)) {
118
            return false;
119
        }
120
121
        if ($this->callbacks !== null) {
122
            foreach ($this->callbacks as $callback) {
123
                if (!$callback($path)) {
124
                    return false;
125
                }
126
            }
127
        }
128
129
        return true;
130
    }
131
132
    /**
133
     * @param string $path
134
     * @return bool
135
     */
136
    private function matchOnly(string $path): bool
137
    {
138
        if ($this->only === null) {
139
            return true;
140
        }
141
142
        $hasFalse = false;
143
        $hasNull = false;
144
145
        foreach ($this->only as $pattern) {
146
            if ($pattern->match($path) === true) {
147
                return true;
148
            }
149
            if ($pattern->match($path) === false) {
150
                $hasFalse = true;
151
            }
152
            if ($pattern->match($path) === null) {
153
                $hasNull = true;
154
            }
155
        }
156
157
        if (is_file($path)) {
158
            return $hasFalse ? false : true;
159
        }
160
161
        return $hasNull ? true : false;
162
    }
163
164
    /**
165
     * @param string $path
166
     * @return bool
167
     */
168
    private function matchExcept(string $path): bool
169
    {
170
        if ($this->except === null) {
171
            return false;
172
        }
173
174
        foreach ($this->except as $pattern) {
175
            if ($pattern->match($path) === true) {
176
                return true;
177
            }
178
        }
179
180
        return false;
181
    }
182
183
    /**
184
     * @param string[]|PathPattern[] $patterns
185
     * @return PathPattern[]
186
     */
187
    private function makePathPatterns(array $patterns): array
188
    {
189
        $pathPatterns = [];
190
        foreach ($patterns as $pattern) {
191
            if ($pattern instanceof PathPattern) {
192
                $pathPatterns[] = $pattern;
193
                continue;
194
            }
195
196
            $isDirectory = StringHelper::endsWith($pattern, '/');
197
            if ($isDirectory) {
198
                $pattern = StringHelper::substring($pattern, 0, -1);
199
            }
200
201
            $pathPattern = new PathPattern($pattern);
202
203
            if ($this->caseSensitive) {
204
                $pathPattern = $pathPattern->caseSensitive();
205
            }
206
207
            if ($this->matchFullPath) {
208
                $pathPattern = $pathPattern->withFullPath();
209
            }
210
211
            if (!$this->matchSlashesExactly) {
212
                $pathPattern = $pathPattern->withNotExactSlashes();
213
            }
214
215
            $pathPattern = $isDirectory ? $pathPattern->onlyDirectories() : $pathPattern->onlyFiles();
216
            $pathPatterns[] = $pathPattern;
217
        }
218
        return $pathPatterns;
219
    }
220
}
221