-
-
Notifications
You must be signed in to change notification settings - Fork 0
β‘ Optimize ServiceDiscovery using PSR-4 path resolution #71
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weβll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: dev
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -198,6 +198,23 @@ class ServiceDiscovery | |
| */ | ||
| protected array $registered = []; | ||
|
|
||
| /** | ||
| * The Composer ClassLoader instance. | ||
| */ | ||
| protected ?\Composer\Autoload\ClassLoader $loader = null; | ||
|
|
||
| /** | ||
| * Normalized PSR-4 prefixes map. | ||
| * | ||
| * @var array<string, array<string>> | ||
| */ | ||
| protected array $normalizedPrefixes = []; | ||
|
|
||
| /** | ||
| * Whether prefixes have been loaded. | ||
| */ | ||
| protected bool $prefixesLoaded = false; | ||
|
|
||
| /** | ||
| * Add a path to scan for service definitions. | ||
| */ | ||
|
|
@@ -597,6 +614,35 @@ protected function performDiscovery(): array | |
| */ | ||
| protected function scanPath(string $path, array &$services): void | ||
| { | ||
| $this->loadPrefixes(); | ||
|
|
||
| // Normalize scan path | ||
| $realScanPath = realpath($path); | ||
| if ($realScanPath === false) { | ||
| $realScanPath = $path; // Fallback | ||
| } | ||
| if (DIRECTORY_SEPARATOR !== '/') { | ||
| $realScanPath = str_replace(DIRECTORY_SEPARATOR, '/', $realScanPath); | ||
| } | ||
| $realScanPath = rtrim($realScanPath, '/').'/'; | ||
|
|
||
| // Filter prefixes relevant to this scan path | ||
| $relevantPrefixes = []; | ||
| foreach ($this->normalizedPrefixes as $namespace => $paths) { | ||
| foreach ($paths as $prefixPath) { | ||
| if (str_starts_with($realScanPath, $prefixPath)) { | ||
| $relevantPrefixes[$namespace][] = $prefixPath; | ||
|
|
||
| continue; | ||
| } | ||
| if (str_starts_with($prefixPath, $realScanPath)) { | ||
| $relevantPrefixes[$namespace][] = $prefixPath; | ||
|
|
||
| continue; | ||
| } | ||
| } | ||
| } | ||
|
|
||
| $files = File::allFiles($path); | ||
|
|
||
| foreach ($files as $file) { | ||
|
|
@@ -614,7 +660,21 @@ protected function scanPath(string $path, array &$services): void | |
| continue; | ||
| } | ||
|
|
||
| $class = $this->getClassFromFile($file->getPathname()); | ||
| // Optimization: Resolve class from path | ||
| $realPath = $file->getRealPath(); | ||
| if ($realPath === false) { | ||
| continue; | ||
| } | ||
| if (DIRECTORY_SEPARATOR !== '/') { | ||
| $realPath = str_replace(DIRECTORY_SEPARATOR, '/', $realPath); | ||
| } | ||
|
|
||
| $class = $this->resolveClass($realPath, $relevantPrefixes); | ||
|
|
||
| if ($class === null) { | ||
| $class = $this->getClassFromFile($file->getPathname()); | ||
| } | ||
|
|
||
| if ($class === null) { | ||
| continue; | ||
| } | ||
|
|
@@ -640,6 +700,87 @@ protected function scanPath(string $path, array &$services): void | |
| } | ||
| } | ||
|
|
||
| /** | ||
| * Get the Composer ClassLoader instance. | ||
| */ | ||
| protected function getComposerLoader(): ?\Composer\Autoload\ClassLoader | ||
| { | ||
| if ($this->loader !== null) { | ||
| return $this->loader; | ||
| } | ||
|
|
||
| foreach (spl_autoload_functions() as $autoloader) { | ||
| if (is_array($autoloader) && $autoloader[0] instanceof \Composer\Autoload\ClassLoader) { | ||
| $this->loader = $autoloader[0]; | ||
|
|
||
| return $this->loader; | ||
| } | ||
| } | ||
|
|
||
| return null; | ||
| } | ||
|
|
||
| /** | ||
| * Load and normalize Composer PSR-4 prefixes. | ||
| */ | ||
| protected function loadPrefixes(): void | ||
| { | ||
| if ($this->prefixesLoaded) { | ||
| return; | ||
| } | ||
|
|
||
| $loader = $this->getComposerLoader(); | ||
| if ($loader === null) { | ||
| $this->prefixesLoaded = true; | ||
|
|
||
| return; | ||
| } | ||
|
|
||
| $prefixes = $loader->getPrefixesPsr4(); | ||
|
|
||
| foreach ($prefixes as $namespace => $paths) { | ||
| foreach ($paths as $path) { | ||
| $realPath = realpath($path); | ||
| if ($realPath === false) { | ||
| continue; | ||
| } | ||
|
|
||
| if (DIRECTORY_SEPARATOR !== '/') { | ||
| $realPath = str_replace(DIRECTORY_SEPARATOR, '/', $realPath); | ||
| } | ||
|
|
||
| $this->normalizedPrefixes[$namespace][] = rtrim($realPath, '/').'/'; | ||
| } | ||
| } | ||
|
|
||
| $this->prefixesLoaded = true; | ||
| } | ||
|
|
||
| /** | ||
| * Resolve class name from real file path using a subset of prefixes. | ||
| * | ||
| * @param string $realPath Normalized real path of the file | ||
| * @param array<string, array<string>> $prefixes Subset of prefixes to check | ||
| */ | ||
| protected function resolveClass(string $realPath, array $prefixes): ?string | ||
| { | ||
| foreach ($prefixes as $namespace => $paths) { | ||
| foreach ($paths as $path) { | ||
| if (str_starts_with($realPath, $path)) { | ||
| // Found matching prefix | ||
| $relativePath = substr($realPath, strlen($path)); | ||
|
|
||
| // Convert path to namespace | ||
| $relativeClass = str_replace(['/', '.php'], ['\\', ''], $relativePath); | ||
|
|
||
| return $namespace.$relativeClass; | ||
| } | ||
| } | ||
| } | ||
|
|
||
| return null; | ||
| } | ||
|
Comment on lines
+759
to
+782
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. π§© Analysis chainπ Script executed: head -30 src/Core/Service/ServiceDiscovery.phpRepository: host-uk/core-php Length of output: 825 π Script executed: sed -n '759,782p' src/Core/Service/ServiceDiscovery.phpRepository: host-uk/core-php Length of output: 926 π Script executed: sed -n '730,790p' src/Core/Service/ServiceDiscovery.phpRepository: host-uk/core-php Length of output: 1894 Use a suffix-only replacement for the Using Safer extension removal- $relativeClass = str_replace(['/', '.php'], ['\\', ''], $relativePath);
+ $relativeClass = str_replace('/', '\\', $relativePath);
+ $relativeClass = preg_replace('/\.php$/i', '', $relativeClass);π€ Prompt for AI Agents |
||
|
|
||
| /** | ||
| * Extract class name from a PHP file. | ||
| */ | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use UK spelling for new identifiers and comments.
Please switch to βnormalised/normalise/optimisationβ (e.g.,
normalisedPrefixes, βNormalise scan pathβ) to comply.βοΈ Suggested rename
π€ Prompt for AI Agents