diff --git a/docs/source/miscellaneous/stubs.rst b/docs/source/miscellaneous/stubs.rst index 6d9ed10c76bd1..0939b83e09d41 100644 --- a/docs/source/miscellaneous/stubs.rst +++ b/docs/source/miscellaneous/stubs.rst @@ -2,237 +2,282 @@ 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 +611,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 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. -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 +681,55 @@ 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 +741,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.