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