Log callers that trigger Title::newFromText $text type warning
[lhc/web/wiklou.git] / includes / Title.php
index bf0fb8e..601211d 100644 (file)
@@ -96,7 +96,7 @@ class Title {
        /** @var array Array of groups allowed to edit this article */
        public $mRestrictions = array();
 
-       /** @var bool */
+       /** @var string|bool */
        protected $mOldRestrictions = false;
 
        /** @var bool Cascade restrictions on this page to included templates and images? */
@@ -225,9 +225,11 @@ class Title {
        public static function newFromDBkey( $key ) {
                $t = new Title();
                $t->mDbkeyform = $key;
-               if ( $t->secureAndSplit() ) {
+
+               try {
+                       $t->secureAndSplit();
                        return $t;
-               } else {
+               } catch ( MalformedTitleException $ex ) {
                        return null;
                }
        }
@@ -263,7 +265,34 @@ class Title {
                if ( is_object( $text ) ) {
                        throw new InvalidArgumentException( '$text must be a string.' );
                } elseif ( !is_string( $text ) ) {
-                       wfWarn( __METHOD__ . ': $text must be a string. This will throw an InvalidArgumentException in future.' );
+                       wfDebugLog( 'T76305', wfGetAllCallers( 5 ) );
+                       wfWarn( __METHOD__ . ': $text must be a string. This will throw an InvalidArgumentException in future.', 2 );
+               }
+
+               try {
+                       return Title::newFromTextThrow( $text, $defaultNamespace );
+               } catch ( MalformedTitleException $ex ) {
+                       return null;
+               }
+       }
+
+       /**
+        * Like Title::newFromText(), but throws MalformedTitleException when the title is invalid,
+        * rather than returning null.
+        *
+        * The exception subclasses encode detailed information about why the title is invalid.
+        *
+        * @see Title::newFromText
+        *
+        * @since 1.25
+        * @param string $text Title text to check
+        * @param int $defaultNamespace
+        * @throws MalformedTitleException If the title is invalid
+        * @return Title
+        */
+       public static function newFromTextThrow( $text, $defaultNamespace = NS_MAIN ) {
+               if ( is_object( $text ) ) {
+                       throw new MWException( 'Title::newFromTextThrow given an object' );
                }
 
                $cache = self::getTitleCache();
@@ -287,14 +316,11 @@ class Title {
                $t->mDbkeyform = str_replace( ' ', '_', $filteredText );
                $t->mDefaultNamespace = intval( $defaultNamespace );
 
-               if ( $t->secureAndSplit() ) {
-                       if ( $defaultNamespace == NS_MAIN ) {
-                               $cache->set( $text, $t );
-                       }
-                       return $t;
-               } else {
-                       return null;
+               $t->secureAndSplit();
+               if ( $defaultNamespace == NS_MAIN ) {
+                       $cache->set( $text, $t );
                }
+               return $t;
        }
 
        /**
@@ -323,9 +349,11 @@ class Title {
                }
 
                $t->mDbkeyform = str_replace( ' ', '_', $url );
-               if ( $t->secureAndSplit() ) {
+
+               try {
+                       $t->secureAndSplit();
                        return $t;
-               } else {
+               } catch ( MalformedTitleException $ex ) {
                        return null;
                }
        }
@@ -451,6 +479,9 @@ class Title {
                        if ( isset( $row->page_lang ) ) {
                                $this->mDbPageLanguage = (string)$row->page_lang;
                        }
+                       if ( isset( $row->page_restrictions ) ) {
+                               $this->mOldRestrictions = $row->page_restrictions;
+                       }
                } else { // page not found
                        $this->mArticleID = 0;
                        $this->mLength = 0;
@@ -504,9 +535,11 @@ class Title {
 
                $t = new Title();
                $t->mDbkeyform = Title::makeName( $ns, $title, $fragment, $interwiki, true );
-               if ( $t->secureAndSplit() ) {
+
+               try {
+                       $t->secureAndSplit();
                        return $t;
-               } else {
+               } catch ( MalformedTitleException $ex ) {
                        return null;
                }
        }
@@ -937,7 +970,6 @@ class Title {
        /**
         * Get the page's content model id, see the CONTENT_MODEL_XXX constants.
         *
-        * @throws MWException
         * @param int $flags A bit field; may be Title::GAID_FOR_UPDATE to select for update
         * @return string Content model id
         */
@@ -952,10 +984,6 @@ class Title {
                        $this->mContentModel = ContentHandler::getDefaultModelFor( $this );
                }
 
-               if ( !$this->mContentModel ) {
-                       throw new MWException( 'Failed to determine content model!' );
-               }
-
                return $this->mContentModel;
        }
 
@@ -2574,6 +2602,7 @@ class Title {
                                if ( $row['permission'] == 'autoconfirmed' ) {
                                        $row['permission'] = 'editsemiprotected'; // B/C
                                }
+                               $row['expiry'] = $dbr->decodeExpiry( $row['expiry'] );
                        }
                        $this->mTitleProtection = $row;
                }
@@ -2711,7 +2740,6 @@ class Title {
         *        false.
         */
        public function getCascadeProtectionSources( $getPages = true ) {
-               global $wgContLang;
                $pagerestrictions = array();
 
                if ( $this->mCascadeSources !== null && $getPages ) {
@@ -2754,7 +2782,7 @@ class Title {
                $now = wfTimestampNow();
 
                foreach ( $res as $row ) {
-                       $expiry = $wgContLang->formatExpiry( $row->pr_expiry, TS_MW );
+                       $expiry = $dbr->decodeExpiry( $row->pr_expiry );
                        if ( $expiry > $now ) {
                                if ( $getPages ) {
                                        $page_id = $row->pr_page;
@@ -2887,28 +2915,29 @@ class Title {
         *   restrictions from page table (pre 1.10)
         */
        public function loadRestrictionsFromRows( $rows, $oldFashionedRestrictions = null ) {
-               global $wgContLang;
                $dbr = wfGetDB( DB_SLAVE );
 
                $restrictionTypes = $this->getRestrictionTypes();
 
                foreach ( $restrictionTypes as $type ) {
                        $this->mRestrictions[$type] = array();
-                       $this->mRestrictionsExpiry[$type] = $wgContLang->formatExpiry( '', TS_MW );
+                       $this->mRestrictionsExpiry[$type] = 'infinity';
                }
 
                $this->mCascadeRestriction = false;
 
                # Backwards-compatibility: also load the restrictions from the page record (old format).
+               if ( $oldFashionedRestrictions !== null ) {
+                       $this->mOldRestrictions = $oldFashionedRestrictions;
+               }
 
-               if ( $oldFashionedRestrictions === null ) {
-                       $oldFashionedRestrictions = $dbr->selectField( 'page', 'page_restrictions',
+               if ( $this->mOldRestrictions === false ) {
+                       $this->mOldRestrictions = $dbr->selectField( 'page', 'page_restrictions',
                                array( 'page_id' => $this->getArticleID() ), __METHOD__ );
                }
 
-               if ( $oldFashionedRestrictions != '' ) {
-
-                       foreach ( explode( ':', trim( $oldFashionedRestrictions ) ) as $restrict ) {
+               if ( $this->mOldRestrictions != '' ) {
+                       foreach ( explode( ':', trim( $this->mOldRestrictions ) ) as $restrict ) {
                                $temp = explode( '=', trim( $restrict ) );
                                if ( count( $temp ) == 1 ) {
                                        // old old format should be treated as edit/move restriction
@@ -2921,9 +2950,6 @@ class Title {
                                        }
                                }
                        }
-
-                       $this->mOldRestrictions = true;
-
                }
 
                if ( count( $rows ) ) {
@@ -2940,7 +2966,7 @@ class Title {
 
                                // This code should be refactored, now that it's being used more generally,
                                // But I don't really see any harm in leaving it in Block for now -werdna
-                               $expiry = $wgContLang->formatExpiry( $row->pr_expiry, TS_MW );
+                               $expiry = $dbr->decodeExpiry( $row->pr_expiry );
 
                                // Only apply the restrictions if they haven't expired!
                                if ( !$expiry || $expiry > $now ) {
@@ -2962,11 +2988,9 @@ class Title {
         *   restrictions from page table (pre 1.10)
         */
        public function loadRestrictions( $oldFashionedRestrictions = null ) {
-               global $wgContLang;
                if ( !$this->mRestrictionsLoaded ) {
+                       $dbr = wfGetDB( DB_SLAVE );
                        if ( $this->exists() ) {
-                               $dbr = wfGetDB( DB_SLAVE );
-
                                $res = $dbr->select(
                                        'page_restrictions',
                                        array( 'pr_type', 'pr_expiry', 'pr_level', 'pr_cascade' ),
@@ -2980,7 +3004,7 @@ class Title {
 
                                if ( $title_protection ) {
                                        $now = wfTimestampNow();
-                                       $expiry = $wgContLang->formatExpiry( $title_protection['expiry'], TS_MW );
+                                       $expiry = $dbr->decodeExpiry( $title_protection['expiry'] );
 
                                        if ( !$expiry || $expiry > $now ) {
                                                // Apply the restrictions
@@ -2990,7 +3014,7 @@ class Title {
                                                $this->mTitleProtection = false;
                                        }
                                } else {
-                                       $this->mRestrictionsExpiry['create'] = $wgContLang->formatExpiry( '', TS_MW );
+                                       $this->mRestrictionsExpiry['create'] = 'infinity';
                                }
                                $this->mRestrictionsLoaded = true;
                        }
@@ -3274,6 +3298,7 @@ class Title {
                }
                $this->mRestrictionsLoaded = false;
                $this->mRestrictions = array();
+               $this->mOldRestrictions = false;
                $this->mRedirect = null;
                $this->mLength = -1;
                $this->mLatestID = false;
@@ -3310,6 +3335,7 @@ class Title {
         * namespace prefixes, sets the other forms, and canonicalizes
         * everything.
         *
+        * @throws MalformedTitleException On invalid titles
         * @return bool True on success
         */
        private function secureAndSplit() {
@@ -3320,15 +3346,12 @@ class Title {
 
                $dbkey = $this->mDbkeyform;
 
-               try {
-                       // @note: splitTitleString() is a temporary hack to allow MediaWikiTitleCodec to share
-                       //        the parsing code with Title, while avoiding massive refactoring.
-                       // @todo: get rid of secureAndSplit, refactor parsing code.
-                       $titleParser = self::getTitleParser();
-                       $parts = $titleParser->splitTitleString( $dbkey, $this->getDefaultNamespace() );
-               } catch ( MalformedTitleException $ex ) {
-                       return false;
-               }
+               // @note: splitTitleString() is a temporary hack to allow MediaWikiTitleCodec to share
+               //        the parsing code with Title, while avoiding massive refactoring.
+               // @todo: get rid of secureAndSplit, refactor parsing code.
+               $titleParser = self::getTitleParser();
+               // MalformedTitleException can be thrown here
+               $parts = $titleParser->splitTitleString( $dbkey, $this->getDefaultNamespace() );
 
                # Fill fields
                $this->setFragment( '#' . $parts['fragment'] );
@@ -3420,8 +3443,6 @@ class Title {
         * @return array Array of Title objects linking here
         */
        public function getLinksFrom( $options = array(), $table = 'pagelinks', $prefix = 'pl' ) {
-               global $wgContentHandlerUseDB;
-
                $id = $this->getArticleID();
 
                # If the page doesn't exist; there can't be any link from this page
@@ -3435,49 +3456,36 @@ class Title {
                        $db = wfGetDB( DB_SLAVE );
                }
 
-               $namespaceFiled = "{$prefix}_namespace";
-               $titleField = "{$prefix}_title";
-
-               $fields = array(
-                       $namespaceFiled,
-                       $titleField,
-                       'page_id',
-                       'page_len',
-                       'page_is_redirect',
-                       'page_latest'
-               );
-
-               if ( $wgContentHandlerUseDB ) {
-                       $fields[] = 'page_content_model';
-               }
+               $blNamespace = "{$prefix}_namespace";
+               $blTitle = "{$prefix}_title";
 
                $res = $db->select(
                        array( $table, 'page' ),
-                       $fields,
+                       array_merge(
+                               array( $blNamespace, $blTitle ),
+                               WikiPage::selectFields()
+                       ),
                        array( "{$prefix}_from" => $id ),
                        __METHOD__,
                        $options,
                        array( 'page' => array(
                                'LEFT JOIN',
-                               array( "page_namespace=$namespaceFiled", "page_title=$titleField" )
+                               array( "page_namespace=$blNamespace", "page_title=$blTitle" )
                        ) )
                );
 
                $retVal = array();
-               if ( $res->numRows() ) {
-                       $linkCache = LinkCache::singleton();
-                       foreach ( $res as $row ) {
-                               $titleObj = Title::makeTitle( $row->$namespaceFiled, $row->$titleField );
-                               if ( $titleObj ) {
-                                       if ( $row->page_id ) {
-                                               $linkCache->addGoodLinkObjFromRow( $titleObj, $row );
-                                       } else {
-                                               $linkCache->addBadLinkObj( $titleObj );
-                                       }
-                                       $retVal[] = $titleObj;
-                               }
+               $linkCache = LinkCache::singleton();
+               foreach ( $res as $row ) {
+                       if ( $row->page_id ) {
+                               $titleObj = Title::newFromRow( $row );
+                       } else {
+                               $titleObj = Title::makeTitle( $row->$blNamespace, $row->$blTitle );
+                               $linkCache->addBadLinkObj( $titleObj );
                        }
+                       $retVal[] = $titleObj;
                }
+
                return $retVal;
        }
 
@@ -4228,10 +4236,12 @@ class Title {
         * If you want to know if a title can be meaningfully viewed, you should
         * probably call the isKnown() method instead.
         *
+        * @param int $flags An optional bit field; may be Title::GAID_FOR_UPDATE to check
+        *   from master/for update
         * @return bool
         */
-       public function exists() {
-               $exists = $this->getArticleID() != 0;
+       public function exists( $flags = 0 ) {
+               $exists = $this->getArticleID( $flags ) != 0;
                Hooks::run( 'TitleExists', array( $this, &$exists ) );
                return $exists;
        }
@@ -4424,35 +4434,29 @@ class Title {
         * @return string|null
         */
        public function getNotificationTimestamp( $user = null ) {
-               global $wgUser, $wgShowUpdatedMarker;
+               global $wgUser;
+
                // Assume current user if none given
                if ( !$user ) {
                        $user = $wgUser;
                }
                // Check cache first
                $uid = $user->getId();
+               if ( !$uid ) {
+                       return false;
+               }
                // avoid isset here, as it'll return false for null entries
                if ( array_key_exists( $uid, $this->mNotificationTimestamp ) ) {
                        return $this->mNotificationTimestamp[$uid];
                }
-               if ( !$uid || !$wgShowUpdatedMarker || !$user->isAllowed( 'viewmywatchlist' ) ) {
-                       $this->mNotificationTimestamp[$uid] = false;
-                       return $this->mNotificationTimestamp[$uid];
-               }
                // Don't cache too much!
                if ( count( $this->mNotificationTimestamp ) >= self::CACHE_MAX ) {
                        $this->mNotificationTimestamp = array();
                }
-               $dbr = wfGetDB( DB_SLAVE );
-               $this->mNotificationTimestamp[$uid] = $dbr->selectField( 'watchlist',
-                       'wl_notificationtimestamp',
-                       array(
-                               'wl_user' => $user->getId(),
-                               'wl_namespace' => $this->getNamespace(),
-                               'wl_title' => $this->getDBkey(),
-                       ),
-                       __METHOD__
-               );
+
+               $watchedItem = WatchedItem::fromUserTitle( $user, $this );
+               $this->mNotificationTimestamp[$uid] = $watchedItem->getNotificationTimestamp();
+
                return $this->mNotificationTimestamp[$uid];
        }