title: How to create a streamed zip archive in PHP suitable for OS X's Archive Utility?
body: <p>I'm trying to integrate an on-demand backup system for a relatively small web-application which allows the user to download a zip file of all the files on the server and a database dump. The zip file is streamed to the user, instead of packaged as a whole in a temporary directory on the server.</p>
  
  <p>I'm using a third-party PHP class, called ZipStream, to accomplish this. The class is hosted <a href="https://github.com/Grandt/PHPZip" rel="nofollow noreferrer">here</a> and the specific class file <a href="https://github.com/Grandt/PHPZip/blob/master/ZipStream.php" rel="nofollow noreferrer">can be viewed here</a>. I've chosen this version (there is <a href="http://pablotron.org/software/zipstream-php/" rel="nofollow noreferrer">another, older one</a>) as it has support for including directories. Also, the setup should work on servers without access to shell commands via <code>shell_exec()</code> or <code>passthru()</code>.</p>
  
  <p>My simple test setup is working and I can extract the resulting streamed zip using the <code>unzip</code> command from the console or using <code>The Unarchiver.app</code>. OS X's Archive Utility (Mac OS X 10.6 and very likely older and later versions) is causing problems however as it fails to extract the archive with the following error:</p>
  
  <pre><code>Unable to unarchive "test.zip" into "test". (Error 1 - Operation not permitted.)
  </code></pre>
  
  <p>The actual error logged is:</p>
  
  <pre><code>ditto: Couldn't read pkzip signature.
  </code></pre>
  
  <p>I've found some possible explanations regarding this problem, <a href="http://ocportal.com/site/news/view/chris_grahams_blog/problem_between_the_mac_2.htm" rel="nofollow noreferrer">here</a> and <a href="https://stackoverflow.com/questions/5573211/dynamically-created-zip-files-by-zipstream-in-php-wont-open-in-osx">here on StackOverflow</a> and I would like to patch the ZipStream class to adhere to the Zip specification so that Archive Utility will expand the archive as expected.</p>
  
  <p><strike>The "Version needed to extract" (stored in the <code>ATTR_VERSION_TO_EXTRACT</code> constant) in the ZipStream class is: <code>x0Ax00</code>, but I'm not sure if that notation differs from the standard <code>0x000A</code> mentioned in the aforementioned question.</strike></p>
  
  <p>The problem seems related to the directories added to the zip file. A zip file only containing a text file doesn't cause issues using Archive Utility.</p>
  
  <p>As this moves alongside the fringes of the realm of PHP I'm not sure how to edit the script with the correct hex strings or how to appropriate the Zend solution to this specific script.</p>
  
  <p>I've uploaded a generated zip here: <a href="http://dl.dropbox.com/u/6887873/test.zip" rel="nofollow noreferrer">http://dl.dropbox.com/u/6887873/test.zip</a></p>
  
  <p>The code to generate is a trimmed down version of the example code:</p>
  
  <pre><code>$extime = ini_get('max_execution_time');
  ini_set('max_execution_time', 600);
  
  include_once("ZipStream.php");
  
  $fileTime = date("D, d M Y H:i:s T");
  
  $chapter1 = "Chapter 1\n"
  . "Lorem ipsum\n"
  . "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec magna lorem, mattis sit amet porta vitae, consectetur ut eros. Nullam id mattis lacus. In eget neque magna, congue imperdiet nulla. Aenean erat lacus, imperdiet a adipiscing non, dignissim eget felis. Nulla facilisi. Vivamus sit amet lorem eget mauris dictum pharetra. In mauris nulla, placerat a accumsan ac, mollis sit amet ligula. Donec eget facilisis dui. Cras elit quam, imperdiet at malesuada vitae, luctus id orci. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Pellentesque eu libero in leo ultrices tristique. Etiam quis ornare massa. Donec in velit leo. Sed eu ante tortor.\n";
  
  $zip = new ZipStream("test.zip");
  
  $zip-&gt;addFile("Hello World!\r\n", "Hello.txt");
  
  $zip-&gt;addDirectory("files/");
  $zip-&gt;addDirectoryContent("../images", "files/testing/");
  
  $zip-&gt;finalize();
  </code></pre>
  
  <p><strong>EDIT:</strong></p>
  
  <p>I think I've narrowed the problem down a bit more. It doesn't seem the hex strings actually make a difference. An generated zip containing only a text file is expanded without errors using Archive Utility, so the corruption seems to occur when adding directories, specifically when calling <code>addDirectoryContent()</code>, which in turn calls <code>addLargeFile()</code> of the ZipStream class. Directories are added correctly and don't result in an invalid file, as soon as <code>addDirectoryContent()</code> is added to the mix Archive Utility fails to extract the files, outputting the errors above.</p>