From b1822899fc36623ca45822cc047c31273992011e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1t=C3=A9=20Kocsis?= Date: Tue, 24 Aug 2021 16:35:33 +0200 Subject: [PATCH] Add support for generating optimizer function info from stubs (#7367) --- Zend/Optimizer/zend_func_info.c | 65 +--- Zend/Optimizer/zend_func_infos.h | 49 +++ Zend/zend_builtin_functions.stub.php | 30 +- Zend/zend_builtin_functions_arginfo.h | 2 +- build/gen_stub.php | 519 ++++++++++++++++++++----- ext/pgsql/pgsql.stub.php | 5 +- ext/pgsql/pgsql_arginfo.h | 2 +- ext/standard/basic_functions.stub.php | 28 +- ext/standard/basic_functions_arginfo.h | 2 +- ext/zlib/zlib.stub.php | 5 +- ext/zlib/zlib_arginfo.h | 2 +- 11 files changed, 548 insertions(+), 161 deletions(-) create mode 100644 Zend/Optimizer/zend_func_infos.h diff --git a/Zend/Optimizer/zend_func_info.c b/Zend/Optimizer/zend_func_info.c index 0c6b8a8cc88..43ee2156bcc 100644 --- a/Zend/Optimizer/zend_func_info.c +++ b/Zend/Optimizer/zend_func_info.c @@ -47,6 +47,8 @@ typedef struct _func_info_t { #define FC(name, callback) \ {name, sizeof(name)-1, 0, callback} +#include "zend_func_infos.h" + static uint32_t zend_range_info(const zend_call_info *call_info, const zend_ssa *ssa) { if (!call_info->send_unpack @@ -85,34 +87,21 @@ static uint32_t zend_range_info(const zend_call_info *call_info, const zend_ssa } } -static const func_info_t func_infos[] = { +static const func_info_t old_func_infos[] = { /* zend */ - F1("zend_version", MAY_BE_STRING), - FN("func_get_args", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_ANY), F1("get_class_vars", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF), - F1("get_class_methods", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING), - F1("get_included_files", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING), - FN("set_error_handler", MAY_BE_NULL | MAY_BE_STRING | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_OBJECT | MAY_BE_OBJECT), - F0("restore_error_handler", MAY_BE_TRUE), - F0("restore_exception_handler", MAY_BE_TRUE), F1("get_declared_traits", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING), F1("get_declared_classes", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING), F1("get_declared_interfaces", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING), F1("get_defined_functions", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ARRAY), F1("get_defined_vars", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ANY | MAY_BE_ARRAY_OF_REF), - F1("get_resource_type", MAY_BE_STRING), - F1("get_defined_constants", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ANY), F1("debug_backtrace", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_ARRAY), F1("get_loaded_extensions", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING), F1("get_extension_funcs", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING), /* ext/standard */ - FN("constant", MAY_BE_ANY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY), F1("bin2hex", MAY_BE_STRING), F1("hex2bin", MAY_BE_FALSE | MAY_BE_STRING), -#if HAVE_NANOSLEEP - F1("time_nanosleep", MAY_BE_FALSE | MAY_BE_TRUE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_LONG), -#endif #if HAVE_STRPTIME F1("strptime", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_STRING), #endif @@ -191,7 +180,6 @@ static const func_info_t func_infos[] = { F0("passthru", MAY_BE_NULL | MAY_BE_FALSE), F1("shell_exec", MAY_BE_NULL | MAY_BE_FALSE | MAY_BE_STRING), #ifdef PHP_CAN_SUPPORT_PROC_OPEN - F1("proc_open", MAY_BE_FALSE | MAY_BE_RESOURCE), F1("proc_get_status", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_FALSE | MAY_BE_ARRAY_OF_TRUE | MAY_BE_ARRAY_OF_LONG | MAY_BE_ARRAY_OF_STRING), #endif F1("random_bytes", MAY_BE_STRING), @@ -266,30 +254,14 @@ static const func_info_t func_infos[] = { #ifdef HAVE_GETHOSTNAME F1("gethostname", MAY_BE_FALSE | MAY_BE_STRING), #endif -#if defined(PHP_WIN32) || HAVE_DNS_SEARCH_FUNC -# if defined(PHP_WIN32) || HAVE_FULL_DNS_FUNCS - F1("dns_get_record", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_ARRAY), -# endif -#endif - F1("popen", MAY_BE_FALSE | MAY_BE_RESOURCE), F1("fgets", MAY_BE_FALSE | MAY_BE_STRING), F1("fread", MAY_BE_FALSE | MAY_BE_STRING), - F1("fopen", MAY_BE_FALSE | MAY_BE_RESOURCE), F1("fstat", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_LONG), F1("tempnam", MAY_BE_FALSE | MAY_BE_STRING), - F1("tmpfile", MAY_BE_FALSE | MAY_BE_RESOURCE), F1("file", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING), F1("file_get_contents", MAY_BE_FALSE | MAY_BE_STRING), - F1("stream_context_create", MAY_BE_RESOURCE), F1("stream_context_get_params", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ANY), FN("stream_context_get_options", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ANY), - FN("stream_context_get_default", MAY_BE_RESOURCE), - FN("stream_context_set_default", MAY_BE_RESOURCE), - FN("stream_filter_prepend", MAY_BE_FALSE | MAY_BE_RESOURCE), - FN("stream_filter_append", MAY_BE_FALSE | MAY_BE_RESOURCE), - F1("stream_socket_client", MAY_BE_FALSE | MAY_BE_RESOURCE), - F1("stream_socket_server", MAY_BE_FALSE | MAY_BE_RESOURCE), - F1("stream_socket_accept", MAY_BE_FALSE | MAY_BE_RESOURCE), F1("stream_socket_get_name", MAY_BE_FALSE | MAY_BE_STRING), F1("stream_socket_recvfrom", MAY_BE_FALSE | MAY_BE_STRING), #if HAVE_SOCKETPAIR @@ -306,13 +278,10 @@ static const func_info_t func_infos[] = { F1("get_headers", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_STRING | MAY_BE_ARRAY_OF_ARRAY), F1("socket_get_status", MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ANY), F1("realpath", MAY_BE_FALSE | MAY_BE_STRING), - F1("fsockopen", MAY_BE_FALSE | MAY_BE_RESOURCE), - FN("pfsockopen", MAY_BE_FALSE | MAY_BE_RESOURCE), F1("pack", MAY_BE_STRING), F1("unpack", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY), F1("get_browser", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_OBJECT | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ANY), F1("crypt", MAY_BE_STRING), - FN("opendir", MAY_BE_FALSE | MAY_BE_RESOURCE), F1("getcwd", MAY_BE_FALSE | MAY_BE_STRING), F1("readdir", MAY_BE_FALSE | MAY_BE_STRING), F1("dir", MAY_BE_FALSE | MAY_BE_OBJECT), @@ -542,7 +511,6 @@ static const func_info_t func_infos[] = { F1("utf8_decode", MAY_BE_STRING), /* ext/zlib */ - F1("gzopen", MAY_BE_FALSE | MAY_BE_RESOURCE), F1("gzfile", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_LONG | MAY_BE_ARRAY_OF_STRING), F1("gzcompress", MAY_BE_FALSE | MAY_BE_STRING), F1("gzuncompress", MAY_BE_FALSE | MAY_BE_STRING), @@ -686,7 +654,6 @@ static const func_info_t func_infos[] = { F1("pg_get_result", MAY_BE_FALSE | MAY_BE_OBJECT), F1("pg_result_status", MAY_BE_LONG | MAY_BE_STRING), F1("pg_get_notify", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_ANY | MAY_BE_ARRAY_OF_ANY), - F1("pg_socket", MAY_BE_FALSE | MAY_BE_RESOURCE), F1("pg_meta_data", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ARRAY), F1("pg_convert", MAY_BE_FALSE | MAY_BE_ARRAY | MAY_BE_ARRAY_KEY_STRING | MAY_BE_ARRAY_OF_ANY), F1("pg_insert", MAY_BE_FALSE | MAY_BE_TRUE | MAY_BE_OBJECT | MAY_BE_STRING), @@ -889,25 +856,33 @@ ZEND_API uint32_t zend_get_func_info( return ret; } -int zend_func_info_startup(void) +void zend_func_info_add(const func_info_t *func_infos, size_t n) { size_t i; + for (i = 0; i < n; i++) { + zend_string *key = zend_string_init_interned(func_infos[i].name, func_infos[i].name_len, 1); + + if (zend_hash_add_ptr(&func_info, key, (void**)&func_infos[i]) == NULL) { + fprintf(stderr, "ERROR: Duplicate function info for \"%s\"\n", func_infos[i].name); + } + + zend_string_release_ex(key, 1); + } +} + +int zend_func_info_startup(void) +{ if (zend_func_info_rid == -1) { zend_func_info_rid = zend_get_resource_handle("Zend Optimizer"); if (zend_func_info_rid < 0) { return FAILURE; } - zend_hash_init(&func_info, sizeof(func_infos)/sizeof(func_info_t), NULL, NULL, 1); - for (i = 0; i < sizeof(func_infos)/sizeof(func_info_t); i++) { - zend_string *key = zend_string_init_interned(func_infos[i].name, func_infos[i].name_len, 1); + zend_hash_init(&func_info, sizeof(old_func_infos)/sizeof(func_info_t) + sizeof(func_infos)/sizeof(func_info_t), NULL, NULL, 1); - if (zend_hash_add_ptr(&func_info, key, (void**)&func_infos[i]) == NULL) { - fprintf(stderr, "ERROR: Duplicate function info for \"%s\"\n", func_infos[i].name); - } - zend_string_release_ex(key, 1); - } + zend_func_info_add(old_func_infos, sizeof(old_func_infos)/sizeof(func_info_t)); + zend_func_info_add(func_infos, sizeof(func_infos)/sizeof(func_info_t)); } return SUCCESS; diff --git a/Zend/Optimizer/zend_func_infos.h b/Zend/Optimizer/zend_func_infos.h new file mode 100644 index 00000000000..fc7232aac7c --- /dev/null +++ b/Zend/Optimizer/zend_func_infos.h @@ -0,0 +1,49 @@ +/* This is a generated file, edit the .stub.php files instead. */ + +static const func_info_t func_infos[] = { + F1("zend_version", MAY_BE_STRING), + FN("func_get_args", MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_LONG|MAY_BE_ARRAY_OF_ANY), + F1("get_class_methods", MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_LONG|MAY_BE_ARRAY_OF_STRING), + F1("get_included_files", MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_LONG|MAY_BE_ARRAY_OF_STRING), + FN("set_error_handler", MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_LONG|MAY_BE_ARRAY_OF_STRING|MAY_BE_ARRAY_OF_OBJECT|MAY_BE_OBJECT|MAY_BE_NULL), + F0("restore_error_handler", MAY_BE_TRUE), + FN("set_exception_handler", MAY_BE_STRING|MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_LONG|MAY_BE_ARRAY_OF_STRING|MAY_BE_ARRAY_OF_OBJECT|MAY_BE_OBJECT|MAY_BE_NULL), + F0("restore_exception_handler", MAY_BE_TRUE), + F1("get_resource_type", MAY_BE_STRING), + F1("get_defined_constants", MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_STRING|MAY_BE_ARRAY_OF_ANY), + FN("socket_export_stream", MAY_BE_RESOURCE|MAY_BE_FALSE), + F1("gzopen", MAY_BE_RESOURCE|MAY_BE_FALSE), +#if HAVE_NANOSLEEP + F1("time_nanosleep", MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_STRING|MAY_BE_ARRAY_OF_LONG|MAY_BE_BOOL), +#endif +#if defined(PHP_WIN32) || HAVE_DNS_SEARCH_FUNC + F1("dns_get_record", MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_LONG|MAY_BE_ARRAY_OF_ARRAY|MAY_BE_FALSE), +#endif + FN("opendir", MAY_BE_RESOURCE|MAY_BE_FALSE), + F1("popen", MAY_BE_RESOURCE|MAY_BE_FALSE), + F1("fopen", MAY_BE_RESOURCE|MAY_BE_FALSE), + F1("tmpfile", MAY_BE_RESOURCE|MAY_BE_FALSE), + F1("fsockopen", MAY_BE_RESOURCE|MAY_BE_FALSE), + FN("pfsockopen", MAY_BE_RESOURCE|MAY_BE_FALSE), +#if defined(PHP_CAN_SUPPORT_PROC_OPEN) + F1("proc_open", MAY_BE_RESOURCE|MAY_BE_FALSE), +#endif + F1("stream_context_create", MAY_BE_RESOURCE), + FN("stream_context_get_default", MAY_BE_RESOURCE), + FN("stream_context_set_default", MAY_BE_RESOURCE), + FN("stream_filter_prepend", MAY_BE_RESOURCE|MAY_BE_FALSE), + FN("stream_filter_append", MAY_BE_RESOURCE|MAY_BE_FALSE), + F1("stream_socket_client", MAY_BE_RESOURCE|MAY_BE_FALSE), + F1("stream_socket_server", MAY_BE_RESOURCE|MAY_BE_FALSE), + F1("stream_socket_accept", MAY_BE_RESOURCE|MAY_BE_FALSE), + FN("zip_open", MAY_BE_RESOURCE|MAY_BE_LONG|MAY_BE_FALSE), + FN("zip_read", MAY_BE_RESOURCE|MAY_BE_FALSE), + FN("oci_new_connect", MAY_BE_RESOURCE|MAY_BE_FALSE), + FN("oci_connect", MAY_BE_RESOURCE|MAY_BE_FALSE), + FN("oci_pconnect", MAY_BE_RESOURCE|MAY_BE_FALSE), + FN("oci_parse", MAY_BE_RESOURCE|MAY_BE_FALSE), + FN("oci_get_implicit_resultset", MAY_BE_RESOURCE|MAY_BE_FALSE), + FN("oci_password_change", MAY_BE_RESOURCE|MAY_BE_BOOL), + FN("oci_new_cursor", MAY_BE_RESOURCE|MAY_BE_FALSE), + F1("pg_socket", MAY_BE_RESOURCE|MAY_BE_FALSE), +}; diff --git a/Zend/zend_builtin_functions.stub.php b/Zend/zend_builtin_functions.stub.php index b48732c5c72..965aa5cd966 100644 --- a/Zend/zend_builtin_functions.stub.php +++ b/Zend/zend_builtin_functions.stub.php @@ -6,12 +6,14 @@ class stdClass { } +/** @refcount 1 */ function zend_version(): string {} function func_num_args(): int {} function func_get_arg(int $position): mixed {} +/** @return array */ function func_get_args(): array {} function strlen(string $string): int {} @@ -48,6 +50,10 @@ function get_object_vars(object $object): array {} function get_mangled_object_vars(object $object): array {} +/** + * @return array + * @refcount 1 + */ function get_class_methods(object|string $object_or_class): array {} /** @param object|string $object_or_class */ @@ -68,9 +74,16 @@ function function_exists(string $function): bool {} function class_alias(string $class, string $alias, bool $autoload = true): bool {} +/** + * @return array + * @refcount 1 + */ function get_included_files(): array {} -/** @alias get_included_files */ +/** + * @return array + * @alias get_included_files + */ function get_required_files(): array {} function trigger_error(string $message, int $error_level = E_USER_NOTICE): bool {} @@ -78,14 +91,16 @@ function trigger_error(string $message, int $error_level = E_USER_NOTICE): bool /** @alias trigger_error */ function user_error(string $message, int $error_level = E_USER_NOTICE): bool {} -/** @return string|array|object|null */ +/** @return string|array|object|null */ function set_error_handler(?callable $callback, int $error_levels = E_ALL) {} +/** @return true */ function restore_error_handler(): bool {} -/** @return string|array|object|null */ +/** @return string|array|object|null */ function set_exception_handler(?callable $callback) {} +/** @return true */ function restore_exception_handler(): bool {} function get_declared_classes(): array {} @@ -98,7 +113,10 @@ function get_defined_functions(bool $exclude_disabled = true): array {} function get_defined_vars(): array {} -/** @param resource $resource */ +/** + * @param resource $resource + * @refcount 1 + */ function get_resource_type($resource): string {} /** @param resource $resource */ @@ -108,6 +126,10 @@ function get_resources(?string $type = null): array {} function get_loaded_extensions(bool $zend_extensions = false): array {} +/** + * @return array + * @refcount 1 + */ function get_defined_constants(bool $categorize = false): array {} function debug_backtrace(int $options = DEBUG_BACKTRACE_PROVIDE_OBJECT, int $limit = 0): array {} diff --git a/Zend/zend_builtin_functions_arginfo.h b/Zend/zend_builtin_functions_arginfo.h index f847216d011..c0128e3fec5 100644 --- a/Zend/zend_builtin_functions_arginfo.h +++ b/Zend/zend_builtin_functions_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 38936b472d60d9eeb2cc8e35c1276e9f707837bf */ + * Stub hash: c8a0a1e4cfece42832f737b33e317ba88b91fab5 */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_zend_version, 0, 0, IS_STRING, 0) ZEND_END_ARG_INFO() diff --git a/build/gen_stub.php b/build/gen_stub.php index 23360b496e5..0a16fdf5d5a 100755 --- a/build/gen_stub.php +++ b/build/gen_stub.php @@ -107,22 +107,59 @@ class Context { public $forceRegeneration = false; } +class ArrayType extends SimpleType { + /** @var Type */ + public $keyType; + + /** @var Type */ + public $valueType; + + public static function createGenericArray(): self + { + return new ArrayType(Type::fromString("int|string"), Type::fromString("mixed")); + } + + public function __construct(Type $keyType, Type $valueType) + { + parent::__construct("array", true); + + $this->keyType = $keyType; + $this->valueType = $valueType; + } + + public function toOptimizerTypeMask(): string { + $typeMasks = [ + parent::toOptimizerTypeMask(), + $this->keyType->toOptimizerTypeMaskForArrayKey(), + $this->valueType->toOptimizerTypeMaskForArrayValue(), + ]; + + return implode("|", $typeMasks); + } + + public function equals(SimpleType $other): bool { + if (!parent::equals($other)) { + return false; + } + + assert(get_class($other) === self::class); + + return Type::equals($this->keyType, $other->keyType) && + Type::equals($this->valueType, $other->valueType); + } +} + class SimpleType { /** @var string */ public $name; /** @var bool */ public $isBuiltin; - public function __construct(string $name, bool $isBuiltin) { - $this->name = $name; - $this->isBuiltin = $isBuiltin; - } - public static function fromNode(Node $node): SimpleType { if ($node instanceof Node\Name) { if ($node->toLowerString() === 'static') { // PHP internally considers "static" a builtin type. - return new SimpleType($node->toString(), true); + return new SimpleType($node->toLowerString(), true); } if ($node->toLowerString() === 'self') { @@ -132,39 +169,55 @@ class SimpleType { assert($node->isFullyQualified()); return new SimpleType($node->toString(), false); } + if ($node instanceof Node\Identifier) { - return new SimpleType($node->toString(), true); + if ($node->toLowerString() === 'array') { + return ArrayType::createGenericArray(); + } + + return new SimpleType($node->toLowerString(), true); } + throw new Exception("Unexpected node type"); } - public static function fromPhpDoc(string $type): SimpleType + public static function fromString(string $typeString): SimpleType { - switch (strtolower($type)) { + switch (strtolower($typeString)) { case "void": case "null": case "false": + case "true": case "bool": case "int": case "float": case "string": - case "array": case "iterable": case "object": case "resource": case "mixed": case "static": case "never": - return new SimpleType(strtolower($type), true); + return new SimpleType(strtolower($typeString), true); + case "array": + return ArrayType::createGenericArray(); case "self": throw new Exception('The exact class name must be used instead of "self"'); } - if (strpos($type, "[]") !== false) { - return new SimpleType("array", true); + $matches = []; + $isArray = preg_match("/(.*)\s*\[\s*\]/", $typeString, $matches); + if ($isArray) { + return new ArrayType(Type::fromString("int"), Type::fromString($matches[1])); } - return new SimpleType($type, false); + $matches = []; + $isArray = preg_match("/array\s*<\s*([A-Za-z0-9_-|]+)\s*,\s*([A-Za-z0-9_-|]+)\s*>/i", $typeString, $matches); + if ($isArray) { + return new ArrayType(Type::fromString($matches[1]), Type::fromString($matches[2])); + } + + return new SimpleType($typeString, false); } public static function null(): SimpleType @@ -177,88 +230,164 @@ class SimpleType { return new SimpleType("void", true); } + protected function __construct(string $name, bool $isBuiltin) { + $this->name = $name; + $this->isBuiltin = $isBuiltin; + } + + public function isScalar(): bool { + return $this->isBuiltin && in_array($this->name, ["null", "false", "true", "bool", "int", "float"], true); + } + public function isNull(): bool { return $this->isBuiltin && $this->name === 'null'; } public function toTypeCode(): string { assert($this->isBuiltin); - switch (strtolower($this->name)) { - case "bool": - return "_IS_BOOL"; - case "int": - return "IS_LONG"; - case "float": - return "IS_DOUBLE"; - case "string": - return "IS_STRING"; - case "array": - return "IS_ARRAY"; - case "object": - return "IS_OBJECT"; - case "void": - return "IS_VOID"; - case "callable": - return "IS_CALLABLE"; - case "iterable": - return "IS_ITERABLE"; - case "mixed": - return "IS_MIXED"; - case "static": - return "IS_STATIC"; - case "never": - return "IS_NEVER"; - default: - throw new Exception("Not implemented: $this->name"); + switch ($this->name) { + case "bool": + return "_IS_BOOL"; + case "int": + return "IS_LONG"; + case "float": + return "IS_DOUBLE"; + case "string": + return "IS_STRING"; + case "array": + return "IS_ARRAY"; + case "object": + return "IS_OBJECT"; + case "void": + return "IS_VOID"; + case "callable": + return "IS_CALLABLE"; + case "iterable": + return "IS_ITERABLE"; + case "mixed": + return "IS_MIXED"; + case "static": + return "IS_STATIC"; + case "never": + return "IS_NEVER"; + default: + throw new Exception("Not implemented: $this->name"); } } - public function toTypeMask() { + public function toTypeMask(): string { assert($this->isBuiltin); - switch (strtolower($this->name)) { - case "null": - return "MAY_BE_NULL"; - case "false": - return "MAY_BE_FALSE"; - case "bool": - return "MAY_BE_BOOL"; - case "int": - return "MAY_BE_LONG"; - case "float": - return "MAY_BE_DOUBLE"; - case "string": - return "MAY_BE_STRING"; - case "array": - return "MAY_BE_ARRAY"; - case "object": - return "MAY_BE_OBJECT"; - case "callable": - return "MAY_BE_CALLABLE"; - case "mixed": - return "MAY_BE_ANY"; - case "static": - return "MAY_BE_STATIC"; - case "never": - return "MAY_BE_NEVER"; - default: - throw new Exception("Not implemented: $this->name"); + + switch ($this->name) { + case "null": + return "MAY_BE_NULL"; + case "false": + return "MAY_BE_FALSE"; + case "bool": + return "MAY_BE_BOOL"; + case "int": + return "MAY_BE_LONG"; + case "float": + return "MAY_BE_DOUBLE"; + case "string": + return "MAY_BE_STRING"; + case "array": + return "MAY_BE_ARRAY"; + case "object": + return "MAY_BE_OBJECT"; + case "callable": + return "MAY_BE_CALLABLE"; + case "mixed": + return "MAY_BE_ANY"; + case "static": + return "MAY_BE_STATIC"; + case "never": + return "MAY_BE_NEVER"; + default: + throw new Exception("Not implemented: $this->name"); } } + public function toOptimizerTypeMaskForArrayKey(): string { + assert($this->isBuiltin); + + switch ($this->name) { + case "int": + return "MAY_BE_ARRAY_KEY_LONG"; + case "string": + return "MAY_BE_ARRAY_KEY_STRING"; + default: + throw new Exception("Type $this->name cannot be an array key"); + } + } + + public function toOptimizerTypeMaskForArrayValue(): string { + if (!$this->isBuiltin) { + return "MAY_BE_ARRAY_OF_OBJECT"; + } + + switch ($this->name) { + case "null": + return "MAY_BE_ARRAY_OF_NULL"; + case "false": + return "MAY_BE_ARRAY_OF_FALSE"; + case "bool": + return "MAY_BE_ARRAY_OF_FALSE|MAY_BE_ARRAY_OF_TRUE"; + case "int": + return "MAY_BE_ARRAY_OF_LONG"; + case "float": + return "MAY_BE_ARRAY_OF_DOUBLE"; + case "string": + return "MAY_BE_ARRAY_OF_STRING"; + case "array": + return "MAY_BE_ARRAY_OF_ARRAY"; + case "object": + return "MAY_BE_ARRAY_OF_OBJECT"; + case "resource": + return "MAY_BE_ARRAY_OF_RESOURCE"; + case "mixed": + return "MAY_BE_ARRAY_OF_ANY"; + default: + throw new Exception("Type $this->name cannot be an array value"); + } + } + + public function toOptimizerTypeMask(): string { + if (!$this->isBuiltin) { + return "MAY_BE_OBJECT"; + } + + if ($this->name === "resource") { + return "MAY_BE_RESOURCE"; + } + + if ($this->name === "true") { + return "MAY_BE_TRUE"; + } + + if ($this->name === "mixed") { + return "MAY_BE_ANY|MAY_BE_ARRAY_KEY_ANY|MAY_BE_ARRAY_OF_ANY"; + } + + return $this->toTypeMask(); + } + public function toEscapedName(): string { return str_replace('\\', '\\\\', $this->name); } - public function equals(SimpleType $other) { - return $this->name === $other->name - && $this->isBuiltin === $other->isBuiltin; + public function equals(SimpleType $other): bool { + return $this->name === $other->name && $this->isBuiltin === $other->isBuiltin; } } class Type { - /** @var SimpleType[] $types */ + /** @var SimpleType[] */ public $types; + /** + * @param SimpleType[] $types + */ public function __construct(array $types) { $this->types = $types; } @@ -276,23 +405,58 @@ class Type { return new Type([SimpleType::fromNode($node)]); } - public static function fromPhpDoc(string $phpDocType) { - $types = explode("|", $phpDocType); - + public static function fromString(string $typeString): self { + $typeString .= "|"; $simpleTypes = []; - foreach ($types as $type) { - $simpleTypes[] = SimpleType::fromPhpDoc($type); + $simpleTypeOffset = 0; + $inArray = false; + + $typeStringLength = strlen($typeString); + for ($i = 0; $i < $typeStringLength; $i++) { + $char = $typeString[$i]; + + if ($char === "<") { + $inArray = true; + continue; + } + + if ($char === ">") { + $inArray = false; + continue; + } + + if ($inArray) { + continue; + } + + if ($char === "|") { + $simpleTypeName = trim(substr($typeString, $simpleTypeOffset, $i - $simpleTypeOffset)); + $simpleTypes[] = SimpleType::fromString($simpleTypeName); + + $simpleTypeOffset = $i + 1; + } } return new Type($simpleTypes); } + public function isScalar(): bool { + foreach ($this->types as $type) { + if (!$type->isScalar()) { + return false; + } + } + + return true; + } + public function isNullable(): bool { foreach ($this->types as $type) { if ($type->isNull()) { return true; } } + return false; } @@ -323,6 +487,36 @@ class Type { return new ArginfoType($classTypes, $builtinTypes); } + public function toOptimizerTypeMask(): string { + $optimizerTypes = []; + + foreach ($this->types as $type) { + $optimizerTypes[] = $type->toOptimizerTypeMask(); + } + + return implode("|", $optimizerTypes); + } + + public function toOptimizerTypeMaskForArrayKey(): string { + $typeMasks = []; + + foreach ($this->types as $type) { + $typeMasks[] = $type->toOptimizerTypeMaskForArrayKey(); + } + + return implode("|", $typeMasks); + } + + public function toOptimizerTypeMaskForArrayValue(): string { + $typeMasks = []; + + foreach ($this->types as $type) { + $typeMasks[] = $type->toOptimizerTypeMaskForArrayValue(); + } + + return implode("|", $typeMasks); + } + public function getTypeForDoc(DOMDocument $doc): DOMElement { if (count($this->types) > 1) { $typeElement = $doc->createElement('type'); @@ -333,7 +527,14 @@ class Type { $typeElement->appendChild($unionTypeElement); } } else { - $typeElement = $doc->createElement('type', $this->types[0]->name); + $type = $this->types[0]; + if ($type->isBuiltin && strtolower($type->name) === "true") { + $name = "bool"; + } else { + $name = $type->name; + } + + $typeElement = $doc->createElement('type', $name); } return $typeElement; @@ -423,8 +624,7 @@ class ArgInfo { $this->name = $name; $this->sendBy = $sendBy; $this->isVariadic = $isVariadic; - $this->type = $type; - $this->phpDocType = $phpDocType; + $this->setTypes($type, $phpDocType); $this->defaultValue = $defaultValue; } @@ -488,6 +688,16 @@ class ArgInfo { return $this->defaultValue; } + + private function setTypes(?Type $type, ?Type $phpDocType): void + { + if ($phpDocType !== null && Type::equals($type, $phpDocType)) { + throw new Exception('PHPDoc param type "' . $phpDocType->__toString() . '" is unnecessary'); + } + + $this->type = $type; + $this->phpDocType = $phpDocType; + } } class PropertyName { @@ -619,6 +829,16 @@ class MethodName implements FunctionOrMethodName { } class ReturnInfo { + const REFCOUNT_0 = "0"; + const REFCOUNT_1 = "1"; + const REFCOUNT_N = "N"; + + const REFCOUNTS = [ + self::REFCOUNT_0, + self::REFCOUNT_1, + self::REFCOUNT_N, + ]; + /** @var bool */ public $byRef; /** @var Type|null */ @@ -627,15 +847,16 @@ class ReturnInfo { public $phpDocType; /** @var bool */ public $tentativeReturnType; + /** @var string */ + public $refcount; - public function __construct(bool $byRef, ?Type $type, ?Type $phpDocType, bool $tentativeReturnType) { + public function __construct(bool $byRef, ?Type $type, ?Type $phpDocType, bool $tentativeReturnType, ?string $refcount) { $this->byRef = $byRef; - $this->type = $type; - $this->phpDocType = $phpDocType; - $this->tentativeReturnType = $tentativeReturnType; + $this->setTypes($type, $phpDocType, $tentativeReturnType); + $this->setRefcount($refcount); } - public function equals(ReturnInfo $other): bool { + public function equalsApartFromPhpDocAndRefcount(ReturnInfo $other): bool { return $this->byRef === $other->byRef && Type::equals($this->type, $other->type) && $this->tentativeReturnType === $other->tentativeReturnType; @@ -644,6 +865,42 @@ class ReturnInfo { public function getMethodSynopsisType(): ?Type { return $this->type ?? $this->phpDocType; } + + private function setTypes(?Type $type, ?Type $phpDocType, bool $tentativeReturnType): void + { + if ($phpDocType !== null && Type::equals($type, $phpDocType)) { + throw new Exception('PHPDoc return type "' . $phpDocType->__toString() . '" is unnecessary'); + } + + $this->type = $type; + $this->phpDocType = $phpDocType; + $this->tentativeReturnType = $tentativeReturnType; + } + + private function setRefcount(?string $refcount): void + { + $type = $this->phpDocType ?? $this->type; + $isScalarType = $type !== null && $type->isScalar(); + + if ($refcount === null) { + $this->refcount = $isScalarType ? self::REFCOUNT_0 : self::REFCOUNT_N; + return; + } + + if (!in_array($refcount, ReturnInfo::REFCOUNTS, true)) { + throw new Exception("@refcount must have one of the following values: \"0\", \"1\", \"N\", $refcount given"); + } + + if ($isScalarType && $refcount !== self::REFCOUNT_0) { + throw new Exception('A scalar return type of "' . $type->__toString() . '" must have a refcount of "' . self::REFCOUNT_0 . '"'); + } + + if (!$isScalarType && $refcount === self::REFCOUNT_0) { + throw new Exception('A non-scalar return type of "' . $type->__toString() . '" cannot have a refcount of "' . self::REFCOUNT_0 . '"'); + } + + $this->refcount = $refcount; + } } class FuncInfo { @@ -752,7 +1009,7 @@ class FuncInfo { return false; } - public function equalsApartFromName(FuncInfo $other): bool { + public function equalsApartFromNameAndRefcount(FuncInfo $other): bool { if (count($this->args) !== count($other->args)) { return false; } @@ -763,7 +1020,7 @@ class FuncInfo { } } - return $this->return->equals($other->return) + return $this->return->equalsApartFromPhpDocAndRefcount($other->return) && $this->numRequiredArgs === $other->numRequiredArgs && $this->cond === $other->cond; } @@ -860,6 +1117,27 @@ class FuncInfo { } } + public function getOptimizerInfo(): ?string { + if ($this->isMethod()) { + return null; + } + + if ($this->alias !== null) { + return null; + } + + if ($this->return->refcount !== ReturnInfo::REFCOUNT_1 && $this->return->phpDocType === null) { + return null; + } + + $type = $this->return->phpDocType ?? $this->return->type; + if ($type === null) { + return null; + } + + return " F" . $this->return->refcount . '("' . $this->name->__toString() . '", ' . $type->toOptimizerTypeMask() . "),\n"; + } + public function discardInfoForOldPhpVersions(): void { $this->return->type = null; foreach ($this->args as $arg) { @@ -1733,12 +2011,12 @@ class DocCommentTag { $matches = []; if ($this->name === "param") { - preg_match('/^\s*([\w\|\\\\\[\]]+)\s*\$\w+.*$/', $value, $matches); + preg_match('/^\s*([\w\|\\\\\[\]<>, ]+)\s*\$\w+.*$/', $value, $matches); } elseif ($this->name === "return") { - preg_match('/^\s*([\w\|\\\\\[\]]+)(\s+|$)/', $value, $matches); + preg_match('/^\s*([\w\|\\\\\[\]<>, ]+)(\s+|$)/', $value, $matches); } - if (isset($matches[1]) === false) { + if (!isset($matches[1])) { throw new Exception("@$this->name doesn't contain a type or has an invalid format \"$value\""); } @@ -1759,7 +2037,7 @@ class DocCommentTag { preg_match('/^\s*\$(\w+).*$/', $value, $matches); } - if (isset($matches[1]) === false) { + if (!isset($matches[1])) { throw new Exception("@$this->name doesn't contain a variable name or has an invalid format \"$value\""); } @@ -1799,6 +2077,7 @@ function parseFunctionLike( $docReturnType = null; $tentativeReturnType = false; $docParamTypes = []; + $refcount = null; if ($comment) { $tags = parseDocComment($comment); @@ -1827,6 +2106,8 @@ function parseFunctionLike( $docReturnType = $tag->getType(); } else if ($tag->name === 'param') { $docParamTypes[$tag->getVariableName()] = $tag->getType(); + } else if ($tag->name === 'refcount') { + $refcount = $tag->getValue(); } } } @@ -1883,7 +2164,7 @@ function parseFunctionLike( $sendBy, $param->variadic, $type, - isset($docParamTypes[$varName]) ? Type::fromPhpDoc($docParamTypes[$varName]) : null, + isset($docParamTypes[$varName]) ? Type::fromString($docParamTypes[$varName]) : null, $param->default ? $prettyPrinter->prettyPrintExpr($param->default) : null ); if (!$param->default && !$param->variadic) { @@ -1903,8 +2184,9 @@ function parseFunctionLike( $return = new ReturnInfo( $func->returnsByRef(), $returnType ? Type::fromNode($returnType) : null, - $docReturnType ? Type::fromPhpDoc($docReturnType) : null, - $tentativeReturnType + $docReturnType ? Type::fromString($docReturnType) : null, + $tentativeReturnType, + $refcount ); return new FuncInfo( @@ -2302,7 +2584,7 @@ function funcInfoToCode(FuncInfo $funcInfo): string { /** @param FuncInfo[] $generatedFuncInfos */ function findEquivalentFuncInfo(array $generatedFuncInfos, FuncInfo $funcInfo): ?FuncInfo { foreach ($generatedFuncInfos as $generatedFuncInfo) { - if ($generatedFuncInfo->equalsApartFromName($funcInfo)) { + if ($generatedFuncInfo->equalsApartFromNameAndRefcount($funcInfo)) { return $generatedFuncInfo; } } @@ -2416,6 +2698,22 @@ function generateFunctionEntries(?Name $className, array $funcInfos): string { return $code; } +/** @param FuncInfo[] $funcInfos */ +function generateOptimizerInfo(array $funcInfos): string { + + $code = "/* This is a generated file, edit the .stub.php files instead. */\n\n"; + + $code .= "static const func_info_t func_infos[] = {\n"; + + $code .= generateCodeWithConditions($funcInfos, "", function (FuncInfo $funcInfo) { + return $funcInfo->getOptimizerInfo(); + }); + + $code .= "};\n"; + + return $code; +} + /** * @param ClassInfo[] $classMap * @return array @@ -2433,7 +2731,6 @@ function generateClassSynopses(array $classMap): array { return $result; } - /** * @param ClassInfo[] $classMap * @return array @@ -2812,7 +3109,7 @@ $options = getopt( "fh", [ "force-regeneration", "parameter-stats", "help", "verify", "generate-classsynopses", "replace-classsynopses", - "generate-methodsynopses", "replace-methodsynopses" + "generate-methodsynopses", "replace-methodsynopses", "generate-optimizer-info" ], $optind ); @@ -2824,8 +3121,9 @@ $generateClassSynopses = isset($options["generate-classsynopses"]); $replaceClassSynopses = isset($options["replace-classsynopses"]); $generateMethodSynopses = isset($options["generate-methodsynopses"]); $replaceMethodSynopses = isset($options["replace-methodsynopses"]); +$generateOptimizerInfo = isset($options["generate-optimizer-info"]); $context->forceRegeneration = isset($options["f"]) || isset($options["force-regeneration"]); -$context->forceParse = $context->forceRegeneration || $printParameterStats || $verify || $generateClassSynopses || $replaceClassSynopses || $generateMethodSynopses || $replaceMethodSynopses; +$context->forceParse = $context->forceRegeneration || $printParameterStats || $verify || $generateClassSynopses || $generateOptimizerInfo || $replaceClassSynopses || $generateMethodSynopses || $replaceMethodSynopses; $targetSynopses = $argv[$argc - 1] ?? null; if ($replaceClassSynopses && $targetSynopses === null) { @@ -2961,13 +3259,22 @@ if ($verify) { $aliasArgs, $aliasedArgs ); + $aliasedReturn = $aliasedFunc->return; + $aliasReturn = $aliasFunc->return; + if (!$aliasedFunc->name->isConstructor() && !$aliasFunc->name->isConstructor()) { - $aliasedReturnType = $aliasedFunc->return->type ?? $aliasedFunc->return->phpDocType; - $aliasReturnType = $aliasFunc->return->type ?? $aliasFunc->return->phpDocType; + $aliasedReturnType = $aliasedReturn->type ?? $aliasedReturn->phpDocType; + $aliasReturnType = $aliasReturn->type ?? $aliasReturn->phpDocType; if ($aliasReturnType != $aliasedReturnType) { $errors[] = "{$aliasFunc->name}() and {$aliasedFunc->name}() must have the same return type"; } } + + $aliasedPhpDocReturnType = $aliasedReturn->phpDocType; + $aliasPhpDocReturnType = $aliasReturn->phpDocType; + if ($aliasedPhpDocReturnType != $aliasPhpDocReturnType && $aliasedPhpDocReturnType != $aliasReturn->type && $aliasPhpDocReturnType != $aliasedReturn->type) { + $errors[] = "{$aliasFunc->name}() and {$aliasedFunc->name}() must have the same PHPDoc return type"; + } } echo implode("\n", $errors); @@ -3004,7 +3311,6 @@ if ($replaceClassSynopses) { } } - if ($generateMethodSynopses) { $methodSynopsesDirectory = getcwd() . "/methodsynopses"; @@ -3031,3 +3337,12 @@ if ($replaceMethodSynopses) { } } } + +if ($generateOptimizerInfo) { + $filename = dirname(__FILE__, 2) . "/Zend/Optimizer/zend_func_infos.h"; + $optimizerInfo = generateOptimizerInfo($funcMap); + + if (file_put_contents($filename, $optimizerInfo)) { + echo "Saved $filename\n"; + } +} diff --git a/ext/pgsql/pgsql.stub.php b/ext/pgsql/pgsql.stub.php index fc8ac003943..16e0f58d0f4 100644 --- a/ext/pgsql/pgsql.stub.php +++ b/ext/pgsql/pgsql.stub.php @@ -401,7 +401,10 @@ namespace { function pg_get_pid(PgSql\Connection $connection): int {} - /** @return resource|false */ + /** + * @return resource|false + * @refcount 1 + */ function pg_socket(PgSql\Connection $connection) {} function pg_consume_input(PgSql\Connection $connection): bool {} diff --git a/ext/pgsql/pgsql_arginfo.h b/ext/pgsql/pgsql_arginfo.h index 28548732267..6229bb93ca2 100644 --- a/ext/pgsql/pgsql_arginfo.h +++ b/ext/pgsql/pgsql_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 37814767f8290b717a4facbdf55ef3bfc1fc24c8 */ + * Stub hash: 09b927fbe51265aa3895c6fadbbe66eb3087d4df */ ZEND_BEGIN_ARG_WITH_RETURN_OBJ_TYPE_MASK_EX(arginfo_pg_connect, 0, 1, PgSql\\Connection, MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, connection_string, IS_STRING, 0) diff --git a/ext/standard/basic_functions.stub.php b/ext/standard/basic_functions.stub.php index 7a21bab7d03..b658f5dbc66 100755 --- a/ext/standard/basic_functions.stub.php +++ b/ext/standard/basic_functions.stub.php @@ -288,6 +288,10 @@ function sleep(int $seconds): int {} function usleep(int $microseconds): void {} #if HAVE_NANOSLEEP +/** + * @return array|bool + * @refcount 1 + */ function time_nanosleep(int $seconds, int $nanoseconds): array|bool {} function time_sleep_until(float $timestamp): bool {} @@ -421,6 +425,8 @@ function checkdnsrr(string $hostname, string $type = "MX"): bool {} /** * @param array $authoritative_name_servers * @param array $additional_records + * @return array|false + * @refcount 1 */ function dns_get_record(string $hostname, int $type = DNS_ANY, &$authoritative_name_servers = null, &$additional_records = null, bool $raw = false): array|false {} @@ -772,7 +778,10 @@ function get_meta_tags(string $filename, bool $use_include_path = false): array| /** @param resource $handle */ function pclose($handle): int {} -/** @return resource|false */ +/** + * @return resource|false + * @refcount 1 + */ function popen(string $command, string $mode) {} /** @param resource|null $context */ @@ -804,6 +813,7 @@ function fread($stream, int $length): string|false {} /** * @param resource|null $context * @return resource|false + * @refcount 1 */ function fopen(string $filename, string $mode, bool $use_include_path = false, $context = null) {} @@ -854,7 +864,10 @@ function copy(string $from, string $to, $context = null): bool {} function tempnam(string $directory, string $prefix): string|false {} -/** @return resource|false */ +/** + * @return resource|false + * @refcount 1 + */ function tmpfile() {} /** @param resource|null $context */ @@ -975,6 +988,7 @@ function vfprintf($stream, string $format, array $values): int {} * @param int $error_code * @param string $error_message * @return resource|false + * @refcount 1 */ function fsockopen(string $hostname, int $port = -1, &$error_code = null, &$error_message = null, ?float $timeout = null) {} @@ -1165,6 +1179,7 @@ function password_algos(): array {} /** * @param array $pipes * @return resource|false + * @refcount 1 */ function proc_open(array|string $command, array $descriptor_spec, &$pipes, ?string $cwd = null, ?array $env_vars = null, ?array $options = null) {} @@ -1214,7 +1229,10 @@ function soundex(string $string): string {} function stream_select(?array &$read, ?array &$write, ?array &$except, ?int $seconds, ?int $microseconds = null): int|false {} -/** @return resource */ +/** + * @return resource + * @refcount 1 + */ function stream_context_create(?array $options = null, ?array $params = null) {} /** @param resource $context */ @@ -1255,6 +1273,7 @@ function stream_filter_remove($stream_filter): bool {} * @param string $error_message * @param resource|null $context * @return resource|false + * @refcount 1 */ function stream_socket_client(string $address, &$error_code = null, &$error_message = null, ?float $timeout = null, int $flags = STREAM_CLIENT_CONNECT, $context = null) {} @@ -1263,14 +1282,15 @@ function stream_socket_client(string $address, &$error_code = null, &$error_mess * @param string $error_message * @param resource|null $context * @return resource|false + * @refcount 1 */ function stream_socket_server(string $address, &$error_code = null, &$error_message = null, int $flags = STREAM_SERVER_BIND | STREAM_SERVER_LISTEN, $context = null) {} /** * @param resource $socket - * @param float $timeout * @param string $peer_name * @return resource|false + * @refcount 1 */ function stream_socket_accept($socket, ?float $timeout = null, &$peer_name = null) {} diff --git a/ext/standard/basic_functions_arginfo.h b/ext/standard/basic_functions_arginfo.h index 049adf53c77..78c00cccbb0 100644 --- a/ext/standard/basic_functions_arginfo.h +++ b/ext/standard/basic_functions_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 82d7b69362e6dc0ccd8b7cb953ec0bdcb494137d */ + * Stub hash: a7e7f1f40e8ea4f8a532bce27b4841329a5a9663 */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_set_time_limit, 0, 1, _IS_BOOL, 0) ZEND_ARG_TYPE_INFO(0, seconds, IS_LONG, 0) diff --git a/ext/zlib/zlib.stub.php b/ext/zlib/zlib.stub.php index f1a53acf604..24ee94d8ea0 100644 --- a/ext/zlib/zlib.stub.php +++ b/ext/zlib/zlib.stub.php @@ -24,7 +24,10 @@ function zlib_get_coding_type(): string|false {} function gzfile(string $filename, int $use_include_path = 0): array|false {} -/** @return resource|false */ +/** + * @return resource|false + * @refcount 1 + */ function gzopen(string $filename, string $mode, int $use_include_path = 0) {} function readgzfile(string $filename, int $use_include_path = 0): int|false {} diff --git a/ext/zlib/zlib_arginfo.h b/ext/zlib/zlib_arginfo.h index 582877b6299..31a290dd475 100644 --- a/ext/zlib/zlib_arginfo.h +++ b/ext/zlib/zlib_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 2e3212e4cbb2fdeb5fc1342b19590bc89ae5d31a */ + * Stub hash: 89215fe1b85a38feee3926e25e9b8eac2365c4cb */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_MASK_EX(arginfo_ob_gzhandler, 0, 2, MAY_BE_STRING|MAY_BE_FALSE) ZEND_ARG_TYPE_INFO(0, data, IS_STRING, 0)