4242 */
4343class ClassLoader
4444{
45- /** @var ?string */
45+ /** @var \Closure(string):void */
46+ private static $ includeFile ;
47+
48+ /** @var string|null */
4649 private $ vendorDir ;
4750
4851 // PSR-4
4952 /**
50- * @var array[]
51- * @psalm-var array<string, array<string, int>>
53+ * @var array<string, array<string, int>>
5254 */
5355 private $ prefixLengthsPsr4 = array ();
5456 /**
55- * @var array[]
56- * @psalm-var array<string, array<int, string>>
57+ * @var array<string, list<string>>
5758 */
5859 private $ prefixDirsPsr4 = array ();
5960 /**
60- * @var array[]
61- * @psalm-var array<string, string>
61+ * @var list<string>
6262 */
6363 private $ fallbackDirsPsr4 = array ();
6464
6565 // PSR-0
6666 /**
67- * @var array[]
68- * @psalm-var array<string, array<string, string[]>>
67+ * List of PSR-0 prefixes
68+ *
69+ * Structured as array('F (first letter)' => array('Foo\Bar (full prefix)' => array('path', 'path2')))
70+ *
71+ * @var array<string, array<string, list<string>>>
6972 */
7073 private $ prefixesPsr0 = array ();
7174 /**
72- * @var array[]
73- * @psalm-var array<string, string>
75+ * @var list<string>
7476 */
7577 private $ fallbackDirsPsr0 = array ();
7678
7779 /** @var bool */
7880 private $ useIncludePath = false ;
7981
8082 /**
81- * @var string[]
82- * @psalm-var array<string, string>
83+ * @var array<string, string>
8384 */
8485 private $ classMap = array ();
8586
8687 /** @var bool */
8788 private $ classMapAuthoritative = false ;
8889
8990 /**
90- * @var bool[]
91- * @psalm-var array<string, bool>
91+ * @var array<string, bool>
9292 */
9393 private $ missingClasses = array ();
9494
95- /** @var ? string */
95+ /** @var string|null */
9696 private $ apcuPrefix ;
9797
9898 /**
99- * @var self[]
99+ * @var array<string, self>
100100 */
101101 private static $ registeredLoaders = array ();
102102
103103 /**
104- * @param ? string $vendorDir
104+ * @param string|null $vendorDir
105105 */
106106 public function __construct ($ vendorDir = null )
107107 {
108108 $ this ->vendorDir = $ vendorDir ;
109+ self ::initializeIncludeClosure ();
109110 }
110111
111112 /**
112- * @return string[]
113+ * @return array< string, list<string>>
113114 */
114115 public function getPrefixes ()
115116 {
@@ -121,44 +122,39 @@ public function getPrefixes()
121122 }
122123
123124 /**
124- * @return array[]
125- * @psalm-return array<string, array<int, string>>
125+ * @return array<string, list<string>>
126126 */
127127 public function getPrefixesPsr4 ()
128128 {
129129 return $ this ->prefixDirsPsr4 ;
130130 }
131131
132132 /**
133- * @return array[]
134- * @psalm-return array<string, string>
133+ * @return list<string>
135134 */
136135 public function getFallbackDirs ()
137136 {
138137 return $ this ->fallbackDirsPsr0 ;
139138 }
140139
141140 /**
142- * @return array[]
143- * @psalm-return array<string, string>
141+ * @return list<string>
144142 */
145143 public function getFallbackDirsPsr4 ()
146144 {
147145 return $ this ->fallbackDirsPsr4 ;
148146 }
149147
150148 /**
151- * @return string[] Array of classname => path
152- * @psalm-return array<string, string>
149+ * @return array<string, string> Array of classname => path
153150 */
154151 public function getClassMap ()
155152 {
156153 return $ this ->classMap ;
157154 }
158155
159156 /**
160- * @param string[] $classMap Class to filename map
161- * @psalm-param array<string, string> $classMap
157+ * @param array<string, string> $classMap Class to filename map
162158 *
163159 * @return void
164160 */
@@ -175,24 +171,25 @@ public function addClassMap(array $classMap)
175171 * Registers a set of PSR-0 directories for a given prefix, either
176172 * appending or prepending to the ones previously set for this prefix.
177173 *
178- * @param string $prefix The prefix
179- * @param string[] |string $paths The PSR-0 root directories
180- * @param bool $prepend Whether to prepend the directories
174+ * @param string $prefix The prefix
175+ * @param list< string> |string $paths The PSR-0 root directories
176+ * @param bool $prepend Whether to prepend the directories
181177 *
182178 * @return void
183179 */
184180 public function add ($ prefix , $ paths , $ prepend = false )
185181 {
182+ $ paths = (array ) $ paths ;
186183 if (!$ prefix ) {
187184 if ($ prepend ) {
188185 $ this ->fallbackDirsPsr0 = array_merge (
189- ( array ) $ paths ,
186+ $ paths ,
190187 $ this ->fallbackDirsPsr0
191188 );
192189 } else {
193190 $ this ->fallbackDirsPsr0 = array_merge (
194191 $ this ->fallbackDirsPsr0 ,
195- ( array ) $ paths
192+ $ paths
196193 );
197194 }
198195
@@ -201,19 +198,19 @@ public function add($prefix, $paths, $prepend = false)
201198
202199 $ first = $ prefix [0 ];
203200 if (!isset ($ this ->prefixesPsr0 [$ first ][$ prefix ])) {
204- $ this ->prefixesPsr0 [$ first ][$ prefix ] = ( array ) $ paths ;
201+ $ this ->prefixesPsr0 [$ first ][$ prefix ] = $ paths ;
205202
206203 return ;
207204 }
208205 if ($ prepend ) {
209206 $ this ->prefixesPsr0 [$ first ][$ prefix ] = array_merge (
210- ( array ) $ paths ,
207+ $ paths ,
211208 $ this ->prefixesPsr0 [$ first ][$ prefix ]
212209 );
213210 } else {
214211 $ this ->prefixesPsr0 [$ first ][$ prefix ] = array_merge (
215212 $ this ->prefixesPsr0 [$ first ][$ prefix ],
216- ( array ) $ paths
213+ $ paths
217214 );
218215 }
219216 }
@@ -222,27 +219,28 @@ public function add($prefix, $paths, $prepend = false)
222219 * Registers a set of PSR-4 directories for a given namespace, either
223220 * appending or prepending to the ones previously set for this namespace.
224221 *
225- * @param string $prefix The prefix/namespace, with trailing '\\'
226- * @param string[] |string $paths The PSR-4 base directories
227- * @param bool $prepend Whether to prepend the directories
222+ * @param string $prefix The prefix/namespace, with trailing '\\'
223+ * @param list< string> |string $paths The PSR-4 base directories
224+ * @param bool $prepend Whether to prepend the directories
228225 *
229226 * @throws \InvalidArgumentException
230227 *
231228 * @return void
232229 */
233230 public function addPsr4 ($ prefix , $ paths , $ prepend = false )
234231 {
232+ $ paths = (array ) $ paths ;
235233 if (!$ prefix ) {
236234 // Register directories for the root namespace.
237235 if ($ prepend ) {
238236 $ this ->fallbackDirsPsr4 = array_merge (
239- ( array ) $ paths ,
237+ $ paths ,
240238 $ this ->fallbackDirsPsr4
241239 );
242240 } else {
243241 $ this ->fallbackDirsPsr4 = array_merge (
244242 $ this ->fallbackDirsPsr4 ,
245- ( array ) $ paths
243+ $ paths
246244 );
247245 }
248246 } elseif (!isset ($ this ->prefixDirsPsr4 [$ prefix ])) {
@@ -252,18 +250,18 @@ public function addPsr4($prefix, $paths, $prepend = false)
252250 throw new \InvalidArgumentException ("A non-empty PSR-4 prefix must end with a namespace separator. " );
253251 }
254252 $ this ->prefixLengthsPsr4 [$ prefix [0 ]][$ prefix ] = $ length ;
255- $ this ->prefixDirsPsr4 [$ prefix ] = ( array ) $ paths ;
253+ $ this ->prefixDirsPsr4 [$ prefix ] = $ paths ;
256254 } elseif ($ prepend ) {
257255 // Prepend directories for an already registered namespace.
258256 $ this ->prefixDirsPsr4 [$ prefix ] = array_merge (
259- ( array ) $ paths ,
257+ $ paths ,
260258 $ this ->prefixDirsPsr4 [$ prefix ]
261259 );
262260 } else {
263261 // Append directories for an already registered namespace.
264262 $ this ->prefixDirsPsr4 [$ prefix ] = array_merge (
265263 $ this ->prefixDirsPsr4 [$ prefix ],
266- ( array ) $ paths
264+ $ paths
267265 );
268266 }
269267 }
@@ -272,8 +270,8 @@ public function addPsr4($prefix, $paths, $prepend = false)
272270 * Registers a set of PSR-0 directories for a given prefix,
273271 * replacing any others previously set for this prefix.
274272 *
275- * @param string $prefix The prefix
276- * @param string[] |string $paths The PSR-0 base directories
273+ * @param string $prefix The prefix
274+ * @param list< string> |string $paths The PSR-0 base directories
277275 *
278276 * @return void
279277 */
@@ -290,8 +288,8 @@ public function set($prefix, $paths)
290288 * Registers a set of PSR-4 directories for a given namespace,
291289 * replacing any others previously set for this namespace.
292290 *
293- * @param string $prefix The prefix/namespace, with trailing '\\'
294- * @param string[] |string $paths The PSR-4 base directories
291+ * @param string $prefix The prefix/namespace, with trailing '\\'
292+ * @param list< string> |string $paths The PSR-4 base directories
295293 *
296294 * @throws \InvalidArgumentException
297295 *
@@ -425,7 +423,8 @@ public function unregister()
425423 public function loadClass ($ class )
426424 {
427425 if ($ file = $ this ->findFile ($ class )) {
428- includeFile ($ file );
426+ $ includeFile = self ::$ includeFile ;
427+ $ includeFile ($ file );
429428
430429 return true ;
431430 }
@@ -476,9 +475,9 @@ public function findFile($class)
476475 }
477476
478477 /**
479- * Returns the currently registered loaders indexed by their corresponding vendor directories.
478+ * Returns the currently registered loaders keyed by their corresponding vendor directories.
480479 *
481- * @return self[]
480+ * @return array<string, self>
482481 */
483482 public static function getRegisteredLoaders ()
484483 {
@@ -555,18 +554,26 @@ private function findFileWithExtension($class, $ext)
555554
556555 return false ;
557556 }
558- }
559557
560- /**
561- * Scope isolated include.
562- *
563- * Prevents access to $this/self from included files.
564- *
565- * @param string $file
566- * @return void
567- * @private
568- */
569- function includeFile ($ file )
570- {
571- include $ file ;
558+ /**
559+ * @return void
560+ */
561+ private static function initializeIncludeClosure ()
562+ {
563+ if (self ::$ includeFile !== null ) {
564+ return ;
565+ }
566+
567+ /**
568+ * Scope isolated include.
569+ *
570+ * Prevents access to $this/self from included files.
571+ *
572+ * @param string $file
573+ * @return void
574+ */
575+ self ::$ includeFile = \Closure::bind (static function ($ file ) {
576+ include $ file ;
577+ }, null , null );
578+ }
572579}
0 commit comments