1 <?php 2 3 declare(strict_types=1); 4 5 namespace OpenSpout\Writer\XLSX\Manager; 6 7 use OpenSpout\Common\Helper\Escaper; 8 9 /** 10 * @internal 11 */ 12 final class SharedStringsManager 13 { 14 public const SHARED_STRINGS_FILE_NAME = 'sharedStrings.xml'; 15 16 public const SHARED_STRINGS_XML_FILE_FIRST_PART_HEADER = <<<'EOD' 17 <?xml version="1.0" encoding="UTF-8" standalone="yes"?> 18 <sst xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" 19 EOD; 20 21 /** 22 * This number must be really big so that the no generated file will have more strings than that. 23 * If the strings number goes above, characters will be overwritten in an unwanted way and will corrupt the file. 24 */ 25 public const DEFAULT_STRINGS_COUNT_PART = 'count="9999999999999" uniqueCount="9999999999999"'; 26 27 /** @var resource Pointer to the sharedStrings.xml file */ 28 private $sharedStringsFilePointer; 29 30 /** @var int Number of shared strings already written */ 31 private int $numSharedStrings = 0; 32 33 /** @var Escaper\XLSX Strings escaper */ 34 private Escaper\XLSX $stringsEscaper; 35 36 /** 37 * @param string $xlFolder Path to the "xl" folder 38 * @param Escaper\XLSX $stringsEscaper Strings escaper 39 */ 40 public function __construct(string $xlFolder, Escaper\XLSX $stringsEscaper) 41 { 42 $sharedStringsFilePath = $xlFolder.\DIRECTORY_SEPARATOR.self::SHARED_STRINGS_FILE_NAME; 43 $resource = fopen($sharedStringsFilePath, 'w'); 44 \assert(false !== $resource); 45 $this->sharedStringsFilePointer = $resource; 46 47 // the headers is split into different parts so that we can fseek and put in the correct count and uniqueCount later 48 $header = self::SHARED_STRINGS_XML_FILE_FIRST_PART_HEADER.' '.self::DEFAULT_STRINGS_COUNT_PART.'>'; 49 fwrite($this->sharedStringsFilePointer, $header); 50 51 $this->stringsEscaper = $stringsEscaper; 52 } 53 54 /** 55 * Writes the given string into the sharedStrings.xml file. 56 * Starting and ending whitespaces are preserved. 57 * 58 * @return int ID of the written shared string 59 */ 60 public function writeString(string $string): int 61 { 62 fwrite($this->sharedStringsFilePointer, '<si><t xml:space="preserve">'.$this->stringsEscaper->escape($string).'</t></si>'); 63 ++$this->numSharedStrings; 64 65 // Shared string ID is zero-based 66 return $this->numSharedStrings - 1; 67 } 68 69 /** 70 * Finishes writing the data in the sharedStrings.xml file and closes the file. 71 */ 72 public function close(): void 73 { 74 fwrite($this->sharedStringsFilePointer, '</sst>'); 75 76 // Replace the default strings count with the actual number of shared strings in the file header 77 $firstPartHeaderLength = \strlen(self::SHARED_STRINGS_XML_FILE_FIRST_PART_HEADER); 78 $defaultStringsCountPartLength = \strlen(self::DEFAULT_STRINGS_COUNT_PART); 79 80 // Adding 1 to take into account the space between the last xml attribute and "count" 81 fseek($this->sharedStringsFilePointer, $firstPartHeaderLength + 1); 82 fwrite($this->sharedStringsFilePointer, sprintf("%-{$defaultStringsCountPartLength}s", 'count="'.$this->numSharedStrings.'" uniqueCount="'.$this->numSharedStrings.'"')); 83 84 fclose($this->sharedStringsFilePointer); 85 } 86 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body