Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.11.x will end 14 Nov 2022 (12 months plus 6 months extension).
  • Bug fixes for security issues in 3.11.x will end 13 Nov 2023 (18 months plus 12 months extension).
  • PHP version: minimum PHP 7.3.0 Note: minimum PHP version has increased since Moodle 3.10. PHP 7.4.x is supported too.

Differences Between: [Versions 310 and 311] [Versions 39 and 311]

   1  <?php
   2  
   3  namespace Box\Spout\Writer\XLSX\Manager;
   4  
   5  use Box\Spout\Common\Exception\IOException;
   6  use Box\Spout\Common\Helper\Escaper;
   7  
   8  /**
   9   * Class SharedStringsManager
  10   * This class provides functions to write shared strings
  11   */
  12  class SharedStringsManager
  13  {
  14      const SHARED_STRINGS_FILE_NAME = 'sharedStrings.xml';
  15  
  16      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      const DEFAULT_STRINGS_COUNT_PART = 'count="9999999999999" uniqueCount="9999999999999"';
  26  
  27      /** @var resource Pointer to the sharedStrings.xml file */
  28      protected $sharedStringsFilePointer;
  29  
  30      /** @var int Number of shared strings already written */
  31      protected $numSharedStrings = 0;
  32  
  33      /** @var Escaper\XLSX Strings escaper */
  34      protected $stringsEscaper;
  35  
  36      /**
  37       * @param string $xlFolder Path to the "xl" folder
  38       * @param Escaper\XLSX $stringsEscaper Strings escaper
  39       */
  40      public function __construct($xlFolder, $stringsEscaper)
  41      {
  42          $sharedStringsFilePath = $xlFolder . '/' . self::SHARED_STRINGS_FILE_NAME;
  43          $this->sharedStringsFilePointer = \fopen($sharedStringsFilePath, 'w');
  44  
  45          $this->throwIfSharedStringsFilePointerIsNotAvailable();
  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       * Checks if the book has been created. Throws an exception if not created yet.
  56       *
  57       * @throws \Box\Spout\Common\Exception\IOException If the sheet data file cannot be opened for writing
  58       * @return void
  59       */
  60      protected function throwIfSharedStringsFilePointerIsNotAvailable()
  61      {
  62          if (!$this->sharedStringsFilePointer) {
  63              throw new IOException('Unable to open shared strings file for writing.');
  64          }
  65      }
  66  
  67      /**
  68       * Writes the given string into the sharedStrings.xml file.
  69       * Starting and ending whitespaces are preserved.
  70       *
  71       * @param string $string
  72       * @return int ID of the written shared string
  73       */
  74      public function writeString($string)
  75      {
  76          \fwrite($this->sharedStringsFilePointer, '<si><t xml:space="preserve">' . $this->stringsEscaper->escape($string) . '</t></si>');
  77          $this->numSharedStrings++;
  78  
  79          // Shared string ID is zero-based
  80          return ($this->numSharedStrings - 1);
  81      }
  82  
  83      /**
  84       * Finishes writing the data in the sharedStrings.xml file and closes the file.
  85       *
  86       * @return void
  87       */
  88      public function close()
  89      {
  90          if (!\is_resource($this->sharedStringsFilePointer)) {
  91              return;
  92          }
  93  
  94          \fwrite($this->sharedStringsFilePointer, '</sst>');
  95  
  96          // Replace the default strings count with the actual number of shared strings in the file header
  97          $firstPartHeaderLength = \strlen(self::SHARED_STRINGS_XML_FILE_FIRST_PART_HEADER);
  98          $defaultStringsCountPartLength = \strlen(self::DEFAULT_STRINGS_COUNT_PART);
  99  
 100          // Adding 1 to take into account the space between the last xml attribute and "count"
 101          \fseek($this->sharedStringsFilePointer, $firstPartHeaderLength + 1);
 102          \fwrite($this->sharedStringsFilePointer, \sprintf("%-{$defaultStringsCountPartLength}s", 'count="' . $this->numSharedStrings . '" uniqueCount="' . $this->numSharedStrings . '"'));
 103  
 104          \fclose($this->sharedStringsFilePointer);
 105      }
 106  }