Define equality for PageIdentity and LinkTarget

Bug: T272420
Change-Id: I2e0eb21989e2d733088ab77d7461bf003c2905eb
This commit is contained in:
daniel 2020-09-25 16:13:54 +02:00 committed by Ppchelko
parent 5db4b26fb4
commit 10ccdb2281
8 changed files with 351 additions and 27 deletions

View file

@ -3930,16 +3930,71 @@ class Title implements LinkTarget, PageIdentity, IDBAccessObject {
}
/**
* Compare with another title.
* Compares with another Title.
*
* @param LinkTarget $title
* A Title object is considered equal to another Title if it has the same text,
* the same interwiki prefix, and the same namespace.
*
* @note This is different from isSameLinkAs(), which also compares the fragment part,
* and from isSamePageAs(), which takes into account the page ID.
*
* @phpcs:disable MediaWiki.Commenting.FunctionComment.ObjectTypeHintParam
* @param object $other
*
* @return bool true if $other is a Title and refers to the same page.
*/
public function equals( object $other ) {
if ( $other instanceof Title ) {
// NOTE: In contrast to isSameLinkAs(), this ignores the fragment part!
// NOTE: In contrast to isSamePageAs(), this ignores the page ID!
// NOTE: === is necessary for proper matching of number-like titles
return $this->getInterwiki() === $other->getInterwiki()
&& $this->getNamespace() === $other->getNamespace()
&& $this->getDBkey() === $other->getDBkey();
} else {
return false;
}
}
/**
* @see LinkTarget::isSameLinkAs()
* @since 1.36
*
* @param LinkTarget $other
* @return bool
*/
public function equals( LinkTarget $title ) {
// Note: === is necessary for proper matching of number-like titles.
return $this->mInterwiki === $title->getInterwiki()
&& $this->mNamespace == $title->getNamespace()
&& $this->mDbkeyform === $title->getDBkey();
public function isSameLinkAs( LinkTarget $other ) {
// NOTE: keep in sync with TitleValue::isSameLinkAs()!
// NOTE: === is needed for number-like titles
return ( $other->getInterwiki() === $this->getInterwiki() )
&& ( $other->getDBkey() === $this->getDBkey() )
&& ( $other->getNamespace() === $this->getNamespace() )
&& ( $other->getFragment() === $this->getFragment() );
}
/**
* @see PageIdentity::isSamePageAs()
* @since 1.36
*
* @param PageIdentity $other
* @return bool
*/
public function isSamePageAs( PageIdentity $other ) {
// NOTE: keep in sync with PageIdentityValue::isSamePageAs()!
if ( $other->getWikiId() !== $this->getWikiId()
|| $other->getId() !== $this->getId() ) {
return false;
}
if ( $this->getId() === 0 ) {
if ( $other->getNamespace() !== $this->getNamespace()
|| $other->getDBkey() !== $this->getDBkey() ) {
return false;
}
}
return true;
}
/**

View file

@ -104,10 +104,21 @@ interface LinkTarget {
public function getInterwiki();
/**
* Returns an informative human readable representation of the link target,
* for use in logging and debugging. There is no requirement for the return
* value to have any relationship with the input of TitleParser.
* @since 1.31
* Checks whether the given LinkTarget refers to the same target as this LinkTarget.
*
* Two link targets are considered the same if they have the same interwiki prefix,
* the same namespace ID, the same text, and the same fragment.
*
* @since 1.36
*
* @param LinkTarget $other
* @return bool
*/
public function isSameLinkAs( LinkTarget $other );
/**
* Returns an informative human readable representation of the page identity,
* for use in logging and debugging.
*
* @return string
*/

View file

@ -183,6 +183,20 @@ interface PageIdentity {
*/
public function getDBkey(): string;
/**
* Checks whether the given PageIdentity refers to the same page as this PageIdentity.
*
* Two PageIdentity instances are considered to refer to the same page if
* the page exists, they belong to the same wiki, and have the same ID;
* or the page does not exist, they belong to the same wiki, and have the
* same namespace and DB key.
*
* @param PageIdentity $other
*
* @return bool
*/
public function isSamePageAs( PageIdentity $other );
/**
* Returns an informative human readable representation of the page identity,
* for use in logging and debugging.

View file

@ -182,4 +182,29 @@ class PageIdentityValue implements ProperPageIdentity {
return $name . ' [' . $this->namespace . ':' . $this->dbKey . ']';
}
/**
* @param PageIdentity $other
*
* @return bool
*/
public function isSamePageAs( PageIdentity $other ) {
// NOTE: keep in sync with Title::isSamePageAs()!
// NOTE: keep in sync with WikiPage::isSamePageAs()!
if ( $other->getWikiId() !== $this->getWikiId()
|| $other->getId() !== $this->getId() ) {
return false;
}
if ( $this->getId() === 0 ) {
if ( $other->getNamespace() !== $this->getNamespace()
|| $other->getDBkey() !== $this->getDBkey() ) {
return false;
}
}
return true;
}
}

View file

@ -315,4 +315,17 @@ class TitleValue implements LinkTarget {
return $name;
}
/**
* @param LinkTarget $other
*
* @return bool
*/
public function isSameLinkAs( LinkTarget $other ) {
// NOTE: keep in sync with Title::isSameLinkAs()!
return ( $other->getInterwiki() === $this->getInterwiki() )
&& ( $other->getDBkey() === $this->getDBkey() )
&& ( $other->getNamespace() === $this->getNamespace() )
&& ( $other->getFragment() === $this->getFragment() );
}
}

View file

@ -5,6 +5,8 @@ use MediaWiki\Interwiki\InterwikiLookup;
use MediaWiki\Linker\LinkTarget;
use MediaWiki\MediaWikiServices;
use MediaWiki\Page\PageIdentity;
use MediaWiki\Page\PageIdentityValue;
use MediaWiki\User\UserIdentityValue;
/**
* @group Database
@ -1243,94 +1245,216 @@ class TitleTest extends MediaWikiIntegrationTestCase {
}
public function provideEquals() {
yield [
yield '(newFromText) same text' => [
Title::newFromText( 'Main Page' ),
Title::newFromText( 'Main Page' ),
true
];
yield [
yield '(newFromText) different text' => [
Title::newFromText( 'Main Page' ),
Title::newFromText( 'Not The Main Page' ),
false
];
yield [
yield '(newFromText) different namespace, same text' => [
Title::newFromText( 'Main Page' ),
Title::newFromText( 'Project:Main Page' ),
false
];
yield [
yield '(newFromText) namespace alias' => [
Title::newFromText( 'File:Example.png' ),
Title::newFromText( 'Image:Example.png' ),
true
];
yield [
yield '(newFromText) same special page' => [
Title::newFromText( 'Special:Version' ),
Title::newFromText( 'Special:Version' ),
true
];
yield [
yield '(newFromText) different special page' => [
Title::newFromText( 'Special:Version' ),
Title::newFromText( 'Special:Recentchanges' ),
false
];
yield [
yield '(newFromText) compare special and normal page' => [
Title::newFromText( 'Special:Version' ),
Title::newFromText( 'Main Page' ),
false
];
yield [
yield '(makeTitle) same text' => [
Title::makeTitle( NS_MAIN, 'Foo', '', '' ),
Title::makeTitle( NS_MAIN, 'Foo', '', '' ),
true
];
yield [
yield '(makeTitle) different text' => [
Title::makeTitle( NS_MAIN, 'Foo', '', '' ),
Title::makeTitle( NS_MAIN, 'Bar', '', '' ),
false
];
yield [
yield '(makeTitle) different namespace, same text' => [
Title::makeTitle( NS_MAIN, 'Foo', '', '' ),
Title::makeTitle( NS_TALK, 'Foo', '', '' ),
false
];
yield [
yield '(makeTitle) same fragment' => [
Title::makeTitle( NS_MAIN, 'Foo', 'Bar', '' ),
Title::makeTitle( NS_MAIN, 'Foo', 'Bar', '' ),
true
];
yield [
yield '(makeTitle) different fragment (ignored)' => [
Title::makeTitle( NS_MAIN, 'Foo', 'Bar', '' ),
Title::makeTitle( NS_MAIN, 'Foo', 'Baz', '' ),
true
];
yield [
yield '(makeTitle) fragment vs no fragment (ignored)' => [
Title::makeTitle( NS_MAIN, 'Foo', 'Bar', '' ),
Title::makeTitle( NS_MAIN, 'Foo', '', '' ),
true
];
yield [
yield '(makeTitle) same interwiki' => [
Title::makeTitle( NS_MAIN, 'Foo', '', 'baz' ),
Title::makeTitle( NS_MAIN, 'Foo', '', 'baz' ),
true
];
yield [
yield '(makeTitle) different interwiki' => [
Title::makeTitle( NS_MAIN, 'Foo', '', '' ),
Title::makeTitle( NS_MAIN, 'Foo', '', 'baz' ),
false
];
// Wrong type
yield '(makeTitle vs PageIdentityValue) name text' => [
Title::makeTitle( NS_MAIN, 'Foo' ),
new PageIdentityValue( 0, NS_MAIN, 'Foo', PageIdentity::LOCAL ),
false
];
yield '(makeTitle vs TitleValue) name text' => [
Title::makeTitle( NS_MAIN, 'Foo' ),
new TitleValue( NS_MAIN, 'Foo' ),
false
];
yield '(makeTitle vs UserIdentityValue) name text' => [
Title::makeTitle( NS_MAIN, 'Foo' ),
new UserIdentityValue( 7, 'Foo', 8 ),
false
];
}
/**
* @covers Title::equals
* @dataProvider provideEquals
*/
public function testEquals( Title $firstValue, /* LinkTarget */ $secondValue, $expectedSame ) {
public function testEquals( Title $firstValue, $secondValue, $expectedSame ) {
$this->assertSame(
$expectedSame,
$firstValue->equals( $secondValue )
);
}
public function provideIsSamePageAs() {
$title = Title::makeTitle( 0, 'Foo' );
$title->resetArticleID( 1 );
yield '(PageIdentityValue) same text, title has ID 0' => [
$title,
new PageIdentityValue( 1, 0, 'Foo', PageIdentity::LOCAL ),
true
];
$title = Title::makeTitle( 1, 'Bar_Baz' );
$title->resetArticleID( 0 );
yield '(PageIdentityValue) same text, PageIdentityValue has ID 0' => [
$title,
new PageIdentityValue( 0, 1, 'Bar_Baz', PageIdentity::LOCAL ),
true
];
$title = Title::makeTitle( 0, 'Foo' );
$title->resetArticleID( 0 );
yield '(PageIdentityValue) different text, both IDs are 0' => [
$title,
new PageIdentityValue( 0, 0, 'Foozz', PageIdentity::LOCAL ),
false
];
$title = Title::makeTitle( 0, 'Foo' );
$title->resetArticleID( 0 );
yield '(PageIdentityValue) different namespace' => [
$title,
new PageIdentityValue( 0, 1, 'Foo', PageIdentity::LOCAL ),
false
];
$title = Title::makeTitle( 0, 'Foo', '' );
$title->resetArticleID( 1 );
yield '(PageIdentityValue) different wiki, different ID' => [
$title,
new PageIdentityValue( 1, 0, 'Foo', 'bar' ),
false
];
$title = Title::makeTitle( 0, 'Foo', '' );
$title->resetArticleID( 0 );
yield '(PageIdentityValue) different wiki, both IDs are 0' => [
$title,
new PageIdentityValue( 0, 0, 'Foo', 'bar' ),
false
];
}
/**
* @covers Title::isSamePageAs
* @dataProvider provideIsSamePageAs
*/
public function testIsSamePageAs( Title $firstValue, $secondValue, $expectedSame ) {
$this->assertSame(
$expectedSame,
$firstValue->isSamePageAs( $secondValue )
);
}
public function provideIsSameLinkAs() {
yield 'same text' => [
Title::makeTitle( 0, 'Foo' ),
new TitleValue( 0, 'Foo' ),
true
];
yield 'same namespace' => [
Title::makeTitle( 1, 'Bar_Baz' ),
new TitleValue( 1, 'Bar_Baz' ),
true
];
yield 'same text, different namespace' => [
Title::makeTitle( 0, 'Foo' ),
new TitleValue( 1, 'Foo' ),
false
];
yield 'different text' => [
Title::makeTitle( 0, 'Foo' ),
new TitleValue( 0, 'Foozz' ),
false
];
yield 'different fragment' => [
Title::makeTitle( 0, 'Foo', '' ),
new TitleValue( 0, 'Foo', 'Bar' ),
false
];
yield 'different interwiki' => [
Title::makeTitle( 0, 'Foo', '', 'bar' ),
new TitleValue( 0, 'Foo', '', '' ),
false
];
}
/**
* @covers Title::isSameLinkAs
* @dataProvider provideIsSameLinkAs
*/
public function testIsSameLinkAs( Title $firstValue, $secondValue, $expectedSame ) {
$this->assertSame(
$expectedSame,
$firstValue->isSameLinkAs( $secondValue )
);
}
/**
* @covers Title::newMainPage
*/

View file

@ -104,4 +104,45 @@ class PageIdentityValueTest extends MediaWikiUnitTestCase {
$value->__toString()
);
}
public function provideIsSamePageAs() {
yield [
new PageIdentityValue( 1, 0, 'Foo', PageIdentity::LOCAL ),
new PageIdentityValue( 1, 0, 'Foo', PageIdentity::LOCAL ),
true
];
yield [
new PageIdentityValue( 0, 1, 'Bar_Baz', PageIdentity::LOCAL ),
new PageIdentityValue( 0, 1, 'Bar_Baz', PageIdentity::LOCAL ),
true
];
yield [
new PageIdentityValue( 0, 0, 'Foo', PageIdentity::LOCAL ),
new PageIdentityValue( 0, 0, 'Foozz', PageIdentity::LOCAL ),
false
];
yield [
new PageIdentityValue( 0, 0, 'Foo', PageIdentity::LOCAL ),
new PageIdentityValue( 0, 1, 'Foo', PageIdentity::LOCAL ),
false
];
yield [
new PageIdentityValue( 1, 0, 'Foo', '' ),
new PageIdentityValue( 1, 0, 'Foo', 'bar' ),
false
];
yield [
new PageIdentityValue( 0, 0, 'Foo', '' ),
new PageIdentityValue( 0, 0, 'Foo', 'bar' ),
false
];
}
/**
* @dataProvider provideIsSamePageAs
*/
public function testIsSamePageAs( PageIdentityValue $a, PageIdentityValue $b, $expected ) {
$this->assertSame( $expected, $a->isSamePageAs( $b ) );
$this->assertSame( $expected, $b->isSamePageAs( $a ) );
}
}

View file

@ -221,4 +221,45 @@ class TitleValueTest extends \MediaWikiUnitTestCase {
$value->__toString()
);
}
public function provideIsSameLinkAs() {
yield [
new TitleValue( 0, 'Foo' ),
new TitleValue( 0, 'Foo' ),
true
];
yield [
new TitleValue( 1, 'Bar_Baz' ),
new TitleValue( 1, 'Bar_Baz' ),
true
];
yield [
new TitleValue( 0, 'Foo' ),
new TitleValue( 1, 'Foo' ),
false
];
yield [
new TitleValue( 0, 'Foo' ),
new TitleValue( 0, 'Foozz' ),
false
];
yield [
new TitleValue( 0, 'Foo', '' ),
new TitleValue( 0, 'Foo', 'Bar' ),
false
];
yield [
new TitleValue( 0, 'Foo', '', 'bar' ),
new TitleValue( 0, 'Foo', '', '' ),
false
];
}
/**
* @dataProvider provideIsSameLinkAs
*/
public function testIsSameLinkAs( TitleValue $a, TitleValue $b, $expected ) {
$this->assertSame( $expected, $a->isSameLinkAs( $b ) );
$this->assertSame( $expected, $b->isSameLinkAs( $a ) );
}
}