1/* 2 +----------------------------------------------------------------------+ 3 | PHP Version 5 | 4 +----------------------------------------------------------------------+ 5 | This source file is subject to version 3.01 of the PHP license, | 6 | that is bundled with this package in the file LICENSE, and is | 7 | available through the world-wide-web at the following url: | 8 | http://www.php.net/license/3_01.txt | 9 | If you did not receive a copy of the PHP license and are unable to | 10 | obtain it through the world-wide-web, please send a note to | 11 | license@php.net so we can mail you a copy immediately. | 12 +----------------------------------------------------------------------+ 13 | Authors: Gustavo Lopes <cataphract@php.net> | 14 +----------------------------------------------------------------------+ 15*/ 16 17#include "../intl_cppshims.h" 18 19#include <unicode/calendar.h> 20#include <unicode/gregocal.h> 21#include <unicode/datefmt.h> 22#include <unicode/smpdtfmt.h> 23#include <unicode/locid.h> 24 25#include "../intl_convertcpp.h" 26 27extern "C" { 28#include "../php_intl.h" 29#include "../locale/locale.h" 30#define USE_CALENDAR_POINTER 1 31#include "../calendar/calendar_class.h" 32#include <ext/date/php_date.h> 33#include "../common/common_date.h" 34} 35 36static const DateFormat::EStyle valid_styles[] = { 37 DateFormat::kNone, 38 DateFormat::kFull, 39 DateFormat::kLong, 40 DateFormat::kMedium, 41 DateFormat::kShort, 42 DateFormat::kFullRelative, 43 DateFormat::kLongRelative, 44 DateFormat::kMediumRelative, 45 DateFormat::kShortRelative, 46}; 47 48static bool valid_format(zval **z) { 49 if (Z_TYPE_PP(z) == IS_LONG) { 50 long lval = Z_LVAL_PP(z); 51 for (int i = 0; i < sizeof(valid_styles) / sizeof(*valid_styles); i++) { 52 if ((long)valid_styles[i] == lval) { 53 return true; 54 } 55 } 56 } 57 58 return false; 59} 60 61U_CFUNC PHP_FUNCTION(datefmt_format_object) 62{ 63 zval *object, 64 **format = NULL; 65 const char *locale_str = NULL; 66 int locale_len; 67 bool pattern = false; 68 UDate date; 69 TimeZone *timeZone = NULL; 70 UErrorCode status = U_ZERO_ERROR; 71 DateFormat *df = NULL; 72 Calendar *cal = NULL; 73 DateFormat::EStyle dateStyle = DateFormat::kDefault, 74 timeStyle = DateFormat::kDefault; 75 76 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "o|Zs!", 77 &object, &format, &locale_str, &locale_len) == FAILURE) { 78 RETURN_FALSE; 79 } 80 81 if (!locale_str) { 82 locale_str = intl_locale_get_default(TSRMLS_C); 83 } 84 85 if (format == NULL || Z_TYPE_PP(format) == IS_NULL) { 86 //nothing 87 } else if (Z_TYPE_PP(format) == IS_ARRAY) { 88 HashTable *ht = Z_ARRVAL_PP(format); 89 HashPosition pos = {0}; 90 zval **z; 91 if (zend_hash_num_elements(ht) != 2) { 92 intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR, 93 "datefmt_format_object: bad format; if array, it must have " 94 "two elements", 0 TSRMLS_CC); 95 RETURN_FALSE; 96 } 97 98 zend_hash_internal_pointer_reset_ex(ht, &pos); 99 zend_hash_get_current_data_ex(ht, (void**)&z, &pos); 100 if (!valid_format(z)) { 101 intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR, 102 "datefmt_format_object: bad format; the date format (first " 103 "element of the array) is not valid", 0 TSRMLS_CC); 104 RETURN_FALSE; 105 } 106 dateStyle = (DateFormat::EStyle)Z_LVAL_PP(z); 107 108 zend_hash_move_forward_ex(ht, &pos); 109 zend_hash_get_current_data_ex(ht, (void**)&z, &pos); 110 if (!valid_format(z)) { 111 intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR, 112 "datefmt_format_object: bad format; the time format (" 113 "second element of the array) is not valid", 0 TSRMLS_CC); 114 RETURN_FALSE; 115 } 116 timeStyle = (DateFormat::EStyle)Z_LVAL_PP(z); 117 } else if (Z_TYPE_PP(format) == IS_LONG) { 118 if (!valid_format(format)) { 119 intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR, 120 "datefmt_format_object: the date/time format type is invalid", 121 0 TSRMLS_CC); 122 RETURN_FALSE; 123 } 124 dateStyle = timeStyle = (DateFormat::EStyle)Z_LVAL_PP(format); 125 } else { 126 convert_to_string_ex(format); 127 if (Z_STRLEN_PP(format) == 0) { 128 intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR, 129 "datefmt_format_object: the format is empty", 0 TSRMLS_CC); 130 RETURN_FALSE; 131 } 132 pattern = true; 133 } 134 135 //there's no support for relative time in ICU yet 136 timeStyle = (DateFormat::EStyle)(timeStyle & ~DateFormat::kRelative); 137 138 zend_class_entry *instance_ce = Z_OBJCE_P(object); 139 if (instanceof_function(instance_ce, Calendar_ce_ptr TSRMLS_CC)) { 140 Calendar *obj_cal = calendar_fetch_native_calendar(object TSRMLS_CC); 141 if (obj_cal == NULL) { 142 intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR, 143 "datefmt_format_object: bad IntlCalendar instance: " 144 "not initialized properly", 0 TSRMLS_CC); 145 RETURN_FALSE; 146 } 147 timeZone = obj_cal->getTimeZone().clone(); 148 date = obj_cal->getTime(status); 149 if (U_FAILURE(status)) { 150 intl_error_set(NULL, status, 151 "datefmt_format_object: error obtaining instant from " 152 "IntlCalendar", 0 TSRMLS_CC); 153 RETVAL_FALSE; 154 goto cleanup; 155 } 156 cal = obj_cal->clone(); 157 } else if (instanceof_function(instance_ce, php_date_get_date_ce() TSRMLS_CC)) { 158 if (intl_datetime_decompose(object, &date, &timeZone, NULL, 159 "datefmt_format_object" TSRMLS_CC) == FAILURE) { 160 RETURN_FALSE; 161 } 162 cal = new GregorianCalendar(Locale::createFromName(locale_str), status); 163 if (U_FAILURE(status)) { 164 intl_error_set(NULL, status, 165 "datefmt_format_object: could not create GregorianCalendar", 166 0 TSRMLS_CC); 167 RETVAL_FALSE; 168 goto cleanup; 169 } 170 } else { 171 intl_error_set(NULL, status, "datefmt_format_object: the passed object " 172 "must be an instance of either IntlCalendar or DateTime", 173 0 TSRMLS_CC); 174 RETURN_FALSE; 175 } 176 177 if (pattern) { 178 df = new SimpleDateFormat( 179 UnicodeString(Z_STRVAL_PP(format), Z_STRLEN_PP(format), 180 UnicodeString::kInvariant), 181 Locale::createFromName(locale_str), 182 status); 183 184 if (U_FAILURE(status)) { 185 intl_error_set(NULL, status, 186 "datefmt_format_object: could not create SimpleDateFormat", 187 0 TSRMLS_CC); 188 RETVAL_FALSE; 189 goto cleanup; 190 } 191 } else { 192 df = DateFormat::createDateTimeInstance(dateStyle, timeStyle, 193 Locale::createFromName(locale_str)); 194 195 if (df == NULL) { /* according to ICU sources, this should never happen */ 196 intl_error_set(NULL, status, 197 "datefmt_format_object: could not create DateFormat", 198 0 TSRMLS_CC); 199 RETVAL_FALSE; 200 goto cleanup; 201 } 202 } 203 204 //must be in this order (or have the cal adopt the tz) 205 df->adoptCalendar(cal); 206 cal = NULL; 207 df->adoptTimeZone(timeZone); 208 timeZone = NULL; 209 210 { 211 UnicodeString result = UnicodeString(); 212 df->format(date, result); 213 214 Z_TYPE_P(return_value) = IS_STRING; 215 if (intl_charFromString(result, &Z_STRVAL_P(return_value), 216 &Z_STRLEN_P(return_value), &status) == FAILURE) { 217 intl_error_set(NULL, status, 218 "datefmt_format_object: error converting result to UTF-8", 219 0 TSRMLS_CC); 220 RETVAL_FALSE; 221 goto cleanup; 222 } 223 } 224 225 226cleanup: 227 delete df; 228 delete timeZone; 229 delete cal; 230} 231