Browse Source

implement Phar->getMetadata() Phar->setMetadata(), add tests, implement meta-data in the header

migration/RELEASE_1_0_0
Greg Beaver 20 years ago
parent
commit
c6dc9be489
  1. 2
      ext/phar/TODO
  2. 2
      ext/phar/package.xml
  3. 65
      ext/phar/phar.c
  4. 3
      ext/phar/phar_internal.h
  5. 47
      ext/phar/phar_object.c
  6. 2
      ext/phar/tests/009.phpt
  7. 2
      ext/phar/tests/010.phpt
  8. 64
      ext/phar/tests/phar_metadata_read.phpt
  9. 69
      ext/phar/tests/phar_metadata_write.phpt
  10. 2
      ext/phar/tests/phar_oo_001.phpt
  11. 4
      ext/phar/tests/phar_test.inc

2
ext/phar/TODO

@ -25,7 +25,7 @@ Version 1.0.0
X add PharFileInfo::getMetaData() [Marcus]
* always throw exceptions from the Phar object, and E_RECOVERABLE_ERROR from
streams interface
* Phar archive metadata Phar::setMetaData($metadata) Phar::getMetaData()
X Phar archive metadata Phar::setMetaData($metadata) Phar::getMetaData() [Greg]
X support rename() in stream wrapper [Greg]
Version 1.1.0

2
ext/phar/package.xml

@ -107,6 +107,8 @@ if the ini variable phar.require_hash is set to true.
<file name="open_for_write_newfile_c.phpt" role="test"/>
<file name="phar_ctx_001.phpt" role="test"/>
<file name="phar_commitwrite.phpt" role="test"/>
<file name="phar_metadata_read.phpt" role="test"/>
<file name="phar_metadata_write.phpt" role="test"/>
<file name="phar_oo_001.phpt" role="test"/>
<file name="phar_oo_002.phpt" role="test"/>
<file name="phar_oo_003.phpt" role="test"/>

65
ext/phar/phar.c

@ -97,6 +97,10 @@ static void phar_destroy_phar_data(phar_archive_data *data TSRMLS_DC) /* {{{ */
if (data->manifest.arBuckets) {
zend_hash_destroy(&data->manifest);
}
if (data->metadata) {
zval_ptr_dtor(&data->metadata);
data->metadata = 0;
}
if (data->fp) {
php_stream_close(data->fp);
data->fp = 0;
@ -802,8 +806,17 @@ int phar_open_file(php_stream *fp, char *fname, int fname_len, char *alias, int
MAPPHAR_FAIL("internal corruption of phar \"%s\" (too many manifest entries for size of manifest)")
}
/* set up our manifest */
mydata = ecalloc(sizeof(phar_archive_data), 1);
if (*(php_uint32 *) buffer) {
if (phar_parse_metadata(fp, &buffer, endbuffer, &mydata->metadata TSRMLS_CC) == FAILURE) {
MAPPHAR_FAIL("unable to read phar metadata in .phar file \"%s\"");
}
} else {
mydata->metadata = 0;
buffer += 4;
}
/* set up our manifest */
zend_hash_init(&mydata->manifest, sizeof(phar_entry_info),
zend_get_hash_value, destroy_phar_manifest, 0);
offset = 0;
@ -838,7 +851,7 @@ int phar_open_file(php_stream *fp, char *fname, int fname_len, char *alias, int
PHAR_GET_32(buffer, entry.flags);
if (*(php_uint32 *) buffer) {
if (phar_parse_metadata(fp, &buffer, endbuffer, &entry.metadata TSRMLS_CC) == FAILURE) {
MAPPHAR_FAIL("unable to read metadata in .phar file \"%s\"");
MAPPHAR_FAIL("unable to read file metadata in .phar file \"%s\"");
}
} else {
buffer += 4;
@ -1855,7 +1868,15 @@ int phar_flush(phar_archive_data *archive, char *user_stub, long len TSRMLS_DC)
* we need to skip entries marked is_deleted. */
zend_hash_apply(&archive->manifest, phar_flush_clean_deleted_apply TSRMLS_CC);
/* compress as necessary, calculate crcs, manifest size, and file sizes */
/* compress as necessary, calculate crcs, serialize meta-data, manifest size, and file sizes */
metadata_str.c = 0;
if (archive->metadata) {
PHP_VAR_SERIALIZE_INIT(metadata_hash);
php_var_serialize(&metadata_str, &archive->metadata, &metadata_hash TSRMLS_CC);
PHP_VAR_SERIALIZE_DESTROY(metadata_hash);
} else {
metadata_str.len = 0;
}
new_manifest_count = 0;
offset = 0;
buf = (char *) emalloc(8192);
@ -1953,14 +1974,17 @@ int phar_flush(phar_archive_data *archive, char *user_stub, long len TSRMLS_DC)
* 4: manifest entry count
* 2: phar version
* 4: phar global flags
* 4: alias length, the rest is the alias itself
* 4: alias length
* ?: the alias itself
* 4: phar metadata length
* ?: phar metadata
*/
restore_alias_len = archive->alias_len;
if (!archive->is_explicit_alias) {
archive->alias_len = 0;
}
manifest_len = offset + archive->alias_len + sizeof(manifest) - 4;
manifest_len = offset + archive->alias_len + sizeof(manifest) + metadata_str.len;
phar_set_32(manifest, manifest_len);
phar_set_32(manifest+4, new_manifest_count);
*(manifest + 8) = (unsigned char) (((PHAR_API_VERSION) >> 8) & 0xFF);
@ -1976,11 +2000,40 @@ int phar_flush(phar_archive_data *archive, char *user_stub, long len TSRMLS_DC)
}
php_stream_close(newfile);
archive->alias_len = restore_alias_len;
php_error_docref(NULL TSRMLS_CC, E_RECOVERABLE_ERROR, "unable to write manifest meta-data of new phar \"%s\"", archive->fname);
php_error_docref(NULL TSRMLS_CC, E_RECOVERABLE_ERROR, "unable to write manifest header of new phar \"%s\"", archive->fname);
return EOF;
}
archive->alias_len = restore_alias_len;
metadata_str.c = 0;
phar_set_32(manifest, metadata_str.len);
if (metadata_str.len) {
if (4 != php_stream_write(newfile, manifest, 4) ||
metadata_str.len != php_stream_write(newfile, metadata_str.c, metadata_str.len)) {
smart_str_free(&metadata_str);
if (closeoldfile) {
php_stream_close(oldfile);
}
php_stream_close(newfile);
archive->alias_len = restore_alias_len;
php_error_docref(NULL TSRMLS_CC, E_RECOVERABLE_ERROR, "unable to write manifest meta-data of new phar \"%s\"", archive->fname);
return EOF;
}
} else {
if (4 != php_stream_write(newfile, manifest, 4)) {
smart_str_free(&metadata_str);
if (closeoldfile) {
php_stream_close(oldfile);
}
php_stream_close(newfile);
archive->alias_len = restore_alias_len;
php_error_docref(NULL TSRMLS_CC, E_RECOVERABLE_ERROR, "unable to write manifest header of new phar \"%s\"", archive->fname);
return EOF;
}
}
smart_str_free(&metadata_str);
/* re-calculate the manifest location to simplify later code */
manifest_ftell = php_stream_tell(newfile);

3
ext/phar/phar_internal.h

@ -59,7 +59,7 @@
#define PHAR_VERSION_STR "1.0.0"
/* x.y.z maps to 0xyz0 */
#define PHAR_API_VERSION 0x1000
#define PHAR_API_MIN_READ 0x0900
#define PHAR_API_MIN_READ 0x1000
#define PHAR_API_MAJORVERSION 0x1000
#define PHAR_API_MAJORVER_MASK 0xF000
#define PHAR_API_VER_MASK 0xFFF0
@ -167,6 +167,7 @@ struct _phar_archive_data {
php_uint32 sig_flags;
int sig_len;
char *signature;
zval *metadata;
int is_explicit_alias:1;
int is_modified:1;
int is_writeable:1;

47
ext/phar/phar_object.c

@ -623,6 +623,41 @@ PHP_METHOD(Phar, getStub)
}
/* }}}*/
/* {{{ proto int Phar::getMetaData()
* Returns the metadata of the phar
*/
PHP_METHOD(Phar, getMetadata)
{
PHAR_ARCHIVE_OBJECT();
if (phar_obj->arc.archive->metadata) {
RETURN_ZVAL(phar_obj->arc.archive->metadata, 1, 0);
}
}
/* }}} */
/* {{{ proto int Phar::setMetaData(mixed $metadata)
* Returns the metadata of the phar
*/
PHP_METHOD(Phar, setMetadata)
{
zval *metadata;
PHAR_ARCHIVE_OBJECT();
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &metadata) == FAILURE) {
return;
}
if (phar_obj->arc.archive->metadata) {
zval_ptr_dtor(&phar_obj->arc.archive->metadata);
phar_obj->arc.archive->metadata = NULL;
}
MAKE_STD_ZVAL(phar_obj->arc.archive->metadata);
ZVAL_ZVAL(phar_obj->arc.archive->metadata, metadata, 1, 0);
}
/* }}} */
/* {{{ proto void PharFileInfo::__construct(string entry)
* Construct a Phar entry object
*/
@ -964,6 +999,11 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_phar_offsetSet, 0, 0, 2)
ZEND_END_ARG_INFO();
#endif
static
ZEND_BEGIN_ARG_INFO_EX(arginfo_entry_setMetadata, 0, 0, 1)
ZEND_ARG_INFO(0, metadata)
ZEND_END_ARG_INFO();
zend_function_entry php_archive_methods[] = {
#if !HAVE_SPL
PHP_ME(Phar, __construct, arginfo_phar___construct, ZEND_ACC_PRIVATE)
@ -984,6 +1024,8 @@ zend_function_entry php_archive_methods[] = {
PHP_ME(Phar, offsetGet, arginfo_phar_offsetExists, ZEND_ACC_PUBLIC)
PHP_ME(Phar, offsetSet, arginfo_phar_offsetSet, ZEND_ACC_PUBLIC)
PHP_ME(Phar, offsetUnset, arginfo_phar_offsetExists, ZEND_ACC_PUBLIC)
PHP_ME(Phar, getMetadata, NULL, 0)
PHP_ME(Phar, setMetadata, arginfo_entry_setMetadata, 0)
#endif
/* static member functions */
PHP_ME(Phar, apiVersion, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC|ZEND_ACC_FINAL)
@ -1001,11 +1043,6 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_entry___construct, 0, 0, 1)
ZEND_ARG_INFO(0, flags)
ZEND_END_ARG_INFO();
static
ZEND_BEGIN_ARG_INFO_EX(arginfo_entry_setMetadata, 0, 0, 1)
ZEND_ARG_INFO(0, metadata)
ZEND_END_ARG_INFO();
zend_function_entry php_entry_methods[] = {
PHP_ME(PharFileInfo, __construct, arginfo_entry___construct, 0)
PHP_ME(PharFileInfo, getCompressedSize, NULL, 0)

2
ext/phar/tests/009.phpt

@ -9,7 +9,7 @@ phar.require_hash=0
$file = "<?php
Phar::mapPhar('hio');
__HALT_COMPILER(); ?>";
$file .= pack('VVnVV', 500, 500, 0x0900, 0x00000000, 0) . str_repeat('A', 500);
$file .= pack('VVnVVV', 500, 500, 0x1000, 0x00000000, 0, 0) . str_repeat('A', 500);
file_put_contents(dirname(__FILE__) . '/' . basename(__FILE__, '.php') . '.phar.php', $file);
include dirname(__FILE__) . '/' . basename(__FILE__, '.php') . '.phar.php';
?>

2
ext/phar/tests/010.phpt

@ -13,7 +13,7 @@ __HALT_COMPILER(); ?>";
// this fails because the manifest length does not include the other 10 byte manifest data
$manifest = pack('V', 1) . 'a' . pack('VVVVVV', 0, time(), 0, crc32(''), 0x00000000, 0);
$file .= pack('VVnVV', strlen($manifest), 1, 0x0900, 0x00000000, 3) . 'hio' . $manifest;
$file .= pack('VVnVV', strlen($manifest), 1, 0x1000, 0x00000000, 3) . 'hio' . pack('V', 0) . $manifest;
file_put_contents(dirname(__FILE__) . '/' . basename(__FILE__, '.php') . '.phar.php', $file);
include dirname(__FILE__) . '/' . basename(__FILE__, '.php') . '.phar.php';

64
ext/phar/tests/phar_metadata_read.phpt

@ -0,0 +1,64 @@
--TEST--
Phar with phar-level meta-data (read)
--SKIPIF--
<?php if (!extension_loaded("phar")) print "skip";?>
--INI--
phar.require_hash=0
--FILE--
<?php
$fname = dirname(__FILE__) . '/' . basename(__FILE__, '.php') . '.phar.php';
$pname = 'phar://' . $fname;
$file = "<?php __HALT_COMPILER(); ?>";
$files = array();
$pmeta = 'hi there';
$files['a'] = array('cont' => 'a');
$files['b'] = array('cont' => 'b');
$files['c'] = array('cont' => 'c', 'meta' => array('hi', 'there'));
$files['d'] = array('cont' => 'd', 'meta' => array('hi'=>'there','foo'=>'bar'));
include 'phar_test.inc';
foreach($files as $name => $cont) {
var_dump(file_get_contents($pname.'/'.$name));
}
$phar = new Phar($fname);
var_dump($phar->getMetaData());
foreach($files as $name => $cont) {
var_dump($phar[$name]->getMetadata());
}
unset($phar);
foreach($files as $name => $cont) {
var_dump(file_get_contents($pname.'/'.$name));
}
?>
===DONE===
--CLEAN--
<?php unlink(dirname(__FILE__) . '/' . basename(__FILE__, '.clean.php') . '.phar.php'); ?>
--EXPECT--
string(1) "a"
string(1) "b"
string(1) "c"
string(1) "d"
string(8) "hi there"
NULL
NULL
array(2) {
[0]=>
string(2) "hi"
[1]=>
string(5) "there"
}
array(2) {
["hi"]=>
string(5) "there"
["foo"]=>
string(3) "bar"
}
string(1) "a"
string(1) "b"
string(1) "c"
string(1) "d"
===DONE===

69
ext/phar/tests/phar_metadata_write.phpt

@ -0,0 +1,69 @@
--TEST--
Phar with phar meta-data (write)
--SKIPIF--
<?php if (!extension_loaded("phar")) print "skip";?>
--INI--
phar.require_hash=0
--FILE--
<?php
$fname = dirname(__FILE__) . '/' . basename(__FILE__, '.php') . '.phar.php';
$pname = 'phar://' . $fname;
$file = "<?php __HALT_COMPILER(); ?>";
$files = array();
$files['a'] = array('cont' => 'a');
$files['b'] = array('cont' => 'b', 'meta' => 'hi there');
$files['c'] = array('cont' => 'c', 'meta' => array('hi', 'there'));
$files['d'] = array('cont' => 'd', 'meta' => array('hi'=>'there','foo'=>'bar'));
include 'phar_test.inc';
foreach($files as $name => $cont) {
var_dump(file_get_contents($pname.'/'.$name));
}
$phar = new Phar($fname);
var_dump($phar->getMetadata());
$phar->setMetadata(array('my' => 'friend'));
var_dump($phar->getMetadata());
$phar['a']->setMetadata(42);
$phar['b']->setMetadata(NULL);
$phar['c']->setMetadata(array(25, 'foo'=>'bar'));
$phar['d']->setMetadata(true);
foreach($files as $name => $cont) {
var_dump($phar[$name]->getMetadata());
}
unset($phar);
foreach($files as $name => $cont) {
var_dump(file_get_contents($pname.'/'.$name));
}
?>
===DONE===
--CLEAN--
<?php unlink(dirname(__FILE__) . '/' . basename(__FILE__, '.clean.php') . '.phar.php'); ?>
--EXPECT--
string(1) "a"
string(1) "b"
string(1) "c"
string(1) "d"
NULL
array(1) {
["my"]=>
string(6) "friend"
}
int(42)
NULL
array(2) {
[0]=>
int(25)
["foo"]=>
string(3) "bar"
}
bool(true)
string(1) "a"
string(1) "b"
string(1) "c"
string(1) "d"
===DONE===

2
ext/phar/tests/phar_oo_001.phpt

@ -39,7 +39,7 @@ unlink(dirname(__FILE__) . '/phar_oo_test.phar.php');
__halt_compiler();
?>
--EXPECT--
string(5) "0.9.0"
string(5) "1.0.0"
int(5)
string(50) "Cannot call method on an uninitialized Phar object"
===DONE===

4
ext/phar/tests/phar_test.inc

@ -49,7 +49,9 @@ foreach($files as $name => $cont)
}
$alias = 'hio';
$manifest = pack('VnVV', count($files), 0x0900, $glags, strlen($alias)) . $alias . $manifest;
if (isset($pmeta)) $pmeta = serialize($pmeta); else $pmeta = '';
$manifest = pack('VnVV', count($files), 0x1000, $glags, strlen($alias)) . $alias . pack('V', strlen($pmeta)) . $pmeta . $manifest;
$file .= pack('V', strlen($manifest)) . $manifest;
foreach($files as $cont)

Loading…
Cancel
Save