Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .github/workflows/unit-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,17 @@ on:
push:
paths:
- 'main/network.c'
- 'main/io/**'
- 'main/php_io.h'
- 'tests/unit/**'
- '.github/workflows/unit-tests.yml'
branches:
- master
pull_request:
paths:
- 'main/network.c'
- 'main/io/**'
- 'main/php_io.h'
- 'tests/unit/**'
- '.github/workflows/unit-tests.yml'
branches:
Expand Down
5 changes: 5 additions & 0 deletions NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@ PHP NEWS
TCP_USER_TIMEOUT, and SO_LINGER options. (Weilin Du)
. Fixed various memory related issues in ext/sockets. (David Carlier)

- Streams:
. Added a new IO copy API used by php_stream_copy_to_stream_ex() that
leverages platform primitives (sendfile, splice, copy_file_range,
TransmitFile) for faster stream copying. (Jakub Zelenka, David Carlier)

16 Jul 2026, PHP 8.6.0alpha2

- Core:
Expand Down
111 changes: 61 additions & 50 deletions UPGRADING.INTERNALS
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,8 @@ PHP 8.6 INTERNALS UPGRADE NOTES
1. Internal API changes
========================

. ZSTR_INIT_LITERAL(), zend_string_starts_with_literal(), and
zend_string_starts_with_literal_ci() now support strings containing NUL
bytes. Passing non-literal char* is no longer supported.
. Added zend_string_equals_cstr_ci().
- Removed:
. The misnamed ZVAL_IS_NULL() has been removed. Use Z_ISNULL() instead.
. New zend_class_entry.ce_flags2 and zend_function.fn_flags2 fields were
added, given the primary flags were running out of bits.
. The zval_is_true() alias of zend_is_true() has been removed. Call
zend_is_true() directly instead.
. The _zval_get_*() compatibility macros for PHP 7.2 have been removed
Expand All @@ -42,45 +37,13 @@ PHP 8.6 INTERNALS UPGRADE NOTES
needs to be obtained use the zend_get_callable_zval_from_fcc() function
instead. If this was used to store a callable, then an FCC should be
stored instead.
. The zend_active_function{_ex}() functions now return a const zend_function
pointer.
. The zend_get_call_trampoline_func() API now takes the __call or
__callStatic zend_function* instead of a CE and a boolean argument.
. The zend_exception_save() and zend_exception_restore() functions were
removed.
. The zend_set_hash_symbol() API has been removed.
. Added zend_hash_str_lookup().
. The WRONG_PARAM_COUNT and ZEND_WRONG_PARAM_COUNT() macros have been
removed. Call zend_wrong_param_count(); followed by RETURN_THROWS();
instead.
. PHP_HAVE_STREAMS macro removed from <php.h>.
. zend_function.arg_info is now always a zend_arg_info*. Before, it was a
zend_internal_arg_info on internal functions, unless the
ZEND_ACC_USER_ARG_INFO flag was set.
. Added zend_ast_call_get_args() to fetch the argument node from any call
node.
. The zend_exception_save() and zend_exception_restore() functions were
removed.
. Internal functions that return by reference are now expected to
automatically unwrap references when the result of the call is stored in an
IS_TMP_VAR variable. This may be achieved by calling the
zend_return_unwrap_ref() function.
. The php_math_round_mode_from_enum() function now takes a
zend_enum_RoundingMode parameter.
. Added Z_PARAM_ENUM().
. Added zend_enum_fetch_case_id().
. Added zend_enum_get_case_by_id().
. Added zend_bin2hex() and zend_bin2hex_str() as helper functions to remove
dependencies on /ext/hash in various extensions.
. ZEND_INI_GET_ADDR() is now a void* pointer instead of a char* pointer. This
more correctly represents the generic nature of the returned pointer and
allows to remove explicit casts, but possibly breaks pointer arithmetic
performed on the result.
. The zend_dval_to_lval_cap() function no longer takes a second
zend_string* parameter.
. EG(in_autoload) was renamed to EG(autoload_current_classnames) and no
longer is a pointer, but a directly embedded HashTable struct.
. Added a C23_ENUM() helper macro to define forward-compatible fixed-size
enums.
. Extended php_stream_filter_ops with seek method.
. The INI_STR(), INI_INT(), INI_FLT(), and INI_BOOL() macros have been
removed. Instead new zend_ini_{bool|long|double|str|string}_literal()
macros have been added. This fixes an internal naming inconsistency as
Expand All @@ -90,13 +53,8 @@ PHP 8.6 INTERNALS UPGRADE NOTES
unused. If this behaviour is required fall back to the zend_ini_*
functions.
. The unused ZEND_AST_PARENT_PROPERTY_HOOK_CALL has been removed.
. ZEND_AST_METHOD_REFERENCE has been renamed to
ZEND_AST_TRAIT_METHOD_REFERENCE.
. The EMPTY_SWITCH_DEFAULT_CASE() macro has been removed. Use
default: ZEND_UNREACHABLE(); instead.
. Functions using zend_forbid_dynamic_call() *must* be flagged with
ZEND_ACC2_FORBID_DYN_CALLS (@forbid-dynamic-calls in stubs). In debug
builds, failing to include that flag will lead to assertion failures.
. The ZEND_RESULT_CODE type has been removed. Use zend_result directly.
. The zend_parse_parameters_none_throw(), zend_parse_parameters_throw(),
and ZEND_PARSE_PARAMS_THROW have been removed due to being misleading,
Expand All @@ -115,16 +73,63 @@ PHP 8.6 INTERNALS UPGRADE NOTES
have been removed, because they are unsafe by relying on the zvals
having a specific type. Use zend_binary_strcmp() / zend_binary_strncmp(),
string_compare_function() or similar instead.
. Added zend_fcall_info.consumed_args together with
zend_fci_consumed_arg(), which allows moving a selected callback argument
instead of copying it in zend_call_function(). Currently only a single
consumed argument is supported.
. Added ZEND_CONTAINER_OF().
. The OPENBASEDIR_CHECKPATH() compatibility macro has been removed, instead
use php_check_open_basedir() directly.
. The Z_CONSTANT(), Z_CONSTANT_P(), Z_OPT_CONSTANT(), and
Z_OPT_CONSTANT_P() macros have been removed. Check for IS_CONSTANT_AST
directly.
. The {_}php_stream_fopen_with_path() functions have been removed as they are
unused.

- Changed:
. Internal functions that return by reference are now expected to
automatically unwrap references when the result of the call is stored in an
IS_TMP_VAR variable. This may be achieved by calling the
zend_return_unwrap_ref() function.
. ZEND_AST_METHOD_REFERENCE has been renamed to
ZEND_AST_TRAIT_METHOD_REFERENCE.
. Functions using zend_forbid_dynamic_call() *must* be flagged with
ZEND_ACC2_FORBID_DYN_CALLS (@forbid-dynamic-calls in stubs). In debug
builds, failing to include that flag will lead to assertion failures.
. The zend_get_call_trampoline_func() API now takes the __call or
__callStatic zend_function* instead of a CE and a boolean argument.
. ZSTR_INIT_LITERAL(), zend_string_starts_with_literal(), and
zend_string_starts_with_literal_ci() now support strings containing NUL
bytes. Passing non-literal char* is no longer supported.
. Added zend_string_equals_cstr_ci().
. The zend_active_function{_ex}() functions now return a const zend_function
pointer.
. zend_function.arg_info is now always a zend_arg_info*. Before, it was a
zend_internal_arg_info on internal functions, unless the
ZEND_ACC_USER_ARG_INFO flag was set.
. ZEND_INI_GET_ADDR() is now a void* pointer instead of a char* pointer. This
more correctly represents the generic nature of the returned pointer and
allows to remove explicit casts, but possibly breaks pointer arithmetic
performed on the result.
. The zend_dval_to_lval_cap() function no longer takes a second
zend_string* parameter.
. EG(in_autoload) was renamed to EG(autoload_current_classnames) and no
longer is a pointer, but a directly embedded HashTable struct.
. Extended php_stream_filter_ops with seek method.

- Added:
. New zend_class_entry.ce_flags2 and zend_function.fn_flags2 fields were
added, given the primary flags were running out of bits.
. Added zend_hash_str_lookup().
. Added zend_ast_call_get_args() to fetch the argument node from any call
node.
. Added Z_PARAM_ENUM().
. Added zend_enum_fetch_case_id().
. Added zend_enum_get_case_by_id().
. Added zend_bin2hex() and zend_bin2hex_str() as helper functions to remove
dependencies on /ext/hash in various extensions.
. Added a C23_ENUM() helper macro to define forward-compatible fixed-size
enums.
. Added zend_fcall_info.consumed_args together with
zend_fci_consumed_arg(), which allows moving a selected callback argument
instead of copying it in zend_call_function(). Currently only a single
consumed argument is supported.
. Added ZEND_CONTAINER_OF().
. Added zend_reflection_property_set_raw_value_without_lazy_initialization(),
zend_reflection_property_set_raw_value() to expose the functionality of
ReflectionProperty::setRawValueWithoutLazyInitialization() and
Expand All @@ -136,6 +141,10 @@ PHP 8.6 INTERNALS UPGRADE NOTES
. Added zend_compile_ast().
. Added zend_check_type_ex().
. Added zend_create_partial_closure().
. Added a new IO copy API in <php_io.h>. php_io_copy() copies bytes between
file descriptors using the most efficient platform primitive available
(sendfile, splice, copy_file_range, TransmitFile), and is now used by
php_stream_copy_to_stream_ex(). The mmap-based copy fallback was removed.

========================
2. Build system changes
Expand Down Expand Up @@ -219,6 +228,8 @@ PHP 8.6 INTERNALS UPGRADE NOTES
. _php_error_log() now accepts zend_string* values instead of char*.
. _php_error_log_ex() has been removed.
. php_mail()'s extra_cmd parameter is now a zend_string*.
. The php_math_round_mode_from_enum() function now takes a
zend_enum_RoundingMode parameter.

- ext/uri:
. The value parameter of the php_uri_property_handler_write callback is now
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ try {
}
?>
--EXPECTF--
ERR#2: include(class://non.existent.Class): Failed to open stream: "CLWrapper::stream_open" call failed @ include
ERR#2: include(): Failed to open stream: "CLWrapper::stream_open" call failed @ include
ERR#2: include(): Failed opening 'class://non.existent.Class' for inclusion (include_path='%s') @ include

Fatal error: Uncaught Exception: Failed loading class://non.existent.Class in %s
Expand Down
4 changes: 2 additions & 2 deletions Zend/tests/bug30998.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,6 @@ set_error_handler('my_error');
$f = fopen("/tmp/blah", "r");
?>
--EXPECTF--
fopen(/tmp/blah): Failed to open stream: %s (2) in %s:%d
fopen(): Failed to open stream: %s (2) in %s:%d

Warning: fopen(/tmp/blah): Failed to open stream: %s in %s on line %d
Warning: fopen(): Failed to open stream: %s in %s on line %d
4 changes: 2 additions & 2 deletions Zend/tests/bug60909_1.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@ set_error_handler(function($errno, $errstr, $errfile, $errline){
require 'notfound.php';
?>
--EXPECTF--
error(require(notfound.php): Failed to open stream: %s)
error(require(): Failed to open stream: %s)
Fatal error: Uncaught Exception: Foo in %sbug60909_1.php:5
Stack trace:
#0 %s(%d): {closure:%s:%d}(2, 'require(notfoun...', '%s', 8)
#0 %s(%d): {closure:%s:%d}(2, 'require(): Fail...', '%s', 8)
#1 %sbug60909_1.php(8): require()
#2 {main}
thrown in %sbug60909_1.php on line 5
Expand Down
2 changes: 1 addition & 1 deletion Zend/tests/gh11735_2.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,5 @@ stream_wrapper_register('foo', 'FooWrapper');
var_dump(fopen('foo://bar', 'r'));
?>
--EXPECTF--
Warning: fopen(foo://bar): Failed to open stream: "FooWrapper::stream_open" call failed in %s on line %d
Warning: fopen(): Failed to open stream: "FooWrapper::stream_open" call failed in %s on line %d
bool(false)
2 changes: 1 addition & 1 deletion Zend/tests/include_stat_is_quiet.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ try {

?>
--EXPECTF--
Warning: require_once(doesnt_exist.php): Failed to open stream: No such file or directory in %s on line %d
Warning: require_once(): Failed to open stream: No such file or directory in %s on line %d

Fatal error: Uncaught Error: Failed opening required 'doesnt_exist.php' (include_path='test://foo:test://bar') in %s:%d
Stack trace:
Expand Down
2 changes: 1 addition & 1 deletion Zend/tests/oss_fuzz_64209.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ oss-fuzz #64209: Fix in-place modification of filename in php_message_handler_fo
require '://@';
?>
--EXPECTF--
Warning: require(://@): Failed to open stream: No such file or directory in %s on line %d
Warning: require(): Failed to open stream: No such file or directory in %s on line %d

Fatal error: Uncaught Error: Failed opening required '://@' (include_path='%s') in %s:%d
Stack trace:
Expand Down
2 changes: 1 addition & 1 deletion Zend/tests/require_once_warning_to_exception.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,4 @@ try {

?>
--EXPECT--
require_once(does-not-exist.php): Failed to open stream: No such file or directory
require_once(): Failed to open stream: No such file or directory
3 changes: 1 addition & 2 deletions Zend/zend_object_handlers.c
Original file line number Diff line number Diff line change
Expand Up @@ -2178,10 +2178,9 @@ ZEND_API zval *zend_std_get_static_property(zend_class_entry *ce, zend_string *p
return zend_std_get_static_property_with_info(ce, property_name, type, &prop_info);
}

ZEND_API ZEND_COLD bool zend_std_unset_static_property(const zend_class_entry *ce, const zend_string *property_name) /* {{{ */
ZEND_API ZEND_COLD void zend_std_unset_static_property(const zend_class_entry *ce, const zend_string *property_name) /* {{{ */
{
zend_throw_error(NULL, "Attempt to unset static property %s::$%s", ZSTR_VAL(ce->name), ZSTR_VAL(property_name));
return 0;
}
/* }}} */

Expand Down
2 changes: 1 addition & 1 deletion Zend/zend_object_handlers.h
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,7 @@ ZEND_API void zend_class_init_statics(zend_class_entry *ce);
ZEND_API zend_function *zend_std_get_static_method(const zend_class_entry *ce, zend_string *function_name_strval, const zval *key);
ZEND_API zval *zend_std_get_static_property_with_info(zend_class_entry *ce, zend_string *property_name, int type, struct _zend_property_info **prop_info);
ZEND_API zval *zend_std_get_static_property(zend_class_entry *ce, zend_string *property_name, int type);
ZEND_API ZEND_COLD bool zend_std_unset_static_property(const zend_class_entry *ce, const zend_string *property_name);
ZEND_API ZEND_COLD void zend_std_unset_static_property(const zend_class_entry *ce, const zend_string *property_name);
ZEND_API zend_function *zend_std_get_constructor(zend_object *object);
ZEND_API struct _zend_property_info *zend_get_property_info(const zend_class_entry *ce, zend_string *member, int silent);
ZEND_API HashTable *zend_std_get_properties(zend_object *object);
Expand Down
14 changes: 14 additions & 0 deletions configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -369,6 +369,9 @@ AC_SEARCH_LIBS([Pgrab], [proc])
dnl Haiku does not have network api in libc.
AC_SEARCH_LIBS([setsockopt], [network])

dnl Solaris/illumos provide sendfile() in libsendfile; libc on Linux/FreeBSD.
AC_SEARCH_LIBS([sendfile], [sendfile])

dnl Check for openpty. It may require linking against libutil or libbsd.
AC_CHECK_FUNCS([openpty],,
[AC_SEARCH_LIBS([openpty], [util bsd], [AC_DEFINE([HAVE_OPENPTY], [1])])])
Expand Down Expand Up @@ -588,10 +591,12 @@ AC_CHECK_FUNCS(m4_normalize([
putenv
reallocarray
scandir
sendfile
setenv
setitimer
shutdown
sigprocmask
splice
statfs
statvfs
std_syslog
Expand Down Expand Up @@ -1685,6 +1690,15 @@ PHP_ADD_SOURCES_X([main],
[PHP_FASTCGI_OBJS],
[no])

PHP_ADD_SOURCES([main/io], m4_normalize([
php_io.c
php_io_copy_linux.c
php_io_copy_freebsd.c
php_io_copy_solaris.c
php_io_copy_macos.c
]),
[-DZEND_ENABLE_STATIC_TSRMLS_CACHE=1])

PHP_ADD_SOURCES([main/poll], m4_normalize([
poll_backend_epoll.c
poll_backend_eventport.c
Expand Down
2 changes: 1 addition & 1 deletion ext/bz2/tests/001.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,6 @@ bzopen(): Argument #2 ($mode) must be either "r" or "w"
bzopen(): Argument #2 ($mode) must be either "r" or "w"
bzopen(): Argument #2 ($mode) must be either "r" or "w"

Warning: bzopen(no_such_file): Failed to open stream: No such file or directory in %s on line %d
Warning: bzopen(): Failed to open stream: No such file or directory in %s on line %d
bool(false)
resource(%d) of type (stream)
4 changes: 2 additions & 2 deletions ext/bz2/tests/002.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -90,10 +90,10 @@ bool(false)
resource(%d) of type (stream)
resource(%d) of type (stream)

Warning: fopen(bz_open_002.txt): Failed to open stream: `br' is not a valid mode for fopen in %s on line %d
Warning: fopen(): Failed to open stream: `br' is not a valid mode for fopen in %s on line %d
bzopen(): Argument #1 ($file) must be of type string or file-resource, false given

Warning: fopen(bz_open_002.txt): Failed to open stream: `br' is not a valid mode for fopen in %s on line %d
Warning: fopen(): Failed to open stream: `br' is not a valid mode for fopen in %s on line %d
bzopen(): Argument #1 ($file) must be of type string or file-resource, false given

Warning: bzopen(): cannot write to a stream opened in read only mode in %s on line %d
Expand Down
16 changes: 8 additions & 8 deletions ext/dba/tests/dba_cdb_creation_matrix.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -25,35 +25,35 @@ clean_creation_tests($handler);
=== OPENING NON-EXISTING FILE ===
Mode parameter is "rl":

Warning: dba_open(cdb_not_existing.db): Failed to open stream: No such file or directory in %s on line %d
Warning: dba_open(): Failed to open stream: No such file or directory in %s on line %d
Opening DB failed
Mode parameter is "rd":

Warning: dba_open(cdb_not_existing.db): Failed to open stream: No such file or directory in %s on line %d
Warning: dba_open(): Failed to open stream: No such file or directory in %s on line %d
Opening DB failed
Mode parameter is "r-":

Warning: dba_open(cdb_not_existing.db): Failed to open stream: No such file or directory in %s on line %d
Warning: dba_open(): Failed to open stream: No such file or directory in %s on line %d
Opening DB failed
Mode parameter is "r":

Warning: dba_open(cdb_not_existing.db): Failed to open stream: No such file or directory in %s on line %d
Warning: dba_open(): Failed to open stream: No such file or directory in %s on line %d
Opening DB failed
Mode parameter is "wl":

Warning: dba_open(cdb_not_existing.db): Failed to open stream: No such file or directory in %s on line %d
Warning: dba_open(): Failed to open stream: No such file or directory in %s on line %d
Opening DB failed
Mode parameter is "wd":

Warning: dba_open(cdb_not_existing.db): Failed to open stream: No such file or directory in %s on line %d
Warning: dba_open(): Failed to open stream: No such file or directory in %s on line %d
Opening DB failed
Mode parameter is "w-":

Warning: dba_open(cdb_not_existing.db): Failed to open stream: No such file or directory in %s on line %d
Warning: dba_open(): Failed to open stream: No such file or directory in %s on line %d
Opening DB failed
Mode parameter is "w":

Warning: dba_open(cdb_not_existing.db): Failed to open stream: No such file or directory in %s on line %d
Warning: dba_open(): Failed to open stream: No such file or directory in %s on line %d
Opening DB failed
Mode parameter is "cl":

Expand Down
Loading