@@ -1576,7 +1576,13 @@ private function writeCellFormula(XMLWriter $objWriter, string $cellValue, Cell
15761576 {
15771577 $ attributes = $ cell ->getFormulaAttributes () ?? [];
15781578 $ coordinate = $ cell ->getCoordinate ();
1579- $ calculatedValue = $ this ->getParentWriter ()->getPreCalculateFormulas () ? $ cell ->getCalculatedValue () : $ cellValue ;
1579+ $ preCalc = $ this ->getParentWriter ()->getPreCalculateFormulas ();
1580+ // When pre-calc is off we have no calculated value to infer the cell type from. The
1581+ // previous fall-back of $cellValue (the formula source) made every formula cell write
1582+ // t="str" because the source is always a string — misleading for formulas that resolve
1583+ // to numbers/booleans. Leave $calculatedValue/$calculatedValueString null so the
1584+ // type-inference branches below are skipped and no t attribute is written.
1585+ $ calculatedValue = $ preCalc ? $ cell ->getCalculatedValue () : null ;
15801586 if ($ calculatedValue === ExcelError::SPILL ()) {
15811587 $ objWriter ->writeAttribute ('t ' , 'e ' );
15821588 //$objWriter->writeAttribute('cm', '1'); // already added
@@ -1592,7 +1598,10 @@ private function writeCellFormula(XMLWriter $objWriter, string $cellValue, Cell
15921598
15931599 return ;
15941600 }
1595- $ calculatedValueString = $ this ->getParentWriter ()->getPreCalculateFormulas () ? $ cell ->getCalculatedValueString () : $ cellValue ;
1601+ // Empty string (not null) so str_starts_with($calculatedValueString, '#') below stays
1602+ // type-correct when pre-calc is off; the surrounding writeElementIf condition guards
1603+ // against actually emitting <v> when there is no calculated value.
1604+ $ calculatedValueString = $ preCalc ? $ cell ->getCalculatedValueString () : '' ;
15961605 $ result = $ calculatedValue ;
15971606 while (is_array ($ result )) {
15981607 $ result = array_shift ($ result );
0 commit comments