diff --git a/src/PhpSpreadsheet/Calculation/Engineering/BitWise.php b/src/PhpSpreadsheet/Calculation/Engineering/BitWise.php index e1ace4f313..c861c21a01 100644 --- a/src/PhpSpreadsheet/Calculation/Engineering/BitWise.php +++ b/src/PhpSpreadsheet/Calculation/Engineering/BitWise.php @@ -36,7 +36,7 @@ private static function splitNumber(float|int $number): array * @return array|int|string If an array of numbers is passed as an argument, then the returned result will also be an array * with the same dimensions */ - public static function BITAND(null|array|bool|float|int|string $number1, null|array|bool|float|int|string $number2): array|string|int + public static function BITAND(null|array|bool|float|int|string $number1, null|array|bool|float|int|string $number2): array|string|int|float { if (is_array($number1) || is_array($number2)) { return self::evaluateArrayArguments([self::class, __FUNCTION__], $number1, $number2); @@ -68,7 +68,7 @@ public static function BITAND(null|array|bool|float|int|string $number1, null|ar * @return array|int|string If an array of numbers is passed as an argument, then the returned result will also be an array * with the same dimensions */ - public static function BITOR(null|array|bool|float|int|string $number1, null|array|bool|float|int|string $number2): array|string|int + public static function BITOR(null|array|bool|float|int|string $number1, null|array|bool|float|int|string $number2): array|string|int|float { if (is_array($number1) || is_array($number2)) { return self::evaluateArrayArguments([self::class, __FUNCTION__], $number1, $number2); @@ -101,7 +101,7 @@ public static function BITOR(null|array|bool|float|int|string $number1, null|arr * @return array|int|string If an array of numbers is passed as an argument, then the returned result will also be an array * with the same dimensions */ - public static function BITXOR(null|array|bool|float|int|string $number1, null|array|bool|float|int|string $number2): array|string|int + public static function BITXOR(null|array|bool|float|int|string $number1, null|array|bool|float|int|string $number2): array|string|int|float { if (is_array($number1) || is_array($number2)) { return self::evaluateArrayArguments([self::class, __FUNCTION__], $number1, $number2); diff --git a/src/PhpSpreadsheet/Chart/Axis.php b/src/PhpSpreadsheet/Chart/Axis.php index 1d27c8b402..b83795858b 100644 --- a/src/PhpSpreadsheet/Chart/Axis.php +++ b/src/PhpSpreadsheet/Chart/Axis.php @@ -84,6 +84,7 @@ public function __construct() public const DISP_UNITS_HUNDRED_MILLIONS = 'hundredMillions'; public const DISP_UNITS_BILLIONS = 'billions'; public const DISP_UNITS_TRILLIONS = 'trillions'; + public const TRILLION_INDEX = (PHP_INT_SIZE > 4) ? 1000000000000 : '1000000000000'; public const DISP_UNITS_BUILTIN_INT = [ 100 => self::DISP_UNITS_HUNDREDS, 1000 => self::DISP_UNITS_THOUSANDS, @@ -93,7 +94,7 @@ public function __construct() 10000000 => self::DISP_UNITS_TEN_MILLIONS, 100000000 => self::DISP_UNITS_HUNDRED_MILLIONS, 1000000000 => self::DISP_UNITS_BILLIONS, - 1000000000000 => self::DISP_UNITS_TRILLIONS, + self::TRILLION_INDEX => self::DISP_UNITS_TRILLIONS, // overflow for 32-bit ]; /** diff --git a/src/PhpSpreadsheet/Reader/Xls.php b/src/PhpSpreadsheet/Reader/Xls.php index a54105a3b4..36c7072441 100644 --- a/src/PhpSpreadsheet/Reader/Xls.php +++ b/src/PhpSpreadsheet/Reader/Xls.php @@ -67,6 +67,10 @@ // external sheet reference structure class Xls extends BaseReader { + private const HIGH_ORDER_BIT = 0x80 << 24; + private const FC000000 = 0xFC << 24; + private const FE000000 = 0xFE << 24; + // ParseXL definitions const XLS_BIFF8 = 0x0600; const XLS_BIFF7 = 0x0500; @@ -2150,8 +2154,8 @@ private function readXf(): void // bit: 30; mask: 0x40000000; 1 = diagonal line from top left to right bottom $diagonalDown = (0x40000000 & self::getInt4d($recordData, 10)) >> 30 ? true : false; - // bit: 31; mask: 0x80000000; 1 = diagonal line from bottom left to top right - $diagonalUp = ((int) 0x80000000 & self::getInt4d($recordData, 10)) >> 31 ? true : false; + // bit: 31; mask: 0x800000; 1 = diagonal line from bottom left to top right + $diagonalUp = (self::HIGH_ORDER_BIT & self::getInt4d($recordData, 10)) >> 31 ? true : false; if ($diagonalUp === false) { if ($diagonalDown === false) { @@ -2181,7 +2185,7 @@ private function readXf(): void } // bit: 31-26; mask: 0xFC000000 fill pattern - if ($fillType = Xls\Style\FillPattern::lookup(((int) 0xFC000000 & self::getInt4d($recordData, 14)) >> 26)) { + if ($fillType = Xls\Style\FillPattern::lookup((self::FC000000 & self::getInt4d($recordData, 14)) >> 26)) { $objStyle->getFill()->setFillType($fillType); } // offset: 18; size: 2; pattern and background colour @@ -2233,7 +2237,7 @@ private function readXf(): void $objStyle->getBorders()->getBottom()->setBorderStyle(Xls\Style\Border::lookup((0x01C00000 & $borderAndBackground) >> 22)); // bit: 31-25; mask: 0xFE000000; bottom line color - $objStyle->getBorders()->getBottom()->colorIndex = ((int) 0xFE000000 & $borderAndBackground) >> 25; + $objStyle->getBorders()->getBottom()->colorIndex = (self::FE000000 & $borderAndBackground) >> 25; // offset: 12; size: 4; cell border lines $borderLines = self::getInt4d($recordData, 12); @@ -7184,10 +7188,10 @@ private static function extractNumber(string $data): int|float { $rknumhigh = self::getInt4d($data, 4); $rknumlow = self::getInt4d($data, 0); - $sign = ($rknumhigh & (int) 0x80000000) >> 31; + $sign = ($rknumhigh & self::HIGH_ORDER_BIT) >> 31; $exp = (($rknumhigh & 0x7FF00000) >> 20) - 1023; $mantissa = (0x100000 | ($rknumhigh & 0x000FFFFF)); - $mantissalow1 = ($rknumlow & (int) 0x80000000) >> 31; + $mantissalow1 = ($rknumlow & self::HIGH_ORDER_BIT) >> 31; $mantissalow2 = ($rknumlow & 0x7FFFFFFF); $value = $mantissa / 2 ** (20 - $exp); @@ -7195,7 +7199,9 @@ private static function extractNumber(string $data): int|float $value += 1 / 2 ** (21 - $exp); } - $value += $mantissalow2 / 2 ** (52 - $exp); + if ($mantissalow2 != 0) { + $value += $mantissalow2 / 2 ** (52 - $exp); + } if ($sign) { $value *= -1; } @@ -7213,7 +7219,7 @@ private static function getIEEE754(int $rknum): float|int // The RK format calls for using only the most significant 30 bits // of the 64 bit floating point value. The other 34 bits are assumed // to be 0 so we use the upper 30 bits of $rknum as follows... - $sign = ($rknum & (int) 0x80000000) >> 31; + $sign = ($rknum & self::HIGH_ORDER_BIT) >> 31; $exp = ($rknum & 0x7FF00000) >> 20; $mantissa = (0x100000 | ($rknum & 0x000FFFFC)); $value = $mantissa / 2 ** (20 - ($exp - 1023)); diff --git a/src/PhpSpreadsheet/Shared/OLE/PPS.php b/src/PhpSpreadsheet/Shared/OLE/PPS.php index 564fe7ec7c..d67dbd6df1 100644 --- a/src/PhpSpreadsheet/Shared/OLE/PPS.php +++ b/src/PhpSpreadsheet/Shared/OLE/PPS.php @@ -29,6 +29,8 @@ */ class PPS { + private const ALL_ONE_BITS = (PHP_INT_SIZE > 4) ? 0xFFFFFFFF : -1; + /** * The PPS index. */ @@ -178,14 +180,14 @@ public function getPpsWk(): string public static function savePpsSetPnt(array &$raList, mixed $to_save, mixed $depth = 0): int { if (!is_array($to_save) || (empty($to_save))) { - return 0xFFFFFFFF; + return self::ALL_ONE_BITS; } elseif (count($to_save) == 1) { $cnt = count($raList); // If the first entry, it's the root... Don't clone it! $raList[$cnt] = ($depth == 0) ? $to_save[0] : clone $to_save[0]; $raList[$cnt]->No = $cnt; - $raList[$cnt]->PrevPps = 0xFFFFFFFF; - $raList[$cnt]->NextPps = 0xFFFFFFFF; + $raList[$cnt]->PrevPps = self::ALL_ONE_BITS; + $raList[$cnt]->NextPps = self::ALL_ONE_BITS; $raList[$cnt]->DirPps = self::savePpsSetPnt($raList, @$raList[$cnt]->children, $depth++); } else { $iPos = (int) floor(count($to_save) / 2); diff --git a/src/PhpSpreadsheet/Writer/Xlsx/Chart.php b/src/PhpSpreadsheet/Writer/Xlsx/Chart.php index c89696ac9e..1e96db305d 100644 --- a/src/PhpSpreadsheet/Writer/Xlsx/Chart.php +++ b/src/PhpSpreadsheet/Writer/Xlsx/Chart.php @@ -968,7 +968,7 @@ private function writeValueAxis(XMLWriter $objWriter, ?Title $yAxisLabel, ?strin if ($xAxis->getAxisType() === Axis::AXIS_TYPE_VALUE) { $dispUnits = $xAxis->getAxisOptionsProperty('dispUnitsBuiltIn'); - $dispUnits = is_numeric($dispUnits) ? (Axis::DISP_UNITS_BUILTIN_INT[(int) $dispUnits] ?? '') : $dispUnits; + $dispUnits = ($dispUnits == Axis::TRILLION_INDEX) ? Axis::DISP_UNITS_TRILLIONS : (is_numeric($dispUnits) ? (Axis::DISP_UNITS_BUILTIN_INT[(int) $dispUnits] ?? '') : $dispUnits); if (in_array($dispUnits, Axis::DISP_UNITS_BUILTIN_INT, true)) { $objWriter->startElement('c:dispUnits'); $objWriter->startElement('c:builtInUnit'); diff --git a/tests/PhpSpreadsheetTests/Calculation/Functions/Engineering/BitAndTest.php b/tests/PhpSpreadsheetTests/Calculation/Functions/Engineering/BitAndTest.php index ed3ba55629..1f913cbed8 100644 --- a/tests/PhpSpreadsheetTests/Calculation/Functions/Engineering/BitAndTest.php +++ b/tests/PhpSpreadsheetTests/Calculation/Functions/Engineering/BitAndTest.php @@ -16,7 +16,7 @@ class BitAndTest extends TestCase /** * @dataProvider providerBITAND */ - public function testDirectCallToBITAND(mixed $expectedResult, null|bool|int|float|string $arg1, null|bool|int|float|string $arg2): void + public function testDirectCallToBITAND(float|int|string $expectedResult, null|bool|int|float|string $arg1, null|bool|int|float|string $arg2): void { $result = BitWise::BITAND($arg1, $arg2); self::assertSame($expectedResult, $result); @@ -25,7 +25,7 @@ public function testDirectCallToBITAND(mixed $expectedResult, null|bool|int|floa /** * @dataProvider providerBITAND */ - public function testBITANDAsFormula(mixed $expectedResult, mixed ...$args): void + public function testBITANDAsFormula(float|int|string $expectedResult, mixed ...$args): void { $arguments = new FormulaArguments(...$args); @@ -39,7 +39,7 @@ public function testBITANDAsFormula(mixed $expectedResult, mixed ...$args): void /** * @dataProvider providerBITAND */ - public function testBITANDInWorksheet(mixed $expectedResult, mixed ...$args): void + public function testBITANDInWorksheet(float|int|string $expectedResult, mixed ...$args): void { $arguments = new FormulaArguments(...$args); diff --git a/tests/PhpSpreadsheetTests/Calculation/Functions/Engineering/BitOrTest.php b/tests/PhpSpreadsheetTests/Calculation/Functions/Engineering/BitOrTest.php index 9471f27d25..f27f731f3c 100644 --- a/tests/PhpSpreadsheetTests/Calculation/Functions/Engineering/BitOrTest.php +++ b/tests/PhpSpreadsheetTests/Calculation/Functions/Engineering/BitOrTest.php @@ -16,7 +16,7 @@ class BitOrTest extends TestCase /** * @dataProvider providerBITOR */ - public function testDirectCallToBITOR(mixed $expectedResult, null|bool|int|float|string $arg1, null|bool|int|float|string $arg2): void + public function testDirectCallToBITOR(float|int|string $expectedResult, null|bool|int|float|string $arg1, null|bool|int|float|string $arg2): void { $result = BitWise::BITOR($arg1, $arg2); self::assertSame($expectedResult, $result); @@ -25,7 +25,7 @@ public function testDirectCallToBITOR(mixed $expectedResult, null|bool|int|float /** * @dataProvider providerBITOR */ - public function testBITORAsFormula(mixed $expectedResult, mixed ...$args): void + public function testBITORAsFormula(float|int|string $expectedResult, mixed ...$args): void { $arguments = new FormulaArguments(...$args); @@ -39,7 +39,7 @@ public function testBITORAsFormula(mixed $expectedResult, mixed ...$args): void /** * @dataProvider providerBITOR */ - public function testBITORInWorksheet(mixed $expectedResult, mixed ...$args): void + public function testBITORInWorksheet(float|int|string $expectedResult, mixed ...$args): void { $arguments = new FormulaArguments(...$args); diff --git a/tests/PhpSpreadsheetTests/Calculation/Functions/Engineering/BitXorTest.php b/tests/PhpSpreadsheetTests/Calculation/Functions/Engineering/BitXorTest.php index a9e6489b9c..1c42c0f041 100644 --- a/tests/PhpSpreadsheetTests/Calculation/Functions/Engineering/BitXorTest.php +++ b/tests/PhpSpreadsheetTests/Calculation/Functions/Engineering/BitXorTest.php @@ -16,7 +16,7 @@ class BitXorTest extends TestCase /** * @dataProvider providerBITXOR */ - public function testDirectCallToBITXOR(int|string $expectedResult, null|bool|int|float|string $arg1, null|bool|int|float|string $arg2): void + public function testDirectCallToBITXOR(float|int|string $expectedResult, null|bool|int|float|string $arg1, null|bool|int|float|string $arg2): void { $result = BitWise::BITXOR($arg1, $arg2); self::assertSame($expectedResult, $result); @@ -25,7 +25,7 @@ public function testDirectCallToBITXOR(int|string $expectedResult, null|bool|int /** * @dataProvider providerBITXOR */ - public function testBITXORAsFormula(mixed $expectedResult, mixed ...$args): void + public function testBITXORAsFormula(float|int|string $expectedResult, mixed ...$args): void { $arguments = new FormulaArguments(...$args); @@ -39,7 +39,7 @@ public function testBITXORAsFormula(mixed $expectedResult, mixed ...$args): void /** * @dataProvider providerBITXOR */ - public function testBITXORInWorksheet(mixed $expectedResult, mixed ...$args): void + public function testBITXORInWorksheet(float|int|string $expectedResult, mixed ...$args): void { $arguments = new FormulaArguments(...$args); diff --git a/tests/PhpSpreadsheetTests/Calculation/Functions/Engineering/ImExpTest.php b/tests/PhpSpreadsheetTests/Calculation/Functions/Engineering/ImExpTest.php index a6834310ca..4ecfa1a884 100644 --- a/tests/PhpSpreadsheetTests/Calculation/Functions/Engineering/ImExpTest.php +++ b/tests/PhpSpreadsheetTests/Calculation/Functions/Engineering/ImExpTest.php @@ -15,7 +15,7 @@ class ImExpTest extends TestCase { - const COMPLEX_PRECISION = 1E-12; + const COMPLEX_PRECISION = (PHP_INT_SIZE > 4) ? 1E-12 : 1E-9; private \PhpOffice\PhpSpreadsheetTests\Custom\ComplexAssert $complexAssert; diff --git a/tests/PhpSpreadsheetTests/Chart/Issue3833Test.php b/tests/PhpSpreadsheetTests/Chart/Issue3833Test.php index 669e23306f..efd7c5f939 100644 --- a/tests/PhpSpreadsheetTests/Chart/Issue3833Test.php +++ b/tests/PhpSpreadsheetTests/Chart/Issue3833Test.php @@ -4,6 +4,7 @@ namespace PhpOffice\PhpSpreadsheetTests\Chart; +use PhpOffice\PhpSpreadsheet\Chart\Axis; use PhpOffice\PhpSpreadsheet\Reader\Xlsx as XlsxReader; use PhpOffice\PhpSpreadsheet\Writer\Xlsx as XlsxWriter; use PhpOffice\PhpSpreadsheetTests\Functional\AbstractFunctional; @@ -136,6 +137,11 @@ public function testLogBase(): void self::assertSame('10', $logBase); $dispUnits = $yAxis->getAxisOptionsProperty('dispUnitsBuiltIn'); self::assertNull($dispUnits); + $yAxis->setAxisOption('dispUnitsBuiltIn', 1000000000000); + $dispUnits = $yAxis->getAxisOptionsProperty('dispUnitsBuiltIn'); + // same logic as in Writer/Xlsx/Chart for 32-bit safety + $dispUnits = ($dispUnits == Axis::TRILLION_INDEX) ? Axis::DISP_UNITS_TRILLIONS : (is_numeric($dispUnits) ? (Axis::DISP_UNITS_BUILTIN_INT[(int) $dispUnits] ?? '') : $dispUnits); + self::assertSame('trillions', $dispUnits); $reloadedSpreadsheet->disconnectWorksheets(); } diff --git a/tests/PhpSpreadsheetTests/Shared/DateTest.php b/tests/PhpSpreadsheetTests/Shared/DateTest.php index f4f1256889..8da86e98ee 100644 --- a/tests/PhpSpreadsheetTests/Shared/DateTest.php +++ b/tests/PhpSpreadsheetTests/Shared/DateTest.php @@ -170,7 +170,7 @@ public static function providerIsDateTimeFormatCode(): array /** * @dataProvider providerDateTimeExcelToTimestamp1900Timezone */ - public function testDateTimeExcelToTimestamp1900Timezone(int $expectedResult, float|int $excelDateTimeValue, string $timezone): void + public function testDateTimeExcelToTimestamp1900Timezone(float|int $expectedResult, float|int $excelDateTimeValue, string $timezone): void { if (is_numeric($expectedResult) && ($expectedResult > PHP_INT_MAX || $expectedResult < PHP_INT_MIN)) { self::markTestSkipped('Test invalid on 32-bit system.');