Blob, Field, DatabaseBase are now auto-detected.
Change-Id: Ib8fae2ec3fbb3f5e4aca7965f81631c5f0485ea1
}
}
-class_alias( 'Database', 'DatabaseBase' );
+class_alias( Database::class, 'DatabaseBase' );
}
}
-class_alias( 'Wikimedia\Rdbms\Blob', 'Blob' );
+class_alias( Blob::class, 'Blob' );
function isNullable();
}
-class_alias( 'Wikimedia\Rdbms\Field', 'Field' );
+class_alias( Field::class, 'Field' );
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' );
}
}
*/
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
$this->namespace = '';
$this->classes = [];
$this->startToken = null;
+ $this->alias = null;
$this->tokens = [];
foreach ( token_get_all( $code ) as $token ) {
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:
case T_TRAIT:
case T_DOUBLE_COLON:
$this->startToken = $token;
+ break;
+ case T_STRING:
+ if ( $token[1] === 'class_alias' ) {
+ $this->startToken = $token;
+ $this->alias = [];
+ }
}
}
}
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:
--- /dev/null
+<?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 );
+ }
+}
}
// We could use token_get_all() here, but this is faster
+ // Note: Keep in sync with ClassCollector
$matches = [];
preg_match_all( '/
^ [\t ]* (?:
([\'"]) (?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 );
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'];
+ }
}
}