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 39 and 401]

   1  <?php
   2  
   3  namespace Box\Spout\Writer\Common\Manager;
   4  
   5  use Box\Spout\Common\Helper\StringHelper;
   6  use Box\Spout\Writer\Common\Entity\Sheet;
   7  use Box\Spout\Writer\Exception\InvalidSheetNameException;
   8  
   9  /**
  10   * Class SheetManager
  11   * Sheet manager
  12   */
  13  class SheetManager
  14  {
  15      /** Sheet name should not exceed 31 characters */
  16      const MAX_LENGTH_SHEET_NAME = 31;
  17  
  18      /** @var array Invalid characters that cannot be contained in the sheet name */
  19      private static $INVALID_CHARACTERS_IN_SHEET_NAME = ['\\', '/', '?', '*', ':', '[', ']'];
  20  
  21      /** @var array Associative array [WORKBOOK_ID] => [[SHEET_INDEX] => [SHEET_NAME]] keeping track of sheets' name to enforce uniqueness per workbook */
  22      private static $SHEETS_NAME_USED = [];
  23  
  24      /** @var StringHelper */
  25      private $stringHelper;
  26  
  27      /**
  28       * SheetManager constructor.
  29       *
  30       * @param StringHelper $stringHelper
  31       */
  32      public function __construct(StringHelper $stringHelper)
  33      {
  34          $this->stringHelper = $stringHelper;
  35      }
  36  
  37      /**
  38       * Throws an exception if the given sheet's name is not valid.
  39       * @see Sheet::setName for validity rules.
  40       *
  41       * @param string $name
  42       * @param Sheet $sheet The sheet whose future name is checked
  43       * @throws \Box\Spout\Writer\Exception\InvalidSheetNameException If the sheet's name is invalid.
  44       * @return void
  45       */
  46      public function throwIfNameIsInvalid($name, Sheet $sheet)
  47      {
  48          if (!\is_string($name)) {
  49              $actualType = \gettype($name);
  50              $errorMessage = "The sheet's name is invalid. It must be a string ($actualType given).";
  51              throw new InvalidSheetNameException($errorMessage);
  52          }
  53  
  54          $failedRequirements = [];
  55          $nameLength = $this->stringHelper->getStringLength($name);
  56  
  57          if (!$this->isNameUnique($name, $sheet)) {
  58              $failedRequirements[] = 'It should be unique';
  59          } else {
  60              if ($nameLength === 0) {
  61                  $failedRequirements[] = 'It should not be blank';
  62              } else {
  63                  if ($nameLength > self::MAX_LENGTH_SHEET_NAME) {
  64                      $failedRequirements[] = 'It should not exceed 31 characters';
  65                  }
  66  
  67                  if ($this->doesContainInvalidCharacters($name)) {
  68                      $failedRequirements[] = 'It should not contain these characters: \\ / ? * : [ or ]';
  69                  }
  70  
  71                  if ($this->doesStartOrEndWithSingleQuote($name)) {
  72                      $failedRequirements[] = 'It should not start or end with a single quote';
  73                  }
  74              }
  75          }
  76  
  77          if (\count($failedRequirements) !== 0) {
  78              $errorMessage = "The sheet's name (\"$name\") is invalid. It did not respect these rules:\n - ";
  79              $errorMessage .= \implode("\n - ", $failedRequirements);
  80              throw new InvalidSheetNameException($errorMessage);
  81          }
  82      }
  83  
  84      /**
  85       * Returns whether the given name contains at least one invalid character.
  86       * @see Sheet::$INVALID_CHARACTERS_IN_SHEET_NAME for the full list.
  87       *
  88       * @param string $name
  89       * @return bool TRUE if the name contains invalid characters, FALSE otherwise.
  90       */
  91      private function doesContainInvalidCharacters($name)
  92      {
  93          return (\str_replace(self::$INVALID_CHARACTERS_IN_SHEET_NAME, '', $name) !== $name);
  94      }
  95  
  96      /**
  97       * Returns whether the given name starts or ends with a single quote
  98       *
  99       * @param string $name
 100       * @return bool TRUE if the name starts or ends with a single quote, FALSE otherwise.
 101       */
 102      private function doesStartOrEndWithSingleQuote($name)
 103      {
 104          $startsWithSingleQuote = ($this->stringHelper->getCharFirstOccurrencePosition('\'', $name) === 0);
 105          $endsWithSingleQuote = ($this->stringHelper->getCharLastOccurrencePosition('\'', $name) === ($this->stringHelper->getStringLength($name) - 1));
 106  
 107          return ($startsWithSingleQuote || $endsWithSingleQuote);
 108      }
 109  
 110      /**
 111       * Returns whether the given name is unique.
 112       *
 113       * @param string $name
 114       * @param Sheet $sheet The sheet whose future name is checked
 115       * @return bool TRUE if the name is unique, FALSE otherwise.
 116       */
 117      private function isNameUnique($name, Sheet $sheet)
 118      {
 119          foreach (self::$SHEETS_NAME_USED[$sheet->getAssociatedWorkbookId()] as $sheetIndex => $sheetName) {
 120              if ($sheetIndex !== $sheet->getIndex() && $sheetName === $name) {
 121                  return false;
 122              }
 123          }
 124  
 125          return true;
 126      }
 127  
 128      /**
 129       * @param int $workbookId Workbook ID associated to a Sheet
 130       * @return void
 131       */
 132      public function markWorkbookIdAsUsed($workbookId)
 133      {
 134          if (!isset(self::$SHEETS_NAME_USED[$workbookId])) {
 135              self::$SHEETS_NAME_USED[$workbookId] = [];
 136          }
 137      }
 138  
 139      /**
 140       * @param Sheet $sheet
 141       * @return void
 142       */
 143      public function markSheetNameAsUsed(Sheet $sheet)
 144      {
 145          self::$SHEETS_NAME_USED[$sheet->getAssociatedWorkbookId()][$sheet->getIndex()] = $sheet->getName();
 146      }
 147  }