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
|
45 |
|
public static function length($value = '') |
25
|
|
|
{ |
26
|
45 |
|
if (is_array($value)) { |
27
|
8 |
|
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $value); |
28
|
|
|
} |
29
|
|
|
|
30
|
45 |
|
$value = Helpers::extractString($value); |
31
|
|
|
|
32
|
45 |
|
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
|
13 |
|
public static function exact($value1, $value2) |
50
|
|
|
{ |
51
|
13 |
|
if (is_array($value1) || is_array($value2)) { |
52
|
10 |
|
return self::evaluateArrayArguments([self::class, __FUNCTION__], $value1, $value2); |
53
|
|
|
} |
54
|
|
|
|
55
|
13 |
|
$value1 = Helpers::extractString($value1); |
56
|
13 |
|
$value2 = Helpers::extractString($value2); |
57
|
|
|
|
58
|
13 |
|
return $value2 === $value1; |
59
|
|
|
} |
60
|
|
|
|
61
|
|
|
/** |
62
|
|
|
* RETURNSTRING. |
63
|
|
|
* |
64
|
|
|
* @param mixed $testValue Value to check |
65
|
|
|
* Or can be an array of values |
66
|
|
|
* |
67
|
|
|
* @return null|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
|
8 |
|
public static function test($testValue = '') |
72
|
|
|
{ |
73
|
8 |
|
if (is_array($testValue)) { |
74
|
3 |
|
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $testValue); |
75
|
|
|
} |
76
|
|
|
|
77
|
8 |
|
if (is_string($testValue)) { |
78
|
5 |
|
return $testValue; |
79
|
|
|
} |
80
|
|
|
|
81
|
6 |
|
return null; |
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
|
10 |
|
public static function split($text, $columnDelimiter = null, $rowDelimiter = null, bool $ignoreEmpty = false, bool $matchMode = true, $padding = '#N/A') |
106
|
|
|
{ |
107
|
10 |
|
$text = Functions::flattenSingleValue($text); |
108
|
|
|
|
109
|
10 |
|
$flags = self::matchFlags($matchMode); |
110
|
|
|
|
111
|
10 |
|
if ($rowDelimiter !== null) { |
112
|
10 |
|
$delimiter = self::buildDelimiter($rowDelimiter); |
113
|
10 |
|
$rows = ($delimiter === '()') |
114
|
4 |
|
? [$text] |
115
|
10 |
|
: preg_split("/{$delimiter}/{$flags}", $text); |
116
|
|
|
} else { |
117
|
|
|
$rows = [$text]; |
118
|
|
|
} |
119
|
|
|
|
120
|
|
|
/** @var array $rows */ |
121
|
10 |
|
if ($ignoreEmpty === true) { |
122
|
1 |
|
$rows = array_values(array_filter( |
123
|
|
|
$rows, |
124
|
1 |
|
function ($row) { |
125
|
1 |
|
return $row !== ''; |
126
|
|
|
} |
127
|
|
|
)); |
128
|
|
|
} |
129
|
|
|
|
130
|
10 |
|
if ($columnDelimiter !== null) { |
131
|
10 |
|
$delimiter = self::buildDelimiter($columnDelimiter); |
132
|
10 |
|
array_walk( |
133
|
|
|
$rows, |
134
|
10 |
|
function (&$row) use ($delimiter, $flags, $ignoreEmpty): void { |
135
|
10 |
|
$row = ($delimiter === '()') |
136
|
2 |
|
? [$row] |
137
|
10 |
|
: preg_split("/{$delimiter}/{$flags}", $row); |
138
|
|
|
/** @var array $row */ |
139
|
10 |
|
if ($ignoreEmpty === true) { |
140
|
1 |
|
$row = array_values(array_filter( |
141
|
|
|
$row, |
142
|
1 |
|
function ($value) { |
143
|
1 |
|
return $value !== ''; |
144
|
|
|
} |
145
|
|
|
)); |
146
|
|
|
} |
147
|
|
|
} |
148
|
|
|
); |
149
|
10 |
|
if ($ignoreEmpty === true) { |
150
|
1 |
|
$rows = array_values(array_filter( |
151
|
|
|
$rows, |
152
|
1 |
|
function ($row) { |
153
|
1 |
|
return $row !== [] && $row !== ['']; |
154
|
|
|
} |
155
|
|
|
)); |
156
|
|
|
} |
157
|
|
|
} |
158
|
|
|
|
159
|
10 |
|
return self::applyPadding($rows, $padding); |
160
|
|
|
} |
161
|
|
|
|
162
|
|
|
/** |
163
|
|
|
* @param mixed $padding |
164
|
|
|
*/ |
165
|
10 |
|
private static function applyPadding(array $rows, $padding): array |
166
|
|
|
{ |
167
|
10 |
|
$columnCount = array_reduce( |
168
|
|
|
$rows, |
169
|
10 |
|
function (int $counter, array $row): int { |
170
|
10 |
|
return max($counter, count($row)); |
171
|
|
|
}, |
172
|
|
|
0 |
173
|
|
|
); |
174
|
|
|
|
175
|
10 |
|
return array_map( |
176
|
10 |
|
function (array $row) use ($columnCount, $padding): array { |
177
|
10 |
|
return (count($row) < $columnCount) |
178
|
2 |
|
? array_merge($row, array_fill(0, $columnCount - count($row), $padding)) |
179
|
10 |
|
: $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
|
10 |
|
private static function buildDelimiter($delimiter): string |
190
|
|
|
{ |
191
|
10 |
|
$valueSet = Functions::flattenArray($delimiter); |
192
|
|
|
|
193
|
10 |
|
if (is_array($delimiter) && count($valueSet) > 1) { |
194
|
3 |
|
$quotedDelimiters = array_map( |
195
|
3 |
|
function ($delimiter) { |
196
|
3 |
|
return preg_quote($delimiter ?? ''); |
197
|
|
|
}, |
198
|
|
|
$valueSet |
199
|
|
|
); |
200
|
3 |
|
$delimiters = implode('|', $quotedDelimiters); |
201
|
|
|
|
202
|
3 |
|
return '(' . $delimiters . ')'; |
203
|
|
|
} |
204
|
|
|
|
205
|
10 |
|
return '(' . preg_quote(Functions::flattenSingleValue($delimiter)) . ')'; |
|
|
|
|
206
|
|
|
} |
207
|
|
|
|
208
|
10 |
|
private static function matchFlags(bool $matchMode): string |
209
|
|
|
{ |
210
|
10 |
|
return ($matchMode === true) ? 'miu' : 'mu'; |
211
|
|
|
} |
212
|
|
|
|
213
|
2 |
|
public static function fromArray(array $array, int $format = 0): string |
214
|
|
|
{ |
215
|
2 |
|
$result = []; |
216
|
2 |
|
foreach ($array as $row) { |
217
|
2 |
|
$cells = []; |
218
|
2 |
|
foreach ($row as $cellValue) { |
219
|
2 |
|
$value = ($format === 1) ? self::formatValueMode1($cellValue) : self::formatValueMode0($cellValue); |
220
|
2 |
|
$cells[] = $value; |
221
|
|
|
} |
222
|
2 |
|
$result[] = implode(($format === 1) ? ',' : ', ', $cells); |
223
|
|
|
} |
224
|
|
|
|
225
|
2 |
|
$result = implode(($format === 1) ? ';' : ', ', $result); |
226
|
|
|
|
227
|
2 |
|
return ($format === 1) ? '{' . $result . '}' : $result; |
228
|
|
|
} |
229
|
|
|
|
230
|
|
|
/** |
231
|
|
|
* @param mixed $cellValue |
232
|
|
|
*/ |
233
|
1 |
|
private static function formatValueMode0($cellValue): string |
234
|
|
|
{ |
235
|
1 |
|
if (is_bool($cellValue)) { |
236
|
1 |
|
return Calculation::getLocaleBoolean($cellValue ? 'TRUE' : 'FALSE'); |
237
|
|
|
} |
238
|
|
|
|
239
|
1 |
|
return (string) $cellValue; |
240
|
|
|
} |
241
|
|
|
|
242
|
|
|
/** |
243
|
|
|
* @param mixed $cellValue |
244
|
|
|
*/ |
245
|
1 |
|
private static function formatValueMode1($cellValue): string |
246
|
|
|
{ |
247
|
1 |
|
if (is_string($cellValue) && ErrorValue::isError($cellValue) === false) { |
248
|
1 |
|
return Calculation::FORMULA_STRING_QUOTE . $cellValue . Calculation::FORMULA_STRING_QUOTE; |
249
|
1 |
|
} elseif (is_bool($cellValue)) { |
250
|
1 |
|
return Calculation::getLocaleBoolean($cellValue ? 'TRUE' : 'FALSE'); |
251
|
|
|
} |
252
|
|
|
|
253
|
1 |
|
return (string) $cellValue; |
254
|
|
|
} |
255
|
|
|
} |
256
|
|
|
|