• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

PHPOffice / PhpSpreadsheet / 23551431468

25 Mar 2026 04:13PM UTC coverage: 96.961% (+0.06%) from 96.904%
23551431468

Pull #4833

github

web-flow
Merge 972cbe815 into eb391d1f2
Pull Request #4833: Optimize XLS (BIFF8) reader performance with reduced per-cell overhead

74 of 77 new or added lines in 3 files covered. (96.1%)

111 existing lines in 4 files now uncovered.

47796 of 49294 relevant lines covered (96.96%)

384.48 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

97.04
/src/PhpSpreadsheet/Style/ConditionalFormatting/ConditionalColorScale.php
1
<?php
2

3
namespace PhpOffice\PhpSpreadsheet\Style\ConditionalFormatting;
4

5
use PhpOffice\PhpSpreadsheet\Calculation\Statistical\Percentiles;
6
use PhpOffice\PhpSpreadsheet\Style\Color;
7

8
class ConditionalColorScale
9
{
10
    private ?ConditionalFormatValueObject $minimumConditionalFormatValueObject = null;
11

12
    private ?ConditionalFormatValueObject $midpointConditionalFormatValueObject = null;
13

14
    private ?ConditionalFormatValueObject $maximumConditionalFormatValueObject = null;
15

16
    private ?Color $minimumColor = null;
17

18
    private ?Color $midpointColor = null;
19

20
    private ?Color $maximumColor = null;
21

22
    private ?string $sqref = null;
23

24
    /** @var mixed[] */
25
    private array $valueArray = [];
26

27
    private float $minValue = 0;
28

29
    private float $maxValue = 0;
30

31
    private float $midValue = 0;
32

33
    private ?\PhpOffice\PhpSpreadsheet\Worksheet\Worksheet $worksheet = null;
34

35
    public function getMinimumConditionalFormatValueObject(): ?ConditionalFormatValueObject
3✔
36
    {
37
        return $this->minimumConditionalFormatValueObject;
3✔
38
    }
39

40
    public function setMinimumConditionalFormatValueObject(ConditionalFormatValueObject $minimumConditionalFormatValueObject): self
10✔
41
    {
42
        $this->minimumConditionalFormatValueObject = $minimumConditionalFormatValueObject;
10✔
43

44
        return $this;
10✔
45
    }
46

47
    public function getMidpointConditionalFormatValueObject(): ?ConditionalFormatValueObject
3✔
48
    {
49
        return $this->midpointConditionalFormatValueObject;
3✔
50
    }
51

52
    public function setMidpointConditionalFormatValueObject(ConditionalFormatValueObject $midpointConditionalFormatValueObject): self
8✔
53
    {
54
        $this->midpointConditionalFormatValueObject = $midpointConditionalFormatValueObject;
8✔
55

56
        return $this;
8✔
57
    }
58

59
    public function getMaximumConditionalFormatValueObject(): ?ConditionalFormatValueObject
3✔
60
    {
61
        return $this->maximumConditionalFormatValueObject;
3✔
62
    }
63

64
    public function setMaximumConditionalFormatValueObject(ConditionalFormatValueObject $maximumConditionalFormatValueObject): self
10✔
65
    {
66
        $this->maximumConditionalFormatValueObject = $maximumConditionalFormatValueObject;
10✔
67

68
        return $this;
10✔
69
    }
70

71
    public function getMinimumColor(): ?Color
3✔
72
    {
73
        return $this->minimumColor;
3✔
74
    }
75

76
    public function setMinimumColor(Color $minimumColor): self
10✔
77
    {
78
        $this->minimumColor = $minimumColor;
10✔
79

80
        return $this;
10✔
81
    }
82

83
    public function getMidpointColor(): ?Color
3✔
84
    {
85
        return $this->midpointColor;
3✔
86
    }
87

88
    public function setMidpointColor(Color $midpointColor): self
8✔
89
    {
90
        $this->midpointColor = $midpointColor;
8✔
91

92
        return $this;
8✔
93
    }
94

95
    public function getMaximumColor(): ?Color
3✔
96
    {
97
        return $this->maximumColor;
3✔
98
    }
99

100
    public function setMaximumColor(Color $maximumColor): self
10✔
101
    {
102
        $this->maximumColor = $maximumColor;
10✔
103

104
        return $this;
10✔
105
    }
106

107
    public function getSqRef(): ?string
1✔
108
    {
109
        return $this->sqref;
1✔
110
    }
111

112
    public function setSqRef(string $sqref, \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet $worksheet): self
9✔
113
    {
114
        $this->sqref = $sqref;
9✔
115
        $this->worksheet = $worksheet;
9✔
116

117
        return $this;
9✔
118
    }
119

120
    public function setScaleArray(): self
6✔
121
    {
122
        if ($this->sqref !== null && $this->worksheet !== null) {
6✔
123
            $values = $this->worksheet->rangesToArray($this->sqref, null, true, false, true);
6✔
124
            $this->valueArray = [];
6✔
125
            foreach ($values as $key => $value) {
6✔
126
                /** @var array<float|int|string> $value */
127
                foreach ($value as $k => $v) {
6✔
128
                    $this->valueArray[] = (float) $v;
6✔
129
                }
130
            }
131
            $this->prepareColorScale();
6✔
132
        }
133

134
        return $this;
6✔
135
    }
136

137
    public function getColorForValue(float $value): string
6✔
138
    {
139
        if ($this->minimumColor === null || $this->midpointColor === null || $this->maximumColor === null) {
6✔
140
            return 'FF000000';
1✔
141
        }
142
        $minColor = $this->minimumColor->getARGB();
6✔
143
        $midColor = $this->midpointColor->getARGB();
6✔
144
        $maxColor = $this->maximumColor->getARGB();
6✔
145

146
        if ($minColor === null || $midColor === null || $maxColor === null) {
6✔
147
            return 'FF000000';
×
148
        }
149

150
        if ($value <= $this->minValue) {
6✔
151
            return $minColor;
6✔
152
        }
153
        if ($value >= $this->maxValue) {
6✔
154
            return $maxColor;
6✔
155
        }
156
        if ($value == $this->midValue) {
6✔
157
            return $midColor;
4✔
158
        }
159
        if ($value < $this->midValue) {
6✔
160
            $blend = ($value - $this->minValue) / ($this->midValue - $this->minValue);
6✔
161
            $alpha1 = hexdec(substr($minColor, 0, 2));
6✔
162
            $alpha2 = hexdec(substr($midColor, 0, 2));
6✔
163
            $red1 = hexdec(substr($minColor, 2, 2));
6✔
164
            $red2 = hexdec(substr($midColor, 2, 2));
6✔
165
            $green1 = hexdec(substr($minColor, 4, 2));
6✔
166
            $green2 = hexdec(substr($midColor, 4, 2));
6✔
167
            $blue1 = hexdec(substr($minColor, 6, 2));
6✔
168
            $blue2 = hexdec(substr($midColor, 6, 2));
6✔
169
        } else {
170
            $blend = ($value - $this->midValue) / ($this->maxValue - $this->midValue);
6✔
171
            $alpha1 = hexdec(substr($midColor, 0, 2));
6✔
172
            $alpha2 = hexdec(substr($maxColor, 0, 2));
6✔
173
            $red1 = hexdec(substr($midColor, 2, 2));
6✔
174
            $red2 = hexdec(substr($maxColor, 2, 2));
6✔
175
            $green1 = hexdec(substr($midColor, 4, 2));
6✔
176
            $green2 = hexdec(substr($maxColor, 4, 2));
6✔
177
            $blue1 = hexdec(substr($midColor, 6, 2));
6✔
178
            $blue2 = hexdec(substr($maxColor, 6, 2));
6✔
179
        }
180
        $alpha = (int) ($alpha2 * $blend + $alpha1 * (1 - $blend));
6✔
181
        $red = (int) ($red2 * $blend + $red1 * (1 - $blend));
6✔
182
        $green = (int) ($green2 * $blend + $green1 * (1 - $blend));
6✔
183
        $blue = (int) ($blue2 * $blend + $blue1 * (1 - $blend));
6✔
184

185
        return sprintf('%02X%02X%02X%02X', $alpha, $red, $green, $blue);
6✔
186
    }
187

188
    private function getLimitValue(string $type, float $value = 0, float $formula = 0): float
6✔
189
    {
190
        if (count($this->valueArray) === 0) {
6✔
UNCOV
191
            return 0;
×
192
        }
193
        switch ($type) {
194
            case 'min':
6✔
195
                /** @var float|int */
196
                $temp = min($this->valueArray);
6✔
197

198
                return (float) $temp;
6✔
199
            case 'max':
6✔
200
                /** @var float|int */
201
                $temp = max($this->valueArray);
6✔
202

203
                return (float) $temp;
6✔
204
            case 'percentile':
5✔
205
                return (float) Percentiles::PERCENTILE($this->valueArray, (float) ($value / 100));
5✔
206
            case 'formula':
2✔
207
                return $formula;
2✔
208
            case 'percent':
2✔
209
                /** @var float|int */
210
                $min = min($this->valueArray);
2✔
211
                $min = (float) $min;
2✔
212
                /** @var float|int */
213
                $max = max($this->valueArray);
2✔
214
                $max = (float) $max;
2✔
215

216
                return $min + (float) ($value / 100) * ($max - $min);
2✔
217
            default:
UNCOV
218
                return 0;
×
219
        }
220
    }
221

222
    /**
223
     * Prepares color scale for execution, see the first if for variables that must be set beforehand.
224
     */
225
    public function prepareColorScale(): self
6✔
226
    {
227
        if ($this->minimumConditionalFormatValueObject !== null && $this->maximumConditionalFormatValueObject !== null && $this->minimumColor !== null && $this->maximumColor !== null) {
6✔
228
            if ($this->midpointConditionalFormatValueObject !== null && $this->midpointConditionalFormatValueObject->getType() !== 'None') {
6✔
229
                $this->minValue = $this->getLimitValue($this->minimumConditionalFormatValueObject->getType(), (float) $this->minimumConditionalFormatValueObject->getValue(), (float) $this->minimumConditionalFormatValueObject->getCellFormula());
5✔
230
                $this->midValue = $this->getLimitValue($this->midpointConditionalFormatValueObject->getType(), (float) $this->midpointConditionalFormatValueObject->getValue(), (float) $this->midpointConditionalFormatValueObject->getCellFormula());
5✔
231
                $this->maxValue = $this->getLimitValue($this->maximumConditionalFormatValueObject->getType(), (float) $this->maximumConditionalFormatValueObject->getValue(), (float) $this->maximumConditionalFormatValueObject->getCellFormula());
5✔
232
            } else {
233
                $this->minValue = $this->getLimitValue($this->minimumConditionalFormatValueObject->getType(), (float) $this->minimumConditionalFormatValueObject->getValue(), (float) $this->minimumConditionalFormatValueObject->getCellFormula());
1✔
234
                $this->maxValue = $this->getLimitValue($this->maximumConditionalFormatValueObject->getType(), (float) $this->maximumConditionalFormatValueObject->getValue(), (float) $this->maximumConditionalFormatValueObject->getCellFormula());
1✔
235
                $this->midValue = (float) ($this->minValue + $this->maxValue) / 2;
1✔
236
                $blend = 0.5;
1✔
237

238
                $minColor = $this->minimumColor->getARGB();
1✔
239
                $maxColor = $this->maximumColor->getARGB();
1✔
240

241
                if ($minColor !== null && $maxColor !== null) {
1✔
242
                    $alpha1 = hexdec(substr($minColor, 0, 2));
1✔
243
                    $alpha2 = hexdec(substr($maxColor, 0, 2));
1✔
244
                    $red1 = hexdec(substr($minColor, 2, 2));
1✔
245
                    $red2 = hexdec(substr($maxColor, 2, 2));
1✔
246
                    $green1 = hexdec(substr($minColor, 4, 2));
1✔
247
                    $green2 = hexdec(substr($maxColor, 4, 2));
1✔
248
                    $blue1 = hexdec(substr($minColor, 6, 2));
1✔
249
                    $blue2 = hexdec(substr($maxColor, 6, 2));
1✔
250
                    $alpha = (int) ($alpha2 * $blend + $alpha1 * (1 - $blend));
1✔
251
                    $red = (int) ($red2 * $blend + $red1 * (1 - $blend));
1✔
252
                    $green = (int) ($green2 * $blend + $green1 * (1 - $blend));
1✔
253
                    $blue = (int) ($blue2 * $blend + $blue1 * (1 - $blend));
1✔
254

255
                    $this->midpointColor = new Color(sprintf('%02X%02X%02X%02X', $alpha, $red, $green, $blue));
1✔
256
                } else {
UNCOV
257
                    $this->midpointColor = null;
×
258
                }
259
            }
260
        }
261

262
        return $this;
6✔
263
    }
264

265
    /**
266
     * Checks that all needed color scale data is in place.
267
     */
268
    public function colorScaleReadyForUse(): bool
6✔
269
    {
270
        if ($this->minimumColor === null || $this->midpointColor === null || $this->maximumColor === null) {
6✔
271
            return false;
1✔
272
        }
273

274
        return true;
6✔
275
    }
276
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc