xref: /php-src/docs/source/miscellaneous/stubs.rst (revision 4a457429)
1#######
2 Stubs
3#######
4
5Stub files are pieces of PHP code which only contain declarations. They do not include runnable
6code, but instead contain empty function and method bodies. A very basic stub looks like this:
7
8.. code:: php
9
10   <?php
11   /** @var string */
12   const ANIMAL = "Elephant";
13   /** @var float */
14   const WEIGHT = 6.8;
15
16   class Atmopshere {
17       public function calculateBar(): float {}
18   }
19
20   function fahrenheitToCelcius(float $fahrenheitToCelcius): float {}
21
22Any kind of symbol can be declared via stubs. Every type can be used, with the exception of
23disjunctive normal form (DNF) types. Additional meta information can be added via PHPDoc blocks or
24PHP attributes. Namespaces can also be used by adding a top-level ``namespace`` declaration or by
25using namespace blocks:
26
27.. code:: php
28
29   <?php
30   namespace {
31       /** @var string */
32       const ANIMAL = "Elephant";
33       /** @var float */
34       const WEIGHT_TON = 6.8;
35
36       class Atmopshere {
37           public function calculateBar(): float {}
38       }
39   }
40
41   namespace Algorithms {
42       function fahrenheitToCelcius(float $fahrenheit): float {}
43   }
44
45The above example declares the global constants ``ANIMAL`` and ``WEIGHT_TON``, and the class
46``Atmopshere`` in the top-level namespace. The ``fahrenheitToCelcius()`` function is declared to be
47in the ``Algorithms`` namespace.
48
49********************
50 Using gen_stub.php
51********************
52
53Stub files have the ``.stub.php`` extension by convention.
54
55They are processed by ``build/gen_stub.php``, which uses PHP-Parser_ for parsing. Depending on the
56configuration and the supplied arguments, it can generate various artefacts.
57
58The following sections will introduce these capabilities.
59
60.. _php-parser: https://github.com/nikic/PHP-Parser
61
62*******************************
63 Generating arginfo Structures
64*******************************
65
66The purpose of stubs files is to make it easier to declare arginfo structures, validate parameters
67parsing declarations, and maintain documentation.
68
69Previously, you had to manually use the different ``ZEND_BEGIN_ARG_* ... ZEND_END_ARG_INFO()``
70macros. This is a tedious and error-prone process. Being able to use pure PHP code on which the C
71code can be generated is a huge benefit.
72
73The arginfo file matching our first example looks like:
74
75.. code:: c
76
77   /* This is a generated file, edit the .stub.php file instead.
78    * Stub hash: e4ed788d54a20272a92a3f6618b73d48ec848f97 */
79
80   ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_fahrenheitToCelcius, 0, 1, IS_DOUBLE, 0)
81       ZEND_ARG_TYPE_INFO(0, fahrenheitToCelcius, IS_DOUBLE, 0)
82   ZEND_END_ARG_INFO()
83
84   ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Atmopshere_calculateBar, 0, 0, IS_DOUBLE, 0)
85   ZEND_END_ARG_INFO()
86
87The hash that is included in the file makes sure that stub files are not reprocessed unless the stub
88file was modified, or something requires it to be processed (e.g. regeneration was forced by using
89the ``-f`` flag).
90
91Another thing to keep in mind is that stub-based type declarations have to be in sync with the
92parameter parsing code in the PHP functions through ``ZEND_PARSE_PARAMETERS_*`` macros (``ZPP``).
93
94In release builds, the arginfo structures are only used with Reflection.
95
96In debug builds, PHP will compare arginfo structures against ZPP macros to ensure that the stubs and
97actual data matches for both arguments and return types. If they do not, an error is generated.
98Therefore it is important that you declare the right types in your stub files.
99
100For documentation purposes, PHPDoc can be used.
101
102Since PHP 8.0, arginfo structures can also contain default values. These can for example be used by
103``ReflectionParameter::getDefaultValue()``.
104
105Besides constant literals, default values can also contain compile-time evaluable expressions, and
106contain references to constants.
107
108In the example below, we define a function with an optional argument, referencing a constant:
109
110.. code:: php
111
112   <?php
113   /** @var string */
114   const ANIMAL = "Elephant";
115
116   function formatName(string $defaultName = ANIMAL . " Mc" . ANIMAL . "Face"): string {}
117
118This will result in the following arginfo:
119
120.. code:: c
121
122   /* This is a generated file, edit the .stub.php file instead.
123    * Stub hash: a9685164284e73f47b15838122b631ebdfef23d6 */
124
125   ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_formatName, 0, 0, IS_STRING, 0)
126       ZEND_ARG_TYPE_INFO_WITH_DEFAULT_VALUE(0, defaultName, IS_STRING, 0, "ANIMAL . \" Mc\" . ANIMAL . \"Face\"")
127   ZEND_END_ARG_INFO()
128
129You can only use constants as long as they are defined in the same stub file.
130
131If this is not possible, then the stub declaring the constant should be included with ``require``:
132
133.. code:: php
134
135   // constants.stub.php
136   <?php
137   /** @var string */
138   const ANIMAL = "Elephant";
139
140.. code:: php
141
142   // example.stub.php
143   <?php
144   require "constants.stub.php";
145
146   function foo(string $param = ANIMAL): string {}
147
148Sometimes arguments have to be passed by reference, or by using the `ZEND_SEND_PREFER_REF` flag.
149
150To signal parsing by reference, use the usual ``&`` syntax.
151
152To include the ``ZEND_SEND_PREFER_REF`` flag, use the ``@prefer-ref`` PHPDoc tag:
153
154.. code:: php
155
156   <?php
157   /**
158    * @param array $herd
159    * @prefer-ref $elephantName
160    */
161   function addElephantsToHerd(&$herd, string $elephantName): string {}
162
163This results in the following arginfo file:
164
165.. code:: c
166
167   ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_addElephantsToHerd, 0, 2, IS_STRING, 0)
168       ZEND_ARG_INFO(1, herd)
169       ZEND_ARG_TYPE_INFO(ZEND_SEND_PREFER_REF, elephantName, IS_STRING, 0)
170   ZEND_END_ARG_INFO()
171
172*****************************
173 Generating Function Entries
174*****************************
175
176Besides arginfo structures, function entries themselves can also be generated via stubs.
177
178In order to generate these, add the file-level ``@generate-function-entries`` PHPDoc tag:
179
180.. code:: php
181
182   <?php
183   /** @generate-function-entries */
184
185   class Atmosphere {
186      public function calculateBar(): float {}
187   }
188
189   function fahrenheitToCelcius(float $fahrenheit): float {}
190
191Now, the following C code is generated:
192
193.. code:: c
194
195   ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_fahrenheitToCelcius, 0, 1, IS_DOUBLE, 0)
196       ZEND_ARG_TYPE_INFO(0, fahrenheit, IS_DOUBLE, 0)
197   ZEND_END_ARG_INFO()
198
199   ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_class_Atmosphere_calculateBar, 0, 0, IS_DOUBLE, 0)
200   ZEND_END_ARG_INFO()
201
202   ZEND_FUNCTION(fahrenheitToCelcius);
203   ZEND_METHOD(Atmosphere, calculateBar);
204
205   static const zend_function_entry ext_functions[] = {
206       ZEND_FE(fahrenheitToCelcius, arginfo_fahrenheitToCelcius)
207       ZEND_FE_END
208   };
209
210   static const zend_function_entry class_Atmosphere_methods[] = {
211       ZEND_ME(Atmosphere, calculateBar, arginfo_class_Atmosphere_calculateBar, ZEND_ACC_PUBLIC)
212       ZEND_FE_END
213   };
214
215The generated ``ext_functions`` variable must be passed as the ``functions`` member of
216`zend_module_entry` struct.
217
218The generated ``class_Atmosphere_methods`` must be used when registering the ``Atmosphere`` class:
219
220.. code:: c
221
222   INIT_CLASS_ENTRY(ce, "Atmosphere", class_Atmosphere_methods);
223
224Additional meta information can be attached to functions, with the following PHPDoc tags:
225
226-  ``@deprecated``: Triggers the usual deprecation notice when the function/method is called.
227
228-  ``@alias``: If a function/method is an alias of another function/method, then the aliased
229   function/method name has to be provided as value. E.g. the function ``sizeof()` has the ``@alias
230   count`` annotation.
231
232-  ``@implementation-alias``: This is very similar to ``@alias`` with some semantic differences.
233   These aliases exists purely to avoid duplicating some code, but there is no other connection
234   between the alias and the aliased function or method.
235
236   A notable example is ``Error::getCode()``, which has the ``@implementation-alias
237   Exception::getCode`` annotation.
238
239   The difference between ``@alias`` and ``@implementation-alias`` is very nuanced and is only
240   observable in the manual.
241
242-  ``@tentative-return-type``: By using this annotation, the return type declaration is reclassified
243   as a `tentative return type`_.
244
245-  ``@genstubs-expose-comment-block``: By adding this annotation at the beginning of a PHPDoc block,
246   the content of the PHPDoc block will be exposed for
247   `ReflectionFunctionAbstract::getDocComment()`. This feature was added in PHP 8.4.0.
248
249.. _tentative return type: https://wiki.php.net/rfc/internal_method_return_types
250
251**************************
252 Generating Class Entries
253**************************
254
255In order to generate code which is necessary for registering constants, classes, properties, enums,
256and traits, use the ``@generate-class-entries`` file-level PHPDoc block.
257
258``@generate-class-entries`` implies ``@generate-function-entries```, so the latter is then
259superfluous.
260
261Given the following stub:
262
263.. code:: php
264
265    <?php
266    /** @generate-class-entries */
267
268   enum Number: string {
269       /** @var string */
270       public const ONE = "one";
271
272       case One = Number::ONE;
273       case Two = Number::TWO;
274   }
275
276   class Elephant extends stdClass {
277       /** @cvalue M_PI */
278       public const float PI = UNKNOWN;
279
280       public readonly string $name;
281   }
282
283The following arginfo file is generated:
284
285.. code:: c
286
287   static const zend_function_entry class_Number_methods[] = {
288       ZEND_FE_END
289   };
290
291   static const zend_function_entry class_Elephant_methods[] = {
292       ZEND_FE_END
293   };
294
295   static zend_class_entry *register_class_Number(void)
296   {
297       zend_class_entry *class_entry = zend_register_internal_enum("Number", IS_STRING, class_Number_methods);
298
299       ...
300
301       return class_entry;
302   }
303
304   static zend_class_entry *register_class_Elephant(zend_class_entry *class_entry_stdClass)
305   {
306       zend_class_entry ce, *class_entry;
307
308       INIT_CLASS_ENTRY(ce, "Elephant", class_Elephant_methods);
309       class_entry = zend_register_internal_class_ex(&ce, class_entry_stdClass);
310
311       ...
312
313       return class_entry;
314   }
315
316The generated ``register_class_*()`` functions must be used to register these classes in the
317``PHP_MINIT_FUNCTION`` directly:
318
319.. code:: c
320
321   zend_class_entry *number_ce = register_class_Number();
322   zend_class_entry *elephpant_ce = register_class_Elephant(zend_standard_class_def);
323
324Class dependencies, such as the parent class or implemented interface, have to be passed to the
325register function. In the example above, we passed the class entry for ``stdClass``
326(``zend_standard_class_def``).
327
328Like functions and methods, classes also support meta information passed via PHPDoc tags:
329
330-  ``@deprecated``: triggers a deprecation notice when the class is used
331
332-  ``@strict-properties``: adds the ``ZEND_ACC_NO_DYNAMIC_PROPERTIES`` flag for the class (as of PHP
333   8.0), which disallow dynamic properties.
334
335-  ``@not-serializable``: adds the ``ZEND_ACC_NOT_SERIALIZABLE`` flag for the class (as of PHP 8.1),
336   which prevents the serialization of the class.
337
338-  ``@genstubs-expose-comment-block``: By adding this tag at the beginning of a PHPDoc block, the
339   content of the PHPDoc block will be exposed for `ReflectionClass::getDocComment()`. This feature
340   is only available as of PHP 8.4.0.
341
342This is an example with all the flags:
343
344.. code:: php
345
346   <?php
347   /**
348    * @generate-class-entries
349    */
350
351   /**
352    * @deprecated
353    * @not-serializable
354    * @strict-properties */
355   /** @genstubs-expose-comment-block
356    * This is a comment
357    * @see https://www.php.net */
358   class Elephant extends stdClass {
359      public readonly string $name;
360   }
361
362Resulting in these changes:
363
364.. code:: c
365
366   ...
367
368   static zend_class_entry *register_class_Elephant(zend_class_entry *class_entry_stdClass)
369   {
370       zend_class_entry ce, *class_entry;
371
372       INIT_CLASS_ENTRY(ce, "Elephant", class_Elephant_methods);
373       class_entry = zend_register_internal_class_ex(&ce, class_entry_stdClass);
374       class_entry->ce_flags |= ZEND_ACC_DEPRECATED|ZEND_ACC_NO_DYNAMIC_PROPERTIES|ZEND_ACC_NOT_SERIALIZABLE;
375       class_entry->doc_comment = zend_string_init_interned("/**\n * This is a comment\n * @see https://www.php.net */", 55, 1);
376
377   ...
378
379       return class_entry;
380   }
381
382********************************************
383 Generating Global Constants and Attributes
384********************************************
385
386Although global constants and function attributes do not relate to classes, they require the ``/**
387@generate-class-entries */`` file-level PHPDoc block.
388
389If a global constant or function attribute are present in the stub file, the generated C-code will
390include a ``register_{$stub_file_name}_symbols()`` file.
391
392Given the following file:
393
394.. code:: php
395
396   // example.stub.php
397   <?php
398   /** @generate-class-entries */
399
400   /** @var string */
401   const ANIMAL = "Elephant";
402
403   /**
404   * @var float
405   * @cvalue M_PI
406   */
407   const BAR = UNKNOWN;
408
409   function connect(#[\SensitiveParameter] string $connectionString): string {}
410
411The following C function will be generated in order to register the two global constants and the
412attribute. The name of this file is ``example.stub.php``:
413
414.. code:: c
415
416   ...
417
418   static void register_example_symbols(int module_number)
419   {
420       REGISTER_STRING_CONSTANT("ANIMAL", "Elephant", CONST_PERSISTENT);
421       REGISTER_DOUBLE_CONSTANT("BAR", M_PI, CONST_PERSISTENT);
422
423
424       zend_add_parameter_attribute(zend_hash_str_find_ptr(CG(function_table), "connect", sizeof("connect") - 1), 0, ZSTR_KNOWN(ZEND_STR_SENSITIVEPARAMETER), 0);
425   }
426
427Similarly to class registration functions, the generated ``register_{$stub_file_name}_symbols()``
428functions must be used in ``PHP_MINIT_FUNCTION``, to make the global constants an attributes
429available:
430
431.. code:: c
432
433   PHP_MINIT_FUNCTION(example)
434   {
435       register_example_symbols(module_number);
436
437       return SUCCESS;
438   }
439
440Global constants always need to have their type specified with a ``@var`` PHPDoc tag. The type for
441class constants is inferred from their type declaration if available, otherwise a ``@var`` PHPDoc
442tag is required. A ``@var`` tag is also required if you enable ``generate-legacy-arginfo`` (see
443below).
444
445If a constant's value is defined by a 3rd party library, PHP's internals, or a specific type such as
446a bitmask, the exact value is not yet known when stubs are used. In these cases, don't duplicate the
447value in the stub file, but instead use the ``UNKNOWN`` constant value with the ``@cvalue`` PHPDoc
448tag.
449
450In the example below we define the ``BAR`` global constant to ``UNKNOWN``, with the value linked
451with ``@cvalue M_PI`` to the C-level constant ``M_PI`` (define by PHP's internals).
452
453Constants can take the following extra meta information passed via PHPDoc tags:
454
455-  ``@deprecated``: Triggers a deprecation notice when the constant is used.
456
457-  ``@genstubs-expose-comment-block``: By adding this tag at the beginning of a PHPDoc block, the
458   content of the PHPDoc block will be exposed for `ReflectionClass::getDocComment()`. This feature
459   is only available as of PHP 8.4.0.
460
461************************************
462 Maintaining Backward Compatibility
463************************************
464
465The stubs in the PHP source distribution only need to support the branch they are part of.
466
467Third party extensions often need to support a wider range of PHP versions, with different features
468supported, that can be enabled through stubs.
469
470Stubs may get new features which are unavailable in earlier PHP versions, or ABI compatibility
471breaks may happen between minor releases. And PHP 7.x versions are substantially different from PHP
4728 versions.
473
474It is possible to tell the arginfo generator script ``gen_stub.php`` to create legacy arginfo too,
475specifying a minimum supported version.
476
477If your extension still needs to handle PHP 7, then add the ``@generate-legacy-arginfo`` file-level
478PHPDoc tag, without any value. In this case, an additional ``_legacy_arginfo.h`` file will be
479generated. You can include this file conditionally, such as:
480
481.. code::
482
483   #if (PHP_VERSION_ID >= 80000)
484   # include "example_arginfo.h"
485   #else
486   # include "example_legacy_arginfo.h"
487   #endif
488
489When ``@generate-legacy-arginfo`` is passed the minimum PHP version ID that needs to be supported,
490then only one arginfo file is going to be generated, and ``#if`` prepocessor directives will ensure
491compatibility with all the required PHP 8 versions.
492
493PHP Version IDs are as follows: ``80000`` for PHP 8.0, ``80100`` for PHP PHP 8.1, ``80200`` for PHP
4948.2, ``80300`` for PHP 8.3, and ``80400`` for PHP 8.4,
495
496In this example we add a PHP 8.0 compatibility requirement to a slightly modified version of a
497previous example:
498
499.. code:: php
500
501   <?php
502   /**
503    * @generate-class-entries
504    * @generate-legacy-arginfo 80000
505    */
506
507   enum Number: string {
508      case One;
509   }
510
511   /**
512    * @strict-properties
513    * @not-serializable */
514   class Elephant {
515      /**
516       * @cvalue M_PI
517       * @var float
518       */
519      public const float PI = UNKNOWN;
520
521      public readonly string $name;
522   }
523
524Then notice the ``#if (PHP_VERSION_ID >= ...)`` conditions in the generated arginfo file:
525
526.. code:: c
527
528   ...
529
530   #if (PHP_VERSION_ID >= 80100)
531   static zend_class_entry *register_class_Number(void)
532   {
533       zend_class_entry *class_entry = zend_register_internal_enum("Number", IS_STRING, class_Number_methods);
534
535       zend_enum_add_case_cstr(class_entry, "One", NULL);
536
537       return class_entry;
538   }
539   #endif
540
541   static zend_class_entry *register_class_Elephant(void)
542   {
543       zend_class_entry ce, *class_entry;
544
545       INIT_CLASS_ENTRY(ce, "Elephant", class_Elephant_methods);
546       class_entry = zend_register_internal_class_ex(&ce, NULL);
547   #if (PHP_VERSION_ID >= 80100)
548       class_entry->ce_flags |= ZEND_ACC_NO_DYNAMIC_PROPERTIES|ZEND_ACC_NOT_SERIALIZABLE;
549   #elif (PHP_VERSION_ID >= 80000)
550       class_entry->ce_flags |= ZEND_ACC_NO_DYNAMIC_PROPERTIES;
551   #endif
552
553       zval const_PI_value;
554       ZVAL_DOUBLE(&const_PI_value, M_PI);
555       zend_string *const_PI_name = zend_string_init_interned("PI", sizeof("PI") - 1, 1);
556   #if (PHP_VERSION_ID >= 80300)
557       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));
558   #else
559       zend_declare_class_constant_ex(class_entry, const_PI_name, &const_PI_value, ZEND_ACC_PUBLIC, NULL);
560   #endif
561       zend_string_release(const_PI_name);
562
563       zval property_name_default_value;
564       ZVAL_UNDEF(&property_name_default_value);
565       zend_string *property_name_name = zend_string_init("name", sizeof("name") - 1, 1);
566   #if (PHP_VERSION_ID >= 80100)
567       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));
568   #elif (PHP_VERSION_ID >= 80000)
569       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));
570   #endif
571       zend_string_release(property_name_name);
572
573       return class_entry;
574   }
575
576The preprocessor conditions are necessary because ``enum``s, ``readonly`` properties, and the
577``not-serializable`` flag, are PHP 8.1 features and don't exist in PHP 8.0.
578
579The registration of ``Number`` is therefore completely omitted, while the ``readonly`` flag is not
580added for``Elephpant::$name`` for PHP versions before 8.1.
581
582Additionally, typed class constants are new in PHP 8.3, and hence a different registration function
583is used for versions before 8.3.
584
585******************************************
586 Generating Information for the Optimizer
587******************************************
588
589A list of functions is maintained for the optimizer in ``Zend/Optimizer/zend_func_infos.h``. This
590file contains extra information about the return type and the cardinality of the return value. This
591can enable more accurate optimizations (i.e. better type inference).
592
593Previously, the file was maintained manually, but since PHP 8.1, ``gen_stub.php`` can take care of
594this with the ``--generate-optimizer-info`` option.
595
596This feature is only available for built-in stubs inside php-src, since currently there is no way to
597provide the function list for the optimizer other than overwriting ``zend_func_infos.h`` directly.
598
599A function is added to ``zend_func_infos.h`` if either the ``@return`` or the ``@refcount`` PHPDoc
600tag supplies more information than what is available based on the return type declaration. By
601default, scalar return types have a ``refcount`` of ``0``, while non-scalar values are ``N``. If a
602function can only return newly created non-scalar values, its ``refcount`` can be set to ``1``.
603
604An example from the built-in functions:
605
606.. code:: php
607
608   /**
609    * @return array<int, string>
610    * @refcount 1
611    */
612   function get_declared_classes(): array {}
613
614Functions can be evaluated at compile-time if their arguments are known in compile-time, and their
615behavior is free from side-effects and is not affected by the global state.
616
617The list of such functions in the optimizer was maintained manually until PHP 8.2.
618
619Since PHP 8.2, the ``@compile-time-eval`` PHPDoc tag can be applied to any function which conforms
620to the above restrictions in order for them to qualify as evaluable at compile-time. The feature
621internally works by adding the ``ZEND_ACC_COMPILE_TIME_EVAL`` function flag.
622
623In PHP 8.4, arity-based frameless functions were introduced. This is another optimization technique,
624which results in faster internal function calls by eliminating unnecessary checks for the number of
625passed parameters—if the number of passed arguments is known at compile-time.
626
627To take advantage of frameless functions, add the ``@frameless-function`` PHPDoc tag with some
628configuration.
629
630Since only arity-based optimizations are supported, the tag has the form: ``@frameless-function
631{"arity": NUM}``. ``NUM`` is the number of parameters for which a frameless function is available.
632
633The stub of ``in_array()`` is a good example:
634
635.. code:: php
636
637   /**
638    * @compile-time-eval
639    * @frameless-function {"arity": 2}
640    * @frameless-function {"arity": 3}
641    */
642   function in_array(mixed $needle, array $haystack, bool $strict = false): bool {}
643
644Apart from being compile-time evaluable, it has a frameless function counterpart for both the 2 and
645the 3-parameter signatures:
646
647.. code:: c
648
649   /* The regular in_array() function */
650   PHP_FUNCTION(in_array)
651   {
652       php_search_array(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
653   }
654
655   /* The frameless version of the in_array() function when 2 arguments are passed */
656   ZEND_FRAMELESS_FUNCTION(in_array, 2)
657   {
658       zval *value, *array;
659
660       Z_FLF_PARAM_ZVAL(1, value);
661       Z_FLF_PARAM_ARRAY(2, array);
662
663       _php_search_array(return_value, value, array, false, 0);
664
665   flf_clean:;
666   }
667
668   /* The frameless version of the in_array() function when 3 arguments are passed */
669   ZEND_FRAMELESS_FUNCTION(in_array, 3)
670   {
671       zval *value, *array;
672       bool strict;
673
674       Z_FLF_PARAM_ZVAL(1, value);
675       Z_FLF_PARAM_ARRAY(2, array);
676       Z_FLF_PARAM_BOOL(3, strict);
677
678       _php_search_array(return_value, value, array, strict, 0);
679
680   flf_clean:;
681   }
682
683**************************************
684 Generating Signatures for the Manual
685**************************************
686
687The manual should reflect the exact same signatures which are represented by the stubs. This is not
688exactly the case yet for built-in symbols, but ``gen_stub.php`` has multiple features to automate
689the process of synchronization.
690
691Newly added functions or methods can be documented by providing the ``--generate-methodsynopses``
692option.
693
694Running ``./build/gen_stub.php --generate-methodsynopses ./ext/mbstring
695../doc-en/reference/mbstring`` will create a dedicated page for each ``ext/mbstring`` function which
696is not yet documented, and saves them into the ``../doc-en/reference/mbstring/functions`` directory.
697
698Since these are stub documentation pages, many of the sections are empty. Relevant descriptions have
699to be added, and irrelevant sections should be removed.
700
701Functions or methods that are already available in the manual, the documented signatures can be
702updated by providing the ``--replace-methodsynopses`` option.
703
704Running ``./build/gen_stub.php --replace-methodsynopses ./ ../doc-en/`` will update the function or
705method signatures in the English documentation whose stub counterpart is found.
706
707Class signatures can be updated in the manual by providing the ``--replace-classsynopses`` option.
708
709Running ``./build/gen_stub.php --replace-classsynopses ./ ../doc-en/`` will update all the class
710signatures in the English documentation whose stub counterpart is found.
711
712If a symbol is not intended to be documented, the ``@undocumentable`` PHPDoc tag should be added to
713it. Doing so will prevent any documentation to be created for the given symbol. To avoid a whole
714stub file to be added to the manual, this PHPDoc tag should be applied to the file itself.
715
716These flags are useful for symbols which exist only for testing purposes (e.g. the ones declared for
717``ext/zend_test``), or by some other reason documentation is not possible.
718
719************
720 Validation
721************
722
723You can use the ``--verify`` flag to ``gen_stub.php`` to validate whether the alias function/method
724signatures are correct.
725
726An alias function/method should have the exact same signature as its aliased function/method
727counterpart, apart from the name. In some cases this is not possible. For example. ``bzwrite()`` is
728an alias of ``fwrite()``, but the name of the first parameter is different because the resource
729types differ.
730
731In order to suppress the error when the check is false positive, the ``@no-verify`` PHPDoc tag
732should be applied to the alias:
733
734.. code:: php
735
736   /**
737    * @param resource $bz
738    * @implementation-alias fwrite
739    * @no-verify Uses different parameter name
740    */
741   function bzwrite($bz, string $data, ?int $length = null): int|false {}
742
743Besides aliases, the contents of the documentation can also be validated by providing the
744``--verify-manual`` option to ``gen_stub.php``. This flag requires the directory with the source
745stubs, and the target manual directory, as in ``./build/gen_stub.php --verify-manual ./
746../doc-en/``.
747
748For this validation, all ``php-src`` stubs and the full English documentation should be available by
749the specified path.
750
751This feature performs the following validations:
752
753-  Detecting missing global constants
754-  Detecting missing classes
755-  Detecting missing methods
756-  Detecting incorrectly documented alias functions or methods
757
758Running it with the stub examples that are used in this guide, the following warnings are shown:
759
760.. code:: shell
761
762   Warning: Missing class synopsis for Number
763   Warning: Missing class synopsis for Elephant
764   Warning: Missing class synopsis for Atmosphere
765   Warning: Missing method synopsis for fahrenheitToCelcius()
766   Warning: Missing method synopsis for Atmosphere::calculateBar()
767
768**********************
769 Parameter Statistics
770**********************
771
772The ``gen_stub.php`` flag ``--parameter-stats`` counts how many times a parameter name occurs in the
773codebase.
774
775A JSON object is displayed, containing the parameter names and the number of their occurrences in
776descending order.
777