1 /**
2  *     Copyright 2016-2017 Couchbase, Inc.
3  *
4  *   Licensed under the Apache License, Version 2.0 (the "License");
5  *   you may not use this file except in compliance with the License.
6  *   You may obtain a copy of the License at
7  *
8  *       http://www.apache.org/licenses/LICENSE-2.0
9  *
10  *   Unless required by applicable law or agreed to in writing, software
11  *   distributed under the License is distributed on an "AS IS" BASIS,
12  *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  *   See the License for the specific language governing permissions and
14  *   limitations under the License.
15  */
16 
17 /**
18  * A FTS query that matches several given terms (a "phrase"), applying further processing
19  * like analyzers to them.
20  */
21 #include "couchbase.h"
22 
23 typedef struct {
24     PCBC_ZEND_OBJECT_PRE
25     double boost;
26     char *field;
27     char *analyzer;
28     char *match_phrase;
29     PCBC_ZEND_OBJECT_POST
30 } pcbc_match_phrase_search_query_t;
31 
32 #if PHP_VERSION_ID >= 70000
pcbc_match_phrase_search_query_fetch_object(zend_object * obj)33 static inline pcbc_match_phrase_search_query_t *pcbc_match_phrase_search_query_fetch_object(zend_object *obj)
34 {
35     return (pcbc_match_phrase_search_query_t *)((char *)obj - XtOffsetOf(pcbc_match_phrase_search_query_t, std));
36 }
37 #define Z_MATCH_PHRASE_SEARCH_QUERY_OBJ(zo) (pcbc_match_phrase_search_query_fetch_object(zo))
38 #define Z_MATCH_PHRASE_SEARCH_QUERY_OBJ_P(zv) (pcbc_match_phrase_search_query_fetch_object(Z_OBJ_P(zv)))
39 #else
40 #define Z_MATCH_PHRASE_SEARCH_QUERY_OBJ(zo) ((pcbc_match_phrase_search_query_t *)zo)
41 #define Z_MATCH_PHRASE_SEARCH_QUERY_OBJ_P(zv)                                                                          \
42     ((pcbc_match_phrase_search_query_t *)zend_object_store_get_object(zv TSRMLS_CC))
43 #endif
44 
45 #define LOGARGS(lvl) LCB_LOG_##lvl, NULL, "pcbc/match_phrase_search_query", __FILE__, __LINE__
46 
47 zend_class_entry *pcbc_match_phrase_search_query_ce;
48 
49 /* {{{ proto void MatchPhraseSearchQuery::__construct() */
PHP_METHOD(MatchPhraseSearchQuery,__construct)50 PHP_METHOD(MatchPhraseSearchQuery, __construct)
51 {
52     throw_pcbc_exception("Accessing private constructor.", LCB_EINVAL);
53 }
54 /* }}} */
55 
56 /* {{{ proto \Couchbase\MatchPhraseSearchQuery MatchPhraseSearchQuery::analyzer(string $analyzer)
57  */
PHP_METHOD(MatchPhraseSearchQuery,analyzer)58 PHP_METHOD(MatchPhraseSearchQuery, analyzer)
59 {
60     pcbc_match_phrase_search_query_t *obj;
61     char *analyzer = NULL;
62     int rv;
63     pcbc_str_arg_size analyzer_len;
64 
65     rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &analyzer, &analyzer_len);
66     if (rv == FAILURE) {
67         RETURN_NULL();
68     }
69 
70     obj = Z_MATCH_PHRASE_SEARCH_QUERY_OBJ_P(getThis());
71     if (obj->analyzer) {
72         efree(obj->analyzer);
73     }
74     obj->analyzer = estrndup(analyzer, analyzer_len);
75 
76     RETURN_ZVAL(getThis(), 1, 0);
77 } /* }}} */
78 
79 /* {{{ proto \Couchbase\MatchPhraseSearchQuery MatchPhraseSearchQuery::field(string $field)
80  */
PHP_METHOD(MatchPhraseSearchQuery,field)81 PHP_METHOD(MatchPhraseSearchQuery, field)
82 {
83     pcbc_match_phrase_search_query_t *obj;
84     char *field = NULL;
85     int rv;
86     pcbc_str_arg_size field_len;
87 
88     rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &field, &field_len);
89     if (rv == FAILURE) {
90         RETURN_NULL();
91     }
92 
93     obj = Z_MATCH_PHRASE_SEARCH_QUERY_OBJ_P(getThis());
94     if (obj->field) {
95         efree(obj->field);
96     }
97     obj->field = estrndup(field, field_len);
98 
99     RETURN_ZVAL(getThis(), 1, 0);
100 } /* }}} */
101 
102 /* {{{ proto \Couchbase\MatchPhraseSearchQuery MatchPhraseSearchQuery::boost(double $boost)
103  */
PHP_METHOD(MatchPhraseSearchQuery,boost)104 PHP_METHOD(MatchPhraseSearchQuery, boost)
105 {
106     pcbc_match_phrase_search_query_t *obj;
107     double boost = 0;
108     int rv;
109 
110     rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &boost);
111     if (rv == FAILURE) {
112         RETURN_NULL();
113     }
114 
115     obj = Z_MATCH_PHRASE_SEARCH_QUERY_OBJ_P(getThis());
116     obj->boost = boost;
117 
118     RETURN_ZVAL(getThis(), 1, 0);
119 } /* }}} */
120 
121 /* {{{ proto array MatchPhraseSearchQuery::jsonSerialize()
122  */
PHP_METHOD(MatchPhraseSearchQuery,jsonSerialize)123 PHP_METHOD(MatchPhraseSearchQuery, jsonSerialize)
124 {
125     pcbc_match_phrase_search_query_t *obj;
126     int rv;
127 
128     rv = zend_parse_parameters_none();
129     if (rv == FAILURE) {
130         RETURN_NULL();
131     }
132 
133     obj = Z_MATCH_PHRASE_SEARCH_QUERY_OBJ_P(getThis());
134     array_init(return_value);
135     ADD_ASSOC_STRING(return_value, "match_phrase", obj->match_phrase);
136     if (obj->field) {
137         ADD_ASSOC_STRING(return_value, "field", obj->field);
138     }
139     if (obj->analyzer) {
140         ADD_ASSOC_STRING(return_value, "analyzer", obj->analyzer);
141     }
142     if (obj->boost >= 0) {
143         ADD_ASSOC_DOUBLE_EX(return_value, "boost", obj->boost);
144     }
145 } /* }}} */
146 
147 ZEND_BEGIN_ARG_INFO_EX(ai_MatchPhraseSearchQuery_none, 0, 0, 0)
148 ZEND_END_ARG_INFO()
149 
150 ZEND_BEGIN_ARG_INFO_EX(ai_MatchPhraseSearchQuery_field, 0, 0, 1)
151 ZEND_ARG_INFO(0, field)
152 ZEND_END_ARG_INFO()
153 
154 ZEND_BEGIN_ARG_INFO_EX(ai_MatchPhraseSearchQuery_analyzer, 0, 0, 1)
155 ZEND_ARG_INFO(0, analyzer)
156 ZEND_END_ARG_INFO()
157 
158 ZEND_BEGIN_ARG_INFO_EX(ai_MatchPhraseSearchQuery_boost, 0, 0, 1)
159 ZEND_ARG_INFO(0, boost)
160 ZEND_END_ARG_INFO()
161 
162 // clang-format off
163 zend_function_entry match_phrase_search_query_methods[] = {
164     PHP_ME(MatchPhraseSearchQuery, __construct, ai_MatchPhraseSearchQuery_none, ZEND_ACC_PRIVATE | ZEND_ACC_FINAL | ZEND_ACC_CTOR)
165     PHP_ME(MatchPhraseSearchQuery, jsonSerialize, ai_MatchPhraseSearchQuery_none, ZEND_ACC_PUBLIC)
166     PHP_ME(MatchPhraseSearchQuery, boost, ai_MatchPhraseSearchQuery_boost, ZEND_ACC_PUBLIC)
167     PHP_ME(MatchPhraseSearchQuery, field, ai_MatchPhraseSearchQuery_field, ZEND_ACC_PUBLIC)
168     PHP_ME(MatchPhraseSearchQuery, analyzer, ai_MatchPhraseSearchQuery_analyzer, ZEND_ACC_PUBLIC)
169     PHP_FE_END
170 };
171 // clang-format on
172 
pcbc_match_phrase_search_query_init(zval * return_value,char * match_phrase,int match_phrase_len TSRMLS_DC)173 void pcbc_match_phrase_search_query_init(zval *return_value, char *match_phrase, int match_phrase_len TSRMLS_DC)
174 {
175     pcbc_match_phrase_search_query_t *obj;
176 
177     object_init_ex(return_value, pcbc_match_phrase_search_query_ce);
178     obj = Z_MATCH_PHRASE_SEARCH_QUERY_OBJ_P(return_value);
179     obj->boost = -1;
180     obj->match_phrase = estrndup(match_phrase, match_phrase_len);
181     obj->field = NULL;
182     obj->analyzer = NULL;
183 }
184 
185 zend_object_handlers match_phrase_search_query_handlers;
186 
match_phrase_search_query_free_object(pcbc_free_object_arg * object TSRMLS_DC)187 static void match_phrase_search_query_free_object(pcbc_free_object_arg *object TSRMLS_DC) /* {{{ */
188 {
189     pcbc_match_phrase_search_query_t *obj = Z_MATCH_PHRASE_SEARCH_QUERY_OBJ(object);
190 
191     if (obj->match_phrase != NULL) {
192         efree(obj->match_phrase);
193     }
194     if (obj->field != NULL) {
195         efree(obj->field);
196     }
197     if (obj->analyzer != NULL) {
198         efree(obj->analyzer);
199     }
200 
201     zend_object_std_dtor(&obj->std TSRMLS_CC);
202 #if PHP_VERSION_ID < 70000
203     efree(obj);
204 #endif
205 } /* }}} */
206 
match_phrase_search_query_create_object(zend_class_entry * class_type TSRMLS_DC)207 static pcbc_create_object_retval match_phrase_search_query_create_object(zend_class_entry *class_type TSRMLS_DC)
208 {
209     pcbc_match_phrase_search_query_t *obj = NULL;
210 
211     obj = PCBC_ALLOC_OBJECT_T(pcbc_match_phrase_search_query_t, class_type);
212 
213     zend_object_std_init(&obj->std, class_type TSRMLS_CC);
214     object_properties_init(&obj->std, class_type);
215 
216 #if PHP_VERSION_ID >= 70000
217     obj->std.handlers = &match_phrase_search_query_handlers;
218     return &obj->std;
219 #else
220     {
221         zend_object_value ret;
222         ret.handle = zend_objects_store_put(obj, (zend_objects_store_dtor_t)zend_objects_destroy_object,
223                                             match_phrase_search_query_free_object, NULL TSRMLS_CC);
224         ret.handlers = &match_phrase_search_query_handlers;
225         return ret;
226     }
227 #endif
228 }
229 
pcbc_match_phrase_search_query_get_debug_info(zval * object,int * is_temp TSRMLS_DC)230 static HashTable *pcbc_match_phrase_search_query_get_debug_info(zval *object, int *is_temp TSRMLS_DC) /* {{{ */
231 {
232     pcbc_match_phrase_search_query_t *obj = NULL;
233 #if PHP_VERSION_ID >= 70000
234     zval retval;
235 #else
236     zval retval = zval_used_for_init;
237 #endif
238 
239     *is_temp = 1;
240     obj = Z_MATCH_PHRASE_SEARCH_QUERY_OBJ_P(object);
241 
242     array_init(&retval);
243     ADD_ASSOC_STRING(&retval, "match_phrase", obj->match_phrase);
244     if (obj->field) {
245         ADD_ASSOC_STRING(&retval, "field", obj->field);
246     }
247     if (obj->analyzer) {
248         ADD_ASSOC_STRING(&retval, "analyzer", obj->analyzer);
249     }
250     if (obj->boost >= 0) {
251         ADD_ASSOC_DOUBLE_EX(&retval, "boost", obj->boost);
252     }
253     return Z_ARRVAL(retval);
254 } /* }}} */
255 
PHP_MINIT_FUNCTION(MatchPhraseSearchQuery)256 PHP_MINIT_FUNCTION(MatchPhraseSearchQuery)
257 {
258     zend_class_entry ce;
259 
260     INIT_NS_CLASS_ENTRY(ce, "Couchbase", "MatchPhraseSearchQuery", match_phrase_search_query_methods);
261     pcbc_match_phrase_search_query_ce = zend_register_internal_class(&ce TSRMLS_CC);
262     pcbc_match_phrase_search_query_ce->create_object = match_phrase_search_query_create_object;
263     PCBC_CE_DISABLE_SERIALIZATION(pcbc_match_phrase_search_query_ce);
264 
265     zend_class_implements(pcbc_match_phrase_search_query_ce TSRMLS_CC, 1, pcbc_json_serializable_ce);
266     zend_class_implements(pcbc_match_phrase_search_query_ce TSRMLS_CC, 1, pcbc_search_query_part_ce);
267 
268     memcpy(&match_phrase_search_query_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
269     match_phrase_search_query_handlers.get_debug_info = pcbc_match_phrase_search_query_get_debug_info;
270 #if PHP_VERSION_ID >= 70000
271     match_phrase_search_query_handlers.free_obj = match_phrase_search_query_free_object;
272     match_phrase_search_query_handlers.offset = XtOffsetOf(pcbc_match_phrase_search_query_t, std);
273 #endif
274 
275     zend_register_class_alias("\\CouchbaseMatchPhraseSearchQuery", pcbc_match_phrase_search_query_ce);
276     return SUCCESS;
277 }
278