mirror of
https://github.com/php/php-src.git
synced 2024-11-23 09:54:15 +08:00
Add Attributes
Co-authored-by: Martin Schröder <m.schroeder2007@gmail.com>
This commit is contained in:
parent
970ac28e27
commit
a7908c2d11
134
Zend/tests/attributes/001_placement.phpt
Normal file
134
Zend/tests/attributes/001_placement.phpt
Normal 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)
|
||||
}
|
43
Zend/tests/attributes/002_rfcexample.phpt
Normal file
43
Zend/tests/attributes/002_rfcexample.phpt
Normal 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"
|
||||
}
|
109
Zend/tests/attributes/003_ast_nodes.phpt
Normal file
109
Zend/tests/attributes/003_ast_nodes.phpt
Normal 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"
|
||||
|
93
Zend/tests/attributes/004_name_resolution.phpt
Normal file
93
Zend/tests/attributes/004_name_resolution.phpt
Normal 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) {
|
||||
}
|
||||
}
|
||||
}
|
120
Zend/tests/attributes/005_objects.phpt
Normal file
120
Zend/tests/attributes/005_objects.phpt
Normal 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>>."
|
112
Zend/tests/attributes/006_filter.phpt
Normal file
112
Zend/tests/attributes/006_filter.phpt
Normal 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"
|
19
Zend/tests/attributes/007_self_reflect_attribute.phpt
Normal file
19
Zend/tests/attributes/007_self_reflect_attribute.phpt
Normal 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) {
|
||||
}
|
9
Zend/tests/attributes/008_wrong_attribution.phpt
Normal file
9
Zend/tests/attributes/008_wrong_attribution.phpt
Normal 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
|
159
Zend/tests/attributes/009_doctrine_annotations_example.phpt
Normal file
159
Zend/tests/attributes/009_doctrine_annotations_example.phpt
Normal 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"
|
||||
}
|
11
Zend/tests/attributes/010_unsupported_const_expression.phpt
Normal file
11
Zend/tests/attributes/010_unsupported_const_expression.phpt
Normal 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
|
99
Zend/tests/attributes/011-inheritance.phpt
Normal file
99
Zend/tests/attributes/011-inheritance.phpt
Normal 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
|
||||
(
|
||||
)
|
54
Zend/tests/attributes/012-ast-export.phpt
Normal file
54
Zend/tests/attributes/012-ast-export.phpt
Normal 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
|
117
Zend/tests/attributes/013_class_scope.phpt
Normal file
117
Zend/tests/attributes/013_class_scope.phpt
Normal 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"
|
14
Zend/tests/attributes/014_class_const_group.phpt
Normal file
14
Zend/tests/attributes/014_class_const_group.phpt
Normal 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
|
14
Zend/tests/attributes/015_property_group.phpt
Normal file
14
Zend/tests/attributes/015_property_group.phpt
Normal 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
|
@ -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
|
53
Zend/tests/attributes/017_closure_scope.phpt
Normal file
53
Zend/tests/attributes/017_closure_scope.phpt
Normal 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
|
||||
)
|
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
146
Zend/zend_attributes.c
Normal 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
69
Zend/zend_attributes.h
Normal 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
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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 */
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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; }
|
||||
;
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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 \
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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(©->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);
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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() {}
|
||||
}
|
||||
|
@ -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
|
||||
};
|
||||
|
@ -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 ]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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 \
|
||||
|
Loading…
Reference in New Issue
Block a user