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/hash.h"
20 #include "util/types.h"
21 #include <time.h>
22 #include <ext/date/php_date.h>
23 
24 zend_class_entry *php_driver_date_ce = NULL;
25 
26 void
php_driver_date_init(INTERNAL_FUNCTION_PARAMETERS)27 php_driver_date_init(INTERNAL_FUNCTION_PARAMETERS)
28 {
29   zval *seconds = NULL;
30   php_driver_date *self;
31 
32   if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|z", &seconds) == FAILURE) {
33     return;
34   }
35 
36   if (getThis() && instanceof_function(Z_OBJCE_P(getThis()), php_driver_date_ce TSRMLS_CC)) {
37     self = PHP_DRIVER_GET_DATE(getThis());
38   } else {
39     object_init_ex(return_value, php_driver_date_ce);
40     self = PHP_DRIVER_GET_DATE(return_value);
41   }
42 
43   if (seconds == NULL) {
44     self->date = cass_date_from_epoch(time(NULL));
45   } else {
46     if (Z_TYPE_P(seconds) != IS_LONG) {
47       INVALID_ARGUMENT(seconds, "a number of seconds since the Unix Epoch");
48     }
49     self->date = cass_date_from_epoch(Z_LVAL_P(seconds));
50   }
51 }
52 
53 /* {{{ Date::__construct(string) */
PHP_METHOD(Date,__construct)54 PHP_METHOD(Date, __construct)
55 {
56   php_driver_date_init(INTERNAL_FUNCTION_PARAM_PASSTHRU);
57 }
58 /* }}} */
59 
60 /* {{{ Date::type() */
PHP_METHOD(Date,type)61 PHP_METHOD(Date, type)
62 {
63   php5to7_zval type = php_driver_type_scalar(CASS_VALUE_TYPE_DATE TSRMLS_CC);
64   RETURN_ZVAL(PHP5TO7_ZVAL_MAYBE_P(type), 1, 1);
65 }
66 /* }}} */
67 
68 /* {{{ Date::seconds() */
PHP_METHOD(Date,seconds)69 PHP_METHOD(Date, seconds)
70 {
71   php_driver_date *self = PHP_DRIVER_GET_DATE(getThis());
72 
73   RETURN_LONG(cass_date_time_to_epoch(self->date, 0));
74 }
75 /* }}} */
76 
77 /* {{{ Date::toDateTime() */
PHP_METHOD(Date,toDateTime)78 PHP_METHOD(Date, toDateTime)
79 {
80   php_driver_date *self;
81   zval *ztime = NULL;
82   php_driver_time* time_obj = NULL;
83   php5to7_zval datetime;
84   php_date_obj *datetime_obj = NULL;
85   char *str;
86   int str_len;
87 
88   if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|z", &ztime) == FAILURE) {
89     return;
90   }
91 
92   if (ztime != NULL) {
93     time_obj = PHP_DRIVER_GET_TIME(ztime);
94   }
95   self = PHP_DRIVER_GET_DATE(getThis());
96 
97   PHP5TO7_ZVAL_MAYBE_MAKE(datetime);
98   php_date_instantiate(php_date_get_date_ce(), PHP5TO7_ZVAL_MAYBE_P(datetime) TSRMLS_CC);
99 
100 #if PHP_MAJOR_VERSION >= 7
101   datetime_obj = php_date_obj_from_obj(Z_OBJ(datetime));
102 #else
103   datetime_obj = zend_object_store_get_object(datetime TSRMLS_CC);
104 #endif
105 
106   str_len = spprintf(&str, 0, "%lld",
107                      cass_date_time_to_epoch(self->date,
108                                              time_obj != NULL ? time_obj->time : 0));
109   php_date_initialize(datetime_obj, str, str_len, "U", NULL, 0 TSRMLS_CC);
110   efree(str);
111 
112   RETVAL_ZVAL(PHP5TO7_ZVAL_MAYBE_P(datetime), 0, 1);
113 }
114 /* }}} */
115 
116 /* {{{ Date::fromDateTime() */
PHP_METHOD(Date,fromDateTime)117 PHP_METHOD(Date, fromDateTime)
118 {
119   php_driver_date *self;
120   zval *zdatetime;
121   php5to7_zval retval;
122 
123   if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &zdatetime) == FAILURE) {
124     return;
125   }
126 
127   zend_call_method_with_0_params(PHP5TO7_ZVAL_MAYBE_ADDR_OF(zdatetime),
128                                  php_date_get_date_ce(),
129                                  NULL,
130                                  "gettimestamp",
131                                  &retval);
132 
133   if (!PHP5TO7_ZVAL_IS_UNDEF(retval) &&
134       Z_TYPE_P(PHP5TO7_ZVAL_MAYBE_P(retval)) == IS_LONG) {
135     object_init_ex(return_value, php_driver_date_ce);
136     self = PHP_DRIVER_GET_DATE(return_value);
137     self->date = cass_date_from_epoch(PHP5TO7_Z_LVAL_MAYBE_P(retval));
138     zval_ptr_dtor(&retval);
139     return;
140   }
141 }
142 /* }}} */
143 
144 
145 /* {{{ Date::__toString() */
PHP_METHOD(Date,__toString)146 PHP_METHOD(Date, __toString)
147 {
148   php_driver_date *self;
149   char *ret = NULL;
150 
151   if (zend_parse_parameters_none() == FAILURE) {
152     return;
153   }
154 
155   self = PHP_DRIVER_GET_DATE(getThis());
156 
157   spprintf(&ret, 0, PHP_DRIVER_NAMESPACE "\\Date(seconds=%lld)", cass_date_time_to_epoch(self->date, 0));
158   PHP5TO7_RETVAL_STRING(ret);
159   efree(ret);
160 }
161 /* }}} */
162 
163 ZEND_BEGIN_ARG_INFO_EX(arginfo__construct, 0, ZEND_RETURN_VALUE, 0)
164   ZEND_ARG_INFO(0, seconds)
165 ZEND_END_ARG_INFO()
166 
167 ZEND_BEGIN_ARG_INFO_EX(arginfo_time, 0, ZEND_RETURN_VALUE, 0)
168   PHP_DRIVER_NAMESPACE_ZEND_ARG_OBJ_INFO(0, time, Time, 0)
169 ZEND_END_ARG_INFO()
170 
171 ZEND_BEGIN_ARG_INFO_EX(arginfo_datetime, 0, ZEND_RETURN_VALUE, 1)
172   ZEND_ARG_OBJ_INFO(0, datetime, DateTime, 0)
173 ZEND_END_ARG_INFO()
174 
175 ZEND_BEGIN_ARG_INFO_EX(arginfo_none, 0, ZEND_RETURN_VALUE, 0)
176 ZEND_END_ARG_INFO()
177 
178 static zend_function_entry php_driver_date_methods[] = {
179   PHP_ME(Date, __construct, arginfo__construct, ZEND_ACC_CTOR|ZEND_ACC_PUBLIC)
180   PHP_ME(Date, type, arginfo_none, ZEND_ACC_PUBLIC)
181   PHP_ME(Date, seconds, arginfo_none, ZEND_ACC_PUBLIC)
182   PHP_ME(Date, toDateTime, arginfo_time, ZEND_ACC_PUBLIC)
183   PHP_ME(Date, fromDateTime, arginfo_datetime, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC)
184   PHP_ME(Date, __toString, arginfo_none, ZEND_ACC_PUBLIC)
185   PHP_FE_END
186 };
187 
188 static php_driver_value_handlers php_driver_date_handlers;
189 
190 static HashTable *
php_driver_date_gc(zval * object,php5to7_zval_gc table,int * n TSRMLS_DC)191 php_driver_date_gc(zval *object, php5to7_zval_gc table, int *n TSRMLS_DC)
192 {
193   *table = NULL;
194   *n = 0;
195   return zend_std_get_properties(object TSRMLS_CC);
196 }
197 
198 static HashTable *
php_driver_date_properties(zval * object TSRMLS_DC)199 php_driver_date_properties(zval *object TSRMLS_DC)
200 {
201   php5to7_zval type;
202   php5to7_zval seconds;
203 
204   php_driver_date *self = PHP_DRIVER_GET_DATE(object);
205   HashTable *props = zend_std_get_properties(object TSRMLS_CC);
206 
207   type = php_driver_type_scalar(CASS_VALUE_TYPE_DATE TSRMLS_CC);
208   PHP5TO7_ZEND_HASH_UPDATE(props, "type", sizeof("type"), PHP5TO7_ZVAL_MAYBE_P(type), sizeof(zval));
209 
210   PHP5TO7_ZVAL_MAYBE_MAKE(seconds);
211   ZVAL_LONG(PHP5TO7_ZVAL_MAYBE_P(seconds), cass_date_time_to_epoch(self->date, 0));
212   PHP5TO7_ZEND_HASH_UPDATE(props, "seconds", sizeof("seconds"), PHP5TO7_ZVAL_MAYBE_P(seconds), sizeof(zval));
213 
214   return props;
215 }
216 
217 static int
php_driver_date_compare(zval * obj1,zval * obj2 TSRMLS_DC)218 php_driver_date_compare(zval *obj1, zval *obj2 TSRMLS_DC)
219 {
220   php_driver_date *date1 = NULL;
221   php_driver_date *date2 = NULL;
222   if (Z_OBJCE_P(obj1) != Z_OBJCE_P(obj2))
223     return 1; /* different classes */
224 
225   date1 = PHP_DRIVER_GET_DATE(obj1);
226   date2 = PHP_DRIVER_GET_DATE(obj2);
227 
228   return PHP_DRIVER_COMPARE(date1->date, date2->date);
229 }
230 
231 static unsigned
php_driver_date_hash_value(zval * obj TSRMLS_DC)232 php_driver_date_hash_value(zval *obj TSRMLS_DC)
233 {
234   php_driver_date *self = PHP_DRIVER_GET_DATE(obj);
235   return 31 * 17 + self->date;
236 }
237 
238 static void
php_driver_date_free(php5to7_zend_object_free * object TSRMLS_DC)239 php_driver_date_free(php5to7_zend_object_free *object TSRMLS_DC)
240 {
241   php_driver_date *self = PHP5TO7_ZEND_OBJECT_GET(date, object);
242 
243   zend_object_std_dtor(&self->zval TSRMLS_CC);
244   PHP5TO7_MAYBE_EFREE(self);
245 }
246 
247 static php5to7_zend_object
php_driver_date_new(zend_class_entry * ce TSRMLS_DC)248 php_driver_date_new(zend_class_entry *ce TSRMLS_DC)
249 {
250   php_driver_date *self =
251       PHP5TO7_ZEND_OBJECT_ECALLOC(date, ce);
252 
253   self->date = 0;
254 
255   PHP5TO7_ZEND_OBJECT_INIT(date, self, ce);
256 }
257 
php_driver_define_Date(TSRMLS_D)258 void php_driver_define_Date(TSRMLS_D)
259 {
260   zend_class_entry ce;
261 
262   INIT_CLASS_ENTRY(ce, PHP_DRIVER_NAMESPACE "\\Date", php_driver_date_methods);
263   php_driver_date_ce = zend_register_internal_class(&ce TSRMLS_CC);
264   zend_class_implements(php_driver_date_ce TSRMLS_CC, 1, php_driver_value_ce);
265   memcpy(&php_driver_date_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
266   php_driver_date_handlers.std.get_properties  = php_driver_date_properties;
267 #if PHP_VERSION_ID >= 50400
268   php_driver_date_handlers.std.get_gc          = php_driver_date_gc;
269 #endif
270   php_driver_date_handlers.std.compare_objects = php_driver_date_compare;
271   php_driver_date_ce->ce_flags |= PHP5TO7_ZEND_ACC_FINAL;
272   php_driver_date_ce->create_object = php_driver_date_new;
273 
274   php_driver_date_handlers.hash_value = php_driver_date_hash_value;
275 }
276