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 311 and 400] [Versions 311 and 401] [Versions 311 and 402] [Versions 311 and 403] [Versions 39 and 311]

   1  <?php
   2  
   3  namespace PhpOffice\PhpSpreadsheet\Reader\Xlsx;
   4  
   5  use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
   6  use PhpOffice\PhpSpreadsheet\Reader\IReadFilter;
   7  use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
   8  use SimpleXMLElement;
   9  
  10  class ColumnAndRowAttributes extends BaseParserClass
  11  {
  12      private $worksheet;
  13  
  14      private $worksheetXml;
  15  
  16      public function __construct(Worksheet $workSheet, ?SimpleXMLElement $worksheetXml = null)
  17      {
  18          $this->worksheet = $workSheet;
  19          $this->worksheetXml = $worksheetXml;
  20      }
  21  
  22      /**
  23       * Set Worksheet column attributes by attributes array passed.
  24       *
  25       * @param string $columnAddress A, B, ... DX, ...
  26       * @param array $columnAttributes array of attributes (indexes are attribute name, values are value)
  27       *                               'xfIndex', 'visible', 'collapsed', 'outlineLevel', 'width', ... ?
  28       */
  29      private function setColumnAttributes($columnAddress, array $columnAttributes): void
  30      {
  31          if (isset($columnAttributes['xfIndex'])) {
  32              $this->worksheet->getColumnDimension($columnAddress)->setXfIndex($columnAttributes['xfIndex']);
  33          }
  34          if (isset($columnAttributes['visible'])) {
  35              $this->worksheet->getColumnDimension($columnAddress)->setVisible($columnAttributes['visible']);
  36          }
  37          if (isset($columnAttributes['collapsed'])) {
  38              $this->worksheet->getColumnDimension($columnAddress)->setCollapsed($columnAttributes['collapsed']);
  39          }
  40          if (isset($columnAttributes['outlineLevel'])) {
  41              $this->worksheet->getColumnDimension($columnAddress)->setOutlineLevel($columnAttributes['outlineLevel']);
  42          }
  43          if (isset($columnAttributes['width'])) {
  44              $this->worksheet->getColumnDimension($columnAddress)->setWidth($columnAttributes['width']);
  45          }
  46      }
  47  
  48      /**
  49       * Set Worksheet row attributes by attributes array passed.
  50       *
  51       * @param int $rowNumber 1, 2, 3, ... 99, ...
  52       * @param array $rowAttributes array of attributes (indexes are attribute name, values are value)
  53       *                               'xfIndex', 'visible', 'collapsed', 'outlineLevel', 'rowHeight', ... ?
  54       */
  55      private function setRowAttributes($rowNumber, array $rowAttributes): void
  56      {
  57          if (isset($rowAttributes['xfIndex'])) {
  58              $this->worksheet->getRowDimension($rowNumber)->setXfIndex($rowAttributes['xfIndex']);
  59          }
  60          if (isset($rowAttributes['visible'])) {
  61              $this->worksheet->getRowDimension($rowNumber)->setVisible($rowAttributes['visible']);
  62          }
  63          if (isset($rowAttributes['collapsed'])) {
  64              $this->worksheet->getRowDimension($rowNumber)->setCollapsed($rowAttributes['collapsed']);
  65          }
  66          if (isset($rowAttributes['outlineLevel'])) {
  67              $this->worksheet->getRowDimension($rowNumber)->setOutlineLevel($rowAttributes['outlineLevel']);
  68          }
  69          if (isset($rowAttributes['rowHeight'])) {
  70              $this->worksheet->getRowDimension($rowNumber)->setRowHeight($rowAttributes['rowHeight']);
  71          }
  72      }
  73  
  74      /**
  75       * @param IReadFilter $readFilter
  76       * @param bool $readDataOnly
  77       */
  78      public function load(?IReadFilter $readFilter = null, $readDataOnly = false): void
  79      {
  80          if ($this->worksheetXml === null) {
  81              return;
  82          }
  83  
  84          $columnsAttributes = [];
  85          $rowsAttributes = [];
  86          if (isset($this->worksheetXml->cols)) {
  87              $columnsAttributes = $this->readColumnAttributes($this->worksheetXml->cols, $readDataOnly);
  88          }
  89  
  90          if ($this->worksheetXml->sheetData && $this->worksheetXml->sheetData->row) {
  91              $rowsAttributes = $this->readRowAttributes($this->worksheetXml->sheetData->row, $readDataOnly);
  92          }
  93  
  94          // set columns/rows attributes
  95          $columnsAttributesAreSet = [];
  96          foreach ($columnsAttributes as $columnCoordinate => $columnAttributes) {
  97              if (
  98                  $readFilter === null ||
  99                  !$this->isFilteredColumn($readFilter, $columnCoordinate, $rowsAttributes)
 100              ) {
 101                  if (!isset($columnsAttributesAreSet[$columnCoordinate])) {
 102                      $this->setColumnAttributes($columnCoordinate, $columnAttributes);
 103                      $columnsAttributesAreSet[$columnCoordinate] = true;
 104                  }
 105              }
 106          }
 107  
 108          $rowsAttributesAreSet = [];
 109          foreach ($rowsAttributes as $rowCoordinate => $rowAttributes) {
 110              if (
 111                  $readFilter === null ||
 112                  !$this->isFilteredRow($readFilter, $rowCoordinate, $columnsAttributes)
 113              ) {
 114                  if (!isset($rowsAttributesAreSet[$rowCoordinate])) {
 115                      $this->setRowAttributes($rowCoordinate, $rowAttributes);
 116                      $rowsAttributesAreSet[$rowCoordinate] = true;
 117                  }
 118              }
 119          }
 120      }
 121  
 122      private function isFilteredColumn(IReadFilter $readFilter, $columnCoordinate, array $rowsAttributes)
 123      {
 124          foreach ($rowsAttributes as $rowCoordinate => $rowAttributes) {
 125              if (!$readFilter->readCell($columnCoordinate, $rowCoordinate, $this->worksheet->getTitle())) {
 126                  return true;
 127              }
 128          }
 129  
 130          return false;
 131      }
 132  
 133      private function readColumnAttributes(SimpleXMLElement $worksheetCols, $readDataOnly)
 134      {
 135          $columnAttributes = [];
 136  
 137          foreach ($worksheetCols->col as $column) {
 138              $startColumn = Coordinate::stringFromColumnIndex((int) $column['min']);
 139              $endColumn = Coordinate::stringFromColumnIndex((int) $column['max']);
 140              ++$endColumn;
 141              for ($columnAddress = $startColumn; $columnAddress !== $endColumn; ++$columnAddress) {
 142                  $columnAttributes[$columnAddress] = $this->readColumnRangeAttributes($column, $readDataOnly);
 143  
 144                  if ((int) ($column['max']) == 16384) {
 145                      break;
 146                  }
 147              }
 148          }
 149  
 150          return $columnAttributes;
 151      }
 152  
 153      private function readColumnRangeAttributes(SimpleXMLElement $column, $readDataOnly)
 154      {
 155          $columnAttributes = [];
 156  
 157          if ($column['style'] && !$readDataOnly) {
 158              $columnAttributes['xfIndex'] = (int) $column['style'];
 159          }
 160          if (self::boolean($column['hidden'])) {
 161              $columnAttributes['visible'] = false;
 162          }
 163          if (self::boolean($column['collapsed'])) {
 164              $columnAttributes['collapsed'] = true;
 165          }
 166          if (((int) $column['outlineLevel']) > 0) {
 167              $columnAttributes['outlineLevel'] = (int) $column['outlineLevel'];
 168          }
 169          $columnAttributes['width'] = (float) $column['width'];
 170  
 171          return $columnAttributes;
 172      }
 173  
 174      private function isFilteredRow(IReadFilter $readFilter, $rowCoordinate, array $columnsAttributes)
 175      {
 176          foreach ($columnsAttributes as $columnCoordinate => $columnAttributes) {
 177              if (!$readFilter->readCell($columnCoordinate, $rowCoordinate, $this->worksheet->getTitle())) {
 178                  return true;
 179              }
 180          }
 181  
 182          return false;
 183      }
 184  
 185      private function readRowAttributes(SimpleXMLElement $worksheetRow, $readDataOnly)
 186      {
 187          $rowAttributes = [];
 188  
 189          foreach ($worksheetRow as $row) {
 190              if ($row['ht'] && !$readDataOnly) {
 191                  $rowAttributes[(int) $row['r']]['rowHeight'] = (float) $row['ht'];
 192              }
 193              if (self::boolean($row['hidden'])) {
 194                  $rowAttributes[(int) $row['r']]['visible'] = false;
 195              }
 196              if (self::boolean($row['collapsed'])) {
 197                  $rowAttributes[(int) $row['r']]['collapsed'] = true;
 198              }
 199              if ((int) $row['outlineLevel'] > 0) {
 200                  $rowAttributes[(int) $row['r']]['outlineLevel'] = (int) $row['outlineLevel'];
 201              }
 202              if ($row['s'] && !$readDataOnly) {
 203                  $rowAttributes[(int) $row['r']]['xfIndex'] = (int) $row['s'];
 204              }
 205          }
 206  
 207          return $rowAttributes;
 208      }
 209  }