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 #include "couchbase.h"
18 #include <Zend/zend_alloc.h>
19
20 #define LOGARGS(builder, lvl) LCB_LOG_##lvl, builder->bucket->conn->lcb, "pcbc/lookup_in_builder", __FILE__, __LINE__
21
22 zend_class_entry *pcbc_lookup_in_builder_ce;
23
24 /* {{{ proto void LookupInBuilder::__construct() Should not be called directly */
PHP_METHOD(LookupInBuilder,__construct)25 PHP_METHOD(LookupInBuilder, __construct)
26 {
27 throw_pcbc_exception("Accessing private constructor.", LCB_EINVAL);
28 }
29 /* }}} */
30
pcbc_lookup_in_builder_get(pcbc_lookup_in_builder_t * builder,char * path,int path_len,zval * options TSRMLS_DC)31 int pcbc_lookup_in_builder_get(pcbc_lookup_in_builder_t *builder, char *path, int path_len, zval *options TSRMLS_DC)
32 {
33 pcbc_sd_spec_t *spec;
34
35 spec = ecalloc(1, sizeof(pcbc_sd_spec_t));
36 spec->next = NULL;
37 if (path) {
38 spec->s.sdcmd = LCB_SDCMD_GET;
39 spec->s.options = pcbc_subdoc_options_to_flags(1, 1, options TSRMLS_CC);
40 PCBC_SDSPEC_COPY_PATH(spec, path, path_len);
41 } else {
42 spec->s.sdcmd = LCB_SDCMD_GET_FULLDOC;
43 }
44 if (builder->tail) {
45 builder->tail->next = spec;
46 }
47 builder->tail = spec;
48 if (builder->head == NULL) {
49 builder->head = builder->tail;
50 }
51 builder->nspecs++;
52 return SUCCESS;
53 }
54
55 /* {{{ proto \Couchbase\LookupInBuilder LookupInBuilder::get(string $path, array $options = []) */
PHP_METHOD(LookupInBuilder,get)56 PHP_METHOD(LookupInBuilder, get)
57 {
58 pcbc_lookup_in_builder_t *obj;
59 char *path = NULL;
60 pcbc_str_arg_size path_len = 0;
61 zval *options = NULL;
62 int rv;
63
64 rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|sz", &path, &path_len, &options);
65 if (rv == FAILURE) {
66 RETURN_NULL();
67 }
68
69 obj = Z_LOOKUP_IN_BUILDER_OBJ_P(getThis());
70 pcbc_lookup_in_builder_get(obj, path, path_len, options TSRMLS_CC);
71
72 RETURN_ZVAL(getThis(), 1, 0);
73 } /* }}} */
74
75 /* {{{ proto \Couchbase\LookupInBuilder LookupInBuilder::exists(string $path, array $options = []) */
PHP_METHOD(LookupInBuilder,exists)76 PHP_METHOD(LookupInBuilder, exists)
77 {
78 pcbc_lookup_in_builder_t *obj;
79 const char *path = NULL;
80 pcbc_str_arg_size path_len = 0;
81 int rv;
82 zval *options = NULL;
83 pcbc_sd_spec_t *spec;
84
85 obj = Z_LOOKUP_IN_BUILDER_OBJ_P(getThis());
86
87 rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|z", &path, &path_len, &options);
88 if (rv == FAILURE) {
89 RETURN_NULL();
90 }
91
92 spec = ecalloc(1, sizeof(pcbc_sd_spec_t));
93 spec->next = NULL;
94 spec->s.sdcmd = LCB_SDCMD_EXISTS;
95 spec->s.options = pcbc_subdoc_options_to_flags(1, 1, options TSRMLS_CC);
96 PCBC_SDSPEC_COPY_PATH(spec, path, path_len);
97 if (obj->tail) {
98 obj->tail->next = spec;
99 }
100 obj->tail = spec;
101 if (obj->head == NULL) {
102 obj->head = obj->tail;
103 }
104 obj->nspecs++;
105
106 RETURN_ZVAL(getThis(), 1, 0);
107 } /* }}} */
108
109 /* {{{ proto \Couchbase\LookupInBuilder LookupInBuilder::getCount(string $path, array $options = []) */
PHP_METHOD(LookupInBuilder,getCount)110 PHP_METHOD(LookupInBuilder, getCount)
111 {
112 pcbc_lookup_in_builder_t *obj;
113 const char *path = NULL;
114 pcbc_str_arg_size path_len = 0;
115 int rv;
116 zval *options = NULL;
117 pcbc_sd_spec_t *spec;
118
119 obj = Z_LOOKUP_IN_BUILDER_OBJ_P(getThis());
120
121 rv = zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|z", &path, &path_len, &options);
122 if (rv == FAILURE) {
123 RETURN_NULL();
124 }
125
126 spec = ecalloc(1, sizeof(pcbc_sd_spec_t));
127 spec->next = NULL;
128 spec->s.sdcmd = LCB_SDCMD_GET_COUNT;
129 spec->s.options = pcbc_subdoc_options_to_flags(1, 1, options TSRMLS_CC);
130 PCBC_SDSPEC_COPY_PATH(spec, path, path_len);
131 if (obj->tail) {
132 obj->tail->next = spec;
133 }
134 obj->tail = spec;
135 if (obj->head == NULL) {
136 obj->head = obj->tail;
137 }
138 obj->nspecs++;
139
140 RETURN_ZVAL(getThis(), 1, 0);
141 } /* }}} */
142
143 /* {{{ proto \Couchbase\LookupInBuilder LookupInBuilder::execute() */
PHP_METHOD(LookupInBuilder,execute)144 PHP_METHOD(LookupInBuilder, execute)
145 {
146 pcbc_lookup_in_builder_t *obj;
147 int rv;
148
149 obj = Z_LOOKUP_IN_BUILDER_OBJ_P(getThis());
150
151 rv = zend_parse_parameters_none();
152 if (rv == FAILURE) {
153 RETURN_NULL();
154 }
155
156 pcbc_bucket_subdoc_request(obj->bucket, obj, 1, return_value TSRMLS_CC);
157 } /* }}} */
158
159 ZEND_BEGIN_ARG_INFO_EX(ai_LookupInBuilder_none, 0, 0, 0)
160 ZEND_END_ARG_INFO()
161
162 ZEND_BEGIN_ARG_INFO_EX(ai_LookupInBuilder_get, 0, 0, 1)
163 ZEND_ARG_INFO(0, path)
164 ZEND_ARG_INFO(0, options)
165 ZEND_END_ARG_INFO()
166
167 // clang-format off
168 zend_function_entry lookup_in_builder_methods[] = {
169 PHP_ME(LookupInBuilder, __construct, ai_LookupInBuilder_none, ZEND_ACC_PRIVATE | ZEND_ACC_FINAL | ZEND_ACC_CTOR)
170 PHP_ME(LookupInBuilder, get, ai_LookupInBuilder_get, ZEND_ACC_PUBLIC)
171 PHP_ME(LookupInBuilder, getCount, ai_LookupInBuilder_get, ZEND_ACC_PUBLIC)
172 PHP_ME(LookupInBuilder, exists, ai_LookupInBuilder_get, ZEND_ACC_PUBLIC)
173 PHP_ME(LookupInBuilder, execute, ai_LookupInBuilder_none, ZEND_ACC_PUBLIC)
174 PHP_FE_END
175 };
176 // clang-format on
177
178 zend_object_handlers pcbc_lookup_in_builder_handlers;
179
pcbc_lookup_in_builder_init(zval * return_value,zval * bucket,const char * id,int id_len,zval * args,int num_args TSRMLS_DC)180 void pcbc_lookup_in_builder_init(zval *return_value, zval *bucket, const char *id, int id_len,
181 #if PHP_VERSION_ID >= 70000
182 zval *args,
183 #else
184 zval ***args,
185 #endif
186 int num_args TSRMLS_DC)
187 {
188 pcbc_lookup_in_builder_t *builder;
189
190 object_init_ex(return_value, pcbc_lookup_in_builder_ce);
191 builder = Z_LOOKUP_IN_BUILDER_OBJ_P(return_value);
192 #if PHP_VERSION_ID >= 70000
193 ZVAL_COPY(&builder->bucket_zval, bucket);
194 #else
195 Z_ADDREF_P(bucket);
196 builder->bucket_zval = bucket;
197 #endif
198 builder->bucket = Z_BUCKET_OBJ_P(bucket);
199 builder->id_len = id_len;
200 builder->id = estrndup(id, id_len);
201 builder->nspecs = 0;
202 builder->head = NULL;
203 builder->tail = NULL;
204 if (num_args && args) {
205 int i;
206 for (i = 0; i < num_args; ++i) {
207 PCBC_ZVAL *path;
208 #if PHP_VERSION_ID >= 70000
209 path = &args[i];
210 #else
211 path = args[i];
212 #endif
213 if (PCBC_P(*path) && Z_TYPE_P(PCBC_P(*path)) != IS_STRING) {
214 pcbc_log(LOGARGS(builder, WARN), "path has to be a string (skipping argument #%d)", i);
215 continue;
216 }
217 pcbc_lookup_in_builder_get(builder, Z_STRVAL_P(PCBC_P(*path)), Z_STRLEN_P(PCBC_P(*path)), NULL TSRMLS_CC);
218 }
219 }
220 }
221
lookup_in_builder_free_object(pcbc_free_object_arg * object TSRMLS_DC)222 static void lookup_in_builder_free_object(pcbc_free_object_arg *object TSRMLS_DC) /* {{{ */
223 {
224 pcbc_lookup_in_builder_t *obj = Z_LOOKUP_IN_BUILDER_OBJ(object);
225 pcbc_sd_spec_t *spec;
226
227 if (obj->id != NULL) {
228 efree(obj->id);
229 }
230 spec = obj->head;
231 while (spec) {
232 pcbc_sd_spec_t *tmp = spec;
233 spec = spec->next;
234 PCBC_SDSPEC_FREE_PATH(tmp);
235 efree(tmp);
236 }
237 obj->head = obj->tail = NULL;
238 Z_DELREF_P(PCBC_P(obj->bucket_zval));
239 ZVAL_UNDEF(PCBC_P(obj->bucket_zval));
240 obj->bucket = NULL;
241 zend_object_std_dtor(&obj->std TSRMLS_CC);
242 #if PHP_VERSION_ID < 70000
243 efree(obj);
244 #endif
245 } /* }}} */
246
lookup_in_builder_create_object(zend_class_entry * class_type TSRMLS_DC)247 static pcbc_create_object_retval lookup_in_builder_create_object(zend_class_entry *class_type TSRMLS_DC)
248 {
249 pcbc_lookup_in_builder_t *obj = NULL;
250
251 obj = PCBC_ALLOC_OBJECT_T(pcbc_lookup_in_builder_t, class_type);
252
253 zend_object_std_init(&obj->std, class_type TSRMLS_CC);
254 object_properties_init(&obj->std, class_type);
255
256 #if PHP_VERSION_ID >= 70000
257 obj->std.handlers = &pcbc_lookup_in_builder_handlers;
258 return &obj->std;
259 #else
260 {
261 zend_object_value ret;
262 ret.handle = zend_objects_store_put(obj, (zend_objects_store_dtor_t)zend_objects_destroy_object,
263 lookup_in_builder_free_object, NULL TSRMLS_CC);
264 ret.handlers = &pcbc_lookup_in_builder_handlers;
265 return ret;
266 }
267 #endif
268 }
269
lookup_in_builder_get_debug_info(zval * object,int * is_temp TSRMLS_DC)270 static HashTable *lookup_in_builder_get_debug_info(zval *object, int *is_temp TSRMLS_DC) /* {{{ */
271 {
272 pcbc_lookup_in_builder_t *obj = NULL;
273 #if PHP_VERSION_ID >= 70000
274 zval retval;
275 #else
276 zval retval = zval_used_for_init;
277 #endif
278 PCBC_ZVAL specs;
279 pcbc_sd_spec_t *spec;
280
281 *is_temp = 1;
282 obj = Z_LOOKUP_IN_BUILDER_OBJ_P(object);
283
284 array_init(&retval);
285 ADD_ASSOC_STRING(&retval, "id", obj->id);
286
287 PCBC_ZVAL_ALLOC(specs);
288 array_init_size(PCBC_P(specs), obj->nspecs);
289
290 spec = obj->head;
291 while (spec) {
292 PCBC_ZVAL s;
293 char *path = NULL;
294 int path_len = 0;
295 #if PHP_VERSION_ID < 70000
296 MAKE_STD_ZVAL(s);
297 #endif
298 array_init(PCBC_P(s));
299 switch (spec->s.sdcmd) {
300 case LCB_SDCMD_GET:
301 ADD_ASSOC_STRING(PCBC_P(s), "cmd", "get");
302 PCBC_SDSPEC_GET_PATH(spec, path, path_len);
303 ADD_ASSOC_STRINGL(PCBC_P(s), "path", path, path_len);
304 ADD_ASSOC_LONG_EX(PCBC_P(s), "options", spec->s.options);
305 break;
306 case LCB_SDCMD_EXISTS:
307 ADD_ASSOC_STRING(PCBC_P(s), "cmd", "exists");
308 PCBC_SDSPEC_GET_PATH(spec, path, path_len);
309 ADD_ASSOC_STRINGL(PCBC_P(s), "path", path, path_len);
310 ADD_ASSOC_LONG_EX(PCBC_P(s), "options", spec->s.options);
311 break;
312 }
313 add_next_index_zval(PCBC_P(specs), PCBC_P(s));
314 spec = spec->next;
315 }
316 add_assoc_zval(&retval, "specs", PCBC_P(specs));
317
318 return Z_ARRVAL(retval);
319 } /* }}} */
320
PHP_MINIT_FUNCTION(LookupInBuilder)321 PHP_MINIT_FUNCTION(LookupInBuilder)
322 {
323 zend_class_entry ce;
324
325 INIT_NS_CLASS_ENTRY(ce, "Couchbase", "LookupInBuilder", lookup_in_builder_methods);
326 pcbc_lookup_in_builder_ce = zend_register_internal_class(&ce TSRMLS_CC);
327 pcbc_lookup_in_builder_ce->create_object = lookup_in_builder_create_object;
328 PCBC_CE_DISABLE_SERIALIZATION(pcbc_lookup_in_builder_ce);
329
330 memcpy(&pcbc_lookup_in_builder_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
331 pcbc_lookup_in_builder_handlers.get_debug_info = lookup_in_builder_get_debug_info;
332 #if PHP_VERSION_ID >= 70000
333 pcbc_lookup_in_builder_handlers.free_obj = lookup_in_builder_free_object;
334 pcbc_lookup_in_builder_handlers.offset = XtOffsetOf(pcbc_lookup_in_builder_t, std);
335 #endif
336
337 zend_register_class_alias("\\CouchbaseLookupInBuilder", pcbc_lookup_in_builder_ce);
338 return SUCCESS;
339 }
340