1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160
|
<?php
/**
* @coversNothing
*/
class AutoLoaderStructureTest extends MediaWikiIntegrationTestCase {
/**
* Assert that there were no classes loaded that are not registered with the AutoLoader.
*
* For example foo.php having class Foo and class Bar but only registering Foo.
* This is important because we should not be relying on Foo being used before Bar.
*/
public function testAutoLoadConfig() {
$results = self::checkAutoLoadConf();
$this->assertEquals(
$results['expected'],
$results['actual']
);
}
private static function parseFile( $contents ) {
// We could use token_get_all() here, but this is faster
// Note: Keep in sync with ClassCollector
$matches = [];
preg_match_all( '/
^ [\t ]* (?:
(?:final\s+)? (?:abstract\s+)? (?:class|interface|trait|enum) \s+
(?P<class> \w+)
|
class_alias \s* \( \s*
([\'"]) (?P<original> [^\'"]+) \g{-2} \s* , \s*
([\'"]) (?P<alias> [^\'"]+ ) \g{-2} \s*
\) \s* ;
|
class_alias \s* \( \s*
(?P<originalStatic> [\w\\\\]+)::class \s* , \s*
([\'"]) (?P<aliasString> [^\'"]+ ) \g{-2} \s*
\) \s* ;
)
/imx', $contents, $matches, PREG_SET_ORDER );
$namespaceMatch = [];
preg_match( '/
^ [\t ]*
namespace \s+
(\w+(\\\\\w+)*)
\s* ;
/imx', $contents, $namespaceMatch );
$fileNamespace = $namespaceMatch ? $namespaceMatch[1] . '\\' : '';
$classesInFile = [];
$aliasesInFile = [];
foreach ( $matches as $match ) {
if ( !empty( $match['class'] ) ) {
// 'class Foo {}'
$class = $fileNamespace . $match['class'];
$classesInFile[$class] = true;
} elseif ( !empty( $match['original'] ) ) {
// 'class_alias( "Foo", "Bar" );'
$aliasesInFile[self::removeSlashes( $match['alias'] )] = $match['original'];
} else {
// 'class_alias( Foo::class, "Bar" );'
$aliasesInFile[self::removeSlashes( $match['aliasString'] )] =
$fileNamespace . $match['originalStatic'];
}
}
return [ $classesInFile, $aliasesInFile ];
}
private static function removeSlashes( $str ) {
return str_replace( '\\\\', '\\', $str );
}
private static function fixSlashes( $str ) {
return str_replace( '\\', '/', $str );
}
protected static function checkAutoLoadConf() {
$IP = MW_INSTALL_PATH;
// wgAutoloadLocalClasses has precedence, just like in includes/AutoLoader.php
$expected = AutoLoader::getClassFiles();
$actual = [];
foreach ( $expected as $class => $file ) {
// Only prefix $IP if it doesn't have it already.
// Generally local classes don't have it, and those from extensions and test suites do.
if ( $file[0] !== '/' && $file[1] !== ':' ) {
$filePath = self::fixSlashes( "$IP/$file" );
} else {
$filePath = self::fixSlashes( $file );
}
if ( !is_file( $filePath ) ) {
$actual[$class] = "[file '$filePath' does not exist]";
continue;
}
$contents = @file_get_contents( $filePath );
if ( $contents === false ) {
$actual[$class] = "[couldn't read file '$filePath']";
continue;
}
[ $classesInFile, $aliasesInFile ] = self::parseFile( $contents );
foreach ( $classesInFile as $className => $ignore ) {
$actual[$className] = $file;
}
// Only accept aliases for classes in the same file, because for correct
// behavior, all aliases for a class must be set up when the class is loaded
// (see <https://bugs.php.net/bug.php?id=61422>).
foreach ( $aliasesInFile as $alias => $class ) {
if ( isset( $classesInFile[$class] ) ) {
$actual[$alias] = $file;
} else {
$actual[$alias] = "[original class not in $file]";
}
}
}
return [
'expected' => $expected,
'actual' => $actual,
];
}
public function testAutoloadOrder() {
$path = __DIR__ . '/../../..';
$oldAutoload = file_get_contents( $path . '/autoload.php' );
$generator = new AutoloadGenerator( $path, 'local' );
$generator->setPsr4Namespaces( AutoLoader::CORE_NAMESPACES );
$generator->initMediaWikiDefault();
$newAutoload = $generator->getAutoload( 'maintenance/generateLocalAutoload.php' );
$this->assertEquals( $oldAutoload, $newAutoload, 'autoload.php does not match' .
' output of generateLocalAutoload.php script.' );
}
/**
* Verify that all the directories specified for PSR-4 autoloading
* actually exist, to prevent situations like T259448
*/
public function testAutoloadNamespaces() {
$missing = [];
$psr4Namespaces = AutoLoader::getNamespaceDirectories();
foreach ( $psr4Namespaces as $ns => $path ) {
if ( !is_dir( $path ) ) {
$missing[] = "Directory $path for namespace $ns does not exist";
}
}
$this->assertSame( [], $missing );
}
}
|