PrefixSearch.php 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. <?php
  2. /**
  3. * PrefixSearch - Handles searching prefixes of titles and finding any page
  4. * names that match. Used largely by the OpenSearch implementation.
  5. *
  6. * @ingroup Search
  7. */
  8. class PrefixSearch {
  9. /**
  10. * Do a prefix search of titles and return a list of matching page names.
  11. * @param string $search
  12. * @param int $limit
  13. * @param array $namespaces - used if query is not explicitely prefixed
  14. * @return array of strings
  15. */
  16. public static function titleSearch( $search, $limit, $namespaces=array() ) {
  17. $search = trim( $search );
  18. if( $search == '' ) {
  19. return array(); // Return empty result
  20. }
  21. $namespaces = self::validateNamespaces( $namespaces );
  22. $title = Title::newFromText( $search );
  23. if( $title && $title->getInterwiki() == '' ) {
  24. $ns = array($title->getNamespace());
  25. if($ns[0] == NS_MAIN)
  26. $ns = $namespaces; // no explicit prefix, use default namespaces
  27. return self::searchBackend(
  28. $ns, $title->getText(), $limit );
  29. }
  30. // Is this a namespace prefix?
  31. $title = Title::newFromText( $search . 'Dummy' );
  32. if( $title && $title->getText() == 'Dummy'
  33. && $title->getNamespace() != NS_MAIN
  34. && $title->getInterwiki() == '' ) {
  35. return self::searchBackend(
  36. array($title->getNamespace()), '', $limit );
  37. }
  38. return self::searchBackend( $namespaces, $search, $limit );
  39. }
  40. /**
  41. * Do a prefix search of titles and return a list of matching page names.
  42. * @param array $namespaces
  43. * @param string $search
  44. * @param int $limit
  45. * @return array of strings
  46. */
  47. protected static function searchBackend( $namespaces, $search, $limit ) {
  48. if( count($namespaces) == 1 ){
  49. $ns = $namespaces[0];
  50. if( $ns == NS_MEDIA ) {
  51. $namespaces = array(NS_FILE);
  52. } elseif( $ns == NS_SPECIAL ) {
  53. return self::specialSearch( $search, $limit );
  54. }
  55. }
  56. $srchres = array();
  57. if( wfRunHooks( 'PrefixSearchBackend', array( $namespaces, $search, $limit, &$srchres ) ) ) {
  58. return self::defaultSearchBackend( $namespaces, $search, $limit );
  59. }
  60. return $srchres;
  61. }
  62. /**
  63. * Prefix search special-case for Special: namespace.
  64. */
  65. protected static function specialSearch( $search, $limit ) {
  66. global $wgContLang;
  67. $searchKey = $wgContLang->caseFold( $search );
  68. // Unlike SpecialPage itself, we want the canonical forms of both
  69. // canonical and alias title forms...
  70. SpecialPage::initList();
  71. SpecialPage::initAliasList();
  72. $keys = array();
  73. foreach( array_keys( SpecialPage::$mList ) as $page ) {
  74. $keys[$wgContLang->caseFold( $page )] = $page;
  75. }
  76. foreach( $wgContLang->getSpecialPageAliases() as $page => $aliases ) {
  77. foreach( $aliases as $alias ) {
  78. $keys[$wgContLang->caseFold( $alias )] = $alias;
  79. }
  80. }
  81. ksort( $keys );
  82. $srchres = array();
  83. foreach( $keys as $pageKey => $page ) {
  84. if( $searchKey === '' || strpos( $pageKey, $searchKey ) === 0 ) {
  85. $srchres[] = Title::makeTitle( NS_SPECIAL, $page )->getPrefixedText();
  86. }
  87. if( count( $srchres ) >= $limit ) {
  88. break;
  89. }
  90. }
  91. return $srchres;
  92. }
  93. /**
  94. * Unless overridden by PrefixSearchBackend hook...
  95. * This is case-sensitive (First character may
  96. * be automatically capitalized by Title::secureAndSpit()
  97. * later on depending on $wgCapitalLinks)
  98. *
  99. * @param array $namespaces Namespaces to search in
  100. * @param string $search term
  101. * @param int $limit max number of items to return
  102. * @return array of title strings
  103. */
  104. protected static function defaultSearchBackend( $namespaces, $search, $limit ) {
  105. $ns = array_shift($namespaces); // support only one namespace
  106. if( in_array(NS_MAIN,$namespaces))
  107. $ns = NS_MAIN; // if searching on many always default to main
  108. // Prepare nested request
  109. $req = new FauxRequest(array (
  110. 'action' => 'query',
  111. 'list' => 'allpages',
  112. 'apnamespace' => $ns,
  113. 'aplimit' => $limit,
  114. 'apprefix' => $search
  115. ));
  116. // Execute
  117. $module = new ApiMain($req);
  118. $module->execute();
  119. // Get resulting data
  120. $data = $module->getResultData();
  121. // Reformat useful data for future printing by JSON engine
  122. $srchres = array ();
  123. foreach ((array)$data['query']['allpages'] as $pageinfo) {
  124. // Note: this data will no be printable by the xml engine
  125. // because it does not support lists of unnamed items
  126. $srchres[] = $pageinfo['title'];
  127. }
  128. return $srchres;
  129. }
  130. /**
  131. * Validate an array of numerical namespace indexes
  132. *
  133. * @param array $namespaces
  134. */
  135. protected static function validateNamespaces($namespaces){
  136. global $wgContLang;
  137. $validNamespaces = $wgContLang->getNamespaces();
  138. if( is_array($namespaces) && count($namespaces)>0 ){
  139. $valid = array();
  140. foreach ($namespaces as $ns){
  141. if( is_numeric($ns) && array_key_exists($ns, $validNamespaces) )
  142. $valid[] = $ns;
  143. }
  144. if( count($valid) > 0 )
  145. return $valid;
  146. }
  147. return array( NS_MAIN );
  148. }
  149. }