@@ -53,8 +53,9 @@ class ClassLoader
5353
5454 private $ useIncludePath = false ;
5555 private $ classMap = array ();
56-
5756 private $ classMapAuthoritative = false ;
57+ private $ missingClasses = array ();
58+ private $ apcuPrefix ;
5859
5960 public function getPrefixes ()
6061 {
@@ -271,6 +272,26 @@ public function isClassMapAuthoritative()
271272 return $ this ->classMapAuthoritative ;
272273 }
273274
275+ /**
276+ * APCu prefix to use to cache found/not-found classes, if the extension is enabled.
277+ *
278+ * @param string|null $apcuPrefix
279+ */
280+ public function setApcuPrefix ($ apcuPrefix )
281+ {
282+ $ this ->apcuPrefix = function_exists ('apcu_fetch ' ) && filter_var (ini_get ('apc.enabled ' ), FILTER_VALIDATE_BOOLEAN ) ? $ apcuPrefix : null ;
283+ }
284+
285+ /**
286+ * The APCu prefix in use, or null if APCu caching is not enabled.
287+ *
288+ * @return string|null
289+ */
290+ public function getApcuPrefix ()
291+ {
292+ return $ this ->apcuPrefix ;
293+ }
294+
274295 /**
275296 * Registers this instance as an autoloader.
276297 *
@@ -313,29 +334,34 @@ public function loadClass($class)
313334 */
314335 public function findFile ($ class )
315336 {
316- // work around for PHP 5.3.0 - 5.3.2 https://bugs.php.net/50731
317- if ('\\' == $ class [0 ]) {
318- $ class = substr ($ class , 1 );
319- }
320-
321337 // class map lookup
322338 if (isset ($ this ->classMap [$ class ])) {
323339 return $ this ->classMap [$ class ];
324340 }
325- if ($ this ->classMapAuthoritative ) {
341+ if ($ this ->classMapAuthoritative || isset ( $ this -> missingClasses [ $ class ]) ) {
326342 return false ;
327343 }
344+ if (null !== $ this ->apcuPrefix ) {
345+ $ file = apcu_fetch ($ this ->apcuPrefix .$ class , $ hit );
346+ if ($ hit ) {
347+ return $ file ;
348+ }
349+ }
328350
329351 $ file = $ this ->findFileWithExtension ($ class , '.php ' );
330352
331353 // Search for Hack files if we are running on HHVM
332- if ($ file === null && defined ('HHVM_VERSION ' )) {
354+ if (false === $ file && defined ('HHVM_VERSION ' )) {
333355 $ file = $ this ->findFileWithExtension ($ class , '.hh ' );
334356 }
335357
336- if ($ file === null ) {
358+ if (null !== $ this ->apcuPrefix ) {
359+ apcu_add ($ this ->apcuPrefix .$ class , $ file );
360+ }
361+
362+ if (false === $ file ) {
337363 // Remember that this class does not exist.
338- return $ this ->classMap [$ class ] = false ;
364+ $ this ->missingClasses [$ class ] = true ;
339365 }
340366
341367 return $ file ;
@@ -348,10 +374,14 @@ private function findFileWithExtension($class, $ext)
348374
349375 $ first = $ class [0 ];
350376 if (isset ($ this ->prefixLengthsPsr4 [$ first ])) {
351- foreach ($ this ->prefixLengthsPsr4 [$ first ] as $ prefix => $ length ) {
352- if (0 === strpos ($ class , $ prefix )) {
353- foreach ($ this ->prefixDirsPsr4 [$ prefix ] as $ dir ) {
354- if (file_exists ($ file = $ dir . DIRECTORY_SEPARATOR . substr ($ logicalPathPsr4 , $ length ))) {
377+ $ subPath = $ class ;
378+ while (false !== $ lastPos = strrpos ($ subPath , '\\' )) {
379+ $ subPath = substr ($ subPath , 0 , $ lastPos );
380+ $ search = $ subPath . '\\' ;
381+ if (isset ($ this ->prefixDirsPsr4 [$ search ])) {
382+ $ pathEnd = DIRECTORY_SEPARATOR . substr ($ logicalPathPsr4 , $ lastPos + 1 );
383+ foreach ($ this ->prefixDirsPsr4 [$ search ] as $ dir ) {
384+ if (file_exists ($ file = $ dir . $ pathEnd )) {
355385 return $ file ;
356386 }
357387 }
@@ -399,6 +429,8 @@ private function findFileWithExtension($class, $ext)
399429 if ($ this ->useIncludePath && $ file = stream_resolve_include_path ($ logicalPathPsr0 )) {
400430 return $ file ;
401431 }
432+
433+ return false ;
402434 }
403435}
404436
0 commit comments