1 /**
2 * Copyright 2015-2017 DataStax, 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 "php_driver.h"
18 #include "php_driver_types.h"
19 #include "util/collections.h"
20 #include "util/hash.h"
21 #include "util/types.h"
22 #include "src/Collection.h"
23
24 zend_class_entry *php_driver_collection_ce = NULL;
25
26 void
php_driver_collection_add(php_driver_collection * collection,zval * object TSRMLS_DC)27 php_driver_collection_add(php_driver_collection *collection, zval *object TSRMLS_DC)
28 {
29 PHP5TO7_ZEND_HASH_NEXT_INDEX_INSERT(&collection->values, object, sizeof(zval *));
30 Z_TRY_ADDREF_P(object);
31 collection->dirty = 1;
32 }
33
34 static int
php_driver_collection_del(php_driver_collection * collection,ulong index)35 php_driver_collection_del(php_driver_collection *collection, ulong index)
36 {
37 if (zend_hash_index_del(&collection->values, index) == SUCCESS) {
38 collection->dirty = 1;
39 return 1;
40 }
41
42 return 0;
43 }
44
45 static int
php_driver_collection_get(php_driver_collection * collection,ulong index,php5to7_zval * zvalue)46 php_driver_collection_get(php_driver_collection *collection, ulong index, php5to7_zval *zvalue)
47 {
48 php5to7_zval *value;
49 if (PHP5TO7_ZEND_HASH_INDEX_FIND(&collection->values, index, value)) {
50 *zvalue = *value;
51 return 1;
52 }
53 return 0;
54 }
55
56 static int
php_driver_collection_find(php_driver_collection * collection,zval * object,long * index TSRMLS_DC)57 php_driver_collection_find(php_driver_collection *collection, zval *object, long *index TSRMLS_DC)
58 {
59 php5to7_ulong num_key;
60 php5to7_zval *current;
61 PHP5TO7_ZEND_HASH_FOREACH_NUM_KEY_VAL(&collection->values, num_key, current) {
62 zval compare;
63 is_equal_function(&compare, object, PHP5TO7_ZVAL_MAYBE_DEREF(current) TSRMLS_CC);
64 if (PHP5TO7_ZVAL_IS_TRUE_P(&compare)) {
65 *index = (long) num_key;
66 return 1;
67 }
68 } PHP5TO7_ZEND_HASH_FOREACH_END(&collection->values);
69
70 return 0;
71 }
72
73 static void
php_driver_collection_populate(php_driver_collection * collection,zval * array)74 php_driver_collection_populate(php_driver_collection *collection, zval *array)
75 {
76 php5to7_zval *current;
77 PHP5TO7_ZEND_HASH_FOREACH_VAL(&collection->values, current) {
78 if (add_next_index_zval(array, PHP5TO7_ZVAL_MAYBE_DEREF(current)) == SUCCESS)
79 Z_TRY_ADDREF_P(PHP5TO7_ZVAL_MAYBE_DEREF(current));
80 else
81 break;
82 } PHP5TO7_ZEND_HASH_FOREACH_END(&collection->values);
83 }
84
85 /* {{{ Collection::__construct(type) */
PHP_METHOD(Collection,__construct)86 PHP_METHOD(Collection, __construct)
87 {
88 php_driver_collection *self;
89 zval *type;
90
91 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &type) == FAILURE)
92 return;
93
94 self = PHP_DRIVER_GET_COLLECTION(getThis());
95
96 if (Z_TYPE_P(type) == IS_STRING) {
97 CassValueType value_type;
98 if (!php_driver_value_type(Z_STRVAL_P(type), &value_type TSRMLS_CC))
99 return;
100 self->type = php_driver_type_collection_from_value_type(value_type TSRMLS_CC);
101 } else if (Z_TYPE_P(type) == IS_OBJECT &&
102 instanceof_function(Z_OBJCE_P(type), php_driver_type_ce TSRMLS_CC)) {
103 if (!php_driver_type_validate(type, "type" TSRMLS_CC)) {
104 return;
105 }
106 self->type = php_driver_type_collection(type TSRMLS_CC);
107 Z_ADDREF_P(type);
108 } else {
109 INVALID_ARGUMENT(type, "a string or an instance of " PHP_DRIVER_NAMESPACE "\\Type");
110 }
111 }
112 /* }}} */
113
114 /* {{{ Collection::type() */
PHP_METHOD(Collection,type)115 PHP_METHOD(Collection, type)
116 {
117 php_driver_collection *self = PHP_DRIVER_GET_COLLECTION(getThis());
118 RETURN_ZVAL(PHP5TO7_ZVAL_MAYBE_P(self->type), 1, 0);
119 }
120
121 /* {{{ Collection::values() */
PHP_METHOD(Collection,values)122 PHP_METHOD(Collection, values)
123 {
124 php_driver_collection *collection = NULL;
125 array_init(return_value);
126 collection = PHP_DRIVER_GET_COLLECTION(getThis());
127 php_driver_collection_populate(collection, return_value);
128 }
129 /* }}} */
130
131 /* {{{ Collection::add(mixed) */
PHP_METHOD(Collection,add)132 PHP_METHOD(Collection, add)
133 {
134 php_driver_collection *self = NULL;
135 php5to7_zval_args args = NULL;
136 int argc = 0, i;
137 php_driver_type *type;
138
139 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "+", &args, &argc) == FAILURE)
140 return;
141
142 self = PHP_DRIVER_GET_COLLECTION(getThis());
143 type = PHP_DRIVER_GET_TYPE(PHP5TO7_ZVAL_MAYBE_P(self->type));
144
145 for (i = 0; i < argc; i++) {
146 if (Z_TYPE_P(PHP5TO7_ZVAL_ARG(args[i])) == IS_NULL) {
147 PHP5TO7_MAYBE_EFREE(args);
148 zend_throw_exception_ex(php_driver_invalid_argument_exception_ce, 0 TSRMLS_CC,
149 "Invalid value: null is not supported inside collections");
150 RETURN_FALSE;
151 }
152
153 if (!php_driver_validate_object(PHP5TO7_ZVAL_ARG(args[i]),
154 PHP5TO7_ZVAL_MAYBE_P(type->data.collection.value_type) TSRMLS_CC)) {
155 PHP5TO7_MAYBE_EFREE(args);
156 RETURN_FALSE;
157 }
158 }
159
160 for (i = 0; i < argc; i++) {
161 php_driver_collection_add(self, PHP5TO7_ZVAL_ARG(args[i]) TSRMLS_CC);
162 }
163
164 PHP5TO7_MAYBE_EFREE(args);
165 RETVAL_LONG(zend_hash_num_elements(&self->values));
166 }
167 /* }}} */
168
169 /* {{{ Collection::get(int) */
PHP_METHOD(Collection,get)170 PHP_METHOD(Collection, get)
171 {
172 long key;
173 php_driver_collection *self = NULL;
174 php5to7_zval value;
175
176 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &key) == FAILURE)
177 return;
178
179 self = PHP_DRIVER_GET_COLLECTION(getThis());
180
181 if (php_driver_collection_get(self, (ulong) key, &value))
182 RETURN_ZVAL(PHP5TO7_ZVAL_MAYBE_P(value), 1, 0);
183 }
184 /* }}} */
185
186 /* {{{ Collection::find(mixed) */
PHP_METHOD(Collection,find)187 PHP_METHOD(Collection, find)
188 {
189 zval *object;
190 php_driver_collection *collection = NULL;
191 long index;
192
193 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &object) == FAILURE)
194 return;
195
196 collection = PHP_DRIVER_GET_COLLECTION(getThis());
197
198 if (php_driver_collection_find(collection, object, &index TSRMLS_CC))
199 RETURN_LONG(index);
200 }
201 /* }}} */
202
203 /* {{{ Collection::count() */
PHP_METHOD(Collection,count)204 PHP_METHOD(Collection, count)
205 {
206 php_driver_collection *collection = PHP_DRIVER_GET_COLLECTION(getThis());
207 RETURN_LONG(zend_hash_num_elements(&collection->values));
208 }
209 /* }}} */
210
211 /* {{{ Collection::current() */
PHP_METHOD(Collection,current)212 PHP_METHOD(Collection, current)
213 {
214 php5to7_zval *current;
215 php_driver_collection *collection = PHP_DRIVER_GET_COLLECTION(getThis());
216
217 if (PHP5TO7_ZEND_HASH_GET_CURRENT_DATA(&collection->values, current)) {
218 RETURN_ZVAL(PHP5TO7_ZVAL_MAYBE_DEREF(current), 1, 0);
219 }
220 }
221 /* }}} */
222
223 /* {{{ Collection::key() */
PHP_METHOD(Collection,key)224 PHP_METHOD(Collection, key)
225 {
226 php5to7_ulong num_key;
227 php_driver_collection *collection = PHP_DRIVER_GET_COLLECTION(getThis());
228 if (PHP5TO7_ZEND_HASH_GET_CURRENT_KEY(&collection->values, NULL, &num_key) == HASH_KEY_IS_LONG) {
229 RETURN_LONG(num_key);
230 }
231 }
232 /* }}} */
233
234 /* {{{ Collection::next() */
PHP_METHOD(Collection,next)235 PHP_METHOD(Collection, next)
236 {
237 php_driver_collection *collection = PHP_DRIVER_GET_COLLECTION(getThis());
238 zend_hash_move_forward(&collection->values);
239 }
240 /* }}} */
241
242 /* {{{ Collection::valid() */
PHP_METHOD(Collection,valid)243 PHP_METHOD(Collection, valid)
244 {
245 php_driver_collection *collection = PHP_DRIVER_GET_COLLECTION(getThis());
246 RETURN_BOOL(zend_hash_has_more_elements(&collection->values) == SUCCESS);
247 }
248 /* }}} */
249
250 /* {{{ Collection::rewind() */
PHP_METHOD(Collection,rewind)251 PHP_METHOD(Collection, rewind)
252 {
253 php_driver_collection *collection = PHP_DRIVER_GET_COLLECTION(getThis());
254 zend_hash_internal_pointer_reset(&collection->values);
255 }
256 /* }}} */
257
258 /* {{{ Collection::remove(key) */
PHP_METHOD(Collection,remove)259 PHP_METHOD(Collection, remove)
260 {
261 long index;
262 php_driver_collection *collection = NULL;
263
264 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &index) == FAILURE) {
265 return;
266 }
267
268 collection = PHP_DRIVER_GET_COLLECTION(getThis());
269
270 if (php_driver_collection_del(collection, (ulong) index)) {
271 RETURN_TRUE;
272 }
273
274 RETURN_FALSE;
275 }
276 /* }}} */
277
278 ZEND_BEGIN_ARG_INFO_EX(arginfo__construct, 0, ZEND_RETURN_VALUE, 1)
279 ZEND_ARG_INFO(0, type)
280 ZEND_END_ARG_INFO()
281
282 ZEND_BEGIN_ARG_INFO_EX(arginfo_value, 0, ZEND_RETURN_VALUE, 1)
283 ZEND_ARG_INFO(0, value)
284 ZEND_END_ARG_INFO()
285
286 ZEND_BEGIN_ARG_INFO_EX(arginfo_index, 0, ZEND_RETURN_VALUE, 1)
287 ZEND_ARG_INFO(0, index)
288 ZEND_END_ARG_INFO()
289
290 ZEND_BEGIN_ARG_INFO_EX(arginfo_none, 0, ZEND_RETURN_VALUE, 0)
291 ZEND_END_ARG_INFO()
292
293 static zend_function_entry php_driver_collection_methods[] = {
294 PHP_ME(Collection, __construct, arginfo__construct, ZEND_ACC_CTOR|ZEND_ACC_PUBLIC)
295 PHP_ME(Collection, type, arginfo_none, ZEND_ACC_PUBLIC)
296 PHP_ME(Collection, values, arginfo_none, ZEND_ACC_PUBLIC)
297 PHP_ME(Collection, add, arginfo_value, ZEND_ACC_PUBLIC)
298 PHP_ME(Collection, get, arginfo_index, ZEND_ACC_PUBLIC)
299 PHP_ME(Collection, find, arginfo_value, ZEND_ACC_PUBLIC)
300 /* Countable */
301 PHP_ME(Collection, count, arginfo_none, ZEND_ACC_PUBLIC)
302 /* Iterator */
303 PHP_ME(Collection, current, arginfo_none, ZEND_ACC_PUBLIC)
304 PHP_ME(Collection, key, arginfo_none, ZEND_ACC_PUBLIC)
305 PHP_ME(Collection, next, arginfo_none, ZEND_ACC_PUBLIC)
306 PHP_ME(Collection, valid, arginfo_none, ZEND_ACC_PUBLIC)
307 PHP_ME(Collection, rewind, arginfo_none, ZEND_ACC_PUBLIC)
308 PHP_ME(Collection, remove, arginfo_index, ZEND_ACC_PUBLIC)
309 PHP_FE_END
310 };
311
312 static php_driver_value_handlers php_driver_collection_handlers;
313
314 static HashTable *
php_driver_collection_gc(zval * object,php5to7_zval_gc table,int * n TSRMLS_DC)315 php_driver_collection_gc(zval *object, php5to7_zval_gc table, int *n TSRMLS_DC)
316 {
317 *table = NULL;
318 *n = 0;
319 return zend_std_get_properties(object TSRMLS_CC);
320 }
321
322 static HashTable *
php_driver_collection_properties(zval * object TSRMLS_DC)323 php_driver_collection_properties(zval *object TSRMLS_DC)
324 {
325 php5to7_zval values;
326
327 php_driver_collection *self = PHP_DRIVER_GET_COLLECTION(object);
328 HashTable *props = zend_std_get_properties(object TSRMLS_CC);
329
330 PHP5TO7_ZEND_HASH_UPDATE(props,
331 "type", sizeof("type"),
332 PHP5TO7_ZVAL_MAYBE_P(self->type), sizeof(zval));
333 Z_ADDREF_P(PHP5TO7_ZVAL_MAYBE_P(self->type));
334
335 PHP5TO7_ZVAL_MAYBE_MAKE(values);
336 array_init(PHP5TO7_ZVAL_MAYBE_P(values));
337 php_driver_collection_populate(self, PHP5TO7_ZVAL_MAYBE_P(values));
338 PHP5TO7_ZEND_HASH_UPDATE(props, "values", sizeof("values"), PHP5TO7_ZVAL_MAYBE_P(values), sizeof(zval));
339
340 return props;
341 }
342
343 static int
php_driver_collection_compare(zval * obj1,zval * obj2 TSRMLS_DC)344 php_driver_collection_compare(zval *obj1, zval *obj2 TSRMLS_DC)
345 {
346 HashPosition pos1;
347 HashPosition pos2;
348 php5to7_zval *current1;
349 php5to7_zval *current2;
350 php_driver_collection *collection1;
351 php_driver_collection *collection2;
352 php_driver_type *type1;
353 php_driver_type *type2;
354 int result;
355
356 if (Z_OBJCE_P(obj1) != Z_OBJCE_P(obj2))
357 return 1; /* different classes */
358
359 collection1 = PHP_DRIVER_GET_COLLECTION(obj1);
360 collection2 = PHP_DRIVER_GET_COLLECTION(obj2);
361
362 type1 = PHP_DRIVER_GET_TYPE(PHP5TO7_ZVAL_MAYBE_P(collection1->type));
363 type2 = PHP_DRIVER_GET_TYPE(PHP5TO7_ZVAL_MAYBE_P(collection2->type));
364
365 result = php_driver_type_compare(type1, type2 TSRMLS_CC);
366 if (result != 0) return result;
367
368 if (zend_hash_num_elements(&collection1->values) != zend_hash_num_elements(&collection2->values)) {
369 return zend_hash_num_elements(&collection1->values) < zend_hash_num_elements(&collection2->values) ? -1 : 1;
370 }
371
372 zend_hash_internal_pointer_reset_ex(&collection1->values, &pos1);
373 zend_hash_internal_pointer_reset_ex(&collection2->values, &pos2);
374
375 while (PHP5TO7_ZEND_HASH_GET_CURRENT_DATA_EX(&collection1->values, current1, &pos1) &&
376 PHP5TO7_ZEND_HASH_GET_CURRENT_DATA_EX(&collection2->values, current2, &pos2)) {
377 result = php_driver_value_compare(PHP5TO7_ZVAL_MAYBE_DEREF(current1),
378 PHP5TO7_ZVAL_MAYBE_DEREF(current2) TSRMLS_CC);
379 if (result != 0) return result;
380 zend_hash_move_forward_ex(&collection1->values, &pos1);
381 zend_hash_move_forward_ex(&collection2->values, &pos2);
382 }
383
384 return 0;
385 }
386
387 static unsigned
php_driver_collection_hash_value(zval * obj TSRMLS_DC)388 php_driver_collection_hash_value(zval *obj TSRMLS_DC)
389 {
390 php5to7_zval *current;
391 unsigned hashv = 0;
392 php_driver_collection *self = PHP_DRIVER_GET_COLLECTION(obj);
393
394 if (!self->dirty) return self->hashv;
395
396 PHP5TO7_ZEND_HASH_FOREACH_VAL(&self->values, current) {
397 hashv = php_driver_combine_hash(hashv,
398 php_driver_value_hash(PHP5TO7_ZVAL_MAYBE_DEREF(current) TSRMLS_CC));
399 } PHP5TO7_ZEND_HASH_FOREACH_END(&self->values);
400
401 self->hashv = hashv;
402 self->dirty = 0;
403
404 return hashv;
405 }
406
407 static void
php_driver_collection_free(php5to7_zend_object_free * object TSRMLS_DC)408 php_driver_collection_free(php5to7_zend_object_free *object TSRMLS_DC)
409 {
410 php_driver_collection *self =
411 PHP5TO7_ZEND_OBJECT_GET(collection, object);
412
413 zend_hash_destroy(&self->values);
414 PHP5TO7_ZVAL_MAYBE_DESTROY(self->type);
415
416 zend_object_std_dtor(&self->zval TSRMLS_CC);
417 PHP5TO7_MAYBE_EFREE(self);
418 }
419
420 static php5to7_zend_object
php_driver_collection_new(zend_class_entry * ce TSRMLS_DC)421 php_driver_collection_new(zend_class_entry *ce TSRMLS_DC)
422 {
423 php_driver_collection *self =
424 PHP5TO7_ZEND_OBJECT_ECALLOC(collection, ce);
425
426 zend_hash_init(&self->values, 0, NULL, ZVAL_PTR_DTOR, 0);
427 self->dirty = 1;
428 PHP5TO7_ZVAL_UNDEF(self->type);
429
430 PHP5TO7_ZEND_OBJECT_INIT(collection, self, ce);
431 }
432
php_driver_define_Collection(TSRMLS_D)433 void php_driver_define_Collection(TSRMLS_D)
434 {
435 zend_class_entry ce;
436
437 INIT_CLASS_ENTRY(ce, PHP_DRIVER_NAMESPACE "\\Collection", php_driver_collection_methods);
438 php_driver_collection_ce = zend_register_internal_class(&ce TSRMLS_CC);
439 zend_class_implements(php_driver_collection_ce TSRMLS_CC, 1, php_driver_value_ce);
440 memcpy(&php_driver_collection_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
441 php_driver_collection_handlers.std.get_properties = php_driver_collection_properties;
442 #if PHP_VERSION_ID >= 50400
443 php_driver_collection_handlers.std.get_gc = php_driver_collection_gc;
444 #endif
445 php_driver_collection_handlers.std.compare_objects = php_driver_collection_compare;
446 php_driver_collection_ce->ce_flags |= PHP5TO7_ZEND_ACC_FINAL;
447 php_driver_collection_ce->create_object = php_driver_collection_new;
448 zend_class_implements(php_driver_collection_ce TSRMLS_CC, 2, spl_ce_Countable, zend_ce_iterator);
449
450 php_driver_collection_handlers.hash_value = php_driver_collection_hash_value;
451 php_driver_collection_handlers.std.clone_obj = NULL;
452 }
453