Allow for arbitrary (class) attributes in stubs

This can be easily extended to other types of attributes.

Closes #8839.
This commit is contained in:
Bob Weinand 2022-07-05 18:22:33 +02:00
parent 82bcc94bea
commit 9f29e2d7e9
10 changed files with 282 additions and 57 deletions

View file

@ -317,27 +317,39 @@ static void free_internal_attribute(zval *v)
pefree(Z_PTR_P(v), 1); 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_mark_internal_attribute(zend_class_entry *ce)
{ {
zend_internal_attribute *internal_attr; zend_internal_attribute *internal_attr;
zend_attribute *attr;
if (ce->type != ZEND_INTERNAL_CLASS) { if (ce->type != ZEND_INTERNAL_CLASS) {
zend_error_noreturn(E_ERROR, "Only internal classes can be registered as compiler attribute"); zend_error_noreturn(E_ERROR, "Only internal classes can be registered as compiler attribute");
} }
internal_attr = pemalloc(sizeof(zend_internal_attribute), 1); ZEND_HASH_FOREACH_PTR(ce->attributes, attr) {
internal_attr->ce = ce; if (zend_string_equals(attr->name, zend_ce_attribute->name)) {
internal_attr->flags = flags; internal_attr = pemalloc(sizeof(zend_internal_attribute), 1);
internal_attr->validator = NULL; 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); return internal_attr;
}
} ZEND_HASH_FOREACH_END();
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); zend_attribute *attr = zend_add_class_attribute(ce, zend_ce_attribute->name, 1);
ZVAL_LONG(&attr->args[0].value, flags); ZVAL_LONG(&attr->args[0].value, flags);
zend_string_release(lcname);
return internal_attr; return zend_mark_internal_attribute(ce);
} }
ZEND_API zend_internal_attribute *zend_internal_attribute_get(zend_string *lcname) ZEND_API zend_internal_attribute *zend_internal_attribute_get(zend_string *lcname)
@ -352,32 +364,23 @@ void zend_register_attribute_ce(void)
zend_hash_init(&internal_attributes, 8, NULL, free_internal_attribute, 1); zend_hash_init(&internal_attributes, 8, NULL, free_internal_attribute, 1);
zend_ce_attribute = register_class_Attribute(); zend_ce_attribute = register_class_Attribute();
attr = zend_internal_attribute_register(zend_ce_attribute, ZEND_ATTRIBUTE_TARGET_CLASS); attr = zend_mark_internal_attribute(zend_ce_attribute);
attr->validator = validate_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_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_mark_internal_attribute(zend_ce_return_type_will_change_attribute);
zend_ce_allow_dynamic_properties = register_class_AllowDynamicProperties(); zend_ce_allow_dynamic_properties = register_class_AllowDynamicProperties();
attr = zend_internal_attribute_register(zend_ce_allow_dynamic_properties, ZEND_ATTRIBUTE_TARGET_CLASS); attr = zend_mark_internal_attribute(zend_ce_allow_dynamic_properties);
attr->validator = validate_allow_dynamic_properties; attr->validator = validate_allow_dynamic_properties;
zend_ce_sensitive_parameter = register_class_SensitiveParameter(); zend_ce_sensitive_parameter = register_class_SensitiveParameter();
attr = zend_internal_attribute_register(zend_ce_sensitive_parameter, ZEND_ATTRIBUTE_TARGET_PARAMETER); zend_mark_internal_attribute(zend_ce_sensitive_parameter);
memcpy(&attributes_object_handlers_sensitive_parameter_value, &std_object_handlers, sizeof(zend_object_handlers)); 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; 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 = register_class_SensitiveParameterValue();
zend_ce_sensitive_parameter_value->create_object = attributes_sensitive_parameter_value_new; zend_ce_sensitive_parameter_value->create_object = attributes_sensitive_parameter_value_new;
} }

View file

@ -77,6 +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 zend_string *zend_get_attribute_target_names(uint32_t targets);
ZEND_API bool zend_is_attribute_repeated(HashTable *attributes, zend_attribute *attr); ZEND_API bool zend_is_attribute_repeated(HashTable *attributes, zend_attribute *attr);
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_register(zend_class_entry *ce, uint32_t flags);
ZEND_API zend_internal_attribute *zend_internal_attribute_get(zend_string *lcname); ZEND_API zend_internal_attribute *zend_internal_attribute_get(zend_string *lcname);

View file

@ -2,18 +2,62 @@
/** @generate-class-entries */ /** @generate-class-entries */
#[Attribute(Attribute::TARGET_CLASS)]
final class Attribute 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 int $flags;
public function __construct(int $flags = Attribute::TARGET_ALL) {} public function __construct(int $flags = Attribute::TARGET_ALL) {}
} }
#[Attribute(Attribute::TARGET_METHOD)]
final class ReturnTypeWillChange final class ReturnTypeWillChange
{ {
public function __construct() {} public function __construct() {}
} }
#[Attribute(Attribute::TARGET_CLASS)]
final class AllowDynamicProperties final class AllowDynamicProperties
{ {
public function __construct() {} public function __construct() {}
@ -22,6 +66,7 @@ final class AllowDynamicProperties
/** /**
* @strict-properties * @strict-properties
*/ */
#[Attribute(Attribute::TARGET_PARAMETER)]
final class SensitiveParameter final class SensitiveParameter
{ {
public function __construct() {} public function __construct() {}

View file

@ -1,5 +1,5 @@
/* This is a generated file, edit the .stub.php file instead. /* 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_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_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, flags, IS_LONG, 0, "Attribute::TARGET_ALL")
@ -71,12 +71,67 @@ static zend_class_entry *register_class_Attribute(void)
class_entry = zend_register_internal_class_ex(&ce, NULL); class_entry = zend_register_internal_class_ex(&ce, NULL);
class_entry->ce_flags |= ZEND_ACC_FINAL; 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 property_flags_default_value;
ZVAL_UNDEF(&property_flags_default_value); ZVAL_UNDEF(&property_flags_default_value);
zend_string *property_flags_name = zend_string_init("flags", sizeof("flags") - 1, 1); 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_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_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; return class_entry;
} }
@ -88,6 +143,13 @@ static zend_class_entry *register_class_ReturnTypeWillChange(void)
class_entry = zend_register_internal_class_ex(&ce, NULL); class_entry = zend_register_internal_class_ex(&ce, NULL);
class_entry->ce_flags |= ZEND_ACC_FINAL; 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; return class_entry;
} }
@ -99,6 +161,13 @@ static zend_class_entry *register_class_AllowDynamicProperties(void)
class_entry = zend_register_internal_class_ex(&ce, NULL); class_entry = zend_register_internal_class_ex(&ce, NULL);
class_entry->ce_flags |= ZEND_ACC_FINAL; 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; return class_entry;
} }
@ -110,6 +179,13 @@ static zend_class_entry *register_class_SensitiveParameter(void)
class_entry = zend_register_internal_class_ex(&ce, NULL); class_entry = zend_register_internal_class_ex(&ce, NULL);
class_entry->ce_flags |= ZEND_ACC_FINAL|ZEND_ACC_NO_DYNAMIC_PROPERTIES; 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; return class_entry;
} }

View file

@ -354,7 +354,10 @@ static zend_class_entry *register_class_stdClass(void)
INIT_CLASS_ENTRY(ce, "stdClass", class_stdClass_methods); INIT_CLASS_ENTRY(ce, "stdClass", class_stdClass_methods);
class_entry = zend_register_internal_class_ex(&ce, NULL); class_entry = zend_register_internal_class_ex(&ce, NULL);
class_entry->ce_flags |= ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES; 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; return class_entry;
} }

View file

@ -46,28 +46,54 @@ function processDirectory(string $dir, Context $context): array {
return $fileInfos; return $fileInfos;
} }
function processStubFile(string $stubFile, Context $context): ?FileInfo { function processStubFile(string $stubFile, Context $context, bool $includeOnly = false): ?FileInfo {
try { try {
if (!file_exists($stubFile)) { if (!file_exists($stubFile)) {
throw new Exception("File $stubFile does not exist"); throw new Exception("File $stubFile does not exist");
} }
$stubFilenameWithoutExtension = str_replace(".stub.php", "", $stubFile); if (!$includeOnly) {
$arginfoFile = "{$stubFilenameWithoutExtension}_arginfo.h"; $stubFilenameWithoutExtension = str_replace(".stub.php", "", $stubFile);
$legacyFile = "{$stubFilenameWithoutExtension}_legacy_arginfo.h"; $arginfoFile = "{$stubFilenameWithoutExtension}_arginfo.h";
$legacyFile = "{$stubFilenameWithoutExtension}_legacy_arginfo.h";
$stubCode = file_get_contents($stubFile); $stubCode = file_get_contents($stubFile);
$stubHash = computeStubHash($stubCode); $stubHash = computeStubHash($stubCode);
$oldStubHash = extractStubHash($arginfoFile); $oldStubHash = extractStubHash($arginfoFile);
if ($stubHash === $oldStubHash && !$context->forceParse) { if ($stubHash === $oldStubHash && !$context->forceParse) {
/* Stub file did not change, do not regenerate. */ /* Stub file did not change, do not regenerate. */
return null; return null;
}
} }
initPhpParser(); if (!$fileInfo = $context->parsedFiles[$stubFile] ?? null) {
$fileInfo = parseStubFile($stubCode); initPhpParser();
$constInfos = $fileInfo->getAllConstInfos(); $fileInfo = parseStubFile($stubCode ?? file_get_contents($stubFile));
$context->allConstInfos = array_merge($context->allConstInfos, $constInfos); $context->parsedFiles[$stubFile] = $fileInfo;
foreach ($fileInfo->dependencies as $dependency) {
// TODO add header search path for extensions?
$prefixes = [dirname($stubFile) . "/", dirname(__DIR__) . "/"];
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( $arginfoCode = generateArgInfoCode(
basename($stubFilenameWithoutExtension), basename($stubFilenameWithoutExtension),
@ -131,6 +157,8 @@ class Context {
public $forceRegeneration = false; public $forceRegeneration = false;
/** @var iterable<ConstInfo> */ /** @var iterable<ConstInfo> */
public iterable $allConstInfos = []; public iterable $allConstInfos = [];
/** @var FileInfo[] */
public array $parsedFiles = [];
} }
class ArrayType extends SimpleType { class ArrayType extends SimpleType {
@ -2257,6 +2285,38 @@ class EnumCaseInfo {
} }
} }
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<ConstInfo> $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 { class ClassInfo {
/** @var Name */ /** @var Name */
public $name; public $name;
@ -2272,8 +2332,8 @@ class ClassInfo {
public $isDeprecated; public $isDeprecated;
/** @var bool */ /** @var bool */
public $isStrictProperties; public $isStrictProperties;
/** @var bool */ /** @var AttributeInfo[] */
public $allowsDynamicProperties; public $attributes;
/** @var bool */ /** @var bool */
public $isNotSerializable; public $isNotSerializable;
/** @var Name[] */ /** @var Name[] */
@ -2292,6 +2352,7 @@ class ClassInfo {
public $cond; public $cond;
/** /**
* @param AttributeInfo[] $attributes
* @param Name[] $extends * @param Name[] $extends
* @param Name[] $implements * @param Name[] $implements
* @param ConstInfo[] $constInfos * @param ConstInfo[] $constInfos
@ -2307,7 +2368,7 @@ class ClassInfo {
?SimpleType $enumBackingType, ?SimpleType $enumBackingType,
bool $isDeprecated, bool $isDeprecated,
bool $isStrictProperties, bool $isStrictProperties,
bool $allowsDynamicProperties, array $attributes,
bool $isNotSerializable, bool $isNotSerializable,
array $extends, array $extends,
array $implements, array $implements,
@ -2324,7 +2385,7 @@ class ClassInfo {
$this->enumBackingType = $enumBackingType; $this->enumBackingType = $enumBackingType;
$this->isDeprecated = $isDeprecated; $this->isDeprecated = $isDeprecated;
$this->isStrictProperties = $isStrictProperties; $this->isStrictProperties = $isStrictProperties;
$this->allowsDynamicProperties = $allowsDynamicProperties; $this->attributes = $attributes;
$this->isNotSerializable = $isNotSerializable; $this->isNotSerializable = $isNotSerializable;
$this->extends = $extends; $this->extends = $extends;
$this->implements = $implements; $this->implements = $implements;
@ -2413,8 +2474,8 @@ class ClassInfo {
$code .= $property->getDeclaration($allConstInfos); $code .= $property->getDeclaration($allConstInfos);
} }
if ($this->allowsDynamicProperties) { foreach ($this->attributes as $attribute) {
$code .= "\tzend_add_class_attribute(class_entry, zend_ce_allow_dynamic_properties->name, 0);\n"; $code .= $attribute->generateCode("zend_add_class_attribute(class_entry", "class_$escapedName", $allConstInfos);
} }
if ($attributeInitializationCode = generateAttributeInitialization($this->funcInfos, $this->cond)) { if ($attributeInitializationCode = generateAttributeInitialization($this->funcInfos, $this->cond)) {
@ -2460,8 +2521,10 @@ class ClassInfo {
$flags[] = "ZEND_ACC_NO_DYNAMIC_PROPERTIES"; $flags[] = "ZEND_ACC_NO_DYNAMIC_PROPERTIES";
} }
if ($this->allowsDynamicProperties) { foreach ($this->attributes as $attr) {
$flags[] = "ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES"; if ($attr->class === "AllowDynamicProperties") {
$flags[] = "ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES";
}
} }
if ($this->isNotSerializable) { if ($this->isNotSerializable) {
@ -2861,6 +2924,8 @@ class ClassInfo {
} }
class FileInfo { class FileInfo {
/** @var string[] */
public $dependencies = [];
/** @var ConstInfo[] */ /** @var ConstInfo[] */
public $constInfos = []; public $constInfos = [];
/** @var FuncInfo[] */ /** @var FuncInfo[] */
@ -3286,6 +3351,7 @@ function parseClass(
$isStrictProperties = false; $isStrictProperties = false;
$isNotSerializable = false; $isNotSerializable = false;
$allowsDynamicProperties = false; $allowsDynamicProperties = false;
$attributes = [];
if ($comment) { if ($comment) {
$tags = parseDocComment($comment); $tags = parseDocComment($comment);
@ -3304,12 +3370,11 @@ function parseClass(
foreach ($class->attrGroups as $attrGroup) { foreach ($class->attrGroups as $attrGroup) {
foreach ($attrGroup->attrs as $attr) { foreach ($attrGroup->attrs as $attr) {
switch ($attr->name->toCodeString()) { $attributes[] = new AttributeInfo($attr->name->toString(), $attr->args);
case '\\AllowDynamicProperties': switch ($attr->name->toString()) {
case 'AllowDynamicProperties':
$allowsDynamicProperties = true; $allowsDynamicProperties = true;
break; break;
default:
throw new Exception("Unhandled attribute {$attr->name->toCodeString()}.");
} }
} }
} }
@ -3348,7 +3413,7 @@ function parseClass(
? SimpleType::fromNode($class->scalarType) : null, ? SimpleType::fromNode($class->scalarType) : null,
$isDeprecated, $isDeprecated,
$isStrictProperties, $isStrictProperties,
$allowsDynamicProperties, $attributes,
$isNotSerializable, $isNotSerializable,
$extends, $extends,
$implements, $implements,
@ -3511,6 +3576,14 @@ function handleStatements(FileInfo $fileInfo, array $stmts, PrettyPrinterAbstrac
continue; 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()}"); throw new Exception("Unexpected node {$stmt->getType()}");
} }
} }

View file

@ -816,7 +816,10 @@ static zend_class_entry *register_class_OCILob(void)
INIT_CLASS_ENTRY(ce, "OCILob", class_OCILob_methods); INIT_CLASS_ENTRY(ce, "OCILob", class_OCILob_methods);
class_entry = zend_register_internal_class_ex(&ce, NULL); class_entry = zend_register_internal_class_ex(&ce, NULL);
class_entry->ce_flags |= ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES; 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; return class_entry;
} }
@ -828,7 +831,10 @@ static zend_class_entry *register_class_OCICollection(void)
INIT_CLASS_ENTRY(ce, "OCICollection", class_OCICollection_methods); INIT_CLASS_ENTRY(ce, "OCICollection", class_OCICollection_methods);
class_entry = zend_register_internal_class_ex(&ce, NULL); class_entry = zend_register_internal_class_ex(&ce, NULL);
class_entry->ce_flags |= ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES; 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; return class_entry;
} }

View file

@ -634,12 +634,12 @@ PHP_MINIT_FUNCTION(zend_test)
zend_test_attribute = register_class_ZendTestAttribute(); 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_mark_internal_attribute(zend_test_attribute);
attr->validator = zend_attribute_validate_zendtestattribute; attr->validator = zend_attribute_validate_zendtestattribute;
} }
zend_test_parameter_attribute = register_class_ZendTestParameterAttribute(); zend_test_parameter_attribute = register_class_ZendTestParameterAttribute();
zend_internal_attribute_register(zend_test_parameter_attribute, ZEND_ATTRIBUTE_TARGET_PARAMETER); zend_mark_internal_attribute(zend_test_parameter_attribute);
{ {
zend_attribute *attr; zend_attribute *attr;

View file

@ -4,6 +4,8 @@
namespace { namespace {
require "Zend/zend_attributes.stub.php";
interface _ZendTestInterface interface _ZendTestInterface
{ {
} }
@ -41,10 +43,12 @@ namespace {
public function testMethod(): bool {} public function testMethod(): bool {}
} }
#[Attribute(Attribute::TARGET_ALL)]
final class ZendTestAttribute { final class ZendTestAttribute {
} }
#[Attribute(Attribute::TARGET_PARAMETER)]
final class ZendTestParameterAttribute { final class ZendTestParameterAttribute {
public string $parameter; public string $parameter;

View file

@ -1,5 +1,5 @@
/* This is a generated file, edit the .stub.php file instead. /* This is a generated file, edit the .stub.php file instead.
* Stub hash: 1a23b7473e5b4525352445545c6b3ab374c4e949 */ * Stub hash: 3ad8ef04d52f1a099d9fd3b6c2c02b90de2980be */
ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_zend_test_array_return, 0, 0, IS_ARRAY, 0) ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_zend_test_array_return, 0, 0, IS_ARRAY, 0)
ZEND_END_ARG_INFO() ZEND_END_ARG_INFO()
@ -391,6 +391,13 @@ static zend_class_entry *register_class_ZendTestAttribute(void)
class_entry = zend_register_internal_class_ex(&ce, NULL); class_entry = zend_register_internal_class_ex(&ce, NULL);
class_entry->ce_flags |= ZEND_ACC_FINAL; 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; return class_entry;
} }
@ -408,6 +415,13 @@ static zend_class_entry *register_class_ZendTestParameterAttribute(void)
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_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_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; return class_entry;
} }