* Create a new Title from text, such as what one would find in a link. De-
* codes any HTML entities in the text.
*
+ * Title objects returned by this method are guaranteed to be valid, and
+ * thus return true from the isValid() method.
+ *
* @param string|int|null $text The link text; spaces, prefixes, and an
* initial ':' indicating the main namespace are accepted.
* @param int $defaultNamespace The namespace to use if none is specified
}
try {
- return Title::newFromTextThrow( strval( $text ), $defaultNamespace );
+ return self::newFromTextThrow( strval( $text ), $defaultNamespace );
} catch ( MalformedTitleException $ex ) {
return null;
}
*
* The exception subclasses encode detailed information about why the title is invalid.
*
+ * Title objects returned by this method are guaranteed to be valid, and
+ * thus return true from the isValid() method.
+ *
* @see Title::newFromText
*
* @since 1.25
__METHOD__
);
if ( $row !== false ) {
- $title = Title::newFromRow( $row );
+ $title = self::newFromRow( $row );
} else {
$title = null;
}
$titles = [];
foreach ( $res as $row ) {
- $titles[] = Title::newFromRow( $row );
+ $titles[] = self::newFromRow( $row );
}
return $titles;
}
/**
* Create a new Title from a namespace index and a DB key.
- * It's assumed that $ns and $title are *valid*, for instance when
- * they came directly from the database or a special page name.
- * For convenience, spaces are converted to underscores so that
- * eg user_text fields can be used directly.
+ *
+ * It's assumed that $ns and $title are safe, for instance when
+ * they came directly from the database or a special page name,
+ * not from user input.
+ *
+ * No validation is applied. For convenience, spaces are normalized
+ * to underscores, so that e.g. user_text fields can be used directly.
+ *
+ * @note This method may return Title objects that are "invalid"
+ * according to the isValid() method. This is usually caused by
+ * configuration changes: e.g. a namespace that was once defined is
+ * no longer configured, or a character that was once allowed in
+ * titles is now forbidden.
*
* @param int $ns The namespace of the article
* @param string $title The unprefixed database key form
* The parameters will be checked for validity, which is a bit slower
* than makeTitle() but safer for user-provided data.
*
+ * Title objects returned by makeTitleSafe() are guaranteed to be valid,
+ * that is, they return true from the isValid() method. If no valid Title
+ * can be constructed from the input, this method returns null.
+ *
* @param int $ns The namespace of the article
* @param string $title Database key form
* @param string $fragment The link fragment (after the "#")
* @return Title|null The new object, or null on an error
*/
public static function makeTitleSafe( $ns, $title, $fragment = '', $interwiki = '' ) {
+ // NOTE: ideally, this would just call makeTitle() and then isValid(),
+ // but presently, that means more overhead on a potential performance hotspot.
+
if ( !MWNamespace::exists( $ns ) ) {
return null;
}
$t = new Title();
- $t->mDbkeyform = Title::makeName( $ns, $title, $fragment, $interwiki, true );
+ $t->mDbkeyform = self::makeName( $ns, $title, $fragment, $interwiki, true );
try {
$t->secureAndSplit();
* @return Title The new object
*/
public static function newMainPage() {
- $title = Title::newFromText( wfMessage( 'mainpage' )->inContentLanguage()->text() );
+ $title = self::newFromText( wfMessage( 'mainpage' )->inContentLanguage()->text() );
// Don't give fatal errors if the message is broken
if ( !$title ) {
- $title = Title::newFromText( 'Main Page' );
+ $title = self::newFromText( 'Main Page' );
}
return $title;
}
/**
* Escape a text fragment, say from a link, for a URL
*
+ * @deprecated since 1.30, use Sanitizer::escapeIdForLink() or escapeIdForExternalInterwiki()
+ *
* @param string $fragment Containing a URL or link fragment (after the "#")
* @return string Escaped string
*/
}
}
+ /**
+ * Returns true if the title is valid, false if it is invalid.
+ *
+ * Valid titles can be round-tripped via makeTitleSafe() and newFromText().
+ * Invalid titles may get returned from makeTitle(), and it may be useful to
+ * allow them to exist, e.g. in order to process log entries about pages in
+ * namespaces that belong to extensions that are no longer installed.
+ *
+ * @note This method is relatively expensive. When constructing Title
+ * objects that need to be valid, use an instantiator method that is guaranteed
+ * to return valid titles, such as makeTitleSafe() or newFromText().
+ *
+ * @return bool
+ */
+ public function isValid() {
+ $ns = $this->getNamespace();
+
+ if ( !MWNamespace::exists( $ns ) ) {
+ return false;
+ }
+
+ try {
+ $parser = MediaWikiServices::getInstance()->getTitleParser();
+ $parser->parseTitle( $this->getDBkey(), $ns );
+ return true;
+ } catch ( MalformedTitleException $ex ) {
+ return false;
+ }
+ }
+
/**
* Determine whether the object refers to a page within
* this project (either this wiki or a wiki with a local
*/
public function getContentModel( $flags = 0 ) {
if ( !$this->mForcedContentModel
- && ( !$this->mContentModel || $flags === Title::GAID_FOR_UPDATE )
+ && ( !$this->mContentModel || $flags === self::GAID_FOR_UPDATE )
&& $this->getArticleID( $flags )
) {
$linkCache = LinkCache::singleton();
* Can this title have a corresponding talk page?
*
* @see MWNamespace::hasTalkNamespace
+ * @since 1.30
*
* @return bool True if this title either is a talk page or can have a talk page associated.
*/
if ( $canonicalName ) {
$localName = SpecialPageFactory::getLocalNameFor( $canonicalName, $par );
if ( $localName != $this->mDbkeyform ) {
- return Title::makeTitle( NS_SPECIAL, $localName );
+ return self::makeTitle( NS_SPECIAL, $localName );
}
}
}
* @return bool
*/
public function isMainPage() {
- return $this->equals( Title::newMainPage() );
+ return $this->equals( self::newMainPage() );
}
/**
* @return Title The object for the talk page
*/
public function getTalkPage() {
- return Title::makeTitle( MWNamespace::getTalk( $this->getNamespace() ), $this->getDBkey() );
+ return self::makeTitle( MWNamespace::getTalk( $this->getNamespace() ), $this->getDBkey() );
+ }
+
+ /**
+ * Get a Title object associated with the talk page of this article,
+ * if such a talk page can exist.
+ *
+ * @since 1.30
+ *
+ * @return Title|null The object for the talk page,
+ * or null if no associated talk page can exist, according to canHaveTalkPage().
+ */
+ public function getTalkPageIfDefined() {
+ if ( !$this->canHaveTalkPage() ) {
+ return null;
+ }
+
+ return $this->getTalkPage();
}
/**
if ( $this->getNamespace() == $subjectNS ) {
return $this;
}
- return Title::makeTitle( $subjectNS, $this->getDBkey() );
+ return self::makeTitle( $subjectNS, $this->getDBkey() );
}
/**
* get the talk page, if it is a subject page get the talk page
*
* @since 1.25
- * @throws MWException
+ * @throws MWException If the page doesn't have an other page
* @return Title
*/
public function getOtherPage() {
if ( $this->isTalkPage() ) {
return $this->getSubjectPage();
} else {
+ if ( !$this->canHaveTalkPage() ) {
+ throw new MWException( "{$this->getPrefixedText()} does not have an other page" );
+ }
return $this->getTalkPage();
}
}
/**
* Get the fragment in URL form, including the "#" character if there is one
+ *
* @return string Fragment in URL form
*/
public function getFragmentForURL() {
if ( !$this->hasFragment() ) {
return '';
- } else {
- return '#' . Title::escapeFragmentForURL( $this->getFragment() );
+ } elseif ( $this->isExternal() && !$this->getTransWikiID() ) {
+ return '#' . Sanitizer::escapeIdForExternalInterwiki( $this->getFragment() );
}
+ return '#' . Sanitizer::escapeIdForLink( $this->getFragment() );
}
/**
* @since 1.20
*/
public function getRootTitle() {
- return Title::makeTitle( $this->getNamespace(), $this->getRootText() );
+ return self::makeTitle( $this->getNamespace(), $this->getRootText() );
}
/**
* @since 1.20
*/
public function getBaseTitle() {
- return Title::makeTitle( $this->getNamespace(), $this->getBaseText() );
+ return self::makeTitle( $this->getNamespace(), $this->getBaseText() );
}
/**
* @since 1.20
*/
public function getSubpage( $text ) {
- return Title::makeTitleSafe( $this->getNamespace(), $this->getText() . '/' . $text );
+ return self::makeTitleSafe( $this->getNamespace(), $this->getText() . '/' . $text );
}
/**
* @see self::getLocalURL for the arguments.
* @param array|string $query
* @param string $proto Protocol type to use in URL
- * @return String. A url suitable to use in an HTTP location header.
+ * @return string A url suitable to use in an HTTP location header.
*/
public function getFullUrlForRedirect( $query = '', $proto = PROTO_CURRENT ) {
$target = $this;
* protocol-relative, the URL will be expanded to http://
*
* @see self::getLocalURL for the arguments.
+ * @param string $query
+ * @param string|bool $query2
* @return string The URL
*/
public function getInternalURL( $query = '', $query2 = false ) {
* NOTE: Unlike getInternalURL(), the canonical URL includes the fragment
*
* @see self::getLocalURL for the arguments.
+ * @param string $query
+ * @param string|bool $query2
* @return string The URL
* @since 1.18
*/
if ( $this->mTitleProtection === null ) {
$dbr = wfGetDB( DB_REPLICA );
+ $commentStore = new CommentStore( 'pt_reason' );
+ $commentQuery = $commentStore->getJoin();
$res = $dbr->select(
- 'protected_titles',
+ [ 'protected_titles' ] + $commentQuery['tables'],
[
'user' => 'pt_user',
- 'reason' => 'pt_reason',
'expiry' => 'pt_expiry',
'permission' => 'pt_create_perm'
- ],
+ ] + $commentQuery['fields'],
[ 'pt_namespace' => $this->getNamespace(), 'pt_title' => $this->getDBkey() ],
- __METHOD__
+ __METHOD__,
+ [],
+ $commentQuery['joins']
);
// fetchRow returns false if there are no rows.
$row = $dbr->fetchRow( $res );
if ( $row ) {
- $row['expiry'] = $dbr->decodeExpiry( $row['expiry'] );
+ $this->mTitleProtection = [
+ 'user' => $row['user'],
+ 'expiry' => $dbr->decodeExpiry( $row['expiry'] ),
+ 'permission' => $row['permission'],
+ 'reason' => $commentStore->getComment( $row )->text,
+ ];
+ } else {
+ $this->mTitleProtection = false;
}
- $this->mTitleProtection = $row;
}
return $this->mTitleProtection;
}
$page_id = $row->pr_page;
$page_ns = $row->page_namespace;
$page_title = $row->page_title;
- $sources[$page_id] = Title::makeTitle( $page_ns, $page_title );
+ $sources[$page_id] = self::makeTitle( $page_ns, $page_title );
# Add groups needed for each restriction type if its not already there
# Make sure this restriction type still exists
if ( $limit > -1 ) {
$options['LIMIT'] = $limit;
}
- $this->mSubpages = TitleArray::newFromResult(
+ return TitleArray::newFromResult(
$dbr->select( 'page',
[ 'page_id', 'page_namespace', 'page_title', 'page_is_redirect' ],
$conds,
$options
)
);
- return $this->mSubpages;
}
/**
* @return int Int or 0 if the page doesn't exist
*/
public function getLatestRevID( $flags = 0 ) {
- if ( !( $flags & Title::GAID_FOR_UPDATE ) && $this->mLatestID !== false ) {
+ if ( !( $flags & self::GAID_FOR_UPDATE ) && $this->mLatestID !== false ) {
return intval( $this->mLatestID );
}
if ( !$this->getArticleID( $flags ) ) {
if ( $res->numRows() ) {
$linkCache = LinkCache::singleton();
foreach ( $res as $row ) {
- $titleObj = Title::makeTitle( $row->page_namespace, $row->page_title );
+ $titleObj = self::makeTitle( $row->page_namespace, $row->page_title );
if ( $titleObj ) {
$linkCache->addGoodLinkObjFromRow( $titleObj, $row );
$retVal[] = $titleObj;
$linkCache = LinkCache::singleton();
foreach ( $res as $row ) {
if ( $row->page_id ) {
- $titleObj = Title::newFromRow( $row );
+ $titleObj = self::newFromRow( $row );
} else {
- $titleObj = Title::makeTitle( $row->$blNamespace, $row->$blTitle );
+ $titleObj = self::makeTitle( $row->$blNamespace, $row->$blTitle );
$linkCache->addBadLinkObj( $titleObj );
}
$retVal[] = $titleObj;
$retVal = [];
foreach ( $res as $row ) {
- $retVal[] = Title::makeTitle( $row->pl_namespace, $row->pl_title );
+ $retVal[] = self::makeTitle( $row->pl_namespace, $row->pl_title );
}
return $retVal;
}
* Returns true if ok, or a getUserPermissionsErrors()-like array otherwise
*
* @deprecated since 1.25, use MovePage's methods instead
- * @param Title $nt The new title
+ * @param Title &$nt The new title
* @param bool $auth Whether to check user permissions (uses $wgUser)
* @param string $reason Is the log summary of the move, used for spam checking
* @return array|bool True on success, getUserPermissionsErrors()-like array on failure
* Move a title to a new location
*
* @deprecated since 1.25, use the MovePage class instead
- * @param Title $nt The new title
+ * @param Title &$nt The new title
* @param bool $auth Indicates whether $wgUser's permissions
* should be checked
* @param string $reason The reason for the move
}
# T16385: we need makeTitleSafe because the new page names may
# be longer than 255 characters.
- $newSubpage = Title::makeTitleSafe( $newNs, $newPageName );
+ $newSubpage = self::makeTitleSafe( $newNs, $newPageName );
$success = $oldSubpage->moveTo( $newSubpage, $auth, $reason, $createRedirect, $changeTags );
if ( $success === true ) {
# Circular reference
$stack[$parent] = [];
} else {
- $nt = Title::newFromText( $parent );
+ $nt = self::newFromText( $parent );
if ( $nt ) {
$stack[$parent] = $nt->getParentCategoryTree( $children + [ $parent => 1 ] );
}