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