MediaWiki  REL1_23
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         $search = new StringPrefixSearch;
00041         return $search->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     protected abstract function titles( array $titles );
00125 
00134     protected abstract 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         # normalize searchKey, so aliases with spaces can be found - bug 25675
00170         $search = str_replace( ' ', '_', $search );
00171 
00172         $searchKey = $wgContLang->caseFold( $search );
00173 
00174         // Unlike SpecialPage itself, we want the canonical forms of both
00175         // canonical and alias title forms...
00176         $keys = array();
00177         foreach ( SpecialPageFactory::getList() as $page => $class ) {
00178             $keys[$wgContLang->caseFold( $page )] = $page;
00179         }
00180 
00181         foreach ( $wgContLang->getSpecialPageAliases() as $page => $aliases ) {
00182             if ( !array_key_exists( $page, SpecialPageFactory::getList() ) ) {# bug 20885
00183                 continue;
00184             }
00185 
00186             foreach ( $aliases as $alias ) {
00187                 $keys[$wgContLang->caseFold( $alias )] = $alias;
00188             }
00189         }
00190         ksort( $keys );
00191 
00192         $srchres = array();
00193         foreach ( $keys as $pageKey => $page ) {
00194             if ( $searchKey === '' || strpos( $pageKey, $searchKey ) === 0 ) {
00195                 // bug 27671: Don't use SpecialPage::getTitleFor() here because it
00196                 // localizes its input leading to searches for e.g. Special:All
00197                 // returning Spezial:MediaWiki-Systemnachrichten and returning
00198                 // Spezial:Alle_Seiten twice when $wgLanguageCode == 'de'
00199                 $srchres[] = Title::makeTitleSafe( NS_SPECIAL, $page );
00200             }
00201 
00202             if ( count( $srchres ) >= $limit ) {
00203                 break;
00204             }
00205         }
00206 
00207         return $srchres;
00208     }
00209 
00221     protected function defaultSearchBackend( $namespaces, $search, $limit ) {
00222         $ns = array_shift( $namespaces ); // support only one namespace
00223         if ( in_array( NS_MAIN, $namespaces ) ) {
00224             $ns = NS_MAIN; // if searching on many always default to main
00225         }
00226 
00227         $t = Title::newFromText( $search, $ns );
00228         $prefix = $t ? $t->getDBkey() : '';
00229         $dbr = wfGetDB( DB_SLAVE );
00230         $res = $dbr->select( 'page',
00231             array( 'page_id', 'page_namespace', 'page_title' ),
00232             array(
00233                 'page_namespace' => $ns,
00234                 'page_title ' . $dbr->buildLike( $prefix, $dbr->anyString() )
00235             ),
00236             __METHOD__,
00237             array( 'LIMIT' => $limit, 'ORDER BY' => 'page_title' )
00238         );
00239         $srchres = array();
00240         foreach ( $res as $row ) {
00241             $srchres[] = Title::newFromRow( $row );
00242         }
00243         return $srchres;
00244     }
00245 
00252     protected function validateNamespaces( $namespaces ) {
00253         global $wgContLang;
00254 
00255         // We will look at each given namespace against wgContLang namespaces
00256         $validNamespaces = $wgContLang->getNamespaces();
00257         if ( is_array( $namespaces ) && count( $namespaces ) > 0 ) {
00258             $valid = array();
00259             foreach ( $namespaces as $ns ) {
00260                 if ( is_numeric( $ns ) && array_key_exists( $ns, $validNamespaces ) ) {
00261                     $valid[] = $ns;
00262                 }
00263             }
00264             if ( count( $valid ) > 0 ) {
00265                 return $valid;
00266             }
00267         }
00268 
00269         return array( NS_MAIN );
00270     }
00271 }
00272 
00277 class TitlePrefixSearch extends PrefixSearch {
00278 
00279     protected function titles( array $titles ) {
00280         return $titles;
00281     }
00282 
00283     protected function strings( array $strings ) {
00284         $titles = array_map( 'Title::newFromText', $strings );
00285         $lb = new LinkBatch( $titles );
00286         $lb->setCaller( __METHOD__ );
00287         $lb->execute();
00288         return $titles;
00289     }
00290 }
00291 
00296 class StringPrefixSearch extends PrefixSearch {
00297 
00298     protected function titles( array $titles ) {
00299         return array_map( function( Title $t ) { return $t->getPrefixedText(); }, $titles );
00300     }
00301 
00302     protected function strings( array $strings ) {
00303         return $strings;
00304     }
00305 }