From c61fdb4ef5dbe0c26a17e57203e0bf99aa0e2440 Mon Sep 17 00:00:00 2001 From: Kevin Israel Date: Thu, 21 Nov 2013 23:13:03 -0500 Subject: [PATCH] Mechanism for renaming/aliasing classes Sometimes it is desirable to change a class name that is still referenced in extensions or config files (e.g. for consistency, as in I507ba00a). PHP's class_alias() function can help in preserving backward compatibility; however, creating an alias first requires that the class be loaded. Hence this is implemented in AutoLoader. Lazy loading continues to work, the list of class names is still maintained in a central location, and optionally, deprecation warnings can be generated. Change-Id: I1d3fb04a448647b5be10bed7fec05238b9fc6fc7 --- includes/AutoLoader.php | 26 +++++++++++++++ .../TestAutoloadedAliasedClassNew.php | 4 +++ tests/phpunit/structure/AutoLoaderTest.php | 33 ++++++++++++++++++- 3 files changed, 62 insertions(+), 1 deletion(-) create mode 100644 tests/phpunit/data/autoloader/TestAutoloadedAliasedClassNew.php diff --git a/includes/AutoLoader.php b/includes/AutoLoader.php index cd062e01cd..e6ce693ca1 100644 --- a/includes/AutoLoader.php +++ b/includes/AutoLoader.php @@ -1209,6 +1209,32 @@ class AutoLoader { return; } + if ( substr( $filename, 0, 6 ) === 'alias:' ) { + // Supported alias formats: + // - No deprecation warning: alias:MyNewClassName + // - Deprecated in MediaWiki 1.1: alias:MyNewClassName?v=1.1 + // - Deprecated in MyExtension 1.1: alias:MyNewClassName?c=MyExtension&v=1.1 + $parts = explode( '?', substr( $filename, 6 ), 2 ); + $newClassName = $parts[0]; + + // If necessary, this will make a recursive call to this function to + // load the class using its actual, canonical name. + class_alias( $newClassName, $className ); + + if ( isset( $parts[1] ) && function_exists( 'wfDeprecated' ) ) { + $info = wfCgiToArray( $parts[1] ); + $function = "name $className for class $newClassName"; + $version = isset( $info['v'] ) ? $info['v'] : false; + $component = isset( $info['c'] ) ? $info['c'] : false; + + // https://github.com/facebook/hhvm/issues/1018 + $callerOffset = wfIsHHVM() ? 2 : 3; + wfDeprecated( $function, $version, $component, $callerOffset ); + } + + return; + } + # Make an absolute path, this improves performance by avoiding some stat calls if ( substr( $filename, 0, 1 ) != '/' && substr( $filename, 1, 1 ) != ':' ) { global $IP; diff --git a/tests/phpunit/data/autoloader/TestAutoloadedAliasedClassNew.php b/tests/phpunit/data/autoloader/TestAutoloadedAliasedClassNew.php new file mode 100644 index 0000000000..5ce8483c89 --- /dev/null +++ b/tests/phpunit/data/autoloader/TestAutoloadedAliasedClassNew.php @@ -0,0 +1,4 @@ + __DIR__ . '/../data/autoloader/TestAutoloadedLocalClass.php', 'TestAutoloadedCamlClass' => __DIR__ . '/../data/autoloader/TestAutoloadedCamlClass.php', 'TestAutoloadedSerializedClass' => __DIR__ . '/../data/autoloader/TestAutoloadedSerializedClass.php', + 'TestAutoloadedAliasedClass' => 'alias:TestAutoloadedAliasedClassNew', + 'TestAutoloadedAliasedClassDeprecated' => 'alias:TestAutoloadedAliasedClassNew?v=1.1', + 'TestAutoloadedAliasedClassNew' => __DIR__ . '/../data/autoloader/TestAutoloadedAliasedClassNew.php', ); $this->setMwGlobals( 'wgAutoloadLocalClasses', $this->testLocalClasses + $wgAutoloadLocalClasses ); AutoLoader::resetAutoloadLocalClassesLower(); @@ -44,7 +47,23 @@ class AutoLoaderTest extends MediaWikiTestCase { $expected = $wgAutoloadLocalClasses + $wgAutoloadClasses; $actual = array(); - $files = array_unique( $expected ); + // Check aliases + foreach ( $expected as $class => $file ) { + if ( substr( $file, 0, 6 ) !== 'alias:' ) { + // Not an alias, so should be an actual file + $files[] = $file; + } else { + $newClass = substr( $file, 6, strcspn( $file, '?', 6 ) ); + if ( isset( $expected[$newClass] ) ) { + if ( substr( $expected[$newClass], 0, 6 ) !== 'alias:' ) { + // Alias pointing to an existing MediaWiki class + $actual[$class] = $file; + } + } + } + } + + $files = array_unique( $files ); foreach ( $files as $file ) { // Only prefix $IP if it doesn't have it already. @@ -92,4 +111,16 @@ class AutoLoaderTest extends MediaWikiTestCase { $this->assertFalse( $uncerealized instanceof __PHP_Incomplete_Class, "unserialize() can load classes case-insensitively." ); } + + function testAliasedClass() { + $this->assertSame( 'TestAutoloadedAliasedClassNew', + get_class( new TestAutoloadedAliasedClass ) ); + } + + function testAliasedClassDeprecated() { + wfSuppressWarnings(); + $this->assertSame( 'TestAutoloadedAliasedClassNew', + get_class( new TestAutoloadedAliasedClassDeprecated ) ); + wfRestoreWarnings(); + } } -- 2.20.1