*/ private static array $types = [ 0x00 => DataValidation::TYPE_NONE, 0x01 => DataValidation::TYPE_WHOLE, 0x02 => DataValidation::TYPE_DECIMAL, 0x03 => DataValidation::TYPE_LIST, 0x04 => DataValidation::TYPE_DATE, 0x05 => DataValidation::TYPE_TIME, 0x06 => DataValidation::TYPE_TEXTLENGTH, 0x07 => DataValidation::TYPE_CUSTOM, ]; /** * @var array */ private static array $errorStyles = [ 0x00 => DataValidation::STYLE_STOP, 0x01 => DataValidation::STYLE_WARNING, 0x02 => DataValidation::STYLE_INFORMATION, ]; /** * @var array */ private static array $operators = [ 0x00 => DataValidation::OPERATOR_BETWEEN, 0x01 => DataValidation::OPERATOR_NOTBETWEEN, 0x02 => DataValidation::OPERATOR_EQUAL, 0x03 => DataValidation::OPERATOR_NOTEQUAL, 0x04 => DataValidation::OPERATOR_GREATERTHAN, 0x05 => DataValidation::OPERATOR_LESSTHAN, 0x06 => DataValidation::OPERATOR_GREATERTHANOREQUAL, 0x07 => DataValidation::OPERATOR_LESSTHANOREQUAL, ]; public static function type(int $type): ?string { return self::$types[$type] ?? null; } public static function errorStyle(int $errorStyle): ?string { return self::$errorStyles[$errorStyle] ?? null; } public static function operator(int $operator): ?string { return self::$operators[$operator] ?? null; } /** * Read DATAVALIDATION record. */ protected function readDataValidation2(Xls $xls): void { $length = self::getUInt2d($xls->data, $xls->pos + 2); $recordData = $xls->readRecordData($xls->data, $xls->pos + 4, $length); // move stream pointer forward to next record $xls->pos += 4 + $length; if ($xls->readDataOnly) { return; } // offset: 0; size: 4; Options $options = self::getInt4d($recordData, 0); // bit: 0-3; mask: 0x0000000F; type $type = (0x0000000F & $options) >> 0; $type = self::type($type); // bit: 4-6; mask: 0x00000070; error type $errorStyle = (0x00000070 & $options) >> 4; $errorStyle = self::errorStyle($errorStyle); // bit: 7; mask: 0x00000080; 1= formula is explicit (only applies to list) // I have only seen cases where this is 1 //$explicitFormula = (0x00000080 & $options) >> 7; // bit: 8; mask: 0x00000100; 1= empty cells allowed $allowBlank = (0x00000100 & $options) >> 8; // bit: 9; mask: 0x00000200; 1= suppress drop down arrow in list type validity $suppressDropDown = (0x00000200 & $options) >> 9; // bit: 18; mask: 0x00040000; 1= show prompt box if cell selected $showInputMessage = (0x00040000 & $options) >> 18; // bit: 19; mask: 0x00080000; 1= show error box if invalid values entered $showErrorMessage = (0x00080000 & $options) >> 19; // bit: 20-23; mask: 0x00F00000; condition operator $operator = (0x00F00000 & $options) >> 20; $operator = self::operator($operator); if ($type === null || $errorStyle === null || $operator === null) { return; } // offset: 4; size: var; title of the prompt box $offset = 4; $string = self::readUnicodeStringLong(substr($recordData, $offset)); $promptTitle = $string['value'] !== chr(0) ? $string['value'] : ''; $offset += $string['size']; // offset: var; size: var; title of the error box $string = self::readUnicodeStringLong(substr($recordData, $offset)); $errorTitle = $string['value'] !== chr(0) ? $string['value'] : ''; $offset += $string['size']; // offset: var; size: var; text of the prompt box $string = self::readUnicodeStringLong(substr($recordData, $offset)); $prompt = $string['value'] !== chr(0) ? $string['value'] : ''; $offset += $string['size']; // offset: var; size: var; text of the error box $string = self::readUnicodeStringLong(substr($recordData, $offset)); $error = $string['value'] !== chr(0) ? $string['value'] : ''; $offset += $string['size']; // offset: var; size: 2; size of the formula data for the first condition $sz1 = self::getUInt2d($recordData, $offset); $offset += 2; // offset: var; size: 2; not used $offset += 2; // offset: var; size: $sz1; formula data for first condition (without size field) $formula1 = substr($recordData, $offset, $sz1); $formula1 = pack('v', $sz1) . $formula1; // prepend the length try { $formula1 = $xls->getFormulaFromStructure($formula1); // in list type validity, null characters are used as item separators if ($type == DataValidation::TYPE_LIST) { $formula1 = str_replace(chr(0), ',', $formula1); } } catch (PhpSpreadsheetException $e) { return; } $offset += $sz1; // offset: var; size: 2; size of the formula data for the first condition $sz2 = self::getUInt2d($recordData, $offset); $offset += 2; // offset: var; size: 2; not used $offset += 2; // offset: var; size: $sz2; formula data for second condition (without size field) $formula2 = substr($recordData, $offset, $sz2); $formula2 = pack('v', $sz2) . $formula2; // prepend the length try { $formula2 = $xls->getFormulaFromStructure($formula2); } catch (PhpSpreadsheetException) { return; } $offset += $sz2; // offset: var; size: var; cell range address list with $cellRangeAddressList = Biff8::readBIFF8CellRangeAddressList(substr($recordData, $offset)); /** @var string[] */ $cellRangeAddresses = $cellRangeAddressList['cellRangeAddresses']; $maxRow = (string) AddressRange::MAX_ROW; $maxCol = AddressRange::MAX_COLUMN; $maxXlsRow = (string) XlsWorksheet::MAX_XLS_ROW; $maxXlsColumnString = (string) XlsWorksheet::MAX_XLS_COLUMN_STRING; foreach ($cellRangeAddresses as $cellRange) { $cellRange = preg_replace( [ "/([a-z]+)1:([a-z]+)$maxXlsRow/i", "/([a-z]+\\d+):([a-z]+)$maxXlsRow/i", "/A(\\d+):$maxXlsColumnString(\\d+)/i", "/([a-z]+\\d+):$maxXlsColumnString(\\d+)/i", ], [ '$1:$2', '$1:${2}' . $maxRow, '$1:$2', '$1:' . $maxCol . '$2', ], $cellRange ) ?? $cellRange; $objValidation = new DataValidation(); $objValidation->setType($type); $objValidation->setErrorStyle($errorStyle); $objValidation->setAllowBlank((bool) $allowBlank); $objValidation->setShowInputMessage((bool) $showInputMessage); $objValidation->setShowErrorMessage((bool) $showErrorMessage); $objValidation->setShowDropDown(!$suppressDropDown); $objValidation->setOperator($operator); $objValidation->setErrorTitle($errorTitle); $objValidation->setError($error); $objValidation->setPromptTitle($promptTitle); $objValidation->setPrompt($prompt); $objValidation->setFormula1($formula1); $objValidation->setFormula2($formula2); $xls->phpSheet->setDataValidation($cellRange, $objValidation); } } }