jenkins-bot has submitted this change and it was merged. ( https://gerrit.wikimedia.org/r/338509 )
Change subject: AutoloadGenerator: Add support for class_alias() ...................................................................... AutoloadGenerator: Add support for class_alias() Blob, Field, DatabaseBase are now auto-detected. Change-Id: Ib8fae2ec3fbb3f5e4aca7965f81631c5f0485ea1 --- M includes/libs/rdbms/database/Database.php M includes/libs/rdbms/encasing/Blob.php M includes/libs/rdbms/field/Field.php M includes/utils/AutoloadGenerator.php A tests/phpunit/includes/utils/ClassCollectorTest.php M tests/phpunit/structure/AutoLoaderTest.php 6 files changed, 122 insertions(+), 13 deletions(-) Approvals: Aaron Schulz: Looks good to me, approved jenkins-bot: Verified diff --git a/includes/libs/rdbms/database/Database.php b/includes/libs/rdbms/database/Database.php index 9d800a2..1c5c77e 100644 --- a/includes/libs/rdbms/database/Database.php +++ b/includes/libs/rdbms/database/Database.php @@ -3467,4 +3467,4 @@ } } -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 d394692..db5b7e5 100644 --- a/includes/libs/rdbms/encasing/Blob.php +++ b/includes/libs/rdbms/encasing/Blob.php @@ -18,4 +18,4 @@ } } -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 7a25f03..7918f36 100644 --- a/includes/libs/rdbms/field/Field.php +++ b/includes/libs/rdbms/field/Field.php @@ -32,4 +32,4 @@ 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 0bfd4a27..55e228a 100644 --- a/includes/utils/AutoloadGenerator.php +++ b/includes/utils/AutoloadGenerator.php @@ -291,15 +291,6 @@ 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' ); } } @@ -329,6 +320,11 @@ protected $tokens; /** + * @var array Class alias with target/name fields + */ + protected $alias; + + /** * @var string $code PHP code (including <?php) to detect class names from * @return array List of FQCN detected within the tokens */ @@ -336,6 +332,7 @@ $this->namespace = ''; $this->classes = []; $this->startToken = null; + $this->alias = null; $this->tokens = []; foreach ( token_get_all( $code ) as $token ) { @@ -358,6 +355,8 @@ 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 @@ 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 @@ } 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 0000000..e8a228e --- /dev/null +++ b/tests/phpunit/includes/utils/ClassCollectorTest.php @@ -0,0 +1,48 @@ +<?php + +/** + * @covers ClassCollector + */ +class ClassCollectorTest extends PHPUnit_Framework_TestCase { + + public static function provideCases() { + return [ + [ + "class Foo {}", + [ 'Foo' ], + ], + [ + "namespace Example;\nclass Foo {}\nclass Bar {}", + [ 'Example\Foo', 'Example\Bar' ], + ], + [ + "class_alias( 'Foo', 'Bar' );", + [ 'Bar' ], + ], + [ + "namespace Example;\nclass Foo {}\nclass_alias( 'Example\Foo', 'Foo' );", + [ 'Example\Foo', 'Foo' ], + ], + [ + "namespace Example;\nclass Foo {}\nclass_alias( 'Example\Foo', 'Bar' );", + [ 'Example\Foo', 'Bar' ], + ], + [ + "class_alias( Foo::class, 'Bar' );", + [ 'Bar' ], + ], + [ + "namespace Example;\nclass Foo {}\nclass_alias( Foo::class, 'Bar' );", + [ 'Example\Foo', 'Bar' ], + ], + ]; + } + + /** + * @dataProvider provideCases + */ + public function testGetClasses( $code, array $classes, $message = null ) { + $cc = new ClassCollector(); + $this->assertEquals( $classes, $cc->getClasses( "<?php\n$code" ), $message ); + } +} diff --git a/tests/phpunit/structure/AutoLoaderTest.php b/tests/phpunit/structure/AutoLoaderTest.php index f36b51a..d81e8c6 100644 --- a/tests/phpunit/structure/AutoLoaderTest.php +++ b/tests/phpunit/structure/AutoLoaderTest.php @@ -68,6 +68,7 @@ } // We could use token_get_all() here, but this is faster + // Note: Keep in sync with ClassCollector $matches = []; preg_match_all( '/ ^ [\t ]* (?: @@ -77,6 +78,11 @@ class_alias \s* \( \s* ([\'"]) (?P<original> [^\'"]+) \g{-2} \s* , \s* ([\'"]) (?P<alias> [^\'"]+ ) \g{-2} \s* + \) \s* ; + | + class_alias \s* \( \s* + (?P<originalStatic> [a-zA-Z0-9_]+)::class \s* , \s* + ([\'"]) (?P<aliasString> [^\'"]+ ) \g{-2} \s* \) \s* ; ) /imx', $contents, $matches, PREG_SET_ORDER ); @@ -95,11 +101,18 @@ 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']; + } } } -- To view, visit https://gerrit.wikimedia.org/r/338509 To unsubscribe, visit https://gerrit.wikimedia.org/r/settings Gerrit-MessageType: merged Gerrit-Change-Id: Ib8fae2ec3fbb3f5e4aca7965f81631c5f0485ea1 Gerrit-PatchSet: 11 Gerrit-Project: mediawiki/core Gerrit-Branch: master Gerrit-Owner: Krinkle <krinklem...@gmail.com> Gerrit-Reviewer: Aaron Schulz <asch...@wikimedia.org> Gerrit-Reviewer: Anomie <bjor...@wikimedia.org> Gerrit-Reviewer: Florianschmidtwelzow <florian.schmidt.stargatewis...@gmail.com> Gerrit-Reviewer: Jforrester <jforres...@wikimedia.org> Gerrit-Reviewer: Krinkle <krinklem...@gmail.com> Gerrit-Reviewer: Legoktm <lego...@member.fsf.org> Gerrit-Reviewer: Reedy <re...@wikimedia.org> Gerrit-Reviewer: Thiemo Mättig (WMDE) <thiemo.maet...@wikimedia.de> Gerrit-Reviewer: jenkins-bot <> _______________________________________________ MediaWiki-commits mailing list MediaWiki-commits@lists.wikimedia.org https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits