mirror of
https://github.com/PHPOffice/PhpSpreadsheet.git
synced 2026-08-30 12:07:56 +00:00
fix: don't infer cell t attribute from formula source when pre-calc is off
writeCellFormula() falls back to using $cellValue (the formula source) as $calculatedValue when getPreCalculateFormulas() is false. The source is always a string, so the is_string($result) branch always fires, every formula cell ends up with t="str" — even those whose formula evaluates to a number or boolean. Some readers (notably older LibreOffice and Gnumeric) treat t="str" as a hint that the cell is a string, and either skip recomputation or present a #NAME error for numeric formulas they would otherwise compute correctly. Fix: leave $calculatedValue null and $calculatedValueString empty when pre-calc is off. The type-inference branches no longer fire, so no t attribute is written. The surrounding writeElementIf already guards <v> emission on getPreCalculateFormulas(), so no cached value is written either — readers see "formula with no cached value, no type hint", respect the workbook's fullCalcOnLoad="1", and recompute on open. Updates the existing PreCalcTest assertion to match: <c r="B2"><f>3+A3</f></c> instead of <c r="B2" t="str"><f>3+A3</f></c>.
This commit is contained in:
@@ -1576,7 +1576,13 @@ class Worksheet extends WriterPart
|
||||
{
|
||||
$attributes = $cell->getFormulaAttributes() ?? [];
|
||||
$coordinate = $cell->getCoordinate();
|
||||
$calculatedValue = $this->getParentWriter()->getPreCalculateFormulas() ? $cell->getCalculatedValue() : $cellValue;
|
||||
$preCalc = $this->getParentWriter()->getPreCalculateFormulas();
|
||||
// When pre-calc is off we have no calculated value to infer the cell type from. The
|
||||
// previous fall-back of $cellValue (the formula source) made every formula cell write
|
||||
// t="str" because the source is always a string — misleading for formulas that resolve
|
||||
// to numbers/booleans. Leave $calculatedValue/$calculatedValueString null so the
|
||||
// type-inference branches below are skipped and no t attribute is written.
|
||||
$calculatedValue = $preCalc ? $cell->getCalculatedValue() : null;
|
||||
if ($calculatedValue === ExcelError::SPILL()) {
|
||||
$objWriter->writeAttribute('t', 'e');
|
||||
//$objWriter->writeAttribute('cm', '1'); // already added
|
||||
@@ -1592,7 +1598,10 @@ class Worksheet extends WriterPart
|
||||
|
||||
return;
|
||||
}
|
||||
$calculatedValueString = $this->getParentWriter()->getPreCalculateFormulas() ? $cell->getCalculatedValueString() : $cellValue;
|
||||
// Empty string (not null) so str_starts_with($calculatedValueString, '#') below stays
|
||||
// type-correct when pre-calc is off; the surrounding writeElementIf condition guards
|
||||
// against actually emitting <v> when there is no calculated value.
|
||||
$calculatedValueString = $preCalc ? $cell->getCalculatedValueString() : '';
|
||||
$result = $calculatedValue;
|
||||
while (is_array($result)) {
|
||||
$result = array_shift($result);
|
||||
|
||||
@@ -108,7 +108,10 @@ class PreCalcTest extends AbstractFunctional
|
||||
$data = self::readFile($file);
|
||||
// confirm that file contains B2 pre-calculated or not as appropriate
|
||||
if ($preCalc === false) {
|
||||
self::assertStringContainsString('<c r="B2" t="str"><f>3+A3</f></c>', $data);
|
||||
// No t="str" when pre-calc is off: with no calculated value to inspect, the
|
||||
// writer can no longer infer the result type from the formula source string
|
||||
// (which is always a string). Readers compute on open per fullCalcOnLoad.
|
||||
self::assertStringContainsString('<c r="B2"><f>3+A3</f></c>', $data);
|
||||
} else {
|
||||
self::assertStringContainsString('<c r="B2"><f>3+A3</f><v>14</v></c>', $data);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user