Completed
Push — master ( 10c12e...b7bc0c )
by Ryuichi
02:43
created

ClassLoader::loadClass()   B

Complexity

Conditions 6
Paths 2

Size

Total Lines 36
Code Lines 23

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 24
CRAP Score 6

Importance

Changes 0
Metric Value
dl 0
loc 36
ccs 24
cts 24
cp 1
rs 8.439
c 0
b 0
f 0
cc 6
eloc 23
nc 2
nop 1
crap 6
1
<?php
2
namespace WebStream\ClassLoader;
3
4
use WebStream\DI\Injector;
5
use WebStream\IO\File;
6
7
/**
8
 * クラスローダ
9
 * @author Ryuichi TANAKA.
10
 * @since 2013/09/02
11
 * @version 0.7
12
 */
13
class ClassLoader
14
{
15
    use Injector;
16
17
    /**
18
     * @var Psr\Log\LoggerInterface
19
     */
20
    private $logger;
21
22
    /**
23
     * @var string アプリケーションルートパス
24
     */
25
    private $applicationRoot;
26
27
    /**
28
     * constructor
29
     * @param string アプリケーションルートパス
30
     */
31
    public function __construct(string $applicationRoot)
32
    {
33
        $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...
34 13
        $this->applicationRoot = $applicationRoot;
35 13
    }
36
37
    /**
38
     * クラスをロードする
39
     * @param mixed クラスまたはクラスリスト
40
     * @return array<string> ロード済みクラスリスト
41
     */
42 7
    public function load($target): array
43
    {
44 7
        return is_array($target) ? $this->loadClassList($target) : $this->loadClass($target);
45
    }
46
47
    /**
48
     * ファイルをインポートする
49
     * @param string ファイルパス
50
     * @param callable フィルタリング無名関数 trueを返すとインポート
51
     * @return bool インポート結果
52
     */
53 3
    public function import($filepath, callable $filter = null): bool
54
    {
55 3
        $file = new File($this->applicationRoot . "/" . $filepath);
56 3
        if ($file->isFile()) {
57 2
            if ($file->getFileExtension() === 'php') {
58 2
                if ($filter === null || (is_callable($filter) && $filter($file->getFilePath()) === true)) {
59 2
                    include_once $file->getFilePath();
60 2
                    $this->logger->debug($file->getFilePath() . " import success.");
61
                }
62
            }
63
64 2
            return true;
65
        }
66
67 1
        return false;
68
    }
69
70
    /**
71
     * 指定ディレクトリのファイルをインポートする
72
     * @param string ディレクトリパス
73
     * @param callable フィルタリング無名関数 trueを返すとインポート
74
     * @return bool インポート結果
75
     */
76 3
    public function importAll($dirPath, callable $filter = null): bool
77
    {
78 3
        $dir = new File($this->applicationRoot . "/" . $dirPath);
79 3
        $isSuccess = true;
80 3
        if ($dir->isDirectory()) {
81 3
            $iterator = $this->getFileSearchIterator($dir->getFilePath());
82 3
            foreach ($iterator as $filepath => $fileObject) {
83 3
                if (preg_match("/(?:\/\.|\/\.\.|\.DS_Store)$/", $filepath)) {
84 3
                    continue;
85
                }
86 3
                $file = new File($filepath);
87 3
                if ($file->isFile()) {
88 3
                    if ($file->getFileExtension() === 'php') {
89 3
                        if ($filter === null || (is_callable($filter) && $filter($file->getFilePath()) === true)) {
90 2
                            include_once $file->getFilePath();
91 3
                            $this->logger->debug($file->getFilePath() . " import success.");
92
                        }
93
                    }
94
                } else {
95
                    $this->logger->warn($filepath . " import failure.");
96 3
                    $isSuccess = false;
97
                }
98
            }
99
        }
100
101 3
        return $isSuccess;
102
    }
103
104
    /**
105
    * ロード可能なクラスを返却する
106
    * @param string クラス名(フルパス指定の場合はクラスパス)
107
    * @return array<string> ロード可能クラス
108
     */
109 7
    private function loadClass(string $className): array
110
    {
111 7
        $rootDir = $this->applicationRoot;
112 7
        $logger = $this->logger;
113
114
        // 名前空間セパレータをパスセパレータに置換
115 7
        if (DIRECTORY_SEPARATOR === '/') {
116 7
            $className = str_replace("\\", DIRECTORY_SEPARATOR, $className);
117
        }
118
119 7
        $search = function ($dirPath, $searchFilePath) use ($logger) {
120 7
            $includeList = [];
121 7
            $dir = new File($dirPath);
122 7
            if (!$dir->isDirectory()) {
123 1
                $logger->error("Invalid search directory path: " . $dir->getFilePath());
124 1
                return $includeList;
125
            }
126 6
            $iterator = $this->getFileSearchIterator($dir->getFilePath());
127 6
            foreach ($iterator as $filepath => $fileObject) {
128 6
                if (!$fileObject->isFile()) {
129 6
                    continue;
130
                }
131 6
                if (strpos($filepath, $searchFilePath) !== false) {
132 5
                    $file = new File($filepath);
133 5
                    $absoluteFilePath = $file->getAbsoluteFilePath();
134 5
                    include_once $absoluteFilePath;
135 5
                    $includeList[] = $absoluteFilePath;
136 6
                    $logger->debug($absoluteFilePath . " load success. (search from " . $dir->getFilePath());
137
                }
138
            }
139
140 6
            return $includeList;
141 7
        };
142
143 7
        return $search("${rootDir}", DIRECTORY_SEPARATOR . "${className}.php");
144
    }
145
146
    /**
147
     * ロード可能なクラスを複数返却する
148
     * @param array クラス名
149
     * @return array<string> ロード済みクラスリスト
150
     */
151
    private function loadClassList(array $classList): array
152
    {
153
        $includedlist = [];
154
        foreach ($classList as $className) {
155
            $result = $this->loadClass($className);
156
            if (is_array($result)) {
157
                $includedlist = array_merge($includedlist, $result);
158
            }
159
        }
160
161
        return $includedlist;
162
    }
163
164
    /**
165
     * ファイル検索イテレータを返却する
166
     * @param string ディレクトリパス
167
     * @return RecursiveIteratorIterator イテレータ
168
     */
169
    private function getFileSearchIterator(string $path): \RecursiveIteratorIterator
170
    {
171 9
        $iterator = [];
172 9
        $file = new File($path);
173 9
        if ($file->isDirectory()) {
174 9
            $iterator = new \RecursiveIteratorIterator(
175 9
                new \RecursiveDirectoryIterator($path),
176 9
                \RecursiveIteratorIterator::LEAVES_ONLY,
177 9
                \RecursiveIteratorIterator::CATCH_GET_CHILD // for Permission deny
178
            );
179
        }
180 9
        return $iterator;
181
    }
182
}
183