1
0
mirror of https://github.com/php/php-src.git synced 2026-03-24 00:02:20 +01:00

Make buildFromIterator() work with custom SplFileInfo objects

While it is possible to return a custom SplFileInfo object in the
iterator used by buildFromIterator(), the data is not actually used from
that object, instead the data from the underlying internal structure is
used. This makes it impossible to override some metadata such as the
path name and modification time.

The main motivation comes from two reasons:
- Consistency. We expect our custom methods to be called when having a
  custom object.
- Support reproducibility. This is the original use case as requested in
  [1].

Add support for this by calling the getMTime() and getPathname() methods
if they're overriden by a user class.

[1] https://github.com/theseer/Autoload/issues/114.
This commit is contained in:
Niels Dossche
2024-05-05 20:58:06 +02:00
committed by Niels Dossche
parent 521c0c5cd2
commit c9008f6dd8
13 changed files with 609 additions and 26 deletions

2
NEWS
View File

@@ -39,6 +39,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

View File

@@ -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

View File

@@ -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);

View File

@@ -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;
@@ -1418,26 +1456,49 @@ static int phar_build(zend_object_iterator *iter, void *puser) /* {{{ */
return ZEND_HASH_APPLY_STOP;
}
switch (intern->type) {
case SPL_FS_DIR: {
char *tmp;
zend_string *test_str = spl_filesystem_object_get_path(intern);
fname_len = spprintf(&tmp, 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(tmp, &ssb) == 0 && S_ISDIR(ssb.sb.st_mode)) {
/* ignore directories */
efree(tmp);
return ZEND_HASH_APPLY_KEEP;
}
zend_string *tmp_dir_str = NULL;
fname = expand_filepath(tmp, NULL);
efree(tmp);
break;
/* 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 (UNEXPECTED(Z_TYPE(rv) != IS_STRING)) {
zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0, "getPathname() must return a string");
return ZEND_HASH_APPLY_STOP;
}
case SPL_FS_INFO:
case SPL_FS_FILE:
fname = expand_filepath(ZSTR_VAL(intern->file_name), NULL);
break;
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;
}
}
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;
}
fname = expand_filepath(ZSTR_VAL(tmp_dir_str), NULL);
zend_string_release_ex(tmp_dir_str, /* persistent */ false);
}
if (!fname) {
@@ -1578,7 +1639,7 @@ after_open_fp:
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);
@@ -3536,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);
@@ -3608,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);

View File

@@ -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);

View File

@@ -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--
<?php
class MySplFileInfo extends SplFileInfo {
public function getMTime(): int|false {
echo "[MTime]\n";
return 123;
}
public function getCTime(): int {
// This should not get called
echo "[CTime]\n";
return 0;
}
public function getATime(): int {
// This should not get called
echo "[ATime]\n";
return 0;
}
}
class MyIterator extends RecursiveDirectoryIterator {
public function current(): SplFileInfo {
echo "[ Found: " . parent::current()->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--
<?php
$workdir = __DIR__.'/getMTime';
@unlink($workdir . '/test.phar');
@unlink($workdir . '/content/hello.txt');
@rmdir($workdir . '/content');
@rmdir($workdir);
?>
--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)

View File

@@ -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--
<?php
class MySplFileInfo extends SplFileInfo {
public function &getMTime(): int|false {
static $data = 123;
echo "[MTime]\n";
return $data;
}
}
class MyIterator extends RecursiveDirectoryIterator {
public function current(): SplFileInfo {
echo "[ Found: " . parent::current()->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--
<?php
$workdir = __DIR__.'/getMTime_byRef';
@unlink($workdir . '/test.phar');
@unlink($workdir . '/content/hello.txt');
@rmdir($workdir . '/content');
@rmdir($workdir);
?>
--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)

View File

@@ -0,0 +1,117 @@
--TEST--
buildFromIterator with user overrides - errors in getMTime()
--EXTENSIONS--
phar
--SKIPIF--
<?php
if (PHP_INT_SIZE != 8) die("skip 64-bit only");
?>
--INI--
phar.readonly=0
phar.require_hash=0
--CREDITS--
Arne Blankerts
N. Dossche
--FILE--
<?php
class MySplFileInfo1 extends SplFileInfo {
public function getMTime(): int|false {
echo "[MTime]\n";
return PHP_INT_MAX;
}
}
class MySplFileInfo2 extends SplFileInfo {
public function getMTime(): int|false {
echo "[MTime]\n";
return false;
}
}
class MySplFileInfo3 extends SplFileInfo {
public function getMTime(): int|false {
echo "[MTime]\n";
throw new Error('Throwing an exception inside getMTime()');
}
}
class MySplFileInfo4 extends SplFileInfo {
#[\ReturnTypeWillChange]
public function getMTime(): string {
echo "[MTime]\n";
return "wrong type";
}
}
class MyIterator extends RecursiveDirectoryIterator {
public function current(): SplFileInfo {
static $counter = 0;
$counter++;
echo "[ Found: " . parent::current()->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--
<?php
$workdir = __DIR__.'/getMTime_errors';
@unlink($workdir . '/content/hello.txt');
@unlink($workdir . '/test3.phar');
@unlink($workdir . '/test2.phar');
@unlink($workdir . '/test1.phar');
@unlink($workdir . '/test0.phar');
@rmdir($workdir . '/content');
@rmdir($workdir);
?>
--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

View File

@@ -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--
<?php
class MyGlobIterator extends GlobIterator {
public function getPathname(): string {
echo "[getPathname]\n";
var_dump(parent::getPathname());
// For testing: always return hello2 such that it will be the only file
return str_replace("hello1.txt", "hello2.txt", parent::getPathname());
}
}
class MyIterator extends RecursiveDirectoryIterator {
public function current(): SplFileInfo {
echo "[ Found: " . parent::current()->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--
<?php
$workdir = __DIR__.'/getPathname';
@unlink($workdir . '/test.phar');
@unlink($workdir . '/content/hello1.txt');
@unlink($workdir . '/content/hello2.txt');
@rmdir($workdir . '/content');
@rmdir($workdir);
?>
--EXPECTF--
[ Found: %shello%d.txt ]
[getPathname]
string(%d) "%shello1.txt"
[ Found: %shello%d.txt ]
[getPathname]
string(%d) "%shello1.txt"
bool(false)
bool(true)

View File

@@ -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--
<?php
class MyGlobIterator extends GlobIterator {
public function &getPathname(): string {
echo "[getPathname]\n";
static $data = parent::getPathname();
return $data;
}
}
class MyIterator extends RecursiveDirectoryIterator {
public function current(): SplFileInfo {
echo "[ Found: " . parent::current()->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--
<?php
$workdir = __DIR__.'/getPathname_byRef';
@unlink($workdir . '/test.phar');
@unlink($workdir . '/content/hello.txt');
@rmdir($workdir . '/content');
@rmdir($workdir);
?>
--EXPECTF--
[ Found: %scontent%chello.txt ]
[getPathname]
bool(true)

View File

@@ -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--
<?php
class MyGlobIterator extends GlobIterator {
public function getPathname(): string {
echo "[getPathname]\n";
var_dump(parent::getPathname());
throw new Error('exception in getPathname()');
}
}
class MyIterator extends RecursiveDirectoryIterator {
public function current(): SplFileInfo {
echo "[ Found: " . parent::current()->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--
<?php
$workdir = __DIR__.'/getPathname_exception';
@unlink($workdir . '/content/hello.txt');
@rmdir($workdir . '/content');
@rmdir($workdir);
?>
--EXPECTF--
[ Found: %shello.txt ]
[getPathname]
string(%d) "%shello.txt"
getPathname() must return a string
Previous: exception in getPathname()

View File

@@ -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--
<?php
class MyGlobIterator extends GlobIterator {
#[\ReturnTypeWillChange]
public function getPathname(): int {
echo "[getPathname]\n";
return 1;
}
}
class MyIterator extends RecursiveDirectoryIterator {
public function current(): SplFileInfo {
echo "[ Found: " . parent::current()->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--
<?php
$workdir = __DIR__.'/getPathname_wrong_type';
@unlink($workdir . '/content/hello.txt');
@rmdir($workdir . '/content');
@rmdir($workdir);
?>
--EXPECTF--
[ Found: %scontent%chello.txt ]
[getPathname]
getPathname() must return a string

View File

@@ -606,7 +606,7 @@ really_get_entry:
/**
* 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);