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/locale - locale_methods.c (source / functions) Hit Total Coverage
Test: PHP Code Coverage Lines: 588 678 86.7 %
Date: 2019-05-06 Functions: 34 34 100.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: Kirti Velankar <kirtig@yahoo-inc.com>                       |
      14             :    +----------------------------------------------------------------------+
      15             : */
      16             : 
      17             : #ifdef HAVE_CONFIG_H
      18             : #include "config.h"
      19             : #endif
      20             : 
      21             : #include <unicode/ustring.h>
      22             : #include <unicode/udata.h>
      23             : #include <unicode/putil.h>
      24             : #include <unicode/ures.h>
      25             : 
      26             : #include "php_intl.h"
      27             : #include "locale.h"
      28             : #include "locale_class.h"
      29             : #include "locale_methods.h"
      30             : #include "intl_convert.h"
      31             : #include "intl_data.h"
      32             : 
      33             : #include <zend_API.h>
      34             : #include <zend.h>
      35             : #include <php.h>
      36             : #include "main/php_ini.h"
      37             : #include "zend_smart_str.h"
      38             : 
      39             : ZEND_EXTERN_MODULE_GLOBALS( intl )
      40             : 
      41             : /* Sizes required for the strings "variant15" , "extlang11", "private12" etc. */
      42             : #define SEPARATOR "_"
      43             : #define SEPARATOR1 "-"
      44             : #define DELIMITER "-_"
      45             : #define EXTLANG_PREFIX "a"
      46             : #define PRIVATE_PREFIX "x"
      47             : #define DISP_NAME "name"
      48             : 
      49             : #define MAX_NO_VARIANT  15
      50             : #define MAX_NO_EXTLANG  3
      51             : #define MAX_NO_PRIVATE  15
      52             : #define MAX_NO_LOOKUP_LANG_TAG  100
      53             : 
      54             : #define LOC_NOT_FOUND 1
      55             : 
      56             : /* Sizes required for the strings "variant15" , "extlang3", "private12" etc. */
      57             : #define VARIANT_KEYNAME_LEN  11
      58             : #define EXTLANG_KEYNAME_LEN  10
      59             : #define PRIVATE_KEYNAME_LEN  11
      60             : 
      61             : /* Based on IANA registry at the time of writing this code
      62             : *
      63             : */
      64             : static const char * const LOC_GRANDFATHERED[] = {
      65             :         "art-lojban",         "i-klingon",          "i-lux",                      "i-navajo",           "no-bok",             "no-nyn",
      66             :         "cel-gaulish",                "en-GB-oed",          "i-ami",
      67             :         "i-bnn",              "i-default",          "i-enochian",
      68             :         "i-mingo",            "i-pwn",              "i-tao",
      69             :         "i-tay",              "i-tsu",              "sgn-BE-fr",
      70             :         "sgn-BE-nl",          "sgn-CH-de",          "zh-cmn",
      71             :         "zh-cmn-Hans",                "zh-cmn-Hant",                "zh-gan" ,
      72             :         "zh-guoyu",           "zh-hakka",           "zh-min",
      73             :         "zh-min-nan",                 "zh-wuu",             "zh-xiang",
      74             :         "zh-yue",             NULL
      75             : };
      76             : 
      77             : /* Based on IANA registry at the time of writing this code
      78             : *  This array lists the preferred values for the grandfathered tags if applicable
      79             : *  This is in sync with the array LOC_GRANDFATHERED
      80             : *  e.g. the offsets of the grandfathered tags match the offset of the preferred  value
      81             : */
      82             : static const int                LOC_PREFERRED_GRANDFATHERED_LEN = 6;
      83             : static const char * const       LOC_PREFERRED_GRANDFATHERED[]  = {
      84             :         "jbo",                        "tlh",                        "lb",
      85             :         "nv",                         "nb",                 "nn",
      86             :         NULL
      87             : };
      88             : 
      89             : /*returns TRUE if a is an ID separator FALSE otherwise*/
      90             : #define isIDSeparator(a) (a == '_' || a == '-')
      91             : #define isKeywordSeparator(a) (a == '@' )
      92             : #define isEndOfTag(a) (a == '\0' )
      93             : 
      94             : #define isPrefixLetter(a) ((a=='x')||(a=='X')||(a=='i')||(a=='I'))
      95             : 
      96             : /*returns TRUE if one of the special prefixes is here (s=string)
      97             :   'x-' or 'i-' */
      98             : #define isIDPrefix(s) (isPrefixLetter(s[0])&&isIDSeparator(s[1]))
      99             : #define isKeywordPrefix(s) ( isKeywordSeparator(s[0]) )
     100             : 
     101             : /* Dot terminates it because of POSIX form  where dot precedes the codepage
     102             :  * except for variant */
     103             : #define isTerminator(a)  ((a==0)||(a=='.')||(a=='@'))
     104             : 
     105             : /* {{{ return the offset of 'key' in the array 'list'.
     106             :  * returns -1 if not present */
     107        1478 : static int16_t findOffset(const char* const* list, const char* key)
     108             : {
     109        1478 :         const char* const* anchor = list;
     110       47312 :         while (*list != NULL) {
     111       44456 :                 if (strcmp(key, *list) == 0) {
     112         100 :                         return (int16_t)(list - anchor);
     113             :                 }
     114       44356 :                 list++;
     115             :         }
     116             : 
     117        1378 :         return -1;
     118             : 
     119             : }
     120             : /*}}}*/
     121             : 
     122          18 : static char* getPreferredTag(const char* gf_tag)
     123             : {
     124          18 :         char* result = NULL;
     125          18 :         zend_off_t grOffset = 0;
     126             : 
     127          18 :         grOffset = findOffset( LOC_GRANDFATHERED ,gf_tag);
     128          18 :         if(grOffset < 0) {
     129           0 :                 return NULL;
     130             :         }
     131          18 :         if( grOffset < LOC_PREFERRED_GRANDFATHERED_LEN ){
     132             :                 /* return preferred tag */
     133           6 :                 result = estrdup( LOC_PREFERRED_GRANDFATHERED[grOffset] );
     134             :         } else {
     135             :                 /* Return correct grandfathered language tag */
     136          12 :                 result = estrdup( LOC_GRANDFATHERED[grOffset] );
     137             :         }
     138          18 :         return result;
     139             : }
     140             : 
     141             : /* {{{
     142             : * returns the position of next token for lookup
     143             : * or -1 if no token
     144             : * strtokr equivalent search for token in reverse direction
     145             : */
     146           9 : static zend_off_t getStrrtokenPos(char* str, zend_off_t savedPos)
     147             : {
     148           9 :         zend_off_t result =-1;
     149             :         zend_off_t i;
     150             : 
     151          42 :         for(i=savedPos-1; i>=0; i--) {
     152          40 :                 if(isIDSeparator(*(str+i)) ){
     153             :                         /* delimiter found; check for singleton */
     154           7 :                         if(i>=2 && isIDSeparator(*(str+i-2)) ){
     155             :                                 /* a singleton; so send the position of token before the singleton */
     156           0 :                                 result = i-2;
     157             :                         } else {
     158           7 :                                 result = i;
     159             :                         }
     160           7 :                         break;
     161             :                 }
     162             :         }
     163           9 :         if(result < 1){
     164             :                 /* Just in case inavlid locale e.g. '-x-xyz' or '-sl_Latn' */
     165           2 :                 result =-1;
     166             :         }
     167           9 :         return result;
     168             : }
     169             : /* }}} */
     170             : 
     171             : /* {{{
     172             : * returns the position of a singleton if present
     173             : * returns -1 if no singleton
     174             : * strtok equivalent search for singleton
     175             : */
     176         354 : static zend_off_t getSingletonPos(const char* str)
     177             : {
     178         354 :         zend_off_t result =-1;
     179         354 :         size_t len = 0;
     180             : 
     181         354 :         if( str && ((len=strlen(str))>0) ){
     182         354 :                 zend_off_t i = 0;
     183        3170 :                 for( i=0; (size_t)i < len ; i++){
     184        2912 :                         if( isIDSeparator(*(str+i)) ){
     185         492 :                                 if( i==1){
     186             :                                         /* string is of the form x-avy or a-prv1 */
     187          18 :                                         result =0;
     188          18 :                                         break;
     189             :                                 } else {
     190             :                                         /* delimiter found; check for singleton */
     191         474 :                                         if( isIDSeparator(*(str+i+2)) ){
     192             :                                                 /* a singleton; so send the position of separator before singleton */
     193          78 :                                                 result = i+1;
     194          78 :                                                 break;
     195             :                                         }
     196             :                                 }
     197             :                         }
     198             :                 }/* end of for */
     199             : 
     200             :         }
     201         354 :         return result;
     202             : }
     203             : /* }}} */
     204             : 
     205             : /* {{{ proto static string Locale::getDefault(  )
     206             :    Get default locale */
     207             : /* }}} */
     208             : /* {{{ proto static string locale_get_default( )
     209             :    Get default locale */
     210          79 : PHP_NAMED_FUNCTION(zif_locale_get_default)
     211             : {
     212         158 :         RETURN_STRING( intl_locale_get_default(  ) );
     213             : }
     214             : 
     215             : /* }}} */
     216             : 
     217             : /* {{{ proto static string Locale::setDefault( string $locale )
     218             :    Set default locale */
     219             : /* }}} */
     220             : /* {{{ proto static string locale_set_default( string $locale )
     221             :    Set default locale */
     222          75 : PHP_NAMED_FUNCTION(zif_locale_set_default)
     223             : {
     224             :         zend_string* locale_name;
     225             :         zend_string *ini_name;
     226          75 :         char *default_locale = NULL;
     227             : 
     228          75 :         if(zend_parse_parameters( ZEND_NUM_ARGS(),  "S", &locale_name) == FAILURE)
     229             :         {
     230           1 :                 RETURN_FALSE;
     231             :         }
     232             : 
     233          74 :         if (ZSTR_LEN(locale_name) == 0) {
     234           0 :                 default_locale = (char *)uloc_getDefault();
     235           0 :                 locale_name = zend_string_init(default_locale, strlen(default_locale), 0);
     236             :         }
     237             : 
     238          74 :         ini_name = zend_string_init(LOCALE_INI_NAME, sizeof(LOCALE_INI_NAME) - 1, 0);
     239          74 :         zend_alter_ini_entry(ini_name, locale_name, PHP_INI_USER, PHP_INI_STAGE_RUNTIME);
     240             :         zend_string_release_ex(ini_name, 0);
     241          74 :         if (default_locale != NULL) {
     242           0 :                 zend_string_release_ex(locale_name, 0);
     243             :         }
     244             : 
     245          74 :         RETURN_TRUE;
     246             : }
     247             : /* }}} */
     248             : 
     249             : /* {{{
     250             : * Gets the value from ICU
     251             : * common code shared by get_primary_language,get_script or get_region or get_variant
     252             : * result = 0 if error, 1 if successful , -1 if no value
     253             : */
     254        1477 : static zend_string* get_icu_value_internal( const char* loc_name , char* tag_name, int* result , int fromParseLocale)
     255             : {
     256        1477 :         zend_string* tag_value      = NULL;
     257        1477 :         int32_t      tag_value_len  = 512;
     258             : 
     259        1477 :         char*        mod_loc_name   = NULL;
     260             : 
     261        1477 :         int32_t      buflen         = 512;
     262        1477 :         UErrorCode   status         = U_ZERO_ERROR;
     263             : 
     264        1477 :         if (strlen(loc_name) > INTL_MAX_LOCALE_LEN) {
     265           0 :                 return NULL;
     266             :         }
     267             : 
     268        1477 :         if( strcmp(tag_name, LOC_CANONICALIZE_TAG) != 0 ){
     269             :                 /* Handle  grandfathered languages */
     270         496 :                 zend_off_t grOffset =  findOffset( LOC_GRANDFATHERED , loc_name );
     271         496 :                 if( grOffset >= 0 ){
     272          12 :                         if( strcmp(tag_name , LOC_LANG_TAG)==0 ){
     273           8 :                                 return zend_string_init(loc_name, strlen(loc_name), 0);
     274             :                         } else {
     275             :                                 /* Since Grandfathered , no value , do nothing , retutn NULL */
     276           8 :                                 return NULL;
     277             :                         }
     278             :                 }
     279             : 
     280         484 :         if( fromParseLocale==1 ){
     281         272 :                 zend_off_t singletonPos = 0;
     282             : 
     283             :                 /* Handle singletons */
     284         272 :                 if( strcmp(tag_name , LOC_LANG_TAG)==0 ){
     285          68 :                         if( strlen(loc_name)>1 && (isIDPrefix(loc_name) == 1) ){
     286           4 :                                 return zend_string_init(loc_name, strlen(loc_name), 0);
     287             :                         }
     288             :                 }
     289             : 
     290         270 :                 singletonPos = getSingletonPos( loc_name );
     291         270 :                 if( singletonPos == 0){
     292             :                         /* singleton at start of script, region , variant etc.
     293             :                          * or invalid singleton at start of language */
     294          14 :                         return NULL;
     295         256 :                 } else if( singletonPos > 0 ){
     296             :                         /* singleton at some position except at start
     297             :                          * strip off the singleton and rest of the loc_name */
     298          56 :                         mod_loc_name = estrndup ( loc_name , singletonPos-1);
     299             :                 }
     300             :         } /* end of if fromParse */
     301             : 
     302             :         } /* end of if != LOC_CANONICAL_TAG */
     303             : 
     304        1449 :         if( mod_loc_name == NULL){
     305        1393 :                 mod_loc_name = estrdup(loc_name );
     306             :         }
     307             : 
     308             :         /* Proceed to ICU */
     309             :         do{
     310        1449 :                 if (tag_value) {
     311           0 :                         tag_value = zend_string_realloc( tag_value , buflen, 0);
     312             :                 } else {
     313        2898 :                         tag_value = zend_string_alloc( buflen, 0);
     314             :                 }
     315        1449 :                 tag_value_len = buflen;
     316             : 
     317        1449 :                 if( strcmp(tag_name , LOC_SCRIPT_TAG)==0 ){
     318         132 :                         buflen = uloc_getScript ( mod_loc_name , tag_value->val , tag_value_len , &status);
     319             :                 }
     320        1449 :                 if( strcmp(tag_name , LOC_LANG_TAG )==0 ){
     321         130 :                         buflen = uloc_getLanguage ( mod_loc_name , tag_value->val , tag_value_len , &status);
     322             :                 }
     323        1449 :                 if( strcmp(tag_name , LOC_REGION_TAG)==0 ){
     324         132 :                         buflen = uloc_getCountry ( mod_loc_name , tag_value->val , tag_value_len , &status);
     325             :                 }
     326        1449 :                 if( strcmp(tag_name , LOC_VARIANT_TAG)==0 ){
     327          74 :                         buflen = uloc_getVariant ( mod_loc_name , tag_value->val , tag_value_len , &status);
     328             :                 }
     329        1449 :                 if( strcmp(tag_name , LOC_CANONICALIZE_TAG)==0 ){
     330         981 :                         buflen = uloc_canonicalize ( mod_loc_name , tag_value->val , tag_value_len , &status);
     331             :                 }
     332             : 
     333        1449 :                 if( U_FAILURE( status ) ) {
     334           0 :                         if( status == U_BUFFER_OVERFLOW_ERROR ) {
     335           0 :                                 status = U_ZERO_ERROR;
     336           0 :                                 buflen++; /* add space for \0 */
     337           0 :                                 continue;
     338             :                         }
     339             : 
     340             :                         /* Error in retrieving data */
     341           0 :                         *result = 0;
     342           0 :                         if( tag_value ){
     343             :                                 zend_string_release_ex( tag_value, 0 );
     344             :                         }
     345           0 :                         if( mod_loc_name ){
     346           0 :                                 efree( mod_loc_name);
     347             :                         }
     348           0 :                         return NULL;
     349             :                 }
     350        1449 :         } while( buflen > tag_value_len );
     351             : 
     352        1449 :         if(  buflen ==0 ){
     353             :                 /* No value found */
     354         198 :                 *result = -1;
     355         198 :                 if( tag_value ){
     356             :                         zend_string_release_ex( tag_value, 0 );
     357             :                 }
     358         198 :                 if( mod_loc_name ){
     359         198 :                         efree( mod_loc_name);
     360             :                 }
     361         198 :                 return NULL;
     362             :         } else {
     363        1251 :                 *result = 1;
     364             :         }
     365             : 
     366        1251 :         if( mod_loc_name ){
     367        1251 :                 efree( mod_loc_name);
     368             :         }
     369             : 
     370        1251 :         tag_value->len = strlen(tag_value->val);
     371        1251 :         return tag_value;
     372             : }
     373             : /* }}} */
     374             : 
     375             : /* {{{
     376             : * Gets the value from ICU , called when PHP userspace function is called
     377             : * common code shared by get_primary_language,get_script or get_region or get_variant
     378             : */
     379         613 : static void get_icu_value_src_php( char* tag_name, INTERNAL_FUNCTION_PARAMETERS)
     380             : {
     381             : 
     382         613 :         const char* loc_name            = NULL;
     383         613 :         size_t         loc_name_len     = 0;
     384             : 
     385         613 :         zend_string*   tag_value                = NULL;
     386         613 :         char*       empty_result        = "";
     387             : 
     388         613 :         int         result              = 0;
     389         613 :         char*       msg                 = NULL;
     390             : 
     391         613 :         UErrorCode  status              = U_ZERO_ERROR;
     392             : 
     393         613 :         intl_error_reset( NULL );
     394             : 
     395         613 :         if(zend_parse_parameters( ZEND_NUM_ARGS(), "s",
     396             :         &loc_name ,&loc_name_len ) == FAILURE) {
     397         617 :                 RETURN_FALSE;
     398             :     }
     399             : 
     400         609 :         if(loc_name_len == 0) {
     401           0 :                 loc_name = intl_locale_get_default();
     402           0 :                 loc_name_len = strlen(loc_name);
     403             :         }
     404             : 
     405         609 :         INTL_CHECK_LOCALE_LEN(loc_name_len);
     406             : 
     407             :         /* Call ICU get */
     408         608 :         tag_value = get_icu_value_internal( loc_name , tag_name , &result ,0);
     409             : 
     410             :         /* No value found */
     411         608 :         if( result == -1 ) {
     412          78 :                 if( tag_value){
     413             :                         zend_string_release_ex( tag_value, 0 );
     414             :                 }
     415         156 :                 RETURN_STRING( empty_result);
     416             :         }
     417             : 
     418             :         /* value found */
     419         530 :         if( tag_value){
     420        1044 :                 RETVAL_STR( tag_value );
     421         522 :                 return;
     422             :         }
     423             : 
     424             :         /* Error encountered while fetching the value */
     425           8 :         if( result ==0) {
     426           8 :                 spprintf(&msg , 0, "locale_get_%s : unable to get locale %s", tag_name , tag_name );
     427           8 :                 intl_error_set( NULL, status, msg , 1 );
     428           8 :                 efree(msg);
     429           8 :                 RETURN_NULL();
     430             :         }
     431             : 
     432             : }
     433             : /* }}} */
     434             : 
     435             : /* {{{ proto static string Locale::getScript($locale)
     436             :  * gets the script for the $locale
     437             :  }}} */
     438             : /* {{{ proto static string locale_get_script($locale)
     439             :  * gets the script for the $locale
     440             :  */
     441          73 : PHP_FUNCTION( locale_get_script )
     442             : {
     443          73 :         get_icu_value_src_php( LOC_SCRIPT_TAG , INTERNAL_FUNCTION_PARAM_PASSTHRU );
     444          73 : }
     445             : /* }}} */
     446             : 
     447             : /* {{{ proto static string Locale::getRegion($locale)
     448             :  * gets the region for the $locale
     449             :  }}} */
     450             : /* {{{ proto static string locale_get_region($locale)
     451             :  * gets the region for the $locale
     452             :  */
     453          73 : PHP_FUNCTION( locale_get_region )
     454             : {
     455          73 :         get_icu_value_src_php( LOC_REGION_TAG , INTERNAL_FUNCTION_PARAM_PASSTHRU );
     456          73 : }
     457             : /* }}} */
     458             : 
     459             : /* {{{ proto static string Locale::getPrimaryLanguage($locale)
     460             :  * gets the primary language for the $locale
     461             :  }}} */
     462             : /* {{{ proto static string locale_get_primary_language($locale)
     463             :  * gets the primary language for the $locale
     464             :  */
     465          72 : PHP_FUNCTION(locale_get_primary_language )
     466             : {
     467          72 :         get_icu_value_src_php( LOC_LANG_TAG , INTERNAL_FUNCTION_PARAM_PASSTHRU );
     468          72 : }
     469             : /* }}} */
     470             : 
     471             : 
     472             : /* {{{
     473             :  * common code shared by display_xyz functions to  get the value from ICU
     474             :  }}} */
     475        1162 : static void get_icu_disp_value_src_php( char* tag_name, INTERNAL_FUNCTION_PARAMETERS)
     476             : {
     477        1162 :         const char* loc_name            = NULL;
     478        1162 :         size_t         loc_name_len     = 0;
     479             : 
     480        1162 :         const char* disp_loc_name       = NULL;
     481        1162 :         size_t      disp_loc_name_len   = 0;
     482        1162 :         int         free_loc_name       = 0;
     483             : 
     484        1162 :         UChar*      disp_name           = NULL;
     485        1162 :         int32_t     disp_name_len       = 0;
     486             : 
     487        1162 :         char*       mod_loc_name        = NULL;
     488             : 
     489        1162 :         int32_t     buflen              = 512;
     490        1162 :         UErrorCode  status              = U_ZERO_ERROR;
     491             : 
     492             :         zend_string* u8str;
     493             : 
     494        1162 :         char*       msg                 = NULL;
     495             : 
     496        1162 :         intl_error_reset( NULL );
     497             : 
     498        1162 :         if(zend_parse_parameters( ZEND_NUM_ARGS(), "s|s",
     499             :                 &loc_name, &loc_name_len ,
     500             :                 &disp_loc_name ,&disp_loc_name_len ) == FAILURE)
     501             :         {
     502          57 :                 RETURN_FALSE;
     503             :         }
     504             : 
     505        1157 :     if(loc_name_len > ULOC_FULLNAME_CAPACITY) {
     506             :         /* See bug 67397: overlong locale names cause trouble in uloc_getDisplayName */
     507           5 :                 spprintf(&msg , 0, "locale_get_display_%s : name too long", tag_name );
     508           5 :                 intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,  msg , 1 );
     509           5 :                 efree(msg);
     510           5 :                 RETURN_FALSE;
     511             :     }
     512             : 
     513        1152 :         if(loc_name_len == 0) {
     514           0 :                 loc_name = intl_locale_get_default();
     515             :         }
     516             : 
     517        1152 :         if( strcmp(tag_name, DISP_NAME) != 0 ){
     518             :                 /* Handle grandfathered languages */
     519         870 :                 int grOffset = findOffset( LOC_GRANDFATHERED , loc_name );
     520         870 :                 if( grOffset >= 0 ){
     521          54 :                         if( strcmp(tag_name , LOC_LANG_TAG)==0 ){
     522          18 :                                 mod_loc_name = getPreferredTag( loc_name );
     523             :                         } else {
     524             :                                 /* Since Grandfathered, no value, do nothing, retutn NULL */
     525          36 :                                 RETURN_FALSE;
     526             :                         }
     527             :                 }
     528             :         } /* end of if != LOC_CANONICAL_TAG */
     529             : 
     530        1116 :         if( mod_loc_name==NULL ){
     531        1098 :                 mod_loc_name = estrdup( loc_name );
     532             :         }
     533             : 
     534             :         /* Check if disp_loc_name passed , if not use default locale */
     535        1116 :         if( !disp_loc_name){
     536           0 :                 disp_loc_name = estrdup(intl_locale_get_default());
     537           0 :                 free_loc_name = 1;
     538             :         }
     539             : 
     540             :     /* Get the disp_value for the given locale */
     541             :     do{
     542        1116 :         disp_name = erealloc( disp_name , buflen * sizeof(UChar)  );
     543        1116 :         disp_name_len = buflen;
     544             : 
     545        1116 :                 if( strcmp(tag_name , LOC_LANG_TAG)==0 ){
     546         222 :                         buflen = uloc_getDisplayLanguage ( mod_loc_name , disp_loc_name , disp_name , disp_name_len , &status);
     547         894 :                 } else if( strcmp(tag_name , LOC_SCRIPT_TAG)==0 ){
     548         204 :                         buflen = uloc_getDisplayScript ( mod_loc_name , disp_loc_name , disp_name , disp_name_len , &status);
     549         690 :                 } else if( strcmp(tag_name , LOC_REGION_TAG)==0 ){
     550         204 :                         buflen = uloc_getDisplayCountry ( mod_loc_name , disp_loc_name , disp_name , disp_name_len , &status);
     551         486 :                 } else if( strcmp(tag_name , LOC_VARIANT_TAG)==0 ){
     552         204 :                         buflen = uloc_getDisplayVariant ( mod_loc_name , disp_loc_name , disp_name , disp_name_len , &status);
     553         282 :                 } else if( strcmp(tag_name , DISP_NAME)==0 ){
     554         282 :                         buflen = uloc_getDisplayName ( mod_loc_name , disp_loc_name , disp_name , disp_name_len , &status);
     555             :                 }
     556             : 
     557             :                 /* U_STRING_NOT_TERMINATED_WARNING is admissible here; don't look for it */
     558        1116 :                 if( U_FAILURE( status ) )
     559             :                 {
     560           6 :                         if( status == U_BUFFER_OVERFLOW_ERROR )
     561             :                         {
     562           0 :                                 status = U_ZERO_ERROR;
     563           0 :                                 continue;
     564             :                         }
     565             : 
     566           6 :                         spprintf(&msg, 0, "locale_get_display_%s : unable to get locale %s", tag_name , tag_name );
     567           6 :                         intl_error_set( NULL, status, msg , 1 );
     568           6 :                         efree(msg);
     569           6 :                         if( disp_name){
     570           6 :                                 efree( disp_name );
     571             :                         }
     572           6 :                         if( mod_loc_name){
     573           6 :                                 efree( mod_loc_name );
     574             :                         }
     575           6 :                         if (free_loc_name) {
     576           0 :                                 efree((void *)disp_loc_name);
     577           0 :                                 disp_loc_name = NULL;
     578             :                         }
     579           6 :                         RETURN_FALSE;
     580             :                 }
     581        1110 :         } while( buflen > disp_name_len );
     582             : 
     583        1110 :         if( mod_loc_name){
     584        1110 :                 efree( mod_loc_name );
     585             :         }
     586        1110 :         if (free_loc_name) {
     587           0 :                 efree((void *)disp_loc_name);
     588           0 :                 disp_loc_name = NULL;
     589             :         }
     590             :         /* Convert display locale name from UTF-16 to UTF-8. */
     591        1110 :         u8str = intl_convert_utf16_to_utf8(disp_name, buflen, &status );
     592        1110 :         efree( disp_name );
     593        1110 :         if( !u8str )
     594             :         {
     595           0 :                 spprintf(&msg, 0, "locale_get_display_%s :error converting display name for %s to UTF-8", tag_name , tag_name );
     596           0 :                 intl_error_set( NULL, status, msg , 1 );
     597           0 :                 efree(msg);
     598           0 :                 RETURN_FALSE;
     599             :         }
     600             : 
     601        1110 :         RETVAL_NEW_STR( u8str );
     602             : }
     603             : /* }}} */
     604             : 
     605             : /* {{{ proto static string Locale::getDisplayName($locale[, $in_locale = null])
     606             : * gets the name for the $locale in $in_locale or default_locale
     607             :  }}} */
     608             : /* {{{ proto static string get_display_name($locale[, $in_locale = null])
     609             : * gets the name for the $locale in $in_locale or default_locale
     610             : */
     611         288 : PHP_FUNCTION(locale_get_display_name)
     612             : {
     613         288 :     get_icu_disp_value_src_php( DISP_NAME , INTERNAL_FUNCTION_PARAM_PASSTHRU );
     614         288 : }
     615             : /* }}} */
     616             : 
     617             : /* {{{ proto static string Locale::getDisplayLanguage($locale[, $in_locale = null])
     618             : * gets the language for the $locale in $in_locale or default_locale
     619             :  }}} */
     620             : /* {{{ proto static string get_display_language($locale[, $in_locale = null])
     621             : * gets the language for the $locale in $in_locale or default_locale
     622             : */
     623         223 : PHP_FUNCTION(locale_get_display_language)
     624             : {
     625         223 :     get_icu_disp_value_src_php( LOC_LANG_TAG , INTERNAL_FUNCTION_PARAM_PASSTHRU );
     626         223 : }
     627             : /* }}} */
     628             : 
     629             : /* {{{ proto static string Locale::getDisplayScript($locale, $in_locale = null)
     630             : * gets the script for the $locale in $in_locale or default_locale
     631             :  }}} */
     632             : /* {{{ proto static string get_display_script($locale, $in_locale = null)
     633             : * gets the script for the $locale in $in_locale or default_locale
     634             : */
     635         217 : PHP_FUNCTION(locale_get_display_script)
     636             : {
     637         217 :     get_icu_disp_value_src_php( LOC_SCRIPT_TAG , INTERNAL_FUNCTION_PARAM_PASSTHRU );
     638         217 : }
     639             : /* }}} */
     640             : 
     641             : /* {{{ proto static string Locale::getDisplayRegion($locale, $in_locale = null)
     642             : * gets the region for the $locale in $in_locale or default_locale
     643             :  }}} */
     644             : /* {{{ proto static string get_display_region($locale, $in_locale = null)
     645             : * gets the region for the $locale in $in_locale or default_locale
     646             : */
     647         217 : PHP_FUNCTION(locale_get_display_region)
     648             : {
     649         217 :     get_icu_disp_value_src_php( LOC_REGION_TAG , INTERNAL_FUNCTION_PARAM_PASSTHRU );
     650         217 : }
     651             : /* }}} */
     652             : 
     653             : /* {{{
     654             : * proto static string Locale::getDisplayVariant($locale, $in_locale = null)
     655             : * gets the variant for the $locale in $in_locale or default_locale
     656             :  }}} */
     657             : /* {{{
     658             : * proto static string get_display_variant($locale, $in_locale = null)
     659             : * gets the variant for the $locale in $in_locale or default_locale
     660             : */
     661         217 : PHP_FUNCTION(locale_get_display_variant)
     662             : {
     663         217 :     get_icu_disp_value_src_php( LOC_VARIANT_TAG , INTERNAL_FUNCTION_PARAM_PASSTHRU );
     664         217 : }
     665             : /* }}} */
     666             : 
     667             :  /* {{{ proto static array getKeywords(string $locale) {
     668             :  * return an associative array containing keyword-value
     669             :  * pairs for this locale. The keys are keys to the array (doh!)
     670             :  * }}}*/
     671             :  /* {{{ proto static array locale_get_keywords(string $locale) {
     672             :  * return an associative array containing keyword-value
     673             :  * pairs for this locale. The keys are keys to the array (doh!)
     674             :  */
     675          75 : PHP_FUNCTION( locale_get_keywords )
     676             : {
     677          75 :     UEnumeration*   e        = NULL;
     678          75 :     UErrorCode      status   = U_ZERO_ERROR;
     679             : 
     680          75 :     const char*         kw_key        = NULL;
     681          75 :     int32_t         kw_key_len    = 0;
     682             : 
     683          75 :     const char*         loc_name        = NULL;
     684          75 :     size_t                      loc_name_len    = 0;
     685             : 
     686          75 :     intl_error_reset( NULL );
     687             : 
     688          75 :     if(zend_parse_parameters( ZEND_NUM_ARGS(), "s",
     689             :         &loc_name, &loc_name_len ) == FAILURE)
     690             :     {
     691           2 :         RETURN_FALSE;
     692             :     }
     693             : 
     694          74 :         INTL_CHECK_LOCALE_LEN(strlen(loc_name));
     695             : 
     696          74 :     if(loc_name_len == 0) {
     697           0 :         loc_name = intl_locale_get_default();
     698             :     }
     699             : 
     700             :         /* Get the keywords */
     701          74 :     e = uloc_openKeywords( loc_name, &status );
     702          74 :     if( e != NULL )
     703             :     {
     704             :                 /*
     705             :                 ICU expects the buffer to be allocated  before calling the function
     706             :                 and so the buffer size has been explicitly specified
     707             :                 ICU uloc.h #define      ULOC_KEYWORD_AND_VALUES_CAPACITY   100
     708             :                 hence the kw_value buffer size is 100
     709             :                 */
     710             : 
     711             :                 /* Traverse it, filling the return array. */
     712          24 :         array_init( return_value );
     713             : 
     714          84 :         while( ( kw_key = uenum_next( e, &kw_key_len, &status ) ) != NULL ){
     715          36 :                 int32_t kw_value_len = 100;
     716          72 :                         zend_string *kw_value_str = zend_string_alloc(kw_value_len, 0);
     717             : 
     718             :                         /* Get the keyword value for each keyword */
     719          36 :                         kw_value_len=uloc_getKeywordValue( loc_name, kw_key, ZSTR_VAL(kw_value_str), kw_value_len, &status );
     720          36 :                         if (status == U_BUFFER_OVERFLOW_ERROR) {
     721           0 :                                 status = U_ZERO_ERROR;
     722           0 :                                 kw_value_str = zend_string_extend(kw_value_str, kw_value_len, 0);
     723           0 :                                 kw_value_len=uloc_getKeywordValue( loc_name,kw_key, ZSTR_VAL(kw_value_str), kw_value_len+1, &status );
     724          36 :                         } else if(!U_FAILURE(status)) {
     725          72 :                                 kw_value_str = zend_string_truncate(kw_value_str, kw_value_len, 0);
     726             :                         }
     727          36 :                         if (U_FAILURE(status)) {
     728           0 :                                 intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR, "locale_get_keywords: Error encountered while getting the keyword  value for the  keyword", 0 );
     729           0 :                                 if( kw_value_str){
     730             :                                         zend_string_efree( kw_value_str );
     731             :                                 }
     732           0 :                                 zend_array_destroy(Z_ARR_P(return_value));
     733           0 :                         RETURN_FALSE;
     734             :                         }
     735             : 
     736          36 :                 add_assoc_str( return_value, (char *)kw_key, kw_value_str);
     737             :                 } /* end of while */
     738             : 
     739             :         } /* end of if e!=NULL */
     740             : 
     741          74 :     uenum_close( e );
     742             : }
     743             : /* }}} */
     744             : 
     745             :  /* {{{ proto static string Locale::canonicalize($locale)
     746             :  * @return string the canonicalized locale
     747             :  * }}} */
     748             :  /* {{{ proto static string locale_canonicalize(Locale $loc, string $locale)
     749             :  * @param string $locale        The locale string to canonicalize
     750             :  */
     751         395 : PHP_FUNCTION(locale_canonicalize)
     752             : {
     753         395 :         get_icu_value_src_php( LOC_CANONICALIZE_TAG , INTERNAL_FUNCTION_PARAM_PASSTHRU );
     754         395 : }
     755             : /* }}} */
     756             : 
     757             : /* {{{ append_key_value
     758             : * Internal function which is called from locale_compose
     759             : * gets the value for the key_name and appends to the loc_name
     760             : * returns 1 if successful , -1 if not found ,
     761             : * 0 if array element is not a string , -2 if buffer-overflow
     762             : */
     763          84 : static int append_key_value(smart_str* loc_name, HashTable* hash_arr, char* key_name)
     764             : {
     765             :         zval *ele_value;
     766             : 
     767          84 :         if ((ele_value = zend_hash_str_find(hash_arr , key_name, strlen(key_name))) != NULL ) {
     768          52 :                 if(Z_TYPE_P(ele_value)!= IS_STRING ){
     769             :                         /* element value is not a string */
     770           2 :                         return FAILURE;
     771             :                 }
     772          80 :                 if(strcmp(key_name, LOC_LANG_TAG) != 0 &&
     773          30 :                    strcmp(key_name, LOC_GRANDFATHERED_LANG_TAG)!=0 ) {
     774             :                         /* not lang or grandfathered tag */
     775             :                         smart_str_appendl(loc_name, SEPARATOR , sizeof(SEPARATOR)-1);
     776             :                 }
     777          50 :                 smart_str_appendl(loc_name, Z_STRVAL_P(ele_value) , Z_STRLEN_P(ele_value));
     778          50 :                 return SUCCESS;
     779             :         }
     780             : 
     781          32 :         return LOC_NOT_FOUND;
     782             : }
     783             : /* }}} */
     784             : 
     785             : /* {{{ append_prefix , appends the prefix needed
     786             : * e.g. private adds 'x'
     787             : */
     788          18 : static void add_prefix(smart_str* loc_name, char* key_name)
     789             : {
     790          18 :         if( strncmp(key_name , LOC_PRIVATE_TAG , 7) == 0 ){
     791             :                 smart_str_appendl(loc_name, SEPARATOR , sizeof(SEPARATOR)-1);
     792             :                 smart_str_appendl(loc_name, PRIVATE_PREFIX , sizeof(PRIVATE_PREFIX)-1);
     793             :         }
     794          18 : }
     795             : /* }}} */
     796             : 
     797             : /* {{{ append_multiple_key_values
     798             : * Internal function which is called from locale_compose
     799             : * gets the multiple values for the key_name and appends to the loc_name
     800             : * used for 'variant','extlang','private'
     801             : * returns 1 if successful , -1 if not found ,
     802             : * 0 if array element is not a string , -2 if buffer-overflow
     803             : */
     804          56 : static int append_multiple_key_values(smart_str* loc_name, HashTable* hash_arr, char* key_name)
     805             : {
     806             :         zval    *ele_value;
     807          56 :         int     isFirstSubtag   = 0;
     808             : 
     809             :         /* Variant/ Extlang/Private etc. */
     810          56 :         if ((ele_value = zend_hash_str_find( hash_arr , key_name , strlen(key_name))) != NULL) {
     811           6 :                 if( Z_TYPE_P(ele_value) == IS_STRING ){
     812           0 :                         add_prefix( loc_name , key_name);
     813             : 
     814             :                         smart_str_appendl(loc_name, SEPARATOR , sizeof(SEPARATOR)-1);
     815           0 :                         smart_str_appendl(loc_name, Z_STRVAL_P(ele_value) , Z_STRLEN_P(ele_value));
     816           0 :                         return SUCCESS;
     817           6 :                 } else if(Z_TYPE_P(ele_value) == IS_ARRAY ) {
     818           6 :                         HashTable *arr = Z_ARRVAL_P(ele_value);
     819             :                         zval *data;
     820             : 
     821          30 :                         ZEND_HASH_FOREACH_VAL(arr, data) {
     822          12 :                                 if(Z_TYPE_P(data) != IS_STRING) {
     823           0 :                                         return FAILURE;
     824             :                                 }
     825          12 :                                 if (isFirstSubtag++ == 0){
     826           6 :                                         add_prefix(loc_name , key_name);
     827             :                                 }
     828             :                                 smart_str_appendl(loc_name, SEPARATOR , sizeof(SEPARATOR)-1);
     829          12 :                                 smart_str_appendl(loc_name, Z_STRVAL_P(data) , Z_STRLEN_P(data));
     830             :                         } ZEND_HASH_FOREACH_END();
     831           6 :                         return SUCCESS;
     832             :                 } else {
     833           0 :                         return FAILURE;
     834             :                 }
     835             :         } else {
     836             :                 char cur_key_name[31];
     837          50 :                 int  max_value = 0, i;
     838             :                 /* Decide the max_value: the max. no. of elements allowed */
     839          50 :                 if( strcmp(key_name , LOC_VARIANT_TAG) ==0 ){
     840          16 :                         max_value  = MAX_NO_VARIANT;
     841             :                 }
     842          50 :                 if( strcmp(key_name , LOC_EXTLANG_TAG) ==0 ){
     843          18 :                         max_value  = MAX_NO_EXTLANG;
     844             :                 }
     845          50 :                 if( strcmp(key_name , LOC_PRIVATE_TAG) ==0 ){
     846          16 :                         max_value  = MAX_NO_PRIVATE;
     847             :                 }
     848             : 
     849             :                 /* Multiple variant values as variant0, variant1 ,variant2 */
     850          50 :                 isFirstSubtag = 0;
     851         582 :                 for( i=0 ; i< max_value; i++ ){
     852         534 :                         snprintf( cur_key_name , 30, "%s%d", key_name , i);
     853         534 :                         if ((ele_value = zend_hash_str_find( hash_arr , cur_key_name , strlen(cur_key_name))) != NULL) {
     854          26 :                                 if( Z_TYPE_P(ele_value)!= IS_STRING ){
     855             :                                         /* variant is not a string */
     856           2 :                                         return FAILURE;
     857             :                                 }
     858             :                                 /* Add the contents */
     859          24 :                                 if (isFirstSubtag++ == 0){
     860          12 :                                         add_prefix(loc_name , cur_key_name);
     861             :                                 }
     862             :                                 smart_str_appendl(loc_name, SEPARATOR , sizeof(SEPARATOR)-1);
     863          24 :                                 smart_str_appendl(loc_name, Z_STRVAL_P(ele_value) , Z_STRLEN_P(ele_value));
     864             :                         }
     865             :                 } /* end of for */
     866             :         } /* end of else */
     867             : 
     868          48 :         return SUCCESS;
     869             : }
     870             : /* }}} */
     871             : 
     872             : /*{{{
     873             : * If applicable sets error message and aborts locale_compose gracefully
     874             : * returns 0  if locale_compose needs to be aborted
     875             : * otherwise returns 1
     876             : */
     877         138 : static int handleAppendResult( int result, smart_str* loc_name)
     878             : {
     879         138 :         intl_error_reset( NULL );
     880         138 :         if( result == FAILURE) {
     881           4 :                 intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
     882             :                          "locale_compose: parameter array element is not a string", 0 );
     883             :                 smart_str_free(loc_name);
     884           4 :                 return 0;
     885             :         }
     886         134 :         return 1;
     887             : }
     888             : /* }}} */
     889             : 
     890             : #define RETURN_SMART_STR(str) smart_str_0((str)); RETURN_NEW_STR((str)->s)
     891             : /* {{{ proto static string Locale::composeLocale($array)
     892             : * Creates a locale by combining the parts of locale-ID passed
     893             : * }}} */
     894             : /* {{{ proto static string compose_locale($array)
     895             : * Creates a locale by combining the parts of locale-ID passed
     896             : * }}} */
     897          25 : PHP_FUNCTION(locale_compose)
     898             : {
     899          25 :         smart_str       loc_name_s = {0};
     900          25 :         smart_str *loc_name = &loc_name_s;
     901          25 :         zval*                   arr     = NULL;
     902          25 :         HashTable*              hash_arr = NULL;
     903          25 :         int                     result = 0;
     904             : 
     905          25 :         intl_error_reset( NULL );
     906             : 
     907          25 :         if(zend_parse_parameters( ZEND_NUM_ARGS(), "a",
     908             :                 &arr) == FAILURE)
     909             :         {
     910           1 :                 RETURN_FALSE;
     911             :         }
     912             : 
     913          24 :         hash_arr = Z_ARRVAL_P( arr );
     914             : 
     915          24 :         if( !hash_arr || zend_hash_num_elements( hash_arr ) == 0 )
     916           0 :                 RETURN_FALSE;
     917             : 
     918             :         /* Check for grandfathered first */
     919          24 :         result = append_key_value(loc_name, hash_arr,  LOC_GRANDFATHERED_LANG_TAG);
     920          24 :         if( result == SUCCESS){
     921           0 :                 RETURN_SMART_STR(loc_name);
     922             :         }
     923          24 :         if( !handleAppendResult( result, loc_name)){
     924           0 :                 RETURN_FALSE;
     925             :         }
     926             : 
     927             :         /* Not grandfathered */
     928          24 :         result = append_key_value(loc_name, hash_arr , LOC_LANG_TAG);
     929          24 :         if( result == LOC_NOT_FOUND ){
     930           2 :                 intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
     931             :                 "locale_compose: parameter array does not contain 'language' tag.", 0 );
     932             :                 smart_str_free(loc_name);
     933           2 :                 RETURN_FALSE;
     934             :         }
     935          22 :         if( !handleAppendResult( result, loc_name)){
     936           2 :                 RETURN_FALSE;
     937             :         }
     938             : 
     939             :         /* Extlang */
     940          20 :         result = append_multiple_key_values(loc_name, hash_arr , LOC_EXTLANG_TAG);
     941          20 :         if( !handleAppendResult( result, loc_name)){
     942           2 :                 RETURN_FALSE;
     943             :         }
     944             : 
     945             :         /* Script */
     946          18 :         result = append_key_value(loc_name, hash_arr , LOC_SCRIPT_TAG);
     947          18 :         if( !handleAppendResult( result, loc_name)){
     948           0 :                 RETURN_FALSE;
     949             :         }
     950             : 
     951             :         /* Region */
     952          18 :         result = append_key_value( loc_name, hash_arr , LOC_REGION_TAG);
     953          18 :         if( !handleAppendResult( result, loc_name)){
     954           0 :                 RETURN_FALSE;
     955             :         }
     956             : 
     957             :         /* Variant */
     958          18 :         result = append_multiple_key_values( loc_name, hash_arr , LOC_VARIANT_TAG);
     959          18 :         if( !handleAppendResult( result, loc_name)){
     960           0 :                 RETURN_FALSE;
     961             :         }
     962             : 
     963             :         /* Private */
     964          18 :         result = append_multiple_key_values( loc_name, hash_arr , LOC_PRIVATE_TAG);
     965          18 :         if( !handleAppendResult( result, loc_name)){
     966           0 :                 RETURN_FALSE;
     967             :         }
     968             : 
     969          18 :         RETURN_SMART_STR(loc_name);
     970             : }
     971             : /* }}} */
     972             : 
     973             : 
     974             : /*{{{
     975             : * Parses the locale and returns private subtags  if existing
     976             : * else returns NULL
     977             : * e.g. for locale='en_US-x-prv1-prv2-prv3'
     978             : * returns a pointer to the string 'prv1-prv2-prv3'
     979             : */
     980          68 : static zend_string* get_private_subtags(const char* loc_name)
     981             : {
     982          68 :         zend_string* result = NULL;
     983          68 :         size_t       len = 0;
     984          68 :         const char*  mod_loc_name =NULL;
     985             : 
     986          68 :         if( loc_name && (len = strlen(loc_name)) > 0 ){
     987          68 :                 zend_off_t singletonPos = 0;
     988          68 :                 mod_loc_name = loc_name ;
     989         152 :                 while( (singletonPos = getSingletonPos(mod_loc_name)) > -1){
     990          26 :                         if( (*(mod_loc_name+singletonPos)=='x') || (*(mod_loc_name+singletonPos)=='X') ){
     991             :                                 /* private subtag start found */
     992          10 :                                 if( singletonPos + 2 ==  len){
     993             :                                         /* loc_name ends with '-x-' ; return  NULL */
     994             :                                 }
     995             :                                 else{
     996             :                                         /* result = mod_loc_name + singletonPos +2; */
     997          20 :                                         result = zend_string_init(mod_loc_name + singletonPos+2  , (len -( singletonPos +2) ), 0);
     998             :                                 }
     999          10 :                                 break;
    1000             :                         }
    1001             :                         else{
    1002          16 :                                 if((size_t)(singletonPos + 1) >= len){
    1003             :                                         /* String end */
    1004           0 :                                         break;
    1005             :                                 } else {
    1006             :                                         /* singleton found but not a private subtag , hence check further in the string for the private subtag */
    1007          16 :                                         mod_loc_name = mod_loc_name + singletonPos +1;
    1008          16 :                                         len = strlen(mod_loc_name);
    1009             :                                 }
    1010             :                         }
    1011             :                 } /* end of while */
    1012             :         }
    1013             : 
    1014          68 :         return result;
    1015             : }
    1016             : /* }}} */
    1017             : 
    1018             : /* {{{ code used by locale_parse
    1019             : */
    1020         340 : static int add_array_entry(const char* loc_name, zval* hash_arr, char* key_name)
    1021             : {
    1022         340 :         zend_string*   key_value        = NULL;
    1023         340 :         char*   cur_key_name    = NULL;
    1024         340 :         char*   token           = NULL;
    1025         340 :         char*   last_ptr        = NULL;
    1026             : 
    1027         340 :         int     result          = 0;
    1028         340 :         int     cur_result      = 0;
    1029             : 
    1030             : 
    1031         340 :         if( strcmp(key_name , LOC_PRIVATE_TAG)==0 ){
    1032          68 :                 key_value = get_private_subtags( loc_name );
    1033          68 :                 result = 1;
    1034             :         } else {
    1035         272 :                 key_value = get_icu_value_internal( loc_name , key_name , &result,1 );
    1036             :         }
    1037         612 :         if( (strcmp(key_name , LOC_PRIVATE_TAG)==0) ||
    1038         272 :                 ( strcmp(key_name , LOC_VARIANT_TAG)==0) ){
    1039         136 :                 if( result > 0 && key_value){
    1040          26 :                         int cnt = 0;
    1041             :                         /* Tokenize on the "_" or "-"  */
    1042          26 :                         token = php_strtok_r( key_value->val , DELIMITER ,&last_ptr);
    1043          26 :                         if( cur_key_name ){
    1044           0 :                                 efree( cur_key_name);
    1045             :                         }
    1046          26 :                         cur_key_name = (char*)ecalloc( 25,  25);
    1047          26 :                         sprintf( cur_key_name , "%s%d", key_name , cnt++);
    1048          26 :                         add_assoc_string( hash_arr, cur_key_name , token);
    1049             :                         /* tokenize on the "_" or "-" and stop  at singleton if any */
    1050          58 :                         while( (token = php_strtok_r(NULL , DELIMITER , &last_ptr)) && (strlen(token)>1) ){
    1051           6 :                                 sprintf( cur_key_name , "%s%d", key_name , cnt++);
    1052           6 :                                 add_assoc_string( hash_arr, cur_key_name , token);
    1053             :                         }
    1054             : /*
    1055             :                         if( strcmp(key_name, LOC_PRIVATE_TAG) == 0 ){
    1056             :                         }
    1057             : */
    1058             :                 }
    1059         272 :                 if (key_value) {
    1060             :                         zend_string_release_ex(key_value, 0);
    1061             :                 }
    1062             :         } else {
    1063         204 :                 if( result == 1 ){
    1064         120 :                         add_assoc_str( hash_arr, key_name , key_value);
    1065         120 :                         cur_result = 1;
    1066          84 :                 } else if (key_value) {
    1067             :                         zend_string_release_ex(key_value, 0);
    1068             :                 }
    1069             :         }
    1070             : 
    1071         340 :         if( cur_key_name ){
    1072          26 :                 efree( cur_key_name);
    1073             :         }
    1074             :         /*if( key_name != LOC_PRIVATE_TAG && key_value){*/
    1075         340 :         return cur_result;
    1076             : }
    1077             : /* }}} */
    1078             : 
    1079             : /* {{{ proto static array Locale::parseLocale($locale)
    1080             : * parses a locale-id into an array the different parts of it
    1081             :  }}} */
    1082             : /* {{{ proto static array parse_locale($locale)
    1083             : * parses a locale-id into an array the different parts of it
    1084             : */
    1085          73 : PHP_FUNCTION(locale_parse)
    1086             : {
    1087          73 :     const char* loc_name        = NULL;
    1088          73 :     size_t         loc_name_len    = 0;
    1089          73 :     int         grOffset        = 0;
    1090             : 
    1091          73 :     intl_error_reset( NULL );
    1092             : 
    1093          73 :     if(zend_parse_parameters( ZEND_NUM_ARGS(), "s",
    1094             :         &loc_name, &loc_name_len ) == FAILURE)
    1095             :     {
    1096           2 :         RETURN_FALSE;
    1097             :     }
    1098             : 
    1099          72 :     INTL_CHECK_LOCALE_LEN(strlen(loc_name));
    1100             : 
    1101          72 :     if(loc_name_len == 0) {
    1102           0 :         loc_name = intl_locale_get_default();
    1103             :     }
    1104             : 
    1105          72 :         array_init( return_value );
    1106             : 
    1107          72 :         grOffset =  findOffset( LOC_GRANDFATHERED , loc_name );
    1108          72 :         if( grOffset >= 0 ){
    1109           4 :                 add_assoc_string( return_value , LOC_GRANDFATHERED_LANG_TAG, (char *)loc_name);
    1110             :         }
    1111             :         else{
    1112             :                 /* Not grandfathered */
    1113          68 :                 add_array_entry( loc_name , return_value , LOC_LANG_TAG);
    1114          68 :                 add_array_entry( loc_name , return_value , LOC_SCRIPT_TAG);
    1115          68 :                 add_array_entry( loc_name , return_value , LOC_REGION_TAG);
    1116          68 :                 add_array_entry( loc_name , return_value , LOC_VARIANT_TAG);
    1117          68 :                 add_array_entry( loc_name , return_value , LOC_PRIVATE_TAG);
    1118             :         }
    1119             : }
    1120             : /* }}} */
    1121             : 
    1122             : /* {{{ proto static array Locale::getAllVariants($locale)
    1123             : * gets an array containing the list of variants, or null
    1124             :  }}} */
    1125             : /* {{{ proto static array locale_get_all_variants($locale)
    1126             : * gets an array containing the list of variants, or null
    1127             : */
    1128          23 : PHP_FUNCTION(locale_get_all_variants)
    1129             : {
    1130          23 :         const char*     loc_name        = NULL;
    1131          23 :         size_t                  loc_name_len    = 0;
    1132             : 
    1133          23 :         int     result          = 0;
    1134          23 :         char*   token           = NULL;
    1135          23 :         zend_string*    variant         = NULL;
    1136          23 :         char*   saved_ptr       = NULL;
    1137             : 
    1138          23 :         intl_error_reset( NULL );
    1139             : 
    1140          23 :         if(zend_parse_parameters( ZEND_NUM_ARGS(), "s",
    1141             :         &loc_name, &loc_name_len ) == FAILURE)
    1142             :         {
    1143           2 :                 RETURN_FALSE;
    1144             :         }
    1145             : 
    1146          22 :         if(loc_name_len == 0) {
    1147           0 :                 loc_name = intl_locale_get_default();
    1148           0 :                 loc_name_len = strlen(loc_name);
    1149             :         }
    1150             : 
    1151          22 :         INTL_CHECK_LOCALE_LEN(loc_name_len);
    1152             : 
    1153          22 :         array_init( return_value );
    1154             : 
    1155             :         /* If the locale is grandfathered, stop, no variants */
    1156          22 :         if( findOffset( LOC_GRANDFATHERED , loc_name ) >=  0 ){
    1157             :                 /* ("Grandfathered Tag. No variants."); */
    1158             :         }
    1159             :         else {
    1160             :         /* Call ICU variant */
    1161          10 :                 variant = get_icu_value_internal( loc_name , LOC_VARIANT_TAG , &result ,0);
    1162          10 :                 if( result > 0 && variant){
    1163             :                         /* Tokenize on the "_" or "-" */
    1164          10 :                         token = php_strtok_r( variant->val , DELIMITER , &saved_ptr);
    1165          10 :                         add_next_index_stringl( return_value, token , strlen(token));
    1166             :                         /* tokenize on the "_" or "-" and stop  at singleton if any */
    1167          26 :                         while( (token = php_strtok_r(NULL , DELIMITER, &saved_ptr)) && (strlen(token)>1) ){
    1168           6 :                                 add_next_index_stringl( return_value, token , strlen(token));
    1169             :                         }
    1170             :                 }
    1171          10 :                 if( variant ){
    1172             :                         zend_string_release_ex( variant, 0 );
    1173             :                 }
    1174             :         }
    1175             : 
    1176             : 
    1177             : }
    1178             : /* }}} */
    1179             : 
    1180             : /*{{{
    1181             : * Converts to lower case and also replaces all hyphens with the underscore
    1182             : */
    1183        1343 : static int strToMatch(const char* str ,char *retstr)
    1184             : {
    1185        1343 :         char*   anchor  = NULL;
    1186        1343 :         const char*     anchor1 = NULL;
    1187        1343 :         int     result  = 0;
    1188             : 
    1189        1343 :     if( (!str) || str[0] == '\0'){
    1190           0 :         return result;
    1191             :     } else {
    1192        1343 :         anchor = retstr;
    1193        1343 :         anchor1 = str;
    1194       14759 :         while( (*str)!='\0' ){
    1195       12073 :                 if( *str == '-' ){
    1196         720 :                         *retstr =  '_';
    1197             :                 } else {
    1198       11353 :                         *retstr = tolower(*str);
    1199             :                 }
    1200       12073 :             str++;
    1201       12073 :             retstr++;
    1202             :         }
    1203        1343 :         *retstr = '\0';
    1204        1343 :         retstr=  anchor;
    1205        1343 :         str=  anchor1;
    1206        1343 :         result = 1;
    1207             :     }
    1208             : 
    1209        1343 :     return(result);
    1210             : }
    1211             : /* }}} */
    1212             : 
    1213             : /* {{{ proto static boolean Locale::filterMatches(string $langtag, string $locale[, bool $canonicalize])
    1214             : * Checks if a $langtag filter matches with $locale according to RFC 4647's basic filtering algorithm
    1215             : */
    1216             : /* }}} */
    1217             : /* {{{ proto bool locale_filter_matches(string $langtag, string $locale[, bool $canonicalize])
    1218             : * Checks if a $langtag filter matches with $locale according to RFC 4647's basic filtering algorithm
    1219             : */
    1220         385 : PHP_FUNCTION(locale_filter_matches)
    1221             : {
    1222         385 :         char*           lang_tag        = NULL;
    1223         385 :         size_t          lang_tag_len    = 0;
    1224         385 :         const char*     loc_range       = NULL;
    1225         385 :         size_t          loc_range_len   = 0;
    1226             : 
    1227         385 :         int             result          = 0;
    1228         385 :         char*           token           = 0;
    1229         385 :         char*           chrcheck        = NULL;
    1230             : 
    1231         385 :         zend_string*    can_lang_tag    = NULL;
    1232         385 :         zend_string*    can_loc_range   = NULL;
    1233             : 
    1234         385 :         char*           cur_lang_tag    = NULL;
    1235         385 :         char*           cur_loc_range   = NULL;
    1236             : 
    1237         385 :         zend_bool       boolCanonical   = 0;
    1238         385 :         UErrorCode      status          = U_ZERO_ERROR;
    1239             : 
    1240         385 :         intl_error_reset( NULL );
    1241             : 
    1242         385 :         if(zend_parse_parameters( ZEND_NUM_ARGS(), "ss|b",
    1243             :                 &lang_tag, &lang_tag_len , &loc_range , &loc_range_len ,
    1244             :                 &boolCanonical) == FAILURE)
    1245             :         {
    1246           1 :                 RETURN_FALSE;
    1247             :         }
    1248             : 
    1249         384 :         if(loc_range_len == 0) {
    1250           0 :                 loc_range = intl_locale_get_default();
    1251           0 :                 loc_range_len = strlen(loc_range);
    1252             :         }
    1253             : 
    1254         384 :         if( strcmp(loc_range,"*")==0){
    1255           0 :                 RETURN_TRUE;
    1256             :         }
    1257             : 
    1258         384 :         INTL_CHECK_LOCALE_LEN(loc_range_len);
    1259         384 :         INTL_CHECK_LOCALE_LEN(lang_tag_len);
    1260             : 
    1261         384 :         if( boolCanonical ){
    1262             :                 /* canonicalize loc_range */
    1263         192 :                 can_loc_range=get_icu_value_internal( loc_range , LOC_CANONICALIZE_TAG , &result , 0);
    1264         192 :                 if( result ==0) {
    1265           0 :                         intl_error_set( NULL, status,
    1266             :                                 "locale_filter_matches : unable to canonicalize loc_range" , 0 );
    1267           0 :                         RETURN_FALSE;
    1268             :                 }
    1269             : 
    1270             :                 /* canonicalize lang_tag */
    1271         192 :                 can_lang_tag = get_icu_value_internal( lang_tag , LOC_CANONICALIZE_TAG , &result ,  0);
    1272         192 :                 if( result ==0) {
    1273           0 :                         intl_error_set( NULL, status,
    1274             :                                 "locale_filter_matches : unable to canonicalize lang_tag" , 0 );
    1275           0 :                         RETURN_FALSE;
    1276             :                 }
    1277             : 
    1278             :                 /* Convert to lower case for case-insensitive comparison */
    1279         192 :                 cur_lang_tag = ecalloc( 1, can_lang_tag->len + 1);
    1280             : 
    1281             :                 /* Convert to lower case for case-insensitive comparison */
    1282         192 :                 result = strToMatch( can_lang_tag->val , cur_lang_tag);
    1283         192 :                 if( result == 0) {
    1284           0 :                         efree( cur_lang_tag );
    1285             :                         zend_string_release_ex( can_lang_tag, 0 );
    1286           0 :                         RETURN_FALSE;
    1287             :                 }
    1288             : 
    1289         192 :                 cur_loc_range = ecalloc( 1, can_loc_range->len + 1);
    1290         192 :                 result = strToMatch( can_loc_range->val , cur_loc_range );
    1291         192 :                 if( result == 0) {
    1292           0 :                         efree( cur_lang_tag );
    1293             :                         zend_string_release_ex( can_lang_tag, 0 );
    1294           0 :                         efree( cur_loc_range );
    1295             :                         zend_string_release_ex( can_loc_range, 0 );
    1296           0 :                         RETURN_FALSE;
    1297             :                 }
    1298             : 
    1299             :                 /* check if prefix */
    1300         192 :                 token   = strstr( cur_lang_tag , cur_loc_range );
    1301             : 
    1302         192 :                 if( token && (token==cur_lang_tag) ){
    1303             :                         /* check if the char. after match is SEPARATOR */
    1304          32 :                         chrcheck = token + (strlen(cur_loc_range));
    1305          32 :                         if( isIDSeparator(*chrcheck) || isEndOfTag(*chrcheck) ){
    1306          30 :                                 efree( cur_lang_tag );
    1307          30 :                                 efree( cur_loc_range );
    1308          30 :                                 if( can_lang_tag){
    1309             :                                         zend_string_release_ex( can_lang_tag, 0 );
    1310             :                                 }
    1311          30 :                                 if( can_loc_range){
    1312             :                                         zend_string_release_ex( can_loc_range, 0 );
    1313             :                                 }
    1314          30 :                                 RETURN_TRUE;
    1315             :                         }
    1316             :                 }
    1317             : 
    1318             :                 /* No prefix as loc_range */
    1319         162 :                 if( cur_lang_tag){
    1320         162 :                         efree( cur_lang_tag );
    1321             :                 }
    1322         162 :                 if( cur_loc_range){
    1323         162 :                         efree( cur_loc_range );
    1324             :                 }
    1325         162 :                 if( can_lang_tag){
    1326             :                         zend_string_release_ex( can_lang_tag, 0 );
    1327             :                 }
    1328         162 :                 if( can_loc_range){
    1329             :                         zend_string_release_ex( can_loc_range, 0 );
    1330             :                 }
    1331         162 :                 RETURN_FALSE;
    1332             : 
    1333             :         } /* end of if isCanonical */
    1334             :         else{
    1335             :                 /* Convert to lower case for case-insensitive comparison */
    1336         192 :                 cur_lang_tag = ecalloc( 1, strlen(lang_tag ) + 1);
    1337             : 
    1338         192 :                 result = strToMatch( lang_tag , cur_lang_tag);
    1339         192 :                 if( result == 0) {
    1340           0 :                         efree( cur_lang_tag );
    1341           0 :                         RETURN_FALSE;
    1342             :                 }
    1343         192 :                 cur_loc_range = ecalloc( 1, strlen(loc_range ) + 1);
    1344         192 :                 result = strToMatch( loc_range , cur_loc_range );
    1345         192 :                 if( result == 0) {
    1346           0 :                         efree( cur_lang_tag );
    1347           0 :                         efree( cur_loc_range );
    1348           0 :                         RETURN_FALSE;
    1349             :                 }
    1350             : 
    1351             :                 /* check if prefix */
    1352         192 :                 token   = strstr( cur_lang_tag , cur_loc_range );
    1353             : 
    1354         192 :                 if( token && (token==cur_lang_tag) ){
    1355             :                         /* check if the char. after match is SEPARATOR */
    1356          32 :                         chrcheck = token + (strlen(cur_loc_range));
    1357          32 :                         if( isIDSeparator(*chrcheck) || isEndOfTag(*chrcheck) ){
    1358          30 :                                 efree( cur_lang_tag );
    1359          30 :                                 efree( cur_loc_range );
    1360          30 :                                 RETURN_TRUE;
    1361             :                         }
    1362             :                 }
    1363             : 
    1364             :                 /* No prefix as loc_range */
    1365         162 :                 if( cur_lang_tag){
    1366         162 :                         efree( cur_lang_tag );
    1367             :                 }
    1368         162 :                 if( cur_loc_range){
    1369         162 :                         efree( cur_loc_range );
    1370             :                 }
    1371         162 :                 RETURN_FALSE;
    1372             : 
    1373             :         }
    1374             : }
    1375             : /* }}} */
    1376             : 
    1377          26 : static void array_cleanup( char* arr[] , int arr_size)
    1378             : {
    1379          26 :         int i=0;
    1380         387 :         for( i=0; i< arr_size; i++ ){
    1381         361 :                 if( arr[i*2] ){
    1382         361 :                         efree( arr[i*2]);
    1383             :                 }
    1384             :         }
    1385          26 :         efree(arr);
    1386          26 : }
    1387             : 
    1388             : #define LOOKUP_CLEAN_RETURN(value)      array_cleanup(cur_arr, cur_arr_len); return (value)
    1389             : /* {{{
    1390             : * returns the lookup result to lookup_loc_range_src_php
    1391             : * internal function
    1392             : */
    1393          26 : static zend_string* lookup_loc_range(const char* loc_range, HashTable* hash_arr, int canonicalize )
    1394             : {
    1395          26 :         int     i = 0;
    1396          26 :         int     cur_arr_len = 0;
    1397          26 :         int result = 0;
    1398             : 
    1399          26 :         zend_string* lang_tag = NULL;
    1400          26 :         zval* ele_value = NULL;
    1401             : 
    1402          26 :         char* cur_loc_range     = NULL;
    1403          26 :         zend_string* can_loc_range      = NULL;
    1404          26 :         zend_off_t saved_pos = 0;
    1405             : 
    1406          26 :         zend_string* return_value = NULL;
    1407             : 
    1408          26 :         char **cur_arr = ecalloc(zend_hash_num_elements(hash_arr)*2, sizeof(char *));
    1409         748 :         ZEND_HASH_FOREACH_VAL(hash_arr, ele_value) {
    1410             :         /* convert the array to lowercase , also replace hyphens with the underscore and store it in cur_arr */
    1411         361 :                 if(Z_TYPE_P(ele_value)!= IS_STRING) {
    1412             :                         /* element value is not a string */
    1413           0 :                         intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR, "lookup_loc_range: locale array element is not a string", 0);
    1414           0 :                         LOOKUP_CLEAN_RETURN(NULL);
    1415             :                 }
    1416         361 :                 cur_arr[cur_arr_len*2] = estrndup(Z_STRVAL_P(ele_value), Z_STRLEN_P(ele_value));
    1417         361 :                 result = strToMatch(Z_STRVAL_P(ele_value), cur_arr[cur_arr_len*2]);
    1418         361 :                 if(result == 0) {
    1419           0 :                         intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR, "lookup_loc_range: unable to canonicalize lang_tag", 0);
    1420           0 :                         LOOKUP_CLEAN_RETURN(NULL);
    1421             :                 }
    1422         361 :                 cur_arr[cur_arr_len*2+1] = Z_STRVAL_P(ele_value);
    1423         361 :                 cur_arr_len++ ;
    1424             :         } ZEND_HASH_FOREACH_END(); /* end of for */
    1425             : 
    1426             :         /* Canonicalize array elements */
    1427          26 :         if(canonicalize) {
    1428         203 :                 for(i=0; i<cur_arr_len; i++) {
    1429         188 :                         lang_tag = get_icu_value_internal(cur_arr[i*2], LOC_CANONICALIZE_TAG, &result, 0);
    1430         188 :                         if(result != 1 || lang_tag == NULL || !lang_tag->val[0]) {
    1431           0 :                                 if(lang_tag) {
    1432             :                                         zend_string_release_ex(lang_tag, 0);
    1433             :                                 }
    1434           0 :                                 intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR, "lookup_loc_range: unable to canonicalize lang_tag" , 0);
    1435           0 :                                 LOOKUP_CLEAN_RETURN(NULL);
    1436             :                         }
    1437         188 :                         cur_arr[i*2] = erealloc(cur_arr[i*2], lang_tag->len+1);
    1438         188 :                         result = strToMatch(lang_tag->val, cur_arr[i*2]);
    1439             :                         zend_string_release_ex(lang_tag, 0);
    1440         188 :                         if(result == 0) {
    1441           0 :                                 intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR, "lookup_loc_range: unable to canonicalize lang_tag" , 0);
    1442           0 :                                 LOOKUP_CLEAN_RETURN(NULL);
    1443             :                         }
    1444             :                 }
    1445             : 
    1446             :         }
    1447             : 
    1448          26 :         if(canonicalize) {
    1449             :                 /* Canonicalize the loc_range */
    1450          15 :                 can_loc_range = get_icu_value_internal(loc_range, LOC_CANONICALIZE_TAG, &result , 0);
    1451          15 :                 if( result != 1 || can_loc_range == NULL || !can_loc_range->val[0]) {
    1452             :                         /* Error */
    1453           0 :                         intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR, "lookup_loc_range: unable to canonicalize loc_range" , 0 );
    1454           0 :                         if(can_loc_range) {
    1455             :                                 zend_string_release_ex(can_loc_range, 0);
    1456             :                         }
    1457           0 :                         LOOKUP_CLEAN_RETURN(NULL);
    1458             :                 } else {
    1459          15 :                         loc_range = can_loc_range->val;
    1460             :                 }
    1461             :         }
    1462             : 
    1463          26 :         cur_loc_range = ecalloc(1, strlen(loc_range)+1);
    1464             :         /* convert to lower and replace hyphens */
    1465          26 :         result = strToMatch(loc_range, cur_loc_range);
    1466          26 :         if(can_loc_range) {
    1467             :                 zend_string_release_ex(can_loc_range, 0);
    1468             :         }
    1469          26 :         if(result == 0) {
    1470           0 :                 intl_error_set(NULL, U_ILLEGAL_ARGUMENT_ERROR, "lookup_loc_range: unable to canonicalize lang_tag" , 0);
    1471           0 :                 LOOKUP_CLEAN_RETURN(NULL);
    1472             :         }
    1473             : 
    1474             :         /* Lookup for the lang_tag match */
    1475          26 :         saved_pos = strlen(cur_loc_range);
    1476          61 :         while(saved_pos > 0) {
    1477         270 :                 for(i=0; i< cur_arr_len; i++){
    1478         261 :                         if(cur_arr[i*2] != NULL && strlen(cur_arr[i*2]) == saved_pos && strncmp(cur_loc_range, cur_arr[i*2], saved_pos) == 0) {
    1479             :                                 /* Match found */
    1480          24 :                                 char *str = canonicalize ? cur_arr[i*2] : cur_arr[i*2+1];
    1481          48 :                                 return_value = zend_string_init(str, strlen(str), 0);
    1482          24 :                                 efree(cur_loc_range);
    1483          24 :                                 LOOKUP_CLEAN_RETURN(return_value);
    1484             :                         }
    1485             :                 }
    1486           9 :                 saved_pos = getStrrtokenPos(cur_loc_range, saved_pos);
    1487             :         }
    1488             : 
    1489             :         /* Match not found */
    1490           2 :         efree(cur_loc_range);
    1491           2 :         LOOKUP_CLEAN_RETURN(NULL);
    1492             : }
    1493             : /* }}} */
    1494             : 
    1495             : /* {{{ proto string Locale::lookup(array $langtag, string $locale[, bool $canonicalize[, string $default = null]])
    1496             : * Searches the items in $langtag for the best match to the language
    1497             : * range
    1498             : */
    1499             : /* }}} */
    1500             : /* {{{ proto string locale_lookup(array $langtag, string $locale[, bool $canonicalize[, string $default = null]])
    1501             : * Searches the items in $langtag for the best match to the language
    1502             : * range
    1503             : */
    1504          27 : PHP_FUNCTION(locale_lookup)
    1505             : {
    1506          27 :         zend_string*    fallback_loc_str        = NULL;
    1507          27 :         const char*     loc_range               = NULL;
    1508          27 :         size_t          loc_range_len           = 0;
    1509             : 
    1510          27 :         zval*           arr                             = NULL;
    1511          27 :         HashTable*      hash_arr                = NULL;
    1512          27 :         zend_bool       boolCanonical   = 0;
    1513          27 :         zend_string*    result_str      = NULL;
    1514             : 
    1515          27 :         intl_error_reset( NULL );
    1516             : 
    1517          27 :         if(zend_parse_parameters( ZEND_NUM_ARGS(), "as|bS", &arr, &loc_range, &loc_range_len,
    1518             :                 &boolCanonical,     &fallback_loc_str) == FAILURE) {
    1519           1 :                 RETURN_FALSE;
    1520             :         }
    1521             : 
    1522          26 :         if(loc_range_len == 0) {
    1523           4 :                 if(fallback_loc_str) {
    1524           3 :                         loc_range = ZSTR_VAL(fallback_loc_str);
    1525           3 :                         loc_range_len = ZSTR_LEN(fallback_loc_str);
    1526             :                 } else {
    1527           1 :                         loc_range = intl_locale_get_default();
    1528           1 :                         loc_range_len = strlen(loc_range);
    1529             :                 }
    1530             :         }
    1531             : 
    1532          26 :         hash_arr = Z_ARRVAL_P(arr);
    1533             : 
    1534          26 :         INTL_CHECK_LOCALE_LEN(loc_range_len);
    1535             : 
    1536          26 :         if( !hash_arr || zend_hash_num_elements( hash_arr ) == 0 ) {
    1537           0 :                 RETURN_EMPTY_STRING();
    1538             :         }
    1539             : 
    1540          26 :         result_str = lookup_loc_range(loc_range, hash_arr, boolCanonical);
    1541          26 :         if(result_str == NULL || ZSTR_VAL(result_str)[0] == '\0') {
    1542           2 :                 if( fallback_loc_str ) {
    1543           4 :                         result_str = zend_string_copy(fallback_loc_str);
    1544             :                 } else {
    1545           0 :                         RETURN_EMPTY_STRING();
    1546             :                 }
    1547             :         }
    1548             : 
    1549          52 :         RETURN_STR(result_str);
    1550             : }
    1551             : /* }}} */
    1552             : 
    1553             : /* {{{ proto string Locale::acceptFromHttp(string $http_accept)
    1554             : * Tries to find out best available locale based on HTTP �Accept-Language� header
    1555             : */
    1556             : /* }}} */
    1557             : /* {{{ proto string locale_accept_from_http(string $http_accept)
    1558             : * Tries to find out best available locale based on HTTP �Accept-Language� header
    1559             : */
    1560          15 : PHP_FUNCTION(locale_accept_from_http)
    1561             : {
    1562             :         UEnumeration *available;
    1563          15 :         char *http_accept = NULL;
    1564             :         size_t http_accept_len;
    1565          15 :         UErrorCode status = 0;
    1566             :         int len;
    1567             :         char resultLocale[INTL_MAX_LOCALE_LEN+1];
    1568             :         UAcceptResult outResult;
    1569             : 
    1570          15 :         if(zend_parse_parameters( ZEND_NUM_ARGS(), "s", &http_accept, &http_accept_len) == FAILURE)
    1571             :         {
    1572           1 :                 RETURN_FALSE;
    1573             :         }
    1574          14 :         if(http_accept_len > ULOC_FULLNAME_CAPACITY) {
    1575             :                 /* check each fragment, if any bigger than capacity, can't do it due to bug #72533 */
    1576           4 :                 char *start = http_accept;
    1577             :                 char *end;
    1578             :                 size_t len;
    1579             :                 do {
    1580         516 :                         end = strchr(start, ',');
    1581         516 :                         len = end ? end-start : http_accept_len-(start-http_accept);
    1582         516 :                         if(len > ULOC_FULLNAME_CAPACITY) {
    1583           2 :                                 intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR,
    1584             :                                                 "locale_accept_from_http: locale string too long", 0 );
    1585           2 :                                 RETURN_FALSE;
    1586             :                         }
    1587         514 :                         if(end) {
    1588         512 :                                 start = end+1;
    1589             :                         }
    1590         514 :                 } while(end != NULL);
    1591             :         }
    1592             : 
    1593          12 :         available = ures_openAvailableLocales(NULL, &status);
    1594          12 :         INTL_CHECK_STATUS(status, "locale_accept_from_http: failed to retrieve locale list");
    1595          12 :         len = uloc_acceptLanguageFromHTTP(resultLocale, INTL_MAX_LOCALE_LEN,
    1596             :                                                 &outResult, http_accept, available, &status);
    1597          12 :         uenum_close(available);
    1598          12 :         INTL_CHECK_STATUS(status, "locale_accept_from_http: failed to find acceptable locale");
    1599          12 :         if (len < 0 || outResult == ULOC_ACCEPT_FAILED) {
    1600           2 :                 RETURN_FALSE;
    1601             :         }
    1602          20 :         RETURN_STRINGL(resultLocale, len);
    1603             : }
    1604             : /* }}} */

Generated by: LCOV version 1.10

Generated at Mon, 06 May 2019 17:58:16 +0000 (992 days ago)

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