]> granicus.if.org Git - php/commitdiff
HUGE speed improvement, from 19 req/sec to 27 req/sec for phpMyAdmin - now speed...
authorGreg Beaver <cellog@php.net>
Sun, 15 Jun 2008 18:15:48 +0000 (18:15 +0000)
committerGreg Beaver <cellog@php.net>
Sun, 15 Jun 2008 18:15:48 +0000 (18:15 +0000)
ext/phar/dirstream.c
ext/phar/func_interceptors.c
ext/phar/phar.c
ext/phar/phar.phar
ext/phar/phar_internal.h
ext/phar/phar_object.c
ext/phar/stream.c
ext/phar/tar.c
ext/phar/util.c
ext/phar/zip.c

index 90f02fc2a1bf1ab636fd158d2ead278e996442f1..19ac6f9cef4c72d1fac7942e100735338b2cb2c7 100644 (file)
@@ -515,6 +515,7 @@ int phar_wrapper_mkdir(php_stream_wrapper *wrapper, char *url_from, int mode, in
                efree(error);
                return FAILURE;
        }
+       phar_add_virtual_dirs(phar, entry.filename, entry.filename_len TSRMLS_CC);
        return SUCCESS;
 }
 /* }}} */
@@ -589,12 +590,12 @@ int phar_wrapper_rmdir(php_stream_wrapper *wrapper, char *url, int options, php_
        entry->is_modified = 1;
        phar_flush(phar, 0, 0, 0, &error TSRMLS_CC);
        if (error) {
-               php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: cannot create directory \"%s\" in phar \"%s\", %s", entry->filename, phar->fname, error);
-               zend_hash_del(&phar->manifest, entry->filename, entry->filename_len);
+               php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "phar error: cannot remove directory \"%s\" in phar \"%s\", %s", entry->filename, phar->fname, error);
                php_url_free(resource);
                efree(error);
                return FAILURE;
        }
+       phar_delete_virtual_dirs(phar, resource->path + 1, strlen(resource->path) - 1 TSRMLS_CC);
        php_url_free(resource);
        return SUCCESS;
 }
index 709f3b0f751de3cdd651b7a00abec33bee1a54f4..621d87392a5d5359921eb4d100c859625f48bf2e 100644 (file)
@@ -629,6 +629,26 @@ void phar_file_stat(const char *filename, php_stat_len filename_length, int type
                        if (SUCCESS == zend_hash_find(&((*pphar)->manifest), entry, entry_len, (void **) &data)) {
                                efree(entry);
                                goto stat_entry;
+                       }
+                       if (SUCCESS == zend_hash_find(&((*pphar)->virtual_dirs), entry, entry_len, (void **) &data)) {
+                               efree(entry);
+                               efree(arch);
+                               if (IS_EXISTS_CHECK(type)) {
+                                       RETURN_TRUE;
+                               }
+                               sb.st_size = 0;
+                               sb.st_mode = 0777;
+                               sb.st_mode |= S_IFDIR; /* regular directory */
+#ifdef NETWARE
+                               sb.st_mtime.tv_sec = (*pphar)->max_timestamp;
+                               sb.st_atime.tv_sec = (*pphar)->max_timestamp;
+                               sb.st_ctime.tv_sec = (*pphar)->max_timestamp;
+#else
+                               sb.st_mtime = (*pphar)->max_timestamp;
+                               sb.st_atime = (*pphar)->max_timestamp;
+                               sb.st_ctime = (*pphar)->max_timestamp;
+#endif
+                               goto statme_baby;
                        } else {
                                char *save, *save2, *actual;
                                int save_len, save2_len, actual_len;
@@ -652,49 +672,36 @@ notfound:
                                        PHAR_G(cwd_len) = save_len;
                                        efree(entry);
                                        efree(save2);
+                                       if (IS_EXISTS_CHECK(type)) {
+                                               RETURN_TRUE;
+                                       }
                                        goto stat_entry;
-                               } else {
-                                       phar_archive_data *phar = *pphar;
-                                       phar_zstr key;
-                                       char *str_key;
-                                       uint keylen;
-                                       ulong unused;
-
+                               }
+                               if (SUCCESS == zend_hash_find(&((*pphar)->virtual_dirs), entry + 1, entry_len - 1, (void **) &data)) {
                                        PHAR_G(cwd) = save;
                                        PHAR_G(cwd_len) = save_len;
-                                       /* original not found either, this is possibly a directory relative to cwd */
-                                       zend_hash_internal_pointer_reset(&phar->manifest);
-                                       while (FAILURE != zend_hash_has_more_elements(&phar->manifest)) {
-                                               if (HASH_KEY_NON_EXISTANT !=
-                                                               zend_hash_get_current_key_ex(
-                                                                       &phar->manifest, &key, &keylen, &unused, 0, NULL)) {
-                                                       PHAR_STR(key, str_key);
-                                                       if (!memcmp(actual, str_key, actual_len)) {
-                                                               efree(save2);
-                                                               efree(entry);
-                                                               /* directory found, all dirs have the same stat */
-                                                               if (str_key[actual_len] == '/') {
-                                                                       sb.st_size = 0;
-                                                                       sb.st_mode = 0777;
-                                                                       sb.st_mode |= S_IFDIR; /* regular directory */
+                                       efree(entry);
+                                       efree(save2);
+                                       efree(arch);
+                                       if (IS_EXISTS_CHECK(type)) {
+                                               RETURN_TRUE;
+                                       }
+                                       sb.st_size = 0;
+                                       sb.st_mode = 0777;
+                                       sb.st_mode |= S_IFDIR; /* regular directory */
 #ifdef NETWARE
-                                                                       sb.st_mtime.tv_sec = phar->max_timestamp;
-                                                                       sb.st_atime.tv_sec = phar->max_timestamp;
-                                                                       sb.st_ctime.tv_sec = phar->max_timestamp;
+                                       sb.st_mtime.tv_sec = (*pphar)->max_timestamp;
+                                       sb.st_atime.tv_sec = (*pphar)->max_timestamp;
+                                       sb.st_ctime.tv_sec = (*pphar)->max_timestamp;
 #else
-                                                                       sb.st_mtime = phar->max_timestamp;
-                                                                       sb.st_atime = phar->max_timestamp;
-                                                                       sb.st_ctime = phar->max_timestamp;
+                                       sb.st_mtime = (*pphar)->max_timestamp;
+                                       sb.st_atime = (*pphar)->max_timestamp;
+                                       sb.st_ctime = (*pphar)->max_timestamp;
 #endif
-                                                                       goto statme_baby;
-                                                               }
-                                                       }
-                                               }
-                                               if (SUCCESS != zend_hash_move_forward(&phar->manifest)) {
-                                                       break;
-                                               }
-                                       }
+                                       goto statme_baby;
                                }
+                               PHAR_G(cwd) = save;
+                               PHAR_G(cwd_len) = save_len;
                                efree(entry);
                                efree(save2);
                                efree(arch);
index a7dc72d0fbf186939c265fdf35c54bced002be8d..cc0913f29e0a6d038117ca2d5914c3af918294a3 100644 (file)
@@ -210,6 +210,10 @@ void phar_destroy_phar_data(phar_archive_data *phar TSRMLS_DC) /* {{{ */
                zend_hash_destroy(&phar->mounted_dirs);
                phar->mounted_dirs.arBuckets = NULL;
        }
+       if (phar->virtual_dirs.arBuckets) {
+               zend_hash_destroy(&phar->virtual_dirs);
+               phar->virtual_dirs.arBuckets = NULL;
+       }
        if (phar->metadata) {
                if (phar->is_persistent) {
                        if (phar->metadata_len) {
@@ -408,13 +412,16 @@ void phar_entry_remove(phar_entry_data *idata, char **error TSRMLS_DC) /* {{{ */
                if (idata->fp && idata->fp != idata->phar->fp && idata->fp != idata->phar->ufp && idata->fp != idata->internal_file->fp) {
                        php_stream_close(idata->fp);
                }
+               phar_delete_virtual_dirs(idata->phar, idata->internal_file->filename, idata->internal_file->filename_len TSRMLS_CC);
                zend_hash_del(&idata->phar->manifest, idata->internal_file->filename, idata->internal_file->filename_len);
                idata->phar->refcount--;
                efree(idata);
        } else {
                idata->internal_file->is_deleted = 1;
+               phar_delete_virtual_dirs(idata->phar, idata->internal_file->filename, idata->internal_file->filename_len TSRMLS_CC);
                phar_entry_delref(idata TSRMLS_CC);
        }
+
        if (!phar->donotflush) {
                phar_flush(phar, 0, 0, 0, error TSRMLS_CC);
        }
@@ -959,6 +966,8 @@ static int phar_parse_pharfile(php_stream *fp, char *fname, int fname_len, char
                zend_get_hash_value, destroy_phar_manifest_entry, mydata->is_persistent);
        zend_hash_init(&mydata->mounted_dirs, sizeof(char *),
                zend_get_hash_value, NULL, mydata->is_persistent);
+       zend_hash_init(&mydata->virtual_dirs, sizeof(char *),
+               zend_get_hash_value, NULL, mydata->is_persistent);
        offset = halt_offset + manifest_len + 4;
        memset(&entry, 0, sizeof(phar_entry_info));
        entry.phar = mydata;
@@ -981,6 +990,7 @@ static int phar_parse_pharfile(php_stream *fp, char *fname, int fname_len, char
                } else {
                        entry.is_dir = 0;
                }
+               phar_add_virtual_dirs(mydata, buffer, entry.filename_len TSRMLS_CC);
                entry.filename = pestrndup(buffer, entry.filename_len, entry.is_persistent);
                buffer += entry.filename_len;
                PHAR_GET_32(buffer, entry.uncompressed_filesize);
@@ -1276,6 +1286,8 @@ int phar_create_or_parse_filename(char *fname, int fname_len, char *alias, int a
                zend_get_hash_value, destroy_phar_manifest_entry, 0);
        zend_hash_init(&mydata->mounted_dirs, sizeof(char *),
                zend_get_hash_value, NULL, 0);
+       zend_hash_init(&mydata->virtual_dirs, sizeof(char *),
+               zend_get_hash_value, NULL, mydata->is_persistent);
        mydata->fname_len = fname_len;
        snprintf(mydata->version, sizeof(mydata->version), "%s", PHP_PHAR_API_VERSION);
        mydata->is_temporary_alias = alias ? 0 : 1;
@@ -3242,6 +3254,7 @@ static void phar_update_cached_entry(void *data, void *argument) /* {{{ */
        }
        entry->metadata_str.c = 0;
        entry->filename = estrndup(entry->filename, entry->filename_len);
+       entry->is_persistent = 0;
        if (entry->metadata) {
                if (entry->metadata_len) {
                        /* assume success, we would have failed before */
index 834529deebd790b4eb28bf890ef7fa69fe22c685..7a2d36a62a3758ea95829d70b77e155fd00506fd 100755 (executable)
Binary files a/ext/phar/phar.phar and b/ext/phar/phar.phar differ
index f1c12a18df7827afd8f560e9d6cce78dc7110d41..ab8d6147faddc5c312f122b526bb7033fdeafbd4 100755 (executable)
@@ -283,6 +283,8 @@ struct _phar_archive_data {
        size_t                   internal_file_start;
        size_t                   halt_offset;
        HashTable                manifest;
+       /* hash of virtual directories, as in path/to/file.txt has path/to and path as virtual directories */
+       HashTable                virtual_dirs;
        /* hash of mounted directory paths */
        HashTable                mounted_dirs;
        php_uint32               flags;
@@ -428,6 +430,8 @@ char *phar_create_default_stub(const char *index_php, const char *web_index, siz
 char *phar_decompress_filter(phar_entry_info * entry, int return_unknown);
 char *phar_compress_filter(phar_entry_info * entry, int return_unknown);
 
+void phar_add_virtual_dirs(phar_archive_data *phar, char *filename, int filename_len TSRMLS_DC);
+void phar_delete_virtual_dirs(phar_archive_data *phar, char *filename, int filename_len TSRMLS_DC);
 int phar_mount_entry(phar_archive_data *phar, char *filename, int filename_len, char *path, int path_len TSRMLS_DC);
 char *phar_find_in_include_path(char *file, int file_len, phar_archive_data **pphar TSRMLS_DC);
 char *phar_fix_filepath(char *path, int *new_len, int use_cwd TSRMLS_DC);
index 092162b95515baf8f0bdaa73ade9b5948c765eec..7c099427db2e0c977a6f96e39fb4fc703057400f 100755 (executable)
@@ -2040,6 +2040,10 @@ static zval *phar_convert_to_other(phar_archive_data *source, int convert, char
 
        zend_hash_init(&(phar->manifest), sizeof(phar_entry_info),
                zend_get_hash_value, destroy_phar_manifest_entry, 0);
+       zend_hash_init(&phar->mounted_dirs, sizeof(char *),
+               zend_get_hash_value, NULL, 0);
+       zend_hash_init(&phar->virtual_dirs, sizeof(char *),
+               zend_get_hash_value, NULL, 0);
 
        phar->fp = php_stream_fopen_tmpfile();
        phar->fname = source->fname;
@@ -2101,12 +2105,15 @@ no_copy:
                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);
+               phar_add_virtual_dirs(phar, newentry.filename, newentry.filename_len TSRMLS_CC);
        }
 
        if ((ret = phar_rename_archive(phar, ext, 0 TSRMLS_CC))) {
                return ret;
        } else {
                zend_hash_destroy(&(phar->manifest));
+               zend_hash_destroy(&(phar->mounted_dirs));
+               zend_hash_destroy(&(phar->virtual_dirs));
                php_stream_close(phar->fp);
                efree(phar->fname);
                efree(phar);
index 32add212d10f09de3817b622fd87341f6a7bb048..a12f21c35ca19d1c2e108b198c0cbdd7cb95267a 100644 (file)
@@ -842,6 +842,9 @@ static int phar_wrapper_rename(php_stream_wrapper *wrapper, char *url_from, char
                        zend_hash_del(&(phar->manifest), entry->filename, strlen(entry->filename));
                        return 0;
                }
+
+               phar_add_virtual_dirs(phar, resource_to->path+1, strlen(resource_to->path)-1 TSRMLS_CC);
+               phar_delete_virtual_dirs(phar, resource_from->path+1, strlen(resource_from->path)-1 TSRMLS_CC);
        }
        php_url_free(resource_from);
        php_url_free(resource_to);
index b6ee4586bc93dc4eb742e3c3ac6da287ae2e38a9..8d4d0355f573f1cad618274f23b756df408b7355 100644 (file)
@@ -220,6 +220,8 @@ int phar_parse_tarfile(php_stream* fp, char *fname, int fname_len, char *alias,
                zend_get_hash_value, destroy_phar_manifest_entry, myphar->is_persistent);
        zend_hash_init(&myphar->mounted_dirs, sizeof(char *),
                zend_get_hash_value, NULL, myphar->is_persistent);
+       zend_hash_init(&myphar->virtual_dirs, sizeof(char *),
+               zend_get_hash_value, NULL, myphar->is_persistent);
        myphar->is_tar = 1;
        /* remember whether this entire phar was compressed with gz/bzip2 */
        myphar->flags = compression;
@@ -255,6 +257,8 @@ bail:
                                myphar->manifest.arBuckets = 0;
                                zend_hash_destroy(&myphar->mounted_dirs);
                                myphar->mounted_dirs.arBuckets = 0;
+                               zend_hash_destroy(&myphar->virtual_dirs);
+                               myphar->virtual_dirs.arBuckets = 0;
                                pefree(myphar, myphar->is_persistent);
                                return FAILURE;
                        }
@@ -296,6 +300,8 @@ bail:
                                        myphar->manifest.arBuckets = 0;
                                        zend_hash_destroy(&myphar->mounted_dirs);
                                        myphar->mounted_dirs.arBuckets = 0;
+                                       zend_hash_destroy(&myphar->virtual_dirs);
+                                       myphar->virtual_dirs.arBuckets = 0;
                                        pefree(myphar, myphar->is_persistent);
                                        return FAILURE;
                                }
@@ -310,6 +316,8 @@ bail:
                                myphar->manifest.arBuckets = 0;
                                zend_hash_destroy(&myphar->mounted_dirs);
                                myphar->mounted_dirs.arBuckets = 0;
+                               zend_hash_destroy(&myphar->virtual_dirs);
+                               myphar->virtual_dirs.arBuckets = 0;
                                pefree(myphar, myphar->is_persistent);
                                return FAILURE;
                        }
@@ -348,6 +356,7 @@ bail:
                                entry.filename_len--;
                        }
                }
+               phar_add_virtual_dirs(myphar, entry.filename, entry.filename_len TSRMLS_CC);
                if (sum1 != sum2) {
                        if (error) {
                                spprintf(error, 4096, "phar error: \"%s\" is a corrupted tar file (checksum mismatch of file \"%s\")", fname, entry.filename);
@@ -358,6 +367,8 @@ bail:
                        myphar->manifest.arBuckets = 0;
                        zend_hash_destroy(&myphar->mounted_dirs);
                        myphar->mounted_dirs.arBuckets = 0;
+                       zend_hash_destroy(&myphar->virtual_dirs);
+                       myphar->virtual_dirs.arBuckets = 0;
                        pefree(myphar, myphar->is_persistent);
                        return FAILURE;
                }
@@ -393,6 +404,8 @@ bail:
                                myphar->manifest.arBuckets = 0;
                                zend_hash_destroy(&myphar->mounted_dirs);
                                myphar->mounted_dirs.arBuckets = 0;
+                               zend_hash_destroy(&myphar->virtual_dirs);
+                               myphar->virtual_dirs.arBuckets = 0;
                                pefree(myphar, entry.is_persistent);
                                return FAILURE;
                        }
@@ -411,6 +424,8 @@ bail:
                                myphar->manifest.arBuckets = 0;
                                zend_hash_destroy(&myphar->mounted_dirs);
                                myphar->mounted_dirs.arBuckets = 0;
+                               zend_hash_destroy(&myphar->virtual_dirs);
+                               myphar->virtual_dirs.arBuckets = 0;
                                pefree(myphar, myphar->is_persistent);
                                return FAILURE;
                        }
@@ -427,6 +442,8 @@ bail:
                                myphar->manifest.arBuckets = 0;
                                zend_hash_destroy(&myphar->mounted_dirs);
                                myphar->mounted_dirs.arBuckets = 0;
+                               zend_hash_destroy(&myphar->virtual_dirs);
+                               myphar->virtual_dirs.arBuckets = 0;
                                pefree(myphar, myphar->is_persistent);
                                return FAILURE;
                        }
@@ -448,6 +465,8 @@ bail:
                                        myphar->manifest.arBuckets = 0;
                                        zend_hash_destroy(&myphar->mounted_dirs);
                                        myphar->mounted_dirs.arBuckets = 0;
+                                       zend_hash_destroy(&myphar->virtual_dirs);
+                                       myphar->virtual_dirs.arBuckets = 0;
                                        pefree(myphar, myphar->is_persistent);
                                        return FAILURE;
                                }
@@ -464,6 +483,8 @@ bail:
                                myphar->manifest.arBuckets = 0;
                                zend_hash_destroy(&myphar->mounted_dirs);
                                myphar->mounted_dirs.arBuckets = 0;
+                               zend_hash_destroy(&myphar->virtual_dirs);
+                               myphar->virtual_dirs.arBuckets = 0;
                                pefree(myphar, myphar->is_persistent);
                                return FAILURE;
                        }
@@ -481,6 +502,8 @@ bail:
                                myphar->manifest.arBuckets = 0;
                                zend_hash_destroy(&myphar->mounted_dirs);
                                myphar->mounted_dirs.arBuckets = 0;
+                               zend_hash_destroy(&myphar->virtual_dirs);
+                               myphar->virtual_dirs.arBuckets = 0;
                                pefree(myphar, myphar->is_persistent);
                                return FAILURE;
                        }
@@ -495,6 +518,8 @@ bail:
                        myphar->manifest.arBuckets = 0;
                        zend_hash_destroy(&myphar->mounted_dirs);
                        myphar->mounted_dirs.arBuckets = 0;
+                       zend_hash_destroy(&myphar->virtual_dirs);
+                       myphar->virtual_dirs.arBuckets = 0;
                        pefree(myphar, myphar->is_persistent);
                        return FAILURE;
                }
@@ -507,6 +532,8 @@ bail:
                myphar->manifest.arBuckets = 0;
                zend_hash_destroy(&myphar->mounted_dirs);
                myphar->mounted_dirs.arBuckets = 0;
+               zend_hash_destroy(&myphar->virtual_dirs);
+               myphar->virtual_dirs.arBuckets = 0;
                pefree(myphar, myphar->is_persistent);
                if (error) {
                        spprintf(error, 0, "tar-based phar \"%s\" does not have a signature", fname);
@@ -546,6 +573,8 @@ bail:
                myphar->manifest.arBuckets = 0;
                zend_hash_destroy(&myphar->mounted_dirs);
                myphar->mounted_dirs.arBuckets = 0;
+               zend_hash_destroy(&myphar->virtual_dirs);
+               myphar->virtual_dirs.arBuckets = 0;
                pefree(myphar, myphar->is_persistent);
                return FAILURE;
        }
index de3eda89421c68addb375d2dfb21c07b2561fa32..1f570956133835f15990e60bbd5f98c88bcfa145 100644 (file)
@@ -689,6 +689,7 @@ phar_entry_data *phar_get_or_create_entry_data(char *fname, int fname_len, char
        } else {
                etemp.flags = etemp.old_flags = PHAR_ENT_PERM_DEF_FILE;
        }
+       phar_add_virtual_dirs(phar, path, path_len TSRMLS_CC);
        etemp.is_modified = 1;
        etemp.timestamp = time(0);
        etemp.is_crc_checked = 1;
@@ -1217,7 +1218,20 @@ phar_entry_info *phar_get_entry_info_dir(phar_archive_data *phar, char *path, in
                        return NULL;
                }
                return entry;
-       } else if (phar->mounted_dirs.arBuckets && zend_hash_num_elements(&phar->mounted_dirs)) {
+       }
+       if (dir) {
+               if (SUCCESS == zend_hash_find(&phar->virtual_dirs, path, path_len, (void**)&entry)) {
+                       /* a file or directory exists in a sub-directory of this path */
+                       entry = (phar_entry_info *) ecalloc(1, sizeof(phar_entry_info));
+                       /* this next line tells PharFileInfo->__destruct() to efree the filename */
+                       entry->is_temp_dir = entry->is_dir = 1;
+                       entry->filename = (char *) estrndup(path, path_len + 1);
+                       entry->filename_len = path_len;
+                       entry->phar = phar;
+                       return entry;
+               }
+       }
+       if (phar->mounted_dirs.arBuckets && zend_hash_num_elements(&phar->mounted_dirs)) {
                phar_zstr key;
                char *str_key;
                ulong unused;
@@ -1290,50 +1304,6 @@ phar_entry_info *phar_get_entry_info_dir(phar_archive_data *phar, char *path, in
                        }
                }
        }
-       if (dir) {
-               /* try to find a directory */
-               HashTable *manifest;
-               phar_zstr key;
-               char *str_key;
-               uint keylen;
-               ulong unused;
-
-               if (!path_len) {
-                       path = "/";
-               }
-               manifest = &phar->manifest;
-               zend_hash_internal_pointer_reset(manifest);
-               while (FAILURE != zend_hash_has_more_elements(manifest)) {
-                       if (HASH_KEY_NON_EXISTANT == zend_hash_get_current_key_ex(manifest, &key, &keylen, &unused, 0, NULL)) {
-                               break;
-                       }
-
-                       PHAR_STR(key, str_key);
-
-                       if (0 != memcmp(str_key, path, path_len)) {
-                               /* entry in directory not found */
-                               if (SUCCESS != zend_hash_move_forward(manifest)) {
-                                       break;
-                               }
-                               continue;
-                       } else {
-                               if (str_key[path_len] != '/') {
-                                       if (SUCCESS != zend_hash_move_forward(manifest)) {
-                                               break;
-                                       }
-                                       continue;
-                               }
-                               /* found a file in this path */
-                               entry = (phar_entry_info *) ecalloc(1, sizeof(phar_entry_info));
-                               /* this next line tells PharFileInfo->__destruct() to efree the filename */
-                               entry->is_temp_dir = entry->is_dir = 1;
-                               entry->filename = (char *) estrndup(path, path_len + 1);
-                               entry->filename_len = path_len;
-                               entry->phar = phar;
-                               return entry;
-                       }
-               }
-       }
        return NULL;
 }
 /* }}} */
@@ -1846,3 +1816,55 @@ int phar_create_signature(phar_archive_data *phar, php_stream *fp, char **signat
        return SUCCESS;
 }
 /* }}} */
+
+/**
+ * add an empty element with a char * key to a hash table, avoiding duplicates
+ *
+ * This is used to get a unique listing of virtual directories within a phar,
+ * for iterating over opendir()ed phar directories.
+ */
+static int phar_add_empty(HashTable *ht, char *arKey, uint nKeyLength)  /* {{{ */
+{
+       void *dummy = (void *) 1;
+       if (SUCCESS == zend_hash_find(ht, arKey, nKeyLength, (void **)&dummy)) {
+               dummy++;
+       }
+
+       return zend_hash_update(ht, arKey, nKeyLength, &dummy, sizeof(void *), NULL);
+}
+/* }}} */
+
+void phar_add_virtual_dirs(phar_archive_data *phar, char *filename, int filename_len TSRMLS_DC) /* {{{ */
+{
+       char *s = filename;
+
+       /* we use filename_len - 1 to avoid adding a virtual dir for empty directory entries */
+       for (; s - filename < filename_len - 1; s++) {
+               if (*s == '/') {
+                       phar_add_empty(&phar->virtual_dirs, filename, s - filename);
+               }
+       }
+}
+/* }}} */
+
+void phar_delete_virtual_dirs(phar_archive_data *phar, char *filename, int filename_len TSRMLS_DC) /* {{{ */
+{
+       char *s = filename;
+
+       /* we use filename_len - 1 to avoid adding a virtual dir for empty directory entries */
+       for (; s - filename < filename_len - 1; s++) {
+               if (*s == '/') {
+                       void *dummy;
+                       if (FAILURE == zend_hash_find(&phar->virtual_dirs, filename, s - filename, (void **)&dummy)) {
+                               continue;
+                       }
+
+                       if (!--dummy) {
+                               zend_hash_del(&phar->virtual_dirs, filename, s - filename);
+                       } else {
+                               zend_hash_update(&phar->virtual_dirs, filename, s - filename, &dummy, sizeof(void *), NULL);
+                       }
+               }
+       }
+}
+/* }}} */
index dbeddfb2f925f0862c787ffa03626cf09e1e8762..3026c3b6f53e890817464dee0039d74aed4a45b8 100644 (file)
@@ -258,6 +258,8 @@ foundit:
                zend_get_hash_value, destroy_phar_manifest_entry, mydata->is_persistent);
        zend_hash_init(&mydata->mounted_dirs, sizeof(char *),
                zend_get_hash_value, NULL, mydata->is_persistent);
+       zend_hash_init(&mydata->virtual_dirs, sizeof(char *),
+               zend_get_hash_value, NULL, mydata->is_persistent);
        entry.phar = mydata;
        entry.is_zip = 1;
        entry.fp_type = PHAR_FP;
@@ -267,6 +269,8 @@ foundit:
                        mydata->manifest.arBuckets = 0; \
                        zend_hash_destroy(&mydata->mounted_dirs); \
                        mydata->mounted_dirs.arBuckets = 0; \
+                       zend_hash_destroy(&mydata->virtual_dirs); \
+                       mydata->virtual_dirs.arBuckets = 0; \
                        php_stream_close(fp); \
                        if (mydata->metadata) { \
                                zval_dtor(mydata->metadata); \
@@ -322,6 +326,7 @@ foundit:
                } else {
                        entry.is_dir = 0;
                }
+               phar_add_virtual_dirs(mydata, entry.filename, entry.filename_len TSRMLS_CC);
                if (PHAR_GET_16(zipentry.extra_len)) {
                        off_t loc = php_stream_tell(fp);
                        if (FAILURE == phar_zip_process_extra(fp, &entry, PHAR_GET_16(zipentry.extra_len) TSRMLS_CC)) {