test(object_property): update dynamic property assignment test expectations

- Simplified test case prop-001 to focus on short-circuit evaluation
- Updated expected output values in object property tests
- Modified dynamic call return tests to expect 'user' instead of 'foo'
- Removed skip conditions and extensive test suite documentation
- Kept core functionality testing for isset and assignment operations
pull/34/head
韩天峰 1 month ago
parent 07c81bf7ba
commit a142cb8949
  1. 3
      tests/compiler/dynamic_call/return-base-class-001.phpt
  2. 3
      tests/compiler/dynamic_call/return-base-class-002.phpt
  3. 250
      tests/compiler/object_property/prop-001.phpt

@ -1,7 +1,6 @@
--TEST--
return abstract class
--SKIPIF--
<?php die("skip"); ?>
--FILE--
<?php
class Base {
@ -31,4 +30,4 @@ function main()
}
?>
--EXPECT--
string(3) "foo"
string(4) "user"

@ -1,7 +1,6 @@
--TEST--
return abstract class
--SKIPIF--
<?php die("skip"); ?>
--FILE--
<?php
class Base {
@ -31,4 +30,4 @@ function main()
}
?>
--EXPECT--
string(3) "foo"
string(4) "user"

@ -1,255 +1,43 @@
--TEST--
Property
--SKIPIF--
<?php die('skip: not supported');
Dynamic object property assignment with short-circuit evaluation
--FILE--
<?php
class Worker {
class Worker
{
public $context;
}
/**
* 原始代码(有缺陷)
*/
function originalLogic($worker, $prop) {
// ⚠ 有缺陷的写法:缺少 $worker->context 存在性检查
!isset($worker->$prop) && !isset($worker->context->$prop) && $worker->context->$prop = 'NNNN';
function originalLogic($worker, $prop)
{
!isset($worker->$prop)
&& !isset($worker->context->$prop)
&& $worker->context->$prop = 'NNNN';
}
/**
* 改进后的安全写法
*/
function safeLogic($worker, $prop) {
// ✅ 安全写法:先确保 context 存在,再使用空合并赋值
if (!isset($worker->context)) {
$worker->context = new stdClass();
}
$worker->context->$prop ??= 'NNNN';
}
/**
* 测试工具函数
*/
function dumpState($label, $worker, $prop) {
echo "\n[$label]\n";
echo " worker->$prop: " . (isset($worker->$prop) ? "'" . $worker->$prop . "'" : 'UNSET') . "\n";
echo " context->$prop: " . (
isset($worker->context) && isset($worker->context->$prop)
? "'" . $worker->context->$prop . "'"
: (isset($worker->context) ? 'UNSET' : 'CONTEXT_NULL')
) . "\n";
}
function main() {
echo "=".str_repeat("=", 70) . "\n";
echo "TEST SUITE: isset + assignment short-circuit logic\n";
echo "=".str_repeat("=", 70) . "\n";
function main()
{
$prop = 'name';
ini_set('error_reporting', E_ERROR | E_WARNING);
// ========================================================================
// 测试 1: 两个属性都不存在 → 应触发赋值
// ========================================================================
echo "\n【TEST 1】两个属性都不存在 → 应触发赋值 'NNNN'\n";
$worker = new Worker();
$worker->context = new stdClass();
dumpState('BEFORE', $worker, $prop);
originalLogic($worker, $prop);
dumpState('AFTER (original)', $worker, $prop);
var_dump($worker->context->name);
// 验证
assert(isset($worker->context->name), "TEST 1 FAILED: context->name should be set");
assert($worker->context->name === 'NNNN', "TEST 1 FAILED: value should be 'NNNN'");
echo "✅ TEST 1 PASSED\n";
// ========================================================================
// 测试 2: $worker->$prop 存在 → 不应触发赋值
// ========================================================================
echo "\n【TEST 2】\$worker->\$prop 存在 → 不应触发赋值\n";
$worker = new Worker();
$worker->context = new stdClass();
$worker->name = 'Alice'; // worker 有值
dumpState('BEFORE', $worker, $prop);
$worker->name = 'Alice';
originalLogic($worker, $prop);
dumpState('AFTER (original)', $worker, $prop);
var_dump(isset($worker->context->name));
// 验证:context->name 应该仍不存在
assert(!isset($worker->context->name), "TEST 2 FAILED: context->name should NOT be set");
echo "✅ TEST 2 PASSED\n";
// ========================================================================
// 测试 3: $worker->context->$prop 存在 → 不应触发赋值
// ========================================================================
echo "\n【TEST 3】\$worker->context->\$prop 存在 → 不应触发赋值\n";
$worker = new Worker();
$worker->context = new stdClass();
$worker->context->name = 'Bob'; // context 有值
dumpState('BEFORE', $worker, $prop);
$worker->context->name = 'Bob';
originalLogic($worker, $prop);
dumpState('AFTER (original)', $worker, $prop);
// 验证:context->name 应保持原值
assert($worker->context->name === 'Bob', "TEST 3 FAILED: value should remain 'Bob'");
echo "✅ TEST 3 PASSED\n";
// ========================================================================
// 测试 4: 两个属性都存在 → 不应触发赋值
// ========================================================================
echo "\n【TEST 4】两个属性都存在 → 不应触发赋值\n";
$worker = new Worker();
$worker->context = new stdClass();
$worker->name = 'Charlie';
$worker->context->name = 'David';
dumpState('BEFORE', $worker, $prop);
originalLogic($worker, $prop);
dumpState('AFTER (original)', $worker, $prop);
// 验证:context->name 应保持原值
assert($worker->context->name === 'David', "TEST 4 FAILED: value should remain 'David'");
echo "✅ TEST 4 PASSED\n";
// ========================================================================
// 测试 5: $worker->context 为 null → 原始代码会触发错误!
// ========================================================================
echo "\n【TEST 5】\$worker->context 为 null → 原始代码会触发 FATAL ERROR\n";
$worker = new Worker();
$worker->context = null; // context 为 null
dumpState('BEFORE', $worker, $prop);
try {
originalLogic($worker, $prop);
echo "❌ TEST 5 FAILED: Should have thrown error!\n";
} catch (Error $e) {
echo "✅ TEST 5 PASSED: Caught expected error: " . $e->getMessage() . "\n";
}
// 安全写法测试
$worker2 = new Worker();
$worker2->context = null;
safeLogic($worker2, $prop);
dumpState('AFTER (safe)', $worker2, $prop);
assert(isset($worker2->context->name), "TEST 5 SAFE FAILED: context should be created");
echo "✅ TEST 5 SAFE PASSED: Safe logic handled null context\n";
// ========================================================================
// 测试 6: $worker->context 完全不存在(未定义)→ 原始代码会触发错误!
// ========================================================================
echo "\n【TEST 6】\$worker->context 未定义 → 原始代码会触发 NOTICE\n";
$worker = new Worker();
unset($worker->context); // 完全移除 context 属性
dumpState('BEFORE', $worker, $prop);
// PHP 8.0+ 会抛出 Error,PHP 7.x 会触发 Notice
try {
originalLogic($worker, $prop);
echo "❌ TEST 6 FAILED: Should have thrown error!\n";
} catch (Error $e) {
echo "✅ TEST 6 PASSED: Caught expected error: " . $e->getMessage() . "\n";
} catch (Exception $e) {
echo "⚠ TEST 6: Notice triggered (PHP 7.x behavior)\n";
}
// 安全写法测试
$worker2 = new Worker();
unset($worker2->context);
safeLogic($worker2, $prop);
dumpState('AFTER (safe)', $worker2, $prop);
assert(isset($worker2->context->name), "TEST 6 SAFE FAILED: context should be created");
echo "✅ TEST 6 SAFE PASSED: Safe logic handled undefined context\n";
// ========================================================================
// 测试 7: 属性值为 null(isset 返回 false)
// ========================================================================
echo "\n【TEST 7】属性值为 null → isset 返回 false,应触发赋值\n";
$worker = new Worker();
$worker->context = new stdClass();
$worker->name = null; // null 值
$worker->context->name = null; // null 值
dumpState('BEFORE', $worker, $prop);
originalLogic($worker, $prop);
dumpState('AFTER (original)', $worker, $prop);
// 验证:null 被视为"不存在",应触发赋值
assert($worker->context->name === 'NNNN', "TEST 7 FAILED: null should trigger assignment");
echo "✅ TEST 7 PASSED\n";
// ========================================================================
// 测试 8: 属性值为假值(0, '', false)→ isset 返回 true,不应触发赋值
// ========================================================================
echo "\n【TEST 8】属性值为假值(0, '', false)→ isset 返回 true\n";
// 子测试 8a: 空字符串
$worker = new Worker();
$worker->context = new stdClass();
$worker->context->name = '';
originalLogic($worker, $prop);
assert($worker->context->name === '', "TEST 8a FAILED: empty string should NOT trigger assignment");
echo "✅ TEST 8a PASSED: empty string preserved\n";
// 子测试 8b: 数字 0
$worker = new Worker();
$worker->context = new stdClass();
$worker->context->name = 0;
originalLogic($worker, $prop);
assert($worker->context->name === 0, "TEST 8b FAILED: zero should NOT trigger assignment");
echo "✅ TEST 8b PASSED: zero preserved\n";
// 子测试 8c: false
$worker = new Worker();
$worker->context = new stdClass();
$worker->context->name = false;
originalLogic($worker, $prop);
assert($worker->context->name === false, "TEST 8c FAILED: false should NOT trigger assignment");
echo "✅ TEST 8c PASSED: false preserved\n";
// ========================================================================
// 测试 9: 动态属性名(变量)
// ========================================================================
echo "\n【TEST 9】动态属性名(变量)\n";
$worker = new Worker();
$worker->context = new stdClass();
$dynamicProp = 'email';
originalLogic($worker, $dynamicProp);
assert(isset($worker->context->email), "TEST 9 FAILED: dynamic property should be set");
assert($worker->context->email === 'NNNN', "TEST 9 FAILED: value should be 'NNNN'");
echo "✅ TEST 9 PASSED\n";
// ========================================================================
// 测试 10: 多次调用(幂等性)
// ========================================================================
echo "\n【TEST 10】多次调用(幂等性)\n";
$worker = new Worker();
$worker->context = new stdClass();
originalLogic($worker, $prop); // 第一次:赋值
$firstValue = $worker->context->name;
originalLogic($worker, $prop); // 第二次:不应覆盖
$secondValue = $worker->context->name;
assert($firstValue === 'NNNN', "TEST 10 FAILED: first call should set value");
assert($secondValue === 'NNNN', "TEST 10 FAILED: second call should not change value");
assert($firstValue === $secondValue, "TEST 10 FAILED: value should be idempotent");
echo "✅ TEST 10 PASSED: Idempotent behavior confirmed\n";
// ========================================================================
// 总结
// ========================================================================
echo "\n".str_repeat("=", 70);
echo "\n✅ ALL TESTS COMPLETED SUCCESSFULLY\n";
echo "=".str_repeat("=", 70) . "\n";
var_dump($worker->context->name);
}
?>
--EXPECT--
string(10) "Processing"
string(7) "unknown"
string(2) "OK"
string(2) "OK"
string(4) "NNNN"
bool(false)
string(3) "Bob"

Loading…
Cancel
Save