summaryrefslogtreecommitdiff
path: root/ext
diff options
context:
space:
mode:
Diffstat (limited to 'ext')
-rw-r--r--ext/date/php_date.c473
-rw-r--r--ext/date/php_date.h14
-rw-r--r--ext/date/tests/date_period-immutable.phpt56
-rw-r--r--ext/date/tests/date_time_immutable-inherited.phpt25
-rw-r--r--ext/date/tests/date_time_immutable.phpt167
-rw-r--r--ext/dba/dba_flatfile.c19
-rw-r--r--ext/dba/dba_gdbm.c17
-rw-r--r--ext/dba/dba_inifile.c1
-rw-r--r--ext/dba/dba_qdbm.c10
-rw-r--r--ext/dba/tests/dba_db1.phpt4
-rw-r--r--ext/dba/tests/dba_db2.phpt4
-rw-r--r--ext/dba/tests/dba_db3.phpt4
-rw-r--r--ext/dba/tests/dba_db4_000.phpt4
-rw-r--r--ext/dba/tests/dba_dbm.phpt4
-rw-r--r--ext/dba/tests/dba_flatfile.phpt4
-rw-r--r--ext/dba/tests/dba_gdbm.phpt2
-rw-r--r--ext/dba/tests/dba_handler.inc12
-rw-r--r--ext/dba/tests/dba_inifile.phpt4
-rw-r--r--ext/dba/tests/dba_ndbm.phpt4
-rw-r--r--ext/dba/tests/dba_qdbm.phpt2
-rw-r--r--ext/dba/tests/dba_tcadb.phpt4
-rw-r--r--ext/mysqli/mysqli.c3
-rw-r--r--ext/mysqli/mysqli_api.c3
-rw-r--r--ext/mysqli/tests/mysqli_constants.phpt4
-rw-r--r--ext/mysqli/tests/mysqli_expire_password.phpt145
-rw-r--r--ext/mysqlnd/mysqlnd.c119
-rw-r--r--ext/mysqlnd/mysqlnd.h1
-rw-r--r--ext/mysqlnd/mysqlnd_auth.c5
-rw-r--r--ext/mysqlnd/mysqlnd_enum_n_def.h5
-rw-r--r--ext/mysqlnd/mysqlnd_libmysql_compat.h3
-rw-r--r--ext/mysqlnd/mysqlnd_structs.h6
-rw-r--r--ext/mysqlnd/mysqlnd_wireprotocol.c66
-rw-r--r--ext/mysqlnd/mysqlnd_wireprotocol.h2
-rw-r--r--ext/pdo_oci/oci_driver.c6
-rw-r--r--ext/pdo_oci/oci_statement.c3
-rw-r--r--ext/pdo_oci/tests/bug57702.phpt165
-rw-r--r--ext/pdo_sqlite/sqlite_statement.c6
-rw-r--r--ext/pdo_sqlite/tests/bug_63916-2.phpt27
-rw-r--r--ext/pdo_sqlite/tests/bug_63916.phpt27
-rw-r--r--ext/pgsql/pgsql.c2
-rw-r--r--ext/pgsql/tests/bug46408.phpt23
-rw-r--r--ext/reflection/php_reflection.c23
-rw-r--r--ext/reflection/tests/bug64007.phpt10
-rw-r--r--ext/sqlite3/sqlite3.c12
-rw-r--r--ext/sqlite3/tests/bug63921-32bit.phpt27
-rw-r--r--ext/sqlite3/tests/bug63921-64bit.phpt27
-rw-r--r--ext/standard/file.c3
-rw-r--r--ext/standard/file.h3
-rw-r--r--ext/standard/html.c4
-rw-r--r--ext/standard/http.c5
-rw-r--r--ext/standard/image.c2
-rw-r--r--ext/standard/proc_open.c20
-rw-r--r--ext/standard/string.c401
-rw-r--r--ext/standard/tests/image/getimagesize.phpt17
-rw-r--r--ext/standard/tests/image/image_type_to_mime_type.phpt10
-rw-r--r--ext/standard/tests/image/test1bpix.bmpbin0 -> 690138 bytes
-rw-r--r--ext/standard/tests/strings/get_html_translation_table_basic10.phpt121
-rw-r--r--ext/standard/tests/strings/strpos.phptbin9990 -> 9981 bytes
-rw-r--r--ext/standard/var_unserializer.c2
-rw-r--r--ext/standard/var_unserializer.re6
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, &timestamp) == 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, &timestamp) == 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, &timestamp) == 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), &copyv, 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, "*", &params, &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
new file mode 100644
index 000000000..5522e503d
--- /dev/null
+++ b/ext/standard/tests/image/test1bpix.bmp
Binary files differ
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
+(
+ ["] => &quot;
+ [&] => &amp;
+ [<] => &lt;
+ [>] => &gt;
+ [ ] => &nbsp;
+ [¡] => &iexcl;
+ [¢] => &cent;
+ [£] => &pound;
+ [¤] => &curren;
+ [¥] => &yen;
+ [¦] => &brvbar;
+ [§] => &sect;
+ [¨] => &uml;
+ [©] => &copy;
+ [ª] => &ordf;
+ [«] => &laquo;
+ [¬] => &not;
+ [­] => &shy;
+ [®] => &reg;
+ [¯] => &macr;
+ [°] => &deg;
+ [±] => &plusmn;
+ [²] => &sup2;
+ [³] => &sup3;
+ [´] => &acute;
+ [µ] => &micro;
+ [¶] => &para;
+ [·] => &middot;
+ [¸] => &cedil;
+ [¹] => &sup1;
+ [º] => &ordm;
+ [»] => &raquo;
+ [¼] => &frac14;
+ [½] => &frac12;
+ [¾] => &frac34;
+ [¿] => &iquest;
+ [À] => &Agrave;
+ [Á] => &Aacute;
+ [Â] => &Acirc;
+ [Ã] => &Atilde;
+ [Ä] => &Auml;
+ [Å] => &Aring;
+ [Æ] => &AElig;
+ [Ç] => &Ccedil;
+ [È] => &Egrave;
+ [É] => &Eacute;
+ [Ê] => &Ecirc;
+ [Ë] => &Euml;
+ [Ì] => &Igrave;
+ [Í] => &Iacute;
+ [Î] => &Icirc;
+ [Ï] => &Iuml;
+ [Ð] => &ETH;
+ [Ñ] => &Ntilde;
+ [Ò] => &Ograve;
+ [Ó] => &Oacute;
+ [Ô] => &Ocirc;
+ [Õ] => &Otilde;
+ [Ö] => &Ouml;
+ [×] => &times;
+ [Ø] => &Oslash;
+ [Ù] => &Ugrave;
+ [Ú] => &Uacute;
+ [Û] => &Ucirc;
+ [Ü] => &Uuml;
+ [Ý] => &Yacute;
+ [Þ] => &THORN;
+ [ß] => &szlig;
+ [à] => &agrave;
+ [á] => &aacute;
+ [â] => &acirc;
+ [ã] => &atilde;
+ [ä] => &auml;
+ [å] => &aring;
+ [æ] => &aelig;
+ [ç] => &ccedil;
+ [è] => &egrave;
+ [é] => &eacute;
+ [ê] => &ecirc;
+ [ë] => &euml;
+ [ì] => &igrave;
+ [í] => &iacute;
+ [î] => &icirc;
+ [ï] => &iuml;
+ [ð] => &eth;
+ [ñ] => &ntilde;
+ [ò] => &ograve;
+ [ó] => &oacute;
+ [ô] => &ocirc;
+ [õ] => &otilde;
+ [ö] => &ouml;
+ [÷] => &divide;
+ [ø] => &oslash;
+ [ù] => &ugrave;
+ [ú] => &uacute;
+ [û] => &ucirc;
+ [ü] => &uuml;
+ [ý] => &yacute;
+ [þ] => &thorn;
+ [ÿ] => &yuml;
+)
+Done
diff --git a/ext/standard/tests/strings/strpos.phpt b/ext/standard/tests/strings/strpos.phpt
index 706ddfdcb..9b44584ee 100644
--- a/ext/standard/tests/strings/strpos.phpt
+++ b/ext/standard/tests/strings/strpos.phpt
Binary files differ
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);
}