]> granicus.if.org Git - php/commitdiff
Add Attributes
authorBenjamin Eberlei <kontakt@beberlei.de>
Sun, 24 May 2020 18:57:00 +0000 (20:57 +0200)
committerBenjamin Eberlei <kontakt@beberlei.de>
Thu, 4 Jun 2020 16:19:49 +0000 (18:19 +0200)
Co-authored-by: Martin Schröder <m.schroeder2007@gmail.com>
44 files changed:
Zend/tests/attributes/001_placement.phpt [new file with mode: 0644]
Zend/tests/attributes/002_rfcexample.phpt [new file with mode: 0644]
Zend/tests/attributes/003_ast_nodes.phpt [new file with mode: 0644]
Zend/tests/attributes/004_name_resolution.phpt [new file with mode: 0644]
Zend/tests/attributes/005_objects.phpt [new file with mode: 0644]
Zend/tests/attributes/006_filter.phpt [new file with mode: 0644]
Zend/tests/attributes/007_self_reflect_attribute.phpt [new file with mode: 0644]
Zend/tests/attributes/008_wrong_attribution.phpt [new file with mode: 0644]
Zend/tests/attributes/009_doctrine_annotations_example.phpt [new file with mode: 0644]
Zend/tests/attributes/010_unsupported_const_expression.phpt [new file with mode: 0644]
Zend/tests/attributes/011-inheritance.phpt [new file with mode: 0644]
Zend/tests/attributes/012-ast-export.phpt [new file with mode: 0644]
Zend/tests/attributes/013_class_scope.phpt [new file with mode: 0644]
Zend/tests/attributes/014_class_const_group.phpt [new file with mode: 0644]
Zend/tests/attributes/015_property_group.phpt [new file with mode: 0644]
Zend/tests/attributes/016_target_resolution_compiler_attributes.phpt [new file with mode: 0644]
Zend/tests/attributes/017_closure_scope.phpt [new file with mode: 0644]
Zend/tests/varSyntax/globalNonSimpleVariableError.phpt
Zend/zend.h
Zend/zend_API.c
Zend/zend_API.h
Zend/zend_ast.c
Zend/zend_ast.h
Zend/zend_attributes.c [new file with mode: 0644]
Zend/zend_attributes.h [new file with mode: 0644]
Zend/zend_compile.c
Zend/zend_compile.h
Zend/zend_default_classes.c
Zend/zend_execute.c
Zend/zend_inheritance.c
Zend/zend_language_parser.y
Zend/zend_opcode.c
configure.ac
ext/opcache/zend_file_cache.c
ext/opcache/zend_persist.c
ext/opcache/zend_persist_calc.c
ext/reflection/php_reflection.c
ext/reflection/php_reflection.h
ext/reflection/php_reflection.stub.php
ext/reflection/php_reflection_arginfo.h
ext/reflection/tests/ReflectionClass_toString_001.phpt
ext/reflection/tests/ReflectionExtension_getClasses_basic.phpt
ext/zend_test/test.c
win32/build/config.w32

diff --git a/Zend/tests/attributes/001_placement.phpt b/Zend/tests/attributes/001_placement.phpt
new file mode 100644 (file)
index 0000000..cf7bcd4
--- /dev/null
@@ -0,0 +1,134 @@
+--TEST--
+Attributes can be placed on all supported elements.
+--FILE--
+<?php
+
+<<A1(1)>>
+class Foo
+{
+    <<A1(2)>>
+    public const FOO = 'foo';
+    
+    <<A1(3)>>
+    public $x;
+    
+    <<A1(4)>>
+    public function foo(<<A1(5)>> $a, <<A1(6)>> $b) { }
+}
+
+$object = new <<A1(7)>> class () { };
+
+<<A1(8)>>
+function f1() { }
+
+$f2 = <<A1(9)>> function () { };
+
+$f3 = <<A1(10)>> 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 (file)
index 0000000..0d38798
--- /dev/null
@@ -0,0 +1,43 @@
+--TEST--
+Attributes: Example from Attributes RFC
+--FILE--
+<?php
+// https://wiki.php.net/rfc/attributes_v2#attribute_syntax
+namespace My\Attributes {
+    use PhpAttribute;
+
+    <<PhpAttribute>>
+    class SingleArgument {
+        public $argumentValue;
+
+        public function __construct($argumentValue) {
+             $this->argumentValue = $argumentValue;
+        }
+    }
+}
+
+namespace {
+    use My\Attributes\SingleArgument;
+
+    <<SingleArgument("Hello World")>>
+    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 (file)
index 0000000..cf43e66
--- /dev/null
@@ -0,0 +1,109 @@
+--TEST--
+Attributes can deal with AST nodes.
+--FILE--
+<?php
+
+define('V1', strtoupper(php_sapi_name()));
+
+<<A1([V1 => 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";
+
+<<A1(V1, 1 + 2, C1::class)>>
+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";
+
+<<A1(self::FOO, C1::BAR)>>
+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";
+
+<<ExampleWithShift(4 >> 1)>>
+class C4 {}
+$ref = new \ReflectionClass(C4::class);
+var_dump($ref->getAttributes()[0]->getArguments());
+
+echo "\n";
+
+<<PhpAttribute>>
+class C5
+{
+       public function __construct() { }
+}
+
+$ref = new \ReflectionFunction(<<C5(MissingClass::SOME_CONST)>> 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 (file)
index 0000000..1ee1f5f
--- /dev/null
@@ -0,0 +1,93 @@
+--TEST--
+Resolve attribute names
+--FILE--
+<?php
+function dump_attributes($attributes) {
+    $arr = [];
+    foreach ($attributes as $attribute) {
+        $arr[] = ['name' => $attribute->getName(), 'args' => $attribute->getArguments()];
+    }
+    var_dump($arr);
+}
+
+namespace Doctrine\ORM\Mapping {
+    class Entity {
+    }
+}
+
+namespace Doctrine\ORM\Attributes {
+    class Table {
+    }
+}
+
+namespace Foo {
+    use Doctrine\ORM\Mapping\Entity;
+    use Doctrine\ORM\Mapping as ORM;
+    use Doctrine\ORM\Attributes;
+
+    <<Entity("imported class")>>
+    <<ORM\Entity("imported namespace")>>
+    <<\Doctrine\ORM\Mapping\Entity("absolute from namespace")>>
+    <<\Entity("import absolute from global")>>
+    <<Attributes\Table()>>
+    function foo() {
+    }
+}
+
+namespace {
+    class Entity {}
+
+    dump_attributes((new ReflectionFunction('Foo\foo'))->getAttributes());
+}
+?>
+--EXPECTF--
+array(5) {
+  [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"
+    }
+  }
+  [4]=>
+  array(2) {
+    ["name"]=>
+    string(29) "Doctrine\ORM\Attributes\Table"
+    ["args"]=>
+    array(0) {
+    }
+  }
+}
diff --git a/Zend/tests/attributes/005_objects.phpt b/Zend/tests/attributes/005_objects.phpt
new file mode 100644 (file)
index 0000000..baf51af
--- /dev/null
@@ -0,0 +1,120 @@
+--TEST--
+Attributes can be converted into objects.
+--FILE--
+<?php
+
+<<PhpAttribute>>
+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(<<A1('test')>> function () { });
+
+foreach ($ref->getAttributes() as $attr) {
+       $obj = $attr->newInstance();
+
+       var_dump(get_class($obj), $obj->name, $obj->ttl);
+}
+
+echo "\n";
+
+$ref = new \ReflectionFunction(<<A1>> function () { });
+
+try {
+       $ref->getAttributes()[0]->newInstance();
+} catch (\ArgumentCountError $e) {
+       var_dump('ERROR 1', $e->getMessage());
+}
+
+echo "\n";
+
+$ref = new \ReflectionFunction(<<A1([])>> function () { });
+
+try {
+       $ref->getAttributes()[0]->newInstance();
+} catch (\TypeError $e) {
+       var_dump('ERROR 2', $e->getMessage());
+}
+
+echo "\n";
+
+$ref = new \ReflectionFunction(<<A2>> function () { });
+
+try {
+       $ref->getAttributes()[0]->newInstance();
+} catch (\Error $e) {
+       var_dump('ERROR 3', $e->getMessage());
+}
+
+echo "\n";
+
+<<PhpAttribute>>
+class A3
+{
+       private function __construct() { }
+}
+
+$ref = new \ReflectionFunction(<<A3>> function () { });
+
+try {
+       $ref->getAttributes()[0]->newInstance();
+} catch (\Error $e) {
+       var_dump('ERROR 4', $e->getMessage());
+}
+
+echo "\n";
+
+<<PhpAttribute>>
+class A4 { }
+
+$ref = new \ReflectionFunction(<<A4(1)>> function () { });
+
+try {
+       $ref->getAttributes()[0]->newInstance();
+} catch (\Error $e) {
+       var_dump('ERROR 5', $e->getMessage());
+}
+
+echo "\n";
+
+class A5 { }
+
+$ref = new \ReflectionFunction(<<A5>> 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 <<PhpAttribute>>."
diff --git a/Zend/tests/attributes/006_filter.phpt b/Zend/tests/attributes/006_filter.phpt
new file mode 100644 (file)
index 0000000..2b7cc9d
--- /dev/null
@@ -0,0 +1,112 @@
+--TEST--
+Attributes can be filtered by name and base type.
+--FILE--
+<?php
+
+$ref = new \ReflectionFunction(<<A1>> <<A2>> function () { });
+$attr = $ref->getAttributes(A3::class);
+
+var_dump(count($attr));
+
+$ref = new \ReflectionFunction(<<A1>> <<A2>> function () { });
+$attr = $ref->getAttributes(A2::class);
+
+var_dump(count($attr), $attr[0]->getName());
+
+$ref = new \ReflectionFunction(<<A1>> <<A2>> <<A2>> 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(<<A1>> <<A2>> <<A5>> 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(<<A1>> <<A2>> 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(<<A1>> <<A2>> 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(<<A1>> <<A2>> <<A3>> 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(<<A1>> <<A2>> <<A3>> 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(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/007_self_reflect_attribute.phpt b/Zend/tests/attributes/007_self_reflect_attribute.phpt
new file mode 100644 (file)
index 0000000..ae19665
--- /dev/null
@@ -0,0 +1,19 @@
+--TEST--
+Attributes: attributes on PhpAttribute return itself
+--FILE--
+<?php
+
+$reflection = new \ReflectionClass(PhpAttribute::class);
+$attributes = $reflection->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 (file)
index 0000000..dcb0b6b
--- /dev/null
@@ -0,0 +1,9 @@
+--TEST--
+Attributes: Prevent PhpAttribute on non classes
+--FILE--
+<?php
+
+<<PhpAttribute>>
+function foo() {}
+--EXPECTF--
+Fatal error: Only classes can be marked with <<PhpAttribute>> 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 (file)
index 0000000..38fc638
--- /dev/null
@@ -0,0 +1,159 @@
+--TEST--
+Doctrine-like attributes usage
+--FILE--
+<?php
+
+namespace Doctrine\ORM\Attributes {
+    class Annotation { public $values; public function construct() { $this->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>>
+/** @ORM\Entity */
+class User
+{
+    /** @ORM\Id @ORM\Column(type="integer"*) @ORM\GeneratedValue */
+    <<ORM\Id>><<ORM\Column("integer")>><<ORM\GeneratedValue>>
+    private $id;
+
+    /**
+     * @ORM\Column(type="string", unique=true)
+     * @Assert\Email(message="The email '{{ value }}' is not a valid email.")
+     */
+    <<ORM\Column("string", ORM\Column::UNIQUE)>>
+    <<Assert\Email(array("message" => "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"
+     * )
+     */
+    <<Assert\Range(["min" => 120, "max" => 180, "minMessage" => "You must be at least {{ limit }}cm tall to enter"])>>
+    <<ORM\Column(ORM\Column::T_INTEGER)>>
+    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)}
+     *      )
+     */
+    <<ORM\ManyToMany(Phonenumber::class)>>
+    <<ORM\JoinTable("users_phonenumbers")>>
+    <<ORM\JoinColumn("user_id", "id")>>
+    <<ORM\InverseJoinColumn("phonenumber_id", "id", ORM\JoinColumn::UNIQUE)>>
+    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 (file)
index 0000000..50afa6c
--- /dev/null
@@ -0,0 +1,11 @@
+--TEST--
+Attribute arguments support only const expressions.
+--FILE--
+<?php
+
+<<A1(foo())>>
+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 (file)
index 0000000..007cd59
--- /dev/null
@@ -0,0 +1,99 @@
+--TEST--
+Attributes comply with inheritance rules.
+--FILE--
+<?php
+
+<<A2>>
+class C1
+{
+       <<A1>>
+       public function foo() { }
+}
+
+class C2 extends C1
+{
+       public function foo() { }
+}
+
+class C3 extends C1
+{
+       <<A1>>
+       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
+{
+       <<A2>>
+       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 (file)
index 0000000..47176b1
--- /dev/null
@@ -0,0 +1,54 @@
+--TEST--
+Attributes AST can be exported.
+--FILE--
+<?php
+
+assert(0 && ($a = <<A1>><<A2>> function ($a, <<A3(1)>> $b) { }));
+
+assert(0 && ($a = <<A1(1, 2, 1 + 2)>> fn () => 1));
+
+assert(0 && ($a = new <<A1>> class() {
+       <<A1>><<A2>> const FOO = 'foo';
+       <<A2>> public $x;
+       <<A3>> function a() { }
+}));
+
+assert(0 && ($a = function () {
+       <<A1>> class Test1 { }
+       <<A2>> interface Test2 { }
+       <<A3>> trait Test3 { }
+}));
+
+?>
+--EXPECTF--
+Warning: assert(): assert(0 && ($a = <<A1>> <<A2>> function ($a, <<A3(1)>> $b) {
+})) failed in %s on line %d
+
+Warning: assert(): assert(0 && ($a = <<A1(1, 2, 1 + 2)>> fn() => 1)) failed in %s on line %d
+
+Warning: assert(): assert(0 && ($a = new <<A1>> class {
+    <<A1>>
+    <<A2>>
+    const FOO = 'foo';
+    <<A2>>
+    public $x;
+    <<A3>>
+    public function a() {
+    }
+
+})) failed in %s on line %d
+
+Warning: assert(): assert(0 && ($a = function () {
+    <<A1>>
+    class Test1 {
+    }
+
+    <<A2>>
+    interface Test2 {
+    }
+
+    <<A3>>
+    trait Test3 {
+    }
+
+})) failed in %s on line %d
diff --git a/Zend/tests/attributes/013_class_scope.phpt b/Zend/tests/attributes/013_class_scope.phpt
new file mode 100644 (file)
index 0000000..3bb2b30
--- /dev/null
@@ -0,0 +1,117 @@
+--TEST--
+Attributes make use of class scope.
+--FILE--
+<?php
+
+<<A1(self::class, self::FOO)>>
+class C1
+{
+       <<A1(self::class, self::FOO)>>
+       private const FOO = 'foo';
+
+       <<A1(self::class, self::FOO)>>
+       public $a;
+
+       <<A1(self::class, self::FOO)>>
+       public function bar(<<A1(self::class, self::FOO)>> $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";
+
+trait T1
+{
+       <<A1(self::class, self::FOO)>>
+       public function foo() { }
+}
+
+class C2
+{
+       use T1;
+       
+       private const FOO = 'bar';
+}
+
+$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
+{
+       private const FOO = 'foo';
+
+       public static function foo()
+       {
+               return new <<A1(self::class, self::FOO)>> class() {
+                       private const FOO = 'bar';
+
+                       <<A1(self::class, self::FOO)>>
+                       public function bar() { }
+               };
+       }
+}
+
+$ref = new \ReflectionObject(C3::foo());
+
+$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--
+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] => bar
+)
+string(7) "ERROR 1"
+string(36) "Undefined class constant 'self::FOO'"
+
+bool(true)
+string(3) "bar"
+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 (file)
index 0000000..18ff936
--- /dev/null
@@ -0,0 +1,14 @@
+--TEST--
+Attributes cannot be applied to groups of class constants.
+--FILE--
+<?php
+
+class C1
+{
+       <<A1>>
+       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 (file)
index 0000000..493e4ae
--- /dev/null
@@ -0,0 +1,14 @@
+--TEST--
+Attributes cannot be applied to groups of properties.
+--FILE--
+<?php
+
+class C1
+{
+       <<A1>>
+       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 (file)
index 0000000..c96b49c
--- /dev/null
@@ -0,0 +1,15 @@
+--TEST--
+Attributes: Compiler Attributes can check for target declarations
+--SKIPIF--
+<?php
+if (!extension_loaded('zend-test')) {
+    echo "skip requires zend-test extension\n";
+}
+--FILE--
+<?php
+
+<<ZendTestAttribute>>
+function foo() {
+}
+--EXPECTF--
+Fatal error: Only classes can be marked with <<ZendTestAttribute>> in %s
diff --git a/Zend/tests/attributes/017_closure_scope.phpt b/Zend/tests/attributes/017_closure_scope.phpt
new file mode 100644 (file)
index 0000000..f57b4b7
--- /dev/null
@@ -0,0 +1,53 @@
+--TEST--
+Attributes make use of closure scope.
+--FILE--
+<?php
+
+class Test1
+{
+       private const FOO = 'bar';
+}
+
+class C1
+{
+       private const FOO = 'foo';
+
+       public static function foo()
+       {
+               return <<A1(self::class, self::FOO)>> function (<<A1(self::class, self::FOO)>> $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
+)
index 6847c6f2ea7ab6481d04202274a9fe848b78c29a..ed04921f89470ab7444180c266bd7d034636f75b 100644 (file)
@@ -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
index cd41cde5ef1b70428d7581fa37cb0202ec5395e5..48908e1530551fbe718c927705fa0c3bda2cf21f 100644 (file)
@@ -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 {
index 241cba8151ba4adb3eb8f6a0e8fa2dd00ec916e7..f65937e3943226a5ba283e3a68c85245a7d670e4 100644 (file)
@@ -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;
 
@@ -3608,12 +3608,13 @@ 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 = NULL;
        property_info->ce = ce;
        property_info->type = type;
 
        zend_hash_update_ptr(&ce->properties_info, name, property_info);
 
-       return SUCCESS;
+       return property_info;
 }
 /* }}} */
 
@@ -3746,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;
 }
 /* }}} */
 
@@ -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 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;
 
@@ -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 = NULL;
        c->ce = ce;
        if (Z_TYPE_P(value) == IS_CONSTANT_AST) {
                ce->ce_flags &= ~ZEND_ACC_CONSTANTS_UPDATED;
@@ -3850,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) {
@@ -3865,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;
 }
 /* }}} */
 
index 44d43d46dedcea308f9ca5d950982057f04c3ccf..d7e5e528a09406dcc086368e4b0f0719d6cec99c 100644 (file)
@@ -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);
index 7451cf28e92be1abb390392ddbe787655ccfbd8a..ba8af9bcdfc8b77e4c659ad3da412d67725de012 100644 (file)
@@ -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;
 
@@ -131,6 +131,7 @@ ZEND_API zend_ast *zend_ast_create_decl(
        ast->child[1] = child1;
        ast->child[2] = child2;
        ast->child[3] = child3;
+       ast->child[4] = child4;
 
        return (zend_ast *) ast;
 }
@@ -860,7 +861,8 @@ tail_call:
                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;
        }
 }
@@ -1313,6 +1315,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, "<<");
+               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]);
+                       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 +1443,10 @@ tail_call:
                case ZEND_AST_ARROW_FUNC:
                case ZEND_AST_METHOD:
                        decl = (zend_ast_decl *) ast;
+                       if (decl->child[4]) {
+                               zend_bool newlines = !(ast->kind == ZEND_AST_CLOSURE || ast->kind == ZEND_AST_ARROW_FUNC);
+                               zend_ast_export_attributes(str, decl->child[4], indent, newlines);
+                       }
                        if (decl->flags & ZEND_ACC_PUBLIC) {
                                smart_str_appends(str, "public ");
                        } else if (decl->flags & ZEND_ACC_PROTECTED) {
@@ -1462,6 +1503,9 @@ tail_call:
                        break;
                case ZEND_AST_CLASS:
                        decl = (zend_ast_decl *) ast;
+                       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 ");
                        } else if (decl->flags & ZEND_ACC_TRAIT) {
@@ -1517,6 +1561,9 @@ simple_list:
                        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 +1588,12 @@ simple_list:
                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 +1836,17 @@ simple_list:
                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->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) {
                                        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 +2059,9 @@ simple_list:
                        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 +2174,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)->child[4] = 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;
+}
index 5b8aae6f96c25a4c8e9734d216dbc533ca3e28bc..0ec922dcea47f0c8b2b83a832e17dbfa4ff66664 100644 (file)
@@ -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;
@@ -192,7 +195,7 @@ typedef struct _zend_ast_decl {
        unsigned char *lex_pos;
        zend_string *doc_comment;
        zend_string *name;
-       zend_ast *child[4];
+       zend_ast *child[5];
 } zend_ast_decl;
 
 typedef void (*zend_ast_process_t)(zend_ast *ast);
@@ -270,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);
@@ -340,4 +343,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 (file)
index 0000000..12a997e
--- /dev/null
@@ -0,0 +1,146 @@
+#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 <<PhpAttribute>>");
+       }
+}
+
+ZEND_API zend_attributes_internal_validator zend_attribute_get_validator(zend_string *lcname)
+{
+       return zend_hash_find_ptr(&internal_validators, lcname);
+}
+
+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 zend_always_inline void free_attribute(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 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 == ((GC_FLAGS(name) & IS_STR_PERSISTENT) != 0)) {
+               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)
+{
+       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);
+
+       zend_hash_update_ptr(&internal_validators, lcname, validator);
+       zend_string_release(lcname);
+
+       zend_add_class_attribute(ce, zend_ce_php_attribute->name, 0);
+}
+
+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 (file)
index 0000000..d7e5417
--- /dev/null
@@ -0,0 +1,69 @@
+#ifndef ZEND_ATTRIBUTES_H
+#define ZEND_ATTRIBUTES_H
+
+#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))
+
+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 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);
+
+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_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);
+}
+
+void zend_register_attribute_ce(void);
+
+#endif
index 5dfa5f2a3d2e00c61bab306d36dbd518f9a9cda9..707d2ac9da319a9d39c9edbc7cf38ee0aa4cc815 100644 (file)
@@ -20,6 +20,7 @@
 
 #include <zend_language_parser.h>
 #include "zend.h"
+#include "zend_attributes.h"
 #include "zend_compile.h"
 #include "zend_constants.h"
 #include "zend_llist.h"
@@ -1821,6 +1822,7 @@ ZEND_API void zend_initialize_class_data(zend_class_entry *ce, zend_bool nullify
        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;
@@ -2372,7 +2374,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 +5718,40 @@ static zend_bool zend_is_valid_default_value(zend_type type, zval *value)
        return 0;
 }
 
+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, j;
+
+       ZEND_ASSERT(ast->kind == ZEND_AST_ATTRIBUTE_LIST);
+
+       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;
+
+               zend_attribute *attr = zend_add_attribute(attributes, 0, offset, name, args ? args->children : 0);
+               zend_string_release(name);
+
+               // Populate arguments
+               if (args) {
+                       ZEND_ASSERT(args->kind == ZEND_AST_ARG_LIST);
+
+                       for (j = 0; j < args->children; j++) {
+                               zend_const_expr_to_zval(&attr->argv[j], args->child[j]);
+                       }
+               }
+
+               // Validate internal attribute
+               zend_attributes_internal_validator validator = zend_attribute_get_validator(attr->lcname);
+
+               if (validator != NULL) {
+                       validator(attr, target);
+               }
+       }
+}
+/* }}} */
+
 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 +5786,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 +5856,10 @@ 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) {
+                       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 +6331,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->kind == ZEND_AST_CLOSURE || decl->kind == ZEND_AST_ARROW_FUNC) {
                op_array->fn_flags |= ZEND_ACC_CLOSURE;
        }
@@ -6309,6 +6351,16 @@ 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;
+               }
+
+               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. */
@@ -6372,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) /* {{{ */
+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);
@@ -6387,6 +6439,7 @@ void zend_compile_prop_decl(zend_ast *ast, zend_ast *type_ast, uint32_t flags) /
        }
 
        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];
@@ -6447,7 +6500,11 @@ 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);
+               info = zend_declare_typed_property(ce, name, &value_zv, flags, doc_comment, type);
+
+               if (attr_ast) {
+                       zend_compile_attributes(&info->attributes, attr_ast, 0, ZEND_ATTRIBUTE_TARGET_PROPERTY);
+               }
        }
 }
 /* }}} */
@@ -6456,8 +6513,14 @@ void zend_compile_prop_group(zend_ast *list) /* {{{ */
 {
        zend_ast *type_ast = list->child[0];
        zend_ast *prop_ast = list->child[1];
+       zend_ast *attr_ast = list->child[2];
 
-       zend_compile_prop_decl(prop_ast, type_ast, list->attr);
+       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, attr_ast);
 }
 /* }}} */
 
@@ -6473,7 +6536,7 @@ 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);
@@ -6484,7 +6547,13 @@ void zend_compile_class_const_decl(zend_ast *ast) /* {{{ */
                return;
        }
 
+       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) {
+               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];
@@ -6498,7 +6567,11 @@ 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);
+               c = zend_declare_class_constant_ex(ce, name, &value_zv, ast->attr, doc_comment);
+
+               if (attr_ast) {
+                       zend_compile_attributes(&c->attributes, attr_ast, 0, ZEND_ATTRIBUTE_TARGET_CLASS_CONST);
+               }
        }
 }
 /* }}} */
@@ -6725,6 +6798,10 @@ void zend_compile_class_decl(znode *result, zend_ast *ast, zend_bool toplevel) /
 
        CG(active_class_entry) = ce;
 
+       if (decl->child[4]) {
+               zend_compile_attributes(&ce->attributes, decl->child[4], 0, ZEND_ATTRIBUTE_TARGET_CLASS);
+       }
+
        if (implements_ast) {
                zend_compile_implements(implements_ast);
        }
@@ -8801,8 +8878,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);
index a90219b1b4453b561f88c2c85a9c2ee46ae3b269..1a53f92e9e6eb673568089d49c972021e415bb69 100644 (file)
@@ -348,6 +348,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 +365,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 +405,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 +455,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 +479,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;
index 2c42763f0c5e5bb5bd2ddeea4a96f045abe3949a..63cdf66d580e8e171231ebdee4143ca8cd5eba61 100644 (file)
@@ -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();
 }
index 224dfb71d07fec974931200dd82e44026e989b7a..11e1904461cd9b58afcf0c2a2f3c123f3e168452 100644 (file)
@@ -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          */
index f42998a8f8ae4537bf7c1a675869dde6a8cc2db2..26a2bc4da5d6c8b5c68209f3d26b6c5888d3b19e 100644 (file)
@@ -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;
@@ -2072,8 +2073,18 @@ 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);
+                       new_prop = zend_declare_typed_property(ce, prop_name, prop_value, flags, doc_comment, property_info->type);
+
+                       if (property_info->attributes) {
+                               new_prop->attributes = property_info->attributes;
+
+                               if (!(GC_FLAGS(new_prop->attributes) & IS_ARRAY_IMMUTABLE)) {
+                                       GC_ADDREF(new_prop->attributes);
+                               }
+                       }
+
                        zend_string_release_ex(prop_name, 0);
                } ZEND_HASH_FOREACH_END();
        }
index 18290acc446f59c6475df31d032a1e05885ff473..d8d66553c1335411558977f26125a79847b50c92 100644 (file)
@@ -258,6 +258,8 @@ static YYSIZE_T zend_yytnamerr(char*, const char*);
 %type <ast> isset_variable type return_type type_expr type_without_static
 %type <ast> identifier type_expr_without_static union_type_without_static
 %type <ast> inline_function union_type
+%type <ast> attributed_statement attributed_class_statement attributed_parameter
+%type <ast> attribute_arguments attribute_decl attribute attributes
 
 %type <num> returns_ref function fn is_reference is_variadic variable_modifiers
 %type <num> 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; }
@@ -497,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:
@@ -513,10 +542,10 @@ is_variadic:
 class_declaration_statement:
                class_modifiers T_CLASS { $<num>$ = CG(zend_lineno); }
                T_STRING extends_from implements_list backup_doc_comment '{' class_statement_list '}'
-                       { $$ = zend_ast_create_decl(ZEND_AST_CLASS, $1, $<num>3, $7, zend_ast_get_str($4), $5, $6, $9, NULL); }
+                       { $$ = zend_ast_create_decl(ZEND_AST_CLASS, $1, $<num>3, $7, zend_ast_get_str($4), $5, $6, $9, NULL, NULL); }
        |       T_CLASS { $<num>$ = CG(zend_lineno); }
                T_STRING extends_from implements_list backup_doc_comment '{' class_statement_list '}'
-                       { $$ = zend_ast_create_decl(ZEND_AST_CLASS, 0, $<num>2, $6, zend_ast_get_str($3), $4, $5, $8, NULL); }
+                       { $$ = zend_ast_create_decl(ZEND_AST_CLASS, 0, $<num>2, $6, zend_ast_get_str($3), $4, $5, $8, NULL, NULL); }
 ;
 
 class_modifiers:
@@ -533,13 +562,13 @@ class_modifier:
 trait_declaration_statement:
                T_TRAIT { $<num>$ = CG(zend_lineno); }
                T_STRING backup_doc_comment '{' class_statement_list '}'
-                       { $$ = zend_ast_create_decl(ZEND_AST_CLASS, ZEND_ACC_TRAIT, $<num>2, $4, zend_ast_get_str($3), NULL, NULL, $6, NULL); }
+                       { $$ = zend_ast_create_decl(ZEND_AST_CLASS, ZEND_ACC_TRAIT, $<num>2, $4, zend_ast_get_str($3), NULL, NULL, $6, NULL, NULL); }
 ;
 
 interface_declaration_statement:
                T_INTERFACE { $<num>$ = CG(zend_lineno); }
                T_STRING interface_extends_list backup_doc_comment '{' class_statement_list '}'
-                       { $$ = zend_ast_create_decl(ZEND_AST_CLASS, ZEND_ACC_INTERFACE, $<num>2, $5, zend_ast_get_str($3), NULL, $4, $7, NULL); }
+                       { $$ = zend_ast_create_decl(ZEND_AST_CLASS, ZEND_ACC_INTERFACE, $<num>2, $5, zend_ast_get_str($3), NULL, $4, $7, NULL, NULL); }
 ;
 
 extends_from:
@@ -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; }
+                                 zend_ast_get_str($4), $7, NULL, $11, $9, NULL); 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:
@@ -896,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, $<num>2, $6, NULL,
-                               $4, $5, $8, NULL);
+                               $4, $5, $8, NULL, NULL);
                        $$ = zend_ast_create(ZEND_AST_NEW, decl, $3);
                }
 ;
@@ -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; }
 ;
 
 
@@ -1035,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; }
 ;
index 0dd42f8e6e1bae376ca608882c0dfd6a1df56af1..5c71d884474b86a4e875c3457cdd33c711fe3fc0 100644 (file)
@@ -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);
        }
index 2c3928b6a9678e791b05dfa97ffca2e2f7cc0615..c5f4dd47bcc302e7c76aa5a18f01d401aac59dfc 100644 (file)
@@ -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 \
index 631d712ba4a383ef4c5512822d05b9c32fb2dd19..03271678c568aae2f89631a535beb71bf0fb10b6 100644 (file)
@@ -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,26 @@ 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)
+{
+       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_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);
+       }
+}
+
 static void zend_file_cache_serialize_type(
                zend_type *type, zend_persistent_script *script, zend_file_cache_metainfo *info, void *buf)
 {
@@ -429,6 +469,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 +596,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 +641,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 +668,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 +722,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 +1166,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)
+{
+       zend_attribute *attr;
+       uint32_t i;
+
+       UNSERIALIZE_PTR(Z_PTR_P(zv));
+       attr = Z_PTR_P(zv);
+
+       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);
+       }
+}
+
 static void zend_file_cache_unserialize_type(
                zend_type *type, zend_persistent_script *script, void *buf)
 {
@@ -1167,6 +1229,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 +1345,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 +1399,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 +1424,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 +1473,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);
 
index 40f37b7bf76a4fc396652691119f78be189e2815..11f26cc123240a891993613d91772c3cc1284b19 100644 (file)
@@ -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,38 @@ 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_put_free(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(&copy->argv[i]);
+                       }
+
+                       ZVAL_PTR(v, copy);
+               } 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 +409,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 +604,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 +749,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 +791,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 +880,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);
index 8a1e99c65df279b8df7e67ec49561e3c888e64c7..a7b347ee0ee2e3621df0e90fd9716f3ead5e9313 100644 (file)
@@ -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) {
index 9aac065e38bea61892d00607b64a0740141490ac..6d4f257eb4a8c09cf0c08ffba39229a7c2340cda 100644 (file)
@@ -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,116 @@ 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) /* {{{ */
+{
+       zend_string *name = NULL;
+       zend_long flags = 0;
+       zend_class_entry *base = NULL;
+
+       if (zend_parse_parameters(ZEND_NUM_ARGS(), "|S!l", &name, &flags) == FAILURE) {
+               RETURN_THROWS();
+       }
+
+       if (flags & ~REFLECTION_ATTRIBUTE_IS_INSTANCEOF) {
+               zend_argument_value_error(2, "must be a valid attribute filter flag");
+               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(return_value);
+
+       if (FAILURE == read_attributes(return_value, attributes, scope, offset, name, base)) {
+               RETURN_THROWS();
+       }
+}
+/* }}} */
+
 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 +1755,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 +2694,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 +3765,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 +4145,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 +5658,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 +6382,201 @@ ZEND_METHOD(ReflectionReference, getId)
 }
 /* }}} */
 
+ZEND_METHOD(ReflectionAttribute, __construct)
+{
+}
+
+ZEND_METHOD(ReflectionAttribute, __clone)
+{
+       /* Should never be executable */
+       _DO_THROW("Cannot clone object using __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) /* {{{ */
+{
+       ZVAL_COPY_OR_DUP(ret, val);
+
+       if (Z_TYPE_P(val) == IS_CONSTANT_AST) {
+               if (SUCCESS != zval_update_constant_ex(ret, scope)) {
+                       zval_ptr_dtor(ret);
+                       return FAILURE;
+               }
+       }
+
+       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 tmp;
+       uint32_t i;
+
+       if (zend_parse_parameters_none() == FAILURE) {
+               RETURN_THROWS();
+       }
+       GET_REFLECTION_OBJECT_PTR(attr);
+
+       array_init(return_value);
+
+       for (i = 0; i < attr->data->argc; i++) {
+               if (FAILURE == import_attribute_value(&tmp, &attr->data->argv[i], attr->scope)) {
+                       RETURN_THROWS();
+               }
+
+               add_next_index_zval(return_value, &tmp);
+       }
+}
+/* }}} */
+
+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 <<PhpAttribute>>.", 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_COPY_VALUE(&obj);
+}
+/* }}} */
 static const zend_function_entry reflection_ext_functions[] = { /* {{{ */
        PHP_FE_END
 }; /* }}} */
@@ -6340,6 +6727,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;
index e4e08c16e60bc9ec7aabe2d8612b235740473d79..654ba55256c8f64679dcd201821247a0aed1068c 100644 (file)
@@ -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);
 
index 30627475073b45f0f8acc9cea0d06024c5e69d4b..c27f971c070cb272dcee07b40d3dcd4b08ce58ea 100644 (file)
@@ -95,6 +95,9 @@ abstract class ReflectionFunctionAbstract implements Reflector
 
     /** @return ReflectionType|null */
     public function getReturnType() {}
+
+    /** @return ReflectionAttribute[] */
+    public function getAttributes(?string $name = null, int $flags = 0): array {}
 }
 
 class ReflectionFunction extends ReflectionFunctionAbstract
@@ -363,6 +366,9 @@ class ReflectionClass implements Reflector
 
     /** @return string */
     public function getShortName() {}
+
+    /** @return ReflectionAttribute[] */
+    public function getAttributes(?string $name = null, int $flags = 0): array {}
 }
 
 class ReflectionObject extends ReflectionClass
@@ -429,6 +435,9 @@ class ReflectionProperty implements Reflector
 
     /** @return mixed */
     public function getDefaultValue() {}
+
+    /** @return ReflectionAttribute[] */
+    public function getAttributes(?string $name = null, int $flags = 0): array {}
 }
 
 class ReflectionClassConstant implements Reflector
@@ -464,6 +473,9 @@ class ReflectionClassConstant implements Reflector
 
     /** @return string|false */
     public function getDocComment() {}
+
+    /** @return ReflectionAttribute[] */
+    public function getAttributes(?string $name = null, int $flags = 0): array {}
 }
 
 class ReflectionParameter implements Reflector
@@ -540,6 +552,9 @@ class ReflectionParameter implements Reflector
 
     /** @return bool */
     public function isVariadic() {}
+
+    /** @return ReflectionAttribute[] */
+    public function getAttributes(?string $name = null, int $flags = 0): array {}
 }
 
 abstract class ReflectionType implements Stringable
@@ -647,3 +662,14 @@ final class ReflectionReference
 
     private function __construct() {}
 }
+
+final class ReflectionAttribute
+{
+    public function getName(): string {}
+    public function getArguments(): array {}
+    public function newInstance(): object {}
+
+    private function __clone() {}
+
+    private function __construct() {}
+}
index 1e3e9988deee028c5fa3ba236cd9c8019358c8a8..3dec73ec8b49573ead5c8653cdce16842218b2cb 100644 (file)
@@ -57,6 +57,11 @@ ZEND_END_ARG_INFO()
 
 #define arginfo_class_ReflectionFunctionAbstract_getReturnType arginfo_class_ReflectionFunctionAbstract___clone
 
+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()
+
 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
+};
index 367645d019d5dae253650048da67f5c6067a1ee0..fb39bdb4defb770fe9d38aad0496562309b60460 100644 (file)
@@ -27,7 +27,7 @@ Class [ <internal:Reflection> class ReflectionClass implements Reflector, String
     Property [ public $name = '' ]
   }
 
-  - Methods [53] {
+  - Methods [54] {
     Method [ <internal:Reflection> final private method __clone ] {
 
       - Parameters [0] {
@@ -365,5 +365,14 @@ Class [ <internal:Reflection> class ReflectionClass implements Reflector, String
       - Parameters [0] {
       }
     }
+
+    Method [ <internal:Reflection> public method getAttributes ] {
+
+      - Parameters [2] {
+        Parameter #0 [ <optional> ?string $name = null ]
+        Parameter #1 [ <optional> int $flags = 0 ]
+      }
+      - Return [ array ]
+    }
   }
 }
index 61a0d24b0887ad1223cc7ac1b28d65c1365d7c93..81d3c8d55b1468f5ec210af79bcf6a7e5f3573f7 100644 (file)
@@ -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"
+  }
 }
index 1625156bdb2e002a7a87e7b40d8a920cb87a5842..69b6196ab6622b5ae4fef746643699744245faee 100644 (file)
 #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 <<ZendTestAttribute>>");
+       }
+}
+
 ZEND_METHOD(_ZendTestClass, __toString) /* {{{ */ {
        RETURN_EMPTY_STRING();
 }
@@ -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;
 }
 
index 7ba5ee345e706cce5a48012a4d8db682ffe08898..9b5e44c37d760481df32ae65ea0dd707ceea6197 100644 (file)
@@ -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 \