@@ -93,6 +93,12 @@ class Autoloader
9393 */
9494 protected $ helpers = ['url ' ];
9595
96+ /**
97+ * Track if Composer namespaces have been loaded in this initialization.
98+ * Resets on each initialize() call to allow tests to load Composer.
99+ */
100+ private bool $ composerLoaded = false ;
101+
96102 public function __construct (private readonly string $ composerPath = COMPOSER_PATH )
97103 {
98104 }
@@ -109,6 +115,8 @@ public function initialize(Autoload $config, Modules $modules)
109115 $ this ->classmap = [];
110116 $ this ->files = [];
111117
118+ $ this ->composerLoaded = false ;
119+
112120 // We have to have one or the other, though we don't enforce the need
113121 // to have both present in order to work.
114122 if ($ config ->psr4 === [] && $ config ->classmap === []) {
@@ -146,16 +154,36 @@ private function loadComposerAutoloader(Modules $modules): void
146154 define ('VENDORPATH ' , dirname ($ this ->composerPath ) . DIRECTORY_SEPARATOR );
147155 }
148156
149- /** @var ClassLoader $composer */
150- $ composer = include $ this ->composerPath ;
157+ // Skip if already loaded in this initialization
158+ if ($ this ->composerLoaded ) {
159+ return ;
160+ }
151161
152- // Should we load through Composer's namespaces, also?
153- if ($ modules ->discoverInComposer ) {
154- $ composerPackages = $ modules ->composerPackages ;
155- $ this ->loadComposerNamespaces ($ composer , $ composerPackages ?? []);
162+ // Use advisory file lock to coordinate between parallel processes
163+ $ lockFile = sys_get_temp_dir () . '/ci_autoload_ ' . sha1 ($ this ->composerPath ) . '.lock ' ;
164+ $ lockFp = @fopen ($ lockFile , 'cb ' );
165+ if ($ lockFp ) {
166+ flock ($ lockFp , LOCK_EX );
156167 }
157168
158- unset($ composer );
169+ try {
170+ /** @var ClassLoader $composer */
171+ $ composer = include $ this ->composerPath ;
172+
173+ // Should we load through Composer's namespaces, also?
174+ if ($ modules ->discoverInComposer ) {
175+ $ composerPackages = $ modules ->composerPackages ;
176+ $ this ->loadComposerNamespaces ($ composer , $ composerPackages ?? []);
177+ $ this ->composerLoaded = true ;
178+ }
179+
180+ unset($ composer );
181+ } finally {
182+ if ($ lockFp ) {
183+ flock ($ lockFp , LOCK_UN );
184+ fclose ($ lockFp );
185+ }
186+ }
159187 }
160188
161189 /**
0 commit comments