Skip to content

Commit

Permalink
Fixes for 32-bit
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
oleibman committed Jan 16, 2024
1 parent ae72efe commit 5cfe66e
Show file tree
Hide file tree
Showing 11 changed files with 42 additions and 27 deletions.
6 changes: 3 additions & 3 deletions src/PhpSpreadsheet/Calculation/Engineering/BitWise.php
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down
3 changes: 2 additions & 1 deletion src/PhpSpreadsheet/Chart/Axis.php
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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
];

/**
Expand Down
22 changes: 14 additions & 8 deletions src/PhpSpreadsheet/Reader/Xls.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -7184,18 +7188,20 @@ 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);

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

$value += $mantissalow2 / 2 ** (52 - $exp);
if ($mantissalow2 != 0) {
$value += $mantissalow2 / 2 ** (52 - $exp);
}
if ($sign) {
$value *= -1;
}
Expand All @@ -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));
Expand Down
8 changes: 5 additions & 3 deletions src/PhpSpreadsheet/Shared/OLE/PPS.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@
*/
class PPS
{
private const ALL_ONE_BITS = (PHP_INT_SIZE > 4) ? 0xFFFFFFFF : -1;

/**
* The PPS index.
*/
Expand Down Expand Up @@ -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);
Expand Down
2 changes: 1 addition & 1 deletion src/PhpSpreadsheet/Writer/Xlsx/Chart.php
Original file line number Diff line number Diff line change
Expand Up @@ -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');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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);

Expand All @@ -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);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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);

Expand All @@ -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);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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);

Expand All @@ -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);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
6 changes: 6 additions & 0 deletions tests/PhpSpreadsheetTests/Chart/Issue3833Test.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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();
}
Expand Down
2 changes: 1 addition & 1 deletion tests/PhpSpreadsheetTests/Shared/DateTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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.');
Expand Down

0 comments on commit 5cfe66e

Please sign in to comment.