From d888259e86c789544e6fe0850cde4c565b0235a6 Mon Sep 17 00:00:00 2001 From: Derick Rethans Date: Tue, 21 May 2024 16:01:48 +0100 Subject: [PATCH 1/4] 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 2/4] 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 3/4] 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 4/4] 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