diff options
Diffstat (limited to 'ext')
60 files changed, 1931 insertions, 217 deletions
diff --git a/ext/date/php_date.c b/ext/date/php_date.c index 0394cb6a5..f4115dc7e 100644 --- a/ext/date/php_date.c +++ b/ext/date/php_date.c @@ -385,7 +385,9 @@ const zend_function_entry date_functions[] = { /* Advanced Interface */ PHP_FE(date_create, arginfo_date_create) + PHP_FE(date_create_immutable, arginfo_date_create) PHP_FE(date_create_from_format, arginfo_date_create_from_format) + PHP_FE(date_create_immutable_from_format, arginfo_date_create_from_format) PHP_FE(date_parse, arginfo_date_parse) PHP_FE(date_parse_from_format, arginfo_date_parse_from_format) PHP_FE(date_get_last_errors, arginfo_date_get_last_errors) @@ -450,6 +452,20 @@ const zend_function_entry date_funcs_date[] = { PHP_FE_END }; +const zend_function_entry date_funcs_immutable[] = { + PHP_ME(DateTimeImmutable, __construct, arginfo_date_create, ZEND_ACC_CTOR|ZEND_ACC_PUBLIC) + PHP_ME(DateTimeImmutable, __set_state, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC) + PHP_ME(DateTimeImmutable, modify, arginfo_date_method_modify, 0) + PHP_ME(DateTimeImmutable, add, arginfo_date_method_add, 0) + PHP_ME(DateTimeImmutable, sub, arginfo_date_method_sub, 0) + PHP_ME(DateTimeImmutable, setTimezone, arginfo_date_method_timezone_set, 0) + PHP_ME(DateTimeImmutable, setTime, arginfo_date_method_time_set, 0) + PHP_ME(DateTimeImmutable, setDate, arginfo_date_method_date_set, 0) + PHP_ME(DateTimeImmutable, setISODate, arginfo_date_method_isodate_set, 0) + PHP_ME(DateTimeImmutable, setTimestamp, arginfo_date_method_timestamp_set, 0) + PHP_FE_END +}; + const zend_function_entry date_funcs_timezone[] = { PHP_ME(DateTimeZone, __construct, arginfo_timezone_open, ZEND_ACC_CTOR|ZEND_ACC_PUBLIC) PHP_ME_MAPPING(getName, timezone_name_get, arginfo_timezone_method_name_get, 0) @@ -508,6 +524,7 @@ PHP_INI_END() /* }}} */ zend_class_entry *date_ce_date, *date_ce_timezone, *date_ce_interval, *date_ce_period; +zend_class_entry *date_ce_immutable; PHPAPI zend_class_entry *php_date_get_date_ce(void) @@ -515,12 +532,18 @@ PHPAPI zend_class_entry *php_date_get_date_ce(void) return date_ce_date; } +PHPAPI zend_class_entry *php_date_get_immutable_ce(void) +{ + return date_ce_immutable; +} + PHPAPI zend_class_entry *php_date_get_timezone_ce(void) { return date_ce_timezone; } static zend_object_handlers date_object_handlers_date; +static zend_object_handlers date_object_handlers_immutable; static zend_object_handlers date_object_handlers_timezone; static zend_object_handlers date_object_handlers_interval; static zend_object_handlers date_object_handlers_period; @@ -555,11 +578,13 @@ static void date_object_free_storage_interval(void *object TSRMLS_DC); static void date_object_free_storage_period(void *object TSRMLS_DC); static zend_object_value date_object_new_date(zend_class_entry *class_type TSRMLS_DC); +static zend_object_value date_object_new_immutable(zend_class_entry *class_type TSRMLS_DC); static zend_object_value date_object_new_timezone(zend_class_entry *class_type TSRMLS_DC); static zend_object_value date_object_new_interval(zend_class_entry *class_type TSRMLS_DC); static zend_object_value date_object_new_period(zend_class_entry *class_type TSRMLS_DC); static zend_object_value date_object_clone_date(zval *this_ptr TSRMLS_DC); +static zend_object_value date_object_clone_immutable(zval *this_ptr TSRMLS_DC); static zend_object_value date_object_clone_timezone(zval *this_ptr TSRMLS_DC); static zend_object_value date_object_clone_interval(zval *this_ptr TSRMLS_DC); static zend_object_value date_object_clone_period(zval *this_ptr TSRMLS_DC); @@ -1822,7 +1847,7 @@ static void date_period_it_current_data(zend_object_iterator *iter, zval ***data /* Create new object */ MAKE_STD_ZVAL(iterator->current); - php_date_instantiate(date_ce_date, iterator->current TSRMLS_CC); + php_date_instantiate(object->start_ce, iterator->current TSRMLS_CC); newdateobj = (php_date_obj *) zend_object_store_get_object(iterator->current TSRMLS_CC); newdateobj->time = timelib_time_ctor(); *newdateobj->time = *it_time; @@ -1908,7 +1933,7 @@ zend_object_iterator *date_object_period_get_iterator(zend_class_entry *ce, zval static void date_register_classes(TSRMLS_D) { - zend_class_entry ce_date, ce_timezone, ce_interval, ce_period; + zend_class_entry ce_date, ce_immutable, ce_timezone, ce_interval, ce_period; INIT_CLASS_ENTRY(ce_date, "DateTime", date_funcs_date); ce_date.create_object = date_object_new_date; @@ -1934,6 +1959,13 @@ static void date_register_classes(TSRMLS_D) REGISTER_DATE_CLASS_CONST_STRING("RSS", DATE_FORMAT_RFC1123); REGISTER_DATE_CLASS_CONST_STRING("W3C", DATE_FORMAT_RFC3339); + INIT_CLASS_ENTRY(ce_immutable, "DateTimeImmutable", date_funcs_immutable); + ce_immutable.create_object = date_object_new_date; + date_ce_immutable = zend_register_internal_class_ex(&ce_immutable, date_ce_date, "DateTime" TSRMLS_CC); + memcpy(&date_object_handlers_immutable, zend_get_std_object_handlers(), sizeof(zend_object_handlers)); + date_object_handlers_immutable.clone_obj = date_object_clone_date; + date_object_handlers_immutable.compare_objects = date_object_compare_date; + date_object_handlers_immutable.get_properties = date_object_get_properties; INIT_CLASS_ENTRY(ce_timezone, "DateTimeZone", date_funcs_timezone); ce_timezone.create_object = date_object_new_timezone; @@ -2034,6 +2066,19 @@ static zend_object_value date_object_clone_date(zval *this_ptr TSRMLS_DC) return new_ov; } +static zval* date_clone_immutable(zval *object TSRMLS_DC) +{ + zval *new_object; + + ALLOC_ZVAL(new_object); + Z_OBJVAL_P(new_object) = date_object_clone_date(object TSRMLS_CC); + Z_SET_REFCOUNT_P(new_object, 1); + Z_SET_ISREF_P(new_object); + Z_TYPE_P(new_object) = IS_OBJECT; + + return new_object; +} + static int date_object_compare_date(zval *d1, zval *d2 TSRMLS_DC) { if (Z_TYPE_P(d1) == IS_OBJECT && Z_TYPE_P(d2) == IS_OBJECT && @@ -2470,6 +2515,26 @@ PHP_FUNCTION(date_create) } /* }}} */ +/* {{{ proto DateTime date_create_immutable([string time[, DateTimeZone object]]) + Returns new DateTime object +*/ +PHP_FUNCTION(date_create_immutable) +{ + zval *timezone_object = NULL; + char *time_str = NULL; + int time_str_len = 0; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|sO!", &time_str, &time_str_len, &timezone_object, date_ce_timezone) == FAILURE) { + RETURN_FALSE; + } + + php_date_instantiate(date_ce_immutable, return_value TSRMLS_CC); + if (!php_date_initialize(zend_object_store_get_object(return_value TSRMLS_CC), time_str, time_str_len, NULL, timezone_object, 0 TSRMLS_CC)) { + RETURN_FALSE; + } +} +/* }}} */ + /* {{{ proto DateTime date_create_from_format(string format, string time[, DateTimeZone object]) Returns new DateTime object formatted according to the specified format */ @@ -2490,6 +2555,26 @@ PHP_FUNCTION(date_create_from_format) } /* }}} */ +/* {{{ proto DateTime date_create_immutable_from_format(string format, string time[, DateTimeZone object]) + Returns new DateTime object formatted according to the specified format +*/ +PHP_FUNCTION(date_create_immutable_from_format) +{ + zval *timezone_object = NULL; + char *time_str = NULL, *format_str = NULL; + int time_str_len = 0, format_str_len = 0; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|O", &format_str, &format_str_len, &time_str, &time_str_len, &timezone_object, date_ce_timezone) == FAILURE) { + RETURN_FALSE; + } + + php_date_instantiate(date_ce_immutable, return_value TSRMLS_CC); + if (!php_date_initialize(zend_object_store_get_object(return_value TSRMLS_CC), time_str, time_str_len, format_str, timezone_object, 0 TSRMLS_CC)) { + RETURN_FALSE; + } +} +/* }}} */ + /* {{{ proto DateTime::__construct([string time[, DateTimeZone object]]) Creates new DateTime object */ @@ -2508,6 +2593,24 @@ PHP_METHOD(DateTime, __construct) } /* }}} */ +/* {{{ proto DateTimeImmutable::__construct([string time[, DateTimeZone object]]) + Creates new DateTimeImmutable object +*/ +PHP_METHOD(DateTimeImmutable, __construct) +{ + zval *timezone_object = NULL; + char *time_str = NULL; + int time_str_len = 0; + zend_error_handling error_handling; + + zend_replace_error_handling(EH_THROW, NULL, &error_handling TSRMLS_CC); + if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|sO!", &time_str, &time_str_len, &timezone_object, date_ce_timezone)) { + php_date_initialize(zend_object_store_get_object(getThis() TSRMLS_CC), time_str, time_str_len, NULL, timezone_object, 1 TSRMLS_CC); + } + zend_restore_error_handling(&error_handling TSRMLS_CC); +} +/* }}} */ + static int php_date_initialize_from_hash(zval **return_value, php_date_obj **dateobj, HashTable *myht TSRMLS_DC) { zval **z_date = NULL; @@ -2575,6 +2678,26 @@ PHP_METHOD(DateTime, __set_state) } /* }}} */ +/* {{{ proto DateTimeImmutable::__set_state() +*/ +PHP_METHOD(DateTimeImmutable, __set_state) +{ + php_date_obj *dateobj; + zval *array; + HashTable *myht; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "a", &array) == FAILURE) { + RETURN_FALSE; + } + + myht = HASH_OF(array); + + php_date_instantiate(date_ce_immutable, return_value TSRMLS_CC); + dateobj = (php_date_obj *) zend_object_store_get_object(return_value TSRMLS_CC); + php_date_initialize_from_hash(&return_value, &dateobj, myht TSRMLS_CC); +} +/* }}} */ + /* {{{ proto DateTime::__wakeup() */ PHP_METHOD(DateTime, __wakeup) @@ -2760,23 +2883,18 @@ PHP_FUNCTION(date_format) } /* }}} */ -/* {{{ proto DateTime date_modify(DateTime object, string modify) - Alters the timestamp. -*/ -PHP_FUNCTION(date_modify) +static int php_date_modify(zval *object, char *modify, int modify_len TSRMLS_DC) { - zval *object; php_date_obj *dateobj; - char *modify; - int modify_len; timelib_time *tmp_time; timelib_error_container *err = NULL; - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os", &object, date_ce_date, &modify, &modify_len) == FAILURE) { - RETURN_FALSE; - } dateobj = (php_date_obj *) zend_object_store_get_object(object TSRMLS_CC); - DATE_CHECK_INITIALIZED(dateobj->time, DateTime); + + if (!(dateobj->time)) { + php_error_docref(NULL TSRMLS_CC, E_WARNING, "The DateTime object has not been correctly initialized by its constructor"); + return 0; + } tmp_time = timelib_strtotime(modify, modify_len, &err, DATE_TIMEZONEDB, php_date_parse_tzfile_wrapper); @@ -2787,7 +2905,7 @@ PHP_FUNCTION(date_modify) php_error_docref(NULL TSRMLS_CC, E_WARNING, "Failed to parse time string (%s) at position %d (%c): %s", modify, err->error_messages[0].position, err->error_messages[0].character, err->error_messages[0].message); timelib_time_dtor(tmp_time); - RETURN_FALSE; + return 0; } memcpy(&dateobj->time->relative, &tmp_time->relative, sizeof(struct timelib_rel_time)); @@ -2823,30 +2941,63 @@ PHP_FUNCTION(date_modify) timelib_update_ts(dateobj->time, NULL); timelib_update_from_sse(dateobj->time); dateobj->time->have_relative = 0; + + return 1; +} - RETURN_ZVAL(object, 1, 0); +/* {{{ proto DateTime date_modify(DateTime object, string modify) + Alters the timestamp. +*/ +PHP_FUNCTION(date_modify) +{ + zval *object; + char *modify; + int modify_len; + + if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os", &object, date_ce_date, &modify, &modify_len) == FAILURE) { + RETURN_FALSE; + } + + if (php_date_modify(object, modify, modify_len TSRMLS_CC)) { + RETURN_ZVAL(object, 1, 0); + } + + RETURN_FALSE; } /* }}} */ -/* {{{ proto DateTime date_add(DateTime object, DateInterval interval) - Adds an interval to the current date in object. +/* {{{ proto DateTimeImmutable::modify() */ -PHP_FUNCTION(date_add) +PHP_METHOD(DateTimeImmutable, modify) { - zval *object, *interval; - php_date_obj *dateobj; - php_interval_obj *intobj; - int bias = 1; + zval *object, *new_object; + char *modify; + int modify_len; - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "OO", &object, date_ce_date, &interval, date_ce_interval) == FAILURE) { + if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Os", &object, date_ce_date, &modify, &modify_len) == FAILURE) { RETURN_FALSE; } + + new_object = date_clone_immutable(object TSRMLS_CC); + if (php_date_modify(new_object, modify, modify_len TSRMLS_CC)) { + RETURN_ZVAL(new_object, 0, 1); + } + + RETURN_FALSE; +} +/* }}} */ + +static void php_date_add(zval *object, zval *interval, zval *return_value TSRMLS_DC) +{ + php_date_obj *dateobj; + php_interval_obj *intobj; + int bias = 1; + dateobj = (php_date_obj *) zend_object_store_get_object(object TSRMLS_CC); DATE_CHECK_INITIALIZED(dateobj->time, DateTime); intobj = (php_interval_obj *) zend_object_store_get_object(interval TSRMLS_CC); DATE_CHECK_INITIALIZED(intobj->initialized, DateInterval); - if (intobj->diff->have_weekday_relative || intobj->diff->have_special_relative) { memcpy(&dateobj->time->relative, intobj->diff, sizeof(struct timelib_rel_time)); } else { @@ -2867,24 +3018,48 @@ PHP_FUNCTION(date_add) timelib_update_ts(dateobj->time, NULL); timelib_update_from_sse(dateobj->time); dateobj->time->have_relative = 0; +} + +/* {{{ proto DateTime date_add(DateTime object, DateInterval interval) + Adds an interval to the current date in object. +*/ +PHP_FUNCTION(date_add) +{ + zval *object, *interval; + + if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "OO", &object, date_ce_date, &interval, date_ce_interval) == FAILURE) { + RETURN_FALSE; + } + + php_date_add(object, interval, return_value TSRMLS_CC); RETURN_ZVAL(object, 1, 0); } /* }}} */ -/* {{{ proto DateTime date_sub(DateTime object, DateInterval interval) - Subtracts an interval to the current date in object. +/* {{{ proto DateTimeImmutable::add() */ -PHP_FUNCTION(date_sub) +PHP_METHOD(DateTimeImmutable, add) { - zval *object, *interval; - php_date_obj *dateobj; - php_interval_obj *intobj; - int bias = 1; + zval *object, *interval, *new_object; if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "OO", &object, date_ce_date, &interval, date_ce_interval) == FAILURE) { RETURN_FALSE; } + + new_object = date_clone_immutable(object TSRMLS_CC); + php_date_add(new_object, interval, return_value TSRMLS_CC); + + RETURN_ZVAL(new_object, 0, 1); +} +/* }}} */ + +static void php_date_sub(zval *object, zval *interval, zval *return_value TSRMLS_DC) +{ + php_date_obj *dateobj; + php_interval_obj *intobj; + int bias = 1; + dateobj = (php_date_obj *) zend_object_store_get_object(object TSRMLS_CC); DATE_CHECK_INITIALIZED(dateobj->time, DateTime); intobj = (php_interval_obj *) zend_object_store_get_object(interval TSRMLS_CC); @@ -2913,11 +3088,42 @@ PHP_FUNCTION(date_sub) timelib_update_from_sse(dateobj->time); dateobj->time->have_relative = 0; +} + +/* {{{ proto DateTime date_sub(DateTime object, DateInterval interval) + Subtracts an interval to the current date in object. +*/ +PHP_FUNCTION(date_sub) +{ + zval *object, *interval; + + if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "OO", &object, date_ce_date, &interval, date_ce_interval) == FAILURE) { + RETURN_FALSE; + } + + php_date_sub(object, interval, return_value TSRMLS_CC); RETURN_ZVAL(object, 1, 0); } /* }}} */ +/* {{{ proto DateTimeImmutable::sub() +*/ +PHP_METHOD(DateTimeImmutable, sub) +{ + zval *object, *interval, *new_object; + + if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "OO", &object, date_ce_date, &interval, date_ce_interval) == FAILURE) { + RETURN_FALSE; + } + + new_object = date_clone_immutable(object TSRMLS_CC); + php_date_sub(new_object, interval, return_value TSRMLS_CC); + + RETURN_ZVAL(new_object, 0, 1); +} +/* }}} */ + /* {{{ proto DateTimeZone date_timezone_get(DateTime object) Return new DateTimeZone object relative to give DateTime */ @@ -2956,19 +3162,11 @@ PHP_FUNCTION(date_timezone_get) } /* }}} */ -/* {{{ proto DateTime date_timezone_set(DateTime object, DateTimeZone object) - Sets the timezone for the DateTime object. -*/ -PHP_FUNCTION(date_timezone_set) +static void php_date_timezone_set(zval *object, zval *timezone_object, zval *return_value TSRMLS_DC) { - zval *object; - zval *timezone_object; php_date_obj *dateobj; php_timezone_obj *tzobj; - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "OO", &object, date_ce_date, &timezone_object, date_ce_timezone) == FAILURE) { - RETURN_FALSE; - } dateobj = (php_date_obj *) zend_object_store_get_object(object TSRMLS_CC); DATE_CHECK_INITIALIZED(dateobj->time, DateTime); tzobj = (php_timezone_obj *) zend_object_store_get_object(timezone_object TSRMLS_CC); @@ -2978,11 +3176,44 @@ PHP_FUNCTION(date_timezone_set) } timelib_set_timezone(dateobj->time, tzobj->tzi.tz); timelib_unixtime2local(dateobj->time, dateobj->time->sse); +} + +/* {{{ proto DateTime date_timezone_set(DateTime object, DateTimeZone object) + Sets the timezone for the DateTime object. +*/ +PHP_FUNCTION(date_timezone_set) +{ + zval *object; + zval *timezone_object; + + if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "OO", &object, date_ce_date, &timezone_object, date_ce_timezone) == FAILURE) { + RETURN_FALSE; + } + + php_date_timezone_set(object, timezone_object, return_value TSRMLS_CC); RETURN_ZVAL(object, 1, 0); } /* }}} */ +/* {{{ proto DateTimeImmutable::setTimezone() +*/ +PHP_METHOD(DateTimeImmutable, setTimezone) +{ + zval *object, *new_object; + zval *timezone_object; + + if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "OO", &object, date_ce_date, &timezone_object, date_ce_timezone) == FAILURE) { + RETURN_FALSE; + } + + new_object = date_clone_immutable(object TSRMLS_CC); + php_date_timezone_set(new_object, timezone_object, return_value TSRMLS_CC); + + RETURN_ZVAL(new_object, 0, 1); +} +/* }}} */ + /* {{{ proto long date_offset_get(DateTime object) Returns the DST offset. */ @@ -3018,64 +3249,106 @@ PHP_FUNCTION(date_offset_get) } /* }}} */ -/* {{{ proto DateTime date_time_set(DateTime object, long hour, long minute[, long second]) - Sets the time. -*/ -PHP_FUNCTION(date_time_set) +static void php_date_time_set(zval *object, long h, long i, long s, zval *return_value TSRMLS_DC) { - zval *object; php_date_obj *dateobj; - long h, i, s = 0; - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oll|l", &object, date_ce_date, &h, &i, &s) == FAILURE) { - RETURN_FALSE; - } dateobj = (php_date_obj *) zend_object_store_get_object(object TSRMLS_CC); DATE_CHECK_INITIALIZED(dateobj->time, DateTime); dateobj->time->h = h; dateobj->time->i = i; dateobj->time->s = s; timelib_update_ts(dateobj->time, NULL); +} + +/* {{{ proto DateTime date_time_set(DateTime object, long hour, long minute[, long second]) + Sets the time. +*/ +PHP_FUNCTION(date_time_set) +{ + zval *object; + long h, i, s = 0; + + if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oll|l", &object, date_ce_date, &h, &i, &s) == FAILURE) { + RETURN_FALSE; + } + + php_date_time_set(object, h, i, s, return_value TSRMLS_CC); RETURN_ZVAL(object, 1, 0); } /* }}} */ -/* {{{ proto DateTime date_date_set(DateTime object, long year, long month, long day) - Sets the date. +/* {{{ proto DateTimeImmutable::setTime() */ -PHP_FUNCTION(date_date_set) +PHP_METHOD(DateTimeImmutable, setTime) { - zval *object; - php_date_obj *dateobj; - long y, m, d; + zval *object, *new_object; + long h, i, s = 0; - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Olll", &object, date_ce_date, &y, &m, &d) == FAILURE) { + if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oll|l", &object, date_ce_date, &h, &i, &s) == FAILURE) { RETURN_FALSE; } + + new_object = date_clone_immutable(object TSRMLS_CC); + php_date_time_set(new_object, h, i, s, return_value TSRMLS_CC); + + RETURN_ZVAL(new_object, 0, 1); +} +/* }}} */ + +static void php_date_date_set(zval *object, long y, long m, long d, zval *return_value TSRMLS_DC) +{ + php_date_obj *dateobj; + dateobj = (php_date_obj *) zend_object_store_get_object(object TSRMLS_CC); DATE_CHECK_INITIALIZED(dateobj->time, DateTime); dateobj->time->y = y; dateobj->time->m = m; dateobj->time->d = d; timelib_update_ts(dateobj->time, NULL); +} + +/* {{{ proto DateTime date_date_set(DateTime object, long year, long month, long day) + Sets the date. +*/ +PHP_FUNCTION(date_date_set) +{ + zval *object; + long y, m, d; + + if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Olll", &object, date_ce_date, &y, &m, &d) == FAILURE) { + RETURN_FALSE; + } + + php_date_date_set(object, y, m, d, return_value TSRMLS_CC); RETURN_ZVAL(object, 1, 0); } /* }}} */ -/* {{{ proto DateTime date_isodate_set(DateTime object, long year, long week[, long day]) - Sets the ISO date. +/* {{{ proto DateTimeImmutable::setDate() */ -PHP_FUNCTION(date_isodate_set) +PHP_METHOD(DateTimeImmutable, setDate) { - zval *object; - php_date_obj *dateobj; - long y, w, d = 1; + zval *object, *new_object; + long y, m, d; - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oll|l", &object, date_ce_date, &y, &w, &d) == FAILURE) { + if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Olll", &object, date_ce_date, &y, &m, &d) == FAILURE) { RETURN_FALSE; } + + new_object = date_clone_immutable(object TSRMLS_CC); + php_date_date_set(new_object, y, m, d, return_value TSRMLS_CC); + + RETURN_ZVAL(new_object, 0, 1); +} +/* }}} */ + +static void php_date_isodate_set(zval *object, long y, long w, long d, zval *return_value TSRMLS_DC) +{ + php_date_obj *dateobj; + dateobj = (php_date_obj *) zend_object_store_get_object(object TSRMLS_CC); DATE_CHECK_INITIALIZED(dateobj->time, DateTime); dateobj->time->y = y; @@ -3086,32 +3359,90 @@ PHP_FUNCTION(date_isodate_set) dateobj->time->have_relative = 1; timelib_update_ts(dateobj->time, NULL); +} + +/* {{{ proto DateTime date_isodate_set(DateTime object, long year, long week[, long day]) + Sets the ISO date. +*/ +PHP_FUNCTION(date_isodate_set) +{ + zval *object; + long y, w, d = 1; + + if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oll|l", &object, date_ce_date, &y, &w, &d) == FAILURE) { + RETURN_FALSE; + } + + php_date_isodate_set(object, y, w, d, return_value TSRMLS_CC); RETURN_ZVAL(object, 1, 0); } /* }}} */ -/* {{{ proto DateTime date_timestamp_set(DateTime object, long unixTimestamp) - Sets the date and time based on an Unix timestamp. +/* {{{ proto DateTimeImmutable::setISODate() */ -PHP_FUNCTION(date_timestamp_set) +PHP_METHOD(DateTimeImmutable, setISODate) { - zval *object; - php_date_obj *dateobj; - long timestamp; + zval *object, *new_object; + long y, w, d = 1; - if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Ol", &object, date_ce_date, ×tamp) == FAILURE) { + if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Oll|l", &object, date_ce_date, &y, &w, &d) == FAILURE) { RETURN_FALSE; } + + new_object = date_clone_immutable(object TSRMLS_CC); + php_date_isodate_set(new_object, y, w, d, return_value TSRMLS_CC); + + RETURN_ZVAL(new_object, 0, 1); +} +/* }}} */ + +static void php_date_timestamp_set(zval *object, long timestamp, zval *return_value TSRMLS_DC) +{ + php_date_obj *dateobj; + dateobj = (php_date_obj *) zend_object_store_get_object(object TSRMLS_CC); DATE_CHECK_INITIALIZED(dateobj->time, DateTime); timelib_unixtime2local(dateobj->time, (timelib_sll)timestamp); timelib_update_ts(dateobj->time, NULL); +} + +/* {{{ proto DateTime date_timestamp_set(DateTime object, long unixTimestamp) + Sets the date and time based on an Unix timestamp. +*/ +PHP_FUNCTION(date_timestamp_set) +{ + zval *object; + long timestamp; + + if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Ol", &object, date_ce_date, ×tamp) == FAILURE) { + RETURN_FALSE; + } + + php_date_timestamp_set(object, timestamp, return_value TSRMLS_CC); RETURN_ZVAL(object, 1, 0); } /* }}} */ +/* {{{ proto DateTimeImmutable::setTimestamp() +*/ +PHP_METHOD(DateTimeImmutable, setTimestamp) +{ + zval *object, *new_object; + long timestamp; + + if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "Ol", &object, date_ce_date, ×tamp) == FAILURE) { + RETURN_FALSE; + } + + new_object = date_clone_immutable(object TSRMLS_CC); + php_date_timestamp_set(new_object, timestamp, return_value TSRMLS_CC); + + RETURN_ZVAL(new_object, 0, 1); +} +/* }}} */ + /* {{{ proto long date_timestamp_get(DateTime object) Gets the Unix timestamp. */ @@ -3861,6 +4192,7 @@ PHP_METHOD(DatePeriod, __construct) if (dpobj->end) { timelib_update_ts(dpobj->end, NULL); } + dpobj->start_ce = date_ce_date; } else { /* init */ intobj = (php_interval_obj *) zend_object_store_get_object(interval TSRMLS_CC); @@ -3876,6 +4208,7 @@ PHP_METHOD(DatePeriod, __construct) clone->tz_info = dateobj->time->tz_info; } dpobj->start = clone; + dpobj->start_ce = Z_OBJCE_P(start); /* interval */ dpobj->interval = timelib_rel_time_clone(intobj->diff); diff --git a/ext/date/php_date.h b/ext/date/php_date.h index f0b662b5d..3af3fa42e 100644 --- a/ext/date/php_date.h +++ b/ext/date/php_date.h @@ -51,7 +51,9 @@ PHP_METHOD(DateTime, __construct); PHP_METHOD(DateTime, __wakeup); PHP_METHOD(DateTime, __set_state); PHP_FUNCTION(date_create); +PHP_FUNCTION(date_create_immutable); PHP_FUNCTION(date_create_from_format); +PHP_FUNCTION(date_create_immutable_from_format); PHP_FUNCTION(date_parse); PHP_FUNCTION(date_parse_from_format); PHP_FUNCTION(date_get_last_errors); @@ -70,6 +72,17 @@ PHP_FUNCTION(date_isodate_set); PHP_FUNCTION(date_timestamp_set); PHP_FUNCTION(date_timestamp_get); +PHP_METHOD(DateTimeImmutable, __construct); +PHP_METHOD(DateTimeImmutable, __set_state); +PHP_METHOD(DateTimeImmutable, modify); +PHP_METHOD(DateTimeImmutable, add); +PHP_METHOD(DateTimeImmutable, sub); +PHP_METHOD(DateTimeImmutable, setTimezone); +PHP_METHOD(DateTimeImmutable, setTime); +PHP_METHOD(DateTimeImmutable, setDate); +PHP_METHOD(DateTimeImmutable, setISODate); +PHP_METHOD(DateTimeImmutable, setTimestamp); + PHP_METHOD(DateTimeZone, __construct); PHP_FUNCTION(timezone_open); PHP_FUNCTION(timezone_name_get); @@ -141,6 +154,7 @@ struct _php_interval_obj { struct _php_period_obj { zend_object std; timelib_time *start; + zend_class_entry *start_ce; timelib_time *current; timelib_time *end; timelib_rel_time *interval; diff --git a/ext/date/tests/date_period-immutable.phpt b/ext/date/tests/date_period-immutable.phpt new file mode 100644 index 000000000..0ec4b4a13 --- /dev/null +++ b/ext/date/tests/date_period-immutable.phpt @@ -0,0 +1,56 @@ +--TEST-- +DatePeriod +--FILE-- +<?php +date_default_timezone_set('UTC'); +$db1 = new DateTimeImmutable( '2008-01-01' ); +$db2 = new DateTime( '2008-01-01' ); +$de = new DateTime( '2008-03-31' ); +$di = DateInterval::createFromDateString( 'first day of next month' ); + +foreach ( new DatePeriod( $db1, $di, $de ) as $dt ) +{ + echo get_class( $dt ), "\n"; + echo $dt->format( "l Y-m-d\n" ); + echo $dt->modify( "3 tuesday" )->format( "l Y-m-d\n" ); + echo $dt->format( "l Y-m-d\n\n" ); +} + +foreach ( new DatePeriod( $db2, $di, $de ) as $dt ) +{ + echo get_class( $dt ), "\n"; + echo $dt->format( "l Y-m-d\n" ); + echo $dt->modify( "3 tuesday" )->format( "l Y-m-d\n" ); + echo $dt->format( "l Y-m-d\n\n" ); +} +?> +--EXPECT-- +DateTimeImmutable +Tuesday 2008-01-01 +Tuesday 2008-01-15 +Tuesday 2008-01-01 + +DateTimeImmutable +Friday 2008-02-01 +Tuesday 2008-02-19 +Friday 2008-02-01 + +DateTimeImmutable +Saturday 2008-03-01 +Tuesday 2008-03-18 +Saturday 2008-03-01 + +DateTime +Tuesday 2008-01-01 +Tuesday 2008-01-15 +Tuesday 2008-01-15 + +DateTime +Friday 2008-02-01 +Tuesday 2008-02-19 +Tuesday 2008-02-19 + +DateTime +Saturday 2008-03-01 +Tuesday 2008-03-18 +Tuesday 2008-03-18 diff --git a/ext/date/tests/date_time_immutable-inherited.phpt b/ext/date/tests/date_time_immutable-inherited.phpt new file mode 100644 index 000000000..ad8b7edb8 --- /dev/null +++ b/ext/date/tests/date_time_immutable-inherited.phpt @@ -0,0 +1,25 @@ +--TEST-- +Tests for DateTimeImmutable. +--INI-- +date.timezone=Europe/London +--FILE-- +<?php +$tz = new DateTimeZone("Asia/Tokyo"); +$current = "2012-12-27 16:24:08"; + +echo "\ngetTimezone():\n"; +$v = date_create_immutable($current); +$x = $v->getTimezone(); +var_dump($x->getName()); + +echo "\ngetTimestamp():\n"; +$v = date_create_immutable($current); +$x = $v->getTimestamp(); +var_dump($x); +?> +--EXPECT-- +getTimezone(): +string(13) "Europe/London" + +getTimestamp(): +int(1356625448) diff --git a/ext/date/tests/date_time_immutable.phpt b/ext/date/tests/date_time_immutable.phpt new file mode 100644 index 000000000..b4a576570 --- /dev/null +++ b/ext/date/tests/date_time_immutable.phpt @@ -0,0 +1,167 @@ +--TEST-- +Tests for DateTimeImmutable. +--INI-- +date.timezone=Europe/London +--FILE-- +<?php +$tz = new DateTimeZone("Asia/Tokyo"); +$current = "2012-12-27 16:24:08"; + +function dump($a, $b, $c) +{ + echo 'orig: ', $a->format('Y-m-d H:i:s e'), "\n"; + echo 'copy: ', $b->format('Y-m-d H:i:s e'), "\n"; + echo 'changed: ', $c->format('Y-m-d H:i:s e'), "\n"; +} + +echo "modify():\n"; +$v = date_create_immutable($current); +$z = $v; +$x = $z->modify("+2 days"); +dump($v, $z, $x); +$v = date_create($current); +$z = $v; +$x = $z->modify("+2 days"); +dump($v, $z, $x); + +echo "\nadd():\n"; +$v = date_create_immutable($current); +$z = $v; +$x = $z->add(new DateInterval("P2DT2S")); +dump($v, $z, $x); +$v = date_create($current); +$z = $v; +$x = $z->add(new DateInterval("P2DT2S")); +dump($v, $z, $x); + +echo "\nsub():\n"; +$v = date_create_immutable($current); +$z = $v; +$x = $z->sub(new DateInterval("P2DT2S")); +dump($v, $z, $x); +$v = date_create($current); +$z = $v; +$x = $z->sub(new DateInterval("P2DT2S")); +dump($v, $z, $x); + +echo "\nsetTimezone():\n"; +$v = date_create_immutable($current); +$z = $v; +$x = $z->setTimezone($tz); +dump($v, $z, $x); +$v = date_create($current); +$z = $v; +$x = $z->setTimezone($tz); +dump($v, $z, $x); +$v = new DateTimeImmutable($current); +$z = $v; +$x = $z->setTimezone($tz); +dump($v, $z, $x); + +echo "\nsetTime():\n"; +$v = date_create_immutable($current); +$z = $v; +$x = $z->setTime(5, 7, 19); +dump($v, $z, $x); +$v = date_create($current); +$z = $v; +$x = $z->setTime(5, 7, 19); +dump($v, $z, $x); + +echo "\nsetDate():\n"; +$v = date_create_immutable($current); +$z = $v; +$x = $z->setDate(5, 7, 19); +dump($v, $z, $x); +$v = date_create($current); +$z = $v; +$x = $z->setDate(5, 7, 19); +dump($v, $z, $x); + +echo "\nsetIsoDate():\n"; +$v = date_create_immutable($current); +$z = $v; +$x = $z->setIsoDate(2012, 2, 6); +dump($v, $z, $x); +$v = date_create($current); +$z = $v; +$x = $z->setIsoDate(2012, 2, 6); +dump($v, $z, $x); + +echo "\nsetTimestamp():\n"; +$v = date_create_immutable($current); +$z = $v; +$x = $z->setTimestamp(2012234222); +dump($v, $z, $x); +$v = date_create($current); +$z = $v; +$x = $z->setTimestamp(2012234222); +dump($v, $z, $x); +?> +--EXPECT-- +modify(): +orig: 2012-12-27 16:24:08 Europe/London +copy: 2012-12-27 16:24:08 Europe/London +changed: 2012-12-29 16:24:08 Europe/London +orig: 2012-12-29 16:24:08 Europe/London +copy: 2012-12-29 16:24:08 Europe/London +changed: 2012-12-29 16:24:08 Europe/London + +add(): +orig: 2012-12-27 16:24:08 Europe/London +copy: 2012-12-27 16:24:08 Europe/London +changed: 2012-12-29 16:24:10 Europe/London +orig: 2012-12-29 16:24:10 Europe/London +copy: 2012-12-29 16:24:10 Europe/London +changed: 2012-12-29 16:24:10 Europe/London + +sub(): +orig: 2012-12-27 16:24:08 Europe/London +copy: 2012-12-27 16:24:08 Europe/London +changed: 2012-12-25 16:24:06 Europe/London +orig: 2012-12-25 16:24:06 Europe/London +copy: 2012-12-25 16:24:06 Europe/London +changed: 2012-12-25 16:24:06 Europe/London + +setTimezone(): +orig: 2012-12-27 16:24:08 Europe/London +copy: 2012-12-27 16:24:08 Europe/London +changed: 2012-12-28 01:24:08 Asia/Tokyo +orig: 2012-12-28 01:24:08 Asia/Tokyo +copy: 2012-12-28 01:24:08 Asia/Tokyo +changed: 2012-12-28 01:24:08 Asia/Tokyo +orig: 2012-12-27 16:24:08 Europe/London +copy: 2012-12-27 16:24:08 Europe/London +changed: 2012-12-28 01:24:08 Asia/Tokyo + +setTime(): +orig: 2012-12-27 16:24:08 Europe/London +copy: 2012-12-27 16:24:08 Europe/London +changed: 2012-12-27 05:07:19 Europe/London +orig: 2012-12-27 05:07:19 Europe/London +copy: 2012-12-27 05:07:19 Europe/London +changed: 2012-12-27 05:07:19 Europe/London + +setDate(): +orig: 2012-12-27 16:24:08 Europe/London +copy: 2012-12-27 16:24:08 Europe/London +changed: 0005-07-19 16:24:08 Europe/London +orig: 0005-07-19 16:24:08 Europe/London +copy: 0005-07-19 16:24:08 Europe/London +changed: 0005-07-19 16:24:08 Europe/London + +setIsoDate(): +orig: 2012-12-27 16:24:08 Europe/London +copy: 2012-12-27 16:24:08 Europe/London +changed: 2012-01-14 16:24:08 Europe/London +orig: 2012-01-14 16:24:08 Europe/London +copy: 2012-01-14 16:24:08 Europe/London +changed: 2012-01-14 16:24:08 Europe/London + +setTimestamp(): +orig: 2012-12-27 16:24:08 Europe/London +copy: 2012-12-27 16:24:08 Europe/London +changed: 2033-10-06 18:57:02 Europe/London +orig: 2033-10-06 18:57:02 Europe/London +copy: 2033-10-06 18:57:02 Europe/London +changed: 2033-10-06 18:57:02 Europe/London diff --git a/ext/dba/dba_flatfile.c b/ext/dba/dba_flatfile.c index 082aa5cdb..34aa635a5 100644 --- a/ext/dba/dba_flatfile.c +++ b/ext/dba/dba_flatfile.c @@ -88,15 +88,16 @@ DBA_UPDATE_FUNC(flatfile) gval.dsize = vallen; switch(flatfile_store(dba, gkey, gval, mode==1 ? FLATFILE_INSERT : FLATFILE_REPLACE TSRMLS_CC)) { - case -1: - php_error_docref1(NULL TSRMLS_CC, key, E_WARNING, "Operation not possible"); - return FAILURE; - default: - case 0: - return SUCCESS; - case 1: - php_error_docref1(NULL TSRMLS_CC, key, E_WARNING, "Key already exists"); - return FAILURE; + case 0: + return SUCCESS; + case 1: + return FAILURE; + case -1: + php_error_docref1(NULL TSRMLS_CC, key, E_WARNING, "Operation not possible"); + return FAILURE; + default: + php_error_docref2(NULL TSRMLS_CC, key, val, E_WARNING, "Unknown return value"); + return FAILURE; } } diff --git a/ext/dba/dba_gdbm.c b/ext/dba/dba_gdbm.c index 7534568d3..47dd57649 100644 --- a/ext/dba/dba_gdbm.c +++ b/ext/dba/dba_gdbm.c @@ -104,11 +104,18 @@ DBA_UPDATE_FUNC(gdbm) gval.dptr = (char *) val; gval.dsize = vallen; - if(gdbm_store(dba->dbf, gkey, gval, - mode == 1 ? GDBM_INSERT : GDBM_REPLACE) == 0) - return SUCCESS; - php_error_docref2(NULL TSRMLS_CC, key, val, E_WARNING, "%s", gdbm_strerror(gdbm_errno)); - return FAILURE; + switch (gdbm_store(dba->dbf, gkey, gval, mode == 1 ? GDBM_INSERT : GDBM_REPLACE)) { + case 0: + return SUCCESS; + case 1: + return FAILURE; + case -1: + php_error_docref2(NULL TSRMLS_CC, key, val, E_WARNING, "%s", gdbm_strerror(gdbm_errno)); + return FAILURE; + default: + php_error_docref2(NULL TSRMLS_CC, key, val, E_WARNING, "Unknown return value"); + return FAILURE; + } } DBA_EXISTS_FUNC(gdbm) diff --git a/ext/dba/dba_inifile.c b/ext/dba/dba_inifile.c index e1359b65e..05ee95c0e 100644 --- a/ext/dba/dba_inifile.c +++ b/ext/dba/dba_inifile.c @@ -101,7 +101,6 @@ DBA_UPDATE_FUNC(inifile) case 0: return SUCCESS; case 1: - php_error_docref1(NULL TSRMLS_CC, key, E_WARNING, "Key already exists"); return FAILURE; } } diff --git a/ext/dba/dba_qdbm.c b/ext/dba/dba_qdbm.c index 485b1997e..eeece5701 100644 --- a/ext/dba/dba_qdbm.c +++ b/ext/dba/dba_qdbm.c @@ -96,13 +96,15 @@ DBA_FETCH_FUNC(qdbm) DBA_UPDATE_FUNC(qdbm) { QDBM_DATA; - int result; - result = dpput(dba->dbf, key, keylen, val, vallen, mode == 1 ? DP_DKEEP : DP_DOVER); - if (result) + if (dpput(dba->dbf, key, keylen, val, vallen, mode == 1 ? DP_DKEEP : DP_DOVER)) { return SUCCESS; + } + + if (dpecode != DP_EKEEP) { + php_error_docref2(NULL TSRMLS_CC, key, val, E_WARNING, "%s", dperrmsg(dpecode)); + } - php_error_docref2(NULL TSRMLS_CC, key, val, E_WARNING, "%s", dperrmsg(dpecode)); return FAILURE; } diff --git a/ext/dba/tests/dba_db1.phpt b/ext/dba/tests/dba_db1.phpt index a24600350..d0e530e02 100644 --- a/ext/dba/tests/dba_db1.phpt +++ b/ext/dba/tests/dba_db1.phpt @@ -18,6 +18,8 @@ database handler: db1 Content String 2 Content 2 replaced Read during write: not allowed +"key number 6" written +Failed to write "key number 6" 2nd time Content 2 replaced 2nd time The 6th value array(3) { @@ -33,6 +35,8 @@ array(3) { Content String 2 Content 2 replaced Read during write: not allowed +"key number 6" written +Failed to write "key number 6" 2nd time Content 2 replaced 2nd time The 6th value array(3) { diff --git a/ext/dba/tests/dba_db2.phpt b/ext/dba/tests/dba_db2.phpt index 89d8a926e..1cfbb3e34 100644 --- a/ext/dba/tests/dba_db2.phpt +++ b/ext/dba/tests/dba_db2.phpt @@ -18,6 +18,8 @@ database handler: db2 Content String 2 Content 2 replaced Read during write: not allowed +"key number 6" written +Failed to write "key number 6" 2nd time Content 2 replaced 2nd time The 6th value array(3) { @@ -33,6 +35,8 @@ array(3) { Content String 2 Content 2 replaced Read during write: not allowed +"key number 6" written +Failed to write "key number 6" 2nd time Content 2 replaced 2nd time The 6th value array(3) { diff --git a/ext/dba/tests/dba_db3.phpt b/ext/dba/tests/dba_db3.phpt index 257c88217..5de7e5a04 100644 --- a/ext/dba/tests/dba_db3.phpt +++ b/ext/dba/tests/dba_db3.phpt @@ -18,6 +18,8 @@ database handler: db3 Content String 2 Content 2 replaced Read during write: not allowed +"key number 6" written +Failed to write "key number 6" 2nd time Content 2 replaced 2nd time The 6th value array(3) { @@ -33,6 +35,8 @@ array(3) { Content String 2 Content 2 replaced Read during write: not allowed +"key number 6" written +Failed to write "key number 6" 2nd time Content 2 replaced 2nd time The 6th value array(3) { diff --git a/ext/dba/tests/dba_db4_000.phpt b/ext/dba/tests/dba_db4_000.phpt index bbbc52c9f..17db4bb62 100644 --- a/ext/dba/tests/dba_db4_000.phpt +++ b/ext/dba/tests/dba_db4_000.phpt @@ -22,6 +22,8 @@ database handler: db4 Content String 2 Content 2 replaced Read during write: not allowed +"key number 6" written +Failed to write "key number 6" 2nd time Content 2 replaced 2nd time The 6th value array(3) { @@ -37,6 +39,8 @@ array(3) { Content String 2 Content 2 replaced Read during write: not allowed +"key number 6" written +Failed to write "key number 6" 2nd time Content 2 replaced 2nd time The 6th value array(3) { diff --git a/ext/dba/tests/dba_dbm.phpt b/ext/dba/tests/dba_dbm.phpt index dd1fe1e31..8bea8463d 100644 --- a/ext/dba/tests/dba_dbm.phpt +++ b/ext/dba/tests/dba_dbm.phpt @@ -18,6 +18,8 @@ database handler: dbm Content String 2 Content 2 replaced Read during write: not allowed +"key number 6" written +Failed to write "key number 6" 2nd time Content 2 replaced 2nd time The 6th value array(3) { @@ -33,6 +35,8 @@ array(3) { Content String 2 Content 2 replaced Read during write: not allowed +"key number 6" written +Failed to write "key number 6" 2nd time Content 2 replaced 2nd time The 6th value array(3) { diff --git a/ext/dba/tests/dba_flatfile.phpt b/ext/dba/tests/dba_flatfile.phpt index 8e1ca6a93..ac7f86ebd 100644 --- a/ext/dba/tests/dba_flatfile.phpt +++ b/ext/dba/tests/dba_flatfile.phpt @@ -22,6 +22,8 @@ database handler: flatfile Content String 2 Content 2 replaced Read during write: not allowed +"key number 6" written +Failed to write "key number 6" 2nd time Content 2 replaced 2nd time The 6th value array(3) { @@ -37,6 +39,8 @@ array(3) { Content String 2 Content 2 replaced Read during write: not allowed +"key number 6" written +Failed to write "key number 6" 2nd time Content 2 replaced 2nd time The 6th value array(3) { diff --git a/ext/dba/tests/dba_gdbm.phpt b/ext/dba/tests/dba_gdbm.phpt index 33d7d2061..e68e8b740 100644 --- a/ext/dba/tests/dba_gdbm.phpt +++ b/ext/dba/tests/dba_gdbm.phpt @@ -21,6 +21,8 @@ database handler: gdbm Content String 2 Content 2 replaced Read during write:%sallowed +"key number 6" written +Failed to write "key number 6" 2nd time Content 2 replaced 2nd time The 6th value array(3) { diff --git a/ext/dba/tests/dba_handler.inc b/ext/dba/tests/dba_handler.inc index 1c3f5127e..a950e903a 100644 --- a/ext/dba/tests/dba_handler.inc +++ b/ext/dba/tests/dba_handler.inc @@ -46,8 +46,16 @@ do { echo "Read during write: allowed\n"; } if ($db_writer!==FALSE) { - dba_insert("key number 6", "The 6th value", $db_writer); - @dba_insert("key number 6", "The 6th value inserted again would be an error", $db_writer); + if (dba_insert("key number 6", "The 6th value", $db_writer)) { + echo '"key number 6" written' . "\n"; + } else { + echo 'Failed to write "key number 6"' . "\n"; + } + if (dba_insert("key number 6", "The 6th value inserted again would be an error", $db_writer)) { + echo '"key number 6" written 2nd time' . "\n"; + } else { + echo 'Failed to write "key number 6" 2nd time' . "\n"; + } dba_replace("key2", "Content 2 replaced 2nd time", $db_writer); dba_delete("key4", $db_writer); echo dba_fetch("key2", $db_writer)."\n"; diff --git a/ext/dba/tests/dba_inifile.phpt b/ext/dba/tests/dba_inifile.phpt index 81ab73879..5975d25f4 100644 --- a/ext/dba/tests/dba_inifile.phpt +++ b/ext/dba/tests/dba_inifile.phpt @@ -18,6 +18,8 @@ database handler: inifile Content String 2 Content 2 replaced Read during write: not allowed +"key number 6" written +Failed to write "key number 6" 2nd time Content 2 replaced 2nd time The 6th value array(3) { @@ -33,6 +35,8 @@ array(3) { Content String 2 Content 2 replaced Read during write: not allowed +"key number 6" written +Failed to write "key number 6" 2nd time Content 2 replaced 2nd time The 6th value array(3) { diff --git a/ext/dba/tests/dba_ndbm.phpt b/ext/dba/tests/dba_ndbm.phpt index b0f5542de..193db6f94 100644 --- a/ext/dba/tests/dba_ndbm.phpt +++ b/ext/dba/tests/dba_ndbm.phpt @@ -18,6 +18,8 @@ database handler: ndbm Content String 2 Content 2 replaced Read during write: not allowed +"key number 6" written +Failed to write "key number 6" 2nd time Content 2 replaced 2nd time The 6th value array(3) { @@ -33,6 +35,8 @@ array(3) { Content String 2 Content 2 replaced Read during write: not allowed +"key number 6" written +Failed to write "key number 6" 2nd time Content 2 replaced 2nd time The 6th value array(3) { diff --git a/ext/dba/tests/dba_qdbm.phpt b/ext/dba/tests/dba_qdbm.phpt index ef216d925..e2205baa2 100644 --- a/ext/dba/tests/dba_qdbm.phpt +++ b/ext/dba/tests/dba_qdbm.phpt @@ -19,6 +19,8 @@ database handler: qdbm Content String 2 Content 2 replaced Read during write:%sallowed +"key number 6" written +Failed to write "key number 6" 2nd time Content 2 replaced 2nd time The 6th value array(3) { diff --git a/ext/dba/tests/dba_tcadb.phpt b/ext/dba/tests/dba_tcadb.phpt index 52dd4de33..28b6dd8f0 100644 --- a/ext/dba/tests/dba_tcadb.phpt +++ b/ext/dba/tests/dba_tcadb.phpt @@ -22,6 +22,8 @@ database handler: tcadb Content String 2 Content 2 replaced Read during write: not allowed +"key number 6" written +Failed to write "key number 6" 2nd time Content 2 replaced 2nd time The 6th value array(3) { @@ -37,6 +39,8 @@ array(3) { Content String 2 Content 2 replaced Read during write: not allowed +"key number 6" written +Failed to write "key number 6" 2nd time Content 2 replaced 2nd time The 6th value array(3) { diff --git a/ext/mysqli/mysqli.c b/ext/mysqli/mysqli.c index d2737bf4a..22e75cd99 100644 --- a/ext/mysqli/mysqli.c +++ b/ext/mysqli/mysqli.c @@ -842,6 +842,9 @@ PHP_MINIT_FUNCTION(mysqli) REGISTER_LONG_CONSTANT("MYSQLI_REFRESH_BACKUP_LOG", REFRESH_BACKUP_LOG, CONST_CS | CONST_PERSISTENT); #endif +#if MYSQL_VERSION_ID >= 50611 || defined(MYSQLI_USE_MYSQLND) + REGISTER_LONG_CONSTANT("MYSQLI_OPT_CAN_HANDLE_EXPIRED_PASSWORDS", MYSQL_OPT_CAN_HANDLE_EXPIRED_PASSWORDS, CONST_CS | CONST_PERSISTENT); +#endif #ifdef MYSQLI_USE_MYSQLND mysqlnd_reverse_api_register_api(&mysqli_reverse_api TSRMLS_CC); diff --git a/ext/mysqli/mysqli_api.c b/ext/mysqli/mysqli_api.c index f062215e9..70a8cc630 100644 --- a/ext/mysqli/mysqli_api.c +++ b/ext/mysqli/mysqli_api.c @@ -1629,6 +1629,9 @@ static int mysqli_options_get_option_zval_type(int option) #ifdef MYSQL_OPT_SSL_VERIFY_SERVER_CERT REGISTER_LONG_CONSTANT("MYSQLI_OPT_SSL_VERIFY_SERVER_CERT", MYSQL_OPT_SSL_VERIFY_SERVER_CERT, CONST_CS | CONST_PERSISTENT); #endif /* MySQL 5.1.1., mysqlnd @ PHP 5.3.3 */ +#if MYSQL_VERSION_ID >= 50611 || defined(MYSQLI_USE_MYSQLND) + case MYSQL_OPT_CAN_HANDLE_EXPIRED_PASSWORDS: +#endif return IS_LONG; #ifdef MYSQL_SHARED_MEMORY_BASE_NAME diff --git a/ext/mysqli/tests/mysqli_constants.phpt b/ext/mysqli/tests/mysqli_constants.phpt index 0f8718721..7c0f6a95e 100644 --- a/ext/mysqli/tests/mysqli_constants.phpt +++ b/ext/mysqli/tests/mysqli_constants.phpt @@ -182,6 +182,10 @@ require_once('skipifconnectfailure.inc'); } } + if (($IS_MYSQLND && version_compare(PHP_VERSION, ' 5.4.12-dev', '>=')) || (!$IS_MYSQLND && ($version > 50610))) { + /* could be that MySQL/libmysql 5.6.9 had the flag already but it was no stable release */ + $expected_constants["MYSQLI_OPT_CAN_HANDLE_EXPIRED_PASSWORDS"] = true; + } $unexpected_constants = array(); diff --git a/ext/mysqli/tests/mysqli_expire_password.phpt b/ext/mysqli/tests/mysqli_expire_password.phpt new file mode 100644 index 000000000..ce89a2167 --- /dev/null +++ b/ext/mysqli/tests/mysqli_expire_password.phpt @@ -0,0 +1,145 @@ +--TEST-- +MySQL 5.6 EXPIRE PASSWORD protocol change +--SKIPIF-- +<?php +require_once('skipif.inc'); +require_once('skipifemb.inc'); +require_once('connect.inc'); + +if ($IS_MYSQLND && !version_compare(PHP_VERSION, '5.4.12-dev', ">=")) { + die("SKIP Available in mysqlnd as of PHP 5.4.12-dev"); +} + +if (!$link = my_mysqli_connect($host, $user, $passwd, $db, $port, $socket)) { + die(sprintf("SKIP Cannot connect to the server using host=%s, user=%s, passwd=***, dbname=%s, port=%s, socket=%s\n", + $host, $user, $db, $port, $socket)); +} + +if ($link->server_version < 50610) + die(sprintf("SKIP Needs MySQL 5.6.10 or newer, found MySQL %s\n", $link->server_info)); + +if (!$IS_MYSQLND && (mysqli_get_client_version() < 50610)) { + die(sprintf("SKIP Needs libmysql 5.6.10 or newer, found %s\n", mysqli_get_client_version())); +} + +mysqli_query($link, 'DROP USER expiretest'); +mysqli_query($link, 'DROP USER expiretest@localhost'); + +if (!mysqli_query($link, 'CREATE USER expiretest@"%"') || + !mysqli_query($link, 'CREATE USER expiretest@"localhost"')) { + printf("skip Cannot create second DB user [%d] %s", mysqli_errno($link), mysqli_error($link)); + mysqli_close($link); + die("skip CREATE USER failed"); +} + +if (!mysqli_query($link, 'ALTER USER expiretest@"%" PASSWORD EXPIRE') || + !mysqli_query($link, 'ALTER USER expiretest@"localhost" PASSWORD EXPIRE')) { + printf("skip Cannot modify second DB user [%d] %s", mysqli_errno($link), mysqli_error($link)); + mysqli_close($link); + die("skip ALTER USER failed"); +} + +if (!$link->query("DROP TABLE IF EXISTS test") || + !$link->query("CREATE TABLE test (id INT)") || !$link->query("INSERT INTO test(id) VALUES (1)")) + die(sprintf("SKIP [%d] %s\n", $link->errno, $link->error)); + + + +if (!mysqli_query($link, sprintf("GRANT SELECT ON TABLE %s.test TO expiretest@'%%'", $db)) || + !mysqli_query($link, sprintf("GRANT SELECT ON TABLE %s.test TO expiretest@'localhost'", $db))) { + printf("skip Cannot grant SELECT to user [%d] %s", mysqli_errno($link), mysqli_error($link)); + mysqli_close($link); + die("skip GRANT failed"); +} +?> +--FILE-- +<?php + require_once('connect.inc'); + require_once('table.inc'); + + /* default */ + if (!$link = my_mysqli_connect($host, 'expiretest', "", $db, $port, $socket)) { + printf("[001] Cannot connect [%d] %s\n", + mysqli_connect_errno(), mysqli_connect_error()); + } else { + $link->query("SELECT id FROM test WHERE id = 1"); + printf("[002] Connect should fail, [%d] %s\n", $link->errno, $link->error); + } + + /* explicitly requesting default */ + $link = mysqli_init(); + $link->options(MYSQLI_OPT_CAN_HANDLE_EXPIRED_PASSWORDS, 0); + if (!my_mysqli_real_connect($link, $host, 'expiretest', "", $db, $port, $socket)) { + printf("[003] Cannot connect [%d] %s\n", + mysqli_connect_errno(), mysqli_connect_error()); + } else { + $link->query("SELECT id FROM test WHERE id = 1"); + printf("[004] Connect should fail, [%d] %s\n", $link->errno, $link->error); + } + + /* allow connect */ + $link = mysqli_init(); + $link->options(MYSQLI_OPT_CAN_HANDLE_EXPIRED_PASSWORDS, 1); + if (!my_mysqli_real_connect($link, $host, 'expiretest', "", $db, $port, $socket)) { + printf("[005] Cannot connect [%d] %s\n", + mysqli_connect_errno(), mysqli_connect_error()); + } else { + $link->query("SELECT id FROM test WHERE id = 1"); + printf("[006] Connect allowed, query fail, [%d] %s\n", $link->errno, $link->error); + $link->close(); + } + + /* allow connect, fix pw */ + $link = mysqli_init(); + $link->options(MYSQLI_OPT_CAN_HANDLE_EXPIRED_PASSWORDS, 1); + if (!my_mysqli_real_connect($link, $host, 'expiretest', "", $db, $port, $socket)) { + printf("[007] Cannot connect [%d] %s\n", + mysqli_connect_errno(), mysqli_connect_error()); + } else { + $link->query("SET PASSWORD=PASSWORD('expiretest')"); + printf("[008] Connect allowed, pw set, [%d] %s\n", $link->errno, $link->error); + if ($res = $link->query("SELECT id FROM test WHERE id = 1")) + var_dump($res->fetch_assoc()); + $link->close(); + } + + + /* check login */ + if (!$link = my_mysqli_connect($host, 'expiretest', "expiretest", $db, $port, $socket)) { + printf("[001] Cannot connect [%d] %s\n", + mysqli_connect_errno(), mysqli_connect_error()); + } else { + $link->query("SELECT id FROM test WHERE id = 1"); + if ($res = $link->query("SELECT id FROM test WHERE id = 1")) + var_dump($res->fetch_assoc()); + $link->close(); + } + + + + print "done!"; +?> +--CLEAN-- +<?php + require_once("clean_table.inc"); + mysqli_query($link, 'DROP USER expiretest'); + mysqli_query($link, 'DROP USER expiretest@localhost'); +?> +--EXPECTF-- + +Warning: mysqli_real_connect(): (HY000/1820): %s in %s on line %d +[001] Cannot connect [1820] %s + +Warning: mysqli_real_connect(): (HY000/1820): %s in %s on line %d +[003] Cannot connect [1820] %s +[006] Connect allowed, query fail, [1820] %s +[008] Connect allowed, pw set, [0%A +array(1) { + ["id"]=> + string(1) "1" +} +array(1) { + ["id"]=> + string(1) "1" +} +done! diff --git a/ext/mysqlnd/mysqlnd.c b/ext/mysqlnd/mysqlnd.c index 653561eae..96c420dee 100644 --- a/ext/mysqlnd/mysqlnd.c +++ b/ext/mysqlnd/mysqlnd.c @@ -95,6 +95,11 @@ MYSQLND_METHOD(mysqlnd_conn_data, free_options)(MYSQLND_CONN_DATA * conn TSRMLS_ mnd_pefree(conn->options->cfg_section, pers); conn->options->cfg_section = NULL; } + if (conn->options->connect_attr) { + zend_hash_destroy(conn->options->connect_attr); + mnd_pefree(conn->options->connect_attr, pers); + conn->options->connect_attr = NULL; + } } /* }}} */ @@ -708,6 +713,8 @@ MYSQLND_METHOD(mysqlnd_conn_data, get_updated_connect_flags)(MYSQLND_CONN_DATA * /* we allow load data local infile by default */ mysql_flags |= MYSQLND_CAPABILITIES; + mysql_flags |= conn->options->flags; /* use the flags from set_client_option() */ + if (PG(open_basedir) && strlen(PG(open_basedir))) { mysql_flags ^= CLIENT_LOCAL_FILES; } @@ -795,13 +802,14 @@ MYSQLND_METHOD(mysqlnd_conn_data, connect_handshake)(MYSQLND_CONN_DATA * conn, goto err; } + conn->client_flag = mysql_flags; + conn->server_capabilities = greet_packet->server_capabilities; + if (FAIL == mysqlnd_connect_run_authentication(conn, user, passwd, db, db_len, (size_t) passwd_len, greet_packet, conn->options, mysql_flags TSRMLS_CC)) { goto err; } - conn->client_flag = mysql_flags; - conn->server_capabilities = greet_packet->server_capabilities; conn->upsert_status->warning_count = 0; conn->upsert_status->server_status = greet_packet->server_status; conn->upsert_status->affected_rows = 0; @@ -809,6 +817,8 @@ MYSQLND_METHOD(mysqlnd_conn_data, connect_handshake)(MYSQLND_CONN_DATA * conn, PACKET_FREE(greet_packet); DBG_RETURN(PASS); err: + conn->client_flag = 0; + conn->server_capabilities = 0; PACKET_FREE(greet_packet); DBG_RETURN(FAIL); } @@ -1084,6 +1094,7 @@ MYSQLND_METHOD(mysqlnd_conn, connect)(MYSQLND * conn_handle, DBG_ENTER("mysqlnd_conn::connect"); if (PASS == conn->m->local_tx_start(conn, this_func TSRMLS_CC)) { + mysqlnd_options4(conn_handle, MYSQL_OPT_CONNECT_ATTR_ADD, "_client_name", "mysqlnd"); ret = conn->m->connect(conn, host, user, passwd, passwd_len, db, db_len, port, socket_or_pipe, mysql_flags TSRMLS_CC); conn->m->local_tx_end(conn, this_func, FAIL TSRMLS_CC); @@ -1092,6 +1103,7 @@ MYSQLND_METHOD(mysqlnd_conn, connect)(MYSQLND * conn_handle, } /* }}} */ + /* {{{ mysqlnd_connect */ PHPAPI MYSQLND * mysqlnd_connect(MYSQLND * conn_handle, const char * host, const char * user, @@ -2282,7 +2294,7 @@ MYSQLND_METHOD(mysqlnd_conn_data, set_client_option)(MYSQLND_CONN_DATA * const c break; #endif case MYSQL_OPT_LOCAL_INFILE: - if (!value || (*(unsigned int*) value) ? 1 : 0) { + if (value && (*(unsigned int*) value) ? 1 : 0) { conn->options->flags |= CLIENT_LOCAL_FILES; } else { conn->options->flags &= ~CLIENT_LOCAL_FILES; @@ -2366,6 +2378,26 @@ MYSQLND_METHOD(mysqlnd_conn_data, set_client_option)(MYSQLND_CONN_DATA * const c DBG_INF_FMT("auth_protocol=%s", conn->options->auth_protocol); break; } + case MYSQL_OPT_CAN_HANDLE_EXPIRED_PASSWORDS: + if (value && (*(unsigned int*) value) ? 1 : 0) { + conn->options->flags |= CLIENT_CAN_HANDLE_EXPIRED_PASSWORDS; + } else { + conn->options->flags &= ~CLIENT_CAN_HANDLE_EXPIRED_PASSWORDS; + } + break; + case MYSQL_OPT_CONNECT_ATTR_RESET: + if (conn->options->connect_attr) { + DBG_INF_FMT("Before reset %d attribute(s)", zend_hash_num_elements(conn->options->connect_attr)); + zend_hash_clean(conn->options->connect_attr); + } + break; + case MYSQL_OPT_CONNECT_ATTR_DELETE: + if (conn->options->connect_attr && value) { + DBG_INF_FMT("Before delete %d attribute(s)", zend_hash_num_elements(conn->options->connect_attr)); + zend_hash_del(conn->options->connect_attr, value, strlen(value)); + DBG_INF_FMT("%d left", zend_hash_num_elements(conn->options->connect_attr)); + } + break; #ifdef WHEN_SUPPORTED_BY_MYSQLI case MYSQL_SHARED_MEMORY_BASE_NAME: case MYSQL_OPT_USE_RESULT: @@ -2386,6 +2418,83 @@ end: /* }}} */ +/* {{{ connect_attr_item_edtor */ +static void +connect_attr_item_edtor(void * pDest) +{ +#ifdef ZTS + TSRMLS_FETCH(); +#endif + DBG_ENTER("connect_attr_item_edtor"); + mnd_efree(*(char **) pDest); + DBG_VOID_RETURN; +} +/* }}} */ + + +/* {{{ connect_attr_item_pdtor */ +static void +connect_attr_item_pdtor(void * pDest) +{ +#ifdef ZTS + TSRMLS_FETCH(); +#endif + DBG_ENTER("connect_attr_item_pdtor"); + mnd_pefree(*(char **) pDest, 1); + DBG_VOID_RETURN; +} +/* }}} */ + + +/* {{{ mysqlnd_conn_data::set_client_option_2d */ +static enum_func_status +MYSQLND_METHOD(mysqlnd_conn_data, set_client_option_2d)(MYSQLND_CONN_DATA * const conn, + enum mysqlnd_option option, + const char * const key, + const char * const value + TSRMLS_DC) +{ + size_t this_func = STRUCT_OFFSET(struct st_mysqlnd_conn_data_methods, set_client_option_2d); + enum_func_status ret = PASS; + DBG_ENTER("mysqlnd_conn_data::set_client_option_2d"); + DBG_INF_FMT("conn=%llu option=%u", conn->thread_id, option); + + if (PASS != conn->m->local_tx_start(conn, this_func TSRMLS_CC)) { + goto end; + } + switch (option) { + case MYSQL_OPT_CONNECT_ATTR_ADD: + if (!conn->options->connect_attr) { + DBG_INF("Initializing connect_attr hash"); + conn->options->connect_attr = mnd_pemalloc(sizeof(HashTable), conn->persistent); + if (!conn->options->connect_attr) { + goto oom; + } + zend_hash_init(conn->options->connect_attr, 0, NULL, conn->persistent? connect_attr_item_pdtor:connect_attr_item_edtor, conn->persistent); + } + DBG_INF_FMT("Adding [%s][%s]", key, value); + { + const char * copyv = mnd_pestrdup(value, conn->persistent); + if (!copyv) { + goto oom; + } + zend_hash_update(conn->options->connect_attr, key, strlen(key), ©v, sizeof(char *), NULL); + } + break; + default: + ret = FAIL; + } + conn->m->local_tx_end(conn, this_func, ret TSRMLS_CC); + DBG_RETURN(ret); +oom: + SET_OOM_ERROR(*conn->error_info); + conn->m->local_tx_end(conn, this_func, FAIL TSRMLS_CC); +end: + DBG_RETURN(FAIL); +} +/* }}} */ + + /* {{{ mysqlnd_conn_data::use_result */ static MYSQLND_RES * MYSQLND_METHOD(mysqlnd_conn_data, use_result)(MYSQLND_CONN_DATA * const conn TSRMLS_DC) @@ -2653,7 +2762,9 @@ MYSQLND_CLASS_METHODS_START(mysqlnd_conn_data) MYSQLND_METHOD(mysqlnd_conn_data, get_updated_connect_flags), MYSQLND_METHOD(mysqlnd_conn_data, connect_handshake), MYSQLND_METHOD(mysqlnd_conn_data, simple_command_send_request), - MYSQLND_METHOD(mysqlnd_conn_data, fetch_auth_plugin_by_name) + MYSQLND_METHOD(mysqlnd_conn_data, fetch_auth_plugin_by_name), + + MYSQLND_METHOD(mysqlnd_conn_data, set_client_option_2d) MYSQLND_CLASS_METHODS_END; diff --git a/ext/mysqlnd/mysqlnd.h b/ext/mysqlnd/mysqlnd.h index 7e02126f3..bf0f70b40 100644 --- a/ext/mysqlnd/mysqlnd.h +++ b/ext/mysqlnd/mysqlnd.h @@ -207,6 +207,7 @@ PHPAPI void mysqlnd_set_local_infile_handler(MYSQLND_CONN_DATA * const conn, con #define mysqlnd_set_character_set(conn, cs) ((conn)->data)->m->set_charset((conn)->data, (cs) TSRMLS_CC) #define mysqlnd_stat(conn, msg, msg_len) ((conn)->data)->m->get_server_statistics(((conn)->data), (msg), (msg_len) TSRMLS_CC) #define mysqlnd_options(conn, opt, value) ((conn)->data)->m->set_client_option((conn)->data, (opt), (value) TSRMLS_CC) +#define mysqlnd_options4(conn, opt, k, v) ((conn)->data)->m->set_client_option_2d((conn)->data, (opt), (k), (v) TSRMLS_CC) #define mysqlnd_set_server_option(conn, op) ((conn)->data)->m->set_server_option((conn)->data, (op) TSRMLS_CC) /* Escaping */ diff --git a/ext/mysqlnd/mysqlnd_auth.c b/ext/mysqlnd/mysqlnd_auth.c index a3b4f36a2..8611d9986 100644 --- a/ext/mysqlnd/mysqlnd_auth.c +++ b/ext/mysqlnd/mysqlnd_auth.c @@ -28,7 +28,6 @@ #include "mysqlnd_charset.h" #include "mysqlnd_debug.h" - /* {{{ mysqlnd_auth_handshake */ enum_func_status mysqlnd_auth_handshake(MYSQLND_CONN_DATA * conn, @@ -99,6 +98,10 @@ mysqlnd_auth_handshake(MYSQLND_CONN_DATA * conn, auth_packet->auth_data = auth_plugin_data; auth_packet->auth_data_len = auth_plugin_data_len; auth_packet->auth_plugin_name = auth_protocol; + + if (conn->server_capabilities & CLIENT_CONNECT_ATTRS) { + auth_packet->connect_attr = conn->options->connect_attr; + } if (!PACKET_WRITE(auth_packet, conn)) { goto end; diff --git a/ext/mysqlnd/mysqlnd_enum_n_def.h b/ext/mysqlnd/mysqlnd_enum_n_def.h index 7dd4d0fb8..cf5b02728 100644 --- a/ext/mysqlnd/mysqlnd_enum_n_def.h +++ b/ext/mysqlnd/mysqlnd_enum_n_def.h @@ -168,7 +168,12 @@ typedef enum mysqlnd_option MYSQL_OPT_SSL_VERIFY_SERVER_CERT, MYSQL_PLUGIN_DIR, MYSQL_DEFAULT_AUTH, + MYSQL_OPT_CONNECT_ATTR_RESET, + MYSQL_OPT_CONNECT_ATTR_ADD, + MYSQL_OPT_CONNECT_ATTR_DELETE, MYSQL_SERVER_PUBLIC_KEY, + MYSQL_ENABLE_CLEARTEXT_PLUGIN, + MYSQL_OPT_CAN_HANDLE_EXPIRED_PASSWORDS, MYSQLND_DEPRECATED_ENUM1 = 200, #ifdef MYSQLND_STRING_TO_INT_CONVERSION MYSQLND_OPT_INT_AND_FLOAT_NATIVE = 201, diff --git a/ext/mysqlnd/mysqlnd_libmysql_compat.h b/ext/mysqlnd/mysqlnd_libmysql_compat.h index c967fb3a9..e3ab9eefa 100644 --- a/ext/mysqlnd/mysqlnd_libmysql_compat.h +++ b/ext/mysqlnd/mysqlnd_libmysql_compat.h @@ -106,7 +106,8 @@ #define mysql_stmt_more_results(s) mysqlnd_stmt_more_results((s)) #define mysql_thread_safe() mysqlnd_thread_safe() #define mysql_info(r) mysqlnd_info((r)) -#define mysql_options(r,a,b) mysqlnd_options((r), (a), (b)) +#define mysql_options(c,a,v) mysqlnd_options((c), (a), (v)) +#define mysql_options4(c,a,k,v) mysqlnd_options4((c), (a), (k), (v)) #define mysql_stmt_init(r) mysqlnd_stmt_init((r)) #define mysql_free_result(r) mysqlnd_free_result((r), FALSE) #define mysql_store_result(r) mysqlnd_store_result((r)) diff --git a/ext/mysqlnd/mysqlnd_structs.h b/ext/mysqlnd/mysqlnd_structs.h index 6302c81c6..ecb1d8921 100644 --- a/ext/mysqlnd/mysqlnd_structs.h +++ b/ext/mysqlnd/mysqlnd_structs.h @@ -172,7 +172,7 @@ typedef struct st_mysqlnd_options The ABI will be broken and the methods structure will be somewhere else in the memory which can crash external code. Feel free to reuse these. */ - char * unused2; + HashTable * connect_attr; char * unused3; char * unused4; char * unused5; @@ -489,6 +489,8 @@ typedef enum_func_status (*func_mysqlnd_conn_data__connect_handshake)(MYSQLND_CO typedef enum_func_status (*func_mysqlnd_conn_data__simple_command_send_request)(MYSQLND_CONN_DATA * conn, enum php_mysqlnd_server_command command, const zend_uchar * const arg, size_t arg_len, zend_bool silent, zend_bool ignore_upsert_status TSRMLS_DC); typedef struct st_mysqlnd_authentication_plugin * (*func_mysqlnd_conn_data__fetch_auth_plugin_by_name)(const char * const requested_protocol TSRMLS_DC); +typedef enum_func_status (*func_mysqlnd_conn_data__set_client_option_2d)(MYSQLND_CONN_DATA * const conn, enum mysqlnd_option option, const char * const key, const char * const value TSRMLS_DC); + struct st_mysqlnd_conn_data_methods { func_mysqlnd_conn_data__init init; @@ -573,6 +575,8 @@ struct st_mysqlnd_conn_data_methods func_mysqlnd_conn_data__connect_handshake connect_handshake; func_mysqlnd_conn_data__simple_command_send_request simple_command_send_request; func_mysqlnd_conn_data__fetch_auth_plugin_by_name fetch_auth_plugin_by_name; + + func_mysqlnd_conn_data__set_client_option_2d set_client_option_2d; }; diff --git a/ext/mysqlnd/mysqlnd_wireprotocol.c b/ext/mysqlnd/mysqlnd_wireprotocol.c index 7c3bf1395..669970789 100644 --- a/ext/mysqlnd/mysqlnd_wireprotocol.c +++ b/ext/mysqlnd/mysqlnd_wireprotocol.c @@ -212,6 +212,24 @@ php_mysqlnd_net_store_length(zend_uchar *packet, uint64_t length) /* }}} */ +/* {{{ php_mysqlnd_net_store_length_size */ +size_t +php_mysqlnd_net_store_length_size(uint64_t length) +{ + if (length < (uint64_t) L64(251)) { + return 1; + } + if (length < (uint64_t) L64(65536)) { + return 3; + } + if (length < (uint64_t) L64(16777216)) { + return 4; + } + return 8; +} +/* }}} */ + + /* {{{ php_mysqlnd_read_error_from_line */ static enum_func_status php_mysqlnd_read_error_from_line(zend_uchar *buf, size_t buf_len, @@ -459,7 +477,7 @@ void php_mysqlnd_greet_free_mem(void * _packet, zend_bool stack_allocation TSRML /* }}} */ -#define AUTH_WRITE_BUFFER_LEN (MYSQLND_HEADER_SIZE + MYSQLND_MAX_ALLOWED_USER_LEN + SCRAMBLE_LENGTH + MYSQLND_MAX_ALLOWED_DB_LEN + 1 + 1024) +#define AUTH_WRITE_BUFFER_LEN (MYSQLND_HEADER_SIZE + MYSQLND_MAX_ALLOWED_USER_LEN + SCRAMBLE_LENGTH + MYSQLND_MAX_ALLOWED_DB_LEN + 1 + 4096) /* {{{ php_mysqlnd_auth_write */ static @@ -540,6 +558,52 @@ size_t php_mysqlnd_auth_write(void * _packet, MYSQLND_CONN_DATA * conn TSRMLS_DC p+= len; *p++= '\0'; } + + if (packet->connect_attr && zend_hash_num_elements(packet->connect_attr)) { + HashPosition pos_value; + const char ** entry_value; + size_t ca_payload_len = 0; + zend_hash_internal_pointer_reset_ex(packet->connect_attr, &pos_value); + while (SUCCESS == zend_hash_get_current_data_ex(packet->connect_attr, (void **)&entry_value, &pos_value)) { + char *s_key; + unsigned int s_len; + unsigned long num_key; + size_t value_len = strlen(*entry_value); + + if (HASH_KEY_IS_STRING == zend_hash_get_current_key_ex(packet->connect_attr, &s_key, &s_len, &num_key, 0, &pos_value)) { + ca_payload_len += php_mysqlnd_net_store_length_size(s_len); + ca_payload_len += s_len; + ca_payload_len += php_mysqlnd_net_store_length_size(value_len); + ca_payload_len += value_len; + } + zend_hash_move_forward_ex(conn->options->connect_attr, &pos_value); + } + + if ((sizeof(buffer) - (p - buffer)) >= (ca_payload_len + php_mysqlnd_net_store_length_size(ca_payload_len))) { + p = php_mysqlnd_net_store_length(p, ca_payload_len); + + zend_hash_internal_pointer_reset_ex(packet->connect_attr, &pos_value); + while (SUCCESS == zend_hash_get_current_data_ex(packet->connect_attr, (void **)&entry_value, &pos_value)) { + char *s_key; + unsigned int s_len; + unsigned long num_key; + size_t value_len = strlen(*entry_value); + if (HASH_KEY_IS_STRING == zend_hash_get_current_key_ex(packet->connect_attr, &s_key, &s_len, &num_key, 0, &pos_value)) { + /* copy key */ + p = php_mysqlnd_net_store_length(p, s_len); + memcpy(p, s_key, s_len); + p+= s_len; + /* copy value */ + p = php_mysqlnd_net_store_length(p, value_len); + memcpy(p, *entry_value, value_len); + p+= value_len; + } + zend_hash_move_forward_ex(conn->options->connect_attr, &pos_value); + } + } else { + /* cannot put the data - skip */ + } + } } if (packet->is_change_user_packet) { if (PASS != conn->m->simple_command(conn, COM_CHANGE_USER, buffer + MYSQLND_HEADER_SIZE, p - buffer - MYSQLND_HEADER_SIZE, diff --git a/ext/mysqlnd/mysqlnd_wireprotocol.h b/ext/mysqlnd/mysqlnd_wireprotocol.h index e9c9024d0..26dd4c65a 100644 --- a/ext/mysqlnd/mysqlnd_wireprotocol.h +++ b/ext/mysqlnd/mysqlnd_wireprotocol.h @@ -103,7 +103,7 @@ typedef struct st_mysqlnd_packet_auth { zend_bool send_auth_data; zend_bool is_change_user_packet; zend_bool silent; - + HashTable *connect_attr; } MYSQLND_PACKET_AUTH; /* Auth response packet */ diff --git a/ext/pdo_oci/oci_driver.c b/ext/pdo_oci/oci_driver.c index a0377ddb1..5497bebc6 100644 --- a/ext/pdo_oci/oci_driver.c +++ b/ext/pdo_oci/oci_driver.c @@ -227,8 +227,10 @@ static int oci_handle_closer(pdo_dbh_t *dbh TSRMLS_DC) /* {{{ */ H->server = NULL; } - OCIHandleFree(H->err, OCI_HTYPE_ERROR); - H->err = NULL; + if (H->err) { + OCIHandleFree(H->err, OCI_HTYPE_ERROR); + H->err = NULL; + } if (H->charset && H->env) { OCIHandleFree(H->env, OCI_HTYPE_ENV); diff --git a/ext/pdo_oci/oci_statement.c b/ext/pdo_oci/oci_statement.c index 2a93a66a8..dcb955780 100644 --- a/ext/pdo_oci/oci_statement.c +++ b/ext/pdo_oci/oci_statement.c @@ -99,7 +99,7 @@ static int oci_stmt_dtor(pdo_stmt_t *stmt TSRMLS_DC) /* {{{ */ switch (S->cols[i].dtype) { case SQLT_BLOB: case SQLT_CLOB: - /* do nothing */ + OCIDescriptorFree(S->cols[i].data, OCI_DTYPE_LOB); break; default: efree(S->cols[i].data); @@ -654,7 +654,6 @@ static int oci_blob_close(php_stream *stream, int close_handle TSRMLS_DC) if (close_handle) { OCILobClose(self->S->H->svc, self->S->err, self->lob); - OCIDescriptorFree(self->lob, OCI_DTYPE_LOB); efree(self); } diff --git a/ext/pdo_oci/tests/bug57702.phpt b/ext/pdo_oci/tests/bug57702.phpt new file mode 100644 index 000000000..9281f6d0f --- /dev/null +++ b/ext/pdo_oci/tests/bug57702.phpt @@ -0,0 +1,165 @@ +--TEST-- +PDO OCI Bug #57702 (Multi-row BLOB fetches) +--SKIPIF-- +<?php +if (!extension_loaded('pdo') || !extension_loaded('pdo_oci')) die('skip not loaded'); +require(dirname(__FILE__).'/../../pdo/tests/pdo_test.inc'); +PDOTest::skip(); +?> +--FILE-- +<?php + +require('ext/pdo/tests/pdo_test.inc'); +$db = PDOTest::test_factory('ext/pdo_oci/tests/common.phpt'); + +// Note the PDO test setup sets PDO::ATTR_STRINGIFY_FETCHES to true +// (and sets PDO::ATTR_CASE to PDO::CASE_LOWER) + +$query = "begin execute immediate 'drop table mytable'; exception when others then if sqlcode <> -942 then raise; end if; end;"; +$stmt = $db->prepare($query); +$stmt->execute(); + +$query = "create table bug57702 (id number, data1 blob, data2 blob)"; +$stmt = $db->prepare($query); +$stmt->execute(); + +function do_insert($db, $id, $data1, $data2) +{ + $db->beginTransaction(); + $stmt = $db->prepare("insert into bug57702 (id, data1, data2) values (:id, empty_blob(), empty_blob()) returning data1, data2 into :blob1, :blob2"); + $stmt->bindParam(':id', $id); + $stmt->bindParam(':blob1', $blob1, PDO::PARAM_LOB); + $stmt->bindParam(':blob2', $blob2, PDO::PARAM_LOB); + $blob1 = null; + $blob2 = null; + $stmt->execute(); + + fwrite($blob1, $data1); + fclose($blob1); + fwrite($blob2, $data2); + fclose($blob2); + $db->commit(); +} + +do_insert($db, 1, "row 1 col 1", "row 1 col 2"); +do_insert($db, 2, "row 2 col 1", "row 2 col 2"); + +//////////////////// + +echo "First Query\n"; + +// Fetch it back +$stmt = $db->prepare('select data1, data2 from bug57702 order by id'); +$stmt->execute(); +$row = $stmt->fetch(PDO::FETCH_ASSOC); +var_dump($row['data1']); +var_dump($row['data2']); +$row = $stmt->fetch(PDO::FETCH_ASSOC); +var_dump($row['data1']); +var_dump($row['data2']); + +//////////////////// + +echo "\nSecond Query\n"; + +foreach($db->query("select data1 as d1, data2 as d2 from bug57702 order by id") as $row) { + var_dump($row['d1']); + var_dump($row['d2']); +} + +//////////////////// + +echo "\nThird Query\n"; + +$stmt = $db->prepare('select data1 as d3_1, data2 as d3_2 from bug57702 order by id'); + +$rs = $stmt->execute(); +$stmt->bindColumn('d3_1' , $clob1, PDO::PARAM_LOB); +$stmt->bindColumn('d3_2' , $clob2, PDO::PARAM_LOB); + +while ($stmt->fetch(PDO::FETCH_BOUND)) { + var_dump($clob1); + var_dump($clob2); +} +print "done\n"; + +//////////////////// + +echo "\nFourth Query\n"; + +$a = array(); +$i = 0; +foreach($db->query("select data1 as d4_1, data2 as d4_2 from bug57702 order by id") as $row) { + $a[$i][0] = $row['d4_1']; + $a[$i][1] = $row['d4_2']; + $i++; +} + +for ($i = 0; $i < count($a); $i++) { + var_dump($a[$i][0]); + var_dump($a[$i][1]); +} + +//////////////////// + +echo "\nFifth Query\n"; + +$db->setAttribute(PDO::ATTR_STRINGIFY_FETCHES, false); // Let's use streams + +// Since each column only has one lob descriptor, the last row is +// shown twice because the lob descriptor for each column is reused in +// the stream + +$a = array(); +$i = 0; +foreach($db->query("select data1 as d4_1, data2 as d4_2 from bug57702 order by id") as $row) { + $a[$i][0] = $row['d4_1']; + $a[$i][1] = $row['d4_2']; + $i++; +} + +for ($i = 0; $i < count($a); $i++) { + var_dump(stream_get_contents($a[$i][0])); + var_dump(stream_get_contents($a[$i][1])); +} + +// Cleanup +$query = "drop table bug57702"; +$stmt = $db->prepare($query); +$stmt->execute(); + +print "done\n"; + +?> +--EXPECTF-- +First Query +string(11) "row 1 col 1" +string(11) "row 1 col 2" +string(11) "row 2 col 1" +string(11) "row 2 col 2" + +Second Query +string(11) "row 1 col 1" +string(11) "row 1 col 2" +string(11) "row 2 col 1" +string(11) "row 2 col 2" + +Third Query +string(11) "row 1 col 1" +string(11) "row 1 col 2" +string(11) "row 2 col 1" +string(11) "row 2 col 2" +done + +Fourth Query +string(11) "row 1 col 1" +string(11) "row 1 col 2" +string(11) "row 2 col 1" +string(11) "row 2 col 2" + +Fifth Query +string(11) "row 2 col 1" +string(11) "row 2 col 2" +string(11) "row 2 col 1" +string(11) "row 2 col 2" +done diff --git a/ext/pdo_sqlite/sqlite_statement.c b/ext/pdo_sqlite/sqlite_statement.c index d5b4df1fd..e970ad3e0 100644 --- a/ext/pdo_sqlite/sqlite_statement.c +++ b/ext/pdo_sqlite/sqlite_statement.c @@ -112,9 +112,15 @@ static int pdo_sqlite_stmt_param_hook(pdo_stmt_t *stmt, struct pdo_bound_param_d } } else { convert_to_long(param->parameter); +#if LONG_MAX > 2147483647 + if (SQLITE_OK == sqlite3_bind_int64(S->stmt, param->paramno + 1, Z_LVAL_P(param->parameter))) { + return 1; + } +#else if (SQLITE_OK == sqlite3_bind_int(S->stmt, param->paramno + 1, Z_LVAL_P(param->parameter))) { return 1; } +#endif } pdo_sqlite_error_stmt(stmt); return 0; diff --git a/ext/pdo_sqlite/tests/bug_63916-2.phpt b/ext/pdo_sqlite/tests/bug_63916-2.phpt new file mode 100644 index 000000000..bc9bfc9c9 --- /dev/null +++ b/ext/pdo_sqlite/tests/bug_63916-2.phpt @@ -0,0 +1,27 @@ +--TEST-- +Bug #63916 PDO::PARAM_INT casts to 32bit int internally even on 64bit builds in pdo_sqlite +--SKIPIF-- +<?php +if (!extension_loaded('pdo_sqlite')) die('skip'); +if (PHP_INT_SIZE > 4) die('skip'); +?> +--FILE-- +<?php +$num = PHP_INT_MAX; // 32 bits +$conn = new PDO('sqlite::memory:'); +$conn->query('CREATE TABLE users (id INTEGER NOT NULL, num INTEGER NOT NULL, PRIMARY KEY(id))'); + +$stmt = $conn->prepare('insert into users (id, num) values (:id, :num)'); +$stmt->bindValue(':id', 1, PDO::PARAM_INT); +$stmt->bindValue(':num', $num, PDO::PARAM_INT); +$stmt->execute(); + +$stmt = $conn->query('SELECT num FROM users'); +$result = $stmt->fetchAll(PDO::FETCH_COLUMN); + +var_dump($num,$result[0]); + +?> +--EXPECT-- +int(2147483647) +string(10) "2147483647" diff --git a/ext/pdo_sqlite/tests/bug_63916.phpt b/ext/pdo_sqlite/tests/bug_63916.phpt new file mode 100644 index 000000000..582413db4 --- /dev/null +++ b/ext/pdo_sqlite/tests/bug_63916.phpt @@ -0,0 +1,27 @@ +--TEST-- +Bug #63916 PDO::PARAM_INT casts to 32bit int internally even on 64bit builds in pdo_sqlite +--SKIPIF-- +<?php +if (!extension_loaded('pdo_sqlite')) die('skip'); +if (PHP_INT_SIZE < 8) die('skip'); +?> +--FILE-- +<?php +$num = 100004313234244; // exceeds 32 bits +$conn = new PDO('sqlite::memory:'); +$conn->query('CREATE TABLE users (id INTEGER NOT NULL, num INTEGER NOT NULL, PRIMARY KEY(id))'); + +$stmt = $conn->prepare('insert into users (id, num) values (:id, :num)'); +$stmt->bindValue(':id', 1, PDO::PARAM_INT); +$stmt->bindValue(':num', $num, PDO::PARAM_INT); +$stmt->execute(); + +$stmt = $conn->query('SELECT num FROM users'); +$result = $stmt->fetchAll(PDO::FETCH_COLUMN); + +var_dump($num,$result[0]); + +?> +--EXPECT-- +int(100004313234244) +string(15) "100004313234244" diff --git a/ext/pgsql/pgsql.c b/ext/pgsql/pgsql.c index d01dda603..7ee838a9f 100644 --- a/ext/pgsql/pgsql.c +++ b/ext/pgsql/pgsql.c @@ -1736,7 +1736,7 @@ PHP_FUNCTION(pg_query_params) } else { zval tmp_val = **tmp; zval_copy_ctor(&tmp_val); - convert_to_string(&tmp_val); + convert_to_cstring(&tmp_val); if (Z_TYPE(tmp_val) != IS_STRING) { php_error_docref(NULL TSRMLS_CC, E_WARNING,"Error converting parameter"); zval_dtor(&tmp_val); diff --git a/ext/pgsql/tests/bug46408.phpt b/ext/pgsql/tests/bug46408.phpt new file mode 100644 index 000000000..8c72ba5f3 --- /dev/null +++ b/ext/pgsql/tests/bug46408.phpt @@ -0,0 +1,23 @@ +--TEST-- +Bug #46408 (Locale number format settings can cause pg_query_params to break with numerics) +--SKIPIF-- +<?php +require_once('skipif.inc'); +?> +--FILE-- +<?php + +require_once('config.inc'); + +$dbh = pg_connect($conn_str); +setlocale(LC_ALL, 'hr_HR.utf-8', 'hr_HR'); +echo 3.5.PHP_EOL; +pg_query_params("SELECT $1::numeric", array(3.5)); +pg_close($dbh); + +echo "Done".PHP_EOL; + +?> +--EXPECTF-- +3,5 +Done diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index ff3a6a508..5c844b8d4 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -4192,13 +4192,21 @@ ZEND_METHOD(reflection_class, newInstance) { zval *retval_ptr = NULL; reflection_object *intern; - zend_class_entry *ce; + zend_class_entry *ce, *old_scope; + zend_function *constructor; METHOD_NOTSTATIC(reflection_class_ptr); GET_REFLECTION_OBJECT_PTR(ce); + object_init_ex(return_value, ce); + + old_scope = EG(scope); + EG(scope) = ce; + constructor = Z_OBJ_HT_P(return_value)->get_constructor(return_value TSRMLS_CC); + EG(scope) = old_scope; + /* Run the constructor if there is one */ - if (ce->constructor) { + if (constructor) { zval ***params = NULL; int num_args = 0; zend_fcall_info fci; @@ -4206,18 +4214,18 @@ ZEND_METHOD(reflection_class, newInstance) if (!(ce->constructor->common.fn_flags & ZEND_ACC_PUBLIC)) { zend_throw_exception_ex(reflection_exception_ptr, 0 TSRMLS_CC, "Access to non-public constructor of class %s", ce->name); - return; + zval_dtor(return_value); + RETURN_NULL(); } if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "*", ¶ms, &num_args) == FAILURE) { if (params) { efree(params); } + zval_dtor(return_value); RETURN_FALSE; } - object_init_ex(return_value, ce); - fci.size = sizeof(fci); fci.function_table = EG(function_table); fci.function_name = NULL; @@ -4242,6 +4250,7 @@ ZEND_METHOD(reflection_class, newInstance) zval_ptr_dtor(&retval_ptr); } php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invocation of %s's constructor failed", ce->name); + zval_dtor(return_value); RETURN_NULL(); } if (retval_ptr) { @@ -4250,9 +4259,7 @@ ZEND_METHOD(reflection_class, newInstance) if (params) { efree(params); } - } else if (!ZEND_NUM_ARGS()) { - object_init_ex(return_value, ce); - } else { + } else if (ZEND_NUM_ARGS()) { zend_throw_exception_ex(reflection_exception_ptr, 0 TSRMLS_CC, "Class %s does not have a constructor, so you cannot pass any constructor arguments", ce->name); } } diff --git a/ext/reflection/tests/bug64007.phpt b/ext/reflection/tests/bug64007.phpt new file mode 100644 index 000000000..34aac7a61 --- /dev/null +++ b/ext/reflection/tests/bug64007.phpt @@ -0,0 +1,10 @@ +--TEST-- +Bug #64007 (There is an ability to create instance of Generator by hand) +--FILE-- +<?php +$reflection = new ReflectionClass('Generator'); +$generator = $reflection->newInstance(); +var_dump($generator); +?> +--EXPECTF-- +Catchable fatal error: The "Generator" class is reserved for internal use and cannot be manually instantiated in %sbug64007.php on line %d diff --git a/ext/sqlite3/sqlite3.c b/ext/sqlite3/sqlite3.c index 881e3c370..df449d738 100644 --- a/ext/sqlite3/sqlite3.c +++ b/ext/sqlite3/sqlite3.c @@ -730,7 +730,11 @@ static int sqlite3_do_callback(struct php_sqlite3_fci *fc, zval *cb, int argc, s switch (sqlite3_value_type(argv[i])) { case SQLITE_INTEGER: +#if LONG_MAX > 2147483647 + ZVAL_LONG(*zargs[i + is_agg], sqlite3_value_int64(argv[i])); +#else ZVAL_LONG(*zargs[i + is_agg], sqlite3_value_int(argv[i])); +#endif break; case SQLITE_FLOAT: @@ -774,7 +778,11 @@ static int sqlite3_do_callback(struct php_sqlite3_fci *fc, zval *cb, int argc, s if (retval) { switch (Z_TYPE_P(retval)) { case IS_LONG: +#if LONG_MAX > 2147483647 + sqlite3_result_int64(context, Z_LVAL_P(retval)); +#else sqlite3_result_int(context, Z_LVAL_P(retval)); +#endif break; case IS_NULL: @@ -1493,7 +1501,11 @@ PHP_METHOD(sqlite3stmt, execute) switch (param->type) { case SQLITE_INTEGER: convert_to_long(param->parameter); +#if LONG_MAX > 2147483647 + sqlite3_bind_int64(stmt_obj->stmt, param->param_number, Z_LVAL_P(param->parameter)); +#else sqlite3_bind_int(stmt_obj->stmt, param->param_number, Z_LVAL_P(param->parameter)); +#endif break; case SQLITE_FLOAT: diff --git a/ext/sqlite3/tests/bug63921-32bit.phpt b/ext/sqlite3/tests/bug63921-32bit.phpt new file mode 100644 index 000000000..8c1c6b941 --- /dev/null +++ b/ext/sqlite3/tests/bug63921-32bit.phpt @@ -0,0 +1,27 @@ +--TEST-- +Bug #63921 sqlite3::bindvalue and relative PHP functions aren't using sqlite3_*_int64 API +--SKIPIF-- +<?php +if (!extension_loaded('sqlite3')) die('skip'); +if (PHP_INT_SIZE > 4) die('skip'); // skip for 64bit builds - there is another test for that +?> +--FILE-- +<?php +$num = PHP_INT_MAX; // 32 bits +$conn = new sqlite3(':memory:'); +$conn->query('CREATE TABLE users (id INTEGER NOT NULL, num INTEGER NOT NULL, PRIMARY KEY(id))'); + +$stmt = $conn->prepare('insert into users (id, num) values (:id, :num)'); +$stmt->bindValue(':id', 1, SQLITE3_INTEGER); +$stmt->bindValue(':num', $num, SQLITE3_INTEGER); +$stmt->execute(); + +$stmt = $conn->query('SELECT num FROM users'); +$result = $stmt->fetchArray(); + +var_dump($num,$result[0]); + +?> +--EXPECT-- +int(2147483647) +string(10) "2147483647" diff --git a/ext/sqlite3/tests/bug63921-64bit.phpt b/ext/sqlite3/tests/bug63921-64bit.phpt new file mode 100644 index 000000000..8e821fd2d --- /dev/null +++ b/ext/sqlite3/tests/bug63921-64bit.phpt @@ -0,0 +1,27 @@ +--TEST-- +Bug #63921 sqlite3::bindvalue and relative PHP functions aren't using sqlite3_*_int64 API +--SKIPIF-- +<?php +if (!extension_loaded('sqlite3')) die('skip'); +if (PHP_INT_SIZE < 8) die('skip'); // skip for 32bit builds - there is another test for that +?> +--FILE-- +<?php +$num = 100004313234244; // notice this exceeds 32 bits +$conn = new sqlite3(':memory:'); +$conn->query('CREATE TABLE users (id INTEGER NOT NULL, num INTEGER NOT NULL, PRIMARY KEY(id))'); + +$stmt = $conn->prepare('insert into users (id, num) values (:id, :num)'); +$stmt->bindValue(':id', 1, SQLITE3_INTEGER); +$stmt->bindValue(':num', $num, SQLITE3_INTEGER); +$stmt->execute(); + +$stmt = $conn->query('SELECT num FROM users'); +$result = $stmt->fetchArray(); + +var_dump($num,$result[0]); + +?> +--EXPECT-- +int(100004313234244) +string(15) "100004313234244" diff --git a/ext/standard/file.c b/ext/standard/file.c index 74577ac49..cf8b15955 100644 --- a/ext/standard/file.c +++ b/ext/standard/file.c @@ -159,6 +159,7 @@ static ZEND_RSRC_DTOR_FUNC(file_context_dtor) static void file_globals_ctor(php_file_globals *file_globals_p TSRMLS_DC) { FG(pclose_ret) = 0; + FG(pclose_wait) = 0; FG(user_stream_current_filename) = NULL; FG(def_chunk_size) = PHP_SOCK_CHUNK_SIZE; FG(wrapper_errors) = NULL; @@ -960,7 +961,9 @@ PHP_FUNCTION(pclose) PHP_STREAM_TO_ZVAL(stream, &arg1); + FG(pclose_wait) = 1; zend_list_delete(stream->rsrc_id); + FG(pclose_wait) = 0; RETURN_LONG(FG(pclose_ret)); } /* }}} */ diff --git a/ext/standard/file.h b/ext/standard/file.h index 0a4512ecd..2bcdfd64b 100644 --- a/ext/standard/file.h +++ b/ext/standard/file.h @@ -115,7 +115,7 @@ typedef struct _php_meta_tags_data { php_meta_tags_token php_next_meta_token(php_meta_tags_data * TSRMLS_DC); typedef struct { - int pclose_ret; + int pclose_ret; size_t def_chunk_size; long auto_detect_line_endings; long default_socket_timeout; @@ -126,6 +126,7 @@ typedef struct { HashTable *stream_wrappers; /* per-request copy of url_stream_wrappers_hash */ HashTable *stream_filters; /* per-request copy of stream_filters_hash */ HashTable *wrapper_errors; /* key: wrapper address; value: linked list of char* */ + int pclose_wait; } php_file_globals; #ifdef ZTS diff --git a/ext/standard/html.c b/ext/standard/html.c index 79a6737ca..414fa65c9 100644 --- a/ext/standard/html.c +++ b/ext/standard/html.c @@ -1628,8 +1628,8 @@ PHP_FUNCTION(get_html_translation_table) unsigned i, j, k, max_i, max_j, max_k; /* no mapping to unicode required */ - if (CHARSET_SINGLE_BYTE(charset)) { - max_i = 1; max_j = 1; max_k = 64; + if (CHARSET_SINGLE_BYTE(charset)) { /* ISO-8859-1 */ + max_i = 1; max_j = 4; max_k = 64; } else { max_i = 0x1E; max_j = 64; max_k = 64; } diff --git a/ext/standard/http.c b/ext/standard/http.c index 3e5073591..547df5218 100644 --- a/ext/standard/http.c +++ b/ext/standard/http.c @@ -69,12 +69,11 @@ PHPAPI int php_url_encode_hash_ex(HashTable *ht, smart_str *formstr, const char *tmp; zend_object *zobj = zend_objects_get_address(type TSRMLS_CC); - if (zend_check_property_access(zobj, key, key_len-1 TSRMLS_CC) != SUCCESS) { + if (zend_check_property_access(zobj, key, key_len TSRMLS_CC) != SUCCESS) { /* private or protected property access outside of the class */ continue; } - zend_unmangle_property_name(key, key_len-1, &tmp, (const char**)&key); - key_len = strlen(key); + zend_unmangle_property_name_ex(key, key_len, &tmp, (const char**)&key, &key_len); } if (zend_hash_get_current_data_ex(ht, (void **)&zdata, NULL) == FAILURE || !zdata || !(*zdata)) { diff --git a/ext/standard/image.c b/ext/standard/image.c index ebf894aa5..4984e4064 100644 --- a/ext/standard/image.c +++ b/ext/standard/image.c @@ -159,7 +159,7 @@ static struct gfxinfo *php_handle_bmp (php_stream * stream TSRMLS_DC) result->width = (((unsigned int)dim[ 5]) << 8) + ((unsigned int) dim[ 4]); result->height = (((unsigned int)dim[ 7]) << 8) + ((unsigned int) dim[ 6]); result->bits = ((unsigned int)dim[11]); - } else if (size > 12 && (size <= 64 || size == 108)) { + } else if (size > 12 && (size <= 64 || size == 108 || size == 124)) { result = (struct gfxinfo *) ecalloc (1, sizeof(struct gfxinfo)); result->width = (((unsigned int)dim[ 7]) << 24) + (((unsigned int)dim[ 6]) << 16) + (((unsigned int)dim[ 5]) << 8) + ((unsigned int) dim[ 4]); result->height = (((unsigned int)dim[11]) << 24) + (((unsigned int)dim[10]) << 16) + (((unsigned int)dim[ 9]) << 8) + ((unsigned int) dim[ 8]); diff --git a/ext/standard/proc_open.c b/ext/standard/proc_open.c index 1edfe7884..4e39a40be 100644 --- a/ext/standard/proc_open.c +++ b/ext/standard/proc_open.c @@ -208,6 +208,7 @@ static void proc_open_rsrc_dtor(zend_rsrc_list_entry *rsrc TSRMLS_DC) DWORD wstatus; #elif HAVE_SYS_WAIT_H int wstatus; + int waitpid_options = 0; pid_t wait_pid; #endif @@ -220,18 +221,27 @@ static void proc_open_rsrc_dtor(zend_rsrc_list_entry *rsrc TSRMLS_DC) } #ifdef PHP_WIN32 - WaitForSingleObject(proc->childHandle, INFINITE); + if (FG(pclose_wait)) { + WaitForSingleObject(proc->childHandle, INFINITE); + } GetExitCodeProcess(proc->childHandle, &wstatus); - FG(pclose_ret) = wstatus; + if (wstatus == STILL_ACTIVE) { + FG(pclose_ret) = -1; + } else { + FG(pclose_ret) = wstatus; + } CloseHandle(proc->childHandle); #elif HAVE_SYS_WAIT_H + if (!FG(pclose_wait)) { + waitpid_options = WNOHANG; + } do { - wait_pid = waitpid(proc->child, &wstatus, 0); + wait_pid = waitpid(proc->child, &wstatus, waitpid_options); } while (wait_pid == -1 && errno == EINTR); - if (wait_pid == -1) { + if (wait_pid <= 0) { FG(pclose_ret) = -1; } else { if (WIFEXITED(wstatus)) @@ -300,7 +310,9 @@ PHP_FUNCTION(proc_close) ZEND_FETCH_RESOURCE(proc, struct php_process_handle *, &zproc, -1, "process", le_proc_open); + FG(pclose_wait) = 1; zend_list_delete(Z_LVAL_P(zproc)); + FG(pclose_wait) = 0; RETURN_LONG(FG(pclose_ret)); } /* }}} */ diff --git a/ext/standard/string.c b/ext/standard/string.c index 06c1984cd..e245ce3fb 100644 --- a/ext/standard/string.c +++ b/ext/standard/string.c @@ -23,6 +23,11 @@ /* Synced with php 3.0 revision 1.193 1999-06-16 [ssb] */ #include <stdio.h> +#ifdef PHP_WIN32 +# include "win32/php_stdint.h" +#else +# include <stdint.h> +#endif #include "php.h" #include "php_rand.h" #include "php_string.h" @@ -57,6 +62,7 @@ #include "php_globals.h" #include "basic_functions.h" #include "php_smart_str.h" +#include <Zend/zend_exceptions.h> #ifdef ZTS #include "TSRM.h" #endif @@ -132,7 +138,7 @@ static char *php_bin2hex(const unsigned char *old, const size_t oldlen, size_t * size_t i, j; result = (unsigned char *) safe_emalloc(oldlen, 2 * sizeof(char), 1); - + for (i = j = 0; i < oldlen; i++) { result[j++] = hexconvtab[old[i] >> 4]; result[j++] = hexconvtab[old[i] & 15]; @@ -2772,112 +2778,333 @@ PHPAPI char *php_strtr(char *str, int len, char *str_from, char *str_to, int trl } /* }}} */ -/* {{{ php_strtr_array - */ -static void php_strtr_array(zval *return_value, char *str, int slen, HashTable *hash) +/* {{{ Definitions for php_strtr_array */ +typedef size_t STRLEN; /* STRLEN should be unsigned */ +typedef uint16_t HASH; +typedef struct { + HASH table_mask; + STRLEN entries[1]; +} SHIFT_TAB; +typedef struct { + HASH table_mask; + int entries[1]; +} HASH_TAB; +typedef struct { + const char *s; + STRLEN l; +} STR; +typedef struct _pat_and_repl { + STR pat; + STR repl; +} PATNREPL; + +#define S(a) ((a)->s) +#define L(a) ((a)->l) + +#define SHIFT_TAB_BITS 13 +#define HASH_TAB_BITS 10 /* should be less than sizeof(HASH) * 8 */ +#define SHIFT_TAB_SIZE (1U << SHIFT_TAB_BITS) +#define HASH_TAB_SIZE (1U << HASH_TAB_BITS) + +typedef struct { + int B; /* size of suffixes */ + int Bp; /* size of prefixes */ + STRLEN m; /* minimum pattern length */ + int patnum; /* number of patterns */ + SHIFT_TAB *shift; /* table mapping hash to allowed shift */ + HASH_TAB *hash; /* table mapping hash to int (pair of pointers) */ + HASH *prefix; /* array of hashes of prefixes by pattern suffix hash order */ + PATNREPL *patterns; /* array of prefixes by pattern suffix hash order */ +} PPRES; +/* }}} */ + +/* {{{ php_strtr_hash */ +static inline HASH php_strtr_hash(const char *str, int len) { - zval **entry; - char *string_key; - uint string_key_len; - zval **trans; - zval ctmp; - ulong num_key; - int minlen = 128*1024; - int maxlen = 0, pos, len, found; - char *key; - HashPosition hpos; - smart_str result = {0}; - HashTable tmp_hash; - - zend_hash_init(&tmp_hash, zend_hash_num_elements(hash), NULL, NULL, 0); - zend_hash_internal_pointer_reset_ex(hash, &hpos); - while (zend_hash_get_current_data_ex(hash, (void **)&entry, &hpos) == SUCCESS) { - switch (zend_hash_get_current_key_ex(hash, &string_key, &string_key_len, &num_key, 0, &hpos)) { - case HASH_KEY_IS_STRING: - len = string_key_len-1; - if (len < 1) { - zend_hash_destroy(&tmp_hash); - RETURN_FALSE; - } - zend_hash_add(&tmp_hash, string_key, string_key_len, entry, sizeof(zval*), NULL); - if (len > maxlen) { - maxlen = len; - } - if (len < minlen) { - minlen = len; - } - break; + HASH res = 0; + int i; + for (i = 0; i < len; i++) { + res = res * 33 + (unsigned char)str[i]; + } + + return res; +} +/* }}} */ +/* {{{ php_strtr_populate_shift */ +static inline void php_strtr_populate_shift(PATNREPL *patterns, int patnum, int B, STRLEN m, SHIFT_TAB *shift) +{ + int i; + STRLEN j, + max_shift; + + max_shift = m - B + 1; + for (i = 0; i < SHIFT_TAB_SIZE; i++) { + shift->entries[i] = max_shift; + } + for (i = 0; i < patnum; i++) { + for (j = 0; j < m - B + 1; j++) { + HASH h = php_strtr_hash(&S(&patterns[i].pat)[j], B) & shift->table_mask; + assert((long long) m - (long long) j - B >= 0); + shift->entries[h] = MIN(shift->entries[h], m - j - B); + } + } +} +/* }}} */ +/* {{{ php_strtr_compare_hash_suffix */ +static int php_strtr_compare_hash_suffix(const void *a, const void *b TSRMLS_DC, void *ctx_g) +{ + const PPRES *res = ctx_g; + const PATNREPL *pnr_a = a, + *pnr_b = b; + HASH hash_a = php_strtr_hash(&S(&pnr_a->pat)[res->m - res->B], res->B) + & res->hash->table_mask, + hash_b = php_strtr_hash(&S(&pnr_b->pat)[res->m - res->B], res->B) + & res->hash->table_mask; + /* TODO: don't recalculate the hashes all the time */ + if (hash_a > hash_b) { + return 1; + } else if (hash_a < hash_b) { + return -1; + } else { + /* longer patterns must be sorted first */ + if (L(&pnr_a->pat) > L(&pnr_b->pat)) { + return -1; + } else if (L(&pnr_a->pat) < L(&pnr_b->pat)) { + return 1; + } else { + return 0; + } + } +} +/* }}} */ +/* {{{ php_strtr_free_strp */ +static void php_strtr_free_strp(void *strp) +{ + STR_FREE(*(char**)strp); +} +/* }}} */ +/* {{{ php_strtr_array_prepare_repls */ +static PATNREPL *php_strtr_array_prepare_repls(int slen, HashTable *pats, zend_llist **allocs, int *outsize) +{ + PATNREPL *patterns; + HashPosition hpos; + zval **entry; + int num_pats = zend_hash_num_elements(pats), + i; + + patterns = safe_emalloc(num_pats, sizeof(*patterns), 0); + *allocs = emalloc(sizeof **allocs); + zend_llist_init(*allocs, sizeof(void*), &php_strtr_free_strp, 0); + + for (i = 0, zend_hash_internal_pointer_reset_ex(pats, &hpos); + zend_hash_get_current_data_ex(pats, (void **)&entry, &hpos) == SUCCESS; + zend_hash_move_forward_ex(pats, &hpos)) { + char *string_key; + uint string_key_len; + ulong num_key; + zval *tzv = NULL; + + switch (zend_hash_get_current_key_ex(pats, &string_key, &string_key_len, &num_key, 0, &hpos)) { + case HASH_KEY_IS_LONG: + string_key_len = 1 + zend_spprintf(&string_key, 0, "%ld", (long)num_key); + zend_llist_add_element(*allocs, &string_key); + /* break missing intentionally */ + + case HASH_KEY_IS_STRING: + string_key_len--; /* exclude final '\0' */ + if (string_key_len == 0) { /* empty string given as pattern */ + efree(patterns); + zend_llist_destroy(*allocs); + efree(*allocs); + *allocs = NULL; + return NULL; + } + if (string_key_len > slen) { /* this pattern can never match */ + continue; + } - case HASH_KEY_IS_LONG: - Z_TYPE(ctmp) = IS_LONG; - Z_LVAL(ctmp) = num_key; + if (Z_TYPE_PP(entry) != IS_STRING) { + tzv = *entry; + zval_addref_p(tzv); + SEPARATE_ZVAL(&tzv); + convert_to_string(tzv); + entry = &tzv; + zend_llist_add_element(*allocs, &Z_STRVAL_PP(entry)); + } - convert_to_string(&ctmp); - len = Z_STRLEN(ctmp); - zend_hash_add(&tmp_hash, Z_STRVAL(ctmp), len+1, entry, sizeof(zval*), NULL); - zval_dtor(&ctmp); + S(&patterns[i].pat) = string_key; + L(&patterns[i].pat) = string_key_len; + S(&patterns[i].repl) = Z_STRVAL_PP(entry); + L(&patterns[i].repl) = Z_STRLEN_PP(entry); + i++; - if (len > maxlen) { - maxlen = len; - } - if (len < minlen) { - minlen = len; - } - break; + if (tzv) { + efree(tzv); + } } - zend_hash_move_forward_ex(hash, &hpos); } - key = emalloc(maxlen+1); - pos = 0; + *outsize = i; + return patterns; +} +/* }}} */ + +/* {{{ PPRES *php_strtr_array_prepare(STR *text, PATNREPL *patterns, int patnum, int B, int Bp) */ +static PPRES *php_strtr_array_prepare(STR *text, PATNREPL *patterns, int patnum, int B, int Bp) +{ + int i; + PPRES *res = emalloc(sizeof *res); - while (pos < slen) { - if ((pos + maxlen) > slen) { - maxlen = slen - pos; + res->m = (STRLEN)-1; + for (i = 0; i < patnum; i++) { + if (L(&patterns[i].pat) < res->m) { + res->m = L(&patterns[i].pat); } + } + assert(res->m > 0); + res->B = B = MIN(B, res->m); + res->Bp = Bp = MIN(Bp, res->m); - found = 0; - memcpy(key, str+pos, maxlen); - - for (len = maxlen; len >= minlen; len--) { - key[len] = 0; + res->shift = safe_emalloc(SHIFT_TAB_SIZE, sizeof(*res->shift->entries), sizeof(*res->shift)); + res->shift->table_mask = SHIFT_TAB_SIZE - 1; + php_strtr_populate_shift(patterns, patnum, B, res->m, res->shift); - if (zend_hash_find(&tmp_hash, key, len+1, (void**)&trans) == SUCCESS) { - char *tval; - int tlen; - zval tmp; + res->hash = safe_emalloc(HASH_TAB_SIZE, sizeof(*res->hash->entries), sizeof(*res->hash)); + res->hash->table_mask = HASH_TAB_SIZE - 1; - if (Z_TYPE_PP(trans) != IS_STRING) { - tmp = **trans; - zval_copy_ctor(&tmp); - convert_to_string(&tmp); - tval = Z_STRVAL(tmp); - tlen = Z_STRLEN(tmp); - } else { - tval = Z_STRVAL_PP(trans); - tlen = Z_STRLEN_PP(trans); - } + res->patterns = safe_emalloc(patnum, sizeof(*res->patterns), 0); + memcpy(res->patterns, patterns, sizeof(*patterns) * patnum); +#ifdef ZTS + zend_qsort_r(res->patterns, patnum, sizeof(*res->patterns), + php_strtr_compare_hash_suffix, res, NULL); /* tsrmls not needed */ +#else + zend_qsort_r(res->patterns, patnum, sizeof(*res->patterns), + php_strtr_compare_hash_suffix, res); +#endif - smart_str_appendl(&result, tval, tlen); - pos += len; - found = 1; + res->prefix = safe_emalloc(patnum, sizeof(*res->prefix), 0); + for (i = 0; i < patnum; i++) { + res->prefix[i] = php_strtr_hash(S(&res->patterns[i].pat), Bp); + } - if (Z_TYPE_PP(trans) != IS_STRING) { - zval_dtor(&tmp); - } - break; + /* Initialize the rest of ->hash */ + for (i = 0; i < HASH_TAB_SIZE; i++) { + res->hash->entries[i] = -1; + } + { + HASH last_h = -1; /* assumes not all bits are used in res->hash */ + /* res->patterns is already ordered by hash. + * Make res->hash->entries[h] de index of the first pattern in + * res->patterns that has hash h */ + for (i = 0; i < patnum; i++) { + HASH h = php_strtr_hash(&S(&res->patterns[i].pat)[res->m - res->B], res->B) + & res->hash->table_mask; + if (h != last_h) { + res->hash->entries[h] = i; + last_h = h; } } + } + res->hash->entries[HASH_TAB_SIZE] = patnum; /* OK, we effectively allocated SIZE+1 */ + for (i = HASH_TAB_SIZE - 1; i >= 0; i--) { + if (res->hash->entries[i] == -1) { + res->hash->entries[i] = res->hash->entries[i + 1]; + } + } + + res->patnum = patnum; + + return res; +} +/* }}} */ +/* {{{ php_strtr_array_destroy_ppres(PPRES *d) */ +static void php_strtr_array_destroy_ppres(PPRES *d) +{ + efree(d->shift); + efree(d->hash); + efree(d->prefix); + efree(d->patterns); + efree(d); +} +/* }}} */ + +/* {{{ php_strtr_array_do_repl(STR *text, PPRES *d, zval *return_value) */ +static void php_strtr_array_do_repl(STR *text, PPRES *d, zval *return_value) +{ + STRLEN pos = 0, + nextwpos = 0, + lastpos = L(text) - d->m; + smart_str result = {0}; + + while (pos <= lastpos) { + HASH h = php_strtr_hash(&S(text)[pos + d->m - d->B], d->B) & d->shift->table_mask; + STRLEN shift = d->shift->entries[h]; - if (! found) { - smart_str_appendc(&result, str[pos++]); + if (shift > 0) { + pos += shift; + } else { + HASH h2 = h & d->hash->table_mask, + prefix_h = php_strtr_hash(&S(text)[pos], d->Bp); + + int offset_start = d->hash->entries[h2], + offset_end = d->hash->entries[h2 + 1], /* exclusive */ + i = 0; + + for (i = offset_start; i < offset_end; i++) { + PATNREPL *pnr; + if (d->prefix[i] != prefix_h) + continue; + + pnr = &d->patterns[i]; + if (L(&pnr->pat) > L(text) - pos || + memcmp(S(&pnr->pat), &S(text)[pos], L(&pnr->pat)) != 0) + continue; + + smart_str_appendl(&result, &S(text)[nextwpos], pos - nextwpos); + smart_str_appendl(&result, S(&pnr->repl), L(&pnr->repl)); + pos += L(&pnr->pat); + nextwpos = pos; + goto end_outer_loop; + } + + pos++; +end_outer_loop: ; } } - efree(key); - zend_hash_destroy(&tmp_hash); - smart_str_0(&result); - RETVAL_STRINGL(result.c, result.len, 0); + smart_str_appendl(&result, &S(text)[nextwpos], L(text) - nextwpos); + + if (result.c != NULL) { + smart_str_0(&result); + RETVAL_STRINGL(result.c, result.len, 0); + } else { + RETURN_EMPTY_STRING(); + } +} +/* }}} */ + +/* {{{ php_strtr_array */ +static void php_strtr_array(zval *return_value, char *str, int slen, HashTable *pats) +{ + PPRES *data; + STR text; + PATNREPL *patterns; + int patterns_len; + zend_llist *allocs; + + S(&text) = str; + L(&text) = slen; + + patterns = php_strtr_array_prepare_repls(slen, pats, &allocs, &patterns_len); + if (patterns == NULL) { + RETURN_FALSE; + } + data = php_strtr_array_prepare(&text, patterns, patterns_len, 2, 2); + efree(patterns); + php_strtr_array_do_repl(&text, data, return_value); + php_strtr_array_destroy_ppres(data); + zend_llist_destroy(allocs); + efree(allocs); } /* }}} */ diff --git a/ext/standard/tests/image/getimagesize.phpt b/ext/standard/tests/image/getimagesize.phpt index ab79c9c76..6cd8275e0 100644 --- a/ext/standard/tests/image/getimagesize.phpt +++ b/ext/standard/tests/image/getimagesize.phpt @@ -23,7 +23,22 @@ GetImageSize() var_dump($result); ?> --EXPECT-- -array(11) { +array(12) { + ["test1bpix.bmp"]=> + array(6) { + [0]=> + int(500) + [1]=> + int(345) + [2]=> + int(6) + [3]=> + string(24) "width="500" height="345"" + ["bits"]=> + int(32) + ["mime"]=> + string(14) "image/x-ms-bmp" + } ["test1pix.bmp"]=> array(6) { [0]=> diff --git a/ext/standard/tests/image/image_type_to_mime_type.phpt b/ext/standard/tests/image/image_type_to_mime_type.phpt index 5d94a6fe5..d83ab8d14 100644 --- a/ext/standard/tests/image/image_type_to_mime_type.phpt +++ b/ext/standard/tests/image/image_type_to_mime_type.phpt @@ -1,8 +1,8 @@ --TEST-- image_type_to_mime_type() --SKIPIF-- -<?php - if (!function_exists('image_type_to_mime_type')) die('skip image_type_to_mime_type() not available'); +<?php + if (!function_exists('image_type_to_mime_type')) die('skip image_type_to_mime_type() not available'); require_once('skipif_imagetype.inc'); ?> --FILE-- @@ -25,7 +25,9 @@ image_type_to_mime_type() var_dump($result); ?> --EXPECT-- -array(11) { +array(12) { + ["test1bpix.bmp"]=> + string(14) "image/x-ms-bmp" ["test1pix.bmp"]=> string(14) "image/x-ms-bmp" ["test1pix.jp2"]=> @@ -48,4 +50,4 @@ array(11) { string(29) "application/x-shockwave-flash" ["test4pix.tif"]=> string(10) "image/tiff" -}
\ No newline at end of file +} diff --git a/ext/standard/tests/image/test1bpix.bmp b/ext/standard/tests/image/test1bpix.bmp Binary files differnew file mode 100644 index 000000000..5522e503d --- /dev/null +++ b/ext/standard/tests/image/test1bpix.bmp diff --git a/ext/standard/tests/strings/get_html_translation_table_basic10.phpt b/ext/standard/tests/strings/get_html_translation_table_basic10.phpt new file mode 100644 index 000000000..a5a356885 --- /dev/null +++ b/ext/standard/tests/strings/get_html_translation_table_basic10.phpt @@ -0,0 +1,121 @@ +--TEST-- +Test get_html_translation_table() function: htmlentities/HTML 4/ISO-8859-1 (bug #64011) +--FILE-- +<?php + +function so($a,$b) { return ord($a) - ord($b); } + +$table = HTML_ENTITIES; +$tt = get_html_translation_table($table, ENT_COMPAT, "ISO-8859-1"); +uksort( $tt, 'so' ); +var_dump( count($tt) ); +print_r( $tt ); +echo "Done\n"; + +?> +--EXPECT-- +int(100) +Array +( + ["] => " + [&] => & + [<] => < + [>] => > + [ ] => + [¡] => ¡ + [¢] => ¢ + [£] => £ + [¤] => ¤ + [¥] => ¥ + [¦] => ¦ + [§] => § + [¨] => ¨ + [©] => © + [ª] => ª + [«] => « + [¬] => ¬ + [] => ­ + [®] => ® + [¯] => ¯ + [°] => ° + [±] => ± + [²] => ² + [³] => ³ + [´] => ´ + [µ] => µ + [¶] => ¶ + [·] => · + [¸] => ¸ + [¹] => ¹ + [º] => º + [»] => » + [¼] => ¼ + [½] => ½ + [¾] => ¾ + [¿] => ¿ + [À] => À + [Á] => Á + [Â] => Â + [Ã] => Ã + [Ä] => Ä + [Å] => Å + [Æ] => Æ + [Ç] => Ç + [È] => È + [É] => É + [Ê] => Ê + [Ë] => Ë + [Ì] => Ì + [Í] => Í + [Î] => Î + [Ï] => Ï + [Ð] => Ð + [Ñ] => Ñ + [Ò] => Ò + [Ó] => Ó + [Ô] => Ô + [Õ] => Õ + [Ö] => Ö + [×] => × + [Ø] => Ø + [Ù] => Ù + [Ú] => Ú + [Û] => Û + [Ü] => Ü + [Ý] => Ý + [Þ] => Þ + [ß] => ß + [à] => à + [á] => á + [â] => â + [ã] => ã + [ä] => ä + [å] => å + [æ] => æ + [ç] => ç + [è] => è + [é] => é + [ê] => ê + [ë] => ë + [ì] => ì + [í] => í + [î] => î + [ï] => ï + [ð] => ð + [ñ] => ñ + [ò] => ò + [ó] => ó + [ô] => ô + [õ] => õ + [ö] => ö + [÷] => ÷ + [ø] => ø + [ù] => ù + [ú] => ú + [û] => û + [ü] => ü + [ý] => ý + [þ] => þ + [ÿ] => ÿ +) +Done diff --git a/ext/standard/tests/strings/strpos.phpt b/ext/standard/tests/strings/strpos.phpt Binary files differindex 706ddfdcb..9b44584ee 100644 --- a/ext/standard/tests/strings/strpos.phpt +++ b/ext/standard/tests/strings/strpos.phpt diff --git a/ext/standard/var_unserializer.c b/ext/standard/var_unserializer.c index e93ddd531..03cf8dc60 100644 --- a/ext/standard/var_unserializer.c +++ b/ext/standard/var_unserializer.c @@ -1,4 +1,4 @@ -/* Generated by re2c 0.13.5 */ +/* Generated by re2c 0.13.5 on Mon Jan 21 11:41:53 2013 */ /* +----------------------------------------------------------------------+ | PHP Version 5 | diff --git a/ext/standard/var_unserializer.re b/ext/standard/var_unserializer.re index e54449a78..204995783 100644 --- a/ext/standard/var_unserializer.re +++ b/ext/standard/var_unserializer.re @@ -678,10 +678,13 @@ object ":" uiv ":" ["] { do { /* Try to find class directly */ + BG(serialize_lock) = 1; if (zend_lookup_class(class_name, len2, &pce TSRMLS_CC) == SUCCESS) { + BG(serialize_lock) = 0; ce = *pce; break; } + BG(serialize_lock) = 0; /* Check for unserialize callback */ if ((PG(unserialize_callback_func) == NULL) || (PG(unserialize_callback_func)[0] == '\0')) { @@ -696,7 +699,9 @@ object ":" uiv ":" ["] { args[0] = &arg_func_name; MAKE_STD_ZVAL(arg_func_name); ZVAL_STRING(arg_func_name, class_name, 1); + BG(serialize_lock) = 1; if (call_user_function_ex(CG(function_table), NULL, user_func, &retval_ptr, 1, args, 0, NULL TSRMLS_CC) != SUCCESS) { + BG(serialize_lock) = 0; php_error_docref(NULL TSRMLS_CC, E_WARNING, "defined (%s) but not found", user_func->value.str.val); incomplete_class = 1; ce = PHP_IC_ENTRY; @@ -704,6 +709,7 @@ object ":" uiv ":" ["] { zval_ptr_dtor(&arg_func_name); break; } + BG(serialize_lock) = 0; if (retval_ptr) { zval_ptr_dtor(&retval_ptr); } |