diff --git a/NEWS b/NEWS index 332fe4e4fe5..355c8fd173b 100644 --- a/NEWS +++ b/NEWS @@ -4,6 +4,7 @@ PHP NEWS - Core: . Fixed bug #81238 (Fiber support missing for Solaris Sparc). (trowski) + . Fixed bug #81237 (Comparison of fake closures doesn't work). (krakjoe) - Reflection: . Fixed bug #80097 (ReflectionAttribute is not a Reflector). (beberlei) diff --git a/Zend/tests/closure_compare.phpt b/Zend/tests/closure_compare.phpt new file mode 100644 index 00000000000..31c5218a23a --- /dev/null +++ b/Zend/tests/closure_compare.phpt @@ -0,0 +1,114 @@ +--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, "traitMethod"]); +$closures[1] = Closure::fromCallable([$foo, "aliasMethod"]); + +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"]); + +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::traitMethod != foo::aliasMethod: 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 e23e8731523..56f5687ebff 100644 --- a/Zend/zend_closures.c +++ b/Zend/zend_closures.c @@ -381,7 +381,39 @@ 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->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) != 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; + } + + 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 (!zend_string_equals(lhs->func.common.function_name, rhs->func.common.function_name)) { + return ZEND_UNCOMPARABLE; + } + + return 0; } /* }}} */