1111class AutoloadRegister{
1212
1313 /**
14- * Autoload All Folder and Sub-Folder files
15- *
16- * @param string $path_to_folder
17- * - Specify the folder to autoload
14+ * The base directory to scan for classes and files.
15+ * @var string
16+ */
17+ static private $ baseDirectory ;
18+
19+ /**
20+ * The class map that stores the class names and their corresponding file paths.
21+ * @var array
22+ */
23+ private static $ classMap = [];
24+
25+ /**
26+ * The file map that stores the file paths and their corresponding relative paths.
27+ * @var array
28+ */
29+ private static $ fileMap = [];
30+
31+ /**
32+ * Autoload function to load class and files in a given folder
33+ *
34+ * @param string|array $baseDirectory
35+ * - The directory path to load
1836 * - Do not include the root path, as The Application already have a copy of your path
1937 * - e.g [classes] or [app/main]
2038 *
2139 * @return void
2240 */
23- static public function load (? string $ path_to_folder = null )
41+ static public function load (string | array $ baseDirectory )
2442 {
25- // If path is not null
26- if (!empty ($ path_to_folder )){
27- spl_autoload_register (function ($ className ) use ($ path_to_folder ) {
28- self ::register ($ path_to_folder );
29- });
43+ if (is_array ($ baseDirectory )){
44+ foreach ($ baseDirectory as $ directory ){
45+ self ::$ baseDirectory = self ::getBasePath ($ directory );
46+ // only allow is an existing directory
47+ if (is_dir (self ::$ baseDirectory )){
48+ self ::boot ();
49+ }
50+ }
51+ } else {
52+ self ::$ baseDirectory = self ::getBasePath ($ baseDirectory );
53+ // only allow is an existing directory
54+ if (is_dir (self ::$ baseDirectory )){
55+ self ::boot ();
56+ }
3057 }
3158 }
3259
3360 /**
34- * Register method
35- *
36- * @param string $path_to_folder
37- *
61+ * Boot the autoloader by setting the base directory,
62+ * - Scanning the directory, and registering the autoload method.
63+ * @return void
64+ */
65+ private static function boot ()
66+ {
67+ self ::generateClassMap ();
68+ self ::generateFileMap ();
69+ self ::loadFiles ();
70+ spl_autoload_register ([__CLASS__ , 'loadClass ' ]);
71+ }
72+
73+ /**
74+ * Autoload function to load the class file based on the class name.
75+ *
76+ * @param string $className The name of the class to load.
77+ * @return void
78+ */
79+ private static function loadClass ($ className )
80+ {
81+ $ filePath = self ::$ classMap [$ className ] ?? null ;
82+ if ($ filePath && file_exists ($ filePath )) {
83+ require_once $ filePath ;
84+ }
85+ }
86+
87+ /**
88+ * Load the files from the file map.
89+ *
90+ * @return void
91+ */
92+ private static function loadFiles ()
93+ {
94+ foreach (self ::$ fileMap as $ fileName => $ filePath ) {
95+ if (file_exists ($ filePath )) {
96+ require_once $ filePath ;
97+ }
98+ }
99+ }
100+
101+ /**
102+ * Generate the class map by scanning the base directory and its subdirectories.
103+ *
38104 * @return void
39105 */
40- static private function register ( $ path_to_folder )
106+ private static function generateClassMap ( )
41107 {
42- // directory full path
43- $ directory = self ::convertPath ($ path_to_folder );
108+ $ fileIterator = new RecursiveIteratorIterator (
109+ new RecursiveDirectoryIterator (self ::$ baseDirectory )
110+ );
44111
45- if (!is_dir ($ directory )) {
46- return ;
112+ foreach ($ fileIterator as $ file ) {
113+ if ($ file ->isFile () && $ file ->getExtension () === 'php ' ) {
114+ $ filePath = $ file ->getPathname ();
115+ $ className = self ::getClassName ($ filePath );
116+ if (!is_null ($ className )) {
117+ self ::$ classMap [ltrim ($ className , '\\' )] = self ::pathReplacer ($ filePath );
118+ }
119+ }
47120 }
121+ }
48122
49- // Create a recursive iterator to iterate through the directory
50- $ iterator = new RecursiveIteratorIterator (
51- new RecursiveDirectoryIterator (
52- $ directory ,
53- RecursiveDirectoryIterator::SKIP_DOTS
54- ),
55- RecursiveIteratorIterator::LEAVES_ONLY
123+ /**
124+ * Generate the file map by scanning the base directory and its subdirectories.
125+ *
126+ * @return void
127+ */
128+ private static function generateFileMap ()
129+ {
130+ $ fileIterator = new RecursiveIteratorIterator (
131+ new RecursiveDirectoryIterator (self ::$ baseDirectory )
56132 );
57133
58- // Loop through the iterator
59- foreach ($ iterator as $ file ) {
60- // Check if the item is a file and has a PHP extension
134+ foreach ($ fileIterator as $ file ) {
61135 if ($ file ->isFile () && $ file ->getExtension () === 'php ' ) {
62- include_once $ file ->getPathname ();
136+ $ filePath = $ file ->getPathname ();
137+ $ className = self ::getClassName ($ filePath );
138+
139+ if ($ className === null ) {
140+ $ relativePath = self ::getRelativePath ($ filePath );
141+ self ::$ fileMap [$ relativePath ] = self ::pathReplacer ($ filePath );
142+ }
143+ }
144+ }
145+ }
146+
147+ /**
148+ * Get the relative path from the file path.
149+ *
150+ * @param string $filePath The file path.
151+ * @return string The relative path.
152+ */
153+ private static function getRelativePath ($ filePath )
154+ {
155+ $ relativePath = substr ($ filePath , strlen (self ::$ baseDirectory ));
156+ return ltrim ($ relativePath , '/ \\' );
157+ }
158+
159+ /**
160+ * Get the class name from the file path.
161+ *
162+ * @param string $filePath The file path.
163+ * @return string|null The class name, or null if not found.
164+ */
165+ private static function getClassName ($ filePath )
166+ {
167+ $ namespace = '' ;
168+ $ content = file_get_contents ($ filePath );
169+ $ tokens = token_get_all ($ content );
170+ $ count = count ($ tokens );
171+
172+ for ($ i = 0 ; $ i < $ count ; $ i ++) {
173+ if ($ tokens [$ i ][0 ] === T_NAMESPACE ) {
174+ for ($ j = $ i + 1 ; $ j < $ count ; $ j ++) {
175+ if ($ tokens [$ j ][0 ] === T_STRING || $ tokens [$ j ][0 ] === T_NS_SEPARATOR ) {
176+ $ namespace .= $ tokens [$ j ][1 ];
177+ } elseif ($ tokens [$ j ] === '{ ' || $ tokens [$ j ] === '; ' ) {
178+ break ;
179+ }
180+ }
181+ }
182+
183+ if ($ tokens [$ i ][0 ] === T_CLASS || $ tokens [$ i ][0 ] === T_TRAIT ) {
184+ for ($ j = $ i + 1 ; $ j < $ count ; $ j ++) {
185+ if ($ tokens [$ j ] === '{ ' || $ tokens [$ j ] === 'extends ' || $ tokens [$ j ] === 'implements ' || $ tokens [$ j ] === 'use ' ) {
186+ break ;
187+ } elseif ($ tokens [$ j ][0 ] === T_STRING ) {
188+ return $ namespace . '\\' . $ tokens [$ j ][1 ];
189+ }
190+ }
63191 }
64192 }
193+ return ;
65194 }
66195
67196 /**
@@ -71,13 +200,27 @@ static private function register($path_to_folder)
71200 *
72201 * @return string
73202 */
74- static private function convertPath ($ path_to_folder )
203+ private static function getBasePath ($ path_to_folder )
75204 {
76205 $ ormDotEnv = new OrmDotEnv ();
206+ return self ::pathReplacer ($ ormDotEnv ->getDirectory () . "/ {$ path_to_folder }" );
207+ }
208+
209+ /**
210+ * Replace path with given string
211+ * \ or /
212+ *
213+ * @param string $path
214+ * @param string $replacer
215+ *
216+ * @return string
217+ */
218+ static private function pathReplacer ($ path , $ replacer = '/ ' )
219+ {
77220 return str_replace (
78- ' / ' ,
79- '\\' ,
80- $ ormDotEnv -> getDirectory () . " / { $ path_to_folder }"
221+ [ '\\' , ' / ' ] ,
222+ $ replacer ,
223+ $ path
81224 );
82225 }
83226
0 commit comments