From 6cd4a6348b3ca8505101451edd9bf51714b9752f Mon Sep 17 00:00:00 2001 From: Bob Weinand Date: Tue, 21 Jun 2022 15:19:01 +0200 Subject: [PATCH 1/5] Allow for arbitrary (class) attributes in stubs This can be easily extended to other types of attributes. Signed-off-by: Bob Weinand --- Zend/zend_attributes.c | 43 +++++++--------- Zend/zend_attributes.h | 2 +- Zend/zend_attributes.stub.php | 45 ++++++++++++++++ Zend/zend_attributes_arginfo.h | 74 ++++++++++++++++++++++++++- Zend/zend_builtin_functions_arginfo.h | 4 +- build/gen_stub.php | 53 ++++++++++++++----- ext/oci8/oci8_arginfo.h | 8 ++- ext/zend_test/test.c | 4 +- ext/zend_test/test.stub.php | 2 + ext/zend_test/test_arginfo.h | 14 ++++- 10 files changed, 203 insertions(+), 46 deletions(-) diff --git a/Zend/zend_attributes.c b/Zend/zend_attributes.c index 6330887b29084..dabde19623f72 100644 --- a/Zend/zend_attributes.c +++ b/Zend/zend_attributes.c @@ -317,27 +317,31 @@ 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_API zend_internal_attribute *zend_internal_attribute_register(zend_class_entry *ce) { zend_internal_attribute *internal_attr; + zend_attribute *attr; if (ce->type != ZEND_INTERNAL_CLASS) { zend_error_noreturn(E_ERROR, "Only internal classes can be registered as compiler attribute"); } - internal_attr = pemalloc(sizeof(zend_internal_attribute), 1); - internal_attr->ce = ce; - internal_attr->flags = flags; - internal_attr->validator = NULL; + ZEND_HASH_FOREACH_PTR(ce->attributes, attr) { + if (zend_string_equals(attr->name, zend_ce_attribute->name)) { + internal_attr = pemalloc(sizeof(zend_internal_attribute), 1); + internal_attr->ce = ce; + internal_attr->flags = Z_LVAL(attr->args[0].value); + internal_attr->validator = NULL; - zend_string *lcname = zend_string_tolower_ex(ce->name, 1); + zend_string *lcname = zend_string_tolower_ex(ce->name, 1); + zend_hash_update_ptr(&internal_attributes, lcname, internal_attr); + zend_string_release(lcname); - zend_hash_update_ptr(&internal_attributes, lcname, internal_attr); - zend_attribute *attr = zend_add_class_attribute(ce, zend_ce_attribute->name, 1); - ZVAL_LONG(&attr->args[0].value, flags); - zend_string_release(lcname); + return internal_attr; + } + } ZEND_HASH_FOREACH_END(); - return internal_attr; + zend_error_noreturn(E_ERROR, "Classes must be first marked as attribute before being able to be registered as internal attribute class"); } ZEND_API zend_internal_attribute *zend_internal_attribute_get(zend_string *lcname) @@ -352,27 +356,18 @@ void zend_register_attribute_ce(void) zend_hash_init(&internal_attributes, 8, NULL, free_internal_attribute, 1); zend_ce_attribute = register_class_Attribute(); - attr = zend_internal_attribute_register(zend_ce_attribute, ZEND_ATTRIBUTE_TARGET_CLASS); + attr = zend_internal_attribute_register(zend_ce_attribute); attr->validator = validate_attribute; - 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); - zend_ce_return_type_will_change_attribute = register_class_ReturnTypeWillChange(); - zend_internal_attribute_register(zend_ce_return_type_will_change_attribute, ZEND_ATTRIBUTE_TARGET_METHOD); + zend_internal_attribute_register(zend_ce_return_type_will_change_attribute); zend_ce_allow_dynamic_properties = register_class_AllowDynamicProperties(); - attr = zend_internal_attribute_register(zend_ce_allow_dynamic_properties, ZEND_ATTRIBUTE_TARGET_CLASS); + attr = zend_internal_attribute_register(zend_ce_allow_dynamic_properties); attr->validator = validate_allow_dynamic_properties; zend_ce_sensitive_parameter = register_class_SensitiveParameter(); - attr = zend_internal_attribute_register(zend_ce_sensitive_parameter, ZEND_ATTRIBUTE_TARGET_PARAMETER); + zend_internal_attribute_register(zend_ce_sensitive_parameter); memcpy(&attributes_object_handlers_sensitive_parameter_value, &std_object_handlers, sizeof(zend_object_handlers)); attributes_object_handlers_sensitive_parameter_value.get_properties_for = attributes_sensitive_parameter_value_get_properties_for; diff --git a/Zend/zend_attributes.h b/Zend/zend_attributes.h index 6ffd3d89f94a0..2f0655b74bf78 100644 --- a/Zend/zend_attributes.h +++ b/Zend/zend_attributes.h @@ -77,7 +77,7 @@ ZEND_API zend_result zend_get_attribute_value(zval *ret, zend_attribute *attr, u ZEND_API zend_string *zend_get_attribute_target_names(uint32_t targets); ZEND_API 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_register(zend_class_entry *ce); ZEND_API zend_internal_attribute *zend_internal_attribute_get(zend_string *lcname); ZEND_API zend_attribute *zend_add_attribute( diff --git a/Zend/zend_attributes.stub.php b/Zend/zend_attributes.stub.php index 842ed9229cd6e..34056404b3aca 100644 --- a/Zend/zend_attributes.stub.php +++ b/Zend/zend_attributes.stub.php @@ -2,18 +2,62 @@ /** @generate-class-entries */ +#[Attribute(Attribute::TARGET_CLASS)] final class Attribute { + /** + * @var int + * @cname ZEND_ATTRIBUTE_TARGET_CLASS + */ + const TARGET_CLASS = UNKNOWN; + /** + * @var int + * @cname ZEND_ATTRIBUTE_TARGET_FUNCTION + */ + const TARGET_FUNCTION = UNKNOWN; + /** + * @var int + * @cname ZEND_ATTRIBUTE_TARGET_METHOD + */ + const TARGET_METHOD = UNKNOWN; + /** + * @var int + * @cname ZEND_ATTRIBUTE_TARGET_PROPERTY + */ + const TARGET_PROPERTY = UNKNOWN; + /** + * @var int + * @cname ZEND_ATTRIBUTE_TARGET_CLASS_CONST + */ + const TARGET_CLASS_CONSTANT = UNKNOWN; + /** + * @var int + * @cname ZEND_ATTRIBUTE_TARGET_PARAMETER + */ + const TARGET_PARAMETER = UNKNOWN; + /** + * @var int + * @cname ZEND_ATTRIBUTE_TARGET_ALL + */ + const TARGET_ALL = UNKNOWN; + /** + * @var int + * @cname ZEND_ATTRIBUTE_IS_REPEATABLE + */ + const IS_REPEATABLE = UNKNOWN; + public int $flags; public function __construct(int $flags = Attribute::TARGET_ALL) {} } +#[Attribute(Attribute::TARGET_METHOD)] final class ReturnTypeWillChange { public function __construct() {} } +#[Attribute(Attribute::TARGET_CLASS)] final class AllowDynamicProperties { public function __construct() {} @@ -22,6 +66,7 @@ public function __construct() {} /** * @strict-properties */ +#[Attribute(Attribute::TARGET_PARAMETER)] final class SensitiveParameter { public function __construct() {} diff --git a/Zend/zend_attributes_arginfo.h b/Zend/zend_attributes_arginfo.h index 7c624949bf24b..7cbc92272cad5 100644 --- a/Zend/zend_attributes_arginfo.h +++ b/Zend/zend_attributes_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 5d9a092c1f0da5f32d9a161cc5166ed794ffe8e9 */ + * Stub hash: a07e5020fd36cda191c1f3b4fca180157bd74cbc */ 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") @@ -71,11 +71,65 @@ static zend_class_entry *register_class_Attribute(void) class_entry = zend_register_internal_class_ex(&ce, NULL); class_entry->ce_flags |= ZEND_ACC_FINAL; + zval const_TARGET_CLASS_value; + ZVAL_LONG(&const_TARGET_CLASS_value, ZEND_ATTRIBUTE_TARGET_CLASS); + zend_string *const_TARGET_CLASS_name = zend_string_init_interned("TARGET_CLASS", sizeof("TARGET_CLASS") - 1, 1); + zend_declare_class_constant_ex(class_entry, const_TARGET_CLASS_name, &const_TARGET_CLASS_value, ZEND_ACC_PUBLIC, NULL); + zend_string_release(const_TARGET_CLASS_name); + + zval const_TARGET_FUNCTION_value; + ZVAL_LONG(&const_TARGET_FUNCTION_value, ZEND_ATTRIBUTE_TARGET_FUNCTION); + zend_string *const_TARGET_FUNCTION_name = zend_string_init_interned("TARGET_FUNCTION", sizeof("TARGET_FUNCTION") - 1, 1); + zend_declare_class_constant_ex(class_entry, const_TARGET_FUNCTION_name, &const_TARGET_FUNCTION_value, ZEND_ACC_PUBLIC, NULL); + zend_string_release(const_TARGET_FUNCTION_name); + + zval const_TARGET_METHOD_value; + ZVAL_LONG(&const_TARGET_METHOD_value, ZEND_ATTRIBUTE_TARGET_METHOD); + zend_string *const_TARGET_METHOD_name = zend_string_init_interned("TARGET_METHOD", sizeof("TARGET_METHOD") - 1, 1); + zend_declare_class_constant_ex(class_entry, const_TARGET_METHOD_name, &const_TARGET_METHOD_value, ZEND_ACC_PUBLIC, NULL); + zend_string_release(const_TARGET_METHOD_name); + + zval const_TARGET_PROPERTY_value; + ZVAL_LONG(&const_TARGET_PROPERTY_value, ZEND_ATTRIBUTE_TARGET_PROPERTY); + zend_string *const_TARGET_PROPERTY_name = zend_string_init_interned("TARGET_PROPERTY", sizeof("TARGET_PROPERTY") - 1, 1); + zend_declare_class_constant_ex(class_entry, const_TARGET_PROPERTY_name, &const_TARGET_PROPERTY_value, ZEND_ACC_PUBLIC, NULL); + zend_string_release(const_TARGET_PROPERTY_name); + + zval const_TARGET_CLASS_CONSTANT_value; + ZVAL_LONG(&const_TARGET_CLASS_CONSTANT_value, ZEND_ATTRIBUTE_TARGET_CLASS_CONST); + zend_string *const_TARGET_CLASS_CONSTANT_name = zend_string_init_interned("TARGET_CLASS_CONSTANT", sizeof("TARGET_CLASS_CONSTANT") - 1, 1); + zend_declare_class_constant_ex(class_entry, const_TARGET_CLASS_CONSTANT_name, &const_TARGET_CLASS_CONSTANT_value, ZEND_ACC_PUBLIC, NULL); + zend_string_release(const_TARGET_CLASS_CONSTANT_name); + + zval const_TARGET_PARAMETER_value; + ZVAL_LONG(&const_TARGET_PARAMETER_value, ZEND_ATTRIBUTE_TARGET_PARAMETER); + zend_string *const_TARGET_PARAMETER_name = zend_string_init_interned("TARGET_PARAMETER", sizeof("TARGET_PARAMETER") - 1, 1); + zend_declare_class_constant_ex(class_entry, const_TARGET_PARAMETER_name, &const_TARGET_PARAMETER_value, ZEND_ACC_PUBLIC, NULL); + zend_string_release(const_TARGET_PARAMETER_name); + + zval const_TARGET_ALL_value; + ZVAL_LONG(&const_TARGET_ALL_value, ZEND_ATTRIBUTE_TARGET_ALL); + zend_string *const_TARGET_ALL_name = zend_string_init_interned("TARGET_ALL", sizeof("TARGET_ALL") - 1, 1); + zend_declare_class_constant_ex(class_entry, const_TARGET_ALL_name, &const_TARGET_ALL_value, ZEND_ACC_PUBLIC, NULL); + zend_string_release(const_TARGET_ALL_name); + + zval const_IS_REPEATABLE_value; + ZVAL_LONG(&const_IS_REPEATABLE_value, ZEND_ATTRIBUTE_IS_REPEATABLE); + zend_string *const_IS_REPEATABLE_name = zend_string_init_interned("IS_REPEATABLE", sizeof("IS_REPEATABLE") - 1, 1); + zend_declare_class_constant_ex(class_entry, const_IS_REPEATABLE_name, &const_IS_REPEATABLE_value, ZEND_ACC_PUBLIC, NULL); + zend_string_release(const_IS_REPEATABLE_name); + zval property_flags_default_value; ZVAL_UNDEF(&property_flags_default_value); zend_string *property_flags_name = zend_string_init("flags", sizeof("flags") - 1, 1); zend_declare_typed_property(class_entry, property_flags_name, &property_flags_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); zend_string_release(property_flags_name); + zend_string *attribute_name_Attribute_class_Attribute = zend_string_init("Attribute", sizeof("Attribute") - 1, 1); + zend_attribute *attribute_Attribute_class_Attribute = zend_add_class_attribute(class_entry, attribute_name_Attribute_class_Attribute, 1); + zend_string_release(attribute_name_Attribute_class_Attribute); + zval attribute_Attribute_class_Attribute_arg0; + ZVAL_LONG(&attribute_Attribute_class_Attribute_arg0, ZEND_ATTRIBUTE_TARGET_CLASS); + ZVAL_COPY_VALUE(&attribute_Attribute_class_Attribute->args[0].value, &attribute_Attribute_class_Attribute_arg0); return class_entry; } @@ -87,6 +141,12 @@ static zend_class_entry *register_class_ReturnTypeWillChange(void) INIT_CLASS_ENTRY(ce, "ReturnTypeWillChange", class_ReturnTypeWillChange_methods); class_entry = zend_register_internal_class_ex(&ce, NULL); class_entry->ce_flags |= ZEND_ACC_FINAL; + zend_string *attribute_name_Attribute_class_ReturnTypeWillChange = zend_string_init("Attribute", sizeof("Attribute") - 1, 1); + zend_attribute *attribute_Attribute_class_ReturnTypeWillChange = zend_add_class_attribute(class_entry, attribute_name_Attribute_class_ReturnTypeWillChange, 1); + zend_string_release(attribute_name_Attribute_class_ReturnTypeWillChange); + zval attribute_Attribute_class_ReturnTypeWillChange_arg0; + ZVAL_LONG(&attribute_Attribute_class_ReturnTypeWillChange_arg0, ZEND_ATTRIBUTE_TARGET_METHOD); + ZVAL_COPY_VALUE(&attribute_Attribute_class_ReturnTypeWillChange->args[0].value, &attribute_Attribute_class_ReturnTypeWillChange_arg0); return class_entry; } @@ -98,6 +158,12 @@ static zend_class_entry *register_class_AllowDynamicProperties(void) INIT_CLASS_ENTRY(ce, "AllowDynamicProperties", class_AllowDynamicProperties_methods); class_entry = zend_register_internal_class_ex(&ce, NULL); class_entry->ce_flags |= ZEND_ACC_FINAL; + zend_string *attribute_name_Attribute_class_AllowDynamicProperties = zend_string_init("Attribute", sizeof("Attribute") - 1, 1); + zend_attribute *attribute_Attribute_class_AllowDynamicProperties = zend_add_class_attribute(class_entry, attribute_name_Attribute_class_AllowDynamicProperties, 1); + zend_string_release(attribute_name_Attribute_class_AllowDynamicProperties); + zval attribute_Attribute_class_AllowDynamicProperties_arg0; + ZVAL_LONG(&attribute_Attribute_class_AllowDynamicProperties_arg0, ZEND_ATTRIBUTE_TARGET_CLASS); + ZVAL_COPY_VALUE(&attribute_Attribute_class_AllowDynamicProperties->args[0].value, &attribute_Attribute_class_AllowDynamicProperties_arg0); return class_entry; } @@ -109,6 +175,12 @@ static zend_class_entry *register_class_SensitiveParameter(void) INIT_CLASS_ENTRY(ce, "SensitiveParameter", class_SensitiveParameter_methods); class_entry = zend_register_internal_class_ex(&ce, NULL); class_entry->ce_flags |= ZEND_ACC_FINAL|ZEND_ACC_NO_DYNAMIC_PROPERTIES; + zend_string *attribute_name_Attribute_class_SensitiveParameter = zend_string_init("Attribute", sizeof("Attribute") - 1, 1); + zend_attribute *attribute_Attribute_class_SensitiveParameter = zend_add_class_attribute(class_entry, attribute_name_Attribute_class_SensitiveParameter, 1); + zend_string_release(attribute_name_Attribute_class_SensitiveParameter); + zval attribute_Attribute_class_SensitiveParameter_arg0; + ZVAL_LONG(&attribute_Attribute_class_SensitiveParameter_arg0, ZEND_ATTRIBUTE_TARGET_PARAMETER); + ZVAL_COPY_VALUE(&attribute_Attribute_class_SensitiveParameter->args[0].value, &attribute_Attribute_class_SensitiveParameter_arg0); return class_entry; } diff --git a/Zend/zend_builtin_functions_arginfo.h b/Zend/zend_builtin_functions_arginfo.h index 1dbc4255945a6..1ca64e227ef55 100644 --- a/Zend/zend_builtin_functions_arginfo.h +++ b/Zend/zend_builtin_functions_arginfo.h @@ -354,7 +354,9 @@ static zend_class_entry *register_class_stdClass(void) INIT_CLASS_ENTRY(ce, "stdClass", class_stdClass_methods); class_entry = zend_register_internal_class_ex(&ce, NULL); class_entry->ce_flags |= ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES; - zend_add_class_attribute(class_entry, zend_ce_allow_dynamic_properties->name, 0); + zend_string *attribute_name_AllowDynamicProperties_class_stdClass = zend_string_init("AllowDynamicProperties", sizeof("AllowDynamicProperties") - 1, 1); + zend_add_class_attribute(class_entry, attribute_name_AllowDynamicProperties_class_stdClass, 0); + zend_string_release(attribute_name_AllowDynamicProperties_class_stdClass); return class_entry; } diff --git a/build/gen_stub.php b/build/gen_stub.php index 202ab1fdc6ed4..1244075598ae3 100755 --- a/build/gen_stub.php +++ b/build/gen_stub.php @@ -2272,8 +2272,8 @@ class ClassInfo { public $isDeprecated; /** @var bool */ public $isStrictProperties; - /** @var bool */ - public $allowsDynamicProperties; + /** @var array{0: string, 1: \PhpParser\Node\Arg[]}[] */ + public $attributes; /** @var bool */ public $isNotSerializable; /** @var Name[] */ @@ -2292,6 +2292,7 @@ class ClassInfo { public $cond; /** + * @param array{0: string, 1: \PhpParser\Node\Arg[]}[] $attributes * @param Name[] $extends * @param Name[] $implements * @param ConstInfo[] $constInfos @@ -2307,7 +2308,7 @@ public function __construct( ?SimpleType $enumBackingType, bool $isDeprecated, bool $isStrictProperties, - bool $allowsDynamicProperties, + array $attributes, bool $isNotSerializable, array $extends, array $implements, @@ -2324,7 +2325,7 @@ public function __construct( $this->enumBackingType = $enumBackingType; $this->isDeprecated = $isDeprecated; $this->isStrictProperties = $isStrictProperties; - $this->allowsDynamicProperties = $allowsDynamicProperties; + $this->attributes = $attributes; $this->isNotSerializable = $isNotSerializable; $this->extends = $extends; $this->implements = $implements; @@ -2413,9 +2414,7 @@ function (Name $item) { $code .= $property->getDeclaration($allConstInfos); } - if ($this->allowsDynamicProperties) { - $code .= "\tzend_add_class_attribute(class_entry, zend_ce_allow_dynamic_properties->name, 0);\n"; - } + $code .= generateAttributesCode($this->attributes, "zend_add_class_attribute(class_entry", "class_$escapedName", $allConstInfos); if ($attributeInitializationCode = generateAttributeInitialization($this->funcInfos, $this->cond)) { $code .= "\n" . $attributeInitializationCode; @@ -2460,8 +2459,10 @@ private function getFlagsAsString(): string $flags[] = "ZEND_ACC_NO_DYNAMIC_PROPERTIES"; } - if ($this->allowsDynamicProperties) { - $flags[] = "ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES"; + foreach ($this->attributes as list($name)) { + if ($name === "AllowDynamicProperties") { + $flags[] = "ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES"; + } } if ($this->isNotSerializable) { @@ -2979,6 +2980,30 @@ public function getVariableName(): string { } } +/** + * @param array{0: string, 1: \PhpParser\Node\Arg[]}[] $attributes + * @param iterable $allConstInfos + */ +function generateAttributesCode(array $attributes, string $invocation, string $nameSuffix, iterable $allConstInfos): string { + $code = ""; + foreach ($attributes as list($name, $args)) { + $escapedAttributeName = strtr($name, '\\', '_'); + $code .= "\tzend_string *attribute_name_{$escapedAttributeName}_$nameSuffix = zend_string_init(\"" . addcslashes($name, "\\") . "\", sizeof(\"" . addcslashes($name, "\\") . "\") - 1, 1);\n"; + $code .= "\t" . ($args ? "zend_attribute *attribute_{$escapedAttributeName}_$nameSuffix = " : "") . "$invocation, attribute_name_{$escapedAttributeName}_$nameSuffix, " . count($args) . ");\n"; + $code .= "\tzend_string_release(attribute_name_{$escapedAttributeName}_$nameSuffix);\n"; + foreach ($args as $i => $arg) { + $value = EvaluatedValue::createFromExpression($arg->value, null, null, $allConstInfos); + $zvalName = "attribute_{$escapedAttributeName}_{$nameSuffix}_arg$i"; + $code .= $value->initializeZval($zvalName, $allConstInfos); + $code .= "\tZVAL_COPY_VALUE(&attribute_{$escapedAttributeName}_{$nameSuffix}->args[$i].value, &$zvalName);\n"; + if ($arg->name) { + $code .= "\tattribute_{$escapedAttributeName}_{$nameSuffix}->args[$i].name = zend_string_init(\"{$arg->name->name}\", sizeof(\"{$arg->name->name}\") - 1, 1);\n"; + } + } + } + return $code; +} + /** @return DocCommentTag[] */ function parseDocComment(DocComment $comment): array { $commentText = substr($comment->getText(), 2, -2); @@ -3286,6 +3311,7 @@ function parseClass( $isStrictProperties = false; $isNotSerializable = false; $allowsDynamicProperties = false; + $attributes = []; if ($comment) { $tags = parseDocComment($comment); @@ -3304,12 +3330,11 @@ function parseClass( foreach ($class->attrGroups as $attrGroup) { foreach ($attrGroup->attrs as $attr) { - switch ($attr->name->toCodeString()) { - case '\\AllowDynamicProperties': + $attributes[] = [$attr->name->toString(), $attr->args]; + switch ($attr->name->toString()) { + case 'AllowDynamicProperties': $allowsDynamicProperties = true; break; - default: - throw new Exception("Unhandled attribute {$attr->name->toCodeString()}."); } } } @@ -3348,7 +3373,7 @@ function parseClass( ? SimpleType::fromNode($class->scalarType) : null, $isDeprecated, $isStrictProperties, - $allowsDynamicProperties, + $attributes, $isNotSerializable, $extends, $implements, diff --git a/ext/oci8/oci8_arginfo.h b/ext/oci8/oci8_arginfo.h index eff5d7c0eee3b..521c422821919 100644 --- a/ext/oci8/oci8_arginfo.h +++ b/ext/oci8/oci8_arginfo.h @@ -816,7 +816,9 @@ static zend_class_entry *register_class_OCILob(void) INIT_CLASS_ENTRY(ce, "OCILob", class_OCILob_methods); class_entry = zend_register_internal_class_ex(&ce, NULL); class_entry->ce_flags |= ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES; - zend_add_class_attribute(class_entry, zend_ce_allow_dynamic_properties->name, 0); + zend_string *attribute_name_AllowDynamicProperties_class_OCILob = zend_string_init("AllowDynamicProperties", sizeof("AllowDynamicProperties") - 1, 1); + zend_add_class_attribute(class_entry, attribute_name_AllowDynamicProperties_class_OCILob, 0); + zend_string_release(attribute_name_AllowDynamicProperties_class_OCILob); return class_entry; } @@ -828,7 +830,9 @@ static zend_class_entry *register_class_OCICollection(void) INIT_CLASS_ENTRY(ce, "OCICollection", class_OCICollection_methods); class_entry = zend_register_internal_class_ex(&ce, NULL); class_entry->ce_flags |= ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES; - zend_add_class_attribute(class_entry, zend_ce_allow_dynamic_properties->name, 0); + zend_string *attribute_name_AllowDynamicProperties_class_OCICollection = zend_string_init("AllowDynamicProperties", sizeof("AllowDynamicProperties") - 1, 1); + zend_add_class_attribute(class_entry, attribute_name_AllowDynamicProperties_class_OCICollection, 0); + zend_string_release(attribute_name_AllowDynamicProperties_class_OCICollection); return class_entry; } diff --git a/ext/zend_test/test.c b/ext/zend_test/test.c index 33a0e62e505dc..64a187c8408fa 100644 --- a/ext/zend_test/test.c +++ b/ext/zend_test/test.c @@ -634,12 +634,12 @@ PHP_MINIT_FUNCTION(zend_test) zend_test_attribute = register_class_ZendTestAttribute(); { - zend_internal_attribute *attr = zend_internal_attribute_register(zend_test_attribute, ZEND_ATTRIBUTE_TARGET_ALL); + zend_internal_attribute *attr = zend_internal_attribute_register(zend_test_attribute); attr->validator = zend_attribute_validate_zendtestattribute; } zend_test_parameter_attribute = register_class_ZendTestParameterAttribute(); - zend_internal_attribute_register(zend_test_parameter_attribute, ZEND_ATTRIBUTE_TARGET_PARAMETER); + zend_internal_attribute_register(zend_test_parameter_attribute); { zend_attribute *attr; diff --git a/ext/zend_test/test.stub.php b/ext/zend_test/test.stub.php index 13b2fd346e7b1..da39f812205a2 100644 --- a/ext/zend_test/test.stub.php +++ b/ext/zend_test/test.stub.php @@ -41,10 +41,12 @@ trait _ZendTestTrait { public function testMethod(): bool {} } + #[Attribute(Attribute::TARGET_ALL)] final class ZendTestAttribute { } + #[Attribute(Attribute::TARGET_PARAMETER)] final class ZendTestParameterAttribute { public string $parameter; diff --git a/ext/zend_test/test_arginfo.h b/ext/zend_test/test_arginfo.h index 8c8e67d26954a..fb7fdfada944e 100644 --- a/ext/zend_test/test_arginfo.h +++ b/ext/zend_test/test_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 1a23b7473e5b4525352445545c6b3ab374c4e949 */ + * Stub hash: 7c78cce33ecdd481c76ebd97cbc163730a47dd4c */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_zend_test_array_return, 0, 0, IS_ARRAY, 0) ZEND_END_ARG_INFO() @@ -390,6 +390,12 @@ static zend_class_entry *register_class_ZendTestAttribute(void) INIT_CLASS_ENTRY(ce, "ZendTestAttribute", class_ZendTestAttribute_methods); class_entry = zend_register_internal_class_ex(&ce, NULL); class_entry->ce_flags |= ZEND_ACC_FINAL; + zend_string *attribute_name_Attribute_class_ZendTestAttribute = zend_string_init("Attribute", sizeof("Attribute") - 1, 1); + zend_attribute *attribute_Attribute_class_ZendTestAttribute = zend_add_class_attribute(class_entry, attribute_name_Attribute_class_ZendTestAttribute, 1); + zend_string_release(attribute_name_Attribute_class_ZendTestAttribute); + zval attribute_Attribute_class_ZendTestAttribute_arg0; + ZVAL_LONG(&attribute_Attribute_class_ZendTestAttribute_arg0, ZEND_ATTRIBUTE_TARGET_ALL); + ZVAL_COPY_VALUE(&attribute_Attribute_class_ZendTestAttribute->args[0].value, &attribute_Attribute_class_ZendTestAttribute_arg0); return class_entry; } @@ -407,6 +413,12 @@ static zend_class_entry *register_class_ZendTestParameterAttribute(void) zend_string *property_parameter_name = zend_string_init("parameter", sizeof("parameter") - 1, 1); zend_declare_typed_property(class_entry, property_parameter_name, &property_parameter_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING)); zend_string_release(property_parameter_name); + zend_string *attribute_name_Attribute_class_ZendTestParameterAttribute = zend_string_init("Attribute", sizeof("Attribute") - 1, 1); + zend_attribute *attribute_Attribute_class_ZendTestParameterAttribute = zend_add_class_attribute(class_entry, attribute_name_Attribute_class_ZendTestParameterAttribute, 1); + zend_string_release(attribute_name_Attribute_class_ZendTestParameterAttribute); + zval attribute_Attribute_class_ZendTestParameterAttribute_arg0; + ZVAL_LONG(&attribute_Attribute_class_ZendTestParameterAttribute_arg0, ZEND_ATTRIBUTE_TARGET_PARAMETER); + ZVAL_COPY_VALUE(&attribute_Attribute_class_ZendTestParameterAttribute->args[0].value, &attribute_Attribute_class_ZendTestParameterAttribute_arg0); return class_entry; } From 3b2a0e731841b36a953ce101f40de3c1587b63d3 Mon Sep 17 00:00:00 2001 From: Bob Weinand Date: Mon, 4 Jul 2022 16:43:39 +0200 Subject: [PATCH 2/5] Create AttributeInfo class instead of array tuple Signed-off-by: Bob Weinand --- Zend/zend_attributes_arginfo.h | 4 ++ Zend/zend_builtin_functions_arginfo.h | 1 + build/gen_stub.php | 70 +++++++++++++++------------ ext/oci8/oci8_arginfo.h | 2 + ext/zend_test/test_arginfo.h | 2 + 5 files changed, 49 insertions(+), 30 deletions(-) diff --git a/Zend/zend_attributes_arginfo.h b/Zend/zend_attributes_arginfo.h index 7cbc92272cad5..4311b12d4afb4 100644 --- a/Zend/zend_attributes_arginfo.h +++ b/Zend/zend_attributes_arginfo.h @@ -124,6 +124,7 @@ static zend_class_entry *register_class_Attribute(void) zend_string *property_flags_name = zend_string_init("flags", sizeof("flags") - 1, 1); zend_declare_typed_property(class_entry, property_flags_name, &property_flags_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_LONG)); zend_string_release(property_flags_name); + zend_string *attribute_name_Attribute_class_Attribute = zend_string_init("Attribute", sizeof("Attribute") - 1, 1); zend_attribute *attribute_Attribute_class_Attribute = zend_add_class_attribute(class_entry, attribute_name_Attribute_class_Attribute, 1); zend_string_release(attribute_name_Attribute_class_Attribute); @@ -141,6 +142,7 @@ static zend_class_entry *register_class_ReturnTypeWillChange(void) INIT_CLASS_ENTRY(ce, "ReturnTypeWillChange", class_ReturnTypeWillChange_methods); class_entry = zend_register_internal_class_ex(&ce, NULL); class_entry->ce_flags |= ZEND_ACC_FINAL; + zend_string *attribute_name_Attribute_class_ReturnTypeWillChange = zend_string_init("Attribute", sizeof("Attribute") - 1, 1); zend_attribute *attribute_Attribute_class_ReturnTypeWillChange = zend_add_class_attribute(class_entry, attribute_name_Attribute_class_ReturnTypeWillChange, 1); zend_string_release(attribute_name_Attribute_class_ReturnTypeWillChange); @@ -158,6 +160,7 @@ static zend_class_entry *register_class_AllowDynamicProperties(void) INIT_CLASS_ENTRY(ce, "AllowDynamicProperties", class_AllowDynamicProperties_methods); class_entry = zend_register_internal_class_ex(&ce, NULL); class_entry->ce_flags |= ZEND_ACC_FINAL; + zend_string *attribute_name_Attribute_class_AllowDynamicProperties = zend_string_init("Attribute", sizeof("Attribute") - 1, 1); zend_attribute *attribute_Attribute_class_AllowDynamicProperties = zend_add_class_attribute(class_entry, attribute_name_Attribute_class_AllowDynamicProperties, 1); zend_string_release(attribute_name_Attribute_class_AllowDynamicProperties); @@ -175,6 +178,7 @@ static zend_class_entry *register_class_SensitiveParameter(void) INIT_CLASS_ENTRY(ce, "SensitiveParameter", class_SensitiveParameter_methods); class_entry = zend_register_internal_class_ex(&ce, NULL); class_entry->ce_flags |= ZEND_ACC_FINAL|ZEND_ACC_NO_DYNAMIC_PROPERTIES; + zend_string *attribute_name_Attribute_class_SensitiveParameter = zend_string_init("Attribute", sizeof("Attribute") - 1, 1); zend_attribute *attribute_Attribute_class_SensitiveParameter = zend_add_class_attribute(class_entry, attribute_name_Attribute_class_SensitiveParameter, 1); zend_string_release(attribute_name_Attribute_class_SensitiveParameter); diff --git a/Zend/zend_builtin_functions_arginfo.h b/Zend/zend_builtin_functions_arginfo.h index 1ca64e227ef55..abcbfaaa9e776 100644 --- a/Zend/zend_builtin_functions_arginfo.h +++ b/Zend/zend_builtin_functions_arginfo.h @@ -354,6 +354,7 @@ static zend_class_entry *register_class_stdClass(void) INIT_CLASS_ENTRY(ce, "stdClass", class_stdClass_methods); class_entry = zend_register_internal_class_ex(&ce, NULL); class_entry->ce_flags |= ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES; + zend_string *attribute_name_AllowDynamicProperties_class_stdClass = zend_string_init("AllowDynamicProperties", sizeof("AllowDynamicProperties") - 1, 1); zend_add_class_attribute(class_entry, attribute_name_AllowDynamicProperties_class_stdClass, 0); zend_string_release(attribute_name_AllowDynamicProperties_class_stdClass); diff --git a/build/gen_stub.php b/build/gen_stub.php index 1244075598ae3..68e5dda3c8527 100755 --- a/build/gen_stub.php +++ b/build/gen_stub.php @@ -2257,6 +2257,38 @@ public function getDeclaration(iterable $allConstInfos): string { } } +class AttributeInfo { + /** @var string */ + public $class; + /** @var \PhpParser\Node\Arg[] */ + public $args; + + /** @param \PhpParser\Node\Arg[] $args */ + public function __construct(string $class, array $args) { + $this->class = $class; + $this->args = $args; + } + + /** @param iterable $allConstInfos */ + public function generateCode(string $invocation, string $nameSuffix, iterable $allConstInfos): string { + $code = "\n"; + $escapedAttributeName = strtr($this->class, '\\', '_'); + $code .= "\tzend_string *attribute_name_{$escapedAttributeName}_$nameSuffix = zend_string_init(\"" . addcslashes($this->class, "\\") . "\", sizeof(\"" . addcslashes($this->class, "\\") . "\") - 1, 1);\n"; + $code .= "\t" . ($this->args ? "zend_attribute *attribute_{$escapedAttributeName}_$nameSuffix = " : "") . "$invocation, attribute_name_{$escapedAttributeName}_$nameSuffix, " . count($this->args) . ");\n"; + $code .= "\tzend_string_release(attribute_name_{$escapedAttributeName}_$nameSuffix);\n"; + foreach ($this->args as $i => $arg) { + $value = EvaluatedValue::createFromExpression($arg->value, null, null, $allConstInfos); + $zvalName = "attribute_{$escapedAttributeName}_{$nameSuffix}_arg$i"; + $code .= $value->initializeZval($zvalName, $allConstInfos); + $code .= "\tZVAL_COPY_VALUE(&attribute_{$escapedAttributeName}_{$nameSuffix}->args[$i].value, &$zvalName);\n"; + if ($arg->name) { + $code .= "\tattribute_{$escapedAttributeName}_{$nameSuffix}->args[$i].name = zend_string_init(\"{$arg->name->name}\", sizeof(\"{$arg->name->name}\") - 1, 1);\n"; + } + } + return $code; + } +} + class ClassInfo { /** @var Name */ public $name; @@ -2272,7 +2304,7 @@ class ClassInfo { public $isDeprecated; /** @var bool */ public $isStrictProperties; - /** @var array{0: string, 1: \PhpParser\Node\Arg[]}[] */ + /** @var AttributeInfo[] */ public $attributes; /** @var bool */ public $isNotSerializable; @@ -2292,7 +2324,7 @@ class ClassInfo { public $cond; /** - * @param array{0: string, 1: \PhpParser\Node\Arg[]}[] $attributes + * @param AttributeInfo[] $attributes * @param Name[] $extends * @param Name[] $implements * @param ConstInfo[] $constInfos @@ -2414,7 +2446,9 @@ function (Name $item) { $code .= $property->getDeclaration($allConstInfos); } - $code .= generateAttributesCode($this->attributes, "zend_add_class_attribute(class_entry", "class_$escapedName", $allConstInfos); + foreach ($this->attributes as $attribute) { + $code .= $attribute->generateCode("zend_add_class_attribute(class_entry", "class_$escapedName", $allConstInfos); + } if ($attributeInitializationCode = generateAttributeInitialization($this->funcInfos, $this->cond)) { $code .= "\n" . $attributeInitializationCode; @@ -2459,8 +2493,8 @@ private function getFlagsAsString(): string $flags[] = "ZEND_ACC_NO_DYNAMIC_PROPERTIES"; } - foreach ($this->attributes as list($name)) { - if ($name === "AllowDynamicProperties") { + foreach ($this->attributes as $attr) { + if ($attr->class === "AllowDynamicProperties") { $flags[] = "ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES"; } } @@ -2980,30 +3014,6 @@ public function getVariableName(): string { } } -/** - * @param array{0: string, 1: \PhpParser\Node\Arg[]}[] $attributes - * @param iterable $allConstInfos - */ -function generateAttributesCode(array $attributes, string $invocation, string $nameSuffix, iterable $allConstInfos): string { - $code = ""; - foreach ($attributes as list($name, $args)) { - $escapedAttributeName = strtr($name, '\\', '_'); - $code .= "\tzend_string *attribute_name_{$escapedAttributeName}_$nameSuffix = zend_string_init(\"" . addcslashes($name, "\\") . "\", sizeof(\"" . addcslashes($name, "\\") . "\") - 1, 1);\n"; - $code .= "\t" . ($args ? "zend_attribute *attribute_{$escapedAttributeName}_$nameSuffix = " : "") . "$invocation, attribute_name_{$escapedAttributeName}_$nameSuffix, " . count($args) . ");\n"; - $code .= "\tzend_string_release(attribute_name_{$escapedAttributeName}_$nameSuffix);\n"; - foreach ($args as $i => $arg) { - $value = EvaluatedValue::createFromExpression($arg->value, null, null, $allConstInfos); - $zvalName = "attribute_{$escapedAttributeName}_{$nameSuffix}_arg$i"; - $code .= $value->initializeZval($zvalName, $allConstInfos); - $code .= "\tZVAL_COPY_VALUE(&attribute_{$escapedAttributeName}_{$nameSuffix}->args[$i].value, &$zvalName);\n"; - if ($arg->name) { - $code .= "\tattribute_{$escapedAttributeName}_{$nameSuffix}->args[$i].name = zend_string_init(\"{$arg->name->name}\", sizeof(\"{$arg->name->name}\") - 1, 1);\n"; - } - } - } - return $code; -} - /** @return DocCommentTag[] */ function parseDocComment(DocComment $comment): array { $commentText = substr($comment->getText(), 2, -2); @@ -3330,7 +3340,7 @@ function parseClass( foreach ($class->attrGroups as $attrGroup) { foreach ($attrGroup->attrs as $attr) { - $attributes[] = [$attr->name->toString(), $attr->args]; + $attributes[] = new AttributeInfo($attr->name->toString(), $attr->args); switch ($attr->name->toString()) { case 'AllowDynamicProperties': $allowsDynamicProperties = true; diff --git a/ext/oci8/oci8_arginfo.h b/ext/oci8/oci8_arginfo.h index 521c422821919..ba49c7a185138 100644 --- a/ext/oci8/oci8_arginfo.h +++ b/ext/oci8/oci8_arginfo.h @@ -816,6 +816,7 @@ static zend_class_entry *register_class_OCILob(void) INIT_CLASS_ENTRY(ce, "OCILob", class_OCILob_methods); class_entry = zend_register_internal_class_ex(&ce, NULL); class_entry->ce_flags |= ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES; + zend_string *attribute_name_AllowDynamicProperties_class_OCILob = zend_string_init("AllowDynamicProperties", sizeof("AllowDynamicProperties") - 1, 1); zend_add_class_attribute(class_entry, attribute_name_AllowDynamicProperties_class_OCILob, 0); zend_string_release(attribute_name_AllowDynamicProperties_class_OCILob); @@ -830,6 +831,7 @@ static zend_class_entry *register_class_OCICollection(void) INIT_CLASS_ENTRY(ce, "OCICollection", class_OCICollection_methods); class_entry = zend_register_internal_class_ex(&ce, NULL); class_entry->ce_flags |= ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES; + zend_string *attribute_name_AllowDynamicProperties_class_OCICollection = zend_string_init("AllowDynamicProperties", sizeof("AllowDynamicProperties") - 1, 1); zend_add_class_attribute(class_entry, attribute_name_AllowDynamicProperties_class_OCICollection, 0); zend_string_release(attribute_name_AllowDynamicProperties_class_OCICollection); diff --git a/ext/zend_test/test_arginfo.h b/ext/zend_test/test_arginfo.h index fb7fdfada944e..d36e805b5c1ee 100644 --- a/ext/zend_test/test_arginfo.h +++ b/ext/zend_test/test_arginfo.h @@ -390,6 +390,7 @@ static zend_class_entry *register_class_ZendTestAttribute(void) INIT_CLASS_ENTRY(ce, "ZendTestAttribute", class_ZendTestAttribute_methods); class_entry = zend_register_internal_class_ex(&ce, NULL); class_entry->ce_flags |= ZEND_ACC_FINAL; + zend_string *attribute_name_Attribute_class_ZendTestAttribute = zend_string_init("Attribute", sizeof("Attribute") - 1, 1); zend_attribute *attribute_Attribute_class_ZendTestAttribute = zend_add_class_attribute(class_entry, attribute_name_Attribute_class_ZendTestAttribute, 1); zend_string_release(attribute_name_Attribute_class_ZendTestAttribute); @@ -413,6 +414,7 @@ static zend_class_entry *register_class_ZendTestParameterAttribute(void) zend_string *property_parameter_name = zend_string_init("parameter", sizeof("parameter") - 1, 1); zend_declare_typed_property(class_entry, property_parameter_name, &property_parameter_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING)); zend_string_release(property_parameter_name); + zend_string *attribute_name_Attribute_class_ZendTestParameterAttribute = zend_string_init("Attribute", sizeof("Attribute") - 1, 1); zend_attribute *attribute_Attribute_class_ZendTestParameterAttribute = zend_add_class_attribute(class_entry, attribute_name_Attribute_class_ZendTestParameterAttribute, 1); zend_string_release(attribute_name_Attribute_class_ZendTestParameterAttribute); From 810133742839fbe45c287d5aabbd1bc4662354c8 Mon Sep 17 00:00:00 2001 From: Bob Weinand Date: Mon, 4 Jul 2022 18:03:23 +0200 Subject: [PATCH 3/5] Add file inclusion as a mechanism to find stub files and cross-reference constants Signed-off-by: Bob Weinand --- build/gen_stub.php | 66 ++++++++++++++++++++++++++++-------- ext/zend_test/test.stub.php | 2 ++ ext/zend_test/test_arginfo.h | 2 +- 3 files changed, 55 insertions(+), 15 deletions(-) diff --git a/build/gen_stub.php b/build/gen_stub.php index 68e5dda3c8527..dcd691d515d36 100755 --- a/build/gen_stub.php +++ b/build/gen_stub.php @@ -46,28 +46,54 @@ function processDirectory(string $dir, Context $context): array { return $fileInfos; } -function processStubFile(string $stubFile, Context $context): ?FileInfo { +function processStubFile(string $stubFile, Context $context, bool $includeOnly = false): ?FileInfo { try { if (!file_exists($stubFile)) { throw new Exception("File $stubFile does not exist"); } - $stubFilenameWithoutExtension = str_replace(".stub.php", "", $stubFile); - $arginfoFile = "{$stubFilenameWithoutExtension}_arginfo.h"; - $legacyFile = "{$stubFilenameWithoutExtension}_legacy_arginfo.h"; + if (!$includeOnly) { + $stubFilenameWithoutExtension = str_replace(".stub.php", "", $stubFile); + $arginfoFile = "{$stubFilenameWithoutExtension}_arginfo.h"; + $legacyFile = "{$stubFilenameWithoutExtension}_legacy_arginfo.h"; - $stubCode = file_get_contents($stubFile); - $stubHash = computeStubHash($stubCode); - $oldStubHash = extractStubHash($arginfoFile); - if ($stubHash === $oldStubHash && !$context->forceParse) { - /* Stub file did not change, do not regenerate. */ - return null; + $stubCode = file_get_contents($stubFile); + $stubHash = computeStubHash($stubCode); + $oldStubHash = extractStubHash($arginfoFile); + if ($stubHash === $oldStubHash && !$context->forceParse) { + /* Stub file did not change, do not regenerate. */ + return null; + } } - initPhpParser(); - $fileInfo = parseStubFile($stubCode); - $constInfos = $fileInfo->getAllConstInfos(); - $context->allConstInfos = array_merge($context->allConstInfos, $constInfos); + if (!$fileInfo = $context->parsedFiles[$stubFile] ?? null) { + initPhpParser(); + $fileInfo = parseStubFile($stubCode ?? file_get_contents($stubFile)); + $context->parsedFiles[$stubFile] = $fileInfo; + + foreach ($fileInfo->dependencies as $dependency) { + // TODO add header search path for extensions? + $prefixes = [dirname($stubFile) . "/", ""]; + foreach ($prefixes as $prefix) { + $depFile = $prefix . $dependency; + if (file_exists($depFile)) { + break; + } + $depFile = null; + } + if (!$depFile) { + throw new Exception("File $stubFile includes a file $dependency which does not exist"); + } + processStubFile($depFile, $context, true); + } + + $constInfos = $fileInfo->getAllConstInfos(); + $context->allConstInfos = array_merge($context->allConstInfos, $constInfos); + } + + if ($includeOnly) { + return $fileInfo; + } $arginfoCode = generateArgInfoCode( basename($stubFilenameWithoutExtension), @@ -131,6 +157,8 @@ class Context { public $forceRegeneration = false; /** @var iterable */ public iterable $allConstInfos = []; + /** @var FileInfo[] */ + public array $parsedFiles = []; } class ArrayType extends SimpleType { @@ -2896,6 +2924,8 @@ private function appendInheritedMemberSectionToClassSynopsis(DOMDocument $doc, D } class FileInfo { + /** @var string[] */ + public $dependencies = []; /** @var ConstInfo[] */ public $constInfos = []; /** @var FuncInfo[] */ @@ -3546,6 +3576,14 @@ function handleStatements(FileInfo $fileInfo, array $stmts, PrettyPrinterAbstrac continue; } + if ($stmt instanceof Stmt\Expression) { + $expr = $stmt->expr; + if ($expr instanceof Expr\Include_) { + $fileInfo->dependencies[] = (string)EvaluatedValue::createFromExpression($expr->expr, null, null, [])->value; + continue; + } + } + throw new Exception("Unexpected node {$stmt->getType()}"); } } diff --git a/ext/zend_test/test.stub.php b/ext/zend_test/test.stub.php index da39f812205a2..b99a28ea82501 100644 --- a/ext/zend_test/test.stub.php +++ b/ext/zend_test/test.stub.php @@ -4,6 +4,8 @@ namespace { + require "Zend/zend_attributes.stub.php"; + interface _ZendTestInterface { } diff --git a/ext/zend_test/test_arginfo.h b/ext/zend_test/test_arginfo.h index d36e805b5c1ee..3e37426913ad4 100644 --- a/ext/zend_test/test_arginfo.h +++ b/ext/zend_test/test_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 7c78cce33ecdd481c76ebd97cbc163730a47dd4c */ + * Stub hash: 3ad8ef04d52f1a099d9fd3b6c2c02b90de2980be */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_zend_test_array_return, 0, 0, IS_ARRAY, 0) ZEND_END_ARG_INFO() From 2231393c2f16f96a8e7ce858a10aeea586a1f876 Mon Sep 17 00:00:00 2001 From: Bob Weinand Date: Mon, 4 Jul 2022 18:09:14 +0200 Subject: [PATCH 4/5] Add note to UPGRADING.INTERNALS about Attribute API changes Signed-off-by: Bob Weinand --- UPGRADING.INTERNALS | 3 +++ 1 file changed, 3 insertions(+) diff --git a/UPGRADING.INTERNALS b/UPGRADING.INTERNALS index eed4239ab7f79..b34f00224812f 100644 --- a/UPGRADING.INTERNALS +++ b/UPGRADING.INTERNALS @@ -39,6 +39,9 @@ PHP 8.2 INTERNALS UPGRADE NOTES - zend_object_do_operation_t * Added a new zero_position argument to php_stream_fopen_from_fd_rel to reflect if this a newly created file so the current file offset needs not to be checked. +* zend_internal_attribute_register() no longer takes a flags argument nor + registers the attribute class directly. Instead specify #[Attribute] with the + appropriate target flags in the .stub.php file. ======================== 2. Build system changes From f490f8e3efb2c423c9264d408bcef3fc724cb7a1 Mon Sep 17 00:00:00 2001 From: Bob Weinand Date: Tue, 5 Jul 2022 15:08:20 +0200 Subject: [PATCH 5/5] Preserve zend_internal_attribute_register as is for internal API BC Signed-off-by: Bob Weinand --- UPGRADING.INTERNALS | 3 --- Zend/zend_attributes.c | 20 ++++++++++++++------ Zend/zend_attributes.h | 3 ++- build/gen_stub.php | 2 +- ext/zend_test/test.c | 4 ++-- 5 files changed, 19 insertions(+), 13 deletions(-) diff --git a/UPGRADING.INTERNALS b/UPGRADING.INTERNALS index b34f00224812f..eed4239ab7f79 100644 --- a/UPGRADING.INTERNALS +++ b/UPGRADING.INTERNALS @@ -39,9 +39,6 @@ PHP 8.2 INTERNALS UPGRADE NOTES - zend_object_do_operation_t * Added a new zero_position argument to php_stream_fopen_from_fd_rel to reflect if this a newly created file so the current file offset needs not to be checked. -* zend_internal_attribute_register() no longer takes a flags argument nor - registers the attribute class directly. Instead specify #[Attribute] with the - appropriate target flags in the .stub.php file. ======================== 2. Build system changes diff --git a/Zend/zend_attributes.c b/Zend/zend_attributes.c index dabde19623f72..5a446c8c2859e 100644 --- a/Zend/zend_attributes.c +++ b/Zend/zend_attributes.c @@ -317,7 +317,7 @@ 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) +ZEND_API zend_internal_attribute *zend_mark_internal_attribute(zend_class_entry *ce) { zend_internal_attribute *internal_attr; zend_attribute *attr; @@ -344,6 +344,14 @@ ZEND_API zend_internal_attribute *zend_internal_attribute_register(zend_class_en zend_error_noreturn(E_ERROR, "Classes must be first marked as attribute before being able to be registered as internal attribute class"); } +ZEND_API zend_internal_attribute *zend_internal_attribute_register(zend_class_entry *ce, uint32_t flags) +{ + zend_attribute *attr = zend_add_class_attribute(ce, zend_ce_attribute->name, 1); + ZVAL_LONG(&attr->args[0].value, flags); + + return zend_mark_internal_attribute(ce); +} + ZEND_API zend_internal_attribute *zend_internal_attribute_get(zend_string *lcname) { return zend_hash_find_ptr(&internal_attributes, lcname); @@ -356,23 +364,23 @@ void zend_register_attribute_ce(void) zend_hash_init(&internal_attributes, 8, NULL, free_internal_attribute, 1); zend_ce_attribute = register_class_Attribute(); - attr = zend_internal_attribute_register(zend_ce_attribute); + attr = zend_mark_internal_attribute(zend_ce_attribute); attr->validator = validate_attribute; zend_ce_return_type_will_change_attribute = register_class_ReturnTypeWillChange(); - zend_internal_attribute_register(zend_ce_return_type_will_change_attribute); + zend_mark_internal_attribute(zend_ce_return_type_will_change_attribute); zend_ce_allow_dynamic_properties = register_class_AllowDynamicProperties(); - attr = zend_internal_attribute_register(zend_ce_allow_dynamic_properties); + attr = zend_mark_internal_attribute(zend_ce_allow_dynamic_properties); attr->validator = validate_allow_dynamic_properties; zend_ce_sensitive_parameter = register_class_SensitiveParameter(); - zend_internal_attribute_register(zend_ce_sensitive_parameter); + zend_mark_internal_attribute(zend_ce_sensitive_parameter); memcpy(&attributes_object_handlers_sensitive_parameter_value, &std_object_handlers, sizeof(zend_object_handlers)); attributes_object_handlers_sensitive_parameter_value.get_properties_for = attributes_sensitive_parameter_value_get_properties_for; - /* This is not an actual attribute, thus the zend_internal_attribute_register() call is missing. */ + /* This is not an actual attribute, thus the zend_mark_internal_attribute() call is missing. */ zend_ce_sensitive_parameter_value = register_class_SensitiveParameterValue(); zend_ce_sensitive_parameter_value->create_object = attributes_sensitive_parameter_value_new; } diff --git a/Zend/zend_attributes.h b/Zend/zend_attributes.h index 2f0655b74bf78..52888ebcf7aa1 100644 --- a/Zend/zend_attributes.h +++ b/Zend/zend_attributes.h @@ -77,7 +77,8 @@ ZEND_API zend_result zend_get_attribute_value(zval *ret, zend_attribute *attr, u ZEND_API zend_string *zend_get_attribute_target_names(uint32_t targets); ZEND_API bool zend_is_attribute_repeated(HashTable *attributes, zend_attribute *attr); -ZEND_API zend_internal_attribute *zend_internal_attribute_register(zend_class_entry *ce); +ZEND_API zend_internal_attribute *zend_mark_internal_attribute(zend_class_entry *ce); +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( diff --git a/build/gen_stub.php b/build/gen_stub.php index dcd691d515d36..c712be21e8c78 100755 --- a/build/gen_stub.php +++ b/build/gen_stub.php @@ -73,7 +73,7 @@ function processStubFile(string $stubFile, Context $context, bool $includeOnly = foreach ($fileInfo->dependencies as $dependency) { // TODO add header search path for extensions? - $prefixes = [dirname($stubFile) . "/", ""]; + $prefixes = [dirname($stubFile) . "/", dirname(__DIR__) . "/"]; foreach ($prefixes as $prefix) { $depFile = $prefix . $dependency; if (file_exists($depFile)) { diff --git a/ext/zend_test/test.c b/ext/zend_test/test.c index 64a187c8408fa..14dcfef98f5ad 100644 --- a/ext/zend_test/test.c +++ b/ext/zend_test/test.c @@ -634,12 +634,12 @@ PHP_MINIT_FUNCTION(zend_test) zend_test_attribute = register_class_ZendTestAttribute(); { - zend_internal_attribute *attr = zend_internal_attribute_register(zend_test_attribute); + zend_internal_attribute *attr = zend_mark_internal_attribute(zend_test_attribute); attr->validator = zend_attribute_validate_zendtestattribute; } zend_test_parameter_attribute = register_class_ZendTestParameterAttribute(); - zend_internal_attribute_register(zend_test_parameter_attribute); + zend_mark_internal_attribute(zend_test_parameter_attribute); { zend_attribute *attr;