Subversion-Projekte lars-tiefland.laravel_shop

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
148 lars 1
<?php declare(strict_types=1);
2
/*
3
 * This file is part of sebastian/code-unit.
4
 *
5
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
6
 *
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 */
10
namespace SebastianBergmann\CodeUnit;
11
 
12
use function array_keys;
13
use function array_merge;
14
use function array_unique;
15
use function array_values;
16
use function class_exists;
17
use function explode;
18
use function function_exists;
19
use function interface_exists;
20
use function ksort;
21
use function method_exists;
22
use function sort;
23
use function sprintf;
24
use function str_replace;
25
use function strpos;
26
use function trait_exists;
27
use ReflectionClass;
28
use ReflectionFunction;
29
use ReflectionMethod;
30
 
31
final class Mapper
32
{
33
    /**
34
     * @psalm-return array<string,list<int>>
35
     */
36
    public function codeUnitsToSourceLines(CodeUnitCollection $codeUnits): array
37
    {
38
        $result = [];
39
 
40
        foreach ($codeUnits as $codeUnit) {
41
            $sourceFileName = $codeUnit->sourceFileName();
42
 
43
            if (!isset($result[$sourceFileName])) {
44
                $result[$sourceFileName] = [];
45
            }
46
 
47
            $result[$sourceFileName] = array_merge($result[$sourceFileName], $codeUnit->sourceLines());
48
        }
49
 
50
        foreach (array_keys($result) as $sourceFileName) {
51
            $result[$sourceFileName] = array_values(array_unique($result[$sourceFileName]));
52
 
53
            sort($result[$sourceFileName]);
54
        }
55
 
56
        ksort($result);
57
 
58
        return $result;
59
    }
60
 
61
    /**
62
     * @throws InvalidCodeUnitException
63
     * @throws ReflectionException
64
     */
65
    public function stringToCodeUnits(string $unit): CodeUnitCollection
66
    {
67
        if (strpos($unit, '::') !== false) {
68
            [$firstPart, $secondPart] = explode('::', $unit);
69
 
70
            if (empty($firstPart) && $this->isUserDefinedFunction($secondPart)) {
71
                return CodeUnitCollection::fromList(CodeUnit::forFunction($secondPart));
72
            }
73
 
74
            if ($this->isUserDefinedClass($firstPart)) {
75
                if ($secondPart === '<public>') {
76
                    return $this->publicMethodsOfClass($firstPart);
77
                }
78
 
79
                if ($secondPart === '<!public>') {
80
                    return $this->protectedAndPrivateMethodsOfClass($firstPart);
81
                }
82
 
83
                if ($secondPart === '<protected>') {
84
                    return $this->protectedMethodsOfClass($firstPart);
85
                }
86
 
87
                if ($secondPart === '<!protected>') {
88
                    return $this->publicAndPrivateMethodsOfClass($firstPart);
89
                }
90
 
91
                if ($secondPart === '<private>') {
92
                    return $this->privateMethodsOfClass($firstPart);
93
                }
94
 
95
                if ($secondPart === '<!private>') {
96
                    return $this->publicAndProtectedMethodsOfClass($firstPart);
97
                }
98
 
99
                if ($this->isUserDefinedMethod($firstPart, $secondPart)) {
100
                    return CodeUnitCollection::fromList(CodeUnit::forClassMethod($firstPart, $secondPart));
101
                }
102
            }
103
 
104
            if ($this->isUserDefinedInterface($firstPart)) {
105
                return CodeUnitCollection::fromList(CodeUnit::forInterfaceMethod($firstPart, $secondPart));
106
            }
107
 
108
            if ($this->isUserDefinedTrait($firstPart)) {
109
                return CodeUnitCollection::fromList(CodeUnit::forTraitMethod($firstPart, $secondPart));
110
            }
111
        } else {
112
            if ($this->isUserDefinedClass($unit)) {
113
                $units = [CodeUnit::forClass($unit)];
114
 
115
                foreach ($this->reflectorForClass($unit)->getTraits() as $trait) {
116
                    if (!$trait->isUserDefined()) {
117
                        // @codeCoverageIgnoreStart
118
                        continue;
119
                        // @codeCoverageIgnoreEnd
120
                    }
121
 
122
                    $units[] = CodeUnit::forTrait($trait->getName());
123
                }
124
 
125
                return CodeUnitCollection::fromArray($units);
126
            }
127
 
128
            if ($this->isUserDefinedInterface($unit)) {
129
                return CodeUnitCollection::fromList(CodeUnit::forInterface($unit));
130
            }
131
 
132
            if ($this->isUserDefinedTrait($unit)) {
133
                return CodeUnitCollection::fromList(CodeUnit::forTrait($unit));
134
            }
135
 
136
            if ($this->isUserDefinedFunction($unit)) {
137
                return CodeUnitCollection::fromList(CodeUnit::forFunction($unit));
138
            }
139
 
140
            $unit = str_replace('<extended>', '', $unit);
141
 
142
            if ($this->isUserDefinedClass($unit)) {
143
                return $this->classAndParentClassesAndTraits($unit);
144
            }
145
        }
146
 
147
        throw new InvalidCodeUnitException(
148
            sprintf(
149
                '"%s" is not a valid code unit',
150
                $unit
151
            )
152
        );
153
    }
154
 
155
    /**
156
     * @psalm-param class-string $className
157
     *
158
     * @throws ReflectionException
159
     */
160
    private function publicMethodsOfClass(string $className): CodeUnitCollection
161
    {
162
        return $this->methodsOfClass($className, ReflectionMethod::IS_PUBLIC);
163
    }
164
 
165
    /**
166
     * @psalm-param class-string $className
167
     *
168
     * @throws ReflectionException
169
     */
170
    private function publicAndProtectedMethodsOfClass(string $className): CodeUnitCollection
171
    {
172
        return $this->methodsOfClass($className, ReflectionMethod::IS_PUBLIC | ReflectionMethod::IS_PROTECTED);
173
    }
174
 
175
    /**
176
     * @psalm-param class-string $className
177
     *
178
     * @throws ReflectionException
179
     */
180
    private function publicAndPrivateMethodsOfClass(string $className): CodeUnitCollection
181
    {
182
        return $this->methodsOfClass($className, ReflectionMethod::IS_PUBLIC | ReflectionMethod::IS_PRIVATE);
183
    }
184
 
185
    /**
186
     * @psalm-param class-string $className
187
     *
188
     * @throws ReflectionException
189
     */
190
    private function protectedMethodsOfClass(string $className): CodeUnitCollection
191
    {
192
        return $this->methodsOfClass($className, ReflectionMethod::IS_PROTECTED);
193
    }
194
 
195
    /**
196
     * @psalm-param class-string $className
197
     *
198
     * @throws ReflectionException
199
     */
200
    private function protectedAndPrivateMethodsOfClass(string $className): CodeUnitCollection
201
    {
202
        return $this->methodsOfClass($className, ReflectionMethod::IS_PROTECTED | ReflectionMethod::IS_PRIVATE);
203
    }
204
 
205
    /**
206
     * @psalm-param class-string $className
207
     *
208
     * @throws ReflectionException
209
     */
210
    private function privateMethodsOfClass(string $className): CodeUnitCollection
211
    {
212
        return $this->methodsOfClass($className, ReflectionMethod::IS_PRIVATE);
213
    }
214
 
215
    /**
216
     * @psalm-param class-string $className
217
     *
218
     * @throws ReflectionException
219
     */
220
    private function methodsOfClass(string $className, int $filter): CodeUnitCollection
221
    {
222
        $units = [];
223
 
224
        foreach ($this->reflectorForClass($className)->getMethods($filter) as $method) {
225
            if (!$method->isUserDefined()) {
226
                continue;
227
            }
228
 
229
            $units[] = CodeUnit::forClassMethod($className, $method->getName());
230
        }
231
 
232
        return CodeUnitCollection::fromArray($units);
233
    }
234
 
235
    /**
236
     * @psalm-param class-string $className
237
     *
238
     * @throws ReflectionException
239
     */
240
    private function classAndParentClassesAndTraits(string $className): CodeUnitCollection
241
    {
242
        $units = [CodeUnit::forClass($className)];
243
 
244
        $reflector = $this->reflectorForClass($className);
245
 
246
        foreach ($this->reflectorForClass($className)->getTraits() as $trait) {
247
            if (!$trait->isUserDefined()) {
248
                // @codeCoverageIgnoreStart
249
                continue;
250
                // @codeCoverageIgnoreEnd
251
            }
252
 
253
            $units[] = CodeUnit::forTrait($trait->getName());
254
        }
255
 
256
        while ($reflector = $reflector->getParentClass()) {
257
            if (!$reflector->isUserDefined()) {
258
                break;
259
            }
260
 
261
            $units[] = CodeUnit::forClass($reflector->getName());
262
 
263
            foreach ($reflector->getTraits() as $trait) {
264
                if (!$trait->isUserDefined()) {
265
                    // @codeCoverageIgnoreStart
266
                    continue;
267
                    // @codeCoverageIgnoreEnd
268
                }
269
 
270
                $units[] = CodeUnit::forTrait($trait->getName());
271
            }
272
        }
273
 
274
        return CodeUnitCollection::fromArray($units);
275
    }
276
 
277
    /**
278
     * @psalm-param class-string $className
279
     *
280
     * @throws ReflectionException
281
     */
282
    private function reflectorForClass(string $className): ReflectionClass
283
    {
284
        try {
285
            return new ReflectionClass($className);
286
            // @codeCoverageIgnoreStart
287
        } catch (\ReflectionException $e) {
288
            throw new ReflectionException(
289
                $e->getMessage(),
290
                (int) $e->getCode(),
291
                $e
292
            );
293
        }
294
        // @codeCoverageIgnoreEnd
295
    }
296
 
297
    /**
298
     * @throws ReflectionException
299
     */
300
    private function isUserDefinedFunction(string $functionName): bool
301
    {
302
        if (!function_exists($functionName)) {
303
            return false;
304
        }
305
 
306
        try {
307
            return (new ReflectionFunction($functionName))->isUserDefined();
308
            // @codeCoverageIgnoreStart
309
        } catch (\ReflectionException $e) {
310
            throw new ReflectionException(
311
                $e->getMessage(),
312
                (int) $e->getCode(),
313
                $e
314
            );
315
        }
316
        // @codeCoverageIgnoreEnd
317
    }
318
 
319
    /**
320
     * @throws ReflectionException
321
     */
322
    private function isUserDefinedClass(string $className): bool
323
    {
324
        if (!class_exists($className)) {
325
            return false;
326
        }
327
 
328
        try {
329
            return (new ReflectionClass($className))->isUserDefined();
330
            // @codeCoverageIgnoreStart
331
        } catch (\ReflectionException $e) {
332
            throw new ReflectionException(
333
                $e->getMessage(),
334
                (int) $e->getCode(),
335
                $e
336
            );
337
        }
338
        // @codeCoverageIgnoreEnd
339
    }
340
 
341
    /**
342
     * @throws ReflectionException
343
     */
344
    private function isUserDefinedInterface(string $interfaceName): bool
345
    {
346
        if (!interface_exists($interfaceName)) {
347
            return false;
348
        }
349
 
350
        try {
351
            return (new ReflectionClass($interfaceName))->isUserDefined();
352
            // @codeCoverageIgnoreStart
353
        } catch (\ReflectionException $e) {
354
            throw new ReflectionException(
355
                $e->getMessage(),
356
                (int) $e->getCode(),
357
                $e
358
            );
359
        }
360
        // @codeCoverageIgnoreEnd
361
    }
362
 
363
    /**
364
     * @throws ReflectionException
365
     */
366
    private function isUserDefinedTrait(string $traitName): bool
367
    {
368
        if (!trait_exists($traitName)) {
369
            return false;
370
        }
371
 
372
        try {
373
            return (new ReflectionClass($traitName))->isUserDefined();
374
            // @codeCoverageIgnoreStart
375
        } catch (\ReflectionException $e) {
376
            throw new ReflectionException(
377
                $e->getMessage(),
378
                (int) $e->getCode(),
379
                $e
380
            );
381
        }
382
        // @codeCoverageIgnoreEnd
383
    }
384
 
385
    /**
386
     * @throws ReflectionException
387
     */
388
    private function isUserDefinedMethod(string $className, string $methodName): bool
389
    {
390
        if (!class_exists($className)) {
391
            // @codeCoverageIgnoreStart
392
            return false;
393
            // @codeCoverageIgnoreEnd
394
        }
395
 
396
        if (!method_exists($className, $methodName)) {
397
            // @codeCoverageIgnoreStart
398
            return false;
399
            // @codeCoverageIgnoreEnd
400
        }
401
 
402
        try {
403
            return (new ReflectionMethod($className, $methodName))->isUserDefined();
404
            // @codeCoverageIgnoreStart
405
        } catch (\ReflectionException $e) {
406
            throw new ReflectionException(
407
                $e->getMessage(),
408
                (int) $e->getCode(),
409
                $e
410
            );
411
        }
412
        // @codeCoverageIgnoreEnd
413
    }
414
}