Skip to content

Add ReflectorWithAttributes interface with getFirstAttribute() and hasAttribute() #13224

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 11 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
146 changes: 128 additions & 18 deletions ext/reflection/php_reflection.c
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ static zend_always_inline zval *reflection_prop_class(zval *object) {

/* Class entry pointers */
PHPAPI zend_class_entry *reflector_ptr;
PHPAPI zend_class_entry *reflector_with_attributes_ptr;
PHPAPI zend_class_entry *reflection_exception_ptr;
PHPAPI zend_class_entry *reflection_ptr;
PHPAPI zend_class_entry *reflection_function_abstract_ptr;
Expand Down Expand Up @@ -1165,7 +1166,7 @@ static void reflection_attribute_factory(zval *object, HashTable *attributes, ze
/* }}} */

static int read_attributes(zval *ret, HashTable *attributes, zend_class_entry *scope,
uint32_t offset, uint32_t target, zend_string *name, zend_class_entry *base, zend_string *filename) /* {{{ */
uint32_t offset, uint32_t target, zend_string *name, zend_class_entry *base, zend_string *filename, bool first) /* {{{ */
{
ZEND_ASSERT(attributes != NULL);

Expand All @@ -1178,7 +1179,12 @@ static int read_attributes(zval *ret, HashTable *attributes, zend_class_entry *s

ZEND_HASH_PACKED_FOREACH_PTR(attributes, attr) {
if (attr->offset == offset && zend_string_equals(attr->lcname, filter)) {
reflection_attribute_factory(&tmp, attributes, attr, scope, target, filename);
reflection_attribute_factory(first ? ret : &tmp, attributes, attr, scope, target, filename);

if (first) {
break;
}

add_next_index_zval(ret, &tmp);
}
} ZEND_HASH_FOREACH_END();
Expand Down Expand Up @@ -1210,7 +1216,12 @@ static int read_attributes(zval *ret, HashTable *attributes, zend_class_entry *s
}
}

reflection_attribute_factory(&tmp, attributes, attr, scope, target, filename);
reflection_attribute_factory(first ? ret : &tmp, attributes, attr, scope, target, filename);

if (first) {
break;
}

add_next_index_zval(ret, &tmp);
} ZEND_HASH_FOREACH_END();

Expand All @@ -1219,7 +1230,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 target, zend_string *filename) /* {{{ */
uint32_t offset, zend_class_entry *scope, uint32_t target, zend_string *filename, bool first) /* {{{ */
{
zend_string *name = NULL;
zend_long flags = 0;
Expand Down Expand Up @@ -1247,14 +1258,20 @@ static void reflect_attributes(INTERNAL_FUNCTION_PARAMETERS, HashTable *attribut
}

if (!attributes) {
RETURN_EMPTY_ARRAY();
if (first) {
RETURN_NULL();
} else {
RETURN_EMPTY_ARRAY();
}
}

array_init(return_value);
if (! first) {
array_init(return_value);
}

if (FAILURE == read_attributes(return_value, attributes, scope, offset, target, name, base, filename)) {
RETURN_THROWS();
}
if (FAILURE == read_attributes(return_value, attributes, scope, offset, target, name, base, filename, first)) {
RETURN_THROWS();
}
}
/* }}} */

Expand Down Expand Up @@ -1985,10 +2002,37 @@ ZEND_METHOD(ReflectionFunctionAbstract, getAttributes)

reflect_attributes(INTERNAL_FUNCTION_PARAM_PASSTHRU,
fptr->common.attributes, 0, fptr->common.scope, target,
fptr->type == ZEND_USER_FUNCTION ? fptr->op_array.filename : NULL);
fptr->type == ZEND_USER_FUNCTION ? fptr->op_array.filename : NULL, false);
}
/* }}} */


/* {{{ Returns the first attribute of this function */
ZEND_METHOD(ReflectionFunctionAbstract, getFirstAttribute)
{
reflection_object *intern;
zend_function *fptr;
uint32_t target;
zend_string *name = NULL;
zend_long flags = 0;

GET_REFLECTION_OBJECT_PTR(fptr);

if (fptr->common.scope && (fptr->common.fn_flags & (ZEND_ACC_CLOSURE|ZEND_ACC_FAKE_CLOSURE)) != ZEND_ACC_CLOSURE) {
target = ZEND_ATTRIBUTE_TARGET_METHOD;
} else {
target = ZEND_ATTRIBUTE_TARGET_FUNCTION;
}

if (zend_parse_parameters(ZEND_NUM_ARGS(), "|S!l", &name, &flags) == FAILURE) {
RETURN_THROWS();
}

reflect_attributes(INTERNAL_FUNCTION_PARAM_PASSTHRU,
fptr->common.attributes, 0, fptr->common.scope, target,
fptr->type == ZEND_USER_FUNCTION ? fptr->op_array.filename : NULL, true);
}
/* }}} */
/* {{{ Returns an associative array containing this function's static variables and their values */
ZEND_METHOD(ReflectionFunctionAbstract, getStaticVariables)
{
Expand Down Expand Up @@ -2866,8 +2910,26 @@ ZEND_METHOD(ReflectionParameter, getAttributes)

reflect_attributes(INTERNAL_FUNCTION_PARAM_PASSTHRU,
attributes, param->offset + 1, scope, ZEND_ATTRIBUTE_TARGET_PARAMETER,
param->fptr->type == ZEND_USER_FUNCTION ? param->fptr->op_array.filename : NULL);
param->fptr->type == ZEND_USER_FUNCTION ? param->fptr->op_array.filename : NULL, false);
}
/* }}} */

/* {{{ Get the first parameter attribute. */
ZEND_METHOD(ReflectionParameter, getFirstAttribute)
{
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, ZEND_ATTRIBUTE_TARGET_PARAMETER,
param->fptr->type == ZEND_USER_FUNCTION ? param->fptr->op_array.filename : NULL, true);
}
/* }}} */

/* {{{ Returns whether this parameter is an optional parameter */
ZEND_METHOD(ReflectionParameter, getPosition)
Expand Down Expand Up @@ -4053,7 +4115,21 @@ ZEND_METHOD(ReflectionClassConstant, getAttributes)

reflect_attributes(INTERNAL_FUNCTION_PARAM_PASSTHRU,
ref->attributes, 0, ref->ce, ZEND_ATTRIBUTE_TARGET_CLASS_CONST,
ref->ce->type == ZEND_USER_CLASS ? ref->ce->info.user.filename : NULL);
ref->ce->type == ZEND_USER_CLASS ? ref->ce->info.user.filename : NULL, false);
}
/* }}} */

/* {{{ Returns the first attribute of this constant */
ZEND_METHOD(ReflectionClassConstant, getFirstAttribute)
{
reflection_object *intern;
zend_class_constant *ref;

GET_REFLECTION_OBJECT_PTR(ref);

reflect_attributes(INTERNAL_FUNCTION_PARAM_PASSTHRU,
ref->attributes, 0, ref->ce, ZEND_ATTRIBUTE_TARGET_CLASS_CONST,
ref->ce->type == ZEND_USER_CLASS ? ref->ce->info.user.filename : NULL, true);
}
/* }}} */

Expand Down Expand Up @@ -4475,7 +4551,21 @@ ZEND_METHOD(ReflectionClass, getAttributes)

reflect_attributes(INTERNAL_FUNCTION_PARAM_PASSTHRU,
ce->attributes, 0, ce, ZEND_ATTRIBUTE_TARGET_CLASS,
ce->type == ZEND_USER_CLASS ? ce->info.user.filename : NULL);
ce->type == ZEND_USER_CLASS ? ce->info.user.filename : NULL, false);
}
/* }}} */

/* {{{ Returns the first attribute for this class */
ZEND_METHOD(ReflectionClass, getFirstAttribute)
{
reflection_object *intern;
zend_class_entry *ce;

GET_REFLECTION_OBJECT_PTR(ce);

reflect_attributes(INTERNAL_FUNCTION_PARAM_PASSTHRU,
ce->attributes, 0, ce, ZEND_ATTRIBUTE_TARGET_CLASS,
ce->type == ZEND_USER_CLASS ? ce->info.user.filename : NULL, true);
}
/* }}} */

Expand Down Expand Up @@ -5912,7 +6002,25 @@ ZEND_METHOD(ReflectionProperty, getAttributes)

reflect_attributes(INTERNAL_FUNCTION_PARAM_PASSTHRU,
ref->prop->attributes, 0, ref->prop->ce, ZEND_ATTRIBUTE_TARGET_PROPERTY,
ref->prop->ce->type == ZEND_USER_CLASS ? ref->prop->ce->info.user.filename : NULL);
ref->prop->ce->type == ZEND_USER_CLASS ? ref->prop->ce->info.user.filename : NULL, false);
}
/* }}} */

/* {{{ Returns the first attribute of this property */
ZEND_METHOD(ReflectionProperty, getFirstAttribute)
{
reflection_object *intern;
property_reference *ref;

GET_REFLECTION_OBJECT_PTR(ref);

if (ref->prop == NULL) {
RETURN_EMPTY_ARRAY();
}

reflect_attributes(INTERNAL_FUNCTION_PARAM_PASSTHRU,
ref->prop->attributes, 0, ref->prop->ce, ZEND_ATTRIBUTE_TARGET_PROPERTY,
ref->prop->ce->type == ZEND_USER_CLASS ? ref->prop->ce->info.user.filename : NULL, true);
}
/* }}} */

Expand Down Expand Up @@ -7402,7 +7510,9 @@ PHP_MINIT_FUNCTION(reflection) /* {{{ */

reflector_ptr = register_class_Reflector(zend_ce_stringable);

reflection_function_abstract_ptr = register_class_ReflectionFunctionAbstract(reflector_ptr);
reflector_with_attributes_ptr = register_class_ReflectorWithAttributes();

reflection_function_abstract_ptr = register_class_ReflectionFunctionAbstract(reflector_ptr, reflector_with_attributes_ptr);
reflection_function_abstract_ptr->default_object_handlers = &reflection_object_handlers;
reflection_function_abstract_ptr->create_object = reflection_objects_new;

Expand All @@ -7414,7 +7524,7 @@ PHP_MINIT_FUNCTION(reflection) /* {{{ */
reflection_generator_ptr->create_object = reflection_objects_new;
reflection_generator_ptr->default_object_handlers = &reflection_object_handlers;

reflection_parameter_ptr = register_class_ReflectionParameter(reflector_ptr);
reflection_parameter_ptr = register_class_ReflectionParameter(reflector_ptr, reflector_with_attributes_ptr);
reflection_parameter_ptr->create_object = reflection_objects_new;
reflection_parameter_ptr->default_object_handlers = &reflection_object_handlers;

Expand All @@ -7438,15 +7548,15 @@ PHP_MINIT_FUNCTION(reflection) /* {{{ */
reflection_method_ptr->create_object = reflection_objects_new;
reflection_method_ptr->default_object_handlers = &reflection_object_handlers;

reflection_class_ptr = register_class_ReflectionClass(reflector_ptr);
reflection_class_ptr = register_class_ReflectionClass(reflector_ptr, reflector_with_attributes_ptr);
reflection_class_ptr->create_object = reflection_objects_new;
reflection_class_ptr->default_object_handlers = &reflection_object_handlers;

reflection_object_ptr = register_class_ReflectionObject(reflection_class_ptr);
reflection_object_ptr->create_object = reflection_objects_new;
reflection_object_ptr->default_object_handlers = &reflection_object_handlers;

reflection_property_ptr = register_class_ReflectionProperty(reflector_ptr);
reflection_property_ptr = register_class_ReflectionProperty(reflector_ptr, reflector_with_attributes_ptr);
reflection_property_ptr->create_object = reflection_objects_new;
reflection_property_ptr->default_object_handlers = &reflection_object_handlers;

Expand Down
1 change: 1 addition & 0 deletions ext/reflection/php_reflection.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ BEGIN_EXTERN_C()

/* Class entry pointers */
extern PHPAPI zend_class_entry *reflector_ptr;
extern PHPAPI zend_class_entry *reflector_with_attributes_ptr;
extern PHPAPI zend_class_entry *reflection_exception_ptr;
extern PHPAPI zend_class_entry *reflection_ptr;
extern PHPAPI zend_class_entry *reflection_function_abstract_ptr;
Expand Down
31 changes: 24 additions & 7 deletions ext/reflection/php_reflection.stub.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,15 @@ interface Reflector extends Stringable
{
}

interface ReflectorWithAttributes
{
public function getAttributes(?string $name = null, int $flags = 0): array;

public function getFirstAttribute(?string $name = null, int $flags = 0): ?ReflectionAttribute;
}

/** @not-serializable */
abstract class ReflectionFunctionAbstract implements Reflector
abstract class ReflectionFunctionAbstract implements Reflector, ReflectorWithAttributes
{
public string $name;

Expand Down Expand Up @@ -111,7 +118,9 @@ public function hasTentativeReturnType(): bool {}

public function getTentativeReturnType(): ?ReflectionType {}

public function getAttributes(?string $name = null, int $flags = 0): array {}
public function getAttributes(?string $name = null, int $flags = 0): array;

public function getFirstAttribute(?string $name = null, int $flags = 0): ?ReflectionAttribute;
}

class ReflectionFunction extends ReflectionFunctionAbstract
Expand Down Expand Up @@ -234,7 +243,7 @@ public function setAccessible(bool $accessible): void {}
}

/** @not-serializable */
class ReflectionClass implements Reflector
class ReflectionClass implements Reflector, ReflectorWithAttributes
{
/**
* @cvalue ZEND_ACC_IMPLICIT_ABSTRACT_CLASS
Expand Down Expand Up @@ -414,6 +423,8 @@ public function getNamespaceName(): string {}
public function getShortName(): string {}

public function getAttributes(?string $name = null, int $flags = 0): array {}

public function getFirstAttribute(?string $name = null, int $flags = 0): ?ReflectionAttribute;
}

class ReflectionObject extends ReflectionClass
Expand All @@ -422,7 +433,7 @@ public function __construct(object $object) {}
}

/** @not-serializable */
class ReflectionProperty implements Reflector
class ReflectionProperty implements Reflector, ReflectorWithAttributes
{
/** @cvalue ZEND_ACC_STATIC */
public const int IS_STATIC = UNKNOWN;
Expand Down Expand Up @@ -500,6 +511,8 @@ public function hasDefaultValue(): bool {}
public function getDefaultValue(): mixed {}

public function getAttributes(?string $name = null, int $flags = 0): array {}

public function getFirstAttribute(?string $name = null, int $flags = 0): ?ReflectionAttribute;
}

/** @not-serializable */
Expand Down Expand Up @@ -550,19 +563,21 @@ public function getDeclaringClass(): ReflectionClass {}
/** @tentative-return-type */
public function getDocComment(): string|false {}

public function getAttributes(?string $name = null, int $flags = 0): array {}

public function isEnumCase(): bool {}

public function isDeprecated(): bool {}

public function hasType(): bool {}

public function getType(): ?ReflectionType {}

public function getAttributes(?string $name = null, int $flags = 0): array {}

public function getFirstAttribute(?string $name = null, int $flags = 0): ?ReflectionAttribute;
}

/** @not-serializable */
class ReflectionParameter implements Reflector
class ReflectionParameter implements Reflector, ReflectorWithAttributes
{
public string $name;

Expand Down Expand Up @@ -640,6 +655,8 @@ public function isVariadic(): bool {}
public function isPromoted(): bool {}

public function getAttributes(?string $name = null, int $flags = 0): array {}

public function getFirstAttribute(?string $name = null, int $flags = 0): ?ReflectionAttribute;
}

/** @not-serializable */
Expand Down
Loading
Loading