Emit deprecation warning for deprecated overrides.

This introduces a utility method in MWDebug, and uses it to clean up
the deprecation mechanism for Skin::setupSkinUserCss().

Bug: T257990
Bug: T267080
Change-Id: I5b07fa82dcc2b0fd112f122e45c9b2562cd09247
This commit is contained in:
daniel 2020-12-02 22:49:22 +01:00
parent b2590ad102
commit 8a7d487545
3 changed files with 92 additions and 14 deletions

View file

@ -206,14 +206,10 @@ class MWDebug {
/**
* Show a warning that $function is deprecated.
* This will send it to the following locations:
* - Debug toolbar, with one item per function and caller, if $wgDebugToolbar
* is set to true.
* - PHP's error log, with level E_USER_DEPRECATED, if $wgDevelopmentWarnings
* is set to true.
* - MediaWiki's debug log, if $wgDevelopmentWarnings is set to false.
*
* @see deprecatedMsg()
* @since 1.19
*
* @param string $function Function that is deprecated.
* @param string|false $version Version in which the function was deprecated.
* @param string|bool $component Component to which the function belongs.
@ -234,6 +230,49 @@ class MWDebug {
self::deprecatedMsg( $msg, $version, $component, $callerOffset + 1 );
}
/**
* Show a warning if $method declared in $class is overridden in $instance.
*
* @since 1.36
* @see deprecatedMsg()
*
* phpcs:ignore MediaWiki.Commenting.FunctionComment.ObjectTypeHintParam
* @param object $instance Object on which to detect deprecated overrides (typically $this).
* @param string $class Class declaring the deprecated method (typically __CLASS__ )
* @param string $method The name of the deprecated method.
* @param string|false $version Version in which the method was deprecated.
* @param string|bool $component Component to which the class belongs.
* If false, it is assumed the class is in MediaWiki core.
* @param int $callerOffset How far up the callstack is the original
* caller. 2 = function that called the function that called
* MWDebug::detectDeprecatedOverride()
*
* @return bool True if the method was overridden, false otherwise. If the method
* was overridden, it should be called. The deprecated method's default
* implementation should call MWDebug::deprecated().
*/
public static function detectDeprecatedOverride( $instance, $class, $method, $version = false,
$component = false, $callerOffset = 2
) {
$reflectionMethod = new ReflectionMethod( $instance, $method );
$declaringClass = $reflectionMethod->getDeclaringClass()->getName();
if ( $declaringClass === $class ) {
// not overridden, nothing to do
return false;
}
if ( $version ) {
$component = $component ?: 'MediaWiki';
$msg = "$declaringClass overrides $method which was deprecated in $component $version.";
} else {
$msg = "$declaringClass overrides $method which is deprecated.";
}
self::deprecatedMsg( $msg, $version, $component, $callerOffset + 1 );
return true;
}
/**
* Log a deprecation warning with arbitrary message text. A caller
* description will be appended. If the message has already been sent for
@ -243,6 +282,14 @@ class MWDebug {
* automatically appended to the message. The message text should include
* information about when the thing was deprecated.
*
* The warning will be sent to the following locations:
* - Debug toolbar, with one item per function and caller, if $wgDebugToolbar
* is set to true.
* - PHP's error log, with level E_USER_DEPRECATED, if $wgDevelopmentWarnings
* is set to true. This is the case in phpunit tests per default, and will
* cause tests to fail.
* - MediaWiki's debug log, if $wgDevelopmentWarnings is set to false.
*
* @since 1.35
*
* @param string $msg The message

View file

@ -456,13 +456,8 @@ abstract class Skin extends ContextSource {
* setupSkinUserCss is required.
*/
final public function doSetupSkinUserCss( OutputPage $out ) {
$stylesBefore = $out->getModuleStyles();
$this->setupSkinUserCss( $out );
$stylesAfter = $out->getModuleStyles();
if ( count( $stylesAfter ) !== count( $stylesBefore ) ) {
// Styles were added by the setupSkinUserCss method. This is no longer allowed.
wfDeprecatedMsg( get_class( $this ) . '::setupSkinUserCss must not modify ' .
'the style module list, this is deprecated since 1.32', '1.32' );
if ( MWDebug::detectDeprecatedOverride( $this, __CLASS__, 'setupSkinUserCss', '1.32' ) ) {
$this->setupSkinUserCss( $out );
}
}
@ -475,7 +470,7 @@ abstract class Skin extends ContextSource {
* @param OutputPage $out Legacy parameter, identical to $this->getOutput()
*/
public function setupSkinUserCss( OutputPage $out ) {
// Stub.
wfDeprecated( __METHOD__, '1.32' );
}
/**

View file

@ -50,6 +50,42 @@ class MWDebugTest extends MediaWikiIntegrationTestCase {
);
}
/**
* @covers MWDebug::detectDeprecatedOverride
*/
public function testDetectDeprecatedOverride() {
$baseclassInstance = new TitleValue( NS_MAIN, 'Test' );
$this->assertFalse(
MWDebug::detectDeprecatedOverride(
$baseclassInstance,
TitleValue::class,
'getNamespace',
MW_VERSION
)
);
// create a dummy subclass that overrides a method
$subclassInstance = new class ( NS_MAIN, 'Test' ) extends TitleValue {
public function getNamespace() {
// never called
return -100;
}
};
$this->assertTrue(
MWDebug::detectDeprecatedOverride(
$subclassInstance,
TitleValue::class,
'getNamespace',
MW_VERSION
)
);
// A warning should have been logged
$this->assertCount( 1, MWDebug::getLog() );
}
/**
* @covers MWDebug::deprecated
*/