Merge "Selenium: replace UserLoginPage with BlankPage where possible"
[lhc/web/wiklou.git] / includes / Title.php
index cf81b98..6e75102 100644 (file)
@@ -23,6 +23,7 @@
  */
 
 use MediaWiki\Permissions\PermissionManager;
+use Wikimedia\Assert\Assert;
 use Wikimedia\Rdbms\Database;
 use Wikimedia\Rdbms\IDatabase;
 use MediaWiki\Linker\LinkTarget;
@@ -851,7 +852,10 @@ class Title implements LinkTarget, IDBAccessObject {
        /**
         * Returns true if the title is valid, false if it is invalid.
         *
-        * Valid titles can be round-tripped via makeTitleSafe() and newFromText().
+        * Valid titles can be round-tripped via makeTitle() and newFromText().
+        * Their DB key can be used in the database, though it may not have the correct
+        * capitalization.
+        *
         * 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.
@@ -870,10 +874,23 @@ class Title implements LinkTarget, IDBAccessObject {
 
                try {
                        $services->getTitleParser()->parseTitle( $this->mDbkeyform, $this->mNamespace );
-                       return true;
                } catch ( MalformedTitleException $ex ) {
                        return false;
                }
+
+               try {
+                       // Title value applies basic syntax checks. Should perhaps be moved elsewhere.
+                       new TitleValue(
+                               $this->mNamespace,
+                               $this->mDbkeyform,
+                               $this->mFragment,
+                               $this->mInterwiki
+                       );
+               } catch ( InvalidArgumentException $ex ) {
+                       return false;
+               }
+
+               return true;
        }
 
        /**
@@ -1128,14 +1145,16 @@ class Title implements LinkTarget, IDBAccessObject {
        /**
         * Can this title have a corresponding talk page?
         *
-        * @see NamespaceInfo::hasTalkNamespace
+        * False for relative section links (with getText() === ''),
+        * interwiki links (with getInterwiki() !== ''), and pages in NS_SPECIAL.
+        *
+        * @see NamespaceInfo::canHaveTalkPage
         * @since 1.30
         *
         * @return bool True if this title either is a talk page or can have a talk page associated.
         */
        public function canHaveTalkPage() {
-               return MediaWikiServices::getInstance()->getNamespaceInfo()->
-                       hasTalkNamespace( $this->mNamespace );
+               return MediaWikiServices::getInstance()->getNamespaceInfo()->canHaveTalkPage( $this );
        }
 
        /**
@@ -1150,11 +1169,15 @@ class Title implements LinkTarget, IDBAccessObject {
        /**
         * Can this title be added to a user's watchlist?
         *
+        * False for relative section links (with getText() === ''),
+        * interwiki links (with getInterwiki() !== ''), and pages in NS_SPECIAL.
+        *
         * @return bool
         */
        public function isWatchable() {
-               return !$this->isExternal() && MediaWikiServices::getInstance()->getNamespaceInfo()->
-                       isWatchable( $this->mNamespace );
+               $nsInfo = MediaWikiServices::getInstance()->getNamespaceInfo();
+               return $this->getText() !== '' && !$this->isExternal() &&
+                       $nsInfo->isWatchable( $this->mNamespace );
        }
 
        /**
@@ -1515,8 +1538,11 @@ class Title implements LinkTarget, IDBAccessObject {
        /**
         * Get a Title object associated with the talk page of this article
         *
-        * @deprecated since 1.34, use NamespaceInfo::getTalkPage
+        * @deprecated since 1.34, use getTalkPageIfDefined() or NamespaceInfo::getTalkPage()
+        *             with NamespaceInfo::canHaveTalkPage().
         * @return Title The object for the talk page
+        * @throws MWException if $target doesn't have talk pages, e.g. because it's in NS_SPECIAL
+        *         or because it's a relative link, or an interwiki link.
         */
        public function getTalkPage() {
                return self::castFromLinkTarget(
@@ -1728,6 +1754,9 @@ class Title implements LinkTarget, IDBAccessObject {
        /**
         * Get the root page name text without a namespace, i.e. the leftmost part before any slashes
         *
+        * @note the return value may contain trailing whitespace and is thus
+        * not safe for use with makeTitle or TitleValue.
+        *
         * @par Example:
         * @code
         * Title::newFromText('User:Foo/Bar/Baz')->getRootText();
@@ -1761,12 +1790,20 @@ class Title implements LinkTarget, IDBAccessObject {
         * @since 1.20
         */
        public function getRootTitle() {
-               return self::makeTitle( $this->mNamespace, $this->getRootText() );
+               $title = self::makeTitleSafe( $this->mNamespace, $this->getRootText() );
+               Assert::postcondition(
+                       $title !== null,
+                       'makeTitleSafe() should always return a Title for the text returned by getRootText().'
+               );
+               return $title;
        }
 
        /**
         * Get the base page name without a namespace, i.e. the part before the subpage name
         *
+        * @note the return value may contain trailing whitespace and is thus
+        * not safe for use with makeTitle or TitleValue.
+        *
         * @par Example:
         * @code
         * Title::newFromText('User:Foo/Bar/Baz')->getBaseText();
@@ -1794,7 +1831,7 @@ class Title implements LinkTarget, IDBAccessObject {
        }
 
        /**
-        * Get the base page name title, i.e. the part before the subpage name
+        * Get the base page name title, i.e. the part before the subpage name.
         *
         * @par Example:
         * @code
@@ -1806,7 +1843,12 @@ class Title implements LinkTarget, IDBAccessObject {
         * @since 1.20
         */
        public function getBaseTitle() {
-               return self::makeTitle( $this->mNamespace, $this->getBaseText() );
+               $title = self::makeTitleSafe( $this->mNamespace, $this->getBaseText() );
+               Assert::postcondition(
+                       $title !== null,
+                       'makeTitleSafe() should always return a Title for the text returned by getBaseText().'
+               );
+               return $title;
        }
 
        /**
@@ -2256,7 +2298,7 @@ class Title implements LinkTarget, IDBAccessObject {
         * Add the resulting error code to the errors array
         *
         * @param array $errors List of current errors
-        * @param array $result Result of errors
+        * @param array|string|MessageSpecifier|false $result Result of errors
         *
         * @return array List of errors
         */