Add Attributes

Co-authored-by: Martin Schröder <m.schroeder2007@gmail.com>
This commit is contained in:
Benjamin Eberlei 2020-05-24 20:57:00 +02:00
parent 970ac28e27
commit a7908c2d11
44 changed files with 2351 additions and 60 deletions

View File

@ -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)
}

View File

@ -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"
}

View File

@ -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"

View File

@ -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) {
}
}
}

View File

@ -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>>."

View File

@ -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"

View File

@ -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) {
}

View File

@ -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

View File

@ -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"
}

View File

@ -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

View File

@ -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
(
)

View File

@ -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

View File

@ -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"

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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
)

View 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

View 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 {

View 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;
}
/* }}} */

View 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);

View 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;
}

View 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

146
Zend/zend_attributes.c Normal file
View File

@ -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);
}

69
Zend/zend_attributes.h Normal file
View File

@ -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

View 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);

View 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;

View 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();
}

View 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 */

View 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();
}

View 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; }
;

View 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);
}

View 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 \

View 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);

View 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);

View 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) {

View 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;

View 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);

View 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() {}
}

View 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
};

View 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 ]
}
}
}

View 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"
}
}

View File

@ -23,11 +23,13 @@
#include "ext/standard/info.h"
#include "php_test.h"
#include "test_arginfo.h"
#include "zend_attributes.h"
static zend_class_entry *zend_test_interface;
static zend_class_entry *zend_test_class;
static zend_class_entry *zend_test_child_class;
static zend_class_entry *zend_test_trait;
static zend_class_entry *zend_test_attribute;
static zend_object_handlers zend_test_class_handlers;
ZEND_FUNCTION(zend_test_func)
@ -181,6 +183,13 @@ static zend_function *zend_test_class_static_method_get(zend_class_entry *ce, ze
}
/* }}} */
void zend_attribute_validate_zendtestattribute(zend_attribute *attr, int target)
{
if (target != ZEND_ATTRIBUTE_TARGET_CLASS) {
zend_error(E_COMPILE_ERROR, "Only classes can be marked with <<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;
}

View 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 \