- 实现了 NullsafePropertyFetch 和 NullsafeMethodCall 的解析支持 - 添加了 parseNullsafePropertyFetch 和 parseNullsafeMethodCall 方法 - 实现了 nullsafe 表达式的完整解析逻辑,包括属性和方法调用链式访问 - 更新了 PropertyDef 类以支持可空类型标记 - 修改了 getNativeMethod 和 findNativeMethod 方法参数类型为 CallLike - 添加了完整的测试用例覆盖 nullsafe 操作符的各种使用场景pull/1/head
parent
f12ceb1713
commit
dd46d6fa5f
6 changed files with 238 additions and 5 deletions
@ -0,0 +1,21 @@ |
||||
--TEST-- |
||||
Nullsafe operator - method and property access |
||||
--FILE-- |
||||
<?php |
||||
class User { |
||||
public function getName(): string { |
||||
return 'Alice'; |
||||
} |
||||
} |
||||
|
||||
function main() { |
||||
$user1 = null; |
||||
var_dump($user1?->getName()); |
||||
$user2 = new User(); |
||||
var_dump($user2?->getName()); |
||||
} |
||||
?> |
||||
--EXPECT-- |
||||
NULL |
||||
string(5) "Alice" |
||||
|
||||
@ -0,0 +1,89 @@ |
||||
--TEST-- |
||||
Nullsafe operator - method and property access |
||||
--FILE-- |
||||
<?php |
||||
class Customer { |
||||
public function __construct( |
||||
public ?Address $address = null, |
||||
public string $name = "John" |
||||
) {} |
||||
|
||||
public function getCountry(): ?string { |
||||
return $this->address?->country ?? 'Unknown'; |
||||
} |
||||
} |
||||
|
||||
class Address { |
||||
public function __construct( |
||||
public string $street = "Main St", |
||||
public string $city = "NYC", |
||||
public string $country = "USA" |
||||
) {} |
||||
|
||||
public function getFullAddress(): string { |
||||
return "{$this->street}, {$this->city}"; |
||||
} |
||||
} |
||||
|
||||
// Test 6: Nullsafe with method returning object |
||||
class ChainTest { |
||||
public ?Child $child = null; |
||||
|
||||
public function getChild(): ?Child { |
||||
return $this->child; |
||||
} |
||||
} |
||||
|
||||
class Child { |
||||
public string $name = "Alice"; |
||||
|
||||
public function getName(): string { |
||||
return $this->name; |
||||
} |
||||
} |
||||
|
||||
function main() { |
||||
// Test 1: Nullsafe method call on non-null object |
||||
$customer1 = new Customer(new Address("5th Avenue", "New York", "USA")); |
||||
echo $customer1->address?->getFullAddress() . "\n"; |
||||
|
||||
// Test 2: Nullsafe method call on null object |
||||
$customer2 = new Customer(null); |
||||
var_dump($customer2->address?->getFullAddress()); |
||||
|
||||
// Test 3: Nullsafe property access on non-null object |
||||
echo $customer1->address?->city . "\n"; |
||||
|
||||
// Test 4: Nullsafe property access on null object |
||||
var_dump($customer2->address?->city); |
||||
|
||||
// Test 5: Chained nullsafe calls |
||||
echo strtoupper($customer1?->address?->getFullAddress()); |
||||
|
||||
$test = new ChainTest(); |
||||
$test->child = new Child(); |
||||
|
||||
// Test 7: Chained nullsafe method calls |
||||
echo $test->getChild()?->getName() . "\n"; |
||||
|
||||
// Test 8: Chained nullsafe with null in middle |
||||
$test2 = new ChainTest(); |
||||
var_dump($test2->getChild()?->getName()); |
||||
|
||||
// Test 9: Nullsafe property access in expression |
||||
$value = $customer1->address?->street ?? 'No street'; |
||||
echo $value . "\n"; |
||||
|
||||
// Test 10: Multiple nullsafe in same expression |
||||
echo ($customer1->address?->city ?? 'Unknown') . ", " . ($customer1->address?->country ?? 'Unknown') . "\n"; |
||||
} |
||||
?> |
||||
--EXPECT-- |
||||
5th Avenue, New York |
||||
NULL |
||||
New York |
||||
NULL |
||||
5TH AVENUE, NEW YORKAlice |
||||
NULL |
||||
5th Avenue |
||||
New York, USA |
||||
@ -0,0 +1,19 @@ |
||||
--TEST-- |
||||
Nullsafe operator - method and property access |
||||
--FILE-- |
||||
<?php |
||||
class User { |
||||
public ?stdClass $prop = null; |
||||
} |
||||
|
||||
function main() { |
||||
$user = new User(); |
||||
var_dump($user?->prop?->data); |
||||
$user->prop = new stdClass(); |
||||
$user->prop->data = 'hello'; |
||||
var_dump($user?->prop?->data); |
||||
} |
||||
?> |
||||
--EXPECT-- |
||||
NULL |
||||
string(5) "hello" |
||||
@ -0,0 +1,28 @@ |
||||
--TEST-- |
||||
Nullsafe operator - method and property access |
||||
--FILE-- |
||||
<?php |
||||
class User1 { |
||||
public function getName(): string { |
||||
return 'Alice'; |
||||
} |
||||
} |
||||
|
||||
class User2 { |
||||
public ?User1 $user = null; |
||||
public function getUser() { |
||||
return $this->user; |
||||
} |
||||
} |
||||
|
||||
function main() { |
||||
$user = new User2(); |
||||
var_dump($user?->getUser()?->getName()); |
||||
|
||||
$user->user = new User1(); |
||||
var_dump($user?->getUser()?->getName()); |
||||
} |
||||
?> |
||||
--EXPECT-- |
||||
NULL |
||||
string(5) "Alice" |
||||
Loading…
Reference in new issue