Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

  • Bug fixes for general core bugs in 4.1.x will end 13 November 2023 (12 months).
  • Bug fixes for security issues in 4.1.x will end 10 November 2025 (36 months).
  • PHP version: minimum PHP 7.4.0 Note: minimum PHP version has increased since Moodle 4.0. PHP 8.0.x is supported too.
   1  <?php
   2  
   3  namespace PhpOffice\PhpSpreadsheet\Cell;
   4  
   5  use PhpOffice\PhpSpreadsheet\Exception;
   6  use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
   7  
   8  class CellRange implements AddressRange
   9  {
  10      /**
  11       * @var CellAddress
  12       */
  13      protected $from;
  14  
  15      /**
  16       * @var CellAddress
  17       */
  18      protected $to;
  19  
  20      public function __construct(CellAddress $from, CellAddress $to)
  21      {
  22          $this->validateFromTo($from, $to);
  23      }
  24  
  25      private function validateFromTo(CellAddress $from, CellAddress $to): void
  26      {
  27          // Identify actual top-left and bottom-right values (in case we've been given top-right and bottom-left)
  28          $firstColumn = min($from->columnId(), $to->columnId());
  29          $firstRow = min($from->rowId(), $to->rowId());
  30          $lastColumn = max($from->columnId(), $to->columnId());
  31          $lastRow = max($from->rowId(), $to->rowId());
  32  
  33          $fromWorksheet = $from->worksheet();
  34          $toWorksheet = $to->worksheet();
  35          $this->validateWorksheets($fromWorksheet, $toWorksheet);
  36  
  37          $this->from = $this->cellAddressWrapper($firstColumn, $firstRow, $fromWorksheet);
  38          $this->to = $this->cellAddressWrapper($lastColumn, $lastRow, $toWorksheet);
  39      }
  40  
  41      private function validateWorksheets(?Worksheet $fromWorksheet, ?Worksheet $toWorksheet): void
  42      {
  43          if ($fromWorksheet !== null && $toWorksheet !== null) {
  44              // We could simply compare worksheets rather than worksheet titles; but at some point we may introduce
  45              //    support for 3d ranges; and at that point we drop this check and let the validation fall through
  46              //    to the check for same workbook; but unless we check on titles, this test will also detect if the
  47              //    worksheets are in different spreadsheets, and the next check will never execute or throw its
  48              //    own exception.
  49              if ($fromWorksheet->getTitle() !== $toWorksheet->getTitle()) {
  50                  throw new Exception('3d Cell Ranges are not supported');
  51              } elseif ($fromWorksheet->getParent() !== $toWorksheet->getParent()) {
  52                  throw new Exception('Worksheets must be in the same spreadsheet');
  53              }
  54          }
  55      }
  56  
  57      private function cellAddressWrapper(int $column, int $row, ?Worksheet $worksheet = null): CellAddress
  58      {
  59          $cellAddress = Coordinate::stringFromColumnIndex($column) . (string) $row;
  60  
  61          return new class ($cellAddress, $worksheet) extends CellAddress {
  62              public function nextRow(int $offset = 1): CellAddress
  63              {
  64                  /** @var CellAddress $result */
  65                  $result = parent::nextRow($offset);
  66                  $this->rowId = $result->rowId;
  67                  $this->cellAddress = $result->cellAddress;
  68  
  69                  return $this;
  70              }
  71  
  72              public function previousRow(int $offset = 1): CellAddress
  73              {
  74                  /** @var CellAddress $result */
  75                  $result = parent::previousRow($offset);
  76                  $this->rowId = $result->rowId;
  77                  $this->cellAddress = $result->cellAddress;
  78  
  79                  return $this;
  80              }
  81  
  82              public function nextColumn(int $offset = 1): CellAddress
  83              {
  84                  /** @var CellAddress $result */
  85                  $result = parent::nextColumn($offset);
  86                  $this->columnId = $result->columnId;
  87                  $this->columnName = $result->columnName;
  88                  $this->cellAddress = $result->cellAddress;
  89  
  90                  return $this;
  91              }
  92  
  93              public function previousColumn(int $offset = 1): CellAddress
  94              {
  95                  /** @var CellAddress $result */
  96                  $result = parent::previousColumn($offset);
  97                  $this->columnId = $result->columnId;
  98                  $this->columnName = $result->columnName;
  99                  $this->cellAddress = $result->cellAddress;
 100  
 101                  return $this;
 102              }
 103          };
 104      }
 105  
 106      public function from(): CellAddress
 107      {
 108          // Re-order from/to in case the cell addresses have been modified
 109          $this->validateFromTo($this->from, $this->to);
 110  
 111          return $this->from;
 112      }
 113  
 114      public function to(): CellAddress
 115      {
 116          // Re-order from/to in case the cell addresses have been modified
 117          $this->validateFromTo($this->from, $this->to);
 118  
 119          return $this->to;
 120      }
 121  
 122      public function __toString(): string
 123      {
 124          // Re-order from/to in case the cell addresses have been modified
 125          $this->validateFromTo($this->from, $this->to);
 126  
 127          if ($this->from->cellAddress() === $this->to->cellAddress()) {
 128              return "{$this->from->fullCellAddress()}";
 129          }
 130  
 131          $fromAddress = $this->from->fullCellAddress();
 132          $toAddress = $this->to->cellAddress();
 133  
 134          return "{$fromAddress}:{$toAddress}";
 135      }
 136  }