[ Index ]

PHP Cross Reference of Unnamed Project

title

Body

[close]

/lib/spout/src/Spout/Reader/XLSX/Helper/SharedStringsCaching/ -> FileBasedStrategy.php (source)

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


Generated: Thu Aug 11 10:00:09 2016 Cross-referenced by PHPXref 0.7.1