Browse Source

部署

master
wanghongjun 2 years ago
parent
commit
b7d4881b82
  1. 30
      vendor/phpoffice/phpspreadsheet/README.md
  2. 6182
      vendor/phpoffice/phpspreadsheet/phpstan-baseline.neon
  3. 51
      vendor/phpoffice/phpspreadsheet/phpstan-conditional.php
  4. 26
      vendor/phpoffice/phpspreadsheet/phpstan.neon.dist
  5. 129
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Time.php
  6. 132
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/TimeParts.php
  7. 77
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/TimeValue.php
  8. 278
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Week.php
  9. 201
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/WorkDay.php
  10. 132
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/YearFrac.php
  11. 108
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/CashFlow/Single.php
  12. 160
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/CashFlow/Variable/Periodic.php
  13. 283
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Securities/Price.php
  14. 137
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Securities/Rates.php
  15. 42
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Securities/SecurityValidations.php
  16. 153
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Securities/Yields.php
  17. 147
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/TreasuryBill.php
  18. 39
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Internal/WildcardMatch.php
  19. 209
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/RowColumnInformation.php
  20. 46
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Selection.php
  21. 105
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/VLookup.php
  22. 99
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Random.php
  23. 846
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Roman.php
  24. 218
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Round.php
  25. 53
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/SeriesSum.php
  26. 38
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Sign.php
  27. 64
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Sqrt.php
  28. 114
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Subtotal.php
  29. 115
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Sum.php
  30. 142
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/SumSquares.php
  31. 64
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Trig/Secant.php
  32. 116
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Trig/Sine.php
  33. 161
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Trig/Tangent.php
  34. 50
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Trunc.php
  35. 1820
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical.php
  36. 62
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/Poisson.php
  37. 141
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/StandardNormal.php
  38. 138
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/StudentT.php
  39. 57
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/Weibull.php
  40. 90
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Permutations.php
  41. 96
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Size.php
  42. 95
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/StandardDeviations.php
  43. 49
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Standardize.php
  44. 45
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/StatisticalValidations.php
  45. 429
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Trends.php
  46. 28
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/VarianceBase.php
  47. 185
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Variances.php
  48. 448
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData.php
  49. 113
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData/Replace.php
  50. 97
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData/Search.php
  51. 80
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData/Text.php
  52. 52
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData/Trim.php
  53. 149
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Token/Stack.php
  54. 27
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Web.php
  55. 75
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Web/Service.php
  56. BIN
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/Translations.xlsx
  57. 124
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/StringValueBinder.php
  58. 109
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/PlotArea.php
  59. 369
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Properties.php
  60. 20
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Renderer/PHP Charting Libraries.txt
  61. 90
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Title.php
  62. 537
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Document/Properties.php
  63. 152
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Document/Security.php
  64. 226
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Helper/Sample.php
  65. 52
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Helper/Size.php
  66. 164
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Gnumeric/Properties.php
  67. 278
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Gnumeric/Styles.php
  68. 129
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Ods/Properties.php
  69. 157
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Security/XmlScanner.php
  70. 596
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Slk.php
  71. 7928
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls.php
  72. 61
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/RC4.php
  73. 2107
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx.php
  74. 109
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/Properties.php
  75. 132
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/SheetViewOptions.php
  76. 156
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/SheetViews.php
  77. 423
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/Styles.php
  78. 93
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/Theme.php
  79. 536
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xml.php
  80. 157
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xml/Properties.php
  81. 83
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xml/Style.php
  82. 32
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xml/Style/StyleBase.php
  83. 1048
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/ReferenceHelper.php
  84. 168
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/RichText/RichText.php
  85. 65
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/RichText/Run.php
  86. 86
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/RichText/TextElement.php
  87. 214
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Settings.php
  88. 75
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Escher/DgContainer/SpgrContainer.php
  89. 369
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Escher/DgContainer/SpgrContainer/SpContainer.php
  90. 245
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/JAMA/QRDecomposition.php
  91. 529
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/JAMA/SingularValueDecomposition.php
  92. 237
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/OLE/PPS.php
  93. 426
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/OLE/PPS/Root.php
  94. 722
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/StringHelper.php
  95. 77
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/TimeZone.php
  96. 202
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Trend/PolynomialBestFit.php
  97. 109
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Trend/PowerBestFit.php
  98. 122
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Trend/Trend.php
  99. 92
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/XMLWriter.php
  100. 277
      vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Xls.php

30
vendor/phpoffice/phpspreadsheet/README.md

@ -0,0 +1,30 @@
# PhpSpreadsheet
[![Build Status](https://github.com/PHPOffice/PhpSpreadsheet/workflows/main/badge.svg)](https://github.com/PHPOffice/PhpSpreadsheet/actions)
[![Code Quality](https://scrutinizer-ci.com/g/PHPOffice/PhpSpreadsheet/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/PHPOffice/PhpSpreadsheet/?branch=master)
[![Code Coverage](https://scrutinizer-ci.com/g/PHPOffice/PhpSpreadsheet/badges/coverage.png?b=master)](https://scrutinizer-ci.com/g/PHPOffice/PhpSpreadsheet/?branch=master)
[![Total Downloads](https://img.shields.io/packagist/dt/PHPOffice/PhpSpreadsheet)](https://packagist.org/packages/phpoffice/phpspreadsheet)
[![Latest Stable Version](https://img.shields.io/github/v/release/PHPOffice/PhpSpreadsheet)](https://packagist.org/packages/phpoffice/phpspreadsheet)
[![License](https://img.shields.io/github/license/PHPOffice/PhpSpreadsheet)](https://packagist.org/packages/phpoffice/phpspreadsheet)
[![Join the chat at https://gitter.im/PHPOffice/PhpSpreadsheet](https://img.shields.io/badge/GITTER-join%20chat-green.svg)](https://gitter.im/PHPOffice/PhpSpreadsheet)
PhpSpreadsheet is a library written in pure PHP and offers a set of classes that
allow you to read and write various spreadsheet file formats such as Excel and LibreOffice Calc.
## Documentation
Read more about it, including install instructions, in the [official documentation](https://phpspreadsheet.readthedocs.io). Or check out the [API documentation](https://phpoffice.github.io/PhpSpreadsheet).
Please ask your support questions on [StackOverflow](https://stackoverflow.com/questions/tagged/phpspreadsheet), or have a quick chat on [Gitter](https://gitter.im/PHPOffice/PhpSpreadsheet).
## PHPExcel vs PhpSpreadsheet ?
PhpSpreadsheet is the next version of PHPExcel. It breaks compatibility to dramatically improve the code base quality (namespaces, PSR compliance, use of latest PHP language features, etc.).
Because all efforts have shifted to PhpSpreadsheet, PHPExcel will no longer be maintained. All contributions for PHPExcel, patches and new features, should target PhpSpreadsheet `master` branch.
Do you need to migrate? There is [an automated tool](/docs/topics/migration-from-PHPExcel.md) for that.
## License
PhpSpreadsheet is licensed under [MIT](https://github.com/PHPOffice/PhpSpreadsheet/blob/master/LICENSE).

6182
vendor/phpoffice/phpspreadsheet/phpstan-baseline.neon

File diff suppressed because it is too large

51
vendor/phpoffice/phpspreadsheet/phpstan-conditional.php

@ -0,0 +1,51 @@
<?php
$config = [];
if (PHP_VERSION_ID < 80000) {
// GdImage not available before PHP8
$config['parameters']['ignoreErrors'][] = [
'message' => '~^Method .* has invalid return type GdImage\.$~',
'path' => __DIR__ . '/src/PhpSpreadsheet/Shared/Drawing.php',
'count' => 1,
];
$config['parameters']['ignoreErrors'][] = [
'message' => '~^Property .* has unknown class GdImage as its type\.$~',
'path' => __DIR__ . '/src/PhpSpreadsheet/Worksheet/MemoryDrawing.php',
'count' => 1,
];
$config['parameters']['ignoreErrors'][] = [
'message' => '~^Method .* has invalid return type GdImage\.$~',
'path' => __DIR__ . '/src/PhpSpreadsheet/Worksheet/MemoryDrawing.php',
'count' => 1,
];
$config['parameters']['ignoreErrors'][] = [
'message' => '~^Parameter .* of method .* has invalid type GdImage\.$~',
'path' => __DIR__ . '/src/PhpSpreadsheet/Worksheet/MemoryDrawing.php',
'count' => 1,
];
$config['parameters']['ignoreErrors'][] = [
'message' => '~^Class GdImage not found\.$~',
'path' => __DIR__ . '/src/PhpSpreadsheet/Writer/Xls/Worksheet.php',
'count' => 1,
];
$config['parameters']['ignoreErrors'][] = [
'message' => '~^Parameter .* of method .* has invalid type GdImage\.$~',
'path' => __DIR__ . '/src/PhpSpreadsheet/Writer/Xls/Worksheet.php',
'count' => 1,
];
// Erroneous analysis by Phpstan before PHP8 - 3rd parameter is nullable
$config['parameters']['ignoreErrors'][] = [
'message' => '#^Parameter \\#3 \\$namespace of method XMLWriter\\:\\:startElementNs\\(\\) expects string, null given\\.$#',
'path' => __DIR__ . '/src/PhpSpreadsheet/Writer/Xlsx/Worksheet.php',
'count' => 8,
];
// Erroneous analysis by Phpstan before PHP8 - mb_strlen does not return false
$config['parameters']['ignoreErrors'][] = [
'message' => '#^Method PhpOffice\\\\PhpSpreadsheet\\\\Shared\\\\StringHelper\\:\\:countCharacters\\(\\) should return int but returns int(<0, max>)?\\|false\\.$#',
'path' => __DIR__ . '/src/PhpSpreadsheet/Shared/StringHelper.php',
'count' => 1,
];
}
return $config;

26
vendor/phpoffice/phpspreadsheet/phpstan.neon.dist

@ -0,0 +1,26 @@
includes:
- phpstan-baseline.neon
- phpstan-conditional.php
- vendor/phpstan/phpstan-phpunit/extension.neon
- vendor/phpstan/phpstan-phpunit/rules.neon
parameters:
level: 8
paths:
- src/
- tests/
parallel:
processTimeout: 300.0
checkMissingIterableValueType: false
ignoreErrors:
- '~^Parameter \#1 \$im(age)? of function (imagedestroy|imageistruecolor|imagealphablending|imagesavealpha|imagecolortransparent|imagecolorsforindex|imagesavealpha|imagesx|imagesy) expects (GdImage|resource), GdImage\|resource given\.$~'
- '~^Parameter \#2 \$src_im(age)? of function imagecopy expects (GdImage|resource), GdImage\|resource given\.$~'
# Accept a bit anything for assert methods
- '~^Parameter \#2 .* of static method PHPUnit\\Framework\\Assert\:\:assert\w+\(\) expects .*, .* given\.$~'
- '~^Method PhpOffice\\PhpSpreadsheetTests\\.*\:\:test.*\(\) has parameter \$args with no type specified\.$~'
# Ignore all JpGraph issues
- '~^Constant (MARK_CIRCLE|MARK_CROSS|MARK_DIAMOND|MARK_DTRIANGLE|MARK_FILLEDCIRCLE|MARK_SQUARE|MARK_STAR|MARK_UTRIANGLE|MARK_X|SIDE_RIGHT) not found\.$~'
- '~^Instantiated class (AccBarPlot|AccLinePlot|BarPlot|ContourPlot|Graph|GroupBarPlot|GroupBarPlot|LinePlot|LinePlot|PieGraph|PiePlot|PiePlot3D|PiePlotC|RadarGraph|RadarPlot|ScatterPlot|Spline|StockPlot) not found\.$~'
- '~^Call to method .*\(\) on an unknown class (AccBarPlot|AccLinePlot|BarPlot|ContourPlot|Graph|GroupBarPlot|GroupBarPlot|LinePlot|LinePlot|PieGraph|PiePlot|PiePlot3D|PiePlotC|RadarGraph|RadarPlot|ScatterPlot|Spline|StockPlot)\.$~'
- '~^Access to property .* on an unknown class (AccBarPlot|AccLinePlot|BarPlot|ContourPlot|Graph|GroupBarPlot|GroupBarPlot|LinePlot|LinePlot|PieGraph|PiePlot|PiePlot3D|PiePlotC|RadarGraph|RadarPlot|ScatterPlot|Spline|StockPlot)\.$~'

129
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Time.php

@ -0,0 +1,129 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel;
use DateTime;
use PhpOffice\PhpSpreadsheet\Calculation\ArrayEnabled;
use PhpOffice\PhpSpreadsheet\Calculation\Exception;
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
use PhpOffice\PhpSpreadsheet\Shared\Date as SharedDateHelper;
class Time
{
use ArrayEnabled;
/**
* TIME.
*
* The TIME function returns a value that represents a particular time.
*
* NOTE: When used in a Cell Formula, MS Excel changes the cell format so that it matches the time
* format of your regional settings. PhpSpreadsheet does not change cell formatting in this way.
*
* Excel Function:
* TIME(hour,minute,second)
*
* @param array|int $hour A number from 0 (zero) to 32767 representing the hour.
* Any value greater than 23 will be divided by 24 and the remainder
* will be treated as the hour value. For example, TIME(27,0,0) =
* TIME(3,0,0) = .125 or 3:00 AM.
* @param array|int $minute A number from 0 to 32767 representing the minute.
* Any value greater than 59 will be converted to hours and minutes.
* For example, TIME(0,750,0) = TIME(12,30,0) = .520833 or 12:30 PM.
* @param array|int $second A number from 0 to 32767 representing the second.
* Any value greater than 59 will be converted to hours, minutes,
* and seconds. For example, TIME(0,0,2000) = TIME(0,33,22) = .023148
* or 12:33:20 AM
* If an array of numbers is passed as the argument, then the returned result will also be an array
* with the same dimensions
*
* @return array|mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object,
* depending on the value of the ReturnDateType flag
* If an array of numbers is passed as the argument, then the returned result will also be an array
* with the same dimensions
*/
public static function fromHMS($hour, $minute, $second)
{
if (is_array($hour) || is_array($minute) || is_array($second)) {
return self::evaluateArrayArguments([self::class, __FUNCTION__], $hour, $minute, $second);
}
try {
$hour = self::toIntWithNullBool($hour);
$minute = self::toIntWithNullBool($minute);
$second = self::toIntWithNullBool($second);
} catch (Exception $e) {
return $e->getMessage();
}
self::adjustSecond($second, $minute);
self::adjustMinute($minute, $hour);
if ($hour > 23) {
$hour = $hour % 24;
} elseif ($hour < 0) {
return Functions::NAN();
}
// Execute function
$retType = Functions::getReturnDateType();
if ($retType === Functions::RETURNDATE_EXCEL) {
$calendar = SharedDateHelper::getExcelCalendar();
$date = (int) ($calendar !== SharedDateHelper::CALENDAR_WINDOWS_1900);
return (float) SharedDateHelper::formattedPHPToExcel($calendar, 1, $date, $hour, $minute, $second);
}
if ($retType === Functions::RETURNDATE_UNIX_TIMESTAMP) {
return (int) SharedDateHelper::excelToTimestamp(SharedDateHelper::formattedPHPToExcel(1970, 1, 1, $hour, $minute, $second)); // -2147468400; // -2147472000 + 3600
}
// RETURNDATE_PHP_DATETIME_OBJECT
// Hour has already been normalized (0-23) above
$phpDateObject = new DateTime('1900-01-01 ' . $hour . ':' . $minute . ':' . $second);
return $phpDateObject;
}
private static function adjustSecond(int &$second, int &$minute): void
{
if ($second < 0) {
$minute += floor($second / 60);
$second = 60 - abs($second % 60);
if ($second == 60) {
$second = 0;
}
} elseif ($second >= 60) {
$minute += floor($second / 60);
$second = $second % 60;
}
}
private static function adjustMinute(int &$minute, int &$hour): void
{
if ($minute < 0) {
$hour += floor($minute / 60);
$minute = 60 - abs($minute % 60);
if ($minute == 60) {
$minute = 0;
}
} elseif ($minute >= 60) {
$hour += floor($minute / 60);
$minute = $minute % 60;
}
}
/**
* @param mixed $value expect int
*/
private static function toIntWithNullBool($value): int
{
$value = $value ?? 0;
if (is_bool($value)) {
$value = (int) $value;
}
if (!is_numeric($value)) {
throw new Exception(Functions::VALUE());
}
return (int) $value;
}
}

132
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/TimeParts.php

@ -0,0 +1,132 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel;
use PhpOffice\PhpSpreadsheet\Calculation\ArrayEnabled;
use PhpOffice\PhpSpreadsheet\Calculation\Exception;
use PhpOffice\PhpSpreadsheet\Shared\Date as SharedDateHelper;
class TimeParts
{
use ArrayEnabled;
/**
* HOUROFDAY.
*
* Returns the hour of a time value.
* The hour is given as an integer, ranging from 0 (12:00 A.M.) to 23 (11:00 P.M.).
*
* Excel Function:
* HOUR(timeValue)
*
* @param mixed $timeValue Excel date serial value (float), PHP date timestamp (integer),
* PHP DateTime object, or a standard time string
* Or can be an array of date/time values
*
* @return array|int|string Hour
* If an array of numbers is passed as the argument, then the returned result will also be an array
* with the same dimensions
*/
public static function hour($timeValue)
{
if (is_array($timeValue)) {
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $timeValue);
}
try {
Helpers::nullFalseTrueToNumber($timeValue);
if (!is_numeric($timeValue)) {
$timeValue = Helpers::getTimeValue($timeValue);
}
Helpers::validateNotNegative($timeValue);
} catch (Exception $e) {
return $e->getMessage();
}
// Execute function
$timeValue = fmod($timeValue, 1);
$timeValue = SharedDateHelper::excelToDateTimeObject($timeValue);
return (int) $timeValue->format('H');
}
/**
* MINUTE.
*
* Returns the minutes of a time value.
* The minute is given as an integer, ranging from 0 to 59.
*
* Excel Function:
* MINUTE(timeValue)
*
* @param mixed $timeValue Excel date serial value (float), PHP date timestamp (integer),
* PHP DateTime object, or a standard time string
* Or can be an array of date/time values
*
* @return array|int|string Minute
* If an array of numbers is passed as the argument, then the returned result will also be an array
* with the same dimensions
*/
public static function minute($timeValue)
{
if (is_array($timeValue)) {
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $timeValue);
}
try {
Helpers::nullFalseTrueToNumber($timeValue);
if (!is_numeric($timeValue)) {
$timeValue = Helpers::getTimeValue($timeValue);
}
Helpers::validateNotNegative($timeValue);
} catch (Exception $e) {
return $e->getMessage();
}
// Execute function
$timeValue = fmod($timeValue, 1);
$timeValue = SharedDateHelper::excelToDateTimeObject($timeValue);
return (int) $timeValue->format('i');
}
/**
* SECOND.
*
* Returns the seconds of a time value.
* The minute is given as an integer, ranging from 0 to 59.
*
* Excel Function:
* SECOND(timeValue)
*
* @param mixed $timeValue Excel date serial value (float), PHP date timestamp (integer),
* PHP DateTime object, or a standard time string
* Or can be an array of date/time values
*
* @return array|int|string Second
* If an array of numbers is passed as the argument, then the returned result will also be an array
* with the same dimensions
*/
public static function second($timeValue)
{
if (is_array($timeValue)) {
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $timeValue);
}
try {
Helpers::nullFalseTrueToNumber($timeValue);
if (!is_numeric($timeValue)) {
$timeValue = Helpers::getTimeValue($timeValue);
}
Helpers::validateNotNegative($timeValue);
} catch (Exception $e) {
return $e->getMessage();
}
// Execute function
$timeValue = fmod($timeValue, 1);
$timeValue = SharedDateHelper::excelToDateTimeObject($timeValue);
return (int) $timeValue->format('s');
}
}

77
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/TimeValue.php

@ -0,0 +1,77 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel;
use Datetime;
use PhpOffice\PhpSpreadsheet\Calculation\ArrayEnabled;
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
use PhpOffice\PhpSpreadsheet\Shared\Date as SharedDateHelper;
class TimeValue
{
use ArrayEnabled;
/**
* TIMEVALUE.
*
* Returns a value that represents a particular time.
* Use TIMEVALUE to convert a time represented by a text string to an Excel or PHP date/time stamp
* value.
*
* NOTE: When used in a Cell Formula, MS Excel changes the cell format so that it matches the time
* format of your regional settings. PhpSpreadsheet does not change cell formatting in this way.
*
* Excel Function:
* TIMEVALUE(timeValue)
*
* @param array|string $timeValue A text string that represents a time in any one of the Microsoft
* Excel time formats; for example, "6:45 PM" and "18:45" text strings
* within quotation marks that represent time.
* Date information in time_text is ignored.
* Or can be an array of date/time values
*
* @return mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object,
* depending on the value of the ReturnDateType flag
* If an array of numbers is passed as the argument, then the returned result will also be an array
* with the same dimensions
*/
public static function fromString($timeValue)
{
if (is_array($timeValue)) {
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $timeValue);
}
$timeValue = trim($timeValue ?? '', '"');
$timeValue = str_replace(['/', '.'], '-', $timeValue);
$arraySplit = preg_split('/[\/:\-\s]/', $timeValue) ?: [];
if ((count($arraySplit) == 2 || count($arraySplit) == 3) && $arraySplit[0] > 24) {
$arraySplit[0] = ($arraySplit[0] % 24);
$timeValue = implode(':', $arraySplit);
}
$PHPDateArray = Helpers::dateParse($timeValue);
$retValue = Functions::VALUE();
if (Helpers::dateParseSucceeded($PHPDateArray)) {
/** @var int */
$hour = $PHPDateArray['hour'];
/** @var int */
$minute = $PHPDateArray['minute'];
/** @var int */
$second = $PHPDateArray['second'];
// OpenOffice-specific code removed - it works just like Excel
$excelDateValue = SharedDateHelper::formattedPHPToExcel(1900, 1, 1, $hour, $minute, $second) - 1;
$retType = Functions::getReturnDateType();
if ($retType === Functions::RETURNDATE_EXCEL) {
$retValue = (float) $excelDateValue;
} elseif ($retType === Functions::RETURNDATE_UNIX_TIMESTAMP) {
$retValue = (int) $phpDateValue = SharedDateHelper::excelToTimestamp($excelDateValue + 25569) - 3600;
} else {
$retValue = new DateTime('1900-01-01 ' . $PHPDateArray['hour'] . ':' . $PHPDateArray['minute'] . ':' . $PHPDateArray['second']);
}
}
return $retValue;
}
}

278
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/Week.php

@ -0,0 +1,278 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel;
use DateTime;
use PhpOffice\PhpSpreadsheet\Calculation\ArrayEnabled;
use PhpOffice\PhpSpreadsheet\Calculation\Exception;
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
use PhpOffice\PhpSpreadsheet\Shared\Date as SharedDateHelper;
class Week
{
use ArrayEnabled;
/**
* WEEKNUM.
*
* Returns the week of the year for a specified date.
* The WEEKNUM function considers the week containing January 1 to be the first week of the year.
* However, there is a European standard that defines the first week as the one with the majority
* of days (four or more) falling in the new year. This means that for years in which there are
* three days or less in the first week of January, the WEEKNUM function returns week numbers
* that are incorrect according to the European standard.
*
* Excel Function:
* WEEKNUM(dateValue[,style])
*
* @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer),
* PHP DateTime object, or a standard date string
* Or can be an array of date values
* @param array|int $method Week begins on Sunday or Monday
* 1 or omitted Week begins on Sunday.
* 2 Week begins on Monday.
* 11 Week begins on Monday.
* 12 Week begins on Tuesday.
* 13 Week begins on Wednesday.
* 14 Week begins on Thursday.
* 15 Week begins on Friday.
* 16 Week begins on Saturday.
* 17 Week begins on Sunday.
* 21 ISO (Jan. 4 is week 1, begins on Monday).
* Or can be an array of methods
*
* @return array|int|string Week Number
* If an array of values is passed as the argument, then the returned result will also be an array
* with the same dimensions
*/
public static function number($dateValue, $method = Constants::STARTWEEK_SUNDAY)
{
if (is_array($dateValue) || is_array($method)) {
return self::evaluateArrayArguments([self::class, __FUNCTION__], $dateValue, $method);
}
$origDateValueNull = empty($dateValue);
try {
$method = self::validateMethod($method);
if ($dateValue === null) { // boolean not allowed
$dateValue = (SharedDateHelper::getExcelCalendar() === SharedDateHelper::CALENDAR_MAC_1904 || $method === Constants::DOW_SUNDAY) ? 0 : 1;
}
$dateValue = self::validateDateValue($dateValue);
if (!$dateValue && self::buggyWeekNum1900($method)) {
// This seems to be an additional Excel bug.
return 0;
}
} catch (Exception $e) {
return $e->getMessage();
}
// Execute function
$PHPDateObject = SharedDateHelper::excelToDateTimeObject($dateValue);
if ($method == Constants::STARTWEEK_MONDAY_ISO) {
Helpers::silly1900($PHPDateObject);
return (int) $PHPDateObject->format('W');
}
if (self::buggyWeekNum1904($method, $origDateValueNull, $PHPDateObject)) {
return 0;
}
Helpers::silly1900($PHPDateObject, '+ 5 years'); // 1905 calendar matches
$dayOfYear = (int) $PHPDateObject->format('z');
$PHPDateObject->modify('-' . $dayOfYear . ' days');
$firstDayOfFirstWeek = (int) $PHPDateObject->format('w');
$daysInFirstWeek = (6 - $firstDayOfFirstWeek + $method) % 7;
$daysInFirstWeek += 7 * !$daysInFirstWeek;
$endFirstWeek = $daysInFirstWeek - 1;
$weekOfYear = floor(($dayOfYear - $endFirstWeek + 13) / 7);
return (int) $weekOfYear;
}
/**
* ISOWEEKNUM.
*
* Returns the ISO 8601 week number of the year for a specified date.
*
* Excel Function:
* ISOWEEKNUM(dateValue)
*
* @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer),
* PHP DateTime object, or a standard date string
* Or can be an array of date values
*
* @return array|int|string Week Number
* If an array of numbers is passed as the argument, then the returned result will also be an array
* with the same dimensions
*/
public static function isoWeekNumber($dateValue)
{
if (is_array($dateValue)) {
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $dateValue);
}
if (self::apparentBug($dateValue)) {
return 52;
}
try {
$dateValue = Helpers::getDateValue($dateValue);
} catch (Exception $e) {
return $e->getMessage();
}
// Execute function
$PHPDateObject = SharedDateHelper::excelToDateTimeObject($dateValue);
Helpers::silly1900($PHPDateObject);
return (int) $PHPDateObject->format('W');
}
/**
* WEEKDAY.
*
* Returns the day of the week for a specified date. The day is given as an integer
* ranging from 0 to 7 (dependent on the requested style).
*
* Excel Function:
* WEEKDAY(dateValue[,style])
*
* @param null|array|float|int|string $dateValue Excel date serial value (float), PHP date timestamp (integer),
* PHP DateTime object, or a standard date string
* Or can be an array of date values
* @param mixed $style A number that determines the type of return value
* 1 or omitted Numbers 1 (Sunday) through 7 (Saturday).
* 2 Numbers 1 (Monday) through 7 (Sunday).
* 3 Numbers 0 (Monday) through 6 (Sunday).
* Or can be an array of styles
*
* @return array|int|string Day of the week value
* If an array of values is passed as the argument, then the returned result will also be an array
* with the same dimensions
*/
public static function day($dateValue, $style = 1)
{
if (is_array($dateValue) || is_array($style)) {
return self::evaluateArrayArguments([self::class, __FUNCTION__], $dateValue, $style);
}
try {
$dateValue = Helpers::getDateValue($dateValue);
$style = self::validateStyle($style);
} catch (Exception $e) {
return $e->getMessage();
}
// Execute function
$PHPDateObject = SharedDateHelper::excelToDateTimeObject($dateValue);
Helpers::silly1900($PHPDateObject);
$DoW = (int) $PHPDateObject->format('w');
switch ($style) {
case 1:
++$DoW;
break;
case 2:
$DoW = self::dow0Becomes7($DoW);
break;
case 3:
$DoW = self::dow0Becomes7($DoW) - 1;
break;
}
return $DoW;
}
/**
* @param mixed $style expect int
*/
private static function validateStyle($style): int
{
if (!is_numeric($style)) {
throw new Exception(Functions::VALUE());
}
$style = (int) $style;
if (($style < 1) || ($style > 3)) {
throw new Exception(Functions::NAN());
}
return $style;
}
private static function dow0Becomes7(int $DoW): int
{
return ($DoW === 0) ? 7 : $DoW;
}
/**
* @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer),
* PHP DateTime object, or a standard date string
*/
private static function apparentBug($dateValue): bool
{
if (SharedDateHelper::getExcelCalendar() !== SharedDateHelper::CALENDAR_MAC_1904) {
if (is_bool($dateValue)) {
return true;
}
if (is_numeric($dateValue) && !((int) $dateValue)) {
return true;
}
}
return false;
}
/**
* Validate dateValue parameter.
*
* @param mixed $dateValue
*/
private static function validateDateValue($dateValue): float
{
if (is_bool($dateValue)) {
throw new Exception(Functions::VALUE());
}
return Helpers::getDateValue($dateValue);
}
/**
* Validate method parameter.
*
* @param mixed $method
*/
private static function validateMethod($method): int
{
if ($method === null) {
$method = Constants::STARTWEEK_SUNDAY;
}
if (!is_numeric($method)) {
throw new Exception(Functions::VALUE());
}
$method = (int) $method;
if (!array_key_exists($method, Constants::METHODARR)) {
throw new Exception(Functions::NAN());
}
$method = Constants::METHODARR[$method];
return $method;
}
private static function buggyWeekNum1900(int $method): bool
{
return $method === Constants::DOW_SUNDAY && SharedDateHelper::getExcelCalendar() === SharedDateHelper::CALENDAR_WINDOWS_1900;
}
private static function buggyWeekNum1904(int $method, bool $origNull, DateTime $dateObject): bool
{
// This appears to be another Excel bug.
return $method === Constants::DOW_SUNDAY && SharedDateHelper::getExcelCalendar() === SharedDateHelper::CALENDAR_MAC_1904 &&
!$origNull && $dateObject->format('Y-m-d') === '1904-01-01';
}
}

201
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/WorkDay.php

@ -0,0 +1,201 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel;
use PhpOffice\PhpSpreadsheet\Calculation\ArrayEnabled;
use PhpOffice\PhpSpreadsheet\Calculation\Exception;
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
class WorkDay
{
use ArrayEnabled;
/**
* WORKDAY.
*
* Returns the date that is the indicated number of working days before or after a date (the
* starting date). Working days exclude weekends and any dates identified as holidays.
* Use WORKDAY to exclude weekends or holidays when you calculate invoice due dates, expected
* delivery times, or the number of days of work performed.
*
* Excel Function:
* WORKDAY(startDate,endDays[,holidays[,holiday[,...]]])
*
* @param array|mixed $startDate Excel date serial value (float), PHP date timestamp (integer),
* PHP DateTime object, or a standard date string
* Or can be an array of date values
* @param array|int $endDays The number of nonweekend and nonholiday days before or after
* startDate. A positive value for days yields a future date; a
* negative value yields a past date.
* Or can be an array of int values
* @param null|mixed $dateArgs An array of dates (such as holidays) to exclude from the calculation
*
* @return array|mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object,
* depending on the value of the ReturnDateType flag
* If an array of values is passed for the $startDate or $endDays,arguments, then the returned result
* will also be an array with matching dimensions
*/
public static function date($startDate, $endDays, ...$dateArgs)
{
if (is_array($startDate) || is_array($endDays)) {
return self::evaluateArrayArgumentsSubset(
[self::class, __FUNCTION__],
2,
$startDate,
$endDays,
...$dateArgs
);
}
// Retrieve the mandatory start date and days that are referenced in the function definition
try {
$startDate = Helpers::getDateValue($startDate);
$endDays = Helpers::validateNumericNull($endDays);
$holidayArray = array_map([Helpers::class, 'getDateValue'], Functions::flattenArray($dateArgs));
} catch (Exception $e) {
return $e->getMessage();
}
$startDate = (float) floor($startDate);
$endDays = (int) floor($endDays);
// If endDays is 0, we always return startDate
if ($endDays == 0) {
return $startDate;
}
if ($endDays < 0) {
return self::decrementing($startDate, $endDays, $holidayArray);
}
return self::incrementing($startDate, $endDays, $holidayArray);
}
/**
* Use incrementing logic to determine Workday.
*
* @return mixed
*/
private static function incrementing(float $startDate, int $endDays, array $holidayArray)
{
// Adjust the start date if it falls over a weekend
$startDoW = self::getWeekDay($startDate, 3);
if ($startDoW >= 5) {
$startDate += 7 - $startDoW;
--$endDays;
}
// Add endDays
$endDate = (float) $startDate + ((int) ($endDays / 5) * 7);
$endDays = $endDays % 5;
while ($endDays > 0) {
++$endDate;
// Adjust the calculated end date if it falls over a weekend
$endDow = self::getWeekDay($endDate, 3);
if ($endDow >= 5) {
$endDate += 7 - $endDow;
}
--$endDays;
}
// Test any extra holiday parameters
if (!empty($holidayArray)) {
$endDate = self::incrementingArray($startDate, $endDate, $holidayArray);
}
return Helpers::returnIn3FormatsFloat($endDate);
}
private static function incrementingArray(float $startDate, float $endDate, array $holidayArray): float
{
$holidayCountedArray = $holidayDates = [];
foreach ($holidayArray as $holidayDate) {
if (self::getWeekDay($holidayDate, 3) < 5) {
$holidayDates[] = $holidayDate;
}
}
sort($holidayDates, SORT_NUMERIC);
foreach ($holidayDates as $holidayDate) {
if (($holidayDate >= $startDate) && ($holidayDate <= $endDate)) {
if (!in_array($holidayDate, $holidayCountedArray)) {
++$endDate;
$holidayCountedArray[] = $holidayDate;
}
}
// Adjust the calculated end date if it falls over a weekend
$endDoW = self::getWeekDay($endDate, 3);
if ($endDoW >= 5) {
$endDate += 7 - $endDoW;
}
}
return $endDate;
}
/**
* Use decrementing logic to determine Workday.
*
* @return mixed
*/
private static function decrementing(float $startDate, int $endDays, array $holidayArray)
{
// Adjust the start date if it falls over a weekend
$startDoW = self::getWeekDay($startDate, 3);
if ($startDoW >= 5) {
$startDate += -$startDoW + 4;
++$endDays;
}
// Add endDays
$endDate = (float) $startDate + ((int) ($endDays / 5) * 7);
$endDays = $endDays % 5;
while ($endDays < 0) {
--$endDate;
// Adjust the calculated end date if it falls over a weekend
$endDow = self::getWeekDay($endDate, 3);
if ($endDow >= 5) {
$endDate += 4 - $endDow;
}
++$endDays;
}
// Test any extra holiday parameters
if (!empty($holidayArray)) {
$endDate = self::decrementingArray($startDate, $endDate, $holidayArray);
}
return Helpers::returnIn3FormatsFloat($endDate);
}
private static function decrementingArray(float $startDate, float $endDate, array $holidayArray): float
{
$holidayCountedArray = $holidayDates = [];
foreach ($holidayArray as $holidayDate) {
if (self::getWeekDay($holidayDate, 3) < 5) {
$holidayDates[] = $holidayDate;
}
}
rsort($holidayDates, SORT_NUMERIC);
foreach ($holidayDates as $holidayDate) {
if (($holidayDate <= $startDate) && ($holidayDate >= $endDate)) {
if (!in_array($holidayDate, $holidayCountedArray)) {
--$endDate;
$holidayCountedArray[] = $holidayDate;
}
}
// Adjust the calculated end date if it falls over a weekend
$endDoW = self::getWeekDay($endDate, 3);
/** int $endDoW */
if ($endDoW >= 5) {
$endDate += -$endDoW + 4;
}
}
return $endDate;
}
private static function getWeekDay(float $date, int $wd): int
{
$result = Functions::scalar(Week::day($date, $wd));
return is_int($result) ? $result : -1;
}
}

132
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/DateTimeExcel/YearFrac.php

@ -0,0 +1,132 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel;
use PhpOffice\PhpSpreadsheet\Calculation\ArrayEnabled;
use PhpOffice\PhpSpreadsheet\Calculation\Exception;
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
use PhpOffice\PhpSpreadsheet\Shared\Date as SharedDateHelper;
class YearFrac
{
use ArrayEnabled;
/**
* YEARFRAC.
*
* Calculates the fraction of the year represented by the number of whole days between two dates
* (the start_date and the end_date).
* Use the YEARFRAC worksheet function to identify the proportion of a whole year's benefits or
* obligations to assign to a specific term.
*
* Excel Function:
* YEARFRAC(startDate,endDate[,method])
* See https://lists.oasis-open.org/archives/office-formula/200806/msg00039.html
* for description of algorithm used in Excel
*
* @param mixed $startDate Excel date serial value (float), PHP date timestamp (integer),
* PHP DateTime object, or a standard date string
* Or can be an array of values
* @param mixed $endDate Excel date serial value (float), PHP date timestamp (integer),
* PHP DateTime object, or a standard date string
* Or can be an array of methods
* @param array|int $method Method used for the calculation
* 0 or omitted US (NASD) 30/360
* 1 Actual/actual
* 2 Actual/360
* 3 Actual/365
* 4 European 30/360
* Or can be an array of methods
*
* @return array|float|string fraction of the year, or a string containing an error
* If an array of values is passed for the $startDate or $endDays,arguments, then the returned result
* will also be an array with matching dimensions
*/
public static function fraction($startDate, $endDate, $method = 0)
{
if (is_array($startDate) || is_array($endDate) || is_array($method)) {
return self::evaluateArrayArguments([self::class, __FUNCTION__], $startDate, $endDate, $method);
}
try {
$method = (int) Helpers::validateNumericNull($method);
$sDate = Helpers::getDateValue($startDate);
$eDate = Helpers::getDateValue($endDate);
$sDate = self::excelBug($sDate, $startDate, $endDate, $method);
$eDate = self::excelBug($eDate, $endDate, $startDate, $method);
$startDate = min($sDate, $eDate);
$endDate = max($sDate, $eDate);
} catch (Exception $e) {
return $e->getMessage();
}
switch ($method) {
case 0:
return Functions::scalar(Days360::between($startDate, $endDate)) / 360;
case 1:
return self::method1($startDate, $endDate);
case 2:
return Functions::scalar(Difference::interval($startDate, $endDate)) / 360;
case 3:
return Functions::scalar(Difference::interval($startDate, $endDate)) / 365;
case 4:
return Functions::scalar(Days360::between($startDate, $endDate, true)) / 360;
}
return Functions::NAN();
}
/**
* Excel 1900 calendar treats date argument of null as 1900-01-00. Really.
*
* @param mixed $startDate
* @param mixed $endDate
*/
private static function excelBug(float $sDate, $startDate, $endDate, int $method): float
{
if (Functions::getCompatibilityMode() !== Functions::COMPATIBILITY_OPENOFFICE && SharedDateHelper::getExcelCalendar() !== SharedDateHelper::CALENDAR_MAC_1904) {
if ($endDate === null && $startDate !== null) {
if (DateParts::month($sDate) == 12 && DateParts::day($sDate) === 31 && $method === 0) {
$sDate += 2;
} else {
++$sDate;
}
}
}
return $sDate;
}
private static function method1(float $startDate, float $endDate): float
{
$days = Functions::scalar(Difference::interval($startDate, $endDate));
$startYear = (int) DateParts::year($startDate);
$endYear = (int) DateParts::year($endDate);
$years = $endYear - $startYear + 1;
$startMonth = (int) DateParts::month($startDate);
$startDay = (int) DateParts::day($startDate);
$endMonth = (int) DateParts::month($endDate);
$endDay = (int) DateParts::day($endDate);
$startMonthDay = 100 * $startMonth + $startDay;
$endMonthDay = 100 * $endMonth + $endDay;
if ($years == 1) {
$tmpCalcAnnualBasis = 365 + (int) Helpers::isLeapYear($endYear);
} elseif ($years == 2 && $startMonthDay >= $endMonthDay) {
if (Helpers::isLeapYear($startYear)) {
$tmpCalcAnnualBasis = 365 + (int) ($startMonthDay <= 229);
} elseif (Helpers::isLeapYear($endYear)) {
$tmpCalcAnnualBasis = 365 + (int) ($endMonthDay >= 229);
} else {
$tmpCalcAnnualBasis = 365;
}
} else {
$tmpCalcAnnualBasis = 0;
for ($year = $startYear; $year <= $endYear; ++$year) {
$tmpCalcAnnualBasis += 365 + (int) Helpers::isLeapYear($year);
}
$tmpCalcAnnualBasis /= $years;
}
return $days / $tmpCalcAnnualBasis;
}
}

108
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/CashFlow/Single.php

@ -0,0 +1,108 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Calculation\Financial\CashFlow;
use PhpOffice\PhpSpreadsheet\Calculation\Exception;
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
class Single
{
/**
* FVSCHEDULE.
*
* Returns the future value of an initial principal after applying a series of compound interest rates.
* Use FVSCHEDULE to calculate the future value of an investment with a variable or adjustable rate.
*
* Excel Function:
* FVSCHEDULE(principal,schedule)
*
* @param mixed $principal the present value
* @param float[] $schedule an array of interest rates to apply
*
* @return float|string
*/
public static function futureValue($principal, $schedule)
{
$principal = Functions::flattenSingleValue($principal);
$schedule = Functions::flattenArray($schedule);
try {
$principal = CashFlowValidations::validateFloat($principal);
foreach ($schedule as $rate) {
$rate = CashFlowValidations::validateFloat($rate);
$principal *= 1 + $rate;
}
} catch (Exception $e) {
return $e->getMessage();
}
return $principal;
}
/**
* PDURATION.
*
* Calculates the number of periods required for an investment to reach a specified value.
*
* @param mixed $rate Interest rate per period
* @param mixed $presentValue Present Value
* @param mixed $futureValue Future Value
*
* @return float|string Result, or a string containing an error
*/
public static function periods($rate, $presentValue, $futureValue)
{
$rate = Functions::flattenSingleValue($rate);
$presentValue = Functions::flattenSingleValue($presentValue);
$futureValue = Functions::flattenSingleValue($futureValue);
try {
$rate = CashFlowValidations::validateRate($rate);
$presentValue = CashFlowValidations::validatePresentValue($presentValue);
$futureValue = CashFlowValidations::validateFutureValue($futureValue);
} catch (Exception $e) {
return $e->getMessage();
}
// Validate parameters
if ($rate <= 0.0 || $presentValue <= 0.0 || $futureValue <= 0.0) {
return Functions::NAN();
}
return (log($futureValue) - log($presentValue)) / log(1 + $rate);
}
/**
* RRI.
*
* Calculates the interest rate required for an investment to grow to a specified future value .
*
* @param float $periods The number of periods over which the investment is made
* @param float $presentValue Present Value
* @param float $futureValue Future Value
*
* @return float|string Result, or a string containing an error
*/
public static function interestRate($periods = 0.0, $presentValue = 0.0, $futureValue = 0.0)
{
$periods = Functions::flattenSingleValue($periods);
$presentValue = Functions::flattenSingleValue($presentValue);
$futureValue = Functions::flattenSingleValue($futureValue);
try {
$periods = CashFlowValidations::validateFloat($periods);
$presentValue = CashFlowValidations::validatePresentValue($presentValue);
$futureValue = CashFlowValidations::validateFutureValue($futureValue);
} catch (Exception $e) {
return $e->getMessage();
}
// Validate parameters
if ($periods <= 0.0 || $presentValue <= 0.0 || $futureValue < 0.0) {
return Functions::NAN();
}
return ($futureValue / $presentValue) ** (1 / $periods) - 1;
}
}

160
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/CashFlow/Variable/Periodic.php

@ -0,0 +1,160 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Calculation\Financial\CashFlow\Variable;
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
class Periodic
{
const FINANCIAL_MAX_ITERATIONS = 128;
const FINANCIAL_PRECISION = 1.0e-08;
/**
* IRR.
*
* Returns the internal rate of return for a series of cash flows represented by the numbers in values.
* These cash flows do not have to be even, as they would be for an annuity. However, the cash flows must occur
* at regular intervals, such as monthly or annually. The internal rate of return is the interest rate received
* for an investment consisting of payments (negative values) and income (positive values) that occur at regular
* periods.
*
* Excel Function:
* IRR(values[,guess])
*
* @param mixed $values An array or a reference to cells that contain numbers for which you want
* to calculate the internal rate of return.
* Values must contain at least one positive value and one negative value to
* calculate the internal rate of return.
* @param mixed $guess A number that you guess is close to the result of IRR
*
* @return float|string
*/
public static function rate($values, $guess = 0.1)
{
if (!is_array($values)) {
return Functions::VALUE();
}
$values = Functions::flattenArray($values);
$guess = Functions::flattenSingleValue($guess);
// create an initial range, with a root somewhere between 0 and guess
$x1 = 0.0;
$x2 = $guess;
$f1 = self::presentValue($x1, $values);
$f2 = self::presentValue($x2, $values);
for ($i = 0; $i < self::FINANCIAL_MAX_ITERATIONS; ++$i) {
if (($f1 * $f2) < 0.0) {
break;
}
if (abs($f1) < abs($f2)) {
$f1 = self::presentValue($x1 += 1.6 * ($x1 - $x2), $values);
} else {
$f2 = self::presentValue($x2 += 1.6 * ($x2 - $x1), $values);
}
}
if (($f1 * $f2) > 0.0) {
return Functions::VALUE();
}
$f = self::presentValue($x1, $values);
if ($f < 0.0) {
$rtb = $x1;
$dx = $x2 - $x1;
} else {
$rtb = $x2;
$dx = $x1 - $x2;
}
for ($i = 0; $i < self::FINANCIAL_MAX_ITERATIONS; ++$i) {
$dx *= 0.5;
$x_mid = $rtb + $dx;
$f_mid = self::presentValue($x_mid, $values);
if ($f_mid <= 0.0) {
$rtb = $x_mid;
}
if ((abs($f_mid) < self::FINANCIAL_PRECISION) || (abs($dx) < self::FINANCIAL_PRECISION)) {
return $x_mid;
}
}
return Functions::VALUE();
}
/**
* MIRR.
*
* Returns the modified internal rate of return for a series of periodic cash flows. MIRR considers both
* the cost of the investment and the interest received on reinvestment of cash.
*
* Excel Function:
* MIRR(values,finance_rate, reinvestment_rate)
*
* @param mixed $values An array or a reference to cells that contain a series of payments and
* income occurring at regular intervals.
* Payments are negative value, income is positive values.
* @param mixed $financeRate The interest rate you pay on the money used in the cash flows
* @param mixed $reinvestmentRate The interest rate you receive on the cash flows as you reinvest them
*
* @return float|string Result, or a string containing an error
*/
public static function modifiedRate($values, $financeRate, $reinvestmentRate)
{
if (!is_array($values)) {
return Functions::VALUE();
}
$values = Functions::flattenArray($values);
$financeRate = Functions::flattenSingleValue($financeRate);
$reinvestmentRate = Functions::flattenSingleValue($reinvestmentRate);
$n = count($values);
$rr = 1.0 + $reinvestmentRate;
$fr = 1.0 + $financeRate;
$npvPos = $npvNeg = 0.0;
foreach ($values as $i => $v) {
if ($v >= 0) {
$npvPos += $v / $rr ** $i;
} else {
$npvNeg += $v / $fr ** $i;
}
}
if (($npvNeg === 0.0) || ($npvPos === 0.0) || ($reinvestmentRate <= -1.0)) {
return Functions::VALUE();
}
$mirr = ((-$npvPos * $rr ** $n)
/ ($npvNeg * ($rr))) ** (1.0 / ($n - 1)) - 1.0;
return is_finite($mirr) ? $mirr : Functions::VALUE();
}
/**
* NPV.
*
* Returns the Net Present Value of a cash flow series given a discount rate.
*
* @param mixed $rate
*
* @return float
*/
public static function presentValue($rate, ...$args)
{
$returnValue = 0;
$rate = Functions::flattenSingleValue($rate);
$aArgs = Functions::flattenArray($args);
// Calculate
$countArgs = count($aArgs);
for ($i = 1; $i <= $countArgs; ++$i) {
// Is it a numeric value?
if (is_numeric($aArgs[$i - 1])) {
$returnValue += $aArgs[$i - 1] / (1 + $rate) ** $i;
}
}
return $returnValue;
}
}

283
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Securities/Price.php

@ -0,0 +1,283 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Calculation\Financial\Securities;
use PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel;
use PhpOffice\PhpSpreadsheet\Calculation\Exception;
use PhpOffice\PhpSpreadsheet\Calculation\Financial\Constants as FinancialConstants;
use PhpOffice\PhpSpreadsheet\Calculation\Financial\Coupons;
use PhpOffice\PhpSpreadsheet\Calculation\Financial\Helpers;
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
class Price
{
/**
* PRICE.
*
* Returns the price per $100 face value of a security that pays periodic interest.
*
* @param mixed $settlement The security's settlement date.
* The security settlement date is the date after the issue date when the security
* is traded to the buyer.
* @param mixed $maturity The security's maturity date.
* The maturity date is the date when the security expires.
* @param mixed $rate the security's annual coupon rate
* @param mixed $yield the security's annual yield
* @param mixed $redemption The number of coupon payments per year.
* For annual payments, frequency = 1;
* for semiannual, frequency = 2;
* for quarterly, frequency = 4.
* @param mixed $frequency
* @param mixed $basis The type of day count to use.
* 0 or omitted US (NASD) 30/360
* 1 Actual/actual
* 2 Actual/360
* 3 Actual/365
* 4 European 30/360
*
* @return float|string Result, or a string containing an error
*/
public static function price(
$settlement,
$maturity,
$rate,
$yield,
$redemption,
$frequency,
$basis = FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
) {
$settlement = Functions::flattenSingleValue($settlement);
$maturity = Functions::flattenSingleValue($maturity);
$rate = Functions::flattenSingleValue($rate);
$yield = Functions::flattenSingleValue($yield);
$redemption = Functions::flattenSingleValue($redemption);
$frequency = Functions::flattenSingleValue($frequency);
$basis = ($basis === null)
? FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
: Functions::flattenSingleValue($basis);
try {
$settlement = SecurityValidations::validateSettlementDate($settlement);
$maturity = SecurityValidations::validateMaturityDate($maturity);
SecurityValidations::validateSecurityPeriod($settlement, $maturity);
$rate = SecurityValidations::validateRate($rate);
$yield = SecurityValidations::validateYield($yield);
$redemption = SecurityValidations::validateRedemption($redemption);
$frequency = SecurityValidations::validateFrequency($frequency);
$basis = SecurityValidations::validateBasis($basis);
} catch (Exception $e) {
return $e->getMessage();
}
$dsc = Coupons::COUPDAYSNC($settlement, $maturity, $frequency, $basis);
$e = Coupons::COUPDAYS($settlement, $maturity, $frequency, $basis);
$n = Coupons::COUPNUM($settlement, $maturity, $frequency, $basis);
$a = Coupons::COUPDAYBS($settlement, $maturity, $frequency, $basis);
$baseYF = 1.0 + ($yield / $frequency);
$rfp = 100 * ($rate / $frequency);
$de = $dsc / $e;
$result = $redemption / $baseYF ** (--$n + $de);
for ($k = 0; $k <= $n; ++$k) {
$result += $rfp / ($baseYF ** ($k + $de));
}
$result -= $rfp * ($a / $e);
return $result;
}
/**
* PRICEDISC.
*
* Returns the price per $100 face value of a discounted security.
*
* @param mixed $settlement The security's settlement date.
* The security settlement date is the date after the issue date when the security
* is traded to the buyer.
* @param mixed $maturity The security's maturity date.
* The maturity date is the date when the security expires.
* @param mixed $discount The security's discount rate
* @param mixed $redemption The security's redemption value per $100 face value
* @param mixed $basis The type of day count to use.
* 0 or omitted US (NASD) 30/360
* 1 Actual/actual
* 2 Actual/360
* 3 Actual/365
* 4 European 30/360
*
* @return float|string Result, or a string containing an error
*/
public static function priceDiscounted(
$settlement,
$maturity,
$discount,
$redemption,
$basis = FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
) {
$settlement = Functions::flattenSingleValue($settlement);
$maturity = Functions::flattenSingleValue($maturity);
$discount = Functions::flattenSingleValue($discount);
$redemption = Functions::flattenSingleValue($redemption);
$basis = ($basis === null)
? FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
: Functions::flattenSingleValue($basis);
try {
$settlement = SecurityValidations::validateSettlementDate($settlement);
$maturity = SecurityValidations::validateMaturityDate($maturity);
SecurityValidations::validateSecurityPeriod($settlement, $maturity);
$discount = SecurityValidations::validateDiscount($discount);
$redemption = SecurityValidations::validateRedemption($redemption);
$basis = SecurityValidations::validateBasis($basis);
} catch (Exception $e) {
return $e->getMessage();
}
$daysBetweenSettlementAndMaturity = Functions::scalar(DateTimeExcel\YearFrac::fraction($settlement, $maturity, $basis));
if (!is_numeric($daysBetweenSettlementAndMaturity)) {
// return date error
return $daysBetweenSettlementAndMaturity;
}
return $redemption * (1 - $discount * $daysBetweenSettlementAndMaturity);
}
/**
* PRICEMAT.
*
* Returns the price per $100 face value of a security that pays interest at maturity.
*
* @param mixed $settlement The security's settlement date.
* The security's settlement date is the date after the issue date when the
* security is traded to the buyer.
* @param mixed $maturity The security's maturity date.
* The maturity date is the date when the security expires.
* @param mixed $issue The security's issue date
* @param mixed $rate The security's interest rate at date of issue
* @param mixed $yield The security's annual yield
* @param mixed $basis The type of day count to use.
* 0 or omitted US (NASD) 30/360
* 1 Actual/actual
* 2 Actual/360
* 3 Actual/365
* 4 European 30/360
*
* @return float|string Result, or a string containing an error
*/
public static function priceAtMaturity(
$settlement,
$maturity,
$issue,
$rate,
$yield,
$basis = FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
) {
$settlement = Functions::flattenSingleValue($settlement);
$maturity = Functions::flattenSingleValue($maturity);
$issue = Functions::flattenSingleValue($issue);
$rate = Functions::flattenSingleValue($rate);
$yield = Functions::flattenSingleValue($yield);
$basis = ($basis === null)
? FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
: Functions::flattenSingleValue($basis);
try {
$settlement = SecurityValidations::validateSettlementDate($settlement);
$maturity = SecurityValidations::validateMaturityDate($maturity);
SecurityValidations::validateSecurityPeriod($settlement, $maturity);
$issue = SecurityValidations::validateIssueDate($issue);
$rate = SecurityValidations::validateRate($rate);
$yield = SecurityValidations::validateYield($yield);
$basis = SecurityValidations::validateBasis($basis);
} catch (Exception $e) {
return $e->getMessage();
}
$daysPerYear = Functions::scalar(Helpers::daysPerYear(DateTimeExcel\DateParts::year($settlement), $basis));
if (!is_numeric($daysPerYear)) {
return $daysPerYear;
}
$daysBetweenIssueAndSettlement = Functions::scalar(DateTimeExcel\YearFrac::fraction($issue, $settlement, $basis));
if (!is_numeric($daysBetweenIssueAndSettlement)) {
// return date error
return $daysBetweenIssueAndSettlement;
}
$daysBetweenIssueAndSettlement *= $daysPerYear;
$daysBetweenIssueAndMaturity = Functions::scalar(DateTimeExcel\YearFrac::fraction($issue, $maturity, $basis));
if (!is_numeric($daysBetweenIssueAndMaturity)) {
// return date error
return $daysBetweenIssueAndMaturity;
}
$daysBetweenIssueAndMaturity *= $daysPerYear;
$daysBetweenSettlementAndMaturity = Functions::scalar(DateTimeExcel\YearFrac::fraction($settlement, $maturity, $basis));
if (!is_numeric($daysBetweenSettlementAndMaturity)) {
// return date error
return $daysBetweenSettlementAndMaturity;
}
$daysBetweenSettlementAndMaturity *= $daysPerYear;
return (100 + (($daysBetweenIssueAndMaturity / $daysPerYear) * $rate * 100)) /
(1 + (($daysBetweenSettlementAndMaturity / $daysPerYear) * $yield)) -
(($daysBetweenIssueAndSettlement / $daysPerYear) * $rate * 100);
}
/**
* RECEIVED.
*
* Returns the amount received at maturity for a fully invested Security.
*
* @param mixed $settlement The security's settlement date.
* The security settlement date is the date after the issue date when the security
* is traded to the buyer.
* @param mixed $maturity The security's maturity date.
* The maturity date is the date when the security expires.
* @param mixed $investment The amount invested in the security
* @param mixed $discount The security's discount rate
* @param mixed $basis The type of day count to use.
* 0 or omitted US (NASD) 30/360
* 1 Actual/actual
* 2 Actual/360
* 3 Actual/365
* 4 European 30/360
*
* @return float|string Result, or a string containing an error
*/
public static function received(
$settlement,
$maturity,
$investment,
$discount,
$basis = FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
) {
$settlement = Functions::flattenSingleValue($settlement);
$maturity = Functions::flattenSingleValue($maturity);
$investment = Functions::flattenSingleValue($investment);
$discount = Functions::flattenSingleValue($discount);
$basis = ($basis === null)
? FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
: Functions::flattenSingleValue($basis);
try {
$settlement = SecurityValidations::validateSettlementDate($settlement);
$maturity = SecurityValidations::validateMaturityDate($maturity);
SecurityValidations::validateSecurityPeriod($settlement, $maturity);
$investment = SecurityValidations::validateFloat($investment);
$discount = SecurityValidations::validateDiscount($discount);
$basis = SecurityValidations::validateBasis($basis);
} catch (Exception $e) {
return $e->getMessage();
}
if ($investment <= 0) {
return Functions::NAN();
}
$daysBetweenSettlementAndMaturity = DateTimeExcel\YearFrac::fraction($settlement, $maturity, $basis);
if (!is_numeric($daysBetweenSettlementAndMaturity)) {
// return date error
return $daysBetweenSettlementAndMaturity;
}
return $investment / (1 - ($discount * $daysBetweenSettlementAndMaturity));
}
}

137
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Securities/Rates.php

@ -0,0 +1,137 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Calculation\Financial\Securities;
use PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel;
use PhpOffice\PhpSpreadsheet\Calculation\Exception;
use PhpOffice\PhpSpreadsheet\Calculation\Financial\Constants as FinancialConstants;
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
class Rates
{
/**
* DISC.
*
* Returns the discount rate for a security.
*
* Excel Function:
* DISC(settlement,maturity,price,redemption[,basis])
*
* @param mixed $settlement The security's settlement date.
* The security settlement date is the date after the issue
* date when the security is traded to the buyer.
* @param mixed $maturity The security's maturity date.
* The maturity date is the date when the security expires.
* @param mixed $price The security's price per $100 face value
* @param mixed $redemption The security's redemption value per $100 face value
* @param mixed $basis The type of day count to use.
* 0 or omitted US (NASD) 30/360
* 1 Actual/actual
* 2 Actual/360
* 3 Actual/365
* 4 European 30/360
*
* @return float|string
*/
public static function discount(
$settlement,
$maturity,
$price,
$redemption,
$basis = FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
) {
$settlement = Functions::flattenSingleValue($settlement);
$maturity = Functions::flattenSingleValue($maturity);
$price = Functions::flattenSingleValue($price);
$redemption = Functions::flattenSingleValue($redemption);
$basis = ($basis === null)
? FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
: Functions::flattenSingleValue($basis);
try {
$settlement = SecurityValidations::validateSettlementDate($settlement);
$maturity = SecurityValidations::validateMaturityDate($maturity);
SecurityValidations::validateSecurityPeriod($settlement, $maturity);
$price = SecurityValidations::validatePrice($price);
$redemption = SecurityValidations::validateRedemption($redemption);
$basis = SecurityValidations::validateBasis($basis);
} catch (Exception $e) {
return $e->getMessage();
}
if ($price <= 0.0) {
return Functions::NAN();
}
$daysBetweenSettlementAndMaturity = Functions::scalar(DateTimeExcel\YearFrac::fraction($settlement, $maturity, $basis));
if (!is_numeric($daysBetweenSettlementAndMaturity)) {
// return date error
return $daysBetweenSettlementAndMaturity;
}
return (1 - $price / $redemption) / $daysBetweenSettlementAndMaturity;
}
/**
* INTRATE.
*
* Returns the interest rate for a fully invested security.
*
* Excel Function:
* INTRATE(settlement,maturity,investment,redemption[,basis])
*
* @param mixed $settlement The security's settlement date.
* The security settlement date is the date after the issue date when the security
* is traded to the buyer.
* @param mixed $maturity The security's maturity date.
* The maturity date is the date when the security expires.
* @param mixed $investment the amount invested in the security
* @param mixed $redemption the amount to be received at maturity
* @param mixed $basis The type of day count to use.
* 0 or omitted US (NASD) 30/360
* 1 Actual/actual
* 2 Actual/360
* 3 Actual/365
* 4 European 30/360
*
* @return float|string
*/
public static function interest(
$settlement,
$maturity,
$investment,
$redemption,
$basis = FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
) {
$settlement = Functions::flattenSingleValue($settlement);
$maturity = Functions::flattenSingleValue($maturity);
$investment = Functions::flattenSingleValue($investment);
$redemption = Functions::flattenSingleValue($redemption);
$basis = ($basis === null)
? FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
: Functions::flattenSingleValue($basis);
try {
$settlement = SecurityValidations::validateSettlementDate($settlement);
$maturity = SecurityValidations::validateMaturityDate($maturity);
SecurityValidations::validateSecurityPeriod($settlement, $maturity);
$investment = SecurityValidations::validateFloat($investment);
$redemption = SecurityValidations::validateRedemption($redemption);
$basis = SecurityValidations::validateBasis($basis);
} catch (Exception $e) {
return $e->getMessage();
}
if ($investment <= 0) {
return Functions::NAN();
}
$daysBetweenSettlementAndMaturity = Functions::scalar(DateTimeExcel\YearFrac::fraction($settlement, $maturity, $basis));
if (!is_numeric($daysBetweenSettlementAndMaturity)) {
// return date error
return $daysBetweenSettlementAndMaturity;
}
return (($redemption / $investment) - 1) / ($daysBetweenSettlementAndMaturity);
}
}

42
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Securities/SecurityValidations.php

@ -0,0 +1,42 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Calculation\Financial\Securities;
use PhpOffice\PhpSpreadsheet\Calculation\Exception;
use PhpOffice\PhpSpreadsheet\Calculation\Financial\FinancialValidations;
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
class SecurityValidations extends FinancialValidations
{
/**
* @param mixed $issue
*/
public static function validateIssueDate($issue): float
{
return self::validateDate($issue);
}
/**
* @param mixed $settlement
* @param mixed $maturity
*/
public static function validateSecurityPeriod($settlement, $maturity): void
{
if ($settlement >= $maturity) {
throw new Exception(Functions::NAN());
}
}
/**
* @param mixed $redemption
*/
public static function validateRedemption($redemption): float
{
$redemption = self::validateFloat($redemption);
if ($redemption <= 0.0) {
throw new Exception(Functions::NAN());
}
return $redemption;
}
}

153
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/Securities/Yields.php

@ -0,0 +1,153 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Calculation\Financial\Securities;
use PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel;
use PhpOffice\PhpSpreadsheet\Calculation\Exception;
use PhpOffice\PhpSpreadsheet\Calculation\Financial\Constants as FinancialConstants;
use PhpOffice\PhpSpreadsheet\Calculation\Financial\Helpers;
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
class Yields
{
/**
* YIELDDISC.
*
* Returns the annual yield of a security that pays interest at maturity.
*
* @param mixed $settlement The security's settlement date.
* The security's settlement date is the date after the issue date when the security
* is traded to the buyer.
* @param mixed $maturity The security's maturity date.
* The maturity date is the date when the security expires.
* @param mixed $price The security's price per $100 face value
* @param mixed $redemption The security's redemption value per $100 face value
* @param mixed $basis The type of day count to use.
* 0 or omitted US (NASD) 30/360
* 1 Actual/actual
* 2 Actual/360
* 3 Actual/365
* 4 European 30/360
*
* @return float|string Result, or a string containing an error
*/
public static function yieldDiscounted(
$settlement,
$maturity,
$price,
$redemption,
$basis = FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
) {
$settlement = Functions::flattenSingleValue($settlement);
$maturity = Functions::flattenSingleValue($maturity);
$price = Functions::flattenSingleValue($price);
$redemption = Functions::flattenSingleValue($redemption);
$basis = ($basis === null)
? FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
: Functions::flattenSingleValue($basis);
try {
$settlement = SecurityValidations::validateSettlementDate($settlement);
$maturity = SecurityValidations::validateMaturityDate($maturity);
SecurityValidations::validateSecurityPeriod($settlement, $maturity);
$price = SecurityValidations::validatePrice($price);
$redemption = SecurityValidations::validateRedemption($redemption);
$basis = SecurityValidations::validateBasis($basis);
} catch (Exception $e) {
return $e->getMessage();
}
$daysPerYear = Helpers::daysPerYear(DateTimeExcel\DateParts::year($settlement), $basis);
if (!is_numeric($daysPerYear)) {
return $daysPerYear;
}
$daysBetweenSettlementAndMaturity = Functions::scalar(DateTimeExcel\YearFrac::fraction($settlement, $maturity, $basis));
if (!is_numeric($daysBetweenSettlementAndMaturity)) {
// return date error
return $daysBetweenSettlementAndMaturity;
}
$daysBetweenSettlementAndMaturity *= $daysPerYear;
return (($redemption - $price) / $price) * ($daysPerYear / $daysBetweenSettlementAndMaturity);
}
/**
* YIELDMAT.
*
* Returns the annual yield of a security that pays interest at maturity.
*
* @param mixed $settlement The security's settlement date.
* The security's settlement date is the date after the issue date when the security
* is traded to the buyer.
* @param mixed $maturity The security's maturity date.
* The maturity date is the date when the security expires.
* @param mixed $issue The security's issue date
* @param mixed $rate The security's interest rate at date of issue
* @param mixed $price The security's price per $100 face value
* @param mixed $basis The type of day count to use.
* 0 or omitted US (NASD) 30/360
* 1 Actual/actual
* 2 Actual/360
* 3 Actual/365
* 4 European 30/360
*
* @return float|string Result, or a string containing an error
*/
public static function yieldAtMaturity(
$settlement,
$maturity,
$issue,
$rate,
$price,
$basis = FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
) {
$settlement = Functions::flattenSingleValue($settlement);
$maturity = Functions::flattenSingleValue($maturity);
$issue = Functions::flattenSingleValue($issue);
$rate = Functions::flattenSingleValue($rate);
$price = Functions::flattenSingleValue($price);
$basis = ($basis === null)
? FinancialConstants::BASIS_DAYS_PER_YEAR_NASD
: Functions::flattenSingleValue($basis);
try {
$settlement = SecurityValidations::validateSettlementDate($settlement);
$maturity = SecurityValidations::validateMaturityDate($maturity);
SecurityValidations::validateSecurityPeriod($settlement, $maturity);
$issue = SecurityValidations::validateIssueDate($issue);
$rate = SecurityValidations::validateRate($rate);
$price = SecurityValidations::validatePrice($price);
$basis = SecurityValidations::validateBasis($basis);
} catch (Exception $e) {
return $e->getMessage();
}
$daysPerYear = Helpers::daysPerYear(DateTimeExcel\DateParts::year($settlement), $basis);
if (!is_numeric($daysPerYear)) {
return $daysPerYear;
}
$daysBetweenIssueAndSettlement = Functions::scalar(DateTimeExcel\YearFrac::fraction($issue, $settlement, $basis));
if (!is_numeric($daysBetweenIssueAndSettlement)) {
// return date error
return $daysBetweenIssueAndSettlement;
}
$daysBetweenIssueAndSettlement *= $daysPerYear;
$daysBetweenIssueAndMaturity = Functions::scalar(DateTimeExcel\YearFrac::fraction($issue, $maturity, $basis));
if (!is_numeric($daysBetweenIssueAndMaturity)) {
// return date error
return $daysBetweenIssueAndMaturity;
}
$daysBetweenIssueAndMaturity *= $daysPerYear;
$daysBetweenSettlementAndMaturity = Functions::scalar(DateTimeExcel\YearFrac::fraction($settlement, $maturity, $basis));
if (!is_numeric($daysBetweenSettlementAndMaturity)) {
// return date error
return $daysBetweenSettlementAndMaturity;
}
$daysBetweenSettlementAndMaturity *= $daysPerYear;
return ((1 + (($daysBetweenIssueAndMaturity / $daysPerYear) * $rate) -
(($price / 100) + (($daysBetweenIssueAndSettlement / $daysPerYear) * $rate))) /
(($price / 100) + (($daysBetweenIssueAndSettlement / $daysPerYear) * $rate))) *
($daysPerYear / $daysBetweenSettlementAndMaturity);
}
}

147
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Financial/TreasuryBill.php

@ -0,0 +1,147 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Calculation\Financial;
use PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel;
use PhpOffice\PhpSpreadsheet\Calculation\Exception;
use PhpOffice\PhpSpreadsheet\Calculation\Financial\Constants as FinancialConstants;
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
class TreasuryBill
{
/**
* TBILLEQ.
*
* Returns the bond-equivalent yield for a Treasury bill.
*
* @param mixed $settlement The Treasury bill's settlement date.
* The Treasury bill's settlement date is the date after the issue date
* when the Treasury bill is traded to the buyer.
* @param mixed $maturity The Treasury bill's maturity date.
* The maturity date is the date when the Treasury bill expires.
* @param mixed $discount The Treasury bill's discount rate
*
* @return float|string Result, or a string containing an error
*/
public static function bondEquivalentYield($settlement, $maturity, $discount)
{
$settlement = Functions::flattenSingleValue($settlement);
$maturity = Functions::flattenSingleValue($maturity);
$discount = Functions::flattenSingleValue($discount);
try {
$settlement = FinancialValidations::validateSettlementDate($settlement);
$maturity = FinancialValidations::validateMaturityDate($maturity);
$discount = FinancialValidations::validateFloat($discount);
} catch (Exception $e) {
return $e->getMessage();
}
if ($discount <= 0) {
return Functions::NAN();
}
$daysBetweenSettlementAndMaturity = $maturity - $settlement;
$daysPerYear = Helpers::daysPerYear(
Functions::scalar(DateTimeExcel\DateParts::year($maturity)),
FinancialConstants::BASIS_DAYS_PER_YEAR_ACTUAL
);
if ($daysBetweenSettlementAndMaturity > $daysPerYear || $daysBetweenSettlementAndMaturity < 0) {
return Functions::NAN();
}
return (365 * $discount) / (360 - $discount * $daysBetweenSettlementAndMaturity);
}
/**
* TBILLPRICE.
*
* Returns the price per $100 face value for a Treasury bill.
*
* @param mixed $settlement The Treasury bill's settlement date.
* The Treasury bill's settlement date is the date after the issue date
* when the Treasury bill is traded to the buyer.
* @param mixed $maturity The Treasury bill's maturity date.
* The maturity date is the date when the Treasury bill expires.
* @param mixed $discount The Treasury bill's discount rate
*
* @return float|string Result, or a string containing an error
*/
public static function price($settlement, $maturity, $discount)
{
$settlement = Functions::flattenSingleValue($settlement);
$maturity = Functions::flattenSingleValue($maturity);
$discount = Functions::flattenSingleValue($discount);
try {
$settlement = FinancialValidations::validateSettlementDate($settlement);
$maturity = FinancialValidations::validateMaturityDate($maturity);
$discount = FinancialValidations::validateFloat($discount);
} catch (Exception $e) {
return $e->getMessage();
}
if ($discount <= 0) {
return Functions::NAN();
}
$daysBetweenSettlementAndMaturity = $maturity - $settlement;
$daysPerYear = Helpers::daysPerYear(
Functions::scalar(DateTimeExcel\DateParts::year($maturity)),
FinancialConstants::BASIS_DAYS_PER_YEAR_ACTUAL
);
if ($daysBetweenSettlementAndMaturity > $daysPerYear || $daysBetweenSettlementAndMaturity < 0) {
return Functions::NAN();
}
$price = 100 * (1 - (($discount * $daysBetweenSettlementAndMaturity) / 360));
if ($price < 0.0) {
return Functions::NAN();
}
return $price;
}
/**
* TBILLYIELD.
*
* Returns the yield for a Treasury bill.
*
* @param mixed $settlement The Treasury bill's settlement date.
* The Treasury bill's settlement date is the date after the issue date when
* the Treasury bill is traded to the buyer.
* @param mixed $maturity The Treasury bill's maturity date.
* The maturity date is the date when the Treasury bill expires.
* @param mixed $price The Treasury bill's price per $100 face value
*
* @return float|string
*/
public static function yield($settlement, $maturity, $price)
{
$settlement = Functions::flattenSingleValue($settlement);
$maturity = Functions::flattenSingleValue($maturity);
$price = Functions::flattenSingleValue($price);
try {
$settlement = FinancialValidations::validateSettlementDate($settlement);
$maturity = FinancialValidations::validateMaturityDate($maturity);
$price = FinancialValidations::validatePrice($price);
} catch (Exception $e) {
return $e->getMessage();
}
$daysBetweenSettlementAndMaturity = $maturity - $settlement;
$daysPerYear = Helpers::daysPerYear(
Functions::scalar(DateTimeExcel\DateParts::year($maturity)),
FinancialConstants::BASIS_DAYS_PER_YEAR_ACTUAL
);
if ($daysBetweenSettlementAndMaturity > $daysPerYear || $daysBetweenSettlementAndMaturity < 0) {
return Functions::NAN();
}
return ((100 - $price) / $price) * (360 / $daysBetweenSettlementAndMaturity);
}
}

39
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Internal/WildcardMatch.php

@ -0,0 +1,39 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Calculation\Internal;
class WildcardMatch
{
private const SEARCH_SET = [
'~~', // convert double tilde to unprintable value
'~\\*', // convert tilde backslash asterisk to [*] (matches literal asterisk in regexp)
'\\*', // convert backslash asterisk to .* (matches string of any length in regexp)
'~\\?', // convert tilde backslash question to [?] (matches literal question mark in regexp)
'\\?', // convert backslash question to . (matches one character in regexp)
"\x1c", // convert original double tilde to single tilde
];
private const REPLACEMENT_SET = [
"\x1c",
'[*]',
'.*',
'[?]',
'.',
'~',
];
public static function wildcard(string $wildcard): string
{
// Preg Escape the wildcard, but protecting the Excel * and ? search characters
return str_replace(self::SEARCH_SET, self::REPLACEMENT_SET, preg_quote($wildcard, '/'));
}
public static function compare(?string $value, string $wildcard): bool
{
if ($value === '' || $value === null) {
return false;
}
return (bool) preg_match("/^{$wildcard}\$/mui", $value);
}
}

209
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/RowColumnInformation.php

@ -0,0 +1,209 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Calculation\LookupRef;
use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
use PhpOffice\PhpSpreadsheet\Cell\Cell;
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
class RowColumnInformation
{
/**
* Test if cellAddress is null or whitespace string.
*
* @param null|array|string $cellAddress A reference to a range of cells
*/
private static function cellAddressNullOrWhitespace($cellAddress): bool
{
return $cellAddress === null || (!is_array($cellAddress) && trim($cellAddress) === '');
}
private static function cellColumn(?Cell $cell): int
{
return ($cell !== null) ? (int) Coordinate::columnIndexFromString($cell->getColumn()) : 1;
}
/**
* COLUMN.
*
* Returns the column number of the given cell reference
* If the cell reference is a range of cells, COLUMN returns the column numbers of each column
* in the reference as a horizontal array.
* If cell reference is omitted, and the function is being called through the calculation engine,
* then it is assumed to be the reference of the cell in which the COLUMN function appears;
* otherwise this function returns 1.
*
* Excel Function:
* =COLUMN([cellAddress])
*
* @param null|array|string $cellAddress A reference to a range of cells for which you want the column numbers
*
* @return int|int[]
*/
public static function COLUMN($cellAddress = null, ?Cell $cell = null)
{
if (self::cellAddressNullOrWhitespace($cellAddress)) {
return self::cellColumn($cell);
}
if (is_array($cellAddress)) {
foreach ($cellAddress as $columnKey => $value) {
$columnKey = preg_replace('/[^a-z]/i', '', $columnKey);
return (int) Coordinate::columnIndexFromString($columnKey);
}
return self::cellColumn($cell);
}
$cellAddress = $cellAddress ?? '';
if ($cell != null) {
[,, $sheetName] = Helpers::extractWorksheet($cellAddress, $cell);
[,, $cellAddress] = Helpers::extractCellAddresses($cellAddress, true, $cell->getWorksheet(), $sheetName);
}
[, $cellAddress] = Worksheet::extractSheetTitle($cellAddress, true);
if (strpos($cellAddress, ':') !== false) {
[$startAddress, $endAddress] = explode(':', $cellAddress);
$startAddress = preg_replace('/[^a-z]/i', '', $startAddress);
$endAddress = preg_replace('/[^a-z]/i', '', $endAddress);
return range(
(int) Coordinate::columnIndexFromString($startAddress),
(int) Coordinate::columnIndexFromString($endAddress)
);
}
$cellAddress = preg_replace('/[^a-z]/i', '', $cellAddress);
return (int) Coordinate::columnIndexFromString($cellAddress);
}
/**
* COLUMNS.
*
* Returns the number of columns in an array or reference.
*
* Excel Function:
* =COLUMNS(cellAddress)
*
* @param null|array|string $cellAddress An array or array formula, or a reference to a range of cells
* for which you want the number of columns
*
* @return int|string The number of columns in cellAddress, or a string if arguments are invalid
*/
public static function COLUMNS($cellAddress = null)
{
if (self::cellAddressNullOrWhitespace($cellAddress)) {
return 1;
}
if (!is_array($cellAddress)) {
return Functions::VALUE();
}
reset($cellAddress);
$isMatrix = (is_numeric(key($cellAddress)));
[$columns, $rows] = Calculation::getMatrixDimensions($cellAddress);
if ($isMatrix) {
return $rows;
}
return $columns;
}
private static function cellRow(?Cell $cell): int
{
return ($cell !== null) ? $cell->getRow() : 1;
}
/**
* ROW.
*
* Returns the row number of the given cell reference
* If the cell reference is a range of cells, ROW returns the row numbers of each row in the reference
* as a vertical array.
* If cell reference is omitted, and the function is being called through the calculation engine,
* then it is assumed to be the reference of the cell in which the ROW function appears;
* otherwise this function returns 1.
*
* Excel Function:
* =ROW([cellAddress])
*
* @param null|array|string $cellAddress A reference to a range of cells for which you want the row numbers
*
* @return int|mixed[]|string
*/
public static function ROW($cellAddress = null, ?Cell $cell = null)
{
if (self::cellAddressNullOrWhitespace($cellAddress)) {
return self::cellRow($cell);
}
if (is_array($cellAddress)) {
foreach ($cellAddress as $rowKey => $rowValue) {
foreach ($rowValue as $columnKey => $cellValue) {
return (int) preg_replace('/\D/', '', $rowKey);
}
}
return self::cellRow($cell);
}
$cellAddress = $cellAddress ?? '';
if ($cell !== null) {
[,, $sheetName] = Helpers::extractWorksheet($cellAddress, $cell);
[,, $cellAddress] = Helpers::extractCellAddresses($cellAddress, true, $cell->getWorksheet(), $sheetName);
}
[, $cellAddress] = Worksheet::extractSheetTitle($cellAddress, true);
if (strpos($cellAddress, ':') !== false) {
[$startAddress, $endAddress] = explode(':', $cellAddress);
$startAddress = preg_replace('/\D/', '', $startAddress);
$endAddress = preg_replace('/\D/', '', $endAddress);
return array_map(
function ($value) {
return [$value];
},
range($startAddress, $endAddress)
);
}
[$cellAddress] = explode(':', $cellAddress);
return (int) preg_replace('/\D/', '', $cellAddress);
}
/**
* ROWS.
*
* Returns the number of rows in an array or reference.
*
* Excel Function:
* =ROWS(cellAddress)
*
* @param null|array|string $cellAddress An array or array formula, or a reference to a range of cells
* for which you want the number of rows
*
* @return int|string The number of rows in cellAddress, or a string if arguments are invalid
*/
public static function ROWS($cellAddress = null)
{
if (self::cellAddressNullOrWhitespace($cellAddress)) {
return 1;
}
if (!is_array($cellAddress)) {
return Functions::VALUE();
}
reset($cellAddress);
$isMatrix = (is_numeric(key($cellAddress)));
[$columns, $rows] = Calculation::getMatrixDimensions($cellAddress);
if ($isMatrix) {
return $columns;
}
return $rows;
}
}

46
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/Selection.php

@ -0,0 +1,46 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Calculation\LookupRef;
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
class Selection
{
/**
* CHOOSE.
*
* Uses lookup_value to return a value from the list of value arguments.
* Use CHOOSE to select one of up to 254 values based on the lookup_value.
*
* Excel Function:
* =CHOOSE(index_num, value1, [value2], ...)
*
* @param mixed ...$chooseArgs Data values
*
* @return mixed The selected value
*/
public static function choose(...$chooseArgs)
{
$chosenEntry = Functions::flattenArray(array_shift($chooseArgs));
$entryCount = count($chooseArgs) - 1;
if (is_array($chosenEntry)) {
$chosenEntry = array_shift($chosenEntry);
}
if (is_numeric($chosenEntry)) {
--$chosenEntry;
} else {
return Functions::VALUE();
}
$chosenEntry = floor($chosenEntry);
if (($chosenEntry < 0) || ($chosenEntry > $entryCount)) {
return Functions::VALUE();
}
if (is_array($chooseArgs[$chosenEntry])) {
return Functions::flattenArray($chooseArgs[$chosenEntry]);
}
return $chooseArgs[$chosenEntry];
}
}

105
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/LookupRef/VLookup.php

@ -0,0 +1,105 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Calculation\LookupRef;
use PhpOffice\PhpSpreadsheet\Calculation\Exception;
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
class VLookup extends LookupBase
{
/**
* VLOOKUP
* The VLOOKUP function searches for value in the left-most column of lookup_array and returns the value
* in the same row based on the index_number.
*
* @param mixed $lookupValue The value that you want to match in lookup_array
* @param mixed $lookupArray The range of cells being searched
* @param mixed $indexNumber The column number in table_array from which the matching value must be returned.
* The first column is 1.
* @param mixed $notExactMatch determines if you are looking for an exact match based on lookup_value
*
* @return mixed The value of the found cell
*/
public static function lookup($lookupValue, $lookupArray, $indexNumber, $notExactMatch = true)
{
$lookupValue = Functions::flattenSingleValue($lookupValue);
$indexNumber = Functions::flattenSingleValue($indexNumber);
$notExactMatch = ($notExactMatch === null) ? true : Functions::flattenSingleValue($notExactMatch);
try {
$indexNumber = self::validateIndexLookup($lookupArray, $indexNumber);
} catch (Exception $e) {
return $e->getMessage();
}
$f = array_keys($lookupArray);
$firstRow = array_pop($f);
if ((!is_array($lookupArray[$firstRow])) || ($indexNumber > count($lookupArray[$firstRow]))) {
return Functions::REF();
}
$columnKeys = array_keys($lookupArray[$firstRow]);
$returnColumn = $columnKeys[--$indexNumber];
$firstColumn = array_shift($columnKeys);
if (!$notExactMatch) {
uasort($lookupArray, ['self', 'vlookupSort']);
}
$rowNumber = self::vLookupSearch($lookupValue, $lookupArray, $firstColumn, $notExactMatch);
if ($rowNumber !== null) {
// return the appropriate value
return $lookupArray[$rowNumber][$returnColumn];
}
return Functions::NA();
}
private static function vlookupSort($a, $b)
{
reset($a);
$firstColumn = key($a);
$aLower = StringHelper::strToLower($a[$firstColumn]);
$bLower = StringHelper::strToLower($b[$firstColumn]);
if ($aLower == $bLower) {
return 0;
}
return ($aLower < $bLower) ? -1 : 1;
}
private static function vLookupSearch($lookupValue, $lookupArray, $column, $notExactMatch)
{
$lookupLower = StringHelper::strToLower($lookupValue);
$rowNumber = null;
foreach ($lookupArray as $rowKey => $rowData) {
$bothNumeric = is_numeric($lookupValue) && is_numeric($rowData[$column]);
$bothNotNumeric = !is_numeric($lookupValue) && !is_numeric($rowData[$column]);
$cellDataLower = StringHelper::strToLower($rowData[$column]);
// break if we have passed possible keys
if (
$notExactMatch &&
(($bothNumeric && ($rowData[$column] > $lookupValue)) ||
($bothNotNumeric && ($cellDataLower > $lookupLower)))
) {
break;
}
$rowNumber = self::checkMatch(
$bothNumeric,
$bothNotNumeric,
$notExactMatch,
$rowKey,
$cellDataLower,
$lookupLower,
$rowNumber
);
}
return $rowNumber;
}
}

99
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Random.php

@ -0,0 +1,99 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Calculation\MathTrig;
use PhpOffice\PhpSpreadsheet\Calculation\ArrayEnabled;
use PhpOffice\PhpSpreadsheet\Calculation\Exception;
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
class Random
{
use ArrayEnabled;
/**
* RAND.
*
* @return float Random number
*/
public static function rand()
{
return (mt_rand(0, 10000000)) / 10000000;
}
/**
* RANDBETWEEN.
*
* @param mixed $min Minimal value
* Or can be an array of values
* @param mixed $max Maximal value
* Or can be an array of values
*
* @return array|float|int|string Random number
* 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 randBetween($min, $max)
{
if (is_array($min) || is_array($max)) {
return self::evaluateArrayArguments([self::class, __FUNCTION__], $min, $max);
}
try {
$min = (int) Helpers::validateNumericNullBool($min);
$max = (int) Helpers::validateNumericNullBool($max);
Helpers::validateNotNegative($max - $min);
} catch (Exception $e) {
return $e->getMessage();
}
return mt_rand($min, $max);
}
/**
* RANDARRAY.
*
* Generates a list of sequential numbers in an array.
*
* Excel Function:
* RANDARRAY([rows],[columns],[start],[step])
*
* @param mixed $rows the number of rows to return, defaults to 1
* @param mixed $columns the number of columns to return, defaults to 1
* @param mixed $min the minimum number to be returned, defaults to 0
* @param mixed $max the maximum number to be returned, defaults to 1
* @param bool $wholeNumber the type of numbers to return:
* False - Decimal numbers to 15 decimal places. (default)
* True - Whole (integer) numbers
*
* @return array|string The resulting array, or a string containing an error
*/
public static function randArray($rows = 1, $columns = 1, $min = 0, $max = 1, $wholeNumber = false)
{
try {
$rows = (int) Helpers::validateNumericNullSubstitution($rows, 1);
Helpers::validatePositive($rows);
$columns = (int) Helpers::validateNumericNullSubstitution($columns, 1);
Helpers::validatePositive($columns);
$min = Helpers::validateNumericNullSubstitution($min, 1);
$max = Helpers::validateNumericNullSubstitution($max, 1);
if ($max <= $min) {
return Functions::VALUE();
}
} catch (Exception $e) {
return $e->getMessage();
}
return array_chunk(
array_map(
function () use ($min, $max, $wholeNumber) {
return $wholeNumber
? mt_rand((int) $min, (int) $max)
: (mt_rand() / mt_getrandmax()) * ($max - $min) + $min;
},
array_fill(0, $rows * $columns, $min)
),
max($columns, 1)
);
}
}

846
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Roman.php

@ -0,0 +1,846 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Calculation\MathTrig;
use PhpOffice\PhpSpreadsheet\Calculation\ArrayEnabled;
use PhpOffice\PhpSpreadsheet\Calculation\Exception;
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
class Roman
{
use ArrayEnabled;
private const VALUES = [
45 => ['VL'],
46 => ['VLI'],
47 => ['VLII'],
48 => ['VLIII'],
49 => ['VLIV', 'IL'],
95 => ['VC'],
96 => ['VCI'],
97 => ['VCII'],
98 => ['VCIII'],
99 => ['VCIV', 'IC'],
145 => ['CVL'],
146 => ['CVLI'],
147 => ['CVLII'],
148 => ['CVLIII'],
149 => ['CVLIV', 'CIL'],
195 => ['CVC'],
196 => ['CVCI'],
197 => ['CVCII'],
198 => ['CVCIII'],
199 => ['CVCIV', 'CIC'],
245 => ['CCVL'],
246 => ['CCVLI'],
247 => ['CCVLII'],
248 => ['CCVLIII'],
249 => ['CCVLIV', 'CCIL'],
295 => ['CCVC'],
296 => ['CCVCI'],
297 => ['CCVCII'],
298 => ['CCVCIII'],
299 => ['CCVCIV', 'CCIC'],
345 => ['CCCVL'],
346 => ['CCCVLI'],
347 => ['CCCVLII'],
348 => ['CCCVLIII'],
349 => ['CCCVLIV', 'CCCIL'],
395 => ['CCCVC'],
396 => ['CCCVCI'],
397 => ['CCCVCII'],
398 => ['CCCVCIII'],
399 => ['CCCVCIV', 'CCCIC'],
445 => ['CDVL'],
446 => ['CDVLI'],
447 => ['CDVLII'],
448 => ['CDVLIII'],
449 => ['CDVLIV', 'CDIL'],
450 => ['LD'],
451 => ['LDI'],
452 => ['LDII'],
453 => ['LDIII'],
454 => ['LDIV'],
455 => ['LDV'],
456 => ['LDVI'],
457 => ['LDVII'],
458 => ['LDVIII'],
459 => ['LDIX'],
460 => ['LDX'],
461 => ['LDXI'],
462 => ['LDXII'],
463 => ['LDXIII'],
464 => ['LDXIV'],
465 => ['LDXV'],
466 => ['LDXVI'],
467 => ['LDXVII'],
468 => ['LDXVIII'],
469 => ['LDXIX'],
470 => ['LDXX'],
471 => ['LDXXI'],
472 => ['LDXXII'],
473 => ['LDXXIII'],
474 => ['LDXXIV'],
475 => ['LDXXV'],
476 => ['LDXXVI'],
477 => ['LDXXVII'],
478 => ['LDXXVIII'],
479 => ['LDXXIX'],
480 => ['LDXXX'],
481 => ['LDXXXI'],
482 => ['LDXXXII'],
483 => ['LDXXXIII'],
484 => ['LDXXXIV'],
485 => ['LDXXXV'],
486 => ['LDXXXVI'],
487 => ['LDXXXVII'],
488 => ['LDXXXVIII'],
489 => ['LDXXXIX'],
490 => ['LDXL', 'XD'],
491 => ['LDXLI', 'XDI'],
492 => ['LDXLII', 'XDII'],
493 => ['LDXLIII', 'XDIII'],
494 => ['LDXLIV', 'XDIV'],
495 => ['LDVL', 'XDV', 'VD'],
496 => ['LDVLI', 'XDVI', 'VDI'],
497 => ['LDVLII', 'XDVII', 'VDII'],
498 => ['LDVLIII', 'XDVIII', 'VDIII'],
499 => ['LDVLIV', 'XDIX', 'VDIV', 'ID'],
545 => ['DVL'],
546 => ['DVLI'],
547 => ['DVLII'],
548 => ['DVLIII'],
549 => ['DVLIV', 'DIL'],
595 => ['DVC'],
596 => ['DVCI'],
597 => ['DVCII'],
598 => ['DVCIII'],
599 => ['DVCIV', 'DIC'],
645 => ['DCVL'],
646 => ['DCVLI'],
647 => ['DCVLII'],
648 => ['DCVLIII'],
649 => ['DCVLIV', 'DCIL'],
695 => ['DCVC'],
696 => ['DCVCI'],
697 => ['DCVCII'],
698 => ['DCVCIII'],
699 => ['DCVCIV', 'DCIC'],
745 => ['DCCVL'],
746 => ['DCCVLI'],
747 => ['DCCVLII'],
748 => ['DCCVLIII'],
749 => ['DCCVLIV', 'DCCIL'],
795 => ['DCCVC'],
796 => ['DCCVCI'],
797 => ['DCCVCII'],
798 => ['DCCVCIII'],
799 => ['DCCVCIV', 'DCCIC'],
845 => ['DCCCVL'],
846 => ['DCCCVLI'],
847 => ['DCCCVLII'],
848 => ['DCCCVLIII'],
849 => ['DCCCVLIV', 'DCCCIL'],
895 => ['DCCCVC'],
896 => ['DCCCVCI'],
897 => ['DCCCVCII'],
898 => ['DCCCVCIII'],
899 => ['DCCCVCIV', 'DCCCIC'],
945 => ['CMVL'],
946 => ['CMVLI'],
947 => ['CMVLII'],
948 => ['CMVLIII'],
949 => ['CMVLIV', 'CMIL'],
950 => ['LM'],
951 => ['LMI'],
952 => ['LMII'],
953 => ['LMIII'],
954 => ['LMIV'],
955 => ['LMV'],
956 => ['LMVI'],
957 => ['LMVII'],
958 => ['LMVIII'],
959 => ['LMIX'],
960 => ['LMX'],
961 => ['LMXI'],
962 => ['LMXII'],
963 => ['LMXIII'],
964 => ['LMXIV'],
965 => ['LMXV'],
966 => ['LMXVI'],
967 => ['LMXVII'],
968 => ['LMXVIII'],
969 => ['LMXIX'],
970 => ['LMXX'],
971 => ['LMXXI'],
972 => ['LMXXII'],
973 => ['LMXXIII'],
974 => ['LMXXIV'],
975 => ['LMXXV'],
976 => ['LMXXVI'],
977 => ['LMXXVII'],
978 => ['LMXXVIII'],
979 => ['LMXXIX'],
980 => ['LMXXX'],
981 => ['LMXXXI'],
982 => ['LMXXXII'],
983 => ['LMXXXIII'],
984 => ['LMXXXIV'],
985 => ['LMXXXV'],
986 => ['LMXXXVI'],
987 => ['LMXXXVII'],
988 => ['LMXXXVIII'],
989 => ['LMXXXIX'],
990 => ['LMXL', 'XM'],
991 => ['LMXLI', 'XMI'],
992 => ['LMXLII', 'XMII'],
993 => ['LMXLIII', 'XMIII'],
994 => ['LMXLIV', 'XMIV'],
995 => ['LMVL', 'XMV', 'VM'],
996 => ['LMVLI', 'XMVI', 'VMI'],
997 => ['LMVLII', 'XMVII', 'VMII'],
998 => ['LMVLIII', 'XMVIII', 'VMIII'],
999 => ['LMVLIV', 'XMIX', 'VMIV', 'IM'],
1045 => ['MVL'],
1046 => ['MVLI'],
1047 => ['MVLII'],
1048 => ['MVLIII'],
1049 => ['MVLIV', 'MIL'],
1095 => ['MVC'],
1096 => ['MVCI'],
1097 => ['MVCII'],
1098 => ['MVCIII'],
1099 => ['MVCIV', 'MIC'],
1145 => ['MCVL'],
1146 => ['MCVLI'],
1147 => ['MCVLII'],
1148 => ['MCVLIII'],
1149 => ['MCVLIV', 'MCIL'],
1195 => ['MCVC'],
1196 => ['MCVCI'],
1197 => ['MCVCII'],
1198 => ['MCVCIII'],
1199 => ['MCVCIV', 'MCIC'],
1245 => ['MCCVL'],
1246 => ['MCCVLI'],
1247 => ['MCCVLII'],
1248 => ['MCCVLIII'],
1249 => ['MCCVLIV', 'MCCIL'],
1295 => ['MCCVC'],
1296 => ['MCCVCI'],
1297 => ['MCCVCII'],
1298 => ['MCCVCIII'],
1299 => ['MCCVCIV', 'MCCIC'],
1345 => ['MCCCVL'],
1346 => ['MCCCVLI'],
1347 => ['MCCCVLII'],
1348 => ['MCCCVLIII'],
1349 => ['MCCCVLIV', 'MCCCIL'],
1395 => ['MCCCVC'],
1396 => ['MCCCVCI'],
1397 => ['MCCCVCII'],
1398 => ['MCCCVCIII'],
1399 => ['MCCCVCIV', 'MCCCIC'],
1445 => ['MCDVL'],
1446 => ['MCDVLI'],
1447 => ['MCDVLII'],
1448 => ['MCDVLIII'],
1449 => ['MCDVLIV', 'MCDIL'],
1450 => ['MLD'],
1451 => ['MLDI'],
1452 => ['MLDII'],
1453 => ['MLDIII'],
1454 => ['MLDIV'],
1455 => ['MLDV'],
1456 => ['MLDVI'],
1457 => ['MLDVII'],
1458 => ['MLDVIII'],
1459 => ['MLDIX'],
1460 => ['MLDX'],
1461 => ['MLDXI'],
1462 => ['MLDXII'],
1463 => ['MLDXIII'],
1464 => ['MLDXIV'],
1465 => ['MLDXV'],
1466 => ['MLDXVI'],
1467 => ['MLDXVII'],
1468 => ['MLDXVIII'],
1469 => ['MLDXIX'],
1470 => ['MLDXX'],
1471 => ['MLDXXI'],
1472 => ['MLDXXII'],
1473 => ['MLDXXIII'],
1474 => ['MLDXXIV'],
1475 => ['MLDXXV'],
1476 => ['MLDXXVI'],
1477 => ['MLDXXVII'],
1478 => ['MLDXXVIII'],
1479 => ['MLDXXIX'],
1480 => ['MLDXXX'],
1481 => ['MLDXXXI'],
1482 => ['MLDXXXII'],
1483 => ['MLDXXXIII'],
1484 => ['MLDXXXIV'],
1485 => ['MLDXXXV'],
1486 => ['MLDXXXVI'],
1487 => ['MLDXXXVII'],
1488 => ['MLDXXXVIII'],
1489 => ['MLDXXXIX'],
1490 => ['MLDXL', 'MXD'],
1491 => ['MLDXLI', 'MXDI'],
1492 => ['MLDXLII', 'MXDII'],
1493 => ['MLDXLIII', 'MXDIII'],
1494 => ['MLDXLIV', 'MXDIV'],
1495 => ['MLDVL', 'MXDV', 'MVD'],
1496 => ['MLDVLI', 'MXDVI', 'MVDI'],
1497 => ['MLDVLII', 'MXDVII', 'MVDII'],
1498 => ['MLDVLIII', 'MXDVIII', 'MVDIII'],
1499 => ['MLDVLIV', 'MXDIX', 'MVDIV', 'MID'],
1545 => ['MDVL'],
1546 => ['MDVLI'],
1547 => ['MDVLII'],
1548 => ['MDVLIII'],
1549 => ['MDVLIV', 'MDIL'],
1595 => ['MDVC'],
1596 => ['MDVCI'],
1597 => ['MDVCII'],
1598 => ['MDVCIII'],
1599 => ['MDVCIV', 'MDIC'],
1645 => ['MDCVL'],
1646 => ['MDCVLI'],
1647 => ['MDCVLII'],
1648 => ['MDCVLIII'],
1649 => ['MDCVLIV', 'MDCIL'],
1695 => ['MDCVC'],
1696 => ['MDCVCI'],
1697 => ['MDCVCII'],
1698 => ['MDCVCIII'],
1699 => ['MDCVCIV', 'MDCIC'],
1745 => ['MDCCVL'],
1746 => ['MDCCVLI'],
1747 => ['MDCCVLII'],
1748 => ['MDCCVLIII'],
1749 => ['MDCCVLIV', 'MDCCIL'],
1795 => ['MDCCVC'],
1796 => ['MDCCVCI'],
1797 => ['MDCCVCII'],
1798 => ['MDCCVCIII'],
1799 => ['MDCCVCIV', 'MDCCIC'],
1845 => ['MDCCCVL'],
1846 => ['MDCCCVLI'],
1847 => ['MDCCCVLII'],
1848 => ['MDCCCVLIII'],
1849 => ['MDCCCVLIV', 'MDCCCIL'],
1895 => ['MDCCCVC'],
1896 => ['MDCCCVCI'],
1897 => ['MDCCCVCII'],
1898 => ['MDCCCVCIII'],
1899 => ['MDCCCVCIV', 'MDCCCIC'],
1945 => ['MCMVL'],
1946 => ['MCMVLI'],
1947 => ['MCMVLII'],
1948 => ['MCMVLIII'],
1949 => ['MCMVLIV', 'MCMIL'],
1950 => ['MLM'],
1951 => ['MLMI'],
1952 => ['MLMII'],
1953 => ['MLMIII'],
1954 => ['MLMIV'],
1955 => ['MLMV'],
1956 => ['MLMVI'],
1957 => ['MLMVII'],
1958 => ['MLMVIII'],
1959 => ['MLMIX'],
1960 => ['MLMX'],
1961 => ['MLMXI'],
1962 => ['MLMXII'],
1963 => ['MLMXIII'],
1964 => ['MLMXIV'],
1965 => ['MLMXV'],
1966 => ['MLMXVI'],
1967 => ['MLMXVII'],
1968 => ['MLMXVIII'],
1969 => ['MLMXIX'],
1970 => ['MLMXX'],
1971 => ['MLMXXI'],
1972 => ['MLMXXII'],
1973 => ['MLMXXIII'],
1974 => ['MLMXXIV'],
1975 => ['MLMXXV'],
1976 => ['MLMXXVI'],
1977 => ['MLMXXVII'],
1978 => ['MLMXXVIII'],
1979 => ['MLMXXIX'],
1980 => ['MLMXXX'],
1981 => ['MLMXXXI'],
1982 => ['MLMXXXII'],
1983 => ['MLMXXXIII'],
1984 => ['MLMXXXIV'],
1985 => ['MLMXXXV'],
1986 => ['MLMXXXVI'],
1987 => ['MLMXXXVII'],
1988 => ['MLMXXXVIII'],
1989 => ['MLMXXXIX'],
1990 => ['MLMXL', 'MXM'],
1991 => ['MLMXLI', 'MXMI'],
1992 => ['MLMXLII', 'MXMII'],
1993 => ['MLMXLIII', 'MXMIII'],
1994 => ['MLMXLIV', 'MXMIV'],
1995 => ['MLMVL', 'MXMV', 'MVM'],
1996 => ['MLMVLI', 'MXMVI', 'MVMI'],
1997 => ['MLMVLII', 'MXMVII', 'MVMII'],
1998 => ['MLMVLIII', 'MXMVIII', 'MVMIII'],
1999 => ['MLMVLIV', 'MXMIX', 'MVMIV', 'MIM'],
2045 => ['MMVL'],
2046 => ['MMVLI'],
2047 => ['MMVLII'],
2048 => ['MMVLIII'],
2049 => ['MMVLIV', 'MMIL'],
2095 => ['MMVC'],
2096 => ['MMVCI'],
2097 => ['MMVCII'],
2098 => ['MMVCIII'],
2099 => ['MMVCIV', 'MMIC'],
2145 => ['MMCVL'],
2146 => ['MMCVLI'],
2147 => ['MMCVLII'],
2148 => ['MMCVLIII'],
2149 => ['MMCVLIV', 'MMCIL'],
2195 => ['MMCVC'],
2196 => ['MMCVCI'],
2197 => ['MMCVCII'],
2198 => ['MMCVCIII'],
2199 => ['MMCVCIV', 'MMCIC'],
2245 => ['MMCCVL'],
2246 => ['MMCCVLI'],
2247 => ['MMCCVLII'],
2248 => ['MMCCVLIII'],
2249 => ['MMCCVLIV', 'MMCCIL'],
2295 => ['MMCCVC'],
2296 => ['MMCCVCI'],
2297 => ['MMCCVCII'],
2298 => ['MMCCVCIII'],
2299 => ['MMCCVCIV', 'MMCCIC'],
2345 => ['MMCCCVL'],
2346 => ['MMCCCVLI'],
2347 => ['MMCCCVLII'],
2348 => ['MMCCCVLIII'],
2349 => ['MMCCCVLIV', 'MMCCCIL'],
2395 => ['MMCCCVC'],
2396 => ['MMCCCVCI'],
2397 => ['MMCCCVCII'],
2398 => ['MMCCCVCIII'],
2399 => ['MMCCCVCIV', 'MMCCCIC'],
2445 => ['MMCDVL'],
2446 => ['MMCDVLI'],
2447 => ['MMCDVLII'],
2448 => ['MMCDVLIII'],
2449 => ['MMCDVLIV', 'MMCDIL'],
2450 => ['MMLD'],
2451 => ['MMLDI'],
2452 => ['MMLDII'],
2453 => ['MMLDIII'],
2454 => ['MMLDIV'],
2455 => ['MMLDV'],
2456 => ['MMLDVI'],
2457 => ['MMLDVII'],
2458 => ['MMLDVIII'],
2459 => ['MMLDIX'],
2460 => ['MMLDX'],
2461 => ['MMLDXI'],
2462 => ['MMLDXII'],
2463 => ['MMLDXIII'],
2464 => ['MMLDXIV'],
2465 => ['MMLDXV'],
2466 => ['MMLDXVI'],
2467 => ['MMLDXVII'],
2468 => ['MMLDXVIII'],
2469 => ['MMLDXIX'],
2470 => ['MMLDXX'],
2471 => ['MMLDXXI'],
2472 => ['MMLDXXII'],
2473 => ['MMLDXXIII'],
2474 => ['MMLDXXIV'],
2475 => ['MMLDXXV'],
2476 => ['MMLDXXVI'],
2477 => ['MMLDXXVII'],
2478 => ['MMLDXXVIII'],
2479 => ['MMLDXXIX'],
2480 => ['MMLDXXX'],
2481 => ['MMLDXXXI'],
2482 => ['MMLDXXXII'],
2483 => ['MMLDXXXIII'],
2484 => ['MMLDXXXIV'],
2485 => ['MMLDXXXV'],
2486 => ['MMLDXXXVI'],
2487 => ['MMLDXXXVII'],
2488 => ['MMLDXXXVIII'],
2489 => ['MMLDXXXIX'],
2490 => ['MMLDXL', 'MMXD'],
2491 => ['MMLDXLI', 'MMXDI'],
2492 => ['MMLDXLII', 'MMXDII'],
2493 => ['MMLDXLIII', 'MMXDIII'],
2494 => ['MMLDXLIV', 'MMXDIV'],
2495 => ['MMLDVL', 'MMXDV', 'MMVD'],
2496 => ['MMLDVLI', 'MMXDVI', 'MMVDI'],
2497 => ['MMLDVLII', 'MMXDVII', 'MMVDII'],
2498 => ['MMLDVLIII', 'MMXDVIII', 'MMVDIII'],
2499 => ['MMLDVLIV', 'MMXDIX', 'MMVDIV', 'MMID'],
2545 => ['MMDVL'],
2546 => ['MMDVLI'],
2547 => ['MMDVLII'],
2548 => ['MMDVLIII'],
2549 => ['MMDVLIV', 'MMDIL'],
2595 => ['MMDVC'],
2596 => ['MMDVCI'],
2597 => ['MMDVCII'],
2598 => ['MMDVCIII'],
2599 => ['MMDVCIV', 'MMDIC'],
2645 => ['MMDCVL'],
2646 => ['MMDCVLI'],
2647 => ['MMDCVLII'],
2648 => ['MMDCVLIII'],
2649 => ['MMDCVLIV', 'MMDCIL'],
2695 => ['MMDCVC'],
2696 => ['MMDCVCI'],
2697 => ['MMDCVCII'],
2698 => ['MMDCVCIII'],
2699 => ['MMDCVCIV', 'MMDCIC'],
2745 => ['MMDCCVL'],
2746 => ['MMDCCVLI'],
2747 => ['MMDCCVLII'],
2748 => ['MMDCCVLIII'],
2749 => ['MMDCCVLIV', 'MMDCCIL'],
2795 => ['MMDCCVC'],
2796 => ['MMDCCVCI'],
2797 => ['MMDCCVCII'],
2798 => ['MMDCCVCIII'],
2799 => ['MMDCCVCIV', 'MMDCCIC'],
2845 => ['MMDCCCVL'],
2846 => ['MMDCCCVLI'],
2847 => ['MMDCCCVLII'],
2848 => ['MMDCCCVLIII'],
2849 => ['MMDCCCVLIV', 'MMDCCCIL'],
2895 => ['MMDCCCVC'],
2896 => ['MMDCCCVCI'],
2897 => ['MMDCCCVCII'],
2898 => ['MMDCCCVCIII'],
2899 => ['MMDCCCVCIV', 'MMDCCCIC'],
2945 => ['MMCMVL'],
2946 => ['MMCMVLI'],
2947 => ['MMCMVLII'],
2948 => ['MMCMVLIII'],
2949 => ['MMCMVLIV', 'MMCMIL'],
2950 => ['MMLM'],
2951 => ['MMLMI'],
2952 => ['MMLMII'],
2953 => ['MMLMIII'],
2954 => ['MMLMIV'],
2955 => ['MMLMV'],
2956 => ['MMLMVI'],
2957 => ['MMLMVII'],
2958 => ['MMLMVIII'],
2959 => ['MMLMIX'],
2960 => ['MMLMX'],
2961 => ['MMLMXI'],
2962 => ['MMLMXII'],
2963 => ['MMLMXIII'],
2964 => ['MMLMXIV'],
2965 => ['MMLMXV'],
2966 => ['MMLMXVI'],
2967 => ['MMLMXVII'],
2968 => ['MMLMXVIII'],
2969 => ['MMLMXIX'],
2970 => ['MMLMXX'],
2971 => ['MMLMXXI'],
2972 => ['MMLMXXII'],
2973 => ['MMLMXXIII'],
2974 => ['MMLMXXIV'],
2975 => ['MMLMXXV'],
2976 => ['MMLMXXVI'],
2977 => ['MMLMXXVII'],
2978 => ['MMLMXXVIII'],
2979 => ['MMLMXXIX'],
2980 => ['MMLMXXX'],
2981 => ['MMLMXXXI'],
2982 => ['MMLMXXXII'],
2983 => ['MMLMXXXIII'],
2984 => ['MMLMXXXIV'],
2985 => ['MMLMXXXV'],
2986 => ['MMLMXXXVI'],
2987 => ['MMLMXXXVII'],
2988 => ['MMLMXXXVIII'],
2989 => ['MMLMXXXIX'],
2990 => ['MMLMXL', 'MMXM'],
2991 => ['MMLMXLI', 'MMXMI'],
2992 => ['MMLMXLII', 'MMXMII'],
2993 => ['MMLMXLIII', 'MMXMIII'],
2994 => ['MMLMXLIV', 'MMXMIV'],
2995 => ['MMLMVL', 'MMXMV', 'MMVM'],
2996 => ['MMLMVLI', 'MMXMVI', 'MMVMI'],
2997 => ['MMLMVLII', 'MMXMVII', 'MMVMII'],
2998 => ['MMLMVLIII', 'MMXMVIII', 'MMVMIII'],
2999 => ['MMLMVLIV', 'MMXMIX', 'MMVMIV', 'MMIM'],
3045 => ['MMMVL'],
3046 => ['MMMVLI'],
3047 => ['MMMVLII'],
3048 => ['MMMVLIII'],
3049 => ['MMMVLIV', 'MMMIL'],
3095 => ['MMMVC'],
3096 => ['MMMVCI'],
3097 => ['MMMVCII'],
3098 => ['MMMVCIII'],
3099 => ['MMMVCIV', 'MMMIC'],
3145 => ['MMMCVL'],
3146 => ['MMMCVLI'],
3147 => ['MMMCVLII'],
3148 => ['MMMCVLIII'],
3149 => ['MMMCVLIV', 'MMMCIL'],
3195 => ['MMMCVC'],
3196 => ['MMMCVCI'],
3197 => ['MMMCVCII'],
3198 => ['MMMCVCIII'],
3199 => ['MMMCVCIV', 'MMMCIC'],
3245 => ['MMMCCVL'],
3246 => ['MMMCCVLI'],
3247 => ['MMMCCVLII'],
3248 => ['MMMCCVLIII'],
3249 => ['MMMCCVLIV', 'MMMCCIL'],
3295 => ['MMMCCVC'],
3296 => ['MMMCCVCI'],
3297 => ['MMMCCVCII'],
3298 => ['MMMCCVCIII'],
3299 => ['MMMCCVCIV', 'MMMCCIC'],
3345 => ['MMMCCCVL'],
3346 => ['MMMCCCVLI'],
3347 => ['MMMCCCVLII'],
3348 => ['MMMCCCVLIII'],
3349 => ['MMMCCCVLIV', 'MMMCCCIL'],
3395 => ['MMMCCCVC'],
3396 => ['MMMCCCVCI'],
3397 => ['MMMCCCVCII'],
3398 => ['MMMCCCVCIII'],
3399 => ['MMMCCCVCIV', 'MMMCCCIC'],
3445 => ['MMMCDVL'],
3446 => ['MMMCDVLI'],
3447 => ['MMMCDVLII'],
3448 => ['MMMCDVLIII'],
3449 => ['MMMCDVLIV', 'MMMCDIL'],
3450 => ['MMMLD'],
3451 => ['MMMLDI'],
3452 => ['MMMLDII'],
3453 => ['MMMLDIII'],
3454 => ['MMMLDIV'],
3455 => ['MMMLDV'],
3456 => ['MMMLDVI'],
3457 => ['MMMLDVII'],
3458 => ['MMMLDVIII'],
3459 => ['MMMLDIX'],
3460 => ['MMMLDX'],
3461 => ['MMMLDXI'],
3462 => ['MMMLDXII'],
3463 => ['MMMLDXIII'],
3464 => ['MMMLDXIV'],
3465 => ['MMMLDXV'],
3466 => ['MMMLDXVI'],
3467 => ['MMMLDXVII'],
3468 => ['MMMLDXVIII'],
3469 => ['MMMLDXIX'],
3470 => ['MMMLDXX'],
3471 => ['MMMLDXXI'],
3472 => ['MMMLDXXII'],
3473 => ['MMMLDXXIII'],
3474 => ['MMMLDXXIV'],
3475 => ['MMMLDXXV'],
3476 => ['MMMLDXXVI'],
3477 => ['MMMLDXXVII'],
3478 => ['MMMLDXXVIII'],
3479 => ['MMMLDXXIX'],
3480 => ['MMMLDXXX'],
3481 => ['MMMLDXXXI'],
3482 => ['MMMLDXXXII'],
3483 => ['MMMLDXXXIII'],
3484 => ['MMMLDXXXIV'],
3485 => ['MMMLDXXXV'],
3486 => ['MMMLDXXXVI'],
3487 => ['MMMLDXXXVII'],
3488 => ['MMMLDXXXVIII'],
3489 => ['MMMLDXXXIX'],
3490 => ['MMMLDXL', 'MMMXD'],
3491 => ['MMMLDXLI', 'MMMXDI'],
3492 => ['MMMLDXLII', 'MMMXDII'],
3493 => ['MMMLDXLIII', 'MMMXDIII'],
3494 => ['MMMLDXLIV', 'MMMXDIV'],
3495 => ['MMMLDVL', 'MMMXDV', 'MMMVD'],
3496 => ['MMMLDVLI', 'MMMXDVI', 'MMMVDI'],
3497 => ['MMMLDVLII', 'MMMXDVII', 'MMMVDII'],
3498 => ['MMMLDVLIII', 'MMMXDVIII', 'MMMVDIII'],
3499 => ['MMMLDVLIV', 'MMMXDIX', 'MMMVDIV', 'MMMID'],
3545 => ['MMMDVL'],
3546 => ['MMMDVLI'],
3547 => ['MMMDVLII'],
3548 => ['MMMDVLIII'],
3549 => ['MMMDVLIV', 'MMMDIL'],
3595 => ['MMMDVC'],
3596 => ['MMMDVCI'],
3597 => ['MMMDVCII'],
3598 => ['MMMDVCIII'],
3599 => ['MMMDVCIV', 'MMMDIC'],
3645 => ['MMMDCVL'],
3646 => ['MMMDCVLI'],
3647 => ['MMMDCVLII'],
3648 => ['MMMDCVLIII'],
3649 => ['MMMDCVLIV', 'MMMDCIL'],
3695 => ['MMMDCVC'],
3696 => ['MMMDCVCI'],
3697 => ['MMMDCVCII'],
3698 => ['MMMDCVCIII'],
3699 => ['MMMDCVCIV', 'MMMDCIC'],
3745 => ['MMMDCCVL'],
3746 => ['MMMDCCVLI'],
3747 => ['MMMDCCVLII'],
3748 => ['MMMDCCVLIII'],
3749 => ['MMMDCCVLIV', 'MMMDCCIL'],
3795 => ['MMMDCCVC'],
3796 => ['MMMDCCVCI'],
3797 => ['MMMDCCVCII'],
3798 => ['MMMDCCVCIII'],
3799 => ['MMMDCCVCIV', 'MMMDCCIC'],
3845 => ['MMMDCCCVL'],
3846 => ['MMMDCCCVLI'],
3847 => ['MMMDCCCVLII'],
3848 => ['MMMDCCCVLIII'],
3849 => ['MMMDCCCVLIV', 'MMMDCCCIL'],
3895 => ['MMMDCCCVC'],
3896 => ['MMMDCCCVCI'],
3897 => ['MMMDCCCVCII'],
3898 => ['MMMDCCCVCIII'],
3899 => ['MMMDCCCVCIV', 'MMMDCCCIC'],
3945 => ['MMMCMVL'],
3946 => ['MMMCMVLI'],
3947 => ['MMMCMVLII'],
3948 => ['MMMCMVLIII'],
3949 => ['MMMCMVLIV', 'MMMCMIL'],
3950 => ['MMMLM'],
3951 => ['MMMLMI'],
3952 => ['MMMLMII'],
3953 => ['MMMLMIII'],
3954 => ['MMMLMIV'],
3955 => ['MMMLMV'],
3956 => ['MMMLMVI'],
3957 => ['MMMLMVII'],
3958 => ['MMMLMVIII'],
3959 => ['MMMLMIX'],
3960 => ['MMMLMX'],
3961 => ['MMMLMXI'],
3962 => ['MMMLMXII'],
3963 => ['MMMLMXIII'],
3964 => ['MMMLMXIV'],
3965 => ['MMMLMXV'],
3966 => ['MMMLMXVI'],
3967 => ['MMMLMXVII'],
3968 => ['MMMLMXVIII'],
3969 => ['MMMLMXIX'],
3970 => ['MMMLMXX'],
3971 => ['MMMLMXXI'],
3972 => ['MMMLMXXII'],
3973 => ['MMMLMXXIII'],
3974 => ['MMMLMXXIV'],
3975 => ['MMMLMXXV'],
3976 => ['MMMLMXXVI'],
3977 => ['MMMLMXXVII'],
3978 => ['MMMLMXXVIII'],
3979 => ['MMMLMXXIX'],
3980 => ['MMMLMXXX'],
3981 => ['MMMLMXXXI'],
3982 => ['MMMLMXXXII'],
3983 => ['MMMLMXXXIII'],
3984 => ['MMMLMXXXIV'],
3985 => ['MMMLMXXXV'],
3986 => ['MMMLMXXXVI'],
3987 => ['MMMLMXXXVII'],
3988 => ['MMMLMXXXVIII'],
3989 => ['MMMLMXXXIX'],
3990 => ['MMMLMXL', 'MMMXM'],
3991 => ['MMMLMXLI', 'MMMXMI'],
3992 => ['MMMLMXLII', 'MMMXMII'],
3993 => ['MMMLMXLIII', 'MMMXMIII'],
3994 => ['MMMLMXLIV', 'MMMXMIV'],
3995 => ['MMMLMVL', 'MMMXMV', 'MMMVM'],
3996 => ['MMMLMVLI', 'MMMXMVI', 'MMMVMI'],
3997 => ['MMMLMVLII', 'MMMXMVII', 'MMMVMII'],
3998 => ['MMMLMVLIII', 'MMMXMVIII', 'MMMVMIII'],
3999 => ['MMMLMVLIV', 'MMMXMIX', 'MMMVMIV', 'MMMIM'],
];
private const THOUSANDS = ['', 'M', 'MM', 'MMM'];
private const HUNDREDS = ['', 'C', 'CC', 'CCC', 'CD', 'D', 'DC', 'DCC', 'DCCC', 'CM'];
private const TENS = ['', 'X', 'XX', 'XXX', 'XL', 'L', 'LX', 'LXX', 'LXXX', 'XC'];
private const ONES = ['', 'I', 'II', 'III', 'IV', 'V', 'VI', 'VII', 'VIII', 'IX'];
const MAX_ROMAN_VALUE = 3999;
const MAX_ROMAN_STYLE = 4;
private static function valueOk(int $aValue, int $style): string
{
$origValue = $aValue;
$m = \intdiv($aValue, 1000);
$aValue %= 1000;
$c = \intdiv($aValue, 100);
$aValue %= 100;
$t = \intdiv($aValue, 10);
$aValue %= 10;
$result = self::THOUSANDS[$m] . self::HUNDREDS[$c] . self::TENS[$t] . self::ONES[$aValue];
if ($style > 0) {
if (array_key_exists($origValue, self::VALUES)) {
$arr = self::VALUES[$origValue];
$idx = min($style, count($arr)) - 1;
$result = $arr[$idx];
}
}
return $result;
}
private static function styleOk(int $aValue, int $style): string
{
return ($aValue < 0 || $aValue > self::MAX_ROMAN_VALUE) ? Functions::VALUE() : self::valueOk($aValue, $style);
}
public static function calculateRoman(int $aValue, int $style): string
{
return ($style < 0 || $style > self::MAX_ROMAN_STYLE) ? Functions::VALUE() : self::styleOk($aValue, $style);
}
/**
* ROMAN.
*
* Converts a number to Roman numeral
*
* @param mixed $aValue Number to convert
* Or can be an array of numbers
* @param mixed $style Number indicating one of five possible forms
* Or can be an array of styles
*
* @return array|string Roman numeral, or a string containing an error
* 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 evaluate($aValue, $style = 0)
{
if (is_array($aValue) || is_array($style)) {
return self::evaluateArrayArguments([self::class, __FUNCTION__], $aValue, $style);
}
try {
$aValue = Helpers::validateNumericNullBool($aValue);
if (is_bool($style)) {
$style = $style ? 0 : 4;
}
$style = Helpers::validateNumericNullSubstitution($style, null);
} catch (Exception $e) {
return $e->getMessage();
}
return self::calculateRoman((int) $aValue, (int) $style);
}
}

218
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Round.php

@ -0,0 +1,218 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Calculation\MathTrig;
use PhpOffice\PhpSpreadsheet\Calculation\ArrayEnabled;
use PhpOffice\PhpSpreadsheet\Calculation\Exception;
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
class Round
{
use ArrayEnabled;
/**
* ROUND.
*
* Returns the result of builtin function round after validating args.
*
* @param mixed $number Should be numeric, or can be an array of numbers
* @param mixed $precision Should be int, or can be an array of numbers
*
* @return array|float|string Rounded number
* If an array of numbers is passed as the argument, then the returned result will also be an array
* with the same dimensions
*/
public static function round($number, $precision)
{
if (is_array($number) || is_array($precision)) {
return self::evaluateArrayArguments([self::class, __FUNCTION__], $number, $precision);
}
try {
$number = Helpers::validateNumericNullBool($number);
$precision = Helpers::validateNumericNullBool($precision);
} catch (Exception $e) {
return $e->getMessage();
}
return round($number, (int) $precision);
}
/**
* ROUNDUP.
*
* Rounds a number up to a specified number of decimal places
*
* @param array|float $number Number to round, or can be an array of numbers
* @param array|int $digits Number of digits to which you want to round $number, or can be an array of numbers
*
* @return array|float|string Rounded Number, or a string containing an error
* If an array of numbers is passed as the argument, then the returned result will also be an array
* with the same dimensions
*/
public static function up($number, $digits)
{
if (is_array($number) || is_array($digits)) {
return self::evaluateArrayArguments([self::class, __FUNCTION__], $number, $digits);
}
try {
$number = Helpers::validateNumericNullBool($number);
$digits = (int) Helpers::validateNumericNullSubstitution($digits, null);
} catch (Exception $e) {
return $e->getMessage();
}
if ($number == 0.0) {
return 0.0;
}
if ($number < 0.0) {
return round($number - 0.5 * 0.1 ** $digits, $digits, PHP_ROUND_HALF_DOWN);
}
return round($number + 0.5 * 0.1 ** $digits, $digits, PHP_ROUND_HALF_DOWN);
}
/**
* ROUNDDOWN.
*
* Rounds a number down to a specified number of decimal places
*
* @param array|float $number Number to round, or can be an array of numbers
* @param array|int $digits Number of digits to which you want to round $number, or can be an array of numbers
*
* @return array|float|string Rounded Number, or a string containing an error
* If an array of numbers is passed as the argument, then the returned result will also be an array
* with the same dimensions
*/
public static function down($number, $digits)
{
if (is_array($number) || is_array($digits)) {
return self::evaluateArrayArguments([self::class, __FUNCTION__], $number, $digits);
}
try {
$number = Helpers::validateNumericNullBool($number);
$digits = (int) Helpers::validateNumericNullSubstitution($digits, null);
} catch (Exception $e) {
return $e->getMessage();
}
if ($number == 0.0) {
return 0.0;
}
if ($number < 0.0) {
return round($number + 0.5 * 0.1 ** $digits, $digits, PHP_ROUND_HALF_UP);
}
return round($number - 0.5 * 0.1 ** $digits, $digits, PHP_ROUND_HALF_UP);
}
/**
* MROUND.
*
* Rounds a number to the nearest multiple of a specified value
*
* @param mixed $number Expect float. Number to round, or can be an array of numbers
* @param mixed $multiple Expect int. Multiple to which you want to round, or can be an array of numbers.
*
* @return array|float|string Rounded Number, or a string containing an error
* If an array of numbers is passed as the argument, then the returned result will also be an array
* with the same dimensions
*/
public static function multiple($number, $multiple)
{
if (is_array($number) || is_array($multiple)) {
return self::evaluateArrayArguments([self::class, __FUNCTION__], $number, $multiple);
}
try {
$number = Helpers::validateNumericNullSubstitution($number, 0);
$multiple = Helpers::validateNumericNullSubstitution($multiple, null);
} catch (Exception $e) {
return $e->getMessage();
}
if ($number == 0 || $multiple == 0) {
return 0;
}
if ((Helpers::returnSign($number)) == (Helpers::returnSign($multiple))) {
$multiplier = 1 / $multiple;
return round($number * $multiplier) / $multiplier;
}
return Functions::NAN();
}
/**
* EVEN.
*
* Returns number rounded up to the nearest even integer.
* You can use this function for processing items that come in twos. For example,
* a packing crate accepts rows of one or two items. The crate is full when
* the number of items, rounded up to the nearest two, matches the crate's
* capacity.
*
* Excel Function:
* EVEN(number)
*
* @param array|float $number Number to round, or can be an array of numbers
*
* @return array|float|string Rounded Number, or a string containing an error
* If an array of numbers is passed as the argument, then the returned result will also be an array
* with the same dimensions
*/
public static function even($number)
{
if (is_array($number)) {
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $number);
}
try {
$number = Helpers::validateNumericNullBool($number);
} catch (Exception $e) {
return $e->getMessage();
}
return Helpers::getEven($number);
}
/**
* ODD.
*
* Returns number rounded up to the nearest odd integer.
*
* @param array|float $number Number to round, or can be an array of numbers
*
* @return array|float|string Rounded Number, or a string containing an error
* If an array of numbers is passed as the argument, then the returned result will also be an array
* with the same dimensions
*/
public static function odd($number)
{
if (is_array($number)) {
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $number);
}
try {
$number = Helpers::validateNumericNullBool($number);
} catch (Exception $e) {
return $e->getMessage();
}
$significance = Helpers::returnSign($number);
if ($significance == 0) {
return 1;
}
$result = ceil($number / $significance) * $significance;
if ($result == Helpers::getEven($result)) {
$result += $significance;
}
return $result;
}
}

53
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/SeriesSum.php

@ -0,0 +1,53 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Calculation\MathTrig;
use PhpOffice\PhpSpreadsheet\Calculation\ArrayEnabled;
use PhpOffice\PhpSpreadsheet\Calculation\Exception;
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
class SeriesSum
{
use ArrayEnabled;
/**
* SERIESSUM.
*
* Returns the sum of a power series
*
* @param mixed $x Input value
* @param mixed $n Initial power
* @param mixed $m Step
* @param mixed[] $args An array of coefficients for the Data Series
*
* @return array|float|string The result, or a string containing an error
*/
public static function evaluate($x, $n, $m, ...$args)
{
if (is_array($x) || is_array($n) || is_array($m)) {
return self::evaluateArrayArgumentsSubset([self::class, __FUNCTION__], 3, $x, $n, $m, ...$args);
}
try {
$x = Helpers::validateNumericNullSubstitution($x, 0);
$n = Helpers::validateNumericNullSubstitution($n, 0);
$m = Helpers::validateNumericNullSubstitution($m, 0);
// Loop through arguments
$aArgs = Functions::flattenArray($args);
$returnValue = 0;
$i = 0;
foreach ($aArgs as $argx) {
if ($argx !== null) {
$arg = Helpers::validateNumericNullSubstitution($argx, 0);
$returnValue += $arg * $x ** ($n + ($m * $i));
++$i;
}
}
} catch (Exception $e) {
return $e->getMessage();
}
return $returnValue;
}
}

38
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Sign.php

@ -0,0 +1,38 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Calculation\MathTrig;
use PhpOffice\PhpSpreadsheet\Calculation\ArrayEnabled;
use PhpOffice\PhpSpreadsheet\Calculation\Exception;
class Sign
{
use ArrayEnabled;
/**
* SIGN.
*
* Determines the sign of a number. Returns 1 if the number is positive, zero (0)
* if the number is 0, and -1 if the number is negative.
*
* @param array|float $number Number to round, or can be an array of numbers
*
* @return array|int|string sign value, or a string containing an error
* If an array of numbers is passed as the argument, then the returned result will also be an array
* with the same dimensions
*/
public static function evaluate($number)
{
if (is_array($number)) {
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $number);
}
try {
$number = Helpers::validateNumericNullBool($number);
} catch (Exception $e) {
return $e->getMessage();
}
return Helpers::returnSign($number);
}
}

64
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Sqrt.php

@ -0,0 +1,64 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Calculation\MathTrig;
use PhpOffice\PhpSpreadsheet\Calculation\ArrayEnabled;
use PhpOffice\PhpSpreadsheet\Calculation\Exception;
class Sqrt
{
use ArrayEnabled;
/**
* SQRT.
*
* Returns the result of builtin function sqrt after validating args.
*
* @param mixed $number Should be numeric, or can be an array of numbers
*
* @return array|float|string square root
* If an array of numbers is passed as the argument, then the returned result will also be an array
* with the same dimensions
*/
public static function sqrt($number)
{
if (is_array($number)) {
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $number);
}
try {
$number = Helpers::validateNumericNullBool($number);
} catch (Exception $e) {
return $e->getMessage();
}
return Helpers::numberOrNan(sqrt($number));
}
/**
* SQRTPI.
*
* Returns the square root of (number * pi).
*
* @param array|float $number Number, or can be an array of numbers
*
* @return array|float|string Square Root of Number * Pi, or a string containing an error
* If an array of numbers is passed as the argument, then the returned result will also be an array
* with the same dimensions
*/
public static function pi($number)
{
if (is_array($number)) {
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $number);
}
try {
$number = Helpers::validateNumericNullSubstitution($number, 0);
Helpers::validateNotNegative($number);
} catch (Exception $e) {
return $e->getMessage();
}
return sqrt($number * M_PI);
}
}

114
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Subtotal.php

@ -0,0 +1,114 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Calculation\MathTrig;
use PhpOffice\PhpSpreadsheet\Calculation\Exception;
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
use PhpOffice\PhpSpreadsheet\Calculation\Statistical;
class Subtotal
{
/**
* @param mixed $cellReference
* @param mixed $args
*/
protected static function filterHiddenArgs($cellReference, $args): array
{
return array_filter(
$args,
function ($index) use ($cellReference) {
[, $row, ] = explode('.', $index);
return $cellReference->getWorksheet()->getRowDimension($row)->getVisible();
},
ARRAY_FILTER_USE_KEY
);
}
/**
* @param mixed $cellReference
* @param mixed $args
*/
protected static function filterFormulaArgs($cellReference, $args): array
{
return array_filter(
$args,
function ($index) use ($cellReference) {
[, $row, $column] = explode('.', $index);
$retVal = true;
if ($cellReference->getWorksheet()->cellExists($column . $row)) {
//take this cell out if it contains the SUBTOTAL or AGGREGATE functions in a formula
$isFormula = $cellReference->getWorksheet()->getCell($column . $row)->isFormula();
$cellFormula = !preg_match(
'/^=.*\b(SUBTOTAL|AGGREGATE)\s*\(/i',
$cellReference->getWorksheet()->getCell($column . $row)->getValue() ?? ''
);
$retVal = !$isFormula || $cellFormula;
}
return $retVal;
},
ARRAY_FILTER_USE_KEY
);
}
/** @var callable[] */
private const CALL_FUNCTIONS = [
1 => [Statistical\Averages::class, 'average'], // 1 and 101
[Statistical\Counts::class, 'COUNT'], // 2 and 102
[Statistical\Counts::class, 'COUNTA'], // 3 and 103
[Statistical\Maximum::class, 'max'], // 4 and 104
[Statistical\Minimum::class, 'min'], // 5 and 105
[Operations::class, 'product'], // 6 and 106
[Statistical\StandardDeviations::class, 'STDEV'], // 7 and 107
[Statistical\StandardDeviations::class, 'STDEVP'], // 8 and 108
[Sum::class, 'sumIgnoringStrings'], // 9 and 109
[Statistical\Variances::class, 'VAR'], // 10 and 110
[Statistical\Variances::class, 'VARP'], // 111 and 111
];
/**
* SUBTOTAL.
*
* Returns a subtotal in a list or database.
*
* @param mixed $functionType
* A number 1 to 11 that specifies which function to
* use in calculating subtotals within a range
* list
* Numbers 101 to 111 shadow the functions of 1 to 11
* but ignore any values in the range that are
* in hidden rows
* @param mixed[] $args A mixed data series of values
*
* @return float|string
*/
public static function evaluate($functionType, ...$args)
{
$cellReference = array_pop($args);
$aArgs = Functions::flattenArrayIndexed($args);
try {
$subtotal = (int) Helpers::validateNumericNullBool($functionType);
} catch (Exception $e) {
return $e->getMessage();
}
// Calculate
if ($subtotal > 100) {
$aArgs = self::filterHiddenArgs($cellReference, $aArgs);
$subtotal -= 100;
}
$aArgs = self::filterFormulaArgs($cellReference, $aArgs);
if (array_key_exists($subtotal, self::CALL_FUNCTIONS)) {
/** @var callable */
$call = self::CALL_FUNCTIONS[$subtotal];
return call_user_func_array($call, $aArgs);
}
return Functions::VALUE();
}
}

115
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Sum.php

@ -0,0 +1,115 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Calculation\MathTrig;
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
class Sum
{
/**
* SUM, ignoring non-numeric non-error strings. This is eventually used by SUMIF.
*
* SUM computes the sum of all the values and cells referenced in the argument list.
*
* Excel Function:
* SUM(value1[,value2[, ...]])
*
* @param mixed ...$args Data values
*
* @return float|string
*/
public static function sumIgnoringStrings(...$args)
{
$returnValue = 0;
// Loop through the arguments
foreach (Functions::flattenArray($args) as $arg) {
// Is it a numeric value?
if (is_numeric($arg)) {
$returnValue += $arg;
} elseif (Functions::isError($arg)) {
return $arg;
}
}
return $returnValue;
}
/**
* SUM, returning error for non-numeric strings. This is used by Excel SUM function.
*
* SUM computes the sum of all the values and cells referenced in the argument list.
*
* Excel Function:
* SUM(value1[,value2[, ...]])
*
* @param mixed ...$args Data values
*
* @return float|string
*/
public static function sumErroringStrings(...$args)
{
$returnValue = 0;
// Loop through the arguments
$aArgs = Functions::flattenArrayIndexed($args);
foreach ($aArgs as $k => $arg) {
// Is it a numeric value?
if (is_numeric($arg) || empty($arg)) {
if (is_string($arg)) {
$arg = (int) $arg;
}
$returnValue += $arg;
} elseif (is_bool($arg)) {
$returnValue += (int) $arg;
} elseif (Functions::isError($arg)) {
return $arg;
// ignore non-numerics from cell, but fail as literals (except null)
} elseif ($arg !== null && !Functions::isCellValue($k)) {
return Functions::VALUE();
}
}
return $returnValue;
}
/**
* SUMPRODUCT.
*
* Excel Function:
* SUMPRODUCT(value1[,value2[, ...]])
*
* @param mixed ...$args Data values
*
* @return float|string The result, or a string containing an error
*/
public static function product(...$args)
{
$arrayList = $args;
$wrkArray = Functions::flattenArray(array_shift($arrayList));
$wrkCellCount = count($wrkArray);
for ($i = 0; $i < $wrkCellCount; ++$i) {
if ((!is_numeric($wrkArray[$i])) || (is_string($wrkArray[$i]))) {
$wrkArray[$i] = 0;
}
}
foreach ($arrayList as $matrixData) {
$array2 = Functions::flattenArray($matrixData);
$count = count($array2);
if ($wrkCellCount != $count) {
return Functions::VALUE();
}
foreach ($array2 as $i => $val) {
if ((!is_numeric($val)) || (is_string($val))) {
$val = 0;
}
$wrkArray[$i] *= $val;
}
}
return array_sum($wrkArray);
}
}

142
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/SumSquares.php

@ -0,0 +1,142 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Calculation\MathTrig;
use PhpOffice\PhpSpreadsheet\Calculation\Exception;
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
class SumSquares
{
/**
* SUMSQ.
*
* SUMSQ returns the sum of the squares of the arguments
*
* Excel Function:
* SUMSQ(value1[,value2[, ...]])
*
* @param mixed ...$args Data values
*
* @return float|string
*/
public static function sumSquare(...$args)
{
try {
$returnValue = 0;
// Loop through arguments
foreach (Functions::flattenArray($args) as $arg) {
$arg1 = Helpers::validateNumericNullSubstitution($arg, 0);
$returnValue += ($arg1 * $arg1);
}
} catch (Exception $e) {
return $e->getMessage();
}
return $returnValue;
}
private static function getCount(array $array1, array $array2): int
{
$count = count($array1);
if ($count !== count($array2)) {
throw new Exception(Functions::NA());
}
return $count;
}
/**
* These functions accept only numeric arguments, not even strings which are numeric.
*
* @param mixed $item
*/
private static function numericNotString($item): bool
{
return is_numeric($item) && !is_string($item);
}
/**
* SUMX2MY2.
*
* @param mixed[] $matrixData1 Matrix #1
* @param mixed[] $matrixData2 Matrix #2
*
* @return float|string
*/
public static function sumXSquaredMinusYSquared($matrixData1, $matrixData2)
{
try {
$array1 = Functions::flattenArray($matrixData1);
$array2 = Functions::flattenArray($matrixData2);
$count = self::getCount($array1, $array2);
$result = 0;
for ($i = 0; $i < $count; ++$i) {
if (self::numericNotString($array1[$i]) && self::numericNotString($array2[$i])) {
$result += ($array1[$i] * $array1[$i]) - ($array2[$i] * $array2[$i]);
}
}
} catch (Exception $e) {
return $e->getMessage();
}
return $result;
}
/**
* SUMX2PY2.
*
* @param mixed[] $matrixData1 Matrix #1
* @param mixed[] $matrixData2 Matrix #2
*
* @return float|string
*/
public static function sumXSquaredPlusYSquared($matrixData1, $matrixData2)
{
try {
$array1 = Functions::flattenArray($matrixData1);
$array2 = Functions::flattenArray($matrixData2);
$count = self::getCount($array1, $array2);
$result = 0;
for ($i = 0; $i < $count; ++$i) {
if (self::numericNotString($array1[$i]) && self::numericNotString($array2[$i])) {
$result += ($array1[$i] * $array1[$i]) + ($array2[$i] * $array2[$i]);
}
}
} catch (Exception $e) {
return $e->getMessage();
}
return $result;
}
/**
* SUMXMY2.
*
* @param mixed[] $matrixData1 Matrix #1
* @param mixed[] $matrixData2 Matrix #2
*
* @return float|string
*/
public static function sumXMinusYSquared($matrixData1, $matrixData2)
{
try {
$array1 = Functions::flattenArray($matrixData1);
$array2 = Functions::flattenArray($matrixData2);
$count = self::getCount($array1, $array2);
$result = 0;
for ($i = 0; $i < $count; ++$i) {
if (self::numericNotString($array1[$i]) && self::numericNotString($array2[$i])) {
$result += ($array1[$i] - $array2[$i]) * ($array1[$i] - $array2[$i]);
}
}
} catch (Exception $e) {
return $e->getMessage();
}
return $result;
}
}

64
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Trig/Secant.php

@ -0,0 +1,64 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Calculation\MathTrig\Trig;
use PhpOffice\PhpSpreadsheet\Calculation\ArrayEnabled;
use PhpOffice\PhpSpreadsheet\Calculation\Exception;
use PhpOffice\PhpSpreadsheet\Calculation\MathTrig\Helpers;
class Secant
{
use ArrayEnabled;
/**
* SEC.
*
* Returns the secant of an angle.
*
* @param array|float $angle Number, or can be an array of numbers
*
* @return array|float|string The secant of the angle
* If an array of numbers is passed as the argument, then the returned result will also be an array
* with the same dimensions
*/
public static function sec($angle)
{
if (is_array($angle)) {
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $angle);
}
try {
$angle = Helpers::validateNumericNullBool($angle);
} catch (Exception $e) {
return $e->getMessage();
}
return Helpers::verySmallDenominator(1.0, cos($angle));
}
/**
* SECH.
*
* Returns the hyperbolic secant of an angle.
*
* @param array|float $angle Number, or can be an array of numbers
*
* @return array|float|string The hyperbolic secant of the angle
* If an array of numbers is passed as the argument, then the returned result will also be an array
* with the same dimensions
*/
public static function sech($angle)
{
if (is_array($angle)) {
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $angle);
}
try {
$angle = Helpers::validateNumericNullBool($angle);
} catch (Exception $e) {
return $e->getMessage();
}
return Helpers::verySmallDenominator(1.0, cosh($angle));
}
}

116
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Trig/Sine.php

@ -0,0 +1,116 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Calculation\MathTrig\Trig;
use PhpOffice\PhpSpreadsheet\Calculation\ArrayEnabled;
use PhpOffice\PhpSpreadsheet\Calculation\Exception;
use PhpOffice\PhpSpreadsheet\Calculation\MathTrig\Helpers;
class Sine
{
use ArrayEnabled;
/**
* SIN.
*
* Returns the result of builtin function sin after validating args.
*
* @param mixed $angle Should be numeric, or can be an array of numbers
*
* @return array|float|string sine
* If an array of numbers is passed as the argument, then the returned result will also be an array
* with the same dimensions
*/
public static function sin($angle)
{
if (is_array($angle)) {
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $angle);
}
try {
$angle = Helpers::validateNumericNullBool($angle);
} catch (Exception $e) {
return $e->getMessage();
}
return sin($angle);
}
/**
* SINH.
*
* Returns the result of builtin function sinh after validating args.
*
* @param mixed $angle Should be numeric, or can be an array of numbers
*
* @return array|float|string hyperbolic sine
* If an array of numbers is passed as the argument, then the returned result will also be an array
* with the same dimensions
*/
public static function sinh($angle)
{
if (is_array($angle)) {
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $angle);
}
try {
$angle = Helpers::validateNumericNullBool($angle);
} catch (Exception $e) {
return $e->getMessage();
}
return sinh($angle);
}
/**
* ASIN.
*
* Returns the arcsine of a number.
*
* @param array|float $number Number, or can be an array of numbers
*
* @return array|float|string The arcsine of the number
* If an array of numbers is passed as the argument, then the returned result will also be an array
* with the same dimensions
*/
public static function asin($number)
{
if (is_array($number)) {
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $number);
}
try {
$number = Helpers::validateNumericNullBool($number);
} catch (Exception $e) {
return $e->getMessage();
}
return Helpers::numberOrNan(asin($number));
}
/**
* ASINH.
*
* Returns the inverse hyperbolic sine of a number.
*
* @param array|float $number Number, or can be an array of numbers
*
* @return array|float|string The inverse hyperbolic sine of the number
* If an array of numbers is passed as the argument, then the returned result will also be an array
* with the same dimensions
*/
public static function asinh($number)
{
if (is_array($number)) {
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $number);
}
try {
$number = Helpers::validateNumericNullBool($number);
} catch (Exception $e) {
return $e->getMessage();
}
return Helpers::numberOrNan(asinh($number));
}
}

161
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Trig/Tangent.php

@ -0,0 +1,161 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Calculation\MathTrig\Trig;
use PhpOffice\PhpSpreadsheet\Calculation\ArrayEnabled;
use PhpOffice\PhpSpreadsheet\Calculation\Exception;
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
use PhpOffice\PhpSpreadsheet\Calculation\MathTrig\Helpers;
class Tangent
{
use ArrayEnabled;
/**
* TAN.
*
* Returns the result of builtin function tan after validating args.
*
* @param mixed $angle Should be numeric, or can be an array of numbers
*
* @return array|float|string tangent
* If an array of numbers is passed as the argument, then the returned result will also be an array
* with the same dimensions
*/
public static function tan($angle)
{
if (is_array($angle)) {
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $angle);
}
try {
$angle = Helpers::validateNumericNullBool($angle);
} catch (Exception $e) {
return $e->getMessage();
}
return Helpers::verySmallDenominator(sin($angle), cos($angle));
}
/**
* TANH.
*
* Returns the result of builtin function sinh after validating args.
*
* @param mixed $angle Should be numeric, or can be an array of numbers
*
* @return array|float|string hyperbolic tangent
* If an array of numbers is passed as the argument, then the returned result will also be an array
* with the same dimensions
*/
public static function tanh($angle)
{
if (is_array($angle)) {
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $angle);
}
try {
$angle = Helpers::validateNumericNullBool($angle);
} catch (Exception $e) {
return $e->getMessage();
}
return tanh($angle);
}
/**
* ATAN.
*
* Returns the arctangent of a number.
*
* @param array|float $number Number, or can be an array of numbers
*
* @return array|float|string The arctangent of the number
* If an array of numbers is passed as the argument, then the returned result will also be an array
* with the same dimensions
*/
public static function atan($number)
{
if (is_array($number)) {
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $number);
}
try {
$number = Helpers::validateNumericNullBool($number);
} catch (Exception $e) {
return $e->getMessage();
}
return Helpers::numberOrNan(atan($number));
}
/**
* ATANH.
*
* Returns the inverse hyperbolic tangent of a number.
*
* @param array|float $number Number, or can be an array of numbers
*
* @return array|float|string The inverse hyperbolic tangent of the number
* If an array of numbers is passed as the argument, then the returned result will also be an array
* with the same dimensions
*/
public static function atanh($number)
{
if (is_array($number)) {
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $number);
}
try {
$number = Helpers::validateNumericNullBool($number);
} catch (Exception $e) {
return $e->getMessage();
}
return Helpers::numberOrNan(atanh($number));
}
/**
* ATAN2.
*
* This function calculates the arc tangent of the two variables x and y. It is similar to
* calculating the arc tangent of y ÷ x, except that the signs of both arguments are used
* to determine the quadrant of the result.
* The arctangent is the angle from the x-axis to a line containing the origin (0, 0) and a
* point with coordinates (xCoordinate, yCoordinate). The angle is given in radians between
* -pi and pi, excluding -pi.
*
* Note that the Excel ATAN2() function accepts its arguments in the reverse order to the standard
* PHP atan2() function, so we need to reverse them here before calling the PHP atan() function.
*
* Excel Function:
* ATAN2(xCoordinate,yCoordinate)
*
* @param mixed $xCoordinate should be float, the x-coordinate of the point, or can be an array of numbers
* @param mixed $yCoordinate should be float, the y-coordinate of the point, or can be an array of numbers
*
* @return array|float|string
* The inverse tangent of the specified x- and y-coordinates, or a string containing an error
* If an array of numbers is passed as one of the arguments, then the returned result will also be an array
* with the same dimensions
*/
public static function atan2($xCoordinate, $yCoordinate)
{
if (is_array($xCoordinate) || is_array($yCoordinate)) {
return self::evaluateArrayArguments([self::class, __FUNCTION__], $xCoordinate, $yCoordinate);
}
try {
$xCoordinate = Helpers::validateNumericNullBool($xCoordinate);
$yCoordinate = Helpers::validateNumericNullBool($yCoordinate);
} catch (Exception $e) {
return $e->getMessage();
}
if (($xCoordinate == 0) && ($yCoordinate == 0)) {
return Functions::DIV0();
}
return atan2($yCoordinate, $xCoordinate);
}
}

50
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/MathTrig/Trunc.php

@ -0,0 +1,50 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Calculation\MathTrig;
use PhpOffice\PhpSpreadsheet\Calculation\ArrayEnabled;
use PhpOffice\PhpSpreadsheet\Calculation\Exception;
class Trunc
{
use ArrayEnabled;
/**
* TRUNC.
*
* Truncates value to the number of fractional digits by number_digits.
*
* @param array|float $value
* Or can be an array of values
* @param array|int $digits
* Or can be an array of values
*
* @return array|float|string Truncated value, or a string containing an error
* 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 evaluate($value = 0, $digits = 0)
{
if (is_array($value) || is_array($digits)) {
return self::evaluateArrayArguments([self::class, __FUNCTION__], $value, $digits);
}
try {
$value = Helpers::validateNumericNullBool($value);
$digits = Helpers::validateNumericNullSubstitution($digits, null);
} catch (Exception $e) {
return $e->getMessage();
}
$digits = floor($digits);
// Truncate
$adjust = 10 ** $digits;
if (($digits > 0) && (rtrim((string) (int) ((abs($value) - abs((int) $value)) * $adjust), '0') < $adjust / 10)) {
return $value;
}
return ((int) ($value * $adjust)) / $adjust;
}
}

1820
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical.php

File diff suppressed because it is too large

62
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/Poisson.php

@ -0,0 +1,62 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Calculation\Statistical\Distributions;
use PhpOffice\PhpSpreadsheet\Calculation\ArrayEnabled;
use PhpOffice\PhpSpreadsheet\Calculation\Exception;
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
use PhpOffice\PhpSpreadsheet\Calculation\MathTrig;
class Poisson
{
use ArrayEnabled;
/**
* POISSON.
*
* Returns the Poisson distribution. A common application of the Poisson distribution
* is predicting the number of events over a specific time, such as the number of
* cars arriving at a toll plaza in 1 minute.
*
* @param mixed $value Float value for which we want the probability
* Or can be an array of values
* @param mixed $mean Mean value as a float
* Or can be an array of values
* @param mixed $cumulative Boolean value indicating if we want the cdf (true) or the pdf (false)
* Or can be an array of values
*
* @return array|float|string The result, or a string containing an error
* 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 distribution($value, $mean, $cumulative)
{
if (is_array($value) || is_array($mean) || is_array($cumulative)) {
return self::evaluateArrayArguments([self::class, __FUNCTION__], $value, $mean, $cumulative);
}
try {
$value = DistributionValidations::validateFloat($value);
$mean = DistributionValidations::validateFloat($mean);
$cumulative = DistributionValidations::validateBool($cumulative);
} catch (Exception $e) {
return $e->getMessage();
}
if (($value < 0) || ($mean < 0)) {
return Functions::NAN();
}
if ($cumulative) {
$summer = 0;
$floor = floor($value);
for ($i = 0; $i <= $floor; ++$i) {
$summer += $mean ** $i / MathTrig\Factorial::fact($i);
}
return exp(0 - $mean) * $summer;
}
return (exp(0 - $mean) * $mean ** $value) / MathTrig\Factorial::fact($value);
}
}

141
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/StandardNormal.php

@ -0,0 +1,141 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Calculation\Statistical\Distributions;
use PhpOffice\PhpSpreadsheet\Calculation\ArrayEnabled;
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
use PhpOffice\PhpSpreadsheet\Calculation\Statistical\Averages;
use PhpOffice\PhpSpreadsheet\Calculation\Statistical\StandardDeviations;
class StandardNormal
{
use ArrayEnabled;
/**
* NORMSDIST.
*
* Returns the standard normal cumulative distribution function. The distribution has
* a mean of 0 (zero) and a standard deviation of one. Use this function in place of a
* table of standard normal curve areas.
*
* NOTE: We don't need to check for arrays to array-enable this function, because that is already
* handled by the logic in Normal::distribution()
* All we need to do is pass the value through as scalar or as array.
*
* @param mixed $value Float value for which we want the probability
* Or can be an array of values
*
* @return array|float|string The result, or a string containing an error
* 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 cumulative($value)
{
return Normal::distribution($value, 0, 1, true);
}
/**
* NORM.S.DIST.
*
* Returns the standard normal cumulative distribution function. The distribution has
* a mean of 0 (zero) and a standard deviation of one. Use this function in place of a
* table of standard normal curve areas.
*
* NOTE: We don't need to check for arrays to array-enable this function, because that is already
* handled by the logic in Normal::distribution()
* All we need to do is pass the value and cumulative through as scalar or as array.
*
* @param mixed $value Float value for which we want the probability
* Or can be an array of values
* @param mixed $cumulative Boolean value indicating if we want the cdf (true) or the pdf (false)
* Or can be an array of values
*
* @return array|float|string The result, or a string containing an error
* 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 distribution($value, $cumulative)
{
return Normal::distribution($value, 0, 1, $cumulative);
}
/**
* NORMSINV.
*
* Returns the inverse of the standard normal cumulative distribution
*
* @param mixed $value float probability for which we want the value
* Or can be an array of values
*
* NOTE: We don't need to check for arrays to array-enable this function, because that is already
* handled by the logic in Normal::inverse()
* All we need to do is pass the value through as scalar or as array
*
* @return array|float|string The result, or a string containing an error
* 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 inverse($value)
{
return Normal::inverse($value, 0, 1);
}
/**
* GAUSS.
*
* Calculates the probability that a member of a standard normal population will fall between
* the mean and z standard deviations from the mean.
*
* @param mixed $value
* Or can be an array of values
*
* @return array|float|string The result, or a string containing an error
* 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 gauss($value)
{
if (is_array($value)) {
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $value);
}
if (!is_numeric($value)) {
return Functions::VALUE();
}
return self::distribution($value, true) - 0.5;
}
/**
* ZTEST.
*
* Returns the one-tailed P-value of a z-test.
*
* For a given hypothesized population mean, x, Z.TEST returns the probability that the sample mean would be
* greater than the average of observations in the data set (array) — that is, the observed sample mean.
*
* @param mixed $dataSet The dataset should be an array of float values for the observations
* @param mixed $m0 Alpha Parameter
* @param mixed $sigma A null or float value for the Beta (Standard Deviation) Parameter;
* if null, we use the standard deviation of the dataset
*
* @return float|string (string if result is an error)
*/
public static function zTest($dataSet, $m0, $sigma = null)
{
$dataSet = Functions::flattenArrayIndexed($dataSet);
$m0 = Functions::flattenSingleValue($m0);
$sigma = Functions::flattenSingleValue($sigma);
if (!is_numeric($m0) || ($sigma !== null && !is_numeric($sigma))) {
return Functions::VALUE();
}
if ($sigma === null) {
$sigma = StandardDeviations::STDEV($dataSet);
}
$n = count($dataSet);
return 1 - self::cumulative((Averages::average($dataSet) - $m0) / ($sigma / sqrt($n)));
}
}

138
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/StudentT.php

@ -0,0 +1,138 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Calculation\Statistical\Distributions;
use PhpOffice\PhpSpreadsheet\Calculation\ArrayEnabled;
use PhpOffice\PhpSpreadsheet\Calculation\Exception;
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
class StudentT
{
use ArrayEnabled;
private const MAX_ITERATIONS = 256;
/**
* TDIST.
*
* Returns the probability of Student's T distribution.
*
* @param mixed $value Float value for the distribution
* Or can be an array of values
* @param mixed $degrees Integer value for degrees of freedom
* Or can be an array of values
* @param mixed $tails Integer value for the number of tails (1 or 2)
* Or can be an array of values
*
* @return array|float|string The result, or a string containing an error
* 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 distribution($value, $degrees, $tails)
{
if (is_array($value) || is_array($degrees) || is_array($tails)) {
return self::evaluateArrayArguments([self::class, __FUNCTION__], $value, $degrees, $tails);
}
try {
$value = DistributionValidations::validateFloat($value);
$degrees = DistributionValidations::validateInt($degrees);
$tails = DistributionValidations::validateInt($tails);
} catch (Exception $e) {
return $e->getMessage();
}
if (($value < 0) || ($degrees < 1) || ($tails < 1) || ($tails > 2)) {
return Functions::NAN();
}
return self::calculateDistribution($value, $degrees, $tails);
}
/**
* TINV.
*
* Returns the one-tailed probability of the chi-squared distribution.
*
* @param mixed $probability Float probability for the function
* Or can be an array of values
* @param mixed $degrees Integer value for degrees of freedom
* Or can be an array of values
*
* @return array|float|string The result, or a string containing an error
* 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 inverse($probability, $degrees)
{
if (is_array($probability) || is_array($degrees)) {
return self::evaluateArrayArguments([self::class, __FUNCTION__], $probability, $degrees);
}
try {
$probability = DistributionValidations::validateProbability($probability);
$degrees = DistributionValidations::validateInt($degrees);
} catch (Exception $e) {
return $e->getMessage();
}
if ($degrees <= 0) {
return Functions::NAN();
}
$callback = function ($value) use ($degrees) {
return self::distribution($value, $degrees, 2);
};
$newtonRaphson = new NewtonRaphson($callback);
return $newtonRaphson->execute($probability);
}
/**
* @return float
*/
private static function calculateDistribution(float $value, int $degrees, int $tails)
{
// tdist, which finds the probability that corresponds to a given value
// of t with k degrees of freedom. This algorithm is translated from a
// pascal function on p81 of "Statistical Computing in Pascal" by D
// Cooke, A H Craven & G M Clark (1985: Edward Arnold (Pubs.) Ltd:
// London). The above Pascal algorithm is itself a translation of the
// fortran algoritm "AS 3" by B E Cooper of the Atlas Computer
// Laboratory as reported in (among other places) "Applied Statistics
// Algorithms", editied by P Griffiths and I D Hill (1985; Ellis
// Horwood Ltd.; W. Sussex, England).
$tterm = $degrees;
$ttheta = atan2($value, sqrt($tterm));
$tc = cos($ttheta);
$ts = sin($ttheta);
if (($degrees % 2) === 1) {
$ti = 3;
$tterm = $tc;
} else {
$ti = 2;
$tterm = 1;
}
$tsum = $tterm;
while ($ti < $degrees) {
$tterm *= $tc * $tc * ($ti - 1) / $ti;
$tsum += $tterm;
$ti += 2;
}
$tsum *= $ts;
if (($degrees % 2) == 1) {
$tsum = Functions::M_2DIVPI * ($tsum + $ttheta);
}
$tValue = 0.5 * (1 + $tsum);
if ($tails == 1) {
return 1 - abs($tValue);
}
return 1 - abs((1 - $tValue) - $tValue);
}
}

57
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Distributions/Weibull.php

@ -0,0 +1,57 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Calculation\Statistical\Distributions;
use PhpOffice\PhpSpreadsheet\Calculation\ArrayEnabled;
use PhpOffice\PhpSpreadsheet\Calculation\Exception;
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
class Weibull
{
use ArrayEnabled;
/**
* WEIBULL.
*
* Returns the Weibull distribution. Use this distribution in reliability
* analysis, such as calculating a device's mean time to failure.
*
* @param mixed $value Float value for the distribution
* Or can be an array of values
* @param mixed $alpha Float alpha Parameter
* Or can be an array of values
* @param mixed $beta Float beta Parameter
* Or can be an array of values
* @param mixed $cumulative Boolean value indicating if we want the cdf (true) or the pdf (false)
* Or can be an array of values
*
* @return array|float|string (string if result is an error)
* 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 distribution($value, $alpha, $beta, $cumulative)
{
if (is_array($value) || is_array($alpha) || is_array($beta) || is_array($cumulative)) {
return self::evaluateArrayArguments([self::class, __FUNCTION__], $value, $alpha, $beta, $cumulative);
}
try {
$value = DistributionValidations::validateFloat($value);
$alpha = DistributionValidations::validateFloat($alpha);
$beta = DistributionValidations::validateFloat($beta);
$cumulative = DistributionValidations::validateBool($cumulative);
} catch (Exception $e) {
return $e->getMessage();
}
if (($value < 0) || ($alpha <= 0) || ($beta <= 0)) {
return Functions::NAN();
}
if ($cumulative) {
return 1 - exp(0 - ($value / $beta) ** $alpha);
}
return ($alpha / $beta ** $alpha) * $value ** ($alpha - 1) * exp(0 - ($value / $beta) ** $alpha);
}
}

90
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Permutations.php

@ -0,0 +1,90 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Calculation\Statistical;
use PhpOffice\PhpSpreadsheet\Calculation\ArrayEnabled;
use PhpOffice\PhpSpreadsheet\Calculation\Exception;
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
use PhpOffice\PhpSpreadsheet\Calculation\MathTrig;
use PhpOffice\PhpSpreadsheet\Shared\IntOrFloat;
class Permutations
{
use ArrayEnabled;
/**
* PERMUT.
*
* Returns the number of permutations for a given number of objects that can be
* selected from number objects. A permutation is any set or subset of objects or
* events where internal order is significant. Permutations are different from
* combinations, for which the internal order is not significant. Use this function
* for lottery-style probability calculations.
*
* @param mixed $numObjs Integer number of different objects
* Or can be an array of values
* @param mixed $numInSet Integer number of objects in each permutation
* Or can be an array of values
*
* @return array|float|int|string Number of permutations, or a string containing an error
* 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 PERMUT($numObjs, $numInSet)
{
if (is_array($numObjs) || is_array($numInSet)) {
return self::evaluateArrayArguments([self::class, __FUNCTION__], $numObjs, $numInSet);
}
try {
$numObjs = StatisticalValidations::validateInt($numObjs);
$numInSet = StatisticalValidations::validateInt($numInSet);
} catch (Exception $e) {
return $e->getMessage();
}
if ($numObjs < $numInSet) {
return Functions::NAN();
}
$result = round(MathTrig\Factorial::fact($numObjs) / MathTrig\Factorial::fact($numObjs - $numInSet));
return IntOrFloat::evaluate($result);
}
/**
* PERMUTATIONA.
*
* Returns the number of permutations for a given number of objects (with repetitions)
* that can be selected from the total objects.
*
* @param mixed $numObjs Integer number of different objects
* Or can be an array of values
* @param mixed $numInSet Integer number of objects in each permutation
* Or can be an array of values
*
* @return array|float|int|string Number of permutations, or a string containing an error
* 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 PERMUTATIONA($numObjs, $numInSet)
{
if (is_array($numObjs) || is_array($numInSet)) {
return self::evaluateArrayArguments([self::class, __FUNCTION__], $numObjs, $numInSet);
}
try {
$numObjs = StatisticalValidations::validateInt($numObjs);
$numInSet = StatisticalValidations::validateInt($numInSet);
} catch (Exception $e) {
return $e->getMessage();
}
if ($numObjs < 0 || $numInSet < 0) {
return Functions::NAN();
}
$result = $numObjs ** $numInSet;
return IntOrFloat::evaluate($result);
}
}

96
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Size.php

@ -0,0 +1,96 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Calculation\Statistical;
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
class Size
{
/**
* LARGE.
*
* Returns the nth largest value in a data set. You can use this function to
* select a value based on its relative standing.
*
* Excel Function:
* LARGE(value1[,value2[, ...]],entry)
*
* @param mixed $args Data values
*
* @return float|string The result, or a string containing an error
*/
public static function large(...$args)
{
$aArgs = Functions::flattenArray($args);
$entry = array_pop($aArgs);
if ((is_numeric($entry)) && (!is_string($entry))) {
$entry = (int) floor($entry);
$mArgs = self::filter($aArgs);
$count = Counts::COUNT($mArgs);
--$entry;
if (($entry < 0) || ($entry >= $count) || ($count == 0)) {
return Functions::NAN();
}
rsort($mArgs);
return $mArgs[$entry];
}
return Functions::VALUE();
}
/**
* SMALL.
*
* Returns the nth smallest value in a data set. You can use this function to
* select a value based on its relative standing.
*
* Excel Function:
* SMALL(value1[,value2[, ...]],entry)
*
* @param mixed $args Data values
*
* @return float|string The result, or a string containing an error
*/
public static function small(...$args)
{
$aArgs = Functions::flattenArray($args);
$entry = array_pop($aArgs);
if ((is_numeric($entry)) && (!is_string($entry))) {
$entry = (int) floor($entry);
$mArgs = self::filter($aArgs);
$count = Counts::COUNT($mArgs);
--$entry;
if (($entry < 0) || ($entry >= $count) || ($count == 0)) {
return Functions::NAN();
}
sort($mArgs);
return $mArgs[$entry];
}
return Functions::VALUE();
}
/**
* @param mixed[] $args Data values
*/
protected static function filter(array $args): array
{
$mArgs = [];
foreach ($args as $arg) {
// Is it a numeric value?
if ((is_numeric($arg)) && (!is_string($arg))) {
$mArgs[] = $arg;
}
}
return $mArgs;
}
}

95
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/StandardDeviations.php

@ -0,0 +1,95 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Calculation\Statistical;
class StandardDeviations
{
/**
* STDEV.
*
* Estimates standard deviation based on a sample. The standard deviation is a measure of how
* widely values are dispersed from the average value (the mean).
*
* Excel Function:
* STDEV(value1[,value2[, ...]])
*
* @param mixed ...$args Data values
*
* @return float|string The result, or a string containing an error
*/
public static function STDEV(...$args)
{
$result = Variances::VAR(...$args);
if (!is_numeric($result)) {
return $result;
}
return sqrt((float) $result);
}
/**
* STDEVA.
*
* Estimates standard deviation based on a sample, including numbers, text, and logical values
*
* Excel Function:
* STDEVA(value1[,value2[, ...]])
*
* @param mixed ...$args Data values
*
* @return float|string
*/
public static function STDEVA(...$args)
{
$result = Variances::VARA(...$args);
if (!is_numeric($result)) {
return $result;
}
return sqrt((float) $result);
}
/**
* STDEVP.
*
* Calculates standard deviation based on the entire population
*
* Excel Function:
* STDEVP(value1[,value2[, ...]])
*
* @param mixed ...$args Data values
*
* @return float|string
*/
public static function STDEVP(...$args)
{
$result = Variances::VARP(...$args);
if (!is_numeric($result)) {
return $result;
}
return sqrt((float) $result);
}
/**
* STDEVPA.
*
* Calculates standard deviation based on the entire population, including numbers, text, and logical values
*
* Excel Function:
* STDEVPA(value1[,value2[, ...]])
*
* @param mixed ...$args Data values
*
* @return float|string
*/
public static function STDEVPA(...$args)
{
$result = Variances::VARPA(...$args);
if (!is_numeric($result)) {
return $result;
}
return sqrt((float) $result);
}
}

49
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Standardize.php

@ -0,0 +1,49 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Calculation\Statistical;
use PhpOffice\PhpSpreadsheet\Calculation\ArrayEnabled;
use PhpOffice\PhpSpreadsheet\Calculation\Exception;
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
class Standardize extends StatisticalValidations
{
use ArrayEnabled;
/**
* STANDARDIZE.
*
* Returns a normalized value from a distribution characterized by mean and standard_dev.
*
* @param array|float $value Value to normalize
* Or can be an array of values
* @param array|float $mean Mean Value
* Or can be an array of values
* @param array|float $stdDev Standard Deviation
* Or can be an array of values
*
* @return array|float|string Standardized value, or a string containing an error
* 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 execute($value, $mean, $stdDev)
{
if (is_array($value) || is_array($mean) || is_array($stdDev)) {
return self::evaluateArrayArguments([self::class, __FUNCTION__], $value, $mean, $stdDev);
}
try {
$value = self::validateFloat($value);
$mean = self::validateFloat($mean);
$stdDev = self::validateFloat($stdDev);
} catch (Exception $e) {
return $e->getMessage();
}
if ($stdDev <= 0) {
return Functions::NAN();
}
return ($value - $mean) / $stdDev;
}
}

45
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/StatisticalValidations.php

@ -0,0 +1,45 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Calculation\Statistical;
use PhpOffice\PhpSpreadsheet\Calculation\Exception;
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
class StatisticalValidations
{
/**
* @param mixed $value
*/
public static function validateFloat($value): float
{
if (!is_numeric($value)) {
throw new Exception(Functions::VALUE());
}
return (float) $value;
}
/**
* @param mixed $value
*/
public static function validateInt($value): int
{
if (!is_numeric($value)) {
throw new Exception(Functions::VALUE());
}
return (int) floor((float) $value);
}
/**
* @param mixed $value
*/
public static function validateBool($value): bool
{
if (!is_bool($value) && !is_numeric($value)) {
throw new Exception(Functions::VALUE());
}
return (bool) $value;
}
}

429
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Trends.php

@ -0,0 +1,429 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Calculation\Statistical;
use PhpOffice\PhpSpreadsheet\Calculation\ArrayEnabled;
use PhpOffice\PhpSpreadsheet\Calculation\Exception;
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
use PhpOffice\PhpSpreadsheet\Shared\Trend\Trend;
class Trends
{
use ArrayEnabled;
private static function filterTrendValues(array &$array1, array &$array2): void
{
foreach ($array1 as $key => $value) {
if ((is_bool($value)) || (is_string($value)) || ($value === null)) {
unset($array1[$key], $array2[$key]);
}
}
}
private static function checkTrendArrays(&$array1, &$array2): void
{
if (!is_array($array1)) {
$array1 = [$array1];
}
if (!is_array($array2)) {
$array2 = [$array2];
}
$array1 = Functions::flattenArray($array1);
$array2 = Functions::flattenArray($array2);
self::filterTrendValues($array1, $array2);
self::filterTrendValues($array2, $array1);
// Reset the array indexes
$array1 = array_merge($array1);
$array2 = array_merge($array2);
}
protected static function validateTrendArrays(array $yValues, array $xValues): void
{
$yValueCount = count($yValues);
$xValueCount = count($xValues);
if (($yValueCount === 0) || ($yValueCount !== $xValueCount)) {
throw new Exception(Functions::NA());
} elseif ($yValueCount === 1) {
throw new Exception(Functions::DIV0());
}
}
/**
* CORREL.
*
* Returns covariance, the average of the products of deviations for each data point pair.
*
* @param mixed $yValues array of mixed Data Series Y
* @param null|mixed $xValues array of mixed Data Series X
*
* @return float|string
*/
public static function CORREL($yValues, $xValues = null)
{
if (($xValues === null) || (!is_array($yValues)) || (!is_array($xValues))) {
return Functions::VALUE();
}
try {
self::checkTrendArrays($yValues, $xValues);
self::validateTrendArrays($yValues, $xValues);
} catch (Exception $e) {
return $e->getMessage();
}
$bestFitLinear = Trend::calculate(Trend::TREND_LINEAR, $yValues, $xValues);
return $bestFitLinear->getCorrelation();
}
/**
* COVAR.
*
* Returns covariance, the average of the products of deviations for each data point pair.
*
* @param mixed $yValues array of mixed Data Series Y
* @param mixed $xValues array of mixed Data Series X
*
* @return float|string
*/
public static function COVAR($yValues, $xValues)
{
try {
self::checkTrendArrays($yValues, $xValues);
self::validateTrendArrays($yValues, $xValues);
} catch (Exception $e) {
return $e->getMessage();
}
$bestFitLinear = Trend::calculate(Trend::TREND_LINEAR, $yValues, $xValues);
return $bestFitLinear->getCovariance();
}
/**
* FORECAST.
*
* Calculates, or predicts, a future value by using existing values.
* The predicted value is a y-value for a given x-value.
*
* @param mixed $xValue Float value of X for which we want to find Y
* Or can be an array of values
* @param mixed $yValues array of mixed Data Series Y
* @param mixed $xValues of mixed Data Series X
*
* @return array|bool|float|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 FORECAST($xValue, $yValues, $xValues)
{
if (is_array($xValue)) {
return self::evaluateArrayArgumentsSubset([self::class, __FUNCTION__], 1, $xValue, $yValues, $xValues);
}
try {
$xValue = StatisticalValidations::validateFloat($xValue);
self::checkTrendArrays($yValues, $xValues);
self::validateTrendArrays($yValues, $xValues);
} catch (Exception $e) {
return $e->getMessage();
}
$bestFitLinear = Trend::calculate(Trend::TREND_LINEAR, $yValues, $xValues);
return $bestFitLinear->getValueOfYForX($xValue);
}
/**
* GROWTH.
*
* Returns values along a predicted exponential Trend
*
* @param mixed[] $yValues Data Series Y
* @param mixed[] $xValues Data Series X
* @param mixed[] $newValues Values of X for which we want to find Y
* @param mixed $const A logical (boolean) value specifying whether to force the intersect to equal 0 or not
*
* @return float[]
*/
public static function GROWTH($yValues, $xValues = [], $newValues = [], $const = true)
{
$yValues = Functions::flattenArray($yValues);
$xValues = Functions::flattenArray($xValues);
$newValues = Functions::flattenArray($newValues);
$const = ($const === null) ? true : (bool) Functions::flattenSingleValue($const);
$bestFitExponential = Trend::calculate(Trend::TREND_EXPONENTIAL, $yValues, $xValues, $const);
if (empty($newValues)) {
$newValues = $bestFitExponential->getXValues();
}
$returnArray = [];
foreach ($newValues as $xValue) {
$returnArray[0][] = [$bestFitExponential->getValueOfYForX($xValue)];
}
return $returnArray;
}
/**
* INTERCEPT.
*
* Calculates the point at which a line will intersect the y-axis by using existing x-values and y-values.
*
* @param mixed[] $yValues Data Series Y
* @param mixed[] $xValues Data Series X
*
* @return float|string
*/
public static function INTERCEPT($yValues, $xValues)
{
try {
self::checkTrendArrays($yValues, $xValues);
self::validateTrendArrays($yValues, $xValues);
} catch (Exception $e) {
return $e->getMessage();
}
$bestFitLinear = Trend::calculate(Trend::TREND_LINEAR, $yValues, $xValues);
return $bestFitLinear->getIntersect();
}
/**
* LINEST.
*
* Calculates the statistics for a line by using the "least squares" method to calculate a straight line
* that best fits your data, and then returns an array that describes the line.
*
* @param mixed[] $yValues Data Series Y
* @param null|mixed[] $xValues Data Series X
* @param mixed $const A logical (boolean) value specifying whether to force the intersect to equal 0 or not
* @param mixed $stats A logical (boolean) value specifying whether to return additional regression statistics
*
* @return array|int|string The result, or a string containing an error
*/
public static function LINEST($yValues, $xValues = null, $const = true, $stats = false)
{
$const = ($const === null) ? true : (bool) Functions::flattenSingleValue($const);
$stats = ($stats === null) ? false : (bool) Functions::flattenSingleValue($stats);
if ($xValues === null) {
$xValues = $yValues;
}
try {
self::checkTrendArrays($yValues, $xValues);
self::validateTrendArrays($yValues, $xValues);
} catch (Exception $e) {
return $e->getMessage();
}
$bestFitLinear = Trend::calculate(Trend::TREND_LINEAR, $yValues, $xValues, $const);
if ($stats === true) {
return [
[
$bestFitLinear->getSlope(),
$bestFitLinear->getIntersect(),
],
[
$bestFitLinear->getSlopeSE(),
($const === false) ? Functions::NA() : $bestFitLinear->getIntersectSE(),
],
[
$bestFitLinear->getGoodnessOfFit(),
$bestFitLinear->getStdevOfResiduals(),
],
[
$bestFitLinear->getF(),
$bestFitLinear->getDFResiduals(),
],
[
$bestFitLinear->getSSRegression(),
$bestFitLinear->getSSResiduals(),
],
];
}
return [
$bestFitLinear->getSlope(),
$bestFitLinear->getIntersect(),
];
}
/**
* LOGEST.
*
* Calculates an exponential curve that best fits the X and Y data series,
* and then returns an array that describes the line.
*
* @param mixed[] $yValues Data Series Y
* @param null|mixed[] $xValues Data Series X
* @param mixed $const A logical (boolean) value specifying whether to force the intersect to equal 0 or not
* @param mixed $stats A logical (boolean) value specifying whether to return additional regression statistics
*
* @return array|int|string The result, or a string containing an error
*/
public static function LOGEST($yValues, $xValues = null, $const = true, $stats = false)
{
$const = ($const === null) ? true : (bool) Functions::flattenSingleValue($const);
$stats = ($stats === null) ? false : (bool) Functions::flattenSingleValue($stats);
if ($xValues === null) {
$xValues = $yValues;
}
try {
self::checkTrendArrays($yValues, $xValues);
self::validateTrendArrays($yValues, $xValues);
} catch (Exception $e) {
return $e->getMessage();
}
foreach ($yValues as $value) {
if ($value < 0.0) {
return Functions::NAN();
}
}
$bestFitExponential = Trend::calculate(Trend::TREND_EXPONENTIAL, $yValues, $xValues, $const);
if ($stats === true) {
return [
[
$bestFitExponential->getSlope(),
$bestFitExponential->getIntersect(),
],
[
$bestFitExponential->getSlopeSE(),
($const === false) ? Functions::NA() : $bestFitExponential->getIntersectSE(),
],
[
$bestFitExponential->getGoodnessOfFit(),
$bestFitExponential->getStdevOfResiduals(),
],
[
$bestFitExponential->getF(),
$bestFitExponential->getDFResiduals(),
],
[
$bestFitExponential->getSSRegression(),
$bestFitExponential->getSSResiduals(),
],
];
}
return [
$bestFitExponential->getSlope(),
$bestFitExponential->getIntersect(),
];
}
/**
* RSQ.
*
* Returns the square of the Pearson product moment correlation coefficient through data points
* in known_y's and known_x's.
*
* @param mixed[] $yValues Data Series Y
* @param mixed[] $xValues Data Series X
*
* @return float|string The result, or a string containing an error
*/
public static function RSQ($yValues, $xValues)
{
try {
self::checkTrendArrays($yValues, $xValues);
self::validateTrendArrays($yValues, $xValues);
} catch (Exception $e) {
return $e->getMessage();
}
$bestFitLinear = Trend::calculate(Trend::TREND_LINEAR, $yValues, $xValues);
return $bestFitLinear->getGoodnessOfFit();
}
/**
* SLOPE.
*
* Returns the slope of the linear regression line through data points in known_y's and known_x's.
*
* @param mixed[] $yValues Data Series Y
* @param mixed[] $xValues Data Series X
*
* @return float|string The result, or a string containing an error
*/
public static function SLOPE($yValues, $xValues)
{
try {
self::checkTrendArrays($yValues, $xValues);
self::validateTrendArrays($yValues, $xValues);
} catch (Exception $e) {
return $e->getMessage();
}
$bestFitLinear = Trend::calculate(Trend::TREND_LINEAR, $yValues, $xValues);
return $bestFitLinear->getSlope();
}
/**
* STEYX.
*
* Returns the standard error of the predicted y-value for each x in the regression.
*
* @param mixed[] $yValues Data Series Y
* @param mixed[] $xValues Data Series X
*
* @return float|string
*/
public static function STEYX($yValues, $xValues)
{
try {
self::checkTrendArrays($yValues, $xValues);
self::validateTrendArrays($yValues, $xValues);
} catch (Exception $e) {
return $e->getMessage();
}
$bestFitLinear = Trend::calculate(Trend::TREND_LINEAR, $yValues, $xValues);
return $bestFitLinear->getStdevOfResiduals();
}
/**
* TREND.
*
* Returns values along a linear Trend
*
* @param mixed[] $yValues Data Series Y
* @param mixed[] $xValues Data Series X
* @param mixed[] $newValues Values of X for which we want to find Y
* @param mixed $const A logical (boolean) value specifying whether to force the intersect to equal 0 or not
*
* @return float[]
*/
public static function TREND($yValues, $xValues = [], $newValues = [], $const = true)
{
$yValues = Functions::flattenArray($yValues);
$xValues = Functions::flattenArray($xValues);
$newValues = Functions::flattenArray($newValues);
$const = ($const === null) ? true : (bool) Functions::flattenSingleValue($const);
$bestFitLinear = Trend::calculate(Trend::TREND_LINEAR, $yValues, $xValues, $const);
if (empty($newValues)) {
$newValues = $bestFitLinear->getXValues();
}
$returnArray = [];
foreach ($newValues as $xValue) {
$returnArray[0][] = [$bestFitLinear->getValueOfYForX($xValue)];
}
return $returnArray;
}
}

28
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/VarianceBase.php

@ -0,0 +1,28 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Calculation\Statistical;
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
abstract class VarianceBase
{
protected static function datatypeAdjustmentAllowStrings($value)
{
if (is_bool($value)) {
return (int) $value;
} elseif (is_string($value)) {
return 0;
}
return $value;
}
protected static function datatypeAdjustmentBooleans($value)
{
if (is_bool($value) && (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_OPENOFFICE)) {
return (int) $value;
}
return $value;
}
}

185
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Statistical/Variances.php

@ -0,0 +1,185 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Calculation\Statistical;
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
class Variances extends VarianceBase
{
/**
* VAR.
*
* Estimates variance based on a sample.
*
* Excel Function:
* VAR(value1[,value2[, ...]])
*
* @param mixed ...$args Data values
*
* @return float|string (string if result is an error)
*/
public static function VAR(...$args)
{
$returnValue = Functions::DIV0();
$summerA = $summerB = 0.0;
// Loop through arguments
$aArgs = Functions::flattenArray($args);
$aCount = 0;
foreach ($aArgs as $arg) {
$arg = self::datatypeAdjustmentBooleans($arg);
// Is it a numeric value?
if ((is_numeric($arg)) && (!is_string($arg))) {
$summerA += ($arg * $arg);
$summerB += $arg;
++$aCount;
}
}
if ($aCount > 1) {
$summerA *= $aCount;
$summerB *= $summerB;
return ($summerA - $summerB) / ($aCount * ($aCount - 1));
}
return $returnValue;
}
/**
* VARA.
*
* Estimates variance based on a sample, including numbers, text, and logical values
*
* Excel Function:
* VARA(value1[,value2[, ...]])
*
* @param mixed ...$args Data values
*
* @return float|string (string if result is an error)
*/
public static function VARA(...$args)
{
$returnValue = Functions::DIV0();
$summerA = $summerB = 0.0;
// Loop through arguments
$aArgs = Functions::flattenArrayIndexed($args);
$aCount = 0;
foreach ($aArgs as $k => $arg) {
if ((is_string($arg)) && (Functions::isValue($k))) {
return Functions::VALUE();
} elseif ((is_string($arg)) && (!Functions::isMatrixValue($k))) {
} else {
// Is it a numeric value?
if ((is_numeric($arg)) || (is_bool($arg)) || ((is_string($arg) & ($arg != '')))) {
$arg = self::datatypeAdjustmentAllowStrings($arg);
$summerA += ($arg * $arg);
$summerB += $arg;
++$aCount;
}
}
}
if ($aCount > 1) {
$summerA *= $aCount;
$summerB *= $summerB;
return ($summerA - $summerB) / ($aCount * ($aCount - 1));
}
return $returnValue;
}
/**
* VARP.
*
* Calculates variance based on the entire population
*
* Excel Function:
* VARP(value1[,value2[, ...]])
*
* @param mixed ...$args Data values
*
* @return float|string (string if result is an error)
*/
public static function VARP(...$args)
{
// Return value
$returnValue = Functions::DIV0();
$summerA = $summerB = 0.0;
// Loop through arguments
$aArgs = Functions::flattenArray($args);
$aCount = 0;
foreach ($aArgs as $arg) {
$arg = self::datatypeAdjustmentBooleans($arg);
// Is it a numeric value?
if ((is_numeric($arg)) && (!is_string($arg))) {
$summerA += ($arg * $arg);
$summerB += $arg;
++$aCount;
}
}
if ($aCount > 0) {
$summerA *= $aCount;
$summerB *= $summerB;
return ($summerA - $summerB) / ($aCount * $aCount);
}
return $returnValue;
}
/**
* VARPA.
*
* Calculates variance based on the entire population, including numbers, text, and logical values
*
* Excel Function:
* VARPA(value1[,value2[, ...]])
*
* @param mixed ...$args Data values
*
* @return float|string (string if result is an error)
*/
public static function VARPA(...$args)
{
$returnValue = Functions::DIV0();
$summerA = $summerB = 0.0;
// Loop through arguments
$aArgs = Functions::flattenArrayIndexed($args);
$aCount = 0;
foreach ($aArgs as $k => $arg) {
if ((is_string($arg)) && (Functions::isValue($k))) {
return Functions::VALUE();
} elseif ((is_string($arg)) && (!Functions::isMatrixValue($k))) {
} else {
// Is it a numeric value?
if ((is_numeric($arg)) || (is_bool($arg)) || ((is_string($arg) & ($arg != '')))) {
$arg = self::datatypeAdjustmentAllowStrings($arg);
$summerA += ($arg * $arg);
$summerB += $arg;
++$aCount;
}
}
}
if ($aCount > 0) {
$summerA *= $aCount;
$summerB *= $summerB;
return ($summerA - $summerB) / ($aCount * $aCount);
}
return $returnValue;
}
}

448
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData.php

@ -0,0 +1,448 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Calculation;
use DateTimeInterface;
/**
* @deprecated 1.18.0
*/
class TextData
{
/**
* CHARACTER.
*
* @Deprecated 1.18.0
*
* @see Use the character() method in the TextData\CharacterConvert class instead
*
* @param string $character Value
*
* @return array|string
*/
public static function CHARACTER($character)
{
return TextData\CharacterConvert::character($character);
}
/**
* TRIMNONPRINTABLE.
*
* @Deprecated 1.18.0
*
* @see Use the nonPrintable() method in the TextData\Trim class instead
*
* @param mixed $stringValue Value to check
*
* @return null|array|string
*/
public static function TRIMNONPRINTABLE($stringValue = '')
{
return TextData\Trim::nonPrintable($stringValue);
}
/**
* TRIMSPACES.
*
* @Deprecated 1.18.0
*
* @see Use the spaces() method in the TextData\Trim class instead
*
* @param mixed $stringValue Value to check
*
* @return array|string
*/
public static function TRIMSPACES($stringValue = '')
{
return TextData\Trim::spaces($stringValue);
}
/**
* ASCIICODE.
*
* @Deprecated 1.18.0
*
* @see Use the code() method in the TextData\CharacterConvert class instead
*
* @param array|string $characters Value
*
* @return array|int|string A string if arguments are invalid
*/
public static function ASCIICODE($characters)
{
return TextData\CharacterConvert::code($characters);
}
/**
* CONCATENATE.
*
* @Deprecated 1.18.0
*
* @see Use the CONCATENATE() method in the TextData\Concatenate class instead
*
* @return string
*/
public static function CONCATENATE(...$args)
{
return TextData\Concatenate::CONCATENATE(...$args);
}
/**
* DOLLAR.
*
* This function converts a number to text using currency format, with the decimals rounded to the specified place.
* The format used is $#,##0.00_);($#,##0.00)..
*
* @Deprecated 1.18.0
*
* @see Use the DOLLAR() method in the TextData\Format class instead
*
* @param float $value The value to format
* @param int $decimals The number of digits to display to the right of the decimal point.
* If decimals is negative, number is rounded to the left of the decimal point.
* If you omit decimals, it is assumed to be 2
*
* @return array|string
*/
public static function DOLLAR($value = 0, $decimals = 2)
{
return TextData\Format::DOLLAR($value, $decimals);
}
/**
* FIND.
*
* @Deprecated 1.18.0
*
* @see Use the sensitive() method in the TextData\Search class instead
*
* @param array|string $needle The string to look for
* @param array|string $haystack The string in which to look
* @param array|int $offset Offset within $haystack
*
* @return array|int|string
*/
public static function SEARCHSENSITIVE($needle, $haystack, $offset = 1)
{
return TextData\Search::sensitive($needle, $haystack, $offset);
}
/**
* SEARCH.
*
* @Deprecated 1.18.0
*
* @see Use the insensitive() method in the TextData\Search class instead
*
* @param array|string $needle The string to look for
* @param array|string $haystack The string in which to look
* @param array|int $offset Offset within $haystack
*
* @return array|int|string
*/
public static function SEARCHINSENSITIVE($needle, $haystack, $offset = 1)
{
return TextData\Search::insensitive($needle, $haystack, $offset);
}
/**
* FIXEDFORMAT.
*
* @Deprecated 1.18.0
*
* @see Use the FIXEDFORMAT() method in the TextData\Format class instead
*
* @param mixed $value Value to check
* @param int $decimals
* @param bool $no_commas
*
* @return array|string
*/
public static function FIXEDFORMAT($value, $decimals = 2, $no_commas = false)
{
return TextData\Format::FIXEDFORMAT($value, $decimals, $no_commas);
}
/**
* LEFT.
*
* @Deprecated 1.18.0
*
* @see Use the left() method in the TextData\Extract class instead
*
* @param array|string $value Value
* @param array|int $chars Number of characters
*
* @return array|string
*/
public static function LEFT($value = '', $chars = 1)
{
return TextData\Extract::left($value, $chars);
}
/**
* MID.
*
* @Deprecated 1.18.0
*
* @see Use the mid() method in the TextData\Extract class instead
*
* @param array|string $value Value
* @param array|int $start Start character
* @param array|int $chars Number of characters
*
* @return array|string
*/
public static function MID($value = '', $start = 1, $chars = null)
{
return TextData\Extract::mid($value, $start, $chars);
}
/**
* RIGHT.
*
* @Deprecated 1.18.0
*
* @see Use the right() method in the TextData\Extract class instead
*
* @param array|string $value Value
* @param array|int $chars Number of characters
*
* @return array|string
*/
public static function RIGHT($value = '', $chars = 1)
{
return TextData\Extract::right($value, $chars);
}
/**
* STRINGLENGTH.
*
* @Deprecated 1.18.0
*
* @see Use the length() method in the TextData\Text class instead
*
* @param string $value Value
*
* @return array|int
*/
public static function STRINGLENGTH($value = '')
{
return TextData\Text::length($value);
}
/**
* LOWERCASE.
*
* Converts a string value to lower case.
*
* @Deprecated 1.18.0
*
* @see Use the lower() method in the TextData\CaseConvert class instead
*
* @param array|string $mixedCaseString
*
* @return array|string
*/
public static function LOWERCASE($mixedCaseString)
{
return TextData\CaseConvert::lower($mixedCaseString);
}
/**
* UPPERCASE.
*
* Converts a string value to upper case.
*
* @Deprecated 1.18.0
*
* @see Use the upper() method in the TextData\CaseConvert class instead
*
* @param string $mixedCaseString
*
* @return array|string
*/
public static function UPPERCASE($mixedCaseString)
{
return TextData\CaseConvert::upper($mixedCaseString);
}
/**
* PROPERCASE.
*
* Converts a string value to proper/title case.
*
* @Deprecated 1.18.0
*
* @see Use the proper() method in the TextData\CaseConvert class instead
*
* @param array|string $mixedCaseString
*
* @return array|string
*/
public static function PROPERCASE($mixedCaseString)
{
return TextData\CaseConvert::proper($mixedCaseString);
}
/**
* REPLACE.
*
* @Deprecated 1.18.0
*
* @see Use the replace() method in the TextData\Replace class instead
*
* @param string $oldText String to modify
* @param int $start Start character
* @param int $chars Number of characters
* @param string $newText String to replace in defined position
*
* @return array|string
*/
public static function REPLACE($oldText, $start, $chars, $newText)
{
return TextData\Replace::replace($oldText, $start, $chars, $newText);
}
/**
* SUBSTITUTE.
*
* @Deprecated 1.18.0
*
* @see Use the substitute() method in the TextData\Replace class instead
*
* @param string $text Value
* @param string $fromText From Value
* @param string $toText To Value
* @param int $instance Instance Number
*
* @return array|string
*/
public static function SUBSTITUTE($text = '', $fromText = '', $toText = '', $instance = 0)
{
return TextData\Replace::substitute($text, $fromText, $toText, $instance);
}
/**
* RETURNSTRING.
*
* @Deprecated 1.18.0
*
* @see Use the test() method in the TextData\Text class instead
*
* @param mixed $testValue Value to check
*
* @return null|array|string
*/
public static function RETURNSTRING($testValue = '')
{
return TextData\Text::test($testValue);
}
/**
* TEXTFORMAT.
*
* @Deprecated 1.18.0
*
* @see Use the TEXTFORMAT() method in the TextData\Format class instead
*
* @param mixed $value Value to check
* @param string $format Format mask to use
*
* @return array|string
*/
public static function TEXTFORMAT($value, $format)
{
return TextData\Format::TEXTFORMAT($value, $format);
}
/**
* VALUE.
*
* @Deprecated 1.18.0
*
* @see Use the VALUE() method in the TextData\Format class instead
*
* @param mixed $value Value to check
*
* @return array|DateTimeInterface|float|int|string A string if arguments are invalid
*/
public static function VALUE($value = '')
{
return TextData\Format::VALUE($value);
}
/**
* NUMBERVALUE.
*
* @Deprecated 1.18.0
*
* @see Use the NUMBERVALUE() method in the TextData\Format class instead
*
* @param mixed $value Value to check
* @param string $decimalSeparator decimal separator, defaults to locale defined value
* @param string $groupSeparator group/thosands separator, defaults to locale defined value
*
* @return array|float|string
*/
public static function NUMBERVALUE($value = '', $decimalSeparator = null, $groupSeparator = null)
{
return TextData\Format::NUMBERVALUE($value, $decimalSeparator, $groupSeparator);
}
/**
* Compares two text strings and returns TRUE if they are exactly the same, FALSE otherwise.
* EXACT is case-sensitive but ignores formatting differences.
* Use EXACT to test text being entered into a document.
*
* @Deprecated 1.18.0
*
* @see Use the exact() method in the TextData\Text class instead
*
* @param mixed $value1
* @param mixed $value2
*
* @return array|bool
*/
public static function EXACT($value1, $value2)
{
return TextData\Text::exact($value1, $value2);
}
/**
* TEXTJOIN.
*
* @Deprecated 1.18.0
*
* @see Use the TEXTJOIN() method in the TextData\Concatenate class instead
*
* @param mixed $delimiter
* @param mixed $ignoreEmpty
* @param mixed $args
*
* @return array|string
*/
public static function TEXTJOIN($delimiter, $ignoreEmpty, ...$args)
{
return TextData\Concatenate::TEXTJOIN($delimiter, $ignoreEmpty, ...$args);
}
/**
* REPT.
*
* Returns the result of builtin function repeat after validating args.
*
* @Deprecated 1.18.0
*
* @see Use the builtinREPT() method in the TextData\Concatenate class instead
*
* @param array|string $str Should be numeric
* @param mixed $number Should be int
*
* @return array|string
*/
public static function builtinREPT($str, $number)
{
return TextData\Concatenate::builtinREPT($str, $number);
}
}

113
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData/Replace.php

@ -0,0 +1,113 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Calculation\TextData;
use PhpOffice\PhpSpreadsheet\Calculation\ArrayEnabled;
use PhpOffice\PhpSpreadsheet\Calculation\Exception as CalcExp;
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
class Replace
{
use ArrayEnabled;
/**
* REPLACE.
*
* @param mixed $oldText The text string value to modify
* Or can be an array of values
* @param mixed $start Integer offset for start character of the replacement
* Or can be an array of values
* @param mixed $chars Integer number of characters to replace from the start offset
* Or can be an array of values
* @param mixed $newText String to replace in the defined position
* Or can be an array of values
*
* @return array|string
* If an array of values is passed for either of the arguments, then the returned result
* will also be an array with matching dimensions
*/
public static function replace($oldText, $start, $chars, $newText)
{
if (is_array($oldText) || is_array($start) || is_array($chars) || is_array($newText)) {
return self::evaluateArrayArguments([self::class, __FUNCTION__], $oldText, $start, $chars, $newText);
}
try {
$start = Helpers::extractInt($start, 1, 0, true);
$chars = Helpers::extractInt($chars, 0, 0, true);
$oldText = Helpers::extractString($oldText);
$newText = Helpers::extractString($newText);
$left = mb_substr($oldText, 0, $start - 1, 'UTF-8');
$right = mb_substr($oldText, $start + $chars - 1, null, 'UTF-8');
} catch (CalcExp $e) {
return $e->getMessage();
}
return $left . $newText . $right;
}
/**
* SUBSTITUTE.
*
* @param mixed $text The text string value to modify
* Or can be an array of values
* @param mixed $fromText The string value that we want to replace in $text
* Or can be an array of values
* @param mixed $toText The string value that we want to replace with in $text
* Or can be an array of values
* @param mixed $instance Integer instance Number for the occurrence of frmText to change
* Or can be an array of values
*
* @return array|string
* If an array of values is passed for either of the arguments, then the returned result
* will also be an array with matching dimensions
*/
public static function substitute($text = '', $fromText = '', $toText = '', $instance = null)
{
if (is_array($text) || is_array($fromText) || is_array($toText) || is_array($instance)) {
return self::evaluateArrayArguments([self::class, __FUNCTION__], $text, $fromText, $toText, $instance);
}
try {
$text = Helpers::extractString($text);
$fromText = Helpers::extractString($fromText);
$toText = Helpers::extractString($toText);
if ($instance === null) {
return str_replace($fromText, $toText, $text);
}
if (is_bool($instance)) {
if ($instance === false || Functions::getCompatibilityMode() !== Functions::COMPATIBILITY_OPENOFFICE) {
return Functions::Value();
}
$instance = 1;
}
$instance = Helpers::extractInt($instance, 1, 0, true);
} catch (CalcExp $e) {
return $e->getMessage();
}
return self::executeSubstitution($text, $fromText, $toText, $instance);
}
/**
* @return string
*/
private static function executeSubstitution(string $text, string $fromText, string $toText, int $instance)
{
$pos = -1;
while ($instance > 0) {
$pos = mb_strpos($text, $fromText, $pos + 1, 'UTF-8');
if ($pos === false) {
break;
}
--$instance;
}
if ($pos !== false) {
return Functions::scalar(self::REPLACE($text, ++$pos, mb_strlen($fromText, 'UTF-8'), $toText));
}
return $text;
}
}

97
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData/Search.php

@ -0,0 +1,97 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Calculation\TextData;
use PhpOffice\PhpSpreadsheet\Calculation\ArrayEnabled;
use PhpOffice\PhpSpreadsheet\Calculation\Exception as CalcExp;
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
class Search
{
use ArrayEnabled;
/**
* FIND (case sensitive search).
*
* @param mixed $needle The string to look for
* Or can be an array of values
* @param mixed $haystack The string in which to look
* Or can be an array of values
* @param mixed $offset Integer offset within $haystack to start searching from
* Or can be an array of values
*
* @return array|int|string The offset where the first occurrence of needle was found in the haystack
* If an array of values is passed for the $value or $chars arguments, then the returned result
* will also be an array with matching dimensions
*/
public static function sensitive($needle, $haystack, $offset = 1)
{
if (is_array($needle) || is_array($haystack) || is_array($offset)) {
return self::evaluateArrayArguments([self::class, __FUNCTION__], $needle, $haystack, $offset);
}
try {
$needle = Helpers::extractString($needle);
$haystack = Helpers::extractString($haystack);
$offset = Helpers::extractInt($offset, 1, 0, true);
} catch (CalcExp $e) {
return $e->getMessage();
}
if (StringHelper::countCharacters($haystack) >= $offset) {
if (StringHelper::countCharacters($needle) === 0) {
return $offset;
}
$pos = mb_strpos($haystack, $needle, --$offset, 'UTF-8');
if ($pos !== false) {
return ++$pos;
}
}
return Functions::VALUE();
}
/**
* SEARCH (case insensitive search).
*
* @param mixed $needle The string to look for
* Or can be an array of values
* @param mixed $haystack The string in which to look
* Or can be an array of values
* @param mixed $offset Integer offset within $haystack to start searching from
* Or can be an array of values
*
* @return array|int|string The offset where the first occurrence of needle was found in the haystack
* If an array of values is passed for the $value or $chars arguments, then the returned result
* will also be an array with matching dimensions
*/
public static function insensitive($needle, $haystack, $offset = 1)
{
if (is_array($needle) || is_array($haystack) || is_array($offset)) {
return self::evaluateArrayArguments([self::class, __FUNCTION__], $needle, $haystack, $offset);
}
try {
$needle = Helpers::extractString($needle);
$haystack = Helpers::extractString($haystack);
$offset = Helpers::extractInt($offset, 1, 0, true);
} catch (CalcExp $e) {
return $e->getMessage();
}
if (StringHelper::countCharacters($haystack) >= $offset) {
if (StringHelper::countCharacters($needle) === 0) {
return $offset;
}
$pos = mb_stripos($haystack, $needle, --$offset, 'UTF-8');
if ($pos !== false) {
return ++$pos;
}
}
return Functions::VALUE();
}
}

80
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData/Text.php

@ -0,0 +1,80 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Calculation\TextData;
use PhpOffice\PhpSpreadsheet\Calculation\ArrayEnabled;
class Text
{
use ArrayEnabled;
/**
* LEN.
*
* @param mixed $value String Value
* Or can be an array of values
*
* @return array|int
* If an array of values is passed for the argument, then the returned result
* will also be an array with matching dimensions
*/
public static function length($value = '')
{
if (is_array($value)) {
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $value);
}
$value = Helpers::extractString($value);
return mb_strlen($value ?? '', 'UTF-8');
}
/**
* Compares two text strings and returns TRUE if they are exactly the same, FALSE otherwise.
* EXACT is case-sensitive but ignores formatting differences.
* Use EXACT to test text being entered into a document.
*
* @param mixed $value1 String Value
* Or can be an array of values
* @param mixed $value2 String Value
* Or can be an array of values
*
* @return array|bool
* If an array of values is passed for either of the arguments, then the returned result
* will also be an array with matching dimensions
*/
public static function exact($value1, $value2)
{
if (is_array($value1) || is_array($value2)) {
return self::evaluateArrayArguments([self::class, __FUNCTION__], $value1, $value2);
}
$value1 = Helpers::extractString($value1);
$value2 = Helpers::extractString($value2);
return $value2 === $value1;
}
/**
* RETURNSTRING.
*
* @param mixed $testValue Value to check
* Or can be an array of values
*
* @return null|array|string
* If an array of values is passed for the argument, then the returned result
* will also be an array with matching dimensions
*/
public static function test($testValue = '')
{
if (is_array($testValue)) {
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $testValue);
}
if (is_string($testValue)) {
return $testValue;
}
return null;
}
}

52
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/TextData/Trim.php

@ -0,0 +1,52 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Calculation\TextData;
use PhpOffice\PhpSpreadsheet\Calculation\ArrayEnabled;
class Trim
{
use ArrayEnabled;
/**
* CLEAN.
*
* @param mixed $stringValue String Value to check
* Or can be an array of values
*
* @return null|array|string
* If an array of values is passed as the argument, then the returned result will also be an array
* with the same dimensions
*/
public static function nonPrintable($stringValue = '')
{
if (is_array($stringValue)) {
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $stringValue);
}
$stringValue = Helpers::extractString($stringValue);
return preg_replace('/[\\x00-\\x1f]/', '', "$stringValue");
}
/**
* TRIM.
*
* @param mixed $stringValue String Value to check
* Or can be an array of values
*
* @return array|string
* If an array of values is passed as the argument, then the returned result will also be an array
* with the same dimensions
*/
public static function spaces($stringValue = '')
{
if (is_array($stringValue)) {
return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $stringValue);
}
$stringValue = Helpers::extractString($stringValue);
return trim(preg_replace('/ +/', ' ', trim("$stringValue", ' ')) ?? '', ' ');
}
}

149
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Token/Stack.php

@ -0,0 +1,149 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Calculation\Token;
use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
class Stack
{
/**
* The parser stack for formulae.
*
* @var mixed[]
*/
private $stack = [];
/**
* Count of entries in the parser stack.
*
* @var int
*/
private $count = 0;
/**
* Return the number of entries on the stack.
*
* @return int
*/
public function count()
{
return $this->count;
}
/**
* Push a new entry onto the stack.
*
* @param mixed $type
* @param mixed $value
* @param mixed $reference
* @param null|string $storeKey will store the result under this alias
* @param null|string $onlyIf will only run computation if the matching
* store key is true
* @param null|string $onlyIfNot will only run computation if the matching
* store key is false
*/
public function push(
$type,
$value,
$reference = null,
$storeKey = null,
$onlyIf = null,
$onlyIfNot = null
): void {
$stackItem = $this->getStackItem($type, $value, $reference, $storeKey, $onlyIf, $onlyIfNot);
$this->stack[$this->count++] = $stackItem;
if ($type == 'Function') {
$localeFunction = Calculation::localeFunc($value);
if ($localeFunction != $value) {
$this->stack[($this->count - 1)]['localeValue'] = $localeFunction;
}
}
}
public function getStackItem(
$type,
$value,
$reference = null,
$storeKey = null,
$onlyIf = null,
$onlyIfNot = null
) {
$stackItem = [
'type' => $type,
'value' => $value,
'reference' => $reference,
];
if (isset($storeKey)) {
$stackItem['storeKey'] = $storeKey;
}
if (isset($onlyIf)) {
$stackItem['onlyIf'] = $onlyIf;
}
if (isset($onlyIfNot)) {
$stackItem['onlyIfNot'] = $onlyIfNot;
}
return $stackItem;
}
/**
* Pop the last entry from the stack.
*
* @return mixed
*/
public function pop()
{
if ($this->count > 0) {
return $this->stack[--$this->count];
}
return null;
}
/**
* Return an entry from the stack without removing it.
*
* @param int $n number indicating how far back in the stack we want to look
*
* @return mixed
*/
public function last($n = 1)
{
if ($this->count - $n < 0) {
return null;
}
return $this->stack[$this->count - $n];
}
/**
* Clear the stack.
*/
public function clear(): void
{
$this->stack = [];
$this->count = 0;
}
public function __toString()
{
$str = 'Stack: ';
foreach ($this->stack as $index => $item) {
if ($index > $this->count - 1) {
break;
}
$value = $item['value'] ?? 'no value';
while (is_array($value)) {
$value = array_pop($value);
}
$str .= $value . ' |> ';
}
return $str;
}
}

27
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Web.php

@ -0,0 +1,27 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Calculation;
/**
* @deprecated 1.18.0
*/
class Web
{
/**
* WEBSERVICE.
*
* Returns data from a web service on the Internet or Intranet.
*
* Excel Function:
* Webservice(url)
*
* @see Web\Service::webService()
* Use the webService() method in the Web\Service class instead
*
* @return string the output resulting from a call to the webservice
*/
public static function WEBSERVICE(string $url)
{
return Web\Service::webService($url);
}
}

75
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Web/Service.php

@ -0,0 +1,75 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Calculation\Web;
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
use PhpOffice\PhpSpreadsheet\Settings;
use Psr\Http\Client\ClientExceptionInterface;
class Service
{
/**
* WEBSERVICE.
*
* Returns data from a web service on the Internet or Intranet.
*
* Excel Function:
* Webservice(url)
*
* @return string the output resulting from a call to the webservice
*/
public static function webService(string $url)
{
$url = trim($url);
if (strlen($url) > 2048) {
return Functions::VALUE(); // Invalid URL length
}
if (!preg_match('/^http[s]?:\/\//', $url)) {
return Functions::VALUE(); // Invalid protocol
}
// Get results from the the webservice
$client = Settings::getHttpClient();
$requestFactory = Settings::getRequestFactory();
$request = $requestFactory->createRequest('GET', $url);
try {
$response = $client->sendRequest($request);
} catch (ClientExceptionInterface $e) {
return Functions::VALUE(); // cURL error
}
if ($response->getStatusCode() != 200) {
return Functions::VALUE(); // cURL error
}
$output = $response->getBody()->getContents();
if (strlen($output) > 32767) {
return Functions::VALUE(); // Output not a string or too long
}
return $output;
}
/**
* URLENCODE.
*
* Returns data from a web service on the Internet or Intranet.
*
* Excel Function:
* urlEncode(text)
*
* @param mixed $text
*
* @return string the url encoded output
*/
public static function urlEncode($text)
{
if (!is_string($text)) {
return Functions::VALUE();
}
return str_replace('+', '%20', urlencode($text));
}
}

BIN
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/locale/Translations.xlsx

Binary file not shown.

124
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Cell/StringValueBinder.php

@ -0,0 +1,124 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Cell;
use DateTimeInterface;
use PhpOffice\PhpSpreadsheet\RichText\RichText;
use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
class StringValueBinder implements IValueBinder
{
/**
* @var bool
*/
protected $convertNull = true;
/**
* @var bool
*/
protected $convertBoolean = true;
/**
* @var bool
*/
protected $convertNumeric = true;
/**
* @var bool
*/
protected $convertFormula = true;
public function setNullConversion(bool $suppressConversion = false): self
{
$this->convertNull = $suppressConversion;
return $this;
}
public function setBooleanConversion(bool $suppressConversion = false): self
{
$this->convertBoolean = $suppressConversion;
return $this;
}
public function getBooleanConversion(): bool
{
return $this->convertBoolean;
}
public function setNumericConversion(bool $suppressConversion = false): self
{
$this->convertNumeric = $suppressConversion;
return $this;
}
public function setFormulaConversion(bool $suppressConversion = false): self
{
$this->convertFormula = $suppressConversion;
return $this;
}
public function setConversionForAllValueTypes(bool $suppressConversion = false): self
{
$this->convertNull = $suppressConversion;
$this->convertBoolean = $suppressConversion;
$this->convertNumeric = $suppressConversion;
$this->convertFormula = $suppressConversion;
return $this;
}
/**
* Bind value to a cell.
*
* @param Cell $cell Cell to bind value to
* @param mixed $value Value to bind in cell
*/
public function bindValue(Cell $cell, $value)
{
if (is_object($value)) {
return $this->bindObjectValue($cell, $value);
}
// sanitize UTF-8 strings
if (is_string($value)) {
$value = StringHelper::sanitizeUTF8($value);
}
if ($value === null && $this->convertNull === false) {
$cell->setValueExplicit($value, DataType::TYPE_NULL);
} elseif (is_bool($value) && $this->convertBoolean === false) {
$cell->setValueExplicit($value, DataType::TYPE_BOOL);
} elseif ((is_int($value) || is_float($value)) && $this->convertNumeric === false) {
$cell->setValueExplicit($value, DataType::TYPE_NUMERIC);
} elseif (is_string($value) && strlen($value) > 1 && $value[0] === '=' && $this->convertFormula === false) {
$cell->setValueExplicit($value, DataType::TYPE_FORMULA);
} else {
if (is_string($value) && strlen($value) > 1 && $value[0] === '=') {
$cell->getStyle()->setQuotePrefix(true);
}
$cell->setValueExplicit((string) $value, DataType::TYPE_STRING);
}
return true;
}
protected function bindObjectValue(Cell $cell, object $value): bool
{
// Handle any objects that might be injected
if ($value instanceof DateTimeInterface) {
$value = $value->format('Y-m-d H:i:s');
} elseif ($value instanceof RichText) {
$cell->setValueExplicit($value, DataType::TYPE_INLINE);
return true;
}
$cell->setValueExplicit((string) $value, DataType::TYPE_STRING);
return true;
}
}

109
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/PlotArea.php

@ -0,0 +1,109 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Chart;
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
class PlotArea
{
/**
* PlotArea Layout.
*
* @var Layout
*/
private $layout;
/**
* Plot Series.
*
* @var DataSeries[]
*/
private $plotSeries = [];
/**
* Create a new PlotArea.
*
* @param DataSeries[] $plotSeries
*/
public function __construct(?Layout $layout = null, array $plotSeries = [])
{
$this->layout = $layout;
$this->plotSeries = $plotSeries;
}
/**
* Get Layout.
*
* @return Layout
*/
public function getLayout()
{
return $this->layout;
}
/**
* Get Number of Plot Groups.
*/
public function getPlotGroupCount(): int
{
return count($this->plotSeries);
}
/**
* Get Number of Plot Series.
*
* @return int
*/
public function getPlotSeriesCount()
{
$seriesCount = 0;
foreach ($this->plotSeries as $plot) {
$seriesCount += $plot->getPlotSeriesCount();
}
return $seriesCount;
}
/**
* Get Plot Series.
*
* @return DataSeries[]
*/
public function getPlotGroup()
{
return $this->plotSeries;
}
/**
* Get Plot Series by Index.
*
* @param mixed $index
*
* @return DataSeries
*/
public function getPlotGroupByIndex($index)
{
return $this->plotSeries[$index];
}
/**
* Set Plot Series.
*
* @param DataSeries[] $plotSeries
*
* @return $this
*/
public function setPlotSeries(array $plotSeries)
{
$this->plotSeries = $plotSeries;
return $this;
}
public function refresh(Worksheet $worksheet): void
{
foreach ($this->plotSeries as $plotSeries) {
$plotSeries->refresh($worksheet);
}
}
}

369
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Properties.php

@ -0,0 +1,369 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Chart;
/**
* Created by PhpStorm.
* User: nhw2h8s
* Date: 7/2/14
* Time: 5:45 PM.
*/
abstract class Properties
{
const
EXCEL_COLOR_TYPE_STANDARD = 'prstClr';
const EXCEL_COLOR_TYPE_SCHEME = 'schemeClr';
const EXCEL_COLOR_TYPE_ARGB = 'srgbClr';
const
AXIS_LABELS_LOW = 'low';
const AXIS_LABELS_HIGH = 'high';
const AXIS_LABELS_NEXT_TO = 'nextTo';
const AXIS_LABELS_NONE = 'none';
const
TICK_MARK_NONE = 'none';
const TICK_MARK_INSIDE = 'in';
const TICK_MARK_OUTSIDE = 'out';
const TICK_MARK_CROSS = 'cross';
const
HORIZONTAL_CROSSES_AUTOZERO = 'autoZero';
const HORIZONTAL_CROSSES_MAXIMUM = 'max';
const
FORMAT_CODE_GENERAL = 'General';
const FORMAT_CODE_NUMBER = '#,##0.00';
const FORMAT_CODE_CURRENCY = '$#,##0.00';
const FORMAT_CODE_ACCOUNTING = '_($* #,##0.00_);_($* (#,##0.00);_($* "-"??_);_(@_)';
const FORMAT_CODE_DATE = 'm/d/yyyy';
const FORMAT_CODE_TIME = '[$-F400]h:mm:ss AM/PM';
const FORMAT_CODE_PERCENTAGE = '0.00%';
const FORMAT_CODE_FRACTION = '# ?/?';
const FORMAT_CODE_SCIENTIFIC = '0.00E+00';
const FORMAT_CODE_TEXT = '@';
const FORMAT_CODE_SPECIAL = '00000';
const
ORIENTATION_NORMAL = 'minMax';
const ORIENTATION_REVERSED = 'maxMin';
const
LINE_STYLE_COMPOUND_SIMPLE = 'sng';
const LINE_STYLE_COMPOUND_DOUBLE = 'dbl';
const LINE_STYLE_COMPOUND_THICKTHIN = 'thickThin';
const LINE_STYLE_COMPOUND_THINTHICK = 'thinThick';
const LINE_STYLE_COMPOUND_TRIPLE = 'tri';
const LINE_STYLE_DASH_SOLID = 'solid';
const LINE_STYLE_DASH_ROUND_DOT = 'sysDot';
const LINE_STYLE_DASH_SQUERE_DOT = 'sysDash';
const LINE_STYPE_DASH_DASH = 'dash';
const LINE_STYLE_DASH_DASH_DOT = 'dashDot';
const LINE_STYLE_DASH_LONG_DASH = 'lgDash';
const LINE_STYLE_DASH_LONG_DASH_DOT = 'lgDashDot';
const LINE_STYLE_DASH_LONG_DASH_DOT_DOT = 'lgDashDotDot';
const LINE_STYLE_CAP_SQUARE = 'sq';
const LINE_STYLE_CAP_ROUND = 'rnd';
const LINE_STYLE_CAP_FLAT = 'flat';
const LINE_STYLE_JOIN_ROUND = 'bevel';
const LINE_STYLE_JOIN_MITER = 'miter';
const LINE_STYLE_JOIN_BEVEL = 'bevel';
const LINE_STYLE_ARROW_TYPE_NOARROW = null;
const LINE_STYLE_ARROW_TYPE_ARROW = 'triangle';
const LINE_STYLE_ARROW_TYPE_OPEN = 'arrow';
const LINE_STYLE_ARROW_TYPE_STEALTH = 'stealth';
const LINE_STYLE_ARROW_TYPE_DIAMOND = 'diamond';
const LINE_STYLE_ARROW_TYPE_OVAL = 'oval';
const LINE_STYLE_ARROW_SIZE_1 = 1;
const LINE_STYLE_ARROW_SIZE_2 = 2;
const LINE_STYLE_ARROW_SIZE_3 = 3;
const LINE_STYLE_ARROW_SIZE_4 = 4;
const LINE_STYLE_ARROW_SIZE_5 = 5;
const LINE_STYLE_ARROW_SIZE_6 = 6;
const LINE_STYLE_ARROW_SIZE_7 = 7;
const LINE_STYLE_ARROW_SIZE_8 = 8;
const LINE_STYLE_ARROW_SIZE_9 = 9;
const
SHADOW_PRESETS_NOSHADOW = null;
const SHADOW_PRESETS_OUTER_BOTTTOM_RIGHT = 1;
const SHADOW_PRESETS_OUTER_BOTTOM = 2;
const SHADOW_PRESETS_OUTER_BOTTOM_LEFT = 3;
const SHADOW_PRESETS_OUTER_RIGHT = 4;
const SHADOW_PRESETS_OUTER_CENTER = 5;
const SHADOW_PRESETS_OUTER_LEFT = 6;
const SHADOW_PRESETS_OUTER_TOP_RIGHT = 7;
const SHADOW_PRESETS_OUTER_TOP = 8;
const SHADOW_PRESETS_OUTER_TOP_LEFT = 9;
const SHADOW_PRESETS_INNER_BOTTTOM_RIGHT = 10;
const SHADOW_PRESETS_INNER_BOTTOM = 11;
const SHADOW_PRESETS_INNER_BOTTOM_LEFT = 12;
const SHADOW_PRESETS_INNER_RIGHT = 13;
const SHADOW_PRESETS_INNER_CENTER = 14;
const SHADOW_PRESETS_INNER_LEFT = 15;
const SHADOW_PRESETS_INNER_TOP_RIGHT = 16;
const SHADOW_PRESETS_INNER_TOP = 17;
const SHADOW_PRESETS_INNER_TOP_LEFT = 18;
const SHADOW_PRESETS_PERSPECTIVE_BELOW = 19;
const SHADOW_PRESETS_PERSPECTIVE_UPPER_RIGHT = 20;
const SHADOW_PRESETS_PERSPECTIVE_UPPER_LEFT = 21;
const SHADOW_PRESETS_PERSPECTIVE_LOWER_RIGHT = 22;
const SHADOW_PRESETS_PERSPECTIVE_LOWER_LEFT = 23;
/**
* @param float $width
*
* @return float
*/
protected function getExcelPointsWidth($width)
{
return $width * 12700;
}
/**
* @param float $angle
*
* @return float
*/
protected function getExcelPointsAngle($angle)
{
return $angle * 60000;
}
protected function getTrueAlpha($alpha)
{
return (string) 100 - $alpha . '000';
}
protected function setColorProperties($color, $alpha, $colorType)
{
return [
'type' => (string) $colorType,
'value' => (string) $color,
'alpha' => (string) $this->getTrueAlpha($alpha),
];
}
protected function getLineStyleArrowSize($arraySelector, $arrayKaySelector)
{
$sizes = [
1 => ['w' => 'sm', 'len' => 'sm'],
2 => ['w' => 'sm', 'len' => 'med'],
3 => ['w' => 'sm', 'len' => 'lg'],
4 => ['w' => 'med', 'len' => 'sm'],
5 => ['w' => 'med', 'len' => 'med'],
6 => ['w' => 'med', 'len' => 'lg'],
7 => ['w' => 'lg', 'len' => 'sm'],
8 => ['w' => 'lg', 'len' => 'med'],
9 => ['w' => 'lg', 'len' => 'lg'],
];
return $sizes[$arraySelector][$arrayKaySelector];
}
protected function getShadowPresetsMap($presetsOption)
{
$presets_options = [
//OUTER
1 => [
'effect' => 'outerShdw',
'blur' => '50800',
'distance' => '38100',
'direction' => '2700000',
'algn' => 'tl',
'rotWithShape' => '0',
],
2 => [
'effect' => 'outerShdw',
'blur' => '50800',
'distance' => '38100',
'direction' => '5400000',
'algn' => 't',
'rotWithShape' => '0',
],
3 => [
'effect' => 'outerShdw',
'blur' => '50800',
'distance' => '38100',
'direction' => '8100000',
'algn' => 'tr',
'rotWithShape' => '0',
],
4 => [
'effect' => 'outerShdw',
'blur' => '50800',
'distance' => '38100',
'algn' => 'l',
'rotWithShape' => '0',
],
5 => [
'effect' => 'outerShdw',
'size' => [
'sx' => '102000',
'sy' => '102000',
],
'blur' => '63500',
'distance' => '38100',
'algn' => 'ctr',
'rotWithShape' => '0',
],
6 => [
'effect' => 'outerShdw',
'blur' => '50800',
'distance' => '38100',
'direction' => '10800000',
'algn' => 'r',
'rotWithShape' => '0',
],
7 => [
'effect' => 'outerShdw',
'blur' => '50800',
'distance' => '38100',
'direction' => '18900000',
'algn' => 'bl',
'rotWithShape' => '0',
],
8 => [
'effect' => 'outerShdw',
'blur' => '50800',
'distance' => '38100',
'direction' => '16200000',
'rotWithShape' => '0',
],
9 => [
'effect' => 'outerShdw',
'blur' => '50800',
'distance' => '38100',
'direction' => '13500000',
'algn' => 'br',
'rotWithShape' => '0',
],
//INNER
10 => [
'effect' => 'innerShdw',
'blur' => '63500',
'distance' => '50800',
'direction' => '2700000',
],
11 => [
'effect' => 'innerShdw',
'blur' => '63500',
'distance' => '50800',
'direction' => '5400000',
],
12 => [
'effect' => 'innerShdw',
'blur' => '63500',
'distance' => '50800',
'direction' => '8100000',
],
13 => [
'effect' => 'innerShdw',
'blur' => '63500',
'distance' => '50800',
],
14 => [
'effect' => 'innerShdw',
'blur' => '114300',
],
15 => [
'effect' => 'innerShdw',
'blur' => '63500',
'distance' => '50800',
'direction' => '10800000',
],
16 => [
'effect' => 'innerShdw',
'blur' => '63500',
'distance' => '50800',
'direction' => '18900000',
],
17 => [
'effect' => 'innerShdw',
'blur' => '63500',
'distance' => '50800',
'direction' => '16200000',
],
18 => [
'effect' => 'innerShdw',
'blur' => '63500',
'distance' => '50800',
'direction' => '13500000',
],
//perspective
19 => [
'effect' => 'outerShdw',
'blur' => '152400',
'distance' => '317500',
'size' => [
'sx' => '90000',
'sy' => '-19000',
],
'direction' => '5400000',
'rotWithShape' => '0',
],
20 => [
'effect' => 'outerShdw',
'blur' => '76200',
'direction' => '18900000',
'size' => [
'sy' => '23000',
'kx' => '-1200000',
],
'algn' => 'bl',
'rotWithShape' => '0',
],
21 => [
'effect' => 'outerShdw',
'blur' => '76200',
'direction' => '13500000',
'size' => [
'sy' => '23000',
'kx' => '1200000',
],
'algn' => 'br',
'rotWithShape' => '0',
],
22 => [
'effect' => 'outerShdw',
'blur' => '76200',
'distance' => '12700',
'direction' => '2700000',
'size' => [
'sy' => '-23000',
'kx' => '-800400',
],
'algn' => 'bl',
'rotWithShape' => '0',
],
23 => [
'effect' => 'outerShdw',
'blur' => '76200',
'distance' => '12700',
'direction' => '8100000',
'size' => [
'sy' => '-23000',
'kx' => '800400',
],
'algn' => 'br',
'rotWithShape' => '0',
],
];
return $presets_options[$presetsOption];
}
protected function getArrayElementsValue($properties, $elements)
{
$reference = &$properties;
if (!is_array($elements)) {
return $reference[$elements];
}
foreach ($elements as $keys) {
$reference = &$reference[$keys];
}
return $reference;
}
}

20
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Renderer/PHP Charting Libraries.txt

@ -0,0 +1,20 @@
ChartDirector
https://www.advsofteng.com/cdphp.html
GraPHPite
http://graphpite.sourceforge.net/
JpGraph
http://www.aditus.nu/jpgraph/
LibChart
https://naku.dohcrew.com/libchart/pages/introduction/
pChart
http://pchart.sourceforge.net/
TeeChart
https://www.steema.com/
PHPGraphLib
http://www.ebrueggeman.com/phpgraphlib

90
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Chart/Title.php

@ -0,0 +1,90 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Chart;
use PhpOffice\PhpSpreadsheet\RichText\RichText;
class Title
{
/**
* Title Caption.
*
* @var array|RichText|string
*/
private $caption = '';
/**
* Title Layout.
*
* @var Layout
*/
private $layout;
/**
* Create a new Title.
*
* @param array|RichText|string $caption
*/
public function __construct($caption = '', ?Layout $layout = null)
{
$this->caption = $caption;
$this->layout = $layout;
}
/**
* Get caption.
*
* @return array|RichText|string
*/
public function getCaption()
{
return $this->caption;
}
public function getCaptionText(): string
{
$caption = $this->caption;
if (is_string($caption)) {
return $caption;
}
if ($caption instanceof RichText) {
return $caption->getPlainText();
}
$retVal = '';
foreach ($caption as $textx) {
/** @var RichText|string */
$text = $textx;
if ($text instanceof RichText) {
$retVal .= $text->getPlainText();
} else {
$retVal .= $text;
}
}
return $retVal;
}
/**
* Set caption.
*
* @param array|RichText|string $caption
*
* @return $this
*/
public function setCaption($caption)
{
$this->caption = $caption;
return $this;
}
/**
* Get Layout.
*
* @return Layout
*/
public function getLayout()
{
return $this->layout;
}
}

537
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Document/Properties.php

@ -0,0 +1,537 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Document;
use DateTime;
use PhpOffice\PhpSpreadsheet\Shared\IntOrFloat;
class Properties
{
/** constants */
public const PROPERTY_TYPE_BOOLEAN = 'b';
public const PROPERTY_TYPE_INTEGER = 'i';
public const PROPERTY_TYPE_FLOAT = 'f';
public const PROPERTY_TYPE_DATE = 'd';
public const PROPERTY_TYPE_STRING = 's';
public const PROPERTY_TYPE_UNKNOWN = 'u';
private const VALID_PROPERTY_TYPE_LIST = [
self::PROPERTY_TYPE_BOOLEAN,
self::PROPERTY_TYPE_INTEGER,
self::PROPERTY_TYPE_FLOAT,
self::PROPERTY_TYPE_DATE,
self::PROPERTY_TYPE_STRING,
];
/**
* Creator.
*
* @var string
*/
private $creator = 'Unknown Creator';
/**
* LastModifiedBy.
*
* @var string
*/
private $lastModifiedBy;
/**
* Created.
*
* @var float|int
*/
private $created;
/**
* Modified.
*
* @var float|int
*/
private $modified;
/**
* Title.
*
* @var string
*/
private $title = 'Untitled Spreadsheet';
/**
* Description.
*
* @var string
*/
private $description = '';
/**
* Subject.
*
* @var string
*/
private $subject = '';
/**
* Keywords.
*
* @var string
*/
private $keywords = '';
/**
* Category.
*
* @var string
*/
private $category = '';
/**
* Manager.
*
* @var string
*/
private $manager = '';
/**
* Company.
*
* @var string
*/
private $company = '';
/**
* Custom Properties.
*
* @var array{value: mixed, type: string}[]
*/
private $customProperties = [];
/**
* Create a new Document Properties instance.
*/
public function __construct()
{
// Initialise values
$this->lastModifiedBy = $this->creator;
$this->created = self::intOrFloatTimestamp(null);
$this->modified = self::intOrFloatTimestamp(null);
}
/**
* Get Creator.
*/
public function getCreator(): string
{
return $this->creator;
}
/**
* Set Creator.
*
* @return $this
*/
public function setCreator(string $creator): self
{
$this->creator = $creator;
return $this;
}
/**
* Get Last Modified By.
*/
public function getLastModifiedBy(): string
{
return $this->lastModifiedBy;
}
/**
* Set Last Modified By.
*
* @return $this
*/
public function setLastModifiedBy(string $modifiedBy): self
{
$this->lastModifiedBy = $modifiedBy;
return $this;
}
/**
* @param null|float|int|string $timestamp
*
* @return float|int
*/
private static function intOrFloatTimestamp($timestamp)
{
if ($timestamp === null) {
$timestamp = (float) (new DateTime())->format('U');
} elseif (is_string($timestamp)) {
if (is_numeric($timestamp)) {
$timestamp = (float) $timestamp;
} else {
$timestamp = preg_replace('/[.][0-9]*$/', '', $timestamp) ?? '';
$timestamp = preg_replace('/^(\\d{4})- (\\d)/', '$1-0$2', $timestamp) ?? '';
$timestamp = preg_replace('/^(\\d{4}-\\d{2})- (\\d)/', '$1-0$2', $timestamp) ?? '';
$timestamp = (float) (new DateTime($timestamp))->format('U');
}
}
return IntOrFloat::evaluate($timestamp);
}
/**
* Get Created.
*
* @return float|int
*/
public function getCreated()
{
return $this->created;
}
/**
* Set Created.
*
* @param null|float|int|string $timestamp
*
* @return $this
*/
public function setCreated($timestamp): self
{
$this->created = self::intOrFloatTimestamp($timestamp);
return $this;
}
/**
* Get Modified.
*
* @return float|int
*/
public function getModified()
{
return $this->modified;
}
/**
* Set Modified.
*
* @param null|float|int|string $timestamp
*
* @return $this
*/
public function setModified($timestamp): self
{
$this->modified = self::intOrFloatTimestamp($timestamp);
return $this;
}
/**
* Get Title.
*/
public function getTitle(): string
{
return $this->title;
}
/**
* Set Title.
*
* @return $this
*/
public function setTitle(string $title): self
{
$this->title = $title;
return $this;
}
/**
* Get Description.
*/
public function getDescription(): string
{
return $this->description;
}
/**
* Set Description.
*
* @return $this
*/
public function setDescription(string $description): self
{
$this->description = $description;
return $this;
}
/**
* Get Subject.
*/
public function getSubject(): string
{
return $this->subject;
}
/**
* Set Subject.
*
* @return $this
*/
public function setSubject(string $subject): self
{
$this->subject = $subject;
return $this;
}
/**
* Get Keywords.
*/
public function getKeywords(): string
{
return $this->keywords;
}
/**
* Set Keywords.
*
* @return $this
*/
public function setKeywords(string $keywords): self
{
$this->keywords = $keywords;
return $this;
}
/**
* Get Category.
*/
public function getCategory(): string
{
return $this->category;
}
/**
* Set Category.
*
* @return $this
*/
public function setCategory(string $category): self
{
$this->category = $category;
return $this;
}
/**
* Get Company.
*/
public function getCompany(): string
{
return $this->company;
}
/**
* Set Company.
*
* @return $this
*/
public function setCompany(string $company): self
{
$this->company = $company;
return $this;
}
/**
* Get Manager.
*/
public function getManager(): string
{
return $this->manager;
}
/**
* Set Manager.
*
* @return $this
*/
public function setManager(string $manager): self
{
$this->manager = $manager;
return $this;
}
/**
* Get a List of Custom Property Names.
*
* @return string[]
*/
public function getCustomProperties(): array
{
return array_keys($this->customProperties);
}
/**
* Check if a Custom Property is defined.
*/
public function isCustomPropertySet(string $propertyName): bool
{
return array_key_exists($propertyName, $this->customProperties);
}
/**
* Get a Custom Property Value.
*
* @return mixed
*/
public function getCustomPropertyValue(string $propertyName)
{
if (isset($this->customProperties[$propertyName])) {
return $this->customProperties[$propertyName]['value'];
}
return null;
}
/**
* Get a Custom Property Type.
*
* @return null|string
*/
public function getCustomPropertyType(string $propertyName)
{
return $this->customProperties[$propertyName]['type'] ?? null;
}
/**
* @param mixed $propertyValue
*/
private function identifyPropertyType($propertyValue): string
{
if (is_float($propertyValue)) {
return self::PROPERTY_TYPE_FLOAT;
}
if (is_int($propertyValue)) {
return self::PROPERTY_TYPE_INTEGER;
}
if (is_bool($propertyValue)) {
return self::PROPERTY_TYPE_BOOLEAN;
}
return self::PROPERTY_TYPE_STRING;
}
/**
* Set a Custom Property.
*
* @param mixed $propertyValue
* @param string $propertyType
* 'i' : Integer
* 'f' : Floating Point
* 's' : String
* 'd' : Date/Time
* 'b' : Boolean
*
* @return $this
*/
public function setCustomProperty(string $propertyName, $propertyValue = '', $propertyType = null): self
{
if (($propertyType === null) || (!in_array($propertyType, self::VALID_PROPERTY_TYPE_LIST))) {
$propertyType = $this->identifyPropertyType($propertyValue);
}
if (!is_object($propertyValue)) {
$this->customProperties[$propertyName] = [
'value' => self::convertProperty($propertyValue, $propertyType),
'type' => $propertyType,
];
}
return $this;
}
private const PROPERTY_TYPE_ARRAY = [
'i' => self::PROPERTY_TYPE_INTEGER, // Integer
'i1' => self::PROPERTY_TYPE_INTEGER, // 1-Byte Signed Integer
'i2' => self::PROPERTY_TYPE_INTEGER, // 2-Byte Signed Integer
'i4' => self::PROPERTY_TYPE_INTEGER, // 4-Byte Signed Integer
'i8' => self::PROPERTY_TYPE_INTEGER, // 8-Byte Signed Integer
'int' => self::PROPERTY_TYPE_INTEGER, // Integer
'ui1' => self::PROPERTY_TYPE_INTEGER, // 1-Byte Unsigned Integer
'ui2' => self::PROPERTY_TYPE_INTEGER, // 2-Byte Unsigned Integer
'ui4' => self::PROPERTY_TYPE_INTEGER, // 4-Byte Unsigned Integer
'ui8' => self::PROPERTY_TYPE_INTEGER, // 8-Byte Unsigned Integer
'uint' => self::PROPERTY_TYPE_INTEGER, // Unsigned Integer
'f' => self::PROPERTY_TYPE_FLOAT, // Real Number
'r4' => self::PROPERTY_TYPE_FLOAT, // 4-Byte Real Number
'r8' => self::PROPERTY_TYPE_FLOAT, // 8-Byte Real Number
'decimal' => self::PROPERTY_TYPE_FLOAT, // Decimal
's' => self::PROPERTY_TYPE_STRING, // String
'empty' => self::PROPERTY_TYPE_STRING, // Empty
'null' => self::PROPERTY_TYPE_STRING, // Null
'lpstr' => self::PROPERTY_TYPE_STRING, // LPSTR
'lpwstr' => self::PROPERTY_TYPE_STRING, // LPWSTR
'bstr' => self::PROPERTY_TYPE_STRING, // Basic String
'd' => self::PROPERTY_TYPE_DATE, // Date and Time
'date' => self::PROPERTY_TYPE_DATE, // Date and Time
'filetime' => self::PROPERTY_TYPE_DATE, // File Time
'b' => self::PROPERTY_TYPE_BOOLEAN, // Boolean
'bool' => self::PROPERTY_TYPE_BOOLEAN, // Boolean
];
private const SPECIAL_TYPES = [
'empty' => '',
'null' => null,
];
/**
* Convert property to form desired by Excel.
*
* @param mixed $propertyValue
*
* @return mixed
*/
public static function convertProperty($propertyValue, string $propertyType)
{
return self::SPECIAL_TYPES[$propertyType] ?? self::convertProperty2($propertyValue, $propertyType);
}
/**
* Convert property to form desired by Excel.
*
* @param mixed $propertyValue
*
* @return mixed
*/
private static function convertProperty2($propertyValue, string $type)
{
$propertyType = self::convertPropertyType($type);
switch ($propertyType) {
case self::PROPERTY_TYPE_INTEGER:
$intValue = (int) $propertyValue;
return ($type[0] === 'u') ? abs($intValue) : $intValue;
case self::PROPERTY_TYPE_FLOAT:
return (float) $propertyValue;
case self::PROPERTY_TYPE_DATE:
return self::intOrFloatTimestamp($propertyValue);
case self::PROPERTY_TYPE_BOOLEAN:
return is_bool($propertyValue) ? $propertyValue : ($propertyValue === 'true');
default: // includes string
return $propertyValue;
}
}
public static function convertPropertyType(string $propertyType): string
{
return self::PROPERTY_TYPE_ARRAY[$propertyType] ?? self::PROPERTY_TYPE_UNKNOWN;
}
}

152
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Document/Security.php

@ -0,0 +1,152 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Document;
use PhpOffice\PhpSpreadsheet\Shared\PasswordHasher;
class Security
{
/**
* LockRevision.
*
* @var bool
*/
private $lockRevision = false;
/**
* LockStructure.
*
* @var bool
*/
private $lockStructure = false;
/**
* LockWindows.
*
* @var bool
*/
private $lockWindows = false;
/**
* RevisionsPassword.
*
* @var string
*/
private $revisionsPassword = '';
/**
* WorkbookPassword.
*
* @var string
*/
private $workbookPassword = '';
/**
* Create a new Document Security instance.
*/
public function __construct()
{
}
/**
* Is some sort of document security enabled?
*/
public function isSecurityEnabled(): bool
{
return $this->lockRevision ||
$this->lockStructure ||
$this->lockWindows;
}
public function getLockRevision(): bool
{
return $this->lockRevision;
}
public function setLockRevision(?bool $locked): self
{
if ($locked !== null) {
$this->lockRevision = $locked;
}
return $this;
}
public function getLockStructure(): bool
{
return $this->lockStructure;
}
public function setLockStructure(?bool $locked): self
{
if ($locked !== null) {
$this->lockStructure = $locked;
}
return $this;
}
public function getLockWindows(): bool
{
return $this->lockWindows;
}
public function setLockWindows(?bool $locked): self
{
if ($locked !== null) {
$this->lockWindows = $locked;
}
return $this;
}
public function getRevisionsPassword(): string
{
return $this->revisionsPassword;
}
/**
* Set RevisionsPassword.
*
* @param string $password
* @param bool $alreadyHashed If the password has already been hashed, set this to true
*
* @return $this
*/
public function setRevisionsPassword(?string $password, bool $alreadyHashed = false)
{
if ($password !== null) {
if (!$alreadyHashed) {
$password = PasswordHasher::hashPassword($password);
}
$this->revisionsPassword = $password;
}
return $this;
}
public function getWorkbookPassword(): string
{
return $this->workbookPassword;
}
/**
* Set WorkbookPassword.
*
* @param string $password
* @param bool $alreadyHashed If the password has already been hashed, set this to true
*
* @return $this
*/
public function setWorkbookPassword(?string $password, bool $alreadyHashed = false)
{
if ($password !== null) {
if (!$alreadyHashed) {
$password = PasswordHasher::hashPassword($password);
}
$this->workbookPassword = $password;
}
return $this;
}
}

226
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Helper/Sample.php

@ -0,0 +1,226 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Helper;
use PhpOffice\PhpSpreadsheet\IOFactory;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Writer\IWriter;
use RecursiveDirectoryIterator;
use RecursiveIteratorIterator;
use RecursiveRegexIterator;
use ReflectionClass;
use RegexIterator;
use RuntimeException;
/**
* Helper class to be used in sample code.
*/
class Sample
{
/**
* Returns whether we run on CLI or browser.
*
* @return bool
*/
public function isCli()
{
return PHP_SAPI === 'cli';
}
/**
* Return the filename currently being executed.
*
* @return string
*/
public function getScriptFilename()
{
return basename($_SERVER['SCRIPT_FILENAME'], '.php');
}
/**
* Whether we are executing the index page.
*
* @return bool
*/
public function isIndex()
{
return $this->getScriptFilename() === 'index';
}
/**
* Return the page title.
*
* @return string
*/
public function getPageTitle()
{
return $this->isIndex() ? 'PHPSpreadsheet' : $this->getScriptFilename();
}
/**
* Return the page heading.
*
* @return string
*/
public function getPageHeading()
{
return $this->isIndex() ? '' : '<h1>' . str_replace('_', ' ', $this->getScriptFilename()) . '</h1>';
}
/**
* Returns an array of all known samples.
*
* @return string[][] [$name => $path]
*/
public function getSamples()
{
// Populate samples
$baseDir = realpath(__DIR__ . '/../../../samples');
$directory = new RecursiveDirectoryIterator($baseDir);
$iterator = new RecursiveIteratorIterator($directory);
$regex = new RegexIterator($iterator, '/^.+\.php$/', RecursiveRegexIterator::GET_MATCH);
$files = [];
foreach ($regex as $file) {
$file = str_replace(str_replace('\\', '/', $baseDir) . '/', '', str_replace('\\', '/', $file[0]));
$info = pathinfo($file);
$category = str_replace('_', ' ', $info['dirname']);
$name = str_replace('_', ' ', preg_replace('/(|\.php)/', '', $info['filename']));
if (!in_array($category, ['.', 'boostrap', 'templates'])) {
if (!isset($files[$category])) {
$files[$category] = [];
}
$files[$category][$name] = $file;
}
}
// Sort everything
ksort($files);
foreach ($files as &$f) {
asort($f);
}
return $files;
}
/**
* Write documents.
*
* @param string $filename
* @param string[] $writers
*/
public function write(Spreadsheet $spreadsheet, $filename, array $writers = ['Xlsx', 'Xls']): void
{
// Set active sheet index to the first sheet, so Excel opens this as the first sheet
$spreadsheet->setActiveSheetIndex(0);
// Write documents
foreach ($writers as $writerType) {
$path = $this->getFilename($filename, mb_strtolower($writerType));
$writer = IOFactory::createWriter($spreadsheet, $writerType);
$callStartTime = microtime(true);
$writer->save($path);
$this->logWrite($writer, $path, $callStartTime);
}
$this->logEndingNotes();
}
protected function isDirOrMkdir(string $folder): bool
{
return \is_dir($folder) || \mkdir($folder);
}
/**
* Returns the temporary directory and make sure it exists.
*
* @return string
*/
private function getTemporaryFolder()
{
$tempFolder = sys_get_temp_dir() . '/phpspreadsheet';
if (!$this->isDirOrMkdir($tempFolder)) {
throw new RuntimeException(sprintf('Directory "%s" was not created', $tempFolder));
}
return $tempFolder;
}
/**
* Returns the filename that should be used for sample output.
*
* @param string $filename
* @param string $extension
*
* @return string
*/
public function getFilename($filename, $extension = 'xlsx')
{
$originalExtension = pathinfo($filename, PATHINFO_EXTENSION);
return $this->getTemporaryFolder() . '/' . str_replace('.' . $originalExtension, '.' . $extension, basename($filename));
}
/**
* Return a random temporary file name.
*
* @param string $extension
*
* @return string
*/
public function getTemporaryFilename($extension = 'xlsx')
{
$temporaryFilename = tempnam($this->getTemporaryFolder(), 'phpspreadsheet-');
unlink($temporaryFilename);
return $temporaryFilename . '.' . $extension;
}
public function log($message): void
{
$eol = $this->isCli() ? PHP_EOL : '<br />';
echo date('H:i:s ') . $message . $eol;
}
/**
* Log ending notes.
*/
public function logEndingNotes(): void
{
// Do not show execution time for index
$this->log('Peak memory usage: ' . (memory_get_peak_usage(true) / 1024 / 1024) . 'MB');
}
/**
* Log a line about the write operation.
*
* @param string $path
* @param float $callStartTime
*/
public function logWrite(IWriter $writer, $path, $callStartTime): void
{
$callEndTime = microtime(true);
$callTime = $callEndTime - $callStartTime;
$reflection = new ReflectionClass($writer);
$format = $reflection->getShortName();
$message = "Write {$format} format to <code>{$path}</code> in " . sprintf('%.4f', $callTime) . ' seconds';
$this->log($message);
}
/**
* Log a line about the read operation.
*
* @param string $format
* @param string $path
* @param float $callStartTime
*/
public function logRead($format, $path, $callStartTime): void
{
$callEndTime = microtime(true);
$callTime = $callEndTime - $callStartTime;
$message = "Read {$format} format from <code>{$path}</code> in " . sprintf('%.4f', $callTime) . ' seconds';
$this->log($message);
}
}

52
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Helper/Size.php

@ -0,0 +1,52 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Helper;
class Size
{
const REGEXP_SIZE_VALIDATION = '/^(?P<size>\d*\.?\d+)(?P<unit>pt|px|em)?$/i';
/**
* @var bool
*/
protected $valid;
/**
* @var string
*/
protected $size = '';
/**
* @var string
*/
protected $unit = '';
public function __construct(string $size)
{
$this->valid = (bool) preg_match(self::REGEXP_SIZE_VALIDATION, $size, $matches);
if ($this->valid) {
$this->size = $matches['size'];
$this->unit = $matches['unit'] ?? 'pt';
}
}
public function valid(): bool
{
return $this->valid;
}
public function size(): string
{
return $this->size;
}
public function unit(): string
{
return $this->unit;
}
public function __toString()
{
return $this->size . $this->unit;
}
}

164
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Gnumeric/Properties.php

@ -0,0 +1,164 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Reader\Gnumeric;
use PhpOffice\PhpSpreadsheet\Reader\Gnumeric;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use SimpleXMLElement;
class Properties
{
/**
* @var Spreadsheet
*/
protected $spreadsheet;
public function __construct(Spreadsheet $spreadsheet)
{
$this->spreadsheet = $spreadsheet;
}
private function docPropertiesOld(SimpleXMLElement $gnmXML): void
{
$docProps = $this->spreadsheet->getProperties();
foreach ($gnmXML->Summary->Item as $summaryItem) {
$propertyName = $summaryItem->name;
$propertyValue = $summaryItem->{'val-string'};
switch ($propertyName) {
case 'title':
$docProps->setTitle(trim($propertyValue));
break;
case 'comments':
$docProps->setDescription(trim($propertyValue));
break;
case 'keywords':
$docProps->setKeywords(trim($propertyValue));
break;
case 'category':
$docProps->setCategory(trim($propertyValue));
break;
case 'manager':
$docProps->setManager(trim($propertyValue));
break;
case 'author':
$docProps->setCreator(trim($propertyValue));
$docProps->setLastModifiedBy(trim($propertyValue));
break;
case 'company':
$docProps->setCompany(trim($propertyValue));
break;
}
}
}
private function docPropertiesDC(SimpleXMLElement $officePropertyDC): void
{
$docProps = $this->spreadsheet->getProperties();
foreach ($officePropertyDC as $propertyName => $propertyValue) {
$propertyValue = trim((string) $propertyValue);
switch ($propertyName) {
case 'title':
$docProps->setTitle($propertyValue);
break;
case 'subject':
$docProps->setSubject($propertyValue);
break;
case 'creator':
$docProps->setCreator($propertyValue);
$docProps->setLastModifiedBy($propertyValue);
break;
case 'date':
$creationDate = $propertyValue;
$docProps->setModified($creationDate);
break;
case 'description':
$docProps->setDescription($propertyValue);
break;
}
}
}
private function docPropertiesMeta(SimpleXMLElement $officePropertyMeta): void
{
$docProps = $this->spreadsheet->getProperties();
foreach ($officePropertyMeta as $propertyName => $propertyValue) {
if ($propertyValue !== null) {
$attributes = $propertyValue->attributes(Gnumeric::NAMESPACE_META);
$propertyValue = trim((string) $propertyValue);
switch ($propertyName) {
case 'keyword':
$docProps->setKeywords($propertyValue);
break;
case 'initial-creator':
$docProps->setCreator($propertyValue);
$docProps->setLastModifiedBy($propertyValue);
break;
case 'creation-date':
$creationDate = $propertyValue;
$docProps->setCreated($creationDate);
break;
case 'user-defined':
if ($attributes) {
[, $attrName] = explode(':', (string) $attributes['name']);
$this->userDefinedProperties($attrName, $propertyValue);
}
break;
}
}
}
}
private function userDefinedProperties(string $attrName, string $propertyValue): void
{
$docProps = $this->spreadsheet->getProperties();
switch ($attrName) {
case 'publisher':
$docProps->setCompany($propertyValue);
break;
case 'category':
$docProps->setCategory($propertyValue);
break;
case 'manager':
$docProps->setManager($propertyValue);
break;
}
}
public function readProperties(SimpleXMLElement $xml, SimpleXMLElement $gnmXML): void
{
$officeXML = $xml->children(Gnumeric::NAMESPACE_OFFICE);
if (!empty($officeXML)) {
$officeDocXML = $officeXML->{'document-meta'};
$officeDocMetaXML = $officeDocXML->meta;
foreach ($officeDocMetaXML as $officePropertyData) {
$officePropertyDC = $officePropertyData->children(Gnumeric::NAMESPACE_DC);
$this->docPropertiesDC($officePropertyDC);
$officePropertyMeta = $officePropertyData->children(Gnumeric::NAMESPACE_META);
$this->docPropertiesMeta($officePropertyMeta);
}
} elseif (isset($gnmXML->Summary)) {
$this->docPropertiesOld($gnmXML);
}
}
}

278
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Gnumeric/Styles.php

@ -0,0 +1,278 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Reader\Gnumeric;
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
use PhpOffice\PhpSpreadsheet\Shared\Date;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Style\Alignment;
use PhpOffice\PhpSpreadsheet\Style\Border;
use PhpOffice\PhpSpreadsheet\Style\Borders;
use PhpOffice\PhpSpreadsheet\Style\Fill;
use PhpOffice\PhpSpreadsheet\Style\Font;
use SimpleXMLElement;
class Styles
{
/**
* @var Spreadsheet
*/
private $spreadsheet;
/**
* @var bool
*/
protected $readDataOnly = false;
/** @var array */
public static $mappings = [
'borderStyle' => [
'0' => Border::BORDER_NONE,
'1' => Border::BORDER_THIN,
'2' => Border::BORDER_MEDIUM,
'3' => Border::BORDER_SLANTDASHDOT,
'4' => Border::BORDER_DASHED,
'5' => Border::BORDER_THICK,
'6' => Border::BORDER_DOUBLE,
'7' => Border::BORDER_DOTTED,
'8' => Border::BORDER_MEDIUMDASHED,
'9' => Border::BORDER_DASHDOT,
'10' => Border::BORDER_MEDIUMDASHDOT,
'11' => Border::BORDER_DASHDOTDOT,
'12' => Border::BORDER_MEDIUMDASHDOTDOT,
'13' => Border::BORDER_MEDIUMDASHDOTDOT,
],
'fillType' => [
'1' => Fill::FILL_SOLID,
'2' => Fill::FILL_PATTERN_DARKGRAY,
'3' => Fill::FILL_PATTERN_MEDIUMGRAY,
'4' => Fill::FILL_PATTERN_LIGHTGRAY,
'5' => Fill::FILL_PATTERN_GRAY125,
'6' => Fill::FILL_PATTERN_GRAY0625,
'7' => Fill::FILL_PATTERN_DARKHORIZONTAL, // horizontal stripe
'8' => Fill::FILL_PATTERN_DARKVERTICAL, // vertical stripe
'9' => Fill::FILL_PATTERN_DARKDOWN, // diagonal stripe
'10' => Fill::FILL_PATTERN_DARKUP, // reverse diagonal stripe
'11' => Fill::FILL_PATTERN_DARKGRID, // diagoanl crosshatch
'12' => Fill::FILL_PATTERN_DARKTRELLIS, // thick diagonal crosshatch
'13' => Fill::FILL_PATTERN_LIGHTHORIZONTAL,
'14' => Fill::FILL_PATTERN_LIGHTVERTICAL,
'15' => Fill::FILL_PATTERN_LIGHTUP,
'16' => Fill::FILL_PATTERN_LIGHTDOWN,
'17' => Fill::FILL_PATTERN_LIGHTGRID, // thin horizontal crosshatch
'18' => Fill::FILL_PATTERN_LIGHTTRELLIS, // thin diagonal crosshatch
],
'horizontal' => [
'1' => Alignment::HORIZONTAL_GENERAL,
'2' => Alignment::HORIZONTAL_LEFT,
'4' => Alignment::HORIZONTAL_RIGHT,
'8' => Alignment::HORIZONTAL_CENTER,
'16' => Alignment::HORIZONTAL_CENTER_CONTINUOUS,
'32' => Alignment::HORIZONTAL_JUSTIFY,
'64' => Alignment::HORIZONTAL_CENTER_CONTINUOUS,
],
'underline' => [
'1' => Font::UNDERLINE_SINGLE,
'2' => Font::UNDERLINE_DOUBLE,
'3' => Font::UNDERLINE_SINGLEACCOUNTING,
'4' => Font::UNDERLINE_DOUBLEACCOUNTING,
],
'vertical' => [
'1' => Alignment::VERTICAL_TOP,
'2' => Alignment::VERTICAL_BOTTOM,
'4' => Alignment::VERTICAL_CENTER,
'8' => Alignment::VERTICAL_JUSTIFY,
],
];
public function __construct(Spreadsheet $spreadsheet, bool $readDataOnly)
{
$this->spreadsheet = $spreadsheet;
$this->readDataOnly = $readDataOnly;
}
public function read(SimpleXMLElement $sheet, int $maxRow, int $maxCol): void
{
if ($sheet->Styles->StyleRegion !== null) {
$this->readStyles($sheet->Styles->StyleRegion, $maxRow, $maxCol);
}
}
private function readStyles(SimpleXMLElement $styleRegion, int $maxRow, int $maxCol): void
{
foreach ($styleRegion as $style) {
$styleAttributes = $style->attributes();
if ($styleAttributes !== null && ($styleAttributes['startRow'] <= $maxRow) && ($styleAttributes['startCol'] <= $maxCol)) {
$cellRange = $this->readStyleRange($styleAttributes, $maxCol, $maxRow);
$styleAttributes = $style->Style->attributes();
$styleArray = [];
// We still set the number format mask for date/time values, even if readDataOnly is true
// so that we can identify whether a float is a float or a date value
$formatCode = $styleAttributes ? (string) $styleAttributes['Format'] : null;
if ($formatCode && Date::isDateTimeFormatCode($formatCode)) {
$styleArray['numberFormat']['formatCode'] = $formatCode;
}
if ($this->readDataOnly === false && $styleAttributes !== null) {
// If readDataOnly is false, we set all formatting information
$styleArray['numberFormat']['formatCode'] = $formatCode;
$styleArray = $this->readStyle($styleArray, $styleAttributes, $style);
}
$this->spreadsheet->getActiveSheet()->getStyle($cellRange)->applyFromArray($styleArray);
}
}
}
private function addBorderDiagonal(SimpleXMLElement $srssb, array &$styleArray): void
{
if (isset($srssb->Diagonal, $srssb->{'Rev-Diagonal'})) {
$styleArray['borders']['diagonal'] = self::parseBorderAttributes($srssb->Diagonal->attributes());
$styleArray['borders']['diagonalDirection'] = Borders::DIAGONAL_BOTH;
} elseif (isset($srssb->Diagonal)) {
$styleArray['borders']['diagonal'] = self::parseBorderAttributes($srssb->Diagonal->attributes());
$styleArray['borders']['diagonalDirection'] = Borders::DIAGONAL_UP;
} elseif (isset($srssb->{'Rev-Diagonal'})) {
$styleArray['borders']['diagonal'] = self::parseBorderAttributes($srssb->{'Rev-Diagonal'}->attributes());
$styleArray['borders']['diagonalDirection'] = Borders::DIAGONAL_DOWN;
}
}
private function addBorderStyle(SimpleXMLElement $srssb, array &$styleArray, string $direction): void
{
$ucDirection = ucfirst($direction);
if (isset($srssb->$ucDirection)) {
$styleArray['borders'][$direction] = self::parseBorderAttributes($srssb->$ucDirection->attributes());
}
}
private function calcRotation(SimpleXMLElement $styleAttributes): int
{
$rotation = (int) $styleAttributes->Rotation;
if ($rotation >= 270 && $rotation <= 360) {
$rotation -= 360;
}
$rotation = (abs($rotation) > 90) ? 0 : $rotation;
return $rotation;
}
private static function addStyle(array &$styleArray, string $key, string $value): void
{
if (array_key_exists($value, self::$mappings[$key])) {
$styleArray[$key] = self::$mappings[$key][$value];
}
}
private static function addStyle2(array &$styleArray, string $key1, string $key, string $value): void
{
if (array_key_exists($value, self::$mappings[$key])) {
$styleArray[$key1][$key] = self::$mappings[$key][$value];
}
}
private static function parseBorderAttributes(?SimpleXMLElement $borderAttributes): array
{
$styleArray = [];
if ($borderAttributes !== null) {
if (isset($borderAttributes['Color'])) {
$styleArray['color']['rgb'] = self::parseGnumericColour($borderAttributes['Color']);
}
self::addStyle($styleArray, 'borderStyle', (string) $borderAttributes['Style']);
}
return $styleArray;
}
private static function parseGnumericColour(string $gnmColour): string
{
[$gnmR, $gnmG, $gnmB] = explode(':', $gnmColour);
$gnmR = substr(str_pad($gnmR, 4, '0', STR_PAD_RIGHT), 0, 2);
$gnmG = substr(str_pad($gnmG, 4, '0', STR_PAD_RIGHT), 0, 2);
$gnmB = substr(str_pad($gnmB, 4, '0', STR_PAD_RIGHT), 0, 2);
return $gnmR . $gnmG . $gnmB;
}
private function addColors(array &$styleArray, SimpleXMLElement $styleAttributes): void
{
$RGB = self::parseGnumericColour((string) $styleAttributes['Fore']);
$styleArray['font']['color']['rgb'] = $RGB;
$RGB = self::parseGnumericColour((string) $styleAttributes['Back']);
$shade = (string) $styleAttributes['Shade'];
if (($RGB !== '000000') || ($shade !== '0')) {
$RGB2 = self::parseGnumericColour((string) $styleAttributes['PatternColor']);
if ($shade === '1') {
$styleArray['fill']['startColor']['rgb'] = $RGB;
$styleArray['fill']['endColor']['rgb'] = $RGB2;
} else {
$styleArray['fill']['endColor']['rgb'] = $RGB;
$styleArray['fill']['startColor']['rgb'] = $RGB2;
}
self::addStyle2($styleArray, 'fill', 'fillType', $shade);
}
}
private function readStyleRange(SimpleXMLElement $styleAttributes, int $maxCol, int $maxRow): string
{
$startColumn = Coordinate::stringFromColumnIndex((int) $styleAttributes['startCol'] + 1);
$startRow = $styleAttributes['startRow'] + 1;
$endColumn = ($styleAttributes['endCol'] > $maxCol) ? $maxCol : (int) $styleAttributes['endCol'];
$endColumn = Coordinate::stringFromColumnIndex($endColumn + 1);
$endRow = 1 + (($styleAttributes['endRow'] > $maxRow) ? $maxRow : (int) $styleAttributes['endRow']);
$cellRange = $startColumn . $startRow . ':' . $endColumn . $endRow;
return $cellRange;
}
private function readStyle(array $styleArray, SimpleXMLElement $styleAttributes, SimpleXMLElement $style): array
{
self::addStyle2($styleArray, 'alignment', 'horizontal', (string) $styleAttributes['HAlign']);
self::addStyle2($styleArray, 'alignment', 'vertical', (string) $styleAttributes['VAlign']);
$styleArray['alignment']['wrapText'] = $styleAttributes['WrapText'] == '1';
$styleArray['alignment']['textRotation'] = $this->calcRotation($styleAttributes);
$styleArray['alignment']['shrinkToFit'] = $styleAttributes['ShrinkToFit'] == '1';
$styleArray['alignment']['indent'] = ((int) ($styleAttributes['Indent']) > 0) ? $styleAttributes['indent'] : 0;
$this->addColors($styleArray, $styleAttributes);
$fontAttributes = $style->Style->Font->attributes();
if ($fontAttributes !== null) {
$styleArray['font']['name'] = (string) $style->Style->Font;
$styleArray['font']['size'] = (int) ($fontAttributes['Unit']);
$styleArray['font']['bold'] = $fontAttributes['Bold'] == '1';
$styleArray['font']['italic'] = $fontAttributes['Italic'] == '1';
$styleArray['font']['strikethrough'] = $fontAttributes['StrikeThrough'] == '1';
self::addStyle2($styleArray, 'font', 'underline', (string) $fontAttributes['Underline']);
switch ($fontAttributes['Script']) {
case '1':
$styleArray['font']['superscript'] = true;
break;
case '-1':
$styleArray['font']['subscript'] = true;
break;
}
}
if (isset($style->Style->StyleBorder)) {
$srssb = $style->Style->StyleBorder;
$this->addBorderStyle($srssb, $styleArray, 'top');
$this->addBorderStyle($srssb, $styleArray, 'bottom');
$this->addBorderStyle($srssb, $styleArray, 'left');
$this->addBorderStyle($srssb, $styleArray, 'right');
$this->addBorderDiagonal($srssb, $styleArray);
}
if (isset($style->Style->HyperLink)) {
// TO DO
$hyperlink = $style->Style->HyperLink->attributes();
}
return $styleArray;
}
}

129
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Ods/Properties.php

@ -0,0 +1,129 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Reader\Ods;
use PhpOffice\PhpSpreadsheet\Document\Properties as DocumentProperties;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use SimpleXMLElement;
class Properties
{
private $spreadsheet;
public function __construct(Spreadsheet $spreadsheet)
{
$this->spreadsheet = $spreadsheet;
}
public function load(SimpleXMLElement $xml, $namespacesMeta): void
{
$docProps = $this->spreadsheet->getProperties();
$officeProperty = $xml->children($namespacesMeta['office']);
foreach ($officeProperty as $officePropertyData) {
// @var \SimpleXMLElement $officePropertyData
if (isset($namespacesMeta['dc'])) {
$officePropertiesDC = $officePropertyData->children($namespacesMeta['dc']);
$this->setCoreProperties($docProps, $officePropertiesDC);
}
$officePropertyMeta = [];
if (isset($namespacesMeta['dc'])) {
$officePropertyMeta = $officePropertyData->children($namespacesMeta['meta']);
}
foreach ($officePropertyMeta as $propertyName => $propertyValue) {
$this->setMetaProperties($namespacesMeta, $propertyValue, $propertyName, $docProps);
}
}
}
private function setCoreProperties(DocumentProperties $docProps, SimpleXMLElement $officePropertyDC): void
{
foreach ($officePropertyDC as $propertyName => $propertyValue) {
$propertyValue = (string) $propertyValue;
switch ($propertyName) {
case 'title':
$docProps->setTitle($propertyValue);
break;
case 'subject':
$docProps->setSubject($propertyValue);
break;
case 'creator':
$docProps->setCreator($propertyValue);
$docProps->setLastModifiedBy($propertyValue);
break;
case 'date':
$docProps->setModified($propertyValue);
break;
case 'description':
$docProps->setDescription($propertyValue);
break;
}
}
}
private function setMetaProperties(
$namespacesMeta,
SimpleXMLElement $propertyValue,
$propertyName,
DocumentProperties $docProps
): void {
$propertyValueAttributes = $propertyValue->attributes($namespacesMeta['meta']);
$propertyValue = (string) $propertyValue;
switch ($propertyName) {
case 'initial-creator':
$docProps->setCreator($propertyValue);
break;
case 'keyword':
$docProps->setKeywords($propertyValue);
break;
case 'creation-date':
$docProps->setCreated($propertyValue);
break;
case 'user-defined':
$this->setUserDefinedProperty($propertyValueAttributes, $propertyValue, $docProps);
break;
}
}
private function setUserDefinedProperty($propertyValueAttributes, $propertyValue, DocumentProperties $docProps): void
{
$propertyValueName = '';
$propertyValueType = DocumentProperties::PROPERTY_TYPE_STRING;
foreach ($propertyValueAttributes as $key => $value) {
if ($key == 'name') {
$propertyValueName = (string) $value;
} elseif ($key == 'value-type') {
switch ($value) {
case 'date':
$propertyValue = DocumentProperties::convertProperty($propertyValue, 'date');
$propertyValueType = DocumentProperties::PROPERTY_TYPE_DATE;
break;
case 'boolean':
$propertyValue = DocumentProperties::convertProperty($propertyValue, 'bool');
$propertyValueType = DocumentProperties::PROPERTY_TYPE_BOOLEAN;
break;
case 'float':
$propertyValue = DocumentProperties::convertProperty($propertyValue, 'r4');
$propertyValueType = DocumentProperties::PROPERTY_TYPE_FLOAT;
break;
default:
$propertyValueType = DocumentProperties::PROPERTY_TYPE_STRING;
}
}
}
$docProps->setCustomProperty($propertyValueName, $propertyValue, $propertyValueType);
}
}

157
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Security/XmlScanner.php

@ -0,0 +1,157 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Reader\Security;
use PhpOffice\PhpSpreadsheet\Reader;
class XmlScanner
{
/**
* String used to identify risky xml elements.
*
* @var string
*/
private $pattern;
private $callback;
private static $libxmlDisableEntityLoaderValue;
/**
* @var bool
*/
private static $shutdownRegistered = false;
public function __construct($pattern = '<!DOCTYPE')
{
$this->pattern = $pattern;
$this->disableEntityLoaderCheck();
// A fatal error will bypass the destructor, so we register a shutdown here
if (!self::$shutdownRegistered) {
self::$shutdownRegistered = true;
register_shutdown_function([__CLASS__, 'shutdown']);
}
}
public static function getInstance(Reader\IReader $reader)
{
switch (true) {
case $reader instanceof Reader\Html:
return new self('<!ENTITY');
case $reader instanceof Reader\Xlsx:
case $reader instanceof Reader\Xml:
case $reader instanceof Reader\Ods:
case $reader instanceof Reader\Gnumeric:
return new self('<!DOCTYPE');
default:
return new self('<!DOCTYPE');
}
}
public static function threadSafeLibxmlDisableEntityLoaderAvailability()
{
if (PHP_MAJOR_VERSION == 7) {
switch (PHP_MINOR_VERSION) {
case 2:
return PHP_RELEASE_VERSION >= 1;
case 1:
return PHP_RELEASE_VERSION >= 13;
case 0:
return PHP_RELEASE_VERSION >= 27;
}
return true;
}
return false;
}
private function disableEntityLoaderCheck(): void
{
if (\PHP_VERSION_ID < 80000) {
$libxmlDisableEntityLoaderValue = libxml_disable_entity_loader(true);
if (self::$libxmlDisableEntityLoaderValue === null) {
self::$libxmlDisableEntityLoaderValue = $libxmlDisableEntityLoaderValue;
}
}
}
public static function shutdown(): void
{
if (self::$libxmlDisableEntityLoaderValue !== null && \PHP_VERSION_ID < 80000) {
libxml_disable_entity_loader(self::$libxmlDisableEntityLoaderValue);
self::$libxmlDisableEntityLoaderValue = null;
}
}
public function __destruct()
{
self::shutdown();
}
public function setAdditionalCallback(callable $callback): void
{
$this->callback = $callback;
}
private function toUtf8($xml)
{
$pattern = '/encoding="(.*?)"/';
$result = preg_match($pattern, $xml, $matches);
$charset = strtoupper($result ? $matches[1] : 'UTF-8');
if ($charset !== 'UTF-8') {
$xml = mb_convert_encoding($xml, 'UTF-8', $charset);
$result = preg_match($pattern, $xml, $matches);
$charset = strtoupper($result ? $matches[1] : 'UTF-8');
if ($charset !== 'UTF-8') {
throw new Reader\Exception('Suspicious Double-encoded XML, spreadsheet file load() aborted to prevent XXE/XEE attacks');
}
}
return $xml;
}
/**
* Scan the XML for use of <!ENTITY to prevent XXE/XEE attacks.
*
* @param mixed $xml
*
* @return string
*/
public function scan($xml)
{
$this->disableEntityLoaderCheck();
$xml = $this->toUtf8($xml);
// Don't rely purely on libxml_disable_entity_loader()
$pattern = '/\\0?' . implode('\\0?', str_split($this->pattern)) . '\\0?/';
if (preg_match($pattern, $xml)) {
throw new Reader\Exception('Detected use of ENTITY in XML, spreadsheet file load() aborted to prevent XXE/XEE attacks');
}
if ($this->callback !== null && is_callable($this->callback)) {
$xml = call_user_func($this->callback, $xml);
}
return $xml;
}
/**
* Scan theXML for use of <!ENTITY to prevent XXE/XEE attacks.
*
* @param string $filestream
*
* @return string
*/
public function scanFile($filestream)
{
return $this->scan(file_get_contents($filestream));
}
}

596
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Slk.php

@ -0,0 +1,596 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Reader;
use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
use PhpOffice\PhpSpreadsheet\Reader\Exception as ReaderException;
use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Style\Border;
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
class Slk extends BaseReader
{
/**
* Input encoding.
*
* @var string
*/
private $inputEncoding = 'ANSI';
/**
* Sheet index to read.
*
* @var int
*/
private $sheetIndex = 0;
/**
* Formats.
*
* @var array
*/
private $formats = [];
/**
* Format Count.
*
* @var int
*/
private $format = 0;
/**
* Fonts.
*
* @var array
*/
private $fonts = [];
/**
* Font Count.
*
* @var int
*/
private $fontcount = 0;
/**
* Create a new SYLK Reader instance.
*/
public function __construct()
{
parent::__construct();
}
/**
* Validate that the current file is a SYLK file.
*/
public function canRead(string $filename): bool
{
try {
$this->openFile($filename);
} catch (ReaderException $e) {
return false;
}
// Read sample data (first 2 KB will do)
$data = (string) fread($this->fileHandle, 2048);
// Count delimiters in file
$delimiterCount = substr_count($data, ';');
$hasDelimiter = $delimiterCount > 0;
// Analyze first line looking for ID; signature
$lines = explode("\n", $data);
$hasId = substr($lines[0], 0, 4) === 'ID;P';
fclose($this->fileHandle);
return $hasDelimiter && $hasId;
}
private function canReadOrBust(string $filename): void
{
if (!$this->canRead($filename)) {
throw new ReaderException($filename . ' is an Invalid SYLK file.');
}
$this->openFile($filename);
}
/**
* Set input encoding.
*
* @deprecated no use is made of this property
*
* @param string $inputEncoding Input encoding, eg: 'ANSI'
*
* @return $this
*
* @codeCoverageIgnore
*/
public function setInputEncoding($inputEncoding)
{
$this->inputEncoding = $inputEncoding;
return $this;
}
/**
* Get input encoding.
*
* @deprecated no use is made of this property
*
* @return string
*
* @codeCoverageIgnore
*/
public function getInputEncoding()
{
return $this->inputEncoding;
}
/**
* Return worksheet info (Name, Last Column Letter, Last Column Index, Total Rows, Total Columns).
*
* @param string $filename
*
* @return array
*/
public function listWorksheetInfo($filename)
{
// Open file
$this->canReadOrBust($filename);
$fileHandle = $this->fileHandle;
rewind($fileHandle);
$worksheetInfo = [];
$worksheetInfo[0]['worksheetName'] = basename($filename, '.slk');
// loop through one row (line) at a time in the file
$rowIndex = 0;
$columnIndex = 0;
while (($rowData = fgets($fileHandle)) !== false) {
$columnIndex = 0;
// convert SYLK encoded $rowData to UTF-8
$rowData = StringHelper::SYLKtoUTF8($rowData);
// explode each row at semicolons while taking into account that literal semicolon (;)
// is escaped like this (;;)
$rowData = explode("\t", str_replace('¤', ';', str_replace(';', "\t", str_replace(';;', '¤', rtrim($rowData)))));
$dataType = array_shift($rowData);
if ($dataType == 'B') {
foreach ($rowData as $rowDatum) {
switch ($rowDatum[0]) {
case 'X':
$columnIndex = (int) substr($rowDatum, 1) - 1;
break;
case 'Y':
$rowIndex = substr($rowDatum, 1);
break;
}
}
break;
}
}
$worksheetInfo[0]['lastColumnIndex'] = $columnIndex;
$worksheetInfo[0]['totalRows'] = $rowIndex;
$worksheetInfo[0]['lastColumnLetter'] = Coordinate::stringFromColumnIndex($worksheetInfo[0]['lastColumnIndex'] + 1);
$worksheetInfo[0]['totalColumns'] = $worksheetInfo[0]['lastColumnIndex'] + 1;
// Close file
fclose($fileHandle);
return $worksheetInfo;
}
/**
* Loads PhpSpreadsheet from file.
*
* @return Spreadsheet
*/
public function load(string $filename, int $flags = 0)
{
$this->processFlags($flags);
// Create new Spreadsheet
$spreadsheet = new Spreadsheet();
// Load into this instance
return $this->loadIntoExisting($filename, $spreadsheet);
}
private const COLOR_ARRAY = [
'FF00FFFF', // 0 - cyan
'FF000000', // 1 - black
'FFFFFFFF', // 2 - white
'FFFF0000', // 3 - red
'FF00FF00', // 4 - green
'FF0000FF', // 5 - blue
'FFFFFF00', // 6 - yellow
'FFFF00FF', // 7 - magenta
];
private const FONT_STYLE_MAPPINGS = [
'B' => 'bold',
'I' => 'italic',
'U' => 'underline',
];
private function processFormula(string $rowDatum, bool &$hasCalculatedValue, string &$cellDataFormula, string $row, string $column): void
{
$cellDataFormula = '=' . substr($rowDatum, 1);
// Convert R1C1 style references to A1 style references (but only when not quoted)
$temp = explode('"', $cellDataFormula);
$key = false;
foreach ($temp as &$value) {
// Only count/replace in alternate array entries
$key = !$key;
if ($key) {
preg_match_all('/(R(\[?-?\d*\]?))(C(\[?-?\d*\]?))/', $value, $cellReferences, PREG_SET_ORDER + PREG_OFFSET_CAPTURE);
// Reverse the matches array, otherwise all our offsets will become incorrect if we modify our way
// through the formula from left to right. Reversing means that we work right to left.through
// the formula
$cellReferences = array_reverse($cellReferences);
// Loop through each R1C1 style reference in turn, converting it to its A1 style equivalent,
// then modify the formula to use that new reference
foreach ($cellReferences as $cellReference) {
$rowReference = $cellReference[2][0];
// Empty R reference is the current row
if ($rowReference == '') {
$rowReference = $row;
}
// Bracketed R references are relative to the current row
if ($rowReference[0] == '[') {
$rowReference = (int) $row + (int) trim($rowReference, '[]');
}
$columnReference = $cellReference[4][0];
// Empty C reference is the current column
if ($columnReference == '') {
$columnReference = $column;
}
// Bracketed C references are relative to the current column
if ($columnReference[0] == '[') {
$columnReference = (int) $column + (int) trim($columnReference, '[]');
}
$A1CellReference = Coordinate::stringFromColumnIndex($columnReference) . $rowReference;
$value = substr_replace($value, $A1CellReference, $cellReference[0][1], strlen($cellReference[0][0]));
}
}
}
unset($value);
// Then rebuild the formula string
$cellDataFormula = implode('"', $temp);
$hasCalculatedValue = true;
}
private function processCRecord(array $rowData, Spreadsheet &$spreadsheet, string &$row, string &$column): void
{
// Read cell value data
$hasCalculatedValue = false;
$cellDataFormula = $cellData = '';
foreach ($rowData as $rowDatum) {
switch ($rowDatum[0]) {
case 'C':
case 'X':
$column = substr($rowDatum, 1);
break;
case 'R':
case 'Y':
$row = substr($rowDatum, 1);
break;
case 'K':
$cellData = substr($rowDatum, 1);
break;
case 'E':
$this->processFormula($rowDatum, $hasCalculatedValue, $cellDataFormula, $row, $column);
break;
case 'A':
$comment = substr($rowDatum, 1);
$columnLetter = Coordinate::stringFromColumnIndex((int) $column);
$spreadsheet->getActiveSheet()
->getComment("$columnLetter$row")
->getText()
->createText($comment);
break;
}
}
$columnLetter = Coordinate::stringFromColumnIndex((int) $column);
$cellData = Calculation::unwrapResult($cellData);
// Set cell value
$this->processCFinal($spreadsheet, $hasCalculatedValue, $cellDataFormula, $cellData, "$columnLetter$row");
}
private function processCFinal(Spreadsheet &$spreadsheet, bool $hasCalculatedValue, string $cellDataFormula, string $cellData, string $coordinate): void
{
// Set cell value
$spreadsheet->getActiveSheet()->getCell($coordinate)->setValue(($hasCalculatedValue) ? $cellDataFormula : $cellData);
if ($hasCalculatedValue) {
$cellData = Calculation::unwrapResult($cellData);
$spreadsheet->getActiveSheet()->getCell($coordinate)->setCalculatedValue($cellData);
}
}
private function processFRecord(array $rowData, Spreadsheet &$spreadsheet, string &$row, string &$column): void
{
// Read cell formatting
$formatStyle = $columnWidth = '';
$startCol = $endCol = '';
$fontStyle = '';
$styleData = [];
foreach ($rowData as $rowDatum) {
switch ($rowDatum[0]) {
case 'C':
case 'X':
$column = substr($rowDatum, 1);
break;
case 'R':
case 'Y':
$row = substr($rowDatum, 1);
break;
case 'P':
$formatStyle = $rowDatum;
break;
case 'W':
[$startCol, $endCol, $columnWidth] = explode(' ', substr($rowDatum, 1));
break;
case 'S':
$this->styleSettings($rowDatum, $styleData, $fontStyle);
break;
}
}
$this->addFormats($spreadsheet, $formatStyle, $row, $column);
$this->addFonts($spreadsheet, $fontStyle, $row, $column);
$this->addStyle($spreadsheet, $styleData, $row, $column);
$this->addWidth($spreadsheet, $columnWidth, $startCol, $endCol);
}
private const STYLE_SETTINGS_FONT = ['D' => 'bold', 'I' => 'italic'];
private const STYLE_SETTINGS_BORDER = [
'B' => 'bottom',
'L' => 'left',
'R' => 'right',
'T' => 'top',
];
private function styleSettings(string $rowDatum, array &$styleData, string &$fontStyle): void
{
$styleSettings = substr($rowDatum, 1);
$iMax = strlen($styleSettings);
for ($i = 0; $i < $iMax; ++$i) {
$char = $styleSettings[$i];
if (array_key_exists($char, self::STYLE_SETTINGS_FONT)) {
$styleData['font'][self::STYLE_SETTINGS_FONT[$char]] = true;
} elseif (array_key_exists($char, self::STYLE_SETTINGS_BORDER)) {
$styleData['borders'][self::STYLE_SETTINGS_BORDER[$char]]['borderStyle'] = Border::BORDER_THIN;
} elseif ($char == 'S') {
$styleData['fill']['fillType'] = \PhpOffice\PhpSpreadsheet\Style\Fill::FILL_PATTERN_GRAY125;
} elseif ($char == 'M') {
if (preg_match('/M([1-9]\\d*)/', $styleSettings, $matches)) {
$fontStyle = $matches[1];
}
}
}
}
private function addFormats(Spreadsheet &$spreadsheet, string $formatStyle, string $row, string $column): void
{
if ($formatStyle && $column > '' && $row > '') {
$columnLetter = Coordinate::stringFromColumnIndex((int) $column);
if (isset($this->formats[$formatStyle])) {
$spreadsheet->getActiveSheet()->getStyle($columnLetter . $row)->applyFromArray($this->formats[$formatStyle]);
}
}
}
private function addFonts(Spreadsheet &$spreadsheet, string $fontStyle, string $row, string $column): void
{
if ($fontStyle && $column > '' && $row > '') {
$columnLetter = Coordinate::stringFromColumnIndex((int) $column);
if (isset($this->fonts[$fontStyle])) {
$spreadsheet->getActiveSheet()->getStyle($columnLetter . $row)->applyFromArray($this->fonts[$fontStyle]);
}
}
}
private function addStyle(Spreadsheet &$spreadsheet, array $styleData, string $row, string $column): void
{
if ((!empty($styleData)) && $column > '' && $row > '') {
$columnLetter = Coordinate::stringFromColumnIndex((int) $column);
$spreadsheet->getActiveSheet()->getStyle($columnLetter . $row)->applyFromArray($styleData);
}
}
private function addWidth(Spreadsheet $spreadsheet, string $columnWidth, string $startCol, string $endCol): void
{
if ($columnWidth > '') {
if ($startCol == $endCol) {
$startCol = Coordinate::stringFromColumnIndex((int) $startCol);
$spreadsheet->getActiveSheet()->getColumnDimension($startCol)->setWidth((float) $columnWidth);
} else {
$startCol = Coordinate::stringFromColumnIndex((int) $startCol);
$endCol = Coordinate::stringFromColumnIndex((int) $endCol);
$spreadsheet->getActiveSheet()->getColumnDimension($startCol)->setWidth((float) $columnWidth);
do {
$spreadsheet->getActiveSheet()->getColumnDimension(++$startCol)->setWidth((float) $columnWidth);
} while ($startCol !== $endCol);
}
}
}
private function processPRecord(array $rowData, Spreadsheet &$spreadsheet): void
{
// Read shared styles
$formatArray = [];
$fromFormats = ['\-', '\ '];
$toFormats = ['-', ' '];
foreach ($rowData as $rowDatum) {
switch ($rowDatum[0]) {
case 'P':
$formatArray['numberFormat']['formatCode'] = str_replace($fromFormats, $toFormats, substr($rowDatum, 1));
break;
case 'E':
case 'F':
$formatArray['font']['name'] = substr($rowDatum, 1);
break;
case 'M':
$formatArray['font']['size'] = substr($rowDatum, 1) / 20;
break;
case 'L':
$this->processPColors($rowDatum, $formatArray);
break;
case 'S':
$this->processPFontStyles($rowDatum, $formatArray);
break;
}
}
$this->processPFinal($spreadsheet, $formatArray);
}
private function processPColors(string $rowDatum, array &$formatArray): void
{
if (preg_match('/L([1-9]\\d*)/', $rowDatum, $matches)) {
$fontColor = $matches[1] % 8;
$formatArray['font']['color']['argb'] = self::COLOR_ARRAY[$fontColor];
}
}
private function processPFontStyles(string $rowDatum, array &$formatArray): void
{
$styleSettings = substr($rowDatum, 1);
$iMax = strlen($styleSettings);
for ($i = 0; $i < $iMax; ++$i) {
if (array_key_exists($styleSettings[$i], self::FONT_STYLE_MAPPINGS)) {
$formatArray['font'][self::FONT_STYLE_MAPPINGS[$styleSettings[$i]]] = true;
}
}
}
private function processPFinal(Spreadsheet &$spreadsheet, array $formatArray): void
{
if (array_key_exists('numberFormat', $formatArray)) {
$this->formats['P' . $this->format] = $formatArray;
++$this->format;
} elseif (array_key_exists('font', $formatArray)) {
++$this->fontcount;
$this->fonts[$this->fontcount] = $formatArray;
if ($this->fontcount === 1) {
$spreadsheet->getDefaultStyle()->applyFromArray($formatArray);
}
}
}
/**
* Loads PhpSpreadsheet from file into PhpSpreadsheet instance.
*
* @param string $filename
*
* @return Spreadsheet
*/
public function loadIntoExisting($filename, Spreadsheet $spreadsheet)
{
// Open file
$this->canReadOrBust($filename);
$fileHandle = $this->fileHandle;
rewind($fileHandle);
// Create new Worksheets
while ($spreadsheet->getSheetCount() <= $this->sheetIndex) {
$spreadsheet->createSheet();
}
$spreadsheet->setActiveSheetIndex($this->sheetIndex);
$spreadsheet->getActiveSheet()->setTitle(substr(basename($filename, '.slk'), 0, Worksheet::SHEET_TITLE_MAXIMUM_LENGTH));
// Loop through file
$column = $row = '';
// loop through one row (line) at a time in the file
while (($rowDataTxt = fgets($fileHandle)) !== false) {
// convert SYLK encoded $rowData to UTF-8
$rowDataTxt = StringHelper::SYLKtoUTF8($rowDataTxt);
// explode each row at semicolons while taking into account that literal semicolon (;)
// is escaped like this (;;)
$rowData = explode("\t", str_replace('¤', ';', str_replace(';', "\t", str_replace(';;', '¤', rtrim($rowDataTxt)))));
$dataType = array_shift($rowData);
if ($dataType == 'P') {
// Read shared styles
$this->processPRecord($rowData, $spreadsheet);
} elseif ($dataType == 'C') {
// Read cell value data
$this->processCRecord($rowData, $spreadsheet, $row, $column);
} elseif ($dataType == 'F') {
// Read cell formatting
$this->processFRecord($rowData, $spreadsheet, $row, $column);
} else {
$this->columnRowFromRowData($rowData, $column, $row);
}
}
// Close file
fclose($fileHandle);
// Return
return $spreadsheet;
}
private function columnRowFromRowData(array $rowData, string &$column, string &$row): void
{
foreach ($rowData as $rowDatum) {
$char0 = $rowDatum[0];
if ($char0 === 'X' || $char0 == 'C') {
$column = substr($rowDatum, 1);
} elseif ($char0 === 'Y' || $char0 == 'R') {
$row = substr($rowDatum, 1);
}
}
}
/**
* Get sheet index.
*
* @return int
*/
public function getSheetIndex()
{
return $this->sheetIndex;
}
/**
* Set sheet index.
*
* @param int $sheetIndex Sheet index
*
* @return $this
*/
public function setSheetIndex($sheetIndex)
{
$this->sheetIndex = $sheetIndex;
return $this;
}
}

7928
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls.php

File diff suppressed because it is too large

61
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xls/RC4.php

@ -0,0 +1,61 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Reader\Xls;
class RC4
{
// Context
protected $s = [];
protected $i = 0;
protected $j = 0;
/**
* RC4 stream decryption/encryption constrcutor.
*
* @param string $key Encryption key/passphrase
*/
public function __construct($key)
{
$len = strlen($key);
for ($this->i = 0; $this->i < 256; ++$this->i) {
$this->s[$this->i] = $this->i;
}
$this->j = 0;
for ($this->i = 0; $this->i < 256; ++$this->i) {
$this->j = ($this->j + $this->s[$this->i] + ord($key[$this->i % $len])) % 256;
$t = $this->s[$this->i];
$this->s[$this->i] = $this->s[$this->j];
$this->s[$this->j] = $t;
}
$this->i = $this->j = 0;
}
/**
* Symmetric decryption/encryption function.
*
* @param string $data Data to encrypt/decrypt
*
* @return string
*/
public function RC4($data)
{
$len = strlen($data);
for ($c = 0; $c < $len; ++$c) {
$this->i = ($this->i + 1) % 256;
$this->j = ($this->j + $this->s[$this->i]) % 256;
$t = $this->s[$this->i];
$this->s[$this->i] = $this->s[$this->j];
$this->s[$this->j] = $t;
$t = ($this->s[$this->i] + $this->s[$this->j]) % 256;
$data[$c] = chr(ord($data[$c]) ^ $this->s[$t]);
}
return $data;
}
}

2107
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx.php

File diff suppressed because it is too large

109
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/Properties.php

@ -0,0 +1,109 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Reader\Xlsx;
use PhpOffice\PhpSpreadsheet\Document\Properties as DocumentProperties;
use PhpOffice\PhpSpreadsheet\Reader\Security\XmlScanner;
use PhpOffice\PhpSpreadsheet\Settings;
use SimpleXMLElement;
class Properties
{
/** @var XmlScanner */
private $securityScanner;
/** @var DocumentProperties */
private $docProps;
public function __construct(XmlScanner $securityScanner, DocumentProperties $docProps)
{
$this->securityScanner = $securityScanner;
$this->docProps = $docProps;
}
/**
* @param mixed $obj
*/
private static function nullOrSimple($obj): ?SimpleXMLElement
{
return ($obj instanceof SimpleXMLElement) ? $obj : null;
}
private function extractPropertyData(string $propertyData): ?SimpleXMLElement
{
// okay to omit namespace because everything will be processed by xpath
$obj = simplexml_load_string(
$this->securityScanner->scan($propertyData),
'SimpleXMLElement',
Settings::getLibXmlLoaderOptions()
);
return self::nullOrSimple($obj);
}
public function readCoreProperties(string $propertyData): void
{
$xmlCore = $this->extractPropertyData($propertyData);
if (is_object($xmlCore)) {
$xmlCore->registerXPathNamespace('dc', Namespaces::DC_ELEMENTS);
$xmlCore->registerXPathNamespace('dcterms', Namespaces::DC_TERMS);
$xmlCore->registerXPathNamespace('cp', Namespaces::CORE_PROPERTIES2);
$this->docProps->setCreator((string) self::getArrayItem($xmlCore->xpath('dc:creator')));
$this->docProps->setLastModifiedBy((string) self::getArrayItem($xmlCore->xpath('cp:lastModifiedBy')));
$this->docProps->setCreated((string) self::getArrayItem($xmlCore->xpath('dcterms:created'))); //! respect xsi:type
$this->docProps->setModified((string) self::getArrayItem($xmlCore->xpath('dcterms:modified'))); //! respect xsi:type
$this->docProps->setTitle((string) self::getArrayItem($xmlCore->xpath('dc:title')));
$this->docProps->setDescription((string) self::getArrayItem($xmlCore->xpath('dc:description')));
$this->docProps->setSubject((string) self::getArrayItem($xmlCore->xpath('dc:subject')));
$this->docProps->setKeywords((string) self::getArrayItem($xmlCore->xpath('cp:keywords')));
$this->docProps->setCategory((string) self::getArrayItem($xmlCore->xpath('cp:category')));
}
}
public function readExtendedProperties(string $propertyData): void
{
$xmlCore = $this->extractPropertyData($propertyData);
if (is_object($xmlCore)) {
if (isset($xmlCore->Company)) {
$this->docProps->setCompany((string) $xmlCore->Company);
}
if (isset($xmlCore->Manager)) {
$this->docProps->setManager((string) $xmlCore->Manager);
}
}
}
public function readCustomProperties(string $propertyData): void
{
$xmlCore = $this->extractPropertyData($propertyData);
if (is_object($xmlCore)) {
foreach ($xmlCore as $xmlProperty) {
/** @var SimpleXMLElement $xmlProperty */
$cellDataOfficeAttributes = $xmlProperty->attributes();
if (isset($cellDataOfficeAttributes['name'])) {
$propertyName = (string) $cellDataOfficeAttributes['name'];
$cellDataOfficeChildren = $xmlProperty->children('http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes');
$attributeType = $cellDataOfficeChildren->getName();
$attributeValue = (string) $cellDataOfficeChildren->{$attributeType};
$attributeValue = DocumentProperties::convertProperty($attributeValue, $attributeType);
$attributeType = DocumentProperties::convertPropertyType($attributeType);
$this->docProps->setCustomProperty($propertyName, $attributeValue, $attributeType);
}
}
}
}
/**
* @param array|false $array
* @param mixed $key
*/
private static function getArrayItem($array, $key = 0): ?SimpleXMLElement
{
return is_array($array) ? ($array[$key] ?? null) : null;
}
}

132
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/SheetViewOptions.php

@ -0,0 +1,132 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Reader\Xlsx;
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
use SimpleXMLElement;
class SheetViewOptions extends BaseParserClass
{
private $worksheet;
private $worksheetXml;
public function __construct(Worksheet $workSheet, ?SimpleXMLElement $worksheetXml = null)
{
$this->worksheet = $workSheet;
$this->worksheetXml = $worksheetXml;
}
public function load(bool $readDataOnly, Styles $styleReader): void
{
if ($this->worksheetXml === null) {
return;
}
if (isset($this->worksheetXml->sheetPr)) {
$this->tabColor($this->worksheetXml->sheetPr, $styleReader);
$this->codeName($this->worksheetXml->sheetPr);
$this->outlines($this->worksheetXml->sheetPr);
$this->pageSetup($this->worksheetXml->sheetPr);
}
if (isset($this->worksheetXml->sheetFormatPr)) {
$this->sheetFormat($this->worksheetXml->sheetFormatPr);
}
if (!$readDataOnly && isset($this->worksheetXml->printOptions)) {
$this->printOptions($this->worksheetXml->printOptions);
}
}
private function tabColor(SimpleXMLElement $sheetPr, Styles $styleReader): void
{
if (isset($sheetPr->tabColor)) {
$this->worksheet->getTabColor()->setARGB($styleReader->readColor($sheetPr->tabColor));
}
}
private function codeName(SimpleXMLElement $sheetPr): void
{
if (isset($sheetPr['codeName'])) {
$this->worksheet->setCodeName((string) $sheetPr['codeName'], false);
}
}
private function outlines(SimpleXMLElement $sheetPr): void
{
if (isset($sheetPr->outlinePr)) {
if (
isset($sheetPr->outlinePr['summaryRight']) &&
!self::boolean((string) $sheetPr->outlinePr['summaryRight'])
) {
$this->worksheet->setShowSummaryRight(false);
} else {
$this->worksheet->setShowSummaryRight(true);
}
if (
isset($sheetPr->outlinePr['summaryBelow']) &&
!self::boolean((string) $sheetPr->outlinePr['summaryBelow'])
) {
$this->worksheet->setShowSummaryBelow(false);
} else {
$this->worksheet->setShowSummaryBelow(true);
}
}
}
private function pageSetup(SimpleXMLElement $sheetPr): void
{
if (isset($sheetPr->pageSetUpPr)) {
if (
isset($sheetPr->pageSetUpPr['fitToPage']) &&
!self::boolean((string) $sheetPr->pageSetUpPr['fitToPage'])
) {
$this->worksheet->getPageSetup()->setFitToPage(false);
} else {
$this->worksheet->getPageSetup()->setFitToPage(true);
}
}
}
private function sheetFormat(SimpleXMLElement $sheetFormatPr): void
{
if (
isset($sheetFormatPr['customHeight']) &&
self::boolean((string) $sheetFormatPr['customHeight']) &&
isset($sheetFormatPr['defaultRowHeight'])
) {
$this->worksheet->getDefaultRowDimension()
->setRowHeight((float) $sheetFormatPr['defaultRowHeight']);
}
if (isset($sheetFormatPr['defaultColWidth'])) {
$this->worksheet->getDefaultColumnDimension()
->setWidth((float) $sheetFormatPr['defaultColWidth']);
}
if (
isset($sheetFormatPr['zeroHeight']) &&
((string) $sheetFormatPr['zeroHeight'] === '1')
) {
$this->worksheet->getDefaultRowDimension()->setZeroHeight(true);
}
}
private function printOptions(SimpleXMLElement $printOptions): void
{
if (self::boolean((string) $printOptions['gridLinesSet'])) {
$this->worksheet->setShowGridlines(true);
}
if (self::boolean((string) $printOptions['gridLines'])) {
$this->worksheet->setPrintGridlines(true);
}
if (self::boolean((string) $printOptions['horizontalCentered'])) {
$this->worksheet->getPageSetup()->setHorizontalCentered(true);
}
if (self::boolean((string) $printOptions['verticalCentered'])) {
$this->worksheet->getPageSetup()->setVerticalCentered(true);
}
}
}

156
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/SheetViews.php

@ -0,0 +1,156 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Reader\Xlsx;
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
use PhpOffice\PhpSpreadsheet\Reader\Xlsx;
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
use SimpleXMLElement;
class SheetViews extends BaseParserClass
{
/** @var SimpleXMLElement */
private $sheetViewXml;
/** @var SimpleXMLElement */
private $sheetViewAttributes;
/** @var Worksheet */
private $worksheet;
public function __construct(SimpleXMLElement $sheetViewXml, Worksheet $workSheet)
{
$this->sheetViewXml = $sheetViewXml;
$this->sheetViewAttributes = Xlsx::testSimpleXml($sheetViewXml->attributes());
$this->worksheet = $workSheet;
}
public function load(): void
{
$this->topLeft();
$this->zoomScale();
$this->view();
$this->gridLines();
$this->headers();
$this->direction();
$this->showZeros();
if (isset($this->sheetViewXml->pane)) {
$this->pane();
}
if (isset($this->sheetViewXml->selection, $this->sheetViewXml->selection->attributes()->sqref)) {
$this->selection();
}
}
private function zoomScale(): void
{
if (isset($this->sheetViewAttributes->zoomScale)) {
$zoomScale = (int) ($this->sheetViewAttributes->zoomScale);
if ($zoomScale <= 0) {
// setZoomScale will throw an Exception if the scale is less than or equals 0
// that is OK when manually creating documents, but we should be able to read all documents
$zoomScale = 100;
}
$this->worksheet->getSheetView()->setZoomScale($zoomScale);
}
if (isset($this->sheetViewAttributes->zoomScaleNormal)) {
$zoomScaleNormal = (int) ($this->sheetViewAttributes->zoomScaleNormal);
if ($zoomScaleNormal <= 0) {
// setZoomScaleNormal will throw an Exception if the scale is less than or equals 0
// that is OK when manually creating documents, but we should be able to read all documents
$zoomScaleNormal = 100;
}
$this->worksheet->getSheetView()->setZoomScaleNormal($zoomScaleNormal);
}
}
private function view(): void
{
if (isset($this->sheetViewAttributes->view)) {
$this->worksheet->getSheetView()->setView((string) $this->sheetViewAttributes->view);
}
}
private function topLeft(): void
{
if (isset($this->sheetViewAttributes->topLeftCell)) {
$this->worksheet->setTopLeftCell($this->sheetViewAttributes->topLeftCell);
}
}
private function gridLines(): void
{
if (isset($this->sheetViewAttributes->showGridLines)) {
$this->worksheet->setShowGridLines(
self::boolean((string) $this->sheetViewAttributes->showGridLines)
);
}
}
private function headers(): void
{
if (isset($this->sheetViewAttributes->showRowColHeaders)) {
$this->worksheet->setShowRowColHeaders(
self::boolean((string) $this->sheetViewAttributes->showRowColHeaders)
);
}
}
private function direction(): void
{
if (isset($this->sheetViewAttributes->rightToLeft)) {
$this->worksheet->setRightToLeft(
self::boolean((string) $this->sheetViewAttributes->rightToLeft)
);
}
}
private function showZeros(): void
{
if (isset($this->sheetViewAttributes->showZeros)) {
$this->worksheet->getSheetView()->setShowZeros(
self::boolean((string) $this->sheetViewAttributes->showZeros)
);
}
}
private function pane(): void
{
$xSplit = 0;
$ySplit = 0;
$topLeftCell = null;
$paneAttributes = $this->sheetViewXml->pane->attributes();
if (isset($paneAttributes->xSplit)) {
$xSplit = (int) ($paneAttributes->xSplit);
}
if (isset($paneAttributes->ySplit)) {
$ySplit = (int) ($paneAttributes->ySplit);
}
if (isset($paneAttributes->topLeftCell)) {
$topLeftCell = (string) $paneAttributes->topLeftCell;
}
$this->worksheet->freezePane(
Coordinate::stringFromColumnIndex($xSplit + 1) . ($ySplit + 1),
$topLeftCell
);
}
private function selection(): void
{
$attributes = $this->sheetViewXml->selection->attributes();
if ($attributes !== null) {
$sqref = (string) $attributes->sqref;
$sqref = explode(' ', $sqref);
$sqref = $sqref[0];
$this->worksheet->setSelectedCells($sqref);
}
}
}

423
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/Styles.php

@ -0,0 +1,423 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Reader\Xlsx;
use PhpOffice\PhpSpreadsheet\Reader\Xlsx;
use PhpOffice\PhpSpreadsheet\Style\Alignment;
use PhpOffice\PhpSpreadsheet\Style\Border;
use PhpOffice\PhpSpreadsheet\Style\Borders;
use PhpOffice\PhpSpreadsheet\Style\Color;
use PhpOffice\PhpSpreadsheet\Style\Fill;
use PhpOffice\PhpSpreadsheet\Style\Font;
use PhpOffice\PhpSpreadsheet\Style\NumberFormat;
use PhpOffice\PhpSpreadsheet\Style\Protection;
use PhpOffice\PhpSpreadsheet\Style\Style;
use SimpleXMLElement;
use stdClass;
class Styles extends BaseParserClass
{
/**
* Theme instance.
*
* @var ?Theme
*/
private $theme;
/** @var array */
private $styles = [];
/** @var array */
private $cellStyles = [];
/** @var SimpleXMLElement */
private $styleXml;
/** @var string */
private $namespace = '';
public function setNamespace(string $namespace): void
{
$this->namespace = $namespace;
}
/**
* Cast SimpleXMLElement to bool to overcome Scrutinizer problem.
*
* @param mixed $value
*/
private static function castBool($value): bool
{
return (bool) $value;
}
private function getStyleAttributes(SimpleXMLElement $value): SimpleXMLElement
{
$attr = null;
if (self::castBool($value)) {
$attr = $value->attributes('');
if ($attr === null || count($attr) === 0) {
$attr = $value->attributes($this->namespace);
}
}
return Xlsx::testSimpleXml($attr);
}
public function setStyleXml(SimpleXmlElement $styleXml): void
{
$this->styleXml = $styleXml;
}
public function setTheme(Theme $theme): void
{
$this->theme = $theme;
}
public function setStyleBaseData(?Theme $theme = null, array $styles = [], array $cellStyles = []): void
{
$this->theme = $theme;
$this->styles = $styles;
$this->cellStyles = $cellStyles;
}
public function readFontStyle(Font $fontStyle, SimpleXMLElement $fontStyleXml): void
{
if (isset($fontStyleXml->name)) {
$attr = $this->getStyleAttributes($fontStyleXml->name);
if (isset($attr['val'])) {
$fontStyle->setName((string) $attr['val']);
}
}
if (isset($fontStyleXml->sz)) {
$attr = $this->getStyleAttributes($fontStyleXml->sz);
if (isset($attr['val'])) {
$fontStyle->setSize((float) $attr['val']);
}
}
if (isset($fontStyleXml->b)) {
$attr = $this->getStyleAttributes($fontStyleXml->b);
$fontStyle->setBold(!isset($attr['val']) || self::boolean((string) $attr['val']));
}
if (isset($fontStyleXml->i)) {
$attr = $this->getStyleAttributes($fontStyleXml->i);
$fontStyle->setItalic(!isset($attr['val']) || self::boolean((string) $attr['val']));
}
if (isset($fontStyleXml->strike)) {
$attr = $this->getStyleAttributes($fontStyleXml->strike);
$fontStyle->setStrikethrough(!isset($attr['val']) || self::boolean((string) $attr['val']));
}
$fontStyle->getColor()->setARGB($this->readColor($fontStyleXml->color));
if (isset($fontStyleXml->u)) {
$attr = $this->getStyleAttributes($fontStyleXml->u);
if (!isset($attr['val'])) {
$fontStyle->setUnderline(Font::UNDERLINE_SINGLE);
} else {
$fontStyle->setUnderline((string) $attr['val']);
}
}
if (isset($fontStyleXml->vertAlign)) {
$attr = $this->getStyleAttributes($fontStyleXml->vertAlign);
if (!isset($attr['val'])) {
$verticalAlign = strtolower((string) $attr['val']);
if ($verticalAlign === 'superscript') {
$fontStyle->setSuperscript(true);
} elseif ($verticalAlign === 'subscript') {
$fontStyle->setSubscript(true);
}
}
}
}
private function readNumberFormat(NumberFormat $numfmtStyle, SimpleXMLElement $numfmtStyleXml): void
{
if ((string) $numfmtStyleXml['formatCode'] !== '') {
$numfmtStyle->setFormatCode(self::formatGeneral((string) $numfmtStyleXml['formatCode']));
return;
}
$numfmt = $this->getStyleAttributes($numfmtStyleXml);
if (isset($numfmt['formatCode'])) {
$numfmtStyle->setFormatCode(self::formatGeneral((string) $numfmt['formatCode']));
}
}
public function readFillStyle(Fill $fillStyle, SimpleXMLElement $fillStyleXml): void
{
if ($fillStyleXml->gradientFill) {
/** @var SimpleXMLElement $gradientFill */
$gradientFill = $fillStyleXml->gradientFill[0];
$attr = $this->getStyleAttributes($gradientFill);
if (!empty($attr['type'])) {
$fillStyle->setFillType((string) $attr['type']);
}
$fillStyle->setRotation((float) ($attr['degree']));
$gradientFill->registerXPathNamespace('sml', Namespaces::MAIN);
$fillStyle->getStartColor()->setARGB($this->readColor(self::getArrayItem($gradientFill->xpath('sml:stop[@position=0]'))->color));
$fillStyle->getEndColor()->setARGB($this->readColor(self::getArrayItem($gradientFill->xpath('sml:stop[@position=1]'))->color));
} elseif ($fillStyleXml->patternFill) {
$defaultFillStyle = Fill::FILL_NONE;
if ($fillStyleXml->patternFill->fgColor) {
$fillStyle->getStartColor()->setARGB($this->readColor($fillStyleXml->patternFill->fgColor, true));
$defaultFillStyle = Fill::FILL_SOLID;
}
if ($fillStyleXml->patternFill->bgColor) {
$fillStyle->getEndColor()->setARGB($this->readColor($fillStyleXml->patternFill->bgColor, true));
$defaultFillStyle = Fill::FILL_SOLID;
}
$type = '';
if ((string) $fillStyleXml->patternFill['patternType'] !== '') {
$type = (string) $fillStyleXml->patternFill['patternType'];
} else {
$attr = $this->getStyleAttributes($fillStyleXml->patternFill);
$type = (string) $attr['patternType'];
}
$patternType = ($type === '') ? $defaultFillStyle : $type;
$fillStyle->setFillType($patternType);
}
}
public function readBorderStyle(Borders $borderStyle, SimpleXMLElement $borderStyleXml): void
{
$diagonalUp = $this->getAttribute($borderStyleXml, 'diagonalUp');
$diagonalUp = self::boolean($diagonalUp);
$diagonalDown = $this->getAttribute($borderStyleXml, 'diagonalDown');
$diagonalDown = self::boolean($diagonalDown);
if (!$diagonalUp && !$diagonalDown) {
$borderStyle->setDiagonalDirection(Borders::DIAGONAL_NONE);
} elseif ($diagonalUp && !$diagonalDown) {
$borderStyle->setDiagonalDirection(Borders::DIAGONAL_UP);
} elseif (!$diagonalUp && $diagonalDown) {
$borderStyle->setDiagonalDirection(Borders::DIAGONAL_DOWN);
} else {
$borderStyle->setDiagonalDirection(Borders::DIAGONAL_BOTH);
}
$this->readBorder($borderStyle->getLeft(), $borderStyleXml->left);
$this->readBorder($borderStyle->getRight(), $borderStyleXml->right);
$this->readBorder($borderStyle->getTop(), $borderStyleXml->top);
$this->readBorder($borderStyle->getBottom(), $borderStyleXml->bottom);
$this->readBorder($borderStyle->getDiagonal(), $borderStyleXml->diagonal);
}
private function getAttribute(SimpleXMLElement $xml, string $attribute): string
{
$style = '';
if ((string) $xml[$attribute] !== '') {
$style = (string) $xml[$attribute];
} else {
$attr = $this->getStyleAttributes($xml);
if (isset($attr[$attribute])) {
$style = (string) $attr[$attribute];
}
}
return $style;
}
private function readBorder(Border $border, SimpleXMLElement $borderXml): void
{
$style = $this->getAttribute($borderXml, 'style');
if ($style !== '') {
$border->setBorderStyle((string) $style);
}
if (isset($borderXml->color)) {
$border->getColor()->setARGB($this->readColor($borderXml->color));
}
}
public function readAlignmentStyle(Alignment $alignment, SimpleXMLElement $alignmentXml): void
{
$horizontal = $this->getAttribute($alignmentXml, 'horizontal');
$alignment->setHorizontal($horizontal);
$vertical = $this->getAttribute($alignmentXml, 'vertical');
$alignment->setVertical((string) $vertical);
$textRotation = (int) $this->getAttribute($alignmentXml, 'textRotation');
if ($textRotation > 90) {
$textRotation = 90 - $textRotation;
}
$alignment->setTextRotation($textRotation);
$wrapText = $this->getAttribute($alignmentXml, 'wrapText');
$alignment->setWrapText(self::boolean((string) $wrapText));
$shrinkToFit = $this->getAttribute($alignmentXml, 'shrinkToFit');
$alignment->setShrinkToFit(self::boolean((string) $shrinkToFit));
$indent = (int) $this->getAttribute($alignmentXml, 'indent');
$alignment->setIndent(max($indent, 0));
$readingOrder = (int) $this->getAttribute($alignmentXml, 'readingOrder');
$alignment->setReadOrder(max($readingOrder, 0));
}
private static function formatGeneral(string $formatString): string
{
if ($formatString === 'GENERAL') {
$formatString = NumberFormat::FORMAT_GENERAL;
}
return $formatString;
}
/**
* Read style.
*
* @param SimpleXMLElement|stdClass $style
*/
public function readStyle(Style $docStyle, $style): void
{
if ($style->numFmt instanceof SimpleXMLElement) {
$this->readNumberFormat($docStyle->getNumberFormat(), $style->numFmt);
} else {
$docStyle->getNumberFormat()->setFormatCode(self::formatGeneral((string) $style->numFmt));
}
if (isset($style->font)) {
$this->readFontStyle($docStyle->getFont(), $style->font);
}
if (isset($style->fill)) {
$this->readFillStyle($docStyle->getFill(), $style->fill);
}
if (isset($style->border)) {
$this->readBorderStyle($docStyle->getBorders(), $style->border);
}
if (isset($style->alignment)) {
$this->readAlignmentStyle($docStyle->getAlignment(), $style->alignment);
}
// protection
if (isset($style->protection)) {
$this->readProtectionLocked($docStyle, $style->protection);
$this->readProtectionHidden($docStyle, $style->protection);
}
// top-level style settings
if (isset($style->quotePrefix)) {
$docStyle->setQuotePrefix((bool) $style->quotePrefix);
}
}
/**
* Read protection locked attribute.
*/
public function readProtectionLocked(Style $docStyle, SimpleXMLElement $style): void
{
$locked = '';
if ((string) $style['locked'] !== '') {
$locked = (string) $style['locked'];
} else {
$attr = $this->getStyleAttributes($style);
if (isset($attr['locked'])) {
$locked = (string) $attr['locked'];
}
}
if ($locked !== '') {
if (self::boolean($locked)) {
$docStyle->getProtection()->setLocked(Protection::PROTECTION_PROTECTED);
} else {
$docStyle->getProtection()->setLocked(Protection::PROTECTION_UNPROTECTED);
}
}
}
/**
* Read protection hidden attribute.
*/
public function readProtectionHidden(Style $docStyle, SimpleXMLElement $style): void
{
$hidden = '';
if ((string) $style['hidden'] !== '') {
$hidden = (string) $style['hidden'];
} else {
$attr = $this->getStyleAttributes($style);
if (isset($attr['hidden'])) {
$hidden = (string) $attr['hidden'];
}
}
if ($hidden !== '') {
if (self::boolean((string) $hidden)) {
$docStyle->getProtection()->setHidden(Protection::PROTECTION_PROTECTED);
} else {
$docStyle->getProtection()->setHidden(Protection::PROTECTION_UNPROTECTED);
}
}
}
public function readColor(SimpleXMLElement $color, bool $background = false): string
{
$attr = $this->getStyleAttributes($color);
if (isset($attr['rgb'])) {
return (string) $attr['rgb'];
}
if (isset($attr['indexed'])) {
return Color::indexedColor((int) ($attr['indexed'] - 7), $background)->getARGB() ?? '';
}
if (isset($attr['theme'])) {
if ($this->theme !== null) {
$returnColour = $this->theme->getColourByIndex((int) $attr['theme']);
if (isset($attr['tint'])) {
$tintAdjust = (float) $attr['tint'];
$returnColour = Color::changeBrightness($returnColour ?? '', $tintAdjust);
}
return 'FF' . $returnColour;
}
}
return ($background) ? 'FFFFFFFF' : 'FF000000';
}
public function dxfs(bool $readDataOnly = false): array
{
$dxfs = [];
if (!$readDataOnly && $this->styleXml) {
// Conditional Styles
if ($this->styleXml->dxfs) {
foreach ($this->styleXml->dxfs->dxf as $dxf) {
$style = new Style(false, true);
$this->readStyle($style, $dxf);
$dxfs[] = $style;
}
}
// Cell Styles
if ($this->styleXml->cellStyles) {
foreach ($this->styleXml->cellStyles->cellStyle as $cellStylex) {
$cellStyle = Xlsx::getAttributes($cellStylex);
if ((int) ($cellStyle['builtinId']) == 0) {
if (isset($this->cellStyles[(int) ($cellStyle['xfId'])])) {
// Set default style
$style = new Style();
$this->readStyle($style, $this->cellStyles[(int) ($cellStyle['xfId'])]);
// normal style, currently not using it for anything
}
}
}
}
}
return $dxfs;
}
public function styles(): array
{
return $this->styles;
}
/**
* Get array item.
*
* @param mixed $array (usually array, in theory can be false)
*
* @return stdClass
*/
private static function getArrayItem($array, int $key = 0)
{
return is_array($array) ? ($array[$key] ?? null) : null;
}
}

93
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xlsx/Theme.php

@ -0,0 +1,93 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Reader\Xlsx;
class Theme
{
/**
* Theme Name.
*
* @var string
*/
private $themeName;
/**
* Colour Scheme Name.
*
* @var string
*/
private $colourSchemeName;
/**
* Colour Map.
*
* @var string[]
*/
private $colourMap;
/**
* Create a new Theme.
*
* @param string $themeName
* @param string $colourSchemeName
* @param string[] $colourMap
*/
public function __construct($themeName, $colourSchemeName, $colourMap)
{
// Initialise values
$this->themeName = $themeName;
$this->colourSchemeName = $colourSchemeName;
$this->colourMap = $colourMap;
}
/**
* Get Theme Name.
*
* @return string
*/
public function getThemeName()
{
return $this->themeName;
}
/**
* Get colour Scheme Name.
*
* @return string
*/
public function getColourSchemeName()
{
return $this->colourSchemeName;
}
/**
* Get colour Map Value by Position.
*
* @param int $index
*
* @return null|string
*/
public function getColourByIndex($index)
{
if (isset($this->colourMap[$index])) {
return $this->colourMap[$index];
}
return null;
}
/**
* Implement PHP __clone to create a deep clone, not just a shallow copy.
*/
public function __clone()
{
$vars = get_object_vars($this);
foreach ($vars as $key => $value) {
if ((is_object($value)) && ($key != '_parent')) {
$this->$key = clone $value;
} else {
$this->$key = $value;
}
}
}
}

536
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xml.php

@ -0,0 +1,536 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Reader;
use DateTime;
use DateTimeZone;
use PhpOffice\PhpSpreadsheet\Cell\AddressHelper;
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
use PhpOffice\PhpSpreadsheet\Cell\DataType;
use PhpOffice\PhpSpreadsheet\DefinedName;
use PhpOffice\PhpSpreadsheet\Reader\Security\XmlScanner;
use PhpOffice\PhpSpreadsheet\Reader\Xml\PageSettings;
use PhpOffice\PhpSpreadsheet\Reader\Xml\Properties;
use PhpOffice\PhpSpreadsheet\Reader\Xml\Style;
use PhpOffice\PhpSpreadsheet\RichText\RichText;
use PhpOffice\PhpSpreadsheet\Settings;
use PhpOffice\PhpSpreadsheet\Shared\Date;
use PhpOffice\PhpSpreadsheet\Shared\File;
use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use SimpleXMLElement;
/**
* Reader for SpreadsheetML, the XML schema for Microsoft Office Excel 2003.
*/
class Xml extends BaseReader
{
/**
* Formats.
*
* @var array
*/
protected $styles = [];
/**
* Create a new Excel2003XML Reader instance.
*/
public function __construct()
{
parent::__construct();
$this->securityScanner = XmlScanner::getInstance($this);
}
private $fileContents = '';
public static function xmlMappings(): array
{
return array_merge(
Style\Fill::FILL_MAPPINGS,
Style\Border::BORDER_MAPPINGS
);
}
/**
* Can the current IReader read the file?
*/
public function canRead(string $filename): bool
{
// Office xmlns:o="urn:schemas-microsoft-com:office:office"
// Excel xmlns:x="urn:schemas-microsoft-com:office:excel"
// XML Spreadsheet xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet"
// Spreadsheet component xmlns:c="urn:schemas-microsoft-com:office:component:spreadsheet"
// XML schema xmlns:s="uuid:BDC6E3F0-6DA3-11d1-A2A3-00AA00C14882"
// XML data type xmlns:dt="uuid:C2F41010-65B3-11d1-A29F-00AA00C14882"
// MS-persist recordset xmlns:rs="urn:schemas-microsoft-com:rowset"
// Rowset xmlns:z="#RowsetSchema"
//
$signature = [
'<?xml version="1.0"',
'xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet',
];
// Open file
$data = file_get_contents($filename);
// Why?
//$data = str_replace("'", '"', $data); // fix headers with single quote
$valid = true;
foreach ($signature as $match) {
// every part of the signature must be present
if (strpos($data, $match) === false) {
$valid = false;
break;
}
}
// Retrieve charset encoding
if (preg_match('/<?xml.*encoding=[\'"](.*?)[\'"].*?>/m', $data, $matches)) {
$charSet = strtoupper($matches[1]);
if (1 == preg_match('/^ISO-8859-\d[\dL]?$/i', $charSet)) {
$data = StringHelper::convertEncoding($data, 'UTF-8', $charSet);
$data = preg_replace('/(<?xml.*encoding=[\'"]).*?([\'"].*?>)/um', '$1' . 'UTF-8' . '$2', $data, 1);
}
}
$this->fileContents = $data;
return $valid;
}
/**
* Check if the file is a valid SimpleXML.
*
* @param string $filename
*
* @return false|SimpleXMLElement
*/
public function trySimpleXMLLoadString($filename)
{
try {
$xml = simplexml_load_string(
$this->securityScanner->scan($this->fileContents ?: file_get_contents($filename)),
'SimpleXMLElement',
Settings::getLibXmlLoaderOptions()
);
} catch (\Exception $e) {
throw new Exception('Cannot load invalid XML file: ' . $filename, 0, $e);
}
$this->fileContents = '';
return $xml;
}
/**
* Reads names of the worksheets from a file, without parsing the whole file to a Spreadsheet object.
*
* @param string $filename
*
* @return array
*/
public function listWorksheetNames($filename)
{
File::assertFile($filename);
if (!$this->canRead($filename)) {
throw new Exception($filename . ' is an Invalid Spreadsheet file.');
}
$worksheetNames = [];
$xml = $this->trySimpleXMLLoadString($filename);
if ($xml === false) {
throw new Exception("Problem reading {$filename}");
}
$namespaces = $xml->getNamespaces(true);
$xml_ss = $xml->children($namespaces['ss']);
foreach ($xml_ss->Worksheet as $worksheet) {
$worksheet_ss = self::getAttributes($worksheet, $namespaces['ss']);
$worksheetNames[] = (string) $worksheet_ss['Name'];
}
return $worksheetNames;
}
/**
* Return worksheet info (Name, Last Column Letter, Last Column Index, Total Rows, Total Columns).
*
* @param string $filename
*
* @return array
*/
public function listWorksheetInfo($filename)
{
File::assertFile($filename);
if (!$this->canRead($filename)) {
throw new Exception($filename . ' is an Invalid Spreadsheet file.');
}
$worksheetInfo = [];
$xml = $this->trySimpleXMLLoadString($filename);
if ($xml === false) {
throw new Exception("Problem reading {$filename}");
}
$namespaces = $xml->getNamespaces(true);
$worksheetID = 1;
$xml_ss = $xml->children($namespaces['ss']);
foreach ($xml_ss->Worksheet as $worksheet) {
$worksheet_ss = self::getAttributes($worksheet, $namespaces['ss']);
$tmpInfo = [];
$tmpInfo['worksheetName'] = '';
$tmpInfo['lastColumnLetter'] = 'A';
$tmpInfo['lastColumnIndex'] = 0;
$tmpInfo['totalRows'] = 0;
$tmpInfo['totalColumns'] = 0;
$tmpInfo['worksheetName'] = "Worksheet_{$worksheetID}";
if (isset($worksheet_ss['Name'])) {
$tmpInfo['worksheetName'] = (string) $worksheet_ss['Name'];
}
if (isset($worksheet->Table->Row)) {
$rowIndex = 0;
foreach ($worksheet->Table->Row as $rowData) {
$columnIndex = 0;
$rowHasData = false;
foreach ($rowData->Cell as $cell) {
if (isset($cell->Data)) {
$tmpInfo['lastColumnIndex'] = max($tmpInfo['lastColumnIndex'], $columnIndex);
$rowHasData = true;
}
++$columnIndex;
}
++$rowIndex;
if ($rowHasData) {
$tmpInfo['totalRows'] = max($tmpInfo['totalRows'], $rowIndex);
}
}
}
$tmpInfo['lastColumnLetter'] = Coordinate::stringFromColumnIndex($tmpInfo['lastColumnIndex'] + 1);
$tmpInfo['totalColumns'] = $tmpInfo['lastColumnIndex'] + 1;
$worksheetInfo[] = $tmpInfo;
++$worksheetID;
}
return $worksheetInfo;
}
/**
* Loads Spreadsheet from file.
*
* @return Spreadsheet
*/
public function load(string $filename, int $flags = 0)
{
$this->processFlags($flags);
// Create new Spreadsheet
$spreadsheet = new Spreadsheet();
$spreadsheet->removeSheetByIndex(0);
// Load into this instance
return $this->loadIntoExisting($filename, $spreadsheet);
}
/**
* Loads from file into Spreadsheet instance.
*
* @param string $filename
*
* @return Spreadsheet
*/
public function loadIntoExisting($filename, Spreadsheet $spreadsheet)
{
File::assertFile($filename);
if (!$this->canRead($filename)) {
throw new Exception($filename . ' is an Invalid Spreadsheet file.');
}
$xml = $this->trySimpleXMLLoadString($filename);
if ($xml === false) {
throw new Exception("Problem reading {$filename}");
}
$namespaces = $xml->getNamespaces(true);
(new Properties($spreadsheet))->readProperties($xml, $namespaces);
$this->styles = (new Style())->parseStyles($xml, $namespaces);
$worksheetID = 0;
$xml_ss = $xml->children($namespaces['ss']);
/** @var null|SimpleXMLElement $worksheetx */
foreach ($xml_ss->Worksheet as $worksheetx) {
$worksheet = $worksheetx ?? new SimpleXMLElement('<xml></xml>');
$worksheet_ss = self::getAttributes($worksheet, $namespaces['ss']);
if (
isset($this->loadSheetsOnly, $worksheet_ss['Name']) &&
(!in_array($worksheet_ss['Name'], $this->loadSheetsOnly))
) {
continue;
}
// Create new Worksheet
$spreadsheet->createSheet();
$spreadsheet->setActiveSheetIndex($worksheetID);
$worksheetName = '';
if (isset($worksheet_ss['Name'])) {
$worksheetName = (string) $worksheet_ss['Name'];
// Use false for $updateFormulaCellReferences to prevent adjustment of worksheet references in
// formula cells... during the load, all formulae should be correct, and we're simply bringing
// the worksheet name in line with the formula, not the reverse
$spreadsheet->getActiveSheet()->setTitle($worksheetName, false, false);
}
// locally scoped defined names
if (isset($worksheet->Names[0])) {
foreach ($worksheet->Names[0] as $definedName) {
$definedName_ss = self::getAttributes($definedName, $namespaces['ss']);
$name = (string) $definedName_ss['Name'];
$definedValue = (string) $definedName_ss['RefersTo'];
$convertedValue = AddressHelper::convertFormulaToA1($definedValue);
if ($convertedValue[0] === '=') {
$convertedValue = substr($convertedValue, 1);
}
$spreadsheet->addDefinedName(DefinedName::createInstance($name, $spreadsheet->getActiveSheet(), $convertedValue, true));
}
}
$columnID = 'A';
if (isset($worksheet->Table->Column)) {
foreach ($worksheet->Table->Column as $columnData) {
$columnData_ss = self::getAttributes($columnData, $namespaces['ss']);
if (isset($columnData_ss['Index'])) {
$columnID = Coordinate::stringFromColumnIndex((int) $columnData_ss['Index']);
}
if (isset($columnData_ss['Width'])) {
$columnWidth = $columnData_ss['Width'];
$spreadsheet->getActiveSheet()->getColumnDimension($columnID)->setWidth($columnWidth / 5.4);
}
++$columnID;
}
}
$rowID = 1;
if (isset($worksheet->Table->Row)) {
$additionalMergedCells = 0;
foreach ($worksheet->Table->Row as $rowData) {
$rowHasData = false;
$row_ss = self::getAttributes($rowData, $namespaces['ss']);
if (isset($row_ss['Index'])) {
$rowID = (int) $row_ss['Index'];
}
$columnID = 'A';
foreach ($rowData->Cell as $cell) {
$cell_ss = self::getAttributes($cell, $namespaces['ss']);
if (isset($cell_ss['Index'])) {
$columnID = Coordinate::stringFromColumnIndex((int) $cell_ss['Index']);
}
$cellRange = $columnID . $rowID;
if ($this->getReadFilter() !== null) {
if (!$this->getReadFilter()->readCell($columnID, $rowID, $worksheetName)) {
++$columnID;
continue;
}
}
if (isset($cell_ss['HRef'])) {
$spreadsheet->getActiveSheet()->getCell($cellRange)->getHyperlink()->setUrl((string) $cell_ss['HRef']);
}
if ((isset($cell_ss['MergeAcross'])) || (isset($cell_ss['MergeDown']))) {
$columnTo = $columnID;
if (isset($cell_ss['MergeAcross'])) {
$additionalMergedCells += (int) $cell_ss['MergeAcross'];
$columnTo = Coordinate::stringFromColumnIndex((int) (Coordinate::columnIndexFromString($columnID) + $cell_ss['MergeAcross']));
}
$rowTo = $rowID;
if (isset($cell_ss['MergeDown'])) {
$rowTo = $rowTo + $cell_ss['MergeDown'];
}
$cellRange .= ':' . $columnTo . $rowTo;
$spreadsheet->getActiveSheet()->mergeCells($cellRange);
}
$hasCalculatedValue = false;
$cellDataFormula = '';
if (isset($cell_ss['Formula'])) {
$cellDataFormula = $cell_ss['Formula'];
$hasCalculatedValue = true;
}
if (isset($cell->Data)) {
$cellData = $cell->Data;
$cellValue = (string) $cellData;
$type = DataType::TYPE_NULL;
$cellData_ss = self::getAttributes($cellData, $namespaces['ss']);
if (isset($cellData_ss['Type'])) {
$cellDataType = $cellData_ss['Type'];
switch ($cellDataType) {
/*
const TYPE_STRING = 's';
const TYPE_FORMULA = 'f';
const TYPE_NUMERIC = 'n';
const TYPE_BOOL = 'b';
const TYPE_NULL = 'null';
const TYPE_INLINE = 'inlineStr';
const TYPE_ERROR = 'e';
*/
case 'String':
$type = DataType::TYPE_STRING;
break;
case 'Number':
$type = DataType::TYPE_NUMERIC;
$cellValue = (float) $cellValue;
if (floor($cellValue) == $cellValue) {
$cellValue = (int) $cellValue;
}
break;
case 'Boolean':
$type = DataType::TYPE_BOOL;
$cellValue = ($cellValue != 0);
break;
case 'DateTime':
$type = DataType::TYPE_NUMERIC;
$dateTime = new DateTime($cellValue, new DateTimeZone('UTC'));
$cellValue = Date::PHPToExcel($dateTime);
break;
case 'Error':
$type = DataType::TYPE_ERROR;
$hasCalculatedValue = false;
break;
}
}
if ($hasCalculatedValue) {
$type = DataType::TYPE_FORMULA;
$columnNumber = Coordinate::columnIndexFromString($columnID);
$cellDataFormula = AddressHelper::convertFormulaToA1($cellDataFormula, $rowID, $columnNumber);
}
$spreadsheet->getActiveSheet()->getCell($columnID . $rowID)->setValueExplicit((($hasCalculatedValue) ? $cellDataFormula : $cellValue), $type);
if ($hasCalculatedValue) {
$spreadsheet->getActiveSheet()->getCell($columnID . $rowID)->setCalculatedValue($cellValue);
}
$rowHasData = true;
}
if (isset($cell->Comment)) {
$this->parseCellComment($cell->Comment, $namespaces, $spreadsheet, $columnID, $rowID);
}
if (isset($cell_ss['StyleID'])) {
$style = (string) $cell_ss['StyleID'];
if ((isset($this->styles[$style])) && (!empty($this->styles[$style]))) {
//if (!$spreadsheet->getActiveSheet()->cellExists($columnID . $rowID)) {
// $spreadsheet->getActiveSheet()->getCell($columnID . $rowID)->setValue(null);
//}
$spreadsheet->getActiveSheet()->getStyle($cellRange)
->applyFromArray($this->styles[$style]);
}
}
++$columnID;
while ($additionalMergedCells > 0) {
++$columnID;
--$additionalMergedCells;
}
}
if ($rowHasData) {
if (isset($row_ss['Height'])) {
$rowHeight = $row_ss['Height'];
$spreadsheet->getActiveSheet()->getRowDimension($rowID)->setRowHeight((float) $rowHeight);
}
}
++$rowID;
}
if (isset($namespaces['x'])) {
$xmlX = $worksheet->children($namespaces['x']);
if (isset($xmlX->WorksheetOptions)) {
(new PageSettings($xmlX, $namespaces))->loadPageSettings($spreadsheet);
}
}
}
++$worksheetID;
}
// Globally scoped defined names
$activeWorksheet = $spreadsheet->setActiveSheetIndex(0);
if (isset($xml->Names[0])) {
foreach ($xml->Names[0] as $definedName) {
$definedName_ss = self::getAttributes($definedName, $namespaces['ss']);
$name = (string) $definedName_ss['Name'];
$definedValue = (string) $definedName_ss['RefersTo'];
$convertedValue = AddressHelper::convertFormulaToA1($definedValue);
if ($convertedValue[0] === '=') {
$convertedValue = substr($convertedValue, 1);
}
$spreadsheet->addDefinedName(DefinedName::createInstance($name, $activeWorksheet, $convertedValue));
}
}
// Return
return $spreadsheet;
}
protected function parseCellComment(
SimpleXMLElement $comment,
array $namespaces,
Spreadsheet $spreadsheet,
string $columnID,
int $rowID
): void {
$commentAttributes = $comment->attributes($namespaces['ss']);
$author = 'unknown';
if (isset($commentAttributes->Author)) {
$author = (string) $commentAttributes->Author;
}
$node = $comment->Data->asXML();
$annotation = strip_tags((string) $node);
$spreadsheet->getActiveSheet()->getComment($columnID . $rowID)
->setAuthor($author)
->setText($this->parseRichText($annotation));
}
protected function parseRichText(string $annotation): RichText
{
$value = new RichText();
$value->createText($annotation);
return $value;
}
private static function getAttributes(?SimpleXMLElement $simple, string $node): SimpleXMLElement
{
return ($simple === null)
? new SimpleXMLElement('<xml></xml>')
: ($simple->attributes($node) ?? new SimpleXMLElement('<xml></xml>'));
}
}

157
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xml/Properties.php

@ -0,0 +1,157 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Reader\Xml;
use PhpOffice\PhpSpreadsheet\Document\Properties as DocumentProperties;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use SimpleXMLElement;
class Properties
{
/**
* @var Spreadsheet
*/
protected $spreadsheet;
public function __construct(Spreadsheet $spreadsheet)
{
$this->spreadsheet = $spreadsheet;
}
public function readProperties(SimpleXMLElement $xml, array $namespaces): void
{
$this->readStandardProperties($xml);
$this->readCustomProperties($xml, $namespaces);
}
protected function readStandardProperties(SimpleXMLElement $xml): void
{
if (isset($xml->DocumentProperties[0])) {
$docProps = $this->spreadsheet->getProperties();
foreach ($xml->DocumentProperties[0] as $propertyName => $propertyValue) {
$propertyValue = (string) $propertyValue;
$this->processStandardProperty($docProps, $propertyName, $propertyValue);
}
}
}
protected function readCustomProperties(SimpleXMLElement $xml, array $namespaces): void
{
if (isset($xml->CustomDocumentProperties)) {
$docProps = $this->spreadsheet->getProperties();
foreach ($xml->CustomDocumentProperties[0] as $propertyName => $propertyValue) {
$propertyAttributes = self::getAttributes($propertyValue, $namespaces['dt']);
$propertyName = preg_replace_callback('/_x([0-9a-f]{4})_/i', [$this, 'hex2str'], $propertyName);
$this->processCustomProperty($docProps, $propertyName, $propertyValue, $propertyAttributes);
}
}
}
protected function processStandardProperty(
DocumentProperties $docProps,
string $propertyName,
string $stringValue
): void {
switch ($propertyName) {
case 'Title':
$docProps->setTitle($stringValue);
break;
case 'Subject':
$docProps->setSubject($stringValue);
break;
case 'Author':
$docProps->setCreator($stringValue);
break;
case 'Created':
$docProps->setCreated($stringValue);
break;
case 'LastAuthor':
$docProps->setLastModifiedBy($stringValue);
break;
case 'LastSaved':
$docProps->setModified($stringValue);
break;
case 'Company':
$docProps->setCompany($stringValue);
break;
case 'Category':
$docProps->setCategory($stringValue);
break;
case 'Manager':
$docProps->setManager($stringValue);
break;
case 'Keywords':
$docProps->setKeywords($stringValue);
break;
case 'Description':
$docProps->setDescription($stringValue);
break;
}
}
protected function processCustomProperty(
DocumentProperties $docProps,
string $propertyName,
?SimpleXMLElement $propertyValue,
SimpleXMLElement $propertyAttributes
): void {
$propertyType = DocumentProperties::PROPERTY_TYPE_UNKNOWN;
switch ((string) $propertyAttributes) {
case 'string':
$propertyType = DocumentProperties::PROPERTY_TYPE_STRING;
$propertyValue = trim((string) $propertyValue);
break;
case 'boolean':
$propertyType = DocumentProperties::PROPERTY_TYPE_BOOLEAN;
$propertyValue = (bool) $propertyValue;
break;
case 'integer':
$propertyType = DocumentProperties::PROPERTY_TYPE_INTEGER;
$propertyValue = (int) $propertyValue;
break;
case 'float':
$propertyType = DocumentProperties::PROPERTY_TYPE_FLOAT;
$propertyValue = (float) $propertyValue;
break;
case 'dateTime.tz':
$propertyType = DocumentProperties::PROPERTY_TYPE_DATE;
$propertyValue = trim((string) $propertyValue);
break;
}
$docProps->setCustomProperty($propertyName, $propertyValue, $propertyType);
}
protected function hex2str(array $hex): string
{
return mb_chr((int) hexdec($hex[1]), 'UTF-8');
}
private static function getAttributes(?SimpleXMLElement $simple, string $node): SimpleXMLElement
{
return ($simple === null)
? new SimpleXMLElement('<xml></xml>')
: ($simple->attributes($node) ?? new SimpleXMLElement('<xml></xml>'));
}
}

83
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xml/Style.php

@ -0,0 +1,83 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Reader\Xml;
use SimpleXMLElement;
class Style
{
/**
* Formats.
*
* @var array
*/
protected $styles = [];
public function parseStyles(SimpleXMLElement $xml, array $namespaces): array
{
if (!isset($xml->Styles)) {
return [];
}
$alignmentStyleParser = new Style\Alignment();
$borderStyleParser = new Style\Border();
$fontStyleParser = new Style\Font();
$fillStyleParser = new Style\Fill();
$numberFormatStyleParser = new Style\NumberFormat();
foreach ($xml->Styles[0] as $style) {
$style_ss = self::getAttributes($style, $namespaces['ss']);
$styleID = (string) $style_ss['ID'];
$this->styles[$styleID] = $this->styles['Default'] ?? [];
$alignment = $border = $font = $fill = $numberFormat = [];
foreach ($style as $styleType => $styleDatax) {
$styleData = $styleDatax ?? new SimpleXMLElement('<xml></xml>');
$styleAttributes = $styleData->attributes($namespaces['ss']);
switch ($styleType) {
case 'Alignment':
if ($styleAttributes) {
$alignment = $alignmentStyleParser->parseStyle($styleAttributes);
}
break;
case 'Borders':
$border = $borderStyleParser->parseStyle($styleData, $namespaces);
break;
case 'Font':
if ($styleAttributes) {
$font = $fontStyleParser->parseStyle($styleAttributes);
}
break;
case 'Interior':
if ($styleAttributes) {
$fill = $fillStyleParser->parseStyle($styleAttributes);
}
break;
case 'NumberFormat':
if ($styleAttributes) {
$numberFormat = $numberFormatStyleParser->parseStyle($styleAttributes);
}
break;
}
}
$this->styles[$styleID] = array_merge($alignment, $border, $font, $fill, $numberFormat);
}
return $this->styles;
}
protected static function getAttributes(?SimpleXMLElement $simple, string $node): SimpleXMLElement
{
return ($simple === null)
? new SimpleXMLElement('<xml></xml>')
: ($simple->attributes($node) ?? new SimpleXMLElement('<xml></xml>'));
}
}

32
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Reader/Xml/Style/StyleBase.php

@ -0,0 +1,32 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Reader\Xml\Style;
use SimpleXMLElement;
abstract class StyleBase
{
protected static function identifyFixedStyleValue(array $styleList, string &$styleAttributeValue): bool
{
$returnValue = false;
$styleAttributeValue = strtolower($styleAttributeValue);
foreach ($styleList as $style) {
if ($styleAttributeValue == strtolower($style)) {
$styleAttributeValue = $style;
$returnValue = true;
break;
}
}
return $returnValue;
}
protected static function getAttributes(?SimpleXMLElement $simple, string $node): SimpleXMLElement
{
return ($simple === null)
? new SimpleXMLElement('<xml></xml>')
: ($simple->attributes($node) ?? new SimpleXMLElement('<xml></xml>'));
}
}

1048
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/ReferenceHelper.php

File diff suppressed because it is too large

168
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/RichText/RichText.php

@ -0,0 +1,168 @@
<?php
namespace PhpOffice\PhpSpreadsheet\RichText;
use PhpOffice\PhpSpreadsheet\Cell\Cell;
use PhpOffice\PhpSpreadsheet\Cell\DataType;
use PhpOffice\PhpSpreadsheet\IComparable;
class RichText implements IComparable
{
/**
* Rich text elements.
*
* @var ITextElement[]
*/
private $richTextElements;
/**
* Create a new RichText instance.
*/
public function __construct(?Cell $cell = null)
{
// Initialise variables
$this->richTextElements = [];
// Rich-Text string attached to cell?
if ($cell !== null) {
// Add cell text and style
if ($cell->getValue() != '') {
$objRun = new Run($cell->getValue());
$objRun->setFont(clone $cell->getWorksheet()->getStyle($cell->getCoordinate())->getFont());
$this->addText($objRun);
}
// Set parent value
$cell->setValueExplicit($this, DataType::TYPE_STRING);
}
}
/**
* Add text.
*
* @param ITextElement $text Rich text element
*
* @return $this
*/
public function addText(ITextElement $text)
{
$this->richTextElements[] = $text;
return $this;
}
/**
* Create text.
*
* @param string $text Text
*
* @return TextElement
*/
public function createText($text)
{
$objText = new TextElement($text);
$this->addText($objText);
return $objText;
}
/**
* Create text run.
*
* @param string $text Text
*
* @return Run
*/
public function createTextRun($text)
{
$objText = new Run($text);
$this->addText($objText);
return $objText;
}
/**
* Get plain text.
*
* @return string
*/
public function getPlainText()
{
// Return value
$returnValue = '';
// Loop through all ITextElements
foreach ($this->richTextElements as $text) {
$returnValue .= $text->getText();
}
return $returnValue;
}
/**
* Convert to string.
*
* @return string
*/
public function __toString()
{
return $this->getPlainText();
}
/**
* Get Rich Text elements.
*
* @return ITextElement[]
*/
public function getRichTextElements()
{
return $this->richTextElements;
}
/**
* Set Rich Text elements.
*
* @param ITextElement[] $textElements Array of elements
*
* @return $this
*/
public function setRichTextElements(array $textElements)
{
$this->richTextElements = $textElements;
return $this;
}
/**
* Get hash code.
*
* @return string Hash code
*/
public function getHashCode()
{
$hashElements = '';
foreach ($this->richTextElements as $element) {
$hashElements .= $element->getHashCode();
}
return md5(
$hashElements .
__CLASS__
);
}
/**
* Implement PHP __clone to create a deep clone, not just a shallow copy.
*/
public function __clone()
{
$vars = get_object_vars($this);
foreach ($vars as $key => $value) {
if (is_object($value)) {
$this->$key = clone $value;
} else {
$this->$key = $value;
}
}
}
}

65
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/RichText/Run.php

@ -0,0 +1,65 @@
<?php
namespace PhpOffice\PhpSpreadsheet\RichText;
use PhpOffice\PhpSpreadsheet\Style\Font;
class Run extends TextElement implements ITextElement
{
/**
* Font.
*
* @var Font
*/
private $font;
/**
* Create a new Run instance.
*
* @param string $text Text
*/
public function __construct($text = '')
{
parent::__construct($text);
// Initialise variables
$this->font = new Font();
}
/**
* Get font.
*
* @return null|\PhpOffice\PhpSpreadsheet\Style\Font
*/
public function getFont()
{
return $this->font;
}
/**
* Set font.
*
* @param Font $font Font
*
* @return $this
*/
public function setFont(?Font $font = null)
{
$this->font = $font;
return $this;
}
/**
* Get hash code.
*
* @return string Hash code
*/
public function getHashCode()
{
return md5(
$this->getText() .
$this->font->getHashCode() .
__CLASS__
);
}
}

86
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/RichText/TextElement.php

@ -0,0 +1,86 @@
<?php
namespace PhpOffice\PhpSpreadsheet\RichText;
class TextElement implements ITextElement
{
/**
* Text.
*
* @var string
*/
private $text;
/**
* Create a new TextElement instance.
*
* @param string $text Text
*/
public function __construct($text = '')
{
// Initialise variables
$this->text = $text;
}
/**
* Get text.
*
* @return string Text
*/
public function getText()
{
return $this->text;
}
/**
* Set text.
*
* @param string $text Text
*
* @return $this
*/
public function setText($text)
{
$this->text = $text;
return $this;
}
/**
* Get font.
*
* @return null|\PhpOffice\PhpSpreadsheet\Style\Font
*/
public function getFont()
{
return null;
}
/**
* Get hash code.
*
* @return string Hash code
*/
public function getHashCode()
{
return md5(
$this->text .
__CLASS__
);
}
/**
* Implement PHP __clone to create a deep clone, not just a shallow copy.
*/
public function __clone()
{
$vars = get_object_vars($this);
foreach ($vars as $key => $value) {
if (is_object($value)) {
$this->$key = clone $value;
} else {
$this->$key = $value;
}
}
}
}

214
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Settings.php

@ -0,0 +1,214 @@
<?php
namespace PhpOffice\PhpSpreadsheet;
use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
use PhpOffice\PhpSpreadsheet\Chart\Renderer\IRenderer;
use PhpOffice\PhpSpreadsheet\Collection\Memory;
use Psr\Http\Client\ClientInterface;
use Psr\Http\Message\RequestFactoryInterface;
use Psr\SimpleCache\CacheInterface;
class Settings
{
/**
* Class name of the chart renderer used for rendering charts
* eg: PhpOffice\PhpSpreadsheet\Chart\Renderer\JpGraph.
*
* @var string
*/
private static $chartRenderer;
/**
* Default options for libxml loader.
*
* @var int
*/
private static $libXmlLoaderOptions;
/**
* The cache implementation to be used for cell collection.
*
* @var CacheInterface
*/
private static $cache;
/**
* The HTTP client implementation to be used for network request.
*
* @var null|ClientInterface
*/
private static $httpClient;
/**
* @var null|RequestFactoryInterface
*/
private static $requestFactory;
/**
* Set the locale code to use for formula translations and any special formatting.
*
* @param string $locale The locale code to use (e.g. "fr" or "pt_br" or "en_uk")
*
* @return bool Success or failure
*/
public static function setLocale(string $locale)
{
return Calculation::getInstance()->setLocale($locale);
}
public static function getLocale(): string
{
return Calculation::getInstance()->getLocale();
}
/**
* Identify to PhpSpreadsheet the external library to use for rendering charts.
*
* @param string $rendererClassName Class name of the chart renderer
* eg: PhpOffice\PhpSpreadsheet\Chart\Renderer\JpGraph
*/
public static function setChartRenderer(string $rendererClassName): void
{
if (!is_a($rendererClassName, IRenderer::class, true)) {
throw new Exception('Chart renderer must implement ' . IRenderer::class);
}
self::$chartRenderer = $rendererClassName;
}
/**
* Return the Chart Rendering Library that PhpSpreadsheet is currently configured to use.
*
* @return null|string Class name of the chart renderer
* eg: PhpOffice\PhpSpreadsheet\Chart\Renderer\JpGraph
*/
public static function getChartRenderer(): ?string
{
return self::$chartRenderer;
}
public static function htmlEntityFlags(): int
{
return \ENT_COMPAT;
}
/**
* Set default options for libxml loader.
*
* @param int $options Default options for libxml loader
*/
public static function setLibXmlLoaderOptions($options): void
{
if ($options === null && defined('LIBXML_DTDLOAD')) {
$options = LIBXML_DTDLOAD | LIBXML_DTDATTR;
}
self::$libXmlLoaderOptions = $options;
}
/**
* Get default options for libxml loader.
* Defaults to LIBXML_DTDLOAD | LIBXML_DTDATTR when not set explicitly.
*
* @return int Default options for libxml loader
*/
public static function getLibXmlLoaderOptions(): int
{
if (self::$libXmlLoaderOptions === null && defined('LIBXML_DTDLOAD')) {
self::setLibXmlLoaderOptions(LIBXML_DTDLOAD | LIBXML_DTDATTR);
} elseif (self::$libXmlLoaderOptions === null) {
self::$libXmlLoaderOptions = 0;
}
return self::$libXmlLoaderOptions;
}
/**
* Deprecated, has no effect.
*
* @param bool $state
*
* @deprecated will be removed without replacement as it is no longer necessary on PHP 7.3.0+
*/
public static function setLibXmlDisableEntityLoader($state): void
{
// noop
}
/**
* Deprecated, has no effect.
*
* @return bool $state
*
* @deprecated will be removed without replacement as it is no longer necessary on PHP 7.3.0+
*/
public static function getLibXmlDisableEntityLoader(): bool
{
return true;
}
/**
* Sets the implementation of cache that should be used for cell collection.
*/
public static function setCache(CacheInterface $cache): void
{
self::$cache = $cache;
}
/**
* Gets the implementation of cache that is being used for cell collection.
*/
public static function getCache(): CacheInterface
{
if (!self::$cache) {
self::$cache = new Memory();
}
return self::$cache;
}
/**
* Set the HTTP client implementation to be used for network request.
*/
public static function setHttpClient(ClientInterface $httpClient, RequestFactoryInterface $requestFactory): void
{
self::$httpClient = $httpClient;
self::$requestFactory = $requestFactory;
}
/**
* Unset the HTTP client configuration.
*/
public static function unsetHttpClient(): void
{
self::$httpClient = null;
self::$requestFactory = null;
}
/**
* Get the HTTP client implementation to be used for network request.
*/
public static function getHttpClient(): ClientInterface
{
self::assertHttpClient();
return self::$httpClient;
}
/**
* Get the HTTP request factory.
*/
public static function getRequestFactory(): RequestFactoryInterface
{
self::assertHttpClient();
return self::$requestFactory;
}
private static function assertHttpClient(): void
{
if (!self::$httpClient || !self::$requestFactory) {
throw new Exception('HTTP client must be configured via Settings::setHttpClient() to be able to use WEBSERVICE function.');
}
}
}

75
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Escher/DgContainer/SpgrContainer.php

@ -0,0 +1,75 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Shared\Escher\DgContainer;
class SpgrContainer
{
/**
* Parent Shape Group Container.
*
* @var null|SpgrContainer
*/
private $parent;
/**
* Shape Container collection.
*
* @var array
*/
private $children = [];
/**
* Set parent Shape Group Container.
*/
public function setParent(?self $parent): void
{
$this->parent = $parent;
}
/**
* Get the parent Shape Group Container if any.
*/
public function getParent(): ?self
{
return $this->parent;
}
/**
* Add a child. This will be either spgrContainer or spContainer.
*
* @param mixed $child
*/
public function addChild($child): void
{
$this->children[] = $child;
$child->setParent($this);
}
/**
* Get collection of Shape Containers.
*/
public function getChildren()
{
return $this->children;
}
/**
* Recursively get all spContainers within this spgrContainer.
*
* @return SpgrContainer\SpContainer[]
*/
public function getAllSpContainers()
{
$allSpContainers = [];
foreach ($this->children as $child) {
if ($child instanceof self) {
$allSpContainers = array_merge($allSpContainers, $child->getAllSpContainers());
} else {
$allSpContainers[] = $child;
}
}
return $allSpContainers;
}
}

369
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Escher/DgContainer/SpgrContainer/SpContainer.php

@ -0,0 +1,369 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Shared\Escher\DgContainer\SpgrContainer;
use PhpOffice\PhpSpreadsheet\Shared\Escher\DgContainer\SpgrContainer;
class SpContainer
{
/**
* Parent Shape Group Container.
*
* @var SpgrContainer
*/
private $parent;
/**
* Is this a group shape?
*
* @var bool
*/
private $spgr = false;
/**
* Shape type.
*
* @var int
*/
private $spType;
/**
* Shape flag.
*
* @var int
*/
private $spFlag;
/**
* Shape index (usually group shape has index 0, and the rest: 1,2,3...).
*
* @var int
*/
private $spId;
/**
* Array of options.
*
* @var array
*/
private $OPT;
/**
* Cell coordinates of upper-left corner of shape, e.g. 'A1'.
*
* @var string
*/
private $startCoordinates;
/**
* Horizontal offset of upper-left corner of shape measured in 1/1024 of column width.
*
* @var int
*/
private $startOffsetX;
/**
* Vertical offset of upper-left corner of shape measured in 1/256 of row height.
*
* @var int
*/
private $startOffsetY;
/**
* Cell coordinates of bottom-right corner of shape, e.g. 'B2'.
*
* @var string
*/
private $endCoordinates;
/**
* Horizontal offset of bottom-right corner of shape measured in 1/1024 of column width.
*
* @var int
*/
private $endOffsetX;
/**
* Vertical offset of bottom-right corner of shape measured in 1/256 of row height.
*
* @var int
*/
private $endOffsetY;
/**
* Set parent Shape Group Container.
*
* @param SpgrContainer $parent
*/
public function setParent($parent): void
{
$this->parent = $parent;
}
/**
* Get the parent Shape Group Container.
*
* @return SpgrContainer
*/
public function getParent()
{
return $this->parent;
}
/**
* Set whether this is a group shape.
*
* @param bool $value
*/
public function setSpgr($value): void
{
$this->spgr = $value;
}
/**
* Get whether this is a group shape.
*
* @return bool
*/
public function getSpgr()
{
return $this->spgr;
}
/**
* Set the shape type.
*
* @param int $value
*/
public function setSpType($value): void
{
$this->spType = $value;
}
/**
* Get the shape type.
*
* @return int
*/
public function getSpType()
{
return $this->spType;
}
/**
* Set the shape flag.
*
* @param int $value
*/
public function setSpFlag($value): void
{
$this->spFlag = $value;
}
/**
* Get the shape flag.
*
* @return int
*/
public function getSpFlag()
{
return $this->spFlag;
}
/**
* Set the shape index.
*
* @param int $value
*/
public function setSpId($value): void
{
$this->spId = $value;
}
/**
* Get the shape index.
*
* @return int
*/
public function getSpId()
{
return $this->spId;
}
/**
* Set an option for the Shape Group Container.
*
* @param int $property The number specifies the option
* @param mixed $value
*/
public function setOPT($property, $value): void
{
$this->OPT[$property] = $value;
}
/**
* Get an option for the Shape Group Container.
*
* @param int $property The number specifies the option
*
* @return mixed
*/
public function getOPT($property)
{
if (isset($this->OPT[$property])) {
return $this->OPT[$property];
}
return null;
}
/**
* Get the collection of options.
*
* @return array
*/
public function getOPTCollection()
{
return $this->OPT;
}
/**
* Set cell coordinates of upper-left corner of shape.
*
* @param string $value eg: 'A1'
*/
public function setStartCoordinates($value): void
{
$this->startCoordinates = $value;
}
/**
* Get cell coordinates of upper-left corner of shape.
*
* @return string
*/
public function getStartCoordinates()
{
return $this->startCoordinates;
}
/**
* Set offset in x-direction of upper-left corner of shape measured in 1/1024 of column width.
*
* @param int $startOffsetX
*/
public function setStartOffsetX($startOffsetX): void
{
$this->startOffsetX = $startOffsetX;
}
/**
* Get offset in x-direction of upper-left corner of shape measured in 1/1024 of column width.
*
* @return int
*/
public function getStartOffsetX()
{
return $this->startOffsetX;
}
/**
* Set offset in y-direction of upper-left corner of shape measured in 1/256 of row height.
*
* @param int $startOffsetY
*/
public function setStartOffsetY($startOffsetY): void
{
$this->startOffsetY = $startOffsetY;
}
/**
* Get offset in y-direction of upper-left corner of shape measured in 1/256 of row height.
*
* @return int
*/
public function getStartOffsetY()
{
return $this->startOffsetY;
}
/**
* Set cell coordinates of bottom-right corner of shape.
*
* @param string $value eg: 'A1'
*/
public function setEndCoordinates($value): void
{
$this->endCoordinates = $value;
}
/**
* Get cell coordinates of bottom-right corner of shape.
*
* @return string
*/
public function getEndCoordinates()
{
return $this->endCoordinates;
}
/**
* Set offset in x-direction of bottom-right corner of shape measured in 1/1024 of column width.
*
* @param int $endOffsetX
*/
public function setEndOffsetX($endOffsetX): void
{
$this->endOffsetX = $endOffsetX;
}
/**
* Get offset in x-direction of bottom-right corner of shape measured in 1/1024 of column width.
*
* @return int
*/
public function getEndOffsetX()
{
return $this->endOffsetX;
}
/**
* Set offset in y-direction of bottom-right corner of shape measured in 1/256 of row height.
*
* @param int $endOffsetY
*/
public function setEndOffsetY($endOffsetY): void
{
$this->endOffsetY = $endOffsetY;
}
/**
* Get offset in y-direction of bottom-right corner of shape measured in 1/256 of row height.
*
* @return int
*/
public function getEndOffsetY()
{
return $this->endOffsetY;
}
/**
* Get the nesting level of this spContainer. This is the number of spgrContainers between this spContainer and
* the dgContainer. A value of 1 = immediately within first spgrContainer
* Higher nesting level occurs if and only if spContainer is part of a shape group.
*
* @return int Nesting level
*/
public function getNestingLevel()
{
$nestingLevel = 0;
$parent = $this->getParent();
while ($parent instanceof SpgrContainer) {
++$nestingLevel;
$parent = $parent->getParent();
}
return $nestingLevel;
}
}

245
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/JAMA/QRDecomposition.php

@ -0,0 +1,245 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Shared\JAMA;
use PhpOffice\PhpSpreadsheet\Calculation\Exception as CalculationException;
/**
* For an m-by-n matrix A with m >= n, the QR decomposition is an m-by-n
* orthogonal matrix Q and an n-by-n upper triangular matrix R so that
* A = Q*R.
*
* The QR decompostion always exists, even if the matrix does not have
* full rank, so the constructor will never fail. The primary use of the
* QR decomposition is in the least squares solution of nonsquare systems
* of simultaneous linear equations. This will fail if isFullRank()
* returns false.
*
* @author Paul Meagher
*
* @version 1.1
*/
class QRDecomposition
{
const MATRIX_RANK_EXCEPTION = 'Can only perform operation on full-rank matrix.';
/**
* Array for internal storage of decomposition.
*
* @var array
*/
private $QR = [];
/**
* Row dimension.
*
* @var int
*/
private $m;
/**
* Column dimension.
*
* @var int
*/
private $n;
/**
* Array for internal storage of diagonal of R.
*
* @var array
*/
private $Rdiag = [];
/**
* QR Decomposition computed by Householder reflections.
*
* @param Matrix $A Rectangular matrix
*/
public function __construct(Matrix $A)
{
// Initialize.
$this->QR = $A->getArray();
$this->m = $A->getRowDimension();
$this->n = $A->getColumnDimension();
// Main loop.
for ($k = 0; $k < $this->n; ++$k) {
// Compute 2-norm of k-th column without under/overflow.
$nrm = 0.0;
for ($i = $k; $i < $this->m; ++$i) {
$nrm = hypo($nrm, $this->QR[$i][$k]);
}
if ($nrm != 0.0) {
// Form k-th Householder vector.
if ($this->QR[$k][$k] < 0) {
$nrm = -$nrm;
}
for ($i = $k; $i < $this->m; ++$i) {
$this->QR[$i][$k] /= $nrm;
}
$this->QR[$k][$k] += 1.0;
// Apply transformation to remaining columns.
for ($j = $k + 1; $j < $this->n; ++$j) {
$s = 0.0;
for ($i = $k; $i < $this->m; ++$i) {
$s += $this->QR[$i][$k] * $this->QR[$i][$j];
}
$s = -$s / $this->QR[$k][$k];
for ($i = $k; $i < $this->m; ++$i) {
$this->QR[$i][$j] += $s * $this->QR[$i][$k];
}
}
}
$this->Rdiag[$k] = -$nrm;
}
}
// function __construct()
/**
* Is the matrix full rank?
*
* @return bool true if R, and hence A, has full rank, else false
*/
public function isFullRank()
{
for ($j = 0; $j < $this->n; ++$j) {
if ($this->Rdiag[$j] == 0) {
return false;
}
}
return true;
}
// function isFullRank()
/**
* Return the Householder vectors.
*
* @return Matrix Lower trapezoidal matrix whose columns define the reflections
*/
public function getH()
{
$H = [];
for ($i = 0; $i < $this->m; ++$i) {
for ($j = 0; $j < $this->n; ++$j) {
if ($i >= $j) {
$H[$i][$j] = $this->QR[$i][$j];
} else {
$H[$i][$j] = 0.0;
}
}
}
return new Matrix($H);
}
// function getH()
/**
* Return the upper triangular factor.
*
* @return Matrix upper triangular factor
*/
public function getR()
{
$R = [];
for ($i = 0; $i < $this->n; ++$i) {
for ($j = 0; $j < $this->n; ++$j) {
if ($i < $j) {
$R[$i][$j] = $this->QR[$i][$j];
} elseif ($i == $j) {
$R[$i][$j] = $this->Rdiag[$i];
} else {
$R[$i][$j] = 0.0;
}
}
}
return new Matrix($R);
}
// function getR()
/**
* Generate and return the (economy-sized) orthogonal factor.
*
* @return Matrix orthogonal factor
*/
public function getQ()
{
$Q = [];
for ($k = $this->n - 1; $k >= 0; --$k) {
for ($i = 0; $i < $this->m; ++$i) {
$Q[$i][$k] = 0.0;
}
$Q[$k][$k] = 1.0;
for ($j = $k; $j < $this->n; ++$j) {
if ($this->QR[$k][$k] != 0) {
$s = 0.0;
for ($i = $k; $i < $this->m; ++$i) {
$s += $this->QR[$i][$k] * $Q[$i][$j];
}
$s = -$s / $this->QR[$k][$k];
for ($i = $k; $i < $this->m; ++$i) {
$Q[$i][$j] += $s * $this->QR[$i][$k];
}
}
}
}
return new Matrix($Q);
}
// function getQ()
/**
* Least squares solution of A*X = B.
*
* @param Matrix $B a Matrix with as many rows as A and any number of columns
*
* @return Matrix matrix that minimizes the two norm of Q*R*X-B
*/
public function solve(Matrix $B)
{
if ($B->getRowDimension() == $this->m) {
if ($this->isFullRank()) {
// Copy right hand side
$nx = $B->getColumnDimension();
$X = $B->getArray();
// Compute Y = transpose(Q)*B
for ($k = 0; $k < $this->n; ++$k) {
for ($j = 0; $j < $nx; ++$j) {
$s = 0.0;
for ($i = $k; $i < $this->m; ++$i) {
$s += $this->QR[$i][$k] * $X[$i][$j];
}
$s = -$s / $this->QR[$k][$k];
for ($i = $k; $i < $this->m; ++$i) {
$X[$i][$j] += $s * $this->QR[$i][$k];
}
}
}
// Solve R*X = Y;
for ($k = $this->n - 1; $k >= 0; --$k) {
for ($j = 0; $j < $nx; ++$j) {
$X[$k][$j] /= $this->Rdiag[$k];
}
for ($i = 0; $i < $k; ++$i) {
for ($j = 0; $j < $nx; ++$j) {
$X[$i][$j] -= $X[$k][$j] * $this->QR[$i][$k];
}
}
}
$X = new Matrix($X);
return $X->getMatrix(0, $this->n - 1, 0, $nx);
}
throw new CalculationException(self::MATRIX_RANK_EXCEPTION);
}
throw new CalculationException(Matrix::MATRIX_DIMENSION_EXCEPTION);
}
}

529
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/JAMA/SingularValueDecomposition.php

@ -0,0 +1,529 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Shared\JAMA;
/**
* For an m-by-n matrix A with m >= n, the singular value decomposition is
* an m-by-n orthogonal matrix U, an n-by-n diagonal matrix S, and
* an n-by-n orthogonal matrix V so that A = U*S*V'.
*
* The singular values, sigma[$k] = S[$k][$k], are ordered so that
* sigma[0] >= sigma[1] >= ... >= sigma[n-1].
*
* The singular value decompostion always exists, so the constructor will
* never fail. The matrix condition number and the effective numerical
* rank can be computed from this decomposition.
*
* @author Paul Meagher
*
* @version 1.1
*/
class SingularValueDecomposition
{
/**
* Internal storage of U.
*
* @var array
*/
private $U = [];
/**
* Internal storage of V.
*
* @var array
*/
private $V = [];
/**
* Internal storage of singular values.
*
* @var array
*/
private $s = [];
/**
* Row dimension.
*
* @var int
*/
private $m;
/**
* Column dimension.
*
* @var int
*/
private $n;
/**
* Construct the singular value decomposition.
*
* Derived from LINPACK code.
*
* @param mixed $Arg Rectangular matrix
*/
public function __construct($Arg)
{
// Initialize.
$A = $Arg->getArray();
$this->m = $Arg->getRowDimension();
$this->n = $Arg->getColumnDimension();
$nu = min($this->m, $this->n);
$e = [];
$work = [];
$wantu = true;
$wantv = true;
$nct = min($this->m - 1, $this->n);
$nrt = max(0, min($this->n - 2, $this->m));
// Reduce A to bidiagonal form, storing the diagonal elements
// in s and the super-diagonal elements in e.
$kMax = max($nct, $nrt);
for ($k = 0; $k < $kMax; ++$k) {
if ($k < $nct) {
// Compute the transformation for the k-th column and
// place the k-th diagonal in s[$k].
// Compute 2-norm of k-th column without under/overflow.
$this->s[$k] = 0;
for ($i = $k; $i < $this->m; ++$i) {
$this->s[$k] = hypo($this->s[$k], $A[$i][$k]);
}
if ($this->s[$k] != 0.0) {
if ($A[$k][$k] < 0.0) {
$this->s[$k] = -$this->s[$k];
}
for ($i = $k; $i < $this->m; ++$i) {
$A[$i][$k] /= $this->s[$k];
}
$A[$k][$k] += 1.0;
}
$this->s[$k] = -$this->s[$k];
}
for ($j = $k + 1; $j < $this->n; ++$j) {
if (($k < $nct) & ($this->s[$k] != 0.0)) {
// Apply the transformation.
$t = 0;
for ($i = $k; $i < $this->m; ++$i) {
$t += $A[$i][$k] * $A[$i][$j];
}
$t = -$t / $A[$k][$k];
for ($i = $k; $i < $this->m; ++$i) {
$A[$i][$j] += $t * $A[$i][$k];
}
// Place the k-th row of A into e for the
// subsequent calculation of the row transformation.
$e[$j] = $A[$k][$j];
}
}
if ($wantu && ($k < $nct)) {
// Place the transformation in U for subsequent back
// multiplication.
for ($i = $k; $i < $this->m; ++$i) {
$this->U[$i][$k] = $A[$i][$k];
}
}
if ($k < $nrt) {
// Compute the k-th row transformation and place the
// k-th super-diagonal in e[$k].
// Compute 2-norm without under/overflow.
$e[$k] = 0;
for ($i = $k + 1; $i < $this->n; ++$i) {
$e[$k] = hypo($e[$k], $e[$i]);
}
if ($e[$k] != 0.0) {
if ($e[$k + 1] < 0.0) {
$e[$k] = -$e[$k];
}
for ($i = $k + 1; $i < $this->n; ++$i) {
$e[$i] /= $e[$k];
}
$e[$k + 1] += 1.0;
}
$e[$k] = -$e[$k];
if (($k + 1 < $this->m) && ($e[$k] != 0.0)) {
// Apply the transformation.
for ($i = $k + 1; $i < $this->m; ++$i) {
$work[$i] = 0.0;
}
for ($j = $k + 1; $j < $this->n; ++$j) {
for ($i = $k + 1; $i < $this->m; ++$i) {
$work[$i] += $e[$j] * $A[$i][$j];
}
}
for ($j = $k + 1; $j < $this->n; ++$j) {
$t = -$e[$j] / $e[$k + 1];
for ($i = $k + 1; $i < $this->m; ++$i) {
$A[$i][$j] += $t * $work[$i];
}
}
}
if ($wantv) {
// Place the transformation in V for subsequent
// back multiplication.
for ($i = $k + 1; $i < $this->n; ++$i) {
$this->V[$i][$k] = $e[$i];
}
}
}
}
// Set up the final bidiagonal matrix or order p.
$p = min($this->n, $this->m + 1);
if ($nct < $this->n) {
$this->s[$nct] = $A[$nct][$nct];
}
if ($this->m < $p) {
$this->s[$p - 1] = 0.0;
}
if ($nrt + 1 < $p) {
$e[$nrt] = $A[$nrt][$p - 1];
}
$e[$p - 1] = 0.0;
// If required, generate U.
if ($wantu) {
for ($j = $nct; $j < $nu; ++$j) {
for ($i = 0; $i < $this->m; ++$i) {
$this->U[$i][$j] = 0.0;
}
$this->U[$j][$j] = 1.0;
}
for ($k = $nct - 1; $k >= 0; --$k) {
if ($this->s[$k] != 0.0) {
for ($j = $k + 1; $j < $nu; ++$j) {
$t = 0;
for ($i = $k; $i < $this->m; ++$i) {
$t += $this->U[$i][$k] * $this->U[$i][$j];
}
$t = -$t / $this->U[$k][$k];
for ($i = $k; $i < $this->m; ++$i) {
$this->U[$i][$j] += $t * $this->U[$i][$k];
}
}
for ($i = $k; $i < $this->m; ++$i) {
$this->U[$i][$k] = -$this->U[$i][$k];
}
$this->U[$k][$k] = 1.0 + $this->U[$k][$k];
for ($i = 0; $i < $k - 1; ++$i) {
$this->U[$i][$k] = 0.0;
}
} else {
for ($i = 0; $i < $this->m; ++$i) {
$this->U[$i][$k] = 0.0;
}
$this->U[$k][$k] = 1.0;
}
}
}
// If required, generate V.
if ($wantv) {
for ($k = $this->n - 1; $k >= 0; --$k) {
if (($k < $nrt) && ($e[$k] != 0.0)) {
for ($j = $k + 1; $j < $nu; ++$j) {
$t = 0;
for ($i = $k + 1; $i < $this->n; ++$i) {
$t += $this->V[$i][$k] * $this->V[$i][$j];
}
$t = -$t / $this->V[$k + 1][$k];
for ($i = $k + 1; $i < $this->n; ++$i) {
$this->V[$i][$j] += $t * $this->V[$i][$k];
}
}
}
for ($i = 0; $i < $this->n; ++$i) {
$this->V[$i][$k] = 0.0;
}
$this->V[$k][$k] = 1.0;
}
}
// Main iteration loop for the singular values.
$pp = $p - 1;
$iter = 0;
$eps = 2.0 ** (-52.0);
while ($p > 0) {
// Here is where a test for too many iterations would go.
// This section of the program inspects for negligible
// elements in the s and e arrays. On completion the
// variables kase and k are set as follows:
// kase = 1 if s(p) and e[k-1] are negligible and k<p
// kase = 2 if s(k) is negligible and k<p
// kase = 3 if e[k-1] is negligible, k<p, and
// s(k), ..., s(p) are not negligible (qr step).
// kase = 4 if e(p-1) is negligible (convergence).
for ($k = $p - 2; $k >= -1; --$k) {
if ($k == -1) {
break;
}
if (abs($e[$k]) <= $eps * (abs($this->s[$k]) + abs($this->s[$k + 1]))) {
$e[$k] = 0.0;
break;
}
}
if ($k == $p - 2) {
$kase = 4;
} else {
for ($ks = $p - 1; $ks >= $k; --$ks) {
if ($ks == $k) {
break;
}
$t = ($ks != $p ? abs($e[$ks]) : 0.) + ($ks != $k + 1 ? abs($e[$ks - 1]) : 0.);
if (abs($this->s[$ks]) <= $eps * $t) {
$this->s[$ks] = 0.0;
break;
}
}
if ($ks == $k) {
$kase = 3;
} elseif ($ks == $p - 1) {
$kase = 1;
} else {
$kase = 2;
$k = $ks;
}
}
++$k;
// Perform the task indicated by kase.
switch ($kase) {
// Deflate negligible s(p).
case 1:
$f = $e[$p - 2];
$e[$p - 2] = 0.0;
for ($j = $p - 2; $j >= $k; --$j) {
$t = hypo($this->s[$j], $f);
$cs = $this->s[$j] / $t;
$sn = $f / $t;
$this->s[$j] = $t;
if ($j != $k) {
$f = -$sn * $e[$j - 1];
$e[$j - 1] = $cs * $e[$j - 1];
}
if ($wantv) {
for ($i = 0; $i < $this->n; ++$i) {
$t = $cs * $this->V[$i][$j] + $sn * $this->V[$i][$p - 1];
$this->V[$i][$p - 1] = -$sn * $this->V[$i][$j] + $cs * $this->V[$i][$p - 1];
$this->V[$i][$j] = $t;
}
}
}
break;
// Split at negligible s(k).
case 2:
$f = $e[$k - 1];
$e[$k - 1] = 0.0;
for ($j = $k; $j < $p; ++$j) {
$t = hypo($this->s[$j], $f);
$cs = $this->s[$j] / $t;
$sn = $f / $t;
$this->s[$j] = $t;
$f = -$sn * $e[$j];
$e[$j] = $cs * $e[$j];
if ($wantu) {
for ($i = 0; $i < $this->m; ++$i) {
$t = $cs * $this->U[$i][$j] + $sn * $this->U[$i][$k - 1];
$this->U[$i][$k - 1] = -$sn * $this->U[$i][$j] + $cs * $this->U[$i][$k - 1];
$this->U[$i][$j] = $t;
}
}
}
break;
// Perform one qr step.
case 3:
// Calculate the shift.
$scale = max(max(max(max(abs($this->s[$p - 1]), abs($this->s[$p - 2])), abs($e[$p - 2])), abs($this->s[$k])), abs($e[$k]));
$sp = $this->s[$p - 1] / $scale;
$spm1 = $this->s[$p - 2] / $scale;
$epm1 = $e[$p - 2] / $scale;
$sk = $this->s[$k] / $scale;
$ek = $e[$k] / $scale;
$b = (($spm1 + $sp) * ($spm1 - $sp) + $epm1 * $epm1) / 2.0;
$c = ($sp * $epm1) * ($sp * $epm1);
$shift = 0.0;
if (($b != 0.0) || ($c != 0.0)) {
$shift = sqrt($b * $b + $c);
if ($b < 0.0) {
$shift = -$shift;
}
$shift = $c / ($b + $shift);
}
$f = ($sk + $sp) * ($sk - $sp) + $shift;
$g = $sk * $ek;
// Chase zeros.
for ($j = $k; $j < $p - 1; ++$j) {
$t = hypo($f, $g);
$cs = $f / $t;
$sn = $g / $t;
if ($j != $k) {
$e[$j - 1] = $t;
}
$f = $cs * $this->s[$j] + $sn * $e[$j];
$e[$j] = $cs * $e[$j] - $sn * $this->s[$j];
$g = $sn * $this->s[$j + 1];
$this->s[$j + 1] = $cs * $this->s[$j + 1];
if ($wantv) {
for ($i = 0; $i < $this->n; ++$i) {
$t = $cs * $this->V[$i][$j] + $sn * $this->V[$i][$j + 1];
$this->V[$i][$j + 1] = -$sn * $this->V[$i][$j] + $cs * $this->V[$i][$j + 1];
$this->V[$i][$j] = $t;
}
}
$t = hypo($f, $g);
$cs = $f / $t;
$sn = $g / $t;
$this->s[$j] = $t;
$f = $cs * $e[$j] + $sn * $this->s[$j + 1];
$this->s[$j + 1] = -$sn * $e[$j] + $cs * $this->s[$j + 1];
$g = $sn * $e[$j + 1];
$e[$j + 1] = $cs * $e[$j + 1];
if ($wantu && ($j < $this->m - 1)) {
for ($i = 0; $i < $this->m; ++$i) {
$t = $cs * $this->U[$i][$j] + $sn * $this->U[$i][$j + 1];
$this->U[$i][$j + 1] = -$sn * $this->U[$i][$j] + $cs * $this->U[$i][$j + 1];
$this->U[$i][$j] = $t;
}
}
}
$e[$p - 2] = $f;
$iter = $iter + 1;
break;
// Convergence.
case 4:
// Make the singular values positive.
if ($this->s[$k] <= 0.0) {
$this->s[$k] = ($this->s[$k] < 0.0 ? -$this->s[$k] : 0.0);
if ($wantv) {
for ($i = 0; $i <= $pp; ++$i) {
$this->V[$i][$k] = -$this->V[$i][$k];
}
}
}
// Order the singular values.
while ($k < $pp) {
if ($this->s[$k] >= $this->s[$k + 1]) {
break;
}
$t = $this->s[$k];
$this->s[$k] = $this->s[$k + 1];
$this->s[$k + 1] = $t;
if ($wantv && ($k < $this->n - 1)) {
for ($i = 0; $i < $this->n; ++$i) {
$t = $this->V[$i][$k + 1];
$this->V[$i][$k + 1] = $this->V[$i][$k];
$this->V[$i][$k] = $t;
}
}
if ($wantu && ($k < $this->m - 1)) {
for ($i = 0; $i < $this->m; ++$i) {
$t = $this->U[$i][$k + 1];
$this->U[$i][$k + 1] = $this->U[$i][$k];
$this->U[$i][$k] = $t;
}
}
++$k;
}
$iter = 0;
--$p;
break;
} // end switch
} // end while
}
/**
* Return the left singular vectors.
*
* @return Matrix U
*/
public function getU()
{
return new Matrix($this->U, $this->m, min($this->m + 1, $this->n));
}
/**
* Return the right singular vectors.
*
* @return Matrix V
*/
public function getV()
{
return new Matrix($this->V);
}
/**
* Return the one-dimensional array of singular values.
*
* @return array diagonal of S
*/
public function getSingularValues()
{
return $this->s;
}
/**
* Return the diagonal matrix of singular values.
*
* @return Matrix S
*/
public function getS()
{
$S = [];
for ($i = 0; $i < $this->n; ++$i) {
for ($j = 0; $j < $this->n; ++$j) {
$S[$i][$j] = 0.0;
}
$S[$i][$i] = $this->s[$i];
}
return new Matrix($S);
}
/**
* Two norm.
*
* @return float max(S)
*/
public function norm2()
{
return $this->s[0];
}
/**
* Two norm condition number.
*
* @return float max(S)/min(S)
*/
public function cond()
{
return $this->s[0] / $this->s[min($this->m, $this->n) - 1];
}
/**
* Effective numerical matrix rank.
*
* @return int Number of nonnegligible singular values
*/
public function rank()
{
$eps = 2.0 ** (-52.0);
$tol = max($this->m, $this->n) * $this->s[0] * $eps;
$r = 0;
$iMax = count($this->s);
for ($i = 0; $i < $iMax; ++$i) {
if ($this->s[$i] > $tol) {
++$r;
}
}
return $r;
}
}

237
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/OLE/PPS.php

@ -0,0 +1,237 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Shared\OLE;
// vim: set expandtab tabstop=4 shiftwidth=4:
// +----------------------------------------------------------------------+
// | PHP Version 4 |
// +----------------------------------------------------------------------+
// | Copyright (c) 1997-2002 The PHP Group |
// +----------------------------------------------------------------------+
// | This source file is subject to version 2.02 of the PHP license, |
// | that is bundled with this package in the file LICENSE, and is |
// | available at through the world-wide-web at |
// | http://www.php.net/license/2_02.txt. |
// | If you did not receive a copy of the PHP license and are unable to |
// | obtain it through the world-wide-web, please send a note to |
// | license@php.net so we can mail you a copy immediately. |
// +----------------------------------------------------------------------+
// | Author: Xavier Noguer <xnoguer@php.net> |
// | Based on OLE::Storage_Lite by Kawai, Takanori |
// +----------------------------------------------------------------------+
//
use PhpOffice\PhpSpreadsheet\Shared\OLE;
/**
* Class for creating PPS's for OLE containers.
*
* @author Xavier Noguer <xnoguer@php.net>
*/
class PPS
{
/**
* The PPS index.
*
* @var int
*/
public $No;
/**
* The PPS name (in Unicode).
*
* @var string
*/
public $Name;
/**
* The PPS type. Dir, Root or File.
*
* @var int
*/
public $Type;
/**
* The index of the previous PPS.
*
* @var int
*/
public $PrevPps;
/**
* The index of the next PPS.
*
* @var int
*/
public $NextPps;
/**
* The index of it's first child if this is a Dir or Root PPS.
*
* @var int
*/
public $DirPps;
/**
* A timestamp.
*
* @var float|int
*/
public $Time1st;
/**
* A timestamp.
*
* @var float|int
*/
public $Time2nd;
/**
* Starting block (small or big) for this PPS's data inside the container.
*
* @var int
*/
public $startBlock;
/**
* The size of the PPS's data (in bytes).
*
* @var int
*/
public $Size;
/**
* The PPS's data (only used if it's not using a temporary file).
*
* @var string
*/
public $_data;
/**
* Array of child PPS's (only used by Root and Dir PPS's).
*
* @var array
*/
public $children = [];
/**
* Pointer to OLE container.
*
* @var OLE
*/
public $ole;
/**
* The constructor.
*
* @param int $No The PPS index
* @param string $name The PPS name
* @param int $type The PPS type. Dir, Root or File
* @param int $prev The index of the previous PPS
* @param int $next The index of the next PPS
* @param int $dir The index of it's first child if this is a Dir or Root PPS
* @param null|float|int $time_1st A timestamp
* @param null|float|int $time_2nd A timestamp
* @param string $data The (usually binary) source data of the PPS
* @param array $children Array containing children PPS for this PPS
*/
public function __construct($No, $name, $type, $prev, $next, $dir, $time_1st, $time_2nd, $data, $children)
{
$this->No = $No;
$this->Name = $name;
$this->Type = $type;
$this->PrevPps = $prev;
$this->NextPps = $next;
$this->DirPps = $dir;
$this->Time1st = $time_1st ?? 0;
$this->Time2nd = $time_2nd ?? 0;
$this->_data = $data;
$this->children = $children;
if ($data != '') {
$this->Size = strlen($data);
} else {
$this->Size = 0;
}
}
/**
* Returns the amount of data saved for this PPS.
*
* @return int The amount of data (in bytes)
*/
public function getDataLen()
{
if (!isset($this->_data)) {
return 0;
}
return strlen($this->_data);
}
/**
* Returns a string with the PPS's WK (What is a WK?).
*
* @return string The binary string
*/
public function getPpsWk()
{
$ret = str_pad($this->Name, 64, "\x00");
$ret .= pack('v', strlen($this->Name) + 2) // 66
. pack('c', $this->Type) // 67
. pack('c', 0x00) //UK // 68
. pack('V', $this->PrevPps) //Prev // 72
. pack('V', $this->NextPps) //Next // 76
. pack('V', $this->DirPps) //Dir // 80
. "\x00\x09\x02\x00" // 84
. "\x00\x00\x00\x00" // 88
. "\xc0\x00\x00\x00" // 92
. "\x00\x00\x00\x46" // 96 // Seems to be ok only for Root
. "\x00\x00\x00\x00" // 100
. OLE::localDateToOLE($this->Time1st) // 108
. OLE::localDateToOLE($this->Time2nd) // 116
. pack('V', $this->startBlock ?? 0) // 120
. pack('V', $this->Size) // 124
. pack('V', 0); // 128
return $ret;
}
/**
* Updates index and pointers to previous, next and children PPS's for this
* PPS. I don't think it'll work with Dir PPS's.
*
* @param array $raList Reference to the array of PPS's for the whole OLE
* container
* @param mixed $to_save
* @param mixed $depth
*
* @return int The index for this PPS
*/
public static function savePpsSetPnt(&$raList, $to_save, $depth = 0)
{
if (!is_array($to_save) || (empty($to_save))) {
return 0xFFFFFFFF;
} 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]->DirPps = self::savePpsSetPnt($raList, @$raList[$cnt]->children, $depth++);
} else {
$iPos = floor(count($to_save) / 2);
$aPrev = array_slice($to_save, 0, $iPos);
$aNext = array_slice($to_save, $iPos + 1);
$cnt = count($raList);
// If the first entry, it's the root... Don't clone it!
$raList[$cnt] = ($depth == 0) ? $to_save[$iPos] : clone $to_save[$iPos];
$raList[$cnt]->No = $cnt;
$raList[$cnt]->PrevPps = self::savePpsSetPnt($raList, $aPrev, $depth++);
$raList[$cnt]->NextPps = self::savePpsSetPnt($raList, $aNext, $depth++);
$raList[$cnt]->DirPps = self::savePpsSetPnt($raList, @$raList[$cnt]->children, $depth++);
}
return $cnt;
}
}

426
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/OLE/PPS/Root.php

@ -0,0 +1,426 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Shared\OLE\PPS;
// vim: set expandtab tabstop=4 shiftwidth=4:
// +----------------------------------------------------------------------+
// | PHP Version 4 |
// +----------------------------------------------------------------------+
// | Copyright (c) 1997-2002 The PHP Group |
// +----------------------------------------------------------------------+
// | This source file is subject to version 2.02 of the PHP license, |
// | that is bundled with this package in the file LICENSE, and is |
// | available at through the world-wide-web at |
// | http://www.php.net/license/2_02.txt. |
// | If you did not receive a copy of the PHP license and are unable to |
// | obtain it through the world-wide-web, please send a note to |
// | license@php.net so we can mail you a copy immediately. |
// +----------------------------------------------------------------------+
// | Author: Xavier Noguer <xnoguer@php.net> |
// | Based on OLE::Storage_Lite by Kawai, Takanori |
// +----------------------------------------------------------------------+
//
use PhpOffice\PhpSpreadsheet\Shared\OLE;
use PhpOffice\PhpSpreadsheet\Shared\OLE\PPS;
/**
* Class for creating Root PPS's for OLE containers.
*
* @author Xavier Noguer <xnoguer@php.net>
*/
class Root extends PPS
{
/**
* @var resource
*/
private $fileHandle;
/**
* @var int
*/
private $smallBlockSize;
/**
* @var int
*/
private $bigBlockSize;
/**
* @param null|float|int $time_1st A timestamp
* @param null|float|int $time_2nd A timestamp
* @param File[] $raChild
*/
public function __construct($time_1st, $time_2nd, $raChild)
{
parent::__construct(null, OLE::ascToUcs('Root Entry'), OLE::OLE_PPS_TYPE_ROOT, null, null, null, $time_1st, $time_2nd, null, $raChild);
}
/**
* Method for saving the whole OLE container (including files).
* In fact, if called with an empty argument (or '-'), it saves to a
* temporary file and then outputs it's contents to stdout.
* If a resource pointer to a stream created by fopen() is passed
* it will be used, but you have to close such stream by yourself.
*
* @param resource $fileHandle the name of the file or stream where to save the OLE container
*
* @return bool true on success
*/
public function save($fileHandle)
{
$this->fileHandle = $fileHandle;
// Initial Setting for saving
$this->bigBlockSize = (int) (2 ** (
(isset($this->bigBlockSize)) ? self::adjust2($this->bigBlockSize) : 9
));
$this->smallBlockSize = (int) (2 ** (
(isset($this->smallBlockSize)) ? self::adjust2($this->smallBlockSize) : 6
));
// Make an array of PPS's (for Save)
$aList = [];
PPS::savePpsSetPnt($aList, [$this]);
// calculate values for header
[$iSBDcnt, $iBBcnt, $iPPScnt] = $this->calcSize($aList); //, $rhInfo);
// Save Header
$this->saveHeader($iSBDcnt, $iBBcnt, $iPPScnt);
// Make Small Data string (write SBD)
$this->_data = $this->makeSmallData($aList);
// Write BB
$this->saveBigData($iSBDcnt, $aList);
// Write PPS
$this->savePps($aList);
// Write Big Block Depot and BDList and Adding Header informations
$this->saveBbd($iSBDcnt, $iBBcnt, $iPPScnt);
return true;
}
/**
* Calculate some numbers.
*
* @param array $raList Reference to an array of PPS's
*
* @return float[] The array of numbers
*/
private function calcSize(&$raList)
{
// Calculate Basic Setting
[$iSBDcnt, $iBBcnt, $iPPScnt] = [0, 0, 0];
$iSmallLen = 0;
$iSBcnt = 0;
$iCount = count($raList);
for ($i = 0; $i < $iCount; ++$i) {
if ($raList[$i]->Type == OLE::OLE_PPS_TYPE_FILE) {
$raList[$i]->Size = $raList[$i]->getDataLen();
if ($raList[$i]->Size < OLE::OLE_DATA_SIZE_SMALL) {
$iSBcnt += floor($raList[$i]->Size / $this->smallBlockSize)
+ (($raList[$i]->Size % $this->smallBlockSize) ? 1 : 0);
} else {
$iBBcnt += (floor($raList[$i]->Size / $this->bigBlockSize) +
(($raList[$i]->Size % $this->bigBlockSize) ? 1 : 0));
}
}
}
$iSmallLen = $iSBcnt * $this->smallBlockSize;
$iSlCnt = floor($this->bigBlockSize / OLE::OLE_LONG_INT_SIZE);
$iSBDcnt = floor($iSBcnt / $iSlCnt) + (($iSBcnt % $iSlCnt) ? 1 : 0);
$iBBcnt += (floor($iSmallLen / $this->bigBlockSize) +
(($iSmallLen % $this->bigBlockSize) ? 1 : 0));
$iCnt = count($raList);
$iBdCnt = $this->bigBlockSize / OLE::OLE_PPS_SIZE;
$iPPScnt = (floor($iCnt / $iBdCnt) + (($iCnt % $iBdCnt) ? 1 : 0));
return [$iSBDcnt, $iBBcnt, $iPPScnt];
}
/**
* Helper function for caculating a magic value for block sizes.
*
* @param int $i2 The argument
*
* @return float
*
* @see save()
*/
private static function adjust2($i2)
{
$iWk = log($i2) / log(2);
return ($iWk > floor($iWk)) ? floor($iWk) + 1 : $iWk;
}
/**
* Save OLE header.
*
* @param int $iSBDcnt
* @param int $iBBcnt
* @param int $iPPScnt
*/
private function saveHeader($iSBDcnt, $iBBcnt, $iPPScnt): void
{
$FILE = $this->fileHandle;
// Calculate Basic Setting
$iBlCnt = $this->bigBlockSize / OLE::OLE_LONG_INT_SIZE;
$i1stBdL = ($this->bigBlockSize - 0x4C) / OLE::OLE_LONG_INT_SIZE;
$iBdExL = 0;
$iAll = $iBBcnt + $iPPScnt + $iSBDcnt;
$iAllW = $iAll;
$iBdCntW = floor($iAllW / $iBlCnt) + (($iAllW % $iBlCnt) ? 1 : 0);
$iBdCnt = floor(($iAll + $iBdCntW) / $iBlCnt) + ((($iAllW + $iBdCntW) % $iBlCnt) ? 1 : 0);
// Calculate BD count
if ($iBdCnt > $i1stBdL) {
while (1) {
++$iBdExL;
++$iAllW;
$iBdCntW = floor($iAllW / $iBlCnt) + (($iAllW % $iBlCnt) ? 1 : 0);
$iBdCnt = floor(($iAllW + $iBdCntW) / $iBlCnt) + ((($iAllW + $iBdCntW) % $iBlCnt) ? 1 : 0);
if ($iBdCnt <= ($iBdExL * $iBlCnt + $i1stBdL)) {
break;
}
}
}
// Save Header
fwrite(
$FILE,
"\xD0\xCF\x11\xE0\xA1\xB1\x1A\xE1"
. "\x00\x00\x00\x00"
. "\x00\x00\x00\x00"
. "\x00\x00\x00\x00"
. "\x00\x00\x00\x00"
. pack('v', 0x3b)
. pack('v', 0x03)
. pack('v', -2)
. pack('v', 9)
. pack('v', 6)
. pack('v', 0)
. "\x00\x00\x00\x00"
. "\x00\x00\x00\x00"
. pack('V', $iBdCnt)
. pack('V', $iBBcnt + $iSBDcnt) //ROOT START
. pack('V', 0)
. pack('V', 0x1000)
. pack('V', $iSBDcnt ? 0 : -2) //Small Block Depot
. pack('V', $iSBDcnt)
);
// Extra BDList Start, Count
if ($iBdCnt < $i1stBdL) {
fwrite(
$FILE,
pack('V', -2) // Extra BDList Start
. pack('V', 0)// Extra BDList Count
);
} else {
fwrite($FILE, pack('V', $iAll + $iBdCnt) . pack('V', $iBdExL));
}
// BDList
for ($i = 0; $i < $i1stBdL && $i < $iBdCnt; ++$i) {
fwrite($FILE, pack('V', $iAll + $i));
}
if ($i < $i1stBdL) {
$jB = $i1stBdL - $i;
for ($j = 0; $j < $jB; ++$j) {
fwrite($FILE, (pack('V', -1)));
}
}
}
/**
* Saving big data (PPS's with data bigger than \PhpOffice\PhpSpreadsheet\Shared\OLE::OLE_DATA_SIZE_SMALL).
*
* @param int $iStBlk
* @param array $raList Reference to array of PPS's
*/
private function saveBigData($iStBlk, &$raList): void
{
$FILE = $this->fileHandle;
// cycle through PPS's
$iCount = count($raList);
for ($i = 0; $i < $iCount; ++$i) {
if ($raList[$i]->Type != OLE::OLE_PPS_TYPE_DIR) {
$raList[$i]->Size = $raList[$i]->getDataLen();
if (($raList[$i]->Size >= OLE::OLE_DATA_SIZE_SMALL) || (($raList[$i]->Type == OLE::OLE_PPS_TYPE_ROOT) && isset($raList[$i]->_data))) {
fwrite($FILE, $raList[$i]->_data);
if ($raList[$i]->Size % $this->bigBlockSize) {
fwrite($FILE, str_repeat("\x00", $this->bigBlockSize - ($raList[$i]->Size % $this->bigBlockSize)));
}
// Set For PPS
$raList[$i]->startBlock = $iStBlk;
$iStBlk +=
(floor($raList[$i]->Size / $this->bigBlockSize) +
(($raList[$i]->Size % $this->bigBlockSize) ? 1 : 0));
}
}
}
}
/**
* get small data (PPS's with data smaller than \PhpOffice\PhpSpreadsheet\Shared\OLE::OLE_DATA_SIZE_SMALL).
*
* @param array $raList Reference to array of PPS's
*
* @return string
*/
private function makeSmallData(&$raList)
{
$sRes = '';
$FILE = $this->fileHandle;
$iSmBlk = 0;
$iCount = count($raList);
for ($i = 0; $i < $iCount; ++$i) {
// Make SBD, small data string
if ($raList[$i]->Type == OLE::OLE_PPS_TYPE_FILE) {
if ($raList[$i]->Size <= 0) {
continue;
}
if ($raList[$i]->Size < OLE::OLE_DATA_SIZE_SMALL) {
$iSmbCnt = floor($raList[$i]->Size / $this->smallBlockSize)
+ (($raList[$i]->Size % $this->smallBlockSize) ? 1 : 0);
// Add to SBD
$jB = $iSmbCnt - 1;
for ($j = 0; $j < $jB; ++$j) {
fwrite($FILE, pack('V', $j + $iSmBlk + 1));
}
fwrite($FILE, pack('V', -2));
// Add to Data String(this will be written for RootEntry)
$sRes .= $raList[$i]->_data;
if ($raList[$i]->Size % $this->smallBlockSize) {
$sRes .= str_repeat("\x00", $this->smallBlockSize - ($raList[$i]->Size % $this->smallBlockSize));
}
// Set for PPS
$raList[$i]->startBlock = $iSmBlk;
$iSmBlk += $iSmbCnt;
}
}
}
$iSbCnt = floor($this->bigBlockSize / OLE::OLE_LONG_INT_SIZE);
if ($iSmBlk % $iSbCnt) {
$iB = $iSbCnt - ($iSmBlk % $iSbCnt);
for ($i = 0; $i < $iB; ++$i) {
fwrite($FILE, pack('V', -1));
}
}
return $sRes;
}
/**
* Saves all the PPS's WKs.
*
* @param array $raList Reference to an array with all PPS's
*/
private function savePps(&$raList): void
{
// Save each PPS WK
$iC = count($raList);
for ($i = 0; $i < $iC; ++$i) {
fwrite($this->fileHandle, $raList[$i]->getPpsWk());
}
// Adjust for Block
$iCnt = count($raList);
$iBCnt = $this->bigBlockSize / OLE::OLE_PPS_SIZE;
if ($iCnt % $iBCnt) {
fwrite($this->fileHandle, str_repeat("\x00", ($iBCnt - ($iCnt % $iBCnt)) * OLE::OLE_PPS_SIZE));
}
}
/**
* Saving Big Block Depot.
*
* @param int $iSbdSize
* @param int $iBsize
* @param int $iPpsCnt
*/
private function saveBbd($iSbdSize, $iBsize, $iPpsCnt): void
{
$FILE = $this->fileHandle;
// Calculate Basic Setting
$iBbCnt = $this->bigBlockSize / OLE::OLE_LONG_INT_SIZE;
$i1stBdL = ($this->bigBlockSize - 0x4C) / OLE::OLE_LONG_INT_SIZE;
$iBdExL = 0;
$iAll = $iBsize + $iPpsCnt + $iSbdSize;
$iAllW = $iAll;
$iBdCntW = floor($iAllW / $iBbCnt) + (($iAllW % $iBbCnt) ? 1 : 0);
$iBdCnt = floor(($iAll + $iBdCntW) / $iBbCnt) + ((($iAllW + $iBdCntW) % $iBbCnt) ? 1 : 0);
// Calculate BD count
if ($iBdCnt > $i1stBdL) {
while (1) {
++$iBdExL;
++$iAllW;
$iBdCntW = floor($iAllW / $iBbCnt) + (($iAllW % $iBbCnt) ? 1 : 0);
$iBdCnt = floor(($iAllW + $iBdCntW) / $iBbCnt) + ((($iAllW + $iBdCntW) % $iBbCnt) ? 1 : 0);
if ($iBdCnt <= ($iBdExL * $iBbCnt + $i1stBdL)) {
break;
}
}
}
// Making BD
// Set for SBD
if ($iSbdSize > 0) {
for ($i = 0; $i < ($iSbdSize - 1); ++$i) {
fwrite($FILE, pack('V', $i + 1));
}
fwrite($FILE, pack('V', -2));
}
// Set for B
for ($i = 0; $i < ($iBsize - 1); ++$i) {
fwrite($FILE, pack('V', $i + $iSbdSize + 1));
}
fwrite($FILE, pack('V', -2));
// Set for PPS
for ($i = 0; $i < ($iPpsCnt - 1); ++$i) {
fwrite($FILE, pack('V', $i + $iSbdSize + $iBsize + 1));
}
fwrite($FILE, pack('V', -2));
// Set for BBD itself ( 0xFFFFFFFD : BBD)
for ($i = 0; $i < $iBdCnt; ++$i) {
fwrite($FILE, pack('V', 0xFFFFFFFD));
}
// Set for ExtraBDList
for ($i = 0; $i < $iBdExL; ++$i) {
fwrite($FILE, pack('V', 0xFFFFFFFC));
}
// Adjust for Block
if (($iAllW + $iBdCnt) % $iBbCnt) {
$iBlock = ($iBbCnt - (($iAllW + $iBdCnt) % $iBbCnt));
for ($i = 0; $i < $iBlock; ++$i) {
fwrite($FILE, pack('V', -1));
}
}
// Extra BDList
if ($iBdCnt > $i1stBdL) {
$iN = 0;
$iNb = 0;
for ($i = $i1stBdL; $i < $iBdCnt; $i++, ++$iN) {
if ($iN >= ($iBbCnt - 1)) {
$iN = 0;
++$iNb;
fwrite($FILE, pack('V', $iAll + $iBdCnt + $iNb));
}
fwrite($FILE, pack('V', $iBsize + $iSbdSize + $iPpsCnt + $i));
}
if (($iBdCnt - $i1stBdL) % ($iBbCnt - 1)) {
$iB = ($iBbCnt - 1) - (($iBdCnt - $i1stBdL) % ($iBbCnt - 1));
for ($i = 0; $i < $iB; ++$i) {
fwrite($FILE, pack('V', -1));
}
}
fwrite($FILE, pack('V', -2));
}
}
}

722
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/StringHelper.php

@ -0,0 +1,722 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Shared;
use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
class StringHelper
{
/** Constants */
/** Regular Expressions */
// Fraction
const STRING_REGEXP_FRACTION = '(-?)(\d+)\s+(\d+\/\d+)';
/**
* Control characters array.
*
* @var string[]
*/
private static $controlCharacters = [];
/**
* SYLK Characters array.
*
* @var array
*/
private static $SYLKCharacters = [];
/**
* Decimal separator.
*
* @var string
*/
private static $decimalSeparator;
/**
* Thousands separator.
*
* @var string
*/
private static $thousandsSeparator;
/**
* Currency code.
*
* @var string
*/
private static $currencyCode;
/**
* Is iconv extension avalable?
*
* @var ?bool
*/
private static $isIconvEnabled;
/**
* iconv options.
*
* @var string
*/
private static $iconvOptions = '//IGNORE//TRANSLIT';
/**
* Build control characters array.
*/
private static function buildControlCharacters(): void
{
for ($i = 0; $i <= 31; ++$i) {
if ($i != 9 && $i != 10 && $i != 13) {
$find = '_x' . sprintf('%04s', strtoupper(dechex($i))) . '_';
$replace = chr($i);
self::$controlCharacters[$find] = $replace;
}
}
}
/**
* Build SYLK characters array.
*/
private static function buildSYLKCharacters(): void
{
self::$SYLKCharacters = [
"\x1B 0" => chr(0),
"\x1B 1" => chr(1),
"\x1B 2" => chr(2),
"\x1B 3" => chr(3),
"\x1B 4" => chr(4),
"\x1B 5" => chr(5),
"\x1B 6" => chr(6),
"\x1B 7" => chr(7),
"\x1B 8" => chr(8),
"\x1B 9" => chr(9),
"\x1B :" => chr(10),
"\x1B ;" => chr(11),
"\x1B <" => chr(12),
"\x1B =" => chr(13),
"\x1B >" => chr(14),
"\x1B ?" => chr(15),
"\x1B!0" => chr(16),
"\x1B!1" => chr(17),
"\x1B!2" => chr(18),
"\x1B!3" => chr(19),
"\x1B!4" => chr(20),
"\x1B!5" => chr(21),
"\x1B!6" => chr(22),
"\x1B!7" => chr(23),
"\x1B!8" => chr(24),
"\x1B!9" => chr(25),
"\x1B!:" => chr(26),
"\x1B!;" => chr(27),
"\x1B!<" => chr(28),
"\x1B!=" => chr(29),
"\x1B!>" => chr(30),
"\x1B!?" => chr(31),
"\x1B'?" => chr(127),
"\x1B(0" => '€', // 128 in CP1252
"\x1B(2" => '‚', // 130 in CP1252
"\x1B(3" => 'ƒ', // 131 in CP1252
"\x1B(4" => '„', // 132 in CP1252
"\x1B(5" => '…', // 133 in CP1252
"\x1B(6" => '†', // 134 in CP1252
"\x1B(7" => '‡', // 135 in CP1252
"\x1B(8" => 'ˆ', // 136 in CP1252
"\x1B(9" => '‰', // 137 in CP1252
"\x1B(:" => 'Š', // 138 in CP1252
"\x1B(;" => '‹', // 139 in CP1252
"\x1BNj" => 'Œ', // 140 in CP1252
"\x1B(>" => 'Ž', // 142 in CP1252
"\x1B)1" => '‘', // 145 in CP1252
"\x1B)2" => '’', // 146 in CP1252
"\x1B)3" => '“', // 147 in CP1252
"\x1B)4" => '”', // 148 in CP1252
"\x1B)5" => '•', // 149 in CP1252
"\x1B)6" => '–', // 150 in CP1252
"\x1B)7" => '—', // 151 in CP1252
"\x1B)8" => '˜', // 152 in CP1252
"\x1B)9" => '™', // 153 in CP1252
"\x1B):" => 'š', // 154 in CP1252
"\x1B);" => '›', // 155 in CP1252
"\x1BNz" => 'œ', // 156 in CP1252
"\x1B)>" => 'ž', // 158 in CP1252
"\x1B)?" => 'Ÿ', // 159 in CP1252
"\x1B*0" => ' ', // 160 in CP1252
"\x1BN!" => '¡', // 161 in CP1252
"\x1BN\"" => '¢', // 162 in CP1252
"\x1BN#" => '£', // 163 in CP1252
"\x1BN(" => '¤', // 164 in CP1252
"\x1BN%" => '¥', // 165 in CP1252
"\x1B*6" => '¦', // 166 in CP1252
"\x1BN'" => '§', // 167 in CP1252
"\x1BNH " => '¨', // 168 in CP1252
"\x1BNS" => '©', // 169 in CP1252
"\x1BNc" => 'ª', // 170 in CP1252
"\x1BN+" => '«', // 171 in CP1252
"\x1B*<" => '¬', // 172 in CP1252
"\x1B*=" => '­', // 173 in CP1252
"\x1BNR" => '®', // 174 in CP1252
"\x1B*?" => '¯', // 175 in CP1252
"\x1BN0" => '°', // 176 in CP1252
"\x1BN1" => '±', // 177 in CP1252
"\x1BN2" => '²', // 178 in CP1252
"\x1BN3" => '³', // 179 in CP1252
"\x1BNB " => '´', // 180 in CP1252
"\x1BN5" => 'µ', // 181 in CP1252
"\x1BN6" => '¶', // 182 in CP1252
"\x1BN7" => '·', // 183 in CP1252
"\x1B+8" => '¸', // 184 in CP1252
"\x1BNQ" => '¹', // 185 in CP1252
"\x1BNk" => 'º', // 186 in CP1252
"\x1BN;" => '»', // 187 in CP1252
"\x1BN<" => '¼', // 188 in CP1252
"\x1BN=" => '½', // 189 in CP1252
"\x1BN>" => '¾', // 190 in CP1252
"\x1BN?" => '¿', // 191 in CP1252
"\x1BNAA" => 'À', // 192 in CP1252
"\x1BNBA" => 'Á', // 193 in CP1252
"\x1BNCA" => 'Â', // 194 in CP1252
"\x1BNDA" => 'Ã', // 195 in CP1252
"\x1BNHA" => 'Ä', // 196 in CP1252
"\x1BNJA" => 'Å', // 197 in CP1252
"\x1BNa" => 'Æ', // 198 in CP1252
"\x1BNKC" => 'Ç', // 199 in CP1252
"\x1BNAE" => 'È', // 200 in CP1252
"\x1BNBE" => 'É', // 201 in CP1252
"\x1BNCE" => 'Ê', // 202 in CP1252
"\x1BNHE" => 'Ë', // 203 in CP1252
"\x1BNAI" => 'Ì', // 204 in CP1252
"\x1BNBI" => 'Í', // 205 in CP1252
"\x1BNCI" => 'Î', // 206 in CP1252
"\x1BNHI" => 'Ï', // 207 in CP1252
"\x1BNb" => 'Ð', // 208 in CP1252
"\x1BNDN" => 'Ñ', // 209 in CP1252
"\x1BNAO" => 'Ò', // 210 in CP1252
"\x1BNBO" => 'Ó', // 211 in CP1252
"\x1BNCO" => 'Ô', // 212 in CP1252
"\x1BNDO" => 'Õ', // 213 in CP1252
"\x1BNHO" => 'Ö', // 214 in CP1252
"\x1B-7" => '×', // 215 in CP1252
"\x1BNi" => 'Ø', // 216 in CP1252
"\x1BNAU" => 'Ù', // 217 in CP1252
"\x1BNBU" => 'Ú', // 218 in CP1252
"\x1BNCU" => 'Û', // 219 in CP1252
"\x1BNHU" => 'Ü', // 220 in CP1252
"\x1B-=" => 'Ý', // 221 in CP1252
"\x1BNl" => 'Þ', // 222 in CP1252
"\x1BN{" => 'ß', // 223 in CP1252
"\x1BNAa" => 'à', // 224 in CP1252
"\x1BNBa" => 'á', // 225 in CP1252
"\x1BNCa" => 'â', // 226 in CP1252
"\x1BNDa" => 'ã', // 227 in CP1252
"\x1BNHa" => 'ä', // 228 in CP1252
"\x1BNJa" => 'å', // 229 in CP1252
"\x1BNq" => 'æ', // 230 in CP1252
"\x1BNKc" => 'ç', // 231 in CP1252
"\x1BNAe" => 'è', // 232 in CP1252
"\x1BNBe" => 'é', // 233 in CP1252
"\x1BNCe" => 'ê', // 234 in CP1252
"\x1BNHe" => 'ë', // 235 in CP1252
"\x1BNAi" => 'ì', // 236 in CP1252
"\x1BNBi" => 'í', // 237 in CP1252
"\x1BNCi" => 'î', // 238 in CP1252
"\x1BNHi" => 'ï', // 239 in CP1252
"\x1BNs" => 'ð', // 240 in CP1252
"\x1BNDn" => 'ñ', // 241 in CP1252
"\x1BNAo" => 'ò', // 242 in CP1252
"\x1BNBo" => 'ó', // 243 in CP1252
"\x1BNCo" => 'ô', // 244 in CP1252
"\x1BNDo" => 'õ', // 245 in CP1252
"\x1BNHo" => 'ö', // 246 in CP1252
"\x1B/7" => '÷', // 247 in CP1252
"\x1BNy" => 'ø', // 248 in CP1252
"\x1BNAu" => 'ù', // 249 in CP1252
"\x1BNBu" => 'ú', // 250 in CP1252
"\x1BNCu" => 'û', // 251 in CP1252
"\x1BNHu" => 'ü', // 252 in CP1252
"\x1B/=" => 'ý', // 253 in CP1252
"\x1BN|" => 'þ', // 254 in CP1252
"\x1BNHy" => 'ÿ', // 255 in CP1252
];
}
/**
* Get whether iconv extension is available.
*
* @return bool
*/
public static function getIsIconvEnabled()
{
if (isset(self::$isIconvEnabled)) {
return self::$isIconvEnabled;
}
// Assume no problems with iconv
self::$isIconvEnabled = true;
// Fail if iconv doesn't exist
if (!function_exists('iconv')) {
self::$isIconvEnabled = false;
} elseif (!@iconv('UTF-8', 'UTF-16LE', 'x')) {
// Sometimes iconv is not working, and e.g. iconv('UTF-8', 'UTF-16LE', 'x') just returns false,
self::$isIconvEnabled = false;
} elseif (defined('PHP_OS') && @stristr(PHP_OS, 'AIX') && defined('ICONV_IMPL') && (@strcasecmp(ICONV_IMPL, 'unknown') == 0) && defined('ICONV_VERSION') && (@strcasecmp(ICONV_VERSION, 'unknown') == 0)) {
// CUSTOM: IBM AIX iconv() does not work
self::$isIconvEnabled = false;
}
// Deactivate iconv default options if they fail (as seen on IMB i)
if (self::$isIconvEnabled && !@iconv('UTF-8', 'UTF-16LE' . self::$iconvOptions, 'x')) {
self::$iconvOptions = '';
}
return self::$isIconvEnabled;
}
private static function buildCharacterSets(): void
{
if (empty(self::$controlCharacters)) {
self::buildControlCharacters();
}
if (empty(self::$SYLKCharacters)) {
self::buildSYLKCharacters();
}
}
/**
* Convert from OpenXML escaped control character to PHP control character.
*
* Excel 2007 team:
* ----------------
* That's correct, control characters are stored directly in the shared-strings table.
* We do encode characters that cannot be represented in XML using the following escape sequence:
* _xHHHH_ where H represents a hexadecimal character in the character's value...
* So you could end up with something like _x0008_ in a string (either in a cell value (<v>)
* element or in the shared string <t> element.
*
* @param string $textValue Value to unescape
*
* @return string
*/
public static function controlCharacterOOXML2PHP($textValue)
{
self::buildCharacterSets();
return str_replace(array_keys(self::$controlCharacters), array_values(self::$controlCharacters), $textValue);
}
/**
* Convert from PHP control character to OpenXML escaped control character.
*
* Excel 2007 team:
* ----------------
* That's correct, control characters are stored directly in the shared-strings table.
* We do encode characters that cannot be represented in XML using the following escape sequence:
* _xHHHH_ where H represents a hexadecimal character in the character's value...
* So you could end up with something like _x0008_ in a string (either in a cell value (<v>)
* element or in the shared string <t> element.
*
* @param string $textValue Value to escape
*
* @return string
*/
public static function controlCharacterPHP2OOXML($textValue)
{
self::buildCharacterSets();
return str_replace(array_values(self::$controlCharacters), array_keys(self::$controlCharacters), $textValue);
}
/**
* Try to sanitize UTF8, stripping invalid byte sequences. Not perfect. Does not surrogate characters.
*
* @param string $textValue
*
* @return string
*/
public static function sanitizeUTF8($textValue)
{
if (self::getIsIconvEnabled()) {
$textValue = @iconv('UTF-8', 'UTF-8', $textValue);
return $textValue;
}
$textValue = mb_convert_encoding($textValue, 'UTF-8', 'UTF-8');
return $textValue;
}
/**
* Check if a string contains UTF8 data.
*
* @param string $textValue
*
* @return bool
*/
public static function isUTF8($textValue)
{
return $textValue === '' || preg_match('/^./su', $textValue) === 1;
}
/**
* Formats a numeric value as a string for output in various output writers forcing
* point as decimal separator in case locale is other than English.
*
* @param mixed $numericValue
*
* @return string
*/
public static function formatNumber($numericValue)
{
if (is_float($numericValue)) {
return str_replace(',', '.', $numericValue);
}
return (string) $numericValue;
}
/**
* Converts a UTF-8 string into BIFF8 Unicode string data (8-bit string length)
* Writes the string using uncompressed notation, no rich text, no Asian phonetics
* If mbstring extension is not available, ASCII is assumed, and compressed notation is used
* although this will give wrong results for non-ASCII strings
* see OpenOffice.org's Documentation of the Microsoft Excel File Format, sect. 2.5.3.
*
* @param string $textValue UTF-8 encoded string
* @param mixed[] $arrcRuns Details of rich text runs in $value
*
* @return string
*/
public static function UTF8toBIFF8UnicodeShort($textValue, $arrcRuns = [])
{
// character count
$ln = self::countCharacters($textValue, 'UTF-8');
// option flags
if (empty($arrcRuns)) {
$data = pack('CC', $ln, 0x0001);
// characters
$data .= self::convertEncoding($textValue, 'UTF-16LE', 'UTF-8');
} else {
$data = pack('vC', $ln, 0x09);
$data .= pack('v', count($arrcRuns));
// characters
$data .= self::convertEncoding($textValue, 'UTF-16LE', 'UTF-8');
foreach ($arrcRuns as $cRun) {
$data .= pack('v', $cRun['strlen']);
$data .= pack('v', $cRun['fontidx']);
}
}
return $data;
}
/**
* Converts a UTF-8 string into BIFF8 Unicode string data (16-bit string length)
* Writes the string using uncompressed notation, no rich text, no Asian phonetics
* If mbstring extension is not available, ASCII is assumed, and compressed notation is used
* although this will give wrong results for non-ASCII strings
* see OpenOffice.org's Documentation of the Microsoft Excel File Format, sect. 2.5.3.
*
* @param string $textValue UTF-8 encoded string
*
* @return string
*/
public static function UTF8toBIFF8UnicodeLong($textValue)
{
// character count
$ln = self::countCharacters($textValue, 'UTF-8');
// characters
$chars = self::convertEncoding($textValue, 'UTF-16LE', 'UTF-8');
return pack('vC', $ln, 0x0001) . $chars;
}
/**
* Convert string from one encoding to another.
*
* @param string $textValue
* @param string $to Encoding to convert to, e.g. 'UTF-8'
* @param string $from Encoding to convert from, e.g. 'UTF-16LE'
*
* @return string
*/
public static function convertEncoding($textValue, $to, $from)
{
if (self::getIsIconvEnabled()) {
$result = iconv($from, $to . self::$iconvOptions, $textValue);
if (false !== $result) {
return $result;
}
}
return mb_convert_encoding($textValue, $to, $from);
}
/**
* Get character count.
*
* @param string $textValue
* @param string $encoding Encoding
*
* @return int Character count
*/
public static function countCharacters($textValue, $encoding = 'UTF-8')
{
return mb_strlen($textValue ?? '', $encoding);
}
/**
* Get a substring of a UTF-8 encoded string.
*
* @param string $textValue UTF-8 encoded string
* @param int $offset Start offset
* @param int $length Maximum number of characters in substring
*
* @return string
*/
public static function substring($textValue, $offset, $length = 0)
{
return mb_substr($textValue, $offset, $length, 'UTF-8');
}
/**
* Convert a UTF-8 encoded string to upper case.
*
* @param string $textValue UTF-8 encoded string
*
* @return string
*/
public static function strToUpper($textValue)
{
return mb_convert_case($textValue ?? '', MB_CASE_UPPER, 'UTF-8');
}
/**
* Convert a UTF-8 encoded string to lower case.
*
* @param string $textValue UTF-8 encoded string
*
* @return string
*/
public static function strToLower($textValue)
{
return mb_convert_case($textValue ?? '', MB_CASE_LOWER, 'UTF-8');
}
/**
* Convert a UTF-8 encoded string to title/proper case
* (uppercase every first character in each word, lower case all other characters).
*
* @param string $textValue UTF-8 encoded string
*
* @return string
*/
public static function strToTitle($textValue)
{
return mb_convert_case($textValue, MB_CASE_TITLE, 'UTF-8');
}
public static function mbIsUpper($character)
{
return mb_strtolower($character, 'UTF-8') != $character;
}
public static function mbStrSplit($string)
{
// Split at all position not after the start: ^
// and not before the end: $
return preg_split('/(?<!^)(?!$)/u', $string);
}
/**
* Reverse the case of a string, so that all uppercase characters become lowercase
* and all lowercase characters become uppercase.
*
* @param string $textValue UTF-8 encoded string
*
* @return string
*/
public static function strCaseReverse($textValue)
{
$characters = self::mbStrSplit($textValue);
foreach ($characters as &$character) {
if (self::mbIsUpper($character)) {
$character = mb_strtolower($character, 'UTF-8');
} else {
$character = mb_strtoupper($character, 'UTF-8');
}
}
return implode('', $characters);
}
/**
* Identify whether a string contains a fractional numeric value,
* and convert it to a numeric if it is.
*
* @param string $operand string value to test
*
* @return bool
*/
public static function convertToNumberIfFraction(&$operand)
{
if (preg_match('/^' . self::STRING_REGEXP_FRACTION . '$/i', $operand, $match)) {
$sign = ($match[1] == '-') ? '-' : '+';
$fractionFormula = '=' . $sign . $match[2] . $sign . $match[3];
$operand = Calculation::getInstance()->_calculateFormulaValue($fractionFormula);
return true;
}
return false;
}
// function convertToNumberIfFraction()
/**
* Get the decimal separator. If it has not yet been set explicitly, try to obtain number
* formatting information from locale.
*
* @return string
*/
public static function getDecimalSeparator()
{
if (!isset(self::$decimalSeparator)) {
$localeconv = localeconv();
self::$decimalSeparator = ($localeconv['decimal_point'] != '')
? $localeconv['decimal_point'] : $localeconv['mon_decimal_point'];
if (self::$decimalSeparator == '') {
// Default to .
self::$decimalSeparator = '.';
}
}
return self::$decimalSeparator;
}
/**
* Set the decimal separator. Only used by NumberFormat::toFormattedString()
* to format output by \PhpOffice\PhpSpreadsheet\Writer\Html and \PhpOffice\PhpSpreadsheet\Writer\Pdf.
*
* @param string $separator Character for decimal separator
*/
public static function setDecimalSeparator($separator): void
{
self::$decimalSeparator = $separator;
}
/**
* Get the thousands separator. If it has not yet been set explicitly, try to obtain number
* formatting information from locale.
*
* @return string
*/
public static function getThousandsSeparator()
{
if (!isset(self::$thousandsSeparator)) {
$localeconv = localeconv();
self::$thousandsSeparator = ($localeconv['thousands_sep'] != '')
? $localeconv['thousands_sep'] : $localeconv['mon_thousands_sep'];
if (self::$thousandsSeparator == '') {
// Default to .
self::$thousandsSeparator = ',';
}
}
return self::$thousandsSeparator;
}
/**
* Set the thousands separator. Only used by NumberFormat::toFormattedString()
* to format output by \PhpOffice\PhpSpreadsheet\Writer\Html and \PhpOffice\PhpSpreadsheet\Writer\Pdf.
*
* @param string $separator Character for thousands separator
*/
public static function setThousandsSeparator($separator): void
{
self::$thousandsSeparator = $separator;
}
/**
* Get the currency code. If it has not yet been set explicitly, try to obtain the
* symbol information from locale.
*
* @return string
*/
public static function getCurrencyCode()
{
if (!empty(self::$currencyCode)) {
return self::$currencyCode;
}
self::$currencyCode = '$';
$localeconv = localeconv();
if (!empty($localeconv['currency_symbol'])) {
self::$currencyCode = $localeconv['currency_symbol'];
return self::$currencyCode;
}
if (!empty($localeconv['int_curr_symbol'])) {
self::$currencyCode = $localeconv['int_curr_symbol'];
return self::$currencyCode;
}
return self::$currencyCode;
}
/**
* Set the currency code. Only used by NumberFormat::toFormattedString()
* to format output by \PhpOffice\PhpSpreadsheet\Writer\Html and \PhpOffice\PhpSpreadsheet\Writer\Pdf.
*
* @param string $currencyCode Character for currency code
*/
public static function setCurrencyCode($currencyCode): void
{
self::$currencyCode = $currencyCode;
}
/**
* Convert SYLK encoded string to UTF-8.
*
* @param string $textValue
*
* @return string UTF-8 encoded string
*/
public static function SYLKtoUTF8($textValue)
{
self::buildCharacterSets();
// If there is no escape character in the string there is nothing to do
if (strpos($textValue, '') === false) {
return $textValue;
}
foreach (self::$SYLKCharacters as $k => $v) {
$textValue = str_replace($k, $v, $textValue);
}
return $textValue;
}
/**
* Retrieve any leading numeric part of a string, or return the full string if no leading numeric
* (handles basic integer or float, but not exponent or non decimal).
*
* @param string $textValue
*
* @return mixed string or only the leading numeric part of the string
*/
public static function testStringAsNumeric($textValue)
{
if (is_numeric($textValue)) {
return $textValue;
}
$v = (float) $textValue;
return (is_numeric(substr($textValue, 0, strlen($v)))) ? $v : $textValue;
}
}

77
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/TimeZone.php

@ -0,0 +1,77 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Shared;
use DateTimeZone;
use PhpOffice\PhpSpreadsheet\Exception as PhpSpreadsheetException;
class TimeZone
{
/**
* Default Timezone used for date/time conversions.
*
* @var string
*/
protected static $timezone = 'UTC';
/**
* Validate a Timezone name.
*
* @param string $timezoneName Time zone (e.g. 'Europe/London')
*
* @return bool Success or failure
*/
private static function validateTimeZone($timezoneName)
{
return in_array($timezoneName, DateTimeZone::listIdentifiers(DateTimeZone::ALL_WITH_BC));
}
/**
* Set the Default Timezone used for date/time conversions.
*
* @param string $timezoneName Time zone (e.g. 'Europe/London')
*
* @return bool Success or failure
*/
public static function setTimeZone($timezoneName)
{
if (self::validateTimezone($timezoneName)) {
self::$timezone = $timezoneName;
return true;
}
return false;
}
/**
* Return the Default Timezone used for date/time conversions.
*
* @return string Timezone (e.g. 'Europe/London')
*/
public static function getTimeZone()
{
return self::$timezone;
}
/**
* Return the Timezone offset used for date/time conversions to/from UST
* This requires both the timezone and the calculated date/time to allow for local DST.
*
* @param ?string $timezoneName The timezone for finding the adjustment to UST
* @param float|int $timestamp PHP date/time value
*
* @return int Number of seconds for timezone adjustment
*/
public static function getTimeZoneAdjustment($timezoneName, $timestamp)
{
$timezoneName = $timezoneName ?? self::$timezone;
$dtobj = Date::dateTimeFromTimestamp("$timestamp");
if (!self::validateTimezone($timezoneName)) {
throw new PhpSpreadsheetException("Invalid timezone $timezoneName");
}
$dtobj->setTimeZone(new DateTimeZone($timezoneName));
return $dtobj->getOffset();
}
}

202
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Trend/PolynomialBestFit.php

@ -0,0 +1,202 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Shared\Trend;
use PhpOffice\PhpSpreadsheet\Shared\JAMA\Matrix;
class PolynomialBestFit extends BestFit
{
/**
* Algorithm type to use for best-fit
* (Name of this Trend class).
*
* @var string
*/
protected $bestFitType = 'polynomial';
/**
* Polynomial order.
*
* @var int
*/
protected $order = 0;
/**
* Return the order of this polynomial.
*
* @return int
*/
public function getOrder()
{
return $this->order;
}
/**
* Return the Y-Value for a specified value of X.
*
* @param float $xValue X-Value
*
* @return float Y-Value
*/
public function getValueOfYForX($xValue)
{
$retVal = $this->getIntersect();
$slope = $this->getSlope();
// @phpstan-ignore-next-line
foreach ($slope as $key => $value) {
if ($value != 0.0) {
$retVal += $value * $xValue ** ($key + 1);
}
}
return $retVal;
}
/**
* Return the X-Value for a specified value of Y.
*
* @param float $yValue Y-Value
*
* @return float X-Value
*/
public function getValueOfXForY($yValue)
{
return ($yValue - $this->getIntersect()) / $this->getSlope();
}
/**
* Return the Equation of the best-fit line.
*
* @param int $dp Number of places of decimal precision to display
*
* @return string
*/
public function getEquation($dp = 0)
{
$slope = $this->getSlope($dp);
$intersect = $this->getIntersect($dp);
$equation = 'Y = ' . $intersect;
// @phpstan-ignore-next-line
foreach ($slope as $key => $value) {
if ($value != 0.0) {
$equation .= ' + ' . $value . ' * X';
if ($key > 0) {
$equation .= '^' . ($key + 1);
}
}
}
return $equation;
}
/**
* Return the Slope of the line.
*
* @param int $dp Number of places of decimal precision to display
*
* @return float
*/
public function getSlope($dp = 0)
{
if ($dp != 0) {
$coefficients = [];
foreach ($this->slope as $coefficient) {
$coefficients[] = round($coefficient, $dp);
}
// @phpstan-ignore-next-line
return $coefficients;
}
return $this->slope;
}
public function getCoefficients($dp = 0)
{
return array_merge([$this->getIntersect($dp)], $this->getSlope($dp));
}
/**
* Execute the regression and calculate the goodness of fit for a set of X and Y data values.
*
* @param int $order Order of Polynomial for this regression
* @param float[] $yValues The set of Y-values for this regression
* @param float[] $xValues The set of X-values for this regression
*/
private function polynomialRegression($order, $yValues, $xValues): void
{
// calculate sums
$x_sum = array_sum($xValues);
$y_sum = array_sum($yValues);
$xx_sum = $xy_sum = $yy_sum = 0;
for ($i = 0; $i < $this->valueCount; ++$i) {
$xy_sum += $xValues[$i] * $yValues[$i];
$xx_sum += $xValues[$i] * $xValues[$i];
$yy_sum += $yValues[$i] * $yValues[$i];
}
/*
* This routine uses logic from the PHP port of polyfit version 0.1
* written by Michael Bommarito and Paul Meagher
*
* The function fits a polynomial function of order $order through
* a series of x-y data points using least squares.
*
*/
$A = [];
$B = [];
for ($i = 0; $i < $this->valueCount; ++$i) {
for ($j = 0; $j <= $order; ++$j) {
$A[$i][$j] = $xValues[$i] ** $j;
}
}
for ($i = 0; $i < $this->valueCount; ++$i) {
$B[$i] = [$yValues[$i]];
}
$matrixA = new Matrix($A);
$matrixB = new Matrix($B);
$C = $matrixA->solve($matrixB);
$coefficients = [];
for ($i = 0; $i < $C->getRowDimension(); ++$i) {
$r = $C->get($i, 0);
if (abs($r) <= 10 ** (-9)) {
$r = 0;
}
$coefficients[] = $r;
}
$this->intersect = array_shift($coefficients);
$this->slope = $coefficients;
$this->calculateGoodnessOfFit($x_sum, $y_sum, $xx_sum, $yy_sum, $xy_sum, 0, 0, 0);
foreach ($this->xValues as $xKey => $xValue) {
$this->yBestFitValues[$xKey] = $this->getValueOfYForX($xValue);
}
}
/**
* Define the regression and calculate the goodness of fit for a set of X and Y data values.
*
* @param int $order Order of Polynomial for this regression
* @param float[] $yValues The set of Y-values for this regression
* @param float[] $xValues The set of X-values for this regression
*/
public function __construct($order, $yValues, $xValues = [])
{
parent::__construct($yValues, $xValues);
if (!$this->error) {
if ($order < $this->valueCount) {
$this->bestFitType .= '_' . $order;
$this->order = $order;
$this->polynomialRegression($order, $yValues, $xValues);
if (($this->getGoodnessOfFit() < 0.0) || ($this->getGoodnessOfFit() > 1.0)) {
$this->error = true;
}
} else {
$this->error = true;
}
}
}
}

109
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Trend/PowerBestFit.php

@ -0,0 +1,109 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Shared\Trend;
class PowerBestFit extends BestFit
{
/**
* Algorithm type to use for best-fit
* (Name of this Trend class).
*
* @var string
*/
protected $bestFitType = 'power';
/**
* Return the Y-Value for a specified value of X.
*
* @param float $xValue X-Value
*
* @return float Y-Value
*/
public function getValueOfYForX($xValue)
{
return $this->getIntersect() * ($xValue - $this->xOffset) ** $this->getSlope();
}
/**
* Return the X-Value for a specified value of Y.
*
* @param float $yValue Y-Value
*
* @return float X-Value
*/
public function getValueOfXForY($yValue)
{
return (($yValue + $this->yOffset) / $this->getIntersect()) ** (1 / $this->getSlope());
}
/**
* Return the Equation of the best-fit line.
*
* @param int $dp Number of places of decimal precision to display
*
* @return string
*/
public function getEquation($dp = 0)
{
$slope = $this->getSlope($dp);
$intersect = $this->getIntersect($dp);
return 'Y = ' . $intersect . ' * X^' . $slope;
}
/**
* Return the Value of X where it intersects Y = 0.
*
* @param int $dp Number of places of decimal precision to display
*
* @return float
*/
public function getIntersect($dp = 0)
{
if ($dp != 0) {
return round(exp($this->intersect), $dp);
}
return exp($this->intersect);
}
/**
* Execute the regression and calculate the goodness of fit for a set of X and Y data values.
*
* @param float[] $yValues The set of Y-values for this regression
* @param float[] $xValues The set of X-values for this regression
*/
private function powerRegression(array $yValues, array $xValues, bool $const): void
{
$adjustedYValues = array_map(
function ($value) {
return ($value < 0.0) ? 0 - log(abs($value)) : log($value);
},
$yValues
);
$adjustedXValues = array_map(
function ($value) {
return ($value < 0.0) ? 0 - log(abs($value)) : log($value);
},
$xValues
);
$this->leastSquareFit($adjustedYValues, $adjustedXValues, $const);
}
/**
* Define the regression and calculate the goodness of fit for a set of X and Y data values.
*
* @param float[] $yValues The set of Y-values for this regression
* @param float[] $xValues The set of X-values for this regression
* @param bool $const
*/
public function __construct($yValues, $xValues = [], $const = true)
{
parent::__construct($yValues, $xValues);
if (!$this->error) {
$this->powerRegression($yValues, $xValues, (bool) $const);
}
}
}

122
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Trend/Trend.php

@ -0,0 +1,122 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Shared\Trend;
class Trend
{
const TREND_LINEAR = 'Linear';
const TREND_LOGARITHMIC = 'Logarithmic';
const TREND_EXPONENTIAL = 'Exponential';
const TREND_POWER = 'Power';
const TREND_POLYNOMIAL_2 = 'Polynomial_2';
const TREND_POLYNOMIAL_3 = 'Polynomial_3';
const TREND_POLYNOMIAL_4 = 'Polynomial_4';
const TREND_POLYNOMIAL_5 = 'Polynomial_5';
const TREND_POLYNOMIAL_6 = 'Polynomial_6';
const TREND_BEST_FIT = 'Bestfit';
const TREND_BEST_FIT_NO_POLY = 'Bestfit_no_Polynomials';
/**
* Names of the best-fit Trend analysis methods.
*
* @var string[]
*/
private static $trendTypes = [
self::TREND_LINEAR,
self::TREND_LOGARITHMIC,
self::TREND_EXPONENTIAL,
self::TREND_POWER,
];
/**
* Names of the best-fit Trend polynomial orders.
*
* @var string[]
*/
private static $trendTypePolynomialOrders = [
self::TREND_POLYNOMIAL_2,
self::TREND_POLYNOMIAL_3,
self::TREND_POLYNOMIAL_4,
self::TREND_POLYNOMIAL_5,
self::TREND_POLYNOMIAL_6,
];
/**
* Cached results for each method when trying to identify which provides the best fit.
*
* @var BestFit[]
*/
private static $trendCache = [];
public static function calculate($trendType = self::TREND_BEST_FIT, $yValues = [], $xValues = [], $const = true)
{
// Calculate number of points in each dataset
$nY = count($yValues);
$nX = count($xValues);
// Define X Values if necessary
if ($nX === 0) {
$xValues = range(1, $nY);
} elseif ($nY !== $nX) {
// Ensure both arrays of points are the same size
trigger_error('Trend(): Number of elements in coordinate arrays do not match.', E_USER_ERROR);
}
$key = md5($trendType . $const . serialize($yValues) . serialize($xValues));
// Determine which Trend method has been requested
switch ($trendType) {
// Instantiate and return the class for the requested Trend method
case self::TREND_LINEAR:
case self::TREND_LOGARITHMIC:
case self::TREND_EXPONENTIAL:
case self::TREND_POWER:
if (!isset(self::$trendCache[$key])) {
$className = '\PhpOffice\PhpSpreadsheet\Shared\Trend\\' . $trendType . 'BestFit';
// @phpstan-ignore-next-line
self::$trendCache[$key] = new $className($yValues, $xValues, $const);
}
return self::$trendCache[$key];
case self::TREND_POLYNOMIAL_2:
case self::TREND_POLYNOMIAL_3:
case self::TREND_POLYNOMIAL_4:
case self::TREND_POLYNOMIAL_5:
case self::TREND_POLYNOMIAL_6:
if (!isset(self::$trendCache[$key])) {
$order = substr($trendType, -1);
self::$trendCache[$key] = new PolynomialBestFit($order, $yValues, $xValues);
}
return self::$trendCache[$key];
case self::TREND_BEST_FIT:
case self::TREND_BEST_FIT_NO_POLY:
// If the request is to determine the best fit regression, then we test each Trend line in turn
// Start by generating an instance of each available Trend method
$bestFit = [];
$bestFitValue = [];
foreach (self::$trendTypes as $trendMethod) {
$className = '\PhpOffice\PhpSpreadsheet\Shared\Trend\\' . $trendType . 'BestFit';
$bestFit[$trendMethod] = new $className($yValues, $xValues, $const);
$bestFitValue[$trendMethod] = $bestFit[$trendMethod]->getGoodnessOfFit();
}
if ($trendType != self::TREND_BEST_FIT_NO_POLY) {
foreach (self::$trendTypePolynomialOrders as $trendMethod) {
$order = substr($trendMethod, -1);
$bestFit[$trendMethod] = new PolynomialBestFit($order, $yValues, $xValues);
if ($bestFit[$trendMethod]->getError()) {
unset($bestFit[$trendMethod]);
} else {
$bestFitValue[$trendMethod] = $bestFit[$trendMethod]->getGoodnessOfFit();
}
}
}
// Determine which of our Trend lines is the best fit, and then we return the instance of that Trend class
arsort($bestFitValue);
$bestFitType = key($bestFitValue);
return $bestFit[$bestFitType];
default:
return false;
}
}
}

92
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/XMLWriter.php

@ -0,0 +1,92 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Shared;
class XMLWriter extends \XMLWriter
{
public static $debugEnabled = false;
/** Temporary storage method */
const STORAGE_MEMORY = 1;
const STORAGE_DISK = 2;
/**
* Temporary filename.
*
* @var string
*/
private $tempFileName = '';
/**
* Create a new XMLWriter instance.
*
* @param int $temporaryStorage Temporary storage location
* @param string $temporaryStorageFolder Temporary storage folder
*/
public function __construct($temporaryStorage = self::STORAGE_MEMORY, $temporaryStorageFolder = null)
{
// Open temporary storage
if ($temporaryStorage == self::STORAGE_MEMORY) {
$this->openMemory();
} else {
// Create temporary filename
if ($temporaryStorageFolder === null) {
$temporaryStorageFolder = File::sysGetTempDir();
}
$this->tempFileName = @tempnam($temporaryStorageFolder, 'xml');
// Open storage
if ($this->openUri($this->tempFileName) === false) {
// Fallback to memory...
$this->openMemory();
}
}
// Set default values
if (self::$debugEnabled) {
$this->setIndent(true);
}
}
/**
* Destructor.
*/
public function __destruct()
{
// Unlink temporary files
if ($this->tempFileName != '') {
@unlink($this->tempFileName);
}
}
/**
* Get written data.
*
* @return string
*/
public function getData()
{
if ($this->tempFileName == '') {
return $this->outputMemory(true);
}
$this->flush();
return file_get_contents($this->tempFileName);
}
/**
* Wrapper method for writeRaw.
*
* @param null|string|string[] $rawTextData
*
* @return bool
*/
public function writeRawData($rawTextData)
{
if (is_array($rawTextData)) {
$rawTextData = implode("\n", $rawTextData);
}
return $this->writeRaw(htmlspecialchars($rawTextData ?? ''));
}
}

277
vendor/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Shared/Xls.php

@ -0,0 +1,277 @@
<?php
namespace PhpOffice\PhpSpreadsheet\Shared;
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
use PhpOffice\PhpSpreadsheet\Helper\Dimension;
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
class Xls
{
/**
* Get the width of a column in pixels. We use the relationship y = ceil(7x) where
* x is the width in intrinsic Excel units (measuring width in number of normal characters)
* This holds for Arial 10.
*
* @param Worksheet $worksheet The sheet
* @param string $col The column
*
* @return int The width in pixels
*/
public static function sizeCol(Worksheet $worksheet, $col = 'A')
{
// default font of the workbook
$font = $worksheet->getParent()->getDefaultStyle()->getFont();
$columnDimensions = $worksheet->getColumnDimensions();
// first find the true column width in pixels (uncollapsed and unhidden)
if (isset($columnDimensions[$col]) && $columnDimensions[$col]->getWidth() != -1) {
// then we have column dimension with explicit width
$columnDimension = $columnDimensions[$col];
$width = $columnDimension->getWidth();
$pixelWidth = Drawing::cellDimensionToPixels($width, $font);
} elseif ($worksheet->getDefaultColumnDimension()->getWidth() != -1) {
// then we have default column dimension with explicit width
$defaultColumnDimension = $worksheet->getDefaultColumnDimension();
$width = $defaultColumnDimension->getWidth();
$pixelWidth = Drawing::cellDimensionToPixels($width, $font);
} else {
// we don't even have any default column dimension. Width depends on default font
$pixelWidth = Font::getDefaultColumnWidthByFont($font, true);
}
// now find the effective column width in pixels
if (isset($columnDimensions[$col]) && !$columnDimensions[$col]->getVisible()) {
$effectivePixelWidth = 0;
} else {
$effectivePixelWidth = $pixelWidth;
}
return $effectivePixelWidth;
}
/**
* Convert the height of a cell from user's units to pixels. By interpolation
* the relationship is: y = 4/3x. If the height hasn't been set by the user we
* use the default value. If the row is hidden we use a value of zero.
*
* @param Worksheet $worksheet The sheet
* @param int $row The row index (1-based)
*
* @return int The width in pixels
*/
public static function sizeRow(Worksheet $worksheet, $row = 1)
{
// default font of the workbook
$font = $worksheet->getParent()->getDefaultStyle()->getFont();
$rowDimensions = $worksheet->getRowDimensions();
// first find the true row height in pixels (uncollapsed and unhidden)
if (isset($rowDimensions[$row]) && $rowDimensions[$row]->getRowHeight() != -1) {
// then we have a row dimension
$rowDimension = $rowDimensions[$row];
$rowHeight = $rowDimension->getRowHeight();
$pixelRowHeight = (int) ceil(4 * $rowHeight / 3); // here we assume Arial 10
} elseif ($worksheet->getDefaultRowDimension()->getRowHeight() != -1) {
// then we have a default row dimension with explicit height
$defaultRowDimension = $worksheet->getDefaultRowDimension();
$pixelRowHeight = $defaultRowDimension->getRowHeight(Dimension::UOM_PIXELS);
} else {
// we don't even have any default row dimension. Height depends on default font
$pointRowHeight = Font::getDefaultRowHeightByFont($font);
$pixelRowHeight = Font::fontSizeToPixels((int) $pointRowHeight);
}
// now find the effective row height in pixels
if (isset($rowDimensions[$row]) && !$rowDimensions[$row]->getVisible()) {
$effectivePixelRowHeight = 0;
} else {
$effectivePixelRowHeight = $pixelRowHeight;
}
return (int) $effectivePixelRowHeight;
}
/**
* Get the horizontal distance in pixels between two anchors
* The distanceX is found as sum of all the spanning columns widths minus correction for the two offsets.
*
* @param string $startColumn
* @param int $startOffsetX Offset within start cell measured in 1/1024 of the cell width
* @param string $endColumn
* @param int $endOffsetX Offset within end cell measured in 1/1024 of the cell width
*
* @return int Horizontal measured in pixels
*/
public static function getDistanceX(Worksheet $worksheet, $startColumn = 'A', $startOffsetX = 0, $endColumn = 'A', $endOffsetX = 0)
{
$distanceX = 0;
// add the widths of the spanning columns
$startColumnIndex = Coordinate::columnIndexFromString($startColumn);
$endColumnIndex = Coordinate::columnIndexFromString($endColumn);
for ($i = $startColumnIndex; $i <= $endColumnIndex; ++$i) {
$distanceX += self::sizeCol($worksheet, Coordinate::stringFromColumnIndex($i));
}
// correct for offsetX in startcell
$distanceX -= (int) floor(self::sizeCol($worksheet, $startColumn) * $startOffsetX / 1024);
// correct for offsetX in endcell
$distanceX -= (int) floor(self::sizeCol($worksheet, $endColumn) * (1 - $endOffsetX / 1024));
return $distanceX;
}
/**
* Get the vertical distance in pixels between two anchors
* The distanceY is found as sum of all the spanning rows minus two offsets.
*
* @param int $startRow (1-based)
* @param int $startOffsetY Offset within start cell measured in 1/256 of the cell height
* @param int $endRow (1-based)
* @param int $endOffsetY Offset within end cell measured in 1/256 of the cell height
*
* @return int Vertical distance measured in pixels
*/
public static function getDistanceY(Worksheet $worksheet, $startRow = 1, $startOffsetY = 0, $endRow = 1, $endOffsetY = 0)
{
$distanceY = 0;
// add the widths of the spanning rows
for ($row = $startRow; $row <= $endRow; ++$row) {
$distanceY += self::sizeRow($worksheet, $row);
}
// correct for offsetX in startcell
$distanceY -= (int) floor(self::sizeRow($worksheet, $startRow) * $startOffsetY / 256);
// correct for offsetX in endcell
$distanceY -= (int) floor(self::sizeRow($worksheet, $endRow) * (1 - $endOffsetY / 256));
return $distanceY;
}
/**
* Convert 1-cell anchor coordinates to 2-cell anchor coordinates
* This function is ported from PEAR Spreadsheet_Writer_Excel with small modifications.
*
* Calculate the vertices that define the position of the image as required by
* the OBJ record.
*
* +------------+------------+
* | A | B |
* +-----+------------+------------+
* | |(x1,y1) | |
* | 1 |(A1)._______|______ |
* | | | | |
* | | | | |
* +-----+----| BITMAP |-----+
* | | | | |
* | 2 | |______________. |
* | | | (B2)|
* | | | (x2,y2)|
* +---- +------------+------------+
*
* Example of a bitmap that covers some of the area from cell A1 to cell B2.
*
* Based on the width and height of the bitmap we need to calculate 8 vars:
* $col_start, $row_start, $col_end, $row_end, $x1, $y1, $x2, $y2.
* The width and height of the cells are also variable and have to be taken into
* account.
* The values of $col_start and $row_start are passed in from the calling
* function. The values of $col_end and $row_end are calculated by subtracting
* the width and height of the bitmap from the width and height of the
* underlying cells.
* The vertices are expressed as a percentage of the underlying cell width as
* follows (rhs values are in pixels):
*
* x1 = X / W *1024
* y1 = Y / H *256
* x2 = (X-1) / W *1024
* y2 = (Y-1) / H *256
*
* Where: X is distance from the left side of the underlying cell
* Y is distance from the top of the underlying cell
* W is the width of the cell
* H is the height of the cell
*
* @param string $coordinates E.g. 'A1'
* @param int $offsetX Horizontal offset in pixels
* @param int $offsetY Vertical offset in pixels
* @param int $width Width in pixels
* @param int $height Height in pixels
*
* @return null|array
*/
public static function oneAnchor2twoAnchor(Worksheet $worksheet, $coordinates, $offsetX, $offsetY, $width, $height)
{
[$col_start, $row] = Coordinate::indexesFromString($coordinates);
$row_start = $row - 1;
$x1 = $offsetX;
$y1 = $offsetY;
// Initialise end cell to the same as the start cell
$col_end = $col_start; // Col containing lower right corner of object
$row_end = $row_start; // Row containing bottom right corner of object
// Zero the specified offset if greater than the cell dimensions
if ($x1 >= self::sizeCol($worksheet, Coordinate::stringFromColumnIndex($col_start))) {
$x1 = 0;
}
if ($y1 >= self::sizeRow($worksheet, $row_start + 1)) {
$y1 = 0;
}
$width = $width + $x1 - 1;
$height = $height + $y1 - 1;
// Subtract the underlying cell widths to find the end cell of the image
while ($width >= self::sizeCol($worksheet, Coordinate::stringFromColumnIndex($col_end))) {
$width -= self::sizeCol($worksheet, Coordinate::stringFromColumnIndex($col_end));
++$col_end;
}
// Subtract the underlying cell heights to find the end cell of the image
while ($height >= self::sizeRow($worksheet, $row_end + 1)) {
$height -= self::sizeRow($worksheet, $row_end + 1);
++$row_end;
}
// Bitmap isn't allowed to start or finish in a hidden cell, i.e. a cell
// with zero height or width.
if (self::sizeCol($worksheet, Coordinate::stringFromColumnIndex($col_start)) == 0) {
return null;
}
if (self::sizeCol($worksheet, Coordinate::stringFromColumnIndex($col_end)) == 0) {
return null;
}
if (self::sizeRow($worksheet, $row_start + 1) == 0) {
return null;
}
if (self::sizeRow($worksheet, $row_end + 1) == 0) {
return null;
}
// Convert the pixel values to the percentage value expected by Excel
$x1 = $x1 / self::sizeCol($worksheet, Coordinate::stringFromColumnIndex($col_start)) * 1024;
$y1 = $y1 / self::sizeRow($worksheet, $row_start + 1) * 256;
$x2 = ($width + 1) / self::sizeCol($worksheet, Coordinate::stringFromColumnIndex($col_end)) * 1024; // Distance to right side of object
$y2 = ($height + 1) / self::sizeRow($worksheet, $row_end + 1) * 256; // Distance to bottom of object
$startCoordinates = Coordinate::stringFromColumnIndex($col_start) . ($row_start + 1);
$endCoordinates = Coordinate::stringFromColumnIndex($col_end) . ($row_end + 1);
return [
'startCoordinates' => $startCoordinates,
'startOffsetX' => $x1,
'startOffsetY' => $y1,
'endCoordinates' => $endCoordinates,
'endOffsetX' => $x2,
'endOffsetY' => $y2,
];
}
}

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save