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 on Couchbase document IDs. Useful to restrict the search space to a list of keys (by using
19  * this in a compound query).
20  */
21 #include "couchbase.h"
22 
23 typedef struct {
24     PCBC_ZEND_OBJECT_PRE
25     double boost;
26     char *field;
27     PCBC_ZVAL doc_ids;
28     PCBC_ZEND_OBJECT_POST
29 } pcbc_doc_id_search_query_t;
30 
31 #if PHP_VERSION_ID >= 70000
pcbc_doc_id_search_query_fetch_object(zend_object * obj)32 static inline pcbc_doc_id_search_query_t *pcbc_doc_id_search_query_fetch_object(zend_object *obj)
33 {
34     return (pcbc_doc_id_search_query_t *)((char *)obj - XtOffsetOf(pcbc_doc_id_search_query_t, std));
35 }
36 #define Z_DOC_ID_SEARCH_QUERY_OBJ(zo) (pcbc_doc_id_search_query_fetch_object(zo))
37 #define Z_DOC_ID_SEARCH_QUERY_OBJ_P(zv) (pcbc_doc_id_search_query_fetch_object(Z_OBJ_P(zv)))
38 #else
39 #define Z_DOC_ID_SEARCH_QUERY_OBJ(zo) ((pcbc_doc_id_search_query_t *)zo)
40 #define Z_DOC_ID_SEARCH_QUERY_OBJ_P(zv) ((pcbc_doc_id_search_query_t *)zend_object_store_get_object(zv TSRMLS_CC))
41 #endif
42 
43 #define LOGARGS(lvl) LCB_LOG_##lvl, NULL, "pcbc/doc_id_search_query", __FILE__, __LINE__
44 
45 zend_class_entry *pcbc_doc_id_search_query_ce;
46 
47 /* {{{ proto void DocIdSearchQuery::__construct() */
PHP_METHOD(DocIdSearchQuery,__construct)48 PHP_METHOD(DocIdSearchQuery, __construct)
49 {
50     throw_pcbc_exception("Accessing private constructor.", LCB_EINVAL);
51 }
52 /* }}} */
53 
54 /* {{{ proto \Couchbase\DocIdSearchQuery DocIdSearchQuery::field(string $field)
55  */
PHP_METHOD(DocIdSearchQuery,field)56 PHP_METHOD(DocIdSearchQuery, field)
57 {
58     pcbc_doc_id_search_query_t *obj;
59     char *field = NULL;
60     int rv;
61     pcbc_str_arg_size field_len;
62 
63     rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &field, &field_len);
64     if (rv == FAILURE) {
65         RETURN_NULL();
66     }
67 
68     obj = Z_DOC_ID_SEARCH_QUERY_OBJ_P(getThis());
69     if (obj->field) {
70         efree(obj->field);
71     }
72     obj->field = estrndup(field, field_len);
73 
74     RETURN_ZVAL(getThis(), 1, 0);
75 } /* }}} */
76 
77 /* {{{ proto \Couchbase\DocIdSearchQuery DocIdSearchQuery::boost(double $boost)
78  */
PHP_METHOD(DocIdSearchQuery,boost)79 PHP_METHOD(DocIdSearchQuery, boost)
80 {
81     pcbc_doc_id_search_query_t *obj;
82     double boost = 0;
83     int rv;
84 
85     rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &boost);
86     if (rv == FAILURE) {
87         RETURN_NULL();
88     }
89 
90     obj = Z_DOC_ID_SEARCH_QUERY_OBJ_P(getThis());
91     obj->boost = boost;
92 
93     RETURN_ZVAL(getThis(), 1, 0);
94 } /* }}} */
95 
96 /* {{{ proto DocIdSearchQuery DocIdSearchQuery::docIds(string ...$documentIds)
97  */
PHP_METHOD(DocIdSearchQuery,docIds)98 PHP_METHOD(DocIdSearchQuery, docIds)
99 {
100     pcbc_doc_id_search_query_t *obj;
101 #if PHP_VERSION_ID >= 70000
102     zval *args = NULL;
103 #else
104     zval ***args = NULL;
105 #endif
106     int num_args = 0;
107     int rv;
108 
109     rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "+", &args, &num_args);
110     if (rv == FAILURE) {
111         return;
112     }
113 
114     obj = Z_DOC_ID_SEARCH_QUERY_OBJ_P(getThis());
115 
116     if (num_args && args) {
117         int i;
118         for (i = 0; i < num_args; ++i) {
119             PCBC_ZVAL *id;
120 #if PHP_VERSION_ID >= 70000
121             id = &args[i];
122 #else
123             id = args[i];
124 #endif
125             if (Z_TYPE_P(PCBC_P(*id)) != IS_STRING) {
126                 pcbc_log(LOGARGS(WARN), "id has to be a string (skipping argument #%d)", i);
127                 continue;
128             }
129             add_next_index_zval(PCBC_P(obj->doc_ids), PCBC_P(*id));
130             PCBC_ADDREF_P(PCBC_P(*id));
131         }
132     }
133 #if PHP_VERSION_ID < 70000
134     if (args) {
135         efree(args);
136     }
137 #endif
138 
139     RETURN_ZVAL(getThis(), 1, 0);
140 } /* }}} */
141 
142 /* {{{ proto array DocIdSearchQuery::jsonSerialize()
143  */
PHP_METHOD(DocIdSearchQuery,jsonSerialize)144 PHP_METHOD(DocIdSearchQuery, jsonSerialize)
145 {
146     pcbc_doc_id_search_query_t *obj;
147     int rv;
148 
149     rv = zend_parse_parameters_none();
150     if (rv == FAILURE) {
151         RETURN_NULL();
152     }
153 
154     obj = Z_DOC_ID_SEARCH_QUERY_OBJ_P(getThis());
155     array_init(return_value);
156     ADD_ASSOC_ZVAL_EX(return_value, "ids", PCBC_P(obj->doc_ids));
157     PCBC_ADDREF_P(PCBC_P(obj->doc_ids));
158     if (obj->field) {
159         ADD_ASSOC_STRING(return_value, "field", obj->field);
160     }
161     if (obj->boost >= 0) {
162         ADD_ASSOC_DOUBLE_EX(return_value, "boost", obj->boost);
163     }
164 } /* }}} */
165 
166 ZEND_BEGIN_ARG_INFO_EX(ai_DocIdSearchQuery_none, 0, 0, 0)
167 ZEND_END_ARG_INFO()
168 
169 ZEND_BEGIN_ARG_INFO_EX(ai_DocIdSearchQuery_field, 0, 0, 1)
170 ZEND_ARG_INFO(0, field)
171 ZEND_END_ARG_INFO()
172 
173 ZEND_BEGIN_ARG_INFO_EX(ai_DocIdSearchQuery_boost, 0, 0, 1)
174 ZEND_ARG_INFO(0, boost)
175 ZEND_END_ARG_INFO()
176 
177 ZEND_BEGIN_ARG_INFO_EX(ai_DocIdSearchQuery_docIds, 0, 0, 1)
178 PCBC_ARG_VARIADIC_INFO(0, documentIds)
179 ZEND_END_ARG_INFO()
180 
181 // clang-format off
182 zend_function_entry doc_id_search_query_methods[] = {
183     PHP_ME(DocIdSearchQuery, __construct, ai_DocIdSearchQuery_none, ZEND_ACC_PRIVATE | ZEND_ACC_FINAL | ZEND_ACC_CTOR)
184     PHP_ME(DocIdSearchQuery, jsonSerialize, ai_DocIdSearchQuery_none, ZEND_ACC_PUBLIC)
185     PHP_ME(DocIdSearchQuery, boost, ai_DocIdSearchQuery_boost, ZEND_ACC_PUBLIC)
186     PHP_ME(DocIdSearchQuery, field, ai_DocIdSearchQuery_field, ZEND_ACC_PUBLIC)
187     PHP_ME(DocIdSearchQuery, docIds, ai_DocIdSearchQuery_docIds, ZEND_ACC_PUBLIC)
188     PHP_FE_END
189 };
190 // clang-format on
191 
pcbc_doc_id_search_query_init(zval * return_value,zval * args,int num_args TSRMLS_DC)192 void pcbc_doc_id_search_query_init(zval *return_value,
193 #if PHP_VERSION_ID >= 70000
194                                    zval *args,
195 #else
196                                    zval ***args,
197 #endif
198                                    int num_args TSRMLS_DC)
199 {
200     pcbc_doc_id_search_query_t *obj;
201 
202     object_init_ex(return_value, pcbc_doc_id_search_query_ce);
203     obj = Z_DOC_ID_SEARCH_QUERY_OBJ_P(return_value);
204     obj->boost = -1;
205     obj->field = NULL;
206 
207     PCBC_ZVAL_ALLOC(obj->doc_ids);
208     array_init(PCBC_P(obj->doc_ids));
209 
210     if (num_args && args) {
211         int i;
212         for (i = 0; i < num_args; ++i) {
213             PCBC_ZVAL *id;
214 #if PHP_VERSION_ID >= 70000
215             id = &args[i];
216 #else
217             id = args[i];
218 #endif
219             if (Z_TYPE_P(PCBC_P(*id)) != IS_STRING) {
220                 pcbc_log(LOGARGS(WARN), "id has to be a string (skipping argument #%d)", i);
221                 continue;
222             }
223             add_next_index_zval(PCBC_P(obj->doc_ids), PCBC_P(*id));
224             PCBC_ADDREF_P(PCBC_P(*id));
225         }
226     }
227 }
228 
229 zend_object_handlers doc_id_search_query_handlers;
230 
doc_id_search_query_free_object(pcbc_free_object_arg * object TSRMLS_DC)231 static void doc_id_search_query_free_object(pcbc_free_object_arg *object TSRMLS_DC) /* {{{ */
232 {
233     pcbc_doc_id_search_query_t *obj = Z_DOC_ID_SEARCH_QUERY_OBJ(object);
234 
235     if (obj->field != NULL) {
236         efree(obj->field);
237     }
238     zval_ptr_dtor(&obj->doc_ids);
239 
240     zend_object_std_dtor(&obj->std TSRMLS_CC);
241 #if PHP_VERSION_ID < 70000
242     efree(obj);
243 #endif
244 } /* }}} */
245 
doc_id_search_query_create_object(zend_class_entry * class_type TSRMLS_DC)246 static pcbc_create_object_retval doc_id_search_query_create_object(zend_class_entry *class_type TSRMLS_DC)
247 {
248     pcbc_doc_id_search_query_t *obj = NULL;
249 
250     obj = PCBC_ALLOC_OBJECT_T(pcbc_doc_id_search_query_t, class_type);
251 
252     zend_object_std_init(&obj->std, class_type TSRMLS_CC);
253     object_properties_init(&obj->std, class_type);
254 
255 #if PHP_VERSION_ID >= 70000
256     obj->std.handlers = &doc_id_search_query_handlers;
257     return &obj->std;
258 #else
259     {
260         zend_object_value ret;
261         ret.handle = zend_objects_store_put(obj, (zend_objects_store_dtor_t)zend_objects_destroy_object,
262                                             doc_id_search_query_free_object, NULL TSRMLS_CC);
263         ret.handlers = &doc_id_search_query_handlers;
264         return ret;
265     }
266 #endif
267 }
268 
pcbc_doc_id_search_query_get_debug_info(zval * object,int * is_temp TSRMLS_DC)269 static HashTable *pcbc_doc_id_search_query_get_debug_info(zval *object, int *is_temp TSRMLS_DC) /* {{{ */
270 {
271     pcbc_doc_id_search_query_t *obj = NULL;
272 #if PHP_VERSION_ID >= 70000
273     zval retval;
274 #else
275     zval retval = zval_used_for_init;
276 #endif
277 
278     *is_temp = 1;
279     obj = Z_DOC_ID_SEARCH_QUERY_OBJ_P(object);
280 
281     array_init(&retval);
282     ADD_ASSOC_ZVAL_EX(&retval, "ids", PCBC_P(obj->doc_ids));
283     PCBC_ADDREF_P(PCBC_P(obj->doc_ids));
284     if (obj->field) {
285         ADD_ASSOC_STRING(&retval, "field", obj->field);
286     }
287     if (obj->boost >= 0) {
288         ADD_ASSOC_DOUBLE_EX(&retval, "boost", obj->boost);
289     }
290     return Z_ARRVAL(retval);
291 } /* }}} */
292 
PHP_MINIT_FUNCTION(DocIdSearchQuery)293 PHP_MINIT_FUNCTION(DocIdSearchQuery)
294 {
295     zend_class_entry ce;
296 
297     INIT_NS_CLASS_ENTRY(ce, "Couchbase", "DocIdSearchQuery", doc_id_search_query_methods);
298     pcbc_doc_id_search_query_ce = zend_register_internal_class(&ce TSRMLS_CC);
299     pcbc_doc_id_search_query_ce->create_object = doc_id_search_query_create_object;
300     PCBC_CE_DISABLE_SERIALIZATION(pcbc_doc_id_search_query_ce);
301 
302     zend_class_implements(pcbc_doc_id_search_query_ce TSRMLS_CC, 1, pcbc_json_serializable_ce);
303     zend_class_implements(pcbc_doc_id_search_query_ce TSRMLS_CC, 1, pcbc_search_query_part_ce);
304 
305     memcpy(&doc_id_search_query_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
306     doc_id_search_query_handlers.get_debug_info = pcbc_doc_id_search_query_get_debug_info;
307 #if PHP_VERSION_ID >= 70000
308     doc_id_search_query_handlers.free_obj = doc_id_search_query_free_object;
309     doc_id_search_query_handlers.offset = XtOffsetOf(pcbc_doc_id_search_query_t, std);
310 #endif
311 
312     zend_register_class_alias("\\CouchbaseDocIdSearchQuery", pcbc_doc_id_search_query_ce);
313     return SUCCESS;
314 }
315