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_globals.h"
19 #include "php_driver_types.h"
20 #include "util/types.h"
21 #if PHP_MAJOR_VERSION >= 7
22 #include <zend_smart_str.h>
23 #else
24 #include <ext/standard/php_smart_str.h>
25 #endif
26 #include "src/Bigint.h"
27 #include "src/Smallint.h"
28 #include "src/Tinyint.h"
29 #include "src/Blob.h"
30 #include "src/Decimal.h"
31 #include "src/Duration.h"
32 #include "src/Float.h"
33 #include "src/Inet.h"
34 #include "src/Timestamp.h"
35 #include "src/Date.h"
36 #include "src/Time.h"
37 #include "src/Timeuuid.h"
38 #include "src/Uuid.h"
39 #include "src/Varint.h"
40 #include "src/Type/Tuple.h"
41 #include "src/Type/UserType.h"
42 
43 struct node_s {
44   struct node_s *parent;
45   const char    *name;
46   size_t         name_length;
47   struct node_s *first_child;
48   struct node_s *last_child;
49   struct node_s *next_sibling;
50   struct node_s *prev_sibling;
51 };
52 static int
hex_value(int c)53 hex_value(int c)
54 {
55   if (c >= '0' && c <= '9') {
56     return c - '0';
57   } else if (c >= 'A' && c <= 'F') {
58     return c - 'A' + 10;
59   } else if (c >= 'a' && c <= 'f') {
60     return c - 'a' + 10;
61   }
62   return -1;
63 }
64 
65 static char*
php_driver_from_hex(const char * hex,size_t hex_length)66 php_driver_from_hex(const char* hex, size_t hex_length)
67 {
68   size_t i, c = 0;
69   size_t size = hex_length / 2;
70   char *result;
71   if ((hex_length & 1) == 1) { /* Invalid if not divisible by 2 */
72     return NULL;
73   }
74   result = emalloc(size + 1);
75   for (i = 0; i < size; ++i) {
76     int half0 = hex_value(hex[i * 2]);
77     int half1 = hex_value(hex[i * 2 + 1]);
78     if (half0 < 0 || half1 < 0) {
79       efree(result);
80       return NULL;
81     }
82     result[c++] = (char)(((uint8_t)half0 << 4) | (uint8_t)half1);
83   }
84   result[size] = '\0';
85   return result;
86 }
87 
88 static php5to7_zval
89 php_driver_create_type(struct node_s *node TSRMLS_DC);
90 
91 static php5to7_zval
php_driver_tuple_from_data_type(const CassDataType * data_type TSRMLS_DC)92 php_driver_tuple_from_data_type(const CassDataType *data_type TSRMLS_DC) {
93   php5to7_zval ztype;
94   php_driver_type *type;
95   size_t i, count;
96 
97   count = cass_data_sub_type_count(data_type);
98   ztype = php_driver_type_tuple(TSRMLS_C);
99   type = PHP_DRIVER_GET_TYPE(PHP5TO7_ZVAL_MAYBE_P(ztype));
100   for (i = 0; i < count; ++i) {
101     php5to7_zval sub_type =
102         php_driver_type_from_data_type(
103           cass_data_type_sub_data_type(data_type, i) TSRMLS_CC);
104     php_driver_type_tuple_add(type,
105                                  PHP5TO7_ZVAL_MAYBE_P(sub_type)
106                                  TSRMLS_CC);
107   }
108 
109   return ztype;
110 }
111 
112 static php5to7_zval
php_driver_tuple_from_node(struct node_s * node TSRMLS_DC)113 php_driver_tuple_from_node(struct node_s *node TSRMLS_DC) {
114   php5to7_zval ztype;
115   php_driver_type *type;
116   struct node_s *current;
117 
118   ztype = php_driver_type_tuple(TSRMLS_C);
119   type = PHP_DRIVER_GET_TYPE(PHP5TO7_ZVAL_MAYBE_P(ztype));
120 
121   for (current = node->first_child;
122        current != NULL;
123        current = current->next_sibling) {
124     php5to7_zval sub_type = php_driver_create_type(current TSRMLS_CC);
125     php_driver_type_tuple_add(type,
126                                  PHP5TO7_ZVAL_MAYBE_P(sub_type)
127                                  TSRMLS_CC);
128   }
129 
130   return ztype;
131 }
132 
133 static php5to7_zval
php_driver_user_type_from_data_type(const CassDataType * data_type TSRMLS_DC)134 php_driver_user_type_from_data_type(const CassDataType *data_type TSRMLS_DC)
135 {
136   php5to7_zval ztype;
137   php_driver_type *type;
138   const char *type_name, *keyspace;
139   size_t  type_name_len, keyspace_len;
140   size_t i, count;
141 
142   count = cass_data_sub_type_count(data_type);
143   ztype = php_driver_type_user_type(TSRMLS_C);
144   type = PHP_DRIVER_GET_TYPE(PHP5TO7_ZVAL_MAYBE_P(ztype));
145 
146   cass_data_type_type_name(data_type, &type_name, &type_name_len);
147   type->data.udt.type_name = estrndup(type_name, type_name_len);
148   cass_data_type_keyspace(data_type, &keyspace, &keyspace_len);
149   type->data.udt.keyspace = estrndup(keyspace, keyspace_len);
150 
151   for (i = 0; i < count; ++i) {
152     const char *name;
153     size_t name_length;
154     php5to7_zval sub_type =
155         php_driver_type_from_data_type(
156           cass_data_type_sub_data_type(data_type, i) TSRMLS_CC);
157     cass_data_type_sub_type_name(data_type, i, &name, &name_length);
158     php_driver_type_user_type_add(type,
159                                      name, name_length,
160                                      PHP5TO7_ZVAL_MAYBE_P(sub_type) TSRMLS_CC);
161   }
162 
163   return ztype;
164 }
165 
166 
167 static php5to7_zval
php_driver_user_type_from_node(struct node_s * node TSRMLS_DC)168 php_driver_user_type_from_node(struct node_s *node TSRMLS_DC)
169 {
170   php5to7_zval ztype;
171   php_driver_type *type;
172   struct node_s *current = node->first_child;
173 
174   ztype = php_driver_type_user_type(TSRMLS_C);
175   type = PHP_DRIVER_GET_TYPE(PHP5TO7_ZVAL_MAYBE_P(ztype));
176 
177   if (current) {
178     type->data.udt.keyspace = estrndup(current->name,
179                               current->name_length);
180     current = current->next_sibling;
181   }
182 
183   if (current) {
184     type->data.udt.type_name = php_driver_from_hex(current->name,
185                                              current->name_length);
186     current = current->next_sibling;
187   }
188 
189   for (; current; current = current->next_sibling) {
190     php5to7_zval sub_type;
191     char *name = php_driver_from_hex(current->name,
192                                         current->name_length);
193     current = current->next_sibling;
194     if (!current) {
195       efree(name);
196       break;
197     }
198     sub_type = php_driver_create_type(current TSRMLS_CC);
199     php_driver_type_user_type_add(type,
200                                      name, strlen(name),
201                                      PHP5TO7_ZVAL_MAYBE_P(sub_type) TSRMLS_CC);
202     efree(name);
203   }
204 
205   return ztype;
206 }
207 
208 php5to7_zval
php_driver_type_from_data_type(const CassDataType * data_type TSRMLS_DC)209 php_driver_type_from_data_type(const CassDataType *data_type TSRMLS_DC)
210 {
211   php5to7_zval ztype;
212   php5to7_zval key_type;
213   php5to7_zval value_type;
214   const char *class_name;
215   size_t class_name_length;
216   CassValueType type = cass_data_type_type(data_type);
217 
218   PHP5TO7_ZVAL_UNDEF(ztype);
219 
220   switch (type) {
221 #define XX_SCALAR(name, value)                       \
222   case value:                                        \
223     ztype = php_driver_type_scalar(value TSRMLS_CC); \
224     break;
225    PHP_DRIVER_SCALAR_TYPES_MAP(XX_SCALAR)
226 #undef XX_SCALAR
227 
228   case CASS_VALUE_TYPE_CUSTOM:
229      cass_data_type_class_name(data_type, &class_name, &class_name_length);
230      ztype = php_driver_type_custom(class_name, class_name_length TSRMLS_CC);
231      break;
232 
233   case CASS_VALUE_TYPE_LIST:
234     value_type = php_driver_type_from_data_type(
235       cass_data_type_sub_data_type(data_type, 0) TSRMLS_CC);
236     ztype = php_driver_type_collection(PHP5TO7_ZVAL_MAYBE_P(value_type) TSRMLS_CC);
237     break;
238 
239   case CASS_VALUE_TYPE_MAP:
240     key_type = php_driver_type_from_data_type(
241                  cass_data_type_sub_data_type(data_type, 0) TSRMLS_CC);
242     value_type = php_driver_type_from_data_type(
243                    cass_data_type_sub_data_type(data_type, 1) TSRMLS_CC);
244     ztype = php_driver_type_map(PHP5TO7_ZVAL_MAYBE_P(key_type),
245                                 PHP5TO7_ZVAL_MAYBE_P(value_type) TSRMLS_CC);
246     break;
247 
248   case CASS_VALUE_TYPE_SET:
249     value_type = php_driver_type_from_data_type(
250       cass_data_type_sub_data_type(data_type, 0) TSRMLS_CC);
251     ztype = php_driver_type_set(PHP5TO7_ZVAL_MAYBE_P(value_type) TSRMLS_CC);
252     break;
253 
254   case CASS_VALUE_TYPE_TUPLE:
255     ztype = php_driver_tuple_from_data_type(data_type TSRMLS_CC);
256     break;
257 
258   case CASS_VALUE_TYPE_UDT:
259     ztype = php_driver_user_type_from_data_type(data_type TSRMLS_CC);
260     break;
261 
262   default:
263     break;
264   }
265 
266   return ztype;
267 }
268 
php_driver_type_validate(zval * object,const char * object_name TSRMLS_DC)269 int php_driver_type_validate(zval *object, const char *object_name TSRMLS_DC)
270 {
271   if (!instanceof_function(Z_OBJCE_P(object), php_driver_type_scalar_ce TSRMLS_CC) &&
272       !instanceof_function(Z_OBJCE_P(object), php_driver_type_collection_ce TSRMLS_CC) &&
273       !instanceof_function(Z_OBJCE_P(object), php_driver_type_map_ce TSRMLS_CC) &&
274       !instanceof_function(Z_OBJCE_P(object), php_driver_type_set_ce TSRMLS_CC) &&
275       !instanceof_function(Z_OBJCE_P(object), php_driver_type_tuple_ce TSRMLS_CC) &&
276       !instanceof_function(Z_OBJCE_P(object), php_driver_type_user_type_ce TSRMLS_CC)) {
277     throw_invalid_argument(object, object_name, "a valid " PHP_DRIVER_NAMESPACE "\\Type" TSRMLS_CC);
278     return 0;
279   }
280   return 1;
281 }
282 
283 static inline int
collection_compare(php_driver_type * type1,php_driver_type * type2 TSRMLS_DC)284 collection_compare(php_driver_type *type1, php_driver_type *type2 TSRMLS_DC)
285 {
286   return php_driver_type_compare(PHP_DRIVER_GET_TYPE(PHP5TO7_ZVAL_MAYBE_P(type1->data.collection.value_type)),
287                                     PHP_DRIVER_GET_TYPE(PHP5TO7_ZVAL_MAYBE_P(type2->data.collection.value_type)) TSRMLS_CC);
288 }
289 
290 static inline int
map_compare(php_driver_type * type1,php_driver_type * type2 TSRMLS_DC)291 map_compare(php_driver_type *type1, php_driver_type *type2 TSRMLS_DC)
292 {
293   int result;
294   result = php_driver_type_compare(PHP_DRIVER_GET_TYPE(PHP5TO7_ZVAL_MAYBE_P(type1->data.map.key_type)),
295                                        PHP_DRIVER_GET_TYPE(PHP5TO7_ZVAL_MAYBE_P(type2->data.map.key_type)) TSRMLS_CC);
296   if (result != 0) return result;
297   result =  php_driver_type_compare(PHP_DRIVER_GET_TYPE(PHP5TO7_ZVAL_MAYBE_P(type1->data.map.value_type)),
298                                        PHP_DRIVER_GET_TYPE(PHP5TO7_ZVAL_MAYBE_P(type2->data.map.value_type)) TSRMLS_CC);
299   if (result != 0) return result;
300   return 0;
301 }
302 
303 static inline int
set_compare(php_driver_type * type1,php_driver_type * type2 TSRMLS_DC)304 set_compare(php_driver_type *type1, php_driver_type *type2 TSRMLS_DC)
305 {
306   return php_driver_type_compare(PHP_DRIVER_GET_TYPE(PHP5TO7_ZVAL_MAYBE_P(type1->data.set.value_type)),
307                                     PHP_DRIVER_GET_TYPE(PHP5TO7_ZVAL_MAYBE_P(type2->data.set.value_type)) TSRMLS_CC);
308 }
309 
310 static inline int
tuple_compare(php_driver_type * type1,php_driver_type * type2 TSRMLS_DC)311 tuple_compare(php_driver_type *type1, php_driver_type *type2 TSRMLS_DC) {
312   HashPosition pos1;
313   HashPosition pos2;
314   php5to7_zval *current1;
315   php5to7_zval *current2;
316 
317   if (zend_hash_num_elements(&type1->data.tuple.types) != zend_hash_num_elements(&type2->data.tuple.types)) {
318     return zend_hash_num_elements(&type1->data.tuple.types) < zend_hash_num_elements(&type2->data.tuple.types) ? -1 : 1;
319   }
320 
321   zend_hash_internal_pointer_reset_ex(&type1->data.tuple.types, &pos1);
322   zend_hash_internal_pointer_reset_ex(&type2->data.tuple.types, &pos2);
323 
324   while (PHP5TO7_ZEND_HASH_GET_CURRENT_DATA_EX(&type1->data.tuple.types, current1, &pos1) &&
325          PHP5TO7_ZEND_HASH_GET_CURRENT_DATA_EX(&type2->data.tuple.types, current2, &pos2)) {
326     php_driver_type *sub_type1 =
327         PHP_DRIVER_GET_TYPE(PHP5TO7_ZVAL_MAYBE_DEREF(current1));
328     php_driver_type *sub_type2 =
329         PHP_DRIVER_GET_TYPE(PHP5TO7_ZVAL_MAYBE_DEREF(current2));
330     int result = php_driver_type_compare(sub_type1, sub_type2 TSRMLS_CC);
331     if (result != 0) return result;
332     zend_hash_move_forward_ex(&type1->data.tuple.types, &pos1);
333     zend_hash_move_forward_ex(&type2->data.tuple.types, &pos2);
334   }
335 
336   return 0;
337 }
338 
339 static inline int
user_type_compare(php_driver_type * type1,php_driver_type * type2 TSRMLS_DC)340 user_type_compare(php_driver_type *type1, php_driver_type *type2 TSRMLS_DC)
341 {
342   HashPosition pos1;
343   HashPosition pos2;
344   php5to7_string key1;
345   php5to7_string key2;
346   php5to7_zval *current1;
347   php5to7_zval *current2;
348 
349   if (zend_hash_num_elements(&type1->data.udt.types) != zend_hash_num_elements(&type2->data.udt.types)) {
350     return zend_hash_num_elements(&type1->data.udt.types) < zend_hash_num_elements(&type2->data.udt.types) ? -1 : 1;
351   }
352 
353   zend_hash_internal_pointer_reset_ex(&type1->data.udt.types, &pos1);
354   zend_hash_internal_pointer_reset_ex(&type2->data.udt.types, &pos2);
355 
356   while (PHP5TO7_ZEND_HASH_GET_CURRENT_KEY_EX(&type1->data.udt.types, &key1, NULL, &pos1) == HASH_KEY_IS_STRING &&
357          PHP5TO7_ZEND_HASH_GET_CURRENT_KEY_EX(&type2->data.udt.types, &key2, NULL, &pos2) == HASH_KEY_IS_STRING &&
358          PHP5TO7_ZEND_HASH_GET_CURRENT_DATA_EX(&type1->data.udt.types, current1, &pos1) &&
359          PHP5TO7_ZEND_HASH_GET_CURRENT_DATA_EX(&type2->data.udt.types, current2, &pos2)) {
360     int result;
361     php_driver_type *sub_type1 =
362         PHP_DRIVER_GET_TYPE(PHP5TO7_ZVAL_MAYBE_DEREF(current1));
363     php_driver_type *sub_type2 =
364         PHP_DRIVER_GET_TYPE(PHP5TO7_ZVAL_MAYBE_DEREF(current2));
365     result = php5to7_string_compare(key1, key2);
366     if (result != 0) return result;
367     result = php_driver_type_compare(sub_type1, sub_type2 TSRMLS_CC);
368     if (result != 0) return result;
369     zend_hash_move_forward_ex(&type1->data.udt.types, &pos1);
370     zend_hash_move_forward_ex(&type2->data.udt.types, &pos2);
371   }
372 
373   return 0;
374 }
375 
376 static inline int
is_string_type(CassValueType type)377 is_string_type(CassValueType type)
378 {
379   return type == CASS_VALUE_TYPE_VARCHAR || type == CASS_VALUE_TYPE_TEXT;
380 }
381 
382 int
php_driver_type_compare(php_driver_type * type1,php_driver_type * type2 TSRMLS_DC)383 php_driver_type_compare(php_driver_type *type1, php_driver_type *type2 TSRMLS_DC)
384 {
385   if (type1->type != type2->type) {
386     if (is_string_type(type1->type) &&
387         is_string_type(type2->type)) { /* varchar and text are aliases */
388       return 0;
389     }
390     return type1->type < type2->type ? -1 : 1;
391   } else {
392     switch (type1->type) {
393     case CASS_VALUE_TYPE_LIST:
394       return collection_compare(type1, type2 TSRMLS_CC);
395 
396     case CASS_VALUE_TYPE_MAP:
397       return map_compare(type1, type2 TSRMLS_CC);
398 
399     case CASS_VALUE_TYPE_SET:
400       return set_compare(type1, type2 TSRMLS_CC);
401 
402     case CASS_VALUE_TYPE_TUPLE:
403       return tuple_compare(type1, type2 TSRMLS_CC);
404 
405     case CASS_VALUE_TYPE_UDT:
406       return user_type_compare(type1, type2 TSRMLS_CC);
407 
408     default:
409       break;
410     }
411     return 0;
412   }
413 }
414 
415 static inline void
collection_string(php_driver_type * type,smart_str * string TSRMLS_DC)416 collection_string(php_driver_type *type, smart_str *string TSRMLS_DC)
417 {
418   smart_str_appendl(string, "list<", 5);
419   php_driver_type_string(PHP_DRIVER_GET_TYPE(PHP5TO7_ZVAL_MAYBE_P(type->data.collection.value_type)), string TSRMLS_CC);
420   smart_str_appendl(string, ">", 1);
421 }
422 
423 static inline void
map_string(php_driver_type * type,smart_str * string TSRMLS_DC)424 map_string(php_driver_type *type, smart_str *string TSRMLS_DC)
425 {
426   smart_str_appendl(string, "map<", 4);
427   php_driver_type_string(PHP_DRIVER_GET_TYPE(PHP5TO7_ZVAL_MAYBE_P(type->data.map.key_type)), string TSRMLS_CC);
428   smart_str_appendl(string, ", ", 2);
429   php_driver_type_string(PHP_DRIVER_GET_TYPE(PHP5TO7_ZVAL_MAYBE_P(type->data.map.value_type)), string TSRMLS_CC);
430   smart_str_appendl(string, ">", 1);
431 }
432 
433 static inline void
set_string(php_driver_type * type,smart_str * string TSRMLS_DC)434 set_string(php_driver_type *type, smart_str *string TSRMLS_DC)
435 {
436   smart_str_appendl(string, "set<", 4);
437   php_driver_type_string(PHP_DRIVER_GET_TYPE(PHP5TO7_ZVAL_MAYBE_P(type->data.set.value_type)), string TSRMLS_CC);
438   smart_str_appendl(string, ">", 1);
439 }
440 
441 static inline void
tuple_string(php_driver_type * type,smart_str * string TSRMLS_DC)442 tuple_string(php_driver_type *type, smart_str *string TSRMLS_DC) {
443   php5to7_zval *current;
444   int first = 1;
445 
446   smart_str_appendl(string, "tuple<", 6);
447   PHP5TO7_ZEND_HASH_FOREACH_VAL(&type->data.tuple.types, current) {
448     php_driver_type *sub_type =
449         PHP_DRIVER_GET_TYPE(PHP5TO7_ZVAL_MAYBE_DEREF(current));
450     if (!first) smart_str_appendl(string, ", ", 2);
451     first = 0;
452     php_driver_type_string(sub_type, string TSRMLS_CC);
453   } PHP5TO7_ZEND_HASH_FOREACH_END(&type->data.tuple.types);
454   smart_str_appendl(string, ">", 1);
455 }
456 
457 static inline void
user_type_string(php_driver_type * type,smart_str * string TSRMLS_DC)458 user_type_string(php_driver_type *type, smart_str *string TSRMLS_DC)
459 {
460   char *name;
461   php5to7_zval *current;
462   int first = 1;
463 
464   if (type->data.udt.type_name) {
465     if (type->data.udt.keyspace) {
466       smart_str_appendl(string, type->data.udt.keyspace, strlen(type->data.udt.keyspace));
467       smart_str_appendl(string, ".", 1);
468     }
469     smart_str_appendl(string, type->data.udt.type_name, strlen(type->data.udt.type_name));
470   } else {
471     smart_str_appendl(string, "userType<", 9);
472     PHP5TO7_ZEND_HASH_FOREACH_STR_KEY_VAL(&type->data.udt.types, name, current) {
473       php_driver_type *sub_type =
474           PHP_DRIVER_GET_TYPE(PHP5TO7_ZVAL_MAYBE_DEREF(current));
475       if (!first) smart_str_appendl(string, ", ", 2);
476       first = 0;
477       smart_str_appendl(string, name, strlen(name));
478       smart_str_appendl(string, ":", 1);
479       php_driver_type_string(sub_type, string TSRMLS_CC);
480     } PHP5TO7_ZEND_HASH_FOREACH_END(&type->data.udt.types);
481     smart_str_appendl(string, ">", 1);
482   }
483 }
484 
485 void
php_driver_type_string(php_driver_type * type,smart_str * string TSRMLS_DC)486 php_driver_type_string(php_driver_type *type, smart_str *string TSRMLS_DC)
487 {
488   switch (type->type) {
489 #define XX_SCALAR(name, value) \
490   case value: \
491     smart_str_appendl(string, #name, strlen(#name)); \
492     break;
493   PHP_DRIVER_SCALAR_TYPES_MAP(XX_SCALAR)
494 #undef XX_SCALAR
495 
496   case CASS_VALUE_TYPE_LIST:
497     collection_string(type, string TSRMLS_CC);
498     break;
499 
500   case CASS_VALUE_TYPE_MAP:
501     map_string(type, string TSRMLS_CC);
502     break;
503 
504   case CASS_VALUE_TYPE_SET:
505     set_string(type, string TSRMLS_CC);
506     break;
507 
508   case CASS_VALUE_TYPE_TUPLE:
509     tuple_string(type, string TSRMLS_CC);
510     break;
511 
512   case CASS_VALUE_TYPE_UDT:
513     user_type_string(type, string TSRMLS_CC);
514     break;
515 
516   default:
517     smart_str_appendl(string, "invalid", 7);
518     break;
519   }
520 }
521 
522 static php5to7_zval
php_driver_type_scalar_new(CassValueType type TSRMLS_DC)523 php_driver_type_scalar_new(CassValueType type TSRMLS_DC)
524 {
525   php5to7_zval ztype;
526   php_driver_type *scalar;
527 
528   PHP5TO7_ZVAL_MAYBE_MAKE(ztype);
529   object_init_ex(PHP5TO7_ZVAL_MAYBE_P(ztype), php_driver_type_scalar_ce);
530   scalar = PHP_DRIVER_GET_TYPE(PHP5TO7_ZVAL_MAYBE_P(ztype));
531   scalar->type = type;
532   scalar->data_type = cass_data_type_new(type);
533 
534   return ztype;
535 }
536 
537 
538 const char *
php_driver_scalar_type_name(CassValueType type TSRMLS_DC)539 php_driver_scalar_type_name(CassValueType type TSRMLS_DC)
540 {
541   switch (type) {
542 #define XX_SCALAR(name, value) \
543   case value: \
544     return #name;
545   PHP_DRIVER_SCALAR_TYPES_MAP(XX_SCALAR)
546 #undef XX_SCALAR
547   default:
548     return "invalid";
549   }
550 }
551 
552 static void
php_driver_varchar_init(INTERNAL_FUNCTION_PARAMETERS)553 php_driver_varchar_init(INTERNAL_FUNCTION_PARAMETERS)
554 {
555   char *string;
556   php5to7_size string_len;
557 
558   if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &string, &string_len) == FAILURE) {
559     return;
560   }
561 
562   PHP5TO7_RETVAL_STRINGL(string, string_len);
563 }
564 
565 static void
php_driver_ascii_init(INTERNAL_FUNCTION_PARAMETERS)566 php_driver_ascii_init(INTERNAL_FUNCTION_PARAMETERS)
567 {
568   php_driver_varchar_init(INTERNAL_FUNCTION_PARAM_PASSTHRU);
569 }
570 
571 static void
php_driver_boolean_init(INTERNAL_FUNCTION_PARAMETERS)572 php_driver_boolean_init(INTERNAL_FUNCTION_PARAMETERS)
573 {
574   zend_bool value;
575 
576   if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "b", &value) == FAILURE) {
577     return;
578   }
579 
580   RETURN_BOOL(value);
581 }
582 
583 static void
php_driver_counter_init(INTERNAL_FUNCTION_PARAMETERS)584 php_driver_counter_init(INTERNAL_FUNCTION_PARAMETERS)
585 {
586   php_driver_bigint_init(INTERNAL_FUNCTION_PARAM_PASSTHRU);
587 }
588 
589 static void
php_driver_double_init(INTERNAL_FUNCTION_PARAMETERS)590 php_driver_double_init(INTERNAL_FUNCTION_PARAMETERS)
591 {
592   double value;
593 
594   if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "d", &value) == FAILURE) {
595     return;
596   }
597 
598   RETURN_DOUBLE(value);
599 }
600 
601 static void
php_driver_int_init(INTERNAL_FUNCTION_PARAMETERS)602 php_driver_int_init(INTERNAL_FUNCTION_PARAMETERS)
603 {
604   long value;
605 
606   if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &value) == FAILURE) {
607     return;
608   }
609 
610   RETURN_LONG(value);
611 }
612 
613 static void
php_driver_text_init(INTERNAL_FUNCTION_PARAMETERS)614 php_driver_text_init(INTERNAL_FUNCTION_PARAMETERS)
615 {
616   php_driver_varchar_init(INTERNAL_FUNCTION_PARAM_PASSTHRU);
617 }
618 
619 #define TYPE_INIT_METHOD(t) php_driver_ ## t ## _init
620 
621 void
php_driver_scalar_init(INTERNAL_FUNCTION_PARAMETERS)622 php_driver_scalar_init(INTERNAL_FUNCTION_PARAMETERS)
623 {
624   php_driver_type *self = PHP_DRIVER_GET_TYPE(getThis());
625 
626 #define XX_SCALAR(name, value) \
627   if (self->type == value) { \
628     TYPE_INIT_METHOD(name)(INTERNAL_FUNCTION_PARAM_PASSTHRU); \
629   }
630   PHP_DRIVER_SCALAR_TYPES_MAP(XX_SCALAR)
631 #undef XX_SCALAR
632 }
633 #undef TYPE_INIT_METHOD
634 
635 #define TYPE_CODE(m) type_ ## m
636 
637 php5to7_zval
php_driver_type_scalar(CassValueType type TSRMLS_DC)638 php_driver_type_scalar(CassValueType type TSRMLS_DC)
639 {
640   php5to7_zval result;
641   PHP5TO7_ZVAL_UNDEF(result);
642 
643 #define XX_SCALAR(name, value) \
644   if (value == type) { \
645     if (PHP5TO7_ZVAL_IS_UNDEF(PHP_DRIVER_G(TYPE_CODE(name)))) { \
646       PHP_DRIVER_G(TYPE_CODE(name)) = php_driver_type_scalar_new(type TSRMLS_CC); \
647     } \
648     Z_ADDREF_P(PHP5TO7_ZVAL_MAYBE_P(PHP_DRIVER_G(TYPE_CODE(name)))); \
649     return PHP_DRIVER_G(TYPE_CODE(name)); \
650   }
651   PHP_DRIVER_SCALAR_TYPES_MAP(XX_SCALAR)
652 #undef XX_SCALAR
653 
654   zend_throw_exception_ex(php_driver_invalid_argument_exception_ce,
655                           0 TSRMLS_CC, "Invalid type");
656   return result;
657 }
658 #undef TYPE_CODE
659 
660 php5to7_zval
php_driver_type_map(zval * key_type,zval * value_type TSRMLS_DC)661 php_driver_type_map(zval *key_type,
662                        zval *value_type TSRMLS_DC)
663 {
664   php5to7_zval ztype;
665   php_driver_type *map;
666   php_driver_type *sub_type;
667 
668   PHP5TO7_ZVAL_MAYBE_MAKE(ztype);
669   object_init_ex(PHP5TO7_ZVAL_MAYBE_P(ztype), php_driver_type_map_ce);
670   map = PHP_DRIVER_GET_TYPE(PHP5TO7_ZVAL_MAYBE_P(ztype));
671 
672   if (!PHP5TO7_ZVAL_IS_UNDEF_P(key_type)) {
673     sub_type = PHP_DRIVER_GET_TYPE(key_type);
674     cass_data_type_add_sub_type(map->data_type, sub_type->data_type);
675   }
676 
677   if (!PHP5TO7_ZVAL_IS_UNDEF_P(value_type)) {
678     sub_type = PHP_DRIVER_GET_TYPE(value_type);
679     cass_data_type_add_sub_type(map->data_type, sub_type->data_type);
680   }
681 
682 #if PHP_MAJOR_VERSION >= 7
683   map->data.map.key_type = *key_type;
684   map->data.map.value_type = *value_type;
685 #else
686   map->data.map.key_type = key_type;
687   map->data.map.value_type = value_type;
688 #endif
689 
690   return ztype;
691 }
692 
693 php5to7_zval
php_driver_type_map_from_value_types(CassValueType key_type,CassValueType value_type TSRMLS_DC)694 php_driver_type_map_from_value_types(CassValueType key_type,
695                                         CassValueType value_type TSRMLS_DC)
696 {
697   php5to7_zval ztype;
698   php_driver_type *map;
699   php_driver_type *sub_type;
700 
701   PHP5TO7_ZVAL_MAYBE_MAKE(ztype);
702   object_init_ex(PHP5TO7_ZVAL_MAYBE_P(ztype), php_driver_type_map_ce);
703   map = PHP_DRIVER_GET_TYPE(PHP5TO7_ZVAL_MAYBE_P(ztype));
704   map->data.map.key_type   = php_driver_type_scalar(key_type TSRMLS_CC);
705   map->data.map.value_type = php_driver_type_scalar(value_type TSRMLS_CC);
706 
707   sub_type = PHP_DRIVER_GET_TYPE(PHP5TO7_ZVAL_MAYBE_P(map->data.map.key_type));
708   cass_data_type_add_sub_type(map->data_type, sub_type->data_type);
709   sub_type = PHP_DRIVER_GET_TYPE(PHP5TO7_ZVAL_MAYBE_P(map->data.map.value_type));
710   cass_data_type_add_sub_type(map->data_type, sub_type->data_type);
711 
712   return ztype;
713 }
714 
715 php5to7_zval
php_driver_type_set(zval * value_type TSRMLS_DC)716 php_driver_type_set(zval *value_type TSRMLS_DC)
717 {
718   php5to7_zval ztype;
719   php_driver_type *set;
720   php_driver_type *sub_type;
721 
722   PHP5TO7_ZVAL_MAYBE_MAKE(ztype);
723   object_init_ex(PHP5TO7_ZVAL_MAYBE_P(ztype), php_driver_type_set_ce);
724   set = PHP_DRIVER_GET_TYPE(PHP5TO7_ZVAL_MAYBE_P(ztype));
725 
726   if (!PHP5TO7_ZVAL_IS_UNDEF_P(value_type)) {
727     sub_type = PHP_DRIVER_GET_TYPE(value_type);
728     cass_data_type_add_sub_type(set->data_type, sub_type->data_type);
729   }
730 
731 #if PHP_MAJOR_VERSION >= 7
732   set->data.set.value_type = *value_type;
733 #else
734   set->data.set.value_type = value_type;
735 #endif
736 
737   return ztype;
738 }
739 
740 php5to7_zval
php_driver_type_set_from_value_type(CassValueType type TSRMLS_DC)741 php_driver_type_set_from_value_type(CassValueType type TSRMLS_DC)
742 {
743   php5to7_zval ztype;
744   php_driver_type *set;
745   php_driver_type *sub_type;
746 
747   PHP5TO7_ZVAL_MAYBE_MAKE(ztype);
748   object_init_ex(PHP5TO7_ZVAL_MAYBE_P(ztype), php_driver_type_set_ce);
749   set = PHP_DRIVER_GET_TYPE(PHP5TO7_ZVAL_MAYBE_P(ztype));
750   set->data.set.value_type = php_driver_type_scalar(type TSRMLS_CC);
751 
752   sub_type = PHP_DRIVER_GET_TYPE(PHP5TO7_ZVAL_MAYBE_P(set->data.set.value_type));
753   cass_data_type_add_sub_type(set->data_type, sub_type->data_type);
754 
755   return ztype;
756 }
757 
758 php5to7_zval
php_driver_type_collection(zval * value_type TSRMLS_DC)759 php_driver_type_collection(zval *value_type TSRMLS_DC)
760 {
761   php5to7_zval ztype;
762   php_driver_type *collection;
763   php_driver_type *sub_type;
764 
765   PHP5TO7_ZVAL_MAYBE_MAKE(ztype);
766   object_init_ex(PHP5TO7_ZVAL_MAYBE_P(ztype), php_driver_type_collection_ce);
767   collection = PHP_DRIVER_GET_TYPE(PHP5TO7_ZVAL_MAYBE_P(ztype));
768 
769   if (!PHP5TO7_ZVAL_IS_UNDEF_P(value_type)) {
770     sub_type = PHP_DRIVER_GET_TYPE(value_type);
771     cass_data_type_add_sub_type(collection->data_type, sub_type->data_type);
772   }
773 
774 #if PHP_MAJOR_VERSION >= 7
775   collection->data.collection.value_type = *value_type;
776 #else
777   collection->data.collection.value_type = value_type;
778 #endif
779 
780   return ztype;
781 }
782 
783 php5to7_zval
php_driver_type_collection_from_value_type(CassValueType type TSRMLS_DC)784 php_driver_type_collection_from_value_type(CassValueType type TSRMLS_DC)
785 {
786   php5to7_zval ztype;
787   php_driver_type *collection;
788   php_driver_type *sub_type;
789 
790   PHP5TO7_ZVAL_MAYBE_MAKE(ztype);
791   object_init_ex(PHP5TO7_ZVAL_MAYBE_P(ztype), php_driver_type_collection_ce);
792   collection = PHP_DRIVER_GET_TYPE(PHP5TO7_ZVAL_MAYBE_P(ztype));
793   collection->data.collection.value_type = php_driver_type_scalar(type TSRMLS_CC);
794 
795   sub_type = PHP_DRIVER_GET_TYPE(PHP5TO7_ZVAL_MAYBE_P(collection->data.collection.value_type));
796   cass_data_type_add_sub_type(collection->data_type, sub_type->data_type);
797 
798   return ztype;
799 }
800 
801 php5to7_zval
php_driver_type_tuple(TSRMLS_D)802 php_driver_type_tuple(TSRMLS_D)
803 {
804   php5to7_zval ztype;
805 
806   PHP5TO7_ZVAL_MAYBE_MAKE(ztype);
807   object_init_ex(PHP5TO7_ZVAL_MAYBE_P(ztype), php_driver_type_tuple_ce);
808 
809   return ztype;
810 }
811 
812 php5to7_zval
php_driver_type_user_type(TSRMLS_D)813 php_driver_type_user_type(TSRMLS_D)
814 {
815   php5to7_zval ztype;
816   php_driver_type *user_type;
817 
818   PHP5TO7_ZVAL_MAYBE_MAKE(ztype);
819   object_init_ex(PHP5TO7_ZVAL_MAYBE_P(ztype), php_driver_type_user_type_ce);
820   user_type = PHP_DRIVER_GET_TYPE(PHP5TO7_ZVAL_MAYBE_P(ztype));
821   user_type->data_type = cass_data_type_new(CASS_VALUE_TYPE_UDT);
822 
823   return ztype;
824 }
825 
826 php5to7_zval
php_driver_type_custom(const char * name,size_t name_length TSRMLS_DC)827 php_driver_type_custom(const char *name, size_t name_length TSRMLS_DC)
828 {
829   php5to7_zval ztype;
830   php_driver_type *custom;
831 
832   PHP5TO7_ZVAL_MAYBE_MAKE(ztype);
833   object_init_ex(PHP5TO7_ZVAL_MAYBE_P(ztype), php_driver_type_custom_ce);
834   custom = PHP_DRIVER_GET_TYPE(PHP5TO7_ZVAL_MAYBE_P(ztype));
835   custom->data.custom.class_name = estrndup(name, name_length);
836 
837   return ztype;
838 }
839 
840 #define EXPECTING_TOKEN(expected) \
841   zend_throw_exception_ex(php_driver_invalid_argument_exception_ce, 0 TSRMLS_CC, \
842     "Unexpected %s at position %d in string \"%s\", expected " expected, \
843     describe_token(token), ((int) (str - validator) - 1), validator \
844   ); \
845   return FAILURE;
846 
847 enum token_type {
848   TOKEN_ILLEGAL = 0,
849   TOKEN_PAREN_OPEN,
850   TOKEN_PAREN_CLOSE,
851   TOKEN_COMMA,
852   TOKEN_COLON,
853   TOKEN_NAME,
854   TOKEN_END
855 };
856 
857 enum parser_state {
858   STATE_CLASS = 0,
859   STATE_AFTER_CLASS,
860   STATE_AFTER_PARENS,
861   STATE_END
862 };
863 
864 static const char *
describe_token(enum token_type token)865 describe_token(enum token_type token)
866 {
867   switch (token) {
868   case TOKEN_ILLEGAL:     return "illegal character";      break;
869   case TOKEN_PAREN_OPEN:  return "opening parenthesis";    break;
870   case TOKEN_PAREN_CLOSE: return "closing parenthesis";    break;
871   case TOKEN_COMMA:       return "comma";                  break;
872   case TOKEN_COLON:       return "colon";                  break;
873   case TOKEN_NAME:        return "alphanumeric character"; break;
874   case TOKEN_END:         return "end of string";          break;
875   default:                return "unknown token";
876   }
877 }
878 
879 static int
isletter(char ch)880 isletter(char ch)
881 {
882   return isalnum(ch) || ch == '.';
883 }
884 
885 static enum token_type
next_token(const char * str,size_t len,const char ** token_str,size_t * token_len,const char ** str_out,size_t * len_out)886 next_token(const char  *str,       size_t  len,
887            const char **token_str, size_t *token_len,
888            const char **str_out,   size_t *len_out)
889 {
890   enum token_type type;
891   unsigned int i = 0;
892   char         c = str[i];
893 
894   if (len == 0) {
895     return TOKEN_END;
896   }
897 
898   if (isalnum(c)) {
899     type = TOKEN_NAME;
900     while (i < len) {
901       if (!isletter(str[i])) {
902         break;
903       }
904       i++;
905     }
906   } else {
907     switch (c) {
908       case '\0':
909         type = TOKEN_END;
910         break;
911       case '(':
912         type = TOKEN_PAREN_OPEN;
913         i++;
914         break;
915       case ')':
916         type = TOKEN_PAREN_CLOSE;
917         i++;
918         break;
919       case ',':
920         type = TOKEN_COMMA;
921         i++;
922         break;
923       case ':':
924         type = TOKEN_COLON;
925         i++;
926         break;
927       default:
928         type = TOKEN_ILLEGAL;
929     }
930   }
931 
932   *token_str = &(str[0]);
933   *token_len = i;
934   *str_out   = &(str[i]);
935   *len_out   = len - i;
936 
937   return type;
938 }
939 
940 static struct node_s *
php_driver_parse_node_new()941 php_driver_parse_node_new()
942 {
943   struct node_s *node;
944   node = emalloc(sizeof(struct node_s));
945   node->parent        = NULL;
946   node->name          = NULL;
947   node->name_length   = 0;
948   node->first_child   = NULL;
949   node->last_child    = NULL;
950   node->next_sibling  = NULL;
951   node->prev_sibling  = NULL;
952 
953   return node;
954 }
955 
956 static void
php_driver_parse_node_free(struct node_s * node)957 php_driver_parse_node_free(struct node_s *node)
958 {
959   if (node->first_child) {
960     php_driver_parse_node_free(node->first_child);
961     node->first_child = NULL;
962   }
963   node->last_child = NULL;
964 
965   if (node->next_sibling) {
966     php_driver_parse_node_free(node->next_sibling);
967     node->next_sibling = NULL;
968   }
969 
970   efree(node);
971 }
972 
973 static int
php_driver_parse_class_name(const char * validator,size_t validator_len,struct node_s ** result TSRMLS_DC)974 php_driver_parse_class_name(const char     *validator,
975                                size_t          validator_len,
976                                struct node_s **result TSRMLS_DC)
977 {
978   const char       *str;
979   size_t            len;
980   const char       *token_str;
981   size_t            token_len;
982   enum parser_state state;
983   enum token_type   token;
984   struct node_s    *root;
985   struct node_s    *node;
986   struct node_s    *child;
987 
988   token_str = NULL;
989   token_len = 0;
990   state     = STATE_CLASS;
991   str       = validator;
992   len       = validator_len;
993   root      = php_driver_parse_node_new();
994   node      = root;
995 
996   while (1) {
997     token = next_token(str, len, &token_str, &token_len, &str, &len);
998 
999     if (token == TOKEN_ILLEGAL) {
1000       zend_throw_exception_ex(php_driver_invalid_argument_exception_ce, 0 TSRMLS_CC,
1001         "Illegal character \"%c\" at position %d in \"%s\"",
1002         *token_str, ((int) (str - validator) - 1), validator);
1003       php_driver_parse_node_free(root);
1004       return FAILURE;
1005     }
1006 
1007     if (state == STATE_AFTER_PARENS) {
1008       if (token == TOKEN_COMMA) {
1009         if (node->parent == NULL) {
1010           EXPECTING_TOKEN("end of string");
1011         }
1012         state = STATE_CLASS;
1013 
1014         child = php_driver_parse_node_new();
1015         child->parent        = node->parent;
1016         child->prev_sibling  = node;
1017         node->next_sibling   = child;
1018         node->parent->last_child = child;
1019 
1020         node = child;
1021         continue;
1022       } else if (token == TOKEN_PAREN_CLOSE) {
1023         if (node->parent == NULL) {
1024           EXPECTING_TOKEN("end of string");
1025         }
1026 
1027         node = node->parent;
1028         continue;
1029       } else if (token == TOKEN_END) {
1030         break;
1031       } else {
1032         EXPECTING_TOKEN("a comma, a closing parenthesis or an end of string");
1033       }
1034     }
1035 
1036     if (state == STATE_AFTER_CLASS) {
1037       if (token == TOKEN_PAREN_OPEN) {
1038         state = STATE_CLASS;
1039 
1040         child = php_driver_parse_node_new();
1041         child->parent = node;
1042 
1043         if (node->first_child == NULL) {
1044           node->first_child = child;
1045         }
1046 
1047         if (node->last_child) {
1048           node->last_child->next_sibling = child;
1049         }
1050 
1051         child->prev_sibling = node->last_child;
1052         node->last_child = child;
1053 
1054         node = child;
1055         continue;
1056       } else if (token == TOKEN_COMMA || token == TOKEN_COLON) {
1057         state = STATE_CLASS;
1058 
1059         child = php_driver_parse_node_new();
1060         child->parent        = node->parent;
1061         child->prev_sibling  = node;
1062         node->next_sibling   = child;
1063         node->parent->last_child = child;
1064 
1065         node = child;
1066         continue;
1067       } else if (token == TOKEN_PAREN_CLOSE) {
1068         state = STATE_AFTER_PARENS;
1069 
1070         node = node->parent;
1071         continue;
1072       } else if (token == TOKEN_END) {
1073         break;
1074       } else {
1075         php_driver_parse_node_free(root);
1076         EXPECTING_TOKEN("opening/closing parenthesis or comma");
1077       }
1078     }
1079 
1080     if (state == STATE_CLASS) {
1081       if (token != TOKEN_NAME) {
1082         php_driver_parse_node_free(root);
1083         EXPECTING_TOKEN("fully qualified class name");
1084       }
1085       state = STATE_AFTER_CLASS;
1086 
1087       node->name        = token_str;
1088       node->name_length = token_len;
1089     }
1090   }
1091 
1092   *result = root;
1093   return SUCCESS;
1094 }
1095 
1096 static CassValueType
php_driver_lookup_type(struct node_s * node TSRMLS_DC)1097 php_driver_lookup_type(struct node_s *node TSRMLS_DC)
1098 {
1099   if (strncmp("org.apache.cassandra.db.marshal.AsciiType", node->name, node->name_length) == 0) {
1100     return CASS_VALUE_TYPE_ASCII;
1101   }
1102 
1103   if (strncmp("org.apache.cassandra.db.marshal.LongType", node->name, node->name_length) == 0) {
1104     return CASS_VALUE_TYPE_BIGINT;
1105   }
1106 
1107   if (strncmp("org.apache.cassandra.db.marshal.ShortType", node->name, node->name_length) == 0) {
1108     return CASS_VALUE_TYPE_SMALL_INT;
1109   }
1110 
1111   if (strncmp("org.apache.cassandra.db.marshal.ByteType", node->name, node->name_length) == 0) {
1112     return CASS_VALUE_TYPE_TINY_INT;
1113   }
1114 
1115   if (strncmp("org.apache.cassandra.db.marshal.BytesType", node->name, node->name_length) == 0) {
1116     return CASS_VALUE_TYPE_BLOB;
1117   }
1118 
1119   if (strncmp("org.apache.cassandra.db.marshal.BooleanType", node->name, node->name_length) == 0) {
1120     return CASS_VALUE_TYPE_BOOLEAN;
1121   }
1122 
1123   if (strncmp("org.apache.cassandra.db.marshal.CounterColumnType", node->name, node->name_length) == 0) {
1124     return CASS_VALUE_TYPE_COUNTER;
1125   }
1126 
1127   if (strncmp("org.apache.cassandra.db.marshal.DecimalType", node->name, node->name_length) == 0) {
1128     return CASS_VALUE_TYPE_DECIMAL;
1129   }
1130 
1131   if (strncmp("org.apache.cassandra.db.marshal.DurationType", node->name, node->name_length) == 0) {
1132     return CASS_VALUE_TYPE_DURATION;
1133   }
1134 
1135   if (strncmp("org.apache.cassandra.db.marshal.DoubleType", node->name, node->name_length) == 0) {
1136     return CASS_VALUE_TYPE_DOUBLE;
1137   }
1138 
1139   if (strncmp("org.apache.cassandra.db.marshal.FloatType", node->name, node->name_length) == 0) {
1140     return CASS_VALUE_TYPE_FLOAT;
1141   }
1142 
1143   if (strncmp("org.apache.cassandra.db.marshal.InetAddressType", node->name, node->name_length) == 0) {
1144     return CASS_VALUE_TYPE_INET;
1145   }
1146 
1147   if (strncmp("org.apache.cassandra.db.marshal.Int32Type", node->name, node->name_length) == 0) {
1148     return CASS_VALUE_TYPE_INT;
1149   }
1150 
1151   if (strncmp("org.apache.cassandra.db.marshal.UTF8Type", node->name, node->name_length) == 0) {
1152     return CASS_VALUE_TYPE_VARCHAR;
1153   }
1154 
1155   if (strncmp("org.apache.cassandra.db.marshal.TimestampType", node->name, node->name_length) == 0 ||
1156       strncmp("org.apache.cassandra.db.marshal.DateType",      node->name, node->name_length) == 0) {
1157     return CASS_VALUE_TYPE_TIMESTAMP;
1158   }
1159 
1160   if (strncmp("org.apache.cassandra.db.marshal.SimpleDateType", node->name, node->name_length) == 0) {
1161     return CASS_VALUE_TYPE_DATE;
1162   }
1163 
1164   if (strncmp("org.apache.cassandra.db.marshal.TimeType", node->name, node->name_length) == 0) {
1165     return CASS_VALUE_TYPE_TIME;
1166   }
1167 
1168   if (strncmp("org.apache.cassandra.db.marshal.UUIDType", node->name, node->name_length) == 0) {
1169     return CASS_VALUE_TYPE_UUID;
1170   }
1171 
1172   if (strncmp("org.apache.cassandra.db.marshal.IntegerType", node->name, node->name_length) == 0) {
1173     return CASS_VALUE_TYPE_VARINT;
1174   }
1175 
1176   if (strncmp("org.apache.cassandra.db.marshal.TimeUUIDType", node->name, node->name_length) == 0) {
1177     return CASS_VALUE_TYPE_TIMEUUID;
1178   }
1179 
1180   if (strncmp("org.apache.cassandra.db.marshal.MapType", node->name, node->name_length) == 0) {
1181     return CASS_VALUE_TYPE_MAP;
1182   }
1183 
1184   if (strncmp("org.apache.cassandra.db.marshal.SetType", node->name, node->name_length) == 0) {
1185     return CASS_VALUE_TYPE_SET;
1186   }
1187 
1188   if (strncmp("org.apache.cassandra.db.marshal.ListType", node->name, node->name_length) == 0) {
1189     return CASS_VALUE_TYPE_LIST;
1190   }
1191 
1192   if (strncmp("org.apache.cassandra.db.marshal.TupleType", node->name, node->name_length) == 0) {
1193     return CASS_VALUE_TYPE_TUPLE;
1194   }
1195 
1196   if (strncmp("org.apache.cassandra.db.marshal.UserType", node->name, node->name_length) == 0) {
1197     return CASS_VALUE_TYPE_UDT;
1198   }
1199 
1200   return CASS_VALUE_TYPE_CUSTOM;
1201 }
1202 
1203 static void
php_driver_node_dump_to(struct node_s * node,smart_str * text)1204 php_driver_node_dump_to(struct node_s *node, smart_str *text)
1205 {
1206   smart_str_appendl(text, node->name, node->name_length);
1207 
1208   if (node->first_child) {
1209     smart_str_appendl(text, "(", 1);
1210     php_driver_node_dump_to(node->first_child, text);
1211     smart_str_appendl(text, ")", 1);
1212   }
1213 
1214   if (node->next_sibling) {
1215     smart_str_appendl(text, ", ", 2);
1216     php_driver_node_dump_to(node->next_sibling, text);
1217   }
1218 }
1219 
1220 static php5to7_zval
php_driver_create_type(struct node_s * node TSRMLS_DC)1221 php_driver_create_type(struct node_s *node TSRMLS_DC)
1222 {
1223   CassValueType type = CASS_VALUE_TYPE_UNKNOWN;
1224 
1225   /* Skip wrapper types */
1226   while (node &&
1227          (strncmp("org.apache.cassandra.db.marshal.FrozenType", node->name, node->name_length)    == 0 ||
1228           strncmp("org.apache.cassandra.db.marshal.ReversedType", node->name, node->name_length)  == 0 ||
1229           strncmp("org.apache.cassandra.db.marshal.CompositeType", node->name, node->name_length) == 0)) {
1230     node = node->first_child;
1231   }
1232 
1233   if (node) {
1234     type = php_driver_lookup_type(node TSRMLS_CC);
1235   }
1236 
1237   if (type == CASS_VALUE_TYPE_UNKNOWN) {
1238     php5to7_zval undef;
1239     PHP5TO7_ZVAL_UNDEF(undef);
1240     return undef;
1241   }
1242 
1243   if (type == CASS_VALUE_TYPE_CUSTOM) {
1244     php5to7_zval ztype;
1245     smart_str class_name = PHP5TO7_SMART_STR_INIT;
1246     php_driver_node_dump_to(node, &class_name);
1247     ztype =  php_driver_type_custom(PHP5TO7_SMART_STR_VAL(class_name),
1248                                     PHP5TO7_SMART_STR_LEN(class_name) TSRMLS_CC);
1249     smart_str_free(&class_name);
1250     return ztype;
1251   } else if (type == CASS_VALUE_TYPE_MAP) {
1252     php5to7_zval key_type;
1253     php5to7_zval value_type;
1254 
1255     if (node->first_child) {
1256       key_type = php_driver_create_type(node->first_child TSRMLS_CC);
1257       value_type = php_driver_create_type(node->first_child->next_sibling TSRMLS_CC);
1258     } else {
1259       PHP5TO7_ZVAL_UNDEF(key_type);
1260       PHP5TO7_ZVAL_UNDEF(value_type);
1261     }
1262     return php_driver_type_map(PHP5TO7_ZVAL_MAYBE_P(key_type),
1263                                   PHP5TO7_ZVAL_MAYBE_P(value_type) TSRMLS_CC);
1264   } else if (type == CASS_VALUE_TYPE_LIST) {
1265     php5to7_zval value_type;
1266     if (node->first_child) {
1267       value_type = php_driver_create_type(node->first_child TSRMLS_CC);
1268     } else {
1269       PHP5TO7_ZVAL_UNDEF(value_type);
1270     }
1271     return php_driver_type_collection(PHP5TO7_ZVAL_MAYBE_P(value_type) TSRMLS_CC);
1272   } else if (type == CASS_VALUE_TYPE_SET) {
1273     php5to7_zval value_type;
1274     if (node->first_child) {
1275       value_type = php_driver_create_type(node->first_child TSRMLS_CC);
1276     } else {
1277       PHP5TO7_ZVAL_UNDEF(value_type);
1278     }
1279     return php_driver_type_set(PHP5TO7_ZVAL_MAYBE_P(value_type) TSRMLS_CC);
1280   } else if (type == CASS_VALUE_TYPE_TUPLE) {
1281     return php_driver_tuple_from_node(node TSRMLS_CC);
1282   } else if (type == CASS_VALUE_TYPE_UDT) {
1283     return php_driver_user_type_from_node(node TSRMLS_CC);
1284   }
1285 
1286   return php_driver_type_scalar(type TSRMLS_CC);
1287 }
1288 
1289 int
php_driver_parse_column_type(const char * validator,size_t validator_len,int * reversed_out,int * frozen_out,php5to7_zval * type_out TSRMLS_DC)1290 php_driver_parse_column_type(const char   *validator,
1291                                 size_t        validator_len,
1292                                 int          *reversed_out,
1293                                 int          *frozen_out,
1294                                 php5to7_zval *type_out TSRMLS_DC)
1295 {
1296   struct node_s *root;
1297   struct node_s *node  = NULL;
1298   cass_bool_t reversed = 0;
1299   cass_bool_t frozen   = 0;
1300 
1301   if (php_driver_parse_class_name(validator, validator_len, &root TSRMLS_CC) == FAILURE) {
1302     return FAILURE;
1303   }
1304 
1305   node = root;
1306 
1307   while (node) {
1308     if (strncmp("org.apache.cassandra.db.marshal.ReversedType", node->name, node->name_length) == 0) {
1309       reversed = 1;
1310       node     = node->first_child;
1311       continue;
1312     }
1313 
1314     if (strncmp("org.apache.cassandra.db.marshal.FrozenType", node->name, node->name_length) == 0) {
1315       frozen = 1;
1316       node   = node->first_child;
1317       continue;
1318     }
1319 
1320     if (strncmp("org.apache.cassandra.db.marshal.CompositeType", node->name, node->name_length) == 0) {
1321       node = node->first_child;
1322       continue;
1323     }
1324 
1325     break;
1326   }
1327 
1328   if (node == NULL) {
1329     php_driver_parse_node_free(root);
1330     zend_throw_exception_ex(php_driver_invalid_argument_exception_ce, 0 TSRMLS_CC,
1331       "Invalid type");
1332     return FAILURE;
1333   }
1334 
1335   *reversed_out = reversed;
1336   *frozen_out   = frozen;
1337   *type_out     = php_driver_create_type(node TSRMLS_CC);
1338 
1339   php_driver_parse_node_free(root);
1340 
1341   return SUCCESS;
1342 }
1343