cellog Wed Jan 9 07:09:03 2008 UTC Added files: /pecl/phar/tests/tar tar_gzip.phpt
Modified files: /pecl/phar TODO package.php phar.c phar_internal.h phar_object.c tar.c Log: implement whole-file compression of phars for phar/tar-based phars still not 100% working, add failing test add Phar::isCompressed(), which returns either 0, Phar::GZ, or Phar::BZ2 [DOC]
http://cvs.php.net/viewvc.cgi/pecl/phar/TODO?r1=1.49&r2=1.50&diff_format=u Index: pecl/phar/TODO diff -u pecl/phar/TODO:1.49 pecl/phar/TODO:1.50 --- pecl/phar/TODO:1.49 Wed Jan 9 03:53:41 2008 +++ pecl/phar/TODO Wed Jan 9 07:09:03 2008 @@ -68,6 +68,5 @@ X Phar::copy($from, $to); [Greg] X Phar::delete($what) [Greg] X Phar::buildFromIterator(Iterator $it[, string $base_directory]) [Greg] - * Layout: Option to compress all content rather than single files. - That excludes stub and manifest haeder. (tar only) + X Layout: Option to compress all content rather than single files. (tar/phar only) [Greg] X clean crap paths like phar://blah.phar/file//../to\\here.php [Greg] http://cvs.php.net/viewvc.cgi/pecl/phar/package.php?r1=1.29&r2=1.30&diff_format=u Index: pecl/phar/package.php diff -u pecl/phar/package.php:1.29 pecl/phar/package.php:1.30 --- pecl/phar/package.php:1.29 Wed Jan 9 03:53:41 2008 +++ pecl/phar/package.php Wed Jan 9 07:09:03 2008 @@ -9,6 +9,7 @@ * include/fopen with include_path all work unmodified within a phar [Greg] * paths with . and .. work (phar://blah.phar/a/../b.php => phar://blah.phar/b.php) [Greg] * add support for mkdir()/rmdir() and support for empty directories to phar file format [Greg] + * add option to compress the entire phar file for phar/tar file format [Greg] * implement Phar::copy(string $from, string $to) [Greg] * implement Phar::buildFromIterator(Iterator $it[, string $base_directory]) [Greg] * add mapping of include/require from within a phar to location within phar [Greg] http://cvs.php.net/viewvc.cgi/pecl/phar/phar.c?r1=1.258&r2=1.259&diff_format=u Index: pecl/phar/phar.c diff -u pecl/phar/phar.c:1.258 pecl/phar/phar.c:1.259 --- pecl/phar/phar.c:1.258 Wed Jan 9 03:47:21 2008 +++ pecl/phar/phar.c Wed Jan 9 07:09:03 2008 @@ -17,7 +17,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: phar.c,v 1.258 2008/01/09 03:47:21 cellog Exp $ */ +/* $Id: phar.c,v 1.259 2008/01/09 07:09:03 cellog Exp $ */ #define PHAR_MAIN 1 #include "phar_internal.h" @@ -979,7 +979,7 @@ * This is used by phar_open_filename to process the manifest, but can be called * directly. */ -int phar_open_file(php_stream *fp, char *fname, int fname_len, char *alias, int alias_len, long halt_offset, phar_archive_data** pphar, char **error TSRMLS_DC) /* {{{ */ +int phar_open_file(php_stream *fp, char *fname, int fname_len, char *alias, int alias_len, long halt_offset, phar_archive_data** pphar, php_uint32 compression, char **error TSRMLS_DC) /* {{{ */ { char b32[4], *buffer, *endbuffer, *savebuf; phar_archive_data *mydata = NULL; @@ -1066,7 +1066,11 @@ PHAR_GET_32(buffer, manifest_flags); - manifest_flags &= ~PHAR_HDR_COMPRESSION_MASK; + manifest_flags &= ~PHAR_HDR_COMPRESSION_MASK; + + manifest_flags &= ~PHAR_FILE_COMPRESSION_MASK; + /* remember whether this entire phar was compressed with gz/bzip2 */ + manifest_flags |= compression; /* The lowest nibble contains the phar wide flags. The compression flags can */ /* be ignored on reading because it is being generated anyways. */ @@ -1624,14 +1628,14 @@ { const char token[] = "__HALT_COMPILER();"; const char zip_magic[] = "PK\x03\x04"; + const char gz_magic[] = "\x1f\x8b\x08"; + const char bz_magic[] = "BZh"; char *pos, buffer[1024 + sizeof(token)], test = '\0'; const long readsize = sizeof(buffer) - sizeof(token); const long tokenlen = sizeof(token) - 1; long halt_offset; size_t got; - - /* Maybe it's better to compile the file instead of just searching, */ - /* but we only want the offset. So we want a .re scanner to find it. */ + php_uint32 compression = PHAR_FILE_COMPRESSED_NONE; if (error) { *error = NULL; @@ -1643,6 +1647,9 @@ buffer[sizeof(buffer)-1] = '\0'; memset(buffer, 32, sizeof(token)); halt_offset = 0; + + /* Maybe it's better to compile the file instead of just searching, */ + /* but we only want the offset. So we want a .re scanner to find it. */ while(!php_stream_eof(fp)) { if ((got = php_stream_read(fp, buffer+tokenlen, readsize)) < (size_t) tokenlen) { MAPPHAR_ALLOC_FAIL("internal corruption of phar \"%s\" (truncated entry)") @@ -1650,19 +1657,85 @@ if (!test) { test = '\1'; pos = buffer+tokenlen; + if (!memcmp(pos, gz_magic, 3)) { + char err = 0; + php_stream_filter *filter; + php_stream *temp; + /* to properly decompress, we have to tell zlib to look for a zlib or gzip header */ + zval filterparams; + + array_init(&filterparams); + add_assoc_long(&filterparams, "window", MAX_WBITS + 32); + /* entire file is gzip-compressed, uncompress to temporary file */ + if (!(temp = php_stream_fopen_tmpfile())) { + MAPPHAR_ALLOC_FAIL("unable to create temporary file for decompression of gzipped phar archive \"%s\"") + } + php_stream_rewind(fp); + filter = php_stream_filter_create("zlib.inflate", &filterparams, php_stream_is_persistent(fp) TSRMLS_CC); + if (!filter) { + err = 1; + add_assoc_long(&filterparams, "window", MAX_WBITS); + filter = php_stream_filter_create("zlib.inflate", &filterparams, php_stream_is_persistent(fp) TSRMLS_CC); + } + zval_dtor(&filterparams); + php_stream_filter_append(&temp->writefilters, filter); + if (0 == php_stream_copy_to_stream(fp, temp, PHP_STREAM_COPY_ALL)) { + if (err) { + MAPPHAR_ALLOC_FAIL("unable to decompress gzipped phar archive \"%s\", ext/zlib is buggy in PHP versions older than 5.2.6") + } + php_stream_close(temp); + MAPPHAR_ALLOC_FAIL("unable to decompress gzipped phar archive \"%s\" to temporary file") + } + php_stream_filter_flush(filter, 1); + php_stream_filter_remove(filter, 1 TSRMLS_CC); + php_stream_close(fp); + fp = temp; + php_stream_rewind(fp); + compression = PHAR_FILE_COMPRESSED_GZ; + + /* now, start over */ + test = '\0'; + continue; + } else if (!memcmp(pos, bz_magic, 3)) { + php_stream_filter *filter; + php_stream *temp; + + /* entire file is bzip-compressed, uncompress to temporary file */ + if (!(temp = php_stream_fopen_tmpfile())) { + MAPPHAR_ALLOC_FAIL("unable to create temporary file for decompression of bzipped phar archive \"%s\"") + } + php_stream_rewind(fp); + filter = php_stream_filter_create("bzip2.decompress", NULL, php_stream_is_persistent(fp) TSRMLS_CC); + php_stream_filter_append(&temp->writefilters, filter); + if (0 == php_stream_copy_to_stream(fp, temp, PHP_STREAM_COPY_ALL)) { + php_stream_close(temp); + MAPPHAR_ALLOC_FAIL("unable to decompress gzipped phar archive \"%s\" to temporary file") + } + php_stream_filter_flush(filter, 1); + php_stream_filter_remove(filter, 1 TSRMLS_CC); + php_stream_close(fp); + fp = temp; + php_stream_rewind(fp); + compression = PHAR_FILE_COMPRESSED_BZ2; + + /* now, start over */ + test = '\0'; + continue; + } if (!memcmp(pos, zip_magic, 4)) { php_stream_close(fp); return phar_open_zipfile(fname, fname_len, alias, alias_len, pphar, error TSRMLS_CC); } if (got > 512) { if (phar_is_tar(pos)) { - return phar_open_tarfile(fp, fname, fname_len, alias, alias_len, options, pphar, error TSRMLS_CC); + php_stream_rewind(fp); + return phar_open_tarfile(fp, fname, fname_len, alias, alias_len, options, pphar, compression, error TSRMLS_CC); } } } if ((pos = strstr(buffer, token)) != NULL) { halt_offset += (pos - buffer); /* no -tokenlen+tokenlen here */ - return phar_open_file(fp, fname, fname_len, alias, alias_len, halt_offset, pphar, error TSRMLS_CC); + return phar_open_file(fp, fname, fname_len, alias, alias_len, halt_offset, pphar, compression, error TSRMLS_CC); } halt_offset += got; @@ -1969,7 +2042,7 @@ return FAILURE; } - return phar_open_file(fp, fname, fname_len, alias, alias_len, halt_offset, NULL, error TSRMLS_CC); + return phar_open_file(fp, fname, fname_len, alias, alias_len, halt_offset, NULL, PHAR_FILE_COMPRESSED_NONE, error TSRMLS_CC); } /* }}} */ @@ -2266,7 +2339,7 @@ * user_stub contains either a string, or a resource pointer, if len is a negative length. * user_stub and len should be both 0 if the default or existing stub should be used */ -int phar_flush(phar_archive_data *archive, char *user_stub, long len, char **error TSRMLS_DC) /* {{{ */ +int phar_flush(phar_archive_data *phar, char *user_stub, long len, char **error TSRMLS_DC) /* {{{ */ { static const char newstub[] = "<?php __HALT_COMPILER(); ?>\r\n"; phar_entry_info *entry, *newentry; @@ -2293,19 +2366,19 @@ } #if HAVE_PHAR_ZIP - if (archive->is_zip) { - return phar_zip_flush(archive, user_stub, len, error TSRMLS_CC); + if (phar->is_zip) { + return phar_zip_flush(phar, user_stub, len, error TSRMLS_CC); } #endif - if (archive->is_tar) { - return phar_tar_flush(archive, user_stub, len, error TSRMLS_CC); + if (phar->is_tar) { + return phar_tar_flush(phar, user_stub, len, error TSRMLS_CC); } - if (archive->fp && !archive->is_brandnew) { - oldfile = archive->fp; + if (phar->fp && !phar->is_brandnew) { + oldfile = phar->fp; closeoldfile = 0; php_stream_rewind(oldfile); } else { - oldfile = php_stream_open_wrapper(archive->fname, "rb", 0, NULL); + oldfile = php_stream_open_wrapper(phar->fname, "rb", 0, NULL); closeoldfile = oldfile != NULL; } newfile = php_stream_fopen_tmpfile(); @@ -2328,7 +2401,7 @@ } php_stream_close(newfile); if (error) { - spprintf(error, 0, "unable to access resource to copy stub to new phar \"%s\"", archive->fname); + spprintf(error, 0, "unable to access resource to copy stub to new phar \"%s\"", phar->fname); } return EOF; } @@ -2344,7 +2417,7 @@ } php_stream_close(newfile); if (error) { - spprintf(error, 0, "unable to read resource to copy stub to new phar \"%s\"", archive->fname); + spprintf(error, 0, "unable to read resource to copy stub to new phar \"%s\"", phar->fname); } return EOF; } @@ -2359,7 +2432,7 @@ } php_stream_close(newfile); if (error) { - spprintf(error, 0, "illegal stub for phar \"%s\"", archive->fname); + spprintf(error, 0, "illegal stub for phar \"%s\"", phar->fname); } if (free_user_stub) { efree(user_stub); @@ -2374,39 +2447,39 @@ } php_stream_close(newfile); if (error) { - spprintf(error, 0, "unable to create stub from string in new phar \"%s\"", archive->fname); + spprintf(error, 0, "unable to create stub from string in new phar \"%s\"", phar->fname); } if (free_user_stub) { efree(user_stub); } return EOF; } - archive->halt_offset = len + 5; + phar->halt_offset = len + 5; if (free_user_stub) { efree(user_stub); } } else { - if (archive->halt_offset && oldfile && !archive->is_brandnew) { - if (archive->halt_offset != php_stream_copy_to_stream(oldfile, newfile, archive->halt_offset)) { + if (phar->halt_offset && oldfile && !phar->is_brandnew) { + if (phar->halt_offset != php_stream_copy_to_stream(oldfile, newfile, phar->halt_offset)) { if (closeoldfile) { php_stream_close(oldfile); } php_stream_close(newfile); if (error) { - spprintf(error, 0, "unable to copy stub of old phar to new phar \"%s\"", archive->fname); + spprintf(error, 0, "unable to copy stub of old phar to new phar \"%s\"", phar->fname); } return EOF; } } else { /* this is a brand new phar */ - archive->halt_offset = sizeof(newstub)-1; + phar->halt_offset = sizeof(newstub)-1; if (sizeof(newstub)-1 != php_stream_write(newfile, newstub, sizeof(newstub)-1)) { if (closeoldfile) { php_stream_close(oldfile); } php_stream_close(newfile); if (error) { - spprintf(error, 0, "unable to create stub in new phar \"%s\"", archive->fname); + spprintf(error, 0, "unable to create stub in new phar \"%s\"", phar->fname); } return EOF; } @@ -2418,13 +2491,13 @@ /* Check whether we can get rid of some of the deleted entries which are * unused. However some might still be in use so even after this clean-up * we need to skip entries marked is_deleted. */ - zend_hash_apply(&archive->manifest, phar_flush_clean_deleted_apply TSRMLS_CC); + zend_hash_apply(&phar->manifest, phar_flush_clean_deleted_apply TSRMLS_CC); /* compress as necessary, calculate crcs, serialize meta-data, manifest size, and file sizes */ main_metadata_str.c = 0; - if (archive->metadata) { + if (phar->metadata) { PHP_VAR_SERIALIZE_INIT(metadata_hash); - php_var_serialize(&main_metadata_str, &archive->metadata, &metadata_hash TSRMLS_CC); + php_var_serialize(&main_metadata_str, &phar->metadata, &metadata_hash TSRMLS_CC); PHP_VAR_SERIALIZE_DESTROY(metadata_hash); } else { main_metadata_str.len = 0; @@ -2432,10 +2505,10 @@ new_manifest_count = 0; offset = 0; buf = (char *) emalloc(8192); - for (zend_hash_internal_pointer_reset(&archive->manifest); - zend_hash_has_more_elements(&archive->manifest) == SUCCESS; - zend_hash_move_forward(&archive->manifest)) { - if (zend_hash_get_current_data(&archive->manifest, (void **)&entry) == FAILURE) { + for (zend_hash_internal_pointer_reset(&phar->manifest); + zend_hash_has_more_elements(&phar->manifest) == SUCCESS; + zend_hash_move_forward(&phar->manifest)) { + if (zend_hash_get_current_data(&phar->manifest, (void **)&entry) == FAILURE) { continue; } if (entry->cfp) { @@ -2471,9 +2544,9 @@ if (oldfile && !entry->is_modified) { continue; } - if (!entry->fp || (entry->is_modified && entry->fp == archive->fp)) { + if (!entry->fp || (entry->is_modified && entry->fp == phar->fp)) { /* re-open internal file pointer just-in-time */ - newentry = phar_open_jit(archive, entry, oldfile, error, 0 TSRMLS_CC); + newentry = phar_open_jit(phar, entry, oldfile, error, 0 TSRMLS_CC); if (!newentry) { /* major problem re-opening, so we ignore this file and the error */ efree(*error); @@ -2483,14 +2556,14 @@ entry = newentry; } file = entry->fp; - if (file == archive->fp) { - if (-1 == php_stream_seek(file, entry->offset_within_phar + archive->internal_file_start, SEEK_SET)) { + if (file == phar->fp) { + if (-1 == php_stream_seek(file, entry->offset_within_phar + phar->internal_file_start, SEEK_SET)) { if (closeoldfile) { php_stream_close(oldfile); } php_stream_close(newfile); if (error) { - spprintf(error, 0, "unable to seek to start of file \"%s\" while creating new phar \"%s\"", entry->filename, archive->fname); + spprintf(error, 0, "unable to seek to start of file \"%s\" while creating new phar \"%s\"", entry->filename, phar->fname); } return EOF; } @@ -2517,11 +2590,11 @@ php_stream_close(newfile); if (entry->flags & PHAR_ENT_COMPRESSED_GZ) { if (error) { - spprintf(error, 0, "unable to gzip compress file \"%s\" to new phar \"%s\"", entry->filename, archive->fname); + spprintf(error, 0, "unable to gzip compress file \"%s\" to new phar \"%s\"", entry->filename, phar->fname); } } else { if (error) { - spprintf(error, 0, "unable to bzip2 compress file \"%s\" to new phar \"%s\"", entry->filename, archive->fname); + spprintf(error, 0, "unable to bzip2 compress file \"%s\" to new phar \"%s\"", entry->filename, phar->fname); } } efree(buf); @@ -2544,14 +2617,14 @@ return EOF; } php_stream_flush(file); - if (file == archive->fp) { - if (-1 == php_stream_seek(file, entry->offset_within_phar + archive->internal_file_start, SEEK_SET)) { + if (file == phar->fp) { + if (-1 == php_stream_seek(file, entry->offset_within_phar + phar->internal_file_start, SEEK_SET)) { if (closeoldfile) { php_stream_close(oldfile); } php_stream_close(newfile); if (error) { - spprintf(error, 0, "unable to seek to start of file \"%s\" while creating new phar \"%s\"", entry->filename, archive->fname); + spprintf(error, 0, "unable to seek to start of file \"%s\" while creating new phar \"%s\"", entry->filename, phar->fname); } return EOF; } @@ -2582,34 +2655,34 @@ * 4: phar metadata length * ?: phar metadata */ - restore_alias_len = archive->alias_len; - if (!archive->is_explicit_alias) { - archive->alias_len = 0; + restore_alias_len = phar->alias_len; + if (!phar->is_explicit_alias) { + phar->alias_len = 0; } - manifest_len = offset + archive->alias_len + sizeof(manifest) + main_metadata_str.len; + manifest_len = offset + phar->alias_len + sizeof(manifest) + main_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); *(manifest + 9) = (unsigned char) (((PHAR_API_VERSION) & 0xF0)); phar_set_32(manifest+10, global_flags); - phar_set_32(manifest+14, archive->alias_len); + phar_set_32(manifest+14, phar->alias_len); /* write the manifest header */ if (sizeof(manifest) != php_stream_write(newfile, manifest, sizeof(manifest)) - || (size_t)archive->alias_len != php_stream_write(newfile, archive->alias, archive->alias_len)) { + || (size_t)phar->alias_len != php_stream_write(newfile, phar->alias, phar->alias_len)) { if (closeoldfile) { php_stream_close(oldfile); } php_stream_close(newfile); - archive->alias_len = restore_alias_len; + phar->alias_len = restore_alias_len; if (error) { - spprintf(error, 0, "unable to write manifest header of new phar \"%s\"", archive->fname); + spprintf(error, 0, "unable to write manifest header of new phar \"%s\"", phar->fname); } return EOF; } - archive->alias_len = restore_alias_len; + phar->alias_len = restore_alias_len; phar_set_32(manifest, main_metadata_str.len); if (main_metadata_str.len) { @@ -2620,9 +2693,9 @@ php_stream_close(oldfile); } php_stream_close(newfile); - archive->alias_len = restore_alias_len; + phar->alias_len = restore_alias_len; if (error) { - spprintf(error, 0, "unable to write manifest meta-data of new phar \"%s\"", archive->fname); + spprintf(error, 0, "unable to write manifest meta-data of new phar \"%s\"", phar->fname); } return EOF; } @@ -2633,9 +2706,9 @@ php_stream_close(oldfile); } php_stream_close(newfile); - archive->alias_len = restore_alias_len; + phar->alias_len = restore_alias_len; if (error) { - spprintf(error, 0, "unable to write manifest header of new phar \"%s\"", archive->fname); + spprintf(error, 0, "unable to write manifest header of new phar \"%s\"", phar->fname); } return EOF; } @@ -2646,10 +2719,10 @@ manifest_ftell = php_stream_tell(newfile); /* now write the manifest */ - for (zend_hash_internal_pointer_reset(&archive->manifest); - zend_hash_has_more_elements(&archive->manifest) == SUCCESS; - zend_hash_move_forward(&archive->manifest)) { - if (zend_hash_get_current_data(&archive->manifest, (void **)&entry) == FAILURE) { + for (zend_hash_internal_pointer_reset(&phar->manifest); + zend_hash_has_more_elements(&phar->manifest) == SUCCESS; + zend_hash_move_forward(&phar->manifest)) { + if (zend_hash_get_current_data(&phar->manifest, (void **)&entry) == FAILURE) { continue; } if (entry->is_deleted) { @@ -2669,7 +2742,7 @@ } php_stream_close(newfile); if (error) { - spprintf(error, 0, "unable to write filename of file \"%s\" to manifest of new phar \"%s\"", entry->filename, archive->fname); + spprintf(error, 0, "unable to write filename of file \"%s\" to manifest of new phar \"%s\"", entry->filename, phar->fname); } return EOF; } @@ -2679,7 +2752,7 @@ } php_stream_close(newfile); if (error) { - spprintf(error, 0, "unable to write filename of directory \"%s\" to manifest of new phar \"%s\"", entry->filename, archive->fname); + spprintf(error, 0, "unable to write filename of directory \"%s\" to manifest of new phar \"%s\"", entry->filename, phar->fname); } return EOF; } @@ -2706,7 +2779,7 @@ } php_stream_close(newfile); if (error) { - spprintf(error, 0, "unable to write temporary manifest of file \"%s\" to manifest of new phar \"%s\"", entry->filename, archive->fname); + spprintf(error, 0, "unable to write temporary manifest of file \"%s\" to manifest of new phar \"%s\"", entry->filename, phar->fname); } return EOF; } @@ -2714,10 +2787,10 @@ /* now copy the actual file data to the new phar */ offset = 0; - for (zend_hash_internal_pointer_reset(&archive->manifest); - zend_hash_has_more_elements(&archive->manifest) == SUCCESS; - zend_hash_move_forward(&archive->manifest)) { - if (zend_hash_get_current_data(&archive->manifest, (void **)&entry) == FAILURE) { + for (zend_hash_internal_pointer_reset(&phar->manifest); + zend_hash_has_more_elements(&phar->manifest) == SUCCESS; + zend_hash_move_forward(&phar->manifest)) { + if (zend_hash_get_current_data(&phar->manifest, (void **)&entry) == FAILURE) { continue; } if (entry->is_deleted) { @@ -2728,14 +2801,14 @@ php_stream_rewind(file); } else if (entry->fp && (entry->is_modified || !oldfile)) { file = entry->fp; - if (file == archive->fp) { - if (-1 == php_stream_seek(file, entry->offset_within_phar + archive->internal_file_start, SEEK_SET)) { + if (file == phar->fp) { + if (-1 == php_stream_seek(file, entry->offset_within_phar + phar->internal_file_start, SEEK_SET)) { if (closeoldfile) { php_stream_close(oldfile); } php_stream_close(newfile); if (error) { - spprintf(error, 0, "unable to seek to start of file \"%s\" while creating new phar \"%s\"", entry->filename, archive->fname); + spprintf(error, 0, "unable to seek to start of file \"%s\" while creating new phar \"%s\"", entry->filename, phar->fname); } return EOF; } @@ -2749,17 +2822,17 @@ } php_stream_close(newfile); if (error) { - spprintf(error, 0, "unable to seek to start of file \"%s\" while creating new phar \"%s\"", entry->filename, archive->fname); + spprintf(error, 0, "unable to seek to start of file \"%s\" while creating new phar \"%s\"", entry->filename, phar->fname); } return EOF; } - if (-1 == php_stream_seek(oldfile, entry->offset_within_phar + archive->internal_file_start, SEEK_SET)) { + if (-1 == php_stream_seek(oldfile, entry->offset_within_phar + phar->internal_file_start, SEEK_SET)) { if (closeoldfile) { php_stream_close(oldfile); } php_stream_close(newfile); if (error) { - spprintf(error, 0, "unable to seek to start of file \"%s\" while creating new phar \"%s\"", entry->filename, archive->fname); + spprintf(error, 0, "unable to seek to start of file \"%s\" while creating new phar \"%s\"", entry->filename, phar->fname); } return EOF; } @@ -2776,7 +2849,7 @@ } php_stream_close(newfile); if (error) { - spprintf(error, 0, "unable to write contents of file \"%s\" to new phar \"%s\"", entry->filename, archive->fname); + spprintf(error, 0, "unable to write contents of file \"%s\" to new phar \"%s\"", entry->filename, phar->fname); } return EOF; } @@ -2786,7 +2859,7 @@ entry->cfp = 0; } if (entry->fp && entry->fp_refcount == 0) { - if (entry->fp != archive->fp) { + if (entry->fp != phar->fp) { php_stream_close(entry->fp); } entry->fp = 0; @@ -2801,11 +2874,11 @@ php_stream_rewind(newfile); - if (archive->signature) { - efree(archive->signature); + if (phar->signature) { + efree(phar->signature); } - switch(archive->sig_flags) { + switch(phar->sig_flags) { #if HAVE_HASH_EXT case PHAR_SIG_SHA512: { unsigned char digest[64]; @@ -2818,7 +2891,7 @@ PHP_SHA512Final(digest, &context); php_stream_write(newfile, (char *) digest, sizeof(digest)); sig_flags |= PHAR_SIG_SHA512; - archive->sig_len = phar_hex_str((const char*)digest, sizeof(digest), &archive->signature); + phar->sig_len = phar_hex_str((const char*)digest, sizeof(digest), &phar->signature); break; } case PHAR_SIG_SHA256: { @@ -2832,7 +2905,7 @@ PHP_SHA256Final(digest, &context); php_stream_write(newfile, (char *) digest, sizeof(digest)); sig_flags |= PHAR_SIG_SHA256; - archive->sig_len = phar_hex_str((const char*)digest, sizeof(digest), &archive->signature); + phar->sig_len = phar_hex_str((const char*)digest, sizeof(digest), &phar->signature); break; } #else @@ -2843,7 +2916,7 @@ } php_stream_close(newfile); if (error) { - spprintf(error, 0, "unable to write contents of file \"%s\" to new phar \"%s\" with requested hash type", entry->filename, archive->fname); + spprintf(error, 0, "unable to write contents of file \"%s\" to new phar \"%s\" with requested hash type", entry->filename, phar->fname); } return EOF; #endif @@ -2861,7 +2934,7 @@ PHP_SHA1Final(digest, &context); php_stream_write(newfile, (char *) digest, sizeof(digest)); sig_flags |= PHAR_SIG_SHA1; - archive->sig_len = phar_hex_str((const char*)digest, sizeof(digest), &archive->signature); + phar->sig_len = phar_hex_str((const char*)digest, sizeof(digest), &phar->signature); break; } case PHAR_SIG_MD5: { @@ -2875,51 +2948,80 @@ PHP_MD5Final(digest, &context); php_stream_write(newfile, (char *) digest, sizeof(digest)); sig_flags |= PHAR_SIG_MD5; - archive->sig_len = phar_hex_str((const char*)digest, sizeof(digest), &archive->signature); + phar->sig_len = phar_hex_str((const char*)digest, sizeof(digest), &phar->signature); break; } } phar_set_32(sig_buf, sig_flags); php_stream_write(newfile, sig_buf, 4); php_stream_write(newfile, "GBMB", 4); - archive->sig_flags = sig_flags; + phar->sig_flags = sig_flags; } /* finally, close the temp file, rename the original phar, move the temp to the old phar, unlink the old phar, and reload it into memory */ - if (archive->fp) { - php_stream_close(archive->fp); + if (phar->fp) { + php_stream_close(phar->fp); } if (closeoldfile) { php_stream_close(oldfile); } - archive->internal_file_start = halt_offset + manifest_len + 4; - archive->is_brandnew = 0; + phar->internal_file_start = halt_offset + manifest_len + 4; + phar->is_brandnew = 0; php_stream_rewind(newfile); - if (archive->donotflush) { + if (phar->donotflush) { /* deferred flush */ - archive->fp = newfile; + phar->fp = newfile; } else { - archive->fp = php_stream_open_wrapper(archive->fname, "w+b", IGNORE_URL|STREAM_MUST_SEEK|REPORT_ERRORS, NULL); - if (!archive->fp) { - archive->fp = newfile; + phar->fp = php_stream_open_wrapper(phar->fname, "w+b", IGNORE_URL|STREAM_MUST_SEEK|REPORT_ERRORS, NULL); + if (!phar->fp) { + phar->fp = newfile; if (error) { - spprintf(error, 0, "unable to open new phar \"%s\" for writing", archive->fname); + spprintf(error, 0, "unable to open new phar \"%s\" for writing", phar->fname); } return EOF; } - php_stream_copy_to_stream(newfile, archive->fp, PHP_STREAM_COPY_ALL); - php_stream_close(newfile); - /* we could also reopen the file in "rb" mode but there is no need for that */ + if (phar->flags & PHAR_FILE_COMPRESSED_GZ) { + php_stream_filter *filter; + /* to properly compress, we have to tell zlib to add a zlib header */ + zval filterparams; + + array_init(&filterparams); + add_assoc_long(&filterparams, "window", MAX_WBITS); + filter = php_stream_filter_create("zlib.deflate", &filterparams, php_stream_is_persistent(phar->fp) TSRMLS_CC); + zval_dtor(&filterparams); + php_stream_filter_append(&phar->fp->writefilters, filter); + php_stream_copy_to_stream(newfile, phar->fp, PHP_STREAM_COPY_ALL); + php_stream_filter_flush(filter, 1); + php_stream_filter_remove(filter, 1 TSRMLS_CC); + php_stream_close(phar->fp); + /* use the temp stream as our base */ + phar->fp = newfile; + } else if (phar->flags & PHAR_FILE_COMPRESSED_BZ2) { + php_stream_filter *filter; + + filter = php_stream_filter_create("bzip2.compress", NULL, php_stream_is_persistent(phar->fp) TSRMLS_CC); + php_stream_filter_append(&phar->fp->writefilters, filter); + php_stream_copy_to_stream(newfile, phar->fp, PHP_STREAM_COPY_ALL); + php_stream_filter_flush(filter, 1); + php_stream_filter_remove(filter, 1 TSRMLS_CC); + php_stream_close(phar->fp); + /* use the temp stream as our base */ + phar->fp = newfile; + } else { + php_stream_copy_to_stream(newfile, phar->fp, PHP_STREAM_COPY_ALL); + /* we could also reopen the file in "rb" mode but there is no need for that */ + php_stream_close(newfile); + } } - if (-1 == php_stream_seek(archive->fp, archive->halt_offset, SEEK_SET)) { + if (-1 == php_stream_seek(phar->fp, phar->halt_offset, SEEK_SET)) { if (error) { - spprintf(error, 0, "unable to seek to __HALT_COMPILER(); in new phar \"%s\"", archive->fname); + spprintf(error, 0, "unable to seek to __HALT_COMPILER(); in new phar \"%s\"", phar->fname); } return EOF; } @@ -3161,7 +3263,7 @@ php_info_print_table_header(2, "Phar: PHP Archive support", "enabled"); php_info_print_table_row(2, "Phar EXT version", PHAR_EXT_VERSION_STR); php_info_print_table_row(2, "Phar API version", PHAR_API_VERSION_STR); - php_info_print_table_row(2, "CVS revision", "$Revision: 1.258 $"); + php_info_print_table_row(2, "CVS revision", "$Revision: 1.259 $"); php_info_print_table_row(2, "Phar-based phar archives", "enabled"); php_info_print_table_row(2, "Tar-based phar archives", "enabled"); #if HAVE_PHAR_ZIP http://cvs.php.net/viewvc.cgi/pecl/phar/phar_internal.h?r1=1.57&r2=1.58&diff_format=u Index: pecl/phar/phar_internal.h diff -u pecl/phar/phar_internal.h:1.57 pecl/phar/phar_internal.h:1.58 --- pecl/phar/phar_internal.h:1.57 Wed Jan 9 03:47:21 2008 +++ pecl/phar/phar_internal.h Wed Jan 9 07:09:03 2008 @@ -17,7 +17,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: phar_internal.h,v 1.57 2008/01/09 03:47:21 cellog Exp $ */ +/* $Id: phar_internal.h,v 1.58 2008/01/09 07:09:03 cellog Exp $ */ #ifdef HAVE_CONFIG_H #include "config.h" @@ -93,6 +93,12 @@ #define PHAR_HDR_COMPRESSED_BZ2 0x00002000 #define PHAR_HDR_SIGNATURE 0x00010000 +/* flags for defining that the entire file should be compressed */ +#define PHAR_FILE_COMPRESSION_MASK 0x00F00000 +#define PHAR_FILE_COMPRESSED_NONE 0x00000000 +#define PHAR_FILE_COMPRESSED_GZ 0x00100000 +#define PHAR_FILE_COMPRESSED_BZ2 0x00200000 + #define PHAR_SIG_MD5 0x0001 #define PHAR_SIG_SHA1 0x0002 #define PHAR_SIG_SHA256 0x0003 @@ -311,10 +317,10 @@ /* tar functions in tar.c */ int phar_is_tar(char *buf); -int phar_open_tarfile(php_stream* fp, char *fname, int fname_len, char *alias, int alias_len, int options, phar_archive_data** pphar, char **error TSRMLS_DC); +int phar_open_tarfile(php_stream* fp, char *fname, int fname_len, char *alias, int alias_len, int options, phar_archive_data** pphar, php_uint32 compression, char **error TSRMLS_DC); int phar_flush(phar_archive_data *archive, char *user_stub, long len, char **error TSRMLS_DC); int phar_open_or_create_tar(char *fname, int fname_len, char *alias, int alias_len, int options, phar_archive_data** pphar, char **error TSRMLS_DC); -int phar_tar_flush(phar_archive_data *archive, char *user_stub, long len, char **error TSRMLS_DC); +int phar_tar_flush(phar_archive_data *phar, char *user_stub, long len, char **error TSRMLS_DC); /* zip functions in zip.c */ int phar_open_zipfile(char *fname, int fname_len, char *alias, int alias_len, phar_archive_data** pphar, char **error TSRMLS_DC); http://cvs.php.net/viewvc.cgi/pecl/phar/phar_object.c?r1=1.113&r2=1.114&diff_format=u Index: pecl/phar/phar_object.c diff -u pecl/phar/phar_object.c:1.113 pecl/phar/phar_object.c:1.114 --- pecl/phar/phar_object.c:1.113 Wed Jan 9 00:58:36 2008 +++ pecl/phar/phar_object.c Wed Jan 9 07:09:03 2008 @@ -17,7 +17,7 @@ +----------------------------------------------------------------------+ */ -/* $Id: phar_object.c,v 1.113 2008/01/09 00:58:36 cellog Exp $ */ +/* $Id: phar_object.c,v 1.114 2008/01/09 07:09:03 cellog Exp $ */ #include "phar_internal.h" @@ -1224,6 +1224,23 @@ } /* }}} */ +/* {{{ proto bool Phar::isCompressed() + * Returns Phar::GZ or PHAR::BZ2 if the entire phar archive is compressed (.tar.gz/tar.bz and so on) + */ +PHP_METHOD(Phar, isCompressed) +{ + PHAR_ARCHIVE_OBJECT(); + + if (phar_obj->arc.archive->flags & PHAR_FILE_COMPRESSED_GZ) { + RETURN_LONG(PHAR_ENT_COMPRESSED_GZ); + } + if (phar_obj->arc.archive->flags & PHAR_FILE_COMPRESSED_BZ2) { + RETURN_LONG(PHAR_ENT_COMPRESSED_BZ2); + } + RETURN_LONG(0); +} +/* }}} */ + /* {{{ proto bool Phar::delete(string file) * Delete a file from within the Phar */ @@ -2776,6 +2793,7 @@ PHP_ME(Phar, isTar, NULL, ZEND_ACC_PUBLIC) PHP_ME(Phar, isZip, NULL, ZEND_ACC_PUBLIC) PHP_ME(Phar, isPhar, NULL, ZEND_ACC_PUBLIC) + PHP_ME(Phar, isCompressed, NULL, ZEND_ACC_PUBLIC) #endif /* static member functions */ PHP_ME(Phar, apiVersion, NULL, ZEND_ACC_PUBLIC|ZEND_ACC_STATIC|ZEND_ACC_FINAL) http://cvs.php.net/viewvc.cgi/pecl/phar/tar.c?r1=1.8&r2=1.9&diff_format=u Index: pecl/phar/tar.c diff -u pecl/phar/tar.c:1.8 pecl/phar/tar.c:1.9 --- pecl/phar/tar.c:1.8 Wed Jan 9 03:47:21 2008 +++ pecl/phar/tar.c Wed Jan 9 07:09:03 2008 @@ -143,7 +143,7 @@ return FAILURE; } -int phar_open_tarfile(php_stream* fp, char *fname, int fname_len, char *alias, int alias_len, int options, phar_archive_data** pphar, char **error TSRMLS_DC) /* {{{ */ +int phar_open_tarfile(php_stream* fp, char *fname, int fname_len, char *alias, int alias_len, int options, phar_archive_data** pphar, php_uint32 compression, char **error TSRMLS_DC) /* {{{ */ { char buf[512], *actual_alias = NULL; phar_entry_info entry = {0}; @@ -155,7 +155,6 @@ if (error) { *error = NULL; } - php_stream_seek(fp, 0, SEEK_SET); read = php_stream_read(fp, buf, sizeof(buf)); if (read != sizeof(buf)) { @@ -172,6 +171,8 @@ zend_hash_init(&myphar->manifest, sizeof(phar_entry_info), zend_get_hash_value, destroy_phar_manifest_entry, 0); myphar->is_tar = 1; + /* remember whether this entire phar was compressed with gz/bzip2 */ + myphar->flags = compression; entry.is_tar = 1; entry.is_crc_checked = 1; @@ -401,7 +402,7 @@ return ZEND_HASH_APPLY_KEEP; } -int phar_tar_flush(phar_archive_data *archive, char *user_stub, long len, char **error TSRMLS_DC) /* {{{ */ +int phar_tar_flush(phar_archive_data *phar, char *user_stub, long len, char **error TSRMLS_DC) /* {{{ */ { phar_entry_info entry = {0}; static const char newstub[] = "<?php // tar-based phar archive stub file\n__HALT_COMPILER();"; @@ -416,16 +417,16 @@ entry.is_crc_checked = 1; entry.is_tar = 1; entry.tar_type = '0'; - entry.phar = archive; + entry.phar = phar; /* set alias */ - if (archive->is_explicit_alias) { + if (phar->is_explicit_alias) { entry.filename = estrndup(".phar/alias.txt", sizeof(".phar/alias.txt")-1); entry.filename_len = sizeof(".phar/alias.txt")-1; entry.fp = php_stream_fopen_tmpfile(); - entry.crc32 = phar_tar_checksum(archive->alias, archive->alias_len); - php_stream_write(entry.fp, archive->alias, archive->alias_len); - entry.uncompressed_filesize = archive->alias_len; - zend_hash_update(&archive->manifest, entry.filename, entry.filename_len, (void*)&entry, sizeof(phar_entry_info), NULL); + entry.crc32 = phar_tar_checksum(phar->alias, phar->alias_len); + php_stream_write(entry.fp, phar->alias, phar->alias_len); + entry.uncompressed_filesize = phar->alias_len; + zend_hash_update(&phar->manifest, entry.filename, entry.filename_len, (void*)&entry, sizeof(phar_entry_info), NULL); } /* set stub */ @@ -435,7 +436,7 @@ /* resource passed in */ if (!(php_stream_from_zval_no_verify(stubfile, (zval **)user_stub))) { if (error) { - spprintf(error, 0, "unable to access resource to copy stub to new tar-based phar \"%s\"", archive->fname); + spprintf(error, 0, "unable to access resource to copy stub to new tar-based phar \"%s\"", phar->fname); } return EOF; } @@ -447,7 +448,7 @@ user_stub = 0; if (!(len = php_stream_copy_to_mem(stubfile, &user_stub, len, 0)) || !user_stub) { if (error) { - spprintf(error, 0, "unable to read resource to copy stub to new tar-based phar \"%s\"", archive->fname); + spprintf(error, 0, "unable to read resource to copy stub to new tar-based phar \"%s\"", phar->fname); } return EOF; } @@ -458,7 +459,7 @@ if ((pos = strstr(user_stub, "__HALT_COMPILER();")) == NULL) { if (error) { - spprintf(error, 0, "illegal stub for tar-based phar \"%s\"", archive->fname); + spprintf(error, 0, "illegal stub for tar-based phar \"%s\"", phar->fname); } if (free_user_stub) { efree(user_stub); @@ -472,7 +473,7 @@ if ((size_t)len != php_stream_write(entry.fp, user_stub, len) || 5 != php_stream_write(entry.fp, " ?>\r\n", 5)) { if (error) { - spprintf(error, 0, "unable to create stub from string in new tar-based phar \"%s\"", archive->fname); + spprintf(error, 0, "unable to create stub from string in new tar-based phar \"%s\"", phar->fname); } if (free_user_stub) { efree(user_stub); @@ -482,33 +483,33 @@ } entry.filename = estrndup(".phar/stub.php", sizeof(".phar/stub.php")-1); entry.filename_len = sizeof(".phar/stub.php")-1; - zend_hash_update(&archive->manifest, entry.filename, entry.filename_len, (void*)&entry, sizeof(phar_entry_info), NULL); + zend_hash_update(&phar->manifest, entry.filename, entry.filename_len, (void*)&entry, sizeof(phar_entry_info), NULL); if (free_user_stub) { efree(user_stub); } } else { - if (!zend_hash_exists(&archive->manifest, ".phar/stub.php", sizeof(".phar/stub.php")-1)) { + if (!zend_hash_exists(&phar->manifest, ".phar/stub.php", sizeof(".phar/stub.php")-1)) { /* this is a brand new phar */ entry.fp = php_stream_fopen_tmpfile(); if (sizeof(newstub)-1 != php_stream_write(entry.fp, newstub, sizeof(newstub)-1)) { if (error) { - spprintf(error, 0, "unable to create stub in new tar-based phar \"%s\"", archive->fname); + spprintf(error, 0, "unable to create stub in new tar-based phar \"%s\"", phar->fname); } return EOF; } entry.uncompressed_filesize = sizeof(newstub) - 1; entry.filename = estrndup(".phar/stub.php", sizeof(".phar/stub.php")-1); entry.filename_len = sizeof(".phar/stub.php")-1; - zend_hash_add(&archive->manifest, entry.filename, entry.filename_len, (void*)&entry, sizeof(phar_entry_info), NULL); + zend_hash_add(&phar->manifest, entry.filename, entry.filename_len, (void*)&entry, sizeof(phar_entry_info), NULL); } } - if (archive->fp && !archive->is_brandnew) { - oldfile = archive->fp; + if (phar->fp && !phar->is_brandnew) { + oldfile = phar->fp; closeoldfile = 0; php_stream_rewind(oldfile); } else { - oldfile = php_stream_open_wrapper(archive->fname, "rb", 0, NULL); + oldfile = php_stream_open_wrapper(phar->fname, "rb", 0, NULL); closeoldfile = oldfile != NULL; } newfile = php_stream_fopen_tmpfile(); @@ -526,7 +527,7 @@ pass.new = newfile; pass.error = error; - zend_hash_apply_with_argument(&archive->manifest, (apply_func_arg_t) phar_tar_writeheaders, (void *) &pass TSRMLS_CC); + zend_hash_apply_with_argument(&phar->manifest, (apply_func_arg_t) phar_tar_writeheaders, (void *) &pass TSRMLS_CC); /* add final zero blocks */ buf = (char *) ecalloc(1024, 1); @@ -541,29 +542,59 @@ php_stream_close(newfile); return EOF; } - if (archive->fp) { - php_stream_close(archive->fp); + if (phar->fp) { + php_stream_close(phar->fp); } - archive->is_brandnew = 0; + phar->is_brandnew = 0; php_stream_rewind(newfile); - if (archive->donotflush) { + if (phar->donotflush) { /* deferred flush */ - archive->fp = newfile; + phar->fp = newfile; } else { - archive->fp = php_stream_open_wrapper(archive->fname, "w+b", IGNORE_URL|STREAM_MUST_SEEK|REPORT_ERRORS, NULL); - if (!archive->fp) { - archive->fp = newfile; + + phar->fp = php_stream_open_wrapper(phar->fname, "w+b", IGNORE_URL|STREAM_MUST_SEEK|REPORT_ERRORS, NULL); + if (!phar->fp) { + phar->fp = newfile; if (error) { - spprintf(error, 0, "unable to open new phar \"%s\" for writing", archive->fname); + spprintf(error, 0, "unable to open new phar \"%s\" for writing", phar->fname); } return EOF; } - php_stream_copy_to_stream(newfile, archive->fp, PHP_STREAM_COPY_ALL); - php_stream_close(newfile); - /* we could also reopen the file in "rb" mode but there is no need for that */ + if (phar->flags & PHAR_FILE_COMPRESSED_GZ) { + php_stream_filter *filter; + /* to properly compress, we have to tell zlib to add a zlib header */ + zval filterparams; + + array_init(&filterparams); + add_assoc_long(&filterparams, "window", MAX_WBITS); + filter = php_stream_filter_create("zlib.deflate", &filterparams, php_stream_is_persistent(phar->fp) TSRMLS_CC); + zval_dtor(&filterparams); + php_stream_filter_append(&phar->fp->writefilters, filter); + php_stream_copy_to_stream(newfile, phar->fp, PHP_STREAM_COPY_ALL); + php_stream_filter_flush(filter, 1); + php_stream_filter_remove(filter, 1 TSRMLS_CC); + php_stream_close(phar->fp); + /* use the temp stream as our base */ + phar->fp = newfile; + } else if (phar->flags & PHAR_FILE_COMPRESSED_BZ2) { + php_stream_filter *filter; + + filter = php_stream_filter_create("bzip2.compress", NULL, php_stream_is_persistent(phar->fp) TSRMLS_CC); + php_stream_filter_append(&phar->fp->writefilters, filter); + php_stream_copy_to_stream(newfile, phar->fp, PHP_STREAM_COPY_ALL); + php_stream_filter_flush(filter, 1); + php_stream_filter_remove(filter, 1 TSRMLS_CC); + php_stream_close(phar->fp); + /* use the temp stream as our base */ + phar->fp = newfile; + } else { + php_stream_copy_to_stream(newfile, phar->fp, PHP_STREAM_COPY_ALL); + /* we could also reopen the file in "rb" mode but there is no need for that */ + php_stream_close(newfile); + } } return EOF; } http://cvs.php.net/viewvc.cgi/pecl/phar/tests/tar/tar_gzip.phpt?view=markup&rev=1.1 Index: pecl/phar/tests/tar/tar_gzip.phpt +++ pecl/phar/tests/tar/tar_gzip.phpt --TEST-- Phar: tar-based phar, gzipped tar --SKIPIF-- <?php if (!extension_loaded('phar')) die('skip'); ?> <?php if (!extension_loaded("spl")) die("skip SPL not available"); ?> <?php if (!extension_loaded("zlib")) die("skip zlib not available"); ?> --INI-- phar.readonly=0 --FILE-- <?php include dirname(__FILE__) . '/tarmaker.php.inc'; $fname = dirname(__FILE__) . '/tar_gzip.phar'; $pname = 'phar://' . $fname; $fname2 = dirname(__FILE__) . '/tar_gzip.phar.tar'; $pname2 = 'phar://' . $fname2; $a = new tarmaker($fname, 'zlib'); $a->init(); $a->addFile('tar_004.php', '<?php var_dump(__FILE__);'); $a->addFile('internal/file/here', "hi there!\n"); $a->mkDir('internal/dir'); $a->mkDir('dir'); $a->addFile('.phar/stub.php', '<?php Phar::mapPhar(); var_dump("it worked"); include "phar://" . __FILE__ . "/tar_004.php"; '); $a->close(); include $fname; $a = new Phar($fname); $a['test'] = 'hi'; copy($fname, $fname2); $b = new Phar($fname2); var_dump($b->isCompressed == Phar::GZ); __HALT_COMPILER(); ?> ===DONE=== --CLEAN-- <?php @unlink(dirname(__FILE__) . '/tar_gzip.phar'); @unlink(dirname(__FILE__) . '/tar_gzip.phar.tar'); ?> --EXPECTF-- string(9) "it worked" string(%d) "phar://%star_gzip.phar/tar_004.php" bool(true) ===DONE===