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