From 767dad17bb61c11c4e7952be5d5ab78ee3daeabb Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Sun, 24 May 2020 20:57:00 +0200 Subject: [PATCH 01/10] Add Attributes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Martin Schröder --- Zend/tests/attributes/001_placement.phpt | 134 ++++++ Zend/tests/attributes/002_rfcexample.phpt | 43 ++ Zend/tests/attributes/003_ast_nodes.phpt | 109 +++++ .../tests/attributes/004_name_resolution.phpt | 78 ++++ Zend/tests/attributes/005_objects.phpt | 120 ++++++ Zend/tests/attributes/006_filter.phpt | 112 +++++ .../007_self_reflect_attribute.phpt | 19 + .../attributes/008_wrong_attribution.phpt | 9 + .../009_doctrine_annotations_example.phpt | 159 +++++++ .../010_unsupported_const_expression.phpt | 11 + Zend/tests/attributes/011-inheritance.phpt | 99 +++++ Zend/tests/attributes/012-ast-export.phpt | 54 +++ .../attributes/013_scope_resolution.phpt | 112 +++++ .../attributes/014_class_const_group.phpt | 14 + Zend/tests/attributes/015_property_group.phpt | 14 + ...target_resolution_compiler_attributes.phpt | 15 + .../globalNonSimpleVariableError.phpt | 2 +- Zend/zend.h | 1 + Zend/zend_API.c | 15 +- Zend/zend_API.h | 6 +- Zend/zend_ast.c | 91 +++- Zend/zend_ast.h | 23 +- Zend/zend_attributes.c | 131 ++++++ Zend/zend_attributes.h | 44 ++ Zend/zend_compile.c | 135 +++++- Zend/zend_compile.h | 6 + Zend/zend_default_classes.c | 2 + Zend/zend_execute.c | 1 + Zend/zend_inheritance.c | 10 +- Zend/zend_language_parser.y | 76 +++- Zend/zend_opcode.c | 16 + configure.ac | 2 +- ext/opcache/zend_file_cache.c | 81 ++++ ext/opcache/zend_persist.c | 51 +++ ext/opcache/zend_persist_calc.c | 36 ++ ext/reflection/php_reflection.c | 406 +++++++++++++++++- ext/reflection/php_reflection.h | 1 + ext/reflection/php_reflection.stub.php | 26 ++ ext/reflection/php_reflection_arginfo.h | 49 +++ .../tests/ReflectionClass_toString_001.phpt | 10 +- .../ReflectionExtension_getClasses_basic.phpt | 7 +- ext/tokenizer/tokenizer.c | 8 +- ext/zend_test/test.c | 23 +- ext/zip/php_zip.c | 2 +- win32/build/config.w32 | 2 +- 45 files changed, 2311 insertions(+), 54 deletions(-) create mode 100644 Zend/tests/attributes/001_placement.phpt create mode 100644 Zend/tests/attributes/002_rfcexample.phpt create mode 100644 Zend/tests/attributes/003_ast_nodes.phpt create mode 100644 Zend/tests/attributes/004_name_resolution.phpt create mode 100644 Zend/tests/attributes/005_objects.phpt create mode 100644 Zend/tests/attributes/006_filter.phpt create mode 100644 Zend/tests/attributes/007_self_reflect_attribute.phpt create mode 100644 Zend/tests/attributes/008_wrong_attribution.phpt create mode 100644 Zend/tests/attributes/009_doctrine_annotations_example.phpt create mode 100644 Zend/tests/attributes/010_unsupported_const_expression.phpt create mode 100644 Zend/tests/attributes/011-inheritance.phpt create mode 100644 Zend/tests/attributes/012-ast-export.phpt create mode 100644 Zend/tests/attributes/013_scope_resolution.phpt create mode 100644 Zend/tests/attributes/014_class_const_group.phpt create mode 100644 Zend/tests/attributes/015_property_group.phpt create mode 100644 Zend/tests/attributes/016_target_resolution_compiler_attributes.phpt create mode 100644 Zend/zend_attributes.c create mode 100644 Zend/zend_attributes.h diff --git a/Zend/tests/attributes/001_placement.phpt b/Zend/tests/attributes/001_placement.phpt new file mode 100644 index 0000000000000..cf7bcd450406d --- /dev/null +++ b/Zend/tests/attributes/001_placement.phpt @@ -0,0 +1,134 @@ +--TEST-- +Attributes can be placed on all supported elements. +--FILE-- +> +class Foo +{ + <> + public const FOO = 'foo'; + + <> + public $x; + + <> + public function foo(<> $a, <> $b) { } +} + +$object = new <> class () { }; + +<> +function f1() { } + +$f2 = <> function () { }; + +$f3 = <> fn () => 1; + +$ref = new \ReflectionClass(Foo::class); + +$sources = [ + $ref, + $ref->getReflectionConstant('FOO'), + $ref->getProperty('x'), + $ref->getMethod('foo'), + $ref->getMethod('foo')->getParameters()[0], + $ref->getMethod('foo')->getParameters()[1], + new \ReflectionObject($object), + new \ReflectionFunction('f1'), + new \ReflectionFunction($f2), + new \ReflectionFunction($f3) +]; + +foreach ($sources as $r) { + $attr = $r->getAttributes(); + var_dump(get_class($r), count($attr)); + + foreach ($attr as $a) { + var_dump($a->getName(), $a->getArguments()); + } + + echo "\n"; +} + +?> +--EXPECT-- +string(15) "ReflectionClass" +int(1) +string(2) "A1" +array(1) { + [0]=> + int(1) +} + +string(23) "ReflectionClassConstant" +int(1) +string(2) "A1" +array(1) { + [0]=> + int(2) +} + +string(18) "ReflectionProperty" +int(1) +string(2) "A1" +array(1) { + [0]=> + int(3) +} + +string(16) "ReflectionMethod" +int(1) +string(2) "A1" +array(1) { + [0]=> + int(4) +} + +string(19) "ReflectionParameter" +int(1) +string(2) "A1" +array(1) { + [0]=> + int(5) +} + +string(19) "ReflectionParameter" +int(1) +string(2) "A1" +array(1) { + [0]=> + int(6) +} + +string(16) "ReflectionObject" +int(1) +string(2) "A1" +array(1) { + [0]=> + int(7) +} + +string(18) "ReflectionFunction" +int(1) +string(2) "A1" +array(1) { + [0]=> + int(8) +} + +string(18) "ReflectionFunction" +int(1) +string(2) "A1" +array(1) { + [0]=> + int(9) +} + +string(18) "ReflectionFunction" +int(1) +string(2) "A1" +array(1) { + [0]=> + int(10) +} diff --git a/Zend/tests/attributes/002_rfcexample.phpt b/Zend/tests/attributes/002_rfcexample.phpt new file mode 100644 index 0000000000000..0d3879877e9d9 --- /dev/null +++ b/Zend/tests/attributes/002_rfcexample.phpt @@ -0,0 +1,43 @@ +--TEST-- +Attributes: Example from Attributes RFC +--FILE-- +> + class SingleArgument { + public $argumentValue; + + public function __construct($argumentValue) { + $this->argumentValue = $argumentValue; + } + } +} + +namespace { + use My\Attributes\SingleArgument; + + <> + class Foo { + } + + $reflectionClass = new \ReflectionClass(Foo::class); + $attributes = $reflectionClass->getAttributes(); + + var_dump($attributes[0]->getName()); + var_dump($attributes[0]->getArguments()); + var_dump($attributes[0]->newInstance()); +} +?> +--EXPECTF-- +string(28) "My\Attributes\SingleArgument" +array(1) { + [0]=> + string(11) "Hello World" +} +object(My\Attributes\SingleArgument)#3 (1) { + ["argumentValue"]=> + string(11) "Hello World" +} diff --git a/Zend/tests/attributes/003_ast_nodes.phpt b/Zend/tests/attributes/003_ast_nodes.phpt new file mode 100644 index 0000000000000..cf43e663d5197 --- /dev/null +++ b/Zend/tests/attributes/003_ast_nodes.phpt @@ -0,0 +1,109 @@ +--TEST-- +Attributes can deal with AST nodes. +--FILE-- + V1])>> +class C1 +{ + public const BAR = 'bar'; +} + +$ref = new \ReflectionClass(C1::class); +$attr = $ref->getAttributes(); +var_dump(count($attr)); + +$args = $attr[0]->getArguments(); +var_dump(count($args), $args[0][V1] === V1); + +echo "\n"; + +<> +class C2 { } + +$ref = new \ReflectionClass(C2::class); +$attr = $ref->getAttributes(); +var_dump(count($attr)); + +$args = $attr[0]->getArguments(); +var_dump(count($args)); +var_dump($args[0] === V1); +var_dump($args[1] === 3); +var_dump($args[2] === C1::class); + +echo "\n"; + +<> +class C3 +{ + private const FOO = 'foo'; +} + +$ref = new \ReflectionClass(C3::class); +$attr = $ref->getAttributes(); +var_dump(count($attr)); + +$args = $attr[0]->getArguments(); +var_dump(count($args)); +var_dump($args[0] === 'foo'); +var_dump($args[1] === C1::BAR); + +echo "\n"; + +<> 1)>> +class C4 {} +$ref = new \ReflectionClass(C4::class); +var_dump($ref->getAttributes()[0]->getArguments()); + +echo "\n"; + +<> +class C5 +{ + public function __construct() { } +} + +$ref = new \ReflectionFunction(<> function () { }); +$attr = $ref->getAttributes(); +var_dump(count($attr)); + +try { + $attr[0]->getArguments(); +} catch (\Error $e) { + var_dump($e->getMessage()); +} + +try { + $attr[0]->newInstance(); +} catch (\Error $e) { + var_dump($e->getMessage()); +} + +?> +--EXPECT-- +int(1) +int(1) +bool(true) + +int(1) +int(3) +bool(true) +bool(true) +bool(true) + +int(1) +int(2) +bool(true) +bool(true) + +array(1) { + [0]=> + int(2) +} + +int(1) +string(30) "Class 'MissingClass' not found" +string(30) "Class 'MissingClass' not found" + diff --git a/Zend/tests/attributes/004_name_resolution.phpt b/Zend/tests/attributes/004_name_resolution.phpt new file mode 100644 index 0000000000000..a2284e2c433c8 --- /dev/null +++ b/Zend/tests/attributes/004_name_resolution.phpt @@ -0,0 +1,78 @@ +--TEST-- +Resolve attribute names +--FILE-- + $attribute->getName(), 'args' => $attribute->getArguments()]; + } + var_dump($arr); +} + +namespace Doctrine\ORM\Mapping { + class Entity { + } +} + +namespace Foo { + use Doctrine\ORM\Mapping\Entity; + use Doctrine\ORM\Mapping as ORM; + + <> + <> + <<\Doctrine\ORM\Mapping\Entity("absolute from namespace")>> + <<\Entity("import absolute from global")>> + function foo() { + } +} + +namespace { + class Entity {} + + dump_attributes((new ReflectionFunction('Foo\foo'))->getAttributes()); +} +?> +--EXPECTF-- +array(4) { + [0]=> + array(2) { + ["name"]=> + string(27) "Doctrine\ORM\Mapping\Entity" + ["args"]=> + array(1) { + [0]=> + string(14) "imported class" + } + } + [1]=> + array(2) { + ["name"]=> + string(27) "Doctrine\ORM\Mapping\Entity" + ["args"]=> + array(1) { + [0]=> + string(18) "imported namespace" + } + } + [2]=> + array(2) { + ["name"]=> + string(27) "Doctrine\ORM\Mapping\Entity" + ["args"]=> + array(1) { + [0]=> + string(23) "absolute from namespace" + } + } + [3]=> + array(2) { + ["name"]=> + string(6) "Entity" + ["args"]=> + array(1) { + [0]=> + string(27) "import absolute from global" + } + } +} diff --git a/Zend/tests/attributes/005_objects.phpt b/Zend/tests/attributes/005_objects.phpt new file mode 100644 index 0000000000000..baf51af775bf2 --- /dev/null +++ b/Zend/tests/attributes/005_objects.phpt @@ -0,0 +1,120 @@ +--TEST-- +Attributes can be converted into objects. +--FILE-- +> +class A1 +{ + public string $name; + public int $ttl; + + public function __construct(string $name, int $ttl = 50) + { + $this->name = $name; + $this->ttl = $ttl; + } +} + +$ref = new \ReflectionFunction(<> function () { }); + +foreach ($ref->getAttributes() as $attr) { + $obj = $attr->newInstance(); + + var_dump(get_class($obj), $obj->name, $obj->ttl); +} + +echo "\n"; + +$ref = new \ReflectionFunction(<> function () { }); + +try { + $ref->getAttributes()[0]->newInstance(); +} catch (\ArgumentCountError $e) { + var_dump('ERROR 1', $e->getMessage()); +} + +echo "\n"; + +$ref = new \ReflectionFunction(<> function () { }); + +try { + $ref->getAttributes()[0]->newInstance(); +} catch (\TypeError $e) { + var_dump('ERROR 2', $e->getMessage()); +} + +echo "\n"; + +$ref = new \ReflectionFunction(<> function () { }); + +try { + $ref->getAttributes()[0]->newInstance(); +} catch (\Error $e) { + var_dump('ERROR 3', $e->getMessage()); +} + +echo "\n"; + +<> +class A3 +{ + private function __construct() { } +} + +$ref = new \ReflectionFunction(<> function () { }); + +try { + $ref->getAttributes()[0]->newInstance(); +} catch (\Error $e) { + var_dump('ERROR 4', $e->getMessage()); +} + +echo "\n"; + +<> +class A4 { } + +$ref = new \ReflectionFunction(<> function () { }); + +try { + $ref->getAttributes()[0]->newInstance(); +} catch (\Error $e) { + var_dump('ERROR 5', $e->getMessage()); +} + +echo "\n"; + +class A5 { } + +$ref = new \ReflectionFunction(<> function () { }); + +try { + $ref->getAttributes()[0]->newInstance(); +} catch (\Error $e) { + var_dump('ERROR 6', $e->getMessage()); +} + +?> +--EXPECT-- +string(2) "A1" +string(4) "test" +int(50) + +string(7) "ERROR 1" +string(81) "Too few arguments to function A1::__construct(), 0 passed and at least 1 expected" + +string(7) "ERROR 2" +string(74) "A1::__construct(): Argument #1 ($name) must be of type string, array given" + +string(7) "ERROR 3" +string(30) "Attribute class 'A2' not found" + +string(7) "ERROR 4" +string(50) "Attribute constructor of class 'A3' must be public" + +string(7) "ERROR 5" +string(71) "Attribute class 'A4' does not have a constructor, cannot pass arguments" + +string(7) "ERROR 6" +string(78) "Attempting to use class 'A5' as attribute that does not have <>." diff --git a/Zend/tests/attributes/006_filter.phpt b/Zend/tests/attributes/006_filter.phpt new file mode 100644 index 0000000000000..78f5b9709f50b --- /dev/null +++ b/Zend/tests/attributes/006_filter.phpt @@ -0,0 +1,112 @@ +--TEST-- +Attributes can be filtered by name and base type. +--FILE-- +> <> function () { }); +$attr = $ref->getAttributes(A3::class); + +var_dump(count($attr)); + +$ref = new \ReflectionFunction(<> <> function () { }); +$attr = $ref->getAttributes(A2::class); + +var_dump(count($attr), $attr[0]->getName()); + +$ref = new \ReflectionFunction(<> <> <> function () { }); +$attr = $ref->getAttributes(A2::class); + +var_dump(count($attr), $attr[0]->getName(), $attr[1]->getName()); + +echo "\n"; + +interface Base { } +class A1 implements Base { } +class A2 implements Base { } +class A3 extends A2 { } + +$ref = new \ReflectionFunction(<> <> <> function () { }); +$attr = $ref->getAttributes(\stdClass::class, \ReflectionAttribute::IS_INSTANCEOF); +var_dump(count($attr)); +print_r(array_map(fn ($a) => $a->getName(), $attr)); + +$ref = new \ReflectionFunction(<> <> function () { }); +$attr = $ref->getAttributes(A1::class, \ReflectionAttribute::IS_INSTANCEOF); +var_dump(count($attr)); +print_r(array_map(fn ($a) => $a->getName(), $attr)); + +$ref = new \ReflectionFunction(<> <> function () { }); +$attr = $ref->getAttributes(Base::class, \ReflectionAttribute::IS_INSTANCEOF); +var_dump(count($attr)); +print_r(array_map(fn ($a) => $a->getName(), $attr)); + +$ref = new \ReflectionFunction(<> <> <> function () { }); +$attr = $ref->getAttributes(A2::class, \ReflectionAttribute::IS_INSTANCEOF); +var_dump(count($attr)); +print_r(array_map(fn ($a) => $a->getName(), $attr)); + +$ref = new \ReflectionFunction(<> <> <> function () { }); +$attr = $ref->getAttributes(Base::class, \ReflectionAttribute::IS_INSTANCEOF); +var_dump(count($attr)); +print_r(array_map(fn ($a) => $a->getName(), $attr)); + +echo "\n"; + +$ref = new \ReflectionFunction(function () { }); + +try { + $ref->getAttributes(A1::class, 3); +} catch (\Error $e) { + var_dump('ERROR 1', $e->getMessage()); +} + +$ref = new \ReflectionFunction(function () { }); + +try { + $ref->getAttributes(SomeMissingClass::class, \ReflectionAttribute::IS_INSTANCEOF); +} catch (\Error $e) { + var_dump('ERROR 2', $e->getMessage()); +} + +?> +--EXPECT-- +int(0) +int(1) +string(2) "A2" +int(2) +string(2) "A2" +string(2) "A2" + +int(0) +Array +( +) +int(1) +Array +( + [0] => A1 +) +int(2) +Array +( + [0] => A1 + [1] => A2 +) +int(2) +Array +( + [0] => A2 + [1] => A3 +) +int(3) +Array +( + [0] => A1 + [1] => A2 + [2] => A3 +) + +string(7) "ERROR 1" +string(39) "Invalid attribute filter flag specified" +string(7) "ERROR 2" +string(34) "Class 'SomeMissingClass' not found" diff --git a/Zend/tests/attributes/007_self_reflect_attribute.phpt b/Zend/tests/attributes/007_self_reflect_attribute.phpt new file mode 100644 index 0000000000000..ae19665dcb628 --- /dev/null +++ b/Zend/tests/attributes/007_self_reflect_attribute.phpt @@ -0,0 +1,19 @@ +--TEST-- +Attributes: attributes on PhpAttribute return itself +--FILE-- +getAttributes(); + +foreach ($attributes as $attribute) { + var_dump($attribute->getName()); + var_dump($attribute->getArguments()); + var_dump($attribute->newInstance()); +} +--EXPECTF-- +string(12) "PhpAttribute" +array(0) { +} +object(PhpAttribute)#3 (0) { +} diff --git a/Zend/tests/attributes/008_wrong_attribution.phpt b/Zend/tests/attributes/008_wrong_attribution.phpt new file mode 100644 index 0000000000000..dcb0b6b51d0fe --- /dev/null +++ b/Zend/tests/attributes/008_wrong_attribution.phpt @@ -0,0 +1,9 @@ +--TEST-- +Attributes: Prevent PhpAttribute on non classes +--FILE-- +> +function foo() {} +--EXPECTF-- +Fatal error: Only classes can be marked with <> in %s diff --git a/Zend/tests/attributes/009_doctrine_annotations_example.phpt b/Zend/tests/attributes/009_doctrine_annotations_example.phpt new file mode 100644 index 0000000000000..38fc6389e070c --- /dev/null +++ b/Zend/tests/attributes/009_doctrine_annotations_example.phpt @@ -0,0 +1,159 @@ +--TEST-- +Doctrine-like attributes usage +--FILE-- +values = func_get_args(); } } + class Entity extends Annotation {} + class Id extends Annotation {} + class Column extends Annotation { const UNIQUE = 'unique'; const T_INTEGER = 'integer'; } + class GeneratedValue extends Annotation {} + class JoinTable extends Annotation {} + class ManyToMany extends Annotation {} + class JoinColumn extends Annotation { const UNIQUE = 'unique'; } + class InverseJoinColumn extends Annotation {} +} + +namespace Symfony\Component\Validator\Constraints { + class Annotation { public $values; public function construct() { $this->values = func_get_args(); } } + class Email extends Annotation {} + class Range extends Annotation {} +} + +namespace { +use Doctrine\ORM\Attributes as ORM; +use Symfony\Component\Validator\Constraints as Assert; + +<> +/** @ORM\Entity */ +class User +{ + /** @ORM\Id @ORM\Column(type="integer"*) @ORM\GeneratedValue */ + <><><> + private $id; + + /** + * @ORM\Column(type="string", unique=true) + * @Assert\Email(message="The email '{{ value }}' is not a valid email.") + */ + <> + < "The email '{{ value }}' is not a valid email."))>> + private $email; + + /** + * @ORM\Column(type="integer") + * @Assert\Range( + * min = 120, + * max = 180, + * minMessage = "You must be at least {{ limit }}cm tall to enter", + * maxMessage = "You cannot be taller than {{ limit }}cm to enter" + * ) + */ + < 120, "max" => 180, "minMessage" => "You must be at least {{ limit }}cm tall to enter"])>> + <> + protected $height; + + /** + * @ORM\ManyToMany(targetEntity="Phonenumber") + * @ORM\JoinTable(name="users_phonenumbers", + * joinColumns={@ORM\JoinColumn(name="user_id", referencedColumnName="id")}, + * inverseJoinColumns={@ORM\JoinColumn(name="phonenumber_id", referencedColumnName="id", unique=true)} + * ) + */ + <> + <> + <> + <> + private $phonenumbers; +} + +$class = new ReflectionClass(User::class); +$attributes = $class->getAttributes(); + +foreach ($attributes as $attribute) { + var_dump($attribute->getName(), $attribute->getArguments()); +} + +foreach ($class->getProperties() as $property) { + $attributes = $property->getAttributes(); + + foreach ($attributes as $attribute) { + var_dump($attribute->getName(), $attribute->getArguments()); + } +} +} +?> +--EXPECT-- +string(30) "Doctrine\ORM\Attributes\Entity" +array(0) { +} +string(26) "Doctrine\ORM\Attributes\Id" +array(0) { +} +string(30) "Doctrine\ORM\Attributes\Column" +array(1) { + [0]=> + string(7) "integer" +} +string(38) "Doctrine\ORM\Attributes\GeneratedValue" +array(0) { +} +string(30) "Doctrine\ORM\Attributes\Column" +array(2) { + [0]=> + string(6) "string" + [1]=> + string(6) "unique" +} +string(45) "Symfony\Component\Validator\Constraints\Email" +array(1) { + [0]=> + array(1) { + ["message"]=> + string(45) "The email '{{ value }}' is not a valid email." + } +} +string(45) "Symfony\Component\Validator\Constraints\Range" +array(1) { + [0]=> + array(3) { + ["min"]=> + int(120) + ["max"]=> + int(180) + ["minMessage"]=> + string(48) "You must be at least {{ limit }}cm tall to enter" + } +} +string(30) "Doctrine\ORM\Attributes\Column" +array(1) { + [0]=> + string(7) "integer" +} +string(34) "Doctrine\ORM\Attributes\ManyToMany" +array(1) { + [0]=> + string(11) "Phonenumber" +} +string(33) "Doctrine\ORM\Attributes\JoinTable" +array(1) { + [0]=> + string(18) "users_phonenumbers" +} +string(34) "Doctrine\ORM\Attributes\JoinColumn" +array(2) { + [0]=> + string(7) "user_id" + [1]=> + string(2) "id" +} +string(41) "Doctrine\ORM\Attributes\InverseJoinColumn" +array(3) { + [0]=> + string(14) "phonenumber_id" + [1]=> + string(2) "id" + [2]=> + string(6) "unique" +} diff --git a/Zend/tests/attributes/010_unsupported_const_expression.phpt b/Zend/tests/attributes/010_unsupported_const_expression.phpt new file mode 100644 index 0000000000000..50afa6c4f65c5 --- /dev/null +++ b/Zend/tests/attributes/010_unsupported_const_expression.phpt @@ -0,0 +1,11 @@ +--TEST-- +Attribute arguments support only const expressions. +--FILE-- +> +class C1 { } + +?> +--EXPECTF-- +Fatal error: Constant expression contains invalid operations in %s diff --git a/Zend/tests/attributes/011-inheritance.phpt b/Zend/tests/attributes/011-inheritance.phpt new file mode 100644 index 0000000000000..007cd5991e7d7 --- /dev/null +++ b/Zend/tests/attributes/011-inheritance.phpt @@ -0,0 +1,99 @@ +--TEST-- +Attributes comply with inheritance rules. +--FILE-- +> +class C1 +{ + <> + public function foo() { } +} + +class C2 extends C1 +{ + public function foo() { } +} + +class C3 extends C1 +{ + <> + public function bar() { } +} + +$ref = new \ReflectionClass(C1::class); +print_r(array_map(fn ($a) => $a->getName(), $ref->getAttributes())); +print_r(array_map(fn ($a) => $a->getName(), $ref->getMethod('foo')->getAttributes())); + +$ref = new \ReflectionClass(C2::class); +print_r(array_map(fn ($a) => $a->getName(), $ref->getAttributes())); +print_r(array_map(fn ($a) => $a->getName(), $ref->getMethod('foo')->getAttributes())); + +$ref = new \ReflectionClass(C3::class); +print_r(array_map(fn ($a) => $a->getName(), $ref->getAttributes())); +print_r(array_map(fn ($a) => $a->getName(), $ref->getMethod('foo')->getAttributes())); + +echo "\n"; + +trait T1 +{ + <> + public $a; +} + +class C4 +{ + use T1; +} + +class C5 +{ + use T1; + + public $a; +} + +$ref = new \ReflectionClass(T1::class); +print_r(array_map(fn ($a) => $a->getName(), $ref->getProperty('a')->getAttributes())); + +$ref = new \ReflectionClass(C4::class); +print_r(array_map(fn ($a) => $a->getName(), $ref->getProperty('a')->getAttributes())); + +$ref = new \ReflectionClass(C5::class); +print_r(array_map(fn ($a) => $a->getName(), $ref->getProperty('a')->getAttributes())); + +?> +--EXPECT-- +Array +( + [0] => A2 +) +Array +( + [0] => A1 +) +Array +( +) +Array +( +) +Array +( +) +Array +( + [0] => A1 +) + +Array +( + [0] => A2 +) +Array +( + [0] => A2 +) +Array +( +) diff --git a/Zend/tests/attributes/012-ast-export.phpt b/Zend/tests/attributes/012-ast-export.phpt new file mode 100644 index 0000000000000..47176b1b0d02d --- /dev/null +++ b/Zend/tests/attributes/012-ast-export.phpt @@ -0,0 +1,54 @@ +--TEST-- +Attributes AST can be exported. +--FILE-- +><> function ($a, <> $b) { })); + +assert(0 && ($a = <> fn () => 1)); + +assert(0 && ($a = new <> class() { + <><> const FOO = 'foo'; + <> public $x; + <> function a() { } +})); + +assert(0 && ($a = function () { + <> class Test1 { } + <> interface Test2 { } + <> trait Test3 { } +})); + +?> +--EXPECTF-- +Warning: assert(): assert(0 && ($a = <> <> function ($a, <> $b) { +})) failed in %s on line %d + +Warning: assert(): assert(0 && ($a = <> fn() => 1)) failed in %s on line %d + +Warning: assert(): assert(0 && ($a = new <> class { + <> + <> + const FOO = 'foo'; + <> + public $x; + <> + public function a() { + } + +})) failed in %s on line %d + +Warning: assert(): assert(0 && ($a = function () { + <> + class Test1 { + } + + <> + interface Test2 { + } + + <> + trait Test3 { + } + +})) failed in %s on line %d diff --git a/Zend/tests/attributes/013_scope_resolution.phpt b/Zend/tests/attributes/013_scope_resolution.phpt new file mode 100644 index 0000000000000..fa4103cbce4f9 --- /dev/null +++ b/Zend/tests/attributes/013_scope_resolution.phpt @@ -0,0 +1,112 @@ +--TEST-- +Attributes make use of correct scope. +--FILE-- +> +class C1 +{ + <> + private const FOO = 'foo'; + + <> + public $a; + + <> + public function bar(<> $p) { } +} + +$ref = new \ReflectionClass(C1::class); +print_r($ref->getAttributes()[0]->getArguments()); +print_r($ref->getReflectionConstant('FOO')->getAttributes()[0]->getArguments()); +print_r($ref->getProperty('a')->getAttributes()[0]->getArguments()); +print_r($ref->getMethod('bar')->getAttributes()[0]->getArguments()); +print_r($ref->getMethod('bar')->getParameters()[0]->getAttributes()[0]->getArguments()); + +echo "\n"; + +class C2 +{ + private const FOO = 'foo'; + + public static function foo() + { + return <> function (<> $p) { }; + } +} + +$ref = new \ReflectionFunction(C2::foo()); +print_r($ref->getAttributes()[0]->getArguments()); +print_r($ref->getParameters()[0]->getAttributes()[0]->getArguments()); + +echo "\n"; + +class C3 +{ + private const FOO = 'foo'; + + public static function foo() + { + return new <> class() { + private const FOO = 'bar'; + + <> + public function bar() { } + }; + } +} + +$obj = C3::foo(); +$ref = new \ReflectionObject($obj); +$name = $ref->getMethod('bar')->getAttributes()[0]->getArguments()[0]; + +print_r($ref->getAttributes()[0]->getArguments()); +var_dump($name == get_class($obj)); +var_dump($ref->getMethod('bar')->getAttributes()[0]->getArguments()[1]); + +?> +--EXPECT-- +Array +( + [0] => C1 + [1] => foo +) +Array +( + [0] => C1 + [1] => foo +) +Array +( + [0] => C1 + [1] => foo +) +Array +( + [0] => C1 + [1] => foo +) +Array +( + [0] => C1 + [1] => foo +) + +Array +( + [0] => C2 + [1] => foo +) +Array +( + [0] => C2 + [1] => foo +) + +Array +( + [0] => C3 + [1] => foo +) +bool(true) +string(3) "bar" diff --git a/Zend/tests/attributes/014_class_const_group.phpt b/Zend/tests/attributes/014_class_const_group.phpt new file mode 100644 index 0000000000000..18ff93683aff9 --- /dev/null +++ b/Zend/tests/attributes/014_class_const_group.phpt @@ -0,0 +1,14 @@ +--TEST-- +Attributes cannot be applied to groups of class constants. +--FILE-- +> + public const A = 1, B = 2; +} + +?> +--EXPECTF-- +Fatal error: Cannot apply attributes to a group of constants in %s diff --git a/Zend/tests/attributes/015_property_group.phpt b/Zend/tests/attributes/015_property_group.phpt new file mode 100644 index 0000000000000..493e4ae260136 --- /dev/null +++ b/Zend/tests/attributes/015_property_group.phpt @@ -0,0 +1,14 @@ +--TEST-- +Attributes cannot be applied to groups of properties. +--FILE-- +> + public $x, $y; +} + +?> +--EXPECTF-- +Fatal error: Cannot apply attributes to a group of properties in %s diff --git a/Zend/tests/attributes/016_target_resolution_compiler_attributes.phpt b/Zend/tests/attributes/016_target_resolution_compiler_attributes.phpt new file mode 100644 index 0000000000000..c96b49cbd7fc1 --- /dev/null +++ b/Zend/tests/attributes/016_target_resolution_compiler_attributes.phpt @@ -0,0 +1,15 @@ +--TEST-- +Attributes: Compiler Attributes can check for target declarations +--SKIPIF-- +> +function foo() { +} +--EXPECTF-- +Fatal error: Only classes can be marked with <> in %s diff --git a/Zend/tests/varSyntax/globalNonSimpleVariableError.phpt b/Zend/tests/varSyntax/globalNonSimpleVariableError.phpt index 6847c6f2ea7ab..ed04921f89470 100644 --- a/Zend/tests/varSyntax/globalNonSimpleVariableError.phpt +++ b/Zend/tests/varSyntax/globalNonSimpleVariableError.phpt @@ -7,4 +7,4 @@ global $$foo->bar; ?> --EXPECTF-- -Parse error: syntax error, unexpected '->' (T_OBJECT_OPERATOR), expecting ';' or ',' in %s on line %d +Parse error: syntax error, unexpected '->' (T_OBJECT_OPERATOR), expecting ',' or ';' in %s on line %d diff --git a/Zend/zend.h b/Zend/zend.h index cd41cde5ef1b7..48908e1530551 100644 --- a/Zend/zend.h +++ b/Zend/zend.h @@ -170,6 +170,7 @@ struct _zend_class_entry { zend_class_name *trait_names; zend_trait_alias **trait_aliases; zend_trait_precedence **trait_precedences; + HashTable *attributes; union { struct { diff --git a/Zend/zend_API.c b/Zend/zend_API.c index 241cba8151ba4..6e91317a62d34 100644 --- a/Zend/zend_API.c +++ b/Zend/zend_API.c @@ -2596,6 +2596,7 @@ static zend_class_entry *do_register_internal_class(zend_class_entry *orig_class zend_initialize_class_data(class_entry, 0); class_entry->ce_flags = ce_flags | ZEND_ACC_CONSTANTS_UPDATED | ZEND_ACC_LINKED | ZEND_ACC_RESOLVED_PARENT | ZEND_ACC_RESOLVED_INTERFACES; class_entry->info.internal.module = EG(current_module); + class_entry->attributes = NULL; if (class_entry->info.internal.builtin_functions) { zend_register_functions(class_entry, class_entry->info.internal.builtin_functions, &class_entry->function_table, EG(current_module)->type); @@ -3515,7 +3516,7 @@ static zend_always_inline zend_bool is_persistent_class(zend_class_entry *ce) { && ce->info.internal.module->type == MODULE_PERSISTENT; } -ZEND_API int zend_declare_typed_property(zend_class_entry *ce, zend_string *name, zval *property, int access_type, zend_string *doc_comment, zend_type type) /* {{{ */ +ZEND_API int zend_declare_typed_property(zend_class_entry *ce, zend_string *name, zval *property, int access_type, zend_string *doc_comment, HashTable *attributes, zend_type type) /* {{{ */ { zend_property_info *property_info, *property_info_ptr; @@ -3608,6 +3609,7 @@ ZEND_API int zend_declare_typed_property(zend_class_entry *ce, zend_string *name property_info->name = zend_new_interned_string(property_info->name); property_info->flags = access_type; property_info->doc_comment = doc_comment; + property_info->attributes = attributes; property_info->ce = ce; property_info->type = type; @@ -3744,16 +3746,16 @@ ZEND_API int zend_try_assign_typed_ref_zval_ex(zend_reference *ref, zval *zv, ze } /* }}} */ -ZEND_API int zend_declare_property_ex(zend_class_entry *ce, zend_string *name, zval *property, int access_type, zend_string *doc_comment) /* {{{ */ +ZEND_API int zend_declare_property_ex(zend_class_entry *ce, zend_string *name, zval *property, int access_type, zend_string *doc_comment, HashTable *attributes) /* {{{ */ { - return zend_declare_typed_property(ce, name, property, access_type, doc_comment, (zend_type) ZEND_TYPE_INIT_NONE(0)); + return zend_declare_typed_property(ce, name, property, access_type, doc_comment, attributes, (zend_type) ZEND_TYPE_INIT_NONE(0)); } /* }}} */ ZEND_API int zend_declare_property(zend_class_entry *ce, const char *name, size_t name_length, zval *property, int access_type) /* {{{ */ { zend_string *key = zend_string_init(name, name_length, is_persistent_class(ce)); - int ret = zend_declare_property_ex(ce, key, property, access_type, NULL); + int ret = zend_declare_property_ex(ce, key, property, access_type, NULL, NULL); zend_string_release(key); return ret; } @@ -3813,7 +3815,7 @@ ZEND_API int zend_declare_property_stringl(zend_class_entry *ce, const char *nam } /* }}} */ -ZEND_API int zend_declare_class_constant_ex(zend_class_entry *ce, zend_string *name, zval *value, int access_type, zend_string *doc_comment) /* {{{ */ +ZEND_API int zend_declare_class_constant_ex(zend_class_entry *ce, zend_string *name, zval *value, int access_type, zend_string *doc_comment, HashTable *attributes) /* {{{ */ { zend_class_constant *c; @@ -3840,6 +3842,7 @@ ZEND_API int zend_declare_class_constant_ex(zend_class_entry *ce, zend_string *n ZVAL_COPY_VALUE(&c->value, value); Z_ACCESS_FLAGS(c->value) = access_type; c->doc_comment = doc_comment; + c->attributes = attributes; c->ce = ce; if (Z_TYPE_P(value) == IS_CONSTANT_AST) { ce->ce_flags &= ~ZEND_ACC_CONSTANTS_UPDATED; @@ -3865,7 +3868,7 @@ ZEND_API int zend_declare_class_constant(zend_class_entry *ce, const char *name, } else { key = zend_string_init(name, name_length, 0); } - ret = zend_declare_class_constant_ex(ce, key, value, ZEND_ACC_PUBLIC, NULL); + ret = zend_declare_class_constant_ex(ce, key, value, ZEND_ACC_PUBLIC, NULL, NULL); zend_string_release(key); return ret; } diff --git a/Zend/zend_API.h b/Zend/zend_API.h index 44d43d46dedce..a8afac147bc50 100644 --- a/Zend/zend_API.h +++ b/Zend/zend_API.h @@ -351,9 +351,9 @@ ZEND_API zend_bool zend_make_callable(zval *callable, zend_string **callable_nam ZEND_API const char *zend_get_module_version(const char *module_name); ZEND_API int zend_get_module_started(const char *module_name); -ZEND_API int zend_declare_typed_property(zend_class_entry *ce, zend_string *name, zval *property, int access_type, zend_string *doc_comment, zend_type type); +ZEND_API int zend_declare_typed_property(zend_class_entry *ce, zend_string *name, zval *property, int access_type, zend_string *doc_comment, HashTable *attributes, zend_type type); -ZEND_API int zend_declare_property_ex(zend_class_entry *ce, zend_string *name, zval *property, int access_type, zend_string *doc_comment); +ZEND_API int zend_declare_property_ex(zend_class_entry *ce, zend_string *name, zval *property, int access_type, zend_string *doc_comment, HashTable *attributes); ZEND_API int zend_declare_property(zend_class_entry *ce, const char *name, size_t name_length, zval *property, int access_type); ZEND_API int zend_declare_property_null(zend_class_entry *ce, const char *name, size_t name_length, int access_type); ZEND_API int zend_declare_property_bool(zend_class_entry *ce, const char *name, size_t name_length, zend_long value, int access_type); @@ -362,7 +362,7 @@ ZEND_API int zend_declare_property_double(zend_class_entry *ce, const char *name ZEND_API int zend_declare_property_string(zend_class_entry *ce, const char *name, size_t name_length, const char *value, int access_type); ZEND_API int zend_declare_property_stringl(zend_class_entry *ce, const char *name, size_t name_length, const char *value, size_t value_len, int access_type); -ZEND_API int zend_declare_class_constant_ex(zend_class_entry *ce, zend_string *name, zval *value, int access_type, zend_string *doc_comment); +ZEND_API int zend_declare_class_constant_ex(zend_class_entry *ce, zend_string *name, zval *value, int access_type, zend_string *doc_comment, HashTable *attributes); ZEND_API int zend_declare_class_constant(zend_class_entry *ce, const char *name, size_t name_length, zval *value); ZEND_API int zend_declare_class_constant_null(zend_class_entry *ce, const char *name, size_t name_length); ZEND_API int zend_declare_class_constant_long(zend_class_entry *ce, const char *name, size_t name_length, zend_long value); diff --git a/Zend/zend_ast.c b/Zend/zend_ast.c index 7451cf28e92be..8a2a4255bef4c 100644 --- a/Zend/zend_ast.c +++ b/Zend/zend_ast.c @@ -126,6 +126,7 @@ ZEND_API zend_ast *zend_ast_create_decl( ast->flags = flags; ast->lex_pos = LANG_SCNG(yy_text); ast->doc_comment = doc_comment; + ast->attributes = NULL; ast->name = name; ast->child[0] = child0; ast->child[1] = child1; @@ -857,6 +858,9 @@ ZEND_API void ZEND_FASTCALL zend_ast_destroy(zend_ast *ast) if (decl->doc_comment) { zend_string_release_ex(decl->doc_comment, 0); } + if (decl->attributes) { + zend_ast_destroy(decl->attributes); + } zend_ast_destroy(decl->child[0]); zend_ast_destroy(decl->child[1]); zend_ast_destroy(decl->child[2]); @@ -1313,6 +1317,41 @@ static ZEND_COLD void zend_ast_export_class_no_header(smart_str *str, zend_ast_d smart_str_appends(str, "}"); } +static ZEND_COLD void zend_ast_export_attributes(smart_str *str, zend_ast *ast, int indent, zend_bool newlines) { + zend_ast_list *list = zend_ast_get_list(ast); + uint32_t i; + + for (i = 0; i < list->children; i++) { + zend_ast *attr = list->child[i]; + + smart_str_appends(str, "<<"); + smart_str_append(str, zend_ast_get_str(attr->child[0])); + + if (attr->child[1]) { + zend_ast_list *args = zend_ast_get_list(attr->child[1]); + uint32_t j; + + smart_str_appendc(str, '('); + for (j = 0; j < args->children; j++) { + if (j) { + smart_str_appends(str, ", "); + } + zend_ast_export_ex(str, args->child[j], 0, indent); + } + smart_str_appendc(str, ')'); + } + + smart_str_appends(str, ">>"); + + if (newlines) { + smart_str_appendc(str, '\n'); + zend_ast_export_indent(str, indent); + } else { + smart_str_appendc(str, ' '); + } + } +} + static ZEND_COLD void zend_ast_export_type(smart_str *str, zend_ast *ast, int indent) { if (ast->kind == ZEND_AST_TYPE_UNION) { zend_ast_list *list = zend_ast_get_list(ast); @@ -1406,6 +1445,10 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio case ZEND_AST_ARROW_FUNC: case ZEND_AST_METHOD: decl = (zend_ast_decl *) ast; + if (decl->attributes) { + zend_bool newlines = (ast->kind == ZEND_AST_CLOSURE || ast->kind == ZEND_AST_ARROW_FUNC) ? 0 : 1; + zend_ast_export_attributes(str, decl->attributes, indent, newlines); + } if (decl->flags & ZEND_ACC_PUBLIC) { smart_str_appends(str, "public "); } else if (decl->flags & ZEND_ACC_PROTECTED) { @@ -1462,6 +1505,9 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio break; case ZEND_AST_CLASS: decl = (zend_ast_decl *) ast; + if (decl->attributes) { + zend_ast_export_attributes(str, decl->attributes, indent, 1); + } if (decl->flags & ZEND_ACC_INTERFACE) { smart_str_appends(str, "interface "); } else if (decl->flags & ZEND_ACC_TRAIT) { @@ -1517,6 +1563,9 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio zend_ast *type_ast = ast->child[0]; zend_ast *prop_ast = ast->child[1]; + if (ast->child[2]) { + zend_ast_export_attributes(str, ast->child[2], indent, 1); + } if (ast->attr & ZEND_ACC_PUBLIC) { smart_str_appends(str, "public "); } else if (ast->attr & ZEND_ACC_PROTECTED) { @@ -1541,6 +1590,12 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio case ZEND_AST_CLASS_CONST_DECL: smart_str_appends(str, "const "); goto simple_list; + case ZEND_AST_CLASS_CONST_GROUP: + if (ast->child[1]) { + zend_ast_export_attributes(str, ast->child[1], indent, 1); + } + zend_ast_export_ex(str, ast->child[0], 0, indent); + break; case ZEND_AST_NAME_LIST: zend_ast_export_name_list(str, (zend_ast_list*)ast, indent); break; @@ -1783,13 +1838,17 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio case ZEND_AST_NEW: smart_str_appends(str, "new "); if (ast->child[0]->kind == ZEND_AST_CLASS) { + zend_ast_decl *decl = (zend_ast_decl *) ast->child[0]; + if (decl->attributes) { + zend_ast_export_attributes(str, decl->attributes, indent, 0); + } smart_str_appends(str, "class"); if (zend_ast_get_list(ast->child[1])->children) { smart_str_appendc(str, '('); zend_ast_export_ex(str, ast->child[1], 0, indent); smart_str_appendc(str, ')'); } - zend_ast_export_class_no_header(str, (zend_ast_decl *) ast->child[0], indent); + zend_ast_export_class_no_header(str, decl, indent); } else { zend_ast_export_ns_name(str, ast->child[0], 0, indent); smart_str_appendc(str, '('); @@ -2002,6 +2061,9 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio zend_ast_export_indent(str, indent); break; case ZEND_AST_PARAM: + if (ast->child[3]) { + zend_ast_export_attributes(str, ast->child[3], indent, 0); + } if (ast->child[0]) { zend_ast_export_type(str, ast->child[0], indent); smart_str_appendc(str, ' '); @@ -2114,3 +2176,30 @@ ZEND_API ZEND_COLD zend_string *zend_ast_export(const char *prefix, zend_ast *as smart_str_0(&str); return str.s; } + +zend_ast * ZEND_FASTCALL zend_ast_with_attributes(zend_ast *ast, zend_ast *attr) +{ + ZEND_ASSERT(attr->kind == ZEND_AST_ATTRIBUTE_LIST); + + switch (ast->kind) { + case ZEND_AST_FUNC_DECL: + case ZEND_AST_CLOSURE: + case ZEND_AST_METHOD: + case ZEND_AST_CLASS: + case ZEND_AST_ARROW_FUNC: + ((zend_ast_decl *) ast)->attributes = attr; + break; + case ZEND_AST_PROP_GROUP: + ast->child[2] = attr; + break; + case ZEND_AST_PARAM: + ast->child[3] = attr; + break; + case ZEND_AST_CLASS_CONST_GROUP: + ast->child[1] = attr; + break; + EMPTY_SWITCH_DEFAULT_CASE() + } + + return ast; +} diff --git a/Zend/zend_ast.h b/Zend/zend_ast.h index 5b8aae6f96c25..90efaa7bbc107 100644 --- a/Zend/zend_ast.h +++ b/Zend/zend_ast.h @@ -62,6 +62,7 @@ enum _zend_ast_kind { ZEND_AST_TRAIT_ADAPTATIONS, ZEND_AST_USE, ZEND_AST_TYPE_UNION, + ZEND_AST_ATTRIBUTE_LIST, /* 0 child nodes */ ZEND_AST_MAGIC_CONST = 0 << ZEND_AST_NUM_CHILDREN_SHIFT, @@ -138,7 +139,8 @@ enum _zend_ast_kind { ZEND_AST_USE_ELEM, ZEND_AST_TRAIT_ALIAS, ZEND_AST_GROUP_USE, - ZEND_AST_PROP_GROUP, + ZEND_AST_CLASS_CONST_GROUP, + ZEND_AST_ATTRIBUTE, /* 3 child nodes */ ZEND_AST_METHOD_CALL = 3 << ZEND_AST_NUM_CHILDREN_SHIFT, @@ -147,13 +149,14 @@ enum _zend_ast_kind { ZEND_AST_TRY, ZEND_AST_CATCH, - ZEND_AST_PARAM, + ZEND_AST_PROP_GROUP, ZEND_AST_PROP_ELEM, ZEND_AST_CONST_ELEM, /* 4 child nodes */ ZEND_AST_FOR = 4 << ZEND_AST_NUM_CHILDREN_SHIFT, ZEND_AST_FOREACH, + ZEND_AST_PARAM, }; typedef uint16_t zend_ast_kind; @@ -191,6 +194,7 @@ typedef struct _zend_ast_decl { uint32_t flags; unsigned char *lex_pos; zend_string *doc_comment; + zend_ast *attributes; zend_string *name; zend_ast *child[4]; } zend_ast_decl; @@ -311,6 +315,12 @@ static zend_always_inline zend_string *zend_ast_get_constant_name(zend_ast *ast) return Z_STR(((zend_ast_zval *) ast)->val); } +static zend_always_inline HashTable *zend_ast_get_hash(zend_ast *ast) { + zval *zv = zend_ast_get_zval(ast); + ZEND_ASSERT(Z_TYPE_P(zv) == IS_ARRAY); + return Z_ARR_P(zv); +} + static zend_always_inline uint32_t zend_ast_get_num_children(zend_ast *ast) { ZEND_ASSERT(!zend_ast_is_list(ast)); return ast->kind >> ZEND_AST_NUM_CHILDREN_SHIFT; @@ -324,6 +334,12 @@ static zend_always_inline uint32_t zend_ast_get_lineno(zend_ast *ast) { } } +static zend_always_inline zend_ast *zend_ast_create_zval_from_hash(HashTable *hash) { + zval zv; + ZVAL_ARR(&zv, hash); + return zend_ast_create_zval(&zv); +} + static zend_always_inline zend_ast *zend_ast_create_binary_op(uint32_t opcode, zend_ast *op0, zend_ast *op1) { return zend_ast_create_ex(ZEND_AST_BINARY_OP, opcode, op0, op1); } @@ -340,4 +356,7 @@ static zend_always_inline zend_ast *zend_ast_list_rtrim(zend_ast *ast) { } return ast; } + +zend_ast * ZEND_FASTCALL zend_ast_with_attributes(zend_ast *ast, zend_ast *attr); + #endif diff --git a/Zend/zend_attributes.c b/Zend/zend_attributes.c new file mode 100644 index 0000000000000..0ebe7043536c1 --- /dev/null +++ b/Zend/zend_attributes.c @@ -0,0 +1,131 @@ +#include "zend.h" +#include "zend_API.h" +#include "zend_attributes.h" + +ZEND_API zend_class_entry *zend_ce_php_attribute; + +static HashTable internal_validators; + +void zend_attribute_validate_phpattribute(zend_attribute *attr, int target) +{ + if (target != ZEND_ATTRIBUTE_TARGET_CLASS) { + zend_error(E_COMPILE_ERROR, "Only classes can be marked with <>"); + } +} + +ZEND_API zend_attributes_internal_validator zend_attribute_get_validator(zend_string *lcname) +{ + return zend_hash_find_ptr(&internal_validators, lcname); +} + +ZEND_API void zend_attribute_free(zend_attribute *attr) +{ + uint32_t i; + + zend_string_release(attr->name); + zend_string_release(attr->lcname); + + for (i = 0; i < attr->argc; i++) { + zval_ptr_dtor(&attr->argv[i]); + } + + efree(attr); +} + +static zend_attribute *get_attribute(HashTable *attributes, zend_string *lcname, uint32_t offset) +{ + if (attributes) { + zend_attribute *attr; + + ZEND_HASH_FOREACH_PTR(attributes, attr) { + if (attr->offset == offset && zend_string_equals(attr->lcname, lcname)) { + return attr; + } + } ZEND_HASH_FOREACH_END(); + } + + return NULL; +} + +static zend_attribute *get_attribute_str(HashTable *attributes, const char *str, size_t len, uint32_t offset) +{ + if (attributes) { + zend_attribute *attr; + + ZEND_HASH_FOREACH_PTR(attributes, attr) { + if (attr->offset == offset && ZSTR_LEN(attr->lcname) == len) { + if (0 == memcmp(ZSTR_VAL(attr->lcname), str, len)) { + return attr; + } + } + } ZEND_HASH_FOREACH_END(); + } + + return NULL; +} + +ZEND_API zend_attribute *zend_get_attribute(HashTable *attributes, zend_string *lcname) +{ + return get_attribute(attributes, lcname, 0); +} + +ZEND_API zend_attribute *zend_get_attribute_str(HashTable *attributes, const char *str, size_t len) +{ + return get_attribute_str(attributes, str, len, 0); +} + +ZEND_API zend_attribute *zend_get_parameter_attribute(HashTable *attributes, zend_string *lcname, uint32_t offset) +{ + return get_attribute(attributes, lcname, offset + 1); +} + +ZEND_API zend_attribute *zend_get_parameter_attribute_str(HashTable *attributes, const char *str, size_t len, uint32_t offset) +{ + return get_attribute_str(attributes, str, len, offset + 1); +} + +static void attribute_ptr_dtor(zval *v) +{ + zend_attribute_free((zend_attribute *) Z_PTR_P(v)); +} + +ZEND_API void zend_compiler_attribute_register(zend_class_entry *ce, zend_attributes_internal_validator validator) +{ + if (ce->type != ZEND_INTERNAL_CLASS) { + zend_error_noreturn(E_ERROR, "Only internal classes can be registered as compiler attribute"); + } + + zend_string *lcname = zend_string_tolower_ex(ce->name, 1); + zval tmp; + + zend_hash_update_ptr(&internal_validators, lcname, validator); + zend_string_release(lcname); + + if (ce->attributes == NULL) { + ce->attributes = pemalloc(sizeof(HashTable), 1); + zend_hash_init(ce->attributes, 8, NULL, attribute_ptr_dtor, 1); + } + + zend_attribute *attr = pemalloc(ZEND_ATTRIBUTE_SIZE(0), 1); + + attr->name = zend_string_copy(zend_ce_php_attribute->name); + attr->lcname = zend_string_tolower_ex(attr->name, 1); + attr->offset = 0; + attr->argc = 0; + + ZVAL_PTR(&tmp, attr); + zend_hash_next_index_insert(ce->attributes, &tmp); +} + +void zend_register_attribute_ce(void) +{ + zend_hash_init(&internal_validators, 8, NULL, NULL, 1); + + zend_class_entry ce; + + INIT_CLASS_ENTRY(ce, "PhpAttribute", NULL); + zend_ce_php_attribute = zend_register_internal_class(&ce); + zend_ce_php_attribute->ce_flags |= ZEND_ACC_FINAL; + + zend_compiler_attribute_register(zend_ce_php_attribute, zend_attribute_validate_phpattribute); +} diff --git a/Zend/zend_attributes.h b/Zend/zend_attributes.h new file mode 100644 index 0000000000000..979e2682b1d34 --- /dev/null +++ b/Zend/zend_attributes.h @@ -0,0 +1,44 @@ +#ifndef ZEND_ATTRIBUTES_H +#define ZEND_ATTRIBUTES_H + +#define ZEND_ATTRIBUTE_TARGET_CLASS 1 +#define ZEND_ATTRIBUTE_TARGET_FUNCTION 2 +#define ZEND_ATTRIBUTE_TARGET_METHOD 4 +#define ZEND_ATTRIBUTE_TARGET_PROPERTY 8 +#define ZEND_ATTRIBUTE_TARGET_CLASS_CONST 16 +#define ZEND_ATTRIBUTE_TARGET_PARAMETER 32 +#define ZEND_ATTRIBUTE_TARGET_ALL 63 + +#define ZEND_ATTRIBUTE_SIZE(argc) (sizeof(zend_attribute) + sizeof(zval) * (argc) - sizeof(zval)) + +BEGIN_EXTERN_C() + +extern ZEND_API zend_class_entry *zend_ce_php_attribute; + +typedef struct _zend_attribute { + zend_string *name; + zend_string *lcname; + /* Parameter offsets start at 1, everything else uses 0. */ + uint32_t offset; + uint32_t argc; + zval argv[1]; +} zend_attribute; + +typedef void (*zend_attributes_internal_validator)(zend_attribute *attr, int target); + +ZEND_API void zend_attribute_free(zend_attribute *attr); + +ZEND_API zend_attribute *zend_get_attribute(HashTable *attributes, zend_string *lcname); +ZEND_API zend_attribute *zend_get_attribute_str(HashTable *attributes, const char *str, size_t len); + +ZEND_API zend_attribute *zend_get_parameter_attribute(HashTable *attributes, zend_string *lcname, uint32_t offset); +ZEND_API zend_attribute *zend_get_parameter_attribute_str(HashTable *attributes, const char *str, size_t len, uint32_t offset); + +ZEND_API void zend_compiler_attribute_register(zend_class_entry *ce, zend_attributes_internal_validator validator); +ZEND_API zend_attributes_internal_validator zend_attribute_get_validator(zend_string *lcname); + +void zend_register_attribute_ce(void); + +END_EXTERN_C() + +#endif diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 5dfa5f2a3d2e0..e283d8dd8ed43 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -20,6 +20,7 @@ #include #include "zend.h" +#include "zend_attributes.h" #include "zend_compile.h" #include "zend_constants.h" #include "zend_llist.h" @@ -1045,6 +1046,10 @@ ZEND_API void function_add_ref(zend_function *function) /* {{{ */ ZEND_MAP_PTR_INIT(op_array->run_time_cache, zend_arena_alloc(&CG(arena), sizeof(void*))); ZEND_MAP_PTR_SET(op_array->run_time_cache, NULL); } + + if (op_array->attributes) { + GC_ADDREF(op_array->attributes); + } } if (function->common.function_name) { @@ -1816,6 +1821,7 @@ ZEND_API void zend_initialize_class_data(zend_class_entry *ce, zend_bool nullify } else { ZEND_MAP_PTR_INIT(ce->static_members_table, &ce->default_static_members_table); ce->info.user.doc_comment = NULL; + ce->attributes = NULL; } ce->default_properties_count = 0; @@ -2372,7 +2378,7 @@ static inline zend_bool zend_is_variable_or_call(zend_ast *ast) /* {{{ */ static inline zend_bool zend_is_unticked_stmt(zend_ast *ast) /* {{{ */ { return ast->kind == ZEND_AST_STMT_LIST || ast->kind == ZEND_AST_LABEL - || ast->kind == ZEND_AST_PROP_DECL || ast->kind == ZEND_AST_CLASS_CONST_DECL + || ast->kind == ZEND_AST_PROP_DECL || ast->kind == ZEND_AST_CLASS_CONST_GROUP || ast->kind == ZEND_AST_USE_TRAIT || ast->kind == ZEND_AST_METHOD; } /* }}} */ @@ -5716,6 +5722,74 @@ static zend_bool zend_is_valid_default_value(zend_type type, zval *value) return 0; } +static zend_attribute *zend_compile_attribute(zend_ast *ast, uint32_t offset) /* {{{ */ +{ + ZEND_ASSERT(ast->kind == ZEND_AST_ATTRIBUTE); + + zend_ast_list *list = ast->child[1] ? zend_ast_get_list(ast->child[1]) : NULL; + zend_attribute *attr = emalloc(ZEND_ATTRIBUTE_SIZE(list ? list->children : 0)); + + attr->name = zend_resolve_class_name_ast(ast->child[0]); + attr->lcname = zend_string_tolower(attr->name); + attr->offset = offset; + attr->argc = list ? list->children : 0; + + if (list) { + ZEND_ASSERT(ast->child[1]->kind == ZEND_AST_ARG_LIST); + + uint32_t i; + + for (i = 0; i < list->children; i++) { + zend_const_expr_to_zval(&attr->argv[i], list->child[i]); + } + } + + return attr; +} +/* }}} */ + +static void attribute_ptr_dtor(zval *v) /* {{{ */ +{ + zend_attribute_free((zend_attribute *) Z_PTR_P(v)); +} +/* }}} */ + +static zend_always_inline HashTable *create_attribute_array() /* {{{ */ +{ + HashTable *attributes; + + ALLOC_HASHTABLE(attributes); + zend_hash_init(attributes, 8, NULL, attribute_ptr_dtor, 0); + + return attributes; +} +/* }}} */ + +static void zend_compile_attributes(HashTable *attributes, zend_ast *ast, uint32_t offset, int target) /* {{{ */ +{ + zend_ast_list *list = zend_ast_get_list(ast); + uint32_t i; + + zval tmp; + + ZEND_ASSERT(ast->kind == ZEND_AST_ATTRIBUTE_LIST); + + for (i = 0; i < list->children; i++) { + zend_attribute *attr = zend_compile_attribute(list->child[i], offset); + + // Validate internal attribute + zend_attributes_internal_validator validator = zend_attribute_get_validator(attr->lcname); + + if (validator != NULL) { + validator(attr, target); + } + + ZVAL_PTR(&tmp, attr); + zend_hash_next_index_insert(attributes, &tmp); + } +} +/* }}} */ + void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast, uint32_t fallback_return_type) /* {{{ */ { zend_ast_list *list = zend_ast_get_list(ast); @@ -5750,6 +5824,7 @@ void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast, uint32_t fall zend_ast *type_ast = param_ast->child[0]; zend_ast *var_ast = param_ast->child[1]; zend_ast *default_ast = param_ast->child[2]; + zend_ast *attributes_ast = param_ast->child[3]; zend_string *name = zval_make_interned_string(zend_ast_get_zval(var_ast)); zend_bool is_ref = (param_ast->attr & ZEND_PARAM_REF) != 0; zend_bool is_variadic = (param_ast->attr & ZEND_PARAM_VARIADIC) != 0; @@ -5819,6 +5894,14 @@ void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast, uint32_t fall arg_info->name = zend_string_copy(name); arg_info->type = (zend_type) ZEND_TYPE_INIT_NONE(0); + if (attributes_ast) { + if (!op_array->attributes) { + op_array->attributes = create_attribute_array(); + } + + zend_compile_attributes(op_array->attributes, attributes_ast, i + 1, ZEND_ATTRIBUTE_TARGET_PARAMETER); + } + if (type_ast) { uint32_t default_type = default_ast ? Z_TYPE(default_node.u.constant) : IS_UNDEF; @@ -6290,6 +6373,15 @@ void zend_compile_func_decl(znode *result, zend_ast *ast, zend_bool toplevel) /* if (decl->doc_comment) { op_array->doc_comment = zend_string_copy(decl->doc_comment); } + if (decl->attributes) { + int target = ZEND_ATTRIBUTE_TARGET_FUNCTION; + + if (is_method) { + target = ZEND_ATTRIBUTE_TARGET_METHOD; + } + op_array->attributes = create_attribute_array(); + zend_compile_attributes(op_array->attributes, decl->attributes, 0, target); + } if (decl->kind == ZEND_AST_CLOSURE || decl->kind == ZEND_AST_ARROW_FUNC) { op_array->fn_flags |= ZEND_ACC_CLOSURE; } @@ -6372,7 +6464,7 @@ void zend_compile_func_decl(znode *result, zend_ast *ast, zend_bool toplevel) /* } /* }}} */ -void zend_compile_prop_decl(zend_ast *ast, zend_ast *type_ast, uint32_t flags) /* {{{ */ +void zend_compile_prop_decl(zend_ast *ast, zend_ast *type_ast, uint32_t flags, HashTable *attributes) /* {{{ */ { zend_ast_list *list = zend_ast_get_list(ast); zend_class_entry *ce = CG(active_class_entry); @@ -6447,17 +6539,29 @@ void zend_compile_prop_decl(zend_ast *ast, zend_ast *type_ast, uint32_t flags) / ZVAL_UNDEF(&value_zv); } - zend_declare_typed_property(ce, name, &value_zv, flags, doc_comment, type); + zend_declare_typed_property(ce, name, &value_zv, flags, doc_comment, attributes, type); } } /* }}} */ void zend_compile_prop_group(zend_ast *list) /* {{{ */ { + HashTable *attributes = NULL; + zend_ast *type_ast = list->child[0]; zend_ast *prop_ast = list->child[1]; - zend_compile_prop_decl(prop_ast, type_ast, list->attr); + if (list->child[2]) { + if (zend_ast_get_list(prop_ast)->children > 1) { + zend_error_noreturn(E_COMPILE_ERROR, "Cannot apply attributes to a group of properties"); + return; + } + + attributes = create_attribute_array(); + zend_compile_attributes(attributes, list->child[2], 0, ZEND_ATTRIBUTE_TARGET_PROPERTY); + } + + zend_compile_prop_decl(prop_ast, type_ast, list->attr, attributes); } /* }}} */ @@ -6473,10 +6577,11 @@ static void zend_check_const_and_trait_alias_attr(uint32_t attr, const char* ent } /* }}} */ -void zend_compile_class_const_decl(zend_ast *ast) /* {{{ */ +void zend_compile_class_const_decl(zend_ast *ast, zend_ast *attr_ast) /* {{{ */ { zend_ast_list *list = zend_ast_get_list(ast); zend_class_entry *ce = CG(active_class_entry); + HashTable *attributes = NULL; uint32_t i; if ((ce->ce_flags & ZEND_ACC_TRAIT) != 0) { @@ -6484,6 +6589,16 @@ void zend_compile_class_const_decl(zend_ast *ast) /* {{{ */ return; } + if (attr_ast) { + if (list->children > 1) { + zend_error_noreturn(E_COMPILE_ERROR, "Cannot apply attributes to a group of constants"); + return; + } + + attributes = create_attribute_array(); + zend_compile_attributes(attributes, attr_ast, 0, ZEND_ATTRIBUTE_TARGET_CLASS_CONST); + } + for (i = 0; i < list->children; ++i) { zend_ast *const_ast = list->child[i]; zend_ast *name_ast = const_ast->child[0]; @@ -6498,7 +6613,7 @@ void zend_compile_class_const_decl(zend_ast *ast) /* {{{ */ } zend_const_expr_to_zval(&value_zv, value_ast); - zend_declare_class_constant_ex(ce, name, &value_zv, ast->attr, doc_comment); + zend_declare_class_constant_ex(ce, name, &value_zv, ast->attr, doc_comment, attributes); } } /* }}} */ @@ -6711,6 +6826,10 @@ void zend_compile_class_decl(znode *result, zend_ast *ast, zend_bool toplevel) / if (decl->doc_comment) { ce->info.user.doc_comment = zend_string_copy(decl->doc_comment); } + if (decl->attributes) { + ce->attributes = create_attribute_array(); + zend_compile_attributes(ce->attributes, decl->attributes, 0, ZEND_ATTRIBUTE_TARGET_CLASS); + } if (UNEXPECTED((decl->flags & ZEND_ACC_ANON_CLASS))) { /* Serialization is not supported for anonymous classes */ @@ -8801,8 +8920,8 @@ void zend_compile_stmt(zend_ast *ast) /* {{{ */ case ZEND_AST_PROP_GROUP: zend_compile_prop_group(ast); break; - case ZEND_AST_CLASS_CONST_DECL: - zend_compile_class_const_decl(ast); + case ZEND_AST_CLASS_CONST_GROUP: + zend_compile_class_const_decl(ast->child[0], ast->child[1]); break; case ZEND_AST_USE_TRAIT: zend_compile_use_trait(ast); diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h index a90219b1b4453..4d47d0da5debc 100644 --- a/Zend/zend_compile.h +++ b/Zend/zend_compile.h @@ -120,6 +120,7 @@ typedef struct _zend_file_context { typedef union _zend_parser_stack_elem { zend_ast *ast; zend_string *str; + HashTable *hash; zend_ulong num; unsigned char *ptr; } zend_parser_stack_elem; @@ -348,6 +349,7 @@ typedef struct _zend_property_info { uint32_t flags; zend_string *name; zend_string *doc_comment; + HashTable *attributes; zend_class_entry *ce; zend_type type; } zend_property_info; @@ -364,6 +366,7 @@ typedef struct _zend_property_info { typedef struct _zend_class_constant { zval value; /* access flags are stored in reserved: zval.u2.access_flags */ zend_string *doc_comment; + HashTable *attributes; zend_class_entry *ce; } zend_class_constant; @@ -403,6 +406,7 @@ struct _zend_op_array { uint32_t num_args; uint32_t required_num_args; zend_arg_info *arg_info; + HashTable *attributes; /* END of common elements */ int cache_size; /* number of run_time_cache_slots * sizeof(void*) */ @@ -452,6 +456,7 @@ typedef struct _zend_internal_function { uint32_t num_args; uint32_t required_num_args; zend_internal_arg_info *arg_info; + HashTable *attributes; /* END of common elements */ zif_handler handler; @@ -475,6 +480,7 @@ union _zend_function { uint32_t num_args; uint32_t required_num_args; zend_arg_info *arg_info; /* index -1 represents the return value info, if any */ + HashTable *attributes; } common; zend_op_array op_array; diff --git a/Zend/zend_default_classes.c b/Zend/zend_default_classes.c index 2c42763f0c5e5..63cdf66d580e8 100644 --- a/Zend/zend_default_classes.c +++ b/Zend/zend_default_classes.c @@ -19,6 +19,7 @@ #include "zend.h" #include "zend_API.h" +#include "zend_attributes.h" #include "zend_builtin_functions.h" #include "zend_interfaces.h" #include "zend_exceptions.h" @@ -34,4 +35,5 @@ ZEND_API void zend_register_default_classes(void) zend_register_closure_ce(); zend_register_generator_ce(); zend_register_weakref_ce(); + zend_register_attribute_ce(); } diff --git a/Zend/zend_execute.c b/Zend/zend_execute.c index 224dfb71d07fe..11e1904461cd9 100644 --- a/Zend/zend_execute.c +++ b/Zend/zend_execute.c @@ -137,6 +137,7 @@ ZEND_API const zend_internal_function zend_pass_function = { 0, /* num_args */ 0, /* required_num_args */ NULL, /* arg_info */ + NULL, /* attributes */ ZEND_FN(pass), /* handler */ NULL, /* module */ {NULL,NULL,NULL,NULL} /* reserved */ diff --git a/Zend/zend_inheritance.c b/Zend/zend_inheritance.c index f42998a8f8ae4..da8a94252172a 100644 --- a/Zend/zend_inheritance.c +++ b/Zend/zend_inheritance.c @@ -1973,6 +1973,7 @@ static void zend_do_traits_property_binding(zend_class_entry *ce, zend_class_ent zval* prop_value; uint32_t flags; zend_string *doc_comment; + HashTable *attributes = NULL; /* In the following steps the properties are inserted into the property table * for that, a very strict approach is applied: @@ -2072,8 +2073,15 @@ static void zend_do_traits_property_binding(zend_class_entry *ce, zend_class_ent Z_TRY_ADDREF_P(prop_value); doc_comment = property_info->doc_comment ? zend_string_copy(property_info->doc_comment) : NULL; + if (property_info->attributes) { + attributes = property_info->attributes; + + if (!(GC_FLAGS(attributes) & IS_ARRAY_IMMUTABLE)) { + GC_ADDREF(attributes); + } + } zend_type_copy_ctor(&property_info->type, /* persistent */ 0); - zend_declare_typed_property(ce, prop_name, prop_value, flags, doc_comment, property_info->type); + zend_declare_typed_property(ce, prop_name, prop_value, flags, doc_comment, attributes, property_info->type); zend_string_release_ex(prop_name, 0); } ZEND_HASH_FOREACH_END(); } diff --git a/Zend/zend_language_parser.y b/Zend/zend_language_parser.y index 18290acc446f5..7acfb7c29c2a1 100644 --- a/Zend/zend_language_parser.y +++ b/Zend/zend_language_parser.y @@ -258,6 +258,8 @@ static YYSIZE_T zend_yytnamerr(char*, const char*); %type isset_variable type return_type type_expr type_without_static %type identifier type_expr_without_static union_type_without_static %type inline_function union_type +%type attributed_statement attributed_class_statement attributed_parameter +%type attribute_arguments attribute_decl attribute attributes %type returns_ref function fn is_reference is_variadic variable_modifiers %type method_modifiers non_empty_member_modifiers member_modifier @@ -312,12 +314,41 @@ name: | T_NS_SEPARATOR namespace_name { $$ = $2; $$->attr = ZEND_NAME_FQ; } ; -top_statement: - statement { $$ = $1; } - | function_declaration_statement { $$ = $1; } +attribute_arguments: + expr + { $$ = zend_ast_create_list(1, ZEND_AST_ARG_LIST, $1); } + | attribute_arguments ',' expr + { $$ = zend_ast_list_add($1, $3); } +; + +attribute_decl: + class_name_reference + { $$ = zend_ast_create(ZEND_AST_ATTRIBUTE, $1, NULL); } + | class_name_reference '(' ')' + { $$ = zend_ast_create(ZEND_AST_ATTRIBUTE, $1, NULL); } + | class_name_reference '(' attribute_arguments ')' + { $$ = zend_ast_create(ZEND_AST_ATTRIBUTE, $1, $3); } +; + +attribute: + T_SL attribute_decl T_SR { $$ = $2; } +; + +attributes: + attribute { $$ = zend_ast_create_list(1, ZEND_AST_ATTRIBUTE_LIST, $1); } + | attributes attribute { $$ = zend_ast_list_add($1, $2); } +; + +attributed_statement: + function_declaration_statement { $$ = $1; } | class_declaration_statement { $$ = $1; } | trait_declaration_statement { $$ = $1; } | interface_declaration_statement { $$ = $1; } + +top_statement: + statement { $$ = $1; } + | attributed_statement { $$ = $1; } + | attributes attributed_statement { $$ = zend_ast_with_attributes($2, $1); } | T_HALT_COMPILER '(' ')' ';' { $$ = zend_ast_create(ZEND_AST_HALT_COMPILER, zend_ast_create_zval_from_long(zend_get_scanned_file_offset())); @@ -415,10 +446,8 @@ inner_statement_list: inner_statement: statement { $$ = $1; } - | function_declaration_statement { $$ = $1; } - | class_declaration_statement { $$ = $1; } - | trait_declaration_statement { $$ = $1; } - | interface_declaration_statement { $$ = $1; } + | attributed_statement { $$ = $1; } + | attributes attributed_statement { $$ = zend_ast_with_attributes($2, $1); } | T_HALT_COMPILER '(' ')' ';' { $$ = NULL; zend_throw_exception(zend_ce_compile_error, "__HALT_COMPILER() can only be used from the outermost scope", 0); YYERROR; } @@ -644,17 +673,22 @@ parameter_list: non_empty_parameter_list: - parameter + attributed_parameter { $$ = zend_ast_create_list(1, ZEND_AST_PARAM_LIST, $1); } - | non_empty_parameter_list ',' parameter + | non_empty_parameter_list ',' attributed_parameter { $$ = zend_ast_list_add($1, $3); } ; +attributed_parameter: + attributes parameter { $$ = zend_ast_with_attributes($2, $1); } + | parameter { $$ = $1; } +; + parameter: optional_type_without_static is_reference is_variadic T_VARIABLE - { $$ = zend_ast_create_ex(ZEND_AST_PARAM, $2 | $3, $1, $4, NULL); } + { $$ = zend_ast_create_ex(ZEND_AST_PARAM, $2 | $3, $1, $4, NULL, NULL); } | optional_type_without_static is_reference is_variadic T_VARIABLE '=' expr - { $$ = zend_ast_create_ex(ZEND_AST_PARAM, $2 | $3, $1, $4, $6); } + { $$ = zend_ast_create_ex(ZEND_AST_PARAM, $2 | $3, $1, $4, $6, NULL); } ; @@ -744,7 +778,6 @@ static_var: | T_VARIABLE '=' expr { $$ = zend_ast_create(ZEND_AST_STATIC, $1, $3); } ; - class_statement_list: class_statement_list class_statement { $$ = zend_ast_list_add($1, $2); } @@ -753,18 +786,22 @@ class_statement_list: ; -class_statement: +attributed_class_statement: variable_modifiers optional_type_without_static property_list ';' - { $$ = zend_ast_create(ZEND_AST_PROP_GROUP, $2, $3); + { $$ = zend_ast_create(ZEND_AST_PROP_GROUP, $2, $3, NULL); $$->attr = $1; } | method_modifiers T_CONST class_const_list ';' - { $$ = $3; $$->attr = $1; } - | T_USE class_name_list trait_adaptations - { $$ = zend_ast_create(ZEND_AST_USE_TRAIT, $2, $3); } + { $$ = zend_ast_create(ZEND_AST_CLASS_CONST_GROUP, $3, NULL); $3->attr = $1; } | method_modifiers function returns_ref identifier backup_doc_comment '(' parameter_list ')' return_type backup_fn_flags method_body backup_fn_flags { $$ = zend_ast_create_decl(ZEND_AST_METHOD, $3 | $1 | $12, $2, $5, zend_ast_get_str($4), $7, NULL, $11, $9); CG(extra_fn_flags) = $10; } + +class_statement: + attributed_class_statement { $$ = $1; } + | attributes attributed_class_statement { $$ = zend_ast_with_attributes($2, $1); } + | T_USE class_name_list trait_adaptations + { $$ = zend_ast_create(ZEND_AST_USE_TRAIT, $2, $3); } ; class_name_list: @@ -906,6 +943,8 @@ new_expr: { $$ = zend_ast_create(ZEND_AST_NEW, $2, $3); } | T_NEW anonymous_class { $$ = $2; } + | T_NEW attributes anonymous_class + { zend_ast_with_attributes($3->child[0], $2); $$ = $3; } ; expr: @@ -1026,7 +1065,10 @@ expr: | T_YIELD_FROM expr { $$ = zend_ast_create(ZEND_AST_YIELD_FROM, $2); CG(extra_fn_flags) |= ZEND_ACC_GENERATOR; } | T_THROW expr { $$ = zend_ast_create(ZEND_AST_THROW, $2); } | inline_function { $$ = $1; } + | attributes inline_function { $$ = zend_ast_with_attributes($2, $1); } | T_STATIC inline_function { $$ = $2; ((zend_ast_decl *) $$)->flags |= ZEND_ACC_STATIC; } + | attributes T_STATIC inline_function + { $$ = zend_ast_with_attributes($3, $1); ((zend_ast_decl *) $$)->flags |= ZEND_ACC_STATIC; } ; diff --git a/Zend/zend_opcode.c b/Zend/zend_opcode.c index 0dd42f8e6e1ba..5c71d884474b8 100644 --- a/Zend/zend_opcode.c +++ b/Zend/zend_opcode.c @@ -63,6 +63,7 @@ void init_op_array(zend_op_array *op_array, zend_uchar type, int initial_ops_siz op_array->function_name = NULL; op_array->filename = zend_get_compiled_filename(); op_array->doc_comment = NULL; + op_array->attributes = NULL; op_array->arg_info = NULL; op_array->num_args = 0; @@ -317,6 +318,9 @@ ZEND_API void destroy_zend_class(zval *zv) if (prop_info->doc_comment) { zend_string_release_ex(prop_info->doc_comment, 0); } + if (prop_info->attributes) { + zend_array_release(prop_info->attributes); + } zend_type_release(prop_info->type, /* persistent */ 0); } } ZEND_HASH_FOREACH_END(); @@ -332,6 +336,9 @@ ZEND_API void destroy_zend_class(zval *zv) if (c->doc_comment) { zend_string_release_ex(c->doc_comment, 0); } + if (c->attributes) { + zend_array_release(c->attributes); + } } } ZEND_HASH_FOREACH_END(); } @@ -350,6 +357,9 @@ ZEND_API void destroy_zend_class(zval *zv) if (ce->info.user.doc_comment) { zend_string_release_ex(ce->info.user.doc_comment, 0); } + if (ce->attributes) { + zend_array_release(ce->attributes); + } if (ce->num_traits > 0) { _destroy_zend_class_traits_info(ce); @@ -401,6 +411,9 @@ ZEND_API void destroy_zend_class(zval *zv) if (c->doc_comment) { zend_string_release_ex(c->doc_comment, 1); } + if (c->attributes) { + zend_array_release(c->attributes); + } } free(c); } ZEND_HASH_FOREACH_END(); @@ -483,6 +496,9 @@ ZEND_API void destroy_op_array(zend_op_array *op_array) if (op_array->doc_comment) { zend_string_release_ex(op_array->doc_comment, 0); } + if (op_array->attributes) { + zend_array_release(op_array->attributes); + } if (op_array->live_range) { efree(op_array->live_range); } diff --git a/configure.ac b/configure.ac index 2c3928b6a9678..c5f4dd47bcc30 100644 --- a/configure.ac +++ b/configure.ac @@ -1469,7 +1469,7 @@ PHP_ADD_SOURCES(Zend, \ zend_execute_API.c zend_highlight.c zend_llist.c \ zend_vm_opcodes.c zend_opcode.c zend_operators.c zend_ptr_stack.c zend_stack.c \ zend_variables.c zend.c zend_API.c zend_extensions.c zend_hash.c \ - zend_list.c zend_builtin_functions.c \ + zend_list.c zend_builtin_functions.c zend_attributes.c \ zend_ini.c zend_sort.c zend_multibyte.c zend_ts_hash.c zend_stream.c \ zend_iterators.c zend_interfaces.c zend_exceptions.c zend_strtod.c zend_gc.c \ zend_closures.c zend_weakrefs.c zend_float.c zend_string.c zend_signal.c zend_generators.c \ diff --git a/ext/opcache/zend_file_cache.c b/ext/opcache/zend_file_cache.c index 631d712ba4a38..17def06aac973 100644 --- a/ext/opcache/zend_file_cache.c +++ b/ext/opcache/zend_file_cache.c @@ -21,6 +21,7 @@ #include "zend_compile.h" #include "zend_vm.h" #include "zend_interfaces.h" +#include "zend_attributes.h" #include "php.h" #ifdef ZEND_WIN32 @@ -162,6 +163,25 @@ static int zend_file_cache_flock(int fd, int type) } \ } while (0) +#define SERIALIZE_ATTRIBUTES(attributes) do { \ + if ((attributes) && !IS_SERIALIZED(attributes)) { \ + HashTable *ht; \ + SERIALIZE_PTR(attributes); \ + ht = (attributes); \ + UNSERIALIZE_PTR(ht); \ + zend_file_cache_serialize_hash(ht, script, info, buf, zend_file_cache_serialize_attribute); \ + } \ +} while (0) + +#define UNSERIALIZE_ATTRIBUTES(attributes) do { \ + if ((attributes) && !IS_UNSERIALIZED(attributes)) { \ + HashTable *ht; \ + UNSERIALIZE_PTR(attributes); \ + ht = (attributes); \ + zend_file_cache_unserialize_hash(ht, script, buf, zend_file_cache_unserialize_attribute, NULL); \ + } \ +} while (0) + static const uint32_t uninitialized_bucket[-HT_MIN_MASK] = {HT_INVALID_IDX, HT_INVALID_IDX}; @@ -379,6 +399,33 @@ static void zend_file_cache_serialize_zval(zval *zv, } } +static void zend_file_cache_serialize_attribute(zval *zv, + zend_persistent_script *script, + zend_file_cache_metainfo *info, + void *buf) +{ + if (!IS_SERIALIZED(Z_PTR_P(zv))) { + zend_attribute *attr = Z_PTR_P(zv); + uint32_t i; + + SERIALIZE_PTR(Z_PTR_P(zv)); + attr = Z_PTR_P(zv); + UNSERIALIZE_PTR(attr); + + if (!IS_SERIALIZED(attr->name)) { + SERIALIZE_STR(attr->name); + } + + if (!IS_SERIALIZED(attr->lcname)) { + SERIALIZE_STR(attr->lcname); + } + + for (i = 0; i < attr->argc; i++) { + zend_file_cache_serialize_zval(&attr->argv[i], script, info, buf); + } + } +} + static void zend_file_cache_serialize_type( zend_type *type, zend_persistent_script *script, zend_file_cache_metainfo *info, void *buf) { @@ -429,6 +476,7 @@ static void zend_file_cache_serialize_op_array(zend_op_array *op_arra SERIALIZE_PTR(op_array->live_range); SERIALIZE_PTR(op_array->scope); SERIALIZE_STR(op_array->doc_comment); + SERIALIZE_ATTRIBUTES(op_array->attributes); SERIALIZE_PTR(op_array->try_catch_array); SERIALIZE_PTR(op_array->prototype); return; @@ -555,6 +603,7 @@ static void zend_file_cache_serialize_op_array(zend_op_array *op_arra SERIALIZE_PTR(op_array->live_range); SERIALIZE_PTR(op_array->scope); SERIALIZE_STR(op_array->doc_comment); + SERIALIZE_ATTRIBUTES(op_array->attributes); SERIALIZE_PTR(op_array->try_catch_array); SERIALIZE_PTR(op_array->prototype); @@ -599,6 +648,7 @@ static void zend_file_cache_serialize_prop_info(zval *zv, if (prop->doc_comment) { SERIALIZE_STR(prop->doc_comment); } + SERIALIZE_ATTRIBUTES(prop->attributes); zend_file_cache_serialize_type(&prop->type, script, info, buf); } } @@ -625,6 +675,8 @@ static void zend_file_cache_serialize_class_constant(zval *z if (c->doc_comment) { SERIALIZE_STR(c->doc_comment); } + + SERIALIZE_ATTRIBUTES(c->attributes); } } } @@ -677,6 +729,7 @@ static void zend_file_cache_serialize_class(zval *zv, zend_file_cache_serialize_hash(&ce->constants_table, script, info, buf, zend_file_cache_serialize_class_constant); SERIALIZE_STR(ce->info.user.filename); SERIALIZE_STR(ce->info.user.doc_comment); + SERIALIZE_ATTRIBUTES(ce->attributes); zend_file_cache_serialize_hash(&ce->properties_info, script, info, buf, zend_file_cache_serialize_prop_info); if (ce->properties_info_table) { @@ -1120,6 +1173,29 @@ static void zend_file_cache_unserialize_zval(zval *zv, } } +static void zend_file_cache_unserialize_attribute(zval *zv, zend_persistent_script *script, void *buf) +{ + if (!IS_UNSERIALIZED(Z_PTR_P(zv))) { + zend_attribute *attr; + uint32_t i; + + UNSERIALIZE_PTR(Z_PTR_P(zv)); + attr = Z_PTR_P(zv); + + if (!IS_UNSERIALIZED(attr->name)) { + UNSERIALIZE_STR(attr->name); + } + + if (!IS_UNSERIALIZED(attr->lcname)) { + UNSERIALIZE_STR(attr->lcname); + } + + for (i = 0; i < attr->argc; i++) { + zend_file_cache_unserialize_zval(&attr->argv[i], script, buf); + } + } +} + static void zend_file_cache_unserialize_type( zend_type *type, zend_persistent_script *script, void *buf) { @@ -1167,6 +1243,7 @@ static void zend_file_cache_unserialize_op_array(zend_op_array *op_arr UNSERIALIZE_PTR(op_array->live_range); UNSERIALIZE_PTR(op_array->scope); UNSERIALIZE_STR(op_array->doc_comment); + UNSERIALIZE_ATTRIBUTES(op_array->attributes); UNSERIALIZE_PTR(op_array->try_catch_array); UNSERIALIZE_PTR(op_array->prototype); return; @@ -1282,6 +1359,7 @@ static void zend_file_cache_unserialize_op_array(zend_op_array *op_arr UNSERIALIZE_PTR(op_array->live_range); UNSERIALIZE_PTR(op_array->scope); UNSERIALIZE_STR(op_array->doc_comment); + UNSERIALIZE_ATTRIBUTES(op_array->attributes); UNSERIALIZE_PTR(op_array->try_catch_array); UNSERIALIZE_PTR(op_array->prototype); @@ -1335,6 +1413,7 @@ static void zend_file_cache_unserialize_prop_info(zval *zv, if (prop->doc_comment) { UNSERIALIZE_STR(prop->doc_comment); } + UNSERIALIZE_ATTRIBUTES(prop->attributes); zend_file_cache_unserialize_type(&prop->type, script, buf); } } @@ -1359,6 +1438,7 @@ static void zend_file_cache_unserialize_class_constant(zval * if (c->doc_comment) { UNSERIALIZE_STR(c->doc_comment); } + UNSERIALIZE_ATTRIBUTES(c->attributes); } } } @@ -1407,6 +1487,7 @@ static void zend_file_cache_unserialize_class(zval *zv, script, buf, zend_file_cache_unserialize_class_constant, NULL); UNSERIALIZE_STR(ce->info.user.filename); UNSERIALIZE_STR(ce->info.user.doc_comment); + UNSERIALIZE_ATTRIBUTES(ce->attributes); zend_file_cache_unserialize_hash(&ce->properties_info, script, buf, zend_file_cache_unserialize_prop_info, NULL); diff --git a/ext/opcache/zend_persist.c b/ext/opcache/zend_persist.c index 40f37b7bf76a4..46142a4627aa0 100644 --- a/ext/opcache/zend_persist.c +++ b/ext/opcache/zend_persist.c @@ -28,6 +28,7 @@ #include "zend_constants.h" #include "zend_operators.h" #include "zend_interfaces.h" +#include "zend_attributes.h" #ifdef HAVE_JIT # include "Optimizer/zend_func_info.h" @@ -259,6 +260,39 @@ static void zend_persist_zval(zval *z) } } +static HashTable *zend_persist_attributes(HashTable *attributes) +{ + HashTable *ptr = zend_shared_alloc_get_xlat_entry(attributes); + + if (!ptr) { + uint32_t i; + zval *v; + + zend_hash_persist(attributes); + + ZEND_HASH_FOREACH_VAL(attributes, v) { + zend_attribute *attr = Z_PTR_P(v); + zend_attribute *copy = zend_shared_memdup(attr, ZEND_ATTRIBUTE_SIZE(attr->argc)); + + zend_accel_store_interned_string(copy->name); + zend_accel_store_interned_string(copy->lcname); + + for (i = 0; i < copy->argc; i++) { + zend_persist_zval(©->argv[i]); + } + + ZVAL_PTR(v, copy); + efree(attr); + } ZEND_HASH_FOREACH_END(); + + ptr = zend_shared_memdup_put_free(attributes, sizeof(HashTable)); + GC_SET_REFCOUNT(ptr, 2); + GC_TYPE_INFO(ptr) = IS_ARRAY | (IS_ARRAY_IMMUTABLE << GC_FLAGS_SHIFT); + } + + return ptr; +} + static void zend_persist_type(zend_type *type) { if (ZEND_TYPE_HAS_LIST(*type)) { zend_type *list_type; @@ -376,6 +410,10 @@ static void zend_persist_op_array_ex(zend_op_array *op_array, zend_persistent_sc op_array->doc_comment = NULL; } } + if (op_array->attributes) { + op_array->attributes = zend_persist_attributes(op_array->attributes); + } + if (op_array->try_catch_array) { op_array->try_catch_array = zend_shared_alloc_get_xlat_entry(op_array->try_catch_array); ZEND_ASSERT(op_array->try_catch_array != NULL); @@ -567,6 +605,10 @@ static void zend_persist_op_array_ex(zend_op_array *op_array, zend_persistent_sc } } + if (op_array->attributes) { + op_array->attributes = zend_persist_attributes(op_array->attributes); + } + if (op_array->try_catch_array) { op_array->try_catch_array = zend_shared_memdup_put_free(op_array->try_catch_array, sizeof(zend_try_catch_element) * op_array->last_try_catch); } @@ -708,6 +750,9 @@ static void zend_persist_property_info(zval *zv) prop->doc_comment = NULL; } } + if (prop->attributes) { + prop->attributes = zend_persist_attributes(prop->attributes); + } zend_persist_type(&prop->type); } @@ -747,6 +792,9 @@ static void zend_persist_class_constant(zval *zv) c->doc_comment = NULL; } } + if (c->attributes) { + c->attributes = zend_persist_attributes(c->attributes); + } } static void zend_persist_class_entry(zval *zv) @@ -833,6 +881,9 @@ static void zend_persist_class_entry(zval *zv) ce->info.user.doc_comment = NULL; } } + if (ce->attributes) { + ce->attributes = zend_persist_attributes(ce->attributes); + } zend_hash_persist(&ce->properties_info); ZEND_HASH_FOREACH_BUCKET(&ce->properties_info, p) { ZEND_ASSERT(p->key != NULL); diff --git a/ext/opcache/zend_persist_calc.c b/ext/opcache/zend_persist_calc.c index 8a1e99c65df27..a7b347ee0ee2e 100644 --- a/ext/opcache/zend_persist_calc.c +++ b/ext/opcache/zend_persist_calc.c @@ -25,6 +25,7 @@ #include "zend_extensions.h" #include "zend_shared_alloc.h" #include "zend_operators.h" +#include "zend_attributes.h" #define ADD_DUP_SIZE(m,s) ZCG(current_persistent_script)->size += zend_shared_memdup_size((void*)m, s) #define ADD_SIZE(m) ZCG(current_persistent_script)->size += ZEND_ALIGNED_SIZE(m) @@ -148,6 +149,28 @@ static void zend_persist_zval_calc(zval *z) } } +static void zend_persist_attributes_calc(HashTable *attributes) +{ + if (!zend_shared_alloc_get_xlat_entry(attributes)) { + zend_attribute *attr; + uint32_t i; + + zend_shared_alloc_register_xlat_entry(attributes, attributes); + ADD_SIZE(sizeof(HashTable)); + zend_hash_persist_calc(attributes); + + ZEND_HASH_FOREACH_PTR(attributes, attr) { + ADD_SIZE(ZEND_ATTRIBUTE_SIZE(attr->argc)); + ADD_INTERNED_STRING(attr->name); + ADD_INTERNED_STRING(attr->lcname); + + for (i = 0; i < attr->argc; i++) { + zend_persist_zval_calc(&attr->argv[i]); + } + } ZEND_HASH_FOREACH_END(); + } +} + static void zend_persist_type_calc(zend_type *type) { if (ZEND_TYPE_HAS_LIST(*type)) { @@ -249,6 +272,10 @@ static void zend_persist_op_array_calc_ex(zend_op_array *op_array) ADD_STRING(op_array->doc_comment); } + if (op_array->attributes) { + zend_persist_attributes_calc(op_array->attributes); + } + if (op_array->try_catch_array) { ADD_SIZE(sizeof(zend_try_catch_element) * op_array->last_try_catch); } @@ -325,6 +352,9 @@ static void zend_persist_property_info_calc(zval *zv) if (ZCG(accel_directives).save_comments && prop->doc_comment) { ADD_STRING(prop->doc_comment); } + if (prop->attributes) { + zend_persist_attributes_calc(prop->attributes); + } } } @@ -339,6 +369,9 @@ static void zend_persist_class_constant_calc(zval *zv) if (ZCG(accel_directives).save_comments && c->doc_comment) { ADD_STRING(c->doc_comment); } + if (c->attributes) { + zend_persist_attributes_calc(c->attributes); + } } } @@ -424,6 +457,9 @@ static void zend_persist_class_entry_calc(zval *zv) if (ZCG(accel_directives).save_comments && ce->info.user.doc_comment) { ADD_STRING(ce->info.user.doc_comment); } + if (ce->attributes) { + zend_persist_attributes_calc(ce->attributes); + } zend_hash_persist_calc(&ce->properties_info); ZEND_HASH_FOREACH_BUCKET(&ce->properties_info, p) { diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index 9aac065e38bea..72312181c22d6 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -31,6 +31,8 @@ #include "zend.h" #include "zend_API.h" +#include "zend_ast.h" +#include "zend_attributes.h" #include "zend_exceptions.h" #include "zend_operators.h" #include "zend_constants.h" @@ -84,6 +86,7 @@ PHPAPI zend_class_entry *reflection_class_constant_ptr; PHPAPI zend_class_entry *reflection_extension_ptr; PHPAPI zend_class_entry *reflection_zend_extension_ptr; PHPAPI zend_class_entry *reflection_reference_ptr; +PHPAPI zend_class_entry *reflection_attribute_ptr; /* Exception throwing macro */ #define _DO_THROW(msg) \ @@ -109,6 +112,8 @@ PHPAPI zend_class_entry *reflection_reference_ptr; #define REGISTER_REFLECTION_CLASS_CONST_LONG(class_name, const_name, value) \ zend_declare_class_constant_long(reflection_ ## class_name ## _ptr, const_name, sizeof(const_name)-1, (zend_long)value); +#define REFLECTION_ATTRIBUTE_IS_INSTANCEOF (1 << 1) + /* {{{ Object structure */ /* Struct for properties */ @@ -132,6 +137,12 @@ typedef struct _type_reference { zend_bool legacy_behavior; } type_reference; +/* Struct for attributes */ +typedef struct _attribute_reference { + zend_attribute *data; + zend_class_entry *scope; +} attribute_reference; + typedef enum { REF_TYPE_OTHER, /* Must be 0 */ REF_TYPE_FUNCTION, @@ -139,7 +150,8 @@ typedef enum { REF_TYPE_PARAMETER, REF_TYPE_TYPE, REF_TYPE_PROPERTY, - REF_TYPE_CLASS_CONSTANT + REF_TYPE_CLASS_CONSTANT, + REF_TYPE_ATTRIBUTE } reflection_type_t; /* Struct for reflection objects */ @@ -238,6 +250,9 @@ static void reflection_free_objects_storage(zend_object *object) /* {{{ */ zend_string_release_ex(prop_reference->unmangled_name, 0); efree(intern->ptr); break; + case REF_TYPE_ATTRIBUTE: + efree(intern->ptr); + break; case REF_TYPE_GENERATOR: case REF_TYPE_CLASS_CONSTANT: case REF_TYPE_OTHER: @@ -1058,6 +1073,121 @@ static void _extension_string(smart_str *str, zend_module_entry *module, char *i } /* }}} */ +/* {{{ reflection_attribute_factory */ +static void reflection_attribute_factory(zval *object, zend_attribute *data, zend_class_entry *scope) +{ + reflection_object *intern; + attribute_reference *reference; + + reflection_instantiate(reflection_attribute_ptr, object); + intern = Z_REFLECTION_P(object); + reference = (attribute_reference*) emalloc(sizeof(attribute_reference)); + reference->data = data; + reference->scope = scope; + intern->ptr = reference; + intern->ref_type = REF_TYPE_ATTRIBUTE; +} +/* }}} */ + +static int read_attributes(zval *ret, HashTable *attributes, zend_class_entry *scope, + uint32_t offset, zend_string *name, zend_class_entry *base) /* {{{ */ +{ + ZEND_ASSERT(attributes != NULL); + + zend_attribute *attr; + zval tmp; + + if (name) { + // Name based filtering using lowercased key. + zend_string *filter = zend_string_tolower(name); + + ZEND_HASH_FOREACH_PTR(attributes, attr) { + if (attr->offset == offset && zend_string_equals(attr->lcname, filter)) { + reflection_attribute_factory(&tmp, attr, scope); + add_next_index_zval(ret, &tmp); + } + } ZEND_HASH_FOREACH_END(); + + zend_string_release(filter); + return SUCCESS; + } + + ZEND_HASH_FOREACH_PTR(attributes, attr) { + if (attr->offset != offset) { + continue; + } + + if (base) { + // Base type filtering. + zend_class_entry *ce = zend_lookup_class_ex(attr->name, attr->lcname, 0); + + if (ce == NULL) { + // Bailout on error, otherwise ignore unavailable class. + if (EG(exception)) { + return FAILURE; + } + + continue; + } + + if (!instanceof_function(ce, base)) { + continue; + } + } + + reflection_attribute_factory(&tmp, attr, scope); + add_next_index_zval(ret, &tmp); + } ZEND_HASH_FOREACH_END(); + + return SUCCESS; +} +/* }}} */ + +static void reflect_attributes(INTERNAL_FUNCTION_PARAMETERS, HashTable *attributes, + uint32_t offset, zend_class_entry *scope) /* {{{ */ +{ + zval ret; + + zend_string *name = NULL; + zend_long flags = 0; + zend_class_entry *base = NULL; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "|Sl", &name, &flags) == FAILURE) { + RETURN_THROWS(); + } + + if (flags & ~REFLECTION_ATTRIBUTE_IS_INSTANCEOF) { + zend_throw_error(NULL, "Invalid attribute filter flag specified"); + RETURN_THROWS(); + } + + if (name && (flags & REFLECTION_ATTRIBUTE_IS_INSTANCEOF)) { + if (NULL == (base = zend_lookup_class(name))) { + if (!EG(exception)) { + zend_throw_error(NULL, "Class '%s' not found", ZSTR_VAL(name)); + } + + RETURN_THROWS(); + } + + name = NULL; + } + + if (!attributes) { + RETURN_EMPTY_ARRAY(); + } + + array_init(&ret); + + if (FAILURE == read_attributes(&ret, attributes, scope, offset, name, base)) { + zval_ptr_dtor(&ret); + RETURN_THROWS(); + } + + RETURN_ZVAL(&ret, 1, 1); +} +/* }}} */ + static void _zend_extension_string(smart_str *str, zend_extension *extension, char *indent) /* {{{ */ { smart_str_append_printf(str, "%sZend Extension [ %s ", indent, extension->name); @@ -1630,6 +1760,19 @@ ZEND_METHOD(ReflectionFunctionAbstract, getDocComment) } /* }}} */ +/* {{{ proto public array ReflectionFunction::getAttributes([ string name, int flags ]) + Returns the attributes of this function */ +ZEND_METHOD(ReflectionFunctionAbstract, getAttributes) +{ + reflection_object *intern; + zend_function *fptr; + + GET_REFLECTION_OBJECT_PTR(fptr); + + reflect_attributes(INTERNAL_FUNCTION_PARAM_PASSTHRU, fptr->common.attributes, 0, fptr->common.scope); +} +/* }}} */ + /* {{{ proto public array ReflectionFunction::getStaticVariables() Returns an associative array containing this function's static variables and their values */ ZEND_METHOD(ReflectionFunctionAbstract, getStaticVariables) @@ -2556,7 +2699,22 @@ ZEND_METHOD(ReflectionParameter, canBePassedByValue) } /* }}} */ -/* {{{ proto public bool ReflectionParameter::getPosition() +/* {{{ proto public array ReflectionParameter::getAttributes([ string name, int flags ]) + Get parameter attributes. */ +ZEND_METHOD(ReflectionParameter, getAttributes) +{ + reflection_object *intern; + parameter_reference *param; + + GET_REFLECTION_OBJECT_PTR(param); + + HashTable *attributes = param->fptr->common.attributes; + zend_class_entry *scope = param->fptr->common.scope; + + reflect_attributes(INTERNAL_FUNCTION_PARAM_PASSTHRU, attributes, param->offset + 1, scope); +} + +/* {{{ proto public int ReflectionParameter::getPosition() Returns whether this parameter is an optional parameter */ ZEND_METHOD(ReflectionParameter, getPosition) { @@ -3612,6 +3770,19 @@ ZEND_METHOD(ReflectionClassConstant, getDocComment) } /* }}} */ +/* {{{ proto public array ReflectionClassConstant::getAttributes([ string name, int flags ]) + Returns the attributes of this constant */ +ZEND_METHOD(ReflectionClassConstant, getAttributes) +{ + reflection_object *intern; + zend_class_constant *ref; + + GET_REFLECTION_OBJECT_PTR(ref); + + reflect_attributes(INTERNAL_FUNCTION_PARAM_PASSTHRU, ref->attributes, 0, ref->ce); +} +/* }}} */ + /* {{{ reflection_class_object_ctor */ static void reflection_class_object_ctor(INTERNAL_FUNCTION_PARAMETERS, int is_object) { @@ -3979,6 +4150,19 @@ ZEND_METHOD(ReflectionClass, getDocComment) } /* }}} */ +/* {{{ proto public array ReflectionClass::getAttributes([ string name, int flags ]) + Returns the attributes for this class */ +ZEND_METHOD(ReflectionClass, getAttributes) +{ + reflection_object *intern; + zend_class_entry *ce; + + GET_REFLECTION_OBJECT_PTR(ce); + + reflect_attributes(INTERNAL_FUNCTION_PARAM_PASSTHRU, ce->attributes, 0, ce); +} +/* }}} */ + /* {{{ proto public ReflectionMethod ReflectionClass::getConstructor() Returns the class' constructor if there is one, NULL otherwise */ ZEND_METHOD(ReflectionClass, getConstructor) @@ -5479,6 +5663,19 @@ ZEND_METHOD(ReflectionProperty, getDocComment) } /* }}} */ +/* {{{ proto public array ReflectionProperty::getAttributes([ string name, int flags ]) + Returns the attributes of this property */ +ZEND_METHOD(ReflectionProperty, getAttributes) +{ + reflection_object *intern; + property_reference *ref; + + GET_REFLECTION_OBJECT_PTR(ref); + + reflect_attributes(INTERNAL_FUNCTION_PARAM_PASSTHRU, ref->prop->attributes, 0, ref->prop->ce); +} +/* }}} */ + /* {{{ proto public int ReflectionProperty::setAccessible(bool visible) Sets whether non-public properties can be requested */ ZEND_METHOD(ReflectionProperty, setAccessible) @@ -6190,6 +6387,204 @@ ZEND_METHOD(ReflectionReference, getId) } /* }}} */ +ZEND_METHOD(ReflectionAttribute, __construct) +{ +} + +ZEND_METHOD(ReflectionAttribute, __clone) +{ +} + +/* {{{ proto public string ReflectionAttribute::getName() + * Returns the name of the attribute */ +ZEND_METHOD(ReflectionAttribute, getName) +{ + reflection_object *intern; + attribute_reference *attr; + + if (zend_parse_parameters_none() == FAILURE) { + RETURN_THROWS(); + } + GET_REFLECTION_OBJECT_PTR(attr); + + RETURN_STR_COPY(attr->data->name); +} +/* }}} */ + +static zend_always_inline int import_attribute_value(zval *ret, zval *val, zend_class_entry *scope) /* {{{ */ +{ + if (Z_TYPE_P(val) == IS_CONSTANT_AST) { + *ret = *val; + + if (FAILURE == zval_update_constant_ex(ret, scope)) { + return FAILURE; + } + } else { + ZVAL_COPY_OR_DUP(ret, val); + } + + return SUCCESS; +} +/* }}} */ + +/* {{{ proto public array ReflectionAttribute::getArguments() + * Returns the arguments passed to the attribute */ +ZEND_METHOD(ReflectionAttribute, getArguments) +{ + reflection_object *intern; + attribute_reference *attr; + + zval ret; + zval tmp; + uint32_t i; + + if (zend_parse_parameters_none() == FAILURE) { + RETURN_THROWS(); + } + GET_REFLECTION_OBJECT_PTR(attr); + + array_init(&ret); + + for (i = 0; i < attr->data->argc; i++) { + if (FAILURE == import_attribute_value(&tmp, &attr->data->argv[i], attr->scope)) { + zval_ptr_dtor(&ret); + RETURN_THROWS(); + } + + add_next_index_zval(&ret, &tmp); + } + + RETURN_ZVAL(&ret, 1, 1); +} +/* }}} */ + +static int call_attribute_constructor(zend_class_entry *ce, zend_object *obj, zval *args, uint32_t argc) /* {{{ */ +{ + zend_fcall_info fci; + zend_fcall_info_cache fcc; + + zend_function *ctor; + zval retval; + int ret; + + ctor = ce->constructor; + + ZEND_ASSERT(ctor != NULL); + + if (!(ctor->common.fn_flags & ZEND_ACC_PUBLIC)) { + zend_throw_error(NULL, "Attribute constructor of class '%s' must be public", ZSTR_VAL(ce->name)); + return FAILURE; + } + + fci.size = sizeof(fci); + ZVAL_UNDEF(&fci.function_name); + fci.object = obj; + fci.retval = &retval; + fci.params = args; + fci.param_count = argc; + fci.no_separation = 1; + + fcc.function_handler = ctor; + fcc.called_scope = ce; + fcc.object = obj; + + ret = zend_call_function(&fci, &fcc); + + if (EG(exception)) { + zend_object_store_ctor_failed(obj); + } + + zval_ptr_dtor(&retval); + + if (ret != SUCCESS) { + zend_throw_error(NULL, "Failed to invoke constructor of attribute class '%s'", ZSTR_VAL(ce->name)); + } + + return EG(exception) ? FAILURE : SUCCESS; +} +/* }}} */ + +static void attribute_ctor_cleanup(zval *obj, zval *args, uint32_t argc) /* {{{ */ +{ + if (obj) { + zval_ptr_dtor(obj); + } + + if (args) { + uint32_t i; + + for (i = 0; i < argc; i++) { + zval_ptr_dtor(&args[i]); + } + + efree(args); + } +} +/* }}} */ + +/* {{{ proto public object ReflectionAttribute::newInstance() + * Returns the attribute as an object */ +ZEND_METHOD(ReflectionAttribute, newInstance) +{ + reflection_object *intern; + attribute_reference *attr; + + zend_class_entry *ce; + zval obj; + + zval *args = NULL; + uint32_t count; + uint32_t argc = 0; + + if (zend_parse_parameters_none() == FAILURE) { + RETURN_THROWS(); + } + + GET_REFLECTION_OBJECT_PTR(attr); + + if (NULL == (ce = zend_lookup_class(attr->data->name))) { + zend_throw_error(NULL, "Attribute class '%s' not found", ZSTR_VAL(attr->data->name)); + RETURN_THROWS(); + } + + if (!zend_get_attribute_str(ce->attributes, ZEND_STRL("phpattribute"))) { + zend_throw_error(NULL, "Attempting to use class '%s' as attribute that does not have <>.", ZSTR_VAL(attr->data->name)); + RETURN_THROWS(); + } + + if (SUCCESS != object_init_ex(&obj, ce)) { + RETURN_THROWS(); + } + + count = attr->data->argc; + + if (count) { + args = emalloc(count * sizeof(zval)); + + for (argc = 0; argc < attr->data->argc; argc++) { + if (FAILURE == import_attribute_value(&args[argc], &attr->data->argv[argc], attr->scope)) { + attribute_ctor_cleanup(&obj, args, argc); + RETURN_THROWS(); + } + } + } + + if (ce->constructor) { + if (FAILURE == call_attribute_constructor(ce, Z_OBJ(obj), args, argc)) { + attribute_ctor_cleanup(&obj, args, argc); + RETURN_THROWS(); + } + } else if (argc) { + attribute_ctor_cleanup(&obj, args, argc); + zend_throw_error(NULL, "Attribute class '%s' does not have a constructor, cannot pass arguments", ZSTR_VAL(ce->name)); + RETURN_THROWS(); + } + + attribute_ctor_cleanup(NULL, args, argc); + + RETURN_ZVAL(&obj, 1, 1); +} +/* }}} */ static const zend_function_entry reflection_ext_functions[] = { /* {{{ */ PHP_FE_END }; /* }}} */ @@ -6340,6 +6735,13 @@ PHP_MINIT_FUNCTION(reflection) /* {{{ */ _reflection_entry.ce_flags |= ZEND_ACC_FINAL; reflection_reference_ptr = zend_register_internal_class(&_reflection_entry); + INIT_CLASS_ENTRY(_reflection_entry, "ReflectionAttribute", class_ReflectionAttribute_methods); + reflection_init_class_handlers(&_reflection_entry); + _reflection_entry.ce_flags |= ZEND_ACC_FINAL; + reflection_attribute_ptr = zend_register_internal_class(&_reflection_entry); + + REGISTER_REFLECTION_CLASS_CONST_LONG(attribute, "IS_INSTANCEOF", REFLECTION_ATTRIBUTE_IS_INSTANCEOF); + REFLECTION_G(key_initialized) = 0; return SUCCESS; diff --git a/ext/reflection/php_reflection.h b/ext/reflection/php_reflection.h index e4e08c16e60bc..654ba55256c8f 100644 --- a/ext/reflection/php_reflection.h +++ b/ext/reflection/php_reflection.h @@ -42,6 +42,7 @@ extern PHPAPI zend_class_entry *reflection_property_ptr; extern PHPAPI zend_class_entry *reflection_extension_ptr; extern PHPAPI zend_class_entry *reflection_zend_extension_ptr; extern PHPAPI zend_class_entry *reflection_reference_ptr; +extern PHPAPI zend_class_entry *reflection_attribute_ptr; PHPAPI void zend_reflection_class_factory(zend_class_entry *ce, zval *object); diff --git a/ext/reflection/php_reflection.stub.php b/ext/reflection/php_reflection.stub.php index 30627475073b4..f5b3adc8db0a0 100644 --- a/ext/reflection/php_reflection.stub.php +++ b/ext/reflection/php_reflection.stub.php @@ -95,6 +95,9 @@ public function hasReturnType() {} /** @return ReflectionType|null */ public function getReturnType() {} + + /** @return ReflectionAttribute[] */ + public function getAttributes(?string $name = null, int $flags = 0) {} } class ReflectionFunction extends ReflectionFunctionAbstract @@ -363,6 +366,9 @@ public function getNamespaceName() {} /** @return string */ public function getShortName() {} + + /** @return ReflectionAttribute[] */ + public function getAttributes(?string $name = null, int $flags = 0) {} } class ReflectionObject extends ReflectionClass @@ -429,6 +435,9 @@ public function hasDefaultValue(): bool {} /** @return mixed */ public function getDefaultValue() {} + + /** @return ReflectionAttribute[] */ + public function getAttributes(?string $name = null, int $flags = 0) {} } class ReflectionClassConstant implements Reflector @@ -464,6 +473,9 @@ public function getDeclaringClass() {} /** @return string|false */ public function getDocComment() {} + + /** @return ReflectionAttribute[] */ + public function getAttributes(?string $name = null, int $flags = 0) {} } class ReflectionParameter implements Reflector @@ -540,6 +552,9 @@ public function getDefaultValueConstantName() {} /** @return bool */ public function isVariadic() {} + + /** @return ReflectionAttribute[] */ + public function getAttributes(?string $name = null, int $flags = 0) {} } abstract class ReflectionType implements Stringable @@ -647,3 +662,14 @@ private function __clone() {} private function __construct() {} } + +final class ReflectionAttribute +{ + public function getName(): string {} + public function getArguments(): array {} + public function newInstance(): object {} + + private function __clone() {} + + private function __construct() {} +} diff --git a/ext/reflection/php_reflection_arginfo.h b/ext/reflection/php_reflection_arginfo.h index 1e3e9988deee0..0efaff41bfd7a 100644 --- a/ext/reflection/php_reflection_arginfo.h +++ b/ext/reflection/php_reflection_arginfo.h @@ -57,6 +57,11 @@ ZEND_END_ARG_INFO() #define arginfo_class_ReflectionFunctionAbstract_getReturnType arginfo_class_ReflectionFunctionAbstract___clone +ZEND_BEGIN_ARG_INFO_EX(arginfo_class_ReflectionFunctionAbstract_getAttributes, 0, 0, 0) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, name, IS_STRING, 1, "null") + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, flags, IS_LONG, 0, "0") +ZEND_END_ARG_INFO() + ZEND_BEGIN_ARG_INFO_EX(arginfo_class_ReflectionFunction___construct, 0, 0, 1) ZEND_ARG_INFO(0, name) ZEND_END_ARG_INFO() @@ -269,6 +274,8 @@ ZEND_END_ARG_INFO() #define arginfo_class_ReflectionClass_getShortName arginfo_class_ReflectionFunctionAbstract___clone +#define arginfo_class_ReflectionClass_getAttributes arginfo_class_ReflectionFunctionAbstract_getAttributes + ZEND_BEGIN_ARG_INFO_EX(arginfo_class_ReflectionObject___construct, 0, 0, 1) ZEND_ARG_TYPE_INFO(0, argument, IS_OBJECT, 0) ZEND_END_ARG_INFO() @@ -322,6 +329,8 @@ ZEND_END_ARG_INFO() #define arginfo_class_ReflectionProperty_getDefaultValue arginfo_class_ReflectionFunctionAbstract___clone +#define arginfo_class_ReflectionProperty_getAttributes arginfo_class_ReflectionFunctionAbstract_getAttributes + #define arginfo_class_ReflectionClassConstant___clone arginfo_class_ReflectionFunctionAbstract___clone #define arginfo_class_ReflectionClassConstant___construct arginfo_class_ReflectionProperty___construct @@ -344,6 +353,8 @@ ZEND_END_ARG_INFO() #define arginfo_class_ReflectionClassConstant_getDocComment arginfo_class_ReflectionFunctionAbstract___clone +#define arginfo_class_ReflectionClassConstant_getAttributes arginfo_class_ReflectionFunctionAbstract_getAttributes + #define arginfo_class_ReflectionParameter___clone arginfo_class_ReflectionFunctionAbstract___clone ZEND_BEGIN_ARG_INFO_EX(arginfo_class_ReflectionParameter___construct, 0, 0, 2) @@ -389,6 +400,8 @@ ZEND_END_ARG_INFO() #define arginfo_class_ReflectionParameter_isVariadic arginfo_class_ReflectionFunctionAbstract___clone +#define arginfo_class_ReflectionParameter_getAttributes arginfo_class_ReflectionFunctionAbstract_getAttributes + #define arginfo_class_ReflectionType___clone arginfo_class_ReflectionFunctionAbstract___clone #define arginfo_class_ReflectionType_allowsNull arginfo_class_ReflectionFunctionAbstract___clone @@ -457,6 +470,17 @@ ZEND_END_ARG_INFO() #define arginfo_class_ReflectionReference___construct arginfo_class_ReflectionFunctionAbstract___clone +#define arginfo_class_ReflectionAttribute_getName arginfo_class_ReflectionFunction___toString + +#define arginfo_class_ReflectionAttribute_getArguments arginfo_class_ReflectionUnionType_getTypes + +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_ReflectionAttribute_newInstance, 0, 0, IS_OBJECT, 0) +ZEND_END_ARG_INFO() + +#define arginfo_class_ReflectionAttribute___clone arginfo_class_ReflectionFunctionAbstract___clone + +#define arginfo_class_ReflectionAttribute___construct arginfo_class_ReflectionFunctionAbstract___clone + ZEND_METHOD(Reflection, getModifierNames); ZEND_METHOD(ReflectionClass, __clone); @@ -485,6 +509,7 @@ ZEND_METHOD(ReflectionFunctionAbstract, getStaticVariables); ZEND_METHOD(ReflectionFunctionAbstract, returnsReference); ZEND_METHOD(ReflectionFunctionAbstract, hasReturnType); ZEND_METHOD(ReflectionFunctionAbstract, getReturnType); +ZEND_METHOD(ReflectionFunctionAbstract, getAttributes); ZEND_METHOD(ReflectionFunction, __construct); ZEND_METHOD(ReflectionFunction, __toString); ZEND_METHOD(ReflectionFunction, isDisabled); @@ -566,6 +591,7 @@ ZEND_METHOD(ReflectionClass, getExtensionName); ZEND_METHOD(ReflectionClass, inNamespace); ZEND_METHOD(ReflectionClass, getNamespaceName); ZEND_METHOD(ReflectionClass, getShortName); +ZEND_METHOD(ReflectionClass, getAttributes); ZEND_METHOD(ReflectionObject, __construct); ZEND_METHOD(ReflectionProperty, __construct); ZEND_METHOD(ReflectionProperty, __toString); @@ -586,6 +612,7 @@ ZEND_METHOD(ReflectionProperty, getType); ZEND_METHOD(ReflectionProperty, hasType); ZEND_METHOD(ReflectionProperty, hasDefaultValue); ZEND_METHOD(ReflectionProperty, getDefaultValue); +ZEND_METHOD(ReflectionProperty, getAttributes); ZEND_METHOD(ReflectionClassConstant, __construct); ZEND_METHOD(ReflectionClassConstant, __toString); ZEND_METHOD(ReflectionClassConstant, getName); @@ -596,6 +623,7 @@ ZEND_METHOD(ReflectionClassConstant, isProtected); ZEND_METHOD(ReflectionClassConstant, getModifiers); ZEND_METHOD(ReflectionClassConstant, getDeclaringClass); ZEND_METHOD(ReflectionClassConstant, getDocComment); +ZEND_METHOD(ReflectionClassConstant, getAttributes); ZEND_METHOD(ReflectionParameter, __construct); ZEND_METHOD(ReflectionParameter, __toString); ZEND_METHOD(ReflectionParameter, getName); @@ -616,6 +644,7 @@ ZEND_METHOD(ReflectionParameter, getDefaultValue); ZEND_METHOD(ReflectionParameter, isDefaultValueConstant); ZEND_METHOD(ReflectionParameter, getDefaultValueConstantName); ZEND_METHOD(ReflectionParameter, isVariadic); +ZEND_METHOD(ReflectionParameter, getAttributes); ZEND_METHOD(ReflectionType, allowsNull); ZEND_METHOD(ReflectionType, __toString); ZEND_METHOD(ReflectionNamedType, getName); @@ -644,6 +673,11 @@ ZEND_METHOD(ReflectionZendExtension, getCopyright); ZEND_METHOD(ReflectionReference, fromArrayElement); ZEND_METHOD(ReflectionReference, getId); ZEND_METHOD(ReflectionReference, __construct); +ZEND_METHOD(ReflectionAttribute, getName); +ZEND_METHOD(ReflectionAttribute, getArguments); +ZEND_METHOD(ReflectionAttribute, newInstance); +ZEND_METHOD(ReflectionAttribute, __clone); +ZEND_METHOD(ReflectionAttribute, __construct); static const zend_function_entry class_ReflectionException_methods[] = { @@ -689,6 +723,7 @@ static const zend_function_entry class_ReflectionFunctionAbstract_methods[] = { ZEND_ME(ReflectionFunctionAbstract, returnsReference, arginfo_class_ReflectionFunctionAbstract_returnsReference, ZEND_ACC_PUBLIC) ZEND_ME(ReflectionFunctionAbstract, hasReturnType, arginfo_class_ReflectionFunctionAbstract_hasReturnType, ZEND_ACC_PUBLIC) ZEND_ME(ReflectionFunctionAbstract, getReturnType, arginfo_class_ReflectionFunctionAbstract_getReturnType, ZEND_ACC_PUBLIC) + ZEND_ME(ReflectionFunctionAbstract, getAttributes, arginfo_class_ReflectionFunctionAbstract_getAttributes, ZEND_ACC_PUBLIC) ZEND_FE_END }; @@ -792,6 +827,7 @@ static const zend_function_entry class_ReflectionClass_methods[] = { ZEND_ME(ReflectionClass, inNamespace, arginfo_class_ReflectionClass_inNamespace, ZEND_ACC_PUBLIC) ZEND_ME(ReflectionClass, getNamespaceName, arginfo_class_ReflectionClass_getNamespaceName, ZEND_ACC_PUBLIC) ZEND_ME(ReflectionClass, getShortName, arginfo_class_ReflectionClass_getShortName, ZEND_ACC_PUBLIC) + ZEND_ME(ReflectionClass, getAttributes, arginfo_class_ReflectionClass_getAttributes, ZEND_ACC_PUBLIC) ZEND_FE_END }; @@ -823,6 +859,7 @@ static const zend_function_entry class_ReflectionProperty_methods[] = { ZEND_ME(ReflectionProperty, hasType, arginfo_class_ReflectionProperty_hasType, ZEND_ACC_PUBLIC) ZEND_ME(ReflectionProperty, hasDefaultValue, arginfo_class_ReflectionProperty_hasDefaultValue, ZEND_ACC_PUBLIC) ZEND_ME(ReflectionProperty, getDefaultValue, arginfo_class_ReflectionProperty_getDefaultValue, ZEND_ACC_PUBLIC) + ZEND_ME(ReflectionProperty, getAttributes, arginfo_class_ReflectionProperty_getAttributes, ZEND_ACC_PUBLIC) ZEND_FE_END }; @@ -839,6 +876,7 @@ static const zend_function_entry class_ReflectionClassConstant_methods[] = { ZEND_ME(ReflectionClassConstant, getModifiers, arginfo_class_ReflectionClassConstant_getModifiers, ZEND_ACC_PUBLIC) ZEND_ME(ReflectionClassConstant, getDeclaringClass, arginfo_class_ReflectionClassConstant_getDeclaringClass, ZEND_ACC_PUBLIC) ZEND_ME(ReflectionClassConstant, getDocComment, arginfo_class_ReflectionClassConstant_getDocComment, ZEND_ACC_PUBLIC) + ZEND_ME(ReflectionClassConstant, getAttributes, arginfo_class_ReflectionClassConstant_getAttributes, ZEND_ACC_PUBLIC) ZEND_FE_END }; @@ -865,6 +903,7 @@ static const zend_function_entry class_ReflectionParameter_methods[] = { ZEND_ME(ReflectionParameter, isDefaultValueConstant, arginfo_class_ReflectionParameter_isDefaultValueConstant, ZEND_ACC_PUBLIC) ZEND_ME(ReflectionParameter, getDefaultValueConstantName, arginfo_class_ReflectionParameter_getDefaultValueConstantName, ZEND_ACC_PUBLIC) ZEND_ME(ReflectionParameter, isVariadic, arginfo_class_ReflectionParameter_isVariadic, ZEND_ACC_PUBLIC) + ZEND_ME(ReflectionParameter, getAttributes, arginfo_class_ReflectionParameter_getAttributes, ZEND_ACC_PUBLIC) ZEND_FE_END }; @@ -929,3 +968,13 @@ static const zend_function_entry class_ReflectionReference_methods[] = { ZEND_ME(ReflectionReference, __construct, arginfo_class_ReflectionReference___construct, ZEND_ACC_PRIVATE) ZEND_FE_END }; + + +static const zend_function_entry class_ReflectionAttribute_methods[] = { + ZEND_ME(ReflectionAttribute, getName, arginfo_class_ReflectionAttribute_getName, ZEND_ACC_PUBLIC) + ZEND_ME(ReflectionAttribute, getArguments, arginfo_class_ReflectionAttribute_getArguments, ZEND_ACC_PUBLIC) + ZEND_ME(ReflectionAttribute, newInstance, arginfo_class_ReflectionAttribute_newInstance, ZEND_ACC_PUBLIC) + ZEND_ME(ReflectionAttribute, __clone, arginfo_class_ReflectionAttribute___clone, ZEND_ACC_PRIVATE) + ZEND_ME(ReflectionAttribute, __construct, arginfo_class_ReflectionAttribute___construct, ZEND_ACC_PRIVATE) + ZEND_FE_END +}; diff --git a/ext/reflection/tests/ReflectionClass_toString_001.phpt b/ext/reflection/tests/ReflectionClass_toString_001.phpt index 367645d019d5d..3dff0725167c9 100644 --- a/ext/reflection/tests/ReflectionClass_toString_001.phpt +++ b/ext/reflection/tests/ReflectionClass_toString_001.phpt @@ -27,7 +27,7 @@ Class [ class ReflectionClass implements Reflector, String Property [ public $name = '' ] } - - Methods [53] { + - Methods [54] { Method [ final private method __clone ] { - Parameters [0] { @@ -365,5 +365,13 @@ Class [ class ReflectionClass implements Reflector, String - Parameters [0] { } } + + Method [ public method getAttributes ] { + + - Parameters [2] { + Parameter #0 [ ?string $name = null ] + Parameter #1 [ int $flags = 0 ] + } + } } } diff --git a/ext/reflection/tests/ReflectionExtension_getClasses_basic.phpt b/ext/reflection/tests/ReflectionExtension_getClasses_basic.phpt index 61a0d24b0887a..81d3c8d55b146 100644 --- a/ext/reflection/tests/ReflectionExtension_getClasses_basic.phpt +++ b/ext/reflection/tests/ReflectionExtension_getClasses_basic.phpt @@ -8,7 +8,7 @@ $ext = new ReflectionExtension('reflection'); var_dump($ext->getClasses()); ?> --EXPECT-- -array(18) { +array(19) { ["ReflectionException"]=> object(ReflectionClass)#2 (1) { ["name"]=> @@ -99,4 +99,9 @@ array(18) { ["name"]=> string(19) "ReflectionReference" } + ["ReflectionAttribute"]=> + object(ReflectionClass)#20 (1) { + ["name"]=> + string(19) "ReflectionAttribute" + } } diff --git a/ext/tokenizer/tokenizer.c b/ext/tokenizer/tokenizer.c index db573232233a9..012a39f2d9c91 100644 --- a/ext/tokenizer/tokenizer.c +++ b/ext/tokenizer/tokenizer.c @@ -266,22 +266,22 @@ PHP_MINIT_FUNCTION(tokenizer) zend_class_implements(php_token_ce, 1, zend_ce_stringable); name = zend_string_init("id", sizeof("id") - 1, 1); - zend_declare_typed_property(php_token_ce, name, &default_val, ZEND_ACC_PUBLIC, NULL, + zend_declare_typed_property(php_token_ce, name, &default_val, ZEND_ACC_PUBLIC, NULL, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); zend_string_release(name); name = zend_string_init("text", sizeof("text") - 1, 1); - zend_declare_typed_property(php_token_ce, name, &default_val, ZEND_ACC_PUBLIC, NULL, + zend_declare_typed_property(php_token_ce, name, &default_val, ZEND_ACC_PUBLIC, NULL, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING)); zend_string_release(name); name = zend_string_init("line", sizeof("line") - 1, 1); - zend_declare_typed_property(php_token_ce, name, &default_val, ZEND_ACC_PUBLIC, NULL, + zend_declare_typed_property(php_token_ce, name, &default_val, ZEND_ACC_PUBLIC, NULL, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); zend_string_release(name); name = zend_string_init("pos", sizeof("pos") - 1, 1); - zend_declare_typed_property(php_token_ce, name, &default_val, ZEND_ACC_PUBLIC, NULL, + zend_declare_typed_property(php_token_ce, name, &default_val, ZEND_ACC_PUBLIC, NULL, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); zend_string_release(name); diff --git a/ext/zend_test/test.c b/ext/zend_test/test.c index 1625156bdb2e0..726cc14c041a3 100644 --- a/ext/zend_test/test.c +++ b/ext/zend_test/test.c @@ -23,11 +23,13 @@ #include "ext/standard/info.h" #include "php_test.h" #include "test_arginfo.h" +#include "zend_attributes.h" static zend_class_entry *zend_test_interface; static zend_class_entry *zend_test_class; static zend_class_entry *zend_test_child_class; static zend_class_entry *zend_test_trait; +static zend_class_entry *zend_test_attribute; static zend_object_handlers zend_test_class_handlers; ZEND_FUNCTION(zend_test_func) @@ -181,6 +183,13 @@ static zend_function *zend_test_class_static_method_get(zend_class_entry *ce, ze } /* }}} */ +void zend_attribute_validate_zendtestattribute(zend_attribute *attr, int target) +{ + if (target != ZEND_ATTRIBUTE_TARGET_CLASS) { + zend_error(E_COMPILE_ERROR, "Only classes can be marked with <>"); + } +} + ZEND_METHOD(_ZendTestClass, __toString) /* {{{ */ { RETURN_EMPTY_STRING(); } @@ -217,7 +226,7 @@ PHP_MINIT_FUNCTION(zend_test) zval val; ZVAL_LONG(&val, 123); zend_declare_typed_property( - zend_test_class, name, &val, ZEND_ACC_PUBLIC, NULL, + zend_test_class, name, &val, ZEND_ACC_PUBLIC, NULL, NULL, (zend_type) ZEND_TYPE_INIT_CODE(IS_LONG, 0, 0)); zend_string_release(name); } @@ -228,7 +237,7 @@ PHP_MINIT_FUNCTION(zend_test) zval val; ZVAL_NULL(&val); zend_declare_typed_property( - zend_test_class, name, &val, ZEND_ACC_PUBLIC, NULL, + zend_test_class, name, &val, ZEND_ACC_PUBLIC, NULL, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(class_name, 1, 0)); zend_string_release(name); } @@ -244,7 +253,7 @@ PHP_MINIT_FUNCTION(zend_test) zend_type type = ZEND_TYPE_INIT_PTR(type_list, _ZEND_TYPE_LIST_BIT, 1, 0); zval val; ZVAL_NULL(&val); - zend_declare_typed_property(zend_test_class, name, &val, ZEND_ACC_PUBLIC, NULL, type); + zend_declare_typed_property(zend_test_class, name, &val, ZEND_ACC_PUBLIC, NULL, NULL, type); zend_string_release(name); } @@ -253,7 +262,7 @@ PHP_MINIT_FUNCTION(zend_test) zval val; ZVAL_LONG(&val, 123); zend_declare_typed_property( - zend_test_class, name, &val, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC, NULL, + zend_test_class, name, &val, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC, NULL, NULL, (zend_type) ZEND_TYPE_INIT_CODE(IS_LONG, 0, 0)); zend_string_release(name); } @@ -272,6 +281,12 @@ PHP_MINIT_FUNCTION(zend_test) zend_register_class_alias("_ZendTestClassAlias", zend_test_class); REGISTER_LONG_CONSTANT("ZEND_TEST_DEPRECATED", 42, CONST_PERSISTENT | CONST_DEPRECATED); + + INIT_CLASS_ENTRY(class_entry, "ZendTestAttribute", NULL); + zend_test_attribute = zend_register_internal_class(&class_entry); + zend_test_attribute->ce_flags |= ZEND_ACC_FINAL; + + zend_compiler_attribute_register(zend_test_attribute, zend_attribute_validate_zendtestattribute); return SUCCESS; } diff --git a/ext/zip/php_zip.c b/ext/zip/php_zip.c index f131966c1ffaa..9e8160742a417 100644 --- a/ext/zip/php_zip.c +++ b/ext/zip/php_zip.c @@ -819,7 +819,7 @@ static void php_zip_register_prop_handler(HashTable *prop_handler, char *name, z /* Register for reflection */ ZVAL_NULL(&tmp); - zend_declare_property_ex(zip_class_entry, str, &tmp, ZEND_ACC_PUBLIC, NULL); + zend_declare_property_ex(zip_class_entry, str, &tmp, ZEND_ACC_PUBLIC, NULL, NULL); zend_string_release_ex(str, 1); } /* }}} */ diff --git a/win32/build/config.w32 b/win32/build/config.w32 index 7ba5ee345e706..9b5e44c37d760 100644 --- a/win32/build/config.w32 +++ b/win32/build/config.w32 @@ -231,7 +231,7 @@ ADD_SOURCES("Zend", "zend_language_parser.c zend_language_scanner.c \ zend_execute_API.c zend_highlight.c \ zend_llist.c zend_vm_opcodes.c zend_opcode.c zend_operators.c zend_ptr_stack.c \ zend_stack.c zend_variables.c zend.c zend_API.c zend_extensions.c \ - zend_hash.c zend_list.c zend_builtin_functions.c \ + zend_hash.c zend_list.c zend_builtin_functions.c zend_attributes.c \ zend_ini.c zend_sort.c zend_multibyte.c zend_ts_hash.c \ zend_stream.c zend_iterators.c zend_interfaces.c zend_objects.c \ zend_object_handlers.c zend_objects_API.c \ From 8d1f6dbe8467a469a448aa200efb6ed78d74c820 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Mon, 25 May 2020 23:56:52 +0200 Subject: [PATCH 02/10] Address review comments. --- .../tests/attributes/004_name_resolution.phpt | 17 ++++++++++++- Zend/tests/attributes/006_filter.phpt | 2 +- Zend/tests/attributes/012-ast-export.phpt | 10 ++++---- .../attributes/013_scope_resolution.phpt | 10 ++++---- Zend/zend_API.c | 1 - Zend/zend_ast.c | 4 ++-- Zend/zend_attributes.c | 10 ++++---- Zend/zend_attributes.h | 16 ++++++------- Zend/zend_compile.c | 9 +++---- Zend/zend_compile.h | 5 ++-- ext/reflection/php_reflection.c | 24 +++++++------------ ext/reflection/php_reflection.stub.php | 10 ++++---- ext/reflection/php_reflection_arginfo.h | 2 +- 13 files changed, 62 insertions(+), 58 deletions(-) diff --git a/Zend/tests/attributes/004_name_resolution.phpt b/Zend/tests/attributes/004_name_resolution.phpt index a2284e2c433c8..1ee1f5f575bf1 100644 --- a/Zend/tests/attributes/004_name_resolution.phpt +++ b/Zend/tests/attributes/004_name_resolution.phpt @@ -15,14 +15,21 @@ namespace Doctrine\ORM\Mapping { } } +namespace Doctrine\ORM\Attributes { + class Table { + } +} + namespace Foo { use Doctrine\ORM\Mapping\Entity; use Doctrine\ORM\Mapping as ORM; + use Doctrine\ORM\Attributes; <> <> <<\Doctrine\ORM\Mapping\Entity("absolute from namespace")>> <<\Entity("import absolute from global")>> + <> function foo() { } } @@ -34,7 +41,7 @@ namespace { } ?> --EXPECTF-- -array(4) { +array(5) { [0]=> array(2) { ["name"]=> @@ -75,4 +82,12 @@ array(4) { string(27) "import absolute from global" } } + [4]=> + array(2) { + ["name"]=> + string(29) "Doctrine\ORM\Attributes\Table" + ["args"]=> + array(0) { + } + } } diff --git a/Zend/tests/attributes/006_filter.phpt b/Zend/tests/attributes/006_filter.phpt index 78f5b9709f50b..2b7cc9dcdbbb9 100644 --- a/Zend/tests/attributes/006_filter.phpt +++ b/Zend/tests/attributes/006_filter.phpt @@ -107,6 +107,6 @@ Array ) string(7) "ERROR 1" -string(39) "Invalid attribute filter flag specified" +string(103) "ReflectionFunctionAbstract::getAttributes(): Argument #2 ($flags) must be a valid attribute filter flag" string(7) "ERROR 2" string(34) "Class 'SomeMissingClass' not found" diff --git a/Zend/tests/attributes/012-ast-export.phpt b/Zend/tests/attributes/012-ast-export.phpt index 47176b1b0d02d..90616b00d2813 100644 --- a/Zend/tests/attributes/012-ast-export.phpt +++ b/Zend/tests/attributes/012-ast-export.phpt @@ -21,10 +21,13 @@ assert(0 && ($a = function () { ?> --EXPECTF-- -Warning: assert(): assert(0 && ($a = <> <> function ($a, <> $b) { +Warning: assert(): assert(0 && ($a = <> +<> +function ($a, <> $b) { })) failed in %s on line %d -Warning: assert(): assert(0 && ($a = <> fn() => 1)) failed in %s on line %d +Warning: assert(): assert(0 && ($a = <> +fn() => 1)) failed in %s on line %d Warning: assert(): assert(0 && ($a = new <> class { <> @@ -32,8 +35,7 @@ Warning: assert(): assert(0 && ($a = new <> class { const FOO = 'foo'; <> public $x; - <> - public function a() { + <> public function a() { } })) failed in %s on line %d diff --git a/Zend/tests/attributes/013_scope_resolution.phpt b/Zend/tests/attributes/013_scope_resolution.phpt index fa4103cbce4f9..6ee34588945ed 100644 --- a/Zend/tests/attributes/013_scope_resolution.phpt +++ b/Zend/tests/attributes/013_scope_resolution.phpt @@ -8,10 +8,10 @@ class C1 { <> private const FOO = 'foo'; - + <> public $a; - + <> public function bar(<> $p) { } } @@ -28,7 +28,7 @@ echo "\n"; class C2 { private const FOO = 'foo'; - + public static function foo() { return <> function (<> $p) { }; @@ -44,12 +44,12 @@ echo "\n"; class C3 { private const FOO = 'foo'; - + public static function foo() { return new <> class() { private const FOO = 'bar'; - + <> public function bar() { } }; diff --git a/Zend/zend_API.c b/Zend/zend_API.c index 6e91317a62d34..b9bb8305acfbe 100644 --- a/Zend/zend_API.c +++ b/Zend/zend_API.c @@ -2596,7 +2596,6 @@ static zend_class_entry *do_register_internal_class(zend_class_entry *orig_class zend_initialize_class_data(class_entry, 0); class_entry->ce_flags = ce_flags | ZEND_ACC_CONSTANTS_UPDATED | ZEND_ACC_LINKED | ZEND_ACC_RESOLVED_PARENT | ZEND_ACC_RESOLVED_INTERFACES; class_entry->info.internal.module = EG(current_module); - class_entry->attributes = NULL; if (class_entry->info.internal.builtin_functions) { zend_register_functions(class_entry, class_entry->info.internal.builtin_functions, &class_entry->function_table, EG(current_module)->type); diff --git a/Zend/zend_ast.c b/Zend/zend_ast.c index 8a2a4255bef4c..bdf3c1fcc8e81 100644 --- a/Zend/zend_ast.c +++ b/Zend/zend_ast.c @@ -1325,7 +1325,7 @@ static ZEND_COLD void zend_ast_export_attributes(smart_str *str, zend_ast *ast, zend_ast *attr = list->child[i]; smart_str_appends(str, "<<"); - smart_str_append(str, zend_ast_get_str(attr->child[0])); + zend_ast_export_ns_name(str, attr->child[0], 0, indent); if (attr->child[1]) { zend_ast_list *args = zend_ast_get_list(attr->child[1]); @@ -1446,7 +1446,7 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio case ZEND_AST_METHOD: decl = (zend_ast_decl *) ast; if (decl->attributes) { - zend_bool newlines = (ast->kind == ZEND_AST_CLOSURE || ast->kind == ZEND_AST_ARROW_FUNC) ? 0 : 1; + zend_bool newlines = (ast->kind == ZEND_AST_CLOSURE || ast->kind == ZEND_AST_ARROW_FUNC); zend_ast_export_attributes(str, decl->attributes, indent, newlines); } if (decl->flags & ZEND_ACC_PUBLIC) { diff --git a/Zend/zend_attributes.c b/Zend/zend_attributes.c index 0ebe7043536c1..5e1d58593b786 100644 --- a/Zend/zend_attributes.c +++ b/Zend/zend_attributes.c @@ -18,7 +18,7 @@ ZEND_API zend_attributes_internal_validator zend_attribute_get_validator(zend_st return zend_hash_find_ptr(&internal_validators, lcname); } -ZEND_API void zend_attribute_free(zend_attribute *attr) +ZEND_API void zend_attribute_free(zend_attribute *attr, int persistent) { uint32_t i; @@ -29,7 +29,7 @@ ZEND_API void zend_attribute_free(zend_attribute *attr) zval_ptr_dtor(&attr->argv[i]); } - efree(attr); + pefree(attr, persistent); } static zend_attribute *get_attribute(HashTable *attributes, zend_string *lcname, uint32_t offset) @@ -86,7 +86,7 @@ ZEND_API zend_attribute *zend_get_parameter_attribute_str(HashTable *attributes, static void attribute_ptr_dtor(zval *v) { - zend_attribute_free((zend_attribute *) Z_PTR_P(v)); + zend_attribute_free((zend_attribute *) Z_PTR_P(v), 1); } ZEND_API void zend_compiler_attribute_register(zend_class_entry *ce, zend_attributes_internal_validator validator) @@ -96,7 +96,6 @@ ZEND_API void zend_compiler_attribute_register(zend_class_entry *ce, zend_attrib } zend_string *lcname = zend_string_tolower_ex(ce->name, 1); - zval tmp; zend_hash_update_ptr(&internal_validators, lcname, validator); zend_string_release(lcname); @@ -113,8 +112,7 @@ ZEND_API void zend_compiler_attribute_register(zend_class_entry *ce, zend_attrib attr->offset = 0; attr->argc = 0; - ZVAL_PTR(&tmp, attr); - zend_hash_next_index_insert(ce->attributes, &tmp); + zend_hash_next_index_insert_ptr(ce->attributes, attr); } void zend_register_attribute_ce(void) diff --git a/Zend/zend_attributes.h b/Zend/zend_attributes.h index 979e2682b1d34..34b11382493ec 100644 --- a/Zend/zend_attributes.h +++ b/Zend/zend_attributes.h @@ -1,13 +1,13 @@ #ifndef ZEND_ATTRIBUTES_H #define ZEND_ATTRIBUTES_H -#define ZEND_ATTRIBUTE_TARGET_CLASS 1 -#define ZEND_ATTRIBUTE_TARGET_FUNCTION 2 -#define ZEND_ATTRIBUTE_TARGET_METHOD 4 -#define ZEND_ATTRIBUTE_TARGET_PROPERTY 8 -#define ZEND_ATTRIBUTE_TARGET_CLASS_CONST 16 -#define ZEND_ATTRIBUTE_TARGET_PARAMETER 32 -#define ZEND_ATTRIBUTE_TARGET_ALL 63 +#define ZEND_ATTRIBUTE_TARGET_CLASS (1<<0) +#define ZEND_ATTRIBUTE_TARGET_FUNCTION (1<<1) +#define ZEND_ATTRIBUTE_TARGET_METHOD (1<<2) +#define ZEND_ATTRIBUTE_TARGET_PROPERTY (1<<3) +#define ZEND_ATTRIBUTE_TARGET_CLASS_CONST (1<<4) +#define ZEND_ATTRIBUTE_TARGET_PARAMETER (1<<5) +#define ZEND_ATTRIBUTE_TARGET_ALL (1<<6) #define ZEND_ATTRIBUTE_SIZE(argc) (sizeof(zend_attribute) + sizeof(zval) * (argc) - sizeof(zval)) @@ -26,7 +26,7 @@ typedef struct _zend_attribute { typedef void (*zend_attributes_internal_validator)(zend_attribute *attr, int target); -ZEND_API void zend_attribute_free(zend_attribute *attr); +ZEND_API void zend_attribute_free(zend_attribute *attr, int persistent); ZEND_API zend_attribute *zend_get_attribute(HashTable *attributes, zend_string *lcname); ZEND_API zend_attribute *zend_get_attribute_str(HashTable *attributes, const char *str, size_t len); diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index e283d8dd8ed43..3052c2bbc9f0c 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -1821,12 +1821,12 @@ ZEND_API void zend_initialize_class_data(zend_class_entry *ce, zend_bool nullify } else { ZEND_MAP_PTR_INIT(ce->static_members_table, &ce->default_static_members_table); ce->info.user.doc_comment = NULL; - ce->attributes = NULL; } ce->default_properties_count = 0; ce->default_static_members_count = 0; ce->properties_info_table = NULL; + ce->attributes = NULL; if (nullify_handlers) { ce->constructor = NULL; @@ -5750,7 +5750,7 @@ static zend_attribute *zend_compile_attribute(zend_ast *ast, uint32_t offset) /* static void attribute_ptr_dtor(zval *v) /* {{{ */ { - zend_attribute_free((zend_attribute *) Z_PTR_P(v)); + zend_attribute_free((zend_attribute *) Z_PTR_P(v), 0); } /* }}} */ @@ -5770,8 +5770,6 @@ static void zend_compile_attributes(HashTable *attributes, zend_ast *ast, uint32 zend_ast_list *list = zend_ast_get_list(ast); uint32_t i; - zval tmp; - ZEND_ASSERT(ast->kind == ZEND_AST_ATTRIBUTE_LIST); for (i = 0; i < list->children; i++) { @@ -5784,8 +5782,7 @@ static void zend_compile_attributes(HashTable *attributes, zend_ast *ast, uint32 validator(attr, target); } - ZVAL_PTR(&tmp, attr); - zend_hash_next_index_insert(attributes, &tmp); + zend_hash_next_index_insert_ptr(attributes, attr); } } /* }}} */ diff --git a/Zend/zend_compile.h b/Zend/zend_compile.h index 4d47d0da5debc..1a53f92e9e6eb 100644 --- a/Zend/zend_compile.h +++ b/Zend/zend_compile.h @@ -120,7 +120,6 @@ typedef struct _zend_file_context { typedef union _zend_parser_stack_elem { zend_ast *ast; zend_string *str; - HashTable *hash; zend_ulong num; unsigned char *ptr; } zend_parser_stack_elem; @@ -406,7 +405,7 @@ struct _zend_op_array { uint32_t num_args; uint32_t required_num_args; zend_arg_info *arg_info; - HashTable *attributes; + HashTable *attributes; /* END of common elements */ int cache_size; /* number of run_time_cache_slots * sizeof(void*) */ @@ -456,7 +455,7 @@ typedef struct _zend_internal_function { uint32_t num_args; uint32_t required_num_args; zend_internal_arg_info *arg_info; - HashTable *attributes; + HashTable *attributes; /* END of common elements */ zif_handler handler; diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index 72312181c22d6..fe3fcb8dab772 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -1146,8 +1146,6 @@ static int read_attributes(zval *ret, HashTable *attributes, zend_class_entry *s static void reflect_attributes(INTERNAL_FUNCTION_PARAMETERS, HashTable *attributes, uint32_t offset, zend_class_entry *scope) /* {{{ */ { - zval ret; - zend_string *name = NULL; zend_long flags = 0; zend_class_entry *base = NULL; @@ -1157,7 +1155,7 @@ static void reflect_attributes(INTERNAL_FUNCTION_PARAMETERS, HashTable *attribut } if (flags & ~REFLECTION_ATTRIBUTE_IS_INSTANCEOF) { - zend_throw_error(NULL, "Invalid attribute filter flag specified"); + zend_argument_value_error(2, "must be a valid attribute filter flag"); RETURN_THROWS(); } @@ -1177,14 +1175,11 @@ static void reflect_attributes(INTERNAL_FUNCTION_PARAMETERS, HashTable *attribut RETURN_EMPTY_ARRAY(); } - array_init(&ret); + array_init(return_value); - if (FAILURE == read_attributes(&ret, attributes, scope, offset, name, base)) { - zval_ptr_dtor(&ret); + if (FAILURE == read_attributes(return_value, attributes, scope, offset, name, base)) { RETURN_THROWS(); } - - RETURN_ZVAL(&ret, 1, 1); } /* }}} */ @@ -6393,6 +6388,8 @@ ZEND_METHOD(ReflectionAttribute, __construct) ZEND_METHOD(ReflectionAttribute, __clone) { + /* Should never be executable */ + _DO_THROW("Cannot clone object using __clone()"); } /* {{{ proto public string ReflectionAttribute::getName() @@ -6415,6 +6412,7 @@ static zend_always_inline int import_attribute_value(zval *ret, zval *val, zend_ { if (Z_TYPE_P(val) == IS_CONSTANT_AST) { *ret = *val; + //ZVAL_COPY(ret, val); if (FAILURE == zval_update_constant_ex(ret, scope)) { return FAILURE; @@ -6434,7 +6432,6 @@ ZEND_METHOD(ReflectionAttribute, getArguments) reflection_object *intern; attribute_reference *attr; - zval ret; zval tmp; uint32_t i; @@ -6443,18 +6440,15 @@ ZEND_METHOD(ReflectionAttribute, getArguments) } GET_REFLECTION_OBJECT_PTR(attr); - array_init(&ret); + array_init(return_value); for (i = 0; i < attr->data->argc; i++) { if (FAILURE == import_attribute_value(&tmp, &attr->data->argv[i], attr->scope)) { - zval_ptr_dtor(&ret); RETURN_THROWS(); } - add_next_index_zval(&ret, &tmp); + add_next_index_zval(return_value, &tmp); } - - RETURN_ZVAL(&ret, 1, 1); } /* }}} */ @@ -6582,7 +6576,7 @@ ZEND_METHOD(ReflectionAttribute, newInstance) attribute_ctor_cleanup(NULL, args, argc); - RETURN_ZVAL(&obj, 1, 1); + RETURN_COPY_VALUE(&obj); } /* }}} */ static const zend_function_entry reflection_ext_functions[] = { /* {{{ */ diff --git a/ext/reflection/php_reflection.stub.php b/ext/reflection/php_reflection.stub.php index f5b3adc8db0a0..c27f971c070cb 100644 --- a/ext/reflection/php_reflection.stub.php +++ b/ext/reflection/php_reflection.stub.php @@ -97,7 +97,7 @@ public function hasReturnType() {} public function getReturnType() {} /** @return ReflectionAttribute[] */ - public function getAttributes(?string $name = null, int $flags = 0) {} + public function getAttributes(?string $name = null, int $flags = 0): array {} } class ReflectionFunction extends ReflectionFunctionAbstract @@ -368,7 +368,7 @@ public function getNamespaceName() {} public function getShortName() {} /** @return ReflectionAttribute[] */ - public function getAttributes(?string $name = null, int $flags = 0) {} + public function getAttributes(?string $name = null, int $flags = 0): array {} } class ReflectionObject extends ReflectionClass @@ -437,7 +437,7 @@ public function hasDefaultValue(): bool {} public function getDefaultValue() {} /** @return ReflectionAttribute[] */ - public function getAttributes(?string $name = null, int $flags = 0) {} + public function getAttributes(?string $name = null, int $flags = 0): array {} } class ReflectionClassConstant implements Reflector @@ -475,7 +475,7 @@ public function getDeclaringClass() {} public function getDocComment() {} /** @return ReflectionAttribute[] */ - public function getAttributes(?string $name = null, int $flags = 0) {} + public function getAttributes(?string $name = null, int $flags = 0): array {} } class ReflectionParameter implements Reflector @@ -554,7 +554,7 @@ public function getDefaultValueConstantName() {} public function isVariadic() {} /** @return ReflectionAttribute[] */ - public function getAttributes(?string $name = null, int $flags = 0) {} + public function getAttributes(?string $name = null, int $flags = 0): array {} } abstract class ReflectionType implements Stringable diff --git a/ext/reflection/php_reflection_arginfo.h b/ext/reflection/php_reflection_arginfo.h index 0efaff41bfd7a..3dec73ec8b495 100644 --- a/ext/reflection/php_reflection_arginfo.h +++ b/ext/reflection/php_reflection_arginfo.h @@ -57,7 +57,7 @@ ZEND_END_ARG_INFO() #define arginfo_class_ReflectionFunctionAbstract_getReturnType arginfo_class_ReflectionFunctionAbstract___clone -ZEND_BEGIN_ARG_INFO_EX(arginfo_class_ReflectionFunctionAbstract_getAttributes, 0, 0, 0) +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_ReflectionFunctionAbstract_getAttributes, 0, 0, IS_ARRAY, 0) ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, name, IS_STRING, 1, "null") ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, flags, IS_LONG, 0, "0") ZEND_END_ARG_INFO() From 52b2a2d5703b13624e1f41095f60a45ccad02665 Mon Sep 17 00:00:00 2001 From: Benjamin Eberlei Date: Tue, 26 May 2020 08:58:14 +0200 Subject: [PATCH 03/10] Fix ReflectionClass::__toString test --- ext/reflection/tests/ReflectionClass_toString_001.phpt | 1 + 1 file changed, 1 insertion(+) diff --git a/ext/reflection/tests/ReflectionClass_toString_001.phpt b/ext/reflection/tests/ReflectionClass_toString_001.phpt index 3dff0725167c9..fb39bdb4defb7 100644 --- a/ext/reflection/tests/ReflectionClass_toString_001.phpt +++ b/ext/reflection/tests/ReflectionClass_toString_001.phpt @@ -372,6 +372,7 @@ Class [ class ReflectionClass implements Reflector, String Parameter #0 [ ?string $name = null ] Parameter #1 [ int $flags = 0 ] } + - Return [ array ] } } } From a8d975193b3a267ad3a182f7b9c38177a532d9b7 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Tue, 26 May 2020 10:20:42 +0200 Subject: [PATCH 04/10] Fix update constant of attribute value --- ext/reflection/php_reflection.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index fe3fcb8dab772..98198f0404a95 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -6410,15 +6410,12 @@ ZEND_METHOD(ReflectionAttribute, getName) static zend_always_inline int import_attribute_value(zval *ret, zval *val, zend_class_entry *scope) /* {{{ */ { + ZVAL_COPY_OR_DUP(ret, val); if (Z_TYPE_P(val) == IS_CONSTANT_AST) { - *ret = *val; - //ZVAL_COPY(ret, val); - if (FAILURE == zval_update_constant_ex(ret, scope)) { + zval_ptr_dtor(ret); return FAILURE; } - } else { - ZVAL_COPY_OR_DUP(ret, val); } return SUCCESS; From 8192d8b2c88f315a1da5d1104e1bd46e21e1258d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Schr=C3=B6der?= Date: Thu, 28 May 2020 12:53:08 +0200 Subject: [PATCH 05/10] Changes to opcache. Fixed AST dump. Proper NULL-handling in reflection. --- Zend/tests/attributes/012-ast-export.phpt | 10 ++--- Zend/zend_ast.c | 2 +- Zend/zend_ast.h | 12 ----- ext/opcache/zend_file_cache.c | 54 +++++++++++------------ ext/opcache/zend_persist.c | 3 +- ext/reflection/php_reflection.c | 2 +- 6 files changed, 32 insertions(+), 51 deletions(-) diff --git a/Zend/tests/attributes/012-ast-export.phpt b/Zend/tests/attributes/012-ast-export.phpt index 90616b00d2813..47176b1b0d02d 100644 --- a/Zend/tests/attributes/012-ast-export.phpt +++ b/Zend/tests/attributes/012-ast-export.phpt @@ -21,13 +21,10 @@ assert(0 && ($a = function () { ?> --EXPECTF-- -Warning: assert(): assert(0 && ($a = <> -<> -function ($a, <> $b) { +Warning: assert(): assert(0 && ($a = <> <> function ($a, <> $b) { })) failed in %s on line %d -Warning: assert(): assert(0 && ($a = <> -fn() => 1)) failed in %s on line %d +Warning: assert(): assert(0 && ($a = <> fn() => 1)) failed in %s on line %d Warning: assert(): assert(0 && ($a = new <> class { <> @@ -35,7 +32,8 @@ Warning: assert(): assert(0 && ($a = new <> class { const FOO = 'foo'; <> public $x; - <> public function a() { + <> + public function a() { } })) failed in %s on line %d diff --git a/Zend/zend_ast.c b/Zend/zend_ast.c index bdf3c1fcc8e81..46084fc923498 100644 --- a/Zend/zend_ast.c +++ b/Zend/zend_ast.c @@ -1446,7 +1446,7 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio case ZEND_AST_METHOD: decl = (zend_ast_decl *) ast; if (decl->attributes) { - zend_bool newlines = (ast->kind == ZEND_AST_CLOSURE || ast->kind == ZEND_AST_ARROW_FUNC); + zend_bool newlines = !(ast->kind == ZEND_AST_CLOSURE || ast->kind == ZEND_AST_ARROW_FUNC); zend_ast_export_attributes(str, decl->attributes, indent, newlines); } if (decl->flags & ZEND_ACC_PUBLIC) { diff --git a/Zend/zend_ast.h b/Zend/zend_ast.h index 90efaa7bbc107..eae7407461f24 100644 --- a/Zend/zend_ast.h +++ b/Zend/zend_ast.h @@ -315,12 +315,6 @@ static zend_always_inline zend_string *zend_ast_get_constant_name(zend_ast *ast) return Z_STR(((zend_ast_zval *) ast)->val); } -static zend_always_inline HashTable *zend_ast_get_hash(zend_ast *ast) { - zval *zv = zend_ast_get_zval(ast); - ZEND_ASSERT(Z_TYPE_P(zv) == IS_ARRAY); - return Z_ARR_P(zv); -} - static zend_always_inline uint32_t zend_ast_get_num_children(zend_ast *ast) { ZEND_ASSERT(!zend_ast_is_list(ast)); return ast->kind >> ZEND_AST_NUM_CHILDREN_SHIFT; @@ -334,12 +328,6 @@ static zend_always_inline uint32_t zend_ast_get_lineno(zend_ast *ast) { } } -static zend_always_inline zend_ast *zend_ast_create_zval_from_hash(HashTable *hash) { - zval zv; - ZVAL_ARR(&zv, hash); - return zend_ast_create_zval(&zv); -} - static zend_always_inline zend_ast *zend_ast_create_binary_op(uint32_t opcode, zend_ast *op0, zend_ast *op1) { return zend_ast_create_ex(ZEND_AST_BINARY_OP, opcode, op0, op1); } diff --git a/ext/opcache/zend_file_cache.c b/ext/opcache/zend_file_cache.c index 17def06aac973..3d03100fceea9 100644 --- a/ext/opcache/zend_file_cache.c +++ b/ext/opcache/zend_file_cache.c @@ -404,25 +404,23 @@ static void zend_file_cache_serialize_attribute(zval *zv, zend_file_cache_metainfo *info, void *buf) { - if (!IS_SERIALIZED(Z_PTR_P(zv))) { - zend_attribute *attr = Z_PTR_P(zv); - uint32_t i; + zend_attribute *attr = Z_PTR_P(zv); + uint32_t i; - SERIALIZE_PTR(Z_PTR_P(zv)); - attr = Z_PTR_P(zv); - UNSERIALIZE_PTR(attr); + SERIALIZE_PTR(Z_PTR_P(zv)); + attr = Z_PTR_P(zv); + UNSERIALIZE_PTR(attr); - if (!IS_SERIALIZED(attr->name)) { - SERIALIZE_STR(attr->name); - } + if (!IS_SERIALIZED(attr->name)) { + SERIALIZE_STR(attr->name); + } - if (!IS_SERIALIZED(attr->lcname)) { - SERIALIZE_STR(attr->lcname); - } + if (!IS_SERIALIZED(attr->lcname)) { + SERIALIZE_STR(attr->lcname); + } - for (i = 0; i < attr->argc; i++) { - zend_file_cache_serialize_zval(&attr->argv[i], script, info, buf); - } + for (i = 0; i < attr->argc; i++) { + zend_file_cache_serialize_zval(&attr->argv[i], script, info, buf); } } @@ -1175,24 +1173,22 @@ static void zend_file_cache_unserialize_zval(zval *zv, static void zend_file_cache_unserialize_attribute(zval *zv, zend_persistent_script *script, void *buf) { - if (!IS_UNSERIALIZED(Z_PTR_P(zv))) { - zend_attribute *attr; - uint32_t i; + zend_attribute *attr; + uint32_t i; - UNSERIALIZE_PTR(Z_PTR_P(zv)); - attr = Z_PTR_P(zv); + UNSERIALIZE_PTR(Z_PTR_P(zv)); + attr = Z_PTR_P(zv); - if (!IS_UNSERIALIZED(attr->name)) { - UNSERIALIZE_STR(attr->name); - } + if (!IS_UNSERIALIZED(attr->name)) { + UNSERIALIZE_STR(attr->name); + } - if (!IS_UNSERIALIZED(attr->lcname)) { - UNSERIALIZE_STR(attr->lcname); - } + if (!IS_UNSERIALIZED(attr->lcname)) { + UNSERIALIZE_STR(attr->lcname); + } - for (i = 0; i < attr->argc; i++) { - zend_file_cache_unserialize_zval(&attr->argv[i], script, buf); - } + for (i = 0; i < attr->argc; i++) { + zend_file_cache_unserialize_zval(&attr->argv[i], script, buf); } } diff --git a/ext/opcache/zend_persist.c b/ext/opcache/zend_persist.c index 46142a4627aa0..11f26cc123240 100644 --- a/ext/opcache/zend_persist.c +++ b/ext/opcache/zend_persist.c @@ -272,7 +272,7 @@ static HashTable *zend_persist_attributes(HashTable *attributes) ZEND_HASH_FOREACH_VAL(attributes, v) { zend_attribute *attr = Z_PTR_P(v); - zend_attribute *copy = zend_shared_memdup(attr, ZEND_ATTRIBUTE_SIZE(attr->argc)); + zend_attribute *copy = zend_shared_memdup_put_free(attr, ZEND_ATTRIBUTE_SIZE(attr->argc)); zend_accel_store_interned_string(copy->name); zend_accel_store_interned_string(copy->lcname); @@ -282,7 +282,6 @@ static HashTable *zend_persist_attributes(HashTable *attributes) } ZVAL_PTR(v, copy); - efree(attr); } ZEND_HASH_FOREACH_END(); ptr = zend_shared_memdup_put_free(attributes, sizeof(HashTable)); diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index 98198f0404a95..9637ca4608a43 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -1150,7 +1150,7 @@ static void reflect_attributes(INTERNAL_FUNCTION_PARAMETERS, HashTable *attribut zend_long flags = 0; zend_class_entry *base = NULL; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "|Sl", &name, &flags) == FAILURE) { + if (zend_parse_parameters(ZEND_NUM_ARGS(), "|S!l", &name, &flags) == FAILURE) { RETURN_THROWS(); } From 1a270b23450414e018d9bbbde26082b85da952e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Schr=C3=B6der?= Date: Thu, 28 May 2020 13:09:29 +0200 Subject: [PATCH 06/10] Refactored declaration AST to store attributes in child[4]. --- Zend/zend_ast.c | 24 +++++++++++------------- Zend/zend_ast.h | 5 ++--- Zend/zend_compile.c | 8 ++++---- Zend/zend_language_parser.y | 18 +++++++++--------- 4 files changed, 26 insertions(+), 29 deletions(-) diff --git a/Zend/zend_ast.c b/Zend/zend_ast.c index 46084fc923498..ba8af9bcdfc8b 100644 --- a/Zend/zend_ast.c +++ b/Zend/zend_ast.c @@ -114,7 +114,7 @@ ZEND_API zend_ast * ZEND_FASTCALL zend_ast_create_class_const_or_name(zend_ast * ZEND_API zend_ast *zend_ast_create_decl( zend_ast_kind kind, uint32_t flags, uint32_t start_lineno, zend_string *doc_comment, - zend_string *name, zend_ast *child0, zend_ast *child1, zend_ast *child2, zend_ast *child3 + zend_string *name, zend_ast *child0, zend_ast *child1, zend_ast *child2, zend_ast *child3, zend_ast *child4 ) { zend_ast_decl *ast; @@ -126,12 +126,12 @@ ZEND_API zend_ast *zend_ast_create_decl( ast->flags = flags; ast->lex_pos = LANG_SCNG(yy_text); ast->doc_comment = doc_comment; - ast->attributes = NULL; ast->name = name; ast->child[0] = child0; ast->child[1] = child1; ast->child[2] = child2; ast->child[3] = child3; + ast->child[4] = child4; return (zend_ast *) ast; } @@ -858,13 +858,11 @@ ZEND_API void ZEND_FASTCALL zend_ast_destroy(zend_ast *ast) if (decl->doc_comment) { zend_string_release_ex(decl->doc_comment, 0); } - if (decl->attributes) { - zend_ast_destroy(decl->attributes); - } zend_ast_destroy(decl->child[0]); zend_ast_destroy(decl->child[1]); zend_ast_destroy(decl->child[2]); - ast = decl->child[3]; + zend_ast_destroy(decl->child[3]); + ast = decl->child[4]; goto tail_call; } } @@ -1445,9 +1443,9 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio case ZEND_AST_ARROW_FUNC: case ZEND_AST_METHOD: decl = (zend_ast_decl *) ast; - if (decl->attributes) { + if (decl->child[4]) { zend_bool newlines = !(ast->kind == ZEND_AST_CLOSURE || ast->kind == ZEND_AST_ARROW_FUNC); - zend_ast_export_attributes(str, decl->attributes, indent, newlines); + zend_ast_export_attributes(str, decl->child[4], indent, newlines); } if (decl->flags & ZEND_ACC_PUBLIC) { smart_str_appends(str, "public "); @@ -1505,8 +1503,8 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio break; case ZEND_AST_CLASS: decl = (zend_ast_decl *) ast; - if (decl->attributes) { - zend_ast_export_attributes(str, decl->attributes, indent, 1); + if (decl->child[4]) { + zend_ast_export_attributes(str, decl->child[4], indent, 1); } if (decl->flags & ZEND_ACC_INTERFACE) { smart_str_appends(str, "interface "); @@ -1839,8 +1837,8 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio smart_str_appends(str, "new "); if (ast->child[0]->kind == ZEND_AST_CLASS) { zend_ast_decl *decl = (zend_ast_decl *) ast->child[0]; - if (decl->attributes) { - zend_ast_export_attributes(str, decl->attributes, indent, 0); + if (decl->child[4]) { + zend_ast_export_attributes(str, decl->child[4], indent, 0); } smart_str_appends(str, "class"); if (zend_ast_get_list(ast->child[1])->children) { @@ -2187,7 +2185,7 @@ zend_ast * ZEND_FASTCALL zend_ast_with_attributes(zend_ast *ast, zend_ast *attr) case ZEND_AST_METHOD: case ZEND_AST_CLASS: case ZEND_AST_ARROW_FUNC: - ((zend_ast_decl *) ast)->attributes = attr; + ((zend_ast_decl *) ast)->child[4] = attr; break; case ZEND_AST_PROP_GROUP: ast->child[2] = attr; diff --git a/Zend/zend_ast.h b/Zend/zend_ast.h index eae7407461f24..0ec922dcea47f 100644 --- a/Zend/zend_ast.h +++ b/Zend/zend_ast.h @@ -194,9 +194,8 @@ typedef struct _zend_ast_decl { uint32_t flags; unsigned char *lex_pos; zend_string *doc_comment; - zend_ast *attributes; zend_string *name; - zend_ast *child[4]; + zend_ast *child[5]; } zend_ast_decl; typedef void (*zend_ast_process_t)(zend_ast *ast); @@ -274,7 +273,7 @@ ZEND_API zend_ast * ZEND_FASTCALL zend_ast_list_add(zend_ast *list, zend_ast *op ZEND_API zend_ast *zend_ast_create_decl( zend_ast_kind kind, uint32_t flags, uint32_t start_lineno, zend_string *doc_comment, - zend_string *name, zend_ast *child0, zend_ast *child1, zend_ast *child2, zend_ast *child3 + zend_string *name, zend_ast *child0, zend_ast *child1, zend_ast *child2, zend_ast *child3, zend_ast *child4 ); ZEND_API int ZEND_FASTCALL zend_ast_evaluate(zval *result, zend_ast *ast, zend_class_entry *scope); diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 3052c2bbc9f0c..d0cfd9b4d3d5d 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -6370,14 +6370,14 @@ void zend_compile_func_decl(znode *result, zend_ast *ast, zend_bool toplevel) /* if (decl->doc_comment) { op_array->doc_comment = zend_string_copy(decl->doc_comment); } - if (decl->attributes) { + if (decl->child[4]) { int target = ZEND_ATTRIBUTE_TARGET_FUNCTION; if (is_method) { target = ZEND_ATTRIBUTE_TARGET_METHOD; } op_array->attributes = create_attribute_array(); - zend_compile_attributes(op_array->attributes, decl->attributes, 0, target); + zend_compile_attributes(op_array->attributes, decl->child[4], 0, target); } if (decl->kind == ZEND_AST_CLOSURE || decl->kind == ZEND_AST_ARROW_FUNC) { op_array->fn_flags |= ZEND_ACC_CLOSURE; @@ -6823,9 +6823,9 @@ void zend_compile_class_decl(znode *result, zend_ast *ast, zend_bool toplevel) / if (decl->doc_comment) { ce->info.user.doc_comment = zend_string_copy(decl->doc_comment); } - if (decl->attributes) { + if (decl->child[4]) { ce->attributes = create_attribute_array(); - zend_compile_attributes(ce->attributes, decl->attributes, 0, ZEND_ATTRIBUTE_TARGET_CLASS); + zend_compile_attributes(ce->attributes, decl->child[4], 0, ZEND_ATTRIBUTE_TARGET_CLASS); } if (UNEXPECTED((decl->flags & ZEND_ACC_ANON_CLASS))) { diff --git a/Zend/zend_language_parser.y b/Zend/zend_language_parser.y index 7acfb7c29c2a1..d8d66553c1335 100644 --- a/Zend/zend_language_parser.y +++ b/Zend/zend_language_parser.y @@ -526,7 +526,7 @@ function_declaration_statement: function returns_ref T_STRING backup_doc_comment '(' parameter_list ')' return_type backup_fn_flags '{' inner_statement_list '}' backup_fn_flags { $$ = zend_ast_create_decl(ZEND_AST_FUNC_DECL, $2 | $13, $1, $4, - zend_ast_get_str($3), $6, NULL, $11, $8); CG(extra_fn_flags) = $9; } + zend_ast_get_str($3), $6, NULL, $11, $8, NULL); CG(extra_fn_flags) = $9; } ; is_reference: @@ -542,10 +542,10 @@ is_variadic: class_declaration_statement: class_modifiers T_CLASS { $$ = CG(zend_lineno); } T_STRING extends_from implements_list backup_doc_comment '{' class_statement_list '}' - { $$ = zend_ast_create_decl(ZEND_AST_CLASS, $1, $3, $7, zend_ast_get_str($4), $5, $6, $9, NULL); } + { $$ = zend_ast_create_decl(ZEND_AST_CLASS, $1, $3, $7, zend_ast_get_str($4), $5, $6, $9, NULL, NULL); } | T_CLASS { $$ = CG(zend_lineno); } T_STRING extends_from implements_list backup_doc_comment '{' class_statement_list '}' - { $$ = zend_ast_create_decl(ZEND_AST_CLASS, 0, $2, $6, zend_ast_get_str($3), $4, $5, $8, NULL); } + { $$ = zend_ast_create_decl(ZEND_AST_CLASS, 0, $2, $6, zend_ast_get_str($3), $4, $5, $8, NULL, NULL); } ; class_modifiers: @@ -562,13 +562,13 @@ class_modifier: trait_declaration_statement: T_TRAIT { $$ = CG(zend_lineno); } T_STRING backup_doc_comment '{' class_statement_list '}' - { $$ = zend_ast_create_decl(ZEND_AST_CLASS, ZEND_ACC_TRAIT, $2, $4, zend_ast_get_str($3), NULL, NULL, $6, NULL); } + { $$ = zend_ast_create_decl(ZEND_AST_CLASS, ZEND_ACC_TRAIT, $2, $4, zend_ast_get_str($3), NULL, NULL, $6, NULL, NULL); } ; interface_declaration_statement: T_INTERFACE { $$ = CG(zend_lineno); } T_STRING interface_extends_list backup_doc_comment '{' class_statement_list '}' - { $$ = zend_ast_create_decl(ZEND_AST_CLASS, ZEND_ACC_INTERFACE, $2, $5, zend_ast_get_str($3), NULL, $4, $7, NULL); } + { $$ = zend_ast_create_decl(ZEND_AST_CLASS, ZEND_ACC_INTERFACE, $2, $5, zend_ast_get_str($3), NULL, $4, $7, NULL, NULL); } ; extends_from: @@ -795,7 +795,7 @@ attributed_class_statement: | method_modifiers function returns_ref identifier backup_doc_comment '(' parameter_list ')' return_type backup_fn_flags method_body backup_fn_flags { $$ = zend_ast_create_decl(ZEND_AST_METHOD, $3 | $1 | $12, $2, $5, - zend_ast_get_str($4), $7, NULL, $11, $9); CG(extra_fn_flags) = $10; } + zend_ast_get_str($4), $7, NULL, $11, $9, NULL); CG(extra_fn_flags) = $10; } class_statement: attributed_class_statement { $$ = $1; } @@ -933,7 +933,7 @@ anonymous_class: extends_from implements_list backup_doc_comment '{' class_statement_list '}' { zend_ast *decl = zend_ast_create_decl( ZEND_AST_CLASS, ZEND_ACC_ANON_CLASS, $2, $6, NULL, - $4, $5, $8, NULL); + $4, $5, $8, NULL, NULL); $$ = zend_ast_create(ZEND_AST_NEW, decl, $3); } ; @@ -1077,11 +1077,11 @@ inline_function: backup_fn_flags '{' inner_statement_list '}' backup_fn_flags { $$ = zend_ast_create_decl(ZEND_AST_CLOSURE, $2 | $13, $1, $3, zend_string_init("{closure}", sizeof("{closure}") - 1, 0), - $5, $7, $11, $8); CG(extra_fn_flags) = $9; } + $5, $7, $11, $8, NULL); CG(extra_fn_flags) = $9; } | fn returns_ref '(' parameter_list ')' return_type backup_doc_comment T_DOUBLE_ARROW backup_fn_flags backup_lex_pos expr backup_fn_flags { $$ = zend_ast_create_decl(ZEND_AST_ARROW_FUNC, $2 | $12, $1, $7, zend_string_init("{closure}", sizeof("{closure}") - 1, 0), $4, NULL, - zend_ast_create(ZEND_AST_RETURN, $11), $6); + zend_ast_create(ZEND_AST_RETURN, $11), $6, NULL); ((zend_ast_decl *) $$)->lex_pos = $10; CG(extra_fn_flags) = $9; } ; From 83fea2c1dcf18c2b4d1cddfc0aaebd7b301d6292 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Schr=C3=B6der?= Date: Thu, 28 May 2020 16:10:08 +0200 Subject: [PATCH 07/10] Aligned scope resolution code. Added closure scope and rebind testing. --- ...e_resolution.phpt => 013_class_scope.phpt} | 49 ++++++++--------- Zend/tests/attributes/017_closure_scope.phpt | 53 +++++++++++++++++++ Zend/zend_compile.c | 32 +++++------ ext/reflection/php_reflection.c | 3 +- 4 files changed, 92 insertions(+), 45 deletions(-) rename Zend/tests/attributes/{013_scope_resolution.phpt => 013_class_scope.phpt} (62%) create mode 100644 Zend/tests/attributes/017_closure_scope.phpt diff --git a/Zend/tests/attributes/013_scope_resolution.phpt b/Zend/tests/attributes/013_class_scope.phpt similarity index 62% rename from Zend/tests/attributes/013_scope_resolution.phpt rename to Zend/tests/attributes/013_class_scope.phpt index 6ee34588945ed..6f5dccd4c84ae 100644 --- a/Zend/tests/attributes/013_scope_resolution.phpt +++ b/Zend/tests/attributes/013_class_scope.phpt @@ -1,5 +1,5 @@ --TEST-- -Attributes make use of correct scope. +Attributes make use of class scope. --FILE-- getMethod('bar')->getParameters()[0]->getAttributes()[0]->getArgum echo "\n"; -class C2 +trait T1 { - private const FOO = 'foo'; - - public static function foo() - { - return <> function (<> $p) { }; - } + <> + public function foo() { } } -$ref = new \ReflectionFunction(C2::foo()); -print_r($ref->getAttributes()[0]->getArguments()); -print_r($ref->getParameters()[0]->getAttributes()[0]->getArguments()); +class C2 +{ + use T1; + + private const FOO = 'bar'; +} +$ref = new \ReflectionClass(C2::class); +print_r($ref->getMethod('foo')->getAttributes()[0]->getArguments()); echo "\n"; class C3 @@ -56,13 +57,13 @@ class C3 } } -$obj = C3::foo(); -$ref = new \ReflectionObject($obj); -$name = $ref->getMethod('bar')->getAttributes()[0]->getArguments()[0]; +$ref = new \ReflectionObject(C3::foo()); -print_r($ref->getAttributes()[0]->getArguments()); -var_dump($name == get_class($obj)); -var_dump($ref->getMethod('bar')->getAttributes()[0]->getArguments()[1]); +$args = $ref->getAttributes()[0]->getArguments(); +var_dump($args[0] == $ref->getName(), $args[1]); + +$args = $ref->getMethod('bar')->getAttributes()[0]->getArguments(); +var_dump($args[0] == $ref->getName(), $args[1]); ?> --EXPECT-- @@ -95,18 +96,10 @@ Array Array ( [0] => C2 - [1] => foo -) -Array -( - [0] => C2 - [1] => foo + [1] => bar ) -Array -( - [0] => C3 - [1] => foo -) +bool(true) +string(3) "bar" bool(true) string(3) "bar" diff --git a/Zend/tests/attributes/017_closure_scope.phpt b/Zend/tests/attributes/017_closure_scope.phpt new file mode 100644 index 0000000000000..f57b4b73b3c08 --- /dev/null +++ b/Zend/tests/attributes/017_closure_scope.phpt @@ -0,0 +1,53 @@ +--TEST-- +Attributes make use of closure scope. +--FILE-- +> function (<> $p) { }; + } +} + +$ref = new \ReflectionFunction(C1::foo()); +print_r($ref->getAttributes()[0]->getArguments()); +print_r($ref->getParameters()[0]->getAttributes()[0]->getArguments()); + +echo "\n"; + +$ref = new \ReflectionFunction(C1::foo()->bindTo(null, Test1::class)); +print_r($ref->getAttributes()[0]->getArguments()); +print_r($ref->getParameters()[0]->getAttributes()[0]->getArguments()); + +?> +--EXPECT-- +Array +( + [0] => C1 + [1] => foo +) +Array +( + [0] => C1 + [1] => foo +) + +Array +( + [0] => Test1 + [1] => bar +) +Array +( + [0] => Test1 + [1] => bar +) diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index d0cfd9b4d3d5d..caf6176b561d6 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -1046,10 +1046,6 @@ ZEND_API void function_add_ref(zend_function *function) /* {{{ */ ZEND_MAP_PTR_INIT(op_array->run_time_cache, zend_arena_alloc(&CG(arena), sizeof(void*))); ZEND_MAP_PTR_SET(op_array->run_time_cache, NULL); } - - if (op_array->attributes) { - GC_ADDREF(op_array->attributes); - } } if (function->common.function_name) { @@ -6370,15 +6366,7 @@ void zend_compile_func_decl(znode *result, zend_ast *ast, zend_bool toplevel) /* if (decl->doc_comment) { op_array->doc_comment = zend_string_copy(decl->doc_comment); } - if (decl->child[4]) { - int target = ZEND_ATTRIBUTE_TARGET_FUNCTION; - if (is_method) { - target = ZEND_ATTRIBUTE_TARGET_METHOD; - } - op_array->attributes = create_attribute_array(); - zend_compile_attributes(op_array->attributes, decl->child[4], 0, target); - } if (decl->kind == ZEND_AST_CLOSURE || decl->kind == ZEND_AST_ARROW_FUNC) { op_array->fn_flags |= ZEND_ACC_CLOSURE; } @@ -6398,6 +6386,17 @@ void zend_compile_func_decl(znode *result, zend_ast *ast, zend_bool toplevel) /* CG(active_op_array) = op_array; + if (decl->child[4]) { + int target = ZEND_ATTRIBUTE_TARGET_FUNCTION; + + if (is_method) { + target = ZEND_ATTRIBUTE_TARGET_METHOD; + } + + op_array->attributes = create_attribute_array(); + zend_compile_attributes(op_array->attributes, decl->child[4], 0, target); + } + /* Do not leak the class scope into free standing functions, even if they are dynamically * defined inside a class method. This is necessary for correct handling of magic constants. * For example __CLASS__ should always be "" inside a free standing function. */ @@ -6823,10 +6822,6 @@ void zend_compile_class_decl(znode *result, zend_ast *ast, zend_bool toplevel) / if (decl->doc_comment) { ce->info.user.doc_comment = zend_string_copy(decl->doc_comment); } - if (decl->child[4]) { - ce->attributes = create_attribute_array(); - zend_compile_attributes(ce->attributes, decl->child[4], 0, ZEND_ATTRIBUTE_TARGET_CLASS); - } if (UNEXPECTED((decl->flags & ZEND_ACC_ANON_CLASS))) { /* Serialization is not supported for anonymous classes */ @@ -6841,6 +6836,11 @@ void zend_compile_class_decl(znode *result, zend_ast *ast, zend_bool toplevel) / CG(active_class_entry) = ce; + if (decl->child[4]) { + ce->attributes = create_attribute_array(); + zend_compile_attributes(ce->attributes, decl->child[4], 0, ZEND_ATTRIBUTE_TARGET_CLASS); + } + if (implements_ast) { zend_compile_implements(implements_ast); } diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index 9637ca4608a43..6d4f257eb4a8c 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -6411,8 +6411,9 @@ ZEND_METHOD(ReflectionAttribute, getName) static zend_always_inline int import_attribute_value(zval *ret, zval *val, zend_class_entry *scope) /* {{{ */ { ZVAL_COPY_OR_DUP(ret, val); + if (Z_TYPE_P(val) == IS_CONSTANT_AST) { - if (FAILURE == zval_update_constant_ex(ret, scope)) { + if (SUCCESS != zval_update_constant_ex(ret, scope)) { zval_ptr_dtor(ret); return FAILURE; } From 8697ccfc8a41248fa9b3d16e95820141527b3262 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Schr=C3=B6der?= Date: Sun, 31 May 2020 11:28:26 +0200 Subject: [PATCH 08/10] Removed unneeded serialization checks. Added trait attribute scope test. --- Zend/tests/attributes/013_class_scope.phpt | 12 ++++++++++++ ext/opcache/zend_file_cache.c | 18 ++++-------------- 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/Zend/tests/attributes/013_class_scope.phpt b/Zend/tests/attributes/013_class_scope.phpt index 6f5dccd4c84ae..3bb2b303ba9a2 100644 --- a/Zend/tests/attributes/013_class_scope.phpt +++ b/Zend/tests/attributes/013_class_scope.phpt @@ -40,6 +40,16 @@ class C2 $ref = new \ReflectionClass(C2::class); print_r($ref->getMethod('foo')->getAttributes()[0]->getArguments()); + +$ref = new \ReflectionClass(T1::class); +$attr = $ref->getMethod('foo')->getAttributes()[0]; + +try { + $attr->getArguments(); +} catch (\Error $e) { + var_dump('ERROR 1', $e->getMessage()); +} + echo "\n"; class C3 @@ -98,6 +108,8 @@ Array [0] => C2 [1] => bar ) +string(7) "ERROR 1" +string(36) "Undefined class constant 'self::FOO'" bool(true) string(3) "bar" diff --git a/ext/opcache/zend_file_cache.c b/ext/opcache/zend_file_cache.c index 3d03100fceea9..03271678c568a 100644 --- a/ext/opcache/zend_file_cache.c +++ b/ext/opcache/zend_file_cache.c @@ -411,13 +411,8 @@ static void zend_file_cache_serialize_attribute(zval *zv, attr = Z_PTR_P(zv); UNSERIALIZE_PTR(attr); - if (!IS_SERIALIZED(attr->name)) { - SERIALIZE_STR(attr->name); - } - - if (!IS_SERIALIZED(attr->lcname)) { - SERIALIZE_STR(attr->lcname); - } + SERIALIZE_STR(attr->name); + SERIALIZE_STR(attr->lcname); for (i = 0; i < attr->argc; i++) { zend_file_cache_serialize_zval(&attr->argv[i], script, info, buf); @@ -1179,13 +1174,8 @@ static void zend_file_cache_unserialize_attribute(zval *zv, zend_persistent_scri UNSERIALIZE_PTR(Z_PTR_P(zv)); attr = Z_PTR_P(zv); - if (!IS_UNSERIALIZED(attr->name)) { - UNSERIALIZE_STR(attr->name); - } - - if (!IS_UNSERIALIZED(attr->lcname)) { - UNSERIALIZE_STR(attr->lcname); - } + UNSERIALIZE_STR(attr->name); + UNSERIALIZE_STR(attr->lcname); for (i = 0; i < attr->argc; i++) { zend_file_cache_unserialize_zval(&attr->argv[i], script, buf); From 6256e247b5f43a5b4a3138a8b9372c9392eb7622 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Schr=C3=B6der?= Date: Thu, 4 Jun 2020 13:43:52 +0200 Subject: [PATCH 09/10] Refactored attributes to avoid ZEND API changes. --- Zend/zend_API.c | 16 ++--- Zend/zend_API.h | 6 +- Zend/zend_attributes.c | 75 ++++++++++++++---------- Zend/zend_attributes.h | 55 +++++++++++++++++- Zend/zend_compile.c | 119 +++++++++++++------------------------- Zend/zend_inheritance.c | 15 +++-- ext/tokenizer/tokenizer.c | 8 +-- ext/zend_test/test.c | 8 +-- ext/zip/php_zip.c | 2 +- 9 files changed, 167 insertions(+), 137 deletions(-) diff --git a/Zend/zend_API.c b/Zend/zend_API.c index b9bb8305acfbe..5d2590c6d5f84 100644 --- a/Zend/zend_API.c +++ b/Zend/zend_API.c @@ -3515,7 +3515,7 @@ static zend_always_inline zend_bool is_persistent_class(zend_class_entry *ce) { && ce->info.internal.module->type == MODULE_PERSISTENT; } -ZEND_API int zend_declare_typed_property(zend_class_entry *ce, zend_string *name, zval *property, int access_type, zend_string *doc_comment, HashTable *attributes, zend_type type) /* {{{ */ +ZEND_API int zend_declare_typed_property(zend_class_entry *ce, zend_string *name, zval *property, int access_type, zend_string *doc_comment, zend_type type) /* {{{ */ { zend_property_info *property_info, *property_info_ptr; @@ -3608,7 +3608,7 @@ ZEND_API int zend_declare_typed_property(zend_class_entry *ce, zend_string *name property_info->name = zend_new_interned_string(property_info->name); property_info->flags = access_type; property_info->doc_comment = doc_comment; - property_info->attributes = attributes; + property_info->attributes = NULL; property_info->ce = ce; property_info->type = type; @@ -3745,16 +3745,16 @@ ZEND_API int zend_try_assign_typed_ref_zval_ex(zend_reference *ref, zval *zv, ze } /* }}} */ -ZEND_API int zend_declare_property_ex(zend_class_entry *ce, zend_string *name, zval *property, int access_type, zend_string *doc_comment, HashTable *attributes) /* {{{ */ +ZEND_API int zend_declare_property_ex(zend_class_entry *ce, zend_string *name, zval *property, int access_type, zend_string *doc_comment) /* {{{ */ { - return zend_declare_typed_property(ce, name, property, access_type, doc_comment, attributes, (zend_type) ZEND_TYPE_INIT_NONE(0)); + return zend_declare_typed_property(ce, name, property, access_type, doc_comment, (zend_type) ZEND_TYPE_INIT_NONE(0)); } /* }}} */ ZEND_API int zend_declare_property(zend_class_entry *ce, const char *name, size_t name_length, zval *property, int access_type) /* {{{ */ { zend_string *key = zend_string_init(name, name_length, is_persistent_class(ce)); - int ret = zend_declare_property_ex(ce, key, property, access_type, NULL, NULL); + int ret = zend_declare_property_ex(ce, key, property, access_type, NULL); zend_string_release(key); return ret; } @@ -3814,7 +3814,7 @@ ZEND_API int zend_declare_property_stringl(zend_class_entry *ce, const char *nam } /* }}} */ -ZEND_API int zend_declare_class_constant_ex(zend_class_entry *ce, zend_string *name, zval *value, int access_type, zend_string *doc_comment, HashTable *attributes) /* {{{ */ +ZEND_API int zend_declare_class_constant_ex(zend_class_entry *ce, zend_string *name, zval *value, int access_type, zend_string *doc_comment) /* {{{ */ { zend_class_constant *c; @@ -3841,7 +3841,7 @@ ZEND_API int zend_declare_class_constant_ex(zend_class_entry *ce, zend_string *n ZVAL_COPY_VALUE(&c->value, value); Z_ACCESS_FLAGS(c->value) = access_type; c->doc_comment = doc_comment; - c->attributes = attributes; + c->attributes = NULL; c->ce = ce; if (Z_TYPE_P(value) == IS_CONSTANT_AST) { ce->ce_flags &= ~ZEND_ACC_CONSTANTS_UPDATED; @@ -3867,7 +3867,7 @@ ZEND_API int zend_declare_class_constant(zend_class_entry *ce, const char *name, } else { key = zend_string_init(name, name_length, 0); } - ret = zend_declare_class_constant_ex(ce, key, value, ZEND_ACC_PUBLIC, NULL, NULL); + ret = zend_declare_class_constant_ex(ce, key, value, ZEND_ACC_PUBLIC, NULL); zend_string_release(key); return ret; } diff --git a/Zend/zend_API.h b/Zend/zend_API.h index a8afac147bc50..44d43d46dedce 100644 --- a/Zend/zend_API.h +++ b/Zend/zend_API.h @@ -351,9 +351,9 @@ ZEND_API zend_bool zend_make_callable(zval *callable, zend_string **callable_nam ZEND_API const char *zend_get_module_version(const char *module_name); ZEND_API int zend_get_module_started(const char *module_name); -ZEND_API int zend_declare_typed_property(zend_class_entry *ce, zend_string *name, zval *property, int access_type, zend_string *doc_comment, HashTable *attributes, zend_type type); +ZEND_API int zend_declare_typed_property(zend_class_entry *ce, zend_string *name, zval *property, int access_type, zend_string *doc_comment, zend_type type); -ZEND_API int zend_declare_property_ex(zend_class_entry *ce, zend_string *name, zval *property, int access_type, zend_string *doc_comment, HashTable *attributes); +ZEND_API int zend_declare_property_ex(zend_class_entry *ce, zend_string *name, zval *property, int access_type, zend_string *doc_comment); ZEND_API int zend_declare_property(zend_class_entry *ce, const char *name, size_t name_length, zval *property, int access_type); ZEND_API int zend_declare_property_null(zend_class_entry *ce, const char *name, size_t name_length, int access_type); ZEND_API int zend_declare_property_bool(zend_class_entry *ce, const char *name, size_t name_length, zend_long value, int access_type); @@ -362,7 +362,7 @@ ZEND_API int zend_declare_property_double(zend_class_entry *ce, const char *name ZEND_API int zend_declare_property_string(zend_class_entry *ce, const char *name, size_t name_length, const char *value, int access_type); ZEND_API int zend_declare_property_stringl(zend_class_entry *ce, const char *name, size_t name_length, const char *value, size_t value_len, int access_type); -ZEND_API int zend_declare_class_constant_ex(zend_class_entry *ce, zend_string *name, zval *value, int access_type, zend_string *doc_comment, HashTable *attributes); +ZEND_API int zend_declare_class_constant_ex(zend_class_entry *ce, zend_string *name, zval *value, int access_type, zend_string *doc_comment); ZEND_API int zend_declare_class_constant(zend_class_entry *ce, const char *name, size_t name_length, zval *value); ZEND_API int zend_declare_class_constant_null(zend_class_entry *ce, const char *name, size_t name_length); ZEND_API int zend_declare_class_constant_long(zend_class_entry *ce, const char *name, size_t name_length, zend_long value); diff --git a/Zend/zend_attributes.c b/Zend/zend_attributes.c index 5e1d58593b786..6c39c910726c6 100644 --- a/Zend/zend_attributes.c +++ b/Zend/zend_attributes.c @@ -18,20 +18,6 @@ ZEND_API zend_attributes_internal_validator zend_attribute_get_validator(zend_st return zend_hash_find_ptr(&internal_validators, lcname); } -ZEND_API void zend_attribute_free(zend_attribute *attr, int persistent) -{ - uint32_t i; - - zend_string_release(attr->name); - zend_string_release(attr->lcname); - - for (i = 0; i < attr->argc; i++) { - zval_ptr_dtor(&attr->argv[i]); - } - - pefree(attr, persistent); -} - static zend_attribute *get_attribute(HashTable *attributes, zend_string *lcname, uint32_t offset) { if (attributes) { @@ -84,9 +70,52 @@ ZEND_API zend_attribute *zend_get_parameter_attribute_str(HashTable *attributes, return get_attribute_str(attributes, str, len, offset + 1); } -static void attribute_ptr_dtor(zval *v) +static zend_always_inline void free_attribute(zend_attribute *attr, int persistent) { - zend_attribute_free((zend_attribute *) Z_PTR_P(v), 1); + uint32_t i; + + zend_string_release(attr->name); + zend_string_release(attr->lcname); + + for (i = 0; i < attr->argc; i++) { + zval_ptr_dtor(&attr->argv[i]); + } + + pefree(attr, persistent); +} + +static void attr_free(zval *v) +{ + free_attribute((zend_attribute *) Z_PTR_P(v), 0); +} + +static void attr_pfree(zval *v) +{ + free_attribute((zend_attribute *) Z_PTR_P(v), 1); +} + +ZEND_API zend_attribute *zend_add_attribute(HashTable **attributes, zend_bool persistent, uint32_t offset, zend_string *name, uint32_t argc) +{ + if (*attributes == NULL) { + *attributes = pemalloc(sizeof(HashTable), persistent); + zend_hash_init(*attributes, 8, NULL, persistent ? attr_pfree : attr_free, persistent); + } + + zend_attribute *attr = pemalloc(ZEND_ATTRIBUTE_SIZE(argc), persistent); + + if (persistent == (zend_bool) (GC_FLAGS(name) & IS_STR_PERSISTENT)) { + attr->name = zend_string_copy(name); + } else { + attr->name = zend_string_dup(name, persistent); + } + + attr->lcname = zend_string_tolower_ex(attr->name, persistent); + attr->offset = offset; + attr->argc = argc; + + zend_hash_next_index_insert_ptr(*attributes, attr); + + return attr; } ZEND_API void zend_compiler_attribute_register(zend_class_entry *ce, zend_attributes_internal_validator validator) @@ -100,19 +129,7 @@ ZEND_API void zend_compiler_attribute_register(zend_class_entry *ce, zend_attrib zend_hash_update_ptr(&internal_validators, lcname, validator); zend_string_release(lcname); - if (ce->attributes == NULL) { - ce->attributes = pemalloc(sizeof(HashTable), 1); - zend_hash_init(ce->attributes, 8, NULL, attribute_ptr_dtor, 1); - } - - zend_attribute *attr = pemalloc(ZEND_ATTRIBUTE_SIZE(0), 1); - - attr->name = zend_string_copy(zend_ce_php_attribute->name); - attr->lcname = zend_string_tolower_ex(attr->name, 1); - attr->offset = 0; - attr->argc = 0; - - zend_hash_next_index_insert_ptr(ce->attributes, attr); + zend_add_class_attribute(ce, zend_ce_php_attribute->name, 0); } void zend_register_attribute_ce(void) diff --git a/Zend/zend_attributes.h b/Zend/zend_attributes.h index 34b11382493ec..140783c51c2ea 100644 --- a/Zend/zend_attributes.h +++ b/Zend/zend_attributes.h @@ -26,8 +26,6 @@ typedef struct _zend_attribute { typedef void (*zend_attributes_internal_validator)(zend_attribute *attr, int target); -ZEND_API void zend_attribute_free(zend_attribute *attr, int persistent); - ZEND_API zend_attribute *zend_get_attribute(HashTable *attributes, zend_string *lcname); ZEND_API zend_attribute *zend_get_attribute_str(HashTable *attributes, const char *str, size_t len); @@ -37,8 +35,59 @@ ZEND_API zend_attribute *zend_get_parameter_attribute_str(HashTable *attributes, ZEND_API void zend_compiler_attribute_register(zend_class_entry *ce, zend_attributes_internal_validator validator); ZEND_API zend_attributes_internal_validator zend_attribute_get_validator(zend_string *lcname); -void zend_register_attribute_ce(void); +ZEND_API zend_attribute *zend_add_attribute(HashTable **attributes, zend_bool persistent, uint32_t offset, zend_string *name, uint32_t argc); END_EXTERN_C() +static zend_always_inline zend_attribute *zend_add_class_attribute(zend_class_entry *ce, zend_string *name, uint32_t argc) +{ + return zend_add_attribute(&ce->attributes, ce->type != ZEND_USER_CLASS, 0, name, argc); +} + +static zend_always_inline zend_attribute *zend_add_function_attribute(zend_function *func, zend_string *name, uint32_t argc) +{ + return zend_add_attribute(&func->common.attributes, func->common.type != ZEND_USER_FUNCTION, 0, name, argc); +} + +static zend_always_inline zend_attribute *zend_add_parameter_attribute(zend_function *func, uint32_t offset, zend_string *name, uint32_t argc) +{ + return zend_add_attribute(&func->common.attributes, func->common.type != ZEND_USER_FUNCTION, offset + 1, name, argc); +} + +static zend_always_inline zend_attribute *zend_add_property_attribute(zend_class_entry *ce, zend_property_info *info, zend_string *name, uint32_t argc) +{ + return zend_add_attribute(&info->attributes, ce->type != ZEND_USER_CLASS, 0, name, argc); +} + +static zend_always_inline zend_attribute *zend_add_property_attribute_str(zend_class_entry *ce, const char *key, size_t len, zend_string *name, uint32_t argc) +{ + zend_property_info *info = (zend_property_info *) zend_hash_str_find_ptr(&ce->properties_info, key, len); + + if (info == NULL) { + zend_error_noreturn(E_ERROR, "Property '%.*s' not found in class '%s'", (int) len, key, ZSTR_VAL(ce->name)); + return NULL; + } + + return zend_add_attribute(&info->attributes, ce->type != ZEND_USER_CLASS, 0, name, argc); +} + +static zend_always_inline zend_attribute *zend_add_class_constant_attribute(zend_class_entry *ce, zend_class_constant *c, zend_string *name, uint32_t argc) +{ + return zend_add_attribute(&c->attributes, ce->type != ZEND_USER_CLASS, 0, name, argc); +} + +static zend_always_inline zend_attribute *zend_add_class_constant_attribute_str(zend_class_entry *ce, const char *key, size_t len, zend_string *name, uint32_t argc) +{ + zend_class_constant *c = (zend_class_constant *) zend_hash_str_find_ptr(&ce->constants_table, key, len); + + if (c == NULL) { + zend_error_noreturn(E_ERROR, "Class constant '%.*s' not found in class '%s'", (int) len, key, ZSTR_VAL(ce->name)); + return NULL; + } + + return zend_add_attribute(&c->attributes, ce->type != ZEND_USER_CLASS, 0, name, argc); +} + +void zend_register_attribute_ce(void); + #endif diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index caf6176b561d6..6e7831fe803f1 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -5718,58 +5718,29 @@ static zend_bool zend_is_valid_default_value(zend_type type, zval *value) return 0; } -static zend_attribute *zend_compile_attribute(zend_ast *ast, uint32_t offset) /* {{{ */ +static void zend_compile_attributes(HashTable **attributes, zend_ast *ast, uint32_t offset, int target) /* {{{ */ { - ZEND_ASSERT(ast->kind == ZEND_AST_ATTRIBUTE); + zend_ast_list *list = zend_ast_get_list(ast); + uint32_t i, j; - zend_ast_list *list = ast->child[1] ? zend_ast_get_list(ast->child[1]) : NULL; - zend_attribute *attr = emalloc(ZEND_ATTRIBUTE_SIZE(list ? list->children : 0)); + ZEND_ASSERT(ast->kind == ZEND_AST_ATTRIBUTE_LIST); - attr->name = zend_resolve_class_name_ast(ast->child[0]); - attr->lcname = zend_string_tolower(attr->name); - attr->offset = offset; - attr->argc = list ? list->children : 0; + for (i = 0; i < list->children; i++) { + zend_ast *el = list->child[i]; + zend_string *name = zend_resolve_class_name_ast(el->child[0]); + zend_ast_list *args = el->child[1] ? zend_ast_get_list(el->child[1]) : NULL; - if (list) { - ZEND_ASSERT(ast->child[1]->kind == ZEND_AST_ARG_LIST); + zend_attribute *attr = zend_add_attribute(attributes, 0, offset, name, args ? args->children : 0); + zend_string_release(name); - uint32_t i; + // Populate arguments + if (args) { + ZEND_ASSERT(args->kind == ZEND_AST_ARG_LIST); - for (i = 0; i < list->children; i++) { - zend_const_expr_to_zval(&attr->argv[i], list->child[i]); + for (j = 0; j < args->children; j++) { + zend_const_expr_to_zval(&attr->argv[j], args->child[j]); + } } - } - - return attr; -} -/* }}} */ - -static void attribute_ptr_dtor(zval *v) /* {{{ */ -{ - zend_attribute_free((zend_attribute *) Z_PTR_P(v), 0); -} -/* }}} */ - -static zend_always_inline HashTable *create_attribute_array() /* {{{ */ -{ - HashTable *attributes; - - ALLOC_HASHTABLE(attributes); - zend_hash_init(attributes, 8, NULL, attribute_ptr_dtor, 0); - - return attributes; -} -/* }}} */ - -static void zend_compile_attributes(HashTable *attributes, zend_ast *ast, uint32_t offset, int target) /* {{{ */ -{ - zend_ast_list *list = zend_ast_get_list(ast); - uint32_t i; - - ZEND_ASSERT(ast->kind == ZEND_AST_ATTRIBUTE_LIST); - - for (i = 0; i < list->children; i++) { - zend_attribute *attr = zend_compile_attribute(list->child[i], offset); // Validate internal attribute zend_attributes_internal_validator validator = zend_attribute_get_validator(attr->lcname); @@ -5777,8 +5748,6 @@ static void zend_compile_attributes(HashTable *attributes, zend_ast *ast, uint32 if (validator != NULL) { validator(attr, target); } - - zend_hash_next_index_insert_ptr(attributes, attr); } } /* }}} */ @@ -5888,11 +5857,7 @@ void zend_compile_params(zend_ast *ast, zend_ast *return_type_ast, uint32_t fall arg_info->type = (zend_type) ZEND_TYPE_INIT_NONE(0); if (attributes_ast) { - if (!op_array->attributes) { - op_array->attributes = create_attribute_array(); - } - - zend_compile_attributes(op_array->attributes, attributes_ast, i + 1, ZEND_ATTRIBUTE_TARGET_PARAMETER); + zend_compile_attributes(&op_array->attributes, attributes_ast, i + 1, ZEND_ATTRIBUTE_TARGET_PARAMETER); } if (type_ast) { @@ -6393,8 +6358,7 @@ void zend_compile_func_decl(znode *result, zend_ast *ast, zend_bool toplevel) /* target = ZEND_ATTRIBUTE_TARGET_METHOD; } - op_array->attributes = create_attribute_array(); - zend_compile_attributes(op_array->attributes, decl->child[4], 0, target); + zend_compile_attributes(&op_array->attributes, decl->child[4], 0, target); } /* Do not leak the class scope into free standing functions, even if they are dynamically @@ -6460,7 +6424,7 @@ void zend_compile_func_decl(znode *result, zend_ast *ast, zend_bool toplevel) /* } /* }}} */ -void zend_compile_prop_decl(zend_ast *ast, zend_ast *type_ast, uint32_t flags, HashTable *attributes) /* {{{ */ +void zend_compile_prop_decl(zend_ast *ast, zend_ast *type_ast, uint32_t flags, zend_ast *attr_ast) /* {{{ */ { zend_ast_list *list = zend_ast_get_list(ast); zend_class_entry *ce = CG(active_class_entry); @@ -6535,29 +6499,28 @@ void zend_compile_prop_decl(zend_ast *ast, zend_ast *type_ast, uint32_t flags, H ZVAL_UNDEF(&value_zv); } - zend_declare_typed_property(ce, name, &value_zv, flags, doc_comment, attributes, type); + zend_declare_typed_property(ce, name, &value_zv, flags, doc_comment, type); + + if (attr_ast) { + zend_property_info *info = (zend_property_info *) zend_hash_find_ptr(&ce->properties_info, name); + zend_compile_attributes(&info->attributes, attr_ast, 0, ZEND_ATTRIBUTE_TARGET_PROPERTY); + } } } /* }}} */ void zend_compile_prop_group(zend_ast *list) /* {{{ */ { - HashTable *attributes = NULL; - zend_ast *type_ast = list->child[0]; zend_ast *prop_ast = list->child[1]; + zend_ast *attr_ast = list->child[2]; - if (list->child[2]) { - if (zend_ast_get_list(prop_ast)->children > 1) { - zend_error_noreturn(E_COMPILE_ERROR, "Cannot apply attributes to a group of properties"); - return; - } - - attributes = create_attribute_array(); - zend_compile_attributes(attributes, list->child[2], 0, ZEND_ATTRIBUTE_TARGET_PROPERTY); + if (attr_ast && zend_ast_get_list(prop_ast)->children > 1) { + zend_error_noreturn(E_COMPILE_ERROR, "Cannot apply attributes to a group of properties"); + return; } - zend_compile_prop_decl(prop_ast, type_ast, list->attr, attributes); + zend_compile_prop_decl(prop_ast, type_ast, list->attr, attr_ast); } /* }}} */ @@ -6577,7 +6540,6 @@ void zend_compile_class_const_decl(zend_ast *ast, zend_ast *attr_ast) /* {{{ */ { zend_ast_list *list = zend_ast_get_list(ast); zend_class_entry *ce = CG(active_class_entry); - HashTable *attributes = NULL; uint32_t i; if ((ce->ce_flags & ZEND_ACC_TRAIT) != 0) { @@ -6585,14 +6547,9 @@ void zend_compile_class_const_decl(zend_ast *ast, zend_ast *attr_ast) /* {{{ */ return; } - if (attr_ast) { - if (list->children > 1) { - zend_error_noreturn(E_COMPILE_ERROR, "Cannot apply attributes to a group of constants"); - return; - } - - attributes = create_attribute_array(); - zend_compile_attributes(attributes, attr_ast, 0, ZEND_ATTRIBUTE_TARGET_CLASS_CONST); + if (attr_ast && list->children > 1) { + zend_error_noreturn(E_COMPILE_ERROR, "Cannot apply attributes to a group of constants"); + return; } for (i = 0; i < list->children; ++i) { @@ -6609,7 +6566,12 @@ void zend_compile_class_const_decl(zend_ast *ast, zend_ast *attr_ast) /* {{{ */ } zend_const_expr_to_zval(&value_zv, value_ast); - zend_declare_class_constant_ex(ce, name, &value_zv, ast->attr, doc_comment, attributes); + zend_declare_class_constant_ex(ce, name, &value_zv, ast->attr, doc_comment); + + if (attr_ast) { + zend_class_constant *c = (zend_class_constant *) zend_hash_find_ptr(&ce->constants_table, name); + zend_compile_attributes(&c->attributes, attr_ast, 0, ZEND_ATTRIBUTE_TARGET_CLASS_CONST); + } } } /* }}} */ @@ -6837,8 +6799,7 @@ void zend_compile_class_decl(znode *result, zend_ast *ast, zend_bool toplevel) / CG(active_class_entry) = ce; if (decl->child[4]) { - ce->attributes = create_attribute_array(); - zend_compile_attributes(ce->attributes, decl->child[4], 0, ZEND_ATTRIBUTE_TARGET_CLASS); + zend_compile_attributes(&ce->attributes, decl->child[4], 0, ZEND_ATTRIBUTE_TARGET_CLASS); } if (implements_ast) { diff --git a/Zend/zend_inheritance.c b/Zend/zend_inheritance.c index da8a94252172a..0aaddd449f38c 100644 --- a/Zend/zend_inheritance.c +++ b/Zend/zend_inheritance.c @@ -1973,7 +1973,6 @@ static void zend_do_traits_property_binding(zend_class_entry *ce, zend_class_ent zval* prop_value; uint32_t flags; zend_string *doc_comment; - HashTable *attributes = NULL; /* In the following steps the properties are inserted into the property table * for that, a very strict approach is applied: @@ -2073,15 +2072,19 @@ static void zend_do_traits_property_binding(zend_class_entry *ce, zend_class_ent Z_TRY_ADDREF_P(prop_value); doc_comment = property_info->doc_comment ? zend_string_copy(property_info->doc_comment) : NULL; + + zend_type_copy_ctor(&property_info->type, /* persistent */ 0); + zend_declare_typed_property(ce, prop_name, prop_value, flags, doc_comment, property_info->type); + if (property_info->attributes) { - attributes = property_info->attributes; + zend_property_info *info = (zend_property_info *) zend_hash_find_ptr(&ce->properties_info, prop_name); + info->attributes = property_info->attributes; - if (!(GC_FLAGS(attributes) & IS_ARRAY_IMMUTABLE)) { - GC_ADDREF(attributes); + if (!(GC_FLAGS(info->attributes) & IS_ARRAY_IMMUTABLE)) { + GC_ADDREF(info->attributes); } } - zend_type_copy_ctor(&property_info->type, /* persistent */ 0); - zend_declare_typed_property(ce, prop_name, prop_value, flags, doc_comment, attributes, property_info->type); + zend_string_release_ex(prop_name, 0); } ZEND_HASH_FOREACH_END(); } diff --git a/ext/tokenizer/tokenizer.c b/ext/tokenizer/tokenizer.c index 012a39f2d9c91..db573232233a9 100644 --- a/ext/tokenizer/tokenizer.c +++ b/ext/tokenizer/tokenizer.c @@ -266,22 +266,22 @@ PHP_MINIT_FUNCTION(tokenizer) zend_class_implements(php_token_ce, 1, zend_ce_stringable); name = zend_string_init("id", sizeof("id") - 1, 1); - zend_declare_typed_property(php_token_ce, name, &default_val, ZEND_ACC_PUBLIC, NULL, NULL, + zend_declare_typed_property(php_token_ce, name, &default_val, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); zend_string_release(name); name = zend_string_init("text", sizeof("text") - 1, 1); - zend_declare_typed_property(php_token_ce, name, &default_val, ZEND_ACC_PUBLIC, NULL, NULL, + zend_declare_typed_property(php_token_ce, name, &default_val, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING)); zend_string_release(name); name = zend_string_init("line", sizeof("line") - 1, 1); - zend_declare_typed_property(php_token_ce, name, &default_val, ZEND_ACC_PUBLIC, NULL, NULL, + zend_declare_typed_property(php_token_ce, name, &default_val, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); zend_string_release(name); name = zend_string_init("pos", sizeof("pos") - 1, 1); - zend_declare_typed_property(php_token_ce, name, &default_val, ZEND_ACC_PUBLIC, NULL, NULL, + zend_declare_typed_property(php_token_ce, name, &default_val, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); zend_string_release(name); diff --git a/ext/zend_test/test.c b/ext/zend_test/test.c index 726cc14c041a3..69b6196ab6622 100644 --- a/ext/zend_test/test.c +++ b/ext/zend_test/test.c @@ -226,7 +226,7 @@ PHP_MINIT_FUNCTION(zend_test) zval val; ZVAL_LONG(&val, 123); zend_declare_typed_property( - zend_test_class, name, &val, ZEND_ACC_PUBLIC, NULL, NULL, + zend_test_class, name, &val, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CODE(IS_LONG, 0, 0)); zend_string_release(name); } @@ -237,7 +237,7 @@ PHP_MINIT_FUNCTION(zend_test) zval val; ZVAL_NULL(&val); zend_declare_typed_property( - zend_test_class, name, &val, ZEND_ACC_PUBLIC, NULL, NULL, + zend_test_class, name, &val, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CLASS(class_name, 1, 0)); zend_string_release(name); } @@ -253,7 +253,7 @@ PHP_MINIT_FUNCTION(zend_test) zend_type type = ZEND_TYPE_INIT_PTR(type_list, _ZEND_TYPE_LIST_BIT, 1, 0); zval val; ZVAL_NULL(&val); - zend_declare_typed_property(zend_test_class, name, &val, ZEND_ACC_PUBLIC, NULL, NULL, type); + zend_declare_typed_property(zend_test_class, name, &val, ZEND_ACC_PUBLIC, NULL, type); zend_string_release(name); } @@ -262,7 +262,7 @@ PHP_MINIT_FUNCTION(zend_test) zval val; ZVAL_LONG(&val, 123); zend_declare_typed_property( - zend_test_class, name, &val, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC, NULL, NULL, + zend_test_class, name, &val, ZEND_ACC_PUBLIC | ZEND_ACC_STATIC, NULL, (zend_type) ZEND_TYPE_INIT_CODE(IS_LONG, 0, 0)); zend_string_release(name); } diff --git a/ext/zip/php_zip.c b/ext/zip/php_zip.c index 9e8160742a417..f131966c1ffaa 100644 --- a/ext/zip/php_zip.c +++ b/ext/zip/php_zip.c @@ -819,7 +819,7 @@ static void php_zip_register_prop_handler(HashTable *prop_handler, char *name, z /* Register for reflection */ ZVAL_NULL(&tmp); - zend_declare_property_ex(zip_class_entry, str, &tmp, ZEND_ACC_PUBLIC, NULL, NULL); + zend_declare_property_ex(zip_class_entry, str, &tmp, ZEND_ACC_PUBLIC, NULL); zend_string_release_ex(str, 1); } /* }}} */ From 9e15b993a3ab0db110d7c02d531ffaa46f280da7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Schr=C3=B6der?= Date: Thu, 4 Jun 2020 14:23:01 +0200 Subject: [PATCH 10/10] Addressed Review Comments. --- Zend/zend_API.c | 17 ++++++++--------- Zend/zend_API.h | 4 ++-- Zend/zend_attributes.c | 2 +- Zend/zend_attributes.h | 24 ------------------------ Zend/zend_compile.c | 8 ++++---- Zend/zend_inheritance.c | 10 +++++----- 6 files changed, 20 insertions(+), 45 deletions(-) diff --git a/Zend/zend_API.c b/Zend/zend_API.c index 5d2590c6d5f84..f65937e394322 100644 --- a/Zend/zend_API.c +++ b/Zend/zend_API.c @@ -3515,7 +3515,7 @@ static zend_always_inline zend_bool is_persistent_class(zend_class_entry *ce) { && ce->info.internal.module->type == MODULE_PERSISTENT; } -ZEND_API int zend_declare_typed_property(zend_class_entry *ce, zend_string *name, zval *property, int access_type, zend_string *doc_comment, zend_type type) /* {{{ */ +ZEND_API zend_property_info *zend_declare_typed_property(zend_class_entry *ce, zend_string *name, zval *property, int access_type, zend_string *doc_comment, zend_type type) /* {{{ */ { zend_property_info *property_info, *property_info_ptr; @@ -3614,7 +3614,7 @@ ZEND_API int zend_declare_typed_property(zend_class_entry *ce, zend_string *name zend_hash_update_ptr(&ce->properties_info, name, property_info); - return SUCCESS; + return property_info; } /* }}} */ @@ -3747,7 +3747,8 @@ ZEND_API int zend_try_assign_typed_ref_zval_ex(zend_reference *ref, zval *zv, ze ZEND_API int zend_declare_property_ex(zend_class_entry *ce, zend_string *name, zval *property, int access_type, zend_string *doc_comment) /* {{{ */ { - return zend_declare_typed_property(ce, name, property, access_type, doc_comment, (zend_type) ZEND_TYPE_INIT_NONE(0)); + zend_declare_typed_property(ce, name, property, access_type, doc_comment, (zend_type) ZEND_TYPE_INIT_NONE(0)); + return SUCCESS; } /* }}} */ @@ -3814,7 +3815,7 @@ ZEND_API int zend_declare_property_stringl(zend_class_entry *ce, const char *nam } /* }}} */ -ZEND_API int zend_declare_class_constant_ex(zend_class_entry *ce, zend_string *name, zval *value, int access_type, zend_string *doc_comment) /* {{{ */ +ZEND_API zend_class_constant *zend_declare_class_constant_ex(zend_class_entry *ce, zend_string *name, zval *value, int access_type, zend_string *doc_comment) /* {{{ */ { zend_class_constant *c; @@ -3852,14 +3853,12 @@ ZEND_API int zend_declare_class_constant_ex(zend_class_entry *ce, zend_string *n "Cannot redefine class constant %s::%s", ZSTR_VAL(ce->name), ZSTR_VAL(name)); } - return SUCCESS; + return c; } /* }}} */ ZEND_API int zend_declare_class_constant(zend_class_entry *ce, const char *name, size_t name_length, zval *value) /* {{{ */ { - int ret; - zend_string *key; if (ce->type == ZEND_INTERNAL_CLASS) { @@ -3867,9 +3866,9 @@ ZEND_API int zend_declare_class_constant(zend_class_entry *ce, const char *name, } else { key = zend_string_init(name, name_length, 0); } - ret = zend_declare_class_constant_ex(ce, key, value, ZEND_ACC_PUBLIC, NULL); + zend_declare_class_constant_ex(ce, key, value, ZEND_ACC_PUBLIC, NULL); zend_string_release(key); - return ret; + return SUCCESS; } /* }}} */ diff --git a/Zend/zend_API.h b/Zend/zend_API.h index 44d43d46dedce..d7e5e528a0940 100644 --- a/Zend/zend_API.h +++ b/Zend/zend_API.h @@ -351,7 +351,7 @@ ZEND_API zend_bool zend_make_callable(zval *callable, zend_string **callable_nam ZEND_API const char *zend_get_module_version(const char *module_name); ZEND_API int zend_get_module_started(const char *module_name); -ZEND_API int zend_declare_typed_property(zend_class_entry *ce, zend_string *name, zval *property, int access_type, zend_string *doc_comment, zend_type type); +ZEND_API zend_property_info *zend_declare_typed_property(zend_class_entry *ce, zend_string *name, zval *property, int access_type, zend_string *doc_comment, zend_type type); ZEND_API int zend_declare_property_ex(zend_class_entry *ce, zend_string *name, zval *property, int access_type, zend_string *doc_comment); ZEND_API int zend_declare_property(zend_class_entry *ce, const char *name, size_t name_length, zval *property, int access_type); @@ -362,7 +362,7 @@ ZEND_API int zend_declare_property_double(zend_class_entry *ce, const char *name ZEND_API int zend_declare_property_string(zend_class_entry *ce, const char *name, size_t name_length, const char *value, int access_type); ZEND_API int zend_declare_property_stringl(zend_class_entry *ce, const char *name, size_t name_length, const char *value, size_t value_len, int access_type); -ZEND_API int zend_declare_class_constant_ex(zend_class_entry *ce, zend_string *name, zval *value, int access_type, zend_string *doc_comment); +ZEND_API zend_class_constant *zend_declare_class_constant_ex(zend_class_entry *ce, zend_string *name, zval *value, int access_type, zend_string *doc_comment); ZEND_API int zend_declare_class_constant(zend_class_entry *ce, const char *name, size_t name_length, zval *value); ZEND_API int zend_declare_class_constant_null(zend_class_entry *ce, const char *name, size_t name_length); ZEND_API int zend_declare_class_constant_long(zend_class_entry *ce, const char *name, size_t name_length, zend_long value); diff --git a/Zend/zend_attributes.c b/Zend/zend_attributes.c index 6c39c910726c6..12a997e72f594 100644 --- a/Zend/zend_attributes.c +++ b/Zend/zend_attributes.c @@ -103,7 +103,7 @@ ZEND_API zend_attribute *zend_add_attribute(HashTable **attributes, zend_bool pe zend_attribute *attr = pemalloc(ZEND_ATTRIBUTE_SIZE(argc), persistent); - if (persistent == (zend_bool) (GC_FLAGS(name) & IS_STR_PERSISTENT)) { + if (persistent == ((GC_FLAGS(name) & IS_STR_PERSISTENT) != 0)) { attr->name = zend_string_copy(name); } else { attr->name = zend_string_dup(name, persistent); diff --git a/Zend/zend_attributes.h b/Zend/zend_attributes.h index 140783c51c2ea..d7e54173cac67 100644 --- a/Zend/zend_attributes.h +++ b/Zend/zend_attributes.h @@ -59,35 +59,11 @@ static zend_always_inline zend_attribute *zend_add_property_attribute(zend_class return zend_add_attribute(&info->attributes, ce->type != ZEND_USER_CLASS, 0, name, argc); } -static zend_always_inline zend_attribute *zend_add_property_attribute_str(zend_class_entry *ce, const char *key, size_t len, zend_string *name, uint32_t argc) -{ - zend_property_info *info = (zend_property_info *) zend_hash_str_find_ptr(&ce->properties_info, key, len); - - if (info == NULL) { - zend_error_noreturn(E_ERROR, "Property '%.*s' not found in class '%s'", (int) len, key, ZSTR_VAL(ce->name)); - return NULL; - } - - return zend_add_attribute(&info->attributes, ce->type != ZEND_USER_CLASS, 0, name, argc); -} - static zend_always_inline zend_attribute *zend_add_class_constant_attribute(zend_class_entry *ce, zend_class_constant *c, zend_string *name, uint32_t argc) { return zend_add_attribute(&c->attributes, ce->type != ZEND_USER_CLASS, 0, name, argc); } -static zend_always_inline zend_attribute *zend_add_class_constant_attribute_str(zend_class_entry *ce, const char *key, size_t len, zend_string *name, uint32_t argc) -{ - zend_class_constant *c = (zend_class_constant *) zend_hash_str_find_ptr(&ce->constants_table, key, len); - - if (c == NULL) { - zend_error_noreturn(E_ERROR, "Class constant '%.*s' not found in class '%s'", (int) len, key, ZSTR_VAL(ce->name)); - return NULL; - } - - return zend_add_attribute(&c->attributes, ce->type != ZEND_USER_CLASS, 0, name, argc); -} - void zend_register_attribute_ce(void); #endif diff --git a/Zend/zend_compile.c b/Zend/zend_compile.c index 6e7831fe803f1..707d2ac9da319 100644 --- a/Zend/zend_compile.c +++ b/Zend/zend_compile.c @@ -6439,6 +6439,7 @@ void zend_compile_prop_decl(zend_ast *ast, zend_ast *type_ast, uint32_t flags, z } for (i = 0; i < children; ++i) { + zend_property_info *info; zend_ast *prop_ast = list->child[i]; zend_ast *name_ast = prop_ast->child[0]; zend_ast *value_ast = prop_ast->child[1]; @@ -6499,10 +6500,9 @@ void zend_compile_prop_decl(zend_ast *ast, zend_ast *type_ast, uint32_t flags, z ZVAL_UNDEF(&value_zv); } - zend_declare_typed_property(ce, name, &value_zv, flags, doc_comment, type); + info = zend_declare_typed_property(ce, name, &value_zv, flags, doc_comment, type); if (attr_ast) { - zend_property_info *info = (zend_property_info *) zend_hash_find_ptr(&ce->properties_info, name); zend_compile_attributes(&info->attributes, attr_ast, 0, ZEND_ATTRIBUTE_TARGET_PROPERTY); } } @@ -6553,6 +6553,7 @@ void zend_compile_class_const_decl(zend_ast *ast, zend_ast *attr_ast) /* {{{ */ } for (i = 0; i < list->children; ++i) { + zend_class_constant *c; zend_ast *const_ast = list->child[i]; zend_ast *name_ast = const_ast->child[0]; zend_ast *value_ast = const_ast->child[1]; @@ -6566,10 +6567,9 @@ void zend_compile_class_const_decl(zend_ast *ast, zend_ast *attr_ast) /* {{{ */ } zend_const_expr_to_zval(&value_zv, value_ast); - zend_declare_class_constant_ex(ce, name, &value_zv, ast->attr, doc_comment); + c = zend_declare_class_constant_ex(ce, name, &value_zv, ast->attr, doc_comment); if (attr_ast) { - zend_class_constant *c = (zend_class_constant *) zend_hash_find_ptr(&ce->constants_table, name); zend_compile_attributes(&c->attributes, attr_ast, 0, ZEND_ATTRIBUTE_TARGET_CLASS_CONST); } } diff --git a/Zend/zend_inheritance.c b/Zend/zend_inheritance.c index 0aaddd449f38c..26a2bc4da5d6c 100644 --- a/Zend/zend_inheritance.c +++ b/Zend/zend_inheritance.c @@ -1967,6 +1967,7 @@ static void zend_do_traits_property_binding(zend_class_entry *ce, zend_class_ent size_t i; zend_property_info *property_info; zend_property_info *coliding_prop; + zend_property_info *new_prop; zend_string* prop_name; const char* class_name_unused; zend_bool not_compatible; @@ -2074,14 +2075,13 @@ static void zend_do_traits_property_binding(zend_class_entry *ce, zend_class_ent doc_comment = property_info->doc_comment ? zend_string_copy(property_info->doc_comment) : NULL; zend_type_copy_ctor(&property_info->type, /* persistent */ 0); - zend_declare_typed_property(ce, prop_name, prop_value, flags, doc_comment, property_info->type); + new_prop = zend_declare_typed_property(ce, prop_name, prop_value, flags, doc_comment, property_info->type); if (property_info->attributes) { - zend_property_info *info = (zend_property_info *) zend_hash_find_ptr(&ce->properties_info, prop_name); - info->attributes = property_info->attributes; + new_prop->attributes = property_info->attributes; - if (!(GC_FLAGS(info->attributes) & IS_ARRAY_IMMUTABLE)) { - GC_ADDREF(info->attributes); + if (!(GC_FLAGS(new_prop->attributes) & IS_ARRAY_IMMUTABLE)) { + GC_ADDREF(new_prop->attributes); } }