From d600213b393de55d4b62752b3ea7ab33a99e72a0 Mon Sep 17 00:00:00 2001 From: Joe Watkins Date: Thu, 8 Jul 2021 21:52:01 +0200 Subject: [PATCH 1/9] fake closure comparison --- Zend/tests/closure_compare.phpt | 106 ++++++++++++++++++++++++++++++++ Zend/zend_closures.c | 40 +++++++++++- 2 files changed, 145 insertions(+), 1 deletion(-) create mode 100644 Zend/tests/closure_compare.phpt diff --git a/Zend/tests/closure_compare.phpt b/Zend/tests/closure_compare.phpt new file mode 100644 index 0000000000000..fc4c64dd7edde --- /dev/null +++ b/Zend/tests/closure_compare.phpt @@ -0,0 +1,106 @@ +--TEST-- +Closure comparison +--FILE-- +bindTo(new Foo); + +printf("foo#0::exists != foo#1::exists: %s\n", $closures[0] != $closures[1] ? "OK" : "FAIL"); + +$baz = new Baz; + +$closures[0] = Closure::fromCallable([$foo, "traitMethod"]); +$closures[1] = Closure::fromCallable([$baz, "traitMethod"]); + +printf("foo::traitMethod != baz::traitMethod: %s\n", $closures[0] != $closures[1] ? "OK" : "FAIL"); + +$closures[0] = Closure::fromCallable([$foo, "exists"]); +$closures[1] = Closure::fromCallable([$foo, "exists"]); + +printf("foo::exists == foo::exists: %s\n", $closures[0] == $closures[1] ? "OK" : "FAIL"); + +$closures[0] = Closure::fromCallable([$foo, "method"]); +$closures[1] = Closure::fromCallable([$foo, "method"]); + +printf("foo::method == foo::method: %s\n", $closures[0] == $closures[1] ? "OK" : "FAIL"); + +$closures[1] = $closures[1]->bindTo(new Bar); + +printf("foo::method != bar::method: %s\n", $closures[0] != $closures[1] ? "OK" : "FAIL"); + +$closures[0] = Closure::fromCallable([$foo, "method"]); +$closures[1] = Closure::fromCallable([$foo, "method2"]); + +printf("foo::method != foo::method2: %s\n", $closures[0] != $closures[1] ? "OK" : "FAIL"); + +$closures[2] = Closure::fromCallable([$closures[0], "__invoke"]); +$closures[3] = Closure::fromCallable([$closures[1], "__invoke"]); + +printf("Closure[0]::invoke != Closure[1]::invoke: %s\n", $closures[2] != $closures[3] ? "OK" : "FAIL"); + +$closures[2] = Closure::fromCallable([$closures[0], "__invoke"]); +$closures[3] = Closure::fromCallable([$closures[0], "__invoke"]); + +printf("Closure[0]::invoke == Closure[0]::invoke: %s\n", $closures[2] == $closures[3] ? "OK" : "FAIL"); +?> +--EXPECT-- +foo == foo: OK +strlen == strlen: OK +strlen != strrev: OK +foo::existsStatic != bar::existsStatic: OK +foo#0::exists != foo#1::exists: OK +foo::traitMethod != baz::traitMethod: OK +foo::exists == foo::exists: OK +foo::method == foo::method: OK +foo::method != bar::method: OK +foo::method != foo::method2: OK +Closure[0]::invoke != Closure[1]::invoke: OK +Closure[0]::invoke == Closure[0]::invoke: OK diff --git a/Zend/zend_closures.c b/Zend/zend_closures.c index 5ae122b7f94ab..08e1f40ac21bd 100644 --- a/Zend/zend_closures.c +++ b/Zend/zend_closures.c @@ -381,7 +381,45 @@ static ZEND_COLD zend_function *zend_closure_get_constructor(zend_object *object static int zend_closure_compare(zval *o1, zval *o2) /* {{{ */ { ZEND_COMPARE_OBJECTS_FALLBACK(o1, o2); - return Z_OBJ_P(o1) != Z_OBJ_P(o2); + + zend_closure *lhs = (zend_closure*) Z_OBJ_P(o1); + zend_closure *rhs = (zend_closure*) Z_OBJ_P(o2); + + if (lhs == rhs) { + return SUCCESS; + } + + if (!((lhs->func.common.fn_flags & ZEND_ACC_FAKE_CLOSURE) && (rhs->func.common.fn_flags & ZEND_ACC_FAKE_CLOSURE))) { + return ZEND_UNCOMPARABLE; + } + + if (Z_TYPE(lhs->this_ptr) == IS_OBJECT && Z_TYPE(rhs->this_ptr) == IS_OBJECT) { + if (Z_OBJ(lhs->this_ptr) != Z_OBJ(rhs->this_ptr)) { + return ZEND_UNCOMPARABLE; + } + } + + if (lhs->called_scope != rhs->called_scope) { + return ZEND_UNCOMPARABLE; + } + + if (lhs->func.type != rhs->func.type) { + return ZEND_UNCOMPARABLE; + } + + if (lhs->func.common.scope != rhs->func.common.scope) { + return ZEND_UNCOMPARABLE; + } + + if (lhs->func.type == ZEND_USER_FUNCTION) { + return lhs->func.op_array.opcodes != rhs->func.op_array.opcodes; + } + + if (!zend_string_equals(lhs->func.common.function_name, rhs->func.common.function_name)) { + return ZEND_UNCOMPARABLE; + } + + return SUCCESS; } /* }}} */ From 26b3ee27611dff0062831dc1e77315bcc9d3acea Mon Sep 17 00:00:00 2001 From: Joe Watkins Date: Fri, 9 Jul 2021 09:54:09 +0200 Subject: [PATCH 2/9] drop equality check --- Zend/zend_closures.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/Zend/zend_closures.c b/Zend/zend_closures.c index 08e1f40ac21bd..3f7bf2c82d977 100644 --- a/Zend/zend_closures.c +++ b/Zend/zend_closures.c @@ -385,10 +385,6 @@ static int zend_closure_compare(zval *o1, zval *o2) /* {{{ */ zend_closure *lhs = (zend_closure*) Z_OBJ_P(o1); zend_closure *rhs = (zend_closure*) Z_OBJ_P(o2); - if (lhs == rhs) { - return SUCCESS; - } - if (!((lhs->func.common.fn_flags & ZEND_ACC_FAKE_CLOSURE) && (rhs->func.common.fn_flags & ZEND_ACC_FAKE_CLOSURE))) { return ZEND_UNCOMPARABLE; } From dc48bd905205c035d3fac2ad931660d2aca1fe91 Mon Sep 17 00:00:00 2001 From: Joe Watkins Date: Fri, 9 Jul 2021 09:58:38 +0200 Subject: [PATCH 3/9] drop object-ness check --- Zend/zend_closures.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/Zend/zend_closures.c b/Zend/zend_closures.c index 3f7bf2c82d977..353eaaa9f1a18 100644 --- a/Zend/zend_closures.c +++ b/Zend/zend_closures.c @@ -389,10 +389,8 @@ static int zend_closure_compare(zval *o1, zval *o2) /* {{{ */ return ZEND_UNCOMPARABLE; } - if (Z_TYPE(lhs->this_ptr) == IS_OBJECT && Z_TYPE(rhs->this_ptr) == IS_OBJECT) { - if (Z_OBJ(lhs->this_ptr) != Z_OBJ(rhs->this_ptr)) { - return ZEND_UNCOMPARABLE; - } + if (Z_OBJ(lhs->this_ptr) != Z_OBJ(rhs->this_ptr)) { + return ZEND_UNCOMPARABLE; } if (lhs->called_scope != rhs->called_scope) { From e33ebca5a8b0bbb5912886827861a2f6e8662ccd Mon Sep 17 00:00:00 2001 From: Joe Watkins Date: Fri, 9 Jul 2021 09:59:05 +0200 Subject: [PATCH 4/9] drop success --- Zend/zend_closures.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Zend/zend_closures.c b/Zend/zend_closures.c index 353eaaa9f1a18..0ca0c2920e628 100644 --- a/Zend/zend_closures.c +++ b/Zend/zend_closures.c @@ -413,7 +413,7 @@ static int zend_closure_compare(zval *o1, zval *o2) /* {{{ */ return ZEND_UNCOMPARABLE; } - return SUCCESS; + return 0; } /* }}} */ From abf6e9ad583dd6d6565dc2e8704e778693690058 Mon Sep 17 00:00:00 2001 From: Joe Watkins Date: Fri, 9 Jul 2021 10:05:39 +0200 Subject: [PATCH 5/9] drop opcode check, handle trait aliasing --- Zend/tests/closure_compare.phpt | 14 ++++++++++++++ Zend/zend_closures.c | 4 ---- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/Zend/tests/closure_compare.phpt b/Zend/tests/closure_compare.phpt index fc4c64dd7edde..967bbb8fb2f1f 100644 --- a/Zend/tests/closure_compare.phpt +++ b/Zend/tests/closure_compare.phpt @@ -25,8 +25,16 @@ trait MethodTrait { public function traitMethod(){} } +trait AliasTrait { + public function nonAliasMethod() {} +} + class Foo { use MethodTrait; + + use AliasTrait { + AliasTrait::nonAliasMethod as aliasMethod; + } public function __call($method, $args) { @@ -62,6 +70,11 @@ $closures[1] = Closure::fromCallable([$baz, "traitMethod"]); printf("foo::traitMethod != baz::traitMethod: %s\n", $closures[0] != $closures[1] ? "OK" : "FAIL"); +$closures[0] = Closure::fromCallable([$foo, "nonAliasMethod"]); +$closures[1] = Closure::fromCallable([$foo, "aliasMethod"]); + +printf("foo::aliasMethod != baz::nonAliasMethod: %s\n", $closures[0] != $closures[1] ? "OK" : "FAIL"); + $closures[0] = Closure::fromCallable([$foo, "exists"]); $closures[1] = Closure::fromCallable([$foo, "exists"]); @@ -98,6 +111,7 @@ strlen != strrev: OK foo::existsStatic != bar::existsStatic: OK foo#0::exists != foo#1::exists: OK foo::traitMethod != baz::traitMethod: OK +foo::aliasMethod != baz::nonAliasMethod: OK foo::exists == foo::exists: OK foo::method == foo::method: OK foo::method != bar::method: OK diff --git a/Zend/zend_closures.c b/Zend/zend_closures.c index 0ca0c2920e628..4111551672d0a 100644 --- a/Zend/zend_closures.c +++ b/Zend/zend_closures.c @@ -405,10 +405,6 @@ static int zend_closure_compare(zval *o1, zval *o2) /* {{{ */ return ZEND_UNCOMPARABLE; } - if (lhs->func.type == ZEND_USER_FUNCTION) { - return lhs->func.op_array.opcodes != rhs->func.op_array.opcodes; - } - if (!zend_string_equals(lhs->func.common.function_name, rhs->func.common.function_name)) { return ZEND_UNCOMPARABLE; } From 48d24ab5e3af00cedf9b1cc81dba174a854cb3c0 Mon Sep 17 00:00:00 2001 From: Joe Watkins Date: Fri, 9 Jul 2021 10:11:01 +0200 Subject: [PATCH 6/9] fix test --- Zend/tests/closure_compare.phpt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Zend/tests/closure_compare.phpt b/Zend/tests/closure_compare.phpt index 967bbb8fb2f1f..0fe06a16430b7 100644 --- a/Zend/tests/closure_compare.phpt +++ b/Zend/tests/closure_compare.phpt @@ -73,7 +73,7 @@ printf("foo::traitMethod != baz::traitMethod: %s\n", $closures[0] != $closures[1 $closures[0] = Closure::fromCallable([$foo, "nonAliasMethod"]); $closures[1] = Closure::fromCallable([$foo, "aliasMethod"]); -printf("foo::aliasMethod != baz::nonAliasMethod: %s\n", $closures[0] != $closures[1] ? "OK" : "FAIL"); +printf("foo::aliasMethod != foo::nonAliasMethod: %s\n", $closures[0] != $closures[1] ? "OK" : "FAIL"); $closures[0] = Closure::fromCallable([$foo, "exists"]); $closures[1] = Closure::fromCallable([$foo, "exists"]); From 67c0361ab9a60aa87339717fe4c26c34cbbcb976 Mon Sep 17 00:00:00 2001 From: Joe Watkins Date: Fri, 9 Jul 2021 10:17:04 +0200 Subject: [PATCH 7/9] fix test, fix this_ptr comparison --- Zend/tests/closure_compare.phpt | 2 +- Zend/zend_closures.c | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/Zend/tests/closure_compare.phpt b/Zend/tests/closure_compare.phpt index 0fe06a16430b7..313c44c4d5827 100644 --- a/Zend/tests/closure_compare.phpt +++ b/Zend/tests/closure_compare.phpt @@ -111,7 +111,7 @@ strlen != strrev: OK foo::existsStatic != bar::existsStatic: OK foo#0::exists != foo#1::exists: OK foo::traitMethod != baz::traitMethod: OK -foo::aliasMethod != baz::nonAliasMethod: OK +foo::aliasMethod != foo::nonAliasMethod: OK foo::exists == foo::exists: OK foo::method == foo::method: OK foo::method != bar::method: OK diff --git a/Zend/zend_closures.c b/Zend/zend_closures.c index 4111551672d0a..33a09135d1b13 100644 --- a/Zend/zend_closures.c +++ b/Zend/zend_closures.c @@ -389,7 +389,11 @@ static int zend_closure_compare(zval *o1, zval *o2) /* {{{ */ return ZEND_UNCOMPARABLE; } - if (Z_OBJ(lhs->this_ptr) != Z_OBJ(rhs->this_ptr)) { + if (Z_TYPE(lhs->this_ptr) != Z_TYPE(rhs->this_ptr)) { + return ZEND_UNCOMPARABLE; + } + + if (Z_TYPE(lhs->this_ptr) == IS_OBJECT && Z_OBJ(lhs->this_ptr) != Z_OBJ(rhs->this_ptr)) { return ZEND_UNCOMPARABLE; } From e2ca6af53aa2c262be3012a319e3bbe5bcb5dbfa Mon Sep 17 00:00:00 2001 From: Joe Watkins Date: Fri, 9 Jul 2021 10:26:27 +0200 Subject: [PATCH 8/9] squash test --- Zend/tests/closure_compare.phpt | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/Zend/tests/closure_compare.phpt b/Zend/tests/closure_compare.phpt index 313c44c4d5827..c4930f0139dd8 100644 --- a/Zend/tests/closure_compare.phpt +++ b/Zend/tests/closure_compare.phpt @@ -25,15 +25,9 @@ trait MethodTrait { public function traitMethod(){} } -trait AliasTrait { - public function nonAliasMethod() {} -} - class Foo { - use MethodTrait; - - use AliasTrait { - AliasTrait::nonAliasMethod as aliasMethod; + use MethodTrait { + MethodTrait::traitMethod as aliasMethod; } public function __call($method, $args) { @@ -70,10 +64,10 @@ $closures[1] = Closure::fromCallable([$baz, "traitMethod"]); printf("foo::traitMethod != baz::traitMethod: %s\n", $closures[0] != $closures[1] ? "OK" : "FAIL"); -$closures[0] = Closure::fromCallable([$foo, "nonAliasMethod"]); +$closures[0] = Closure::fromCallable([$foo, "traitMetod"]); $closures[1] = Closure::fromCallable([$foo, "aliasMethod"]); -printf("foo::aliasMethod != foo::nonAliasMethod: %s\n", $closures[0] != $closures[1] ? "OK" : "FAIL"); +printf("foo::traitMethod != foo::aliasMethod: %s\n", $closures[0] != $closures[1] ? "OK" : "FAIL"); $closures[0] = Closure::fromCallable([$foo, "exists"]); $closures[1] = Closure::fromCallable([$foo, "exists"]); @@ -111,7 +105,7 @@ strlen != strrev: OK foo::existsStatic != bar::existsStatic: OK foo#0::exists != foo#1::exists: OK foo::traitMethod != baz::traitMethod: OK -foo::aliasMethod != foo::nonAliasMethod: OK +foo::traitMethod != foo::aliasMethod: OK foo::exists == foo::exists: OK foo::method == foo::method: OK foo::method != bar::method: OK From e1a9b0f324a9cbbf581eb50091634c0ed7125aa0 Mon Sep 17 00:00:00 2001 From: Joe Watkins Date: Fri, 9 Jul 2021 10:36:53 +0200 Subject: [PATCH 9/9] fix test --- Zend/tests/closure_compare.phpt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Zend/tests/closure_compare.phpt b/Zend/tests/closure_compare.phpt index c4930f0139dd8..31c5218a23a90 100644 --- a/Zend/tests/closure_compare.phpt +++ b/Zend/tests/closure_compare.phpt @@ -64,7 +64,7 @@ $closures[1] = Closure::fromCallable([$baz, "traitMethod"]); printf("foo::traitMethod != baz::traitMethod: %s\n", $closures[0] != $closures[1] ? "OK" : "FAIL"); -$closures[0] = Closure::fromCallable([$foo, "traitMetod"]); +$closures[0] = Closure::fromCallable([$foo, "traitMethod"]); $closures[1] = Closure::fromCallable([$foo, "aliasMethod"]); printf("foo::traitMethod != foo::aliasMethod: %s\n", $closures[0] != $closures[1] ? "OK" : "FAIL");