See Release Notes
Long Term Support Release
Differences Between: [Versions 310 and 401] [Versions 39 and 401]
1 <?php 2 3 namespace Box\Spout\Writer\ODS\Helper; 4 5 use Box\Spout\Writer\Common\Entity\Worksheet; 6 use Box\Spout\Writer\Common\Helper\FileSystemWithRootFolderHelperInterface; 7 use Box\Spout\Writer\Common\Helper\ZipHelper; 8 use Box\Spout\Writer\ODS\Manager\Style\StyleManager; 9 use Box\Spout\Writer\ODS\Manager\WorksheetManager; 10 11 /** 12 * Class FileSystemHelper 13 * This class provides helper functions to help with the file system operations 14 * like files/folders creation & deletion for ODS files 15 */ 16 class FileSystemHelper extends \Box\Spout\Common\Helper\FileSystemHelper implements FileSystemWithRootFolderHelperInterface 17 { 18 const APP_NAME = 'Spout'; 19 const MIMETYPE = 'application/vnd.oasis.opendocument.spreadsheet'; 20 21 const META_INF_FOLDER_NAME = 'META-INF'; 22 const SHEETS_CONTENT_TEMP_FOLDER_NAME = 'worksheets-temp'; 23 24 const MANIFEST_XML_FILE_NAME = 'manifest.xml'; 25 const CONTENT_XML_FILE_NAME = 'content.xml'; 26 const META_XML_FILE_NAME = 'meta.xml'; 27 const MIMETYPE_FILE_NAME = 'mimetype'; 28 const STYLES_XML_FILE_NAME = 'styles.xml'; 29 30 /** @var ZipHelper Helper to perform tasks with Zip archive */ 31 private $zipHelper; 32 33 /** @var string Path to the root folder inside the temp folder where the files to create the ODS will be stored */ 34 protected $rootFolder; 35 36 /** @var string Path to the "META-INF" folder inside the root folder */ 37 protected $metaInfFolder; 38 39 /** @var string Path to the temp folder, inside the root folder, where specific sheets content will be written to */ 40 protected $sheetsContentTempFolder; 41 42 /** 43 * @param string $baseFolderPath The path of the base folder where all the I/O can occur 44 * @param ZipHelper $zipHelper Helper to perform tasks with Zip archive 45 */ 46 public function __construct($baseFolderPath, $zipHelper) 47 { 48 parent::__construct($baseFolderPath); 49 $this->zipHelper = $zipHelper; 50 } 51 52 /** 53 * @return string 54 */ 55 public function getRootFolder() 56 { 57 return $this->rootFolder; 58 } 59 60 /** 61 * @return string 62 */ 63 public function getSheetsContentTempFolder() 64 { 65 return $this->sheetsContentTempFolder; 66 } 67 68 /** 69 * Creates all the folders needed to create a ODS file, as well as the files that won't change. 70 * 71 * @throws \Box\Spout\Common\Exception\IOException If unable to create at least one of the base folders 72 * @return void 73 */ 74 public function createBaseFilesAndFolders() 75 { 76 $this 77 ->createRootFolder() 78 ->createMetaInfoFolderAndFile() 79 ->createSheetsContentTempFolder() 80 ->createMetaFile() 81 ->createMimetypeFile(); 82 } 83 84 /** 85 * Creates the folder that will be used as root 86 * 87 * @throws \Box\Spout\Common\Exception\IOException If unable to create the folder 88 * @return FileSystemHelper 89 */ 90 protected function createRootFolder() 91 { 92 $this->rootFolder = $this->createFolder($this->baseFolderRealPath, \uniqid('ods')); 93 94 return $this; 95 } 96 97 /** 98 * Creates the "META-INF" folder under the root folder as well as the "manifest.xml" file in it 99 * 100 * @throws \Box\Spout\Common\Exception\IOException If unable to create the folder or the "manifest.xml" file 101 * @return FileSystemHelper 102 */ 103 protected function createMetaInfoFolderAndFile() 104 { 105 $this->metaInfFolder = $this->createFolder($this->rootFolder, self::META_INF_FOLDER_NAME); 106 107 $this->createManifestFile(); 108 109 return $this; 110 } 111 112 /** 113 * Creates the "manifest.xml" file under the "META-INF" folder (under root) 114 * 115 * @throws \Box\Spout\Common\Exception\IOException If unable to create the file 116 * @return FileSystemHelper 117 */ 118 protected function createManifestFile() 119 { 120 $manifestXmlFileContents = <<<'EOD' 121 <?xml version="1.0" encoding="UTF-8"?> 122 <manifest:manifest xmlns:manifest="urn:oasis:names:tc:opendocument:xmlns:manifest:1.0" manifest:version="1.2"> 123 <manifest:file-entry manifest:full-path="/" manifest:media-type="application/vnd.oasis.opendocument.spreadsheet"/> 124 <manifest:file-entry manifest:full-path="styles.xml" manifest:media-type="text/xml"/> 125 <manifest:file-entry manifest:full-path="content.xml" manifest:media-type="text/xml"/> 126 <manifest:file-entry manifest:full-path="meta.xml" manifest:media-type="text/xml"/> 127 </manifest:manifest> 128 EOD; 129 130 $this->createFileWithContents($this->metaInfFolder, self::MANIFEST_XML_FILE_NAME, $manifestXmlFileContents); 131 132 return $this; 133 } 134 135 /** 136 * Creates the temp folder where specific sheets content will be written to. 137 * This folder is not part of the final ODS file and is only used to be able to jump between sheets. 138 * 139 * @throws \Box\Spout\Common\Exception\IOException If unable to create the folder 140 * @return FileSystemHelper 141 */ 142 protected function createSheetsContentTempFolder() 143 { 144 $this->sheetsContentTempFolder = $this->createFolder($this->rootFolder, self::SHEETS_CONTENT_TEMP_FOLDER_NAME); 145 146 return $this; 147 } 148 149 /** 150 * Creates the "meta.xml" file under the root folder 151 * 152 * @throws \Box\Spout\Common\Exception\IOException If unable to create the file 153 * @return FileSystemHelper 154 */ 155 protected function createMetaFile() 156 { 157 $appName = self::APP_NAME; 158 $createdDate = (new \DateTime())->format(\DateTime::W3C); 159 160 $metaXmlFileContents = <<<EOD 161 <?xml version="1.0" encoding="UTF-8" standalone="yes"?> 162 <office:document-meta office:version="1.2" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:meta="urn:oasis:names:tc:opendocument:xmlns:meta:1.0" xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:xlink="http://www.w3.org/1999/xlink"> 163 <office:meta> 164 <dc:creator>$appName</dc:creator> 165 <meta:creation-date>$createdDate</meta:creation-date> 166 <dc:date>$createdDate</dc:date> 167 </office:meta> 168 </office:document-meta> 169 EOD; 170 171 $this->createFileWithContents($this->rootFolder, self::META_XML_FILE_NAME, $metaXmlFileContents); 172 173 return $this; 174 } 175 176 /** 177 * Creates the "mimetype" file under the root folder 178 * 179 * @throws \Box\Spout\Common\Exception\IOException If unable to create the file 180 * @return FileSystemHelper 181 */ 182 protected function createMimetypeFile() 183 { 184 $this->createFileWithContents($this->rootFolder, self::MIMETYPE_FILE_NAME, self::MIMETYPE); 185 186 return $this; 187 } 188 189 /** 190 * Creates the "content.xml" file under the root folder 191 * 192 * @param WorksheetManager $worksheetManager 193 * @param StyleManager $styleManager 194 * @param Worksheet[] $worksheets 195 * @return FileSystemHelper 196 */ 197 public function createContentFile($worksheetManager, $styleManager, $worksheets) 198 { 199 $contentXmlFileContents = <<<'EOD' 200 <?xml version="1.0" encoding="UTF-8" standalone="yes"?> 201 <office:document-content office:version="1.2" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:calcext="urn:org:documentfoundation:names:experimental:calc:xmlns:calcext:1.0" xmlns:draw="urn:oasis:names:tc:opendocument:xmlns:drawing:1.0" xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" xmlns:msoxl="http://schemas.microsoft.com/office/excel/formula" xmlns:number="urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0" xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" xmlns:svg="urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0" xmlns:table="urn:oasis:names:tc:opendocument:xmlns:table:1.0" xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" xmlns:xlink="http://www.w3.org/1999/xlink"> 202 EOD; 203 204 $contentXmlFileContents .= $styleManager->getContentXmlFontFaceSectionContent(); 205 $contentXmlFileContents .= $styleManager->getContentXmlAutomaticStylesSectionContent($worksheets); 206 207 $contentXmlFileContents .= '<office:body><office:spreadsheet>'; 208 209 $this->createFileWithContents($this->rootFolder, self::CONTENT_XML_FILE_NAME, $contentXmlFileContents); 210 211 // Append sheets content to "content.xml" 212 $contentXmlFilePath = $this->rootFolder . '/' . self::CONTENT_XML_FILE_NAME; 213 $contentXmlHandle = \fopen($contentXmlFilePath, 'a'); 214 215 foreach ($worksheets as $worksheet) { 216 // write the "<table:table>" node, with the final sheet's name 217 \fwrite($contentXmlHandle, $worksheetManager->getTableElementStartAsString($worksheet)); 218 219 $worksheetFilePath = $worksheet->getFilePath(); 220 $this->copyFileContentsToTarget($worksheetFilePath, $contentXmlHandle); 221 222 \fwrite($contentXmlHandle, '</table:table>'); 223 } 224 225 $contentXmlFileContents = '</office:spreadsheet></office:body></office:document-content>'; 226 227 \fwrite($contentXmlHandle, $contentXmlFileContents); 228 \fclose($contentXmlHandle); 229 230 return $this; 231 } 232 233 /** 234 * Streams the content of the file at the given path into the target resource. 235 * Depending on which mode the target resource was created with, it will truncate then copy 236 * or append the content to the target file. 237 * 238 * @param string $sourceFilePath Path of the file whose content will be copied 239 * @param resource $targetResource Target resource that will receive the content 240 * @return void 241 */ 242 protected function copyFileContentsToTarget($sourceFilePath, $targetResource) 243 { 244 $sourceHandle = \fopen($sourceFilePath, 'r'); 245 \stream_copy_to_stream($sourceHandle, $targetResource); 246 \fclose($sourceHandle); 247 } 248 249 /** 250 * Deletes the temporary folder where sheets content was stored. 251 * 252 * @return FileSystemHelper 253 */ 254 public function deleteWorksheetTempFolder() 255 { 256 $this->deleteFolderRecursively($this->sheetsContentTempFolder); 257 258 return $this; 259 } 260 261 /** 262 * Creates the "styles.xml" file under the root folder 263 * 264 * @param StyleManager $styleManager 265 * @param int $numWorksheets Number of created worksheets 266 * @return FileSystemHelper 267 */ 268 public function createStylesFile($styleManager, $numWorksheets) 269 { 270 $stylesXmlFileContents = $styleManager->getStylesXMLFileContent($numWorksheets); 271 $this->createFileWithContents($this->rootFolder, self::STYLES_XML_FILE_NAME, $stylesXmlFileContents); 272 273 return $this; 274 } 275 276 /** 277 * Zips the root folder and streams the contents of the zip into the given stream 278 * 279 * @param resource $streamPointer Pointer to the stream to copy the zip 280 * @return void 281 */ 282 public function zipRootFolderAndCopyToStream($streamPointer) 283 { 284 $zip = $this->zipHelper->createZip($this->rootFolder); 285 286 $zipFilePath = $this->zipHelper->getZipFilePath($zip); 287 288 // In order to have the file's mime type detected properly, files need to be added 289 // to the zip file in a particular order. 290 // @see http://www.jejik.com/articles/2010/03/how_to_correctly_create_odf_documents_using_zip/ 291 $this->zipHelper->addUncompressedFileToArchive($zip, $this->rootFolder, self::MIMETYPE_FILE_NAME); 292 293 $this->zipHelper->addFolderToArchive($zip, $this->rootFolder, ZipHelper::EXISTING_FILES_SKIP); 294 $this->zipHelper->closeArchiveAndCopyToStream($zip, $streamPointer); 295 296 // once the zip is copied, remove it 297 $this->deleteFile($zipFilePath); 298 } 299 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body