Skip to content

Commit 0300706

Browse files
committed
$this placeholder
1 parent 6317b8d commit 0300706

32 files changed

Lines changed: 1020 additions & 248 deletions

Zend/Optimizer/compact_literals.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -577,17 +577,17 @@ void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx
577577
opline->op1.constant,
578578
opline->op2.constant,
579579
LITERAL_STATIC_METHOD,
580-
&cache_size);
580+
&cache_size) | (opline->result.num & ZEND_INIT_CALLABLE_THIS);
581581
} else {
582-
opline->result.num = cache_size;
582+
opline->result.num = cache_size | (opline->result.num & ZEND_INIT_CALLABLE_THIS);
583583
cache_size += 2 * sizeof(void *);
584584
}
585585
} else if (opline->op1_type == IS_CONST) {
586586
// op1 class
587587
if (class_slot[opline->op1.constant] >= 0) {
588-
opline->result.num = class_slot[opline->op1.constant];
588+
opline->result.num = class_slot[opline->op1.constant] | (opline->result.num & ZEND_INIT_CALLABLE_THIS);
589589
} else {
590-
opline->result.num = cache_size;
590+
opline->result.num = cache_size | (opline->result.num & ZEND_INIT_CALLABLE_THIS);
591591
cache_size += sizeof(void *);
592592
class_slot[opline->op1.constant] = opline->result.num;
593593
}

Zend/tests/partial_application/rfc_examples.inc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,10 @@ function check_equivalence($tests)
5353
'string' => (string) (100 + $i),
5454
'Point' => new Point,
5555
'mixed' => "mixed($i)",
56+
'MyInterface' => new ParentClass,
57+
'ParentClass' => new ParentClass,
58+
'ChildClass' => new ChildClass,
59+
'array|string' => (string) (100 + $i),
5660
'' => "mixed($i)",
5761
};
5862
}
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
--TEST--
2+
PFA with $this placeholder basics
3+
--FILE--
4+
<?php
5+
6+
class C {
7+
function f($a = null, $b = null) {
8+
printf("%s::%s(%s, %s)\n", get_class($this), __FUNCTION__, $a, $b);
9+
}
10+
}
11+
12+
echo "# Basics\n";
13+
14+
$f = C::f($this: ?, ?, ?);
15+
echo new ReflectionFunction($f), "\n";
16+
$f(new C, 'a', 'b');
17+
18+
echo "\n# \$this: can be used anywhere, and makes anything before it required\n";
19+
20+
$f = C::f(?, $this: ?, ?);
21+
echo new ReflectionFunction($f), "\n";
22+
$f('a', new C, 'b');
23+
24+
echo "\n# \$this: can be used anywhere, and makes anything before it required (2)\n";
25+
26+
$f = C::f(?, ?, $this: ?);
27+
echo new ReflectionFunction($f), "\n";
28+
$f('a', 'b', new C);
29+
30+
echo "\n# \$this: can be used anywhere (variadic placeholder)\n";
31+
32+
$f = C::f($this: ?, ...);
33+
echo new ReflectionFunction($f), "\n";
34+
$f(new C, 'a', 'b');
35+
36+
echo "\n# \$this: can be used anywhere (names params)\n";
37+
38+
$f = C::f(b: ?, $this: ?, a: ?);
39+
echo new ReflectionFunction($f), "\n";
40+
$f('b', new C, 'a');
41+
42+
?>
43+
--EXPECTF--
44+
# Basics
45+
Closure [ <user> static public method {closure:pfa:%s:11} ] {
46+
@@ %s 11 - 11
47+
48+
- Parameters [3] {
49+
Parameter #0 [ <required> C $__this ]
50+
Parameter #1 [ <optional> $a = NULL ]
51+
Parameter #2 [ <optional> $b = NULL ]
52+
}
53+
}
54+
55+
C::f(a, b)
56+
57+
# $this: can be used anywhere, and makes anything before it required
58+
Closure [ <user> static public method {closure:pfa:%s:17} ] {
59+
@@ %s 17 - 17
60+
61+
- Parameters [3] {
62+
Parameter #0 [ <required> $a ]
63+
Parameter #1 [ <required> C $__this ]
64+
Parameter #2 [ <optional> $b = NULL ]
65+
}
66+
}
67+
68+
C::f(a, b)
69+
70+
# $this: can be used anywhere, and makes anything before it required (2)
71+
Closure [ <user> static public method {closure:pfa:%s:23} ] {
72+
@@ %s 23 - 23
73+
74+
- Parameters [3] {
75+
Parameter #0 [ <required> $a ]
76+
Parameter #1 [ <required> $b ]
77+
Parameter #2 [ <required> C $__this ]
78+
}
79+
}
80+
81+
C::f(a, b)
82+
83+
# $this: can be used anywhere (variadic placeholder)
84+
Closure [ <user> static public method {closure:pfa:%s:29} ] {
85+
@@ %s 29 - 29
86+
87+
- Parameters [3] {
88+
Parameter #0 [ <required> C $__this ]
89+
Parameter #1 [ <optional> $a = NULL ]
90+
Parameter #2 [ <optional> $b = NULL ]
91+
}
92+
}
93+
94+
C::f(a, b)
95+
96+
# $this: can be used anywhere (names params)
97+
Closure [ <user> static public method {closure:pfa:%s:35} ] {
98+
@@ %s 35 - 35
99+
100+
- Parameters [3] {
101+
Parameter #0 [ <required> $b ]
102+
Parameter #1 [ <required> C $__this ]
103+
Parameter #2 [ <optional> $a = NULL ]
104+
}
105+
}
106+
107+
C::f(a, b)
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
--TEST--
2+
PFA with $this placeholder - abstract
3+
--FILE--
4+
<?php
5+
6+
abstract class BaseClass {
7+
public abstract function method(string $foo): int|string|bool;
8+
}
9+
10+
class ParentClass extends BaseClass {
11+
public function method(string|array $foo): string {
12+
return sprintf("%s(%s)", __METHOD__, $foo);
13+
}
14+
}
15+
16+
class ChildClass extends ParentClass {
17+
public function method(string|array $bar): string {
18+
return sprintf("%s(%s)", __METHOD__, $bar);
19+
}
20+
}
21+
22+
$i = BaseClass::method($this: ?, ...);
23+
echo new ReflectionFunction($i), "\n";
24+
var_dump($i(new ParentClass(), 'a'));
25+
var_dump($i(new ChildClass(), 'a'));
26+
27+
?>
28+
--EXPECTF--
29+
Closure [ <user> static public method {closure:pfa:%s:19} ] {
30+
@@ %s 19 - 19
31+
32+
- Parameters [2] {
33+
Parameter #0 [ <required> BaseClass $__this ]
34+
Parameter #1 [ <required> string $foo ]
35+
}
36+
- Return [ string|int|bool ]
37+
}
38+
39+
string(22) "ParentClass::method(a)"
40+
string(21) "ChildClass::method(a)"
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
--TEST--
2+
PFA with $this placeholder - custom get_method()
3+
--FILE--
4+
<?php
5+
6+
try {
7+
// Closure::__invoke is resolvable only via obj->handlers->get_method()
8+
$f = Closure::__invoke($this: ?);
9+
} catch (Error $e) {
10+
echo $e::class, ": ", $e->getMessage(), "\n";
11+
}
12+
13+
// SplFileObject implements a custom get_method() handler, but methods can be
14+
// resolved by zend_std_get_static_method()
15+
$f = SplFileObject::ftell($this: ?);
16+
var_dump($f(new SplFileObject(__FILE__)));
17+
18+
?>
19+
--EXPECT--
20+
Error: Method Closure::__invoke() does not exist or does not support partial application
21+
int(0)
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
--TEST--
2+
PFA with $this placeholder: $this placeholder may only appear once
3+
--FILE--
4+
<?php
5+
6+
class C {
7+
function f($a = null, $b = null) {
8+
}
9+
}
10+
11+
C::f($this: ?, $this: ?);
12+
13+
?>
14+
--EXPECTF--
15+
Fatal error: $this placeholder may only appear once in %s on line %d
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
--TEST--
2+
PFA with $this placeholder: $this placeholder on free-standing function
3+
--FILE--
4+
<?php
5+
6+
$f = strlen($this: ?, ?);
7+
8+
?>
9+
--EXPECTF--
10+
Fatal error: Invalid use of $this placeholder in %s on line %d
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
--TEST--
2+
PFA with $this placeholder: $this placeholder on instance method
3+
--FILE--
4+
<?php
5+
6+
$f = $foo->bar($this: ?);
7+
8+
?>
9+
--EXPECTF--
10+
Fatal error: Invalid use of $this placeholder in %s on line %d
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
--TEST--
2+
PFA with $this placeholder: T_VARIABLE placeholder can only be $this
3+
--FILE--
4+
<?php
5+
6+
class C {
7+
function f($a = null, $b = null) {
8+
}
9+
}
10+
11+
C::f($test: ?);
12+
13+
?>
14+
--EXPECTF--
15+
Fatal error: Invalid parameter name: $test in %s on line %d
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
--TEST--
2+
PFA with $this placeholder: $this name allowed only for placeholders
3+
--FILE--
4+
<?php
5+
6+
class C {
7+
function f($a = null, $b = null) {
8+
}
9+
}
10+
11+
C::f($this: 1);
12+
13+
?>
14+
--EXPECTF--
15+
Parse error: syntax error, unexpected integer "1", expecting "?" in %s on line %d

0 commit comments

Comments
 (0)