- Add runtime check mechanism for objects that require value validation - Prevent null assignment to typed objects after unset with proper error message - Disable native call optimizations when object values become unpredictable - Maintain class constraints even after unset and reassignment - Generate appropriate error handling for invalid object operations - Update SSA analysis to recognize unset operations as dangerous for property hoisting - Add comprehensive tests for typed object unset behavior scenariospull/47/head
parent
a4f1188a7e
commit
70927ee68a
17 changed files with 232 additions and 11 deletions
@ -0,0 +1,22 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
use native_types; |
||||||
|
|
||||||
|
class NativePropertyUnsetDisablesHoist |
||||||
|
{ |
||||||
|
public int $value = 7; |
||||||
|
|
||||||
|
public function run(): void |
||||||
|
{ |
||||||
|
var_dump($this->value); |
||||||
|
unset($this->value); |
||||||
|
var_dump($this->value); |
||||||
|
$this->value = 11; |
||||||
|
var_dump($this->value); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
function main(): void |
||||||
|
{ |
||||||
|
(new NativePropertyUnsetDisablesHoist())->run(); |
||||||
|
} |
||||||
@ -0,0 +1,12 @@ |
|||||||
|
<?php |
||||||
|
|
||||||
|
class TypedObjectUnsetNullValue |
||||||
|
{ |
||||||
|
} |
||||||
|
|
||||||
|
function main(): void |
||||||
|
{ |
||||||
|
$value = new TypedObjectUnsetNullValue(); |
||||||
|
unset($value); |
||||||
|
$value = null; |
||||||
|
} |
||||||
@ -0,0 +1,58 @@ |
|||||||
|
--TEST-- |
||||||
|
unset typed object retains its declared class constraint on reassignment |
||||||
|
--FILE-- |
||||||
|
<?php |
||||||
|
class UnsetReassignExpected |
||||||
|
{ |
||||||
|
public function value(): string |
||||||
|
{ |
||||||
|
return 'expected'; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
class UnsetReassignOther |
||||||
|
{ |
||||||
|
} |
||||||
|
|
||||||
|
function makeUnsetReassignExpected(): UnsetReassignExpected |
||||||
|
{ |
||||||
|
return new UnsetReassignExpected(); |
||||||
|
} |
||||||
|
|
||||||
|
function makeUnsetReassignOtherDynamic(): mixed |
||||||
|
{ |
||||||
|
return new UnsetReassignOther(); |
||||||
|
} |
||||||
|
|
||||||
|
function makeUnsetReassignNullDynamic(): mixed |
||||||
|
{ |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
function main() |
||||||
|
{ |
||||||
|
$value = makeUnsetReassignExpected(); |
||||||
|
unset($value); |
||||||
|
|
||||||
|
try { |
||||||
|
$value = makeUnsetReassignOtherDynamic(); |
||||||
|
echo "invalid assignment accepted\n"; |
||||||
|
} catch (Throwable $error) { |
||||||
|
echo $error::class, "\n"; |
||||||
|
} |
||||||
|
|
||||||
|
try { |
||||||
|
$value = makeUnsetReassignNullDynamic(); |
||||||
|
echo "null assignment accepted\n"; |
||||||
|
} catch (Throwable $error) { |
||||||
|
echo $error::class, "\n"; |
||||||
|
} |
||||||
|
|
||||||
|
$value = makeUnsetReassignExpected(); |
||||||
|
var_dump($value->value()); |
||||||
|
} |
||||||
|
?> |
||||||
|
--EXPECT-- |
||||||
|
TypeError |
||||||
|
TypeError |
||||||
|
string(8) "expected" |
||||||
@ -0,0 +1,58 @@ |
|||||||
|
--TEST-- |
||||||
|
unset typed object invalidates native-call assumptions and reads as null |
||||||
|
--FILE-- |
||||||
|
<?php |
||||||
|
class UnsetTypedObjectValue |
||||||
|
{ |
||||||
|
public function value(): string |
||||||
|
{ |
||||||
|
return 'value'; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
function makeUnsetTypedObjectValue(): UnsetTypedObjectValue |
||||||
|
{ |
||||||
|
return new UnsetTypedObjectValue(); |
||||||
|
} |
||||||
|
|
||||||
|
function acceptUnsetTypedObjectValue(UnsetTypedObjectValue $value): string |
||||||
|
{ |
||||||
|
echo "entered\n"; |
||||||
|
return $value->value(); |
||||||
|
} |
||||||
|
|
||||||
|
function main() |
||||||
|
{ |
||||||
|
$value = makeUnsetTypedObjectValue(); |
||||||
|
unset($value); |
||||||
|
|
||||||
|
var_dump(@$value === null); |
||||||
|
var_dump(@$value instanceof UnsetTypedObjectValue); |
||||||
|
var_dump(isset($value)); |
||||||
|
|
||||||
|
try { |
||||||
|
acceptUnsetTypedObjectValue(@$value); |
||||||
|
} catch (Throwable $error) { |
||||||
|
echo $error::class, "\n"; |
||||||
|
} |
||||||
|
|
||||||
|
try { |
||||||
|
@$value->value(); |
||||||
|
} catch (Throwable $error) { |
||||||
|
echo $error::class, "\n"; |
||||||
|
} |
||||||
|
|
||||||
|
$value = makeUnsetTypedObjectValue(); |
||||||
|
var_dump(acceptUnsetTypedObjectValue($value)); |
||||||
|
var_dump($value->value()); |
||||||
|
} |
||||||
|
?> |
||||||
|
--EXPECT-- |
||||||
|
bool(true) |
||||||
|
bool(false) |
||||||
|
bool(false) |
||||||
|
TypeError |
||||||
|
Error |
||||||
|
entered |
||||||
|
string(5) "value" |
||||||
|
string(5) "value" |
||||||
Loading…
Reference in new issue