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/types.h"
20 #include "src/UserTypeValue.h"
21 #include "util/collections.h"
22 
23 #if PHP_MAJOR_VERSION >= 7
24 #include <zend_smart_str.h>
25 #else
26 #include <ext/standard/php_smart_str.h>
27 #endif
28 
29 zend_class_entry *php_driver_type_user_type_ce = NULL;
30 
php_driver_type_user_type_add(php_driver_type * type,const char * name,size_t name_length,zval * zsub_type TSRMLS_DC)31 int php_driver_type_user_type_add(php_driver_type *type,
32                                      const char *name, size_t name_length,
33                                      zval *zsub_type TSRMLS_DC)
34 {
35   php_driver_type *sub_type = PHP_DRIVER_GET_TYPE(zsub_type);
36   if (cass_data_type_add_sub_type_by_name_n(type->data_type,
37                                             name, name_length,
38                                             sub_type->data_type) != CASS_OK) {
39     return 0;
40   }
41   PHP5TO7_ZEND_HASH_ADD(&type->data.udt.types,
42                         name, name_length + 1,
43                         zsub_type, sizeof(zval *));
44   return 1;
45 }
46 
PHP_METHOD(TypeUserType,__construct)47 PHP_METHOD(TypeUserType, __construct)
48 {
49   zend_throw_exception_ex(php_driver_logic_exception_ce, 0 TSRMLS_CC,
50     "Instantiation of a " PHP_DRIVER_NAMESPACE "\\Type\\UserType type is not supported."
51   );
52   return;
53 }
54 
PHP_METHOD(TypeUserType,withName)55 PHP_METHOD(TypeUserType, withName)
56 {
57   char *name;
58   php5to7_size name_len;
59   php_driver_type *self;
60   php_driver_type *user_type;
61 
62   if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &name, &name_len) == FAILURE) {
63     return;
64   }
65 
66   self = PHP_DRIVER_GET_TYPE(getThis());
67 
68   object_init_ex(return_value, php_driver_type_user_type_ce);
69   user_type = PHP_DRIVER_GET_TYPE(return_value);
70   user_type->data_type = cass_data_type_new_from_existing(self->data_type);
71 
72   user_type->data.udt.type_name = estrndup(name, name_len);
73 
74   if (self->data.udt.keyspace) {
75     user_type->data.udt.keyspace = estrdup(self->data.udt.keyspace);
76   }
77 
78   PHP5TO7_ZEND_HASH_ZVAL_COPY(&user_type->data.udt.types, &self->data.udt.types);
79 }
80 
PHP_METHOD(TypeUserType,name)81 PHP_METHOD(TypeUserType, name)
82 {
83   php_driver_type *self;
84 
85   if (zend_parse_parameters_none() == FAILURE) {
86     return;
87   }
88 
89   self = PHP_DRIVER_GET_TYPE(getThis());
90 
91   if (!self->data.udt.type_name)
92     RETURN_NULL();
93 
94   PHP5TO7_RETVAL_STRING(self->data.udt.type_name);
95 }
96 
PHP_METHOD(TypeUserType,withKeyspace)97 PHP_METHOD(TypeUserType, withKeyspace)
98 {
99   char *keyspace;
100   php5to7_size keyspace_len;
101   php_driver_type *self;
102   php_driver_type *user_type;
103 
104   if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &keyspace, &keyspace_len) == FAILURE) {
105     return;
106   }
107 
108   self = PHP_DRIVER_GET_TYPE(getThis());
109 
110   object_init_ex(return_value, php_driver_type_user_type_ce);
111   user_type = PHP_DRIVER_GET_TYPE(return_value);
112   user_type->data_type = cass_data_type_new_from_existing(self->data_type);
113 
114   if (self->data.udt.type_name) {
115     user_type->data.udt.type_name = estrdup(self->data.udt.type_name);
116   }
117 
118   user_type->data.udt.keyspace = estrndup(keyspace, keyspace_len);
119 
120   PHP5TO7_ZEND_HASH_ZVAL_COPY(&user_type->data.udt.types, &self->data.udt.types);
121 }
122 
PHP_METHOD(TypeUserType,keyspace)123 PHP_METHOD(TypeUserType, keyspace)
124 {
125   php_driver_type *self;
126 
127   if (zend_parse_parameters_none() == FAILURE) {
128     return;
129   }
130 
131   self = PHP_DRIVER_GET_TYPE(getThis());
132 
133   if (!self->data.udt.keyspace)
134     RETURN_NULL();
135 
136   PHP5TO7_RETVAL_STRING(self->data.udt.keyspace);
137 }
138 
PHP_METHOD(TypeUserType,types)139 PHP_METHOD(TypeUserType, types)
140 {
141   php_driver_type *self;
142 
143   if (zend_parse_parameters_none() == FAILURE) {
144     return;
145   }
146 
147   self = PHP_DRIVER_GET_TYPE(getThis());
148 
149   array_init(return_value);
150   PHP5TO7_ZEND_HASH_ZVAL_COPY(Z_ARRVAL_P(return_value), &self->data.udt.types);
151 }
152 
PHP_METHOD(TypeUserType,__toString)153 PHP_METHOD(TypeUserType, __toString)
154 {
155   php_driver_type *self;
156   smart_str string = PHP5TO7_SMART_STR_INIT;
157 
158   if (zend_parse_parameters_none() == FAILURE) {
159     return;
160   }
161 
162   self = PHP_DRIVER_GET_TYPE(getThis());
163 
164   php_driver_type_string(self, &string TSRMLS_CC);
165   smart_str_0(&string);
166 
167   PHP5TO7_RETVAL_STRING(PHP5TO7_SMART_STR_VAL(string));
168   smart_str_free(&string);
169 }
170 
PHP_METHOD(TypeUserType,create)171 PHP_METHOD(TypeUserType, create)
172 {
173   php_driver_type *self;
174   php_driver_user_type_value *user_type_value;
175   php5to7_zval_args args = NULL;
176   int argc = 0, i;
177 
178   if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "*",
179                             &args, &argc) == FAILURE) {
180     return;
181   }
182 
183   self = PHP_DRIVER_GET_TYPE(getThis());
184 
185   object_init_ex(return_value, php_driver_user_type_value_ce);
186   user_type_value = PHP_DRIVER_GET_USER_TYPE_VALUE(return_value);
187 
188   PHP5TO7_ZVAL_COPY(PHP5TO7_ZVAL_MAYBE_P(user_type_value->type), getThis());
189 
190   if (argc > 0) {
191     if (argc % 2 == 1) {
192       zend_throw_exception_ex(php_driver_invalid_argument_exception_ce, 0 TSRMLS_CC,
193                               "Not enough name/value pairs, user_types can only be created " \
194                               "from an even number of name/value pairs, where each odd " \
195                               "argument is a name and each even argument is a value, " \
196                               "e.g user_type(name, value, name, value, name, value)");
197       PHP5TO7_MAYBE_EFREE(args);
198       return;
199     }
200 
201     for (i = 0; i < argc; i += 2) {
202       zval *name = PHP5TO7_ZVAL_ARG(args[i]);
203       zval *value = PHP5TO7_ZVAL_ARG(args[i + 1]);
204       php5to7_zval *sub_type;
205       if (Z_TYPE_P(name) != IS_STRING) {
206         zend_throw_exception_ex(php_driver_invalid_argument_exception_ce, 0 TSRMLS_CC,
207                                 "Argument %d is not a string", i + 1);
208         PHP5TO7_MAYBE_EFREE(args);
209         return;
210       }
211       if (!PHP5TO7_ZEND_HASH_FIND(&self->data.udt.types,
212                                   Z_STRVAL_P(name), Z_STRLEN_P(name) + 1,
213                                   sub_type)) {
214         zend_throw_exception_ex(php_driver_invalid_argument_exception_ce,
215                                 0 TSRMLS_CC,
216                                 "Invalid name '%s'", Z_STRVAL_P(name));
217         PHP5TO7_MAYBE_EFREE(args);
218         return;
219       }
220       if (!php_driver_validate_object(value,
221                                       PHP5TO7_ZVAL_MAYBE_DEREF(sub_type) TSRMLS_CC)) {
222         PHP5TO7_MAYBE_EFREE(args);
223         return;
224       }
225       php_driver_user_type_value_set(user_type_value,
226                                      Z_STRVAL_P(name), Z_STRLEN_P(name),
227                                      value TSRMLS_CC);
228     }
229 
230     PHP5TO7_MAYBE_EFREE(args);
231   }
232 }
233 
234 ZEND_BEGIN_ARG_INFO_EX(arginfo_none, 0, ZEND_RETURN_VALUE, 0)
235 ZEND_END_ARG_INFO()
236 
237 ZEND_BEGIN_ARG_INFO_EX(arginfo_value, 0, ZEND_RETURN_VALUE, 0)
238   ZEND_ARG_INFO(0, value)
239 ZEND_END_ARG_INFO()
240 
241 ZEND_BEGIN_ARG_INFO_EX(arginfo_name, 0, ZEND_RETURN_VALUE, 1)
242   ZEND_ARG_INFO(0, name)
243 ZEND_END_ARG_INFO()
244 
245 ZEND_BEGIN_ARG_INFO_EX(arginfo_keyspace, 0, ZEND_RETURN_VALUE, 1)
246   ZEND_ARG_INFO(0, keyspace)
247 ZEND_END_ARG_INFO()
248 
249 static zend_function_entry php_driver_type_user_type_methods[] = {
250   PHP_ME(TypeUserType, __construct,  arginfo_none,     ZEND_ACC_PRIVATE)
251   PHP_ME(TypeUserType, withName,     arginfo_name,     ZEND_ACC_PUBLIC)
252   PHP_ME(TypeUserType, name,         arginfo_none,     ZEND_ACC_PUBLIC)
253   PHP_ME(TypeUserType, withKeyspace, arginfo_keyspace, ZEND_ACC_PUBLIC)
254   PHP_ME(TypeUserType, keyspace,     arginfo_none,     ZEND_ACC_PUBLIC)
255   PHP_ME(TypeUserType, __toString,   arginfo_none,     ZEND_ACC_PUBLIC)
256   PHP_ME(TypeUserType, types,        arginfo_none,     ZEND_ACC_PUBLIC)
257   PHP_ME(TypeUserType, create,       arginfo_value,    ZEND_ACC_PUBLIC)
258   PHP_FE_END
259 };
260 
261 static zend_object_handlers php_driver_type_user_type_handlers;
262 
263 static HashTable *
php_driver_type_user_type_gc(zval * object,php5to7_zval_gc table,int * n TSRMLS_DC)264 php_driver_type_user_type_gc(zval *object, php5to7_zval_gc table, int *n TSRMLS_DC)
265 {
266   *table = NULL;
267   *n = 0;
268   return zend_std_get_properties(object TSRMLS_CC);
269 }
270 
271 static HashTable *
php_driver_type_user_type_properties(zval * object TSRMLS_DC)272 php_driver_type_user_type_properties(zval *object TSRMLS_DC)
273 {
274   php5to7_zval types;
275 
276   php_driver_type *self  = PHP_DRIVER_GET_TYPE(object);
277   HashTable      *props = zend_std_get_properties(object TSRMLS_CC);
278 
279   PHP5TO7_ZVAL_MAYBE_MAKE(types);
280   array_init(PHP5TO7_ZVAL_MAYBE_P(types));
281   PHP5TO7_ZEND_HASH_ZVAL_COPY(PHP5TO7_Z_ARRVAL_MAYBE_P(types), &self->data.udt.types);
282   PHP5TO7_ZEND_HASH_UPDATE(props,
283                            "types", sizeof("types"),
284                            PHP5TO7_ZVAL_MAYBE_P(types), sizeof(zval));
285 
286   return props;
287 }
288 
289 static int
php_driver_type_user_type_compare(zval * obj1,zval * obj2 TSRMLS_DC)290 php_driver_type_user_type_compare(zval *obj1, zval *obj2 TSRMLS_DC)
291 {
292   php_driver_type* type1 = PHP_DRIVER_GET_TYPE(obj1);
293   php_driver_type* type2 = PHP_DRIVER_GET_TYPE(obj2);
294 
295   return php_driver_type_compare(type1, type2 TSRMLS_CC);
296 }
297 
298 static void
php_driver_type_user_type_free(php5to7_zend_object_free * object TSRMLS_DC)299 php_driver_type_user_type_free(php5to7_zend_object_free *object TSRMLS_DC)
300 {
301   php_driver_type *self = PHP5TO7_ZEND_OBJECT_GET(type, object);
302 
303   if (self->data_type) cass_data_type_free(self->data_type);
304   if (self->data.udt.keyspace) efree(self->data.udt.keyspace);
305   if (self->data.udt.type_name) efree(self->data.udt.type_name);
306   zend_hash_destroy(&self->data.udt.types);
307 
308   zend_object_std_dtor(&self->zval TSRMLS_CC);
309   PHP5TO7_MAYBE_EFREE(self);
310 }
311 
312 static php5to7_zend_object
php_driver_type_user_type_new(zend_class_entry * ce TSRMLS_DC)313 php_driver_type_user_type_new(zend_class_entry *ce TSRMLS_DC)
314 {
315   php_driver_type *self = PHP5TO7_ZEND_OBJECT_ECALLOC(type, ce);
316 
317   self->type = CASS_VALUE_TYPE_UDT;
318   self->data_type = NULL;
319   self->data.udt.keyspace = self->data.udt.type_name = NULL;
320   zend_hash_init(&self->data.udt.types, 0, NULL, ZVAL_PTR_DTOR, 0);
321 
322   PHP5TO7_ZEND_OBJECT_INIT_EX(type, type_user_type, self, ce);
323 }
324 
php_driver_define_TypeUserType(TSRMLS_D)325 void php_driver_define_TypeUserType(TSRMLS_D)
326 {
327   zend_class_entry ce;
328 
329   INIT_CLASS_ENTRY(ce, PHP_DRIVER_NAMESPACE "\\Type\\UserType", php_driver_type_user_type_methods);
330   php_driver_type_user_type_ce = php5to7_zend_register_internal_class_ex(&ce, php_driver_type_ce);
331   memcpy(&php_driver_type_user_type_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
332   php_driver_type_user_type_handlers.get_properties  = php_driver_type_user_type_properties;
333 #if PHP_VERSION_ID >= 50400
334   php_driver_type_user_type_handlers.get_gc          = php_driver_type_user_type_gc;
335 #endif
336   php_driver_type_user_type_handlers.compare_objects = php_driver_type_user_type_compare;
337   php_driver_type_user_type_ce->ce_flags     |= PHP5TO7_ZEND_ACC_FINAL;
338   php_driver_type_user_type_ce->create_object = php_driver_type_user_type_new;
339 }
340