msgformat_helpers.cpp 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714
  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: Stanislav Malyshev <stas@zend.com> |
  14. +----------------------------------------------------------------------+
  15. */
  16. #ifdef HAVE_CONFIG_H
  17. #include "config.h"
  18. #endif
  19. #include "../intl_cppshims.h"
  20. #include <limits.h>
  21. #include <unicode/msgfmt.h>
  22. #include <unicode/chariter.h>
  23. #include <unicode/ustdio.h>
  24. #include <unicode/timezone.h>
  25. #include <unicode/datefmt.h>
  26. #include <unicode/calendar.h>
  27. #include <vector>
  28. #include "../intl_convertcpp.h"
  29. #include "../common/common_date.h"
  30. extern "C" {
  31. #include "php_intl.h"
  32. #include "msgformat_class.h"
  33. #include "msgformat_format.h"
  34. #include "msgformat_helpers.h"
  35. #include "intl_convert.h"
  36. #define USE_TIMEZONE_POINTER
  37. #include "../timezone/timezone_class.h"
  38. }
  39. #if U_ICU_VERSION_MAJOR_NUM * 10 + U_ICU_VERSION_MINOR_NUM >= 48
  40. #define HAS_MESSAGE_PATTERN 1
  41. #endif
  42. U_NAMESPACE_BEGIN
  43. /**
  44. * This class isolates our access to private internal methods of
  45. * MessageFormat. It is never instantiated; it exists only for C++
  46. * access management.
  47. */
  48. class MessageFormatAdapter {
  49. public:
  50. static const Formattable::Type* getArgTypeList(const MessageFormat& m,
  51. int32_t& count);
  52. #ifdef HAS_MESSAGE_PATTERN
  53. static const MessagePattern getMessagePattern(MessageFormat* m);
  54. #endif
  55. };
  56. const Formattable::Type*
  57. MessageFormatAdapter::getArgTypeList(const MessageFormat& m,
  58. int32_t& count) {
  59. return m.getArgTypeList(count);
  60. }
  61. #ifdef HAS_MESSAGE_PATTERN
  62. const MessagePattern
  63. MessageFormatAdapter::getMessagePattern(MessageFormat* m) {
  64. return m->msgPattern;
  65. }
  66. #endif
  67. U_NAMESPACE_END
  68. U_CFUNC int32_t umsg_format_arg_count(UMessageFormat *fmt)
  69. {
  70. int32_t fmt_count = 0;
  71. MessageFormatAdapter::getArgTypeList(*(const MessageFormat*)fmt, fmt_count);
  72. return fmt_count;
  73. }
  74. static HashTable *umsg_get_numeric_types(MessageFormatter_object *mfo,
  75. intl_error& err TSRMLS_DC)
  76. {
  77. HashTable *ret;
  78. int32_t parts_count;
  79. if (U_FAILURE(err.code)) {
  80. return NULL;
  81. }
  82. if (mfo->mf_data.arg_types) {
  83. /* already cached */
  84. return mfo->mf_data.arg_types;
  85. }
  86. const Formattable::Type *types = MessageFormatAdapter::getArgTypeList(
  87. *(MessageFormat*)mfo->mf_data.umsgf, parts_count);
  88. /* Hash table will store Formattable::Type objects directly,
  89. * so no need for destructor */
  90. ALLOC_HASHTABLE(ret);
  91. zend_hash_init(ret, parts_count, NULL, NULL, 0);
  92. for (int i = 0; i < parts_count; i++) {
  93. const Formattable::Type t = types[i];
  94. if (zend_hash_index_update(ret, (ulong)i, (void*)&t, sizeof(t), NULL)
  95. == FAILURE) {
  96. intl_errors_set(&err, U_MEMORY_ALLOCATION_ERROR,
  97. "Write to argument types hash table failed", 0 TSRMLS_CC);
  98. break;
  99. }
  100. }
  101. if (U_FAILURE(err.code)) {
  102. zend_hash_destroy(ret);
  103. efree(ret);
  104. return NULL;
  105. }
  106. mfo->mf_data.arg_types = ret;
  107. return ret;
  108. }
  109. #ifdef HAS_MESSAGE_PATTERN
  110. static HashTable *umsg_parse_format(MessageFormatter_object *mfo,
  111. const MessagePattern& mp,
  112. intl_error& err TSRMLS_DC)
  113. {
  114. HashTable *ret;
  115. int32_t parts_count;
  116. if (U_FAILURE(err.code)) {
  117. return NULL;
  118. }
  119. if (!((MessageFormat *)mfo->mf_data.umsgf)->usesNamedArguments()) {
  120. return umsg_get_numeric_types(mfo, err TSRMLS_CC);
  121. }
  122. if (mfo->mf_data.arg_types) {
  123. /* already cached */
  124. return mfo->mf_data.arg_types;
  125. }
  126. /* Hash table will store Formattable::Type objects directly,
  127. * so no need for destructor */
  128. ALLOC_HASHTABLE(ret);
  129. zend_hash_init(ret, 32, NULL, NULL, 0);
  130. parts_count = mp.countParts();
  131. // See MessageFormat::cacheExplicitFormats()
  132. /*
  133. * Looking through the pattern, go to each arg_start part type.
  134. * The arg-typeof that tells us the argument type (simple, complicated)
  135. * then the next part is either the arg_name or arg number
  136. * and then if it's simple after that there could be a part-type=arg-type
  137. * while substring will tell us number, spellout, etc.
  138. * If the next thing isn't an arg-type then assume string.
  139. */
  140. /* The last two "parts" can at most be ARG_LIMIT and MSG_LIMIT
  141. * which we need not examine. */
  142. for (int32_t i = 0; i < parts_count - 2 && U_SUCCESS(err.code); i++) {
  143. MessagePattern::Part p = mp.getPart(i);
  144. if (p.getType() != UMSGPAT_PART_TYPE_ARG_START) {
  145. continue;
  146. }
  147. MessagePattern::Part name_part = mp.getPart(++i); /* Getting name, advancing i */
  148. Formattable::Type type,
  149. *storedType;
  150. if (name_part.getType() == UMSGPAT_PART_TYPE_ARG_NAME) {
  151. UnicodeString argName = mp.getSubstring(name_part);
  152. if (zend_hash_find(ret, (char*)argName.getBuffer(), argName.length(),
  153. (void**)&storedType) == FAILURE) {
  154. /* not found already; create new entry in HT */
  155. Formattable::Type bogusType = Formattable::kObject;
  156. if (zend_hash_update(ret, (char*)argName.getBuffer(), argName.length(),
  157. (void*)&bogusType, sizeof(bogusType), (void**)&storedType) == FAILURE) {
  158. intl_errors_set(&err, U_MEMORY_ALLOCATION_ERROR,
  159. "Write to argument types hash table failed", 0 TSRMLS_CC);
  160. continue;
  161. }
  162. }
  163. } else if (name_part.getType() == UMSGPAT_PART_TYPE_ARG_NUMBER) {
  164. int32_t argNumber = name_part.getValue();
  165. if (argNumber < 0) {
  166. intl_errors_set(&err, U_INVALID_FORMAT_ERROR,
  167. "Found part with negative number", 0 TSRMLS_CC);
  168. continue;
  169. }
  170. if (zend_hash_index_find(ret, (ulong)argNumber, (void**)&storedType)
  171. == FAILURE) {
  172. /* not found already; create new entry in HT */
  173. Formattable::Type bogusType = Formattable::kObject;
  174. if (zend_hash_index_update(ret, (ulong)argNumber, (void*)&bogusType,
  175. sizeof(bogusType), (void**)&storedType) == FAILURE) {
  176. intl_errors_set(&err, U_MEMORY_ALLOCATION_ERROR,
  177. "Write to argument types hash table failed", 0 TSRMLS_CC);
  178. continue;
  179. }
  180. }
  181. } else {
  182. intl_errors_set(&err, U_INVALID_FORMAT_ERROR, "Invalid part type encountered", 0 TSRMLS_CC);
  183. continue;
  184. }
  185. UMessagePatternArgType argType = p.getArgType();
  186. /* No type specified, treat it as a string */
  187. if (argType == UMSGPAT_ARG_TYPE_NONE) {
  188. type = Formattable::kString;
  189. } else { /* Some type was specified, might be simple or complicated */
  190. if (argType == UMSGPAT_ARG_TYPE_SIMPLE) {
  191. /* For a SIMPLE arg, after the name part, there should be
  192. * an ARG_TYPE part whose string value tells us what to do */
  193. MessagePattern::Part type_part = mp.getPart(++i); /* Getting type, advancing i */
  194. if (type_part.getType() == UMSGPAT_PART_TYPE_ARG_TYPE) {
  195. UnicodeString typeString = mp.getSubstring(type_part);
  196. /* This is all based on the rules in the docs for MessageFormat
  197. * @see http://icu-project.org/apiref/icu4c/classMessageFormat.html */
  198. if (typeString == "number") {
  199. MessagePattern::Part style_part = mp.getPart(i + 1); /* Not advancing i */
  200. if (style_part.getType() == UMSGPAT_PART_TYPE_ARG_STYLE) {
  201. UnicodeString styleString = mp.getSubstring(style_part);
  202. if (styleString == "integer") {
  203. type = Formattable::kInt64;
  204. } else if (styleString == "currency") {
  205. type = Formattable::kDouble;
  206. } else if (styleString == "percent") {
  207. type = Formattable::kDouble;
  208. } else { /* some style invalid/unknown to us */
  209. type = Formattable::kDouble;
  210. }
  211. } else { // if missing style, part, make it a double
  212. type = Formattable::kDouble;
  213. }
  214. } else if ((typeString == "date") || (typeString == "time")) {
  215. type = Formattable::kDate;
  216. } else if ((typeString == "spellout") || (typeString == "ordinal")
  217. || (typeString == "duration")) {
  218. type = Formattable::kDouble;
  219. }
  220. } else {
  221. /* If there's no UMSGPAT_PART_TYPE_ARG_TYPE right after a
  222. * UMSGPAT_ARG_TYPE_SIMPLE argument, then the pattern
  223. * is broken. */
  224. intl_errors_set(&err, U_PARSE_ERROR,
  225. "Expected UMSGPAT_PART_TYPE_ARG_TYPE part following "
  226. "UMSGPAT_ARG_TYPE_SIMPLE part", 0 TSRMLS_CC);
  227. continue;
  228. }
  229. } else if (argType == UMSGPAT_ARG_TYPE_PLURAL) {
  230. type = Formattable::kDouble;
  231. } else if (argType == UMSGPAT_ARG_TYPE_CHOICE) {
  232. type = Formattable::kDouble;
  233. } else if (argType == UMSGPAT_ARG_TYPE_SELECT) {
  234. type = Formattable::kString;
  235. #if U_ICU_VERSION_MAJOR_NUM >= 50
  236. } else if (argType == UMSGPAT_ARG_TYPE_SELECTORDINAL) {
  237. type = Formattable::kDouble;
  238. #endif
  239. } else {
  240. type = Formattable::kString;
  241. }
  242. } /* was type specified? */
  243. /* We found a different type for the same arg! */
  244. if (*storedType != Formattable::kObject && *storedType != type) {
  245. intl_errors_set(&err, U_ARGUMENT_TYPE_MISMATCH,
  246. "Inconsistent types declared for an argument", 0 TSRMLS_CC);
  247. continue;
  248. }
  249. *storedType = type;
  250. } /* visiting each part */
  251. if (U_FAILURE(err.code)) {
  252. zend_hash_destroy(ret);
  253. efree(ret);
  254. return NULL;
  255. }
  256. mfo->mf_data.arg_types = ret;
  257. return ret;
  258. }
  259. #endif
  260. static HashTable *umsg_get_types(MessageFormatter_object *mfo,
  261. intl_error& err TSRMLS_DC)
  262. {
  263. MessageFormat *mf = (MessageFormat *)mfo->mf_data.umsgf;
  264. #ifdef HAS_MESSAGE_PATTERN
  265. const MessagePattern mp = MessageFormatAdapter::getMessagePattern(mf);
  266. return umsg_parse_format(mfo, mp, err TSRMLS_CC);
  267. #else
  268. if (mf->usesNamedArguments()) {
  269. intl_errors_set(&err, U_UNSUPPORTED_ERROR,
  270. "This extension supports named arguments only on ICU 4.8+",
  271. 0 TSRMLS_CC);
  272. return NULL;
  273. }
  274. return umsg_get_numeric_types(mfo, err TSRMLS_CC);
  275. #endif
  276. }
  277. static void umsg_set_timezone(MessageFormatter_object *mfo,
  278. intl_error& err TSRMLS_DC)
  279. {
  280. MessageFormat *mf = (MessageFormat *)mfo->mf_data.umsgf;
  281. TimeZone *used_tz = NULL;
  282. const Format **formats;
  283. int32_t count;
  284. /* Unfortanely, this cannot change the time zone for arguments that
  285. * appear inside complex formats because ::getFormats() returns NULL
  286. * for all uncached formats, which is the case for complex formats
  287. * unless they were set via one of the ::setFormat() methods */
  288. if (mfo->mf_data.tz_set) {
  289. return; /* already done */
  290. }
  291. formats = mf->getFormats(count);
  292. if (formats == NULL) {
  293. intl_errors_set(&err, U_MEMORY_ALLOCATION_ERROR,
  294. "Out of memory retrieving subformats", 0 TSRMLS_CC);
  295. }
  296. for (int i = 0; U_SUCCESS(err.code) && i < count; i++) {
  297. DateFormat* df = dynamic_cast<DateFormat*>(
  298. const_cast<Format *>(formats[i]));
  299. if (df == NULL) {
  300. continue;
  301. }
  302. if (used_tz == NULL) {
  303. zval nullzv = zval_used_for_init,
  304. *zvptr = &nullzv;
  305. used_tz = timezone_process_timezone_argument(&zvptr, &err,
  306. "msgfmt_format" TSRMLS_CC);
  307. if (used_tz == NULL) {
  308. continue;
  309. }
  310. }
  311. df->setTimeZone(*used_tz);
  312. }
  313. if (U_SUCCESS(err.code)) {
  314. mfo->mf_data.tz_set = 1;
  315. }
  316. }
  317. U_CFUNC void umsg_format_helper(MessageFormatter_object *mfo,
  318. HashTable *args,
  319. UChar **formatted,
  320. int *formatted_len TSRMLS_DC)
  321. {
  322. int arg_count = zend_hash_num_elements(args);
  323. std::vector<Formattable> fargs;
  324. std::vector<UnicodeString> farg_names;
  325. MessageFormat *mf = (MessageFormat *)mfo->mf_data.umsgf;
  326. HashTable *types;
  327. intl_error& err = INTL_DATA_ERROR(mfo);
  328. if (U_FAILURE(err.code)) {
  329. return;
  330. }
  331. types = umsg_get_types(mfo, err TSRMLS_CC);
  332. umsg_set_timezone(mfo, err TSRMLS_CC);
  333. fargs.resize(arg_count);
  334. farg_names.resize(arg_count);
  335. int argNum = 0;
  336. HashPosition pos;
  337. zval **elem;
  338. // Key related variables
  339. int key_type;
  340. char *str_index;
  341. uint str_len;
  342. ulong num_index;
  343. for (zend_hash_internal_pointer_reset_ex(args, &pos);
  344. U_SUCCESS(err.code) &&
  345. (key_type = zend_hash_get_current_key_ex(
  346. args, &str_index, &str_len, &num_index, 0, &pos),
  347. zend_hash_get_current_data_ex(args, (void **)&elem, &pos)
  348. ) == SUCCESS;
  349. zend_hash_move_forward_ex(args, &pos), argNum++)
  350. {
  351. Formattable& formattable = fargs[argNum];
  352. UnicodeString& key = farg_names[argNum];
  353. Formattable::Type argType = Formattable::kObject, //unknown
  354. *storedArgType = NULL;
  355. /* Process key and retrieve type */
  356. if (key_type == HASH_KEY_IS_LONG) {
  357. /* includes case where index < 0 because it's exposed as unsigned */
  358. if (num_index > (ulong)INT32_MAX) {
  359. intl_errors_set(&err, U_ILLEGAL_ARGUMENT_ERROR,
  360. "Found negative or too large array key", 0 TSRMLS_CC);
  361. continue;
  362. }
  363. UChar temp[16];
  364. int32_t len = u_sprintf(temp, "%u", (uint32_t)num_index);
  365. key.append(temp, len);
  366. zend_hash_index_find(types, (ulong)num_index, (void**)&storedArgType);
  367. } else { //string; assumed to be in UTF-8
  368. intl_stringFromChar(key, str_index, str_len-1, &err.code);
  369. if (U_FAILURE(err.code)) {
  370. char *message;
  371. spprintf(&message, 0,
  372. "Invalid UTF-8 data in argument key: '%s'", str_index);
  373. intl_errors_set(&err, err.code, message, 1 TSRMLS_CC);
  374. efree(message);
  375. continue;
  376. }
  377. zend_hash_find(types, (char*)key.getBuffer(), key.length(),
  378. (void**)&storedArgType);
  379. }
  380. if (storedArgType != NULL) {
  381. argType = *storedArgType;
  382. }
  383. /* Convert zval to formattable according to message format type
  384. * or (as a fallback) the zval type */
  385. if (argType != Formattable::kObject) {
  386. switch (argType) {
  387. case Formattable::kString:
  388. {
  389. string_arg:
  390. /* This implicitly converts objects
  391. * Note that our vectors will leak if object conversion fails
  392. * and PHP ends up with a fatal error and calls longjmp
  393. * as a result of that.
  394. */
  395. convert_to_string_ex(elem);
  396. UnicodeString *text = new UnicodeString();
  397. intl_stringFromChar(*text,
  398. Z_STRVAL_PP(elem), Z_STRLEN_PP(elem), &err.code);
  399. if (U_FAILURE(err.code)) {
  400. char *message;
  401. spprintf(&message, 0, "Invalid UTF-8 data in string argument: "
  402. "'%s'", Z_STRVAL_PP(elem));
  403. intl_errors_set(&err, err.code, message, 1 TSRMLS_CC);
  404. efree(message);
  405. delete text;
  406. continue;
  407. }
  408. formattable.adoptString(text);
  409. break;
  410. }
  411. case Formattable::kDouble:
  412. {
  413. double d;
  414. if (Z_TYPE_PP(elem) == IS_DOUBLE) {
  415. d = Z_DVAL_PP(elem);
  416. } else if (Z_TYPE_PP(elem) == IS_LONG) {
  417. d = (double)Z_LVAL_PP(elem);
  418. } else {
  419. SEPARATE_ZVAL_IF_NOT_REF(elem);
  420. convert_scalar_to_number(*elem TSRMLS_CC);
  421. d = (Z_TYPE_PP(elem) == IS_DOUBLE)
  422. ? Z_DVAL_PP(elem)
  423. : (double)Z_LVAL_PP(elem);
  424. }
  425. formattable.setDouble(d);
  426. break;
  427. }
  428. case Formattable::kLong:
  429. {
  430. int32_t tInt32 = 0;
  431. retry_klong:
  432. if (Z_TYPE_PP(elem) == IS_DOUBLE) {
  433. if (Z_DVAL_PP(elem) > (double)INT32_MAX ||
  434. Z_DVAL_PP(elem) < (double)INT32_MIN) {
  435. intl_errors_set(&err, U_ILLEGAL_ARGUMENT_ERROR,
  436. "Found PHP float with absolute value too large for "
  437. "32 bit integer argument", 0 TSRMLS_CC);
  438. } else {
  439. tInt32 = (int32_t)Z_DVAL_PP(elem);
  440. }
  441. } else if (Z_TYPE_PP(elem) == IS_LONG) {
  442. if (Z_LVAL_PP(elem) > INT32_MAX ||
  443. Z_LVAL_PP(elem) < INT32_MIN) {
  444. intl_errors_set(&err, U_ILLEGAL_ARGUMENT_ERROR,
  445. "Found PHP integer with absolute value too large "
  446. "for 32 bit integer argument", 0 TSRMLS_CC);
  447. } else {
  448. tInt32 = (int32_t)Z_LVAL_PP(elem);
  449. }
  450. } else {
  451. SEPARATE_ZVAL_IF_NOT_REF(elem);
  452. convert_scalar_to_number(*elem TSRMLS_CC);
  453. goto retry_klong;
  454. }
  455. formattable.setLong(tInt32);
  456. break;
  457. }
  458. case Formattable::kInt64:
  459. {
  460. int64_t tInt64 = 0;
  461. retry_kint64:
  462. if (Z_TYPE_PP(elem) == IS_DOUBLE) {
  463. if (Z_DVAL_PP(elem) > (double)U_INT64_MAX ||
  464. Z_DVAL_PP(elem) < (double)U_INT64_MIN) {
  465. intl_errors_set(&err, U_ILLEGAL_ARGUMENT_ERROR,
  466. "Found PHP float with absolute value too large for "
  467. "64 bit integer argument", 0 TSRMLS_CC);
  468. } else {
  469. tInt64 = (int64_t)Z_DVAL_PP(elem);
  470. }
  471. } else if (Z_TYPE_PP(elem) == IS_LONG) {
  472. /* assume long is not wider than 64 bits */
  473. tInt64 = (int64_t)Z_LVAL_PP(elem);
  474. } else {
  475. SEPARATE_ZVAL_IF_NOT_REF(elem);
  476. convert_scalar_to_number(*elem TSRMLS_CC);
  477. goto retry_kint64;
  478. }
  479. formattable.setInt64(tInt64);
  480. break;
  481. }
  482. case Formattable::kDate:
  483. {
  484. double dd = intl_zval_to_millis(*elem, &err, "msgfmt_format" TSRMLS_CC);
  485. if (U_FAILURE(err.code)) {
  486. char *message, *key_char;
  487. int key_len;
  488. UErrorCode status = UErrorCode();
  489. if (intl_charFromString(key, &key_char, &key_len,
  490. &status) == SUCCESS) {
  491. spprintf(&message, 0, "The argument for key '%s' "
  492. "cannot be used as a date or time", key_char);
  493. intl_errors_set(&err, err.code, message, 1 TSRMLS_CC);
  494. efree(key_char);
  495. efree(message);
  496. }
  497. continue;
  498. }
  499. formattable.setDate(dd);
  500. break;
  501. }
  502. default:
  503. intl_errors_set(&err, U_ILLEGAL_ARGUMENT_ERROR,
  504. "Found unsupported argument type", 0 TSRMLS_CC);
  505. break;
  506. }
  507. } else {
  508. /* We couldn't find any information about the argument in the pattern, this
  509. * means it's an extra argument. So convert it to a number if it's a number or
  510. * bool or null and to a string if it's anything else except arrays . */
  511. switch (Z_TYPE_PP(elem)) {
  512. case IS_DOUBLE:
  513. formattable.setDouble(Z_DVAL_PP(elem));
  514. break;
  515. case IS_BOOL:
  516. convert_to_long_ex(elem);
  517. /* Intentional fallthrough */
  518. case IS_LONG:
  519. formattable.setInt64((int64_t)Z_LVAL_PP(elem));
  520. break;
  521. case IS_NULL:
  522. formattable.setInt64((int64_t)0);
  523. break;
  524. case IS_STRING:
  525. case IS_OBJECT:
  526. goto string_arg;
  527. default:
  528. {
  529. char *message, *key_char;
  530. int key_len;
  531. UErrorCode status = UErrorCode();
  532. if (intl_charFromString(key, &key_char, &key_len,
  533. &status) == SUCCESS) {
  534. spprintf(&message, 0, "No strategy to convert the "
  535. "value given for the argument with key '%s' "
  536. "is available", key_char);
  537. intl_errors_set(&err,
  538. U_ILLEGAL_ARGUMENT_ERROR, message, 1 TSRMLS_CC);
  539. efree(key_char);
  540. efree(message);
  541. }
  542. }
  543. }
  544. }
  545. } // visiting each argument
  546. if (U_FAILURE(err.code)) {
  547. return;
  548. }
  549. UnicodeString resultStr;
  550. FieldPosition fieldPosition(0);
  551. /* format the message */
  552. mf->format(farg_names.empty() ? NULL : &farg_names[0],
  553. fargs.empty() ? NULL : &fargs[0], arg_count, resultStr, err.code);
  554. if (U_FAILURE(err.code)) {
  555. intl_errors_set(&err, err.code,
  556. "Call to ICU MessageFormat::format() has failed", 0 TSRMLS_CC);
  557. return;
  558. }
  559. *formatted_len = resultStr.length();
  560. *formatted = eumalloc(*formatted_len+1);
  561. resultStr.extract(*formatted, *formatted_len+1, err.code);
  562. if (U_FAILURE(err.code)) {
  563. intl_errors_set(&err, err.code,
  564. "Error copying format() result", 0 TSRMLS_CC);
  565. return;
  566. }
  567. }
  568. #define cleanup_zvals() for(int j=i;j>=0;j--) { zval_ptr_dtor((*args)+i); }
  569. U_CFUNC void umsg_parse_helper(UMessageFormat *fmt, int *count, zval ***args, UChar *source, int source_len, UErrorCode *status)
  570. {
  571. UnicodeString srcString(source, source_len);
  572. Formattable *fargs = ((const MessageFormat*)fmt)->parse(srcString, *count, *status);
  573. if(U_FAILURE(*status)) {
  574. return;
  575. }
  576. *args = (zval **)safe_emalloc(*count, sizeof(zval *), 0);
  577. // assign formattables to varargs
  578. for(int32_t i = 0; i < *count; i++) {
  579. int64_t aInt64;
  580. double aDate;
  581. UnicodeString temp;
  582. char *stmp;
  583. int stmp_len;
  584. ALLOC_INIT_ZVAL((*args)[i]);
  585. switch(fargs[i].getType()) {
  586. case Formattable::kDate:
  587. aDate = ((double)fargs[i].getDate())/U_MILLIS_PER_SECOND;
  588. ZVAL_DOUBLE((*args)[i], aDate);
  589. break;
  590. case Formattable::kDouble:
  591. ZVAL_DOUBLE((*args)[i], (double)fargs[i].getDouble());
  592. break;
  593. case Formattable::kLong:
  594. ZVAL_LONG((*args)[i], fargs[i].getLong());
  595. break;
  596. case Formattable::kInt64:
  597. aInt64 = fargs[i].getInt64();
  598. if(aInt64 > LONG_MAX || aInt64 < -LONG_MAX) {
  599. ZVAL_DOUBLE((*args)[i], (double)aInt64);
  600. } else {
  601. ZVAL_LONG((*args)[i], (long)aInt64);
  602. }
  603. break;
  604. case Formattable::kString:
  605. fargs[i].getString(temp);
  606. intl_convert_utf16_to_utf8(&stmp, &stmp_len, temp.getBuffer(), temp.length(), status);
  607. if(U_FAILURE(*status)) {
  608. cleanup_zvals();
  609. return;
  610. }
  611. ZVAL_STRINGL((*args)[i], stmp, stmp_len, 0);
  612. break;
  613. case Formattable::kObject:
  614. case Formattable::kArray:
  615. *status = U_ILLEGAL_ARGUMENT_ERROR;
  616. cleanup_zvals();
  617. break;
  618. }
  619. }
  620. delete[] fargs;
  621. }
  622. /*
  623. * Local variables:
  624. * tab-width: 4
  625. * c-basic-offset: 4
  626. * End:
  627. * vim600: noet sw=4 ts=4 fdm=marker
  628. * vim<600: noet sw=4 ts=4
  629. */