Skip to content

Commit 9ca9d74

Browse files
author
MarkBaker
committed
Resolve saving cell references, string literals and formula as values for conditional formatting rules in the Xls file save
The code is ugly as sin; but it works... I'll do some refactoring and cleaning later (once I've had some sleep, because I'm stupidly still working on this at 3am) The main remaining issue is formulae that can't be parsed in BIFF8 files; e.g. formulae that use functions that aren't available. In this case, all CF in that worksheet is corrupted, and the file errors when opened, so it is a serious issue. The ISODD()/ISEVEN example in 07_Expression_Comparisons.php uses these, so I've temporarily commented out setting that CF range until I've solved that problem. There are other limitations listed in the BIFF documentation; but they're harder to detect. I've also left a couple of debug statements in the code to display these formula errors: I'll remove them once I've resolved the issue.
1 parent 7e89d33 commit 9ca9d74

File tree

3 files changed

+59
-19
lines changed

3 files changed

+59
-19
lines changed

samples/ConditionalFormatting/07_Expression_Comparisons.php

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
$spreadsheet->setActiveSheetIndex(0);
2929
$spreadsheet->getActiveSheet()
3030
->setCellValue('A1', 'Odd/Even Expression Comparison')
31+
->setCellValue('A4', 'Note that these functions are not available for Xls files')
3132
->setCellValue('A15', 'Sales Grid Expression Comparison')
3233
->setCellValue('A25', 'Sales Grid Multiple Expression Comparison');
3334

@@ -101,9 +102,9 @@
101102
->setStyle($yellowStyle);
102103
$conditionalStyles[] = $expressionWizard->getConditional();
103104

104-
$spreadsheet->getActiveSheet()
105-
->getStyle($expressionWizard->getCellRange())
106-
->setConditionalStyles($conditionalStyles);
105+
//$spreadsheet->getActiveSheet()
106+
// ->getStyle($expressionWizard->getCellRange())
107+
// ->setConditionalStyles($conditionalStyles);
107108

108109
// Set rules for Sales Grid Row match against Country Comparison
109110
$cellRange = 'A17:D22';

src/PhpSpreadsheet/Style/ConditionalFormatting/Wizard/WizardAbstract.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ protected static function reverseCellAdjustment(array $matches, int $referenceCo
122122
return "{$worksheet}{$column}{$row}";
123123
}
124124

125-
protected static function reverseAdjustCellRef(string $condition, string $cellRange): string
125+
public static function reverseAdjustCellRef(string $condition, string $cellRange): string
126126
{
127127
$conditionalRange = Coordinate::splitRange(str_replace('$', '', strtoupper($cellRange)));
128128
[$referenceCell] = $conditionalRange[0];

src/PhpSpreadsheet/Writer/Xls/Worksheet.php

Lines changed: 54 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
use PhpOffice\PhpSpreadsheet\Style\Border;
1414
use PhpOffice\PhpSpreadsheet\Style\Color;
1515
use PhpOffice\PhpSpreadsheet\Style\Conditional;
16+
use PhpOffice\PhpSpreadsheet\Style\ConditionalFormatting\Wizard;
1617
use PhpOffice\PhpSpreadsheet\Style\Protection;
1718
use PhpOffice\PhpSpreadsheet\Worksheet\PageSetup;
1819
use PhpOffice\PhpSpreadsheet\Worksheet\SheetView;
@@ -569,7 +570,7 @@ private function writeConditionalFormatting(): void
569570
$arrConditional[$conditional->getHashCode()] = true;
570571

571572
// Write CFRULE record
572-
$this->writeCFRule($conditional);
573+
$this->writeCFRule($conditional, $cellCoordinate);
573574
}
574575
}
575576
}
@@ -2779,7 +2780,7 @@ private function writePageLayoutView(): void
27792780
/**
27802781
* Write CFRule Record.
27812782
*/
2782-
private function writeCFRule(Conditional $conditional): void
2783+
private function writeCFRule(Conditional $conditional, string $cellRange): void
27832784
{
27842785
$record = 0x01B1; // Record identifier
27852786
$type = null; // Type of the CF
@@ -2832,21 +2833,59 @@ private function writeCFRule(Conditional $conditional): void
28322833
// $szValue2 : size of the formula data for second value or formula
28332834
$arrConditions = $conditional->getConditions();
28342835
$numConditions = count($arrConditions);
2836+
2837+
$szValue1 = 0x0000;
2838+
$szValue2 = 0x0000;
2839+
$operand1 = null;
2840+
$operand2 = null;
2841+
28352842
if ($numConditions == 1) {
2836-
$szValue1 = ($arrConditions[0] <= 65535 ? 3 : 0x0000);
2837-
$szValue2 = 0x0000;
2838-
$operand1 = pack('Cv', 0x1E, $arrConditions[0]);
2839-
$operand2 = null;
2843+
if (is_int($arrConditions[0]) || is_float($arrConditions[0])) {
2844+
$szValue1 = ($arrConditions[0] <= 65535 ? 3 : 0x0000);
2845+
$operand1 = pack('Cv', 0x1E, $arrConditions[0]);
2846+
} else {
2847+
try {
2848+
$formula1 = Wizard\WizardAbstract::reverseAdjustCellRef((string) $arrConditions[0], $cellRange);
2849+
$this->parser->parse($formula1);
2850+
$formula1 = $this->parser->toReversePolish();
2851+
$szValue1 = strlen($formula1);
2852+
} catch (PhpSpreadsheetException $e) {
2853+
var_dump("PARSER EXCEPTION: {$e->getMessage()}");
2854+
$formula1 = null;
2855+
}
2856+
$operand1 = $formula1;
2857+
}
28402858
} elseif ($numConditions == 2 && ($conditional->getOperatorType() == Conditional::OPERATOR_BETWEEN)) {
2841-
$szValue1 = ($arrConditions[0] <= 65535 ? 3 : 0x0000);
2842-
$szValue2 = ($arrConditions[1] <= 65535 ? 3 : 0x0000);
2843-
$operand1 = pack('Cv', 0x1E, $arrConditions[0]);
2844-
$operand2 = pack('Cv', 0x1E, $arrConditions[1]);
2845-
} else {
2846-
$szValue1 = 0x0000;
2847-
$szValue2 = 0x0000;
2848-
$operand1 = null;
2849-
$operand2 = null;
2859+
if (is_int($arrConditions[0]) || is_float($arrConditions[0])) {
2860+
$szValue1 = ($arrConditions[0] <= 65535 ? 3 : 0x0000);
2861+
$operand1 = pack('Cv', 0x1E, $arrConditions[0]);
2862+
} else {
2863+
try {
2864+
$formula1 = Wizard\WizardAbstract::reverseAdjustCellRef((string) $arrConditions[0], $cellRange);
2865+
$this->parser->parse($formula1);
2866+
$formula1 = $this->parser->toReversePolish();
2867+
$szValue1 = strlen($formula1);
2868+
} catch (PhpSpreadsheetException $e) {
2869+
var_dump("PARSER EXCEPTION: {$e->getMessage()}");
2870+
$formula1 = null;
2871+
}
2872+
$operand1 = $formula1;
2873+
}
2874+
if (is_int($arrConditions[1]) || is_float($arrConditions[1])) {
2875+
$szValue2 = ($arrConditions[1] <= 65535 ? 3 : 0x0000);
2876+
$operand2 = pack('Cv', 0x1E, $arrConditions[1]);
2877+
} else {
2878+
try {
2879+
$formula2 = Wizard\WizardAbstract::reverseAdjustCellRef((string) $arrConditions[1], $cellRange);
2880+
$this->parser->parse($formula2);
2881+
$formula2 = $this->parser->toReversePolish();
2882+
$szValue2 = strlen($formula2);
2883+
} catch (PhpSpreadsheetException $e) {
2884+
var_dump("PARSER EXCEPTION: {$e->getMessage()}");
2885+
$formula2 = null;
2886+
}
2887+
$operand2 = $formula2;
2888+
}
28502889
}
28512890

28522891
// $flags : Option flags

0 commit comments

Comments
 (0)