@@ -97,6 +97,95 @@ public function testResolveMultipleBasePaths(): void
9797 rmdir ($ pluginPath );
9898 }
9999
100+ public function testLazyPluginPathCallbackIsNotInvokedOnConstruction (): void
101+ {
102+ $ callbackInvoked = false ;
103+ $ callback = function () use (&$ callbackInvoked ): array {
104+ $ callbackInvoked = true ;
105+
106+ return ['/some/plugin/path ' ];
107+ };
108+
109+ new PathResolver ($ this ->testAppPath , $ callback );
110+
111+ // Callback should NOT be invoked during construction
112+ $ this ->assertFalse ($ callbackInvoked , 'Plugin path callback should not be invoked during construction ' );
113+ }
114+
115+ public function testLazyPluginPathCallbackIsInvokedOnFirstResolve (): void
116+ {
117+ $ callbackInvoked = false ;
118+ $ callback = function () use (&$ callbackInvoked ): array {
119+ $ callbackInvoked = true ;
120+
121+ return [];
122+ };
123+
124+ $ resolver = new PathResolver ($ this ->testAppPath , $ callback );
125+
126+ // Invoke resolveAllPaths to trigger callback
127+ iterator_to_array ($ resolver ->resolveAllPaths (['src/*.php ' ]));
128+
129+ // Callback should be invoked on first resolve
130+ $ this ->assertTrue ($ callbackInvoked , 'Plugin path callback should be invoked on first path resolution ' );
131+ }
132+
133+ public function testLazyPluginPathCallbackIsOnlyInvokedOnce (): void
134+ {
135+ $ callbackInvokeCount = 0 ;
136+ $ callback = function () use (&$ callbackInvokeCount ): array {
137+ $ callbackInvokeCount ++;
138+
139+ return [];
140+ };
141+
142+ $ resolver = new PathResolver ($ this ->testAppPath , $ callback );
143+
144+ // Invoke resolveAllPaths multiple times
145+ iterator_to_array ($ resolver ->resolveAllPaths (['src/*.php ' ]));
146+ iterator_to_array ($ resolver ->resolveAllPaths (['src/*.php ' ]));
147+ iterator_to_array ($ resolver ->resolveAllPaths (['src/*.php ' ]));
148+
149+ // Callback should only be invoked once
150+ $ this ->assertSame (1 , $ callbackInvokeCount , 'Plugin path callback should only be invoked once ' );
151+ }
152+
153+ public function testLazyPluginPathsAreMergedWithBasePath (): void
154+ {
155+ // Create a plugin path
156+ $ pluginPath = sys_get_temp_dir () . '/attribute_registry_lazy_plugin_ ' . uniqid ();
157+ mkdir ($ pluginPath . '/src ' , 0755 , true );
158+ file_put_contents ($ pluginPath . '/src/PluginClass.php ' , "<?php \n// Plugin file " );
159+
160+ $ callback = fn (): array => [$ pluginPath ];
161+
162+ $ resolver = new PathResolver ($ this ->testAppPath , $ callback );
163+
164+ $ patterns = ['src/*.php ' ];
165+ $ paths = iterator_to_array ($ resolver ->resolveAllPaths ($ patterns ));
166+
167+ // Should find files from both base path and lazily resolved plugin paths
168+ $ this ->assertContains ($ this ->testAppPath . '/src/TestClass.php ' , $ paths );
169+ $ this ->assertContains ($ pluginPath . '/src/PluginClass.php ' , $ paths );
170+
171+ // Cleanup plugin path
172+ unlink ($ pluginPath . '/src/PluginClass.php ' );
173+ rmdir ($ pluginPath . '/src ' );
174+ rmdir ($ pluginPath );
175+ }
176+
177+ public function testPathResolverWorksWithoutLazyCallback (): void
178+ {
179+ // Ensure backward compatibility - can be constructed without callback
180+ $ resolver = new PathResolver ($ this ->testAppPath );
181+
182+ $ patterns = ['src/*.php ' ];
183+ $ paths = iterator_to_array ($ resolver ->resolveAllPaths ($ patterns ));
184+
185+ $ this ->assertContains ($ this ->testAppPath . '/src/TestClass.php ' , $ paths );
186+ $ this ->assertNotEmpty ($ paths );
187+ }
188+
100189 private function createTestStructure (): void
101190 {
102191 $ structure = [
0 commit comments