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 a given term, applying further processing to it
19  * like analyzers, stemming and even #fuzziness(int).
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;
29     int prefix_length;
30     int fuzziness;
31     PCBC_ZEND_OBJECT_POST
32 } pcbc_match_search_query_t;
33 
34 #if PHP_VERSION_ID >= 70000
pcbc_match_search_query_fetch_object(zend_object * obj)35 static inline pcbc_match_search_query_t *pcbc_match_search_query_fetch_object(zend_object *obj)
36 {
37     return (pcbc_match_search_query_t *)((char *)obj - XtOffsetOf(pcbc_match_search_query_t, std));
38 }
39 #define Z_MATCH_SEARCH_QUERY_OBJ(zo) (pcbc_match_search_query_fetch_object(zo))
40 #define Z_MATCH_SEARCH_QUERY_OBJ_P(zv) (pcbc_match_search_query_fetch_object(Z_OBJ_P(zv)))
41 #else
42 #define Z_MATCH_SEARCH_QUERY_OBJ(zo) ((pcbc_match_search_query_t *)zo)
43 #define Z_MATCH_SEARCH_QUERY_OBJ_P(zv) ((pcbc_match_search_query_t *)zend_object_store_get_object(zv TSRMLS_CC))
44 #endif
45 
46 #define LOGARGS(lvl) LCB_LOG_##lvl, NULL, "pcbc/match_search_query", __FILE__, __LINE__
47 
48 zend_class_entry *pcbc_match_search_query_ce;
49 
50 /* {{{ proto void MatchSearchQuery::__construct() */
PHP_METHOD(MatchSearchQuery,__construct)51 PHP_METHOD(MatchSearchQuery, __construct)
52 {
53     throw_pcbc_exception("Accessing private constructor.", LCB_EINVAL);
54 }
55 /* }}} */
56 
57 /* {{{ proto \Couchbase\MatchSearchQuery MatchSearchQuery::analyzer(string $analyzer)
58  */
PHP_METHOD(MatchSearchQuery,analyzer)59 PHP_METHOD(MatchSearchQuery, analyzer)
60 {
61     pcbc_match_search_query_t *obj;
62     char *analyzer = NULL;
63     int rv;
64     pcbc_str_arg_size analyzer_len;
65 
66     rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &analyzer, &analyzer_len);
67     if (rv == FAILURE) {
68         RETURN_NULL();
69     }
70 
71     obj = Z_MATCH_SEARCH_QUERY_OBJ_P(getThis());
72     if (obj->analyzer) {
73         efree(obj->analyzer);
74     }
75     obj->analyzer = estrndup(analyzer, analyzer_len);
76 
77     RETURN_ZVAL(getThis(), 1, 0);
78 } /* }}} */
79 
80 /* {{{ proto \Couchbase\MatchSearchQuery MatchSearchQuery::field(string $field)
81  */
PHP_METHOD(MatchSearchQuery,field)82 PHP_METHOD(MatchSearchQuery, field)
83 {
84     pcbc_match_search_query_t *obj;
85     char *field = NULL;
86     int rv;
87     pcbc_str_arg_size field_len;
88 
89     rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &field, &field_len);
90     if (rv == FAILURE) {
91         RETURN_NULL();
92     }
93 
94     obj = Z_MATCH_SEARCH_QUERY_OBJ_P(getThis());
95     if (obj->field) {
96         efree(obj->field);
97     }
98     obj->field = estrndup(field, field_len);
99 
100     RETURN_ZVAL(getThis(), 1, 0);
101 } /* }}} */
102 
103 /* {{{ proto \Couchbase\MatchSearchQuery MatchSearchQuery::prefixLength(int $prefixLength)
104  */
PHP_METHOD(MatchSearchQuery,prefixLength)105 PHP_METHOD(MatchSearchQuery, prefixLength)
106 {
107     pcbc_match_search_query_t *obj;
108     long prefix_length = 0;
109     int rv;
110 
111     rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &prefix_length);
112     if (rv == FAILURE) {
113         RETURN_NULL();
114     }
115 
116     obj = Z_MATCH_SEARCH_QUERY_OBJ_P(getThis());
117     obj->prefix_length = prefix_length;
118 
119     RETURN_ZVAL(getThis(), 1, 0);
120 } /* }}} */
121 
122 /* {{{ proto \Couchbase\MatchSearchQuery MatchSearchQuery::fuzziness(int $fuzziness)
123  */
PHP_METHOD(MatchSearchQuery,fuzziness)124 PHP_METHOD(MatchSearchQuery, fuzziness)
125 {
126     pcbc_match_search_query_t *obj;
127     long fuzziness = 0;
128     int rv;
129 
130     rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &fuzziness);
131     if (rv == FAILURE) {
132         RETURN_NULL();
133     }
134 
135     obj = Z_MATCH_SEARCH_QUERY_OBJ_P(getThis());
136     obj->fuzziness = fuzziness;
137 
138     RETURN_ZVAL(getThis(), 1, 0);
139 } /* }}} */
140 
141 /* {{{ proto \Couchbase\MatchSearchQuery MatchSearchQuery::boost(double $boost)
142  */
PHP_METHOD(MatchSearchQuery,boost)143 PHP_METHOD(MatchSearchQuery, boost)
144 {
145     pcbc_match_search_query_t *obj;
146     double boost = 0;
147     int rv;
148 
149     rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &boost);
150     if (rv == FAILURE) {
151         RETURN_NULL();
152     }
153 
154     obj = Z_MATCH_SEARCH_QUERY_OBJ_P(getThis());
155     obj->boost = boost;
156 
157     RETURN_ZVAL(getThis(), 1, 0);
158 } /* }}} */
159 
160 /* {{{ proto array MatchSearchQuery::jsonSerialize()
161  */
PHP_METHOD(MatchSearchQuery,jsonSerialize)162 PHP_METHOD(MatchSearchQuery, jsonSerialize)
163 {
164     pcbc_match_search_query_t *obj;
165     int rv;
166 
167     rv = zend_parse_parameters_none();
168     if (rv == FAILURE) {
169         RETURN_NULL();
170     }
171 
172     obj = Z_MATCH_SEARCH_QUERY_OBJ_P(getThis());
173     array_init(return_value);
174     ADD_ASSOC_STRING(return_value, "match", obj->match);
175     if (obj->field) {
176         ADD_ASSOC_STRING(return_value, "field", obj->field);
177     }
178     if (obj->analyzer) {
179         ADD_ASSOC_STRING(return_value, "analyzer", obj->analyzer);
180     }
181     if (obj->prefix_length >= 0) {
182         ADD_ASSOC_LONG_EX(return_value, "prefix_length", obj->prefix_length);
183     }
184     if (obj->fuzziness >= 0) {
185         ADD_ASSOC_LONG_EX(return_value, "fuzziness", obj->fuzziness);
186     }
187     if (obj->boost >= 0) {
188         ADD_ASSOC_DOUBLE_EX(return_value, "boost", obj->boost);
189     }
190 } /* }}} */
191 
192 ZEND_BEGIN_ARG_INFO_EX(ai_MatchSearchQuery_none, 0, 0, 0)
193 ZEND_END_ARG_INFO()
194 
195 ZEND_BEGIN_ARG_INFO_EX(ai_MatchSearchQuery_field, 0, 0, 1)
196 ZEND_ARG_INFO(0, field)
197 ZEND_END_ARG_INFO()
198 
199 ZEND_BEGIN_ARG_INFO_EX(ai_MatchSearchQuery_analyzer, 0, 0, 1)
200 ZEND_ARG_INFO(0, analyzer)
201 ZEND_END_ARG_INFO()
202 
203 ZEND_BEGIN_ARG_INFO_EX(ai_MatchSearchQuery_prefixLength, 0, 0, 1)
204 ZEND_ARG_INFO(0, prefixLength)
205 ZEND_END_ARG_INFO()
206 
207 ZEND_BEGIN_ARG_INFO_EX(ai_MatchSearchQuery_fuzziness, 0, 0, 1)
208 ZEND_ARG_INFO(0, fuzziness)
209 ZEND_END_ARG_INFO()
210 
211 ZEND_BEGIN_ARG_INFO_EX(ai_MatchSearchQuery_boost, 0, 0, 1)
212 ZEND_ARG_INFO(0, boost)
213 ZEND_END_ARG_INFO()
214 
215 // clang-format off
216 zend_function_entry match_search_query_methods[] = {
217     PHP_ME(MatchSearchQuery, __construct, ai_MatchSearchQuery_none, ZEND_ACC_PRIVATE | ZEND_ACC_FINAL | ZEND_ACC_CTOR)
218     PHP_ME(MatchSearchQuery, jsonSerialize, ai_MatchSearchQuery_none, ZEND_ACC_PUBLIC)
219     PHP_ME(MatchSearchQuery, boost, ai_MatchSearchQuery_boost, ZEND_ACC_PUBLIC)
220     PHP_ME(MatchSearchQuery, field, ai_MatchSearchQuery_field, ZEND_ACC_PUBLIC)
221     PHP_ME(MatchSearchQuery, analyzer, ai_MatchSearchQuery_analyzer, ZEND_ACC_PUBLIC)
222     PHP_ME(MatchSearchQuery, prefixLength, ai_MatchSearchQuery_prefixLength, ZEND_ACC_PUBLIC)
223     PHP_ME(MatchSearchQuery, fuzziness, ai_MatchSearchQuery_fuzziness, ZEND_ACC_PUBLIC)
224     PHP_FE_END
225 };
226 // clang-format on
227 
pcbc_match_search_query_init(zval * return_value,char * match,int match_len TSRMLS_DC)228 void pcbc_match_search_query_init(zval *return_value, char *match, int match_len TSRMLS_DC)
229 {
230     pcbc_match_search_query_t *obj;
231 
232     object_init_ex(return_value, pcbc_match_search_query_ce);
233     obj = Z_MATCH_SEARCH_QUERY_OBJ_P(return_value);
234     obj->boost = -1;
235     obj->match = estrndup(match, match_len);
236     obj->field = NULL;
237     obj->analyzer = NULL;
238     obj->prefix_length = -1;
239     obj->fuzziness = -1;
240 }
241 
242 zend_object_handlers match_search_query_handlers;
243 
match_search_query_free_object(pcbc_free_object_arg * object TSRMLS_DC)244 static void match_search_query_free_object(pcbc_free_object_arg *object TSRMLS_DC) /* {{{ */
245 {
246     pcbc_match_search_query_t *obj = Z_MATCH_SEARCH_QUERY_OBJ(object);
247 
248     if (obj->match != NULL) {
249         efree(obj->match);
250     }
251     if (obj->field != NULL) {
252         efree(obj->field);
253     }
254     if (obj->analyzer != NULL) {
255         efree(obj->analyzer);
256     }
257 
258     zend_object_std_dtor(&obj->std TSRMLS_CC);
259 #if PHP_VERSION_ID < 70000
260     efree(obj);
261 #endif
262 } /* }}} */
263 
match_search_query_create_object(zend_class_entry * class_type TSRMLS_DC)264 static pcbc_create_object_retval match_search_query_create_object(zend_class_entry *class_type TSRMLS_DC)
265 {
266     pcbc_match_search_query_t *obj = NULL;
267 
268     obj = PCBC_ALLOC_OBJECT_T(pcbc_match_search_query_t, class_type);
269 
270     zend_object_std_init(&obj->std, class_type TSRMLS_CC);
271     object_properties_init(&obj->std, class_type);
272 
273 #if PHP_VERSION_ID >= 70000
274     obj->std.handlers = &match_search_query_handlers;
275     return &obj->std;
276 #else
277     {
278         zend_object_value ret;
279         ret.handle = zend_objects_store_put(obj, (zend_objects_store_dtor_t)zend_objects_destroy_object,
280                                             match_search_query_free_object, NULL TSRMLS_CC);
281         ret.handlers = &match_search_query_handlers;
282         return ret;
283     }
284 #endif
285 }
286 
pcbc_match_search_query_get_debug_info(zval * object,int * is_temp TSRMLS_DC)287 static HashTable *pcbc_match_search_query_get_debug_info(zval *object, int *is_temp TSRMLS_DC) /* {{{ */
288 {
289     pcbc_match_search_query_t *obj = NULL;
290 #if PHP_VERSION_ID >= 70000
291     zval retval;
292 #else
293     zval retval = zval_used_for_init;
294 #endif
295 
296     *is_temp = 1;
297     obj = Z_MATCH_SEARCH_QUERY_OBJ_P(object);
298 
299     array_init(&retval);
300     ADD_ASSOC_STRING(&retval, "match", obj->match);
301     if (obj->field) {
302         ADD_ASSOC_STRING(&retval, "field", obj->field);
303     }
304     if (obj->analyzer) {
305         ADD_ASSOC_STRING(&retval, "analyzer", obj->analyzer);
306     }
307     if (obj->prefix_length >= 0) {
308         ADD_ASSOC_LONG_EX(&retval, "prefix_length", obj->prefix_length);
309     }
310     if (obj->fuzziness >= 0) {
311         ADD_ASSOC_LONG_EX(&retval, "fuzziness", obj->fuzziness);
312     }
313     if (obj->boost >= 0) {
314         ADD_ASSOC_DOUBLE_EX(&retval, "boost", obj->boost);
315     }
316     return Z_ARRVAL(retval);
317 } /* }}} */
318 
PHP_MINIT_FUNCTION(MatchSearchQuery)319 PHP_MINIT_FUNCTION(MatchSearchQuery)
320 {
321     zend_class_entry ce;
322 
323     INIT_NS_CLASS_ENTRY(ce, "Couchbase", "MatchSearchQuery", match_search_query_methods);
324     pcbc_match_search_query_ce = zend_register_internal_class(&ce TSRMLS_CC);
325     pcbc_match_search_query_ce->create_object = match_search_query_create_object;
326     PCBC_CE_DISABLE_SERIALIZATION(pcbc_match_search_query_ce);
327 
328     zend_class_implements(pcbc_match_search_query_ce TSRMLS_CC, 1, pcbc_json_serializable_ce);
329     zend_class_implements(pcbc_match_search_query_ce TSRMLS_CC, 1, pcbc_search_query_part_ce);
330 
331     memcpy(&match_search_query_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
332     match_search_query_handlers.get_debug_info = pcbc_match_search_query_get_debug_info;
333 #if PHP_VERSION_ID >= 70000
334     match_search_query_handlers.free_obj = match_search_query_free_object;
335     match_search_query_handlers.offset = XtOffsetOf(pcbc_match_search_query_t, std);
336 #endif
337 
338     zend_register_class_alias("\\CouchbaseMatchSearchQuery", pcbc_match_search_query_ce);
339     return SUCCESS;
340 }
341