ClassLoader   A
last analyzed

Complexity

Total Complexity 38

Size/Duplication

Total Lines 199
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 82
dl 0
loc 199
rs 9.36
c 0
b 0
f 0
wmc 38

17 Methods

Rating   Name   Duplication   Size   Complexity  
__construct() 0 4 ?
A hp$0 ➔ load() 0 3 2
import() 0 15 ?
B hp$0 ➔ loadClass() 0 35 6
A hp$0 ➔ getFileSearchIterator() 0 12 2
A hp$0 ➔ import() 0 15 6
importAll() 0 26 ?
getNamespaces() 0 24 ?
B hp$0 ➔ getNamespaces() 0 24 8
B hp$0 ➔ importAll() 0 26 9
load() 0 3 ?
A hp$0 ➔ __call() 0 1 1
loadClassList() 0 11 ?
A hp$0 ➔ loadClassList() 0 11 3
A hp$0 ➔ __construct() 0 4 1
getFileSearchIterator() 0 12 ?
loadClass() 0 35 ?
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
0 ignored issues
show
Bug introduced by
The type WebStream\ClassLoader\Psr\Log\LoggerInterface was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

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