Commit e2a923265d6 for php.net
commit e2a923265d6080db5950ec4f6947c2bb9d0915e3
Author: Gina Peter Banyard <girgias@php.net>
Date: Thu Apr 16 19:58:24 2026 +0100
ext/phar: use zend_string for entry in phar_split_fname() (#21767)
This saves us to do multiple reallocations from zend_string* to char* back to a zend_string*.
diff --git a/ext/phar/dirstream.c b/ext/phar/dirstream.c
index 51e5d70a6e6..c486fd41abf 100644
--- a/ext/phar/dirstream.c
+++ b/ext/phar/dirstream.c
@@ -350,7 +350,7 @@ int phar_wrapper_mkdir(php_stream_wrapper *wrapper, const char *url_from, int mo
php_url *resource = NULL;
/* pre-readonly check, we need to know if this is a data phar */
- if (FAILURE == phar_split_fname(url_from, strlen(url_from), &arch, &arch_len, NULL, NULL, 2, 2)) {
+ if (FAILURE == phar_split_fname(url_from, strlen(url_from), &arch, &arch_len, NULL, 2, 2)) {
php_stream_wrapper_log_error(wrapper, options, "phar error: cannot create directory \"%s\", no phar archive specified", url_from);
return 0;
}
@@ -476,7 +476,7 @@ int phar_wrapper_rmdir(php_stream_wrapper *wrapper, const char *url, int options
php_url *resource = NULL;
/* pre-readonly check, we need to know if this is a data phar */
- if (FAILURE == phar_split_fname(url, strlen(url), &arch, &arch_len, NULL, NULL, 2, 2)) {
+ if (FAILURE == phar_split_fname(url, strlen(url), &arch, &arch_len, NULL, 2, 2)) {
php_stream_wrapper_log_error(wrapper, options, "phar error: cannot remove directory \"%s\", no phar archive specified, or phar archive does not exist", url);
return 0;
}
diff --git a/ext/phar/func_interceptors.c b/ext/phar/func_interceptors.c
index a8867ed57cd..e70dcb5e5a1 100644
--- a/ext/phar/func_interceptors.c
+++ b/ext/phar/func_interceptors.c
@@ -48,7 +48,7 @@ PHP_FUNCTION(phar_opendir) /* {{{ */
goto skip_phar;
}
- if (SUCCESS == phar_split_fname(ZSTR_VAL(fname), ZSTR_LEN(fname), &arch, &arch_len, NULL, NULL, 2, 0)) {
+ if (SUCCESS == phar_split_fname(ZSTR_VAL(fname), ZSTR_LEN(fname), &arch, &arch_len, NULL, 2, 0)) {
php_stream_context *context = NULL;
php_stream *stream;
char *name;
@@ -94,7 +94,7 @@ static zend_string* phar_get_name_for_relative_paths(zend_string *filename, bool
return NULL;
}
- if (FAILURE == phar_split_fname(ZSTR_VAL(fname), ZSTR_LEN(fname), &arch, &arch_len, NULL, NULL, 2, 0)) {
+ if (FAILURE == phar_split_fname(ZSTR_VAL(fname), ZSTR_LEN(fname), &arch, &arch_len, NULL, 2, 0)) {
return NULL;
}
@@ -496,7 +496,7 @@ static void phar_file_stat(const char *filename, size_t filename_length, int typ
phar = PHAR_G(last_phar);
goto splitted;
}
- if (SUCCESS == phar_split_fname(ZSTR_VAL(fname), ZSTR_LEN(fname), &arch, &arch_len, NULL, NULL, 2, 0)) {
+ if (SUCCESS == phar_split_fname(ZSTR_VAL(fname), ZSTR_LEN(fname), &arch, &arch_len, NULL, 2, 0)) {
/* fopen within phar, if :// is not in the url, then prepend phar://<archive>/ */
if (FAILURE == phar_get_archive(&phar, arch, arch_len, NULL, 0, NULL)) {
efree(arch);
@@ -737,7 +737,7 @@ PHP_FUNCTION(phar_is_file) /* {{{ */
goto skip_phar;
}
- if (SUCCESS == phar_split_fname(ZSTR_VAL(fname), ZSTR_LEN(fname), &arch, &arch_len, NULL, NULL, 2, 0)) {
+ if (SUCCESS == phar_split_fname(ZSTR_VAL(fname), ZSTR_LEN(fname), &arch, &arch_len, NULL, 2, 0)) {
phar_archive_data *phar;
/* fopen within phar, if :// is not in the url, then prepend phar://<archive>/ */
@@ -795,7 +795,7 @@ PHP_FUNCTION(phar_is_link) /* {{{ */
goto skip_phar;
}
- if (SUCCESS == phar_split_fname(ZSTR_VAL(fname), ZSTR_LEN(fname), &arch, &arch_len, NULL, NULL, 2, 0)) {
+ if (SUCCESS == phar_split_fname(ZSTR_VAL(fname), ZSTR_LEN(fname), &arch, &arch_len, NULL, 2, 0)) {
phar_archive_data *phar;
/* fopen within phar, if :// is not in the url, then prepend phar://<archive>/ */
diff --git a/ext/phar/phar.c b/ext/phar/phar.c
index 02a2f09f952..d597434d5eb 100644
--- a/ext/phar/phar.c
+++ b/ext/phar/phar.c
@@ -2163,7 +2163,7 @@ zend_string* phar_fix_filepath(const char *path, size_t path_length, bool use_cw
*
* This is used by phar_parse_url()
*/
-zend_result phar_split_fname(const char *filename, size_t filename_len, char **arch, size_t *arch_len, char **entry, size_t *entry_len, int executable, int for_create) /* {{{ */
+zend_result phar_split_fname(const char *filename, size_t filename_len, char **arch, size_t *arch_len, zend_string **entry, int executable, int for_create) /* {{{ */
{
const char *ext_str;
#ifdef PHP_WIN32
@@ -2219,13 +2219,9 @@ zend_result phar_split_fname(const char *filename, size_t filename_len, char **a
size_t computed_entry_len = filename_len - *arch_len;
/* We don't need to unixify the path on Windows,
* as ext_str is derived from filename that was already unixify */
- zend_string *entry_str = phar_fix_filepath(ext_str+ext_len, computed_entry_len, false);
- *entry = estrndup(ZSTR_VAL(entry_str), ZSTR_LEN(entry_str));
- *entry_len = ZSTR_LEN(entry_str);
- zend_string_release_ex(entry_str, false);
+ *entry = phar_fix_filepath(ext_str+ext_len, computed_entry_len, false);
} else {
- *entry_len = 1;
- *entry = estrndup("/", 1);
+ *entry = ZSTR_CHAR('/');
}
}
diff --git a/ext/phar/phar_internal.h b/ext/phar/phar_internal.h
index a2b7e67a2e8..c20e5d42b31 100644
--- a/ext/phar/phar_internal.h
+++ b/ext/phar/phar_internal.h
@@ -474,7 +474,7 @@ ZEND_ATTRIBUTE_NONNULL zend_result phar_get_entry_data(phar_entry_data **ret, ch
ZEND_ATTRIBUTE_NONNULL_ARGS(1, 4) int phar_flush_ex(phar_archive_data *archive, zend_string *user_stub, bool is_default_stub, char **error);
ZEND_ATTRIBUTE_NONNULL int phar_flush(phar_archive_data *archive, char **error);
zend_result phar_detect_phar_fname_ext(const char *filename, size_t filename_len, const char **ext_str, size_t *ext_len, int executable, int for_create, bool is_complete);
-zend_result phar_split_fname(const char *filename, size_t filename_len, char **arch, size_t *arch_len, char **entry, size_t *entry_len, int executable, int for_create);
+zend_result phar_split_fname(const char *filename, size_t filename_len, char **arch, size_t *arch_len, zend_string **entry, int executable, int for_create);
typedef enum {
pcr_use_query,
diff --git a/ext/phar/phar_object.c b/ext/phar/phar_object.c
index f522d52ba6c..ef997184d03 100644
--- a/ext/phar/phar_object.c
+++ b/ext/phar/phar_object.c
@@ -419,7 +419,7 @@ PHP_METHOD(Phar, running)
if (
zend_string_starts_with_literal_ci(fname, "phar://")
- && SUCCESS == phar_split_fname(ZSTR_VAL(fname), ZSTR_LEN(fname), &arch, &arch_len, NULL, NULL, 2, 0)
+ && SUCCESS == phar_split_fname(ZSTR_VAL(fname), ZSTR_LEN(fname), &arch, &arch_len, NULL, 2, 0)
) {
if (retphar) {
RETVAL_STRINGL(ZSTR_VAL(fname), arch_len + 7);
@@ -444,8 +444,8 @@ PHP_METHOD(Phar, running)
*/
PHP_METHOD(Phar, mount)
{
- char *fname, *arch = NULL, *entry = NULL, *path, *actual;
- size_t fname_len, arch_len, entry_len;
+ char *fname, *arch = NULL, *path, *actual;
+ size_t fname_len, arch_len;
size_t path_len, actual_len;
phar_archive_data *pphar;
#ifdef PHP_WIN32
@@ -476,9 +476,8 @@ PHP_METHOD(Phar, mount)
}
#endif
- if (fname_len > 7 && !memcmp(fname, "phar://", 7) && SUCCESS == phar_split_fname(fname, fname_len, &arch, &arch_len, NULL, NULL, 2, 0)) {
- entry = NULL;
-
+ zend_string *entry = NULL;
+ if (fname_len > 7 && !memcmp(fname, "phar://", 7) && SUCCESS == phar_split_fname(fname, fname_len, &arch, &arch_len, NULL, 2, 0)) {
if (path_len > 7 && !memcmp(path, "phar://", 7)) {
zend_throw_exception_ex(phar_ce_PharException, 0, "Can only mount internal paths within a phar archive, use a relative path instead of \"%s\"", path);
efree(arch);
@@ -505,8 +504,8 @@ PHP_METHOD(Phar, mount)
zend_throw_exception_ex(phar_ce_PharException, 0, "Mounting of %s to %s within phar %s failed", path, actual, arch);
}
- if (entry && path == entry) {
- efree(entry);
+ if (entry && path == ZSTR_VAL(entry)) {
+ zend_string_release_ex(entry, false);
}
if (arch) {
@@ -522,9 +521,9 @@ PHP_METHOD(Phar, mount)
}
goto carry_on;
- } else if (SUCCESS == phar_split_fname(path, path_len, &arch, &arch_len, &entry, &entry_len, 2, 0)) {
- path = entry;
- path_len = entry_len;
+ } else if (SUCCESS == phar_split_fname(path, path_len, &arch, &arch_len, &entry, 2, 0)) {
+ path = ZSTR_VAL(entry);
+ path_len = ZSTR_LEN(entry);
goto carry_on2;
}
@@ -1078,9 +1077,9 @@ static const spl_other_handler phar_spl_foreign_handler = {
*/
PHP_METHOD(Phar, __construct)
{
- char *fname, *alias = NULL, *error, *arch = NULL, *entry = NULL, *save_fname;
+ char *fname, *alias = NULL, *error, *arch = NULL, *save_fname;
size_t fname_len, alias_len = 0;
- size_t arch_len, entry_len;
+ size_t arch_len;
bool is_data;
zend_long flags = SPL_FILE_DIR_SKIPDOTS|SPL_FILE_DIR_UNIXPATHS;
zend_long format = 0;
@@ -1108,7 +1107,8 @@ PHP_METHOD(Phar, __construct)
}
save_fname = fname;
- if (SUCCESS == phar_split_fname(fname, fname_len, &arch, &arch_len, &entry, &entry_len, !is_data, 2)) {
+ zend_string *entry = NULL;
+ if (SUCCESS == phar_split_fname(fname, fname_len, &arch, &arch_len, &entry, !is_data, 2)) {
/* use arch (the basename for the archive) for fname instead of fname */
/* this allows support for RecursiveDirectoryIterator of subdirectories */
#ifdef PHP_WIN32
@@ -1133,7 +1133,7 @@ PHP_METHOD(Phar, __construct)
}
if (entry) {
- efree(entry);
+ zend_string_release_ex(entry, false);
}
if (error) {
@@ -1166,7 +1166,7 @@ PHP_METHOD(Phar, __construct)
zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0,
"Phar class can only be used for executable tar and zip archives");
}
- efree(entry);
+ zend_string_release_ex(entry, false);
RETURN_THROWS();
}
@@ -1184,9 +1184,9 @@ PHP_METHOD(Phar, __construct)
file_name_for_recursive_director_iterator_constructor = zend_string_concat3(
ZEND_STRL("phar://"),
phar_data->fname, phar_data->fname_len,
- entry, entry_len
+ ZSTR_VAL(entry), ZSTR_LEN(entry)
);
- efree(entry);
+ zend_string_release_ex(entry, false);
} else {
file_name_for_recursive_director_iterator_constructor = zend_string_concat2(
ZEND_STRL("phar://"),
@@ -1289,7 +1289,7 @@ PHP_METHOD(Phar, unlinkArchive)
if (
zend_file_name
&& zend_string_starts_with_literal_ci(zend_file_name, "phar://")
- && SUCCESS == phar_split_fname(ZSTR_VAL(zend_file_name), ZSTR_LEN(zend_file_name), &arch, &arch_len, NULL, NULL, 2, 0)
+ && SUCCESS == phar_split_fname(ZSTR_VAL(zend_file_name), ZSTR_LEN(zend_file_name), &arch, &arch_len, NULL, 2, 0)
) {
if (arch_len == fname_len && !memcmp(arch, fname, arch_len)) {
zend_throw_exception_ex(phar_ce_PharException, 0, "phar archive \"%s\" cannot be unlinked from within itself", fname);
@@ -4392,11 +4392,10 @@ PHP_METHOD(Phar, extractTo)
/* {{{ Construct a Phar entry object */
PHP_METHOD(PharFileInfo, __construct)
{
- char *fname, *arch, *entry, *error;
+ char *fname, *arch, *error;
size_t fname_len;
- size_t arch_len, entry_len;
+ size_t arch_len;
phar_entry_object *entry_obj;
- phar_entry_info *entry_info;
phar_archive_data *phar_data;
zval arg1;
@@ -4411,7 +4410,8 @@ PHP_METHOD(PharFileInfo, __construct)
RETURN_THROWS();
}
- if (fname_len < 7 || memcmp(fname, "phar://", 7) || phar_split_fname(fname, fname_len, &arch, &arch_len, &entry, &entry_len, 2, 0) == FAILURE) {
+ zend_string *entry = NULL;
+ if (fname_len < 7 || memcmp(fname, "phar://", 7) || phar_split_fname(fname, fname_len, &arch, &arch_len, &entry, 2, 0) == FAILURE) {
zend_throw_exception_ex(spl_ce_RuntimeException, 0,
"'%s' is not a valid phar archive URL (must have at least phar://filename.phar)", fname);
RETURN_THROWS();
@@ -4431,16 +4431,17 @@ PHP_METHOD(PharFileInfo, __construct)
RETURN_THROWS();
}
- if ((entry_info = phar_get_entry_info_dir(phar_data, entry, entry_len, 1, &error, true)) == NULL) {
+ phar_entry_info *entry_info = phar_get_entry_info_dir(phar_data, ZSTR_VAL(entry), ZSTR_LEN(entry), 1, &error, true);
+ if (UNEXPECTED(!entry_info)) {
zend_throw_exception_ex(spl_ce_RuntimeException, 0,
- "Cannot access phar file entry '%s' in archive '%s'%s%s", entry, arch, error ? ", " : "", error ? error : "");
+ "Cannot access phar file entry '%s' in archive '%s'%s%s", ZSTR_VAL(entry), arch, error ? ", " : "", error ? error : "");
+ zend_string_release_ex(entry, false);
efree(arch);
- efree(entry);
RETURN_THROWS();
}
+ zend_string_release_ex(entry, false);
efree(arch);
- efree(entry);
entry_obj->entry = entry_info;
if (!entry_info->is_persistent && !entry_info->is_temp_dir) {
diff --git a/ext/phar/stream.c b/ext/phar/stream.c
index 6694fec9479..4b49703b4ab 100644
--- a/ext/phar/stream.c
+++ b/ext/phar/stream.c
@@ -58,8 +58,8 @@ const php_stream_wrapper php_stream_phar_wrapper = {
php_url* phar_parse_url(php_stream_wrapper *wrapper, const char *filename, const char *mode, int options) /* {{{ */
{
php_url *resource;
- char *arch = NULL, *entry = NULL, *error;
- size_t arch_len, entry_len;
+ char *arch = NULL, *error;
+ size_t arch_len;
if (strncasecmp(filename, "phar://", 7)) {
return NULL;
@@ -70,7 +70,8 @@ php_url* phar_parse_url(php_stream_wrapper *wrapper, const char *filename, const
}
return NULL;
}
- if (phar_split_fname(filename, strlen(filename), &arch, &arch_len, &entry, &entry_len, 2, (mode[0] == 'w' ? 2 : 0)) == FAILURE) {
+ zend_string *entry = NULL;
+ if (phar_split_fname(filename, strlen(filename), &arch, &arch_len, &entry, 2, (mode[0] == 'w' ? 2 : 0)) == FAILURE) {
if (!(options & PHP_STREAM_URL_STAT_QUIET)) {
if (arch && !entry) {
php_stream_wrapper_log_error(wrapper, options, "phar error: no directory in \"%s\", must have at least phar://%s/ for root directory (always use full path to a new phar)", filename, arch);
@@ -85,8 +86,7 @@ php_url* phar_parse_url(php_stream_wrapper *wrapper, const char *filename, const
resource->scheme = ZSTR_INIT_LITERAL("phar", 0);
resource->host = zend_string_init(arch, arch_len, 0);
efree(arch);
- resource->path = zend_string_init(entry, entry_len, 0);
- efree(entry);
+ resource->path = entry;
#ifdef MBO_0
if (resource) {
diff --git a/ext/phar/util.c b/ext/phar/util.c
index aad6e5f7f08..1e9fedbcbb2 100644
--- a/ext/phar/util.c
+++ b/ext/phar/util.c
@@ -300,7 +300,7 @@ zend_string *phar_find_in_include_path(zend_string *filename, phar_archive_data
goto splitted;
}
- if (!is_file_a_phar_wrapper || SUCCESS != phar_split_fname(ZSTR_VAL(fname), ZSTR_LEN(fname), &arch, &arch_len, NULL, NULL, 1, 0)) {
+ if (!is_file_a_phar_wrapper || SUCCESS != phar_split_fname(ZSTR_VAL(fname), ZSTR_LEN(fname), &arch, &arch_len, NULL, 1, 0)) {
return NULL;
}
@@ -344,7 +344,7 @@ zend_string *phar_find_in_include_path(zend_string *filename, phar_archive_data
if (ret && zend_string_starts_with_literal_ci(ret, "phar://")) {
/* found phar:// */
- if (SUCCESS != phar_split_fname(ZSTR_VAL(ret), ZSTR_LEN(ret), &arch, &arch_len, NULL, NULL, 1, 0)) {
+ if (SUCCESS != phar_split_fname(ZSTR_VAL(ret), ZSTR_LEN(ret), &arch, &arch_len, NULL, 1, 0)) {
return ret;
}