示例#1
0
 /**
  * The most basic file transaction: add a single entry (file or directory) to
  * the archive.
  *
  * @param bool $isVirtual If true, the next parameter contains file data instead of a file name
  * @param string $sourceNameOrData Absolute file name to read data from or the file data itself is $isVirtual is true
  * @param string $targetName The (relative) file name under which to store the file in the archive
  * @return True on success, false otherwise
  * @since 1.2.1
  * @access protected
  * @abstract
  */
 function _addFile($isVirtual, &$sourceNameOrData, $targetName)
 {
     static $configuration;
     $cube =& JoomlapackCUBE::getInstance();
     // Note down the starting disk number for Split ZIP archives
     if ($this->_useSplitZIP) {
         $starting_disk_number_for_this_file = $this->_currentFragment - 1;
     } else {
         $starting_disk_number_for_this_file = 0;
     }
     if (!$configuration) {
         jpimport('models.registry', true);
         $configuration =& JoomlapackModelRegistry::getInstance();
     }
     // See if it's a directory
     $isDir = $isVirtual ? false : is_dir($sourceNameOrData);
     // See if it's a symlink (w/out dereference)
     $isSymlink = false;
     if ($this->_symlink_store_target) {
         $isSymlink = is_link($sourceNameOrData);
     }
     // Get real size before compression
     if ($isVirtual) {
         $fileSize = strlen($sourceNameOrData);
     } else {
         if ($isSymlink) {
             $fileSize = strlen(@readlink($sourceNameOrData));
         } else {
             $fileSize = $isDir ? 0 : @filesize($sourceNameOrData);
         }
     }
     // Get last modification time to store in archive
     $ftime = $isVirtual ? time() : @filemtime($sourceNameOrData);
     // Decide if we will compress
     if ($isDir || $isSymlink) {
         $compressionMethod = 0;
         // don't compress directories...
     } else {
         // Do we have plenty of memory left?
         $memLimit = ini_get("memory_limit");
         if ($memLimit == "" || $fileSize >= _JoomlapackPackerZIP_COMPRESSION_THRESHOLD) {
             // No memory limit, or over 1Mb files => always compress up to 1Mb files (otherwise it times out)
             $compressionMethod = $fileSize <= _JoomlapackPackerZIP_COMPRESSION_THRESHOLD ? 8 : 0;
         } elseif (function_exists("memory_get_usage")) {
             // PHP can report memory usage, see if there's enough available memory; Joomla! alone eats about 5-6Mb! This code is called on files <= 1Mb
             $memLimit = $this->_return_bytes($memLimit);
             $availableRAM = $memLimit - memory_get_usage();
             $compressionMethod = $availableRAM / 2.5 >= $fileSize ? 8 : 0;
         } else {
             // PHP can't report memory usage, compress only files up to 512Kb (conservative approach) and hope it doesn't break
             $compressionMethod = $fileSize <= 524288 ? 8 : 0;
         }
     }
     $compressionMethod = function_exists("gzcompress") ? $compressionMethod : 0;
     $storedName = $targetName;
     if ($isVirtual) {
         JoomlapackLogger::WriteLog(_JP_LOG_DEBUG, '  Virtual add:' . $storedName . ' (' . $fileSize . ') - ' . $compressionMethod);
     }
     /* "Local file header" segment. */
     $unc_len =& $fileSize;
     // File size
     if (!$isDir) {
         // Get CRC for regular files, not dirs
         if ($isVirtual) {
             $crc = crc32($sourceNameOrData);
         } else {
             $crcCalculator = new CRC32CalcClass();
             $crc = $crcCalculator->crc32_file($sourceNameOrData, $this->JoomlapackPackerZIP_CHUNK_SIZE);
             // This is supposed to be the fast way to calculate CRC32 of a (large) file.
             unset($crcCalculator);
             // If the file was unreadable, $crc will be false, so we skip the file
             if ($crc === false) {
                 $cube->addWarning('Could not calculate CRC32 for ' . $sourceNameOrData);
                 return false;
             }
         }
     } else {
         if ($isSymlink) {
             $crc = crc32(@readlink($sourceNameOrData));
         } else {
             // Dummy CRC for dirs
             $crc = 0;
             $storedName .= "/";
             $unc_len = 0;
         }
     }
     // If we have to compress, read the data in memory and compress it
     if ($compressionMethod == 8) {
         // Get uncompressed data
         if ($isVirtual) {
             $udata =& $sourceNameOrData;
         } else {
             if (function_exists("file_get_contents") && _JoomlapackPackerZIP_FORCE_FOPEN == false) {
                 $udata = @file_get_contents($sourceNameOrData);
                 // PHP > 4.3.0 saves us the trouble
             } else {
                 // Argh... the hard way!
                 $udatafp = @fopen($sourceNameOrData, "rb");
                 if (!($udatafp === false)) {
                     $udata = "";
                     while (!feof($udatafp)) {
                         if ($configuration->get("enableMySQLKeepalive", false)) {
                             list($usec, $sec) = explode(" ", microtime());
                             $endTime = (double) $usec + (double) $sec;
                             if ($endTime - $this->startTime > 0.5) {
                                 $this->startTime = $endTime;
                                 JoomlapackCUBETables::WriteVar('dummy', 1);
                             }
                         }
                         $udata .= fread($udatafp, JPPACK_CHUNK);
                     }
                     fclose($udatafp);
                 } else {
                     $udata = false;
                 }
             }
         }
         if ($udata === FALSE) {
             // Unreadable file, skip it. Normally, we should have exited on CRC code above
             $cube->addWarning(JText::sprintf('CUBE_WARN_UNREADABLEFILE', $sourceNameOrData));
             return false;
         } else {
             // Proceed with compression
             $zdata = @gzcompress($udata);
             if ($zdata === false) {
                 // If compression fails, let it behave like no compression was available
                 $c_len =& $unc_len;
                 $compressionMethod = 0;
             } else {
                 unset($udata);
                 $zdata = substr(substr($zdata, 0, strlen($zdata) - 4), 2);
                 $c_len = strlen($zdata);
             }
         }
     } else {
         $c_len = $unc_len;
     }
     /* Get the hex time. */
     $dtime = dechex($this->_unix2DosTime($ftime));
     if (strlen($dtime) < 8) {
         $dtime = "00000000";
     }
     $hexdtime = chr(hexdec($dtime[6] . $dtime[7])) . chr(hexdec($dtime[4] . $dtime[5])) . chr(hexdec($dtime[2] . $dtime[3])) . chr(hexdec($dtime[0] . $dtime[1]));
     // Get current data file size
     clearstatcache();
     $old_offset = @filesize($this->_dataFileName);
     // If it's a split ZIP file, we've got to make sure that the header can fit in the part
     if ($this->_useSplitZIP) {
         // Get header size, taking into account any extra header necessary
         $header_size = 30 + strlen($storedName);
         // Compare to free part space
         clearstatcache();
         $current_part_size = @filesize($this->_dataFileName);
         $free_space = $this->_fragmentSize - ($current_part_size === false ? 0 : $current_part_size);
         if ($free_space <= $header_size) {
             // Not enough space on current part, create new part
             if (!$this->_createNewPart()) {
                 $this->setError('Could not create new ZIP part file ' . basename($this->_dataFileName));
                 return false;
             }
         }
     }
     // Open data file for output
     $fp = @fopen($this->_dataFileName, "ab");
     if ($fp === false) {
         $this->setError("Could not open archive file {$this->_dataFileName} for append!");
         return false;
     }
     $this->_fwrite($fp, $this->_fileHeader);
     /* Begin creating the ZIP data. */
     if (!$isSymlink) {
         $this->_fwrite($fp, "");
         /* Version needed to extract. */
     } else {
         $this->_fwrite($fp, "\n");
         /* Version needed to extract. */
     }
     $this->_fwrite($fp, "");
     /* General purpose bit flag. */
     $this->_fwrite($fp, $compressionMethod == 8 ? "" : "");
     /* Compression method. */
     $this->_fwrite($fp, $hexdtime);
     /* Last modification time/date. */
     $this->_fwrite($fp, pack('V', $crc));
     /* CRC 32 information. */
     $this->_fwrite($fp, pack('V', $c_len));
     /* Compressed filesize. */
     $this->_fwrite($fp, pack('V', $unc_len));
     /* Uncompressed filesize. */
     $this->_fwrite($fp, pack('v', strlen($storedName)));
     /* Length of filename. */
     $this->_fwrite($fp, pack('v', 0));
     /* Extra field length. */
     $this->_fwrite($fp, $storedName);
     /* File name. */
     /* "File data" segment. */
     if ($compressionMethod == 8) {
         // Just dump the compressed data
         if (!$this->_useSplitZIP) {
             $this->_fwrite($fp, $zdata);
             if ($this->getError()) {
                 return;
             }
         } else {
             // Split ZIP. Check if we need to split the part in the middle of the data.
             clearstatcache();
             $current_part_size = @filesize($this->_dataFileName);
             $free_space = $this->_fragmentSize - ($current_part_size === false ? 0 : $current_part_size);
             if ($free_space >= strlen($zdata)) {
                 // Write in one part
                 $this->_fwrite($fp, $zdata);
                 if ($this->getError()) {
                     return;
                 }
             } else {
                 $bytes_left = strlen($zdata);
                 while ($bytes_left > 0) {
                     clearstatcache();
                     $current_part_size = @filesize($this->_dataFileName);
                     $free_space = $this->_fragmentSize - ($current_part_size === false ? 0 : $current_part_size);
                     // Split between parts - Write a part
                     $this->_fwrite($fp, $zdata, $free_space);
                     if ($this->getError()) {
                         return;
                     }
                     // Get the rest of the data
                     $bytes_left = strlen($zdata) - $free_space;
                     if ($bytes_left > 0) {
                         // Create new part
                         if (!$this->_createNewPart()) {
                             // Die if we couldn't create the new part
                             $this->setError('Could not create new ZIP part file ' . basename($this->_dataFileName));
                             return false;
                         } else {
                             // Close the old data file
                             fclose($fp);
                             // Open data file for output
                             $fp = @fopen($this->_dataFileName, "ab");
                             if ($fp === false) {
                                 $this->setError("Could not open archive file {$this->_dataFileName} for append!");
                                 return false;
                             }
                         }
                         $zdata = substr($zdata, -$bytes_left);
                     }
                 }
             }
         }
         unset($zdata);
     } elseif (!($isDir || $isSymlink)) {
         // Virtual file, just write the data!
         if ($isVirtual) {
             // Just dump the data
             if (!$this->_useSplitZIP) {
                 $this->_fwrite($fp, $sourceNameOrData);
                 if ($this->getError()) {
                     return;
                 }
             } else {
                 // Split ZIP. Check if we need to split the part in the middle of the data.
                 clearstatcache();
                 $current_part_size = @filesize($this->_dataFileName);
                 $free_space = $this->_fragmentSize - ($current_part_size === false ? 0 : $current_part_size);
                 if ($free_space >= strlen($sourceNameOrData)) {
                     // Write in one part
                     $this->_fwrite($fp, $sourceNameOrData);
                     if ($this->getError()) {
                         return;
                     }
                 } else {
                     $bytes_left = strlen($sourceNameOrData);
                     while ($bytes_left > 0) {
                         clearstatcache();
                         $current_part_size = @filesize($this->_dataFileName);
                         $free_space = $this->_fragmentSize - ($current_part_size === false ? 0 : $current_part_size);
                         // Split between parts - Write first part
                         $this->_fwrite($fp, $sourceNameOrData, $free_space);
                         if ($this->getError()) {
                             return;
                         }
                         // Get the rest of the data
                         $rest_size = strlen($sourceNameOrData) - $free_space;
                         if ($rest_size > 0) {
                             // Create new part if required
                             if (!$this->_createNewPart()) {
                                 // Die if we couldn't create the new part
                                 $this->setError('Could not create new ZIP part file ' . basename($this->_dataFileName));
                                 return false;
                             } else {
                                 // Close the old data file
                                 fclose($fp);
                                 // Open data file for output
                                 $fp = @fopen($this->_dataFileName, "ab");
                                 if ($fp === false) {
                                     $this->setError("Could not open archive file {$this->_dataFileName} for append!");
                                     return false;
                                 }
                             }
                             // Get the rest of the compressed data
                             $zdata = substr($sourceNameOrData, -$rest_size);
                         }
                         $bytes_left = $rest_size;
                     }
                 }
             }
         } else {
             // Copy the file contents, ignore directories
             $zdatafp = @fopen($sourceNameOrData, "rb");
             if ($zdatafp === FALSE) {
                 $cube->addWarning(JText::sprintf('CUBE_WARN_UNREADABLEFILE', $sourceNameOrData));
                 return false;
             } else {
                 if (!$this->_useSplitZIP) {
                     // For non Split ZIP, just dump the file very fast
                     while (!feof($zdatafp)) {
                         $zdata = fread($zdatafp, JPPACK_CHUNK);
                         $this->_fwrite($fp, $zdata);
                         if ($this->getError()) {
                             return;
                         }
                     }
                 } else {
                     // Split ZIP - Do we have enough space to host the whole file?
                     clearstatcache();
                     $current_part_size = @filesize($this->_dataFileName);
                     $free_space = $this->_fragmentSize - ($current_part_size === false ? 0 : $current_part_size);
                     if ($free_space >= $unc_len) {
                         // Yes, it will fit inside this part, do quick copy
                         while (!feof($zdatafp)) {
                             $zdata = fread($zdatafp, JPPACK_CHUNK);
                             $this->_fwrite($fp, $zdata);
                             if ($this->getError()) {
                                 return;
                             }
                         }
                     } else {
                         // No, we'll have to split between parts. We'll loop until we run
                         // out of space.
                         $bytes_left = $unc_len;
                         while ($bytes_left > 0 && !feof($zdatafp)) {
                             // No, we'll have to split between parts. Write the first part
                             // Find optimal chunk size
                             clearstatcache();
                             $current_part_size = @filesize($this->_dataFileName);
                             $free_space = $this->_fragmentSize - ($current_part_size === false ? 0 : $current_part_size);
                             $chunk_size_primary = min(JPPACK_CHUNK, $free_space);
                             if ($chunk_size_primary <= 0) {
                                 $chunk_size_primary = max(JPPACK_CHUNK, $free_space);
                             }
                             // Calculate if we have to read some more data (smaller chunk size)
                             // and how many times we must read w/ the primary chunk size
                             $chunk_size_secondary = $free_space % $chunk_size_primary;
                             $loop_times = ($free_space - $chunk_size_secondary) / $chunk_size_primary;
                             // Read and write with the primary chunk size
                             for ($i = 1; $i <= $loop_times; $i++) {
                                 $zdata = fread($zdatafp, $chunk_size_primary);
                                 $this->_fwrite($fp, $zdata);
                                 if ($this->getError()) {
                                     return;
                                 }
                             }
                             // Read and write w/ secondary chunk size, if non-zero
                             if ($chunk_size_secondary > 0) {
                                 $zdata = fread($zdatafp, $chunk_size_secondary);
                                 $this->_fwrite($fp, $zdata);
                                 if ($this->getError()) {
                                     return;
                                 }
                             }
                             // Create new ZIP part, but only if we'll have more data to write
                             if (!feof($zdatafp)) {
                                 // Create new ZIP part
                                 if (!$this->_createNewPart()) {
                                     // Die if we couldn't create the new part
                                     $this->setError('Could not create new ZIP part file ' . basename($this->_dataFileName));
                                     return false;
                                 } else {
                                     // Close the old data file
                                     fclose($fp);
                                     // Open data file for output
                                     $fp = @fopen($this->_dataFileName, "ab");
                                     if ($fp === false) {
                                         $this->setError("Could not open archive file {$this->_dataFileName} for append!");
                                         return false;
                                     }
                                 }
                             }
                         }
                         // end while
                     }
                 }
                 fclose($zdatafp);
             }
         }
     } elseif ($isSymlink) {
         $this->_fwrite($fp, @readlink($sourceNameOrData));
     }
     // Done with data file.
     fclose($fp);
     // Open the central directory file for append
     $fp = @fopen($this->_ctrlDirFileName, "ab");
     if ($fp === false) {
         $this->setError("Could not open Central Directory temporary file for append!");
         return false;
     }
     $this->_fwrite($fp, $this->_ctrlDirHeader);
     if (!$isSymlink) {
         $this->_fwrite($fp, "");
         /* Version made by. */
         $this->_fwrite($fp, "");
         /* Version needed to extract */
         $this->_fwrite($fp, "");
         /* General purpose bit flag */
         $this->_fwrite($fp, $compressionMethod == 8 ? "" : "");
         /* Compression method. */
     } else {
         // Symlinks get special treatment
         $this->_fwrite($fp, "");
         /* Version made by. */
         $this->_fwrite($fp, "\n");
         /* Version needed to extract */
         $this->_fwrite($fp, "");
         /* General purpose bit flag */
         $this->_fwrite($fp, "");
         /* Compression method. */
     }
     $this->_fwrite($fp, $hexdtime);
     /* Last mod time/date. */
     $this->_fwrite($fp, pack('V', $crc));
     /* CRC 32 information. */
     $this->_fwrite($fp, pack('V', $c_len));
     /* Compressed filesize. */
     $this->_fwrite($fp, pack('V', $unc_len));
     /* Uncompressed filesize. */
     $this->_fwrite($fp, pack('v', strlen($storedName)));
     /* Length of filename. */
     $this->_fwrite($fp, pack('v', 0));
     /* Extra field length. */
     $this->_fwrite($fp, pack('v', 0));
     /* File comment length. */
     $this->_fwrite($fp, pack('v', $starting_disk_number_for_this_file));
     /* Disk number start. */
     $this->_fwrite($fp, pack('v', 0));
     /* Internal file attributes. */
     if (!$isSymlink) {
         $this->_fwrite($fp, pack('V', $isDir ? 0x41ff0010 : 0.0));
         /* External file attributes -   'archive' bit set. */
     } else {
         // For SymLinks we store UNIX file attributes
         $this->_fwrite($fp, " €ÿ¡");
         /* External file attributes for Symlink. */
     }
     $this->_fwrite($fp, pack('V', $old_offset));
     /* Relative offset of local header. */
     $this->_fwrite($fp, $storedName);
     /* File name. */
     /* Optional extra field, file comment goes here. */
     // Finished with Central Directory
     fclose($fp);
     // Finaly, increase the file counter by one
     $this->_totalFileEntries++;
     // ... and return TRUE = success
     return TRUE;
 }
示例#2
0
 /**
  * The most basic file transaction: add a single entry (file or directory) to
  * the archive.
  *
  * @param bool $isVirtual If true, the next parameter contains file data instead of a file name
  * @param string $sourceNameOrData Absolute file name to read data from or the file data itself is $isVirtual is true
  * @param string $targetName The (relative) file name under which to store the file in the archive
  * @return True on success, false otherwise
  * @since 1.2.1
  * @access protected
  * @abstract 
  */
 function _addFile($isVirtual, &$sourceNameOrData, $targetName)
 {
     // See if it's a directory
     $isDir = $isVirtual ? false : is_dir($sourceNameOrData);
     // Get real size before compression
     if ($isVirtual) {
         $fileSize = strlen($sourceNameOrData);
     } else {
         $fileSize = $isDir ? 0 : filesize($sourceNameOrData);
     }
     // Get last modification time to store in archive
     $ftime = $isVirtual ? time() : filemtime($sourceNameOrData);
     // Decide if we will compress
     if ($isDir) {
         $compressionMethod = 0;
         // don't compress directories...
     } else {
         // Do we have plenty of memory left?
         $memLimit = ini_get("memory_limit");
         if ($memLimit == "" || $fileSize >= _JoomlapackPackerZIP_COMPRESSION_THRESHOLD) {
             // No memory limit, or over 1Mb files => always compress up to 1Mb files (otherwise it times out)
             $compressionMethod = $fileSize <= _JoomlapackPackerZIP_COMPRESSION_THRESHOLD ? 8 : 0;
         } elseif (function_exists("memory_get_usage")) {
             // PHP can report memory usage, see if there's enough available memory; Joomla! alone eats about 5-6Mb! This code is called on files <= 1Mb
             $memLimit = $this->_return_bytes($memLimit);
             $availableRAM = $memLimit - memory_get_usage();
             $compressionMethod = $availableRAM / 2.5 >= $fileSize ? 8 : 0;
         } else {
             // PHP can't report memory usage, compress only files up to 512Kb (conservative approach) and hope it doesn't break
             $compressionMethod = $fileSize <= 524288 ? 8 : 0;
         }
     }
     $compressionMethod = function_exists("gzcompress") ? $compressionMethod : 0;
     $storedName = $targetName;
     if ($isVirtual) {
         JoomlapackLogger::WriteLog(_JP_LOG_DEBUG, '  Virtual add:' . $storedName . ' (' . $fileSize . ') - ' . $compressionMethod);
     }
     /* "Local file header" segment. */
     $unc_len =& $fileSize;
     // File size
     if (!$isDir) {
         // Get CRC for regular files, not dirs
         if ($isVirtual) {
             $crc = crc32($sourceNameOrData);
         } else {
             $crcCalculator = new CRC32CalcClass();
             $crc = $crcCalculator->crc32_file($sourceNameOrData, $this->JoomlapackPackerZIP_CHUNK_SIZE);
             // This is supposed to be the fast way to calculate CRC32 of a (large) file.
             unset($crcCalculator);
             // If the file was unreadable, $crc will be false, so we skip the file
             if ($crc === false) {
                 $this->setWarning('Could not calculate CRC32 for ' . $sourceNameOrData);
                 return false;
             }
         }
     } else {
         // Dummy CRC for dirs
         $crc = 0;
         $storedName .= "/";
         $unc_len = 0;
     }
     // If we have to compress, read the data in memory and compress it
     if ($compressionMethod == 8) {
         // Get uncompressed data
         if ($isVirtual) {
             $udata =& $sourceNameOrData;
         } else {
             if (function_exists("file_get_contents") && _JoomlapackPackerZIP_FORCE_FOPEN == false) {
                 $udata = @file_get_contents($sourceNameOrData);
                 // PHP > 4.3.0 saves us the trouble
             } else {
                 // Argh... the hard way!
                 $udatafp = @fopen($sourceNameOrData, "rb");
                 if (!($udatafp === false)) {
                     $udata = "";
                     while (!feof($udatafp)) {
                         $udata .= fread($udatafp, 524288);
                     }
                     fclose($udatafp);
                 } else {
                     $udata = false;
                 }
             }
         }
         if ($udata === FALSE) {
             // Unreadable file, skip it. Normally, we should have exited on CRC code above
             $this->setWarning('Unreadable file ' . $sourceNameOrData);
             return false;
         } else {
             // Proceed with compression
             $zdata = @gzcompress($udata);
             if ($zdata === false) {
                 // If compression fails, let it behave like no compression was available
                 $c_len =& $unc_len;
                 $compressionMethod = 0;
             } else {
                 unset($udata);
                 $zdata = substr(substr($zdata, 0, strlen($zdata) - 4), 2);
                 $c_len = strlen($zdata);
             }
         }
     } else {
         $c_len = $unc_len;
     }
     /* Get the hex time. */
     $dtime = dechex($this->_unix2DosTime($ftime));
     $hexdtime = chr(hexdec($dtime[6] . $dtime[7])) . chr(hexdec($dtime[4] . $dtime[5])) . chr(hexdec($dtime[2] . $dtime[3])) . chr(hexdec($dtime[0] . $dtime[1]));
     // Get current data file size
     clearstatcache();
     $old_offset = filesize($this->_dataFileName);
     // Open data file for output
     $fp = @fopen($this->_dataFileName, "ab");
     if ($fp === false) {
         $this->setError("Could not open archive file {$this->_dataFileName} for append!");
         return false;
     }
     $this->_fwrite($fp, $this->_fileHeader);
     /* Begin creating the ZIP data. */
     $this->_fwrite($fp, "");
     /* Version needed to extract. */
     $this->_fwrite($fp, "");
     /* General purpose bit flag. */
     $this->_fwrite($fp, $compressionMethod == 8 ? "" : "");
     /* Compression method. */
     $this->_fwrite($fp, $hexdtime);
     /* Last modification time/date. */
     $this->_fwrite($fp, pack('V', $crc));
     /* CRC 32 information. */
     $this->_fwrite($fp, pack('V', $c_len));
     /* Compressed filesize. */
     $this->_fwrite($fp, pack('V', $unc_len));
     /* Uncompressed filesize. */
     $this->_fwrite($fp, pack('v', strlen($storedName)));
     /* Length of filename. */
     $this->_fwrite($fp, pack('v', 0));
     /* Extra field length. */
     $this->_fwrite($fp, $storedName);
     /* File name. */
     /* "File data" segment. */
     if ($compressionMethod == 8) {
         // Just dump the compressed data
         $this->_fwrite($fp, $zdata);
         if ($this->hasError()) {
             return;
         }
         unset($zdata);
     } elseif (!$isDir) {
         if ($isVirtual) {
             $this->_fwrite($fp, $sourceNameOrData);
             if ($this->hasError()) {
                 return;
             }
         } else {
             // Copy the file contents, ignore directories
             $zdatafp = @fopen($sourceNameOrData, "rb");
             while (!feof($zdatafp)) {
                 $zdata = fread($zdatafp, 524288);
                 $this->_fwrite($fp, $zdata);
                 if ($this->hasError()) {
                     return;
                 }
             }
             fclose($zdatafp);
         }
     }
     // Done with data file.
     fclose($fp);
     // Open the central directory file for append
     $fp = @fopen($this->_ctrlDirFileName, "ab");
     if ($fp === false) {
         $this->setError("Could not open Central Directory temporary file for append!");
         return false;
     }
     $this->_fwrite($fp, $this->_ctrlDirHeader);
     $this->_fwrite($fp, "");
     /* Version made by. */
     $this->_fwrite($fp, "");
     /* Version needed to extract */
     $this->_fwrite($fp, "");
     /* General purpose bit flag */
     $this->_fwrite($fp, $compressionMethod == 8 ? "" : "");
     /* Compression method. */
     $this->_fwrite($fp, $hexdtime);
     /* Last mod time/date. */
     $this->_fwrite($fp, pack('V', $crc));
     /* CRC 32 information. */
     $this->_fwrite($fp, pack('V', $c_len));
     /* Compressed filesize. */
     $this->_fwrite($fp, pack('V', $unc_len));
     /* Uncompressed filesize. */
     $this->_fwrite($fp, pack('v', strlen($storedName)));
     /* Length of filename. */
     $this->_fwrite($fp, pack('v', 0));
     /* Extra field length. */
     $this->_fwrite($fp, pack('v', 0));
     /* File comment length. */
     $this->_fwrite($fp, pack('v', 0));
     /* Disk number start. */
     $this->_fwrite($fp, pack('v', 0));
     /* Internal file attributes. */
     $this->_fwrite($fp, pack('V', $isDir ? 0x41ff0010 : 0xfe49ffe0));
     /* External file attributes -   'archive' bit set. */
     $this->_fwrite($fp, pack('V', $old_offset));
     /* Relative offset of local
        header. */
     $this->_fwrite($fp, $storedName);
     /* File name. */
     /* Optional extra field, file comment goes here. */
     // Finished with Central Directory
     fclose($fp);
     // Finaly, increase the file counter by one
     $this->_totalFileEntries++;
     // ... and return TRUE = success
     return TRUE;
 }