diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 4ee0a9efd3..746f1d586d 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -1320,11 +1320,6 @@ parameters: count: 1 path: src/PhpSpreadsheet/Calculation/TextData.php - - - message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\TextData\\:\\:TRIMSPACES\\(\\) should return string but returns string\\|null\\.$#" - count: 1 - path: src/PhpSpreadsheet/Calculation/TextData.php - - message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\TextData\\:\\:CONCATENATE\\(\\) has parameter \\$args with no typehint specified\\.$#" count: 1 @@ -1340,96 +1335,6 @@ parameters: count: 1 path: src/PhpSpreadsheet/Calculation/TextData.php - - - message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\TextData\\\\CharacterConvert\\:\\:character\\(\\) should return string but returns string\\|false\\.$#" - count: 1 - path: src/PhpSpreadsheet/Calculation/TextData/CharacterConvert.php - - - - message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\TextData\\\\CharacterConvert\\:\\:unicodeToOrd\\(\\) has no return typehint specified\\.$#" - count: 1 - path: src/PhpSpreadsheet/Calculation/TextData/CharacterConvert.php - - - - message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\TextData\\\\CharacterConvert\\:\\:unicodeToOrd\\(\\) has parameter \\$character with no typehint specified\\.$#" - count: 1 - path: src/PhpSpreadsheet/Calculation/TextData/CharacterConvert.php - - - - message: "#^Cannot access offset 1 on array\\|false\\.$#" - count: 1 - path: src/PhpSpreadsheet/Calculation/TextData/CharacterConvert.php - - - - message: "#^Parameter \\#2 \\$data of function unpack expects string, string\\|false given\\.$#" - count: 1 - path: src/PhpSpreadsheet/Calculation/TextData/CharacterConvert.php - - - - message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\TextData\\\\CharacterConvert\\:\\:convertBooleanValue\\(\\) has no return typehint specified\\.$#" - count: 1 - path: src/PhpSpreadsheet/Calculation/TextData/CharacterConvert.php - - - - message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\TextData\\\\CharacterConvert\\:\\:convertBooleanValue\\(\\) has parameter \\$value with no typehint specified\\.$#" - count: 1 - path: src/PhpSpreadsheet/Calculation/TextData/CharacterConvert.php - - - - message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\TextData\\\\Concatenate\\:\\:CONCATENATE\\(\\) has parameter \\$args with no typehint specified\\.$#" - count: 1 - path: src/PhpSpreadsheet/Calculation/TextData/Concatenate.php - - - - message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\TextData\\\\Concatenate\\:\\:convertBooleanValue\\(\\) has no return typehint specified\\.$#" - count: 1 - path: src/PhpSpreadsheet/Calculation/TextData/Concatenate.php - - - - message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\TextData\\\\Concatenate\\:\\:convertBooleanValue\\(\\) has parameter \\$value with no typehint specified\\.$#" - count: 1 - path: src/PhpSpreadsheet/Calculation/TextData/Concatenate.php - - - - message: "#^Parameter \\#3 \\$length of function mb_substr expects int\\|null, float\\|int\\<0, max\\>\\|string given\\.$#" - count: 3 - path: src/PhpSpreadsheet/Calculation/TextData/Extract.php - - - - message: "#^Parameter \\#2 \\$start of function mb_substr expects int, float\\|int given\\.$#" - count: 2 - path: src/PhpSpreadsheet/Calculation/TextData/Extract.php - - - - message: "#^Cannot cast array\\|float\\|int\\|string to float\\.$#" - count: 1 - path: src/PhpSpreadsheet/Calculation/TextData/Format.php - - - - message: "#^Parameter \\#3 \\$offset of function mb_strpos expects int, float\\|int given\\.$#" - count: 1 - path: src/PhpSpreadsheet/Calculation/TextData/Search.php - - - - message: "#^Parameter \\#3 \\$offset of function mb_stripos expects int, float\\|int given\\.$#" - count: 1 - path: src/PhpSpreadsheet/Calculation/TextData/Search.php - - - - message: "#^Property PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\TextData\\\\Trim\\:\\:\\$invalidChars has no typehint specified\\.$#" - count: 1 - path: src/PhpSpreadsheet/Calculation/TextData/Trim.php - - - - message: "#^Parameter \\#1 \\$str of function trim expects string, float\\|int\\|string given\\.$#" - count: 2 - path: src/PhpSpreadsheet/Calculation/TextData/Trim.php - - - - message: "#^Parameter \\#1 \\$str of function trim expects string, string\\|null given\\.$#" - count: 1 - path: src/PhpSpreadsheet/Calculation/TextData/Trim.php - - message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\Token\\\\Stack\\:\\:getStackItem\\(\\) has no return typehint specified\\.$#" count: 1 diff --git a/src/PhpSpreadsheet/Calculation/TextData/CaseConvert.php b/src/PhpSpreadsheet/Calculation/TextData/CaseConvert.php index 36b5efbd6c..664cc2d883 100644 --- a/src/PhpSpreadsheet/Calculation/TextData/CaseConvert.php +++ b/src/PhpSpreadsheet/Calculation/TextData/CaseConvert.php @@ -2,7 +2,6 @@ namespace PhpOffice\PhpSpreadsheet\Calculation\TextData; -use PhpOffice\PhpSpreadsheet\Calculation\Calculation; use PhpOffice\PhpSpreadsheet\Calculation\Functions; use PhpOffice\PhpSpreadsheet\Shared\StringHelper; @@ -18,10 +17,7 @@ class CaseConvert public static function lower($mixedCaseValue): string { $mixedCaseValue = Functions::flattenSingleValue($mixedCaseValue); - - if (is_bool($mixedCaseValue)) { - $mixedCaseValue = ($mixedCaseValue === true) ? Calculation::getTRUE() : Calculation::getFALSE(); - } + $mixedCaseValue = Helpers::extractString($mixedCaseValue); return StringHelper::strToLower($mixedCaseValue); } @@ -36,10 +32,7 @@ public static function lower($mixedCaseValue): string public static function upper($mixedCaseValue): string { $mixedCaseValue = Functions::flattenSingleValue($mixedCaseValue); - - if (is_bool($mixedCaseValue)) { - $mixedCaseValue = ($mixedCaseValue === true) ? Calculation::getTRUE() : Calculation::getFALSE(); - } + $mixedCaseValue = Helpers::extractString($mixedCaseValue); return StringHelper::strToUpper($mixedCaseValue); } @@ -54,10 +47,7 @@ public static function upper($mixedCaseValue): string public static function proper($mixedCaseValue): string { $mixedCaseValue = Functions::flattenSingleValue($mixedCaseValue); - - if (is_bool($mixedCaseValue)) { - $mixedCaseValue = ($mixedCaseValue === true) ? Calculation::getTRUE() : Calculation::getFALSE(); - } + $mixedCaseValue = Helpers::extractString($mixedCaseValue); return StringHelper::strToTitle($mixedCaseValue); } diff --git a/src/PhpSpreadsheet/Calculation/TextData/CharacterConvert.php b/src/PhpSpreadsheet/Calculation/TextData/CharacterConvert.php index 4397b538b5..0aca57e6ce 100644 --- a/src/PhpSpreadsheet/Calculation/TextData/CharacterConvert.php +++ b/src/PhpSpreadsheet/Calculation/TextData/CharacterConvert.php @@ -2,34 +2,29 @@ namespace PhpOffice\PhpSpreadsheet\Calculation\TextData; -use PhpOffice\PhpSpreadsheet\Calculation\Calculation; use PhpOffice\PhpSpreadsheet\Calculation\Functions; class CharacterConvert { /** - * CHARACTER. + * CHAR. * * @param mixed $character Integer Value to convert to its character representation */ public static function character($character): string { - $character = Functions::flattenSingleValue($character); - - if (!is_numeric($character)) { - return Functions::VALUE(); - } - - $character = (int) $character; - if ($character < 1 || $character > 255) { + $character = Helpers::validateInt($character); + $min = Functions::getCompatibilityMode() === Functions::COMPATIBILITY_OPENOFFICE ? 0 : 1; + if ($character < $min || $character > 255) { return Functions::VALUE(); } + $result = iconv('UCS-4LE', 'UTF-8', pack('V', $character)); - return iconv('UCS-4LE', 'UTF-8', pack('V', $character)); + return ($result === false) ? '' : $result; } /** - * ASCIICODE. + * CODE. * * @param mixed $characters String character to convert to its ASCII value * @@ -37,13 +32,10 @@ public static function character($character): string */ public static function code($characters) { - if (($characters === null) || ($characters === '')) { + $characters = Helpers::extractString($characters); + if ($characters === '') { return Functions::VALUE(); } - $characters = Functions::flattenSingleValue($characters); - if (is_bool($characters)) { - $characters = self::convertBooleanValue($characters); - } $character = $characters; if (mb_strlen($characters, 'UTF-8') > 1) { @@ -53,17 +45,17 @@ public static function code($characters) return self::unicodeToOrd($character); } - private static function unicodeToOrd($character) - { - return unpack('V', iconv('UTF-8', 'UCS-4LE', $character))[1]; - } - - private static function convertBooleanValue($value) + private static function unicodeToOrd(string $character): int { - if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_OPENOFFICE) { - return (int) $value; + $retVal = 0; + $iconv = iconv('UTF-8', 'UCS-4LE', $character); + if ($iconv !== false) { + $result = unpack('V', $iconv); + if (is_array($result) && isset($result[1])) { + $retVal = $result[1]; + } } - return ($value) ? Calculation::getTRUE() : Calculation::getFALSE(); + return $retVal; } } diff --git a/src/PhpSpreadsheet/Calculation/TextData/Concatenate.php b/src/PhpSpreadsheet/Calculation/TextData/Concatenate.php index 5780bb6e26..d53fc82289 100644 --- a/src/PhpSpreadsheet/Calculation/TextData/Concatenate.php +++ b/src/PhpSpreadsheet/Calculation/TextData/Concatenate.php @@ -2,13 +2,14 @@ namespace PhpOffice\PhpSpreadsheet\Calculation\TextData; -use PhpOffice\PhpSpreadsheet\Calculation\Calculation; use PhpOffice\PhpSpreadsheet\Calculation\Functions; class Concatenate { /** * CONCATENATE. + * + * @param array $args */ public static function CONCATENATE(...$args): string { @@ -16,11 +17,9 @@ public static function CONCATENATE(...$args): string // Loop through arguments $aArgs = Functions::flattenArray($args); + foreach ($aArgs as $arg) { - if (is_bool($arg)) { - $arg = self::convertBooleanValue($arg); - } - $returnValue .= $arg; + $returnValue .= Helpers::extractString($arg); } return $returnValue; @@ -35,13 +34,15 @@ public static function CONCATENATE(...$args): string */ public static function TEXTJOIN($delimiter, $ignoreEmpty, ...$args): string { + $delimiter = Functions::flattenSingleValue($delimiter); + $ignoreEmpty = Functions::flattenSingleValue($ignoreEmpty); // Loop through arguments $aArgs = Functions::flattenArray($args); foreach ($aArgs as $key => &$arg) { if ($ignoreEmpty === true && is_string($arg) && trim($arg) === '') { unset($aArgs[$key]); } elseif (is_bool($arg)) { - $arg = self::convertBooleanValue($arg); + $arg = Helpers::convertBooleanValue($arg); } } @@ -59,24 +60,12 @@ public static function TEXTJOIN($delimiter, $ignoreEmpty, ...$args): string public static function builtinREPT($stringValue, $repeatCount): string { $repeatCount = Functions::flattenSingleValue($repeatCount); + $stringValue = Helpers::extractString($stringValue); if (!is_numeric($repeatCount) || $repeatCount < 0) { return Functions::VALUE(); } - if (is_bool($stringValue)) { - $stringValue = self::convertBooleanValue($stringValue); - } - return str_repeat($stringValue, (int) $repeatCount); } - - private static function convertBooleanValue($value) - { - if (Functions::getCompatibilityMode() === Functions::COMPATIBILITY_OPENOFFICE) { - return (int) $value; - } - - return ($value) ? Calculation::getTRUE() : Calculation::getFALSE(); - } } diff --git a/src/PhpSpreadsheet/Calculation/TextData/Extract.php b/src/PhpSpreadsheet/Calculation/TextData/Extract.php index 015fabfb84..3dd281f28f 100644 --- a/src/PhpSpreadsheet/Calculation/TextData/Extract.php +++ b/src/PhpSpreadsheet/Calculation/TextData/Extract.php @@ -2,8 +2,7 @@ namespace PhpOffice\PhpSpreadsheet\Calculation\TextData; -use PhpOffice\PhpSpreadsheet\Calculation\Calculation; -use PhpOffice\PhpSpreadsheet\Calculation\Functions; +use PhpOffice\PhpSpreadsheet\Calculation\Exception as CalcExp; class Extract { @@ -13,20 +12,16 @@ class Extract * @param mixed $value String value from which to extract characters * @param mixed $chars The number of characters to extract (as an integer) */ - public static function left($value = '', $chars = 1): string + public static function left($value, $chars = 1): string { - $value = Functions::flattenSingleValue($value); - $chars = Functions::flattenSingleValue($chars); - - if (!is_numeric($chars) || $chars < 0) { - return Functions::VALUE(); - } - - if (is_bool($value)) { - $value = ($value) ? Calculation::getTRUE() : Calculation::getFALSE(); + try { + $value = Helpers::extractString($value); + $chars = Helpers::extractInt($chars, 0, 1); + } catch (CalcExp $e) { + return $e->getMessage(); } - return mb_substr($value ?? '', 0, $chars, 'UTF-8'); + return mb_substr($value, 0, $chars, 'UTF-8'); } /** @@ -36,21 +31,17 @@ public static function left($value = '', $chars = 1): string * @param mixed $start Integer offset of the first character that we want to extract * @param mixed $chars The number of characters to extract (as an integer) */ - public static function mid($value = '', $start = 1, $chars = null): string + public static function mid($value, $start, $chars): string { - $value = Functions::flattenSingleValue($value); - $start = Functions::flattenSingleValue($start); - $chars = Functions::flattenSingleValue($chars); - - if (!is_numeric($start) || $start < 1 || !is_numeric($chars) || $chars < 0) { - return Functions::VALUE(); + try { + $value = Helpers::extractString($value); + $start = Helpers::extractInt($start, 1); + $chars = Helpers::extractInt($chars, 0); + } catch (CalcExp $e) { + return $e->getMessage(); } - if (is_bool($value)) { - $value = ($value) ? Calculation::getTRUE() : Calculation::getFALSE(); - } - - return mb_substr($value ?? '', --$start, $chars, 'UTF-8'); + return mb_substr($value, --$start, $chars, 'UTF-8'); } /** @@ -59,19 +50,15 @@ public static function mid($value = '', $start = 1, $chars = null): string * @param mixed $value String value from which to extract characters * @param mixed $chars The number of characters to extract (as an integer) */ - public static function right($value = '', $chars = 1): string + public static function right($value, $chars = 1): string { - $value = Functions::flattenSingleValue($value); - $chars = Functions::flattenSingleValue($chars); - - if (!is_numeric($chars) || $chars < 0) { - return Functions::VALUE(); - } - - if (is_bool($value)) { - $value = ($value) ? Calculation::getTRUE() : Calculation::getFALSE(); + try { + $value = Helpers::extractString($value); + $chars = Helpers::extractInt($chars, 0, 1); + } catch (CalcExp $e) { + return $e->getMessage(); } - return mb_substr($value ?? '', mb_strlen($value ?? '', 'UTF-8') - $chars, $chars, 'UTF-8'); + return mb_substr($value, mb_strlen($value, 'UTF-8') - $chars, $chars, 'UTF-8'); } } diff --git a/src/PhpSpreadsheet/Calculation/TextData/Format.php b/src/PhpSpreadsheet/Calculation/TextData/Format.php index 6bad17441a..3286de0cb0 100644 --- a/src/PhpSpreadsheet/Calculation/TextData/Format.php +++ b/src/PhpSpreadsheet/Calculation/TextData/Format.php @@ -4,6 +4,7 @@ use DateTimeInterface; use PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel; +use PhpOffice\PhpSpreadsheet\Calculation\Exception as CalcExp; use PhpOffice\PhpSpreadsheet\Calculation\Functions; use PhpOffice\PhpSpreadsheet\Calculation\MathTrig; use PhpOffice\PhpSpreadsheet\Shared\Date; @@ -25,14 +26,12 @@ class Format */ public static function DOLLAR($value = 0, $decimals = 2): string { - $value = Functions::flattenSingleValue($value); - $decimals = $decimals === null ? 2 : Functions::flattenSingleValue($decimals); - - // Validate parameters - if (!is_numeric($value) || !is_numeric($decimals)) { - return Functions::VALUE(); + try { + $value = Helpers::extractFloat($value); + $decimals = Helpers::extractInt($decimals, -100, 0, true); + } catch (CalcExp $e) { + return $e->getMessage(); } - $decimals = (int) $decimals; $mask = '$#,##0'; if ($decimals > 0) { @@ -50,7 +49,7 @@ public static function DOLLAR($value = 0, $decimals = 2): string } /** - * FIXEDFORMAT. + * FIXED. * * @param mixed $value The value to format * @param mixed $decimals Integer value for the number of decimal places that should be formatted @@ -58,17 +57,13 @@ public static function DOLLAR($value = 0, $decimals = 2): string */ public static function FIXEDFORMAT($value, $decimals = 2, $noCommas = false): string { - $value = Functions::flattenSingleValue($value); - $decimals = $decimals === null ? 2 : Functions::flattenSingleValue($decimals); - $noCommas = Functions::flattenSingleValue($noCommas); - - // Validate parameters - if (!is_numeric($value) || !is_numeric($decimals)) { - return Functions::VALUE(); + try { + $value = Helpers::extractFloat($value); + $decimals = Helpers::extractInt($decimals, -100, 0, true); + $noCommas = Functions::flattenSingleValue($noCommas); + } catch (CalcExp $e) { + return $e->getMessage(); } - $decimals = (float) $decimals; - $value = (float) $value; - $decimals = (int) floor($decimals); $valueResult = round($value, $decimals); if ($decimals < 0) { @@ -87,23 +82,42 @@ public static function FIXEDFORMAT($value, $decimals = 2, $noCommas = false): st } /** - * TEXTFORMAT. + * TEXT. * * @param mixed $value The value to format * @param mixed $format A string with the Format mask that should be used */ public static function TEXTFORMAT($value, $format): string { - $value = Functions::flattenSingleValue($value); - $format = Functions::flattenSingleValue($format); + $value = Helpers::extractString($value); + $format = Helpers::extractString($format); - if ((is_string($value)) && (!is_numeric($value)) && Date::isDateTimeFormatCode($format)) { + if (!is_numeric($value) && Date::isDateTimeFormatCode($format)) { $value = DateTimeExcel\DateValue::fromString($value); } return (string) NumberFormat::toFormattedString($value, $format); } + /** + * @param mixed $value Value to check + * + * @return mixed + */ + private static function convertValue($value) + { + $value = ($value === null) ? 0 : Functions::flattenSingleValue($value); + if (is_bool($value)) { + if (Functions::getCompatibilityMode() === Functions::COMPATIBILITY_OPENOFFICE) { + $value = (int) $value; + } else { + throw new CalcExp(Functions::VALUE()); + } + } + + return $value; + } + /** * VALUE. * @@ -113,8 +127,11 @@ public static function TEXTFORMAT($value, $format): string */ public static function VALUE($value = '') { - $value = Functions::flattenSingleValue($value); - + try { + $value = self::convertValue($value); + } catch (CalcExp $e) { + return $e->getMessage(); + } if (!is_numeric($value)) { $numberValue = str_replace( StringHelper::getThousandsSeparator(), @@ -150,6 +167,26 @@ public static function VALUE($value = '') return (float) $value; } + /** + * @param mixed $decimalSeparator + */ + private static function getDecimalSeparator($decimalSeparator): string + { + $decimalSeparator = Functions::flattenSingleValue($decimalSeparator); + + return empty($decimalSeparator) ? StringHelper::getDecimalSeparator() : (string) $decimalSeparator; + } + + /** + * @param mixed $groupSeparator + */ + private static function getGroupSeparator($groupSeparator): string + { + $groupSeparator = Functions::flattenSingleValue($groupSeparator); + + return empty($groupSeparator) ? StringHelper::getThousandsSeparator() : (string) $groupSeparator; + } + /** * NUMBERVALUE. * @@ -161,14 +198,15 @@ public static function VALUE($value = '') */ public static function NUMBERVALUE($value = '', $decimalSeparator = null, $groupSeparator = null) { - $value = Functions::flattenSingleValue($value); - $decimalSeparator = Functions::flattenSingleValue($decimalSeparator); - $groupSeparator = Functions::flattenSingleValue($groupSeparator); + try { + $value = self::convertValue($value); + $decimalSeparator = self::getDecimalSeparator($decimalSeparator); + $groupSeparator = self::getGroupSeparator($groupSeparator); + } catch (CalcExp $e) { + return $e->getMessage(); + } if (!is_numeric($value)) { - $decimalSeparator = empty($decimalSeparator) ? StringHelper::getDecimalSeparator() : $decimalSeparator; - $groupSeparator = empty($groupSeparator) ? StringHelper::getThousandsSeparator() : $groupSeparator; - $decimalPositions = preg_match_all('/' . preg_quote($decimalSeparator) . '/', $value, $matches, PREG_OFFSET_CAPTURE); if ($decimalPositions > 1) { return Functions::VALUE(); @@ -193,6 +231,6 @@ public static function NUMBERVALUE($value = '', $decimalSeparator = null, $group } } - return (float) $value; + return is_array($value) ? Functions::VALUE() : (float) $value; } } diff --git a/src/PhpSpreadsheet/Calculation/TextData/Helpers.php b/src/PhpSpreadsheet/Calculation/TextData/Helpers.php new file mode 100644 index 0000000000..623bf32b4a --- /dev/null +++ b/src/PhpSpreadsheet/Calculation/TextData/Helpers.php @@ -0,0 +1,90 @@ +getMessage(); + } return $left . $newText . $right; } @@ -35,15 +41,25 @@ public static function replace($oldText, $start, $chars, $newText): string * @param mixed $toText The string value that we want to replace with in $text * @param mixed $instance Integer instance Number for the occurrence of frmText to change */ - public static function substitute($text = '', $fromText = '', $toText = '', $instance = 0): string + public static function substitute($text = '', $fromText = '', $toText = '', $instance = null): string { - $text = Functions::flattenSingleValue($text); - $fromText = Functions::flattenSingleValue($fromText); - $toText = Functions::flattenSingleValue($toText); - $instance = floor(Functions::flattenSingleValue($instance)); - - if ($instance == 0) { - return str_replace($fromText, $toText, $text); + try { + $text = Helpers::extractString($text); + $fromText = Helpers::extractString($fromText); + $toText = Helpers::extractString($toText); + $instance = Functions::flattenSingleValue($instance); + if ($instance === null) { + return str_replace($fromText, $toText, $text); + } + if (is_bool($instance)) { + if ($instance === false || Functions::getCompatibilityMode() !== Functions::COMPATIBILITY_OPENOFFICE) { + return Functions::Value(); + } + $instance = 1; + } + $instance = Helpers::extractInt($instance, 1, 0, true); + } catch (CalcExp $e) { + return $e->getMessage(); } $pos = -1; diff --git a/src/PhpSpreadsheet/Calculation/TextData/Search.php b/src/PhpSpreadsheet/Calculation/TextData/Search.php index 2da688d884..c9eed2e5a5 100644 --- a/src/PhpSpreadsheet/Calculation/TextData/Search.php +++ b/src/PhpSpreadsheet/Calculation/TextData/Search.php @@ -2,14 +2,14 @@ namespace PhpOffice\PhpSpreadsheet\Calculation\TextData; -use PhpOffice\PhpSpreadsheet\Calculation\Calculation; +use PhpOffice\PhpSpreadsheet\Calculation\Exception as CalcExp; use PhpOffice\PhpSpreadsheet\Calculation\Functions; use PhpOffice\PhpSpreadsheet\Shared\StringHelper; class Search { /** - * SEARCHSENSITIVE. + * FIND (case sensitive search). * * @param mixed $needle The string to look for * @param mixed $haystack The string in which to look @@ -19,24 +19,22 @@ class Search */ public static function sensitive($needle, $haystack, $offset = 1) { - $needle = Functions::flattenSingleValue($needle); - $haystack = Functions::flattenSingleValue($haystack); - $offset = Functions::flattenSingleValue($offset); + try { + $needle = Helpers::extractString($needle); + $haystack = Helpers::extractString($haystack); + $offset = Helpers::extractInt($offset, 1, 0, true); + } catch (CalcExp $e) { + return $e->getMessage(); + } - if (!is_bool($needle)) { - if (is_bool($haystack)) { - $haystack = ($haystack) ? Calculation::getTRUE() : Calculation::getFALSE(); + if (StringHelper::countCharacters($haystack) >= $offset) { + if (StringHelper::countCharacters($needle) === 0) { + return $offset; } - if (($offset > 0) && (StringHelper::countCharacters($haystack) > $offset)) { - if (StringHelper::countCharacters($needle) === 0) { - return $offset; - } - - $pos = mb_strpos($haystack, $needle, --$offset, 'UTF-8'); - if ($pos !== false) { - return ++$pos; - } + $pos = mb_strpos($haystack, $needle, --$offset, 'UTF-8'); + if ($pos !== false) { + return ++$pos; } } @@ -44,7 +42,7 @@ public static function sensitive($needle, $haystack, $offset = 1) } /** - * SEARCHINSENSITIVE. + * SEARCH (case insensitive search). * * @param mixed $needle The string to look for * @param mixed $haystack The string in which to look @@ -54,24 +52,22 @@ public static function sensitive($needle, $haystack, $offset = 1) */ public static function insensitive($needle, $haystack, $offset = 1) { - $needle = Functions::flattenSingleValue($needle); - $haystack = Functions::flattenSingleValue($haystack); - $offset = Functions::flattenSingleValue($offset); + try { + $needle = Helpers::extractString($needle); + $haystack = Helpers::extractString($haystack); + $offset = Helpers::extractInt($offset, 1, 0, true); + } catch (CalcExp $e) { + return $e->getMessage(); + } - if (!is_bool($needle)) { - if (is_bool($haystack)) { - $haystack = ($haystack) ? Calculation::getTRUE() : Calculation::getFALSE(); + if (StringHelper::countCharacters($haystack) >= $offset) { + if (StringHelper::countCharacters($needle) === 0) { + return $offset; } - if (($offset > 0) && (StringHelper::countCharacters($haystack) > $offset)) { - if (StringHelper::countCharacters($needle) === 0) { - return $offset; - } - - $pos = mb_stripos($haystack, $needle, --$offset, 'UTF-8'); - if ($pos !== false) { - return ++$pos; - } + $pos = mb_stripos($haystack, $needle, --$offset, 'UTF-8'); + if ($pos !== false) { + return ++$pos; } } diff --git a/src/PhpSpreadsheet/Calculation/TextData/Text.php b/src/PhpSpreadsheet/Calculation/TextData/Text.php index f3da4bb339..dda0baa58a 100644 --- a/src/PhpSpreadsheet/Calculation/TextData/Text.php +++ b/src/PhpSpreadsheet/Calculation/TextData/Text.php @@ -2,25 +2,20 @@ namespace PhpOffice\PhpSpreadsheet\Calculation\TextData; -use PhpOffice\PhpSpreadsheet\Calculation\Calculation; use PhpOffice\PhpSpreadsheet\Calculation\Functions; class Text { /** - * STRINGLENGTH. + * LEN. * * @param mixed $value String Value */ public static function length($value = ''): int { - $value = Functions::flattenSingleValue($value); + $value = Helpers::extractString($value); - if (is_bool($value)) { - $value = ($value) ? Calculation::getTRUE() : Calculation::getFALSE(); - } - - return mb_strlen($value ?? '', 'UTF-8'); + return mb_strlen($value, 'UTF-8'); } /** @@ -33,10 +28,10 @@ public static function length($value = ''): int */ public static function exact($value1, $value2): bool { - $value1 = Functions::flattenSingleValue($value1); - $value2 = Functions::flattenSingleValue($value2); + $value1 = Helpers::extractString($value1); + $value2 = Helpers::extractString($value2); - return ((string) $value2) === ((string) $value1); + return $value2 === $value1; } /** diff --git a/src/PhpSpreadsheet/Calculation/TextData/Trim.php b/src/PhpSpreadsheet/Calculation/TextData/Trim.php index b5d6645549..22f4554b33 100644 --- a/src/PhpSpreadsheet/Calculation/TextData/Trim.php +++ b/src/PhpSpreadsheet/Calculation/TextData/Trim.php @@ -2,15 +2,10 @@ namespace PhpOffice\PhpSpreadsheet\Calculation\TextData; -use PhpOffice\PhpSpreadsheet\Calculation\Calculation; -use PhpOffice\PhpSpreadsheet\Calculation\Functions; - class Trim { - private static $invalidChars; - /** - * TRIMNONPRINTABLE. + * CLEAN. * * @param mixed $stringValue String Value to check * @@ -18,41 +13,22 @@ class Trim */ public static function nonPrintable($stringValue = '') { - $stringValue = Functions::flattenSingleValue($stringValue); - - if (is_bool($stringValue)) { - return ($stringValue) ? Calculation::getTRUE() : Calculation::getFALSE(); - } - - if (self::$invalidChars === null) { - self::$invalidChars = range(chr(0), chr(31)); - } + $stringValue = Helpers::extractString($stringValue); - if (is_string($stringValue) || is_numeric($stringValue)) { - return str_replace(self::$invalidChars, '', trim($stringValue, "\x00..\x1F")); - } - - return null; + return preg_replace('/[\\x00-\\x1f]/', '', "$stringValue"); } /** - * TRIMSPACES. + * TRIM. * * @param mixed $stringValue String Value to check * - * @return null|string + * @return string */ public static function spaces($stringValue = '') { - $stringValue = Functions::flattenSingleValue($stringValue); - if (is_bool($stringValue)) { - return ($stringValue) ? Calculation::getTRUE() : Calculation::getFALSE(); - } - - if (is_string($stringValue) || is_numeric($stringValue)) { - return trim(preg_replace('/ +/', ' ', trim($stringValue, ' ')), ' '); - } + $stringValue = Helpers::extractString($stringValue); - return null; + return trim(preg_replace('/ +/', ' ', trim("$stringValue", ' ')) ?? '', ' '); } } diff --git a/src/PhpSpreadsheet/Settings.php b/src/PhpSpreadsheet/Settings.php index 8fdccbad18..b51cd9d0b2 100644 --- a/src/PhpSpreadsheet/Settings.php +++ b/src/PhpSpreadsheet/Settings.php @@ -68,6 +68,11 @@ public static function setLocale($locale) return Calculation::getInstance()->setLocale($locale); } + public static function getLocale(): string + { + return Calculation::getInstance()->getLocale(); + } + /** * Identify to PhpSpreadsheet the external library to use for rendering charts. * diff --git a/src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php b/src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php index 493fd07e41..99f3cfb09b 100644 --- a/src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php +++ b/src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php @@ -1234,6 +1234,7 @@ private function writeCellFormula(XMLWriter $objWriter, string $cellValue, Cell return; } $objWriter->writeAttribute('t', 'str'); + $calculatedValue = StringHelper::controlCharacterPHP2OOXML($calculatedValue); } elseif (is_bool($calculatedValue)) { $objWriter->writeAttribute('t', 'b'); $calculatedValue = (int) $calculatedValue; diff --git a/tests/PhpSpreadsheetTests/Calculation/Functions/TextData/AllSetupTeardown.php b/tests/PhpSpreadsheetTests/Calculation/Functions/TextData/AllSetupTeardown.php new file mode 100644 index 0000000000..e0343b2b87 --- /dev/null +++ b/tests/PhpSpreadsheetTests/Calculation/Functions/TextData/AllSetupTeardown.php @@ -0,0 +1,105 @@ +locale = Settings::getLocale(); + $this->compatibilityMode = Functions::getCompatibilityMode(); + } + + protected function tearDown(): void + { + Functions::setCompatibilityMode($this->compatibilityMode); + Settings::setLocale($this->locale); + $this->sheet = null; + if ($this->spreadsheet !== null) { + $this->spreadsheet->disconnectWorksheets(); + $this->spreadsheet = null; + } + } + + protected static function setOpenOffice(): void + { + Functions::setCompatibilityMode(Functions::COMPATIBILITY_OPENOFFICE); + } + + protected static function setGnumeric(): void + { + Functions::setCompatibilityMode(Functions::COMPATIBILITY_GNUMERIC); + } + + /** + * @param mixed $expectedResult + */ + protected function mightHaveException($expectedResult): void + { + if ($expectedResult === 'exception') { + $this->expectException(CalcException::class); + } + } + + /** + * @param mixed $value + */ + protected function setCell(string $cell, $value): void + { + if ($value !== null) { + if (is_string($value) && is_numeric($value)) { + $this->getSheet()->getCell($cell)->setValueExplicit($value, DataType::TYPE_STRING); + } else { + $this->getSheet()->getCell($cell)->setValue($value); + } + } + } + + protected function getSpreadsheet(): Spreadsheet + { + if ($this->spreadsheet !== null) { + return $this->spreadsheet; + } + $this->spreadsheet = new Spreadsheet(); + + return $this->spreadsheet; + } + + protected function getSheet(): Worksheet + { + if ($this->sheet !== null) { + return $this->sheet; + } + $this->sheet = $this->getSpreadsheet()->getActiveSheet(); + + return $this->sheet; + } +} diff --git a/tests/PhpSpreadsheetTests/Calculation/Functions/TextData/CharNonPrintableTest.php b/tests/PhpSpreadsheetTests/Calculation/Functions/TextData/CharNonPrintableTest.php new file mode 100644 index 0000000000..3caadcb53e --- /dev/null +++ b/tests/PhpSpreadsheetTests/Calculation/Functions/TextData/CharNonPrintableTest.php @@ -0,0 +1,47 @@ +getActiveSheet(); + $sheet->getCell('B1')->setValue('=CHAR(2)'); + $sheet->getCell('C1')->setValue('=CHAR(127)'); + $hello = "hello\nthere"; + $sheet->getCell('D2')->setValue($hello); + $sheet->getCell('D1')->setValue('=D2'); + $reloadedSpreadsheet = $this->writeAndReload($spreadsheet, $type); + $result = $reloadedSpreadsheet->getActiveSheet()->getCell('B1')->getCalculatedValue(); + self::assertEquals("\x02", $result); + $result = $reloadedSpreadsheet->getActiveSheet()->getCell('C1')->getCalculatedValue(); + self::assertEquals("\x7f", $result); + $result = $reloadedSpreadsheet->getActiveSheet()->getCell('D1')->getCalculatedValue(); + self::assertEquals($hello, $result); + $spreadsheet->disconnectWorksheets(); + $reloadedSpreadsheet->disconnectWorksheets(); + } + + public function providerType(): array + { + return [ + ['Xlsx'], + ['Xls'], + //['Ods'], // no support yet in Reader or Writer + // without csv suffix, Reader/Csv decides type via mime_get_type, + // and control character makes it guess application/octet-stream, + // so Reader/Csv decides it can't read it. + //['Csv'], + // DOMDocument.loadHTML() rejects '' even though legal html. + //['Html'], + ]; + } +} diff --git a/tests/PhpSpreadsheetTests/Calculation/Functions/TextData/CharTest.php b/tests/PhpSpreadsheetTests/Calculation/Functions/TextData/CharTest.php index e8934ce53c..66d01cb084 100644 --- a/tests/PhpSpreadsheetTests/Calculation/Functions/TextData/CharTest.php +++ b/tests/PhpSpreadsheetTests/Calculation/Functions/TextData/CharTest.php @@ -2,10 +2,7 @@ namespace PhpOffice\PhpSpreadsheetTests\Calculation\Functions\TextData; -use PhpOffice\PhpSpreadsheet\Calculation\TextData; -use PHPUnit\Framework\TestCase; - -class CharTest extends TestCase +class CharTest extends AllSetupTeardown { /** * @dataProvider providerCHAR @@ -13,9 +10,17 @@ class CharTest extends TestCase * @param mixed $expectedResult * @param mixed $character */ - public function testCHAR($expectedResult, $character): void + public function testCHAR($expectedResult, $character = 'omitted'): void { - $result = TextData::CHARACTER($character); + $this->mightHaveException($expectedResult); + $sheet = $this->getSheet(); + if ($character === 'omitted') { + $sheet->getCell('B1')->setValue('=CHAR()'); + } else { + $this->setCell('A1', $character); + $sheet->getCell('B1')->setValue('=CHAR(A1)'); + } + $result = $sheet->getCell('B1')->getCalculatedValue(); self::assertEquals($expectedResult, $result); } diff --git a/tests/PhpSpreadsheetTests/Calculation/Functions/TextData/CleanTest.php b/tests/PhpSpreadsheetTests/Calculation/Functions/TextData/CleanTest.php index e3ad554493..03939882d1 100644 --- a/tests/PhpSpreadsheetTests/Calculation/Functions/TextData/CleanTest.php +++ b/tests/PhpSpreadsheetTests/Calculation/Functions/TextData/CleanTest.php @@ -2,10 +2,7 @@ namespace PhpOffice\PhpSpreadsheetTests\Calculation\Functions\TextData; -use PhpOffice\PhpSpreadsheet\Calculation\TextData; -use PHPUnit\Framework\TestCase; - -class CleanTest extends TestCase +class CleanTest extends AllSetupTeardown { /** * @dataProvider providerCLEAN @@ -13,9 +10,17 @@ class CleanTest extends TestCase * @param mixed $expectedResult * @param mixed $value */ - public function testCLEAN($expectedResult, $value): void + public function testCLEAN($expectedResult, $value = 'omitted'): void { - $result = TextData::TRIMNONPRINTABLE($value); + $this->mightHaveException($expectedResult); + $sheet = $this->getSheet(); + if ($value === 'omitted') { + $sheet->getCell('B1')->setValue('=CLEAN()'); + } else { + $this->setCell('A1', $value); + $sheet->getCell('B1')->setValue('=CLEAN(A1)'); + } + $result = $sheet->getCell('B1')->getCalculatedValue(); self::assertEquals($expectedResult, $result); } diff --git a/tests/PhpSpreadsheetTests/Calculation/Functions/TextData/CodeTest.php b/tests/PhpSpreadsheetTests/Calculation/Functions/TextData/CodeTest.php index 6f0f6b0654..52baf1a118 100644 --- a/tests/PhpSpreadsheetTests/Calculation/Functions/TextData/CodeTest.php +++ b/tests/PhpSpreadsheetTests/Calculation/Functions/TextData/CodeTest.php @@ -2,10 +2,7 @@ namespace PhpOffice\PhpSpreadsheetTests\Calculation\Functions\TextData; -use PhpOffice\PhpSpreadsheet\Calculation\TextData; -use PHPUnit\Framework\TestCase; - -class CodeTest extends TestCase +class CodeTest extends AllSetupTeardown { /** * @dataProvider providerCODE @@ -13,9 +10,17 @@ class CodeTest extends TestCase * @param mixed $expectedResult * @param mixed $character */ - public function testCODE($expectedResult, $character): void + public function testCODE($expectedResult, $character = 'omitted'): void { - $result = TextData::ASCIICODE($character); + $this->mightHaveException($expectedResult); + $sheet = $this->getSheet(); + if ($character === 'omitted') { + $sheet->getCell('B1')->setValue('=CODE()'); + } else { + $this->setCell('A1', $character); + $sheet->getCell('B1')->setValue('=CODE(A1)'); + } + $result = $sheet->getCell('B1')->getCalculatedValue(); self::assertEquals($expectedResult, $result); } diff --git a/tests/PhpSpreadsheetTests/Calculation/Functions/TextData/ConcatenateTest.php b/tests/PhpSpreadsheetTests/Calculation/Functions/TextData/ConcatenateTest.php index 88ad6bfd71..6c9a871de7 100644 --- a/tests/PhpSpreadsheetTests/Calculation/Functions/TextData/ConcatenateTest.php +++ b/tests/PhpSpreadsheetTests/Calculation/Functions/TextData/ConcatenateTest.php @@ -2,19 +2,27 @@ namespace PhpOffice\PhpSpreadsheetTests\Calculation\Functions\TextData; -use PhpOffice\PhpSpreadsheet\Calculation\TextData; -use PHPUnit\Framework\TestCase; - -class ConcatenateTest extends TestCase +class ConcatenateTest extends AllSetupTeardown { /** * @dataProvider providerCONCATENATE * * @param mixed $expectedResult + * @param array $args */ public function testCONCATENATE($expectedResult, ...$args): void { - $result = TextData::CONCATENATE(...$args); + $this->mightHaveException($expectedResult); + $sheet = $this->getSheet(); + $finalArg = ''; + $row = 0; + foreach ($args as $arg) { + ++$row; + $this->setCell("A$row", $arg); + $finalArg = "A1:A$row"; + } + $this->setCell('B1', "=CONCAT($finalArg)"); + $result = $sheet->getCell('B1')->getCalculatedValue(); self::assertEquals($expectedResult, $result); } diff --git a/tests/PhpSpreadsheetTests/Calculation/Functions/TextData/DeprecatedTest.php b/tests/PhpSpreadsheetTests/Calculation/Functions/TextData/DeprecatedTest.php new file mode 100644 index 0000000000..49ac421b84 --- /dev/null +++ b/tests/PhpSpreadsheetTests/Calculation/Functions/TextData/DeprecatedTest.php @@ -0,0 +1,44 @@ +mightHaveException($expectedResult); + $sheet = $this->getSheet(); + if ($amount === 'omitted') { + $sheet->getCell('B1')->setValue('=DOLLAR()'); + } elseif ($decimals === 'omitted') { + $this->setCell('A1', $amount); + $sheet->getCell('B1')->setValue('=DOLLAR(A1)'); + } else { + $this->setCell('A1', $amount); + $this->setCell('A2', $decimals); + $sheet->getCell('B1')->setValue('=DOLLAR(A1, A2)'); + } + $result = $sheet->getCell('B1')->getCalculatedValue(); self::assertEquals($expectedResult, $result); } diff --git a/tests/PhpSpreadsheetTests/Calculation/Functions/TextData/ExactTest.php b/tests/PhpSpreadsheetTests/Calculation/Functions/TextData/ExactTest.php index e856832f4c..fc0f481e16 100644 --- a/tests/PhpSpreadsheetTests/Calculation/Functions/TextData/ExactTest.php +++ b/tests/PhpSpreadsheetTests/Calculation/Functions/TextData/ExactTest.php @@ -2,21 +2,31 @@ namespace PhpOffice\PhpSpreadsheetTests\Calculation\Functions\TextData; -use PhpOffice\PhpSpreadsheet\Calculation\TextData; -use PHPUnit\Framework\TestCase; - -class ExactTest extends TestCase +class ExactTest extends AllSetupTeardown { /** * @dataProvider providerEXACT * * @param mixed $expectedResult - * @param array $args + * @param mixed $string1 + * @param mixed $string2 */ - public function testEXACT($expectedResult, ...$args): void + public function testEXACT($expectedResult, $string1 = 'omitted', $string2 = 'omitted'): void { - $result = TextData::EXACT(...$args); - self::assertSame($expectedResult, $result); + $this->mightHaveException($expectedResult); + $sheet = $this->getSheet(); + if ($string1 === 'omitted') { + $sheet->getCell('B1')->setValue('=EXACT()'); + } elseif ($string2 === 'omitted') { + $this->setCell('A1', $string1); + $sheet->getCell('B1')->setValue('=EXACT(A1)'); + } else { + $this->setCell('A1', $string1); + $this->setCell('A2', $string2); + $sheet->getCell('B1')->setValue('=EXACT(A1, A2)'); + } + $result = $sheet->getCell('B1')->getCalculatedValue(); + self::assertEquals($expectedResult, $result); } public function providerEXACT(): array diff --git a/tests/PhpSpreadsheetTests/Calculation/Functions/TextData/FindTest.php b/tests/PhpSpreadsheetTests/Calculation/Functions/TextData/FindTest.php index 3631c6a7b7..14e48b7a08 100644 --- a/tests/PhpSpreadsheetTests/Calculation/Functions/TextData/FindTest.php +++ b/tests/PhpSpreadsheetTests/Calculation/Functions/TextData/FindTest.php @@ -2,19 +2,36 @@ namespace PhpOffice\PhpSpreadsheetTests\Calculation\Functions\TextData; -use PhpOffice\PhpSpreadsheet\Calculation\TextData; -use PHPUnit\Framework\TestCase; - -class FindTest extends TestCase +class FindTest extends AllSetupTeardown { /** * @dataProvider providerFIND * * @param mixed $expectedResult + * @param mixed $string1 + * @param mixed $string2 + * @param mixed $start */ - public function testFIND($expectedResult, ...$args): void + public function testFIND($expectedResult, $string1 = 'omitted', $string2 = 'omitted', $start = 'omitted'): void { - $result = TextData::SEARCHSENSITIVE(...$args); + $this->mightHaveException($expectedResult); + $sheet = $this->getSheet(); + if ($string1 === 'omitted') { + $sheet->getCell('B1')->setValue('=FIND()'); + } elseif ($string2 === 'omitted') { + $this->setCell('A1', $string1); + $sheet->getCell('B1')->setValue('=FIND(A1)'); + } elseif ($start === 'omitted') { + $this->setCell('A1', $string1); + $this->setCell('A2', $string2); + $sheet->getCell('B1')->setValue('=FIND(A1, A2)'); + } else { + $this->setCell('A1', $string1); + $this->setCell('A2', $string2); + $this->setCell('A3', $start); + $sheet->getCell('B1')->setValue('=FIND(A1, A2, A3)'); + } + $result = $sheet->getCell('B1')->getCalculatedValue(); self::assertEquals($expectedResult, $result); } diff --git a/tests/PhpSpreadsheetTests/Calculation/Functions/TextData/FixedTest.php b/tests/PhpSpreadsheetTests/Calculation/Functions/TextData/FixedTest.php index ab3cfb7a7f..a6b6e19708 100644 --- a/tests/PhpSpreadsheetTests/Calculation/Functions/TextData/FixedTest.php +++ b/tests/PhpSpreadsheetTests/Calculation/Functions/TextData/FixedTest.php @@ -2,19 +2,36 @@ namespace PhpOffice\PhpSpreadsheetTests\Calculation\Functions\TextData; -use PhpOffice\PhpSpreadsheet\Calculation\TextData; -use PHPUnit\Framework\TestCase; - -class FixedTest extends TestCase +class FixedTest extends AllSetupTeardown { /** * @dataProvider providerFIXED * * @param mixed $expectedResult + * @param mixed $number + * @param mixed $decimals + * @param mixed $noCommas */ - public function testFIXED($expectedResult, ...$args): void + public function testFIXED($expectedResult, $number = 'omitted', $decimals = 'omitted', $noCommas = 'omitted'): void { - $result = TextData::FIXEDFORMAT(...$args); + $this->mightHaveException($expectedResult); + $sheet = $this->getSheet(); + if ($number === 'omitted') { + $sheet->getCell('B1')->setValue('=FIXED()'); + } elseif ($decimals === 'omitted') { + $this->setCell('A1', $number); + $sheet->getCell('B1')->setValue('=FIXED(A1)'); + } elseif ($noCommas === 'omitted') { + $this->setCell('A1', $number); + $this->setCell('A2', $decimals); + $sheet->getCell('B1')->setValue('=FIXED(A1, A2)'); + } else { + $this->setCell('A1', $number); + $this->setCell('A2', $decimals); + $this->setCell('A3', $noCommas); + $sheet->getCell('B1')->setValue('=FIXED(A1, A2, A3)'); + } + $result = $sheet->getCell('B1')->getCalculatedValue(); self::assertEquals($expectedResult, $result); } diff --git a/tests/PhpSpreadsheetTests/Calculation/Functions/TextData/LeftTest.php b/tests/PhpSpreadsheetTests/Calculation/Functions/TextData/LeftTest.php index c2508dfd62..37ea2f3dc1 100644 --- a/tests/PhpSpreadsheetTests/Calculation/Functions/TextData/LeftTest.php +++ b/tests/PhpSpreadsheetTests/Calculation/Functions/TextData/LeftTest.php @@ -2,25 +2,33 @@ namespace PhpOffice\PhpSpreadsheetTests\Calculation\Functions\TextData; -use PhpOffice\PhpSpreadsheet\Calculation\TextData; +use PhpOffice\PhpSpreadsheet\Calculation\Functions; use PhpOffice\PhpSpreadsheet\Settings; -use PHPUnit\Framework\TestCase; -class LeftTest extends TestCase +class LeftTest extends AllSetupTeardown { - protected function tearDown(): void - { - Settings::setLocale('en_US'); - } - /** * @dataProvider providerLEFT * * @param mixed $expectedResult + * @param mixed $str string from which to extract + * @param mixed $cnt number of characters to extract */ - public function testLEFT($expectedResult, ...$args): void + public function testLEFT($expectedResult, $str = 'omitted', $cnt = 'omitted'): void { - $result = TextData::LEFT(...$args); + $this->mightHaveException($expectedResult); + $sheet = $this->getSheet(); + if ($str === 'omitted') { + $sheet->getCell('B1')->setValue('=LEFT()'); + } elseif ($cnt === 'omitted') { + $this->setCell('A1', $str); + $sheet->getCell('B1')->setValue('=LEFT(A1)'); + } else { + $this->setCell('A1', $str); + $this->setCell('A2', $cnt); + $sheet->getCell('B1')->setValue('=LEFT(A1, A2)'); + } + $result = $sheet->getCell('B1')->getCalculatedValue(); self::assertEquals($expectedResult, $result); } @@ -41,14 +49,15 @@ public function testLowerWithLocaleBoolean($expectedResult, $locale, $value, $ch { $newLocale = Settings::setLocale($locale); if ($newLocale === false) { - Settings::setLocale('en_US'); self::markTestSkipped('Unable to set locale for locale-specific test'); } - $result = TextData::LEFT($value, $characters); + $sheet = $this->getSheet(); + $this->setCell('A1', $value); + $this->setCell('A2', $characters); + $sheet->getCell('B1')->setValue('=LEFT(A1, A2)'); + $result = $sheet->getCell('B1')->getCalculatedValue(); self::assertEquals($expectedResult, $result); - - Settings::setLocale('en_US'); } public function providerLocaleLEFT(): array @@ -64,4 +73,111 @@ public function providerLocaleLEFT(): array ['ЛОЖ', 'bg', false, 3], ]; } + + /** + * @dataProvider providerCalculationTypeLEFTTrue + */ + public function testCalculationTypeTrue(string $type, string $resultB1, string $resultB2): void + { + Functions::setCompatibilityMode($type); + $sheet = $this->getSheet(); + $this->setCell('A1', true); + $this->setCell('A2', 'Hello'); + $this->setCell('B1', '=LEFT(A1, 1)'); + $this->setCell('B2', '=LEFT(A2, A1)'); + self::assertEquals($resultB1, $sheet->getCell('B1')->getCalculatedValue()); + self::assertEquals($resultB2, $sheet->getCell('B2')->getCalculatedValue()); + } + + public function providerCalculationTypeLEFTTrue(): array + { + return [ + 'Excel LEFT(true, 1) AND LEFT("hello", true)' => [ + Functions::COMPATIBILITY_EXCEL, + 'T', + 'H', + ], + 'Gnumeric LEFT(true, 1) AND LEFT("hello", true)' => [ + Functions::COMPATIBILITY_GNUMERIC, + 'T', + 'H', + ], + 'OpenOffice LEFT(true, 1) AND LEFT("hello", true)' => [ + Functions::COMPATIBILITY_OPENOFFICE, + '1', + '#VALUE!', + ], + ]; + } + + /** + * @dataProvider providerCalculationTypeLEFTFalse + */ + public function testCalculationTypeFalse(string $type, string $resultB1, string $resultB2): void + { + Functions::setCompatibilityMode($type); + $sheet = $this->getSheet(); + $this->setCell('A1', false); + $this->setCell('A2', 'Hello'); + $this->setCell('B1', '=LEFT(A1, 1)'); + $this->setCell('B2', '=LEFT(A2, A1)'); + self::assertEquals($resultB1, $sheet->getCell('B1')->getCalculatedValue()); + self::assertEquals($resultB2, $sheet->getCell('B2')->getCalculatedValue()); + } + + public function providerCalculationTypeLEFTFalse(): array + { + return [ + 'Excel LEFT(false, 1) AND LEFT("hello", false)' => [ + Functions::COMPATIBILITY_EXCEL, + 'F', + '', + ], + 'Gnumeric LEFT(false, 1) AND LEFT("hello", false)' => [ + Functions::COMPATIBILITY_GNUMERIC, + 'F', + '', + ], + 'OpenOffice LEFT(false, 1) AND LEFT("hello", false)' => [ + Functions::COMPATIBILITY_OPENOFFICE, + '0', + '#VALUE!', + ], + ]; + } + + /** + * @dataProvider providerCalculationTypeLEFTNull + */ + public function testCalculationTypeNull(string $type, string $resultB1, string $resultB2): void + { + Functions::setCompatibilityMode($type); + $sheet = $this->getSheet(); + $this->setCell('A2', 'Hello'); + $this->setCell('B1', '=LEFT(A1, 1)'); + $this->setCell('B2', '=LEFT(A2, A1)'); + self::assertEquals($resultB1, $sheet->getCell('B1')->getCalculatedValue()); + self::assertEquals($resultB2, $sheet->getCell('B2')->getCalculatedValue()); + } + + public function providerCalculationTypeLEFTNull(): array + { + return [ + 'Excel LEFT(null, 1) AND LEFT("hello", null)' => [ + Functions::COMPATIBILITY_EXCEL, + '', + '', + ], + 'Gnumeric LEFT(null, 1) AND LEFT("hello", null)' => [ + Functions::COMPATIBILITY_GNUMERIC, + '', + 'H', + ], + 'OpenOffice LEFT(null, 1) AND LEFT("hello", null)' => [ + Functions::COMPATIBILITY_OPENOFFICE, + '', + '', + ], + ]; + } } diff --git a/tests/PhpSpreadsheetTests/Calculation/Functions/TextData/LenTest.php b/tests/PhpSpreadsheetTests/Calculation/Functions/TextData/LenTest.php index 7ab36b677c..b2ce124e1a 100644 --- a/tests/PhpSpreadsheetTests/Calculation/Functions/TextData/LenTest.php +++ b/tests/PhpSpreadsheetTests/Calculation/Functions/TextData/LenTest.php @@ -2,20 +2,25 @@ namespace PhpOffice\PhpSpreadsheetTests\Calculation\Functions\TextData; -use PhpOffice\PhpSpreadsheet\Calculation\TextData; -use PHPUnit\Framework\TestCase; - -class LenTest extends TestCase +class LenTest extends AllSetupTeardown { /** * @dataProvider providerLEN * * @param mixed $expectedResult - * @param mixed $value + * @param mixed $str */ - public function testLEN($expectedResult, $value): void + public function testLEN($expectedResult, $str = 'omitted'): void { - $result = TextData::STRINGLENGTH($value); + $this->mightHaveException($expectedResult); + $sheet = $this->getSheet(); + if ($str === 'omitted') { + $sheet->getCell('B1')->setValue('=LEN()'); + } else { + $this->setCell('A1', $str); + $sheet->getCell('B1')->setValue('=LEN(A1)'); + } + $result = $sheet->getCell('B1')->getCalculatedValue(); self::assertEquals($expectedResult, $result); } diff --git a/tests/PhpSpreadsheetTests/Calculation/Functions/TextData/LowerTest.php b/tests/PhpSpreadsheetTests/Calculation/Functions/TextData/LowerTest.php index 1d11f1e56a..4639624a2b 100644 --- a/tests/PhpSpreadsheetTests/Calculation/Functions/TextData/LowerTest.php +++ b/tests/PhpSpreadsheetTests/Calculation/Functions/TextData/LowerTest.php @@ -2,26 +2,27 @@ namespace PhpOffice\PhpSpreadsheetTests\Calculation\Functions\TextData; -use PhpOffice\PhpSpreadsheet\Calculation\TextData; use PhpOffice\PhpSpreadsheet\Settings; -use PHPUnit\Framework\TestCase; -class LowerTest extends TestCase +class LowerTest extends AllSetupTeardown { - protected function tearDown(): void - { - Settings::setLocale('en_US'); - } - /** * @dataProvider providerLOWER * * @param mixed $expectedResult - * @param mixed $value + * @param mixed $str */ - public function testLOWER($expectedResult, $value): void + public function testLOWER($expectedResult, $str = 'omitted'): void { - $result = TextData::LOWERCASE($value); + $this->mightHaveException($expectedResult); + $sheet = $this->getSheet(); + if ($str === 'omitted') { + $sheet->getCell('B1')->setValue('=LOWER()'); + } else { + $this->setCell('A1', $str); + $sheet->getCell('B1')->setValue('=LOWER(A1)'); + } + $result = $sheet->getCell('B1')->getCalculatedValue(); self::assertEquals($expectedResult, $result); } @@ -41,14 +42,13 @@ public function testLowerWithLocaleBoolean($expectedResult, $locale, $value): vo { $newLocale = Settings::setLocale($locale); if ($newLocale === false) { - Settings::setLocale('en_US'); self::markTestSkipped('Unable to set locale for locale-specific test'); } - - $result = TextData::LOWERCASE($value); + $sheet = $this->getSheet(); + $this->setCell('A1', $value); + $sheet->getCell('B1')->setValue('=LOWER(A1)'); + $result = $sheet->getCell('B1')->getCalculatedValue(); self::assertEquals($expectedResult, $result); - - Settings::setLocale('en_US'); } public function providerLocaleLOWER(): array diff --git a/tests/PhpSpreadsheetTests/Calculation/Functions/TextData/MidTest.php b/tests/PhpSpreadsheetTests/Calculation/Functions/TextData/MidTest.php index 267d689452..6bf058a524 100644 --- a/tests/PhpSpreadsheetTests/Calculation/Functions/TextData/MidTest.php +++ b/tests/PhpSpreadsheetTests/Calculation/Functions/TextData/MidTest.php @@ -2,25 +2,39 @@ namespace PhpOffice\PhpSpreadsheetTests\Calculation\Functions\TextData; -use PhpOffice\PhpSpreadsheet\Calculation\TextData; +use PhpOffice\PhpSpreadsheet\Calculation\Functions; use PhpOffice\PhpSpreadsheet\Settings; -use PHPUnit\Framework\TestCase; -class MidTest extends TestCase +class MidTest extends AllSetupTeardown { - protected function tearDown(): void - { - Settings::setLocale('en_US'); - } - /** * @dataProvider providerMID * * @param mixed $expectedResult + * @param mixed $str string from which to extract + * @param mixed $start position at which to start + * @param mixed $cnt number of characters to extract */ - public function testMID($expectedResult, ...$args): void + public function testMID($expectedResult, $str = 'omitted', $start = 'omitted', $cnt = 'omitted'): void { - $result = TextData::MID(...$args); + $this->mightHaveException($expectedResult); + $sheet = $this->getSheet(); + if ($str === 'omitted') { + $sheet->getCell('B1')->setValue('=MID()'); + } elseif ($start === 'omitted') { + $this->setCell('A1', $str); + $sheet->getCell('B1')->setValue('=MID(A1)'); + } elseif ($cnt === 'omitted') { + $this->setCell('A1', $str); + $this->setCell('A2', $start); + $sheet->getCell('B1')->setValue('=MID(A1, A2)'); + } else { + $this->setCell('A1', $str); + $this->setCell('A2', $start); + $this->setCell('A3', $cnt); + $sheet->getCell('B1')->setValue('=MID(A1, A2, A3)'); + } + $result = $sheet->getCell('B1')->getCalculatedValue(); self::assertEquals($expectedResult, $result); } @@ -38,18 +52,20 @@ public function providerMID(): array * @param mixed $offset * @param mixed $characters */ - public function testLowerWithLocaleBoolean($expectedResult, $locale, $value, $offset, $characters): void + public function testMiddleWithLocaleBoolean($expectedResult, $locale, $value, $offset, $characters): void { $newLocale = Settings::setLocale($locale); if ($newLocale === false) { - Settings::setLocale('en_US'); self::markTestSkipped('Unable to set locale for locale-specific test'); } - $result = TextData::MID($value, $offset, $characters); + $sheet = $this->getSheet(); + $this->setCell('A1', $value); + $this->setCell('A2', $offset); + $this->setCell('A3', $characters); + $sheet->getCell('B1')->setValue('=MID(A1, A2, A3)'); + $result = $sheet->getCell('B1')->getCalculatedValue(); self::assertEquals($expectedResult, $result); - - Settings::setLocale('en_US'); } public function providerLocaleMID(): array @@ -65,4 +81,124 @@ public function providerLocaleMID(): array ['ОЖ', 'bg', false, 2, 2], ]; } + + /** + * @dataProvider providerCalculationTypeMIDTrue + */ + public function testCalculationTypeTrue(string $type, string $resultB1, string $resultB2, string $resultB3): void + { + Functions::setCompatibilityMode($type); + $sheet = $this->getSheet(); + $this->setCell('A1', true); + $this->setCell('A2', 'hello'); + $this->setCell('B1', '=MID(A1, 3, 1)'); + $this->setCell('B2', '=MID(A2, A1, 1)'); + $this->setCell('B3', '=MID(A2, 2, A1)'); + self::assertEquals($resultB1, $sheet->getCell('B1')->getCalculatedValue()); + self::assertEquals($resultB2, $sheet->getCell('B2')->getCalculatedValue()); + } + + public function providerCalculationTypeMIDTrue(): array + { + return [ + 'Excel MID(true,3,1), MID("hello",true, 1), MID("hello", 2, true)' => [ + Functions::COMPATIBILITY_EXCEL, + 'U', + 'h', + 'e', + ], + 'Gnumeric MID(true,3,1), MID("hello",true, 1), MID("hello", 2, true)' => [ + Functions::COMPATIBILITY_GNUMERIC, + 'U', + 'h', + 'e', + ], + 'OpenOffice MID(true,3,1), MID("hello",true, 1), MID("hello", 2, true)' => [ + Functions::COMPATIBILITY_OPENOFFICE, + '', + '#VALUE!', + '#VALUE!', + ], + ]; + } + + /** + * @dataProvider providerCalculationTypeMIDFalse + */ + public function testCalculationTypeFalse(string $type, string $resultB1, string $resultB2): void + { + Functions::setCompatibilityMode($type); + $sheet = $this->getSheet(); + $this->setCell('A1', false); + $this->setCell('A2', 'Hello'); + $this->setCell('B1', '=MID(A1, 3, 1)'); + $this->setCell('B2', '=MID(A2, A1, 1)'); + $this->setCell('B3', '=MID(A2, 2, A1)'); + self::assertEquals($resultB1, $sheet->getCell('B1')->getCalculatedValue()); + self::assertEquals($resultB2, $sheet->getCell('B2')->getCalculatedValue()); + } + + public function providerCalculationTypeMIDFalse(): array + { + return [ + 'Excel MID(false,3,1), MID("hello", false, 1), MID("hello", 2, false)' => [ + Functions::COMPATIBILITY_EXCEL, + 'L', + '#VALUE!', + '', + ], + 'Gnumeric MID(false,3,1), MID("hello", false, 1), MID("hello", 2, false)' => [ + Functions::COMPATIBILITY_GNUMERIC, + 'L', + '#VALUE!', + '', + ], + 'OpenOffice MID(false,3,1), MID("hello", false, 1), MID("hello", 2, false)' => [ + Functions::COMPATIBILITY_OPENOFFICE, + '', + '#VALUE!', + '#VALUE!', + ], + ]; + } + + /** + * @dataProvider providerCalculationTypeMIDNull + */ + public function testCalculationTypeNull(string $type, string $resultB1, string $resultB2, string $resultB3): void + { + Functions::setCompatibilityMode($type); + $sheet = $this->getSheet(); + $this->setCell('A2', 'Hello'); + $this->setCell('B1', '=MID(A1, 3, 1)'); + $this->setCell('B2', '=MID(A2, A1, 1)'); + $this->setCell('B3', '=MID(A2, 2, A1)'); + self::assertEquals($resultB1, $sheet->getCell('B1')->getCalculatedValue()); + self::assertEquals($resultB2, $sheet->getCell('B2')->getCalculatedValue()); + self::assertEquals($resultB3, $sheet->getCell('B3')->getCalculatedValue()); + } + + public function providerCalculationTypeMIDNull(): array + { + return [ + 'Excel MID(null,3,1), MID("hello", null, 1), MID("hello", 2, null)' => [ + Functions::COMPATIBILITY_EXCEL, + '', + '#VALUE!', + '', + ], + 'Gnumeric MID(null,3,1), MID("hello", null, 1), MID("hello", 2, null)' => [ + Functions::COMPATIBILITY_GNUMERIC, + '', + '#VALUE!', + '', + ], + 'OpenOffice MID(null,3,1), MID("hello", null, 1), MID("hello", 2, null)' => [ + Functions::COMPATIBILITY_OPENOFFICE, + '', + '#VALUE!', + '', + ], + ]; + } } diff --git a/tests/PhpSpreadsheetTests/Calculation/Functions/TextData/NumberValueTest.php b/tests/PhpSpreadsheetTests/Calculation/Functions/TextData/NumberValueTest.php index f54bfc3d9a..86d8ca8b9e 100644 --- a/tests/PhpSpreadsheetTests/Calculation/Functions/TextData/NumberValueTest.php +++ b/tests/PhpSpreadsheetTests/Calculation/Functions/TextData/NumberValueTest.php @@ -2,19 +2,36 @@ namespace PhpOffice\PhpSpreadsheetTests\Calculation\Functions\TextData; -use PhpOffice\PhpSpreadsheet\Calculation\TextData; -use PHPUnit\Framework\TestCase; - -class NumberValueTest extends TestCase +class NumberValueTest extends AllSetupTeardown { /** * @dataProvider providerNUMBERVALUE * * @param mixed $expectedResult + * @param mixed $number + * @param mixed $decimal + * @param mixed $group */ - public function testNUMBERVALUE($expectedResult, array $args): void + public function testNUMBERVALUE($expectedResult, $number = 'omitted', $decimal = 'omitted', $group = 'omitted'): void { - $result = TextData::NUMBERVALUE(...$args); + $this->mightHaveException($expectedResult); + $sheet = $this->getSheet(); + if ($number === 'omitted') { + $sheet->getCell('B1')->setValue('=NUMBERVALUE()'); + } elseif ($decimal === 'omitted') { + $this->setCell('A1', $number); + $sheet->getCell('B1')->setValue('=NUMBERVALUE(A1)'); + } elseif ($group === 'omitted') { + $this->setCell('A1', $number); + $this->setCell('A2', $decimal); + $sheet->getCell('B1')->setValue('=NUMBERVALUE(A1, A2)'); + } else { + $this->setCell('A1', $number); + $this->setCell('A2', $decimal); + $this->setCell('A3', $group); + $sheet->getCell('B1')->setValue('=NUMBERVALUE(A1, A2, A3)'); + } + $result = $sheet->getCell('B1')->getCalculatedValue(); self::assertEquals($expectedResult, $result); } diff --git a/tests/PhpSpreadsheetTests/Calculation/Functions/TextData/OpenOfficeTest.php b/tests/PhpSpreadsheetTests/Calculation/Functions/TextData/OpenOfficeTest.php new file mode 100644 index 0000000000..7421e41aec --- /dev/null +++ b/tests/PhpSpreadsheetTests/Calculation/Functions/TextData/OpenOfficeTest.php @@ -0,0 +1,26 @@ +setOpenOffice(); + $this->mightHaveException($expectedResult); + $sheet = $this->getSheet(); + $this->setCell('A1', $formula); + $result = $sheet->getCell('A1')->getCalculatedValue(); + self::assertEquals($expectedResult, $result); + } + + public function providerOpenOffice(): array + { + return require 'tests/data/Calculation/TextData/OpenOffice.php'; + } +} diff --git a/tests/PhpSpreadsheetTests/Calculation/Functions/TextData/ProperTest.php b/tests/PhpSpreadsheetTests/Calculation/Functions/TextData/ProperTest.php index 8366b07055..51968d83f7 100644 --- a/tests/PhpSpreadsheetTests/Calculation/Functions/TextData/ProperTest.php +++ b/tests/PhpSpreadsheetTests/Calculation/Functions/TextData/ProperTest.php @@ -2,26 +2,27 @@ namespace PhpOffice\PhpSpreadsheetTests\Calculation\Functions\TextData; -use PhpOffice\PhpSpreadsheet\Calculation\TextData; use PhpOffice\PhpSpreadsheet\Settings; -use PHPUnit\Framework\TestCase; -class ProperTest extends TestCase +class ProperTest extends AllSetupTeardown { - protected function tearDown(): void - { - Settings::setLocale('en_US'); - } - /** * @dataProvider providerPROPER * * @param mixed $expectedResult - * @param mixed $value + * @param mixed $str */ - public function testPROPER($expectedResult, $value): void + public function testPROPER($expectedResult, $str = 'omitted'): void { - $result = TextData::PROPERCASE($value); + $this->mightHaveException($expectedResult); + $sheet = $this->getSheet(); + if ($str === 'omitted') { + $sheet->getCell('B1')->setValue('=PROPER()'); + } else { + $this->setCell('A1', $str); + $sheet->getCell('B1')->setValue('=PROPER(A1)'); + } + $result = $sheet->getCell('B1')->getCalculatedValue(); self::assertEquals($expectedResult, $result); } @@ -41,14 +42,13 @@ public function testLowerWithLocaleBoolean($expectedResult, $locale, $value): vo { $newLocale = Settings::setLocale($locale); if ($newLocale === false) { - Settings::setLocale('en_US'); self::markTestSkipped('Unable to set locale for locale-specific test'); } - - $result = TextData::PROPERCASE($value); + $sheet = $this->getSheet(); + $this->setCell('A1', $value); + $sheet->getCell('B1')->setValue('=PROPER(A1)'); + $result = $sheet->getCell('B1')->getCalculatedValue(); self::assertEquals($expectedResult, $result); - - Settings::setLocale('en_US'); } public function providerLocaleLOWER(): array diff --git a/tests/PhpSpreadsheetTests/Calculation/Functions/TextData/ReplaceTest.php b/tests/PhpSpreadsheetTests/Calculation/Functions/TextData/ReplaceTest.php index 781e454cb2..0e12086429 100644 --- a/tests/PhpSpreadsheetTests/Calculation/Functions/TextData/ReplaceTest.php +++ b/tests/PhpSpreadsheetTests/Calculation/Functions/TextData/ReplaceTest.php @@ -2,19 +2,43 @@ namespace PhpOffice\PhpSpreadsheetTests\Calculation\Functions\TextData; -use PhpOffice\PhpSpreadsheet\Calculation\TextData; -use PHPUnit\Framework\TestCase; - -class ReplaceTest extends TestCase +class ReplaceTest extends AllSetupTeardown { /** * @dataProvider providerREPLACE * * @param mixed $expectedResult + * @param mixed $oldText + * @param mixed $start + * @param mixed $count + * @param mixed $newText */ - public function testREPLACE($expectedResult, ...$args): void + public function testREPLACE($expectedResult, $oldText = 'omitted', $start = 'omitted', $count = 'omitted', $newText = 'omitted'): void { - $result = TextData::REPLACE(...$args); + $this->mightHaveException($expectedResult); + $sheet = $this->getSheet(); + if ($oldText === 'omitted') { + $sheet->getCell('B1')->setValue('=REPLACE()'); + } elseif ($start === 'omitted') { + $this->setCell('A1', $oldText); + $sheet->getCell('B1')->setValue('=REPLACE(A1)'); + } elseif ($count === 'omitted') { + $this->setCell('A1', $oldText); + $this->setCell('A2', $start); + $sheet->getCell('B1')->setValue('=REPLACE(A1, A2)'); + } elseif ($newText === 'omitted') { + $this->setCell('A1', $oldText); + $this->setCell('A2', $start); + $this->setCell('A3', $count); + $sheet->getCell('B1')->setValue('=REPLACE(A1, A2, A3)'); + } else { + $this->setCell('A1', $oldText); + $this->setCell('A2', $start); + $this->setCell('A3', $count); + $this->setCell('A4', $newText); + $sheet->getCell('B1')->setValue('=REPLACE(A1, A2, A3, A4)'); + } + $result = $sheet->getCell('B1')->getCalculatedValue(); self::assertEquals($expectedResult, $result); } diff --git a/tests/PhpSpreadsheetTests/Calculation/Functions/TextData/ReptTest.php b/tests/PhpSpreadsheetTests/Calculation/Functions/TextData/ReptTest.php index a0c089cf66..ea86d1b60e 100644 --- a/tests/PhpSpreadsheetTests/Calculation/Functions/TextData/ReptTest.php +++ b/tests/PhpSpreadsheetTests/Calculation/Functions/TextData/ReptTest.php @@ -2,13 +2,7 @@ namespace PhpOffice\PhpSpreadsheetTests\Calculation\Functions\TextData; -use PhpOffice\PhpSpreadsheet\Calculation\Calculation; -use PhpOffice\PhpSpreadsheet\Calculation\Exception as CalcExp; -use PhpOffice\PhpSpreadsheet\Calculation\TextData; -use PhpOffice\PhpSpreadsheet\Spreadsheet; -use PHPUnit\Framework\TestCase; - -class ReptTest extends TestCase +class ReptTest extends AllSetupTeardown { /** * @dataProvider providerREPT @@ -17,37 +11,21 @@ class ReptTest extends TestCase * @param mixed $val * @param mixed $rpt */ - public function testReptDirect($expectedResult, $val = null, $rpt = null): void - { - $result = TextData::builtinREPT(is_string($val) ? trim($val, '"') : $val, $rpt); - self::assertEquals($expectedResult, $result); - } - - /** - * @dataProvider providerREPT - * - * @param mixed $expectedResult - * @param mixed $val - * @param mixed $rpt - */ - public function testReptThroughEngine($expectedResult, $val = null, $rpt = null): void + public function testReptThroughEngine($expectedResult, $val = 'omitted', $rpt = 'omitted'): void { - if ($val === null) { - $this->expectException(CalcExp::class); - $formula = '=REPT()'; - } elseif ($rpt === null) { - $this->expectException(CalcExp::class); - $formula = "=REPT($val)"; + $this->mightHaveException($expectedResult); + $sheet = $this->getSheet(); + if ($val === 'omitted') { + $sheet->getCell('B1')->setValue('=REPT()'); + } elseif ($rpt === 'omitted') { + $this->setCell('A1', $val); + $sheet->getCell('B1')->setValue('=REPT(A1)'); } else { - if (is_bool($val)) { - $val = ($val) ? Calculation::getTRUE() : Calculation::getFALSE(); - } - $formula = "=REPT($val, $rpt)"; + $this->setCell('A1', $val); + $this->setCell('A2', $rpt); + $sheet->getCell('B1')->setValue('=REPT(A1, A2)'); } - $spreadsheet = new Spreadsheet(); - $sheet = $spreadsheet->getActiveSheet(); - $sheet->getCell('A1')->setValue($formula); - $result = $sheet->getCell('A1')->getCalculatedValue(); + $result = $sheet->getCell('B1')->getCalculatedValue(); self::assertEquals($expectedResult, $result); } diff --git a/tests/PhpSpreadsheetTests/Calculation/Functions/TextData/RightTest.php b/tests/PhpSpreadsheetTests/Calculation/Functions/TextData/RightTest.php index a2ab3a4c36..b9051d0da1 100644 --- a/tests/PhpSpreadsheetTests/Calculation/Functions/TextData/RightTest.php +++ b/tests/PhpSpreadsheetTests/Calculation/Functions/TextData/RightTest.php @@ -2,25 +2,33 @@ namespace PhpOffice\PhpSpreadsheetTests\Calculation\Functions\TextData; -use PhpOffice\PhpSpreadsheet\Calculation\TextData; +use PhpOffice\PhpSpreadsheet\Calculation\Functions; use PhpOffice\PhpSpreadsheet\Settings; -use PHPUnit\Framework\TestCase; -class RightTest extends TestCase +class RightTest extends AllSetupTeardown { - protected function tearDown(): void - { - Settings::setLocale('en_US'); - } - /** * @dataProvider providerRIGHT * * @param mixed $expectedResult + * @param mixed $str string from which to extract + * @param mixed $cnt number of characters to extract */ - public function testRIGHT($expectedResult, ...$args): void + public function testRIGHT($expectedResult, $str = 'omitted', $cnt = 'omitted'): void { - $result = TextData::RIGHT(...$args); + $this->mightHaveException($expectedResult); + $sheet = $this->getSheet(); + if ($str === 'omitted') { + $sheet->getCell('B1')->setValue('=RIGHT()'); + } elseif ($cnt === 'omitted') { + $this->setCell('A1', $str); + $sheet->getCell('B1')->setValue('=RIGHT(A1)'); + } else { + $this->setCell('A1', $str); + $this->setCell('A2', $cnt); + $sheet->getCell('B1')->setValue('=RIGHT(A1, A2)'); + } + $result = $sheet->getCell('B1')->getCalculatedValue(); self::assertEquals($expectedResult, $result); } @@ -41,14 +49,15 @@ public function testLowerWithLocaleBoolean($expectedResult, $locale, $value, $ch { $newLocale = Settings::setLocale($locale); if ($newLocale === false) { - Settings::setLocale('en_US'); self::markTestSkipped('Unable to set locale for locale-specific test'); } - $result = TextData::RIGHT($value, $characters); + $sheet = $this->getSheet(); + $this->setCell('A1', $value); + $this->setCell('A2', $characters); + $sheet->getCell('B1')->setValue('=RIGHT(A1, A2)'); + $result = $sheet->getCell('B1')->getCalculatedValue(); self::assertEquals($expectedResult, $result); - - Settings::setLocale('en_US'); } public function providerLocaleRIGHT(): array @@ -64,4 +73,111 @@ public function providerLocaleRIGHT(): array ['ЖЬ', 'bg', false, 2], ]; } + + /** + * @dataProvider providerCalculationTypeRIGHTTrue + */ + public function testCalculationTypeTrue(string $type, string $resultB1, string $resultB2): void + { + Functions::setCompatibilityMode($type); + $sheet = $this->getSheet(); + $this->setCell('A1', true); + $this->setCell('A2', 'Hello'); + $this->setCell('B1', '=RIGHT(A1, 1)'); + $this->setCell('B2', '=RIGHT(A2, A1)'); + self::assertEquals($resultB1, $sheet->getCell('B1')->getCalculatedValue()); + self::assertEquals($resultB2, $sheet->getCell('B2')->getCalculatedValue()); + } + + public function providerCalculationTypeRIGHTTrue(): array + { + return [ + 'Excel RIGHT(true, 1) AND RIGHT("hello", true)' => [ + Functions::COMPATIBILITY_EXCEL, + 'E', + 'o', + ], + 'Gnumeric RIGHT(true, 1) AND RIGHT("hello", true)' => [ + Functions::COMPATIBILITY_GNUMERIC, + 'E', + 'o', + ], + 'OpenOffice RIGHT(true, 1) AND RIGHT("hello", true)' => [ + Functions::COMPATIBILITY_OPENOFFICE, + '1', + '#VALUE!', + ], + ]; + } + + /** + * @dataProvider providerCalculationTypeRIGHTFalse + */ + public function testCalculationTypeFalse(string $type, string $resultB1, string $resultB2): void + { + Functions::setCompatibilityMode($type); + $sheet = $this->getSheet(); + $this->setCell('A1', false); + $this->setCell('A2', 'Hello'); + $this->setCell('B1', '=RIGHT(A1, 1)'); + $this->setCell('B2', '=RIGHT(A2, A1)'); + self::assertEquals($resultB1, $sheet->getCell('B1')->getCalculatedValue()); + self::assertEquals($resultB2, $sheet->getCell('B2')->getCalculatedValue()); + } + + public function providerCalculationTypeRIGHTFalse(): array + { + return [ + 'Excel RIGHT(false, 1) AND RIGHT("hello", false)' => [ + Functions::COMPATIBILITY_EXCEL, + 'E', + '', + ], + 'Gnumeric RIGHT(false, 1) AND RIGHT("hello", false)' => [ + Functions::COMPATIBILITY_GNUMERIC, + 'E', + '', + ], + 'OpenOffice RIGHT(false, 1) AND RIGHT("hello", false)' => [ + Functions::COMPATIBILITY_OPENOFFICE, + '0', + '#VALUE!', + ], + ]; + } + + /** + * @dataProvider providerCalculationTypeRIGHTNull + */ + public function testCalculationTypeNull(string $type, string $resultB1, string $resultB2): void + { + Functions::setCompatibilityMode($type); + $sheet = $this->getSheet(); + $this->setCell('A2', 'Hello'); + $this->setCell('B1', '=RIGHT(A1, 1)'); + $this->setCell('B2', '=RIGHT(A2, A1)'); + self::assertEquals($resultB1, $sheet->getCell('B1')->getCalculatedValue()); + self::assertEquals($resultB2, $sheet->getCell('B2')->getCalculatedValue()); + } + + public function providerCalculationTypeRIGHTNull(): array + { + return [ + 'Excel RIGHT(null, 1) AND RIGHT("hello", null)' => [ + Functions::COMPATIBILITY_EXCEL, + '', + '', + ], + 'Gnumeric RIGHT(null, 1) AND RIGHT("hello", null)' => [ + Functions::COMPATIBILITY_GNUMERIC, + '', + 'o', + ], + 'OpenOffice RIGHT(null, 1) AND RIGHT("hello", null)' => [ + Functions::COMPATIBILITY_OPENOFFICE, + '', + '', + ], + ]; + } } diff --git a/tests/PhpSpreadsheetTests/Calculation/Functions/TextData/SearchTest.php b/tests/PhpSpreadsheetTests/Calculation/Functions/TextData/SearchTest.php index 14f5735ba7..5f1c526d1d 100644 --- a/tests/PhpSpreadsheetTests/Calculation/Functions/TextData/SearchTest.php +++ b/tests/PhpSpreadsheetTests/Calculation/Functions/TextData/SearchTest.php @@ -2,19 +2,36 @@ namespace PhpOffice\PhpSpreadsheetTests\Calculation\Functions\TextData; -use PhpOffice\PhpSpreadsheet\Calculation\TextData; -use PHPUnit\Framework\TestCase; - -class SearchTest extends TestCase +class SearchTest extends AllSetupTeardown { /** * @dataProvider providerSEARCH * * @param mixed $expectedResult + * @param mixed $findText + * @param mixed $withinText + * @param mixed $start */ - public function testSEARCH($expectedResult, ...$args): void + public function testSEARCH($expectedResult, $findText = 'omitted', $withinText = 'omitted', $start = 'omitted'): void { - $result = TextData::SEARCHINSENSITIVE(...$args); + $this->mightHaveException($expectedResult); + $sheet = $this->getSheet(); + if ($findText === 'omitted') { + $sheet->getCell('B1')->setValue('=SEARCH()'); + } elseif ($withinText === 'omitted') { + $this->setCell('A1', $findText); + $sheet->getCell('B1')->setValue('=SEARCH(A1)'); + } elseif ($start === 'omitted') { + $this->setCell('A1', $findText); + $this->setCell('A2', $withinText); + $sheet->getCell('B1')->setValue('=SEARCH(A1, A2)'); + } else { + $this->setCell('A1', $findText); + $this->setCell('A2', $withinText); + $this->setCell('A3', $start); + $sheet->getCell('B1')->setValue('=SEARCH(A1, A2, A3)'); + } + $result = $sheet->getCell('B1')->getCalculatedValue(); self::assertEquals($expectedResult, $result); } diff --git a/tests/PhpSpreadsheetTests/Calculation/Functions/TextData/SubstituteTest.php b/tests/PhpSpreadsheetTests/Calculation/Functions/TextData/SubstituteTest.php index 792e1a1520..bbb055912c 100644 --- a/tests/PhpSpreadsheetTests/Calculation/Functions/TextData/SubstituteTest.php +++ b/tests/PhpSpreadsheetTests/Calculation/Functions/TextData/SubstituteTest.php @@ -2,19 +2,43 @@ namespace PhpOffice\PhpSpreadsheetTests\Calculation\Functions\TextData; -use PhpOffice\PhpSpreadsheet\Calculation\TextData; -use PHPUnit\Framework\TestCase; - -class SubstituteTest extends TestCase +class SubstituteTest extends AllSetupTeardown { /** * @dataProvider providerSUBSTITUTE * * @param mixed $expectedResult + * @param mixed $text + * @param mixed $oldText + * @param mixed $newText + * @param mixed $instance */ - public function testSUBSTITUTE($expectedResult, ...$args): void + public function testSUBSTITUTE($expectedResult, $text = 'omitted', $oldText = 'omitted', $newText = 'omitted', $instance = 'omitted'): void { - $result = TextData::SUBSTITUTE(...$args); + $this->mightHaveException($expectedResult); + $sheet = $this->getSheet(); + if ($text === 'omitted') { + $sheet->getCell('B1')->setValue('=SUBSTITUTE()'); + } elseif ($oldText === 'omitted') { + $this->setCell('A1', $text); + $sheet->getCell('B1')->setValue('=SUBSTITUTE(A1)'); + } elseif ($newText === 'omitted') { + $this->setCell('A1', $text); + $this->setCell('A2', $oldText); + $sheet->getCell('B1')->setValue('=SUBSTITUTE(A1, A2)'); + } elseif ($instance === 'omitted') { + $this->setCell('A1', $text); + $this->setCell('A2', $oldText); + $this->setCell('A3', $newText); + $sheet->getCell('B1')->setValue('=SUBSTITUTE(A1, A2, A3)'); + } else { + $this->setCell('A1', $text); + $this->setCell('A2', $oldText); + $this->setCell('A3', $newText); + $this->setCell('A4', $instance); + $sheet->getCell('B1')->setValue('=SUBSTITUTE(A1, A2, A3, A4)'); + } + $result = $sheet->getCell('B1')->getCalculatedValue(); self::assertEquals($expectedResult, $result); } diff --git a/tests/PhpSpreadsheetTests/Calculation/Functions/TextData/TextJoinTest.php b/tests/PhpSpreadsheetTests/Calculation/Functions/TextData/TextJoinTest.php index 78e72f967e..60b21661cb 100644 --- a/tests/PhpSpreadsheetTests/Calculation/Functions/TextData/TextJoinTest.php +++ b/tests/PhpSpreadsheetTests/Calculation/Functions/TextData/TextJoinTest.php @@ -2,10 +2,7 @@ namespace PhpOffice\PhpSpreadsheetTests\Calculation\Functions\TextData; -use PhpOffice\PhpSpreadsheet\Calculation\TextData; -use PHPUnit\Framework\TestCase; - -class TextJoinTest extends TestCase +class TextJoinTest extends AllSetupTeardown { /** * @dataProvider providerTEXTJOIN @@ -14,7 +11,20 @@ class TextJoinTest extends TestCase */ public function testTEXTJOIN($expectedResult, array $args): void { - $result = TextData::TEXTJOIN(...$args); + $this->mightHaveException($expectedResult); + $sheet = $this->getSheet(); + $b1Formula = '=TEXTJOIN('; + $comma = ''; + $row = 0; + foreach ($args as $arg) { + ++$row; + $this->setCell("A$row", $arg); + $b1Formula .= $comma . "A$row"; + $comma = ','; + } + $b1Formula .= ')'; + $this->setCell('B1', $b1Formula); + $result = $sheet->getCell('B1')->getCalculatedValue(); self::assertEquals($expectedResult, $result); } diff --git a/tests/PhpSpreadsheetTests/Calculation/Functions/TextData/TextTest.php b/tests/PhpSpreadsheetTests/Calculation/Functions/TextData/TextTest.php index 914180652f..3ede2ffe9e 100644 --- a/tests/PhpSpreadsheetTests/Calculation/Functions/TextData/TextTest.php +++ b/tests/PhpSpreadsheetTests/Calculation/Functions/TextData/TextTest.php @@ -2,19 +2,30 @@ namespace PhpOffice\PhpSpreadsheetTests\Calculation\Functions\TextData; -use PhpOffice\PhpSpreadsheet\Calculation\TextData; -use PHPUnit\Framework\TestCase; - -class TextTest extends TestCase +class TextTest extends AllSetupTeardown { /** * @dataProvider providerTEXT * * @param mixed $expectedResult + * @param mixed $value + * @param mixed $format */ - public function testTEXT($expectedResult, ...$args): void + public function testTEXT($expectedResult, $value = 'omitted', $format = 'omitted'): void { - $result = TextData::TEXTFORMAT(...$args); + $this->mightHaveException($expectedResult); + $sheet = $this->getSheet(); + if ($value === 'omitted') { + $sheet->getCell('B1')->setValue('=TEXT()'); + } elseif ($format === 'omitted') { + $this->setCell('A1', $value); + $sheet->getCell('B1')->setValue('=TEXT(A1)'); + } else { + $this->setCell('A1', $value); + $this->setCell('A2', $format); + $sheet->getCell('B1')->setValue('=TEXT(A1, A2)'); + } + $result = $sheet->getCell('B1')->getCalculatedValue(); self::assertEquals($expectedResult, $result); } diff --git a/tests/PhpSpreadsheetTests/Calculation/Functions/TextData/TrimTest.php b/tests/PhpSpreadsheetTests/Calculation/Functions/TextData/TrimTest.php index 302dce4f9b..133e850f64 100644 --- a/tests/PhpSpreadsheetTests/Calculation/Functions/TextData/TrimTest.php +++ b/tests/PhpSpreadsheetTests/Calculation/Functions/TextData/TrimTest.php @@ -2,10 +2,7 @@ namespace PhpOffice\PhpSpreadsheetTests\Calculation\Functions\TextData; -use PhpOffice\PhpSpreadsheet\Calculation\TextData; -use PHPUnit\Framework\TestCase; - -class TrimTest extends TestCase +class TrimTest extends AllSetupTeardown { /** * @dataProvider providerTRIM @@ -13,9 +10,17 @@ class TrimTest extends TestCase * @param mixed $expectedResult * @param mixed $character */ - public function testTRIM($expectedResult, $character): void + public function testTRIM($expectedResult, $character = 'omitted'): void { - $result = TextData::TRIMSPACES($character); + $this->mightHaveException($expectedResult); + $sheet = $this->getSheet(); + if ($character === 'omitted') { + $sheet->getCell('B1')->setValue('=TRIM()'); + } else { + $this->setCell('A1', $character); + $sheet->getCell('B1')->setValue('=TRIM(A1)'); + } + $result = $sheet->getCell('B1')->getCalculatedValue(); self::assertEquals($expectedResult, $result); } diff --git a/tests/PhpSpreadsheetTests/Calculation/Functions/TextData/UpperTest.php b/tests/PhpSpreadsheetTests/Calculation/Functions/TextData/UpperTest.php index 45f7aea888..0147b5c183 100644 --- a/tests/PhpSpreadsheetTests/Calculation/Functions/TextData/UpperTest.php +++ b/tests/PhpSpreadsheetTests/Calculation/Functions/TextData/UpperTest.php @@ -2,26 +2,27 @@ namespace PhpOffice\PhpSpreadsheetTests\Calculation\Functions\TextData; -use PhpOffice\PhpSpreadsheet\Calculation\TextData; use PhpOffice\PhpSpreadsheet\Settings; -use PHPUnit\Framework\TestCase; -class UpperTest extends TestCase +class UpperTest extends AllSetupTeardown { - protected function tearDown(): void - { - Settings::setLocale('en_US'); - } - /** * @dataProvider providerUPPER * * @param mixed $expectedResult - * @param mixed $value + * @param mixed $str */ - public function testUPPER($expectedResult, $value): void + public function testUPPER($expectedResult, $str = 'omitted'): void { - $result = TextData::UPPERCASE($value); + $this->mightHaveException($expectedResult); + $sheet = $this->getSheet(); + if ($str === 'omitted') { + $sheet->getCell('B1')->setValue('=UPPER()'); + } else { + $this->setCell('A1', $str); + $sheet->getCell('B1')->setValue('=UPPER(A1)'); + } + $result = $sheet->getCell('B1')->getCalculatedValue(); self::assertEquals($expectedResult, $result); } @@ -41,14 +42,13 @@ public function testLowerWithLocaleBoolean($expectedResult, $locale, $value): vo { $newLocale = Settings::setLocale($locale); if ($newLocale === false) { - Settings::setLocale('en_US'); self::markTestSkipped('Unable to set locale for locale-specific test'); } - - $result = TextData::UPPERCASE($value); + $sheet = $this->getSheet(); + $this->setCell('A1', $value); + $sheet->getCell('B1')->setValue('=UPPER(A1)'); + $result = $sheet->getCell('B1')->getCalculatedValue(); self::assertEquals($expectedResult, $result); - - Settings::setLocale('en_US'); } public function providerLocaleLOWER(): array diff --git a/tests/PhpSpreadsheetTests/Calculation/Functions/TextData/ValueTest.php b/tests/PhpSpreadsheetTests/Calculation/Functions/TextData/ValueTest.php index b3ba1246cb..fbef254b4b 100644 --- a/tests/PhpSpreadsheetTests/Calculation/Functions/TextData/ValueTest.php +++ b/tests/PhpSpreadsheetTests/Calculation/Functions/TextData/ValueTest.php @@ -2,11 +2,9 @@ namespace PhpOffice\PhpSpreadsheetTests\Calculation\Functions\TextData; -use PhpOffice\PhpSpreadsheet\Calculation\TextData; use PhpOffice\PhpSpreadsheet\Shared\StringHelper; -use PHPUnit\Framework\TestCase; -class ValueTest extends TestCase +class ValueTest extends AllSetupTeardown { /** * @var string @@ -25,6 +23,7 @@ class ValueTest extends TestCase protected function setUp(): void { + parent::setUp(); $this->currencyCode = StringHelper::getCurrencyCode(); $this->decimalSeparator = StringHelper::getDecimalSeparator(); $this->thousandsSeparator = StringHelper::getThousandsSeparator(); @@ -32,6 +31,7 @@ protected function setUp(): void protected function tearDown(): void { + parent::tearDown(); StringHelper::setCurrencyCode($this->currencyCode); StringHelper::setDecimalSeparator($this->decimalSeparator); StringHelper::setThousandsSeparator($this->thousandsSeparator); @@ -43,13 +43,21 @@ protected function tearDown(): void * @param mixed $expectedResult * @param mixed $value */ - public function testVALUE($expectedResult, $value): void + public function testVALUE($expectedResult, $value = 'omitted'): void { StringHelper::setDecimalSeparator('.'); StringHelper::setThousandsSeparator(' '); StringHelper::setCurrencyCode('$'); - $result = TextData::VALUE($value); + $this->mightHaveException($expectedResult); + $sheet = $this->getSheet(); + if ($value === 'omitted') { + $sheet->getCell('B1')->setValue('=VALUE()'); + } else { + $this->setCell('A1', $value); + $sheet->getCell('B1')->setValue('=VALUE(A1)'); + } + $result = $sheet->getCell('B1')->getCalculatedValue(); self::assertEqualsWithDelta($expectedResult, $result, 1E-8); } diff --git a/tests/data/Calculation/TextData/CHAR.php b/tests/data/Calculation/TextData/CHAR.php index 276d701518..f02437b7d0 100644 --- a/tests/data/Calculation/TextData/CHAR.php +++ b/tests/data/Calculation/TextData/CHAR.php @@ -61,4 +61,10 @@ '#VALUE!', // '†', 0x2020, ], + 'omitted argument' => ['exception'], + 'non-printable' => ["\x02", 2], + 'bool argument' => ["\x01", true], + 'null argument' => ['#VALUE!', null], + 'ascii 1 is 49' => ['1', 49], + 'ascii 0 is 48' => ['0', 48], ]; diff --git a/tests/data/Calculation/TextData/CLEAN.php b/tests/data/Calculation/TextData/CLEAN.php index 6760888327..ad668271a3 100644 --- a/tests/data/Calculation/TextData/CLEAN.php +++ b/tests/data/Calculation/TextData/CLEAN.php @@ -25,4 +25,5 @@ null, null, ], + 'omitted argument' => ['exception'], ]; diff --git a/tests/data/Calculation/TextData/CODE.php b/tests/data/Calculation/TextData/CODE.php index 1b74b69918..8ab927263e 100644 --- a/tests/data/Calculation/TextData/CODE.php +++ b/tests/data/Calculation/TextData/CODE.php @@ -69,4 +69,5 @@ 0x2020, '†', ], + 'omitted argument' => ['exception'], ]; diff --git a/tests/data/Calculation/TextData/CONCATENATE.php b/tests/data/Calculation/TextData/CONCATENATE.php index f17b024bb3..c6b583eb2b 100644 --- a/tests/data/Calculation/TextData/CONCATENATE.php +++ b/tests/data/Calculation/TextData/CONCATENATE.php @@ -18,4 +18,5 @@ '-', true, ], + 'no arguments' => ['exception'], ]; diff --git a/tests/data/Calculation/TextData/DOLLAR.php b/tests/data/Calculation/TextData/DOLLAR.php index 205527b8d2..78fcc1b52e 100644 --- a/tests/data/Calculation/TextData/DOLLAR.php +++ b/tests/data/Calculation/TextData/DOLLAR.php @@ -45,4 +45,10 @@ 123.456, 'ABC', ], + 'omitted amount' => ['exception'], + 'omitted decimals' => ['$123.46', 123.456], + 'null decimals' => ['$123', 123.456, null], + 'boolean decimals' => ['$123.5', 123.456, true], + 'boolean value' => ['$1.00', true], + 'null value' => ['$0.00', null], ]; diff --git a/tests/data/Calculation/TextData/EXACT.php b/tests/data/Calculation/TextData/EXACT.php index f760a58fbc..f392b0b055 100644 --- a/tests/data/Calculation/TextData/EXACT.php +++ b/tests/data/Calculation/TextData/EXACT.php @@ -36,4 +36,6 @@ ' ', '', ], + 'no arguments' => ['exception'], + 'one argument1' => ['exception', 'abc'], ]; diff --git a/tests/data/Calculation/TextData/FIND.php b/tests/data/Calculation/TextData/FIND.php index 04d3276d47..1ae6d730ea 100644 --- a/tests/data/Calculation/TextData/FIND.php +++ b/tests/data/Calculation/TextData/FIND.php @@ -106,4 +106,6 @@ true, 'Mark Baker', ], + 'no arguments' => ['exception'], + 'one argument' => ['exception', 'a'], ]; diff --git a/tests/data/Calculation/TextData/FIXED.php b/tests/data/Calculation/TextData/FIXED.php index 2083eccebe..29734404cd 100644 --- a/tests/data/Calculation/TextData/FIXED.php +++ b/tests/data/Calculation/TextData/FIXED.php @@ -59,4 +59,9 @@ 'ABC', null, ], + 'no arguments' => ['exception'], + 'just one argument is okay' => ['123.00', 123], + 'null second argument' => ['123', 123, null], + 'false second argument' => ['123', 123, false], + 'true second argument' => ['123.0', 123, true], ]; diff --git a/tests/data/Calculation/TextData/LEFT.php b/tests/data/Calculation/TextData/LEFT.php index d524dc36f8..17d3fcf7a5 100644 --- a/tests/data/Calculation/TextData/LEFT.php +++ b/tests/data/Calculation/TextData/LEFT.php @@ -26,11 +26,15 @@ 'QWERTYUIOP', 'NaN', ], - [ - '#VALUE!', + 'null length defaults to 0' => [ + '', 'QWERTYUIOP', null, ], + 'omitted length defaults to 1' => [ + 'Q', + 'QWERTYUIOP', + ], [ 'ABC', 'ABCDEFGHI', @@ -61,4 +65,7 @@ false, 2, ], + 'string not specified' => [ + 'exception', + ], ]; diff --git a/tests/data/Calculation/TextData/LEN.php b/tests/data/Calculation/TextData/LEN.php index 6bf44fc9e5..babf3535a6 100644 --- a/tests/data/Calculation/TextData/LEN.php +++ b/tests/data/Calculation/TextData/LEN.php @@ -25,4 +25,5 @@ 5, false, ], + 'no arguments' => ['exception'], ]; diff --git a/tests/data/Calculation/TextData/LOWER.php b/tests/data/Calculation/TextData/LOWER.php index c5360b959d..23825546d2 100644 --- a/tests/data/Calculation/TextData/LOWER.php +++ b/tests/data/Calculation/TextData/LOWER.php @@ -29,4 +29,5 @@ 'false', false, ], + 'no argument' => ['exception'], ]; diff --git a/tests/data/Calculation/TextData/MID.php b/tests/data/Calculation/TextData/MID.php index b434f6704d..45e0ceab9b 100644 --- a/tests/data/Calculation/TextData/MID.php +++ b/tests/data/Calculation/TextData/MID.php @@ -37,11 +37,24 @@ 2, 'NaN', ], - [ - '#VALUE!', + 'length null treated as zero' => [ + '', + 'QWERTYUIOP', + 2, + null, + ], + 'length not specified' => [ + 'exception', 'QWERTYUIOP', 5, ], + 'start not specified' => [ + 'exception', + 'QWERTYUIOP', + ], + 'string not specified' => [ + 'exception', + ], [ 'IOP', 'QWERTYUIOP', diff --git a/tests/data/Calculation/TextData/NUMBERVALUE.php b/tests/data/Calculation/TextData/NUMBERVALUE.php index 3a6510b848..791e61cf85 100644 --- a/tests/data/Calculation/TextData/NUMBERVALUE.php +++ b/tests/data/Calculation/TextData/NUMBERVALUE.php @@ -3,50 +3,52 @@ return [ [ 1234567.89, - ['1,234,567.890'], + '1,234,567.890', ], [ 1234567.89, - ['1 234 567,890', ',', ' '], + '1 234 567,890', ',', ' ', ], [ -1234567.89, - ['-1 234 567,890', ',', ' '], + '-1 234 567,890', ',', ' ', ], [ '#VALUE!', - ['1 234 567,890-', ',', ' '], + '1 234 567,890-', ',', ' ', ], [ '#VALUE!', - ['1,234,567.890,123'], + '1,234,567.890,123', ], [ '#VALUE!', - ['1.234.567.890,123'], + '1.234.567.890,123', ], [ 1234567.890, - ['1.234.567,890', ',', '.'], + '1.234.567,890', ',', '.', ], [ '#VALUE!', - ['1.234.567,89'], + '1.234.567,89', ], [ 12345.6789, - ['1,234,567.89%'], + '1,234,567.89%', ], [ 123.456789, - ['1,234,567.89%%'], + '1,234,567.89%%', ], [ 1.23456789, - ['1,234,567.89%%%'], + '1,234,567.89%%%', ], [ '#VALUE!', - ['1,234,567.89-%'], + '1,234,567.89-%', ], + 'no arguments' => ['exception'], + 'boolean argument' => ['#VALUE!', true], ]; diff --git a/tests/data/Calculation/TextData/OpenOffice.php b/tests/data/Calculation/TextData/OpenOffice.php new file mode 100644 index 0000000000..fc79d5f11c --- /dev/null +++ b/tests/data/Calculation/TextData/OpenOffice.php @@ -0,0 +1,28 @@ + ["\x00", '=CHAR(0)'], + 'OO treats CODE(bool) as 0/1' => ['48', '=CODE(FALSE)'], + 'OO treats bool as string as 0/1 to REPT' => ['111', '=REPT(true, 3)'], + 'OO treats bool as string as 0/1 to CLEAN' => ['0', '=CLEAN(false)'], + 'OO treats bool as string as 0/1 to TRIM' => ['1', '=TRIM(true)'], + 'OO treats bool as string as 0/1 to LEN' => ['1', '=LEN(false)'], + 'OO treats bool as string as 0/1 to EXACT parm 1' => [true, '=EXACT(true, 1)'], + 'OO treats bool as string as 0/1 to EXACT parm 2' => [true, '=EXACT(0, false)'], + 'OO treats bool as string as 0/1 to FIND parm 1' => [2, '=FIND(true, "210")'], + 'OO treats bool as string as 0/1 to FIND parm 2' => [1, '=FIND(0, false)'], + 'OO treats true as int 1 to FIND parm 3' => [1, '=FIND("a", "aba", true)'], + 'OO treats false as int 0 to FIND parm 3' => ['#VALUE!', '=FIND("a", "aba", false)'], + 'OO treats bool as string as 0/1 to SEARCH parm 1' => [2, '=SEARCH(true, "210")'], + 'OO treats bool as string as 0/1 to SEARCH parm 2' => [1, '=SEARCH(0, false)'], + 'OO treats true as int 1 to SEARCH parm 3' => [1, '=SEARCH("a", "aba", true)'], + 'OO treats false as int 0 to SEARCH parm 3' => ['#VALUE!', '=SEARCH("a", "aba", false)'], + 'OO treats true as 1 to REPLACE parm 1' => ['10', '=REPLACE(true, 3, 1, false)'], + 'OO treats false as 0 to REPLACE parm 4' => ['he0lo', '=REPLACE("hello", 3, 1, false)'], + 'OO treats false as 0 SUBSTITUTE parm 1' => ['6', '=SUBSTITUTE(true, "1", "6")'], + 'OO treats true as 1 SUBSTITUTE parm 4' => ['zbcade', '=SUBSTITUTE("abcade", "a", "z", true)'], + 'OO TEXT boolean in lieu of string' => ['0', '=TEXT(false, "@")'], + 'OO VALUE boolean in lieu of string' => ['0', '=VALUE(false)'], + 'OO NUMBERVALUE boolean in lieu of string' => ['1', '=NUMBERVALUE(true)'], + 'OO TEXTJOIN boolean in lieu of string' => ['1-0-1', '=TEXTJOIN("-", true, true, false, true)'], +]; diff --git a/tests/data/Calculation/TextData/PROPER.php b/tests/data/Calculation/TextData/PROPER.php index 8bbf0e5c66..e9a5d4ceb6 100644 --- a/tests/data/Calculation/TextData/PROPER.php +++ b/tests/data/Calculation/TextData/PROPER.php @@ -25,4 +25,5 @@ 'False', false, ], + 'no argument' => ['exception'], ]; diff --git a/tests/data/Calculation/TextData/REPLACE.php b/tests/data/Calculation/TextData/REPLACE.php index 09e2296825..8f02e2e36d 100644 --- a/tests/data/Calculation/TextData/REPLACE.php +++ b/tests/data/Calculation/TextData/REPLACE.php @@ -57,4 +57,12 @@ 0, ' ', ], + 'no arguments' => ['exception'], + 'one argument' => ['exception', 'hello'], + 'two arguments' => ['exception', 'hello', 2], + 'three arguments' => ['exception', 'hello', 2, 2], + 'position zero' => ['#VALUE!', 'hello', 0, 2, 'xyz'], + 'negative length' => ['#VALUE!', 'hello', 3, -1, 'xyz'], + 'boolean 1st parm' => ['TRDFGE', true, 3, 1, 'DFG'], + 'boolean 4th parm' => ['heFALSElo', 'hello', 3, 1, false], ]; diff --git a/tests/data/Calculation/TextData/REPT.php b/tests/data/Calculation/TextData/REPT.php index 2c8d1c0d53..f8aef72c8b 100644 --- a/tests/data/Calculation/TextData/REPT.php +++ b/tests/data/Calculation/TextData/REPT.php @@ -1,14 +1,14 @@ ['exception'], + 'one argument' => ['exception', 'ABC'], + ['#VALUE!', 'ABC', 'DEF'], + ['ABCABCABC', 'ABC', 3], + ['ABCABC', 'ABC', 2.2], + ['', 'ABC', 0], ['TRUETRUE', true, 2], ['111', 1, 3], - ['δύο δύο ', '"δύο "', 2], - ['#VALUE!', '"ABC"', -1], + ['δύο δύο ', 'δύο ', 2], + ['#VALUE!', 'ABC', -1], ]; diff --git a/tests/data/Calculation/TextData/RIGHT.php b/tests/data/Calculation/TextData/RIGHT.php index e6928df229..77ea64bf71 100644 --- a/tests/data/Calculation/TextData/RIGHT.php +++ b/tests/data/Calculation/TextData/RIGHT.php @@ -21,11 +21,15 @@ 'QWERTYUIOP', 'NaN', ], - [ - '#VALUE!', + 'null length defaults to 0' => [ + '', 'QWERTYUIOP', null, ], + 'omitted length defaults to 1' => [ + 'P', + 'QWERTYUIOP', + ], [ 'GHI', 'ABCDEFGHI', @@ -61,4 +65,7 @@ false, 2, ], + 'string not specified' => [ + 'exception', + ], ]; diff --git a/tests/data/Calculation/TextData/SEARCH.php b/tests/data/Calculation/TextData/SEARCH.php index fa970bec43..335a7f12a8 100644 --- a/tests/data/Calculation/TextData/SEARCH.php +++ b/tests/data/Calculation/TextData/SEARCH.php @@ -99,4 +99,6 @@ true, 'Mark Baker', ], + 'no arguments' => ['exception'], + 'one argument' => ['exception', 'string'], ]; diff --git a/tests/data/Calculation/TextData/SUBSTITUTE.php b/tests/data/Calculation/TextData/SUBSTITUTE.php index 97cb8d0ff5..9b695a715e 100644 --- a/tests/data/Calculation/TextData/SUBSTITUTE.php +++ b/tests/data/Calculation/TextData/SUBSTITUTE.php @@ -73,4 +73,16 @@ "\u{00E5}", 'x', ], + 'no arguments' => ['exception'], + 'one argument' => ['exception', 'a'], + 'two arguments' => ['exception', 'a', 'b'], + 'negative instance' => ['#VALUE!', 'abcdefg', 'def', 123, -1], + 'non-numeric instance' => ['#VALUE!', 'abcdefg', 'def', 123, 'xyz'], + 'null instance' => ['abc123g', 'abcdefg', 'def', 123], + '0 instance' => ['#VALUE!', 'abcdefg', 'def', 123, 0], + '1 instance' => ['abc123g', 'abcdefg', 'def', 123, 1], + 'past last instance' => ['abcdefg', 'abcdefg', 'def', 123, 2], + 'bool false instance' => ['#VALUE!', 'abcdefg', 'def', '123', false], + 'bool true instance' => ['#VALUE!', 'abcdefg', 'def', '123', true], + 'bool text' => ['FA-SE', false, 'L', '-'], ]; diff --git a/tests/data/Calculation/TextData/TEXT.php b/tests/data/Calculation/TextData/TEXT.php index fcb05191ae..1c7c338adb 100644 --- a/tests/data/Calculation/TextData/TEXT.php +++ b/tests/data/Calculation/TextData/TEXT.php @@ -66,4 +66,7 @@ 1.75, '# ?/?', ], + 'no arguments' => ['exception'], + 'one argument' => ['exception', 1.75], + 'boolean in lieu of string' => ['TRUE', true, '@'], ]; diff --git a/tests/data/Calculation/TextData/TEXTJOIN.php b/tests/data/Calculation/TextData/TEXTJOIN.php index 9c6b424689..e345f7c177 100644 --- a/tests/data/Calculation/TextData/TEXTJOIN.php +++ b/tests/data/Calculation/TextData/TEXTJOIN.php @@ -37,4 +37,9 @@ 'C:\\Users\\Mark\\Documents\\notes.doc', ['\\', true, 'C:', 'Users', 'Mark', 'Documents', 'notes.doc'], ], + 'no argument' => ['exception', []], + 'one argument' => ['exception', ['-']], + 'two arguments' => ['exception', ['-', true]], + 'three arguments' => ['a', ['-', true, 'a']], + 'boolean as string' => ['TRUE-FALSE-TRUE', ['-', true, true, false, true]], ]; diff --git a/tests/data/Calculation/TextData/TRIM.php b/tests/data/Calculation/TextData/TRIM.php index f400f1872f..882dc824d1 100644 --- a/tests/data/Calculation/TextData/TRIM.php +++ b/tests/data/Calculation/TextData/TRIM.php @@ -29,4 +29,5 @@ null, null, ], + 'no arguments' => ['exception'], ]; diff --git a/tests/data/Calculation/TextData/UPPER.php b/tests/data/Calculation/TextData/UPPER.php index e5d2f18eae..21b04ae46a 100644 --- a/tests/data/Calculation/TextData/UPPER.php +++ b/tests/data/Calculation/TextData/UPPER.php @@ -29,4 +29,5 @@ 'FALSE', false, ], + 'no arguments' => ['exception'], ]; diff --git a/tests/data/Calculation/TextData/VALUE.php b/tests/data/Calculation/TextData/VALUE.php index d859c08703..666f12d102 100644 --- a/tests/data/Calculation/TextData/VALUE.php +++ b/tests/data/Calculation/TextData/VALUE.php @@ -41,4 +41,7 @@ '0.11527777777778', '2:46 AM', ], + 'no arguments' => ['exception'], + 'bool argument' => ['#VALUE!', false], + 'null argument' => ['0', null], ];