Passed
Push — v7 ( f08608...4c8ab7 )
by Georges
01:47
created

ClassNamespaceResolverTrait::getClassNamespace()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 3
nc 2
nop 0
dl 0
loc 7
rs 9.4285
c 0
b 0
f 0
1
<?php
2
/**
3
 *
4
 * This file is part of phpFastCache.
5
 *
6
 * @license MIT License (MIT)
7
 *
8
 * For full copyright and license information, please see the docs/CREDITS.txt file.
9
 *
10
 * @author Khoa Bui (khoaofgod)  <[email protected]> http://www.phpfastcache.com
11
 * @author Georges.L (Geolim4)  <[email protected]>
12
 *
13
 */
14
declare(strict_types=1);
15
16
namespace Phpfastcache\Util;
17
18
/**
19
 * Trait ClassNamespaceResolverTrait
20
 * @package phpFastCache\Core
21
 */
22
trait ClassNamespaceResolverTrait
23
{
24
    /**
25
     * @var string
26
     */
27
    protected $namespace;
28
29
    /**
30
     * @return string
31
     */
32
    protected function getClassNamespace(): string
33
    {
34
        if (!$this->namespace) {
35
            $this->namespace = \substr(static::class, 0, strrpos(static::class, '\\'));
36
        }
37
38
        return $this->namespace;
39
    }
40
41
    /**
42
     * Iterate over all files in the given directory searching for classes.
43
     *
44
     * NOTICE: This method has been borrowed from Symfony ClassLoader 3.4 since they
45
     * deprecated the whole component as of SF4. Our thanks to them.
46
     *
47
     * @param \Iterator|string|array $dir The directory to search in or an iterator
48
     *
49
     * @return array A class map array
50
     */
51
    protected static function createClassMap($dir): array
52
    {
53
        if (\is_string($dir)) {
54
            $dir = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($dir));
55
        }
56
        $map = [];
57
58
        if(\is_array($dir) || $dir instanceof \Traversable){
0 ignored issues
show
introduced by
$dir is always a sub-type of Traversable.
Loading history...
59
            foreach ($dir as $file) {
60
                if (!$file->isFile()) {
61
                    continue;
62
                }
63
                $path = $file->getRealPath() ?: $file->getPathname();
64
                if ('php' !== pathinfo($path, PATHINFO_EXTENSION)) {
65
                    continue;
66
                }
67
                $classes = self::findClasses($path);
68
                if (\PHP_VERSION_ID >= 70000) {
69
                    // PHP 7 memory manager will not release after token_get_all(), see https://bugs.php.net/70098
70
                    gc_mem_caches();
71
                }
72
                foreach ($classes as $class) {
73
                    $map[ $class ] = $path;
74
                }
75
            }
76
        }
77
78
        return $map;
79
    }
80
81
    /**
82
     * Extract the classes in the given file.
83
     *
84
     * NOTICE: This method has been borrowed from Symfony ClassLoader 3.4 since they
85
     * deprecated the whole component as of SF4. Our thanks to them.
86
     *
87
     * @param string $path The file to check
88
     *
89
     * @return array The found classes
90
     */
91
    protected static function findClasses(string $path): array
92
    {
93
        $contents = file_get_contents($path);
94
        $tokens = token_get_all($contents);
95
        $classes = [];
96
        $namespace = '';
97
        for ($i = 0; isset($tokens[ $i ]); ++$i) {
98
            $token = $tokens[ $i ];
99
            if (!isset($token[ 1 ])) {
100
                continue;
101
            }
102
            $class = '';
103
            switch ($token[ 0 ]) {
104
                case T_NAMESPACE:
105
                    $namespace = '';
106
                    // If there is a namespace, extract it
107
                    while (isset($tokens[ ++$i ][ 1 ])) {
108
                        if (in_array($tokens[ $i ][ 0 ], [T_STRING, T_NS_SEPARATOR])) {
109
                            $namespace .= $tokens[ $i ][ 1 ];
110
                        }
111
                    }
112
                    $namespace .= '\\';
113
                    break;
114
                case T_CLASS:
115
                case T_INTERFACE:
116
                case T_TRAIT:
117
                    // Skip usage of ::class constant
118
                    $isClassConstant = false;
119
                    for ($j = $i - 1; $j > 0; --$j) {
120
                        if (!isset($tokens[ $j ][ 1 ])) {
121
                            break;
122
                        }
123
                        if (T_DOUBLE_COLON === $tokens[ $j ][ 0 ]) {
124
                            $isClassConstant = true;
125
                            break;
126
                        } elseif (!\in_array($tokens[ $j ][ 0 ], [T_WHITESPACE, T_DOC_COMMENT, T_COMMENT], false)) {
127
                            break;
128
                        }
129
                    }
130
                    if ($isClassConstant) {
131
                        break;
132
                    }
133
                    // Find the classname
134
                    while (isset($tokens[ ++$i ][ 1 ])) {
135
                        $t = $tokens[ $i ];
136
                        if (T_STRING === $t[ 0 ]) {
137
                            $class .= $t[ 1 ];
138
                        } elseif ('' !== $class && T_WHITESPACE === $t[ 0 ]) {
139
                            break;
140
                        }
141
                    }
142
                    $classes[] = ltrim($namespace . $class, '\\');
143
                    break;
144
                default:
145
                    break;
146
            }
147
        }
148
        return $classes;
149
    }
150
}