msgformat_helpers.cpp 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713
  1. /*
  2. +----------------------------------------------------------------------+
  3. | PHP Version 7 |
  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 <unicode/strenum.h>
  28. #include <vector>
  29. #include "../intl_convertcpp.h"
  30. #include "../common/common_date.h"
  31. extern "C" {
  32. #include "php_intl.h"
  33. #include "msgformat_class.h"
  34. #include "msgformat_format.h"
  35. #include "msgformat_helpers.h"
  36. #include "intl_convert.h"
  37. #define USE_TIMEZONE_POINTER
  38. #include "../timezone/timezone_class.h"
  39. }
  40. #if U_ICU_VERSION_MAJOR_NUM * 10 + U_ICU_VERSION_MINOR_NUM >= 48
  41. #define HAS_MESSAGE_PATTERN 1
  42. #define HAS_MISALLOCATE_MEMORY_BUG 1
  43. #endif
  44. U_NAMESPACE_BEGIN
  45. /**
  46. * This class isolates our access to private internal methods of
  47. * MessageFormat. It is never instantiated; it exists only for C++
  48. * access management.
  49. */
  50. class MessageFormatAdapter {
  51. public:
  52. static const Formattable::Type* getArgTypeList(const MessageFormat& m,
  53. int32_t& count);
  54. #ifdef HAS_MESSAGE_PATTERN
  55. static const MessagePattern getMessagePattern(MessageFormat* m);
  56. #endif
  57. };
  58. const Formattable::Type*
  59. MessageFormatAdapter::getArgTypeList(const MessageFormat& m,
  60. int32_t& count) {
  61. return m.getArgTypeList(count);
  62. }
  63. #ifdef HAS_MESSAGE_PATTERN
  64. const MessagePattern
  65. MessageFormatAdapter::getMessagePattern(MessageFormat* m) {
  66. return m->msgPattern;
  67. }
  68. #endif
  69. U_NAMESPACE_END
  70. using icu::Formattable;
  71. using icu::Format;
  72. using icu::DateFormat;
  73. using icu::MessageFormat;
  74. #ifdef HAS_MESSAGE_PATTERN
  75. using icu::MessagePattern;
  76. #endif
  77. using icu::MessageFormatAdapter;
  78. using icu::FieldPosition;
  79. U_CFUNC int32_t umsg_format_arg_count(UMessageFormat *fmt)
  80. {
  81. int32_t fmt_count = 0;
  82. MessageFormatAdapter::getArgTypeList(*(const MessageFormat*)fmt, fmt_count);
  83. return fmt_count;
  84. }
  85. static void arg_types_dtor(zval *el) {
  86. efree(Z_PTR_P(el));
  87. }
  88. static HashTable *umsg_get_numeric_types(MessageFormatter_object *mfo,
  89. intl_error& err)
  90. {
  91. HashTable *ret;
  92. int32_t parts_count;
  93. if (U_FAILURE(err.code)) {
  94. return NULL;
  95. }
  96. if (mfo->mf_data.arg_types) {
  97. /* already cached */
  98. return mfo->mf_data.arg_types;
  99. }
  100. const Formattable::Type *types = MessageFormatAdapter::getArgTypeList(
  101. *(MessageFormat*)mfo->mf_data.umsgf, parts_count);
  102. /* Hash table will store Formattable::Type objects directly,
  103. * so no need for destructor */
  104. ALLOC_HASHTABLE(ret);
  105. zend_hash_init(ret, parts_count, NULL, arg_types_dtor, 0);
  106. for (int i = 0; i < parts_count; i++) {
  107. const Formattable::Type t = types[i];
  108. zend_hash_index_update_mem(ret, (zend_ulong)i, (void*)&t, sizeof(t));
  109. }
  110. if (U_FAILURE(err.code)) {
  111. zend_hash_destroy(ret);
  112. efree(ret);
  113. return NULL;
  114. }
  115. mfo->mf_data.arg_types = ret;
  116. return ret;
  117. }
  118. #ifdef HAS_MESSAGE_PATTERN
  119. static HashTable *umsg_parse_format(MessageFormatter_object *mfo,
  120. const MessagePattern& mp,
  121. intl_error& err)
  122. {
  123. HashTable *ret;
  124. int32_t parts_count;
  125. if (U_FAILURE(err.code)) {
  126. return NULL;
  127. }
  128. if (!((MessageFormat *)mfo->mf_data.umsgf)->usesNamedArguments()) {
  129. return umsg_get_numeric_types(mfo, err);
  130. }
  131. if (mfo->mf_data.arg_types) {
  132. /* already cached */
  133. return mfo->mf_data.arg_types;
  134. }
  135. /* Hash table will store Formattable::Type objects directly,
  136. * so no need for destructor */
  137. ALLOC_HASHTABLE(ret);
  138. zend_hash_init(ret, 32, NULL, arg_types_dtor, 0);
  139. parts_count = mp.countParts();
  140. // See MessageFormat::cacheExplicitFormats()
  141. /*
  142. * Looking through the pattern, go to each arg_start part type.
  143. * The arg-typeof that tells us the argument type (simple, complicated)
  144. * then the next part is either the arg_name or arg number
  145. * and then if it's simple after that there could be a part-type=arg-type
  146. * while substring will tell us number, spellout, etc.
  147. * If the next thing isn't an arg-type then assume string.
  148. */
  149. /* The last two "parts" can at most be ARG_LIMIT and MSG_LIMIT
  150. * which we need not examine. */
  151. for (int32_t i = 0; i < parts_count - 2 && U_SUCCESS(err.code); i++) {
  152. MessagePattern::Part p = mp.getPart(i);
  153. if (p.getType() != UMSGPAT_PART_TYPE_ARG_START) {
  154. continue;
  155. }
  156. MessagePattern::Part name_part = mp.getPart(++i); /* Getting name, advancing i */
  157. Formattable::Type type,
  158. *storedType;
  159. if (name_part.getType() == UMSGPAT_PART_TYPE_ARG_NAME) {
  160. UnicodeString argName = mp.getSubstring(name_part);
  161. if ((storedType = (Formattable::Type*)zend_hash_str_find_ptr(ret, (char*)argName.getBuffer(), argName.length() * sizeof(UChar))) == NULL) {
  162. /* not found already; create new entry in HT */
  163. Formattable::Type bogusType = Formattable::kObject;
  164. storedType = (Formattable::Type*)zend_hash_str_update_mem(ret, (char*)argName.getBuffer(), argName.length() * sizeof(UChar),
  165. (void*)&bogusType, sizeof(bogusType));
  166. }
  167. } else if (name_part.getType() == UMSGPAT_PART_TYPE_ARG_NUMBER) {
  168. int32_t argNumber = name_part.getValue();
  169. if (argNumber < 0) {
  170. intl_errors_set(&err, U_INVALID_FORMAT_ERROR,
  171. "Found part with negative number", 0);
  172. continue;
  173. }
  174. if ((storedType = (Formattable::Type*)zend_hash_index_find_ptr(ret, (zend_ulong)argNumber)) == NULL) {
  175. /* not found already; create new entry in HT */
  176. Formattable::Type bogusType = Formattable::kObject;
  177. storedType = (Formattable::Type*)zend_hash_index_update_mem(ret, (zend_ulong)argNumber, (void*)&bogusType, sizeof(bogusType));
  178. }
  179. } else {
  180. intl_errors_set(&err, U_INVALID_FORMAT_ERROR, "Invalid part type encountered", 0);
  181. continue;
  182. }
  183. UMessagePatternArgType argType = p.getArgType();
  184. /* No type specified, treat it as a string */
  185. if (argType == UMSGPAT_ARG_TYPE_NONE) {
  186. type = Formattable::kString;
  187. } else { /* Some type was specified, might be simple or complicated */
  188. if (argType == UMSGPAT_ARG_TYPE_SIMPLE) {
  189. /* For a SIMPLE arg, after the name part, there should be
  190. * an ARG_TYPE part whose string value tells us what to do */
  191. MessagePattern::Part type_part = mp.getPart(++i); /* Getting type, advancing i */
  192. if (type_part.getType() == UMSGPAT_PART_TYPE_ARG_TYPE) {
  193. UnicodeString typeString = mp.getSubstring(type_part);
  194. /* This is all based on the rules in the docs for MessageFormat
  195. * @see http://icu-project.org/apiref/icu4c/classMessageFormat.html */
  196. #define ASCII_LITERAL(s) UNICODE_STRING(s, sizeof(s)-1)
  197. if (typeString == ASCII_LITERAL("number")) {
  198. MessagePattern::Part style_part = mp.getPart(i + 1); /* Not advancing i */
  199. if (style_part.getType() == UMSGPAT_PART_TYPE_ARG_STYLE) {
  200. UnicodeString styleString = mp.getSubstring(style_part);
  201. if (styleString == ASCII_LITERAL("integer")) {
  202. type = Formattable::kInt64;
  203. } else if (styleString == ASCII_LITERAL("currency")) {
  204. type = Formattable::kDouble;
  205. } else if (styleString == ASCII_LITERAL("percent")) {
  206. type = Formattable::kDouble;
  207. } else { /* some style invalid/unknown to us */
  208. type = Formattable::kDouble;
  209. }
  210. } else { // if missing style, part, make it a double
  211. type = Formattable::kDouble;
  212. }
  213. } else if ((typeString == ASCII_LITERAL("date")) || (typeString == ASCII_LITERAL("time"))) {
  214. type = Formattable::kDate;
  215. } else if ((typeString == ASCII_LITERAL("spellout")) || (typeString == ASCII_LITERAL("ordinal"))
  216. || (typeString == ASCII_LITERAL("duration"))) {
  217. type = Formattable::kDouble;
  218. }
  219. #undef ASCII_LITERAL
  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);
  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);
  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)
  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);
  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);
  272. return NULL;
  273. }
  274. return umsg_get_numeric_types(mfo, err);
  275. #endif
  276. }
  277. static void umsg_set_timezone(MessageFormatter_object *mfo,
  278. intl_error& err)
  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. #ifdef HAS_MISALLOCATE_MEMORY_BUG
  292. /* There is a bug in ICU which prevents MessageFormatter::getFormats()
  293. to handle more than 10 formats correctly. The enumerator could be
  294. used to walk through the present formatters using getFormat(), which
  295. however seems to provide just a readonly access. This workaround
  296. prevents crash when there are > 10 formats but doesn't set any error.
  297. As a result, only DateFormatters with > 10 subformats are affected.
  298. This workaround should be ifdef'd out, when the bug has been fixed
  299. in ICU. */
  300. icu::StringEnumeration* fnames = mf->getFormatNames(err.code);
  301. if (!fnames || U_FAILURE(err.code)) {
  302. return;
  303. }
  304. count = fnames->count(err.code);
  305. delete fnames;
  306. if (count > 10) {
  307. return;
  308. }
  309. #endif
  310. formats = mf->getFormats(count);
  311. if (formats == NULL) {
  312. intl_errors_set(&err, U_MEMORY_ALLOCATION_ERROR,
  313. "Out of memory retrieving subformats", 0);
  314. }
  315. for (int i = 0; U_SUCCESS(err.code) && i < count; i++) {
  316. DateFormat* df = dynamic_cast<DateFormat*>(
  317. const_cast<Format *>(formats[i]));
  318. if (df == NULL) {
  319. continue;
  320. }
  321. if (used_tz == NULL) {
  322. zval nullzv, *zvptr = &nullzv;
  323. ZVAL_NULL(zvptr);
  324. used_tz = timezone_process_timezone_argument(zvptr, &err, "msgfmt_format");
  325. if (used_tz == NULL) {
  326. continue;
  327. }
  328. }
  329. df->setTimeZone(*used_tz);
  330. }
  331. if (U_SUCCESS(err.code)) {
  332. mfo->mf_data.tz_set = 1;
  333. }
  334. }
  335. U_CFUNC void umsg_format_helper(MessageFormatter_object *mfo,
  336. HashTable *args,
  337. UChar **formatted,
  338. int32_t *formatted_len)
  339. {
  340. int arg_count = zend_hash_num_elements(args);
  341. std::vector<Formattable> fargs;
  342. std::vector<UnicodeString> farg_names;
  343. MessageFormat *mf = (MessageFormat *)mfo->mf_data.umsgf;
  344. HashTable *types;
  345. intl_error& err = INTL_DATA_ERROR(mfo);
  346. if (U_FAILURE(err.code)) {
  347. return;
  348. }
  349. types = umsg_get_types(mfo, err);
  350. umsg_set_timezone(mfo, err);
  351. fargs.resize(arg_count);
  352. farg_names.resize(arg_count);
  353. int argNum = 0;
  354. zval *elem;
  355. // Key related variables
  356. zend_string *str_index;
  357. zend_ulong num_index;
  358. ZEND_HASH_FOREACH_KEY_VAL(args, num_index, str_index, elem) {
  359. Formattable& formattable = fargs[argNum];
  360. UnicodeString& key = farg_names[argNum];
  361. Formattable::Type argType = Formattable::kObject, //unknown
  362. *storedArgType = NULL;
  363. if (!U_SUCCESS(err.code)) {
  364. break;
  365. }
  366. /* Process key and retrieve type */
  367. if (str_index == NULL) {
  368. /* includes case where index < 0 because it's exposed as unsigned */
  369. if (num_index > (zend_ulong)INT32_MAX) {
  370. intl_errors_set(&err, U_ILLEGAL_ARGUMENT_ERROR,
  371. "Found negative or too large array key", 0);
  372. continue;
  373. }
  374. UChar temp[16];
  375. int32_t len = u_sprintf(temp, "%u", (uint32_t)num_index);
  376. key.append(temp, len);
  377. storedArgType = (Formattable::Type*)zend_hash_index_find_ptr(types, (zend_ulong)num_index);
  378. } else { //string; assumed to be in UTF-8
  379. intl_stringFromChar(key, ZSTR_VAL(str_index), ZSTR_LEN(str_index), &err.code);
  380. if (U_FAILURE(err.code)) {
  381. char *message;
  382. spprintf(&message, 0,
  383. "Invalid UTF-8 data in argument key: '%s'", ZSTR_VAL(str_index));
  384. intl_errors_set(&err, err.code, message, 1);
  385. efree(message);
  386. continue;
  387. }
  388. storedArgType = (Formattable::Type*)zend_hash_str_find_ptr(types, (char*)key.getBuffer(), key.length() * sizeof(UChar));
  389. }
  390. if (storedArgType != NULL) {
  391. argType = *storedArgType;
  392. }
  393. /* Convert zval to formattable according to message format type
  394. * or (as a fallback) the zval type */
  395. if (argType != Formattable::kObject) {
  396. switch (argType) {
  397. case Formattable::kString:
  398. {
  399. zend_string *str, *tmp_str;
  400. string_arg:
  401. /* This implicitly converts objects
  402. * Note that our vectors will leak if object conversion fails
  403. * and PHP ends up with a fatal error and calls longjmp
  404. * as a result of that.
  405. */
  406. str = zval_get_tmp_string(elem, &tmp_str);
  407. UnicodeString *text = new UnicodeString();
  408. intl_stringFromChar(*text,
  409. ZSTR_VAL(str), ZSTR_LEN(str), &err.code);
  410. if (U_FAILURE(err.code)) {
  411. char *message;
  412. spprintf(&message, 0, "Invalid UTF-8 data in string argument: "
  413. "'%s'", ZSTR_VAL(str));
  414. intl_errors_set(&err, err.code, message, 1);
  415. efree(message);
  416. delete text;
  417. continue;
  418. }
  419. formattable.adoptString(text);
  420. zend_tmp_string_release(tmp_str);
  421. break;
  422. }
  423. case Formattable::kDouble:
  424. {
  425. double d = zval_get_double(elem);
  426. formattable.setDouble(d);
  427. break;
  428. }
  429. case Formattable::kLong:
  430. {
  431. int32_t tInt32 = 0;
  432. if (Z_TYPE_P(elem) == IS_DOUBLE) {
  433. if (Z_DVAL_P(elem) > (double)INT32_MAX ||
  434. Z_DVAL_P(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);
  438. } else {
  439. tInt32 = (int32_t)Z_DVAL_P(elem);
  440. }
  441. } else if (Z_TYPE_P(elem) == IS_LONG) {
  442. if (Z_LVAL_P(elem) > INT32_MAX ||
  443. Z_LVAL_P(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);
  447. } else {
  448. tInt32 = (int32_t)Z_LVAL_P(elem);
  449. }
  450. } else {
  451. tInt32 = (int32_t)zval_get_long(elem);
  452. }
  453. formattable.setLong(tInt32);
  454. break;
  455. }
  456. case Formattable::kInt64:
  457. {
  458. int64_t tInt64 = 0;
  459. if (Z_TYPE_P(elem) == IS_DOUBLE) {
  460. if (Z_DVAL_P(elem) > (double)U_INT64_MAX ||
  461. Z_DVAL_P(elem) < (double)U_INT64_MIN) {
  462. intl_errors_set(&err, U_ILLEGAL_ARGUMENT_ERROR,
  463. "Found PHP float with absolute value too large for "
  464. "64 bit integer argument", 0);
  465. } else {
  466. tInt64 = (int64_t)Z_DVAL_P(elem);
  467. }
  468. } else if (Z_TYPE_P(elem) == IS_LONG) {
  469. /* assume long is not wider than 64 bits */
  470. tInt64 = (int64_t)Z_LVAL_P(elem);
  471. } else {
  472. tInt64 = (int64_t)zval_get_long(elem);
  473. }
  474. formattable.setInt64(tInt64);
  475. break;
  476. }
  477. case Formattable::kDate:
  478. {
  479. double dd = intl_zval_to_millis(elem, &err, "msgfmt_format");
  480. if (U_FAILURE(err.code)) {
  481. char *message;
  482. zend_string *u8key;
  483. UErrorCode status = UErrorCode();
  484. u8key = intl_charFromString(key, &status);
  485. if (u8key) {
  486. spprintf(&message, 0, "The argument for key '%s' "
  487. "cannot be used as a date or time", ZSTR_VAL(u8key));
  488. intl_errors_set(&err, err.code, message, 1);
  489. zend_string_release_ex(u8key, 0);
  490. efree(message);
  491. }
  492. continue;
  493. }
  494. formattable.setDate(dd);
  495. break;
  496. }
  497. default:
  498. intl_errors_set(&err, U_ILLEGAL_ARGUMENT_ERROR,
  499. "Found unsupported argument type", 0);
  500. break;
  501. }
  502. } else {
  503. /* We couldn't find any information about the argument in the pattern, this
  504. * means it's an extra argument. So convert it to a number if it's a number or
  505. * bool or null and to a string if it's anything else except arrays . */
  506. switch (Z_TYPE_P(elem)) {
  507. case IS_DOUBLE:
  508. formattable.setDouble(Z_DVAL_P(elem));
  509. break;
  510. case IS_LONG:
  511. formattable.setInt64((int64_t)Z_LVAL_P(elem));
  512. break;
  513. case IS_NULL:
  514. case IS_FALSE:
  515. formattable.setInt64((int64_t)0);
  516. break;
  517. case IS_TRUE:
  518. formattable.setInt64((int64_t)1);
  519. break;
  520. case IS_STRING:
  521. case IS_OBJECT:
  522. goto string_arg;
  523. default:
  524. {
  525. char *message;
  526. zend_string *u8key;
  527. UErrorCode status = UErrorCode();
  528. u8key = intl_charFromString(key, &status);
  529. if (u8key) {
  530. spprintf(&message, 0, "No strategy to convert the "
  531. "value given for the argument with key '%s' "
  532. "is available", ZSTR_VAL(u8key));
  533. intl_errors_set(&err,
  534. U_ILLEGAL_ARGUMENT_ERROR, message, 1);
  535. zend_string_release_ex(u8key, 0);
  536. efree(message);
  537. }
  538. }
  539. }
  540. }
  541. argNum++;
  542. } ZEND_HASH_FOREACH_END(); // visiting each argument
  543. if (U_FAILURE(err.code)) {
  544. return;
  545. }
  546. UnicodeString resultStr;
  547. FieldPosition fieldPosition(0);
  548. /* format the message */
  549. mf->format(farg_names.empty() ? NULL : &farg_names[0],
  550. fargs.empty() ? NULL : &fargs[0], arg_count, resultStr, err.code);
  551. if (U_FAILURE(err.code)) {
  552. intl_errors_set(&err, err.code,
  553. "Call to ICU MessageFormat::format() has failed", 0);
  554. return;
  555. }
  556. *formatted_len = resultStr.length();
  557. *formatted = eumalloc(*formatted_len+1);
  558. resultStr.extract(*formatted, *formatted_len+1, err.code);
  559. if (U_FAILURE(err.code)) {
  560. intl_errors_set(&err, err.code,
  561. "Error copying format() result", 0);
  562. return;
  563. }
  564. }
  565. #define cleanup_zvals() for(int j=i;j>=0;j--) { zval_ptr_dtor((*args)+i); }
  566. U_CFUNC void umsg_parse_helper(UMessageFormat *fmt, int *count, zval **args, UChar *source, int32_t source_len, UErrorCode *status)
  567. {
  568. UnicodeString srcString(source, source_len);
  569. Formattable *fargs = ((const MessageFormat*)fmt)->parse(srcString, *count, *status);
  570. if(U_FAILURE(*status)) {
  571. return;
  572. }
  573. *args = (zval *)safe_emalloc(*count, sizeof(zval), 0);
  574. // assign formattables to varargs
  575. for(int32_t i = 0; i < *count; i++) {
  576. int64_t aInt64;
  577. double aDate;
  578. UnicodeString temp;
  579. zend_string *u8str;
  580. switch(fargs[i].getType()) {
  581. case Formattable::kDate:
  582. aDate = ((double)fargs[i].getDate())/U_MILLIS_PER_SECOND;
  583. ZVAL_DOUBLE(&(*args)[i], aDate);
  584. break;
  585. case Formattable::kDouble:
  586. ZVAL_DOUBLE(&(*args)[i], (double)fargs[i].getDouble());
  587. break;
  588. case Formattable::kLong:
  589. ZVAL_LONG(&(*args)[i], fargs[i].getLong());
  590. break;
  591. case Formattable::kInt64:
  592. aInt64 = fargs[i].getInt64();
  593. if(aInt64 > ZEND_LONG_MAX || aInt64 < -ZEND_LONG_MAX) {
  594. ZVAL_DOUBLE(&(*args)[i], (double)aInt64);
  595. } else {
  596. ZVAL_LONG(&(*args)[i], (zend_long)aInt64);
  597. }
  598. break;
  599. case Formattable::kString:
  600. fargs[i].getString(temp);
  601. u8str = intl_convert_utf16_to_utf8(temp.getBuffer(), temp.length(), status);
  602. if(!u8str) {
  603. cleanup_zvals();
  604. return;
  605. }
  606. ZVAL_NEW_STR(&(*args)[i], u8str);
  607. break;
  608. case Formattable::kObject:
  609. case Formattable::kArray:
  610. *status = U_ILLEGAL_ARGUMENT_ERROR;
  611. cleanup_zvals();
  612. break;
  613. }
  614. }
  615. delete[] fargs;
  616. }
  617. /*
  618. * Local variables:
  619. * tab-width: 4
  620. * c-basic-offset: 4
  621. * End:
  622. * vim600: noet sw=4 ts=4 fdm=marker
  623. * vim<600: noet sw=4 ts=4
  624. */