Issues (16)

ClassLoader.php (2 issues)

1
<?php
2
3
namespace WebStream\ClassLoader;
4
5
use RecursiveIteratorIterator;
6
use WebStream\DI\Injector;
7
use WebStream\IO\File;
8
use WebStream\IO\FileInputStream;
9
10
/**
11
 * クラスローダ
12
 * @author Ryuichi TANAKA.
13
 * @since 2013/09/02
14
 * @version 0.7
15
 */
16
class ClassLoader
17
{
18
    use Injector;
19
20
    /**
21
     * @var Psr\Log\LoggerInterface
22
     */
23
    private $logger;
24
25
    /**
26
     * @var string アプリケーションルートパス
27
     */
28
    private string $applicationRoot;
29
30
    /**
31
     * constructor
32
     * @param string アプリケーションルートパス
33
     */
34 16
    public function __construct(string $applicationRoot)
35
    {
36 16
        $this->logger = new class () { public function __call($name, $args) {} };
0 ignored issues
show
Closing brace must not be followed by any comment or statement on the same line
Loading history...
The closing brace for the class must go on the next line after the body
Loading history...
37 16
        $this->applicationRoot = $applicationRoot;
38
    }
39
40
    /**
41
     * クラスをロードする
42
     * @param mixed クラスまたはクラスリスト
43
     * @return array<string> ロード済みクラスリスト
44
     */
45 7
    public function load($target): array
46
    {
47 7
        return is_array($target) ? $this->loadClassList($target) : $this->loadClass($target);
48
    }
49
50
    /**
51
     * ファイルをインポートする
52
     * @param string ファイルパス
53
     * @param callable|null $filter フィルタリング無名関数 trueを返すとインポート
54
     * @return bool インポート結果
55
     * @throws \WebStream\Exception\Extend\IOException
56
     */
57 3
    public function import($filepath, callable $filter = null): bool
58
    {
59 3
        $file = new File($this->applicationRoot . "/" . $filepath);
60 3
        if ($file->isFile()) {
61 2
            if ($file->getFileExtension() === 'php') {
62 2
                if ($filter === null || (is_callable($filter) && $filter($file->getFilePath()) === true)) {
63 2
                    include_once $file->getFilePath();
64 2
                    $this->logger->debug($file->getAbsoluteFilePath() . " import success.");
65
                }
66
            }
67
68 2
            return true;
69
        }
70
71 1
        return false;
72
    }
73
74
    /**
75
     * 指定ディレクトリのファイルをインポートする
76
     * @param string ディレクトリパス
77
     * @param callable|null $filter フィルタリング無名関数 trueを返すとインポート
78
     * @return bool インポート結果
79
     * @throws \WebStream\Exception\Extend\IOException
80
     */
81 3
    public function importAll($dirPath, callable $filter = null): bool
82
    {
83 3
        $dir = new File($this->applicationRoot . "/" . $dirPath);
84 3
        $isSuccess = true;
85 3
        if ($dir->isDirectory()) {
86 3
            $iterator = $this->getFileSearchIterator($dir->getAbsoluteFilePath());
87 3
            foreach ($iterator as $filepath => $fileObject) {
88 3
                if (preg_match("/(?:\/\.|\/\.\.|\.DS_Store)$/", $filepath)) {
89 3
                    continue;
90
                }
91 3
                $file = new File($filepath);
92 3
                if ($file->isFile()) {
93 3
                    if ($file->getFileExtension() === 'php') {
94 3
                        if ($filter === null || (is_callable($filter) && $filter($file->getFilePath()) === true)) {
95 1
                            include_once $file->getFilePath();
96 1
                            $this->logger->debug($file->getAbsoluteFilePath() . " import success.");
97
                        }
98
                    }
99
                } else {
100
                    $this->logger->warn($filepath . " import failure.");
101
                    $isSuccess = false;
102
                }
103
            }
104
        }
105
106 3
        return $isSuccess;
107
    }
108
109
    /**
110
     * 名前空間リストを返却する
111
     * @param string ファイル名
112
     * @return array<string> 名前空間リスト
113
     * @throws \WebStream\Exception\Extend\IOException
114
     * @throws \WebStream\Exception\Extend\InvalidArgumentException
115
     */
116 3
    public function getNamespaces($fileName): array
117
    {
118 3
        $dir = new File($this->applicationRoot);
119 3
        $namespaces = [];
120 3
        if ($dir->isDirectory()) {
121 3
            $iterator = $this->getFileSearchIterator($dir->getAbsoluteFilePath());
122 3
            foreach ($iterator as $filepath => $fileObject) {
123 3
                if (preg_match("/(?:\/\.|\/\.\.|\.DS_Store)$/", $filepath)) {
124 3
                    continue;
125
                }
126 3
                $file = new File($filepath);
127 3
                if ($file->isFile() && $file->getFileName() === $fileName) {
128 2
                    $fis = new FileInputStream($file);
129 2
                    while (($line = $fis->readLine()) !== null) {
130 2
                        if (preg_match("/^namespace\s(.*);$/", $line, $matches)) {
131 2
                            $namespaces[] = $matches[1];
132
                        }
133
                    }
134 2
                    $fis->close();
135
                }
136
            }
137
        }
138
139 3
        return $namespaces;
140
    }
141
142
    /**
143
     * ロード可能なクラスを返却する
144
     * @param string クラス名(フルパス指定の場合はクラスパス)
145
     * @return array<string> ロード可能クラス
146
     * @throws \WebStream\Exception\Extend\IOException
147
     */
148 7
    private function loadClass(string $className): array
149
    {
150 7
        $rootDir = $this->applicationRoot;
151 7
        $logger = $this->logger;
152
153
        // 名前空間セパレータをパスセパレータに置換
154 7
        if (DIRECTORY_SEPARATOR === '/') {
155 7
            $className = str_replace("\\", DIRECTORY_SEPARATOR, $className);
156
        }
157
158 7
        $search = function ($dirPath, $searchFilePath) use ($logger) {
159 7
            $includeList = [];
160 7
            $dir = new File($dirPath);
161 7
            if (!$dir->isDirectory()) {
162 1
                $logger->error("Invalid search directory path: " . $dir->getFilePath());
163 1
                return $includeList;
164
            }
165 6
            $iterator = $this->getFileSearchIterator($dir->getAbsoluteFilePath());
166 6
            foreach ($iterator as $filepath => $fileObject) {
167 6
                if (!$fileObject->isFile()) {
168 6
                    continue;
169
                }
170 6
                if (strpos($filepath, $searchFilePath) !== false) {
171 5
                    $file = new File($filepath);
172 5
                    $absoluteFilePath = $file->getAbsoluteFilePath();
173 5
                    include_once $absoluteFilePath;
174 5
                    $includeList[] = $absoluteFilePath;
175 5
                    $logger->debug($absoluteFilePath . " load success. (search from " . $dir->getAbsoluteFilePath() . ")");
176
                }
177
            }
178
179 6
            return $includeList;
180 7
        };
181
182 7
        return $search("${rootDir}", DIRECTORY_SEPARATOR . "${className}.php");
183
    }
184
185
    /**
186
     * ロード可能なクラスを複数返却する
187
     * @param array クラス名
188
     * @return array<string> ロード済みクラスリスト
189
     * @throws \WebStream\Exception\Extend\IOException
190
     */
191
    private function loadClassList(array $classList): array
192
    {
193
        $includedList = [];
194
        foreach ($classList as $className) {
195
            $result = $this->loadClass($className);
196
            if (is_array($result)) {
197
                $includedList = array_merge($includedList, $result);
198
            }
199
        }
200
201
        return $includedList;
202
    }
203
204
    /**
205
     * ファイル検索イテレータを返却する
206
     * @param string ディレクトリパス
207
     * @return RecursiveIteratorIterator イテレータ
208
     */
209 12
    private function getFileSearchIterator(string $path): \RecursiveIteratorIterator
210
    {
211 12
        $iterator = [];
212 12
        $file = new File($path);
213 12
        if ($file->isDirectory()) {
214 12
            $iterator = new \RecursiveIteratorIterator(
215 12
                new \RecursiveDirectoryIterator($path),
216 12
                \RecursiveIteratorIterator::LEAVES_ONLY,
217 12
                \RecursiveIteratorIterator::CATCH_GET_CHILD // for Permission deny
218
            );
219
        }
220 12
        return $iterator;
221
    }
222
}
223