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.

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

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