ClassNamespaceResolverTrait::getClassName()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
c 0
b 0
f 0
nc 1
nop 0
dl 0
loc 3
rs 10
1
<?php
2
3
/**
4
 *
5
 * This file is part of Phpfastcache.
6
 *
7
 * @license MIT License (MIT)
8
 *
9
 * For full copyright and license information, please see the docs/CREDITS.txt and LICENCE files.
10
 *
11
 * @author Georges.L (Geolim4)  <[email protected]>
12
 * @author Contributors  https://github.com/PHPSocialNetwork/phpfastcache/graphs/contributors
13
 */
14
15
declare(strict_types=1);
16
17
namespace Phpfastcache\Util;
18
19
use RecursiveDirectoryIterator;
20
use RecursiveIteratorIterator;
21
22
trait ClassNamespaceResolverTrait
23
{
24
    /**
25
     * @var array<string, string>
26
     */
27
    protected static array $namespaces = [];
28
29
    /**
30
     * Iterate over all files in the given directory searching for classes.
31
     *
32
     * NOTICE: This method has been borrowed from Symfony ClassLoader 3.4 since they
33
     * deprecated the whole component as of SF4. Our thanks to them.
34
     *
35
     * @param string $dir The directory to search in or an iterator
36
     *
37
     * @return array<string, string> A class map array
38
     */
39
    protected static function createClassMap(string $dir): array
40
    {
41
        $dirIterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($dir));
42
43
        $map = [];
44
45
        foreach ($dirIterator as $file) {
46
            if (!$file->isFile()) {
47
                continue;
48
            }
49
            $path = $file->getRealPath() ?: $file->getPathname();
50
            if ('php' !== pathinfo($path, PATHINFO_EXTENSION)) {
51
                continue;
52
            }
53
            $classes = self::findClasses($path);
54
            gc_mem_caches();
55
            foreach ($classes as $class) {
56
                $map[$class] = $path;
57
            }
58
        }
59
60
        return $map;
61
    }
62
63
    /**
64
     * Extract the classes in the given file.
65
     *
66
     * NOTICE: This method has been borrowed from Symfony ClassLoader 3.4 since they
67
     * deprecated the whole component as of SF4. Our thanks to them.
68
     *
69
     * @param string $path The file to check
70
     *
71
     * @return string[] The found classes
72
     *
73
     * @SuppressWarnings(PHPMD.CyclomaticComplexity)
74
     */
75
    protected static function findClasses(string $path): array
76
    {
77
        $contents = file_get_contents($path);
78
        $tokens = token_get_all($contents);
79
        $classes = [];
80
        $namespace = '';
81
        for ($i = 0; isset($tokens[$i]); ++$i) {
82
            $token = $tokens[$i];
83
            if (!isset($token[1])) {
84
                continue;
85
            }
86
            $class = '';
87
            switch ($token[0]) {
88
                case T_NAMESPACE:
89
                    $namespace = self::buildTokenNamespace($i, $tokens);
90
                    break;
91
                case T_CLASS:
92
                case T_INTERFACE:
93
                case T_TRAIT:
94
                    $classes = self::buildTokenClasses($namespace, $class, $classes, $i, $tokens);
95
                    break;
96
                default:
97
                    break;
98
            }
99
        }
100
101
        return $classes;
102
    }
103
104
    /**
105
     * @param string $namespace
106
     * @param string $class
107
     * @param string[] $classes
108
     * @param int $index
109
     * @param array<array<mixed>|string> $tokens
110
     * @return string[]
111
     */
112
    protected static function buildTokenClasses(string $namespace, string $class, array $classes, int $index, array $tokens): array
113
    {
114
        // Skip usage of ::class constant
115
        $isClassConstant = false;
116
        for ($j = $index - 1; $j > 0; --$j) {
117
            if (!isset($tokens[$j][1])) {
118
                break;
119
            }
120
            if (T_DOUBLE_COLON === $tokens[$j][0]) {
121
                $isClassConstant = true;
122
                break;
123
            }
124
125
            if (!\in_array($tokens[$j][0], [T_WHITESPACE, T_DOC_COMMENT, T_COMMENT], false)) {
126
                break;
127
            }
128
        }
129
        if ($isClassConstant) {
130
            return $classes;
131
        }
132
133
        // Find the classname
134
        while (isset($tokens[++$index][1])) {
135
            $t = $tokens[$index];
136
            if (T_STRING === $t[0]) {
137
                $class .= $t[1];
138
            } elseif ('' !== $class && T_WHITESPACE === $t[0]) {
139
                break;
140
            }
141
        }
142
143
        return \array_merge($classes, [\ltrim($namespace . $class, '\\')]);
144
    }
145
146
    /**
147
     * @param int $index
148
     * @param array<array<mixed>|string> $tokens
149
     * @return string
150
     */
151
    protected static function buildTokenNamespace(int $index, array $tokens): string
152
    {
153
        $namespace = '';
154
155
        // If there is a namespace, extract it (PHP 8 test)
156
        if (\defined('T_NAME_QUALIFIED')) {
157
            while (isset($tokens[++$index][1])) {
158
                if ($tokens[$index][0] === T_NAME_QUALIFIED) {
159
                    $namespace = $tokens[$index][1];
160
                    break;
161
                }
162
            }
163
        } else {
164
            while (isset($tokens[++$index][1])) {
165
                if (\in_array($tokens[$index][0], [T_STRING, T_NS_SEPARATOR], true)) {
166
                    $namespace .= $tokens[$index][1];
167
                }
168
            }
169
        }
170
171
        return $namespace . '\\';
172
    }
173
174
    /**
175
     * @return string
176
     */
177
    public static function getClassNamespace(): string
178
    {
179
        if (!isset(self::$namespaces[static::class])) {
180
            self::$namespaces[static::class] = substr(static::class, 0, strrpos(static::class, '\\'));
181
        }
182
183
        return self::$namespaces[static::class];
184
    }
185
186
    /**
187
     * @return string
188
     */
189
    public function getClassName(): string
190
    {
191
        return static::class;
192
    }
193
}
194