refactor(compiler): replace ExtensionProvider with MethodsFor attribute

- Renamed ExtensionProvider attribute to MethodsFor across all test files
- Updated keyword extension tests to use MethodsFor instead of ExtensionProvider
- Modified universal method extension tests to reflect the attribute name change
- Adjusted stream method extension tests to use new attribute naming
- Changed object extension tests to use MethodsFor attribute
- Updated constant expression validation logic for PHP 8.3+ static initializers
- Added support for dynamic static variable initializers in PHP 8.3 and later
- Removed
pull/34/head
韩天峰 1 month ago
parent 2bc1cc12ad
commit 07c81bf7ba
  1. 9
      phpunit/src/ConstantExpressionValidatorTest.php
  2. 10
      src/Transform/ConstantExpressionValidationVisitor.php
  3. 12
      tests/compiler/attribute/003.phpt
  4. 7
      tests/compiler/attribute/attributes.phpt
  5. 4
      tests/compiler/keyword_extension/001.phpt
  6. 6
      tests/compiler/keyword_extension/camel.phpt
  7. 2
      tests/compiler/stream_method/extension.phpt
  8. 7
      tests/compiler/trait/trait-basic.phpt
  9. 4
      tests/compiler/universal_method/object_extension.phpt
  10. 4
      tests/compiler/universal_method/object_extension_exact_name.phpt
  11. 8
      tests/compiler/universal_method/universal_method_extension.phpt
  12. 6
      tests/compiler/universal_method/universal_method_extension_camel.phpt
  13. 8
      tests/compiler/universal_method/universal_method_extension_chain.phpt
  14. 2
      tests/compiler/universal_method/universal_method_internal.phpt
  15. 2
      tests/compiler/var_convert/002.phpt

@ -173,6 +173,10 @@ final class ConstantExpressionValidatorTest extends PHPUnit\Framework\TestCase
yield 'parameter default allows new' => ['function f($value = new Value()) {}', '8.4'];
yield 'global const allows new' => ['const VALUE = new Value();', '8.4'];
yield 'static variable allows new' => ['function f() { static $value = new Value(); }', '8.4'];
yield 'PHP 8.3 static variable allows dynamic initializer' => [
'function f(int $seed) { static $value = loadValue($seed); }',
'8.3',
];
yield 'PHP 8.5 class constant allows static closure' => [
'class C { const VALUE = static function (): int { return 1; }; }',
'8.5',
@ -198,6 +202,11 @@ final class ConstantExpressionValidatorTest extends PHPUnit\Framework\TestCase
'8.4',
'New expressions are not supported in this context',
];
yield 'PHP 8.2 static variable rejects dynamic initializer' => [
'function f(int $seed) { static $value = loadValue($seed); }',
'8.2',
'Constant expression contains invalid operations',
];
yield 'property rejects new' => [
'class C { public mixed $value = new Value(); }',
'8.4',

@ -15,15 +15,21 @@ use PhpParser\NodeVisitorAbstract;
* Applies the allow_dynamic values used by php-src at each declaration site.
*
* false: class constants, property defaults and enum cases.
* true: attributes, parameter defaults, global constants and static variables.
* true: attributes, parameter defaults and global constants.
*
* Static variable initializers are constant expressions on PHP 8.2. PHP 8.3
* and later compile them as regular expressions and evaluate them only once.
*/
final class ConstantExpressionValidationVisitor extends NodeVisitorAbstract
{
private readonly ConstantExpressionValidator $validator;
private readonly bool $supportsDynamicStaticInitializers;
public function __construct(string $phpVersion)
{
$this->validator = new ConstantExpressionValidator($phpVersion);
$this->supportsDynamicStaticInitializers = version_compare($phpVersion, '8.3', '>=');
}
public function enterNode(Node $node): null
@ -70,7 +76,7 @@ final class ConstantExpressionValidationVisitor extends NodeVisitorAbstract
return null;
}
if ($node instanceof Node\Stmt\Static_) {
if ($node instanceof Node\Stmt\Static_ && !$this->supportsDynamicStaticInitializers) {
foreach ($node->vars as $variable) {
if ($variable->default !== null) {
$this->validator->validate($variable->default, allowDynamic: true);

@ -1,7 +1,6 @@
--TEST--
Attribute: 003
--SKIPIF--
<?php die("skip"); ?>
--FILE--
<?php
@ -25,10 +24,15 @@ function main() {
--EXPECT--
object(Thing)#1 (0) {
}
string(11) "MyAttribute"
array(1) {
[0]=>
object(ReflectionAttribute)#3 (1) {
["name"]=>
string(11) "MyAttribute"
array(3) {
[0]=>
int(1)
[1]=>
int(2)
[2]=>
int(3)
}
}

@ -1,9 +1,6 @@
--TEST--
Attributes (Annotations) - PHP 8+ metadata syntax
--SKIPIF--
<?php
echo "skip Attributes/Annotations not supported in AOT";
?>
--FILE--
<?php
// Define attribute classes
@ -104,7 +101,7 @@ function main() {
?>
--EXPECT--
int(1)
string(11) "/api/users"
string(10) "/api/users"
array(2) {
[0]=>
string(3) "GET"
@ -117,4 +114,4 @@ int(1)
string(2) "id"
string(3) "int"
int(1)
string(11) "/api/posts"
string(10) "/api/posts"

@ -1,11 +1,11 @@
--TEST--
Keyword ExtensionProvider method with snake_case name
Keyword MethodsFor method with snake_case name
--FILE--
<?php
declare(strict_types=1);
use native_types;
#[ExtensionProvider('*')]
#[MethodsFor('*')]
final class KeywordExtensions
{
public static function var_dump(mixed $var): void

@ -1,12 +1,12 @@
--TEST--
Keyword ExtensionProvider method with lowerCamelCase name
Keyword MethodsFor method with lowerCamelCase name
--FILE--
<?php
declare(strict_types=1);
use native_types;
#[ExtensionProvider('*')]
#[MethodsFor('*')]
final class KeywordExtensions
{
public static function inspectValue(mixed $value, string $prefix): void
@ -15,7 +15,7 @@ final class KeywordExtensions
}
}
#[ExtensionProvider(Type::Any)]
#[MethodsFor(Type::Any)]
final class AnyExtensions
{
public static function dynamicType(mixed $value): string

@ -3,7 +3,7 @@ stream extension method support
--FILE--
<?php
#[ExtensionProvider(Type::Stream)]
#[MethodsFor(Type::Stream)]
final class StreamExtensions
{
public static function readChunk(stream $stream, int $size): string

@ -1,9 +1,6 @@
--TEST--
Traits - Basic functionality and method inheritance
--SKIPIF--
<?php
echo "skip Traits not yet supported in AOT";
?>
--FILE--
<?php
// Test basic trait usage
@ -90,8 +87,8 @@ function main() {
string(5) "Hello"
string(7) "Goodbye"
string(4) "John"
string(22) "[users] User created"
string(20) "[users] User created"
string(5) "Hello"
string(9) "Test Post"
string(19) "2024-01-01 00:00:00"
string(19) "2024-01-02 00:00:00"
string(19) "2024-01-02 00:00:00"

@ -1,5 +1,5 @@
--TEST--
Namespaced object methods use an ExtensionProvider class
Namespaced object methods use a MethodsFor class
--FILE--
<?php
@ -23,7 +23,7 @@ namespace App {
}
}
#[\ExtensionProvider(User::class)]
#[\MethodsFor(User::class)]
final class UserExtensions
{
public static function testMethod(User $user, string $suffix): string

@ -1,5 +1,5 @@
--TEST--
Object ExtensionProvider methods require consistent names and ignore letter case
Object MethodsFor methods require consistent names and ignore letter case
--FILE--
<?php
@ -18,7 +18,7 @@ namespace App {
}
}
#[\ExtensionProvider(UserService::class)]
#[\MethodsFor(UserService::class)]
final class UserServiceExtensions
{
public static function displayName(UserService $service): string

@ -1,11 +1,11 @@
--TEST--
Universal methods provided by ExtensionProvider classes
Universal methods provided by MethodsFor classes
--FILE--
<?php
use native_types;
#[ExtensionProvider(Type::Int)]
#[MethodsFor(Type::Int)]
final class IntExtensions
{
public static function to_bytes(int $int, string $unit = 'Kb'): string
@ -14,7 +14,7 @@ final class IntExtensions
}
}
#[ExtensionProvider(Type::Array)]
#[MethodsFor(Type::Array)]
final class ArrayExtensions
{
public static function get_first_element(array $array): mixed
@ -23,7 +23,7 @@ final class ArrayExtensions
}
}
#[ExtensionProvider(Type::String)]
#[MethodsFor(Type::String)]
final class StringExtensions
{
public static function shout(string $str): string

@ -1,11 +1,11 @@
--TEST--
Universal ExtensionProvider methods use their declared names
Universal MethodsFor methods use their declared names
--FILE--
<?php
use native_types;
#[ExtensionProvider(Type::Int)]
#[MethodsFor(Type::Int)]
final class IntExtensions
{
public static function toBytes(int $value): string
@ -14,7 +14,7 @@ final class IntExtensions
}
}
#[ExtensionProvider(Type::Array)]
#[MethodsFor(Type::Array)]
final class ArrayExtensions
{
public static function getFirstElement(array $value): mixed

@ -1,11 +1,11 @@
--TEST--
ExtensionProvider method chaining with typed returns
MethodsFor method chaining with typed returns
--FILE--
<?php
use native_types;
#[ExtensionProvider(Type::Int)]
#[MethodsFor(Type::Int)]
final class IntExtensions
{
public static function to_words(int $int): string
@ -15,7 +15,7 @@ final class IntExtensions
}
}
#[ExtensionProvider(Type::String)]
#[MethodsFor(Type::String)]
final class StringExtensions
{
public static function double(string $str): string
@ -34,7 +34,7 @@ final class StringExtensions
}
}
#[ExtensionProvider(Type::Array)]
#[MethodsFor(Type::Array)]
final class ArrayExtensions
{
public static function last(array $arr): mixed

@ -5,7 +5,7 @@ Universal method provider may wrap a PHP internal function
use native_types;
#[ExtensionProvider(Type::String)]
#[MethodsFor(Type::String)]
final class StringExtensions
{
public static function rot13(string $value): string

@ -8,7 +8,7 @@ if (PHP_OS_FAMILY != 'Linux') {
}
--FILE--
<?php
#[ExtensionProvider(Type::Stream)]
#[MethodsFor(Type::Stream)]
final class StreamExtensions
{
public static function writeTest(stream $stream): void

Loading…
Cancel
Save