Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 4.2.x will end 22 April 2024 (12 months).
  • Bug fixes for security issues in 4.2.x will end 7 October 2024 (18 months).
  • PHP version: minimum PHP 8.0.0 Note: minimum PHP version has increased since Moodle 4.1. PHP 8.1.x is supported too.
   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  }