Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions Zend/Optimizer/compact_literals.c
Original file line number Diff line number Diff line change
Expand Up @@ -577,17 +577,17 @@ void zend_optimizer_compact_literals(zend_op_array *op_array, zend_optimizer_ctx
opline->op1.constant,
opline->op2.constant,
LITERAL_STATIC_METHOD,
&cache_size);
&cache_size) | (opline->result.num & ZEND_INIT_CALLABLE_THIS);
} else {
opline->result.num = cache_size;
opline->result.num = cache_size | (opline->result.num & ZEND_INIT_CALLABLE_THIS);
cache_size += 2 * sizeof(void *);
}
} else if (opline->op1_type == IS_CONST) {
// op1 class
if (class_slot[opline->op1.constant] >= 0) {
opline->result.num = class_slot[opline->op1.constant];
opline->result.num = class_slot[opline->op1.constant] | (opline->result.num & ZEND_INIT_CALLABLE_THIS);
} else {
opline->result.num = cache_size;
opline->result.num = cache_size | (opline->result.num & ZEND_INIT_CALLABLE_THIS);
cache_size += sizeof(void *);
class_slot[opline->op1.constant] = opline->result.num;
}
Expand Down
4 changes: 4 additions & 0 deletions Zend/tests/partial_application/rfc_examples.inc
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,10 @@ function check_equivalence($tests)
'string' => (string) (100 + $i),
'Point' => new Point,
'mixed' => "mixed($i)",
'MyInterface' => new ParentClass,
'ParentClass' => new ParentClass,
'ChildClass' => new ChildClass,
'array|string' => (string) (100 + $i),
'' => "mixed($i)",
};
}
Expand Down
107 changes: 107 additions & 0 deletions Zend/tests/partial_application/this_placeholder.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
--TEST--
PFA with $this placeholder basics
--FILE--
<?php

class C {
function f($a = null, $b = null) {
printf("%s::%s(%s, %s)\n", get_class($this), __FUNCTION__, $a, $b);
}
}

echo "# Basics\n";

$f = C::f($this: ?, ?, ?);
echo new ReflectionFunction($f), "\n";
$f(new C, 'a', 'b');

echo "\n# \$this: can be used anywhere, and makes anything before it required\n";

$f = C::f(?, $this: ?, ?);
echo new ReflectionFunction($f), "\n";
$f('a', new C, 'b');

echo "\n# \$this: can be used anywhere, and makes anything before it required (2)\n";

$f = C::f(?, ?, $this: ?);
echo new ReflectionFunction($f), "\n";
$f('a', 'b', new C);

echo "\n# \$this: can be used anywhere (variadic placeholder)\n";

$f = C::f($this: ?, ...);
echo new ReflectionFunction($f), "\n";
$f(new C, 'a', 'b');

echo "\n# \$this: can be used anywhere (names params)\n";

$f = C::f(b: ?, $this: ?, a: ?);
echo new ReflectionFunction($f), "\n";
$f('b', new C, 'a');

?>
--EXPECTF--
# Basics
Closure [ <user> static public method {closure:pfa:%s:11} ] {
@@ %s 11 - 11

- Parameters [3] {
Parameter #0 [ <required> C $__this ]
Parameter #1 [ <optional> $a = NULL ]
Parameter #2 [ <optional> $b = NULL ]
}
}

C::f(a, b)

# $this: can be used anywhere, and makes anything before it required
Closure [ <user> static public method {closure:pfa:%s:17} ] {
@@ %s 17 - 17

- Parameters [3] {
Parameter #0 [ <required> $a ]
Parameter #1 [ <required> C $__this ]
Parameter #2 [ <optional> $b = NULL ]
}
}

C::f(a, b)

# $this: can be used anywhere, and makes anything before it required (2)
Closure [ <user> static public method {closure:pfa:%s:23} ] {
@@ %s 23 - 23

- Parameters [3] {
Parameter #0 [ <required> $a ]
Parameter #1 [ <required> $b ]
Parameter #2 [ <required> C $__this ]
}
}

C::f(a, b)

# $this: can be used anywhere (variadic placeholder)
Closure [ <user> static public method {closure:pfa:%s:29} ] {
@@ %s 29 - 29

- Parameters [3] {
Parameter #0 [ <required> C $__this ]
Parameter #1 [ <optional> $a = NULL ]
Parameter #2 [ <optional> $b = NULL ]
}
}

C::f(a, b)

# $this: can be used anywhere (names params)
Closure [ <user> static public method {closure:pfa:%s:35} ] {
@@ %s 35 - 35

- Parameters [3] {
Parameter #0 [ <required> $b ]
Parameter #1 [ <required> C $__this ]
Parameter #2 [ <optional> $a = NULL ]
}
}

C::f(a, b)
40 changes: 40 additions & 0 deletions Zend/tests/partial_application/this_placeholder_abstract.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
--TEST--
PFA with $this placeholder - abstract
--FILE--
<?php

abstract class BaseClass {
public abstract function method(string $foo): int|string|bool;
}

class ParentClass extends BaseClass {
public function method(string|array $foo): string {
return sprintf("%s(%s)", __METHOD__, $foo);
}
}

class ChildClass extends ParentClass {
public function method(string|array $bar): string {
return sprintf("%s(%s)", __METHOD__, $bar);
}
}

$i = BaseClass::method($this: ?, ...);
echo new ReflectionFunction($i), "\n";
var_dump($i(new ParentClass(), 'a'));
var_dump($i(new ChildClass(), 'a'));

?>
--EXPECTF--
Closure [ <user> static public method {closure:pfa:%s:19} ] {
@@ %s 19 - 19

- Parameters [2] {
Parameter #0 [ <required> BaseClass $__this ]
Parameter #1 [ <required> string $foo ]
}
- Return [ string|int|bool ]
}

string(22) "ParentClass::method(a)"
string(21) "ChildClass::method(a)"
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
--TEST--
PFA with $this placeholder - custom get_method()
--FILE--
<?php

try {
// Closure::__invoke is resolvable only via obj->handlers->get_method()
$f = Closure::__invoke($this: ?);
} catch (Error $e) {
echo $e::class, ": ", $e->getMessage(), "\n";
}

// SplFileObject implements a custom get_method() handler, but methods can be
// resolved by zend_std_get_static_method()
$f = SplFileObject::ftell($this: ?);
var_dump($f(new SplFileObject(__FILE__)));

?>
--EXPECT--
Error: Method Closure::__invoke() does not exist or does not support partial application
int(0)
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
--TEST--
PFA with $this placeholder: $this placeholder may only appear once
--FILE--
<?php

class C {
function f($a = null, $b = null) {
}
}

C::f($this: ?, $this: ?);

?>
--EXPECTF--
Fatal error: $this placeholder may only appear once in %s on line %d
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
--TEST--
PFA with $this placeholder: $this placeholder on free-standing function
--FILE--
<?php

$f = strlen($this: ?, ?);

?>
--EXPECTF--
Fatal error: Invalid use of $this placeholder in %s on line %d
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
--TEST--
PFA with $this placeholder: $this placeholder on instance method
--FILE--
<?php

$f = $foo->bar($this: ?);

?>
--EXPECTF--
Fatal error: Invalid use of $this placeholder in %s on line %d
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
--TEST--
PFA with $this placeholder: T_VARIABLE placeholder can only be $this
--FILE--
<?php

class C {
function f($a = null, $b = null) {
}
}

C::f($test: ?);

?>
--EXPECTF--
Fatal error: Invalid parameter name: $test in %s on line %d
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
--TEST--
PFA with $this placeholder: $this name allowed only for placeholders
--FILE--
<?php

class C {
function f($a = null, $b = null) {
}
}

C::f($this: 1);

?>
--EXPECTF--
Parse error: syntax error, unexpected integer "1", expecting "?" in %s on line %d
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
--TEST--
PFA with $this placeholder: $this placeholder can not be variadic
--FILE--
<?php

class C {
function f($a = null, $b = null) {
}
}

C::f($this: ...);

?>
--EXPECTF--
Parse error: syntax error, unexpected token "...", expecting "?" in %s on line %d
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
--TEST--
PFA with $this placeholder: $this placeholder not allowed after variadic placeholder
--FILE--
<?php

class C {
function f($a = null, $b = null) {
}
}

C::f(..., $this: ?);

?>
--EXPECTF--
Fatal error: Variadic placeholder must be last in %s on line %d
40 changes: 40 additions & 0 deletions Zend/tests/partial_application/this_placeholder_interface.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
--TEST--
PFA with $this placeholder - interface
--FILE--
<?php

interface MyInterface {
public function method(string $foo): int|string|bool;
}

class ParentClass implements MyInterface {
public function method(string|array $foo): string {
return sprintf("%s(%s)", __METHOD__, $foo);
}
}

class ChildClass extends ParentClass {
public function method(string|array $bar): string {
return sprintf("%s(%s)", __METHOD__, $bar);
}
}

$i = MyInterface::method($this: ?, ...);
echo new ReflectionFunction($i), "\n";
var_dump($i(new ParentClass(), 'a'));
var_dump($i(new ChildClass(), 'a'));

?>
--EXPECTF--
Closure [ <user> static public method {closure:pfa:%s:19} ] {
@@ %s 19 - 19

- Parameters [2] {
Parameter #0 [ <required> MyInterface $__this ]
Parameter #1 [ <required> string $foo ]
}
- Return [ string|int|bool ]
}

string(22) "ParentClass::method(a)"
string(21) "ChildClass::method(a)"
50 changes: 50 additions & 0 deletions Zend/tests/partial_application/this_placeholder_magic_call.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
--TEST--
PFA with $this placeholder - __call()
--FILE--
<?php

class C {
public function __call($name, $args) {
printf("%s::%s(%s, %s)\n", get_class($this), __FUNCTION__, $name, $args[0]);
}
public static function __callStatic($name, $args) {
die("Not called");
}
}

class D {
public function __call($name, $args) {
printf("%s::%s(%s, %s)\n", __CLASS__, __FUNCTION__, $name, $args[0]);
}
}

$f = C::f($this: ?, ...);
echo new ReflectionFunction($f), "\n";
$f(new C(), 'a', 'b');

$f = D::f($this: ?, ...);
echo new ReflectionFunction($f), "\n";
$f(new D(), 'a', 'b');

?>
--EXPECTF--
Closure [ <user> static public method {closure:pfa:%s:18} ] {
@@ %s 18 - 18

- Parameters [2] {
Parameter #0 [ <required> C $__this ]
Parameter #1 [ <optional> mixed ...$arguments ]
}
}

C::__call(f, a)
Closure [ <user> static public method {closure:pfa:%s:22} ] {
@@ %s 22 - 22

- Parameters [2] {
Parameter #0 [ <required> D $__this ]
Parameter #1 [ <optional> mixed ...$arguments ]
}
}

D::__call(f, a)
Loading
Loading