Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 4.3.x will end 7 October 2024 (12 months).
  • Bug fixes for security issues in 4.3.x will end 21 April 2025 (18 months).
  • PHP version: minimum PHP 8.0.0 Note: minimum PHP version has increased since Moodle 4.1. PHP 8.2.x is supported too.
   1  <?php
   2  
   3  declare(strict_types=1);
   4  
   5  namespace OpenSpout\Reader\XLSX;
   6  
   7  use OpenSpout\Common\Exception\IOException;
   8  use OpenSpout\Reader\Common\ColumnWidth;
   9  use OpenSpout\Reader\Common\XMLProcessor;
  10  use OpenSpout\Reader\Wrapper\XMLReader;
  11  
  12  final class SheetHeaderReader
  13  {
  14      public const XML_NODE_COL = 'col';
  15      public const XML_NODE_SHEETDATA = 'sheetData';
  16      public const XML_ATTRIBUTE_MIN = 'min';
  17      public const XML_ATTRIBUTE_MAX = 'max';
  18      public const XML_ATTRIBUTE_WIDTH = 'width';
  19  
  20      /** @var string Path of the XLSX file being read */
  21      private string $filePath;
  22  
  23      /** @var string Path of the sheet data XML file as in [Content_Types].xml */
  24      private string $sheetDataXMLFilePath;
  25  
  26      /** @var XMLReader The XMLReader object that will help read sheet's XML data */
  27      private XMLReader $xmlReader;
  28  
  29      /** @var XMLProcessor Helper Object to process XML nodes */
  30      private XMLProcessor $xmlProcessor;
  31  
  32      /** @var ColumnWidth[] The widths of the columns in the sheet, if specified */
  33      private array $columnWidths = [];
  34  
  35      /**
  36       * @param string       $filePath             Path of the XLSX file being read
  37       * @param string       $sheetDataXMLFilePath Path of the sheet data XML file as in [Content_Types].xml
  38       * @param XMLReader    $xmlReader            XML Reader
  39       * @param XMLProcessor $xmlProcessor         Helper to process XML files
  40       */
  41      public function __construct(
  42          string $filePath,
  43          string $sheetDataXMLFilePath,
  44          XMLReader $xmlReader,
  45          XMLProcessor $xmlProcessor
  46      ) {
  47          $this->filePath = $filePath;
  48          $this->sheetDataXMLFilePath = $this->normalizeSheetDataXMLFilePath($sheetDataXMLFilePath);
  49          $this->xmlReader = $xmlReader;
  50  
  51          // Register all callbacks to process different nodes when reading the XML file
  52          $this->xmlProcessor = $xmlProcessor;
  53          $this->xmlProcessor->registerCallback(self::XML_NODE_COL, XMLProcessor::NODE_TYPE_START, [$this, 'processColStartingNode']);
  54          $this->xmlProcessor->registerCallback(self::XML_NODE_SHEETDATA, XMLProcessor::NODE_TYPE_START, [$this, 'processSheetDataStartingNode']);
  55  
  56          // The reader should be unused, but we close to be sure
  57          $this->xmlReader->close();
  58  
  59          if (false === $this->xmlReader->openFileInZip($this->filePath, $this->sheetDataXMLFilePath)) {
  60              throw new IOException("Could not open \"{$this->sheetDataXMLFilePath}\".");
  61          }
  62  
  63          // Now read the entire header of the sheet, until we reach the <sheetData> element
  64          $this->xmlProcessor->readUntilStopped();
  65  
  66          // We don't need the reader anymore, so we close it
  67          $this->xmlReader->close();
  68      }
  69  
  70      /**
  71       * @internal
  72       *
  73       * @return ColumnWidth[]
  74       */
  75      public function getColumnWidths(): array
  76      {
  77          return $this->columnWidths;
  78      }
  79  
  80      /**
  81       * @param XMLReader $xmlReader XMLReader object, positioned on a "<col>" starting node
  82       *
  83       * @return int A return code that indicates what action should the processor take next
  84       */
  85      private function processColStartingNode(XMLReader $xmlReader): int
  86      {
  87          $min = (int) $xmlReader->getAttribute(self::XML_ATTRIBUTE_MIN);
  88          $max = (int) $xmlReader->getAttribute(self::XML_ATTRIBUTE_MAX);
  89          $width = (float) $xmlReader->getAttribute(self::XML_ATTRIBUTE_WIDTH);
  90  
  91          \assert($min > 0);
  92          \assert($max > 0);
  93  
  94          $columnwidth = new ColumnWidth($min, $max, $width);
  95          $this->columnWidths[] = $columnwidth;
  96  
  97          return XMLProcessor::PROCESSING_CONTINUE;
  98      }
  99  
 100      /**
 101       * @return int A return code that indicates what action should the processor take next
 102       */
 103      private function processSheetDataStartingNode(): int
 104      {
 105          // The opening "<sheetData>" marks the end of the file
 106          return XMLProcessor::PROCESSING_STOP;
 107      }
 108  
 109      /**
 110       * @param string $sheetDataXMLFilePath Path of the sheet data XML file as in [Content_Types].xml
 111       *
 112       * @return string path of the XML file containing the sheet data,
 113       *                without the leading slash
 114       */
 115      private function normalizeSheetDataXMLFilePath(string $sheetDataXMLFilePath): string
 116      {
 117          return ltrim($sheetDataXMLFilePath, '/');
 118      }
 119  }