Differences Between: [Versions 310 and 311] [Versions 310 and 400] [Versions 310 and 401]
1 <?php 2 3 namespace Box\Spout\Reader\XLSX\Manager\SharedStringsCaching; 4 5 use Box\Spout\Reader\Exception\SharedStringNotFoundException; 6 use Box\Spout\Reader\XLSX\Creator\HelperFactory; 7 8 /** 9 * Class FileBasedStrategy 10 * 11 * This class implements the file-based caching strategy for shared strings. 12 * Shared strings are stored in small files (with a max number of strings per file). 13 * This strategy is slower than an in-memory strategy but is used to avoid out of memory crashes. 14 */ 15 class FileBasedStrategy implements CachingStrategyInterface 16 { 17 /** Value to use to escape the line feed character ("\n") */ 18 const ESCAPED_LINE_FEED_CHARACTER = '_x000A_'; 19 20 /** @var \Box\Spout\Common\Helper\GlobalFunctionsHelper Helper to work with global functions */ 21 protected $globalFunctionsHelper; 22 23 /** @var \Box\Spout\Common\Helper\FileSystemHelper Helper to perform file system operations */ 24 protected $fileSystemHelper; 25 26 /** @var string Temporary folder where the temporary files will be created */ 27 protected $tempFolder; 28 29 /** 30 * @var int Maximum number of strings that can be stored in one temp file 31 * @see CachingStrategyFactory::MAX_NUM_STRINGS_PER_TEMP_FILE 32 */ 33 protected $maxNumStringsPerTempFile; 34 35 /** @var resource Pointer to the last temp file a shared string was written to */ 36 protected $tempFilePointer; 37 38 /** 39 * @var string Path of the temporary file whose contents is currently stored in memory 40 * @see CachingStrategyFactory::MAX_NUM_STRINGS_PER_TEMP_FILE 41 */ 42 protected $inMemoryTempFilePath; 43 44 /** 45 * @var array Contents of the temporary file that was last read 46 * @see CachingStrategyFactory::MAX_NUM_STRINGS_PER_TEMP_FILE 47 */ 48 protected $inMemoryTempFileContents; 49 50 /** 51 * @param string $tempFolder Temporary folder where the temporary files to store shared strings will be stored 52 * @param int $maxNumStringsPerTempFile Maximum number of strings that can be stored in one temp file 53 * @param HelperFactory $helperFactory Factory to create helpers 54 */ 55 public function __construct($tempFolder, $maxNumStringsPerTempFile, $helperFactory) 56 { 57 $this->fileSystemHelper = $helperFactory->createFileSystemHelper($tempFolder); 58 $this->tempFolder = $this->fileSystemHelper->createFolder($tempFolder, uniqid('sharedstrings')); 59 60 $this->maxNumStringsPerTempFile = $maxNumStringsPerTempFile; 61 62 $this->globalFunctionsHelper = $helperFactory->createGlobalFunctionsHelper(); 63 $this->tempFilePointer = null; 64 } 65 66 /** 67 * Adds the given string to the cache. 68 * 69 * @param string $sharedString The string to be added to the cache 70 * @param int $sharedStringIndex Index of the shared string in the sharedStrings.xml file 71 * @return void 72 */ 73 public function addStringForIndex($sharedString, $sharedStringIndex) 74 { 75 $tempFilePath = $this->getSharedStringTempFilePath($sharedStringIndex); 76 77 if (!$this->globalFunctionsHelper->file_exists($tempFilePath)) { 78 if ($this->tempFilePointer) { 79 $this->globalFunctionsHelper->fclose($this->tempFilePointer); 80 } 81 $this->tempFilePointer = $this->globalFunctionsHelper->fopen($tempFilePath, 'w'); 82 } 83 84 // The shared string retrieval logic expects each cell data to be on one line only 85 // Encoding the line feed character allows to preserve this assumption 86 $lineFeedEncodedSharedString = $this->escapeLineFeed($sharedString); 87 88 $this->globalFunctionsHelper->fwrite($this->tempFilePointer, $lineFeedEncodedSharedString . PHP_EOL); 89 } 90 91 /** 92 * Returns the path for the temp file that should contain the string for the given index 93 * 94 * @param int $sharedStringIndex Index of the shared string in the sharedStrings.xml file 95 * @return string The temp file path for the given index 96 */ 97 protected function getSharedStringTempFilePath($sharedStringIndex) 98 { 99 $numTempFile = (int) ($sharedStringIndex / $this->maxNumStringsPerTempFile); 100 101 return $this->tempFolder . '/sharedstrings' . $numTempFile; 102 } 103 104 /** 105 * Closes the cache after the last shared string was added. 106 * This prevents any additional string from being added to the cache. 107 * 108 * @return void 109 */ 110 public function closeCache() 111 { 112 // close pointer to the last temp file that was written 113 if ($this->tempFilePointer) { 114 $this->globalFunctionsHelper->fclose($this->tempFilePointer); 115 } 116 } 117 118 /** 119 * Returns the string located at the given index from the cache. 120 * 121 * @param int $sharedStringIndex Index of the shared string in the sharedStrings.xml file 122 * @throws \Box\Spout\Reader\Exception\SharedStringNotFoundException If no shared string found for the given index 123 * @return string The shared string at the given index 124 */ 125 public function getStringAtIndex($sharedStringIndex) 126 { 127 $tempFilePath = $this->getSharedStringTempFilePath($sharedStringIndex); 128 $indexInFile = $sharedStringIndex % $this->maxNumStringsPerTempFile; 129 130 if (!$this->globalFunctionsHelper->file_exists($tempFilePath)) { 131 throw new SharedStringNotFoundException("Shared string temp file not found: $tempFilePath ; for index: $sharedStringIndex"); 132 } 133 134 if ($this->inMemoryTempFilePath !== $tempFilePath) { 135 // free memory 136 unset($this->inMemoryTempFileContents); 137 138 $this->inMemoryTempFileContents = explode(PHP_EOL, $this->globalFunctionsHelper->file_get_contents($tempFilePath)); 139 $this->inMemoryTempFilePath = $tempFilePath; 140 } 141 142 $sharedString = null; 143 144 // Using isset here because it is way faster than array_key_exists... 145 if (isset($this->inMemoryTempFileContents[$indexInFile])) { 146 $escapedSharedString = $this->inMemoryTempFileContents[$indexInFile]; 147 $sharedString = $this->unescapeLineFeed($escapedSharedString); 148 } 149 150 if ($sharedString === null) { 151 throw new SharedStringNotFoundException("Shared string not found for index: $sharedStringIndex"); 152 } 153 154 return rtrim($sharedString, PHP_EOL); 155 } 156 157 /** 158 * Escapes the line feed characters (\n) 159 * 160 * @param string $unescapedString 161 * @return string 162 */ 163 private function escapeLineFeed($unescapedString) 164 { 165 return str_replace("\n", self::ESCAPED_LINE_FEED_CHARACTER, $unescapedString); 166 } 167 168 /** 169 * Unescapes the line feed characters (\n) 170 * 171 * @param string $escapedString 172 * @return string 173 */ 174 private function unescapeLineFeed($escapedString) 175 { 176 return str_replace(self::ESCAPED_LINE_FEED_CHARACTER, "\n", $escapedString); 177 } 178 179 /** 180 * Destroys the cache, freeing memory and removing any created artifacts 181 * 182 * @return void 183 */ 184 public function clearCache() 185 { 186 if ($this->tempFolder) { 187 $this->fileSystemHelper->deleteFolderRecursively($this->tempFolder); 188 } 189 } 190 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body