MediaWiki  REL1_24
PrefixSearch.php
Go to the documentation of this file.
00001 <?php
00029 abstract class PrefixSearch {
00039     public static function titleSearch( $search, $limit, $namespaces = array() ) {
00040         $prefixSearch = new StringPrefixSearch;
00041         return $prefixSearch->search( $search, $limit, $namespaces );
00042     }
00043 
00052     public function search( $search, $limit, $namespaces = array() ) {
00053         $search = trim( $search );
00054         if ( $search == '' ) {
00055             return array(); // Return empty result
00056         }
00057         $namespaces = $this->validateNamespaces( $namespaces );
00058 
00059         // Find a Title which is not an interwiki and is in NS_MAIN
00060         $title = Title::newFromText( $search );
00061         if ( $title && !$title->isExternal() ) {
00062             $ns = array( $title->getNamespace() );
00063             if ( $ns[0] == NS_MAIN ) {
00064                 $ns = $namespaces; // no explicit prefix, use default namespaces
00065             }
00066             return $this->searchBackend(
00067                 $ns, $title->getText(), $limit );
00068         }
00069 
00070         // Is this a namespace prefix?
00071         $title = Title::newFromText( $search . 'Dummy' );
00072         if ( $title && $title->getText() == 'Dummy'
00073             && $title->getNamespace() != NS_MAIN
00074             && !$title->isExternal() )
00075         {
00076             $namespaces = array( $title->getNamespace() );
00077             $search = '';
00078         }
00079 
00080         return $this->searchBackend( $namespaces, $search, $limit );
00081     }
00082 
00091     public function searchWithVariants( $search, $limit, array $namespaces ) {
00092         wfProfileIn( __METHOD__ );
00093         $searches = $this->search( $search, $limit, $namespaces );
00094 
00095         // if the content language has variants, try to retrieve fallback results
00096         $fallbackLimit = $limit - count( $searches );
00097         if ( $fallbackLimit > 0 ) {
00098             global $wgContLang;
00099 
00100             $fallbackSearches = $wgContLang->autoConvertToAllVariants( $search );
00101             $fallbackSearches = array_diff( array_unique( $fallbackSearches ), array( $search ) );
00102 
00103             foreach ( $fallbackSearches as $fbs ) {
00104                 $fallbackSearchResult = $this->search( $fbs, $fallbackLimit, $namespaces );
00105                 $searches = array_merge( $searches, $fallbackSearchResult );
00106                 $fallbackLimit -= count( $fallbackSearchResult );
00107 
00108                 if ( $fallbackLimit == 0 ) {
00109                     break;
00110                 }
00111             }
00112         }
00113         wfProfileOut( __METHOD__ );
00114         return $searches;
00115     }
00116 
00124     abstract protected function titles( array $titles );
00125 
00134     abstract protected function strings( array $strings );
00135 
00143     protected function searchBackend( $namespaces, $search, $limit ) {
00144         if ( count( $namespaces ) == 1 ) {
00145             $ns = $namespaces[0];
00146             if ( $ns == NS_MEDIA ) {
00147                 $namespaces = array( NS_FILE );
00148             } elseif ( $ns == NS_SPECIAL ) {
00149                 return $this->titles( $this->specialSearch( $search, $limit ) );
00150             }
00151         }
00152         $srchres = array();
00153         if ( wfRunHooks( 'PrefixSearchBackend', array( $namespaces, $search, $limit, &$srchres ) ) ) {
00154             return $this->titles( $this->defaultSearchBackend( $namespaces, $search, $limit ) );
00155         }
00156         return $this->strings( $srchres );
00157     }
00158 
00166     protected function specialSearch( $search, $limit ) {
00167         global $wgContLang;
00168 
00169         $searchParts = explode( '/', $search, 2 );
00170         $searchKey = $searchParts[0];
00171         $subpageSearch = isset( $searchParts[1] ) ? $searchParts[1] : null;
00172 
00173         // Handle subpage search separately.
00174         if ( $subpageSearch !== null ) {
00175             // Try matching the full search string as a page name
00176             $specialTitle = Title::makeTitleSafe( NS_SPECIAL, $searchKey );
00177             if ( !$specialTitle ) {
00178                 return array();
00179             }
00180             $special = SpecialPageFactory::getPage( $specialTitle->getText() );
00181             if ( $special ) {
00182                 $subpages = $special->prefixSearchSubpages( $subpageSearch, $limit );
00183                 return array_map( function ( $sub ) use ( $specialTitle ) {
00184                     return $specialTitle->getSubpage( $sub );
00185                 }, $subpages );
00186             } else {
00187                 return array();
00188             }
00189         }
00190 
00191         # normalize searchKey, so aliases with spaces can be found - bug 25675
00192         $searchKey = str_replace( ' ', '_', $searchKey );
00193         $searchKey = $wgContLang->caseFold( $searchKey );
00194 
00195         // Unlike SpecialPage itself, we want the canonical forms of both
00196         // canonical and alias title forms...
00197         $keys = array();
00198         foreach ( SpecialPageFactory::getNames() as $page  ) {
00199             $keys[$wgContLang->caseFold( $page )] = $page;
00200         }
00201 
00202         foreach ( $wgContLang->getSpecialPageAliases() as $page => $aliases ) {
00203             if ( !in_array( $page, SpecialPageFactory::getNames() ) ) {# bug 20885
00204                 continue;
00205             }
00206 
00207             foreach ( $aliases as $alias ) {
00208                 $keys[$wgContLang->caseFold( $alias )] = $alias;
00209             }
00210         }
00211         ksort( $keys );
00212 
00213         $srchres = array();
00214         foreach ( $keys as $pageKey => $page ) {
00215             if ( $searchKey === '' || strpos( $pageKey, $searchKey ) === 0 ) {
00216                 // bug 27671: Don't use SpecialPage::getTitleFor() here because it
00217                 // localizes its input leading to searches for e.g. Special:All
00218                 // returning Spezial:MediaWiki-Systemnachrichten and returning
00219                 // Spezial:Alle_Seiten twice when $wgLanguageCode == 'de'
00220                 $srchres[] = Title::makeTitleSafe( NS_SPECIAL, $page );
00221             }
00222 
00223             if ( count( $srchres ) >= $limit ) {
00224                 break;
00225             }
00226         }
00227 
00228         return $srchres;
00229     }
00230 
00242     protected function defaultSearchBackend( $namespaces, $search, $limit ) {
00243         $ns = array_shift( $namespaces ); // support only one namespace
00244         if ( in_array( NS_MAIN, $namespaces ) ) {
00245             $ns = NS_MAIN; // if searching on many always default to main
00246         }
00247 
00248         $t = Title::newFromText( $search, $ns );
00249         $prefix = $t ? $t->getDBkey() : '';
00250         $dbr = wfGetDB( DB_SLAVE );
00251         $res = $dbr->select( 'page',
00252             array( 'page_id', 'page_namespace', 'page_title' ),
00253             array(
00254                 'page_namespace' => $ns,
00255                 'page_title ' . $dbr->buildLike( $prefix, $dbr->anyString() )
00256             ),
00257             __METHOD__,
00258             array( 'LIMIT' => $limit, 'ORDER BY' => 'page_title' )
00259         );
00260         $srchres = array();
00261         foreach ( $res as $row ) {
00262             $srchres[] = Title::newFromRow( $row );
00263         }
00264         return $srchres;
00265     }
00266 
00273     protected function validateNamespaces( $namespaces ) {
00274         global $wgContLang;
00275 
00276         // We will look at each given namespace against wgContLang namespaces
00277         $validNamespaces = $wgContLang->getNamespaces();
00278         if ( is_array( $namespaces ) && count( $namespaces ) > 0 ) {
00279             $valid = array();
00280             foreach ( $namespaces as $ns ) {
00281                 if ( is_numeric( $ns ) && array_key_exists( $ns, $validNamespaces ) ) {
00282                     $valid[] = $ns;
00283                 }
00284             }
00285             if ( count( $valid ) > 0 ) {
00286                 return $valid;
00287             }
00288         }
00289 
00290         return array( NS_MAIN );
00291     }
00292 }
00293 
00298 class TitlePrefixSearch extends PrefixSearch {
00299 
00300     protected function titles( array $titles ) {
00301         return $titles;
00302     }
00303 
00304     protected function strings( array $strings ) {
00305         $titles = array_map( 'Title::newFromText', $strings );
00306         $lb = new LinkBatch( $titles );
00307         $lb->setCaller( __METHOD__ );
00308         $lb->execute();
00309         return $titles;
00310     }
00311 }
00312 
00317 class StringPrefixSearch extends PrefixSearch {
00318 
00319     protected function titles( array $titles ) {
00320         return array_map( function ( Title $t ) {
00321             return $t->getPrefixedText();
00322         }, $titles );
00323     }
00324 
00325     protected function strings( array $strings ) {
00326         return $strings;
00327     }
00328 }