- Extended BinaryOpTrait to handle temporary variable cleanup for STR, ARRAY, and OBJECT types - Added parseDynamicClassConstNameFetch method to handle dynamic constant name resolution - Implemented proper evaluation order for class target and dynamic constant name - Added support for static, self, parent, and regular class references in dynamic context - Created materializeDynamicClassConstOperand helper for operand processing - Updated temporary variable management to clear zval-owning PHPX wrappers - Added comprehensive tests for dynamic class constant name evaluation semantics - Implemented proper object lifetime management for temporary arguments - Added tests for reference-returning function alias preservation - Fixed temporary call argument lifetime handling in object constructorspull/48/head
parent
8aa8cdb065
commit
01ea7c40be
5 changed files with 210 additions and 4 deletions
@ -0,0 +1,75 @@ |
||||
--TEST-- |
||||
dynamic class constant names preserve PHP lookup and evaluation semantics |
||||
--FILE-- |
||||
<?php |
||||
|
||||
class DynamicConstantName |
||||
{ |
||||
public const ANSWER = 42; |
||||
private const SECRET = 'secret'; |
||||
|
||||
public static function secret(string $name): string |
||||
{ |
||||
return self::{$name}; |
||||
} |
||||
} |
||||
|
||||
enum DynamicConstantCase |
||||
{ |
||||
case READY; |
||||
} |
||||
|
||||
function dynamic_constant_target(): string |
||||
{ |
||||
echo "target\n"; |
||||
return DynamicConstantName::class; |
||||
} |
||||
|
||||
function dynamic_constant_name(): string |
||||
{ |
||||
echo "name\n"; |
||||
return 'ANSWER'; |
||||
} |
||||
|
||||
function main(): void |
||||
{ |
||||
$name = 'ANSWER'; |
||||
var_dump(DynamicConstantName::{$name}); |
||||
|
||||
var_dump(dynamic_constant_target()::{dynamic_constant_name()}); |
||||
|
||||
$className = 'class'; |
||||
var_dump(DynamicConstantName::{$className}); |
||||
$className = 'CLASS'; |
||||
var_dump(DynamicConstantName::{$className}); |
||||
|
||||
$case = 'READY'; |
||||
var_dump(DynamicConstantCase::{$case} === DynamicConstantCase::READY); |
||||
|
||||
$secret = 'SECRET'; |
||||
var_dump(DynamicConstantName::secret($secret)); |
||||
try { |
||||
DynamicConstantName::{$secret}; |
||||
} catch (Error $error) { |
||||
echo $error->getMessage(), "\n"; |
||||
} |
||||
|
||||
$invalid = 1; |
||||
try { |
||||
DynamicConstantName::{$invalid}; |
||||
} catch (TypeError $error) { |
||||
echo $error->getMessage(), "\n"; |
||||
} |
||||
} |
||||
?> |
||||
--EXPECT-- |
||||
int(42) |
||||
target |
||||
name |
||||
int(42) |
||||
string(19) "DynamicConstantName" |
||||
string(19) "DynamicConstantName" |
||||
bool(true) |
||||
string(6) "secret" |
||||
Cannot access private constant DynamicConstantName::SECRET |
||||
Cannot use value of type int as class constant name |
||||
@ -0,0 +1,36 @@ |
||||
--TEST-- |
||||
Owned call argument temporaries are released at the end of the call statement |
||||
--FILE-- |
||||
<?php |
||||
|
||||
final class TemporaryArgumentLifetimeProbe |
||||
{ |
||||
public static int $destroyed = 0; |
||||
|
||||
public function __destruct() |
||||
{ |
||||
self::$destroyed++; |
||||
} |
||||
} |
||||
|
||||
function consume_temporary_object(TemporaryArgumentLifetimeProbe $value): void |
||||
{ |
||||
} |
||||
|
||||
function consume_temporary_array(array $values): void |
||||
{ |
||||
} |
||||
|
||||
function main(): void |
||||
{ |
||||
TemporaryArgumentLifetimeProbe::$destroyed = 0; |
||||
consume_temporary_object(new TemporaryArgumentLifetimeProbe()); |
||||
echo 'object=', TemporaryArgumentLifetimeProbe::$destroyed, "\n"; |
||||
|
||||
consume_temporary_array([new TemporaryArgumentLifetimeProbe()]); |
||||
echo 'array=', TemporaryArgumentLifetimeProbe::$destroyed, "\n"; |
||||
} |
||||
?> |
||||
--EXPECT-- |
||||
object=1 |
||||
array=2 |
||||
@ -0,0 +1,50 @@ |
||||
--TEST-- |
||||
Reference-returning functions preserve aliases to nested array elements and object properties |
||||
--FILE-- |
||||
<?php |
||||
|
||||
final class NestedReferenceBox |
||||
{ |
||||
public string $value = 'object-before'; |
||||
} |
||||
|
||||
function &array_element_ref(array &$values): mixed |
||||
{ |
||||
return $values['item']; |
||||
} |
||||
|
||||
function &nested_array_element_ref(array &$values): mixed |
||||
{ |
||||
return $values['outer']['inner']; |
||||
} |
||||
|
||||
function &object_property_ref(NestedReferenceBox $box): mixed |
||||
{ |
||||
return $box->value; |
||||
} |
||||
|
||||
function main(): void |
||||
{ |
||||
$values = [ |
||||
'item' => 'before', |
||||
'outer' => ['inner' => 'nested-before'], |
||||
]; |
||||
|
||||
$item =& array_element_ref($values); |
||||
$item = 'after'; |
||||
var_dump($values['item']); |
||||
|
||||
$inner =& nested_array_element_ref($values); |
||||
$inner = 'nested-after'; |
||||
var_dump($values['outer']['inner']); |
||||
|
||||
$box = new NestedReferenceBox(); |
||||
$property =& object_property_ref($box); |
||||
$property = 'object-after'; |
||||
var_dump($box->value); |
||||
} |
||||
?> |
||||
--EXPECT-- |
||||
string(5) "after" |
||||
string(12) "nested-after" |
||||
string(12) "object-after" |
||||
Loading…
Reference in new issue