diff --git a/UPGRADING.INTERNALS b/UPGRADING.INTERNALS index 84ef7ac8bc7d4..cdc8932df15d3 100644 --- a/UPGRADING.INTERNALS +++ b/UPGRADING.INTERNALS @@ -67,6 +67,9 @@ PHP 8.3 INTERNALS UPGRADE NOTES - zend_set_user_opcode_handler - zend_ssa_inference * Removed unused macros PHP_FNV1_32A_INIT and PHP_FNV1A_64_INIT. See GH-11114. +* _php_stream_dirent now has an extra d_type field that is used to store the + directory entry type. This can be used to avoid additional stat calls for + types when the type is already known. ======================== 2. Build system changes @@ -115,6 +118,7 @@ PHP 8.3 INTERNALS UPGRADE NOTES e. ext/spl - The PHPAPI spl_iterator_apply() function now returns zend_result instead of int. There are no functional changes. + - The field _spl_filesystem_object->is_recursive has been removed. f. ext/dom - A new function dom_get_doc_props_read_only() is added to gather the document diff --git a/Zend/zend_virtual_cwd.h b/Zend/zend_virtual_cwd.h index f819389af33c7..15c3b984a9dc7 100644 --- a/Zend/zend_virtual_cwd.h +++ b/Zend/zend_virtual_cwd.h @@ -87,6 +87,16 @@ typedef unsigned short mode_t; #else #ifdef HAVE_DIRENT_H #include + +#ifndef DT_UNKNOWN +# define DT_UNKNOWN 0 +#endif +#ifndef DT_DIR +# define DT_DIR 4 +#endif +#ifndef DT_REG +# define DT_REG 8 +#endif #endif #define DEFAULT_SLASH '/' diff --git a/ext/phar/dirstream.c b/ext/phar/dirstream.c index ede5397bb51d7..d7fda32de0422 100644 --- a/ext/phar/dirstream.c +++ b/ext/phar/dirstream.c @@ -108,6 +108,7 @@ static ssize_t phar_dir_read(php_stream *stream, char *buf, size_t count) /* {{{ memset(buf, 0, sizeof(php_stream_dirent)); memcpy(((php_stream_dirent *) buf)->d_name, ZSTR_VAL(str_key), to_read); ((php_stream_dirent *) buf)->d_name[to_read + 1] = '\0'; + ((php_stream_dirent *) buf)->d_type = DT_UNKNOWN; return sizeof(php_stream_dirent); } diff --git a/ext/spl/spl_directory.c b/ext/spl/spl_directory.c index 378f707f44c3b..1b29dce787a08 100644 --- a/ext/spl/spl_directory.c +++ b/ext/spl/spl_directory.c @@ -759,8 +759,6 @@ static void spl_filesystem_object_construct(INTERNAL_FUNCTION_PARAMETERS, zend_l } zend_restore_error_handling(&error_handling); - - intern->u.dir.is_recursive = instanceof_function(intern->std.ce, spl_ce_RecursiveDirectoryIterator) ? 1 : 0; } /* }}} */ @@ -1487,6 +1485,11 @@ PHP_METHOD(RecursiveDirectoryIterator, hasChildren) if (spl_filesystem_is_invalid_or_dot(intern->u.dir.entry.d_name)) { RETURN_FALSE; } else { + if (intern->u.dir.entry.d_type == DT_DIR) { + RETURN_TRUE; + } else if (intern->u.dir.entry.d_type == DT_REG) { + RETURN_FALSE; + } if (spl_filesystem_object_get_file_name(intern) == FAILURE) { RETURN_THROWS(); } diff --git a/ext/spl/spl_directory.h b/ext/spl/spl_directory.h index f94ac670d4fa0..d036013161cb1 100644 --- a/ext/spl/spl_directory.h +++ b/ext/spl/spl_directory.h @@ -64,7 +64,6 @@ struct _spl_filesystem_object { php_stream *dirp; zend_string *sub_path; int index; - int is_recursive; zend_function *func_rewind; zend_function *func_next; zend_function *func_valid; diff --git a/ext/standard/ftp_fopen_wrapper.c b/ext/standard/ftp_fopen_wrapper.c index c3b107be65527..1757921d3d47f 100644 --- a/ext/standard/ftp_fopen_wrapper.c +++ b/ext/standard/ftp_fopen_wrapper.c @@ -629,6 +629,7 @@ static ssize_t php_ftp_dirstream_read(php_stream *stream, char *buf, size_t coun memcpy(ent->d_name, ZSTR_VAL(basename), tmp_len); ent->d_name[tmp_len - 1] = '\0'; zend_string_release_ex(basename, 0); + ent->d_type = DT_UNKNOWN; /* Trim off trailing whitespace characters */ while (tmp_len > 0 && diff --git a/main/php_streams.h b/main/php_streams.h index 5acb94f3042d3..db7cc3d236995 100644 --- a/main/php_streams.h +++ b/main/php_streams.h @@ -107,7 +107,12 @@ typedef struct _php_stream_statbuf { } php_stream_statbuf; typedef struct _php_stream_dirent { +#ifdef NAME_MAX + char d_name[NAME_MAX + 1]; +#else char d_name[MAXPATHLEN]; +#endif + unsigned char d_type; } php_stream_dirent; /* operations on streams that are file-handles */ diff --git a/main/streams/glob_wrapper.c b/main/streams/glob_wrapper.c index 813e0bacdfd3f..dc18964d7a8ff 100644 --- a/main/streams/glob_wrapper.c +++ b/main/streams/glob_wrapper.c @@ -150,6 +150,7 @@ static ssize_t php_glob_stream_read(php_stream *stream, char *buf, size_t count) php_glob_stream_path_split(pglob, pglob->glob.gl_pathv[index], pglob->flags & GLOB_APPEND, &path); ++pglob->index; PHP_STRLCPY(ent->d_name, path, sizeof(ent->d_name), strlen(path)); + ent->d_type = DT_UNKNOWN; return sizeof(php_stream_dirent); } pglob->index = glob_result_count; diff --git a/main/streams/plain_wrapper.c b/main/streams/plain_wrapper.c index 128c3410aa835..86c517132482e 100644 --- a/main/streams/plain_wrapper.c +++ b/main/streams/plain_wrapper.c @@ -1032,6 +1032,11 @@ static ssize_t php_plain_files_dirstream_read(php_stream *stream, char *buf, siz result = readdir(dir); if (result) { PHP_STRLCPY(ent->d_name, result->d_name, sizeof(ent->d_name), strlen(result->d_name)); +#ifdef _DIRENT_HAVE_D_TYPE + ent->d_type = result->d_type; +#else + ent->d_type = DT_UNKNOWN; +#endif return sizeof(php_stream_dirent); } return 0; diff --git a/main/streams/userspace.c b/main/streams/userspace.c index 165bd7da3ad98..c7e57ab3ba078 100644 --- a/main/streams/userspace.c +++ b/main/streams/userspace.c @@ -1320,6 +1320,7 @@ static ssize_t php_userstreamop_readdir(php_stream *stream, char *buf, size_t co if (call_result == SUCCESS && Z_TYPE(retval) != IS_FALSE && Z_TYPE(retval) != IS_TRUE) { convert_to_string(&retval); PHP_STRLCPY(ent->d_name, Z_STRVAL(retval), sizeof(ent->d_name), Z_STRLEN(retval)); + ent->d_type = DT_UNKNOWN; didread = sizeof(php_stream_dirent); } else if (call_result == FAILURE) { diff --git a/win32/readdir.c b/win32/readdir.c index 2c3344381913c..89381e5834b62 100644 --- a/win32/readdir.c +++ b/win32/readdir.c @@ -120,6 +120,13 @@ struct dirent *readdir(DIR *dp) dp->dent.d_ino = 1; dp->dent.d_off = dp->offset; + if (dp->fileinfo.dwFileAttributes & (FILE_ATTRIBUTE_REPARSE_POINT | FILE_ATTRIBUTE_DEVICE)) { + dp->dent.d_type = DT_UNKNOWN; /* conservative */ + } else if (dp->fileinfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { + dp->dent.d_type = DT_DIR; + } else { + dp->dent.d_type = DT_REG; + } return &(dp->dent); }/*}}}*/ diff --git a/win32/readdir.h b/win32/readdir.h index cc8e1a9a2510c..4d5a7ce476f0e 100644 --- a/win32/readdir.h +++ b/win32/readdir.h @@ -15,11 +15,17 @@ extern "C" { #include "ioutil.h" +#define _DIRENT_HAVE_D_TYPE +#define DT_UNKNOWN 0 +#define DT_DIR 4 +#define DT_REG 8 + /* struct dirent - same as Unix */ struct dirent { long d_ino; /* inode (always 1 in WIN32) */ off_t d_off; /* offset to this dirent */ unsigned short d_reclen; /* length of d_name */ + unsigned char d_type; char d_name[1]; /* null terminated filename in the current encoding, glyph number <= 255 wchar_t's + \0 byte */ };