Skip to content

Commit 770b35f

Browse files
added a Manager::class static public method - startEnvIFNotStarted()
1 parent c968fe4 commit 770b35f

File tree

6 files changed

+300
-147
lines changed

6 files changed

+300
-147
lines changed

AutoloadRegister.php

Lines changed: 117 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -4,196 +4,219 @@
44

55
namespace Tamedevelopers\Support;
66

7+
use FilesystemIterator;
78
use RecursiveIteratorIterator;
89
use RecursiveDirectoryIterator;
910
use Tamedevelopers\Support\Capsule\File;
1011
use Tamedevelopers\Support\Traits\ServerTrait;
1112

12-
class AutoloadRegister{
13-
13+
14+
/**
15+
* Improved autoloader and file loader.
16+
*
17+
* Differences from AutoloadRegister:
18+
* - Skips dot entries during directory scan (better perf, fewer edge cases)
19+
* - Supports class, trait, interface, and enum (PHP 8.1+)
20+
* - Idempotent autoload registration (won't re-register repeatedly)
21+
* - Same public API and usage: AutoloadRegister2::load('dir') or ::load(['dir1','dir2'])
22+
*/
23+
class AutoloadRegister
24+
{
1425
use ServerTrait;
15-
26+
1627
/**
17-
* The base directory to scan for classes and files.
18-
* @var string
28+
* Directories to scan
29+
* @var array<string>
1930
*/
20-
private static $baseDirectory;
31+
private static array $baseDirectories = [];
2132

2233
/**
23-
* The class map that stores the class names and their corresponding file paths.
24-
* @var array
34+
* FQN class => file path
35+
* @var array<string,string>
2536
*/
26-
private static $classMap = [];
37+
private static array $classMap = [];
2738

2839
/**
29-
* The file map that stores the file paths and their corresponding relative paths.
30-
* @var array
40+
* relative php file (no class/trait/interface/enum) => file path
41+
* @var array<string,string>
3142
*/
32-
private static $fileMap = [];
43+
private static array $fileMap = [];
3344

3445
/**
35-
* Autoload function to load class and files in a given folder
46+
* Ensure we only register spl_autoload once.
47+
*/
48+
private static bool $registered = false;
49+
50+
/**
51+
* Autoload function to load classes and files in the given folder(s).
3652
*
37-
* @param string|array $baseDirectory
38-
* - The directory path to load
39-
* - Do not include the root path, as The Application already have a copy of your path
40-
* - e.g [classes] or [app/main]
41-
*
42-
* @return void
53+
* @param string|array $baseDirectory Directory path(s) relative to application base.
54+
* - Do not include the root path. e.g. 'classes' or 'app/main'
4355
*/
44-
public static function load(string|array $baseDirectory)
56+
public static function load(string|array $baseDirectory): void
4557
{
46-
if(is_array($baseDirectory)){
47-
foreach($baseDirectory as $directory){
48-
self::$baseDirectory = self::formatWithBaseDirectory($directory);
49-
// only allow is an existing directory
50-
if(is_dir(self::$baseDirectory)){
51-
self::boot();
52-
}
53-
}
54-
} else{
55-
self::$baseDirectory = self::formatWithBaseDirectory($baseDirectory);
56-
// only allow is an existing directory
57-
if(is_dir(self::$baseDirectory)){
58-
self::boot();
58+
self::$baseDirectories = [];
59+
60+
$dirs = is_array($baseDirectory) ? $baseDirectory : [$baseDirectory];
61+
foreach ($dirs as $directory) {
62+
$path = self::formatWithBaseDirectory($directory);
63+
if (File::isDirectory($path)) {
64+
self::$baseDirectories[] = $path;
5965
}
6066
}
67+
68+
if (empty(self::$baseDirectories)) {
69+
return; // nothing to do
70+
}
71+
72+
self::boot();
6173
}
6274

6375
/**
64-
* Boot the autoloader by setting the base directory,
65-
* - Scanning the directory, and registering the autoload method.
66-
* @return void
76+
* Boot the autoloader by scanning directories and registering autoload.
6777
*/
68-
private static function boot()
78+
private static function boot(): void
6979
{
70-
self::generateClassMap();
71-
self::generateFileMap();
80+
// reset maps to avoid duplicates across repeated calls
81+
self::$classMap = [];
82+
self::$fileMap = [];
83+
84+
foreach (self::$baseDirectories as $base) {
85+
self::generateClassMapFor($base);
86+
self::generateFileMapFor($base);
87+
}
88+
7289
self::loadFiles();
73-
spl_autoload_register([__CLASS__, 'loadClass']);
90+
91+
if (!self::$registered) {
92+
spl_autoload_register([__CLASS__, 'loadClass']);
93+
self::$registered = true;
94+
}
7495
}
7596

7697
/**
77-
* Autoload function to load the class file based on the class name.
78-
*
79-
* @param string $className The name of the class to load.
80-
* @return void
98+
* PSR-like autoload callback using the internally built class map.
8199
*/
82-
private static function loadClass($className)
100+
private static function loadClass(string $className): void
83101
{
102+
$className = ltrim($className, '\\');
84103
$filePath = self::$classMap[$className] ?? null;
85-
if ($filePath && file_exists($filePath)) {
104+
if ($filePath && File::exists($filePath)) {
86105
require_once $filePath;
87106
}
88107
}
89108

90109
/**
91-
* Load the files from the file map.
92-
*
93-
* @return void
110+
* Load standalone files (without class/trait/interface/enum) from the file map.
94111
*/
95-
private static function loadFiles()
112+
private static function loadFiles(): void
96113
{
97-
foreach (self::$fileMap as $fileName => $filePath) {
98-
if (file_exists($filePath)) {
114+
foreach (self::$fileMap as $filePath) {
115+
if (File::exists($filePath)) {
99116
require_once $filePath;
100117
}
101118
}
102119
}
103120

104121
/**
105-
* Generate the class map by scanning the base directory and its subdirectories.
106-
*
107-
* @return void
122+
* Build class map for a single base directory.
108123
*/
109-
private static function generateClassMap()
124+
private static function generateClassMapFor(string $base): void
110125
{
111-
$fileIterator = new RecursiveIteratorIterator(
112-
new RecursiveDirectoryIterator(self::$baseDirectory)
126+
$iterator = new RecursiveIteratorIterator(
127+
new RecursiveDirectoryIterator($base, FilesystemIterator::SKIP_DOTS)
113128
);
114129

115-
foreach ($fileIterator as $file) {
130+
foreach ($iterator as $file) {
116131
if ($file->isFile() && $file->getExtension() === 'php') {
117-
$filePath = $file->getPathname();
118-
$className = self::getClassName($filePath);
119-
if (!is_null($className)) {
132+
$filePath = $file->getPathname();
133+
$className = self::getClassName($filePath);
134+
if ($className !== null) {
120135
self::$classMap[ltrim($className, '\\')] = self::pathReplacer($filePath);
121136
}
122137
}
123138
}
124139
}
125140

126141
/**
127-
* Generate the file map by scanning the base directory and its subdirectories.
128-
*
129-
* @return void
142+
* Build file map (php files without class/trait/interface/enum) for a base directory.
130143
*/
131-
private static function generateFileMap()
144+
private static function generateFileMapFor(string $base): void
132145
{
133-
$fileIterator = new RecursiveIteratorIterator(
134-
new RecursiveDirectoryIterator(self::$baseDirectory)
146+
$iterator = new RecursiveIteratorIterator(
147+
new RecursiveDirectoryIterator($base, FilesystemIterator::SKIP_DOTS)
135148
);
136149

137-
foreach ($fileIterator as $file) {
150+
foreach ($iterator as $file) {
138151
if ($file->isFile() && $file->getExtension() === 'php') {
139-
$filePath = $file->getPathname();
152+
$filePath = $file->getPathname();
140153
$className = self::getClassName($filePath);
141-
142154
if ($className === null) {
143-
$relativePath = self::getRelativePath($filePath);
155+
$relativePath = self::getRelativePath($base, $filePath);
144156
self::$fileMap[$relativePath] = self::pathReplacer($filePath);
145157
}
146158
}
147159
}
148160
}
149161

150162
/**
151-
* Get the relative path from the file path.
152-
*
153-
* @param string $filePath The file path.
154-
* @return string The relative path.
163+
* Get the relative path for a file with respect to the provided base directory.
155164
*/
156-
private static function getRelativePath($filePath)
165+
private static function getRelativePath(string $base, string $filePath): string
157166
{
158-
$relativePath = substr($filePath, strlen(self::$baseDirectory));
167+
$relativePath = substr($filePath, strlen($base));
159168
return ltrim($relativePath, '/\\');
160169
}
161170

162171
/**
163-
* Get the class name from the file path.
164-
*
165-
* @param string $filePath The file path.
166-
* @return string|null The class name, or null if not found.
172+
* Parse a PHP file and return the fully-qualified class/trait/interface/enum name, or null.
167173
*/
168-
private static function getClassName($filePath)
174+
private static function getClassName(string $filePath): ?string
169175
{
170-
$namespace = '';
171-
$content = File::get($filePath);
172-
$tokens = token_get_all($content);
173-
$count = count($tokens);
176+
$namespace = '';
177+
$content = File::get($filePath);
178+
if ($content === '' || $content === null) {
179+
return null;
180+
}
181+
182+
$tokens = token_get_all($content);
183+
$count = count($tokens);
184+
185+
// Target tokens to detect named declarations
186+
$targets = [T_CLASS, T_TRAIT, T_INTERFACE];
187+
if (defined('T_ENUM')) {
188+
$targets[] = T_ENUM; // PHP 8.1+
189+
}
174190

175191
for ($i = 0; $i < $count; $i++) {
176-
if ($tokens[$i][0] === T_NAMESPACE) {
192+
// Namespace collection
193+
if (is_array($tokens[$i]) && $tokens[$i][0] === T_NAMESPACE) {
194+
$namespace = '';
177195
for ($j = $i + 1; $j < $count; $j++) {
178-
if ($tokens[$j][0] === T_STRING || $tokens[$j][0] === T_NS_SEPARATOR) {
196+
if (is_array($tokens[$j]) && ($tokens[$j][0] === T_STRING || $tokens[$j][0] === T_NS_SEPARATOR)) {
179197
$namespace .= $tokens[$j][1];
180198
} elseif ($tokens[$j] === '{' || $tokens[$j] === ';') {
181199
break;
182200
}
183201
}
184202
}
185203

186-
if ($tokens[$i][0] === T_CLASS || $tokens[$i][0] === T_TRAIT) {
204+
// Class/Trait/Interface/Enum name collection
205+
if (is_array($tokens[$i]) && in_array($tokens[$i][0], $targets, true)) {
206+
// Skip anonymous class: "class(" pattern (no T_STRING name)
187207
for ($j = $i + 1; $j < $count; $j++) {
188-
if ($tokens[$j] === '{' || $tokens[$j] === 'extends' || $tokens[$j] === 'implements' || $tokens[$j] === 'use') {
208+
if ($tokens[$j] === '{' || $tokens[$j] === '(') {
209+
// '{' for regular bodies, '(' likely anonymous class
189210
break;
190-
} elseif ($tokens[$j][0] === T_STRING) {
191-
return $namespace . '\\' . $tokens[$j][1];
211+
} elseif (is_array($tokens[$j]) && $tokens[$j][0] === T_STRING) {
212+
$name = $tokens[$j][1];
213+
return ltrim(($namespace !== '' ? $namespace . '\\' : '') . $name, '\\');
192214
}
193215
}
194216
}
195217
}
196-
return;
218+
219+
return null;
197220
}
198221

199222
}

0 commit comments

Comments
 (0)