diff --git a/src/PhpSpreadsheet/Calculation/TextData/Extract.php b/src/PhpSpreadsheet/Calculation/TextData/Extract.php index 1a9e84db..ee7e31b7 100644 --- a/src/PhpSpreadsheet/Calculation/TextData/Extract.php +++ b/src/PhpSpreadsheet/Calculation/TextData/Extract.php @@ -104,7 +104,8 @@ class Extract * * @param mixed $text the text that you're searching * Or can be an array of values - * @param ?string $delimiter the text that marks the point before which you want to extract + * @param null|array|string $delimiter the text that marks the point before which you want to extract + * Multiple delimiters can be passed as an array of string values * @param mixed $instance The instance of the delimiter after which you want to extract the text. * By default, this is the first instance (1). * A negative value means start searching from the end of the text string. @@ -132,7 +133,6 @@ class Extract } $text = Helpers::extractString($text ?? ''); - $delimiter = Helpers::extractString(Functions::flattenSingleValue($delimiter ?? '')); $instance = (int) $instance; $matchMode = (int) $matchMode; $matchEnd = (int) $matchEnd; @@ -141,13 +141,14 @@ class Extract if (is_array($split) === false) { return $split; } - if ($delimiter === '') { + if (Helpers::extractString(Functions::flattenSingleValue($delimiter ?? '')) === '') { return ($instance > 0) ? '' : $text; } // Adjustment for a match as the first element of the split $flags = self::matchFlags($matchMode); - $adjust = preg_match('/^' . preg_quote($delimiter) . "\$/{$flags}", $split[0]); + $delimiter = self::buildDelimiter($delimiter); + $adjust = preg_match('/^' . $delimiter . "\$/{$flags}", $split[0]); $oddReverseAdjustment = count($split) % 2; $split = ($instance < 0) @@ -161,7 +162,8 @@ class Extract * TEXTAFTER. * * @param mixed $text the text that you're searching - * @param ?string $delimiter the text that marks the point before which you want to extract + * @param null|array|string $delimiter the text that marks the point before which you want to extract + * Multiple delimiters can be passed as an array of string values * @param mixed $instance The instance of the delimiter after which you want to extract the text. * By default, this is the first instance (1). * A negative value means start searching from the end of the text string. @@ -189,7 +191,6 @@ class Extract } $text = Helpers::extractString($text ?? ''); - $delimiter = Helpers::extractString(Functions::flattenSingleValue($delimiter ?? '')); $instance = (int) $instance; $matchMode = (int) $matchMode; $matchEnd = (int) $matchEnd; @@ -198,13 +199,14 @@ class Extract if (is_array($split) === false) { return $split; } - if ($delimiter === '') { + if (Helpers::extractString(Functions::flattenSingleValue($delimiter ?? '')) === '') { return ($instance < 0) ? '' : $text; } // Adjustment for a match as the first element of the split $flags = self::matchFlags($matchMode); - $adjust = preg_match('/^' . preg_quote($delimiter) . "\$/{$flags}", $split[0]); + $delimiter = self::buildDelimiter($delimiter); + $adjust = preg_match('/^' . $delimiter . "\$/{$flags}", $split[0]); $oddReverseAdjustment = count($split) % 2; $split = ($instance < 0) @@ -215,21 +217,23 @@ class Extract } /** + * @param null|array|string $delimiter * @param int $matchMode * @param int $matchEnd * @param mixed $ifNotFound * * @return string|string[] */ - private static function validateTextBeforeAfter(string $text, string $delimiter, int $instance, $matchMode, $matchEnd, $ifNotFound) + private static function validateTextBeforeAfter(string $text, $delimiter, int $instance, $matchMode, $matchEnd, $ifNotFound) { $flags = self::matchFlags($matchMode); + $delimiter = self::buildDelimiter($delimiter); - if (preg_match('/' . preg_quote($delimiter) . "/{$flags}", $text) === 0 && $matchEnd === 0) { + if (preg_match('/' . $delimiter . "/{$flags}", $text) === 0 && $matchEnd === 0) { return $ifNotFound; } - $split = preg_split('/(' . preg_quote($delimiter) . ")/{$flags}", $text, 0, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE); + $split = preg_split('/' . $delimiter . "/{$flags}", $text, 0, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE); if ($split === false) { return ExcelError::NA(); } @@ -247,6 +251,28 @@ class Extract return $split; } + /** + * @param null|array|string $delimiter the text that marks the point before which you want to extract + * Multiple delimiters can be passed as an array of string values + */ + private static function buildDelimiter($delimiter): string + { + if (is_array($delimiter)) { + $delimiter = Functions::flattenArray($delimiter); + $quotedDelimiters = array_map( + function ($delimiter) { + return preg_quote($delimiter ?? ''); + }, + $delimiter + ); + $delimiters = implode('|', $quotedDelimiters); + + return '(' . $delimiters . ')'; + } + + return '(' . preg_quote($delimiter ?? '') . ')'; + } + private static function matchFlags(int $matchMode): string { return ($matchMode === 0) ? 'mu' : 'miu'; diff --git a/tests/PhpSpreadsheetTests/Calculation/Functions/TextData/TextAfterTest.php b/tests/PhpSpreadsheetTests/Calculation/Functions/TextData/TextAfterTest.php index b3b01d24..00483260 100644 --- a/tests/PhpSpreadsheetTests/Calculation/Functions/TextData/TextAfterTest.php +++ b/tests/PhpSpreadsheetTests/Calculation/Functions/TextData/TextAfterTest.php @@ -2,8 +2,6 @@ namespace PhpOffice\PhpSpreadsheetTests\Calculation\Functions\TextData; -use PhpOffice\PhpSpreadsheet\Calculation\Calculation; - class TextAfterTest extends AllSetupTeardown { /** @@ -14,14 +12,17 @@ class TextAfterTest extends AllSetupTeardown $text = $arguments[0]; $delimiter = $arguments[1]; - $args = 'A1, A2'; + $args = (is_array($delimiter)) ? 'A1, {A2,A3}' : 'A1, A2'; $args .= (isset($arguments[2])) ? ", {$arguments[2]}" : ','; $args .= (isset($arguments[3])) ? ", {$arguments[3]}" : ','; $args .= (isset($arguments[4])) ? ", {$arguments[4]}" : ','; $worksheet = $this->getSheet(); $worksheet->getCell('A1')->setValue($text); - $worksheet->getCell('A2')->setValue($delimiter); + $worksheet->getCell('A2')->setValue((is_array($delimiter)) ? $delimiter[0] : $delimiter); + if (is_array($delimiter)) { + $worksheet->getCell('A3')->setValue($delimiter[1]); + } $worksheet->getCell('B1')->setValue("=TEXTAFTER({$args})"); $result = $worksheet->getCell('B1')->getCalculatedValue(); @@ -32,18 +33,4 @@ class TextAfterTest extends AllSetupTeardown { return require 'tests/data/Calculation/TextData/TEXTAFTER.php'; } - - public function testTextAfterWithArray(): void - { - $calculation = Calculation::getInstance(); - - $text = "Red Riding Hood's red riding hood"; - $delimiter = 'red'; - - $args = "\"{$text}\", \"{$delimiter}\", 1, {0;1}"; - - $formula = "=TEXTAFTER({$args})"; - $result = $calculation->_calculateFormulaValue($formula); - self::assertEquals([[' riding hood'], [" Riding Hood's red riding hood"]], $result); - } } diff --git a/tests/PhpSpreadsheetTests/Calculation/Functions/TextData/TextBeforeTest.php b/tests/PhpSpreadsheetTests/Calculation/Functions/TextData/TextBeforeTest.php index 17938b5e..37e46636 100644 --- a/tests/PhpSpreadsheetTests/Calculation/Functions/TextData/TextBeforeTest.php +++ b/tests/PhpSpreadsheetTests/Calculation/Functions/TextData/TextBeforeTest.php @@ -12,14 +12,17 @@ class TextBeforeTest extends AllSetupTeardown $text = $arguments[0]; $delimiter = $arguments[1]; - $args = 'A1, A2'; + $args = (is_array($delimiter)) ? 'A1, {A2,A3}' : 'A1, A2'; $args .= (isset($arguments[2])) ? ", {$arguments[2]}" : ','; $args .= (isset($arguments[3])) ? ", {$arguments[3]}" : ','; $args .= (isset($arguments[4])) ? ", {$arguments[4]}" : ','; $worksheet = $this->getSheet(); $worksheet->getCell('A1')->setValue($text); - $worksheet->getCell('A2')->setValue($delimiter); + $worksheet->getCell('A2')->setValue((is_array($delimiter)) ? $delimiter[0] : $delimiter); + if (is_array($delimiter)) { + $worksheet->getCell('A3')->setValue($delimiter[1]); + } $worksheet->getCell('B1')->setValue("=TEXTBEFORE({$args})"); $result = $worksheet->getCell('B1')->getCalculatedValue(); diff --git a/tests/data/Calculation/TextData/TEXTAFTER.php b/tests/data/Calculation/TextData/TEXTAFTER.php index c594ecdc..ebcfecb9 100644 --- a/tests/data/Calculation/TextData/TEXTAFTER.php +++ b/tests/data/Calculation/TextData/TEXTAFTER.php @@ -212,4 +212,40 @@ return [ 1, ], ], + 'Multi-delimiter Case-Insensitive Offset 1' => [ + " riding hood's red riding hood", + [ + "Little Red riding hood's red riding hood", + ['HOOD', 'RED'], + 1, + 1, + ], + ], + 'Multi-delimiter Case-Insensitive Offset 2' => [ + "'s red riding hood", + [ + "Little Red riding hood's red riding hood", + ['HOOD', 'RED'], + 2, + 1, + ], + ], + 'Multi-delimiter Case-Insensitive Offset 3' => [ + ' riding hood', + [ + "Little Red riding hood's red riding hood", + ['HOOD', 'RED'], + 3, + 1, + ], + ], + 'Multi-delimiter Case-Insensitive Offset -2' => [ + ' riding hood', + [ + "Little Red riding hood's red riding hood", + ['HOOD', 'RED'], + -2, + 1, + ], + ], ]; diff --git a/tests/data/Calculation/TextData/TEXTBEFORE.php b/tests/data/Calculation/TextData/TEXTBEFORE.php index f94d5f28..1929354c 100644 --- a/tests/data/Calculation/TextData/TEXTBEFORE.php +++ b/tests/data/Calculation/TextData/TEXTBEFORE.php @@ -204,4 +204,40 @@ return [ 1, ], ], + 'Multi-delimiter Case-Insensitive Offset 1' => [ + 'Little ', + [ + "Little Red riding hood's red riding hood", + ['HOOD', 'RED'], + 1, + 1, + ], + ], + 'Multi-delimiter Case-Insensitive Offset 2' => [ + 'Little Red riding ', + [ + "Little Red riding hood's red riding hood", + ['HOOD', 'RED'], + 2, + 1, + ], + ], + 'Multi-delimiter Case-Insensitive Offset 3' => [ + "Little Red riding hood's ", + [ + "Little Red riding hood's red riding hood", + ['HOOD', 'RED'], + 3, + 1, + ], + ], + 'Multi-delimiter Case-Insensitive Offset -2' => [ + "Little Red riding hood's ", + [ + "Little Red riding hood's red riding hood", + ['HOOD', 'RED'], + -2, + 1, + ], + ], ];