commit
a6ff37c769
1023 changed files with 95756 additions and 887 deletions
@ -0,0 +1,399 @@ |
||||
# AOT 编译器核心重构计划 |
||||
|
||||
## 背景 |
||||
|
||||
当前 AOT 编译器核心类承担了过多职责,尤其是 `CompilerBase`、`Translator` 等类同时包含 AST 分发、类型推导、属性访问解析、调用解析、代码生成、诊断信息、上下文状态维护等逻辑。随着功能持续增加,这种结构会带来以下问题: |
||||
|
||||
- 封装性不足,修改一个语义点时容易影响多个代码路径。 |
||||
- 代码复用不足,同类逻辑在普通属性、静态属性、nullsafe、assignment、isset/empty/refval 等路径中重复实现。 |
||||
- 编译期检查容易出现绕过路径,例如某些动态 fallback 没有复用静态 resolver。 |
||||
- 单个类代码量过大,review、测试定位和长期维护成本持续升高。 |
||||
- 设计边界不清晰,类型系统、符号解析、属性访问、调用生成之间耦合过深。 |
||||
|
||||
本计划用于指导后续逐步重构,目标是改善架构质量,同时避免一次性大规模重写带来的行为回归风险。 |
||||
|
||||
## 核心原则 |
||||
|
||||
1. 渐进式重构,禁止一次性重写核心编译流程。 |
||||
2. 优先抽离无状态或低状态依赖的纯逻辑,再抽离依赖编译上下文的逻辑。 |
||||
3. 每一步重构应尽量保持行为不变,行为变更必须单独说明并补测试。 |
||||
4. 新模块应围绕稳定领域建模,而不是简单按 AST 节点机械拆分。 |
||||
5. 优先使用小型 service/helper、明确 DTO、resolver、emitter;仅在边界稳定后再引入 Visitor、Strategy 等模式。 |
||||
6. 所有错误信息保持 PHP 风格,不暴露实现细节,不使用 “AOT 禁止” 这类表述。 |
||||
7. 每个阶段必须有 phpunit 或 phpt 回归验证,尤其是属性、类型、调用、继承、异常等高风险路径。 |
||||
|
||||
## 目标架构方向 |
||||
|
||||
### CompilerBase |
||||
|
||||
最终应收敛为编译上下文、公共工具、AST 调度入口和跨模块协作层,不再直接承载大量领域逻辑。 |
||||
|
||||
保留职责: |
||||
|
||||
- 当前文件、函数、类、方法上下文管理。 |
||||
- 临时变量、局部变量、作用域状态维护。 |
||||
- AST 顶层分发入口。 |
||||
- fatal/warning 的统一入口。 |
||||
- 与下层 resolver/emitter 的协作。 |
||||
|
||||
逐步移出职责: |
||||
|
||||
- 类型声明解析和类型兼容判断。 |
||||
- 属性可见性、属性 offset、typed property 检查。 |
||||
- 函数和方法调用解析。 |
||||
- 复杂表达式代码生成。 |
||||
- union/intersection/nullable runtime typecheck 生成。 |
||||
|
||||
### Translator |
||||
|
||||
保留文件级、类级、函数级编译流程控制,逐步减少具体语义检查和表达式生成逻辑。 |
||||
|
||||
保留职责: |
||||
|
||||
- 文件扫描和转换入口。 |
||||
- 类、函数、方法代码块生成流程。 |
||||
- class/function/constant/property metadata 注册。 |
||||
- 编译产物组织。 |
||||
|
||||
逐步移出职责: |
||||
|
||||
- 继承兼容检查的细节逻辑。 |
||||
- trait 属性冲突检查细节。 |
||||
- 参数、返回值、属性类型兼容判断。 |
||||
|
||||
## 模块拆分计划 |
||||
|
||||
### 1. TypeSystem |
||||
|
||||
职责: |
||||
|
||||
- 类型声明解析。 |
||||
- PHP 类型到 AOT 内部类型映射。 |
||||
- nullable、union、intersection 类型展开。 |
||||
- 参数、返回值、属性、常量的类型兼容判断。 |
||||
- 静态类型与 runtime typecheck 的边界定义。 |
||||
- 类型字符串格式化。 |
||||
|
||||
建议子模块: |
||||
|
||||
- `TypeResolver` |
||||
- `TypeCompatibility` |
||||
- `TypeCheckEmitter` |
||||
- `TypeStringFormatter` |
||||
|
||||
设计要求: |
||||
|
||||
- union、intersection、nullable 在静态阶段仍可按 mixed/any 处理,但必须保留 runtime typecheck 信息。 |
||||
- `self`、`parent`、`static` 等特殊类型名的解析规则必须集中,避免多个路径实现不一致。 |
||||
- 类型错误信息必须包含函数、方法、参数、属性等必要上下文。 |
||||
|
||||
### 2. SymbolResolver |
||||
|
||||
职责: |
||||
|
||||
- 命名空间解析。 |
||||
- use alias 解析。 |
||||
- `self`、`parent`、`static` 解析。 |
||||
- 类、接口、trait、函数、常量名称规范化。 |
||||
- 动态符号和静态可解析符号的边界判断。 |
||||
|
||||
建议接口: |
||||
|
||||
- `resolveClassName(NodeAbstract $node): SymbolResolution` |
||||
- `resolveFunctionName(NodeAbstract $node): SymbolResolution` |
||||
- `resolveMethodScope(NodeAbstract $node): SymbolResolution` |
||||
- `resolveClassConstScope(NodeAbstract $node): SymbolResolution` |
||||
|
||||
设计要求: |
||||
|
||||
- 不同调用路径不能重复实现 `self/parent/static` 规则。 |
||||
- 对无法静态确定的符号,应明确返回 dynamic 状态,而不是默默退化为字符串拼接。 |
||||
|
||||
### 3. PropertyAccessResolver |
||||
|
||||
职责: |
||||
|
||||
- 对象属性访问解析。 |
||||
- 静态属性访问解析。 |
||||
- nullsafe 属性访问静态检查。 |
||||
- private/protected/public 可见性检查。 |
||||
- native property offset 查找。 |
||||
- typed property 写入 typecheck 信息生成入口。 |
||||
- static-vs-instance 属性误用检查。 |
||||
|
||||
建议接口: |
||||
|
||||
- `resolveInstancePropertyAccess(PropertyAccessRequest $request): PropertyAccessResult` |
||||
- `resolveStaticPropertyAccess(StaticPropertyAccessRequest $request): PropertyAccessResult` |
||||
- `assertReadable(PropertyAccessResult $result): void` |
||||
- `assertWritable(PropertyAccessResult $result): void` |
||||
- `emitRead(PropertyAccessResult $result): string` |
||||
- `emitWrite(PropertyAccessResult $result, string $value): string` |
||||
|
||||
需要统一覆盖的路径: |
||||
|
||||
- `$obj->prop` |
||||
- `$obj?->prop` |
||||
- `Class::$prop` |
||||
- `self::$prop` |
||||
- `parent::$prop` |
||||
- `static::$prop` |
||||
- `isset($obj->prop)` |
||||
- `empty($obj->prop)` |
||||
- `refval($obj->prop)` |
||||
- 普通赋值、复合赋值、自增自减、unset。 |
||||
|
||||
第一优先级建议从本模块开始,因为近期问题集中在属性访问和可见性绕过,测试边界相对清晰。 |
||||
|
||||
### 4. CallResolver |
||||
|
||||
职责: |
||||
|
||||
- 函数调用解析。 |
||||
- 对象方法调用解析。 |
||||
- 静态方法调用解析。 |
||||
- native call 与 dynamic call 选择。 |
||||
- named args、unpack、by-ref 参数处理。 |
||||
- closure 和动态 callable 退化规则。 |
||||
|
||||
建议接口: |
||||
|
||||
- `resolveFunctionCall(CallRequest $request): CallResolution` |
||||
- `resolveMethodCall(MethodCallRequest $request): CallResolution` |
||||
- `resolveStaticCall(StaticCallRequest $request): CallResolution` |
||||
- `emitCall(CallResolution $resolution): string` |
||||
|
||||
设计要求: |
||||
|
||||
- 静态 function 和内置 function 参数信息明确时,可以自动转引用。 |
||||
- 动态调用、closure、编译期无法获取参数 by-ref 信息时,必须要求显式 `refval()`。 |
||||
- 使用 unpack 并追加尾部 named args 时,应退化为 dynamic call,不能走 native call。 |
||||
|
||||
### 5. ExpressionEmitter |
||||
|
||||
职责: |
||||
|
||||
- 表达式级代码生成。 |
||||
- 将大型 `parseExpr()` 分发逻辑逐步拆分。 |
||||
- 复用 resolver 结果生成 C++ 代码。 |
||||
|
||||
建议按领域拆分,而不是一开始拆成大量 AST visitor: |
||||
|
||||
- `AssignmentEmitter` |
||||
- `PropertyEmitter` |
||||
- `CallEmitter` |
||||
- `ArrayEmitter` |
||||
- `ControlExprEmitter` |
||||
- `ObjectEmitter` |
||||
|
||||
设计要求: |
||||
|
||||
- 先保持现有 `parseExpr()` 作为调度入口。 |
||||
- 每次只迁移一组表达式,迁移后跑对应测试组。 |
||||
- 对表达式副作用、求值顺序、临时变量生成必须保守处理。 |
||||
|
||||
### 6. Diagnostic |
||||
|
||||
职责: |
||||
|
||||
- 统一 fatal/warning 构造。 |
||||
- 提供上下文增强能力。 |
||||
- 保证错误消息风格接近 PHP。 |
||||
|
||||
建议能力: |
||||
|
||||
- 当前函数/方法名。 |
||||
- 参数名。 |
||||
- 属性名和类名。 |
||||
- 源码位置。 |
||||
- 声明位置与使用位置。 |
||||
|
||||
设计要求: |
||||
|
||||
- 错误信息不出现 “AOT” 作为行为主体。 |
||||
- 对用户可修复的问题,应包含准确符号名。 |
||||
- 编译期能发现的问题优先编译期 fatal,不应依赖运行时 typecheck 异常兜底。 |
||||
|
||||
## 阶段计划 |
||||
|
||||
### 阶段 1:属性访问解析模块化 |
||||
|
||||
目标: |
||||
|
||||
- 抽离 `findNativeProperty()`、`findNativeStaticProperty()`、`canAccessProtectedProperty()` 等属性访问解析逻辑。 |
||||
- 保持生成代码基本不变。 |
||||
- 建立统一的 `PropertyAccessResult`,承载属性声明、声明类、访问类、是否 native、offset、是否 dynamic 等信息。 |
||||
|
||||
范围: |
||||
|
||||
- 普通对象属性读取。 |
||||
- 静态属性读取。 |
||||
- nullsafe 属性静态检查。 |
||||
- 属性可见性检查。 |
||||
|
||||
当前进展: |
||||
|
||||
- 已建立 `PropertyAccessResolver` 和 `PropertyAccessResult` 作为属性访问解析的第一层抽象。 |
||||
- 实例属性读取已通过 resolver 的 `resolveNativeInstanceProperty()` 显式接口完成 native property 查找、static-vs-instance 检查和可见性检查。 |
||||
- `findNativeStaticProperty()` 已通过 resolver 的 `resolveNativeStaticProperty()` 显式接口完成静态属性检查。 |
||||
- nullsafe 属性链检查已通过 resolver 的 `resolveNullsafePropertyChain()` 显式接口完成类名推进和可见性检查。 |
||||
- 旧的 `CompilerBase::findNativeProperty()` 泛型入口已移除,避免后续继续扩散带 `$static` 布尔参数的访问模式。 |
||||
- `CompilerBase::isSameClassName()`、`isSameOrSubclassOf()`、`canAccessProtectedProperty()` 已委托 resolver,避免规则继续扩散。 |
||||
- 已添加 `prepare/convert/idle` 编译阶段状态;`PropertyAccessResolver` 只能在 convert 阶段创建和使用,避免预处理阶段误用不完整的类表状态。 |
||||
- 当前迁移保持生成代码不变,后续阶段再统一 read/write emitter。 |
||||
|
||||
验证: |
||||
|
||||
- `phpunit/src/NativePropertyTest.php` |
||||
- `phpunit/src/InheritanceErrorTest.php` |
||||
- object property 相关 phpt。 |
||||
- static property 相关 phpt。 |
||||
- nullsafe 相关 phpt。 |
||||
|
||||
### 阶段 2:属性写入路径统一 |
||||
|
||||
目标: |
||||
|
||||
- 所有属性写入路径先 resolve,再 emit。 |
||||
- typed property runtime typecheck 从散落逻辑收敛到属性写入模块。 |
||||
- 消除 assignment、compound assignment、inc/dec、unset 各自实现属性访问规则的问题。 |
||||
|
||||
范围: |
||||
|
||||
- `$obj->prop = $value` |
||||
- `$obj->prop += $value` |
||||
- `$obj->prop++` |
||||
- `unset($obj->prop)` |
||||
- `Class::$prop = $value` |
||||
- `??=` 相关属性路径。 |
||||
|
||||
验证: |
||||
|
||||
- typed property 相关 phpunit/phpt。 |
||||
- object property optimization 相关 phpt。 |
||||
- nullsafe 写上下文错误测试。 |
||||
- private/protected/static 属性错误测试。 |
||||
|
||||
### 阶段 3:类型系统模块化 |
||||
|
||||
目标: |
||||
|
||||
- 抽离类型声明解析、类型兼容、runtime typecheck 生成。 |
||||
- 明确静态类型推导和运行时 typecheck 的职责边界。 |
||||
- 减少参数、返回值、属性、常量各自重复处理类型规则。 |
||||
|
||||
范围: |
||||
|
||||
- `parseTypeDecl()`。 |
||||
- `buildTypeCheckFromNode()`。 |
||||
- 参数 typecheck。 |
||||
- 返回值 typecheck。 |
||||
- 属性 typecheck。 |
||||
- 常量类型处理。 |
||||
|
||||
验证: |
||||
|
||||
- union、intersection、nullable 类型测试。 |
||||
- 参数、返回值、属性 typecheck 测试。 |
||||
- namespace constant 测试。 |
||||
- constructor、void expression 相关测试。 |
||||
|
||||
### 阶段 4:调用解析模块化 |
||||
|
||||
目标: |
||||
|
||||
- 统一函数、方法、静态方法调用解析。 |
||||
- 明确 native call、dynamic call、closure call 的退化规则。 |
||||
- 集中处理 named args、unpack、by-ref 参数。 |
||||
|
||||
范围: |
||||
|
||||
- `parseFuncCall()`。 |
||||
- `parseMethodCall()`。 |
||||
- `parseStaticCall()`。 |
||||
- call args 解析。 |
||||
- native/internal/user function 调用路径。 |
||||
|
||||
验证: |
||||
|
||||
- named args、unpack 相关 phpt。 |
||||
- by-ref 参数相关 phpt。 |
||||
- closure 相关 phpt。 |
||||
- parent/self/static call 相关 phpt。 |
||||
|
||||
### 阶段 5:表达式生成器拆分 |
||||
|
||||
目标: |
||||
|
||||
- 将 `parseExpr()` 背后的大量表达式生成逻辑迁移到领域 emitter。 |
||||
- `CompilerBase` 保持调度和共享上下文能力。 |
||||
- 降低单文件和单类代码量。 |
||||
|
||||
范围: |
||||
|
||||
- AssignmentEmitter。 |
||||
- PropertyEmitter。 |
||||
- CallEmitter。 |
||||
- ArrayEmitter。 |
||||
- ControlExprEmitter。 |
||||
|
||||
验证: |
||||
|
||||
- 每迁移一个 emitter,跑对应测试组。 |
||||
- 最后跑核心 phpunit 和选定 phpt 回归集。 |
||||
|
||||
### 阶段 6:Translator 收敛 |
||||
|
||||
目标: |
||||
|
||||
- 将继承兼容、trait 合并、类成员校验等逻辑抽离成专门 checker。 |
||||
- `Translator` 聚焦编译流程组织。 |
||||
|
||||
建议子模块: |
||||
|
||||
- `InheritanceChecker` |
||||
- `TraitCompositionChecker` |
||||
- `ClassMemberValidator` |
||||
- `FunctionSignatureChecker` |
||||
|
||||
验证: |
||||
|
||||
- 继承错误 phpunit。 |
||||
- trait 相关 phpt。 |
||||
- interface/abstract/final/readonly 相关 phpt。 |
||||
|
||||
## 测试门禁 |
||||
|
||||
每个重构 PR 或阶段至少满足: |
||||
|
||||
- 相关 phpunit 必须通过。 |
||||
- 相关 phpt 必须通过。 |
||||
- 如果修改 C++/phpx,需要补 gtest 并通过对应测试。 |
||||
- 如果引入新编译期错误,需要新增固定 fixture 到 `phpunit/code` 或新增 phpt。 |
||||
- 不使用 `file_put_contents()` 临时生成源码作为新测试方式。 |
||||
|
||||
建议按模块维护最小回归集: |
||||
|
||||
- 属性模块:`NativePropertyTest`、`InheritanceErrorTest`、object property、static property、nullsafe。 |
||||
- 类型模块:type_decl、type_hits、typed property、union/intersection/nullable。 |
||||
- 调用模块:function call、method call、parent_call、closure、named args、unpack、by-ref。 |
||||
- 控制流和表达式:ternary、match、goto、loop、array、coalesce、void expression。 |
||||
|
||||
## 风险控制 |
||||
|
||||
- 不在同一个变更中同时做大规模文件移动和行为变更。 |
||||
- 每次迁移前先补当前行为的测试,尤其是历史 bug 对应路径。 |
||||
- 保留旧入口一段时间,通过 adapter 调用新模块,降低切换风险。 |
||||
- 对动态 PHP 语义保持保守,无法静态确定时不要过度优化。 |
||||
- 对 AOT 明确不兼容 PHP 历史包袱的地方,应在文档和错误信息中表达为语言规则,而不是实现限制。 |
||||
|
||||
## 推荐下一步 |
||||
|
||||
从 `PropertyAccessResolver` 开始。 |
||||
|
||||
理由: |
||||
|
||||
- 最近 bug 多集中在属性访问、可见性、typed property、nullsafe、static-vs-instance 路径。 |
||||
- 现有测试较容易扩展。 |
||||
- 属性访问是类型系统、优化器、调用生成之外相对独立的领域,适合先建立 resolver/result 模式。 |
||||
- 完成后可直接减少 `CompilerBase` 中的复杂分支,并为后续 ExpressionEmitter 拆分打基础。 |
||||
@ -0,0 +1,15 @@ |
||||
<?php |
||||
abstract class AbstractSignatureBase |
||||
{ |
||||
abstract public function run(int $id): string; |
||||
} |
||||
|
||||
class AbstractSignatureChild extends AbstractSignatureBase |
||||
{ |
||||
public function run(string $id): string |
||||
{ |
||||
return $id; |
||||
} |
||||
} |
||||
|
||||
function main() {} |
||||
@ -0,0 +1,11 @@ |
||||
<?php |
||||
abstract class AbstractParentMissing |
||||
{ |
||||
abstract public function run(): void; |
||||
} |
||||
|
||||
class ConcreteParentMissing extends AbstractParentMissing |
||||
{ |
||||
} |
||||
|
||||
function main() {} |
||||
@ -0,0 +1,18 @@ |
||||
<?php |
||||
class Base { |
||||
private $prop = 999; |
||||
public function dump() |
||||
{ |
||||
var_dump($this->prop); |
||||
} |
||||
} |
||||
class User extends Base { |
||||
public $prop; |
||||
} |
||||
|
||||
function main() { |
||||
$u = new User(); |
||||
$u->prop = 12; |
||||
var_dump($u); |
||||
$u->dump(); |
||||
} |
||||
@ -0,0 +1,18 @@ |
||||
<?php |
||||
|
||||
trait TestTraitProp { |
||||
private $prop = 999; |
||||
} |
||||
|
||||
class Base { |
||||
use TestTraitProp; |
||||
} |
||||
class User extends Base { |
||||
public $prop; |
||||
} |
||||
|
||||
function main() { |
||||
$u = new User(); |
||||
$u->prop = 12; |
||||
var_dump($u); |
||||
} |
||||
@ -0,0 +1,32 @@ |
||||
<?php |
||||
|
||||
namespace Demo\Dispatch { |
||||
class ChildOverrideNs extends ParentBaseNs |
||||
{ |
||||
public function bar(): void |
||||
{ |
||||
echo "Child\n"; |
||||
} |
||||
} |
||||
|
||||
class ParentBaseNs |
||||
{ |
||||
public function run(): void |
||||
{ |
||||
$this->bar(); |
||||
} |
||||
|
||||
public function bar(): void |
||||
{ |
||||
echo "Parent\n"; |
||||
} |
||||
} |
||||
} |
||||
|
||||
namespace { |
||||
function main(): void |
||||
{ |
||||
$o = new Demo\Dispatch\ChildOverrideNs(); |
||||
$o->run(); |
||||
} |
||||
} |
||||
@ -0,0 +1,13 @@ |
||||
<?php |
||||
|
||||
class CloneInvalidReturnType |
||||
{ |
||||
public function __clone(): int |
||||
{ |
||||
return 1; |
||||
} |
||||
} |
||||
|
||||
function main(): void |
||||
{ |
||||
} |
||||
@ -0,0 +1,8 @@ |
||||
<?php |
||||
|
||||
function main(): void |
||||
{ |
||||
$fn = function (&$value): void { |
||||
$value = 1; |
||||
}; |
||||
} |
||||
@ -0,0 +1,6 @@ |
||||
<?php |
||||
|
||||
function main(): void |
||||
{ |
||||
echo "ok\n"; |
||||
} |
||||
@ -0,0 +1 @@ |
||||
int main() { return 0; } |
||||
@ -0,0 +1,2 @@ |
||||
<?php |
||||
function ignored() {} |
||||
@ -0,0 +1,2 @@ |
||||
<?php |
||||
function kept() {} |
||||
@ -0,0 +1,2 @@ |
||||
<?php |
||||
function main() {} |
||||
@ -0,0 +1,2 @@ |
||||
<?php |
||||
function skipped() {} |
||||
@ -0,0 +1,13 @@ |
||||
<?php |
||||
|
||||
class ConstructorReturnType |
||||
{ |
||||
public function __construct(): int |
||||
{ |
||||
return 1; |
||||
} |
||||
} |
||||
|
||||
function main(): void |
||||
{ |
||||
} |
||||
@ -0,0 +1,14 @@ |
||||
<?php |
||||
|
||||
class ConstructorReturnValue |
||||
{ |
||||
public function __construct() |
||||
{ |
||||
return 123; |
||||
} |
||||
} |
||||
|
||||
function main(): void |
||||
{ |
||||
new ConstructorReturnValue(); |
||||
} |
||||
@ -0,0 +1,12 @@ |
||||
<?php |
||||
|
||||
class DestructorReturnType |
||||
{ |
||||
public function __destruct(): void |
||||
{ |
||||
} |
||||
} |
||||
|
||||
function main(): void |
||||
{ |
||||
} |
||||
@ -0,0 +1,11 @@ |
||||
<?php |
||||
|
||||
function target(int $a, int $b): void |
||||
{ |
||||
} |
||||
|
||||
function main(): void |
||||
{ |
||||
$fn = 'target'; |
||||
$fn(...[1], 2); |
||||
} |
||||
@ -0,0 +1,11 @@ |
||||
<?php |
||||
|
||||
class InheritanceConstVisibilityWidenParent |
||||
{ |
||||
protected const VALUE = 1; |
||||
} |
||||
|
||||
class InheritanceConstVisibilityWidenChild extends InheritanceConstVisibilityWidenParent |
||||
{ |
||||
public const VALUE = 2; |
||||
} |
||||
@ -0,0 +1,15 @@ |
||||
<?php |
||||
|
||||
class InheritanceFinalMethodParent |
||||
{ |
||||
final public function run(): void |
||||
{ |
||||
} |
||||
} |
||||
|
||||
class InheritanceFinalMethodChild extends InheritanceFinalMethodParent |
||||
{ |
||||
public function run(): void |
||||
{ |
||||
} |
||||
} |
||||
@ -0,0 +1,23 @@ |
||||
<?php |
||||
|
||||
class InheritanceParamCovariantParentType |
||||
{ |
||||
} |
||||
|
||||
class InheritanceParamCovariantChildType extends InheritanceParamCovariantParentType |
||||
{ |
||||
} |
||||
|
||||
class InheritanceParamCovariantParent |
||||
{ |
||||
public function handle(InheritanceParamCovariantParentType $value): void |
||||
{ |
||||
} |
||||
} |
||||
|
||||
class InheritanceParamCovariantChild extends InheritanceParamCovariantParent |
||||
{ |
||||
public function handle(InheritanceParamCovariantChildType $value): void |
||||
{ |
||||
} |
||||
} |
||||
@ -0,0 +1,25 @@ |
||||
<?php |
||||
|
||||
class InheritanceReturnContravariantParentType |
||||
{ |
||||
} |
||||
|
||||
class InheritanceReturnContravariantChildType extends InheritanceReturnContravariantParentType |
||||
{ |
||||
} |
||||
|
||||
class InheritanceReturnContravariantParent |
||||
{ |
||||
public function make(): InheritanceReturnContravariantChildType |
||||
{ |
||||
return new InheritanceReturnContravariantChildType(); |
||||
} |
||||
} |
||||
|
||||
class InheritanceReturnContravariantChild extends InheritanceReturnContravariantParent |
||||
{ |
||||
public function make(): InheritanceReturnContravariantParentType |
||||
{ |
||||
return new InheritanceReturnContravariantParentType(); |
||||
} |
||||
} |
||||
@ -0,0 +1,15 @@ |
||||
<?php |
||||
|
||||
class InheritanceStaticParent |
||||
{ |
||||
public static function run(): void |
||||
{ |
||||
} |
||||
} |
||||
|
||||
class InheritanceStaticChild extends InheritanceStaticParent |
||||
{ |
||||
public function run(): void |
||||
{ |
||||
} |
||||
} |
||||
@ -0,0 +1,12 @@ |
||||
<?php |
||||
class A |
||||
{ |
||||
public function f() {} |
||||
} |
||||
|
||||
class B extends A |
||||
{ |
||||
protected function f() {} |
||||
} |
||||
|
||||
function main() {} |
||||
@ -0,0 +1,12 @@ |
||||
<?php |
||||
class A |
||||
{ |
||||
public function f($x) {} |
||||
} |
||||
|
||||
class B extends A |
||||
{ |
||||
public function f($x, $y = 1) {} |
||||
} |
||||
|
||||
function main() {} |
||||
@ -0,0 +1,23 @@ |
||||
<?php |
||||
|
||||
class InheritanceParamContravariantParentType |
||||
{ |
||||
} |
||||
|
||||
class InheritanceParamContravariantChildType extends InheritanceParamContravariantParentType |
||||
{ |
||||
} |
||||
|
||||
class InheritanceParamContravariantParent |
||||
{ |
||||
public function handle(InheritanceParamContravariantChildType $value): void |
||||
{ |
||||
} |
||||
} |
||||
|
||||
class InheritanceParamContravariantChild extends InheritanceParamContravariantParent |
||||
{ |
||||
public function handle(InheritanceParamContravariantParentType $value): void |
||||
{ |
||||
} |
||||
} |
||||
@ -0,0 +1,11 @@ |
||||
<?php |
||||
|
||||
class InheritancePrivateConstRedeclareParent |
||||
{ |
||||
private const int VALUE = 1; |
||||
} |
||||
|
||||
class InheritancePrivateConstRedeclareChild extends InheritancePrivateConstRedeclareParent |
||||
{ |
||||
public const string VALUE = 'child'; |
||||
} |
||||
@ -0,0 +1,11 @@ |
||||
<?php |
||||
|
||||
class InheritancePrivatePropRedeclareParent |
||||
{ |
||||
private int $value = 1; |
||||
} |
||||
|
||||
class InheritancePrivatePropRedeclareChild extends InheritancePrivatePropRedeclareParent |
||||
{ |
||||
private string $value = 'child'; |
||||
} |
||||
@ -0,0 +1,11 @@ |
||||
<?php |
||||
|
||||
class InheritancePropVisibilityWidenParent |
||||
{ |
||||
protected int $value = 1; |
||||
} |
||||
|
||||
class InheritancePropVisibilityWidenChild extends InheritancePropVisibilityWidenParent |
||||
{ |
||||
public int $value = 2; |
||||
} |
||||
@ -0,0 +1,14 @@ |
||||
<?php |
||||
|
||||
class InheritanceProtectedChildConstAccessParent |
||||
{ |
||||
public function readChild(): int |
||||
{ |
||||
return InheritanceProtectedChildConstAccessChild::VALUE; |
||||
} |
||||
} |
||||
|
||||
class InheritanceProtectedChildConstAccessChild extends InheritanceProtectedChildConstAccessParent |
||||
{ |
||||
protected const VALUE = 1; |
||||
} |
||||
@ -0,0 +1,16 @@ |
||||
<?php |
||||
|
||||
class InheritanceProtectedChildMethodAccessParent |
||||
{ |
||||
public function callChild(): void |
||||
{ |
||||
InheritanceProtectedChildMethodAccessChild::run(); |
||||
} |
||||
} |
||||
|
||||
class InheritanceProtectedChildMethodAccessChild extends InheritanceProtectedChildMethodAccessParent |
||||
{ |
||||
protected static function run(): void |
||||
{ |
||||
} |
||||
} |
||||
@ -0,0 +1,25 @@ |
||||
<?php |
||||
|
||||
class InheritanceReturnCovariantParentType |
||||
{ |
||||
} |
||||
|
||||
class InheritanceReturnCovariantChildType extends InheritanceReturnCovariantParentType |
||||
{ |
||||
} |
||||
|
||||
class InheritanceReturnCovariantParent |
||||
{ |
||||
public function make(): InheritanceReturnCovariantParentType |
||||
{ |
||||
return new InheritanceReturnCovariantParentType(); |
||||
} |
||||
} |
||||
|
||||
class InheritanceReturnCovariantChild extends InheritanceReturnCovariantParent |
||||
{ |
||||
public function make(): InheritanceReturnCovariantChildType |
||||
{ |
||||
return new InheritanceReturnCovariantChildType(); |
||||
} |
||||
} |
||||
@ -0,0 +1,17 @@ |
||||
<?php |
||||
|
||||
class InheritanceReturnNoneParent |
||||
{ |
||||
public function run() |
||||
{ |
||||
return 1; |
||||
} |
||||
} |
||||
|
||||
class InheritanceReturnNoneChild extends InheritanceReturnNoneParent |
||||
{ |
||||
public function run(): int |
||||
{ |
||||
return 1; |
||||
} |
||||
} |
||||
@ -0,0 +1,19 @@ |
||||
<?php |
||||
interface ContractAbstractMissing |
||||
{ |
||||
public function run(int $id): string; |
||||
} |
||||
|
||||
abstract class AbstractImplMissing implements ContractAbstractMissing |
||||
{ |
||||
} |
||||
|
||||
class ConcreteImplMissing extends AbstractImplMissing |
||||
{ |
||||
public function run(int $id): string |
||||
{ |
||||
return (string) $id; |
||||
} |
||||
} |
||||
|
||||
function main() {} |
||||
@ -0,0 +1,12 @@ |
||||
<?php |
||||
interface ContractAbstractMismatch |
||||
{ |
||||
public function run(int $id): string; |
||||
} |
||||
|
||||
abstract class AbstractImplMismatch implements ContractAbstractMismatch |
||||
{ |
||||
abstract public function run(string $id): string; |
||||
} |
||||
|
||||
function main() {} |
||||
@ -0,0 +1,20 @@ |
||||
<?php |
||||
interface ContractAbstractMethod |
||||
{ |
||||
public function run(int $id): string; |
||||
} |
||||
|
||||
abstract class AbstractImplMethod implements ContractAbstractMethod |
||||
{ |
||||
abstract public function run(int $id): string; |
||||
} |
||||
|
||||
class ConcreteImplMethod extends AbstractImplMethod |
||||
{ |
||||
public function run(int $id): string |
||||
{ |
||||
return (string) $id; |
||||
} |
||||
} |
||||
|
||||
function main() {} |
||||
@ -0,0 +1,15 @@ |
||||
<?php |
||||
interface ContractAbstractParentMissing |
||||
{ |
||||
public function run(): void; |
||||
} |
||||
|
||||
abstract class AbstractContractParentMissing implements ContractAbstractParentMissing |
||||
{ |
||||
} |
||||
|
||||
class ConcreteContractParentMissing extends AbstractContractParentMissing |
||||
{ |
||||
} |
||||
|
||||
function main() {} |
||||
@ -0,0 +1,7 @@ |
||||
<?php |
||||
interface InterfaceArrayConstant |
||||
{ |
||||
public const ITEMS = [1, 2, 3]; |
||||
} |
||||
|
||||
function main() {} |
||||
@ -0,0 +1,14 @@ |
||||
<?php |
||||
interface InterfaceArrayConstantContract |
||||
{ |
||||
public const ITEMS = [1, 2, 3]; |
||||
} |
||||
|
||||
class InterfaceArrayConstantImpl implements InterfaceArrayConstantContract |
||||
{ |
||||
} |
||||
|
||||
function main() |
||||
{ |
||||
var_dump(InterfaceArrayConstantImpl::ITEMS); |
||||
} |
||||
@ -0,0 +1,20 @@ |
||||
<?php |
||||
interface ContractTrait |
||||
{ |
||||
public function run(int $id): string; |
||||
} |
||||
|
||||
trait ContractTraitImpl |
||||
{ |
||||
public function run(int $id): string |
||||
{ |
||||
return (string) $id; |
||||
} |
||||
} |
||||
|
||||
class ImplTrait implements ContractTrait |
||||
{ |
||||
use ContractTraitImpl; |
||||
} |
||||
|
||||
function main() {} |
||||
@ -0,0 +1,11 @@ |
||||
<?php |
||||
interface ContractMissing |
||||
{ |
||||
public function run(int $id): void; |
||||
} |
||||
|
||||
class ImplMissing implements ContractMissing |
||||
{ |
||||
} |
||||
|
||||
function main() {} |
||||
@ -0,0 +1,15 @@ |
||||
<?php |
||||
interface ContractMismatch |
||||
{ |
||||
public function run(int $id): string; |
||||
} |
||||
|
||||
class ImplMismatch implements ContractMismatch |
||||
{ |
||||
public function run(string $id): string |
||||
{ |
||||
return $id; |
||||
} |
||||
} |
||||
|
||||
function main() {} |
||||
@ -0,0 +1,13 @@ |
||||
<?php |
||||
|
||||
interface InterfaceStaticContract |
||||
{ |
||||
public static function run(): void; |
||||
} |
||||
|
||||
class InterfaceStaticImpl implements InterfaceStaticContract |
||||
{ |
||||
public function run(): void |
||||
{ |
||||
} |
||||
} |
||||
@ -0,0 +1,6 @@ |
||||
<?php |
||||
|
||||
function main(): void |
||||
{ |
||||
str_replace(search: "a", subject: "b"); |
||||
} |
||||
@ -0,0 +1,6 @@ |
||||
<?php |
||||
|
||||
function main(): void |
||||
{ |
||||
strlen(foo: "abc"); |
||||
} |
||||
@ -0,0 +1,6 @@ |
||||
<?php |
||||
|
||||
function main(): void |
||||
{ |
||||
$value = var_dump('hello'); |
||||
} |
||||
@ -0,0 +1,6 @@ |
||||
<?php |
||||
|
||||
function main(): void |
||||
{ |
||||
$value = var_dump('hello') + 1; |
||||
} |
||||
@ -0,0 +1,16 @@ |
||||
<?php |
||||
|
||||
interface ITypeParent |
||||
{ |
||||
} |
||||
|
||||
class BaseIntersectionParent implements ITypeParent |
||||
{ |
||||
} |
||||
|
||||
class DemoParentIntersection extends BaseIntersectionParent |
||||
{ |
||||
public function run(parent&ITypeParent $value): void {} |
||||
} |
||||
|
||||
function main() {} |
||||
@ -0,0 +1,12 @@ |
||||
<?php |
||||
|
||||
interface ITypeSelf |
||||
{ |
||||
} |
||||
|
||||
class DemoSelfIntersection implements ITypeSelf |
||||
{ |
||||
public function run(self&ITypeSelf $value): void {} |
||||
} |
||||
|
||||
function main() {} |
||||
@ -0,0 +1,15 @@ |
||||
<?php |
||||
|
||||
interface ITypeStatic |
||||
{ |
||||
} |
||||
|
||||
class DemoStaticIntersection implements ITypeStatic |
||||
{ |
||||
public function run(): static&ITypeStatic |
||||
{ |
||||
return $this; |
||||
} |
||||
} |
||||
|
||||
function main() {} |
||||
@ -0,0 +1,13 @@ |
||||
<?php |
||||
|
||||
class MagicCallStaticInvalid |
||||
{ |
||||
public static function __call(string $name, array $arguments): mixed |
||||
{ |
||||
return null; |
||||
} |
||||
} |
||||
|
||||
function main(): void |
||||
{ |
||||
} |
||||
@ -0,0 +1,13 @@ |
||||
<?php |
||||
|
||||
class MagicCallStaticNonStaticInvalid |
||||
{ |
||||
public function __callStatic(string $name, array $arguments): mixed |
||||
{ |
||||
return null; |
||||
} |
||||
} |
||||
|
||||
function main(): void |
||||
{ |
||||
} |
||||
@ -0,0 +1,12 @@ |
||||
<?php |
||||
|
||||
class MagicDestructArgsInvalid |
||||
{ |
||||
public function __destruct($extra) |
||||
{ |
||||
} |
||||
} |
||||
|
||||
function main(): void |
||||
{ |
||||
} |
||||
@ -0,0 +1,13 @@ |
||||
<?php |
||||
|
||||
class MagicGetParamTypeInvalid |
||||
{ |
||||
public function __get(int $name): mixed |
||||
{ |
||||
return null; |
||||
} |
||||
} |
||||
|
||||
function main(): void |
||||
{ |
||||
} |
||||
@ -0,0 +1,13 @@ |
||||
<?php |
||||
|
||||
class MagicGetProtectedInvalid |
||||
{ |
||||
protected function __get(string $name): mixed |
||||
{ |
||||
return null; |
||||
} |
||||
} |
||||
|
||||
function main(): void |
||||
{ |
||||
} |
||||
@ -0,0 +1,13 @@ |
||||
<?php |
||||
|
||||
class MagicSetStateNonStaticInvalid |
||||
{ |
||||
public function __set_state(array $properties): object |
||||
{ |
||||
return new self(); |
||||
} |
||||
} |
||||
|
||||
function main(): void |
||||
{ |
||||
} |
||||
@ -0,0 +1,13 @@ |
||||
<?php |
||||
|
||||
class MagicToStringArgsInvalid |
||||
{ |
||||
public function __toString($extra): string |
||||
{ |
||||
return ''; |
||||
} |
||||
} |
||||
|
||||
function main(): void |
||||
{ |
||||
} |
||||
@ -0,0 +1,12 @@ |
||||
<?php |
||||
|
||||
class OptionalBeforeRequired |
||||
{ |
||||
public function method($first = 1, $second): void |
||||
{ |
||||
} |
||||
} |
||||
|
||||
function main(): void |
||||
{ |
||||
} |
||||
@ -0,0 +1,10 @@ |
||||
<?php |
||||
|
||||
function target(int $a, int $b): void |
||||
{ |
||||
} |
||||
|
||||
function main(): void |
||||
{ |
||||
target(...[1], 2); |
||||
} |
||||
@ -0,0 +1,13 @@ |
||||
<?php |
||||
|
||||
class Target |
||||
{ |
||||
public function __construct(int $a, int $b) |
||||
{ |
||||
} |
||||
} |
||||
|
||||
function main(): void |
||||
{ |
||||
new Target(...[1], 2); |
||||
} |
||||
@ -0,0 +1,24 @@ |
||||
<?php |
||||
|
||||
use native_types; |
||||
|
||||
class NullsafeNestedOwner |
||||
{ |
||||
public NullsafeNestedChild $child; |
||||
|
||||
public function __construct() |
||||
{ |
||||
$this->child = new NullsafeNestedChild(); |
||||
} |
||||
} |
||||
|
||||
class NullsafeNestedChild |
||||
{ |
||||
private int $value = 1; |
||||
} |
||||
|
||||
function main(): void |
||||
{ |
||||
$owner = new NullsafeNestedOwner(); |
||||
var_dump($owner?->child?->value); |
||||
} |
||||
@ -0,0 +1,14 @@ |
||||
<?php |
||||
|
||||
use native_types; |
||||
|
||||
class NullsafePrivateOwner |
||||
{ |
||||
private int $value = 1; |
||||
} |
||||
|
||||
function main(): void |
||||
{ |
||||
$owner = new NullsafePrivateOwner(); |
||||
var_dump($owner?->value); |
||||
} |
||||
@ -0,0 +1,12 @@ |
||||
<?php |
||||
|
||||
class NullsafeWriteAssignOp |
||||
{ |
||||
public int $value = 1; |
||||
} |
||||
|
||||
function main(): void |
||||
{ |
||||
$object = new NullsafeWriteAssignOp(); |
||||
$object?->value += 2; |
||||
} |
||||
@ -0,0 +1,13 @@ |
||||
<?php |
||||
|
||||
class NullsafeWriteAssignRefLeft |
||||
{ |
||||
public int $value = 1; |
||||
} |
||||
|
||||
function main(): void |
||||
{ |
||||
$object = new NullsafeWriteAssignRefLeft(); |
||||
$value = 2; |
||||
$object?->value =& $value; |
||||
} |
||||
@ -0,0 +1,12 @@ |
||||
<?php |
||||
|
||||
class NullsafeWriteAssignRefRight |
||||
{ |
||||
public int $value = 1; |
||||
} |
||||
|
||||
function main(): void |
||||
{ |
||||
$object = new NullsafeWriteAssignRefRight(); |
||||
$value =& $object?->value; |
||||
} |
||||
@ -0,0 +1,12 @@ |
||||
<?php |
||||
|
||||
class NullsafeWriteAssign |
||||
{ |
||||
public int $value = 1; |
||||
} |
||||
|
||||
function main(): void |
||||
{ |
||||
$object = new NullsafeWriteAssign(); |
||||
$object?->value = 2; |
||||
} |
||||
@ -0,0 +1,12 @@ |
||||
<?php |
||||
|
||||
class NullsafeWriteInc |
||||
{ |
||||
public int $value = 1; |
||||
} |
||||
|
||||
function main(): void |
||||
{ |
||||
$object = new NullsafeWriteInc(); |
||||
$object?->value++; |
||||
} |
||||
@ -0,0 +1,12 @@ |
||||
<?php |
||||
|
||||
class NullsafeWriteUnset |
||||
{ |
||||
public int $value = 1; |
||||
} |
||||
|
||||
function main(): void |
||||
{ |
||||
$object = new NullsafeWriteUnset(); |
||||
unset($object?->value); |
||||
} |
||||
@ -0,0 +1,9 @@ |
||||
<?php |
||||
|
||||
function test($a = 1, $b, $c): void |
||||
{ |
||||
} |
||||
|
||||
function main(): void |
||||
{ |
||||
} |
||||
@ -0,0 +1,21 @@ |
||||
<?php |
||||
|
||||
class ParentConstructorArgument |
||||
{ |
||||
public function __construct() |
||||
{ |
||||
} |
||||
} |
||||
|
||||
class ChildConstructorArgument extends ParentConstructorArgument |
||||
{ |
||||
public function __construct() |
||||
{ |
||||
var_dump(parent::__construct()); |
||||
} |
||||
} |
||||
|
||||
function main(): void |
||||
{ |
||||
new ChildConstructorArgument(); |
||||
} |
||||
@ -0,0 +1,21 @@ |
||||
<?php |
||||
|
||||
class ParentConstructorValue |
||||
{ |
||||
public function __construct() |
||||
{ |
||||
} |
||||
} |
||||
|
||||
class ChildConstructorValue extends ParentConstructorValue |
||||
{ |
||||
public function __construct() |
||||
{ |
||||
$ret = parent::__construct(); |
||||
} |
||||
} |
||||
|
||||
function main(): void |
||||
{ |
||||
new ChildConstructorValue(); |
||||
} |
||||
@ -0,0 +1,4 @@ |
||||
<?php |
||||
abstract class PreprocessorAbstractSignature { |
||||
abstract public function load(int $id, ?string $name = null): self; |
||||
} |
||||
@ -0,0 +1,4 @@ |
||||
<?php |
||||
class PreprocessorClassConstants { |
||||
public const TEXT = 'value', ITEMS = []; |
||||
} |
||||
@ -0,0 +1,2 @@ |
||||
<?php |
||||
class SortDependencyClass implements SortDependencyInterface {} |
||||
@ -0,0 +1,4 @@ |
||||
<?php |
||||
class SortDependencyTraitUser { |
||||
use SortDependencyTrait; |
||||
} |
||||
@ -0,0 +1,2 @@ |
||||
<?php |
||||
interface SortDependencyInterface {} |
||||
@ -0,0 +1,2 @@ |
||||
<?php |
||||
trait SortDependencyTrait {} |
||||
@ -0,0 +1,5 @@ |
||||
<?php |
||||
abstract class PreprocessorDuplicateAbstractMethod { |
||||
abstract public function load(): void; |
||||
abstract public function load(): void; |
||||
} |
||||
@ -0,0 +1,5 @@ |
||||
<?php |
||||
class PreprocessorDuplicateClassConstant { |
||||
public const VALUE = 1; |
||||
public const VALUE = 2; |
||||
} |
||||
@ -0,0 +1,4 @@ |
||||
<?php |
||||
namespace App; |
||||
interface Demo {} |
||||
class Demo {} |
||||
@ -0,0 +1,7 @@ |
||||
<?php |
||||
interface ParentA {} |
||||
interface ParentB {} |
||||
interface Demo extends ParentA, ParentB { |
||||
public const VERSION = '1.0'; |
||||
public function run(int $id, ?string $name = null): ParentA|ParentB; |
||||
} |
||||
@ -0,0 +1,3 @@ |
||||
<?php |
||||
namespace App; |
||||
const VERSION = 100; |
||||
@ -0,0 +1,10 @@ |
||||
<?php |
||||
trait AliasModifierTrait { |
||||
public function hello() {} |
||||
} |
||||
|
||||
class AliasModifierUser { |
||||
use AliasModifierTrait { |
||||
hello as private; |
||||
} |
||||
} |
||||
@ -0,0 +1,6 @@ |
||||
<?php |
||||
|
||||
function test() |
||||
{ |
||||
throw 1; |
||||
} |
||||
@ -0,0 +1,14 @@ |
||||
<?php |
||||
trait TraitConstantConflict |
||||
{ |
||||
public const VALUE = 1; |
||||
} |
||||
|
||||
class TraitConstantConflictUser |
||||
{ |
||||
use TraitConstantConflict; |
||||
|
||||
public const VALUE = 2; |
||||
} |
||||
|
||||
function main() {} |
||||
@ -0,0 +1,16 @@ |
||||
<?php |
||||
trait TraitMemberCompatible |
||||
{ |
||||
public const VALUE = 1; |
||||
public int $count = 1; |
||||
} |
||||
|
||||
class TraitMemberCompatibleUser |
||||
{ |
||||
use TraitMemberCompatible; |
||||
|
||||
public const VALUE = 1; |
||||
public int $count = 1; |
||||
} |
||||
|
||||
function main() {} |
||||
@ -0,0 +1,14 @@ |
||||
<?php |
||||
trait TraitPropertyConflict |
||||
{ |
||||
public int $count = 1; |
||||
} |
||||
|
||||
class TraitPropertyConflictUser |
||||
{ |
||||
use TraitPropertyConflict; |
||||
|
||||
public int $count = 2; |
||||
} |
||||
|
||||
function main() {} |
||||
@ -0,0 +1,12 @@ |
||||
<?php |
||||
|
||||
class BaseParentUnion |
||||
{ |
||||
} |
||||
|
||||
class DemoParentUnion extends BaseParentUnion |
||||
{ |
||||
public function run(parent|string $value): void {} |
||||
} |
||||
|
||||
function main() {} |
||||
@ -0,0 +1,8 @@ |
||||
<?php |
||||
|
||||
class DemoSelfUnion |
||||
{ |
||||
public function run(self|string $value): void {} |
||||
} |
||||
|
||||
function main() {} |
||||
@ -0,0 +1,10 @@ |
||||
<?php |
||||
|
||||
function target($a, $b): void |
||||
{ |
||||
} |
||||
|
||||
function main(): void |
||||
{ |
||||
target(a: 1, ...[2]); |
||||
} |
||||
@ -0,0 +1,9 @@ |
||||
<?php |
||||
|
||||
function collect(&...$args): void |
||||
{ |
||||
} |
||||
|
||||
function main(): void |
||||
{ |
||||
} |
||||
@ -0,0 +1,21 @@ |
||||
<?php |
||||
|
||||
class VoidExpressionArrayParent |
||||
{ |
||||
public function __construct() |
||||
{ |
||||
} |
||||
} |
||||
|
||||
class VoidExpressionArrayChild extends VoidExpressionArrayParent |
||||
{ |
||||
public function __construct() |
||||
{ |
||||
$value = [parent::__construct()]; |
||||
} |
||||
} |
||||
|
||||
function main(): void |
||||
{ |
||||
new VoidExpressionArrayChild(); |
||||
} |
||||
@ -0,0 +1,21 @@ |
||||
<?php |
||||
|
||||
class VoidExpressionBinaryParent |
||||
{ |
||||
public function __construct() |
||||
{ |
||||
} |
||||
} |
||||
|
||||
class VoidExpressionBinaryChild extends VoidExpressionBinaryParent |
||||
{ |
||||
public function __construct() |
||||
{ |
||||
$value = parent::__construct() + 1; |
||||
} |
||||
} |
||||
|
||||
function main(): void |
||||
{ |
||||
new VoidExpressionBinaryChild(); |
||||
} |
||||
@ -0,0 +1,23 @@ |
||||
<?php |
||||
|
||||
class VoidExpressionConditionParent |
||||
{ |
||||
public function __construct() |
||||
{ |
||||
} |
||||
} |
||||
|
||||
class VoidExpressionConditionChild extends VoidExpressionConditionParent |
||||
{ |
||||
public function __construct() |
||||
{ |
||||
if (parent::__construct()) { |
||||
echo "unreachable\n"; |
||||
} |
||||
} |
||||
} |
||||
|
||||
function main(): void |
||||
{ |
||||
new VoidExpressionConditionChild(); |
||||
} |
||||
@ -0,0 +1,23 @@ |
||||
<?php |
||||
|
||||
class VoidExpressionMatchParent |
||||
{ |
||||
public function __construct() |
||||
{ |
||||
} |
||||
} |
||||
|
||||
class VoidExpressionMatchChild extends VoidExpressionMatchParent |
||||
{ |
||||
public function __construct() |
||||
{ |
||||
$value = match (1) { |
||||
1 => parent::__construct(), |
||||
}; |
||||
} |
||||
} |
||||
|
||||
function main(): void |
||||
{ |
||||
new VoidExpressionMatchChild(); |
||||
} |
||||
@ -0,0 +1,21 @@ |
||||
<?php |
||||
|
||||
class VoidExpressionTernaryParent |
||||
{ |
||||
public function __construct() |
||||
{ |
||||
} |
||||
} |
||||
|
||||
class VoidExpressionTernaryChild extends VoidExpressionTernaryParent |
||||
{ |
||||
public function __construct() |
||||
{ |
||||
$value = true ? parent::__construct() : 1; |
||||
} |
||||
} |
||||
|
||||
function main(): void |
||||
{ |
||||
new VoidExpressionTernaryChild(); |
||||
} |
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue