- Add ATTR_SCOPED_CALLBACK attribute to track callback scope requirements - Introduce getCallableScopeExpr method to generate proper scope expressions - Modify call argument generation to wrap callbacks with scope information - Update dynamic method call logic to preserve declaring class scope - Replace legacy markRuntimeObjectMethodCall with markUnpackedScopedCallbackCall - Add support for scoped callbacks in array functions like array_map - Implement proper scope handling for nullsafe method calls - Generate scoped callable wrappers in placeholder generator - Add comprehensive tests for dynamic method lexical scope behavior - Add tests for internal callback functions preserving method scopepull/48/head
parent
f5ca997e65
commit
8e48b9d7fe
7 changed files with 209 additions and 83 deletions
@ -0,0 +1,53 @@ |
|||||||
|
--TEST-- |
||||||
|
Dynamic method calls use the declaring class scope |
||||||
|
--FILE-- |
||||||
|
<?php |
||||||
|
|
||||||
|
class DynamicScopeBase |
||||||
|
{ |
||||||
|
private function privateValue(): string |
||||||
|
{ |
||||||
|
return 'base-private'; |
||||||
|
} |
||||||
|
|
||||||
|
public function callPrivate(): string |
||||||
|
{ |
||||||
|
$method = 'privateValue'; |
||||||
|
return $this->$method(); |
||||||
|
} |
||||||
|
|
||||||
|
public function callSubclassHook(): string |
||||||
|
{ |
||||||
|
return $this->subclassHook(); |
||||||
|
} |
||||||
|
|
||||||
|
public function callPrivateCallback(): array |
||||||
|
{ |
||||||
|
return array_map([$this, 'privateValue'], [null]); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
class DynamicScopeChild extends DynamicScopeBase |
||||||
|
{ |
||||||
|
protected function subclassHook(): string |
||||||
|
{ |
||||||
|
return 'child-protected'; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
function main(): void |
||||||
|
{ |
||||||
|
$object = new DynamicScopeChild(); |
||||||
|
var_dump($object->callPrivate()); |
||||||
|
var_dump($object->callSubclassHook()); |
||||||
|
var_dump($object->callPrivateCallback()); |
||||||
|
} |
||||||
|
|
||||||
|
?> |
||||||
|
--EXPECT-- |
||||||
|
string(12) "base-private" |
||||||
|
string(15) "child-protected" |
||||||
|
array(1) { |
||||||
|
[0]=> |
||||||
|
string(12) "base-private" |
||||||
|
} |
||||||
@ -0,0 +1,73 @@ |
|||||||
|
--TEST-- |
||||||
|
Internal callback functions preserve the declaring method scope |
||||||
|
--FILE-- |
||||||
|
<?php |
||||||
|
|
||||||
|
class ScopedInternalCallbacks |
||||||
|
{ |
||||||
|
private function triple(int $value): int |
||||||
|
{ |
||||||
|
return $value * 3; |
||||||
|
} |
||||||
|
|
||||||
|
private static function replace(array $match): string |
||||||
|
{ |
||||||
|
return strtoupper($match[0]); |
||||||
|
} |
||||||
|
|
||||||
|
private static function compareValue(mixed $left, mixed $right): int |
||||||
|
{ |
||||||
|
return $left <=> $right; |
||||||
|
} |
||||||
|
|
||||||
|
private static function compareKey(mixed $left, mixed $right): int |
||||||
|
{ |
||||||
|
return $left <=> $right; |
||||||
|
} |
||||||
|
|
||||||
|
public function run(): void |
||||||
|
{ |
||||||
|
var_dump(array_map([$this, 'triple'], [1, 2, 3])); |
||||||
|
var_dump(array_map(array: [4, 5], callback: [$this, 'triple'])); |
||||||
|
var_dump(preg_replace_callback_array([ |
||||||
|
'/a+/' => [self::class, 'replace'], |
||||||
|
], 'caaab')); |
||||||
|
var_dump(array_udiff_uassoc( |
||||||
|
['a' => 1, 'b' => 2], |
||||||
|
['a' => 1, 'c' => 2], |
||||||
|
[self::class, 'compareValue'], |
||||||
|
[self::class, 'compareKey'], |
||||||
|
)); |
||||||
|
|
||||||
|
$unpacked = [[$this, 'triple'], 6]; |
||||||
|
var_dump(call_user_func(...$unpacked)); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
function main(): void |
||||||
|
{ |
||||||
|
(new ScopedInternalCallbacks())->run(); |
||||||
|
} |
||||||
|
|
||||||
|
?> |
||||||
|
--EXPECT-- |
||||||
|
array(3) { |
||||||
|
[0]=> |
||||||
|
int(3) |
||||||
|
[1]=> |
||||||
|
int(6) |
||||||
|
[2]=> |
||||||
|
int(9) |
||||||
|
} |
||||||
|
array(2) { |
||||||
|
[0]=> |
||||||
|
int(12) |
||||||
|
[1]=> |
||||||
|
int(15) |
||||||
|
} |
||||||
|
string(5) "cAAAb" |
||||||
|
array(1) { |
||||||
|
["b"]=> |
||||||
|
int(2) |
||||||
|
} |
||||||
|
int(18) |
||||||
Loading…
Reference in new issue