├── CREDITS ├── tests ├── pecl_17007.php ├── pecl_18635.php ├── example.sql ├── sphinx-0.9.9.conf ├── sphinx-1.10.conf ├── api_0.9.8.php ├── api_0.9.9.php ├── test_1.10.php └── api │ └── sphinxapi-1.10.php ├── config.w32 ├── php_sphinx.h ├── LICENSE ├── config.m4 ├── package.xml └── sphinx.c /CREDITS: -------------------------------------------------------------------------------- 1 | Sphinx client extension for PHP 2 | Antony Dovgal (tony2001) 3 | Alexey Romanenko (santiago) 4 | -------------------------------------------------------------------------------- /tests/pecl_17007.php: -------------------------------------------------------------------------------- 1 | setServer('localhost', 3312); 4 | $sphinx->setMatchMode(SPH_MATCH_EXTENDED2); 5 | $sphinx->addQuery('test)'); 6 | $results = $sphinx->runQueries(); 7 | var_dump($results); 8 | -------------------------------------------------------------------------------- /tests/pecl_18635.php: -------------------------------------------------------------------------------- 1 | setServer('localhost',9312); 4 | $s->setMatchMode(SPH_MATCH_ANY); 5 | $s->setMaxQueryTime(3); 6 | 7 | 8 | $result = $s->query('test'); 9 | var_dump($result); 10 | 11 | if ($result === false) 12 | { 13 | print "Query failed: " . $s->GetLastError() . ".\n"; 14 | } 15 | -------------------------------------------------------------------------------- /config.w32: -------------------------------------------------------------------------------- 1 | // $Id$ 2 | // vim:ft=javascript 3 | 4 | ARG_WITH("sphinx", "for sphinx support", "yes"); 5 | 6 | if (PHP_SPHINX == "yes") { 7 | if (CHECK_LIB("libsphinxclient.lib", "sphinx") && 8 | CHECK_HEADER_ADD_INCLUDE("sphinxclient.h", "CFLAGS_SPHINX")) { 9 | 10 | ADD_FLAG("CFLAGS_SPHINX", "/D HAVE_3ARG_SPHINX_SET_RANKING_MODE=1"); 11 | 12 | EXTENSION("sphinx", "sphinx.c"); 13 | } 14 | } 15 | 16 | -------------------------------------------------------------------------------- /tests/example.sql: -------------------------------------------------------------------------------- 1 | DROP TABLE IF EXISTS test.documents; 2 | CREATE TABLE test.documents 3 | ( 4 | id INTEGER PRIMARY KEY NOT NULL AUTO_INCREMENT, 5 | group_id INTEGER NOT NULL, 6 | group_id2 INTEGER NOT NULL, 7 | date_added DATETIME NOT NULL, 8 | title VARCHAR(255) NOT NULL, 9 | content TEXT NOT NULL 10 | ); 11 | 12 | REPLACE INTO test.documents ( id, group_id, group_id2, date_added, title, content ) VALUES 13 | ( 1, 1, 5, NOW(), 'test one', 'this is my test document number one. also checking search within phrases.' ), 14 | ( 2, 1, 6, NOW(), 'test two', 'this is my test document number two' ), 15 | ( 3, 2, 7, NOW(), 'another doc', 'this is another group' ), 16 | ( 4, 2, 8, NOW(), 'doc number four', 'this is to test groups' ); 17 | 18 | DROP TABLE IF EXISTS test.tags; 19 | CREATE TABLE test.tags 20 | ( 21 | docid INTEGER NOT NULL, 22 | tagid INTEGER NOT NULL, 23 | UNIQUE(docid,tagid) 24 | ); 25 | 26 | INSERT INTO test.tags VALUES 27 | (1,1), (1,3), (1,5), (1,7), 28 | (2,6), (2,4), (2,2), 29 | (3,15), 30 | (4,7), (4,40); 31 | -------------------------------------------------------------------------------- /tests/sphinx-0.9.9.conf: -------------------------------------------------------------------------------- 1 | 2 | source src1 3 | { 4 | type = mysql 5 | 6 | sql_host = localhost 7 | sql_user = test 8 | sql_pass = 9 | sql_db = test 10 | sql_port = 3306 # optional, default is 3306 11 | 12 | sql_query = SELECT id, group_id, group_id2, UNIX_TIMESTAMP(date_added) AS date_added, title, content \ 13 | FROM documents 14 | 15 | sql_attr_uint = group_id 16 | sql_attr_uint = group_id2 17 | sql_attr_timestamp = date_added 18 | sql_attr_multi = uint mva from query; SELECT docid, tagid FROM tags 19 | sql_attr_multi = uint mva2 from query; SELECT docid, tagid FROM tags 20 | 21 | sql_query_info = SELECT * FROM documents WHERE id=$id 22 | } 23 | 24 | 25 | index test1 26 | { 27 | source = src1 28 | path = /tmp/sphinx-0.9.9/var/data/test1 29 | docinfo = extern 30 | charset_type = sbcs 31 | } 32 | 33 | 34 | indexer 35 | { 36 | mem_limit = 32M 37 | } 38 | 39 | 40 | searchd 41 | { 42 | listen = 9312 43 | listen = 9306:mysql41 44 | log = /tmp/sphinx-0.9.9/var/log/searchd.log 45 | query_log = /tmp/sphinx-0.9.9/var/log/query.log 46 | read_timeout = 5 47 | max_children = 30 48 | pid_file = /tmp/sphinx-0.9.9/var/log/searchd.pid 49 | max_matches = 1000 50 | seamless_rotate = 1 51 | preopen_indexes = 0 52 | unlink_old = 1 53 | } 54 | -------------------------------------------------------------------------------- /tests/sphinx-1.10.conf: -------------------------------------------------------------------------------- 1 | 2 | source src1 3 | { 4 | type = mysql 5 | 6 | sql_host = localhost 7 | sql_user = test 8 | sql_pass = 9 | sql_db = test 10 | sql_port = 3306 # optional, default is 3306 11 | 12 | sql_query = SELECT id, group_id, group_id2, UNIX_TIMESTAMP(date_added) AS date_added, title, content \ 13 | FROM documents 14 | 15 | sql_attr_uint = group_id 16 | sql_attr_uint = group_id2 17 | sql_attr_timestamp = date_added 18 | sql_attr_string = title 19 | sql_attr_multi = uint mva from query; SELECT docid, tagid FROM tags 20 | sql_attr_multi = uint mva2 from query; SELECT docid, tagid FROM tags 21 | 22 | sql_query_info = SELECT * FROM documents WHERE id=$id 23 | } 24 | 25 | 26 | index test1 27 | { 28 | source = src1 29 | path = /tmp/sphinx-1.10/var/data/test1 30 | docinfo = extern 31 | charset_type = sbcs 32 | } 33 | 34 | 35 | index testrt 36 | { 37 | type = rt 38 | rt_mem_limit = 32M 39 | 40 | path = /tmp/sphinx-1.10/var/data/testrt 41 | charset_type = utf-8 42 | 43 | rt_field = title 44 | rt_field = content 45 | rt_attr_uint = gid 46 | } 47 | 48 | 49 | indexer 50 | { 51 | mem_limit = 32M 52 | } 53 | 54 | 55 | searchd 56 | { 57 | listen = 9312 58 | listen = 9306:mysql41 59 | log = /tmp/sphinx-1.10/var/log/searchd.log 60 | query_log = /tmp/sphinx-1.10/var/log/query.log 61 | read_timeout = 5 62 | max_children = 30 63 | pid_file = /tmp/sphinx-1.10/var/log/searchd.pid 64 | max_matches = 1000 65 | seamless_rotate = 1 66 | preopen_indexes = 0 67 | unlink_old = 1 68 | workers = threads # for RT to work 69 | } 70 | -------------------------------------------------------------------------------- /php_sphinx.h: -------------------------------------------------------------------------------- 1 | /* 2 | +----------------------------------------------------------------------+ 3 | | PHP Version 5 | 4 | +----------------------------------------------------------------------+ 5 | | Copyright (c) 1997-2008 The PHP Group | 6 | +----------------------------------------------------------------------+ 7 | | This source file is subject to version 3.01 of the PHP license, | 8 | | that is bundled with this package in the file LICENSE, and is | 9 | | available through the world-wide-web at the following url: | 10 | | http://www.php.net/license/3_01.txt | 11 | | If you did not receive a copy of the PHP license and are unable to | 12 | | obtain it through the world-wide-web, please send a note to | 13 | | license@php.net so we can mail you a copy immediately. | 14 | +----------------------------------------------------------------------+ 15 | | Author: Antony Dovgal | 16 | | Based on Sphinx PHP API by Andrew Aksyonoff | 17 | +----------------------------------------------------------------------+ 18 | */ 19 | 20 | /* $Id$ */ 21 | 22 | #ifndef PHP_SPHINX_H 23 | #define PHP_SPHINX_H 24 | 25 | extern zend_module_entry sphinx_module_entry; 26 | #define phpext_sphinx_ptr &sphinx_module_entry 27 | 28 | #ifdef ZTS 29 | #include "TSRM.h" 30 | #endif 31 | 32 | PHP_MINIT_FUNCTION(sphinx); 33 | PHP_MINFO_FUNCTION(sphinx); 34 | 35 | #define PHP_SPHINX_VERSION "1.3.3" 36 | 37 | #endif /* PHP_SPHINX_H */ 38 | 39 | /* 40 | * Local variables: 41 | * tab-width: 4 42 | * c-basic-offset: 4 43 | * End: 44 | * vim600: noet sw=4 ts=4 fdm=marker 45 | * vim<600: noet sw=4 ts=4 46 | */ 47 | -------------------------------------------------------------------------------- /tests/api_0.9.8.php: -------------------------------------------------------------------------------- 1 | SetServer ( $host, $port ); 27 | $cl->SetConnectTimeout ( 1 ); 28 | $cl->SetArrayResult ( true ); 29 | $cl->SetFieldWeights ( array ( 100, 1 ) ); 30 | $cl->SetMatchMode ( $mode ); 31 | if ( count($filtervals) ) $cl->SetFilter ( $filter, $filtervals ); 32 | if ( $groupby ) $cl->SetGroupBy ( $groupby, SPH_GROUPBY_ATTR, $groupsort ); 33 | if ( $sortby ) $cl->SetSortMode ( SPH_SORT_EXTENDED, $sortby ); 34 | if ( $sortexpr ) $cl->SetSortMode ( SPH_SORT_EXPR, $sortexpr ); 35 | if ( $distinct ) $cl->SetGroupDistinct ( $distinct ); 36 | if ( $limit ) $cl->SetLimits ( 0, $limit, ( $limit>1000 ) ? $limit : 1000 ); 37 | $cl->SetRankingMode ( $ranker ); 38 | $res = $cl->Query ( $q, $index ); 39 | 40 | /////////////////////// 41 | // print query results 42 | /////////////////////// 43 | 44 | //print_r($res); 45 | 46 | if ( $res===false ) 47 | { 48 | print "Query failed: " . $cl->GetLastError() . ".\n"; 49 | 50 | } else 51 | { 52 | if ( $cl->GetLastWarning() ) 53 | print "WARNING: " . $cl->GetLastWarning() . "\n\n"; 54 | 55 | print "Query '$q' retrieved $res[total] of $res[total_found] matches in $res[time] sec.\n"; 56 | print "Query stats:\n"; 57 | if ( is_array($res["words"]) ) 58 | foreach ( $res["words"] as $word => $info ) 59 | print " '$word' found $info[hits] times in $info[docs] documents\n"; 60 | print "\n"; 61 | 62 | if ( is_array($res["matches"]) ) 63 | { 64 | $n = 1; 65 | print "Matches:\n"; 66 | foreach ( $res["matches"] as $docinfo ) 67 | { 68 | print "$n. doc_id=$docinfo[id], weight=$docinfo[weight]"; 69 | foreach ( $res["attrs"] as $attrname => $attrtype ) 70 | { 71 | $value = $docinfo["attrs"][$attrname]; 72 | if ( $attrtype & SPH_ATTR_MULTI ) 73 | { 74 | $value = "(" . join ( ",", $value ) .")"; 75 | } else 76 | { 77 | if ( $attrtype==SPH_ATTR_TIMESTAMP ) 78 | $value = date ( "Y-m-d H:i:s", $value ); 79 | } 80 | print ", $attrname=$value"; 81 | } 82 | print "\n"; 83 | $n++; 84 | } 85 | } 86 | } 87 | 88 | 89 | -------------------------------------------------------------------------------- /tests/api_0.9.9.php: -------------------------------------------------------------------------------- 1 | SetServer ( $host, $port ); 29 | $cl->SetConnectTimeout ( 1 ); 30 | $cl->Open(); 31 | */ 32 | $cl->SetArrayResult ( true ); 33 | $cl->SetFieldWeights ( array ( 100, 1 ) ); 34 | $cl->SetMatchMode ( $mode ); 35 | 36 | if ( count($filtervals) ) $cl->SetFilter ( $filter, $filtervals ); 37 | if ( $groupby ) $cl->SetGroupBy ( $groupby, SPH_GROUPBY_ATTR, $groupsort ); 38 | if ( $sortby ) $cl->SetSortMode ( SPH_SORT_EXTENDED, $sortby ); 39 | if ( $sortexpr ) $cl->SetSortMode ( SPH_SORT_EXPR, $sortexpr ); 40 | if ( $distinct ) $cl->SetGroupDistinct ( $distinct ); 41 | if ( $select ) $cl->SetSelect ( $select ); 42 | if ( $limit ) $cl->SetLimits ( 0, $limit, ( $limit>1000 ) ? $limit : 1000 ); 43 | $cl->SetRankingMode ( $ranker ); 44 | $res = $cl->Query ( $q, $index ); 45 | 46 | /////////////////////// 47 | // print query results 48 | /////////////////////// 49 | 50 | //print_r($res); 51 | 52 | if ( $res===false ) 53 | { 54 | print "Query failed: " . $cl->GetLastError() . ".\n"; 55 | 56 | } else 57 | { 58 | if ( $cl->GetLastWarning() ) 59 | print "WARNING: " . $cl->GetLastWarning() . "\n\n"; 60 | 61 | print "Query '$q' retrieved $res[total] of $res[total_found] matches in $res[time] sec.\n"; 62 | print "Query stats:\n"; 63 | if ( is_array($res["words"]) ) 64 | foreach ( $res["words"] as $word => $info ) 65 | print " '$word' found $info[hits] times in $info[docs] documents\n"; 66 | print "\n"; 67 | 68 | if ( is_array($res["matches"]) ) 69 | { 70 | $n = 1; 71 | print "Matches:\n"; 72 | foreach ( $res["matches"] as $docinfo ) 73 | { 74 | print "$n. doc_id=$docinfo[id], weight=$docinfo[weight]"; 75 | foreach ( $res["attrs"] as $attrname => $attrtype ) 76 | { 77 | $value = $docinfo["attrs"][$attrname]; 78 | if ( $attrtype & SPH_ATTR_MULTI ) 79 | { 80 | $value = "(" . join ( ",", $value ) .")"; 81 | } else 82 | { 83 | if ( $attrtype==SPH_ATTR_TIMESTAMP ) 84 | $value = date ( "Y-m-d H:i:s", $value ); 85 | } 86 | print ", $attrname=$value"; 87 | } 88 | print "\n"; 89 | $n++; 90 | } 91 | } 92 | } 93 | $cl->Close(); 94 | print "\n"; 95 | 96 | 97 | 98 | 99 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------- 2 | The PHP License, version 3.01 3 | Copyright (c) 1999 - 2010 The PHP Group. All rights reserved. 4 | -------------------------------------------------------------------- 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, is permitted provided that the following conditions 8 | are met: 9 | 10 | 1. Redistributions of source code must retain the above copyright 11 | notice, this list of conditions and the following disclaimer. 12 | 13 | 2. Redistributions in binary form must reproduce the above copyright 14 | notice, this list of conditions and the following disclaimer in 15 | the documentation and/or other materials provided with the 16 | distribution. 17 | 18 | 3. The name "PHP" must not be used to endorse or promote products 19 | derived from this software without prior written permission. For 20 | written permission, please contact group@php.net. 21 | 22 | 4. Products derived from this software may not be called "PHP", nor 23 | may "PHP" appear in their name, without prior written permission 24 | from group@php.net. You may indicate that your software works in 25 | conjunction with PHP by saying "Foo for PHP" instead of calling 26 | it "PHP Foo" or "phpfoo" 27 | 28 | 5. The PHP Group may publish revised and/or new versions of the 29 | license from time to time. Each version will be given a 30 | distinguishing version number. 31 | Once covered code has been published under a particular version 32 | of the license, you may always continue to use it under the terms 33 | of that version. You may also choose to use such covered code 34 | under the terms of any subsequent version of the license 35 | published by the PHP Group. No one other than the PHP Group has 36 | the right to modify the terms applicable to covered code created 37 | under this License. 38 | 39 | 6. Redistributions of any form whatsoever must retain the following 40 | acknowledgment: 41 | "This product includes PHP software, freely available from 42 | ". 43 | 44 | THIS SOFTWARE IS PROVIDED BY THE PHP DEVELOPMENT TEAM ``AS IS'' AND 45 | ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 46 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 47 | PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE PHP 48 | DEVELOPMENT TEAM OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 49 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 50 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 51 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 52 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 53 | STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 54 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 55 | OF THE POSSIBILITY OF SUCH DAMAGE. 56 | 57 | -------------------------------------------------------------------- 58 | 59 | This software consists of voluntary contributions made by many 60 | individuals on behalf of the PHP Group. 61 | 62 | The PHP Group can be contacted via Email at group@php.net. 63 | 64 | For more information on the PHP Group and the PHP project, 65 | please see . 66 | 67 | PHP includes the Zend Engine, freely available at 68 | . 69 | -------------------------------------------------------------------------------- /config.m4: -------------------------------------------------------------------------------- 1 | dnl $Id$ 2 | 3 | AC_DEFUN([SPHINX_CHECK_ENUM], [ 4 | AC_MSG_CHECKING([for $1 in sphinxclient.h]) 5 | AC_TRY_COMPILE([#include ], [int i = $1], 6 | [ 7 | AC_DEFINE([HAVE_]$1, [], [Define if $1 is available]) 8 | AC_MSG_RESULT([found]) 9 | ], [ 10 | AC_MSG_RESULT([not found]) 11 | ]) 12 | ]) 13 | 14 | PHP_ARG_WITH(sphinx, for sphinx support, 15 | [ --with-sphinx Include sphinx support]) 16 | 17 | if test "$PHP_SPHINX" != "no"; then 18 | 19 | SEARCH_PATH="/usr/local /usr /local /opt" 20 | SEARCH_FOR="/include/sphinxclient.h" 21 | 22 | if test "$PHP_SPHINX" = "yes"; then 23 | AC_MSG_CHECKING([for libsphinxclient headers in default path]) 24 | for i in $SEARCH_PATH ; do 25 | if test -r $i/$SEARCH_FOR; then 26 | SPHINX_DIR=$i 27 | AC_MSG_RESULT(found in $i) 28 | fi 29 | done 30 | else 31 | AC_MSG_CHECKING([for libsphinxclient headers in $PHP_SPHINX]) 32 | if test -r $PHP_SPHINX/$SEARCH_FOR; then 33 | SPHINX_DIR=$PHP_SPHINX 34 | AC_MSG_RESULT([found]) 35 | fi 36 | fi 37 | 38 | if test -z "$SPHINX_DIR"; then 39 | AC_MSG_RESULT([not found]) 40 | AC_MSG_ERROR([Cannot find libsphinxclient headers]) 41 | fi 42 | 43 | PHP_ADD_INCLUDE($SPHINX_DIR/include) 44 | 45 | LIBNAME=sphinxclient 46 | LIBSYMBOL=sphinx_create 47 | 48 | PHP_CHECK_LIBRARY($LIBNAME,$LIBSYMBOL, 49 | [ 50 | PHP_ADD_LIBRARY_WITH_PATH($LIBNAME, $SPHINX_DIR/$PHP_LIBDIR, SPHINX_SHARED_LIBADD) 51 | AC_DEFINE(HAVE_SPHINXLIB,1,[ ]) 52 | ],[ 53 | AC_MSG_ERROR([wrong libsphinxclient version or lib not found]) 54 | ],[ 55 | -L$SPHINX_DIR/$PHP_LIBDIR -lm 56 | ]) 57 | 58 | PHP_CHECK_LIBRARY($LIBNAME,sphinx_get_string, 59 | [ 60 | PHP_ADD_LIBRARY_WITH_PATH($LIBNAME, $SPHINX_DIR/$PHP_LIBDIR, SPHINX_SHARED_LIBADD) 61 | AC_DEFINE(LIBSPHINX_VERSION_ID,110,[ ]) 62 | LIBSPHINX_VERSION_ID="110" 63 | ],[],[ 64 | -L$SPHINX_DIR/$PHP_LIBDIR -lm 65 | ]) 66 | 67 | if test "x$LIBSPHINX_VERSION_ID" = "x"; then 68 | PHP_CHECK_LIBRARY($LIBNAME,sphinx_set_select, 69 | [ 70 | PHP_ADD_LIBRARY_WITH_PATH($LIBNAME, $SPHINX_DIR/$PHP_LIBDIR, SPHINX_SHARED_LIBADD) 71 | AC_DEFINE(LIBSPHINX_VERSION_ID,99,[ ]) 72 | LIBSPHINX_VERSION_ID="99" 73 | ],[],[ 74 | -L$SPHINX_DIR/$PHP_LIBDIR -lm 75 | ]) 76 | fi 77 | 78 | if test "x$LIBSPHINX_VERSION_ID" = "x"; then 79 | AC_DEFINE(LIBSPHINX_VERSION_ID,98,[ ]) 80 | fi 81 | 82 | PHP_CHECK_LIBRARY($LIBNAME,sphinx_add_filter_string, 83 | [ 84 | AC_DEFINE(HAVE_SPHINX_ADD_FILTER_STRING,1,[ ]) 85 | ],[],[ 86 | -L$SPHINX_DIR/$PHP_LIBDIR -lm 87 | ]) 88 | 89 | _SAVE_CFLAGS=$CFLAGS 90 | CFLAGS="$CFLAGS -I$SPHINX_DIR/include" 91 | AC_CACHE_CHECK([for new sphinx_set_ranking_mode() signature], ac_cv_3arg_setrankingmode, 92 | [AC_TRY_COMPILE([#include ], [sphinx_set_ranking_mode(0, 0, 0)], 93 | ac_cv_3arg_setrankingmode=yes, ac_cv_3arg_setrankingmode=no)]) 94 | if test "$ac_cv_3arg_setrankingmode" = yes; then 95 | AC_DEFINE(HAVE_3ARG_SPHINX_SET_RANKING_MODE,1,[Whether we have 3 arg sphinx_set_ranking_mode()]) 96 | fi 97 | CFLAGS=$_SAVE_CFLAGS 98 | 99 | _SAVE_CPPFLAGS=$CPPFLAGS 100 | CPPFLAGS="$CPPFLAGS -I$SPHINX_DIR/include" 101 | 102 | SPHINX_CHECK_ENUM(SPH_RANK_PROXIMITY) 103 | SPHINX_CHECK_ENUM(SPH_RANK_MATCHANY) 104 | SPHINX_CHECK_ENUM(SPH_RANK_FIELDMASK) 105 | SPHINX_CHECK_ENUM(SPH_RANK_SPH04) 106 | SPHINX_CHECK_ENUM(SPH_RANK_EXPR) 107 | SPHINX_CHECK_ENUM(SPH_RANK_TOTAL) 108 | 109 | CPPFLAGS=$_SAVE_CPPFLAGS 110 | 111 | PHP_SUBST(SPHINX_SHARED_LIBADD) 112 | 113 | PHP_NEW_EXTENSION(sphinx, sphinx.c, $ext_shared) 114 | fi 115 | -------------------------------------------------------------------------------- /tests/test_1.10.php: -------------------------------------------------------------------------------- 1 | SetFieldWeights ( array ( 'title' => 100, 'content' => 1 ) ); 13 | 14 | if ( $test_extras ) 15 | { 16 | $client->SetOverride ( "group_id", SPH_ATTR_INTEGER, array ( $override_docid => $override_value) ); 17 | $client->SetSelect ( "*, group_id*1000+@id*10 AS q" ); 18 | } 19 | 20 | $client->SetArrayResult(true); 21 | $res = $client->Query ( $query, $index ); 22 | if ( $res === false ) 23 | { 24 | print "Query failed: " . $client->GetLastError() . ".\n"; 25 | } 26 | else 27 | { 28 | if ( $client->GetLastWarning() ) 29 | print "WARNING: " . $client->GetLastWarning() . "\n\n"; 30 | 31 | print "Query '$query' retrieved $res[total] of $res[total_found] matches in $res[time] sec.\n"; 32 | print "Query stats:\n"; 33 | if ( is_array($res["words"]) ) 34 | foreach ( $res["words"] as $word => $info ) 35 | print " '$word' found $info[hits] times in $info[docs] documents\n"; 36 | print "\n"; 37 | 38 | if ( is_array($res["matches"]) ) 39 | { 40 | $n = 1; 41 | print "Matches:\n"; 42 | foreach ( $res["matches"] as $docinfo ) 43 | { 44 | print "$n. doc_id=$docinfo[id], weight=$docinfo[weight]"; 45 | foreach ( $res["attrs"] as $attrname => $attrtype ) 46 | { 47 | $value = $docinfo["attrs"][$attrname]; 48 | if ( $attrtype & SPH_ATTR_MULTI ) 49 | { 50 | $value = "(" . join ( ",", $value ) .")"; 51 | } 52 | else 53 | { 54 | if ( $attrtype==SPH_ATTR_TIMESTAMP ) 55 | $value = date ( "Y-m-d H:i:s", $value ); 56 | } 57 | print ", $attrname=$value"; 58 | } 59 | print "\n"; 60 | $n++; 61 | } 62 | } 63 | } 64 | print "\n"; 65 | } 66 | 67 | function test_excerpt ( $client ) 68 | { 69 | $docs = array 70 | ( 71 | "this is my test text to be highlighted, and for the sake of the testing we need to pump its length somewhat", 72 | "another test text to be highlighted, below limit", 73 | "test number three, without phrase match", 74 | "final test, not only without phrase match, but also above limit and with swapped phrase text test as well" 75 | ); 76 | $words = "test text"; 77 | $index = "test1"; 78 | $opts = array 79 | ( 80 | "chunk_separator" => " ... ", 81 | "limit" => 60, 82 | "around" => 3, 83 | ); 84 | 85 | for ( $j=0; $j<2; $j++ ) 86 | { 87 | $opts["exact_phrase"] = $j; 88 | print "exact_phrase=$j\n" ; 89 | 90 | $res = $client->BuildExcerpts ( $docs, $index, $words, $opts ); 91 | if ( !$res ) 92 | { 93 | print "query failed: " . $client->GetLastError() . ".\n" ; 94 | } 95 | else 96 | { 97 | $n = 0; 98 | foreach ( $res as $entry ) 99 | { 100 | $n++; 101 | print "n=$n, res=$entry\n"; 102 | } 103 | } 104 | print "\n"; 105 | } 106 | } 107 | 108 | function test_update ( $client ) 109 | { 110 | $attrs = array( "group_id" , "group_id2" ); 111 | $vals = array( 2 => array ( 22, 33 ) , 4 => array ( 444, 555 ) ); 112 | 113 | $res = $client->UpdateAttributes( "test1", $attrs, $vals); 114 | if ( $res < 0 ) 115 | print "query failed: " . $client->GetLastError() . ".\n\n"; 116 | else 117 | print "update success, $res rows updated\n\n"; 118 | } 119 | 120 | function test_update_mva ( $client ) 121 | { 122 | $attrs = array ( "mva" , "mva2" ); 123 | $vals = array ( 124 | 1 => array( array ( 123, 456 ) , array ( 321 ) ) , 125 | 2 => array( array ( 78, 910, 11 ) , array ( 87, 12 ) ), 126 | 4 => array( array ( 55 ) , array ( 66 , 77 ) ) 127 | ); 128 | 129 | $res = $client->UpdateAttributes( "test1", $attrs, $vals, true ); 130 | if ( $res === false || $res < 0 ) 131 | print "query mva failed: " . $client->GetLastError() . ".\n\n"; 132 | else 133 | print "update mva success, $res rows updated\n\n"; 134 | } 135 | 136 | function test_keywords ( $client ) 137 | { 138 | $words = $client->BuildKeywords("hello test one", "test1", true); 139 | 140 | if ( !$words ) 141 | { 142 | print "build_keywords failed: " . $client->GetLastError() . "\n\n" ; 143 | } 144 | else 145 | { 146 | print "build_keywords result:\n"; 147 | 148 | $n = 0; 149 | foreach ( $words as $entry ) 150 | { 151 | $n++; 152 | printf ( "%d. tokenized=%s, normalized=%s, docs=%d, hits=%d\n", $n, 153 | $entry['tokenized'], $entry['normalized'], 154 | $entry['docs'], $entry['hits'] ); 155 | } 156 | print "\n"; 157 | } 158 | } 159 | 160 | function test_status ( $client ) 161 | { 162 | $status = $client->Status(); 163 | if ( !$status ) 164 | { 165 | print "status failed: " . $client->GetLastError() . "\n\n"; 166 | } 167 | 168 | foreach ( $status as $item ) 169 | { 170 | print "$item[0]: $item[1]\n"; 171 | } 172 | print "\n"; 173 | } 174 | 175 | date_default_timezone_set('Europe/Moscow'); 176 | 177 | $host = "localhost"; 178 | $port = 9312; 179 | 180 | $client = new SphinxClient (); 181 | $client->SetServer ( $host, $port ); 182 | 183 | test_query ( $client, false ); 184 | test_excerpt ( $client ); 185 | test_update ( $client ); 186 | test_update_mva ( $client ); 187 | test_query ( $client, false ); 188 | 189 | test_keywords ( $client ); 190 | test_query ( $client, true ); 191 | 192 | $client->Open(); 193 | test_update ( $client ); 194 | test_update ( $client ); 195 | test_query ( $client, false ); 196 | test_query ( $client, false ); 197 | $client->Close(); 198 | 199 | test_status ( $client ); 200 | 201 | 202 | 203 | -------------------------------------------------------------------------------- /package.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | sphinx 7 | pecl.php.net 8 | Client extension for Sphinx - opensource SQL full-text search engine 9 | 10 | This extension provides bindings for libsphinxclient, client library for Sphinx. 11 | 12 | 13 | Antony Dovgal 14 | tony2001 15 | tony2001@php.net 16 | yes 17 | 18 | 19 | Alexey Romanenko 20 | santiago 21 | santiago739@gmail.com 22 | yes 23 | 24 | 2015-05-21 25 | 26 | 27 | 1.3.3 28 | 1.3.3 29 | 30 | 31 | stable 32 | stable 33 | 34 | PHP 35 | - Added setFilterString() method, available only when built with sphinxclient lib >= 2.2.3. 36 | - Fixed bug #67669 (SphinxClient::escapeString() is missing several symbols) 37 | - Fixed bug #69675 (crash when accessing properties of subclass) 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 5.2.2 53 | 6.0.0 54 | 6.0.0 55 | 56 | 57 | 1.4.0b1 58 | 59 | 60 | 61 | sphinx 62 | 63 | 64 | 65 | 66 | 67 | stablestable 68 | 1.3.21.3.2 69 | 2014-05-06 70 | - Fixed build with older libsphinxclient versions (Dmitry Saprykin) 71 | - Added LICENSE to the package. 72 | 73 | 74 | 75 | stablestable 76 | 1.3.11.3.1 77 | 2014-05-06 78 | - Fixed bug #67216 (segfault on setGroupBy()) (Dmitry Saprykin) 79 | - Fixed bug #66272 (Some constants are not defined) 80 | 81 | 82 | 83 | stablestable 84 | 1.3.01.3.0 85 | 2013-04-04 86 | - Fixed setRankingMode's proto and add new ranking mode constants (patch by Michael Squires) 87 | - Fixed segfault when reading object properties. 88 | - Fixed bug #62026 (Second param in setRankingMode should be optional) 89 | 90 | 91 | 92 | stablestable 93 | 1.2.01.2.0 94 | 2012-04-03 95 | - Added mva support to updateAttributes(). 96 | - Fixed build with 5_4. 97 | - Fixed bug #61392 (build failure with new version of libsphinxclient) 98 | 99 | 100 | 101 | stablestable 102 | 1.1.01.1.0 103 | 2010-09-17 104 | - Added support for new API in sphinx 1.10 105 | - Fixed PECL bug #18325 (support 1.10-beta string attributes) 106 | - Fixed PECL bug #17558 (time taken in search result was incorrect) 107 | 108 | 109 | 110 | stablestable 111 | 1.0.41.0.4 112 | 2010-02-26 113 | - Fixed PECL bug #17007 (incorrect query causes SEGFAULT) 114 | 115 | 116 | stablestable 117 | 1.0.31.0.3 118 | 2010-01-12 119 | - Added support for open(), close(), status() and setOverride() available in sphinx 0.9.9 new API 120 | 121 | 122 | stablestable 123 | 1.0.21.0.2 124 | 2009-12-03 125 | - Fixed crash when extending class doesn't call parent constructor. 126 | 127 | 128 | stablestable 129 | 1.0.11.0.1 130 | 2009-11-03 131 | - Fixed build failure with PHP 5.1.x. 132 | - Added support for setSelect(), available in sphinx 0.9.9 new API (patch by Olivier Poitrey) 133 | 134 | 135 | 136 | stablestable 137 | 1.0.01.0.0 138 | 2009-01-29 139 | - Added __sleep() and __wakeup() methods to prevent (un)serialization of SphinxClient instances. 140 | - Fixed PECL bug #15033 (segfaults on SPARC because of unaligned read). 141 | - Fixed PECL bug #14930 (configure fails on freebsd). (patch by nobleclem at fatalexception dot us) 142 | - Fixed PECL bug #14752 (32-bit Client does not work with 64-bit ids). (nobleclem at fatalexception dot us, Tony) 143 | 144 | 145 | 146 | betabeta 147 | 0.2.00.2.0 148 | 2008-07-31 149 | - Updated SphinxClient::updateAttributes() to return number of updated attributes on success. 150 | - Updated SphinxClient::setIDRange() to accept integers, not floats. 151 | - Updated SphinxClient::addQuery() to return integer on success. 152 | 153 | 154 | 155 | betabeta 156 | 0.1.00.1.0 157 | 2008-07-21 158 | - Initial PECL release. 159 | 160 | 161 | 162 | 163 | -------------------------------------------------------------------------------- /tests/api/sphinxapi-1.10.php: -------------------------------------------------------------------------------- 1 | =8 ) 131 | { 132 | $v = (int)$v; 133 | return pack ( "NN", $v>>32, $v&0xFFFFFFFF ); 134 | } 135 | 136 | // x32, int 137 | if ( is_int($v) ) 138 | return pack ( "NN", $v < 0 ? -1 : 0, $v ); 139 | 140 | // x32, bcmath 141 | if ( function_exists("bcmul") ) 142 | { 143 | if ( bccomp ( $v, 0 ) == -1 ) 144 | $v = bcadd ( "18446744073709551616", $v ); 145 | $h = bcdiv ( $v, "4294967296", 0 ); 146 | $l = bcmod ( $v, "4294967296" ); 147 | return pack ( "NN", (float)$h, (float)$l ); // conversion to float is intentional; int would lose 31st bit 148 | } 149 | 150 | // x32, no-bcmath 151 | $p = max(0, strlen($v) - 13); 152 | $lo = abs((float)substr($v, $p)); 153 | $hi = abs((float)substr($v, 0, $p)); 154 | 155 | $m = $lo + $hi*1316134912.0; // (10 ^ 13) % (1 << 32) = 1316134912 156 | $q = floor($m/4294967296.0); 157 | $l = $m - ($q*4294967296.0); 158 | $h = $hi*2328.0 + $q; // (10 ^ 13) / (1 << 32) = 2328 159 | 160 | if ( $v<0 ) 161 | { 162 | if ( $l==0 ) 163 | $h = 4294967296.0 - $h; 164 | else 165 | { 166 | $h = 4294967295.0 - $h; 167 | $l = 4294967296.0 - $l; 168 | } 169 | } 170 | return pack ( "NN", $h, $l ); 171 | } 172 | 173 | /// pack 64-bit unsigned 174 | function sphPackU64 ( $v ) 175 | { 176 | assert ( is_numeric($v) ); 177 | 178 | // x64 179 | if ( PHP_INT_SIZE>=8 ) 180 | { 181 | assert ( $v>=0 ); 182 | 183 | // x64, int 184 | if ( is_int($v) ) 185 | return pack ( "NN", $v>>32, $v&0xFFFFFFFF ); 186 | 187 | // x64, bcmath 188 | if ( function_exists("bcmul") ) 189 | { 190 | $h = bcdiv ( $v, 4294967296, 0 ); 191 | $l = bcmod ( $v, 4294967296 ); 192 | return pack ( "NN", $h, $l ); 193 | } 194 | 195 | // x64, no-bcmath 196 | $p = max ( 0, strlen($v) - 13 ); 197 | $lo = (int)substr ( $v, $p ); 198 | $hi = (int)substr ( $v, 0, $p ); 199 | 200 | $m = $lo + $hi*1316134912; 201 | $l = $m % 4294967296; 202 | $h = $hi*2328 + (int)($m/4294967296); 203 | 204 | return pack ( "NN", $h, $l ); 205 | } 206 | 207 | // x32, int 208 | if ( is_int($v) ) 209 | return pack ( "NN", 0, $v ); 210 | 211 | // x32, bcmath 212 | if ( function_exists("bcmul") ) 213 | { 214 | $h = bcdiv ( $v, "4294967296", 0 ); 215 | $l = bcmod ( $v, "4294967296" ); 216 | return pack ( "NN", (float)$h, (float)$l ); // conversion to float is intentional; int would lose 31st bit 217 | } 218 | 219 | // x32, no-bcmath 220 | $p = max(0, strlen($v) - 13); 221 | $lo = (float)substr($v, $p); 222 | $hi = (float)substr($v, 0, $p); 223 | 224 | $m = $lo + $hi*1316134912.0; 225 | $q = floor($m / 4294967296.0); 226 | $l = $m - ($q * 4294967296.0); 227 | $h = $hi*2328.0 + $q; 228 | 229 | return pack ( "NN", $h, $l ); 230 | } 231 | 232 | // unpack 64-bit unsigned 233 | function sphUnpackU64 ( $v ) 234 | { 235 | list ( $hi, $lo ) = array_values ( unpack ( "N*N*", $v ) ); 236 | 237 | if ( PHP_INT_SIZE>=8 ) 238 | { 239 | if ( $hi<0 ) $hi += (1<<32); // because php 5.2.2 to 5.2.5 is totally fucked up again 240 | if ( $lo<0 ) $lo += (1<<32); 241 | 242 | // x64, int 243 | if ( $hi<=2147483647 ) 244 | return ($hi<<32) + $lo; 245 | 246 | // x64, bcmath 247 | if ( function_exists("bcmul") ) 248 | return bcadd ( $lo, bcmul ( $hi, "4294967296" ) ); 249 | 250 | // x64, no-bcmath 251 | $C = 100000; 252 | $h = ((int)($hi / $C) << 32) + (int)($lo / $C); 253 | $l = (($hi % $C) << 32) + ($lo % $C); 254 | if ( $l>$C ) 255 | { 256 | $h += (int)($l / $C); 257 | $l = $l % $C; 258 | } 259 | 260 | if ( $h==0 ) 261 | return $l; 262 | return sprintf ( "%d%05d", $h, $l ); 263 | } 264 | 265 | // x32, int 266 | if ( $hi==0 ) 267 | { 268 | if ( $lo>0 ) 269 | return $lo; 270 | return sprintf ( "%u", $lo ); 271 | } 272 | 273 | $hi = sprintf ( "%u", $hi ); 274 | $lo = sprintf ( "%u", $lo ); 275 | 276 | // x32, bcmath 277 | if ( function_exists("bcmul") ) 278 | return bcadd ( $lo, bcmul ( $hi, "4294967296" ) ); 279 | 280 | // x32, no-bcmath 281 | $hi = (float)$hi; 282 | $lo = (float)$lo; 283 | 284 | $q = floor($hi/10000000.0); 285 | $r = $hi - $q*10000000.0; 286 | $m = $lo + $r*4967296.0; 287 | $mq = floor($m/10000000.0); 288 | $l = $m - $mq*10000000.0; 289 | $h = $q*4294967296.0 + $r*429.0 + $mq; 290 | 291 | $h = sprintf ( "%.0f", $h ); 292 | $l = sprintf ( "%07.0f", $l ); 293 | if ( $h=="0" ) 294 | return sprintf( "%.0f", (float)$l ); 295 | return $h . $l; 296 | } 297 | 298 | // unpack 64-bit signed 299 | function sphUnpackI64 ( $v ) 300 | { 301 | list ( $hi, $lo ) = array_values ( unpack ( "N*N*", $v ) ); 302 | 303 | // x64 304 | if ( PHP_INT_SIZE>=8 ) 305 | { 306 | if ( $hi<0 ) $hi += (1<<32); // because php 5.2.2 to 5.2.5 is totally fucked up again 307 | if ( $lo<0 ) $lo += (1<<32); 308 | 309 | return ($hi<<32) + $lo; 310 | } 311 | 312 | // x32, int 313 | if ( $hi==0 ) 314 | { 315 | if ( $lo>0 ) 316 | return $lo; 317 | return sprintf ( "%u", $lo ); 318 | } 319 | // x32, int 320 | elseif ( $hi==-1 ) 321 | { 322 | if ( $lo<0 ) 323 | return $lo; 324 | return sprintf ( "%.0f", $lo - 4294967296.0 ); 325 | } 326 | 327 | $neg = ""; 328 | $c = 0; 329 | if ( $hi<0 ) 330 | { 331 | $hi = ~$hi; 332 | $lo = ~$lo; 333 | $c = 1; 334 | $neg = "-"; 335 | } 336 | 337 | $hi = sprintf ( "%u", $hi ); 338 | $lo = sprintf ( "%u", $lo ); 339 | 340 | // x32, bcmath 341 | if ( function_exists("bcmul") ) 342 | return $neg . bcadd ( bcadd ( $lo, bcmul ( $hi, "4294967296" ) ), $c ); 343 | 344 | // x32, no-bcmath 345 | $hi = (float)$hi; 346 | $lo = (float)$lo; 347 | 348 | $q = floor($hi/10000000.0); 349 | $r = $hi - $q*10000000.0; 350 | $m = $lo + $r*4967296.0; 351 | $mq = floor($m/10000000.0); 352 | $l = $m - $mq*10000000.0 + $c; 353 | $h = $q*4294967296.0 + $r*429.0 + $mq; 354 | if ( $l==10000000 ) 355 | { 356 | $l = 0; 357 | $h += 1; 358 | } 359 | 360 | $h = sprintf ( "%.0f", $h ); 361 | $l = sprintf ( "%07.0f", $l ); 362 | if ( $h=="0" ) 363 | return $neg . sprintf( "%.0f", (float)$l ); 364 | return $neg . $h . $l; 365 | } 366 | 367 | 368 | function sphFixUint ( $value ) 369 | { 370 | if ( PHP_INT_SIZE>=8 ) 371 | { 372 | // x64 route, workaround broken unpack() in 5.2.2+ 373 | if ( $value<0 ) $value += (1<<32); 374 | return $value; 375 | } 376 | else 377 | { 378 | // x32 route, workaround php signed/unsigned braindamage 379 | return sprintf ( "%u", $value ); 380 | } 381 | } 382 | 383 | 384 | /// sphinx searchd client class 385 | class SphinxClient 386 | { 387 | var $_host; ///< searchd host (default is "localhost") 388 | var $_port; ///< searchd port (default is 9312) 389 | var $_offset; ///< how many records to seek from result-set start (default is 0) 390 | var $_limit; ///< how many records to return from result-set starting at offset (default is 20) 391 | var $_mode; ///< query matching mode (default is SPH_MATCH_ALL) 392 | var $_weights; ///< per-field weights (default is 1 for all fields) 393 | var $_sort; ///< match sorting mode (default is SPH_SORT_RELEVANCE) 394 | var $_sortby; ///< attribute to sort by (defualt is "") 395 | var $_min_id; ///< min ID to match (default is 0, which means no limit) 396 | var $_max_id; ///< max ID to match (default is 0, which means no limit) 397 | var $_filters; ///< search filters 398 | var $_groupby; ///< group-by attribute name 399 | var $_groupfunc; ///< group-by function (to pre-process group-by attribute value with) 400 | var $_groupsort; ///< group-by sorting clause (to sort groups in result set with) 401 | var $_groupdistinct;///< group-by count-distinct attribute 402 | var $_maxmatches; ///< max matches to retrieve 403 | var $_cutoff; ///< cutoff to stop searching at (default is 0) 404 | var $_retrycount; ///< distributed retries count 405 | var $_retrydelay; ///< distributed retries delay 406 | var $_anchor; ///< geographical anchor point 407 | var $_indexweights; ///< per-index weights 408 | var $_ranker; ///< ranking mode (default is SPH_RANK_PROXIMITY_BM25) 409 | var $_maxquerytime; ///< max query time, milliseconds (default is 0, do not limit) 410 | var $_fieldweights; ///< per-field-name weights 411 | var $_overrides; ///< per-query attribute values overrides 412 | var $_select; ///< select-list (attributes or expressions, with optional aliases) 413 | 414 | var $_error; ///< last error message 415 | var $_warning; ///< last warning message 416 | var $_connerror; ///< connection error vs remote error flag 417 | 418 | var $_reqs; ///< requests array for multi-query 419 | var $_mbenc; ///< stored mbstring encoding 420 | var $_arrayresult; ///< whether $result["matches"] should be a hash or an array 421 | var $_timeout; ///< connect timeout 422 | 423 | ///////////////////////////////////////////////////////////////////////////// 424 | // common stuff 425 | ///////////////////////////////////////////////////////////////////////////// 426 | 427 | /// create a new client object and fill defaults 428 | function SphinxClient () 429 | { 430 | // per-client-object settings 431 | $this->_host = "localhost"; 432 | $this->_port = 9312; 433 | $this->_path = false; 434 | $this->_socket = false; 435 | 436 | // per-query settings 437 | $this->_offset = 0; 438 | $this->_limit = 20; 439 | $this->_mode = SPH_MATCH_ALL; 440 | $this->_weights = array (); 441 | $this->_sort = SPH_SORT_RELEVANCE; 442 | $this->_sortby = ""; 443 | $this->_min_id = 0; 444 | $this->_max_id = 0; 445 | $this->_filters = array (); 446 | $this->_groupby = ""; 447 | $this->_groupfunc = SPH_GROUPBY_DAY; 448 | $this->_groupsort = "@group desc"; 449 | $this->_groupdistinct= ""; 450 | $this->_maxmatches = 1000; 451 | $this->_cutoff = 0; 452 | $this->_retrycount = 0; 453 | $this->_retrydelay = 0; 454 | $this->_anchor = array (); 455 | $this->_indexweights= array (); 456 | $this->_ranker = SPH_RANK_PROXIMITY_BM25; 457 | $this->_maxquerytime= 0; 458 | $this->_fieldweights= array(); 459 | $this->_overrides = array(); 460 | $this->_select = "*"; 461 | 462 | $this->_error = ""; // per-reply fields (for single-query case) 463 | $this->_warning = ""; 464 | $this->_connerror = false; 465 | 466 | $this->_reqs = array (); // requests storage (for multi-query case) 467 | $this->_mbenc = ""; 468 | $this->_arrayresult = false; 469 | $this->_timeout = 0; 470 | } 471 | 472 | function __destruct() 473 | { 474 | if ( $this->_socket !== false ) 475 | fclose ( $this->_socket ); 476 | } 477 | 478 | /// get last error message (string) 479 | function GetLastError () 480 | { 481 | return $this->_error; 482 | } 483 | 484 | /// get last warning message (string) 485 | function GetLastWarning () 486 | { 487 | return $this->_warning; 488 | } 489 | 490 | /// get last error flag (to tell network connection errors from searchd errors or broken responses) 491 | function IsConnectError() 492 | { 493 | return $this->_connerror; 494 | } 495 | 496 | /// set searchd host name (string) and port (integer) 497 | function SetServer ( $host, $port = 0 ) 498 | { 499 | assert ( is_string($host) ); 500 | if ( $host[0] == '/') 501 | { 502 | $this->_path = 'unix://' . $host; 503 | return; 504 | } 505 | if ( substr ( $host, 0, 7 )=="unix://" ) 506 | { 507 | $this->_path = $host; 508 | return; 509 | } 510 | 511 | assert ( is_int($port) ); 512 | $this->_host = $host; 513 | $this->_port = $port; 514 | $this->_path = ''; 515 | 516 | } 517 | 518 | /// set server connection timeout (0 to remove) 519 | function SetConnectTimeout ( $timeout ) 520 | { 521 | assert ( is_numeric($timeout) ); 522 | $this->_timeout = $timeout; 523 | } 524 | 525 | 526 | function _Send ( $handle, $data, $length ) 527 | { 528 | if ( feof($handle) || fwrite ( $handle, $data, $length ) !== $length ) 529 | { 530 | $this->_error = 'connection unexpectedly closed (timed out?)'; 531 | $this->_connerror = true; 532 | return false; 533 | } 534 | return true; 535 | } 536 | 537 | ///////////////////////////////////////////////////////////////////////////// 538 | 539 | /// enter mbstring workaround mode 540 | function _MBPush () 541 | { 542 | $this->_mbenc = ""; 543 | if ( ini_get ( "mbstring.func_overload" ) & 2 ) 544 | { 545 | $this->_mbenc = mb_internal_encoding(); 546 | mb_internal_encoding ( "latin1" ); 547 | } 548 | } 549 | 550 | /// leave mbstring workaround mode 551 | function _MBPop () 552 | { 553 | if ( $this->_mbenc ) 554 | mb_internal_encoding ( $this->_mbenc ); 555 | } 556 | 557 | /// connect to searchd server 558 | function _Connect () 559 | { 560 | if ( $this->_socket!==false ) 561 | { 562 | // we are in persistent connection mode, so we have a socket 563 | // however, need to check whether it's still alive 564 | if ( !@feof ( $this->_socket ) ) 565 | return $this->_socket; 566 | 567 | // force reopen 568 | $this->_socket = false; 569 | } 570 | 571 | $errno = 0; 572 | $errstr = ""; 573 | $this->_connerror = false; 574 | 575 | if ( $this->_path ) 576 | { 577 | $host = $this->_path; 578 | $port = 0; 579 | } 580 | else 581 | { 582 | $host = $this->_host; 583 | $port = $this->_port; 584 | } 585 | 586 | if ( $this->_timeout<=0 ) 587 | $fp = @fsockopen ( $host, $port, $errno, $errstr ); 588 | else 589 | $fp = @fsockopen ( $host, $port, $errno, $errstr, $this->_timeout ); 590 | 591 | if ( !$fp ) 592 | { 593 | if ( $this->_path ) 594 | $location = $this->_path; 595 | else 596 | $location = "{$this->_host}:{$this->_port}"; 597 | 598 | $errstr = trim ( $errstr ); 599 | $this->_error = "connection to $location failed (errno=$errno, msg=$errstr)"; 600 | $this->_connerror = true; 601 | return false; 602 | } 603 | 604 | // send my version 605 | // this is a subtle part. we must do it before (!) reading back from searchd. 606 | // because otherwise under some conditions (reported on FreeBSD for instance) 607 | // TCP stack could throttle write-write-read pattern because of Nagle. 608 | if ( !$this->_Send ( $fp, pack ( "N", 1 ), 4 ) ) 609 | { 610 | fclose ( $fp ); 611 | $this->_error = "failed to send client protocol version"; 612 | return false; 613 | } 614 | 615 | // check version 616 | list(,$v) = unpack ( "N*", fread ( $fp, 4 ) ); 617 | $v = (int)$v; 618 | if ( $v<1 ) 619 | { 620 | fclose ( $fp ); 621 | $this->_error = "expected searchd protocol version 1+, got version '$v'"; 622 | return false; 623 | } 624 | 625 | return $fp; 626 | } 627 | 628 | /// get and check response packet from searchd server 629 | function _GetResponse ( $fp, $client_ver ) 630 | { 631 | $response = ""; 632 | $len = 0; 633 | 634 | $header = fread ( $fp, 8 ); 635 | if ( strlen($header)==8 ) 636 | { 637 | list ( $status, $ver, $len ) = array_values ( unpack ( "n2a/Nb", $header ) ); 638 | $left = $len; 639 | while ( $left>0 && !feof($fp) ) 640 | { 641 | $chunk = fread ( $fp, $left ); 642 | if ( $chunk ) 643 | { 644 | $response .= $chunk; 645 | $left -= strlen($chunk); 646 | } 647 | } 648 | } 649 | if ( $this->_socket === false ) 650 | fclose ( $fp ); 651 | 652 | // check response 653 | $read = strlen ( $response ); 654 | if ( !$response || $read!=$len ) 655 | { 656 | $this->_error = $len 657 | ? "failed to read searchd response (status=$status, ver=$ver, len=$len, read=$read)" 658 | : "received zero-sized searchd response"; 659 | return false; 660 | } 661 | 662 | // check status 663 | if ( $status==SEARCHD_WARNING ) 664 | { 665 | list(,$wlen) = unpack ( "N*", substr ( $response, 0, 4 ) ); 666 | $this->_warning = substr ( $response, 4, $wlen ); 667 | return substr ( $response, 4+$wlen ); 668 | } 669 | if ( $status==SEARCHD_ERROR ) 670 | { 671 | $this->_error = "searchd error: " . substr ( $response, 4 ); 672 | return false; 673 | } 674 | if ( $status==SEARCHD_RETRY ) 675 | { 676 | $this->_error = "temporary searchd error: " . substr ( $response, 4 ); 677 | return false; 678 | } 679 | if ( $status!=SEARCHD_OK ) 680 | { 681 | $this->_error = "unknown status code '$status'"; 682 | return false; 683 | } 684 | 685 | // check version 686 | if ( $ver<$client_ver ) 687 | { 688 | $this->_warning = sprintf ( "searchd command v.%d.%d older than client's v.%d.%d, some options might not work", 689 | $ver>>8, $ver&0xff, $client_ver>>8, $client_ver&0xff ); 690 | } 691 | 692 | return $response; 693 | } 694 | 695 | ///////////////////////////////////////////////////////////////////////////// 696 | // searching 697 | ///////////////////////////////////////////////////////////////////////////// 698 | 699 | /// set offset and count into result set, 700 | /// and optionally set max-matches and cutoff limits 701 | function SetLimits ( $offset, $limit, $max=0, $cutoff=0 ) 702 | { 703 | assert ( is_int($offset) ); 704 | assert ( is_int($limit) ); 705 | assert ( $offset>=0 ); 706 | assert ( $limit>0 ); 707 | assert ( $max>=0 ); 708 | $this->_offset = $offset; 709 | $this->_limit = $limit; 710 | if ( $max>0 ) 711 | $this->_maxmatches = $max; 712 | if ( $cutoff>0 ) 713 | $this->_cutoff = $cutoff; 714 | } 715 | 716 | /// set maximum query time, in milliseconds, per-index 717 | /// integer, 0 means "do not limit" 718 | function SetMaxQueryTime ( $max ) 719 | { 720 | assert ( is_int($max) ); 721 | assert ( $max>=0 ); 722 | $this->_maxquerytime = $max; 723 | } 724 | 725 | /// set matching mode 726 | function SetMatchMode ( $mode ) 727 | { 728 | assert ( $mode==SPH_MATCH_ALL 729 | || $mode==SPH_MATCH_ANY 730 | || $mode==SPH_MATCH_PHRASE 731 | || $mode==SPH_MATCH_BOOLEAN 732 | || $mode==SPH_MATCH_EXTENDED 733 | || $mode==SPH_MATCH_FULLSCAN 734 | || $mode==SPH_MATCH_EXTENDED2 ); 735 | $this->_mode = $mode; 736 | } 737 | 738 | /// set ranking mode 739 | function SetRankingMode ( $ranker ) 740 | { 741 | assert ( $ranker>=0 && $ranker_ranker = $ranker; 743 | } 744 | 745 | /// set matches sorting mode 746 | function SetSortMode ( $mode, $sortby="" ) 747 | { 748 | assert ( 749 | $mode==SPH_SORT_RELEVANCE || 750 | $mode==SPH_SORT_ATTR_DESC || 751 | $mode==SPH_SORT_ATTR_ASC || 752 | $mode==SPH_SORT_TIME_SEGMENTS || 753 | $mode==SPH_SORT_EXTENDED || 754 | $mode==SPH_SORT_EXPR ); 755 | assert ( is_string($sortby) ); 756 | assert ( $mode==SPH_SORT_RELEVANCE || strlen($sortby)>0 ); 757 | 758 | $this->_sort = $mode; 759 | $this->_sortby = $sortby; 760 | } 761 | 762 | /// bind per-field weights by order 763 | /// DEPRECATED; use SetFieldWeights() instead 764 | function SetWeights ( $weights ) 765 | { 766 | assert ( is_array($weights) ); 767 | foreach ( $weights as $weight ) 768 | assert ( is_int($weight) ); 769 | 770 | $this->_weights = $weights; 771 | } 772 | 773 | /// bind per-field weights by name 774 | function SetFieldWeights ( $weights ) 775 | { 776 | assert ( is_array($weights) ); 777 | foreach ( $weights as $name=>$weight ) 778 | { 779 | assert ( is_string($name) ); 780 | assert ( is_int($weight) ); 781 | } 782 | $this->_fieldweights = $weights; 783 | } 784 | 785 | /// bind per-index weights by name 786 | function SetIndexWeights ( $weights ) 787 | { 788 | assert ( is_array($weights) ); 789 | foreach ( $weights as $index=>$weight ) 790 | { 791 | assert ( is_string($index) ); 792 | assert ( is_int($weight) ); 793 | } 794 | $this->_indexweights = $weights; 795 | } 796 | 797 | /// set IDs range to match 798 | /// only match records if document ID is beetwen $min and $max (inclusive) 799 | function SetIDRange ( $min, $max ) 800 | { 801 | assert ( is_numeric($min) ); 802 | assert ( is_numeric($max) ); 803 | assert ( $min<=$max ); 804 | $this->_min_id = $min; 805 | $this->_max_id = $max; 806 | } 807 | 808 | /// set values set filter 809 | /// only match records where $attribute value is in given set 810 | function SetFilter ( $attribute, $values, $exclude=false ) 811 | { 812 | assert ( is_string($attribute) ); 813 | assert ( is_array($values) ); 814 | assert ( count($values) ); 815 | 816 | if ( is_array($values) && count($values) ) 817 | { 818 | foreach ( $values as $value ) 819 | assert ( is_numeric($value) ); 820 | 821 | $this->_filters[] = array ( "type"=>SPH_FILTER_VALUES, "attr"=>$attribute, "exclude"=>$exclude, "values"=>$values ); 822 | } 823 | } 824 | 825 | /// set range filter 826 | /// only match records if $attribute value is beetwen $min and $max (inclusive) 827 | function SetFilterRange ( $attribute, $min, $max, $exclude=false ) 828 | { 829 | assert ( is_string($attribute) ); 830 | assert ( is_numeric($min) ); 831 | assert ( is_numeric($max) ); 832 | assert ( $min<=$max ); 833 | 834 | $this->_filters[] = array ( "type"=>SPH_FILTER_RANGE, "attr"=>$attribute, "exclude"=>$exclude, "min"=>$min, "max"=>$max ); 835 | } 836 | 837 | /// set float range filter 838 | /// only match records if $attribute value is beetwen $min and $max (inclusive) 839 | function SetFilterFloatRange ( $attribute, $min, $max, $exclude=false ) 840 | { 841 | assert ( is_string($attribute) ); 842 | assert ( is_float($min) ); 843 | assert ( is_float($max) ); 844 | assert ( $min<=$max ); 845 | 846 | $this->_filters[] = array ( "type"=>SPH_FILTER_FLOATRANGE, "attr"=>$attribute, "exclude"=>$exclude, "min"=>$min, "max"=>$max ); 847 | } 848 | 849 | /// setup anchor point for geosphere distance calculations 850 | /// required to use @geodist in filters and sorting 851 | /// latitude and longitude must be in radians 852 | function SetGeoAnchor ( $attrlat, $attrlong, $lat, $long ) 853 | { 854 | assert ( is_string($attrlat) ); 855 | assert ( is_string($attrlong) ); 856 | assert ( is_float($lat) ); 857 | assert ( is_float($long) ); 858 | 859 | $this->_anchor = array ( "attrlat"=>$attrlat, "attrlong"=>$attrlong, "lat"=>$lat, "long"=>$long ); 860 | } 861 | 862 | /// set grouping attribute and function 863 | function SetGroupBy ( $attribute, $func, $groupsort="@group desc" ) 864 | { 865 | assert ( is_string($attribute) ); 866 | assert ( is_string($groupsort) ); 867 | assert ( $func==SPH_GROUPBY_DAY 868 | || $func==SPH_GROUPBY_WEEK 869 | || $func==SPH_GROUPBY_MONTH 870 | || $func==SPH_GROUPBY_YEAR 871 | || $func==SPH_GROUPBY_ATTR 872 | || $func==SPH_GROUPBY_ATTRPAIR ); 873 | 874 | $this->_groupby = $attribute; 875 | $this->_groupfunc = $func; 876 | $this->_groupsort = $groupsort; 877 | } 878 | 879 | /// set count-distinct attribute for group-by queries 880 | function SetGroupDistinct ( $attribute ) 881 | { 882 | assert ( is_string($attribute) ); 883 | $this->_groupdistinct = $attribute; 884 | } 885 | 886 | /// set distributed retries count and delay 887 | function SetRetries ( $count, $delay=0 ) 888 | { 889 | assert ( is_int($count) && $count>=0 ); 890 | assert ( is_int($delay) && $delay>=0 ); 891 | $this->_retrycount = $count; 892 | $this->_retrydelay = $delay; 893 | } 894 | 895 | /// set result set format (hash or array; hash by default) 896 | /// PHP specific; needed for group-by-MVA result sets that may contain duplicate IDs 897 | function SetArrayResult ( $arrayresult ) 898 | { 899 | assert ( is_bool($arrayresult) ); 900 | $this->_arrayresult = $arrayresult; 901 | } 902 | 903 | /// set attribute values override 904 | /// there can be only one override per attribute 905 | /// $values must be a hash that maps document IDs to attribute values 906 | function SetOverride ( $attrname, $attrtype, $values ) 907 | { 908 | assert ( is_string ( $attrname ) ); 909 | assert ( in_array ( $attrtype, array ( SPH_ATTR_INTEGER, SPH_ATTR_TIMESTAMP, SPH_ATTR_BOOL, SPH_ATTR_FLOAT, SPH_ATTR_BIGINT ) ) ); 910 | assert ( is_array ( $values ) ); 911 | 912 | $this->_overrides[$attrname] = array ( "attr"=>$attrname, "type"=>$attrtype, "values"=>$values ); 913 | } 914 | 915 | /// set select-list (attributes or expressions), SQL-like syntax 916 | function SetSelect ( $select ) 917 | { 918 | assert ( is_string ( $select ) ); 919 | $this->_select = $select; 920 | } 921 | 922 | ////////////////////////////////////////////////////////////////////////////// 923 | 924 | /// clear all filters (for multi-queries) 925 | function ResetFilters () 926 | { 927 | $this->_filters = array(); 928 | $this->_anchor = array(); 929 | } 930 | 931 | /// clear groupby settings (for multi-queries) 932 | function ResetGroupBy () 933 | { 934 | $this->_groupby = ""; 935 | $this->_groupfunc = SPH_GROUPBY_DAY; 936 | $this->_groupsort = "@group desc"; 937 | $this->_groupdistinct= ""; 938 | } 939 | 940 | /// clear all attribute value overrides (for multi-queries) 941 | function ResetOverrides () 942 | { 943 | $this->_overrides = array (); 944 | } 945 | 946 | ////////////////////////////////////////////////////////////////////////////// 947 | 948 | /// connect to searchd server, run given search query through given indexes, 949 | /// and return the search results 950 | function Query ( $query, $index="*", $comment="" ) 951 | { 952 | assert ( empty($this->_reqs) ); 953 | 954 | $this->AddQuery ( $query, $index, $comment ); 955 | $results = $this->RunQueries (); 956 | $this->_reqs = array (); // just in case it failed too early 957 | 958 | if ( !is_array($results) ) 959 | return false; // probably network error; error message should be already filled 960 | 961 | $this->_error = $results[0]["error"]; 962 | $this->_warning = $results[0]["warning"]; 963 | if ( $results[0]["status"]==SEARCHD_ERROR ) 964 | return false; 965 | else 966 | return $results[0]; 967 | } 968 | 969 | /// helper to pack floats in network byte order 970 | function _PackFloat ( $f ) 971 | { 972 | $t1 = pack ( "f", $f ); // machine order 973 | list(,$t2) = unpack ( "L*", $t1 ); // int in machine order 974 | return pack ( "N", $t2 ); 975 | } 976 | 977 | /// add query to multi-query batch 978 | /// returns index into results array from RunQueries() call 979 | function AddQuery ( $query, $index="*", $comment="" ) 980 | { 981 | // mbstring workaround 982 | $this->_MBPush (); 983 | 984 | // build request 985 | $req = pack ( "NNNNN", $this->_offset, $this->_limit, $this->_mode, $this->_ranker, $this->_sort ); // mode and limits 986 | $req .= pack ( "N", strlen($this->_sortby) ) . $this->_sortby; 987 | $req .= pack ( "N", strlen($query) ) . $query; // query itself 988 | $req .= pack ( "N", count($this->_weights) ); // weights 989 | foreach ( $this->_weights as $weight ) 990 | $req .= pack ( "N", (int)$weight ); 991 | $req .= pack ( "N", strlen($index) ) . $index; // indexes 992 | $req .= pack ( "N", 1 ); // id64 range marker 993 | $req .= sphPackU64 ( $this->_min_id ) . sphPackU64 ( $this->_max_id ); // id64 range 994 | 995 | // filters 996 | $req .= pack ( "N", count($this->_filters) ); 997 | foreach ( $this->_filters as $filter ) 998 | { 999 | $req .= pack ( "N", strlen($filter["attr"]) ) . $filter["attr"]; 1000 | $req .= pack ( "N", $filter["type"] ); 1001 | switch ( $filter["type"] ) 1002 | { 1003 | case SPH_FILTER_VALUES: 1004 | $req .= pack ( "N", count($filter["values"]) ); 1005 | foreach ( $filter["values"] as $value ) 1006 | $req .= sphPackI64 ( $value ); 1007 | break; 1008 | 1009 | case SPH_FILTER_RANGE: 1010 | $req .= sphPackI64 ( $filter["min"] ) . sphPackI64 ( $filter["max"] ); 1011 | break; 1012 | 1013 | case SPH_FILTER_FLOATRANGE: 1014 | $req .= $this->_PackFloat ( $filter["min"] ) . $this->_PackFloat ( $filter["max"] ); 1015 | break; 1016 | 1017 | default: 1018 | assert ( 0 && "internal error: unhandled filter type" ); 1019 | } 1020 | $req .= pack ( "N", $filter["exclude"] ); 1021 | } 1022 | 1023 | // group-by clause, max-matches count, group-sort clause, cutoff count 1024 | $req .= pack ( "NN", $this->_groupfunc, strlen($this->_groupby) ) . $this->_groupby; 1025 | $req .= pack ( "N", $this->_maxmatches ); 1026 | $req .= pack ( "N", strlen($this->_groupsort) ) . $this->_groupsort; 1027 | $req .= pack ( "NNN", $this->_cutoff, $this->_retrycount, $this->_retrydelay ); 1028 | $req .= pack ( "N", strlen($this->_groupdistinct) ) . $this->_groupdistinct; 1029 | 1030 | // anchor point 1031 | if ( empty($this->_anchor) ) 1032 | { 1033 | $req .= pack ( "N", 0 ); 1034 | } else 1035 | { 1036 | $a =& $this->_anchor; 1037 | $req .= pack ( "N", 1 ); 1038 | $req .= pack ( "N", strlen($a["attrlat"]) ) . $a["attrlat"]; 1039 | $req .= pack ( "N", strlen($a["attrlong"]) ) . $a["attrlong"]; 1040 | $req .= $this->_PackFloat ( $a["lat"] ) . $this->_PackFloat ( $a["long"] ); 1041 | } 1042 | 1043 | // per-index weights 1044 | $req .= pack ( "N", count($this->_indexweights) ); 1045 | foreach ( $this->_indexweights as $idx=>$weight ) 1046 | $req .= pack ( "N", strlen($idx) ) . $idx . pack ( "N", $weight ); 1047 | 1048 | // max query time 1049 | $req .= pack ( "N", $this->_maxquerytime ); 1050 | 1051 | // per-field weights 1052 | $req .= pack ( "N", count($this->_fieldweights) ); 1053 | foreach ( $this->_fieldweights as $field=>$weight ) 1054 | $req .= pack ( "N", strlen($field) ) . $field . pack ( "N", $weight ); 1055 | 1056 | // comment 1057 | $req .= pack ( "N", strlen($comment) ) . $comment; 1058 | 1059 | // attribute overrides 1060 | $req .= pack ( "N", count($this->_overrides) ); 1061 | foreach ( $this->_overrides as $key => $entry ) 1062 | { 1063 | $req .= pack ( "N", strlen($entry["attr"]) ) . $entry["attr"]; 1064 | $req .= pack ( "NN", $entry["type"], count($entry["values"]) ); 1065 | foreach ( $entry["values"] as $id=>$val ) 1066 | { 1067 | assert ( is_numeric($id) ); 1068 | assert ( is_numeric($val) ); 1069 | 1070 | $req .= sphPackU64 ( $id ); 1071 | switch ( $entry["type"] ) 1072 | { 1073 | case SPH_ATTR_FLOAT: $req .= $this->_PackFloat ( $val ); break; 1074 | case SPH_ATTR_BIGINT: $req .= sphPackI64 ( $val ); break; 1075 | default: $req .= pack ( "N", $val ); break; 1076 | } 1077 | } 1078 | } 1079 | 1080 | // select-list 1081 | $req .= pack ( "N", strlen($this->_select) ) . $this->_select; 1082 | 1083 | // mbstring workaround 1084 | $this->_MBPop (); 1085 | 1086 | // store request to requests array 1087 | $this->_reqs[] = $req; 1088 | return count($this->_reqs)-1; 1089 | } 1090 | 1091 | /// connect to searchd, run queries batch, and return an array of result sets 1092 | function RunQueries () 1093 | { 1094 | if ( empty($this->_reqs) ) 1095 | { 1096 | $this->_error = "no queries defined, issue AddQuery() first"; 1097 | return false; 1098 | } 1099 | 1100 | // mbstring workaround 1101 | $this->_MBPush (); 1102 | 1103 | if (!( $fp = $this->_Connect() )) 1104 | { 1105 | $this->_MBPop (); 1106 | return false; 1107 | } 1108 | 1109 | // send query, get response 1110 | $nreqs = count($this->_reqs); 1111 | $req = join ( "", $this->_reqs ); 1112 | $len = 4+strlen($req); 1113 | $req = pack ( "nnNN", SEARCHD_COMMAND_SEARCH, VER_COMMAND_SEARCH, $len, $nreqs ) . $req; // add header 1114 | 1115 | if ( !( $this->_Send ( $fp, $req, $len+8 ) ) || 1116 | !( $response = $this->_GetResponse ( $fp, VER_COMMAND_SEARCH ) ) ) 1117 | { 1118 | $this->_MBPop (); 1119 | return false; 1120 | } 1121 | 1122 | // query sent ok; we can reset reqs now 1123 | $this->_reqs = array (); 1124 | 1125 | // parse and return response 1126 | return $this->_ParseSearchResponse ( $response, $nreqs ); 1127 | } 1128 | 1129 | /// parse and return search query (or queries) response 1130 | function _ParseSearchResponse ( $response, $nreqs ) 1131 | { 1132 | $p = 0; // current position 1133 | $max = strlen($response); // max position for checks, to protect against broken responses 1134 | 1135 | $results = array (); 1136 | for ( $ires=0; $ires<$nreqs && $p<$max; $ires++ ) 1137 | { 1138 | $results[] = array(); 1139 | $result =& $results[$ires]; 1140 | 1141 | $result["error"] = ""; 1142 | $result["warning"] = ""; 1143 | 1144 | // extract status 1145 | list(,$status) = unpack ( "N*", substr ( $response, $p, 4 ) ); $p += 4; 1146 | $result["status"] = $status; 1147 | if ( $status!=SEARCHD_OK ) 1148 | { 1149 | list(,$len) = unpack ( "N*", substr ( $response, $p, 4 ) ); $p += 4; 1150 | $message = substr ( $response, $p, $len ); $p += $len; 1151 | 1152 | if ( $status==SEARCHD_WARNING ) 1153 | { 1154 | $result["warning"] = $message; 1155 | } else 1156 | { 1157 | $result["error"] = $message; 1158 | continue; 1159 | } 1160 | } 1161 | 1162 | // read schema 1163 | $fields = array (); 1164 | $attrs = array (); 1165 | 1166 | list(,$nfields) = unpack ( "N*", substr ( $response, $p, 4 ) ); $p += 4; 1167 | while ( $nfields-->0 && $p<$max ) 1168 | { 1169 | list(,$len) = unpack ( "N*", substr ( $response, $p, 4 ) ); $p += 4; 1170 | $fields[] = substr ( $response, $p, $len ); $p += $len; 1171 | } 1172 | $result["fields"] = $fields; 1173 | 1174 | list(,$nattrs) = unpack ( "N*", substr ( $response, $p, 4 ) ); $p += 4; 1175 | while ( $nattrs-->0 && $p<$max ) 1176 | { 1177 | list(,$len) = unpack ( "N*", substr ( $response, $p, 4 ) ); $p += 4; 1178 | $attr = substr ( $response, $p, $len ); $p += $len; 1179 | list(,$type) = unpack ( "N*", substr ( $response, $p, 4 ) ); $p += 4; 1180 | $attrs[$attr] = $type; 1181 | } 1182 | $result["attrs"] = $attrs; 1183 | 1184 | // read match count 1185 | list(,$count) = unpack ( "N*", substr ( $response, $p, 4 ) ); $p += 4; 1186 | list(,$id64) = unpack ( "N*", substr ( $response, $p, 4 ) ); $p += 4; 1187 | 1188 | // read matches 1189 | $idx = -1; 1190 | while ( $count-->0 && $p<$max ) 1191 | { 1192 | // index into result array 1193 | $idx++; 1194 | 1195 | // parse document id and weight 1196 | if ( $id64 ) 1197 | { 1198 | $doc = sphUnpackU64 ( substr ( $response, $p, 8 ) ); $p += 8; 1199 | list(,$weight) = unpack ( "N*", substr ( $response, $p, 4 ) ); $p += 4; 1200 | } 1201 | else 1202 | { 1203 | list ( $doc, $weight ) = array_values ( unpack ( "N*N*", 1204 | substr ( $response, $p, 8 ) ) ); 1205 | $p += 8; 1206 | $doc = sphFixUint($doc); 1207 | } 1208 | $weight = sprintf ( "%u", $weight ); 1209 | 1210 | // create match entry 1211 | if ( $this->_arrayresult ) 1212 | $result["matches"][$idx] = array ( "id"=>$doc, "weight"=>$weight ); 1213 | else 1214 | $result["matches"][$doc]["weight"] = $weight; 1215 | 1216 | // parse and create attributes 1217 | $attrvals = array (); 1218 | foreach ( $attrs as $attr=>$type ) 1219 | { 1220 | // handle 64bit ints 1221 | if ( $type==SPH_ATTR_BIGINT ) 1222 | { 1223 | $attrvals[$attr] = sphUnpackI64 ( substr ( $response, $p, 8 ) ); $p += 8; 1224 | continue; 1225 | } 1226 | 1227 | // handle floats 1228 | if ( $type==SPH_ATTR_FLOAT ) 1229 | { 1230 | list(,$uval) = unpack ( "N*", substr ( $response, $p, 4 ) ); $p += 4; 1231 | list(,$fval) = unpack ( "f*", pack ( "L", $uval ) ); 1232 | $attrvals[$attr] = $fval; 1233 | continue; 1234 | } 1235 | 1236 | // handle everything else as unsigned ints 1237 | list(,$val) = unpack ( "N*", substr ( $response, $p, 4 ) ); $p += 4; 1238 | if ( $type & SPH_ATTR_MULTI ) 1239 | { 1240 | $attrvals[$attr] = array (); 1241 | $nvalues = $val; 1242 | while ( $nvalues-->0 && $p<$max ) 1243 | { 1244 | list(,$val) = unpack ( "N*", substr ( $response, $p, 4 ) ); $p += 4; 1245 | $attrvals[$attr][] = sphFixUint($val); 1246 | } 1247 | } else if ( $type==SPH_ATTR_STRING ) 1248 | { 1249 | $attrvals[$attr] = substr ( $response, $p, $val ); 1250 | $p += $val; 1251 | } else 1252 | { 1253 | $attrvals[$attr] = sphFixUint($val); 1254 | } 1255 | } 1256 | 1257 | if ( $this->_arrayresult ) 1258 | $result["matches"][$idx]["attrs"] = $attrvals; 1259 | else 1260 | $result["matches"][$doc]["attrs"] = $attrvals; 1261 | } 1262 | 1263 | list ( $total, $total_found, $msecs, $words ) = 1264 | array_values ( unpack ( "N*N*N*N*", substr ( $response, $p, 16 ) ) ); 1265 | $result["total"] = sprintf ( "%u", $total ); 1266 | $result["total_found"] = sprintf ( "%u", $total_found ); 1267 | $result["time"] = sprintf ( "%.3f", $msecs/1000 ); 1268 | $p += 16; 1269 | 1270 | while ( $words-->0 && $p<$max ) 1271 | { 1272 | list(,$len) = unpack ( "N*", substr ( $response, $p, 4 ) ); $p += 4; 1273 | $word = substr ( $response, $p, $len ); $p += $len; 1274 | list ( $docs, $hits ) = array_values ( unpack ( "N*N*", substr ( $response, $p, 8 ) ) ); $p += 8; 1275 | $result["words"][$word] = array ( 1276 | "docs"=>sprintf ( "%u", $docs ), 1277 | "hits"=>sprintf ( "%u", $hits ) ); 1278 | } 1279 | } 1280 | 1281 | $this->_MBPop (); 1282 | return $results; 1283 | } 1284 | 1285 | ///////////////////////////////////////////////////////////////////////////// 1286 | // excerpts generation 1287 | ///////////////////////////////////////////////////////////////////////////// 1288 | 1289 | /// connect to searchd server, and generate exceprts (snippets) 1290 | /// of given documents for given query. returns false on failure, 1291 | /// an array of snippets on success 1292 | function BuildExcerpts ( $docs, $index, $words, $opts=array() ) 1293 | { 1294 | assert ( is_array($docs) ); 1295 | assert ( is_string($index) ); 1296 | assert ( is_string($words) ); 1297 | assert ( is_array($opts) ); 1298 | 1299 | $this->_MBPush (); 1300 | 1301 | if (!( $fp = $this->_Connect() )) 1302 | { 1303 | $this->_MBPop(); 1304 | return false; 1305 | } 1306 | 1307 | ///////////////// 1308 | // fixup options 1309 | ///////////////// 1310 | 1311 | if ( !isset($opts["before_match"]) ) $opts["before_match"] = ""; 1312 | if ( !isset($opts["after_match"]) ) $opts["after_match"] = ""; 1313 | if ( !isset($opts["chunk_separator"]) ) $opts["chunk_separator"] = " ... "; 1314 | if ( !isset($opts["limit"]) ) $opts["limit"] = 256; 1315 | if ( !isset($opts["limit_passages"]) ) $opts["limit_passages"] = 0; 1316 | if ( !isset($opts["limit_words"]) ) $opts["limit_words"] = 0; 1317 | if ( !isset($opts["around"]) ) $opts["around"] = 5; 1318 | if ( !isset($opts["exact_phrase"]) ) $opts["exact_phrase"] = false; 1319 | if ( !isset($opts["single_passage"]) ) $opts["single_passage"] = false; 1320 | if ( !isset($opts["use_boundaries"]) ) $opts["use_boundaries"] = false; 1321 | if ( !isset($opts["weight_order"]) ) $opts["weight_order"] = false; 1322 | if ( !isset($opts["query_mode"]) ) $opts["query_mode"] = false; 1323 | if ( !isset($opts["force_all_words"]) ) $opts["force_all_words"] = false; 1324 | if ( !isset($opts["start_passage_id"]) ) $opts["start_passage_id"] = 1; 1325 | if ( !isset($opts["load_files"]) ) $opts["load_files"] = false; 1326 | if ( !isset($opts["html_strip_mode"]) ) $opts["html_strip_mode"] = "index"; 1327 | if ( !isset($opts["allow_empty"]) ) $opts["allow_empty"] = false; 1328 | 1329 | ///////////////// 1330 | // build request 1331 | ///////////////// 1332 | 1333 | // v.1.2 req 1334 | $flags = 1; // remove spaces 1335 | if ( $opts["exact_phrase"] ) $flags |= 2; 1336 | if ( $opts["single_passage"] ) $flags |= 4; 1337 | if ( $opts["use_boundaries"] ) $flags |= 8; 1338 | if ( $opts["weight_order"] ) $flags |= 16; 1339 | if ( $opts["query_mode"] ) $flags |= 32; 1340 | if ( $opts["force_all_words"] ) $flags |= 64; 1341 | if ( $opts["load_files"] ) $flags |= 128; 1342 | if ( $opts["allow_empty"] ) $flags |= 256; 1343 | $req = pack ( "NN", 0, $flags ); // mode=0, flags=$flags 1344 | $req .= pack ( "N", strlen($index) ) . $index; // req index 1345 | $req .= pack ( "N", strlen($words) ) . $words; // req words 1346 | 1347 | // options 1348 | $req .= pack ( "N", strlen($opts["before_match"]) ) . $opts["before_match"]; 1349 | $req .= pack ( "N", strlen($opts["after_match"]) ) . $opts["after_match"]; 1350 | $req .= pack ( "N", strlen($opts["chunk_separator"]) ) . $opts["chunk_separator"]; 1351 | $req .= pack ( "NN", (int)$opts["limit"], (int)$opts["around"] ); 1352 | $req .= pack ( "NNN", (int)$opts["limit_passages"], (int)$opts["limit_words"], (int)$opts["start_passage_id"] ); // v.1.2 1353 | $req .= pack ( "N", strlen($opts["html_strip_mode"]) ) . $opts["html_strip_mode"]; 1354 | 1355 | // documents 1356 | $req .= pack ( "N", count($docs) ); 1357 | foreach ( $docs as $doc ) 1358 | { 1359 | assert ( is_string($doc) ); 1360 | $req .= pack ( "N", strlen($doc) ) . $doc; 1361 | } 1362 | 1363 | //////////////////////////// 1364 | // send query, get response 1365 | //////////////////////////// 1366 | 1367 | $len = strlen($req); 1368 | $req = pack ( "nnN", SEARCHD_COMMAND_EXCERPT, VER_COMMAND_EXCERPT, $len ) . $req; // add header 1369 | if ( !( $this->_Send ( $fp, $req, $len+8 ) ) || 1370 | !( $response = $this->_GetResponse ( $fp, VER_COMMAND_EXCERPT ) ) ) 1371 | { 1372 | $this->_MBPop (); 1373 | return false; 1374 | } 1375 | 1376 | ////////////////// 1377 | // parse response 1378 | ////////////////// 1379 | 1380 | $pos = 0; 1381 | $res = array (); 1382 | $rlen = strlen($response); 1383 | for ( $i=0; $i $rlen ) 1389 | { 1390 | $this->_error = "incomplete reply"; 1391 | $this->_MBPop (); 1392 | return false; 1393 | } 1394 | $res[] = $len ? substr ( $response, $pos, $len ) : ""; 1395 | $pos += $len; 1396 | } 1397 | 1398 | $this->_MBPop (); 1399 | return $res; 1400 | } 1401 | 1402 | 1403 | ///////////////////////////////////////////////////////////////////////////// 1404 | // keyword generation 1405 | ///////////////////////////////////////////////////////////////////////////// 1406 | 1407 | /// connect to searchd server, and generate keyword list for a given query 1408 | /// returns false on failure, 1409 | /// an array of words on success 1410 | function BuildKeywords ( $query, $index, $hits ) 1411 | { 1412 | assert ( is_string($query) ); 1413 | assert ( is_string($index) ); 1414 | assert ( is_bool($hits) ); 1415 | 1416 | $this->_MBPush (); 1417 | 1418 | if (!( $fp = $this->_Connect() )) 1419 | { 1420 | $this->_MBPop(); 1421 | return false; 1422 | } 1423 | 1424 | ///////////////// 1425 | // build request 1426 | ///////////////// 1427 | 1428 | // v.1.0 req 1429 | $req = pack ( "N", strlen($query) ) . $query; // req query 1430 | $req .= pack ( "N", strlen($index) ) . $index; // req index 1431 | $req .= pack ( "N", (int)$hits ); 1432 | 1433 | //////////////////////////// 1434 | // send query, get response 1435 | //////////////////////////// 1436 | 1437 | $len = strlen($req); 1438 | $req = pack ( "nnN", SEARCHD_COMMAND_KEYWORDS, VER_COMMAND_KEYWORDS, $len ) . $req; // add header 1439 | if ( !( $this->_Send ( $fp, $req, $len+8 ) ) || 1440 | !( $response = $this->_GetResponse ( $fp, VER_COMMAND_KEYWORDS ) ) ) 1441 | { 1442 | $this->_MBPop (); 1443 | return false; 1444 | } 1445 | 1446 | ////////////////// 1447 | // parse response 1448 | ////////////////// 1449 | 1450 | $pos = 0; 1451 | $res = array (); 1452 | $rlen = strlen($response); 1453 | list(,$nwords) = unpack ( "N*", substr ( $response, $pos, 4 ) ); 1454 | $pos += 4; 1455 | for ( $i=0; $i<$nwords; $i++ ) 1456 | { 1457 | list(,$len) = unpack ( "N*", substr ( $response, $pos, 4 ) ); $pos += 4; 1458 | $tokenized = $len ? substr ( $response, $pos, $len ) : ""; 1459 | $pos += $len; 1460 | 1461 | list(,$len) = unpack ( "N*", substr ( $response, $pos, 4 ) ); $pos += 4; 1462 | $normalized = $len ? substr ( $response, $pos, $len ) : ""; 1463 | $pos += $len; 1464 | 1465 | $res[] = array ( "tokenized"=>$tokenized, "normalized"=>$normalized ); 1466 | 1467 | if ( $hits ) 1468 | { 1469 | list($ndocs,$nhits) = array_values ( unpack ( "N*N*", substr ( $response, $pos, 8 ) ) ); 1470 | $pos += 8; 1471 | $res [$i]["docs"] = $ndocs; 1472 | $res [$i]["hits"] = $nhits; 1473 | } 1474 | 1475 | if ( $pos > $rlen ) 1476 | { 1477 | $this->_error = "incomplete reply"; 1478 | $this->_MBPop (); 1479 | return false; 1480 | } 1481 | } 1482 | 1483 | $this->_MBPop (); 1484 | return $res; 1485 | } 1486 | 1487 | function EscapeString ( $string ) 1488 | { 1489 | $from = array ( '\\', '(',')','|','-','!','@','~','"','&', '/', '^', '$', '=' ); 1490 | $to = array ( '\\\\', '\(','\)','\|','\-','\!','\@','\~','\"', '\&', '\/', '\^', '\$', '\=' ); 1491 | 1492 | return str_replace ( $from, $to, $string ); 1493 | } 1494 | 1495 | ///////////////////////////////////////////////////////////////////////////// 1496 | // attribute updates 1497 | ///////////////////////////////////////////////////////////////////////////// 1498 | 1499 | /// batch update given attributes in given rows in given indexes 1500 | /// returns amount of updated documents (0 or more) on success, or -1 on failure 1501 | function UpdateAttributes ( $index, $attrs, $values, $mva=false ) 1502 | { 1503 | // verify everything 1504 | assert ( is_string($index) ); 1505 | assert ( is_bool($mva) ); 1506 | 1507 | assert ( is_array($attrs) ); 1508 | foreach ( $attrs as $attr ) 1509 | assert ( is_string($attr) ); 1510 | 1511 | assert ( is_array($values) ); 1512 | foreach ( $values as $id=>$entry ) 1513 | { 1514 | assert ( is_numeric($id) ); 1515 | assert ( is_array($entry) ); 1516 | assert ( count($entry)==count($attrs) ); 1517 | foreach ( $entry as $v ) 1518 | { 1519 | if ( $mva ) 1520 | { 1521 | assert ( is_array($v) ); 1522 | foreach ( $v as $vv ) 1523 | assert ( is_int($vv) ); 1524 | } else 1525 | assert ( is_int($v) ); 1526 | } 1527 | } 1528 | 1529 | // build request 1530 | $this->_MBPush (); 1531 | $req = pack ( "N", strlen($index) ) . $index; 1532 | 1533 | $req .= pack ( "N", count($attrs) ); 1534 | foreach ( $attrs as $attr ) 1535 | { 1536 | $req .= pack ( "N", strlen($attr) ) . $attr; 1537 | $req .= pack ( "N", $mva ? 1 : 0 ); 1538 | } 1539 | 1540 | $req .= pack ( "N", count($values) ); 1541 | foreach ( $values as $id=>$entry ) 1542 | { 1543 | $req .= sphPackU64 ( $id ); 1544 | foreach ( $entry as $v ) 1545 | { 1546 | $req .= pack ( "N", $mva ? count($v) : $v ); 1547 | if ( $mva ) 1548 | foreach ( $v as $vv ) 1549 | $req .= pack ( "N", $vv ); 1550 | } 1551 | } 1552 | 1553 | // connect, send query, get response 1554 | if (!( $fp = $this->_Connect() )) 1555 | { 1556 | $this->_MBPop (); 1557 | return -1; 1558 | } 1559 | 1560 | $len = strlen($req); 1561 | $req = pack ( "nnN", SEARCHD_COMMAND_UPDATE, VER_COMMAND_UPDATE, $len ) . $req; // add header 1562 | if ( !$this->_Send ( $fp, $req, $len+8 ) ) 1563 | { 1564 | $this->_MBPop (); 1565 | return -1; 1566 | } 1567 | 1568 | if (!( $response = $this->_GetResponse ( $fp, VER_COMMAND_UPDATE ) )) 1569 | { 1570 | $this->_MBPop (); 1571 | return -1; 1572 | } 1573 | 1574 | // parse response 1575 | list(,$updated) = unpack ( "N*", substr ( $response, 0, 4 ) ); 1576 | $this->_MBPop (); 1577 | return $updated; 1578 | } 1579 | 1580 | ///////////////////////////////////////////////////////////////////////////// 1581 | // persistent connections 1582 | ///////////////////////////////////////////////////////////////////////////// 1583 | 1584 | function Open() 1585 | { 1586 | if ( $this->_socket !== false ) 1587 | { 1588 | $this->_error = 'already connected'; 1589 | return false; 1590 | } 1591 | if ( !$fp = $this->_Connect() ) 1592 | return false; 1593 | 1594 | // command, command version = 0, body length = 4, body = 1 1595 | $req = pack ( "nnNN", SEARCHD_COMMAND_PERSIST, 0, 4, 1 ); 1596 | if ( !$this->_Send ( $fp, $req, 12 ) ) 1597 | return false; 1598 | 1599 | $this->_socket = $fp; 1600 | return true; 1601 | } 1602 | 1603 | function Close() 1604 | { 1605 | if ( $this->_socket === false ) 1606 | { 1607 | $this->_error = 'not connected'; 1608 | return false; 1609 | } 1610 | 1611 | fclose ( $this->_socket ); 1612 | $this->_socket = false; 1613 | 1614 | return true; 1615 | } 1616 | 1617 | ////////////////////////////////////////////////////////////////////////// 1618 | // status 1619 | ////////////////////////////////////////////////////////////////////////// 1620 | 1621 | function Status () 1622 | { 1623 | $this->_MBPush (); 1624 | if (!( $fp = $this->_Connect() )) 1625 | { 1626 | $this->_MBPop(); 1627 | return false; 1628 | } 1629 | 1630 | $req = pack ( "nnNN", SEARCHD_COMMAND_STATUS, VER_COMMAND_STATUS, 4, 1 ); // len=4, body=1 1631 | if ( !( $this->_Send ( $fp, $req, 12 ) ) || 1632 | !( $response = $this->_GetResponse ( $fp, VER_COMMAND_STATUS ) ) ) 1633 | { 1634 | $this->_MBPop (); 1635 | return false; 1636 | } 1637 | 1638 | $res = substr ( $response, 4 ); // just ignore length, error handling, etc 1639 | $p = 0; 1640 | list ( $rows, $cols ) = array_values ( unpack ( "N*N*", substr ( $response, $p, 8 ) ) ); $p += 8; 1641 | 1642 | $res = array(); 1643 | for ( $i=0; $i<$rows; $i++ ) 1644 | for ( $j=0; $j<$cols; $j++ ) 1645 | { 1646 | list(,$len) = unpack ( "N*", substr ( $response, $p, 4 ) ); $p += 4; 1647 | $res[$i][] = substr ( $response, $p, $len ); $p += $len; 1648 | } 1649 | 1650 | $this->_MBPop (); 1651 | return $res; 1652 | } 1653 | 1654 | ////////////////////////////////////////////////////////////////////////// 1655 | // flush 1656 | ////////////////////////////////////////////////////////////////////////// 1657 | 1658 | function FlushAttributes () 1659 | { 1660 | $this->_MBPush (); 1661 | if (!( $fp = $this->_Connect() )) 1662 | { 1663 | $this->_MBPop(); 1664 | return -1; 1665 | } 1666 | 1667 | $req = pack ( "nnN", SEARCHD_COMMAND_FLUSHATTRS, VER_COMMAND_FLUSHATTRS, 0 ); // len=0 1668 | if ( !( $this->_Send ( $fp, $req, 8 ) ) || 1669 | !( $response = $this->_GetResponse ( $fp, VER_COMMAND_FLUSHATTRS ) ) ) 1670 | { 1671 | $this->_MBPop (); 1672 | return -1; 1673 | } 1674 | 1675 | $tag = -1; 1676 | if ( strlen($response)==4 ) 1677 | list(,$tag) = unpack ( "N*", $response ); 1678 | else 1679 | $this->_error = "unexpected response length"; 1680 | 1681 | $this->_MBPop (); 1682 | return $tag; 1683 | } 1684 | } 1685 | 1686 | // 1687 | // $Id: sphinxapi.php 2376 2010-06-29 14:08:19Z shodan $ 1688 | // 1689 | -------------------------------------------------------------------------------- /sphinx.c: -------------------------------------------------------------------------------- 1 | /* 2 | +----------------------------------------------------------------------+ 3 | | PHP Version 5 | 4 | +----------------------------------------------------------------------+ 5 | | Copyright (c) 1997-2008 The PHP Group | 6 | +----------------------------------------------------------------------+ 7 | | This source file is subject to version 3.01 of the PHP license, | 8 | | that is bundled with this package in the file LICENSE, and is | 9 | | available through the world-wide-web at the following url: | 10 | | http://www.php.net/license/3_01.txt | 11 | | If you did not receive a copy of the PHP license and are unable to | 12 | | obtain it through the world-wide-web, please send a note to | 13 | | license@php.net so we can mail you a copy immediately. | 14 | +----------------------------------------------------------------------+ 15 | | Author: Antony Dovgal | 16 | | Based on Sphinx PHP API by Andrew Aksyonoff | 17 | +----------------------------------------------------------------------+ 18 | */ 19 | 20 | /* $Id$ */ 21 | 22 | #ifdef HAVE_CONFIG_H 23 | #include "config.h" 24 | #endif 25 | 26 | #include "php.h" 27 | #include "php_ini.h" 28 | #include "ext/standard/info.h" 29 | #include "ext/standard/file.h" 30 | #include "zend_operators.h" 31 | #include "php_sphinx.h" 32 | 33 | #include 34 | 35 | static zend_class_entry *ce_sphinx_client; 36 | 37 | static zend_object_handlers php_sphinx_client_handlers; 38 | static zend_object_handlers cannot_be_cloned; 39 | 40 | typedef struct _php_sphinx_client { 41 | zend_object std; 42 | sphinx_client *sphinx; 43 | zend_bool array_result; 44 | } php_sphinx_client; 45 | 46 | #ifdef COMPILE_DL_SPHINX 47 | ZEND_GET_MODULE(sphinx) 48 | #endif 49 | 50 | #ifndef E_RECOVERABLE_ERROR 51 | #define E_RECOVERABLE_ERROR E_WARNING 52 | #endif 53 | 54 | #define SPHINX_CONST(name) REGISTER_LONG_CONSTANT(#name, name, CONST_CS | CONST_PERSISTENT) 55 | 56 | #define SPHINX_INITIALIZED(c) \ 57 | if (!(c) || !(c)->sphinx) { \ 58 | php_error_docref(NULL TSRMLS_CC, E_WARNING, "using uninitialized SphinxClient object"); \ 59 | RETURN_FALSE; \ 60 | } 61 | 62 | static void php_sphinx_client_obj_dtor(void *object TSRMLS_DC) /* {{{ */ 63 | { 64 | php_sphinx_client *c = (php_sphinx_client *)object; 65 | 66 | sphinx_destroy(c->sphinx); 67 | zend_object_std_dtor(&c->std TSRMLS_CC); 68 | efree(c); 69 | } 70 | /* }}} */ 71 | 72 | static zend_object_value php_sphinx_client_new(zend_class_entry *ce TSRMLS_DC) /* {{{ */ 73 | { 74 | php_sphinx_client *c; 75 | zend_object_value retval; 76 | #if PHP_VERSION_ID < 50399 77 | zval *tmp; 78 | #endif 79 | 80 | c = ecalloc(1, sizeof(*c)); 81 | zend_object_std_init(&c->std, ce TSRMLS_CC); 82 | 83 | #if PHP_VERSION_ID < 50399 84 | ALLOC_HASHTABLE(c->std.properties); 85 | zend_hash_init(c->std.properties, 0, NULL, ZVAL_PTR_DTOR, 0); 86 | zend_hash_copy(c->std.properties, &ce->default_properties, (copy_ctor_func_t) zval_add_ref, (void *) &tmp, sizeof(zval *)); 87 | #else 88 | object_properties_init(&c->std, ce); 89 | #endif 90 | retval.handle = zend_objects_store_put(c, (zend_objects_store_dtor_t)zend_objects_destroy_object, php_sphinx_client_obj_dtor, NULL TSRMLS_CC); 91 | retval.handlers = &php_sphinx_client_handlers; 92 | return retval; 93 | } 94 | /* }}} */ 95 | 96 | #if PHP_MAJOR_VERSION >= 5 && PHP_MINOR_VERSION >= 4 97 | static zval *php_sphinx_client_read_property(zval *object, zval *member, int type, const zend_literal *key TSRMLS_DC) 98 | #else 99 | static zval *php_sphinx_client_read_property(zval *object, zval *member, int type TSRMLS_DC) /* {{{ */ 100 | #endif 101 | { 102 | zval tmp_member; 103 | zval *retval; 104 | zend_object_handlers *std_hnd; 105 | 106 | if (member->type != IS_STRING) { 107 | tmp_member = *member; 108 | zval_copy_ctor(&tmp_member); 109 | convert_to_string(&tmp_member); 110 | member = &tmp_member; 111 | } 112 | 113 | /* XXX we can either create retval ourselves (for custom properties) or use standard handlers */ 114 | 115 | std_hnd = zend_get_std_object_handlers(); 116 | #if PHP_MAJOR_VERSION >= 5 && PHP_MINOR_VERSION >= 4 117 | retval = std_hnd->read_property(object, member, type, key TSRMLS_CC); 118 | #else 119 | retval = std_hnd->read_property(object, member, type TSRMLS_CC); 120 | #endif 121 | 122 | if (member == &tmp_member) { 123 | zval_dtor(member); 124 | } 125 | return retval; 126 | } 127 | /* }}} */ 128 | 129 | static HashTable *php_sphinx_client_get_properties(zval *object TSRMLS_DC) /* {{{ */ 130 | { 131 | php_sphinx_client *c; 132 | const char *warning, *error; 133 | zval *tmp; 134 | HashTable *props; 135 | 136 | c = (php_sphinx_client *)zend_objects_get_address(object TSRMLS_CC); 137 | 138 | props = zend_std_get_properties(object TSRMLS_CC); 139 | 140 | error = sphinx_error(c->sphinx); 141 | MAKE_STD_ZVAL(tmp); 142 | ZVAL_STRING(tmp, (char *)error, 1); 143 | zend_hash_update(props, "error", sizeof("error"), (void *)&tmp, sizeof(zval *), NULL); 144 | 145 | warning = sphinx_warning(c->sphinx); 146 | MAKE_STD_ZVAL(tmp); 147 | ZVAL_STRING(tmp, (char *)warning, 1); 148 | zend_hash_update(props, "warning", sizeof("warning"), (void *)&tmp, sizeof(zval *), NULL); 149 | return c->std.properties; 150 | } 151 | /* }}} */ 152 | 153 | #ifdef TONY_200807015 154 | static inline void php_sphinx_error(php_sphinx_client *c TSRMLS_DC) /* {{{ */ 155 | { 156 | const char *err; 157 | 158 | err = sphinx_error(c->sphinx); 159 | if (!err || err[0] == '\0') { 160 | php_error_docref(NULL TSRMLS_CC, E_WARNING, "unknown error"); 161 | } else { 162 | php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", err); 163 | } 164 | } 165 | /* }}} */ 166 | #endif 167 | 168 | static void php_sphinx_result_to_array(php_sphinx_client *c, sphinx_result *result, zval **array TSRMLS_DC) /* {{{ */ 169 | { 170 | zval *tmp, *tmp_element, *sub_element, *sub_sub_element; 171 | int i, j; 172 | 173 | array_init(*array); 174 | 175 | /* error */ 176 | if (!result->error) { 177 | add_assoc_string_ex(*array, "error", sizeof("error"), "", 1); 178 | } else { 179 | add_assoc_string_ex(*array, "error", sizeof("error"), (char *)(result->error), 1); 180 | } 181 | 182 | /* warning */ 183 | if (!result->warning) { 184 | add_assoc_string_ex(*array, "warning", sizeof("warning"), "", 1); 185 | } else { 186 | add_assoc_string_ex(*array, "warning", sizeof("warning"), (char *)result->warning, 1); 187 | } 188 | 189 | /* status */ 190 | add_assoc_long_ex(*array, "status", sizeof("status"), result->status); 191 | 192 | switch(result->status) { 193 | case SEARCHD_OK: 194 | /* ok, continue reading data */ 195 | break; 196 | case SEARCHD_WARNING: 197 | /* this seems to be safe, too */ 198 | break; 199 | default: 200 | /* libsphinxclient doesn't nullify the data 201 | in case of error, so it's not safe to continue. */ 202 | return; 203 | } 204 | 205 | /* fields */ 206 | MAKE_STD_ZVAL(tmp); 207 | array_init(tmp); 208 | 209 | for (i = 0; i < result->num_fields; i++) { 210 | add_next_index_string(tmp, result->fields[i], 1); 211 | } 212 | add_assoc_zval_ex(*array, "fields", sizeof("fields"), tmp); 213 | 214 | /* attrs */ 215 | MAKE_STD_ZVAL(tmp); 216 | array_init(tmp); 217 | 218 | for (i = 0; i < result->num_attrs; i++) { 219 | #if SIZEOF_LONG == 8 220 | add_assoc_long_ex(tmp, result->attr_names[i], strlen(result->attr_names[i]) + 1, result->attr_types[i]); 221 | #else 222 | double float_value; 223 | char buf[128]; 224 | 225 | float_value = (double)result->attr_types[i]; 226 | slprintf(buf, sizeof(buf), "%.0f", float_value); 227 | add_assoc_string_ex(tmp, result->attr_names[i], strlen(result->attr_names[i]) + 1, buf, 1); 228 | #endif 229 | } 230 | add_assoc_zval_ex(*array, "attrs", sizeof("attrs"), tmp); 231 | 232 | /* matches */ 233 | if (result->num_matches) { 234 | MAKE_STD_ZVAL(tmp); 235 | array_init(tmp); 236 | 237 | for (i = 0; i < result->num_matches; i++) { 238 | MAKE_STD_ZVAL(tmp_element); 239 | array_init(tmp_element); 240 | 241 | if (c->array_result) { 242 | /* id */ 243 | #if SIZEOF_LONG == 8 244 | add_assoc_long_ex(tmp_element, "id", sizeof("id"), sphinx_get_id(result, i)); 245 | #else 246 | double float_id; 247 | char buf[128]; 248 | 249 | float_id = (double)sphinx_get_id(result, i); 250 | slprintf(buf, sizeof(buf), "%.0f", float_id); 251 | add_assoc_string_ex(tmp_element, "id", sizeof("id"), buf, 1); 252 | #endif 253 | } 254 | 255 | /* weight */ 256 | add_assoc_long_ex(tmp_element, "weight", sizeof("weight"), sphinx_get_weight(result, i)); 257 | 258 | /* attrs */ 259 | MAKE_STD_ZVAL(sub_element); 260 | array_init(sub_element); 261 | 262 | for (j = 0; j < result->num_attrs; j++) { 263 | #if SIZEOF_LONG != 8 264 | double float_value; 265 | char buf[128]; 266 | #endif 267 | 268 | MAKE_STD_ZVAL(sub_sub_element); 269 | 270 | switch(result->attr_types[j]) { 271 | case SPH_ATTR_MULTI | SPH_ATTR_INTEGER: 272 | { 273 | int k; 274 | unsigned int *mva = sphinx_get_mva(result, i, j); 275 | unsigned int tmp, num; 276 | 277 | array_init(sub_sub_element); 278 | 279 | if (!mva) { 280 | break; 281 | } 282 | 283 | memcpy(&num, mva, sizeof(unsigned int)); 284 | 285 | for (k = 1; k <= num; k++) { 286 | mva++; 287 | memcpy(&tmp, mva, sizeof(unsigned int)); 288 | #if SIZEOF_LONG == 8 289 | add_next_index_long(sub_sub_element, tmp); 290 | #else 291 | float_value = (double)tmp; 292 | slprintf(buf, sizeof(buf), "%.0f", float_value); 293 | add_next_index_string(sub_sub_element, buf, 1); 294 | #endif 295 | } 296 | } break; 297 | 298 | case SPH_ATTR_FLOAT: 299 | ZVAL_DOUBLE(sub_sub_element, sphinx_get_float(result, i, j)); 300 | break; 301 | #if LIBSPHINX_VERSION_ID >= 110 302 | case SPH_ATTR_STRING: 303 | ZVAL_STRING(sub_sub_element, sphinx_get_string(result, i, j), 1); 304 | break; 305 | #endif 306 | default: 307 | #if SIZEOF_LONG == 8 308 | ZVAL_LONG(sub_sub_element, sphinx_get_int(result, i, j)); 309 | #else 310 | float_value = (double)sphinx_get_int(result, i, j); 311 | slprintf(buf, sizeof(buf), "%.0f", float_value); 312 | ZVAL_STRING(sub_sub_element, buf, 1); 313 | #endif 314 | break; 315 | } 316 | 317 | add_assoc_zval(sub_element, result->attr_names[j], sub_sub_element); 318 | } 319 | 320 | add_assoc_zval_ex(tmp_element, "attrs", sizeof("attrs"), sub_element); 321 | 322 | if (c->array_result) { 323 | add_next_index_zval(tmp, tmp_element); 324 | } else { 325 | #if SIZEOF_LONG == 8 326 | add_index_zval(tmp, sphinx_get_id(result, i), tmp_element); 327 | #else 328 | char buf[128]; 329 | double float_id; 330 | int buf_len; 331 | 332 | float_id = (double)sphinx_get_id(result, i); 333 | buf_len = slprintf(buf, sizeof(buf), "%.0f", float_id); 334 | add_assoc_zval_ex(tmp, buf, buf_len + 1, tmp_element); 335 | #endif 336 | } 337 | } 338 | 339 | add_assoc_zval_ex(*array, "matches", sizeof("matches"), tmp); 340 | } 341 | 342 | /* total */ 343 | add_assoc_long_ex(*array, "total", sizeof("total"), result->total); 344 | 345 | /* total_found */ 346 | add_assoc_long_ex(*array, "total_found", sizeof("total_found"), result->total_found); 347 | 348 | /* time */ 349 | add_assoc_double_ex(*array, "time", sizeof("time"), (double)result->time_msec/1000.0); 350 | 351 | /* words */ 352 | if (result->num_words) { 353 | MAKE_STD_ZVAL(tmp); 354 | array_init(tmp); 355 | for (i = 0; i < result->num_words; i++) { 356 | MAKE_STD_ZVAL(sub_element); 357 | array_init(sub_element); 358 | 359 | add_assoc_long_ex(sub_element, "docs", sizeof("docs"), result->words[i].docs); 360 | add_assoc_long_ex(sub_element, "hits", sizeof("hits"), result->words[i].hits); 361 | add_assoc_zval_ex(tmp, (char *)result->words[i].word, strlen(result->words[i].word) + 1, sub_element); 362 | } 363 | add_assoc_zval_ex(*array, "words", sizeof("words"), tmp); 364 | } 365 | } 366 | /* }}} */ 367 | 368 | 369 | /* {{{ proto void SphinxClient::__construct() */ 370 | static PHP_METHOD(SphinxClient, __construct) 371 | { 372 | php_sphinx_client *c; 373 | 374 | c = (php_sphinx_client *)zend_object_store_get_object(getThis() TSRMLS_CC); 375 | 376 | if (c->sphinx) { 377 | /* called __construct() twice, bail out */ 378 | return; 379 | } 380 | 381 | c->sphinx = sphinx_create(1 /* copy string args */); 382 | 383 | sphinx_set_connect_timeout(c->sphinx, FG(default_socket_timeout)); 384 | } 385 | /* }}} */ 386 | 387 | /* {{{ proto bool SphinxClient::setServer(string server, int port) */ 388 | static PHP_METHOD(SphinxClient, setServer) 389 | { 390 | php_sphinx_client *c; 391 | long port; 392 | char *server; 393 | int server_len, res; 394 | 395 | if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sl", &server, &server_len, &port) == FAILURE) { 396 | return; 397 | } 398 | 399 | c = (php_sphinx_client *)zend_object_store_get_object(getThis() TSRMLS_CC); 400 | SPHINX_INITIALIZED(c) 401 | 402 | res = sphinx_set_server(c->sphinx, server, (int)port); 403 | if (!res) { 404 | RETURN_FALSE; 405 | } 406 | RETURN_TRUE; 407 | } 408 | /* }}} */ 409 | 410 | /* {{{ proto bool SphinxClient::setLimits(int offset, int limit[, int max_matches[, int cutoff]]) */ 411 | static PHP_METHOD(SphinxClient, setLimits) 412 | { 413 | php_sphinx_client *c; 414 | long offset, limit, max_matches = 1000, cutoff = 0; 415 | int res; 416 | 417 | if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ll|ll", &offset, &limit, &max_matches, &cutoff) == FAILURE) { 418 | return; 419 | } 420 | 421 | c = (php_sphinx_client *)zend_object_store_get_object(getThis() TSRMLS_CC); 422 | SPHINX_INITIALIZED(c) 423 | 424 | res = sphinx_set_limits(c->sphinx, (int)offset, (int)limit, (int)max_matches, (int)cutoff); 425 | if (!res) { 426 | RETURN_FALSE; 427 | } 428 | RETURN_TRUE; 429 | } 430 | /* }}} */ 431 | 432 | /* {{{ proto bool SphinxClient::setMatchMode(int mode) */ 433 | static PHP_METHOD(SphinxClient, setMatchMode) 434 | { 435 | php_sphinx_client *c; 436 | long mode; 437 | int res; 438 | 439 | if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &mode) == FAILURE) { 440 | return; 441 | } 442 | 443 | c = (php_sphinx_client *)zend_object_store_get_object(getThis() TSRMLS_CC); 444 | SPHINX_INITIALIZED(c) 445 | 446 | res = sphinx_set_match_mode(c->sphinx, mode); 447 | if (!res) { 448 | RETURN_FALSE; 449 | } 450 | RETURN_TRUE; 451 | } 452 | /* }}} */ 453 | 454 | /* {{{ proto bool SphinxClient::setIndexWeights(array weights) */ 455 | static PHP_METHOD(SphinxClient, setIndexWeights) 456 | { 457 | php_sphinx_client *c; 458 | zval *weights, **item; 459 | int num_weights, res = 0, i; 460 | int *index_weights; 461 | char **index_names; 462 | char *string_key; 463 | unsigned int string_key_len; 464 | unsigned long int num_key; 465 | 466 | if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &weights) == FAILURE) { 467 | return; 468 | } 469 | 470 | c = (php_sphinx_client *)zend_object_store_get_object(getThis() TSRMLS_CC); 471 | SPHINX_INITIALIZED(c) 472 | 473 | num_weights = zend_hash_num_elements(Z_ARRVAL_P(weights)); 474 | if (!num_weights) { 475 | /* check for empty array and return false right away */ 476 | RETURN_FALSE; 477 | } 478 | 479 | index_names = safe_emalloc(num_weights, sizeof(char *), 0); 480 | index_weights = safe_emalloc(num_weights, sizeof(int), 0); 481 | 482 | /* reset num_weights, we'll reuse it count _real_ number of entries */ 483 | num_weights = 0; 484 | 485 | for (zend_hash_internal_pointer_reset(Z_ARRVAL_P(weights)); 486 | zend_hash_get_current_data(Z_ARRVAL_P(weights), (void **)&item) != FAILURE; 487 | zend_hash_move_forward(Z_ARRVAL_P(weights))) { 488 | 489 | if (zend_hash_get_current_key_ex(Z_ARRVAL_P(weights), &string_key, &string_key_len, &num_key, 0, NULL) != HASH_KEY_IS_STRING) { 490 | /* if the key is not string.. well.. you're screwed */ 491 | break; 492 | } 493 | 494 | convert_to_long_ex(item); 495 | 496 | index_names[num_weights] = estrndup(string_key, string_key_len); 497 | index_weights[num_weights] = Z_LVAL_PP(item); 498 | 499 | num_weights++; 500 | } 501 | 502 | if (num_weights) { 503 | res = sphinx_set_index_weights(c->sphinx, num_weights, (const char **)index_names, index_weights); 504 | } 505 | 506 | for (i = 0; i != num_weights; i++) { 507 | efree(index_names[i]); 508 | } 509 | efree(index_names); 510 | efree(index_weights); 511 | 512 | if (!res) { 513 | RETURN_FALSE; 514 | } 515 | RETURN_TRUE; 516 | } 517 | /* }}} */ 518 | 519 | #if LIBSPHINX_VERSION_ID >= 99 520 | /* {{{ proto bool SphinxClient::setSelect(string clause) */ 521 | static PHP_METHOD(SphinxClient, setSelect) 522 | { 523 | php_sphinx_client *c; 524 | char *clause; 525 | int clause_len, res; 526 | 527 | if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &clause, &clause_len) == FAILURE) { 528 | return; 529 | } 530 | 531 | c = (php_sphinx_client *)zend_object_store_get_object(getThis() TSRMLS_CC); 532 | SPHINX_INITIALIZED(c) 533 | 534 | res = sphinx_set_select(c->sphinx, clause); 535 | if (!res) { 536 | RETURN_FALSE; 537 | } 538 | RETURN_TRUE; 539 | } 540 | /* }}} */ 541 | #endif 542 | 543 | /* {{{ proto bool SphinxClient::setIDRange(int min, int max) */ 544 | static PHP_METHOD(SphinxClient, setIDRange) 545 | { 546 | php_sphinx_client *c; 547 | long min, max; 548 | int res; 549 | 550 | if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ll", &min, &max) == FAILURE) { 551 | return; 552 | } 553 | 554 | c = (php_sphinx_client *)zend_object_store_get_object(getThis() TSRMLS_CC); 555 | SPHINX_INITIALIZED(c) 556 | 557 | res = sphinx_set_id_range(c->sphinx, (sphinx_uint64_t)min, (sphinx_uint64_t)max); 558 | if (!res) { 559 | RETURN_FALSE; 560 | } 561 | RETURN_TRUE; 562 | } 563 | /* }}} */ 564 | 565 | /* {{{ proto bool SphinxClient::setFilter(string attribute, array values[, bool exclude]) */ 566 | static PHP_METHOD(SphinxClient, setFilter) 567 | { 568 | php_sphinx_client *c; 569 | zval *values, **item; 570 | char *attribute; 571 | int attribute_len, num_values, i = 0, res; 572 | zend_bool exclude = 0; 573 | sphinx_int64_t *u_values; 574 | 575 | if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sa|b", &attribute, &attribute_len, &values, &exclude) == FAILURE) { 576 | return; 577 | } 578 | 579 | c = (php_sphinx_client *)zend_object_store_get_object(getThis() TSRMLS_CC); 580 | SPHINX_INITIALIZED(c) 581 | 582 | num_values = zend_hash_num_elements(Z_ARRVAL_P(values)); 583 | if (!num_values) { 584 | RETURN_FALSE; 585 | } 586 | 587 | u_values = safe_emalloc(num_values, sizeof(sphinx_int64_t), 0); 588 | 589 | for (zend_hash_internal_pointer_reset(Z_ARRVAL_P(values)); 590 | zend_hash_get_current_data(Z_ARRVAL_P(values), (void **) &item) != FAILURE; 591 | zend_hash_move_forward(Z_ARRVAL_P(values))) { 592 | 593 | convert_to_double_ex(item); 594 | u_values[i] = (sphinx_int64_t)Z_DVAL_PP(item); 595 | i++; 596 | } 597 | 598 | res = sphinx_add_filter(c->sphinx, attribute, num_values, u_values, exclude ? 1 : 0); 599 | efree(u_values); 600 | 601 | if (!res) { 602 | RETURN_FALSE; 603 | } 604 | RETURN_TRUE; 605 | } 606 | /* }}} */ 607 | 608 | #ifdef HAVE_SPHINX_ADD_FILTER_STRING 609 | /* {{{ proto bool SphinxClient::setFilterString(string attribute, string value[, bool exclude]) */ 610 | static PHP_METHOD(SphinxClient, setFilterString) 611 | { 612 | php_sphinx_client *c; 613 | char *attribute, *value; 614 | int attribute_len, value_len, res; 615 | zend_bool exclude = 0; 616 | 617 | if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|b", &attribute, &attribute_len, &value, &value_len, &exclude) == FAILURE) { 618 | return; 619 | } 620 | 621 | c = (php_sphinx_client *)zend_object_store_get_object(getThis() TSRMLS_CC); 622 | SPHINX_INITIALIZED(c) 623 | 624 | res = sphinx_add_filter_string(c->sphinx, attribute, value, exclude ? 1 : 0); 625 | if (!res) { 626 | RETURN_FALSE; 627 | } 628 | RETURN_TRUE; 629 | } 630 | /* }}} */ 631 | #endif 632 | 633 | /* {{{ proto bool SphinxClient::setFilterRange(string attribute, int min, int max[, bool exclude]) */ 634 | static PHP_METHOD(SphinxClient, setFilterRange) 635 | { 636 | php_sphinx_client *c; 637 | char *attribute; 638 | int attribute_len, res; 639 | long min, max; 640 | zend_bool exclude = 0; 641 | 642 | if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sll|b", &attribute, &attribute_len, &min, &max, &exclude ) == FAILURE) { 643 | return; 644 | } 645 | 646 | c = (php_sphinx_client *)zend_object_store_get_object(getThis() TSRMLS_CC); 647 | SPHINX_INITIALIZED(c) 648 | 649 | res = sphinx_add_filter_range(c->sphinx, attribute, min, max, exclude); 650 | 651 | if (!res) { 652 | RETURN_FALSE; 653 | } 654 | RETURN_TRUE; 655 | } 656 | /* }}} */ 657 | 658 | /* {{{ proto bool SphinxClient::setFilterFloatRange(string attribute, float min, float max[, bool exclude]) */ 659 | static PHP_METHOD(SphinxClient, setFilterFloatRange) 660 | { 661 | php_sphinx_client *c; 662 | char *attribute; 663 | int attribute_len, res; 664 | double min, max; 665 | zend_bool exclude = 0; 666 | 667 | if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sdd|b", &attribute, &attribute_len, &min, &max, &exclude) == FAILURE) { 668 | return; 669 | } 670 | 671 | c = (php_sphinx_client *)zend_object_store_get_object(getThis() TSRMLS_CC); 672 | SPHINX_INITIALIZED(c) 673 | 674 | res = sphinx_add_filter_float_range(c->sphinx, attribute, min, max, exclude); 675 | 676 | if (!res) { 677 | RETURN_FALSE; 678 | } 679 | RETURN_TRUE; 680 | } 681 | /* }}} */ 682 | 683 | /* {{{ proto bool SphinxClient::setGeoAnchor(string attrlat, string attrlong, float latitude, float longitude) */ 684 | static PHP_METHOD(SphinxClient, setGeoAnchor) 685 | { 686 | php_sphinx_client *c; 687 | char *attrlat, *attrlong; 688 | int attrlat_len, attrlong_len, res; 689 | double latitude, longitude; 690 | 691 | if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ssdd", &attrlat, &attrlat_len, &attrlong, &attrlong_len, &latitude, &longitude) == FAILURE) { 692 | return; 693 | } 694 | 695 | c = (php_sphinx_client *)zend_object_store_get_object(getThis() TSRMLS_CC); 696 | SPHINX_INITIALIZED(c) 697 | 698 | res = sphinx_set_geoanchor(c->sphinx, attrlat, attrlong, latitude, longitude); 699 | 700 | if (!res) { 701 | RETURN_FALSE; 702 | } 703 | RETURN_TRUE; 704 | } 705 | /* }}} */ 706 | 707 | /* {{{ proto bool SphinxClient::setGroupBy(string attribute, int func[, string groupsort]) */ 708 | static PHP_METHOD(SphinxClient, setGroupBy) 709 | { 710 | php_sphinx_client *c; 711 | char *attribute, *groupsort = NULL; 712 | int attribute_len, groupsort_len, res; 713 | long func; 714 | 715 | if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sl|s", &attribute, &attribute_len, &func, &groupsort, &groupsort_len) == FAILURE) { 716 | return; 717 | } 718 | 719 | if (groupsort == NULL) { 720 | groupsort = "@group desc"; 721 | } 722 | 723 | if (func < SPH_GROUPBY_DAY || func > SPH_GROUPBY_ATTRPAIR) { 724 | php_error_docref(NULL TSRMLS_CC, E_WARNING, "invalid group func specified (%ld)", func); 725 | RETURN_FALSE; 726 | } 727 | 728 | c = (php_sphinx_client *)zend_object_store_get_object(getThis() TSRMLS_CC); 729 | SPHINX_INITIALIZED(c) 730 | 731 | res = sphinx_set_groupby(c->sphinx, attribute, func, groupsort); 732 | 733 | if (!res) { 734 | RETURN_FALSE; 735 | } 736 | RETURN_TRUE; 737 | } 738 | /* }}} */ 739 | 740 | /* {{{ proto bool SphinxClient::setGroupDistinct(string attribute) */ 741 | static PHP_METHOD(SphinxClient, setGroupDistinct) 742 | { 743 | php_sphinx_client *c; 744 | char *attribute; 745 | int attribute_len, res; 746 | 747 | if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &attribute, &attribute_len) == FAILURE) { 748 | return; 749 | } 750 | 751 | c = (php_sphinx_client *)zend_object_store_get_object(getThis() TSRMLS_CC); 752 | SPHINX_INITIALIZED(c) 753 | 754 | res = sphinx_set_groupby_distinct(c->sphinx, attribute); 755 | 756 | if (!res) { 757 | RETURN_FALSE; 758 | } 759 | RETURN_TRUE; 760 | } 761 | /* }}} */ 762 | 763 | /* {{{ proto bool SphinxClient::setRetries(int count[, int delay]) */ 764 | static PHP_METHOD(SphinxClient, setRetries) 765 | { 766 | php_sphinx_client *c; 767 | long count, delay = 0; 768 | int res; 769 | 770 | if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l|l", &count, &delay) == FAILURE) { 771 | return; 772 | } 773 | 774 | c = (php_sphinx_client *)zend_object_store_get_object(getThis() TSRMLS_CC); 775 | SPHINX_INITIALIZED(c) 776 | 777 | res = sphinx_set_retries(c->sphinx, (int)count, (int)delay); 778 | 779 | if (!res) { 780 | RETURN_FALSE; 781 | } 782 | RETURN_TRUE; 783 | } 784 | /* }}} */ 785 | 786 | /* {{{ proto bool SphinxClient::setMaxQueryTime(int qtime) */ 787 | static PHP_METHOD(SphinxClient, setMaxQueryTime) 788 | { 789 | php_sphinx_client *c; 790 | long qtime; 791 | int res; 792 | 793 | if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &qtime) == FAILURE) { 794 | return; 795 | } 796 | 797 | c = (php_sphinx_client *)zend_object_store_get_object(getThis() TSRMLS_CC); 798 | SPHINX_INITIALIZED(c) 799 | 800 | res = sphinx_set_max_query_time(c->sphinx, (int)qtime); 801 | 802 | if (!res) { 803 | RETURN_FALSE; 804 | } 805 | RETURN_TRUE; 806 | } 807 | /* }}} */ 808 | 809 | #ifdef HAVE_3ARG_SPHINX_SET_RANKING_MODE 810 | /* {{{ proto bool SphinxClient::setRankingMode(int ranker[, string ranking_expression]) */ 811 | static PHP_METHOD(SphinxClient, setRankingMode) 812 | { 813 | php_sphinx_client *c; 814 | long ranker; 815 | int res, rank_expr_len; 816 | char *rank_expr = NULL; 817 | 818 | if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l|s", &ranker, &rank_expr, &rank_expr_len) == FAILURE) { 819 | return; 820 | } 821 | 822 | c = (php_sphinx_client *)zend_object_store_get_object(getThis() TSRMLS_CC); 823 | SPHINX_INITIALIZED(c) 824 | 825 | res = sphinx_set_ranking_mode(c->sphinx, (int)ranker, rank_expr); 826 | 827 | if (!res) { 828 | RETURN_FALSE; 829 | } 830 | RETURN_TRUE; 831 | } 832 | /* }}} */ 833 | #else 834 | /* {{{ proto bool SphinxClient::setRankingMode(int ranker) */ 835 | static PHP_METHOD(SphinxClient, setRankingMode) 836 | { 837 | php_sphinx_client *c; 838 | long ranker; 839 | int res; 840 | 841 | if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &ranker) == FAILURE) { 842 | return; 843 | } 844 | 845 | c = (php_sphinx_client *)zend_object_store_get_object(getThis() TSRMLS_CC); 846 | SPHINX_INITIALIZED(c) 847 | 848 | res = sphinx_set_ranking_mode(c->sphinx, (int)ranker); 849 | 850 | if (!res) { 851 | RETURN_FALSE; 852 | } 853 | RETURN_TRUE; 854 | } 855 | /* }}} */ 856 | #endif 857 | 858 | /* {{{ proto bool SphinxClient::setFieldWeights(array weights) */ 859 | static PHP_METHOD(SphinxClient, setFieldWeights) 860 | { 861 | php_sphinx_client *c; 862 | zval *weights, **item; 863 | int num_weights, res = 0, i; 864 | int *field_weights; 865 | char **field_names; 866 | char *string_key; 867 | unsigned int string_key_len; 868 | unsigned long int num_key; 869 | 870 | if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &weights) == FAILURE) { 871 | return; 872 | } 873 | 874 | c = (php_sphinx_client *)zend_object_store_get_object(getThis() TSRMLS_CC); 875 | SPHINX_INITIALIZED(c) 876 | 877 | num_weights = zend_hash_num_elements(Z_ARRVAL_P(weights)); 878 | if (!num_weights) { 879 | /* check for empty array and return false right away */ 880 | RETURN_FALSE; 881 | } 882 | 883 | field_names = safe_emalloc(num_weights, sizeof(char *), 0); 884 | field_weights = safe_emalloc(num_weights, sizeof(int), 0); 885 | 886 | /* reset num_weights, we'll reuse it count _real_ number of entries */ 887 | num_weights = 0; 888 | 889 | for (zend_hash_internal_pointer_reset(Z_ARRVAL_P(weights)); 890 | zend_hash_get_current_data(Z_ARRVAL_P(weights), (void **)&item) != FAILURE; 891 | zend_hash_move_forward(Z_ARRVAL_P(weights))) { 892 | 893 | if (zend_hash_get_current_key_ex(Z_ARRVAL_P(weights), &string_key, &string_key_len, &num_key, 0, NULL) != HASH_KEY_IS_STRING) { 894 | /* if the key is not string.. well.. you're screwed */ 895 | break; 896 | } 897 | 898 | convert_to_long_ex(item); 899 | 900 | field_names[num_weights] = estrndup(string_key, string_key_len); 901 | field_weights[num_weights] = Z_LVAL_PP(item); 902 | 903 | num_weights++; 904 | } 905 | 906 | if (num_weights) { 907 | res = sphinx_set_field_weights(c->sphinx, num_weights, (const char **) field_names, field_weights); 908 | } 909 | 910 | for (i = 0; i != num_weights; i++) { 911 | efree(field_names[i]); 912 | } 913 | efree(field_names); 914 | efree(field_weights); 915 | 916 | if (!res) { 917 | RETURN_FALSE; 918 | } 919 | RETURN_TRUE; 920 | } 921 | /* }}} */ 922 | 923 | /* {{{ proto bool SphinxClient::setSortMode(int mode[, string sortby]) */ 924 | static PHP_METHOD(SphinxClient, setSortMode) 925 | { 926 | php_sphinx_client *c; 927 | long mode; 928 | char *sortby = NULL; 929 | int sortby_len, res; 930 | 931 | if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l|s", &mode, &sortby, &sortby_len) == FAILURE) { 932 | return; 933 | } 934 | 935 | c = (php_sphinx_client *)zend_object_store_get_object(getThis() TSRMLS_CC); 936 | SPHINX_INITIALIZED(c) 937 | 938 | res = sphinx_set_sort_mode(c->sphinx, (int)mode, sortby); 939 | 940 | if (!res) { 941 | RETURN_FALSE; 942 | } 943 | RETURN_TRUE; 944 | } 945 | /* }}} */ 946 | 947 | /* {{{ proto bool SphinxClient::setConnectTimeout(float timeout) */ 948 | static PHP_METHOD(SphinxClient, setConnectTimeout) 949 | { 950 | php_sphinx_client *c; 951 | double timeout; 952 | int res; 953 | 954 | if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &timeout) == FAILURE) { 955 | return; 956 | } 957 | 958 | c = (php_sphinx_client *)zend_object_store_get_object(getThis() TSRMLS_CC); 959 | SPHINX_INITIALIZED(c) 960 | 961 | res = sphinx_set_connect_timeout(c->sphinx, timeout); 962 | 963 | if (!res) { 964 | RETURN_FALSE; 965 | } 966 | RETURN_TRUE; 967 | } 968 | /* }}} */ 969 | 970 | /* {{{ proto bool SphinxClient::setArrayResult(bool array_result) */ 971 | static PHP_METHOD(SphinxClient, setArrayResult) 972 | { 973 | php_sphinx_client *c; 974 | zend_bool array_result; 975 | 976 | if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "b", &array_result) == FAILURE) { 977 | return; 978 | } 979 | 980 | c = (php_sphinx_client *)zend_object_store_get_object(getThis() TSRMLS_CC); 981 | SPHINX_INITIALIZED(c) 982 | 983 | c->array_result = array_result; 984 | RETURN_TRUE; 985 | } 986 | /* }}} */ 987 | 988 | /* {{{ proto int SphinxClient::updateAttributes(string index, array attributes, array values[, bool mva]) */ 989 | static PHP_METHOD(SphinxClient, updateAttributes) 990 | { 991 | php_sphinx_client *c; 992 | zval *attributes, *values, **item; 993 | char *index; 994 | const char **attrs; 995 | int index_len, attrs_num, values_num; 996 | int res = 0; 997 | sphinx_uint64_t *docids = NULL; 998 | sphinx_int64_t *vals = NULL; 999 | unsigned int *vals_mva = NULL; 1000 | #if LIBSPHINX_VERSION_ID >= 110 1001 | int res_mva, values_mva_num, values_mva_size = 0; 1002 | zval **attr_value_mva; 1003 | #endif 1004 | int a = 0, i = 0, j = 0; 1005 | zend_bool mva = 0; 1006 | 1007 | if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "saa|b", &index, &index_len, &attributes, &values, &mva) == FAILURE) { 1008 | return; 1009 | } 1010 | 1011 | #if LIBSPHINX_VERSION_ID < 110 1012 | if (mva) { 1013 | php_error_docref(NULL TSRMLS_CC, E_WARNING, "update mva attributes is not supported"); 1014 | RETURN_FALSE; 1015 | } 1016 | #endif 1017 | 1018 | c = (php_sphinx_client *)zend_object_store_get_object(getThis() TSRMLS_CC); 1019 | SPHINX_INITIALIZED(c) 1020 | 1021 | attrs_num = zend_hash_num_elements(Z_ARRVAL_P(attributes)); 1022 | 1023 | if (!attrs_num) { 1024 | php_error_docref(NULL TSRMLS_CC, E_WARNING, "empty attributes array passed"); 1025 | RETURN_FALSE; 1026 | } 1027 | 1028 | values_num = zend_hash_num_elements(Z_ARRVAL_P(values)); 1029 | 1030 | if (!values_num) { 1031 | php_error_docref(NULL TSRMLS_CC, E_WARNING, "empty values array passed"); 1032 | RETURN_FALSE; 1033 | } 1034 | 1035 | attrs = emalloc(sizeof(char *) * attrs_num); 1036 | for (zend_hash_internal_pointer_reset(Z_ARRVAL_P(attributes)); 1037 | zend_hash_get_current_data(Z_ARRVAL_P(attributes), (void **) &item) != FAILURE; 1038 | zend_hash_move_forward(Z_ARRVAL_P(attributes))) { 1039 | if (Z_TYPE_PP(item) != IS_STRING) { 1040 | php_error_docref(NULL TSRMLS_CC, E_WARNING, "non-string attributes are not allowed"); 1041 | break; 1042 | } 1043 | attrs[a] = Z_STRVAL_PP(item); /* no copying here! */ 1044 | a++; 1045 | } 1046 | 1047 | /* cleanup on error */ 1048 | if (a != attrs_num) { 1049 | RETVAL_FALSE; 1050 | goto cleanup; 1051 | } 1052 | 1053 | docids = emalloc(sizeof(sphinx_int64_t) * values_num); 1054 | if (!mva) { 1055 | vals = safe_emalloc(values_num * attrs_num, sizeof(sphinx_int64_t), 0); 1056 | } 1057 | for (zend_hash_internal_pointer_reset(Z_ARRVAL_P(values)); 1058 | zend_hash_get_current_data(Z_ARRVAL_P(values), (void **) &item) != FAILURE; 1059 | zend_hash_move_forward(Z_ARRVAL_P(values))) { 1060 | char *str_id; 1061 | ulong id; 1062 | zval **attr_value; 1063 | int failed = 0, key_type; 1064 | uint str_id_len; 1065 | double float_id = 0; 1066 | unsigned char id_type; 1067 | 1068 | if (Z_TYPE_PP(item) != IS_ARRAY) { 1069 | php_error_docref(NULL TSRMLS_CC, E_WARNING, "value is not an array of attributes"); 1070 | break; 1071 | } 1072 | 1073 | if (zend_hash_num_elements(Z_ARRVAL_PP(item)) != attrs_num) { 1074 | php_error_docref(NULL TSRMLS_CC, E_WARNING, "number of values is not equal to the number of attributes"); 1075 | break; 1076 | } 1077 | 1078 | key_type = zend_hash_get_current_key_ex(Z_ARRVAL_P(values), &str_id, &str_id_len, &id, 0, NULL); 1079 | 1080 | if (key_type == HASH_KEY_IS_LONG) { 1081 | /* ok */ 1082 | id_type = IS_LONG; 1083 | } else if (key_type == HASH_KEY_IS_STRING) { 1084 | id_type = is_numeric_string(str_id, str_id_len, (long *)&id, &float_id, 0); 1085 | if (id_type == IS_LONG || id_type == IS_DOUBLE) { 1086 | /* ok */ 1087 | } else { 1088 | php_error_docref(NULL TSRMLS_CC, E_WARNING, "document ID must be numeric"); 1089 | break; 1090 | } 1091 | } else { 1092 | php_error_docref(NULL TSRMLS_CC, E_WARNING, "document ID must be integer"); 1093 | break; 1094 | } 1095 | 1096 | if (id_type == IS_LONG) { 1097 | docids[i] = (sphinx_uint64_t)id; 1098 | } else { /* IS_FLOAT */ 1099 | docids[i] = (sphinx_uint64_t)float_id; 1100 | } 1101 | 1102 | a = 0; 1103 | for (zend_hash_internal_pointer_reset(Z_ARRVAL_PP(item)); 1104 | zend_hash_get_current_data(Z_ARRVAL_PP(item), (void **) &attr_value) != FAILURE; 1105 | zend_hash_move_forward(Z_ARRVAL_PP(item))) { 1106 | if (mva) { 1107 | #if LIBSPHINX_VERSION_ID >= 110 1108 | if (Z_TYPE_PP(attr_value) != IS_ARRAY) { 1109 | php_error_docref(NULL TSRMLS_CC, E_WARNING, "attribute value must be an array"); 1110 | failed = 1; 1111 | break; 1112 | } 1113 | values_mva_num = zend_hash_num_elements(Z_ARRVAL_PP(attr_value)); 1114 | if (values_mva_num > values_mva_size) { 1115 | values_mva_size = values_mva_num; 1116 | vals_mva = safe_erealloc(vals_mva, values_mva_size, sizeof(unsigned int), 0); 1117 | } 1118 | if (vals_mva) { 1119 | memset(vals_mva, 0, values_mva_size * sizeof(unsigned int)); 1120 | } 1121 | 1122 | j = 0; 1123 | for (zend_hash_internal_pointer_reset(Z_ARRVAL_PP(attr_value)); 1124 | zend_hash_get_current_data(Z_ARRVAL_PP(attr_value), (void **) &attr_value_mva) != FAILURE; 1125 | zend_hash_move_forward(Z_ARRVAL_PP(attr_value))) { 1126 | if (Z_TYPE_PP(attr_value_mva) != IS_LONG) { 1127 | php_error_docref(NULL TSRMLS_CC, E_WARNING, "mva attribute value must be integer"); 1128 | failed = 1; 1129 | break; 1130 | } 1131 | vals_mva[j] = (unsigned int)Z_LVAL_PP(attr_value_mva); 1132 | j++; 1133 | } 1134 | if (failed) { 1135 | break; 1136 | } 1137 | 1138 | res_mva = sphinx_update_attributes_mva(c->sphinx, index, attrs[a], docids[i], values_mva_num, vals_mva); 1139 | 1140 | if (res_mva < 0) { 1141 | failed = 1; 1142 | break; 1143 | } 1144 | #endif 1145 | a++; 1146 | } else { 1147 | if (Z_TYPE_PP(attr_value) != IS_LONG) { 1148 | php_error_docref(NULL TSRMLS_CC, E_WARNING, "attribute value must be integer"); 1149 | failed = 1; 1150 | break; 1151 | } 1152 | vals[j] = (sphinx_int64_t)Z_LVAL_PP(attr_value); 1153 | j++; 1154 | } 1155 | } 1156 | 1157 | if (failed) { 1158 | break; 1159 | } 1160 | 1161 | if (mva) { 1162 | res++; 1163 | } 1164 | i++; 1165 | } 1166 | 1167 | if (!mva && i != values_num) { 1168 | RETVAL_FALSE; 1169 | goto cleanup; 1170 | } 1171 | 1172 | if (!mva) { 1173 | res = sphinx_update_attributes(c->sphinx, index, (int)attrs_num, attrs, values_num, docids, vals); 1174 | } 1175 | 1176 | if (res < 0) { 1177 | RETVAL_FALSE; 1178 | } else { 1179 | RETVAL_LONG(res); 1180 | } 1181 | 1182 | cleanup: 1183 | efree(attrs); 1184 | if (docids) { 1185 | efree(docids); 1186 | } 1187 | if (vals) { 1188 | efree(vals); 1189 | } 1190 | if (vals_mva) { 1191 | efree(vals_mva); 1192 | } 1193 | } 1194 | /* }}} */ 1195 | 1196 | /* {{{ proto array SphinxClient::buildExcerpts(array docs, string index, string words[, array opts]) */ 1197 | static PHP_METHOD(SphinxClient, buildExcerpts) 1198 | { 1199 | php_sphinx_client *c; 1200 | zval *docs_array, *opts_array = NULL, **item; 1201 | char *index, *words; 1202 | const char **docs; 1203 | sphinx_excerpt_options opts; 1204 | int index_len, words_len; 1205 | int docs_num, i = 0; 1206 | char **result; 1207 | 1208 | if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ass|a", &docs_array, &index, &index_len, &words, &words_len, &opts_array) == FAILURE) { 1209 | return; 1210 | } 1211 | 1212 | c = (php_sphinx_client *)zend_object_store_get_object(getThis() TSRMLS_CC); 1213 | SPHINX_INITIALIZED(c) 1214 | 1215 | docs_num = zend_hash_num_elements(Z_ARRVAL_P(docs_array)); 1216 | 1217 | if (!docs_num) { 1218 | php_error_docref(NULL TSRMLS_CC, E_WARNING, "empty documents array passed"); 1219 | RETURN_FALSE; 1220 | } 1221 | 1222 | docs = emalloc(sizeof(char *) * docs_num); 1223 | for (zend_hash_internal_pointer_reset(Z_ARRVAL_P(docs_array)); 1224 | zend_hash_get_current_data(Z_ARRVAL_P(docs_array), (void **) &item) != FAILURE; 1225 | zend_hash_move_forward(Z_ARRVAL_P(docs_array))) { 1226 | if (Z_TYPE_PP(item) != IS_STRING) { 1227 | php_error_docref(NULL TSRMLS_CC, E_WARNING, "non-string documents are not allowed"); 1228 | break; 1229 | } 1230 | docs[i] = Z_STRVAL_PP(item); /* no copying here! */ 1231 | i++; 1232 | } 1233 | 1234 | if (i != docs_num) { 1235 | RETVAL_FALSE; 1236 | goto cleanup; 1237 | } 1238 | 1239 | #define OPTS_EQUAL(str, str_len, txt) str_len == sizeof(txt) && memcmp(txt, str, sizeof(txt)) == 0 1240 | 1241 | if (opts_array) { 1242 | char *string_key; 1243 | unsigned int string_key_len; 1244 | ulong dummy; 1245 | 1246 | /* nullify everything */ 1247 | sphinx_init_excerpt_options(&opts); 1248 | for (zend_hash_internal_pointer_reset(Z_ARRVAL_P(opts_array)); 1249 | zend_hash_get_current_data(Z_ARRVAL_P(opts_array), (void **) &item) != FAILURE; 1250 | zend_hash_move_forward(Z_ARRVAL_P(opts_array))) { 1251 | 1252 | switch (Z_TYPE_PP(item)) { 1253 | case IS_STRING: 1254 | case IS_LONG: 1255 | case IS_BOOL: 1256 | break; 1257 | default: 1258 | continue; /* ignore invalid options */ 1259 | } 1260 | 1261 | if (zend_hash_get_current_key_ex(Z_ARRVAL_P(opts_array), &string_key, &string_key_len, &dummy, 0, NULL) != HASH_KEY_IS_STRING) { 1262 | continue; /* ignore invalid option names */ 1263 | } 1264 | 1265 | if (OPTS_EQUAL(string_key, string_key_len, "before_match")) { 1266 | SEPARATE_ZVAL(item); 1267 | convert_to_string_ex(item); 1268 | opts.before_match = Z_STRVAL_PP(item); 1269 | } else if (OPTS_EQUAL(string_key, string_key_len, "after_match")) { 1270 | SEPARATE_ZVAL(item); 1271 | convert_to_string_ex(item); 1272 | opts.after_match = Z_STRVAL_PP(item); 1273 | } else if (OPTS_EQUAL(string_key, string_key_len, "chunk_separator")) { 1274 | SEPARATE_ZVAL(item); 1275 | convert_to_string_ex(item); 1276 | opts.chunk_separator = Z_STRVAL_PP(item); 1277 | } else if (OPTS_EQUAL(string_key, string_key_len, "limit")) { 1278 | SEPARATE_ZVAL(item); 1279 | convert_to_long_ex(item); 1280 | opts.limit = (int)Z_LVAL_PP(item); 1281 | } else if (OPTS_EQUAL(string_key, string_key_len, "around")) { 1282 | SEPARATE_ZVAL(item); 1283 | convert_to_long_ex(item); 1284 | opts.around = (int)Z_LVAL_PP(item); 1285 | } else if (OPTS_EQUAL(string_key, string_key_len, "exact_phrase")) { 1286 | SEPARATE_ZVAL(item); 1287 | convert_to_boolean_ex(item); 1288 | opts.exact_phrase = Z_LVAL_PP(item); 1289 | } else if (OPTS_EQUAL(string_key, string_key_len, "single_passage")) { 1290 | SEPARATE_ZVAL(item); 1291 | convert_to_boolean_ex(item); 1292 | opts.single_passage = Z_LVAL_PP(item); 1293 | } else if (OPTS_EQUAL(string_key, string_key_len, "use_boundaries")) { 1294 | SEPARATE_ZVAL(item); 1295 | convert_to_boolean_ex(item); 1296 | opts.use_boundaries = Z_LVAL_PP(item); 1297 | } else if (OPTS_EQUAL(string_key, string_key_len, "weight_order")) { 1298 | SEPARATE_ZVAL(item); 1299 | convert_to_boolean_ex(item); 1300 | opts.weight_order = Z_LVAL_PP(item); 1301 | #if LIBSPHINX_VERSION_ID >= 110 1302 | } else if (OPTS_EQUAL(string_key, string_key_len, "query_mode")) { 1303 | SEPARATE_ZVAL(item); 1304 | convert_to_boolean_ex(item); 1305 | opts.query_mode = Z_LVAL_PP(item); 1306 | } else if (OPTS_EQUAL(string_key, string_key_len, "force_all_words")) { 1307 | SEPARATE_ZVAL(item); 1308 | convert_to_boolean_ex(item); 1309 | opts.force_all_words = Z_LVAL_PP(item); 1310 | } else if (OPTS_EQUAL(string_key, string_key_len, "limit_passages")) { 1311 | SEPARATE_ZVAL(item); 1312 | convert_to_long_ex(item); 1313 | opts.limit_passages = (int)Z_LVAL_PP(item); 1314 | } else if (OPTS_EQUAL(string_key, string_key_len, "limit_words")) { 1315 | SEPARATE_ZVAL(item); 1316 | convert_to_long_ex(item); 1317 | opts.limit_words = (int)Z_LVAL_PP(item); 1318 | } else if (OPTS_EQUAL(string_key, string_key_len, "start_passage_id")) { 1319 | SEPARATE_ZVAL(item); 1320 | convert_to_long_ex(item); 1321 | opts.start_passage_id = (int)Z_LVAL_PP(item); 1322 | } else if (OPTS_EQUAL(string_key, string_key_len, "load_files")) { 1323 | SEPARATE_ZVAL(item); 1324 | convert_to_boolean_ex(item); 1325 | opts.load_files = Z_LVAL_PP(item); 1326 | } else if (OPTS_EQUAL(string_key, string_key_len, "html_strip_mode")) { 1327 | SEPARATE_ZVAL(item); 1328 | convert_to_string_ex(item); 1329 | opts.html_strip_mode = Z_STRVAL_PP(item); 1330 | } else if (OPTS_EQUAL(string_key, string_key_len, "allow_empty")) { 1331 | SEPARATE_ZVAL(item); 1332 | convert_to_boolean_ex(item); 1333 | opts.allow_empty = Z_LVAL_PP(item); 1334 | #endif 1335 | } else { 1336 | /* ignore invalid option names */ 1337 | } 1338 | } 1339 | } 1340 | 1341 | if (opts_array) { 1342 | result = sphinx_build_excerpts(c->sphinx, docs_num, docs, index, words, &opts); 1343 | } else { 1344 | result = sphinx_build_excerpts(c->sphinx, docs_num, docs, index, words, NULL); 1345 | } 1346 | 1347 | if (!result) { 1348 | RETVAL_FALSE; 1349 | } else { 1350 | array_init(return_value); 1351 | for (i = 0; i < docs_num; i++) { 1352 | if (result[i] && result[i][0] != '\0') { 1353 | add_next_index_string(return_value, result[i], 1); 1354 | } else { 1355 | add_next_index_string(return_value, "", 1); 1356 | } 1357 | free(result[i]); 1358 | } 1359 | free(result); 1360 | } 1361 | 1362 | cleanup: 1363 | efree(docs); 1364 | } 1365 | /* }}} */ 1366 | 1367 | /* {{{ proto array SphinxClient::buildKeywords(string query, string index, bool hits) */ 1368 | static PHP_METHOD(SphinxClient, buildKeywords) 1369 | { 1370 | php_sphinx_client *c; 1371 | char *query, *index; 1372 | int query_len, index_len; 1373 | zend_bool hits; 1374 | sphinx_keyword_info *result; 1375 | int i, num_keywords; 1376 | zval *tmp; 1377 | 1378 | if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ssb", &query, &query_len, &index, &index_len, &hits) == FAILURE) { 1379 | return; 1380 | } 1381 | 1382 | c = (php_sphinx_client *)zend_object_store_get_object(getThis() TSRMLS_CC); 1383 | SPHINX_INITIALIZED(c) 1384 | 1385 | result = sphinx_build_keywords(c->sphinx, query, index, hits, &num_keywords); 1386 | if (!result || num_keywords <= 0) { 1387 | RETURN_FALSE; 1388 | } 1389 | 1390 | array_init(return_value); 1391 | for (i = 0; i < num_keywords; i++) { 1392 | MAKE_STD_ZVAL(tmp); 1393 | array_init(tmp); 1394 | 1395 | add_assoc_string_ex(tmp, "tokenized", sizeof("tokenized"), result[i].tokenized, 1); 1396 | add_assoc_string_ex(tmp, "normalized", sizeof("normalized"), result[i].normalized, 1); 1397 | 1398 | if (hits) { 1399 | add_assoc_long_ex(tmp, "docs", sizeof("docs"), result[i].num_docs); 1400 | add_assoc_long_ex(tmp, "hits", sizeof("hits"), result[i].num_hits); 1401 | } 1402 | 1403 | add_next_index_zval(return_value, tmp); 1404 | 1405 | free(result[i].tokenized); 1406 | free(result[i].normalized); 1407 | } 1408 | free(result); 1409 | } 1410 | /* }}} */ 1411 | 1412 | /* {{{ proto void SphinxClient::resetFilters() */ 1413 | static PHP_METHOD(SphinxClient, resetFilters) 1414 | { 1415 | php_sphinx_client *c; 1416 | 1417 | c = (php_sphinx_client *)zend_object_store_get_object(getThis() TSRMLS_CC); 1418 | SPHINX_INITIALIZED(c) 1419 | 1420 | sphinx_reset_filters(c->sphinx); 1421 | } 1422 | /* }}} */ 1423 | 1424 | /* {{{ proto void SphinxClient::resetGroupBy() */ 1425 | static PHP_METHOD(SphinxClient, resetGroupBy) 1426 | { 1427 | php_sphinx_client *c; 1428 | 1429 | c = (php_sphinx_client *)zend_object_store_get_object(getThis() TSRMLS_CC); 1430 | SPHINX_INITIALIZED(c) 1431 | 1432 | sphinx_reset_groupby(c->sphinx); 1433 | } 1434 | /* }}} */ 1435 | 1436 | /* {{{ proto string SphinxClient::getLastWarning() */ 1437 | static PHP_METHOD(SphinxClient, getLastWarning) 1438 | { 1439 | php_sphinx_client *c; 1440 | const char *warning; 1441 | 1442 | c = (php_sphinx_client *)zend_object_store_get_object(getThis() TSRMLS_CC); 1443 | SPHINX_INITIALIZED(c) 1444 | 1445 | warning = sphinx_warning(c->sphinx); 1446 | if (!warning || !warning[0]) { 1447 | RETURN_EMPTY_STRING(); 1448 | } 1449 | RETURN_STRING((char *)warning, 1); 1450 | } 1451 | /* }}} */ 1452 | 1453 | /* {{{ proto string SphinxClient::getLastError() */ 1454 | static PHP_METHOD(SphinxClient, getLastError) 1455 | { 1456 | php_sphinx_client *c; 1457 | const char *error; 1458 | 1459 | c = (php_sphinx_client *)zend_object_store_get_object(getThis() TSRMLS_CC); 1460 | SPHINX_INITIALIZED(c) 1461 | 1462 | error = sphinx_error(c->sphinx); 1463 | if (!error || !error[0]) { 1464 | RETURN_EMPTY_STRING(); 1465 | } 1466 | RETURN_STRING((char *)error, 1); 1467 | } 1468 | /* }}} */ 1469 | 1470 | /* {{{ proto array SphinxClient::query(string query[, string index[, string comment]]) */ 1471 | static PHP_METHOD(SphinxClient, query) 1472 | { 1473 | php_sphinx_client *c; 1474 | char *query, *index = "*", *comment = ""; 1475 | int query_len, index_len, comment_len; 1476 | sphinx_result *result; 1477 | 1478 | if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|ss", &query, &query_len, &index, &index_len, &comment, &comment_len) == FAILURE) { 1479 | return; 1480 | } 1481 | 1482 | c = (php_sphinx_client *)zend_object_store_get_object(getThis() TSRMLS_CC); 1483 | SPHINX_INITIALIZED(c) 1484 | 1485 | result = sphinx_query(c->sphinx, query, index, comment); 1486 | 1487 | if (!result) { 1488 | RETURN_FALSE; 1489 | } 1490 | 1491 | php_sphinx_result_to_array(c, result, &return_value TSRMLS_CC); 1492 | } 1493 | 1494 | /* }}} */ 1495 | 1496 | /* {{{ proto int SphinxClient::addQuery(string query[, string index[, string comment]]) */ 1497 | static PHP_METHOD(SphinxClient, addQuery) 1498 | { 1499 | php_sphinx_client *c; 1500 | char *query, *index = "*", *comment = ""; 1501 | int query_len, index_len, comment_len, res; 1502 | 1503 | if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|ss", &query, &query_len, &index, &index_len, &comment, &comment_len) == FAILURE) { 1504 | return; 1505 | } 1506 | 1507 | c = (php_sphinx_client *)zend_object_store_get_object(getThis() TSRMLS_CC); 1508 | SPHINX_INITIALIZED(c) 1509 | 1510 | res = sphinx_add_query(c->sphinx, query, index, comment); 1511 | 1512 | if (res < 0) { 1513 | RETURN_FALSE; 1514 | } 1515 | RETURN_LONG(res); 1516 | } 1517 | 1518 | /* }}} */ 1519 | 1520 | /* {{{ proto array SphinxClient::runQueries() */ 1521 | static PHP_METHOD(SphinxClient, runQueries) 1522 | { 1523 | php_sphinx_client *c; 1524 | sphinx_result *results; 1525 | int i, num_results; 1526 | zval *single_result; 1527 | 1528 | c = (php_sphinx_client *)zend_object_store_get_object(getThis() TSRMLS_CC); 1529 | SPHINX_INITIALIZED(c) 1530 | 1531 | results = sphinx_run_queries(c->sphinx); 1532 | 1533 | if (!results) { 1534 | RETURN_FALSE; 1535 | } 1536 | 1537 | num_results = sphinx_get_num_results(c->sphinx); 1538 | 1539 | array_init(return_value); 1540 | for (i = 0; i < num_results; i++) { 1541 | MAKE_STD_ZVAL(single_result); 1542 | php_sphinx_result_to_array(c, &results[i], &single_result TSRMLS_CC); 1543 | add_next_index_zval(return_value, single_result); 1544 | } 1545 | } 1546 | /* }}} */ 1547 | 1548 | /* {{{ proto string SphinxClient::escapeString(string data) */ 1549 | static PHP_METHOD(SphinxClient, escapeString) 1550 | { 1551 | char *str, *new_str, *source, *target; 1552 | int str_len, new_str_len, i; 1553 | 1554 | if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &str, &str_len) == FAILURE) { 1555 | return; 1556 | } 1557 | 1558 | if (!str_len) { 1559 | RETURN_EMPTY_STRING(); 1560 | } 1561 | 1562 | new_str = safe_emalloc(2, str_len, 1); 1563 | target = new_str; 1564 | source = str; 1565 | for (i = 0; i < str_len; i++) { 1566 | switch (*source) { 1567 | case '(': 1568 | case ')': 1569 | case '|': 1570 | case '-': 1571 | case '!': 1572 | case '@': 1573 | case '~': 1574 | case '"': 1575 | case '&': 1576 | case '/': 1577 | case '\\': 1578 | case '^': 1579 | case '$': 1580 | case '=': 1581 | case '<': 1582 | *target++ = '\\'; 1583 | *target++ = *source; 1584 | break; 1585 | default: 1586 | *target++ = *source; 1587 | break; 1588 | } 1589 | source++; 1590 | } 1591 | *target = '\0'; 1592 | 1593 | new_str_len = target - new_str; 1594 | new_str = erealloc(new_str, new_str_len + 1); 1595 | RETURN_STRINGL(new_str, new_str_len, 0); 1596 | } 1597 | /* }}} */ 1598 | 1599 | #if LIBSPHINX_VERSION_ID >= 99 1600 | /* {{{ proto bool SphinxClient::open() */ 1601 | static PHP_METHOD(SphinxClient, open) 1602 | { 1603 | php_sphinx_client *c; 1604 | int res; 1605 | 1606 | c = (php_sphinx_client *)zend_object_store_get_object(getThis() TSRMLS_CC); 1607 | SPHINX_INITIALIZED(c) 1608 | 1609 | res = sphinx_open(c->sphinx); 1610 | if (!res) { 1611 | RETURN_FALSE; 1612 | } 1613 | RETURN_TRUE; 1614 | } 1615 | /* }}} */ 1616 | 1617 | /* {{{ proto bool SphinxClient::close() */ 1618 | static PHP_METHOD(SphinxClient, close) 1619 | { 1620 | php_sphinx_client *c; 1621 | int res; 1622 | 1623 | c = (php_sphinx_client *)zend_object_store_get_object(getThis() TSRMLS_CC); 1624 | SPHINX_INITIALIZED(c) 1625 | 1626 | res = sphinx_close(c->sphinx); 1627 | if (!res) { 1628 | RETURN_FALSE; 1629 | } 1630 | RETURN_TRUE; 1631 | } 1632 | /* }}} */ 1633 | 1634 | /* {{{ proto array SphinxClient::status() */ 1635 | static PHP_METHOD(SphinxClient, status) 1636 | { 1637 | php_sphinx_client *c; 1638 | char **result; 1639 | int i, j, k, num_rows, num_cols; 1640 | zval *tmp; 1641 | 1642 | c = (php_sphinx_client *)zend_object_store_get_object(getThis() TSRMLS_CC); 1643 | SPHINX_INITIALIZED(c) 1644 | 1645 | result = sphinx_status(c->sphinx, &num_rows, &num_cols); 1646 | 1647 | if (!result || num_rows <= 0) { 1648 | RETURN_FALSE; 1649 | } 1650 | 1651 | k = 0; 1652 | array_init(return_value); 1653 | for (i = 0; i < num_rows; i++) { 1654 | MAKE_STD_ZVAL(tmp); 1655 | array_init(tmp); 1656 | 1657 | for (j = 0; j < num_cols; j++, k++) { 1658 | add_next_index_string(tmp, result[k], 1); 1659 | } 1660 | add_next_index_zval(return_value, tmp); 1661 | } 1662 | sphinx_status_destroy(result, num_rows, num_cols); 1663 | } 1664 | /* }}} */ 1665 | 1666 | /* {{{ proto bool SphinxClient::setOverride(string attribute, int type, array values) */ 1667 | static PHP_METHOD(SphinxClient, setOverride) 1668 | { 1669 | php_sphinx_client *c; 1670 | zval *values, **attr_value; 1671 | char *attribute; 1672 | long type; 1673 | int attribute_len, values_num, i = 0; 1674 | int res; 1675 | sphinx_uint64_t *docids = NULL; 1676 | unsigned int *vals = NULL; 1677 | 1678 | if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sla", &attribute, &attribute_len, &type, &values) == FAILURE) { 1679 | return; 1680 | } 1681 | 1682 | c = (php_sphinx_client *)zend_object_store_get_object(getThis() TSRMLS_CC); 1683 | SPHINX_INITIALIZED(c) 1684 | 1685 | if (type != SPH_ATTR_INTEGER && type != SPH_ATTR_TIMESTAMP 1686 | && type != SPH_ATTR_BOOL && type != SPH_ATTR_FLOAT) { 1687 | php_error_docref(NULL TSRMLS_CC, E_WARNING, "type must be scalar"); 1688 | RETURN_FALSE; 1689 | } 1690 | 1691 | values_num = zend_hash_num_elements(Z_ARRVAL_P(values)); 1692 | if (!values_num) { 1693 | php_error_docref(NULL TSRMLS_CC, E_WARNING, "empty values array passed"); 1694 | RETURN_FALSE; 1695 | } 1696 | 1697 | docids = emalloc(sizeof(sphinx_uint64_t) * values_num); 1698 | vals = safe_emalloc(values_num, sizeof(unsigned int), 0); 1699 | for (zend_hash_internal_pointer_reset(Z_ARRVAL_P(values)); 1700 | zend_hash_get_current_data(Z_ARRVAL_P(values), (void **) &attr_value) != FAILURE; 1701 | zend_hash_move_forward(Z_ARRVAL_P(values))) 1702 | { 1703 | char *str_id; 1704 | ulong id; 1705 | int key_type; 1706 | uint str_id_len; 1707 | double float_id = 0; 1708 | unsigned char id_type; 1709 | 1710 | if (Z_TYPE_PP(attr_value) != IS_LONG) { 1711 | php_error_docref(NULL TSRMLS_CC, E_WARNING, "attribute value must be integer"); 1712 | break; 1713 | } 1714 | 1715 | key_type = zend_hash_get_current_key_ex(Z_ARRVAL_P(values), &str_id, &str_id_len, &id, 0, NULL); 1716 | 1717 | if (key_type == HASH_KEY_IS_LONG) { 1718 | /* ok */ 1719 | id_type = IS_LONG; 1720 | } else if (key_type == HASH_KEY_IS_STRING) { 1721 | id_type = is_numeric_string(str_id, str_id_len, (long *)&id, &float_id, 0); 1722 | if (id_type == IS_LONG || id_type == IS_DOUBLE) { 1723 | /* ok */ 1724 | } else { 1725 | php_error_docref(NULL TSRMLS_CC, E_WARNING, "document ID must be numeric"); 1726 | break; 1727 | } 1728 | } else { 1729 | php_error_docref(NULL TSRMLS_CC, E_WARNING, "document ID must be integer"); 1730 | break; 1731 | } 1732 | vals[i] = (sphinx_uint64_t)Z_LVAL_PP(attr_value); 1733 | 1734 | if (id_type == IS_LONG) { 1735 | docids[i] = (sphinx_uint64_t)id; 1736 | } else { /* IS_FLOAT */ 1737 | docids[i] = (sphinx_uint64_t)float_id; 1738 | } 1739 | i++; 1740 | } 1741 | 1742 | if (i != values_num) { 1743 | RETVAL_FALSE; 1744 | goto cleanup; 1745 | } 1746 | 1747 | res = sphinx_add_override(c->sphinx, attribute, docids, values_num, vals); 1748 | if (!res) { 1749 | RETVAL_FALSE; 1750 | } else { 1751 | RETVAL_TRUE; 1752 | } 1753 | 1754 | cleanup: 1755 | if (docids) { 1756 | efree(docids); 1757 | } 1758 | if (vals) { 1759 | efree(vals); 1760 | } 1761 | } 1762 | /* }}} */ 1763 | #endif 1764 | 1765 | /* {{{ proto int SphinxClient::__sleep() */ 1766 | static PHP_METHOD(SphinxClient, __sleep) 1767 | { 1768 | php_error_docref(NULL TSRMLS_CC, E_RECOVERABLE_ERROR, "SphinxClient instance cannot be (un)serialized"); 1769 | } 1770 | /* }}} */ 1771 | 1772 | /* {{{ proto int SphinxClient::__wakeup() */ 1773 | static PHP_METHOD(SphinxClient, __wakeup) 1774 | { 1775 | php_error_docref(NULL TSRMLS_CC, E_RECOVERABLE_ERROR, "SphinxClient instance cannot be (un)serialized"); 1776 | } 1777 | /* }}} */ 1778 | 1779 | /* {{{ arginfo */ 1780 | ZEND_BEGIN_ARG_INFO_EX(arginfo_sphinxclient_setserver, 0, 0, 2) 1781 | ZEND_ARG_INFO(0, server) 1782 | ZEND_ARG_INFO(0, port) 1783 | ZEND_END_ARG_INFO() 1784 | 1785 | ZEND_BEGIN_ARG_INFO_EX(arginfo_sphinxclient_setlimits, 0, 0, 2) 1786 | ZEND_ARG_INFO(0, offset) 1787 | ZEND_ARG_INFO(0, limit) 1788 | ZEND_ARG_INFO(0, max_matches) 1789 | ZEND_ARG_INFO(0, cutoff) 1790 | ZEND_END_ARG_INFO() 1791 | 1792 | ZEND_BEGIN_ARG_INFO_EX(arginfo_sphinxclient_setmatchmode, 0, 0, 1) 1793 | ZEND_ARG_INFO(0, mode) 1794 | ZEND_END_ARG_INFO() 1795 | 1796 | ZEND_BEGIN_ARG_INFO_EX(arginfo_sphinxclient_setindexweights, 0, 0, 1) 1797 | ZEND_ARG_INFO(0, weights) 1798 | ZEND_END_ARG_INFO() 1799 | 1800 | #if LIBSPHINX_VERSION_ID >= 99 1801 | ZEND_BEGIN_ARG_INFO_EX(arginfo_sphinxclient_setselect, 0, 0, 1) 1802 | ZEND_ARG_INFO(0, clause) 1803 | ZEND_END_ARG_INFO() 1804 | #endif 1805 | 1806 | ZEND_BEGIN_ARG_INFO_EX(arginfo_sphinxclient_setidrange, 0, 0, 2) 1807 | ZEND_ARG_INFO(0, min) 1808 | ZEND_ARG_INFO(0, max) 1809 | ZEND_END_ARG_INFO() 1810 | 1811 | ZEND_BEGIN_ARG_INFO_EX(arginfo_sphinxclient_setfilter, 0, 0, 2) 1812 | ZEND_ARG_INFO(0, attribute) 1813 | ZEND_ARG_INFO(0, values) 1814 | ZEND_ARG_INFO(0, exclude) 1815 | ZEND_END_ARG_INFO() 1816 | 1817 | ZEND_BEGIN_ARG_INFO_EX(arginfo_sphinxclient_setfilterstring, 0, 0, 2) 1818 | ZEND_ARG_INFO(0, attribute) 1819 | ZEND_ARG_INFO(0, value) 1820 | ZEND_ARG_INFO(0, exclude) 1821 | ZEND_END_ARG_INFO() 1822 | 1823 | ZEND_BEGIN_ARG_INFO_EX(arginfo_sphinxclient_setfilterrange, 0, 0, 3) 1824 | ZEND_ARG_INFO(0, attribute) 1825 | ZEND_ARG_INFO(0, min) 1826 | ZEND_ARG_INFO(0, max) 1827 | ZEND_ARG_INFO(0, exclude) 1828 | ZEND_END_ARG_INFO() 1829 | 1830 | ZEND_BEGIN_ARG_INFO_EX(arginfo_sphinxclient_setgeoanchor, 0, 0, 4) 1831 | ZEND_ARG_INFO(0, attrlat) 1832 | ZEND_ARG_INFO(0, attrlong) 1833 | ZEND_ARG_INFO(0, latitude) 1834 | ZEND_ARG_INFO(0, longitude) 1835 | ZEND_END_ARG_INFO() 1836 | 1837 | ZEND_BEGIN_ARG_INFO_EX(arginfo_sphinxclient_setgroupby, 0, 0, 2) 1838 | ZEND_ARG_INFO(0, attribute) 1839 | ZEND_ARG_INFO(0, func) 1840 | ZEND_ARG_INFO(0, groupsort) 1841 | ZEND_END_ARG_INFO() 1842 | 1843 | ZEND_BEGIN_ARG_INFO_EX(arginfo_sphinxclient_setgroupdistinct, 0, 0, 1) 1844 | ZEND_ARG_INFO(0, attribute) 1845 | ZEND_END_ARG_INFO() 1846 | 1847 | ZEND_BEGIN_ARG_INFO_EX(arginfo_sphinxclient_setretries, 0, 0, 1) 1848 | ZEND_ARG_INFO(0, count) 1849 | ZEND_ARG_INFO(0, delay) 1850 | ZEND_END_ARG_INFO() 1851 | 1852 | ZEND_BEGIN_ARG_INFO_EX(arginfo_sphinxclient_setmaxquerytime, 0, 0, 1) 1853 | ZEND_ARG_INFO(0, qtime) 1854 | ZEND_END_ARG_INFO() 1855 | 1856 | #if LIBSPHINX_VERSION_ID >= 99 1857 | ZEND_BEGIN_ARG_INFO_EX(arginfo_sphinxclient_setoverride, 0, 0, 3) 1858 | ZEND_ARG_INFO(0, attribute) 1859 | ZEND_ARG_INFO(0, type) 1860 | ZEND_ARG_INFO(0, values) 1861 | ZEND_END_ARG_INFO() 1862 | #endif 1863 | 1864 | #if HAVE_3ARG_SPHINX_SET_RANKING_MODE 1865 | ZEND_BEGIN_ARG_INFO_EX(arginfo_sphinxclient_setrankingmode, 0, 0, 1) 1866 | ZEND_ARG_INFO(0, ranker) 1867 | ZEND_ARG_INFO(0, rank_expression) 1868 | ZEND_END_ARG_INFO() 1869 | #else 1870 | ZEND_BEGIN_ARG_INFO_EX(arginfo_sphinxclient_setrankingmode, 0, 0, 1) 1871 | ZEND_ARG_INFO(0, ranker) 1872 | ZEND_END_ARG_INFO() 1873 | #endif 1874 | 1875 | ZEND_BEGIN_ARG_INFO_EX(arginfo_sphinxclient_setsortmode, 0, 0, 1) 1876 | ZEND_ARG_INFO(0, mode) 1877 | ZEND_ARG_INFO(0, sortby) 1878 | ZEND_END_ARG_INFO() 1879 | 1880 | ZEND_BEGIN_ARG_INFO_EX(arginfo_sphinxclient_setconnecttimeout, 0, 0, 1) 1881 | ZEND_ARG_INFO(0, timeout) 1882 | ZEND_END_ARG_INFO() 1883 | 1884 | ZEND_BEGIN_ARG_INFO_EX(arginfo_sphinxclient_setarrayresult, 0, 0, 1) 1885 | ZEND_ARG_INFO(0, array_result) 1886 | ZEND_END_ARG_INFO() 1887 | 1888 | ZEND_BEGIN_ARG_INFO_EX(arginfo_sphinxclient_updateattributes, 0, 0, 3) 1889 | ZEND_ARG_INFO(0, index) 1890 | ZEND_ARG_INFO(0, attributes) 1891 | ZEND_ARG_INFO(0, values) 1892 | ZEND_ARG_INFO(0, mva) 1893 | ZEND_END_ARG_INFO() 1894 | 1895 | ZEND_BEGIN_ARG_INFO_EX(arginfo_sphinxclient_buildexcerpts, 0, 0, 3) 1896 | ZEND_ARG_INFO(0, docs) 1897 | ZEND_ARG_INFO(0, index) 1898 | ZEND_ARG_INFO(0, words) 1899 | ZEND_ARG_INFO(0, opts) 1900 | ZEND_END_ARG_INFO() 1901 | 1902 | ZEND_BEGIN_ARG_INFO_EX(arginfo_sphinxclient_buildkeywords, 0, 0, 3) 1903 | ZEND_ARG_INFO(0, query) 1904 | ZEND_ARG_INFO(0, index) 1905 | ZEND_ARG_INFO(0, hits) 1906 | ZEND_END_ARG_INFO() 1907 | 1908 | ZEND_BEGIN_ARG_INFO_EX(arginfo_sphinxclient_query, 0, 0, 1) 1909 | ZEND_ARG_INFO(0, query) 1910 | ZEND_ARG_INFO(0, index) 1911 | ZEND_ARG_INFO(0, comment) 1912 | ZEND_END_ARG_INFO() 1913 | 1914 | ZEND_BEGIN_ARG_INFO(arginfo_sphinxclient__param_void, 0) 1915 | ZEND_END_ARG_INFO() 1916 | 1917 | ZEND_BEGIN_ARG_INFO_EX(arginfo_sphinxclient_escapestring, 0, 0, 1) 1918 | ZEND_ARG_INFO(0, data) 1919 | ZEND_END_ARG_INFO() 1920 | /* }}} */ 1921 | 1922 | static zend_function_entry sphinx_client_methods[] = { /* {{{ */ 1923 | PHP_ME(SphinxClient, __construct, arginfo_sphinxclient__param_void, ZEND_ACC_PUBLIC) 1924 | PHP_ME(SphinxClient, addQuery, arginfo_sphinxclient_query, ZEND_ACC_PUBLIC) 1925 | PHP_ME(SphinxClient, buildExcerpts, arginfo_sphinxclient_buildexcerpts, ZEND_ACC_PUBLIC) 1926 | PHP_ME(SphinxClient, buildKeywords, arginfo_sphinxclient_buildkeywords, ZEND_ACC_PUBLIC) 1927 | #if LIBSPHINX_VERSION_ID >= 99 1928 | PHP_ME(SphinxClient, close, arginfo_sphinxclient__param_void, ZEND_ACC_PUBLIC) 1929 | #endif 1930 | PHP_ME(SphinxClient, getLastError, arginfo_sphinxclient__param_void, ZEND_ACC_PUBLIC) 1931 | PHP_ME(SphinxClient, getLastWarning, arginfo_sphinxclient__param_void, ZEND_ACC_PUBLIC) 1932 | PHP_ME(SphinxClient, escapeString, arginfo_sphinxclient_escapestring, ZEND_ACC_PUBLIC) 1933 | #if LIBSPHINX_VERSION_ID >= 99 1934 | PHP_ME(SphinxClient, open, arginfo_sphinxclient__param_void, ZEND_ACC_PUBLIC) 1935 | #endif 1936 | PHP_ME(SphinxClient, query, arginfo_sphinxclient_query, ZEND_ACC_PUBLIC) 1937 | PHP_ME(SphinxClient, resetFilters, arginfo_sphinxclient__param_void, ZEND_ACC_PUBLIC) 1938 | PHP_ME(SphinxClient, resetGroupBy, arginfo_sphinxclient__param_void, ZEND_ACC_PUBLIC) 1939 | PHP_ME(SphinxClient, runQueries, arginfo_sphinxclient__param_void, ZEND_ACC_PUBLIC) 1940 | PHP_ME(SphinxClient, setArrayResult, arginfo_sphinxclient_setarrayresult, ZEND_ACC_PUBLIC) 1941 | PHP_ME(SphinxClient, setConnectTimeout, arginfo_sphinxclient_setconnecttimeout, ZEND_ACC_PUBLIC) 1942 | PHP_ME(SphinxClient, setFieldWeights, arginfo_sphinxclient_setindexweights, ZEND_ACC_PUBLIC) 1943 | PHP_ME(SphinxClient, setFilter, arginfo_sphinxclient_setfilter, ZEND_ACC_PUBLIC) 1944 | #ifdef HAVE_SPHINX_ADD_FILTER_STRING 1945 | PHP_ME(SphinxClient, setFilterString, arginfo_sphinxclient_setfilterstring, ZEND_ACC_PUBLIC) 1946 | #endif 1947 | PHP_ME(SphinxClient, setFilterFloatRange, arginfo_sphinxclient_setfilterrange, ZEND_ACC_PUBLIC) 1948 | PHP_ME(SphinxClient, setFilterRange, arginfo_sphinxclient_setfilterrange, ZEND_ACC_PUBLIC) 1949 | PHP_ME(SphinxClient, setGeoAnchor, arginfo_sphinxclient_setgeoanchor, ZEND_ACC_PUBLIC) 1950 | PHP_ME(SphinxClient, setGroupBy, arginfo_sphinxclient_setgroupby, ZEND_ACC_PUBLIC) 1951 | PHP_ME(SphinxClient, setGroupDistinct, arginfo_sphinxclient_setgroupdistinct, ZEND_ACC_PUBLIC) 1952 | PHP_ME(SphinxClient, setIndexWeights, arginfo_sphinxclient_setindexweights, ZEND_ACC_PUBLIC) 1953 | PHP_ME(SphinxClient, setIDRange, arginfo_sphinxclient_setidrange, ZEND_ACC_PUBLIC) 1954 | #if LIBSPHINX_VERSION_ID >= 99 1955 | PHP_ME(SphinxClient, setSelect, arginfo_sphinxclient_setselect, ZEND_ACC_PUBLIC) 1956 | #endif 1957 | PHP_ME(SphinxClient, setLimits, arginfo_sphinxclient_setlimits, ZEND_ACC_PUBLIC) 1958 | PHP_ME(SphinxClient, setMatchMode, arginfo_sphinxclient_setmatchmode, ZEND_ACC_PUBLIC) 1959 | PHP_ME(SphinxClient, setMaxQueryTime, arginfo_sphinxclient_setmaxquerytime, ZEND_ACC_PUBLIC) 1960 | #if LIBSPHINX_VERSION_ID >= 99 1961 | PHP_ME(SphinxClient, setOverride, arginfo_sphinxclient_setoverride, ZEND_ACC_PUBLIC) 1962 | #endif 1963 | PHP_ME(SphinxClient, setRankingMode, arginfo_sphinxclient_setrankingmode, ZEND_ACC_PUBLIC) 1964 | PHP_ME(SphinxClient, setRetries, arginfo_sphinxclient_setretries, ZEND_ACC_PUBLIC) 1965 | PHP_ME(SphinxClient, setServer, arginfo_sphinxclient_setserver, ZEND_ACC_PUBLIC) 1966 | PHP_ME(SphinxClient, setSortMode, arginfo_sphinxclient_setsortmode, ZEND_ACC_PUBLIC) 1967 | #if LIBSPHINX_VERSION_ID >= 99 1968 | PHP_ME(SphinxClient, status, arginfo_sphinxclient__param_void, ZEND_ACC_PUBLIC) 1969 | #endif 1970 | PHP_ME(SphinxClient, updateAttributes, arginfo_sphinxclient_updateattributes, ZEND_ACC_PUBLIC) 1971 | PHP_ME(SphinxClient, __sleep, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL) 1972 | PHP_ME(SphinxClient, __wakeup, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_FINAL) 1973 | {NULL, NULL, NULL} 1974 | }; 1975 | /* }}} */ 1976 | 1977 | /* {{{ PHP_MINIT_FUNCTION 1978 | */ 1979 | PHP_MINIT_FUNCTION(sphinx) 1980 | { 1981 | zend_class_entry ce; 1982 | 1983 | memcpy(&cannot_be_cloned, zend_get_std_object_handlers(), sizeof(zend_object_handlers)); 1984 | cannot_be_cloned.clone_obj = NULL; 1985 | 1986 | memcpy(&php_sphinx_client_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers)); 1987 | php_sphinx_client_handlers.clone_obj = NULL; 1988 | php_sphinx_client_handlers.read_property = php_sphinx_client_read_property; 1989 | php_sphinx_client_handlers.get_properties = php_sphinx_client_get_properties; 1990 | 1991 | INIT_CLASS_ENTRY(ce, "SphinxClient", sphinx_client_methods); 1992 | ce_sphinx_client = zend_register_internal_class(&ce TSRMLS_CC); 1993 | ce_sphinx_client->create_object = php_sphinx_client_new; 1994 | 1995 | SPHINX_CONST(SEARCHD_OK); 1996 | SPHINX_CONST(SEARCHD_ERROR); 1997 | SPHINX_CONST(SEARCHD_RETRY); 1998 | SPHINX_CONST(SEARCHD_WARNING); 1999 | 2000 | SPHINX_CONST(SPH_MATCH_ALL); 2001 | SPHINX_CONST(SPH_MATCH_ANY); 2002 | SPHINX_CONST(SPH_MATCH_PHRASE); 2003 | SPHINX_CONST(SPH_MATCH_BOOLEAN); 2004 | SPHINX_CONST(SPH_MATCH_EXTENDED); 2005 | SPHINX_CONST(SPH_MATCH_FULLSCAN); 2006 | SPHINX_CONST(SPH_MATCH_EXTENDED2); 2007 | 2008 | SPHINX_CONST(SPH_RANK_PROXIMITY_BM25); 2009 | SPHINX_CONST(SPH_RANK_BM25); 2010 | SPHINX_CONST(SPH_RANK_NONE); 2011 | SPHINX_CONST(SPH_RANK_WORDCOUNT); 2012 | #ifdef HAVE_SPH_RANK_PROXIMITY 2013 | SPHINX_CONST(SPH_RANK_PROXIMITY); 2014 | #endif 2015 | #ifdef HAVE_SPH_RANK_MATCHANY 2016 | SPHINX_CONST(SPH_RANK_MATCHANY); 2017 | #endif 2018 | #ifdef HAVE_SPH_RANK_FIELDMASK 2019 | SPHINX_CONST(SPH_RANK_FIELDMASK); 2020 | #endif 2021 | #ifdef HAVE_SPH_RANK_SPH04 2022 | SPHINX_CONST(SPH_RANK_SPH04); 2023 | #endif 2024 | #ifdef HAVE_SPH_RANK_EXPR 2025 | SPHINX_CONST(SPH_RANK_EXPR); 2026 | #endif 2027 | #ifdef HAVE_SPH_RANK_TOTAL 2028 | SPHINX_CONST(SPH_RANK_TOTAL); 2029 | #endif 2030 | 2031 | SPHINX_CONST(SPH_SORT_RELEVANCE); 2032 | SPHINX_CONST(SPH_SORT_ATTR_DESC); 2033 | SPHINX_CONST(SPH_SORT_ATTR_ASC); 2034 | SPHINX_CONST(SPH_SORT_TIME_SEGMENTS); 2035 | SPHINX_CONST(SPH_SORT_EXTENDED); 2036 | SPHINX_CONST(SPH_SORT_EXPR); 2037 | 2038 | SPHINX_CONST(SPH_FILTER_VALUES); 2039 | SPHINX_CONST(SPH_FILTER_RANGE); 2040 | SPHINX_CONST(SPH_FILTER_FLOATRANGE); 2041 | 2042 | SPHINX_CONST(SPH_ATTR_INTEGER); 2043 | SPHINX_CONST(SPH_ATTR_TIMESTAMP); 2044 | SPHINX_CONST(SPH_ATTR_ORDINAL); 2045 | SPHINX_CONST(SPH_ATTR_BOOL); 2046 | SPHINX_CONST(SPH_ATTR_FLOAT); 2047 | SPHINX_CONST(SPH_ATTR_MULTI); 2048 | 2049 | SPHINX_CONST(SPH_GROUPBY_DAY); 2050 | SPHINX_CONST(SPH_GROUPBY_WEEK); 2051 | SPHINX_CONST(SPH_GROUPBY_MONTH); 2052 | SPHINX_CONST(SPH_GROUPBY_YEAR); 2053 | SPHINX_CONST(SPH_GROUPBY_ATTR); 2054 | SPHINX_CONST(SPH_GROUPBY_ATTRPAIR); 2055 | 2056 | return SUCCESS; 2057 | } 2058 | /* }}} */ 2059 | 2060 | /* {{{ PHP_MINFO_FUNCTION 2061 | */ 2062 | PHP_MINFO_FUNCTION(sphinx) 2063 | { 2064 | php_info_print_table_start(); 2065 | php_info_print_table_header(2, "sphinx support", "enabled"); 2066 | php_info_print_table_header(2, "Version", PHP_SPHINX_VERSION); 2067 | php_info_print_table_header(2, "Revision", "$Revision$"); 2068 | php_info_print_table_end(); 2069 | } 2070 | /* }}} */ 2071 | 2072 | static zend_function_entry sphinx_functions[] = { /* {{{ */ 2073 | {NULL, NULL, NULL} 2074 | }; 2075 | /* }}} */ 2076 | 2077 | /* {{{ sphinx_module_entry 2078 | */ 2079 | zend_module_entry sphinx_module_entry = { 2080 | #if ZEND_MODULE_API_NO >= 20010901 2081 | STANDARD_MODULE_HEADER, 2082 | #endif 2083 | "sphinx", 2084 | sphinx_functions, 2085 | PHP_MINIT(sphinx), 2086 | NULL, 2087 | NULL, 2088 | NULL, 2089 | PHP_MINFO(sphinx), 2090 | #if ZEND_MODULE_API_NO >= 20010901 2091 | PHP_SPHINX_VERSION, 2092 | #endif 2093 | STANDARD_MODULE_PROPERTIES 2094 | }; 2095 | /* }}} */ 2096 | 2097 | /* 2098 | * Local variables: 2099 | * tab-width: 4 2100 | * c-basic-offset: 4 2101 | * End: 2102 | * vim600: noet sw=4 ts=4 fdm=marker 2103 | * vim<600: noet sw=4 ts=4 2104 | */ 2105 | --------------------------------------------------------------------------------