Skip to content

Commit 1c587e9

Browse files
authored
Merge pull request #2972 from PHPOffice/TextFunctions-New
Allow multiple delimiters for `TEXTBEFORE()` and `TEXTAFTER()` functions
2 parents 641b6d0 + 290d073 commit 1c587e9

File tree

5 files changed

+119
-31
lines changed

5 files changed

+119
-31
lines changed

src/PhpSpreadsheet/Calculation/TextData/Extract.php

Lines changed: 37 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,8 @@ public static function right($value, $chars = 1)
104104
*
105105
* @param mixed $text the text that you're searching
106106
* Or can be an array of values
107-
* @param ?string $delimiter the text that marks the point before which you want to extract
107+
* @param null|array|string $delimiter the text that marks the point before which you want to extract
108+
* Multiple delimiters can be passed as an array of string values
108109
* @param mixed $instance The instance of the delimiter after which you want to extract the text.
109110
* By default, this is the first instance (1).
110111
* A negative value means start searching from the end of the text string.
@@ -132,7 +133,6 @@ public static function before($text, $delimiter, $instance = 1, $matchMode = 0,
132133
}
133134

134135
$text = Helpers::extractString($text ?? '');
135-
$delimiter = Helpers::extractString(Functions::flattenSingleValue($delimiter ?? ''));
136136
$instance = (int) $instance;
137137
$matchMode = (int) $matchMode;
138138
$matchEnd = (int) $matchEnd;
@@ -141,13 +141,14 @@ public static function before($text, $delimiter, $instance = 1, $matchMode = 0,
141141
if (is_array($split) === false) {
142142
return $split;
143143
}
144-
if ($delimiter === '') {
144+
if (Helpers::extractString(Functions::flattenSingleValue($delimiter ?? '')) === '') {
145145
return ($instance > 0) ? '' : $text;
146146
}
147147

148148
// Adjustment for a match as the first element of the split
149149
$flags = self::matchFlags($matchMode);
150-
$adjust = preg_match('/^' . preg_quote($delimiter) . "\$/{$flags}", $split[0]);
150+
$delimiter = self::buildDelimiter($delimiter);
151+
$adjust = preg_match('/^' . $delimiter . "\$/{$flags}", $split[0]);
151152
$oddReverseAdjustment = count($split) % 2;
152153

153154
$split = ($instance < 0)
@@ -161,7 +162,8 @@ public static function before($text, $delimiter, $instance = 1, $matchMode = 0,
161162
* TEXTAFTER.
162163
*
163164
* @param mixed $text the text that you're searching
164-
* @param ?string $delimiter the text that marks the point before which you want to extract
165+
* @param null|array|string $delimiter the text that marks the point before which you want to extract
166+
* Multiple delimiters can be passed as an array of string values
165167
* @param mixed $instance The instance of the delimiter after which you want to extract the text.
166168
* By default, this is the first instance (1).
167169
* A negative value means start searching from the end of the text string.
@@ -189,7 +191,6 @@ public static function after($text, $delimiter, $instance = 1, $matchMode = 0, $
189191
}
190192

191193
$text = Helpers::extractString($text ?? '');
192-
$delimiter = Helpers::extractString(Functions::flattenSingleValue($delimiter ?? ''));
193194
$instance = (int) $instance;
194195
$matchMode = (int) $matchMode;
195196
$matchEnd = (int) $matchEnd;
@@ -198,13 +199,14 @@ public static function after($text, $delimiter, $instance = 1, $matchMode = 0, $
198199
if (is_array($split) === false) {
199200
return $split;
200201
}
201-
if ($delimiter === '') {
202+
if (Helpers::extractString(Functions::flattenSingleValue($delimiter ?? '')) === '') {
202203
return ($instance < 0) ? '' : $text;
203204
}
204205

205206
// Adjustment for a match as the first element of the split
206207
$flags = self::matchFlags($matchMode);
207-
$adjust = preg_match('/^' . preg_quote($delimiter) . "\$/{$flags}", $split[0]);
208+
$delimiter = self::buildDelimiter($delimiter);
209+
$adjust = preg_match('/^' . $delimiter . "\$/{$flags}", $split[0]);
208210
$oddReverseAdjustment = count($split) % 2;
209211

210212
$split = ($instance < 0)
@@ -215,21 +217,23 @@ public static function after($text, $delimiter, $instance = 1, $matchMode = 0, $
215217
}
216218

217219
/**
220+
* @param null|array|string $delimiter
218221
* @param int $matchMode
219222
* @param int $matchEnd
220223
* @param mixed $ifNotFound
221224
*
222225
* @return string|string[]
223226
*/
224-
private static function validateTextBeforeAfter(string $text, string $delimiter, int $instance, $matchMode, $matchEnd, $ifNotFound)
227+
private static function validateTextBeforeAfter(string $text, $delimiter, int $instance, $matchMode, $matchEnd, $ifNotFound)
225228
{
226229
$flags = self::matchFlags($matchMode);
230+
$delimiter = self::buildDelimiter($delimiter);
227231

228-
if (preg_match('/' . preg_quote($delimiter) . "/{$flags}", $text) === 0 && $matchEnd === 0) {
232+
if (preg_match('/' . $delimiter . "/{$flags}", $text) === 0 && $matchEnd === 0) {
229233
return $ifNotFound;
230234
}
231235

232-
$split = preg_split('/(' . preg_quote($delimiter) . ")/{$flags}", $text, 0, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE);
236+
$split = preg_split('/' . $delimiter . "/{$flags}", $text, 0, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE);
233237
if ($split === false) {
234238
return ExcelError::NA();
235239
}
@@ -247,6 +251,28 @@ private static function validateTextBeforeAfter(string $text, string $delimiter,
247251
return $split;
248252
}
249253

254+
/**
255+
* @param null|array|string $delimiter the text that marks the point before which you want to extract
256+
* Multiple delimiters can be passed as an array of string values
257+
*/
258+
private static function buildDelimiter($delimiter): string
259+
{
260+
if (is_array($delimiter)) {
261+
$delimiter = Functions::flattenArray($delimiter);
262+
$quotedDelimiters = array_map(
263+
function ($delimiter) {
264+
return preg_quote($delimiter ?? '');
265+
},
266+
$delimiter
267+
);
268+
$delimiters = implode('|', $quotedDelimiters);
269+
270+
return '(' . $delimiters . ')';
271+
}
272+
273+
return '(' . preg_quote($delimiter ?? '') . ')';
274+
}
275+
250276
private static function matchFlags(int $matchMode): string
251277
{
252278
return ($matchMode === 0) ? 'mu' : 'miu';

tests/PhpSpreadsheetTests/Calculation/Functions/TextData/TextAfterTest.php

Lines changed: 5 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@
22

33
namespace PhpOffice\PhpSpreadsheetTests\Calculation\Functions\TextData;
44

5-
use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
6-
75
class TextAfterTest extends AllSetupTeardown
86
{
97
/**
@@ -14,14 +12,17 @@ public function testTextAfter(string $expectedResult, array $arguments): void
1412
$text = $arguments[0];
1513
$delimiter = $arguments[1];
1614

17-
$args = 'A1, A2';
15+
$args = (is_array($delimiter)) ? 'A1, {A2,A3}' : 'A1, A2';
1816
$args .= (isset($arguments[2])) ? ", {$arguments[2]}" : ',';
1917
$args .= (isset($arguments[3])) ? ", {$arguments[3]}" : ',';
2018
$args .= (isset($arguments[4])) ? ", {$arguments[4]}" : ',';
2119

2220
$worksheet = $this->getSheet();
2321
$worksheet->getCell('A1')->setValue($text);
24-
$worksheet->getCell('A2')->setValue($delimiter);
22+
$worksheet->getCell('A2')->setValue((is_array($delimiter)) ? $delimiter[0] : $delimiter);
23+
if (is_array($delimiter)) {
24+
$worksheet->getCell('A3')->setValue($delimiter[1]);
25+
}
2526
$worksheet->getCell('B1')->setValue("=TEXTAFTER({$args})");
2627

2728
$result = $worksheet->getCell('B1')->getCalculatedValue();
@@ -32,18 +33,4 @@ public function providerTEXTAFTER(): array
3233
{
3334
return require 'tests/data/Calculation/TextData/TEXTAFTER.php';
3435
}
35-
36-
public function testTextAfterWithArray(): void
37-
{
38-
$calculation = Calculation::getInstance();
39-
40-
$text = "Red Riding Hood's red riding hood";
41-
$delimiter = 'red';
42-
43-
$args = "\"{$text}\", \"{$delimiter}\", 1, {0;1}";
44-
45-
$formula = "=TEXTAFTER({$args})";
46-
$result = $calculation->_calculateFormulaValue($formula);
47-
self::assertEquals([[' riding hood'], [" Riding Hood's red riding hood"]], $result);
48-
}
4936
}

tests/PhpSpreadsheetTests/Calculation/Functions/TextData/TextBeforeTest.php

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,17 @@ public function testTextBefore(string $expectedResult, array $arguments): void
1212
$text = $arguments[0];
1313
$delimiter = $arguments[1];
1414

15-
$args = 'A1, A2';
15+
$args = (is_array($delimiter)) ? 'A1, {A2,A3}' : 'A1, A2';
1616
$args .= (isset($arguments[2])) ? ", {$arguments[2]}" : ',';
1717
$args .= (isset($arguments[3])) ? ", {$arguments[3]}" : ',';
1818
$args .= (isset($arguments[4])) ? ", {$arguments[4]}" : ',';
1919

2020
$worksheet = $this->getSheet();
2121
$worksheet->getCell('A1')->setValue($text);
22-
$worksheet->getCell('A2')->setValue($delimiter);
22+
$worksheet->getCell('A2')->setValue((is_array($delimiter)) ? $delimiter[0] : $delimiter);
23+
if (is_array($delimiter)) {
24+
$worksheet->getCell('A3')->setValue($delimiter[1]);
25+
}
2326
$worksheet->getCell('B1')->setValue("=TEXTBEFORE({$args})");
2427

2528
$result = $worksheet->getCell('B1')->getCalculatedValue();

tests/data/Calculation/TextData/TEXTAFTER.php

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,4 +212,40 @@
212212
1,
213213
],
214214
],
215+
'Multi-delimiter Case-Insensitive Offset 1' => [
216+
" riding hood's red riding hood",
217+
[
218+
"Little Red riding hood's red riding hood",
219+
['HOOD', 'RED'],
220+
1,
221+
1,
222+
],
223+
],
224+
'Multi-delimiter Case-Insensitive Offset 2' => [
225+
"'s red riding hood",
226+
[
227+
"Little Red riding hood's red riding hood",
228+
['HOOD', 'RED'],
229+
2,
230+
1,
231+
],
232+
],
233+
'Multi-delimiter Case-Insensitive Offset 3' => [
234+
' riding hood',
235+
[
236+
"Little Red riding hood's red riding hood",
237+
['HOOD', 'RED'],
238+
3,
239+
1,
240+
],
241+
],
242+
'Multi-delimiter Case-Insensitive Offset -2' => [
243+
' riding hood',
244+
[
245+
"Little Red riding hood's red riding hood",
246+
['HOOD', 'RED'],
247+
-2,
248+
1,
249+
],
250+
],
215251
];

tests/data/Calculation/TextData/TEXTBEFORE.php

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,4 +204,40 @@
204204
1,
205205
],
206206
],
207+
'Multi-delimiter Case-Insensitive Offset 1' => [
208+
'Little ',
209+
[
210+
"Little Red riding hood's red riding hood",
211+
['HOOD', 'RED'],
212+
1,
213+
1,
214+
],
215+
],
216+
'Multi-delimiter Case-Insensitive Offset 2' => [
217+
'Little Red riding ',
218+
[
219+
"Little Red riding hood's red riding hood",
220+
['HOOD', 'RED'],
221+
2,
222+
1,
223+
],
224+
],
225+
'Multi-delimiter Case-Insensitive Offset 3' => [
226+
"Little Red riding hood's ",
227+
[
228+
"Little Red riding hood's red riding hood",
229+
['HOOD', 'RED'],
230+
3,
231+
1,
232+
],
233+
],
234+
'Multi-delimiter Case-Insensitive Offset -2' => [
235+
"Little Red riding hood's ",
236+
[
237+
"Little Red riding hood's red riding hood",
238+
['HOOD', 'RED'],
239+
-2,
240+
1,
241+
],
242+
],
207243
];

0 commit comments

Comments
 (0)