MediaWiki
REL1_24
|
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 }