timezone_class.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426
  1. /*
  2. +----------------------------------------------------------------------+
  3. | This source file is subject to version 3.01 of the PHP license, |
  4. | that is bundled with this package in the file LICENSE, and is |
  5. | available through the world-wide-web at the following url: |
  6. | https://www.php.net/license/3_01.txt |
  7. | If you did not receive a copy of the PHP license and are unable to |
  8. | obtain it through the world-wide-web, please send a note to |
  9. | license@php.net so we can mail you a copy immediately. |
  10. +----------------------------------------------------------------------+
  11. | Authors: Gustavo Lopes <cataphract@php.net> |
  12. +----------------------------------------------------------------------+
  13. */
  14. #ifdef HAVE_CONFIG_H
  15. #include "config.h"
  16. #endif
  17. #include "../intl_cppshims.h"
  18. #include <unicode/timezone.h>
  19. #include <unicode/calendar.h>
  20. #include "../intl_convertcpp.h"
  21. #include "../common/common_date.h"
  22. extern "C" {
  23. #include "../intl_convert.h"
  24. #define USE_TIMEZONE_POINTER 1
  25. #include "timezone_class.h"
  26. #include "timezone_arginfo.h"
  27. #include <zend_exceptions.h>
  28. #include <zend_interfaces.h>
  29. #include <ext/date/php_date.h>
  30. }
  31. using icu::Calendar;
  32. /* {{{ Global variables */
  33. U_CDECL_BEGIN
  34. zend_class_entry *TimeZone_ce_ptr = NULL;
  35. zend_object_handlers TimeZone_handlers;
  36. U_CDECL_END
  37. /* }}} */
  38. /* {{{ timezone_object_construct */
  39. U_CFUNC void timezone_object_construct(const TimeZone *zone, zval *object, int owned)
  40. {
  41. TimeZone_object *to;
  42. object_init_ex(object, TimeZone_ce_ptr);
  43. TIMEZONE_METHOD_FETCH_OBJECT_NO_CHECK; /* fetch zend object from zval "object" into "to" */
  44. to->utimezone = zone;
  45. to->should_delete = owned;
  46. }
  47. /* }}} */
  48. /* {{{ timezone_convert_to_datetimezone
  49. * Convert from TimeZone to DateTimeZone object */
  50. U_CFUNC zval *timezone_convert_to_datetimezone(const TimeZone *timeZone,
  51. intl_error *outside_error,
  52. const char *func, zval *ret)
  53. {
  54. UnicodeString id;
  55. char *message = NULL;
  56. php_timezone_obj *tzobj;
  57. zval arg;
  58. timeZone->getID(id);
  59. if (id.isBogus()) {
  60. spprintf(&message, 0, "%s: could not obtain TimeZone id", func);
  61. intl_errors_set(outside_error, U_ILLEGAL_ARGUMENT_ERROR,
  62. message, 1);
  63. goto error;
  64. }
  65. object_init_ex(ret, php_date_get_timezone_ce());
  66. tzobj = Z_PHPTIMEZONE_P(ret);
  67. if (id.compare(0, 3, UnicodeString("GMT", sizeof("GMT")-1, US_INV)) == 0) {
  68. /* The DateTimeZone constructor doesn't support offset time zones,
  69. * so we must mess with DateTimeZone structure ourselves */
  70. tzobj->initialized = 1;
  71. tzobj->type = TIMELIB_ZONETYPE_OFFSET;
  72. //convert offset from milliseconds to seconds
  73. tzobj->tzi.utc_offset = timeZone->getRawOffset() / 1000;
  74. } else {
  75. zend_string *u8str;
  76. /* Call the constructor! */
  77. u8str = intl_charFromString(id, &INTL_ERROR_CODE(*outside_error));
  78. if (!u8str) {
  79. spprintf(&message, 0, "%s: could not convert id to UTF-8", func);
  80. intl_errors_set(outside_error, INTL_ERROR_CODE(*outside_error),
  81. message, 1);
  82. goto error;
  83. }
  84. ZVAL_STR(&arg, u8str);
  85. zend_call_known_instance_method_with_1_params(
  86. Z_OBJCE_P(ret)->constructor, Z_OBJ_P(ret), NULL, &arg);
  87. if (EG(exception)) {
  88. spprintf(&message, 0,
  89. "%s: DateTimeZone constructor threw exception", func);
  90. intl_errors_set(outside_error, U_ILLEGAL_ARGUMENT_ERROR,
  91. message, 1);
  92. zend_object_store_ctor_failed(Z_OBJ_P(ret));
  93. zval_ptr_dtor(&arg);
  94. goto error;
  95. }
  96. zval_ptr_dtor(&arg);
  97. }
  98. if (0) {
  99. error:
  100. if (ret) {
  101. zval_ptr_dtor(ret);
  102. }
  103. ret = NULL;
  104. }
  105. if (message) {
  106. efree(message);
  107. }
  108. return ret;
  109. }
  110. /* }}} */
  111. /* {{{ timezone_process_timezone_argument
  112. * TimeZone argument processor. outside_error may be NULL (for static functions/constructors) */
  113. U_CFUNC TimeZone *timezone_process_timezone_argument(zval *zv_timezone,
  114. intl_error *outside_error,
  115. const char *func)
  116. {
  117. zval local_zv_tz;
  118. char *message = NULL;
  119. TimeZone *timeZone;
  120. if (zv_timezone == NULL || Z_TYPE_P(zv_timezone) == IS_NULL) {
  121. timelib_tzinfo *tzinfo = get_timezone_info();
  122. ZVAL_STRING(&local_zv_tz, tzinfo->name);
  123. zv_timezone = &local_zv_tz;
  124. } else {
  125. ZVAL_NULL(&local_zv_tz);
  126. }
  127. if (Z_TYPE_P(zv_timezone) == IS_OBJECT &&
  128. instanceof_function(Z_OBJCE_P(zv_timezone), TimeZone_ce_ptr)) {
  129. TimeZone_object *to = Z_INTL_TIMEZONE_P(zv_timezone);
  130. if (to->utimezone == NULL) {
  131. spprintf(&message, 0, "%s: passed IntlTimeZone is not "
  132. "properly constructed", func);
  133. if (message) {
  134. intl_errors_set(outside_error, U_ILLEGAL_ARGUMENT_ERROR, message, 1);
  135. efree(message);
  136. }
  137. zval_ptr_dtor_str(&local_zv_tz);
  138. return NULL;
  139. }
  140. timeZone = to->utimezone->clone();
  141. if (timeZone == NULL) {
  142. spprintf(&message, 0, "%s: could not clone TimeZone", func);
  143. if (message) {
  144. intl_errors_set(outside_error, U_MEMORY_ALLOCATION_ERROR, message, 1);
  145. efree(message);
  146. }
  147. zval_ptr_dtor_str(&local_zv_tz);
  148. return NULL;
  149. }
  150. } else if (Z_TYPE_P(zv_timezone) == IS_OBJECT &&
  151. instanceof_function(Z_OBJCE_P(zv_timezone), php_date_get_timezone_ce())) {
  152. php_timezone_obj *tzobj = Z_PHPTIMEZONE_P(zv_timezone);
  153. zval_ptr_dtor_str(&local_zv_tz);
  154. return timezone_convert_datetimezone(tzobj->type, tzobj, 0,
  155. outside_error, func);
  156. } else {
  157. UnicodeString id;
  158. UErrorCode status = U_ZERO_ERROR; /* outside_error may be NULL */
  159. if (!try_convert_to_string(zv_timezone)) {
  160. zval_ptr_dtor_str(&local_zv_tz);
  161. return NULL;
  162. }
  163. if (intl_stringFromChar(id, Z_STRVAL_P(zv_timezone), Z_STRLEN_P(zv_timezone),
  164. &status) == FAILURE) {
  165. spprintf(&message, 0, "%s: Time zone identifier given is not a "
  166. "valid UTF-8 string", func);
  167. if (message) {
  168. intl_errors_set(outside_error, status, message, 1);
  169. efree(message);
  170. }
  171. zval_ptr_dtor_str(&local_zv_tz);
  172. return NULL;
  173. }
  174. timeZone = TimeZone::createTimeZone(id);
  175. if (timeZone == NULL) {
  176. spprintf(&message, 0, "%s: Could not create time zone", func);
  177. if (message) {
  178. intl_errors_set(outside_error, U_MEMORY_ALLOCATION_ERROR, message, 1);
  179. efree(message);
  180. }
  181. zval_ptr_dtor_str(&local_zv_tz);
  182. return NULL;
  183. }
  184. if (*timeZone == TimeZone::getUnknown()) {
  185. spprintf(&message, 0, "%s: No such time zone: '%s'",
  186. func, Z_STRVAL_P(zv_timezone));
  187. if (message) {
  188. intl_errors_set(outside_error, U_ILLEGAL_ARGUMENT_ERROR, message, 1);
  189. efree(message);
  190. }
  191. zval_ptr_dtor_str(&local_zv_tz);
  192. delete timeZone;
  193. return NULL;
  194. }
  195. }
  196. zval_ptr_dtor_str(&local_zv_tz);
  197. return timeZone;
  198. }
  199. /* }}} */
  200. /* {{{ clone handler for TimeZone */
  201. static zend_object *TimeZone_clone_obj(zend_object *object)
  202. {
  203. TimeZone_object *to_orig,
  204. *to_new;
  205. zend_object *ret_val;
  206. intl_error_reset(NULL);
  207. to_orig = php_intl_timezone_fetch_object(object);
  208. intl_error_reset(TIMEZONE_ERROR_P(to_orig));
  209. ret_val = TimeZone_ce_ptr->create_object(object->ce);
  210. to_new = php_intl_timezone_fetch_object(ret_val);
  211. zend_objects_clone_members(&to_new->zo, &to_orig->zo);
  212. if (to_orig->utimezone != NULL) {
  213. TimeZone *newTimeZone;
  214. newTimeZone = to_orig->utimezone->clone();
  215. to_new->should_delete = 1;
  216. if (!newTimeZone) {
  217. zend_string *err_msg;
  218. intl_errors_set_code(TIMEZONE_ERROR_P(to_orig),
  219. U_MEMORY_ALLOCATION_ERROR);
  220. intl_errors_set_custom_msg(TIMEZONE_ERROR_P(to_orig),
  221. "Could not clone IntlTimeZone", 0);
  222. err_msg = intl_error_get_message(TIMEZONE_ERROR_P(to_orig));
  223. zend_throw_exception(NULL, ZSTR_VAL(err_msg), 0);
  224. zend_string_free(err_msg);
  225. } else {
  226. to_new->utimezone = newTimeZone;
  227. }
  228. } else {
  229. zend_throw_exception(NULL, "Cannot clone unconstructed IntlTimeZone", 0);
  230. }
  231. return ret_val;
  232. }
  233. /* }}} */
  234. /* {{{ compare_objects handler for TimeZone
  235. * Can't be used for >, >=, <, <= comparisons */
  236. static int TimeZone_compare_objects(zval *object1, zval *object2)
  237. {
  238. TimeZone_object *to1,
  239. *to2;
  240. ZEND_COMPARE_OBJECTS_FALLBACK(object1, object2);
  241. to1 = Z_INTL_TIMEZONE_P(object1);
  242. to2 = Z_INTL_TIMEZONE_P(object2);
  243. if (to1->utimezone == NULL || to2->utimezone == NULL) {
  244. zend_throw_exception(NULL, "Comparison with at least one unconstructed "
  245. "IntlTimeZone operand", 0);
  246. /* intentionally not returning */
  247. } else {
  248. if (*to1->utimezone == *to2->utimezone) {
  249. return 0;
  250. }
  251. }
  252. return ZEND_UNCOMPARABLE;
  253. }
  254. /* }}} */
  255. /* {{{ get_debug_info handler for TimeZone */
  256. static HashTable *TimeZone_get_debug_info(zend_object *object, int *is_temp)
  257. {
  258. zval zv;
  259. TimeZone_object *to;
  260. const TimeZone *tz;
  261. UnicodeString ustr;
  262. zend_string *u8str;
  263. HashTable *debug_info;
  264. UErrorCode uec = U_ZERO_ERROR;
  265. *is_temp = 1;
  266. debug_info = zend_new_array(8);
  267. to = php_intl_timezone_fetch_object(object);
  268. tz = to->utimezone;
  269. if (tz == NULL) {
  270. ZVAL_FALSE(&zv);
  271. zend_hash_str_update(debug_info, "valid", sizeof("valid") - 1, &zv);
  272. return debug_info;
  273. }
  274. ZVAL_TRUE(&zv);
  275. zend_hash_str_update(debug_info, "valid", sizeof("valid") - 1, &zv);
  276. tz->getID(ustr);
  277. u8str = intl_convert_utf16_to_utf8(
  278. ustr.getBuffer(), ustr.length(), &uec);
  279. if (!u8str) {
  280. return debug_info;
  281. }
  282. ZVAL_NEW_STR(&zv, u8str);
  283. zend_hash_str_update(debug_info, "id", sizeof("id") - 1, &zv);
  284. int32_t rawOffset, dstOffset;
  285. UDate now = Calendar::getNow();
  286. tz->getOffset(now, false, rawOffset, dstOffset, uec);
  287. if (U_FAILURE(uec)) {
  288. return debug_info;
  289. }
  290. ZVAL_LONG(&zv, (zend_long)rawOffset);
  291. zend_hash_str_update(debug_info,"rawOffset", sizeof("rawOffset") - 1, &zv);
  292. ZVAL_LONG(&zv, (zend_long)(rawOffset + dstOffset));
  293. zend_hash_str_update(debug_info,"currentOffset", sizeof("currentOffset") - 1, &zv);
  294. return debug_info;
  295. }
  296. /* }}} */
  297. /* {{{ void TimeZone_object_init(TimeZone_object* to)
  298. * Initialize internals of TImeZone_object not specific to zend standard objects.
  299. */
  300. static void TimeZone_object_init(TimeZone_object *to)
  301. {
  302. intl_error_init(TIMEZONE_ERROR_P(to));
  303. to->utimezone = NULL;
  304. to->should_delete = 0;
  305. }
  306. /* }}} */
  307. /* {{{ TimeZone_objects_free */
  308. static void TimeZone_objects_free(zend_object *object)
  309. {
  310. TimeZone_object* to = php_intl_timezone_fetch_object(object);
  311. if (to->utimezone && to->should_delete) {
  312. delete to->utimezone;
  313. to->utimezone = NULL;
  314. }
  315. intl_error_reset(TIMEZONE_ERROR_P(to));
  316. zend_object_std_dtor(&to->zo);
  317. }
  318. /* }}} */
  319. /* {{{ TimeZone_object_create */
  320. static zend_object *TimeZone_object_create(zend_class_entry *ce)
  321. {
  322. TimeZone_object* intern;
  323. intern = (TimeZone_object*)ecalloc(1, sizeof(TimeZone_object) + sizeof(zval) * (ce->default_properties_count - 1));
  324. zend_object_std_init(&intern->zo, ce);
  325. object_properties_init(&intern->zo, ce);
  326. TimeZone_object_init(intern);
  327. intern->zo.handlers = &TimeZone_handlers;
  328. return &intern->zo;
  329. }
  330. /* }}} */
  331. /* {{{ timezone_register_IntlTimeZone_class
  332. * Initialize 'IntlTimeZone' class
  333. */
  334. U_CFUNC void timezone_register_IntlTimeZone_class(void)
  335. {
  336. /* Create and register 'IntlTimeZone' class. */
  337. TimeZone_ce_ptr = register_class_IntlTimeZone();
  338. TimeZone_ce_ptr->create_object = TimeZone_object_create;
  339. memcpy(&TimeZone_handlers, &std_object_handlers,
  340. sizeof TimeZone_handlers);
  341. TimeZone_handlers.offset = XtOffsetOf(TimeZone_object, zo);
  342. TimeZone_handlers.clone_obj = TimeZone_clone_obj;
  343. TimeZone_handlers.compare = TimeZone_compare_objects;
  344. TimeZone_handlers.get_debug_info = TimeZone_get_debug_info;
  345. TimeZone_handlers.free_obj = TimeZone_objects_free;
  346. /* Declare 'IntlTimeZone' class constants */
  347. #define TIMEZONE_DECL_LONG_CONST(name, val) \
  348. zend_declare_class_constant_long(TimeZone_ce_ptr, name, sizeof(name) - 1, \
  349. val)
  350. TIMEZONE_DECL_LONG_CONST("DISPLAY_SHORT", TimeZone::SHORT);
  351. TIMEZONE_DECL_LONG_CONST("DISPLAY_LONG", TimeZone::LONG);
  352. TIMEZONE_DECL_LONG_CONST("DISPLAY_SHORT_GENERIC", TimeZone::SHORT_GENERIC);
  353. TIMEZONE_DECL_LONG_CONST("DISPLAY_LONG_GENERIC", TimeZone::LONG_GENERIC);
  354. TIMEZONE_DECL_LONG_CONST("DISPLAY_SHORT_GMT", TimeZone::SHORT_GMT);
  355. TIMEZONE_DECL_LONG_CONST("DISPLAY_LONG_GMT", TimeZone::LONG_GMT);
  356. TIMEZONE_DECL_LONG_CONST("DISPLAY_SHORT_COMMONLY_USED", TimeZone::SHORT_COMMONLY_USED);
  357. TIMEZONE_DECL_LONG_CONST("DISPLAY_GENERIC_LOCATION", TimeZone::GENERIC_LOCATION);
  358. TIMEZONE_DECL_LONG_CONST("TYPE_ANY", UCAL_ZONE_TYPE_ANY);
  359. TIMEZONE_DECL_LONG_CONST("TYPE_CANONICAL", UCAL_ZONE_TYPE_CANONICAL);
  360. TIMEZONE_DECL_LONG_CONST("TYPE_CANONICAL_LOCATION", UCAL_ZONE_TYPE_CANONICAL_LOCATION);
  361. /* Declare 'IntlTimeZone' class properties */
  362. }
  363. /* }}} */