From cf3247f43e0d39cbb9913a8f3c57f166baac204d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1t=C3=A9=20Kocsis?= Date: Mon, 4 Mar 2024 09:36:41 +0100 Subject: [PATCH 1/9] Document stubs --- docs/source/index.rst | 6 + docs/source/miscellaneous/stubs.rst | 635 ++++++++++++++++++++++++++++ 2 files changed, 641 insertions(+) create mode 100644 docs/source/miscellaneous/stubs.rst diff --git a/docs/source/index.rst b/docs/source/index.rst index 667f761497be4..53178820892a6 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -15,6 +15,12 @@ core/data-structures/index +.. toctree:: + :caption: Miscellaneous + :hidden: + + miscellaneous/stubs + Welcome to the php-src documentation! .. warning:: diff --git a/docs/source/miscellaneous/stubs.rst b/docs/source/miscellaneous/stubs.rst new file mode 100644 index 0000000000000..02ab433ee01d5 --- /dev/null +++ b/docs/source/miscellaneous/stubs.rst @@ -0,0 +1,635 @@ +################# + Stubs +################# + +Stub files are pieces of plain PHP code which only contain declarations without actually runnable code. A very basic stub +looks like this: + +.. code:: php + + /** @var string */ + const FOO = "foo"; + /** @var int */ + const BAR = "bar"; + + class Foo { + public function bar(): string {} + } + + function foo(string $param): string {} + +Any kind of symbols can be declared via stubs, and with the exception of disjunctive normal form (DNF) types, basically +any kind of type declaration is supported. Additional meta information can be added either via PHPDoc or attributes. +Namespaces can also be used by either adding a top-level ``namespace`` declaration or by using namespace blocks: + +.. code:: php + + namespace { + /** @var string */ + const FOO = "foo"; + /** @var string */ + const BAR = "bar"; + + class Foo { + public function bar(): string {} + } + } + + namespace Foo { + function foo(string $param): string {} + } + +The above example declares the global constants and class ``Foo`` in the top-level namespace, while ``foo()`` will be +available in the ``Foo`` namespace. + +********** +Using gen_stub.php +********** + +By convention, stub files have a ``.stub.php`` extension. They are processed by ``build/gen_stub.php``: it uses +`PHP-Parser`_ for parsing, then depending on the configuration and the supplied arguments, it can generate various artifacts. +The following sections will introduce these capabilities. + +.. _PHP-Parser: https://github.com/nikic/PHP-Parser + +********** +Generating arginfo structures +********** + +The original purpose of stubs was to make it easier to declare arginfo structures. Previously, one had to manually use +the different ``ZEND_BEGIN_ARG_* ... ZEND_END_ARG_INFO()`` macros. This was a tedious and error-prone process, so being +able to use pure PHP code based on which the C code can be generated is a huge relief. The first example above results +in the following arginfo file: + +.. code:: c + + /* This is a generated file, edit the .stub.php file instead. + * Stub hash: 3026b5948bc28345598b3dfa649e310d59f370a5 */ + + ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_foo, 0, 1, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, param, IS_STRING, 0) + ZEND_END_ARG_INFO() + + ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Foo_bar, 0, 0, IS_STRING, 0) + ZEND_END_ARG_INFO() + +Please note the hash above. It makes sure that stub files are not reprocessed unless there was any actual change, or +something requires it to be processed (e.g. regeneration was forced by using the `-f` flag). + +Another very important thing to keep in mind is that stub-based type declarations have to be in sync with the parameter +parsing code (``ZPP``). Even though only ZPP is run in case of release builds when an internal function or method is invoked, +(meaning that arginfo structures are only used for reflection purposes), however, the result of ``ZPP`` as well as the +actual type of the return value is compared with the available reflection information in debug builds, and errors are +raised in case of any incompatibility. That's why only absolutely correct types should ever be declared in stubs. For +documentation purposes, PHPDoc can be used. + +Since PHP 8.0, arginfo structures can also store default values in order to be used by +``ReflectionParameter::getDefaultValue()`` among some other use-cases. Default values may not only be literals, but +compile-time evaluable expressions, possibly containing references to constants. Let's modify function ``foo()`` +slightly in our original example by making ``$param`` an optional parameter: + +.. code:: php + + function foo(string $param = FOO . "bar"): string {} + +This will result in the following arginfo: + +.. code:: c + + ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_foo, 0, 0, IS_STRING, 0) + ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, param, IS_LONG, 0, "FOO . \"bar\"") + ZEND_END_ARG_INFO() + +By default, referencing constants works as long as the constant is available in the same stub file. If this is +not possible by any reason, then the stub declaring the necessary constant should be included: + +.. code:: php + + // constants.stub.php + + /** @var string */ + const FOO = "foo"; + /** @var string */ + const BAR = "bar"; + +.. code:: php + + // example.stub.php + + require "constants.stub.php"; + + function foo(string $param = FOO): string {} + +Sometimes, arguments have to be passed by reference, or by using the `ZEND_SEND_PREFER_REF` flag. Passing by reference +is trivial to declare by the usual syntax, while the latter can be achieved by using the ``@prefer-ref`` PHPDoc tag: + +.. code:: php + + /** + * @param string $param1 + * @prefer-ref $param2 + */ + function foo(&$param1, string $param2): string {} + +This is going to yield the following arginfo structure: + +.. code:: c + + ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_foo, 0, 2, IS_STRING, 0) + ZEND_ARG_INFO(1, param1) + ZEND_ARG_TYPE_INFO(ZEND_SEND_PREFER_REF, param2, IS_STRING, 0) + ZEND_END_ARG_INFO() + +********** +Generating function entries +********** + +Besides arginfo structures, function entries themselves can also be generated via stubs. In order to make this work, +the file-level ``@generate-function-entries`` PHPDoc tag has to be added to our stub: + +.. code:: php + + /** @generate-function-entries */ + + class Foo { + public function bar(): string {} + } + + function foo(string $param): string {} + +Now, the following C code is added to our original arginfo file: + +.. code:: c + + /* ... */ + + ZEND_FUNCTION(foo); + ZEND_METHOD(Foo, bar); + + static const zend_function_entry ext_functions[] = { + ZEND_FE(foo, arginfo_foo) + ZEND_FE_END + }; + + static const zend_function_entry class_Foo_methods[] = { + ZEND_ME(Foo, bar, arginfo_class_Foo_bar, ZEND_ACC_PUBLIC) + ZEND_FE_END + }; + +The ``ext_functions`` variable is to be passed for the ``functions`` member of `zend_module_entry`, while +``class_Foo_methods`` can be used when registering the ``Foo`` class: + +.. code:: c + + INIT_CLASS_ENTRY(ce, "Foo", class_Foo_methods); + +Function entries may make use of extra meta information passed via the following PHPDoc tags: + +- ``@deprecated``: Triggers the usual deprecation notice when the function/method is called. + +- ``@alias``: If a function/method is an alias of another function/method, then the aliased function/method name + has to be provided as value. E.g. function ``sizeof()` has the ``@alias count`` annotation. + +- ``@implementation-alias``: This is very similar to ``@alias`` with some semantic difference: these aliases + exists purely to avoid duplicating some code, but there is no other connection between the alias and the aliased + function or method. A notable example is ``Error::getCode()`` which has the ``@implementation-alias Exception::getCode`` + annotation. The difference between ``@alias`` and ``@implementation-alias`` is very nuanced and is only observable + in the manual. + +- ``@tentative-return-type``: By using this annotation, the return type declaration is reclassified as a + `tentative return type`_. + +- ``@genstubs-expose-comment-block``: By adding this annotation at the beginning of a docblock, the content of the + docblock will be exposed for `ReflectionFunctionAbstract::getDocComment()`. This feature is only available as of + PHP 8.4.0. + +.. _tentative return type: https://wiki.php.net/rfc/internal_method_return_types + +********** +Generating class entries +********** + +Until now, we only covered how to deal with functions. But as mentioned in the beginning, stubs can declare any kind of +symbols. In order to generate the code which is necessary for registering constants, classes, properties, enums, and +traits, the ``@generate-class-entries`` file-level PHPDoc tag has to be added to the stub. Using +``@generate-class-entries`` automatically implies ``@generate-function-entries```, so there is no use of adding the latter. + +Given the following stub: + +.. code:: php + + /** @generate-class-entries */ + + enum Number: string { + /** @var string */ + public const ONE = "one"; + + case One = Number::ONE; + } + + class Foo { + /** @cvalue M_PI */ + public const float PI = UNKNOWN; + + public readonly string $prop; + } + +The following arginfo file is generated: + +.. code:: c + + static const zend_function_entry class_Number_methods[] = { + ZEND_FE_END + }; + + static const zend_function_entry class_Foo_methods[] = { + ZEND_FE_END + }; + + static zend_class_entry *register_class_Number(void) + { + zend_class_entry *class_entry = zend_register_internal_enum("Number", IS_STRING, class_Number_methods); + + zval const_ONE_value; + zend_string *const_ONE_value_str = zend_string_init("one", strlen("one"), 1); + ZVAL_STR(&const_ONE_value, const_ONE_value_str); + zend_string *const_ONE_name = zend_string_init_interned("ONE", sizeof("ONE") - 1, 1); + zend_declare_class_constant_ex(class_entry, const_ONE_name, &const_ONE_value, ZEND_ACC_PUBLIC, NULL); + zend_string_release(const_ONE_name); + + zval enum_case_One_value; + zend_string *enum_case_One_value_str = zend_string_init("one", strlen("one"), 1); + ZVAL_STR(&enum_case_One_value, enum_case_One_value_str); + zend_enum_add_case_cstr(class_entry, "One", &enum_case_One_value); + + return class_entry; + } + + static zend_class_entry *register_class_Foo(void) + { + zend_class_entry ce, *class_entry; + + INIT_CLASS_ENTRY(ce, "Foo", class_Foo_methods); + class_entry = zend_register_internal_class_ex(&ce, NULL); + + zval const_PI_value; + ZVAL_DOUBLE(&const_PI_value, M_PI); + zend_string *const_PI_name = zend_string_init_interned("PI", sizeof("PI") - 1, 1); + zend_declare_typed_class_constant(class_entry, const_PI_name, &const_PI_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_DOUBLE)); + zend_string_release(const_PI_name); + + zval property_prop_default_value; + ZVAL_UNDEF(&property_prop_default_value); + zend_string *property_prop_name = zend_string_init("prop", sizeof("prop") - 1, 1); + zend_declare_typed_property(class_entry, property_prop_name, &property_prop_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_READONLY, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING)); + zend_string_release(property_prop_name); + + return class_entry; + } + +We can disregard the implementation details of the ``register_class_*()`` functions, and directly use them to register +enum ``Number`` and class ``Foo``: + +.. code:: c + + zend_class_entry *number_ce = register_class_Number(); + zend_class_entry *foo_ce = register_class_Foo(zend_standard_class_def); + +It's worth to note that the class entry of any dependency (e.g. the parent class or implemented interfaces) has to be +manually passed to the register function: in this specific case, the class entry for ``stdClass`` (``zend_standard_class_def``) +was passed. + +Just like functions and methods, classes also support some meta information passed via PHPDoc tags: + +- ``@deprecated``: triggers a deprecation notice when the class is used + +- ``@strict-properties``: adds the ``ZEND_ACC_NO_DYNAMIC_PROPERTIES`` flag for the class (as of PHP 8.0), which disallow + dynamic properties. + +- ``@not-serializable``: adds the ``ZEND_ACC_NOT_SERIALIZABLE`` flag for the class (as of PHP 8.1), which prevents + the serialization of the class. + +- ``@genstubs-expose-comment-block``: By adding this tag at the beginning of a docblock, the content of the + docblock will be exposed for `ReflectionClass::getDocComment()`. This feature is only available as of PHP 8.4.0. + +********** +Generating global constants and attributes +********** + +We have not covered so far how to register global constants and attributes for functions. Slightly surprisingly, +the ``/** @generate-class-entries */``` file-level PHPDoc tag is necessary for it to work, even though, neither +of them relate to classes. That's also why the C code which registers these symbols takes place in a function +called ``register_{{ STUB FILE NAME }}_symbols()```. Given the following ``example.stub.php``` file: + +.. code:: php + + = ...)`` conditions in the generated arginfo file: + +.. code:: c + + static const zend_function_entry class_Number_methods[] = { + ZEND_FE_END + }; + + static const zend_function_entry class_Foo_methods[] = { + ZEND_ME(Foo, foo, arginfo_class_Foo_foo, ZEND_ACC_PUBLIC) + ZEND_FE_END + }; + + #if (PHP_VERSION_ID >= 80100) + static zend_class_entry *register_class_Number(void) + { + zend_class_entry *class_entry = zend_register_internal_enum("Number", IS_UNDEF, class_Number_methods); + + zend_enum_add_case_cstr(class_entry, "One", NULL); + + return class_entry; + } + #endif + + static zend_class_entry *register_class_Foo(void) + { + zend_class_entry ce, *class_entry; + + INIT_CLASS_ENTRY(ce, "Foo", class_Foo_methods); + class_entry = zend_register_internal_class_ex(&ce, NULL); + + zval property_prop_default_value; + ZVAL_UNDEF(&property_prop_default_value); + zend_string *property_prop_name = zend_string_init("prop", sizeof("prop") - 1, 1); + #if (PHP_VERSION_ID >= 80100) + zend_declare_typed_property(class_entry, property_prop_name, &property_prop_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_READONLY, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING)); + #elif (PHP_VERSION_ID >= 80000) + zend_declare_typed_property(class_entry, property_prop_name, &property_prop_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING)); + #endif + zend_string_release(property_prop_name); + + return class_entry; + } + +The prepocessor conditions are necessary because ``enum``s and ``readonly`` properties are PHP 8.1 features and consequently, +they don't exist in PHP 8.0. Therefore, the registration of ``Number`` is completely omitted, while the ``readonly`` flag is +not added for ``Foo::$prop`` below PHP 8.1 versions. + +********** +Generating information for the optimizer +********** + +A list of functions is maintained for the optimizer in ``Zend/Optimizer/zend_func_infos.h`` containing extra information +about the return type and the cardinality of the return value. These pieces of information can enable more accurate +optimizations (i.e. better type inference). Previously, the file was maintained manually, however since PHP 8.1, +``gen_stub.php`` takes care of this task by passing the ``--generate-optimizer-info`` option. + +A function is added to ``zend_func_infos.h`` if either the ``@return`` or the ``@refcount`` PHPDoc tag supplies +more information than what is available based on the return type declaration. By default, scalar return types have a +``refcount`` of ``0``, while non-scalar values are ``N``. If a function can only return newly created non-scalar values, +its ``refcount`` can be set to ``1``. + +An example from the built-in functions: + +.. code:: php + + /** + * @return array + * @refcount 1 + */ + function get_declared_classes(): array {} + +Based on which the following func info entry is provided for the optimizer: + +.. code:: c + + F1("get_declared_classes", MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_LONG|MAY_BE_ARRAY_OF_STRING), + +Please note that the feature is only available for built-in stubs inside php-src, since currently there +is no way to provide the function list for the optimizer other than overwriting ``zend_func_infos.h`` directly. + +Additionally, functions can be evaluated in compile-time if their arguments are known in compile-time and their +behavior if free from side-effects as well as it is not affected by the global state. Until PHP 8.2, a list of such +functions was maintained manually in the optimizer. However, since PHP 8.2, the ``@compile-time-eval`` PHPDoc tag +can be applied to any functions which conform to the above restrictions in order for them to qualify as evaluable in +compile-time. The feature internally works by adding the ``ZEND_ACC_COMPILE_TIME_EVAL`` function flag. + +As of PHP 8.4, the concept of arity-based frameless functions was introduced. This is another optimization technique, +which results in faster internal function calls by eliminating unnecessary checks for the number of passed parameters +(if the number of passed arguments is known in compile-time). + +In order to take advantage of frameless functions, the ``@frameless-function`` PHPDoc tag has to be provided along +with some configuration. Since currently only arity-based optimizations are supported, the following should be provided: +``@frameless-function {"arity": NUM}``, where ``NUM`` is the number of parameters for which a frameless function +is available. Let's see the stub of ``in_array()`` as an example: + +.. code:: php + + /** + * @compile-time-eval + * @frameless-function {"arity": 2} + * @frameless-function {"arity": 3} + */ + function in_array(mixed $needle, array $haystack, bool $strict = false): bool {} + +Apart from being compile-time evaluable, it has a frameless function counterpart for both the 2 and the 3-parameter +signatures: + +.. code:: c + + /* The regular in_array() function */ + PHP_FUNCTION(in_array) + { + php_search_array(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0); + } + + /* The frameless version of the in_array() function when 2 arguments are passed */ + ZEND_FRAMELESS_FUNCTION(in_array, 2) + { + zval *value, *array; + + Z_FLF_PARAM_ZVAL(1, value); + Z_FLF_PARAM_ARRAY(2, array); + + _php_search_array(return_value, value, array, false, 0); + + flf_clean:; + } + + /* The frameless version of the in_array() function when 3 arguments are passed */ + ZEND_FRAMELESS_FUNCTION(in_array, 3) + { + zval *value, *array; + bool strict; + + Z_FLF_PARAM_ZVAL(1, value); + Z_FLF_PARAM_ARRAY(2, array); + Z_FLF_PARAM_BOOL(3, strict); + + _php_search_array(return_value, value, array, strict, 0); + + flf_clean:; + } + +********** +Generating signatures for the manual +********** + +Theoretically, the manual should reflect the exact same signatures which are represented by the stubs. This is not +exactly the case yet for built-in symbols, but ``gen_stub.php`` have multiple features to automate the process of +syncronization. + +First of all, newly added functions or methods can be documented by providing the ``--generate-methodsynopses`` option. +E.g. running ``./build/gen_stub.php --generate-methodsynopses ./ext/mbstring ../doc-en/reference/mbstring`` will create a +dedicated page for each ``ext/mbstring`` function which is not yet documented, saving them into the +``../doc-en/reference/mbstring/functions`` directory. Since the generated pages are stubs, the relevant +descriptions have to be added, while the irrelevant ones have to be removed. + +For functions or methods which are already available in the manual, the documented signatures can be updated by providing +the ``--replace-methodsynopses`` option. E.g. running ``./build/gen_stub.php --replace-methodsynopses ./ ../doc-en/`` will +update all the function or method signatures in the English documentation whose stub counterpart is found. + +Class signatures can be updated by providing the ``--replace-classsynopses`` option. E.g. running +``./build/gen_stub.php --replace-classsynopses ./ ../doc-en/`` will +update all the class signatures in the English documentation whose stub counterpart is found. + +If a symbol is not intended to be documented, the ``@undocumentable`` PHPDoc tag should be added to it. Doing so will prevent +any documentation to be created for the given symbol. In order not to add a whole stub file not to the manual, the PHPDoc +tag should be applied to the file itself. These possibilities are useful for symbols which exist only for testing +purposes (e.g. the ones declared for ``ext/zend_test``), or by some other reason documentation is not possible. + +********** +Validation +********** + +It's possible to validate whether the alias function/method signatures are correct by providing the ``--verify`` flag to +``gen_stub.php``. Normally, an alias function/method should have the exact same signature as its aliased +function/method counterpart has apart from the name. In some cases this is not achievable by some reason (i.e. +``bzwrite()`` is an alias of ``fwrite()``, but the name of the first parameter is different because the resource +types differ). In order to suppress the error when the check is false positive, the ``@no-verify`` PHPDoc tag should be +applied to the alias: + +.. code:: php + + /** + * @param resource $bz + * @implementation-alias fwrite + * @no-verify Uses different parameter name + */ + function bzwrite($bz, string $data, ?int $length = null): int|false {} + +Besides aliases, the contents of the documentation can also be validated by providing the ``--verify-manual`` option +to ``gen_stub.php`` along with the path of the manual as the last argument: e.g. +``./build/gen_stub.php --verify-manual ./ ../doc-en/`` when validation is based on all stubs in ``php-src`` and +the English documentation is available in the parent directory. + +When this feature is used, the following validations are performed: + +- Detecting missing global constants + +- Detecting missing classes + +- Detecting missing methods + +- Detecting incorrectly documented alias functions or methods + +********** +Parameter statistics +********** + +A less commonly used feature of ``gen_stub.php`` is to count how many times a parameter name occurs in the codebase: +``./build/gen_stub.php --parameter-stats``. The result is a JSON object containing the parameter names and the +number of their occurrences in descending order. From 785143075e62880acebc52f39ebdd8b556674057 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1t=C3=A9=20Kocsis?= Date: Thu, 14 Mar 2024 07:54:08 +0100 Subject: [PATCH 2/9] Apply rstfmt formatting --- docs/source/miscellaneous/stubs.rst | 408 +++++++++++++++------------- 1 file changed, 217 insertions(+), 191 deletions(-) diff --git a/docs/source/miscellaneous/stubs.rst b/docs/source/miscellaneous/stubs.rst index 02ab433ee01d5..627fd2787a40e 100644 --- a/docs/source/miscellaneous/stubs.rst +++ b/docs/source/miscellaneous/stubs.rst @@ -1,9 +1,9 @@ -################# +####### Stubs -################# +####### -Stub files are pieces of plain PHP code which only contain declarations without actually runnable code. A very basic stub -looks like this: +Stub files are pieces of plain PHP code which only contain declarations without actually runnable +code. A very basic stub looks like this: .. code:: php @@ -18,9 +18,10 @@ looks like this: function foo(string $param): string {} -Any kind of symbols can be declared via stubs, and with the exception of disjunctive normal form (DNF) types, basically -any kind of type declaration is supported. Additional meta information can be added either via PHPDoc or attributes. -Namespaces can also be used by either adding a top-level ``namespace`` declaration or by using namespace blocks: +Any kind of symbols can be declared via stubs, and with the exception of disjunctive normal form +(DNF) types, basically any kind of type declaration is supported. Additional meta information can be +added either via PHPDoc or attributes. Namespaces can also be used by either adding a top-level +``namespace`` declaration or by using namespace blocks: .. code:: php @@ -39,27 +40,28 @@ Namespaces can also be used by either adding a top-level ``namespace`` declarati function foo(string $param): string {} } -The above example declares the global constants and class ``Foo`` in the top-level namespace, while ``foo()`` will be -available in the ``Foo`` namespace. +The above example declares the global constants and class ``Foo`` in the top-level namespace, while +``foo()`` will be available in the ``Foo`` namespace. -********** -Using gen_stub.php -********** +******************** + Using gen_stub.php +******************** -By convention, stub files have a ``.stub.php`` extension. They are processed by ``build/gen_stub.php``: it uses -`PHP-Parser`_ for parsing, then depending on the configuration and the supplied arguments, it can generate various artifacts. -The following sections will introduce these capabilities. +By convention, stub files have a ``.stub.php`` extension. They are processed by +``build/gen_stub.php``: it uses PHP-Parser_ for parsing, then depending on the configuration and the +supplied arguments, it can generate various artifacts. The following sections will introduce these +capabilities. -.. _PHP-Parser: https://github.com/nikic/PHP-Parser +.. _php-parser: https://github.com/nikic/PHP-Parser -********** -Generating arginfo structures -********** +******************************* + Generating arginfo structures +******************************* -The original purpose of stubs was to make it easier to declare arginfo structures. Previously, one had to manually use -the different ``ZEND_BEGIN_ARG_* ... ZEND_END_ARG_INFO()`` macros. This was a tedious and error-prone process, so being -able to use pure PHP code based on which the C code can be generated is a huge relief. The first example above results -in the following arginfo file: +The original purpose of stubs was to make it easier to declare arginfo structures. Previously, one +had to manually use the different ``ZEND_BEGIN_ARG_* ... ZEND_END_ARG_INFO()`` macros. This was a +tedious and error-prone process, so being able to use pure PHP code based on which the C code can be +generated is a huge relief. The first example above results in the following arginfo file: .. code:: c @@ -73,20 +75,23 @@ in the following arginfo file: ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Foo_bar, 0, 0, IS_STRING, 0) ZEND_END_ARG_INFO() -Please note the hash above. It makes sure that stub files are not reprocessed unless there was any actual change, or -something requires it to be processed (e.g. regeneration was forced by using the `-f` flag). +Please note the hash above. It makes sure that stub files are not reprocessed unless there was any +actual change, or something requires it to be processed (e.g. regeneration was forced by using the +`-f` flag). -Another very important thing to keep in mind is that stub-based type declarations have to be in sync with the parameter -parsing code (``ZPP``). Even though only ZPP is run in case of release builds when an internal function or method is invoked, -(meaning that arginfo structures are only used for reflection purposes), however, the result of ``ZPP`` as well as the -actual type of the return value is compared with the available reflection information in debug builds, and errors are -raised in case of any incompatibility. That's why only absolutely correct types should ever be declared in stubs. For -documentation purposes, PHPDoc can be used. +Another very important thing to keep in mind is that stub-based type declarations have to be in sync +with the parameter parsing code (``ZPP``). Even though only ZPP is run in case of release builds +when an internal function or method is invoked, (meaning that arginfo structures are only used for +reflection purposes), however, the result of ``ZPP`` as well as the actual type of the return value +is compared with the available reflection information in debug builds, and errors are raised in case +of any incompatibility. That's why only absolutely correct types should ever be declared in stubs. +For documentation purposes, PHPDoc can be used. Since PHP 8.0, arginfo structures can also store default values in order to be used by -``ReflectionParameter::getDefaultValue()`` among some other use-cases. Default values may not only be literals, but -compile-time evaluable expressions, possibly containing references to constants. Let's modify function ``foo()`` -slightly in our original example by making ``$param`` an optional parameter: +``ReflectionParameter::getDefaultValue()`` among some other use-cases. Default values may not only +be literals, but compile-time evaluable expressions, possibly containing references to constants. +Let's modify function ``foo()`` slightly in our original example by making ``$param`` an optional +parameter: .. code:: php @@ -100,8 +105,9 @@ This will result in the following arginfo: ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, param, IS_LONG, 0, "FOO . \"bar\"") ZEND_END_ARG_INFO() -By default, referencing constants works as long as the constant is available in the same stub file. If this is -not possible by any reason, then the stub declaring the necessary constant should be included: +By default, referencing constants works as long as the constant is available in the same stub file. +If this is not possible by any reason, then the stub declaring the necessary constant should be +included: .. code:: php @@ -120,8 +126,9 @@ not possible by any reason, then the stub declaring the necessary constant shoul function foo(string $param = FOO): string {} -Sometimes, arguments have to be passed by reference, or by using the `ZEND_SEND_PREFER_REF` flag. Passing by reference -is trivial to declare by the usual syntax, while the latter can be achieved by using the ``@prefer-ref`` PHPDoc tag: +Sometimes, arguments have to be passed by reference, or by using the `ZEND_SEND_PREFER_REF` flag. +Passing by reference is trivial to declare by the usual syntax, while the latter can be achieved by +using the ``@prefer-ref`` PHPDoc tag: .. code:: php @@ -140,12 +147,13 @@ This is going to yield the following arginfo structure: ZEND_ARG_TYPE_INFO(ZEND_SEND_PREFER_REF, param2, IS_STRING, 0) ZEND_END_ARG_INFO() -********** -Generating function entries -********** +***************************** + Generating function entries +***************************** -Besides arginfo structures, function entries themselves can also be generated via stubs. In order to make this work, -the file-level ``@generate-function-entries`` PHPDoc tag has to be added to our stub: +Besides arginfo structures, function entries themselves can also be generated via stubs. In order to +make this work, the file-level ``@generate-function-entries`` PHPDoc tag has to be added to our +stub: .. code:: php @@ -176,8 +184,8 @@ Now, the following C code is added to our original arginfo file: ZEND_FE_END }; -The ``ext_functions`` variable is to be passed for the ``functions`` member of `zend_module_entry`, while -``class_Foo_methods`` can be used when registering the ``Foo`` class: +The ``ext_functions`` variable is to be passed for the ``functions`` member of `zend_module_entry`, +while ``class_Foo_methods`` can be used when registering the ``Foo`` class: .. code:: c @@ -187,32 +195,34 @@ Function entries may make use of extra meta information passed via the following - ``@deprecated``: Triggers the usual deprecation notice when the function/method is called. -- ``@alias``: If a function/method is an alias of another function/method, then the aliased function/method name - has to be provided as value. E.g. function ``sizeof()` has the ``@alias count`` annotation. +- ``@alias``: If a function/method is an alias of another function/method, then the aliased + function/method name has to be provided as value. E.g. function ``sizeof()` has the ``@alias + count`` annotation. -- ``@implementation-alias``: This is very similar to ``@alias`` with some semantic difference: these aliases - exists purely to avoid duplicating some code, but there is no other connection between the alias and the aliased - function or method. A notable example is ``Error::getCode()`` which has the ``@implementation-alias Exception::getCode`` - annotation. The difference between ``@alias`` and ``@implementation-alias`` is very nuanced and is only observable - in the manual. +- ``@implementation-alias``: This is very similar to ``@alias`` with some semantic difference: + these aliases exists purely to avoid duplicating some code, but there is no other connection + between the alias and the aliased function or method. A notable example is ``Error::getCode()`` + which has the ``@implementation-alias Exception::getCode`` annotation. The difference between + ``@alias`` and ``@implementation-alias`` is very nuanced and is only observable in the manual. -- ``@tentative-return-type``: By using this annotation, the return type declaration is reclassified as a - `tentative return type`_. +- ``@tentative-return-type``: By using this annotation, the return type declaration is reclassified + as a `tentative return type`_. -- ``@genstubs-expose-comment-block``: By adding this annotation at the beginning of a docblock, the content of the - docblock will be exposed for `ReflectionFunctionAbstract::getDocComment()`. This feature is only available as of - PHP 8.4.0. +- ``@genstubs-expose-comment-block``: By adding this annotation at the beginning of a docblock, the + content of the docblock will be exposed for `ReflectionFunctionAbstract::getDocComment()`. This + feature is only available as of PHP 8.4.0. .. _tentative return type: https://wiki.php.net/rfc/internal_method_return_types -********** -Generating class entries -********** +************************** + Generating class entries +************************** -Until now, we only covered how to deal with functions. But as mentioned in the beginning, stubs can declare any kind of -symbols. In order to generate the code which is necessary for registering constants, classes, properties, enums, and -traits, the ``@generate-class-entries`` file-level PHPDoc tag has to be added to the stub. Using -``@generate-class-entries`` automatically implies ``@generate-function-entries```, so there is no use of adding the latter. +Until now, we only covered how to deal with functions. But as mentioned in the beginning, stubs can +declare any kind of symbols. In order to generate the code which is necessary for registering +constants, classes, properties, enums, and traits, the ``@generate-class-entries`` file-level PHPDoc +tag has to be added to the stub. Using ``@generate-class-entries`` automatically implies +``@generate-function-entries```, so there is no use of adding the latter. Given the following stub: @@ -287,39 +297,41 @@ The following arginfo file is generated: return class_entry; } -We can disregard the implementation details of the ``register_class_*()`` functions, and directly use them to register -enum ``Number`` and class ``Foo``: +We can disregard the implementation details of the ``register_class_*()`` functions, and directly +use them to register enum ``Number`` and class ``Foo``: .. code:: c zend_class_entry *number_ce = register_class_Number(); zend_class_entry *foo_ce = register_class_Foo(zend_standard_class_def); -It's worth to note that the class entry of any dependency (e.g. the parent class or implemented interfaces) has to be -manually passed to the register function: in this specific case, the class entry for ``stdClass`` (``zend_standard_class_def``) -was passed. +It's worth to note that the class entry of any dependency (e.g. the parent class or implemented +interfaces) has to be manually passed to the register function: in this specific case, the class +entry for ``stdClass`` (``zend_standard_class_def``) was passed. Just like functions and methods, classes also support some meta information passed via PHPDoc tags: - ``@deprecated``: triggers a deprecation notice when the class is used -- ``@strict-properties``: adds the ``ZEND_ACC_NO_DYNAMIC_PROPERTIES`` flag for the class (as of PHP 8.0), which disallow - dynamic properties. +- ``@strict-properties``: adds the ``ZEND_ACC_NO_DYNAMIC_PROPERTIES`` flag for the class (as of PHP + 8.0), which disallow dynamic properties. -- ``@not-serializable``: adds the ``ZEND_ACC_NOT_SERIALIZABLE`` flag for the class (as of PHP 8.1), which prevents - the serialization of the class. +- ``@not-serializable``: adds the ``ZEND_ACC_NOT_SERIALIZABLE`` flag for the class (as of PHP 8.1), + which prevents the serialization of the class. -- ``@genstubs-expose-comment-block``: By adding this tag at the beginning of a docblock, the content of the - docblock will be exposed for `ReflectionClass::getDocComment()`. This feature is only available as of PHP 8.4.0. +- ``@genstubs-expose-comment-block``: By adding this tag at the beginning of a docblock, the + content of the docblock will be exposed for `ReflectionClass::getDocComment()`. This feature is + only available as of PHP 8.4.0. -********** -Generating global constants and attributes -********** +******************************************** + Generating global constants and attributes +******************************************** -We have not covered so far how to register global constants and attributes for functions. Slightly surprisingly, -the ``/** @generate-class-entries */``` file-level PHPDoc tag is necessary for it to work, even though, neither -of them relate to classes. That's also why the C code which registers these symbols takes place in a function -called ``register_{{ STUB FILE NAME }}_symbols()```. Given the following ``example.stub.php``` file: +We have not covered so far how to register global constants and attributes for functions. Slightly +surprisingly, the ``/** @generate-class-entries */``` file-level PHPDoc tag is necessary for it to +work, even though, neither of them relate to classes. That's also why the C code which registers +these symbols takes place in a function called ``register_{{ STUB FILE NAME }}_symbols()```. Given +the following ``example.stub.php``` file: .. code:: php @@ -338,7 +350,8 @@ called ``register_{{ STUB FILE NAME }}_symbols()```. Given the following ``examp function foo(#[\SensitiveParameter] string $param): string {} -The following C function will be generated in order to register the two global constants and the attribute: +The following C function will be generated in order to register the two global constants and the +attribute: .. code:: c @@ -350,8 +363,9 @@ The following C function will be generated in order to register the two global c zend_add_parameter_attribute(zend_hash_str_find_ptr(CG(function_table), "foo", sizeof("foo") - 1), 0, ZSTR_KNOWN(ZEND_STR_SENSITIVEPARAMETER), 0); } -Similarly to class registration functions, ``register_example_symbols()`` also has to be called during the module -initialization process (``MINIT``), and the ``module_number`` variable has to be passed to it: +Similarly to class registration functions, ``register_example_symbols()`` also has to be called +during the module initialization process (``MINIT``), and the ``module_number`` variable has to be +passed to it: .. code:: c @@ -362,46 +376,52 @@ initialization process (``MINIT``), and the ``module_number`` variable has to be return SUCCESS; } -Constant registration needs more elaboration: no matter if the constant type can be inferred from the value, it always -has to be provided via the ``@var`` PHPDoc tag. For typed class constants, ``@var`` can be omitted, and the type -declaration will serve for this purpose. +Constant registration needs more elaboration: no matter if the constant type can be inferred from +the value, it always has to be provided via the ``@var`` PHPDoc tag. For typed class constants, +``@var`` can be omitted, and the type declaration will serve for this purpose. -In lots of cases, constants have a value which comes from a 3rd party library, or it may also be possible that the value -is a C expression (e.g. think about a combination of bit flags). Sometimes the exact value is not even known, because it -depends on the system. In these cases, it's better not to duplicate the exact value in the stub, but the C value should -be referenced instead. That's when the ``UNKNOWN`` constant value along with the ``@cvalue`` PHPDoc tag is useful: this -combination can be used to refer to a C value (see constant ``BAR`` in the last example). +In lots of cases, constants have a value which comes from a 3rd party library, or it may also be +possible that the value is a C expression (e.g. think about a combination of bit flags). Sometimes +the exact value is not even known, because it depends on the system. In these cases, it's better not +to duplicate the exact value in the stub, but the C value should be referenced instead. That's when +the ``UNKNOWN`` constant value along with the ``@cvalue`` PHPDoc tag is useful: this combination can +be used to refer to a C value (see constant ``BAR`` in the last example). Constants can take the following extra meta information passed via PHPDoc tags: - ``@deprecated``: Triggers a deprecation notice when the constant is used. -- ``@genstubs-expose-comment-block``: By adding this tag at the beginning of a docblock, the content of the - docblock will be exposed for `ReflectionClass::getDocComment()`. This feature is only available as of PHP 8.4.0. - -********** -Maintaining backward compatibility -********** - -While php-src itself processes the built-in stubs with only the latest version of ``gen_stub.php`` which is available in -a specific PHP version, the same is not true for 3rd party extensions: their stubs need to keep backward compatibility -with older PHP versions even when using the latest version of ``gen_stub.php``. Achieving this is not straightforward, -since stubs may get new features which are unavailable in earlier PHP versions, or ABI compatibility breaks may happen -between minor releases. Not to mention the fact that PHP 7.x versions are substantially different from nowadays' PHP -versions. - -That's why it is useful to be able to declare the backward compatibility expectations of a stub. This is possible via -using the ``@generate-legacy-arginfo`` file-level PHPDoc tag. If no value is passed to the annotation, the -generated arginfo code will be compatible with PHP 7.0. In order to achieve this, an additional arginfo file is generated -with a ``_legacy_arginfo.h`` suffix besides the regular one. Symbols declared by a legacy arginfo file will miss any -type information and any PHP 8.x features. An extension author can then include the proper arginfo file depending on which +- ``@genstubs-expose-comment-block``: By adding this tag at the beginning of a docblock, the + content of the docblock will be exposed for `ReflectionClass::getDocComment()`. This feature is + only available as of PHP 8.4.0. + +************************************ + Maintaining backward compatibility +************************************ + +While php-src itself processes the built-in stubs with only the latest version of ``gen_stub.php`` +which is available in a specific PHP version, the same is not true for 3rd party extensions: their +stubs need to keep backward compatibility with older PHP versions even when using the latest version +of ``gen_stub.php``. Achieving this is not straightforward, since stubs may get new features which +are unavailable in earlier PHP versions, or ABI compatibility breaks may happen between minor +releases. Not to mention the fact that PHP 7.x versions are substantially different from nowadays' +PHP versions. + +That's why it is useful to be able to declare the backward compatibility expectations of a stub. +This is possible via using the ``@generate-legacy-arginfo`` file-level PHPDoc tag. If no value is +passed to the annotation, the generated arginfo code will be compatible with PHP 7.0. In order to +achieve this, an additional arginfo file is generated with a ``_legacy_arginfo.h`` suffix besides +the regular one. Symbols declared by a legacy arginfo file will miss any type information and any +PHP 8.x features. An extension author can then include the proper arginfo file depending on which PHP version the extension is built for. -When ``@generate-legacy-arginfo`` is passed a PHP version ID (``80000`` for PHP 8.0, ``80100`` for PHP PHP 8.1, -``80200`` for PHP 8.2, ``80300`` for PHP 8.3, and ``80400`` for PHP 8.4), then only one arginfo file is going to be generated, -and ``#if`` prepocessor directives will ensure compatibility with all the required PHP versions. +When ``@generate-legacy-arginfo`` is passed a PHP version ID (``80000`` for PHP 8.0, ``80100`` for +PHP PHP 8.1, ``80200`` for PHP 8.2, ``80300`` for PHP 8.3, and ``80400`` for PHP 8.4), then only one +arginfo file is going to be generated, and ``#if`` prepocessor directives will ensure compatibility +with all the required PHP versions. -Let's add the PHP 8.0 compatibility requirement to the slightly modified version of our previous example: +Let's add the PHP 8.0 compatibility requirement to the slightly modified version of our previous +example: .. code:: php @@ -464,23 +484,25 @@ Then notice the ``#if (PHP_VERSION_ID >= ...)`` conditions in the generated argi return class_entry; } -The prepocessor conditions are necessary because ``enum``s and ``readonly`` properties are PHP 8.1 features and consequently, -they don't exist in PHP 8.0. Therefore, the registration of ``Number`` is completely omitted, while the ``readonly`` flag is -not added for ``Foo::$prop`` below PHP 8.1 versions. +The prepocessor conditions are necessary because ``enum``s and ``readonly`` properties are PHP 8.1 +features and consequently, they don't exist in PHP 8.0. Therefore, the registration of ``Number`` is +completely omitted, while the ``readonly`` flag is not added for ``Foo::$prop`` below PHP 8.1 +versions. -********** -Generating information for the optimizer -********** +****************************************** + Generating information for the optimizer +****************************************** -A list of functions is maintained for the optimizer in ``Zend/Optimizer/zend_func_infos.h`` containing extra information -about the return type and the cardinality of the return value. These pieces of information can enable more accurate -optimizations (i.e. better type inference). Previously, the file was maintained manually, however since PHP 8.1, -``gen_stub.php`` takes care of this task by passing the ``--generate-optimizer-info`` option. +A list of functions is maintained for the optimizer in ``Zend/Optimizer/zend_func_infos.h`` +containing extra information about the return type and the cardinality of the return value. These +pieces of information can enable more accurate optimizations (i.e. better type inference). +Previously, the file was maintained manually, however since PHP 8.1, ``gen_stub.php`` takes care of +this task by passing the ``--generate-optimizer-info`` option. -A function is added to ``zend_func_infos.h`` if either the ``@return`` or the ``@refcount`` PHPDoc tag supplies -more information than what is available based on the return type declaration. By default, scalar return types have a -``refcount`` of ``0``, while non-scalar values are ``N``. If a function can only return newly created non-scalar values, -its ``refcount`` can be set to ``1``. +A function is added to ``zend_func_infos.h`` if either the ``@return`` or the ``@refcount`` PHPDoc +tag supplies more information than what is available based on the return type declaration. By +default, scalar return types have a ``refcount`` of ``0``, while non-scalar values are ``N``. If a +function can only return newly created non-scalar values, its ``refcount`` can be set to ``1``. An example from the built-in functions: @@ -498,23 +520,27 @@ Based on which the following func info entry is provided for the optimizer: F1("get_declared_classes", MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_LONG|MAY_BE_ARRAY_OF_STRING), -Please note that the feature is only available for built-in stubs inside php-src, since currently there -is no way to provide the function list for the optimizer other than overwriting ``zend_func_infos.h`` directly. +Please note that the feature is only available for built-in stubs inside php-src, since currently +there is no way to provide the function list for the optimizer other than overwriting +``zend_func_infos.h`` directly. -Additionally, functions can be evaluated in compile-time if their arguments are known in compile-time and their -behavior if free from side-effects as well as it is not affected by the global state. Until PHP 8.2, a list of such -functions was maintained manually in the optimizer. However, since PHP 8.2, the ``@compile-time-eval`` PHPDoc tag -can be applied to any functions which conform to the above restrictions in order for them to qualify as evaluable in -compile-time. The feature internally works by adding the ``ZEND_ACC_COMPILE_TIME_EVAL`` function flag. +Additionally, functions can be evaluated in compile-time if their arguments are known in +compile-time and their behavior if free from side-effects as well as it is not affected by the +global state. Until PHP 8.2, a list of such functions was maintained manually in the optimizer. +However, since PHP 8.2, the ``@compile-time-eval`` PHPDoc tag can be applied to any functions which +conform to the above restrictions in order for them to qualify as evaluable in compile-time. The +feature internally works by adding the ``ZEND_ACC_COMPILE_TIME_EVAL`` function flag. -As of PHP 8.4, the concept of arity-based frameless functions was introduced. This is another optimization technique, -which results in faster internal function calls by eliminating unnecessary checks for the number of passed parameters -(if the number of passed arguments is known in compile-time). +As of PHP 8.4, the concept of arity-based frameless functions was introduced. This is another +optimization technique, which results in faster internal function calls by eliminating unnecessary +checks for the number of passed parameters (if the number of passed arguments is known in +compile-time). -In order to take advantage of frameless functions, the ``@frameless-function`` PHPDoc tag has to be provided along -with some configuration. Since currently only arity-based optimizations are supported, the following should be provided: -``@frameless-function {"arity": NUM}``, where ``NUM`` is the number of parameters for which a frameless function -is available. Let's see the stub of ``in_array()`` as an example: +In order to take advantage of frameless functions, the ``@frameless-function`` PHPDoc tag has to be +provided along with some configuration. Since currently only arity-based optimizations are +supported, the following should be provided: ``@frameless-function {"arity": NUM}``, where ``NUM`` +is the number of parameters for which a frameless function is available. Let's see the stub of +``in_array()`` as an example: .. code:: php @@ -525,8 +551,8 @@ is available. Let's see the stub of ``in_array()`` as an example: */ function in_array(mixed $needle, array $haystack, bool $strict = false): bool {} -Apart from being compile-time evaluable, it has a frameless function counterpart for both the 2 and the 3-parameter -signatures: +Apart from being compile-time evaluable, it has a frameless function counterpart for both the 2 and +the 3-parameter signatures: .. code:: c @@ -564,43 +590,46 @@ signatures: flf_clean:; } -********** -Generating signatures for the manual -********** +************************************** + Generating signatures for the manual +************************************** -Theoretically, the manual should reflect the exact same signatures which are represented by the stubs. This is not -exactly the case yet for built-in symbols, but ``gen_stub.php`` have multiple features to automate the process of -syncronization. +Theoretically, the manual should reflect the exact same signatures which are represented by the +stubs. This is not exactly the case yet for built-in symbols, but ``gen_stub.php`` have multiple +features to automate the process of syncronization. -First of all, newly added functions or methods can be documented by providing the ``--generate-methodsynopses`` option. -E.g. running ``./build/gen_stub.php --generate-methodsynopses ./ext/mbstring ../doc-en/reference/mbstring`` will create a -dedicated page for each ``ext/mbstring`` function which is not yet documented, saving them into the -``../doc-en/reference/mbstring/functions`` directory. Since the generated pages are stubs, the relevant -descriptions have to be added, while the irrelevant ones have to be removed. +First of all, newly added functions or methods can be documented by providing the +``--generate-methodsynopses`` option. E.g. running ``./build/gen_stub.php --generate-methodsynopses +./ext/mbstring ../doc-en/reference/mbstring`` will create a dedicated page for each ``ext/mbstring`` +function which is not yet documented, saving them into the +``../doc-en/reference/mbstring/functions`` directory. Since the generated pages are stubs, the +relevant descriptions have to be added, while the irrelevant ones have to be removed. -For functions or methods which are already available in the manual, the documented signatures can be updated by providing -the ``--replace-methodsynopses`` option. E.g. running ``./build/gen_stub.php --replace-methodsynopses ./ ../doc-en/`` will -update all the function or method signatures in the English documentation whose stub counterpart is found. +For functions or methods which are already available in the manual, the documented signatures can be +updated by providing the ``--replace-methodsynopses`` option. E.g. running ``./build/gen_stub.php +--replace-methodsynopses ./ ../doc-en/`` will update all the function or method signatures in the +English documentation whose stub counterpart is found. Class signatures can be updated by providing the ``--replace-classsynopses`` option. E.g. running -``./build/gen_stub.php --replace-classsynopses ./ ../doc-en/`` will -update all the class signatures in the English documentation whose stub counterpart is found. - -If a symbol is not intended to be documented, the ``@undocumentable`` PHPDoc tag should be added to it. Doing so will prevent -any documentation to be created for the given symbol. In order not to add a whole stub file not to the manual, the PHPDoc -tag should be applied to the file itself. These possibilities are useful for symbols which exist only for testing -purposes (e.g. the ones declared for ``ext/zend_test``), or by some other reason documentation is not possible. - -********** -Validation -********** - -It's possible to validate whether the alias function/method signatures are correct by providing the ``--verify`` flag to -``gen_stub.php``. Normally, an alias function/method should have the exact same signature as its aliased -function/method counterpart has apart from the name. In some cases this is not achievable by some reason (i.e. -``bzwrite()`` is an alias of ``fwrite()``, but the name of the first parameter is different because the resource -types differ). In order to suppress the error when the check is false positive, the ``@no-verify`` PHPDoc tag should be -applied to the alias: +``./build/gen_stub.php --replace-classsynopses ./ ../doc-en/`` will update all the class signatures +in the English documentation whose stub counterpart is found. + +If a symbol is not intended to be documented, the ``@undocumentable`` PHPDoc tag should be added to +it. Doing so will prevent any documentation to be created for the given symbol. In order not to add +a whole stub file not to the manual, the PHPDoc tag should be applied to the file itself. These +possibilities are useful for symbols which exist only for testing purposes (e.g. the ones declared +for ``ext/zend_test``), or by some other reason documentation is not possible. + +************ + Validation +************ + +It's possible to validate whether the alias function/method signatures are correct by providing the +``--verify`` flag to ``gen_stub.php``. Normally, an alias function/method should have the exact same +signature as its aliased function/method counterpart has apart from the name. In some cases this is +not achievable by some reason (i.e. ``bzwrite()`` is an alias of ``fwrite()``, but the name of the +first parameter is different because the resource types differ). In order to suppress the error when +the check is false positive, the ``@no-verify`` PHPDoc tag should be applied to the alias: .. code:: php @@ -611,25 +640,22 @@ applied to the alias: */ function bzwrite($bz, string $data, ?int $length = null): int|false {} -Besides aliases, the contents of the documentation can also be validated by providing the ``--verify-manual`` option -to ``gen_stub.php`` along with the path of the manual as the last argument: e.g. -``./build/gen_stub.php --verify-manual ./ ../doc-en/`` when validation is based on all stubs in ``php-src`` and -the English documentation is available in the parent directory. +Besides aliases, the contents of the documentation can also be validated by providing the +``--verify-manual`` option to ``gen_stub.php`` along with the path of the manual as the last +argument: e.g. ``./build/gen_stub.php --verify-manual ./ ../doc-en/`` when validation is based on +all stubs in ``php-src`` and the English documentation is available in the parent directory. When this feature is used, the following validations are performed: - Detecting missing global constants - - Detecting missing classes - - Detecting missing methods - - Detecting incorrectly documented alias functions or methods -********** -Parameter statistics -********** +********************** + Parameter statistics +********************** -A less commonly used feature of ``gen_stub.php`` is to count how many times a parameter name occurs in the codebase: -``./build/gen_stub.php --parameter-stats``. The result is a JSON object containing the parameter names and the -number of their occurrences in descending order. +A less commonly used feature of ``gen_stub.php`` is to count how many times a parameter name occurs +in the codebase: ``./build/gen_stub.php --parameter-stats``. The result is a JSON object containing +the parameter names and the number of their occurrences in descending order. From cbb3412c85c2a2f67e8e23ecb35213f276f0722c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1t=C3=A9=20Kocsis?= Date: Thu, 14 Mar 2024 08:14:05 +0100 Subject: [PATCH 3/9] Remove some irrelevant code example sections + fix some wording --- docs/source/miscellaneous/stubs.rst | 83 ++++++----------------------- 1 file changed, 16 insertions(+), 67 deletions(-) diff --git a/docs/source/miscellaneous/stubs.rst b/docs/source/miscellaneous/stubs.rst index 627fd2787a40e..43a7e2413ff74 100644 --- a/docs/source/miscellaneous/stubs.rst +++ b/docs/source/miscellaneous/stubs.rst @@ -138,15 +138,6 @@ using the ``@prefer-ref`` PHPDoc tag: */ function foo(&$param1, string $param2): string {} -This is going to yield the following arginfo structure: - -.. code:: c - - ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_foo, 0, 2, IS_STRING, 0) - ZEND_ARG_INFO(1, param1) - ZEND_ARG_TYPE_INFO(ZEND_SEND_PREFER_REF, param2, IS_STRING, 0) - ZEND_END_ARG_INFO() - ***************************** Generating function entries ***************************** @@ -260,17 +251,7 @@ The following arginfo file is generated: { zend_class_entry *class_entry = zend_register_internal_enum("Number", IS_STRING, class_Number_methods); - zval const_ONE_value; - zend_string *const_ONE_value_str = zend_string_init("one", strlen("one"), 1); - ZVAL_STR(&const_ONE_value, const_ONE_value_str); - zend_string *const_ONE_name = zend_string_init_interned("ONE", sizeof("ONE") - 1, 1); - zend_declare_class_constant_ex(class_entry, const_ONE_name, &const_ONE_value, ZEND_ACC_PUBLIC, NULL); - zend_string_release(const_ONE_name); - - zval enum_case_One_value; - zend_string *enum_case_One_value_str = zend_string_init("one", strlen("one"), 1); - ZVAL_STR(&enum_case_One_value, enum_case_One_value_str); - zend_enum_add_case_cstr(class_entry, "One", &enum_case_One_value); + /* ... */ return class_entry; } @@ -279,20 +260,7 @@ The following arginfo file is generated: { zend_class_entry ce, *class_entry; - INIT_CLASS_ENTRY(ce, "Foo", class_Foo_methods); - class_entry = zend_register_internal_class_ex(&ce, NULL); - - zval const_PI_value; - ZVAL_DOUBLE(&const_PI_value, M_PI); - zend_string *const_PI_name = zend_string_init_interned("PI", sizeof("PI") - 1, 1); - zend_declare_typed_class_constant(class_entry, const_PI_name, &const_PI_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_DOUBLE)); - zend_string_release(const_PI_name); - - zval property_prop_default_value; - ZVAL_UNDEF(&property_prop_default_value); - zend_string *property_prop_name = zend_string_init("prop", sizeof("prop") - 1, 1); - zend_declare_typed_property(class_entry, property_prop_name, &property_prop_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_READONLY, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING)); - zend_string_release(property_prop_name); + /* ... */ return class_entry; } @@ -357,10 +325,7 @@ attribute: static void register_example_symbols(int module_number) { - REGISTER_STRING_CONSTANT("FOO", "foo", CONST_PERSISTENT); - REGISTER_DOUBLE_CONSTANT("BAR", M_PI, CONST_PERSISTENT); - - zend_add_parameter_attribute(zend_hash_str_find_ptr(CG(function_table), "foo", sizeof("foo") - 1), 0, ZSTR_KNOWN(ZEND_STR_SENSITIVEPARAMETER), 0); + /* ... */ } Similarly to class registration functions, ``register_example_symbols()`` also has to be called @@ -444,23 +409,12 @@ Then notice the ``#if (PHP_VERSION_ID >= ...)`` conditions in the generated argi .. code:: c - static const zend_function_entry class_Number_methods[] = { - ZEND_FE_END - }; - - static const zend_function_entry class_Foo_methods[] = { - ZEND_ME(Foo, foo, arginfo_class_Foo_foo, ZEND_ACC_PUBLIC) - ZEND_FE_END - }; + /* ... */ #if (PHP_VERSION_ID >= 80100) static zend_class_entry *register_class_Number(void) { - zend_class_entry *class_entry = zend_register_internal_enum("Number", IS_UNDEF, class_Number_methods); - - zend_enum_add_case_cstr(class_entry, "One", NULL); - - return class_entry; + /* ... */ } #endif @@ -484,7 +438,7 @@ Then notice the ``#if (PHP_VERSION_ID >= ...)`` conditions in the generated argi return class_entry; } -The prepocessor conditions are necessary because ``enum``s and ``readonly`` properties are PHP 8.1 +The preprocessor conditions are necessary because ``enum``s and ``readonly`` properties are PHP 8.1 features and consequently, they don't exist in PHP 8.0. Therefore, the registration of ``Number`` is completely omitted, while the ``readonly`` flag is not added for ``Foo::$prop`` below PHP 8.1 versions. @@ -496,8 +450,8 @@ versions. A list of functions is maintained for the optimizer in ``Zend/Optimizer/zend_func_infos.h`` containing extra information about the return type and the cardinality of the return value. These pieces of information can enable more accurate optimizations (i.e. better type inference). -Previously, the file was maintained manually, however since PHP 8.1, ``gen_stub.php`` takes care of -this task by passing the ``--generate-optimizer-info`` option. +Previously, the file was maintained manually, however since PHP 8.1, ``gen_stub.php`` can take care +of this task when the ``--generate-optimizer-info`` option is passed to it. A function is added to ``zend_func_infos.h`` if either the ``@return`` or the ``@refcount`` PHPDoc tag supplies more information than what is available based on the return type declaration. By @@ -514,12 +468,6 @@ An example from the built-in functions: */ function get_declared_classes(): array {} -Based on which the following func info entry is provided for the optimizer: - -.. code:: c - - F1("get_declared_classes", MAY_BE_ARRAY|MAY_BE_ARRAY_KEY_LONG|MAY_BE_ARRAY_OF_STRING), - Please note that the feature is only available for built-in stubs inside php-src, since currently there is no way to provide the function list for the optimizer other than overwriting ``zend_func_infos.h`` directly. @@ -596,27 +544,28 @@ the 3-parameter signatures: Theoretically, the manual should reflect the exact same signatures which are represented by the stubs. This is not exactly the case yet for built-in symbols, but ``gen_stub.php`` have multiple -features to automate the process of syncronization. +features to automate the process of synchronization. First of all, newly added functions or methods can be documented by providing the ``--generate-methodsynopses`` option. E.g. running ``./build/gen_stub.php --generate-methodsynopses ./ext/mbstring ../doc-en/reference/mbstring`` will create a dedicated page for each ``ext/mbstring`` function which is not yet documented, saving them into the -``../doc-en/reference/mbstring/functions`` directory. Since the generated pages are stubs, the -relevant descriptions have to be added, while the irrelevant ones have to be removed. +``../doc-en/reference/mbstring/functions`` directory. Since these are stub pages, many of the +sections are empty by default, so the relevant descriptions have to be added, while the irrelevant +ones have to be removed. For functions or methods which are already available in the manual, the documented signatures can be updated by providing the ``--replace-methodsynopses`` option. E.g. running ``./build/gen_stub.php --replace-methodsynopses ./ ../doc-en/`` will update all the function or method signatures in the English documentation whose stub counterpart is found. -Class signatures can be updated by providing the ``--replace-classsynopses`` option. E.g. running -``./build/gen_stub.php --replace-classsynopses ./ ../doc-en/`` will update all the class signatures -in the English documentation whose stub counterpart is found. +Class signatures can be updated in the manual by providing the ``--replace-classsynopses`` option. +E.g. running ``./build/gen_stub.php --replace-classsynopses ./ ../doc-en/`` will update all the +class signatures in the English documentation whose stub counterpart is found. If a symbol is not intended to be documented, the ``@undocumentable`` PHPDoc tag should be added to it. Doing so will prevent any documentation to be created for the given symbol. In order not to add -a whole stub file not to the manual, the PHPDoc tag should be applied to the file itself. These +a whole stub file to the manual, the PHPDoc tag should be applied to the file itself. These possibilities are useful for symbols which exist only for testing purposes (e.g. the ones declared for ``ext/zend_test``), or by some other reason documentation is not possible. From 1addf642ae26f0f1b0edf47b0fd94a97b7700718 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1t=C3=A9=20Kocsis?= Date: Sun, 17 Mar 2024 23:18:15 +0100 Subject: [PATCH 4/9] Fix some review comments --- docs/source/miscellaneous/stubs.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/source/miscellaneous/stubs.rst b/docs/source/miscellaneous/stubs.rst index 43a7e2413ff74..6d9ed10c76bd1 100644 --- a/docs/source/miscellaneous/stubs.rst +++ b/docs/source/miscellaneous/stubs.rst @@ -472,16 +472,16 @@ Please note that the feature is only available for built-in stubs inside php-src there is no way to provide the function list for the optimizer other than overwriting ``zend_func_infos.h`` directly. -Additionally, functions can be evaluated in compile-time if their arguments are known in +Additionally, functions can be evaluated at compile-time if their arguments are known in compile-time and their behavior if free from side-effects as well as it is not affected by the global state. Until PHP 8.2, a list of such functions was maintained manually in the optimizer. However, since PHP 8.2, the ``@compile-time-eval`` PHPDoc tag can be applied to any functions which -conform to the above restrictions in order for them to qualify as evaluable in compile-time. The +conform to the above restrictions in order for them to qualify as evaluable at compile-time. The feature internally works by adding the ``ZEND_ACC_COMPILE_TIME_EVAL`` function flag. As of PHP 8.4, the concept of arity-based frameless functions was introduced. This is another optimization technique, which results in faster internal function calls by eliminating unnecessary -checks for the number of passed parameters (if the number of passed arguments is known in +checks for the number of passed parameters (if the number of passed arguments is known at compile-time). In order to take advantage of frameless functions, the ``@frameless-function`` PHPDoc tag has to be From d888259e86c789544e6fe0850cde4c565b0235a6 Mon Sep 17 00:00:00 2001 From: Derick Rethans Date: Tue, 21 May 2024 16:01:48 +0100 Subject: [PATCH 5/9] Edit for clarity, add some examples, and use foo/bar less --- docs/source/miscellaneous/stubs.rst | 672 ++++++++++++++++++---------- 1 file changed, 424 insertions(+), 248 deletions(-) diff --git a/docs/source/miscellaneous/stubs.rst b/docs/source/miscellaneous/stubs.rst index 6d9ed10c76bd1..d0ddaef72f99b 100644 --- a/docs/source/miscellaneous/stubs.rst +++ b/docs/source/miscellaneous/stubs.rst @@ -2,248 +2,301 @@ Stubs ####### -Stub files are pieces of plain PHP code which only contain declarations without actually runnable -code. A very basic stub looks like this: +Stub files are pieces of PHP code which only contain declarations. They do not include runnable +code, but instead contain empty function and method bodies. A very basic stub looks like this: .. code:: php + ce_flags |= ZEND_ACC_DEPRECATED|ZEND_ACC_NO_DYNAMIC_PROPERTIES|ZEND_ACC_NOT_SERIALIZABLE; + class_entry->doc_comment = zend_string_init_interned("/**\n * This is a comment\n * @see https://www.php.net */", 55, 1); + + ... + + return class_entry; + } ******************************************** - Generating global constants and attributes + Generating Global Constants and Attributes ******************************************** -We have not covered so far how to register global constants and attributes for functions. Slightly -surprisingly, the ``/** @generate-class-entries */``` file-level PHPDoc tag is necessary for it to -work, even though, neither of them relate to classes. That's also why the C code which registers -these symbols takes place in a function called ``register_{{ STUB FILE NAME }}_symbols()```. Given -the following ``example.stub.php``` file: +Although global constants and function attributes do not relate to classes, they require the ``/** +@generate-class-entries */`` file-level PHPDoc block. + +If a global constant or function attribute are present in the stub file, the generated C-code will +include a ``register_{$stub_file_name}_symbols()`` file. + +Given the following file: .. code:: php + // example.stub.php = 80000) + # include "example_arginfo.h" + #else + # include "example_legacy_arginfo.h" + #endif + +When ``@generate-legacy-arginfo`` is passed the minimum PHP version ID that needs to be supported, +then only one arginfo file is going to be generated, and ``#if`` prepocessor directives will ensure +compatibility with all the required PHP 8 versions. + +PHP Version IDs are as follows: ``80000`` for PHP 8.0, ``80100`` for PHP PHP 8.1, ``80200`` for PHP +8.2, ``80300`` for PHP 8.3, and ``80400`` for PHP 8.4, + +In this example we add a PHP 8.0 compatibility requirement to a slightly modified version of a +previous example: .. code:: php + = ...)`` conditions in the generated arginfo file: .. code:: c - /* ... */ + ... #if (PHP_VERSION_ID >= 80100) static zend_class_entry *register_class_Number(void) { - /* ... */ + zend_class_entry *class_entry = zend_register_internal_enum("Number", IS_STRING, class_Number_methods); + + zend_enum_add_case_cstr(class_entry, "One", NULL); + + return class_entry; } #endif - static zend_class_entry *register_class_Foo(void) + static zend_class_entry *register_class_Elephant(void) { zend_class_entry ce, *class_entry; - INIT_CLASS_ENTRY(ce, "Foo", class_Foo_methods); + INIT_CLASS_ENTRY(ce, "Elephant", class_Elephant_methods); class_entry = zend_register_internal_class_ex(&ce, NULL); + #if (PHP_VERSION_ID >= 80100) + class_entry->ce_flags |= ZEND_ACC_NO_DYNAMIC_PROPERTIES|ZEND_ACC_NOT_SERIALIZABLE; + #elif (PHP_VERSION_ID >= 80000) + class_entry->ce_flags |= ZEND_ACC_NO_DYNAMIC_PROPERTIES; + #endif - zval property_prop_default_value; - ZVAL_UNDEF(&property_prop_default_value); - zend_string *property_prop_name = zend_string_init("prop", sizeof("prop") - 1, 1); + zval const_PI_value; + ZVAL_DOUBLE(&const_PI_value, M_PI); + zend_string *const_PI_name = zend_string_init_interned("PI", sizeof("PI") - 1, 1); + #if (PHP_VERSION_ID >= 80300) + zend_declare_typed_class_constant(class_entry, const_PI_name, &const_PI_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_DOUBLE)); + #else + zend_declare_class_constant_ex(class_entry, const_PI_name, &const_PI_value, ZEND_ACC_PUBLIC, NULL); + #endif + zend_string_release(const_PI_name); + + zval property_name_default_value; + ZVAL_UNDEF(&property_name_default_value); + zend_string *property_name_name = zend_string_init("name", sizeof("name") - 1, 1); #if (PHP_VERSION_ID >= 80100) - zend_declare_typed_property(class_entry, property_prop_name, &property_prop_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_READONLY, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING)); + zend_declare_typed_property(class_entry, property_name_name, &property_name_default_value, ZEND_ACC_PUBLIC|ZEND_ACC_READONLY, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING)); #elif (PHP_VERSION_ID >= 80000) - zend_declare_typed_property(class_entry, property_prop_name, &property_prop_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING)); + zend_declare_typed_property(class_entry, property_name_name, &property_name_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING)); #endif - zend_string_release(property_prop_name); + zend_string_release(property_name_name); return class_entry; } -The preprocessor conditions are necessary because ``enum``s and ``readonly`` properties are PHP 8.1 -features and consequently, they don't exist in PHP 8.0. Therefore, the registration of ``Number`` is -completely omitted, while the ``readonly`` flag is not added for ``Foo::$prop`` below PHP 8.1 -versions. +The preprocessor conditions are necessary because ``enum``s, ``readonly`` properties, and the +``not-serializable`` flag, are PHP 8.1 features and don't exist in PHP 8.0. + +The registration of ``Number`` is therefore completely omitted, while the ``readonly`` flag is not +added for``Elephpant::$name`` for PHP versions before 8.1. + +Additionally, typed class constants are new in PHP 8.3, and hence a different registration function +is used for versions before 8.3. ****************************************** - Generating information for the optimizer + Generating Information for the Optimizer ****************************************** -A list of functions is maintained for the optimizer in ``Zend/Optimizer/zend_func_infos.h`` -containing extra information about the return type and the cardinality of the return value. These -pieces of information can enable more accurate optimizations (i.e. better type inference). -Previously, the file was maintained manually, however since PHP 8.1, ``gen_stub.php`` can take care -of this task when the ``--generate-optimizer-info`` option is passed to it. +A list of functions is maintained for the optimizer in ``Zend/Optimizer/zend_func_infos.h``. This +file contains extra information about the return type and the cardinality of the return value. This +can enable more accurate optimizations (i.e. better type inference). + +Previously, the file was maintained manually, but since PHP 8.1, ``gen_stub.php`` can take care of +this with the ``--generate-optimizer-info`` option. + +This feature is only available for built-in stubs inside php-src, since currently there is no way to +provide the function list for the optimizer other than overwriting ``zend_func_infos.h`` directly. A function is added to ``zend_func_infos.h`` if either the ``@return`` or the ``@refcount`` PHPDoc tag supplies more information than what is available based on the return type declaration. By @@ -468,27 +619,26 @@ An example from the built-in functions: */ function get_declared_classes(): array {} -Please note that the feature is only available for built-in stubs inside php-src, since currently -there is no way to provide the function list for the optimizer other than overwriting -``zend_func_infos.h`` directly. +Functions can be evaluated at compile-time if their arguments are known in compile-time, and their +behavior is free from side-effects and is not affected by the global state. + +The list of such functions in the optimizer was maintained manually until PHP 8.2. -Additionally, functions can be evaluated at compile-time if their arguments are known in -compile-time and their behavior if free from side-effects as well as it is not affected by the -global state. Until PHP 8.2, a list of such functions was maintained manually in the optimizer. -However, since PHP 8.2, the ``@compile-time-eval`` PHPDoc tag can be applied to any functions which -conform to the above restrictions in order for them to qualify as evaluable at compile-time. The -feature internally works by adding the ``ZEND_ACC_COMPILE_TIME_EVAL`` function flag. +Since PHP 8.2, the ``@compile-time-eval`` PHPDoc tag can be applied to any function which conform to +the above restrictions in order for them to qualify as evaluable at compile-time. The feature +internally works by adding the ``ZEND_ACC_COMPILE_TIME_EVAL`` function flag. -As of PHP 8.4, the concept of arity-based frameless functions was introduced. This is another -optimization technique, which results in faster internal function calls by eliminating unnecessary -checks for the number of passed parameters (if the number of passed arguments is known at -compile-time). +In PHP 8.4, arity-based frameless functions were introduced. This is another optimization technique, +which results in faster internal function calls by eliminating unnecessary checks for the number of +passed parameters—if the number of passed arguments is known at compile-time. -In order to take advantage of frameless functions, the ``@frameless-function`` PHPDoc tag has to be -provided along with some configuration. Since currently only arity-based optimizations are -supported, the following should be provided: ``@frameless-function {"arity": NUM}``, where ``NUM`` -is the number of parameters for which a frameless function is available. Let's see the stub of -``in_array()`` as an example: +To take advantage of frameless functions, add the ``@frameless-function`` PHPDoc tag with some +configuration. + +Since only arity-based optimizations are supported, the tag has the form: ``@frameless-function +{"arity": NUM}``. ``NUM`` is the number of parameters for which a frameless function is available. + +The stub of ``in_array()`` is a good example: .. code:: php @@ -539,46 +689,57 @@ the 3-parameter signatures: } ************************************** - Generating signatures for the manual + Generating Signatures for the Manual ************************************** -Theoretically, the manual should reflect the exact same signatures which are represented by the -stubs. This is not exactly the case yet for built-in symbols, but ``gen_stub.php`` have multiple -features to automate the process of synchronization. +The manual should reflect the exact same signatures which are represented by the stubs. This is not +exactly the case yet for built-in symbols, but ``gen_stub.php`` has multiple features to automate +the process of synchronization. + +Newly added functions or methods can be documented by providing the ``--generate-methodsynopses`` +option. + +Running ``./build/gen_stub.php --generate-methodsynopses ./ext/mbstring +../doc-en/reference/mbstring`` will create a dedicated page for each ``ext/mbstring`` function which +is not yet documented, and saves them into the ``../doc-en/reference/mbstring/functions`` directory. -First of all, newly added functions or methods can be documented by providing the -``--generate-methodsynopses`` option. E.g. running ``./build/gen_stub.php --generate-methodsynopses -./ext/mbstring ../doc-en/reference/mbstring`` will create a dedicated page for each ``ext/mbstring`` -function which is not yet documented, saving them into the -``../doc-en/reference/mbstring/functions`` directory. Since these are stub pages, many of the -sections are empty by default, so the relevant descriptions have to be added, while the irrelevant -ones have to be removed. +Since these are stub documentation pages, many of the sections are empty. Relevant descriptions have +to be added, and irrelevant sections should be removed. -For functions or methods which are already available in the manual, the documented signatures can be -updated by providing the ``--replace-methodsynopses`` option. E.g. running ``./build/gen_stub.php ---replace-methodsynopses ./ ../doc-en/`` will update all the function or method signatures in the -English documentation whose stub counterpart is found. +Functions or methods that are already available in the manual, the documented signatures can be +updated by providing the ``--replace-methodsynopses`` option. + +Running ``./build/gen_stub.php --replace-methodsynopses ./ ../doc-en/`` will update the function or +method signatures in the English documentation whose stub counterpart is found. Class signatures can be updated in the manual by providing the ``--replace-classsynopses`` option. -E.g. running ``./build/gen_stub.php --replace-classsynopses ./ ../doc-en/`` will update all the -class signatures in the English documentation whose stub counterpart is found. + +Running ``./build/gen_stub.php --replace-classsynopses ./ ../doc-en/`` will update all the class +signatures in the English documentation whose stub counterpart is found. If a symbol is not intended to be documented, the ``@undocumentable`` PHPDoc tag should be added to -it. Doing so will prevent any documentation to be created for the given symbol. In order not to add -a whole stub file to the manual, the PHPDoc tag should be applied to the file itself. These -possibilities are useful for symbols which exist only for testing purposes (e.g. the ones declared -for ``ext/zend_test``), or by some other reason documentation is not possible. +it. Doing so will prevent any documentation to be created for the given symbol. + +To avoid a whole stub file to be added to the manual, this PHPDoc tag should be applied to the file +itself. + +These flags are useful for symbols which exist only for testing purposes (e.g. the ones declared for +``ext/zend_test``), or by some other reason documentation is not possible. ************ Validation ************ -It's possible to validate whether the alias function/method signatures are correct by providing the -``--verify`` flag to ``gen_stub.php``. Normally, an alias function/method should have the exact same -signature as its aliased function/method counterpart has apart from the name. In some cases this is -not achievable by some reason (i.e. ``bzwrite()`` is an alias of ``fwrite()``, but the name of the -first parameter is different because the resource types differ). In order to suppress the error when -the check is false positive, the ``@no-verify`` PHPDoc tag should be applied to the alias: +You can use the ``--verify`` flag to ``gen_stub.php`` to validate whether the alias function/method +signatures are correct. + +An alias function/method should have the exact same signature as its aliased function/method +counterpart, apart from the name. In some cases this is not possible. For example. ``bzwrite()`` is +an alias of ``fwrite()``, but the name of the first parameter is different because the resource +types differ. + +In order to suppress the error when the check is false positive, the ``@no-verify`` PHPDoc tag +should be applied to the alias: .. code:: php @@ -590,21 +751,36 @@ the check is false positive, the ``@no-verify`` PHPDoc tag should be applied to function bzwrite($bz, string $data, ?int $length = null): int|false {} Besides aliases, the contents of the documentation can also be validated by providing the -``--verify-manual`` option to ``gen_stub.php`` along with the path of the manual as the last -argument: e.g. ``./build/gen_stub.php --verify-manual ./ ../doc-en/`` when validation is based on -all stubs in ``php-src`` and the English documentation is available in the parent directory. +``--verify-manual`` option to ``gen_stub.php``. This flag requires the directory with the source +stubs, and the target manual directory, as in ``./build/gen_stub.php --verify-manual ./ +../doc-en/``. -When this feature is used, the following validations are performed: +For this validation, all ``php-src`` stubs and the full English documentation should be available by +the specified path. + +This feature performs the following validations: - Detecting missing global constants - Detecting missing classes - Detecting missing methods - Detecting incorrectly documented alias functions or methods +Running it with the stub examples that are used in this guide, the following warnings are shown: + +.. code:: shell + + Warning: Missing class synopsis for Number + Warning: Missing class synopsis for Elephant + Warning: Missing class synopsis for Atmosphere + Warning: Missing method synopsis for fahrenheitToCelcius() + Warning: Missing method synopsis for Atmosphere::calculateBar() + ********************** - Parameter statistics + Parameter Statistics ********************** -A less commonly used feature of ``gen_stub.php`` is to count how many times a parameter name occurs -in the codebase: ``./build/gen_stub.php --parameter-stats``. The result is a JSON object containing -the parameter names and the number of their occurrences in descending order. +The ``gen_stub.php`` flag ``--parameter-stats`` counts how many times a parameter name occurs in the +codebase. + +A JSON object is displayed, containing the parameter names and the number of their occurrences in +descending order. From 622c777a6c05e78de85b15dd1b6664eb867dcfbd Mon Sep 17 00:00:00 2001 From: Derick Rethans Date: Thu, 23 May 2024 17:44:42 +0100 Subject: [PATCH 6/9] Remove duplicate file header sections --- docs/source/miscellaneous/stubs.rst | 9 --------- 1 file changed, 9 deletions(-) diff --git a/docs/source/miscellaneous/stubs.rst b/docs/source/miscellaneous/stubs.rst index d0ddaef72f99b..569e7895de914 100644 --- a/docs/source/miscellaneous/stubs.rst +++ b/docs/source/miscellaneous/stubs.rst @@ -163,9 +163,6 @@ This results in the following arginfo file: .. code:: c - /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 377cb85cf4568134395f5834f193fdb5560f9205 */ - ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_addElephantsToHerd, 0, 2, IS_STRING, 0) ZEND_ARG_INFO(1, herd) ZEND_ARG_TYPE_INFO(ZEND_SEND_PREFER_REF, elephantName, IS_STRING, 0) @@ -194,9 +191,6 @@ Now, the following C code is generated: .. code:: c - /* This is a generated file, edit the .stub.php file instead. - * Stub hash: cf2623bf018b829c95e956dcc79d161d9678f96f */ - ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_fahrenheitToCelcius, 0, 1, IS_DOUBLE, 0) ZEND_ARG_TYPE_INFO(0, fahrenheit, IS_DOUBLE, 0) ZEND_END_ARG_INFO() @@ -289,9 +283,6 @@ The following arginfo file is generated: .. code:: c - /* This is a generated file, edit the .stub.php file instead. - * Stub hash: 6cb86300f1bf294019e10cb330dde2c142ed9794 */ - static const zend_function_entry class_Number_methods[] = { ZEND_FE_END }; From 5f303265588dab4152dffe151f3001dea44b4ac0 Mon Sep 17 00:00:00 2001 From: Derick Rethans Date: Thu, 23 May 2024 17:45:04 +0100 Subject: [PATCH 7/9] Tweaks from review --- docs/source/miscellaneous/stubs.rst | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/docs/source/miscellaneous/stubs.rst b/docs/source/miscellaneous/stubs.rst index 569e7895de914..9d2b36e4e4942 100644 --- a/docs/source/miscellaneous/stubs.rst +++ b/docs/source/miscellaneous/stubs.rst @@ -243,7 +243,7 @@ Additional meta information can be attached to functions, with the following PHP - ``@genstubs-expose-comment-block``: By adding this annotation at the beginning of a PHPDoc block, the content of the PHPDoc block will be exposed for - `ReflectionFunctionAbstract::getDocComment()`. This feature as added in PHP 8.4.0. + `ReflectionFunctionAbstract::getDocComment()`. This feature was added in PHP 8.4.0. .. _tentative return type: https://wiki.php.net/rfc/internal_method_return_types @@ -615,8 +615,8 @@ behavior is free from side-effects and is not affected by the global state. The list of such functions in the optimizer was maintained manually until PHP 8.2. -Since PHP 8.2, the ``@compile-time-eval`` PHPDoc tag can be applied to any function which conform to -the above restrictions in order for them to qualify as evaluable at compile-time. The feature +Since PHP 8.2, the ``@compile-time-eval`` PHPDoc tag can be applied to any function which conforms +to the above restrictions in order for them to qualify as evaluable at compile-time. The feature internally works by adding the ``ZEND_ACC_COMPILE_TIME_EVAL`` function flag. In PHP 8.4, arity-based frameless functions were introduced. This is another optimization technique, @@ -709,10 +709,8 @@ Running ``./build/gen_stub.php --replace-classsynopses ./ ../doc-en/`` will upda signatures in the English documentation whose stub counterpart is found. If a symbol is not intended to be documented, the ``@undocumentable`` PHPDoc tag should be added to -it. Doing so will prevent any documentation to be created for the given symbol. - -To avoid a whole stub file to be added to the manual, this PHPDoc tag should be applied to the file -itself. +it. Doing so will prevent any documentation to be created for the given symbol. To avoid a whole +stub file to be added to the manual, this PHPDoc tag should be applied to the file itself. These flags are useful for symbols which exist only for testing purposes (e.g. the ones declared for ``ext/zend_test``), or by some other reason documentation is not possible. From 300378e4c06235f39ed9e33688631d7dbbafc1f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1t=C3=A9=20Kocsis?= Date: Mon, 27 May 2024 22:33:51 +0200 Subject: [PATCH 8/9] Update docs/source/miscellaneous/stubs.rst --- docs/source/miscellaneous/stubs.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/source/miscellaneous/stubs.rst b/docs/source/miscellaneous/stubs.rst index 9d2b36e4e4942..0939b83e09d41 100644 --- a/docs/source/miscellaneous/stubs.rst +++ b/docs/source/miscellaneous/stubs.rst @@ -63,7 +63,8 @@ The following sections will introduce these capabilities. Generating arginfo Structures ******************************* -The purpose of stubs is to make it easier to declare arginfo structures. +The purpose of stubs files is to make it easier to declare arginfo structures, +validate parameters parsing declarations, and maintain documentation. Previously, you had to manually use the different ``ZEND_BEGIN_ARG_* ... ZEND_END_ARG_INFO()`` macros. This is a tedious and error-prone process. Being able to use pure PHP code on which the C From 97e534fffa87f693bb4b09210588f63a4224bc27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1t=C3=A9=20Kocsis?= Date: Mon, 27 May 2024 22:39:52 +0200 Subject: [PATCH 9/9] Fix formatting --- docs/source/miscellaneous/stubs.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/source/miscellaneous/stubs.rst b/docs/source/miscellaneous/stubs.rst index 0939b83e09d41..5a3f5b02217b0 100644 --- a/docs/source/miscellaneous/stubs.rst +++ b/docs/source/miscellaneous/stubs.rst @@ -63,8 +63,8 @@ The following sections will introduce these capabilities. Generating arginfo Structures ******************************* -The purpose of stubs files is to make it easier to declare arginfo structures, -validate parameters parsing declarations, and maintain documentation. +The purpose of stubs files is to make it easier to declare arginfo structures, validate parameters +parsing declarations, and maintain documentation. Previously, you had to manually use the different ``ZEND_BEGIN_ARG_* ... ZEND_END_ARG_INFO()`` macros. This is a tedious and error-prone process. Being able to use pure PHP code on which the C