Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
76 / 76
100.00% covered (success)
100.00%
14 / 14
CRAP
100.00% covered (success)
100.00%
1 / 1
Preloader
100.00% covered (success)
100.00%
74 / 74
100.00% covered (success)
100.00%
14 / 14
39
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
2
 getAutoloader
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getLocator
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setPackagesDir
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
3
 getPackagesDir
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 withPackages
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 withDevPackages
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 listPackagesFiles
100.00% covered (success)
100.00%
15 / 15
100.00% covered (success)
100.00%
1 / 1
9
 isDevelopmentClass
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
2
 listFiles
100.00% covered (success)
100.00%
20 / 20
100.00% covered (success)
100.00%
1 / 1
10
 load
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
2
 getAllDeclarations
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
1
 getDeclarations
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
4
 getIncludedFiles
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
1<?php declare(strict_types=1);
2/*
3 * This file is part of Aplus Framework Autoload Library.
4 *
5 * (c) Natan Felles <natanfelles@gmail.com>
6 *
7 * For the full copyright and license information, please view the LICENSE
8 * file that was distributed with this source code.
9 */
10namespace Framework\Autoload;
11
12use InvalidArgumentException;
13
14require_once __DIR__ . '/Autoloader.php';
15require_once __DIR__ . '/Locator.php';
16
17/**
18 * Class Preloader.
19 *
20 * @see https://www.php.net/manual/en/opcache.preloading.php
21 *
22 * @package autoload
23 */
24class Preloader
25{
26    /**
27     * The main 'aplus' packages directory.
28     *
29     * @var string
30     */
31    protected string $packagesDir;
32    protected bool $loadPackages = true;
33    protected bool $loadDevPackages = false;
34    /**
35     * The Autoloader instance necessary to autoload required classes.
36     *
37     * @var Autoloader
38     */
39    protected Autoloader $autoloader;
40    /**
41     * The Locator instance used to list files.
42     *
43     * @var Locator
44     */
45    protected Locator $locator;
46
47    /**
48     * Preloader constructor.
49     *
50     * @param Autoloader|null $autoloader A custom Autoloader instance or null
51     * to auto initialize a new
52     * @param string|null $packagesDir The main 'aplus' packages directory or
53     * null to disable packages loading
54     */
55    public function __construct(
56        Autoloader $autoloader = null,
57        ?string $packagesDir = __DIR__ . '/../../'
58    ) {
59        $this->loadPackages = $packagesDir !== null;
60        if ($this->loadPackages) {
61            $this->setPackagesDir($packagesDir);
62        }
63        $this->autoloader = $autoloader ?? new Autoloader();
64        $this->locator = new Locator($this->autoloader);
65    }
66
67    public function getAutoloader() : Autoloader
68    {
69        return $this->autoloader;
70    }
71
72    public function getLocator() : Locator
73    {
74        return $this->locator;
75    }
76
77    public function setPackagesDir(string $packagesDir) : static
78    {
79        $realpath = \realpath($packagesDir);
80        if ( ! $realpath || ! \is_dir($packagesDir)) {
81            throw new InvalidArgumentException('Invalid packages dir: ' . $packagesDir);
82        }
83        $this->packagesDir = $realpath . \DIRECTORY_SEPARATOR;
84        return $this;
85    }
86
87    public function getPackagesDir() : string
88    {
89        return $this->packagesDir;
90    }
91
92    public function withPackages() : static
93    {
94        $this->loadPackages = true;
95        return $this;
96    }
97
98    public function withDevPackages() : static
99    {
100        $this->loadDevPackages = true;
101        return $this;
102    }
103
104    /**
105     * @param bool $setClasses
106     *
107     * @return array<int,string>
108     */
109    public function listPackagesFiles(bool $setClasses = true) : array
110    {
111        $result = [];
112        foreach ($this->getLocator()->listFiles($this->getPackagesDir()) as $file) {
113            if ( ! \str_ends_with($file, '.php')) {
114                continue;
115            }
116            $className = $this->getLocator()->getClassName($file);
117            if ( ! $className
118                || ($className !== 'Aplus' && ! \str_starts_with($className, 'Framework\\'))
119            ) {
120                continue;
121            }
122            if ( ! $this->loadDevPackages && $this->isDevelopmentClass($className)) {
123                continue;
124            }
125            if ($setClasses) {
126                $this->getAutoloader()->setClass($className, $file);
127            }
128            $result[] = $file;
129        }
130        \sort($result);
131        return \array_unique($result);
132    }
133
134    protected function isDevelopmentClass(string $className) : bool
135    {
136        return \str_starts_with($className, 'Framework\\CodingStandard\\')
137            || \str_starts_with($className, 'Framework\\Testing\\');
138    }
139
140    /**
141     * @param bool $setClasses
142     *
143     * @return array<int,string>
144     */
145    public function listFiles(bool $setClasses = true) : array
146    {
147        $result = [];
148        foreach ($this->getAutoloader()->getClasses() as $file) {
149            $result[] = $file;
150        }
151        foreach ($this->getAutoloader()->getNamespaces() as $namespace => $directories) {
152            foreach ($directories as $directory) {
153                $files = $this->getLocator()->listFiles($directory);
154                foreach ($files as $file) {
155                    if ( ! \str_ends_with($file, '.php')) {
156                        continue;
157                    }
158                    $className = $this->getLocator()->getClassName($file);
159                    if ( ! $className
160                        || ! \str_starts_with($className, $namespace . '\\')
161                    ) {
162                        continue;
163                    }
164                    if ($setClasses) {
165                        $this->getAutoloader()->setClass($className, $file);
166                    }
167                    $result[] = $file;
168                }
169            }
170        }
171        if ($this->loadPackages) {
172            $result = [...$result, ...$this->listPackagesFiles($setClasses)];
173        }
174        \sort($result);
175        return \array_unique($result);
176    }
177
178    /**
179     * Load files to be seen by the PHP OPcache Preloading when the engine starts.
180     *
181     * @return array<int,string> The loaded files
182     */
183    public function load() : array
184    {
185        $loadedFiles = [];
186        foreach ($this->listFiles() as $file) {
187            (static function () use ($file) : void {
188                require_once $file;
189            })();
190            $loadedFiles[] = $file;
191        }
192        return $loadedFiles;
193    }
194
195    /**
196     * Get a list of all declared classes, interfaces and traits.
197     *
198     * @return array<int,string>
199     */
200    public static function getAllDeclarations() : array
201    {
202        $declarations = [
203            ...\get_declared_classes(),
204            ...\get_declared_interfaces(),
205            ...\get_declared_traits(),
206        ];
207        \sort($declarations);
208        return $declarations;
209    }
210
211    /**
212     * Get a list of Framework declarations.
213     *
214     * @return array<int,string>
215     */
216    public static function getDeclarations() : array
217    {
218        $result = [];
219        foreach (static::getAllDeclarations() as $declaration) {
220            if ($declaration === 'Aplus' || \str_starts_with($declaration, 'Framework\\')) {
221                $result[] = $declaration;
222            }
223        }
224        return $result;
225    }
226
227    /**
228     * Get a list of all included/required files.
229     *
230     * @return array<int,string>
231     */
232    public static function getIncludedFiles() : array
233    {
234        return \get_included_files();
235    }
236}