Merge pull request #2972 from PHPOffice/TextFunctions-New
Allow multiple delimiters for `TEXTBEFORE()` and `TEXTAFTER()` functions
This commit is contained in:
commit
1c587e90c6
|
|
@ -104,7 +104,8 @@ class Extract
|
||||||
*
|
*
|
||||||
* @param mixed $text the text that you're searching
|
* @param mixed $text the text that you're searching
|
||||||
* Or can be an array of values
|
* 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.
|
* @param mixed $instance The instance of the delimiter after which you want to extract the text.
|
||||||
* By default, this is the first instance (1).
|
* By default, this is the first instance (1).
|
||||||
* A negative value means start searching from the end of the text string.
|
* A negative value means start searching from the end of the text string.
|
||||||
|
|
@ -132,7 +133,6 @@ class Extract
|
||||||
}
|
}
|
||||||
|
|
||||||
$text = Helpers::extractString($text ?? '');
|
$text = Helpers::extractString($text ?? '');
|
||||||
$delimiter = Helpers::extractString(Functions::flattenSingleValue($delimiter ?? ''));
|
|
||||||
$instance = (int) $instance;
|
$instance = (int) $instance;
|
||||||
$matchMode = (int) $matchMode;
|
$matchMode = (int) $matchMode;
|
||||||
$matchEnd = (int) $matchEnd;
|
$matchEnd = (int) $matchEnd;
|
||||||
|
|
@ -141,13 +141,14 @@ class Extract
|
||||||
if (is_array($split) === false) {
|
if (is_array($split) === false) {
|
||||||
return $split;
|
return $split;
|
||||||
}
|
}
|
||||||
if ($delimiter === '') {
|
if (Helpers::extractString(Functions::flattenSingleValue($delimiter ?? '')) === '') {
|
||||||
return ($instance > 0) ? '' : $text;
|
return ($instance > 0) ? '' : $text;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Adjustment for a match as the first element of the split
|
// Adjustment for a match as the first element of the split
|
||||||
$flags = self::matchFlags($matchMode);
|
$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;
|
$oddReverseAdjustment = count($split) % 2;
|
||||||
|
|
||||||
$split = ($instance < 0)
|
$split = ($instance < 0)
|
||||||
|
|
@ -161,7 +162,8 @@ class Extract
|
||||||
* TEXTAFTER.
|
* TEXTAFTER.
|
||||||
*
|
*
|
||||||
* @param mixed $text the text that you're searching
|
* @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.
|
* @param mixed $instance The instance of the delimiter after which you want to extract the text.
|
||||||
* By default, this is the first instance (1).
|
* By default, this is the first instance (1).
|
||||||
* A negative value means start searching from the end of the text string.
|
* A negative value means start searching from the end of the text string.
|
||||||
|
|
@ -189,7 +191,6 @@ class Extract
|
||||||
}
|
}
|
||||||
|
|
||||||
$text = Helpers::extractString($text ?? '');
|
$text = Helpers::extractString($text ?? '');
|
||||||
$delimiter = Helpers::extractString(Functions::flattenSingleValue($delimiter ?? ''));
|
|
||||||
$instance = (int) $instance;
|
$instance = (int) $instance;
|
||||||
$matchMode = (int) $matchMode;
|
$matchMode = (int) $matchMode;
|
||||||
$matchEnd = (int) $matchEnd;
|
$matchEnd = (int) $matchEnd;
|
||||||
|
|
@ -198,13 +199,14 @@ class Extract
|
||||||
if (is_array($split) === false) {
|
if (is_array($split) === false) {
|
||||||
return $split;
|
return $split;
|
||||||
}
|
}
|
||||||
if ($delimiter === '') {
|
if (Helpers::extractString(Functions::flattenSingleValue($delimiter ?? '')) === '') {
|
||||||
return ($instance < 0) ? '' : $text;
|
return ($instance < 0) ? '' : $text;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Adjustment for a match as the first element of the split
|
// Adjustment for a match as the first element of the split
|
||||||
$flags = self::matchFlags($matchMode);
|
$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;
|
$oddReverseAdjustment = count($split) % 2;
|
||||||
|
|
||||||
$split = ($instance < 0)
|
$split = ($instance < 0)
|
||||||
|
|
@ -215,21 +217,23 @@ class Extract
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @param null|array|string $delimiter
|
||||||
* @param int $matchMode
|
* @param int $matchMode
|
||||||
* @param int $matchEnd
|
* @param int $matchEnd
|
||||||
* @param mixed $ifNotFound
|
* @param mixed $ifNotFound
|
||||||
*
|
*
|
||||||
* @return string|string[]
|
* @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);
|
$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;
|
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) {
|
if ($split === false) {
|
||||||
return ExcelError::NA();
|
return ExcelError::NA();
|
||||||
}
|
}
|
||||||
|
|
@ -247,6 +251,28 @@ class Extract
|
||||||
return $split;
|
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
|
private static function matchFlags(int $matchMode): string
|
||||||
{
|
{
|
||||||
return ($matchMode === 0) ? 'mu' : 'miu';
|
return ($matchMode === 0) ? 'mu' : 'miu';
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,6 @@
|
||||||
|
|
||||||
namespace PhpOffice\PhpSpreadsheetTests\Calculation\Functions\TextData;
|
namespace PhpOffice\PhpSpreadsheetTests\Calculation\Functions\TextData;
|
||||||
|
|
||||||
use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
|
|
||||||
|
|
||||||
class TextAfterTest extends AllSetupTeardown
|
class TextAfterTest extends AllSetupTeardown
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
|
|
@ -14,14 +12,17 @@ class TextAfterTest extends AllSetupTeardown
|
||||||
$text = $arguments[0];
|
$text = $arguments[0];
|
||||||
$delimiter = $arguments[1];
|
$delimiter = $arguments[1];
|
||||||
|
|
||||||
$args = 'A1, A2';
|
$args = (is_array($delimiter)) ? 'A1, {A2,A3}' : 'A1, A2';
|
||||||
$args .= (isset($arguments[2])) ? ", {$arguments[2]}" : ',';
|
$args .= (isset($arguments[2])) ? ", {$arguments[2]}" : ',';
|
||||||
$args .= (isset($arguments[3])) ? ", {$arguments[3]}" : ',';
|
$args .= (isset($arguments[3])) ? ", {$arguments[3]}" : ',';
|
||||||
$args .= (isset($arguments[4])) ? ", {$arguments[4]}" : ',';
|
$args .= (isset($arguments[4])) ? ", {$arguments[4]}" : ',';
|
||||||
|
|
||||||
$worksheet = $this->getSheet();
|
$worksheet = $this->getSheet();
|
||||||
$worksheet->getCell('A1')->setValue($text);
|
$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})");
|
$worksheet->getCell('B1')->setValue("=TEXTAFTER({$args})");
|
||||||
|
|
||||||
$result = $worksheet->getCell('B1')->getCalculatedValue();
|
$result = $worksheet->getCell('B1')->getCalculatedValue();
|
||||||
|
|
@ -32,18 +33,4 @@ class TextAfterTest extends AllSetupTeardown
|
||||||
{
|
{
|
||||||
return require 'tests/data/Calculation/TextData/TEXTAFTER.php';
|
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,14 +12,17 @@ class TextBeforeTest extends AllSetupTeardown
|
||||||
$text = $arguments[0];
|
$text = $arguments[0];
|
||||||
$delimiter = $arguments[1];
|
$delimiter = $arguments[1];
|
||||||
|
|
||||||
$args = 'A1, A2';
|
$args = (is_array($delimiter)) ? 'A1, {A2,A3}' : 'A1, A2';
|
||||||
$args .= (isset($arguments[2])) ? ", {$arguments[2]}" : ',';
|
$args .= (isset($arguments[2])) ? ", {$arguments[2]}" : ',';
|
||||||
$args .= (isset($arguments[3])) ? ", {$arguments[3]}" : ',';
|
$args .= (isset($arguments[3])) ? ", {$arguments[3]}" : ',';
|
||||||
$args .= (isset($arguments[4])) ? ", {$arguments[4]}" : ',';
|
$args .= (isset($arguments[4])) ? ", {$arguments[4]}" : ',';
|
||||||
|
|
||||||
$worksheet = $this->getSheet();
|
$worksheet = $this->getSheet();
|
||||||
$worksheet->getCell('A1')->setValue($text);
|
$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})");
|
$worksheet->getCell('B1')->setValue("=TEXTBEFORE({$args})");
|
||||||
|
|
||||||
$result = $worksheet->getCell('B1')->getCalculatedValue();
|
$result = $worksheet->getCell('B1')->getCalculatedValue();
|
||||||
|
|
|
||||||
|
|
@ -212,4 +212,40 @@ return [
|
||||||
1,
|
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,
|
||||||
|
],
|
||||||
|
],
|
||||||
];
|
];
|
||||||
|
|
|
||||||
|
|
@ -204,4 +204,40 @@ return [
|
||||||
1,
|
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,
|
||||||
|
],
|
||||||
|
],
|
||||||
];
|
];
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue