/* +----------------------------------------------------------------------+ | PHP Version 5 | +----------------------------------------------------------------------+ | This source file is subject to version 3.01 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | | available through the world-wide-web at the following url: | | http://www.php.net/license/3_01.txt | | If you did not receive a copy of the PHP license and are unable to | | obtain it through the world-wide-web, please send a note to | | license@php.net so we can mail you a copy immediately. | +----------------------------------------------------------------------+ | Authors: Vadim Savchuk | | Dmitry Lakhtyuk | +----------------------------------------------------------------------+ */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "php_intl.h" #include "collator_class.h" #include "collator_is_numeric.h" #include "collator_convert.h" #include "intl_convert.h" #include #include #if (PHP_MAJOR_VERSION == 5 && PHP_MINOR_VERSION <= 1) #define CAST_OBJECT_SHOULD_FREE ,0 #else #define CAST_OBJECT_SHOULD_FREE #endif #define COLLATOR_CONVERT_RETURN_FAILED(retval) { \ zval_add_ref( &retval ); \ return retval; \ } /* {{{ collator_convert_hash_item_from_utf8_to_utf16 */ static void collator_convert_hash_item_from_utf8_to_utf16( HashTable* hash, int hashKeyType, char* hashKey, ulong hashIndex, UErrorCode* status ) { const char* old_val; int old_val_len; UChar* new_val = NULL; int new_val_len = 0; zval** hashData = NULL; zval* znew_val = NULL; // Get current hash item. zend_hash_get_current_data( hash, (void**) &hashData ); // Process string values only. if( Z_TYPE_P( *hashData ) != IS_STRING ) return; old_val = Z_STRVAL_P( *hashData ); old_val_len = Z_STRLEN_P( *hashData ); // Convert it from UTF-8 to UTF-16LE and save the result to new_val[_len]. intl_convert_utf8_to_utf16( &new_val, &new_val_len, old_val, old_val_len, status ); if( U_FAILURE( *status ) ) return; // Update current hash item with the converted value. MAKE_STD_ZVAL( znew_val ); ZVAL_STRINGL( znew_val, (char*)new_val, UBYTES(new_val_len), FALSE ); if( hashKeyType == HASH_KEY_IS_STRING ) { zend_hash_update( hash, hashKey, strlen( hashKey ) + 1, (void*) &znew_val, sizeof(zval*), NULL ); } else // hashKeyType == HASH_KEY_IS_LONG { zend_hash_index_update( hash, hashIndex, (void*) &znew_val, sizeof(zval*), NULL ); } } /* }}} */ /* {{{ collator_convert_hash_item_from_utf16_to_utf8 */ static void collator_convert_hash_item_from_utf16_to_utf8( HashTable* hash, int hashKeyType, char* hashKey, ulong hashIndex, UErrorCode* status ) { const char* old_val; int old_val_len; char* new_val = NULL; int new_val_len = 0; zval** hashData = NULL; zval* znew_val = NULL; // Get current hash item. zend_hash_get_current_data( hash, (void**) &hashData ); // Process string values only. if( Z_TYPE_P( *hashData ) != IS_STRING ) return; old_val = Z_STRVAL_P( *hashData ); old_val_len = Z_STRLEN_P( *hashData ); // Convert it from UTF-16LE to UTF-8 and save the result to new_val[_len]. intl_convert_utf16_to_utf8( &new_val, &new_val_len, (UChar*)old_val, UCHARS(old_val_len), status ); if( U_FAILURE( *status ) ) return; // Update current hash item with the converted value. MAKE_STD_ZVAL( znew_val ); ZVAL_STRINGL( znew_val, (char*)new_val, new_val_len, FALSE ); if( hashKeyType == HASH_KEY_IS_STRING ) { zend_hash_update( hash, hashKey, strlen( hashKey ) + 1, (void*) &znew_val, sizeof(zval*), NULL ); } else // hashKeyType == HASH_KEY_IS_LONG { zend_hash_index_update( hash, hashIndex, (void*) &znew_val, sizeof(zval*), NULL ); } } /* }}} */ /* {{{ collator_convert_hash_from_utf8_to_utf16 * Convert values of the given hash from UTF-8 encoding to UTF-16LE. */ void collator_convert_hash_from_utf8_to_utf16( HashTable* hash, UErrorCode* status ) { ulong hashIndex = 0; char* hashKey = NULL; int hashKeyType = 0; zend_hash_internal_pointer_reset( hash ); while( ( hashKeyType = zend_hash_get_current_key( hash, &hashKey, &hashIndex, 0 ) ) != HASH_KEY_NON_EXISTANT ) { // Convert current hash item from UTF-8 to UTF-16LE. collator_convert_hash_item_from_utf8_to_utf16( hash, hashKeyType, hashKey, hashIndex, status ); if( U_FAILURE( *status ) ) return; // Proceed to the next item. zend_hash_move_forward( hash ); } } /* }}} */ /* {{{ collator_convert_hash_from_utf16_to_utf8 * Convert values of the given hash from UTF-16LE encoding to UTF-8. */ void collator_convert_hash_from_utf16_to_utf8( HashTable* hash, UErrorCode* status ) { ulong hashIndex = 0; char* hashKey = NULL; int hashKeyType = 0; zend_hash_internal_pointer_reset( hash ); while( ( hashKeyType = zend_hash_get_current_key( hash, &hashKey, &hashIndex, 0 ) ) != HASH_KEY_NON_EXISTANT ) { // Convert current hash item from UTF-16LE to UTF-8. collator_convert_hash_item_from_utf16_to_utf8( hash, hashKeyType, hashKey, hashIndex, status ); if( U_FAILURE( *status ) ) return; // Proceed to the next item. zend_hash_move_forward( hash ); } } /* }}} */ /* {{{ collator_convert_zstr_utf16_to_utf8 * * Convert string from utf16 to utf8. * * @param zval* utf16_zval String to convert. * * @return zval* Converted string. */ zval* collator_convert_zstr_utf16_to_utf8( zval* utf16_zval ) { zval* utf8_zval = NULL; char* str = NULL; int str_len = 0; UErrorCode status = U_ZERO_ERROR; // Convert to utf8 then. intl_convert_utf16_to_utf8( &str, &str_len, (UChar*) Z_STRVAL_P(utf16_zval), UCHARS( Z_STRLEN_P(utf16_zval) ), &status ); if( U_FAILURE( status ) ) php_error( E_WARNING, "Error converting utf16 to utf8 in collator_convert_zval_utf16_to_utf8()" ); ALLOC_INIT_ZVAL( utf8_zval ); ZVAL_STRINGL( utf8_zval, str, str_len, FALSE ); return utf8_zval; } /* }}} */ /* {{{ collator_convert_zstr_utf8_to_utf16 * * Convert string from utf8 to utf16. * * @param zval* utf8_zval String to convert. * * @return zval* Converted string. */ zval* collator_convert_zstr_utf8_to_utf16( zval* utf8_zval ) { zval* zstr = NULL; UChar* ustr = NULL; int ustr_len = 0; UErrorCode status = U_ZERO_ERROR; // Convert the string to UTF-16. intl_convert_utf8_to_utf16( &ustr, &ustr_len, Z_STRVAL_P( utf8_zval ), Z_STRLEN_P( utf8_zval ), &status ); if( U_FAILURE( status ) ) php_error( E_WARNING, "Error casting object to string in collator_convert_zstr_utf8_to_utf16()" ); // Set string. ALLOC_INIT_ZVAL( zstr ); ZVAL_STRINGL( zstr, (char*)ustr, UBYTES(ustr_len), FALSE ); return zstr; } /* }}} */ /* {{{ collator_convert_object_to_string * Convert object to UTF16-encoded string. */ zval* collator_convert_object_to_string( zval* obj TSRMLS_DC ) { zval* zstr = NULL; UErrorCode status = U_ZERO_ERROR; UChar* ustr = NULL; int ustr_len = 0; // Bail out if it's not an object. if( Z_TYPE_P( obj ) != IS_OBJECT ) { COLLATOR_CONVERT_RETURN_FAILED( obj ); } // Try object's handlers. if( Z_OBJ_HT_P(obj)->get ) { zstr = Z_OBJ_HT_P(obj)->get( obj TSRMLS_CC ); switch( Z_TYPE_P( zstr ) ) { case IS_OBJECT: { // Bail out. zval_ptr_dtor( &zstr ); COLLATOR_CONVERT_RETURN_FAILED( obj ); } break; case IS_STRING: break; default: { convert_to_string( zstr ); } break; } } else if( Z_OBJ_HT_P(obj)->cast_object ) { ALLOC_INIT_ZVAL( zstr ); if( Z_OBJ_HT_P(obj)->cast_object( obj, zstr, IS_STRING CAST_OBJECT_SHOULD_FREE TSRMLS_CC ) == FAILURE ) { // cast_object failed => bail out. zval_ptr_dtor( &zstr ); COLLATOR_CONVERT_RETURN_FAILED( obj ); } } // Object wasn't successfuly converted => bail out. if( zstr == NULL ) { COLLATOR_CONVERT_RETURN_FAILED( obj ); } // Convert the string to UTF-16. intl_convert_utf8_to_utf16( &ustr, &ustr_len, Z_STRVAL_P( zstr ), Z_STRLEN_P( zstr ), &status ); if( U_FAILURE( status ) ) php_error( E_WARNING, "Error casting object to string in collator_convert_object_to_string()" ); // Cleanup zstr to hold utf16 string. zval_dtor( zstr ); // Set string. ZVAL_STRINGL( zstr, (char*)ustr, UBYTES(ustr_len), FALSE ); // Don't free ustr cause it's set in zstr without copy. // efree( ustr ); return zstr; } /* }}} */ /* {{{ collator_convert_string_to_number * * Convert string to number. * * @param zval* str String to convert. * * @return zval* Number. If str is not numeric string return number zero. */ zval* collator_convert_string_to_number( zval* str ) { zval* num = collator_convert_string_to_number_if_possible( str ); if( num == str ) { // String wasn't converted => return zero. zval_ptr_dtor( &num ); ALLOC_INIT_ZVAL( num ); ZVAL_LONG( num, 0 ); } return num; } /* }}} */ /* {{{ collator_convert_string_to_double * * Convert string to double. * * @param zval* str String to convert. * * @return zval* Number. If str is not numeric string return number zero. */ zval* collator_convert_string_to_double( zval* str ) { zval* num = collator_convert_string_to_number( str ); if( Z_TYPE_P(num) == IS_LONG ) { ZVAL_DOUBLE( num, Z_LVAL_P( num ) ); } return num; } /* }}} */ /* {{{ collator_convert_string_to_number_if_possible * * Convert string to numer. * * @param zval* str String to convert. * * @return zval* Number if str is numeric string. Otherwise * original str param. */ zval* collator_convert_string_to_number_if_possible( zval* str ) { zval* num = NULL; int is_numeric = 0; long lval = 0; double dval = 0; if( Z_TYPE_P( str ) != IS_STRING ) { COLLATOR_CONVERT_RETURN_FAILED( str ); } if( ( is_numeric = collator_is_numeric( (UChar*) Z_STRVAL_P(str), UCHARS( Z_STRLEN_P(str) ), &lval, &dval, 1 ) ) ) { ALLOC_INIT_ZVAL( num ); if( is_numeric == IS_LONG ) Z_LVAL_P(num) = lval; if( is_numeric == IS_DOUBLE ) Z_DVAL_P(num) = dval; Z_TYPE_P(num) = is_numeric; } else { COLLATOR_CONVERT_RETURN_FAILED( str ); } return num; } /* }}} */ /* {{{ collator_make_printable_zval * * Returns string from input zval. * * @param zval* arg zval to get string from * * @return zval* UTF16 string. */ zval* collator_make_printable_zval( zval* arg ) { zval arg_copy; int use_copy = 0; zval* str = NULL; if( Z_TYPE_P(arg) != IS_STRING ) { zend_make_printable_zval(arg, &arg_copy, &use_copy); if( use_copy ) { str = collator_convert_zstr_utf8_to_utf16( &arg_copy ); zval_dtor( &arg_copy ); } else { str = collator_convert_zstr_utf8_to_utf16( arg ); } } else { COLLATOR_CONVERT_RETURN_FAILED( arg ); } return str; } /* }}} */ /* {{{ collator_normalize_sort_argument * * Normalize argument to use in sort's compare function. * * @param zval* arg Sort's argument to normalize. * * @return zval* Normalized copy of arg or unmodified arg * if normalization is not needed. */ zval* collator_normalize_sort_argument( zval* arg ) { zval* n_arg = NULL; if( Z_TYPE_P( arg ) != IS_STRING ) { // If its not a string then nothing to do. // Return original arg. COLLATOR_CONVERT_RETURN_FAILED( arg ); } // Try convert to number. n_arg = collator_convert_string_to_number_if_possible( arg ); if( n_arg == arg ) { // Conversion to number failed. zval_ptr_dtor( &n_arg ); // Convert string to utf8. n_arg = collator_convert_zstr_utf16_to_utf8( arg ); } return n_arg; } /* }}} */ /* * Local variables: * tab-width: 4 * c-basic-offset: 4 * End: * vim600: noet sw=4 ts=4 fdm=marker * vim<600: noet sw=4 ts=4 */