TextData - Minor Changes, Test Coverage (#2151)

* PHP8.1 Deprecation Passing Null to String Function

For each of the files in this PR, one or more statements can pass a null to string functions like strlower. This is deprecated in PHP8.1, and, when deprecated messages are enabled, causes many tests to error out. In every case, use coercion to pass null string rather than null.

* TextData - Minor Changes, Test Coverage

Per agreement on a previous push, I looked into standardizing the initialization of the TextData functions (like Engineering and MathTrig), with particular regard for avoiding multiple later null coercions. This simplifies the code quite a bit. This PR also increases coverage to 100% for all TextData modules. All entries in Phpstan baseline for non-deprecated TEXTDATA functions are removed. There were some minor bugfixes.

Whereas Excel (and Gnumeric) treat booleans when supplied as strings as 'TRUE' or 'FALSE', ODS treats them as '1' or '0'. Unlike Excel, ODS generally does not allow bool for int arguments; it does, however, allow them for FIND and SEARCH. ODS allows boolean for into for SUBSTITUTE even though Excel doesn't. ODS allows bool for string for NUMBERVALUE and VALUE even though Excel doesn't. ODS accepts 0 as an argument for CHAR; Excel doesn't. Most of this seems like random decisions on the part of the developers; I've done my best to follow the products in each case. There is a new test member devoted to ODS tests.

Gnumeric has an anomaly vis-a-vis the others - if length is supplied to LEFT/MID/RIGHT as null, Gnumeric treats it as 0 rather than 1.

All tests now take place in the context of a spreadsheet ...

Except for RETURNSTRING, which is not the implementation of an Excel function, and is referred to in the rest of PhpSpreadsheet only in the unit tests for itself. It should probably be deprecated, but that is not part of this PR, just in case there is some reason for it that I couldn't discern.

I have tried to make the first line of each doc block identify the Excel function name rather than its name in PhpSpreadsheet. I think it makes things more comprehensible.

Some tests call Settings::setLocale, but there was no Settings::getLocale. At the end of the tests which do it, they invoke setLocale('EN-US'), which, in a practical sense, is sufficient. However, in theory it would be better for them to get the current locale before changing it, then changing it back to the original when the time came. I have added getLocale and made the appropriate testing change.

The CHAR function took an interesting turn. One can set the value of a cell to, say, CHAR(2), the ASCII/UTF-8 representation of a control character, which is not legal in certain contexts. The only Reader/Writer that could handle this without problems is Xls, which deals with binary data all the time. However, if you tried to write it to Xlsx, Excel would not be able to open the resulting file because of what it considers an illegal character. I changed the Xlsx writer to escape such characters when writing the value of a string function. I did not make any other changes to the Xlsx writer - it seems to me that setting a cell to CHAR(2) is legitimate, but setting it to say `"\x02"` seems less likely to be legitimate, so the latter will still fail (although `="\x02"` should work). The Xlsx reader already supports the escape mechanism that I added to the writer.

CHAR control character and Ods - not supported by either Reader or Writer. I did not attempt to add this now. There is lots still missing from ODS, and this item just can't be a high priority amongst all of those.

CHAR control character and Csv - it is supported by reader and writer if the file has a csv extension. However, trying to guess the mime type without an extension - the control character makes mime_get_type guess application/octet-stream, and PhpSpreadsheet therefore thinks that Csv can't read it.

CHAR control character and Html. Actual use of the control character in the file is subject to the same problems as Xml (i.e. Xlsx and Ods). It wasn't terribly difficult to get the Html Writer to change `"\x02"` to "``". I believe that this is technically legal; however, DOMDocument.loadHTML rejects it as an illegal entity, and I am not convinced that it is wrong to do so, so I haven't changed the Html writer.

* Scrutinizer

Correct 3 minor errors.
This commit is contained in:
oleibman 2021-06-14 23:37:17 -07:00 committed by GitHub
parent 55b95201ab
commit 4f06d84248
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
66 changed files with 1425 additions and 559 deletions

View File

@ -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

View File

@ -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 @@ class CaseConvert
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 @@ class CaseConvert
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);
}

View File

@ -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)) {
$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));
$character = (int) $character;
if ($character < 1 || $character > 255) {
return Functions::VALUE();
}
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 @@ class CharacterConvert
*/
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 @@ class CharacterConvert
return self::unicodeToOrd($character);
}
private static function unicodeToOrd($character)
private static function unicodeToOrd(string $character): int
{
return unpack('V', iconv('UTF-8', 'UCS-4LE', $character))[1];
}
private static function convertBooleanValue($value)
{
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;
}
}

View File

@ -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 @@ class Concatenate
// 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 @@ class Concatenate
*/
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 @@ class Concatenate
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();
}
}

View File

@ -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,17 +12,13 @@ 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');
@ -36,18 +31,14 @@ class Extract
* @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();
}
if (is_bool($value)) {
$value = ($value) ? Calculation::getTRUE() : Calculation::getFALSE();
try {
$value = Helpers::extractString($value);
$start = Helpers::extractInt($start, 1);
$chars = Helpers::extractInt($chars, 0);
} catch (CalcExp $e) {
return $e->getMessage();
}
return mb_substr($value ?? '', --$start, $chars, 'UTF-8');
@ -59,17 +50,13 @@ 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 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');

View File

@ -4,6 +4,7 @@ namespace PhpOffice\PhpSpreadsheet\Calculation\TextData;
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 @@ class Format
}
/**
* 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 @@ class Format
*/
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 @@ class Format
}
/**
* 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 @@ class Format
*/
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 @@ class Format
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 @@ class Format
*/
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 @@ class Format
}
}
return (float) $value;
return is_array($value) ? Functions::VALUE() : (float) $value;
}
}

View File

@ -0,0 +1,90 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Calculation\TextData;
use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
use PhpOffice\PhpSpreadsheet\Calculation\Exception as CalcExp;
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
class Helpers
{
public static function convertBooleanValue(bool $value): string
{
if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_OPENOFFICE) {
return $value ? '1' : '0';
}
return ($value) ? Calculation::getTRUE() : Calculation::getFALSE();
}
/**
* @param mixed $value String value from which to extract characters
*/
public static function extractString($value): string
{
$value = Functions::flattenSingleValue($value);
if (is_bool($value)) {
return self::convertBooleanValue($value);
}
return (string) $value;
}
/**
* @param mixed $value
*/
public static function extractInt($value, int $minValue, int $gnumericNull = 0, bool $ooBoolOk = false): int
{
$value = Functions::flattenSingleValue($value);
if ($value === null) {
// usually 0, but sometimes 1 for Gnumeric
$value = (Functions::getCompatibilityMode() === Functions::COMPATIBILITY_GNUMERIC) ? $gnumericNull : 0;
}
if (is_bool($value) && ($ooBoolOk || Functions::getCompatibilityMode() !== Functions::COMPATIBILITY_OPENOFFICE)) {
$value = (int) $value;
}
if (!is_numeric($value)) {
throw new CalcExp(Functions::VALUE());
}
$value = (int) $value;
if ($value < $minValue) {
throw new CalcExp(Functions::VALUE());
}
return (int) $value;
}
/**
* @param mixed $value
*/
public static function extractFloat($value): float
{
$value = Functions::flattenSingleValue($value);
if ($value === null) {
$value = 0.0;
}
if (is_bool($value)) {
$value = (float) $value;
}
if (!is_numeric($value)) {
throw new CalcExp(Functions::VALUE());
}
return (float) $value;
}
/**
* @param mixed $value
*/
public static function validateInt($value): int
{
$value = Functions::flattenSingleValue($value);
if ($value === null) {
$value = 0;
} elseif (is_bool($value)) {
$value = (int) $value;
}
return (int) $value;
}
}

View File

@ -2,6 +2,7 @@
namespace PhpOffice\PhpSpreadsheet\Calculation\TextData;
use PhpOffice\PhpSpreadsheet\Calculation\Exception as CalcExp;
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
class Replace
@ -16,13 +17,17 @@ class Replace
*/
public static function replace($oldText, $start, $chars, $newText): string
{
$oldText = Functions::flattenSingleValue($oldText);
$start = Functions::flattenSingleValue($start);
$chars = Functions::flattenSingleValue($chars);
$newText = Functions::flattenSingleValue($newText);
try {
$start = Helpers::extractInt($start, 1, 0, true);
$chars = Helpers::extractInt($chars, 0, 0, true);
$oldText = Helpers::extractString($oldText);
$newText = Helpers::extractString($newText);
$left = mb_substr($oldText, 0, $start - 1, 'UTF-8');
$left = Extract::left($oldText, $start - 1);
$right = Extract::right($oldText, Text::length($oldText) - ($start + $chars) + 1);
$right = mb_substr($oldText, $start + $chars - 1, null, 'UTF-8');
} catch (CalcExp $e) {
return $e->getMessage();
}
return $left . $newText . $right;
}
@ -35,15 +40,25 @@ class Replace
* @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;

View File

@ -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 @@ class Search
}
/**
* 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 @@ class Search
*/
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;
}
}

View File

@ -2,23 +2,18 @@
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);
if (is_bool($value)) {
$value = ($value) ? Calculation::getTRUE() : Calculation::getFALSE();
}
$value = Helpers::extractString($value);
return mb_strlen($value ?? '', 'UTF-8');
}
@ -33,10 +28,10 @@ class Text
*/
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;
}
/**

View File

@ -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);
$stringValue = Helpers::extractString($stringValue);
if (is_bool($stringValue)) {
return ($stringValue) ? Calculation::getTRUE() : Calculation::getFALSE();
}
if (self::$invalidChars === null) {
self::$invalidChars = range(chr(0), chr(31));
}
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();
}
$stringValue = Helpers::extractString($stringValue);
if (is_string($stringValue) || is_numeric($stringValue)) {
return trim(preg_replace('/ +/', ' ', trim($stringValue, ' ')), ' ');
}
return null;
return trim(preg_replace('/ +/', ' ', trim("$stringValue", ' ')) ?? '', ' ');
}
}

View File

@ -68,6 +68,11 @@ class Settings
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.
*

View File

@ -1234,6 +1234,7 @@ class Worksheet extends WriterPart
return;
}
$objWriter->writeAttribute('t', 'str');
$calculatedValue = StringHelper::controlCharacterPHP2OOXML($calculatedValue);
} elseif (is_bool($calculatedValue)) {
$objWriter->writeAttribute('t', 'b');
$calculatedValue = (int) $calculatedValue;

View File

@ -0,0 +1,105 @@
<?php
namespace PhpOffice\PhpSpreadsheetTests\Calculation\Functions\TextData;
use PhpOffice\PhpSpreadsheet\Calculation\Exception as CalcException;
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
use PhpOffice\PhpSpreadsheet\Cell\DataType;
use PhpOffice\PhpSpreadsheet\Settings;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
use PHPUnit\Framework\TestCase;
class AllSetupTeardown extends TestCase
{
/**
* @var string
*/
private $compatibilityMode;
/**
* @var string
*/
private $locale;
/**
* @var ?Spreadsheet
*/
private $spreadsheet;
/**
* @var ?Worksheet
*/
private $sheet;
protected function setUp(): void
{
$this->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;
}
}

View File

@ -0,0 +1,47 @@
<?php
namespace PhpOffice\PhpSpreadsheetTests\Calculation\Functions\TextData;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheetTests\Functional\AbstractFunctional;
class CharNonPrintableTest extends AbstractFunctional
{
/**
* @dataProvider providerType
*/
public function testNotPrintable(string $type): void
{
$spreadsheet = new Spreadsheet();
$sheet = $spreadsheet->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 '&#2;' even though legal html.
//['Html'],
];
}
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -0,0 +1,44 @@
<?php
namespace PhpOffice\PhpSpreadsheetTests\Calculation\Functions\TextData;
use PhpOffice\PhpSpreadsheet\Calculation\TextData;
use PHPUnit\Framework\TestCase;
// Sanity tests for functions which have been moved out of TextData
// to their own classes. A deprecated version remains in TextData;
// this class contains cursory tests to ensure that those work properly.
// If Scrutinizer fails the PR because of these deprecations, I will
// remove this class from the PR.
class DeprecatedTest extends TestCase
{
public function testDeprecated(): void
{
self::assertSame('x', TextData::TRIMNONPRINTABLE('x'));
self::assertSame('x y', TextData::TRIMSPACES('x y'));
self::assertSame(48, TextData::ASCIICODE('0'));
self::assertSame('abc', TextData::CONCATENATE('a', 'b', 'c'));
self::assertSame('$1.00', TextData::DOLLAR(1));
self::assertEquals(2, TextData::SEARCHSENSITIVE('b', 'abc'));
self::assertEquals(2, TextData::SEARCHINSENSITIVE('b', 'abc'));
self::assertSame('1.00', TextData::FIXEDFORMAT(1));
self::assertSame('xyz', TextData::LEFT('xyzw', 3));
self::assertSame('yz', TextData::MID('xyzw', 2, 2));
self::assertSame('zw', TextData::RIGHT('xyzw', 2));
self::assertSame(4, TextData::STRINGLENGTH('xyzw'));
self::assertSame('xyzw', TextData::LOWERCASE('Xyzw'));
self::assertSame('XYZW', TextData::UPPERCASE('Xyzw'));
self::assertSame('Xyzw', TextData::PROPERCASE('xyzw'));
self::assertSame('xabw', TextData::REPLACE('xyzw', 2, 2, 'ab'));
self::assertSame('xyzw', TextData::TEXTFORMAT('xyzw', '@'));
self::assertEquals(3, TextData::VALUE('3'));
self::assertEquals(3, TextData::NUMBERVALUE('3'));
self::assertTrue(TextData::EXACT('3', '3'));
self::assertSame('a,b,c', TextData::TEXTJOIN(',', true, 'a', 'b', 'c'));
self::assertSame('aaa', TextData::builtinREPT('a', 3));
self::assertSame('ayxw', TextData::SUBSTITUTE('xyxw', 'x', 'a', 1));
self::assertSame('1', TextData::CHARACTER('49'));
self::assertSame('0', TextData::CHARACTER('48'));
}
}

View File

@ -2,19 +2,30 @@
namespace PhpOffice\PhpSpreadsheetTests\Calculation\Functions\TextData;
use PhpOffice\PhpSpreadsheet\Calculation\TextData;
use PHPUnit\Framework\TestCase;
class DollarTest extends TestCase
class DollarTest extends AllSetupTeardown
{
/**
* @dataProvider providerDOLLAR
*
* @param mixed $expectedResult
* @param mixed $amount
* @param mixed $decimals
*/
public function testDOLLAR($expectedResult, ...$args): void
public function testDOLLAR($expectedResult, $amount = 'omitted', $decimals = 'omitted'): void
{
$result = TextData::DOLLAR(...$args);
$this->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);
}

View File

@ -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

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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 @@ class LeftTest extends TestCase
{
$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 @@ class LeftTest extends TestCase
['ЛОЖ', '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,
'',
'',
],
];
}
}

View File

@ -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);
}

View File

@ -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 @@ class LowerTest extends TestCase
{
$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

View File

@ -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 @@ class MidTest extends TestCase
* @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,125 @@ class MidTest extends TestCase
['ОЖ', '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());
self::assertEquals($resultB3, $sheet->getCell('B3')->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!',
'',
],
];
}
}

View File

@ -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);
}

View File

@ -0,0 +1,26 @@
<?php
namespace PhpOffice\PhpSpreadsheetTests\Calculation\Functions\TextData;
class OpenOfficeTest extends AllSetupTeardown
{
/**
* @dataProvider providerOpenOffice
*
* @param mixed $expectedResult
*/
public function testOpenOffice($expectedResult, string $formula): void
{
$this->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';
}
}

View File

@ -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 @@ class ProperTest extends TestCase
{
$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

View File

@ -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);
}

View File

@ -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
public function testReptThroughEngine($expectedResult, $val = 'omitted', $rpt = 'omitted'): 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
{
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);
}

View File

@ -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 @@ class RightTest extends TestCase
{
$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 @@ class RightTest extends TestCase
['ЖЬ', '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,
'',
'',
],
];
}
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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 @@ class UpperTest extends TestCase
{
$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

View File

@ -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 @@ class ValueTest extends TestCase
protected function tearDown(): void
{
parent::tearDown();
StringHelper::setCurrencyCode($this->currencyCode);
StringHelper::setDecimalSeparator($this->decimalSeparator);
StringHelper::setThousandsSeparator($this->thousandsSeparator);
@ -43,13 +43,21 @@ class ValueTest extends TestCase
* @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);
}

View File

@ -61,4 +61,10 @@ return [
'#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],
];

View File

@ -25,4 +25,5 @@ return [
null,
null,
],
'omitted argument' => ['exception'],
];

View File

@ -69,4 +69,5 @@ return [
0x2020,
'†',
],
'omitted argument' => ['exception'],
];

View File

@ -18,4 +18,5 @@ return [
'-',
true,
],
'no arguments' => ['exception'],
];

View File

@ -45,4 +45,10 @@ return [
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],
];

View File

@ -36,4 +36,6 @@ return [
' ',
'',
],
'no arguments' => ['exception'],
'one argument1' => ['exception', 'abc'],
];

View File

@ -106,4 +106,6 @@ return [
true,
'Mark Baker',
],
'no arguments' => ['exception'],
'one argument' => ['exception', 'a'],
];

View File

@ -59,4 +59,9 @@ return [
'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],
];

View File

@ -26,11 +26,15 @@ return [
'QWERTYUIOP',
'NaN',
],
[
'#VALUE!',
'null length defaults to 0' => [
'',
'QWERTYUIOP',
null,
],
'omitted length defaults to 1' => [
'Q',
'QWERTYUIOP',
],
[
'ABC',
'ABCDEFGHI',
@ -61,4 +65,7 @@ return [
false,
2,
],
'string not specified' => [
'exception',
],
];

View File

@ -25,4 +25,5 @@ return [
5,
false,
],
'no arguments' => ['exception'],
];

View File

@ -29,4 +29,5 @@ return [
'false',
false,
],
'no argument' => ['exception'],
];

View File

@ -37,11 +37,24 @@ return [
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',

View File

@ -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],
];

View File

@ -0,0 +1,28 @@
<?php
return [
'OO allows CHAR(0)' => ["\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)'],
];

View File

@ -25,4 +25,5 @@ return [
'False',
false,
],
'no argument' => ['exception'],
];

View File

@ -57,4 +57,12 @@ return [
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],
];

View File

@ -1,14 +1,14 @@
<?php
return [
['#VALUE!'], // exception not enough args
['#VALUE!', '"ABC"'], // exception not enough args
['#VALUE!', '"ABC"', '"DEF"'],
['ABCABCABC', '"ABC"', 3],
['ABCABC', '"ABC"', 2.2],
['', '"ABC"', 0],
'no arguments' => ['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],
];

View File

@ -21,11 +21,15 @@ return [
'QWERTYUIOP',
'NaN',
],
[
'#VALUE!',
'null length defaults to 0' => [
'',
'QWERTYUIOP',
null,
],
'omitted length defaults to 1' => [
'P',
'QWERTYUIOP',
],
[
'GHI',
'ABCDEFGHI',
@ -61,4 +65,7 @@ return [
false,
2,
],
'string not specified' => [
'exception',
],
];

View File

@ -99,4 +99,6 @@ return [
true,
'Mark Baker',
],
'no arguments' => ['exception'],
'one argument' => ['exception', 'string'],
];

View File

@ -73,4 +73,16 @@ return [
"\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', '-'],
];

View File

@ -66,4 +66,7 @@ return [
1.75,
'# ?/?',
],
'no arguments' => ['exception'],
'one argument' => ['exception', 1.75],
'boolean in lieu of string' => ['TRUE', true, '@'],
];

View File

@ -37,4 +37,9 @@ return [
'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]],
];

View File

@ -29,4 +29,5 @@ return [
null,
null,
],
'no arguments' => ['exception'],
];

View File

@ -29,4 +29,5 @@ return [
'FALSE',
false,
],
'no arguments' => ['exception'],
];

View File

@ -41,4 +41,7 @@ return [
'0.11527777777778',
'2:46 AM',
],
'no arguments' => ['exception'],
'bool argument' => ['#VALUE!', false],
'null argument' => ['0', null],
];