Skip to content

Commit d600213

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

File tree

2 files changed

+145
-1
lines changed

2 files changed

+145
-1
lines changed

Zend/tests/closure_compare.phpt

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
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+
trait MethodTrait {
25+
public function traitMethod(){}
26+
}
27+
28+
class Foo {
29+
use MethodTrait;
30+
31+
public function __call($method, $args) {
32+
33+
}
34+
35+
public function exists() {}
36+
37+
public static function existsStatic() {}
38+
}
39+
40+
class Bar extends Foo {}
41+
42+
class Baz {
43+
use MethodTrait;
44+
}
45+
46+
$closures[0] = Closure::fromCallable([Foo::class, "existsStatic"]);
47+
$closures[1] = Closure::fromCallable([Bar::class, "existsStatic"]);
48+
49+
printf("foo::existsStatic != bar::existsStatic: %s\n", $closures[0] != $closures[1] ? "OK" : "FAIL");
50+
51+
$foo = new Foo;
52+
53+
$closures[0] = Closure::fromCallable([$foo, "exists"]);
54+
$closures[1] = $closures[0]->bindTo(new Foo);
55+
56+
printf("foo#0::exists != foo#1::exists: %s\n", $closures[0] != $closures[1] ? "OK" : "FAIL");
57+
58+
$baz = new Baz;
59+
60+
$closures[0] = Closure::fromCallable([$foo, "traitMethod"]);
61+
$closures[1] = Closure::fromCallable([$baz, "traitMethod"]);
62+
63+
printf("foo::traitMethod != baz::traitMethod: %s\n", $closures[0] != $closures[1] ? "OK" : "FAIL");
64+
65+
$closures[0] = Closure::fromCallable([$foo, "exists"]);
66+
$closures[1] = Closure::fromCallable([$foo, "exists"]);
67+
68+
printf("foo::exists == foo::exists: %s\n", $closures[0] == $closures[1] ? "OK" : "FAIL");
69+
70+
$closures[0] = Closure::fromCallable([$foo, "method"]);
71+
$closures[1] = Closure::fromCallable([$foo, "method"]);
72+
73+
printf("foo::method == foo::method: %s\n", $closures[0] == $closures[1] ? "OK" : "FAIL");
74+
75+
$closures[1] = $closures[1]->bindTo(new Bar);
76+
77+
printf("foo::method != bar::method: %s\n", $closures[0] != $closures[1] ? "OK" : "FAIL");
78+
79+
$closures[0] = Closure::fromCallable([$foo, "method"]);
80+
$closures[1] = Closure::fromCallable([$foo, "method2"]);
81+
82+
printf("foo::method != foo::method2: %s\n", $closures[0] != $closures[1] ? "OK" : "FAIL");
83+
84+
$closures[2] = Closure::fromCallable([$closures[0], "__invoke"]);
85+
$closures[3] = Closure::fromCallable([$closures[1], "__invoke"]);
86+
87+
printf("Closure[0]::invoke != Closure[1]::invoke: %s\n", $closures[2] != $closures[3] ? "OK" : "FAIL");
88+
89+
$closures[2] = Closure::fromCallable([$closures[0], "__invoke"]);
90+
$closures[3] = Closure::fromCallable([$closures[0], "__invoke"]);
91+
92+
printf("Closure[0]::invoke == Closure[0]::invoke: %s\n", $closures[2] == $closures[3] ? "OK" : "FAIL");
93+
?>
94+
--EXPECT--
95+
foo == foo: OK
96+
strlen == strlen: OK
97+
strlen != strrev: OK
98+
foo::existsStatic != bar::existsStatic: OK
99+
foo#0::exists != foo#1::exists: OK
100+
foo::traitMethod != baz::traitMethod: OK
101+
foo::exists == foo::exists: OK
102+
foo::method == foo::method: OK
103+
foo::method != bar::method: OK
104+
foo::method != foo::method2: OK
105+
Closure[0]::invoke != Closure[1]::invoke: OK
106+
Closure[0]::invoke == Closure[0]::invoke: OK

Zend/zend_closures.c

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -381,7 +381,45 @@ static ZEND_COLD zend_function *zend_closure_get_constructor(zend_object *object
381381
static int zend_closure_compare(zval *o1, zval *o2) /* {{{ */
382382
{
383383
ZEND_COMPARE_OBJECTS_FALLBACK(o1, o2);
384-
return Z_OBJ_P(o1) != Z_OBJ_P(o2);
384+
385+
zend_closure *lhs = (zend_closure*) Z_OBJ_P(o1);
386+
zend_closure *rhs = (zend_closure*) Z_OBJ_P(o2);
387+
388+
if (lhs == rhs) {
389+
return SUCCESS;
390+
}
391+
392+
if (!((lhs->func.common.fn_flags & ZEND_ACC_FAKE_CLOSURE) && (rhs->func.common.fn_flags & ZEND_ACC_FAKE_CLOSURE))) {
393+
return ZEND_UNCOMPARABLE;
394+
}
395+
396+
if (Z_TYPE(lhs->this_ptr) == IS_OBJECT && Z_TYPE(rhs->this_ptr) == IS_OBJECT) {
397+
if (Z_OBJ(lhs->this_ptr) != Z_OBJ(rhs->this_ptr)) {
398+
return ZEND_UNCOMPARABLE;
399+
}
400+
}
401+
402+
if (lhs->called_scope != rhs->called_scope) {
403+
return ZEND_UNCOMPARABLE;
404+
}
405+
406+
if (lhs->func.type != rhs->func.type) {
407+
return ZEND_UNCOMPARABLE;
408+
}
409+
410+
if (lhs->func.common.scope != rhs->func.common.scope) {
411+
return ZEND_UNCOMPARABLE;
412+
}
413+
414+
if (lhs->func.type == ZEND_USER_FUNCTION) {
415+
return lhs->func.op_array.opcodes != rhs->func.op_array.opcodes;
416+
}
417+
418+
if (!zend_string_equals(lhs->func.common.function_name, rhs->func.common.function_name)) {
419+
return ZEND_UNCOMPARABLE;
420+
}
421+
422+
return SUCCESS;
385423
}
386424
/* }}} */
387425

0 commit comments

Comments
 (0)