Skip to content

Commit 7c3a7b9

Browse files
committed
fake closure comparison
1 parent 7bc0dd2 commit 7c3a7b9

File tree

2 files changed

+127
-0
lines changed

2 files changed

+127
-0
lines changed

Zend/tests/closure_compare.phpt

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
--TEST--
2+
Closure comparison
3+
--FILE--
4+
<?php
5+
function foo() {
6+
static $var;
7+
}
8+
9+
$closures[0] = Closure::fromCallable('foo');
10+
$closures[1] = Closure::fromCallable('foo');
11+
12+
printf("foo == foo: %s\n", $closures[0] == $closures[1] ? "OK" : "FAIL");
13+
14+
$closures[0] = Closure::fromCallable('strlen');
15+
$closures[1] = Closure::fromCallable('strlen');
16+
17+
printf("strlen == strlen: %s\n", $closures[0] == $closures[1] ? "OK" : "FAIL");
18+
19+
$closures[0] = Closure::fromCallable('strlen');
20+
$closures[1] = Closure::fromCallable('strrev');
21+
22+
printf("strlen != strrev: %s\n", $closures[0] != $closures[1] ? "OK" : "FAIL");
23+
24+
class Foo {
25+
public function __call($method, $args) {
26+
27+
}
28+
29+
public function exists() {}
30+
31+
public static function existsStatic() {}
32+
}
33+
34+
class Bar extends Foo {}
35+
36+
$closures[0] = Closure::fromCallable([Foo::class, "existsStatic"]);
37+
$closures[1] = Closure::fromCallable([Bar::class, "existsStatic"]);
38+
39+
printf("foo::existsStatic != bar::existsStatic: %s\n", $closures[0] != $closures[1] ? "OK" : "FAIL");
40+
41+
$foo = new Foo;
42+
43+
$closures[0] = Closure::fromCallable([$foo, "exists"]);
44+
$closures[1] = $closures[0]->bindTo(new Foo);
45+
46+
printf("foo#0::exists != foo#1::exists: %s\n", $closures[0] != $closures[1] ? "OK" : "FAIL");
47+
48+
$closures[0] = Closure::fromCallable([$foo, "exists"]);
49+
$closures[1] = Closure::fromCallable([$foo, "exists"]);
50+
51+
printf("foo::exists == foo::exists: %s\n", $closures[0] == $closures[1] ? "OK" : "FAIL");
52+
53+
$closures[0] = Closure::fromCallable([$foo, "method"]);
54+
$closures[1] = Closure::fromCallable([$foo, "method"]);
55+
56+
printf("foo::method == foo::method: %s\n", $closures[0] == $closures[1] ? "OK" : "FAIL");
57+
58+
$closures[1] = $closures[1]->bindTo(new Bar);
59+
60+
printf("foo::method != bar::method: %s\n", $closures[0] != $closures[1] ? "OK" : "FAIL");
61+
62+
$closures[0] = Closure::fromCallable([$foo, "method"]);
63+
$closures[1] = Closure::fromCallable([$foo, "method2"]);
64+
65+
printf("foo::method != foo::method2: %s\n", $closures[0] != $closures[1] ? "OK" : "FAIL");
66+
67+
$closures[2] = Closure::fromCallable([$closures[0], "__invoke"]);
68+
$closures[3] = Closure::fromCallable([$closures[1], "__invoke"]);
69+
70+
printf("Closure[0]::invoke != Closure[1]::invoke: %s\n", $closures[2] != $closures[3] ? "OK" : "FAIL");
71+
72+
$closures[2] = Closure::fromCallable([$closures[0], "__invoke"]);
73+
$closures[3] = Closure::fromCallable([$closures[0], "__invoke"]);
74+
75+
printf("Closure[0]::invoke == Closure[0]::invoke: %s\n", $closures[2] == $closures[3] ? "OK" : "FAIL");
76+
?>
77+
--EXPECT--
78+
foo == foo: OK
79+
strlen == strlen: OK
80+
strlen != strrev: OK
81+
foo::existsStatic != bar::existsStatic: OK
82+
foo#0::exists != foo#1::exists: OK
83+
foo::exists == foo::exists: OK
84+
foo::method == foo::method: OK
85+
foo::method != bar::method: OK
86+
foo::method != foo::method2: OK
87+
Closure[0]::invoke != Closure[1]::invoke: OK
88+
Closure[0]::invoke == Closure[0]::invoke: OK

Zend/zend_closures.c

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -377,10 +377,49 @@ static ZEND_COLD zend_function *zend_closure_get_constructor(zend_object *object
377377
}
378378
/* }}} */
379379

380+
static zend_always_inline int zend_closure_compare_fakes(zend_closure *lhs, zend_closure *rhs) { /* {{{ */
381+
if (Z_TYPE(lhs->this_ptr) == IS_OBJECT && Z_TYPE(rhs->this_ptr) == IS_OBJECT) {
382+
if (Z_OBJ(lhs->this_ptr) != Z_OBJ(rhs->this_ptr)) {
383+
return ZEND_UNCOMPARABLE;
384+
}
385+
}
386+
387+
if (lhs->called_scope != rhs->called_scope) {
388+
return ZEND_UNCOMPARABLE;
389+
}
390+
391+
if (lhs->func.type != rhs->func.type) {
392+
return ZEND_UNCOMPARABLE;
393+
}
394+
395+
if (lhs->func.type == ZEND_USER_FUNCTION) {
396+
return lhs->func.op_array.opcodes != rhs->func.op_array.opcodes;
397+
} else {
398+
if (lhs->func.common.scope != rhs->func.common.scope) {
399+
return ZEND_UNCOMPARABLE;
400+
}
401+
402+
if (!zend_string_equals(lhs->func.common.function_name, rhs->func.common.function_name)) {
403+
return ZEND_UNCOMPARABLE;
404+
}
405+
}
406+
407+
return 0;
408+
} /* }}} */
409+
380410
/* int return due to Object Handler API */
381411
static int zend_closure_compare(zval *o1, zval *o2) /* {{{ */
382412
{
383413
ZEND_COMPARE_OBJECTS_FALLBACK(o1, o2);
414+
415+
zend_closure *lhs = (zend_closure*) Z_OBJ_P(o1);
416+
zend_closure *rhs = (zend_closure*) Z_OBJ_P(o2);
417+
418+
if (lhs->func.common.fn_flags & ZEND_ACC_FAKE_CLOSURE &&
419+
rhs->func.common.fn_flags & ZEND_ACC_FAKE_CLOSURE) {
420+
return zend_closure_compare_fakes(lhs, rhs);
421+
}
422+
384423
return Z_OBJ_P(o1) != Z_OBJ_P(o2);
385424
}
386425
/* }}} */

0 commit comments

Comments
 (0)