diff options
author | Luke Shumaker <LukeShu@sbcglobal.net> | 2014-01-28 09:50:25 -0500 |
---|---|---|
committer | Luke Shumaker <LukeShu@sbcglobal.net> | 2014-01-28 09:50:25 -0500 |
commit | 5744df39e15f85c6cc8a9faf8924d77e76d2b216 (patch) | |
tree | a8c8dd40a94d1fa0d5377566aa5548ae55a163da /includes/search | |
parent | 4bb2aeca1d198391ca856aa16c40b8559c68daec (diff) | |
parent | 224b22a051051f6c2e494c3a2fb4adb42898e2d1 (diff) |
Merge branch 'archwiki'
Conflicts:
extensions/FluxBBAuthPlugin.php
extensions/SyntaxHighlight_GeSHi/README
extensions/SyntaxHighlight_GeSHi/SyntaxHighlight_GeSHi.class.php
extensions/SyntaxHighlight_GeSHi/SyntaxHighlight_GeSHi.i18n.php
extensions/SyntaxHighlight_GeSHi/SyntaxHighlight_GeSHi.php
extensions/SyntaxHighlight_GeSHi/geshi/docs/CHANGES
extensions/SyntaxHighlight_GeSHi/geshi/docs/THANKS
extensions/SyntaxHighlight_GeSHi/geshi/docs/TODO
extensions/SyntaxHighlight_GeSHi/geshi/docs/api/media/images/AbstractClass.png
extensions/SyntaxHighlight_GeSHi/geshi/docs/api/media/images/AbstractClass_logo.png
extensions/SyntaxHighlight_GeSHi/geshi/docs/api/media/images/AbstractMethod.png
extensions/SyntaxHighlight_GeSHi/geshi/docs/api/media/images/AbstractPrivateClass.png
extensions/SyntaxHighlight_GeSHi/geshi/docs/api/media/images/AbstractPrivateClass_logo.png
extensions/SyntaxHighlight_GeSHi/geshi/docs/api/media/images/AbstractPrivateMethod.png
extensions/SyntaxHighlight_GeSHi/geshi/docs/api/media/images/Class.png
extensions/SyntaxHighlight_GeSHi/geshi/docs/api/media/images/Class_logo.png
extensions/SyntaxHighlight_GeSHi/geshi/docs/api/media/images/Constant.png
extensions/SyntaxHighlight_GeSHi/geshi/docs/api/media/images/Constructor.png
extensions/SyntaxHighlight_GeSHi/geshi/docs/api/media/images/Destructor.png
extensions/SyntaxHighlight_GeSHi/geshi/docs/api/media/images/Function.png
extensions/SyntaxHighlight_GeSHi/geshi/docs/api/media/images/Global.png
extensions/SyntaxHighlight_GeSHi/geshi/docs/api/media/images/I.png
extensions/SyntaxHighlight_GeSHi/geshi/docs/api/media/images/Index.png
extensions/SyntaxHighlight_GeSHi/geshi/docs/api/media/images/Interface.png
extensions/SyntaxHighlight_GeSHi/geshi/docs/api/media/images/Interface_logo.png
extensions/SyntaxHighlight_GeSHi/geshi/docs/api/media/images/L.png
extensions/SyntaxHighlight_GeSHi/geshi/docs/api/media/images/Lminus.png
extensions/SyntaxHighlight_GeSHi/geshi/docs/api/media/images/Lplus.png
extensions/SyntaxHighlight_GeSHi/geshi/docs/api/media/images/Method.png
extensions/SyntaxHighlight_GeSHi/geshi/docs/api/media/images/Page.png
extensions/SyntaxHighlight_GeSHi/geshi/docs/api/media/images/Page_logo.png
extensions/SyntaxHighlight_GeSHi/geshi/docs/api/media/images/PrivateClass.png
extensions/SyntaxHighlight_GeSHi/geshi/docs/api/media/images/PrivateClass_logo.png
extensions/SyntaxHighlight_GeSHi/geshi/docs/api/media/images/PrivateMethod.png
extensions/SyntaxHighlight_GeSHi/geshi/docs/api/media/images/PrivateVariable.png
extensions/SyntaxHighlight_GeSHi/geshi/docs/api/media/images/StaticMethod.png
extensions/SyntaxHighlight_GeSHi/geshi/docs/api/media/images/StaticVariable.png
extensions/SyntaxHighlight_GeSHi/geshi/docs/api/media/images/T.png
extensions/SyntaxHighlight_GeSHi/geshi/docs/api/media/images/Tminus.png
extensions/SyntaxHighlight_GeSHi/geshi/docs/api/media/images/Tplus.png
extensions/SyntaxHighlight_GeSHi/geshi/docs/api/media/images/Variable.png
extensions/SyntaxHighlight_GeSHi/geshi/docs/api/media/images/blank.png
extensions/SyntaxHighlight_GeSHi/geshi/docs/api/media/images/class_folder.png
extensions/SyntaxHighlight_GeSHi/geshi/docs/api/media/images/file.png
extensions/SyntaxHighlight_GeSHi/geshi/docs/api/media/images/folder.png
extensions/SyntaxHighlight_GeSHi/geshi/docs/api/media/images/function_folder.png
extensions/SyntaxHighlight_GeSHi/geshi/docs/api/media/images/next_button.png
extensions/SyntaxHighlight_GeSHi/geshi/docs/api/media/images/next_button_disabled.png
extensions/SyntaxHighlight_GeSHi/geshi/docs/api/media/images/package.png
extensions/SyntaxHighlight_GeSHi/geshi/docs/api/media/images/package_folder.png
extensions/SyntaxHighlight_GeSHi/geshi/docs/api/media/images/previous_button.png
extensions/SyntaxHighlight_GeSHi/geshi/docs/api/media/images/previous_button_disabled.png
extensions/SyntaxHighlight_GeSHi/geshi/docs/api/media/images/private_class_logo.png
extensions/SyntaxHighlight_GeSHi/geshi/docs/api/media/images/tutorial.png
extensions/SyntaxHighlight_GeSHi/geshi/docs/api/media/images/tutorial_folder.png
extensions/SyntaxHighlight_GeSHi/geshi/docs/api/media/images/up_button.png
extensions/SyntaxHighlight_GeSHi/geshi/docs/geshi-doc.html
extensions/SyntaxHighlight_GeSHi/geshi/docs/geshi-doc.txt
extensions/SyntaxHighlight_GeSHi/geshi/geshi.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/4cs.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/6502acme.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/6502kickass.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/6502tasm.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/68000devpac.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/abap.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/actionscript.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/actionscript3.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/ada.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/algol68.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/apache.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/applescript.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/apt_sources.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/asm.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/asp.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/autoconf.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/autohotkey.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/autoit.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/avisynth.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/awk.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/bascomavr.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/bash.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/basic4gl.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/bf.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/bibtex.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/blitzbasic.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/bnf.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/boo.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/c.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/c_loadrunner.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/c_mac.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/caddcl.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/cadlisp.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/cfdg.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/cfm.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/chaiscript.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/cil.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/clojure.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/cmake.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/cobol.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/coffeescript.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/cpp-qt.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/cpp.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/csharp.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/css.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/cuesheet.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/d.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/dcs.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/delphi.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/diff.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/div.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/dos.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/dot.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/e.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/ecmascript.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/eiffel.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/email.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/epc.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/erlang.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/euphoria.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/f1.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/falcon.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/fo.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/fortran.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/freebasic.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/fsharp.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/gambas.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/gdb.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/genero.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/genie.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/gettext.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/glsl.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/gml.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/gnuplot.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/go.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/groovy.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/gwbasic.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/haskell.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/hicest.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/hq9plus.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/html4strict.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/html5.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/icon.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/idl.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/ini.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/inno.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/intercal.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/io.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/j.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/java.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/java5.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/javascript.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/jquery.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/kixtart.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/klonec.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/klonecpp.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/latex.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/lb.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/lisp.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/llvm.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/locobasic.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/logtalk.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/lolcode.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/lotusformulas.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/lotusscript.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/lscript.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/lsl2.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/lua.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/m68k.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/magiksf.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/make.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/mapbasic.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/matlab.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/mirc.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/mmix.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/modula2.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/modula3.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/mpasm.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/mxml.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/mysql.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/newlisp.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/nsis.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/oberon2.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/objc.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/objeck.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/ocaml-brief.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/ocaml.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/oobas.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/oracle11.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/oracle8.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/oxygene.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/oz.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/pascal.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/pcre.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/per.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/perl.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/perl6.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/pf.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/php-brief.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/php.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/pic16.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/pike.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/pixelbender.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/pli.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/plsql.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/postgresql.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/povray.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/powerbuilder.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/powershell.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/proftpd.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/progress.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/prolog.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/properties.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/providex.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/purebasic.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/pycon.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/python.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/q.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/qbasic.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/rails.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/rebol.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/reg.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/robots.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/rpmspec.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/rsplus.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/ruby.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/sas.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/scala.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/scheme.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/scilab.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/sdlbasic.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/smalltalk.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/smarty.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/sql.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/systemverilog.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/tcl.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/teraterm.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/text.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/thinbasic.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/tsql.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/typoscript.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/unicon.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/uscript.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/vala.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/vb.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/vbnet.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/verilog.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/vhdl.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/vim.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/visualfoxpro.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/visualprolog.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/whitespace.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/whois.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/winbatch.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/xbasic.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/xml.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/xorg_conf.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/xpp.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/yaml.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/z80.php
extensions/SyntaxHighlight_GeSHi/geshi/geshi/zxbasic.php
Diffstat (limited to 'includes/search')
-rw-r--r-- | includes/search/SearchEngine.php | 244 | ||||
-rw-r--r-- | includes/search/SearchIBM_DB2.php | 234 | ||||
-rw-r--r-- | includes/search/SearchMssql.php | 25 | ||||
-rw-r--r-- | includes/search/SearchMySQL.php | 74 | ||||
-rw-r--r-- | includes/search/SearchOracle.php | 170 | ||||
-rw-r--r-- | includes/search/SearchPostgres.php | 83 | ||||
-rw-r--r-- | includes/search/SearchSqlite.php | 52 | ||||
-rw-r--r-- | includes/search/SearchUpdate.php | 132 |
8 files changed, 468 insertions, 546 deletions
diff --git a/includes/search/SearchEngine.php b/includes/search/SearchEngine.php index 27a321ac..71c05d8b 100644 --- a/includes/search/SearchEngine.php +++ b/includes/search/SearchEngine.php @@ -45,7 +45,7 @@ class SearchEngine { */ protected $db; - function __construct($db = null) { + function __construct( $db = null ) { if ( $db ) { $this->db = $db; } else { @@ -58,8 +58,8 @@ class SearchEngine { * If title searches are not supported or disabled, return null. * STUB * - * @param $term String: raw search term - * @return SearchResultSet + * @param string $term raw search term + * @return SearchResultSet|Status|null */ function searchText( $term ) { return null; @@ -70,8 +70,8 @@ class SearchEngine { * If title searches are not supported or disabled, return null. * STUB * - * @param $term String: raw search term - * @return SearchResultSet + * @param string $term raw search term + * @return SearchResultSet|null */ function searchTitle( $term ) { return null; @@ -93,8 +93,9 @@ class SearchEngine { * @return Boolean */ public function supports( $feature ) { - switch( $feature ) { + switch ( $feature ) { case 'list-redirects': + case 'search-update': return true; case 'title-suffix-filter': default: @@ -118,7 +119,7 @@ class SearchEngine { * on text to be used for searching or updating search index. * Default implementation does nothing (simply returns $string). * - * @param $string string: String to process + * @param string $string String to process * @return string */ public function normalizeText( $string ) { @@ -163,7 +164,7 @@ class SearchEngine { /** * Really find the title match. - * @return null|\Title + * @return null|Title */ private static function getNearMatchInternal( $searchterm ) { global $wgContLang, $wgEnableSearchContributorsByIP; @@ -183,10 +184,15 @@ class SearchEngine { # Exact match? No need to look further. $title = Title::newFromText( $term ); - if ( is_null( $title ) ){ + if ( is_null( $title ) ) { return null; } + # Try files if searching in the Media: namespace + if ( $title->getNamespace() == NS_MEDIA ) { + $title = Title::makeTitle( NS_FILE, $title->getText() ); + } + if ( $title->isSpecialPage() || $title->isExternal() || $title->exists() ) { return $title; } @@ -197,22 +203,23 @@ class SearchEngine { return $title; } + if ( !wfRunHooks( 'SearchAfterNoDirectMatch', array( $term, &$title ) ) ) { + return $title; + } + # Now try all lower case (i.e. first letter capitalized) - # $title = Title::newFromText( $wgContLang->lc( $term ) ); if ( $title && $title->exists() ) { return $title; } # Now try capitalized string - # $title = Title::newFromText( $wgContLang->ucwords( $term ) ); if ( $title && $title->exists() ) { return $title; } # Now try all upper case - # $title = Title::newFromText( $wgContLang->uc( $term ) ); if ( $title && $title->exists() ) { return $title; @@ -233,7 +240,6 @@ class SearchEngine { $title = Title::newFromText( $searchterm ); - # Entering an IP address goes to the contributions page if ( $wgEnableSearchContributorsByIP ) { if ( ( $title->getNamespace() == NS_USER && User::isIP( $title->getText() ) ) @@ -242,7 +248,6 @@ class SearchEngine { } } - # Entering a user goes to the user page whether it's there or not if ( $title->getNamespace() == NS_USER ) { return $title; @@ -327,8 +332,9 @@ class SearchEngine { $parsed = substr( $query, strlen( $prefix ) + 1 ); } } - if ( trim( $parsed ) == '' ) + if ( trim( $parsed ) == '' ) { $parsed = $query; // prefix was the whole query + } wfRunHooks( 'SearchEngineReplacePrefixesComplete', array( $this, $query, &$parsed ) ); @@ -416,8 +422,9 @@ class SearchEngine { $formatted = array_map( array( $wgContLang, 'getFormattedNsText' ), $namespaces ); foreach ( $formatted as $key => $ns ) { - if ( empty( $ns ) ) + if ( empty( $ns ) ) { $formatted[$key] = wfMessage( 'blanknamespace' )->text(); + } } return $formatted; } @@ -447,23 +454,46 @@ class SearchEngine { * Load up the appropriate search engine class for the currently * active database backend, and return a configured instance. * + * @param String $type Type of search backend, if not the default * @return SearchEngine */ - public static function create() { + public static function create( $type = null ) { global $wgSearchType; $dbr = null; - if ( $wgSearchType ) { + + $alternatives = self::getSearchTypes(); + + if ( $type && in_array( $type, $alternatives ) ) { + $class = $type; + } elseif ( $wgSearchType !== null ) { $class = $wgSearchType; } else { $dbr = wfGetDB( DB_SLAVE ); $class = $dbr->getSearchEngine(); } + $search = new $class( $dbr ); $search->setLimitOffset( 0, 0 ); return $search; } /** + * Return the search engines we support. If only $wgSearchType + * is set, it'll be an array of just that one item. + * + * @return array + */ + public static function getSearchTypes() { + global $wgSearchType, $wgSearchTypeAlternatives; + static $alternatives = null; + if ( $alternatives === null ) { + $alternatives = $wgSearchTypeAlternatives ?: array(); + array_unshift( $alternatives, $wgSearchType ); + } + return $alternatives; + } + + /** * Create or update the search index record for the given page. * Title and text should be pre-processed. * STUB @@ -489,6 +519,18 @@ class SearchEngine { } /** + * Delete an indexed page + * Title should be pre-processed. + * STUB + * + * @param Integer $id Page id that was deleted + * @param String $title Title of page that was deleted + */ + function delete( $id, $title ) { + // no-op + } + + /** * Get OpenSearch suggestion template * * @return String @@ -505,6 +547,31 @@ class SearchEngine { return $wgCanonicalServer . wfScript( 'api' ) . '?action=opensearch&search={searchTerms}&namespace=' . $ns; } } + + /** + * Get the raw text for updating the index from a content object + * Nicer search backends could possibly do something cooler than + * just returning raw text + * + * @todo This isn't ideal, we'd really like to have content-specific handling here + * @param Title $t Title we're indexing + * @param Content $c Content of the page to index + * @return string + */ + public function getTextFromContent( Title $t, Content $c = null ) { + return $c ? $c->getTextForSearchIndex() : ''; + } + + /** + * If an implementation of SearchEngine handles all of its own text processing + * in getTextFromContent() and doesn't require SearchUpdate::updateText()'s + * rather silly handling, it should return true here instead. + * + * @return bool + */ + public function textAlreadyUpdatedForIndex() { + return false; + } } /** @@ -637,26 +704,30 @@ class SqlSearchResultSet extends SearchResultSet { } function numRows() { - if ( $this->mResultSet === false ) + if ( $this->mResultSet === false ) { return false; + } return $this->mResultSet->numRows(); } function next() { - if ( $this->mResultSet === false ) + if ( $this->mResultSet === false ) { return false; + } $row = $this->mResultSet->fetchObject(); - if ( $row === false ) + if ( $row === false ) { return false; + } return SearchResult::newFromRow( $row ); } function free() { - if ( $this->mResultSet === false ) + if ( $this->mResultSet === false ) { return false; + } $this->mResultSet->free(); } @@ -669,7 +740,6 @@ class SearchResultTooMany { # # Some search engines may bail out if too many matches are found } - /** * @todo FIXME: This class is horribly factored. It would probably be better to * have a useful base class to which you pass some standard information, then @@ -747,8 +817,9 @@ class SearchResult { wfRunHooks( 'SearchResultInitFromTitle', array( $title, &$id ) ); $this->mRevision = Revision::newFromTitle( $this->mTitle, $id, Revision::READ_NORMAL ); - if ( $this->mTitle->getNamespace() === NS_FILE ) + if ( $this->mTitle->getNamespace() === NS_FILE ) { $this->mImage = wfFindFile( $this->mTitle ); + } } } @@ -758,8 +829,9 @@ class SearchResult { * @return Boolean */ function isBrokenTitle() { - if ( is_null( $this->mTitle ) ) + if ( is_null( $this->mTitle ) ) { return true; + } return false; } @@ -791,31 +863,35 @@ class SearchResult { */ protected function initText() { if ( !isset( $this->mText ) ) { - if ( $this->mRevision != null ) - $this->mText = $this->mRevision->getText(); - else // TODO: can we fetch raw wikitext for commons images? + if ( $this->mRevision != null ) { + $this->mText = SearchEngine::create() + ->getTextFromContent( $this->mTitle, $this->mRevision->getContent() ); + } else { // TODO: can we fetch raw wikitext for commons images? $this->mText = ''; - + } } } /** - * @param $terms Array: terms to highlight + * @param array $terms terms to highlight * @return String: highlighted text snippet, null (and not '') if not supported */ function getTextSnippet( $terms ) { - global $wgUser, $wgAdvancedSearchHighlighting; + global $wgAdvancedSearchHighlighting; $this->initText(); - list( $contextlines, $contextchars ) = SearchEngine::userHighlightPrefs( $wgUser ); + + // TODO: make highliter take a content object. Make ContentHandler a factory for SearchHighliter. + list( $contextlines, $contextchars ) = SearchEngine::userHighlightPrefs(); $h = new SearchHighlighter(); - if ( $wgAdvancedSearchHighlighting ) + if ( $wgAdvancedSearchHighlighting ) { return $h->highlightText( $this->mText, $terms, $contextlines, $contextchars ); - else + } else { return $h->highlightSimple( $this->mText, $terms, $contextlines, $contextchars ); + } } /** - * @param $terms Array: terms to highlight + * @param array $terms terms to highlight * @return String: highlighted title, '' if not supported */ function getTitleSnippet( $terms ) { @@ -823,7 +899,7 @@ class SearchResult { } /** - * @param $terms Array: terms to highlight + * @param array $terms terms to highlight * @return String: highlighted redirect name (redirect to this page), '' if none or not supported */ function getRedirectSnippet( $terms ) { @@ -855,10 +931,11 @@ class SearchResult { * @return String: timestamp */ function getTimestamp() { - if ( $this->mRevision ) + if ( $this->mRevision ) { return $this->mRevision->getTimestamp(); - elseif ( $this->mImage ) + } elseif ( $this->mImage ) { return $this->mImage->getTimestamp(); + } return ''; } @@ -934,7 +1011,7 @@ class SearchHighlighter { * Default implementation of wikitext highlighting * * @param $text String - * @param $terms Array: terms to highlight (unescaped) + * @param array $terms terms to highlight (unescaped) * @param $contextlines Integer * @param $contextchars Integer * @return String @@ -944,8 +1021,9 @@ class SearchHighlighter { global $wgSearchHighlightBoundaries; $fname = __METHOD__; - if ( $text == '' ) + if ( $text == '' ) { return ''; + } // spli text into text + templates/links/tables $spat = "/(\\{\\{)|(\\[\\[[^\\]:]+:)|(\n\\{\\|)"; @@ -962,7 +1040,7 @@ class SearchHighlighter { } $spat .= '/'; $textExt = array(); // text extracts - $otherExt = array(); // other extracts + $otherExt = array(); // other extracts wfProfileIn( "$fname-split" ); $start = 0; $textLen = strlen( $text ); @@ -976,8 +1054,9 @@ class SearchHighlighter { if ( $key == 2 ) { // see if this is an image link $ns = substr( $val[0], 2, - 1 ); - if ( $wgContLang->getNsIndex( $ns ) != NS_FILE ) + if ( $wgContLang->getNsIndex( $ns ) != NS_FILE ) { break; + } } $epat = $endPatterns[$key]; @@ -998,7 +1077,7 @@ class SearchHighlighter { $len = strlen( $endMatches[2][0] ); $off = $endMatches[2][1]; $this->splitAndAdd( $otherExt, $count, - substr( $text, $start, $off + $len - $start ) ); + substr( $text, $start, $off + $len - $start ) ); $start = $off + $len; $found = true; break; @@ -1111,7 +1190,7 @@ class SearchHighlighter { // if begin of the article contains the whole phrase, show only that !! if ( array_key_exists( $first, $snippets ) && preg_match( $pat1, $snippets[$first] ) && $offsets[$first] < $contextchars * 2 ) { - $snippets = array ( $first => $snippets[$first] ); + $snippets = array( $first => $snippets[$first] ); } // calc by how much to extend existing snippets @@ -1131,8 +1210,8 @@ class SearchHighlighter { // add more lines $add = $index + 1; while ( $len < $targetchars - 20 - && array_key_exists( $add, $all ) - && !array_key_exists( $add, $snippets ) ) { + && array_key_exists( $add, $all ) + && !array_key_exists( $add, $snippets ) ) { $offsets[$add] = 0; $tt = "\n" . $this->extract( $all[$add], 0, $targetchars - $len, $offsets[$add] ); $extended[$add] = $tt; @@ -1142,22 +1221,24 @@ class SearchHighlighter { } } - // $snippets = array_map('htmlspecialchars', $extended); + // $snippets = array_map( 'htmlspecialchars', $extended ); $snippets = $extended; $last = - 1; $extract = ''; foreach ( $snippets as $index => $line ) { - if ( $last == - 1 ) + if ( $last == - 1 ) { $extract .= $line; // first line - elseif ( $last + 1 == $index && $offsets[$last] + strlen( $snippets[$last] ) >= strlen( $all[$last] ) ) + } elseif ( $last + 1 == $index && $offsets[$last] + strlen( $snippets[$last] ) >= strlen( $all[$last] ) ) { $extract .= " " . $line; // continous lines - else + } else { $extract .= '<b> ... </b>' . $line; + } $last = $index; } - if ( $extract ) + if ( $extract ) { $extract .= '<b> ... </b>'; + } $processed = array(); foreach ( $terms as $term ) { @@ -1177,7 +1258,7 @@ class SearchHighlighter { /** * Split text into lines and add it to extracts array * - * @param $extracts Array: index -> $line + * @param array $extracts index -> $line * @param $count Integer * @param $text String */ @@ -1185,8 +1266,9 @@ class SearchHighlighter { $split = explode( "\n", $this->mCleanWikitext ? $this->removeWiki( $text ) : $text ); foreach ( $split as $line ) { $tt = trim( $line ); - if ( $tt ) + if ( $tt ) { $extracts[$count++] = $tt; + } } } @@ -1232,7 +1314,7 @@ class SearchHighlighter { $posEnd = $end; } - if ( $end > $start ) { + if ( $end > $start ) { return substr( $text, $start, $end - $start ); } else { return ''; @@ -1260,8 +1342,9 @@ class SearchHighlighter { while ( $char >= 0x80 && $char < 0xc0 ) { // skip trailing bytes $point++; - if ( $point >= strlen( $text ) ) + if ( $point >= strlen( $text ) ) { return strlen( $text ); + } $char = ord( $text[$point] ); } return $point; @@ -1272,33 +1355,37 @@ class SearchHighlighter { /** * Search extracts for a pattern, and return snippets * - * @param $pattern String: regexp for matching lines - * @param $extracts Array: extracts to search + * @param string $pattern regexp for matching lines + * @param array $extracts extracts to search * @param $linesleft Integer: number of extracts to make * @param $contextchars Integer: length of snippet - * @param $out Array: map for highlighted snippets - * @param $offsets Array: map of starting points of snippets + * @param array $out map for highlighted snippets + * @param array $offsets map of starting points of snippets * @protected */ function process( $pattern, $extracts, &$linesleft, &$contextchars, &$out, &$offsets ) { - if ( $linesleft == 0 ) + if ( $linesleft == 0 ) { return; // nothing to do + } foreach ( $extracts as $index => $line ) { - if ( array_key_exists( $index, $out ) ) + if ( array_key_exists( $index, $out ) ) { continue; // this line already highlighted + } $m = array(); - if ( !preg_match( $pattern, $line, $m, PREG_OFFSET_CAPTURE ) ) + if ( !preg_match( $pattern, $line, $m, PREG_OFFSET_CAPTURE ) ) { continue; + } $offset = $m[0][1]; $len = strlen( $m[0][0] ); - if ( $offset + $len < $contextchars ) + if ( $offset + $len < $contextchars ) { $begin = 0; - elseif ( $len > $contextchars ) + } elseif ( $len > $contextchars ) { $begin = $offset; - else + } else { $begin = $offset + intval( ( $len - $contextchars ) / 2 ); + } $end = $begin + $contextchars; @@ -1307,8 +1394,9 @@ class SearchHighlighter { $out[$index] = $this->extract( $line, $begin, $end, $posBegin ); $offsets[$index] = $posBegin; $linesleft--; - if ( $linesleft == 0 ) + if ( $linesleft == 0 ) { return; + } } } @@ -1321,12 +1409,12 @@ class SearchHighlighter { $fname = __METHOD__; wfProfileIn( $fname ); - // $text = preg_replace("/'{2,5}/", "", $text); - // $text = preg_replace("/\[[a-z]+:\/\/[^ ]+ ([^]]+)\]/", "\\2", $text); - // $text = preg_replace("/\[\[([^]|]+)\]\]/", "\\1", $text); - // $text = preg_replace("/\[\[([^]]+\|)?([^|]]+)\]\]/", "\\2", $text); - // $text = preg_replace("/\\{\\|(.*?)\\|\\}/", "", $text); - // $text = preg_replace("/\\[\\[[A-Za-z_-]+:([^|]+?)\\]\\]/", "", $text); + // $text = preg_replace( "/'{2,5}/", "", $text ); + // $text = preg_replace( "/\[[a-z]+:\/\/[^ ]+ ([^]]+)\]/", "\\2", $text ); + // $text = preg_replace( "/\[\[([^]|]+)\]\]/", "\\1", $text ); + // $text = preg_replace( "/\[\[([^]]+\|)?([^|]]+)\]\]/", "\\2", $text ); + // $text = preg_replace( "/\\{\\|(.*?)\\|\\}/", "", $text ); + // $text = preg_replace( "/\\[\\[[A-Za-z_-]+:([^|]+?)\\]\\]/", "", $text ); $text = preg_replace( "/\\{\\{([^|]+?)\\}\\}/", "", $text ); $text = preg_replace( "/\\{\\{([^|]+\\|)(.*?)\\}\\}/", "\\2", $text ); $text = preg_replace( "/\\[\\[([^|]+?)\\]\\]/", "\\1", $text ); @@ -1349,16 +1437,17 @@ class SearchHighlighter { */ function linkReplace( $matches ) { $colon = strpos( $matches[1], ':' ); - if ( $colon === false ) + if ( $colon === false ) { return $matches[2]; // replace with caption + } global $wgContLang; $ns = substr( $matches[1], 0, $colon ); $index = $wgContLang->getNsIndex( $ns ); - if ( $index !== false && ( $index == NS_FILE || $index == NS_CATEGORY ) ) + if ( $index !== false && ( $index == NS_FILE || $index == NS_CATEGORY ) ) { return $matches[0]; // return the whole thing - else + } else { return $matches[2]; - + } } /** @@ -1408,8 +1497,7 @@ class SearchHighlighter { $line = htmlspecialchars( $pre . $found . $post ); $pat2 = '/(' . $terms . ")/i"; - $line = preg_replace( $pat2, - "<span class='searchmatch'>\\1</span>", $line ); + $line = preg_replace( $pat2, "<span class='searchmatch'>\\1</span>", $line ); $extract .= "${line}\n"; } diff --git a/includes/search/SearchIBM_DB2.php b/includes/search/SearchIBM_DB2.php deleted file mode 100644 index 51ed000f..00000000 --- a/includes/search/SearchIBM_DB2.php +++ /dev/null @@ -1,234 +0,0 @@ -<?php -/** - * IBM DB2 search engine - * - * Copyright © 2004 Brion Vibber <brion@pobox.com> - * http://www.mediawiki.org/ - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * http://www.gnu.org/copyleft/gpl.html - * - * @file - * @ingroup Search - */ - -/** - * Search engine hook base class for IBM DB2 - * @ingroup Search - */ -class SearchIBM_DB2 extends SearchEngine { - - /** - * Creates an instance of this class - * @param $db DatabaseIbm_db2: database object - */ - function __construct($db) { - parent::__construct( $db ); - } - - /** - * Perform a full text search query and return a result set. - * - * @param $term String: raw search term - * @return SqlSearchResultSet - */ - function searchText( $term ) { - $resultSet = $this->db->resultObject($this->db->query($this->getQuery($this->filter($term), true))); - return new SqlSearchResultSet($resultSet, $this->searchTerms); - } - - /** - * Perform a title-only search query and return a result set. - * - * @param $term String: taw search term - * @return SqlSearchResultSet - */ - function searchTitle($term) { - $resultSet = $this->db->resultObject($this->db->query($this->getQuery($this->filter($term), false))); - return new SqlSearchResultSet($resultSet, $this->searchTerms); - } - - - /** - * Return a partial WHERE clause to exclude redirects, if so set - * @return String - */ - function queryRedirect() { - if ($this->showRedirects) { - return ''; - } else { - return 'AND page_is_redirect=0'; - } - } - - /** - * Return a partial WHERE clause to limit the search to the given namespaces - * @return String - */ - function queryNamespaces() { - if( is_null($this->namespaces) ) - return ''; - $namespaces = implode(',', $this->namespaces); - if ($namespaces == '') { - $namespaces = '0'; - } - return 'AND page_namespace IN (' . $namespaces . ')'; - } - - /** - * Return a LIMIT clause to limit results on the query. - * @return String - */ - function queryLimit( $sql ) { - return $this->db->limitResult($sql, $this->limit, $this->offset); - } - - /** - * Does not do anything for generic search engine - * subclasses may define this though - * @return String - */ - function queryRanking($filteredTerm, $fulltext) { - // requires Net Search Extender or equivalent - // return ' ORDER BY score(1)'; - return ''; - } - - /** - * Construct the full SQL query to do the search. - * The guts shoulds be constructed in queryMain() - * @param $filteredTerm String - * @param $fulltext Boolean - * @return String - */ - function getQuery( $filteredTerm, $fulltext ) { - return $this->queryLimit($this->queryMain($filteredTerm, $fulltext) . ' ' . - $this->queryRedirect() . ' ' . - $this->queryNamespaces() . ' ' . - $this->queryRanking( $filteredTerm, $fulltext ) . ' '); - } - - - /** - * Picks which field to index on, depending on what type of query. - * @param $fulltext Boolean - * @return String - */ - function getIndexField($fulltext) { - return $fulltext ? 'si_text' : 'si_title'; - } - - /** - * Get the base part of the search query. - * - * @param $filteredTerm String - * @param $fulltext Boolean - * @return String - */ - function queryMain( $filteredTerm, $fulltext ) { - $match = $this->parseQuery($filteredTerm, $fulltext); - $page = $this->db->tableName('page'); - $searchindex = $this->db->tableName('searchindex'); - return 'SELECT page_id, page_namespace, page_title ' . - "FROM $page,$searchindex " . - 'WHERE page_id=si_page AND ' . $match; - } - - /** @todo document - * @return string - */ - function parseQuery($filteredText, $fulltext) { - global $wgContLang; - $lc = SearchEngine::legalSearchChars(); - $this->searchTerms = array(); - - # @todo FIXME: This doesn't handle parenthetical expressions. - $m = array(); - $q = array(); - - if (preg_match_all('/([-+<>~]?)(([' . $lc . ']+)(\*?)|"[^"]*")/', - $filteredText, $m, PREG_SET_ORDER)) { - foreach($m as $terms) { - - // Search terms in all variant forms, only - // apply on wiki with LanguageConverter - $temp_terms = $wgContLang->autoConvertToAllVariants( $terms[2] ); - if( is_array( $temp_terms )) { - $temp_terms = array_unique( array_values( $temp_terms )); - foreach( $temp_terms as $t ) - $q[] = $terms[1] . $wgContLang->normalizeForSearch( $t ); - } - else - $q[] = $terms[1] . $wgContLang->normalizeForSearch( $terms[2] ); - - if (!empty($terms[3])) { - $regexp = preg_quote( $terms[3], '/' ); - if ($terms[4]) - $regexp .= "[0-9A-Za-z_]+"; - } else { - $regexp = preg_quote(str_replace('"', '', $terms[2]), '/'); - } - $this->searchTerms[] = $regexp; - } - } - - $searchon = $this->db->strencode(join(',', $q)); - $field = $this->getIndexField($fulltext); - - // requires Net Search Extender or equivalent - //return " CONTAINS($field, '$searchon') > 0 "; - - return " lcase($field) LIKE lcase('%$searchon%')"; - } - - /** - * Create or update the search index record for the given page. - * Title and text should be pre-processed. - * - * @param $id Integer - * @param $title String - * @param $text String - */ - function update($id, $title, $text) { - $dbw = wfGetDB(DB_MASTER); - $dbw->replace('searchindex', - array('si_page'), - array( - 'si_page' => $id, - 'si_title' => $title, - 'si_text' => $text - ), 'SearchIBM_DB2::update' ); - // ? - //$dbw->query("CALL ctx_ddl.sync_index('si_text_idx')"); - //$dbw->query("CALL ctx_ddl.sync_index('si_title_idx')"); - } - - /** - * Update a search index record's title only. - * Title should be pre-processed. - * - * @param $id Integer - * @param $title String - */ - function updateTitle($id, $title) { - $dbw = wfGetDB(DB_MASTER); - - $dbw->update('searchindex', - array('si_title' => $title), - array('si_page' => $id), - 'SearchIBM_DB2::updateTitle', - array()); - } -} diff --git a/includes/search/SearchMssql.php b/includes/search/SearchMssql.php index 69c92ba3..cbc1a7a7 100644 --- a/includes/search/SearchMssql.php +++ b/includes/search/SearchMssql.php @@ -38,7 +38,7 @@ class SearchMssql extends SearchEngine { /** * Perform a full text search query and return a result set. * - * @param $term String: raw search term + * @param string $term raw search term * @return MssqlSearchResultSet * @access public */ @@ -50,7 +50,7 @@ class SearchMssql extends SearchEngine { /** * Perform a title-only search query and return a result set. * - * @param $term String: raw search term + * @param string $term raw search term * @return MssqlSearchResultSet * @access public */ @@ -59,7 +59,6 @@ class SearchMssql extends SearchEngine { return new MssqlSearchResultSet( $resultSet, $this->searchTerms ); } - /** * Return a partial WHERE clause to exclude redirects, if so set * @@ -78,7 +77,7 @@ class SearchMssql extends SearchEngine { * Return a partial WHERE clause to limit the search to the given namespaces * * @return String - * @private + * @private */ function queryNamespaces() { $namespaces = implode( ',', $this->namespaces ); @@ -144,9 +143,9 @@ class SearchMssql extends SearchEngine { */ function queryMain( $filteredTerm, $fulltext ) { $match = $this->parseQuery( $filteredTerm, $fulltext ); - $page = $this->db->tableName( 'page' ); + $page = $this->db->tableName( 'page' ); $searchindex = $this->db->tableName( 'searchindex' ); - + return 'SELECT page_id, page_namespace, page_title, ftindex.[RANK]' . "FROM $page,FREETEXTTABLE($searchindex , $match, LANGUAGE 'English') as ftindex " . 'WHERE page_id=ftindex.[KEY] '; @@ -171,8 +170,9 @@ class SearchMssql extends SearchEngine { if ( !empty( $terms[3] ) ) { $regexp = preg_quote( $terms[3], '/' ); - if ( $terms[4] ) + if ( $terms[4] ) { $regexp .= "[0-9A-Za-z_]+"; + } } else { $regexp = preg_quote( str_replace( '"', '', $terms[2] ), '/' ); } @@ -192,11 +192,11 @@ class SearchMssql extends SearchEngine { * @param $id Integer * @param $title String * @param $text String - * @return bool|\ResultWrapper + * @return bool|ResultWrapper */ function update( $id, $title, $text ) { // We store the column data as UTF-8 byte order marked binary stream - // because we are invoking the plain text IFilter on it so that, and we want it + // because we are invoking the plain text IFilter on it so that, and we want it // to properly decode the stream as UTF-8. SQL doesn't support UTF8 as a data type // but the indexer will correctly handle it by this method. Since all we are doing // is passing this data to the indexer and never retrieving it via PHP, this will save space @@ -215,7 +215,7 @@ class SearchMssql extends SearchEngine { * * @param $id Integer * @param $title String - * @return bool|\ResultWrapper + * @return bool|ResultWrapper */ function updateTitle( $id, $title ) { $table = $this->db->tableName( 'searchindex' ); @@ -248,10 +248,9 @@ class MssqlSearchResultSet extends SearchResultSet { function next() { $row = $this->mResultSet->fetchObject(); - if ( $row === false ) + if ( $row === false ) { return false; + } return new SearchResult( $row ); } } - - diff --git a/includes/search/SearchMySQL.php b/includes/search/SearchMySQL.php index 5cee03e0..b2bc1c26 100644 --- a/includes/search/SearchMySQL.php +++ b/includes/search/SearchMySQL.php @@ -57,12 +57,12 @@ class SearchMySQL extends SearchEngine { # @todo FIXME: This doesn't handle parenthetical expressions. $m = array(); - if( preg_match_all( '/([-+<>~]?)(([' . $lc . ']+)(\*?)|"[^"]*")/', - $filteredText, $m, PREG_SET_ORDER ) ) { - foreach( $m as $bits ) { + if ( preg_match_all( '/([-+<>~]?)(([' . $lc . ']+)(\*?)|"[^"]*")/', + $filteredText, $m, PREG_SET_ORDER ) ) { + foreach ( $m as $bits ) { @list( /* all */, $modifier, $term, $nonQuoted, $wildcard ) = $bits; - if( $nonQuoted != '' ) { + if ( $nonQuoted != '' ) { $term = $nonQuoted; $quote = ''; } else { @@ -70,8 +70,10 @@ class SearchMySQL extends SearchEngine { $quote = '"'; } - if( $searchon !== '' ) $searchon .= ' '; - if( $this->strictMatching && ($modifier == '') ) { + if ( $searchon !== '' ) { + $searchon .= ' '; + } + if ( $this->strictMatching && ( $modifier == '' ) ) { // If we leave this out, boolean op defaults to OR which is rarely helpful. $modifier = '+'; } @@ -79,7 +81,7 @@ class SearchMySQL extends SearchEngine { // Some languages such as Serbian store the input form in the search index, // so we may need to search for matches in multiple writing system variants. $convertedVariants = $wgContLang->autoConvertToAllVariants( $term ); - if( is_array( $convertedVariants ) ) { + if ( is_array( $convertedVariants ) ) { $variants = array_unique( array_values( $convertedVariants ) ); } else { $variants = array( $term ); @@ -99,11 +101,12 @@ class SearchMySQL extends SearchEngine { $strippedVariants = array_unique( $strippedVariants ); $searchon .= $modifier; - if( count( $strippedVariants) > 1 ) + if ( count( $strippedVariants ) > 1 ) { $searchon .= '('; - foreach( $strippedVariants as $stripped ) { + } + foreach ( $strippedVariants as $stripped ) { $stripped = $this->normalizeText( $stripped ); - if( $nonQuoted && strpos( $stripped, ' ' ) !== false ) { + if ( $nonQuoted && strpos( $stripped, ' ' ) !== false ) { // Hack for Chinese: we need to toss in quotes for // multiple-character phrases since normalizeForSearch() // added spaces between them to make word breaks. @@ -111,8 +114,9 @@ class SearchMySQL extends SearchEngine { } $searchon .= "$quote$stripped$quote$wildcard "; } - if( count( $strippedVariants) > 1 ) + if ( count( $strippedVariants ) > 1 ) { $searchon .= ')'; + } // Match individual terms or quoted phrase in result highlighting... // Note that variants will be introduced in a later stage for highlighting! @@ -134,8 +138,8 @@ class SearchMySQL extends SearchEngine { global $wgContLang; $regex = preg_quote( $string, '/' ); - if( $wgContLang->hasWordBreaks() ) { - if( $wildcard ) { + if ( $wgContLang->hasWordBreaks() ) { + if ( $wildcard ) { // Don't cut off the final bit! $regex = "\b$regex"; } else { @@ -156,7 +160,7 @@ class SearchMySQL extends SearchEngine { /** * Perform a full text search query and return a result set. * - * @param $term String: raw search term + * @param string $term raw search term * @return MySQLSearchResultSet */ function searchText( $term ) { @@ -166,7 +170,7 @@ class SearchMySQL extends SearchEngine { /** * Perform a title-only search query and return a result set. * - * @param $term String: raw search term + * @param string $term raw search term * @return MySQLSearchResultSet */ function searchTitle( $term ) { @@ -177,7 +181,9 @@ class SearchMySQL extends SearchEngine { global $wgCountTotalSearchHits; // This seems out of place, why is this called with empty term? - if ( trim( $term ) === '' ) return null; + if ( trim( $term ) === '' ) { + return null; + } $filteredTerm = $this->filter( $term ); $query = $this->getQuery( $filteredTerm, $fulltext ); @@ -187,7 +193,7 @@ class SearchMySQL extends SearchEngine { ); $total = null; - if( $wgCountTotalSearchHits ) { + if ( $wgCountTotalSearchHits ) { $query = $this->getCountQuery( $filteredTerm, $fulltext ); $totalResult = $this->db->select( $query['tables'], $query['fields'], $query['conds'], @@ -195,7 +201,7 @@ class SearchMySQL extends SearchEngine { ); $row = $totalResult->fetchObject(); - if( $row ) { + if ( $row ) { $total = intval( $row->c ); } $totalResult->free(); @@ -205,12 +211,11 @@ class SearchMySQL extends SearchEngine { } public function supports( $feature ) { - switch( $feature ) { - case 'list-redirects': + switch ( $feature ) { case 'title-suffix-filter': return true; default: - return false; + return parent::supports( $feature ); } } @@ -221,9 +226,9 @@ class SearchMySQL extends SearchEngine { */ protected function queryFeatures( &$query ) { foreach ( $this->features as $feature => $value ) { - if ( $feature === 'list-redirects' && !$value ) { + if ( $feature === 'list-redirects' && !$value ) { $query['conds']['page_is_redirect'] = 0; - } elseif( $feature === 'title-suffix-filter' && $value ) { + } elseif ( $feature === 'title-suffix-filter' && $value ) { $query['conds'][] = 'page_title' . $this->db->buildLike( $this->db->anyString(), $value ); } } @@ -358,12 +363,25 @@ class SearchMySQL extends SearchEngine { $dbw->update( 'searchindex', array( 'si_title' => $this->normalizeText( $title ) ), - array( 'si_page' => $id ), + array( 'si_page' => $id ), __METHOD__, array( $dbw->lowPriorityOption() ) ); } /** + * Delete an indexed page + * Title should be pre-processed. + * + * @param Integer $id Page id that was deleted + * @param String $title Title of page that was deleted + */ + function delete( $id, $title ) { + $dbw = wfGetDB( DB_MASTER ); + + $dbw->delete( 'searchindex', array( 'si_page' => $id ), __METHOD__ ); + } + + /** * Converts some characters for MySQL's indexing to grok it correctly, * and pads short words to overcome limitations. * @return mixed|string @@ -386,7 +404,7 @@ class SearchMySQL extends SearchEngine { // ignores short words... Pad them so we can pass them // through without reconfiguring the server... $minLength = $this->minSearchLength(); - if( $minLength > 1 ) { + if ( $minLength > 1 ) { $n = $minLength - 1; $out = preg_replace( "/\b(\w{1,$n})\b/", @@ -427,7 +445,7 @@ class SearchMySQL extends SearchEngine { * @return int */ protected function minSearchLength() { - if( is_null( self::$mMinSearchLength ) ) { + if ( is_null( self::$mMinSearchLength ) ) { $sql = "SHOW GLOBAL VARIABLES LIKE 'ft\\_min\\_word\\_len'"; $dbr = wfGetDB( DB_SLAVE ); @@ -435,7 +453,7 @@ class SearchMySQL extends SearchEngine { $row = $result->fetchObject(); $result->free(); - if( $row && $row->Variable_name == 'ft_min_word_len' ) { + if ( $row && $row->Variable_name == 'ft_min_word_len' ) { self::$mMinSearchLength = intval( $row->Value ); } else { self::$mMinSearchLength = 0; @@ -449,7 +467,7 @@ class SearchMySQL extends SearchEngine { * @ingroup Search */ class MySQLSearchResultSet extends SqlSearchResultSet { - function __construct( $resultSet, $terms, $totalHits=null ) { + function __construct( $resultSet, $terms, $totalHits = null ) { parent::__construct( $resultSet, $terms ); $this->mTotalHits = $totalHits; } diff --git a/includes/search/SearchOracle.php b/includes/search/SearchOracle.php index a2db52f3..a8479654 100644 --- a/includes/search/SearchOracle.php +++ b/includes/search/SearchOracle.php @@ -29,77 +29,80 @@ * @ingroup Search */ class SearchOracle extends SearchEngine { - - private $reservedWords = array ('ABOUT' => 1, - 'ACCUM' => 1, - 'AND' => 1, - 'BT' => 1, - 'BTG' => 1, - 'BTI' => 1, - 'BTP' => 1, - 'FUZZY' => 1, - 'HASPATH' => 1, - 'INPATH' => 1, - 'MINUS' => 1, - 'NEAR' => 1, - 'NOT' => 1, - 'NT' => 1, - 'NTG' => 1, - 'NTI' => 1, - 'NTP' => 1, - 'OR' => 1, - 'PT' => 1, - 'RT' => 1, - 'SQE' => 1, - 'SYN' => 1, - 'TR' => 1, - 'TRSYN' => 1, - 'TT' => 1, - 'WITHIN' => 1); + + private $reservedWords = array( + 'ABOUT' => 1, + 'ACCUM' => 1, + 'AND' => 1, + 'BT' => 1, + 'BTG' => 1, + 'BTI' => 1, + 'BTP' => 1, + 'FUZZY' => 1, + 'HASPATH' => 1, + 'INPATH' => 1, + 'MINUS' => 1, + 'NEAR' => 1, + 'NOT' => 1, + 'NT' => 1, + 'NTG' => 1, + 'NTI' => 1, + 'NTP' => 1, + 'OR' => 1, + 'PT' => 1, + 'RT' => 1, + 'SQE' => 1, + 'SYN' => 1, + 'TR' => 1, + 'TRSYN' => 1, + 'TT' => 1, + 'WITHIN' => 1, + ); /** * Creates an instance of this class * @param $db DatabasePostgres: database object */ - function __construct($db) { + function __construct( $db ) { parent::__construct( $db ); } /** * Perform a full text search query and return a result set. * - * @param $term String: raw search term + * @param string $term raw search term * @return SqlSearchResultSet */ function searchText( $term ) { - if ($term == '') - return new SqlSearchResultSet(false, ''); + if ( $term == '' ) { + return new SqlSearchResultSet( false, '' ); + } - $resultSet = $this->db->resultObject($this->db->query($this->getQuery($this->filter($term), true))); - return new SqlSearchResultSet($resultSet, $this->searchTerms); + $resultSet = $this->db->resultObject( $this->db->query( $this->getQuery( $this->filter( $term ), true ) ) ); + return new SqlSearchResultSet( $resultSet, $this->searchTerms ); } /** * Perform a title-only search query and return a result set. * - * @param $term String: raw search term + * @param string $term raw search term * @return SqlSearchResultSet */ - function searchTitle($term) { - if ($term == '') - return new SqlSearchResultSet(false, ''); + function searchTitle( $term ) { + if ( $term == '' ) { + return new SqlSearchResultSet( false, '' ); + } - $resultSet = $this->db->resultObject($this->db->query($this->getQuery($this->filter($term), false))); - return new MySQLSearchResultSet($resultSet, $this->searchTerms); + $resultSet = $this->db->resultObject( $this->db->query( $this->getQuery( $this->filter( $term ), false ) ) ); + return new MySQLSearchResultSet( $resultSet, $this->searchTerms ); } - /** * Return a partial WHERE clause to exclude redirects, if so set * @return String */ function queryRedirect() { - if ($this->showRedirects) { + if ( $this->showRedirects ) { return ''; } else { return 'AND page_is_redirect=0'; @@ -111,8 +114,9 @@ class SearchOracle extends SearchEngine { * @return String */ function queryNamespaces() { - if( is_null($this->namespaces) ) + if ( is_null( $this->namespaces ) ) { return ''; + } if ( !count( $this->namespaces ) ) { $namespaces = '0'; } else { @@ -129,7 +133,7 @@ class SearchOracle extends SearchEngine { * @return String */ function queryLimit( $sql ) { - return $this->db->limitResult($sql, $this->limit, $this->offset); + return $this->db->limitResult( $sql, $this->limit, $this->offset ); } /** @@ -150,19 +154,18 @@ class SearchOracle extends SearchEngine { * @return String */ function getQuery( $filteredTerm, $fulltext ) { - return $this->queryLimit($this->queryMain($filteredTerm, $fulltext) . ' ' . + return $this->queryLimit( $this->queryMain( $filteredTerm, $fulltext ) . ' ' . $this->queryRedirect() . ' ' . $this->queryNamespaces() . ' ' . - $this->queryRanking( $filteredTerm, $fulltext ) . ' '); + $this->queryRanking( $filteredTerm, $fulltext ) . ' ' ); } - /** * Picks which field to index on, depending on what type of query. * @param $fulltext Boolean * @return String */ - function getIndexField($fulltext) { + function getIndexField( $fulltext ) { return $fulltext ? 'si_text' : 'si_title'; } @@ -174,9 +177,9 @@ class SearchOracle extends SearchEngine { * @return String */ function queryMain( $filteredTerm, $fulltext ) { - $match = $this->parseQuery($filteredTerm, $fulltext); - $page = $this->db->tableName('page'); - $searchindex = $this->db->tableName('searchindex'); + $match = $this->parseQuery( $filteredTerm, $fulltext ); + $page = $this->db->tableName( 'page' ); + $searchindex = $this->db->tableName( 'searchindex' ); return 'SELECT page_id, page_namespace, page_title ' . "FROM $page,$searchindex " . 'WHERE page_id=si_page AND ' . $match; @@ -187,7 +190,7 @@ class SearchOracle extends SearchEngine { * as part of a WHERE clause * @return string */ - function parseQuery($filteredText, $fulltext) { + function parseQuery( $filteredText, $fulltext ) { global $wgContLang; $lc = SearchEngine::legalSearchChars(); $this->searchTerms = array(); @@ -195,44 +198,44 @@ class SearchOracle extends SearchEngine { # @todo FIXME: This doesn't handle parenthetical expressions. $m = array(); $searchon = ''; - if (preg_match_all('/([-+<>~]?)(([' . $lc . ']+)(\*?)|"[^"]*")/', - $filteredText, $m, PREG_SET_ORDER)) { - foreach($m as $terms) { + if ( preg_match_all( '/([-+<>~]?)(([' . $lc . ']+)(\*?)|"[^"]*")/', + $filteredText, $m, PREG_SET_ORDER ) ) { + foreach ( $m as $terms ) { // Search terms in all variant forms, only // apply on wiki with LanguageConverter $temp_terms = $wgContLang->autoConvertToAllVariants( $terms[2] ); - if( is_array( $temp_terms )) { - $temp_terms = array_unique( array_values( $temp_terms )); - foreach( $temp_terms as $t ) { - $searchon .= ($terms[1] == '-' ? ' ~' : ' & ') . $this->escapeTerm( $t ); + if ( is_array( $temp_terms ) ) { + $temp_terms = array_unique( array_values( $temp_terms ) ); + foreach ( $temp_terms as $t ) { + $searchon .= ( $terms[1] == '-' ? ' ~' : ' & ' ) . $this->escapeTerm( $t ); } } else { - $searchon .= ($terms[1] == '-' ? ' ~' : ' & ') . $this->escapeTerm( $terms[2] ); + $searchon .= ( $terms[1] == '-' ? ' ~' : ' & ' ) . $this->escapeTerm( $terms[2] ); } - if (!empty($terms[3])) { + if ( !empty( $terms[3] ) ) { $regexp = preg_quote( $terms[3], '/' ); - if ($terms[4]) + if ( $terms[4] ) { $regexp .= "[0-9A-Za-z_]+"; + } } else { - $regexp = preg_quote(str_replace('"', '', $terms[2]), '/'); + $regexp = preg_quote( str_replace( '"', '', $terms[2] ), '/' ); } $this->searchTerms[] = $regexp; } } - - $searchon = $this->db->addQuotes(ltrim($searchon, ' &')); - $field = $this->getIndexField($fulltext); + $searchon = $this->db->addQuotes( ltrim( $searchon, ' &' ) ); + $field = $this->getIndexField( $fulltext ); return " CONTAINS($field, $searchon, 1) > 0 "; } - private function escapeTerm($t) { + private function escapeTerm( $t ) { global $wgContLang; - $t = $wgContLang->normalizeForSearch($t); - $t = isset($this->reservedWords[strtoupper($t)]) ? '{'.$t.'}' : $t; - $t = preg_replace('/^"(.*)"$/', '($1)', $t); - $t = preg_replace('/([-&|])/', '\\\\$1', $t); + $t = $wgContLang->normalizeForSearch( $t ); + $t = isset( $this->reservedWords[strtoupper( $t )] ) ? '{' . $t . '}' : $t; + $t = preg_replace( '/^"(.*)"$/', '($1)', $t ); + $t = preg_replace( '/([-&|])/', '\\\\$1', $t ); return $t; } /** @@ -243,10 +246,10 @@ class SearchOracle extends SearchEngine { * @param $title String * @param $text String */ - function update($id, $title, $text) { - $dbw = wfGetDB(DB_MASTER); - $dbw->replace('searchindex', - array('si_page'), + function update( $id, $title, $text ) { + $dbw = wfGetDB( DB_MASTER ); + $dbw->replace( 'searchindex', + array( 'si_page' ), array( 'si_page' => $id, 'si_title' => $title, @@ -254,13 +257,13 @@ class SearchOracle extends SearchEngine { ), 'SearchOracle::update' ); // Sync the index - // We need to specify the DB name (i.e. user/schema) here so that + // We need to specify the DB name (i.e. user/schema) here so that // it can work from the installer, where // ALTER SESSION SET CURRENT_SCHEMA = ... // was used. - $dbw->query( "CALL ctx_ddl.sync_index(" . + $dbw->query( "CALL ctx_ddl.sync_index(" . $dbw->addQuotes( $dbw->getDBname() . '.' . $dbw->tableName( 'si_text_idx', 'raw' ) ) . ")" ); - $dbw->query( "CALL ctx_ddl.sync_index(" . + $dbw->query( "CALL ctx_ddl.sync_index(" . $dbw->addQuotes( $dbw->getDBname() . '.' . $dbw->tableName( 'si_title_idx', 'raw' ) ) . ")" ); } @@ -271,17 +274,16 @@ class SearchOracle extends SearchEngine { * @param $id Integer * @param $title String */ - function updateTitle($id, $title) { - $dbw = wfGetDB(DB_MASTER); + function updateTitle( $id, $title ) { + $dbw = wfGetDB( DB_MASTER ); - $dbw->update('searchindex', - array('si_title' => $title), - array('si_page' => $id), + $dbw->update( 'searchindex', + array( 'si_title' => $title ), + array( 'si_page' => $id ), 'SearchOracle::updateTitle', - array()); + array() ); } - public static function legalSearchChars() { return "\"" . parent::legalSearchChars(); } diff --git a/includes/search/SearchPostgres.php b/includes/search/SearchPostgres.php index 68648894..7f19ed13 100644 --- a/includes/search/SearchPostgres.php +++ b/includes/search/SearchPostgres.php @@ -47,15 +47,15 @@ class SearchPostgres extends SearchEngine { * Currently searches a page's current title (page.page_title) and * latest revision article text (pagecontent.old_text) * - * @param $term String: raw search term + * @param string $term raw search term * @return PostgresSearchResultSet */ function searchTitle( $term ) { - $q = $this->searchQuery( $term , 'titlevector', 'page_title' ); - $olderror = error_reporting(E_ERROR); + $q = $this->searchQuery( $term, 'titlevector', 'page_title' ); + $olderror = error_reporting( E_ERROR ); $resultSet = $this->db->resultObject( $this->db->query( $q, 'SearchPostgres', true ) ); - error_reporting($olderror); - if (!$resultSet) { + error_reporting( $olderror ); + if ( !$resultSet ) { // Needed for "Query requires full scan, GIN doesn't support it" return new SearchResultTooMany(); } @@ -64,10 +64,10 @@ class SearchPostgres extends SearchEngine { function searchText( $term ) { $q = $this->searchQuery( $term, 'textvector', 'old_text' ); - $olderror = error_reporting(E_ERROR); + $olderror = error_reporting( E_ERROR ); $resultSet = $this->db->resultObject( $this->db->query( $q, 'SearchPostgres', true ) ); - error_reporting($olderror); - if (!$resultSet) { + error_reporting( $olderror ); + if ( !$resultSet ) { return new SearchResultTooMany(); } return new PostgresSearchResultSet( $resultSet, $this->searchTerms ); @@ -86,29 +86,29 @@ class SearchPostgres extends SearchEngine { wfDebug( "parseQuery received: $term \n" ); ## No backslashes allowed - $term = preg_replace('/\\\/', '', $term); + $term = preg_replace( '/\\\/', '', $term ); ## Collapse parens into nearby words: - $term = preg_replace('/\s*\(\s*/', ' (', $term); - $term = preg_replace('/\s*\)\s*/', ') ', $term); + $term = preg_replace( '/\s*\(\s*/', ' (', $term ); + $term = preg_replace( '/\s*\)\s*/', ') ', $term ); ## Treat colons as word separators: - $term = preg_replace('/:/', ' ', $term); + $term = preg_replace( '/:/', ' ', $term ); $searchstring = ''; $m = array(); - if( preg_match_all('/([-!]?)(\S+)\s*/', $term, $m, PREG_SET_ORDER ) ) { - foreach( $m as $terms ) { - if (strlen($terms[1])) { + if ( preg_match_all( '/([-!]?)(\S+)\s*/', $term, $m, PREG_SET_ORDER ) ) { + foreach ( $m as $terms ) { + if ( strlen( $terms[1] ) ) { $searchstring .= ' & !'; } - if (strtolower($terms[2]) === 'and') { + if ( strtolower( $terms[2] ) === 'and' ) { $searchstring .= ' & '; } - elseif (strtolower($terms[2]) === 'or' or $terms[2] === '|') { + elseif ( strtolower( $terms[2] ) === 'or' or $terms[2] === '|' ) { $searchstring .= ' | '; } - elseif (strtolower($terms[2]) === 'not') { + elseif ( strtolower( $terms[2] ) === 'not' ) { $searchstring .= ' & !'; } else { @@ -118,22 +118,22 @@ class SearchPostgres extends SearchEngine { } ## Strip out leading junk - $searchstring = preg_replace('/^[\s\&\|]+/', '', $searchstring); + $searchstring = preg_replace( '/^[\s\&\|]+/', '', $searchstring ); ## Remove any doubled-up operators - $searchstring = preg_replace('/([\!\&\|]) +(?:[\&\|] +)+/', "$1 ", $searchstring); + $searchstring = preg_replace( '/([\!\&\|]) +(?:[\&\|] +)+/', "$1 ", $searchstring ); ## Remove any non-spaced operators (e.g. "Zounds!") - $searchstring = preg_replace('/([^ ])[\!\&\|]/', "$1", $searchstring); + $searchstring = preg_replace( '/([^ ])[\!\&\|]/', "$1", $searchstring ); ## Remove any trailing whitespace or operators - $searchstring = preg_replace('/[\s\!\&\|]+$/', '', $searchstring); + $searchstring = preg_replace( '/[\s\!\&\|]+$/', '', $searchstring ); ## Remove unnecessary quotes around everything - $searchstring = preg_replace('/^[\'"](.*)[\'"]$/', "$1", $searchstring); + $searchstring = preg_replace( '/^[\'"](.*)[\'"]$/', "$1", $searchstring ); ## Quote the whole thing - $searchstring = $this->db->addQuotes($searchstring); + $searchstring = $this->db->addQuotes( $searchstring ); wfDebug( "parseQuery returned: $searchstring \n" ); @@ -154,42 +154,43 @@ class SearchPostgres extends SearchEngine { ## We need a separate query here so gin does not complain about empty searches $SQL = "SELECT to_tsquery($searchstring)"; - $res = $this->db->query($SQL); - if (!$res) { + $res = $this->db->query( $SQL ); + if ( !$res ) { ## TODO: Better output (example to catch: one 'two) - die ("Sorry, that was not a valid search string. Please go back and try again"); + die( "Sorry, that was not a valid search string. Please go back and try again" ); } $top = $res->fetchRow(); $top = $top[0]; - if ($top === "") { ## e.g. if only stopwords are used XXX return something better - $query = "SELECT page_id, page_namespace, page_title, 0 AS score ". + if ( $top === "" ) { ## e.g. if only stopwords are used XXX return something better + $query = "SELECT page_id, page_namespace, page_title, 0 AS score " . "FROM page p, revision r, pagecontent c WHERE p.page_latest = r.rev_id " . "AND r.rev_text_id = c.old_id AND 1=0"; } else { $m = array(); - if( preg_match_all("/'([^']+)'/", $top, $m, PREG_SET_ORDER ) ) { - foreach( $m as $terms ) { + if ( preg_match_all( "/'([^']+)'/", $top, $m, PREG_SET_ORDER ) ) { + foreach ( $m as $terms ) { $this->searchTerms[$terms[1]] = $terms[1]; } } - $query = "SELECT page_id, page_namespace, page_title, ". - "ts_rank($fulltext, to_tsquery($searchstring), 5) AS score ". + $query = "SELECT page_id, page_namespace, page_title, " . + "ts_rank($fulltext, to_tsquery($searchstring), 5) AS score " . "FROM page p, revision r, pagecontent c WHERE p.page_latest = r.rev_id " . "AND r.rev_text_id = c.old_id AND $fulltext @@ to_tsquery($searchstring)"; } ## Redirects - if (! $this->showRedirects) + if ( !$this->showRedirects ) { $query .= ' AND page_is_redirect = 0'; + } ## Namespaces - defaults to 0 - if( !is_null($this->namespaces) ){ // null -> search all - if ( count($this->namespaces) < 1) + if ( !is_null( $this->namespaces ) ) { // null -> search all + if ( count( $this->namespaces ) < 1 ) { $query .= ' AND page_namespace = 0'; - else { + } else { $namespaces = $this->db->makeList( $this->namespaces ); $query .= " AND page_namespace IN ($namespaces)"; } @@ -208,10 +209,10 @@ class SearchPostgres extends SearchEngine { function update( $pageid, $title, $text ) { ## We don't want to index older revisions - $SQL = "UPDATE pagecontent SET textvector = NULL WHERE old_id IN ". + $SQL = "UPDATE pagecontent SET textvector = NULL WHERE old_id IN " . "(SELECT rev_text_id FROM revision WHERE rev_page = " . intval( $pageid ) . " ORDER BY rev_text_id DESC OFFSET 1)"; - $this->db->query($SQL); + $this->db->query( $SQL ); return true; } @@ -226,7 +227,7 @@ class SearchPostgres extends SearchEngine { */ class PostgresSearchResult extends SearchResult { function __construct( $row ) { - parent::__construct($row); + parent::__construct( $row ); $this->score = $row->score; } function getScore() { @@ -244,7 +245,7 @@ class PostgresSearchResultSet extends SqlSearchResultSet { function next() { $row = $this->mResultSet->fetchObject(); - if( $row === false ) { + if ( $row === false ) { return false; } else { return new PostgresSearchResult( $row ); diff --git a/includes/search/SearchSqlite.php b/includes/search/SearchSqlite.php index e52e4fe3..554181f6 100644 --- a/includes/search/SearchSqlite.php +++ b/includes/search/SearchSqlite.php @@ -61,12 +61,12 @@ class SearchSqlite extends SearchEngine { $this->searchTerms = array(); $m = array(); - if( preg_match_all( '/([-+<>~]?)(([' . $lc . ']+)(\*?)|"[^"]*")/', - $filteredText, $m, PREG_SET_ORDER ) ) { - foreach( $m as $bits ) { + if ( preg_match_all( '/([-+<>~]?)(([' . $lc . ']+)(\*?)|"[^"]*")/', + $filteredText, $m, PREG_SET_ORDER ) ) { + foreach ( $m as $bits ) { @list( /* all */, $modifier, $term, $nonQuoted, $wildcard ) = $bits; - if( $nonQuoted != '' ) { + if ( $nonQuoted != '' ) { $term = $nonQuoted; $quote = ''; } else { @@ -74,14 +74,14 @@ class SearchSqlite extends SearchEngine { $quote = '"'; } - if( $searchon !== '' ) { + if ( $searchon !== '' ) { $searchon .= ' '; } // Some languages such as Serbian store the input form in the search index, // so we may need to search for matches in multiple writing system variants. $convertedVariants = $wgContLang->autoConvertToAllVariants( $term ); - if( is_array( $convertedVariants ) ) { + if ( is_array( $convertedVariants ) ) { $variants = array_unique( array_values( $convertedVariants ) ); } else { $variants = array( $term ); @@ -101,10 +101,11 @@ class SearchSqlite extends SearchEngine { $strippedVariants = array_unique( $strippedVariants ); $searchon .= $modifier; - if( count( $strippedVariants) > 1 ) + if ( count( $strippedVariants ) > 1 ) { $searchon .= '('; - foreach( $strippedVariants as $stripped ) { - if( $nonQuoted && strpos( $stripped, ' ' ) !== false ) { + } + foreach ( $strippedVariants as $stripped ) { + if ( $nonQuoted && strpos( $stripped, ' ' ) !== false ) { // Hack for Chinese: we need to toss in quotes for // multiple-character phrases since normalizeForSearch() // added spaces between them to make word breaks. @@ -112,8 +113,9 @@ class SearchSqlite extends SearchEngine { } $searchon .= "$quote$stripped$quote$wildcard "; } - if( count( $strippedVariants) > 1 ) + if ( count( $strippedVariants ) > 1 ) { $searchon .= ')'; + } // Match individual terms or quoted phrase in result highlighting... // Note that variants will be introduced in a later stage for highlighting! @@ -134,8 +136,8 @@ class SearchSqlite extends SearchEngine { global $wgContLang; $regex = preg_quote( $string, '/' ); - if( $wgContLang->hasWordBreaks() ) { - if( $wildcard ) { + if ( $wgContLang->hasWordBreaks() ) { + if ( $wildcard ) { // Don't cut off the final bit! $regex = "\b$regex"; } else { @@ -156,7 +158,7 @@ class SearchSqlite extends SearchEngine { /** * Perform a full text search query and return a result set. * - * @param $term String: raw search term + * @param string $term raw search term * @return SqliteSearchResultSet */ function searchText( $term ) { @@ -166,7 +168,7 @@ class SearchSqlite extends SearchEngine { /** * Perform a title-only search query and return a result set. * - * @param $term String: raw search term + * @param string $term raw search term * @return SqliteSearchResultSet */ function searchTitle( $term ) { @@ -184,10 +186,10 @@ class SearchSqlite extends SearchEngine { $resultSet = $this->db->query( $this->getQuery( $filteredTerm, $fulltext ) ); $total = null; - if( $wgCountTotalSearchHits ) { + if ( $wgCountTotalSearchHits ) { $totalResult = $this->db->query( $this->getCountQuery( $filteredTerm, $fulltext ) ); $row = $totalResult->fetchObject(); - if( $row ) { + if ( $row ) { $total = intval( $row->c ); } $totalResult->free(); @@ -196,13 +198,12 @@ class SearchSqlite extends SearchEngine { return new SqliteSearchResultSet( $resultSet, $this->searchTerms, $total ); } - /** * Return a partial WHERE clause to exclude redirects, if so set * @return String */ function queryRedirect() { - if( $this->showRedirects ) { + if ( $this->showRedirects ) { return ''; } else { return 'AND page_is_redirect=0'; @@ -214,8 +215,9 @@ class SearchSqlite extends SearchEngine { * @return String */ function queryNamespaces() { - if( is_null($this->namespaces) ) + if ( is_null( $this->namespaces ) ) { return ''; # search all + } if ( !count( $this->namespaces ) ) { $namespaces = '0'; } else { @@ -266,7 +268,7 @@ class SearchSqlite extends SearchEngine { */ function queryMain( $filteredTerm, $fulltext ) { $match = $this->parseQuery( $filteredTerm, $fulltext ); - $page = $this->db->tableName( 'page' ); + $page = $this->db->tableName( 'page' ); $searchindex = $this->db->tableName( 'searchindex' ); return "SELECT $searchindex.rowid, page_namespace, page_title " . "FROM $page,$searchindex " . @@ -275,7 +277,7 @@ class SearchSqlite extends SearchEngine { function getCountQuery( $filteredTerm, $fulltext ) { $match = $this->parseQuery( $filteredTerm, $fulltext ); - $page = $this->db->tableName( 'page' ); + $page = $this->db->tableName( 'page' ); $searchindex = $this->db->tableName( 'searchindex' ); return "SELECT COUNT(*) AS c " . "FROM $page,$searchindex " . @@ -296,7 +298,7 @@ class SearchSqlite extends SearchEngine { if ( !$this->fulltextSearchSupported() ) { return; } - // @todo: find a method to do it in a single request, + // @todo find a method to do it in a single request, // couldn't do it so far due to typelessness of FTS3 tables. $dbw = wfGetDB( DB_MASTER ); @@ -317,7 +319,7 @@ class SearchSqlite extends SearchEngine { * @param $id Integer * @param $title String */ - function updateTitle( $id, $title ) { + function updateTitle( $id, $title ) { if ( !$this->fulltextSearchSupported() ) { return; } @@ -325,7 +327,7 @@ class SearchSqlite extends SearchEngine { $dbw->update( 'searchindex', array( 'si_title' => $title ), - array( 'rowid' => $id ), + array( 'rowid' => $id ), __METHOD__ ); } } @@ -334,7 +336,7 @@ class SearchSqlite extends SearchEngine { * @ingroup Search */ class SqliteSearchResultSet extends SqlSearchResultSet { - function __construct( $resultSet, $terms, $totalHits=null ) { + function __construct( $resultSet, $terms, $totalHits = null ) { parent::__construct( $resultSet, $terms ); $this->mTotalHits = $totalHits; } diff --git a/includes/search/SearchUpdate.php b/includes/search/SearchUpdate.php index 40dd36c2..82a413e9 100644 --- a/includes/search/SearchUpdate.php +++ b/includes/search/SearchUpdate.php @@ -29,52 +29,114 @@ * @ingroup Search */ class SearchUpdate implements DeferrableUpdate { + /** + * Page id being updated + * @var int + */ + private $id = 0; + + /** + * Title we're updating + * @var Title + */ + private $title; + + /** + * Content of the page (not text) + * @var Content|false + */ + private $content; + + /** + * Constructor + * + * @param int $id Page id to update + * @param Title|string $title Title of page to update + * @param Content|string|false $c Content of the page to update. + * If a Content object, text will be gotten from it. String is for back-compat. + * Passing false tells the backend to just update the title, not the content + */ + public function __construct( $id, $title, $c = false ) { + if ( is_string( $title ) ) { + $nt = Title::newFromText( $title ); + } else { + $nt = $title; + } - private $mId = 0, $mNamespace, $mTitle, $mText; - private $mTitleWords; - - function __construct( $id, $title, $text = false ) { - $nt = Title::newFromText( $title ); - if( $nt ) { - $this->mId = $id; - $this->mText = $text; - - $this->mNamespace = $nt->getNamespace(); - $this->mTitle = $nt->getText(); # Discard namespace - - $this->mTitleWords = $this->mTextWords = array(); + if ( $nt ) { + $this->id = $id; + // is_string() check is back-compat for ApprovedRevs + if ( is_string( $c ) ) { + $this->content = new TextContent( $c ); + } else { + $this->content = $c ?: false; + } + $this->title = $nt; } else { wfDebug( "SearchUpdate object created with invalid title '$title'\n" ); } } - function doUpdate() { - global $wgContLang, $wgDisableSearchUpdate; + /** + * Perform actual update for the entry + */ + public function doUpdate() { + global $wgDisableSearchUpdate; - if( $wgDisableSearchUpdate || !$this->mId ) { + if ( $wgDisableSearchUpdate || !$this->id ) { return; } wfProfileIn( __METHOD__ ); - $search = SearchEngine::create(); - $lc = SearchEngine::legalSearchChars() . '&#;'; + $page = WikiPage::newFromId( $this->id, WikiPage::READ_LATEST ); + $indexTitle = Title::indexTitle( $this->title->getNamespace(), $this->title->getText() ); - if( $this->mText === false ) { - $search->updateTitle($this->mId, - $search->normalizeText( Title::indexTitle( $this->mNamespace, $this->mTitle ) ) ); - wfProfileOut( __METHOD__ ); - return; + foreach ( SearchEngine::getSearchTypes() as $type ) { + $search = SearchEngine::create( $type ); + if ( !$search->supports( 'search-update' ) ) { + continue; + } + + $normalTitle = $search->normalizeText( $indexTitle ); + + if ( $page === null ) { + $search->delete( $this->id, $normalTitle ); + continue; + } elseif ( $this->content === false ) { + $search->updateTitle( $this->id, $normalTitle ); + continue; + } + + $text = $search->getTextFromContent( $this->title, $this->content ); + if ( !$search->textAlreadyUpdatedForIndex() ) { + $text = self::updateText( $text ); + } + + # Perform the actual update + $search->update( $this->id, $normalTitle, $search->normalizeText( $text ) ); } + wfProfileOut( __METHOD__ ); + } + + /** + * Clean text for indexing. Only really suitable for indexing in databases. + * If you're using a real search engine, you'll probably want to override + * this behavior and do something nicer with the original wikitext. + */ + public static function updateText( $text ) { + global $wgContLang; + # Language-specific strip/conversion - $text = $wgContLang->normalizeForSearch( $this->mText ); + $text = $wgContLang->normalizeForSearch( $text ); + $lc = SearchEngine::legalSearchChars() . '&#;'; wfProfileIn( __METHOD__ . '-regexps' ); $text = preg_replace( "/<\\/?\\s*[A-Za-z][^>]*?>/", ' ', $wgContLang->lc( " " . $text . " " ) ); # Strip HTML markup $text = preg_replace( "/(^|\\n)==\\s*([^\\n]+)\\s*==(\\s)/sD", - "\\1\\2 \\2 \\2\\3", $text ); # Emphasize headings + "\\1\\2 \\2 \\2\\3", $text ); # Emphasize headings # Strip external URLs $uc = "A-Za-z0-9_\\/:.,~%\\-+&;#?!=()@\\x80-\\xFF"; @@ -92,7 +154,7 @@ class SearchUpdate implements DeferrableUpdate { $text = preg_replace( $pat2, " \\1 \\3", $text ); $text = preg_replace( "/([^{$lc}])([{$lc}]+)]]([a-z]+)/", - "\\1\\2 \\2\\3", $text ); # Handle [[game]]s + "\\1\\2 \\2\\3", $text ); # Handle [[game]]s # Strip all remaining non-search characters $text = preg_replace( "/[^{$lc}]+/", " ", $text ); @@ -118,22 +180,6 @@ class SearchUpdate implements DeferrableUpdate { # Strip wiki '' and ''' $text = preg_replace( "/''[']*/", " ", $text ); wfProfileOut( __METHOD__ . '-regexps' ); - - wfRunHooks( 'SearchUpdate', array( $this->mId, $this->mNamespace, $this->mTitle, &$text ) ); - - # Perform the actual update - $search->update($this->mId, $search->normalizeText( Title::indexTitle( $this->mNamespace, $this->mTitle ) ), - $search->normalizeText( $text ) ); - - wfProfileOut( __METHOD__ ); + return $text; } } - -/** - * Placeholder class - * - * @ingroup Search - */ -class SearchUpdateMyISAM extends SearchUpdate { - # Inherits everything -} |