From 4f26e8f350f0c4a9b9c9e1981367b9e8d67e425c Mon Sep 17 00:00:00 2001 From: Rudi Theunissen Date: Sun, 24 Jun 2018 08:33:44 -0400 Subject: [PATCH 01/40] Add __compareTo to struct _zend_class_entry --- Zend/zend.h | 1 + 1 file changed, 1 insertion(+) diff --git a/Zend/zend.h b/Zend/zend.h index b1c1e66867338..f10ea3daa1651 100644 --- a/Zend/zend.h +++ b/Zend/zend.h @@ -139,6 +139,7 @@ struct _zend_class_entry { union _zend_function *__callstatic; union _zend_function *__tostring; union _zend_function *__debugInfo; + union _zend_function *__compareTo; union _zend_function *serialize_func; union _zend_function *unserialize_func; From e8f12aec65c0ab6798982e00b7c39e8779c9dbf4 Mon Sep 17 00:00:00 2001 From: Rudi Theunissen Date: Sun, 24 Jun 2018 08:34:34 -0400 Subject: [PATCH 02/40] Register __compareTo in zend_register_functions --- Zend/zend_API.c | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/Zend/zend_API.c b/Zend/zend_API.c index 1699f1b50a94b..94d4972cbdebb 100644 --- a/Zend/zend_API.c +++ b/Zend/zend_API.c @@ -2163,7 +2163,21 @@ ZEND_API int zend_register_functions(zend_class_entry *scope, const zend_functio int count=0, unload=0; HashTable *target_function_table = function_table; int error_type; - zend_function *ctor = NULL, *dtor = NULL, *clone = NULL, *__get = NULL, *__set = NULL, *__unset = NULL, *__isset = NULL, *__call = NULL, *__callstatic = NULL, *__tostring = NULL, *__debugInfo = NULL; + + zend_function + *ctor = NULL, + *dtor = NULL, + *clone = NULL, + *__get = NULL, + *__set = NULL, + *__unset = NULL, + *__isset = NULL, + *__call = NULL, + *__callstatic = NULL, + *__tostring = NULL, + *__debugInfo = NULL, + *__compareTo = NULL; + zend_string *lowercase_name; size_t fname_len; const char *lc_class_name = NULL; @@ -2355,6 +2369,8 @@ ZEND_API int zend_register_functions(zend_class_entry *scope, const zend_functio __callstatic = reg_function; } else if (zend_string_equals_literal(lowercase_name, ZEND_TOSTRING_FUNC_NAME)) { __tostring = reg_function; + } else if (zend_string_equals_literal(lowercase_name, ZEND_COMPARETO_FUNC_NAME)) { + __compareTo = reg_function; } else if (zend_string_equals_literal(lowercase_name, ZEND_GET_FUNC_NAME)) { __get = reg_function; scope->ce_flags |= ZEND_ACC_USE_GUARDS; @@ -2409,6 +2425,7 @@ ZEND_API int zend_register_functions(zend_class_entry *scope, const zend_functio scope->__unset = __unset; scope->__isset = __isset; scope->__debugInfo = __debugInfo; + scope->__compareTo = __compareTo; if (ctor) { ctor->common.fn_flags |= ZEND_ACC_CTOR; if (ctor->common.fn_flags & ZEND_ACC_STATIC) { @@ -2476,7 +2493,11 @@ ZEND_API int zend_register_functions(zend_class_entry *scope, const zend_functio zend_error(error_type, "Method %s::%s() cannot be static", ZSTR_VAL(scope->name), ZSTR_VAL(__debugInfo->common.function_name)); } } - + if (__compareTo) { + if (__compareTo->common.fn_flags & ZEND_ACC_STATIC) { + zend_error(error_type, "Method %s::%s() cannot be static", ZSTR_VAL(scope->name), ZSTR_VAL(__compareTo->common.function_name)); + } + } if (ctor && ctor->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE && ctor->common.fn_flags & ZEND_ACC_CTOR) { zend_error_noreturn(E_CORE_ERROR, "Constructor %s::%s() cannot declare a return type", ZSTR_VAL(scope->name), ZSTR_VAL(ctor->common.function_name)); } From bd4bcfef1fa8580cdcc4a2b116023b6abdd62ff4 Mon Sep 17 00:00:00 2001 From: Rudi Theunissen Date: Sun, 24 Jun 2018 08:35:26 -0400 Subject: [PATCH 03/40] Add __compareTo to class_container in struct _zend_fcall_info_cache --- Zend/zend_API.h | 1 + 1 file changed, 1 insertion(+) diff --git a/Zend/zend_API.h b/Zend/zend_API.h index 63c82a4b06ad9..38187f6ce0f06 100644 --- a/Zend/zend_API.h +++ b/Zend/zend_API.h @@ -211,6 +211,7 @@ typedef struct _zend_fcall_info_cache { class_container.__unset = NULL; \ class_container.__isset = NULL; \ class_container.__debugInfo = NULL; \ + class_container.__compareTo = NULL; \ class_container.serialize_func = NULL; \ class_container.unserialize_func = NULL; \ class_container.parent = NULL; \ From 4504026421eaeecc720aa14bd79224d010ea48fe Mon Sep 17 00:00:00 2001 From: Rudi Theunissen Date: Sun, 24 Jun 2018 08:36:24 -0400 Subject: [PATCH 04/40] Add __compareTo to visbility and static checks --- Zend/zend_compile.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index c050855ae8d42..a4336cf3d5200 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -1763,6 +1763,7 @@ ZEND_API void zend_initialize_class_data(zend_class_entry *ce, zend_bool nullify ce->serialize_func = NULL; ce->unserialize_func = NULL; ce->__debugInfo = NULL; + ce->__compareTo = NULL; if (ce->type == ZEND_INTERNAL_CLASS) { ce->info.internal.module = NULL; ce->info.internal.builtin_functions = NULL; @@ -5847,6 +5848,11 @@ void zend_begin_method_decl(zend_op_array *op_array, zend_string *name, zend_boo zend_error(E_WARNING, "The magic method __debugInfo() must have " "public visibility and cannot be static"); } + } else if (zend_string_equals_literal(lcname, ZEND_COMPARETO_FUNC_NAME)) { + if (!is_public || is_static) { + zend_error(E_WARNING, "The magic method __compareTo() must have " + "public visibility and cannot be static"); + } } } else { if (!in_trait && zend_string_equals_ci(lcname, ce->name)) { @@ -5920,6 +5926,12 @@ void zend_begin_method_decl(zend_op_array *op_array, zend_string *name, zend_boo "public visibility and cannot be static"); } ce->__debugInfo = (zend_function *) op_array; + } else if (zend_string_equals_literal(lcname, ZEND_COMPARETO_FUNC_NAME)) { + if (!is_public || is_static) { + zend_error(E_WARNING, "The magic method __compareTo() must have " + "public visibility and cannot be static"); + } + ce->__compareTo = (zend_function *) op_array; } else if (!is_static) { op_array->fn_flags |= ZEND_ACC_ALLOW_STATIC; } From 6b914f4598e75729e15e103c3a6974334bfa2c4d Mon Sep 17 00:00:00 2001 From: Rudi Theunissen Date: Sun, 24 Jun 2018 08:36:56 -0400 Subject: [PATCH 05/40] Add ZEND_COMPARETO_FUNC_NAME constant --- Zend/zend_compile.h | 1 + 1 file changed, 1 insertion(+) diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h index 9ba1b54f33bd7..052920e48d8c7 100644 --- a/Zend/zend_compile.h +++ b/Zend/zend_compile.h @@ -979,6 +979,7 @@ END_EXTERN_C() #define ZEND_AUTOLOAD_FUNC_NAME "__autoload" #define ZEND_INVOKE_FUNC_NAME "__invoke" #define ZEND_DEBUGINFO_FUNC_NAME "__debuginfo" +#define ZEND_COMPARETO_FUNC_NAME "__compareto" /* The following constants may be combined in CG(compiler_options) * to change the default compiler behavior */ From 9db27620c82a4d55834988ffe3f5ee1875190ff3 Mon Sep 17 00:00:00 2001 From: Rudi Theunissen Date: Sun, 24 Jun 2018 08:37:21 -0400 Subject: [PATCH 06/40] Add inheritance support for __compareTo --- Zend/zend_inheritance.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Zend/zend_inheritance.c b/Zend/zend_inheritance.c index b66464c37542a..779863e67f6a1 100644 --- a/Zend/zend_inheritance.c +++ b/Zend/zend_inheritance.c @@ -138,6 +138,9 @@ static void do_inherit_parent_constructor(zend_class_entry *ce) /* {{{ */ if (EXPECTED(!ce->__debugInfo)) { ce->__debugInfo = ce->parent->__debugInfo; } + if (EXPECTED(!ce->__compareTo)) { + ce->__compareTo = ce->parent->__compareTo; + } if (ce->constructor) { if (ce->parent->constructor && UNEXPECTED(ce->parent->constructor->common.fn_flags & ZEND_ACC_FINAL)) { @@ -1157,6 +1160,8 @@ static void zend_add_magic_methods(zend_class_entry* ce, zend_string* mname, zen ce->__tostring = fe; } else if (zend_string_equals_literal(mname, ZEND_DEBUGINFO_FUNC_NAME)) { ce->__debugInfo = fe; + } else if (zend_string_equals_literal(mname, ZEND_COMPARETO_FUNC_NAME)) { + ce->__compareTo = fe; } } /* }}} */ From e2cb3cc5c932dccc6ac09f09087913b03a5a0f92 Mon Sep 17 00:00:00 2001 From: Rudi Theunissen Date: Sun, 24 Jun 2018 08:37:43 -0400 Subject: [PATCH 07/40] Add default object handler for compare --- Zend/zend_object_handlers.c | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/Zend/zend_object_handlers.c b/Zend/zend_object_handlers.c index 97a0dcd817040..8fc01720d8845 100644 --- a/Zend/zend_object_handlers.c +++ b/Zend/zend_object_handlers.c @@ -1551,6 +1551,28 @@ ZEND_API zend_function *zend_std_get_constructor(zend_object *zobj) /* {{{ */ } /* }}} */ +ZEND_API int zend_std_compare(zval *result, zval *op1, zval *op2) /* {{{ */ +{ + if (Z_TYPE_P(op1) == IS_OBJECT) { + zend_class_entry *ce = Z_OBJCE_P(op1); + + if (ce->__compareTo) { + zend_call_method_with_1_params(op1, ce, &ce->__compareTo, ZEND_COMPARETO_FUNC_NAME, result, op2); + + /* Returning null indicates that the comparison could not be made or + * isn't supported, falling back to default behaviour. */ + if (Z_TYPE_P(result) == IS_NULL || Z_TYPE_P(result) == IS_UNDEF) { + return FAILURE; + } + + return SUCCESS; + } + } + + return FAILURE; +} +/* }}} */ + ZEND_API int zend_std_compare_objects(zval *o1, zval *o2) /* {{{ */ { zend_object *zobj1, *zobj2; @@ -1863,7 +1885,7 @@ ZEND_API const zend_object_handlers std_object_handlers = { zend_std_get_closure, /* get_closure */ zend_std_get_gc, /* get_gc */ NULL, /* do_operation */ - NULL, /* compare */ + zend_std_compare, /* compare */ }; /* From dae3517bfe31e44d2cb0424c6411667943b191e7 Mon Sep 17 00:00:00 2001 From: Rudi Theunissen Date: Sun, 24 Jun 2018 08:38:05 -0400 Subject: [PATCH 08/40] Fix type in zend_object_compare_zvals_t --- Zend/zend_object_handlers.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Zend/zend_object_handlers.h b/Zend/zend_object_handlers.h index 1dfed715f3535..62510ff5edcae 100644 --- a/Zend/zend_object_handlers.h +++ b/Zend/zend_object_handlers.h @@ -113,7 +113,7 @@ typedef zend_object* (*zend_object_clone_obj_t)(zval *object); typedef zend_string *(*zend_object_get_class_name_t)(const zend_object *object); typedef int (*zend_object_compare_t)(zval *object1, zval *object2); -typedef int (*zend_object_compare_zvals_t)(zval *resul, zval *op1, zval *op2); +typedef int (*zend_object_compare_zvals_t)(zval *result, zval *op1, zval *op2); /* Cast an object to some other type. * readobj and retval must point to distinct zvals. From 91e72945c2885bc574c93ed23851a81164b9ba9c Mon Sep 17 00:00:00 2001 From: Rudi Theunissen Date: Sun, 24 Jun 2018 08:39:02 -0400 Subject: [PATCH 09/40] Normalise compare return and add compare fallthrough --- Zend/zend_operators.c | 39 ++++++++++++++++++++++++++++++--------- 1 file changed, 30 insertions(+), 9 deletions(-) diff --git a/Zend/zend_operators.c b/Zend/zend_operators.c index 333bce5dfc73d..6524a6df1b611 100644 --- a/Zend/zend_operators.c +++ b/Zend/zend_operators.c @@ -31,6 +31,7 @@ #include "zend_strtod.h" #include "zend_exceptions.h" #include "zend_closures.h" +#include "zend_interfaces.h" #if ZEND_USE_TOLOWER_L #include @@ -1951,12 +1952,14 @@ static void ZEND_FASTCALL convert_compare_result_to_long(zval *result) /* {{{ */ ZVAL_LONG(result, ZEND_NORMALIZE_BOOL(Z_DVAL_P(result))); } else { convert_to_long(result); + Z_LVAL_P(result) = ZEND_NORMALIZE_BOOL(Z_LVAL_P(result)); } } /* }}} */ ZEND_API int ZEND_FASTCALL compare_function(zval *result, zval *op1, zval *op2) /* {{{ */ { + int ret; int converted = 0; zval op1_copy, op2_copy; @@ -2024,10 +2027,25 @@ ZEND_API int ZEND_FASTCALL compare_function(zval *result, zval *op1, zval *op2) return SUCCESS; case TYPE_PAIR(IS_OBJECT, IS_NULL): + if (Z_OBJ_HANDLER_P(op1, compare)) { + if (Z_OBJ_HANDLER_P(op1, compare)(result, op1, op2) == SUCCESS) { + convert_compare_result_to_long(result); + return SUCCESS; + } + } + ZVAL_LONG(result, 1); return SUCCESS; case TYPE_PAIR(IS_NULL, IS_OBJECT): + if (Z_OBJ_HANDLER_P(op2, compare)) { + if (Z_OBJ_HANDLER_P(op2, compare)(result, op2, op1) == SUCCESS) { + convert_compare_result_to_long(result); + Z_LVAL_P(result) *= -1; + return SUCCESS; + } + } + ZVAL_LONG(result, -1); return SUCCESS; @@ -2041,17 +2059,20 @@ ZEND_API int ZEND_FASTCALL compare_function(zval *result, zval *op1, zval *op2) } if (Z_TYPE_P(op1) == IS_OBJECT && Z_OBJ_HANDLER_P(op1, compare)) { - ret = Z_OBJ_HANDLER_P(op1, compare)(result, op1, op2); - if (UNEXPECTED(Z_TYPE_P(result) != IS_LONG)) { - convert_compare_result_to_long(result); - } - return ret; + + /* If compare fails, we want to fall through to give the + * other comparison functions a chance to run. */ + if (Z_OBJ_HANDLER_P(op1, compare)(result, op1, op2) == SUCCESS) { + convert_compare_result_to_long(result); + return SUCCESS; + } + } else if (Z_TYPE_P(op2) == IS_OBJECT && Z_OBJ_HANDLER_P(op2, compare)) { - ret = Z_OBJ_HANDLER_P(op2, compare)(result, op1, op2); - if (UNEXPECTED(Z_TYPE_P(result) != IS_LONG)) { + if (Z_OBJ_HANDLER_P(op2, compare)(result, op1, op2) == SUCCESS) { convert_compare_result_to_long(result); - } - return ret; + Z_LVAL_P(result) *= -1; + return SUCCESS; + } } if (Z_TYPE_P(op1) == IS_OBJECT && Z_TYPE_P(op2) == IS_OBJECT) { From 1866849dfd7e38c20d1a30ce03c3b0a9c6b99ac1 Mon Sep 17 00:00:00 2001 From: Rudi Theunissen Date: Sun, 24 Jun 2018 08:41:34 -0400 Subject: [PATCH 10/40] Use is_equal_function directly rather than compare_function in ZEND_VM_HANDLER --- Zend/zend_vm_def.h | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index c5ff50cf852d6..b4460a023401a 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -438,8 +438,7 @@ ZEND_VM_COLD_CONSTCONST_HANDLER(17, ZEND_IS_EQUAL, CONST|TMPVAR|CV, CONST|TMPVAR op2 = GET_OP2_UNDEF_CV(op2, BP_VAR_R); } result = EX_VAR(opline->result.var); - compare_function(result, op1, op2); - ZVAL_BOOL(result, Z_LVAL_P(result) == 0); + is_equal_function(result, op1, op2); FREE_OP1(); FREE_OP2(); ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); @@ -496,8 +495,7 @@ ZEND_VM_COLD_CONSTCONST_HANDLER(18, ZEND_IS_NOT_EQUAL, CONST|TMPVAR|CV, CONST|TM op2 = GET_OP2_UNDEF_CV(op2, BP_VAR_R); } result = EX_VAR(opline->result.var); - compare_function(result, op1, op2); - ZVAL_BOOL(result, Z_LVAL_P(result) != 0); + is_not_equal_function(result, op1, op2); FREE_OP1(); FREE_OP2(); ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); From a0d084ff847d5549bc7b06e5fa046865e78632b7 Mon Sep 17 00:00:00 2001 From: Rudi Theunissen Date: Sun, 24 Jun 2018 08:42:18 -0400 Subject: [PATCH 11/40] Add serialize/unserialize of __compareTo pointer in zend_file_cache --- ext/opcache/zend_file_cache.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ext/opcache/zend_file_cache.c b/ext/opcache/zend_file_cache.c index 36576d892ed9b..de6e43eb2b6ae 100644 --- a/ext/opcache/zend_file_cache.c +++ b/ext/opcache/zend_file_cache.c @@ -723,6 +723,7 @@ static void zend_file_cache_serialize_class(zval *zv, SERIALIZE_PTR(ce->__tostring); SERIALIZE_PTR(ce->__callstatic); SERIALIZE_PTR(ce->__debugInfo); + SERIALIZE_PTR(ce->__compareTo); } static void zend_file_cache_serialize(zend_persistent_script *script, @@ -1347,6 +1348,7 @@ static void zend_file_cache_unserialize_class(zval *zv, UNSERIALIZE_PTR(ce->__tostring); UNSERIALIZE_PTR(ce->__callstatic); UNSERIALIZE_PTR(ce->__debugInfo); + UNSERIALIZE_PTR(ce->__compareTo); if (UNEXPECTED((ce->ce_flags & ZEND_ACC_ANON_CLASS))) { ce->serialize = zend_class_serialize_deny; From 1fab514920f2f66c232629bb6ce67c8e666aaa63 Mon Sep 17 00:00:00 2001 From: Rudi Theunissen Date: Sun, 24 Jun 2018 08:42:45 -0400 Subject: [PATCH 12/40] Add __compareTo to zend_update_parent_ce --- ext/opcache/zend_persist.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ext/opcache/zend_persist.c b/ext/opcache/zend_persist.c index ecd0da516335e..dc6d52c444680 100644 --- a/ext/opcache/zend_persist.c +++ b/ext/opcache/zend_persist.c @@ -884,6 +884,9 @@ static int zend_update_parent_ce(zval *zv) if (ce->__debugInfo) { ce->__debugInfo = zend_shared_alloc_get_xlat_entry(ce->__debugInfo); } + if (ce->__compareTo) { + ce->__compareTo = zend_shared_alloc_get_xlat_entry(ce->__compareTo); + } // zend_hash_apply(&ce->properties_info, (apply_func_t) zend_update_property_info_ce); return 0; } From d89cb7766684f26ebe2fb82025952d3e93430974 Mon Sep 17 00:00:00 2001 From: Rudi Theunissen Date: Sun, 24 Jun 2018 08:43:03 -0400 Subject: [PATCH 13/40] Add tests for __compareTo --- Zend/tests/compare/Comparable.inc | 36 ++++++++++ Zend/tests/compare/array-in-array.phpt | 26 +++++++ Zend/tests/compare/array-sort.phpt | 19 +++++ Zend/tests/compare/compare-basic.phpt | 20 ++++++ Zend/tests/compare/compare-inheritance.phpt | 72 +++++++++++++++++++ .../compare/compare-must-not-be-static.phpt | 16 +++++ Zend/tests/compare/compare-normalize.phpt | 38 ++++++++++ .../compare/compare-public-not-static.phpt | 24 +++++++ Zend/tests/compare/compare-to-null.phpt | 30 ++++++++ Zend/tests/compare/compare-to-scalar.phpt | 18 +++++ .../tests/compare/legacy-compare-objects.phpt | 27 +++++++ .../compare/legacy-strict-comparison.phpt | 26 +++++++ Zend/tests/compare/return-false.phpt | 18 +++++ Zend/tests/compare/return-float.phpt | 38 ++++++++++ Zend/tests/compare/return-object.phpt | 19 +++++ Zend/tests/compare/return-true.phpt | 18 +++++ Zend/tests/compare/throw-exception.phpt | 26 +++++++ 17 files changed, 471 insertions(+) create mode 100644 Zend/tests/compare/Comparable.inc create mode 100644 Zend/tests/compare/array-in-array.phpt create mode 100644 Zend/tests/compare/array-sort.phpt create mode 100644 Zend/tests/compare/compare-basic.phpt create mode 100644 Zend/tests/compare/compare-inheritance.phpt create mode 100644 Zend/tests/compare/compare-must-not-be-static.phpt create mode 100644 Zend/tests/compare/compare-normalize.phpt create mode 100644 Zend/tests/compare/compare-public-not-static.phpt create mode 100644 Zend/tests/compare/compare-to-null.phpt create mode 100644 Zend/tests/compare/compare-to-scalar.phpt create mode 100644 Zend/tests/compare/legacy-compare-objects.phpt create mode 100644 Zend/tests/compare/legacy-strict-comparison.phpt create mode 100644 Zend/tests/compare/return-false.phpt create mode 100644 Zend/tests/compare/return-float.phpt create mode 100644 Zend/tests/compare/return-object.phpt create mode 100644 Zend/tests/compare/return-true.phpt create mode 100644 Zend/tests/compare/throw-exception.phpt diff --git a/Zend/tests/compare/Comparable.inc b/Zend/tests/compare/Comparable.inc new file mode 100644 index 0000000000000..8533bcd020f46 --- /dev/null +++ b/Zend/tests/compare/Comparable.inc @@ -0,0 +1,36 @@ +value = $value; + $this->decoy = $decoy; + } + + public function __compareTo($other) { + if ($other instanceof self) { + return $this->value <=> $other->value; + } + + /** + * Allow comparing to scalar numeric values. + */ + if (is_numeric($other)) { + return $this->value <=> $other; + } + + /** + * Return NULL to fall back to default behaviour. This indicates that + * it's an unsupported comparison and most implementations would throw + * an exception here to catch unintended behaviour. + */ + return null; + } +} diff --git a/Zend/tests/compare/array-in-array.phpt b/Zend/tests/compare/array-in-array.phpt new file mode 100644 index 0000000000000..e6115d59b9dda --- /dev/null +++ b/Zend/tests/compare/array-in-array.phpt @@ -0,0 +1,26 @@ +--TEST-- +__compareTo: Supported by array functions with equality semantics (searching) +--FILE-- + +--EXPECTF-- +bool(true) +bool(true) +bool(true) +bool(false) +bool(false) diff --git a/Zend/tests/compare/array-sort.phpt b/Zend/tests/compare/array-sort.phpt new file mode 100644 index 0000000000000..5dab6613ee8d4 --- /dev/null +++ b/Zend/tests/compare/array-sort.phpt @@ -0,0 +1,19 @@ +--TEST-- +__compareTo: Supported by array functions with ordering semantics (sorting) +--FILE-- + +--EXPECTF-- +bool(true) diff --git a/Zend/tests/compare/compare-basic.phpt b/Zend/tests/compare/compare-basic.phpt new file mode 100644 index 0000000000000..32c7be3f137d1 --- /dev/null +++ b/Zend/tests/compare/compare-basic.phpt @@ -0,0 +1,20 @@ +--TEST-- +__compareTo: Basic comparison behaviour +--FILE-- + new Comparable(0, 1)); +var_dump(new Comparable(1, 2) <=> new Comparable(1, 1)); +var_dump(new Comparable(1, 2) <=> new Comparable(2, 1)); + +?> +--EXPECTF-- + +int(1) +int(0) +int(-1) diff --git a/Zend/tests/compare/compare-inheritance.phpt b/Zend/tests/compare/compare-inheritance.phpt new file mode 100644 index 0000000000000..ddda70c42efa7 --- /dev/null +++ b/Zend/tests/compare/compare-inheritance.phpt @@ -0,0 +1,72 @@ +--TEST-- +__compareTo: Inheritance of magic method +--FILE-- + new InheritedComparable(0, 1)); +var_dump(new Comparable(1, 2) <=> new InheritedComparable(1, 1)); +var_dump(new Comparable(1, 2) <=> new InheritedComparable(2, 1)); + +var_dump(new InheritedComparable(1, 2) <=> new Comparable(0, 1)); +var_dump(new InheritedComparable(1, 2) <=> new Comparable(1, 1)); +var_dump(new InheritedComparable(1, 2) <=> new Comparable(2, 1)); + +/** + * Compare against an extending object that overrides the base behaviour. + */ +var_dump(new Comparable(1, 2) <=> new ReversedComparable(0, 1)); +var_dump(new Comparable(1, 2) <=> new ReversedComparable(1, 1)); +var_dump(new Comparable(1, 2) <=> new ReversedComparable(2, 1)); + +var_dump(new ReversedComparable(1, 2) <=> new Comparable(0, 1)); // --. +var_dump(new ReversedComparable(1, 2) <=> new Comparable(1, 1)); // |== These will use ReversedComparable's __compareTo +var_dump(new ReversedComparable(1, 2) <=> new Comparable(2, 1)); // --' + + +?> +--EXPECTF-- + +int(1) +int(0) +int(-1) +int(1) +int(0) +int(-1) +int(1) +int(0) +int(-1) +int(-1) +int(0) +int(1) diff --git a/Zend/tests/compare/compare-must-not-be-static.phpt b/Zend/tests/compare/compare-must-not-be-static.phpt new file mode 100644 index 0000000000000..c7428297defe8 --- /dev/null +++ b/Zend/tests/compare/compare-must-not-be-static.phpt @@ -0,0 +1,16 @@ +--TEST-- +__compareTo: +--FILE-- + +--EXPECTF-- +Warning: The magic method __compareTo() must have public visibility and cannot be static in %s on line %d diff --git a/Zend/tests/compare/compare-normalize.phpt b/Zend/tests/compare/compare-normalize.phpt new file mode 100644 index 0000000000000..1bc4acc9663f0 --- /dev/null +++ b/Zend/tests/compare/compare-normalize.phpt @@ -0,0 +1,38 @@ +--TEST-- +__compareTo: Comparison always returns -1, 0, or 1 +--FILE-- + 1); +var_dump(new B <=> 2); +var_dump(new C <=> 3); + +?> +--EXPECTF-- +int(1) +int(-1) +int(0) diff --git a/Zend/tests/compare/compare-public-not-static.phpt b/Zend/tests/compare/compare-public-not-static.phpt new file mode 100644 index 0000000000000..73c74c1cecb38 --- /dev/null +++ b/Zend/tests/compare/compare-public-not-static.phpt @@ -0,0 +1,24 @@ +--TEST-- +__compareTo: Magic method must be public and non-static +--FILE-- + +--EXPECTF-- +Warning: The magic method __compareTo() must have public visibility and cannot be static in %s on line %d + +Warning: The magic method __compareTo() must have public visibility and cannot be static in %s on line %d + +Warning: The magic method __compareTo() must have public visibility and cannot be static in %s on line %d diff --git a/Zend/tests/compare/compare-to-null.phpt b/Zend/tests/compare/compare-to-null.phpt new file mode 100644 index 0000000000000..a9b5fb05c5e5e --- /dev/null +++ b/Zend/tests/compare/compare-to-null.phpt @@ -0,0 +1,30 @@ +--TEST-- +__compareTo: Compare against NULL +--FILE-- + null); + +/** + * This should also call __compareTo even though we're using the RHS + */ +var_dump(null <=> new Comparable(1)); + +?> +--EXPECTF-- + +string(10) "Comparing!" +int(1) +string(10) "Comparing!" +int(-1) diff --git a/Zend/tests/compare/compare-to-scalar.phpt b/Zend/tests/compare/compare-to-scalar.phpt new file mode 100644 index 0000000000000..d75b4045cf21e --- /dev/null +++ b/Zend/tests/compare/compare-to-scalar.phpt @@ -0,0 +1,18 @@ +--TEST-- +__compareTo: Compare against scalar values +--FILE-- + 0); +var_dump(new Comparable(1) <=> 1.0); +var_dump(new Comparable(1) <=> 1.5); + +?> +--EXPECTF-- +int(1) +int(0) +int(-1) diff --git a/Zend/tests/compare/legacy-compare-objects.phpt b/Zend/tests/compare/legacy-compare-objects.phpt new file mode 100644 index 0000000000000..4c4666dd8d67d --- /dev/null +++ b/Zend/tests/compare/legacy-compare-objects.phpt @@ -0,0 +1,27 @@ +--TEST-- +__compareTo: Objects implementing compare_objects handler are unaffected +--SKIPIF-- + +--FILE-- + $a); +var_dump($a <=> $b); +var_dump($b <=> $a); +var_dump($b <=> $b); + +?> +--EXPECTF-- +int(0) +int(-1) +int(1) +int(0) diff --git a/Zend/tests/compare/legacy-strict-comparison.phpt b/Zend/tests/compare/legacy-strict-comparison.phpt new file mode 100644 index 0000000000000..d71f4271a1898 --- /dev/null +++ b/Zend/tests/compare/legacy-strict-comparison.phpt @@ -0,0 +1,26 @@ +--TEST-- +__compareTo: Check that === isn't affected by __compareTo +--FILE-- + +--EXPECTF-- +bool(true) +bool(false) diff --git a/Zend/tests/compare/return-false.phpt b/Zend/tests/compare/return-false.phpt new file mode 100644 index 0000000000000..62acdf65807dd --- /dev/null +++ b/Zend/tests/compare/return-false.phpt @@ -0,0 +1,18 @@ +--TEST-- +__compareTo: Returning false should be converted to integer 0 +--FILE-- + new A); + +?> +--EXPECTF-- +int(0) diff --git a/Zend/tests/compare/return-float.phpt b/Zend/tests/compare/return-float.phpt new file mode 100644 index 0000000000000..e0dd1587d7abe --- /dev/null +++ b/Zend/tests/compare/return-float.phpt @@ -0,0 +1,38 @@ +--TEST-- +__compareTo: Returning a float should not be truncated to integer +--FILE-- + 1); +var_dump(new B <=> 2); +var_dump(new C <=> 3); + +?> +--EXPECTF-- +int(1) +int(-1) +int(0) diff --git a/Zend/tests/compare/return-object.phpt b/Zend/tests/compare/return-object.phpt new file mode 100644 index 0000000000000..fb3e0f3502672 --- /dev/null +++ b/Zend/tests/compare/return-object.phpt @@ -0,0 +1,19 @@ +--TEST-- +__compareTo: Returning an object should be converted to int +--FILE-- + new A); + +?> +--EXPECTF-- +Notice: Object of class A could not be converted to int in %s on line %d +int(1) diff --git a/Zend/tests/compare/return-true.phpt b/Zend/tests/compare/return-true.phpt new file mode 100644 index 0000000000000..bb18d7707c072 --- /dev/null +++ b/Zend/tests/compare/return-true.phpt @@ -0,0 +1,18 @@ +--TEST-- +__compareTo: Returning true should be converted to integer 1 +--FILE-- + new A); + +?> +--EXPECTF-- +int(1) diff --git a/Zend/tests/compare/throw-exception.phpt b/Zend/tests/compare/throw-exception.phpt new file mode 100644 index 0000000000000..80170551fd4f5 --- /dev/null +++ b/Zend/tests/compare/throw-exception.phpt @@ -0,0 +1,26 @@ +--TEST-- +__compareTo: Throwing exception in __compareTo is caught +--FILE-- + new BrokenComparison; + +?> +--EXPECTF-- + +Fatal error: Uncaught Exception in %s:%d +Stack trace: +#0 %s: BrokenComparison->__compareTo(Object(BrokenComparison)) +#1 {main} + thrown in %s on line %d From 6ec54ec3f029dfda47ac3eef49c06b49b89d9da7 Mon Sep 17 00:00:00 2001 From: Rudi Theunissen Date: Sun, 24 Jun 2018 09:29:48 -0400 Subject: [PATCH 14/40] Revert unintended whitespace changes --- Zend/zend_API.c | 18 ++---------------- Zend/zend_compile.c | 20 ++++++++++---------- Zend/zend_compile.h | 2 +- Zend/zend_object_handlers.c | 4 ++-- Zend/zend_operators.c | 5 ++--- Zend/zend_vm_def.h | 8 ++++---- 6 files changed, 21 insertions(+), 36 deletions(-) diff --git a/Zend/zend_API.c b/Zend/zend_API.c index 94d4972cbdebb..a6805c52e4fef 100644 --- a/Zend/zend_API.c +++ b/Zend/zend_API.c @@ -2163,21 +2163,7 @@ ZEND_API int zend_register_functions(zend_class_entry *scope, const zend_functio int count=0, unload=0; HashTable *target_function_table = function_table; int error_type; - - zend_function - *ctor = NULL, - *dtor = NULL, - *clone = NULL, - *__get = NULL, - *__set = NULL, - *__unset = NULL, - *__isset = NULL, - *__call = NULL, - *__callstatic = NULL, - *__tostring = NULL, - *__debugInfo = NULL, - *__compareTo = NULL; - + zend_function *ctor = NULL, *dtor = NULL, *clone = NULL, *__get = NULL, *__set = NULL, *__unset = NULL, *__isset = NULL, *__call = NULL, *__callstatic = NULL, *__tostring = NULL, *__debugInfo = NULL, *__compareTo = NULL; zend_string *lowercase_name; size_t fname_len; const char *lc_class_name = NULL; @@ -2316,7 +2302,7 @@ ZEND_API int zend_register_functions(zend_class_entry *scope, const zend_functio } if (reg_function->common.arg_info && - (reg_function->common.fn_flags & (ZEND_ACC_HAS_RETURN_TYPE|ZEND_ACC_HAS_TYPE_HINTS))) { + (reg_function->common.fn_flags & (ZEND_ACC_HAS_RETURN_TYPE|ZEND_ACC_HAS_TYPE_HINTS))) { /* convert "const char*" class type names into "zend_string*" */ uint32_t i; uint32_t num_args = reg_function->common.num_args + 1; diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index a4336cf3d5200..d7d7f609e775c 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -1239,7 +1239,7 @@ static void zend_mark_function_as_generator() /* {{{ */ if (ZEND_TYPE_CODE(return_info.type) != IS_ITERABLE) { const char *msg = "Generators may only declare a return type of Generator, Iterator, Traversable, or iterable, %s is not permitted"; - + if (!ZEND_TYPE_IS_CLASS(return_info.type)) { zend_error_noreturn(E_COMPILE_ERROR, msg, zend_get_type_by_const(ZEND_TYPE_CODE(return_info.type))); } @@ -2156,7 +2156,7 @@ static void zend_emit_tick(void) /* {{{ */ if (CG(active_op_array)->last && CG(active_op_array)->opcodes[CG(active_op_array)->last - 1].opcode == ZEND_TICKS) { return; } - + opline = get_next_op(CG(active_op_array)); opline->opcode = ZEND_TICKS; @@ -2607,7 +2607,7 @@ static zend_op *zend_compile_simple_var_no_cv(znode *result, zend_ast *ast, uint opline = zend_emit_op(result, ZEND_FETCH_R, &name_node, NULL); } - if (name_node.op_type == IS_CONST && + if (name_node.op_type == IS_CONST && zend_is_auto_global(Z_STR(name_node.u.constant))) { opline->extended_value = ZEND_FETCH_GLOBAL; @@ -5063,7 +5063,7 @@ void zend_compile_switch(zend_ast *ast) /* {{{ */ if (jumptable) { zval *cond_zv = zend_ast_get_zval(cond_ast); zval jmp_target; - ZVAL_LONG(&jmp_target, get_next_op_number(CG(active_op_array))); + ZVAL_LONG(&jmp_target, get_next_op_number(CG(active_op_array))); ZEND_ASSERT(Z_TYPE_P(cond_zv) == jumptable_type); if (Z_TYPE_P(cond_zv) == IS_LONG) { @@ -5239,7 +5239,7 @@ void zend_compile_try(zend_ast *ast) /* {{{ */ if (finally_ast) { zend_loop_var discard_exception; uint32_t opnum_jmp = get_next_op_number(CG(active_op_array)) + 1; - + /* Pop FAST_CALL from unwind stack */ zend_stack_del_top(&CG(loop_var_stack)); @@ -5493,7 +5493,7 @@ void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast) /* {{{ */ uint32_t i; zend_op_array *op_array = CG(active_op_array); zend_arg_info *arg_infos; - + if (return_type_ast) { zend_bool allow_null = 0; @@ -5637,7 +5637,7 @@ void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast) /* {{{ */ "with a float type can only be float, integer, or NULL"); } break; - + case IS_ITERABLE: if (Z_TYPE(default_node.u.constant) != IS_ARRAY) { zend_error_noreturn(E_COMPILE_ERROR, "Default value for parameters " @@ -5649,7 +5649,7 @@ void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast) /* {{{ */ zend_error_noreturn(E_COMPILE_ERROR, "Default value for parameters " "with an object type can only be NULL"); break; - + default: if (!ZEND_SAME_FAKE_TYPE(ZEND_TYPE_CODE(arg_info->type), Z_TYPE(default_node.u.constant))) { zend_error_noreturn(E_COMPILE_ERROR, "Default value for parameters " @@ -5678,7 +5678,7 @@ void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast) /* {{{ */ if (opline->opcode != ZEND_RECV_INIT) { opline->op2.num = -1; } - } + } } /* These are assigned at the end to avoid uninitialized memory in case of an error */ @@ -7951,7 +7951,7 @@ static void zend_compile_encaps_list(znode *result, zend_ast *ast) /* {{{ */ i = ((j * sizeof(zend_string*)) + (sizeof(zval) - 1)) / sizeof(zval); while (i > 1) { get_temporary_variable(CG(active_op_array)); - i--; + i--; } zend_end_live_range(CG(active_op_array), range, opline - CG(active_op_array)->opcodes, diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h index 052920e48d8c7..53f2fc2fac99a 100644 --- a/Zend/zend_compile.h +++ b/Zend/zend_compile.h @@ -979,7 +979,7 @@ END_EXTERN_C() #define ZEND_AUTOLOAD_FUNC_NAME "__autoload" #define ZEND_INVOKE_FUNC_NAME "__invoke" #define ZEND_DEBUGINFO_FUNC_NAME "__debuginfo" -#define ZEND_COMPARETO_FUNC_NAME "__compareto" +#define ZEND_COMPARETO_FUNC_NAME "__compareto" /* The following constants may be combined in CG(compiler_options) * to change the default compiler behavior */ diff --git a/Zend/zend_object_handlers.c b/Zend/zend_object_handlers.c index 8fc01720d8845..50fb6e0a228b0 100644 --- a/Zend/zend_object_handlers.c +++ b/Zend/zend_object_handlers.c @@ -77,7 +77,7 @@ ZEND_API void rebuild_object_properties(zend_object *zobj) /* {{{ */ HT_FLAGS(zobj->properties) |= HASH_FLAG_HAS_EMPTY_IND; } - _zend_hash_append_ind(zobj->properties, prop_info->name, + _zend_hash_append_ind(zobj->properties, prop_info->name, OBJ_PROP(zobj, prop_info->offset)); } } ZEND_HASH_FOREACH_END(); @@ -684,7 +684,7 @@ ZEND_API zval *zend_std_read_property(zval *object, zval *member, int type, void *guard |= IN_ISSET; zend_std_call_issetter(&tmp_object, member, &tmp_result); *guard &= ~IN_ISSET; - + if (!zend_is_true(&tmp_result)) { retval = &EG(uninitialized_zval); zval_ptr_dtor(&tmp_object); diff --git a/Zend/zend_operators.c b/Zend/zend_operators.c index 6524a6df1b611..804e745b188c4 100644 --- a/Zend/zend_operators.c +++ b/Zend/zend_operators.c @@ -1959,7 +1959,6 @@ static void ZEND_FASTCALL convert_compare_result_to_long(zval *result) /* {{{ */ ZEND_API int ZEND_FASTCALL compare_function(zval *result, zval *op1, zval *op2) /* {{{ */ { - int ret; int converted = 0; zval op1_copy, op2_copy; @@ -3126,7 +3125,7 @@ ZEND_API zend_uchar ZEND_FASTCALL _is_numeric_string_ex(const char *str, size_t } /* }}} */ -/* +/* * String matching - Sunday algorithm * http://www.iti.fh-flensburg.de/lang/algorithmen/pattern/sundayen.htm */ @@ -3210,7 +3209,7 @@ ZEND_API const char* ZEND_FASTCALL zend_memnrstr_ex(const char *haystack, const if (i == needle_len) { return (const char *)p; } - + if (UNEXPECTED(p == haystack)) { return NULL; } diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index b4460a023401a..984cc253adbc8 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -987,7 +987,7 @@ ZEND_VM_INLINE_HELPER(zend_binary_assign_op_helper, VAR|UNUSED|THIS|CV, CONST|TM ZEND_VM_DISPATCH_TO_HELPER(zend_binary_assign_op_dim_helper, binary_op, binary_op); } # endif - + ZEND_VM_DISPATCH_TO_HELPER(zend_binary_assign_op_obj_helper, binary_op, binary_op); #endif } @@ -2501,7 +2501,7 @@ ZEND_VM_HOT_NOCONST_HANDLER(43, ZEND_JMPZ, CONST|TMPVAR|CV, JMP_ADDR) zval *val; val = GET_OP1_ZVAL_PTR_UNDEF(BP_VAR_R); - + if (Z_TYPE_INFO_P(val) == IS_TRUE) { ZEND_VM_NEXT_OPCODE(); } else if (EXPECTED(Z_TYPE_INFO_P(val) <= IS_TRUE)) { @@ -3231,7 +3231,7 @@ ZEND_VM_HANDLER(113, ZEND_INIT_STATIC_METHOD_CALL, UNUSED|CLASS_FETCH|CONST|VAR, if (OP1_TYPE == IS_UNUSED) { /* previous opcode is ZEND_FETCH_CLASS */ - if ((opline->op1.num & ZEND_FETCH_CLASS_MASK) == ZEND_FETCH_CLASS_PARENT || + if ((opline->op1.num & ZEND_FETCH_CLASS_MASK) == ZEND_FETCH_CLASS_PARENT || (opline->op1.num & ZEND_FETCH_CLASS_MASK) == ZEND_FETCH_CLASS_SELF) { if (Z_TYPE(EX(This)) == IS_OBJECT) { ce = Z_OBJCE(EX(This)); @@ -3684,7 +3684,7 @@ ZEND_VM_HOT_HANDLER(60, ZEND_DO_FCALL, ANY, ANY, SPEC(RETVAL)) } else { zend_execute_internal(call, ret); } - + #if ZEND_DEBUG if (!EG(exception) && call->func) { ZEND_ASSERT(!(call->func->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE) || From cde9763469061ebca13a35e15a256ecc7f0aaf19 Mon Sep 17 00:00:00 2001 From: Rudi Theunissen Date: Sun, 24 Jun 2018 10:27:54 -0400 Subject: [PATCH 15/40] Move tests to compareTo dir --- .../compare/compare-must-not-be-static.phpt | 16 ---------------- Zend/tests/{compare => compareTo}/Comparable.inc | 0 .../{compare => compareTo}/array-in-array.phpt | 0 .../tests/{compare => compareTo}/array-sort.phpt | 0 .../{compare => compareTo}/compare-basic.phpt | 0 .../compare-inheritance.phpt | 0 .../compare-normalize.phpt | 0 .../compare-public-not-static.phpt | 0 .../{compare => compareTo}/compare-to-null.phpt | 0 .../compare-to-scalar.phpt | 0 .../legacy-compare-objects.phpt | 0 .../legacy-strict-comparison.phpt | 0 .../{compare => compareTo}/return-false.phpt | 0 .../{compare => compareTo}/return-float.phpt | 0 .../{compare => compareTo}/return-object.phpt | 0 .../{compare => compareTo}/return-true.phpt | 0 .../{compare => compareTo}/throw-exception.phpt | 0 17 files changed, 16 deletions(-) delete mode 100644 Zend/tests/compare/compare-must-not-be-static.phpt rename Zend/tests/{compare => compareTo}/Comparable.inc (100%) rename Zend/tests/{compare => compareTo}/array-in-array.phpt (100%) rename Zend/tests/{compare => compareTo}/array-sort.phpt (100%) rename Zend/tests/{compare => compareTo}/compare-basic.phpt (100%) rename Zend/tests/{compare => compareTo}/compare-inheritance.phpt (100%) rename Zend/tests/{compare => compareTo}/compare-normalize.phpt (100%) rename Zend/tests/{compare => compareTo}/compare-public-not-static.phpt (100%) rename Zend/tests/{compare => compareTo}/compare-to-null.phpt (100%) rename Zend/tests/{compare => compareTo}/compare-to-scalar.phpt (100%) rename Zend/tests/{compare => compareTo}/legacy-compare-objects.phpt (100%) rename Zend/tests/{compare => compareTo}/legacy-strict-comparison.phpt (100%) rename Zend/tests/{compare => compareTo}/return-false.phpt (100%) rename Zend/tests/{compare => compareTo}/return-float.phpt (100%) rename Zend/tests/{compare => compareTo}/return-object.phpt (100%) rename Zend/tests/{compare => compareTo}/return-true.phpt (100%) rename Zend/tests/{compare => compareTo}/throw-exception.phpt (100%) diff --git a/Zend/tests/compare/compare-must-not-be-static.phpt b/Zend/tests/compare/compare-must-not-be-static.phpt deleted file mode 100644 index c7428297defe8..0000000000000 --- a/Zend/tests/compare/compare-must-not-be-static.phpt +++ /dev/null @@ -1,16 +0,0 @@ ---TEST-- -__compareTo: ---FILE-- - ---EXPECTF-- -Warning: The magic method __compareTo() must have public visibility and cannot be static in %s on line %d diff --git a/Zend/tests/compare/Comparable.inc b/Zend/tests/compareTo/Comparable.inc similarity index 100% rename from Zend/tests/compare/Comparable.inc rename to Zend/tests/compareTo/Comparable.inc diff --git a/Zend/tests/compare/array-in-array.phpt b/Zend/tests/compareTo/array-in-array.phpt similarity index 100% rename from Zend/tests/compare/array-in-array.phpt rename to Zend/tests/compareTo/array-in-array.phpt diff --git a/Zend/tests/compare/array-sort.phpt b/Zend/tests/compareTo/array-sort.phpt similarity index 100% rename from Zend/tests/compare/array-sort.phpt rename to Zend/tests/compareTo/array-sort.phpt diff --git a/Zend/tests/compare/compare-basic.phpt b/Zend/tests/compareTo/compare-basic.phpt similarity index 100% rename from Zend/tests/compare/compare-basic.phpt rename to Zend/tests/compareTo/compare-basic.phpt diff --git a/Zend/tests/compare/compare-inheritance.phpt b/Zend/tests/compareTo/compare-inheritance.phpt similarity index 100% rename from Zend/tests/compare/compare-inheritance.phpt rename to Zend/tests/compareTo/compare-inheritance.phpt diff --git a/Zend/tests/compare/compare-normalize.phpt b/Zend/tests/compareTo/compare-normalize.phpt similarity index 100% rename from Zend/tests/compare/compare-normalize.phpt rename to Zend/tests/compareTo/compare-normalize.phpt diff --git a/Zend/tests/compare/compare-public-not-static.phpt b/Zend/tests/compareTo/compare-public-not-static.phpt similarity index 100% rename from Zend/tests/compare/compare-public-not-static.phpt rename to Zend/tests/compareTo/compare-public-not-static.phpt diff --git a/Zend/tests/compare/compare-to-null.phpt b/Zend/tests/compareTo/compare-to-null.phpt similarity index 100% rename from Zend/tests/compare/compare-to-null.phpt rename to Zend/tests/compareTo/compare-to-null.phpt diff --git a/Zend/tests/compare/compare-to-scalar.phpt b/Zend/tests/compareTo/compare-to-scalar.phpt similarity index 100% rename from Zend/tests/compare/compare-to-scalar.phpt rename to Zend/tests/compareTo/compare-to-scalar.phpt diff --git a/Zend/tests/compare/legacy-compare-objects.phpt b/Zend/tests/compareTo/legacy-compare-objects.phpt similarity index 100% rename from Zend/tests/compare/legacy-compare-objects.phpt rename to Zend/tests/compareTo/legacy-compare-objects.phpt diff --git a/Zend/tests/compare/legacy-strict-comparison.phpt b/Zend/tests/compareTo/legacy-strict-comparison.phpt similarity index 100% rename from Zend/tests/compare/legacy-strict-comparison.phpt rename to Zend/tests/compareTo/legacy-strict-comparison.phpt diff --git a/Zend/tests/compare/return-false.phpt b/Zend/tests/compareTo/return-false.phpt similarity index 100% rename from Zend/tests/compare/return-false.phpt rename to Zend/tests/compareTo/return-false.phpt diff --git a/Zend/tests/compare/return-float.phpt b/Zend/tests/compareTo/return-float.phpt similarity index 100% rename from Zend/tests/compare/return-float.phpt rename to Zend/tests/compareTo/return-float.phpt diff --git a/Zend/tests/compare/return-object.phpt b/Zend/tests/compareTo/return-object.phpt similarity index 100% rename from Zend/tests/compare/return-object.phpt rename to Zend/tests/compareTo/return-object.phpt diff --git a/Zend/tests/compare/return-true.phpt b/Zend/tests/compareTo/return-true.phpt similarity index 100% rename from Zend/tests/compare/return-true.phpt rename to Zend/tests/compareTo/return-true.phpt diff --git a/Zend/tests/compare/throw-exception.phpt b/Zend/tests/compareTo/throw-exception.phpt similarity index 100% rename from Zend/tests/compare/throw-exception.phpt rename to Zend/tests/compareTo/throw-exception.phpt From 4f3bd7702e621616994cf6a31f2a2d99f678cb01 Mon Sep 17 00:00:00 2001 From: Rudi Theunissen Date: Sun, 24 Jun 2018 22:05:20 -0400 Subject: [PATCH 16/40] Extend tests that compare objects to null --- Zend/tests/compareTo/compare-to-null.phpt | 24 ++++++++++-- .../compareTo/legacy-compare-to-null.phpt | 39 +++++++++++++++++++ 2 files changed, 60 insertions(+), 3 deletions(-) create mode 100644 Zend/tests/compareTo/legacy-compare-to-null.phpt diff --git a/Zend/tests/compareTo/compare-to-null.phpt b/Zend/tests/compareTo/compare-to-null.phpt index a9b5fb05c5e5e..46779c6df9caf 100644 --- a/Zend/tests/compareTo/compare-to-null.phpt +++ b/Zend/tests/compareTo/compare-to-null.phpt @@ -5,9 +5,17 @@ __compareTo: Compare against NULL class Comparable { + private $value; + + public function __construct($value) + { + $this->value = $value; + } + public function __compareTo($other) { - var_dump("Comparing!"); + echo "Comparing!\n"; + return $this->value <=> $other; } } @@ -21,10 +29,20 @@ var_dump(new Comparable(1) <=> null); */ var_dump(null <=> new Comparable(1)); +/** + * We're doing a non-strict comparison between 0 and NULL here. + */ +var_dump(null <=> new Comparable(0)); +var_dump(new Comparable(0) <=> null); + ?> --EXPECTF-- -string(10) "Comparing!" +Comparing! int(1) -string(10) "Comparing!" +Comparing! int(-1) +Comparing! +int(0) +Comparing! +int(0) diff --git a/Zend/tests/compareTo/legacy-compare-to-null.phpt b/Zend/tests/compareTo/legacy-compare-to-null.phpt new file mode 100644 index 0000000000000..6434741d6e44b --- /dev/null +++ b/Zend/tests/compareTo/legacy-compare-to-null.phpt @@ -0,0 +1,39 @@ +--TEST-- +__compareTo: Check that comparing to NULL is not broken for existing objects +--FILE-- += null); +var_dump(new stdClass <= null); +var_dump(new stdClass < null); +var_dump(new stdClass > null); +var_dump(new stdClass <=> null); + +echo "\n"; + +var_dump(null == new stdClass); +var_dump(null >= new stdClass); +var_dump(null <= new stdClass); +var_dump(null < new stdClass); +var_dump(null > new stdClass); +var_dump(null <=> new stdClass); + +?> +--EXPECTF-- +bool(false) +bool(true) +bool(false) +bool(false) +bool(true) +int(1) + +bool(false) +bool(false) +bool(true) +bool(true) +bool(false) +int(-1) From d79ca4834160757410f7eafcb42e12ba7669beb5 Mon Sep 17 00:00:00 2001 From: Rudi Theunissen Date: Mon, 25 Jun 2018 17:30:30 -0400 Subject: [PATCH 17/40] Add new tests and some for __equals too --- Zend/tests/compareTo/array-in-array.phpt | 26 ------- Zend/tests/compareTo/array-sort.phpt | 19 ----- Zend/tests/compareTo/compare-basic.phpt | 20 ----- Zend/tests/compareTo/compare-inheritance.phpt | 72 ------------------ Zend/tests/compareTo/compare-to-scalar.phpt | 18 ----- .../tests/comparisons/compare-array-sort.phpt | 38 ++++++++++ Zend/tests/comparisons/compare-basic.phpt | 71 ++++++++++++++++++ Zend/tests/comparisons/compare-in-array.phpt | 42 +++++++++++ .../comparisons/compare-inheritance.phpt | 64 ++++++++++++++++ .../compare-normalize.phpt | 0 .../compare-public-not-static.phpt | 0 .../compare-return-false.phpt} | 0 .../compare-return-float.phpt} | 0 .../compare-return-object.phpt} | 0 .../compare-return-true.phpt} | 0 .../compare-throw-exception.phpt} | 0 .../compare-to-null.phpt | 1 - Zend/tests/comparisons/compare-to-scalar.phpt | 30 ++++++++ Zend/tests/comparisons/equals-basic.phpt | 73 +++++++++++++++++++ .../comparisons/equals-before-compare.phpt | 24 ++++++ Zend/tests/comparisons/equals-in-array.phpt | 35 +++++++++ .../comparisons/equals-public-not-static.phpt | 24 ++++++ .../legacy-compare-objects.phpt | 0 .../legacy-compare-to-null.phpt | 0 .../legacy-strict-comparison.phpt | 3 - .../Comparable.inc => comparisons/setup.inc} | 38 ++++++---- 26 files changed, 425 insertions(+), 173 deletions(-) delete mode 100644 Zend/tests/compareTo/array-in-array.phpt delete mode 100644 Zend/tests/compareTo/array-sort.phpt delete mode 100644 Zend/tests/compareTo/compare-basic.phpt delete mode 100644 Zend/tests/compareTo/compare-inheritance.phpt delete mode 100644 Zend/tests/compareTo/compare-to-scalar.phpt create mode 100644 Zend/tests/comparisons/compare-array-sort.phpt create mode 100644 Zend/tests/comparisons/compare-basic.phpt create mode 100644 Zend/tests/comparisons/compare-in-array.phpt create mode 100644 Zend/tests/comparisons/compare-inheritance.phpt rename Zend/tests/{compareTo => comparisons}/compare-normalize.phpt (100%) rename Zend/tests/{compareTo => comparisons}/compare-public-not-static.phpt (100%) rename Zend/tests/{compareTo/return-false.phpt => comparisons/compare-return-false.phpt} (100%) rename Zend/tests/{compareTo/return-float.phpt => comparisons/compare-return-float.phpt} (100%) rename Zend/tests/{compareTo/return-object.phpt => comparisons/compare-return-object.phpt} (100%) rename Zend/tests/{compareTo/return-true.phpt => comparisons/compare-return-true.phpt} (100%) rename Zend/tests/{compareTo/throw-exception.phpt => comparisons/compare-throw-exception.phpt} (100%) rename Zend/tests/{compareTo => comparisons}/compare-to-null.phpt (99%) create mode 100644 Zend/tests/comparisons/compare-to-scalar.phpt create mode 100644 Zend/tests/comparisons/equals-basic.phpt create mode 100644 Zend/tests/comparisons/equals-before-compare.phpt create mode 100644 Zend/tests/comparisons/equals-in-array.phpt create mode 100644 Zend/tests/comparisons/equals-public-not-static.phpt rename Zend/tests/{compareTo => comparisons}/legacy-compare-objects.phpt (100%) rename Zend/tests/{compareTo => comparisons}/legacy-compare-to-null.phpt (100%) rename Zend/tests/{compareTo => comparisons}/legacy-strict-comparison.phpt (78%) rename Zend/tests/{compareTo/Comparable.inc => comparisons/setup.inc} (55%) diff --git a/Zend/tests/compareTo/array-in-array.phpt b/Zend/tests/compareTo/array-in-array.phpt deleted file mode 100644 index e6115d59b9dda..0000000000000 --- a/Zend/tests/compareTo/array-in-array.phpt +++ /dev/null @@ -1,26 +0,0 @@ ---TEST-- -__compareTo: Supported by array functions with equality semantics (searching) ---FILE-- - ---EXPECTF-- -bool(true) -bool(true) -bool(true) -bool(false) -bool(false) diff --git a/Zend/tests/compareTo/array-sort.phpt b/Zend/tests/compareTo/array-sort.phpt deleted file mode 100644 index 5dab6613ee8d4..0000000000000 --- a/Zend/tests/compareTo/array-sort.phpt +++ /dev/null @@ -1,19 +0,0 @@ ---TEST-- -__compareTo: Supported by array functions with ordering semantics (sorting) ---FILE-- - ---EXPECTF-- -bool(true) diff --git a/Zend/tests/compareTo/compare-basic.phpt b/Zend/tests/compareTo/compare-basic.phpt deleted file mode 100644 index 32c7be3f137d1..0000000000000 --- a/Zend/tests/compareTo/compare-basic.phpt +++ /dev/null @@ -1,20 +0,0 @@ ---TEST-- -__compareTo: Basic comparison behaviour ---FILE-- - new Comparable(0, 1)); -var_dump(new Comparable(1, 2) <=> new Comparable(1, 1)); -var_dump(new Comparable(1, 2) <=> new Comparable(2, 1)); - -?> ---EXPECTF-- - -int(1) -int(0) -int(-1) diff --git a/Zend/tests/compareTo/compare-inheritance.phpt b/Zend/tests/compareTo/compare-inheritance.phpt deleted file mode 100644 index ddda70c42efa7..0000000000000 --- a/Zend/tests/compareTo/compare-inheritance.phpt +++ /dev/null @@ -1,72 +0,0 @@ ---TEST-- -__compareTo: Inheritance of magic method ---FILE-- - new InheritedComparable(0, 1)); -var_dump(new Comparable(1, 2) <=> new InheritedComparable(1, 1)); -var_dump(new Comparable(1, 2) <=> new InheritedComparable(2, 1)); - -var_dump(new InheritedComparable(1, 2) <=> new Comparable(0, 1)); -var_dump(new InheritedComparable(1, 2) <=> new Comparable(1, 1)); -var_dump(new InheritedComparable(1, 2) <=> new Comparable(2, 1)); - -/** - * Compare against an extending object that overrides the base behaviour. - */ -var_dump(new Comparable(1, 2) <=> new ReversedComparable(0, 1)); -var_dump(new Comparable(1, 2) <=> new ReversedComparable(1, 1)); -var_dump(new Comparable(1, 2) <=> new ReversedComparable(2, 1)); - -var_dump(new ReversedComparable(1, 2) <=> new Comparable(0, 1)); // --. -var_dump(new ReversedComparable(1, 2) <=> new Comparable(1, 1)); // |== These will use ReversedComparable's __compareTo -var_dump(new ReversedComparable(1, 2) <=> new Comparable(2, 1)); // --' - - -?> ---EXPECTF-- - -int(1) -int(0) -int(-1) -int(1) -int(0) -int(-1) -int(1) -int(0) -int(-1) -int(-1) -int(0) -int(1) diff --git a/Zend/tests/compareTo/compare-to-scalar.phpt b/Zend/tests/compareTo/compare-to-scalar.phpt deleted file mode 100644 index d75b4045cf21e..0000000000000 --- a/Zend/tests/compareTo/compare-to-scalar.phpt +++ /dev/null @@ -1,18 +0,0 @@ ---TEST-- -__compareTo: Compare against scalar values ---FILE-- - 0); -var_dump(new Comparable(1) <=> 1.0); -var_dump(new Comparable(1) <=> 1.5); - -?> ---EXPECTF-- -int(1) -int(0) -int(-1) diff --git a/Zend/tests/comparisons/compare-array-sort.phpt b/Zend/tests/comparisons/compare-array-sort.phpt new file mode 100644 index 0000000000000..e17575f88f952 --- /dev/null +++ b/Zend/tests/comparisons/compare-array-sort.phpt @@ -0,0 +1,38 @@ +--TEST-- +__compareTo: Supported by array functions with ordering semantics (sorting) +--FILE-- + +--EXPECTF-- +object(Comparable)#2 (2) { + ["decoy":protected]=> + int(-1) + ["value":protected]=> + int(1) +} +object(Comparable)#3 (2) { + ["decoy":protected]=> + int(-2) + ["value":protected]=> + int(2) +} +object(Comparable)#1 (2) { + ["decoy":protected]=> + int(-3) + ["value":protected]=> + int(3) +} diff --git a/Zend/tests/comparisons/compare-basic.phpt b/Zend/tests/comparisons/compare-basic.phpt new file mode 100644 index 0000000000000..ba41a96ef6860 --- /dev/null +++ b/Zend/tests/comparisons/compare-basic.phpt @@ -0,0 +1,71 @@ +--TEST-- +__compareTo: Basic comparison behaviour +--FILE-- + new Comparable(0)); // true +var_dump(new Comparable(1) > new Comparable(1)); // false +var_dump(new Comparable(1) > new Comparable(2)); // false + +var_dump(new Comparable(1) <= new Comparable(0)); // false +var_dump(new Comparable(1) <= new Comparable(1)); // true +var_dump(new Comparable(1) <= new Comparable(2)); // true + +var_dump(new Comparable(1) >= new Comparable(0)); // true +var_dump(new Comparable(1) >= new Comparable(1)); // true +var_dump(new Comparable(1) >= new Comparable(2)); // false + +var_dump(new Comparable(1) == new Comparable(0)); // false +var_dump(new Comparable(1) == new Comparable(1)); // true +var_dump(new Comparable(1) == new Comparable(2)); // false + +var_dump(new Comparable(1) != new Comparable(0)); // true +var_dump(new Comparable(1) != new Comparable(1)); // false +var_dump(new Comparable(1) != new Comparable(2)); // true + +var_dump(new Comparable(1) === new Comparable(0)); // false +var_dump(new Comparable(1) === new Comparable(1)); // false +var_dump(new Comparable(1) === new Comparable(2)); // false + +var_dump(new Comparable(1) !== new Comparable(0)); // true +var_dump(new Comparable(1) !== new Comparable(1)); // true +var_dump(new Comparable(1) !== new Comparable(2)); // true + +var_dump(new Comparable(1) <=> new Comparable(0)); // 1 +var_dump(new Comparable(1) <=> new Comparable(1)); // 0 +var_dump(new Comparable(1) <=> new Comparable(2)); // -1 + +?> +--EXPECTF-- +bool(false) +bool(false) +bool(true) +bool(true) +bool(false) +bool(false) +bool(false) +bool(true) +bool(true) +bool(true) +bool(true) +bool(false) +bool(false) +bool(true) +bool(false) +bool(true) +bool(false) +bool(true) +bool(false) +bool(false) +bool(false) +bool(true) +bool(true) +bool(true) +int(1) +int(0) +int(-1) diff --git a/Zend/tests/comparisons/compare-in-array.phpt b/Zend/tests/comparisons/compare-in-array.phpt new file mode 100644 index 0000000000000..68a60ac68334c --- /dev/null +++ b/Zend/tests/comparisons/compare-in-array.phpt @@ -0,0 +1,42 @@ +--TEST-- +__compareTo: Supported by array functions with equality semantics (searching) +--FILE-- + +--EXPECTF-- +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(false) +bool(false) diff --git a/Zend/tests/comparisons/compare-inheritance.phpt b/Zend/tests/comparisons/compare-inheritance.phpt new file mode 100644 index 0000000000000..12bf9d399dccf --- /dev/null +++ b/Zend/tests/comparisons/compare-inheritance.phpt @@ -0,0 +1,64 @@ +--TEST-- +__compareTo: Inheritance of magic method +--FILE-- + new InheritedComparable(0)); // 1 +var_dump(new Comparable(1) <=> new InheritedComparable(1)); // 0 +var_dump(new Comparable(1) <=> new InheritedComparable(2)); // -1 + +var_dump(new InheritedComparable(1) <=> new Comparable(0)); // 1 +var_dump(new InheritedComparable(1) <=> new Comparable(1)); // 0 +var_dump(new InheritedComparable(1) <=> new Comparable(2)); // -1 + +/** + * Compare against an extending object that overrides the base behaviour. + */ +var_dump(new Comparable(1) <=> new ReversedComparable(0)); // 1 +var_dump(new Comparable(1) <=> new ReversedComparable(1)); // 0 +var_dump(new Comparable(1) <=> new ReversedComparable(2)); // -1 + +/** + * These will use ReversedComparable's __compareTo + */ +var_dump(new ReversedComparable(1) <=> new Comparable(0)); // -1 +var_dump(new ReversedComparable(1) <=> new Comparable(1)); // 0 +var_dump(new ReversedComparable(1) <=> new Comparable(2)); // 1 + + +?> +--EXPECTF-- +int(1) +int(0) +int(-1) +int(1) +int(0) +int(-1) +int(1) +int(0) +int(-1) +int(-1) +int(0) +int(1) diff --git a/Zend/tests/compareTo/compare-normalize.phpt b/Zend/tests/comparisons/compare-normalize.phpt similarity index 100% rename from Zend/tests/compareTo/compare-normalize.phpt rename to Zend/tests/comparisons/compare-normalize.phpt diff --git a/Zend/tests/compareTo/compare-public-not-static.phpt b/Zend/tests/comparisons/compare-public-not-static.phpt similarity index 100% rename from Zend/tests/compareTo/compare-public-not-static.phpt rename to Zend/tests/comparisons/compare-public-not-static.phpt diff --git a/Zend/tests/compareTo/return-false.phpt b/Zend/tests/comparisons/compare-return-false.phpt similarity index 100% rename from Zend/tests/compareTo/return-false.phpt rename to Zend/tests/comparisons/compare-return-false.phpt diff --git a/Zend/tests/compareTo/return-float.phpt b/Zend/tests/comparisons/compare-return-float.phpt similarity index 100% rename from Zend/tests/compareTo/return-float.phpt rename to Zend/tests/comparisons/compare-return-float.phpt diff --git a/Zend/tests/compareTo/return-object.phpt b/Zend/tests/comparisons/compare-return-object.phpt similarity index 100% rename from Zend/tests/compareTo/return-object.phpt rename to Zend/tests/comparisons/compare-return-object.phpt diff --git a/Zend/tests/compareTo/return-true.phpt b/Zend/tests/comparisons/compare-return-true.phpt similarity index 100% rename from Zend/tests/compareTo/return-true.phpt rename to Zend/tests/comparisons/compare-return-true.phpt diff --git a/Zend/tests/compareTo/throw-exception.phpt b/Zend/tests/comparisons/compare-throw-exception.phpt similarity index 100% rename from Zend/tests/compareTo/throw-exception.phpt rename to Zend/tests/comparisons/compare-throw-exception.phpt diff --git a/Zend/tests/compareTo/compare-to-null.phpt b/Zend/tests/comparisons/compare-to-null.phpt similarity index 99% rename from Zend/tests/compareTo/compare-to-null.phpt rename to Zend/tests/comparisons/compare-to-null.phpt index 46779c6df9caf..503d2be650eb6 100644 --- a/Zend/tests/compareTo/compare-to-null.phpt +++ b/Zend/tests/comparisons/compare-to-null.phpt @@ -37,7 +37,6 @@ var_dump(new Comparable(0) <=> null); ?> --EXPECTF-- - Comparing! int(1) Comparing! diff --git a/Zend/tests/comparisons/compare-to-scalar.phpt b/Zend/tests/comparisons/compare-to-scalar.phpt new file mode 100644 index 0000000000000..f754738b8df0a --- /dev/null +++ b/Zend/tests/comparisons/compare-to-scalar.phpt @@ -0,0 +1,30 @@ +--TEST-- +__compareTo: Compare against scalar values +--FILE-- + 0); +var_dump(new Comparable(1) <=> 1.0); +var_dump(new Comparable(1) <=> 1.5); + +/** + * We didn't implement something that compares against strings, so __compareTo + * will return null, which will be converted to 0, and therefore equal. + */ +var_dump(new Comparable(1) <=> 'a'); +var_dump(new Comparable(1) <=> 'b'); +var_dump(new Comparable(1) <=> 'c'); + + +?> +--EXPECTF-- +int(1) +int(0) +int(-1) +int(0) +int(0) +int(0) diff --git a/Zend/tests/comparisons/equals-basic.phpt b/Zend/tests/comparisons/equals-basic.phpt new file mode 100644 index 0000000000000..62e20787cc2ce --- /dev/null +++ b/Zend/tests/comparisons/equals-basic.phpt @@ -0,0 +1,73 @@ +--TEST-- +__equals: Basic equality behaviour +--FILE-- + new Equatable(0)); // false +var_dump(new Equatable(1) > new Equatable(1)); // false +var_dump(new Equatable(1) > new Equatable(2)); // true + +var_dump(new Equatable(1) <= new Equatable(0)); // true +var_dump(new Equatable(1) <= new Equatable(1)); // true +var_dump(new Equatable(1) <= new Equatable(2)); // false + +var_dump(new Equatable(1) >= new Equatable(0)); // false +var_dump(new Equatable(1) >= new Equatable(1)); // true +var_dump(new Equatable(1) >= new Equatable(2)); // true + +/* These should use __equals */ +var_dump(new Equatable(1) == new Equatable(0)); // false +var_dump(new Equatable(1) == new Equatable(1)); // true +var_dump(new Equatable(1) == new Equatable(2)); // false + +var_dump(new Equatable(1) != new Equatable(0)); // true +var_dump(new Equatable(1) != new Equatable(1)); // false +var_dump(new Equatable(1) != new Equatable(2)); // true +/****/ + +var_dump(new Equatable(1) === new Equatable(0)); // false +var_dump(new Equatable(1) === new Equatable(1)); // false +var_dump(new Equatable(1) === new Equatable(2)); // false + +var_dump(new Equatable(1) !== new Equatable(0)); // true +var_dump(new Equatable(1) !== new Equatable(1)); // true +var_dump(new Equatable(1) !== new Equatable(2)); // true + +var_dump(new Equatable(1) <=> new Equatable(0)); // -1 +var_dump(new Equatable(1) <=> new Equatable(1)); // 0 +var_dump(new Equatable(1) <=> new Equatable(2)); // 1 + +?> +--EXPECTF-- +bool(true) +bool(false) +bool(false) +bool(false) +bool(false) +bool(true) +bool(true) +bool(true) +bool(false) +bool(false) +bool(true) +bool(true) +bool(false) +bool(true) +bool(false) +bool(true) +bool(false) +bool(true) +bool(false) +bool(false) +bool(false) +bool(true) +bool(true) +bool(true) +int(-1) +int(0) +int(1) diff --git a/Zend/tests/comparisons/equals-before-compare.phpt b/Zend/tests/comparisons/equals-before-compare.phpt new file mode 100644 index 0000000000000..eada7c8e62957 --- /dev/null +++ b/Zend/tests/comparisons/equals-before-compare.phpt @@ -0,0 +1,24 @@ +--TEST-- +__equals: Equal objects automatically have the same ordering +--FILE-- + 1); +var_dump(new A <=> 2); + +?> +--EXPECTF-- +int(1) +int(0) diff --git a/Zend/tests/comparisons/equals-in-array.phpt b/Zend/tests/comparisons/equals-in-array.phpt new file mode 100644 index 0000000000000..2e9ef0776e6af --- /dev/null +++ b/Zend/tests/comparisons/equals-in-array.phpt @@ -0,0 +1,35 @@ +--TEST-- +__equals: Supported by array functions with equality semantics (searching) +--FILE-- + +--EXPECTF-- +bool(true) +bool(true) +bool(true) +bool(false) +bool(false) +bool(false) +bool(false) +bool(false) diff --git a/Zend/tests/comparisons/equals-public-not-static.phpt b/Zend/tests/comparisons/equals-public-not-static.phpt new file mode 100644 index 0000000000000..45dacb786a14e --- /dev/null +++ b/Zend/tests/comparisons/equals-public-not-static.phpt @@ -0,0 +1,24 @@ +--TEST-- +__equals: Magic method must be public and non-static +--FILE-- + +--EXPECTF-- +Warning: The magic method __equals() must have public visibility and cannot be static in %s on line %d + +Warning: The magic method __equals() must have public visibility and cannot be static in %s on line %d + +Warning: The magic method __equals() must have public visibility and cannot be static in %s on line %d diff --git a/Zend/tests/compareTo/legacy-compare-objects.phpt b/Zend/tests/comparisons/legacy-compare-objects.phpt similarity index 100% rename from Zend/tests/compareTo/legacy-compare-objects.phpt rename to Zend/tests/comparisons/legacy-compare-objects.phpt diff --git a/Zend/tests/compareTo/legacy-compare-to-null.phpt b/Zend/tests/comparisons/legacy-compare-to-null.phpt similarity index 100% rename from Zend/tests/compareTo/legacy-compare-to-null.phpt rename to Zend/tests/comparisons/legacy-compare-to-null.phpt diff --git a/Zend/tests/compareTo/legacy-strict-comparison.phpt b/Zend/tests/comparisons/legacy-strict-comparison.phpt similarity index 78% rename from Zend/tests/compareTo/legacy-strict-comparison.phpt rename to Zend/tests/comparisons/legacy-strict-comparison.phpt index d71f4271a1898..adfb320be7c5e 100644 --- a/Zend/tests/compareTo/legacy-strict-comparison.phpt +++ b/Zend/tests/comparisons/legacy-strict-comparison.phpt @@ -3,9 +3,6 @@ __compareTo: Check that === isn't affected by __compareTo --FILE-- decoy = $value * -1; $this->value = $value; - $this->decoy = $decoy; + } +} + +/** + * This is a class that compares by a given $value only. There is also a $decoy + * value that makes sure that the tests would fail if `__equals` is not used. + */ +class Equatable extends Base +{ + public function __equals($other): bool { + return $other instanceof self && $this->value === $other->value; } +} +/** + * This is a class that compares by a given $value only. There is also a $decoy + * value that makes sure that the tests would fail if `__compareTo` is not used. + */ +class Comparable extends Base +{ public function __compareTo($other) { if ($other instanceof self) { return $this->value <=> $other->value; @@ -25,12 +42,5 @@ class Comparable if (is_numeric($other)) { return $this->value <=> $other; } - - /** - * Return NULL to fall back to default behaviour. This indicates that - * it's an unsupported comparison and most implementations would throw - * an exception here to catch unintended behaviour. - */ - return null; } } From 08e0985c9b04258f7d4899bfe36042de756e7762 Mon Sep 17 00:00:00 2001 From: Rudi Theunissen Date: Mon, 25 Jun 2018 17:31:34 -0400 Subject: [PATCH 18/40] Add __equals magic method and equals object handler --- Zend/zend.h | 1 + Zend/zend_API.c | 10 +- Zend/zend_API.h | 1 + Zend/zend_compile.c | 12 + Zend/zend_compile.h | 1 + Zend/zend_inheritance.c | 5 + Zend/zend_object_handlers.c | 29 +- Zend/zend_object_handlers.h | 4 + Zend/zend_operators.c | 66 +- Zend/zend_operators.h | 12 +- ext/mysqli/run-tests.php | 2902 ++++++++++++++++++++++ ext/opcache/tests/bug66338-Officials.inc | 3 + ext/opcache/zend_file_cache.c | 2 + ext/opcache/zend_persist.c | 3 + 14 files changed, 3029 insertions(+), 22 deletions(-) create mode 100644 ext/mysqli/run-tests.php create mode 100644 ext/opcache/tests/bug66338-Officials.inc diff --git a/Zend/zend.h b/Zend/zend.h index f10ea3daa1651..feeaa2f79fb51 100644 --- a/Zend/zend.h +++ b/Zend/zend.h @@ -140,6 +140,7 @@ struct _zend_class_entry { union _zend_function *__tostring; union _zend_function *__debugInfo; union _zend_function *__compareTo; + union _zend_function *__equals; union _zend_function *serialize_func; union _zend_function *unserialize_func; diff --git a/Zend/zend_API.c b/Zend/zend_API.c index a6805c52e4fef..81adce59c6cf3 100644 --- a/Zend/zend_API.c +++ b/Zend/zend_API.c @@ -2163,7 +2163,7 @@ ZEND_API int zend_register_functions(zend_class_entry *scope, const zend_functio int count=0, unload=0; HashTable *target_function_table = function_table; int error_type; - zend_function *ctor = NULL, *dtor = NULL, *clone = NULL, *__get = NULL, *__set = NULL, *__unset = NULL, *__isset = NULL, *__call = NULL, *__callstatic = NULL, *__tostring = NULL, *__debugInfo = NULL, *__compareTo = NULL; + zend_function *ctor = NULL, *dtor = NULL, *clone = NULL, *__get = NULL, *__set = NULL, *__unset = NULL, *__isset = NULL, *__call = NULL, *__callstatic = NULL, *__tostring = NULL, *__debugInfo = NULL, *__compareTo = NULL, *__equals = NULL; zend_string *lowercase_name; size_t fname_len; const char *lc_class_name = NULL; @@ -2357,6 +2357,8 @@ ZEND_API int zend_register_functions(zend_class_entry *scope, const zend_functio __tostring = reg_function; } else if (zend_string_equals_literal(lowercase_name, ZEND_COMPARETO_FUNC_NAME)) { __compareTo = reg_function; + } else if (zend_string_equals_literal(lowercase_name, ZEND_EQUALS_FUNC_NAME)) { + __equals = reg_function; } else if (zend_string_equals_literal(lowercase_name, ZEND_GET_FUNC_NAME)) { __get = reg_function; scope->ce_flags |= ZEND_ACC_USE_GUARDS; @@ -2412,6 +2414,7 @@ ZEND_API int zend_register_functions(zend_class_entry *scope, const zend_functio scope->__isset = __isset; scope->__debugInfo = __debugInfo; scope->__compareTo = __compareTo; + scope->__equals = __equals; if (ctor) { ctor->common.fn_flags |= ZEND_ACC_CTOR; if (ctor->common.fn_flags & ZEND_ACC_STATIC) { @@ -2484,6 +2487,11 @@ ZEND_API int zend_register_functions(zend_class_entry *scope, const zend_functio zend_error(error_type, "Method %s::%s() cannot be static", ZSTR_VAL(scope->name), ZSTR_VAL(__compareTo->common.function_name)); } } + if (__equals) { + if (__equals->common.fn_flags & ZEND_ACC_STATIC) { + zend_error(error_type, "Method %s::%s() cannot be static", ZSTR_VAL(scope->name), ZSTR_VAL(__equals->common.function_name)); + } + } if (ctor && ctor->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE && ctor->common.fn_flags & ZEND_ACC_CTOR) { zend_error_noreturn(E_CORE_ERROR, "Constructor %s::%s() cannot declare a return type", ZSTR_VAL(scope->name), ZSTR_VAL(ctor->common.function_name)); } diff --git a/Zend/zend_API.h b/Zend/zend_API.h index 38187f6ce0f06..bd8f0c9d7fe41 100644 --- a/Zend/zend_API.h +++ b/Zend/zend_API.h @@ -212,6 +212,7 @@ typedef struct _zend_fcall_info_cache { class_container.__isset = NULL; \ class_container.__debugInfo = NULL; \ class_container.__compareTo = NULL; \ + class_container.__equals = NULL; \ class_container.serialize_func = NULL; \ class_container.unserialize_func = NULL; \ class_container.parent = NULL; \ diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index d7d7f609e775c..d42404b19f5c5 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -1764,6 +1764,7 @@ ZEND_API void zend_initialize_class_data(zend_class_entry *ce, zend_bool nullify ce->unserialize_func = NULL; ce->__debugInfo = NULL; ce->__compareTo = NULL; + ce->__equals = NULL; if (ce->type == ZEND_INTERNAL_CLASS) { ce->info.internal.module = NULL; ce->info.internal.builtin_functions = NULL; @@ -5853,6 +5854,11 @@ void zend_begin_method_decl(zend_op_array *op_array, zend_string *name, zend_boo zend_error(E_WARNING, "The magic method __compareTo() must have " "public visibility and cannot be static"); } + } else if (zend_string_equals_literal(lcname, ZEND_EQUALS_FUNC_NAME)) { + if (!is_public || is_static) { + zend_error(E_WARNING, "The magic method __equals() must have " + "public visibility and cannot be static"); + } } } else { if (!in_trait && zend_string_equals_ci(lcname, ce->name)) { @@ -5932,6 +5938,12 @@ void zend_begin_method_decl(zend_op_array *op_array, zend_string *name, zend_boo "public visibility and cannot be static"); } ce->__compareTo = (zend_function *) op_array; + } else if (zend_string_equals_literal(lcname, ZEND_EQUALS_FUNC_NAME)) { + if (!is_public || is_static) { + zend_error(E_WARNING, "The magic method __equals() must have " + "public visibility and cannot be static"); + } + ce->__equals = (zend_function *) op_array; } else if (!is_static) { op_array->fn_flags |= ZEND_ACC_ALLOW_STATIC; } diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h index 53f2fc2fac99a..437a2817698fb 100644 --- a/Zend/zend_compile.h +++ b/Zend/zend_compile.h @@ -980,6 +980,7 @@ END_EXTERN_C() #define ZEND_INVOKE_FUNC_NAME "__invoke" #define ZEND_DEBUGINFO_FUNC_NAME "__debuginfo" #define ZEND_COMPARETO_FUNC_NAME "__compareto" +#define ZEND_EQUALS_FUNC_NAME "__equals" /* The following constants may be combined in CG(compiler_options) * to change the default compiler behavior */ diff --git a/Zend/zend_inheritance.c b/Zend/zend_inheritance.c index 779863e67f6a1..b1073158e61a5 100644 --- a/Zend/zend_inheritance.c +++ b/Zend/zend_inheritance.c @@ -141,6 +141,9 @@ static void do_inherit_parent_constructor(zend_class_entry *ce) /* {{{ */ if (EXPECTED(!ce->__compareTo)) { ce->__compareTo = ce->parent->__compareTo; } + if (EXPECTED(!ce->__equals)) { + ce->__equals = ce->parent->__equals; + } if (ce->constructor) { if (ce->parent->constructor && UNEXPECTED(ce->parent->constructor->common.fn_flags & ZEND_ACC_FINAL)) { @@ -1162,6 +1165,8 @@ static void zend_add_magic_methods(zend_class_entry* ce, zend_string* mname, zen ce->__debugInfo = fe; } else if (zend_string_equals_literal(mname, ZEND_COMPARETO_FUNC_NAME)) { ce->__compareTo = fe; + } else if (zend_string_equals_literal(mname, ZEND_EQUALS_FUNC_NAME)) { + ce->__equals = fe; } } /* }}} */ diff --git a/Zend/zend_object_handlers.c b/Zend/zend_object_handlers.c index 50fb6e0a228b0..f4a5f019e0fee 100644 --- a/Zend/zend_object_handlers.c +++ b/Zend/zend_object_handlers.c @@ -1551,6 +1551,24 @@ ZEND_API zend_function *zend_std_get_constructor(zend_object *zobj) /* {{{ */ } /* }}} */ +ZEND_API int zend_std_equals(zval *result, zval *op1, zval *op2) /* {{{ */ +{ + if (Z_TYPE_P(op1) == IS_OBJECT) { + zend_class_entry *ce = Z_OBJCE_P(op1); + + if (ce->__equals) { + zend_call_method_with_1_params(op1, ce, &ce->__equals, ZEND_EQUALS_FUNC_NAME, result, op2); + + if (Z_TYPE_P(result) != IS_UNDEF) { + return SUCCESS; + } + } + } + + return FAILURE; +} +/* }}} */ + ZEND_API int zend_std_compare(zval *result, zval *op1, zval *op2) /* {{{ */ { if (Z_TYPE_P(op1) == IS_OBJECT) { @@ -1558,14 +1576,10 @@ ZEND_API int zend_std_compare(zval *result, zval *op1, zval *op2) /* {{{ */ if (ce->__compareTo) { zend_call_method_with_1_params(op1, ce, &ce->__compareTo, ZEND_COMPARETO_FUNC_NAME, result, op2); - - /* Returning null indicates that the comparison could not be made or - * isn't supported, falling back to default behaviour. */ - if (Z_TYPE_P(result) == IS_NULL || Z_TYPE_P(result) == IS_UNDEF) { - return FAILURE; + + if (Z_TYPE_P(result) != IS_UNDEF) { + return SUCCESS; } - - return SUCCESS; } } @@ -1886,6 +1900,7 @@ ZEND_API const zend_object_handlers std_object_handlers = { zend_std_get_gc, /* get_gc */ NULL, /* do_operation */ zend_std_compare, /* compare */ + zend_std_equals, /* equals */ }; /* diff --git a/Zend/zend_object_handlers.h b/Zend/zend_object_handlers.h index 62510ff5edcae..1fd869c8496c3 100644 --- a/Zend/zend_object_handlers.h +++ b/Zend/zend_object_handlers.h @@ -112,8 +112,11 @@ typedef zend_object* (*zend_object_clone_obj_t)(zval *object); * Must be defined and must return a non-NULL value. */ typedef zend_string *(*zend_object_get_class_name_t)(const zend_object *object); +/* Comparison and equality + */ typedef int (*zend_object_compare_t)(zval *object1, zval *object2); typedef int (*zend_object_compare_zvals_t)(zval *result, zval *op1, zval *op2); +typedef int (*zend_object_equals_t)(zval *result, zval *op1, zval *op2); /* Cast an object to some other type. * readobj and retval must point to distinct zvals. @@ -162,6 +165,7 @@ struct _zend_object_handlers { zend_object_get_gc_t get_gc; zend_object_do_operation_t do_operation; zend_object_compare_zvals_t compare; + zend_object_equals_t equals; }; BEGIN_EXTERN_C() diff --git a/Zend/zend_operators.c b/Zend/zend_operators.c index 804e745b188c4..3b369ba24055d 100644 --- a/Zend/zend_operators.c +++ b/Zend/zend_operators.c @@ -2026,6 +2026,15 @@ ZEND_API int ZEND_FASTCALL compare_function(zval *result, zval *op1, zval *op2) return SUCCESS; case TYPE_PAIR(IS_OBJECT, IS_NULL): + if (Z_OBJ_HANDLER_P(op1, equals)) { + if (Z_OBJ_HANDLER_P(op1, equals)(result, op1, op2) == SUCCESS) { + if (i_zend_is_true(result)) { + ZVAL_LONG(result, 0); + return SUCCESS; + } + } + } + if (Z_OBJ_HANDLER_P(op1, compare)) { if (Z_OBJ_HANDLER_P(op1, compare)(result, op1, op2) == SUCCESS) { convert_compare_result_to_long(result); @@ -2037,6 +2046,15 @@ ZEND_API int ZEND_FASTCALL compare_function(zval *result, zval *op1, zval *op2) return SUCCESS; case TYPE_PAIR(IS_NULL, IS_OBJECT): + if (Z_OBJ_HANDLER_P(op2, equals)) { + if (Z_OBJ_HANDLER_P(op2, equals)(result, op2, op1) == SUCCESS) { + if (i_zend_is_true(result)) { + ZVAL_LONG(result, 0); + return SUCCESS; + } + } + } + if (Z_OBJ_HANDLER_P(op2, compare)) { if (Z_OBJ_HANDLER_P(op2, compare)(result, op2, op1) == SUCCESS) { convert_compare_result_to_long(result); @@ -2057,17 +2075,29 @@ ZEND_API int ZEND_FASTCALL compare_function(zval *result, zval *op1, zval *op2) continue; } - if (Z_TYPE_P(op1) == IS_OBJECT && Z_OBJ_HANDLER_P(op1, compare)) { + if (Z_TYPE_P(op1) == IS_OBJECT) { + if (Z_OBJ_HANDLER_P(op1, equals) && Z_OBJ_HANDLER_P(op1, equals)(result, op1, op2) == SUCCESS) { + if (i_zend_is_true(result)) { + ZVAL_LONG(result, 0); + return SUCCESS; + } + } - /* If compare fails, we want to fall through to give the - * other comparison functions a chance to run. */ - if (Z_OBJ_HANDLER_P(op1, compare)(result, op1, op2) == SUCCESS) { + if (Z_OBJ_HANDLER_P(op1, compare) && Z_OBJ_HANDLER_P(op1, compare)(result, op1, op2) == SUCCESS) { convert_compare_result_to_long(result); return SUCCESS; } + } + + if (Z_TYPE_P(op2) == IS_OBJECT) { + if (Z_OBJ_HANDLER_P(op2, equals) && Z_OBJ_HANDLER_P(op2, equals)(result, op2, op1) == SUCCESS) { + if (i_zend_is_true(result)) { + ZVAL_LONG(result, 0); + return SUCCESS; + } + } - } else if (Z_TYPE_P(op2) == IS_OBJECT && Z_OBJ_HANDLER_P(op2, compare)) { - if (Z_OBJ_HANDLER_P(op2, compare)(result, op1, op2) == SUCCESS) { + if (Z_OBJ_HANDLER_P(op2, compare) && Z_OBJ_HANDLER_P(op2, compare)(result, op2, op1) == SUCCESS) { convert_compare_result_to_long(result); Z_LVAL_P(result) *= -1; return SUCCESS; @@ -2085,6 +2115,7 @@ ZEND_API int ZEND_FASTCALL compare_function(zval *result, zval *op1, zval *op2) return SUCCESS; } } + if (Z_TYPE_P(op1) == IS_OBJECT) { if (Z_OBJ_HT_P(op1)->get) { zval rv; @@ -2104,6 +2135,7 @@ ZEND_API int ZEND_FASTCALL compare_function(zval *result, zval *op1, zval *op2) return ret; } } + if (Z_TYPE_P(op2) == IS_OBJECT) { if (Z_OBJ_HT_P(op2)->get) { zval rv; @@ -2126,6 +2158,7 @@ ZEND_API int ZEND_FASTCALL compare_function(zval *result, zval *op1, zval *op2) return SUCCESS; } } + if (!converted) { if (Z_TYPE_P(op1) == IS_NULL || Z_TYPE_P(op1) == IS_FALSE) { ZVAL_LONG(result, zval_is_true(op2) ? -1 : 0); @@ -2234,9 +2267,24 @@ ZEND_API int ZEND_FASTCALL is_not_identical_function(zval *result, zval *op1, zv ZEND_API int ZEND_FASTCALL is_equal_function(zval *result, zval *op1, zval *op2) /* {{{ */ { + if (Z_TYPE_P(op1) == IS_OBJECT && Z_OBJ_HANDLER_P(op1, equals)) { + if (Z_OBJ_HANDLER_P(op1, equals)(result, op1, op2) == SUCCESS) { + convert_to_boolean(result); + return SUCCESS; + } + } + + if (Z_TYPE_P(op2) == IS_OBJECT && Z_OBJ_HANDLER_P(op2, equals)) { + if (Z_OBJ_HANDLER_P(op2, equals)(result, op2, op1) == SUCCESS) { + convert_to_boolean(result); + return SUCCESS; + } + } + if (compare_function(result, op1, op2) == FAILURE) { return FAILURE; } + ZVAL_BOOL(result, (Z_LVAL_P(result) == 0)); return SUCCESS; } @@ -2244,10 +2292,12 @@ ZEND_API int ZEND_FASTCALL is_equal_function(zval *result, zval *op1, zval *op2) ZEND_API int ZEND_FASTCALL is_not_equal_function(zval *result, zval *op1, zval *op2) /* {{{ */ { - if (compare_function(result, op1, op2) == FAILURE) { + if (is_equal_function(result, op1, op2) == FAILURE) { return FAILURE; } - ZVAL_BOOL(result, (Z_LVAL_P(result) != 0)); + + /* */ + ZVAL_BOOL(result, Z_TYPE_P(result) == IS_TRUE ? 0 : 1); return SUCCESS; } /* }}} */ diff --git a/Zend/zend_operators.h b/Zend/zend_operators.h index b142bc94dd11f..fd1eb21a52796 100644 --- a/Zend/zend_operators.h +++ b/Zend/zend_operators.h @@ -753,8 +753,8 @@ static zend_always_inline int fast_equal_check_function(zval *op1, zval *op2) return zend_fast_equal_strings(Z_STR_P(op1), Z_STR_P(op2)); } } - compare_function(&result, op1, op2); - return Z_LVAL(result) == 0; + is_equal_function(&result, op1, op2); + return Z_TYPE(result) == IS_TRUE; } static zend_always_inline int fast_equal_check_long(zval *op1, zval *op2) @@ -763,8 +763,8 @@ static zend_always_inline int fast_equal_check_long(zval *op1, zval *op2) if (EXPECTED(Z_TYPE_P(op2) == IS_LONG)) { return Z_LVAL_P(op1) == Z_LVAL_P(op2); } - compare_function(&result, op1, op2); - return Z_LVAL(result) == 0; + is_equal_function(&result, op1, op2); + return Z_TYPE(result) == IS_TRUE; } static zend_always_inline int fast_equal_check_string(zval *op1, zval *op2) @@ -773,8 +773,8 @@ static zend_always_inline int fast_equal_check_string(zval *op1, zval *op2) if (EXPECTED(Z_TYPE_P(op2) == IS_STRING)) { return zend_fast_equal_strings(Z_STR_P(op1), Z_STR_P(op2)); } - compare_function(&result, op1, op2); - return Z_LVAL(result) == 0; + is_equal_function(&result, op1, op2); + return Z_TYPE(result) == IS_TRUE; } static zend_always_inline int fast_is_identical_function(zval *op1, zval *op2) diff --git a/ext/mysqli/run-tests.php b/ext/mysqli/run-tests.php new file mode 100644 index 0000000000000..1991b56a087a4 --- /dev/null +++ b/ext/mysqli/run-tests.php @@ -0,0 +1,2902 @@ +#!/usr/bin/env php + | + | Preston L. Bannister | + | Marcus Boerger | + | Derick Rethans | + | Sander Roobol | + | (based on version by: Stig Bakken ) | + | (based on the PHP 3 test framework by Rasmus Lerdorf) | + +----------------------------------------------------------------------+ + */ + +/* $Id$ */ + +/* Sanity check to ensure that pcre extension needed by this script is available. + * In the event it is not, print a nice error message indicating that this script will + * not run without it. + */ + +if (!extension_loaded('pcre')) { + echo <<'; + save_text($info_file, $php_info); + $info_params = array(); + settings2array($ini_overwrites, $info_params); + settings2params($info_params); + $php_info = `$php $pass_options $info_params $no_file_cache "$info_file"`; + define('TESTED_PHP_VERSION', `$php -n -r "echo PHP_VERSION;"`); + + if ($php_cgi && $php != $php_cgi) { + $php_info_cgi = `$php_cgi $pass_options $info_params $no_file_cache -q "$info_file"`; + $php_info_sep = "\n---------------------------------------------------------------------"; + $php_cgi_info = "$php_info_sep\nPHP : $php_cgi $php_info_cgi$php_info_sep"; + } else { + $php_cgi_info = ''; + } + + if ($phpdbg) { + $phpdbg_info = `$phpdbg $pass_options $info_params $no_file_cache -qrr "$info_file"`; + $php_info_sep = "\n---------------------------------------------------------------------"; + $phpdbg_info = "$php_info_sep\nPHP : $phpdbg $phpdbg_info$php_info_sep"; + } else { + $phpdbg_info = ''; + } + + if (function_exists('opcache_invalidate')) { + opcache_invalidate($info_file, true); + } + @unlink($info_file); + + // load list of enabled extensions + save_text($info_file, ''); + $exts_to_test = explode(',',`$php $pass_options $info_params $no_file_cache "$info_file"`); + // check for extensions that need special handling and regenerate + $info_params_ex = array( + 'session' => array('session.auto_start=0'), + 'tidy' => array('tidy.clean_output=0'), + 'zlib' => array('zlib.output_compression=Off'), + 'xdebug' => array('xdebug.default_enable=0'), + 'mbstring' => array('mbstring.func_overload=0'), + ); + + foreach($info_params_ex as $ext => $ini_overwrites_ex) { + if (in_array($ext, $exts_to_test)) { + $ini_overwrites = array_merge($ini_overwrites, $ini_overwrites_ex); + } + } + + if (function_exists('opcache_invalidate')) { + opcache_invalidate($info_file, true); + } + @unlink($info_file); + + // Write test context information. + echo " +===================================================================== +PHP : $php $php_info $php_cgi_info $phpdbg_info +CWD : $cwd +Extra dirs : "; + foreach ($user_tests as $test_dir) { + echo "{$test_dir}\n "; + } + echo " +VALGRIND : " . ($leak_check ? $valgrind_header : 'Not used') . " +===================================================================== +"; +} + +define('PHP_QA_EMAIL', 'qa-reports@lists.php.net'); +define('QA_SUBMISSION_PAGE', 'http://qa.php.net/buildtest-process.php'); +define('QA_REPORTS_PAGE', 'http://qa.php.net/reports'); +define('TRAVIS_CI' , (bool) getenv('TRAVIS')); + +function save_or_mail_results() +{ + global $sum_results, $just_save_results, $failed_test_summary, + $PHP_FAILED_TESTS, $CUR_DIR, $php, $output_file, $compression; + + /* We got failed Tests, offer the user to send an e-mail to QA team, unless NO_INTERACTION is set */ + if (!getenv('NO_INTERACTION') && !TRAVIS_CI) { + $fp = fopen("php://stdin", "r+"); + if ($sum_results['FAILED'] || $sum_results['BORKED'] || $sum_results['WARNED'] || $sum_results['LEAKED']) { + echo "\nYou may have found a problem in PHP."; + } + echo "\nThis report can be automatically sent to the PHP QA team at\n"; + echo QA_REPORTS_PAGE . " and http://news.php.net/php.qa.reports\n"; + echo "This gives us a better understanding of PHP's behavior.\n"; + echo "If you don't want to send the report immediately you can choose\n"; + echo "option \"s\" to save it. You can then email it to ". PHP_QA_EMAIL . " later.\n"; + echo "Do you want to send this report now? [Yns]: "; + flush(); + + $user_input = fgets($fp, 10); + $just_save_results = (strtolower($user_input[0]) == 's'); + } + + if ($just_save_results || !getenv('NO_INTERACTION') || TRAVIS_CI) { + if ($just_save_results || TRAVIS_CI || strlen(trim($user_input)) == 0 || strtolower($user_input[0]) == 'y') { + /* + * Collect information about the host system for our report + * Fetch phpinfo() output so that we can see the PHP environment + * Make an archive of all the failed tests + * Send an email + */ + if ($just_save_results) { + $user_input = 's'; + } + + /* Ask the user to provide an email address, so that QA team can contact the user */ + if (TRAVIS_CI) { + $user_email = 'travis at php dot net'; + } elseif (!strncasecmp($user_input, 'y', 1) || strlen(trim($user_input)) == 0) { + echo "\nPlease enter your email address.\n(Your address will be mangled so that it will not go out on any\nmailinglist in plain text): "; + flush(); + $user_email = trim(fgets($fp, 1024)); + $user_email = str_replace("@", " at ", str_replace(".", " dot ", $user_email)); + } + + $failed_tests_data = ''; + $sep = "\n" . str_repeat('=', 80) . "\n"; + $failed_tests_data .= $failed_test_summary . "\n"; + $failed_tests_data .= get_summary(true, false) . "\n"; + + if ($sum_results['FAILED']) { + foreach ($PHP_FAILED_TESTS['FAILED'] as $test_info) { + $failed_tests_data .= $sep . $test_info['name'] . $test_info['info']; + $failed_tests_data .= $sep . file_get_contents(realpath($test_info['output']), FILE_BINARY); + $failed_tests_data .= $sep . file_get_contents(realpath($test_info['diff']), FILE_BINARY); + $failed_tests_data .= $sep . "\n\n"; + } + $status = "failed"; + } else { + $status = "success"; + } + + $failed_tests_data .= "\n" . $sep . 'BUILD ENVIRONMENT' . $sep; + $failed_tests_data .= "OS:\n" . PHP_OS . " - " . php_uname() . "\n\n"; + $ldd = $autoconf = $sys_libtool = $libtool = $compiler = 'N/A'; + + if (substr(PHP_OS, 0, 3) != "WIN") { + /* If PHP_AUTOCONF is set, use it; otherwise, use 'autoconf'. */ + if (getenv('PHP_AUTOCONF')) { + $autoconf = shell_exec(getenv('PHP_AUTOCONF') . ' --version'); + } else { + $autoconf = shell_exec('autoconf --version'); + } + + /* Always use the generated libtool - Mac OSX uses 'glibtool' */ + $libtool = shell_exec($CUR_DIR . '/libtool --version'); + + /* Use shtool to find out if there is glibtool present (MacOSX) */ + $sys_libtool_path = shell_exec(__DIR__ . '/build/shtool path glibtool libtool'); + + if ($sys_libtool_path) { + $sys_libtool = shell_exec(str_replace("\n", "", $sys_libtool_path) . ' --version'); + } + + /* Try the most common flags for 'version' */ + $flags = array('-v', '-V', '--version'); + $cc_status = 0; + + foreach($flags AS $flag) { + system(getenv('CC') . " $flag >/dev/null 2>&1", $cc_status); + if ($cc_status == 0) { + $compiler = shell_exec(getenv('CC') . " $flag 2>&1"); + break; + } + } + + $ldd = shell_exec("ldd $php 2>/dev/null"); + } + + $failed_tests_data .= "Autoconf:\n$autoconf\n"; + $failed_tests_data .= "Bundled Libtool:\n$libtool\n"; + $failed_tests_data .= "System Libtool:\n$sys_libtool\n"; + $failed_tests_data .= "Compiler:\n$compiler\n"; + $failed_tests_data .= "Bison:\n". shell_exec('bison --version 2>/dev/null') . "\n"; + $failed_tests_data .= "Libraries:\n$ldd\n"; + $failed_tests_data .= "\n"; + + if (isset($user_email)) { + $failed_tests_data .= "User's E-mail: " . $user_email . "\n\n"; + } + + $failed_tests_data .= $sep . "PHPINFO" . $sep; + $failed_tests_data .= shell_exec($php . ' -ddisplay_errors=stderr -dhtml_errors=0 -i 2> /dev/null'); + + if ($just_save_results || !mail_qa_team($failed_tests_data, $compression, $status) && !TRAVIS_CI) { + file_put_contents($output_file, $failed_tests_data); + + if (!$just_save_results) { + echo "\nThe test script was unable to automatically send the report to PHP's QA Team\n"; + } + + echo "Please send " . $output_file . " to " . PHP_QA_EMAIL . " manually, thank you.\n"; + } elseif (!getenv('NO_INTERACTION') && !TRAVIS_CI) { + fwrite($fp, "\nThank you for helping to make PHP better.\n"); + fclose($fp); + } + } + } +} + +// Determine the tests to be run. + +$test_files = array(); +$redir_tests = array(); +$test_results = array(); +$PHP_FAILED_TESTS = array('BORKED' => array(), 'FAILED' => array(), 'WARNED' => array(), 'LEAKED' => array(), 'XFAILED' => array()); + +// If parameters given assume they represent selected tests to run. +$failed_tests_file= false; +$pass_option_n = false; +$pass_options = ''; + +$compression = 0; +$output_file = $CUR_DIR . '/php_test_results_' . date('Ymd_Hi') . '.txt'; + +if ($compression && in_array("compress.zlib", stream_get_filters())) { + $output_file = 'compress.zlib://' . $output_file . '.gz'; +} + +$just_save_results = false; +$leak_check = false; +$html_output = false; +$html_file = null; +$temp_source = null; +$temp_target = null; +$temp_urlbase = null; +$conf_passed = null; +$no_clean = false; + +$cfgtypes = array('show', 'keep'); +$cfgfiles = array('skip', 'php', 'clean', 'out', 'diff', 'exp'); +$cfg = array(); + +foreach($cfgtypes as $type) { + $cfg[$type] = array(); + + foreach($cfgfiles as $file) { + $cfg[$type][$file] = false; + } +} + +if (getenv('TEST_PHP_ARGS')) { + + if (!isset($argc) || !$argc || !isset($argv)) { + $argv = array(__FILE__); + } + + $argv = array_merge($argv, explode(' ', getenv('TEST_PHP_ARGS'))); + $argc = count($argv); +} + +if (isset($argc) && $argc > 1) { + + for ($i=1; $i<$argc; $i++) { + $is_switch = false; + $switch = substr($argv[$i],1,1); + $repeat = substr($argv[$i],0,1) == '-'; + + while ($repeat) { + + if (!$is_switch) { + $switch = substr($argv[$i],1,1); + } + + $is_switch = true; + + if ($repeat) { + foreach($cfgtypes as $type) { + if (strpos($switch, '--' . $type) === 0) { + foreach($cfgfiles as $file) { + if ($switch == '--' . $type . '-' . $file) { + $cfg[$type][$file] = true; + $is_switch = false; + break; + } + } + } + } + } + + if (!$is_switch) { + $is_switch = true; + break; + } + + $repeat = false; + + switch($switch) { + case 'r': + case 'l': + $test_list = file($argv[++$i]); + if ($test_list) { + foreach($test_list as $test) { + $matches = array(); + if (preg_match('/^#.*\[(.*)\]\:\s+(.*)$/', $test, $matches)) { + $redir_tests[] = array($matches[1], $matches[2]); + } else if (strlen($test)) { + $test_files[] = trim($test); + } + } + } + if ($switch != 'l') { + break; + } + $i--; + // break left intentionally + case 'w': + $failed_tests_file = fopen($argv[++$i], 'w+t'); + break; + case 'a': + $failed_tests_file = fopen($argv[++$i], 'a+t'); + break; + case 'c': + $conf_passed = $argv[++$i]; + break; + case 'd': + $ini_overwrites[] = $argv[++$i]; + break; + case 'g': + $SHOW_ONLY_GROUPS = explode(",", $argv[++$i]); + break; + //case 'h' + case '--keep-all': + foreach($cfgfiles as $file) { + $cfg['keep'][$file] = true; + } + break; + //case 'l' + case 'm': + $leak_check = true; + $valgrind_cmd = "valgrind --version"; + $valgrind_header = system_with_timeout($valgrind_cmd, $environment); + $replace_count = 0; + if (!$valgrind_header) { + error("Valgrind returned no version info, cannot proceed.\nPlease check if Valgrind is installed."); + } else { + $valgrind_version = preg_replace("/valgrind-(\d+)\.(\d+)\.(\d+)([.\w_-]+)?(\s+)/", '$1.$2.$3', $valgrind_header, 1, $replace_count); + if ($replace_count != 1) { + error("Valgrind returned invalid version info (\"$valgrind_header\"), cannot proceed."); + } + $valgrind_header = trim($valgrind_header); + } + break; + case 'n': + if (!$pass_option_n) { + $pass_options .= ' -n'; + } + $pass_option_n = true; + break; + case 'e': + $pass_options .= ' -e'; + break; + case '--no-clean': + $no_clean = true; + break; + case 'p': + $php = $argv[++$i]; + putenv("TEST_PHP_EXECUTABLE=$php"); + $environment['TEST_PHP_EXECUTABLE'] = $php; + break; + case 'P': + if(constant('PHP_BINARY')) { + $php = PHP_BINARY; + } else { + break; + } + putenv("TEST_PHP_EXECUTABLE=$php"); + $environment['TEST_PHP_EXECUTABLE'] = $php; + break; + case 'q': + putenv('NO_INTERACTION=1'); + break; + //case 'r' + case 's': + $output_file = $argv[++$i]; + $just_save_results = true; + break; + case '--set-timeout': + $environment['TEST_TIMEOUT'] = $argv[++$i]; + break; + case '--show-all': + foreach($cfgfiles as $file) { + $cfg['show'][$file] = true; + } + break; + case '--temp-source': + $temp_source = $argv[++$i]; + break; + case '--temp-target': + $temp_target = $argv[++$i]; + if ($temp_urlbase) { + $temp_urlbase = $temp_target; + } + break; + case '--temp-urlbase': + $temp_urlbase = $argv[++$i]; + break; + case 'v': + case '--verbose': + $DETAILED = true; + break; + case 'x': + $environment['SKIP_SLOW_TESTS'] = 1; + break; + case '--offline': + $environment['SKIP_ONLINE_TESTS'] = 1; + break; + //case 'w' + case '-': + // repeat check with full switch + $switch = $argv[$i]; + if ($switch != '-') { + $repeat = true; + } + break; + case '--html': + $html_file = fopen($argv[++$i], 'wt'); + $html_output = is_resource($html_file); + break; + case '--version': + echo '$Id$' . "\n"; + exit(1); + + default: + echo "Illegal switch '$switch' specified!\n"; + case 'h': + case '-help': + case '--help': + echo << Read the testfiles to be executed from . After the test + has finished all failed tests are written to the same . + If the list is empty and no further test is specified then + all tests are executed (same as: -r -w ). + + -r Read the testfiles to be executed from . + + -w Write a list of all failed tests to . + + -a Same as -w but append rather then truncating . + + -c Look for php.ini in directory or use as ini. + + -n Pass -n option to the php binary (Do not use a php.ini). + + -d foo=bar Pass -d option to the php binary (Define INI entry foo + with value 'bar'). + + -g Comma separated list of groups to show during test run + (possible values: PASS, FAIL, XFAIL, SKIP, BORK, WARN, LEAK, REDIRECT). + + -m Test for memory leaks with Valgrind. + + -p Specify PHP executable to run. + + -P Use PHP_BINARY as PHP executable to run. + + -q Quiet, no user interaction (same as environment NO_INTERACTION). + + -s Write output to . + + -x Sets 'SKIP_SLOW_TESTS' environmental variable. + + --offline Sets 'SKIP_ONLINE_TESTS' environmental variable. + + --verbose + -v Verbose mode. + + --help + -h This Help. + + --html Generate HTML output. + + --temp-source --temp-target [--temp-urlbase ] + Write temporary files to by replacing from the + filenames to generate with . If --html is being used and + given then the generated links are relative and prefixed + with the given url. In general you want to make the path + to your source files and some pach in your web page + hierarchy with pointing to . + + --keep-[all|php|skip|clean] + Do not delete 'all' files, 'php' test file, 'skip' or 'clean' + file. + + --set-timeout [n] + Set timeout for individual tests, where [n] is the number of + seconds. The default value is 60 seconds, or 300 seconds when + testing for memory leaks. + + --show-[all|php|skip|clean|exp|diff|out] + Show 'all' files, 'php' test file, 'skip' or 'clean' file. You + can also use this to show the output 'out', the expected result + 'exp' or the difference between them 'diff'. The result types + get written independent of the log format, however 'diff' only + exists when a test fails. + + --no-clean Do not execute clean section if any. + +HELP; + exit(1); + } + } + + if (!$is_switch) { + $testfile = realpath($argv[$i]); + + if (!$testfile && strpos($argv[$i], '*') !== false && function_exists('glob')) { + + if (preg_match("/\.phpt$/", $argv[$i])) { + $pattern_match = glob($argv[$i]); + } else if (preg_match("/\*$/", $argv[$i])) { + $pattern_match = glob($argv[$i] . '.phpt'); + } else { + die("bogus test name " . $argv[$i] . "\n"); + } + + if (is_array($pattern_match)) { + $test_files = array_merge($test_files, $pattern_match); + } + + } else if (is_dir($testfile)) { + find_files($testfile); + } else if (preg_match("/\.phpt$/", $testfile)) { + $test_files[] = $testfile; + } else { + die("bogus test name " . $argv[$i] . "\n"); + } + } + } + + if (strlen($conf_passed)) { + if (substr(PHP_OS, 0, 3) == "WIN") { + $pass_options .= " -c " . escapeshellarg($conf_passed); + } else { + $pass_options .= " -c '$conf_passed'"; + } + } + + $test_files = array_unique($test_files); + $test_files = array_merge($test_files, $redir_tests); + + // Run selected tests. + $test_cnt = count($test_files); + + if ($test_cnt) { + putenv('NO_INTERACTION=1'); + verify_config(); + write_information($html_output); + usort($test_files, "test_sort"); + $start_time = time(); + + if (!$html_output) { + echo "Running selected tests.\n"; + } else { + show_start($start_time); + } + + $test_idx = 0; + run_all_tests($test_files, $environment); + $end_time = time(); + + if ($html_output) { + show_end($end_time); + } + + if ($failed_tests_file) { + fclose($failed_tests_file); + } + + compute_summary(); + if ($html_output) { + fwrite($html_file, "
\n" . get_summary(false, true)); + } + echo "====================================================================="; + echo get_summary(false, false); + + if ($html_output) { + fclose($html_file); + } + + if ($output_file != '' && $just_save_results) { + save_or_mail_results(); + } + + junit_save_xml(); + + if (getenv('REPORT_EXIT_STATUS') == 1 && ($sum_results['FAILED'] || $sum_results['BORKED'])) { + exit(1); + } + + exit(0); + } +} + +verify_config(); +write_information($html_output); + +// Compile a list of all test files (*.phpt). +$test_files = array(); +$exts_tested = count($exts_to_test); +$exts_skipped = 0; +$ignored_by_ext = 0; +sort($exts_to_test); +$test_dirs = array(); +$optionals = array('tests', 'ext', 'Zend', 'sapi'); + +foreach($optionals as $dir) { + if (@filetype($dir) == 'dir') { + $test_dirs[] = $dir; + } +} + +// Convert extension names to lowercase +foreach ($exts_to_test as $key => $val) { + $exts_to_test[$key] = strtolower($val); +} + +foreach ($test_dirs as $dir) { + find_files("{$cwd}/{$dir}", ($dir == 'ext')); +} + +foreach ($user_tests as $dir) { + find_files($dir, ($dir == 'ext')); +} + +function find_files($dir, $is_ext_dir = false, $ignore = false) +{ + global $test_files, $exts_to_test, $ignored_by_ext, $exts_skipped, $exts_tested; + + $o = opendir($dir) or error("cannot open directory: $dir"); + + while (($name = readdir($o)) !== false) { + + if (is_dir("{$dir}/{$name}") && !in_array($name, array('.', '..', '.svn'))) { + $skip_ext = ($is_ext_dir && !in_array(strtolower($name), $exts_to_test)); + if ($skip_ext) { + $exts_skipped++; + } + find_files("{$dir}/{$name}", false, $ignore || $skip_ext); + } + + // Cleanup any left-over tmp files from last run. + if (substr($name, -4) == '.tmp') { + @unlink("$dir/$name"); + continue; + } + + // Otherwise we're only interested in *.phpt files. + if (substr($name, -5) == '.phpt') { + if ($ignore) { + $ignored_by_ext++; + } else { + $testfile = realpath("{$dir}/{$name}"); + $test_files[] = $testfile; + } + } + } + + closedir($o); +} + +function test_name($name) +{ + if (is_array($name)) { + return $name[0] . ':' . $name[1]; + } else { + return $name; + } +} + +function test_sort($a, $b) +{ + global $cwd; + + $a = test_name($a); + $b = test_name($b); + + $ta = strpos($a, "{$cwd}/tests") === 0 ? 1 + (strpos($a, "{$cwd}/tests/run-test") === 0 ? 1 : 0) : 0; + $tb = strpos($b, "{$cwd}/tests") === 0 ? 1 + (strpos($b, "{$cwd}/tests/run-test") === 0 ? 1 : 0) : 0; + + if ($ta == $tb) { + return strcmp($a, $b); + } else { + return $tb - $ta; + } +} + +$test_files = array_unique($test_files); +usort($test_files, "test_sort"); + +$start_time = time(); +show_start($start_time); + +$test_cnt = count($test_files); +$test_idx = 0; +run_all_tests($test_files, $environment); +$end_time = time(); + +if ($failed_tests_file) { + fclose($failed_tests_file); +} + +// Summarize results + +if (0 == count($test_results)) { + echo "No tests were run.\n"; + return; +} + +compute_summary(); + +show_end($end_time); +show_summary(); + +if ($html_output) { + fclose($html_file); +} + +save_or_mail_results(); + +junit_save_xml(); + +if (getenv('REPORT_EXIT_STATUS') == 1 && ($sum_results['FAILED'] || $sum_results['BORKED'])) { + exit(1); +} +exit(0); + +// +// Send Email to QA Team +// + +function mail_qa_team($data, $compression, $status = false) +{ + $url_bits = parse_url(QA_SUBMISSION_PAGE); + + if (($proxy = getenv('http_proxy'))) { + $proxy = parse_url($proxy); + $path = $url_bits['host'].$url_bits['path']; + $host = $proxy['host']; + if (empty($proxy['port'])) { + $proxy['port'] = 80; + } + $port = $proxy['port']; + } else { + $path = $url_bits['path']; + $host = $url_bits['host']; + $port = empty($url_bits['port']) ? 80 : $port = $url_bits['port']; + } + + $data = "php_test_data=" . urlencode(base64_encode(str_replace("\00", '[0x0]', $data))); + $data_length = strlen($data); + + $fs = fsockopen($host, $port, $errno, $errstr, 10); + + if (!$fs) { + return false; + } + + $php_version = urlencode(TESTED_PHP_VERSION); + + echo "\nPosting to ". QA_SUBMISSION_PAGE . "\n"; + fwrite($fs, "POST " . $path . "?status=$status&version=$php_version HTTP/1.1\r\n"); + fwrite($fs, "Host: " . $host . "\r\n"); + fwrite($fs, "User-Agent: QA Browser 0.1\r\n"); + fwrite($fs, "Content-Type: application/x-www-form-urlencoded\r\n"); + fwrite($fs, "Content-Length: " . $data_length . "\r\n\r\n"); + fwrite($fs, $data); + fwrite($fs, "\r\n\r\n"); + fclose($fs); + + return 1; +} + + +// +// Write the given text to a temporary file, and return the filename. +// + +function save_text($filename, $text, $filename_copy = null) +{ + global $DETAILED; + + if ($filename_copy && $filename_copy != $filename) { + if (file_put_contents($filename_copy, $text, FILE_BINARY) === false) { + error("Cannot open file '" . $filename_copy . "' (save_text)"); + } + } + + if (file_put_contents($filename, $text, FILE_BINARY) === false) { + error("Cannot open file '" . $filename . "' (save_text)"); + } + + if (1 < $DETAILED) echo " +FILE $filename {{{ +$text +}}} +"; +} + +// +// Write an error in a format recognizable to Emacs or MSVC. +// + +function error_report($testname, $logname, $tested) +{ + $testname = realpath($testname); + $logname = realpath($logname); + + switch (strtoupper(getenv('TEST_PHP_ERROR_STYLE'))) { + case 'MSVC': + echo $testname . "(1) : $tested\n"; + echo $logname . "(1) : $tested\n"; + break; + case 'EMACS': + echo $testname . ":1: $tested\n"; + echo $logname . ":1: $tested\n"; + break; + } +} + +function system_with_timeout($commandline, $env = null, $stdin = null) +{ + global $leak_check, $cwd; + + $data = ''; + + $bin_env = array(); + foreach((array)$env as $key => $value) { + $bin_env[$key] = $value; + } + + $proc = proc_open($commandline, array( + 0 => array('pipe', 'r'), + 1 => array('pipe', 'w'), + 2 => array('pipe', 'w') + ), $pipes, $cwd, $bin_env, array('suppress_errors' => true, 'binary_pipes' => true)); + + if (!$proc) { + return false; + } + + if (!is_null($stdin)) { + fwrite($pipes[0], $stdin); + } + fclose($pipes[0]); + unset($pipes[0]); + + $timeout = $leak_check ? 300 : (isset($env['TEST_TIMEOUT']) ? $env['TEST_TIMEOUT'] : 60); + + while (true) { + /* hide errors from interrupted syscalls */ + $r = $pipes; + $w = null; + $e = null; + + $n = @stream_select($r, $w, $e, $timeout); + + if ($n === false) { + break; + } else if ($n === 0) { + /* timed out */ + $data .= "\n ** ERROR: process timed out **\n"; + proc_terminate($proc, 9); + return $data; + } else if ($n > 0) { + $line = fread($pipes[1], 8192); + if (strlen($line) == 0) { + /* EOF */ + break; + } + $data .= $line; + } + } + + $stat = proc_get_status($proc); + + if ($stat['signaled']) { + $data .= "\nTermsig=" . $stat['stopsig'] . "\n"; + } + if ($stat["exitcode"] > 128 && $stat["exitcode"] < 160) { + $data .= "\nTermsig=" . ($stat["exitcode"] - 128) . "\n"; + } + + $code = proc_close($proc); + return $data; +} + +function run_all_tests($test_files, $env, $redir_tested = null) +{ + global $test_results, $failed_tests_file, $php, $test_cnt, $test_idx; + + foreach($test_files as $name) { + + if (is_array($name)) { + $index = "# $name[1]: $name[0]"; + + if ($redir_tested) { + $name = $name[0]; + } + } else if ($redir_tested) { + $index = "# $redir_tested: $name"; + } else { + $index = $name; + } + $test_idx++; + $result = run_test($php, $name, $env); + + if (!is_array($name) && $result != 'REDIR') { + $test_results[$index] = $result; + if ($failed_tests_file && ($result == 'XFAILED' || $result == 'FAILED' || $result == 'WARNED' || $result == 'LEAKED')) { + fwrite($failed_tests_file, "$index\n"); + } + } + } +} + +// +// Show file or result block +// +function show_file_block($file, $block, $section = null) +{ + global $cfg; + + if ($cfg['show'][$file]) { + + if (is_null($section)) { + $section = strtoupper($file); + } + + echo "\n========" . $section . "========\n"; + echo rtrim($block); + echo "\n========DONE========\n"; + } +} + +// +// Run an individual test case. +// +function run_test($php, $file, $env) +{ + global $log_format, $info_params, $ini_overwrites, $cwd, $PHP_FAILED_TESTS; + global $pass_options, $DETAILED, $IN_REDIRECT, $test_cnt, $test_idx; + global $leak_check, $temp_source, $temp_target, $cfg, $environment; + global $no_clean; + global $valgrind_version; + global $JUNIT; + global $SHOW_ONLY_GROUPS; + global $no_file_cache; + $temp_filenames = null; + $org_file = $file; + + if (isset($env['TEST_PHP_CGI_EXECUTABLE'])) { + $php_cgi = $env['TEST_PHP_CGI_EXECUTABLE']; + } + + if (isset($env['TEST_PHPDBG_EXECUTABLE'])) { + $phpdbg = $env['TEST_PHPDBG_EXECUTABLE']; + } + + if (is_array($file)) { + $file = $file[0]; + } + + if ($DETAILED) echo " +================= +TEST $file +"; + + // Load the sections of the test file. + $section_text = array('TEST' => ''); + + $fp = fopen($file, "rb") or error("Cannot open test file: $file"); + + $borked = false; + $bork_info = ''; + + if (!feof($fp)) { + $line = fgets($fp); + + if ($line === false) { + $bork_info = "cannot read test"; + $borked = true; + } + } else { + $bork_info = "empty test [$file]"; + $borked = true; + } + if (!$borked && strncmp('--TEST--', $line, 8)) { + $bork_info = "tests must start with --TEST-- [$file]"; + $borked = true; + } + + $section = 'TEST'; + $secfile = false; + $secdone = false; + + while (!feof($fp)) { + $line = fgets($fp); + + if ($line === false) { + break; + } + + // Match the beginning of a section. + if (preg_match('/^--([_A-Z]+)--/', $line, $r)) { + $section = $r[1]; + settype($section, 'string'); + + if (isset($section_text[$section])) { + $bork_info = "duplicated $section section"; + $borked = true; + } + + $section_text[$section] = ''; + $secfile = $section == 'FILE' || $section == 'FILEEOF' || $section == 'FILE_EXTERNAL'; + $secdone = false; + continue; + } + + // Add to the section text. + if (!$secdone) { + $section_text[$section] .= $line; + } + + // End of actual test? + if ($secfile && preg_match('/^===DONE===\s*$/', $line)) { + $secdone = true; + } + } + + // the redirect section allows a set of tests to be reused outside of + // a given test dir + if (!$borked) { + if (@count($section_text['REDIRECTTEST']) == 1) { + + if ($IN_REDIRECT) { + $borked = true; + $bork_info = "Can't redirect a test from within a redirected test"; + } else { + $borked = false; + } + + } else { + + if (!isset($section_text['PHPDBG']) && @count($section_text['FILE']) + @count($section_text['FILEEOF']) + @count($section_text['FILE_EXTERNAL']) != 1) { + $bork_info = "missing section --FILE--"; + $borked = true; + } + + if (@count($section_text['FILEEOF']) == 1) { + $section_text['FILE'] = preg_replace("/[\r\n]+$/", '', $section_text['FILEEOF']); + unset($section_text['FILEEOF']); + } + + foreach (array( 'FILE', 'EXPECT', 'EXPECTF', 'EXPECTREGEX' ) as $prefix) { + $key = $prefix . '_EXTERNAL'; + + if (@count($section_text[$key]) == 1) { + // don't allow tests to retrieve files from anywhere but this subdirectory + $section_text[$key] = dirname($file) . '/' . trim(str_replace('..', '', $section_text[$key])); + + if (file_exists($section_text[$key])) { + $section_text[$prefix] = file_get_contents($section_text[$key], FILE_BINARY); + unset($section_text[$key]); + } else { + $bork_info = "could not load --" . $key . "-- " . dirname($file) . '/' . trim($section_text[$key]); + $borked = true; + } + } + } + + if ((@count($section_text['EXPECT']) + @count($section_text['EXPECTF']) + @count($section_text['EXPECTREGEX'])) != 1) { + $bork_info = "missing section --EXPECT--, --EXPECTF-- or --EXPECTREGEX--"; + $borked = true; + } + } + } + fclose($fp); + + $shortname = str_replace($cwd . '/', '', $file); + $tested_file = $shortname; + + if ($borked) { + show_result("BORK", $bork_info, $tested_file); + $PHP_FAILED_TESTS['BORKED'][] = array ( + 'name' => $file, + 'test_name' => '', + 'output' => '', + 'diff' => '', + 'info' => "$bork_info [$file]", + ); + + junit_mark_test_as('BORK', $shortname, $tested_file, 0, $bork_info); + return 'BORKED'; + } + + $tested = trim($section_text['TEST']); + + /* For GET/POST/PUT tests, check if cgi sapi is available and if it is, use it. */ + if (!empty($section_text['GET']) || !empty($section_text['POST']) || !empty($section_text['GZIP_POST']) || !empty($section_text['DEFLATE_POST']) || !empty($section_text['POST_RAW']) || !empty($section_text['PUT']) || !empty($section_text['COOKIE']) || !empty($section_text['EXPECTHEADERS'])) { + if (isset($php_cgi)) { + $old_php = $php; + $php = $php_cgi . ' -C '; + } else if (!strncasecmp(PHP_OS, "win", 3) && file_exists(dirname($php) . "/php-cgi.exe")) { + $old_php = $php; + $php = realpath(dirname($php) . "/php-cgi.exe") . ' -C '; + } else { + if (file_exists(dirname($php) . "/../../sapi/cgi/php-cgi")) { + $old_php = $php; + $php = realpath(dirname($php) . "/../../sapi/cgi/php-cgi") . ' -C '; + } else if (file_exists("./sapi/cgi/php-cgi")) { + $old_php = $php; + $php = realpath("./sapi/cgi/php-cgi") . ' -C '; + } else if (file_exists(dirname($php) . "/php-cgi")) { + $old_php = $php; + $php = realpath(dirname($php) . "/php-cgi") . ' -C '; + } else { + show_result('SKIP', $tested, $tested_file, "reason: CGI not available"); + + junit_init_suite(junit_get_suitename_for($shortname)); + junit_mark_test_as('SKIP', $shortname, $tested, 0, 'CGI not available'); + return 'SKIPPED'; + } + } + $uses_cgi = true; + } + + /* For phpdbg tests, check if phpdbg sapi is available and if it is, use it. */ + if (array_key_exists('PHPDBG', $section_text)) { + if (!isset($section_text['STDIN'])) { + $section_text['STDIN'] = $section_text['PHPDBG']."\n"; + } + + if (isset($phpdbg)) { + $old_php = $php; + $php = $phpdbg . ' -qIb'; + } else { + show_result('SKIP', $tested, $tested_file, "reason: phpdbg not available"); + + junit_init_suite(junit_get_suitename_for($shortname)); + junit_mark_test_as('SKIP', $shortname, $tested, 0, 'phpdbg not available'); + return 'SKIPPED'; + } + } + + if (!$SHOW_ONLY_GROUPS) { + show_test($test_idx, $shortname); + } + + if (is_array($IN_REDIRECT)) { + $temp_dir = $test_dir = $IN_REDIRECT['dir']; + } else { + $temp_dir = $test_dir = realpath(dirname($file)); + } + + if ($temp_source && $temp_target) { + $temp_dir = str_replace($temp_source, $temp_target, $temp_dir); + } + + $main_file_name = basename($file,'phpt'); + + $diff_filename = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name . 'diff'; + $log_filename = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name . 'log'; + $exp_filename = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name . 'exp'; + $output_filename = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name . 'out'; + $memcheck_filename = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name . 'mem'; + $sh_filename = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name . 'sh'; + $temp_file = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name . 'php'; + $test_file = $test_dir . DIRECTORY_SEPARATOR . $main_file_name . 'php'; + $temp_skipif = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name . 'skip.php'; + $test_skipif = $test_dir . DIRECTORY_SEPARATOR . $main_file_name . 'skip.php'; + $temp_clean = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name . 'clean.php'; + $test_clean = $test_dir . DIRECTORY_SEPARATOR . $main_file_name . 'clean.php'; + $tmp_post = $temp_dir . DIRECTORY_SEPARATOR . uniqid('/phpt.'); + $tmp_relative_file = str_replace(__DIR__ . DIRECTORY_SEPARATOR, '', $test_file) . 't'; + + if ($temp_source && $temp_target) { + $temp_skipif .= 's'; + $temp_file .= 's'; + $temp_clean .= 's'; + $copy_file = $temp_dir . DIRECTORY_SEPARATOR . basename(is_array($file) ? $file[1] : $file) . '.phps'; + + if (!is_dir(dirname($copy_file))) { + mkdir(dirname($copy_file), 0777, true) or error("Cannot create output directory - " . dirname($copy_file)); + } + + if (isset($section_text['FILE'])) { + save_text($copy_file, $section_text['FILE']); + } + + $temp_filenames = array( + 'file' => $copy_file, + 'diff' => $diff_filename, + 'log' => $log_filename, + 'exp' => $exp_filename, + 'out' => $output_filename, + 'mem' => $memcheck_filename, + 'sh' => $sh_filename, + 'php' => $temp_file, + 'skip' => $temp_skipif, + 'clean'=> $temp_clean); + } + + if (is_array($IN_REDIRECT)) { + $tested = $IN_REDIRECT['prefix'] . ' ' . trim($section_text['TEST']); + $tested_file = $tmp_relative_file; + } + + // unlink old test results + @unlink($diff_filename); + @unlink($log_filename); + @unlink($exp_filename); + @unlink($output_filename); + @unlink($memcheck_filename); + @unlink($sh_filename); + @unlink($temp_file); + @unlink($test_file); + @unlink($temp_skipif); + @unlink($test_skipif); + @unlink($tmp_post); + @unlink($temp_clean); + @unlink($test_clean); + + // Reset environment from any previous test. + $env['REDIRECT_STATUS'] = ''; + $env['QUERY_STRING'] = ''; + $env['PATH_TRANSLATED'] = ''; + $env['SCRIPT_FILENAME'] = ''; + $env['REQUEST_METHOD'] = ''; + $env['CONTENT_TYPE'] = ''; + $env['CONTENT_LENGTH'] = ''; + $env['TZ'] = ''; + + if (!empty($section_text['ENV'])) { + + foreach(explode("\n", trim($section_text['ENV'])) as $e) { + $e = explode('=', trim($e), 2); + + if (!empty($e[0]) && isset($e[1])) { + $env[$e[0]] = $e[1]; + } + } + } + + // Default ini settings + $ini_settings = array(); + + // Additional required extensions + if (array_key_exists('EXTENSIONS', $section_text)) { + $ext_dir=`$php -r 'echo ini_get("extension_dir");'`; + $extensions = preg_split("/[\n\r]+/", trim($section_text['EXTENSIONS'])); + $loaded = explode(",", `$php -n -r 'echo join(",", get_loaded_extensions());'`); + foreach ($extensions as $req_ext) { + if (!in_array($req_ext, $loaded)) { + if ($req_ext == 'opcache') { + $ini_settings['zend_extension'][] = $ext_dir . DIRECTORY_SEPARATOR . $req_ext . '.' . PHP_SHLIB_SUFFIX; + } else { + $ini_settings['extension'][] = $ext_dir . DIRECTORY_SEPARATOR . $req_ext . '.' . PHP_SHLIB_SUFFIX; + } + } + } + } + + // additional ini overwrites + //$ini_overwrites[] = 'setting=value'; + settings2array($ini_overwrites, $ini_settings); + + // Any special ini settings + // these may overwrite the test defaults... + if (array_key_exists('INI', $section_text)) { + if (strpos($section_text['INI'], '{PWD}') !== false) { + $section_text['INI'] = str_replace('{PWD}', dirname($file), $section_text['INI']); + } + settings2array(preg_split( "/[\n\r]+/", $section_text['INI']), $ini_settings); + } + + settings2params($ini_settings); + + // Check if test should be skipped. + $info = ''; + $warn = false; + + if (array_key_exists('SKIPIF', $section_text)) { + + if (trim($section_text['SKIPIF'])) { + show_file_block('skip', $section_text['SKIPIF']); + save_text($test_skipif, $section_text['SKIPIF'], $temp_skipif); + $extra = substr(PHP_OS, 0, 3) !== "WIN" ? + "unset REQUEST_METHOD; unset QUERY_STRING; unset PATH_TRANSLATED; unset SCRIPT_FILENAME; unset REQUEST_METHOD;": ""; + + if ($leak_check) { + $env['USE_ZEND_ALLOC'] = '0'; + $env['ZEND_DONT_UNLOAD_MODULES'] = 1; + } else { + $env['USE_ZEND_ALLOC'] = '1'; + $env['ZEND_DONT_UNLOAD_MODULES'] = 0; + } + + junit_start_timer($shortname); + + $output = system_with_timeout("$extra $php $pass_options -q $ini_settings $no_file_cache -d display_errors=0 \"$test_skipif\"", $env); + + junit_finish_timer($shortname); + + if (!$cfg['keep']['skip']) { + @unlink($test_skipif); + } + + if (!strncasecmp('skip', ltrim($output), 4)) { + + if (preg_match('/^\s*skip\s*(.+)\s*/i', $output, $m)) { + show_result('SKIP', $tested, $tested_file, "reason: $m[1]", $temp_filenames); + } else { + show_result('SKIP', $tested, $tested_file, '', $temp_filenames); + } + + if (isset($old_php)) { + $php = $old_php; + } + + if (!$cfg['keep']['skip']) { + @unlink($test_skipif); + } + + $message = !empty($m[1]) ? $m[1] : ''; + junit_mark_test_as('SKIP', $shortname, $tested, null, $message); + return 'SKIPPED'; + } + + if (!strncasecmp('info', ltrim($output), 4)) { + if (preg_match('/^\s*info\s*(.+)\s*/i', $output, $m)) { + $info = " (info: $m[1])"; + } + } + + if (!strncasecmp('warn', ltrim($output), 4)) { + if (preg_match('/^\s*warn\s*(.+)\s*/i', $output, $m)) { + $warn = true; /* only if there is a reason */ + $info = " (warn: $m[1])"; + } + } + + if (!strncasecmp('xfail', ltrim($output), 5)) { + // Pretend we have an XFAIL section + $section_text['XFAIL'] = trim(substr(ltrim($output), 5)); + } + } + } + + if (!extension_loaded("zlib") + && ( array_key_exists("GZIP_POST", $section_text) + || array_key_exists("DEFLATE_POST", $section_text)) + ) { + $message = "ext/zlib required"; + show_result('SKIP', $tested, $tested_file, "reason: $message", $temp_filenames); + junit_mark_test_as('SKIP', $shortname, $tested, null, $message); + return 'SKIPPED'; + } + + if (@count($section_text['REDIRECTTEST']) == 1) { + $test_files = array(); + + $IN_REDIRECT = eval($section_text['REDIRECTTEST']); + $IN_REDIRECT['via'] = "via [$shortname]\n\t"; + $IN_REDIRECT['dir'] = realpath(dirname($file)); + $IN_REDIRECT['prefix'] = trim($section_text['TEST']); + + if (count($IN_REDIRECT['TESTS']) == 1) { + + if (is_array($org_file)) { + $test_files[] = $org_file[1]; + } else { + $GLOBALS['test_files'] = $test_files; + find_files($IN_REDIRECT['TESTS']); + + foreach($GLOBALS['test_files'] as $f) { + $test_files[] = array($f, $file); + } + } + $test_cnt += @count($test_files) - 1; + $test_idx--; + + show_redirect_start($IN_REDIRECT['TESTS'], $tested, $tested_file); + + // set up environment + $redirenv = array_merge($environment, $IN_REDIRECT['ENV']); + $redirenv['REDIR_TEST_DIR'] = realpath($IN_REDIRECT['TESTS']) . DIRECTORY_SEPARATOR; + + usort($test_files, "test_sort"); + run_all_tests($test_files, $redirenv, $tested); + + show_redirect_ends($IN_REDIRECT['TESTS'], $tested, $tested_file); + + // a redirected test never fails + $IN_REDIRECT = false; + + junit_mark_test_as('PASS', $shortname, $tested); + return 'REDIR'; + + } else { + + $bork_info = "Redirect info must contain exactly one TEST string to be used as redirect directory."; + show_result("BORK", $bork_info, '', $temp_filenames); + $PHP_FAILED_TESTS['BORKED'][] = array ( + 'name' => $file, + 'test_name' => '', + 'output' => '', + 'diff' => '', + 'info' => "$bork_info [$file]", + ); + } + } + + if (is_array($org_file) || @count($section_text['REDIRECTTEST']) == 1) { + + if (is_array($org_file)) { + $file = $org_file[0]; + } + + $bork_info = "Redirected test did not contain redirection info"; + show_result("BORK", $bork_info, '', $temp_filenames); + $PHP_FAILED_TESTS['BORKED'][] = array ( + 'name' => $file, + 'test_name' => '', + 'output' => '', + 'diff' => '', + 'info' => "$bork_info [$file]", + ); + + junit_mark_test_as('BORK', $shortname, $tested, null, $bork_info); + + return 'BORKED'; + } + + // We've satisfied the preconditions - run the test! + if (isset($section_text['FILE'])) { + show_file_block('php', $section_text['FILE'], 'TEST'); + save_text($test_file, $section_text['FILE'], $temp_file); + } else { + $test_file = $temp_file = ""; + } + + if (array_key_exists('GET', $section_text)) { + $query_string = trim($section_text['GET']); + } else { + $query_string = ''; + } + + $env['REDIRECT_STATUS'] = '1'; + if (empty($env['QUERY_STRING'])) { + $env['QUERY_STRING'] = $query_string; + } + if (empty($env['PATH_TRANSLATED'])) { + $env['PATH_TRANSLATED'] = $test_file; + } + if (empty($env['SCRIPT_FILENAME'])) { + $env['SCRIPT_FILENAME'] = $test_file; + } + + if (array_key_exists('COOKIE', $section_text)) { + $env['HTTP_COOKIE'] = trim($section_text['COOKIE']); + } else { + $env['HTTP_COOKIE'] = ''; + } + + $args = isset($section_text['ARGS']) ? ' -- ' . $section_text['ARGS'] : ''; + + if (array_key_exists('POST_RAW', $section_text) && !empty($section_text['POST_RAW'])) { + + $post = trim($section_text['POST_RAW']); + $raw_lines = explode("\n", $post); + + $request = ''; + $started = false; + + foreach ($raw_lines as $line) { + + if (empty($env['CONTENT_TYPE']) && preg_match('/^Content-Type:(.*)/i', $line, $res)) { + $env['CONTENT_TYPE'] = trim(str_replace("\r", '', $res[1])); + continue; + } + + if ($started) { + $request .= "\n"; + } + + $started = true; + $request .= $line; + } + + $env['CONTENT_LENGTH'] = strlen($request); + $env['REQUEST_METHOD'] = 'POST'; + + if (empty($request)) { + junit_mark_test_as('BORK', $shortname, $tested, null, 'empty $request'); + return 'BORKED'; + } + + save_text($tmp_post, $request); + $cmd = "$php $pass_options $ini_settings -f \"$test_file\" 2>&1 < \"$tmp_post\""; + + } elseif (array_key_exists('PUT', $section_text) && !empty($section_text['PUT'])) { + + $post = trim($section_text['PUT']); + $raw_lines = explode("\n", $post); + + $request = ''; + $started = false; + + foreach ($raw_lines as $line) { + + if (empty($env['CONTENT_TYPE']) && preg_match('/^Content-Type:(.*)/i', $line, $res)) { + $env['CONTENT_TYPE'] = trim(str_replace("\r", '', $res[1])); + continue; + } + + if ($started) { + $request .= "\n"; + } + + $started = true; + $request .= $line; + } + + $env['CONTENT_LENGTH'] = strlen($request); + $env['REQUEST_METHOD'] = 'PUT'; + + if (empty($request)) { + junit_mark_test_as('BORK', $shortname, $tested, null, 'empty $request'); + return 'BORKED'; + } + + save_text($tmp_post, $request); + $cmd = "$php $pass_options $ini_settings -f \"$test_file\" 2>&1 < \"$tmp_post\""; + + } else if (array_key_exists('POST', $section_text) && !empty($section_text['POST'])) { + + $post = trim($section_text['POST']); + $content_length = strlen($post); + save_text($tmp_post, $post); + + $env['REQUEST_METHOD'] = 'POST'; + if (empty($env['CONTENT_TYPE'])) { + $env['CONTENT_TYPE'] = 'application/x-www-form-urlencoded'; + } + + if (empty($env['CONTENT_LENGTH'])) { + $env['CONTENT_LENGTH'] = $content_length; + } + + $cmd = "$php $pass_options $ini_settings -f \"$test_file\" 2>&1 < \"$tmp_post\""; + + } else if (array_key_exists('GZIP_POST', $section_text) && !empty($section_text['GZIP_POST'])) { + + $post = trim($section_text['GZIP_POST']); + $post = gzencode($post, 9, FORCE_GZIP); + $env['HTTP_CONTENT_ENCODING'] = 'gzip'; + + save_text($tmp_post, $post); + $content_length = strlen($post); + + $env['REQUEST_METHOD'] = 'POST'; + $env['CONTENT_TYPE'] = 'application/x-www-form-urlencoded'; + $env['CONTENT_LENGTH'] = $content_length; + + $cmd = "$php $pass_options $ini_settings -f \"$test_file\" 2>&1 < \"$tmp_post\""; + + } else if (array_key_exists('DEFLATE_POST', $section_text) && !empty($section_text['DEFLATE_POST'])) { + $post = trim($section_text['DEFLATE_POST']); + $post = gzcompress($post, 9); + $env['HTTP_CONTENT_ENCODING'] = 'deflate'; + save_text($tmp_post, $post); + $content_length = strlen($post); + + $env['REQUEST_METHOD'] = 'POST'; + $env['CONTENT_TYPE'] = 'application/x-www-form-urlencoded'; + $env['CONTENT_LENGTH'] = $content_length; + + $cmd = "$php $pass_options $ini_settings -f \"$test_file\" 2>&1 < \"$tmp_post\""; + + } else { + + $env['REQUEST_METHOD'] = 'GET'; + $env['CONTENT_TYPE'] = ''; + $env['CONTENT_LENGTH'] = ''; + + $cmd = "$php $pass_options $ini_settings -f \"$test_file\" $args 2>&1"; + } + + if ($leak_check) { + $env['USE_ZEND_ALLOC'] = '0'; + $env['ZEND_DONT_UNLOAD_MODULES'] = 1; + + /* --vex-iropt-register-updates=allregs-at-mem-access is necessary for phpdbg watchpoint tests */ + if (version_compare($valgrind_version, '3.8.0', '>=')) { + /* valgrind 3.3.0+ doesn't have --log-file-exactly option */ + $cmd = "valgrind -q --tool=memcheck --trace-children=yes --vex-iropt-register-updates=allregs-at-mem-access --log-file=$memcheck_filename $cmd"; + } elseif (version_compare($valgrind_version, '3.3.0', '>=')) { + $cmd = "valgrind -q --tool=memcheck --trace-children=yes --vex-iropt-precise-memory-exns=yes --log-file=$memcheck_filename $cmd"; + } else { + $cmd = "valgrind -q --tool=memcheck --trace-children=yes --vex-iropt-precise-memory-exns=yes --log-file-exactly=$memcheck_filename $cmd"; + } + + } else { + $env['USE_ZEND_ALLOC'] = '1'; + $env['ZEND_DONT_UNLOAD_MODULES'] = 0; + } + + if ($DETAILED) echo " +CONTENT_LENGTH = " . $env['CONTENT_LENGTH'] . " +CONTENT_TYPE = " . $env['CONTENT_TYPE'] . " +PATH_TRANSLATED = " . $env['PATH_TRANSLATED'] . " +QUERY_STRING = " . $env['QUERY_STRING'] . " +REDIRECT_STATUS = " . $env['REDIRECT_STATUS'] . " +REQUEST_METHOD = " . $env['REQUEST_METHOD'] . " +SCRIPT_FILENAME = " . $env['SCRIPT_FILENAME'] . " +HTTP_COOKIE = " . $env['HTTP_COOKIE'] . " +COMMAND $cmd +"; + + junit_start_timer($shortname); + + $out = system_with_timeout($cmd, $env, isset($section_text['STDIN']) ? $section_text['STDIN'] : null); + + junit_finish_timer($shortname); + + if (array_key_exists('CLEAN', $section_text) && (!$no_clean || $cfg['keep']['clean'])) { + + if (trim($section_text['CLEAN'])) { + show_file_block('clean', $section_text['CLEAN']); + save_text($test_clean, trim($section_text['CLEAN']), $temp_clean); + + if (!$no_clean) { + $clean_params = array(); + settings2array($ini_overwrites, $clean_params); + settings2params($clean_params); + $extra = substr(PHP_OS, 0, 3) !== "WIN" ? + "unset REQUEST_METHOD; unset QUERY_STRING; unset PATH_TRANSLATED; unset SCRIPT_FILENAME; unset REQUEST_METHOD;": ""; + system_with_timeout("$extra $php $pass_options -q $clean_params $no_file_cache \"$test_clean\"", $env); + } + + if (!$cfg['keep']['clean']) { + @unlink($test_clean); + } + } + } + + @unlink($tmp_post); + + $leaked = false; + $passed = false; + + if ($leak_check) { // leak check + $leaked = filesize($memcheck_filename) > 0; + + if (!$leaked) { + @unlink($memcheck_filename); + } + } + + // Does the output match what is expected? + $output = preg_replace("/\r\n/", "\n", trim($out)); + + /* when using CGI, strip the headers from the output */ + $headers = ""; + + if (!empty($uses_cgi) && preg_match("/^(.*?)\r?\n\r?\n(.*)/s", $out, $match)) { + $output = trim($match[2]); + $rh = preg_split("/[\n\r]+/", $match[1]); + $headers = array(); + + foreach ($rh as $line) { + if (strpos($line, ':') !== false) { + $line = explode(':', $line, 2); + $headers[trim($line[0])] = trim($line[1]); + } + } + } + + $failed_headers = false; + + if (isset($section_text['EXPECTHEADERS'])) { + $want = array(); + $wanted_headers = array(); + $lines = preg_split("/[\n\r]+/", $section_text['EXPECTHEADERS']); + + foreach($lines as $line) { + if (strpos($line, ':') !== false) { + $line = explode(':', $line, 2); + $want[trim($line[0])] = trim($line[1]); + $wanted_headers[] = trim($line[0]) . ': ' . trim($line[1]); + } + } + + $org_headers = $headers; + $headers = array(); + $output_headers = array(); + + foreach($want as $k => $v) { + + if (isset($org_headers[$k])) { + $headers = $org_headers[$k]; + $output_headers[] = $k . ': ' . $org_headers[$k]; + } + + if (!isset($org_headers[$k]) || $org_headers[$k] != $v) { + $failed_headers = true; + } + } + + ksort($wanted_headers); + $wanted_headers = join("\n", $wanted_headers); + ksort($output_headers); + $output_headers = join("\n", $output_headers); + } + + show_file_block('out', $output); + + if (isset($section_text['EXPECTF']) || isset($section_text['EXPECTREGEX'])) { + + if (isset($section_text['EXPECTF'])) { + $wanted = trim($section_text['EXPECTF']); + } else { + $wanted = trim($section_text['EXPECTREGEX']); + } + + show_file_block('exp', $wanted); + $wanted_re = preg_replace('/\r\n/', "\n", $wanted); + + if (isset($section_text['EXPECTF'])) { + + // do preg_quote, but miss out any %r delimited sections + $temp = ""; + $r = "%r"; + $startOffset = 0; + $length = strlen($wanted_re); + while($startOffset < $length) { + $start = strpos($wanted_re, $r, $startOffset); + if ($start !== false) { + // we have found a start tag + $end = strpos($wanted_re, $r, $start+2); + if ($end === false) { + // unbalanced tag, ignore it. + $end = $start = $length; + } + } else { + // no more %r sections + $start = $end = $length; + } + // quote a non re portion of the string + $temp = $temp . preg_quote(substr($wanted_re, $startOffset, ($start - $startOffset)), '/'); + // add the re unquoted. + if ($end > $start) { + $temp = $temp . '(' . substr($wanted_re, $start+2, ($end - $start-2)). ')'; + } + $startOffset = $end + 2; + } + $wanted_re = $temp; + + $wanted_re = str_replace( + array('%binary_string_optional%'), + 'string', + $wanted_re + ); + $wanted_re = str_replace( + array('%unicode_string_optional%'), + 'string', + $wanted_re + ); + $wanted_re = str_replace( + array('%unicode\|string%', '%string\|unicode%'), + 'string', + $wanted_re + ); + $wanted_re = str_replace( + array('%u\|b%', '%b\|u%'), + '', + $wanted_re + ); + // Stick to basics + $wanted_re = str_replace('%e', '\\' . DIRECTORY_SEPARATOR, $wanted_re); + $wanted_re = str_replace('%s', '[^\r\n]+', $wanted_re); + $wanted_re = str_replace('%S', '[^\r\n]*', $wanted_re); + $wanted_re = str_replace('%a', '.+', $wanted_re); + $wanted_re = str_replace('%A', '.*', $wanted_re); + $wanted_re = str_replace('%w', '\s*', $wanted_re); + $wanted_re = str_replace('%i', '[+-]?\d+', $wanted_re); + $wanted_re = str_replace('%d', '\d+', $wanted_re); + $wanted_re = str_replace('%x', '[0-9a-fA-F]+', $wanted_re); + $wanted_re = str_replace('%f', '[+-]?\.?\d+\.?\d*(?:[Ee][+-]?\d+)?', $wanted_re); + $wanted_re = str_replace('%c', '.', $wanted_re); + // %f allows two points "-.0.0" but that is the best *simple* expression + } +/* DEBUG YOUR REGEX HERE + var_dump($wanted_re); + print(str_repeat('=', 80) . "\n"); + var_dump($output); +*/ + if (preg_match("/^$wanted_re\$/s", $output)) { + $passed = true; + if (!$cfg['keep']['php']) { + @unlink($test_file); + } + if (isset($old_php)) { + $php = $old_php; + } + + if (!$leaked && !$failed_headers) { + if (isset($section_text['XFAIL'] )) { + $warn = true; + $info = " (warn: XFAIL section but test passes)"; + }else { + show_result("PASS", $tested, $tested_file, '', $temp_filenames); + junit_mark_test_as('PASS', $shortname, $tested); + return 'PASSED'; + } + } + } + + } else { + + $wanted = trim($section_text['EXPECT']); + $wanted = preg_replace('/\r\n/',"\n", $wanted); + show_file_block('exp', $wanted); + + // compare and leave on success + if (!strcmp($output, $wanted)) { + $passed = true; + + if (!$cfg['keep']['php']) { + @unlink($test_file); + } + + if (isset($old_php)) { + $php = $old_php; + } + + if (!$leaked && !$failed_headers) { + if (isset($section_text['XFAIL'] )) { + $warn = true; + $info = " (warn: XFAIL section but test passes)"; + }else { + show_result("PASS", $tested, $tested_file, '', $temp_filenames); + junit_mark_test_as('PASS', $shortname, $tested); + return 'PASSED'; + } + } + } + + $wanted_re = null; + } + + // Test failed so we need to report details. + if ($failed_headers) { + $passed = false; + $wanted = $wanted_headers . "\n--HEADERS--\n" . $wanted; + $output = $output_headers . "\n--HEADERS--\n" . $output; + + if (isset($wanted_re)) { + $wanted_re = preg_quote($wanted_headers . "\n--HEADERS--\n", '/') . $wanted_re; + } + } + + if ($leaked) { + $restype[] = 'LEAK'; + } + + if ($warn) { + $restype[] = 'WARN'; + } + + if (!$passed) { + if (isset($section_text['XFAIL'])) { + $restype[] = 'XFAIL'; + $info = ' XFAIL REASON: ' . rtrim($section_text['XFAIL']); + } else { + $restype[] = 'FAIL'; + } + } + + if (!$passed) { + + // write .exp + if (strpos($log_format, 'E') !== false && file_put_contents($exp_filename, $wanted, FILE_BINARY) === false) { + error("Cannot create expected test output - $exp_filename"); + } + + // write .out + if (strpos($log_format, 'O') !== false && file_put_contents($output_filename, $output, FILE_BINARY) === false) { + error("Cannot create test output - $output_filename"); + } + + // write .diff + $diff = generate_diff($wanted, $wanted_re, $output); + if (is_array($IN_REDIRECT)) { + $diff = "# original source file: $shortname\n" . $diff; + } + show_file_block('diff', $diff); + if (strpos($log_format, 'D') !== false && file_put_contents($diff_filename, $diff, FILE_BINARY) === false) { + error("Cannot create test diff - $diff_filename"); + } + + // write .sh + if (strpos($log_format, 'S') !== false && file_put_contents($sh_filename, "#!/bin/sh + +{$cmd} +", FILE_BINARY) === false) { + error("Cannot create test shell script - $sh_filename"); + } + chmod($sh_filename, 0755); + + // write .log + if (strpos($log_format, 'L') !== false && file_put_contents($log_filename, " +---- EXPECTED OUTPUT +$wanted +---- ACTUAL OUTPUT +$output +---- FAILED +", FILE_BINARY) === false) { + error("Cannot create test log - $log_filename"); + error_report($file, $log_filename, $tested); + } + } + + show_result(implode('&', $restype), $tested, $tested_file, $info, $temp_filenames); + + foreach ($restype as $type) { + $PHP_FAILED_TESTS[$type.'ED'][] = array ( + 'name' => $file, + 'test_name' => (is_array($IN_REDIRECT) ? $IN_REDIRECT['via'] : '') . $tested . " [$tested_file]", + 'output' => $output_filename, + 'diff' => $diff_filename, + 'info' => $info, + ); + } + + if (isset($old_php)) { + $php = $old_php; + } + + $diff = empty($diff) ? '' : preg_replace('/\e/', '', $diff); + + junit_mark_test_as($restype, str_replace($cwd . '/', '', $tested_file), $tested, null, $info, $diff); + + return $restype[0] . 'ED'; +} + +function comp_line($l1, $l2, $is_reg) +{ + if ($is_reg) { + return preg_match('/^'. $l1 . '$/s', $l2); + } else { + return !strcmp($l1, $l2); + } +} + +function count_array_diff($ar1, $ar2, $is_reg, $w, $idx1, $idx2, $cnt1, $cnt2, $steps) +{ + $equal = 0; + + while ($idx1 < $cnt1 && $idx2 < $cnt2 && comp_line($ar1[$idx1], $ar2[$idx2], $is_reg)) { + $idx1++; + $idx2++; + $equal++; + $steps--; + } + if (--$steps > 0) { + $eq1 = 0; + $st = $steps / 2; + + for ($ofs1 = $idx1 + 1; $ofs1 < $cnt1 && $st-- > 0; $ofs1++) { + $eq = @count_array_diff($ar1, $ar2, $is_reg, $w, $ofs1, $idx2, $cnt1, $cnt2, $st); + + if ($eq > $eq1) { + $eq1 = $eq; + } + } + + $eq2 = 0; + $st = $steps; + + for ($ofs2 = $idx2 + 1; $ofs2 < $cnt2 && $st-- > 0; $ofs2++) { + $eq = @count_array_diff($ar1, $ar2, $is_reg, $w, $idx1, $ofs2, $cnt1, $cnt2, $st); + if ($eq > $eq2) { + $eq2 = $eq; + } + } + + if ($eq1 > $eq2) { + $equal += $eq1; + } else if ($eq2 > 0) { + $equal += $eq2; + } + } + + return $equal; +} + +function generate_array_diff($ar1, $ar2, $is_reg, $w) +{ + $idx1 = 0; $ofs1 = 0; $cnt1 = @count($ar1); + $idx2 = 0; $ofs2 = 0; $cnt2 = @count($ar2); + $diff = array(); + $old1 = array(); + $old2 = array(); + + while ($idx1 < $cnt1 && $idx2 < $cnt2) { + + if (comp_line($ar1[$idx1], $ar2[$idx2], $is_reg)) { + $idx1++; + $idx2++; + continue; + } else { + + $c1 = @count_array_diff($ar1, $ar2, $is_reg, $w, $idx1+1, $idx2, $cnt1, $cnt2, 10); + $c2 = @count_array_diff($ar1, $ar2, $is_reg, $w, $idx1, $idx2+1, $cnt1, $cnt2, 10); + + if ($c1 > $c2) { + $old1[$idx1] = sprintf("%03d- ", $idx1+1) . $w[$idx1++]; + $last = 1; + } else if ($c2 > 0) { + $old2[$idx2] = sprintf("%03d+ ", $idx2+1) . $ar2[$idx2++]; + $last = 2; + } else { + $old1[$idx1] = sprintf("%03d- ", $idx1+1) . $w[$idx1++]; + $old2[$idx2] = sprintf("%03d+ ", $idx2+1) . $ar2[$idx2++]; + } + } + } + + reset($old1); $k1 = key($old1); $l1 = -2; + reset($old2); $k2 = key($old2); $l2 = -2; + + while ($k1 !== null || $k2 !== null) { + + if ($k1 == $l1 + 1 || $k2 === null) { + $l1 = $k1; + $diff[] = current($old1); + $k1 = next($old1) ? key($old1) : null; + } else if ($k2 == $l2 + 1 || $k1 === null) { + $l2 = $k2; + $diff[] = current($old2); + $k2 = next($old2) ? key($old2) : null; + } else if ($k1 < $k2) { + $l1 = $k1; + $diff[] = current($old1); + $k1 = next($old1) ? key($old1) : null; + } else { + $l2 = $k2; + $diff[] = current($old2); + $k2 = next($old2) ? key($old2) : null; + } + } + + while ($idx1 < $cnt1) { + $diff[] = sprintf("%03d- ", $idx1 + 1) . $w[$idx1++]; + } + + while ($idx2 < $cnt2) { + $diff[] = sprintf("%03d+ ", $idx2 + 1) . $ar2[$idx2++]; + } + + return $diff; +} + +function generate_diff($wanted, $wanted_re, $output) +{ + $w = explode("\n", $wanted); + $o = explode("\n", $output); + $r = is_null($wanted_re) ? $w : explode("\n", $wanted_re); + $diff = generate_array_diff($r, $o, !is_null($wanted_re), $w); + + return implode("\r\n", $diff); +} + +function error($message) +{ + echo "ERROR: {$message}\n"; + exit(1); +} + +function settings2array($settings, &$ini_settings) +{ + foreach($settings as $setting) { + + if (strpos($setting, '=') !== false) { + $setting = explode("=", $setting, 2); + $name = trim($setting[0]); + $value = trim($setting[1]); + + if ($name == 'extension' || $name == 'zend_extension') { + + if (!isset($ini_settings[$name])) { + $ini_settings[$name] = array(); + } + + $ini_settings[$name][] = $value; + + } else { + $ini_settings[$name] = $value; + } + } + } +} + +function settings2params(&$ini_settings) +{ + $settings = ''; + + foreach($ini_settings as $name => $value) { + + if (is_array($value)) { + foreach($value as $val) { + $val = addslashes($val); + $settings .= " -d \"$name=$val\""; + } + } else { + if (substr(PHP_OS, 0, 3) == "WIN" && !empty($value) && $value{0} == '"') { + $len = strlen($value); + + if ($value{$len - 1} == '"') { + $value{0} = "'"; + $value{$len - 1} = "'"; + } + } else { + $value = addslashes($value); + } + + $settings .= " -d \"$name=$value\""; + } + } + + $ini_settings = $settings; +} + +function compute_summary() +{ + global $n_total, $test_results, $ignored_by_ext, $sum_results, $percent_results; + + $n_total = count($test_results); + $n_total += $ignored_by_ext; + $sum_results = array( + 'PASSED' => 0, + 'WARNED' => 0, + 'SKIPPED' => 0, + 'FAILED' => 0, + 'BORKED' => 0, + 'LEAKED' => 0, + 'XFAILED' => 0 + ); + + foreach ($test_results as $v) { + $sum_results[$v]++; + } + + $sum_results['SKIPPED'] += $ignored_by_ext; + $percent_results = array(); + + while (list($v, $n) = each($sum_results)) { + $percent_results[$v] = (100.0 * $n) / $n_total; + } +} + +function get_summary($show_ext_summary, $show_html) +{ + global $exts_skipped, $exts_tested, $n_total, $sum_results, $percent_results, $end_time, $start_time, $failed_test_summary, $PHP_FAILED_TESTS, $leak_check; + + $x_total = $n_total - $sum_results['SKIPPED'] - $sum_results['BORKED']; + + if ($x_total) { + $x_warned = (100.0 * $sum_results['WARNED']) / $x_total; + $x_failed = (100.0 * $sum_results['FAILED']) / $x_total; + $x_xfailed = (100.0 * $sum_results['XFAILED']) / $x_total; + $x_leaked = (100.0 * $sum_results['LEAKED']) / $x_total; + $x_passed = (100.0 * $sum_results['PASSED']) / $x_total; + } else { + $x_warned = $x_failed = $x_passed = $x_leaked = $x_xfailed = 0; + } + + $summary = ''; + + if ($show_html) { + $summary .= "
\n";
+	}
+
+	if ($show_ext_summary) {
+		$summary .= '
+=====================================================================
+TEST RESULT SUMMARY
+---------------------------------------------------------------------
+Exts skipped    : ' . sprintf('%4d', $exts_skipped) . '
+Exts tested     : ' . sprintf('%4d', $exts_tested) . '
+---------------------------------------------------------------------
+';
+	}
+
+	$summary .= '
+Number of tests : ' . sprintf('%4d', $n_total) . '          ' . sprintf('%8d', $x_total);
+
+	if ($sum_results['BORKED']) {
+		$summary .= '
+Tests borked    : ' . sprintf('%4d (%5.1f%%)', $sum_results['BORKED'], $percent_results['BORKED']) . ' --------';
+	}
+
+	$summary .= '
+Tests skipped   : ' . sprintf('%4d (%5.1f%%)', $sum_results['SKIPPED'], $percent_results['SKIPPED']) . ' --------
+Tests warned    : ' . sprintf('%4d (%5.1f%%)', $sum_results['WARNED'], $percent_results['WARNED']) . ' ' . sprintf('(%5.1f%%)', $x_warned) . '
+Tests failed    : ' . sprintf('%4d (%5.1f%%)', $sum_results['FAILED'], $percent_results['FAILED']) . ' ' . sprintf('(%5.1f%%)', $x_failed) . '
+Expected fail   : ' . sprintf('%4d (%5.1f%%)', $sum_results['XFAILED'], $percent_results['XFAILED']) . ' ' . sprintf('(%5.1f%%)', $x_xfailed);
+
+	if ($leak_check) {
+		$summary .= '
+Tests leaked    : ' . sprintf('%4d (%5.1f%%)', $sum_results['LEAKED'], $percent_results['LEAKED']) . ' ' . sprintf('(%5.1f%%)', $x_leaked);
+	}
+
+	$summary .= '
+Tests passed    : ' . sprintf('%4d (%5.1f%%)', $sum_results['PASSED'], $percent_results['PASSED']) . ' ' . sprintf('(%5.1f%%)', $x_passed) . '
+---------------------------------------------------------------------
+Time taken      : ' . sprintf('%4d seconds', $end_time - $start_time) . '
+=====================================================================
+';
+	$failed_test_summary = '';
+
+	if (count($PHP_FAILED_TESTS['XFAILED'])) {
+		$failed_test_summary .= '
+=====================================================================
+EXPECTED FAILED TEST SUMMARY
+---------------------------------------------------------------------
+';
+		foreach ($PHP_FAILED_TESTS['XFAILED'] as $failed_test_data) {
+			$failed_test_summary .= $failed_test_data['test_name'] . $failed_test_data['info'] . "\n";
+		}
+		$failed_test_summary .=  "=====================================================================\n";
+	}
+
+	if (count($PHP_FAILED_TESTS['BORKED'])) {
+		$failed_test_summary .= '
+=====================================================================
+BORKED TEST SUMMARY
+---------------------------------------------------------------------
+';
+		foreach ($PHP_FAILED_TESTS['BORKED'] as $failed_test_data) {
+			$failed_test_summary .= $failed_test_data['info'] . "\n";
+		}
+
+		$failed_test_summary .=  "=====================================================================\n";
+	}
+
+	if (count($PHP_FAILED_TESTS['FAILED'])) {
+		$failed_test_summary .= '
+=====================================================================
+FAILED TEST SUMMARY
+---------------------------------------------------------------------
+';
+		foreach ($PHP_FAILED_TESTS['FAILED'] as $failed_test_data) {
+			$failed_test_summary .= $failed_test_data['test_name'] . $failed_test_data['info'] . "\n";
+		}
+		$failed_test_summary .=  "=====================================================================\n";
+	}
+	if (count($PHP_FAILED_TESTS['WARNED'])) {
+		$failed_test_summary .= '
+=====================================================================
+WARNED TEST SUMMARY
+---------------------------------------------------------------------
+';
+		foreach ($PHP_FAILED_TESTS['WARNED'] as $failed_test_data) {
+			$failed_test_summary .= $failed_test_data['test_name'] . $failed_test_data['info'] . "\n";
+		}
+
+		$failed_test_summary .=  "=====================================================================\n";
+	}
+
+	if (count($PHP_FAILED_TESTS['LEAKED'])) {
+		$failed_test_summary .= '
+=====================================================================
+LEAKED TEST SUMMARY
+---------------------------------------------------------------------
+';
+		foreach ($PHP_FAILED_TESTS['LEAKED'] as $failed_test_data) {
+			$failed_test_summary .= $failed_test_data['test_name'] . $failed_test_data['info'] . "\n";
+		}
+
+		$failed_test_summary .=  "=====================================================================\n";
+	}
+
+	if ($failed_test_summary && !getenv('NO_PHPTEST_SUMMARY')) {
+		$summary .= $failed_test_summary;
+	}
+
+	if ($show_html) {
+		$summary .= "
"; + } + + return $summary; +} + +function show_start($start_time) +{ + global $html_output, $html_file; + + if ($html_output) { + fwrite($html_file, "

Time Start: " . date('Y-m-d H:i:s', $start_time) . "

\n"); + fwrite($html_file, "\n"); + } + + echo "TIME START " . date('Y-m-d H:i:s', $start_time) . "\n=====================================================================\n"; +} + +function show_end($end_time) +{ + global $html_output, $html_file; + + if ($html_output) { + fwrite($html_file, "
\n"); + fwrite($html_file, "

Time End: " . date('Y-m-d H:i:s', $end_time) . "

\n"); + } + + echo "=====================================================================\nTIME END " . date('Y-m-d H:i:s', $end_time) . "\n"; +} + +function show_summary() +{ + global $html_output, $html_file; + + if ($html_output) { + fwrite($html_file, "
\n" . get_summary(true, true)); + } + + echo get_summary(true, false); +} + +function show_redirect_start($tests, $tested, $tested_file) +{ + global $html_output, $html_file, $line_length, $SHOW_ONLY_GROUPS; + + if ($html_output) { + fwrite($html_file, "---> $tests ($tested [$tested_file]) begin\n"); + } + + if (!$SHOW_ONLY_GROUPS || in_array('REDIRECT', $SHOW_ONLY_GROUPS)) { + echo "REDIRECT $tests ($tested [$tested_file]) begin\n"; + } else { + // Write over the last line to avoid random trailing chars on next echo + echo str_repeat(" ", $line_length), "\r"; + } +} + +function show_redirect_ends($tests, $tested, $tested_file) +{ + global $html_output, $html_file, $line_length, $SHOW_ONLY_GROUPS; + + if ($html_output) { + fwrite($html_file, "---> $tests ($tested [$tested_file]) done\n"); + } + + if (!$SHOW_ONLY_GROUPS || in_array('REDIRECT', $SHOW_ONLY_GROUPS)) { + echo "REDIRECT $tests ($tested [$tested_file]) done\n"; + } else { + // Write over the last line to avoid random trailing chars on next echo + echo str_repeat(" ", $line_length), "\r"; + } +} + +function show_test($test_idx, $shortname) +{ + global $test_cnt; + global $line_length; + + $str = "TEST $test_idx/$test_cnt [$shortname]\r"; + $line_length = strlen($str); + echo $str; + flush(); +} + +function show_result($result, $tested, $tested_file, $extra = '', $temp_filenames = null) +{ + global $html_output, $html_file, $temp_target, $temp_urlbase, $line_length, $SHOW_ONLY_GROUPS; + + if (!$SHOW_ONLY_GROUPS || in_array($result, $SHOW_ONLY_GROUPS)) { + echo "$result $tested [$tested_file] $extra\n"; + } else if (!$SHOW_ONLY_GROUPS) { + // Write over the last line to avoid random trailing chars on next echo + echo str_repeat(" ", $line_length), "\r"; + } + + if ($html_output) { + + if (isset($temp_filenames['file']) && file_exists($temp_filenames['file'])) { + $url = str_replace($temp_target, $temp_urlbase, $temp_filenames['file']); + $tested = "$tested"; + } + + if (isset($temp_filenames['skip']) && file_exists($temp_filenames['skip'])) { + + if (empty($extra)) { + $extra = "skipif"; + } + + $url = str_replace($temp_target, $temp_urlbase, $temp_filenames['skip']); + $extra = "$extra"; + + } else if (empty($extra)) { + $extra = " "; + } + + if (isset($temp_filenames['diff']) && file_exists($temp_filenames['diff'])) { + $url = str_replace($temp_target, $temp_urlbase, $temp_filenames['diff']); + $diff = "diff"; + } else { + $diff = " "; + } + + if (isset($temp_filenames['mem']) && file_exists($temp_filenames['mem'])) { + $url = str_replace($temp_target, $temp_urlbase, $temp_filenames['mem']); + $mem = "leaks"; + } else { + $mem = " "; + } + + fwrite($html_file, + "" . + "$result" . + "$tested" . + "$extra" . + "$diff" . + "$mem" . + "\n"); + } +} + +function junit_init() { + // Check whether a junit log is wanted. + $JUNIT = getenv('TEST_PHP_JUNIT'); + if (empty($JUNIT)) { + $JUNIT = FALSE; + } elseif (!$fp = fopen($JUNIT, 'w')) { + error("Failed to open $JUNIT for writing."); + } else { + $JUNIT = array( + 'fp' => $fp, + 'name' => 'php-src', + 'test_total' => 0, + 'test_pass' => 0, + 'test_fail' => 0, + 'test_error' => 0, + 'test_skip' => 0, + 'test_warn' => 0, + 'execution_time'=> 0, + 'suites' => array(), + 'files' => array() + ); + } + + $GLOBALS['JUNIT'] = $JUNIT; +} + +function junit_save_xml() { + global $JUNIT; + if (!junit_enabled()) return; + + $xml = ''. PHP_EOL . + '' . PHP_EOL; + $xml .= junit_get_suite_xml(); + $xml .= ''; + fwrite($JUNIT['fp'], $xml); +} + +function junit_get_suite_xml($suite_name = '') { + global $JUNIT; + + $suite = $suite_name ? $JUNIT['suites'][$suite_name] : $JUNIT; + + $result = sprintf( + '' . PHP_EOL, + $suite['name'], $suite['test_total'], $suite['test_fail'], $suite['test_error'], $suite['test_skip'], + $suite['execution_time'] + ); + + foreach($suite['suites'] as $sub_suite) { + $result .= junit_get_suite_xml($sub_suite['name']); + } + + // Output files only in subsuites + if (!empty($suite_name)) { + foreach($suite['files'] as $file) { + $result .= $JUNIT['files'][$file]['xml']; + } + } + + $result .= '' . PHP_EOL; + + return $result; +} + +function junit_enabled() { + global $JUNIT; + return !empty($JUNIT); +} + +/** + * @param array|string $type + * @param string $file_name + * @param string $test_name + * @param int|string $time + * @param string $message + * @param string $details + * @return void + */ +function junit_mark_test_as($type, $file_name, $test_name, $time = null, $message = '', $details = '') { + global $JUNIT; + if (!junit_enabled()) return; + + $suite = junit_get_suitename_for($file_name); + + junit_suite_record($suite, 'test_total'); + + $time = null !== $time ? $time : junit_get_timer($file_name); + junit_suite_record($suite, 'execution_time', $time); + + $escaped_details = htmlspecialchars($details, ENT_QUOTES, 'UTF-8'); + $escaped_details = preg_replace_callback('/[\0-\x08\x0B\x0C\x0E-\x1F]/', function ($c) { + return sprintf('[[0x%02x]]', ord($c[0])); + }, $escaped_details); + $escaped_message = htmlspecialchars($message, ENT_QUOTES, 'UTF-8'); + + $escaped_test_name = basename($file_name) . ' - ' . htmlspecialchars($test_name, ENT_QUOTES); + $JUNIT['files'][$file_name]['xml'] = "\n"; + + if (is_array($type)) { + $output_type = $type[0] . 'ED'; + $temp = array_intersect(array('XFAIL', 'FAIL', 'WARN'), $type); + $type = reset($temp); + } else { + $output_type = $type . 'ED'; + } + + if ('PASS' == $type || 'XFAIL' == $type) { + junit_suite_record($suite, 'test_pass'); + } elseif ('BORK' == $type) { + junit_suite_record($suite, 'test_error'); + $JUNIT['files'][$file_name]['xml'] .= "\n"; + } elseif ('SKIP' == $type) { + junit_suite_record($suite, 'test_skip'); + $JUNIT['files'][$file_name]['xml'] .= "$escaped_message\n"; + } elseif ('WARN' == $type) { + junit_suite_record($suite, 'test_warn'); + $JUNIT['files'][$file_name]['xml'] .= "$escaped_message\n"; + } elseif ('FAIL' == $type) { + junit_suite_record($suite, 'test_fail'); + $JUNIT['files'][$file_name]['xml'] .= "$escaped_details\n"; + } else { + junit_suite_record($suite, 'test_error'); + $JUNIT['files'][$file_name]['xml'] .= "$escaped_details\n"; + } + + $JUNIT['files'][$file_name]['xml'] .= "\n"; + +} + +function junit_suite_record($suite, $param, $value = 1) { + global $JUNIT; + + $JUNIT[$param] += $value; + $JUNIT['suites'][$suite][$param] += $value; +} + +function junit_get_timer($file_name) { + global $JUNIT; + if (!junit_enabled()) return 0; + + if (isset($JUNIT['files'][$file_name]['total'])) { + return number_format($JUNIT['files'][$file_name]['total'], 4); + } + + return 0; +} + +function junit_start_timer($file_name) { + global $JUNIT; + if (!junit_enabled()) return; + + if (!isset($JUNIT['files'][$file_name]['start'])) { + $JUNIT['files'][$file_name]['start'] = microtime(true); + + $suite = junit_get_suitename_for($file_name); + junit_init_suite($suite); + $JUNIT['suites'][$suite]['files'][$file_name] = $file_name; + } +} + +function junit_get_suitename_for($file_name) { + return junit_path_to_classname(dirname($file_name)); +} + +function junit_path_to_classname($file_name) { + global $JUNIT; + return $JUNIT['name'] . '.' . str_replace(DIRECTORY_SEPARATOR, '.', $file_name); +} + +function junit_init_suite($suite_name) { + global $JUNIT; + if (!junit_enabled()) return; + + if (!empty($JUNIT['suites'][$suite_name])) { + return; + } + + $JUNIT['suites'][$suite_name] = array( + 'name' => $suite_name, + 'test_total' => 0, + 'test_pass' => 0, + 'test_fail' => 0, + 'test_error' => 0, + 'test_skip' => 0, + 'suites' => array(), + 'files' => array(), + 'execution_time'=> 0, + ); +} + +function junit_finish_timer($file_name) { + global $JUNIT; + if (!junit_enabled()) return; + + if (!isset($JUNIT['files'][$file_name]['start'])) { + error("Timer for $file_name was not started!"); + } + + if (!isset($JUNIT['files'][$file_name]['total'])) { + $JUNIT['files'][$file_name]['total'] = 0; + } + + $start = $JUNIT['files'][$file_name]['start']; + $JUNIT['files'][$file_name]['total'] += microtime(true) - $start; + unset($JUNIT['files'][$file_name]['start']); +} + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim: noet sw=4 ts=4 + */ +?> diff --git a/ext/opcache/tests/bug66338-Officials.inc b/ext/opcache/tests/bug66338-Officials.inc new file mode 100644 index 0000000000000..11240f7b889de --- /dev/null +++ b/ext/opcache/tests/bug66338-Officials.inc @@ -0,0 +1,3 @@ +__callstatic); SERIALIZE_PTR(ce->__debugInfo); SERIALIZE_PTR(ce->__compareTo); + SERIALIZE_PTR(ce->__equals); } static void zend_file_cache_serialize(zend_persistent_script *script, @@ -1349,6 +1350,7 @@ static void zend_file_cache_unserialize_class(zval *zv, UNSERIALIZE_PTR(ce->__callstatic); UNSERIALIZE_PTR(ce->__debugInfo); UNSERIALIZE_PTR(ce->__compareTo); + UNSERIALIZE_PTR(ce->__equals); if (UNEXPECTED((ce->ce_flags & ZEND_ACC_ANON_CLASS))) { ce->serialize = zend_class_serialize_deny; diff --git a/ext/opcache/zend_persist.c b/ext/opcache/zend_persist.c index dc6d52c444680..3e99a4f42d9ec 100644 --- a/ext/opcache/zend_persist.c +++ b/ext/opcache/zend_persist.c @@ -887,6 +887,9 @@ static int zend_update_parent_ce(zval *zv) if (ce->__compareTo) { ce->__compareTo = zend_shared_alloc_get_xlat_entry(ce->__compareTo); } + if (ce->__equals) { + ce->__equals = zend_shared_alloc_get_xlat_entry(ce->__equals); + } // zend_hash_apply(&ce->properties_info, (apply_func_t) zend_update_property_info_ce); return 0; } From c9ee7ec42f4ab57b2e0116a888dbe04311465b63 Mon Sep 17 00:00:00 2001 From: Rudi Theunissen Date: Mon, 25 Jun 2018 17:53:51 -0400 Subject: [PATCH 19/40] Remove the intrinsic __equals check during comparison --- .../comparisons/equals-before-compare.phpt | 12 ++-- .../comparisons/equals-disallow-compare.phpt | 60 +++++++++++++++++++ Zend/zend_operators.c | 56 ++++++++--------- 3 files changed, 95 insertions(+), 33 deletions(-) create mode 100644 Zend/tests/comparisons/equals-disallow-compare.phpt diff --git a/Zend/tests/comparisons/equals-before-compare.phpt b/Zend/tests/comparisons/equals-before-compare.phpt index eada7c8e62957..62d4f9118fbe3 100644 --- a/Zend/tests/comparisons/equals-before-compare.phpt +++ b/Zend/tests/comparisons/equals-before-compare.phpt @@ -1,12 +1,12 @@ --TEST-- -__equals: Equal objects automatically have the same ordering +__equals: Should not be called for comparisons. --FILE-- 1); -var_dump(new A <=> 2); +$a = new A(); + +var_dump($a <=> 1); +var_dump($a <=> $a); ?> --EXPECTF-- int(1) -int(0) +int(1) diff --git a/Zend/tests/comparisons/equals-disallow-compare.phpt b/Zend/tests/comparisons/equals-disallow-compare.phpt new file mode 100644 index 0000000000000..a22df9359683a --- /dev/null +++ b/Zend/tests/comparisons/equals-disallow-compare.phpt @@ -0,0 +1,60 @@ +--TEST-- +__equals: Disallow comparison, but allow equality checks +--FILE-- +value = $value; + } + + public function __compareTo($other) + { + throw new Exception("This object does not support comparison"); + } + + public function __equals($other) + { + return $other instanceof self && $this->value === $other->value; + } +} + + +/** + * + */ +var_dump(new Comparable(1) == new Comparable(2)); + +/** + * + */ +var_dump(new Comparable(2) == new Comparable(2)); + +/** + * + */ +try { + new Comparable(2) <= new Comparable(2); +} catch (Exception $e) { + var_dump($e->getMessage()); +} + +/** + * + */ +try { + new Comparable(1) > new Comparable(2); +} catch (Exception $e) { + var_dump($e->getMessage()); +} + +?> +--EXPECTF-- +bool(false) +bool(true) +string(39) "This object does not support comparison" +string(39) "This object does not support comparison" diff --git a/Zend/zend_operators.c b/Zend/zend_operators.c index 3b369ba24055d..067ce35ef6aa9 100644 --- a/Zend/zend_operators.c +++ b/Zend/zend_operators.c @@ -2026,14 +2026,14 @@ ZEND_API int ZEND_FASTCALL compare_function(zval *result, zval *op1, zval *op2) return SUCCESS; case TYPE_PAIR(IS_OBJECT, IS_NULL): - if (Z_OBJ_HANDLER_P(op1, equals)) { - if (Z_OBJ_HANDLER_P(op1, equals)(result, op1, op2) == SUCCESS) { - if (i_zend_is_true(result)) { - ZVAL_LONG(result, 0); - return SUCCESS; - } - } - } + // if (Z_OBJ_HANDLER_P(op1, equals)) { + // if (Z_OBJ_HANDLER_P(op1, equals)(result, op1, op2) == SUCCESS) { + // if (i_zend_is_true(result)) { + // ZVAL_LONG(result, 0); + // return SUCCESS; + // } + // } + // } if (Z_OBJ_HANDLER_P(op1, compare)) { if (Z_OBJ_HANDLER_P(op1, compare)(result, op1, op2) == SUCCESS) { @@ -2046,14 +2046,14 @@ ZEND_API int ZEND_FASTCALL compare_function(zval *result, zval *op1, zval *op2) return SUCCESS; case TYPE_PAIR(IS_NULL, IS_OBJECT): - if (Z_OBJ_HANDLER_P(op2, equals)) { - if (Z_OBJ_HANDLER_P(op2, equals)(result, op2, op1) == SUCCESS) { - if (i_zend_is_true(result)) { - ZVAL_LONG(result, 0); - return SUCCESS; - } - } - } + // if (Z_OBJ_HANDLER_P(op2, equals)) { + // if (Z_OBJ_HANDLER_P(op2, equals)(result, op2, op1) == SUCCESS) { + // if (i_zend_is_true(result)) { + // ZVAL_LONG(result, 0); + // return SUCCESS; + // } + // } + // } if (Z_OBJ_HANDLER_P(op2, compare)) { if (Z_OBJ_HANDLER_P(op2, compare)(result, op2, op1) == SUCCESS) { @@ -2076,12 +2076,12 @@ ZEND_API int ZEND_FASTCALL compare_function(zval *result, zval *op1, zval *op2) } if (Z_TYPE_P(op1) == IS_OBJECT) { - if (Z_OBJ_HANDLER_P(op1, equals) && Z_OBJ_HANDLER_P(op1, equals)(result, op1, op2) == SUCCESS) { - if (i_zend_is_true(result)) { - ZVAL_LONG(result, 0); - return SUCCESS; - } - } + // if (Z_OBJ_HANDLER_P(op1, equals) && Z_OBJ_HANDLER_P(op1, equals)(result, op1, op2) == SUCCESS) { + // if (i_zend_is_true(result)) { + // ZVAL_LONG(result, 0); + // return SUCCESS; + // } + // } if (Z_OBJ_HANDLER_P(op1, compare) && Z_OBJ_HANDLER_P(op1, compare)(result, op1, op2) == SUCCESS) { convert_compare_result_to_long(result); @@ -2090,12 +2090,12 @@ ZEND_API int ZEND_FASTCALL compare_function(zval *result, zval *op1, zval *op2) } if (Z_TYPE_P(op2) == IS_OBJECT) { - if (Z_OBJ_HANDLER_P(op2, equals) && Z_OBJ_HANDLER_P(op2, equals)(result, op2, op1) == SUCCESS) { - if (i_zend_is_true(result)) { - ZVAL_LONG(result, 0); - return SUCCESS; - } - } + // if (Z_OBJ_HANDLER_P(op2, equals) && Z_OBJ_HANDLER_P(op2, equals)(result, op2, op1) == SUCCESS) { + // if (i_zend_is_true(result)) { + // ZVAL_LONG(result, 0); + // return SUCCESS; + // } + // } if (Z_OBJ_HANDLER_P(op2, compare) && Z_OBJ_HANDLER_P(op2, compare)(result, op2, op1) == SUCCESS) { convert_compare_result_to_long(result); From af25d267f1b3605355730812c47bf7b01fbfa5cd Mon Sep 17 00:00:00 2001 From: Rudi Theunissen Date: Mon, 25 Jun 2018 17:58:14 -0400 Subject: [PATCH 20/40] Remove commented code --- Zend/zend_operators.c | 32 -------------------------------- 1 file changed, 32 deletions(-) diff --git a/Zend/zend_operators.c b/Zend/zend_operators.c index 067ce35ef6aa9..66a612043e5d6 100644 --- a/Zend/zend_operators.c +++ b/Zend/zend_operators.c @@ -2026,15 +2026,6 @@ ZEND_API int ZEND_FASTCALL compare_function(zval *result, zval *op1, zval *op2) return SUCCESS; case TYPE_PAIR(IS_OBJECT, IS_NULL): - // if (Z_OBJ_HANDLER_P(op1, equals)) { - // if (Z_OBJ_HANDLER_P(op1, equals)(result, op1, op2) == SUCCESS) { - // if (i_zend_is_true(result)) { - // ZVAL_LONG(result, 0); - // return SUCCESS; - // } - // } - // } - if (Z_OBJ_HANDLER_P(op1, compare)) { if (Z_OBJ_HANDLER_P(op1, compare)(result, op1, op2) == SUCCESS) { convert_compare_result_to_long(result); @@ -2046,15 +2037,6 @@ ZEND_API int ZEND_FASTCALL compare_function(zval *result, zval *op1, zval *op2) return SUCCESS; case TYPE_PAIR(IS_NULL, IS_OBJECT): - // if (Z_OBJ_HANDLER_P(op2, equals)) { - // if (Z_OBJ_HANDLER_P(op2, equals)(result, op2, op1) == SUCCESS) { - // if (i_zend_is_true(result)) { - // ZVAL_LONG(result, 0); - // return SUCCESS; - // } - // } - // } - if (Z_OBJ_HANDLER_P(op2, compare)) { if (Z_OBJ_HANDLER_P(op2, compare)(result, op2, op1) == SUCCESS) { convert_compare_result_to_long(result); @@ -2076,13 +2058,6 @@ ZEND_API int ZEND_FASTCALL compare_function(zval *result, zval *op1, zval *op2) } if (Z_TYPE_P(op1) == IS_OBJECT) { - // if (Z_OBJ_HANDLER_P(op1, equals) && Z_OBJ_HANDLER_P(op1, equals)(result, op1, op2) == SUCCESS) { - // if (i_zend_is_true(result)) { - // ZVAL_LONG(result, 0); - // return SUCCESS; - // } - // } - if (Z_OBJ_HANDLER_P(op1, compare) && Z_OBJ_HANDLER_P(op1, compare)(result, op1, op2) == SUCCESS) { convert_compare_result_to_long(result); return SUCCESS; @@ -2090,13 +2065,6 @@ ZEND_API int ZEND_FASTCALL compare_function(zval *result, zval *op1, zval *op2) } if (Z_TYPE_P(op2) == IS_OBJECT) { - // if (Z_OBJ_HANDLER_P(op2, equals) && Z_OBJ_HANDLER_P(op2, equals)(result, op2, op1) == SUCCESS) { - // if (i_zend_is_true(result)) { - // ZVAL_LONG(result, 0); - // return SUCCESS; - // } - // } - if (Z_OBJ_HANDLER_P(op2, compare) && Z_OBJ_HANDLER_P(op2, compare)(result, op2, op1) == SUCCESS) { convert_compare_result_to_long(result); Z_LVAL_P(result) *= -1; From af3796d06db24db49f2fb774290314baf8435479 Mon Sep 17 00:00:00 2001 From: Rudi Theunissen Date: Mon, 25 Jun 2018 19:58:49 -0400 Subject: [PATCH 21/40] Generate zend_vm --- Zend/zend_vm_execute.h | 36 ++++++++++++------------------------ 1 file changed, 12 insertions(+), 24 deletions(-) diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 2724bca69ce2f..5e9b630da4bca 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -4273,8 +4273,7 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_EQUAL_SPEC_CON op2 = GET_OP2_UNDEF_CV(op2, BP_VAR_R); } result = EX_VAR(opline->result.var); - compare_function(result, op1, op2); - ZVAL_BOOL(result, Z_LVAL_P(result) == 0); + is_equal_function(result, op1, op2); ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); @@ -4331,8 +4330,7 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_NOT_EQUAL_SPEC op2 = GET_OP2_UNDEF_CV(op2, BP_VAR_R); } result = EX_VAR(opline->result.var); - compare_function(result, op1, op2); - ZVAL_BOOL(result, Z_LVAL_P(result) != 0); + is_not_equal_function(result, op1, op2); ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); @@ -13191,8 +13189,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_EQUAL_SPEC_TMPVAR_CONST_HAN op2 = GET_OP2_UNDEF_CV(op2, BP_VAR_R); } result = EX_VAR(opline->result.var); - compare_function(result, op1, op2); - ZVAL_BOOL(result, Z_LVAL_P(result) == 0); + is_equal_function(result, op1, op2); zval_ptr_dtor_nogc(free_op1); ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); @@ -13249,8 +13246,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_NOT_EQUAL_SPEC_TMPVAR_CONST op2 = GET_OP2_UNDEF_CV(op2, BP_VAR_R); } result = EX_VAR(opline->result.var); - compare_function(result, op1, op2); - ZVAL_BOOL(result, Z_LVAL_P(result) != 0); + is_not_equal_function(result, op1, op2); zval_ptr_dtor_nogc(free_op1); ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); @@ -14920,8 +14916,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_EQUAL_SPEC_TMPVAR_TMPVAR_HA op2 = GET_OP2_UNDEF_CV(op2, BP_VAR_R); } result = EX_VAR(opline->result.var); - compare_function(result, op1, op2); - ZVAL_BOOL(result, Z_LVAL_P(result) == 0); + is_equal_function(result, op1, op2); zval_ptr_dtor_nogc(free_op1); zval_ptr_dtor_nogc(free_op2); ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); @@ -14978,8 +14973,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_NOT_EQUAL_SPEC_TMPVAR_TMPVA op2 = GET_OP2_UNDEF_CV(op2, BP_VAR_R); } result = EX_VAR(opline->result.var); - compare_function(result, op1, op2); - ZVAL_BOOL(result, Z_LVAL_P(result) != 0); + is_not_equal_function(result, op1, op2); zval_ptr_dtor_nogc(free_op1); zval_ptr_dtor_nogc(free_op2); ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); @@ -38260,8 +38254,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_EQUAL_SPEC_CV_CONST_HANDLER op2 = GET_OP2_UNDEF_CV(op2, BP_VAR_R); } result = EX_VAR(opline->result.var); - compare_function(result, op1, op2); - ZVAL_BOOL(result, Z_LVAL_P(result) == 0); + is_equal_function(result, op1, op2); ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); @@ -38318,8 +38311,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_NOT_EQUAL_SPEC_CV_CONST_HAN op2 = GET_OP2_UNDEF_CV(op2, BP_VAR_R); } result = EX_VAR(opline->result.var); - compare_function(result, op1, op2); - ZVAL_BOOL(result, Z_LVAL_P(result) != 0); + is_not_equal_function(result, op1, op2); ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); @@ -42138,8 +42130,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_EQUAL_SPEC_CV_TMPVAR_HANDLE op2 = GET_OP2_UNDEF_CV(op2, BP_VAR_R); } result = EX_VAR(opline->result.var); - compare_function(result, op1, op2); - ZVAL_BOOL(result, Z_LVAL_P(result) == 0); + is_equal_function(result, op1, op2); zval_ptr_dtor_nogc(free_op2); ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); @@ -42196,8 +42187,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_NOT_EQUAL_SPEC_CV_TMPVAR_HA op2 = GET_OP2_UNDEF_CV(op2, BP_VAR_R); } result = EX_VAR(opline->result.var); - compare_function(result, op1, op2); - ZVAL_BOOL(result, Z_LVAL_P(result) != 0); + is_not_equal_function(result, op1, op2); zval_ptr_dtor_nogc(free_op2); ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); @@ -47697,8 +47687,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_EQUAL_SPEC_CV_CV_HANDLER(ZE op2 = GET_OP2_UNDEF_CV(op2, BP_VAR_R); } result = EX_VAR(opline->result.var); - compare_function(result, op1, op2); - ZVAL_BOOL(result, Z_LVAL_P(result) == 0); + is_equal_function(result, op1, op2); ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); @@ -47755,8 +47744,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_NOT_EQUAL_SPEC_CV_CV_HANDLE op2 = GET_OP2_UNDEF_CV(op2, BP_VAR_R); } result = EX_VAR(opline->result.var); - compare_function(result, op1, op2); - ZVAL_BOOL(result, Z_LVAL_P(result) != 0); + is_not_equal_function(result, op1, op2); ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); From 5a64530574b59314520a371037fa3e825fcdd50a Mon Sep 17 00:00:00 2001 From: Rudi Theunissen Date: Mon, 25 Jun 2018 20:00:57 -0400 Subject: [PATCH 22/40] Remove seemingly unrelated files --- ext/mysqli/run-tests.php | 2902 ---------------------- ext/opcache/tests/bug66338-Officials.inc | 3 - 2 files changed, 2905 deletions(-) delete mode 100644 ext/mysqli/run-tests.php delete mode 100644 ext/opcache/tests/bug66338-Officials.inc diff --git a/ext/mysqli/run-tests.php b/ext/mysqli/run-tests.php deleted file mode 100644 index 1991b56a087a4..0000000000000 --- a/ext/mysqli/run-tests.php +++ /dev/null @@ -1,2902 +0,0 @@ -#!/usr/bin/env php - | - | Preston L. Bannister | - | Marcus Boerger | - | Derick Rethans | - | Sander Roobol | - | (based on version by: Stig Bakken ) | - | (based on the PHP 3 test framework by Rasmus Lerdorf) | - +----------------------------------------------------------------------+ - */ - -/* $Id$ */ - -/* Sanity check to ensure that pcre extension needed by this script is available. - * In the event it is not, print a nice error message indicating that this script will - * not run without it. - */ - -if (!extension_loaded('pcre')) { - echo <<'; - save_text($info_file, $php_info); - $info_params = array(); - settings2array($ini_overwrites, $info_params); - settings2params($info_params); - $php_info = `$php $pass_options $info_params $no_file_cache "$info_file"`; - define('TESTED_PHP_VERSION', `$php -n -r "echo PHP_VERSION;"`); - - if ($php_cgi && $php != $php_cgi) { - $php_info_cgi = `$php_cgi $pass_options $info_params $no_file_cache -q "$info_file"`; - $php_info_sep = "\n---------------------------------------------------------------------"; - $php_cgi_info = "$php_info_sep\nPHP : $php_cgi $php_info_cgi$php_info_sep"; - } else { - $php_cgi_info = ''; - } - - if ($phpdbg) { - $phpdbg_info = `$phpdbg $pass_options $info_params $no_file_cache -qrr "$info_file"`; - $php_info_sep = "\n---------------------------------------------------------------------"; - $phpdbg_info = "$php_info_sep\nPHP : $phpdbg $phpdbg_info$php_info_sep"; - } else { - $phpdbg_info = ''; - } - - if (function_exists('opcache_invalidate')) { - opcache_invalidate($info_file, true); - } - @unlink($info_file); - - // load list of enabled extensions - save_text($info_file, ''); - $exts_to_test = explode(',',`$php $pass_options $info_params $no_file_cache "$info_file"`); - // check for extensions that need special handling and regenerate - $info_params_ex = array( - 'session' => array('session.auto_start=0'), - 'tidy' => array('tidy.clean_output=0'), - 'zlib' => array('zlib.output_compression=Off'), - 'xdebug' => array('xdebug.default_enable=0'), - 'mbstring' => array('mbstring.func_overload=0'), - ); - - foreach($info_params_ex as $ext => $ini_overwrites_ex) { - if (in_array($ext, $exts_to_test)) { - $ini_overwrites = array_merge($ini_overwrites, $ini_overwrites_ex); - } - } - - if (function_exists('opcache_invalidate')) { - opcache_invalidate($info_file, true); - } - @unlink($info_file); - - // Write test context information. - echo " -===================================================================== -PHP : $php $php_info $php_cgi_info $phpdbg_info -CWD : $cwd -Extra dirs : "; - foreach ($user_tests as $test_dir) { - echo "{$test_dir}\n "; - } - echo " -VALGRIND : " . ($leak_check ? $valgrind_header : 'Not used') . " -===================================================================== -"; -} - -define('PHP_QA_EMAIL', 'qa-reports@lists.php.net'); -define('QA_SUBMISSION_PAGE', 'http://qa.php.net/buildtest-process.php'); -define('QA_REPORTS_PAGE', 'http://qa.php.net/reports'); -define('TRAVIS_CI' , (bool) getenv('TRAVIS')); - -function save_or_mail_results() -{ - global $sum_results, $just_save_results, $failed_test_summary, - $PHP_FAILED_TESTS, $CUR_DIR, $php, $output_file, $compression; - - /* We got failed Tests, offer the user to send an e-mail to QA team, unless NO_INTERACTION is set */ - if (!getenv('NO_INTERACTION') && !TRAVIS_CI) { - $fp = fopen("php://stdin", "r+"); - if ($sum_results['FAILED'] || $sum_results['BORKED'] || $sum_results['WARNED'] || $sum_results['LEAKED']) { - echo "\nYou may have found a problem in PHP."; - } - echo "\nThis report can be automatically sent to the PHP QA team at\n"; - echo QA_REPORTS_PAGE . " and http://news.php.net/php.qa.reports\n"; - echo "This gives us a better understanding of PHP's behavior.\n"; - echo "If you don't want to send the report immediately you can choose\n"; - echo "option \"s\" to save it. You can then email it to ". PHP_QA_EMAIL . " later.\n"; - echo "Do you want to send this report now? [Yns]: "; - flush(); - - $user_input = fgets($fp, 10); - $just_save_results = (strtolower($user_input[0]) == 's'); - } - - if ($just_save_results || !getenv('NO_INTERACTION') || TRAVIS_CI) { - if ($just_save_results || TRAVIS_CI || strlen(trim($user_input)) == 0 || strtolower($user_input[0]) == 'y') { - /* - * Collect information about the host system for our report - * Fetch phpinfo() output so that we can see the PHP environment - * Make an archive of all the failed tests - * Send an email - */ - if ($just_save_results) { - $user_input = 's'; - } - - /* Ask the user to provide an email address, so that QA team can contact the user */ - if (TRAVIS_CI) { - $user_email = 'travis at php dot net'; - } elseif (!strncasecmp($user_input, 'y', 1) || strlen(trim($user_input)) == 0) { - echo "\nPlease enter your email address.\n(Your address will be mangled so that it will not go out on any\nmailinglist in plain text): "; - flush(); - $user_email = trim(fgets($fp, 1024)); - $user_email = str_replace("@", " at ", str_replace(".", " dot ", $user_email)); - } - - $failed_tests_data = ''; - $sep = "\n" . str_repeat('=', 80) . "\n"; - $failed_tests_data .= $failed_test_summary . "\n"; - $failed_tests_data .= get_summary(true, false) . "\n"; - - if ($sum_results['FAILED']) { - foreach ($PHP_FAILED_TESTS['FAILED'] as $test_info) { - $failed_tests_data .= $sep . $test_info['name'] . $test_info['info']; - $failed_tests_data .= $sep . file_get_contents(realpath($test_info['output']), FILE_BINARY); - $failed_tests_data .= $sep . file_get_contents(realpath($test_info['diff']), FILE_BINARY); - $failed_tests_data .= $sep . "\n\n"; - } - $status = "failed"; - } else { - $status = "success"; - } - - $failed_tests_data .= "\n" . $sep . 'BUILD ENVIRONMENT' . $sep; - $failed_tests_data .= "OS:\n" . PHP_OS . " - " . php_uname() . "\n\n"; - $ldd = $autoconf = $sys_libtool = $libtool = $compiler = 'N/A'; - - if (substr(PHP_OS, 0, 3) != "WIN") { - /* If PHP_AUTOCONF is set, use it; otherwise, use 'autoconf'. */ - if (getenv('PHP_AUTOCONF')) { - $autoconf = shell_exec(getenv('PHP_AUTOCONF') . ' --version'); - } else { - $autoconf = shell_exec('autoconf --version'); - } - - /* Always use the generated libtool - Mac OSX uses 'glibtool' */ - $libtool = shell_exec($CUR_DIR . '/libtool --version'); - - /* Use shtool to find out if there is glibtool present (MacOSX) */ - $sys_libtool_path = shell_exec(__DIR__ . '/build/shtool path glibtool libtool'); - - if ($sys_libtool_path) { - $sys_libtool = shell_exec(str_replace("\n", "", $sys_libtool_path) . ' --version'); - } - - /* Try the most common flags for 'version' */ - $flags = array('-v', '-V', '--version'); - $cc_status = 0; - - foreach($flags AS $flag) { - system(getenv('CC') . " $flag >/dev/null 2>&1", $cc_status); - if ($cc_status == 0) { - $compiler = shell_exec(getenv('CC') . " $flag 2>&1"); - break; - } - } - - $ldd = shell_exec("ldd $php 2>/dev/null"); - } - - $failed_tests_data .= "Autoconf:\n$autoconf\n"; - $failed_tests_data .= "Bundled Libtool:\n$libtool\n"; - $failed_tests_data .= "System Libtool:\n$sys_libtool\n"; - $failed_tests_data .= "Compiler:\n$compiler\n"; - $failed_tests_data .= "Bison:\n". shell_exec('bison --version 2>/dev/null') . "\n"; - $failed_tests_data .= "Libraries:\n$ldd\n"; - $failed_tests_data .= "\n"; - - if (isset($user_email)) { - $failed_tests_data .= "User's E-mail: " . $user_email . "\n\n"; - } - - $failed_tests_data .= $sep . "PHPINFO" . $sep; - $failed_tests_data .= shell_exec($php . ' -ddisplay_errors=stderr -dhtml_errors=0 -i 2> /dev/null'); - - if ($just_save_results || !mail_qa_team($failed_tests_data, $compression, $status) && !TRAVIS_CI) { - file_put_contents($output_file, $failed_tests_data); - - if (!$just_save_results) { - echo "\nThe test script was unable to automatically send the report to PHP's QA Team\n"; - } - - echo "Please send " . $output_file . " to " . PHP_QA_EMAIL . " manually, thank you.\n"; - } elseif (!getenv('NO_INTERACTION') && !TRAVIS_CI) { - fwrite($fp, "\nThank you for helping to make PHP better.\n"); - fclose($fp); - } - } - } -} - -// Determine the tests to be run. - -$test_files = array(); -$redir_tests = array(); -$test_results = array(); -$PHP_FAILED_TESTS = array('BORKED' => array(), 'FAILED' => array(), 'WARNED' => array(), 'LEAKED' => array(), 'XFAILED' => array()); - -// If parameters given assume they represent selected tests to run. -$failed_tests_file= false; -$pass_option_n = false; -$pass_options = ''; - -$compression = 0; -$output_file = $CUR_DIR . '/php_test_results_' . date('Ymd_Hi') . '.txt'; - -if ($compression && in_array("compress.zlib", stream_get_filters())) { - $output_file = 'compress.zlib://' . $output_file . '.gz'; -} - -$just_save_results = false; -$leak_check = false; -$html_output = false; -$html_file = null; -$temp_source = null; -$temp_target = null; -$temp_urlbase = null; -$conf_passed = null; -$no_clean = false; - -$cfgtypes = array('show', 'keep'); -$cfgfiles = array('skip', 'php', 'clean', 'out', 'diff', 'exp'); -$cfg = array(); - -foreach($cfgtypes as $type) { - $cfg[$type] = array(); - - foreach($cfgfiles as $file) { - $cfg[$type][$file] = false; - } -} - -if (getenv('TEST_PHP_ARGS')) { - - if (!isset($argc) || !$argc || !isset($argv)) { - $argv = array(__FILE__); - } - - $argv = array_merge($argv, explode(' ', getenv('TEST_PHP_ARGS'))); - $argc = count($argv); -} - -if (isset($argc) && $argc > 1) { - - for ($i=1; $i<$argc; $i++) { - $is_switch = false; - $switch = substr($argv[$i],1,1); - $repeat = substr($argv[$i],0,1) == '-'; - - while ($repeat) { - - if (!$is_switch) { - $switch = substr($argv[$i],1,1); - } - - $is_switch = true; - - if ($repeat) { - foreach($cfgtypes as $type) { - if (strpos($switch, '--' . $type) === 0) { - foreach($cfgfiles as $file) { - if ($switch == '--' . $type . '-' . $file) { - $cfg[$type][$file] = true; - $is_switch = false; - break; - } - } - } - } - } - - if (!$is_switch) { - $is_switch = true; - break; - } - - $repeat = false; - - switch($switch) { - case 'r': - case 'l': - $test_list = file($argv[++$i]); - if ($test_list) { - foreach($test_list as $test) { - $matches = array(); - if (preg_match('/^#.*\[(.*)\]\:\s+(.*)$/', $test, $matches)) { - $redir_tests[] = array($matches[1], $matches[2]); - } else if (strlen($test)) { - $test_files[] = trim($test); - } - } - } - if ($switch != 'l') { - break; - } - $i--; - // break left intentionally - case 'w': - $failed_tests_file = fopen($argv[++$i], 'w+t'); - break; - case 'a': - $failed_tests_file = fopen($argv[++$i], 'a+t'); - break; - case 'c': - $conf_passed = $argv[++$i]; - break; - case 'd': - $ini_overwrites[] = $argv[++$i]; - break; - case 'g': - $SHOW_ONLY_GROUPS = explode(",", $argv[++$i]); - break; - //case 'h' - case '--keep-all': - foreach($cfgfiles as $file) { - $cfg['keep'][$file] = true; - } - break; - //case 'l' - case 'm': - $leak_check = true; - $valgrind_cmd = "valgrind --version"; - $valgrind_header = system_with_timeout($valgrind_cmd, $environment); - $replace_count = 0; - if (!$valgrind_header) { - error("Valgrind returned no version info, cannot proceed.\nPlease check if Valgrind is installed."); - } else { - $valgrind_version = preg_replace("/valgrind-(\d+)\.(\d+)\.(\d+)([.\w_-]+)?(\s+)/", '$1.$2.$3', $valgrind_header, 1, $replace_count); - if ($replace_count != 1) { - error("Valgrind returned invalid version info (\"$valgrind_header\"), cannot proceed."); - } - $valgrind_header = trim($valgrind_header); - } - break; - case 'n': - if (!$pass_option_n) { - $pass_options .= ' -n'; - } - $pass_option_n = true; - break; - case 'e': - $pass_options .= ' -e'; - break; - case '--no-clean': - $no_clean = true; - break; - case 'p': - $php = $argv[++$i]; - putenv("TEST_PHP_EXECUTABLE=$php"); - $environment['TEST_PHP_EXECUTABLE'] = $php; - break; - case 'P': - if(constant('PHP_BINARY')) { - $php = PHP_BINARY; - } else { - break; - } - putenv("TEST_PHP_EXECUTABLE=$php"); - $environment['TEST_PHP_EXECUTABLE'] = $php; - break; - case 'q': - putenv('NO_INTERACTION=1'); - break; - //case 'r' - case 's': - $output_file = $argv[++$i]; - $just_save_results = true; - break; - case '--set-timeout': - $environment['TEST_TIMEOUT'] = $argv[++$i]; - break; - case '--show-all': - foreach($cfgfiles as $file) { - $cfg['show'][$file] = true; - } - break; - case '--temp-source': - $temp_source = $argv[++$i]; - break; - case '--temp-target': - $temp_target = $argv[++$i]; - if ($temp_urlbase) { - $temp_urlbase = $temp_target; - } - break; - case '--temp-urlbase': - $temp_urlbase = $argv[++$i]; - break; - case 'v': - case '--verbose': - $DETAILED = true; - break; - case 'x': - $environment['SKIP_SLOW_TESTS'] = 1; - break; - case '--offline': - $environment['SKIP_ONLINE_TESTS'] = 1; - break; - //case 'w' - case '-': - // repeat check with full switch - $switch = $argv[$i]; - if ($switch != '-') { - $repeat = true; - } - break; - case '--html': - $html_file = fopen($argv[++$i], 'wt'); - $html_output = is_resource($html_file); - break; - case '--version': - echo '$Id$' . "\n"; - exit(1); - - default: - echo "Illegal switch '$switch' specified!\n"; - case 'h': - case '-help': - case '--help': - echo << Read the testfiles to be executed from . After the test - has finished all failed tests are written to the same . - If the list is empty and no further test is specified then - all tests are executed (same as: -r -w ). - - -r Read the testfiles to be executed from . - - -w Write a list of all failed tests to . - - -a Same as -w but append rather then truncating . - - -c Look for php.ini in directory or use as ini. - - -n Pass -n option to the php binary (Do not use a php.ini). - - -d foo=bar Pass -d option to the php binary (Define INI entry foo - with value 'bar'). - - -g Comma separated list of groups to show during test run - (possible values: PASS, FAIL, XFAIL, SKIP, BORK, WARN, LEAK, REDIRECT). - - -m Test for memory leaks with Valgrind. - - -p Specify PHP executable to run. - - -P Use PHP_BINARY as PHP executable to run. - - -q Quiet, no user interaction (same as environment NO_INTERACTION). - - -s Write output to . - - -x Sets 'SKIP_SLOW_TESTS' environmental variable. - - --offline Sets 'SKIP_ONLINE_TESTS' environmental variable. - - --verbose - -v Verbose mode. - - --help - -h This Help. - - --html Generate HTML output. - - --temp-source --temp-target [--temp-urlbase ] - Write temporary files to by replacing from the - filenames to generate with . If --html is being used and - given then the generated links are relative and prefixed - with the given url. In general you want to make the path - to your source files and some pach in your web page - hierarchy with pointing to . - - --keep-[all|php|skip|clean] - Do not delete 'all' files, 'php' test file, 'skip' or 'clean' - file. - - --set-timeout [n] - Set timeout for individual tests, where [n] is the number of - seconds. The default value is 60 seconds, or 300 seconds when - testing for memory leaks. - - --show-[all|php|skip|clean|exp|diff|out] - Show 'all' files, 'php' test file, 'skip' or 'clean' file. You - can also use this to show the output 'out', the expected result - 'exp' or the difference between them 'diff'. The result types - get written independent of the log format, however 'diff' only - exists when a test fails. - - --no-clean Do not execute clean section if any. - -HELP; - exit(1); - } - } - - if (!$is_switch) { - $testfile = realpath($argv[$i]); - - if (!$testfile && strpos($argv[$i], '*') !== false && function_exists('glob')) { - - if (preg_match("/\.phpt$/", $argv[$i])) { - $pattern_match = glob($argv[$i]); - } else if (preg_match("/\*$/", $argv[$i])) { - $pattern_match = glob($argv[$i] . '.phpt'); - } else { - die("bogus test name " . $argv[$i] . "\n"); - } - - if (is_array($pattern_match)) { - $test_files = array_merge($test_files, $pattern_match); - } - - } else if (is_dir($testfile)) { - find_files($testfile); - } else if (preg_match("/\.phpt$/", $testfile)) { - $test_files[] = $testfile; - } else { - die("bogus test name " . $argv[$i] . "\n"); - } - } - } - - if (strlen($conf_passed)) { - if (substr(PHP_OS, 0, 3) == "WIN") { - $pass_options .= " -c " . escapeshellarg($conf_passed); - } else { - $pass_options .= " -c '$conf_passed'"; - } - } - - $test_files = array_unique($test_files); - $test_files = array_merge($test_files, $redir_tests); - - // Run selected tests. - $test_cnt = count($test_files); - - if ($test_cnt) { - putenv('NO_INTERACTION=1'); - verify_config(); - write_information($html_output); - usort($test_files, "test_sort"); - $start_time = time(); - - if (!$html_output) { - echo "Running selected tests.\n"; - } else { - show_start($start_time); - } - - $test_idx = 0; - run_all_tests($test_files, $environment); - $end_time = time(); - - if ($html_output) { - show_end($end_time); - } - - if ($failed_tests_file) { - fclose($failed_tests_file); - } - - compute_summary(); - if ($html_output) { - fwrite($html_file, "
\n" . get_summary(false, true)); - } - echo "====================================================================="; - echo get_summary(false, false); - - if ($html_output) { - fclose($html_file); - } - - if ($output_file != '' && $just_save_results) { - save_or_mail_results(); - } - - junit_save_xml(); - - if (getenv('REPORT_EXIT_STATUS') == 1 && ($sum_results['FAILED'] || $sum_results['BORKED'])) { - exit(1); - } - - exit(0); - } -} - -verify_config(); -write_information($html_output); - -// Compile a list of all test files (*.phpt). -$test_files = array(); -$exts_tested = count($exts_to_test); -$exts_skipped = 0; -$ignored_by_ext = 0; -sort($exts_to_test); -$test_dirs = array(); -$optionals = array('tests', 'ext', 'Zend', 'sapi'); - -foreach($optionals as $dir) { - if (@filetype($dir) == 'dir') { - $test_dirs[] = $dir; - } -} - -// Convert extension names to lowercase -foreach ($exts_to_test as $key => $val) { - $exts_to_test[$key] = strtolower($val); -} - -foreach ($test_dirs as $dir) { - find_files("{$cwd}/{$dir}", ($dir == 'ext')); -} - -foreach ($user_tests as $dir) { - find_files($dir, ($dir == 'ext')); -} - -function find_files($dir, $is_ext_dir = false, $ignore = false) -{ - global $test_files, $exts_to_test, $ignored_by_ext, $exts_skipped, $exts_tested; - - $o = opendir($dir) or error("cannot open directory: $dir"); - - while (($name = readdir($o)) !== false) { - - if (is_dir("{$dir}/{$name}") && !in_array($name, array('.', '..', '.svn'))) { - $skip_ext = ($is_ext_dir && !in_array(strtolower($name), $exts_to_test)); - if ($skip_ext) { - $exts_skipped++; - } - find_files("{$dir}/{$name}", false, $ignore || $skip_ext); - } - - // Cleanup any left-over tmp files from last run. - if (substr($name, -4) == '.tmp') { - @unlink("$dir/$name"); - continue; - } - - // Otherwise we're only interested in *.phpt files. - if (substr($name, -5) == '.phpt') { - if ($ignore) { - $ignored_by_ext++; - } else { - $testfile = realpath("{$dir}/{$name}"); - $test_files[] = $testfile; - } - } - } - - closedir($o); -} - -function test_name($name) -{ - if (is_array($name)) { - return $name[0] . ':' . $name[1]; - } else { - return $name; - } -} - -function test_sort($a, $b) -{ - global $cwd; - - $a = test_name($a); - $b = test_name($b); - - $ta = strpos($a, "{$cwd}/tests") === 0 ? 1 + (strpos($a, "{$cwd}/tests/run-test") === 0 ? 1 : 0) : 0; - $tb = strpos($b, "{$cwd}/tests") === 0 ? 1 + (strpos($b, "{$cwd}/tests/run-test") === 0 ? 1 : 0) : 0; - - if ($ta == $tb) { - return strcmp($a, $b); - } else { - return $tb - $ta; - } -} - -$test_files = array_unique($test_files); -usort($test_files, "test_sort"); - -$start_time = time(); -show_start($start_time); - -$test_cnt = count($test_files); -$test_idx = 0; -run_all_tests($test_files, $environment); -$end_time = time(); - -if ($failed_tests_file) { - fclose($failed_tests_file); -} - -// Summarize results - -if (0 == count($test_results)) { - echo "No tests were run.\n"; - return; -} - -compute_summary(); - -show_end($end_time); -show_summary(); - -if ($html_output) { - fclose($html_file); -} - -save_or_mail_results(); - -junit_save_xml(); - -if (getenv('REPORT_EXIT_STATUS') == 1 && ($sum_results['FAILED'] || $sum_results['BORKED'])) { - exit(1); -} -exit(0); - -// -// Send Email to QA Team -// - -function mail_qa_team($data, $compression, $status = false) -{ - $url_bits = parse_url(QA_SUBMISSION_PAGE); - - if (($proxy = getenv('http_proxy'))) { - $proxy = parse_url($proxy); - $path = $url_bits['host'].$url_bits['path']; - $host = $proxy['host']; - if (empty($proxy['port'])) { - $proxy['port'] = 80; - } - $port = $proxy['port']; - } else { - $path = $url_bits['path']; - $host = $url_bits['host']; - $port = empty($url_bits['port']) ? 80 : $port = $url_bits['port']; - } - - $data = "php_test_data=" . urlencode(base64_encode(str_replace("\00", '[0x0]', $data))); - $data_length = strlen($data); - - $fs = fsockopen($host, $port, $errno, $errstr, 10); - - if (!$fs) { - return false; - } - - $php_version = urlencode(TESTED_PHP_VERSION); - - echo "\nPosting to ". QA_SUBMISSION_PAGE . "\n"; - fwrite($fs, "POST " . $path . "?status=$status&version=$php_version HTTP/1.1\r\n"); - fwrite($fs, "Host: " . $host . "\r\n"); - fwrite($fs, "User-Agent: QA Browser 0.1\r\n"); - fwrite($fs, "Content-Type: application/x-www-form-urlencoded\r\n"); - fwrite($fs, "Content-Length: " . $data_length . "\r\n\r\n"); - fwrite($fs, $data); - fwrite($fs, "\r\n\r\n"); - fclose($fs); - - return 1; -} - - -// -// Write the given text to a temporary file, and return the filename. -// - -function save_text($filename, $text, $filename_copy = null) -{ - global $DETAILED; - - if ($filename_copy && $filename_copy != $filename) { - if (file_put_contents($filename_copy, $text, FILE_BINARY) === false) { - error("Cannot open file '" . $filename_copy . "' (save_text)"); - } - } - - if (file_put_contents($filename, $text, FILE_BINARY) === false) { - error("Cannot open file '" . $filename . "' (save_text)"); - } - - if (1 < $DETAILED) echo " -FILE $filename {{{ -$text -}}} -"; -} - -// -// Write an error in a format recognizable to Emacs or MSVC. -// - -function error_report($testname, $logname, $tested) -{ - $testname = realpath($testname); - $logname = realpath($logname); - - switch (strtoupper(getenv('TEST_PHP_ERROR_STYLE'))) { - case 'MSVC': - echo $testname . "(1) : $tested\n"; - echo $logname . "(1) : $tested\n"; - break; - case 'EMACS': - echo $testname . ":1: $tested\n"; - echo $logname . ":1: $tested\n"; - break; - } -} - -function system_with_timeout($commandline, $env = null, $stdin = null) -{ - global $leak_check, $cwd; - - $data = ''; - - $bin_env = array(); - foreach((array)$env as $key => $value) { - $bin_env[$key] = $value; - } - - $proc = proc_open($commandline, array( - 0 => array('pipe', 'r'), - 1 => array('pipe', 'w'), - 2 => array('pipe', 'w') - ), $pipes, $cwd, $bin_env, array('suppress_errors' => true, 'binary_pipes' => true)); - - if (!$proc) { - return false; - } - - if (!is_null($stdin)) { - fwrite($pipes[0], $stdin); - } - fclose($pipes[0]); - unset($pipes[0]); - - $timeout = $leak_check ? 300 : (isset($env['TEST_TIMEOUT']) ? $env['TEST_TIMEOUT'] : 60); - - while (true) { - /* hide errors from interrupted syscalls */ - $r = $pipes; - $w = null; - $e = null; - - $n = @stream_select($r, $w, $e, $timeout); - - if ($n === false) { - break; - } else if ($n === 0) { - /* timed out */ - $data .= "\n ** ERROR: process timed out **\n"; - proc_terminate($proc, 9); - return $data; - } else if ($n > 0) { - $line = fread($pipes[1], 8192); - if (strlen($line) == 0) { - /* EOF */ - break; - } - $data .= $line; - } - } - - $stat = proc_get_status($proc); - - if ($stat['signaled']) { - $data .= "\nTermsig=" . $stat['stopsig'] . "\n"; - } - if ($stat["exitcode"] > 128 && $stat["exitcode"] < 160) { - $data .= "\nTermsig=" . ($stat["exitcode"] - 128) . "\n"; - } - - $code = proc_close($proc); - return $data; -} - -function run_all_tests($test_files, $env, $redir_tested = null) -{ - global $test_results, $failed_tests_file, $php, $test_cnt, $test_idx; - - foreach($test_files as $name) { - - if (is_array($name)) { - $index = "# $name[1]: $name[0]"; - - if ($redir_tested) { - $name = $name[0]; - } - } else if ($redir_tested) { - $index = "# $redir_tested: $name"; - } else { - $index = $name; - } - $test_idx++; - $result = run_test($php, $name, $env); - - if (!is_array($name) && $result != 'REDIR') { - $test_results[$index] = $result; - if ($failed_tests_file && ($result == 'XFAILED' || $result == 'FAILED' || $result == 'WARNED' || $result == 'LEAKED')) { - fwrite($failed_tests_file, "$index\n"); - } - } - } -} - -// -// Show file or result block -// -function show_file_block($file, $block, $section = null) -{ - global $cfg; - - if ($cfg['show'][$file]) { - - if (is_null($section)) { - $section = strtoupper($file); - } - - echo "\n========" . $section . "========\n"; - echo rtrim($block); - echo "\n========DONE========\n"; - } -} - -// -// Run an individual test case. -// -function run_test($php, $file, $env) -{ - global $log_format, $info_params, $ini_overwrites, $cwd, $PHP_FAILED_TESTS; - global $pass_options, $DETAILED, $IN_REDIRECT, $test_cnt, $test_idx; - global $leak_check, $temp_source, $temp_target, $cfg, $environment; - global $no_clean; - global $valgrind_version; - global $JUNIT; - global $SHOW_ONLY_GROUPS; - global $no_file_cache; - $temp_filenames = null; - $org_file = $file; - - if (isset($env['TEST_PHP_CGI_EXECUTABLE'])) { - $php_cgi = $env['TEST_PHP_CGI_EXECUTABLE']; - } - - if (isset($env['TEST_PHPDBG_EXECUTABLE'])) { - $phpdbg = $env['TEST_PHPDBG_EXECUTABLE']; - } - - if (is_array($file)) { - $file = $file[0]; - } - - if ($DETAILED) echo " -================= -TEST $file -"; - - // Load the sections of the test file. - $section_text = array('TEST' => ''); - - $fp = fopen($file, "rb") or error("Cannot open test file: $file"); - - $borked = false; - $bork_info = ''; - - if (!feof($fp)) { - $line = fgets($fp); - - if ($line === false) { - $bork_info = "cannot read test"; - $borked = true; - } - } else { - $bork_info = "empty test [$file]"; - $borked = true; - } - if (!$borked && strncmp('--TEST--', $line, 8)) { - $bork_info = "tests must start with --TEST-- [$file]"; - $borked = true; - } - - $section = 'TEST'; - $secfile = false; - $secdone = false; - - while (!feof($fp)) { - $line = fgets($fp); - - if ($line === false) { - break; - } - - // Match the beginning of a section. - if (preg_match('/^--([_A-Z]+)--/', $line, $r)) { - $section = $r[1]; - settype($section, 'string'); - - if (isset($section_text[$section])) { - $bork_info = "duplicated $section section"; - $borked = true; - } - - $section_text[$section] = ''; - $secfile = $section == 'FILE' || $section == 'FILEEOF' || $section == 'FILE_EXTERNAL'; - $secdone = false; - continue; - } - - // Add to the section text. - if (!$secdone) { - $section_text[$section] .= $line; - } - - // End of actual test? - if ($secfile && preg_match('/^===DONE===\s*$/', $line)) { - $secdone = true; - } - } - - // the redirect section allows a set of tests to be reused outside of - // a given test dir - if (!$borked) { - if (@count($section_text['REDIRECTTEST']) == 1) { - - if ($IN_REDIRECT) { - $borked = true; - $bork_info = "Can't redirect a test from within a redirected test"; - } else { - $borked = false; - } - - } else { - - if (!isset($section_text['PHPDBG']) && @count($section_text['FILE']) + @count($section_text['FILEEOF']) + @count($section_text['FILE_EXTERNAL']) != 1) { - $bork_info = "missing section --FILE--"; - $borked = true; - } - - if (@count($section_text['FILEEOF']) == 1) { - $section_text['FILE'] = preg_replace("/[\r\n]+$/", '', $section_text['FILEEOF']); - unset($section_text['FILEEOF']); - } - - foreach (array( 'FILE', 'EXPECT', 'EXPECTF', 'EXPECTREGEX' ) as $prefix) { - $key = $prefix . '_EXTERNAL'; - - if (@count($section_text[$key]) == 1) { - // don't allow tests to retrieve files from anywhere but this subdirectory - $section_text[$key] = dirname($file) . '/' . trim(str_replace('..', '', $section_text[$key])); - - if (file_exists($section_text[$key])) { - $section_text[$prefix] = file_get_contents($section_text[$key], FILE_BINARY); - unset($section_text[$key]); - } else { - $bork_info = "could not load --" . $key . "-- " . dirname($file) . '/' . trim($section_text[$key]); - $borked = true; - } - } - } - - if ((@count($section_text['EXPECT']) + @count($section_text['EXPECTF']) + @count($section_text['EXPECTREGEX'])) != 1) { - $bork_info = "missing section --EXPECT--, --EXPECTF-- or --EXPECTREGEX--"; - $borked = true; - } - } - } - fclose($fp); - - $shortname = str_replace($cwd . '/', '', $file); - $tested_file = $shortname; - - if ($borked) { - show_result("BORK", $bork_info, $tested_file); - $PHP_FAILED_TESTS['BORKED'][] = array ( - 'name' => $file, - 'test_name' => '', - 'output' => '', - 'diff' => '', - 'info' => "$bork_info [$file]", - ); - - junit_mark_test_as('BORK', $shortname, $tested_file, 0, $bork_info); - return 'BORKED'; - } - - $tested = trim($section_text['TEST']); - - /* For GET/POST/PUT tests, check if cgi sapi is available and if it is, use it. */ - if (!empty($section_text['GET']) || !empty($section_text['POST']) || !empty($section_text['GZIP_POST']) || !empty($section_text['DEFLATE_POST']) || !empty($section_text['POST_RAW']) || !empty($section_text['PUT']) || !empty($section_text['COOKIE']) || !empty($section_text['EXPECTHEADERS'])) { - if (isset($php_cgi)) { - $old_php = $php; - $php = $php_cgi . ' -C '; - } else if (!strncasecmp(PHP_OS, "win", 3) && file_exists(dirname($php) . "/php-cgi.exe")) { - $old_php = $php; - $php = realpath(dirname($php) . "/php-cgi.exe") . ' -C '; - } else { - if (file_exists(dirname($php) . "/../../sapi/cgi/php-cgi")) { - $old_php = $php; - $php = realpath(dirname($php) . "/../../sapi/cgi/php-cgi") . ' -C '; - } else if (file_exists("./sapi/cgi/php-cgi")) { - $old_php = $php; - $php = realpath("./sapi/cgi/php-cgi") . ' -C '; - } else if (file_exists(dirname($php) . "/php-cgi")) { - $old_php = $php; - $php = realpath(dirname($php) . "/php-cgi") . ' -C '; - } else { - show_result('SKIP', $tested, $tested_file, "reason: CGI not available"); - - junit_init_suite(junit_get_suitename_for($shortname)); - junit_mark_test_as('SKIP', $shortname, $tested, 0, 'CGI not available'); - return 'SKIPPED'; - } - } - $uses_cgi = true; - } - - /* For phpdbg tests, check if phpdbg sapi is available and if it is, use it. */ - if (array_key_exists('PHPDBG', $section_text)) { - if (!isset($section_text['STDIN'])) { - $section_text['STDIN'] = $section_text['PHPDBG']."\n"; - } - - if (isset($phpdbg)) { - $old_php = $php; - $php = $phpdbg . ' -qIb'; - } else { - show_result('SKIP', $tested, $tested_file, "reason: phpdbg not available"); - - junit_init_suite(junit_get_suitename_for($shortname)); - junit_mark_test_as('SKIP', $shortname, $tested, 0, 'phpdbg not available'); - return 'SKIPPED'; - } - } - - if (!$SHOW_ONLY_GROUPS) { - show_test($test_idx, $shortname); - } - - if (is_array($IN_REDIRECT)) { - $temp_dir = $test_dir = $IN_REDIRECT['dir']; - } else { - $temp_dir = $test_dir = realpath(dirname($file)); - } - - if ($temp_source && $temp_target) { - $temp_dir = str_replace($temp_source, $temp_target, $temp_dir); - } - - $main_file_name = basename($file,'phpt'); - - $diff_filename = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name . 'diff'; - $log_filename = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name . 'log'; - $exp_filename = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name . 'exp'; - $output_filename = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name . 'out'; - $memcheck_filename = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name . 'mem'; - $sh_filename = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name . 'sh'; - $temp_file = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name . 'php'; - $test_file = $test_dir . DIRECTORY_SEPARATOR . $main_file_name . 'php'; - $temp_skipif = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name . 'skip.php'; - $test_skipif = $test_dir . DIRECTORY_SEPARATOR . $main_file_name . 'skip.php'; - $temp_clean = $temp_dir . DIRECTORY_SEPARATOR . $main_file_name . 'clean.php'; - $test_clean = $test_dir . DIRECTORY_SEPARATOR . $main_file_name . 'clean.php'; - $tmp_post = $temp_dir . DIRECTORY_SEPARATOR . uniqid('/phpt.'); - $tmp_relative_file = str_replace(__DIR__ . DIRECTORY_SEPARATOR, '', $test_file) . 't'; - - if ($temp_source && $temp_target) { - $temp_skipif .= 's'; - $temp_file .= 's'; - $temp_clean .= 's'; - $copy_file = $temp_dir . DIRECTORY_SEPARATOR . basename(is_array($file) ? $file[1] : $file) . '.phps'; - - if (!is_dir(dirname($copy_file))) { - mkdir(dirname($copy_file), 0777, true) or error("Cannot create output directory - " . dirname($copy_file)); - } - - if (isset($section_text['FILE'])) { - save_text($copy_file, $section_text['FILE']); - } - - $temp_filenames = array( - 'file' => $copy_file, - 'diff' => $diff_filename, - 'log' => $log_filename, - 'exp' => $exp_filename, - 'out' => $output_filename, - 'mem' => $memcheck_filename, - 'sh' => $sh_filename, - 'php' => $temp_file, - 'skip' => $temp_skipif, - 'clean'=> $temp_clean); - } - - if (is_array($IN_REDIRECT)) { - $tested = $IN_REDIRECT['prefix'] . ' ' . trim($section_text['TEST']); - $tested_file = $tmp_relative_file; - } - - // unlink old test results - @unlink($diff_filename); - @unlink($log_filename); - @unlink($exp_filename); - @unlink($output_filename); - @unlink($memcheck_filename); - @unlink($sh_filename); - @unlink($temp_file); - @unlink($test_file); - @unlink($temp_skipif); - @unlink($test_skipif); - @unlink($tmp_post); - @unlink($temp_clean); - @unlink($test_clean); - - // Reset environment from any previous test. - $env['REDIRECT_STATUS'] = ''; - $env['QUERY_STRING'] = ''; - $env['PATH_TRANSLATED'] = ''; - $env['SCRIPT_FILENAME'] = ''; - $env['REQUEST_METHOD'] = ''; - $env['CONTENT_TYPE'] = ''; - $env['CONTENT_LENGTH'] = ''; - $env['TZ'] = ''; - - if (!empty($section_text['ENV'])) { - - foreach(explode("\n", trim($section_text['ENV'])) as $e) { - $e = explode('=', trim($e), 2); - - if (!empty($e[0]) && isset($e[1])) { - $env[$e[0]] = $e[1]; - } - } - } - - // Default ini settings - $ini_settings = array(); - - // Additional required extensions - if (array_key_exists('EXTENSIONS', $section_text)) { - $ext_dir=`$php -r 'echo ini_get("extension_dir");'`; - $extensions = preg_split("/[\n\r]+/", trim($section_text['EXTENSIONS'])); - $loaded = explode(",", `$php -n -r 'echo join(",", get_loaded_extensions());'`); - foreach ($extensions as $req_ext) { - if (!in_array($req_ext, $loaded)) { - if ($req_ext == 'opcache') { - $ini_settings['zend_extension'][] = $ext_dir . DIRECTORY_SEPARATOR . $req_ext . '.' . PHP_SHLIB_SUFFIX; - } else { - $ini_settings['extension'][] = $ext_dir . DIRECTORY_SEPARATOR . $req_ext . '.' . PHP_SHLIB_SUFFIX; - } - } - } - } - - // additional ini overwrites - //$ini_overwrites[] = 'setting=value'; - settings2array($ini_overwrites, $ini_settings); - - // Any special ini settings - // these may overwrite the test defaults... - if (array_key_exists('INI', $section_text)) { - if (strpos($section_text['INI'], '{PWD}') !== false) { - $section_text['INI'] = str_replace('{PWD}', dirname($file), $section_text['INI']); - } - settings2array(preg_split( "/[\n\r]+/", $section_text['INI']), $ini_settings); - } - - settings2params($ini_settings); - - // Check if test should be skipped. - $info = ''; - $warn = false; - - if (array_key_exists('SKIPIF', $section_text)) { - - if (trim($section_text['SKIPIF'])) { - show_file_block('skip', $section_text['SKIPIF']); - save_text($test_skipif, $section_text['SKIPIF'], $temp_skipif); - $extra = substr(PHP_OS, 0, 3) !== "WIN" ? - "unset REQUEST_METHOD; unset QUERY_STRING; unset PATH_TRANSLATED; unset SCRIPT_FILENAME; unset REQUEST_METHOD;": ""; - - if ($leak_check) { - $env['USE_ZEND_ALLOC'] = '0'; - $env['ZEND_DONT_UNLOAD_MODULES'] = 1; - } else { - $env['USE_ZEND_ALLOC'] = '1'; - $env['ZEND_DONT_UNLOAD_MODULES'] = 0; - } - - junit_start_timer($shortname); - - $output = system_with_timeout("$extra $php $pass_options -q $ini_settings $no_file_cache -d display_errors=0 \"$test_skipif\"", $env); - - junit_finish_timer($shortname); - - if (!$cfg['keep']['skip']) { - @unlink($test_skipif); - } - - if (!strncasecmp('skip', ltrim($output), 4)) { - - if (preg_match('/^\s*skip\s*(.+)\s*/i', $output, $m)) { - show_result('SKIP', $tested, $tested_file, "reason: $m[1]", $temp_filenames); - } else { - show_result('SKIP', $tested, $tested_file, '', $temp_filenames); - } - - if (isset($old_php)) { - $php = $old_php; - } - - if (!$cfg['keep']['skip']) { - @unlink($test_skipif); - } - - $message = !empty($m[1]) ? $m[1] : ''; - junit_mark_test_as('SKIP', $shortname, $tested, null, $message); - return 'SKIPPED'; - } - - if (!strncasecmp('info', ltrim($output), 4)) { - if (preg_match('/^\s*info\s*(.+)\s*/i', $output, $m)) { - $info = " (info: $m[1])"; - } - } - - if (!strncasecmp('warn', ltrim($output), 4)) { - if (preg_match('/^\s*warn\s*(.+)\s*/i', $output, $m)) { - $warn = true; /* only if there is a reason */ - $info = " (warn: $m[1])"; - } - } - - if (!strncasecmp('xfail', ltrim($output), 5)) { - // Pretend we have an XFAIL section - $section_text['XFAIL'] = trim(substr(ltrim($output), 5)); - } - } - } - - if (!extension_loaded("zlib") - && ( array_key_exists("GZIP_POST", $section_text) - || array_key_exists("DEFLATE_POST", $section_text)) - ) { - $message = "ext/zlib required"; - show_result('SKIP', $tested, $tested_file, "reason: $message", $temp_filenames); - junit_mark_test_as('SKIP', $shortname, $tested, null, $message); - return 'SKIPPED'; - } - - if (@count($section_text['REDIRECTTEST']) == 1) { - $test_files = array(); - - $IN_REDIRECT = eval($section_text['REDIRECTTEST']); - $IN_REDIRECT['via'] = "via [$shortname]\n\t"; - $IN_REDIRECT['dir'] = realpath(dirname($file)); - $IN_REDIRECT['prefix'] = trim($section_text['TEST']); - - if (count($IN_REDIRECT['TESTS']) == 1) { - - if (is_array($org_file)) { - $test_files[] = $org_file[1]; - } else { - $GLOBALS['test_files'] = $test_files; - find_files($IN_REDIRECT['TESTS']); - - foreach($GLOBALS['test_files'] as $f) { - $test_files[] = array($f, $file); - } - } - $test_cnt += @count($test_files) - 1; - $test_idx--; - - show_redirect_start($IN_REDIRECT['TESTS'], $tested, $tested_file); - - // set up environment - $redirenv = array_merge($environment, $IN_REDIRECT['ENV']); - $redirenv['REDIR_TEST_DIR'] = realpath($IN_REDIRECT['TESTS']) . DIRECTORY_SEPARATOR; - - usort($test_files, "test_sort"); - run_all_tests($test_files, $redirenv, $tested); - - show_redirect_ends($IN_REDIRECT['TESTS'], $tested, $tested_file); - - // a redirected test never fails - $IN_REDIRECT = false; - - junit_mark_test_as('PASS', $shortname, $tested); - return 'REDIR'; - - } else { - - $bork_info = "Redirect info must contain exactly one TEST string to be used as redirect directory."; - show_result("BORK", $bork_info, '', $temp_filenames); - $PHP_FAILED_TESTS['BORKED'][] = array ( - 'name' => $file, - 'test_name' => '', - 'output' => '', - 'diff' => '', - 'info' => "$bork_info [$file]", - ); - } - } - - if (is_array($org_file) || @count($section_text['REDIRECTTEST']) == 1) { - - if (is_array($org_file)) { - $file = $org_file[0]; - } - - $bork_info = "Redirected test did not contain redirection info"; - show_result("BORK", $bork_info, '', $temp_filenames); - $PHP_FAILED_TESTS['BORKED'][] = array ( - 'name' => $file, - 'test_name' => '', - 'output' => '', - 'diff' => '', - 'info' => "$bork_info [$file]", - ); - - junit_mark_test_as('BORK', $shortname, $tested, null, $bork_info); - - return 'BORKED'; - } - - // We've satisfied the preconditions - run the test! - if (isset($section_text['FILE'])) { - show_file_block('php', $section_text['FILE'], 'TEST'); - save_text($test_file, $section_text['FILE'], $temp_file); - } else { - $test_file = $temp_file = ""; - } - - if (array_key_exists('GET', $section_text)) { - $query_string = trim($section_text['GET']); - } else { - $query_string = ''; - } - - $env['REDIRECT_STATUS'] = '1'; - if (empty($env['QUERY_STRING'])) { - $env['QUERY_STRING'] = $query_string; - } - if (empty($env['PATH_TRANSLATED'])) { - $env['PATH_TRANSLATED'] = $test_file; - } - if (empty($env['SCRIPT_FILENAME'])) { - $env['SCRIPT_FILENAME'] = $test_file; - } - - if (array_key_exists('COOKIE', $section_text)) { - $env['HTTP_COOKIE'] = trim($section_text['COOKIE']); - } else { - $env['HTTP_COOKIE'] = ''; - } - - $args = isset($section_text['ARGS']) ? ' -- ' . $section_text['ARGS'] : ''; - - if (array_key_exists('POST_RAW', $section_text) && !empty($section_text['POST_RAW'])) { - - $post = trim($section_text['POST_RAW']); - $raw_lines = explode("\n", $post); - - $request = ''; - $started = false; - - foreach ($raw_lines as $line) { - - if (empty($env['CONTENT_TYPE']) && preg_match('/^Content-Type:(.*)/i', $line, $res)) { - $env['CONTENT_TYPE'] = trim(str_replace("\r", '', $res[1])); - continue; - } - - if ($started) { - $request .= "\n"; - } - - $started = true; - $request .= $line; - } - - $env['CONTENT_LENGTH'] = strlen($request); - $env['REQUEST_METHOD'] = 'POST'; - - if (empty($request)) { - junit_mark_test_as('BORK', $shortname, $tested, null, 'empty $request'); - return 'BORKED'; - } - - save_text($tmp_post, $request); - $cmd = "$php $pass_options $ini_settings -f \"$test_file\" 2>&1 < \"$tmp_post\""; - - } elseif (array_key_exists('PUT', $section_text) && !empty($section_text['PUT'])) { - - $post = trim($section_text['PUT']); - $raw_lines = explode("\n", $post); - - $request = ''; - $started = false; - - foreach ($raw_lines as $line) { - - if (empty($env['CONTENT_TYPE']) && preg_match('/^Content-Type:(.*)/i', $line, $res)) { - $env['CONTENT_TYPE'] = trim(str_replace("\r", '', $res[1])); - continue; - } - - if ($started) { - $request .= "\n"; - } - - $started = true; - $request .= $line; - } - - $env['CONTENT_LENGTH'] = strlen($request); - $env['REQUEST_METHOD'] = 'PUT'; - - if (empty($request)) { - junit_mark_test_as('BORK', $shortname, $tested, null, 'empty $request'); - return 'BORKED'; - } - - save_text($tmp_post, $request); - $cmd = "$php $pass_options $ini_settings -f \"$test_file\" 2>&1 < \"$tmp_post\""; - - } else if (array_key_exists('POST', $section_text) && !empty($section_text['POST'])) { - - $post = trim($section_text['POST']); - $content_length = strlen($post); - save_text($tmp_post, $post); - - $env['REQUEST_METHOD'] = 'POST'; - if (empty($env['CONTENT_TYPE'])) { - $env['CONTENT_TYPE'] = 'application/x-www-form-urlencoded'; - } - - if (empty($env['CONTENT_LENGTH'])) { - $env['CONTENT_LENGTH'] = $content_length; - } - - $cmd = "$php $pass_options $ini_settings -f \"$test_file\" 2>&1 < \"$tmp_post\""; - - } else if (array_key_exists('GZIP_POST', $section_text) && !empty($section_text['GZIP_POST'])) { - - $post = trim($section_text['GZIP_POST']); - $post = gzencode($post, 9, FORCE_GZIP); - $env['HTTP_CONTENT_ENCODING'] = 'gzip'; - - save_text($tmp_post, $post); - $content_length = strlen($post); - - $env['REQUEST_METHOD'] = 'POST'; - $env['CONTENT_TYPE'] = 'application/x-www-form-urlencoded'; - $env['CONTENT_LENGTH'] = $content_length; - - $cmd = "$php $pass_options $ini_settings -f \"$test_file\" 2>&1 < \"$tmp_post\""; - - } else if (array_key_exists('DEFLATE_POST', $section_text) && !empty($section_text['DEFLATE_POST'])) { - $post = trim($section_text['DEFLATE_POST']); - $post = gzcompress($post, 9); - $env['HTTP_CONTENT_ENCODING'] = 'deflate'; - save_text($tmp_post, $post); - $content_length = strlen($post); - - $env['REQUEST_METHOD'] = 'POST'; - $env['CONTENT_TYPE'] = 'application/x-www-form-urlencoded'; - $env['CONTENT_LENGTH'] = $content_length; - - $cmd = "$php $pass_options $ini_settings -f \"$test_file\" 2>&1 < \"$tmp_post\""; - - } else { - - $env['REQUEST_METHOD'] = 'GET'; - $env['CONTENT_TYPE'] = ''; - $env['CONTENT_LENGTH'] = ''; - - $cmd = "$php $pass_options $ini_settings -f \"$test_file\" $args 2>&1"; - } - - if ($leak_check) { - $env['USE_ZEND_ALLOC'] = '0'; - $env['ZEND_DONT_UNLOAD_MODULES'] = 1; - - /* --vex-iropt-register-updates=allregs-at-mem-access is necessary for phpdbg watchpoint tests */ - if (version_compare($valgrind_version, '3.8.0', '>=')) { - /* valgrind 3.3.0+ doesn't have --log-file-exactly option */ - $cmd = "valgrind -q --tool=memcheck --trace-children=yes --vex-iropt-register-updates=allregs-at-mem-access --log-file=$memcheck_filename $cmd"; - } elseif (version_compare($valgrind_version, '3.3.0', '>=')) { - $cmd = "valgrind -q --tool=memcheck --trace-children=yes --vex-iropt-precise-memory-exns=yes --log-file=$memcheck_filename $cmd"; - } else { - $cmd = "valgrind -q --tool=memcheck --trace-children=yes --vex-iropt-precise-memory-exns=yes --log-file-exactly=$memcheck_filename $cmd"; - } - - } else { - $env['USE_ZEND_ALLOC'] = '1'; - $env['ZEND_DONT_UNLOAD_MODULES'] = 0; - } - - if ($DETAILED) echo " -CONTENT_LENGTH = " . $env['CONTENT_LENGTH'] . " -CONTENT_TYPE = " . $env['CONTENT_TYPE'] . " -PATH_TRANSLATED = " . $env['PATH_TRANSLATED'] . " -QUERY_STRING = " . $env['QUERY_STRING'] . " -REDIRECT_STATUS = " . $env['REDIRECT_STATUS'] . " -REQUEST_METHOD = " . $env['REQUEST_METHOD'] . " -SCRIPT_FILENAME = " . $env['SCRIPT_FILENAME'] . " -HTTP_COOKIE = " . $env['HTTP_COOKIE'] . " -COMMAND $cmd -"; - - junit_start_timer($shortname); - - $out = system_with_timeout($cmd, $env, isset($section_text['STDIN']) ? $section_text['STDIN'] : null); - - junit_finish_timer($shortname); - - if (array_key_exists('CLEAN', $section_text) && (!$no_clean || $cfg['keep']['clean'])) { - - if (trim($section_text['CLEAN'])) { - show_file_block('clean', $section_text['CLEAN']); - save_text($test_clean, trim($section_text['CLEAN']), $temp_clean); - - if (!$no_clean) { - $clean_params = array(); - settings2array($ini_overwrites, $clean_params); - settings2params($clean_params); - $extra = substr(PHP_OS, 0, 3) !== "WIN" ? - "unset REQUEST_METHOD; unset QUERY_STRING; unset PATH_TRANSLATED; unset SCRIPT_FILENAME; unset REQUEST_METHOD;": ""; - system_with_timeout("$extra $php $pass_options -q $clean_params $no_file_cache \"$test_clean\"", $env); - } - - if (!$cfg['keep']['clean']) { - @unlink($test_clean); - } - } - } - - @unlink($tmp_post); - - $leaked = false; - $passed = false; - - if ($leak_check) { // leak check - $leaked = filesize($memcheck_filename) > 0; - - if (!$leaked) { - @unlink($memcheck_filename); - } - } - - // Does the output match what is expected? - $output = preg_replace("/\r\n/", "\n", trim($out)); - - /* when using CGI, strip the headers from the output */ - $headers = ""; - - if (!empty($uses_cgi) && preg_match("/^(.*?)\r?\n\r?\n(.*)/s", $out, $match)) { - $output = trim($match[2]); - $rh = preg_split("/[\n\r]+/", $match[1]); - $headers = array(); - - foreach ($rh as $line) { - if (strpos($line, ':') !== false) { - $line = explode(':', $line, 2); - $headers[trim($line[0])] = trim($line[1]); - } - } - } - - $failed_headers = false; - - if (isset($section_text['EXPECTHEADERS'])) { - $want = array(); - $wanted_headers = array(); - $lines = preg_split("/[\n\r]+/", $section_text['EXPECTHEADERS']); - - foreach($lines as $line) { - if (strpos($line, ':') !== false) { - $line = explode(':', $line, 2); - $want[trim($line[0])] = trim($line[1]); - $wanted_headers[] = trim($line[0]) . ': ' . trim($line[1]); - } - } - - $org_headers = $headers; - $headers = array(); - $output_headers = array(); - - foreach($want as $k => $v) { - - if (isset($org_headers[$k])) { - $headers = $org_headers[$k]; - $output_headers[] = $k . ': ' . $org_headers[$k]; - } - - if (!isset($org_headers[$k]) || $org_headers[$k] != $v) { - $failed_headers = true; - } - } - - ksort($wanted_headers); - $wanted_headers = join("\n", $wanted_headers); - ksort($output_headers); - $output_headers = join("\n", $output_headers); - } - - show_file_block('out', $output); - - if (isset($section_text['EXPECTF']) || isset($section_text['EXPECTREGEX'])) { - - if (isset($section_text['EXPECTF'])) { - $wanted = trim($section_text['EXPECTF']); - } else { - $wanted = trim($section_text['EXPECTREGEX']); - } - - show_file_block('exp', $wanted); - $wanted_re = preg_replace('/\r\n/', "\n", $wanted); - - if (isset($section_text['EXPECTF'])) { - - // do preg_quote, but miss out any %r delimited sections - $temp = ""; - $r = "%r"; - $startOffset = 0; - $length = strlen($wanted_re); - while($startOffset < $length) { - $start = strpos($wanted_re, $r, $startOffset); - if ($start !== false) { - // we have found a start tag - $end = strpos($wanted_re, $r, $start+2); - if ($end === false) { - // unbalanced tag, ignore it. - $end = $start = $length; - } - } else { - // no more %r sections - $start = $end = $length; - } - // quote a non re portion of the string - $temp = $temp . preg_quote(substr($wanted_re, $startOffset, ($start - $startOffset)), '/'); - // add the re unquoted. - if ($end > $start) { - $temp = $temp . '(' . substr($wanted_re, $start+2, ($end - $start-2)). ')'; - } - $startOffset = $end + 2; - } - $wanted_re = $temp; - - $wanted_re = str_replace( - array('%binary_string_optional%'), - 'string', - $wanted_re - ); - $wanted_re = str_replace( - array('%unicode_string_optional%'), - 'string', - $wanted_re - ); - $wanted_re = str_replace( - array('%unicode\|string%', '%string\|unicode%'), - 'string', - $wanted_re - ); - $wanted_re = str_replace( - array('%u\|b%', '%b\|u%'), - '', - $wanted_re - ); - // Stick to basics - $wanted_re = str_replace('%e', '\\' . DIRECTORY_SEPARATOR, $wanted_re); - $wanted_re = str_replace('%s', '[^\r\n]+', $wanted_re); - $wanted_re = str_replace('%S', '[^\r\n]*', $wanted_re); - $wanted_re = str_replace('%a', '.+', $wanted_re); - $wanted_re = str_replace('%A', '.*', $wanted_re); - $wanted_re = str_replace('%w', '\s*', $wanted_re); - $wanted_re = str_replace('%i', '[+-]?\d+', $wanted_re); - $wanted_re = str_replace('%d', '\d+', $wanted_re); - $wanted_re = str_replace('%x', '[0-9a-fA-F]+', $wanted_re); - $wanted_re = str_replace('%f', '[+-]?\.?\d+\.?\d*(?:[Ee][+-]?\d+)?', $wanted_re); - $wanted_re = str_replace('%c', '.', $wanted_re); - // %f allows two points "-.0.0" but that is the best *simple* expression - } -/* DEBUG YOUR REGEX HERE - var_dump($wanted_re); - print(str_repeat('=', 80) . "\n"); - var_dump($output); -*/ - if (preg_match("/^$wanted_re\$/s", $output)) { - $passed = true; - if (!$cfg['keep']['php']) { - @unlink($test_file); - } - if (isset($old_php)) { - $php = $old_php; - } - - if (!$leaked && !$failed_headers) { - if (isset($section_text['XFAIL'] )) { - $warn = true; - $info = " (warn: XFAIL section but test passes)"; - }else { - show_result("PASS", $tested, $tested_file, '', $temp_filenames); - junit_mark_test_as('PASS', $shortname, $tested); - return 'PASSED'; - } - } - } - - } else { - - $wanted = trim($section_text['EXPECT']); - $wanted = preg_replace('/\r\n/',"\n", $wanted); - show_file_block('exp', $wanted); - - // compare and leave on success - if (!strcmp($output, $wanted)) { - $passed = true; - - if (!$cfg['keep']['php']) { - @unlink($test_file); - } - - if (isset($old_php)) { - $php = $old_php; - } - - if (!$leaked && !$failed_headers) { - if (isset($section_text['XFAIL'] )) { - $warn = true; - $info = " (warn: XFAIL section but test passes)"; - }else { - show_result("PASS", $tested, $tested_file, '', $temp_filenames); - junit_mark_test_as('PASS', $shortname, $tested); - return 'PASSED'; - } - } - } - - $wanted_re = null; - } - - // Test failed so we need to report details. - if ($failed_headers) { - $passed = false; - $wanted = $wanted_headers . "\n--HEADERS--\n" . $wanted; - $output = $output_headers . "\n--HEADERS--\n" . $output; - - if (isset($wanted_re)) { - $wanted_re = preg_quote($wanted_headers . "\n--HEADERS--\n", '/') . $wanted_re; - } - } - - if ($leaked) { - $restype[] = 'LEAK'; - } - - if ($warn) { - $restype[] = 'WARN'; - } - - if (!$passed) { - if (isset($section_text['XFAIL'])) { - $restype[] = 'XFAIL'; - $info = ' XFAIL REASON: ' . rtrim($section_text['XFAIL']); - } else { - $restype[] = 'FAIL'; - } - } - - if (!$passed) { - - // write .exp - if (strpos($log_format, 'E') !== false && file_put_contents($exp_filename, $wanted, FILE_BINARY) === false) { - error("Cannot create expected test output - $exp_filename"); - } - - // write .out - if (strpos($log_format, 'O') !== false && file_put_contents($output_filename, $output, FILE_BINARY) === false) { - error("Cannot create test output - $output_filename"); - } - - // write .diff - $diff = generate_diff($wanted, $wanted_re, $output); - if (is_array($IN_REDIRECT)) { - $diff = "# original source file: $shortname\n" . $diff; - } - show_file_block('diff', $diff); - if (strpos($log_format, 'D') !== false && file_put_contents($diff_filename, $diff, FILE_BINARY) === false) { - error("Cannot create test diff - $diff_filename"); - } - - // write .sh - if (strpos($log_format, 'S') !== false && file_put_contents($sh_filename, "#!/bin/sh - -{$cmd} -", FILE_BINARY) === false) { - error("Cannot create test shell script - $sh_filename"); - } - chmod($sh_filename, 0755); - - // write .log - if (strpos($log_format, 'L') !== false && file_put_contents($log_filename, " ----- EXPECTED OUTPUT -$wanted ----- ACTUAL OUTPUT -$output ----- FAILED -", FILE_BINARY) === false) { - error("Cannot create test log - $log_filename"); - error_report($file, $log_filename, $tested); - } - } - - show_result(implode('&', $restype), $tested, $tested_file, $info, $temp_filenames); - - foreach ($restype as $type) { - $PHP_FAILED_TESTS[$type.'ED'][] = array ( - 'name' => $file, - 'test_name' => (is_array($IN_REDIRECT) ? $IN_REDIRECT['via'] : '') . $tested . " [$tested_file]", - 'output' => $output_filename, - 'diff' => $diff_filename, - 'info' => $info, - ); - } - - if (isset($old_php)) { - $php = $old_php; - } - - $diff = empty($diff) ? '' : preg_replace('/\e/', '', $diff); - - junit_mark_test_as($restype, str_replace($cwd . '/', '', $tested_file), $tested, null, $info, $diff); - - return $restype[0] . 'ED'; -} - -function comp_line($l1, $l2, $is_reg) -{ - if ($is_reg) { - return preg_match('/^'. $l1 . '$/s', $l2); - } else { - return !strcmp($l1, $l2); - } -} - -function count_array_diff($ar1, $ar2, $is_reg, $w, $idx1, $idx2, $cnt1, $cnt2, $steps) -{ - $equal = 0; - - while ($idx1 < $cnt1 && $idx2 < $cnt2 && comp_line($ar1[$idx1], $ar2[$idx2], $is_reg)) { - $idx1++; - $idx2++; - $equal++; - $steps--; - } - if (--$steps > 0) { - $eq1 = 0; - $st = $steps / 2; - - for ($ofs1 = $idx1 + 1; $ofs1 < $cnt1 && $st-- > 0; $ofs1++) { - $eq = @count_array_diff($ar1, $ar2, $is_reg, $w, $ofs1, $idx2, $cnt1, $cnt2, $st); - - if ($eq > $eq1) { - $eq1 = $eq; - } - } - - $eq2 = 0; - $st = $steps; - - for ($ofs2 = $idx2 + 1; $ofs2 < $cnt2 && $st-- > 0; $ofs2++) { - $eq = @count_array_diff($ar1, $ar2, $is_reg, $w, $idx1, $ofs2, $cnt1, $cnt2, $st); - if ($eq > $eq2) { - $eq2 = $eq; - } - } - - if ($eq1 > $eq2) { - $equal += $eq1; - } else if ($eq2 > 0) { - $equal += $eq2; - } - } - - return $equal; -} - -function generate_array_diff($ar1, $ar2, $is_reg, $w) -{ - $idx1 = 0; $ofs1 = 0; $cnt1 = @count($ar1); - $idx2 = 0; $ofs2 = 0; $cnt2 = @count($ar2); - $diff = array(); - $old1 = array(); - $old2 = array(); - - while ($idx1 < $cnt1 && $idx2 < $cnt2) { - - if (comp_line($ar1[$idx1], $ar2[$idx2], $is_reg)) { - $idx1++; - $idx2++; - continue; - } else { - - $c1 = @count_array_diff($ar1, $ar2, $is_reg, $w, $idx1+1, $idx2, $cnt1, $cnt2, 10); - $c2 = @count_array_diff($ar1, $ar2, $is_reg, $w, $idx1, $idx2+1, $cnt1, $cnt2, 10); - - if ($c1 > $c2) { - $old1[$idx1] = sprintf("%03d- ", $idx1+1) . $w[$idx1++]; - $last = 1; - } else if ($c2 > 0) { - $old2[$idx2] = sprintf("%03d+ ", $idx2+1) . $ar2[$idx2++]; - $last = 2; - } else { - $old1[$idx1] = sprintf("%03d- ", $idx1+1) . $w[$idx1++]; - $old2[$idx2] = sprintf("%03d+ ", $idx2+1) . $ar2[$idx2++]; - } - } - } - - reset($old1); $k1 = key($old1); $l1 = -2; - reset($old2); $k2 = key($old2); $l2 = -2; - - while ($k1 !== null || $k2 !== null) { - - if ($k1 == $l1 + 1 || $k2 === null) { - $l1 = $k1; - $diff[] = current($old1); - $k1 = next($old1) ? key($old1) : null; - } else if ($k2 == $l2 + 1 || $k1 === null) { - $l2 = $k2; - $diff[] = current($old2); - $k2 = next($old2) ? key($old2) : null; - } else if ($k1 < $k2) { - $l1 = $k1; - $diff[] = current($old1); - $k1 = next($old1) ? key($old1) : null; - } else { - $l2 = $k2; - $diff[] = current($old2); - $k2 = next($old2) ? key($old2) : null; - } - } - - while ($idx1 < $cnt1) { - $diff[] = sprintf("%03d- ", $idx1 + 1) . $w[$idx1++]; - } - - while ($idx2 < $cnt2) { - $diff[] = sprintf("%03d+ ", $idx2 + 1) . $ar2[$idx2++]; - } - - return $diff; -} - -function generate_diff($wanted, $wanted_re, $output) -{ - $w = explode("\n", $wanted); - $o = explode("\n", $output); - $r = is_null($wanted_re) ? $w : explode("\n", $wanted_re); - $diff = generate_array_diff($r, $o, !is_null($wanted_re), $w); - - return implode("\r\n", $diff); -} - -function error($message) -{ - echo "ERROR: {$message}\n"; - exit(1); -} - -function settings2array($settings, &$ini_settings) -{ - foreach($settings as $setting) { - - if (strpos($setting, '=') !== false) { - $setting = explode("=", $setting, 2); - $name = trim($setting[0]); - $value = trim($setting[1]); - - if ($name == 'extension' || $name == 'zend_extension') { - - if (!isset($ini_settings[$name])) { - $ini_settings[$name] = array(); - } - - $ini_settings[$name][] = $value; - - } else { - $ini_settings[$name] = $value; - } - } - } -} - -function settings2params(&$ini_settings) -{ - $settings = ''; - - foreach($ini_settings as $name => $value) { - - if (is_array($value)) { - foreach($value as $val) { - $val = addslashes($val); - $settings .= " -d \"$name=$val\""; - } - } else { - if (substr(PHP_OS, 0, 3) == "WIN" && !empty($value) && $value{0} == '"') { - $len = strlen($value); - - if ($value{$len - 1} == '"') { - $value{0} = "'"; - $value{$len - 1} = "'"; - } - } else { - $value = addslashes($value); - } - - $settings .= " -d \"$name=$value\""; - } - } - - $ini_settings = $settings; -} - -function compute_summary() -{ - global $n_total, $test_results, $ignored_by_ext, $sum_results, $percent_results; - - $n_total = count($test_results); - $n_total += $ignored_by_ext; - $sum_results = array( - 'PASSED' => 0, - 'WARNED' => 0, - 'SKIPPED' => 0, - 'FAILED' => 0, - 'BORKED' => 0, - 'LEAKED' => 0, - 'XFAILED' => 0 - ); - - foreach ($test_results as $v) { - $sum_results[$v]++; - } - - $sum_results['SKIPPED'] += $ignored_by_ext; - $percent_results = array(); - - while (list($v, $n) = each($sum_results)) { - $percent_results[$v] = (100.0 * $n) / $n_total; - } -} - -function get_summary($show_ext_summary, $show_html) -{ - global $exts_skipped, $exts_tested, $n_total, $sum_results, $percent_results, $end_time, $start_time, $failed_test_summary, $PHP_FAILED_TESTS, $leak_check; - - $x_total = $n_total - $sum_results['SKIPPED'] - $sum_results['BORKED']; - - if ($x_total) { - $x_warned = (100.0 * $sum_results['WARNED']) / $x_total; - $x_failed = (100.0 * $sum_results['FAILED']) / $x_total; - $x_xfailed = (100.0 * $sum_results['XFAILED']) / $x_total; - $x_leaked = (100.0 * $sum_results['LEAKED']) / $x_total; - $x_passed = (100.0 * $sum_results['PASSED']) / $x_total; - } else { - $x_warned = $x_failed = $x_passed = $x_leaked = $x_xfailed = 0; - } - - $summary = ''; - - if ($show_html) { - $summary .= "
\n";
-	}
-
-	if ($show_ext_summary) {
-		$summary .= '
-=====================================================================
-TEST RESULT SUMMARY
----------------------------------------------------------------------
-Exts skipped    : ' . sprintf('%4d', $exts_skipped) . '
-Exts tested     : ' . sprintf('%4d', $exts_tested) . '
----------------------------------------------------------------------
-';
-	}
-
-	$summary .= '
-Number of tests : ' . sprintf('%4d', $n_total) . '          ' . sprintf('%8d', $x_total);
-
-	if ($sum_results['BORKED']) {
-		$summary .= '
-Tests borked    : ' . sprintf('%4d (%5.1f%%)', $sum_results['BORKED'], $percent_results['BORKED']) . ' --------';
-	}
-
-	$summary .= '
-Tests skipped   : ' . sprintf('%4d (%5.1f%%)', $sum_results['SKIPPED'], $percent_results['SKIPPED']) . ' --------
-Tests warned    : ' . sprintf('%4d (%5.1f%%)', $sum_results['WARNED'], $percent_results['WARNED']) . ' ' . sprintf('(%5.1f%%)', $x_warned) . '
-Tests failed    : ' . sprintf('%4d (%5.1f%%)', $sum_results['FAILED'], $percent_results['FAILED']) . ' ' . sprintf('(%5.1f%%)', $x_failed) . '
-Expected fail   : ' . sprintf('%4d (%5.1f%%)', $sum_results['XFAILED'], $percent_results['XFAILED']) . ' ' . sprintf('(%5.1f%%)', $x_xfailed);
-
-	if ($leak_check) {
-		$summary .= '
-Tests leaked    : ' . sprintf('%4d (%5.1f%%)', $sum_results['LEAKED'], $percent_results['LEAKED']) . ' ' . sprintf('(%5.1f%%)', $x_leaked);
-	}
-
-	$summary .= '
-Tests passed    : ' . sprintf('%4d (%5.1f%%)', $sum_results['PASSED'], $percent_results['PASSED']) . ' ' . sprintf('(%5.1f%%)', $x_passed) . '
----------------------------------------------------------------------
-Time taken      : ' . sprintf('%4d seconds', $end_time - $start_time) . '
-=====================================================================
-';
-	$failed_test_summary = '';
-
-	if (count($PHP_FAILED_TESTS['XFAILED'])) {
-		$failed_test_summary .= '
-=====================================================================
-EXPECTED FAILED TEST SUMMARY
----------------------------------------------------------------------
-';
-		foreach ($PHP_FAILED_TESTS['XFAILED'] as $failed_test_data) {
-			$failed_test_summary .= $failed_test_data['test_name'] . $failed_test_data['info'] . "\n";
-		}
-		$failed_test_summary .=  "=====================================================================\n";
-	}
-
-	if (count($PHP_FAILED_TESTS['BORKED'])) {
-		$failed_test_summary .= '
-=====================================================================
-BORKED TEST SUMMARY
----------------------------------------------------------------------
-';
-		foreach ($PHP_FAILED_TESTS['BORKED'] as $failed_test_data) {
-			$failed_test_summary .= $failed_test_data['info'] . "\n";
-		}
-
-		$failed_test_summary .=  "=====================================================================\n";
-	}
-
-	if (count($PHP_FAILED_TESTS['FAILED'])) {
-		$failed_test_summary .= '
-=====================================================================
-FAILED TEST SUMMARY
----------------------------------------------------------------------
-';
-		foreach ($PHP_FAILED_TESTS['FAILED'] as $failed_test_data) {
-			$failed_test_summary .= $failed_test_data['test_name'] . $failed_test_data['info'] . "\n";
-		}
-		$failed_test_summary .=  "=====================================================================\n";
-	}
-	if (count($PHP_FAILED_TESTS['WARNED'])) {
-		$failed_test_summary .= '
-=====================================================================
-WARNED TEST SUMMARY
----------------------------------------------------------------------
-';
-		foreach ($PHP_FAILED_TESTS['WARNED'] as $failed_test_data) {
-			$failed_test_summary .= $failed_test_data['test_name'] . $failed_test_data['info'] . "\n";
-		}
-
-		$failed_test_summary .=  "=====================================================================\n";
-	}
-
-	if (count($PHP_FAILED_TESTS['LEAKED'])) {
-		$failed_test_summary .= '
-=====================================================================
-LEAKED TEST SUMMARY
----------------------------------------------------------------------
-';
-		foreach ($PHP_FAILED_TESTS['LEAKED'] as $failed_test_data) {
-			$failed_test_summary .= $failed_test_data['test_name'] . $failed_test_data['info'] . "\n";
-		}
-
-		$failed_test_summary .=  "=====================================================================\n";
-	}
-
-	if ($failed_test_summary && !getenv('NO_PHPTEST_SUMMARY')) {
-		$summary .= $failed_test_summary;
-	}
-
-	if ($show_html) {
-		$summary .= "
"; - } - - return $summary; -} - -function show_start($start_time) -{ - global $html_output, $html_file; - - if ($html_output) { - fwrite($html_file, "

Time Start: " . date('Y-m-d H:i:s', $start_time) . "

\n"); - fwrite($html_file, "\n"); - } - - echo "TIME START " . date('Y-m-d H:i:s', $start_time) . "\n=====================================================================\n"; -} - -function show_end($end_time) -{ - global $html_output, $html_file; - - if ($html_output) { - fwrite($html_file, "
\n"); - fwrite($html_file, "

Time End: " . date('Y-m-d H:i:s', $end_time) . "

\n"); - } - - echo "=====================================================================\nTIME END " . date('Y-m-d H:i:s', $end_time) . "\n"; -} - -function show_summary() -{ - global $html_output, $html_file; - - if ($html_output) { - fwrite($html_file, "
\n" . get_summary(true, true)); - } - - echo get_summary(true, false); -} - -function show_redirect_start($tests, $tested, $tested_file) -{ - global $html_output, $html_file, $line_length, $SHOW_ONLY_GROUPS; - - if ($html_output) { - fwrite($html_file, "---> $tests ($tested [$tested_file]) begin\n"); - } - - if (!$SHOW_ONLY_GROUPS || in_array('REDIRECT', $SHOW_ONLY_GROUPS)) { - echo "REDIRECT $tests ($tested [$tested_file]) begin\n"; - } else { - // Write over the last line to avoid random trailing chars on next echo - echo str_repeat(" ", $line_length), "\r"; - } -} - -function show_redirect_ends($tests, $tested, $tested_file) -{ - global $html_output, $html_file, $line_length, $SHOW_ONLY_GROUPS; - - if ($html_output) { - fwrite($html_file, "---> $tests ($tested [$tested_file]) done\n"); - } - - if (!$SHOW_ONLY_GROUPS || in_array('REDIRECT', $SHOW_ONLY_GROUPS)) { - echo "REDIRECT $tests ($tested [$tested_file]) done\n"; - } else { - // Write over the last line to avoid random trailing chars on next echo - echo str_repeat(" ", $line_length), "\r"; - } -} - -function show_test($test_idx, $shortname) -{ - global $test_cnt; - global $line_length; - - $str = "TEST $test_idx/$test_cnt [$shortname]\r"; - $line_length = strlen($str); - echo $str; - flush(); -} - -function show_result($result, $tested, $tested_file, $extra = '', $temp_filenames = null) -{ - global $html_output, $html_file, $temp_target, $temp_urlbase, $line_length, $SHOW_ONLY_GROUPS; - - if (!$SHOW_ONLY_GROUPS || in_array($result, $SHOW_ONLY_GROUPS)) { - echo "$result $tested [$tested_file] $extra\n"; - } else if (!$SHOW_ONLY_GROUPS) { - // Write over the last line to avoid random trailing chars on next echo - echo str_repeat(" ", $line_length), "\r"; - } - - if ($html_output) { - - if (isset($temp_filenames['file']) && file_exists($temp_filenames['file'])) { - $url = str_replace($temp_target, $temp_urlbase, $temp_filenames['file']); - $tested = "$tested"; - } - - if (isset($temp_filenames['skip']) && file_exists($temp_filenames['skip'])) { - - if (empty($extra)) { - $extra = "skipif"; - } - - $url = str_replace($temp_target, $temp_urlbase, $temp_filenames['skip']); - $extra = "$extra"; - - } else if (empty($extra)) { - $extra = " "; - } - - if (isset($temp_filenames['diff']) && file_exists($temp_filenames['diff'])) { - $url = str_replace($temp_target, $temp_urlbase, $temp_filenames['diff']); - $diff = "diff"; - } else { - $diff = " "; - } - - if (isset($temp_filenames['mem']) && file_exists($temp_filenames['mem'])) { - $url = str_replace($temp_target, $temp_urlbase, $temp_filenames['mem']); - $mem = "leaks"; - } else { - $mem = " "; - } - - fwrite($html_file, - "" . - "$result" . - "$tested" . - "$extra" . - "$diff" . - "$mem" . - "\n"); - } -} - -function junit_init() { - // Check whether a junit log is wanted. - $JUNIT = getenv('TEST_PHP_JUNIT'); - if (empty($JUNIT)) { - $JUNIT = FALSE; - } elseif (!$fp = fopen($JUNIT, 'w')) { - error("Failed to open $JUNIT for writing."); - } else { - $JUNIT = array( - 'fp' => $fp, - 'name' => 'php-src', - 'test_total' => 0, - 'test_pass' => 0, - 'test_fail' => 0, - 'test_error' => 0, - 'test_skip' => 0, - 'test_warn' => 0, - 'execution_time'=> 0, - 'suites' => array(), - 'files' => array() - ); - } - - $GLOBALS['JUNIT'] = $JUNIT; -} - -function junit_save_xml() { - global $JUNIT; - if (!junit_enabled()) return; - - $xml = ''. PHP_EOL . - '' . PHP_EOL; - $xml .= junit_get_suite_xml(); - $xml .= ''; - fwrite($JUNIT['fp'], $xml); -} - -function junit_get_suite_xml($suite_name = '') { - global $JUNIT; - - $suite = $suite_name ? $JUNIT['suites'][$suite_name] : $JUNIT; - - $result = sprintf( - '' . PHP_EOL, - $suite['name'], $suite['test_total'], $suite['test_fail'], $suite['test_error'], $suite['test_skip'], - $suite['execution_time'] - ); - - foreach($suite['suites'] as $sub_suite) { - $result .= junit_get_suite_xml($sub_suite['name']); - } - - // Output files only in subsuites - if (!empty($suite_name)) { - foreach($suite['files'] as $file) { - $result .= $JUNIT['files'][$file]['xml']; - } - } - - $result .= '' . PHP_EOL; - - return $result; -} - -function junit_enabled() { - global $JUNIT; - return !empty($JUNIT); -} - -/** - * @param array|string $type - * @param string $file_name - * @param string $test_name - * @param int|string $time - * @param string $message - * @param string $details - * @return void - */ -function junit_mark_test_as($type, $file_name, $test_name, $time = null, $message = '', $details = '') { - global $JUNIT; - if (!junit_enabled()) return; - - $suite = junit_get_suitename_for($file_name); - - junit_suite_record($suite, 'test_total'); - - $time = null !== $time ? $time : junit_get_timer($file_name); - junit_suite_record($suite, 'execution_time', $time); - - $escaped_details = htmlspecialchars($details, ENT_QUOTES, 'UTF-8'); - $escaped_details = preg_replace_callback('/[\0-\x08\x0B\x0C\x0E-\x1F]/', function ($c) { - return sprintf('[[0x%02x]]', ord($c[0])); - }, $escaped_details); - $escaped_message = htmlspecialchars($message, ENT_QUOTES, 'UTF-8'); - - $escaped_test_name = basename($file_name) . ' - ' . htmlspecialchars($test_name, ENT_QUOTES); - $JUNIT['files'][$file_name]['xml'] = "\n"; - - if (is_array($type)) { - $output_type = $type[0] . 'ED'; - $temp = array_intersect(array('XFAIL', 'FAIL', 'WARN'), $type); - $type = reset($temp); - } else { - $output_type = $type . 'ED'; - } - - if ('PASS' == $type || 'XFAIL' == $type) { - junit_suite_record($suite, 'test_pass'); - } elseif ('BORK' == $type) { - junit_suite_record($suite, 'test_error'); - $JUNIT['files'][$file_name]['xml'] .= "\n"; - } elseif ('SKIP' == $type) { - junit_suite_record($suite, 'test_skip'); - $JUNIT['files'][$file_name]['xml'] .= "$escaped_message\n"; - } elseif ('WARN' == $type) { - junit_suite_record($suite, 'test_warn'); - $JUNIT['files'][$file_name]['xml'] .= "$escaped_message\n"; - } elseif ('FAIL' == $type) { - junit_suite_record($suite, 'test_fail'); - $JUNIT['files'][$file_name]['xml'] .= "$escaped_details\n"; - } else { - junit_suite_record($suite, 'test_error'); - $JUNIT['files'][$file_name]['xml'] .= "$escaped_details\n"; - } - - $JUNIT['files'][$file_name]['xml'] .= "\n"; - -} - -function junit_suite_record($suite, $param, $value = 1) { - global $JUNIT; - - $JUNIT[$param] += $value; - $JUNIT['suites'][$suite][$param] += $value; -} - -function junit_get_timer($file_name) { - global $JUNIT; - if (!junit_enabled()) return 0; - - if (isset($JUNIT['files'][$file_name]['total'])) { - return number_format($JUNIT['files'][$file_name]['total'], 4); - } - - return 0; -} - -function junit_start_timer($file_name) { - global $JUNIT; - if (!junit_enabled()) return; - - if (!isset($JUNIT['files'][$file_name]['start'])) { - $JUNIT['files'][$file_name]['start'] = microtime(true); - - $suite = junit_get_suitename_for($file_name); - junit_init_suite($suite); - $JUNIT['suites'][$suite]['files'][$file_name] = $file_name; - } -} - -function junit_get_suitename_for($file_name) { - return junit_path_to_classname(dirname($file_name)); -} - -function junit_path_to_classname($file_name) { - global $JUNIT; - return $JUNIT['name'] . '.' . str_replace(DIRECTORY_SEPARATOR, '.', $file_name); -} - -function junit_init_suite($suite_name) { - global $JUNIT; - if (!junit_enabled()) return; - - if (!empty($JUNIT['suites'][$suite_name])) { - return; - } - - $JUNIT['suites'][$suite_name] = array( - 'name' => $suite_name, - 'test_total' => 0, - 'test_pass' => 0, - 'test_fail' => 0, - 'test_error' => 0, - 'test_skip' => 0, - 'suites' => array(), - 'files' => array(), - 'execution_time'=> 0, - ); -} - -function junit_finish_timer($file_name) { - global $JUNIT; - if (!junit_enabled()) return; - - if (!isset($JUNIT['files'][$file_name]['start'])) { - error("Timer for $file_name was not started!"); - } - - if (!isset($JUNIT['files'][$file_name]['total'])) { - $JUNIT['files'][$file_name]['total'] = 0; - } - - $start = $JUNIT['files'][$file_name]['start']; - $JUNIT['files'][$file_name]['total'] += microtime(true) - $start; - unset($JUNIT['files'][$file_name]['start']); -} - -/* - * Local variables: - * tab-width: 4 - * c-basic-offset: 4 - * End: - * vim: noet sw=4 ts=4 - */ -?> diff --git a/ext/opcache/tests/bug66338-Officials.inc b/ext/opcache/tests/bug66338-Officials.inc deleted file mode 100644 index 11240f7b889de..0000000000000 --- a/ext/opcache/tests/bug66338-Officials.inc +++ /dev/null @@ -1,3 +0,0 @@ - Date: Mon, 25 Jun 2018 20:04:38 -0400 Subject: [PATCH 23/40] Attempt to remove whitespace changes --- Zend/zend_compile.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h index 437a2817698fb..c1a2e56e37422 100644 --- a/Zend/zend_compile.h +++ b/Zend/zend_compile.h @@ -980,7 +980,7 @@ END_EXTERN_C() #define ZEND_INVOKE_FUNC_NAME "__invoke" #define ZEND_DEBUGINFO_FUNC_NAME "__debuginfo" #define ZEND_COMPARETO_FUNC_NAME "__compareto" -#define ZEND_EQUALS_FUNC_NAME "__equals" +#define ZEND_EQUALS_FUNC_NAME "__equals" /* The following constants may be combined in CG(compiler_options) * to change the default compiler behavior */ From 76eed35328782e9eedbf650bcd0368c092ac1c0c Mon Sep 17 00:00:00 2001 From: Rudi Theunissen Date: Mon, 25 Jun 2018 20:06:30 -0400 Subject: [PATCH 24/40] Revert "Revert unintended whitespace changes" This reverts commit bc3eda501393f51c85b697638cf0dda54dd0cc0d. --- Zend/zend_API.c | 2 +- Zend/zend_compile.c | 20 ++++++++++---------- Zend/zend_object_handlers.c | 4 ++-- Zend/zend_operators.c | 5 +++-- Zend/zend_vm_def.h | 8 ++++---- 5 files changed, 20 insertions(+), 19 deletions(-) diff --git a/Zend/zend_API.c b/Zend/zend_API.c index 81adce59c6cf3..c846533ef777c 100644 --- a/Zend/zend_API.c +++ b/Zend/zend_API.c @@ -2302,7 +2302,7 @@ ZEND_API int zend_register_functions(zend_class_entry *scope, const zend_functio } if (reg_function->common.arg_info && - (reg_function->common.fn_flags & (ZEND_ACC_HAS_RETURN_TYPE|ZEND_ACC_HAS_TYPE_HINTS))) { + (reg_function->common.fn_flags & (ZEND_ACC_HAS_RETURN_TYPE|ZEND_ACC_HAS_TYPE_HINTS))) { /* convert "const char*" class type names into "zend_string*" */ uint32_t i; uint32_t num_args = reg_function->common.num_args + 1; diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index d42404b19f5c5..9ea0a64c8c80d 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -1239,7 +1239,7 @@ static void zend_mark_function_as_generator() /* {{{ */ if (ZEND_TYPE_CODE(return_info.type) != IS_ITERABLE) { const char *msg = "Generators may only declare a return type of Generator, Iterator, Traversable, or iterable, %s is not permitted"; - + if (!ZEND_TYPE_IS_CLASS(return_info.type)) { zend_error_noreturn(E_COMPILE_ERROR, msg, zend_get_type_by_const(ZEND_TYPE_CODE(return_info.type))); } @@ -2157,7 +2157,7 @@ static void zend_emit_tick(void) /* {{{ */ if (CG(active_op_array)->last && CG(active_op_array)->opcodes[CG(active_op_array)->last - 1].opcode == ZEND_TICKS) { return; } - + opline = get_next_op(CG(active_op_array)); opline->opcode = ZEND_TICKS; @@ -2608,7 +2608,7 @@ static zend_op *zend_compile_simple_var_no_cv(znode *result, zend_ast *ast, uint opline = zend_emit_op(result, ZEND_FETCH_R, &name_node, NULL); } - if (name_node.op_type == IS_CONST && + if (name_node.op_type == IS_CONST && zend_is_auto_global(Z_STR(name_node.u.constant))) { opline->extended_value = ZEND_FETCH_GLOBAL; @@ -5064,7 +5064,7 @@ void zend_compile_switch(zend_ast *ast) /* {{{ */ if (jumptable) { zval *cond_zv = zend_ast_get_zval(cond_ast); zval jmp_target; - ZVAL_LONG(&jmp_target, get_next_op_number(CG(active_op_array))); + ZVAL_LONG(&jmp_target, get_next_op_number(CG(active_op_array))); ZEND_ASSERT(Z_TYPE_P(cond_zv) == jumptable_type); if (Z_TYPE_P(cond_zv) == IS_LONG) { @@ -5240,7 +5240,7 @@ void zend_compile_try(zend_ast *ast) /* {{{ */ if (finally_ast) { zend_loop_var discard_exception; uint32_t opnum_jmp = get_next_op_number(CG(active_op_array)) + 1; - + /* Pop FAST_CALL from unwind stack */ zend_stack_del_top(&CG(loop_var_stack)); @@ -5494,7 +5494,7 @@ void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast) /* {{{ */ uint32_t i; zend_op_array *op_array = CG(active_op_array); zend_arg_info *arg_infos; - + if (return_type_ast) { zend_bool allow_null = 0; @@ -5638,7 +5638,7 @@ void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast) /* {{{ */ "with a float type can only be float, integer, or NULL"); } break; - + case IS_ITERABLE: if (Z_TYPE(default_node.u.constant) != IS_ARRAY) { zend_error_noreturn(E_COMPILE_ERROR, "Default value for parameters " @@ -5650,7 +5650,7 @@ void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast) /* {{{ */ zend_error_noreturn(E_COMPILE_ERROR, "Default value for parameters " "with an object type can only be NULL"); break; - + default: if (!ZEND_SAME_FAKE_TYPE(ZEND_TYPE_CODE(arg_info->type), Z_TYPE(default_node.u.constant))) { zend_error_noreturn(E_COMPILE_ERROR, "Default value for parameters " @@ -5679,7 +5679,7 @@ void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast) /* {{{ */ if (opline->opcode != ZEND_RECV_INIT) { opline->op2.num = -1; } - } + } } /* These are assigned at the end to avoid uninitialized memory in case of an error */ @@ -7963,7 +7963,7 @@ static void zend_compile_encaps_list(znode *result, zend_ast *ast) /* {{{ */ i = ((j * sizeof(zend_string*)) + (sizeof(zval) - 1)) / sizeof(zval); while (i > 1) { get_temporary_variable(CG(active_op_array)); - i--; + i--; } zend_end_live_range(CG(active_op_array), range, opline - CG(active_op_array)->opcodes, diff --git a/Zend/zend_object_handlers.c b/Zend/zend_object_handlers.c index f4a5f019e0fee..faf3a4f384ff9 100644 --- a/Zend/zend_object_handlers.c +++ b/Zend/zend_object_handlers.c @@ -77,7 +77,7 @@ ZEND_API void rebuild_object_properties(zend_object *zobj) /* {{{ */ HT_FLAGS(zobj->properties) |= HASH_FLAG_HAS_EMPTY_IND; } - _zend_hash_append_ind(zobj->properties, prop_info->name, + _zend_hash_append_ind(zobj->properties, prop_info->name, OBJ_PROP(zobj, prop_info->offset)); } } ZEND_HASH_FOREACH_END(); @@ -684,7 +684,7 @@ ZEND_API zval *zend_std_read_property(zval *object, zval *member, int type, void *guard |= IN_ISSET; zend_std_call_issetter(&tmp_object, member, &tmp_result); *guard &= ~IN_ISSET; - + if (!zend_is_true(&tmp_result)) { retval = &EG(uninitialized_zval); zval_ptr_dtor(&tmp_object); diff --git a/Zend/zend_operators.c b/Zend/zend_operators.c index 66a612043e5d6..f3181a101b136 100644 --- a/Zend/zend_operators.c +++ b/Zend/zend_operators.c @@ -1959,6 +1959,7 @@ static void ZEND_FASTCALL convert_compare_result_to_long(zval *result) /* {{{ */ ZEND_API int ZEND_FASTCALL compare_function(zval *result, zval *op1, zval *op2) /* {{{ */ { + int ret; int converted = 0; zval op1_copy, op2_copy; @@ -3143,7 +3144,7 @@ ZEND_API zend_uchar ZEND_FASTCALL _is_numeric_string_ex(const char *str, size_t } /* }}} */ -/* +/* * String matching - Sunday algorithm * http://www.iti.fh-flensburg.de/lang/algorithmen/pattern/sundayen.htm */ @@ -3227,7 +3228,7 @@ ZEND_API const char* ZEND_FASTCALL zend_memnrstr_ex(const char *haystack, const if (i == needle_len) { return (const char *)p; } - + if (UNEXPECTED(p == haystack)) { return NULL; } diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index 984cc253adbc8..b4460a023401a 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -987,7 +987,7 @@ ZEND_VM_INLINE_HELPER(zend_binary_assign_op_helper, VAR|UNUSED|THIS|CV, CONST|TM ZEND_VM_DISPATCH_TO_HELPER(zend_binary_assign_op_dim_helper, binary_op, binary_op); } # endif - + ZEND_VM_DISPATCH_TO_HELPER(zend_binary_assign_op_obj_helper, binary_op, binary_op); #endif } @@ -2501,7 +2501,7 @@ ZEND_VM_HOT_NOCONST_HANDLER(43, ZEND_JMPZ, CONST|TMPVAR|CV, JMP_ADDR) zval *val; val = GET_OP1_ZVAL_PTR_UNDEF(BP_VAR_R); - + if (Z_TYPE_INFO_P(val) == IS_TRUE) { ZEND_VM_NEXT_OPCODE(); } else if (EXPECTED(Z_TYPE_INFO_P(val) <= IS_TRUE)) { @@ -3231,7 +3231,7 @@ ZEND_VM_HANDLER(113, ZEND_INIT_STATIC_METHOD_CALL, UNUSED|CLASS_FETCH|CONST|VAR, if (OP1_TYPE == IS_UNUSED) { /* previous opcode is ZEND_FETCH_CLASS */ - if ((opline->op1.num & ZEND_FETCH_CLASS_MASK) == ZEND_FETCH_CLASS_PARENT || + if ((opline->op1.num & ZEND_FETCH_CLASS_MASK) == ZEND_FETCH_CLASS_PARENT || (opline->op1.num & ZEND_FETCH_CLASS_MASK) == ZEND_FETCH_CLASS_SELF) { if (Z_TYPE(EX(This)) == IS_OBJECT) { ce = Z_OBJCE(EX(This)); @@ -3684,7 +3684,7 @@ ZEND_VM_HOT_HANDLER(60, ZEND_DO_FCALL, ANY, ANY, SPEC(RETVAL)) } else { zend_execute_internal(call, ret); } - + #if ZEND_DEBUG if (!EG(exception) && call->func) { ZEND_ASSERT(!(call->func->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE) || From d7d880d61dfdf8d4bd07eb74632309c725f9eeac Mon Sep 17 00:00:00 2001 From: Rudi Theunissen Date: Tue, 26 Jun 2018 08:53:14 -0400 Subject: [PATCH 25/40] Allow equality to automatically determine equal ordering --- .../comparisons/compare-rhs-is-used.phpt | 28 +++++++++++++++ .../equals-only-auto-ordering.phpt | 17 +++++++++ Zend/zend_operators.c | 36 +++++++++++++++++++ 3 files changed, 81 insertions(+) create mode 100644 Zend/tests/comparisons/compare-rhs-is-used.phpt create mode 100644 Zend/tests/comparisons/equals-only-auto-ordering.phpt diff --git a/Zend/tests/comparisons/compare-rhs-is-used.phpt b/Zend/tests/comparisons/compare-rhs-is-used.phpt new file mode 100644 index 0000000000000..a487638829899 --- /dev/null +++ b/Zend/tests/comparisons/compare-rhs-is-used.phpt @@ -0,0 +1,28 @@ +--TEST-- +__compareTo: RHS operator should be considered even if LHS doesn't implement +--FILE-- + new B); +var_dump(new B <=> new A); + +?> +--EXPECTF-- +Comparing! +int(-1) +Comparing! +int(1) diff --git a/Zend/tests/comparisons/equals-only-auto-ordering.phpt b/Zend/tests/comparisons/equals-only-auto-ordering.phpt new file mode 100644 index 0000000000000..12f43910a544a --- /dev/null +++ b/Zend/tests/comparisons/equals-only-auto-ordering.phpt @@ -0,0 +1,17 @@ +--TEST-- +__equals: Equal values automatically have equal ordering +--FILE-- +unique = 10; + +var_dump($a <=> $b); + +?> +--EXPECTF-- +int(0) diff --git a/Zend/zend_operators.c b/Zend/zend_operators.c index f3181a101b136..50d695bf0df08 100644 --- a/Zend/zend_operators.c +++ b/Zend/zend_operators.c @@ -2031,6 +2031,15 @@ ZEND_API int ZEND_FASTCALL compare_function(zval *result, zval *op1, zval *op2) if (Z_OBJ_HANDLER_P(op1, compare)(result, op1, op2) == SUCCESS) { convert_compare_result_to_long(result); return SUCCESS; + } else { + if (Z_OBJ_HANDLER_P(op1, equals)) { + if (Z_OBJ_HANDLER_P(op1, equals)(result, op1, op2) == SUCCESS) { + if (i_zend_is_true(result)) { + ZVAL_LONG(result, 0); + return SUCCESS; + } + } + } } } @@ -2043,6 +2052,15 @@ ZEND_API int ZEND_FASTCALL compare_function(zval *result, zval *op1, zval *op2) convert_compare_result_to_long(result); Z_LVAL_P(result) *= -1; return SUCCESS; + } else { + if (Z_OBJ_HANDLER_P(op2, equals)) { + if (Z_OBJ_HANDLER_P(op2, equals)(result, op2, op1) == SUCCESS) { + if (i_zend_is_true(result)) { + ZVAL_LONG(result, 0); + return SUCCESS; + } + } + } } } @@ -2062,6 +2080,15 @@ ZEND_API int ZEND_FASTCALL compare_function(zval *result, zval *op1, zval *op2) if (Z_OBJ_HANDLER_P(op1, compare) && Z_OBJ_HANDLER_P(op1, compare)(result, op1, op2) == SUCCESS) { convert_compare_result_to_long(result); return SUCCESS; + } else { + if (Z_OBJ_HANDLER_P(op1, equals)) { + if (Z_OBJ_HANDLER_P(op1, equals)(result, op1, op2) == SUCCESS) { + if (i_zend_is_true(result)) { + ZVAL_LONG(result, 0); + return SUCCESS; + } + } + } } } @@ -2070,6 +2097,15 @@ ZEND_API int ZEND_FASTCALL compare_function(zval *result, zval *op1, zval *op2) convert_compare_result_to_long(result); Z_LVAL_P(result) *= -1; return SUCCESS; + } else { + if (Z_OBJ_HANDLER_P(op2, equals)) { + if (Z_OBJ_HANDLER_P(op2, equals)(result, op2, op1) == SUCCESS) { + if (i_zend_is_true(result)) { + ZVAL_LONG(result, 0); + return SUCCESS; + } + } + } } } From 5f9eeb97d4a2a49f21ccc7748143f0420de070e7 Mon Sep 17 00:00:00 2001 From: Rudi Theunissen Date: Wed, 27 Jun 2018 17:35:10 -0400 Subject: [PATCH 26/40] Update for changes to the RFC --- .../compare-not-called-for-equality.phpt | 49 ++++++++++++++++ .../comparisons/equals-before-compare.phpt | 26 --------- .../equals-not-called-for-ordering.phpt | 19 +++++++ .../equals-only-auto-ordering.phpt | 17 ------ Zend/zend_operators.c | 56 +++++++------------ Zend/zend_operators.h | 2 + Zend/zend_vm_def.h | 6 +- Zend/zend_vm_execute.h | 1 - 8 files changed, 92 insertions(+), 84 deletions(-) create mode 100644 Zend/tests/comparisons/compare-not-called-for-equality.phpt delete mode 100644 Zend/tests/comparisons/equals-before-compare.phpt create mode 100644 Zend/tests/comparisons/equals-not-called-for-ordering.phpt delete mode 100644 Zend/tests/comparisons/equals-only-auto-ordering.phpt diff --git a/Zend/tests/comparisons/compare-not-called-for-equality.phpt b/Zend/tests/comparisons/compare-not-called-for-equality.phpt new file mode 100644 index 0000000000000..ba3cdbc2536c3 --- /dev/null +++ b/Zend/tests/comparisons/compare-not-called-for-equality.phpt @@ -0,0 +1,49 @@ +--TEST-- +__compareTo: __compareTo should be called when comparing for equality +--FILE-- + +--EXPECT-- +A::__compareTo +A::__compareTo +B::__equals +B::__equals +B::__equals +B::__equals +B::__equals +B::__equals diff --git a/Zend/tests/comparisons/equals-before-compare.phpt b/Zend/tests/comparisons/equals-before-compare.phpt deleted file mode 100644 index 62d4f9118fbe3..0000000000000 --- a/Zend/tests/comparisons/equals-before-compare.phpt +++ /dev/null @@ -1,26 +0,0 @@ ---TEST-- -__equals: Should not be called for comparisons. ---FILE-- - 1); -var_dump($a <=> $a); - -?> ---EXPECTF-- -int(1) -int(1) diff --git a/Zend/tests/comparisons/equals-not-called-for-ordering.phpt b/Zend/tests/comparisons/equals-not-called-for-ordering.phpt new file mode 100644 index 0000000000000..77afe32c9a8de --- /dev/null +++ b/Zend/tests/comparisons/equals-not-called-for-ordering.phpt @@ -0,0 +1,19 @@ +--TEST-- +__equals: __equals should not be called when comparing for ordering +--FILE-- + new Equatable; +new Equatable >= new Equatable; +new Equatable <= new Equatable; +new Equatable <=> new Equatable; +?> +--EXPECT-- diff --git a/Zend/tests/comparisons/equals-only-auto-ordering.phpt b/Zend/tests/comparisons/equals-only-auto-ordering.phpt deleted file mode 100644 index 12f43910a544a..0000000000000 --- a/Zend/tests/comparisons/equals-only-auto-ordering.phpt +++ /dev/null @@ -1,17 +0,0 @@ ---TEST-- -__equals: Equal values automatically have equal ordering ---FILE-- -unique = 10; - -var_dump($a <=> $b); - -?> ---EXPECTF-- -int(0) diff --git a/Zend/zend_operators.c b/Zend/zend_operators.c index 50d695bf0df08..35a46199f5034 100644 --- a/Zend/zend_operators.c +++ b/Zend/zend_operators.c @@ -2031,15 +2031,6 @@ ZEND_API int ZEND_FASTCALL compare_function(zval *result, zval *op1, zval *op2) if (Z_OBJ_HANDLER_P(op1, compare)(result, op1, op2) == SUCCESS) { convert_compare_result_to_long(result); return SUCCESS; - } else { - if (Z_OBJ_HANDLER_P(op1, equals)) { - if (Z_OBJ_HANDLER_P(op1, equals)(result, op1, op2) == SUCCESS) { - if (i_zend_is_true(result)) { - ZVAL_LONG(result, 0); - return SUCCESS; - } - } - } } } @@ -2052,15 +2043,6 @@ ZEND_API int ZEND_FASTCALL compare_function(zval *result, zval *op1, zval *op2) convert_compare_result_to_long(result); Z_LVAL_P(result) *= -1; return SUCCESS; - } else { - if (Z_OBJ_HANDLER_P(op2, equals)) { - if (Z_OBJ_HANDLER_P(op2, equals)(result, op2, op1) == SUCCESS) { - if (i_zend_is_true(result)) { - ZVAL_LONG(result, 0); - return SUCCESS; - } - } - } } } @@ -2080,15 +2062,6 @@ ZEND_API int ZEND_FASTCALL compare_function(zval *result, zval *op1, zval *op2) if (Z_OBJ_HANDLER_P(op1, compare) && Z_OBJ_HANDLER_P(op1, compare)(result, op1, op2) == SUCCESS) { convert_compare_result_to_long(result); return SUCCESS; - } else { - if (Z_OBJ_HANDLER_P(op1, equals)) { - if (Z_OBJ_HANDLER_P(op1, equals)(result, op1, op2) == SUCCESS) { - if (i_zend_is_true(result)) { - ZVAL_LONG(result, 0); - return SUCCESS; - } - } - } } } @@ -2097,15 +2070,6 @@ ZEND_API int ZEND_FASTCALL compare_function(zval *result, zval *op1, zval *op2) convert_compare_result_to_long(result); Z_LVAL_P(result) *= -1; return SUCCESS; - } else { - if (Z_OBJ_HANDLER_P(op2, equals)) { - if (Z_OBJ_HANDLER_P(op2, equals)(result, op2, op1) == SUCCESS) { - if (i_zend_is_true(result)) { - ZVAL_LONG(result, 0); - return SUCCESS; - } - } - } } } @@ -2327,6 +2291,26 @@ ZEND_API int ZEND_FASTCALL is_smaller_or_equal_function(zval *result, zval *op1, } /* }}} */ +ZEND_API int ZEND_FASTCALL is_greater_function(zval *result, zval *op1, zval *op2) /* {{{ */ +{ + if (compare_function(result, op1, op2) == FAILURE) { + return FAILURE; + } + ZVAL_BOOL(result, (Z_LVAL_P(result) > 0)); + return SUCCESS; +} +/* }}} */ + +ZEND_API int ZEND_FASTCALL is_greater_or_equal_function(zval *result, zval *op1, zval *op2) /* {{{ */ +{ + if (compare_function(result, op1, op2) == FAILURE) { + return FAILURE; + } + ZVAL_BOOL(result, (Z_LVAL_P(result) >= 0)); + return SUCCESS; +} +/* }}} */ + static zend_bool ZEND_FASTCALL instanceof_interface_only(const zend_class_entry *instance_ce, const zend_class_entry *ce) /* {{{ */ { uint32_t i; diff --git a/Zend/zend_operators.h b/Zend/zend_operators.h index fd1eb21a52796..23e468fa8f7af 100644 --- a/Zend/zend_operators.h +++ b/Zend/zend_operators.h @@ -67,6 +67,8 @@ ZEND_API int ZEND_FASTCALL is_not_identical_function(zval *result, zval *op1, zv ZEND_API int ZEND_FASTCALL is_not_equal_function(zval *result, zval *op1, zval *op2); ZEND_API int ZEND_FASTCALL is_smaller_function(zval *result, zval *op1, zval *op2); ZEND_API int ZEND_FASTCALL is_smaller_or_equal_function(zval *result, zval *op1, zval *op2); +ZEND_API int ZEND_FASTCALL is_greater_function(zval *result, zval *op1, zval *op2); +ZEND_API int ZEND_FASTCALL is_greater_or_equal_function(zval *result, zval *op1, zval *op2); ZEND_API zend_bool ZEND_FASTCALL instanceof_function_ex(const zend_class_entry *instance_ce, const zend_class_entry *ce, zend_bool interfaces_only); ZEND_API zend_bool ZEND_FASTCALL instanceof_function(const zend_class_entry *instance_ce, const zend_class_entry *ce); diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index b4460a023401a..69ea576335538 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -544,8 +544,7 @@ ZEND_VM_COLD_CONSTCONST_HANDLER(19, ZEND_IS_SMALLER, CONST|TMPVAR|CV, CONST|TMPV op2 = GET_OP2_UNDEF_CV(op2, BP_VAR_R); } result = EX_VAR(opline->result.var); - compare_function(result, op1, op2); - ZVAL_BOOL(result, Z_LVAL_P(result) < 0); + is_smaller_function(result); FREE_OP1(); FREE_OP2(); ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); @@ -594,8 +593,7 @@ ZEND_VM_COLD_CONSTCONST_HANDLER(20, ZEND_IS_SMALLER_OR_EQUAL, CONST|TMPVAR|CV, C op2 = GET_OP2_UNDEF_CV(op2, BP_VAR_R); } result = EX_VAR(opline->result.var); - compare_function(result, op1, op2); - ZVAL_BOOL(result, Z_LVAL_P(result) <= 0); + is_smaller_or_equal_function(result); FREE_OP1(); FREE_OP2(); ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 5e9b630da4bca..b0832c46bffdf 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -47689,7 +47689,6 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_EQUAL_SPEC_CV_CV_HANDLER(ZE result = EX_VAR(opline->result.var); is_equal_function(result, op1, op2); - ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); } From ab980084bebc2c88e3d65ae7014b489ff3c81334 Mon Sep 17 00:00:00 2001 From: Rudi Theunissen Date: Wed, 27 Jun 2018 17:37:21 -0400 Subject: [PATCH 27/40] Updated Zend/zend_vm_execute.h with vm_gen --- Zend/zend_vm_execute.h | 55 +++++++++++++++--------------------------- 1 file changed, 19 insertions(+), 36 deletions(-) diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index b0832c46bffdf..39e3a86cbfb05 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -4379,8 +4379,7 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_SMALLER_SPEC_C op2 = GET_OP2_UNDEF_CV(op2, BP_VAR_R); } result = EX_VAR(opline->result.var); - compare_function(result, op1, op2); - ZVAL_BOOL(result, Z_LVAL_P(result) < 0); + is_smaller_function(result); ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); @@ -4429,8 +4428,7 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_SMALLER_OR_EQU op2 = GET_OP2_UNDEF_CV(op2, BP_VAR_R); } result = EX_VAR(opline->result.var); - compare_function(result, op1, op2); - ZVAL_BOOL(result, Z_LVAL_P(result) <= 0); + is_smaller_or_equal_function(result); ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); @@ -6701,8 +6699,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_SMALLER_SPEC_CONST_TMPVAR_H op2 = GET_OP2_UNDEF_CV(op2, BP_VAR_R); } result = EX_VAR(opline->result.var); - compare_function(result, op1, op2); - ZVAL_BOOL(result, Z_LVAL_P(result) < 0); + is_smaller_function(result); zval_ptr_dtor_nogc(free_op2); ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); @@ -6751,8 +6748,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_SMALLER_OR_EQUAL_SPEC_CONST op2 = GET_OP2_UNDEF_CV(op2, BP_VAR_R); } result = EX_VAR(opline->result.var); - compare_function(result, op1, op2); - ZVAL_BOOL(result, Z_LVAL_P(result) <= 0); + is_smaller_or_equal_function(result); zval_ptr_dtor_nogc(free_op2); ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); @@ -9794,8 +9790,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_SMALLER_SPEC_CONST_CV_HANDL op2 = GET_OP2_UNDEF_CV(op2, BP_VAR_R); } result = EX_VAR(opline->result.var); - compare_function(result, op1, op2); - ZVAL_BOOL(result, Z_LVAL_P(result) < 0); + is_smaller_function(result); ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); @@ -9844,8 +9839,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_SMALLER_OR_EQUAL_SPEC_CONST op2 = GET_OP2_UNDEF_CV(op2, BP_VAR_R); } result = EX_VAR(opline->result.var); - compare_function(result, op1, op2); - ZVAL_BOOL(result, Z_LVAL_P(result) <= 0); + is_smaller_or_equal_function(result); ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); @@ -13295,8 +13289,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_SMALLER_SPEC_TMPVAR_CONST_H op2 = GET_OP2_UNDEF_CV(op2, BP_VAR_R); } result = EX_VAR(opline->result.var); - compare_function(result, op1, op2); - ZVAL_BOOL(result, Z_LVAL_P(result) < 0); + is_smaller_function(result); zval_ptr_dtor_nogc(free_op1); ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); @@ -13345,8 +13338,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_SMALLER_OR_EQUAL_SPEC_TMPVA op2 = GET_OP2_UNDEF_CV(op2, BP_VAR_R); } result = EX_VAR(opline->result.var); - compare_function(result, op1, op2); - ZVAL_BOOL(result, Z_LVAL_P(result) <= 0); + is_smaller_or_equal_function(result); zval_ptr_dtor_nogc(free_op1); ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); @@ -15022,8 +15014,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_SMALLER_SPEC_TMPVAR_TMPVAR_ op2 = GET_OP2_UNDEF_CV(op2, BP_VAR_R); } result = EX_VAR(opline->result.var); - compare_function(result, op1, op2); - ZVAL_BOOL(result, Z_LVAL_P(result) < 0); + is_smaller_function(result); zval_ptr_dtor_nogc(free_op1); zval_ptr_dtor_nogc(free_op2); ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); @@ -15072,8 +15063,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_SMALLER_OR_EQUAL_SPEC_TMPVA op2 = GET_OP2_UNDEF_CV(op2, BP_VAR_R); } result = EX_VAR(opline->result.var); - compare_function(result, op1, op2); - ZVAL_BOOL(result, Z_LVAL_P(result) <= 0); + is_smaller_or_equal_function(result); zval_ptr_dtor_nogc(free_op1); zval_ptr_dtor_nogc(free_op2); ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); @@ -16992,8 +16982,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_SMALLER_SPEC_TMPVAR_CV_HAND op2 = GET_OP2_UNDEF_CV(op2, BP_VAR_R); } result = EX_VAR(opline->result.var); - compare_function(result, op1, op2); - ZVAL_BOOL(result, Z_LVAL_P(result) < 0); + is_smaller_function(result); zval_ptr_dtor_nogc(free_op1); ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); @@ -17042,8 +17031,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_SMALLER_OR_EQUAL_SPEC_TMPVA op2 = GET_OP2_UNDEF_CV(op2, BP_VAR_R); } result = EX_VAR(opline->result.var); - compare_function(result, op1, op2); - ZVAL_BOOL(result, Z_LVAL_P(result) <= 0); + is_smaller_or_equal_function(result); zval_ptr_dtor_nogc(free_op1); ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); @@ -38360,8 +38348,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_SMALLER_SPEC_CV_CONST_HANDL op2 = GET_OP2_UNDEF_CV(op2, BP_VAR_R); } result = EX_VAR(opline->result.var); - compare_function(result, op1, op2); - ZVAL_BOOL(result, Z_LVAL_P(result) < 0); + is_smaller_function(result); ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); @@ -38410,8 +38397,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_SMALLER_OR_EQUAL_SPEC_CV_CO op2 = GET_OP2_UNDEF_CV(op2, BP_VAR_R); } result = EX_VAR(opline->result.var); - compare_function(result, op1, op2); - ZVAL_BOOL(result, Z_LVAL_P(result) <= 0); + is_smaller_or_equal_function(result); ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); @@ -42236,8 +42222,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_SMALLER_SPEC_CV_TMPVAR_HAND op2 = GET_OP2_UNDEF_CV(op2, BP_VAR_R); } result = EX_VAR(opline->result.var); - compare_function(result, op1, op2); - ZVAL_BOOL(result, Z_LVAL_P(result) < 0); + is_smaller_function(result); zval_ptr_dtor_nogc(free_op2); ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); @@ -42286,8 +42271,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_SMALLER_OR_EQUAL_SPEC_CV_TM op2 = GET_OP2_UNDEF_CV(op2, BP_VAR_R); } result = EX_VAR(opline->result.var); - compare_function(result, op1, op2); - ZVAL_BOOL(result, Z_LVAL_P(result) <= 0); + is_smaller_or_equal_function(result); zval_ptr_dtor_nogc(free_op2); ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); @@ -47689,6 +47673,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_EQUAL_SPEC_CV_CV_HANDLER(ZE result = EX_VAR(opline->result.var); is_equal_function(result, op1, op2); + ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); } @@ -47792,8 +47777,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_SMALLER_SPEC_CV_CV_HANDLER( op2 = GET_OP2_UNDEF_CV(op2, BP_VAR_R); } result = EX_VAR(opline->result.var); - compare_function(result, op1, op2); - ZVAL_BOOL(result, Z_LVAL_P(result) < 0); + is_smaller_function(result); ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); @@ -47842,8 +47826,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_SMALLER_OR_EQUAL_SPEC_CV_CV op2 = GET_OP2_UNDEF_CV(op2, BP_VAR_R); } result = EX_VAR(opline->result.var); - compare_function(result, op1, op2); - ZVAL_BOOL(result, Z_LVAL_P(result) <= 0); + is_smaller_or_equal_function(result); ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); From c0789be6559cce8a0d0fec5ef9971ea5859ca2f0 Mon Sep 17 00:00:00 2001 From: Rudi Theunissen Date: Wed, 27 Jun 2018 19:25:33 -0400 Subject: [PATCH 28/40] Fix unnecessary EXPECTF and fill out some comment placeholders --- .../tests/comparisons/compare-array-sort.phpt | 2 +- Zend/tests/comparisons/compare-basic.phpt | 2 +- Zend/tests/comparisons/compare-in-array.phpt | 2 +- .../comparisons/compare-inheritance.phpt | 2 +- Zend/tests/comparisons/compare-normalize.phpt | 2 +- .../comparisons/compare-return-false.phpt | 2 +- .../comparisons/compare-return-float.phpt | 2 +- .../comparisons/compare-return-true.phpt | 2 +- .../comparisons/compare-rhs-is-used.phpt | 2 +- Zend/tests/comparisons/compare-to-null.phpt | 2 +- Zend/tests/comparisons/compare-to-scalar.phpt | 2 +- Zend/tests/comparisons/equals-basic.phpt | 2 +- .../comparisons/equals-disallow-compare.phpt | 40 +++++++++++++++---- Zend/tests/comparisons/equals-in-array.phpt | 2 +- .../comparisons/legacy-compare-objects.phpt | 2 +- .../comparisons/legacy-compare-to-null.phpt | 2 +- .../comparisons/legacy-strict-comparison.phpt | 2 +- Zend/tests/comparisons/setup.inc | 4 +- 18 files changed, 52 insertions(+), 24 deletions(-) diff --git a/Zend/tests/comparisons/compare-array-sort.phpt b/Zend/tests/comparisons/compare-array-sort.phpt index e17575f88f952..09956812fa173 100644 --- a/Zend/tests/comparisons/compare-array-sort.phpt +++ b/Zend/tests/comparisons/compare-array-sort.phpt @@ -17,7 +17,7 @@ var_dump($array[1]); var_dump($array[2]); ?> ---EXPECTF-- +--EXPECT-- object(Comparable)#2 (2) { ["decoy":protected]=> int(-1) diff --git a/Zend/tests/comparisons/compare-basic.phpt b/Zend/tests/comparisons/compare-basic.phpt index ba41a96ef6860..ac2db65762ab6 100644 --- a/Zend/tests/comparisons/compare-basic.phpt +++ b/Zend/tests/comparisons/compare-basic.phpt @@ -41,7 +41,7 @@ var_dump(new Comparable(1) <=> new Comparable(1)); // 0 var_dump(new Comparable(1) <=> new Comparable(2)); // -1 ?> ---EXPECTF-- +--EXPECT-- bool(false) bool(false) bool(true) diff --git a/Zend/tests/comparisons/compare-in-array.phpt b/Zend/tests/comparisons/compare-in-array.phpt index 68a60ac68334c..5810e1548ebd1 100644 --- a/Zend/tests/comparisons/compare-in-array.phpt +++ b/Zend/tests/comparisons/compare-in-array.phpt @@ -28,7 +28,7 @@ var_dump(in_array(new Comparable(4), $array)); // no var_dump(in_array(new Comparable(5), $array)); // no ?> ---EXPECTF-- +--EXPECT-- bool(true) bool(true) bool(true) diff --git a/Zend/tests/comparisons/compare-inheritance.phpt b/Zend/tests/comparisons/compare-inheritance.phpt index 12bf9d399dccf..0490ae6092a3d 100644 --- a/Zend/tests/comparisons/compare-inheritance.phpt +++ b/Zend/tests/comparisons/compare-inheritance.phpt @@ -49,7 +49,7 @@ var_dump(new ReversedComparable(1) <=> new Comparable(2)); // 1 ?> ---EXPECTF-- +--EXPECT-- int(1) int(0) int(-1) diff --git a/Zend/tests/comparisons/compare-normalize.phpt b/Zend/tests/comparisons/compare-normalize.phpt index 1bc4acc9663f0..fdf10760dedb5 100644 --- a/Zend/tests/comparisons/compare-normalize.phpt +++ b/Zend/tests/comparisons/compare-normalize.phpt @@ -32,7 +32,7 @@ var_dump(new B <=> 2); var_dump(new C <=> 3); ?> ---EXPECTF-- +--EXPECT-- int(1) int(-1) int(0) diff --git a/Zend/tests/comparisons/compare-return-false.phpt b/Zend/tests/comparisons/compare-return-false.phpt index 62acdf65807dd..8165bf9882fb8 100644 --- a/Zend/tests/comparisons/compare-return-false.phpt +++ b/Zend/tests/comparisons/compare-return-false.phpt @@ -14,5 +14,5 @@ class A var_dump(new A <=> new A); ?> ---EXPECTF-- +--EXPECT-- int(0) diff --git a/Zend/tests/comparisons/compare-return-float.phpt b/Zend/tests/comparisons/compare-return-float.phpt index e0dd1587d7abe..81bfe0bab1109 100644 --- a/Zend/tests/comparisons/compare-return-float.phpt +++ b/Zend/tests/comparisons/compare-return-float.phpt @@ -32,7 +32,7 @@ var_dump(new B <=> 2); var_dump(new C <=> 3); ?> ---EXPECTF-- +--EXPECT-- int(1) int(-1) int(0) diff --git a/Zend/tests/comparisons/compare-return-true.phpt b/Zend/tests/comparisons/compare-return-true.phpt index bb18d7707c072..5e142ab025ec0 100644 --- a/Zend/tests/comparisons/compare-return-true.phpt +++ b/Zend/tests/comparisons/compare-return-true.phpt @@ -14,5 +14,5 @@ class A var_dump(new A <=> new A); ?> ---EXPECTF-- +--EXPECT-- int(1) diff --git a/Zend/tests/comparisons/compare-rhs-is-used.phpt b/Zend/tests/comparisons/compare-rhs-is-used.phpt index a487638829899..b476becc58f2c 100644 --- a/Zend/tests/comparisons/compare-rhs-is-used.phpt +++ b/Zend/tests/comparisons/compare-rhs-is-used.phpt @@ -21,7 +21,7 @@ var_dump(new A <=> new B); var_dump(new B <=> new A); ?> ---EXPECTF-- +--EXPECT-- Comparing! int(-1) Comparing! diff --git a/Zend/tests/comparisons/compare-to-null.phpt b/Zend/tests/comparisons/compare-to-null.phpt index 503d2be650eb6..2b993a8e396ff 100644 --- a/Zend/tests/comparisons/compare-to-null.phpt +++ b/Zend/tests/comparisons/compare-to-null.phpt @@ -36,7 +36,7 @@ var_dump(null <=> new Comparable(0)); var_dump(new Comparable(0) <=> null); ?> ---EXPECTF-- +--EXPECT-- Comparing! int(1) Comparing! diff --git a/Zend/tests/comparisons/compare-to-scalar.phpt b/Zend/tests/comparisons/compare-to-scalar.phpt index f754738b8df0a..a57760402c061 100644 --- a/Zend/tests/comparisons/compare-to-scalar.phpt +++ b/Zend/tests/comparisons/compare-to-scalar.phpt @@ -21,7 +21,7 @@ var_dump(new Comparable(1) <=> 'c'); ?> ---EXPECTF-- +--EXPECT-- int(1) int(0) int(-1) diff --git a/Zend/tests/comparisons/equals-basic.phpt b/Zend/tests/comparisons/equals-basic.phpt index 62e20787cc2ce..9472bacc24e06 100644 --- a/Zend/tests/comparisons/equals-basic.phpt +++ b/Zend/tests/comparisons/equals-basic.phpt @@ -43,7 +43,7 @@ var_dump(new Equatable(1) <=> new Equatable(1)); // 0 var_dump(new Equatable(1) <=> new Equatable(2)); // 1 ?> ---EXPECTF-- +--EXPECT-- bool(true) bool(false) bool(false) diff --git a/Zend/tests/comparisons/equals-disallow-compare.phpt b/Zend/tests/comparisons/equals-disallow-compare.phpt index a22df9359683a..1a6052710960b 100644 --- a/Zend/tests/comparisons/equals-disallow-compare.phpt +++ b/Zend/tests/comparisons/equals-disallow-compare.phpt @@ -23,19 +23,18 @@ class Comparable } } - /** - * + * This should call __equals which returns FALSE. */ var_dump(new Comparable(1) == new Comparable(2)); /** - * + * This should call __equals which returns TRUE. */ var_dump(new Comparable(2) == new Comparable(2)); /** - * + * This should call __compareTo which is explicitly unsupported. */ try { new Comparable(2) <= new Comparable(2); @@ -44,17 +43,44 @@ try { } /** - * + * These should call __compareTo which is explicitly unsupported. */ try { - new Comparable(1) > new Comparable(2); + new Comparable(1) > new Comparable(1); +} catch (Exception $e) { + var_dump($e->getMessage()); +} + +try { + new Comparable(1) < new Comparable(1); +} catch (Exception $e) { + var_dump($e->getMessage()); +} + +try { + new Comparable(1) >= new Comparable(1); +} catch (Exception $e) { + var_dump($e->getMessage()); +} + +try { + new Comparable(1) <= new Comparable(1); +} catch (Exception $e) { + var_dump($e->getMessage()); +} + +try { + new Comparable(1) <=> new Comparable(1); } catch (Exception $e) { var_dump($e->getMessage()); } ?> ---EXPECTF-- +--EXPECT-- bool(false) bool(true) string(39) "This object does not support comparison" string(39) "This object does not support comparison" +string(39) "This object does not support comparison" +string(39) "This object does not support comparison" +string(39) "This object does not support comparison" diff --git a/Zend/tests/comparisons/equals-in-array.phpt b/Zend/tests/comparisons/equals-in-array.phpt index 2e9ef0776e6af..c7ee91760f779 100644 --- a/Zend/tests/comparisons/equals-in-array.phpt +++ b/Zend/tests/comparisons/equals-in-array.phpt @@ -24,7 +24,7 @@ var_dump(in_array(new Equatable(4), $array)); var_dump(in_array(new Equatable(5), $array)); ?> ---EXPECTF-- +--EXPECT-- bool(true) bool(true) bool(true) diff --git a/Zend/tests/comparisons/legacy-compare-objects.phpt b/Zend/tests/comparisons/legacy-compare-objects.phpt index 4c4666dd8d67d..7a9a65d82f3a4 100644 --- a/Zend/tests/comparisons/legacy-compare-objects.phpt +++ b/Zend/tests/comparisons/legacy-compare-objects.phpt @@ -20,7 +20,7 @@ var_dump($b <=> $a); var_dump($b <=> $b); ?> ---EXPECTF-- +--EXPECT-- int(0) int(-1) int(1) diff --git a/Zend/tests/comparisons/legacy-compare-to-null.phpt b/Zend/tests/comparisons/legacy-compare-to-null.phpt index 6434741d6e44b..981589e63ece3 100644 --- a/Zend/tests/comparisons/legacy-compare-to-null.phpt +++ b/Zend/tests/comparisons/legacy-compare-to-null.phpt @@ -23,7 +23,7 @@ var_dump(null > new stdClass); var_dump(null <=> new stdClass); ?> ---EXPECTF-- +--EXPECT-- bool(false) bool(true) bool(false) diff --git a/Zend/tests/comparisons/legacy-strict-comparison.phpt b/Zend/tests/comparisons/legacy-strict-comparison.phpt index adfb320be7c5e..2b5176b790a39 100644 --- a/Zend/tests/comparisons/legacy-strict-comparison.phpt +++ b/Zend/tests/comparisons/legacy-strict-comparison.phpt @@ -18,6 +18,6 @@ var_dump($a === $a); var_dump($a === $b); ?> ---EXPECTF-- +--EXPECT-- bool(true) bool(false) diff --git a/Zend/tests/comparisons/setup.inc b/Zend/tests/comparisons/setup.inc index 8edeb9ba53bc5..5759d21c88455 100644 --- a/Zend/tests/comparisons/setup.inc +++ b/Zend/tests/comparisons/setup.inc @@ -1,7 +1,9 @@ Date: Wed, 27 Jun 2018 19:35:55 -0400 Subject: [PATCH 29/40] Fix bad is_smaller_or_equal_function call --- Zend/zend_vm_def.h | 2 +- Zend/zend_vm_execute.h | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index 69ea576335538..71ea618ee1da8 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -593,7 +593,7 @@ ZEND_VM_COLD_CONSTCONST_HANDLER(20, ZEND_IS_SMALLER_OR_EQUAL, CONST|TMPVAR|CV, C op2 = GET_OP2_UNDEF_CV(op2, BP_VAR_R); } result = EX_VAR(opline->result.var); - is_smaller_or_equal_function(result); + is_smaller_or_equal_function(result, op1, op2); FREE_OP1(); FREE_OP2(); ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 39e3a86cbfb05..b9d19f5bde27d 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -4428,7 +4428,7 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_SMALLER_OR_EQU op2 = GET_OP2_UNDEF_CV(op2, BP_VAR_R); } result = EX_VAR(opline->result.var); - is_smaller_or_equal_function(result); + is_smaller_or_equal_function(result, op1, op2); ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); @@ -6748,7 +6748,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_SMALLER_OR_EQUAL_SPEC_CONST op2 = GET_OP2_UNDEF_CV(op2, BP_VAR_R); } result = EX_VAR(opline->result.var); - is_smaller_or_equal_function(result); + is_smaller_or_equal_function(result, op1, op2); zval_ptr_dtor_nogc(free_op2); ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); @@ -9839,7 +9839,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_SMALLER_OR_EQUAL_SPEC_CONST op2 = GET_OP2_UNDEF_CV(op2, BP_VAR_R); } result = EX_VAR(opline->result.var); - is_smaller_or_equal_function(result); + is_smaller_or_equal_function(result, op1, op2); ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); @@ -13338,7 +13338,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_SMALLER_OR_EQUAL_SPEC_TMPVA op2 = GET_OP2_UNDEF_CV(op2, BP_VAR_R); } result = EX_VAR(opline->result.var); - is_smaller_or_equal_function(result); + is_smaller_or_equal_function(result, op1, op2); zval_ptr_dtor_nogc(free_op1); ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); @@ -15063,7 +15063,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_SMALLER_OR_EQUAL_SPEC_TMPVA op2 = GET_OP2_UNDEF_CV(op2, BP_VAR_R); } result = EX_VAR(opline->result.var); - is_smaller_or_equal_function(result); + is_smaller_or_equal_function(result, op1, op2); zval_ptr_dtor_nogc(free_op1); zval_ptr_dtor_nogc(free_op2); ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); @@ -17031,7 +17031,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_SMALLER_OR_EQUAL_SPEC_TMPVA op2 = GET_OP2_UNDEF_CV(op2, BP_VAR_R); } result = EX_VAR(opline->result.var); - is_smaller_or_equal_function(result); + is_smaller_or_equal_function(result, op1, op2); zval_ptr_dtor_nogc(free_op1); ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); @@ -38397,7 +38397,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_SMALLER_OR_EQUAL_SPEC_CV_CO op2 = GET_OP2_UNDEF_CV(op2, BP_VAR_R); } result = EX_VAR(opline->result.var); - is_smaller_or_equal_function(result); + is_smaller_or_equal_function(result, op1, op2); ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); @@ -42271,7 +42271,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_SMALLER_OR_EQUAL_SPEC_CV_TM op2 = GET_OP2_UNDEF_CV(op2, BP_VAR_R); } result = EX_VAR(opline->result.var); - is_smaller_or_equal_function(result); + is_smaller_or_equal_function(result, op1, op2); zval_ptr_dtor_nogc(free_op2); ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); @@ -47826,7 +47826,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_SMALLER_OR_EQUAL_SPEC_CV_CV op2 = GET_OP2_UNDEF_CV(op2, BP_VAR_R); } result = EX_VAR(opline->result.var); - is_smaller_or_equal_function(result); + is_smaller_or_equal_function(result, op1, op2); ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); From 1b8d8b466f044ebff15c7a38a528724f2823c00e Mon Sep 17 00:00:00 2001 From: Rudi Theunissen Date: Wed, 27 Jun 2018 19:37:30 -0400 Subject: [PATCH 30/40] Fix bad is_smaller_function call --- Zend/zend_vm_def.h | 2 +- Zend/zend_vm_execute.h | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index 71ea618ee1da8..9e050fbc79465 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -544,7 +544,7 @@ ZEND_VM_COLD_CONSTCONST_HANDLER(19, ZEND_IS_SMALLER, CONST|TMPVAR|CV, CONST|TMPV op2 = GET_OP2_UNDEF_CV(op2, BP_VAR_R); } result = EX_VAR(opline->result.var); - is_smaller_function(result); + is_smaller_function(result, op1, op2); FREE_OP1(); FREE_OP2(); ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index b9d19f5bde27d..0f0639d24f869 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -4379,7 +4379,7 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_SMALLER_SPEC_C op2 = GET_OP2_UNDEF_CV(op2, BP_VAR_R); } result = EX_VAR(opline->result.var); - is_smaller_function(result); + is_smaller_function(result, op1, op2); ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); @@ -6699,7 +6699,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_SMALLER_SPEC_CONST_TMPVAR_H op2 = GET_OP2_UNDEF_CV(op2, BP_VAR_R); } result = EX_VAR(opline->result.var); - is_smaller_function(result); + is_smaller_function(result, op1, op2); zval_ptr_dtor_nogc(free_op2); ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); @@ -9790,7 +9790,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_SMALLER_SPEC_CONST_CV_HANDL op2 = GET_OP2_UNDEF_CV(op2, BP_VAR_R); } result = EX_VAR(opline->result.var); - is_smaller_function(result); + is_smaller_function(result, op1, op2); ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); @@ -13289,7 +13289,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_SMALLER_SPEC_TMPVAR_CONST_H op2 = GET_OP2_UNDEF_CV(op2, BP_VAR_R); } result = EX_VAR(opline->result.var); - is_smaller_function(result); + is_smaller_function(result, op1, op2); zval_ptr_dtor_nogc(free_op1); ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); @@ -15014,7 +15014,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_SMALLER_SPEC_TMPVAR_TMPVAR_ op2 = GET_OP2_UNDEF_CV(op2, BP_VAR_R); } result = EX_VAR(opline->result.var); - is_smaller_function(result); + is_smaller_function(result, op1, op2); zval_ptr_dtor_nogc(free_op1); zval_ptr_dtor_nogc(free_op2); ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); @@ -16982,7 +16982,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_SMALLER_SPEC_TMPVAR_CV_HAND op2 = GET_OP2_UNDEF_CV(op2, BP_VAR_R); } result = EX_VAR(opline->result.var); - is_smaller_function(result); + is_smaller_function(result, op1, op2); zval_ptr_dtor_nogc(free_op1); ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); @@ -38348,7 +38348,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_SMALLER_SPEC_CV_CONST_HANDL op2 = GET_OP2_UNDEF_CV(op2, BP_VAR_R); } result = EX_VAR(opline->result.var); - is_smaller_function(result); + is_smaller_function(result, op1, op2); ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); @@ -42222,7 +42222,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_SMALLER_SPEC_CV_TMPVAR_HAND op2 = GET_OP2_UNDEF_CV(op2, BP_VAR_R); } result = EX_VAR(opline->result.var); - is_smaller_function(result); + is_smaller_function(result, op1, op2); zval_ptr_dtor_nogc(free_op2); ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); @@ -47777,7 +47777,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IS_SMALLER_SPEC_CV_CV_HANDLER( op2 = GET_OP2_UNDEF_CV(op2, BP_VAR_R); } result = EX_VAR(opline->result.var); - is_smaller_function(result); + is_smaller_function(result, op1, op2); ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); From 6f520e4f3c4981e1f9b7915e017bfb4069f9bde8 Mon Sep 17 00:00:00 2001 From: Rudi Theunissen Date: Wed, 27 Jun 2018 20:01:45 -0400 Subject: [PATCH 31/40] Fix broken test --- Zend/tests/comparisons/equals-disallow-compare.phpt | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/Zend/tests/comparisons/equals-disallow-compare.phpt b/Zend/tests/comparisons/equals-disallow-compare.phpt index 1a6052710960b..4a7779bad1a9e 100644 --- a/Zend/tests/comparisons/equals-disallow-compare.phpt +++ b/Zend/tests/comparisons/equals-disallow-compare.phpt @@ -34,7 +34,7 @@ var_dump(new Comparable(1) == new Comparable(2)); var_dump(new Comparable(2) == new Comparable(2)); /** - * This should call __compareTo which is explicitly unsupported. + * These should call __compareTo which is explicitly unsupported. */ try { new Comparable(2) <= new Comparable(2); @@ -42,9 +42,6 @@ try { var_dump($e->getMessage()); } -/** - * These should call __compareTo which is explicitly unsupported. - */ try { new Comparable(1) > new Comparable(1); } catch (Exception $e) { @@ -84,3 +81,4 @@ string(39) "This object does not support comparison" string(39) "This object does not support comparison" string(39) "This object does not support comparison" string(39) "This object does not support comparison" +string(39) "This object does not support comparison" From b4337342e04cba2a4facc3f33c38d725c58d29dc Mon Sep 17 00:00:00 2001 From: Rudi Theunissen Date: Wed, 27 Jun 2018 21:12:59 -0400 Subject: [PATCH 32/40] Update IN_ARRAY op handler and in_array sccp optimisation to use is_equal_function --- Zend/zend_vm_def.h | 7 +++---- Zend/zend_vm_execute.h | 25 +++++++++++-------------- ext/opcache/Optimizer/sccp.c | 4 ++-- 3 files changed, 16 insertions(+), 20 deletions(-) diff --git a/Zend/zend_vm_def.h b/Zend/zend_vm_def.h index 9e050fbc79465..7857b5081b74c 100644 --- a/Zend/zend_vm_def.h +++ b/Zend/zend_vm_def.h @@ -4879,8 +4879,7 @@ ZEND_VM_HANDLER(48, ZEND_CASE, TMPVAR, CONST|TMPVAR|CV) op2 = GET_OP2_UNDEF_CV(op2, BP_VAR_R); } result = EX_VAR(opline->result.var); - compare_function(result, op1, op2); - ZVAL_BOOL(result, Z_LVAL_P(result) == 0); + is_equal_function(result, op1, op2); FREE_OP2(); ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); } @@ -8034,8 +8033,8 @@ ZEND_VM_COLD_CONSTCONST_HANDLER(189, ZEND_IN_ARRAY, CONST|TMP|VAR|CV, CONST, NUM result = 0; ZEND_HASH_FOREACH_STR_KEY(ht, key) { ZVAL_STR(&key_tmp, key); - compare_function(&result_tmp, op1, &key_tmp); - if (Z_LVAL(result_tmp) == 0) { + is_equal_function(&result_tmp, op1, &key_tmp); + if (Z_TYPE(result_tmp) == IS_TRUE) { result = 1; break; } diff --git a/Zend/zend_vm_execute.h b/Zend/zend_vm_execute.h index 0f0639d24f869..7f84030a6157b 100644 --- a/Zend/zend_vm_execute.h +++ b/Zend/zend_vm_execute.h @@ -6116,8 +6116,8 @@ static ZEND_VM_COLD ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IN_ARRAY_SPEC_CON result = 0; ZEND_HASH_FOREACH_STR_KEY(ht, key) { ZVAL_STR(&key_tmp, key); - compare_function(&result_tmp, op1, &key_tmp); - if (Z_LVAL(result_tmp) == 0) { + is_equal_function(&result_tmp, op1, &key_tmp); + if (Z_TYPE(result_tmp) == IS_TRUE) { result = 1; break; } @@ -14133,8 +14133,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CASE_SPEC_TMPVAR_CONST_HANDLER op2 = GET_OP2_UNDEF_CV(op2, BP_VAR_R); } result = EX_VAR(opline->result.var); - compare_function(result, op1, op2); - ZVAL_BOOL(result, Z_LVAL_P(result) == 0); + is_equal_function(result, op1, op2); ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); } @@ -15734,8 +15733,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CASE_SPEC_TMPVAR_TMPVAR_HANDLE op2 = GET_OP2_UNDEF_CV(op2, BP_VAR_R); } result = EX_VAR(opline->result.var); - compare_function(result, op1, op2); - ZVAL_BOOL(result, Z_LVAL_P(result) == 0); + is_equal_function(result, op1, op2); zval_ptr_dtor_nogc(free_op2); ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); } @@ -17604,8 +17602,7 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_CASE_SPEC_TMPVAR_CV_HANDLER(ZE op2 = GET_OP2_UNDEF_CV(op2, BP_VAR_R); } result = EX_VAR(opline->result.var); - compare_function(result, op1, op2); - ZVAL_BOOL(result, Z_LVAL_P(result) == 0); + is_equal_function(result, op1, op2); ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION(); } @@ -18921,8 +18918,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IN_ARRAY_SPEC_TMP_CONST_HANDLE result = 0; ZEND_HASH_FOREACH_STR_KEY(ht, key) { ZVAL_STR(&key_tmp, key); - compare_function(&result_tmp, op1, &key_tmp); - if (Z_LVAL(result_tmp) == 0) { + is_equal_function(&result_tmp, op1, &key_tmp); + if (Z_TYPE(result_tmp) == IS_TRUE) { result = 1; break; } @@ -24186,8 +24183,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IN_ARRAY_SPEC_VAR_CONST_HANDLE result = 0; ZEND_HASH_FOREACH_STR_KEY(ht, key) { ZVAL_STR(&key_tmp, key); - compare_function(&result_tmp, op1, &key_tmp); - if (Z_LVAL(result_tmp) == 0) { + is_equal_function(&result_tmp, op1, &key_tmp); + if (Z_TYPE(result_tmp) == IS_TRUE) { result = 1; break; } @@ -41630,8 +41627,8 @@ static ZEND_OPCODE_HANDLER_RET ZEND_FASTCALL ZEND_IN_ARRAY_SPEC_CV_CONST_HANDLER result = 0; ZEND_HASH_FOREACH_STR_KEY(ht, key) { ZVAL_STR(&key_tmp, key); - compare_function(&result_tmp, op1, &key_tmp); - if (Z_LVAL(result_tmp) == 0) { + is_equal_function(&result_tmp, op1, &key_tmp); + if (Z_TYPE(result_tmp) == IS_TRUE) { result = 1; break; } diff --git a/ext/opcache/Optimizer/sccp.c b/ext/opcache/Optimizer/sccp.c index c9acb0498387c..52ca68d52d0f0 100644 --- a/ext/opcache/Optimizer/sccp.c +++ b/ext/opcache/Optimizer/sccp.c @@ -688,8 +688,8 @@ static inline int ct_eval_in_array(zval *result, uint32_t extended_value, zval * res = 0; ZEND_HASH_FOREACH_STR_KEY(ht, key) { ZVAL_STR(&key_tmp, key); - compare_function(&result_tmp, op1, &key_tmp); - if (Z_LVAL(result_tmp) == 0) { + is_equal_function(&result_tmp, op1, &key_tmp); + if (Z_TYPE(result_tmp) == IS_TRUE) { res = 1; break; } From d218d327c36b193d9e371bba648a4e1121b58510 Mon Sep 17 00:00:00 2001 From: Rudi Theunissen Date: Thu, 28 Jun 2018 07:50:15 -0400 Subject: [PATCH 33/40] Add __equals and __compareTo to zend_accelerator_util_funcs inheritance updates --- ext/opcache/zend_accelerator_util_funcs.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ext/opcache/zend_accelerator_util_funcs.c b/ext/opcache/zend_accelerator_util_funcs.c index 05708a7068d84..155b591ff383d 100644 --- a/ext/opcache/zend_accelerator_util_funcs.c +++ b/ext/opcache/zend_accelerator_util_funcs.c @@ -424,6 +424,10 @@ static void zend_class_copy_ctor(zend_class_entry **pce) zend_update_inherited_handler(__callstatic); zend_update_inherited_handler(__debugInfo); +/* Comparison methods */ + zend_update_inherited_handler(__compareTo); + zend_update_inherited_handler(__equals); + /* 5.4 traits */ if (ce->trait_aliases) { zend_trait_alias **trait_aliases; From 3c1c9ea61c52ce1831430ee7b09a6abd7ed7a502 Mon Sep 17 00:00:00 2001 From: Rudi Theunissen Date: Thu, 28 Jun 2018 12:54:32 -0400 Subject: [PATCH 34/40] Move compareTo and equals tests into their own directories and improve them --- .../{setup.inc => __compareTo/Comparable.inc} | 27 +--- .../compare-array-func-array-keys.phpt | 121 ++++++++++++++++++ .../compare-array-func-array-search.phpt | 71 ++++++++++ .../compare-array-func-in-array.phpt | 71 ++++++++++ .../{ => __compareTo}/compare-array-sort.phpt | 4 +- .../compare-as-equality-fallback.phpt} | 2 +- .../compare-inheritance.phpt | 16 ++- .../compare-method-header.phpt} | 8 ++ .../__compareTo/compare-normalize-to-int.phpt | 26 ++++ .../__compareTo/compare-objects-handler.phpt | 40 ++++++ .../compare-operators.phpt} | 4 +- .../compare-rhs.phpt} | 0 .../compare-throw-exception.phpt | 4 +- .../{ => __compareTo}/compare-to-null.phpt | 35 +++++ .../__compareTo/compare-to-scalar.phpt | 33 +++++ Zend/tests/comparisons/__equals/Equatable.inc | 28 ++++ .../equals-array-func-array-keys.phpt | 121 ++++++++++++++++++ .../equals-array-func-array-search.phpt | 71 ++++++++++ .../__equals/equals-array-func-in-array.phpt | 71 ++++++++++ .../__equals/equals-array-sort.phpt | 42 ++++++ .../equals-compare-objects-handler.phpt | 41 ++++++ .../__equals/equals-disallow-compare.phpt | 50 ++++++++ .../__equals/equals-inheritance.phpt | 78 +++++++++++ .../equals-method-header.phpt} | 8 ++ .../__equals/equals-normalize-to-bool.phpt | 26 ++++ .../equals-not-called-for-ordering.phpt | 19 +++ .../equals-operators.phpt} | 4 +- .../comparisons/__equals/equals-rhs.phpt | 28 ++++ .../__equals/equals-throw-exception.phpt | 26 ++++ .../comparisons/__equals/equals-to-null.phpt | 62 +++++++++ .../__equals/equals-to-scalar.phpt | 33 +++++ Zend/tests/comparisons/compare-in-array.phpt | 42 ------ Zend/tests/comparisons/compare-normalize.phpt | 38 ------ .../comparisons/compare-return-false.phpt | 18 --- .../comparisons/compare-return-float.phpt | 38 ------ .../comparisons/compare-return-object.phpt | 19 --- .../comparisons/compare-return-true.phpt | 18 --- Zend/tests/comparisons/compare-to-scalar.phpt | 30 ----- .../comparisons/equals-disallow-compare.phpt | 84 ------------ Zend/tests/comparisons/equals-in-array.phpt | 35 ----- .../equals-not-called-for-ordering.phpt | 19 --- .../comparisons/legacy-compare-objects.phpt | 27 ---- .../comparisons/legacy-compare-to-null.phpt | 39 ------ .../comparisons/legacy-strict-comparison.phpt | 23 ---- 44 files changed, 1138 insertions(+), 462 deletions(-) rename Zend/tests/comparisons/{setup.inc => __compareTo/Comparable.inc} (53%) create mode 100644 Zend/tests/comparisons/__compareTo/compare-array-func-array-keys.phpt create mode 100644 Zend/tests/comparisons/__compareTo/compare-array-func-array-search.phpt create mode 100644 Zend/tests/comparisons/__compareTo/compare-array-func-in-array.phpt rename Zend/tests/comparisons/{ => __compareTo}/compare-array-sort.phpt (83%) rename Zend/tests/comparisons/{compare-not-called-for-equality.phpt => __compareTo/compare-as-equality-fallback.phpt} (91%) rename Zend/tests/comparisons/{ => __compareTo}/compare-inheritance.phpt (92%) rename Zend/tests/comparisons/{compare-public-not-static.phpt => __compareTo/compare-method-header.phpt} (80%) create mode 100644 Zend/tests/comparisons/__compareTo/compare-normalize-to-int.phpt create mode 100644 Zend/tests/comparisons/__compareTo/compare-objects-handler.phpt rename Zend/tests/comparisons/{compare-basic.phpt => __compareTo/compare-operators.phpt} (96%) rename Zend/tests/comparisons/{compare-rhs-is-used.phpt => __compareTo/compare-rhs.phpt} (100%) rename Zend/tests/comparisons/{ => __compareTo}/compare-throw-exception.phpt (73%) rename Zend/tests/comparisons/{ => __compareTo}/compare-to-null.phpt (54%) create mode 100644 Zend/tests/comparisons/__compareTo/compare-to-scalar.phpt create mode 100644 Zend/tests/comparisons/__equals/Equatable.inc create mode 100644 Zend/tests/comparisons/__equals/equals-array-func-array-keys.phpt create mode 100644 Zend/tests/comparisons/__equals/equals-array-func-array-search.phpt create mode 100644 Zend/tests/comparisons/__equals/equals-array-func-in-array.phpt create mode 100644 Zend/tests/comparisons/__equals/equals-array-sort.phpt create mode 100644 Zend/tests/comparisons/__equals/equals-compare-objects-handler.phpt create mode 100644 Zend/tests/comparisons/__equals/equals-disallow-compare.phpt create mode 100644 Zend/tests/comparisons/__equals/equals-inheritance.phpt rename Zend/tests/comparisons/{equals-public-not-static.phpt => __equals/equals-method-header.phpt} (80%) create mode 100644 Zend/tests/comparisons/__equals/equals-normalize-to-bool.phpt create mode 100644 Zend/tests/comparisons/__equals/equals-not-called-for-ordering.phpt rename Zend/tests/comparisons/{equals-basic.phpt => __equals/equals-operators.phpt} (96%) create mode 100644 Zend/tests/comparisons/__equals/equals-rhs.phpt create mode 100644 Zend/tests/comparisons/__equals/equals-throw-exception.phpt create mode 100644 Zend/tests/comparisons/__equals/equals-to-null.phpt create mode 100644 Zend/tests/comparisons/__equals/equals-to-scalar.phpt delete mode 100644 Zend/tests/comparisons/compare-in-array.phpt delete mode 100644 Zend/tests/comparisons/compare-normalize.phpt delete mode 100644 Zend/tests/comparisons/compare-return-false.phpt delete mode 100644 Zend/tests/comparisons/compare-return-float.phpt delete mode 100644 Zend/tests/comparisons/compare-return-object.phpt delete mode 100644 Zend/tests/comparisons/compare-return-true.phpt delete mode 100644 Zend/tests/comparisons/compare-to-scalar.phpt delete mode 100644 Zend/tests/comparisons/equals-disallow-compare.phpt delete mode 100644 Zend/tests/comparisons/equals-in-array.phpt delete mode 100644 Zend/tests/comparisons/equals-not-called-for-ordering.phpt delete mode 100644 Zend/tests/comparisons/legacy-compare-objects.phpt delete mode 100644 Zend/tests/comparisons/legacy-compare-to-null.phpt delete mode 100644 Zend/tests/comparisons/legacy-strict-comparison.phpt diff --git a/Zend/tests/comparisons/setup.inc b/Zend/tests/comparisons/__compareTo/Comparable.inc similarity index 53% rename from Zend/tests/comparisons/setup.inc rename to Zend/tests/comparisons/__compareTo/Comparable.inc index 5759d21c88455..453fa3ea7a051 100644 --- a/Zend/tests/comparisons/setup.inc +++ b/Zend/tests/comparisons/__compareTo/Comparable.inc @@ -1,11 +1,10 @@ decoy = $value * -1; $this->value = $value; } -} - -/** - * This is a class that compares by a given $value only. There is also a $decoy - * value that makes sure that the tests would fail if `__equals` is not used. - */ -class Equatable extends Base -{ - public function __equals($other): bool { - return $other instanceof self && $this->value === $other->value; - } -} -/** - * This is a class that compares by a given $value only. There is also a $decoy - * value that makes sure that the tests would fail if `__compareTo` is not used. - */ -class Comparable extends Base -{ public function __compareTo($other) { if ($other instanceof self) { return $this->value <=> $other->value; @@ -44,5 +25,7 @@ class Comparable extends Base if (is_numeric($other)) { return $this->value <=> $other; } + + throw new Exception("Failed to compare"); } } diff --git a/Zend/tests/comparisons/__compareTo/compare-array-func-array-keys.phpt b/Zend/tests/comparisons/__compareTo/compare-array-func-array-keys.phpt new file mode 100644 index 0000000000000..8230009f14c1e --- /dev/null +++ b/Zend/tests/comparisons/__compareTo/compare-array-func-array-keys.phpt @@ -0,0 +1,121 @@ +--TEST-- +__compareTo: Called by array_keys which has equality semantics (searching) +--FILE-- + $a, + 'b' => $b, + 'c' => $c, +]; + +var_dump(array_keys($array, $a, $strict = false)); // a +var_dump(array_keys($array, $b, $strict = false)); // b +var_dump(array_keys($array, $c, $strict = false)); // c + +var_dump(array_keys($array, $a, $strict = true)); // a +var_dump(array_keys($array, $b, $strict = true)); // b +var_dump(array_keys($array, $c, $strict = true)); // c + +/* Found, because Comparable::__compareTo returned 0 */ +var_dump(array_keys($array, new Comparable(1), $strict = false)); // a +var_dump(array_keys($array, new Comparable(2), $strict = false)); // b +var_dump(array_keys($array, new Comparable(3), $strict = false)); // c + +/* Not found because strict comparison doesn't call __compareTo */ +var_dump(array_keys($array, new Comparable(1), $strict = true)); +var_dump(array_keys($array, new Comparable(2), $strict = true)); +var_dump(array_keys($array, new Comparable(3), $strict = true)); + +/* Found, because Comparable::__compareTo returned 0 and we haven't implemented __equals */ +var_dump(array_keys($array, 1, $strict = false)); // a +var_dump(array_keys($array, 2, $strict = false)); // b +var_dump(array_keys($array, 3, $strict = false)); // c + +/* Not found because strict comparison doesn't call __compareTo */ +var_dump(array_keys($array, 1, $strict = true)); +var_dump(array_keys($array, 2, $strict = true)); +var_dump(array_keys($array, 3, $strict = true)); + +/* Not found */ +var_dump(array_keys($array, new Comparable(4), $strict = true)); +var_dump(array_keys($array, new Comparable(5), $strict = true)); + +var_dump(array_keys($array, new Comparable(4), $strict = false)); +var_dump(array_keys($array, new Comparable(5), $strict = false)); + +?> +--EXPECT-- +array(1) { + [0]=> + string(1) "a" +} +array(1) { + [0]=> + string(1) "b" +} +array(1) { + [0]=> + string(1) "c" +} +array(1) { + [0]=> + string(1) "a" +} +array(1) { + [0]=> + string(1) "b" +} +array(1) { + [0]=> + string(1) "c" +} +array(1) { + [0]=> + string(1) "a" +} +array(1) { + [0]=> + string(1) "b" +} +array(1) { + [0]=> + string(1) "c" +} +array(0) { +} +array(0) { +} +array(0) { +} +array(1) { + [0]=> + string(1) "a" +} +array(1) { + [0]=> + string(1) "b" +} +array(1) { + [0]=> + string(1) "c" +} +array(0) { +} +array(0) { +} +array(0) { +} +array(0) { +} +array(0) { +} +array(0) { +} +array(0) { +} diff --git a/Zend/tests/comparisons/__compareTo/compare-array-func-array-search.phpt b/Zend/tests/comparisons/__compareTo/compare-array-func-array-search.phpt new file mode 100644 index 0000000000000..8f22231f15694 --- /dev/null +++ b/Zend/tests/comparisons/__compareTo/compare-array-func-array-search.phpt @@ -0,0 +1,71 @@ +--TEST-- +__compareTo: Called by array_search which has equality semantics (searching) +--FILE-- + +--EXPECT-- +int(0) +int(1) +int(2) +int(0) +int(1) +int(2) +int(0) +int(1) +int(2) +bool(false) +bool(false) +bool(false) +int(0) +int(1) +int(2) +bool(false) +bool(false) +bool(false) +bool(false) +bool(false) +bool(false) +bool(false) diff --git a/Zend/tests/comparisons/__compareTo/compare-array-func-in-array.phpt b/Zend/tests/comparisons/__compareTo/compare-array-func-in-array.phpt new file mode 100644 index 0000000000000..01d1d8d742646 --- /dev/null +++ b/Zend/tests/comparisons/__compareTo/compare-array-func-in-array.phpt @@ -0,0 +1,71 @@ +--TEST-- +__compareTo: Called by in_array which has equality semantics (searching) +--FILE-- + +--EXPECT-- +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(false) +bool(false) +bool(false) +bool(true) +bool(true) +bool(true) +bool(false) +bool(false) +bool(false) +bool(false) +bool(false) +bool(false) +bool(false) diff --git a/Zend/tests/comparisons/compare-array-sort.phpt b/Zend/tests/comparisons/__compareTo/compare-array-sort.phpt similarity index 83% rename from Zend/tests/comparisons/compare-array-sort.phpt rename to Zend/tests/comparisons/__compareTo/compare-array-sort.phpt index 09956812fa173..b1ef451f4a728 100644 --- a/Zend/tests/comparisons/compare-array-sort.phpt +++ b/Zend/tests/comparisons/__compareTo/compare-array-sort.phpt @@ -1,8 +1,8 @@ --TEST-- -__compareTo: Supported by array functions with ordering semantics (sorting) +__compareTo: Called by array functions with ordering semantics (sorting) --FILE-- new InheritedComparable(0)); // 1 var_dump(new Comparable(1) <=> new InheritedComparable(1)); // 0 var_dump(new Comparable(1) <=> new InheritedComparable(2)); // -1 +echo "\n"; + var_dump(new InheritedComparable(1) <=> new Comparable(0)); // 1 var_dump(new InheritedComparable(1) <=> new Comparable(1)); // 0 var_dump(new InheritedComparable(1) <=> new Comparable(2)); // -1 +echo "\n"; + /** * Compare against an extending object that overrides the base behaviour. */ @@ -40,6 +46,8 @@ var_dump(new Comparable(1) <=> new ReversedComparable(0)); // 1 var_dump(new Comparable(1) <=> new ReversedComparable(1)); // 0 var_dump(new Comparable(1) <=> new ReversedComparable(2)); // -1 +echo "\n"; + /** * These will use ReversedComparable's __compareTo */ @@ -53,12 +61,18 @@ var_dump(new ReversedComparable(1) <=> new Comparable(2)); // 1 int(1) int(0) int(-1) + int(1) int(0) int(-1) + int(1) int(0) int(-1) + +Reverse! int(-1) +Reverse! int(0) +Reverse! int(1) diff --git a/Zend/tests/comparisons/compare-public-not-static.phpt b/Zend/tests/comparisons/__compareTo/compare-method-header.phpt similarity index 80% rename from Zend/tests/comparisons/compare-public-not-static.phpt rename to Zend/tests/comparisons/__compareTo/compare-method-header.phpt index 73c74c1cecb38..134a1da231fb6 100644 --- a/Zend/tests/comparisons/compare-public-not-static.phpt +++ b/Zend/tests/comparisons/__compareTo/compare-method-header.phpt @@ -15,6 +15,14 @@ class C { static public function __compareTo() {} } +class D { + function __compareTo() {} // This is okay. +} + +class E { + public function __compareTo(E $value): E {} // This is okay. +} + ?> --EXPECTF-- Warning: The magic method __compareTo() must have public visibility and cannot be static in %s on line %d diff --git a/Zend/tests/comparisons/__compareTo/compare-normalize-to-int.phpt b/Zend/tests/comparisons/__compareTo/compare-normalize-to-int.phpt new file mode 100644 index 0000000000000..2af0ea88e0859 --- /dev/null +++ b/Zend/tests/comparisons/__compareTo/compare-normalize-to-int.phpt @@ -0,0 +1,26 @@ +--TEST-- +__compareTo: Comparison always returns -1, 0, or 1 +--FILE-- + 1); +var_dump(new class { public function __compareTo($other) { return -2; } } <=> 1); +var_dump(new class { public function __compareTo($other) { return 0.5; } } <=> 1); // Not truncated +var_dump(new class { public function __compareTo($other) { return -0.5; } } <=> 1); // Not truncated +var_dump(new class { public function __compareTo($other) { return 'a'; } } <=> 1); +var_dump(new class { public function __compareTo($other) { return true; } } <=> 1); +var_dump(new class { public function __compareTo($other) { return new stdClass; } } <=> 1); // Attempt to convert +var_dump(new class { public function __compareTo($other) { } } <=> 1); + +?> +--EXPECTF-- +int(1) +int(-1) +int(1) +int(-1) +int(0) +int(1) + +Notice: Object of class stdClass could not be converted to int in %s on line %d +int(1) +int(0) diff --git a/Zend/tests/comparisons/__compareTo/compare-objects-handler.phpt b/Zend/tests/comparisons/__compareTo/compare-objects-handler.phpt new file mode 100644 index 0000000000000..fe51ad7195f4c --- /dev/null +++ b/Zend/tests/comparisons/__compareTo/compare-objects-handler.phpt @@ -0,0 +1,40 @@ +--TEST-- +__compareTo: Objects implementing compare_objects handler are unaffected +--SKIPIF-- + +--FILE-- + $a); // 0 +var_dump($a <=> $b); // 0 +var_dump($a <=> $c); // -1 + +var_dump($b <=> $a); // 0 +var_dump($b <=> $b); // 0 +var_dump($b <=> $c); // -1 + +var_dump($c <=> $a); // 1 +var_dump($c <=> $b); // 1 +var_dump($c <=> $c); // 0 + +?> +--EXPECT-- +int(0) +int(0) +int(-1) +int(0) +int(0) +int(-1) +int(1) +int(1) +int(0) diff --git a/Zend/tests/comparisons/compare-basic.phpt b/Zend/tests/comparisons/__compareTo/compare-operators.phpt similarity index 96% rename from Zend/tests/comparisons/compare-basic.phpt rename to Zend/tests/comparisons/__compareTo/compare-operators.phpt index ac2db65762ab6..1bb8cb262fb7f 100644 --- a/Zend/tests/comparisons/compare-basic.phpt +++ b/Zend/tests/comparisons/__compareTo/compare-operators.phpt @@ -1,8 +1,8 @@ --TEST-- -__compareTo: Basic comparison behaviour +__compareTo: Basic comparison operator behaviour --FILE-- new Comparable(1)); var_dump(null <=> new Comparable(0)); var_dump(new Comparable(0) <=> null); +echo "\n"; + +/** + * Check that default behaviour still works as expected. + */ +var_dump(new stdClass == null); +var_dump(new stdClass >= null); +var_dump(new stdClass <= null); +var_dump(new stdClass < null); +var_dump(new stdClass > null); +var_dump(new stdClass <=> null); + +echo "\n"; + +var_dump(null == new stdClass); +var_dump(null >= new stdClass); +var_dump(null <= new stdClass); +var_dump(null < new stdClass); +var_dump(null > new stdClass); +var_dump(null <=> new stdClass); + ?> --EXPECT-- Comparing! @@ -45,3 +66,17 @@ Comparing! int(0) Comparing! int(0) + +bool(false) +bool(true) +bool(false) +bool(false) +bool(true) +int(1) + +bool(false) +bool(false) +bool(true) +bool(true) +bool(false) +int(-1) diff --git a/Zend/tests/comparisons/__compareTo/compare-to-scalar.phpt b/Zend/tests/comparisons/__compareTo/compare-to-scalar.phpt new file mode 100644 index 0000000000000..1d4cca41b45bb --- /dev/null +++ b/Zend/tests/comparisons/__compareTo/compare-to-scalar.phpt @@ -0,0 +1,33 @@ +--TEST-- +__compareTo: Compare against scalar values +--FILE-- + 0); // 1 +var_dump(new Comparable(1) <=> 1); // 0 +var_dump(new Comparable(1) <=> '1'); // 0 +var_dump(new Comparable(1) <=> 1.0); // 0 +var_dump(new Comparable(1) <=> 1.5); // -1 + +/** + * We didn't define comparison against non-numeric strings. + */ +var_dump(new Comparable(1) <=> 'a'); + +?> +--EXPECTF-- +int(1) +int(0) +int(0) +int(0) +int(-1) + +Fatal error: Uncaught Exception: Failed to compare in %s:%d +Stack trace: +#0 %s: Comparable->__compareTo('a') +#1 {main} + thrown in %s on line %d diff --git a/Zend/tests/comparisons/__equals/Equatable.inc b/Zend/tests/comparisons/__equals/Equatable.inc new file mode 100644 index 0000000000000..3c75f3cb0b53b --- /dev/null +++ b/Zend/tests/comparisons/__equals/Equatable.inc @@ -0,0 +1,28 @@ +decoy = $value * -1; + $this->value = $value; + } + + public function __equals($other): bool { + if ($other instanceof self) { + return $this->value == $other->value; + } + + if (is_numeric($other)) { + return $this->value == $other; + } + + throw new Exception("Failed to equate"); + } +} diff --git a/Zend/tests/comparisons/__equals/equals-array-func-array-keys.phpt b/Zend/tests/comparisons/__equals/equals-array-func-array-keys.phpt new file mode 100644 index 0000000000000..ff5502299f7f1 --- /dev/null +++ b/Zend/tests/comparisons/__equals/equals-array-func-array-keys.phpt @@ -0,0 +1,121 @@ +--TEST-- +__equals: Called by array_keys which has equality semantics (searching) +--FILE-- + $a, + 'b' => $b, + 'c' => $c, +]; + +var_dump(array_keys($array, $a, $strict = false)); // a +var_dump(array_keys($array, $b, $strict = false)); // b +var_dump(array_keys($array, $c, $strict = false)); // c + +var_dump(array_keys($array, $a, $strict = true)); // a +var_dump(array_keys($array, $b, $strict = true)); // b +var_dump(array_keys($array, $c, $strict = true)); // c + +/* Found, because Equatable::__equals returned TRUE */ +var_dump(array_keys($array, new Equatable(1), $strict = false)); // a +var_dump(array_keys($array, new Equatable(2), $strict = false)); // b +var_dump(array_keys($array, new Equatable(3), $strict = false)); // c + +/* Not found because strict comparison doesn't call __equals */ +var_dump(array_keys($array, new Equatable(1), $strict = true)); +var_dump(array_keys($array, new Equatable(2), $strict = true)); +var_dump(array_keys($array, new Equatable(3), $strict = true)); + +/* Found, because Equatable::__equals returned TRUE */ +var_dump(array_keys($array, 1, $strict = false)); // a +var_dump(array_keys($array, 2, $strict = false)); // b +var_dump(array_keys($array, 3, $strict = false)); // c + +/* Not found because strict comparison doesn't call __equals */ +var_dump(array_keys($array, 1, $strict = true)); +var_dump(array_keys($array, 2, $strict = true)); +var_dump(array_keys($array, 3, $strict = true)); + +/* Not found */ +var_dump(array_keys($array, new Equatable(4), $strict = true)); +var_dump(array_keys($array, new Equatable(5), $strict = true)); + +var_dump(array_keys($array, new Equatable(4), $strict = false)); +var_dump(array_keys($array, new Equatable(5), $strict = false)); + +?> +--EXPECT-- +array(1) { + [0]=> + string(1) "a" +} +array(1) { + [0]=> + string(1) "b" +} +array(1) { + [0]=> + string(1) "c" +} +array(1) { + [0]=> + string(1) "a" +} +array(1) { + [0]=> + string(1) "b" +} +array(1) { + [0]=> + string(1) "c" +} +array(1) { + [0]=> + string(1) "a" +} +array(1) { + [0]=> + string(1) "b" +} +array(1) { + [0]=> + string(1) "c" +} +array(0) { +} +array(0) { +} +array(0) { +} +array(1) { + [0]=> + string(1) "a" +} +array(1) { + [0]=> + string(1) "b" +} +array(1) { + [0]=> + string(1) "c" +} +array(0) { +} +array(0) { +} +array(0) { +} +array(0) { +} +array(0) { +} +array(0) { +} +array(0) { +} diff --git a/Zend/tests/comparisons/__equals/equals-array-func-array-search.phpt b/Zend/tests/comparisons/__equals/equals-array-func-array-search.phpt new file mode 100644 index 0000000000000..731e71949e497 --- /dev/null +++ b/Zend/tests/comparisons/__equals/equals-array-func-array-search.phpt @@ -0,0 +1,71 @@ +--TEST-- +__equals: Called by array_search which has equality semantics (searching) +--FILE-- + +--EXPECT-- +int(0) +int(1) +int(2) +int(0) +int(1) +int(2) +int(0) +int(1) +int(2) +bool(false) +bool(false) +bool(false) +int(0) +int(1) +int(2) +bool(false) +bool(false) +bool(false) +bool(false) +bool(false) +bool(false) +bool(false) diff --git a/Zend/tests/comparisons/__equals/equals-array-func-in-array.phpt b/Zend/tests/comparisons/__equals/equals-array-func-in-array.phpt new file mode 100644 index 0000000000000..f1dc424d2eefa --- /dev/null +++ b/Zend/tests/comparisons/__equals/equals-array-func-in-array.phpt @@ -0,0 +1,71 @@ +--TEST-- +__equals: Called by in_array which has equality semantics (searching) +--FILE-- + +--EXPECT-- +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(false) +bool(false) +bool(false) +bool(true) +bool(true) +bool(true) +bool(false) +bool(false) +bool(false) +bool(false) +bool(false) +bool(false) +bool(false) diff --git a/Zend/tests/comparisons/__equals/equals-array-sort.phpt b/Zend/tests/comparisons/__equals/equals-array-sort.phpt new file mode 100644 index 0000000000000..e0d3673d57704 --- /dev/null +++ b/Zend/tests/comparisons/__equals/equals-array-sort.phpt @@ -0,0 +1,42 @@ +--TEST-- +__equals: Not called by array functions with ordering semantics (sorting) +--FILE-- + +--EXPECT-- +object(Equatable)#3 (2) { + ["decoy":protected]=> + int(-3) + ["value":protected]=> + int(3) +} +object(Equatable)#2 (2) { + ["decoy":protected]=> + int(-2) + ["value":protected]=> + int(2) +} +object(Equatable)#1 (2) { + ["decoy":protected]=> + int(-1) + ["value":protected]=> + int(1) +} diff --git a/Zend/tests/comparisons/__equals/equals-compare-objects-handler.phpt b/Zend/tests/comparisons/__equals/equals-compare-objects-handler.phpt new file mode 100644 index 0000000000000..fdc8de36a582a --- /dev/null +++ b/Zend/tests/comparisons/__equals/equals-compare-objects-handler.phpt @@ -0,0 +1,41 @@ +--TEST-- +__equals: Objects implementing compare_objects handler are unaffected +--SKIPIF-- + +--FILE-- + +--EXPECT-- +bool(true) +bool(true) +bool(false) +bool(true) +bool(true) +bool(false) +bool(false) +bool(false) +bool(true) + diff --git a/Zend/tests/comparisons/__equals/equals-disallow-compare.phpt b/Zend/tests/comparisons/__equals/equals-disallow-compare.phpt new file mode 100644 index 0000000000000..0cc7771ec7e12 --- /dev/null +++ b/Zend/tests/comparisons/__equals/equals-disallow-compare.phpt @@ -0,0 +1,50 @@ +--TEST-- +__equals: Disallow comparison with an exception, but allow equality checks +--FILE-- +value = $value; + } + + public function __compareTo($other) + { + throw new Exception("This object does not support comparison"); + } + + public function __equals($other) + { + return $other instanceof self && $this->value === $other->value; + } +} + +/** + * This should call __equals which returns FALSE. + */ +var_dump(new Test(1) == new Test(2)); + +/** + * This should call __equals which returns TRUE. + */ +var_dump(new Test(2) == new Test(2)); + +/** + * This should call __compareTo which is explicitly unsupported. + */ +new Test(1) <=> new Test(1); + +?> +--EXPECTF-- +bool(false) +bool(true) + +Fatal error: Uncaught Exception: %s:%d +Stack trace: +#0 %s: Test->__compareTo(Object(Test)) +#1 {main} + thrown in %s on line %d diff --git a/Zend/tests/comparisons/__equals/equals-inheritance.phpt b/Zend/tests/comparisons/__equals/equals-inheritance.phpt new file mode 100644 index 0000000000000..ae5861cada11e --- /dev/null +++ b/Zend/tests/comparisons/__equals/equals-inheritance.phpt @@ -0,0 +1,78 @@ +--TEST-- +__equals: Inheritance of magic method +--FILE-- + +--EXPECT-- +bool(false) +bool(true) +bool(false) + +bool(false) +bool(true) +bool(false) + +bool(false) +bool(true) +bool(false) + +Reverse! +bool(true) +Reverse! +bool(false) +Reverse! +bool(true) diff --git a/Zend/tests/comparisons/equals-public-not-static.phpt b/Zend/tests/comparisons/__equals/equals-method-header.phpt similarity index 80% rename from Zend/tests/comparisons/equals-public-not-static.phpt rename to Zend/tests/comparisons/__equals/equals-method-header.phpt index 45dacb786a14e..f640035ec2fd8 100644 --- a/Zend/tests/comparisons/equals-public-not-static.phpt +++ b/Zend/tests/comparisons/__equals/equals-method-header.phpt @@ -15,6 +15,14 @@ class C { static public function __equals() {} } +class D { + function __equals() {} // This is okay. +} + +class E { + public function __equals(E $value): bool {} // This is okay. +} + ?> --EXPECTF-- Warning: The magic method __equals() must have public visibility and cannot be static in %s on line %d diff --git a/Zend/tests/comparisons/__equals/equals-normalize-to-bool.phpt b/Zend/tests/comparisons/__equals/equals-normalize-to-bool.phpt new file mode 100644 index 0000000000000..0184a2adfb3de --- /dev/null +++ b/Zend/tests/comparisons/__equals/equals-normalize-to-bool.phpt @@ -0,0 +1,26 @@ +--TEST-- +__equals: Converts to boolean on return +--FILE-- + +--EXPECT-- +bool(false) +bool(false) +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +bool(false) +bool(false) diff --git a/Zend/tests/comparisons/__equals/equals-not-called-for-ordering.phpt b/Zend/tests/comparisons/__equals/equals-not-called-for-ordering.phpt new file mode 100644 index 0000000000000..846e672412830 --- /dev/null +++ b/Zend/tests/comparisons/__equals/equals-not-called-for-ordering.phpt @@ -0,0 +1,19 @@ +--TEST-- +__equals: Should not be called when comparing for ordering +--FILE-- + new A; +new A >= new A; +new A <= new A; +new A <=> new A; +?> +--EXPECT-- diff --git a/Zend/tests/comparisons/equals-basic.phpt b/Zend/tests/comparisons/__equals/equals-operators.phpt similarity index 96% rename from Zend/tests/comparisons/equals-basic.phpt rename to Zend/tests/comparisons/__equals/equals-operators.phpt index 9472bacc24e06..bde6c33eae286 100644 --- a/Zend/tests/comparisons/equals-basic.phpt +++ b/Zend/tests/comparisons/__equals/equals-operators.phpt @@ -1,8 +1,8 @@ --TEST-- -__equals: Basic equality behaviour +__equals: Basic equality operator behaviour --FILE-- +--EXPECT-- +Comparing! +bool(true) +Comparing! +bool(true) diff --git a/Zend/tests/comparisons/__equals/equals-throw-exception.phpt b/Zend/tests/comparisons/__equals/equals-throw-exception.phpt new file mode 100644 index 0000000000000..0a451ad174ffc --- /dev/null +++ b/Zend/tests/comparisons/__equals/equals-throw-exception.phpt @@ -0,0 +1,26 @@ +--TEST-- +__equals: Throwing exception is caught +--FILE-- + +--EXPECTF-- + +Fatal error: Uncaught Exception in %s:%d +Stack trace: +#0 %s: BrokenEquatable->__equals(Object(BrokenEquatable)) +#1 {main} + thrown in %s on line %d diff --git a/Zend/tests/comparisons/__equals/equals-to-null.phpt b/Zend/tests/comparisons/__equals/equals-to-null.phpt new file mode 100644 index 0000000000000..10dd79837e3ea --- /dev/null +++ b/Zend/tests/comparisons/__equals/equals-to-null.phpt @@ -0,0 +1,62 @@ +--TEST-- +__equals: Compare against NULL +--FILE-- +value = $value; + } + + public function __equals($other) + { + echo "Comparing!\n"; + return $this->value == $other; + } +} + +/** + * This should call __equals. + */ +var_dump(new Equatable(1) == null); // false + +/** + * This should also call __equals even though we're using the RHS + */ +var_dump(null == new Equatable(1)); // false + +/** + * We're doing a non-strict comparison between 0 and NULL here. + */ +var_dump(null == new Equatable(0)); // true +var_dump(new Equatable(0) == null); // true + +echo "\n"; + +/** + * Check that default behaviour still works as expected. + */ +var_dump(new stdClass == null); +var_dump(new stdClass != null); +var_dump(null == new stdClass); +var_dump(null != new stdClass); + +?> +--EXPECT-- +Comparing! +bool(false) +Comparing! +bool(false) +Comparing! +bool(true) +Comparing! +bool(true) + +bool(false) +bool(true) +bool(false) +bool(true) diff --git a/Zend/tests/comparisons/__equals/equals-to-scalar.phpt b/Zend/tests/comparisons/__equals/equals-to-scalar.phpt new file mode 100644 index 0000000000000..13ccab65901f7 --- /dev/null +++ b/Zend/tests/comparisons/__equals/equals-to-scalar.phpt @@ -0,0 +1,33 @@ +--TEST-- +__equals: Compare against scalar values +--FILE-- + +--EXPECTF-- +bool(false) +bool(true) +bool(true) +bool(true) +bool(false) + +Fatal error: Uncaught Exception: Failed to equate in %s:%d +Stack trace: +#0 %s: Equatable->__equals('a') +#1 {main} + thrown in %s on line %d diff --git a/Zend/tests/comparisons/compare-in-array.phpt b/Zend/tests/comparisons/compare-in-array.phpt deleted file mode 100644 index 5810e1548ebd1..0000000000000 --- a/Zend/tests/comparisons/compare-in-array.phpt +++ /dev/null @@ -1,42 +0,0 @@ ---TEST-- -__compareTo: Supported by array functions with equality semantics (searching) ---FILE-- - ---EXPECT-- -bool(true) -bool(true) -bool(true) -bool(true) -bool(true) -bool(true) -bool(true) -bool(true) -bool(true) -bool(false) -bool(false) diff --git a/Zend/tests/comparisons/compare-normalize.phpt b/Zend/tests/comparisons/compare-normalize.phpt deleted file mode 100644 index fdf10760dedb5..0000000000000 --- a/Zend/tests/comparisons/compare-normalize.phpt +++ /dev/null @@ -1,38 +0,0 @@ ---TEST-- -__compareTo: Comparison always returns -1, 0, or 1 ---FILE-- - 1); -var_dump(new B <=> 2); -var_dump(new C <=> 3); - -?> ---EXPECT-- -int(1) -int(-1) -int(0) diff --git a/Zend/tests/comparisons/compare-return-false.phpt b/Zend/tests/comparisons/compare-return-false.phpt deleted file mode 100644 index 8165bf9882fb8..0000000000000 --- a/Zend/tests/comparisons/compare-return-false.phpt +++ /dev/null @@ -1,18 +0,0 @@ ---TEST-- -__compareTo: Returning false should be converted to integer 0 ---FILE-- - new A); - -?> ---EXPECT-- -int(0) diff --git a/Zend/tests/comparisons/compare-return-float.phpt b/Zend/tests/comparisons/compare-return-float.phpt deleted file mode 100644 index 81bfe0bab1109..0000000000000 --- a/Zend/tests/comparisons/compare-return-float.phpt +++ /dev/null @@ -1,38 +0,0 @@ ---TEST-- -__compareTo: Returning a float should not be truncated to integer ---FILE-- - 1); -var_dump(new B <=> 2); -var_dump(new C <=> 3); - -?> ---EXPECT-- -int(1) -int(-1) -int(0) diff --git a/Zend/tests/comparisons/compare-return-object.phpt b/Zend/tests/comparisons/compare-return-object.phpt deleted file mode 100644 index fb3e0f3502672..0000000000000 --- a/Zend/tests/comparisons/compare-return-object.phpt +++ /dev/null @@ -1,19 +0,0 @@ ---TEST-- -__compareTo: Returning an object should be converted to int ---FILE-- - new A); - -?> ---EXPECTF-- -Notice: Object of class A could not be converted to int in %s on line %d -int(1) diff --git a/Zend/tests/comparisons/compare-return-true.phpt b/Zend/tests/comparisons/compare-return-true.phpt deleted file mode 100644 index 5e142ab025ec0..0000000000000 --- a/Zend/tests/comparisons/compare-return-true.phpt +++ /dev/null @@ -1,18 +0,0 @@ ---TEST-- -__compareTo: Returning true should be converted to integer 1 ---FILE-- - new A); - -?> ---EXPECT-- -int(1) diff --git a/Zend/tests/comparisons/compare-to-scalar.phpt b/Zend/tests/comparisons/compare-to-scalar.phpt deleted file mode 100644 index a57760402c061..0000000000000 --- a/Zend/tests/comparisons/compare-to-scalar.phpt +++ /dev/null @@ -1,30 +0,0 @@ ---TEST-- -__compareTo: Compare against scalar values ---FILE-- - 0); -var_dump(new Comparable(1) <=> 1.0); -var_dump(new Comparable(1) <=> 1.5); - -/** - * We didn't implement something that compares against strings, so __compareTo - * will return null, which will be converted to 0, and therefore equal. - */ -var_dump(new Comparable(1) <=> 'a'); -var_dump(new Comparable(1) <=> 'b'); -var_dump(new Comparable(1) <=> 'c'); - - -?> ---EXPECT-- -int(1) -int(0) -int(-1) -int(0) -int(0) -int(0) diff --git a/Zend/tests/comparisons/equals-disallow-compare.phpt b/Zend/tests/comparisons/equals-disallow-compare.phpt deleted file mode 100644 index 4a7779bad1a9e..0000000000000 --- a/Zend/tests/comparisons/equals-disallow-compare.phpt +++ /dev/null @@ -1,84 +0,0 @@ ---TEST-- -__equals: Disallow comparison, but allow equality checks ---FILE-- -value = $value; - } - - public function __compareTo($other) - { - throw new Exception("This object does not support comparison"); - } - - public function __equals($other) - { - return $other instanceof self && $this->value === $other->value; - } -} - -/** - * This should call __equals which returns FALSE. - */ -var_dump(new Comparable(1) == new Comparable(2)); - -/** - * This should call __equals which returns TRUE. - */ -var_dump(new Comparable(2) == new Comparable(2)); - -/** - * These should call __compareTo which is explicitly unsupported. - */ -try { - new Comparable(2) <= new Comparable(2); -} catch (Exception $e) { - var_dump($e->getMessage()); -} - -try { - new Comparable(1) > new Comparable(1); -} catch (Exception $e) { - var_dump($e->getMessage()); -} - -try { - new Comparable(1) < new Comparable(1); -} catch (Exception $e) { - var_dump($e->getMessage()); -} - -try { - new Comparable(1) >= new Comparable(1); -} catch (Exception $e) { - var_dump($e->getMessage()); -} - -try { - new Comparable(1) <= new Comparable(1); -} catch (Exception $e) { - var_dump($e->getMessage()); -} - -try { - new Comparable(1) <=> new Comparable(1); -} catch (Exception $e) { - var_dump($e->getMessage()); -} - -?> ---EXPECT-- -bool(false) -bool(true) -string(39) "This object does not support comparison" -string(39) "This object does not support comparison" -string(39) "This object does not support comparison" -string(39) "This object does not support comparison" -string(39) "This object does not support comparison" -string(39) "This object does not support comparison" diff --git a/Zend/tests/comparisons/equals-in-array.phpt b/Zend/tests/comparisons/equals-in-array.phpt deleted file mode 100644 index c7ee91760f779..0000000000000 --- a/Zend/tests/comparisons/equals-in-array.phpt +++ /dev/null @@ -1,35 +0,0 @@ ---TEST-- -__equals: Supported by array functions with equality semantics (searching) ---FILE-- - ---EXPECT-- -bool(true) -bool(true) -bool(true) -bool(false) -bool(false) -bool(false) -bool(false) -bool(false) diff --git a/Zend/tests/comparisons/equals-not-called-for-ordering.phpt b/Zend/tests/comparisons/equals-not-called-for-ordering.phpt deleted file mode 100644 index 77afe32c9a8de..0000000000000 --- a/Zend/tests/comparisons/equals-not-called-for-ordering.phpt +++ /dev/null @@ -1,19 +0,0 @@ ---TEST-- -__equals: __equals should not be called when comparing for ordering ---FILE-- - new Equatable; -new Equatable >= new Equatable; -new Equatable <= new Equatable; -new Equatable <=> new Equatable; -?> ---EXPECT-- diff --git a/Zend/tests/comparisons/legacy-compare-objects.phpt b/Zend/tests/comparisons/legacy-compare-objects.phpt deleted file mode 100644 index 7a9a65d82f3a4..0000000000000 --- a/Zend/tests/comparisons/legacy-compare-objects.phpt +++ /dev/null @@ -1,27 +0,0 @@ ---TEST-- -__compareTo: Objects implementing compare_objects handler are unaffected ---SKIPIF-- - ---FILE-- - $a); -var_dump($a <=> $b); -var_dump($b <=> $a); -var_dump($b <=> $b); - -?> ---EXPECT-- -int(0) -int(-1) -int(1) -int(0) diff --git a/Zend/tests/comparisons/legacy-compare-to-null.phpt b/Zend/tests/comparisons/legacy-compare-to-null.phpt deleted file mode 100644 index 981589e63ece3..0000000000000 --- a/Zend/tests/comparisons/legacy-compare-to-null.phpt +++ /dev/null @@ -1,39 +0,0 @@ ---TEST-- -__compareTo: Check that comparing to NULL is not broken for existing objects ---FILE-- -= null); -var_dump(new stdClass <= null); -var_dump(new stdClass < null); -var_dump(new stdClass > null); -var_dump(new stdClass <=> null); - -echo "\n"; - -var_dump(null == new stdClass); -var_dump(null >= new stdClass); -var_dump(null <= new stdClass); -var_dump(null < new stdClass); -var_dump(null > new stdClass); -var_dump(null <=> new stdClass); - -?> ---EXPECT-- -bool(false) -bool(true) -bool(false) -bool(false) -bool(true) -int(1) - -bool(false) -bool(false) -bool(true) -bool(true) -bool(false) -int(-1) diff --git a/Zend/tests/comparisons/legacy-strict-comparison.phpt b/Zend/tests/comparisons/legacy-strict-comparison.phpt deleted file mode 100644 index 2b5176b790a39..0000000000000 --- a/Zend/tests/comparisons/legacy-strict-comparison.phpt +++ /dev/null @@ -1,23 +0,0 @@ ---TEST-- -__compareTo: Check that === isn't affected by __compareTo ---FILE-- - ---EXPECT-- -bool(true) -bool(false) From e79ef083fa8e1c9a303bcf7ee794658c1040a124 Mon Sep 17 00:00:00 2001 From: Rudi Theunissen Date: Thu, 28 Jun 2018 12:54:51 -0400 Subject: [PATCH 35/40] Move existing comparison tests into their own directory --- Zend/tests/{ => comparisons}/compare_001.phpt | 0 Zend/tests/{ => comparisons}/compare_001_64bit.phpt | 0 Zend/tests/{ => comparisons}/compare_002.phpt | 0 Zend/tests/{ => comparisons}/compare_002_64bit.phpt | 0 Zend/tests/{ => comparisons}/compare_003.phpt | 0 Zend/tests/{ => comparisons}/compare_003_64bit.phpt | 0 Zend/tests/{ => comparisons}/compare_004.phpt | 0 Zend/tests/{ => comparisons}/compare_004_64bit.phpt | 0 Zend/tests/{ => comparisons}/compare_005.phpt | 0 Zend/tests/{ => comparisons}/compare_005_64bit.phpt | 0 Zend/tests/{ => comparisons}/compare_006.phpt | 0 Zend/tests/{ => comparisons}/compare_006_64bit.phpt | 0 12 files changed, 0 insertions(+), 0 deletions(-) rename Zend/tests/{ => comparisons}/compare_001.phpt (100%) rename Zend/tests/{ => comparisons}/compare_001_64bit.phpt (100%) rename Zend/tests/{ => comparisons}/compare_002.phpt (100%) rename Zend/tests/{ => comparisons}/compare_002_64bit.phpt (100%) rename Zend/tests/{ => comparisons}/compare_003.phpt (100%) rename Zend/tests/{ => comparisons}/compare_003_64bit.phpt (100%) rename Zend/tests/{ => comparisons}/compare_004.phpt (100%) rename Zend/tests/{ => comparisons}/compare_004_64bit.phpt (100%) rename Zend/tests/{ => comparisons}/compare_005.phpt (100%) rename Zend/tests/{ => comparisons}/compare_005_64bit.phpt (100%) rename Zend/tests/{ => comparisons}/compare_006.phpt (100%) rename Zend/tests/{ => comparisons}/compare_006_64bit.phpt (100%) diff --git a/Zend/tests/compare_001.phpt b/Zend/tests/comparisons/compare_001.phpt similarity index 100% rename from Zend/tests/compare_001.phpt rename to Zend/tests/comparisons/compare_001.phpt diff --git a/Zend/tests/compare_001_64bit.phpt b/Zend/tests/comparisons/compare_001_64bit.phpt similarity index 100% rename from Zend/tests/compare_001_64bit.phpt rename to Zend/tests/comparisons/compare_001_64bit.phpt diff --git a/Zend/tests/compare_002.phpt b/Zend/tests/comparisons/compare_002.phpt similarity index 100% rename from Zend/tests/compare_002.phpt rename to Zend/tests/comparisons/compare_002.phpt diff --git a/Zend/tests/compare_002_64bit.phpt b/Zend/tests/comparisons/compare_002_64bit.phpt similarity index 100% rename from Zend/tests/compare_002_64bit.phpt rename to Zend/tests/comparisons/compare_002_64bit.phpt diff --git a/Zend/tests/compare_003.phpt b/Zend/tests/comparisons/compare_003.phpt similarity index 100% rename from Zend/tests/compare_003.phpt rename to Zend/tests/comparisons/compare_003.phpt diff --git a/Zend/tests/compare_003_64bit.phpt b/Zend/tests/comparisons/compare_003_64bit.phpt similarity index 100% rename from Zend/tests/compare_003_64bit.phpt rename to Zend/tests/comparisons/compare_003_64bit.phpt diff --git a/Zend/tests/compare_004.phpt b/Zend/tests/comparisons/compare_004.phpt similarity index 100% rename from Zend/tests/compare_004.phpt rename to Zend/tests/comparisons/compare_004.phpt diff --git a/Zend/tests/compare_004_64bit.phpt b/Zend/tests/comparisons/compare_004_64bit.phpt similarity index 100% rename from Zend/tests/compare_004_64bit.phpt rename to Zend/tests/comparisons/compare_004_64bit.phpt diff --git a/Zend/tests/compare_005.phpt b/Zend/tests/comparisons/compare_005.phpt similarity index 100% rename from Zend/tests/compare_005.phpt rename to Zend/tests/comparisons/compare_005.phpt diff --git a/Zend/tests/compare_005_64bit.phpt b/Zend/tests/comparisons/compare_005_64bit.phpt similarity index 100% rename from Zend/tests/compare_005_64bit.phpt rename to Zend/tests/comparisons/compare_005_64bit.phpt diff --git a/Zend/tests/compare_006.phpt b/Zend/tests/comparisons/compare_006.phpt similarity index 100% rename from Zend/tests/compare_006.phpt rename to Zend/tests/comparisons/compare_006.phpt diff --git a/Zend/tests/compare_006_64bit.phpt b/Zend/tests/comparisons/compare_006_64bit.phpt similarity index 100% rename from Zend/tests/compare_006_64bit.phpt rename to Zend/tests/comparisons/compare_006_64bit.phpt From 399febc3a09a9b56063341ec231d588be4a22de3 Mon Sep 17 00:00:00 2001 From: Rudi Theunissen Date: Sun, 1 Jul 2018 22:01:58 -0400 Subject: [PATCH 36/40] Minor changes, comments --- Zend/zend_operators.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Zend/zend_operators.c b/Zend/zend_operators.c index 35a46199f5034..27ebf59672006 100644 --- a/Zend/zend_operators.c +++ b/Zend/zend_operators.c @@ -1959,7 +1959,6 @@ static void ZEND_FASTCALL convert_compare_result_to_long(zval *result) /* {{{ */ ZEND_API int ZEND_FASTCALL compare_function(zval *result, zval *op1, zval *op2) /* {{{ */ { - int ret; int converted = 0; zval op1_copy, op2_copy; @@ -2058,6 +2057,7 @@ ZEND_API int ZEND_FASTCALL compare_function(zval *result, zval *op1, zval *op2) continue; } + /* compare handlers fall through if not successful */ if (Z_TYPE_P(op1) == IS_OBJECT) { if (Z_OBJ_HANDLER_P(op1, compare) && Z_OBJ_HANDLER_P(op1, compare)(result, op1, op2) == SUCCESS) { convert_compare_result_to_long(result); @@ -2264,8 +2264,6 @@ ZEND_API int ZEND_FASTCALL is_not_equal_function(zval *result, zval *op1, zval * if (is_equal_function(result, op1, op2) == FAILURE) { return FAILURE; } - - /* */ ZVAL_BOOL(result, Z_TYPE_P(result) == IS_TRUE ? 0 : 1); return SUCCESS; } From 0f6755beecd2ff4581e56f0fe92b6d47f057988a Mon Sep 17 00:00:00 2001 From: Rudi Theunissen Date: Mon, 2 Jul 2018 10:55:39 -0400 Subject: [PATCH 37/40] Added draft upgrading and news additions --- NEWS | 2 ++ UPGRADING | 24 ++++++++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/NEWS b/NEWS index a7c1d4ef90593..184a787ea6598 100644 --- a/NEWS +++ b/NEWS @@ -22,6 +22,8 @@ PDO_Firebird: logging. (Philip Prindeville) . Fixed bug #63217 (Constant numeric strings become integers when used as ArrayAccess offset). (Rudi Theunissen, Dmitry) + . Added user-defined object comparison. + (https://wiki.php.net/rfc/object-comparison) (Rudi Theunissen) - DOM: . Fixed bug #76285 (DOMDocument::formatOutput attribute sometimes ignored). diff --git a/UPGRADING b/UPGRADING index f616bf9cda308..fef4dcbba82d3 100644 --- a/UPGRADING +++ b/UPGRADING @@ -247,6 +247,30 @@ Core: are available under Linux, FreeBSD, Windows, Mac, SunOS, AIX and their derivatives. If no required timers are provided by a corresponding platform, the function returns false. + . Added two new magic methods: __compareTo and __equals. Classes may choose to + implement these to define relative natural ordering and equality, which + affects functions like in_array() and sort(), and comparison operators: + <, >, <=, >=, <=>, ==, !=. The behaviour of === and !== has not changed. + Classes that do not implement these methods will continue to be compared + according to the rules that were introduced in PHP 5. + (RFC: https://wiki.php.net/rfc/object-comparison) + + + +Changes to object comparison behaviour ++-------------------------------------- ++ ++* Classes may now implement a Comparable interface to override how objects are ++ compared to other values. If implemented, the class must implement a public ++ compareTo() method with the signature: ++ ++ public function compareTo($other) ++ ++ This method must return a negative integer to signal that the object is less ++ than $other, zero to signal that the object is equal to $other, or a positive ++ integer to signal that the object is greater than $other. ++ ++ Objects that don't implement Comparable will continue to be compared ++ according to the rules used in PHP 5. Date: . Added the DateTime::createFromImmutable() method, which mirrors From be33ca6e6da9402ba3170e34eace17f8a4e76bc3 Mon Sep 17 00:00:00 2001 From: Rudi Theunissen Date: Wed, 4 Jul 2018 14:11:12 -0400 Subject: [PATCH 38/40] Remove some blank lines --- Zend/zend_object_handlers.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Zend/zend_object_handlers.c b/Zend/zend_object_handlers.c index faf3a4f384ff9..bfaff45894a28 100644 --- a/Zend/zend_object_handlers.c +++ b/Zend/zend_object_handlers.c @@ -1555,7 +1555,7 @@ ZEND_API int zend_std_equals(zval *result, zval *op1, zval *op2) /* {{{ */ { if (Z_TYPE_P(op1) == IS_OBJECT) { zend_class_entry *ce = Z_OBJCE_P(op1); - + if (ce->__equals) { zend_call_method_with_1_params(op1, ce, &ce->__equals, ZEND_EQUALS_FUNC_NAME, result, op2); @@ -1564,7 +1564,6 @@ ZEND_API int zend_std_equals(zval *result, zval *op1, zval *op2) /* {{{ */ } } } - return FAILURE; } /* }}} */ @@ -1582,7 +1581,6 @@ ZEND_API int zend_std_compare(zval *result, zval *op1, zval *op2) /* {{{ */ } } } - return FAILURE; } /* }}} */ From 7bbae20fe25646687efd3346942bc6509a857cec Mon Sep 17 00:00:00 2001 From: Rudi Theunissen Date: Wed, 4 Jul 2018 14:11:44 -0400 Subject: [PATCH 39/40] Add small regression tests for objects of different classes --- Zend/tests/comparisons/compare_007.phpt | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 Zend/tests/comparisons/compare_007.phpt diff --git a/Zend/tests/comparisons/compare_007.phpt b/Zend/tests/comparisons/compare_007.phpt new file mode 100644 index 0000000000000..991feeba4f087 --- /dev/null +++ b/Zend/tests/comparisons/compare_007.phpt @@ -0,0 +1,24 @@ +--TEST-- +comparing objects of different types +--FILE-- + new DateTime()); // false +var_dump(new stdClass() == new DateTime()); // false + +var_dump(new DateTime() < new stdClass()); // false +var_dump(new DateTime() > new stdClass()); // false +var_dump(new DateTime() == new stdClass()); // false + +var_dump(new stdClass() <=> new DateTime()); // 1 +var_dump(new DateTime() <=> new stdClass()); // 1 +?> +--EXPECTF-- +bool(false) +bool(false) +bool(false) +bool(false) +bool(false) +bool(false) +int(1) +int(1) From 6ca0304cbdf518975a53c2ddc6f88f582b753328 Mon Sep 17 00:00:00 2001 From: Rudi Theunissen Date: Sat, 7 Jul 2018 02:08:05 -0400 Subject: [PATCH 40/40] Change Z_TYPE_P call to Z_ISUNDF_P --- Zend/zend_object_handlers.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Zend/zend_object_handlers.c b/Zend/zend_object_handlers.c index bfaff45894a28..45b39de48e0e3 100644 --- a/Zend/zend_object_handlers.c +++ b/Zend/zend_object_handlers.c @@ -1559,7 +1559,7 @@ ZEND_API int zend_std_equals(zval *result, zval *op1, zval *op2) /* {{{ */ if (ce->__equals) { zend_call_method_with_1_params(op1, ce, &ce->__equals, ZEND_EQUALS_FUNC_NAME, result, op2); - if (Z_TYPE_P(result) != IS_UNDEF) { + if (!Z_ISUNDEF_P(result)) { return SUCCESS; } } @@ -1576,7 +1576,7 @@ ZEND_API int zend_std_compare(zval *result, zval *op1, zval *op2) /* {{{ */ if (ce->__compareTo) { zend_call_method_with_1_params(op1, ce, &ce->__compareTo, ZEND_COMPARETO_FUNC_NAME, result, op2); - if (Z_TYPE_P(result) != IS_UNDEF) { + if (!Z_ISUNDEF_P(result)) { return SUCCESS; } }