mirror of
https://github.com/PHPOffice/PhpSpreadsheet.git
synced 2026-09-04 22:50:27 +00:00
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:
@@ -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);
|
||||
|
||||
@@ -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
|
||||
];
|
||||
|
||||
/**
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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.');
|
||||
|
||||
Reference in New Issue
Block a user