mirror of
https://github.com/php/php-src.git
synced 2024-09-21 09:57:23 +00:00
Implement Attribute Amendments.
RFC: https://wiki.php.net/rfc/attribute_amendments Support for attribute grouping is left out, because the short attribute syntax RFC will likely make it obsolete. Closes GH-5751.
This commit is contained in:
parent
46e38a1927
commit
053ef28b8d
@ -554,6 +554,7 @@ PHP 8.0 UPGRADE NOTES
|
||||
RFC: https://wiki.php.net/rfc/mixed_type_v2
|
||||
. Added support for Attributes
|
||||
RFC: https://wiki.php.net/rfc/attributes_v2
|
||||
RFC: https://wiki.php.net/rfc/attribute_amendments
|
||||
. Added support for constructor property promotion (declaring properties in
|
||||
the constructor signature).
|
||||
RFC: https://wiki.php.net/rfc/constructor_promotion
|
||||
|
@ -4,9 +4,9 @@ Attributes: Example from Attributes RFC
|
||||
<?php
|
||||
// https://wiki.php.net/rfc/attributes_v2#attribute_syntax
|
||||
namespace My\Attributes {
|
||||
use PhpAttribute;
|
||||
use Attribute;
|
||||
|
||||
<<PhpAttribute>>
|
||||
<<Attribute>>
|
||||
class SingleArgument {
|
||||
public $argumentValue;
|
||||
|
||||
@ -37,7 +37,7 @@ array(1) {
|
||||
[0]=>
|
||||
string(11) "Hello World"
|
||||
}
|
||||
object(My\Attributes\SingleArgument)#3 (1) {
|
||||
object(My\Attributes\SingleArgument)#%d (1) {
|
||||
["argumentValue"]=>
|
||||
string(11) "Hello World"
|
||||
}
|
||||
|
@ -59,7 +59,7 @@ var_dump($ref->getAttributes()[0]->getArguments());
|
||||
|
||||
echo "\n";
|
||||
|
||||
<<PhpAttribute>>
|
||||
<<Attribute>>
|
||||
class C5
|
||||
{
|
||||
public function __construct() { }
|
||||
|
@ -3,7 +3,7 @@ Attributes can be converted into objects.
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
<<PhpAttribute>>
|
||||
<<Attribute(Attribute::TARGET_FUNCTION)>>
|
||||
class A1
|
||||
{
|
||||
public string $name;
|
||||
@ -56,7 +56,7 @@ try {
|
||||
|
||||
echo "\n";
|
||||
|
||||
<<PhpAttribute>>
|
||||
<<Attribute>>
|
||||
class A3
|
||||
{
|
||||
private function __construct() { }
|
||||
@ -72,7 +72,7 @@ try {
|
||||
|
||||
echo "\n";
|
||||
|
||||
<<PhpAttribute>>
|
||||
<<Attribute>>
|
||||
class A4 { }
|
||||
|
||||
$ref = new \ReflectionFunction(<<A4(1)>> function () { });
|
||||
@ -117,4 +117,4 @@ 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>>."
|
||||
string(55) "Attempting to use non-attribute class 'A5' as attribute"
|
||||
|
@ -1,19 +1,22 @@
|
||||
--TEST--
|
||||
Attributes: attributes on PhpAttribute return itself
|
||||
Attributes: attributes on Attribute return itself
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
$reflection = new \ReflectionClass(PhpAttribute::class);
|
||||
$reflection = new \ReflectionClass(Attribute::class);
|
||||
$attributes = $reflection->getAttributes();
|
||||
|
||||
foreach ($attributes as $attribute) {
|
||||
var_dump($attribute->getName());
|
||||
var_dump($attribute->getArguments());
|
||||
var_dump($attribute->newInstance());
|
||||
|
||||
$a = $attribute->newInstance();
|
||||
var_dump(get_class($a));
|
||||
var_dump($a->flags == Attribute::TARGET_ALL);
|
||||
}
|
||||
--EXPECTF--
|
||||
string(12) "PhpAttribute"
|
||||
string(9) "Attribute"
|
||||
array(0) {
|
||||
}
|
||||
object(PhpAttribute)#3 (0) {
|
||||
}
|
||||
string(9) "Attribute"
|
||||
bool(true)
|
||||
|
@ -1,9 +1,9 @@
|
||||
--TEST--
|
||||
Attributes: Prevent PhpAttribute on non classes
|
||||
Attributes: Prevent Attribute on non classes
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
<<PhpAttribute>>
|
||||
<<Attribute>>
|
||||
function foo() {}
|
||||
--EXPECTF--
|
||||
Fatal error: Only classes can be marked with <<PhpAttribute>> in %s
|
||||
Fatal error: Attribute "Attribute" cannot target function (allowed targets: class) in %s
|
||||
|
70
Zend/tests/attributes/020_userland_attribute_validation.phpt
Normal file
70
Zend/tests/attributes/020_userland_attribute_validation.phpt
Normal file
@ -0,0 +1,70 @@
|
||||
--TEST--
|
||||
Attributes expose and verify target and repeatable data.
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
<<Attribute(Attribute::TARGET_FUNCTION | Attribute::TARGET_METHOD)>>
|
||||
class A1 { }
|
||||
|
||||
$ref = new \ReflectionFunction(<<A1>> function () { });
|
||||
$attr = $ref->getAttributes()[0];
|
||||
var_dump($attr->getName(), $attr->getTarget() == Attribute::TARGET_FUNCTION, $attr->isRepeated());
|
||||
var_dump(get_class($attr->newInstance()));
|
||||
|
||||
echo "\n";
|
||||
|
||||
$ref = new \ReflectionObject(new <<A1>> class() { });
|
||||
$attr = $ref->getAttributes()[0];
|
||||
var_dump($attr->getName(), $attr->getTarget() == Attribute::TARGET_CLASS, $attr->isRepeated());
|
||||
|
||||
try {
|
||||
$attr->newInstance();
|
||||
} catch (\Throwable $e) {
|
||||
var_dump('ERROR 1', $e->getMessage());
|
||||
}
|
||||
|
||||
echo "\n";
|
||||
|
||||
$ref = new \ReflectionFunction(<<A1>> <<A1>> function () { });
|
||||
$attr = $ref->getAttributes()[0];
|
||||
var_dump($attr->getName(), $attr->getTarget() == Attribute::TARGET_FUNCTION, $attr->isRepeated());
|
||||
|
||||
try {
|
||||
$attr->newInstance();
|
||||
} catch (\Throwable $e) {
|
||||
var_dump('ERROR 2', $e->getMessage());
|
||||
}
|
||||
|
||||
echo "\n";
|
||||
|
||||
<<Attribute(Attribute::TARGET_CLASS | Attribute::IS_REPEATABLE)>>
|
||||
class A2 { }
|
||||
|
||||
$ref = new \ReflectionObject(new <<A2>> <<A2>> class() { });
|
||||
$attr = $ref->getAttributes()[0];
|
||||
var_dump($attr->getName(), $attr->getTarget() == Attribute::TARGET_CLASS, $attr->isRepeated());
|
||||
var_dump(get_class($attr->newInstance()));
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
string(2) "A1"
|
||||
bool(true)
|
||||
bool(false)
|
||||
string(2) "A1"
|
||||
|
||||
string(2) "A1"
|
||||
bool(true)
|
||||
bool(false)
|
||||
string(7) "ERROR 1"
|
||||
string(70) "Attribute "A1" cannot target class (allowed targets: function, method)"
|
||||
|
||||
string(2) "A1"
|
||||
bool(true)
|
||||
bool(true)
|
||||
string(7) "ERROR 2"
|
||||
string(35) "Attribute "A1" must not be repeated"
|
||||
|
||||
string(2) "A2"
|
||||
bool(true)
|
||||
bool(true)
|
||||
string(2) "A2"
|
@ -0,0 +1,11 @@
|
||||
--TEST--
|
||||
Attribute flags type is validated.
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
<<Attribute("foo")>>
|
||||
class A1 { }
|
||||
|
||||
?>
|
||||
--EXPECTF--
|
||||
Fatal error: Attribute::__construct(): Argument #1 ($flags) must must be of type int, string given in %s
|
@ -0,0 +1,11 @@
|
||||
--TEST--
|
||||
Attribute flags value is validated.
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
<<Attribute(-1)>>
|
||||
class A1 { }
|
||||
|
||||
?>
|
||||
--EXPECTF--
|
||||
Fatal error: Invalid attribute flags specified in %s
|
11
Zend/tests/attributes/023_ast_node_in_validation.phpt
Normal file
11
Zend/tests/attributes/023_ast_node_in_validation.phpt
Normal file
@ -0,0 +1,11 @@
|
||||
--TEST--
|
||||
Attribute flags value is validated.
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
<<Attribute(Foo::BAR)>>
|
||||
class A1 { }
|
||||
|
||||
?>
|
||||
--EXPECTF--
|
||||
Fatal error: Class 'Foo' not found in %s
|
11
Zend/tests/attributes/024_internal_target_validation.phpt
Normal file
11
Zend/tests/attributes/024_internal_target_validation.phpt
Normal file
@ -0,0 +1,11 @@
|
||||
--TEST--
|
||||
Internal attribute targets are validated.
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
<<Attribute>>
|
||||
function a1() { }
|
||||
|
||||
?>
|
||||
--EXPECTF--
|
||||
Fatal error: Attribute "Attribute" cannot target function (allowed targets: class) in %s
|
@ -0,0 +1,12 @@
|
||||
--TEST--
|
||||
Internal attribute targets are validated.
|
||||
--FILE--
|
||||
<?php
|
||||
|
||||
<<Attribute>>
|
||||
<<Attribute>>
|
||||
class A1 { }
|
||||
|
||||
?>
|
||||
--EXPECTF--
|
||||
Fatal error: Attribute "Attribute" must not be repeated in %s
|
@ -1,21 +1,66 @@
|
||||
/*
|
||||
+----------------------------------------------------------------------+
|
||||
| Zend Engine |
|
||||
+----------------------------------------------------------------------+
|
||||
| Copyright (c) Zend Technologies Ltd. (http://www.zend.com) |
|
||||
+----------------------------------------------------------------------+
|
||||
| This source file is subject to version 2.00 of the Zend license, |
|
||||
| that is bundled with this package in the file LICENSE, and is |
|
||||
| available through the world-wide-web at the following url: |
|
||||
| http://www.zend.com/license/2_00.txt. |
|
||||
| If you did not receive a copy of the Zend license and are unable to |
|
||||
| obtain it through the world-wide-web, please send a note to |
|
||||
| license@zend.com so we can mail you a copy immediately. |
|
||||
+----------------------------------------------------------------------+
|
||||
| Authors: Benjamin Eberlei <kontakt@beberlei.de> |
|
||||
| Martin Schröder <m.schroeder2007@gmail.com> |
|
||||
+----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
#include "zend.h"
|
||||
#include "zend_API.h"
|
||||
#include "zend_attributes.h"
|
||||
#include "zend_attributes_arginfo.h"
|
||||
#include "zend_smart_str.h"
|
||||
|
||||
ZEND_API zend_class_entry *zend_ce_php_attribute;
|
||||
ZEND_API zend_class_entry *zend_ce_attribute;
|
||||
|
||||
static HashTable internal_validators;
|
||||
static HashTable internal_attributes;
|
||||
|
||||
void zend_attribute_validate_phpattribute(zend_attribute *attr, int target)
|
||||
void validate_attribute(zend_attribute *attr, uint32_t target, zend_class_entry *scope)
|
||||
{
|
||||
if (target != ZEND_ATTRIBUTE_TARGET_CLASS) {
|
||||
zend_error(E_COMPILE_ERROR, "Only classes can be marked with <<PhpAttribute>>");
|
||||
if (attr->argc > 0) {
|
||||
zval flags;
|
||||
|
||||
if (FAILURE == zend_get_attribute_value(&flags, attr, 0, scope)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (Z_TYPE(flags) != IS_LONG) {
|
||||
zend_error_noreturn(E_ERROR,
|
||||
"Attribute::__construct(): Argument #1 ($flags) must must be of type int, %s given",
|
||||
zend_zval_type_name(&flags)
|
||||
);
|
||||
}
|
||||
|
||||
if (Z_LVAL(flags) & ~ZEND_ATTRIBUTE_FLAGS) {
|
||||
zend_error_noreturn(E_ERROR, "Invalid attribute flags specified");
|
||||
}
|
||||
|
||||
zval_ptr_dtor(&flags);
|
||||
}
|
||||
}
|
||||
|
||||
ZEND_API zend_attributes_internal_validator zend_attribute_get_validator(zend_string *lcname)
|
||||
ZEND_METHOD(Attribute, __construct)
|
||||
{
|
||||
return zend_hash_find_ptr(&internal_validators, lcname);
|
||||
zend_long flags = ZEND_ATTRIBUTE_TARGET_ALL;
|
||||
|
||||
ZEND_PARSE_PARAMETERS_START(0, 1)
|
||||
Z_PARAM_OPTIONAL
|
||||
Z_PARAM_LONG(flags)
|
||||
ZEND_PARSE_PARAMETERS_END();
|
||||
|
||||
ZVAL_LONG(OBJ_PROP_NUM(Z_OBJ_P(ZEND_THIS), 0), flags);
|
||||
}
|
||||
|
||||
static zend_attribute *get_attribute(HashTable *attributes, zend_string *lcname, uint32_t offset)
|
||||
@ -70,6 +115,65 @@ ZEND_API zend_attribute *zend_get_parameter_attribute_str(HashTable *attributes,
|
||||
return get_attribute_str(attributes, str, len, offset + 1);
|
||||
}
|
||||
|
||||
ZEND_API int zend_get_attribute_value(zval *ret, zend_attribute *attr, uint32_t i, zend_class_entry *scope)
|
||||
{
|
||||
if (i >= attr->argc) {
|
||||
return FAILURE;
|
||||
}
|
||||
|
||||
ZVAL_COPY_OR_DUP(ret, &attr->argv[i]);
|
||||
|
||||
if (Z_TYPE_P(ret) == IS_CONSTANT_AST) {
|
||||
if (SUCCESS != zval_update_constant_ex(ret, scope)) {
|
||||
zval_ptr_dtor(ret);
|
||||
return FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
static const char *target_names[] = {
|
||||
"class",
|
||||
"function",
|
||||
"method",
|
||||
"property",
|
||||
"class constant",
|
||||
"parameter"
|
||||
};
|
||||
|
||||
ZEND_API zend_string *zend_get_attribute_target_names(uint32_t flags)
|
||||
{
|
||||
smart_str str = { 0 };
|
||||
|
||||
for (uint32_t i = 0; i < (sizeof(target_names) / sizeof(char *)); i++) {
|
||||
if (flags & (1 << i)) {
|
||||
if (smart_str_get_len(&str)) {
|
||||
smart_str_appends(&str, ", ");
|
||||
}
|
||||
|
||||
smart_str_appends(&str, target_names[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return smart_str_extract(&str);
|
||||
}
|
||||
|
||||
ZEND_API zend_bool zend_is_attribute_repeated(HashTable *attributes, zend_attribute *attr)
|
||||
{
|
||||
zend_attribute *other;
|
||||
|
||||
ZEND_HASH_FOREACH_PTR(attributes, other) {
|
||||
if (other != attr && other->offset == attr->offset) {
|
||||
if (zend_string_equals(other->lcname, attr->lcname)) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
} ZEND_HASH_FOREACH_END();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static zend_always_inline void free_attribute(zend_attribute *attr, int persistent)
|
||||
{
|
||||
uint32_t i;
|
||||
@ -123,34 +227,70 @@ ZEND_API zend_attribute *zend_add_attribute(HashTable **attributes, zend_bool pe
|
||||
return attr;
|
||||
}
|
||||
|
||||
ZEND_API void zend_compiler_attribute_register(zend_class_entry *ce, zend_attributes_internal_validator validator)
|
||||
static void free_internal_attribute(zval *v)
|
||||
{
|
||||
pefree(Z_PTR_P(v), 1);
|
||||
}
|
||||
|
||||
ZEND_API zend_internal_attribute *zend_internal_attribute_register(zend_class_entry *ce, uint32_t flags)
|
||||
{
|
||||
zend_internal_attribute *attr;
|
||||
|
||||
if (ce->type != ZEND_INTERNAL_CLASS) {
|
||||
zend_error_noreturn(E_ERROR, "Only internal classes can be registered as compiler attribute");
|
||||
}
|
||||
|
||||
attr = pemalloc(sizeof(zend_internal_attribute), 1);
|
||||
attr->ce = ce;
|
||||
attr->flags = flags;
|
||||
attr->validator = NULL;
|
||||
|
||||
zend_string *lcname = zend_string_tolower_ex(ce->name, 1);
|
||||
|
||||
zend_hash_update_ptr(&internal_validators, lcname, validator);
|
||||
zend_hash_update_ptr(&internal_attributes, lcname, attr);
|
||||
zend_add_class_attribute(ce, zend_ce_attribute->name, 0);
|
||||
zend_string_release(lcname);
|
||||
|
||||
zend_add_class_attribute(ce, zend_ce_php_attribute->name, 0);
|
||||
return attr;
|
||||
}
|
||||
|
||||
ZEND_API zend_internal_attribute *zend_internal_attribute_get(zend_string *lcname)
|
||||
{
|
||||
return zend_hash_find_ptr(&internal_attributes, lcname);
|
||||
}
|
||||
|
||||
void zend_register_attribute_ce(void)
|
||||
{
|
||||
zend_hash_init(&internal_validators, 8, NULL, NULL, 1);
|
||||
|
||||
zend_internal_attribute *attr;
|
||||
zend_class_entry ce;
|
||||
zend_string *str;
|
||||
zval tmp;
|
||||
|
||||
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_hash_init(&internal_attributes, 8, NULL, free_internal_attribute, 1);
|
||||
|
||||
zend_compiler_attribute_register(zend_ce_php_attribute, zend_attribute_validate_phpattribute);
|
||||
INIT_CLASS_ENTRY(ce, "Attribute", class_Attribute_methods);
|
||||
zend_ce_attribute = zend_register_internal_class(&ce);
|
||||
zend_ce_attribute->ce_flags |= ZEND_ACC_FINAL;
|
||||
|
||||
zend_declare_class_constant_long(zend_ce_attribute, ZEND_STRL("TARGET_CLASS"), ZEND_ATTRIBUTE_TARGET_CLASS);
|
||||
zend_declare_class_constant_long(zend_ce_attribute, ZEND_STRL("TARGET_FUNCTION"), ZEND_ATTRIBUTE_TARGET_FUNCTION);
|
||||
zend_declare_class_constant_long(zend_ce_attribute, ZEND_STRL("TARGET_METHOD"), ZEND_ATTRIBUTE_TARGET_METHOD);
|
||||
zend_declare_class_constant_long(zend_ce_attribute, ZEND_STRL("TARGET_PROPERTY"), ZEND_ATTRIBUTE_TARGET_PROPERTY);
|
||||
zend_declare_class_constant_long(zend_ce_attribute, ZEND_STRL("TARGET_CLASS_CONSTANT"), ZEND_ATTRIBUTE_TARGET_CLASS_CONST);
|
||||
zend_declare_class_constant_long(zend_ce_attribute, ZEND_STRL("TARGET_PARAMETER"), ZEND_ATTRIBUTE_TARGET_PARAMETER);
|
||||
zend_declare_class_constant_long(zend_ce_attribute, ZEND_STRL("TARGET_ALL"), ZEND_ATTRIBUTE_TARGET_ALL);
|
||||
zend_declare_class_constant_long(zend_ce_attribute, ZEND_STRL("IS_REPEATABLE"), ZEND_ATTRIBUTE_IS_REPEATABLE);
|
||||
|
||||
ZVAL_UNDEF(&tmp);
|
||||
str = zend_string_init(ZEND_STRL("flags"), 1);
|
||||
zend_declare_typed_property(zend_ce_attribute, str, &tmp, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_CODE(IS_LONG, 0, 0));
|
||||
zend_string_release(str);
|
||||
|
||||
attr = zend_internal_attribute_register(zend_ce_attribute, ZEND_ATTRIBUTE_TARGET_CLASS);
|
||||
attr->validator = validate_attribute;
|
||||
}
|
||||
|
||||
void zend_attributes_shutdown(void)
|
||||
{
|
||||
zend_hash_destroy(&internal_validators);
|
||||
zend_hash_destroy(&internal_attributes);
|
||||
}
|
||||
|
@ -1,3 +1,22 @@
|
||||
/*
|
||||
+----------------------------------------------------------------------+
|
||||
| Zend Engine |
|
||||
+----------------------------------------------------------------------+
|
||||
| Copyright (c) Zend Technologies Ltd. (http://www.zend.com) |
|
||||
+----------------------------------------------------------------------+
|
||||
| This source file is subject to version 2.00 of the Zend license, |
|
||||
| that is bundled with this package in the file LICENSE, and is |
|
||||
| available through the world-wide-web at the following url: |
|
||||
| http://www.zend.com/license/2_00.txt. |
|
||||
| If you did not receive a copy of the Zend license and are unable to |
|
||||
| obtain it through the world-wide-web, please send a note to |
|
||||
| license@zend.com so we can mail you a copy immediately. |
|
||||
+----------------------------------------------------------------------+
|
||||
| Authors: Benjamin Eberlei <kontakt@beberlei.de> |
|
||||
| Martin Schröder <m.schroeder2007@gmail.com> |
|
||||
+----------------------------------------------------------------------+
|
||||
*/
|
||||
|
||||
#ifndef ZEND_ATTRIBUTES_H
|
||||
#define ZEND_ATTRIBUTES_H
|
||||
|
||||
@ -7,13 +26,15 @@
|
||||
#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_TARGET_ALL ((1<<6) - 1)
|
||||
#define ZEND_ATTRIBUTE_IS_REPEATABLE (1<<6)
|
||||
#define ZEND_ATTRIBUTE_FLAGS ((1<<7) - 1)
|
||||
|
||||
#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;
|
||||
extern ZEND_API zend_class_entry *zend_ce_attribute;
|
||||
|
||||
typedef struct _zend_attribute {
|
||||
zend_string *name;
|
||||
@ -24,7 +45,11 @@ typedef struct _zend_attribute {
|
||||
zval argv[1];
|
||||
} zend_attribute;
|
||||
|
||||
typedef void (*zend_attributes_internal_validator)(zend_attribute *attr, int target);
|
||||
typedef struct _zend_internal_attribute {
|
||||
zend_class_entry *ce;
|
||||
uint32_t flags;
|
||||
void (*validator)(zend_attribute *attr, uint32_t target, zend_class_entry *scope);
|
||||
} zend_internal_attribute;
|
||||
|
||||
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);
|
||||
@ -32,8 +57,13 @@ ZEND_API zend_attribute *zend_get_attribute_str(HashTable *attributes, const cha
|
||||
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 int zend_get_attribute_value(zval *ret, zend_attribute *attr, uint32_t i, zend_class_entry *scope);
|
||||
|
||||
ZEND_API zend_string *zend_get_attribute_target_names(uint32_t targets);
|
||||
ZEND_API zend_bool zend_is_attribute_repeated(HashTable *attributes, zend_attribute *attr);
|
||||
|
||||
ZEND_API zend_internal_attribute *zend_internal_attribute_register(zend_class_entry *ce, uint32_t flags);
|
||||
ZEND_API zend_internal_attribute *zend_internal_attribute_get(zend_string *lcname);
|
||||
|
||||
ZEND_API zend_attribute *zend_add_attribute(HashTable **attributes, zend_bool persistent, uint32_t offset, zend_string *name, uint32_t argc);
|
||||
|
||||
|
8
Zend/zend_attributes.stub.php
Normal file
8
Zend/zend_attributes.stub.php
Normal file
@ -0,0 +1,8 @@
|
||||
<?php
|
||||
|
||||
/** @generate-function-entries */
|
||||
|
||||
final class Attribute
|
||||
{
|
||||
public function __construct(int $flags = Attribute::TARGET_ALL) {}
|
||||
}
|
15
Zend/zend_attributes_arginfo.h
Normal file
15
Zend/zend_attributes_arginfo.h
Normal file
@ -0,0 +1,15 @@
|
||||
/* This is a generated file, edit the .stub.php file instead.
|
||||
* Stub hash: 54eede8541597ec2ac5c04e31d14e2db7e8c5556 */
|
||||
|
||||
ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Attribute___construct, 0, 0, 0)
|
||||
ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, flags, IS_LONG, 0, "Attribute::TARGET_ALL")
|
||||
ZEND_END_ARG_INFO()
|
||||
|
||||
|
||||
ZEND_METHOD(Attribute, __construct);
|
||||
|
||||
|
||||
static const zend_function_entry class_Attribute_methods[] = {
|
||||
ZEND_ME(Attribute, __construct, arginfo_class_Attribute___construct, ZEND_ACC_PUBLIC)
|
||||
ZEND_FE_END
|
||||
};
|
@ -5718,22 +5718,27 @@ 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) /* {{{ */
|
||||
static void zend_compile_attributes(HashTable **attributes, zend_ast *ast, uint32_t offset, uint32_t target) /* {{{ */
|
||||
{
|
||||
zend_attribute *attr;
|
||||
zend_internal_attribute *config;
|
||||
|
||||
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_ASSERT(list->child[i]->kind == ZEND_AST_ATTRIBUTE);
|
||||
|
||||
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);
|
||||
attr = zend_add_attribute(attributes, 0, offset, name, args ? args->children : 0);
|
||||
zend_string_release(name);
|
||||
|
||||
// Populate arguments
|
||||
/* Populate arguments */
|
||||
if (args) {
|
||||
ZEND_ASSERT(args->kind == ZEND_AST_ARG_LIST);
|
||||
|
||||
@ -5741,14 +5746,33 @@ static void zend_compile_attributes(HashTable **attributes, zend_ast *ast, uint3
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/* Validate attributes in a secondary loop (needed to detect repeated attributes). */
|
||||
ZEND_HASH_FOREACH_PTR(*attributes, attr) {
|
||||
if (attr->offset != offset || NULL == (config = zend_internal_attribute_get(attr->lcname))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!(target & (config->flags & ZEND_ATTRIBUTE_TARGET_ALL))) {
|
||||
zend_string *location = zend_get_attribute_target_names(target);
|
||||
zend_string *allowed = zend_get_attribute_target_names(config->flags);
|
||||
|
||||
zend_error_noreturn(E_ERROR, "Attribute \"%s\" cannot target %s (allowed targets: %s)",
|
||||
ZSTR_VAL(attr->name), ZSTR_VAL(location), ZSTR_VAL(allowed)
|
||||
);
|
||||
}
|
||||
|
||||
if (!(config->flags & ZEND_ATTRIBUTE_IS_REPEATABLE)) {
|
||||
if (zend_is_attribute_repeated(*attributes, attr)) {
|
||||
zend_error_noreturn(E_ERROR, "Attribute \"%s\" must not be repeated", ZSTR_VAL(attr->name));
|
||||
}
|
||||
}
|
||||
|
||||
if (config->validator != NULL) {
|
||||
config->validator(attr, target, CG(active_class_entry));
|
||||
}
|
||||
} ZEND_HASH_FOREACH_END();
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
|
@ -139,8 +139,10 @@ typedef struct _type_reference {
|
||||
|
||||
/* Struct for attributes */
|
||||
typedef struct _attribute_reference {
|
||||
HashTable *attributes;
|
||||
zend_attribute *data;
|
||||
zend_class_entry *scope;
|
||||
uint32_t target;
|
||||
} attribute_reference;
|
||||
|
||||
typedef enum {
|
||||
@ -1074,7 +1076,8 @@ 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)
|
||||
static void reflection_attribute_factory(zval *object, HashTable *attributes, zend_attribute *data,
|
||||
zend_class_entry *scope, uint32_t target)
|
||||
{
|
||||
reflection_object *intern;
|
||||
attribute_reference *reference;
|
||||
@ -1082,15 +1085,17 @@ static void reflection_attribute_factory(zval *object, zend_attribute *data, zen
|
||||
reflection_instantiate(reflection_attribute_ptr, object);
|
||||
intern = Z_REFLECTION_P(object);
|
||||
reference = (attribute_reference*) emalloc(sizeof(attribute_reference));
|
||||
reference->attributes = attributes;
|
||||
reference->data = data;
|
||||
reference->scope = scope;
|
||||
reference->target = target;
|
||||
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) /* {{{ */
|
||||
uint32_t offset, uint32_t target, zend_string *name, zend_class_entry *base) /* {{{ */
|
||||
{
|
||||
ZEND_ASSERT(attributes != NULL);
|
||||
|
||||
@ -1103,7 +1108,7 @@ static int read_attributes(zval *ret, HashTable *attributes, zend_class_entry *s
|
||||
|
||||
ZEND_HASH_FOREACH_PTR(attributes, attr) {
|
||||
if (attr->offset == offset && zend_string_equals(attr->lcname, filter)) {
|
||||
reflection_attribute_factory(&tmp, attr, scope);
|
||||
reflection_attribute_factory(&tmp, attributes, attr, scope, target);
|
||||
add_next_index_zval(ret, &tmp);
|
||||
}
|
||||
} ZEND_HASH_FOREACH_END();
|
||||
@ -1135,7 +1140,7 @@ static int read_attributes(zval *ret, HashTable *attributes, zend_class_entry *s
|
||||
}
|
||||
}
|
||||
|
||||
reflection_attribute_factory(&tmp, attr, scope);
|
||||
reflection_attribute_factory(&tmp, attributes, attr, scope, target);
|
||||
add_next_index_zval(ret, &tmp);
|
||||
} ZEND_HASH_FOREACH_END();
|
||||
|
||||
@ -1144,7 +1149,7 @@ static int read_attributes(zval *ret, HashTable *attributes, zend_class_entry *s
|
||||
/* }}} */
|
||||
|
||||
static void reflect_attributes(INTERNAL_FUNCTION_PARAMETERS, HashTable *attributes,
|
||||
uint32_t offset, zend_class_entry *scope) /* {{{ */
|
||||
uint32_t offset, zend_class_entry *scope, uint32_t target) /* {{{ */
|
||||
{
|
||||
zend_string *name = NULL;
|
||||
zend_long flags = 0;
|
||||
@ -1177,7 +1182,7 @@ static void reflect_attributes(INTERNAL_FUNCTION_PARAMETERS, HashTable *attribut
|
||||
|
||||
array_init(return_value);
|
||||
|
||||
if (FAILURE == read_attributes(return_value, attributes, scope, offset, name, base)) {
|
||||
if (FAILURE == read_attributes(return_value, attributes, scope, offset, target, name, base)) {
|
||||
RETURN_THROWS();
|
||||
}
|
||||
}
|
||||
@ -1755,10 +1760,17 @@ ZEND_METHOD(ReflectionFunctionAbstract, getAttributes)
|
||||
{
|
||||
reflection_object *intern;
|
||||
zend_function *fptr;
|
||||
uint32_t target;
|
||||
|
||||
GET_REFLECTION_OBJECT_PTR(fptr);
|
||||
|
||||
reflect_attributes(INTERNAL_FUNCTION_PARAM_PASSTHRU, fptr->common.attributes, 0, fptr->common.scope);
|
||||
if (fptr->common.scope) {
|
||||
target = ZEND_ATTRIBUTE_TARGET_METHOD;
|
||||
} else {
|
||||
target = ZEND_ATTRIBUTE_TARGET_FUNCTION;
|
||||
}
|
||||
|
||||
reflect_attributes(INTERNAL_FUNCTION_PARAM_PASSTHRU, fptr->common.attributes, 0, fptr->common.scope, target);
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
@ -2696,7 +2708,7 @@ ZEND_METHOD(ReflectionParameter, getAttributes)
|
||||
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);
|
||||
reflect_attributes(INTERNAL_FUNCTION_PARAM_PASSTHRU, attributes, param->offset + 1, scope, ZEND_ATTRIBUTE_TARGET_PARAMETER);
|
||||
}
|
||||
|
||||
/* {{{ proto public int ReflectionParameter::getPosition()
|
||||
@ -3779,7 +3791,7 @@ ZEND_METHOD(ReflectionClassConstant, getAttributes)
|
||||
|
||||
GET_REFLECTION_OBJECT_PTR(ref);
|
||||
|
||||
reflect_attributes(INTERNAL_FUNCTION_PARAM_PASSTHRU, ref->attributes, 0, ref->ce);
|
||||
reflect_attributes(INTERNAL_FUNCTION_PARAM_PASSTHRU, ref->attributes, 0, ref->ce, ZEND_ATTRIBUTE_TARGET_CLASS_CONST);
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
@ -4194,7 +4206,7 @@ ZEND_METHOD(ReflectionClass, getAttributes)
|
||||
|
||||
GET_REFLECTION_OBJECT_PTR(ce);
|
||||
|
||||
reflect_attributes(INTERNAL_FUNCTION_PARAM_PASSTHRU, ce->attributes, 0, ce);
|
||||
reflect_attributes(INTERNAL_FUNCTION_PARAM_PASSTHRU, ce->attributes, 0, ce, ZEND_ATTRIBUTE_TARGET_CLASS);
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
@ -5686,7 +5698,7 @@ ZEND_METHOD(ReflectionProperty, getAttributes)
|
||||
|
||||
GET_REFLECTION_OBJECT_PTR(ref);
|
||||
|
||||
reflect_attributes(INTERNAL_FUNCTION_PARAM_PASSTHRU, ref->prop->attributes, 0, ref->prop->ce);
|
||||
reflect_attributes(INTERNAL_FUNCTION_PARAM_PASSTHRU, ref->prop->attributes, 0, ref->prop->ce, ZEND_ATTRIBUTE_TARGET_PROPERTY);
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
@ -6427,18 +6439,35 @@ ZEND_METHOD(ReflectionAttribute, getName)
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
static zend_always_inline int import_attribute_value(zval *ret, zval *val, zend_class_entry *scope) /* {{{ */
|
||||
/* {{{ proto public int ReflectionAttribute::getTarget()
|
||||
* Returns the target of the attribute */
|
||||
ZEND_METHOD(ReflectionAttribute, getTarget)
|
||||
{
|
||||
ZVAL_COPY_OR_DUP(ret, val);
|
||||
reflection_object *intern;
|
||||
attribute_reference *attr;
|
||||
|
||||
if (Z_TYPE_P(val) == IS_CONSTANT_AST) {
|
||||
if (SUCCESS != zval_update_constant_ex(ret, scope)) {
|
||||
zval_ptr_dtor(ret);
|
||||
return FAILURE;
|
||||
}
|
||||
if (zend_parse_parameters_none() == FAILURE) {
|
||||
RETURN_THROWS();
|
||||
}
|
||||
GET_REFLECTION_OBJECT_PTR(attr);
|
||||
|
||||
return SUCCESS;
|
||||
RETURN_LONG(attr->target);
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
/* {{{ proto public bool ReflectionAttribute::isRepeated()
|
||||
* Returns true if the attribute is repeated */
|
||||
ZEND_METHOD(ReflectionAttribute, isRepeated)
|
||||
{
|
||||
reflection_object *intern;
|
||||
attribute_reference *attr;
|
||||
|
||||
if (zend_parse_parameters_none() == FAILURE) {
|
||||
RETURN_THROWS();
|
||||
}
|
||||
GET_REFLECTION_OBJECT_PTR(attr);
|
||||
|
||||
RETURN_BOOL(zend_is_attribute_repeated(attr->attributes, attr->data));
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
@ -6460,7 +6489,7 @@ ZEND_METHOD(ReflectionAttribute, getArguments)
|
||||
array_init(return_value);
|
||||
|
||||
for (i = 0; i < attr->data->argc; i++) {
|
||||
if (FAILURE == import_attribute_value(&tmp, &attr->data->argv[i], attr->scope)) {
|
||||
if (FAILURE == zend_get_attribute_value(&tmp, attr->data, i, attr->scope)) {
|
||||
RETURN_THROWS();
|
||||
}
|
||||
|
||||
@ -6513,6 +6542,7 @@ ZEND_METHOD(ReflectionAttribute, newInstance)
|
||||
{
|
||||
reflection_object *intern;
|
||||
attribute_reference *attr;
|
||||
zend_attribute *marker;
|
||||
|
||||
zend_class_entry *ce;
|
||||
zval obj;
|
||||
@ -6532,11 +6562,46 @@ ZEND_METHOD(ReflectionAttribute, newInstance)
|
||||
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));
|
||||
if (NULL == (marker = zend_get_attribute_str(ce->attributes, ZEND_STRL("attribute")))) {
|
||||
zend_throw_error(NULL, "Attempting to use non-attribute class '%s' as attribute", ZSTR_VAL(attr->data->name));
|
||||
RETURN_THROWS();
|
||||
}
|
||||
|
||||
if (ce->type == ZEND_USER_CLASS) {
|
||||
uint32_t flags = ZEND_ATTRIBUTE_TARGET_ALL;
|
||||
|
||||
if (marker->argc > 0) {
|
||||
zval tmp;
|
||||
|
||||
if (FAILURE == zend_get_attribute_value(&tmp, marker, 0, ce)) {
|
||||
RETURN_THROWS();
|
||||
}
|
||||
|
||||
flags = (uint32_t) Z_LVAL(tmp);
|
||||
}
|
||||
|
||||
if (!(attr->target & flags)) {
|
||||
zend_string *location = zend_get_attribute_target_names(attr->target);
|
||||
zend_string *allowed = zend_get_attribute_target_names(flags);
|
||||
|
||||
zend_throw_error(NULL, "Attribute \"%s\" cannot target %s (allowed targets: %s)",
|
||||
ZSTR_VAL(attr->data->name), ZSTR_VAL(location), ZSTR_VAL(allowed)
|
||||
);
|
||||
|
||||
zend_string_release(location);
|
||||
zend_string_release(allowed);
|
||||
|
||||
RETURN_THROWS();
|
||||
}
|
||||
|
||||
if (!(flags & ZEND_ATTRIBUTE_IS_REPEATABLE)) {
|
||||
if (zend_is_attribute_repeated(attr->attributes, attr->data)) {
|
||||
zend_throw_error(NULL, "Attribute \"%s\" must not be repeated", ZSTR_VAL(attr->data->name));
|
||||
RETURN_THROWS();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (SUCCESS != object_init_ex(&obj, ce)) {
|
||||
RETURN_THROWS();
|
||||
}
|
||||
@ -6547,7 +6612,7 @@ ZEND_METHOD(ReflectionAttribute, newInstance)
|
||||
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)) {
|
||||
if (FAILURE == zend_get_attribute_value(&args[argc], attr->data, argc, attr->scope)) {
|
||||
attribute_ctor_cleanup(&obj, args, argc);
|
||||
RETURN_THROWS();
|
||||
}
|
||||
|
@ -670,6 +670,8 @@ final class ReflectionReference
|
||||
final class ReflectionAttribute
|
||||
{
|
||||
public function getName(): string {}
|
||||
public function getTarget(): int {}
|
||||
public function isRepeated(): bool {}
|
||||
public function getArguments(): array {}
|
||||
public function newInstance(): object {}
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/* This is a generated file, edit the .stub.php file instead.
|
||||
* Stub hash: 36c0a18b7bd07ac8835ae9130f2495eceac0a176 */
|
||||
* Stub hash: 2facddef786be36211215451083b610a5d09dec7 */
|
||||
|
||||
ZEND_BEGIN_ARG_INFO_EX(arginfo_class_Reflection_getModifierNames, 0, 0, 1)
|
||||
ZEND_ARG_TYPE_INFO(0, modifiers, IS_LONG, 0)
|
||||
@ -479,6 +479,11 @@ ZEND_END_ARG_INFO()
|
||||
|
||||
#define arginfo_class_ReflectionAttribute_getName arginfo_class_ReflectionFunction___toString
|
||||
|
||||
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_ReflectionAttribute_getTarget, 0, 0, IS_LONG, 0)
|
||||
ZEND_END_ARG_INFO()
|
||||
|
||||
#define arginfo_class_ReflectionAttribute_isRepeated arginfo_class_ReflectionProperty_isPromoted
|
||||
|
||||
#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)
|
||||
@ -683,6 +688,8 @@ ZEND_METHOD(ReflectionReference, fromArrayElement);
|
||||
ZEND_METHOD(ReflectionReference, getId);
|
||||
ZEND_METHOD(ReflectionReference, __construct);
|
||||
ZEND_METHOD(ReflectionAttribute, getName);
|
||||
ZEND_METHOD(ReflectionAttribute, getTarget);
|
||||
ZEND_METHOD(ReflectionAttribute, isRepeated);
|
||||
ZEND_METHOD(ReflectionAttribute, getArguments);
|
||||
ZEND_METHOD(ReflectionAttribute, newInstance);
|
||||
ZEND_METHOD(ReflectionAttribute, __clone);
|
||||
@ -983,6 +990,8 @@ static const zend_function_entry class_ReflectionReference_methods[] = {
|
||||
|
||||
static const zend_function_entry class_ReflectionAttribute_methods[] = {
|
||||
ZEND_ME(ReflectionAttribute, getName, arginfo_class_ReflectionAttribute_getName, ZEND_ACC_PUBLIC)
|
||||
ZEND_ME(ReflectionAttribute, getTarget, arginfo_class_ReflectionAttribute_getTarget, ZEND_ACC_PUBLIC)
|
||||
ZEND_ME(ReflectionAttribute, isRepeated, arginfo_class_ReflectionAttribute_isRepeated, 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)
|
||||
|
@ -183,7 +183,7 @@ static zend_function *zend_test_class_static_method_get(zend_class_entry *ce, ze
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
void zend_attribute_validate_zendtestattribute(zend_attribute *attr, int target)
|
||||
void zend_attribute_validate_zendtestattribute(zend_attribute *attr, uint32_t target, zend_class_entry *scope)
|
||||
{
|
||||
if (target != ZEND_ATTRIBUTE_TARGET_CLASS) {
|
||||
zend_error(E_COMPILE_ERROR, "Only classes can be marked with <<ZendTestAttribute>>");
|
||||
@ -286,7 +286,11 @@ PHP_MINIT_FUNCTION(zend_test)
|
||||
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);
|
||||
{
|
||||
zend_internal_attribute *attr = zend_internal_attribute_register(zend_test_attribute, ZEND_ATTRIBUTE_TARGET_ALL);
|
||||
attr->validator = zend_attribute_validate_zendtestattribute;
|
||||
}
|
||||
|
||||
return SUCCESS;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user