Failed Conditions
Pull Request — master (#4118)
by Owen
24:41 queued 14:40
created

ConditionalFormatting::setCFRules()   A

Complexity

Conditions 4
Paths 5

Size

Total Lines 20
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 15
CRAP Score 4

Importance

Changes 0
Metric Value
eloc 14
c 0
b 0
f 0
dl 0
loc 20
ccs 15
cts 15
cp 1
rs 9.7998
cc 4
nc 5
nop 8
crap 4

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
3
namespace PhpOffice\PhpSpreadsheet\Reader\Xls;
4
5
use PhpOffice\PhpSpreadsheet\Exception as PhpSpreadsheetException;
6
use PhpOffice\PhpSpreadsheet\Reader\Xls;
7
use PhpOffice\PhpSpreadsheet\Reader\Xls\Style\FillPattern;
8
use PhpOffice\PhpSpreadsheet\Style\Conditional;
9
use PhpOffice\PhpSpreadsheet\Style\Fill;
10
use PhpOffice\PhpSpreadsheet\Style\Style;
11
12
class ConditionalFormatting extends Xls
13
{
14
    /**
15
     * @var array<int, string>
16
     */
17
    private static array $types = [
18
        0x01 => Conditional::CONDITION_CELLIS,
19
        0x02 => Conditional::CONDITION_EXPRESSION,
20
    ];
21
22
    /**
23
     * @var array<int, string>
24
     */
25
    private static array $operators = [
26
        0x00 => Conditional::OPERATOR_NONE,
27
        0x01 => Conditional::OPERATOR_BETWEEN,
28
        0x02 => Conditional::OPERATOR_NOTBETWEEN,
29
        0x03 => Conditional::OPERATOR_EQUAL,
30
        0x04 => Conditional::OPERATOR_NOTEQUAL,
31
        0x05 => Conditional::OPERATOR_GREATERTHAN,
32
        0x06 => Conditional::OPERATOR_LESSTHAN,
33
        0x07 => Conditional::OPERATOR_GREATERTHANOREQUAL,
34
        0x08 => Conditional::OPERATOR_LESSTHANOREQUAL,
35
    ];
36
37 21
    public static function type(int $type): ?string
38
    {
39 21
        if (isset(self::$types[$type])) {
40 21
            return self::$types[$type];
41
        }
42
43
        return null;
44
    }
45
46 21
    public static function operator(int $operator): ?string
47
    {
48 21
        if (isset(self::$operators[$operator])) {
49 21
            return self::$operators[$operator];
50
        }
51
52
        return null;
53
    }
54
55
    /**
56
     * Parse conditional formatting blocks.
57
     *
58
     * @see https://www.openoffice.org/sc/excelfileformat.pdf Search for CFHEADER followed by CFRULE
59
     */
60 23
    protected function readCFHeader2(Xls $xls): array
61
    {
62 23
        $length = self::getUInt2d($xls->data, $xls->pos + 2);
63 23
        $recordData = $xls->readRecordData($xls->data, $xls->pos + 4, $length);
64
65
        // move stream pointer forward to next record
66 23
        $xls->pos += 4 + $length;
67
68 23
        if ($xls->readDataOnly) {
69 2
            return [];
70
        }
71
72
        // offset: 0; size: 2; Rule Count
73
//        $ruleCount = self::getUInt2d($recordData, 0);
74
75
        // offset: var; size: var; cell range address list with
76 21
        $cellRangeAddressList = ($xls->version == self::XLS_BIFF8)
77 21
            ? Biff8::readBIFF8CellRangeAddressList(substr($recordData, 12))
78
            : Biff5::readBIFF5CellRangeAddressList(substr($recordData, 12));
79 21
        $cellRangeAddresses = $cellRangeAddressList['cellRangeAddresses'];
80
81 21
        return $cellRangeAddresses;
82
    }
83
84 23
    protected function readCFRule2(array $cellRangeAddresses, Xls $xls): void
85
    {
86 23
        $length = self::getUInt2d($xls->data, $xls->pos + 2);
87 23
        $recordData = $xls->readRecordData($xls->data, $xls->pos + 4, $length);
88
89
        // move stream pointer forward to next record
90 23
        $xls->pos += 4 + $length;
91
92 23
        if ($xls->readDataOnly) {
93 2
            return;
94
        }
95
96
        // offset: 0; size: 2; Options
97 21
        $cfRule = self::getUInt2d($recordData, 0);
98
99
        // bit: 8-15; mask: 0x00FF; type
100 21
        $type = (0x00FF & $cfRule) >> 0;
101 21
        $type = self::type($type);
102
103
        // bit: 0-7; mask: 0xFF00; type
104 21
        $operator = (0xFF00 & $cfRule) >> 8;
105 21
        $operator = self::operator($operator);
106
107 21
        if ($type === null || $operator === null) {
108
            return;
109
        }
110
111
        // offset: 2; size: 2; Size1
112 21
        $size1 = self::getUInt2d($recordData, 2);
113
114
        // offset: 4; size: 2; Size2
115 21
        $size2 = self::getUInt2d($recordData, 4);
116
117
        // offset: 6; size: 4; Options
118 21
        $options = self::getInt4d($recordData, 6);
119
120 21
        $style = new Style(false, true); // non-supervisor, conditional
121 21
        $noFormatSet = true;
122
        //$xls->getCFStyleOptions($options, $style);
123
124 21
        $hasFontRecord = (bool) ((0x04000000 & $options) >> 26);
125 21
        $hasAlignmentRecord = (bool) ((0x08000000 & $options) >> 27);
126 21
        $hasBorderRecord = (bool) ((0x10000000 & $options) >> 28);
127 21
        $hasFillRecord = (bool) ((0x20000000 & $options) >> 29);
128 21
        $hasProtectionRecord = (bool) ((0x40000000 & $options) >> 30);
129
        // note unexpected values for following 4
130 21
        $hasBorderLeft = !(bool) (0x00000400 & $options);
131 21
        $hasBorderRight = !(bool) (0x00000800 & $options);
132 21
        $hasBorderTop = !(bool) (0x00001000 & $options);
133 21
        $hasBorderBottom = !(bool) (0x00002000 & $options);
134
135 21
        $offset = 12;
136
137 21
        if ($hasFontRecord === true) {
138 20
            $fontStyle = substr($recordData, $offset, 118);
139 20
            $this->getCFFontStyle($fontStyle, $style, $xls);
140 20
            $offset += 118;
141 20
            $noFormatSet = false;
142
        }
143
144 21
        if ($hasAlignmentRecord === true) {
145
            //$alignmentStyle = substr($recordData, $offset, 8);
146
            //$this->getCFAlignmentStyle($alignmentStyle, $style, $xls);
147
            $offset += 8;
148
        }
149
150 21
        if ($hasBorderRecord === true) {
151 1
            $borderStyle = substr($recordData, $offset, 8);
152 1
            $this->getCFBorderStyle($borderStyle, $style, $hasBorderLeft, $hasBorderRight, $hasBorderTop, $hasBorderBottom, $xls);
153 1
            $offset += 8;
154 1
            $noFormatSet = false;
155
        }
156
157 21
        if ($hasFillRecord === true) {
158 4
            $fillStyle = substr($recordData, $offset, 4);
159 4
            $this->getCFFillStyle($fillStyle, $style, $xls);
160 4
            $offset += 4;
161 4
            $noFormatSet = false;
162
        }
163
164 21
        if ($hasProtectionRecord === true) {
165
            //$protectionStyle = substr($recordData, $offset, 4);
166
            //$this->getCFProtectionStyle($protectionStyle, $style, $xls);
167
            $offset += 2;
168
        }
169
170 21
        $formula1 = $formula2 = null;
171 21
        if ($size1 > 0) {
172 21
            $formula1 = $this->readCFFormula($recordData, $offset, $size1, $xls);
173 21
            if ($formula1 === null) {
174
                return;
175
            }
176
177 21
            $offset += $size1;
178
        }
179
180 21
        if ($size2 > 0) {
181 8
            $formula2 = $this->readCFFormula($recordData, $offset, $size2, $xls);
182 8
            if ($formula2 === null) {
183
                return;
184
            }
185
186 8
            $offset += $size2;
187
        }
188
189 21
        $this->setCFRules($cellRangeAddresses, $type, $operator, $formula1, $formula2, $style, $noFormatSet, $xls);
190
    }
191
192
    /*private function getCFStyleOptions(int $options, Style $style, Xls $xls): void
193
    {
194
    }*/
195
196 20
    private function getCFFontStyle(string $options, Style $style, Xls $xls): void
197
    {
198 20
        $fontSize = self::getInt4d($options, 64);
199 20
        if ($fontSize !== -1) {
200 12
            $style->getFont()->setSize($fontSize / 20); // Convert twips to points
201
        }
202 20
        $options68 = self::getInt4d($options, 68);
203 20
        $options88 = self::getInt4d($options, 88);
204
205 20
        if (($options88 & 2) === 0) {
206 7
            $bold = self::getUInt2d($options, 72); // 400 = normal, 700 = bold
207 7
            if ($bold !== 0) {
208 7
                $style->getFont()->setBold($bold >= 550);
209
            }
210 7
            if (($options68 & 2) !== 0) {
211 2
                $style->getFont()->setItalic(true);
212
            }
213
        }
214 20
        if (($options88 & 0x80) === 0) {
215 1
            if (($options68 & 0x80) !== 0) {
216 1
                $style->getFont()->setStrikethrough(true);
217
            }
218
        }
219
220 20
        $color = self::getInt4d($options, 80);
221
222 20
        if ($color !== -1) {
223 19
            $style->getFont()->getColor()->setRGB(Color::map($color, $xls->palette, $xls->version)['rgb']);
224
        }
225
    }
226
227
    /*private function getCFAlignmentStyle(string $options, Style $style, Xls $xls): void
228
    {
229
    }*/
230
231 1
    private function getCFBorderStyle(string $options, Style $style, bool $hasBorderLeft, bool $hasBorderRight, bool $hasBorderTop, bool $hasBorderBottom, Xls $xls): void
232
    {
233 1
        $valueArray = unpack('V', $options);
234 1
        $value = is_array($valueArray) ? $valueArray[1] : 0;
235 1
        $left = $value & 15;
236 1
        $right = ($value >> 4) & 15;
237 1
        $top = ($value >> 8) & 15;
238 1
        $bottom = ($value >> 12) & 15;
239 1
        $leftc = ($value >> 16) & 0x7F;
240 1
        $rightc = ($value >> 23) & 0x7F;
241 1
        $valueArray = unpack('V', substr($options, 4));
242 1
        $value = is_array($valueArray) ? $valueArray[1] : 0;
243 1
        $topc = $value & 0x7F;
244 1
        $bottomc = ($value & 0x3F80) >> 7;
245 1
        if ($hasBorderLeft) {
246 1
            $style->getBorders()->getLeft()
247 1
                ->setBorderStyle(self::BORDER_STYLE_MAP[$left]);
248 1
            $style->getBorders()->getLeft()->getColor()
249 1
                ->setRGB(Color::map($leftc, $xls->palette, $xls->version)['rgb']);
250
        }
251 1
        if ($hasBorderRight) {
252 1
            $style->getBorders()->getRight()
253 1
                ->setBorderStyle(self::BORDER_STYLE_MAP[$right]);
254 1
            $style->getBorders()->getRight()->getColor()
255 1
                ->setRGB(Color::map($rightc, $xls->palette, $xls->version)['rgb']);
256
        }
257 1
        if ($hasBorderTop) {
258 1
            $style->getBorders()->getTop()
259 1
                ->setBorderStyle(self::BORDER_STYLE_MAP[$top]);
260 1
            $style->getBorders()->getTop()->getColor()
261 1
                ->setRGB(Color::map($topc, $xls->palette, $xls->version)['rgb']);
262
        }
263 1
        if ($hasBorderBottom) {
264 1
            $style->getBorders()->getBottom()
265 1
                ->setBorderStyle(self::BORDER_STYLE_MAP[$bottom]);
266 1
            $style->getBorders()->getBottom()->getColor()
267 1
                ->setRGB(Color::map($bottomc, $xls->palette, $xls->version)['rgb']);
268
        }
269
    }
270
271 4
    private function getCFFillStyle(string $options, Style $style, Xls $xls): void
272
    {
273 4
        $fillPattern = self::getUInt2d($options, 0);
274
        // bit: 10-15; mask: 0xFC00; type
275 4
        $fillPattern = (0xFC00 & $fillPattern) >> 10;
276 4
        $fillPattern = FillPattern::lookup($fillPattern);
277 4
        $fillPattern = $fillPattern === Fill::FILL_NONE ? Fill::FILL_SOLID : $fillPattern;
278
279 4
        if ($fillPattern !== Fill::FILL_NONE) {
280 4
            $style->getFill()->setFillType($fillPattern);
281
282 4
            $fillColors = self::getUInt2d($options, 2);
283
284
            // bit: 0-6; mask: 0x007F; type
285 4
            $color1 = (0x007F & $fillColors) >> 0;
286
287
            // bit: 7-13; mask: 0x3F80; type
288 4
            $color2 = (0x3F80 & $fillColors) >> 7;
289 4
            if ($fillPattern === Fill::FILL_SOLID) {
290 4
                $style->getFill()->getStartColor()->setRGB(Color::map($color2, $xls->palette, $xls->version)['rgb']);
291
            } else {
292 2
                $style->getFill()->getStartColor()->setRGB(Color::map($color1, $xls->palette, $xls->version)['rgb']);
293 2
                $style->getFill()->getEndColor()->setRGB(Color::map($color2, $xls->palette, $xls->version)['rgb']);
294
            }
295
        }
296
    }
297
298
    /*private function getCFProtectionStyle(string $options, Style $style, Xls $xls): void
299
    {
300
    }*/
301
302 21
    private function readCFFormula(string $recordData, int $offset, int $size, Xls $xls): float|int|string|null
303
    {
304
        try {
305 21
            $formula = substr($recordData, $offset, $size);
306 21
            $formula = pack('v', $size) . $formula; // prepend the length
307
308 21
            $formula = $xls->getFormulaFromStructure($formula);
309 21
            if (is_numeric($formula)) {
310 18
                return (str_contains($formula, '.')) ? (float) $formula : (int) $formula;
311
            }
312
313 9
            return $formula;
314
        } catch (PhpSpreadsheetException) {
315
            return null;
316
        }
317
    }
318
319 21
    private function setCFRules(array $cellRanges, string $type, string $operator, null|float|int|string $formula1, null|float|int|string $formula2, Style $style, bool $noFormatSet, Xls $xls): void
320
    {
321 21
        foreach ($cellRanges as $cellRange) {
322 21
            $conditional = new Conditional();
323 21
            $conditional->setNoFormatSet($noFormatSet);
324 21
            $conditional->setConditionType($type);
325 21
            $conditional->setOperatorType($operator);
326 21
            $conditional->setStopIfTrue(true);
327 21
            if ($formula1 !== null) {
328 21
                $conditional->addCondition($formula1);
329
            }
330 21
            if ($formula2 !== null) {
331 8
                $conditional->addCondition($formula2);
332
            }
333 21
            $conditional->setStyle($style);
334
335 21
            $conditionalStyles = $xls->phpSheet->getStyle($cellRange)->getConditionalStyles();
336 21
            $conditionalStyles[] = $conditional;
337
338 21
            $xls->phpSheet->getStyle($cellRange)->setConditionalStyles($conditionalStyles);
339
        }
340
    }
341
}
342