diff --git a/Zend/tests/closure_compare.phpt b/Zend/tests/closure_compare.phpt new file mode 100644 index 0000000000000..31c5218a23a90 --- /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 5ae122b7f94ab..33a09135d1b13 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; } /* }}} */