Skip to content

Commit 5cfe66e

Browse files
committed
Fixes for 32-bit
I check from time to time. There are a number of problems now, mostly due to the elimination of Php 7.4 and replacement of doc-block typing with explicit Php typing. - Bitwise functions were particularly affected by PR PHPOffice#3718 and PR PHPOffice#3793. - Chart/Axis and Writer/Xlsx were amusingly affected by PR PHPOffice#3836, which added a scaling option which included an array indexed by the known allowable factors, one of which is 1 trillion, which cannot be represented as an integer on a 32-bit system. Issue3833Test, introduced by the same PR (and not suffering any errors) was expanded to test this value. - Some minor changes to Reader/Xls and Shared/OLE/PPS to accommodate hex values which are negative in 32-bit but which Php-32 may wind up casting to large floating point numbers; it is not clear to me why these hadn't shown up as problems previously. Possibly this is the result of changes in the most recent Php versions. - BitAndTest, BitOrTest, BitXorTest and Shared/DateTest were adversely affected by PR PHPOffice#3859 when arguments and/or expected results too large for a 32-bit integer were supplied. - ImExpTest required a slightly reduced precision for 32-bit. No idea why this hadn't shown up earlier.
1 parent ae72efe commit 5cfe66e

File tree

11 files changed

+42
-27
lines changed

11 files changed

+42
-27
lines changed

src/PhpSpreadsheet/Calculation/Engineering/BitWise.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ private static function splitNumber(float|int $number): array
3636
* @return array|int|string If an array of numbers is passed as an argument, then the returned result will also be an array
3737
* with the same dimensions
3838
*/
39-
public static function BITAND(null|array|bool|float|int|string $number1, null|array|bool|float|int|string $number2): array|string|int
39+
public static function BITAND(null|array|bool|float|int|string $number1, null|array|bool|float|int|string $number2): array|string|int|float
4040
{
4141
if (is_array($number1) || is_array($number2)) {
4242
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
6868
* @return array|int|string If an array of numbers is passed as an argument, then the returned result will also be an array
6969
* with the same dimensions
7070
*/
71-
public static function BITOR(null|array|bool|float|int|string $number1, null|array|bool|float|int|string $number2): array|string|int
71+
public static function BITOR(null|array|bool|float|int|string $number1, null|array|bool|float|int|string $number2): array|string|int|float
7272
{
7373
if (is_array($number1) || is_array($number2)) {
7474
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
101101
* @return array|int|string If an array of numbers is passed as an argument, then the returned result will also be an array
102102
* with the same dimensions
103103
*/
104-
public static function BITXOR(null|array|bool|float|int|string $number1, null|array|bool|float|int|string $number2): array|string|int
104+
public static function BITXOR(null|array|bool|float|int|string $number1, null|array|bool|float|int|string $number2): array|string|int|float
105105
{
106106
if (is_array($number1) || is_array($number2)) {
107107
return self::evaluateArrayArguments([self::class, __FUNCTION__], $number1, $number2);

src/PhpSpreadsheet/Chart/Axis.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ public function __construct()
8484
public const DISP_UNITS_HUNDRED_MILLIONS = 'hundredMillions';
8585
public const DISP_UNITS_BILLIONS = 'billions';
8686
public const DISP_UNITS_TRILLIONS = 'trillions';
87+
public const TRILLION_INDEX = (PHP_INT_SIZE > 4) ? 1000000000000 : '1000000000000';
8788
public const DISP_UNITS_BUILTIN_INT = [
8889
100 => self::DISP_UNITS_HUNDREDS,
8990
1000 => self::DISP_UNITS_THOUSANDS,
@@ -93,7 +94,7 @@ public function __construct()
9394
10000000 => self::DISP_UNITS_TEN_MILLIONS,
9495
100000000 => self::DISP_UNITS_HUNDRED_MILLIONS,
9596
1000000000 => self::DISP_UNITS_BILLIONS,
96-
1000000000000 => self::DISP_UNITS_TRILLIONS,
97+
self::TRILLION_INDEX => self::DISP_UNITS_TRILLIONS, // overflow for 32-bit
9798
];
9899

99100
/**

src/PhpSpreadsheet/Reader/Xls.php

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,10 @@
6767
// external sheet reference structure
6868
class Xls extends BaseReader
6969
{
70+
private const HIGH_ORDER_BIT = 0x80 << 24;
71+
private const FC000000 = 0xFC << 24;
72+
private const FE000000 = 0xFE << 24;
73+
7074
// ParseXL definitions
7175
const XLS_BIFF8 = 0x0600;
7276
const XLS_BIFF7 = 0x0500;
@@ -2150,8 +2154,8 @@ private function readXf(): void
21502154
// bit: 30; mask: 0x40000000; 1 = diagonal line from top left to right bottom
21512155
$diagonalDown = (0x40000000 & self::getInt4d($recordData, 10)) >> 30 ? true : false;
21522156

2153-
// bit: 31; mask: 0x80000000; 1 = diagonal line from bottom left to top right
2154-
$diagonalUp = ((int) 0x80000000 & self::getInt4d($recordData, 10)) >> 31 ? true : false;
2157+
// bit: 31; mask: 0x800000; 1 = diagonal line from bottom left to top right
2158+
$diagonalUp = (self::HIGH_ORDER_BIT & self::getInt4d($recordData, 10)) >> 31 ? true : false;
21552159

21562160
if ($diagonalUp === false) {
21572161
if ($diagonalDown === false) {
@@ -2181,7 +2185,7 @@ private function readXf(): void
21812185
}
21822186

21832187
// bit: 31-26; mask: 0xFC000000 fill pattern
2184-
if ($fillType = Xls\Style\FillPattern::lookup(((int) 0xFC000000 & self::getInt4d($recordData, 14)) >> 26)) {
2188+
if ($fillType = Xls\Style\FillPattern::lookup((self::FC000000 & self::getInt4d($recordData, 14)) >> 26)) {
21852189
$objStyle->getFill()->setFillType($fillType);
21862190
}
21872191
// offset: 18; size: 2; pattern and background colour
@@ -2233,7 +2237,7 @@ private function readXf(): void
22332237
$objStyle->getBorders()->getBottom()->setBorderStyle(Xls\Style\Border::lookup((0x01C00000 & $borderAndBackground) >> 22));
22342238

22352239
// bit: 31-25; mask: 0xFE000000; bottom line color
2236-
$objStyle->getBorders()->getBottom()->colorIndex = ((int) 0xFE000000 & $borderAndBackground) >> 25;
2240+
$objStyle->getBorders()->getBottom()->colorIndex = (self::FE000000 & $borderAndBackground) >> 25;
22372241

22382242
// offset: 12; size: 4; cell border lines
22392243
$borderLines = self::getInt4d($recordData, 12);
@@ -7184,18 +7188,20 @@ private static function extractNumber(string $data): int|float
71847188
{
71857189
$rknumhigh = self::getInt4d($data, 4);
71867190
$rknumlow = self::getInt4d($data, 0);
7187-
$sign = ($rknumhigh & (int) 0x80000000) >> 31;
7191+
$sign = ($rknumhigh & self::HIGH_ORDER_BIT) >> 31;
71887192
$exp = (($rknumhigh & 0x7FF00000) >> 20) - 1023;
71897193
$mantissa = (0x100000 | ($rknumhigh & 0x000FFFFF));
7190-
$mantissalow1 = ($rknumlow & (int) 0x80000000) >> 31;
7194+
$mantissalow1 = ($rknumlow & self::HIGH_ORDER_BIT) >> 31;
71917195
$mantissalow2 = ($rknumlow & 0x7FFFFFFF);
71927196
$value = $mantissa / 2 ** (20 - $exp);
71937197

71947198
if ($mantissalow1 != 0) {
71957199
$value += 1 / 2 ** (21 - $exp);
71967200
}
71977201

7198-
$value += $mantissalow2 / 2 ** (52 - $exp);
7202+
if ($mantissalow2 != 0) {
7203+
$value += $mantissalow2 / 2 ** (52 - $exp);
7204+
}
71997205
if ($sign) {
72007206
$value *= -1;
72017207
}
@@ -7213,7 +7219,7 @@ private static function getIEEE754(int $rknum): float|int
72137219
// The RK format calls for using only the most significant 30 bits
72147220
// of the 64 bit floating point value. The other 34 bits are assumed
72157221
// to be 0 so we use the upper 30 bits of $rknum as follows...
7216-
$sign = ($rknum & (int) 0x80000000) >> 31;
7222+
$sign = ($rknum & self::HIGH_ORDER_BIT) >> 31;
72177223
$exp = ($rknum & 0x7FF00000) >> 20;
72187224
$mantissa = (0x100000 | ($rknum & 0x000FFFFC));
72197225
$value = $mantissa / 2 ** (20 - ($exp - 1023));

src/PhpSpreadsheet/Shared/OLE/PPS.php

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@
2929
*/
3030
class PPS
3131
{
32+
private const ALL_ONE_BITS = (PHP_INT_SIZE > 4) ? 0xFFFFFFFF : -1;
33+
3234
/**
3335
* The PPS index.
3436
*/
@@ -178,14 +180,14 @@ public function getPpsWk(): string
178180
public static function savePpsSetPnt(array &$raList, mixed $to_save, mixed $depth = 0): int
179181
{
180182
if (!is_array($to_save) || (empty($to_save))) {
181-
return 0xFFFFFFFF;
183+
return self::ALL_ONE_BITS;
182184
} elseif (count($to_save) == 1) {
183185
$cnt = count($raList);
184186
// If the first entry, it's the root... Don't clone it!
185187
$raList[$cnt] = ($depth == 0) ? $to_save[0] : clone $to_save[0];
186188
$raList[$cnt]->No = $cnt;
187-
$raList[$cnt]->PrevPps = 0xFFFFFFFF;
188-
$raList[$cnt]->NextPps = 0xFFFFFFFF;
189+
$raList[$cnt]->PrevPps = self::ALL_ONE_BITS;
190+
$raList[$cnt]->NextPps = self::ALL_ONE_BITS;
189191
$raList[$cnt]->DirPps = self::savePpsSetPnt($raList, @$raList[$cnt]->children, $depth++);
190192
} else {
191193
$iPos = (int) floor(count($to_save) / 2);

src/PhpSpreadsheet/Writer/Xlsx/Chart.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -968,7 +968,7 @@ private function writeValueAxis(XMLWriter $objWriter, ?Title $yAxisLabel, ?strin
968968

969969
if ($xAxis->getAxisType() === Axis::AXIS_TYPE_VALUE) {
970970
$dispUnits = $xAxis->getAxisOptionsProperty('dispUnitsBuiltIn');
971-
$dispUnits = is_numeric($dispUnits) ? (Axis::DISP_UNITS_BUILTIN_INT[(int) $dispUnits] ?? '') : $dispUnits;
971+
$dispUnits = ($dispUnits == Axis::TRILLION_INDEX) ? Axis::DISP_UNITS_TRILLIONS : (is_numeric($dispUnits) ? (Axis::DISP_UNITS_BUILTIN_INT[(int) $dispUnits] ?? '') : $dispUnits);
972972
if (in_array($dispUnits, Axis::DISP_UNITS_BUILTIN_INT, true)) {
973973
$objWriter->startElement('c:dispUnits');
974974
$objWriter->startElement('c:builtInUnit');

tests/PhpSpreadsheetTests/Calculation/Functions/Engineering/BitAndTest.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ class BitAndTest extends TestCase
1616
/**
1717
* @dataProvider providerBITAND
1818
*/
19-
public function testDirectCallToBITAND(mixed $expectedResult, null|bool|int|float|string $arg1, null|bool|int|float|string $arg2): void
19+
public function testDirectCallToBITAND(float|int|string $expectedResult, null|bool|int|float|string $arg1, null|bool|int|float|string $arg2): void
2020
{
2121
$result = BitWise::BITAND($arg1, $arg2);
2222
self::assertSame($expectedResult, $result);
@@ -25,7 +25,7 @@ public function testDirectCallToBITAND(mixed $expectedResult, null|bool|int|floa
2525
/**
2626
* @dataProvider providerBITAND
2727
*/
28-
public function testBITANDAsFormula(mixed $expectedResult, mixed ...$args): void
28+
public function testBITANDAsFormula(float|int|string $expectedResult, mixed ...$args): void
2929
{
3030
$arguments = new FormulaArguments(...$args);
3131

@@ -39,7 +39,7 @@ public function testBITANDAsFormula(mixed $expectedResult, mixed ...$args): void
3939
/**
4040
* @dataProvider providerBITAND
4141
*/
42-
public function testBITANDInWorksheet(mixed $expectedResult, mixed ...$args): void
42+
public function testBITANDInWorksheet(float|int|string $expectedResult, mixed ...$args): void
4343
{
4444
$arguments = new FormulaArguments(...$args);
4545

tests/PhpSpreadsheetTests/Calculation/Functions/Engineering/BitOrTest.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ class BitOrTest extends TestCase
1616
/**
1717
* @dataProvider providerBITOR
1818
*/
19-
public function testDirectCallToBITOR(mixed $expectedResult, null|bool|int|float|string $arg1, null|bool|int|float|string $arg2): void
19+
public function testDirectCallToBITOR(float|int|string $expectedResult, null|bool|int|float|string $arg1, null|bool|int|float|string $arg2): void
2020
{
2121
$result = BitWise::BITOR($arg1, $arg2);
2222
self::assertSame($expectedResult, $result);
@@ -25,7 +25,7 @@ public function testDirectCallToBITOR(mixed $expectedResult, null|bool|int|float
2525
/**
2626
* @dataProvider providerBITOR
2727
*/
28-
public function testBITORAsFormula(mixed $expectedResult, mixed ...$args): void
28+
public function testBITORAsFormula(float|int|string $expectedResult, mixed ...$args): void
2929
{
3030
$arguments = new FormulaArguments(...$args);
3131

@@ -39,7 +39,7 @@ public function testBITORAsFormula(mixed $expectedResult, mixed ...$args): void
3939
/**
4040
* @dataProvider providerBITOR
4141
*/
42-
public function testBITORInWorksheet(mixed $expectedResult, mixed ...$args): void
42+
public function testBITORInWorksheet(float|int|string $expectedResult, mixed ...$args): void
4343
{
4444
$arguments = new FormulaArguments(...$args);
4545

tests/PhpSpreadsheetTests/Calculation/Functions/Engineering/BitXorTest.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ class BitXorTest extends TestCase
1616
/**
1717
* @dataProvider providerBITXOR
1818
*/
19-
public function testDirectCallToBITXOR(int|string $expectedResult, null|bool|int|float|string $arg1, null|bool|int|float|string $arg2): void
19+
public function testDirectCallToBITXOR(float|int|string $expectedResult, null|bool|int|float|string $arg1, null|bool|int|float|string $arg2): void
2020
{
2121
$result = BitWise::BITXOR($arg1, $arg2);
2222
self::assertSame($expectedResult, $result);
@@ -25,7 +25,7 @@ public function testDirectCallToBITXOR(int|string $expectedResult, null|bool|int
2525
/**
2626
* @dataProvider providerBITXOR
2727
*/
28-
public function testBITXORAsFormula(mixed $expectedResult, mixed ...$args): void
28+
public function testBITXORAsFormula(float|int|string $expectedResult, mixed ...$args): void
2929
{
3030
$arguments = new FormulaArguments(...$args);
3131

@@ -39,7 +39,7 @@ public function testBITXORAsFormula(mixed $expectedResult, mixed ...$args): void
3939
/**
4040
* @dataProvider providerBITXOR
4141
*/
42-
public function testBITXORInWorksheet(mixed $expectedResult, mixed ...$args): void
42+
public function testBITXORInWorksheet(float|int|string $expectedResult, mixed ...$args): void
4343
{
4444
$arguments = new FormulaArguments(...$args);
4545

tests/PhpSpreadsheetTests/Calculation/Functions/Engineering/ImExpTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515

1616
class ImExpTest extends TestCase
1717
{
18-
const COMPLEX_PRECISION = 1E-12;
18+
const COMPLEX_PRECISION = (PHP_INT_SIZE > 4) ? 1E-12 : 1E-9;
1919

2020
private \PhpOffice\PhpSpreadsheetTests\Custom\ComplexAssert $complexAssert;
2121

tests/PhpSpreadsheetTests/Chart/Issue3833Test.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
namespace PhpOffice\PhpSpreadsheetTests\Chart;
66

7+
use PhpOffice\PhpSpreadsheet\Chart\Axis;
78
use PhpOffice\PhpSpreadsheet\Reader\Xlsx as XlsxReader;
89
use PhpOffice\PhpSpreadsheet\Writer\Xlsx as XlsxWriter;
910
use PhpOffice\PhpSpreadsheetTests\Functional\AbstractFunctional;
@@ -136,6 +137,11 @@ public function testLogBase(): void
136137
self::assertSame('10', $logBase);
137138
$dispUnits = $yAxis->getAxisOptionsProperty('dispUnitsBuiltIn');
138139
self::assertNull($dispUnits);
140+
$yAxis->setAxisOption('dispUnitsBuiltIn', 1000000000000);
141+
$dispUnits = $yAxis->getAxisOptionsProperty('dispUnitsBuiltIn');
142+
// same logic as in Writer/Xlsx/Chart for 32-bit safety
143+
$dispUnits = ($dispUnits == Axis::TRILLION_INDEX) ? Axis::DISP_UNITS_TRILLIONS : (is_numeric($dispUnits) ? (Axis::DISP_UNITS_BUILTIN_INT[(int) $dispUnits] ?? '') : $dispUnits);
144+
self::assertSame('trillions', $dispUnits);
139145

140146
$reloadedSpreadsheet->disconnectWorksheets();
141147
}

tests/PhpSpreadsheetTests/Shared/DateTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,7 @@ public static function providerIsDateTimeFormatCode(): array
170170
/**
171171
* @dataProvider providerDateTimeExcelToTimestamp1900Timezone
172172
*/
173-
public function testDateTimeExcelToTimestamp1900Timezone(int $expectedResult, float|int $excelDateTimeValue, string $timezone): void
173+
public function testDateTimeExcelToTimestamp1900Timezone(float|int $expectedResult, float|int $excelDateTimeValue, string $timezone): void
174174
{
175175
if (is_numeric($expectedResult) && ($expectedResult > PHP_INT_MAX || $expectedResult < PHP_INT_MIN)) {
176176
self::markTestSkipped('Test invalid on 32-bit system.');

0 commit comments

Comments
 (0)