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:
committed by
Niels Dossche
parent
521c0c5cd2
commit
c9008f6dd8
2
NEWS
2
NEWS
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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
|
||||
@@ -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)
|
||||
@@ -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)
|
||||
@@ -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()
|
||||
@@ -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
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user