Passed
Push — master ( f9b1b7...3922d9 )
by
unknown
18:56 queued 11:14
created

ConditionalFormatting::setCFRules()   A

Complexity

Conditions 4
Paths 5

Size

Total Lines 24
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 19
CRAP Score 4

Importance

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