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.

Differences Between: [Versions 310 and 403] [Versions 311 and 403] [Versions 39 and 403] [Versions 400 and 403] [Versions 401 and 403] [Versions 402 and 403]

   1  <?php
   2  
   3  namespace PhpOffice\PhpSpreadsheet\Style;
   4  
   5  use PhpOffice\PhpSpreadsheet\Calculation\Functions;
   6  use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
   7  use PhpOffice\PhpSpreadsheet\Exception;
   8  use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
   9  use PhpOffice\PhpSpreadsheet\Spreadsheet;
  10  
  11  class Style extends Supervisor
  12  {
  13      /**
  14       * Font.
  15       *
  16       * @var Font
  17       */
  18      protected $font;
  19  
  20      /**
  21       * Fill.
  22       *
  23       * @var Fill
  24       */
  25      protected $fill;
  26  
  27      /**
  28       * Borders.
  29       *
  30       * @var Borders
  31       */
  32      protected $borders;
  33  
  34      /**
  35       * Alignment.
  36       *
  37       * @var Alignment
  38       */
  39      protected $alignment;
  40  
  41      /**
  42       * Number Format.
  43       *
  44       * @var NumberFormat
  45       */
  46      protected $numberFormat;
  47  
  48      /**
  49       * Protection.
  50       *
  51       * @var Protection
  52       */
  53      protected $protection;
  54  
  55      /**
  56       * Index of style in collection. Only used for real style.
  57       *
  58       * @var int
  59       */
  60      protected $index;
  61  
  62      /**
  63       * Use Quote Prefix when displaying in cell editor. Only used for real style.
  64       *
  65       * @var bool
  66       */
  67      protected $quotePrefix = false;
  68  
  69      /**
  70       * Internal cache for styles
  71       * Used when applying style on range of cells (column or row) and cleared when
  72       * all cells in range is styled.
  73       *
  74       * PhpSpreadsheet will always minimize the amount of styles used. So cells with
  75       * same styles will reference the same Style instance. To check if two styles
  76       * are similar Style::getHashCode() is used. This call is expensive. To minimize
  77       * the need to call this method we can cache the internal PHP object id of the
  78       * Style in the range. Style::getHashCode() will then only be called when we
  79       * encounter a unique style.
  80       *
  81       * @see Style::applyFromArray()
  82       * @see Style::getHashCode()
  83       *
  84       * @var null|array<string, array>
  85       */
  86      private static $cachedStyles;
  87  
  88      /**
  89       * Create a new Style.
  90       *
  91       * @param bool $isSupervisor Flag indicating if this is a supervisor or not
  92       *         Leave this value at default unless you understand exactly what
  93       *    its ramifications are
  94       * @param bool $isConditional Flag indicating if this is a conditional style or not
  95       *       Leave this value at default unless you understand exactly what
  96       *    its ramifications are
  97       */
  98      public function __construct($isSupervisor = false, $isConditional = false)
  99      {
 100          parent::__construct($isSupervisor);
 101  
 102          // Initialise values
 103          $this->font = new Font($isSupervisor, $isConditional);
 104          $this->fill = new Fill($isSupervisor, $isConditional);
 105          $this->borders = new Borders($isSupervisor, $isConditional);
 106          $this->alignment = new Alignment($isSupervisor, $isConditional);
 107          $this->numberFormat = new NumberFormat($isSupervisor, $isConditional);
 108          $this->protection = new Protection($isSupervisor, $isConditional);
 109  
 110          // bind parent if we are a supervisor
 111          if ($isSupervisor) {
 112              $this->font->bindParent($this);
 113              $this->fill->bindParent($this);
 114              $this->borders->bindParent($this);
 115              $this->alignment->bindParent($this);
 116              $this->numberFormat->bindParent($this);
 117              $this->protection->bindParent($this);
 118          }
 119      }
 120  
 121      /**
 122       * Get the shared style component for the currently active cell in currently active sheet.
 123       * Only used for style supervisor.
 124       */
 125      public function getSharedComponent(): self
 126      {
 127          $activeSheet = $this->getActiveSheet();
 128          $selectedCell = Functions::trimSheetFromCellReference($this->getActiveCell()); // e.g. 'A1'
 129  
 130          if ($activeSheet->cellExists($selectedCell)) {
 131              $xfIndex = $activeSheet->getCell($selectedCell)->getXfIndex();
 132          } else {
 133              $xfIndex = 0;
 134          }
 135  
 136          return $activeSheet->getParentOrThrow()->getCellXfByIndex($xfIndex);
 137      }
 138  
 139      /**
 140       * Get parent. Only used for style supervisor.
 141       */
 142      public function getParent(): Spreadsheet
 143      {
 144          return $this->getActiveSheet()->getParentOrThrow();
 145      }
 146  
 147      /**
 148       * Build style array from subcomponents.
 149       *
 150       * @param array $array
 151       *
 152       * @return array
 153       */
 154      public function getStyleArray($array)
 155      {
 156          return ['quotePrefix' => $array];
 157      }
 158  
 159      /**
 160       * Apply styles from array.
 161       *
 162       * <code>
 163       * $spreadsheet->getActiveSheet()->getStyle('B2')->applyFromArray(
 164       *     [
 165       *         'font' => [
 166       *             'name' => 'Arial',
 167       *             'bold' => true,
 168       *             'italic' => false,
 169       *             'underline' => Font::UNDERLINE_DOUBLE,
 170       *             'strikethrough' => false,
 171       *             'color' => [
 172       *                 'rgb' => '808080'
 173       *             ]
 174       *         ],
 175       *         'borders' => [
 176       *             'bottom' => [
 177       *                 'borderStyle' => Border::BORDER_DASHDOT,
 178       *                 'color' => [
 179       *                     'rgb' => '808080'
 180       *                 ]
 181       *             ],
 182       *             'top' => [
 183       *                 'borderStyle' => Border::BORDER_DASHDOT,
 184       *                 'color' => [
 185       *                     'rgb' => '808080'
 186       *                 ]
 187       *             ]
 188       *         ],
 189       *         'alignment' => [
 190       *             'horizontal' => Alignment::HORIZONTAL_CENTER,
 191       *             'vertical' => Alignment::VERTICAL_CENTER,
 192       *             'wrapText' => true,
 193       *         ],
 194       *         'quotePrefix'    => true
 195       *     ]
 196       * );
 197       * </code>
 198       *
 199       * @param array $styleArray Array containing style information
 200       * @param bool $advancedBorders advanced mode for setting borders
 201       *
 202       * @return $this
 203       */
 204      public function applyFromArray(array $styleArray, $advancedBorders = true)
 205      {
 206          if ($this->isSupervisor) {
 207              $pRange = $this->getSelectedCells();
 208  
 209              // Uppercase coordinate and strip any Worksheet reference from the selected range
 210              $pRange = strtoupper($pRange);
 211              if (strpos($pRange, '!') !== false) {
 212                  $pRangeWorksheet = StringHelper::strToUpper(trim(substr($pRange, 0, (int) strrpos($pRange, '!')), "'"));
 213                  if ($pRangeWorksheet !== '' && StringHelper::strToUpper($this->getActiveSheet()->getTitle()) !== $pRangeWorksheet) {
 214                      throw new Exception('Invalid Worksheet for specified Range');
 215                  }
 216                  $pRange = strtoupper(Functions::trimSheetFromCellReference($pRange));
 217              }
 218  
 219              // Is it a cell range or a single cell?
 220              if (strpos($pRange, ':') === false) {
 221                  $rangeA = $pRange;
 222                  $rangeB = $pRange;
 223              } else {
 224                  [$rangeA, $rangeB] = explode(':', $pRange);
 225              }
 226  
 227              // Calculate range outer borders
 228              $rangeStart = Coordinate::coordinateFromString($rangeA);
 229              $rangeEnd = Coordinate::coordinateFromString($rangeB);
 230              $rangeStartIndexes = Coordinate::indexesFromString($rangeA);
 231              $rangeEndIndexes = Coordinate::indexesFromString($rangeB);
 232  
 233              $columnStart = $rangeStart[0];
 234              $columnEnd = $rangeEnd[0];
 235  
 236              // Make sure we can loop upwards on rows and columns
 237              if ($rangeStartIndexes[0] > $rangeEndIndexes[0] && $rangeStartIndexes[1] > $rangeEndIndexes[1]) {
 238                  $tmp = $rangeStartIndexes;
 239                  $rangeStartIndexes = $rangeEndIndexes;
 240                  $rangeEndIndexes = $tmp;
 241              }
 242  
 243              // ADVANCED MODE:
 244              if ($advancedBorders && isset($styleArray['borders'])) {
 245                  // 'allBorders' is a shorthand property for 'outline' and 'inside' and
 246                  //        it applies to components that have not been set explicitly
 247                  if (isset($styleArray['borders']['allBorders'])) {
 248                      foreach (['outline', 'inside'] as $component) {
 249                          if (!isset($styleArray['borders'][$component])) {
 250                              $styleArray['borders'][$component] = $styleArray['borders']['allBorders'];
 251                          }
 252                      }
 253                      unset($styleArray['borders']['allBorders']); // not needed any more
 254                  }
 255                  // 'outline' is a shorthand property for 'top', 'right', 'bottom', 'left'
 256                  //        it applies to components that have not been set explicitly
 257                  if (isset($styleArray['borders']['outline'])) {
 258                      foreach (['top', 'right', 'bottom', 'left'] as $component) {
 259                          if (!isset($styleArray['borders'][$component])) {
 260                              $styleArray['borders'][$component] = $styleArray['borders']['outline'];
 261                          }
 262                      }
 263                      unset($styleArray['borders']['outline']); // not needed any more
 264                  }
 265                  // 'inside' is a shorthand property for 'vertical' and 'horizontal'
 266                  //        it applies to components that have not been set explicitly
 267                  if (isset($styleArray['borders']['inside'])) {
 268                      foreach (['vertical', 'horizontal'] as $component) {
 269                          if (!isset($styleArray['borders'][$component])) {
 270                              $styleArray['borders'][$component] = $styleArray['borders']['inside'];
 271                          }
 272                      }
 273                      unset($styleArray['borders']['inside']); // not needed any more
 274                  }
 275                  // width and height characteristics of selection, 1, 2, or 3 (for 3 or more)
 276                  $xMax = min($rangeEndIndexes[0] - $rangeStartIndexes[0] + 1, 3);
 277                  $yMax = min($rangeEndIndexes[1] - $rangeStartIndexes[1] + 1, 3);
 278  
 279                  // loop through up to 3 x 3 = 9 regions
 280                  for ($x = 1; $x <= $xMax; ++$x) {
 281                      // start column index for region
 282                      $colStart = ($x == 3) ?
 283                          Coordinate::stringFromColumnIndex($rangeEndIndexes[0])
 284                          : Coordinate::stringFromColumnIndex($rangeStartIndexes[0] + $x - 1);
 285                      // end column index for region
 286                      $colEnd = ($x == 1) ?
 287                          Coordinate::stringFromColumnIndex($rangeStartIndexes[0])
 288                          : Coordinate::stringFromColumnIndex($rangeEndIndexes[0] - $xMax + $x);
 289  
 290                      for ($y = 1; $y <= $yMax; ++$y) {
 291                          // which edges are touching the region
 292                          $edges = [];
 293                          if ($x == 1) {
 294                              // are we at left edge
 295                              $edges[] = 'left';
 296                          }
 297                          if ($x == $xMax) {
 298                              // are we at right edge
 299                              $edges[] = 'right';
 300                          }
 301                          if ($y == 1) {
 302                              // are we at top edge?
 303                              $edges[] = 'top';
 304                          }
 305                          if ($y == $yMax) {
 306                              // are we at bottom edge?
 307                              $edges[] = 'bottom';
 308                          }
 309  
 310                          // start row index for region
 311                          $rowStart = ($y == 3) ?
 312                              $rangeEndIndexes[1] : $rangeStartIndexes[1] + $y - 1;
 313  
 314                          // end row index for region
 315                          $rowEnd = ($y == 1) ?
 316                              $rangeStartIndexes[1] : $rangeEndIndexes[1] - $yMax + $y;
 317  
 318                          // build range for region
 319                          $range = $colStart . $rowStart . ':' . $colEnd . $rowEnd;
 320  
 321                          // retrieve relevant style array for region
 322                          $regionStyles = $styleArray;
 323                          unset($regionStyles['borders']['inside']);
 324  
 325                          // what are the inner edges of the region when looking at the selection
 326                          $innerEdges = array_diff(['top', 'right', 'bottom', 'left'], $edges);
 327  
 328                          // inner edges that are not touching the region should take the 'inside' border properties if they have been set
 329                          foreach ($innerEdges as $innerEdge) {
 330                              switch ($innerEdge) {
 331                                  case 'top':
 332                                  case 'bottom':
 333                                      // should pick up 'horizontal' border property if set
 334                                      if (isset($styleArray['borders']['horizontal'])) {
 335                                          $regionStyles['borders'][$innerEdge] = $styleArray['borders']['horizontal'];
 336                                      } else {
 337                                          unset($regionStyles['borders'][$innerEdge]);
 338                                      }
 339  
 340                                      break;
 341                                  case 'left':
 342                                  case 'right':
 343                                      // should pick up 'vertical' border property if set
 344                                      if (isset($styleArray['borders']['vertical'])) {
 345                                          $regionStyles['borders'][$innerEdge] = $styleArray['borders']['vertical'];
 346                                      } else {
 347                                          unset($regionStyles['borders'][$innerEdge]);
 348                                      }
 349  
 350                                      break;
 351                              }
 352                          }
 353  
 354                          // apply region style to region by calling applyFromArray() in simple mode
 355                          $this->getActiveSheet()->getStyle($range)->applyFromArray($regionStyles, false);
 356                      }
 357                  }
 358  
 359                  // restore initial cell selection range
 360                  $this->getActiveSheet()->getStyle($pRange);
 361  
 362                  return $this;
 363              }
 364  
 365              // SIMPLE MODE:
 366              // Selection type, inspect
 367              if (preg_match('/^[A-Z]+1:[A-Z]+1048576$/', $pRange)) {
 368                  $selectionType = 'COLUMN';
 369  
 370                  // Enable caching of styles
 371                  self::$cachedStyles = ['hashByObjId' => [], 'styleByHash' => []];
 372              } elseif (preg_match('/^A\d+:XFD\d+$/', $pRange)) {
 373                  $selectionType = 'ROW';
 374  
 375                  // Enable caching of styles
 376                  self::$cachedStyles = ['hashByObjId' => [], 'styleByHash' => []];
 377              } else {
 378                  $selectionType = 'CELL';
 379              }
 380  
 381              // First loop through columns, rows, or cells to find out which styles are affected by this operation
 382              $oldXfIndexes = $this->getOldXfIndexes($selectionType, $rangeStartIndexes, $rangeEndIndexes, $columnStart, $columnEnd, $styleArray);
 383  
 384              // clone each of the affected styles, apply the style array, and add the new styles to the workbook
 385              $workbook = $this->getActiveSheet()->getParentOrThrow();
 386              $newXfIndexes = [];
 387              foreach ($oldXfIndexes as $oldXfIndex => $dummy) {
 388                  $style = $workbook->getCellXfByIndex($oldXfIndex);
 389  
 390                  // $cachedStyles is set when applying style for a range of cells, either column or row
 391                  if (self::$cachedStyles === null) {
 392                      // Clone the old style and apply style-array
 393                      $newStyle = clone $style;
 394                      $newStyle->applyFromArray($styleArray);
 395  
 396                      // Look for existing style we can use instead (reduce memory usage)
 397                      $existingStyle = $workbook->getCellXfByHashCode($newStyle->getHashCode());
 398                  } else {
 399                      // Style cache is stored by Style::getHashCode(). But calling this method is
 400                      // expensive. So we cache the php obj id -> hash.
 401                      $objId = spl_object_id($style);
 402  
 403                      // Look for the original HashCode
 404                      $styleHash = self::$cachedStyles['hashByObjId'][$objId] ?? null;
 405                      if ($styleHash === null) {
 406                          // This object_id is not cached, store the hashcode in case encounter again
 407                          $styleHash = self::$cachedStyles['hashByObjId'][$objId] = $style->getHashCode();
 408                      }
 409  
 410                      // Find existing style by hash.
 411                      $existingStyle = self::$cachedStyles['styleByHash'][$styleHash] ?? null;
 412  
 413                      if (!$existingStyle) {
 414                          // The old style combined with the new style array is not cached, so we create it now
 415                          $newStyle = clone $style;
 416                          $newStyle->applyFromArray($styleArray);
 417  
 418                          // Look for similar style in workbook to reduce memory usage
 419                          $existingStyle = $workbook->getCellXfByHashCode($newStyle->getHashCode());
 420  
 421                          // Cache the new style by original hashcode
 422                          self::$cachedStyles['styleByHash'][$styleHash] = $existingStyle instanceof self ? $existingStyle : $newStyle;
 423                      }
 424                  }
 425  
 426                  if ($existingStyle) {
 427                      // there is already such cell Xf in our collection
 428                      $newXfIndexes[$oldXfIndex] = $existingStyle->getIndex();
 429                  } else {
 430                      if (!isset($newStyle)) {
 431                          // Handle bug in PHPStan, see https://github.com/phpstan/phpstan/issues/5805
 432                          // $newStyle should always be defined.
 433                          // This block might not be needed in the future
 434                          // @codeCoverageIgnoreStart
 435                          $newStyle = clone $style;
 436                          $newStyle->applyFromArray($styleArray);
 437                          // @codeCoverageIgnoreEnd
 438                      }
 439  
 440                      // we don't have such a cell Xf, need to add
 441                      $workbook->addCellXf($newStyle);
 442                      $newXfIndexes[$oldXfIndex] = $newStyle->getIndex();
 443                  }
 444              }
 445  
 446              // Loop through columns, rows, or cells again and update the XF index
 447              switch ($selectionType) {
 448                  case 'COLUMN':
 449                      for ($col = $rangeStartIndexes[0]; $col <= $rangeEndIndexes[0]; ++$col) {
 450                          $columnDimension = $this->getActiveSheet()->getColumnDimensionByColumn($col);
 451                          $oldXfIndex = $columnDimension->getXfIndex();
 452                          $columnDimension->setXfIndex($newXfIndexes[$oldXfIndex]);
 453                      }
 454  
 455                      // Disable caching of styles
 456                      self::$cachedStyles = null;
 457  
 458                      break;
 459                  case 'ROW':
 460                      for ($row = $rangeStartIndexes[1]; $row <= $rangeEndIndexes[1]; ++$row) {
 461                          $rowDimension = $this->getActiveSheet()->getRowDimension($row);
 462                          // row without explicit style should be formatted based on default style
 463                          $oldXfIndex = $rowDimension->getXfIndex() ?? 0;
 464                          $rowDimension->setXfIndex($newXfIndexes[$oldXfIndex]);
 465                      }
 466  
 467                      // Disable caching of styles
 468                      self::$cachedStyles = null;
 469  
 470                      break;
 471                  case 'CELL':
 472                      for ($col = $rangeStartIndexes[0]; $col <= $rangeEndIndexes[0]; ++$col) {
 473                          for ($row = $rangeStartIndexes[1]; $row <= $rangeEndIndexes[1]; ++$row) {
 474                              $cell = $this->getActiveSheet()->getCell([$col, $row]);
 475                              $oldXfIndex = $cell->getXfIndex();
 476                              $cell->setXfIndex($newXfIndexes[$oldXfIndex]);
 477                          }
 478                      }
 479  
 480                      break;
 481              }
 482          } else {
 483              // not a supervisor, just apply the style array directly on style object
 484              if (isset($styleArray['fill'])) {
 485                  $this->getFill()->applyFromArray($styleArray['fill']);
 486              }
 487              if (isset($styleArray['font'])) {
 488                  $this->getFont()->applyFromArray($styleArray['font']);
 489              }
 490              if (isset($styleArray['borders'])) {
 491                  $this->getBorders()->applyFromArray($styleArray['borders']);
 492              }
 493              if (isset($styleArray['alignment'])) {
 494                  $this->getAlignment()->applyFromArray($styleArray['alignment']);
 495              }
 496              if (isset($styleArray['numberFormat'])) {
 497                  $this->getNumberFormat()->applyFromArray($styleArray['numberFormat']);
 498              }
 499              if (isset($styleArray['protection'])) {
 500                  $this->getProtection()->applyFromArray($styleArray['protection']);
 501              }
 502              if (isset($styleArray['quotePrefix'])) {
 503                  $this->quotePrefix = $styleArray['quotePrefix'];
 504              }
 505          }
 506  
 507          return $this;
 508      }
 509  
 510      private function getOldXfIndexes(string $selectionType, array $rangeStart, array $rangeEnd, string $columnStart, string $columnEnd, array $styleArray): array
 511      {
 512          $oldXfIndexes = [];
 513          switch ($selectionType) {
 514              case 'COLUMN':
 515                  for ($col = $rangeStart[0]; $col <= $rangeEnd[0]; ++$col) {
 516                      $oldXfIndexes[$this->getActiveSheet()->getColumnDimensionByColumn($col)->getXfIndex()] = true;
 517                  }
 518                  foreach ($this->getActiveSheet()->getColumnIterator($columnStart, $columnEnd) as $columnIterator) {
 519                      $cellIterator = $columnIterator->getCellIterator();
 520                      $cellIterator->setIterateOnlyExistingCells(true);
 521                      foreach ($cellIterator as $columnCell) {
 522                          if ($columnCell !== null) {
 523                              $columnCell->getStyle()->applyFromArray($styleArray);
 524                          }
 525                      }
 526                  }
 527  
 528                  break;
 529              case 'ROW':
 530                  for ($row = $rangeStart[1]; $row <= $rangeEnd[1]; ++$row) {
 531                      if ($this->getActiveSheet()->getRowDimension($row)->getXfIndex() === null) {
 532                          $oldXfIndexes[0] = true; // row without explicit style should be formatted based on default style
 533                      } else {
 534                          $oldXfIndexes[$this->getActiveSheet()->getRowDimension($row)->getXfIndex()] = true;
 535                      }
 536                  }
 537                  foreach ($this->getActiveSheet()->getRowIterator((int) $rangeStart[1], (int) $rangeEnd[1]) as $rowIterator) {
 538                      $cellIterator = $rowIterator->getCellIterator();
 539                      $cellIterator->setIterateOnlyExistingCells(true);
 540                      foreach ($cellIterator as $rowCell) {
 541                          if ($rowCell !== null) {
 542                              $rowCell->getStyle()->applyFromArray($styleArray);
 543                          }
 544                      }
 545                  }
 546  
 547                  break;
 548              case 'CELL':
 549                  for ($col = $rangeStart[0]; $col <= $rangeEnd[0]; ++$col) {
 550                      for ($row = $rangeStart[1]; $row <= $rangeEnd[1]; ++$row) {
 551                          $oldXfIndexes[$this->getActiveSheet()->getCell([$col, $row])->getXfIndex()] = true;
 552                      }
 553                  }
 554  
 555                  break;
 556          }
 557  
 558          return $oldXfIndexes;
 559      }
 560  
 561      /**
 562       * Get Fill.
 563       *
 564       * @return Fill
 565       */
 566      public function getFill()
 567      {
 568          return $this->fill;
 569      }
 570  
 571      /**
 572       * Get Font.
 573       *
 574       * @return Font
 575       */
 576      public function getFont()
 577      {
 578          return $this->font;
 579      }
 580  
 581      /**
 582       * Set font.
 583       *
 584       * @return $this
 585       */
 586      public function setFont(Font $font)
 587      {
 588          $this->font = $font;
 589  
 590          return $this;
 591      }
 592  
 593      /**
 594       * Get Borders.
 595       *
 596       * @return Borders
 597       */
 598      public function getBorders()
 599      {
 600          return $this->borders;
 601      }
 602  
 603      /**
 604       * Get Alignment.
 605       *
 606       * @return Alignment
 607       */
 608      public function getAlignment()
 609      {
 610          return $this->alignment;
 611      }
 612  
 613      /**
 614       * Get Number Format.
 615       *
 616       * @return NumberFormat
 617       */
 618      public function getNumberFormat()
 619      {
 620          return $this->numberFormat;
 621      }
 622  
 623      /**
 624       * Get Conditional Styles. Only used on supervisor.
 625       *
 626       * @return Conditional[]
 627       */
 628      public function getConditionalStyles()
 629      {
 630          return $this->getActiveSheet()->getConditionalStyles($this->getActiveCell());
 631      }
 632  
 633      /**
 634       * Set Conditional Styles. Only used on supervisor.
 635       *
 636       * @param Conditional[] $conditionalStyleArray Array of conditional styles
 637       *
 638       * @return $this
 639       */
 640      public function setConditionalStyles(array $conditionalStyleArray)
 641      {
 642          $this->getActiveSheet()->setConditionalStyles($this->getSelectedCells(), $conditionalStyleArray);
 643  
 644          return $this;
 645      }
 646  
 647      /**
 648       * Get Protection.
 649       *
 650       * @return Protection
 651       */
 652      public function getProtection()
 653      {
 654          return $this->protection;
 655      }
 656  
 657      /**
 658       * Get quote prefix.
 659       *
 660       * @return bool
 661       */
 662      public function getQuotePrefix()
 663      {
 664          if ($this->isSupervisor) {
 665              return $this->getSharedComponent()->getQuotePrefix();
 666          }
 667  
 668          return $this->quotePrefix;
 669      }
 670  
 671      /**
 672       * Set quote prefix.
 673       *
 674       * @param bool $quotePrefix
 675       *
 676       * @return $this
 677       */
 678      public function setQuotePrefix($quotePrefix)
 679      {
 680          if ($quotePrefix == '') {
 681              $quotePrefix = false;
 682          }
 683          if ($this->isSupervisor) {
 684              $styleArray = ['quotePrefix' => $quotePrefix];
 685              $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
 686          } else {
 687              $this->quotePrefix = (bool) $quotePrefix;
 688          }
 689  
 690          return $this;
 691      }
 692  
 693      /**
 694       * Get hash code.
 695       *
 696       * @return string Hash code
 697       */
 698      public function getHashCode()
 699      {
 700          return md5(
 701              $this->fill->getHashCode() .
 702              $this->font->getHashCode() .
 703              $this->borders->getHashCode() .
 704              $this->alignment->getHashCode() .
 705              $this->numberFormat->getHashCode() .
 706              $this->protection->getHashCode() .
 707              ($this->quotePrefix ? 't' : 'f') .
 708              __CLASS__
 709          );
 710      }
 711  
 712      /**
 713       * Get own index in style collection.
 714       *
 715       * @return int
 716       */
 717      public function getIndex()
 718      {
 719          return $this->index;
 720      }
 721  
 722      /**
 723       * Set own index in style collection.
 724       *
 725       * @param int $index
 726       */
 727      public function setIndex($index): void
 728      {
 729          $this->index = $index;
 730      }
 731  
 732      protected function exportArray1(): array
 733      {
 734          $exportedArray = [];
 735          $this->exportArray2($exportedArray, 'alignment', $this->getAlignment());
 736          $this->exportArray2($exportedArray, 'borders', $this->getBorders());
 737          $this->exportArray2($exportedArray, 'fill', $this->getFill());
 738          $this->exportArray2($exportedArray, 'font', $this->getFont());
 739          $this->exportArray2($exportedArray, 'numberFormat', $this->getNumberFormat());
 740          $this->exportArray2($exportedArray, 'protection', $this->getProtection());
 741          $this->exportArray2($exportedArray, 'quotePrefx', $this->getQuotePrefix());
 742  
 743          return $exportedArray;
 744      }
 745  }