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 facet that categorizes hits into numerical ranges (or buckets) provided by the user.
19  */
20 #include "couchbase.h"
21 
22 typedef struct {
23     PCBC_ZEND_OBJECT_PRE
24     double boost;
25     char *field;
26     int limit;
27     PCBC_ZVAL ranges; /* array or arrays like ["range-name" => ["min" => ..., "max" => ...], ...] */
28     PCBC_ZEND_OBJECT_POST
29 } pcbc_numeric_range_search_facet_t;
30 
31 #if PHP_VERSION_ID >= 70000
pcbc_numeric_range_search_facet_fetch_object(zend_object * obj)32 static inline pcbc_numeric_range_search_facet_t *pcbc_numeric_range_search_facet_fetch_object(zend_object *obj)
33 {
34     return (pcbc_numeric_range_search_facet_t *)((char *)obj - XtOffsetOf(pcbc_numeric_range_search_facet_t, std));
35 }
36 #define Z_NUMERIC_RANGE_SEARCH_FACET_OBJ(zo) (pcbc_numeric_range_search_facet_fetch_object(zo))
37 #define Z_NUMERIC_RANGE_SEARCH_FACET_OBJ_P(zv) (pcbc_numeric_range_search_facet_fetch_object(Z_OBJ_P(zv)))
38 #else
39 #define Z_NUMERIC_RANGE_SEARCH_FACET_OBJ(zo) ((pcbc_numeric_range_search_facet_t *)zo)
40 #define Z_NUMERIC_RANGE_SEARCH_FACET_OBJ_P(zv)                                                                         \
41     ((pcbc_numeric_range_search_facet_t *)zend_object_store_get_object(zv TSRMLS_CC))
42 #endif
43 
44 #define LOGARGS(lvl) LCB_LOG_##lvl, NULL, "pcbc/numeric_search_facet", __FILE__, __LINE__
45 
46 zend_class_entry *pcbc_numeric_range_search_facet_ce;
47 
48 /* {{{ proto void NumericRangeSearchFacet::__construct() */
PHP_METHOD(NumericRangeSearchFacet,__construct)49 PHP_METHOD(NumericRangeSearchFacet, __construct)
50 {
51     throw_pcbc_exception("Accessing private constructor.", LCB_EINVAL);
52 }
53 /* }}} */
54 
55 /* {{{ proto \Couchbase\NumericRangeSearchFacet NumericRangeSearchFacet::addRange(string $name, double $min, double
56  * $max)
57  */
PHP_METHOD(NumericRangeSearchFacet,addRange)58 PHP_METHOD(NumericRangeSearchFacet, addRange)
59 {
60     pcbc_numeric_range_search_facet_t *obj;
61     char *name = NULL;
62     PCBC_ZVAL range;
63     int rv;
64     pcbc_str_arg_size name_len = 0;
65     double min = 0, max = 0;
66     zend_bool min_null = 0, max_null = 0;
67 
68 #if PHP_VERSION_ID >= 50600
69     rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sd!d!", &name, &name_len, &min, &min_null, &max, &max_null);
70 #else
71     zval *zmin = NULL, *zmax = NULL;
72     rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "szz", &name, &name_len, &min, &max);
73     if (zmin) {
74         min = Z_DVAL_P(zmin);
75     } else {
76         min_null = 1;
77     }
78     if (zmax) {
79         min = Z_DVAL_P(zmax);
80     } else {
81         min_null = 1;
82     }
83 #endif
84     if (rv == FAILURE) {
85         RETURN_NULL();
86     }
87 
88     obj = Z_NUMERIC_RANGE_SEARCH_FACET_OBJ_P(getThis());
89 
90     PCBC_ZVAL_ALLOC(range);
91     array_init_size(PCBC_P(range), 3);
92     ADD_ASSOC_STRINGL(PCBC_P(range), "name", name, name_len);
93     if (!min_null) {
94         ADD_ASSOC_DOUBLE_EX(PCBC_P(range), "min", min);
95     }
96     if (!max_null) {
97         ADD_ASSOC_DOUBLE_EX(PCBC_P(range), "max", max);
98     }
99     add_next_index_zval(PCBC_P(obj->ranges), PCBC_P(range));
100 
101     RETURN_ZVAL(getThis(), 1, 0);
102 } /* }}} */
103 
104 /* {{{ proto array NumericRangeSearchFacet::jsonSerialize()
105  */
PHP_METHOD(NumericRangeSearchFacet,jsonSerialize)106 PHP_METHOD(NumericRangeSearchFacet, jsonSerialize)
107 {
108     pcbc_numeric_range_search_facet_t *obj;
109     int rv;
110 
111     rv = zend_parse_parameters_none();
112     if (rv == FAILURE) {
113         RETURN_NULL();
114     }
115 
116     obj = Z_NUMERIC_RANGE_SEARCH_FACET_OBJ_P(getThis());
117     array_init(return_value);
118     ADD_ASSOC_STRING(return_value, "field", obj->field);
119     ADD_ASSOC_LONG_EX(return_value, "size", obj->limit);
120     ADD_ASSOC_ZVAL_EX(return_value, "numeric_ranges", PCBC_P(obj->ranges));
121     PCBC_ADDREF_P(PCBC_P(obj->ranges));
122 } /* }}} */
123 
124 ZEND_BEGIN_ARG_INFO_EX(ai_NumericRangeSearchFacet_none, 0, 0, 0)
125 ZEND_END_ARG_INFO()
126 
127 ZEND_BEGIN_ARG_INFO_EX(ai_NumericRangeSearchFacet_addRange, 0, 0, 3)
128 ZEND_ARG_INFO(0, name)
129 ZEND_ARG_INFO(0, min)
130 ZEND_ARG_INFO(0, max)
131 ZEND_END_ARG_INFO()
132 
133 // clang-format off
134 zend_function_entry numeric_search_facet_methods[] = {
135     PHP_ME(NumericRangeSearchFacet, __construct, ai_NumericRangeSearchFacet_none, ZEND_ACC_PRIVATE | ZEND_ACC_FINAL | ZEND_ACC_CTOR)
136     PHP_ME(NumericRangeSearchFacet, jsonSerialize, ai_NumericRangeSearchFacet_none, ZEND_ACC_PUBLIC)
137     PHP_ME(NumericRangeSearchFacet, addRange, ai_NumericRangeSearchFacet_addRange, ZEND_ACC_PUBLIC)
138     PHP_FE_END
139 };
140 // clang-format on
141 
pcbc_numeric_range_search_facet_init(zval * return_value,char * field,int field_len,int limit TSRMLS_DC)142 void pcbc_numeric_range_search_facet_init(zval *return_value, char *field, int field_len, int limit TSRMLS_DC)
143 {
144     pcbc_numeric_range_search_facet_t *obj;
145 
146     object_init_ex(return_value, pcbc_numeric_range_search_facet_ce);
147     obj = Z_NUMERIC_RANGE_SEARCH_FACET_OBJ_P(return_value);
148     obj->field = estrndup(field, field_len);
149     obj->limit = limit;
150 
151     PCBC_ZVAL_ALLOC(obj->ranges);
152     array_init(PCBC_P(obj->ranges));
153 }
154 
155 zend_object_handlers numeric_search_facet_handlers;
156 
numeric_search_facet_free_object(pcbc_free_object_arg * object TSRMLS_DC)157 static void numeric_search_facet_free_object(pcbc_free_object_arg *object TSRMLS_DC) /* {{{ */
158 {
159     pcbc_numeric_range_search_facet_t *obj = Z_NUMERIC_RANGE_SEARCH_FACET_OBJ(object);
160 
161     if (obj->field != NULL) {
162         efree(obj->field);
163     }
164     zval_ptr_dtor(&obj->ranges);
165 
166     zend_object_std_dtor(&obj->std TSRMLS_CC);
167 #if PHP_VERSION_ID < 70000
168     efree(obj);
169 #endif
170 } /* }}} */
171 
numeric_search_facet_create_object(zend_class_entry * class_type TSRMLS_DC)172 static pcbc_create_object_retval numeric_search_facet_create_object(zend_class_entry *class_type TSRMLS_DC)
173 {
174     pcbc_numeric_range_search_facet_t *obj = NULL;
175 
176     obj = PCBC_ALLOC_OBJECT_T(pcbc_numeric_range_search_facet_t, class_type);
177 
178     zend_object_std_init(&obj->std, class_type TSRMLS_CC);
179     object_properties_init(&obj->std, class_type);
180 
181 #if PHP_VERSION_ID >= 70000
182     obj->std.handlers = &numeric_search_facet_handlers;
183     return &obj->std;
184 #else
185     {
186         zend_object_value ret;
187         ret.handle = zend_objects_store_put(obj, (zend_objects_store_dtor_t)zend_objects_destroy_object,
188                                             numeric_search_facet_free_object, NULL TSRMLS_CC);
189         ret.handlers = &numeric_search_facet_handlers;
190         return ret;
191     }
192 #endif
193 }
194 
pcbc_numeric_range_search_facet_get_debug_info(zval * object,int * is_temp TSRMLS_DC)195 static HashTable *pcbc_numeric_range_search_facet_get_debug_info(zval *object, int *is_temp TSRMLS_DC) /* {{{ */
196 {
197     pcbc_numeric_range_search_facet_t *obj = NULL;
198 #if PHP_VERSION_ID >= 70000
199     zval retval;
200 #else
201     zval retval = zval_used_for_init;
202 #endif
203 
204     *is_temp = 1;
205     obj = Z_NUMERIC_RANGE_SEARCH_FACET_OBJ_P(object);
206 
207     array_init(&retval);
208     ADD_ASSOC_STRING(&retval, "field", obj->field);
209     ADD_ASSOC_LONG_EX(&retval, "limit", obj->limit);
210     ADD_ASSOC_ZVAL_EX(&retval, "numeric_ranges", PCBC_P(obj->ranges));
211     PCBC_ADDREF_P(PCBC_P(obj->ranges));
212     return Z_ARRVAL(retval);
213 } /* }}} */
214 
PHP_MINIT_FUNCTION(NumericRangeSearchFacet)215 PHP_MINIT_FUNCTION(NumericRangeSearchFacet)
216 {
217     zend_class_entry ce;
218 
219     INIT_NS_CLASS_ENTRY(ce, "Couchbase", "NumericRangeSearchFacet", numeric_search_facet_methods);
220     pcbc_numeric_range_search_facet_ce = zend_register_internal_class(&ce TSRMLS_CC);
221     pcbc_numeric_range_search_facet_ce->create_object = numeric_search_facet_create_object;
222     PCBC_CE_DISABLE_SERIALIZATION(pcbc_numeric_range_search_facet_ce);
223 
224     zend_class_implements(pcbc_numeric_range_search_facet_ce TSRMLS_CC, 1, pcbc_json_serializable_ce);
225     zend_class_implements(pcbc_numeric_range_search_facet_ce TSRMLS_CC, 1, pcbc_search_facet_ce);
226 
227     memcpy(&numeric_search_facet_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
228     numeric_search_facet_handlers.get_debug_info = pcbc_numeric_range_search_facet_get_debug_info;
229 #if PHP_VERSION_ID >= 70000
230     numeric_search_facet_handlers.free_obj = numeric_search_facet_free_object;
231     numeric_search_facet_handlers.offset = XtOffsetOf(pcbc_numeric_range_search_facet_t, std);
232 #endif
233 
234     zend_register_class_alias("\\CouchbaseNumericRangeSearchFacet", pcbc_numeric_range_search_facet_ce);
235     return SUCCESS;
236 }
237