From 516f2700e033aff68600fa09ac3cef0f1c09ab85 Mon Sep 17 00:00:00 2001 From: Timo Tijhof Date: Fri, 17 Feb 2017 19:11:33 -0800 Subject: [PATCH] AutoloadGenerator: Add support for class_alias() Blob, Field, DatabaseBase are now auto-detected. Change-Id: Ib8fae2ec3fbb3f5e4aca7965f81631c5f0485ea1 --- includes/libs/rdbms/database/Database.php | 2 +- includes/libs/rdbms/encasing/Blob.php | 2 +- includes/libs/rdbms/field/Field.php | 2 +- includes/utils/AutoloadGenerator.php | 66 ++++++++++++++++--- .../includes/utils/ClassCollectorTest.php | 48 ++++++++++++++ tests/phpunit/structure/AutoLoaderTest.php | 15 ++++- 6 files changed, 122 insertions(+), 13 deletions(-) create mode 100644 tests/phpunit/includes/utils/ClassCollectorTest.php diff --git a/includes/libs/rdbms/database/Database.php b/includes/libs/rdbms/database/Database.php index 9d800a29c8..1c5c77e41c 100644 --- a/includes/libs/rdbms/database/Database.php +++ b/includes/libs/rdbms/database/Database.php @@ -3467,4 +3467,4 @@ abstract class Database implements IDatabase, IMaintainableDatabase, LoggerAware } } -class_alias( 'Database', 'DatabaseBase' ); +class_alias( Database::class, 'DatabaseBase' ); diff --git a/includes/libs/rdbms/encasing/Blob.php b/includes/libs/rdbms/encasing/Blob.php index d394692323..db5b7e5113 100644 --- a/includes/libs/rdbms/encasing/Blob.php +++ b/includes/libs/rdbms/encasing/Blob.php @@ -18,4 +18,4 @@ class Blob implements IBlob { } } -class_alias( 'Wikimedia\Rdbms\Blob', 'Blob' ); +class_alias( Blob::class, 'Blob' ); diff --git a/includes/libs/rdbms/field/Field.php b/includes/libs/rdbms/field/Field.php index 7a25f03b5f..7918f36067 100644 --- a/includes/libs/rdbms/field/Field.php +++ b/includes/libs/rdbms/field/Field.php @@ -32,4 +32,4 @@ interface Field { function isNullable(); } -class_alias( 'Wikimedia\Rdbms\Field', 'Field' ); +class_alias( Field::class, 'Field' ); diff --git a/includes/utils/AutoloadGenerator.php b/includes/utils/AutoloadGenerator.php index 0bfd4a27fa..55e228afcd 100644 --- a/includes/utils/AutoloadGenerator.php +++ b/includes/utils/AutoloadGenerator.php @@ -291,15 +291,6 @@ EOD; foreach ( glob( $this->basepath . '/*.php' ) as $file ) { $this->readFile( $file ); } - - // Legacy aliases (1.28) - $this->forceClassPath( 'DatabaseBase', - $this->basepath . '/includes/libs/rdbms/database/Database.php' ); - // Legacy aliases (1.29) - $this->forceClassPath( 'Blob', - $this->basepath . '/includes/libs/rdbms/encasing/Blob.php' ); - $this->forceClassPath( 'Field', - $this->basepath . '/includes/libs/rdbms/field/Field.php' ); } } @@ -328,6 +319,11 @@ class ClassCollector { */ protected $tokens; + /** + * @var array Class alias with target/name fields + */ + protected $alias; + /** * @var string $code PHP code (including namespace = ''; $this->classes = []; $this->startToken = null; + $this->alias = null; $this->tokens = []; foreach ( token_get_all( $code ) as $token ) { @@ -358,6 +355,8 @@ class ClassCollector { if ( is_string( $token ) ) { return; } + // Note: When changing class name discovery logic, + // AutoLoaderTest.php may also need to be updated. switch ( $token[0] ) { case T_NAMESPACE: case T_CLASS: @@ -365,6 +364,12 @@ class ClassCollector { case T_TRAIT: case T_DOUBLE_COLON: $this->startToken = $token; + break; + case T_STRING: + if ( $token[1] === 'class_alias' ) { + $this->startToken = $token; + $this->alias = []; + } } } @@ -388,6 +393,49 @@ class ClassCollector { } break; + case T_STRING: + if ( $this->alias !== null ) { + // Flow 1 - Two string literals: + // - T_STRING class_alias + // - '(' + // - T_CONSTANT_ENCAPSED_STRING 'TargetClass' + // - ',' + // - T_WHITESPACE + // - T_CONSTANT_ENCAPSED_STRING 'AliasName' + // - ')' + // Flow 2 - Use of ::class syntax for first parameter + // - T_STRING class_alias + // - '(' + // - T_STRING TargetClass + // - T_DOUBLE_COLON :: + // - T_CLASS class + // - ',' + // - T_WHITESPACE + // - T_CONSTANT_ENCAPSED_STRING 'AliasName' + // - ')' + if ( $token === '(' ) { + // Start of a function call to class_alias() + $this->alias = [ 'target' => false, 'name' => false ]; + } elseif ( $token === ',' ) { + // Record that we're past the first parameter + if ( $this->alias['target'] === false ) { + $this->alias['target'] = true; + } + } elseif ( is_array( $token ) && $token[0] === T_CONSTANT_ENCAPSED_STRING ) { + if ( $this->alias['target'] === true ) { + // We already saw a first argument, this must be the second. + // Strip quotes from the string literal. + $this->alias['name'] = substr( $token[1], 1, -1 ); + } + } elseif ( $token === ')' ) { + // End of function call + $this->classes[] = $this->alias['name']; + $this->alias = null; + $this->startToken = null; + } + } + break; + case T_CLASS: case T_INTERFACE: case T_TRAIT: diff --git a/tests/phpunit/includes/utils/ClassCollectorTest.php b/tests/phpunit/includes/utils/ClassCollectorTest.php new file mode 100644 index 0000000000..e8a228e314 --- /dev/null +++ b/tests/phpunit/includes/utils/ClassCollectorTest.php @@ -0,0 +1,48 @@ +assertEquals( $classes, $cc->getClasses( " [^\'"]+) \g{-2} \s* , \s* ([\'"]) (?P [^\'"]+ ) \g{-2} \s* \) \s* ; + | + class_alias \s* \( \s* + (?P [a-zA-Z0-9_]+)::class \s* , \s* + ([\'"]) (?P [^\'"]+ ) \g{-2} \s* + \) \s* ; ) /imx', $contents, $matches, PREG_SET_ORDER ); @@ -95,11 +101,18 @@ class AutoLoaderTest extends MediaWikiTestCase { foreach ( $matches as $match ) { if ( !empty( $match['class'] ) ) { + // 'class Foo {}' $class = $fileNamespace . $match['class']; $actual[$class] = $file; $classesInFile[$class] = true; } else { - $aliasesInFile[$match['alias']] = $match['original']; + if ( !empty( $match['original'] ) ) { + // 'class_alias( "Foo", "Bar" );' + $aliasesInFile[$match['alias']] = $match['original']; + } else { + // 'class_alias( Foo::class, "Bar" );' + $aliasesInFile[$match['aliasString']] = $fileNamespace . $match['originalStatic']; + } } } -- 2.20.1