Merge branch 'master' into CalcEngine_Structured-References

This commit is contained in:
Mark Baker
2022-12-29 17:38:13 +01:00
committed by GitHub
17 changed files with 330 additions and 48 deletions
+17 -1
View File
@@ -184,6 +184,22 @@ code:
it disables several Office2007 file format options, resulting in a
lower-featured Office2007 spreadsheet.
### Form Control Fields
PhpSpreadsheet offers limited support for Forms Controls (buttons,
checkboxes, etc.). The support is available only for Excel 2007 format,
and is offered solely to allow loading a spreadsheet with such controls
and saving it as a new file.
Support is not available for adding such elements to the spreadsheet,
nor even to locate them to determine their properties
(so you can't modify or delete them).
Modifications to a worksheet with controls are "caveat emptor";
some modifications will work correctly,
but others are very likely to cause problems,
e.g. adding a comment to the worksheet,
or inserting or deleting rows or columns in a manner that would
cause the controls to change location.
## Excel 5 (BIFF) file format
Xls file format is the old Excel file format, implemented in
@@ -1099,7 +1115,7 @@ Flags that are available that can be passed to the Reader in this way include:
| Slk | N/A | NO | NO |
| Csv | N/A | NO | NO |
Likewise, when saving a file using a Writer, loaded charts wil not be saved unless you explicitly tell the Writer to include them:
Likewise, when saving a file using a Writer, loaded charts will not be saved unless you explicitly tell the Writer to include them:
```php
$writer = IOFactory::createWriter($spreadsheet, 'Xlsx');
@@ -0,0 +1,22 @@
<?php
use PhpOffice\PhpSpreadsheet\IOFactory;
require __DIR__ . '/../Header.php';
$helper->log('Start');
$inputFileType = 'Xlsx';
$inputFileName = __DIR__ . '/sampleData/formscomments.xlsx';
$helper->log('Loading file ' . $inputFileName . ' using IOFactory with a defined reader type of ' . $inputFileType);
$reader = IOFactory::createReader($inputFileType);
$helper->log('Loading all WorkSheets');
$reader->setLoadAllSheets();
$spreadsheet = $reader->load($inputFileName);
// Save
$helper->write($spreadsheet, __FILE__, ['Xlsx']);
$spreadsheet->disconnectWorksheets();
$helper->log('end');
+1
View File
@@ -17,5 +17,6 @@ $spreadsheet = $reader->load($inputFileName);
// Save
$helper->write($spreadsheet, __FILE__);
$spreadsheet->disconnectWorksheets();
$helper->log('end');
Binary file not shown.
@@ -51,8 +51,11 @@ class NonPeriodic
$f2 = self::xnpvOrdered($x2, $values, $dates, false);
$found = false;
for ($i = 0; $i < self::FINANCIAL_MAX_ITERATIONS; ++$i) {
if (!is_numeric($f1) || !is_numeric($f2)) {
break;
if (!is_numeric($f1)) {
return $f1;
}
if (!is_numeric($f2)) {
return $f2;
}
$f1 = (float) $f1;
$f2 = (float) $f2;
@@ -68,11 +71,32 @@ class NonPeriodic
$f2 = self::xnpvOrdered($x2, $values, $dates, false);
}
}
if (!$found) {
return ExcelError::NAN();
if ($found) {
return self::xirrPart3($values, $dates, $x1, $x2);
}
return self::xirrPart3($values, $dates, $x1, $x2);
// Newton-Raphson didn't work - try bisection
$x1 = $guess - 0.5;
$x2 = $guess + 0.5;
for ($i = 0; $i < self::FINANCIAL_MAX_ITERATIONS; ++$i) {
$f1 = self::xnpvOrdered($x1, $values, $dates, false, true);
$f2 = self::xnpvOrdered($x2, $values, $dates, false, true);
if (!is_numeric($f1) || !is_numeric($f2)) {
break;
}
if ($f1 * $f2 <= 0) {
$found = true;
break;
}
$x1 -= 0.5;
$x2 += 0.5;
}
if ($found) {
return self::xirrBisection($values, $dates, $x1, $x2);
}
return ExcelError::NAN();
}
/**
@@ -190,6 +214,45 @@ class NonPeriodic
return $rslt;
}
/**
* @return float|string
*/
private static function xirrBisection(array $values, array $dates, float $x1, float $x2)
{
$rslt = ExcelError::NAN();
for ($i = 0; $i < self::FINANCIAL_MAX_ITERATIONS; ++$i) {
$rslt = ExcelError::NAN();
$f1 = self::xnpvOrdered($x1, $values, $dates, false, true);
$f2 = self::xnpvOrdered($x2, $values, $dates, false, true);
if (!is_numeric($f1) || !is_numeric($f2)) {
break;
}
$f1 = (float) $f1;
$f2 = (float) $f2;
if (abs($f1) < self::FINANCIAL_PRECISION && abs($f2) < self::FINANCIAL_PRECISION) {
break;
}
if ($f1 * $f2 > 0) {
break;
}
$rslt = ($x1 + $x2) / 2;
$f3 = self::xnpvOrdered($rslt, $values, $dates, false, true);
if (!is_float($f3)) {
break;
}
if ($f3 * $f1 < 0) {
$x2 = $rslt;
} else {
$x1 = $rslt;
}
if (abs($f3) < self::FINANCIAL_PRECISION) {
break;
}
}
return $rslt;
}
/**
* @param mixed $rate
* @param mixed $values
@@ -197,7 +260,7 @@ class NonPeriodic
*
* @return float|string
*/
private static function xnpvOrdered($rate, $values, $dates, bool $ordered = true)
private static function xnpvOrdered($rate, $values, $dates, bool $ordered = true, bool $capAtNegative1 = false)
{
$rate = Functions::flattenSingleValue($rate);
$values = Functions::flattenArray($values);
@@ -206,6 +269,9 @@ class NonPeriodic
try {
self::validateXnpv($rate, $values, $dates);
if ($capAtNegative1 && $rate <= -1) {
$rate = -1.0 + 1.0E-10;
}
$date0 = DateTimeExcel\Helpers::getDateValue($dates[0]);
} catch (Exception $e) {
return $e->getMessage();
+15 -2
View File
@@ -122,9 +122,12 @@ class Xlsx extends BaseReader
return is_array($value) ? $value : [];
}
private function loadZip(string $filename, string $ns = ''): SimpleXMLElement
private function loadZip(string $filename, string $ns = '', bool $replaceUnclosedBr = false): SimpleXMLElement
{
$contents = $this->getFromZipArchive($this->zip, $filename);
if ($replaceUnclosedBr) {
$contents = str_replace('<br>', '<br/>', $contents);
}
$rels = simplexml_load_string(
$this->securityScanner->scan($contents),
'SimpleXMLElement',
@@ -1029,6 +1032,7 @@ class Xlsx extends BaseReader
// later we will remove from it real vmlComments
$unparsedVmlDrawings = $vmlComments;
$vmlDrawingContents = [];
// Loop through VML comments
foreach ($vmlComments as $relName => $relPath) {
@@ -1037,7 +1041,7 @@ class Xlsx extends BaseReader
try {
// no namespace okay - processed with Xpath
$vmlCommentsFile = $this->loadZip($relPath, '');
$vmlCommentsFile = $this->loadZip($relPath, '', true);
$vmlCommentsFile->registerXPathNamespace('v', Namespaces::URN_VML);
} catch (Throwable $ex) {
//Ignore unparsable vmlDrawings. Later they will be moved from $unparsedVmlDrawings to $unparsedLoadedData
@@ -1047,6 +1051,7 @@ class Xlsx extends BaseReader
// Locate VML drawings image relations
$drowingImages = [];
$VMLDrawingsRelations = dirname($relPath) . '/_rels/' . basename($relPath) . '.rels';
$vmlDrawingContents[$relName] = $this->securityScanner->scan($this->getFromZipArchive($zip, $relPath));
if ($zip->locateName($VMLDrawingsRelations)) {
$relsVMLDrawing = $this->loadZip($VMLDrawingsRelations, Namespaces::RELATIONSHIPS);
foreach ($relsVMLDrawing->Relationship as $elex) {
@@ -1519,6 +1524,14 @@ class Xlsx extends BaseReader
}
}
}
if ($xmlSheet->legacyDrawing && !$this->readDataOnly) {
foreach ($xmlSheet->legacyDrawing as $drawing) {
$drawingRelId = (string) self::getArrayItem(self::getAttributes($drawing, $xmlNamespaceBase), 'id');
if (isset($vmlDrawingContents[$drawingRelId])) {
$unparsedLoadedData['sheets'][$docSheet->getCodeName()]['legacyDrawing'] = $vmlDrawingContents[$drawingRelId];
}
}
}
// unparsed drawing AlternateContent
$xmlAltDrawing = $this->loadZip((string) $fileDrawing, Namespaces::COMPATIBILITY);
+9 -4
View File
@@ -455,14 +455,17 @@ class Xlsx extends BaseWriter
}
// Add comment relationship parts
if (count($this->spreadSheet->getSheet($i)->getComments()) > 0) {
$legacy = $unparsedLoadedData['sheets'][$this->spreadSheet->getSheet($i)->getCodeName()]['legacyDrawing'] ?? null;
if (count($this->spreadSheet->getSheet($i)->getComments()) > 0 || $legacy !== null) {
// VML Comments relationships
$zipContent['xl/drawings/_rels/vmlDrawing' . ($i + 1) . '.vml.rels'] = $this->getWriterPartRels()->writeVMLDrawingRelationships($this->spreadSheet->getSheet($i));
// VML Comments
$zipContent['xl/drawings/vmlDrawing' . ($i + 1) . '.vml'] = $this->getWriterPartComments()->writeVMLComments($this->spreadSheet->getSheet($i));
$zipContent['xl/drawings/vmlDrawing' . ($i + 1) . '.vml'] = $legacy ?? $this->getWriterPartComments()->writeVMLComments($this->spreadSheet->getSheet($i));
}
// Comments
// Comments
if (count($this->spreadSheet->getSheet($i)->getComments()) > 0) {
$zipContent['xl/comments' . ($i + 1) . '.xml'] = $this->getWriterPartComments()->writeComments($this->spreadSheet->getSheet($i));
// Media
@@ -477,7 +480,9 @@ class Xlsx extends BaseWriter
// Add unparsed relationship parts
if (isset($unparsedLoadedData['sheets'][$sheetCodeName]['vmlDrawings'])) {
foreach ($unparsedLoadedData['sheets'][$sheetCodeName]['vmlDrawings'] as $vmlDrawing) {
$zipContent[$vmlDrawing['filePath']] = $vmlDrawing['content'];
if (!isset($zipContent[$vmlDrawing['filePath']])) {
$zipContent[$vmlDrawing['filePath']] = $vmlDrawing['content'];
}
}
}
+3 -1
View File
@@ -239,14 +239,16 @@ class Rels extends WriterPart
// Write comments relationship?
$i = 1;
if (count($worksheet->getComments()) > 0) {
if (count($worksheet->getComments()) > 0 || isset($unparsedLoadedData['sheets'][$worksheet->getCodeName()]['legacyDrawing'])) {
$this->writeRelationship(
$objWriter,
'_comments_vml' . $i,
Namespaces::VML,
'../drawings/vmlDrawing' . $worksheetId . '.vml'
);
}
if (count($worksheet->getComments()) > 0) {
$this->writeRelationship(
$objWriter,
'_comments' . $i,
+2 -1
View File
@@ -1333,7 +1333,8 @@ class Worksheet extends WriterPart
private function writeLegacyDrawing(XMLWriter $objWriter, PhpspreadsheetWorksheet $worksheet): void
{
// If sheet contains comments, add the relationships
if (count($worksheet->getComments()) > 0) {
$unparsedLoadedData = $worksheet->getParent()->getUnparsedLoadedData();
if (count($worksheet->getComments()) > 0 || isset($unparsedLoadedData['sheets'][$worksheet->getCodeName()]['legacyDrawing'])) {
$objWriter->startElement('legacyDrawing');
$objWriter->writeAttribute('r:id', 'rId_comments_vml1');
$objWriter->endElement();
@@ -3,6 +3,7 @@
namespace PhpOffice\PhpSpreadsheetTests\Calculation\Functions\Engineering;
use PhpOffice\PhpSpreadsheet\Calculation\Engineering;
use PhpOffice\PhpSpreadsheetTests\Custom\ComplexAssert;
use PHPUnit\Framework\TestCase;
// Sanity tests for functions which have been moved out of Engineering
@@ -28,4 +29,60 @@ class MovedFunctionsTest extends TestCase
self::assertEquals(357, /** @scrutinizer ignore-deprecated */ Engineering::OCTTODEC(545));
self::assertEquals('1AB', /** @scrutinizer ignore-deprecated */ Engineering::OCTTOHEX(653));
}
public function testOthers(): void
{
self::assertEqualsWithDelta(30596.33413506702, /** @scrutinizer ignore-deprecated */ Engineering::BESSELI(-12.5, 0), 1E-8);
self::assertEqualsWithDelta(0.146884054700421, /** @scrutinizer ignore-deprecated */ Engineering::BESSELJ(-12.5, 0), 1E-8);
self::assertEqualsWithDelta(2.20786908479938, /** @scrutinizer ignore-deprecated */ Engineering::BESSELK(0.125, 0), 1E-8);
self::assertEqualsWithDelta(-1.38968063456627, /** @scrutinizer ignore-deprecated */ Engineering::BESSELY(0.125, 0), 1E-8);
self::assertEqualsWithDelta(0.0, /** @scrutinizer ignore-deprecated */ Engineering::DELTA(-0.75, -1.5), 1E-8);
self::assertEqualsWithDelta(0.0112834155558496, /** @scrutinizer ignore-deprecated */ Engineering::ERF(0.01), 1E-8);
self::assertEqualsWithDelta(0.98871658444415, /** @scrutinizer ignore-deprecated */ Engineering::ERFC(0.01), 1E-8);
self::assertEqualsWithDelta(0.0112834155558496, /** @scrutinizer ignore-deprecated */ Engineering::ERFPRECISE(0.01), 1E-8);
self::assertEqualsWithDelta(1.0, /** @scrutinizer ignore-deprecated */ Engineering::GESTEP(-0.75, -1.5), 1E-8);
}
public function testConversions(): void
{
self::assertEqualsWithDelta(1.942559385723E-03, /** @scrutinizer ignore-deprecated */ Engineering::CONVERTUOM(1.0, 'ozm', 'sg'), 1E-8);
self::assertContains('Temperature', /** @scrutinizer ignore-deprecated */ Engineering::getConversionGroups());
self::assertArrayHasKey('Weight and Mass', /** @scrutinizer ignore-deprecated */ Engineering::getConversionGroupUnits());
self::assertEquals('Degrees Celsius', /** @scrutinizer ignore-deprecated */ Engineering::getConversionGroupUnitDetails('Temperature')['Temperature'][0]['description']);
self::assertEquals('yotta', /** @scrutinizer ignore-deprecated */ Engineering::getConversionMultipliers()['Y']['name']);
self::assertEquals(1024, /** @scrutinizer ignore-deprecated */ Engineering::getBinaryConversionMultipliers()['ki']['multiplier']);
}
public function testImaginary(): void
{
$complexAssert = new ComplexAssert();
$complexAssert->setDelta(1.0E-8);
self::assertSame('3+4i', /** @scrutinizer ignore-deprecated */ Engineering::COMPLEX(3, 4));
self::assertEqualsWithDelta(5.67, /** @scrutinizer ignore-deprecated */ Engineering::IMAGINARY('12.34+5.67j'), 1E-8);
self::assertEqualsWithDelta(12.34, /** @scrutinizer ignore-deprecated */ Engineering::IMREAL('12.34+5.67j'), 1E-8);
self::assertEqualsWithDelta(13.58029822942, /** @scrutinizer ignore-deprecated */ Engineering::IMABS('12.34+5.67j'), 1E-8);
self::assertEqualsWithDelta(0.43071059555, /** @scrutinizer ignore-deprecated */ Engineering::IMARGUMENT('12.34+5.67j'), 1E-8);
$complexAssert->runAssertComplexEquals('12.34-5.67j', /** @scrutinizer ignore-deprecated */ Engineering::IMCONJUGATE('12.34+5.67j'));
$complexAssert->runAssertComplexEquals('141.319179436356+32.547610312508j', /** @scrutinizer ignore-deprecated */ Engineering::IMCOS('12.34+5.67j'));
$complexAssert->runAssertComplexEquals('93502.0563713182121-65794.6618967782119j', /** @scrutinizer ignore-deprecated */ Engineering::IMCOSH('12.34+5.67j'));
$complexAssert->runAssertComplexEquals('93502.0563713182121-65794.6618967782119j', /** @scrutinizer ignore-deprecated */ Engineering::IMCOSH('12.34+5.67j'));
$complexAssert->runAssertComplexEquals('-0.0000104004141424230319-1.00002138037057154j', /** @scrutinizer ignore-deprecated */ Engineering::IMCOT('12.34+5.67j'));
$complexAssert->runAssertComplexEquals('-0.00154774455592154432-0.00671986631601416928j', /** @scrutinizer ignore-deprecated */ Engineering::IMCSC('12.34+5.67j'));
$complexAssert->runAssertComplexEquals('7.15308425027293823E-6+5.03341614148979354E-6j', /** @scrutinizer ignore-deprecated */ Engineering::IMCSCH('12.34+5.67j'));
$complexAssert->runAssertComplexEquals('0.0961415519586104-0.00694248653276682j', /** @scrutinizer ignore-deprecated */ Engineering::IMDIV('12.34+5.67j', '123.45+67.89j'));
$complexAssert->runAssertComplexEquals('187004.11273906-131589.323796073j', /** @scrutinizer ignore-deprecated */ Engineering::IMEXP('12.34+5.67j'));
$complexAssert->runAssertComplexEquals('2.60862008281875+0.430710595550204j', /** @scrutinizer ignore-deprecated */ Engineering::IMLN('12.34+5.67j'));
$complexAssert->runAssertComplexEquals('3.76344325733562+0.621384040306436j', /** @scrutinizer ignore-deprecated */ Engineering::IMLOG2('12.34+5.67j'));
$complexAssert->runAssertComplexEquals('1.13290930735019+0.187055234944717j', /** @scrutinizer ignore-deprecated */ Engineering::IMLOG10('12.34+5.67j'));
$complexAssert->runAssertComplexEquals('120.1267+139.9356j', /** @scrutinizer ignore-deprecated */ Engineering::IMPOWER('12.34+5.67j', 2));
$complexAssert->runAssertComplexEquals('6454.936089+8718.895647i', /** @scrutinizer ignore-deprecated */ Engineering::IMPRODUCT('12.34+5.67i', '123.45+67.89i', '5.67'));
$complexAssert->runAssertComplexEquals('0.00671973874162309199-0.00154764157870523791j', /** @scrutinizer ignore-deprecated */ Engineering::IMSEC('12.34+5.67j'));
$complexAssert->runAssertComplexEquals('7.15308425036177674E-6+5.03341614116724074E-6j', /** @scrutinizer ignore-deprecated */ Engineering::IMSECH('12.34+5.67j'));
$complexAssert->runAssertComplexEquals('-32.5483841590412+141.315819535092j', /** @scrutinizer ignore-deprecated */ Engineering::IMSIN('12.34+5.67j'));
$complexAssert->runAssertComplexEquals('93502.0563677416700-65794.6618992949199j', /** @scrutinizer ignore-deprecated */ Engineering::IMSINH('12.34+5.67j'));
$complexAssert->runAssertComplexEquals('3.60002071031685+0.787495469644252j', /** @scrutinizer ignore-deprecated */ Engineering::IMSQRT('12.34+5.67j'));
$complexAssert->runAssertComplexEquals('-111.11-62.22j', /** @scrutinizer ignore-deprecated */ Engineering::IMSUB('12.34+5.67j', '123.45+67.89j'));
$complexAssert->runAssertComplexEquals('135.79+73.56j', /** @scrutinizer ignore-deprecated */ Engineering::IMSUM('12.34+5.67j', '123.45+67.89j'));
$complexAssert->runAssertComplexEquals('-0.0000103999694261435177+0.999978619978377253j', /** @scrutinizer ignore-deprecated */ Engineering::IMTAN('12.34+5.67j'));
}
}
@@ -3,7 +3,6 @@
namespace PhpOffice\PhpSpreadsheetTests\Calculation\Functions\LookupRef;
use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
use PhpOffice\PhpSpreadsheet\Calculation\LookupRef;
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
use PhpOffice\PhpSpreadsheet\NamedRange;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
@@ -69,29 +68,6 @@ class HLookupTest extends TestCase
return require 'tests/data/Calculation/LookupRef/HLOOKUP.php';
}
public function testGrandfathered(): void
{
// Second parameter is supposed to be array of arrays.
// Some old tests called function directly using array of strings;
// ensure these work as before.
$expectedResult = '#REF!';
$result = /** @scrutinizer ignore-deprecated */ LookupRef::HLOOKUP(
'Selection column',
['Selection column', 'Value to retrieve'],
5,
false
);
self::assertSame($expectedResult, $result);
$expectedResult = 'Value to retrieve';
$result = /** @scrutinizer ignore-deprecated */ LookupRef::HLOOKUP(
'Selection column',
['Selection column', 'Value to retrieve'],
2,
false
);
self::assertSame($expectedResult, $result);
}
/**
* @dataProvider providerHLookupNamedRange
*/
@@ -19,11 +19,84 @@ class MovedFunctionsTest extends TestCase
{
public function testMovedFunctions(): void
{
self::assertSame('$G$3', /** @scrutinizer ignore-deprecated */ LookupRef::cellAddress(3, 7));
self::assertSame(3, /** @scrutinizer ignore-deprecated */ LookupRef::COLUMN('C5'));
self::assertSame(2, /** @scrutinizer ignore-deprecated */ LookupRef::COLUMNS([[1, 2], [3, 5], [7, 9]]));
self::assertSame(30, /** @scrutinizer ignore-deprecated */ LookupRef::CHOOSE(3, 10, 20, 30, 40));
self::assertSame(ExcelError::REF(), /** @scrutinizer ignore-deprecated */ LookupRef::FORMULATEXT('A1'));
self::assertSame(ExcelError::REF(), /** @scrutinizer ignore-deprecated */ LookupRef::HYPERLINK('https://phpspreadsheet.readthedocs.io/en/latest/', 'Read the Docs'));
self::assertSame(60, /** @scrutinizer ignore-deprecated */ LookupRef::INDEX([[10, 20, 30], [40, 50, 60], [70, 80, 90]], 2, 3));
self::assertSame(3, /** @scrutinizer ignore-deprecated */ LookupRef::MATCH(3, [1, 10, 3, 8], 0));
self::assertSame('#VALUE!', /** @scrutinizer ignore-deprecated */ LookupRef::OFFSET(null));
self::assertSame(5, /** @scrutinizer ignore-deprecated */ LookupRef::ROW('C5'));
self::assertSame(3, /** @scrutinizer ignore-deprecated */ LookupRef::ROWS([[1, 2], [3, 5], [7, 9]]));
self::assertSame([[1, 2], [3, 4]], /** @scrutinizer ignore-deprecated */ LookupRef::TRANSPOSE([[1, 3], [2, 4]]));
}
public function testLookup(): void
{
$densityGrid = [
['Density', 'Viscosity', 'Temperature'],
[0.457, 3.55, 500],
[0.525, 3.25, 400],
[0.616, 2.93, 300],
[0.675, 2.75, 250],
[0.746, 2.57, 200],
[0.835, 2.38, 150],
[0.946, 2.17, 100],
[1.090, 1.95, 50],
[1.290, 1.71, 0],
];
$expectedResult = 100;
$result = /** @scrutinizer ignore-deprecated */ LookupRef::VLOOKUP(1, $densityGrid, 3, true);
self::assertSame($expectedResult, $result);
$orderGrid = [
['Order ID', 10247, 10249, 10250, 10251, 10252, 10253],
['Unit Price', 14.00, 18.60, 7.70, 16.80, 16.80, 64.80],
['Quantity', 12, 9, 10, 6, 20, 40],
];
$expectedResult = 16.80;
$result = /** @scrutinizer ignore-deprecated */ LookupRef::HLOOKUP(10251, $orderGrid, 2, false);
self::assertSame($expectedResult, $result);
$array1 = [
[4.14],
[4.19],
[5.17],
[5.77],
[6, 39],
];
$array2 = [
['red'],
['orange'],
['yellow'],
['green'],
['blue'],
];
$expectedResult = 'orange';
$result = /** @scrutinizer ignore-deprecated */ LookupRef::LOOKUP(4.19, $array1, $array2);
self::assertSame($expectedResult, $result);
}
public function testGrandfatheredHlookup(): void
{
// Second parameter is supposed to be array of arrays.
// Some old tests called function directly using array of strings;
// ensure these work as before.
$expectedResult = '#REF!';
$result = /** @scrutinizer ignore-deprecated */ LookupRef::HLOOKUP(
'Selection column',
['Selection column', 'Value to retrieve'],
5,
false
);
self::assertSame($expectedResult, $result);
$expectedResult = 'Value to retrieve';
$result = /** @scrutinizer ignore-deprecated */ LookupRef::HLOOKUP(
'Selection column',
['Selection column', 'Value to retrieve'],
2,
false
);
self::assertSame($expectedResult, $result);
}
}
@@ -40,5 +40,6 @@ class DeprecatedTest extends TestCase
self::assertSame('ayxw', /** @scrutinizer ignore-deprecated */ TextData::SUBSTITUTE('xyxw', 'x', 'a', 1));
self::assertSame('1', /** @scrutinizer ignore-deprecated */ TextData::CHARACTER('49'));
self::assertSame('0', /** @scrutinizer ignore-deprecated */ TextData::CHARACTER('48'));
self::assertSame('xyz', /** @scrutinizer ignore-deprecated */ TextData::RETURNSTRING('xyz'));
}
}
@@ -46,7 +46,9 @@ class TextSplitTest extends AllSetupTeardown
$worksheet = $this->getSheet();
$worksheet->getCell('A1')->setValue($text);
$this->setDelimiterValues($worksheet, 'B', $columnDelimiter);
$this->setDelimiterValues($worksheet, 'C', $rowDelimiter);
if (!empty($rowDelimiter)) {
$this->setDelimiterValues($worksheet, 'C', $rowDelimiter);
}
$worksheet->getCell('H1')->setValue("=TEXTSPLIT({$args})");
$result = Calculation::getInstance($this->getSpreadsheet())->calculateCellValue($worksheet->getCell('H1'));
@@ -2,6 +2,9 @@
namespace PhpOffice\PhpSpreadsheetTests\Calculation\Functions\TextData;
use PhpOffice\PhpSpreadsheet\Calculation\TextData\Format;
use PhpOffice\PhpSpreadsheet\RichText\RichText;
class ValueToTextTest extends AllSetupTeardown
{
/**
@@ -25,4 +28,15 @@ class ValueToTextTest extends AllSetupTeardown
{
return require 'tests/data/Calculation/TextData/VALUETOTEXT.php';
}
// In Spreadsheet context, never see cell value as RichText.
// It will use calculatedValue, which is a string.
// Add an additional test for that condition.
public function testRichText(): void
{
$richText1 = new RichText();
$richText1->createTextRun('Hello');
$richText1->createText(' World');
self::assertSame('Hello World', Format::valueToText($richText1, 0));
}
}
@@ -3,14 +3,18 @@
namespace PhpOffice\PhpSpreadsheetTests\Custom;
use Complex\Complex;
use PHPUnit\Framework\TestCase;
class ComplexAssert
class ComplexAssert extends TestCase
{
/**
* @var string
*/
private $errorMessage = '';
/** @var float */
private $delta = 0.0;
/**
* @param mixed $expected
* @param mixed $actual
@@ -40,16 +44,26 @@ class ComplexAssert
return $adjustedDelta > 1.0 ? 1.0 : $adjustedDelta;
}
public function setDelta(float $delta): self
{
$this->delta = $delta;
return $this;
}
/**
* @param mixed $expected
* @param mixed $actual
*/
public function assertComplexEquals($expected, $actual, float $delta = 0): bool
public function assertComplexEquals($expected, $actual, ?float $delta = null): bool
{
if ($expected === INF || (is_string($expected) && $expected[0] === '#')) {
return $this->testExpectedExceptions($expected, $actual);
}
if ($delta === null) {
$delta = $this->delta;
}
$expectedComplex = new Complex($expected);
$actualComplex = new Complex($actual);
@@ -80,4 +94,13 @@ class ComplexAssert
{
return $this->errorMessage;
}
/**
* @param mixed $expected
* @param mixed $actual
*/
public function runAssertComplexEquals($expected, $actual, ?float $delta = null): void
{
self::assertTrue($this->assertComplexEquals($expected, $actual, $delta), $this->getErrorMessage());
}
}
+16 -6
View File
@@ -69,6 +69,16 @@ return [
['2015-04-01', '2019-06-27'],
0.1,
],
'issue 689' => [
-0.6118824173,
[-1000000.706, 947003.58],
['2018-09-05', '2018-09-26'],
],
'issue 689 updated 2022-12-25' => [
-0.935842486,
[-20972.36, 20350.545],
['2022-12-12', '2022-12-16'],
],
'XIRR calculation #1 is incorrect' => [
0.137963527441025,
[139947.43, 1893.67, 52573.25, 48849.74, 26369.16, -273029.18],
@@ -186,13 +196,13 @@ return [
[-10000, 3027.25, 630.68, 2018.2, 1513.62, 1765.89, 4036.33, 4036.33, 1513.62, 1513.62, 2018.16, 1513.62, 1009.08, 1513.62, 1513.62, 1765.89, 1765.89, 22421.55],
['2000-05-24', '2000-06-05', '2001-04-09', '2004-02-24', '2005-03-18', '2006-02-15', '2007-01-10', '2007-11-14', '2008-12-17', '2010-01-15', '2011-01-14', '2012-02-03', '2013-01-18', '2014-01-24', '2015-01-30', '2016-01-22', '2017-01-20', '2017-06-05'],
],
'DeCampo issue5a, agree with Excel not DeCampo' => [
'#NUM!', //-0.7640294,
'DeCampo issue5a, agree with DeCampo not Excel' => [
-0.7640294, // '#NUM!'
[-2610, -2589, -5110, -2550, -5086, -2561, -5040, -2552, -2530, 29520],
['2001-06-22', '2001-07-03', '2001-07-05', '2001-07-06', '2001-07-09', '2001-07-10', '2001-07-12', '2001-07-13', '2001-07-16', '2001-07-17'],
],
'DeCampo issue5b, agree with Excel not DeCampo' => [
'#NUM!', //-0.8353404,
'DeCampo issue5b, agree with DeCampo not Excel' => [
-0.8353404, // '#NUM!'
[-2610, -2589, -5110, -2550, -5086, -2561, -5040, -2552, -2530, -9840, 38900],
['2001-06-22', '2001-07-03', '2001-07-05', '2001-07-06', '2001-07-09', '2001-07-10', '2001-07-12', '2001-07-13', '2001-07-16', '2001-07-17', '2001-07-18'],
],
@@ -206,8 +216,8 @@ return [
[-2236.3994659663, -47.3417585212, -46.52619316339632, 10424.74612565936, -13.077972551952],
['2017-12-16', '2017-12-26', '2017-12-29', '2017-12-31', '2017-12-20'],
],
'Python XIRR test line 39, agree with Excel not Python' => [
'#NUM!', //-1,
'Python XIRR test line 39, agree with Python not Excel' => [
-1, // '#NUM!',
[18902, 83600, -5780, -4080, -56780, -2210, -2380, 33975, 23067.98, -1619.57],
['2016-04-06', '2016-05-04', '2016-05-12', '2017-05-08', '2017-07-03', '2018-05-07', '2019-05-06', '2019-10-01', '2020-03-13', '2020-05-07'],
],