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