Completed
Push — master ( e5138f...a70f05 )
by Ryuichi
02:49 queued 53s
created

ClassLoader::getNamespaces()   D

Complexity

Conditions 9
Paths 2

Size

Total Lines 29
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 19
CRAP Score 9

Importance

Changes 0
Metric Value
dl 0
loc 29
ccs 19
cts 19
cp 1
rs 4.909
c 0
b 0
f 0
cc 9
eloc 19
nc 2
nop 1
crap 9
1
<?php
2
namespace WebStream\ClassLoader;
3
4
use WebStream\DI\Injector;
5
use WebStream\IO\File;
6
use WebStream\IO\FileInputStream;
7
8
/**
9
 * クラスローダ
10
 * @author Ryuichi TANAKA.
11
 * @since 2013/09/02
12
 * @version 0.7
13
 */
14
class ClassLoader
15
{
16
    use Injector;
17
18
    /**
19
     * @var Psr\Log\LoggerInterface
20
     */
21
    private $logger;
22
23
    /**
24
     * @var string アプリケーションルートパス
25
     */
26
    private $applicationRoot;
27
28
    /**
29
     * constructor
30
     * @param string アプリケーションルートパス
31
     */
32
    public function __construct(string $applicationRoot)
33
    {
34
        $this->logger = new class() { function __call($name, $args) {} };
0 ignored issues
show
Documentation Bug introduced by
It seems like new class { function...e, $args) { } } of type object<WebStream\ClassLo...ous//ClassLoader.php$0> is incompatible with the declared type object<WebStream\ClassLo...sr\Log\LoggerInterface> of property $logger.

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...
35 16
        $this->applicationRoot = $applicationRoot;
36 16
    }
37
38
    /**
39
     * クラスをロードする
40
     * @param mixed クラスまたはクラスリスト
41
     * @return array<string> ロード済みクラスリスト
42
     */
43 7
    public function load($target): array
44
    {
45 7
        return is_array($target) ? $this->loadClassList($target) : $this->loadClass($target);
46
    }
47
48
    /**
49
     * ファイルをインポートする
50
     * @param string ファイルパス
51
     * @param callable フィルタリング無名関数 trueを返すとインポート
52
     * @return bool インポート結果
53
     */
54 3
    public function import($filepath, callable $filter = null): bool
55
    {
56 3
        $file = new File($this->applicationRoot . "/" . $filepath);
57 3
        if ($file->isFile()) {
58 2
            if ($file->getFileExtension() === 'php') {
59 2
                if ($filter === null || (is_callable($filter) && $filter($file->getFilePath()) === true)) {
60 2
                    include_once $file->getFilePath();
61 2
                    $this->logger->debug($file->getFilePath() . " import success.");
62
                }
63
            }
64
65 2
            return true;
66
        }
67
68 1
        return false;
69
    }
70
71
    /**
72
     * 指定ディレクトリのファイルをインポートする
73
     * @param string ディレクトリパス
74
     * @param callable フィルタリング無名関数 trueを返すとインポート
75
     * @return bool インポート結果
76
     */
77 3
    public function importAll($dirPath, callable $filter = null): bool
78
    {
79 3
        $dir = new File($this->applicationRoot . "/" . $dirPath);
80 3
        $isSuccess = true;
81 3
        if ($dir->isDirectory()) {
82 3
            $iterator = $this->getFileSearchIterator($dir->getFilePath());
83 3
            foreach ($iterator as $filepath => $fileObject) {
84 3
                if (preg_match("/(?:\/\.|\/\.\.|\.DS_Store)$/", $filepath)) {
85 3
                    continue;
86
                }
87 3
                $file = new File($filepath);
88 3
                if ($file->isFile()) {
89 3
                    if ($file->getFileExtension() === 'php') {
90 3
                        if ($filter === null || (is_callable($filter) && $filter($file->getFilePath()) === true)) {
91 2
                            include_once $file->getFilePath();
92 3
                            $this->logger->debug($file->getFilePath() . " import success.");
93
                        }
94
                    }
95
                } else {
96
                    $this->logger->warn($filepath . " import failure.");
97 3
                    $isSuccess = false;
98
                }
99
            }
100
        }
101
102 3
        return $isSuccess;
103
    }
104
105
    /**
106
     * 名前空間リストを返却する
107
     * @param string ファイル名
108
     * @return array<string> 名前空間リスト
109
     */
110 3
    public function getNamespaces($fileName)
111
    {
112 3
        $dir = new File($this->applicationRoot);
113 3
        $namespaces = [];
114 3
        if ($dir->isDirectory()) {
115 3
            $iterator = $this->getFileSearchIterator($dir->getFilePath());
116 3
            foreach ($iterator as $filepath => $fileObject) {
117 3
                if (preg_match("/(?:\/\.|\/\.\.|\.DS_Store)$/", $filepath)) {
118 3
                    continue;
119
                }
120 3
                $file = new File($filepath);
121 3
                if ($file->isFile() && $file->getFileName() === $fileName) {
122 2
                    $fis = new FileInputStream($file);
123 2
                    while (($line = $fis->readLine()) !== null) {
124 2
                        if (preg_match("/^namespace\s(.*);$/", $line, $matches)) {
125 2
                            $namespace = $matches[1];
126 2
                            if (substr($namespace, 0) !== '\\') {
127 2
                                $namespace = '\\' . $namespace;
128
                            }
129 2
                            $namespaces[] = $namespace;
130
                        }
131
                    }
132 3
                    $fis->close();
133
                }
134
            }
135
        }
136
137 3
        return $namespaces;
138
    }
139
140
    /**
141
    * ロード可能なクラスを返却する
142
    * @param string クラス名(フルパス指定の場合はクラスパス)
143
    * @return array<string> ロード可能クラス
144
     */
145 7
    private function loadClass(string $className): array
146
    {
147 7
        $rootDir = $this->applicationRoot;
148 7
        $logger = $this->logger;
149
150
        // 名前空間セパレータをパスセパレータに置換
151 7
        if (DIRECTORY_SEPARATOR === '/') {
152 7
            $className = str_replace("\\", DIRECTORY_SEPARATOR, $className);
153
        }
154
155 7
        $search = function ($dirPath, $searchFilePath) use ($logger) {
156 7
            $includeList = [];
157 7
            $dir = new File($dirPath);
158 7
            if (!$dir->isDirectory()) {
159 1
                $logger->error("Invalid search directory path: " . $dir->getFilePath());
160 1
                return $includeList;
161
            }
162 6
            $iterator = $this->getFileSearchIterator($dir->getFilePath());
163 6
            foreach ($iterator as $filepath => $fileObject) {
164 6
                if (!$fileObject->isFile()) {
165 6
                    continue;
166
                }
167 6
                if (strpos($filepath, $searchFilePath) !== false) {
168 5
                    $file = new File($filepath);
169 5
                    $absoluteFilePath = $file->getAbsoluteFilePath();
170 5
                    include_once $absoluteFilePath;
171 5
                    $includeList[] = $absoluteFilePath;
172 6
                    $logger->debug($absoluteFilePath . " load success. (search from " . $dir->getFilePath());
173
                }
174
            }
175
176 6
            return $includeList;
177 7
        };
178
179 7
        return $search("${rootDir}", DIRECTORY_SEPARATOR . "${className}.php");
180
    }
181
182
    /**
183
     * ロード可能なクラスを複数返却する
184
     * @param array クラス名
185
     * @return array<string> ロード済みクラスリスト
186
     */
187
    private function loadClassList(array $classList): array
188
    {
189
        $includedlist = [];
190
        foreach ($classList as $className) {
191
            $result = $this->loadClass($className);
192
            if (is_array($result)) {
193
                $includedlist = array_merge($includedlist, $result);
194
            }
195
        }
196
197
        return $includedlist;
198
    }
199
200
    /**
201
     * ファイル検索イテレータを返却する
202
     * @param string ディレクトリパス
203
     * @return RecursiveIteratorIterator イテレータ
204
     */
205
    private function getFileSearchIterator(string $path): \RecursiveIteratorIterator
206
    {
207 12
        $iterator = [];
208 12
        $file = new File($path);
209 12
        if ($file->isDirectory()) {
210 12
            $iterator = new \RecursiveIteratorIterator(
211 12
                new \RecursiveDirectoryIterator($path),
212 12
                \RecursiveIteratorIterator::LEAVES_ONLY,
213 12
                \RecursiveIteratorIterator::CATCH_GET_CHILD // for Permission deny
214
            );
215
        }
216 12
        return $iterator;
217
    }
218
}
219