diff --git a/NEWS b/NEWS index 1ec90015ef21a..930b5f0034924 100644 --- a/NEWS +++ b/NEWS @@ -34,6 +34,8 @@ PHP NEWS . Support reference values in Phar::mungServer(). (ndossche) . Invalid values now throw in Phar::mungServer() instead of being silently ignored. (ndossche) + . Support overridden methods in SplFileInfo for getMTime() and getPathname() + when building a phar. (ndossche) - Reflection: . Fixed bug GH-20217 (ReflectionClass::isIterable() incorrectly returns true diff --git a/UPGRADING b/UPGRADING index 7f0fcaf8943a8..4e511d094797e 100644 --- a/UPGRADING +++ b/UPGRADING @@ -37,6 +37,11 @@ PHP 8.6 UPGRADE NOTES IntlNumberRangeFormatter::IDENTITY_FALLBACK_RANGE identity fallbacks. It is supported from icu 63. +- Phar: + . Overriding the getMTime() and getPathname() methods of SplFileInfo now + influences the result of the phar buildFrom family of functions. + This makes it possible to override the timestamp and names of files. + - Streams: . Added stream socket context option so_reuseaddr that allows disabling address reuse (SO_REUSEADDR) and explicitly uses SO_EXCLUSIVEADDRUSE on diff --git a/ext/phar/phar_internal.h b/ext/phar/phar_internal.h index 5fc4354545318..144a3c1359f90 100644 --- a/ext/phar/phar_internal.h +++ b/ext/phar/phar_internal.h @@ -196,6 +196,7 @@ typedef struct _phar_metadata_tracker { typedef struct _phar_entry_info { /* first bytes are exactly as in file */ uint32_t uncompressed_filesize; + /* modification time */ uint32_t timestamp; uint32_t compressed_filesize; uint32_t crc32; @@ -464,7 +465,7 @@ void phar_entry_delref(phar_entry_data *idata); phar_entry_info *phar_get_entry_info(phar_archive_data *phar, char *path, size_t path_len, char **error, bool security); phar_entry_info *phar_get_entry_info_dir(phar_archive_data *phar, char *path, size_t path_len, char dir, char **error, bool security); -ZEND_ATTRIBUTE_NONNULL phar_entry_data *phar_get_or_create_entry_data(char *fname, size_t fname_len, char *path, size_t path_len, const char *mode, char allow_dir, char **error, bool security); +ZEND_ATTRIBUTE_NONNULL phar_entry_data *phar_get_or_create_entry_data(char *fname, size_t fname_len, char *path, size_t path_len, const char *mode, char allow_dir, char **error, bool security, uint32_t timestamp); ZEND_ATTRIBUTE_NONNULL zend_result phar_get_entry_data(phar_entry_data **ret, char *fname, size_t fname_len, char *path, size_t path_len, const char *mode, char allow_dir, char **error, bool security); 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); diff --git a/ext/phar/phar_object.c b/ext/phar/phar_object.c index 11a0dd17a4e57..cd00880a0bf01 100644 --- a/ext/phar/phar_object.c +++ b/ext/phar/phar_object.c @@ -1341,6 +1341,44 @@ struct _phar_t { int count; }; +static zend_always_inline void phar_call_method_with_unwrap(zend_object *obj, const char *name, zval *rv) +{ + zend_call_method_with_0_params(obj, obj->ce, NULL, name, rv); + if (Z_ISREF_P(rv)) { + zend_unwrap_reference(rv); + } +} + +/* This is the same as phar_get_or_create_entry_data(), but allows overriding metadata via SplFileInfo. */ +static phar_entry_data *phar_build_entry_data(char *fname, size_t fname_len, char *path, size_t path_len, char **error, zval *file_info) +{ + uint32_t timestamp; + + /* Expects an instance of SplFileInfo if it is an object, which is verified in phar_build(). */ + if (Z_TYPE_P(file_info) == IS_OBJECT && Z_OBJCE_P(file_info)->type == ZEND_USER_CLASS) { + zval rv; + phar_call_method_with_unwrap(Z_OBJ_P(file_info), "getMTime", &rv); + + if (UNEXPECTED(Z_TYPE(rv) != IS_LONG)) { + /* Either it's a tentative type failure, an exception happened, or the function returned false to indicate failure. */ + *error = estrdup("getMTime() must return an int"); + return NULL; + } + + /* Sanity check bounds. See GH-14141. */ + if (ZEND_LONG_UINT_OVFL(Z_LVAL(rv))) { + *error = estrdup("timestamp is limited to 32-bit"); + return NULL; + } + + timestamp = Z_LVAL(rv); + } else { + timestamp = time(NULL); + } + + return phar_get_or_create_entry_data(fname, fname_len, path, path_len, "w+b", 0, error, true, timestamp); +} + static int phar_build(zend_object_iterator *iter, void *puser) /* {{{ */ { zval *value; @@ -1351,7 +1389,7 @@ static int phar_build(zend_object_iterator *iter, void *puser) /* {{{ */ php_stream *fp; size_t fname_len; size_t contents_len; - char *fname, *error = NULL, *base = ZSTR_VAL(p_obj->base), *save = NULL, *temp = NULL; + char *fname = NULL, *error = NULL, *base = ZSTR_VAL(p_obj->base), *save = NULL, *temp = NULL; zend_string *opened; char *str_key; zend_class_entry *ce = p_obj->c; @@ -1411,7 +1449,6 @@ static int phar_build(zend_object_iterator *iter, void *puser) /* {{{ */ goto after_open_fp; case IS_OBJECT: if (instanceof_function(Z_OBJCE_P(value), spl_ce_SplFileInfo)) { - char *test = NULL; spl_filesystem_object *intern = PHAR_FETCH_INTERNAL_EX(value); if (!base_len) { @@ -1419,43 +1456,59 @@ static int phar_build(zend_object_iterator *iter, void *puser) /* {{{ */ return ZEND_HASH_APPLY_STOP; } - switch (intern->type) { - case SPL_FS_DIR: { - zend_string *test_str = spl_filesystem_object_get_path(intern); - fname_len = spprintf(&fname, 0, "%s%c%s", ZSTR_VAL(test_str), DEFAULT_SLASH, intern->u.dir.entry.d_name); - zend_string_release_ex(test_str, /* persistent */ false); - if (php_stream_stat_path(fname, &ssb) == 0 && S_ISDIR(ssb.sb.st_mode)) { - /* ignore directories */ - efree(fname); - return ZEND_HASH_APPLY_KEEP; - } + zend_string *tmp_dir_str = NULL; - test = expand_filepath(fname, NULL); - efree(fname); + /* Take into account that SplFileObject may be overridden. + * The purpose here is to grab the path name of the file to add. */ + if (Z_OBJCE_P(value)->type == ZEND_USER_CLASS) { + zval rv; + phar_call_method_with_unwrap(Z_OBJ_P(value), "getPathname", &rv); - if (test) { - fname = test; - fname_len = strlen(fname); - } else { - zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0, "Could not resolve file path"); - return ZEND_HASH_APPLY_STOP; + if (UNEXPECTED(Z_TYPE(rv) != IS_STRING)) { + zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0, "getPathname() must return a string"); + return ZEND_HASH_APPLY_STOP; + } + tmp_dir_str = Z_STR(rv); + } else { + /* Not a user class, so we can grab the data internally quickly. */ + switch (intern->type) { + case SPL_FS_DIR: { + zend_string *test_str = spl_filesystem_object_get_path(intern); + const char slash = DEFAULT_SLASH; + tmp_dir_str = zend_string_concat3( + ZSTR_VAL(test_str), ZSTR_LEN(test_str), + &slash, 1, + intern->u.dir.entry.d_name, strlen(intern->u.dir.entry.d_name) + ); + zend_string_release_ex(test_str, /* persistent */ false); + break; } + case SPL_FS_INFO: + case SPL_FS_FILE: + fname = expand_filepath(ZSTR_VAL(intern->file_name), NULL); + break; + } + } - save = fname; - goto phar_spl_fileinfo; + if (tmp_dir_str) { + if (php_stream_stat_path(ZSTR_VAL(tmp_dir_str), &ssb) == 0 && S_ISDIR(ssb.sb.st_mode)) { + /* ignore directories */ + zend_string_release_ex(tmp_dir_str, /* persistent */ false); + return ZEND_HASH_APPLY_KEEP; } - case SPL_FS_INFO: - case SPL_FS_FILE: - fname = expand_filepath(ZSTR_VAL(intern->file_name), NULL); - if (!fname) { - zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0, "Could not resolve file path"); - return ZEND_HASH_APPLY_STOP; - } - fname_len = strlen(fname); - save = fname; - goto phar_spl_fileinfo; + fname = expand_filepath(ZSTR_VAL(tmp_dir_str), NULL); + zend_string_release_ex(tmp_dir_str, /* persistent */ false); } + + if (!fname) { + zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0, "Could not resolve file path"); + return ZEND_HASH_APPLY_STOP; + } + + fname_len = strlen(fname); + save = fname; + goto phar_spl_fileinfo; } ZEND_FALLTHROUGH; default: @@ -1586,7 +1639,7 @@ static int phar_build(zend_object_iterator *iter, void *puser) /* {{{ */ return ZEND_HASH_APPLY_KEEP; } - if (!(data = phar_get_or_create_entry_data(phar_obj->archive->fname, phar_obj->archive->fname_len, str_key, str_key_len, "w+b", 0, &error, true))) { + if (!(data = phar_build_entry_data(phar_obj->archive->fname, phar_obj->archive->fname_len, str_key, str_key_len, &error, value))) { zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "Entry %s cannot be created: %s", str_key, error); efree(error); @@ -3544,7 +3597,7 @@ static void phar_add_file(phar_archive_data **pphar, zend_string *file_name, con } #endif - if (!(data = phar_get_or_create_entry_data((*pphar)->fname, (*pphar)->fname_len, filename, filename_len, "w+b", 0, &error, true))) { + if (!(data = phar_get_or_create_entry_data((*pphar)->fname, (*pphar)->fname_len, filename, filename_len, "w+b", 0, &error, true, time(NULL)))) { if (error) { zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "Entry %s does not exist and cannot be created: %s", filename, error); efree(error); @@ -3616,7 +3669,7 @@ static void phar_mkdir(phar_archive_data **pphar, zend_string *dir_name) char *error; phar_entry_data *data; - if (!(data = phar_get_or_create_entry_data((*pphar)->fname, (*pphar)->fname_len, ZSTR_VAL(dir_name), ZSTR_LEN(dir_name), "w+b", 2, &error, true))) { + if (!(data = phar_get_or_create_entry_data((*pphar)->fname, (*pphar)->fname_len, ZSTR_VAL(dir_name), ZSTR_LEN(dir_name), "w+b", 2, &error, true, time(NULL)))) { if (error) { zend_throw_exception_ex(spl_ce_BadMethodCallException, 0, "Directory %s does not exist and cannot be created: %s", ZSTR_VAL(dir_name), error); efree(error); diff --git a/ext/phar/stream.c b/ext/phar/stream.c index bb60f00d8f162..08da1847cd9ce 100644 --- a/ext/phar/stream.c +++ b/ext/phar/stream.c @@ -191,7 +191,7 @@ static php_stream * phar_wrapper_open_url(php_stream_wrapper *wrapper, const cha /* strip leading "/" */ internal_file = estrndup(ZSTR_VAL(resource->path) + 1, ZSTR_LEN(resource->path) - 1); if (mode[0] == 'w' || (mode[0] == 'r' && mode[1] == '+')) { - if (NULL == (idata = phar_get_or_create_entry_data(ZSTR_VAL(resource->host), ZSTR_LEN(resource->host), internal_file, strlen(internal_file), mode, 0, &error, true))) { + if (NULL == (idata = phar_get_or_create_entry_data(ZSTR_VAL(resource->host), ZSTR_LEN(resource->host), internal_file, strlen(internal_file), mode, 0, &error, true, time(NULL)))) { if (error) { php_stream_wrapper_log_error(wrapper, options, "%s", error); efree(error); diff --git a/ext/phar/tests/buildFromIterator_user_overrides/getMTime.phpt b/ext/phar/tests/buildFromIterator_user_overrides/getMTime.phpt new file mode 100644 index 0000000000000..eefe352a0cbb6 --- /dev/null +++ b/ext/phar/tests/buildFromIterator_user_overrides/getMTime.phpt @@ -0,0 +1,81 @@ +--TEST-- +buildFromIterator with user overrides - getMTime() +--EXTENSIONS-- +phar +--INI-- +phar.readonly=0 +phar.require_hash=0 +--CREDITS-- +Arne Blankerts +N. Dossche +--FILE-- +getPathname() . " ]\n"; + return new MySplFileInfo(parent::current()->getPathname()); + } +} + +$workdir = __DIR__.'/getMTime'; +mkdir($workdir . '/content', recursive: true); +file_put_contents($workdir . '/content/hello.txt', "Hello world."); + +$phar = new \Phar($workdir . '/test.phar'); +$phar->startBuffering(); +$phar->buildFromIterator( + new RecursiveIteratorIterator( + new MyIterator($workdir . '/content', FilesystemIterator::SKIP_DOTS) + ), + $workdir +); +$phar->stopBuffering(); + + +$result = new \Phar($workdir . '/test.phar', 0, 'test.phar'); +var_dump($result['content/hello.txt']); +var_dump($result['content/hello.txt']->getATime()); +var_dump($result['content/hello.txt']->getMTime()); +var_dump($result['content/hello.txt']->getCTime()); + +?> +--CLEAN-- + +--EXPECTF-- +[ Found: %shello.txt ] +[MTime] +object(PharFileInfo)#%d (2) { + ["pathName":"SplFileInfo":private]=> + string(%d) "phar://%shello.txt" + ["fileName":"SplFileInfo":private]=> + string(%d) "hello.txt" +} +int(123) +int(123) +int(123) diff --git a/ext/phar/tests/buildFromIterator_user_overrides/getMTime_byRef.phpt b/ext/phar/tests/buildFromIterator_user_overrides/getMTime_byRef.phpt new file mode 100644 index 0000000000000..a7f81709a34e3 --- /dev/null +++ b/ext/phar/tests/buildFromIterator_user_overrides/getMTime_byRef.phpt @@ -0,0 +1,70 @@ +--TEST-- +buildFromIterator with user overrides - getMTime() by ref +--EXTENSIONS-- +phar +--INI-- +phar.readonly=0 +phar.require_hash=0 +--CREDITS-- +Arne Blankerts +N. Dossche +--FILE-- +getPathname() . " ]\n"; + return new MySplFileInfo(parent::current()->getPathname()); + } +} + +$workdir = __DIR__.'/getMTime_byRef'; +mkdir($workdir . '/content', recursive: true); +file_put_contents($workdir . '/content/hello.txt', "Hello world."); + +$phar = new \Phar($workdir . '/test.phar'); +$phar->startBuffering(); +$phar->buildFromIterator( + new RecursiveIteratorIterator( + new MyIterator($workdir . '/content', FilesystemIterator::SKIP_DOTS) + ), + $workdir +); +$phar->stopBuffering(); + + +$result = new \Phar($workdir . '/test.phar', 0, 'test.phar'); +var_dump($result['content/hello.txt']); +var_dump($result['content/hello.txt']->getATime()); +var_dump($result['content/hello.txt']->getMTime()); +var_dump($result['content/hello.txt']->getCTime()); + +?> +--CLEAN-- + +--EXPECTF-- +[ Found: %shello.txt ] +[MTime] +object(PharFileInfo)#%d (2) { + ["pathName":"SplFileInfo":private]=> + string(%d) "phar://%shello.txt" + ["fileName":"SplFileInfo":private]=> + string(%d) "hello.txt" +} +int(123) +int(123) +int(123) diff --git a/ext/phar/tests/buildFromIterator_user_overrides/getMTime_errors.phpt b/ext/phar/tests/buildFromIterator_user_overrides/getMTime_errors.phpt new file mode 100644 index 0000000000000..f43a7a496a762 --- /dev/null +++ b/ext/phar/tests/buildFromIterator_user_overrides/getMTime_errors.phpt @@ -0,0 +1,117 @@ +--TEST-- +buildFromIterator with user overrides - errors in getMTime() +--EXTENSIONS-- +phar +--SKIPIF-- + +--INI-- +phar.readonly=0 +phar.require_hash=0 +--CREDITS-- +Arne Blankerts +N. Dossche +--FILE-- +getPathname() . " ]\n"; + if ($counter === 1) { + return new MySplFileInfo1(parent::current()->getPathname()); + } else if ($counter === 2) { + return new MySplFileInfo2(parent::current()->getPathname()); + } else if ($counter === 3) { + return new MySplFileInfo3(parent::current()->getPathname()); + } else if ($counter === 4) { + return new MySplFileInfo4(parent::current()->getPathname()); + } + } +} + +$workdir = __DIR__.'/getMTime_errors'; +mkdir($workdir . '/content', recursive: true); +file_put_contents($workdir . '/content/hello.txt', "Hello world."); + +for ($i = 0; $i < 4; $i++) { + echo "--- Iteration $i ---\n"; + try { + $phar = new \Phar($workdir . "/test$i.phar"); + $phar->startBuffering(); + $phar->buildFromIterator( + new RecursiveIteratorIterator( + new MyIterator($workdir . '/content', FilesystemIterator::SKIP_DOTS) + ), + $workdir + ); + $phar->stopBuffering(); + } catch (Throwable $e) { + echo $e->getMessage(), "\n"; + if ($previous = $e->getPrevious()) { + echo "Previous: ", $previous->getMessage(), "\n"; + } + } +} + +?> +--CLEAN-- + +--EXPECTF-- +--- Iteration 0 --- +[ Found: %shello.txt ] +[MTime] +Entry content%chello.txt cannot be created: timestamp is limited to 32-bit +--- Iteration 1 --- +[ Found: %shello.txt ] +[MTime] +Entry content%chello.txt cannot be created: getMTime() must return an int +--- Iteration 2 --- +[ Found: %shello.txt ] +[MTime] +Entry content%chello.txt cannot be created: getMTime() must return an int +Previous: Throwing an exception inside getMTime() +--- Iteration 3 --- +[ Found: %shello.txt ] +[MTime] +Entry content%chello.txt cannot be created: getMTime() must return an int diff --git a/ext/phar/tests/buildFromIterator_user_overrides/getPathname.phpt b/ext/phar/tests/buildFromIterator_user_overrides/getPathname.phpt new file mode 100644 index 0000000000000..144eed1d874a6 --- /dev/null +++ b/ext/phar/tests/buildFromIterator_user_overrides/getPathname.phpt @@ -0,0 +1,67 @@ +--TEST-- +buildFromIterator with user overrides - getPathname() +--EXTENSIONS-- +phar +--INI-- +phar.readonly=0 +phar.require_hash=0 +--CREDITS-- +Arne Blankerts +N. Dossche +--FILE-- +getPathname() . " ]\n"; + return new MyGlobIterator(parent::current()->getPath() . '/*'); + } +} + +$workdir = __DIR__.'/getPathname'; +mkdir($workdir . '/content', recursive: true); +file_put_contents($workdir . '/content/hello1.txt', "Hello world 1."); +file_put_contents($workdir . '/content/hello2.txt', "Hello world 2."); + +$phar = new \Phar($workdir . "/test.phar"); +$phar->startBuffering(); +$phar->buildFromIterator( + new RecursiveIteratorIterator( + new MyIterator($workdir . '/content', FilesystemIterator::SKIP_DOTS) + ), + $workdir +); +$phar->stopBuffering(); + +$result = new \Phar($workdir . '/test.phar', 0, 'test.phar'); +var_dump(isset($result['content/hello1.txt'])); +var_dump(isset($result['content/hello2.txt'])); + +?> +--CLEAN-- + +--EXPECTF-- +[ Found: %shello%d.txt ] +[getPathname] +string(%d) "%shello1.txt" +[ Found: %shello%d.txt ] +[getPathname] +string(%d) "%shello1.txt" +bool(false) +bool(true) diff --git a/ext/phar/tests/buildFromIterator_user_overrides/getPathname_byRef.phpt b/ext/phar/tests/buildFromIterator_user_overrides/getPathname_byRef.phpt new file mode 100644 index 0000000000000..0ae2946ce1ade --- /dev/null +++ b/ext/phar/tests/buildFromIterator_user_overrides/getPathname_byRef.phpt @@ -0,0 +1,58 @@ +--TEST-- +buildFromIterator with user overrides - getPathname() by ref +--EXTENSIONS-- +phar +--INI-- +phar.readonly=0 +phar.require_hash=0 +--CREDITS-- +Arne Blankerts +N. Dossche +--FILE-- +getPathname() . " ]\n"; + return new MyGlobIterator(parent::current()->getPath() . '/*'); + } +} + +$workdir = __DIR__.'/getPathname_byRef'; +mkdir($workdir . '/content', recursive: true); +file_put_contents($workdir . '/content/hello.txt', "Hello world 1."); + +$phar = new \Phar($workdir . "/test.phar"); +$phar->startBuffering(); +$phar->buildFromIterator( + new RecursiveIteratorIterator( + new MyIterator($workdir . '/content', FilesystemIterator::SKIP_DOTS) + ), + $workdir +); +$phar->stopBuffering(); + +$result = new \Phar($workdir . '/test.phar', 0, 'test.phar'); +var_dump(isset($result['content/hello.txt'])); + +?> +--CLEAN-- + +--EXPECTF-- +[ Found: %scontent%chello.txt ] +[getPathname] +bool(true) diff --git a/ext/phar/tests/buildFromIterator_user_overrides/getPathname_exception.phpt b/ext/phar/tests/buildFromIterator_user_overrides/getPathname_exception.phpt new file mode 100644 index 0000000000000..86b66050a973f --- /dev/null +++ b/ext/phar/tests/buildFromIterator_user_overrides/getPathname_exception.phpt @@ -0,0 +1,63 @@ +--TEST-- +buildFromIterator with user overrides - exception in getPathname() +--EXTENSIONS-- +phar +--INI-- +phar.readonly=0 +phar.require_hash=0 +--CREDITS-- +Arne Blankerts +N. Dossche +--FILE-- +getPathname() . " ]\n"; + return new MyGlobIterator(parent::current()->getPath() . '/*'); + } +} + +$workdir = __DIR__.'/getPathname_exception'; +mkdir($workdir . '/content', recursive: true); +file_put_contents($workdir . '/content/hello.txt', "Hello world."); + +$phar = new \Phar($workdir . "/test.phar"); +$phar->startBuffering(); +try { + $phar->buildFromIterator( + new RecursiveIteratorIterator( + new MyIterator($workdir . '/content', FilesystemIterator::SKIP_DOTS) + ), + $workdir + ); +} catch (Throwable $e) { + echo $e->getMessage(), "\n"; + if ($previous = $e->getPrevious()) { + echo "Previous: ", $previous->getMessage(), "\n"; + } +} +$phar->stopBuffering(); + +?> +--CLEAN-- + +--EXPECTF-- +[ Found: %shello.txt ] +[getPathname] +string(%d) "%shello.txt" +getPathname() must return a string +Previous: exception in getPathname() diff --git a/ext/phar/tests/buildFromIterator_user_overrides/getPathname_wrong_type.phpt b/ext/phar/tests/buildFromIterator_user_overrides/getPathname_wrong_type.phpt new file mode 100644 index 0000000000000..dfb3fb5f2c550 --- /dev/null +++ b/ext/phar/tests/buildFromIterator_user_overrides/getPathname_wrong_type.phpt @@ -0,0 +1,58 @@ +--TEST-- +buildFromIterator with user overrides - wrong return type in getPathname() +--EXTENSIONS-- +phar +--INI-- +phar.readonly=0 +phar.require_hash=0 +--CREDITS-- +Arne Blankerts +N. Dossche +--FILE-- +getPathname() . " ]\n"; + return new MyGlobIterator(parent::current()->getPath() . '/*'); + } +} + +$workdir = __DIR__.'/getPathname_wrong_type'; +mkdir($workdir . '/content', recursive: true); +file_put_contents($workdir . '/content/hello.txt', "Hello world."); + +$phar = new \Phar($workdir . "/test.phar"); +$phar->startBuffering(); +try { + $phar->buildFromIterator( + new RecursiveIteratorIterator( + new MyIterator($workdir . '/content', FilesystemIterator::SKIP_DOTS) + ), + $workdir + ); +} catch (Throwable $e) { + echo $e->getMessage(), "\n"; +} +$phar->stopBuffering(); + +?> +--CLEAN-- + +--EXPECTF-- +[ Found: %scontent%chello.txt ] +[getPathname] +getPathname() must return a string diff --git a/ext/phar/util.c b/ext/phar/util.c index 60607006f6712..82c784e621712 100644 --- a/ext/phar/util.c +++ b/ext/phar/util.c @@ -606,7 +606,7 @@ ZEND_ATTRIBUTE_NONNULL zend_result phar_get_entry_data(phar_entry_data **ret, ch /** * Create a new dummy file slot within a writeable phar for a newly created file */ -ZEND_ATTRIBUTE_NONNULL phar_entry_data *phar_get_or_create_entry_data(char *fname, size_t fname_len, char *path, size_t path_len, const char *mode, char allow_dir, char **error, bool security) /* {{{ */ +ZEND_ATTRIBUTE_NONNULL phar_entry_data *phar_get_or_create_entry_data(char *fname, size_t fname_len, char *path, size_t path_len, const char *mode, char allow_dir, char **error, bool security, uint32_t timestamp) /* {{{ */ { phar_archive_data *phar; phar_entry_info *entry, etemp; @@ -668,7 +668,7 @@ ZEND_ATTRIBUTE_NONNULL phar_entry_data *phar_get_or_create_entry_data(char *fnam phar_add_virtual_dirs(phar, path, path_len); etemp.is_modified = 1; - etemp.timestamp = time(0); + etemp.timestamp = timestamp; etemp.is_crc_checked = 1; etemp.phar = phar; etemp.filename = zend_string_init(path, path_len, false);