PHP  
 PHP: Test and Code Coverage Analysis
downloads | QA | documentation | faq | getting help | mailing lists | reporting bugs | php.net sites | links | my php.net 
 

LCOV - code coverage report
Current view: top level - ext/intl/msgformat - msgformat_helpers.cpp (source / functions) Hit Total Coverage
Test: PHP Code Coverage Lines: 258 329 78.4 %
Date: 2022-01-29 Functions: 9 10 90.0 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.10

Generated at Sat, 29 Jan 2022 07:14:34 +0000 (26 minutes ago)

Copyright © 2005-2022 The PHP Group
All rights reserved.