fix(compiler): 支持抽象/接口方法的按引用参数签名解析 #15
Merged
韩天峰
merged 2 commits from fix-ref-20260709 into master 2 months ago
14 changed files with 491 additions and 25 deletions
@ -0,0 +1,24 @@ |
|||||||
|
<?php |
||||||
|
abstract class AbstractByRefBase |
||||||
|
{ |
||||||
|
public function __construct() |
||||||
|
{ |
||||||
|
$this->abc($value); |
||||||
|
var_dump($value); |
||||||
|
} |
||||||
|
|
||||||
|
abstract public function abc(&$value); |
||||||
|
} |
||||||
|
|
||||||
|
class AbstractByRefChild extends AbstractByRefBase |
||||||
|
{ |
||||||
|
public function abc(&$value) |
||||||
|
{ |
||||||
|
$value = 1; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
function main() |
||||||
|
{ |
||||||
|
new AbstractByRefChild; |
||||||
|
} |
||||||
@ -0,0 +1,22 @@ |
|||||||
|
<?php |
||||||
|
namespace { |
||||||
|
interface IByRef |
||||||
|
{ |
||||||
|
public function abc(&$value); |
||||||
|
} |
||||||
|
|
||||||
|
class ByRefInterfaceImpl implements IByRef |
||||||
|
{ |
||||||
|
public function abc(&$value) |
||||||
|
{ |
||||||
|
$value = 'x'; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
function main() |
||||||
|
{ |
||||||
|
$t = new ByRefInterfaceImpl; |
||||||
|
$t->abc($v); |
||||||
|
var_dump($v); |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,29 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
interface ParentByRef |
||||||
|
{ |
||||||
|
public function setValue(&$value): void; |
||||||
|
} |
||||||
|
|
||||||
|
interface ChildByRef extends ParentByRef |
||||||
|
{ |
||||||
|
} |
||||||
|
|
||||||
|
final class ByRefInterfaceReceiver implements ChildByRef |
||||||
|
{ |
||||||
|
public function setValue(&$value): void |
||||||
|
{ |
||||||
|
$value = 42; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
function invokeByRefInterface(ChildByRef $receiver): void |
||||||
|
{ |
||||||
|
$receiver->setValue(value: $value); |
||||||
|
var_dump($value); |
||||||
|
} |
||||||
|
|
||||||
|
function main(): void |
||||||
|
{ |
||||||
|
invokeByRefInterface(new ByRefInterfaceReceiver()); |
||||||
|
} |
||||||
@ -0,0 +1,30 @@ |
|||||||
|
<?php |
||||||
|
namespace { |
||||||
|
abstract class ByRefMultilevelBase |
||||||
|
{ |
||||||
|
abstract public function abc(&$value); |
||||||
|
|
||||||
|
public function run() |
||||||
|
{ |
||||||
|
$this->abc($value); |
||||||
|
var_dump($value); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
abstract class ByRefMultilevelMid extends ByRefMultilevelBase |
||||||
|
{ |
||||||
|
} |
||||||
|
|
||||||
|
class ByRefMultilevelChild extends ByRefMultilevelMid |
||||||
|
{ |
||||||
|
public function abc(&$value) |
||||||
|
{ |
||||||
|
$value = [1, 2]; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
function main() |
||||||
|
{ |
||||||
|
(new ByRefMultilevelChild)->run(); |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,34 @@ |
|||||||
|
<?php |
||||||
|
namespace A { |
||||||
|
abstract class AbstractByRefBase |
||||||
|
{ |
||||||
|
public function __construct() |
||||||
|
{ |
||||||
|
$this->abc($value); |
||||||
|
var_dump($value); |
||||||
|
} |
||||||
|
|
||||||
|
abstract public function abc(&$value); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
namespace B { |
||||||
|
use A\AbstractByRefBase; |
||||||
|
|
||||||
|
class AbstractByRefChild extends AbstractByRefBase |
||||||
|
{ |
||||||
|
public function abc(&$value) |
||||||
|
{ |
||||||
|
$value = [1]; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
namespace { |
||||||
|
use B\AbstractByRefChild; |
||||||
|
|
||||||
|
function main() |
||||||
|
{ |
||||||
|
new AbstractByRefChild; |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,33 @@ |
|||||||
|
--TEST-- |
||||||
|
abstract method with reference parameter, passing an already-defined variable |
||||||
|
--FILE-- |
||||||
|
<?php |
||||||
|
namespace { |
||||||
|
abstract class Base |
||||||
|
{ |
||||||
|
abstract public function abc(&$value); |
||||||
|
|
||||||
|
public function run() |
||||||
|
{ |
||||||
|
$v = 0; |
||||||
|
$this->abc($v); |
||||||
|
var_dump($v); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
class Test extends Base |
||||||
|
{ |
||||||
|
public function abc(&$value) |
||||||
|
{ |
||||||
|
$value = 42; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
function main() |
||||||
|
{ |
||||||
|
(new Test)->run(); |
||||||
|
} |
||||||
|
} |
||||||
|
?> |
||||||
|
--EXPECT-- |
||||||
|
int(42) |
||||||
@ -0,0 +1,29 @@ |
|||||||
|
--TEST-- |
||||||
|
interface method declared with reference parameter, implemented by a class |
||||||
|
--FILE-- |
||||||
|
<?php |
||||||
|
namespace { |
||||||
|
interface IByRef |
||||||
|
{ |
||||||
|
public function abc(&$value); |
||||||
|
} |
||||||
|
|
||||||
|
class Test implements IByRef |
||||||
|
{ |
||||||
|
public function abc(&$value) |
||||||
|
{ |
||||||
|
$value = 'x'; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
function main() |
||||||
|
{ |
||||||
|
$t = new Test; |
||||||
|
// $v 未定义,按引用传给接口的按引用方法后由实现类赋值 |
||||||
|
$t->abc($v); |
||||||
|
var_dump($v); |
||||||
|
} |
||||||
|
} |
||||||
|
?> |
||||||
|
--EXPECT-- |
||||||
|
string(1) "x" |
||||||
@ -0,0 +1,36 @@ |
|||||||
|
--TEST-- |
||||||
|
abstract method with two reference parameters, one defined and one undefined |
||||||
|
--FILE-- |
||||||
|
<?php |
||||||
|
namespace { |
||||||
|
abstract class Base |
||||||
|
{ |
||||||
|
abstract public function abc(&$a, &$b); |
||||||
|
|
||||||
|
public function run() |
||||||
|
{ |
||||||
|
$x = 5; |
||||||
|
// $x 已定义;$y 未定义,按引用传参后由实现类赋值 |
||||||
|
$this->abc($x, $y); |
||||||
|
var_dump($x, $y); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
class Test extends Base |
||||||
|
{ |
||||||
|
public function abc(&$a, &$b) |
||||||
|
{ |
||||||
|
$a *= 2; |
||||||
|
$b = 'done'; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
function main() |
||||||
|
{ |
||||||
|
(new Test)->run(); |
||||||
|
} |
||||||
|
} |
||||||
|
?> |
||||||
|
--EXPECT-- |
||||||
|
int(10) |
||||||
|
string(4) "done" |
||||||
@ -0,0 +1,43 @@ |
|||||||
|
--TEST-- |
||||||
|
abstract method with reference parameter across multiple levels of inheritance |
||||||
|
--FILE-- |
||||||
|
<?php |
||||||
|
namespace { |
||||||
|
abstract class Base |
||||||
|
{ |
||||||
|
abstract public function abc(&$value); |
||||||
|
|
||||||
|
public function run() |
||||||
|
{ |
||||||
|
// $value 在调用前未定义,按引用传给抽象方法后由最终实现类赋值 |
||||||
|
$this->abc($value); |
||||||
|
var_dump($value); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// 中间类继续继承抽象方法,不实现 |
||||||
|
abstract class Mid extends Base |
||||||
|
{ |
||||||
|
} |
||||||
|
|
||||||
|
class Test extends Mid |
||||||
|
{ |
||||||
|
public function abc(&$value) |
||||||
|
{ |
||||||
|
$value = [1, 2]; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
function main() |
||||||
|
{ |
||||||
|
(new Test)->run(); |
||||||
|
} |
||||||
|
} |
||||||
|
?> |
||||||
|
--EXPECT-- |
||||||
|
array(2) { |
||||||
|
[0]=> |
||||||
|
int(1) |
||||||
|
[1]=> |
||||||
|
int(2) |
||||||
|
} |
||||||
@ -0,0 +1,44 @@ |
|||||||
|
--TEST-- |
||||||
|
abstract method with reference parameter across namespaces, called from base constructor with undefined variable |
||||||
|
--FILE-- |
||||||
|
<?php |
||||||
|
namespace A { |
||||||
|
abstract class Base |
||||||
|
{ |
||||||
|
public function __construct() |
||||||
|
{ |
||||||
|
// $value 在调用前未定义,按引用传给抽象方法后由实现类赋值 |
||||||
|
$this->abc($value); |
||||||
|
var_dump($value); |
||||||
|
} |
||||||
|
|
||||||
|
abstract public function abc(&$value); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
namespace B { |
||||||
|
use A\Base; |
||||||
|
|
||||||
|
class Test extends Base |
||||||
|
{ |
||||||
|
public function abc(&$value) |
||||||
|
{ |
||||||
|
$value = [1]; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
namespace { |
||||||
|
use B\Test; |
||||||
|
|
||||||
|
function main() |
||||||
|
{ |
||||||
|
new Test; |
||||||
|
} |
||||||
|
} |
||||||
|
?> |
||||||
|
--EXPECT-- |
||||||
|
array(1) { |
||||||
|
[0]=> |
||||||
|
int(1) |
||||||
|
} |
||||||
@ -0,0 +1,31 @@ |
|||||||
|
--TEST-- |
||||||
|
abstract method with reference parameter called from base constructor with undefined variable |
||||||
|
--FILE-- |
||||||
|
<?php |
||||||
|
abstract class Base |
||||||
|
{ |
||||||
|
public function __construct() |
||||||
|
{ |
||||||
|
// $value 在调用前未定义,按引用传给抽象方法后由实现类赋值 |
||||||
|
$this->abc($value); |
||||||
|
var_dump($value); |
||||||
|
} |
||||||
|
|
||||||
|
abstract public function abc(&$value); |
||||||
|
} |
||||||
|
|
||||||
|
class Test extends Base |
||||||
|
{ |
||||||
|
public function abc(&$value) |
||||||
|
{ |
||||||
|
$value = 1; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
function main() |
||||||
|
{ |
||||||
|
new Test; |
||||||
|
} |
||||||
|
?> |
||||||
|
--EXPECT-- |
||||||
|
int(1) |
||||||
@ -0,0 +1,34 @@ |
|||||||
|
--TEST-- |
||||||
|
interface-typed receiver resolves inherited reference parameter signature |
||||||
|
--FILE-- |
||||||
|
<?php |
||||||
|
interface ParentByRef |
||||||
|
{ |
||||||
|
public function setValue(&$value): void; |
||||||
|
} |
||||||
|
|
||||||
|
interface ChildByRef extends ParentByRef |
||||||
|
{ |
||||||
|
} |
||||||
|
|
||||||
|
final class ByRefInterfaceReceiver implements ChildByRef |
||||||
|
{ |
||||||
|
public function setValue(&$value): void |
||||||
|
{ |
||||||
|
$value = 42; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
function invokeByRefInterface(ChildByRef $receiver): void |
||||||
|
{ |
||||||
|
$receiver->setValue(value: $value); |
||||||
|
var_dump($value); |
||||||
|
} |
||||||
|
|
||||||
|
function main(): void |
||||||
|
{ |
||||||
|
invokeByRefInterface(new ByRefInterfaceReceiver()); |
||||||
|
} |
||||||
|
?> |
||||||
|
--EXPECT-- |
||||||
|
int(42) |
||||||
Loading…
Reference in new issue