[ Index ]

PHP Cross Reference of MediaWiki-1.24.0

title

Body

[close]

/includes/ -> PrefixSearch.php (source)

   1  <?php
   2  /**
   3   * Prefix search of page names.
   4   *
   5   * This program is free software; you can redistribute it and/or modify
   6   * it under the terms of the GNU General Public License as published by
   7   * the Free Software Foundation; either version 2 of the License, or
   8   * (at your option) any later version.
   9   *
  10   * This program is distributed in the hope that it will be useful,
  11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13   * GNU General Public License for more details.
  14   *
  15   * You should have received a copy of the GNU General Public License along
  16   * with this program; if not, write to the Free Software Foundation, Inc.,
  17   * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  18   * http://www.gnu.org/copyleft/gpl.html
  19   *
  20   * @file
  21   */
  22  
  23  /**
  24   * Handles searching prefixes of titles and finding any page
  25   * names that match. Used largely by the OpenSearch implementation.
  26   *
  27   * @ingroup Search
  28   */
  29  abstract class PrefixSearch {
  30      /**
  31       * Do a prefix search of titles and return a list of matching page names.
  32       * @deprecated Since 1.23, use TitlePrefixSearch or StringPrefixSearch classes
  33       *
  34       * @param string $search
  35       * @param int $limit
  36       * @param array $namespaces Used if query is not explicitly prefixed
  37       * @return array Array of strings
  38       */
  39  	public static function titleSearch( $search, $limit, $namespaces = array() ) {
  40          $prefixSearch = new StringPrefixSearch;
  41          return $prefixSearch->search( $search, $limit, $namespaces );
  42      }
  43  
  44      /**
  45       * Do a prefix search of titles and return a list of matching page names.
  46       *
  47       * @param string $search
  48       * @param int $limit
  49       * @param array $namespaces Used if query is not explicitly prefixed
  50       * @return array Array of strings or Title objects
  51       */
  52  	public function search( $search, $limit, $namespaces = array() ) {
  53          $search = trim( $search );
  54          if ( $search == '' ) {
  55              return array(); // Return empty result
  56          }
  57          $namespaces = $this->validateNamespaces( $namespaces );
  58  
  59          // Find a Title which is not an interwiki and is in NS_MAIN
  60          $title = Title::newFromText( $search );
  61          if ( $title && !$title->isExternal() ) {
  62              $ns = array( $title->getNamespace() );
  63              if ( $ns[0] == NS_MAIN ) {
  64                  $ns = $namespaces; // no explicit prefix, use default namespaces
  65              }
  66              return $this->searchBackend(
  67                  $ns, $title->getText(), $limit );
  68          }
  69  
  70          // Is this a namespace prefix?
  71          $title = Title::newFromText( $search . 'Dummy' );
  72          if ( $title && $title->getText() == 'Dummy'
  73              && $title->getNamespace() != NS_MAIN
  74              && !$title->isExternal() )
  75          {
  76              $namespaces = array( $title->getNamespace() );
  77              $search = '';
  78          }
  79  
  80          return $this->searchBackend( $namespaces, $search, $limit );
  81      }
  82  
  83      /**
  84       * Do a prefix search for all possible variants of the prefix
  85       * @param string $search
  86       * @param int $limit
  87       * @param array $namespaces
  88       *
  89       * @return array
  90       */
  91  	public function searchWithVariants( $search, $limit, array $namespaces ) {
  92          wfProfileIn( __METHOD__ );
  93          $searches = $this->search( $search, $limit, $namespaces );
  94  
  95          // if the content language has variants, try to retrieve fallback results
  96          $fallbackLimit = $limit - count( $searches );
  97          if ( $fallbackLimit > 0 ) {
  98              global $wgContLang;
  99  
 100              $fallbackSearches = $wgContLang->autoConvertToAllVariants( $search );
 101              $fallbackSearches = array_diff( array_unique( $fallbackSearches ), array( $search ) );
 102  
 103              foreach ( $fallbackSearches as $fbs ) {
 104                  $fallbackSearchResult = $this->search( $fbs, $fallbackLimit, $namespaces );
 105                  $searches = array_merge( $searches, $fallbackSearchResult );
 106                  $fallbackLimit -= count( $fallbackSearchResult );
 107  
 108                  if ( $fallbackLimit == 0 ) {
 109                      break;
 110                  }
 111              }
 112          }
 113          wfProfileOut( __METHOD__ );
 114          return $searches;
 115      }
 116  
 117      /**
 118       * When implemented in a descendant class, receives an array of Title objects and returns
 119       * either an unmodified array or an array of strings corresponding to titles passed to it.
 120       *
 121       * @param array $titles
 122       * @return array
 123       */
 124      abstract protected function titles( array $titles );
 125  
 126      /**
 127       * When implemented in a descendant class, receives an array of titles as strings and returns
 128       * either an unmodified array or an array of Title objects corresponding to strings received.
 129       *
 130       * @param array $strings
 131       *
 132       * @return array
 133       */
 134      abstract protected function strings( array $strings );
 135  
 136      /**
 137       * Do a prefix search of titles and return a list of matching page names.
 138       * @param array $namespaces
 139       * @param string $search
 140       * @param int $limit
 141       * @return array Array of strings
 142       */
 143  	protected function searchBackend( $namespaces, $search, $limit ) {
 144          if ( count( $namespaces ) == 1 ) {
 145              $ns = $namespaces[0];
 146              if ( $ns == NS_MEDIA ) {
 147                  $namespaces = array( NS_FILE );
 148              } elseif ( $ns == NS_SPECIAL ) {
 149                  return $this->titles( $this->specialSearch( $search, $limit ) );
 150              }
 151          }
 152          $srchres = array();
 153          if ( wfRunHooks( 'PrefixSearchBackend', array( $namespaces, $search, $limit, &$srchres ) ) ) {
 154              return $this->titles( $this->defaultSearchBackend( $namespaces, $search, $limit ) );
 155          }
 156          return $this->strings( $srchres );
 157      }
 158  
 159      /**
 160       * Prefix search special-case for Special: namespace.
 161       *
 162       * @param string $search Term
 163       * @param int $limit Max number of items to return
 164       * @return array
 165       */
 166  	protected function specialSearch( $search, $limit ) {
 167          global $wgContLang;
 168  
 169          $searchParts = explode( '/', $search, 2 );
 170          $searchKey = $searchParts[0];
 171          $subpageSearch = isset( $searchParts[1] ) ? $searchParts[1] : null;
 172  
 173          // Handle subpage search separately.
 174          if ( $subpageSearch !== null ) {
 175              // Try matching the full search string as a page name
 176              $specialTitle = Title::makeTitleSafe( NS_SPECIAL, $searchKey );
 177              if ( !$specialTitle ) {
 178                  return array();
 179              }
 180              $special = SpecialPageFactory::getPage( $specialTitle->getText() );
 181              if ( $special ) {
 182                  $subpages = $special->prefixSearchSubpages( $subpageSearch, $limit );
 183                  return array_map( function ( $sub ) use ( $specialTitle ) {
 184                      return $specialTitle->getSubpage( $sub );
 185                  }, $subpages );
 186              } else {
 187                  return array();
 188              }
 189          }
 190  
 191          # normalize searchKey, so aliases with spaces can be found - bug 25675
 192          $searchKey = str_replace( ' ', '_', $searchKey );
 193          $searchKey = $wgContLang->caseFold( $searchKey );
 194  
 195          // Unlike SpecialPage itself, we want the canonical forms of both
 196          // canonical and alias title forms...
 197          $keys = array();
 198          foreach ( SpecialPageFactory::getNames() as $page  ) {
 199              $keys[$wgContLang->caseFold( $page )] = $page;
 200          }
 201  
 202          foreach ( $wgContLang->getSpecialPageAliases() as $page => $aliases ) {
 203              if ( !in_array( $page, SpecialPageFactory::getNames() ) ) {# bug 20885
 204                  continue;
 205              }
 206  
 207              foreach ( $aliases as $alias ) {
 208                  $keys[$wgContLang->caseFold( $alias )] = $alias;
 209              }
 210          }
 211          ksort( $keys );
 212  
 213          $srchres = array();
 214          foreach ( $keys as $pageKey => $page ) {
 215              if ( $searchKey === '' || strpos( $pageKey, $searchKey ) === 0 ) {
 216                  // bug 27671: Don't use SpecialPage::getTitleFor() here because it
 217                  // localizes its input leading to searches for e.g. Special:All
 218                  // returning Spezial:MediaWiki-Systemnachrichten and returning
 219                  // Spezial:Alle_Seiten twice when $wgLanguageCode == 'de'
 220                  $srchres[] = Title::makeTitleSafe( NS_SPECIAL, $page );
 221              }
 222  
 223              if ( count( $srchres ) >= $limit ) {
 224                  break;
 225              }
 226          }
 227  
 228          return $srchres;
 229      }
 230  
 231      /**
 232       * Unless overridden by PrefixSearchBackend hook...
 233       * This is case-sensitive (First character may
 234       * be automatically capitalized by Title::secureAndSpit()
 235       * later on depending on $wgCapitalLinks)
 236       *
 237       * @param array $namespaces Namespaces to search in
 238       * @param string $search Term
 239       * @param int $limit Max number of items to return
 240       * @return array Array of Title objects
 241       */
 242  	protected function defaultSearchBackend( $namespaces, $search, $limit ) {
 243          $ns = array_shift( $namespaces ); // support only one namespace
 244          if ( in_array( NS_MAIN, $namespaces ) ) {
 245              $ns = NS_MAIN; // if searching on many always default to main
 246          }
 247  
 248          $t = Title::newFromText( $search, $ns );
 249          $prefix = $t ? $t->getDBkey() : '';
 250          $dbr = wfGetDB( DB_SLAVE );
 251          $res = $dbr->select( 'page',
 252              array( 'page_id', 'page_namespace', 'page_title' ),
 253              array(
 254                  'page_namespace' => $ns,
 255                  'page_title ' . $dbr->buildLike( $prefix, $dbr->anyString() )
 256              ),
 257              __METHOD__,
 258              array( 'LIMIT' => $limit, 'ORDER BY' => 'page_title' )
 259          );
 260          $srchres = array();
 261          foreach ( $res as $row ) {
 262              $srchres[] = Title::newFromRow( $row );
 263          }
 264          return $srchres;
 265      }
 266  
 267      /**
 268       * Validate an array of numerical namespace indexes
 269       *
 270       * @param array $namespaces
 271       * @return array (default: contains only NS_MAIN)
 272       */
 273  	protected function validateNamespaces( $namespaces ) {
 274          global $wgContLang;
 275  
 276          // We will look at each given namespace against wgContLang namespaces
 277          $validNamespaces = $wgContLang->getNamespaces();
 278          if ( is_array( $namespaces ) && count( $namespaces ) > 0 ) {
 279              $valid = array();
 280              foreach ( $namespaces as $ns ) {
 281                  if ( is_numeric( $ns ) && array_key_exists( $ns, $validNamespaces ) ) {
 282                      $valid[] = $ns;
 283                  }
 284              }
 285              if ( count( $valid ) > 0 ) {
 286                  return $valid;
 287              }
 288          }
 289  
 290          return array( NS_MAIN );
 291      }
 292  }
 293  
 294  /**
 295   * Performs prefix search, returning Title objects
 296   * @ingroup Search
 297   */
 298  class TitlePrefixSearch extends PrefixSearch {
 299  
 300  	protected function titles( array $titles ) {
 301          return $titles;
 302      }
 303  
 304  	protected function strings( array $strings ) {
 305          $titles = array_map( 'Title::newFromText', $strings );
 306          $lb = new LinkBatch( $titles );
 307          $lb->setCaller( __METHOD__ );
 308          $lb->execute();
 309          return $titles;
 310      }
 311  }
 312  
 313  /**
 314   * Performs prefix search, returning strings
 315   * @ingroup Search
 316   */
 317  class StringPrefixSearch extends PrefixSearch {
 318  
 319  	protected function titles( array $titles ) {
 320          return array_map( function ( Title $t ) {
 321              return $t->getPrefixedText();
 322          }, $titles );
 323      }
 324  
 325  	protected function strings( array $strings ) {
 326          return $strings;
 327      }
 328  }


Generated: Fri Nov 28 14:03:12 2014 Cross-referenced by PHPXref 0.7.1