diff options
Diffstat (limited to 'ext/intl/collator')
-rw-r--r-- | ext/intl/collator/collator.c | 96 | ||||
-rw-r--r-- | ext/intl/collator/collator.h | 29 | ||||
-rw-r--r-- | ext/intl/collator/collator_attr.c | 157 | ||||
-rw-r--r-- | ext/intl/collator/collator_attr.h | 28 | ||||
-rw-r--r-- | ext/intl/collator/collator_class.c | 202 | ||||
-rw-r--r-- | ext/intl/collator/collator_class.h | 69 | ||||
-rw-r--r-- | ext/intl/collator/collator_compare.c | 133 | ||||
-rw-r--r-- | ext/intl/collator/collator_compare.h | 25 | ||||
-rw-r--r-- | ext/intl/collator/collator_convert.c | 485 | ||||
-rw-r--r-- | ext/intl/collator/collator_convert.h | 38 | ||||
-rw-r--r-- | ext/intl/collator/collator_create.c | 87 | ||||
-rw-r--r-- | ext/intl/collator/collator_create.h | 27 | ||||
-rw-r--r-- | ext/intl/collator/collator_error.c | 94 | ||||
-rw-r--r-- | ext/intl/collator/collator_error.h | 26 | ||||
-rw-r--r-- | ext/intl/collator/collator_is_numeric.c | 305 | ||||
-rw-r--r-- | ext/intl/collator/collator_is_numeric.h | 26 | ||||
-rw-r--r-- | ext/intl/collator/collator_locale.c | 80 | ||||
-rw-r--r-- | ext/intl/collator/collator_locale.h | 25 | ||||
-rw-r--r-- | ext/intl/collator/collator_sort.c | 621 | ||||
-rw-r--r-- | ext/intl/collator/collator_sort.h | 30 |
20 files changed, 2583 insertions, 0 deletions
diff --git a/ext/intl/collator/collator.c b/ext/intl/collator/collator.c new file mode 100644 index 0000000..047a738 --- /dev/null +++ b/ext/intl/collator/collator.c @@ -0,0 +1,96 @@ +/* + +----------------------------------------------------------------------+ + | 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 <vsavchuk@productengine.com> | + | Dmitry Lakhtyuk <dlakhtyuk@productengine.com> | + +----------------------------------------------------------------------+ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "collator_class.h" +#include "collator.h" + +#include <unicode/utypes.h> +#include <unicode/ucol.h> +#include <unicode/ustring.h> + +/* {{{ collator_register_constants + * Register constants common for the both (OO and procedural) + * APIs. + */ +void collator_register_constants( INIT_FUNC_ARGS ) +{ + if( !Collator_ce_ptr ) + { + zend_error( E_ERROR, "Collator class not defined" ); + return; + } + + #define COLLATOR_EXPOSE_CONST(x) REGISTER_LONG_CONSTANT(#x, x, CONST_CS) + #define COLLATOR_EXPOSE_CLASS_CONST(x) zend_declare_class_constant_long( Collator_ce_ptr, ZEND_STRS( #x ) - 1, UCOL_##x TSRMLS_CC ); + #define COLLATOR_EXPOSE_CUSTOM_CLASS_CONST(name, value) zend_declare_class_constant_long( Collator_ce_ptr, ZEND_STRS( name ) - 1, value TSRMLS_CC ); + + /* UColAttributeValue constants */ + COLLATOR_EXPOSE_CUSTOM_CLASS_CONST( "DEFAULT_VALUE", UCOL_DEFAULT ); + + COLLATOR_EXPOSE_CLASS_CONST( PRIMARY ); + COLLATOR_EXPOSE_CLASS_CONST( SECONDARY ); + COLLATOR_EXPOSE_CLASS_CONST( TERTIARY ); + COLLATOR_EXPOSE_CLASS_CONST( DEFAULT_STRENGTH ); + COLLATOR_EXPOSE_CLASS_CONST( QUATERNARY ); + COLLATOR_EXPOSE_CLASS_CONST( IDENTICAL ); + + COLLATOR_EXPOSE_CLASS_CONST( OFF ); + COLLATOR_EXPOSE_CLASS_CONST( ON ); + + COLLATOR_EXPOSE_CLASS_CONST( SHIFTED ); + COLLATOR_EXPOSE_CLASS_CONST( NON_IGNORABLE ); + + COLLATOR_EXPOSE_CLASS_CONST( LOWER_FIRST ); + COLLATOR_EXPOSE_CLASS_CONST( UPPER_FIRST ); + + /* UColAttribute constants */ + COLLATOR_EXPOSE_CLASS_CONST( FRENCH_COLLATION ); + COLLATOR_EXPOSE_CLASS_CONST( ALTERNATE_HANDLING ); + COLLATOR_EXPOSE_CLASS_CONST( CASE_FIRST ); + COLLATOR_EXPOSE_CLASS_CONST( CASE_LEVEL ); + COLLATOR_EXPOSE_CLASS_CONST( NORMALIZATION_MODE ); + COLLATOR_EXPOSE_CLASS_CONST( STRENGTH ); + COLLATOR_EXPOSE_CLASS_CONST( HIRAGANA_QUATERNARY_MODE ); + COLLATOR_EXPOSE_CLASS_CONST( NUMERIC_COLLATION ); + + /* ULocDataLocaleType constants */ + COLLATOR_EXPOSE_CONST( ULOC_ACTUAL_LOCALE ); + COLLATOR_EXPOSE_CONST( ULOC_VALID_LOCALE ); + + /* sort flags */ + COLLATOR_EXPOSE_CUSTOM_CLASS_CONST( "SORT_REGULAR", COLLATOR_SORT_REGULAR ); + COLLATOR_EXPOSE_CUSTOM_CLASS_CONST( "SORT_STRING", COLLATOR_SORT_STRING ); + COLLATOR_EXPOSE_CUSTOM_CLASS_CONST( "SORT_NUMERIC", COLLATOR_SORT_NUMERIC ); + + #undef COLLATOR_EXPOSE_CUSTOM_CLASS_CONST + #undef COLLATOR_EXPOSE_CLASS_CONST + #undef COLLATOR_EXPOSE_CONST +} +/* }}} */ + +/* + * 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 + */ diff --git a/ext/intl/collator/collator.h b/ext/intl/collator/collator.h new file mode 100644 index 0000000..96e7aa0 --- /dev/null +++ b/ext/intl/collator/collator.h @@ -0,0 +1,29 @@ +/* + +----------------------------------------------------------------------+ + | 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 <vsavchuk@productengine.com> | + | Dmitry Lakhtyuk <dlakhtyuk@productengine.com> | + +----------------------------------------------------------------------+ + */ + +#ifndef COLLATOR_COLLATOR_H +#define CCOLLATOR_COLLATOR_H + +#include <php.h> + +#define COLLATOR_SORT_REGULAR 0 +#define COLLATOR_SORT_STRING 1 +#define COLLATOR_SORT_NUMERIC 2 + +void collator_register_constants( INIT_FUNC_ARGS ); + +#endif // COLLATOR_COLLATOR_H diff --git a/ext/intl/collator/collator_attr.c b/ext/intl/collator/collator_attr.c new file mode 100644 index 0000000..684e72c --- /dev/null +++ b/ext/intl/collator/collator_attr.c @@ -0,0 +1,157 @@ +/* + +----------------------------------------------------------------------+ + | 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 <vsavchuk@productengine.com> | + | Dmitry Lakhtyuk <dlakhtyuk@productengine.com> | + +----------------------------------------------------------------------+ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "php_intl.h" +#include "collator_class.h" +#include "collator_convert.h" +#include "collator_attr.h" + +#include <unicode/ustring.h> + +/* {{{ proto int Collator::getAttribute( int $attr ) + * Get collation attribute value. }}} */ +/* {{{ proto int collator_get_attribute( Collator $coll, int $attr ) + * Get collation attribute value. + */ +PHP_FUNCTION( collator_get_attribute ) +{ + long attribute, value; + + COLLATOR_METHOD_INIT_VARS + + /* Parse parameters. */ + if( zend_parse_method_parameters( ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Ol", + &object, Collator_ce_ptr, &attribute ) == FAILURE ) + { + intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR, + "collator_get_attribute: unable to parse input params", 0 TSRMLS_CC ); + + RETURN_FALSE; + } + + /* Fetch the object. */ + COLLATOR_METHOD_FETCH_OBJECT; + + value = ucol_getAttribute( co->ucoll, attribute, COLLATOR_ERROR_CODE_P( co ) ); + COLLATOR_CHECK_STATUS( co, "Error getting attribute value" ); + + RETURN_LONG( value ); +} +/* }}} */ + +/* {{{ proto bool Collator::getAttribute( int $attr ) + * Get collation attribute value. }}} */ +/* {{{ proto bool collator_set_attribute( Collator $coll, int $attr, int $val ) + * Set collation attribute. + */ +PHP_FUNCTION( collator_set_attribute ) +{ + long attribute, value; + COLLATOR_METHOD_INIT_VARS + + + /* Parse parameters. */ + if( zend_parse_method_parameters( ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oll", + &object, Collator_ce_ptr, &attribute, &value ) == FAILURE) + { + intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR, + "collator_set_attribute: unable to parse input params", 0 TSRMLS_CC ); + + RETURN_FALSE; + } + + /* Fetch the object. */ + COLLATOR_METHOD_FETCH_OBJECT; + + /* Set new value for the given attribute. */ + ucol_setAttribute( co->ucoll, attribute, value, COLLATOR_ERROR_CODE_P( co ) ); + COLLATOR_CHECK_STATUS( co, "Error setting attribute value" ); + + RETURN_TRUE; +} +/* }}} */ + +/* {{{ proto int Collator::getStrength() + * Returns the current collation strength. }}} */ +/* {{{ proto int collator_get_strength(Collator coll) + * Returns the current collation strength. + */ +PHP_FUNCTION( collator_get_strength ) +{ + COLLATOR_METHOD_INIT_VARS + + /* Parse parameters. */ + if( zend_parse_method_parameters( ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", + &object, Collator_ce_ptr ) == FAILURE ) + { + intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR, + "collator_get_strength: unable to parse input params", 0 TSRMLS_CC ); + + RETURN_FALSE; + } + + /* Fetch the object. */ + COLLATOR_METHOD_FETCH_OBJECT; + + /* Get current strength and return it. */ + RETURN_LONG( ucol_getStrength( co->ucoll ) ); +} +/* }}} */ + +/* {{{ proto bool Collator::setStrength(int strength) + * Set the collation strength. }}} */ +/* {{{ proto bool collator_set_strength(Collator coll, int strength) + * Set the collation strength. + */ +PHP_FUNCTION( collator_set_strength ) +{ + long strength; + + COLLATOR_METHOD_INIT_VARS + + /* Parse parameters. */ + if( zend_parse_method_parameters( ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Ol", + &object, Collator_ce_ptr, &strength ) == FAILURE ) + { + intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR, + "collator_set_strength: unable to parse input params", 0 TSRMLS_CC ); + + RETURN_FALSE; + } + + /* Fetch the object. */ + COLLATOR_METHOD_FETCH_OBJECT; + + /* Set given strength. */ + ucol_setStrength( co->ucoll, strength ); + + RETURN_TRUE; +} +/* }}} */ + +/* + * 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 + */ diff --git a/ext/intl/collator/collator_attr.h b/ext/intl/collator/collator_attr.h new file mode 100644 index 0000000..85636cc --- /dev/null +++ b/ext/intl/collator/collator_attr.h @@ -0,0 +1,28 @@ +/* + +----------------------------------------------------------------------+ + | 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 <vsavchuk@productengine.com> | + | Dmitry Lakhtyuk <dlakhtyuk@productengine.com> | + +----------------------------------------------------------------------+ + */ + +#ifndef COLLATOR_ATTR_H +#define CCOLLATOR_ATTR_H + +#include <php.h> + +PHP_FUNCTION( collator_get_attribute ); +PHP_FUNCTION( collator_set_attribute ); +PHP_FUNCTION( collator_get_strength ); +PHP_FUNCTION( collator_set_strength ); + +#endif // COLLATOR_ATTR_H diff --git a/ext/intl/collator/collator_class.c b/ext/intl/collator/collator_class.c new file mode 100644 index 0000000..d1fa10e --- /dev/null +++ b/ext/intl/collator/collator_class.c @@ -0,0 +1,202 @@ +/* + +----------------------------------------------------------------------+ + | 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 <vsavchuk@productengine.com> | + | Dmitry Lakhtyuk <dlakhtyuk@productengine.com> | + +----------------------------------------------------------------------+ + */ + +#include "collator_class.h" +#include "php_intl.h" +#include "collator_attr.h" +#include "collator_compare.h" +#include "collator_sort.h" +#include "collator_convert.h" +#include "collator_locale.h" +#include "collator_create.h" +#include "collator_error.h" +#include "intl_error.h" + +#include <unicode/ucol.h> + +zend_class_entry *Collator_ce_ptr = NULL; +static zend_object_handlers Collator_handlers; + +/* + * Auxiliary functions needed by objects of 'Collator' class + */ + +/* {{{ Collator_objects_dtor */ +static void Collator_objects_dtor( + void *object, + zend_object_handle handle TSRMLS_DC ) +{ + zend_objects_destroy_object( object, handle TSRMLS_CC ); +} +/* }}} */ + +/* {{{ Collator_objects_free */ +void Collator_objects_free( zend_object *object TSRMLS_DC ) +{ + Collator_object* co = (Collator_object*)object; + + zend_object_std_dtor( &co->zo TSRMLS_CC ); + + collator_object_destroy( co TSRMLS_CC ); + + efree( co ); +} +/* }}} */ + +/* {{{ Collator_object_create */ +zend_object_value Collator_object_create( + zend_class_entry *ce TSRMLS_DC ) +{ + zend_object_value retval; + Collator_object* intern; + + intern = ecalloc( 1, sizeof(Collator_object) ); + intl_error_init( COLLATOR_ERROR_P( intern ) TSRMLS_CC ); + zend_object_std_init( &intern->zo, ce TSRMLS_CC ); + object_properties_init(&intern->zo, ce); + + retval.handle = zend_objects_store_put( + intern, + Collator_objects_dtor, + (zend_objects_free_object_storage_t)Collator_objects_free, + NULL TSRMLS_CC ); + + retval.handlers = &Collator_handlers; + + return retval; +} +/* }}} */ + +/* + * 'Collator' class registration structures & functions + */ + +/* {{{ Collator methods arguments info */ +/* NOTE: modifying 'collator_XX_args' do not forget to + modify approptiate 'collator_XX_args' for + the procedural API. +*/ +ZEND_BEGIN_ARG_INFO_EX( collator_0_args, 0, 0, 0 ) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX( collator_1_arg, 0, 0, 1 ) + ZEND_ARG_INFO( 0, arg1 ) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX( collator_2_args, 0, 0, 2 ) + ZEND_ARG_INFO( 0, arg1 ) + ZEND_ARG_INFO( 0, arg2 ) +ZEND_END_ARG_INFO() + +ZEND_BEGIN_ARG_INFO_EX( collator_sort_args, 0, 0, 1 ) + ZEND_ARG_ARRAY_INFO( 1, arr, 0 ) + ZEND_ARG_INFO( 0, flags ) +ZEND_END_ARG_INFO() + +/* }}} */ + +/* {{{ Collator_class_functions + * Every 'Collator' class method has an entry in this table + */ + +zend_function_entry Collator_class_functions[] = { + PHP_ME( Collator, __construct, collator_1_arg, ZEND_ACC_PUBLIC|ZEND_ACC_CTOR ) + ZEND_FENTRY( create, ZEND_FN( collator_create ), collator_1_arg, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC ) + PHP_NAMED_FE( compare, ZEND_FN( collator_compare ), collator_2_args ) + PHP_NAMED_FE( sort, ZEND_FN( collator_sort ), collator_sort_args ) + PHP_NAMED_FE( sortWithSortKeys, ZEND_FN( collator_sort_with_sort_keys ), collator_sort_args ) + PHP_NAMED_FE( asort, ZEND_FN( collator_asort ), collator_sort_args ) + PHP_NAMED_FE( getAttribute, ZEND_FN( collator_get_attribute ), collator_1_arg ) + PHP_NAMED_FE( setAttribute, ZEND_FN( collator_set_attribute ), collator_2_args ) + PHP_NAMED_FE( getStrength, ZEND_FN( collator_get_strength ), collator_0_args ) + PHP_NAMED_FE( setStrength, ZEND_FN( collator_set_strength ), collator_1_arg ) + PHP_NAMED_FE( getLocale, ZEND_FN( collator_get_locale ), collator_1_arg ) + PHP_NAMED_FE( getErrorCode, ZEND_FN( collator_get_error_code ), collator_0_args ) + PHP_NAMED_FE( getErrorMessage, ZEND_FN( collator_get_error_message ), collator_0_args ) + PHP_NAMED_FE( getSortKey, ZEND_FN( collator_get_sort_key ), collator_2_args ) + PHP_FE_END +}; +/* }}} */ + +/* {{{ collator_register_Collator_class + * Initialize 'Collator' class + */ +void collator_register_Collator_class( TSRMLS_D ) +{ + zend_class_entry ce; + + /* Create and register 'Collator' class. */ + INIT_CLASS_ENTRY( ce, "Collator", Collator_class_functions ); + ce.create_object = Collator_object_create; + Collator_ce_ptr = zend_register_internal_class( &ce TSRMLS_CC ); + + memcpy(&Collator_handlers, zend_get_std_object_handlers(), + sizeof Collator_handlers); + /* Collator has no usable clone semantics - ucol_cloneBinary/ucol_openBinary require binary buffer + for which we don't have the place to keep */ + Collator_handlers.clone_obj = NULL; + + /* Declare 'Collator' class properties. */ + if( !Collator_ce_ptr ) + { + zend_error( E_ERROR, + "Collator: attempt to create properties " + "on a non-registered class." ); + return; + } +} +/* }}} */ + +/* {{{ void collator_object_init( Collator_object* co ) + * Initialize internals of Collator_object. + * Must be called before any other call to 'collator_object_...' functions. + */ +void collator_object_init( Collator_object* co TSRMLS_DC ) +{ + if( !co ) + return; + + intl_error_init( COLLATOR_ERROR_P( co ) TSRMLS_CC ); +} +/* }}} */ + +/* {{{ void collator_object_destroy( Collator_object* co ) + * Clean up mem allocted by internals of Collator_object + */ +void collator_object_destroy( Collator_object* co TSRMLS_DC ) +{ + if( !co ) + return; + + if( co->ucoll ) + { + ucol_close( co->ucoll ); + co->ucoll = NULL; + } + + intl_error_reset( COLLATOR_ERROR_P( co ) TSRMLS_CC ); +} +/* }}} */ + +/* + * 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 + */ diff --git a/ext/intl/collator/collator_class.h b/ext/intl/collator/collator_class.h new file mode 100644 index 0000000..7a56dfc --- /dev/null +++ b/ext/intl/collator/collator_class.h @@ -0,0 +1,69 @@ +/* + +----------------------------------------------------------------------+ + | 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 <vsavchuk@productengine.com> | + | Dmitry Lakhtyuk <dlakhtyuk@productengine.com> | + +----------------------------------------------------------------------+ + */ + +#ifndef COLLATOR_CLASS_H +#define COLLATOR_CLASS_H + +#include <php.h> + +#include "../intl_common.h" +#include "../intl_error.h" +#include "../intl_data.h" + +#include <unicode/ucol.h> + +typedef struct { + zend_object zo; + + // error handling + intl_error err; + + // ICU collator + UCollator* ucoll; +} Collator_object; + +#define COLLATOR_ERROR(co) (co)->err +#define COLLATOR_ERROR_P(co) &(COLLATOR_ERROR(co)) + +#define COLLATOR_ERROR_CODE(co) INTL_ERROR_CODE(COLLATOR_ERROR(co)) +#define COLLATOR_ERROR_CODE_P(co) &(INTL_ERROR_CODE(COLLATOR_ERROR(co))) + +void collator_register_Collator_class( TSRMLS_D ); +void collator_object_init( Collator_object* co TSRMLS_DC ); +void collator_object_destroy( Collator_object* co TSRMLS_DC ); + +extern zend_class_entry *Collator_ce_ptr; + +/* Auxiliary macros */ + +#define COLLATOR_METHOD_INIT_VARS \ + zval* object = NULL; \ + Collator_object* co = NULL; \ + intl_error_reset( NULL TSRMLS_CC ); \ + +#define COLLATOR_METHOD_FETCH_OBJECT INTL_METHOD_FETCH_OBJECT(Collator, co) + +// Macro to check return value of a ucol_* function call. +#define COLLATOR_CHECK_STATUS( co, msg ) \ + intl_error_set_code( NULL, COLLATOR_ERROR_CODE( co ) TSRMLS_CC ); \ + if( U_FAILURE( COLLATOR_ERROR_CODE( co ) ) ) \ + { \ + intl_errors_set_custom_msg( COLLATOR_ERROR_P( co ), msg, 0 TSRMLS_CC ); \ + RETURN_FALSE; \ + } \ + +#endif // #ifndef COLLATOR_CLASS_H diff --git a/ext/intl/collator/collator_compare.c b/ext/intl/collator/collator_compare.c new file mode 100644 index 0000000..4384558 --- /dev/null +++ b/ext/intl/collator/collator_compare.c @@ -0,0 +1,133 @@ +/* + +----------------------------------------------------------------------+ + | 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 <vsavchuk@productengine.com> | + | Dmitry Lakhtyuk <dlakhtyuk@productengine.com> | + +----------------------------------------------------------------------+ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "php_intl.h" +#include "collator_class.h" +#include "collator_compare.h" +#include "intl_convert.h" + +/* {{{ proto int Collator::compare( string $str1, string $str2 ) + * Compare two strings. }}} */ +/* {{{ proto int collator_compare( Collator $coll, string $str1, string $str2 ) + * Compare two strings. + */ +PHP_FUNCTION( collator_compare ) +{ + char* str1 = NULL; + char* str2 = NULL; + int str1_len = 0; + int str2_len = 0; + + UChar* ustr1 = NULL; + UChar* ustr2 = NULL; + int ustr1_len = 0; + int ustr2_len = 0; + + UCollationResult result; + + COLLATOR_METHOD_INIT_VARS + + /* Parse parameters. */ + if( zend_parse_method_parameters( ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oss", + &object, Collator_ce_ptr, &str1, &str1_len, &str2, &str2_len ) == FAILURE ) + { + intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR, + "collator_compare: unable to parse input params", 0 TSRMLS_CC ); + + RETURN_FALSE; + } + + /* Fetch the object. */ + COLLATOR_METHOD_FETCH_OBJECT; + + if (!co || !co->ucoll) { + intl_error_set_code( NULL, COLLATOR_ERROR_CODE( co ) TSRMLS_CC ); + intl_errors_set_custom_msg( COLLATOR_ERROR_P( co ), + "Object not initialized", 0 TSRMLS_CC ); + php_error_docref(NULL TSRMLS_CC, E_RECOVERABLE_ERROR, "Object not initialized"); + + RETURN_FALSE; + } + + /* + * Compare given strings (converting them to UTF-16 first). + */ + + /* First convert the strings to UTF-16. */ + intl_convert_utf8_to_utf16( + &ustr1, &ustr1_len, str1, str1_len, COLLATOR_ERROR_CODE_P( co ) ); + if( U_FAILURE( COLLATOR_ERROR_CODE( co ) ) ) + { + /* Set global error code. */ + intl_error_set_code( NULL, COLLATOR_ERROR_CODE( co ) TSRMLS_CC ); + + /* Set error messages. */ + intl_errors_set_custom_msg( COLLATOR_ERROR_P( co ), + "Error converting first argument to UTF-16", 0 TSRMLS_CC ); + if (ustr1) { + efree( ustr1 ); + } + RETURN_FALSE; + } + + intl_convert_utf8_to_utf16( + &ustr2, &ustr2_len, str2, str2_len, COLLATOR_ERROR_CODE_P( co ) ); + if( U_FAILURE( COLLATOR_ERROR_CODE( co ) ) ) + { + /* Set global error code. */ + intl_error_set_code( NULL, COLLATOR_ERROR_CODE( co ) TSRMLS_CC ); + + /* Set error messages. */ + intl_errors_set_custom_msg( COLLATOR_ERROR_P( co ), + "Error converting second argument to UTF-16", 0 TSRMLS_CC ); + if (ustr1) { + efree( ustr1 ); + } + if (ustr2) { + efree( ustr2 ); + } + RETURN_FALSE; + } + + /* Then compare them. */ + result = ucol_strcoll( + co->ucoll, + ustr1, ustr1_len, + ustr2, ustr2_len ); + + if( ustr1 ) + efree( ustr1 ); + if( ustr2 ) + efree( ustr2 ); + + /* Return result of the comparison. */ + RETURN_LONG( result ); +} +/* }}} */ + +/* + * 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 + */ diff --git a/ext/intl/collator/collator_compare.h b/ext/intl/collator/collator_compare.h new file mode 100644 index 0000000..4e38b79 --- /dev/null +++ b/ext/intl/collator/collator_compare.h @@ -0,0 +1,25 @@ +/* + +----------------------------------------------------------------------+ + | 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 <vsavchuk@productengine.com> | + | Dmitry Lakhtyuk <dlakhtyuk@productengine.com> | + +----------------------------------------------------------------------+ + */ + +#ifndef COLLATOR_COMPARE_H +#define COLLATOR_COMPARE_H + +#include <php.h> + +PHP_FUNCTION( collator_compare ); + +#endif // COLLATOR_COMPARE_H diff --git a/ext/intl/collator/collator_convert.c b/ext/intl/collator/collator_convert.c new file mode 100644 index 0000000..e989d4c --- /dev/null +++ b/ext/intl/collator/collator_convert.c @@ -0,0 +1,485 @@ +/* + +----------------------------------------------------------------------+ + | 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 <vsavchuk@productengine.com> | + | Dmitry Lakhtyuk <dlakhtyuk@productengine.com> | + +----------------------------------------------------------------------+ + */ + +#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 <unicode/ustring.h> +#include <php.h> + +#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 + */ diff --git a/ext/intl/collator/collator_convert.h b/ext/intl/collator/collator_convert.h new file mode 100644 index 0000000..8322ea9 --- /dev/null +++ b/ext/intl/collator/collator_convert.h @@ -0,0 +1,38 @@ +/* + +----------------------------------------------------------------------+ + | 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 <vsavchuk@productengine.com> | + | Dmitry Lakhtyuk <dlakhtyuk@productengine.com> | + +----------------------------------------------------------------------+ + */ + +#ifndef COLLATOR_CONVERT_H +#define COLLATOR_CONVERT_H + +#include <php.h> +#include <unicode/utypes.h> + +void collator_convert_hash_from_utf8_to_utf16( HashTable* hash, UErrorCode* status ); +void collator_convert_hash_from_utf16_to_utf8( HashTable* hash, UErrorCode* status ); + +zval* collator_convert_zstr_utf16_to_utf8( zval* utf16_zval ); +zval* collator_convert_zstr_utf8_to_utf16( zval* utf8_zval ); + +zval* collator_normalize_sort_argument( zval* arg ); +zval* collator_convert_object_to_string( zval* obj TSRMLS_DC ); +zval* collator_convert_string_to_number( zval* arg ); +zval* collator_convert_string_to_number_if_possible( zval* str ); +zval* collator_convert_string_to_double( zval* str ); + +zval* collator_make_printable_zval( zval* arg ); + +#endif // COLLATOR_CONVERT_H diff --git a/ext/intl/collator/collator_create.c b/ext/intl/collator/collator_create.c new file mode 100644 index 0000000..b2a9968 --- /dev/null +++ b/ext/intl/collator/collator_create.c @@ -0,0 +1,87 @@ +/* + +----------------------------------------------------------------------+ + | 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 <vsavchuk@productengine.com> | + | Dmitry Lakhtyuk <dlakhtyuk@productengine.com> | + +----------------------------------------------------------------------+ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "php_intl.h" +#include "collator_class.h" +#include "collator_create.h" +#include "intl_data.h" + +/* {{{ */ +static void collator_ctor(INTERNAL_FUNCTION_PARAMETERS) +{ + char* locale; + int locale_len = 0; + zval* object; + Collator_object* co; + + intl_error_reset( NULL TSRMLS_CC ); + object = return_value; + /* Parse parameters. */ + if( zend_parse_parameters( ZEND_NUM_ARGS() TSRMLS_CC, "s", + &locale, &locale_len ) == FAILURE ) + { + intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR, + "collator_create: unable to parse input params", 0 TSRMLS_CC ); + zval_dtor(return_value); + RETURN_NULL(); + } + + INTL_CHECK_LOCALE_LEN_OBJ(locale_len, return_value); + COLLATOR_METHOD_FETCH_OBJECT; + + if(locale_len == 0) { + locale = INTL_G(default_locale); + } + + /* Open ICU collator. */ + co->ucoll = ucol_open( locale, COLLATOR_ERROR_CODE_P( co ) ); + INTL_CTOR_CHECK_STATUS(co, "collator_create: unable to open ICU collator"); +} +/* }}} */ + +/* {{{ proto Collator collator_create( string $locale ) + * Create collator. + */ +PHP_FUNCTION( collator_create ) +{ + object_init_ex( return_value, Collator_ce_ptr ); + collator_ctor(INTERNAL_FUNCTION_PARAM_PASSTHRU); +} +/* }}} */ + +/* {{{ proto Collator Collator::__construct( string $locale ) + * Collator object constructor. + */ +PHP_METHOD( Collator, __construct ) +{ + return_value = getThis(); + collator_ctor(INTERNAL_FUNCTION_PARAM_PASSTHRU); +} +/* }}} */ + +/* + * 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 + */ diff --git a/ext/intl/collator/collator_create.h b/ext/intl/collator/collator_create.h new file mode 100644 index 0000000..b740e82 --- /dev/null +++ b/ext/intl/collator/collator_create.h @@ -0,0 +1,27 @@ +/* + +----------------------------------------------------------------------+ + | 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 <vsavchuk@productengine.com> | + | Dmitry Lakhtyuk <dlakhtyuk@productengine.com> | + +----------------------------------------------------------------------+ + */ + +#ifndef COLLATOR_CREATE_H +#define COLLATOR_CREATE_H + +#include <php.h> + +PHP_FUNCTION( collator_create ); + +PHP_METHOD( Collator, __construct ); + +#endif // COLLATOR_CREATE_H diff --git a/ext/intl/collator/collator_error.c b/ext/intl/collator/collator_error.c new file mode 100644 index 0000000..c4e4125 --- /dev/null +++ b/ext/intl/collator/collator_error.c @@ -0,0 +1,94 @@ +/* + +----------------------------------------------------------------------+ + | 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 <vsavchuk@productengine.com> | + | Dmitry Lakhtyuk <dlakhtyuk@productengine.com> | + +----------------------------------------------------------------------+ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "php_intl.h" +#include "collator_class.h" +#include "collator_error.h" + +/* {{{ proto int Collator::getErrorCode( Collator $coll ) + * Get collator's last error code. }}} */ +/* {{{ proto int collator_get_error_code( Collator $coll ) + * Get collator's last error code. + */ +PHP_FUNCTION( collator_get_error_code ) +{ + COLLATOR_METHOD_INIT_VARS + + /* Parse parameters. */ + if( zend_parse_method_parameters( ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", + &object, Collator_ce_ptr ) == FAILURE ) + { + intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR, + "collator_get_error_code: unable to parse input params", 0 TSRMLS_CC ); + + RETURN_FALSE; + } + + /* Fetch the object (without resetting its last error code). */ + co = (Collator_object *) zend_object_store_get_object(object TSRMLS_CC); + if( co == NULL ) + RETURN_FALSE; + + /* Return collator's last error code. */ + RETURN_LONG( COLLATOR_ERROR_CODE( co ) ); +} +/* }}} */ + +/* {{{ proto string Collator::getErrorMessage( Collator $coll ) + * Get text description for collator's last error code. }}} */ +/* {{{ proto string collator_get_error_message( Collator $coll ) + * Get text description for collator's last error code. + */ +PHP_FUNCTION( collator_get_error_message ) +{ + const char* message = NULL; + + COLLATOR_METHOD_INIT_VARS + + /* Parse parameters. */ + if( zend_parse_method_parameters( ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "O", + &object, Collator_ce_ptr ) == FAILURE ) + { + intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR, + "collator_get_error_message: unable to parse input params", 0 TSRMLS_CC ); + + RETURN_FALSE; + } + + /* Fetch the object (without resetting its last error code). */ + co = (Collator_object *) zend_object_store_get_object( object TSRMLS_CC ); + if( co == NULL ) + RETURN_FALSE; + + /* Return last error message. */ + message = intl_error_get_message( COLLATOR_ERROR_P( co ) TSRMLS_CC ); + RETURN_STRING( (char*)message, FALSE ); +} +/* }}} */ + +/* + * 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 + */ diff --git a/ext/intl/collator/collator_error.h b/ext/intl/collator/collator_error.h new file mode 100644 index 0000000..b2f44ea --- /dev/null +++ b/ext/intl/collator/collator_error.h @@ -0,0 +1,26 @@ +/* + +----------------------------------------------------------------------+ + | 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 <vsavchuk@productengine.com> | + | Dmitry Lakhtyuk <dlakhtyuk@productengine.com> | + +----------------------------------------------------------------------+ + */ + +#ifndef COLLATOR_ERROR_H +#define COLLATOR_ERROR_H + +#include <php.h> + +PHP_FUNCTION( collator_get_error_code ); +PHP_FUNCTION( collator_get_error_message ); + +#endif // COLLATOR_ERROR_H diff --git a/ext/intl/collator/collator_is_numeric.c b/ext/intl/collator/collator_is_numeric.c new file mode 100644 index 0000000..a8abfac --- /dev/null +++ b/ext/intl/collator/collator_is_numeric.c @@ -0,0 +1,305 @@ +/* + +----------------------------------------------------------------------+ + | 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 <vsavchuk@productengine.com> | + | Dmitry Lakhtyuk <dlakhtyuk@productengine.com> | + +----------------------------------------------------------------------+ + */ + +#include "collator_is_numeric.h" + +#if ZEND_MODULE_API_NO < 20071006 +/* not 5.3 */ +#ifndef ALLOCA_FLAG +#define ALLOCA_FLAG(use_heap) +#endif +#define _do_alloca(x, y) do_alloca((x)) +#define _free_alloca(x, y) free_alloca((x)) +#else +#define _do_alloca do_alloca +#define _free_alloca free_alloca +#endif +/* {{{ collator_u_strtod + * Taken from PHP6:zend_u_strtod() + */ +static double collator_u_strtod(const UChar *nptr, UChar **endptr) /* {{{ */ +{ + const UChar *u = nptr, *nstart; + UChar c = *u; + int any = 0; + ALLOCA_FLAG(use_heap); + + while (u_isspace(c)) { + c = *++u; + } + nstart = u; + + if (c == 0x2D /*'-'*/ || c == 0x2B /*'+'*/) { + c = *++u; + } + + while (c >= 0x30 /*'0'*/ && c <= 0x39 /*'9'*/) { + any = 1; + c = *++u; + } + + if (c == 0x2E /*'.'*/) { + c = *++u; + while (c >= 0x30 /*'0'*/ && c <= 0x39 /*'9'*/) { + any = 1; + c = *++u; + } + } + + if ((c == 0x65 /*'e'*/ || c == 0x45 /*'E'*/) && any) { + const UChar *e = u; + int any_exp = 0; + + c = *++u; + if (c == 0x2D /*'-'*/ || c == 0x2B /*'+'*/) { + c = *++u; + } + + while (c >= 0x30 /*'0'*/ && c <= 0x39 /*'9'*/) { + any_exp = 1; + c = *++u; + } + + if (!any_exp) { + u = e; + } + } + + if (any) { + char buf[64], *numbuf, *bufpos; + int length = u - nstart; + double value; + + if (length < sizeof(buf)) { + numbuf = buf; + } else { + numbuf = (char *) _do_alloca(length + 1, use_heap); + } + + bufpos = numbuf; + + while (nstart < u) { + *bufpos++ = (char) *nstart++; + } + + *bufpos = '\0'; + value = zend_strtod(numbuf, NULL); + + if (numbuf != buf) { + _free_alloca(numbuf, use_heap); + } + + if (endptr != NULL) { + *endptr = (UChar *)u; + } + + return value; + } + + if (endptr != NULL) { + *endptr = (UChar *)nptr; + } + + return 0; +} +/* }}} */ + +/* {{{ collator_u_strtol + * Taken from PHP6:zend_u_strtol() + * + * Convert a Unicode string to a long integer. + * + * Ignores `locale' stuff. + */ +static long collator_u_strtol(nptr, endptr, base) + const UChar *nptr; + UChar **endptr; + register int base; +{ + register const UChar *s = nptr; + register unsigned long acc; + register UChar c; + register unsigned long cutoff; + register int neg = 0, any, cutlim; + + if (s == NULL) { + errno = ERANGE; + if (endptr != NULL) { + *endptr = NULL; + } + return 0; + } + + /* + * Skip white space and pick up leading +/- sign if any. + * If base is 0, allow 0x for hex and 0 for octal, else + * assume decimal; if base is already 16, allow 0x. + */ + do { + c = *s++; + } while (u_isspace(c)); + if (c == 0x2D /*'-'*/) { + neg = 1; + c = *s++; + } else if (c == 0x2B /*'+'*/) + c = *s++; + if ((base == 0 || base == 16) && + (c == 0x30 /*'0'*/) + && (*s == 0x78 /*'x'*/ || *s == 0x58 /*'X'*/)) { + c = s[1]; + s += 2; + base = 16; + } + if (base == 0) + base = (c == 0x30 /*'0'*/) ? 8 : 10; + + /* + * Compute the cutoff value between legal numbers and illegal + * numbers. That is the largest legal value, divided by the + * base. An input number that is greater than this value, if + * followed by a legal input character, is too big. One that + * is equal to this value may be valid or not; the limit + * between valid and invalid numbers is then based on the last + * digit. For instance, if the range for longs is + * [-2147483648..2147483647] and the input base is 10, + * cutoff will be set to 214748364 and cutlim to either + * 7 (neg==0) or 8 (neg==1), meaning that if we have accumulated + * a value > 214748364, or equal but the next digit is > 7 (or 8), + * the number is too big, and we will return a range error. + * + * Set any if any `digits' consumed; make it negative to indicate + * overflow. + */ + cutoff = neg ? -(unsigned long)LONG_MIN : LONG_MAX; + cutlim = cutoff % (unsigned long)base; + cutoff /= (unsigned long)base; + for (acc = 0, any = 0;; c = *s++) { + if (c >= 0x30 /*'0'*/ && c <= 0x39 /*'9'*/) + c -= 0x30 /*'0'*/; + else if (c >= 0x41 /*'A'*/ && c <= 0x5A /*'Z'*/) + c -= 0x41 /*'A'*/ - 10; + else if (c >= 0x61 /*'a'*/ && c <= 0x7A /*'z'*/) + c -= 0x61 /*'a'*/ - 10; + else + break; + if (c >= base) + break; + + if (any < 0 || acc > cutoff || (acc == cutoff && c > cutlim)) + any = -1; + else { + any = 1; + acc *= base; + acc += c; + } + } + if (any < 0) { + acc = neg ? LONG_MIN : LONG_MAX; + errno = ERANGE; + } else if (neg) + acc = -acc; + if (endptr != NULL) + *endptr = (UChar *)(any ? s - 1 : nptr); + return (acc); +} +/* }}} */ + + +/* {{{ collator_is_numeric] + * Taken from PHP6:is_numeric_unicode() + */ +zend_uchar collator_is_numeric( UChar *str, int length, long *lval, double *dval, int allow_errors ) +{ + long local_lval; + double local_dval; + UChar *end_ptr_long, *end_ptr_double; + int conv_base=10; + + if (!length) { + return 0; + } + + /* handle hex numbers */ + if (length>=2 && str[0]=='0' && (str[1]=='x' || str[1]=='X')) { + conv_base=16; + } + + errno=0; + local_lval = collator_u_strtol(str, &end_ptr_long, conv_base); + if (errno != ERANGE) { + if (end_ptr_long == str+length) { /* integer string */ + if (lval) { + *lval = local_lval; + } + return IS_LONG; + } else if (end_ptr_long == str && *end_ptr_long != '\0' && *str != '.' && *str != '-') { /* ignore partial string matches */ + return 0; + } + } else { + end_ptr_long = NULL; + } + + if (conv_base == 16) { /* hex string, under UNIX strtod() messes it up */ + /* UTODO: keep compatibility with is_numeric_string() here? */ + return 0; + } + + local_dval = collator_u_strtod(str, &end_ptr_double); + if (local_dval == 0 && end_ptr_double == str) { + end_ptr_double = NULL; + } else { + if (end_ptr_double == str+length) { /* floating point string */ + if (!zend_finite(local_dval)) { + /* "inf","nan" and maybe other weird ones */ + return 0; + } + + if (dval) { + *dval = local_dval; + } + return IS_DOUBLE; + } + } + + if (!allow_errors) { + return 0; + } + if (allow_errors == -1) { + zend_error(E_NOTICE, "A non well formed numeric value encountered"); + } + + if (allow_errors) { + if (end_ptr_double > end_ptr_long && dval) { + *dval = local_dval; + return IS_DOUBLE; + } else if (end_ptr_long && lval) { + *lval = local_lval; + return IS_LONG; + } + } + return 0; +} +/* }}} */ + +/* + * 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 + */ diff --git a/ext/intl/collator/collator_is_numeric.h b/ext/intl/collator/collator_is_numeric.h new file mode 100644 index 0000000..585d589 --- /dev/null +++ b/ext/intl/collator/collator_is_numeric.h @@ -0,0 +1,26 @@ +/* + +----------------------------------------------------------------------+ + | 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 <vsavchuk@productengine.com> | + | Dmitry Lakhtyuk <dlakhtyuk@productengine.com> | + +----------------------------------------------------------------------+ + */ + +#ifndef COLLATOR_IS_NUMERIC_H +#define COLLATOR_IS_NUMERIC_H + +#include <php.h> +#include <unicode/uchar.h> + +zend_uchar collator_is_numeric( UChar *str, int length, long *lval, double *dval, int allow_errors ); + +#endif // COLLATOR_IS_NUMERIC_H diff --git a/ext/intl/collator/collator_locale.c b/ext/intl/collator/collator_locale.c new file mode 100644 index 0000000..b30b021 --- /dev/null +++ b/ext/intl/collator/collator_locale.c @@ -0,0 +1,80 @@ +/* + +----------------------------------------------------------------------+ + | 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 <vsavchuk@productengine.com> | + | Dmitry Lakhtyuk <dlakhtyuk@productengine.com> | + +----------------------------------------------------------------------+ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "php_intl.h" +#include "collator_class.h" +#include "collator_locale.h" +#include "intl_convert.h" + +#include <zend_API.h> + +/* {{{ proto string Collator::getLocale( int $type ) + * Gets the locale name of the collator. }}} */ +/* {{{ proto string collator_get_locale( Collator $coll, int $type ) + * Gets the locale name of the collator. + */ +PHP_FUNCTION( collator_get_locale ) +{ + long type = 0; + char* locale_name = NULL; + + COLLATOR_METHOD_INIT_VARS + + /* Parse parameters. */ + if( zend_parse_method_parameters( ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Ol", + &object, Collator_ce_ptr, &type ) == FAILURE ) + { + intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR, + "collator_get_locale: unable to parse input params", 0 TSRMLS_CC ); + + RETURN_FALSE; + } + + /* Fetch the object. */ + COLLATOR_METHOD_FETCH_OBJECT; + + if (!co || !co->ucoll) { + intl_error_set_code( NULL, COLLATOR_ERROR_CODE( co ) TSRMLS_CC ); + intl_errors_set_custom_msg( COLLATOR_ERROR_P( co ), + "Object not initialized", 0 TSRMLS_CC ); + php_error_docref(NULL TSRMLS_CC, E_RECOVERABLE_ERROR, "Object not initialized"); + + RETURN_FALSE; + } + + /* Get locale by specified type. */ + locale_name = (char*) ucol_getLocaleByType( + co->ucoll, type, COLLATOR_ERROR_CODE_P( co ) ); + COLLATOR_CHECK_STATUS( co, "Error getting locale by type" ); + + /* Return it. */ + RETVAL_STRINGL( locale_name, strlen(locale_name), TRUE ); +} +/* }}} */ + +/* + * 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 + */ diff --git a/ext/intl/collator/collator_locale.h b/ext/intl/collator/collator_locale.h new file mode 100644 index 0000000..bda90cd --- /dev/null +++ b/ext/intl/collator/collator_locale.h @@ -0,0 +1,25 @@ +/* + +----------------------------------------------------------------------+ + | 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 <vsavchuk@productengine.com> | + | Dmitry Lakhtyuk <dlakhtyuk@productengine.com> | + +----------------------------------------------------------------------+ + */ + +#ifndef COLLATOR_LOCALE_H +#define COLLATOR_LOCALE_H + +#include <php.h> + +PHP_FUNCTION( collator_get_locale ); + +#endif // COLLATOR_LOCALE_H diff --git a/ext/intl/collator/collator_sort.c b/ext/intl/collator/collator_sort.c new file mode 100644 index 0000000..0785111 --- /dev/null +++ b/ext/intl/collator/collator_sort.c @@ -0,0 +1,621 @@ +/* + +----------------------------------------------------------------------+ + | 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 <vsavchuk@productengine.com> | + | Dmitry Lakhtyuk <dlakhtyuk@productengine.com> | + +----------------------------------------------------------------------+ + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "php_intl.h" +#include "collator.h" +#include "collator_class.h" +#include "collator_sort.h" +#include "collator_convert.h" +#include "intl_convert.h" + +#if !defined(HAVE_PTRDIFF_T) && !defined(_PTRDIFF_T_DEFINED) +typedef long ptrdiff_t; +#endif + +/** + * Declare 'index' which will point to sort key in sort key + * buffer. + */ +typedef struct _collator_sort_key_index { + char* key; /* pointer to sort key */ + zval** zstr; /* pointer to original string(hash-item) */ +} collator_sort_key_index_t; + +ZEND_EXTERN_MODULE_GLOBALS( intl ) + +static const size_t DEF_SORT_KEYS_BUF_SIZE = 1048576; +static const size_t DEF_SORT_KEYS_BUF_INCREMENT = 1048576; + +static const size_t DEF_SORT_KEYS_INDX_BUF_SIZE = 1048576; +static const size_t DEF_SORT_KEYS_INDX_BUF_INCREMENT = 1048576; + +static const size_t DEF_UTF16_BUF_SIZE = 1024; + +/* {{{ collator_regular_compare_function */ +static int collator_regular_compare_function(zval *result, zval *op1, zval *op2 TSRMLS_DC) +{ + Collator_object* co = NULL; + + int rc = SUCCESS; + + zval* str1 = collator_convert_object_to_string( op1 TSRMLS_CC ); + zval* str2 = collator_convert_object_to_string( op2 TSRMLS_CC ); + + zval* num1 = NULL; + zval* num2 = NULL; + zval* norm1 = NULL; + zval* norm2 = NULL; + + /* If both args are strings AND either of args is not numeric string + * then use ICU-compare. Otherwise PHP-compare. */ + if( Z_TYPE_P(str1) == IS_STRING && Z_TYPE_P(str2) == IS_STRING && + ( str1 == ( num1 = collator_convert_string_to_number_if_possible( str1 ) ) || + str2 == ( num2 = collator_convert_string_to_number_if_possible( str2 ) ) ) ) + { + /* Fetch collator object. */ + co = (Collator_object *) zend_object_store_get_object( INTL_G(current_collator) TSRMLS_CC ); + + if (!co || !co->ucoll) { + intl_error_set_code( NULL, COLLATOR_ERROR_CODE( co ) TSRMLS_CC ); + intl_errors_set_custom_msg( COLLATOR_ERROR_P( co ), + "Object not initialized", 0 TSRMLS_CC ); + php_error_docref(NULL TSRMLS_CC, E_RECOVERABLE_ERROR, "Object not initialized"); + } + + /* Compare the strings using ICU. */ + result->value.lval = ucol_strcoll( + co->ucoll, + INTL_Z_STRVAL_P(str1), INTL_Z_STRLEN_P(str1), + INTL_Z_STRVAL_P(str2), INTL_Z_STRLEN_P(str2) ); + result->type = IS_LONG; + } + else + { + /* num1 is set if str1 and str2 are strings. */ + if( num1 ) + { + if( num1 == str1 ) + { + /* str1 is string but not numeric string + * just convert it to utf8. + */ + norm1 = collator_convert_zstr_utf16_to_utf8( str1 ); + + /* num2 is not set but str2 is string => do normalization. */ + norm2 = collator_normalize_sort_argument( str2 ); + } + else + { + /* str1 is numeric strings => passthru to PHP-compare. */ + zval_add_ref( &num1 ); + norm1 = num1; + + /* str2 is numeric strings => passthru to PHP-compare. */ + zval_add_ref( &num2 ); + norm2 = num2; + } + } + else + { + /* num1 is not set if str1 or str2 is not a string => do normalization. */ + norm1 = collator_normalize_sort_argument( str1 ); + + /* if num1 is not set then num2 is not set as well => do normalization. */ + norm2 = collator_normalize_sort_argument( str2 ); + } + + rc = compare_function( result, norm1, norm2 TSRMLS_CC ); + + zval_ptr_dtor( &norm1 ); + zval_ptr_dtor( &norm2 ); + } + + if( num1 ) + zval_ptr_dtor( &num1 ); + + if( num2 ) + zval_ptr_dtor( &num2 ); + + zval_ptr_dtor( &str1 ); + zval_ptr_dtor( &str2 ); + + return rc; +} +/* }}} */ + +/* {{{ collator_numeric_compare_function + * Convert input args to double and compare it. + */ +static int collator_numeric_compare_function(zval *result, zval *op1, zval *op2 TSRMLS_DC) +{ + int rc = SUCCESS; + zval* num1 = NULL; + zval* num2 = NULL; + + if( Z_TYPE_P(op1) == IS_STRING ) + { + num1 = collator_convert_string_to_double( op1 ); + op1 = num1; + } + + if( Z_TYPE_P(op2) == IS_STRING ) + { + num2 = collator_convert_string_to_double( op2 ); + op2 = num2; + } + + rc = numeric_compare_function( result, op1, op2 TSRMLS_CC); + + if( num1 ) + zval_ptr_dtor( &num1 ); + if( num2 ) + zval_ptr_dtor( &num2 ); + + return rc; +} +/* }}} */ + +/* {{{ collator_icu_compare_function + * Direct use of ucol_strcoll. +*/ +static int collator_icu_compare_function(zval *result, zval *op1, zval *op2 TSRMLS_DC) +{ + int rc = SUCCESS; + Collator_object* co = NULL; + zval* str1 = NULL; + zval* str2 = NULL; + + str1 = collator_make_printable_zval( op1 ); + str2 = collator_make_printable_zval( op2 ); + + /* Fetch collator object. */ + co = (Collator_object *) zend_object_store_get_object( INTL_G(current_collator) TSRMLS_CC ); + + /* Compare the strings using ICU. */ + result->value.lval = ucol_strcoll( + co->ucoll, + INTL_Z_STRVAL_P(str1), INTL_Z_STRLEN_P(str1), + INTL_Z_STRVAL_P(str2), INTL_Z_STRLEN_P(str2) ); + result->type = IS_LONG; + + zval_ptr_dtor( &str1 ); + zval_ptr_dtor( &str2 ); + + return rc; +} +/* }}} */ + +/* {{{ collator_compare_func + * Taken from PHP5 source (array_data_compare). + */ +static int collator_compare_func( const void* a, const void* b TSRMLS_DC ) +{ + Bucket *f; + Bucket *s; + zval result; + zval *first; + zval *second; + + f = *((Bucket **) a); + s = *((Bucket **) b); + + first = *((zval **) f->pData); + second = *((zval **) s->pData); + + if( INTL_G(compare_func)( &result, first, second TSRMLS_CC) == FAILURE ) + return 0; + + if( Z_TYPE(result) == IS_DOUBLE ) + { + if( Z_DVAL(result) < 0 ) + return -1; + else if( Z_DVAL(result) > 0 ) + return 1; + else + return 0; + } + + convert_to_long(&result); + + if( Z_LVAL(result) < 0 ) + return -1; + else if( Z_LVAL(result) > 0 ) + return 1; + + return 0; +} +/* }}} */ + +/* {{{ collator_cmp_sort_keys + * Compare sort keys + */ +static int collator_cmp_sort_keys( const void *p1, const void *p2 TSRMLS_DC ) +{ + char* key1 = ((collator_sort_key_index_t*)p1)->key; + char* key2 = ((collator_sort_key_index_t*)p2)->key; + + return strcmp( key1, key2 ); +} +/* }}} */ + +/* {{{ collator_get_compare_function + * Choose compare function according to sort flags. + */ +static collator_compare_func_t collator_get_compare_function( const long sort_flags ) +{ + collator_compare_func_t func; + + switch( sort_flags ) + { + case COLLATOR_SORT_NUMERIC: + func = collator_numeric_compare_function; + break; + + case COLLATOR_SORT_STRING: + func = collator_icu_compare_function; + break; + + case COLLATOR_SORT_REGULAR: + default: + func = collator_regular_compare_function; + break; + } + + return func; +} +/* }}} */ + +/* {{{ collator_sort_internal + * Common code shared by collator_sort() and collator_asort() API functions. + */ +static void collator_sort_internal( int renumber, INTERNAL_FUNCTION_PARAMETERS ) +{ + zval* array = NULL; + HashTable* hash = NULL; + zval* saved_collator = NULL; + long sort_flags = COLLATOR_SORT_REGULAR; + + COLLATOR_METHOD_INIT_VARS + + /* Parse parameters. */ + if( zend_parse_method_parameters( ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oa|l", + &object, Collator_ce_ptr, &array, &sort_flags ) == FAILURE ) + { + intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR, + "collator_sort_internal: unable to parse input params", 0 TSRMLS_CC ); + + RETURN_FALSE; + } + + /* Fetch the object. */ + COLLATOR_METHOD_FETCH_OBJECT; + + /* Set 'compare function' according to sort flags. */ + INTL_G(compare_func) = collator_get_compare_function( sort_flags ); + + hash = HASH_OF( array ); + + /* Convert strings in the specified array from UTF-8 to UTF-16. */ + collator_convert_hash_from_utf8_to_utf16( hash, COLLATOR_ERROR_CODE_P( co ) ); + COLLATOR_CHECK_STATUS( co, "Error converting hash from UTF-8 to UTF-16" ); + + /* Save specified collator in the request-global (?) variable. */ + saved_collator = INTL_G( current_collator ); + INTL_G( current_collator ) = object; + + /* Sort specified array. */ + zend_hash_sort( hash, zend_qsort, collator_compare_func, renumber TSRMLS_CC ); + + /* Restore saved collator. */ + INTL_G( current_collator ) = saved_collator; + + /* Convert strings in the specified array back to UTF-8. */ + collator_convert_hash_from_utf16_to_utf8( hash, COLLATOR_ERROR_CODE_P( co ) ); + COLLATOR_CHECK_STATUS( co, "Error converting hash from UTF-16 to UTF-8" ); + + RETURN_TRUE; +} +/* }}} */ + +/* {{{ proto bool Collator::sort( Collator $coll, array(string) $arr [, int $sort_flags] ) + * Sort array using specified collator. }}} */ +/* {{{ proto bool collator_sort( Collator $coll, array(string) $arr [, int $sort_flags] ) + * Sort array using specified collator. + */ +PHP_FUNCTION( collator_sort ) +{ + collator_sort_internal( TRUE, INTERNAL_FUNCTION_PARAM_PASSTHRU ); +} +/* }}} */ + +/* {{{ proto bool Collator::sortWithSortKeys( Collator $coll, array(string) $arr ) + * Equivalent to standard PHP sort using Collator. + * Uses ICU ucol_getSortKey for performance. }}} */ +/* {{{ proto bool collator_sort_with_sort_keys( Collator $coll, array(string) $arr ) + * Equivalent to standard PHP sort using Collator. + * Uses ICU ucol_getSortKey for performance. + */ +PHP_FUNCTION( collator_sort_with_sort_keys ) +{ + zval* array = NULL; + HashTable* hash = NULL; + zval** hashData = NULL; /* currently processed item of input hash */ + + char* sortKeyBuf = NULL; /* buffer to store sort keys */ + uint32_t sortKeyBufSize = DEF_SORT_KEYS_BUF_SIZE; /* buffer size */ + ptrdiff_t sortKeyBufOffset = 0; /* pos in buffer to store sort key */ + int32_t sortKeyLen = 0; /* the length of currently processing key */ + uint32_t bufLeft = 0; + uint32_t bufIncrement = 0; + + collator_sort_key_index_t* sortKeyIndxBuf = NULL; /* buffer to store 'indexes' which will be passed to 'qsort' */ + uint32_t sortKeyIndxBufSize = DEF_SORT_KEYS_INDX_BUF_SIZE; + uint32_t sortKeyIndxSize = sizeof( collator_sort_key_index_t ); + + uint32_t sortKeyCount = 0; + uint32_t j = 0; + + UChar* utf16_buf = NULL; /* tmp buffer to hold current processing string in utf-16 */ + int utf16_buf_size = DEF_UTF16_BUF_SIZE; /* the length of utf16_buf */ + int utf16_len = 0; /* length of converted string */ + + HashTable* sortedHash = NULL; + + COLLATOR_METHOD_INIT_VARS + + /* Parse parameters. */ + if( zend_parse_method_parameters( ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oa", + &object, Collator_ce_ptr, &array ) == FAILURE ) + { + intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR, + "collator_sort_with_sort_keys: unable to parse input params", 0 TSRMLS_CC ); + + RETURN_FALSE; + } + + /* Fetch the object. */ + COLLATOR_METHOD_FETCH_OBJECT; + + if (!co || !co->ucoll) { + intl_error_set_code( NULL, COLLATOR_ERROR_CODE( co ) TSRMLS_CC ); + intl_errors_set_custom_msg( COLLATOR_ERROR_P( co ), + "Object not initialized", 0 TSRMLS_CC ); + php_error_docref(NULL TSRMLS_CC, E_RECOVERABLE_ERROR, "Object not initialized"); + + RETURN_FALSE; + } + + /* + * Sort specified array. + */ + hash = HASH_OF( array ); + + if( !hash || zend_hash_num_elements( hash ) == 0 ) + RETURN_TRUE; + + /* Create bufers */ + sortKeyBuf = ecalloc( sortKeyBufSize, sizeof( char ) ); + sortKeyIndxBuf = ecalloc( sortKeyIndxBufSize, sizeof( uint8_t ) ); + utf16_buf = eumalloc( utf16_buf_size ); + + /* Iterate through input hash and create a sort key for each value. */ + zend_hash_internal_pointer_reset( hash ); + while( zend_hash_get_current_data( hash, (void**) &hashData ) == SUCCESS ) + { + /* Convert current hash item from UTF-8 to UTF-16LE and save the result to utf16_buf. */ + + utf16_len = utf16_buf_size; + + /* Process string values only. */ + if( Z_TYPE_PP( hashData ) == IS_STRING ) + { + intl_convert_utf8_to_utf16( &utf16_buf, &utf16_len, Z_STRVAL_PP( hashData ), Z_STRLEN_PP( hashData ), COLLATOR_ERROR_CODE_P( co ) ); + + if( U_FAILURE( COLLATOR_ERROR_CODE( co ) ) ) + { + intl_error_set_code( NULL, COLLATOR_ERROR_CODE( co ) TSRMLS_CC ); + intl_errors_set_custom_msg( COLLATOR_ERROR_P( co ), "Sort with sort keys failed", 0 TSRMLS_CC ); + + if( utf16_buf ) + efree( utf16_buf ); + + efree( sortKeyIndxBuf ); + efree( sortKeyBuf ); + + RETURN_FALSE; + } + } + else + { + /* Set empty string */ + utf16_len = 0; + utf16_buf[utf16_len] = 0; + } + + if( (utf16_len + 1) > utf16_buf_size ) + utf16_buf_size = utf16_len + 1; + + /* Get sort key, reallocating the buffer if needed. */ + bufLeft = sortKeyBufSize - sortKeyBufOffset; + + sortKeyLen = ucol_getSortKey( co->ucoll, + utf16_buf, + utf16_len, + (uint8_t*)sortKeyBuf + sortKeyBufOffset, + bufLeft ); + + /* check for sortKeyBuf overflow, increasing its size of the buffer if needed */ + if( sortKeyLen > bufLeft ) + { + bufIncrement = ( sortKeyLen > DEF_SORT_KEYS_BUF_INCREMENT ) ? sortKeyLen : DEF_SORT_KEYS_BUF_INCREMENT; + + sortKeyBufSize += bufIncrement; + bufLeft += bufIncrement; + + sortKeyBuf = erealloc( sortKeyBuf, sortKeyBufSize ); + + sortKeyLen = ucol_getSortKey( co->ucoll, utf16_buf, utf16_len, (uint8_t*)sortKeyBuf + sortKeyBufOffset, bufLeft ); + } + + /* check sortKeyIndxBuf overflow, increasing its size of the buffer if needed */ + if( ( sortKeyCount + 1 ) * sortKeyIndxSize > sortKeyIndxBufSize ) + { + bufIncrement = ( sortKeyIndxSize > DEF_SORT_KEYS_INDX_BUF_INCREMENT ) ? sortKeyIndxSize : DEF_SORT_KEYS_INDX_BUF_INCREMENT; + + sortKeyIndxBufSize += bufIncrement; + + sortKeyIndxBuf = erealloc( sortKeyIndxBuf, sortKeyIndxBufSize ); + } + + sortKeyIndxBuf[sortKeyCount].key = (char*)sortKeyBufOffset; /* remeber just offset, cause address */ + /* of 'sortKeyBuf' may be changed due to realloc. */ + sortKeyIndxBuf[sortKeyCount].zstr = hashData; + + sortKeyBufOffset += sortKeyLen; + ++sortKeyCount; + + zend_hash_move_forward( hash ); + } + + /* update ptrs to point to valid keys. */ + for( j = 0; j < sortKeyCount; j++ ) + sortKeyIndxBuf[j].key = sortKeyBuf + (ptrdiff_t)sortKeyIndxBuf[j].key; + + /* sort it */ + zend_qsort( sortKeyIndxBuf, sortKeyCount, sortKeyIndxSize, collator_cmp_sort_keys TSRMLS_CC ); + + /* for resulting hash we'll assign new hash keys rather then reordering */ + ALLOC_HASHTABLE( sortedHash ); + zend_hash_init( sortedHash, 0, NULL, ZVAL_PTR_DTOR, 0 ); + + for( j = 0; j < sortKeyCount; j++ ) + { + zval_add_ref( sortKeyIndxBuf[j].zstr ); + zend_hash_next_index_insert( sortedHash, sortKeyIndxBuf[j].zstr, sizeof(zval **), NULL ); + } + + /* Save sorted hash into return variable. */ + zval_dtor( array ); + (array)->value.ht = sortedHash; + (array)->type = IS_ARRAY; + + if( utf16_buf ) + efree( utf16_buf ); + + efree( sortKeyIndxBuf ); + efree( sortKeyBuf ); + + RETURN_TRUE; +} +/* }}} */ + +/* {{{ proto bool Collator::asort( Collator $coll, array(string) $arr ) + * Sort array using specified collator, maintaining index association. }}} */ +/* {{{ proto bool collator_asort( Collator $coll, array(string) $arr ) + * Sort array using specified collator, maintaining index association. + */ +PHP_FUNCTION( collator_asort ) +{ + collator_sort_internal( FALSE, INTERNAL_FUNCTION_PARAM_PASSTHRU ); +} +/* }}} */ + +/* {{{ proto bool Collator::getSortKey( Collator $coll, string $str ) + * Get a sort key for a string from a Collator. }}} */ +/* {{{ proto bool collator_get_sort_key( Collator $coll, string $str ) + * Get a sort key for a string from a Collator. }}} */ +PHP_FUNCTION( collator_get_sort_key ) +{ + char* str = NULL; + int str_len = 0; + UChar* ustr = NULL; + int ustr_len = 0; + uint8_t* key = NULL; + int key_len = 0; + + COLLATOR_METHOD_INIT_VARS + + /* Parse parameters. */ + if( zend_parse_method_parameters( ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os", + &object, Collator_ce_ptr, &str, &str_len ) == FAILURE ) + { + intl_error_set( NULL, U_ILLEGAL_ARGUMENT_ERROR, + "collator_get_sort_key: unable to parse input params", 0 TSRMLS_CC ); + + RETURN_FALSE; + } + + /* Fetch the object. */ + COLLATOR_METHOD_FETCH_OBJECT; + + if (!co || !co->ucoll) { + intl_error_set_code( NULL, COLLATOR_ERROR_CODE( co ) TSRMLS_CC ); + intl_errors_set_custom_msg( COLLATOR_ERROR_P( co ), + "Object not initialized", 0 TSRMLS_CC ); + php_error_docref(NULL TSRMLS_CC, E_RECOVERABLE_ERROR, "Object not initialized"); + + RETURN_FALSE; + } + + /* + * Compare given strings (converting them to UTF-16 first). + */ + + /* First convert the strings to UTF-16. */ + intl_convert_utf8_to_utf16( + &ustr, &ustr_len, str, str_len, COLLATOR_ERROR_CODE_P( co ) ); + if( U_FAILURE( COLLATOR_ERROR_CODE( co ) ) ) + { + /* Set global error code. */ + intl_error_set_code( NULL, COLLATOR_ERROR_CODE( co ) TSRMLS_CC ); + + /* Set error messages. */ + intl_errors_set_custom_msg( COLLATOR_ERROR_P( co ), + "Error converting first argument to UTF-16", 0 TSRMLS_CC ); + efree( ustr ); + RETURN_FALSE; + } + + /* ucol_getSortKey is exception in that the key length includes the + * NUL terminator*/ + key_len = ucol_getSortKey(co->ucoll, ustr, ustr_len, key, 0); + if(!key_len) { + efree( ustr ); + RETURN_FALSE; + } + key = emalloc(key_len); + key_len = ucol_getSortKey(co->ucoll, ustr, ustr_len, key, key_len); + efree( ustr ); + if(!key_len) { + RETURN_FALSE; + } + RETURN_STRINGL((char *)key, key_len - 1, 0); +} +/* }}} */ + +/* + * 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 + */ diff --git a/ext/intl/collator/collator_sort.h b/ext/intl/collator/collator_sort.h new file mode 100644 index 0000000..a990cdf --- /dev/null +++ b/ext/intl/collator/collator_sort.h @@ -0,0 +1,30 @@ +/* + +----------------------------------------------------------------------+ + | 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 <vsavchuk@productengine.com> | + | Dmitry Lakhtyuk <dlakhtyuk@productengine.com> | + +----------------------------------------------------------------------+ + */ + +#ifndef COLLATOR_SORT_H +#define COLLATOR_SORT_H + +#include <php.h> + +typedef int (*collator_compare_func_t)( zval *result, zval *op1, zval *op2 TSRMLS_DC ); + +PHP_FUNCTION( collator_sort ); +PHP_FUNCTION( collator_sort_with_sort_keys ); +PHP_FUNCTION( collator_get_sort_key ); +PHP_FUNCTION( collator_asort ); + +#endif // COLLATOR_SORT_H |