Passed
Pull Request — master (#4240)
by Owen
27:46 queued 17:34
created

DataValidationHelper   A

Complexity

Total Complexity 16

Size/Duplication

Total Lines 205
Duplicated Lines 0 %

Test Coverage

Coverage 93.68%

Importance

Changes 0
Metric Value
wmc 16
eloc 112
dl 0
loc 205
ccs 89
cts 95
cp 0.9368
rs 10
c 0
b 0
f 0

4 Methods

Rating   Name   Duplication   Size   Complexity  
A operator() 0 3 1
A type() 0 3 1
A errorStyle() 0 3 1
C readDataValidation2() 0 148 13
1
<?php
2
3
namespace PhpOffice\PhpSpreadsheet\Reader\Xls;
4
5
use PhpOffice\PhpSpreadsheet\Cell\AddressRange;
6
use PhpOffice\PhpSpreadsheet\Cell\DataValidation;
7
use PhpOffice\PhpSpreadsheet\Exception as PhpSpreadsheetException;
8
use PhpOffice\PhpSpreadsheet\Reader\Xls;
9
use PhpOffice\PhpSpreadsheet\Writer\Xls\Worksheet as XlsWorksheet;
10
11
class DataValidationHelper extends Xls
12
{
13
    /**
14
     * @var array<int, string>
15
     */
16
    private static array $types = [
17
        0x00 => DataValidation::TYPE_NONE,
18
        0x01 => DataValidation::TYPE_WHOLE,
19
        0x02 => DataValidation::TYPE_DECIMAL,
20
        0x03 => DataValidation::TYPE_LIST,
21
        0x04 => DataValidation::TYPE_DATE,
22
        0x05 => DataValidation::TYPE_TIME,
23
        0x06 => DataValidation::TYPE_TEXTLENGTH,
24
        0x07 => DataValidation::TYPE_CUSTOM,
25
    ];
26
27
    /**
28
     * @var array<int, string>
29
     */
30
    private static array $errorStyles = [
31
        0x00 => DataValidation::STYLE_STOP,
32
        0x01 => DataValidation::STYLE_WARNING,
33
        0x02 => DataValidation::STYLE_INFORMATION,
34
    ];
35
36
    /**
37
     * @var array<int, string>
38
     */
39
    private static array $operators = [
40
        0x00 => DataValidation::OPERATOR_BETWEEN,
41
        0x01 => DataValidation::OPERATOR_NOTBETWEEN,
42
        0x02 => DataValidation::OPERATOR_EQUAL,
43
        0x03 => DataValidation::OPERATOR_NOTEQUAL,
44
        0x04 => DataValidation::OPERATOR_GREATERTHAN,
45
        0x05 => DataValidation::OPERATOR_LESSTHAN,
46
        0x06 => DataValidation::OPERATOR_GREATERTHANOREQUAL,
47
        0x07 => DataValidation::OPERATOR_LESSTHANOREQUAL,
48
    ];
49
50 4
    public static function type(int $type): ?string
51
    {
52 4
        return self::$types[$type] ?? null;
53
    }
54
55 4
    public static function errorStyle(int $errorStyle): ?string
56
    {
57 4
        return self::$errorStyles[$errorStyle] ?? null;
58
    }
59
60 4
    public static function operator(int $operator): ?string
61
    {
62 4
        return self::$operators[$operator] ?? null;
63
    }
64
65
    /**
66
     * Read DATAVALIDATION record.
67
     */
68 4
    protected function readDataValidation2(Xls $xls): void
69
    {
70 4
        $length = self::getUInt2d($xls->data, $xls->pos + 2);
71 4
        $recordData = $xls->readRecordData($xls->data, $xls->pos + 4, $length);
72
73
        // move stream pointer forward to next record
74 4
        $xls->pos += 4 + $length;
75
76 4
        if ($xls->readDataOnly) {
77
            return;
78
        }
79
80
        // offset: 0; size: 4; Options
81 4
        $options = self::getInt4d($recordData, 0);
82
83
        // bit: 0-3; mask: 0x0000000F; type
84 4
        $type = (0x0000000F & $options) >> 0;
85 4
        $type = self::type($type);
86
87
        // bit: 4-6; mask: 0x00000070; error type
88 4
        $errorStyle = (0x00000070 & $options) >> 4;
89 4
        $errorStyle = self::errorStyle($errorStyle);
90
91
        // bit: 7; mask: 0x00000080; 1= formula is explicit (only applies to list)
92
        // I have only seen cases where this is 1
93
        //$explicitFormula = (0x00000080 & $options) >> 7;
94
95
        // bit: 8; mask: 0x00000100; 1= empty cells allowed
96 4
        $allowBlank = (0x00000100 & $options) >> 8;
97
98
        // bit: 9; mask: 0x00000200; 1= suppress drop down arrow in list type validity
99 4
        $suppressDropDown = (0x00000200 & $options) >> 9;
100
101
        // bit: 18; mask: 0x00040000; 1= show prompt box if cell selected
102 4
        $showInputMessage = (0x00040000 & $options) >> 18;
103
104
        // bit: 19; mask: 0x00080000; 1= show error box if invalid values entered
105 4
        $showErrorMessage = (0x00080000 & $options) >> 19;
106
107
        // bit: 20-23; mask: 0x00F00000; condition operator
108 4
        $operator = (0x00F00000 & $options) >> 20;
109 4
        $operator = self::operator($operator);
110
111 4
        if ($type === null || $errorStyle === null || $operator === null) {
112
            return;
113
        }
114
115
        // offset: 4; size: var; title of the prompt box
116 4
        $offset = 4;
117 4
        $string = self::readUnicodeStringLong(substr($recordData, $offset));
118 4
        $promptTitle = $string['value'] !== chr(0) ? $string['value'] : '';
119 4
        $offset += $string['size'];
120
121
        // offset: var; size: var; title of the error box
122 4
        $string = self::readUnicodeStringLong(substr($recordData, $offset));
123 4
        $errorTitle = $string['value'] !== chr(0) ? $string['value'] : '';
124 4
        $offset += $string['size'];
125
126
        // offset: var; size: var; text of the prompt box
127 4
        $string = self::readUnicodeStringLong(substr($recordData, $offset));
128 4
        $prompt = $string['value'] !== chr(0) ? $string['value'] : '';
129 4
        $offset += $string['size'];
130
131
        // offset: var; size: var; text of the error box
132 4
        $string = self::readUnicodeStringLong(substr($recordData, $offset));
133 4
        $error = $string['value'] !== chr(0) ? $string['value'] : '';
134 4
        $offset += $string['size'];
135
136
        // offset: var; size: 2; size of the formula data for the first condition
137 4
        $sz1 = self::getUInt2d($recordData, $offset);
138 4
        $offset += 2;
139
140
        // offset: var; size: 2; not used
141 4
        $offset += 2;
142
143
        // offset: var; size: $sz1; formula data for first condition (without size field)
144 4
        $formula1 = substr($recordData, $offset, $sz1);
145 4
        $formula1 = pack('v', $sz1) . $formula1; // prepend the length
146
147
        try {
148 4
            $formula1 = $xls->getFormulaFromStructure($formula1);
149
150
            // in list type validity, null characters are used as item separators
151 4
            if ($type == DataValidation::TYPE_LIST) {
152 4
                $formula1 = str_replace(chr(0), ',', $formula1);
153
            }
154
        } catch (PhpSpreadsheetException $e) {
155
            return;
156
        }
157 4
        $offset += $sz1;
158
159
        // offset: var; size: 2; size of the formula data for the first condition
160 4
        $sz2 = self::getUInt2d($recordData, $offset);
161 4
        $offset += 2;
162
163
        // offset: var; size: 2; not used
164 4
        $offset += 2;
165
166
        // offset: var; size: $sz2; formula data for second condition (without size field)
167 4
        $formula2 = substr($recordData, $offset, $sz2);
168 4
        $formula2 = pack('v', $sz2) . $formula2; // prepend the length
169
170
        try {
171 4
            $formula2 = $xls->getFormulaFromStructure($formula2);
172
        } catch (PhpSpreadsheetException) {
173
            return;
174
        }
175 4
        $offset += $sz2;
176
177
        // offset: var; size: var; cell range address list with
178 4
        $cellRangeAddressList = Biff8::readBIFF8CellRangeAddressList(substr($recordData, $offset));
179 4
        $cellRangeAddresses = $cellRangeAddressList['cellRangeAddresses'];
180 4
        $maxRow = (string) AddressRange::MAX_ROW;
181 4
        $maxCol = AddressRange::MAX_COLUMN;
182 4
        $maxXlsRow = (string) XlsWorksheet::MAX_XLS_ROW;
183 4
        $maxXlsColumnString = (string) XlsWorksheet::MAX_XLS_COLUMN_STRING;
184
185 4
        foreach ($cellRangeAddresses as $cellRange) {
186 4
            $cellRange = preg_replace(
187 4
                [
188 4
                    "/([a-z]+)1:([a-z]+)$maxXlsRow/i",
189 4
                    "/([a-z]+\\d+):([a-z]+)$maxXlsRow/i",
190 4
                    "/A(\\d+):$maxXlsColumnString(\\d+)/i",
191 4
                    "/([a-z]+\\d+):$maxXlsColumnString(\\d+)/i",
192 4
                ],
193 4
                [
194 4
                    '$1:$2',
195 4
                    '$1:${2}' . $maxRow,
196 4
                    '$1:$2',
197 4
                    '$1:' . $maxCol . '$2',
198 4
                ],
199 4
                $cellRange
200 4
            ) ?? $cellRange;
201 4
            $objValidation = new DataValidation();
202 4
            $objValidation->setType($type);
203 4
            $objValidation->setErrorStyle($errorStyle);
204 4
            $objValidation->setAllowBlank((bool) $allowBlank);
205 4
            $objValidation->setShowInputMessage((bool) $showInputMessage);
206 4
            $objValidation->setShowErrorMessage((bool) $showErrorMessage);
207 4
            $objValidation->setShowDropDown(!$suppressDropDown);
208 4
            $objValidation->setOperator($operator);
209 4
            $objValidation->setErrorTitle($errorTitle);
210 4
            $objValidation->setError($error);
211 4
            $objValidation->setPromptTitle($promptTitle);
212 4
            $objValidation->setPrompt($prompt);
213 4
            $objValidation->setFormula1($formula1);
214 4
            $objValidation->setFormula2($formula2);
215 4
            $xls->phpSheet->setDataValidation($cellRange, $objValidation);
216
        }
217
    }
218
}
219