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