diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml
index ffb45c9a20cd..e8e615fd5288 100644
--- a/.github/workflows/docs.yml
+++ b/.github/workflows/docs.yml
@@ -22,6 +22,8 @@ jobs:
run: pip install -r docs/requirements.txt
- name: Check formatting
run: make -C docs check-formatting
+ - name: Build
+ run: make -C docs html
- name: Publish
if: github.event_name == 'push'
uses: sphinx-notes/pages@v3
diff --git a/docs-old/output-api.md b/docs-old/output-api.md
deleted file mode 100644
index 67bdfa3668dd..000000000000
--- a/docs-old/output-api.md
+++ /dev/null
@@ -1,136 +0,0 @@
-# API adjustment to the old output control code
-
-Everything now resides beneath the php_output namespace, and there's an API call
-for every output handler op.
-
- Checking output control layers status:
- // Using OG()
- php_output_get_status();
-
- Starting the default output handler:
- // php_start_ob_buffer(NULL, 0, 1);
- php_output_start_default();
-
- Starting an user handler by zval:
- // php_start_ob_buffer(zhandler, chunk_size, erase);
- php_output_start_user(zhandler, chunk_size, flags);
-
- Starting an internal handler without context:
- // php_ob_set_internal_handler(my_php_output_handler_func_t, buffer_size, "output handler name", erase);
- php_output_start_internal(handler_name, handler_name_len, my_php_output_handler_func_t, chunk_size, flags);
-
- Starting an internal handler with context:
- // not possible with old API
- php_output_handler *h;
- h = php_output_handler_create_internal(handler_name, handler_name_len, my_php_output_handler_context_func_t, chunk_size, flags);
- php_output_handler_set_context(h, my_context, my_context_dtor);
- php_output_handler_start(h);
-
- Testing whether a certain output handler has already been started:
- // php_ob_handler_used("output handler name");
- php_output_handler_started(handler_name, handler_name_len);
-
- Flushing one output buffer:
- // php_end_ob_buffer(1, 1);
- php_output_flush();
-
- Flushing all output buffers:
- // not possible with old API
- php_output_flush_all();
-
- Cleaning one output buffer:
- // php_ob_end_buffer(0, 1);
- php_output_clean();
-
- Cleaning all output buffers:
- // not possible with old API
- php_output_clean_all();
-
- Discarding one output buffer:
- // php_ob_end_buffer(0, 0);
- php_output_discard();
-
- Discarding all output buffers:
- // php_ob_end_buffers(0);
- php_output_discard_all();
-
- Stopping (and dropping) one output buffer:
- // php_ob_end_buffer(1, 0)
- php_output_end();
-
- Stopping (and dropping) all output buffers:
- // php_ob_end_buffers(1, 0);
- php_output_end_all();
-
- Retrieving output buffers contents:
- // php_ob_get_buffer(zstring);
- php_output_get_contents(zstring);
-
- Retrieving output buffers length:
- // php_ob_get_length(zlength);
- php_output_get_length(zlength);
-
- Retrieving output buffering level:
- // OG(nesting_level);
- php_output_get_level();
-
- Issue a warning because of an output handler conflict:
- // php_ob_init_conflict("to be started handler name", "to be tested if already started handler name");
- php_output_handler_conflict(new_handler_name, new_handler_name_len, set_handler_name, set_handler_name_len);
-
- Registering a conflict checking function, which will be checked prior starting the handler:
- // not possible with old API, unless hardcoding into output.c
- php_output_handler_conflict_register(handler_name, handler_name_len, my_php_output_handler_conflict_check_t);
-
- Registering a reverse conflict checking function, which will be checked prior starting the specified foreign handler:
- // not possible with old API
- php_output_handler_reverse_conflict_register(foreign_handler_name, foreign_handler_name_len, my_php_output_handler_conflict_check_t);
-
- Facilitating a context from within an output handler callable with ob_start():
- // not possible with old API
- php_output_handler_hook(PHP_OUTPUT_HANDLER_HOOK_GET_OPAQ, (void *) &custom_ctx_ptr_ptr);
-
- Disabling of the output handler by itself:
- //not possible with old API
- php_output_handler_hook(PHP_OUTPUT_HANDLER_HOOK_DISABLE, NULL);
-
- Marking an output handler immutable by itself because of irreversibility of its operation:
- // not possible with old API
- php_output_handler_hook(PHP_OUTPUT_HANDLER_HOOK_IMMUTABLE, NULL);
-
- Restarting the output handler because of a CLEAN operation:
- // not possible with old API
- if (flags & PHP_OUTPUT_HANDLER_CLEAN) { ... }
-
- Recognizing by the output handler itself if it gets discarded:
- // not possible with old API
- if ((flags & PHP_OUTPUT_HANDLER_CLEAN) && (flags & PHP_OUTPUT_HANDLER_FINAL)) { ... }
-
-## Output handler hooks
-
-The output handler can change its abilities at runtime. For example, the gz handler can
-remove the CLEANABLE and REMOVABLE bits when the first output has passed through it;
-or handlers implemented in C to be used with ob_start() can contain a non-global
-context:
-
- PHP_OUTPUT_HANDLER_HOOK_GET_OPAQ
- pass a void*** pointer as second arg to receive the address of a pointer
- pointer to the opaque field of the output handler context
- PHP_OUTPUT_HANDLER_HOOK_GET_FLAGS
- pass a int* pointer as second arg to receive the flags set for the output handler
- PHP_OUTPUT_HANDLER_HOOK_GET_LEVEL
- pass a int* pointer as second arg to receive the level of this output handler
- (starts with 0)
- PHP_OUTPUT_HANDLER_HOOK_IMMUTABLE
- the second arg is ignored; marks the output handler to be neither cleanable
- nor removable
- PHP_OUTPUT_HANDLER_HOOK_DISABLE
- the second arg is ignored; marks the output handler as disabled
-
-## Open questions
-
-* Should the userland API be adjusted and unified?
-
-Many bits of the manual (and very first implementation) do not comply with the
-behaviour of the current (to be obsoleted) code, thus should the manual or the
-behaviour be adjusted?
diff --git a/docs/Makefile b/docs/Makefile
index 40f8dbfb5ed2..0824dc428092 100644
--- a/docs/Makefile
+++ b/docs/Makefile
@@ -4,33 +4,29 @@
# If people set these on the make command line, use 'em
SPHINXBUILD ?= sphinx-build
+MDFORMAT ?= mdformat
SOURCEDIR = source
BUILDDIR = build
-RSTFMT = rstfmt
-RSTFMTFLAGS = -w 100
rwildcard = $(foreach d,$(wildcard $(1:=/*)),$(call rwildcard,$d,$2) $(filter $(subst *,%,$2),$d))
-FILES = $(call rwildcard,$(SOURCEDIR),*.rst)
+MARKDOWN_FILES = $(call rwildcard,$(SOURCEDIR),*.md) $(call rwildcard,$(SOURCEDIR),*.rst)
all : html
-.PHONY : check-formatting clean html preflight
+.PHONY : check-formatting clean format html
.SUFFIXES : # Disable legacy behavior
check-formatting :
- $(RSTFMT) $(RSTFMTFLAGS) --check $(SOURCEDIR)
+ $(MDFORMAT) --check $(MARKDOWN_FILES)
clean :
- rm -rf -- $(wildcard $(SOURCEDIR)/.~ $(BUILDDIR))
+ rm -rf -- $(BUILDDIR)
-html : preflight
+format :
+ $(MDFORMAT) $(MARKDOWN_FILES)
+
+html :
$(SPHINXBUILD) -M $@ $(SOURCEDIR) $(BUILDDIR)
@printf 'Browse the \e]8;;%s\e\\%s\e]8;;\e\\.\n' \
"file://$(abspath $(BUILDDIR))/$@/index.$@" "php-src html docs locally"
-
-preflight : $(SOURCEDIR)/.~
-
-$(SOURCEDIR)/.~ : $(FILES)
- $(RSTFMT) $(RSTFMTFLAGS) $?
- touch $@
diff --git a/docs/README.md b/docs/README.md
index 3c0d7ddd2bcc..2d4328c95b67 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -27,12 +27,11 @@ your browser.
## Formatting
-The files in this documentation are formatted using the
-[``rstfmt``](https://github.com/dzhu/rstfmt) tool.
+The files in this documentation are formatted using
+[mdformat](https://mdformat.readthedocs.io/) with its
+[MyST plugin](https://github.com/executablebooks/mdformat-myst).
```bash
-rstfmt -w 100 source
+make format
+make check-formatting
```
-
-This tool is not perfect. It breaks on custom directives, so we might switch to
-either a fork or something else in the future.
diff --git a/docs/requirements.txt b/docs/requirements.txt
index ca19fe15c2e0..026f279a0705 100644
--- a/docs/requirements.txt
+++ b/docs/requirements.txt
@@ -1,4 +1,6 @@
Sphinx
+mdformat==1.0.0
+mdformat-myst==0.3.0
+myst-parser>=5.1
sphinx-design
sphinxawesome-theme
-rstfmt
diff --git a/docs/source/_templates/redirect.html b/docs/source/_templates/redirect.html
new file mode 100644
index 000000000000..b7a37e55b720
--- /dev/null
+++ b/docs/source/_templates/redirect.html
@@ -0,0 +1,15 @@
+
+
+
+
+
diff --git a/docs/source/conf.py b/docs/source/conf.py
index f28102206a9f..28ee3caee85e 100644
--- a/docs/source/conf.py
+++ b/docs/source/conf.py
@@ -15,9 +15,18 @@
project = 'php-src docs'
author = 'The PHP Group'
extensions = [
+ 'myst_parser',
'sphinx_design',
'sphinx.ext.autosectionlabel',
]
+exclude_patterns = ['**/*TODO.md']
+myst_enable_extensions = [
+ 'alert',
+ 'gfm_autolink',
+ 'strikethrough',
+ 'tasklist',
+]
+myst_heading_anchors = 6
templates_path = ['_templates']
html_theme = 'sphinxawesome_theme'
html_static_path = ['_static']
@@ -58,3 +67,17 @@
)
html_theme_options = asdict(theme_options)
pygments_style = 'sphinx'
+
+
+redirects = {
+ 'core/data-structures/reference-counting': '../memory-management/reference-counting.html',
+}
+
+
+def generate_redirects(_app):
+ for source, target in redirects.items():
+ yield source, {'redirect_url': target}, 'redirect.html'
+
+
+def setup(app):
+ app.connect('html-collect-pages', generate_redirects)
diff --git a/docs/source/core/TODO.md b/docs/source/core/TODO.md
new file mode 100644
index 000000000000..9cf1af025b26
--- /dev/null
+++ b/docs/source/core/TODO.md
@@ -0,0 +1,8 @@
+Core TODO
+
+Pages to add:
+
+- Parser and AST: grammar generation, AST representation, compilation boundaries,
+ and important extension points.
+- Virtual Machine: opcode execution, operands, call frames, and VM variants.
+- Object Handlers: handler contracts, object storage, and common ownership traps.
diff --git a/docs/source/core/data-structures/TODO.md b/docs/source/core/data-structures/TODO.md
new file mode 100644
index 000000000000..b2ee1dfc2153
--- /dev/null
+++ b/docs/source/core/data-structures/TODO.md
@@ -0,0 +1,4 @@
+Data Structures TODO
+
+- Add a HashTable page covering ownership, iteration, mutation, and common APIs.
+- Expand the zval macro table and document the remaining internal zval types.
diff --git a/docs/source/core/data-structures/index.md b/docs/source/core/data-structures/index.md
new file mode 100644
index 000000000000..08dd9e4adf65
--- /dev/null
+++ b/docs/source/core/data-structures/index.md
@@ -0,0 +1,12 @@
+# Data Structures
+
+```{toctree}
+---
+hidden: true
+---
+zval
+zend_string
+zend_constant
+```
+
+This section provides an overview of the core data structures used in php-src.
diff --git a/docs/source/core/data-structures/index.rst b/docs/source/core/data-structures/index.rst
deleted file mode 100644
index 8fcb860f686b..000000000000
--- a/docs/source/core/data-structures/index.rst
+++ /dev/null
@@ -1,13 +0,0 @@
-#################
- Data structures
-#################
-
-.. toctree::
- :hidden:
-
- zval
- reference-counting
- zend_string
- zend_constant
-
-This section provides an overview of the core data structures used in php-src.
diff --git a/docs/source/core/data-structures/reference-counting.rst b/docs/source/core/data-structures/reference-counting.rst
deleted file mode 100644
index 6895d5d682a8..000000000000
--- a/docs/source/core/data-structures/reference-counting.rst
+++ /dev/null
@@ -1,228 +0,0 @@
-####################
- Reference counting
-####################
-
-In languages like C, when you need memory for storing data for an indefinite period of time or in a
-large amount, you call ``malloc`` and ``free`` to acquire and release blocks of memory of some size.
-This sounds simple on the surface but turns out to be quite tricky, mainly because the data may not
-be freed for as long as it is used anywhere in the program. Sometimes this makes it unclear who is
-responsible for freeing the memory, and when to do so. Failure to handle this correctly may result
-in a use-after-free, double-free, or memory leak.
-
-In PHP you usually do not need to think about memory management. The engine takes care of allocating
-and freeing memory for you by tracking which values are no longer needed. It does this by assigning
-a reference count to each allocated value, often abbreviated as refcount or RC. Whenever a reference
-to a value is passed somewhere else, its reference count is increased to indicate the value is now
-used by another party. When the party no longer needs the value, it is responsible for decreasing
-the reference count. Once the reference count reaches zero, we know the value is no longer needed
-anywhere, and that it may be freed.
-
-.. code:: php
-
- $a = new stdClass; // RC 1
- $b = $a; // RC 2
- unset($a); // RC 1
- unset($b); // RC 0, free
-
-Reference counting is needed for types that store auxiliary data, which are the following:
-
-- Strings
-- Arrays
-- Objects
-- References
-- Resources
-
-These are either reference types (objects, references and resources) or they are large types that
-don't fit in a single ``zend_value`` directly (strings, arrays). Simpler types either don't store a
-value at all (``null``, ``false``, ``true``) or their value is small enough to fit directly in
-``zend_value`` (``int``, ``float``).
-
-All of the reference counted types share a common initial struct sequence.
-
-.. code:: c
-
- typedef struct _zend_refcounted_h {
- uint32_t refcount; /* reference counter 32-bit */
- union {
- uint32_t type_info;
- } u;
- } zend_refcounted_h;
-
- struct _zend_string {
- zend_refcounted_h gc;
- // ...
- };
-
- struct _zend_array {
- zend_refcounted_h gc;
- // ...
- };
-
-The ``zend_refcounted_h`` struct is simple. It contains the reference count, and a ``type_info``
-field that repeats some of the type information that is also stored in the ``zval``, for situations
-where we're not dealing with a ``zval`` directly. It also stores some additional fields, described
-under `GC flags`_.
-
-********
- Macros
-********
-
-As with ``zval``, ``zend_refcounted_h`` members should not be accessed directly. Instead, you should
-use the provided macros. There are macros that work with reference counted types directly, prefixed
-with ``GC_``, or macros that work on ``zval`` values, usually prefixed with ``Z_``. Unfortunately,
-naming is not always consistent.
-
-.. list-table:: ``zval`` macros
- :header-rows: 1
-
- - - Macro
- - Non-RC [#non-rc]_
- - Description
-
- - - ``Z_REFCOUNT[_P]``
- - No
- - Returns the reference count.
-
- - - ``Z_ADDREF[_P]``
- - No
- - Increases the reference count.
-
- - - ``Z_TRY_ADDREF[_P]``
- - Yes
- - Increases the reference count. May be called on any ``zval``.
-
- - - ``zval_ptr_dtor``
- - Yes
- - Decreases the reference count and frees the value if the reference count reaches zero.
-
-.. [#non-rc]
-
- Whether the macro works with non-reference counted types. If it does, the operation is usually a
- no-op. If it does not, using the macro on these values is undefined behavior.
-
-.. list-table:: ``zend_refcounted_h`` macros
- :header-rows: 1
-
- - - Macro
- - Immutable [#immutable]_
- - Description
-
- - - ``GC_REFCOUNT[_P]``
- - Yes
- - Returns the reference count.
-
- - - ``GC_ADDREF[_P]``
- - No
- - Increases the reference count.
-
- - - ``GC_TRY_ADDREF[_P]``
- - Yes
- - Increases the reference count.
-
- - - ``GC_DTOR[_P]``
- - Yes
- - Decreases the reference count and frees the value if the reference count reaches zero.
-
-.. [#immutable]
-
- Whether the macro works with immutable types, described under `Immutable reference counted types`_.
-
-************
- Separation
-************
-
-PHP has value and reference types. Reference types are types that are shared through a reference, a
-"pointer" to the value, rather than the value itself. Modifying such a value in one place changes it
-for all of its observers. For example, writing to a property changes the property in every place the
-object is referenced. Value types, on the other hand, are copied when passed to another party.
-Modifying the original value does not affect the copy, and vice versa.
-
-In PHP, arrays and strings are value types. Since they are also reference counted types, this
-requires some special care when modifying values. In particular, we need to make sure that modifying
-the value is not observable from other places. Modifying a value with RC 1 is unproblematic, since
-we are the values sole owner. However, if the value has a reference count of >1, we need to create a
-fresh copy before modifying it. This process is called separation or CoW (copy on write).
-
-.. code:: php
-
- $a = [1, 2, 3]; // RC 1
- $b = $a; // RC 2
- $b[] = 4; // Separation, $a RC 1, $b RC 1
- var_dump($a); // [1, 2, 3]
- var_dump($b); // [1, 2, 3, 4]
-
-***********************************
- Immutable reference counted types
-***********************************
-
-Sometimes, even a reference counted type is not reference counted. When PHP runs in a multi-process
-or multi-threaded environment with opcache enabled, it shares some common values between processes
-or threads to reduce memory consumption. As you may know, sharing memory between processes or
-threads can be tricky and requires special care when modifying values. In particular, modification
-usually requires exclusive access to the memory so that the other processes or threads wait until
-the value is done being updated. In this case, this synchronization is avoided by making the value
-immutable and never modifying the reference count. Such values will receive the ``GC_IMMUTABLE``
-flag in their ``gc->u.type_info`` field.
-
-Some macros like ``GC_TRY_ADDREF`` will guard against immutable values. You should not use immutable
-values on some macros, like ``GC_ADDREF``. This will result in undefined behavior, because the macro
-will not check whether the value is immutable before performing the reference count modifications.
-You may execute PHP with the ``-d opcache.protect_memory=1`` flag to mark the shared memory as
-read-only and trigger a hardware exception if the code accidentally attempts to modify it.
-
-*****************
- Cycle collector
-*****************
-
-Sometimes, reference counting is not enough. Consider the following example:
-
-.. code:: php
-
- $a = new stdClass;
- $b = new stdClass;
- $a->b = $b;
- $b->a = $a;
- unset($a);
- unset($b);
-
-When this code finishes, the reference count of both instances of ``stdClass`` will still be 1, as
-they reference each other. This is called a reference cycle.
-
-PHP implements a cycle collector that detects such cycles and frees values that are only reachable
-through their own references. The cycle collector will record values that may be involved in a
-cycle, and run when this buffer becomes full. It is also possible to invoke it explicitly by calling
-the ``gc_collect_cycles()`` function. The cycle collectors design is described in the `Cycle
-collector `_ chapter.
-
-**********
- GC flags
-**********
-
-.. code:: c
-
- /* zval_gc_flags(zval.value->gc.u.type_info) (common flags) */
- #define GC_NOT_COLLECTABLE (1<<4)
- #define GC_PROTECTED (1<<5) /* used for recursion detection */
- #define GC_IMMUTABLE (1<<6) /* can't be changed in place */
- #define GC_PERSISTENT (1<<7) /* allocated using malloc */
- #define GC_PERSISTENT_LOCAL (1<<8) /* persistent, but thread-local */
-
-The ``GC_NOT_COLLECTABLE`` flag indicates that the value may not be involved in a reference cycle.
-This allows for a fast way to detect values that don't need to be added to the cycle collector
-buffer. Only arrays and objects may actually be involved in reference cycles.
-
-The ``GC_PROTECTED`` flag is used to protect against recursion in various internal functions. For
-example, ``var_dump`` recursively prints the contents of values, and marks visited values with the
-``GC_PROTECTED`` flag. If the value is recursive, it prevents the same value from being visited
-again.
-
-``GC_IMMUTABLE`` has been discussed in `Immutable reference counted types`_.
-
-The ``GC_PERSISTENT`` flag indicates that the value was allocated using ``malloc``, instead of PHPs
-own allocator. Usually, such values are alive for the entire lifetime of the process, instead of
-being freed at the end of the request. See the `Zend allocator `_ chapter for more
-information.
-
-The ``GC_PERSISTENT_LOCAL`` flag indicates that a ``GC_PERSISTENT`` value is only accessible in one
-thread, and is thus still safe to modify. This flag is only used in debug builds to satisfy an
-``assert``.
diff --git a/docs/source/core/data-structures/zend_constant.md b/docs/source/core/data-structures/zend_constant.md
new file mode 100644
index 000000000000..fedcad44c359
--- /dev/null
+++ b/docs/source/core/data-structures/zend_constant.md
@@ -0,0 +1,63 @@
+# zend_constant
+
+PHP constants (referring to non-class constants) are stored in a dedicated structure
+`zend_constant`, which holds both the value of the constant and details for using it.
+
+## definition
+
+```c
+typedef struct _zend_constant {
+ zval value;
+ zend_string *name;
+ zend_string *filename;
+ HashTable *attributes;
+} zend_constant;
+```
+
+The `value` field stores both the value itself and some metadata. The `name` and `filename`
+store the name of the constant and the name of the file in which it was defined. The `attributes`
+field stores the attributes applied to the constant.
+
+## value
+
+The value of the constant is stored in the {doc}`./zval` `value`. However, since the `zval`
+structure has extra space, for constants this is used to store both the number of the module that
+the constant was defined in, and a combination of the flags that affect the usage of the constant.
+
+This extra information is placed in the `uint32_t` field `value.u2.constant_flags`.
+
+The bottom 16 bits are used to hold flags about the constant
+
+```c
+#define CONST_PERSISTENT (1<<0) /* Persistent */
+#define CONST_NO_FILE_CACHE (1<<1) /* Can't be saved in file cache */
+#define CONST_DEPRECATED (1<<2) /* Deprecated */
+#define CONST_OWNED (1<<3) /* constant should be destroyed together
+ with class */
+```
+
+These bottom 16 bits can be accessed with the `ZEND_CONSTANT_FLAGS()` macro, which is given a
+`zend_constant` pointer as a parameter.
+
+On the other hand, the top 16 bits are used to store the number of the PHP module that registered
+the constant. For constants defined by the user, the module number stored will be
+`PHP_USER_CONSTANT`. This module number can be accessed with the `ZEND_CONSTANT_MODULE_NUMBER()`
+macro, which is likewise given a `zend_constant` pointer as a parameter.
+
+## name
+
+The `name` holds a {doc}`zend_string` with the name of the constant, to allow searching for
+constants that have already been defined. This string is released when the constant itself is freed.
+
+## filename
+
+The `filename` holds another `zend_string` with the name of the file in which the constant was
+defined, or `NULL` if not defined userland code. This field provides the foundation for the PHP
+method `ReflectionConstant::getFileName()`.
+
+## attributes
+
+The `attributes` holds a `HashTable` (essentially an array) with the details of the attributes
+that were applied to the constant. Note that attributes can only be added to constants declared at
+compile time via `const`, e.g. `const EXAMPLE = 123`, not those declared at runtime, e.g.
+`define( 'EXAMPLE', 123 );`.
diff --git a/docs/source/core/data-structures/zend_constant.rst b/docs/source/core/data-structures/zend_constant.rst
deleted file mode 100644
index a5e85dc78638..000000000000
--- a/docs/source/core/data-structures/zend_constant.rst
+++ /dev/null
@@ -1,75 +0,0 @@
-###############
- zend_constant
-###############
-
-PHP constants (referring to non-class constants) are stored in a dedicated structure
-``zend_constant``, which holds both the value of the constant and details for using it.
-
-************
- definition
-************
-
-.. code:: c
-
- typedef struct _zend_constant {
- zval value;
- zend_string *name;
- zend_string *filename;
- HashTable *attributes;
- } zend_constant;
-
-The ``value`` field stores both the value itself and some metadata. The ``name`` and ``filename``
-store the name of the constant and the name of the file in which it was defined. The ``attributes``
-field stores the attributes applied to the constant.
-
-*******
- value
-*******
-
-The value of the constant is stored in the :doc:`./zval` ``value``. However, since the ``zval``
-structure has extra space, for constants this is used to store both the number of the module that
-the constant was defined in, and a combination of the flags that affect the usage of the constant.
-
-This extra information is placed in the ``uint32_t`` field ``value.u2.constant_flags``.
-
-The bottom 16 bits are used to hold flags about the constant
-
-.. code:: c
-
- #define CONST_PERSISTENT (1<<0) /* Persistent */
- #define CONST_NO_FILE_CACHE (1<<1) /* Can't be saved in file cache */
- #define CONST_DEPRECATED (1<<2) /* Deprecated */
- #define CONST_OWNED (1<<3) /* constant should be destroyed together
- with class */
-
-These bottom 16 bits can be accessed with the ``ZEND_CONSTANT_FLAGS()`` macro, which is given a
-``zend_constant`` pointer as a parameter.
-
-On the other hand, the top 16 bits are used to store the number of the PHP module that registered
-the constant. For constants defined by the user, the module number stored will be
-``PHP_USER_CONSTANT``. This module number can be accessed with the ``ZEND_CONSTANT_MODULE_NUMBER()``
-macro, which is likewise given a ``zend_constant`` pointer as a parameter.
-
-******
- name
-******
-
-The ``name`` holds a :doc:`zend_string` with the name of the constant, to allow searching for
-constants that have already been defined. This string is released when the constant itself is freed.
-
-**********
- filename
-**********
-
-The ``filename`` holds another ``zend_string`` with the name of the file in which the constant was
-defined, or ``NULL`` if not defined userland code. This field provides the foundation for the PHP
-method ``ReflectionConstant::getFileName()``.
-
-************
- attributes
-************
-
-The ``attributes`` holds a ``HashTable`` (essentially an array) with the details of the attributes
-that were applied to the constant. Note that attributes can only be added to constants declared at
-compile time via ``const``, e.g. ``const EXAMPLE = 123``, not those declared at runtime, e.g.
-``define( 'EXAMPLE', 123 );``.
diff --git a/docs/source/core/data-structures/zend_string.md b/docs/source/core/data-structures/zend_string.md
new file mode 100644
index 000000000000..e5dae0bd931c
--- /dev/null
+++ b/docs/source/core/data-structures/zend_string.md
@@ -0,0 +1,135 @@
+# zend_string
+
+In C, strings are represented as sequential lists of characters, `char*` or `char[]`. The end of
+the string is usually indicated by the special NUL character, `'\0'`. This comes with a few
+significant downsides:
+
+- Calculating the length of the string is expensive, as it requires walking the entire string to
+ look for the terminating NUL character.
+- The string may not contain the NUL character itself.
+- It is easy to run into buffer overflows if the NUL byte is accidentally missing.
+
+php-src uses the `zend_string` struct as an abstraction over `char*`, which explicitly stores
+the strings length, along with some other fields. It looks as follows:
+
+```c
+struct _zend_string {
+ zend_refcounted_h gc;
+ zend_ulong h; /* hash value */
+ size_t len;
+ char val[1];
+};
+```
+
+The `gc` field is used for {doc}`../memory-management/reference-counting`. The `h` field contains a hash value,
+which is used for hash table lookups. The `len` field stores the length of the string in bytes, and
+the `val` field contains the actual string data.
+
+You may wonder why the `val` field is declared as `char val[1]`. This is called the [struct
+hack](https://www.geeksforgeeks.org/struct-hack/) in C. It is used to create structs with a flexible size, namely by allowing the last element
+to be expanded arbitrarily. In this case, the size of `zend_string` depends on the string's
+length, which is determined at runtime (see `_ZSTR_STRUCT_SIZE`). When allocating the string, we
+append enough bytes to the allocation to hold the strings content.
+
+Here's a basic example of how to use `zend_string`:
+
+```c
+// Allocate the string.
+zend_string *string = ZSTR_INIT_LITERAL("Hello world!", /* persistent */ false);
+// Write it to the output buffer.
+zend_write(ZSTR_VAL(string), ZSTR_LEN(string));
+// Decrease the reference count and free it if necessary.
+zend_string_release(string);
+```
+
+`ZSTR_INIT_LITERAL` creates a `zend_string` from a string literal. It is just a wrapper around
+`zend_string_init(char *string, size_t length, bool persistent)` that provides the length of the
+string at compile time. The `persistent` parameter indicates whether the string is allocated using
+`malloc` (`persistent == true`) or `emalloc`, PHP's custom allocator (`persistent == false`) that is emptied after each request.
+
+When you're done using the string, you must call `zend_string_release`, or the memory will leak.
+`zend_string_release` will automatically call `malloc` or `emalloc`, depending on how the
+string was allocated. After releasing the string, you must not access any of its fields anymore, as
+it may have been freed if you were its last user.
+
+## API
+
+The string API is defined in `Zend/zend_string.h`. It provides a number of functions for creating
+new strings.
+
+**`zend_string` creation**
+
+| Function/Macro [^persistent] | Description |
+| ------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| `ZSTR_INIT_LITERAL(s, p)` | Creates a new string from a string literal. |
+| `zend_string_init(s, l, p)` | Creates a new string from a character buffer. |
+| `zend_string_alloc(l, p)` | Creates a new string of a given length without initializing its content. |
+| `zend_string_concat2(s1, l1, s2, l2)` | Creates a non-persistent string by concatenating two character buffers. |
+| `zend_string_concat3(...)` | Same as `zend_string_concat2`, but for three character buffers. |
+| `ZSTR_EMPTY_ALLOC()` | Gets an immutable, empty string. This does not allocate memory. |
+| `ZSTR_CHAR(char)` | Gets an immutable, single-character string. This does not allocate memory. |
+| `ZSTR_KNOWN(ZEND_STR_const)` | Gets an immutable, predefined string. Used for string common within PHP itself, e.g. `"class"`. See `ZEND_KNOWN_STRINGS` in `Zend/zend_string.h`. This does not allocate memory. |
+
+As per php-src fashion, you are not supposed to access the `zend_string` fields directly. Instead,
+use the following macros. There are macros for both `zend_string` and `zvals` known to contain
+strings.
+
+**Accessor macros**
+
+| `zend_string` | `zval` | Description |
+| ------------- | --------------- | ------------------------------------------------------------------------------------ |
+| `ZSTR_LEN` | `Z_STRLEN[_P]` | Returns the length of the string in bytes. |
+| `ZSTR_VAL` | `Z_STRVAL[_P]` | Returns the string data as a `char*`. |
+| `ZSTR_HASH` | `Z_STRHASH[_P]` | Computes the string hash if it hasn't already been, and returns it. |
+| `ZSTR_H` | - | Returns the string hash. This macro assumes that the hash has already been computed. |
+
+**Reference counting macros**
+
+| Macro | Description |
+| ------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| `zend_string_copy(s)` | Increases the reference count and returns the same string. The reference count is not increased if the string is interned. |
+| `zend_string_release(s)` | Decreases the reference count and frees the string if it goes to 0. |
+| `zend_string_dup(s, p)` | Creates a true copy of the string in a new allocation, except if the string is interned. |
+| `zend_string_separate(s)` | Duplicates the string if the reference count is greater than 1. See {doc}`../memory-management/reference-counting` for details. |
+| `zend_string_realloc(s, l, p)` | Changes the size of the string. If the string has a reference count greater than 1 or if the string is interned, a new string is created. You must always use the return value of this function, as the original array may have been moved to a new location in memory. |
+
+There are various functions to compare strings. The `zend_string_equals` function compares two
+strings in full, while `zend_string_starts_with` checks whether the first argument starts with the
+second. There are variations for `_ci` and `_literal`, i.e. case-insensitive comparison and
+literal strings, respectively. We won't go over all variations here, as they are straightforward to
+use.
+
+## Interned strings
+
+Programs use some strings many times. For example, if your program declares a class called
+`MyClass`, it would be wasteful to allocate a new string `"MyClass"` every time it is referenced
+within your program. Instead, when repeated strings are expected, php-src uses a technique called
+string interning. Essentially, this is just a simple `HashTable` where existing interned strings are
+stored. When creating a new interned string, php-src first checks the interned string buffer. If it
+finds it there, it can return a pointer to the existing string. If it doesn't, it allocates a new
+string and adds it to the buffer.
+
+```c
+zend_string *str1 = zend_new_interned_string(
+ ZSTR_INIT_LITERAL("MyClass", /* persistent */ false));
+
+// In some other place entirely.
+zend_string *str2 = zend_new_interned_string(
+ ZSTR_INIT_LITERAL("MyClass", /* persistent */ false));
+
+assert(ZSTR_IS_INTERNED(str1));
+assert(ZSTR_IS_INTERNED(str2));
+assert(str1 == str2);
+```
+
+Interned strings are *not* reference counted, as they are expected to live for the entire request,
+or longer.
+
+With opcache, this goes one step further by sharing strings across different processes. For example,
+if you're using php-fpm with 8 workers, all workers will share the same interned strings buffer. It
+gets a bit more complicated. During requests, no interned strings are actually created. Instead,
+this is delayed until the script is persisted to shared memory. This means that
+`zend_new_interned_string` may not actually return an interned string if opcache is enabled.
+Usually you don't have to worry about this.
+
+[^persistent]: `s` = `zend_string`, `l` = `length`, `p` = `persistent`.
diff --git a/docs/source/core/data-structures/zend_string.rst b/docs/source/core/data-structures/zend_string.rst
deleted file mode 100644
index d6b20a49a74c..000000000000
--- a/docs/source/core/data-structures/zend_string.rst
+++ /dev/null
@@ -1,196 +0,0 @@
-#############
- zend_string
-#############
-
-In C, strings are represented as sequential lists of characters, ``char*`` or ``char[]``. The end of
-the string is usually indicated by the special NUL character, ``'\0'``. This comes with a few
-significant downsides:
-
-- Calculating the length of the string is expensive, as it requires walking the entire string to
- look for the terminating NUL character.
-- The string may not contain the NUL character itself.
-- It is easy to run into buffer overflows if the NUL byte is accidentally missing.
-
-php-src uses the ``zend_string`` struct as an abstraction over ``char*``, which explicitly stores
-the strings length, along with some other fields. It looks as follows:
-
-.. code:: c
-
- struct _zend_string {
- zend_refcounted_h gc;
- zend_ulong h; /* hash value */
- size_t len;
- char val[1];
- };
-
-The ``gc`` field is used for :doc:`./reference-counting`. The ``h`` field contains a hash value,
-which is used for `hash table `__ lookups. The ``len`` field stores the length of the string
-in bytes, and the ``val`` field contains the actual string data.
-
-You may wonder why the ``val`` field is declared as ``char val[1]``. This is called the `struct
-hack`_ in C. It is used to create structs with a flexible size, namely by allowing the last element
-to be expanded arbitrarily. In this case, the size of ``zend_string`` depends on the string's
-length, which is determined at runtime (see ``_ZSTR_STRUCT_SIZE``). When allocating the string, we
-append enough bytes to the allocation to hold the strings content.
-
-.. _struct hack: https://www.geeksforgeeks.org/struct-hack/
-
-Here's a basic example of how to use ``zend_string``:
-
-.. code:: c
-
- // Allocate the string.
- zend_string *string = ZSTR_INIT_LITERAL("Hello world!", /* persistent */ false);
- // Write it to the output buffer.
- zend_write(ZSTR_VAL(string), ZSTR_LEN(string));
- // Decrease the reference count and free it if necessary.
- zend_string_release(string);
-
-``ZSTR_INIT_LITERAL`` creates a ``zend_string`` from a string literal. It is just a wrapper around
-``zend_string_init(char *string, size_t length, bool persistent)`` that provides the length of the
-string at compile time. The ``persistent`` parameter indicates whether the string is allocated using
-``malloc`` (``persistent == true``) or ``emalloc``, `PHPs custom allocator `__ (``persistent
-== false``) that is emptied after each request.
-
-When you're done using the string, you must call ``zend_string_release``, or the memory will leak.
-``zend_string_release`` will automatically call ``malloc`` or ``emalloc``, depending on how the
-string was allocated. After releasing the string, you must not access any of its fields anymore, as
-it may have been freed if you were its last user.
-
-*****
- API
-*****
-
-The string API is defined in ``Zend/zend_string.h``. It provides a number of functions for creating
-new strings.
-
-.. list-table:: ``zend_string`` creation
- :header-rows: 1
-
- - - Function/Macro [#persistent]_
- - Description
-
- - - ``ZSTR_INIT_LITERAL(s, p)``
- - Creates a new string from a string literal.
-
- - - ``zend_string_init(s, l, p)``
- - Creates a new string from a character buffer.
-
- - - ``zend_string_alloc(l, p)``
- - Creates a new string of a given length without initializing its content.
-
- - - ``zend_string_concat2(s1, l1, s2, l2)``
- - Creates a non-persistent string by concatenating two character buffers.
-
- - - ``zend_string_concat3(...)``
- - Same as ``zend_string_concat2``, but for three character buffers.
-
- - - ``ZSTR_EMPTY_ALLOC()``
- - Gets an immutable, empty string. This does not allocate memory.
-
- - - ``ZSTR_CHAR(char)``
- - Gets an immutable, single-character string. This does not allocate memory.
-
- - - ``ZSTR_KNOWN(ZEND_STR_const)``
-
- - Gets an immutable, predefined string. Used for string common within PHP itself, e.g.
- ``"class"``. See ``ZEND_KNOWN_STRINGS`` in ``Zend/zend_string.h``. This does not allocate
- memory.
-
-.. [#persistent]
-
- ``s`` = ``zend_string``, ``l`` = ``length``, ``p`` = ``persistent``.
-
-As per php-src fashion, you are not supposed to access the ``zend_string`` fields directly. Instead,
-use the following macros. There are macros for both ``zend_string`` and ``zvals`` known to contain
-strings.
-
-.. list-table:: Accessor macros
- :header-rows: 1
-
- - - ``zend_string``
- - ``zval``
- - Description
-
- - - ``ZSTR_LEN``
- - ``Z_STRLEN[_P]``
- - Returns the length of the string in bytes.
-
- - - ``ZSTR_VAL``
- - ``Z_STRVAL[_P]``
- - Returns the string data as a ``char*``.
-
- - - ``ZSTR_HASH``
- - ``Z_STRHASH[_P]``
- - Computes the string hash if it hasn't already been, and returns it.
-
- - - ``ZSTR_H``
- - \-
- - Returns the string hash. This macro assumes that the hash has already been computed.
-
-.. list-table:: Reference counting macros
- :header-rows: 1
-
- - - Macro
- - Description
-
- - - ``zend_string_copy(s)``
- - Increases the reference count and returns the same string. The reference count is not
- increased if the string is interned.
-
- - - ``zend_string_release(s)``
- - Decreases the reference count and frees the string if it goes to 0.
-
- - - ``zend_string_dup(s, p)``
- - Creates a true copy of the string in a new allocation, except if the string is interned.
-
- - - ``zend_string_separate(s)``
- - Duplicates the string if the reference count is greater than 1. See
- :doc:`./reference-counting` for details.
-
- - - ``zend_string_realloc(s, l, p)``
-
- - Changes the size of the string. If the string has a reference count greater than 1 or if
- the string is interned, a new string is created. You must always use the return value of
- this function, as the original array may have been moved to a new location in memory.
-
-There are various functions to compare strings. The ``zend_string_equals`` function compares two
-strings in full, while ``zend_string_starts_with`` checks whether the first argument starts with the
-second. There are variations for ``_ci`` and ``_literal``, i.e. case-insensitive comparison and
-literal strings, respectively. We won't go over all variations here, as they are straightforward to
-use.
-
-******************
- Interned strings
-******************
-
-Programs use some strings many times. For example, if your program declares a class called
-``MyClass``, it would be wasteful to allocate a new string ``"MyClass"`` every time it is referenced
-within your program. Instead, when repeated strings are expected, php-src uses a technique called
-string interning. Essentially, this is just a simple `HashTable `__ where existing interned
-strings are stored. When creating a new interned string, php-src first checks the interned string
-buffer. If it finds it there, it can return a pointer to the existing string. If it doesn't, it
-allocates a new string and adds it to the buffer.
-
-.. code:: c
-
- zend_string *str1 = zend_new_interned_string(
- ZSTR_INIT_LITERAL("MyClass", /* persistent */ false));
-
- // In some other place entirely.
- zend_string *str2 = zend_new_interned_string(
- ZSTR_INIT_LITERAL("MyClass", /* persistent */ false));
-
- assert(ZSTR_IS_INTERNED(str1));
- assert(ZSTR_IS_INTERNED(str2));
- assert(str1 == str2);
-
-Interned strings are *not* reference counted, as they are expected to live for the entire request,
-or longer.
-
-With opcache, this goes one step further by sharing strings across different processes. For example,
-if you're using php-fpm with 8 workers, all workers will share the same interned strings buffer. It
-gets a bit more complicated. During requests, no interned strings are actually created. Instead,
-this is delayed until the script is persisted to shared memory. This means that
-``zend_new_interned_string`` may not actually return an interned string if opcache is enabled.
-Usually you don't have to worry about this.
diff --git a/docs/source/core/data-structures/zval.md b/docs/source/core/data-structures/zval.md
new file mode 100644
index 000000000000..ad482cd180d2
--- /dev/null
+++ b/docs/source/core/data-structures/zval.md
@@ -0,0 +1,198 @@
+# zval
+
+PHP is a dynamic language. A variable can typically contain a value of any type, and the type of the
+variable may even change during the execution of the program. Under the hood, this is implemented
+through the `zval` struct. It is one of the most important data structures in php-src. It is
+implemented as a "tagged union", meaning it stores what type of value it contains, and the value
+itself. Let's look at the type first.
+
+## zval types
+
+```c
+#define IS_UNDEF 0 /* A variable that was never written to. */
+#define IS_NULL 1
+#define IS_FALSE 2
+#define IS_TRUE 3
+#define IS_LONG 4 /* An integer value. */
+#define IS_DOUBLE 5 /* A floating point value. */
+#define IS_STRING 6
+#define IS_ARRAY 7
+#define IS_OBJECT 8
+#define IS_RESOURCE 9
+#define IS_REFERENCE 10
+```
+
+These simple integer constants determine what value is currently stored in a variable. If you are a
+PHP developer, these types should sound fairly familiar. They are pretty much an exact reflection of
+the types you may use in regular PHP code. One small oddity is that `IS_FALSE` and `IS_TRUE` are
+implemented as separate types, instead of as a `IS_BOOL` type.
+
+Some of these types are self-contained, they don't store any auxiliary data. This includes
+`IS_UNDEF`, `IS_NULL`, `IS_FALSE` and `IS_TRUE`. For the rest of the types, we are going to
+require some additional memory to store the actual value of the variable.
+
+## zend_value
+
+```c
+typedef union _zend_value {
+ zend_long lval; /* long value, i.e. int. */
+ double dval; /* double value, i.e. float. */
+ zend_refcounted *counted;
+ zend_string *str;
+ zend_array *arr;
+ zend_object *obj;
+ zend_resource *res;
+ zend_reference *ref;
+ // Less important for now.
+ zend_ast_ref *ast;
+ zval *zv;
+ void *ptr;
+ zend_class_entry *ce;
+ zend_function *func;
+ struct {
+ uint32_t w1;
+ uint32_t w2;
+ } ww;
+} zend_value;
+```
+
+A C union is a data type that may store any one of its members at a time, by being (at least) as big
+as its biggest member. For example, `zend_value` may store the `lval` member, or the `dval`
+member, but never both at the same time. However, it doesn't know which member is being stored.
+Remembering this is our job, and that's exactly what the `IS_*` constants are for.
+
+The top members of `zend_value` mostly mirror the `IS_*` constants, with the exception of
+`counted`. `counted` polymorphically refers to any [reference-counted](../memory-management/reference-counting.md)
+value, including strings, arrays, objects, resources and references. `null` and `bool` are missing
+from `zend_value` because their types are self-contained.
+
+The rest of the fields aren't important for now.
+
+## zval
+
+Together, the value and the tag make up the `zval`, along with some other fields. It may look
+intimidating at first. We'll go over it step by step.
+
+```c
+typedef struct _zval_struct zval;
+
+struct _zval_struct {
+ zend_value value;
+ union {
+ uint32_t type_info;
+ struct {
+ ZEND_ENDIAN_LOHI_3(
+ uint8_t type, /* active type */
+ uint8_t type_flags,
+ union {
+ uint16_t extra; /* not further specified */
+ } u)
+ } v;
+ } u1;
+ union {
+ uint32_t next; /* hash collision chain */
+ uint32_t cache_slot; /* cache slot (for RECV_INIT) */
+ uint32_t opline_num; /* opline number (for FAST_CALL) */
+ uint32_t lineno; /* line number (for ast nodes) */
+ uint32_t num_args; /* arguments number for EX(This) */
+ uint32_t fe_pos; /* foreach position */
+ uint32_t fe_iter_idx; /* foreach iterator index */
+ uint32_t guard; /* recursion and single property guard */
+ uint32_t constant_flags; /* constant flags */
+ uint32_t extra; /* not further specified */
+ } u2;
+};
+```
+
+`zval.value` reserves space for the actual variable data, as discussed above.
+
+`zval.u1` stores the variable type, the given `IS_*` constant, along with some other flags. It's
+definition looks a bit complicated. You can think of the entire field as a 4 byte integer, split
+into 3 parts. `v.type` stores the actual variable type, `v.type_flags` is used for some
+[reference-counting](../memory-management/reference-counting.md) flags, and `v.u.extra` is pretty
+much unused.
+
+`zval.u2` defines some more storage for various contexts that is often unoccupied. It's there
+because the memory would otherwise be wasted due to padding, so we may as well make use of it. We'll
+go over the relevant ones in their corresponding chapters.
+
+## Macros
+
+The fields in `zval` should never be accessed directly. Instead, there are a plethora of macros to
+access them, concealing some of the implementation details of the `zval` struct. For many macros,
+there's a `_P`-suffixed variant that performs the same operation on a pointer to the given
+`zval`.
+
+**`zval` macros**
+
+| Macro | Description |
+| ----------------------- | --------------------------------------------------------------------------------------- |
+| `Z_TYPE[_P]` | Access the `zval.u1.v.type` part of the type flags, containing the `IS_*` type. |
+| `Z_LVAL[_P]` | Access the underlying `int` value. |
+| `Z_DVAL[_P]` | Access the underlying `float` value. |
+| `Z_STR[_P]` | Access the underlying `zend_string` pointer. |
+| `Z_STRVAL[_P]` | Access the strings raw `char *` pointer. |
+| `Z_STRLEN[_P]` | Access the strings length. |
+| `ZVAL_COPY_VALUE(t, s)` | Copy one `zval` to another, including type and value. |
+| `ZVAL_COPY(t, s)` | Same as `ZVAL_COPY_VALUE`, but if the value is reference counted, increase the counter. |
+
+## Other zval types
+
+`zval`s are sometimes used internally with types that don't exist in userland.
+
+```c
+#define IS_CONSTANT_AST 11
+#define IS_INDIRECT 12
+#define IS_PTR 13
+#define IS_ALIAS_PTR 14
+#define _IS_ERROR 15
+```
+
+`IS_CONSTANT_AST` is used to represent constant values (the right hand side of `const`,
+property/parameter initializers, etc.) before they are evaluated. The evaluation of a constant
+expression is not always possible during compilation, because they may contain references to values
+only available at runtime. Until that evaluation is possible, the constants contain the AST of the
+expression rather than the concrete values. When this flag is set, the `zval.value.ast` union member
+is set accordingly.
+
+`IS_INDIRECT` indicates that the `zval.value.zv` member is populated. This field stores a
+pointer to some other `zval`. This type is mainly used in two situations, namely for intermediate
+values between `FETCH` and `ASSIGN` instructions, and for the sharing of variables in the symbol
+table.
+
+`IS_PTR` is used for pointers to arbitrary data. Most commonly, this type is used internally for
+`HashTable`, as `HashTable` may only store `zval` values. For example, `EG(class_table)`
+represents the class table, which is a hash map of class names to the corresponding
+`zend_class_entry`, representing the class. The same goes for functions and many other data types.
+`IS_ALIAS_PTR` is used for class aliases registered via `class_alias`. Essentially, it just
+allows differencing between members in the class table that are aliases, or actual classes.
+Otherwise, it is essentially the same as `IS_PTR`. Arbitrary data is accessed through
+`zval.value.ptr`, and casted to the correct type depending on context. If `ptr` stores a class
+or function, the `zval.value.ce` or `zval.value.func` fields may be used, respectively.
+
+`_IS_ERROR` is used as an error value for some object handlers.
+
+```c
+/* Fake types used only for type hinting.
+ * These are allowed to overlap with the types below. */
+#define IS_CALLABLE 12
+#define IS_ITERABLE 13
+#define IS_VOID 14
+#define IS_STATIC 15
+#define IS_MIXED 16
+#define IS_NEVER 17
+
+/* used for casts */
+#define _IS_BOOL 18
+#define _IS_NUMBER 19
+```
+
+These flags are never actually stored in `zval.u1`. They are used for type hinting and in the
+object handler API.
+
+This only leaves the `zval.value.ww` field. In short, this field is used on 32-bit platforms when
+copying data from one `zval` to another. Normally, `zval.value.counted` is copied as a generic
+value, no matter what the actual underlying type is. `zend_value` always consists of 8 bytes due
+to the `double` field. Pointers, however, consist only of 4. Because we would otherwise miss the
+other 4 bytes, they are copied manually using `z->value.ww.w2 = _w2;`. This happens in the
+`ZVAL_COPY_VALUE_EX` macro, you won't ever have to care about this.
diff --git a/docs/source/core/data-structures/zval.rst b/docs/source/core/data-structures/zval.rst
deleted file mode 100644
index 512abfdf2195..000000000000
--- a/docs/source/core/data-structures/zval.rst
+++ /dev/null
@@ -1,225 +0,0 @@
-######
- zval
-######
-
-PHP is a dynamic language. A variable can typically contain a value of any type, and the type of the
-variable may even change during the execution of the program. Under the hood, this is implemented
-through the ``zval`` struct. It is one of the most important data structures in php-src. It is
-implemented as a "tagged union", meaning it stores what type of value it contains, and the value
-itself. Let's look at the type first.
-
-************
- zval types
-************
-
-.. code:: c
-
- #define IS_UNDEF 0 /* A variable that was never written to. */
- #define IS_NULL 1
- #define IS_FALSE 2
- #define IS_TRUE 3
- #define IS_LONG 4 /* An integer value. */
- #define IS_DOUBLE 5 /* A floating point value. */
- #define IS_STRING 6
- #define IS_ARRAY 7
- #define IS_OBJECT 8
- #define IS_RESOURCE 9
- #define IS_REFERENCE 10
-
-These simple integer constants determine what value is currently stored in a variable. If you are a
-PHP developer, these types should sound fairly familiar. They are pretty much an exact reflection of
-the types you may use in regular PHP code. One small oddity is that ``IS_FALSE`` and ``IS_TRUE`` are
-implemented as separate types, instead of as a ``IS_BOOL`` type.
-
-Some of these types are self-contained, they don't store any auxiliary data. This includes
-``IS_UNDEF``, ``IS_NULL``, ``IS_FALSE`` and ``IS_TRUE``. For the rest of the types, we are going to
-require some additional memory to store the actual value of the variable.
-
-************
- zend_value
-************
-
-.. code:: c
-
- typedef union _zend_value {
- zend_long lval; /* long value, i.e. int. */
- double dval; /* double value, i.e. float. */
- zend_refcounted *counted;
- zend_string *str;
- zend_array *arr;
- zend_object *obj;
- zend_resource *res;
- zend_reference *ref;
- // Less important for now.
- zend_ast_ref *ast;
- zval *zv;
- void *ptr;
- zend_class_entry *ce;
- zend_function *func;
- struct {
- uint32_t w1;
- uint32_t w2;
- } ww;
- } zend_value;
-
-A C union is a data type that may store any one of its members at a time, by being (at least) as big
-as its biggest member. For example, ``zend_value`` may store the ``lval`` member, or the ``dval``
-member, but never both at the same time. However, it doesn't know which member is being stored.
-Remembering this is our job, and that's exactly what the ``IS_*`` constants are for.
-
-The top members of ``zend_value`` mostly mirror the ``IS_*`` constants, with the exception of
-``counted``. ``counted`` polymorphically refers to any `reference counted `__ value, including
-strings, arrays, objects, resources and references. ``null`` and ``bool`` are missing from
-``zend_value`` because their types are self-contained.
-
-The rest of the fields aren't important for now.
-
-******
- zval
-******
-
-Together, the value and the tag make up the ``zval``, along with some other fields. It may look
-intimidating at first. We'll go over it step by step.
-
-.. code:: c
-
- typedef struct _zval_struct zval;
-
- struct _zval_struct {
- zend_value value;
- union {
- uint32_t type_info;
- struct {
- ZEND_ENDIAN_LOHI_3(
- uint8_t type, /* active type */
- uint8_t type_flags,
- union {
- uint16_t extra; /* not further specified */
- } u)
- } v;
- } u1;
- union {
- uint32_t next; /* hash collision chain */
- uint32_t cache_slot; /* cache slot (for RECV_INIT) */
- uint32_t opline_num; /* opline number (for FAST_CALL) */
- uint32_t lineno; /* line number (for ast nodes) */
- uint32_t num_args; /* arguments number for EX(This) */
- uint32_t fe_pos; /* foreach position */
- uint32_t fe_iter_idx; /* foreach iterator index */
- uint32_t guard; /* recursion and single property guard */
- uint32_t constant_flags; /* constant flags */
- uint32_t extra; /* not further specified */
- } u2;
- };
-
-``zval.value`` reserves space for the actual variable data, as discussed above.
-
-``zval.u1`` stores the variable type, the given ``IS_*`` constant, along with some other flags. It's
-definition looks a bit complicated. You can think of the entire field as a 4 byte integer, split
-into 3 parts. ``v.type`` stores the actual variable type, ``v.type_flags`` is used for some
-`reference counting `__ flags, and ``v.u.extra`` is pretty much unused.
-
-``zval.u2`` defines some more storage for various contexts that is often unoccupied. It's there
-because the memory would otherwise be wasted due to padding, so we may as well make use of it. We'll
-go over the relevant ones in their corresponding chapters.
-
-********
- Macros
-********
-
-The fields in ``zval`` should never be accessed directly. Instead, there are a plethora of macros to
-access them, concealing some of the implementation details of the ``zval`` struct. For many macros,
-there's a ``_P``-suffixed variant that performs the same operation on a pointer to the given
-``zval``.
-
-.. list-table:: ``zval`` macros
- :header-rows: 1
-
- - - Macro
- - Description
- - - ``Z_TYPE[_P]``
- - Access the ``zval.u1.v.type`` part of the type flags, containing the ``IS_*`` type.
- - - ``Z_LVAL[_P]``
- - Access the underlying ``int`` value.
- - - ``Z_DVAL[_P]``
- - Access the underlying ``float`` value.
- - - ``Z_STR[_P]``
- - Access the underlying ``zend_string`` pointer.
- - - ``Z_STRVAL[_P]``
- - Access the strings raw ``char *`` pointer.
- - - ``Z_STRLEN[_P]``
- - Access the strings length.
- - - ``ZVAL_COPY_VALUE(t, s)``
- - Copy one ``zval`` to another, including type and value.
- - - ``ZVAL_COPY(t, s)``
- - Same as ``ZVAL_COPY_VALUE``, but if the value is reference counted, increase the counter.
-
-..
- _todo: There are many more.
-
-******************
- Other zval types
-******************
-
-``zval``\ s are sometimes used internally with types that don't exist in userland.
-
-.. code:: c
-
- #define IS_CONSTANT_AST 11
- #define IS_INDIRECT 12
- #define IS_PTR 13
- #define IS_ALIAS_PTR 14
- #define _IS_ERROR 15
-
-``IS_CONSTANT_AST`` is used to represent constant values (the right hand side of ``const``,
-property/parameter initializers, etc.) before they are evaluated. The evaluation of a constant
-expression is not always possible during compilation, because they may contain references to values
-only available at runtime. Until that evaluation is possible, the constants contain the AST of the
-expression rather than the concrete values. Check the `parser `__ chapter for more information
-on ASTs. When this flag is set, the ``zval.value.ast`` union member is set accordingly.
-
-``IS_INDIRECT`` indicates that the ``zval.value.zv`` member is populated. This field stores a
-pointer to some other ``zval``. This type is mainly used in two situations, namely for intermediate
-values between ``FETCH`` and ``ASSIGN`` instructions, and for the sharing of variables in the symbol
-table.
-
-..
- _todo: There are many more.
-
-``IS_PTR`` is used for pointers to arbitrary data. Most commonly, this type is used internally for
-``HashTable``, as ``HashTable`` may only store ``zval`` values. For example, ``EG(class_table)``
-represents the class table, which is a hash map of class names to the corresponding
-``zend_class_entry``, representing the class. The same goes for functions and many other data types.
-``IS_ALIAS_PTR`` is used for class aliases registered via ``class_alias``. Essentially, it just
-allows differencing between members in the class table that are aliases, or actual classes.
-Otherwise, it is essentially the same as ``IS_PTR``. Arbitrary data is accessed through
-``zval.value.ptr``, and casted to the correct type depending on context. If ``ptr`` stores a class
-or function, the ``zval.value.ce`` or ``zval.value.func`` fields may be used, respectively.
-
-``_IS_ERROR`` is used as an error value for some `object handlers `__. It is described in more
-detail in its own chapter.
-
-.. code:: c
-
- /* Fake types used only for type hinting.
- * These are allowed to overlap with the types below. */
- #define IS_CALLABLE 12
- #define IS_ITERABLE 13
- #define IS_VOID 14
- #define IS_STATIC 15
- #define IS_MIXED 16
- #define IS_NEVER 17
-
- /* used for casts */
- #define _IS_BOOL 18
- #define _IS_NUMBER 19
-
-These flags are never actually stored in ``zval.u1``. They are used for type hinting and in the
-`object handler `__ API.
-
-This only leaves the ``zval.value.ww`` field. In short, this field is used on 32-bit platforms when
-copying data from one ``zval`` to another. Normally, ``zval.value.counted`` is copied as a generic
-value, no matter what the actual underlying type is. ``zend_value`` always consists of 8 bytes due
-to the ``double`` field. Pointers, however, consist only of 4. Because we would otherwise miss the
-other 4 bytes, they are copied manually using ``z->value.ww.w2 = _w2;``. This happens in the
-``ZVAL_COPY_VALUE_EX`` macro, you won't ever have to care about this.
diff --git a/docs/source/core/memory-management/TODO.md b/docs/source/core/memory-management/TODO.md
new file mode 100644
index 000000000000..869060e09a88
--- /dev/null
+++ b/docs/source/core/memory-management/TODO.md
@@ -0,0 +1,12 @@
+Memory Management TODO
+
+The Reference Counting page contained TODOs for dedicated Cycle Collector and
+Zend Allocator pages; grouping them under Memory Management makes sense. Pages
+to be added:
+
+- Cycle Collector:
+ candidate buffering, collection phases, collectable types, and correct use of
+ GC flags and macros.
+- Zend Allocator:
+ allocator pairing, overflow-safe allocation, request/persistent lifetimes, and
+ relevant arena cleanup.
diff --git a/docs/source/core/memory-management/index.md b/docs/source/core/memory-management/index.md
new file mode 100644
index 000000000000..e818837dfef1
--- /dev/null
+++ b/docs/source/core/memory-management/index.md
@@ -0,0 +1,10 @@
+# Memory Management
+
+```{toctree}
+---
+hidden: true
+---
+reference-counting
+```
+
+This section describes how php-src manages the lifetime of allocated data.
diff --git a/docs/source/core/memory-management/reference-counting.md b/docs/source/core/memory-management/reference-counting.md
new file mode 100644
index 000000000000..8e7aac0e7c71
--- /dev/null
+++ b/docs/source/core/memory-management/reference-counting.md
@@ -0,0 +1,182 @@
+# Reference Counting
+
+In languages like C, when you need memory for storing data for an indefinite period of time or in a
+large amount, you call `malloc` and `free` to acquire and release blocks of memory of some size.
+This sounds simple on the surface but turns out to be quite tricky, mainly because the data may not
+be freed for as long as it is used anywhere in the program. Sometimes this makes it unclear who is
+responsible for freeing the memory, and when to do so. Failure to handle this correctly may result
+in a use-after-free, double-free, or memory leak.
+
+In PHP you usually do not need to think about memory management. The engine takes care of allocating
+and freeing memory for you by tracking which values are no longer needed. It does this by assigning
+a reference count to each allocated value, often abbreviated as refcount or RC. Whenever a reference
+to a value is passed somewhere else, its reference count is increased to indicate the value is now
+used by another party. When the party no longer needs the value, it is responsible for decreasing
+the reference count. Once the reference count reaches zero, we know the value is no longer needed
+anywhere, and that it may be freed.
+
+```php
+$a = new stdClass; // RC 1
+$b = $a; // RC 2
+unset($a); // RC 1
+unset($b); // RC 0, free
+```
+
+Reference counting is needed for types that store auxiliary data, which are the following:
+
+- Strings
+- Arrays
+- Objects
+- References
+- Resources
+
+These are either reference types (objects, references and resources) or they are large types that
+don't fit in a single `zend_value` directly (strings, arrays). Simpler types either don't store a
+value at all (`null`, `false`, `true`) or their value is small enough to fit directly in
+`zend_value` (`int`, `float`).
+
+All of the reference counted types share a common initial struct sequence.
+
+```c
+typedef struct _zend_refcounted_h {
+ uint32_t refcount; /* reference counter 32-bit */
+ union {
+ uint32_t type_info;
+ } u;
+} zend_refcounted_h;
+
+ struct _zend_string {
+ zend_refcounted_h gc;
+ // ...
+ };
+
+ struct _zend_array {
+ zend_refcounted_h gc;
+ // ...
+ };
+```
+
+The `zend_refcounted_h` struct is simple. It contains the reference count, and a `type_info`
+field that repeats some of the type information that is also stored in the `zval`, for situations
+where we're not dealing with a `zval` directly. It also stores some additional fields, described
+under [GC flags](#gc-flags).
+
+## Macros
+
+As with `zval`, `zend_refcounted_h` members should not be accessed directly. Instead, you should
+use the provided macros. There are macros that work with reference counted types directly, prefixed
+with `GC_`, or macros that work on `zval` values, usually prefixed with `Z_`. Unfortunately,
+naming is not always consistent.
+
+**`zval` macros**
+
+| Macro | Non-RC [^non-rc] | Description |
+| ------------------ | ---------------- | -------------------------------------------------------------------------------------- |
+| `Z_REFCOUNT[_P]` | No | Returns the reference count. |
+| `Z_ADDREF[_P]` | No | Increases the reference count. |
+| `Z_TRY_ADDREF[_P]` | Yes | Increases the reference count. May be called on any `zval`. |
+| `zval_ptr_dtor` | Yes | Decreases the reference count and frees the value if the reference count reaches zero. |
+
+**`zend_refcounted_h` macros**
+
+| Macro | Immutable [^immutable] | Description |
+| ------------------- | ---------------------- | -------------------------------------------------------------------------------------- |
+| `GC_REFCOUNT[_P]` | Yes | Returns the reference count. |
+| `GC_ADDREF[_P]` | No | Increases the reference count. |
+| `GC_TRY_ADDREF[_P]` | Yes | Increases the reference count. |
+| `GC_DTOR[_P]` | Yes | Decreases the reference count and frees the value if the reference count reaches zero. |
+
+## Separation
+
+PHP has value and reference types. Reference types are types that are shared through a reference, a
+"pointer" to the value, rather than the value itself. Modifying such a value in one place changes it
+for all of its observers. For example, writing to a property changes the property in every place the
+object is referenced. Value types, on the other hand, are copied when passed to another party.
+Modifying the original value does not affect the copy, and vice versa.
+
+In PHP, arrays and strings are value types. Since they are also reference counted types, this
+requires some special care when modifying values. In particular, we need to make sure that modifying
+the value is not observable from other places. Modifying a value with RC 1 is unproblematic, since
+we are the values sole owner. However, if the value has a reference count of >1, we need to create a
+fresh copy before modifying it. This process is called separation or CoW (copy on write).
+
+```php
+$a = [1, 2, 3]; // RC 1
+$b = $a; // RC 2
+$b[] = 4; // Separation, $a RC 1, $b RC 1
+var_dump($a); // [1, 2, 3]
+var_dump($b); // [1, 2, 3, 4]
+```
+
+## Immutable reference counted types
+
+Sometimes, even a reference counted type is not reference counted. When PHP runs in a multi-process
+or multi-threaded environment with opcache enabled, it shares some common values between processes
+or threads to reduce memory consumption. As you may know, sharing memory between processes or
+threads can be tricky and requires special care when modifying values. In particular, modification
+usually requires exclusive access to the memory so that the other processes or threads wait until
+the value is done being updated. In this case, this synchronization is avoided by making the value
+immutable and never modifying the reference count. Such values will receive the `GC_IMMUTABLE`
+flag in their `gc->u.type_info` field.
+
+Some macros like `GC_TRY_ADDREF` will guard against immutable values. You should not use immutable
+values on some macros, like `GC_ADDREF`. This will result in undefined behavior, because the macro
+will not check whether the value is immutable before performing the reference count modifications.
+You may execute PHP with the `-d opcache.protect_memory=1` flag to mark the shared memory as
+read-only and trigger a hardware exception if the code accidentally attempts to modify it.
+
+## Cycle collector
+
+Sometimes, reference counting is not enough. Consider the following example:
+
+```php
+$a = new stdClass;
+$b = new stdClass;
+$a->b = $b;
+$b->a = $a;
+unset($a);
+unset($b);
+```
+
+When this code finishes, the reference count of both instances of `stdClass` will still be 1, as
+they reference each other. This is called a reference cycle.
+
+PHP implements a cycle collector that detects such cycles and frees values that are only reachable
+through their own references. The cycle collector will record values that may be involved in a
+cycle, and run when this buffer becomes full. It is also possible to invoke it explicitly by calling
+the `gc_collect_cycles()` function.
+
+## GC flags
+
+```c
+/* zval_gc_flags(zval.value->gc.u.type_info) (common flags) */
+#define GC_NOT_COLLECTABLE (1<<4)
+#define GC_PROTECTED (1<<5) /* used for recursion detection */
+#define GC_IMMUTABLE (1<<6) /* can't be changed in place */
+#define GC_PERSISTENT (1<<7) /* allocated using malloc */
+#define GC_PERSISTENT_LOCAL (1<<8) /* persistent, but thread-local */
+```
+
+The `GC_NOT_COLLECTABLE` flag indicates that the value may not be involved in a reference cycle.
+This allows for a fast way to detect values that don't need to be added to the cycle collector
+buffer. Only arrays and objects may actually be involved in reference cycles.
+
+The `GC_PROTECTED` flag is used to protect against recursion in various internal functions. For
+example, `var_dump` recursively prints the contents of values, and marks visited values with the
+`GC_PROTECTED` flag. If the value is recursive, it prevents the same value from being visited
+again.
+
+`GC_IMMUTABLE` has been discussed in [Immutable reference counted types](#immutable-reference-counted-types).
+
+The `GC_PERSISTENT` flag indicates that the value was allocated using `malloc`, instead of PHPs
+own allocator. Usually, such values are alive for the entire lifetime of the process, instead of
+being freed at the end of the request.
+
+The `GC_PERSISTENT_LOCAL` flag indicates that a `GC_PERSISTENT` value is only accessible in one
+thread, and is thus still safe to modify. This flag is only used in debug builds to satisfy an
+`assert`.
+
+[^non-rc]: Whether the macro works with non-reference counted types. If it does, the operation is usually a
+ no-op. If it does not, using the macro on these values is undefined behavior.
+
+[^immutable]: Whether the macro works with immutable types, described under [Immutable reference counted types](#immutable-reference-counted-types).
diff --git a/docs/source/core/output-buffering-TODO.md b/docs/source/core/output-buffering-TODO.md
new file mode 100644
index 000000000000..4d262e775279
--- /dev/null
+++ b/docs/source/core/output-buffering-TODO.md
@@ -0,0 +1,7 @@
+## Open Questions
+
+- Should the userland API be adjusted and unified?
+
+Many bits of the manual (and very first implementation) do not comply with the
+behaviour of the current (to be obsoleted) code, thus should the manual or the
+behaviour be adjusted?
diff --git a/docs/source/core/output-buffering.md b/docs/source/core/output-buffering.md
new file mode 100644
index 000000000000..28028ea8f3c6
--- /dev/null
+++ b/docs/source/core/output-buffering.md
@@ -0,0 +1,132 @@
+# API adjustment to the old output control code
+
+Everything now resides beneath the php_output namespace, and there's an API call
+for every output handler op.
+
+```
+Checking output control layers status:
+ // Using OG()
+ php_output_get_status();
+
+Starting the default output handler:
+ // php_start_ob_buffer(NULL, 0, 1);
+ php_output_start_default();
+
+Starting an user handler by zval:
+ // php_start_ob_buffer(zhandler, chunk_size, erase);
+ php_output_start_user(zhandler, chunk_size, flags);
+
+Starting an internal handler without context:
+ // php_ob_set_internal_handler(my_php_output_handler_func_t, buffer_size, "output handler name", erase);
+ php_output_start_internal(handler_name, handler_name_len, my_php_output_handler_func_t, chunk_size, flags);
+
+Starting an internal handler with context:
+ // not possible with old API
+ php_output_handler *h;
+ h = php_output_handler_create_internal(handler_name, handler_name_len, my_php_output_handler_context_func_t, chunk_size, flags);
+ php_output_handler_set_context(h, my_context, my_context_dtor);
+ php_output_handler_start(h);
+
+Testing whether a certain output handler has already been started:
+ // php_ob_handler_used("output handler name");
+ php_output_handler_started(handler_name, handler_name_len);
+
+Flushing one output buffer:
+ // php_end_ob_buffer(1, 1);
+ php_output_flush();
+
+Flushing all output buffers:
+ // not possible with old API
+ php_output_flush_all();
+
+Cleaning one output buffer:
+ // php_ob_end_buffer(0, 1);
+ php_output_clean();
+
+Cleaning all output buffers:
+ // not possible with old API
+ php_output_clean_all();
+
+Discarding one output buffer:
+ // php_ob_end_buffer(0, 0);
+ php_output_discard();
+
+Discarding all output buffers:
+ // php_ob_end_buffers(0);
+ php_output_discard_all();
+
+Stopping (and dropping) one output buffer:
+ // php_ob_end_buffer(1, 0)
+ php_output_end();
+
+Stopping (and dropping) all output buffers:
+ // php_ob_end_buffers(1, 0);
+ php_output_end_all();
+
+Retrieving output buffers contents:
+ // php_ob_get_buffer(zstring);
+ php_output_get_contents(zstring);
+
+Retrieving output buffers length:
+ // php_ob_get_length(zlength);
+ php_output_get_length(zlength);
+
+Retrieving output buffering level:
+ // OG(nesting_level);
+ php_output_get_level();
+
+Issue a warning because of an output handler conflict:
+ // php_ob_init_conflict("to be started handler name", "to be tested if already started handler name");
+ php_output_handler_conflict(new_handler_name, new_handler_name_len, set_handler_name, set_handler_name_len);
+
+Registering a conflict checking function, which will be checked prior starting the handler:
+ // not possible with old API, unless hardcoding into output.c
+ php_output_handler_conflict_register(handler_name, handler_name_len, my_php_output_handler_conflict_check_t);
+
+Registering a reverse conflict checking function, which will be checked prior starting the specified foreign handler:
+ // not possible with old API
+ php_output_handler_reverse_conflict_register(foreign_handler_name, foreign_handler_name_len, my_php_output_handler_conflict_check_t);
+
+Facilitating a context from within an output handler callable with ob_start():
+ // not possible with old API
+ php_output_handler_hook(PHP_OUTPUT_HANDLER_HOOK_GET_OPAQ, (void *) &custom_ctx_ptr_ptr);
+
+Disabling of the output handler by itself:
+ //not possible with old API
+ php_output_handler_hook(PHP_OUTPUT_HANDLER_HOOK_DISABLE, NULL);
+
+Marking an output handler immutable by itself because of irreversibility of its operation:
+ // not possible with old API
+ php_output_handler_hook(PHP_OUTPUT_HANDLER_HOOK_IMMUTABLE, NULL);
+
+Restarting the output handler because of a CLEAN operation:
+ // not possible with old API
+ if (flags & PHP_OUTPUT_HANDLER_CLEAN) { ... }
+
+Recognizing by the output handler itself if it gets discarded:
+ // not possible with old API
+ if ((flags & PHP_OUTPUT_HANDLER_CLEAN) && (flags & PHP_OUTPUT_HANDLER_FINAL)) { ... }
+```
+
+## Output handler hooks
+
+The output handler can change its abilities at runtime. For example, the gz handler can
+remove the CLEANABLE and REMOVABLE bits when the first output has passed through it;
+or handlers implemented in C to be used with ob_start() can contain a non-global
+context:
+
+```
+PHP_OUTPUT_HANDLER_HOOK_GET_OPAQ
+ pass a void*** pointer as second arg to receive the address of a pointer
+ pointer to the opaque field of the output handler context
+PHP_OUTPUT_HANDLER_HOOK_GET_FLAGS
+ pass a int* pointer as second arg to receive the flags set for the output handler
+PHP_OUTPUT_HANDLER_HOOK_GET_LEVEL
+ pass a int* pointer as second arg to receive the level of this output handler
+ (starts with 0)
+PHP_OUTPUT_HANDLER_HOOK_IMMUTABLE
+ the second arg is ignored; marks the output handler to be neither cleanable
+ nor removable
+PHP_OUTPUT_HANDLER_HOOK_DISABLE
+ the second arg is ignored; marks the output handler as disabled
+```
diff --git a/docs-old/streams.md b/docs/source/core/streams/index.md
similarity index 87%
rename from docs-old/streams.md
rename to docs/source/core/streams/index.md
index 8220f9db78fc..1f05dee90e1a 100644
--- a/docs-old/streams.md
+++ b/docs/source/core/streams/index.md
@@ -1,6 +1,7 @@
# An overview of the PHP streams abstraction
-WARNING: some prototypes in this file are out of date.
+> [!WARNING]
+> Some prototypes in this file are out of date.
## Why streams?
@@ -51,21 +52,21 @@ PHPAPI php_stream *php_stream_open_wrapper(const char *path, const char *mode,
Where:
-* `path` is the file or resource to open.
-* `mode` is the stdio compatible mode eg: "wb", "rb" etc.
-* `options` is a combination of the following values:
- * `IGNORE_PATH` (default) - don't use include path to search for the file
- * `USE_PATH` - use include path to search for the file
- * `IGNORE_URL` - do not use plugin wrappers
- * `REPORT_ERRORS` - show errors in a standard format if something goes wrong.
- * `STREAM_MUST_SEEK` - If you really need to be able to seek the stream and
+- `path` is the file or resource to open.
+- `mode` is the stdio compatible mode eg: "wb", "rb" etc.
+- `options` is a combination of the following values:
+ - `IGNORE_PATH` (default) - don't use include path to search for the file
+ - `USE_PATH` - use include path to search for the file
+ - `IGNORE_URL` - do not use plugin wrappers
+ - `REPORT_ERRORS` - show errors in a standard format if something goes wrong.
+ - `STREAM_MUST_SEEK` - If you really need to be able to seek the stream and
don't need to be able to write to the original file/URL, use this option to
arrange for the stream to be copied (if needed) into a stream that can be
seek()ed.
-* `opened_path` is used to return the path of the actual file opened, but if you
+- `opened_path` is used to return the path of the actual file opened, but if you
used `STREAM_MUST_SEEK`, may not be valid. You are responsible for
`efree()ing` `opened_path`.
-* `opened_path` may be (and usually is) `NULL`.
+- `opened_path` may be (and usually is) `NULL`.
If you need to open a specific stream, or convert standard resources into
streams there are a range of functions to do this defined in `php_streams.h`. A
@@ -147,22 +148,27 @@ It returns one of the following values:
`make_seekable` will always set newstream to be the stream that is valid if the
function succeeds. When you have finished, remember to close the stream.
-NOTE: If you only need to seek forward, there is no need to call this function,
-as the `php_stream_seek` can emulate forward seeking when the whence parameter
-is `SEEK_CUR`.
+> [!NOTE]
+> If you only need to seek forward, there is no need to call this function, as
+> `php_stream_seek` can emulate forward seeking when the whence parameter is
+> `SEEK_CUR`.
-NOTE: Writing to the stream may not affect the original source, so it only makes
-sense to use this for read-only use.
+> [!NOTE]
+> Writing to the stream may not affect the original source, so it only makes
+> sense to use this for read-only use.
-NOTE: If the origstream is network based, this function will block until the
-whole contents have been downloaded.
+> [!NOTE]
+> If the origstream is network based, this function will block until the whole
+> contents have been downloaded.
-NOTE: Never call this function with an origstream that is referenced as a
-resource! It will close the origstream on success, and this can lead to a crash
-when the resource is later used/released.
+> [!NOTE]
+> Never call this function with an origstream that is referenced as a resource!
+> It will close the origstream on success, and this can lead to a crash when the
+> resource is later used/released.
-NOTE: If you are opening a stream and need it to be seekable, use the
-`STREAM_MUST_SEEK` option to php_stream_open_wrapper();
+> [!NOTE]
+> If you are opening a stream and need it to be seekable, use the
+> `STREAM_MUST_SEEK` option to `php_stream_open_wrapper()`.
```c
PHPAPI int php_stream_supports_lock(php_stream * stream);
@@ -205,7 +211,7 @@ PHP_STREAM_AS_SOCKETD - a socket descriptor
If you ask a socket stream for a `FILE*`, the abstraction will use fdopen to
create it for you. Be warned that doing so may cause buffered data to be lost
-if you mix ANSI stdio calls on the FILE* with php stream calls on the stream.
+if you mix ANSI stdio calls on the FILE\* with php stream calls on the stream.
If your system has the fopencookie function, php streams can synthesize a
`FILE*` on top of any stream, which is useful for SSL sockets, memory based
@@ -291,17 +297,17 @@ PHPAPI php_stream * php_stream_alloc(php_stream_ops * ops, void * abstract,
size_t bufsize, int persistent, const char * mode)
```
-* `ops` is a pointer to the implementation,
-* `abstract` holds implementation specific data that is relevant to this
+- `ops` is a pointer to the implementation,
+- `abstract` holds implementation specific data that is relevant to this
instance of the stream,
-* `bufsize` is the size of the buffer to use - if 0, then buffering at the
+- `bufsize` is the size of the buffer to use - if 0, then buffering at the
stream
-* `level` will be disabled (recommended for underlying sources that implement
+- `level` will be disabled (recommended for underlying sources that implement
their own buffering - such a `FILE*`)
-* `persistent` controls how the memory is to be allocated - persistently so that
+- `persistent` controls how the memory is to be allocated - persistently so that
it lasts across requests, or non-persistently so that it is freed at the end
of a request (it uses pemalloc),
-* `mode` is the stdio-like mode of operation - php streams places no real
+- `mode` is the stdio-like mode of operation - php streams places no real
meaning in the mode parameter, except that it checks for a `w` in the string
when attempting to write (this may change).
@@ -310,14 +316,14 @@ into a `FILE*`, so it should be compatible with the mode parameter of `fopen()`.
## Writing your own stream implementation
-* **RULE #1**: when writing your own streams: make sure you have configured PHP
+- **RULE #1**: when writing your own streams: make sure you have configured PHP
with `--enable-debug`.
Some great great pains have been taken to hook into the Zend memory manager to
help track down allocation problems. It will also help you spot incorrect use
of the STREAMS_DC, STREAMS_CC and the semi-private STREAMS_REL_CC macros for
function definitions.
-* RULE #2: Please use the stdio stream as a reference; it will help you
+- RULE #2: Please use the stdio stream as a reference; it will help you
understand the semantics of the stream operations, and it will always be more
up to date than these docs :-)
diff --git a/docs-old/unix-build-system.md b/docs/source/extensions/bundled-extensions.md
similarity index 79%
rename from docs-old/unix-build-system.md
rename to docs/source/extensions/bundled-extensions.md
index 01bb8e4e51cb..dff0b854bbaf 100644
--- a/docs-old/unix-build-system.md
+++ b/docs/source/extensions/bundled-extensions.md
@@ -1,22 +1,29 @@
# PHP build system V5 overview
-* supports Makefile.ins during transition phase
-* not-really-portable Makefile includes have been eliminated
-* supports separate build directories without VPATH by using explicit rules only
-* does not waste disk-space/CPU-time for building temporary libraries =>
+```{toctree}
+---
+hidden: true
+---
+bundled-extensions/filter
+```
+
+- supports Makefile.ins during transition phase
+- not-really-portable Makefile includes have been eliminated
+- supports separate build directories without VPATH by using explicit rules only
+- does not waste disk-space/CPU-time for building temporary libraries =>
especially noticeable on slower systems
-* slow recursive make replaced with one global Makefile
-* eases integration of proper dependencies
-* abandoning the "one library per directory" concept
-* improved integration of the CLI
-* several new targets:
- * `build-modules`: builds and copies dynamic modules into `modules/`
- * `install-cli`: installs the CLI only, so that the install-sapi target does
+- slow recursive make replaced with one global Makefile
+- eases integration of proper dependencies
+- abandoning the "one library per directory" concept
+- improved integration of the CLI
+- several new targets:
+ - `build-modules`: builds and copies dynamic modules into `modules/`
+ - `install-cli`: installs the CLI only, so that the install-sapi target does
only what its name says
-* finally abandoned automake
-* changed some configure-time constructs to run at buildconf-time
-* upgraded shtool to 1.5.4
-* removed `$(moduledir)` (use `EXTENSION_DIR`)
+- finally abandoned automake
+- changed some configure-time constructs to run at buildconf-time
+- upgraded shtool to 1.5.4
+- removed `$(moduledir)` (use `EXTENSION_DIR`)
## The reason for a new system
diff --git a/docs-old/input-filter.md b/docs/source/extensions/bundled-extensions/filter.md
similarity index 99%
rename from docs-old/input-filter.md
rename to docs/source/extensions/bundled-extensions/filter.md
index b4df9a6e77df..01b71d38994d 100644
--- a/docs-old/input-filter.md
+++ b/docs/source/extensions/bundled-extensions/filter.md
@@ -1,4 +1,4 @@
-# Input filter support in PHP
+# ext/filter
XSS (Cross Site Scripting) hacks are becoming more and more prevalent, and can
be quite difficult to prevent. Whenever you accept user data and somehow display
diff --git a/docs-old/parameter-parsing-api.md b/docs/source/extensions/parameter-parsing.md
similarity index 92%
rename from docs-old/parameter-parsing-api.md
rename to docs/source/extensions/parameter-parsing.md
index fae10f2fec8a..04c939ab40fc 100644
--- a/docs-old/parameter-parsing-api.md
+++ b/docs/source/extensions/parameter-parsing.md
@@ -24,7 +24,7 @@ int zend_parse_parameters_ex(int flags, int num_args, char *type_spec, ...);
The `zend_parse_parameters()` function takes the number of parameters passed to
the extension function, the type specifier string, and the list of pointers to
-variables to store the results in. The _ex() version also takes 'flags' argument
+variables to store the results in. The \_ex() version also takes 'flags' argument
-- current only `ZEND_PARSE_PARAMS_QUIET` can be used as 'flags' to specify that
the function should operate quietly and not output any error messages.
@@ -61,7 +61,7 @@ See also
The following list shows the type specifier, its meaning, and the parameter types
that need to be passed by address. All passed parameters are set if the PHP
parameter is non-optional and untouched if optional and the parameter is not
-present. The only exception is O where the zend_class_entry* has to be provided
+present. The only exception is O where the zend_class_entry\* has to be provided
on input and is used to verify the PHP parameter is an instance of that class.
```txt
@@ -96,18 +96,18 @@ z - the actual zval (zval*)
The following characters also have a meaning in the specifier string:
-* `|` - indicates that the remaining parameters are optional, they should be
+- `|` - indicates that the remaining parameters are optional, they should be
initialized to default values by the extension since they will not be touched
by the parsing function if they are not passed to it.
-* `/` - use SEPARATE_ZVAL() on the parameter it follows
-* `!` - the parameter it follows can be of specified type or NULL. If NULL is
+- `/` - use SEPARATE_ZVAL() on the parameter it follows
+- `!` - the parameter it follows can be of specified type or NULL. If NULL is
passed, and the output for such type is a pointer, then the output pointer is
set to a native NULL pointer. For 'b', 'l' and 'd', an extra argument of type
- bool* must be passed after the corresponding bool*, zend_long* or
- double* arguments, respectively. A non-zero value will be written to the
+ bool\* must be passed after the corresponding bool\*, zend_long\* or
+ double\* arguments, respectively. A non-zero value will be written to the
bool if a PHP NULL is passed.
- For `f` use the ``ZEND_FCI_INITIALIZED(fci)`` macro to check if a callable
- has been provided and ``!ZEND_FCI_INITIALIZED(fci)`` to check if a PHP NULL
+ For `f` use the `ZEND_FCI_INITIALIZED(fci)` macro to check if a callable
+ has been provided and `!ZEND_FCI_INITIALIZED(fci)` to check if a PHP NULL
is passed.
## Note on 64bit compatibility
@@ -119,7 +119,7 @@ and `size_t` to strings length (i.e. for "s" you need to pass char `*` and
Both mistakes might cause memory corruptions and segfaults:
-* 1
+- 1
```c
char *str;
@@ -127,7 +127,7 @@ long str_len; /* XXX THIS IS WRONG!! Use size_t instead. */
zend_parse_parameters(ZEND_NUM_ARGS(), "s", &str, &str_len)
```
-* 2
+- 2
```c
int num; /* XXX THIS IS WRONG!! Use zend_long instead. */
diff --git a/docs-old/self-contained-extensions.md b/docs/source/extensions/unbundled-extensions.md
similarity index 93%
rename from docs-old/self-contained-extensions.md
rename to docs/source/extensions/unbundled-extensions.md
index 522716c37089..ff0441befb12 100644
--- a/docs-old/self-contained-extensions.md
+++ b/docs/source/extensions/unbundled-extensions.md
@@ -3,8 +3,8 @@
A self-contained extension can be distributed independently of the PHP source.
To create such an extension, two things are required:
-* Configuration file (config.m4)
-* Source code for your module
+- Configuration file (config.m4)
+- Source code for your module
We will describe now how to create these and how to put things together.
@@ -12,12 +12,14 @@ We will describe now how to create these and how to put things together.
While the result will run on any system, a developer's setup needs these tools:
-* GNU autoconf
-* GNU m4
+- GNU autoconf
+- GNU m4
All of these are available from
- ftp://ftp.gnu.org/pub/gnu/
+```
+ftp://ftp.gnu.org/pub/gnu/
+```
## Converting an existing extension
@@ -144,10 +146,10 @@ an existing module called `foo`.
automatically be able to use `--with-foo=shared[,..]` or
`--enable-foo=shared[,..]`.
-2. In `config.m4`, use `PHP_NEW_EXTENSION([foo],.., [$ext_shared])` to enable
+1. In `config.m4`, use `PHP_NEW_EXTENSION([foo],.., [$ext_shared])` to enable
building the extension.
-3. Add the following lines to your C source file:
+1. Add the following lines to your C source file:
```c
#ifdef COMPILE_DL_FOO
@@ -162,7 +164,7 @@ points to be regarded.
1. Add `LICENSE` or `COPYING` to the `package.xml`
-2. The following should be defined in one of the extension header files
+1. The following should be defined in one of the extension header files
```c
#define PHP_FOO_VERSION "1.2.3"
diff --git a/docs/source/index.rst b/docs/source/index.md
similarity index 56%
rename from docs/source/index.rst
rename to docs/source/index.md
index 21e2526f47f6..d1a8c272b363 100644
--- a/docs/source/index.rst
+++ b/docs/source/index.md
@@ -1,38 +1,53 @@
-##############
- php-src docs
-##############
-
-.. toctree::
- :caption: Introduction
- :hidden:
-
- introduction/high-level-overview
- introduction/ides/index
-
-.. toctree::
- :caption: Core
- :hidden:
-
- core/data-structures/index
-
-.. toctree::
- :caption: Miscellaneous
- :hidden:
-
- miscellaneous/stubs
- miscellaneous/writing-tests
- miscellaneous/running-tests
+# php-src docs
+
+```{toctree}
+---
+caption: Introduction
+hidden: true
+---
+introduction/high-level-overview
+introduction/ides/index
+```
+
+```{toctree}
+---
+caption: Core
+hidden: true
+---
+core/data-structures/index
+core/memory-management/index
+core/output-buffering
+core/streams/index
+```
+
+```{toctree}
+---
+caption: Extensions
+hidden: true
+---
+extensions/parameter-parsing
+extensions/bundled-extensions
+extensions/unbundled-extensions
+```
+
+```{toctree}
+---
+caption: Miscellaneous
+hidden: true
+---
+miscellaneous/stubs
+miscellaneous/writing-tests
+miscellaneous/running-tests
+```
Welcome to the php-src documentation!
-.. warning::
-
- This documentation is work in progress.
-
- At this point in time, there are other guides that provide a more complete picture of the PHP
- project. Check the `CONTRIBUTING.md
- `__ file for a
- list of technical resources.
+> [!WARNING]
+> This documentation is work in progress.
+>
+> At this point in time, there are other guides that provide a more complete picture of the PHP
+> project. Check the [CONTRIBUTING.md](https://github.com/php/php-src/blob/master/CONTRIBUTING.md#technical-resources)
+> file for a list of technical resources.
php-src is the canonical implementation of the interpreter for the PHP programming language, as well
as various extensions that provide common functionality. This documentation is intended to help you
@@ -43,21 +58,17 @@ This documentation is not intended to be comprehensive, but is meant to explain
are not easy to grasp by reading code alone. It describes best practices, and will frequently omit
APIs that are discouraged for general use.
-******************
- How to get help?
-******************
+## How to get help?
Getting started with a new and complicated project like php-src can be overwhelming. While there's
no way around reading lots and lots of code, asking questions of somebody with experience can save a
lot of time. Luckily, many core developers are eager to help. Here are some ways you can get in
touch.
-- `Discord `__ (``#php-internals`` channel)
-- `R11 on StackOverflow `__
+- [Discord](https://phpc.chat) (`#php-internals` channel)
+- [R11 on StackOverflow](https://chat.stackoverflow.com/rooms/11/php)
-***************
- Prerequisites
-***************
+## Prerequisites
The php-src interpreter is written in C, and so are most of the bundled extensions. While extensions
may also be written in C++, ext-intl is currently the only bundled extension to do so. It is
diff --git a/docs/source/introduction/high-level-overview.rst b/docs/source/introduction/high-level-overview.md
similarity index 57%
rename from docs/source/introduction/high-level-overview.rst
rename to docs/source/introduction/high-level-overview.md
index 1240bed4c0e6..08cc3510415b 100644
--- a/docs/source/introduction/high-level-overview.rst
+++ b/docs/source/introduction/high-level-overview.md
@@ -1,6 +1,4 @@
-#####################
- High-level overview
-#####################
+# High-level overview
PHP is an interpreted language. Interpreted languages differ from compiled ones in that they aren't
compiled into machine-readable code ahead of time. Instead, the source files are read, processed and
@@ -9,110 +7,96 @@ prototyping, as it skips a lengthy compilation phase. However, it also poses som
to performance, which is one of the primary reasons interpreters can be complex. php-src borrows
many concepts from other compilers and interpreters.
-**********
- Pipeline
-**********
+## Pipeline
The goal of the interpreter is to read the users source files, and to simulate the users intent.
This process can be split into distinct phases that are easier to understand and implement.
-- Tokenization - splitting whole source files into words, called tokens.
-- Parsing - building a tree structure from tokens, called AST (abstract syntax tree).
-- Compilation - traversing the AST and building a list of operations, called opcodes.
-- Interpretation - reading and executing opcodes.
+- Tokenization - splitting whole source files into words, called tokens.
+- Parsing - building a tree structure from tokens, called AST (abstract syntax tree).
+- Compilation - traversing the AST and building a list of operations, called opcodes.
+- Interpretation - reading and executing opcodes.
php-src as a whole can be seen as a pipeline consisting of these stages, using the input of the
previous phase and producing some output for the next.
-.. code:: haskell
-
- source_code
- |> tokenizer -- tokens
- |> parser -- ast
- |> compiler -- opcodes
- |> interpreter
+```haskell
+source_code
+ |> tokenizer -- tokens
+ |> parser -- ast
+ |> compiler -- opcodes
+ |> interpreter
+```
Let's go into each phase in a bit more detail.
-**************
- Tokenization
-**************
+## Tokenization
Tokenization, often called "lexing" or "scanning", is the process of taking an entire program file
and splitting it into a list of words and symbols. Tokens generally consist of a type, a simple
integer constant representing the token, and a lexeme, the literal string used in the source code.
-.. code:: php
-
- if ($cond) {
- echo "Cond is true\n";
- }
-
-.. code:: text
-
- T_IF "if"
- T_WHITESPACE " "
- "("
- T_VARIABLE "$cond"
- ")"
- T_WHITESPACE " "
- "{"
- T_WHITESPACE "\n "
- T_ECHO "echo"
- T_WHITESPACE " "
- T_CONSTANT_ENCAPSED_STRING '"Cond is true\n"'
- ";"
- T_WHITESPACE "\n"
- "}"
-
-While tokenizers are not difficult to write by hand, PHP uses a tool called ``re2c`` to automate
+```php
+if ($cond) {
+ echo "Cond is true\n";
+}
+```
+
+```text
+T_IF "if"
+T_WHITESPACE " "
+ "("
+T_VARIABLE "$cond"
+ ")"
+T_WHITESPACE " "
+ "{"
+T_WHITESPACE "\n "
+T_ECHO "echo"
+T_WHITESPACE " "
+T_CONSTANT_ENCAPSED_STRING '"Cond is true\n"'
+ ";"
+T_WHITESPACE "\n"
+ "}"
+```
+
+While tokenizers are not difficult to write by hand, PHP uses a tool called `re2c` to automate
this process. It takes a definition file and generates efficient C code to build these tokens from a
-stream of characters. The definition for PHP lives in ``Zend/zend_language_scanner.l``. Check the
-`re2c documentation`_ for details.
-
-.. _re2c documentation: https://re2c.org/
+stream of characters. The definition for PHP lives in `Zend/zend_language_scanner.l`. Check the
+[re2c documentation](https://re2c.org/) for details.
-*********
- Parsing
-*********
+## Parsing
Parsing is the process of reading the tokens generated from the tokenizer and building a tree
structure from it. To humans, how source code elements are grouped seems obvious through whitespace
-and the usage of symbols like ``()`` and ``{}``. However, computers cannot visually glance over the
+and the usage of symbols like `()` and `{}`. However, computers cannot visually glance over the
code to determine these boundaries quickly. To make it easier and faster to work with, we build a
tree structure from the tokens to more closely reflect the source code the way humans see it.
Here is a simplified example of what an AST from the tokens above might look like.
-.. code:: text
-
- ZEND_AST_IF {
- ZEND_AST_IF_ELEM {
- ZEND_AST_VAR {
- ZEND_AST_ZVAL { "cond" },
- },
- ZEND_AST_STMT_LIST {
- ZEND_AST_ECHO {
- ZEND_AST_ZVAL { "Cond is true\n" },
- },
- },
- },
- }
+```text
+ZEND_AST_IF {
+ ZEND_AST_IF_ELEM {
+ ZEND_AST_VAR {
+ ZEND_AST_ZVAL { "cond" },
+ },
+ ZEND_AST_STMT_LIST {
+ ZEND_AST_ECHO {
+ ZEND_AST_ZVAL { "Cond is true\n" },
+ },
+ },
+ },
+}
+```
Each AST node has a type and may have children. They also store their original position in the
source code, and may define some arbitrary flags. These are omitted for brevity.
-Like with tokenization, we use a tool called ``Bison`` to generate the parser implementation from a
-grammar specification. The grammar lives in the ``Zend/zend_language_parser.y`` file. Check the
-`Bison documentation`_ for details. Luckily, the syntax is quite approachable.
-
-.. _bison documentation: https://www.gnu.org/software/bison/manual/
-
-Parsing is described in more detail in its `dedicated chapter `__.
+Like with tokenization, we use a tool called `Bison` to generate the parser implementation from a
+grammar specification. The grammar lives in the `Zend/zend_language_parser.y` file. Check the
+[Bison documentation](https://www.gnu.org/software/bison/manual/) for details. Luckily, the syntax is quite approachable.
-*************
- Compilation
-*************
+## Compilation
Computers don't understand human language, or even programming languages. They only understand
machine code, which are sequences of simple, mostly atomic instructions for doing one thing. For
@@ -130,65 +114,55 @@ in an actual CPU instruction set (e.g. adding two numbers), while others are muc
With that little detour out of the way, the job of the compiler is to read the AST and translate it
into our virtual machine instructions, also called opcodes. The code responsible for this
-transformation lives in ``Zend/zend_compile.c``. It essentially traverses the AST and generates a
+transformation lives in `Zend/zend_compile.c`. It essentially traverses the AST and generates a
number of instructions, before going to the next node.
Here's what the surprisingly compact opcodes for the AST above might look like:
-.. code:: text
+```text
+0000 JMPZ CV0($cond) 0002
+0001 ECHO string("Cond is true\n")
+0002 RETURN int(1)
+```
- 0000 JMPZ CV0($cond) 0002
- 0001 ECHO string("Cond is true\n")
- 0002 RETURN int(1)
+## Interpretation
-****************
- Interpretation
-****************
-
-Finally, the opcodes are read and executed by the interpreter. PHPs uses `three-address code`_ for
+Finally, the opcodes are read and executed by the interpreter. PHPs uses [three-address code](https://en.wikipedia.org/wiki/Three-address_code) for
instructions. This essentially means that each instructions may have a result value, and at most two
-operands. Most modern CPUs also use this format. Both result and operands in PHP are :doc:`zvals
-<../core/data-structures/zval>`.
-
-.. _three-address code: https://en.wikipedia.org/wiki/Three-address_code
+operands. Most modern CPUs also use this format. Both result and operands in PHP are {doc}`zvals <../core/data-structures/zval>`.
How exactly each opcode behaves depends on its purpose. You can find a complete list of opcodes in
-the generated ``Zend/zend_vm_opcodes.h`` file. The behavior of each instruction is defined in
-``Zend/zend_vm_def.h``.
+the generated `Zend/zend_vm_opcodes.h` file. The behavior of each instruction is defined in
+`Zend/zend_vm_def.h`.
Let's step through the opcodes form the example above:
-- We start at the top, i.e. ``JMPZ``. If its first operand contains a "falsy" value, it will jump
- to the instruction encoded in its second operand. If it is truthy, it will simply fall-through to
- the next instruction.
-
-- The ``ECHO`` instruction prints its first operand.
-
-- The ``RETURN`` operand terminates the current function.
+- We start at the top, i.e. `JMPZ`. If its first operand contains a "falsy" value, it will jump
+ to the instruction encoded in its second operand. If it is truthy, it will simply fall-through to
+ the next instruction.
+- The `ECHO` instruction prints its first operand.
+- The `RETURN` operand terminates the current function.
-With these simple rules, we can see that the interpreter will ``echo`` only when ``$cond`` is
-truthy, and skip over the ``echo`` otherwise.
+With these simple rules, we can see that the interpreter will `echo` only when `$cond` is
+truthy, and skip over the `echo` otherwise.
That's it! This is how PHP works, fundamentally. Of course, we skipped over a ton of details. The VM
-is quite complex, and will be discussed separately in the `virtual machine `__ chapter.
+is quite complex.
-*********
- Opcache
-*********
+## Opcache
As you may imagine, running this whole pipeline every time PHP serves a request is time consuming.
Luckily, it is also not necessary. We can cache the opcodes in memory between requests, to skip over
all of the phases, except for the execution phase. This is precisely what the opcache extension
-does. It lives in the ``ext/opcache`` directory.
+does. It lives in the `ext/opcache` directory.
Opcache also performs some optimizations on the opcodes before caching them. As opcaches are
expected to be reused many times, it is profitable to spend some additional time simplifying them if
-possible to improve performance during execution. The optimizer lives in ``Zend/Optimizer``.
+possible to improve performance during execution. The optimizer lives in `Zend/Optimizer`.
-JIT
-===
+### JIT
The opcache also implements a JIT compiler, which stands for just-in-time compiler. This compiler
takes the virtual PHP opcodes and turns it into actual machine instructions, with additional
information gained at runtime. JITs are very complex pieces of software, so this book will likely
-barely scratch the surface of how it works. It lives in ``ext/opcache/jit``.
+barely scratch the surface of how it works. It lives in `ext/opcache/jit`.
diff --git a/docs/source/introduction/ides/TODO.md b/docs/source/introduction/ides/TODO.md
new file mode 100644
index 000000000000..510dbaa955f0
--- /dev/null
+++ b/docs/source/introduction/ides/TODO.md
@@ -0,0 +1,3 @@
+IDEs TODO
+
+- Add LLDB setup and debugging instructions, particularly for macOS.
diff --git a/docs/source/introduction/ides/index.rst b/docs/source/introduction/ides/index.md
similarity index 58%
rename from docs/source/introduction/ides/index.rst
rename to docs/source/introduction/ides/index.md
index e12e0d5c7ccd..047e320a43b8 100644
--- a/docs/source/introduction/ides/index.rst
+++ b/docs/source/introduction/ides/index.md
@@ -1,10 +1,10 @@
-######
- IDEs
-######
+# IDEs
-.. toctree::
- :hidden:
-
- visual-studio-code
+```{toctree}
+---
+hidden: true
+---
+visual-studio-code
+```
Here you can find instructions on how to effectively use common IDEs for php-src development.
diff --git a/docs/source/introduction/ides/visual-studio-code.md b/docs/source/introduction/ides/visual-studio-code.md
new file mode 100644
index 000000000000..6e966150bd2a
--- /dev/null
+++ b/docs/source/introduction/ides/visual-studio-code.md
@@ -0,0 +1,112 @@
+# Visual Studio Code
+
+> [!NOTE]
+> These instructions have been tested on Linux. macOS should mostly work the same. For Windows,
+> ymmv.
+
+An IDE can make navigating large code bases tremendously easier. Visual Studio Code is a popular and
+free IDE that is well-suited for C development. It contains syntax highlighting, navigation,
+auto-completion and a debugger. Check the [official website](https://code.visualstudio.com/) for
+installation instructions.
+
+> [!NOTE]
+> The `settings.json` file referenced below can be opened in the Settings page by pressing the
+> "Open Settings (JSON)" button in the top right corner. Most of these settings can also be
+> adjusted through the GUI.
+
+## C/C++ extension
+
+The [C/C++ extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode.cpptools) provides most of the features we'll need for php-src development. You can
+find it in the extensions marketplace. You will also need `gcc` or `clang` installed. The
+extension will mostly work out of the box, but it is advisable to use the `compile_commands.json`
+file. It contains a list of all compiled files, along with the commands used to compile them. It
+provides the extension with the necessary information about include paths and other compiler flags.
+
+To generate the `compile_commands.json` file, you can use the [compiledb](https://github.com/nickdiego/compiledb) tool. Install it using
+`pip`, and then prefix your `make` command accordingly:
+
+```bash
+# Install compiledb
+pip install compiledb
+# Compile php-src and generate compile_commands.json
+compiledb make -j8
+```
+
+To tell the C/C++ extension to use the `compile_commands.json` file, add the following to your
+`settings.json` file:
+
+```json
+{
+ "C_Cpp.default.compileCommands": "${workspaceFolder}/compile_commands.json"
+}
+```
+
+## clangd
+
+The C/C++ extension usually works well enough. Some people find that `clangd` works better.
+`clangd` is a language server built on top of the `clang` compiler. It only provides navigation
+and code completion but no syntax highlighting and no debugger. As such, it should be used in
+conjunction with the C/C++ extension. For the two extensions not to clash, add the following to your
+`settings.json` file:
+
+```json
+{
+ "C_Cpp.intelliSenseEngine": "disabled"
+}
+```
+
+Follow the [official installation instructions for clangd](https://clangd.llvm.org/installation.html), and then install the [clangd extension](https://marketplace.visualstudio.com/items?itemName=llvm-vs-code-extensions.vscode-clangd).
+Alternatively, you can let the extension install `clangd` for you. `clangd` requires a
+`compile_commands.json` file, so make sure to follow the instructions from the previous section.
+By default, `clangd` will auto-include header files on completion. php-src headers are somewhat
+peculiar, so you might want to disable this option in your `settings.json` file:
+
+```json
+{
+ "clangd.arguments": [
+ "-header-insertion=never"
+ ]
+}
+```
+
+## gdb
+
+The C/C++ extension provides the ability to use Visual Studio Code as a frontend for `gdb`. Of
+course, you will need `gdb` installed on your system, and php-src must be compiled with the
+`--enable-debug` configure flag. Copy the following into your projects `.vscode/launch.json`
+file:
+
+```json
+{
+ "version": "0.2.0",
+ "configurations": [
+ {
+ "name": "(gdb) Launch",
+ "type": "cppdbg",
+ "request": "launch",
+ "program": "${workspaceFolder}/sapi/cli/php",
+ "args": [
+ // Any options you want to test with
+ // "-dopcache.enable_cli=1",
+ "${relativeFile}",
+ ],
+ "stopAtEntry": false,
+ "cwd": "${workspaceFolder}",
+ // Useful if you build with --enable-address-sanitizer
+ "environment": [
+ { "name": "USE_ZEND_ALLOC", "value": "0" },
+ { "name": "USE_TRACKED_ALLOC", "value": "1" },
+ { "name": "LSAN_OPTIONS", "value": "detect_leaks=0" },
+ ],
+ "externalConsole": false,
+ "MIMode": "gdb",
+ "setupCommands": [
+ { "text": "source ${workspaceFolder}/.gdbinit" },
+ ]
+ }
+ ]
+}
+```
+
+Set any breakpoint in your C code, open a `php` (or `phpt`) file and start debugging from the
+"Run and Debug" tab in the sidebar.
diff --git a/docs/source/introduction/ides/visual-studio-code.rst b/docs/source/introduction/ides/visual-studio-code.rst
deleted file mode 100644
index 3493c00e83aa..000000000000
--- a/docs/source/introduction/ides/visual-studio-code.rst
+++ /dev/null
@@ -1,132 +0,0 @@
-####################
- Visual Studio Code
-####################
-
-.. note::
-
- These instructions have been tested on Linux. macOS should mostly work the same. For Windows,
- ymmv.
-
-An IDE can make navigating large code bases tremendously easier. Visual Studio Code is a popular and
-free IDE that is well-suited for C development. It contains syntax highlighting, navigation,
-auto-completion and a debugger. Check the `official website `__ for
-installation instructions.
-
-.. note::
-
- The ``settings.json`` file referenced below can be opened in the Settings page by pressing the
- "Open Settings (JSON)" button in the top right corner. Most of these settings can also be
- adjusted through the GUI.
-
-*****************
- C/C++ extension
-*****************
-
-The `C/C++ extension`_ provides most of the features we'll need for php-src development. You can
-find it in the extensions marketplace. You will also need ``gcc`` or ``clang`` installed. The
-extension will mostly work out of the box, but it is advisable to use the ``compile_commands.json``
-file. It contains a list of all compiled files, along with the commands used to compile them. It
-provides the extension with the necessary information about include paths and other compiler flags.
-
-.. _c/c++ extension: https://marketplace.visualstudio.com/items?itemName=ms-vscode.cpptools
-
-To generate the ``compile_commands.json`` file, you can use the compiledb_ tool. Install it using
-``pip``, and then prefix your ``make`` command accordingly:
-
-.. _compiledb: https://github.com/nickdiego/compiledb
-
-.. code:: bash
-
- # Install compiledb
- pip install compiledb
- # Compile php-src and generate compile_commands.json
- compiledb make -j8
-
-To tell the C/C++ extension to use the ``compile_commands.json`` file, add the following to your
-``settings.json`` file:
-
-.. code:: json
-
- {
- "C_Cpp.default.compileCommands": "${workspaceFolder}/compile_commands.json"
- }
-
-********
- clangd
-********
-
-The C/C++ extension usually works well enough. Some people find that ``clangd`` works better.
-``clangd`` is a language server built on top of the ``clang`` compiler. It only provides navigation
-and code completion but no syntax highlighting and no debugger. As such, it should be used in
-conjunction with the C/C++ extension. For the two extensions not to clash, add the following to your
-``settings.json`` file:
-
-.. code:: json
-
- {
- "C_Cpp.intelliSenseEngine": "disabled"
- }
-
-Follow the `official installation instructions for clangd
-`__, and then install the `clangd extension`_.
-Alternatively, you can let the extension install ``clangd`` for you. ``clangd`` requires a
-``compile_commands.json`` file, so make sure to follow the instructions from the previous section.
-By default, ``clangd`` will auto-include header files on completion. php-src headers are somewhat
-peculiar, so you might want to disable this option in your ``settings.json`` file:
-
-.. _clangd extension: https://marketplace.visualstudio.com/items?itemName=llvm-vs-code-extensions.vscode-clangd
-
-.. code:: json
-
- {
- "clangd.arguments": [
- "-header-insertion=never"
- ]
- }
-
-*****
- gdb
-*****
-
-The C/C++ extension provides the ability to use Visual Studio Code as a frontend for ``gdb``. Of
-course, you will need ``gdb`` installed on your system, and php-src must be compiled with the
-``--enable-debug`` configure flag. Copy the following into your projects ``.vscode/launch.json``
-file:
-
-.. code:: json
-
- {
- "version": "0.2.0",
- "configurations": [
- {
- "name": "(gdb) Launch",
- "type": "cppdbg",
- "request": "launch",
- "program": "${workspaceFolder}/sapi/cli/php",
- "args": [
- // Any options you want to test with
- // "-dopcache.enable_cli=1",
- "${relativeFile}",
- ],
- "stopAtEntry": false,
- "cwd": "${workspaceFolder}",
- // Useful if you build with --enable-address-sanitizer
- "environment": [
- { "name": "USE_ZEND_ALLOC", "value": "0" },
- { "name": "USE_TRACKED_ALLOC", "value": "1" },
- { "name": "LSAN_OPTIONS", "value": "detect_leaks=0" },
- ],
- "externalConsole": false,
- "MIMode": "gdb",
- "setupCommands": [
- { "text": "source ${workspaceFolder}/.gdbinit" },
- ]
- }
- ]
- }
-
-Set any breakpoint in your C code, open a ``php`` (or ``phpt``) file and start debugging from the
-"Run and Debug" tab in the sidebar.
-
-..
- _todo: lldb should work mostly the same, I believe. It's available by default on macOS, and as such might be more convenient.
diff --git a/docs/source/miscellaneous/running-tests.md b/docs/source/miscellaneous/running-tests.md
new file mode 100644
index 000000000000..f9ca124bf81f
--- /dev/null
+++ b/docs/source/miscellaneous/running-tests.md
@@ -0,0 +1,172 @@
+# Running Tests
+
+The easiest way to test your PHP build is to run make test from the command line after successfully
+compiling. This will run the all tests for all enabled functionalities and extensions located in
+tests folders under the source root directory using the PHP CLI binary.
+
+`make test` executes the `run-tests.php` script under the source root (parallel builds will not
+work). Therefore you can execute the script as follows:
+
+```shell
+sapi/cli/php [-c /path/to/php.ini] run-tests.php [ext/foo/tests/GLOB]
+```
+
+## Which php executable does make test use?
+
+If you are running the `run-tests.php` script from the command line (as above) you can set the
+`TEST_PHP_EXECUTABLE` environment variable to explicitly select the PHP executable that is to be
+tested, that is, used to run the test scripts, otherwise it will use the PHP CLI binary that you
+have compiled (`sapi/cli/php`).
+
+If you run the tests using make test, the PHP CLI and CGI executables are automatically set for you.
+`make test` executes `run-tests.php` script with the CLI binary. Some test scripts such as
+session must be executed by CGI SAPI. Therefore, you must build PHP with CGI SAPI to perform all
+tests.
+
+> [!NOTE]
+> The PHP binary executing `run-tests.php` and the PHP binary used for executing test scripts may
+> differ. If you use different PHP binary for executing `run-tests.php` script, you may get errors.
+
+## Which php.ini is used?
+
+`make test` uses the same `php.ini` file as it would once installed. The tests have been written
+to be independent of that `php.ini` file, so if you find a test that is affected by a setting,
+please report this, so we can address the issue.
+
+## Which test scripts are executed?
+
+The `run-tests.php` (`make test`), without any arguments executes all test scripts by extracting
+all directories named tests from the source root and any subdirectories below. If there are files,
+which have a phpt extension, `run-tests.php` looks at the sections in these files, determines
+whether it should run it, by evaluating the `SKIPIF` section. If the test is eligible for
+execution, the `FILE` section is extracted into a `.php` file (with the same name besides the
+extension) and gets executed. When an argument is given or `TESTS` environment variable is set,
+the GLOB is expanded by the shell and any file with extension `*.phpt` is regarded as a test file.
+
+Tester can easily execute tests selectively with as follows:
+
+```shell
+./sapi/cli/php run-tests.php ext/mbstring/*
+./sapi/cli/php run-tests.php ext/mbstring/020.phpt
+```
+
+## Test Runner Options
+
+The `run-tests.php` test runner has many options. You can see these options by using the `-h`
+option with `run-tests.php`.
+
+You can set options by specifying them on the command line when you run `php run-tests.php` or if
+you use `make test` through the `TEST_PHP_ARGS` environment variable:
+
+```shell
+php run-tests.php -j24
+# or
+TEST_PHP_ARGS="-j24" make test
+```
+
+### Running Tests in Parallel
+
+The test runner can run tests in parallel, by using the `-j` option:
+
+```shell
+php run-tests.php -j24 ext/date/*.phpt
+```
+
+## Test results
+
+Test results are printed to standard output. If there is a failed test, the `run-tests.php` script
+saves the result, the expected result and the code executed to the test script directory. For
+example, if `ext/myext/tests/myext.phpt` fails to pass, the following files are created:
+
+- `ext/myext/tests/myext.php` - actual test file executed
+- `ext/myext/tests/myext.log` - log of test execution (L)
+- `ext/myext/tests/myext.exp` - expected output (E)
+- `ext/myext/tests/myext.out` - output from test script (O)
+- `ext/myext/tests/myext.diff` - diff of .out and .exp (D)
+
+Failed tests are always bugs. Either the test is bugged or not considering factors applying to the
+tester's environment, or there is a bug in PHP. If this is a known bug, we strive to provide bug
+numbers, in either the test name or the file name. You can check the status of such a bug, by going
+to: `https://bugs.php.net/12345` where 12345 is the bug number. For clarity and automated
+processing, bug numbers are prefixed by a hash sign '#' in test names and/or test cases are named
+`bug12345.phpt`.
+
+> [!NOTE]
+> The files generated by tests can be selected by setting the environment variable
+> `TEST_PHP_LOG_FORMAT`. For each file you want to be generated use the character in brackets as
+> shown above (default is LEOD). The php file will be generated always.
+
+> [!NOTE]
+> You can set environment variable `TEST_PHP_DETAILED` to enable detailed test information.
+
+## Automated testing
+
+If you like to keep up to speed, with latest developments and quality assurance, setting the
+environment variable `NO_INTERACTION` to 1, will not prompt the tester for any user input.
+
+Normally, the exit status of make test is zero, regardless of the results of independent tests. Set
+the environment variable `REPORT_EXIT_STATUS` to `1`, and make test will set the exit status
+("\$?") to non-zero, when an individual test has failed.
+
+Example script to be run by cron:
+
+```shell
+========== qa-test.sh =============
+#!/bin/sh
+
+CO_DIR=$HOME/cvs/php7
+MYMAIL=qa-test@domain.com
+TMPDIR=/var/tmp
+TODAY=`date +"%Y%m%d"`
+
+# Make sure compilation environment is correct
+CONFIGURE_OPTS='--disable-all --enable-cli --with-pcre'
+export MAKE=gmake
+export CC=gcc
+
+# Set test environment
+export NO_INTERACTION=1
+export REPORT_EXIT_STATUS=1
+
+cd $CO_DIR
+cvs update . >>$TMPDIR/phpqatest.$TODAY
+./cvsclean ; ./buildconf ; ./configure $CONFIGURE_OPTS ; $MAKE
+$MAKE test >>$TMPDIR/phpqatest.$TODAY 2>&1
+if test $? -gt 0
+then
+ cat $TMPDIR/phpqatest.$TODAY | mail -s"PHP-QA Test Failed for $TODAY" $MYMAIL
+fi
+========== end of qa-test.sh =============
+```
+
+> [!NOTE]
+> The exit status of `run-tests.php` will be `1` when `REPORT_EXIT_STATUS` is set. The result of
+> make test may be higher than that. At present, gmake 3.79.1 returns 2, so it is advised to test
+> for non-zero, rather then a specific value.
+
+When `make test` finished running tests, and if there are any failed tests, the script asks to
+send the logs to the PHP QA mailing list. Please answer `y` to this question so that we can
+efficiently process the results, entering your e-mail address (which will not be transmitted in
+plain text to any list) enables us to ask you some more information if a test failed. Note that this
+script also uploads php -i output so your hostname may be transmitted.
+
+Specific tests can also be executed, like running tests for a certain extension. To do this you can
+do like so (for example the standard library):
+
+```shell
+make test TESTS=ext/standard.
+```
+
+Where `TESTS=` points to a directory containing .phpt files or a single .phpt file like:
+
+```shell
+make test TESTS=tests/basic/001.phpt.
+```
+
+You can also pass options directly to the underlying script that runs the test suite
+(`run-tests.phpt`) using `TESTS=`, for example to check for memory leaks using Valgrind, the
+`-m` option can be passed along: `make test TESTS="-m Zend/"`. For a full list of options that
+can be passed along, then run `make test TESTS=-h`.
+
+*Windows users:* On Windows the `make` command is called `nmake` instead of `make`. This means
+that on Windows you will have to run `nmake test`, to run the test suite.
diff --git a/docs/source/miscellaneous/running-tests.rst b/docs/source/miscellaneous/running-tests.rst
deleted file mode 100644
index bb2c56dcecd2..000000000000
--- a/docs/source/miscellaneous/running-tests.rst
+++ /dev/null
@@ -1,185 +0,0 @@
-###############
- Running Tests
-###############
-
-The easiest way to test your PHP build is to run make test from the command line after successfully
-compiling. This will run the all tests for all enabled functionalities and extensions located in
-tests folders under the source root directory using the PHP CLI binary.
-
-``make test`` executes the ``run-tests.php`` script under the source root (parallel builds will not
-work). Therefore you can execute the script as follows:
-
-.. code:: shell
-
- sapi/cli/php [-c /path/to/php.ini] run-tests.php [ext/foo/tests/GLOB]
-
-******************************************
- Which php executable does make test use?
-******************************************
-
-If you are running the ``run-tests.php`` script from the command line (as above) you can set the
-``TEST_PHP_EXECUTABLE`` environment variable to explicitly select the PHP executable that is to be
-tested, that is, used to run the test scripts, otherwise it will use the PHP CLI binary that you
-have compiled (``sapi/cli/php``).
-
-If you run the tests using make test, the PHP CLI and CGI executables are automatically set for you.
-``make test`` executes ``run-tests.php`` script with the CLI binary. Some test scripts such as
-session must be executed by CGI SAPI. Therefore, you must build PHP with CGI SAPI to perform all
-tests.
-
-**Note:** The PHP binary executing ``run-tests.php`` and the PHP binary used for executing test
-scripts may differ. If you use different PHP binary for executing ``run-tests.php`` script, you may
-get errors.
-
-************************
- Which php.ini is used?
-************************
-
-``make test`` uses the same ``php.ini`` file as it would once installed. The tests have been written
-to be independent of that ``php.ini`` file, so if you find a test that is affected by a setting,
-please report this, so we can address the issue.
-
-**********************************
- Which test scripts are executed?
-**********************************
-
-The ``run-tests.php`` (``make test``), without any arguments executes all test scripts by extracting
-all directories named tests from the source root and any subdirectories below. If there are files,
-which have a phpt extension, ``run-tests.php`` looks at the sections in these files, determines
-whether it should run it, by evaluating the ``SKIPIF`` section. If the test is eligible for
-execution, the ``FILE`` section is extracted into a ``.php`` file (with the same name besides the
-extension) and gets executed. When an argument is given or ``TESTS`` environment variable is set,
-the GLOB is expanded by the shell and any file with extension ``*.phpt`` is regarded as a test file.
-
-Tester can easily execute tests selectively with as follows:
-
-.. code:: shell
-
- ./sapi/cli/php run-tests.php ext/mbstring/*
- ./sapi/cli/php run-tests.php ext/mbstring/020.phpt
-
-*********************
- Test Runner Options
-*********************
-
-The ``run-tests.php`` test runner has many options. You can see these options by using the ``-h``
-option with ``run-tests.php``.
-
-You can set options by specifying them on the command line when you run ``php run-tests.php`` or if
-you use ``make test`` through the ``TEST_PHP_ARGS`` environment variable:
-
-.. code:: shell
-
- php run-tests.php -j24
- # or
- TEST_PHP_ARGS="-j24" make test
-
-Running Tests in Parallel
-=========================
-
-The test runner can run tests in parallel, by using the ``-j`` option:
-
-.. code:: shell
-
- php run-tests.php -j24 ext/date/*.phpt
-
-**************
- Test results
-**************
-
-Test results are printed to standard output. If there is a failed test, the ``run-tests.php`` script
-saves the result, the expected result and the code executed to the test script directory. For
-example, if ``ext/myext/tests/myext.phpt`` fails to pass, the following files are created:
-
-- ``ext/myext/tests/myext.php`` - actual test file executed
-- ``ext/myext/tests/myext.log`` - log of test execution (L)
-- ``ext/myext/tests/myext.exp`` - expected output (E)
-- ``ext/myext/tests/myext.out`` - output from test script (O)
-- ``ext/myext/tests/myext.diff`` - diff of .out and .exp (D)
-
-Failed tests are always bugs. Either the test is bugged or not considering factors applying to the
-tester's environment, or there is a bug in PHP. If this is a known bug, we strive to provide bug
-numbers, in either the test name or the file name. You can check the status of such a bug, by going
-to: ``https://bugs.php.net/12345`` where 12345 is the bug number. For clarity and automated
-processing, bug numbers are prefixed by a hash sign '#' in test names and/or test cases are named
-``bug12345.phpt``.
-
-**Note:** The files generated by tests can be selected by setting the environment variable
-``TEST_PHP_LOG_FORMAT``. For each file you want to be generated use the character in brackets as
-shown above (default is LEOD). The php file will be generated always.
-
-**Note**: You can set environment variable ``TEST_PHP_DETAILED`` to enable detailed test
-information.
-
-*******************
- Automated testing
-*******************
-
-If you like to keep up to speed, with latest developments and quality assurance, setting the
-environment variable ``NO_INTERACTION`` to 1, will not prompt the tester for any user input.
-
-Normally, the exit status of make test is zero, regardless of the results of independent tests. Set
-the environment variable ``REPORT_EXIT_STATUS`` to ``1``, and make test will set the exit status
-("$?") to non-zero, when an individual test has failed.
-
-Example script to be run by cron:
-
-.. code:: shell
-
- ========== qa-test.sh =============
- #!/bin/sh
-
- CO_DIR=$HOME/cvs/php7
- MYMAIL=qa-test@domain.com
- TMPDIR=/var/tmp
- TODAY=`date +"%Y%m%d"`
-
- # Make sure compilation environment is correct
- CONFIGURE_OPTS='--disable-all --enable-cli --with-pcre'
- export MAKE=gmake
- export CC=gcc
-
- # Set test environment
- export NO_INTERACTION=1
- export REPORT_EXIT_STATUS=1
-
- cd $CO_DIR
- cvs update . >>$TMPDIR/phpqatest.$TODAY
- ./cvsclean ; ./buildconf ; ./configure $CONFIGURE_OPTS ; $MAKE
- $MAKE test >>$TMPDIR/phpqatest.$TODAY 2>&1
- if test $? -gt 0
- then
- cat $TMPDIR/phpqatest.$TODAY | mail -s"PHP-QA Test Failed for $TODAY" $MYMAIL
- fi
- ========== end of qa-test.sh =============
-
-**Note:** The exit status of ``run-tests.php`` will be ``1`` when ``REPORT_EXIT_STATUS`` is set. The
-result of make test may be higher than that. At present, gmake 3.79.1 returns 2, so it is advised to
-test for non-zero, rather then a specific value.
-
-When ``make test`` finished running tests, and if there are any failed tests, the script asks to
-send the logs to the PHP QA mailing list. Please answer ``y`` to this question so that we can
-efficiently process the results, entering your e-mail address (which will not be transmitted in
-plain text to any list) enables us to ask you some more information if a test failed. Note that this
-script also uploads php -i output so your hostname may be transmitted.
-
-Specific tests can also be executed, like running tests for a certain extension. To do this you can
-do like so (for example the standard library):
-
-.. code:: shell
-
- make test TESTS=ext/standard.
-
-Where ``TESTS=`` points to a directory containing .phpt files or a single .phpt file like:
-
-.. code:: shell
-
- make test TESTS=tests/basic/001.phpt.
-
-You can also pass options directly to the underlying script that runs the test suite
-(``run-tests.phpt``) using ``TESTS=``, for example to check for memory leaks using Valgrind, the
-``-m`` option can be passed along: ``make test TESTS="-m Zend/"``. For a full list of options that
-can be passed along, then run ``make test TESTS=-h``.
-
-*Windows users:* On Windows the ``make`` command is called ``nmake`` instead of ``make``. This means
-that on Windows you will have to run ``nmake test``, to run the test suite.
diff --git a/docs/source/miscellaneous/stubs.md b/docs/source/miscellaneous/stubs.md
new file mode 100644
index 000000000000..ff9b4e633a56
--- /dev/null
+++ b/docs/source/miscellaneous/stubs.md
@@ -0,0 +1,742 @@
+# Stubs
+
+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:
+
+```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
+
+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:
+
+```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` preprocessor 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:
+
+```php
+= ...)` conditions in the generated arginfo file:
+
+```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_Elephant(void)
+{
+ zend_class_entry ce, *class_entry;
+
+ 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 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_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_name_name, &property_name_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING));
+#endif
+ zend_string_release(property_name_name);
+
+ return class_entry;
+}
+```
+
+The preprocessor conditions are necessary because enumerations (`enum`), `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
+
+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
+default, scalar return types have a `refcount` of `0`, while non-scalar values are `N`. If a
+function can only return newly created non-scalar values, its `refcount` can be set to `1`.
+
+An example from the built-in functions:
+
+```php
+/**
+ * @return array
+ * @refcount 1
+ */
+function get_declared_classes(): array {}
+```
+
+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.
+
+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,
+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.
+
+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:
+
+```php
+/**
+ * @compile-time-eval
+ * @frameless-function {"arity": 2}
+ * @frameless-function {"arity": 3}
+ */
+function in_array(mixed $needle, array $haystack, bool $strict = false): bool {}
+```
+
+Apart from being compile-time evaluable, it has a frameless function counterpart for both the 2 and
+the 3-parameter signatures:
+
+```c
+/* The regular in_array() function */
+PHP_FUNCTION(in_array)
+{
+ php_search_array(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
+}
+
+/* The frameless version of the in_array() function when 2 arguments are passed */
+ZEND_FRAMELESS_FUNCTION(in_array, 2)
+{
+ zval *value, *array;
+
+ Z_FLF_PARAM_ZVAL(1, value);
+ Z_FLF_PARAM_ARRAY(2, array);
+
+ _php_search_array(return_value, value, array, false, 0);
+
+flf_clean:;
+}
+
+/* The frameless version of the in_array() function when 3 arguments are passed */
+ZEND_FRAMELESS_FUNCTION(in_array, 3)
+{
+ zval *value, *array;
+ bool strict;
+
+ Z_FLF_PARAM_ZVAL(1, value);
+ Z_FLF_PARAM_ARRAY(2, array);
+ Z_FLF_PARAM_BOOL(3, strict);
+
+ _php_search_array(return_value, value, array, strict, 0);
+
+flf_clean:;
+}
+```
+
+## Generating Signatures for the Manual
+
+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.
+
+Since these are stub documentation pages, many of the sections are empty. Relevant descriptions have
+to be added, and irrelevant sections should be removed.
+
+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.
+
+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. 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
+
+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:
+
+```php
+/**
+ * @param resource $bz
+ * @implementation-alias fwrite
+ * @no-verify Uses different parameter name
+ */
+function bzwrite($bz, string $data, ?int $length = null): int|false {}
+```
+
+Besides aliases, the contents of the documentation can also be validated by providing the
+`--verify-manual` option to `gen_stub.php`. This flag requires the directory with the source
+stubs, and the target manual directory, as in `./build/gen_stub.php --verify-manual ./ ../doc-en/`.
+
+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:
+
+```shell
+Warning: Missing class synopsis for Number
+Warning: Missing class synopsis for Elephant
+Warning: Missing class synopsis for Atmosphere
+Warning: Missing method synopsis for fahrenheitToCelsius()
+Warning: Missing method synopsis for Atmosphere::calculateBar()
+```
+
+## Parameter Statistics
+
+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.
diff --git a/docs/source/miscellaneous/stubs.rst b/docs/source/miscellaneous/stubs.rst
deleted file mode 100644
index 395034afe8d5..000000000000
--- a/docs/source/miscellaneous/stubs.rst
+++ /dev/null
@@ -1,778 +0,0 @@
-#######
- Stubs
-#######
-
-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
-********************************************
-
-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`` preprocessor 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_Elephant(void)
- {
- zend_class_entry ce, *class_entry;
-
- 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 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_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_name_name, &property_name_default_value, ZEND_ACC_PUBLIC, NULL, (zend_type) ZEND_TYPE_INIT_MASK(MAY_BE_STRING));
- #endif
- zend_string_release(property_name_name);
-
- return class_entry;
- }
-
-The preprocessor conditions are necessary because enumerations (``enum``), ``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
-******************************************
-
-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
-default, scalar return types have a ``refcount`` of ``0``, while non-scalar values are ``N``. If a
-function can only return newly created non-scalar values, its ``refcount`` can be set to ``1``.
-
-An example from the built-in functions:
-
-.. code:: php
-
- /**
- * @return array
- * @refcount 1
- */
- function get_declared_classes(): array {}
-
-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.
-
-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,
-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.
-
-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
-
- /**
- * @compile-time-eval
- * @frameless-function {"arity": 2}
- * @frameless-function {"arity": 3}
- */
- function in_array(mixed $needle, array $haystack, bool $strict = false): bool {}
-
-Apart from being compile-time evaluable, it has a frameless function counterpart for both the 2 and
-the 3-parameter signatures:
-
-.. code:: c
-
- /* The regular in_array() function */
- PHP_FUNCTION(in_array)
- {
- php_search_array(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
- }
-
- /* The frameless version of the in_array() function when 2 arguments are passed */
- ZEND_FRAMELESS_FUNCTION(in_array, 2)
- {
- zval *value, *array;
-
- Z_FLF_PARAM_ZVAL(1, value);
- Z_FLF_PARAM_ARRAY(2, array);
-
- _php_search_array(return_value, value, array, false, 0);
-
- flf_clean:;
- }
-
- /* The frameless version of the in_array() function when 3 arguments are passed */
- ZEND_FRAMELESS_FUNCTION(in_array, 3)
- {
- zval *value, *array;
- bool strict;
-
- Z_FLF_PARAM_ZVAL(1, value);
- Z_FLF_PARAM_ARRAY(2, array);
- Z_FLF_PARAM_BOOL(3, strict);
-
- _php_search_array(return_value, value, array, strict, 0);
-
- flf_clean:;
- }
-
-**************************************
- Generating Signatures for the Manual
-**************************************
-
-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.
-
-Since these are stub documentation pages, many of the sections are empty. Relevant descriptions have
-to be added, and irrelevant sections should be removed.
-
-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.
-
-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. 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
-************
-
-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
-
- /**
- * @param resource $bz
- * @implementation-alias fwrite
- * @no-verify Uses different parameter name
- */
- function bzwrite($bz, string $data, ?int $length = null): int|false {}
-
-Besides aliases, the contents of the documentation can also be validated by providing the
-``--verify-manual`` option to ``gen_stub.php``. This flag requires the directory with the source
-stubs, and the target manual directory, as in ``./build/gen_stub.php --verify-manual ./
-../doc-en/``.
-
-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 fahrenheitToCelsius()
- Warning: Missing method synopsis for Atmosphere::calculateBar()
-
-**********************
- Parameter Statistics
-**********************
-
-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.
diff --git a/docs/source/miscellaneous/writing-tests.md b/docs/source/miscellaneous/writing-tests.md
new file mode 100644
index 000000000000..cee965a6ec9a
--- /dev/null
+++ b/docs/source/miscellaneous/writing-tests.md
@@ -0,0 +1,2773 @@
+# Writing Tests
+
+## phpt Test Basics
+
+The first thing you need to know about tests is that we need more!!! Although PHP works just great
+99.99% of the time, not having a very comprehensive test suite means that we take more risks every
+time we add to or modify the PHP implementation. The second thing you need to know is that if you
+can write PHP you can write tests. Thirdly — we are a friendly and welcoming community, don't be
+scared about writing to ([php-qa@lists.php.net](mailto:php-qa@lists.php.net)) — we won't bite!
+
+So what are phpt tests?
+
+> A phpt test is a little script used by the php internal and quality assurance teams to test PHP's
+> functionality. It can be used with new releases to make sure they can do all the things that
+> previous releases can, or to help find bugs in current releases. By writing phpt tests you are
+> helping to make PHP more stable.
+
+What skills are needed to write a phpt test?
+
+> All that is really needed to write a phpt test is a basic understanding of the PHP language, a
+> text editor, and a way to get the results of your code. That is it. So if you have been writing
+> and running PHP scripts already — you have everything you need.
+
+What do you write phpt tests on?
+
+> Basically you can write a phpt test on one of the various php functions available. You can write
+> a test on a basic language function (a string function or an array function) , or a function
+> provided by one of PHP's numerous extensions (a mysql function or a image function or a mcrypt
+> function).
+>
+> You can find out what functions already have phpt tests by looking in the [html version](https://github.com/php/php-src) of the git repository (`ext/standard/tests/` is a good place
+> to start looking — though not all the tests currently written are in there).
+>
+> If you want more guidance than that you can always ask the PHP Quality Assurance Team on their
+> mailing list ([php-qa@lists.php.net](mailto:php-qa@lists.php.net)) where they would like you to direct your attentions.
+
+How is a phpt test used?
+
+> When a test is called by the `run-tests.php` script it takes various parts of the phpt file to
+> name and create a .php file. That .php file is then executed. The output of the .php file is then
+> compared to a different section of the phpt file. If the output of the script "matches" the
+> output provided in the phpt script — it passes.
+
+What should a phpt test do?
+
+> Basically — it should try and break the PHP function. It should check not only the functions
+> normal parameters, but it should also check edge cases. Intentionally generating an error is
+> allowed and encouraged.
+
+## Writing phpt Tests
+
+### Naming Conventions
+
+Phpt tests follow a very strict naming convention. This is done to easily identify what each phpt
+test is for. Tests should be named according to the following list:
+
+- Tests for bugs
+ - `bug.phpt` (`bug17123.phpt`)
+- Tests for a function's basic behaviour
+ - `_basic.phpt` (`dba_open_basic.phpt`)
+- Tests for a function's error behaviour
+ - `_error.phpt` (`dba_open_error.phpt`)
+- Tests for variations in a function's behaviour
+ - `_variation.phpt` (`dba_open_variation.phpt`)
+- General tests for extensions
+ - `.phpt` (`dba_003.phpt`)
+
+The convention of using \_basic, \_error and \_variation was introduced when we found that writing a
+single test case for each function resulted in unacceptably large test cases. It's quite hard to
+debug problems when the test case generates 100s of lines of output.
+
+The "basic" test case for a function should just address the single most simple thing that the
+function is designed to do. For example, if writing a test for the sin() function a basic test would
+just be to check that sin() returns the correct values for some known angles — eg 30, 90, 180.
+
+The "error" tests for a function are test cases which are designed to provoke errors, warnings or
+notices. There can be more than one error case, if so the convention is to name the test cases
+mytest_error1.phpt, mytest_error2.phpt and so on.
+
+The "variation" tests are any tests that don't fit into "basic" or "error" tests. For example one
+might use a variation tests to test boundary conditions.
+
+### How big is a test case?
+
+Small. Really — the smaller the better, a good guide is no more than 10 lines of output. The reason
+for this is that if we break something in PHP and it breaks your test case we need to be able to
+find out quite quickly what we broke, going through 1000s of line of test case output is not easy.
+Having said that it's sometimes just not practical to stay within the 10 line guideline, in this
+case you can help a lot by commenting the output. You may find plenty of much longer tests in PHP -
+the small tests message is something that we learnt over time, in fact we are slowly going through
+and splitting tests up when we need to.
+
+### Comments
+
+Comments help. Not an essay — just a couple of lines on what the objective of the test is. It may
+seem completely obvious to you as you write it, but it might not be to someone looking at it later
+on.
+
+### Basic Format
+
+A test must contain the sections TEST, FILE and either EXPECT or EXPECTF at a minimum. The example
+below illustrates a minimal test.
+
+*ext/standard/tests/strings/strtr.phpt*
+
+```php
+--TEST--
+strtr() function — basic test for strtr()
+--FILE--
+"hi", "hi"=>"hello", "a"=>"A", "world"=>"planet");
+var_dump(strtr("# hi all, I said hello world! #", $trans));
+?>
+--EXPECT--
+string(32) "# hello All, I sAid hi planet! #"
+```
+
+As you can see the file is divided into several sections. The TEST section holds a one line title of
+the phpt test, this should be a simple description and shouldn't ever exceed one line, if you need
+to write more explanation add comments in the body of the test case. The phpt files name is used
+when generating a .php file. The FILE section is used as the body of the .php file, so don't forget
+to open and close your php tags. The EXPECT section is the part used as a comparison to see if the
+test passes. It is a good idea to generate output with var_dump() calls.
+
+### PHPT structure details
+
+A phpt test can have many more parts than just the minimum. In fact some of the mandatory parts have
+alternatives that may be used if the situation warrants it. The phpt sections are documented here.
+
+### Analyzing failing tests
+
+While writing tests you will probably run into tests not passing while you think they should. The
+'make test' command provides you with debug information. Several files will be added per test in the
+same directory as the .phpt file itself. Considering your test file is named foo.phpt, these files
+provide you with information that can help you find out what went wrong:
+
+foo.diff
+
+> A diff file between the expected output (be it in EXPECT, EXPECTF or another option) and the
+> actual output.
+
+foo.exp
+
+> The expected output.
+
+foo.log
+
+> A log containing expected output, actual output and results. Most likely very similar to info in
+> the other files.
+
+foo.out
+
+> The actual output of your .phpt test part.
+
+foo.php
+
+> The php code that was executed for this test.
+
+foo.sh
+
+> An executable file that executes the test for you as it was executed during failure.
+
+### Testing your test cases
+
+Most people who write tests for PHP don't have access to a huge number of operating systems but the
+tests are run on every system that runs PHP. It's good to test your test on as many platforms as you
+can — Linux and Windows are the most important, it's increasingly important to make sure that tests
+run on 64 bit as well as 32 bit platforms. If you only have access to one operating system — don't
+worry, if you have karma, commit the test but watch [php-qa@lists.php.net](mailto:php-qa@lists.php.net) for reports of failures on
+other platforms. If you don't have karma to commit have a look at the next section.
+
+When you are testing your test case it's really important to make sure that you clean up any
+temporary resources (eg files) that you used in the test. There is a special `--CLEAN--` section
+to help you do this — see [here](#--clean--).
+
+Tests run in parallel by default. Mutable resources such as files, directories, ports, database
+objects, and IPC identifiers must therefore be unique to each test. Read-only fixtures may be
+shared. If a resource cannot be isolated, declare the narrowest applicable conflict using
+`--CONFLICTS--` or a `CONFLICTS` file.
+
+Another good check is to look at what lines of code in the PHP source your test case covers. This is
+easy to do, there are some instructions on the [PHP Wiki](https://wiki.php.net/doc/articles/writing-tests).
+
+### What should I do with my test case when I've written and tested it?
+
+The next step is to get someone to review it. If it's short you can paste it into a note and send it
+to [php-qa@lists.php.net](mailto:php-qa@lists.php.net). If the test is a bit too long for that then put it somewhere were people
+can download it ([pastebin](https://pastebin.com/) is sometimes used). Appending tests to notes as
+files doesn't work well - so please don't do that. Your note to [php-qa@lists.php.net](mailto:php-qa@lists.php.net) should say what
+level of PHP you have tested it on and what platform(s) you've run it on. Someone from the PHP QA
+group will review your test and reply to you. They may ask for some changes or suggest better ways
+to do things, or they may commit it to PHP.
+
+### Writing Portable PHP Tests
+
+Writing portable tests can be hard if you don't have access to all the many platforms that PHP can
+run on. Do your best. If in doubt, don't disable a test. It is better that the test runs in as many
+environments as possible.
+
+If you know a new test won't run in a specific environment, try to write the complementary test for
+that environment.
+
+Make sure sets of data are consistently ordered. SQL queries are not guaranteed to return results in
+the same order unless an ORDER BY clause is used. Directory listings are another example that can
+vary: use an appropriate PHP function to sort them before printing. Both of these examples have
+affected PHP tests in the past.
+
+Make sure that any test touching parsing or display of dates uses a hard-defined timezone —
+preferable 'UTC'. It is important that this is defined in the file section using:
+
+```php
+date_default_timezone_set('UTC');
+```
+
+and not in the INI section. This is because of the order in which settings are checked which is:
+
+```
+date_default_timezone_set() -> TZ environmental -> INI setting -> System Setting
+```
+
+If a TZ environmental variable is found the INI setting will be ignored.
+
+Tests that run, or only have matching EXPECT output, on 32bit platforms can use a SKIPIF section
+like:
+
+```php
+--SKIPIF--
+
+```
+
+Tests for 64bit platforms can use:
+
+```php
+--SKIPIF--
+
+```
+
+To run a test only on Windows:
+
+```php
+--SKIPIF--
+
+```
+
+To run a test only on Linux:
+
+```php
+--SKIPIF--
+
+```
+
+To skip a test on Mac OS X Darwin:
+
+```php
+--SKIPIF--
+
+```
+
+## Examples
+
+### EXPECTF
+
+`/ext/standard/tests/strings/str_shuffle.phpt` is a good example for using `EXPECTF` instead of
+`EXPECT`. From time to time the algorithm used for shuffle changed and sometimes the machine used
+to execute the code has influence on the result of shuffle. But it always returns a three character
+string detectable by `%s` (that matches any string until the end of the line). Other scan-able
+forms are `%a` for any amount of chars (at least one), `%i` for integers, `%d` for numbers
+only, `%f` for floating point values, `%c` for single characters, `%x` for hexadecimal values,
+`%w` for any number of whitespace characters and `%e` for `DIRECTORY_SEPARATOR` (`'\'` or
+`'/'`).
+
+See also [EXPECTF](#expectf) details.
+
+*/ext/standard/tests/strings/str_shuffle.phpt*
+
+```php
+--TEST--
+Testing str_shuffle.
+--FILE--
+
+--EXPECTF--
+string(3) "%s"
+string(3) "123"
+```
+
+### EXPECTREGEX
+
+`/ext/standard/tests/strings/strings001.phpt` is a good example for using `EXPECTREGEX` instead
+of `EXPECT`. This test also shows that in `EXPECTREGEX` some characters need to be escaped since
+otherwise they would be interpreted as a regular expression.
+
+*/ext/standard/tests/strings/strings001.phpt*
+
+```php
+--TEST--
+Test whether strstr() and strrchr() are binary safe.
+--FILE--
+
+--EXPECTREGEX--
+string\(18\) \"nica\x00turska panica\"
+string\(19\) \" nica\x00turska panica\"
+```
+
+### EXTENSIONS
+
+Some tests depend on PHP extensions that may be unavailable. These extensions should be listed in
+the `EXTENSIONS` section. If an extension is missing, PHP will try to find it in a shared module
+and skip the test if it's not there.
+
+*/ext/sodium/tests/crypto_scalarmult.phpt*
+
+```php
+--TEST--
+Check for libsodium scalarmult
+--EXTENSIONS--
+sodium
+--FILE--
+
+--FILE--
+[snip]
+```
+
+Test script and `SKIPIF` code should be directly written into `\*.phpt`. However, it is
+recommended to use include files when more test scripts depend on the same `SKIPIF` code or when
+certain test files need the same values for some input.
+
+> [!NOTE]
+> No file used by any test should have one of the following extensions: ".php", ".log", ".mem",
+> ".exp", ".out" or ".diff". When you use an include file for the `SKIPIF` section it should be
+> named "skipif.inc" and an include file used in the `FILE` section of many tests should be named
+> "test.inc".
+
+## Final Notes
+
+### Cleaning up after running a test
+
+Sometimes test cases create files or directories as part of the test case and it's important to
+remove these after the test ends, the `--CLEAN--` section is provided to help with this.
+
+The PHP code in the `--CLEAN--` section is executed separately from the code in the `--FILE--`
+section. For example, this code:
+
+```php
+--TEST--
+Will fail to clean up
+--FILE--
+
+--CLEAN--
+
+--EXPECT--
+```
+
+will not remove the temporary file because the variable \$temp_filename is not defined in the
+`--CLEAN--` section.
+
+Here is a better way to write the code:
+
+```php
+--TEST--
+This will remove temporary files
+--FILE--
+
+--CLEAN--
+
+--EXPECT--
+```
+
+Note the use of the `__DIR__` construct which will ensure that the temporary file is created in
+the same directory as the phpt test script.
+
+When creating temporary files it is a good idea to use an extension that indicates the use of the
+file, eg .tmp. It's also a good idea to avoid using extensions that are already used for other
+purposes, eg .inc, .php. Similarly, it is helpful to give the temporary file a name that is clearly
+related to the test case. For example, mytest.phpt should create mytest.tmp (or mytestN.tmp, N=1,
+2,3,...) then if by any chance the temporary file isnt't removed properly it will be obvious which
+test case created it.
+
+When writing and debugging a test case with a `--CLEAN--` section it is helpful to remember that
+the php code in the `--CLEAN--` section is executed separately from the code in the `--FILE--`
+section. For example, in a test case called mytest.phpt, code from the `--FILE--` section is run
+from a file called mytest.php and code from the `--CLEAN--` section is run from a file called
+mytest.clean.php. If the test passes, both the .php and .clean.php files are removed by
+`run-tests.php`. You can prevent the removal by using the --keep option of `run-tests.php`, this
+is a very useful option if you need to check that the `--CLEAN--` section code is working as you
+intended.
+
+Finally — if you are using CVS it's helpful to add the extension that you use for test-related
+temporary files to the .cvsignore file — this will help to prevent you from accidentally checking
+temporary files into CVS.
+
+### Redirecting tests
+
+Using `--REDIRECTTEST--` it is possible to redirect from one test to a bunch of other tests. That
+way multiple extensions can refer to the same set of test scripts probably using it with a different
+configuration.
+
+The block is eval'd and supposed to return an array describing how to redirect. The resulting array
+must contain the key 'TEST' that stores the redirect target as a string. This string usually is the
+directory where the test scripts are located and should be relative. Optionally you can use the
+'ENV' as an array configuring the environment to be set when executing the tests. This way you can
+pass configuration to the executed tests.
+
+Redirect tests may especially contain `--SKIPIF--`, `--ENV--`, and `--ARGS--` sections but
+they no not use any `--EXPECT--` section.
+
+The redirected tests themselves are just normal tests.
+
+### Error reporting in tests
+
+All tests should run correctly with error_reporting(E_ALL) and display_errors=1. This is the default
+when called from `run-tests.php`. If you have a good reason for lowering the error reporting, use
+`--INI--` section and comment this in your testcode.
+
+If your test intentionally generates a PHP warning message use \$php_errormsg variable, which you can
+then output. This will result in a consistent error message output across all platforms and PHP
+configurations, preventing your test from failing due inconsistencies in the error message content.
+Alternatively you can use `--EXPECTF--` and check for the message by replacing the path of the
+source of the message with `%s` and the line number with `%d`. The end of a message in a test
+file `example.phpt` then looks like `in %sexample.php on line %d`. We explicitly dropped the
+last path divider as that is a system dependent character `/` or `\`.
+
+### Last bit
+
+Often you want to run test scripts without `run-tests.php` by executing them on command line like
+any other php script. But sometimes it disturbs having a long `--EXPECT--` block, so that you
+don't see the actual output as it scrolls away overwritten by the blocks following the actual file
+block. The workaround is to use terminate the `--FILE--` section with the two lines `===DONE===`
+and ``. When doing so `run-tests.php` does not execute the line containing the
+exit call as that would suppress leak messages. Actually `run-tests.php` ignores any part after a
+line consisting only of `===DONE===`.
+
+Here is an example:
+
+```php
+--TEST--
+Test hypot() — dealing with mixed number/character input
+--INI--
+precision=14
+--FILE--
+
+===DONE===
+
+--EXPECTF--
+23abc :-33 float(40.224370722238)
+===DONE===
+```
+
+If executed as PHP script the output will stop after the code on the `--FILE--` section has been
+run.
+
+## Reference
+
+### PHPT Sections
+
+#### `--TEST--`
+
+**Description:** Title of test as a single line short description.
+
+**Required:** Yes
+
+**Format:** Plain text. We recommend a single line only.
+
+Example 1 (snippet):
+
+```text
+--TEST--
+Test filter_input() with GET and POST data.
+```
+
+Example 1 (full): {ref}`sample001.phpt`
+
+#### `--DESCRIPTION--`
+
+**Description:** If your test requires more than a single line title to adequately describe it, you
+can use this section for further explanation. Multiple lines are allowed and besides being used for
+information, this section is completely ignored by the test binary.
+
+**Required:** No
+
+**Format:** Plain text, multiple lines.
+
+Example 1 (snippet):
+
+```text
+--DESCRIPTION--
+This test covers both valid and invalid usages of filter_input() with INPUT_GET and INPUT_POST data and several different filter sanitizers.
+```
+
+Example 1 (full): {ref}`sample001.phpt`
+
+#### `--CREDITS--`
+
+**Description:** Used to credit contributors without CVS commit rights, who put their name and email
+on the first line. If the test was part of a TestFest event, then # followed by the name of the
+event and the date (YYYY-MM-DD) on the second line.
+
+**Required:** No. For newly created tests the section should no longer be used for simple authorship
+claims or listing all contributors who edited the test; as it is already accurately tracked by Git.
+It may be used if more specific attribution is useful, for example to credit the original reporter
+of a bug or a contributor who is not credited via `Co-authored-by` tag.
+
+**Format:** Name Email [Event]
+
+Example 1 (snippet):
+
+```text
+--CREDITS--
+Felipe Pena
+```
+
+Example 1 (full): {ref}`sample001.phpt`
+
+Example 2 (snippet):
+
+```text
+--CREDITS--
+Zoe Slattery zoe@php.net
+# TestFest Munich 2009-05-19
+```
+
+Example 2 (full): {ref}`sample002.phpt`
+
+#### `--SKIPIF--`
+
+**Description:** A condition or set of conditions used to determine if a test should be skipped.
+Tests that are only applicable to a certain platform, extension or PHP version are good reasons for
+using a `--SKIPIF--` section.
+
+A common practice for extension tests is to write your `--SKIPIF--` extension criteria into a file
+call skipif.inc and then including that file in the `--SKIPIF--` section of all your extension
+tests. This promotes the DRY principle and reduces future code maintenance.
+
+**Required:** No.
+
+**Format:** PHP code enclosed by PHP tags. If the output of this scripts starts with "skip", the
+test is skipped. If the output starts with "xfail", the test is marked as expected failure. If the
+output starts with "flaky", the test is marked as flaky test. The "xfail" convention is supported as
+of PHP 7.2.0. The "flaky" convention is supported as of PHP 8.2.25 and PHP 8.3.13, respectively.
+
+Example 1 (snippet):
+
+```php
+--SKIPIF--
+
+```
+
+Example 1 (full): {ref}`sample001.phpt`
+
+Example 2 (snippet):
+
+```php
+--SKIPIF--
+
+```
+
+Example 2 (full): {ref}`sample003.phpt`
+
+Example 3 (snippet):
+
+```php
+--SKIPIF--
+
+```
+
+Example 3 (full): {ref}`xfailif.phpt`
+
+Example 4 (snippet):
+
+```php
+--SKIPIF--
+string&d=12345.7
+```
+
+Example 1 (full): {ref}`sample001.phpt`
+
+Example 2 (snippet):
+
+```xml
+--POST--
+
+
+
+
+
+```
+
+Example 2 (full): {ref}`sample005.phpt`
+
+#### `--POST_RAW--`
+
+**Description:** Raw POST data to be passed to the test script. This differs from the section above
+because it doesn't automatically set the Content-Type, this leaves you free to define your own
+within the section. This section forces the use of the CGI binary instead of the usual CLI one.
+
+**Required:** No.
+
+Requirements: PHP CGI binary.
+
+**Test Script Support:** `run-tests.php`
+
+**Format:** Follows the HTTP post data format.
+
+Example 1 (snippet):
+
+```text
+--POST_RAW--
+Content-type: multipart/form-data, boundary=AaB03x
+
+--AaB03x content-disposition: form-data; name="field1"
+
+Joe Blow
+--AaB03x
+content-disposition: form-data; name="pics"; filename="file1.txt"
+Content-Type: text/plain
+
+abcdef123456789
+--AaB03x--
+```
+
+Example 1 (full): {ref}`sample006.phpt`
+
+#### `--PUT--`
+
+**Description:** Similar to the section above, PUT data to be passed to the test script. This
+section forces the use of the CGI binary instead of the usual CLI one.
+
+**Required:** No.
+
+Requirements: PHP CGI binary.
+
+**Test Script Support:** `run-tests.php`
+
+**Format:** Raw data optionally preceded by a Content-Type header.
+
+Example 1 (snippet):
+
+```text
+--PUT--
+Content-Type: text/json
+
+{"name":"default output handler","type":0,"flags":112,"level":0,"chunk_size":0,"buffer_size":16384,"buffer_used":3}
+```
+
+#### `--GZIP_POST--`
+
+**Description:** When this section exists, the POST data will be gzencode()'d. This section forces
+the use of the CGI binary instead of the usual CLI one.
+
+**Required:** No.
+
+**Test Script Support:** `run-tests.php`
+
+**Format:** Just add the content to be gzencode()'d in the section.
+
+Example 1 (snippet):
+
+```xml
+--GZIP_POST--
+
+
+
+
+
+```
+
+Example 1 (full): {ref}`sample005.phpt`
+
+#### `--DEFLATE_POST--`
+
+**Description:** When this section exists, the POST data will be gzcompress()'ed. This section
+forces the use of the CGI binary instead of the usual CLI one.
+
+**Required:** No.
+
+Requirements:
+
+**Test Script Support:** `run-tests.php`
+
+**Format:** Just add the content to be gzcompress()'ed in the section.
+
+Example 1 (snippet):
+
+```xml
+--DEFLATE_POST--
+
+
+
+
+
+
+```
+
+Example 1 (full): {ref}`sample007.phpt`
+
+#### `--GET--`
+
+**Description:** GET variables to be passed to the test script. This section forces the use of the
+CGI binary instead of the usual CLI one.
+
+**Required:** No.
+
+Requirements: PHP CGI binary.
+
+**Format:** A single line of text passed as the GET data to the script.
+
+Example 1 (snippet):
+
+```text
+--GET--
+a=test&b=http://example.com
+```
+
+Example 1 (full): {ref}`sample001.phpt`
+
+Example 2 (snippet):
+
+```text
+--GET--
+ar[elm1]=1234&ar[elm2]=0660&a=0234
+```
+
+Example 2 (full): {ref}`sample008.phpt`
+
+#### `--COOKIE--`
+
+**Description:** Cookies to be passed to the test script. This section forces the use of the CGI
+binary instead of the usual CLI one.
+
+**Required:** No.
+
+Requirements: PHP CGI binary.
+
+**Test Script Support:** `run-tests.php`
+
+**Format:** A single line of text in a valid HTTP cookie format.
+
+Example 1 (snippet):
+
+```
+--COOKIE--
+hello=World;goodbye=MrChips
+```
+
+Example 1 (full): {ref}`sample002.phpt`
+
+#### `--STDIN--`
+
+**Description:** Data to be fed to the test script's standard input.
+
+**Required:** No.
+
+**Test Script Support:** `run-tests.php`
+
+**Format:** Any text within this section is passed as STDIN to PHP.
+
+Example 1 (snippet):
+
+```text
+--STDIN--
+fooBar
+use this to input some thing to the php script
+```
+
+Example 1 (full): {ref}`sample009.phpt`
+
+#### `--INI--`
+
+**Description:** To be used if you need a specific php.ini setting for the test.
+
+**Required:** No.
+
+**Format:** Key value pairs including automatically replaced tags. One setting per line. Content
+that is not a valid ini setting may cause failures.
+
+The following is a list of all tags and what they are used to represent:
+
+- `{PWD}`: Represents the directory of the file containing the `--INI--` section.
+- `{TMP}`: Represents the system's temporary directory. Available as of PHP 7.2.19 and 7.3.6.
+
+Example 1 (snippet):
+
+```text
+--INI--
+precision=14
+```
+
+Example 1 (full): {ref}`sample001.phpt`
+
+Example 2 (snippet):
+
+```text
+--INI--
+session.use_cookies=0
+session.cache_limiter=
+register_globals=1
+session.serialize_handler=php
+session.save_handler=files
+```
+
+Example 2 (full): {ref}`sample003.phpt`
+
+#### `--ARGS--`
+
+**Description:** A single line defining the arguments passed to PHP.
+
+**Required:** No.
+
+**Format:** A single line of text that is passed as the argument(s) to the PHP CLI.
+
+Example 1 (snippet):
+
+```text
+--ARGS--
+--arg value --arg=value -avalue -a=value -a value
+```
+
+Example 1 (full): {ref}`sample010.phpt`
+
+#### `--ENV--`
+
+**Description:** Configures environment variables such as those found in the `$_SERVER` global
+array.
+
+**Required:** No.
+
+**Format:** Key value pairs. One setting per line.
+
+Example 1 (snippet):
+
+```text
+--ENV--
+SCRIPT_NAME=/frontcontroller10.php
+REQUEST_URI=/frontcontroller10.php/hi
+PATH_INFO=/hi
+```
+
+Example 1 (full): {ref}`sample018.phpt`
+
+#### `--PHPDBG--`
+
+**Description:** This section takes arbitrary phpdbg commands and executes the test file according
+to them as it would be run in the phpdbg prompt.
+
+**Required:** No.
+
+**Format:** Arbitrary phpdbg commands
+
+Example 1 (snippet):
+
+```text
+--PHPDBG--
+b
+4
+b
+del
+0
+b
+5
+r
+b
+del
+1
+r
+y
+q
+```
+
+Example 1 (full): {ref}`phpdbg_1.phpt`
+
+#### `--FILE--`
+
+**Description:** The test source code.
+
+**Required:** One of the `FILE` type sections is required.
+
+**Format:** PHP source code enclosed by PHP tags.
+
+Example 1 (snippet):
+
+```php
+--FILE--
+
+```
+
+Example 1 (full): {ref}`sample001.phpt`
+
+#### `--FILEEOF--`
+
+**Description:** An alternative to `--FILE--` where any trailing line breaks (n || r || rn
+found at the end of the section) are omitted. This is an extreme edge-case feature, so 99.99% of the
+time you won't need this section.
+
+**Required:** One of the `FILE` type sections is required.
+
+**Test Script Support:** `run-tests.php`
+
+**Format:** PHP source code enclosed by PHP tags.
+
+Example 1 (snippet):
+
+```php
+--FILEEOF--
+ array(
+ 'PDOTEST_DSN' => 'sqlite2::memory:'
+ ),
+ 'TESTS' => 'ext/pdo/tests'
+ );
+```
+
+Example 1 (full): {ref}`sample013.phpt`
+
+> [!NOTE]
+> The destination tests for this example are not included. See the PDO extension tests for
+> reference to live tests using this section.
+
+Example 2 (snippet):
+
+```php
+--REDIRECTTEST--
+# magic auto-configuration
+
+$config = array(
+ 'TESTS' => 'ext/pdo/tests'
+);
+
+if (false !== getenv('PDO_MYSQL_TEST_DSN')) {
+ # user set them from their shell
+ $config['ENV']['PDOTEST_DSN'] = getenv('PDO_MYSQL_TEST_DSN');
+ $config['ENV']['PDOTEST_USER'] = getenv('PDO_MYSQL_TEST_USER');
+ $config['ENV']['PDOTEST_PASS'] = getenv('PDO_MYSQL_TEST_PASS');
+ if (false !== getenv('PDO_MYSQL_TEST_ATTR')) {
+ $config['ENV']['PDOTEST_ATTR'] = getenv('PDO_MYSQL_TEST_ATTR');
+ }
+} else {
+ $config['ENV']['PDOTEST_DSN'] = 'mysql:host=localhost;dbname=test';
+ $config['ENV']['PDOTEST_USER'] = 'root';
+ $config['ENV']['PDOTEST_PASS'] = '';
+}
+
+return $config;
+```
+
+Example 2 (full): {ref}`sample014.phpt`
+
+> [!NOTE]
+> The destination tests for this example are not included. See the PDO extension tests for
+> reference to live tests using this section.
+
+#### `--CGI--`
+
+**Description:** This section takes no value. It merely provides a simple marker for tests that MUST
+be run as CGI, even if there is no `--POST--` or `--GET--` sections in the test file.
+
+**Required:** No.
+
+**Format:** No value, just the `--CGI--` statement.
+
+Example 1 (snippet):
+
+```text
+--CGI--
+```
+
+Example 1 (full): {ref}`sample016.phpt`
+
+#### `--XFAIL--`
+
+**Description:** This section identifies this test as one that is currently expected to fail. It
+should include a brief description of why it's expected to fail. Reasons for such expectations
+include tests that are written before the functionality they are testing is implemented or notice of
+a bug which is due to upstream code such as an extension which provides PHP support for some other
+software.
+
+Please do NOT include an `--XFAIL--` without providing a text description for the reason it's
+being used.
+
+**Required:** No.
+
+**Test Script Support:** `run-tests.php`
+
+**Format:** A short plain text description of why this test is currently expected to fail.
+
+Example 1 (snippet):
+
+```text
+--XFAIL--
+This bug might be still open on aix5.2-ppc64 and hpux11.23-ia64
+```
+
+Example 1 (full): {ref}`sample017.phpt`
+
+#### `--FLAKY--`
+
+**Description:** This section identifies this test as one that occasionally fails. If the test
+actually fails, it will be retried one more time, and that result will be reported. The section
+should include a brief description of why the test is flaky. Reasons for this include tests that
+rely on relatively precise timing, or temporary disc states. Available as of PHP 8.1.22 and 8.2.9,
+respectively.
+
+Please do NOT include a `--FLAKY--` section without providing a text description for the reason it
+is being used.
+
+**Required:** No.
+
+**Test Script Support:** `run-tests.php`
+
+**Format:** A short plain text description of why this test is flaky.
+
+Example 1 (snippet):
+
+```
+--FLAKY--
+This test frequently fails in CI
+```
+
+Example 1 (full): flaky.phpt
+
+#### `--EXPECTHEADERS--`
+
+**Description:** The expected headers. Any header specified here must exist in the response and have
+the same value or the test fails. Additional headers found in the actual tests while running are
+ignored.
+
+**Required:** No.
+
+**Format:** HTTP style headers. May include multiple lines.
+
+Example 1 (snippet):
+
+--EXPECTHEADERS-- Status: 404
+
+Example 1 (snippet):
+
+```text
+--EXPECTHEADERS--
+Content-type: text/html; charset=UTF-8
+Status: 403 Access Denied
+```
+
+Example 1 (full): {ref}`sample018.phpt`
+
+> [!NOTE]
+> The destination tests for this example are not included. See the phar extension tests for
+> reference to live tests using this section.
+
+#### `--EXPECT--`
+
+**Description:** The expected output from the test script. This must match the actual output from
+the test script exactly for the test to pass.
+
+**Required:** One of the `EXPECT` type sections is required.
+
+**Format:** Plain text. Multiple lines of text are allowed.
+
+Example 1 (snippet):
+
+```text
+--EXPECT--
+array(2) {
+ ["hello"]=>
+ string(5) "World"
+ ["goodbye"]=>
+ string(7) "MrChips"
+}
+```
+
+Example 1 (full): {ref}`sample002.phpt`
+
+#### `--EXPECT_EXTERNAL--`
+
+**Description:** Similar to `--EXPECT--` section, but just stating a filename where to load the
+expected output from.
+
+**Required:** One of the `EXPECT` type sections is required.
+
+**Test Script Support:** `run-tests.php`
+
+Example 1 (snippet):
+
+```text
+--EXPECT_EXTERNAL--
+test001.expected.txt
+```
+
+*test001.expected.txt*
+
+```php
+array(2) {
+ ["hello"]=>
+ string(5) "World"
+ ["goodbye"]=>
+ string(7) "MrChips"
+}
+```
+
+#### `--EXPECTF--`
+
+**Description:** An alternative of `--EXPECT--`. Where it differs from `--EXPECT--` is that it
+uses a number of substitution tags for strings, spaces, digits, etc. that appear in test case output
+but which may vary between test runs. The most common example of this is to use %s and %d to match
+the file path and line number which are output by PHP Warnings.
+
+**Required:** One of the `EXPECT` type sections is required.
+
+**Format:** Plain text including tags which are inserted to represent different types of output
+which are not guaranteed to have the same value on subsequent runs or when run on different
+platforms.
+
+The following is a list of all tags and what they are used to represent:
+
+> - `%e`: Represents a directory separator, for example / on Linux.
+> - `%s`: One or more of anything (character or white space) except the end of line character.
+> - `%S`: Zero or more of anything (character or white space) except the end of line character.
+> - `%a`: One or more of anything (character or white space) including the end of line
+> character.
+> - `%A`: Zero or more of anything (character or white space) including the end of line
+> character.
+> - `%w`: Zero or more white space characters.
+> - `%i`: A signed integer value, for example +3142, -3142, 3142.
+> - `%d`: An unsigned integer value, for example 123456.
+> - `%x`: One or more hexadecimal character. That is, characters in the range 0-9, a-f, A-F.
+> - `%f`: A floating point number, for example: 3.142, -3.142, 3.142E-10, 3.142e+10.
+> - `%c`: A single character of any sort (.).
+> - `%r...%r`: Any string (...) enclosed between two `%r` will be treated as a regular
+> expression.
+
+Example 1 (snippet):
+
+```text
+--EXPECTF--
+string(4) "test"
+string(18) "http://example.com"
+string(27) "<b>test</b>"
+
+Notice: Object of class stdClass could not be converted to int in %ssample001.php on line %d
+bool(false)
+string(6) "string"
+float(12345.7)
+string(29) "<p>string</p>"
+bool(false)
+
+Warning: filter_var() expects parameter 2 to be long, string given in %s011.php on line %d
+NULL
+
+Warning: filter_input() expects parameter 3 to be long, string given in %s011.php on line %d
+NULL
+
+Warning: filter_var() expects at most 3 parameters, 5 given in %s011.php on line %d
+NULL
+
+Warning: filter_var() expects at most 3 parameters, 5 given in %s011.php on line %d
+NULL
+Done
+```
+
+Example 1 (full): {ref}`sample001.phpt`
+
+Example 2 (snippet):
+
+```text
+--EXPECTF--
+Warning: bzopen() expects exactly 2 parameters, 0 given in %s on line %d NULL
+
+Warning: bzopen(): '' is not a valid mode for bzopen(). Only 'w' and 'r' are supported. in %s on line %d
+bool(false)
+
+Warning: bzopen(): filename cannot be empty in %s on line %d
+bool(false)
+
+Warning: bzopen(): filename cannot be empty in %s on line %d
+bool(false)
+
+Warning: bzopen(): 'x' is not a valid mode for bzopen(). Only 'w' and 'r' are supported. in %s on line %d
+bool(false)
+
+Warning: bzopen(): 'rw' is not a valid mode for bzopen(). Only 'w' and 'r' are supported. in %s on line %d
+bool(false)
+
+Warning: bzopen(no_such_file): failed to open stream: No such file or directory in %s on line %d
+bool(false)
+resource(%d) of type (stream) Done
+```
+
+Example 2 (full): {ref}`sample019.phpt`
+
+Example 3 (snippet):
+
+```text
+--EXPECTF--
+object(DOMNodeList)#%d (0) {
+}
+int(0)
+bool(true)
+bool(true)
+string(0) ""
+bool(true)
+bool(true)
+bool(false)
+bool(false)
+```
+
+Example 2 (full): {ref}`sample020.phpt`
+
+#### `--EXPECTF_EXTERNAL--`
+
+**Description:** Similar to `--EXPECTF--` section, but like the `--EXPECT_EXTERNAL--` section
+just stating a filename where to load the expected output from.
+
+**Required:** One of the `EXPECT` type sections is required.
+
+**Test Script Support:** `run-tests.php`
+
+#### `--EXPECTREGEX--`
+
+**Description:** An alternative of `--EXPECT--`. This form allows the tester to specify the result
+in a regular expression.
+
+**Required:** One of the `EXPECT` type sections is required.
+
+**Format:** Plain text including regular expression patterns which represent data that can vary
+between subsequent runs of a test or when run on different platforms.
+
+Example 1 (snippet):
+
+```text
+--EXPECTREGEX--
+M_E : 2.718281[0-9]*
+M_LOG2E : 1.442695[0-9]*
+M_LOG10E : 0.434294[0-9]*
+M_LN2 : 0.693147[0-9]*
+M_LN10 : 2.302585[0-9]*
+M_PI : 3.141592[0-9]*
+M_PI_2 : 1.570796[0-9]*
+M_PI_4 : 0.785398[0-9]*
+M_1_PI : 0.318309[0-9]*
+M_2_PI : 0.636619[0-9]*
+M_SQRTPI : 1.772453[0-9]*
+M_2_SQRTPI: 1.128379[0-9]*
+M_LNPI : 1.144729[0-9]*
+M_EULER : 0.577215[0-9]*
+M_SQRT2 : 1.414213[0-9]*
+M_SQRT1_2 : 0.707106[0-9]*
+M_SQRT3 : 1.732050[0-9]*
+```
+
+Example 1 (full): {ref}`sample021.phpt`
+
+Example 2 (snippet):
+
+```text
+--EXPECTF--
+*** Testing imap_append() : basic functionality ***
+Create a new mailbox for test
+Create a temporary mailbox and add 0 msgs
+.. mailbox '%s' created
+Add a couple of msgs to new mailbox {%s}INBOX.%s
+bool(true)
+bool(true)
+Msg Count after append : 2
+List the msg headers
+array(2) {
+ [0]=>
+ string(%d) "%w%s 1)%s webmaster@something. Test message (%d chars)"
+ [1]=>
+ string(%d) "%w%s 2)%s webmaster@something. Another test (%d chars)"
+}
+```
+
+Example 2 (full): {ref}`sample025.phpt`
+
+Example 3 (snippet):
+
+```text
+--EXPECTREGEX--
+string\(4\) \"-012\"
+string\(8\) \"2d303132\"
+(string\(13\) \" 4294967284\"|string\(20\) \"18446744073709551604\")
+(string\(26\) \"20202034323934393637323834\"|string\(40\) \"3138343436373434303733373039353531363034\")
+```
+
+Example 3 (full): {ref}`sample023.phpt`
+
+#### `--EXPECTREGEX_EXTERNAL--`
+
+**Description:** Similar to `--EXPECTREGEX--` section, but like the `--EXPECT_EXTERNAL--`
+section just stating a filename where to load the expected output from.
+
+**Required:** One of the `EXPECT` type sections is required.
+
+**Test Script Support:** `run-tests.php`
+
+#### `--CLEAN--`
+
+**Description:** Code that is executed after a test completes. It's main purpose is to allow you to
+clean up after yourself. You might need to remove files created during the test or close sockets or
+database connections following a test. Infact, even if a test fails or encounters a fatal error
+during the test, the code found in the `--CLEAN--` section will still run.
+
+Code in the clean section is run in a completely different process than the one the test was run in.
+So do not try accessing variables you created in the `--FILE--` section from inside the
+`--CLEAN--` section, they won't exist.
+
+Using the switch `--no-clean` on `run-tests.php`, you can prevent the code found in the
+`--CLEAN--` section of a test from running. This allows you to inspect generated data or files
+without them being removed by the `--CLEAN--` section.
+
+**Required:** No.
+
+**Test Script Support:** `run-tests.php`
+
+**Format:** PHP source code enclosed by PHP tags.
+
+Example 1 (snippet):
+
+```php
+--CLEAN--
+
+```
+
+Example 1 (full): {ref}`sample024.phpt`
+
+Example 2 (snippet):
+
+```php
+--CLEAN--
+
+```
+
+Example 2 (full): {ref}`sample025.phpt`
+
+Example 3 (snippet):
+
+```php
+--CLEAN--
+
+```
+
+Example 3 (full): {ref}`sample022.phpt`
+
+### Samples
+
+#### capture_stdio_1.phpt
+
+```php
+--TEST--
+Test covering the I/O stdin and stdout streams.
+--DESCRIPTION--
+This tests checks if the output of stdin and stdout I/O streams match the
+expected content.
+--CAPTURE_STDIO--
+STDIN STDERR
+--FILE--
+
+--EXPECT--
+This is error sent to the stderr I/O stream
+```
+
+#### capture_stdio_2.phpt
+
+```php
+--TEST--
+Test covering the I/O stdin and stderr streams.
+--DESCRIPTION--
+This tests checks if the output of stdin and stderr I/O streams match the
+expected content.
+--CAPTURE_STDIO--
+STDIN STDOUT
+--FILE--
+
+--EXPECT--
+Hello, world. This is sent to the stdout I/O stream
+```
+
+#### capture_stdio_3.phpt
+
+```php
+--TEST--
+Test covering the all standard I/O streams.
+--DESCRIPTION--
+This tests checks if the output of stdin, stdout and stderr I/O streams match
+the expected content.
+--CAPTURE_STDIO--
+STDIN STDOUT STDERR
+--FILE--
+
+--EXPECT--
+Hello, world. This is sent to the stdout I/O stream
+This is error sent to the stderr I/O stream
+```
+
+#### clean.php
+
+```php
+Nmsgs; $i++) {
+ imap_delete($imap_stream, $i);
+}
+
+$mailboxes = imap_getmailboxes($imap_stream, $server, '*');
+
+foreach($mailboxes as $value) {
+ // Only delete mailboxes with our prefix
+ if (preg_match('/\{.*?\}INBOX\.(.+)/', $value->name, $match) == 1) {
+ if (strlen($match[1]) >= strlen($mailbox_prefix)
+ && substr_compare($match[1], $mailbox_prefix, 0, strlen($mailbox_prefix)) == 0) {
+ imap_deletemailbox($imap_stream, $value->name);
+ }
+ }
+}
+
+imap_close($imap_stream, CL_EXPUNGE);
+?>
+```
+
+#### conflicts_1.phpt
+
+```php
+--TEST--
+Test get_headers() function : test with context
+--CONFLICTS--
+server
+--FILE--
+ array(
+ 'method' => 'HEAD'
+ )
+);
+
+$context = stream_context_create($opts);
+$headers = get_headers("http://".PHP_CLI_SERVER_ADDRESS, 1, $context);
+echo $headers["X-Request-Method"]."\n";
+
+stream_context_set_default($opts);
+$headers = get_headers("http://".PHP_CLI_SERVER_ADDRESS, 1);
+echo $headers["X-Request-Method"]."\n";
+
+echo "Done";
+?>
+--EXPECT--
+HEAD
+HEAD
+Done
+```
+
+#### extensions.phpt
+
+```php
+--TEST--
+phpt EXTENSIONS directive with shared extensions
+--DESCRIPTION--
+This test covers the presence of some loaded extensions with a list of additional
+extensions to be loaded when running test.
+--EXTENSIONS--
+curl
+imagick
+tokenizer
+--FILE--
+
+--EXPECT--
+bool(true)
+bool(true)
+bool(true)
+```
+
+#### file012.phpt
+
+```php
+
+```
+
+#### phpdbg_1.phpt
+
+```php
+--TEST--
+Test deleting breakpoints
+--PHPDBG--
+b 4
+b del 0
+b 5
+r
+b del 1
+r
+y
+q
+--EXPECTF--
+[Successful compilation of %s]
+prompt> [Breakpoint #0 added at %s:4]
+prompt> [Deleted breakpoint #0]
+prompt> [Breakpoint #1 added at %s:5]
+prompt> 12
+[Breakpoint #1 at %s:5, hits: 1]
+>00005: echo $i++;
+ 00006: echo $i++;
+ 00007:
+prompt> [Deleted breakpoint #1]
+prompt> Do you really want to restart execution? (type y or n): 1234
+[Script ended normally]
+prompt>
+--FILE--
+
+--INI--
+precision=14
+--SKIPIF--
+
+--GET--
+a=test&b=https://example.com
+--POST--
+c=
string
&d=12345.7
+--FILE--
+
+--EXPECTF--
+string(4) "test"
+string(19) "https://example.com"
+string(27) "<b>test</b>"
+
+Notice: Object of class stdClass could not be converted to int in %ssample001.php on line %d
+bool(false)
+string(6) "string"
+float(12345.7)
+string(29) "<p>string</p>"
+bool(false)
+
+Warning: filter_var() expects parameter 2 to be long, string given in %ssample001.php on line %d
+NULL
+
+Warning: filter_input() expects parameter 3 to be long, string given in %ssample001.php on line %d
+NULL
+
+Warning: filter_var() expects at most 3 parameters, 5 given in %ssample001.php on line %d
+NULL
+
+Warning: filter_var() expects at most 3 parameters, 5 given in %ssample001.php on line %d
+NULL
+Done
+```
+
+#### sample002.phpt
+
+```php
+--TEST--
+Test receipt of cookie data.
+--CREDITS--
+Zoe Slattery zoe@php.net
+# TestFest Munich 2009-05-19
+--COOKIE--
+hello=World;goodbye=MrChips
+--FILE--
+
+--EXPECT--
+array(2) {
+ ["hello"]=>
+ string(5) "World"
+ ["goodbye"]=>
+ string(7) "MrChips"
+}
+```
+
+#### sample003.phpt
+
+```php
+--TEST--
+session object deserialization
+--SKIPIF--
+
+--INI--
+session.use_cookies=0
+session.cache_limiter=
+register_globals=1
+session.serialize_handler=php
+session.save_handler=files
+--FILE--
+yes++; }
+}
+
+session_id("abtest");
+session_start();
+session_decode('baz|O:3:"foo":2:{s:3:"bar";s:2:"ok";s:3:"yes";i:1;}arr|a:1:{i:3;O:3:"foo":2:{s:3:"bar";s:2:"ok";s:3:"yes";i:1;}}');
+
+$baz->method();
+$arr[3]->method();
+
+var_dump($baz);
+var_dump($arr);
+session_destroy();
+--EXPECT--
+object(foo)#1 (2) {
+ ["bar"]=>
+ string(2) "ok"
+ ["yes"]=>
+ int(2)
+}
+array(1) {
+ [3]=>
+ object(foo)#2 (2) {
+ ["bar"]=>
+ string(2) "ok"
+ ["yes"]=>
+ int(2)
+ }
+}
+```
+
+#### sample005.phpt
+
+```php
+--TEST--
+SOAP Server 19: compressed request (gzip)
+--SKIPIF--
+
+--INI--
+precision=14
+--GZIP_POST--
+
+
+
+
+
+--FILE--
+"http://testuri.org"));
+$server->addfunction("test");
+$server->handle();
+echo "ok\n";
+?>
+--EXPECT--
+
+Hello World
+ok
+```
+
+#### sample006.phpt
+
+```php
+--TEST--
+is_uploaded_file() function
+--CREDITS--
+Dave Kelsey
+--SKIPIF--
+
+--POST_RAW--
+Content-type: multipart/form-data, boundary=AaB03x
+
+--AaB03x
+content-disposition: form-data; name="field1"
+
+Joe Blow
+--AaB03x
+content-disposition: form-data; name="pics"; filename="file1.txt"
+Content-Type: text/plain
+
+abcdef123456789
+--AaB03x--
+--FILE--
+
+--EXPECTF--
+bool(true)
+bool(false)
+bool(false)
+bool(false)
+
+Warning: is_uploaded_file() expects exactly 1 parameter, 0 given in %s on line %d
+NULL
+
+Warning: is_uploaded_file() expects exactly 1 parameter, 2 given in %s on line %d
+NULL
+```
+
+#### sample007.phpt
+
+```php
+--TEST--
+SOAP Server 20: compressed request (deflate)
+--SKIPIF--
+
+--INI--
+precision=14
+--DEFLATE_POST--
+
+
+
+
+
+
+--FILE--
+"http://testuri.org"));
+$server->addfunction("test");
+$server->handle();
+echo "ok\n";
+?>
+--EXPECT--
+
+Hello World
+ok
+```
+
+#### sample008.phpt
+
+```php
+--TEST--
+GET/POST/REQUEST Test with input_filter
+--SKIPIF--
+
+--POST--
+d=379
+--GET--
+ar[elm1]=1234&ar[elm2]=0660&a=0234
+--FILE--
+FILTER_FLAG_ALLOW_OCTAL));
+var_dump($ret);
+
+$ret = filter_input(INPUT_GET, 'ar', FILTER_VALIDATE_INT, array('flags'=>FILTER_REQUIRE_ARRAY));
+var_dump($ret);
+
+$ret = filter_input(INPUT_GET, 'ar', FILTER_VALIDATE_INT, array('flags'=>FILTER_FLAG_ALLOW_OCTAL|FILTER_REQUIRE_ARRAY));
+var_dump($ret);
+
+?>
+--EXPECT--
+bool(false)
+int(156)
+array(2) {
+ ["elm1"]=>
+ int(1234)
+ ["elm2"]=>
+ bool(false)
+}
+array(2) {
+ ["elm1"]=>
+ int(1234)
+ ["elm2"]=>
+ int(432)
+}
+```
+
+#### sample009.phpt
+
+```php
+--TEST--
+STDIN input
+--FILE--
+
+--STDIN--
+fooBar
+use this to input some thing to the php script
+--EXPECT--
+string(54) "fooBar
+use this to input some thing to the php script
+"
+```
+
+#### sample010.phpt
+
+```php
+--TEST--
+getopt#005 (Required values)
+--ARGS--
+--arg value --arg=value -avalue -a=value -a value
+--INI--
+register_argc_argv=On
+variables_order=GPS
+--FILE--
+
+--EXPECT--
+array(2) {
+ ["arg"]=>
+ array(2) {
+ [0]=>
+ string(5) "value"
+ [1]=>
+ string(5) "value"
+ }
+ ["a"]=>
+ array(3) {
+ [0]=>
+ string(5) "value"
+ [1]=>
+ string(5) "value"
+ [2]=>
+ string(5) "value"
+ }
+}
+```
+
+#### sample011.phpt
+
+```php
+--TEST--
+Bug #35382 (Comment in end of file produces fatal error)
+--FILEEOF--
+
+--REDIRECTTEST--
+return array(
+ 'ENV' => array(
+ 'PDOTEST_DSN' => 'sqlite2::memory:'
+ ),
+ 'TESTS' => 'ext/pdo/tests'
+ );
+```
+
+#### sample014.phpt
+
+```php
+--TEST--
+MySQL
+--SKIPIF--
+
+--REDIRECTTEST--
+# magic auto-configuration
+
+$config = array(
+ 'TESTS' => 'ext/pdo/tests'
+);
+
+if (false !== getenv('PDO_MYSQL_TEST_DSN')) {
+ # user set them from their shell
+ $config['ENV']['PDOTEST_DSN'] = getenv('PDO_MYSQL_TEST_DSN');
+ $config['ENV']['PDOTEST_USER'] = getenv('PDO_MYSQL_TEST_USER');
+ $config['ENV']['PDOTEST_PASS'] = getenv('PDO_MYSQL_TEST_PASS');
+ if (false !== getenv('PDO_MYSQL_TEST_ATTR')) {
+ $config['ENV']['PDOTEST_ATTR'] = getenv('PDO_MYSQL_TEST_ATTR');
+ }
+} else {
+ $config['ENV']['PDOTEST_DSN'] = 'mysql:host=localhost;dbname=test';
+ $config['ENV']['PDOTEST_USER'] = 'root';
+ $config['ENV']['PDOTEST_PASS'] = '';
+}
+
+return $config;
+```
+
+#### sample016.phpt
+
+```php
+--TEST--
+Test get variables with CGI binary
+--GET--
+hello=World&goodbye=MrChips
+--CGI--
+--FILE--
+
+--EXPECT--
+array(2) {
+ ["hello"]=>
+ string(5) "World"
+ ["goodbye"]=>
+ string(7) "MrChips"
+}
+```
+
+#### sample017.phpt
+
+```php
+--TEST--
+PDO Common: Bug #34630 (inserting streams as LOBs)
+--SKIPIF--
+
+--FILE--
+getAttribute(PDO::ATTR_DRIVER_NAME);
+$is_oci = $driver == 'oci';
+
+if ($is_oci) {
+ $db->exec('CREATE TABLE test (id int NOT NULL PRIMARY KEY, val BLOB)');
+} else {
+ $db->exec('CREATE TABLE test (id int NOT NULL PRIMARY KEY, val VARCHAR(256))');
+}
+$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
+
+$fp = tmpfile();
+fwrite($fp, "I am the LOB data");
+rewind($fp);
+
+if ($is_oci) {
+ /* oracle is a bit different; you need to initiate a transaction otherwise
+ * the empty blob will be committed implicitly when the statement is
+ * executed */
+ $db->beginTransaction();
+ $insert = $db->prepare("insert into test (id, val) values (1, EMPTY_BLOB()) RETURNING val INTO :blob");
+} else {
+ $insert = $db->prepare("insert into test (id, val) values (1, :blob)");
+}
+$insert->bindValue(':blob', $fp, PDO::PARAM_LOB);
+$insert->execute();
+$insert = null;
+
+$db->setAttribute(PDO::ATTR_STRINGIFY_FETCHES, true);
+var_dump($db->query("SELECT * from test")->fetchAll(PDO::FETCH_ASSOC));
+
+?>
+--XFAIL--
+This bug might be still open on aix5.2-ppc64 and hpux11.23-ia64
+--EXPECT--
+array(1) {
+ [0]=>
+ array(2) {
+ ["id"]=>
+ string(1) "1"
+ ["val"]=>
+ string(17) "I am the LOB data"
+ }
+}
+```
+
+#### sample018.phpt
+
+```php
+--TEST--
+Phar front controller rewrite access denied [cache_list]
+--INI--
+default_charset=UTF-8
+phar.cache_list={PWD}/frontcontroller10.php
+--SKIPIF--
+
+--ENV--
+SCRIPT_NAME=/frontcontroller10.php
+REQUEST_URI=/frontcontroller10.php/hi
+PATH_INFO=/hi
+--FILE_EXTERNAL--
+files/frontcontroller4.phar
+--EXPECTHEADERS--
+Content-type: text/html; charset=UTF-8
+Status: 403 Access Denied
+--EXPECT--
+
+
+ Access Denied
+
+
+
403 - File /hi Access Denied
+
+
+```
+
+#### sample019.phpt
+
+```php
+--TEST--
+bzopen() and invalid parameters
+--SKIPIF--
+
+--FILE--
+
+--EXPECTF--
+Warning: bzopen() expects exactly 2 parameters, 0 given in %s on line %d
+NULL
+
+Warning: bzopen(): '' is not a valid mode for bzopen(). Only 'w' and 'r' are supported. in %s on line %d
+bool(false)
+
+Warning: bzopen(): filename cannot be empty in %s on line %d
+bool(false)
+
+Warning: bzopen(): filename cannot be empty in %s on line %d
+bool(false)
+
+Warning: bzopen(): 'x' is not a valid mode for bzopen(). Only 'w' and 'r' are supported. in %s on line %d
+bool(false)
+
+Warning: bzopen(): 'rw' is not a valid mode for bzopen(). Only 'w' and 'r' are supported. in %s on line %d
+bool(false)
+
+Warning: bzopen(no_such_file): failed to open stream: No such file or directory in %s on line %d
+bool(false)
+resource(%d) of type (stream)
+Done
+```
+
+#### sample020.phpt
+
+```php
+--TEST--
+Bug #42082 (NodeList length zero should be empty)
+--FILE--
+query('*');
+var_dump($nodes);
+var_dump($nodes->length);
+$length = $nodes->length;
+var_dump(empty($nodes->length), empty($length));
+
+$doc->loadXML("");
+var_dump($doc->firstChild->nodeValue, empty($doc->firstChild->nodeValue), isset($doc->firstChild->nodeValue));
+var_dump(empty($doc->nodeType), empty($doc->firstChild->nodeType))
+?>
+--EXPECTF--
+object(DOMNodeList)#%d (0) {
+}
+int(0)
+bool(true)
+bool(true)
+string(0) ""
+bool(true)
+bool(true)
+bool(false)
+bool(false)
+```
+
+#### sample021.phpt
+
+```php
+--TEST--
+Math constants
+--INI--
+precision=14
+--FILE--
+
+--EXPECTREGEX--
+M_E : 2.718281[0-9]*
+M_LOG2E : 1.442695[0-9]*
+M_LOG10E : 0.434294[0-9]*
+M_LN2 : 0.693147[0-9]*
+M_LN10 : 2.302585[0-9]*
+M_PI : 3.141592[0-9]*
+M_PI_2 : 1.570796[0-9]*
+M_PI_4 : 0.785398[0-9]*
+M_1_PI : 0.318309[0-9]*
+M_2_PI : 0.636619[0-9]*
+M_SQRTPI : 1.772453[0-9]*
+M_2_SQRTPI: 1.128379[0-9]*
+M_LNPI : 1.144729[0-9]*
+M_EULER : 0.577215[0-9]*
+M_SQRT2 : 1.414213[0-9]*
+M_SQRT1_2 : 0.707106[0-9]*
+M_SQRT3 : 1.732050[0-9]*
+```
+
+#### sample022.phpt
+
+```php
+--TEST--
+shm_detach() tests
+--SKIPIF--
+
+--FILE--
+
+--CLEAN--
+
+--EXPECTF--
+Warning: shm_detach() expects exactly 1 parameter, 0 given in %ssample022.php on line %d
+NULL
+
+Warning: shm_detach() expects exactly 1 parameter, 2 given in %ssample022.php on line %d
+NULL
+bool(true)
+
+Warning: shm_detach(): %d is not a valid sysvshm resource in %ssample022.php on line %d
+bool(false)
+
+Warning: shm_remove(): %d is not a valid sysvshm resource in %ssample022.php on line %d
+
+Warning: shm_detach() expects parameter 1 to be resource, integer given in %ssample022.php on line %d
+NULL
+
+Warning: shm_detach() expects parameter 1 to be resource, integer given in %ssample022.php on line %d
+NULL
+
+Warning: shm_detach() expects parameter 1 to be resource, integer given in %ssample022.php on line %d
+NULL
+Done
+```
+
+#### sample023.phpt
+
+```php
+--TEST--
+Bug #23894 (sprintf() decimal specifiers problem)
+--FILE--
+
+--EXPECTREGEX--
+string\(4\) \"-012\"
+string\(8\) \"2d303132\"
+(string\(13\) \" 4294967284\"|string\(20\) \"18446744073709551604\")
+(string\(26\) \"20202034323934393637323834\"|string\(40\) \"3138343436373434303733373039353531363034\")
+```
+
+#### sample024.phpt
+
+```php
+--TEST--
+DOMDocument::save Test basic function of save method
+--SKIPIF--
+
+--FILE--
+formatOutput = true;
+
+$root = $doc->createElement('book');
+
+$root = $doc->appendChild($root);
+
+$title = $doc->createElement('title');
+$title = $root->appendChild($title);
+
+$text = $doc->createTextNode('This is the title');
+$text = $title->appendChild($text);
+
+$temp_filename = __DIR__.'/DomDocument_save_basic.tmp';
+
+echo 'Wrote: ' . $doc->save($temp_filename) . ' bytes'; // Wrote: 72 bytes
+?>
+--CLEAN--
+
+--EXPECTF--
+Wrote: 72 bytes
+```
+
+#### sample025.phpt
+
+```php
+--TEST--
+Test imap_append() function : basic functionality
+--SKIPIF--
+
+--FILE--
+Mailbox . "\n";
+var_dump(imap_append($imap_stream, $mb_details->Mailbox
+ , "From: webmaster@something.com\r\n"
+ . "To: info@something.com\r\n"
+ . "Subject: Test message\r\n"
+ . "\r\n"
+ . "this is a test message, please ignore\r\n"
+ ));
+
+var_dump(imap_append($imap_stream, $mb_details->Mailbox
+ , "From: webmaster@something.com\r\n"
+ . "To: info@something.com\r\n"
+ . "Subject: Another test\r\n"
+ . "\r\n"
+ . "this is another test message, please ignore it too!!\r\n"
+ ));
+
+$check = imap_check($imap_stream);
+echo "Msg Count after append : ". $check->Nmsgs . "\n";
+
+echo "List the msg headers\n";
+var_dump(imap_headers($imap_stream));
+
+imap_close($imap_stream);
+?>
+--CLEAN--
+
+--EXPECTF--
+*** Testing imap_append() : basic functionality ***
+Create a new mailbox for test
+Create a temporary mailbox and add 0 msgs
+.. mailbox '%s' created
+Add a couple of msgs to new mailbox {%s}INBOX.%s
+bool(true)
+bool(true)
+Msg Count after append : 2
+List the msg headers
+array(2) {
+ [0]=>
+ string(%d) "%w%s 1)%s webmaster@something. Test message (%d chars)"
+ [1]=>
+ string(%d) "%w%s 2)%s webmaster@something. Another test (%d chars)"
+}
+```
+
+#### sample026.phpt
+
+```php
+--TEST--
+SPL: ArrayIterator implementing RecursiveIterator
+--FILE--
+ array(21, 22 => array(221, 222), 23 => array(231)), 3);
+
+$dir = new RecursiveIteratorIterator(new RecursiveArrayIterator($array), RecursiveIteratorIterator::LEAVES_ONLY);
+
+foreach ($dir as $file) {
+ print "$file\n";
+}
+
+?>
+===DONE===
+
+--EXPECT--
+1
+21
+221
+222
+231
+3
+```
+
+#### skipif2.phpt
+
+```php
+
+```
+
+#### skipif.phpt
+
+```php
+
+```
+
+#### xfailif.phpt
+
+```php
+--TEST--
+Handling of errors during linking
+--INI--
+opcache.enable=1
+opcache.enable_cli=1
+opcache.optimization_level=-1
+opcache.preload={PWD}/preload_inheritance_error_ind.inc
+--SKIPIF--
+
+--FILE--
+
+--EXPECTF--
+Fatal error: Declaration of B::foo($bar) must be compatible with A::foo() in %spreload_inheritance_error.inc on line 8
+```
diff --git a/docs/source/miscellaneous/writing-tests.rst b/docs/source/miscellaneous/writing-tests.rst
deleted file mode 100644
index 40e273b6fd71..000000000000
--- a/docs/source/miscellaneous/writing-tests.rst
+++ /dev/null
@@ -1,2874 +0,0 @@
-###############
- Writing Tests
-###############
-
-******************
- phpt Test Basics
-******************
-
-The first thing you need to know about tests is that we need more!!! Although PHP works just great
-99.99% of the time, not having a very comprehensive test suite means that we take more risks every
-time we add to or modify the PHP implementation. The second thing you need to know is that if you
-can write PHP you can write tests. Thirdly — we are a friendly and welcoming community, don't be
-scared about writing to (php-qa@lists.php.net) — we won't bite!
-
-So what are phpt tests?
-
- A phpt test is a little script used by the php internal and quality assurance teams to test PHP's
- functionality. It can be used with new releases to make sure they can do all the things that
- previous releases can, or to help find bugs in current releases. By writing phpt tests you are
- helping to make PHP more stable.
-
-What skills are needed to write a phpt test?
-
- All that is really needed to write a phpt test is a basic understanding of the PHP language, a
- text editor, and a way to get the results of your code. That is it. So if you have been writing
- and running PHP scripts already — you have everything you need.
-
-What do you write phpt tests on?
-
- Basically you can write a phpt test on one of the various php functions available. You can write
- a test on a basic language function (a string function or an array function) , or a function
- provided by one of PHP's numerous extensions (a mysql function or a image function or a mcrypt
- function).
-
- You can find out what functions already have phpt tests by looking in the `html version
- `_ of the git repository (``ext/standard/tests/`` is a good place
- to start looking — though not all the tests currently written are in there).
-
- If you want more guidance than that you can always ask the PHP Quality Assurance Team on their
- mailing list (php-qa@lists.php.net) where they would like you to direct your attentions.
-
-How is a phpt test used?
-
- When a test is called by the ``run-tests.php`` script it takes various parts of the phpt file to
- name and create a .php file. That .php file is then executed. The output of the .php file is then
- compared to a different section of the phpt file. If the output of the script "matches" the
- output provided in the phpt script — it passes.
-
-What should a phpt test do?
-
- Basically — it should try and break the PHP function. It should check not only the functions
- normal parameters, but it should also check edge cases. Intentionally generating an error is
- allowed and encouraged.
-
-********************
- Writing phpt Tests
-********************
-
-Naming Conventions
-==================
-
-Phpt tests follow a very strict naming convention. This is done to easily identify what each phpt
-test is for. Tests should be named according to the following list:
-
-Tests for bugs
- bug.phpt (bug17123.phpt)
-
-Tests for a function's basic behaviour
- _basic.phpt (dba_open_basic.phpt)
-
-Tests for a function's error behaviour
- _error.phpt (dba_open_error.phpt)
-
-Tests for variations in a function's behaviour
- _variation.phpt (dba_open_variation.phpt)
-
-General tests for extensions
- .phpt (dba_003.phpt)
-
-The convention of using _basic, _error and _variation was introduced when we found that writing a
-single test case for each function resulted in unacceptably large test cases. It's quite hard to
-debug problems when the test case generates 100s of lines of output.
-
-The "basic" test case for a function should just address the single most simple thing that the
-function is designed to do. For example, if writing a test for the sin() function a basic test would
-just be to check that sin() returns the correct values for some known angles — eg 30, 90, 180.
-
-The "error" tests for a function are test cases which are designed to provoke errors, warnings or
-notices. There can be more than one error case, if so the convention is to name the test cases
-mytest_error1.phpt, mytest_error2.phpt and so on.
-
-The "variation" tests are any tests that don't fit into "basic" or "error" tests. For example one
-might use a variation tests to test boundary conditions.
-
-How big is a test case?
-=======================
-
-Small. Really — the smaller the better, a good guide is no more than 10 lines of output. The reason
-for this is that if we break something in PHP and it breaks your test case we need to be able to
-find out quite quickly what we broke, going through 1000s of line of test case output is not easy.
-Having said that it's sometimes just not practical to stay within the 10 line guideline, in this
-case you can help a lot by commenting the output. You may find plenty of much longer tests in PHP -
-the small tests message is something that we learnt over time, in fact we are slowly going through
-and splitting tests up when we need to.
-
-Comments
-========
-
-Comments help. Not an essay — just a couple of lines on what the objective of the test is. It may
-seem completely obvious to you as you write it, but it might not be to someone looking at it later
-on.
-
-Basic Format
-============
-
-A test must contain the sections TEST, FILE and either EXPECT or EXPECTF at a minimum. The example
-below illustrates a minimal test.
-
-*ext/standard/tests/strings/strtr.phpt*
-
-.. code:: php
-
- --TEST--
- strtr() function — basic test for strtr()
- --FILE--
- "hi", "hi"=>"hello", "a"=>"A", "world"=>"planet");
- var_dump(strtr("# hi all, I said hello world! #", $trans));
- ?>
- --EXPECT--
- string(32) "# hello All, I sAid hi planet! #"
-
-As you can see the file is divided into several sections. The TEST section holds a one line title of
-the phpt test, this should be a simple description and shouldn't ever exceed one line, if you need
-to write more explanation add comments in the body of the test case. The phpt files name is used
-when generating a .php file. The FILE section is used as the body of the .php file, so don't forget
-to open and close your php tags. The EXPECT section is the part used as a comparison to see if the
-test passes. It is a good idea to generate output with var_dump() calls.
-
-PHPT structure details
-======================
-
-A phpt test can have many more parts than just the minimum. In fact some of the mandatory parts have
-alternatives that may be used if the situation warrants it. The phpt sections are documented here.
-
-Analyzing failing tests
-=======================
-
-While writing tests you will probably run into tests not passing while you think they should. The
-'make test' command provides you with debug information. Several files will be added per test in the
-same directory as the .phpt file itself. Considering your test file is named foo.phpt, these files
-provide you with information that can help you find out what went wrong:
-
-foo.diff
-
- A diff file between the expected output (be it in EXPECT, EXPECTF or another option) and the
- actual output.
-
-foo.exp
-
- The expected output.
-
-foo.log
-
- A log containing expected output, actual output and results. Most likely very similar to info in
- the other files.
-
-foo.out
-
- The actual output of your .phpt test part.
-
-foo.php
-
- The php code that was executed for this test.
-
-foo.sh
-
- An executable file that executes the test for you as it was executed during failure.
-
-Testing your test cases
-=======================
-
-Most people who write tests for PHP don't have access to a huge number of operating systems but the
-tests are run on every system that runs PHP. It's good to test your test on as many platforms as you
-can — Linux and Windows are the most important, it's increasingly important to make sure that tests
-run on 64 bit as well as 32 bit platforms. If you only have access to one operating system — don't
-worry, if you have karma, commit the test but watch php-qa@lists.php.net for reports of failures on
-other platforms. If you don't have karma to commit have a look at the next section.
-
-When you are testing your test case it's really important to make sure that you clean up any
-temporary resources (eg files) that you used in the test. There is a special ``--CLEAN--`` section
-to help you do this — see `here <#clean>`_.
-
-Tests run in parallel by default. Mutable resources such as files, directories, ports, database
-objects, and IPC identifiers must therefore be unique to each test. Read-only fixtures may be
-shared. If a resource cannot be isolated, declare the narrowest applicable conflict using
-``--CONFLICTS--`` or a ``CONFLICTS`` file.
-
-Another good check is to look at what lines of code in the PHP source your test case covers. This is
-easy to do, there are some instructions on the `PHP Wiki
-`_.
-
-What should I do with my test case when I've written and tested it?
-===================================================================
-
-The next step is to get someone to review it. If it's short you can paste it into a note and send it
-to php-qa@lists.php.net. If the test is a bit too long for that then put it somewhere were people
-can download it (`pastebin `_ is sometimes used). Appending tests to notes as
-files doesn't work well - so please don't do that. Your note to php-qa@lists.php.net should say what
-level of PHP you have tested it on and what platform(s) you've run it on. Someone from the PHP QA
-group will review your test and reply to you. They may ask for some changes or suggest better ways
-to do things, or they may commit it to PHP.
-
-Writing Portable PHP Tests
-==========================
-
-Writing portable tests can be hard if you don't have access to all the many platforms that PHP can
-run on. Do your best. If in doubt, don't disable a test. It is better that the test runs in as many
-environments as possible.
-
-If you know a new test won't run in a specific environment, try to write the complementary test for
-that environment.
-
-Make sure sets of data are consistently ordered. SQL queries are not guaranteed to return results in
-the same order unless an ORDER BY clause is used. Directory listings are another example that can
-vary: use an appropriate PHP function to sort them before printing. Both of these examples have
-affected PHP tests in the past.
-
-Make sure that any test touching parsing or display of dates uses a hard-defined timezone —
-preferable 'UTC'. It is important that this is defined in the file section using:
-
-.. code:: php
-
- date_default_timezone_set('UTC');
-
-and not in the INI section. This is because of the order in which settings are checked which is:
-
-.. code::
-
- date_default_timezone_set() -> TZ environmental -> INI setting -> System Setting
-
-If a TZ environmental variable is found the INI setting will be ignored.
-
-Tests that run, or only have matching EXPECT output, on 32bit platforms can use a SKIPIF section
-like:
-
-.. code:: php
-
- --SKIPIF--
-
-
-Tests for 64bit platforms can use:
-
-.. code:: php
-
- --SKIPIF--
-
-
-To run a test only on Windows:
-
-.. code:: php
-
- --SKIPIF--
-
-
-To run a test only on Linux:
-
-.. code:: php
-
- --SKIPIF--
-
-
-To skip a test on Mac OS X Darwin:
-
-.. code:: php
-
- --SKIPIF--
-
-
-**********
- Examples
-**********
-
-EXPECTF
-=======
-
-``/ext/standard/tests/strings/str_shuffle.phpt`` is a good example for using ``EXPECTF`` instead of
-``EXPECT``. From time to time the algorithm used for shuffle changed and sometimes the machine used
-to execute the code has influence on the result of shuffle. But it always returns a three character
-string detectable by ``%s`` (that matches any string until the end of the line). Other scan-able
-forms are ``%a`` for any amount of chars (at least one), ``%i`` for integers, ``%d`` for numbers
-only, ``%f`` for floating point values, ``%c`` for single characters, ``%x`` for hexadecimal values,
-``%w`` for any number of whitespace characters and ``%e`` for ``DIRECTORY_SEPARATOR`` (``'\'`` or
-``'/'``).
-
-See also `EXPECTF <#expectf>`_ details.
-
-*/ext/standard/tests/strings/str_shuffle.phpt*
-
-.. code:: php
-
- --TEST--
- Testing str_shuffle.
- --FILE--
-
- --EXPECTF--
- string(3) "%s"
- string(3) "123"
-
-EXPECTREGEX
-===========
-
-``/ext/standard/tests/strings/strings001.phpt`` is a good example for using ``EXPECTREGEX`` instead
-of ``EXPECT``. This test also shows that in ``EXPECTREGEX`` some characters need to be escaped since
-otherwise they would be interpreted as a regular expression.
-
-*/ext/standard/tests/strings/strings001.phpt*
-
-.. code:: php
-
- --TEST--
- Test whether strstr() and strrchr() are binary safe.
- --FILE--
-
- --EXPECTREGEX--
- string\(18\) \"nica\x00turska panica\"
- string\(19\) \" nica\x00turska panica\"
-
-EXTENSIONS
-==========
-
-Some tests depend on PHP extensions that may be unavailable. These extensions should be listed in
-the ``EXTENSIONS`` section. If an extension is missing, PHP will try to find it in a shared module
-and skip the test if it's not there.
-
-*/ext/sodium/tests/crypto_scalarmult.phpt*
-
-.. code:: php
-
- --TEST--
- Check for libsodium scalarmult
- --EXTENSIONS--
- sodium
- --FILE--
-
- --FILE--
- [snip]
-
-Test script and ``SKIPIF`` code should be directly written into ``\*.phpt``. However, it is
-recommended to use include files when more test scripts depend on the same ``SKIPIF`` code or when
-certain test files need the same values for some input.
-
-Note: no file used by any test should have one of the following extensions: ".php", ".log", ".mem",
-".exp", ".out" or ".diff". When you use an include file for the ``SKIPIF`` section it should be
-named "skipif.inc" and an include file used in the ``FILE`` section of many tests should be named
-"test.inc".
-
-*************
- Final Notes
-*************
-
-Cleaning up after running a test
-================================
-
-Sometimes test cases create files or directories as part of the test case and it's important to
-remove these after the test ends, the ``--CLEAN--`` section is provided to help with this.
-
-The PHP code in the ``--CLEAN--`` section is executed separately from the code in the ``--FILE--``
-section. For example, this code:
-
-.. code:: php
-
- --TEST--
- Will fail to clean up
- --FILE--
-
- --CLEAN--
-
- --EXPECT--
-
-will not remove the temporary file because the variable $temp_filename is not defined in the
-``--CLEAN--`` section.
-
-Here is a better way to write the code:
-
-.. code:: php
-
- --TEST--
- This will remove temporary files
- --FILE--
-
- --CLEAN--
-
- --EXPECT--
-
-Note the use of the ``__DIR__`` construct which will ensure that the temporary file is created in
-the same directory as the phpt test script.
-
-When creating temporary files it is a good idea to use an extension that indicates the use of the
-file, eg .tmp. It's also a good idea to avoid using extensions that are already used for other
-purposes, eg .inc, .php. Similarly, it is helpful to give the temporary file a name that is clearly
-related to the test case. For example, mytest.phpt should create mytest.tmp (or mytestN.tmp, N=1,
-2,3,...) then if by any chance the temporary file isnt't removed properly it will be obvious which
-test case created it.
-
-When writing and debugging a test case with a ``--CLEAN--`` section it is helpful to remember that
-the php code in the ``--CLEAN--`` section is executed separately from the code in the ``--FILE--``
-section. For example, in a test case called mytest.phpt, code from the ``--FILE--`` section is run
-from a file called mytest.php and code from the ``--CLEAN--`` section is run from a file called
-mytest.clean.php. If the test passes, both the .php and .clean.php files are removed by
-``run-tests.php``. You can prevent the removal by using the --keep option of ``run-tests.php``, this
-is a very useful option if you need to check that the ``--CLEAN--`` section code is working as you
-intended.
-
-Finally — if you are using CVS it's helpful to add the extension that you use for test-related
-temporary files to the .cvsignore file — this will help to prevent you from accidentally checking
-temporary files into CVS.
-
-Redirecting tests
-=================
-
-Using ``--REDIRECTTEST--`` it is possible to redirect from one test to a bunch of other tests. That
-way multiple extensions can refer to the same set of test scripts probably using it with a different
-configuration.
-
-The block is eval'd and supposed to return an array describing how to redirect. The resulting array
-must contain the key 'TEST' that stores the redirect target as a string. This string usually is the
-directory where the test scripts are located and should be relative. Optionally you can use the
-'ENV' as an array configuring the environment to be set when executing the tests. This way you can
-pass configuration to the executed tests.
-
-Redirect tests may especially contain ``--SKIPIF--``, ``--ENV--``, and ``--ARGS--`` sections but
-they no not use any ``--EXPECT--`` section.
-
-The redirected tests themselves are just normal tests.
-
-Error reporting in tests
-========================
-
-All tests should run correctly with error_reporting(E_ALL) and display_errors=1. This is the default
-when called from ``run-tests.php``. If you have a good reason for lowering the error reporting, use
-``--INI--`` section and comment this in your testcode.
-
-If your test intentionally generates a PHP warning message use $php_errormsg variable, which you can
-then output. This will result in a consistent error message output across all platforms and PHP
-configurations, preventing your test from failing due inconsistencies in the error message content.
-Alternatively you can use ``--EXPECTF--`` and check for the message by replacing the path of the
-source of the message with ``%s`` and the line number with ``%d``. The end of a message in a test
-file ``example.phpt`` then looks like ``in %sexample.php on line %d``. We explicitly dropped the
-last path divider as that is a system dependent character ``/`` or ``\``.
-
-Last bit
-========
-
-Often you want to run test scripts without ``run-tests.php`` by executing them on command line like
-any other php script. But sometimes it disturbs having a long ``--EXPECT--`` block, so that you
-don't see the actual output as it scrolls away overwritten by the blocks following the actual file
-block. The workaround is to use terminate the ``--FILE--`` section with the two lines ``===DONE===``
-and ````. When doing so ``run-tests.php`` does not execute the line containing the
-exit call as that would suppress leak messages. Actually ``run-tests.php`` ignores any part after a
-line consisting only of ``===DONE===``.
-
-Here is an example:
-
-.. code:: php
-
- --TEST--
- Test hypot() — dealing with mixed number/character input
- --INI--
- precision=14
- --FILE--
-
- ===DONE===
-
- --EXPECTF--
- 23abc :-33 float(40.224370722238)
- ===DONE===
-
-If executed as PHP script the output will stop after the code on the ``--FILE--`` section has been
-run.
-
-***********
- Reference
-***********
-
-PHPT Sections
-=============
-
-``--TEST--``
-------------
-
-**Description:** Title of test as a single line short description.
-
-**Required:** Yes
-
-**Format:** Plain text. We recommend a single line only.
-
-Example 1 (snippet):
-
-.. code:: text
-
- --TEST--
- Test filter_input() with GET and POST data.
-
-Example 1 (full): :ref:`sample001.phpt`
-
-``--DESCRIPTION--``
--------------------
-
-**Description:** If your test requires more than a single line title to adequately describe it, you
-can use this section for further explanation. Multiple lines are allowed and besides being used for
-information, this section is completely ignored by the test binary.
-
-**Required:** No
-
-**Format:** Plain text, multiple lines.
-
-Example 1 (snippet):
-
-.. code:: text
-
- --DESCRIPTION--
- This test covers both valid and invalid usages of filter_input() with INPUT_GET and INPUT_POST data and several different filter sanitizers.
-
-Example 1 (full): :ref:`sample001.phpt`
-
-``--CREDITS--``
----------------
-
-**Description:** Used to credit contributors without CVS commit rights, who put their name and email
-on the first line. If the test was part of a TestFest event, then # followed by the name of the
-event and the date (YYYY-MM-DD) on the second line.
-
-**Required:** No. For newly created tests the section should no longer be used for simple authorship
-claims or listing all contributors who edited the test; as it is already accurately tracked by Git.
-It may be used if more specific attribution is useful, for example to credit the original reporter
-of a bug or a contributor who is not credited via `Co-authored-by` tag.
-
-**Format:** Name Email [Event]
-
-Example 1 (snippet):
-
-.. code:: text
-
- --CREDITS--
- Felipe Pena
-
-Example 1 (full): :ref:`sample001.phpt`
-
-Example 2 (snippet):
-
-.. code:: text
-
- --CREDITS--
- Zoe Slattery zoe@php.net
- # TestFest Munich 2009-05-19
-
-Example 2 (full): :ref:`sample002.phpt`
-
-``--SKIPIF--``
---------------
-
-**Description:** A condition or set of conditions used to determine if a test should be skipped.
-Tests that are only applicable to a certain platform, extension or PHP version are good reasons for
-using a ``--SKIPIF--`` section.
-
-A common practice for extension tests is to write your ``--SKIPIF--`` extension criteria into a file
-call skipif.inc and then including that file in the ``--SKIPIF--`` section of all your extension
-tests. This promotes the DRY principle and reduces future code maintenance.
-
-**Required:** No.
-
-**Format:** PHP code enclosed by PHP tags. If the output of this scripts starts with "skip", the
-test is skipped. If the output starts with "xfail", the test is marked as expected failure. If the
-output starts with "flaky", the test is marked as flaky test. The "xfail" convention is supported as
-of PHP 7.2.0. The "flaky" convention is supported as of PHP 8.2.25 and PHP 8.3.13, respectively.
-
-Example 1 (snippet):
-
-.. code:: php
-
- --SKIPIF--
-
-
-Example 1 (full): :ref:`sample001.phpt`
-
-Example 2 (snippet):
-
-.. code:: php
-
- --SKIPIF--
-
-
-Example 2 (full): :ref:`sample003.phpt`
-
-Example 3 (snippet):
-
-.. code:: php
-
- --SKIPIF--
-
-
-Example 3 (full): :ref:`xfailif.phpt`
-
-Example 4 (snippet):
-
-.. code:: php
-
- --SKIPIF--
- string&d=12345.7
-
-Example 1 (full): :ref:`sample001.phpt`
-
-Example 2 (snippet):
-
-.. code:: xml
-
- --POST--
-
-
-
-
-
-
-Example 2 (full): :ref:`sample005.phpt`
-
-``--POST_RAW--``
-----------------
-
-**Description:** Raw POST data to be passed to the test script. This differs from the section above
-because it doesn't automatically set the Content-Type, this leaves you free to define your own
-within the section. This section forces the use of the CGI binary instead of the usual CLI one.
-
-**Required:** No.
-
-Requirements: PHP CGI binary.
-
-**Test Script Support:** ``run-tests.php``
-
-**Format:** Follows the HTTP post data format.
-
-Example 1 (snippet):
-
-.. code:: text
-
- --POST_RAW--
- Content-type: multipart/form-data, boundary=AaB03x
-
- --AaB03x content-disposition: form-data; name="field1"
-
- Joe Blow
- --AaB03x
- content-disposition: form-data; name="pics"; filename="file1.txt"
- Content-Type: text/plain
-
- abcdef123456789
- --AaB03x--
-
-Example 1 (full): :ref:`sample006.phpt`
-
-``--PUT--``
------------
-
-**Description:** Similar to the section above, PUT data to be passed to the test script. This
-section forces the use of the CGI binary instead of the usual CLI one.
-
-**Required:** No.
-
-Requirements: PHP CGI binary.
-
-**Test Script Support:** ``run-tests.php``
-
-**Format:** Raw data optionally preceded by a Content-Type header.
-
-Example 1 (snippet):
-
-.. code:: text
-
- --PUT--
- Content-Type: text/json
-
- {"name":"default output handler","type":0,"flags":112,"level":0,"chunk_size":0,"buffer_size":16384,"buffer_used":3}
-
-``--GZIP_POST--``
------------------
-
-**Description:** When this section exists, the POST data will be gzencode()'d. This section forces
-the use of the CGI binary instead of the usual CLI one.
-
-**Required:** No.
-
-**Test Script Support:** ``run-tests.php``
-
-**Format:** Just add the content to be gzencode()'d in the section.
-
-Example 1 (snippet):
-
-.. code:: xml
-
- --GZIP_POST--
-
-
-
-
-
-
-Example 1 (full): :ref:`sample005.phpt`
-
-``--DEFLATE_POST--``
---------------------
-
-**Description:** When this section exists, the POST data will be gzcompress()'ed. This section
-forces the use of the CGI binary instead of the usual CLI one.
-
-**Required:** No.
-
-Requirements:
-
-**Test Script Support:** ``run-tests.php``
-
-**Format:** Just add the content to be gzcompress()'ed in the section.
-
-Example 1 (snippet):
-
-.. code:: xml
-
- --DEFLATE_POST--
-
-
-
-
-
-
-
-Example 1 (full): :ref:`sample007.phpt`
-
-``--GET--``
------------
-
-**Description:** GET variables to be passed to the test script. This section forces the use of the
-CGI binary instead of the usual CLI one.
-
-**Required:** No.
-
-Requirements: PHP CGI binary.
-
-**Format:** A single line of text passed as the GET data to the script.
-
-Example 1 (snippet):
-
-.. code:: text
-
- --GET--
- a=test&b=http://example.com
-
-Example 1 (full): :ref:`sample001.phpt`
-
-Example 2 (snippet):
-
-.. code:: text
-
- --GET--
- ar[elm1]=1234&ar[elm2]=0660&a=0234
-
-Example 2 (full): :ref:`sample008.phpt`
-
-``--COOKIE--``
---------------
-
-**Description:** Cookies to be passed to the test script. This section forces the use of the CGI
-binary instead of the usual CLI one.
-
-**Required:** No.
-
-Requirements: PHP CGI binary.
-
-**Test Script Support:** ``run-tests.php``
-
-**Format:** A single line of text in a valid HTTP cookie format.
-
-Example 1 (snippet):
-
-.. code::
-
- --COOKIE--
- hello=World;goodbye=MrChips
-
-Example 1 (full): :ref:`sample002.phpt`
-
-``--STDIN--``
--------------
-
-**Description:** Data to be fed to the test script's standard input.
-
-**Required:** No.
-
-**Test Script Support:** ``run-tests.php``
-
-**Format:** Any text within this section is passed as STDIN to PHP.
-
-Example 1 (snippet):
-
-.. code:: text
-
- --STDIN--
- fooBar
- use this to input some thing to the php script
-
-Example 1 (full): :ref:`sample009.phpt`
-
-``--INI--``
------------
-
-**Description:** To be used if you need a specific php.ini setting for the test.
-
-**Required:** No.
-
-**Format:** Key value pairs including automatically replaced tags. One setting per line. Content
-that is not a valid ini setting may cause failures.
-
-The following is a list of all tags and what they are used to represent:
-
-- ``{PWD}``: Represents the directory of the file containing the ``--INI--`` section.
-- ``{TMP}``: Represents the system's temporary directory. Available as of PHP 7.2.19 and 7.3.6.
-
-Example 1 (snippet):
-
-.. code:: text
-
- --INI--
- precision=14
-
-Example 1 (full): :ref:`sample001.phpt`
-
-Example 2 (snippet):
-
-.. code:: text
-
- --INI--
- session.use_cookies=0
- session.cache_limiter=
- register_globals=1
- session.serialize_handler=php
- session.save_handler=files
-
-Example 2 (full): :ref:`sample003.phpt`
-
-``--ARGS--``
-------------
-
-**Description:** A single line defining the arguments passed to PHP.
-
-**Required:** No.
-
-**Format:** A single line of text that is passed as the argument(s) to the PHP CLI.
-
-Example 1 (snippet):
-
-.. code:: text
-
- --ARGS--
- --arg value --arg=value -avalue -a=value -a value
-
-Example 1 (full): :ref:`sample010.phpt`
-
-``--ENV--``
------------
-
-**Description:** Configures environment variables such as those found in the ``$_SERVER`` global
-array.
-
-**Required:** No.
-
-**Format:** Key value pairs. One setting per line.
-
-Example 1 (snippet):
-
-.. code:: text
-
- --ENV--
- SCRIPT_NAME=/frontcontroller10.php
- REQUEST_URI=/frontcontroller10.php/hi
- PATH_INFO=/hi
-
-Example 1 (full): :ref:`sample018.phpt`
-
-``--PHPDBG--``
---------------
-
-**Description:** This section takes arbitrary phpdbg commands and executes the test file according
-to them as it would be run in the phpdbg prompt.
-
-**Required:** No.
-
-**Format:** Arbitrary phpdbg commands
-
-Example 1 (snippet):
-
-.. code:: text
-
- --PHPDBG--
- b
- 4
- b
- del
- 0
- b
- 5
- r
- b
- del
- 1
- r
- y
- q
-
-Example 1 (full): :ref:`phpdbg_1.phpt`
-
-``--FILE--``
-------------
-
-**Description:** The test source code.
-
-**Required:** One of the ``FILE`` type sections is required.
-
-**Format:** PHP source code enclosed by PHP tags.
-
-Example 1 (snippet):
-
-.. code:: php
-
- --FILE--
-
-
-Example 1 (full): :ref:`sample001.phpt`
-
-``--FILEEOF--``
----------------
-
-**Description:** An alternative to ``--FILE--`` where any trailing line breaks (\n || \r || \r\n
-found at the end of the section) are omitted. This is an extreme edge-case feature, so 99.99% of the
-time you won't need this section.
-
-**Required:** One of the ``FILE`` type sections is required.
-
-**Test Script Support:** ``run-tests.php``
-
-**Format:** PHP source code enclosed by PHP tags.
-
-Example 1 (snippet):
-
-.. code:: php
-
- --FILEEOF--
- array(
- 'PDOTEST_DSN' => 'sqlite2::memory:'
- ),
- 'TESTS' => 'ext/pdo/tests'
- );
-
-Example 1 (full): :ref:`sample013.phpt` Note: The destination tests for this example are not
-included. See the PDO extension tests for reference to live tests using this section.
-
-Example 2 (snippet):
-
-.. code:: php
-
- --REDIRECTTEST--
- # magic auto-configuration
-
- $config = array(
- 'TESTS' => 'ext/pdo/tests'
- );
-
- if (false !== getenv('PDO_MYSQL_TEST_DSN')) {
- # user set them from their shell
- $config['ENV']['PDOTEST_DSN'] = getenv('PDO_MYSQL_TEST_DSN');
- $config['ENV']['PDOTEST_USER'] = getenv('PDO_MYSQL_TEST_USER');
- $config['ENV']['PDOTEST_PASS'] = getenv('PDO_MYSQL_TEST_PASS');
- if (false !== getenv('PDO_MYSQL_TEST_ATTR')) {
- $config['ENV']['PDOTEST_ATTR'] = getenv('PDO_MYSQL_TEST_ATTR');
- }
- } else {
- $config['ENV']['PDOTEST_DSN'] = 'mysql:host=localhost;dbname=test';
- $config['ENV']['PDOTEST_USER'] = 'root';
- $config['ENV']['PDOTEST_PASS'] = '';
- }
-
- return $config;
-
-Example 2 (full): :ref:`sample014.phpt`
-
-Note: The destination tests for this example are not included. See the PDO extension tests for
-reference to live tests using this section.
-
-``--CGI--``
------------
-
-**Description:** This section takes no value. It merely provides a simple marker for tests that MUST
-be run as CGI, even if there is no ``--POST--`` or ``--GET--`` sections in the test file.
-
-**Required:** No.
-
-**Format:** No value, just the ``--CGI--`` statement.
-
-Example 1 (snippet):
-
-.. code:: text
-
- --CGI--
-
-Example 1 (full): :ref:`sample016.phpt`
-
-``--XFAIL--``
--------------
-
-**Description:** This section identifies this test as one that is currently expected to fail. It
-should include a brief description of why it's expected to fail. Reasons for such expectations
-include tests that are written before the functionality they are testing is implemented or notice of
-a bug which is due to upstream code such as an extension which provides PHP support for some other
-software.
-
-Please do NOT include an ``--XFAIL--`` without providing a text description for the reason it's
-being used.
-
-**Required:** No.
-
-**Test Script Support:** ``run-tests.php``
-
-**Format:** A short plain text description of why this test is currently expected to fail.
-
-Example 1 (snippet):
-
-.. code:: text
-
- --XFAIL--
- This bug might be still open on aix5.2-ppc64 and hpux11.23-ia64
-
-Example 1 (full): :ref:`sample017.phpt`
-
-``--FLAKY--``
--------------
-
-**Description:** This section identifies this test as one that occasionally fails. If the test
-actually fails, it will be retried one more time, and that result will be reported. The section
-should include a brief description of why the test is flaky. Reasons for this include tests that
-rely on relatively precise timing, or temporary disc states. Available as of PHP 8.1.22 and 8.2.9,
-respectively.
-
-Please do NOT include a ``--FLAKY--`` section without providing a text description for the reason it
-is being used.
-
-**Required:** No.
-
-**Test Script Support:** ``run-tests.php``
-
-**Format:** A short plain text description of why this test is flaky.
-
-Example 1 (snippet):
-
-.. code::
-
- --FLAKY--
- This test frequently fails in CI
-
-Example 1 (full): flaky.phpt
-
-``--EXPECTHEADERS--``
----------------------
-
-**Description:** The expected headers. Any header specified here must exist in the response and have
-the same value or the test fails. Additional headers found in the actual tests while running are
-ignored.
-
-**Required:** No.
-
-**Format:** HTTP style headers. May include multiple lines.
-
-Example 1 (snippet):
-
---EXPECTHEADERS-- Status: 404
-
-Example 1 (snippet):
-
-.. code:: text
-
- --EXPECTHEADERS--
- Content-type: text/html; charset=UTF-8
- Status: 403 Access Denied
-
-Example 1 (full): :ref:`sample018.phpt`
-
-Note: The destination tests for this example are not included. See the phar extension tests for
-reference to live tests using this section.
-
-``--EXPECT--``
---------------
-
-**Description:** The expected output from the test script. This must match the actual output from
-the test script exactly for the test to pass.
-
-**Required:** One of the ``EXPECT`` type sections is required.
-
-**Format:** Plain text. Multiple lines of text are allowed.
-
-Example 1 (snippet):
-
-.. code:: text
-
- --EXPECT--
- array(2) {
- ["hello"]=>
- string(5) "World"
- ["goodbye"]=>
- string(7) "MrChips"
- }
-
-Example 1 (full): :ref:`sample002.phpt`
-
-``--EXPECT_EXTERNAL--``
------------------------
-
-**Description:** Similar to ``--EXPECT--`` section, but just stating a filename where to load the
-expected output from.
-
-**Required:** One of the ``EXPECT`` type sections is required.
-
-**Test Script Support:** ``run-tests.php``
-
-Example 1 (snippet):
-
-.. code:: text
-
- --EXPECT_EXTERNAL--
- test001.expected.txt
-
-*test001.expected.txt*
-
-.. code:: php
-
- array(2) {
- ["hello"]=>
- string(5) "World"
- ["goodbye"]=>
- string(7) "MrChips"
- }
-
-``--EXPECTF--``
----------------
-
-**Description:** An alternative of ``--EXPECT--``. Where it differs from ``--EXPECT--`` is that it
-uses a number of substitution tags for strings, spaces, digits, etc. that appear in test case output
-but which may vary between test runs. The most common example of this is to use %s and %d to match
-the file path and line number which are output by PHP Warnings.
-
-**Required:** One of the ``EXPECT`` type sections is required.
-
-**Format:** Plain text including tags which are inserted to represent different types of output
-which are not guaranteed to have the same value on subsequent runs or when run on different
-platforms.
-
-The following is a list of all tags and what they are used to represent:
-
- - ``%e``: Represents a directory separator, for example / on Linux.
- - ``%s``: One or more of anything (character or white space) except the end of line character.
- - ``%S``: Zero or more of anything (character or white space) except the end of line character.
- - ``%a``: One or more of anything (character or white space) including the end of line
- character.
- - ``%A``: Zero or more of anything (character or white space) including the end of line
- character.
- - ``%w``: Zero or more white space characters.
- - ``%i``: A signed integer value, for example +3142, -3142, 3142.
- - ``%d``: An unsigned integer value, for example 123456.
- - ``%x``: One or more hexadecimal character. That is, characters in the range 0-9, a-f, A-F.
- - ``%f``: A floating point number, for example: 3.142, -3.142, 3.142E-10, 3.142e+10.
- - ``%c``: A single character of any sort (.).
- - ``%r...%r``: Any string (...) enclosed between two ``%r`` will be treated as a regular
- expression.
-
-Example 1 (snippet):
-
-.. code:: text
-
- --EXPECTF--
- string(4) "test"
- string(18) "http://example.com"
- string(27) "<b>test</b>"
-
- Notice: Object of class stdClass could not be converted to int in %ssample001.php on line %d
- bool(false)
- string(6) "string"
- float(12345.7)
- string(29) "<p>string</p>"
- bool(false)
-
- Warning: filter_var() expects parameter 2 to be long, string given in %s011.php on line %d
- NULL
-
- Warning: filter_input() expects parameter 3 to be long, string given in %s011.php on line %d
- NULL
-
- Warning: filter_var() expects at most 3 parameters, 5 given in %s011.php on line %d
- NULL
-
- Warning: filter_var() expects at most 3 parameters, 5 given in %s011.php on line %d
- NULL
- Done
-
-Example 1 (full): :ref:`sample001.phpt`
-
-Example 2 (snippet):
-
-.. code:: text
-
- --EXPECTF--
- Warning: bzopen() expects exactly 2 parameters, 0 given in %s on line %d NULL
-
- Warning: bzopen(): '' is not a valid mode for bzopen(). Only 'w' and 'r' are supported. in %s on line %d
- bool(false)
-
- Warning: bzopen(): filename cannot be empty in %s on line %d
- bool(false)
-
- Warning: bzopen(): filename cannot be empty in %s on line %d
- bool(false)
-
- Warning: bzopen(): 'x' is not a valid mode for bzopen(). Only 'w' and 'r' are supported. in %s on line %d
- bool(false)
-
- Warning: bzopen(): 'rw' is not a valid mode for bzopen(). Only 'w' and 'r' are supported. in %s on line %d
- bool(false)
-
- Warning: bzopen(no_such_file): failed to open stream: No such file or directory in %s on line %d
- bool(false)
- resource(%d) of type (stream) Done
-
-Example 2 (full): :ref:`sample019.phpt`
-
-Example 3 (snippet):
-
-.. code:: text
-
- --EXPECTF--
- object(DOMNodeList)#%d (0) {
- }
- int(0)
- bool(true)
- bool(true)
- string(0) ""
- bool(true)
- bool(true)
- bool(false)
- bool(false)
-
-Example 2 (full): :ref:`sample020.phpt`
-
-``--EXPECTF_EXTERNAL--``
-------------------------
-
-**Description:** Similar to ``--EXPECTF--`` section, but like the ``--EXPECT_EXTERNAL--`` section
-just stating a filename where to load the expected output from.
-
-**Required:** One of the ``EXPECT`` type sections is required.
-
-**Test Script Support:** ``run-tests.php``
-
-``--EXPECTREGEX--``
--------------------
-
-**Description:** An alternative of ``--EXPECT--``. This form allows the tester to specify the result
-in a regular expression.
-
-**Required:** One of the ``EXPECT`` type sections is required.
-
-**Format:** Plain text including regular expression patterns which represent data that can vary
-between subsequent runs of a test or when run on different platforms.
-
-Example 1 (snippet):
-
-.. code:: text
-
- --EXPECTREGEX--
- M_E : 2.718281[0-9]*
- M_LOG2E : 1.442695[0-9]*
- M_LOG10E : 0.434294[0-9]*
- M_LN2 : 0.693147[0-9]*
- M_LN10 : 2.302585[0-9]*
- M_PI : 3.141592[0-9]*
- M_PI_2 : 1.570796[0-9]*
- M_PI_4 : 0.785398[0-9]*
- M_1_PI : 0.318309[0-9]*
- M_2_PI : 0.636619[0-9]*
- M_SQRTPI : 1.772453[0-9]*
- M_2_SQRTPI: 1.128379[0-9]*
- M_LNPI : 1.144729[0-9]*
- M_EULER : 0.577215[0-9]*
- M_SQRT2 : 1.414213[0-9]*
- M_SQRT1_2 : 0.707106[0-9]*
- M_SQRT3 : 1.732050[0-9]*
-
-Example 1 (full): :ref:`sample021.phpt`
-
-Example 2 (snippet):
-
-.. code:: text
-
- --EXPECTF--
- *** Testing imap_append() : basic functionality ***
- Create a new mailbox for test
- Create a temporary mailbox and add 0 msgs
- .. mailbox '%s' created
- Add a couple of msgs to new mailbox {%s}INBOX.%s
- bool(true)
- bool(true)
- Msg Count after append : 2
- List the msg headers
- array(2) {
- [0]=>
- string(%d) "%w%s 1)%s webmaster@something. Test message (%d chars)"
- [1]=>
- string(%d) "%w%s 2)%s webmaster@something. Another test (%d chars)"
- }
-
-Example 2 (full): :ref:`sample025.phpt`
-
-Example 3 (snippet):
-
-.. code:: text
-
- --EXPECTREGEX--
- string\(4\) \"-012\"
- string\(8\) \"2d303132\"
- (string\(13\) \" 4294967284\"|string\(20\) \"18446744073709551604\")
- (string\(26\) \"20202034323934393637323834\"|string\(40\) \"3138343436373434303733373039353531363034\")
-
- Example 3 (full): :ref:`sample023.phpt`
-
-``--EXPECTREGEX_EXTERNAL--``
-----------------------------
-
-**Description:** Similar to ``--EXPECTREGEX--`` section, but like the ``--EXPECT_EXTERNAL--``
-section just stating a filename where to load the expected output from.
-
-**Required:** One of the ``EXPECT`` type sections is required.
-
-**Test Script Support:** ``run-tests.php``
-
-``--CLEAN--``
--------------
-
-**Description:** Code that is executed after a test completes. It's main purpose is to allow you to
-clean up after yourself. You might need to remove files created during the test or close sockets or
-database connections following a test. Infact, even if a test fails or encounters a fatal error
-during the test, the code found in the ``--CLEAN--`` section will still run.
-
-Code in the clean section is run in a completely different process than the one the test was run in.
-So do not try accessing variables you created in the ``--FILE--`` section from inside the
-``--CLEAN--`` section, they won't exist.
-
-Using the switch ``--no-clean`` on ``run-tests.php``, you can prevent the code found in the
-``--CLEAN--`` section of a test from running. This allows you to inspect generated data or files
-without them being removed by the ``--CLEAN--`` section.
-
-**Required:** No.
-
-**Test Script Support:** ``run-tests.php``
-
-**Format:** PHP source code enclosed by PHP tags.
-
-Example 1 (snippet):
-
-.. code:: php
-
- --CLEAN--
-
-
-Example 1 (full): :ref:`sample024.phpt`
-
-Example 2 (snippet):
-
-.. code:: php
-
- --CLEAN--
-
-
-Example 2 (full): :ref:`sample025.phpt`
-
-Example 3 (snippet):
-
-.. code:: php
-
- --CLEAN--
-
-
-Example 3 (full): :ref:`sample022.phpt`
-
-Samples
-=======
-
-capture_stdio_1.phpt
---------------------
-
-.. code:: php
-
- --TEST--
- Test covering the I/O stdin and stdout streams.
- --DESCRIPTION--
- This tests checks if the output of stdin and stdout I/O streams match the
- expected content.
- --CAPTURE_STDIO--
- STDIN STDERR
- --FILE--
-
- --EXPECT--
- This is error sent to the stderr I/O stream
-
-capture_stdio_2.phpt
---------------------
-
-.. code:: php
-
- --TEST--
- Test covering the I/O stdin and stderr streams.
- --DESCRIPTION--
- This tests checks if the output of stdin and stderr I/O streams match the
- expected content.
- --CAPTURE_STDIO--
- STDIN STDOUT
- --FILE--
-
- --EXPECT--
- Hello, world. This is sent to the stdout I/O stream
-
-capture_stdio_3.phpt
---------------------
-
-.. code:: php
-
- --TEST--
- Test covering the all standard I/O streams.
- --DESCRIPTION--
- This tests checks if the output of stdin, stdout and stderr I/O streams match
- the expected content.
- --CAPTURE_STDIO--
- STDIN STDOUT STDERR
- --FILE--
-
- --EXPECT--
- Hello, world. This is sent to the stdout I/O stream
- This is error sent to the stderr I/O stream
-
-clean.php
----------
-
-.. code:: php
-
- Nmsgs; $i++) {
- imap_delete($imap_stream, $i);
- }
-
- $mailboxes = imap_getmailboxes($imap_stream, $server, '*');
-
- foreach($mailboxes as $value) {
- // Only delete mailboxes with our prefix
- if (preg_match('/\{.*?\}INBOX\.(.+)/', $value->name, $match) == 1) {
- if (strlen($match[1]) >= strlen($mailbox_prefix)
- && substr_compare($match[1], $mailbox_prefix, 0, strlen($mailbox_prefix)) == 0) {
- imap_deletemailbox($imap_stream, $value->name);
- }
- }
- }
-
- imap_close($imap_stream, CL_EXPUNGE);
- ?>
-
-conflicts_1.phpt
-----------------
-
-.. code:: php
-
- --TEST--
- Test get_headers() function : test with context
- --CONFLICTS--
- server
- --FILE--
- array(
- 'method' => 'HEAD'
- )
- );
-
- $context = stream_context_create($opts);
- $headers = get_headers("http://".PHP_CLI_SERVER_ADDRESS, 1, $context);
- echo $headers["X-Request-Method"]."\n";
-
- stream_context_set_default($opts);
- $headers = get_headers("http://".PHP_CLI_SERVER_ADDRESS, 1);
- echo $headers["X-Request-Method"]."\n";
-
- echo "Done";
- ?>
- --EXPECT--
- HEAD
- HEAD
- Done
-
-extensions.phpt
----------------
-
-.. code:: php
-
- --TEST--
- phpt EXTENSIONS directive with shared extensions
- --DESCRIPTION--
- This test covers the presence of some loaded extensions with a list of additional
- extensions to be loaded when running test.
- --EXTENSIONS--
- curl
- imagick
- tokenizer
- --FILE--
-
- --EXPECT--
- bool(true)
- bool(true)
- bool(true)
-
-file012.phpt
-------------
-
-.. code:: php
-
-
-
-phpdbg_1.phpt
--------------
-
-.. code:: php
-
- --TEST--
- Test deleting breakpoints
- --PHPDBG--
- b 4
- b del 0
- b 5
- r
- b del 1
- r
- y
- q
- --EXPECTF--
- [Successful compilation of %s]
- prompt> [Breakpoint #0 added at %s:4]
- prompt> [Deleted breakpoint #0]
- prompt> [Breakpoint #1 added at %s:5]
- prompt> 12
- [Breakpoint #1 at %s:5, hits: 1]
- >00005: echo $i++;
- 00006: echo $i++;
- 00007:
- prompt> [Deleted breakpoint #1]
- prompt> Do you really want to restart execution? (type y or n): 1234
- [Script ended normally]
- prompt>
- --FILE--
-
- --INI--
- precision=14
- --SKIPIF--
-
- --GET--
- a=test&b=https://example.com
- --POST--
- c=
string
&d=12345.7
- --FILE--
-
- --EXPECTF--
- string(4) "test"
- string(19) "https://example.com"
- string(27) "<b>test</b>"
-
- Notice: Object of class stdClass could not be converted to int in %ssample001.php on line %d
- bool(false)
- string(6) "string"
- float(12345.7)
- string(29) "<p>string</p>"
- bool(false)
-
- Warning: filter_var() expects parameter 2 to be long, string given in %ssample001.php on line %d
- NULL
-
- Warning: filter_input() expects parameter 3 to be long, string given in %ssample001.php on line %d
- NULL
-
- Warning: filter_var() expects at most 3 parameters, 5 given in %ssample001.php on line %d
- NULL
-
- Warning: filter_var() expects at most 3 parameters, 5 given in %ssample001.php on line %d
- NULL
- Done
-
-sample002.phpt
---------------
-
-.. code:: php
-
- --TEST--
- Test receipt of cookie data.
- --CREDITS--
- Zoe Slattery zoe@php.net
- # TestFest Munich 2009-05-19
- --COOKIE--
- hello=World;goodbye=MrChips
- --FILE--
-
- --EXPECT--
- array(2) {
- ["hello"]=>
- string(5) "World"
- ["goodbye"]=>
- string(7) "MrChips"
- }
-
-sample003.phpt
---------------
-
-.. code:: php
-
- --TEST--
- session object deserialization
- --SKIPIF--
-
- --INI--
- session.use_cookies=0
- session.cache_limiter=
- register_globals=1
- session.serialize_handler=php
- session.save_handler=files
- --FILE--
- yes++; }
- }
-
- session_id("abtest");
- session_start();
- session_decode('baz|O:3:"foo":2:{s:3:"bar";s:2:"ok";s:3:"yes";i:1;}arr|a:1:{i:3;O:3:"foo":2:{s:3:"bar";s:2:"ok";s:3:"yes";i:1;}}');
-
- $baz->method();
- $arr[3]->method();
-
- var_dump($baz);
- var_dump($arr);
- session_destroy();
- --EXPECT--
- object(foo)#1 (2) {
- ["bar"]=>
- string(2) "ok"
- ["yes"]=>
- int(2)
- }
- array(1) {
- [3]=>
- object(foo)#2 (2) {
- ["bar"]=>
- string(2) "ok"
- ["yes"]=>
- int(2)
- }
- }
-
-sample005.phpt
---------------
-
-.. code:: php
-
- --TEST--
- SOAP Server 19: compressed request (gzip)
- --SKIPIF--
-
- --INI--
- precision=14
- --GZIP_POST--
-
-
-
-
-
- --FILE--
- "http://testuri.org"));
- $server->addfunction("test");
- $server->handle();
- echo "ok\n";
- ?>
- --EXPECT--
-
- Hello World
- ok
-
-sample006.phpt
---------------
-
-.. code:: php
-
- --TEST--
- is_uploaded_file() function
- --CREDITS--
- Dave Kelsey
- --SKIPIF--
-
- --POST_RAW--
- Content-type: multipart/form-data, boundary=AaB03x
-
- --AaB03x
- content-disposition: form-data; name="field1"
-
- Joe Blow
- --AaB03x
- content-disposition: form-data; name="pics"; filename="file1.txt"
- Content-Type: text/plain
-
- abcdef123456789
- --AaB03x--
- --FILE--
-
- --EXPECTF--
- bool(true)
- bool(false)
- bool(false)
- bool(false)
-
- Warning: is_uploaded_file() expects exactly 1 parameter, 0 given in %s on line %d
- NULL
-
- Warning: is_uploaded_file() expects exactly 1 parameter, 2 given in %s on line %d
- NULL
-
-sample007.phpt
---------------
-
-.. code:: php
-
- --TEST--
- SOAP Server 20: compressed request (deflate)
- --SKIPIF--
-
- --INI--
- precision=14
- --DEFLATE_POST--
-
-
-
-
-
-
- --FILE--
- "http://testuri.org"));
- $server->addfunction("test");
- $server->handle();
- echo "ok\n";
- ?>
- --EXPECT--
-
- Hello World
- ok
-
-sample008.phpt
---------------
-
-.. code:: php
-
- --TEST--
- GET/POST/REQUEST Test with input_filter
- --SKIPIF--
-
- --POST--
- d=379
- --GET--
- ar[elm1]=1234&ar[elm2]=0660&a=0234
- --FILE--
- FILTER_FLAG_ALLOW_OCTAL));
- var_dump($ret);
-
- $ret = filter_input(INPUT_GET, 'ar', FILTER_VALIDATE_INT, array('flags'=>FILTER_REQUIRE_ARRAY));
- var_dump($ret);
-
- $ret = filter_input(INPUT_GET, 'ar', FILTER_VALIDATE_INT, array('flags'=>FILTER_FLAG_ALLOW_OCTAL|FILTER_REQUIRE_ARRAY));
- var_dump($ret);
-
- ?>
- --EXPECT--
- bool(false)
- int(156)
- array(2) {
- ["elm1"]=>
- int(1234)
- ["elm2"]=>
- bool(false)
- }
- array(2) {
- ["elm1"]=>
- int(1234)
- ["elm2"]=>
- int(432)
- }
-
-sample009.phpt
---------------
-
-.. code:: php
-
- --TEST--
- STDIN input
- --FILE--
-
- --STDIN--
- fooBar
- use this to input some thing to the php script
- --EXPECT--
- string(54) "fooBar
- use this to input some thing to the php script
- "
-
-sample010.phpt
---------------
-
-.. code:: php
-
- --TEST--
- getopt#005 (Required values)
- --ARGS--
- --arg value --arg=value -avalue -a=value -a value
- --INI--
- register_argc_argv=On
- variables_order=GPS
- --FILE--
-
- --EXPECT--
- array(2) {
- ["arg"]=>
- array(2) {
- [0]=>
- string(5) "value"
- [1]=>
- string(5) "value"
- }
- ["a"]=>
- array(3) {
- [0]=>
- string(5) "value"
- [1]=>
- string(5) "value"
- [2]=>
- string(5) "value"
- }
- }
-
-sample011.phpt
---------------
-
-.. code:: php
-
- --TEST--
- Bug #35382 (Comment in end of file produces fatal error)
- --FILEEOF--
-
- --REDIRECTTEST--
- return array(
- 'ENV' => array(
- 'PDOTEST_DSN' => 'sqlite2::memory:'
- ),
- 'TESTS' => 'ext/pdo/tests'
- );
-
-sample014.phpt
---------------
-
-.. code:: php
-
- --TEST--
- MySQL
- --SKIPIF--
-
- --REDIRECTTEST--
- # magic auto-configuration
-
- $config = array(
- 'TESTS' => 'ext/pdo/tests'
- );
-
- if (false !== getenv('PDO_MYSQL_TEST_DSN')) {
- # user set them from their shell
- $config['ENV']['PDOTEST_DSN'] = getenv('PDO_MYSQL_TEST_DSN');
- $config['ENV']['PDOTEST_USER'] = getenv('PDO_MYSQL_TEST_USER');
- $config['ENV']['PDOTEST_PASS'] = getenv('PDO_MYSQL_TEST_PASS');
- if (false !== getenv('PDO_MYSQL_TEST_ATTR')) {
- $config['ENV']['PDOTEST_ATTR'] = getenv('PDO_MYSQL_TEST_ATTR');
- }
- } else {
- $config['ENV']['PDOTEST_DSN'] = 'mysql:host=localhost;dbname=test';
- $config['ENV']['PDOTEST_USER'] = 'root';
- $config['ENV']['PDOTEST_PASS'] = '';
- }
-
- return $config;
-
-sample016.phpt
---------------
-
-.. code:: php
-
- --TEST--
- Test get variables with CGI binary
- --GET--
- hello=World&goodbye=MrChips
- --CGI--
- --FILE--
-
- --EXPECT--
- array(2) {
- ["hello"]=>
- string(5) "World"
- ["goodbye"]=>
- string(7) "MrChips"
- }
-
-sample017.phpt
---------------
-
-.. code:: php
-
- --TEST--
- PDO Common: Bug #34630 (inserting streams as LOBs)
- --SKIPIF--
-
- --FILE--
- getAttribute(PDO::ATTR_DRIVER_NAME);
- $is_oci = $driver == 'oci';
-
- if ($is_oci) {
- $db->exec('CREATE TABLE test (id int NOT NULL PRIMARY KEY, val BLOB)');
- } else {
- $db->exec('CREATE TABLE test (id int NOT NULL PRIMARY KEY, val VARCHAR(256))');
- }
- $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
-
- $fp = tmpfile();
- fwrite($fp, "I am the LOB data");
- rewind($fp);
-
- if ($is_oci) {
- /* oracle is a bit different; you need to initiate a transaction otherwise
- * the empty blob will be committed implicitly when the statement is
- * executed */
- $db->beginTransaction();
- $insert = $db->prepare("insert into test (id, val) values (1, EMPTY_BLOB()) RETURNING val INTO :blob");
- } else {
- $insert = $db->prepare("insert into test (id, val) values (1, :blob)");
- }
- $insert->bindValue(':blob', $fp, PDO::PARAM_LOB);
- $insert->execute();
- $insert = null;
-
- $db->setAttribute(PDO::ATTR_STRINGIFY_FETCHES, true);
- var_dump($db->query("SELECT * from test")->fetchAll(PDO::FETCH_ASSOC));
-
- ?>
- --XFAIL--
- This bug might be still open on aix5.2-ppc64 and hpux11.23-ia64
- --EXPECT--
- array(1) {
- [0]=>
- array(2) {
- ["id"]=>
- string(1) "1"
- ["val"]=>
- string(17) "I am the LOB data"
- }
- }
-
-sample018.phpt
---------------
-
-.. code:: php
-
- --TEST--
- Phar front controller rewrite access denied [cache_list]
- --INI--
- default_charset=UTF-8
- phar.cache_list={PWD}/frontcontroller10.php
- --SKIPIF--
-
- --ENV--
- SCRIPT_NAME=/frontcontroller10.php
- REQUEST_URI=/frontcontroller10.php/hi
- PATH_INFO=/hi
- --FILE_EXTERNAL--
- files/frontcontroller4.phar
- --EXPECTHEADERS--
- Content-type: text/html; charset=UTF-8
- Status: 403 Access Denied
- --EXPECT--
-
-
- Access Denied
-
-
-
403 - File /hi Access Denied
-
-
-
-sample019.phpt
---------------
-
-.. code:: php
-
- --TEST--
- bzopen() and invalid parameters
- --SKIPIF--
-
- --FILE--
-
- --EXPECTF--
- Warning: bzopen() expects exactly 2 parameters, 0 given in %s on line %d
- NULL
-
- Warning: bzopen(): '' is not a valid mode for bzopen(). Only 'w' and 'r' are supported. in %s on line %d
- bool(false)
-
- Warning: bzopen(): filename cannot be empty in %s on line %d
- bool(false)
-
- Warning: bzopen(): filename cannot be empty in %s on line %d
- bool(false)
-
- Warning: bzopen(): 'x' is not a valid mode for bzopen(). Only 'w' and 'r' are supported. in %s on line %d
- bool(false)
-
- Warning: bzopen(): 'rw' is not a valid mode for bzopen(). Only 'w' and 'r' are supported. in %s on line %d
- bool(false)
-
- Warning: bzopen(no_such_file): failed to open stream: No such file or directory in %s on line %d
- bool(false)
- resource(%d) of type (stream)
- Done
-
-sample020.phpt
---------------
-
-.. code:: php
-
- --TEST--
- Bug #42082 (NodeList length zero should be empty)
- --FILE--
- query('*');
- var_dump($nodes);
- var_dump($nodes->length);
- $length = $nodes->length;
- var_dump(empty($nodes->length), empty($length));
-
- $doc->loadXML("");
- var_dump($doc->firstChild->nodeValue, empty($doc->firstChild->nodeValue), isset($doc->firstChild->nodeValue));
- var_dump(empty($doc->nodeType), empty($doc->firstChild->nodeType))
- ?>
- --EXPECTF--
- object(DOMNodeList)#%d (0) {
- }
- int(0)
- bool(true)
- bool(true)
- string(0) ""
- bool(true)
- bool(true)
- bool(false)
- bool(false)
-
-sample021.phpt
---------------
-
-.. code:: php
-
- --TEST--
- Math constants
- --INI--
- precision=14
- --FILE--
-
- --EXPECTREGEX--
- M_E : 2.718281[0-9]*
- M_LOG2E : 1.442695[0-9]*
- M_LOG10E : 0.434294[0-9]*
- M_LN2 : 0.693147[0-9]*
- M_LN10 : 2.302585[0-9]*
- M_PI : 3.141592[0-9]*
- M_PI_2 : 1.570796[0-9]*
- M_PI_4 : 0.785398[0-9]*
- M_1_PI : 0.318309[0-9]*
- M_2_PI : 0.636619[0-9]*
- M_SQRTPI : 1.772453[0-9]*
- M_2_SQRTPI: 1.128379[0-9]*
- M_LNPI : 1.144729[0-9]*
- M_EULER : 0.577215[0-9]*
- M_SQRT2 : 1.414213[0-9]*
- M_SQRT1_2 : 0.707106[0-9]*
- M_SQRT3 : 1.732050[0-9]*
-
-sample022.phpt
---------------
-
-.. code:: php
-
- --TEST--
- shm_detach() tests
- --SKIPIF--
-
- --FILE--
-
- --CLEAN--
-
- --EXPECTF--
- Warning: shm_detach() expects exactly 1 parameter, 0 given in %ssample022.php on line %d
- NULL
-
- Warning: shm_detach() expects exactly 1 parameter, 2 given in %ssample022.php on line %d
- NULL
- bool(true)
-
- Warning: shm_detach(): %d is not a valid sysvshm resource in %ssample022.php on line %d
- bool(false)
-
- Warning: shm_remove(): %d is not a valid sysvshm resource in %ssample022.php on line %d
-
- Warning: shm_detach() expects parameter 1 to be resource, integer given in %ssample022.php on line %d
- NULL
-
- Warning: shm_detach() expects parameter 1 to be resource, integer given in %ssample022.php on line %d
- NULL
-
- Warning: shm_detach() expects parameter 1 to be resource, integer given in %ssample022.php on line %d
- NULL
- Done
-
-sample023.phpt
---------------
-
-.. code:: php
-
- --TEST--
- Bug #23894 (sprintf() decimal specifiers problem)
- --FILE--
-
- --EXPECTREGEX--
- string\(4\) \"-012\"
- string\(8\) \"2d303132\"
- (string\(13\) \" 4294967284\"|string\(20\) \"18446744073709551604\")
- (string\(26\) \"20202034323934393637323834\"|string\(40\) \"3138343436373434303733373039353531363034\")
-
-sample024.phpt
---------------
-
-.. code:: php
-
- --TEST--
- DOMDocument::save Test basic function of save method
- --SKIPIF--
-
- --FILE--
- formatOutput = true;
-
- $root = $doc->createElement('book');
-
- $root = $doc->appendChild($root);
-
- $title = $doc->createElement('title');
- $title = $root->appendChild($title);
-
- $text = $doc->createTextNode('This is the title');
- $text = $title->appendChild($text);
-
- $temp_filename = __DIR__.'/DomDocument_save_basic.tmp';
-
- echo 'Wrote: ' . $doc->save($temp_filename) . ' bytes'; // Wrote: 72 bytes
- ?>
- --CLEAN--
-
- --EXPECTF--
- Wrote: 72 bytes
-
-sample025.phpt
---------------
-
-.. code:: php
-
- --TEST--
- Test imap_append() function : basic functionality
- --SKIPIF--
-
- --FILE--
- Mailbox . "\n";
- var_dump(imap_append($imap_stream, $mb_details->Mailbox
- , "From: webmaster@something.com\r\n"
- . "To: info@something.com\r\n"
- . "Subject: Test message\r\n"
- . "\r\n"
- . "this is a test message, please ignore\r\n"
- ));
-
- var_dump(imap_append($imap_stream, $mb_details->Mailbox
- , "From: webmaster@something.com\r\n"
- . "To: info@something.com\r\n"
- . "Subject: Another test\r\n"
- . "\r\n"
- . "this is another test message, please ignore it too!!\r\n"
- ));
-
- $check = imap_check($imap_stream);
- echo "Msg Count after append : ". $check->Nmsgs . "\n";
-
- echo "List the msg headers\n";
- var_dump(imap_headers($imap_stream));
-
- imap_close($imap_stream);
- ?>
- --CLEAN--
-
- --EXPECTF--
- *** Testing imap_append() : basic functionality ***
- Create a new mailbox for test
- Create a temporary mailbox and add 0 msgs
- .. mailbox '%s' created
- Add a couple of msgs to new mailbox {%s}INBOX.%s
- bool(true)
- bool(true)
- Msg Count after append : 2
- List the msg headers
- array(2) {
- [0]=>
- string(%d) "%w%s 1)%s webmaster@something. Test message (%d chars)"
- [1]=>
- string(%d) "%w%s 2)%s webmaster@something. Another test (%d chars)"
- }
-
-sample026.phpt
---------------
-
-.. code:: php
-
- --TEST--
- SPL: ArrayIterator implementing RecursiveIterator
- --FILE--
- array(21, 22 => array(221, 222), 23 => array(231)), 3);
-
- $dir = new RecursiveIteratorIterator(new RecursiveArrayIterator($array), RecursiveIteratorIterator::LEAVES_ONLY);
-
- foreach ($dir as $file) {
- print "$file\n";
- }
-
- ?>
- ===DONE===
-
- --EXPECT--
- 1
- 21
- 221
- 222
- 231
- 3
-
-skipif2.phpt
-------------
-
-.. code:: php
-
-
-
-skipif.phpt
------------
-
-.. code:: php
-
-
-
-xfailif.phpt
-------------
-
-.. code:: php
-
- --TEST--
- Handling of errors during linking
- --INI--
- opcache.enable=1
- opcache.enable_cli=1
- opcache.optimization_level=-1
- opcache.preload={PWD}/preload_inheritance_error_ind.inc
- --SKIPIF--
-
- --FILE--
-
- --EXPECTF--
- Fatal error: Declaration of B::foo($bar) must be compatible with A::foo() in %spreload_inheritance_error.inc on line 8