AutoloadGenerator: Filter PSR4-compliant classes instead of ignoring directories
authorBrad Jorsch <bjorsch@wikimedia.org>
Thu, 20 Sep 2018 17:44:35 +0000 (13:44 -0400)
committerBrad Jorsch <bjorsch@wikimedia.org>
Thu, 20 Sep 2018 19:15:27 +0000 (15:15 -0400)
Per discussion in T166010, we're going to handle class aliases (e.g. for
BC) by including the class_alias() call in the same file as the target
class. When the target class is a PSR4-compliant class, we still need to
pick up that alias for inclusion in autoload.php.

Thus, instead of excluding whole directories, we need to process the
files and filter out only those found classes that are PSR4 compliant.

Bug: T204983
Change-Id: I1c516998df368531c90ea54acc5be8be96e1db6c

includes/utils/AutoloadGenerator.php
maintenance/generateLocalAutoload.php
tests/phpunit/structure/AutoLoaderStructureTest.php

index 511b673..2fc7bc0 100644 (file)
@@ -49,6 +49,13 @@ class AutoloadGenerator {
         */
        protected $excludePaths = [];
 
+       /**
+        * Configured PSR4 namespaces
+        *
+        * @var string[] namespace => path
+        */
+       protected $psr4Namespaces = [];
+
        /**
         * @param string $basepath Root path of the project being scanned for classes
         * @param array|string $flags
@@ -79,6 +86,22 @@ class AutoloadGenerator {
                }
        }
 
+       /**
+        * Set PSR4 namespaces
+        *
+        * Unlike self::setExcludePaths(), this will only skip outputting the
+        * autoloader entry when the namespace matches the path.
+        *
+        * @since 1.32
+        * @param string[] $namespaces Associative array mapping namespace to path
+        */
+       public function setPsr4Namespaces( array $namespaces ) {
+               foreach ( $namespaces as $ns => $path ) {
+                       $ns = rtrim( $ns, '\\' ) . '\\';
+                       $this->psr4Namespaces[$ns] = rtrim( self::normalizePathSeparator( $path ), '/' );
+               }
+       }
+
        /**
         * Whether the file should be excluded
         *
@@ -135,6 +158,25 @@ class AutoloadGenerator {
                $result = $this->collector->getClasses(
                        file_get_contents( $inputPath )
                );
+
+               // Filter out classes that will be found by PSR4
+               $result = array_filter( $result, function ( $class ) use ( $inputPath ) {
+                       $parts = explode( '\\', $class );
+                       for ( $i = count( $parts ) - 1; $i > 0; $i-- ) {
+                               $ns = implode( '\\', array_slice( $parts, 0, $i ) ) . '\\';
+                               if ( isset( $this->psr4Namespaces[$ns] ) ) {
+                                       $expectedPath = $this->psr4Namespaces[$ns] . '/'
+                                               . implode( '/', array_slice( $parts, $i ) )
+                                               . '.php';
+                                       if ( $inputPath === $expectedPath ) {
+                                               return false;
+                                       }
+                               }
+                       }
+
+                       return true;
+               } );
+
                if ( $result ) {
                        $shortpath = substr( $inputPath, $len );
                        $this->classes[$shortpath] = $result;
index 189858c..19b7ee5 100644 (file)
@@ -11,7 +11,7 @@ require_once __DIR__ . '/../includes/utils/AutoloadGenerator.php';
 $base = dirname( __DIR__ );
 
 $generator = new AutoloadGenerator( $base, 'local' );
-$generator->setExcludePaths( array_values( AutoLoader::getAutoloadNamespaces() ) );
+$generator->setPsr4Namespaces( AutoLoader::getAutoloadNamespaces() );
 $generator->initMediaWikiDefault();
 
 // Write out the autoload
index 2800d02..8be5760 100644 (file)
@@ -130,6 +130,11 @@ class AutoLoaderStructureTest extends MediaWikiTestCase {
                $expected = $wgAutoloadLocalClasses + $wgAutoloadClasses;
                $actual = [];
 
+               $psr4Namespaces = [];
+               foreach ( AutoLoader::getAutoloadNamespaces() as $ns => $path ) {
+                       $psr4Namespaces[rtrim( $ns, '\\' ) . '\\'] = rtrim( $path, '/' );
+               }
+
                $files = array_unique( $expected );
 
                foreach ( $files as $class => $file ) {
@@ -158,6 +163,21 @@ class AutoLoaderStructureTest extends MediaWikiTestCase {
                        list( $classesInFile, $aliasesInFile ) = self::parseFile( $contents );
 
                        foreach ( $classesInFile as $className => $ignore ) {
+                               // Skip if it's a PSR4 class
+                               $parts = explode( '\\', $className );
+                               for ( $i = count( $parts ) - 1; $i > 0; $i-- ) {
+                                       $ns = implode( '\\', array_slice( $parts, 0, $i ) ) . '\\';
+                                       if ( isset( $psr4Namespaces[$ns] ) ) {
+                                               $expectedPath = $psr4Namespaces[$ns] . '/'
+                                                       . implode( '/', array_slice( $parts, $i ) )
+                                                       . '.php';
+                                               if ( $filePath === $expectedPath ) {
+                                                       continue 2;
+                                               }
+                                       }
+                               }
+
+                               // Nope, add it.
                                $actual[$className] = $file;
                        }
 
@@ -183,7 +203,7 @@ class AutoLoaderStructureTest extends MediaWikiTestCase {
                $path = realpath( __DIR__ . '/../../..' );
                $oldAutoload = file_get_contents( $path . '/autoload.php' );
                $generator = new AutoloadGenerator( $path, 'local' );
-               $generator->setExcludePaths( array_values( AutoLoader::getAutoloadNamespaces() ) );
+               $generator->setPsr4Namespaces( AutoLoader::getAutoloadNamespaces() );
                $generator->initMediaWikiDefault();
                $newAutoload = $generator->getAutoload( 'maintenance/generateLocalAutoload.php' );