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

PHPOffice / PhpSpreadsheet / 18014029598

25 Sep 2025 04:20PM UTC coverage: 95.867% (+0.3%) from 95.602%
18014029598

push

github

web-flow
Merge pull request #4663 from oleibman/tweakcoveralls

Tweak Coveralls

45116 of 47061 relevant lines covered (95.87%)

373.63 hits per line

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

81.25
/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
7✔
41
    {
42
        $this->minimumConditionalFormatValueObject = $minimumConditionalFormatValueObject;
7✔
43

44
        return $this;
7✔
45
    }
46

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

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

56
        return $this;
6✔
57
    }
58

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

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

68
        return $this;
7✔
69
    }
70

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

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

80
        return $this;
7✔
81
    }
82

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

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

92
        return $this;
6✔
93
    }
94

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

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

104
        return $this;
7✔
105
    }
106

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

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

117
        return $this;
6✔
118
    }
119

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

134
        return $this;
3✔
135
    }
136

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

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

150
        if ($value <= $this->minValue) {
3✔
151
            return $minColor;
3✔
152
        }
153
        if ($value >= $this->maxValue) {
3✔
154
            return $maxColor;
3✔
155
        }
156
        if ($value == $this->midValue) {
3✔
157
            return $midColor;
2✔
158
        }
159
        if ($value < $this->midValue) {
3✔
160
            $blend = ($value - $this->minValue) / ($this->midValue - $this->minValue);
3✔
161
            $alpha1 = hexdec(substr($minColor, 0, 2));
3✔
162
            $alpha2 = hexdec(substr($midColor, 0, 2));
3✔
163
            $red1 = hexdec(substr($minColor, 2, 2));
3✔
164
            $red2 = hexdec(substr($midColor, 2, 2));
3✔
165
            $green1 = hexdec(substr($minColor, 4, 2));
3✔
166
            $green2 = hexdec(substr($midColor, 4, 2));
3✔
167
            $blue1 = hexdec(substr($minColor, 6, 2));
3✔
168
            $blue2 = hexdec(substr($midColor, 6, 2));
3✔
169

170
            return strtoupper(dechex((int) ($alpha2 * $blend + $alpha1 * (1 - $blend))) . '' . dechex((int) ($red2 * $blend + $red1 * (1 - $blend))) . '' . dechex((int) ($green2 * $blend + $green1 * (1 - $blend))) . '' . dechex((int) ($blue2 * $blend + $blue1 * (1 - $blend))));
3✔
171
        }
172
        $blend = ($value - $this->midValue) / ($this->maxValue - $this->midValue);
3✔
173
        $alpha1 = hexdec(substr($midColor, 0, 2));
3✔
174
        $alpha2 = hexdec(substr($maxColor, 0, 2));
3✔
175
        $red1 = hexdec(substr($midColor, 2, 2));
3✔
176
        $red2 = hexdec(substr($maxColor, 2, 2));
3✔
177
        $green1 = hexdec(substr($midColor, 4, 2));
3✔
178
        $green2 = hexdec(substr($maxColor, 4, 2));
3✔
179
        $blue1 = hexdec(substr($midColor, 6, 2));
3✔
180
        $blue2 = hexdec(substr($maxColor, 6, 2));
3✔
181

182
        return strtoupper(dechex((int) ($alpha2 * $blend + $alpha1 * (1 - $blend))) . '' . dechex((int) ($red2 * $blend + $red1 * (1 - $blend))) . '' . dechex((int) ($green2 * $blend + $green1 * (1 - $blend))) . '' . dechex((int) ($blue2 * $blend + $blue1 * (1 - $blend))));
3✔
183
    }
184

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

195
                return (float) $temp;
3✔
196
            case 'max':
3✔
197
                /** @var float|int */
198
                $temp = max($this->valueArray);
3✔
199

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

213
                return $min + (float) ($value / 100) * ($max - $min);
2✔
214
            default:
215
                return 0;
×
216
        }
217
    }
218

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

235
                $minColor = $this->minimumColor->getARGB();
×
236
                $maxColor = $this->maximumColor->getARGB();
×
237

238
                if ($minColor !== null && $maxColor !== null) {
×
239
                    $alpha1 = hexdec(substr($minColor, 0, 2));
×
240
                    $alpha2 = hexdec(substr($maxColor, 0, 2));
×
241
                    $red1 = hexdec(substr($minColor, 2, 2));
×
242
                    $red2 = hexdec(substr($maxColor, 2, 2));
×
243
                    $green1 = hexdec(substr($minColor, 4, 2));
×
244
                    $green2 = hexdec(substr($maxColor, 4, 2));
×
245
                    $blue1 = hexdec(substr($minColor, 6, 2));
×
246
                    $blue2 = hexdec(substr($maxColor, 6, 2));
×
247
                    $this->midpointColor = new Color(strtoupper(dechex((int) ($alpha2 * $blend + $alpha1 * (1 - $blend))) . '' . dechex((int) ($red2 * $blend + $red1 * (1 - $blend))) . '' . dechex((int) ($green2 * $blend + $green1 * (1 - $blend))) . '' . dechex((int) ($blue2 * $blend + $blue1 * (1 - $blend)))));
×
248
                } else {
249
                    $this->midpointColor = null;
×
250
                }
251
            }
252
        }
253

254
        return $this;
3✔
255
    }
256

257
    /**
258
     * Checks that all needed color scale data is in place.
259
     */
260
    public function colorScaleReadyForUse(): bool
3✔
261
    {
262
        if ($this->minimumColor === null || $this->midpointColor === null || $this->maximumColor === null) {
3✔
263
            return false;
×
264
        }
265

266
        return true;
3✔
267
    }
268
}
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

© 2025 Coveralls, Inc