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 #3718 and PR #3793.
- Chart/Axis and Writer/Xlsx were amusingly affected by PR #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 #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.
This commit is contained in:
oleibman
2024-01-16 13:09:57 -08:00
parent ae72efe43a
commit 5cfe66e18f
11 changed files with 42 additions and 27 deletions
@@ -36,7 +36,7 @@ class BitWise
* @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 @@ class BitWise
* @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 @@ class BitWise
* @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);
+2 -1
View File
@@ -84,6 +84,7 @@ class Axis extends Properties
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 @@ class Axis extends Properties
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
];
/**
+14 -8
View File
@@ -67,6 +67,10 @@ use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
// 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 @@ class Xls extends BaseReader
// 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 @@ class Xls extends BaseReader
}
// 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 @@ class Xls extends BaseReader
$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 @@ class Xls extends BaseReader
{
$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 @@ class Xls extends BaseReader
$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 @@ class Xls extends BaseReader
// 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));
+5 -3
View File
@@ -29,6 +29,8 @@ use PhpOffice\PhpSpreadsheet\Shared\OLE;
*/
class PPS
{
private const ALL_ONE_BITS = (PHP_INT_SIZE > 4) ? 0xFFFFFFFF : -1;
/**
* The PPS index.
*/
@@ -178,14 +180,14 @@ class PPS
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);
+1 -1
View File
@@ -968,7 +968,7 @@ class Chart extends WriterPart
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');
@@ -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 @@ class BitAndTest extends TestCase
/**
* @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 @@ class BitAndTest extends TestCase
/**
* @dataProvider providerBITAND
*/
public function testBITANDInWorksheet(mixed $expectedResult, mixed ...$args): void
public function testBITANDInWorksheet(float|int|string $expectedResult, mixed ...$args): void
{
$arguments = new FormulaArguments(...$args);
@@ -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 @@ class BitOrTest extends TestCase
/**
* @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 @@ class BitOrTest extends TestCase
/**
* @dataProvider providerBITOR
*/
public function testBITORInWorksheet(mixed $expectedResult, mixed ...$args): void
public function testBITORInWorksheet(float|int|string $expectedResult, mixed ...$args): void
{
$arguments = new FormulaArguments(...$args);
@@ -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 @@ class BitXorTest extends TestCase
/**
* @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 @@ class BitXorTest extends TestCase
/**
* @dataProvider providerBITXOR
*/
public function testBITXORInWorksheet(mixed $expectedResult, mixed ...$args): void
public function testBITXORInWorksheet(float|int|string $expectedResult, mixed ...$args): void
{
$arguments = new FormulaArguments(...$args);
@@ -15,7 +15,7 @@ use PHPUnit\Framework\TestCase;
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;
@@ -4,6 +4,7 @@ declare(strict_types=1);
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 @@ class Issue3833Test extends AbstractFunctional
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();
}
@@ -170,7 +170,7 @@ class DateTest extends TestCase
/**
* @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.');