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

   1  <?php
   2  
   3  namespace PhpOffice\PhpSpreadsheet\Calculation\TextData;
   4  
   5  use PhpOffice\PhpSpreadsheet\Calculation\ArrayEnabled;
   6  use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
   7  use PhpOffice\PhpSpreadsheet\Calculation\Functions;
   8  use PhpOffice\PhpSpreadsheet\Calculation\Information\ErrorValue;
   9  
  10  class Text
  11  {
  12      use ArrayEnabled;
  13  
  14      /**
  15       * LEN.
  16       *
  17       * @param mixed $value String Value
  18       *                         Or can be an array of values
  19       *
  20       * @return array|int
  21       *         If an array of values is passed for the argument, then the returned result
  22       *            will also be an array with matching dimensions
  23       */
  24      public static function length($value = '')
  25      {
  26          if (is_array($value)) {
  27              return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $value);
  28          }
  29  
  30          $value = Helpers::extractString($value);
  31  
  32          return mb_strlen($value, 'UTF-8');
  33      }
  34  
  35      /**
  36       * Compares two text strings and returns TRUE if they are exactly the same, FALSE otherwise.
  37       * EXACT is case-sensitive but ignores formatting differences.
  38       * Use EXACT to test text being entered into a document.
  39       *
  40       * @param mixed $value1 String Value
  41       *                         Or can be an array of values
  42       * @param mixed $value2 String Value
  43       *                         Or can be an array of values
  44       *
  45       * @return array|bool
  46       *         If an array of values is passed for either of the arguments, then the returned result
  47       *            will also be an array with matching dimensions
  48       */
  49      public static function exact($value1, $value2)
  50      {
  51          if (is_array($value1) || is_array($value2)) {
  52              return self::evaluateArrayArguments([self::class, __FUNCTION__], $value1, $value2);
  53          }
  54  
  55          $value1 = Helpers::extractString($value1);
  56          $value2 = Helpers::extractString($value2);
  57  
  58          return $value2 === $value1;
  59      }
  60  
  61      /**
  62       * T.
  63       *
  64       * @param mixed $testValue Value to check
  65       *                         Or can be an array of values
  66       *
  67       * @return array|string
  68       *         If an array of values is passed for the argument, then the returned result
  69       *            will also be an array with matching dimensions
  70       */
  71      public static function test($testValue = '')
  72      {
  73          if (is_array($testValue)) {
  74              return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $testValue);
  75          }
  76  
  77          if (is_string($testValue)) {
  78              return $testValue;
  79          }
  80  
  81          return '';
  82      }
  83  
  84      /**
  85       * TEXTSPLIT.
  86       *
  87       * @param mixed $text the text that you're searching
  88       * @param null|array|string $columnDelimiter The text that marks the point where to spill the text across columns.
  89       *                          Multiple delimiters can be passed as an array of string values
  90       * @param null|array|string $rowDelimiter The text that marks the point where to spill the text down rows.
  91       *                          Multiple delimiters can be passed as an array of string values
  92       * @param bool $ignoreEmpty Specify FALSE to create an empty cell when two delimiters are consecutive.
  93       *                              true = create empty cells
  94       *                              false = skip empty cells
  95       *                              Defaults to TRUE, which creates an empty cell
  96       * @param bool $matchMode Determines whether the match is case-sensitive or not.
  97       *                              true = case-sensitive
  98       *                              false = case-insensitive
  99       *                         By default, a case-sensitive match is done.
 100       * @param mixed $padding The value with which to pad the result.
 101       *                              The default is #N/A.
 102       *
 103       * @return array the array built from the text, split by the row and column delimiters
 104       */
 105      public static function split($text, $columnDelimiter = null, $rowDelimiter = null, bool $ignoreEmpty = false, bool $matchMode = true, $padding = '#N/A')
 106      {
 107          $text = Functions::flattenSingleValue($text);
 108  
 109          $flags = self::matchFlags($matchMode);
 110  
 111          if ($rowDelimiter !== null) {
 112              $delimiter = self::buildDelimiter($rowDelimiter);
 113              $rows = ($delimiter === '()')
 114                  ? [$text]
 115                  : preg_split("/{$delimiter}/{$flags}", $text);
 116          } else {
 117              $rows = [$text];
 118          }
 119  
 120          /** @var array $rows */
 121          if ($ignoreEmpty === true) {
 122              $rows = array_values(array_filter(
 123                  $rows,
 124                  function ($row) {
 125                      return $row !== '';
 126                  }
 127              ));
 128          }
 129  
 130          if ($columnDelimiter !== null) {
 131              $delimiter = self::buildDelimiter($columnDelimiter);
 132              array_walk(
 133                  $rows,
 134                  function (&$row) use ($delimiter, $flags, $ignoreEmpty): void {
 135                      $row = ($delimiter === '()')
 136                          ? [$row]
 137                          : preg_split("/{$delimiter}/{$flags}", $row);
 138                      /** @var array $row */
 139                      if ($ignoreEmpty === true) {
 140                          $row = array_values(array_filter(
 141                              $row,
 142                              function ($value) {
 143                                  return $value !== '';
 144                              }
 145                          ));
 146                      }
 147                  }
 148              );
 149              if ($ignoreEmpty === true) {
 150                  $rows = array_values(array_filter(
 151                      $rows,
 152                      function ($row) {
 153                          return $row !== [] && $row !== [''];
 154                      }
 155                  ));
 156              }
 157          }
 158  
 159          return self::applyPadding($rows, $padding);
 160      }
 161  
 162      /**
 163       * @param mixed $padding
 164       */
 165      private static function applyPadding(array $rows, $padding): array
 166      {
 167          $columnCount = array_reduce(
 168              $rows,
 169              function (int $counter, array $row): int {
 170                  return max($counter, count($row));
 171              },
 172              0
 173          );
 174  
 175          return array_map(
 176              function (array $row) use ($columnCount, $padding): array {
 177                  return (count($row) < $columnCount)
 178                      ? array_merge($row, array_fill(0, $columnCount - count($row), $padding))
 179                      : $row;
 180              },
 181              $rows
 182          );
 183      }
 184  
 185      /**
 186       * @param null|array|string $delimiter the text that marks the point before which you want to split
 187       *                                 Multiple delimiters can be passed as an array of string values
 188       */
 189      private static function buildDelimiter($delimiter): string
 190      {
 191          $valueSet = Functions::flattenArray($delimiter);
 192  
 193          if (is_array($delimiter) && count($valueSet) > 1) {
 194              $quotedDelimiters = array_map(
 195                  function ($delimiter) {
 196                      return preg_quote($delimiter ?? '', '/');
 197                  },
 198                  $valueSet
 199              );
 200              $delimiters = implode('|', $quotedDelimiters);
 201  
 202              return '(' . $delimiters . ')';
 203          }
 204  
 205          return '(' . preg_quote(/** @scrutinizer ignore-type */ Functions::flattenSingleValue($delimiter), '/') . ')';
 206      }
 207  
 208      private static function matchFlags(bool $matchMode): string
 209      {
 210          return ($matchMode === true) ? 'miu' : 'mu';
 211      }
 212  
 213      public static function fromArray(array $array, int $format = 0): string
 214      {
 215          $result = [];
 216          foreach ($array as $row) {
 217              $cells = [];
 218              foreach ($row as $cellValue) {
 219                  $value = ($format === 1) ? self::formatValueMode1($cellValue) : self::formatValueMode0($cellValue);
 220                  $cells[] = $value;
 221              }
 222              $result[] = implode(($format === 1) ? ',' : ', ', $cells);
 223          }
 224  
 225          $result = implode(($format === 1) ? ';' : ', ', $result);
 226  
 227          return ($format === 1) ? '{' . $result . '}' : $result;
 228      }
 229  
 230      /**
 231       * @param mixed $cellValue
 232       */
 233      private static function formatValueMode0($cellValue): string
 234      {
 235          if (is_bool($cellValue)) {
 236              return Calculation::getLocaleBoolean($cellValue ? 'TRUE' : 'FALSE');
 237          }
 238  
 239          return (string) $cellValue;
 240      }
 241  
 242      /**
 243       * @param mixed $cellValue
 244       */
 245      private static function formatValueMode1($cellValue): string
 246      {
 247          if (is_string($cellValue) && ErrorValue::isError($cellValue) === false) {
 248              return Calculation::FORMULA_STRING_QUOTE . $cellValue . Calculation::FORMULA_STRING_QUOTE;
 249          } elseif (is_bool($cellValue)) {
 250              return Calculation::getLocaleBoolean($cellValue ? 'TRUE' : 'FALSE');
 251          }
 252  
 253          return (string) $cellValue;
 254      }
 255  }