Prep Work for Phpstan Upgrade (#2728)

Dependabot submitted PRs #2719 and #2720 to upgrade Phpstan. As with most Phpstan upgrades, there are new error messages; this PR is an attempt to fix all 58 of the new problems in order to allow the upgrade to proceed.

Most of these fixes involve the addition of doc-block type annotations, often involving the assignment of the 'objectionable' portion of the statement to a new variable. Some use explicit casting when I am sure that's safe. Some (Reader/Ods) involve defeating result caching by Phpstan.
This commit is contained in:
oleibman 2022-04-10 08:14:05 -07:00 committed by GitHub
parent 28bb2cd7c7
commit 64e61d8dec
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 143 additions and 73 deletions

View File

@ -340,11 +340,6 @@ parameters:
count: 1
path: src/PhpSpreadsheet/Calculation/Engineering/BesselJ.php
-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\Engineering\\\\BesselK\\:\\:besselK2\\(\\) has no return type specified\\.$#"
count: 1
path: src/PhpSpreadsheet/Calculation/Engineering/BesselK.php
-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Calculation\\\\Engineering\\\\ConvertBase\\:\\:validatePlaces\\(\\) has parameter \\$places with no type specified\\.$#"
count: 1
@ -2070,11 +2065,6 @@ parameters:
count: 1
path: src/PhpSpreadsheet/Reader/Ods.php
-
message: "#^If condition is always true\\.$#"
count: 1
path: src/PhpSpreadsheet/Reader/Ods.php
-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Reader\\\\Ods\\:\\:listWorksheetNames\\(\\) should return array\\<string\\> but returns array\\<int, string\\|null\\>\\.$#"
count: 1
@ -2095,11 +2085,6 @@ parameters:
count: 1
path: src/PhpSpreadsheet/Reader/Ods.php
-
message: "#^While loop condition is always true\\.$#"
count: 2
path: src/PhpSpreadsheet/Reader/Ods.php
-
message: "#^Cannot call method getElementsByTagNameNS\\(\\) on DOMElement\\|null\\.$#"
count: 3
@ -3880,11 +3865,6 @@ parameters:
count: 1
path: src/PhpSpreadsheet/Shared/Trend/Trend.php
-
message: "#^Parameter \\#1 \\$order of class PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\Trend\\\\PolynomialBestFit constructor expects int, string given\\.$#"
count: 2
path: src/PhpSpreadsheet/Shared/Trend/Trend.php
-
message: "#^Method PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\XMLWriter\\:\\:getData\\(\\) should return string but returns string\\|false\\.$#"
count: 1

View File

@ -68,13 +68,28 @@ class BesselK
return self::besselK2($x, $ord);
}
/**
* Mollify Phpstan.
*
* @codeCoverageIgnore
*/
private static function callBesselI(float $x, int $ord): float
{
$rslt = BesselI::BESSELI($x, $ord);
if (!is_float($rslt)) {
throw new Exception('Unexpected array or string');
}
return $rslt;
}
private static function besselK0(float $x): float
{
if ($x <= 2) {
$fNum2 = $x * 0.5;
$y = ($fNum2 * $fNum2);
return -log($fNum2) * BesselI::BESSELI($x, 0) +
return -log($fNum2) * self::callBesselI($x, 0) +
(-0.57721566 + $y * (0.42278420 + $y * (0.23069756 + $y * (0.3488590e-1 + $y * (0.262698e-2 + $y *
(0.10750e-3 + $y * 0.74e-5))))));
}
@ -92,7 +107,7 @@ class BesselK
$fNum2 = $x * 0.5;
$y = ($fNum2 * $fNum2);
return log($fNum2) * BesselI::BESSELI($x, 1) +
return log($fNum2) * self::callBesselI($x, 1) +
(1 + $y * (0.15443144 + $y * (-0.67278579 + $y * (-0.18156897 + $y * (-0.1919402e-1 + $y *
(-0.110404e-2 + $y * (-0.4686e-4))))))) / $x;
}
@ -104,7 +119,7 @@ class BesselK
(0.325614e-2 + $y * (-0.68245e-3)))))));
}
private static function besselK2(float $x, int $ord)
private static function besselK2(float $x, int $ord): float
{
$fTox = 2 / $x;
$fBkm = self::besselK0($x);

View File

@ -66,6 +66,21 @@ class BesselY
return self::besselY2($x, $ord);
}
/**
* Mollify Phpstan.
*
* @codeCoverageIgnore
*/
private static function callBesselJ(float $x, int $ord): float
{
$rslt = BesselJ::BESSELJ($x, $ord);
if (!is_float($rslt)) {
throw new Exception('Unexpected array or string');
}
return $rslt;
}
private static function besselY0(float $x): float
{
if ($x < 8.0) {
@ -75,7 +90,7 @@ class BesselY
$ans2 = 40076544269.0 + $y * (745249964.8 + $y * (7189466.438 + $y *
(47447.26470 + $y * (226.1030244 + $y))));
return $ans1 / $ans2 + 0.636619772 * BesselJ::BESSELJ($x, 0) * log($x);
return $ans1 / $ans2 + 0.636619772 * self::callBesselJ($x, 0) * log($x);
}
$z = 8.0 / $x;
@ -97,7 +112,7 @@ class BesselY
$ans2 = 0.2499580570e14 + $y * (0.4244419664e12 + $y * (0.3733650367e10 + $y * (0.2245904002e8 + $y *
(0.1020426050e6 + $y * (0.3549632885e3 + $y)))));
return ($ans1 / $ans2) + 0.636619772 * (BesselJ::BESSELJ($x, 1) * log($x) - 1 / $x);
return ($ans1 / $ans2) + 0.636619772 * (self::callBesselJ($x, 1) * log($x) - 1 / $x);
}
$z = 8.0 / $x;

View File

@ -70,10 +70,12 @@ class Amortization
return $e->getMessage();
}
$yearFrac = DateTimeExcel\YearFrac::fraction($purchased, $firstPeriod, $basis);
if (is_string($yearFrac)) {
return $yearFrac;
$yearFracx = DateTimeExcel\YearFrac::fraction($purchased, $firstPeriod, $basis);
if (is_string($yearFracx)) {
return $yearFracx;
}
/** @var float */
$yearFrac = $yearFracx;
$amortiseCoeff = self::getAmortizationCoefficient($rate);
@ -161,10 +163,12 @@ class Amortization
$fCostDelta = $cost - $salvage;
// Note, quirky variation for leap years on the YEARFRAC for this function
$purchasedYear = DateTimeExcel\DateParts::year($purchased);
$yearFrac = DateTimeExcel\YearFrac::fraction($purchased, $firstPeriod, $basis);
if (is_string($yearFrac)) {
return $yearFrac;
$yearFracx = DateTimeExcel\YearFrac::fraction($purchased, $firstPeriod, $basis);
if (is_string($yearFracx)) {
return $yearFracx;
}
/** @var float */
$yearFrac = $yearFracx;
if (
($basis == FinancialConstants::BASIS_DAYS_PER_YEAR_ACTUAL) &&

View File

@ -199,6 +199,7 @@ class Coupons
return $e->getMessage();
}
/** @var int */
$daysPerYear = Helpers::daysPerYear(DateTimeExcel\DateParts::year($settlement), $basis);
$next = self::couponFirstPeriodDate($settlement, $maturity, $frequency, self::PERIOD_DATE_NEXT);

View File

@ -44,7 +44,9 @@ class Confidence
if (($alpha <= 0) || ($alpha >= 1) || ($stdDev <= 0) || ($size < 1)) {
return ExcelError::NAN();
}
/** @var float */
$temp = Distributions\StandardNormal::inverse(1 - $alpha / 2);
return Functions::scalar(Distributions\StandardNormal::inverse(1 - $alpha / 2) * $stdDev / sqrt($size));
return Functions::scalar($temp * $stdDev / sqrt($size));
}
}

View File

@ -56,8 +56,10 @@ class Binomial
if ($cumulative) {
return self::calculateCumulativeBinomial($value, $trials, $probability);
}
/** @var float */
$comb = Combinations::withoutRepetition($trials, $value);
return Combinations::withoutRepetition($trials, $value) * $probability ** $value
return $comb * $probability ** $value
* (1 - $probability) ** ($trials - $value);
}
@ -107,7 +109,9 @@ class Binomial
$summer = 0;
for ($i = $successes; $i <= $limit; ++$i) {
$summer += Combinations::withoutRepetition($trials, $i) * $probability ** $i
/** @var float */
$comb = Combinations::withoutRepetition($trials, $i);
$summer += $comb * $probability ** $i
* (1 - $probability) ** ($trials - $i);
}
@ -159,8 +163,10 @@ class Binomial
return ExcelError::NAN();
}
}
/** @var float */
$comb = Combinations::withoutRepetition($failures + $successes - 1, $successes - 1);
return (Combinations::withoutRepetition($failures + $successes - 1, $successes - 1))
return $comb
* ($probability ** $successes) * ((1 - $probability) ** $failures);
}
@ -220,7 +226,9 @@ class Binomial
{
$summer = 0;
for ($i = 0; $i <= $value; ++$i) {
$summer += Combinations::withoutRepetition($trials, $i) * $probability ** $i
/** @var float */
$comb = Combinations::withoutRepetition($trials, $i);
$summer += $comb * $probability ** $i
* (1 - $probability) ** ($trials - $i);
}

View File

@ -281,6 +281,7 @@ class ChiSquared
// Relative error controlled by the eps parameter
private static function gser($n, $x)
{
/** @var float */
$gln = Gamma::ln($n / 2);
$a = 0.5 * $n;
$ap = $a;
@ -304,6 +305,7 @@ class ChiSquared
// Relative error controlled by the eps parameter
private static function gcf($n, $x)
{
/** @var float */
$gln = Gamma::ln($n / 2);
$a = 0.5 * $n;
$b = $x + 1 - $a;

View File

@ -131,7 +131,9 @@ class LogNormal
if ($stdDev <= 0) {
return ExcelError::NAN();
}
/** @var float */
$inverse = StandardNormal::inverse($probability);
return exp($mean + $stdDev * StandardNormal::inverse($probability));
return exp($mean + $stdDev * $inverse);
}
}

View File

@ -51,12 +51,16 @@ class Poisson
$summer = 0;
$floor = floor($value);
for ($i = 0; $i <= $floor; ++$i) {
$summer += $mean ** $i / MathTrig\Factorial::fact($i);
/** @var float */
$fact = MathTrig\Factorial::fact($i);
$summer += $mean ** $i / $fact;
}
return exp(0 - $mean) * $summer;
}
/** @var float */
$fact = MathTrig\Factorial::fact($value);
return (exp(0 - $mean) * $mean ** $value) / MathTrig\Factorial::fact($value);
return (exp(0 - $mean) * $mean ** $value) / $fact;
}
}

View File

@ -103,8 +103,10 @@ class StandardNormal
if (!is_numeric($value)) {
return ExcelError::VALUE();
}
/** @var float */
$dist = self::distribution($value, true);
return self::distribution($value, true) - 0.5;
return $dist - 0.5;
}
/**
@ -139,6 +141,7 @@ class StandardNormal
}
if ($sigma === null) {
/** @var float */
$sigma = StandardDeviations::STDEV($dataSet);
}
$n = count($dataSet);

View File

@ -31,7 +31,7 @@ class Size
$mArgs = self::filter($aArgs);
$count = Counts::COUNT($mArgs);
--$entry;
if (($entry < 0) || ($entry >= $count) || ($count == 0)) {
if ($count === 0 || $entry < 0 || $entry >= $count) {
return ExcelError::NAN();
}
rsort($mArgs);
@ -67,7 +67,7 @@ class Size
$mArgs = self::filter($aArgs);
$count = Counts::COUNT($mArgs);
--$entry;
if (($entry < 0) || ($entry >= $count) || ($count == 0)) {
if ($count === 0 || $entry < 0 || $entry >= $count) {
return ExcelError::NAN();
}
sort($mArgs);

View File

@ -179,6 +179,8 @@ class AdvancedValueBinder extends DefaultValueBinder implements IValueBinder
{
// Convert value to number
[$hours, $minutes] = explode(':', $value);
$hours = (int) $hours;
$minutes = (int) $minutes;
$days = ($hours / 24) + ($minutes / 1440);
$cell->setValueExplicit($days, DataType::TYPE_NUMERIC);
@ -193,6 +195,9 @@ class AdvancedValueBinder extends DefaultValueBinder implements IValueBinder
{
// Convert value to number
[$hours, $minutes, $seconds] = explode(':', $value);
$hours = (int) $hours;
$minutes = (int) $minutes;
$seconds = (int) $seconds;
$days = ($hours / 24) + ($minutes / 1440) + ($seconds / 86400);
$cell->setValueExplicit($days, DataType::TYPE_NUMERIC);

View File

@ -105,7 +105,7 @@ class Ods extends BaseReader
$xml->read();
while ($xml->read()) {
// Quickly jump through to the office:body node
while ($xml->name !== 'office:body') {
while (self::getXmlName($xml) !== 'office:body') {
if ($xml->isEmptyElement) {
$xml->read();
} else {
@ -114,12 +114,13 @@ class Ods extends BaseReader
}
// Now read each node until we find our first table:table node
while ($xml->read()) {
if ($xml->name == 'table:table' && $xml->nodeType == XMLReader::ELEMENT) {
$xmlName = self::getXmlName($xml);
if ($xmlName == 'table:table' && $xml->nodeType == XMLReader::ELEMENT) {
// Loop through each table:table node reading the table:name attribute for each worksheet name
do {
$worksheetNames[] = $xml->getAttribute('table:name');
$xml->next();
} while ($xml->name == 'table:table' && $xml->nodeType == XMLReader::ELEMENT);
} while (self::getXmlName($xml) == 'table:table' && $xml->nodeType == XMLReader::ELEMENT);
}
}
}
@ -152,7 +153,7 @@ class Ods extends BaseReader
$xml->read();
while ($xml->read()) {
// Quickly jump through to the office:body node
while ($xml->name !== 'office:body') {
while (self::getXmlName($xml) !== 'office:body') {
if ($xml->isEmptyElement) {
$xml->read();
} else {
@ -161,7 +162,7 @@ class Ods extends BaseReader
}
// Now read each node until we find our first table:table node
while ($xml->read()) {
if ($xml->name == 'table:table' && $xml->nodeType == XMLReader::ELEMENT) {
if (self::getXmlName($xml) == 'table:table' && $xml->nodeType == XMLReader::ELEMENT) {
$worksheetNames[] = $xml->getAttribute('table:name');
$tmpInfo = [
@ -176,7 +177,7 @@ class Ods extends BaseReader
$currCells = 0;
do {
$xml->read();
if ($xml->name == 'table:table-row' && $xml->nodeType == XMLReader::ELEMENT) {
if (self::getXmlName($xml) == 'table:table-row' && $xml->nodeType == XMLReader::ELEMENT) {
$rowspan = $xml->getAttribute('table:number-rows-repeated');
$rowspan = empty($rowspan) ? 1 : $rowspan;
$tmpInfo['totalRows'] += $rowspan;
@ -186,22 +187,22 @@ class Ods extends BaseReader
$xml->read();
do {
$doread = true;
if ($xml->name == 'table:table-cell' && $xml->nodeType == XMLReader::ELEMENT) {
if (self::getXmlName($xml) == 'table:table-cell' && $xml->nodeType == XMLReader::ELEMENT) {
if (!$xml->isEmptyElement) {
++$currCells;
$xml->next();
$doread = false;
}
} elseif ($xml->name == 'table:covered-table-cell' && $xml->nodeType == XMLReader::ELEMENT) {
} elseif (self::getXmlName($xml) == 'table:covered-table-cell' && $xml->nodeType == XMLReader::ELEMENT) {
$mergeSize = $xml->getAttribute('table:number-columns-repeated');
$currCells += (int) $mergeSize;
}
if ($doread) {
$xml->read();
}
} while ($xml->name != 'table:table-row');
} while (self::getXmlName($xml) != 'table:table-row');
}
} while ($xml->name != 'table:table');
} while (self::getXmlName($xml) != 'table:table');
$tmpInfo['totalColumns'] = max($tmpInfo['totalColumns'], $currCells);
$tmpInfo['lastColumnIndex'] = $tmpInfo['totalColumns'] - 1;
@ -214,6 +215,16 @@ class Ods extends BaseReader
return $worksheetInfo;
}
/**
* Counteract Phpstan caching.
*
* @phpstan-impure
*/
private static function getXmlName(XMLReader $xml): string
{
return $xml->name;
}
/**
* Loads PhpSpreadsheet from file.
*/
@ -681,12 +692,7 @@ class Ods extends BaseReader
// Multiple spaces?
/** @var DOMAttr $cAttr */
$cAttr = $child->attributes->getNamedItem('c');
if ($cAttr) {
$multiplier = (int) $cAttr->nodeValue;
} else {
$multiplier = 1;
}
$multiplier = self::getMultiplier($cAttr);
$str .= str_repeat(' ', $multiplier);
}
@ -698,6 +704,17 @@ class Ods extends BaseReader
return $str;
}
private static function getMultiplier(?DOMAttr $cAttr): int
{
if ($cAttr) {
$multiplier = (int) $cAttr->nodeValue;
} else {
$multiplier = 1;
}
return $multiplier;
}
/**
* @param string $is
*

View File

@ -894,7 +894,7 @@ class Xlsx extends BaseReader
}
foreach ($xmlSheet->extLst->ext->children('x14', true)->dataValidations->dataValidation as $item) {
$node = $xmlSheet->dataValidations->addChild('dataValidation');
$node = self::testSimpleXml($xmlSheet->dataValidations)->addChild('dataValidation');
foreach ($item->attributes() ?? [] as $attr) {
$node->addAttribute($attr->getName(), $attr);
}

View File

@ -83,7 +83,7 @@ class Trend
case self::TREND_POLYNOMIAL_5:
case self::TREND_POLYNOMIAL_6:
if (!isset(self::$trendCache[$key])) {
$order = substr($trendType, -1);
$order = (int) substr($trendType, -1);
self::$trendCache[$key] = new PolynomialBestFit($order, $yValues, $xValues);
}
@ -101,7 +101,7 @@ class Trend
}
if ($trendType != self::TREND_BEST_FIT_NO_POLY) {
foreach (self::$trendTypePolynomialOrders as $trendMethod) {
$order = substr($trendMethod, -1);
$order = (int) substr($trendMethod, -1);
$bestFit[$trendMethod] = new PolynomialBestFit($order, $yValues, $xValues);
if ($bestFit[$trendMethod]->getError()) {
unset($bestFit[$trendMethod]);

View File

@ -293,7 +293,10 @@ class Color extends Supervisor
*/
private static function getColourComponent($rgbValue, $offset, $hex = true)
{
$colour = substr($rgbValue, $offset, 2);
$colour = substr($rgbValue, $offset, 2) ?: '';
if (preg_match('/^[0-9a-f]{2}$/i', $colour) !== 1) {
$colour = '00';
}
return ($hex) ? $colour : (int) hexdec($colour);
}

View File

@ -26,9 +26,12 @@ class FractionFormatter extends BaseFormatter
$decimalLength = strlen($decimalPart);
$decimalDivisor = 10 ** $decimalLength;
/** @var float */
$GCD = MathTrig\Gcd::evaluate($decimalPart, $decimalDivisor);
/** @var float */
$decimalPartx = $decimalPart;
$adjustedDecimalPart = $decimalPart / $GCD;
$adjustedDecimalPart = $decimalPartx / $GCD;
$adjustedDecimalDivisor = $decimalDivisor / $GCD;
if ((strpos($format, '0') !== false)) {

View File

@ -54,6 +54,12 @@ use PhpOffice\PhpSpreadsheet\Writer\Exception as WriterException;
// */
class Worksheet extends BIFFwriter
{
/** @var int */
private static $always0 = 0;
/** @var int */
private static $always1 = 1;
/**
* Formula parser.
*
@ -2923,11 +2929,11 @@ class Worksheet extends BIFFwriter
$flags |= (1 == $bAlignWrapTx ? 0x00000004 : 0);
$flags |= (1 == $bTxRotation ? 0x00000008 : 0);
// Justify last line flag
$flags |= (1 == 1 ? 0x00000010 : 0);
$flags |= (1 == self::$always1 ? 0x00000010 : 0);
$flags |= (1 == $bIndent ? 0x00000020 : 0);
$flags |= (1 == $bShrinkToFit ? 0x00000040 : 0);
// Default
$flags |= (1 == 1 ? 0x00000080 : 0);
$flags |= (1 == self::$always1 ? 0x00000080 : 0);
// Protection
$flags |= (1 == $bProtLocked ? 0x00000100 : 0);
$flags |= (1 == $bProtHidden ? 0x00000200 : 0);
@ -2936,13 +2942,13 @@ class Worksheet extends BIFFwriter
$flags |= (1 == $bBorderRight ? 0x00000800 : 0);
$flags |= (1 == $bBorderTop ? 0x00001000 : 0);
$flags |= (1 == $bBorderBottom ? 0x00002000 : 0);
$flags |= (1 == 1 ? 0x00004000 : 0); // Top left to Bottom right border
$flags |= (1 == 1 ? 0x00008000 : 0); // Bottom left to Top right border
$flags |= (1 == self::$always1 ? 0x00004000 : 0); // Top left to Bottom right border
$flags |= (1 == self::$always1 ? 0x00008000 : 0); // Bottom left to Top right border
// Pattern
$flags |= (1 == $bFillStyle ? 0x00010000 : 0);
$flags |= (1 == $bFillColor ? 0x00020000 : 0);
$flags |= (1 == $bFillColorBg ? 0x00040000 : 0);
$flags |= (1 == 1 ? 0x00380000 : 0);
$flags |= (1 == self::$always1 ? 0x00380000 : 0);
// Font
$flags |= (1 == $bFormatFont ? 0x04000000 : 0);
// Alignment:
@ -2954,7 +2960,7 @@ class Worksheet extends BIFFwriter
// Protection
$flags |= (1 == $bFormatProt ? 0x40000000 : 0);
// Text direction
$flags |= (1 == 0 ? 0x80000000 : 0);
$flags |= (1 == self::$always0 ? 0x80000000 : 0);
$dataBlockFont = null;
$dataBlockAlign = null;
@ -3040,10 +3046,10 @@ class Worksheet extends BIFFwriter
$optionsFlags = 0;
$optionsFlagsBold = ($conditional->getStyle()->getFont()->getBold() === null ? 1 : 0);
$optionsFlags |= (1 == $optionsFlagsBold ? 0x00000002 : 0);
$optionsFlags |= (1 == 1 ? 0x00000008 : 0);
$optionsFlags |= (1 == 1 ? 0x00000010 : 0);
$optionsFlags |= (1 == 0 ? 0x00000020 : 0);
$optionsFlags |= (1 == 1 ? 0x00000080 : 0);
$optionsFlags |= (1 == self::$always1 ? 0x00000008 : 0);
$optionsFlags |= (1 == self::$always1 ? 0x00000010 : 0);
$optionsFlags |= (1 == self::$always0 ? 0x00000020 : 0);
$optionsFlags |= (1 == self::$always1 ? 0x00000080 : 0);
$dataBlockFont .= pack('V', $optionsFlags);
// Escapement type
$dataBlockFont .= pack('V', $fontEscapement);