]> granicus.if.org Git - php/commitdiff
add Phar::convertToTar(), Phar::convertToZip(), and Phar::convertToPhar()
authorGreg Beaver <cellog@php.net>
Wed, 16 Jan 2008 07:24:39 +0000 (07:24 +0000)
committerGreg Beaver <cellog@php.net>
Wed, 16 Jan 2008 07:24:39 +0000 (07:24 +0000)
ext/phar/package.php
ext/phar/phar.c
ext/phar/phar_internal.h
ext/phar/phar_object.c
ext/phar/tests/phar_convert_tar.phpt [new file with mode: 0644]
ext/phar/tests/phar_convert_zip.phpt [new file with mode: 0644]
ext/phar/tests/tar/phar_convert_phar.phpt [new file with mode: 0644]
ext/phar/tests/zip/phar_convert_phar.phpt [new file with mode: 0644]
ext/phar/zip.c

index 82d807e36a75eb94dab44f5859514c55355debfa..4368edbb3108ecd100cd5f33cf488882d4811d01 100644 (file)
@@ -4,6 +4,7 @@ $notes = '
 Major feature functionality release
  * add support for tar-based and zip-based phar archives [Greg]
  * add Phar::isTar(), Phar::isZip(), and Phar::isPhar() [Greg]
+ * add Phar::convertToTar(), Phar::convertToZip(), and Phar::convertToPhar() [Greg]
  * add Phar::webPhar() for running a web-based application unmodified
    directly from a phar archive [Greg]
  * file functions (fopen-based and stat-based) can be instructed to only look for
index df70fc284bca8ec99ca1299a32930453df8f52ac..b49771046975ca33e2125a97968dcfe7977d86db 100644 (file)
@@ -791,7 +791,7 @@ phar_entry_data *phar_get_or_create_entry_data(char *fname, int fname_len, char
        }
        etemp.is_modified = 1;
        etemp.timestamp = time(0);
-       etemp.offset_within_phar = -1;
+       etemp.offset_within_phar = (phar->is_zip ? 0 : -1);
        etemp.is_crc_checked = 1;
        etemp.phar = phar;
 #if HAVE_PHAR_ZIP
@@ -2168,7 +2168,6 @@ phar_entry_info * phar_open_jit(phar_archive_data *phar, phar_entry_info *entry,
 # if HAVE_BZ2
                                char *filter_name;
                                php_stream_filter *filter;
-                               php_stream *fp;
                                /* we have to decompress this by hand */
 
                                if (!phar_has_bz2) {
index 95b3d80e222f70d02dc50a4e8417162690947c76..4973b5ab2f76cba614d9cd86b2af0e5633cd4cda 100755 (executable)
@@ -43,6 +43,7 @@
 #include "main/streams/php_stream_plain_wrapper.h"
 #include "main/SAPI.h"
 #include "main/php_main.h"
+#include "main/php_open_temporary_file.h"
 #include "ext/standard/info.h"
 #include "ext/standard/basic_functions.h"
 #include "ext/standard/file.h"
index 7e9d614297356c5234f4bce7db9a6ea20cd68831..f84e41e6377e8ea4b6b263d905c227cd17845431 100755 (executable)
@@ -1304,6 +1304,543 @@ PHP_METHOD(Phar, isPhar)
 }
 /* }}} */
 
+static int phar_copy_file_contents(phar_entry_info *entry, php_stream *fp TSRMLS_DC) /* {{{ */
+{
+       /* if not available, open the file for reading */
+       if (!entry->fp) {
+               char *error;
+               if (!phar_open_jit(entry->phar, entry, entry->phar->fp, &error, 0 TSRMLS_CC)) {
+                       if (error) {
+                               zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC,
+                                       "Cannot convert phar archive \"%s\", unable to open entry \"%s\" contents: %s", entry->phar->fname, entry->filename, error);
+                               efree(error);
+                       } else {
+                               zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC,
+                                       "Cannot convert phar archive \"%s\", unable to open entry \"%s\" contents", entry->phar->fname, entry->filename);
+                       }
+                       return FAILURE;
+               }
+       }
+       /* copy old contents in entirety */
+       php_stream_seek(entry->fp, entry->phar->internal_file_start + entry->offset_within_phar, SEEK_SET);
+       if (entry->uncompressed_filesize != php_stream_copy_to_stream(entry->fp, fp, entry->uncompressed_filesize)) {
+               zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC,
+                       "Cannot convert phar archive \"%s\", unable to copy entry \"%s\" contents", entry->phar->fname, entry->filename);
+               return FAILURE;
+       }
+       if (entry->fp != entry->phar->fp) {
+               php_stream_close(entry->fp);
+               entry->fp = NULL;
+       }
+       return SUCCESS;
+}
+/* }}} */
+
+static void phar_convert_to_other(phar_archive_data *source, int convert TSRMLS_DC) /* {{{ */
+{
+       phar_archive_data phar = {0};
+       long offset = 0;
+       char *error, *opened_path = NULL;
+       int fd, ziperror;
+
+       switch (convert) {
+               case 1 :
+                       phar.is_tar = 1;
+                       break;
+               case 2 :
+                       phar.is_zip = 1;
+#if HAVE_PHAR_ZIP
+                       if (!((fd = php_open_temporary_fd(NULL, "pharzip", &opened_path TSRMLS_CC)) >= 0)) {
+                               zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC,
+                                       "Cannot convert phar archive \"%s\", unable to open temporary zip archive", source->fname);
+                               return;
+                       }
+                       close(fd);
+                       unlink(opened_path);
+                       phar.zip = zip_open(opened_path, ZIP_CREATE, &ziperror);
+                       if (!phar.zip) {
+                               /* now for the stupid hoops libzip forces... */
+                               char *tmp;
+                               int tmp_len;
+
+                               efree(opened_path);
+                               tmp_len = zip_error_to_str(NULL, 0, ziperror, ziperror);
+                               if (!(tmp = emalloc((tmp_len + 1) * sizeof(char)))) {
+                                       zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC,
+                                               "Cannot convert phar archive \"%s\", unable to open temporary zip archive", source->fname);
+                               } else {
+                                       if (!zip_error_to_str(tmp, tmp_len + 1, ziperror, ziperror)) {
+                                               efree(tmp);
+                                               zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC,
+                                                       "Cannot convert phar archive \"%s\", unable to open temporary zip archive", source->fname);
+                                       } else {
+                                               zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC, 
+                                                       "Cannot convert phar archive \"%s\", unable to open temporary zip archive: %s", source->fname, tmp);
+                                               efree(tmp);
+                                       }
+                               }
+                               return;
+                       }
+#endif
+                       break;
+       }
+
+       zend_hash_init(&(phar.manifest), sizeof(phar_entry_info),
+               zend_get_hash_value, destroy_phar_manifest_entry, 0);
+
+       phar.fp = php_stream_fopen_tmpfile();
+       phar.fname = source->fname;
+       /* first copy each file's uncompressed contents to a temporary file and set per-file flags */
+       for (zend_hash_internal_pointer_reset(&source->manifest); SUCCESS == zend_hash_has_more_elements(&source->manifest); zend_hash_move_forward(&source->manifest)) {
+               phar_entry_info *entry, newentry;
+               if (FAILURE == zend_hash_get_current_data(&source->manifest, (void **) &entry)) {
+                       zend_hash_destroy(&(phar.manifest));
+                       php_stream_close(phar.fp);
+                       zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC,
+                               "Cannot convert phar archive \"%s\"", source->fname);
+                       if (opened_path) {
+                               efree(opened_path);
+                       }
+#if HAVE_PHAR_ZIP
+                       if (phar.is_zip) {
+                               _zip_free(phar.zip);
+                       }
+#endif
+                       return;
+               }
+               if (entry->fp_refcount > 0) {
+                       zend_hash_destroy(&(phar.manifest));
+                       php_stream_close(phar.fp);
+                       zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC,
+                               "Cannot convert phar archive \"%s\", open file pointers for entry \"%s\"", source->fname, entry->filename);
+#if HAVE_PHAR_ZIP
+                       if (phar.is_zip) {
+                               _zip_free(phar.zip);
+                       }
+#endif
+                       if (opened_path) {
+                               efree(opened_path);
+                       }
+                       return;
+               }
+               newentry = *entry;
+               if (FAILURE == phar_copy_file_contents(entry, phar.fp TSRMLS_CC)) {
+                       zend_hash_destroy(&(phar.manifest));
+                       php_stream_close(phar.fp);
+                       /* exception already thrown */
+#if HAVE_PHAR_ZIP
+                       if (phar.is_zip) {
+                               _zip_free(phar.zip);
+                       }
+#endif
+                       if (opened_path) {
+                               efree(opened_path);
+                       }
+                       return;
+               }
+               newentry.filename = estrndup(newentry.filename, newentry.filename_len);
+               if (newentry.metadata) {
+                       SEPARATE_ZVAL(&(newentry.metadata));
+               }
+               newentry.offset_within_phar = offset;
+               offset += newentry.uncompressed_filesize;
+               newentry.is_zip = phar.is_zip;
+               newentry.is_tar = phar.is_tar;
+               if (newentry.is_tar) {
+                       newentry.tar_type = (entry->is_dir ? TAR_DIR : TAR_FILE);
+               }
+               newentry.fp = phar.fp;
+#if HAVE_PHAR_ZIP
+               newentry.index = -1;
+#endif
+               newentry.is_modified = 1;
+               newentry.phar = &phar;
+               newentry.old_flags = newentry.flags & ~PHAR_ENT_COMPRESSION_MASK; /* remove compression from old_flags */
+               zend_hash_add(&(phar.manifest), newentry.filename, newentry.filename_len, (void*)&newentry, sizeof(phar_entry_info), NULL);
+       }
+
+       /* next copy the stub and flush */
+       if (phar.is_zip) {
+               phar.fname = opened_path;
+       }
+
+       if (source->is_zip) {
+               phar_entry_info *entry;
+               long tmp = 0;
+               zval *userz;
+
+               if (FAILURE == (zend_hash_find(&source->manifest, ".phar/stub.php", sizeof(".phar/stub.php")-1, (void **)&entry))) {
+                       /* use default stub - the existing one in the manifest will be used if present */
+                       phar_flush(&phar, NULL, 0, &error TSRMLS_CC);
+                       goto finalize;
+               }
+               if (!entry->fp && !phar_open_jit(source, entry, NULL, &error, 0 TSRMLS_CC)) {
+                       if (error) {
+                               efree(error);
+                       }
+                       php_stream_close(phar.fp);
+                       zend_hash_destroy(&(phar.manifest));
+                       zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC,
+                               "Cannot convert phar archive \"%s\": unable to retrieve stub", source->fname, error);
+#if HAVE_PHAR_ZIP
+                       if (phar.is_zip) {
+                               _zip_free(phar.zip);
+                       }
+#endif
+                       if (opened_path) {
+                               efree(opened_path);
+                       }
+                       return;
+               }
+               /* copy the other phar's stub */
+               php_stream_seek(entry->fp, 0, SEEK_SET);
+               ALLOC_ZVAL(userz);
+               php_stream_to_zval(entry->fp, userz);
+               tmp = entry->uncompressed_filesize;
+               phar_flush(&phar, (char *) &userz, -tmp, &error TSRMLS_CC);
+               efree(userz);
+               php_stream_close(entry->fp);
+               if (error) {
+                       zend_hash_destroy(&(phar.manifest));
+                       php_stream_close(phar.fp);
+                       zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC,
+                               "Cannot convert phar archive \"%s\": %s", source->fname, error);
+                       efree(error);
+#if HAVE_PHAR_ZIP
+                       if (phar.is_zip) {
+                               _zip_free(phar.zip);
+                       }
+#endif
+                       if (opened_path) {
+                               efree(opened_path);
+                       }
+                       return;
+               }
+       } else {
+               zval *userz;
+
+               if (source->is_tar) {
+                       phar_entry_info *entry;
+
+                       if (FAILURE == (zend_hash_find(&source->manifest, ".phar/stub.php", sizeof(".phar/stub.php")-1, (void **)&entry))) {
+                               /* use default stub - the existing one in the manifest will be used if present */
+                               phar_flush(&phar, NULL, 0, &error TSRMLS_CC);
+                               goto finalize;
+                       }
+                       php_stream_seek(source->fp, entry->offset_within_phar, SEEK_SET);
+                       /* use this unused value to set the stub size */
+                       source->internal_file_start = entry->uncompressed_filesize;
+               } else {
+                       php_stream_seek(source->fp, 0, SEEK_SET);
+               }
+               /* copy the other phar's stub */
+               ALLOC_ZVAL(userz);
+               php_stream_to_zval(source->fp, userz);
+               phar_flush(&phar, (char *) &userz, -(source->internal_file_start), &error TSRMLS_CC);
+               efree(userz);
+               if (source->is_tar) {
+                       source->internal_file_start = 0;
+               }
+       }
+
+       if (error) {
+               zend_hash_destroy(&(phar.manifest));
+               zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC,
+                       "Cannot convert phar archive \"%s\": %s", source->fname, error);
+               efree(error);
+               php_stream_close(phar.fp);
+#if HAVE_PHAR_ZIP
+               if (phar.is_zip) {
+                       _zip_free(phar.zip);
+               }
+#endif
+               if (opened_path) {
+                       efree(opened_path);
+               }
+               return;
+       }
+
+finalize:
+       /* third, update the old phar's manifest */
+       for (zend_hash_internal_pointer_reset(&(phar.manifest)); SUCCESS == zend_hash_has_more_elements(&(phar.manifest)); zend_hash_move_forward(&(phar.manifest))) {
+               phar_entry_info *entry, *mine;
+               if (FAILURE == zend_hash_get_current_data(&(phar.manifest), (void **) &entry)) {
+                       zend_hash_destroy(&(phar.manifest));
+                       php_stream_close(phar.fp);
+                       zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC,
+                               "Cannot convert phar archive \"%s\"", source->fname);
+#if HAVE_PHAR_ZIP
+                       if (phar.is_zip) {
+                               _zip_free(phar.zip);
+                       }
+#endif
+                       if (opened_path) {
+                               efree(opened_path);
+                       }
+                       /* we can't throw an exception or bad crap will happen */
+                       zend_error(E_ERROR, "Error: could not convert phar archive \"%s\", phar is in unstable state, shutting down", source->fname);
+                       return;
+               }
+               if (FAILURE == zend_hash_find(&source->manifest, entry->filename, entry->filename_len, (void **) &mine)) {
+                       if (phar.is_tar || phar.is_zip) {
+                               if (entry->filename_len == sizeof(".phar/stub.php")-1 && !strncmp(entry->filename, ".phar/stub.php", sizeof(".phar/stub.php")-1)) {
+                                       /* a little hack to prevent destruction of data */
+                                       dtor_func_t save;
+
+                                       save = phar.manifest.pDestructor;
+                                       phar.manifest.pDestructor = NULL;
+                                       zend_hash_add(&source->manifest, ".phar/stub.php", sizeof(".phar/stub.php")-1, (void*)entry, sizeof(phar_entry_info), NULL);
+                                       zend_hash_del(&(phar.manifest), ".phar/stub.php", sizeof(".phar/stub.php")-1);
+                                       phar.manifest.pDestructor = save;
+                                       continue;
+                               }
+                               if (entry->filename_len == sizeof(".phar/alias.txt")-1 && !strncmp(entry->filename, ".phar/alias.txt", sizeof(".phar/alias.txt")-1)) {
+                                       /* a little hack to prevent destruction of data */
+                                       dtor_func_t save;
+
+                                       save = phar.manifest.pDestructor;
+                                       phar.manifest.pDestructor = NULL;
+                                       zend_hash_add(&source->manifest, ".phar/alias.txt", sizeof(".phar/alias.txt")-1, (void*)entry, sizeof(phar_entry_info), NULL);
+                                       zend_hash_del(&(phar.manifest), ".phar/alias.txt", sizeof(".phar/alias.txt")-1);
+                                       phar.manifest.pDestructor = save;
+                                       continue;
+                               }
+                       }
+                       zend_hash_destroy(&(phar.manifest));
+                       php_stream_close(phar.fp);
+#if HAVE_PHAR_ZIP
+                       if (phar.is_zip) {
+                               _zip_free(phar.zip);
+                       }
+#endif
+                       if (opened_path) {
+                               efree(opened_path);
+                       }
+                       /* we can't throw an exception or bad crap will happen */
+                       zend_error(E_ERROR, "Error: could not convert phar archive \"%s\", phar is in unstable state, shutting down", source->fname);
+                       return;
+               }
+               /* now, replace the manifest entry with the new guy */
+               efree(mine->filename);
+               if (mine->metadata) {
+                       zval_dtor(mine->metadata);
+                       efree(mine->metadata);
+               }
+               *mine = *entry;
+               mine->phar = source;
+       }
+       source->fp = phar.fp;
+       /* don't free stuff */
+       phar.manifest.pDestructor = NULL;
+       zend_hash_destroy(&(phar.manifest));
+       source->is_zip = phar.is_zip;
+       source->is_tar = phar.is_tar;
+       if (phar.signature) {
+               if (source->signature) {
+                       efree(source->signature);
+               }
+               source->signature = phar.signature;
+       }
+#if HAVE_PHAR_ZIP
+       if (phar.is_zip) {
+               _zip_free(phar.zip);
+               _zip_free(source->zip);
+               if (FAILURE == php_copy_file(opened_path, source->fname)) {
+                       /* we can't throw an exception or bad crap will happen */
+                       zend_error(E_ERROR, "Error: could not copy newly created zip to \"%s\", phar is in unstable state, shutting down", source->fname);
+               }
+               source->zip = zip_open(source->fname, 0, &ziperror);
+               if (!source->zip) {
+                       efree(opened_path);
+                       /* now for the stupid hoops libzip forces... */
+                       char *tmp;
+                       int tmp_len;
+                       tmp_len = zip_error_to_str(NULL, 0, ziperror, ziperror);
+                       if (!(tmp = emalloc((tmp_len + 1) * sizeof(char)))) {
+                               zend_error(E_ERROR, "Error: could not re-open newly created zip \"%s\", phar is in unstable state, shutting down", source->fname);
+                       } else {
+                               if (!zip_error_to_str(tmp, tmp_len + 1, ziperror, ziperror)) {
+                                       efree(tmp);
+                                       zend_error(E_ERROR, "Error: could not re-open newly created zip \"%s\", phar is in unstable state, shutting down", source->fname);
+                               } else {
+                                       zend_error(E_ERROR, "Error: could not re-open newly created zip \"%s\" (%s), phar is in unstable state, shutting down", source->fname, tmp);
+                                       efree(tmp);
+                               }
+                       }
+                       return;
+               }
+               efree(opened_path);
+       }
+#endif
+}
+
+/* {{{ proto bool Phar::convertToTar()
+ * Convert the phar archive to the tar file format
+ */
+PHP_METHOD(Phar, convertToTar)
+{
+       char *error;
+       PHAR_ARCHIVE_OBJECT();
+
+       if (phar_obj->arc.archive->is_tar) {
+               RETURN_TRUE;
+       }
+       if (PHAR_G(readonly)) {
+               zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC,
+                       "Cannot write out phar archive, phar is read-only");
+               return;
+       }
+
+       if (!zend_hash_num_elements(&phar_obj->arc.archive->manifest)) {
+               /* nothing need be done specially, this has no files in it */
+               phar_obj->arc.archive->is_tar = 1;
+               phar_obj->arc.archive->is_modified = 1;
+               RETURN_TRUE;
+       }
+
+       if (phar_obj->arc.archive->donotflush) {
+               zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC,
+                       "Cannot convert phar archive to tar format, call stopBuffering() first");
+               return;
+       }
+
+       phar_flush(phar_obj->arc.archive, NULL, 0, &error TSRMLS_CC);
+       if (error) {
+               zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, error);
+               efree(error);
+               return;
+       }
+       phar_convert_to_other(phar_obj->arc.archive, 1);
+}
+/* }}} */
+
+
+/* {{{ proto bool Phar::convertToZip()
+ * Convert the phar archive to the zip file format
+ */
+PHP_METHOD(Phar, convertToZip)
+{
+       char *error;
+       PHAR_ARCHIVE_OBJECT();
+       
+       if (phar_obj->arc.archive->is_zip) {
+               RETURN_TRUE;
+       }
+       if (PHAR_G(readonly)) {
+               zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC,
+                       "Cannot write out phar archive, phar is read-only");
+               return;
+       }
+
+#if HAVE_PHAR_ZIP
+       if (!phar_has_zip) {
+               zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC,
+                       "Cannot convert phar archive to zip format, zip-based phar archives are disabled (enable ext/zip in php.ini)");
+               return;
+       }
+#else
+       zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC,
+               "Cannot convert phar archive to zip format, zip-based phar archives are unavailable");
+       return;
+#endif
+
+       if (!zend_hash_num_elements(&phar_obj->arc.archive->manifest)) {
+               int ziperror;
+               /* nothing need be done specially, this has no files in it */
+               phar_obj->arc.archive->is_zip = 1;
+               phar_obj->arc.archive->internal_file_start = 0;
+               phar_obj->arc.archive->is_modified = 1;
+               phar_obj->arc.archive->zip = zip_open(phar_obj->arc.archive->fname, ZIP_CREATE, &ziperror);
+               if (!phar_obj->arc.archive->zip) {
+                       /* now for the stupid hoops libzip forces... */
+                       char *tmp;
+                       int tmp_len;
+                       tmp_len = zip_error_to_str(NULL, 0, ziperror, ziperror);
+                       if (!(tmp = emalloc((tmp_len + 1) * sizeof(char)))) {
+                               zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC,
+                                       "Cannot convert phar archive to zip format, unable to open", phar_obj->arc.archive->fname);
+                       } else {
+                               if (!zip_error_to_str(tmp, tmp_len + 1, ziperror, ziperror)) {
+                                       efree(tmp);
+                                       zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC,
+                                               "Cannot convert phar archive to zip format, unable to open", phar_obj->arc.archive->fname);
+                               } else {
+                               zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC,
+                                       "Cannot convert phar archive to zip format, unable to open: %s", phar_obj->arc.archive->fname, tmp);
+                                       efree(tmp);
+                               }
+                       }
+                       return;
+               }
+               RETURN_TRUE;
+       }
+
+       if (phar_obj->arc.archive->donotflush) {
+               zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC,
+                       "Cannot convert phar archive to zip format, call stopBuffering() first");
+               return;
+       }
+
+       phar_flush(phar_obj->arc.archive, NULL, 0, &error TSRMLS_CC);
+       if (error) {
+               zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, error);
+               efree(error);
+               return;
+       }
+       phar_convert_to_other(phar_obj->arc.archive, 2);
+}
+/* }}} */
+
+
+/* {{{ proto bool Phar::convertToPhar()
+ * Convert the phar archive to the phar file format
+ */
+PHP_METHOD(Phar, convertToPhar)
+{
+       char *error;
+       PHAR_ARCHIVE_OBJECT();
+       
+       if (!phar_obj->arc.archive->is_tar && !phar_obj->arc.archive->is_zip) {
+               RETURN_TRUE;
+       }
+       if (PHAR_G(readonly)) {
+               zend_throw_exception_ex(spl_ce_UnexpectedValueException, 0 TSRMLS_CC,
+                       "Cannot write out phar archive, phar is read-only");
+               return;
+       }
+
+       if (phar_obj->arc.archive->donotflush) {
+               zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC,
+                       "Cannot convert phar archive to phar format, call stopBuffering() first");
+               return;
+       }
+
+       if (!zend_hash_num_elements(&phar_obj->arc.archive->manifest)) {
+               /* nothing need be done specially, this has no files in it */
+               phar_obj->arc.archive->is_tar = 0;
+               phar_obj->arc.archive->is_zip = 0;
+               phar_obj->arc.archive->is_modified = 1;
+               RETURN_TRUE;
+       }
+
+       if (phar_obj->arc.archive->donotflush) {
+               zend_throw_exception_ex(spl_ce_BadMethodCallException, 0 TSRMLS_CC,
+                       "Cannot convert phar archive to zip format, call stopBuffering() first");
+               return;
+       }
+
+       phar_flush(phar_obj->arc.archive, NULL, 0, &error TSRMLS_CC);
+       if (error) {
+               zend_throw_exception_ex(phar_ce_PharException, 0 TSRMLS_CC, error);
+               efree(error);
+               return;
+       }
+               
+       phar_convert_to_other(phar_obj->arc.archive, 0);
+       RETURN_TRUE;
+}
+/* }}} */
+
 /* {{{ proto bool Phar::isCompressed()
  * Returns Phar::GZ or PHAR::BZ2 if the entire phar archive is compressed (.tar.gz/tar.bz and so on)
  */
@@ -2916,6 +3453,9 @@ zend_function_entry php_archive_methods[] = {
        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, convertToTar,          NULL,                      ZEND_ACC_PUBLIC)
+       PHP_ME(Phar, convertToZip,          NULL,                      ZEND_ACC_PUBLIC)
+       PHP_ME(Phar, convertToPhar,         NULL,                      ZEND_ACC_PUBLIC)
        PHP_ME(Phar, isCompressed,          NULL,                      ZEND_ACC_PUBLIC)
 #endif
        /* static member functions */
diff --git a/ext/phar/tests/phar_convert_tar.phpt b/ext/phar/tests/phar_convert_tar.phpt
new file mode 100644 (file)
index 0000000..7eabb08
--- /dev/null
@@ -0,0 +1,42 @@
+--TEST--
+Phar::convertToTar()
+--SKIPIF--
+<?php if (!extension_loaded("phar")) die("skip"); ?>
+--INI--
+phar.require_hash=0
+phar.readonly=0
+--FILE--
+<?php
+$fname = dirname(__FILE__) . '/' . basename(__FILE__, '.php') . '.phar.php';
+$pname = 'phar://' . $fname;
+$fname2 = dirname(__FILE__) . '/' . basename(__FILE__, '.php') . '.2.phar.php';
+$pname2 = 'phar://' . $fname;
+$stub = '<?php echo "first stub\n"; __HALT_COMPILER(); ?>';
+$file = $stub;
+
+$files = array();
+$files['a'] = 'a';
+$files['b'] = 'b';
+$files['c'] = 'c';
+
+include 'phar_test.inc';
+
+$phar = new Phar($fname);
+$phar->convertToTar();
+var_dump($phar->isTar());
+copy($fname, $fname2);
+
+$phar = new Phar($fname2);
+var_dump($phar->isTar());
+?>
+===DONE===
+--CLEAN--
+<?php 
+unlink(dirname(__FILE__) . '/' . basename(__FILE__, '.clean.php') . '.phar.php');
+unlink(dirname(__FILE__) . '/' . basename(__FILE__, '.clean.php') . '.2.phar.php');
+__HALT_COMPILER();
+?>
+--EXPECT--
+bool(true)
+bool(true)
+===DONE===
diff --git a/ext/phar/tests/phar_convert_zip.phpt b/ext/phar/tests/phar_convert_zip.phpt
new file mode 100644 (file)
index 0000000..0f02ccb
--- /dev/null
@@ -0,0 +1,42 @@
+--TEST--
+Phar::convertToZip()
+--SKIPIF--
+<?php if (!extension_loaded("phar")) die("skip"); ?>
+--INI--
+phar.require_hash=0
+phar.readonly=0
+--FILE--
+<?php
+$fname = dirname(__FILE__) . '/' . basename(__FILE__, '.php') . '.phar.php';
+$pname = 'phar://' . $fname;
+$fname2 = dirname(__FILE__) . '/' . basename(__FILE__, '.php') . '.2.phar.php';
+$pname2 = 'phar://' . $fname;
+$stub = '<?php echo "first stub\n"; __HALT_COMPILER(); ?>';
+$file = $stub;
+
+$files = array();
+$files['a'] = 'a';
+$files['b'] = 'b';
+$files['c'] = 'c';
+
+include 'phar_test.inc';
+
+$phar = new Phar($fname);
+$phar->convertToZip();
+var_dump($phar->isZip());
+copy($fname, $fname2);
+
+$phar = new Phar($fname2);
+var_dump($phar->isZip());
+?>
+===DONE===
+--CLEAN--
+<?php 
+unlink(dirname(__FILE__) . '/' . basename(__FILE__, '.clean.php') . '.phar.php');
+unlink(dirname(__FILE__) . '/' . basename(__FILE__, '.clean.php') . '.2.phar.php');
+__HALT_COMPILER();
+?>
+--EXPECT--
+bool(true)
+bool(true)
+===DONE===
diff --git a/ext/phar/tests/tar/phar_convert_phar.phpt b/ext/phar/tests/tar/phar_convert_phar.phpt
new file mode 100644 (file)
index 0000000..7095f61
--- /dev/null
@@ -0,0 +1,37 @@
+--TEST--
+Phar::convertToPhar() from tar
+--SKIPIF--
+<?php if (!extension_loaded("phar")) die("skip"); ?>
+--INI--
+phar.require_hash=0
+phar.readonly=0
+--FILE--
+<?php
+$fname = dirname(__FILE__) . '/' . basename(__FILE__, '.php') . '.phar.php';
+$pname = 'phar://' . $fname;
+$fname2 = dirname(__FILE__) . '/' . basename(__FILE__, '.php') . '.2.phar.php';
+$pname2 = 'phar://' . $fname;
+
+$phar = new Phar($fname);
+$phar->convertToTar();
+var_dump($phar->isTar());
+$phar['a'] = 'hi there';
+$phar->convertToPhar();
+var_dump($phar->isPhar());
+copy($fname, $fname2);
+
+$phar = new Phar($fname2);
+var_dump($phar->isPhar());
+?>
+===DONE===
+--CLEAN--
+<?php 
+unlink(dirname(__FILE__) . '/' . basename(__FILE__, '.clean.php') . '.phar.php');
+unlink(dirname(__FILE__) . '/' . basename(__FILE__, '.clean.php') . '.2.phar.php');
+__HALT_COMPILER();
+?>
+--EXPECT--
+bool(true)
+bool(true)
+bool(true)
+===DONE===
diff --git a/ext/phar/tests/zip/phar_convert_phar.phpt b/ext/phar/tests/zip/phar_convert_phar.phpt
new file mode 100644 (file)
index 0000000..337682e
--- /dev/null
@@ -0,0 +1,37 @@
+--TEST--
+Phar::convertToPhar() from zip
+--SKIPIF--
+<?php if (!extension_loaded("phar")) die("skip"); ?>
+--INI--
+phar.require_hash=0
+phar.readonly=0
+--FILE--
+<?php
+$fname = dirname(__FILE__) . '/' . basename(__FILE__, '.php') . '.phar.php';
+$pname = 'phar://' . $fname;
+$fname2 = dirname(__FILE__) . '/' . basename(__FILE__, '.php') . '.2.phar.php';
+$pname2 = 'phar://' . $fname;
+
+$phar = new Phar($fname);
+$phar->convertToZip();
+var_dump($phar->isZip());
+$phar['a'] = 'hi there';
+$phar->convertToPhar();
+var_dump($phar->isPhar());
+copy($fname, $fname2);
+
+$phar = new Phar($fname2);
+var_dump($phar->isPhar());
+?>
+===DONE===
+--CLEAN--
+<?php 
+unlink(dirname(__FILE__) . '/' . basename(__FILE__, '.clean.php') . '.phar.php');
+unlink(dirname(__FILE__) . '/' . basename(__FILE__, '.clean.php') . '.2.phar.php');
+__HALT_COMPILER();
+?>
+--EXPECT--
+bool(true)
+bool(true)
+bool(true)
+===DONE===
index 236509f739b80444274e168dc16be29078f42af7..d6dc7815a9cd3673eff7c6ef3301cf8bff9fd285 100644 (file)
@@ -73,6 +73,7 @@ int phar_open_zipfile(char *fname, int fname_len, char *alias, int alias_len, ph
                                spprintf(error, 4096, "phar zip error: cannot open zip-based phar \"%s\"", fname);
                        } else {
                                if (!zip_error_to_str(tmp, tmp_len + 1, ziperror, ziperror)) {
+                                       efree(tmp);
                                        spprintf(error, 4096, "phar zip error: cannot open zip-based phar \"%s\"", fname);
                                } else {
                                        spprintf(error, 4096, "phar zip error: cannot open zip-based phar \"%s\": %s", fname, tmp);
@@ -330,7 +331,8 @@ static ssize_t phar_zip_source(void *state, void *data, size_t len, enum zip_sou
 
        switch (cmd) {
                case ZIP_SOURCE_OPEN :
-                       php_stream_seek(entry->fp, 0, SEEK_SET);
+                       /* offset_within_phar is only non-zero when converting from tar/phar-based to zip-based */
+                       php_stream_seek(entry->fp, entry->offset_within_phar, SEEK_SET);
                        return 0;
                case ZIP_SOURCE_READ :
                        read = php_stream_read(entry->fp, buf, len);
@@ -343,7 +345,8 @@ static ssize_t phar_zip_source(void *state, void *data, size_t len, enum zip_sou
                        return sizeof(struct zip_stat);
                case ZIP_SOURCE_FREE:
                        entry->is_modified = 0;
-                       if (entry->fp && entry->fp_refcount == 0) {
+                       /* phar->fp is set only if we're converting from a tar/phar-based archive */
+                       if (entry->fp && entry->fp_refcount == 0 && entry->fp != entry->phar->fp) {
                                php_stream_close(entry->fp);
                                entry->fp = NULL;
                        }