- 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